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>
31
static int cd_drive = 0;
32
static int grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap);
34
static int grub_biosdisk_get_num_floppies (void)
36
struct grub_bios_int_registers regs;
39
/* reset the disk system first */
42
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
44
grub_bios_interrupt (0x13, ®s);
46
for (drive = 0; drive < 2; drive++)
48
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT | GRUB_CPU_INT_FLAGS_CARRY;
51
/* call GET DISK TYPE */
53
grub_bios_interrupt (0x13, ®s);
54
if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
57
/* check if this drive exists */
58
if (!(regs.eax & 0x300))
66
* Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP
67
* is passed for disk address packet. If an error occurs, return
68
* non-zero, otherwise zero.
72
grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap)
74
struct grub_bios_int_registers regs;
76
/* compute the address of disk_address_packet */
77
regs.ds = (((grub_addr_t) dap) & 0xffff0000) >> 4;
78
regs.esi = (((grub_addr_t) dap) & 0xffff);
80
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
82
grub_bios_interrupt (0x13, ®s);
83
return (regs.eax >> 8) & 0xff;
87
* Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
88
* NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
89
* return non-zero, otherwise zero.
92
grub_biosdisk_rw_standard (int ah, int drive, int coff, int hoff,
93
int soff, int nsec, int segment)
98
for (i = 0; i < 3; i++)
100
struct grub_bios_int_registers regs;
102
/* set up CHS information */
103
/* set %ch to low eight bits of cylinder */
104
regs.ecx = (coff << 8) & 0xff00;
105
/* set bits 6-7 of %cl to high two bits of cylinder */
106
regs.ecx |= (coff >> 2) & 0xc0;
107
/* set bits 0-5 of %cl to sector */
108
regs.ecx |= soff & 0x3f;
110
/* set %dh to head and %dl to drive */
111
regs.edx = (drive & 0xff) | ((hoff << 8) & 0xff00);
113
regs.eax = (ah << 8) & 0xff00;
114
/* set %al to NSEC */
115
regs.eax |= nsec & 0xff;
120
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
122
grub_bios_interrupt (0x13, ®s);
123
/* check if successful */
124
if (!(regs.flags & GRUB_CPU_INT_FLAGS_CARRY))
127
/* save return value */
130
/* if fail, reset the disk system */
132
regs.edx = (drive & 0xff);
133
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
134
grub_bios_interrupt (0x13, ®s);
140
* Check if LBA is supported for DRIVE. If it is supported, then return
141
* the major version of extensions, otherwise zero.
144
grub_biosdisk_check_int13_extensions (int drive)
146
struct grub_bios_int_registers regs;
148
regs.edx = drive & 0xff;
151
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
152
grub_bios_interrupt (0x13, ®s);
154
if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
157
if ((regs.ebx & 0xffff) != 0xaa55)
160
/* check if AH=0x42 is supported */
164
return (regs.eax >> 8) & 0xff;
168
* Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
169
* error occurs, then return non-zero, otherwise zero.
172
grub_biosdisk_get_diskinfo_standard (int drive,
173
unsigned long *cylinders,
174
unsigned long *heads,
175
unsigned long *sectors)
177
struct grub_bios_int_registers regs;
180
regs.edx = drive & 0xff;
182
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
183
grub_bios_interrupt (0x13, ®s);
185
/* Check if unsuccessful. Ignore return value if carry isn't set to
186
workaround some buggy BIOSes. */
187
if ((regs.flags & GRUB_CPU_INT_FLAGS_CARRY) && ((regs.eax & 0xff00) != 0))
188
return (regs.eax & 0xff00) >> 8;
190
/* bogus BIOSes may not return an error number */
191
/* 0 sectors means no disk */
192
if (!(regs.ecx & 0x3f))
193
/* XXX 0x60 is one of the unused error numbers */
196
/* the number of heads is counted from zero */
197
*heads = ((regs.edx >> 8) & 0xff) + 1;
198
*cylinders = (((regs.ecx >> 8) & 0xff) | ((regs.ecx << 2) & 0x0300)) + 1;
199
*sectors = regs.ecx & 0x3f;
204
grub_biosdisk_get_diskinfo_real (int drive, void *drp, grub_uint16_t ax)
206
struct grub_bios_int_registers regs;
210
/* compute the address of drive parameters */
211
regs.esi = ((grub_addr_t) drp) & 0xf;
212
regs.ds = ((grub_addr_t) drp) >> 4;
213
regs.edx = drive & 0xff;
215
regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
216
grub_bios_interrupt (0x13, ®s);
218
/* Check if unsuccessful. Ignore return value if carry isn't set to
219
workaround some buggy BIOSes. */
220
if ((regs.flags & GRUB_CPU_INT_FLAGS_CARRY) && ((regs.eax & 0xff00) != 0))
221
return (regs.eax & 0xff00) >> 8;
227
* Return the cdrom information of DRIVE in CDRP. If an error occurs,
228
* then return non-zero, otherwise zero.
231
grub_biosdisk_get_cdinfo_int13_extensions (int drive, void *cdrp)
233
return grub_biosdisk_get_diskinfo_real (drive, cdrp, 0x4b01);
237
* Return the geometry of DRIVE in a drive parameters, DRP. If an error
238
* occurs, then return non-zero, otherwise zero.
241
grub_biosdisk_get_diskinfo_int13_extensions (int drive, void *drp)
243
return grub_biosdisk_get_diskinfo_real (drive, drp, 0x4800);
247
grub_biosdisk_get_drive (const char *name)
251
if (name[0] == 'c' && name[1] == 'd' && name[2] == 0 && cd_drive)
254
if ((name[0] != 'f' && name[0] != 'h') || name[1] != 'd')
257
drive = grub_strtoul (name + 2, 0, 10);
258
if (grub_errno != GRUB_ERR_NONE)
267
grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a biosdisk");
272
grub_biosdisk_call_hook (int (*hook) (const char *name), int drive)
276
if (cd_drive && drive == cd_drive)
279
grub_snprintf (name, sizeof (name),
280
(drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80));
285
grub_biosdisk_iterate (int (*hook) (const char *name))
290
/* For hard disks, attempt to read the MBR. */
291
for (drive = 0x80; drive < 0x90; drive++)
293
if (grub_biosdisk_rw_standard (0x02, drive, 0, 0, 1, 1,
294
GRUB_MEMORY_MACHINE_SCRATCH_SEG) != 0)
296
grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive);
300
if (grub_biosdisk_call_hook (hook, drive))
306
if (grub_biosdisk_call_hook (hook, cd_drive))
310
/* For floppy disks, we can get the number safely. */
311
num_floppies = grub_biosdisk_get_num_floppies ();
312
for (drive = 0; drive < num_floppies; drive++)
313
if (grub_biosdisk_call_hook (hook, drive))
320
grub_biosdisk_open (const char *name, grub_disk_t disk)
322
grub_uint64_t total_sectors = 0;
324
struct grub_biosdisk_data *data;
326
drive = grub_biosdisk_get_drive (name);
332
data = (struct grub_biosdisk_data *) grub_zalloc (sizeof (*data));
338
if ((cd_drive) && (drive == cd_drive))
340
data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM;
342
/* TODO: get the correct size. */
343
total_sectors = GRUB_DISK_SIZE_UNKNOWN;
350
version = grub_biosdisk_check_int13_extensions (drive);
353
struct grub_biosdisk_drp *drp
354
= (struct grub_biosdisk_drp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
356
/* Clear out the DRP. */
357
grub_memset (drp, 0, sizeof (*drp));
358
drp->size = sizeof (*drp);
359
if (! grub_biosdisk_get_diskinfo_int13_extensions (drive, drp))
361
data->flags = GRUB_BIOSDISK_FLAG_LBA;
363
if (drp->total_sectors)
364
total_sectors = drp->total_sectors;
366
/* Some buggy BIOSes doesn't return the total sectors
367
correctly but returns zero. So if it is zero, compute
368
it by C/H/S returned by the LBA BIOS call. */
369
total_sectors = drp->cylinders * drp->heads * drp->sectors;
374
if (! (data->flags & GRUB_BIOSDISK_FLAG_CDROM))
376
if (grub_biosdisk_get_diskinfo_standard (drive,
379
&data->sectors) != 0)
381
if (total_sectors && (data->flags & GRUB_BIOSDISK_FLAG_LBA))
386
= grub_divmod64 (total_sectors
387
+ data->heads * data->sectors - 1,
388
data->heads * data->sectors, 0);
393
return grub_error (GRUB_ERR_BAD_DEVICE, "%s cannot get C/H/S values", disk->name);
398
total_sectors = data->cylinders * data->heads * data->sectors;
401
disk->total_sectors = total_sectors;
404
return GRUB_ERR_NONE;
408
grub_biosdisk_close (grub_disk_t disk)
410
grub_free (disk->data);
413
/* For readability. */
414
#define GRUB_BIOSDISK_READ 0
415
#define GRUB_BIOSDISK_WRITE 1
417
#define GRUB_BIOSDISK_CDROM_RETRY_COUNT 3
420
grub_biosdisk_rw (int cmd, grub_disk_t disk,
421
grub_disk_addr_t sector, grub_size_t size,
424
struct grub_biosdisk_data *data = disk->data;
426
if (data->flags & GRUB_BIOSDISK_FLAG_LBA)
428
struct grub_biosdisk_dap *dap;
430
dap = (struct grub_biosdisk_dap *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR
432
<< GRUB_DISK_SECTOR_BITS));
433
dap->length = sizeof (*dap);
436
dap->buffer = segment << 16; /* The format SEGMENT:ADDRESS. */
439
if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
444
return grub_error (GRUB_ERR_WRITE_ERROR, "can\'t write to cdrom");
446
dap->blocks = ALIGN_UP (dap->blocks, 4) >> 2;
449
for (i = 0; i < GRUB_BIOSDISK_CDROM_RETRY_COUNT; i++)
450
if (! grub_biosdisk_rw_int13_extensions (0x42, data->drive, dap))
453
if (i == GRUB_BIOSDISK_CDROM_RETRY_COUNT)
454
return grub_error (GRUB_ERR_READ_ERROR, "cdrom read error");
457
if (grub_biosdisk_rw_int13_extensions (cmd + 0x42, data->drive, dap))
459
/* Fall back to the CHS mode. */
460
data->flags &= ~GRUB_BIOSDISK_FLAG_LBA;
461
disk->total_sectors = data->cylinders * data->heads * data->sectors;
462
return grub_biosdisk_rw (cmd, disk, sector, size, segment);
467
unsigned coff, hoff, soff;
470
/* It is impossible to reach over 8064 MiB (a bit less than LBA24) with
471
the traditional CHS access. */
473
1024 /* cylinders */ *
476
return grub_error (GRUB_ERR_OUT_OF_RANGE, "%s out of disk", disk->name);
478
soff = ((grub_uint32_t) sector) % data->sectors + 1;
479
head = ((grub_uint32_t) sector) / data->sectors;
480
hoff = head % data->heads;
481
coff = head / data->heads;
483
if (coff >= data->cylinders)
484
return grub_error (GRUB_ERR_OUT_OF_RANGE, "%s out of disk", disk->name);
486
if (grub_biosdisk_rw_standard (cmd + 0x02, data->drive,
487
coff, hoff, soff, size, segment))
491
case GRUB_BIOSDISK_READ:
492
return grub_error (GRUB_ERR_READ_ERROR, "%s read error", disk->name);
493
case GRUB_BIOSDISK_WRITE:
494
return grub_error (GRUB_ERR_WRITE_ERROR, "%s write error", disk->name);
499
return GRUB_ERR_NONE;
502
/* Return the number of sectors which can be read safely at a time. */
504
get_safe_sectors (grub_disk_addr_t sector, grub_uint32_t sectors)
507
grub_uint32_t offset;
509
/* OFFSET = SECTOR % SECTORS */
510
grub_divmod64 (sector, sectors, &offset);
512
size = sectors - offset;
514
/* Limit the max to 0x7f because of Phoenix EDD. */
522
grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
523
grub_size_t size, char *buf)
525
struct grub_biosdisk_data *data = disk->data;
530
grub_size_t cdoff = 0;
532
len = get_safe_sectors (sector, data->sectors);
534
if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
536
cdoff = (sector & 3) << GRUB_DISK_SECTOR_BITS;
537
len = ALIGN_UP (sector + len, 4) - (sector & ~3);
544
if (grub_biosdisk_rw (GRUB_BIOSDISK_READ, disk, sector, len,
545
GRUB_MEMORY_MACHINE_SCRATCH_SEG))
548
grub_memcpy (buf, (void *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + cdoff),
549
len << GRUB_DISK_SECTOR_BITS);
550
buf += len << GRUB_DISK_SECTOR_BITS;
559
grub_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
560
grub_size_t size, const char *buf)
562
struct grub_biosdisk_data *data = disk->data;
564
if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
565
return grub_error (GRUB_ERR_IO, "can't write to CDROM");
571
len = get_safe_sectors (sector, data->sectors);
575
grub_memcpy ((void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, buf,
576
len << GRUB_DISK_SECTOR_BITS);
578
if (grub_biosdisk_rw (GRUB_BIOSDISK_WRITE, disk, sector, len,
579
GRUB_MEMORY_MACHINE_SCRATCH_SEG))
582
buf += len << GRUB_DISK_SECTOR_BITS;
590
static struct grub_disk_dev grub_biosdisk_dev =
593
.id = GRUB_DISK_DEVICE_BIOSDISK_ID,
594
.iterate = grub_biosdisk_iterate,
595
.open = grub_biosdisk_open,
596
.close = grub_biosdisk_close,
597
.read = grub_biosdisk_read,
598
.write = grub_biosdisk_write,
603
grub_disk_biosdisk_fini (void)
605
grub_disk_dev_unregister (&grub_biosdisk_dev);
608
GRUB_MOD_INIT(biosdisk)
610
struct grub_biosdisk_cdrp *cdrp
611
= (struct grub_biosdisk_cdrp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
613
if (grub_disk_firmware_is_tainted)
615
grub_printf ("Firmware is marked as tainted, refusing to initialize.\n");
618
grub_disk_firmware_fini = grub_disk_biosdisk_fini;
620
grub_memset (cdrp, 0, sizeof (*cdrp));
621
cdrp->size = sizeof (*cdrp);
622
cdrp->media_type = 0xFF;
623
if ((! grub_biosdisk_get_cdinfo_int13_extensions (grub_boot_drive, cdrp)) &&
624
((cdrp->media_type & GRUB_BIOSDISK_CDTYPE_MASK)
625
== GRUB_BIOSDISK_CDTYPE_NO_EMUL))
626
cd_drive = cdrp->drive_no;
627
/* Since diskboot.S rejects devices over 0x90 it must be a CD booted with
630
if (grub_boot_drive >= 0x90)
631
cd_drive = grub_boot_drive;
633
grub_disk_dev_register (&grub_biosdisk_dev);
636
GRUB_MOD_FINI(biosdisk)
638
grub_disk_biosdisk_fini ();