Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / disk / ata.c
blob8ba4e5c5033e84d5600807e8a24ef4c7a2aeb818
1 /* ata.c - ATA disk access. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/ata.h>
21 #include <grub/dl.h>
22 #include <grub/disk.h>
23 #include <grub/mm.h>
24 #include <grub/scsi.h>
26 GRUB_MOD_LICENSE ("GPLv3+");
28 static grub_ata_dev_t grub_ata_dev_list;
30 /* Byteorder has to be changed before strings can be read. */
31 static void
32 grub_ata_strncpy (grub_uint16_t *dst16, grub_uint16_t *src16, grub_size_t len)
34 unsigned int i;
36 for (i = 0; i < len / 2; i++)
37 *(dst16++) = grub_swap_bytes16 (*(src16++));
38 *dst16 = 0;
41 static void
42 grub_ata_dumpinfo (struct grub_ata *dev, grub_uint16_t *info)
44 grub_uint16_t text[21];
46 /* The device information was read, dump it for debugging. */
47 grub_ata_strncpy (text, info + 10, 20);
48 grub_dprintf ("ata", "Serial: %s\n", (char *) text);
49 grub_ata_strncpy (text, info + 23, 8);
50 grub_dprintf ("ata", "Firmware: %s\n", (char *) text);
51 grub_ata_strncpy (text, info + 27, 40);
52 grub_dprintf ("ata", "Model: %s\n", (char *) text);
54 if (! dev->atapi)
56 grub_dprintf ("ata", "Addressing: %d\n", dev->addr);
57 grub_dprintf ("ata", "Sectors: %lld\n", (unsigned long long) dev->size);
58 grub_dprintf ("ata", "Sector size: %u\n", 1U << dev->log_sector_size);
62 static grub_err_t
63 grub_atapi_identify (struct grub_ata *dev)
65 struct grub_disk_ata_pass_through_parms parms;
66 grub_uint16_t *info;
67 grub_err_t err;
69 info = grub_malloc (GRUB_DISK_SECTOR_SIZE);
70 if (! info)
71 return grub_errno;
73 grub_memset (&parms, 0, sizeof (parms));
74 parms.taskfile.disk = 0xE0;
75 parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE;
76 parms.size = GRUB_DISK_SECTOR_SIZE;
77 parms.buffer = info;
79 err = dev->dev->readwrite (dev, &parms, *dev->present);
80 if (err)
82 *dev->present = 0;
83 return err;
86 if (parms.size != GRUB_DISK_SECTOR_SIZE)
88 *dev->present = 0;
89 return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
90 "device cannot be identified");
93 dev->atapi = 1;
95 grub_ata_dumpinfo (dev, info);
97 grub_free (info);
99 return GRUB_ERR_NONE;
102 static grub_err_t
103 grub_ata_identify (struct grub_ata *dev)
105 struct grub_disk_ata_pass_through_parms parms;
106 grub_uint64_t *info64;
107 grub_uint32_t *info32;
108 grub_uint16_t *info16;
109 grub_err_t err;
111 if (dev->atapi)
112 return grub_atapi_identify (dev);
114 info64 = grub_malloc (GRUB_DISK_SECTOR_SIZE);
115 info32 = (grub_uint32_t *) info64;
116 info16 = (grub_uint16_t *) info64;
117 if (! info16)
118 return grub_errno;
120 grub_memset (&parms, 0, sizeof (parms));
121 parms.buffer = info16;
122 parms.size = GRUB_DISK_SECTOR_SIZE;
123 parms.taskfile.disk = 0xE0;
125 parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_DEVICE;
127 err = dev->dev->readwrite (dev, &parms, *dev->present);
129 if (err || parms.size != GRUB_DISK_SECTOR_SIZE)
131 grub_uint8_t sts = parms.taskfile.status;
132 grub_free (info16);
133 grub_errno = GRUB_ERR_NONE;
134 if ((sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ
135 | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_ERR
136 && (parms.taskfile.error & 0x04 /* ABRT */))
137 /* Device without ATA IDENTIFY, try ATAPI. */
138 return grub_atapi_identify (dev);
140 else if (sts == 0x00)
142 *dev->present = 0;
143 /* No device, return error but don't print message. */
144 return GRUB_ERR_UNKNOWN_DEVICE;
146 else
148 *dev->present = 0;
149 /* Other Error. */
150 return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
151 "device cannot be identified");
155 /* Now it is certain that this is not an ATAPI device. */
156 dev->atapi = 0;
158 /* CHS is always supported. */
159 dev->addr = GRUB_ATA_CHS;
161 /* Check if LBA is supported. */
162 if (info16[49] & grub_cpu_to_le16_compile_time ((1 << 9)))
164 /* Check if LBA48 is supported. */
165 if (info16[83] & grub_cpu_to_le16_compile_time ((1 << 10)))
166 dev->addr = GRUB_ATA_LBA48;
167 else
168 dev->addr = GRUB_ATA_LBA;
171 /* Determine the amount of sectors. */
172 if (dev->addr != GRUB_ATA_LBA48)
173 dev->size = grub_le_to_cpu32 (info32[30]);
174 else
175 dev->size = grub_le_to_cpu64 (info64[25]);
177 if (info16[106] & grub_cpu_to_le16_compile_time ((1 << 12)))
179 grub_uint32_t secsize;
180 secsize = grub_le_to_cpu32 (grub_get_unaligned32 (&info16[117]));
181 if (secsize & (secsize - 1) || !secsize
182 || secsize > 1048576)
183 secsize = 256;
184 for (dev->log_sector_size = 0;
185 (1U << dev->log_sector_size) < secsize;
186 dev->log_sector_size++);
187 dev->log_sector_size++;
189 else
190 dev->log_sector_size = 9;
192 /* Read CHS information. */
193 dev->cylinders = grub_le_to_cpu16 (info16[1]);
194 dev->heads = grub_le_to_cpu16 (info16[3]);
195 dev->sectors_per_track = grub_le_to_cpu16 (info16[6]);
197 grub_ata_dumpinfo (dev, info16);
199 grub_free (info16);
201 return 0;
204 static grub_err_t
205 grub_ata_setaddress (struct grub_ata *dev,
206 struct grub_disk_ata_pass_through_parms *parms,
207 grub_disk_addr_t sector,
208 grub_size_t size,
209 grub_ata_addressing_t addressing)
211 switch (addressing)
213 case GRUB_ATA_CHS:
215 unsigned int cylinder;
216 unsigned int head;
217 unsigned int sect;
219 if (dev->sectors_per_track == 0
220 || dev->heads == 0)
221 return grub_error (GRUB_ERR_OUT_OF_RANGE,
222 "sector %d cannot be addressed "
223 "using CHS addressing", sector);
225 /* Calculate the sector, cylinder and head to use. */
226 sect = ((grub_uint32_t) sector % dev->sectors_per_track) + 1;
227 cylinder = (((grub_uint32_t) sector / dev->sectors_per_track)
228 / dev->heads);
229 head = ((grub_uint32_t) sector / dev->sectors_per_track) % dev->heads;
231 if (sect > dev->sectors_per_track
232 || cylinder > dev->cylinders
233 || head > dev->heads)
234 return grub_error (GRUB_ERR_OUT_OF_RANGE,
235 "sector %d cannot be addressed "
236 "using CHS addressing", sector);
238 parms->taskfile.disk = 0xE0 | head;
239 parms->taskfile.sectnum = sect;
240 parms->taskfile.cyllsb = cylinder & 0xFF;
241 parms->taskfile.cylmsb = cylinder >> 8;
243 break;
246 case GRUB_ATA_LBA:
247 if (size == 256)
248 size = 0;
249 parms->taskfile.disk = 0xE0 | ((sector >> 24) & 0x0F);
251 parms->taskfile.sectors = size;
252 parms->taskfile.lba_low = sector & 0xFF;
253 parms->taskfile.lba_mid = (sector >> 8) & 0xFF;
254 parms->taskfile.lba_high = (sector >> 16) & 0xFF;
255 break;
257 case GRUB_ATA_LBA48:
258 if (size == 65536)
259 size = 0;
261 parms->taskfile.disk = 0xE0;
263 /* Set "Previous". */
264 parms->taskfile.sectors = size & 0xFF;
265 parms->taskfile.lba_low = sector & 0xFF;
266 parms->taskfile.lba_mid = (sector >> 8) & 0xFF;
267 parms->taskfile.lba_high = (sector >> 16) & 0xFF;
269 /* Set "Current". */
270 parms->taskfile.sectors48 = (size >> 8) & 0xFF;
271 parms->taskfile.lba48_low = (sector >> 24) & 0xFF;
272 parms->taskfile.lba48_mid = (sector >> 32) & 0xFF;
273 parms->taskfile.lba48_high = (sector >> 40) & 0xFF;
275 break;
278 return GRUB_ERR_NONE;
281 static grub_err_t
282 grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
283 grub_size_t size, char *buf, int rw)
285 struct grub_ata *ata = disk->data;
287 grub_ata_addressing_t addressing = ata->addr;
288 grub_size_t batch;
289 int cmd, cmd_write;
290 grub_size_t nsectors = 0;
292 grub_dprintf("ata", "grub_ata_readwrite (size=%llu, rw=%d)\n",
293 (unsigned long long) size, rw);
295 if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0)
297 if (ata->dma)
299 cmd = GRUB_ATA_CMD_READ_SECTORS_DMA_EXT;
300 cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_DMA_EXT;
302 else
304 cmd = GRUB_ATA_CMD_READ_SECTORS_EXT;
305 cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_EXT;
308 else
310 if (addressing == GRUB_ATA_LBA48)
311 addressing = GRUB_ATA_LBA;
312 if (ata->dma)
314 cmd = GRUB_ATA_CMD_READ_SECTORS_DMA;
315 cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_DMA;
317 else
319 cmd = GRUB_ATA_CMD_READ_SECTORS;
320 cmd_write = GRUB_ATA_CMD_WRITE_SECTORS;
324 if (addressing != GRUB_ATA_CHS)
325 batch = 256;
326 else
327 batch = 1;
329 while (nsectors < size)
331 struct grub_disk_ata_pass_through_parms parms;
332 grub_err_t err;
334 if (size - nsectors < batch)
335 batch = size - nsectors;
337 grub_dprintf("ata", "rw=%d, sector=%llu, batch=%llu\n", rw, (unsigned long long) sector, (unsigned long long) batch);
338 grub_memset (&parms, 0, sizeof (parms));
339 grub_ata_setaddress (ata, &parms, sector, batch, addressing);
340 parms.taskfile.cmd = (! rw ? cmd : cmd_write);
341 parms.buffer = buf;
342 parms.size = batch << ata->log_sector_size;
343 parms.write = rw;
344 if (ata->dma)
345 parms.dma = 1;
347 err = ata->dev->readwrite (ata, &parms, 0);
348 if (err)
349 return err;
350 if (parms.size != batch << ata->log_sector_size)
351 return grub_error (GRUB_ERR_READ_ERROR, "incomplete read");
352 buf += batch << ata->log_sector_size;
353 sector += batch;
354 nsectors += batch;
357 return GRUB_ERR_NONE;
362 static inline void
363 grub_ata_real_close (struct grub_ata *ata)
365 if (ata->dev->close)
366 ata->dev->close (ata);
369 static struct grub_ata *
370 grub_ata_real_open (int id, int bus)
372 struct grub_ata *ata;
373 grub_ata_dev_t p;
375 ata = grub_zalloc (sizeof (*ata));
376 if (!ata)
377 return NULL;
378 for (p = grub_ata_dev_list; p; p = p->next)
380 grub_err_t err;
381 if (p->open (id, bus, ata))
383 grub_errno = GRUB_ERR_NONE;
384 continue;
386 ata->dev = p;
387 /* Use the IDENTIFY DEVICE command to query the device. */
388 err = grub_ata_identify (ata);
389 if (err)
391 if (!grub_errno)
392 grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATA device");
393 grub_free (ata);
394 return NULL;
396 return ata;
398 grub_free (ata);
399 grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATA device");
400 return NULL;
403 /* Context for grub_ata_iterate. */
404 struct grub_ata_iterate_ctx
406 grub_disk_dev_iterate_hook_t hook;
407 void *hook_data;
410 /* Helper for grub_ata_iterate. */
411 static int
412 grub_ata_iterate_iter (int id, int bus, void *data)
414 struct grub_ata_iterate_ctx *ctx = data;
415 struct grub_ata *ata;
416 int ret;
417 char devname[40];
419 ata = grub_ata_real_open (id, bus);
421 if (!ata)
423 grub_errno = GRUB_ERR_NONE;
424 return 0;
426 if (ata->atapi)
428 grub_ata_real_close (ata);
429 return 0;
431 grub_snprintf (devname, sizeof (devname),
432 "%s%d", grub_scsi_names[id], bus);
433 ret = ctx->hook (devname, ctx->hook_data);
434 grub_ata_real_close (ata);
435 return ret;
438 static int
439 grub_ata_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
440 grub_disk_pull_t pull)
442 struct grub_ata_iterate_ctx ctx = { hook, hook_data };
443 grub_ata_dev_t p;
445 for (p = grub_ata_dev_list; p; p = p->next)
446 if (p->iterate && p->iterate (grub_ata_iterate_iter, &ctx, pull))
447 return 1;
448 return 0;
451 static grub_err_t
452 grub_ata_open (const char *name, grub_disk_t disk)
454 unsigned id, bus;
455 struct grub_ata *ata;
457 for (id = 0; id < GRUB_SCSI_NUM_SUBSYSTEMS; id++)
458 if (grub_strncmp (grub_scsi_names[id], name,
459 grub_strlen (grub_scsi_names[id])) == 0
460 && grub_isdigit (name[grub_strlen (grub_scsi_names[id])]))
461 break;
462 if (id == GRUB_SCSI_NUM_SUBSYSTEMS)
463 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
464 bus = grub_strtoul (name + grub_strlen (grub_scsi_names[id]), 0, 0);
465 ata = grub_ata_real_open (id, bus);
466 if (!ata)
467 return grub_errno;
469 if (ata->atapi)
470 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
472 disk->total_sectors = ata->size;
473 disk->max_agglomerate = (ata->maxbuffer >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS));
474 if (disk->max_agglomerate > (256U >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS - ata->log_sector_size)))
475 disk->max_agglomerate = (256U >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS - ata->log_sector_size));
477 disk->log_sector_size = ata->log_sector_size;
479 disk->id = grub_make_scsi_id (id, bus, 0);
481 disk->data = ata;
483 return 0;
486 static void
487 grub_ata_close (grub_disk_t disk)
489 struct grub_ata *ata = disk->data;
490 grub_ata_real_close (ata);
493 static grub_err_t
494 grub_ata_read (grub_disk_t disk, grub_disk_addr_t sector,
495 grub_size_t size, char *buf)
497 return grub_ata_readwrite (disk, sector, size, buf, 0);
500 static grub_err_t
501 grub_ata_write (grub_disk_t disk,
502 grub_disk_addr_t sector,
503 grub_size_t size,
504 const char *buf)
506 return grub_ata_readwrite (disk, sector, size, (char *) buf, 1);
509 static struct grub_disk_dev grub_atadisk_dev =
511 .name = "ATA",
512 .id = GRUB_DISK_DEVICE_ATA_ID,
513 .iterate = grub_ata_iterate,
514 .open = grub_ata_open,
515 .close = grub_ata_close,
516 .read = grub_ata_read,
517 .write = grub_ata_write,
518 .next = 0
523 /* ATAPI code. */
525 static grub_err_t
526 grub_atapi_read (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
527 grub_size_t size, char *buf)
529 struct grub_ata *dev = scsi->data;
530 struct grub_disk_ata_pass_through_parms parms;
531 grub_err_t err;
533 grub_dprintf("ata", "grub_atapi_read (size=%llu)\n", (unsigned long long) size);
534 grub_memset (&parms, 0, sizeof (parms));
536 parms.taskfile.disk = 0;
537 parms.taskfile.features = 0;
538 parms.taskfile.atapi_ireason = 0;
539 parms.taskfile.atapi_cnthigh = size >> 8;
540 parms.taskfile.atapi_cntlow = size & 0xff;
541 parms.taskfile.cmd = GRUB_ATA_CMD_PACKET;
542 parms.cmd = cmd;
543 parms.cmdsize = cmdsize;
545 parms.size = size;
546 parms.buffer = buf;
548 err = dev->dev->readwrite (dev, &parms, 0);
549 if (err)
550 return err;
552 if (parms.size != size)
553 return grub_error (GRUB_ERR_READ_ERROR, "incomplete ATAPI read");
554 return GRUB_ERR_NONE;
557 static grub_err_t
558 grub_atapi_write (struct grub_scsi *scsi __attribute__((unused)),
559 grub_size_t cmdsize __attribute__((unused)),
560 char *cmd __attribute__((unused)),
561 grub_size_t size __attribute__((unused)),
562 const char *buf __attribute__((unused)))
564 // XXX: scsi.mod does not use write yet.
565 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ATAPI write not implemented");
568 static grub_err_t
569 grub_atapi_open (int id, int bus, struct grub_scsi *scsi)
571 struct grub_ata *ata;
573 ata = grub_ata_real_open (id, bus);
574 if (!ata)
575 return grub_errno;
577 if (! ata->atapi)
578 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device");
580 scsi->data = ata;
581 scsi->luns = 1;
583 return GRUB_ERR_NONE;
586 /* Context for grub_atapi_iterate. */
587 struct grub_atapi_iterate_ctx
589 grub_scsi_dev_iterate_hook_t hook;
590 void *hook_data;
593 /* Helper for grub_atapi_iterate. */
594 static int
595 grub_atapi_iterate_iter (int id, int bus, void *data)
597 struct grub_atapi_iterate_ctx *ctx = data;
598 struct grub_ata *ata;
599 int ret;
601 ata = grub_ata_real_open (id, bus);
603 if (!ata)
605 grub_errno = GRUB_ERR_NONE;
606 return 0;
608 if (!ata->atapi)
610 grub_ata_real_close (ata);
611 return 0;
613 ret = ctx->hook (id, bus, 1, ctx->hook_data);
614 grub_ata_real_close (ata);
615 return ret;
618 static int
619 grub_atapi_iterate (grub_scsi_dev_iterate_hook_t hook, void *hook_data,
620 grub_disk_pull_t pull)
622 struct grub_atapi_iterate_ctx ctx = { hook, hook_data };
623 grub_ata_dev_t p;
625 for (p = grub_ata_dev_list; p; p = p->next)
626 if (p->iterate && p->iterate (grub_atapi_iterate_iter, &ctx, pull))
627 return 1;
628 return 0;
631 static void
632 grub_atapi_close (grub_scsi_t disk)
634 struct grub_ata *ata = disk->data;
635 grub_ata_real_close (ata);
639 void
640 grub_ata_dev_register (grub_ata_dev_t dev)
642 dev->next = grub_ata_dev_list;
643 grub_ata_dev_list = dev;
646 void
647 grub_ata_dev_unregister (grub_ata_dev_t dev)
649 grub_ata_dev_t *p, q;
651 for (p = &grub_ata_dev_list, q = *p; q; p = &(q->next), q = q->next)
652 if (q == dev)
654 *p = q->next;
655 break;
659 static struct grub_scsi_dev grub_atapi_dev =
661 .iterate = grub_atapi_iterate,
662 .open = grub_atapi_open,
663 .close = grub_atapi_close,
664 .read = grub_atapi_read,
665 .write = grub_atapi_write,
666 .next = 0
671 GRUB_MOD_INIT(ata)
673 grub_disk_dev_register (&grub_atadisk_dev);
675 /* ATAPI devices are handled by scsi.mod. */
676 grub_scsi_dev_register (&grub_atapi_dev);
679 GRUB_MOD_FINI(ata)
681 grub_scsi_dev_unregister (&grub_atapi_dev);
682 grub_disk_dev_unregister (&grub_atadisk_dev);