1
/* bios.c - implement C part of low-level BIOS disk input and output */
3
* GRUB -- GRand Unified Bootloader
4
* Copyright (C) 1999,2000,2003,2004 Free Software Foundation, Inc.
6
* This program 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 2 of the License, or
9
* (at your option) any later version.
11
* This program 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 this program; if not, write to the Free Software
18
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24
/* These are defined in asm.S, and never be used elsewhere, so declare the
26
extern int biosdisk_standard (int ah, int drive,
27
int coff, int hoff, int soff,
28
int nsec, int segment);
29
extern int get_diskinfo_standard (int drive,
30
unsigned long *cylinders,
32
unsigned long *sectors);
34
extern struct drive_map_slot hooked_drive_map[DRIVE_MAP_SIZE + 1];
35
extern int drive_map_slot_empty (struct drive_map_slot item);
37
/* Read/write NSEC sectors starting from SECTOR in DRIVE disk with GEOMETRY
38
from/into SEGMENT segment. If READ is BIOSDISK_READ, then read it,
39
else if READ is BIOSDISK_WRITE, then write it. If an geometry error
40
occurs, return BIOSDISK_ERROR_GEOMETRY, and if other error occurs, then
41
return the error number. Otherwise, return 0. */
43
biosdisk (int read, int drive, struct geometry *geometry,
44
int sector, int nsec, int segment)
48
/* first, use EBIOS if possible */
49
if ((geometry->flags & BIOSDISK_FLAG_LBA_EXTENSION) && (! (geometry->flags & BIOSDISK_FLAG_BIFURCATE) || (drive & 0xFFFFFF00) == 0x100))
51
struct disk_address_packet
54
unsigned char reserved;
55
unsigned short blocks;
57
unsigned long long block;
59
/* This structure is passed in the stack. A buggy BIOS could write
60
* garbage data to the tail of the struct and hang the machine. So
61
* we need this protection. - Tinybit
63
unsigned char dummy[16];
64
} __attribute__ ((packed)) *dap;
66
/* Even the above protection is not enough to avoid stupid actions by
67
* buggy BIOSes. So we do it in the 0040:0000 segment. - Tinybit
69
dap = (struct disk_address_packet *)0x580;
71
if (drive == 0xffff || (drive == ram_drive && rd_base != 0xffffffff))
76
if (nsec <=0 || nsec >= 0x80)
77
return 1; /* failure */
79
disk_sector = (char *)((sector<<9) + ((drive==0xffff) ? 0 : rd_base));
80
buf_address = (char *)(segment<<4);
82
if (read) /* read == 1 really means write to DISK */
83
grub_memmove (disk_sector, buf_address, nsec << 9);
84
else /* read == 0 really means read from DISK */
85
grub_memmove (buf_address, disk_sector, nsec << 9);
87
return 0; /* success */
94
dap->buffer = segment << 16;
96
err = biosdisk_int13_extensions ((read + 0x42) << 8, (unsigned char)drive, dap);
99
return 0; /* success */
101
/* bootable CD-ROM specification has no standard CHS-mode call */
102
if (geometry->flags & BIOSDISK_FLAG_CDROM)
106
grub_printf ("biosdisk_int13_extensions read=%d, drive=0x%x, dap=%x, err=0x%x\n", read, drive, dap, err);
111
if (geometry->flags & BIOSDISK_FLAG_BIFURCATE)
114
} /* if (geometry->flags & BIOSDISK_FLAG_LBA_EXTENSION) */
116
/* try the standard CHS mode */
119
int cylinder_offset, head_offset, sector_offset;
122
/* SECTOR_OFFSET is counted from one, while HEAD_OFFSET and
123
CYLINDER_OFFSET are counted from zero. */
124
sector_offset = sector % geometry->sectors + 1;
125
head = sector / geometry->sectors;
126
head_offset = head % geometry->heads;
127
cylinder_offset = head / geometry->heads;
129
err = biosdisk_standard (read + 0x02, drive,
130
cylinder_offset, head_offset, sector_offset,
137
/* Check bootable CD-ROM emulation status. Return 0 on failure. */
139
get_cdinfo (int drive, struct geometry *geometry)
142
struct iso_spec_packet
145
unsigned char media_type;
146
unsigned char drive_no;
147
unsigned char controller_no;
148
unsigned long image_lba;
149
unsigned short device_spec;
150
unsigned short cache_seg;
151
unsigned short load_seg;
152
unsigned short length_sec512;
153
unsigned char cylinders;
154
unsigned char sectors;
157
unsigned char dummy[16];
158
} __attribute__ ((packed));
160
struct iso_spec_packet *cdrp;
162
cdrp = (struct iso_spec_packet *)0x580;
163
grub_memset (cdrp, 0, sizeof (struct iso_spec_packet));
164
cdrp->size = sizeof (struct iso_spec_packet) - 16;
168
grub_printf (" int13/4B01(%X),", drive);
170
err = biosdisk_int13_extensions (0x4B01, drive, cdrp);
173
grub_printf ("err=%X,drive=%X, ", err, drive);
176
if (drive == 0x7F && drive < (unsigned long)(cdrp->drive_no))
177
drive = cdrp->drive_no;
179
if (! err && cdrp->drive_no == drive && !(cdrp->media_type & 0x0F))
181
/* No-emulation mode bootable CD-ROM */
182
geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION | BIOSDISK_FLAG_CDROM;
183
geometry->cylinders = 65536;
184
geometry->heads = 255;
185
geometry->sectors = 15;
186
geometry->sector_size = 2048;
187
geometry->total_sectors = 65536 * 255 * 15;
190
return 0; /* failure */
193
static unsigned long flags;
194
static unsigned long cylinders;
195
static unsigned long heads;
196
static unsigned long sectors;
199
/* Return the geometry of DRIVE in GEOMETRY. If an error occurs, return
200
non-zero, otherwise zero. */
202
get_diskinfo (int drive, struct geometry *geometry)
206
unsigned long long total_sectors = 0, tmp = 0;
208
if (drive == 0xffff) /* memory disk */
210
unsigned long long total_mem_bytes;
214
if (mbi.flags & MB_INFO_MEM_MAP)
216
struct AddrRangeDesc *map = (struct AddrRangeDesc *) saved_mmap_addr;
217
unsigned long end_addr = saved_mmap_addr + saved_mmap_length;
219
for (; end_addr > (unsigned long) map; map = (struct AddrRangeDesc *) (((int) map) + 4 + map->size))
221
unsigned long long top_end;
223
if (map->Type != MB_ARD_MEMORY)
225
top_end = map->BaseAddr + map->Length;
226
if (top_end > 0x100000000ULL)
227
top_end = 0x100000000ULL;
228
if (total_mem_bytes < top_end)
229
total_mem_bytes = top_end;
234
grub_printf ("Address Map BIOS Interface is not activated.\n");
238
geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION;
239
geometry->sector_size = SECTOR_SIZE;
240
geometry->total_sectors = (total_mem_bytes /*+ SECTOR_SIZE - 1*/) >> SECTOR_BITS;
241
geometry->heads = 255;
242
geometry->sectors = 63;
243
geometry->cylinders = (geometry->total_sectors + 255 * 63 -1) / (255 * 63);
247
} else if (drive == ram_drive) /* ram disk device */
249
if (rd_base != 0xffffffff)
251
geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION;
252
geometry->sector_size = SECTOR_SIZE;
253
geometry->total_sectors = (rd_size ? ((rd_size + SECTOR_SIZE - 1)>> SECTOR_BITS) : 0x800000);
254
geometry->heads = 255;
255
geometry->sectors = 63;
256
geometry->cylinders = (geometry->total_sectors + 255 * 63 -1) / (255 * 63);
261
#if defined(GRUB_UTIL) || defined(STAGE1_5)
262
if (drive == cdrom_drive)
264
if (drive == cdrom_drive || (drive >= (unsigned char)min_cdrom_id && drive < (unsigned char)(min_cdrom_id + atapi_dev_count)))
267
/* No-emulation mode bootable CD-ROM */
268
geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION | BIOSDISK_FLAG_CDROM;
269
geometry->cylinders = 65536;
270
geometry->heads = 255;
271
geometry->sectors = 15;
272
geometry->sector_size = 2048;
273
geometry->total_sectors = 65536 * 255 * 15;
277
/* Clear the flags. */
281
#define FIND_DRIVES 8
283
#define FIND_DRIVES (*((char *)0x475))
285
if (((unsigned char)drive) >= 0x80 + FIND_DRIVES /* || (version && (drive & 0x80)) */ )
287
/* Possible CD-ROM - check the status. */
288
if (get_cdinfo ((unsigned char)drive, geometry))
292
#if (! defined(GRUB_UTIL)) && (! defined(STAGE1_5))
297
/* check if the drive is virtual. */
298
d = (unsigned char)drive;
299
j = DRIVE_MAP_SIZE; /* real drive */
300
if (! unset_int13_handler (1))
301
for (j = 0; j < DRIVE_MAP_SIZE; j++)
303
if (drive_map_slot_empty (hooked_drive_map[j]))
305
j = DRIVE_MAP_SIZE; /* real drive */
309
if (((unsigned char)drive) != hooked_drive_map[j].from_drive)
311
if ((hooked_drive_map[j].max_sector & 0x3E) == 0 && hooked_drive_map[j].start_sector == 0 && hooked_drive_map[j].sector_count <= 1)
313
/* this is a map for the whole drive. */
314
d = hooked_drive_map[j].to_drive;
315
j = DRIVE_MAP_SIZE; /* real drive */
320
if (j == DRIVE_MAP_SIZE) /* real drive */
322
if (d >= 0x80 && d < 0x84)
325
if (hd_geom[d].sector_size == 512 && hd_geom[d].sectors > 0 && hd_geom[d].sectors <= 63 && hd_geom[d].heads <= 256)
327
geometry->flags = hd_geom[d].flags;
328
if ((geometry->flags & BIOSDISK_FLAG_BIFURCATE) && (drive & 0xFFFFFF00) == 0x100)
330
if (geometry->flags & BIOSDISK_FLAG_CDROM)
332
geometry->cylinders = 65536;
333
geometry->heads = 255;
334
geometry->sectors = 15;
335
geometry->sector_size = 2048;
336
geometry->total_sectors = 65536 * 255 * 15;
340
geometry->sector_size = hd_geom[d].sector_size;
341
geometry->total_sectors = hd_geom[d].total_sectors;
342
geometry->heads = hd_geom[d].heads;
343
geometry->sectors = hd_geom[d].sectors;
344
geometry->cylinders = hd_geom[d].cylinders;
348
if (fd_geom[d].sector_size == 512 && fd_geom[d].sectors > 0 && fd_geom[d].sectors <= 63 && fd_geom[d].heads <= 256)
350
geometry->flags = fd_geom[d].flags;
351
if ((geometry->flags & BIOSDISK_FLAG_BIFURCATE) && (drive & 0xFFFFFF00) == 0x100)
353
if (geometry->flags & BIOSDISK_FLAG_CDROM)
355
geometry->cylinders = 65536;
356
geometry->heads = 255;
357
geometry->sectors = 15;
358
geometry->sector_size = 2048;
359
geometry->total_sectors = 65536 * 255 * 15;
363
geometry->sector_size = fd_geom[d].sector_size;
364
geometry->total_sectors = fd_geom[d].total_sectors;
365
geometry->heads = fd_geom[d].heads;
366
geometry->sectors = fd_geom[d].sectors;
367
geometry->cylinders = fd_geom[d].cylinders;
377
grub_printf (" int13/41(%X),", drive);
379
version = check_int13_extensions ((unsigned char)drive);
382
grub_printf ("version=%X, ", version);
385
/* Set the LBA flag. */
386
if (version & 1) /* support functions 42h-44h, 47h-48h */
388
flags = BIOSDISK_FLAG_LBA_EXTENSION;
394
grub_printf (" int13/08(%X),", drive);
397
version = get_diskinfo_standard ((unsigned char)drive, &cylinders, &heads, §ors);
401
grub_printf ("version=%X, C/H/S=%d/%d/%d, ", version, cylinders, heads, sectors);
406
grub_printf (" int13/02(%X),", drive);
409
/* read the boot sector: int 13, AX=0x201, CX=1, DH=0 */
410
err = biosdisk_standard (0x02, (unsigned char)drive, 0, 0, 1, 1, 0x5F00/*SCRATCHSEG*/);
414
grub_printf ("err=%X, ", err);
419
/* try again using LBA */
420
if (flags & BIOSDISK_FLAG_LBA_EXTENSION || ((unsigned char)drive) >= 0x80 + FIND_DRIVES)
422
struct disk_address_packet
424
unsigned char length;
425
unsigned char reserved;
426
unsigned short blocks;
427
unsigned long buffer;
428
unsigned long long block;
430
unsigned char dummy[16];
431
} __attribute__ ((packed)) *dap;
433
dap = (struct disk_address_packet *)0x580;
438
dap->buffer = 0x5F80/*SCRATCHSEG*/ << 16;
441
/* set a known value */
442
grub_memset ((char *)0x5F800, 0xEC, 0x800);
443
version = biosdisk_int13_extensions (0x4200, (unsigned char)drive, dap);
444
/* see if it is a big sector */
447
for (p = (char *)0x5FA00; p < (char *)0x60000; p++)
449
if ((*p) != (char)0xEC)
451
flags |= BIOSDISK_FLAG_CDROM | BIOSDISK_FLAG_LBA_EXTENSION;
453
flags |= BIOSDISK_FLAG_BIFURCATE;
458
if ((! version) && (! err))
460
if (grub_memcmp ((char *)0x5F000, (char *)0x5F800, 0x200))
462
flags |= BIOSDISK_FLAG_BIFURCATE;
465
if (err && ! (flags & BIOSDISK_FLAG_BIFURCATE) && !(flags & BIOSDISK_FLAG_CDROM))
467
grub_memmove ((char *)0x5F000, (char *)0x5F800, 0x200);
470
} /* if (geometry->flags & BIOSDISK_FLAG_LBA_EXTENSION) */
473
return err; /* When we return with ERROR, we should not change the geometry!! */
475
geometry->flags = flags;
477
if (err && (flags & BIOSDISK_FLAG_CDROM))
479
geometry->cylinders = 65536;
480
geometry->heads = 255;
481
geometry->sectors = 15;
482
geometry->sector_size = 2048;
483
geometry->total_sectors = 65536 * 255 * 15;
487
geometry->cylinders = cylinders;
488
geometry->heads = heads;
489
geometry->sectors = sectors;
490
geometry->sector_size = SECTOR_SIZE;
491
if (geometry->heads > 256)
492
geometry->heads = 256;
493
if (geometry->sectors * geometry->sector_size > 63 * 512)
494
geometry->sectors = 63 * 512 / geometry->sector_size;
495
tmp = (unsigned long long)(geometry->cylinders) *
496
(unsigned long long)(geometry->heads) *
497
(unsigned long long)(geometry->sectors);
498
if (total_sectors < tmp)
500
geometry->total_sectors = total_sectors;
502
/* successfully read boot sector */
508
if ((err = probe_mbr((struct master_and_dos_boot_sector *)0x5F000/*SCRATCHADDR*/, 0, total_sectors, 0)))
511
grub_printf ("\nWarning: Unrecognized partition table for drive %X. Please rebuild it using\na Microsoft-compatible FDISK tool(err=%d). Current C/H/S=%d/%d/%d\n", drive, err, geometry->cylinders, geometry->heads, geometry->sectors);
512
goto failure_probe_boot_sector;
517
if (probe_bpb((struct master_and_dos_boot_sector *)0x5F000/*SCRATCHADDR*/))
519
goto failure_probe_boot_sector;
526
if (probed_cylinders != geometry->cylinders)
528
grub_printf ("\nWarning: %s cylinders(%d) is not equal to the BIOS one(%d).\n", (char *)err, probed_cylinders, geometry->cylinders);
530
geometry->cylinders = probed_cylinders;
532
if (probed_heads != geometry->heads)
534
grub_printf ("\nWarning: %s heads(%d) is not equal to the BIOS one(%d).\n", (char *)err, probed_heads, geometry->heads);
536
geometry->heads = probed_heads;
538
if (probed_sectors_per_track != geometry->sectors)
540
grub_printf ("\nWarning: %s sectors per track(%d) is not equal to the BIOS one(%d).\n", (char *)err, probed_sectors_per_track, geometry->sectors);
542
geometry->sectors = probed_sectors_per_track;
544
if (probed_total_sectors > total_sectors)
548
grub_printf ("\nWarning: %s total sectors(%d) is greater than the BIOS one(%d).\nSome buggy BIOSes could hang when you access sectors exceeding the BIOS limit.\n", (char *)err, probed_total_sectors, total_sectors);
549
geometry->total_sectors = probed_total_sectors;
553
if (probed_total_sectors < total_sectors)
555
grub_printf ("\nWarning: %s total sectors(%d) is less than the BIOS one(%d).\n", (char *)err, probed_total_sectors, total_sectors);
557
failure_probe_boot_sector:
561
if (!(geometry->flags & BIOSDISK_FLAG_LBA_EXTENSION) && ! ((*(char *)0x8205) & 0x08))
563
err = geometry->heads;
564
version = geometry->sectors;
566
/* DH non-zero for geometry_tune */
567
get_diskinfo_standard (drive | 0x0100, &geometry->cylinders, &geometry->heads, &geometry->sectors);
571
if (err != geometry->heads)
572
grub_printf ("\nNotice: number of heads for drive %X tuned from %d to %d.\n", drive, err, geometry->heads);
573
if (version != geometry->sectors)
574
grub_printf ("\nNotice: sectors-per-track for drive %X tuned from %d to %d.\n", drive, version, geometry->sectors);
580
/* if C/H/S=0/0/0, use a safe default one. */
581
if (geometry->sectors == 0)
586
geometry->sectors = 63;
589
if (geometry->total_sectors > 5760)
590
geometry->sectors = 63;
591
else if (geometry->total_sectors > 2880)
592
geometry->sectors = 36;
594
geometry->sectors = 18;
597
if (geometry->heads == 0)
602
geometry->heads = 255;
605
if (geometry->total_sectors > 5760)
606
geometry->heads = 255;
611
if (geometry->cylinders == 0)
613
geometry->cylinders = (geometry->total_sectors / geometry->heads / geometry->sectors);
616
if (geometry->cylinders == 0)
617
geometry->cylinders = 1;
618
#endif /* ! STAGE1_5 */
620
/* backup the geometry into array hd_geom or fd_geom. */
622
#if (! defined(GRUB_UTIL)) && (! defined(STAGE1_5))
627
/* check if the drive is virtual. */
628
d = (unsigned char)drive;
629
j = DRIVE_MAP_SIZE; /* real drive */
630
if (! unset_int13_handler (1))
631
for (j = 0; j < DRIVE_MAP_SIZE; j++)
633
if (drive_map_slot_empty (hooked_drive_map[j]))
635
j = DRIVE_MAP_SIZE; /* real drive */
639
if (((unsigned char)drive) != hooked_drive_map[j].from_drive)
641
if ((hooked_drive_map[j].max_sector & 0x3E) == 0 && hooked_drive_map[j].start_sector == 0 && hooked_drive_map[j].sector_count <= 1)
643
/* this is a map for the whole drive. */
644
d = hooked_drive_map[j].to_drive;
645
j = DRIVE_MAP_SIZE; /* real drive */
650
if (j == DRIVE_MAP_SIZE) /* real drive */
652
if (d >= 0x80 && d < 0x84)
655
if (hd_geom[d].sector_size != 512 || hd_geom[d].sectors <= 0 || hd_geom[d].sectors > 63 || hd_geom[d].heads > 256)
657
hd_geom[d].flags = geometry->flags;
658
hd_geom[d].sector_size = geometry->sector_size;
659
hd_geom[d].total_sectors = geometry->total_sectors;
660
hd_geom[d].heads = geometry->heads;
661
hd_geom[d].sectors = geometry->sectors;
662
hd_geom[d].cylinders = geometry->cylinders;
665
if (fd_geom[d].sector_size != 512 || fd_geom[d].sectors <= 0 || fd_geom[d].sectors > 63 || fd_geom[d].heads > 256)
667
fd_geom[d].flags = geometry->flags;
668
fd_geom[d].sector_size = geometry->sector_size;
669
fd_geom[d].total_sectors = geometry->total_sectors;
670
fd_geom[d].heads = geometry->heads;
671
fd_geom[d].sectors = geometry->sectors;
672
fd_geom[d].cylinders = geometry->cylinders;
677
if ((geometry->flags & BIOSDISK_FLAG_BIFURCATE) && (drive & 0xFFFFFF00) == 0x100)
679
if (geometry->flags & BIOSDISK_FLAG_CDROM)
681
geometry->cylinders = 65536;
682
geometry->heads = 255;
683
geometry->sectors = 15;
684
geometry->sector_size = 2048;
685
geometry->total_sectors = 65536 * 255 * 15;