2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/machine/biosdisk.h>
20 #include <grub/machine/kernel.h>
21 #include <grub/machine/memory.h>
22 #include <grub/machine/int.h>
23 #include <grub/disk.h>
26 #include <grub/types.h>
27 #include <grub/misc.h>
29 #include <grub/term.h>
30 #include <grub/i18n.h>
32 GRUB_MOD_LICENSE ("GPLv3+");
34 static int cd_drive
= 0;
35 static int grub_biosdisk_rw_int13_extensions (int ah
, int drive
, void *dap
);
37 static int grub_biosdisk_get_num_floppies (void)
39 struct grub_bios_int_registers regs
;
42 /* reset the disk system first */
45 regs
.flags
= GRUB_CPU_INT_FLAGS_DEFAULT
;
47 grub_bios_interrupt (0x13, ®s
);
49 for (drive
= 0; drive
< 2; drive
++)
51 regs
.flags
= GRUB_CPU_INT_FLAGS_DEFAULT
| GRUB_CPU_INT_FLAGS_CARRY
;
54 /* call GET DISK TYPE */
56 grub_bios_interrupt (0x13, ®s
);
57 if (regs
.flags
& GRUB_CPU_INT_FLAGS_CARRY
)
60 /* check if this drive exists */
61 if (!(regs
.eax
& 0x300))
69 * Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP
70 * is passed for disk address packet. If an error occurs, return
71 * non-zero, otherwise zero.
75 grub_biosdisk_rw_int13_extensions (int ah
, int drive
, void *dap
)
77 struct grub_bios_int_registers regs
;
79 /* compute the address of disk_address_packet */
80 regs
.ds
= (((grub_addr_t
) dap
) & 0xffff0000) >> 4;
81 regs
.esi
= (((grub_addr_t
) dap
) & 0xffff);
83 regs
.flags
= GRUB_CPU_INT_FLAGS_DEFAULT
;
85 grub_bios_interrupt (0x13, ®s
);
86 return (regs
.eax
>> 8) & 0xff;
90 * Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
91 * NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
92 * return non-zero, otherwise zero.
95 grub_biosdisk_rw_standard (int ah
, int drive
, int coff
, int hoff
,
96 int soff
, int nsec
, int segment
)
101 for (i
= 0; i
< 3; i
++)
103 struct grub_bios_int_registers regs
;
105 /* set up CHS information */
106 /* set %ch to low eight bits of cylinder */
107 regs
.ecx
= (coff
<< 8) & 0xff00;
108 /* set bits 6-7 of %cl to high two bits of cylinder */
109 regs
.ecx
|= (coff
>> 2) & 0xc0;
110 /* set bits 0-5 of %cl to sector */
111 regs
.ecx
|= soff
& 0x3f;
113 /* set %dh to head and %dl to drive */
114 regs
.edx
= (drive
& 0xff) | ((hoff
<< 8) & 0xff00);
116 regs
.eax
= (ah
<< 8) & 0xff00;
117 /* set %al to NSEC */
118 regs
.eax
|= nsec
& 0xff;
123 regs
.flags
= GRUB_CPU_INT_FLAGS_DEFAULT
;
125 grub_bios_interrupt (0x13, ®s
);
126 /* check if successful */
127 if (!(regs
.flags
& GRUB_CPU_INT_FLAGS_CARRY
))
130 /* save return value */
133 /* if fail, reset the disk system */
135 regs
.edx
= (drive
& 0xff);
136 regs
.flags
= GRUB_CPU_INT_FLAGS_DEFAULT
;
137 grub_bios_interrupt (0x13, ®s
);
143 * Check if LBA is supported for DRIVE. If it is supported, then return
144 * the major version of extensions, otherwise zero.
147 grub_biosdisk_check_int13_extensions (int drive
)
149 struct grub_bios_int_registers regs
;
151 regs
.edx
= drive
& 0xff;
154 regs
.flags
= GRUB_CPU_INT_FLAGS_DEFAULT
;
155 grub_bios_interrupt (0x13, ®s
);
157 if (regs
.flags
& GRUB_CPU_INT_FLAGS_CARRY
)
160 if ((regs
.ebx
& 0xffff) != 0xaa55)
163 /* check if AH=0x42 is supported */
167 return (regs
.eax
>> 8) & 0xff;
171 * Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
172 * error occurs, then return non-zero, otherwise zero.
175 grub_biosdisk_get_diskinfo_standard (int drive
,
176 unsigned long *cylinders
,
177 unsigned long *heads
,
178 unsigned long *sectors
)
180 struct grub_bios_int_registers regs
;
183 regs
.edx
= drive
& 0xff;
185 regs
.flags
= GRUB_CPU_INT_FLAGS_DEFAULT
;
186 grub_bios_interrupt (0x13, ®s
);
188 /* Check if unsuccessful. Ignore return value if carry isn't set to
189 workaround some buggy BIOSes. */
190 if ((regs
.flags
& GRUB_CPU_INT_FLAGS_CARRY
) && ((regs
.eax
& 0xff00) != 0))
191 return (regs
.eax
& 0xff00) >> 8;
193 /* bogus BIOSes may not return an error number */
194 /* 0 sectors means no disk */
195 if (!(regs
.ecx
& 0x3f))
196 /* XXX 0x60 is one of the unused error numbers */
199 /* the number of heads is counted from zero */
200 *heads
= ((regs
.edx
>> 8) & 0xff) + 1;
201 *cylinders
= (((regs
.ecx
>> 8) & 0xff) | ((regs
.ecx
<< 2) & 0x0300)) + 1;
202 *sectors
= regs
.ecx
& 0x3f;
207 grub_biosdisk_get_diskinfo_real (int drive
, void *drp
, grub_uint16_t ax
)
209 struct grub_bios_int_registers regs
;
213 /* compute the address of drive parameters */
214 regs
.esi
= ((grub_addr_t
) drp
) & 0xf;
215 regs
.ds
= ((grub_addr_t
) drp
) >> 4;
216 regs
.edx
= drive
& 0xff;
218 regs
.flags
= GRUB_CPU_INT_FLAGS_DEFAULT
;
219 grub_bios_interrupt (0x13, ®s
);
221 /* Check if unsuccessful. Ignore return value if carry isn't set to
222 workaround some buggy BIOSes. */
223 if ((regs
.flags
& GRUB_CPU_INT_FLAGS_CARRY
) && ((regs
.eax
& 0xff00) != 0))
224 return (regs
.eax
& 0xff00) >> 8;
230 * Return the cdrom information of DRIVE in CDRP. If an error occurs,
231 * then return non-zero, otherwise zero.
234 grub_biosdisk_get_cdinfo_int13_extensions (int drive
, void *cdrp
)
236 return grub_biosdisk_get_diskinfo_real (drive
, cdrp
, 0x4b01);
240 * Return the geometry of DRIVE in a drive parameters, DRP. If an error
241 * occurs, then return non-zero, otherwise zero.
244 grub_biosdisk_get_diskinfo_int13_extensions (int drive
, void *drp
)
246 return grub_biosdisk_get_diskinfo_real (drive
, drp
, 0x4800);
250 grub_biosdisk_get_drive (const char *name
)
254 if (name
[0] == 'c' && name
[1] == 'd' && name
[2] == 0 && cd_drive
)
257 if ((name
[0] != 'f' && name
[0] != 'h') || name
[1] != 'd')
260 drive
= grub_strtoul (name
+ 2, 0, 10);
261 if (grub_errno
!= GRUB_ERR_NONE
)
270 grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "not a biosdisk");
275 grub_biosdisk_call_hook (int (*hook
) (const char *name
), int drive
)
279 if (cd_drive
&& drive
== cd_drive
)
282 grub_snprintf (name
, sizeof (name
),
283 (drive
& 0x80) ? "hd%d" : "fd%d", drive
& (~0x80));
288 grub_biosdisk_iterate (int (*hook
) (const char *name
),
289 grub_disk_pull_t pull
__attribute__ ((unused
)))
294 /* For hard disks, attempt to read the MBR. */
297 case GRUB_DISK_PULL_NONE
:
298 for (drive
= 0x80; drive
< 0x90; drive
++)
300 if (grub_biosdisk_rw_standard (0x02, drive
, 0, 0, 1, 1,
301 GRUB_MEMORY_MACHINE_SCRATCH_SEG
) != 0)
303 grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive
);
307 if (grub_biosdisk_call_hook (hook
, drive
))
312 case GRUB_DISK_PULL_REMOVABLE
:
315 if (grub_biosdisk_call_hook (hook
, cd_drive
))
319 /* For floppy disks, we can get the number safely. */
320 num_floppies
= grub_biosdisk_get_num_floppies ();
321 for (drive
= 0; drive
< num_floppies
; drive
++)
322 if (grub_biosdisk_call_hook (hook
, drive
))
332 grub_biosdisk_open (const char *name
, grub_disk_t disk
)
334 grub_uint64_t total_sectors
= 0;
336 struct grub_biosdisk_data
*data
;
338 drive
= grub_biosdisk_get_drive (name
);
344 data
= (struct grub_biosdisk_data
*) grub_zalloc (sizeof (*data
));
350 if ((cd_drive
) && (drive
== cd_drive
))
352 data
->flags
= GRUB_BIOSDISK_FLAG_LBA
| GRUB_BIOSDISK_FLAG_CDROM
;
354 disk
->log_sector_size
= 11;
355 /* TODO: get the correct size. */
356 total_sectors
= GRUB_DISK_SIZE_UNKNOWN
;
363 disk
->log_sector_size
= 9;
365 version
= grub_biosdisk_check_int13_extensions (drive
);
368 struct grub_biosdisk_drp
*drp
369 = (struct grub_biosdisk_drp
*) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
;
371 /* Clear out the DRP. */
372 grub_memset (drp
, 0, sizeof (*drp
));
373 drp
->size
= sizeof (*drp
);
374 if (! grub_biosdisk_get_diskinfo_int13_extensions (drive
, drp
))
376 data
->flags
= GRUB_BIOSDISK_FLAG_LBA
;
378 if (drp
->total_sectors
)
379 total_sectors
= drp
->total_sectors
;
381 /* Some buggy BIOSes doesn't return the total sectors
382 correctly but returns zero. So if it is zero, compute
383 it by C/H/S returned by the LBA BIOS call. */
384 total_sectors
= drp
->cylinders
* drp
->heads
* drp
->sectors
;
385 if (drp
->bytes_per_sector
386 && !(drp
->bytes_per_sector
& (drp
->bytes_per_sector
- 1))
387 && drp
->bytes_per_sector
>= 512
388 && drp
->bytes_per_sector
<= 16384)
390 for (disk
->log_sector_size
= 0;
391 (1 << disk
->log_sector_size
) < drp
->bytes_per_sector
;
392 disk
->log_sector_size
++);
398 if (! (data
->flags
& GRUB_BIOSDISK_FLAG_CDROM
))
400 if (grub_biosdisk_get_diskinfo_standard (drive
,
403 &data
->sectors
) != 0)
405 if (total_sectors
&& (data
->flags
& GRUB_BIOSDISK_FLAG_LBA
))
410 = grub_divmod64 (total_sectors
411 + data
->heads
* data
->sectors
- 1,
412 data
->heads
* data
->sectors
, 0);
417 return grub_error (GRUB_ERR_BAD_DEVICE
, "%s cannot get C/H/S values", disk
->name
);
422 total_sectors
= data
->cylinders
* data
->heads
* data
->sectors
;
425 disk
->total_sectors
= total_sectors
;
428 return GRUB_ERR_NONE
;
432 grub_biosdisk_close (grub_disk_t disk
)
434 grub_free (disk
->data
);
437 /* For readability. */
438 #define GRUB_BIOSDISK_READ 0
439 #define GRUB_BIOSDISK_WRITE 1
441 #define GRUB_BIOSDISK_CDROM_RETRY_COUNT 3
444 grub_biosdisk_rw (int cmd
, grub_disk_t disk
,
445 grub_disk_addr_t sector
, grub_size_t size
,
448 struct grub_biosdisk_data
*data
= disk
->data
;
450 if (data
->flags
& GRUB_BIOSDISK_FLAG_LBA
)
452 struct grub_biosdisk_dap
*dap
;
454 dap
= (struct grub_biosdisk_dap
*) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR
456 << disk
->log_sector_size
));
457 dap
->length
= sizeof (*dap
);
460 dap
->buffer
= segment
<< 16; /* The format SEGMENT:ADDRESS. */
463 if (data
->flags
& GRUB_BIOSDISK_FLAG_CDROM
)
468 return grub_error (GRUB_ERR_WRITE_ERROR
, N_("cannot write to CD-ROM"));
470 for (i
= 0; i
< GRUB_BIOSDISK_CDROM_RETRY_COUNT
; i
++)
471 if (! grub_biosdisk_rw_int13_extensions (0x42, data
->drive
, dap
))
474 if (i
== GRUB_BIOSDISK_CDROM_RETRY_COUNT
)
475 return grub_error (GRUB_ERR_READ_ERROR
, N_("failure reading sector 0x%llx "
477 (unsigned long long) sector
,
481 if (grub_biosdisk_rw_int13_extensions (cmd
+ 0x42, data
->drive
, dap
))
483 /* Fall back to the CHS mode. */
484 data
->flags
&= ~GRUB_BIOSDISK_FLAG_LBA
;
485 disk
->total_sectors
= data
->cylinders
* data
->heads
* data
->sectors
;
486 return grub_biosdisk_rw (cmd
, disk
, sector
, size
, segment
);
491 unsigned coff
, hoff
, soff
;
494 /* It is impossible to reach over 8064 MiB (a bit less than LBA24) with
495 the traditional CHS access. */
497 1024 /* cylinders */ *
500 return grub_error (GRUB_ERR_OUT_OF_RANGE
,
501 N_("attempt to read or write outside of disk `%s'"),
504 soff
= ((grub_uint32_t
) sector
) % data
->sectors
+ 1;
505 head
= ((grub_uint32_t
) sector
) / data
->sectors
;
506 hoff
= head
% data
->heads
;
507 coff
= head
/ data
->heads
;
509 if (coff
>= data
->cylinders
)
510 return grub_error (GRUB_ERR_OUT_OF_RANGE
,
511 N_("attempt to read or write outside of disk `%s'"),
514 if (grub_biosdisk_rw_standard (cmd
+ 0x02, data
->drive
,
515 coff
, hoff
, soff
, size
, segment
))
519 case GRUB_BIOSDISK_READ
:
520 return grub_error (GRUB_ERR_READ_ERROR
, N_("failure reading sector 0x%llx "
522 (unsigned long long) sector
,
524 case GRUB_BIOSDISK_WRITE
:
525 return grub_error (GRUB_ERR_WRITE_ERROR
, N_("failure writing sector 0x%llx "
527 (unsigned long long) sector
,
533 return GRUB_ERR_NONE
;
536 /* Return the number of sectors which can be read safely at a time. */
538 get_safe_sectors (grub_disk_t disk
, grub_disk_addr_t sector
)
541 grub_uint64_t offset
;
542 struct grub_biosdisk_data
*data
= disk
->data
;
543 grub_uint32_t sectors
= data
->sectors
;
545 /* OFFSET = SECTOR % SECTORS */
546 grub_divmod64 (sector
, sectors
, &offset
);
548 size
= sectors
- offset
;
550 /* Limit the max to 0x7f because of Phoenix EDD. */
551 if (size
> ((0x7fU
<< GRUB_DISK_SECTOR_BITS
) >> disk
->log_sector_size
))
552 size
= ((0x7fU
<< GRUB_DISK_SECTOR_BITS
) >> disk
->log_sector_size
);
558 grub_biosdisk_read (grub_disk_t disk
, grub_disk_addr_t sector
,
559 grub_size_t size
, char *buf
)
565 len
= get_safe_sectors (disk
, sector
);
570 if (grub_biosdisk_rw (GRUB_BIOSDISK_READ
, disk
, sector
, len
,
571 GRUB_MEMORY_MACHINE_SCRATCH_SEG
))
574 grub_memcpy (buf
, (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
,
575 len
<< disk
->log_sector_size
);
577 buf
+= len
<< disk
->log_sector_size
;
586 grub_biosdisk_write (grub_disk_t disk
, grub_disk_addr_t sector
,
587 grub_size_t size
, const char *buf
)
589 struct grub_biosdisk_data
*data
= disk
->data
;
591 if (data
->flags
& GRUB_BIOSDISK_FLAG_CDROM
)
592 return grub_error (GRUB_ERR_IO
, N_("cannot write to CD-ROM"));
598 len
= get_safe_sectors (disk
, sector
);
602 grub_memcpy ((void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
, buf
,
603 len
<< disk
->log_sector_size
);
605 if (grub_biosdisk_rw (GRUB_BIOSDISK_WRITE
, disk
, sector
, len
,
606 GRUB_MEMORY_MACHINE_SCRATCH_SEG
))
609 buf
+= len
<< disk
->log_sector_size
;
617 static struct grub_disk_dev grub_biosdisk_dev
=
620 .id
= GRUB_DISK_DEVICE_BIOSDISK_ID
,
621 .iterate
= grub_biosdisk_iterate
,
622 .open
= grub_biosdisk_open
,
623 .close
= grub_biosdisk_close
,
624 .read
= grub_biosdisk_read
,
625 .write
= grub_biosdisk_write
,
630 grub_disk_biosdisk_fini (void)
632 grub_disk_dev_unregister (&grub_biosdisk_dev
);
635 GRUB_MOD_INIT(biosdisk
)
637 struct grub_biosdisk_cdrp
*cdrp
638 = (struct grub_biosdisk_cdrp
*) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
;
639 grub_uint8_t boot_drive
;
641 if (grub_disk_firmware_is_tainted
)
643 grub_puts_ (N_("Native disk drivers are in use. "
644 "Refusing to use firmware disk interface."));
647 grub_disk_firmware_fini
= grub_disk_biosdisk_fini
;
649 grub_memset (cdrp
, 0, sizeof (*cdrp
));
650 cdrp
->size
= sizeof (*cdrp
);
651 cdrp
->media_type
= 0xFF;
652 boot_drive
= (grub_boot_device
>> 24);
653 if ((! grub_biosdisk_get_cdinfo_int13_extensions (boot_drive
, cdrp
))
654 && ((cdrp
->media_type
& GRUB_BIOSDISK_CDTYPE_MASK
)
655 == GRUB_BIOSDISK_CDTYPE_NO_EMUL
))
656 cd_drive
= cdrp
->drive_no
;
657 /* Since diskboot.S rejects devices over 0x90 it must be a CD booted with
660 if (boot_drive
>= 0x90)
661 cd_drive
= boot_drive
;
663 grub_disk_dev_register (&grub_biosdisk_dev
);
666 GRUB_MOD_FINI(biosdisk
)
668 grub_disk_biosdisk_fini ();