1 /* ata.c - ATA disk access. */
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/>.
22 #include <grub/disk.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. */
32 grub_ata_strncpy (grub_uint16_t
*dst16
, grub_uint16_t
*src16
, grub_size_t len
)
36 for (i
= 0; i
< len
/ 2; i
++)
37 *(dst16
++) = grub_swap_bytes16 (*(src16
++));
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
);
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
);
63 grub_atapi_identify (struct grub_ata
*dev
)
65 struct grub_disk_ata_pass_through_parms parms
;
69 info
= grub_malloc (GRUB_DISK_SECTOR_SIZE
);
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
;
79 err
= dev
->dev
->readwrite (dev
, &parms
, *dev
->present
);
86 if (parms
.size
!= GRUB_DISK_SECTOR_SIZE
)
89 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
,
90 "device cannot be identified");
95 grub_ata_dumpinfo (dev
, info
);
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
;
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
;
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
;
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)
143 /* No device, return error but don't print message. */
144 return GRUB_ERR_UNKNOWN_DEVICE
;
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. */
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
;
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]);
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)
184 for (dev
->log_sector_size
= 0;
185 (1U << dev
->log_sector_size
) < secsize
;
186 dev
->log_sector_size
++);
187 dev
->log_sector_size
++;
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
);
205 grub_ata_setaddress (struct grub_ata
*dev
,
206 struct grub_disk_ata_pass_through_parms
*parms
,
207 grub_disk_addr_t sector
,
209 grub_ata_addressing_t addressing
)
215 unsigned int cylinder
;
219 if (dev
->sectors_per_track
== 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
)
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;
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;
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;
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;
278 return GRUB_ERR_NONE
;
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
;
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)
299 cmd
= GRUB_ATA_CMD_READ_SECTORS_DMA_EXT
;
300 cmd_write
= GRUB_ATA_CMD_WRITE_SECTORS_DMA_EXT
;
304 cmd
= GRUB_ATA_CMD_READ_SECTORS_EXT
;
305 cmd_write
= GRUB_ATA_CMD_WRITE_SECTORS_EXT
;
310 if (addressing
== GRUB_ATA_LBA48
)
311 addressing
= GRUB_ATA_LBA
;
314 cmd
= GRUB_ATA_CMD_READ_SECTORS_DMA
;
315 cmd_write
= GRUB_ATA_CMD_WRITE_SECTORS_DMA
;
319 cmd
= GRUB_ATA_CMD_READ_SECTORS
;
320 cmd_write
= GRUB_ATA_CMD_WRITE_SECTORS
;
324 if (addressing
!= GRUB_ATA_CHS
)
329 while (nsectors
< size
)
331 struct grub_disk_ata_pass_through_parms parms
;
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
);
342 parms
.size
= batch
<< ata
->log_sector_size
;
347 err
= ata
->dev
->readwrite (ata
, &parms
, 0);
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
;
357 return GRUB_ERR_NONE
;
363 grub_ata_real_close (struct grub_ata
*ata
)
366 ata
->dev
->close (ata
);
369 static struct grub_ata
*
370 grub_ata_real_open (int id
, int bus
)
372 struct grub_ata
*ata
;
375 ata
= grub_zalloc (sizeof (*ata
));
378 for (p
= grub_ata_dev_list
; p
; p
= p
->next
)
381 if (p
->open (id
, bus
, ata
))
383 grub_errno
= GRUB_ERR_NONE
;
387 /* Use the IDENTIFY DEVICE command to query the device. */
388 err
= grub_ata_identify (ata
);
392 grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "no such ATA device");
399 grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "no such ATA device");
403 /* Context for grub_ata_iterate. */
404 struct grub_ata_iterate_ctx
406 grub_disk_dev_iterate_hook_t hook
;
410 /* Helper for grub_ata_iterate. */
412 grub_ata_iterate_iter (int id
, int bus
, void *data
)
414 struct grub_ata_iterate_ctx
*ctx
= data
;
415 struct grub_ata
*ata
;
419 ata
= grub_ata_real_open (id
, bus
);
423 grub_errno
= GRUB_ERR_NONE
;
428 grub_ata_real_close (ata
);
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
);
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
};
445 for (p
= grub_ata_dev_list
; p
; p
= p
->next
)
446 if (p
->iterate
&& p
->iterate (grub_ata_iterate_iter
, &ctx
, pull
))
452 grub_ata_open (const char *name
, grub_disk_t disk
)
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
])]))
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
);
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);
487 grub_ata_close (grub_disk_t disk
)
489 struct grub_ata
*ata
= disk
->data
;
490 grub_ata_real_close (ata
);
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);
501 grub_ata_write (grub_disk_t disk
,
502 grub_disk_addr_t sector
,
506 return grub_ata_readwrite (disk
, sector
, size
, (char *) buf
, 1);
509 static struct grub_disk_dev grub_atadisk_dev
=
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
,
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
;
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
;
543 parms
.cmdsize
= cmdsize
;
548 err
= dev
->dev
->readwrite (dev
, &parms
, 0);
552 if (parms
.size
!= size
)
553 return grub_error (GRUB_ERR_READ_ERROR
, "incomplete ATAPI read");
554 return GRUB_ERR_NONE
;
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");
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
);
578 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "no such ATAPI device");
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
;
593 /* Helper for grub_atapi_iterate. */
595 grub_atapi_iterate_iter (int id
, int bus
, void *data
)
597 struct grub_atapi_iterate_ctx
*ctx
= data
;
598 struct grub_ata
*ata
;
601 ata
= grub_ata_real_open (id
, bus
);
605 grub_errno
= GRUB_ERR_NONE
;
610 grub_ata_real_close (ata
);
613 ret
= ctx
->hook (id
, bus
, 1, ctx
->hook_data
);
614 grub_ata_real_close (ata
);
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
};
625 for (p
= grub_ata_dev_list
; p
; p
= p
->next
)
626 if (p
->iterate
&& p
->iterate (grub_atapi_iterate_iter
, &ctx
, pull
))
632 grub_atapi_close (grub_scsi_t disk
)
634 struct grub_ata
*ata
= disk
->data
;
635 grub_ata_real_close (ata
);
640 grub_ata_dev_register (grub_ata_dev_t dev
)
642 dev
->next
= grub_ata_dev_list
;
643 grub_ata_dev_list
= dev
;
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
)
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
,
673 grub_disk_dev_register (&grub_atadisk_dev
);
675 /* ATAPI devices are handled by scsi.mod. */
676 grub_scsi_dev_register (&grub_atapi_dev
);
681 grub_scsi_dev_unregister (&grub_atapi_dev
);
682 grub_disk_dev_unregister (&grub_atadisk_dev
);