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 (grub_disk_dev_iterate_hook_t hook
, void *hook_data
,
280 if (cd_drive
&& drive
== cd_drive
)
281 return hook ("cd", hook_data
);
283 grub_snprintf (name
, sizeof (name
),
284 (drive
& 0x80) ? "hd%d" : "fd%d", drive
& (~0x80));
285 return hook (name
, hook_data
);
289 grub_biosdisk_iterate (grub_disk_dev_iterate_hook_t hook
, void *hook_data
,
290 grub_disk_pull_t pull
)
295 /* For hard disks, attempt to read the MBR. */
298 case GRUB_DISK_PULL_NONE
:
299 for (drive
= 0x80; drive
< 0x90; drive
++)
301 if (grub_biosdisk_rw_standard (0x02, drive
, 0, 0, 1, 1,
302 GRUB_MEMORY_MACHINE_SCRATCH_SEG
) != 0)
304 grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive
);
308 if (grub_biosdisk_call_hook (hook
, hook_data
, drive
))
313 case GRUB_DISK_PULL_REMOVABLE
:
316 if (grub_biosdisk_call_hook (hook
, hook_data
, cd_drive
))
320 /* For floppy disks, we can get the number safely. */
321 num_floppies
= grub_biosdisk_get_num_floppies ();
322 for (drive
= 0; drive
< num_floppies
; drive
++)
323 if (grub_biosdisk_call_hook (hook
, hook_data
, drive
))
333 typedef struct ventoy_part_table
335 grub_uint8_t Active
; // 0x00 0x80
337 grub_uint8_t StartHead
;
338 grub_uint16_t StartSector
: 6;
339 grub_uint16_t StartCylinder
: 10;
343 grub_uint8_t EndHead
;
344 grub_uint16_t EndSector
: 6;
345 grub_uint16_t EndCylinder
: 10;
347 grub_uint32_t StartSectorId
;
348 grub_uint32_t SectorCount
;
351 typedef struct ventoy_mbr_head
353 grub_uint8_t BootCode
[446];
354 ventoy_part_table PartTbl
[4];
361 grub_biosdisk_rw (int cmd
, grub_disk_t disk
,
362 grub_disk_addr_t sector
, grub_size_t size
,
365 static int ventoy_is_mbr_match(ventoy_mbr_head
*head
)
367 grub_uint32_t PartStartSector
;
369 if (head
->Byte55
!= 0x55 || head
->ByteAA
!= 0xAA) {
373 if (head
->PartTbl
[2].SectorCount
> 0 || head
->PartTbl
[3].SectorCount
> 0) {
377 if (head
->PartTbl
[0].FsFlag
!= 0x07 || head
->PartTbl
[0].StartSectorId
!= 2048) {
381 if (head
->PartTbl
[1].Active
!= 0x80 || head
->PartTbl
[1].FsFlag
!= 0xEF) {
385 PartStartSector
= head
->PartTbl
[0].StartSectorId
+ head
->PartTbl
[0].SectorCount
;
387 if (head
->PartTbl
[1].StartSectorId
!= PartStartSector
|| head
->PartTbl
[1].SectorCount
!= 65536) {
395 grub_biosdisk_open (const char *name
, grub_disk_t disk
)
397 grub_uint64_t total_sectors
= 0;
399 struct grub_biosdisk_data
*data
;
401 drive
= grub_biosdisk_get_drive (name
);
407 data
= (struct grub_biosdisk_data
*) grub_zalloc (sizeof (*data
));
413 if ((cd_drive
) && (drive
== cd_drive
))
415 data
->flags
= GRUB_BIOSDISK_FLAG_LBA
| GRUB_BIOSDISK_FLAG_CDROM
;
417 disk
->log_sector_size
= 11;
418 /* TODO: get the correct size. */
419 total_sectors
= GRUB_DISK_SIZE_UNKNOWN
;
426 disk
->log_sector_size
= 9;
428 version
= grub_biosdisk_check_int13_extensions (drive
);
431 struct grub_biosdisk_drp
*drp
432 = (struct grub_biosdisk_drp
*) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
;
434 /* Clear out the DRP. */
435 grub_memset (drp
, 0, sizeof (*drp
));
436 drp
->size
= sizeof (*drp
);
437 if (! grub_biosdisk_get_diskinfo_int13_extensions (drive
, drp
))
439 data
->flags
= GRUB_BIOSDISK_FLAG_LBA
;
441 if (drp
->total_sectors
)
442 total_sectors
= drp
->total_sectors
;
444 /* Some buggy BIOSes doesn't return the total sectors
445 correctly but returns zero. So if it is zero, compute
446 it by C/H/S returned by the LBA BIOS call. */
447 total_sectors
= ((grub_uint64_t
) drp
->cylinders
)
448 * drp
->heads
* drp
->sectors
;
449 if (drp
->bytes_per_sector
450 && !(drp
->bytes_per_sector
& (drp
->bytes_per_sector
- 1))
451 && drp
->bytes_per_sector
>= 512
452 && drp
->bytes_per_sector
<= 16384)
454 for (disk
->log_sector_size
= 0;
455 (1 << disk
->log_sector_size
) < drp
->bytes_per_sector
;
456 disk
->log_sector_size
++);
462 if (! (data
->flags
& GRUB_BIOSDISK_FLAG_CDROM
))
464 if (grub_biosdisk_get_diskinfo_standard (drive
,
467 &data
->sectors
) != 0)
469 if (total_sectors
&& (data
->flags
& GRUB_BIOSDISK_FLAG_LBA
))
474 = grub_divmod64 (total_sectors
475 + data
->heads
* data
->sectors
- 1,
476 data
->heads
* data
->sectors
, 0);
481 return grub_error (GRUB_ERR_BAD_DEVICE
, "%s cannot get C/H/S values", disk
->name
);
485 if (data
->sectors
== 0)
487 if (data
->heads
== 0)
491 total_sectors
= ((grub_uint64_t
) data
->cylinders
)
492 * data
->heads
* data
->sectors
;
495 disk
->total_sectors
= total_sectors
;
496 /* Limit the max to 0x7f because of Phoenix EDD. */
497 disk
->max_agglomerate
= 0x7f >> GRUB_DISK_CACHE_BITS
;
498 COMPILE_TIME_ASSERT ((0x7f >> GRUB_DISK_CACHE_BITS
499 << (GRUB_DISK_SECTOR_BITS
+ GRUB_DISK_CACHE_BITS
))
500 + sizeof (struct grub_biosdisk_dap
)
501 < GRUB_MEMORY_MACHINE_SCRATCH_SIZE
);
505 //fixup some buggy bios
506 if (total_sectors
> (16434495 - 2097152) && total_sectors
< (16434495 + 2097152) &&
507 (data
->flags
& GRUB_BIOSDISK_FLAG_LBA
) > 0 && (data
->flags
& GRUB_BIOSDISK_FLAG_CDROM
) == 0) {
508 if (grub_biosdisk_rw(0, disk
, 0, 1, GRUB_MEMORY_MACHINE_SCRATCH_SEG
) == 0) {
509 ventoy_mbr_head
*mbr
= (ventoy_mbr_head
*)GRUB_MEMORY_MACHINE_SCRATCH_ADDR
;
510 if (ventoy_is_mbr_match(mbr
)) {
511 total_sectors
= mbr
->PartTbl
[1].StartSectorId
+ mbr
->PartTbl
[1].SectorCount
+ 1;
512 if (disk
->total_sectors
< total_sectors
) {
513 disk
->total_sectors
= total_sectors
;
519 return GRUB_ERR_NONE
;
523 grub_biosdisk_close (grub_disk_t disk
)
525 grub_free (disk
->data
);
528 /* For readability. */
529 #define GRUB_BIOSDISK_READ 0
530 #define GRUB_BIOSDISK_WRITE 1
532 #define GRUB_BIOSDISK_CDROM_RETRY_COUNT 3
535 grub_biosdisk_rw (int cmd
, grub_disk_t disk
,
536 grub_disk_addr_t sector
, grub_size_t size
,
539 struct grub_biosdisk_data
*data
= disk
->data
;
541 /* VirtualBox fails with sectors above 2T on CDs.
542 Since even BD-ROMS are never that big anyway, return error. */
543 if ((data
->flags
& GRUB_BIOSDISK_FLAG_CDROM
)
545 return grub_error (GRUB_ERR_OUT_OF_RANGE
,
546 N_("attempt to read or write outside of disk `%s'"),
549 if (data
->flags
& GRUB_BIOSDISK_FLAG_LBA
)
551 struct grub_biosdisk_dap
*dap
;
553 dap
= (struct grub_biosdisk_dap
*) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR
555 << disk
->log_sector_size
));
556 dap
->length
= sizeof (*dap
);
559 dap
->buffer
= segment
<< 16; /* The format SEGMENT:ADDRESS. */
562 if (data
->flags
& GRUB_BIOSDISK_FLAG_CDROM
)
567 return grub_error (GRUB_ERR_WRITE_ERROR
, N_("cannot write to CD-ROM"));
569 for (i
= 0; i
< GRUB_BIOSDISK_CDROM_RETRY_COUNT
; i
++)
570 if (! grub_biosdisk_rw_int13_extensions (0x42, data
->drive
, dap
))
573 if (i
== GRUB_BIOSDISK_CDROM_RETRY_COUNT
)
574 return grub_error (GRUB_ERR_READ_ERROR
, N_("failure reading sector 0x%llx "
576 (unsigned long long) sector
,
580 if (grub_biosdisk_rw_int13_extensions (cmd
+ 0x42, data
->drive
, dap
))
582 /* Fall back to the CHS mode. */
583 data
->flags
&= ~GRUB_BIOSDISK_FLAG_LBA
;
584 disk
->total_sectors
= data
->cylinders
* data
->heads
* data
->sectors
;
585 return grub_biosdisk_rw (cmd
, disk
, sector
, size
, segment
);
590 unsigned coff
, hoff
, soff
;
593 /* It is impossible to reach over 8064 MiB (a bit less than LBA24) with
594 the traditional CHS access. */
596 1024 /* cylinders */ *
599 return grub_error (GRUB_ERR_OUT_OF_RANGE
,
600 N_("attempt to read or write outside of disk `%s'"),
603 soff
= ((grub_uint32_t
) sector
) % data
->sectors
+ 1;
604 head
= ((grub_uint32_t
) sector
) / data
->sectors
;
605 hoff
= head
% data
->heads
;
606 coff
= head
/ data
->heads
;
608 if (coff
>= data
->cylinders
)
609 return grub_error (GRUB_ERR_OUT_OF_RANGE
,
610 N_("attempt to read or write outside of disk `%s'"),
613 if (grub_biosdisk_rw_standard (cmd
+ 0x02, data
->drive
,
614 coff
, hoff
, soff
, size
, segment
))
618 case GRUB_BIOSDISK_READ
:
619 return grub_error (GRUB_ERR_READ_ERROR
, N_("failure reading sector 0x%llx "
621 (unsigned long long) sector
,
623 case GRUB_BIOSDISK_WRITE
:
624 return grub_error (GRUB_ERR_WRITE_ERROR
, N_("failure writing sector 0x%llx "
626 (unsigned long long) sector
,
632 return GRUB_ERR_NONE
;
635 /* Return the number of sectors which can be read safely at a time. */
637 get_safe_sectors (grub_disk_t disk
, grub_disk_addr_t sector
)
640 grub_uint64_t offset
;
641 struct grub_biosdisk_data
*data
= disk
->data
;
642 grub_uint32_t sectors
= data
->sectors
;
644 /* OFFSET = SECTOR % SECTORS */
645 grub_divmod64 (sector
, sectors
, &offset
);
647 size
= sectors
- offset
;
653 grub_biosdisk_read (grub_disk_t disk
, grub_disk_addr_t sector
,
654 grub_size_t size
, char *buf
)
660 len
= get_safe_sectors (disk
, sector
);
665 if (grub_biosdisk_rw (GRUB_BIOSDISK_READ
, disk
, sector
, len
,
666 GRUB_MEMORY_MACHINE_SCRATCH_SEG
))
669 grub_memcpy (buf
, (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
,
670 len
<< disk
->log_sector_size
);
672 buf
+= len
<< disk
->log_sector_size
;
681 grub_biosdisk_write (grub_disk_t disk
, grub_disk_addr_t sector
,
682 grub_size_t size
, const char *buf
)
684 struct grub_biosdisk_data
*data
= disk
->data
;
686 if (data
->flags
& GRUB_BIOSDISK_FLAG_CDROM
)
687 return grub_error (GRUB_ERR_IO
, N_("cannot write to CD-ROM"));
693 len
= get_safe_sectors (disk
, sector
);
697 grub_memcpy ((void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
, buf
,
698 len
<< disk
->log_sector_size
);
700 if (grub_biosdisk_rw (GRUB_BIOSDISK_WRITE
, disk
, sector
, len
,
701 GRUB_MEMORY_MACHINE_SCRATCH_SEG
))
704 buf
+= len
<< disk
->log_sector_size
;
712 static struct grub_disk_dev grub_biosdisk_dev
=
715 .id
= GRUB_DISK_DEVICE_BIOSDISK_ID
,
716 .disk_iterate
= grub_biosdisk_iterate
,
717 .disk_open
= grub_biosdisk_open
,
718 .disk_close
= grub_biosdisk_close
,
719 .disk_read
= grub_biosdisk_read
,
720 .disk_write
= grub_biosdisk_write
,
725 grub_disk_biosdisk_fini (void)
727 grub_disk_dev_unregister (&grub_biosdisk_dev
);
730 GRUB_MOD_INIT(biosdisk
)
732 struct grub_biosdisk_cdrp
*cdrp
733 = (struct grub_biosdisk_cdrp
*) GRUB_MEMORY_MACHINE_SCRATCH_ADDR
;
734 grub_uint8_t boot_drive
;
736 if (grub_disk_firmware_is_tainted
)
738 grub_puts_ (N_("Native disk drivers are in use. "
739 "Refusing to use firmware disk interface."));
742 grub_disk_firmware_fini
= grub_disk_biosdisk_fini
;
744 grub_memset (cdrp
, 0, sizeof (*cdrp
));
745 cdrp
->size
= sizeof (*cdrp
);
746 cdrp
->media_type
= 0xFF;
747 boot_drive
= (grub_boot_device
>> 24);
748 if ((! grub_biosdisk_get_cdinfo_int13_extensions (boot_drive
, cdrp
))
749 && ((cdrp
->media_type
& GRUB_BIOSDISK_CDTYPE_MASK
)
750 == GRUB_BIOSDISK_CDTYPE_NO_EMUL
))
751 cd_drive
= cdrp
->drive_no
;
752 /* Since diskboot.S rejects devices over 0x90 it must be a CD booted with
755 if (boot_drive
>= 0x90)
756 cd_drive
= boot_drive
;
758 grub_disk_dev_register (&grub_biosdisk_dev
);
761 GRUB_MOD_FINI(biosdisk
)
763 grub_disk_biosdisk_fini ();