Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / disk / i386 / pc / biosdisk.c
blob7ca89e3f77c840a61f1b1f0742de5b312fbc3514
1 /*
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>
24 #include <grub/dl.h>
25 #include <grub/mm.h>
26 #include <grub/types.h>
27 #include <grub/misc.h>
28 #include <grub/err.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;
40 int drive;
42 /* reset the disk system first */
43 regs.eax = 0;
44 regs.edx = 0;
45 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
47 grub_bios_interrupt (0x13, &regs);
49 for (drive = 0; drive < 2; drive++)
51 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT | GRUB_CPU_INT_FLAGS_CARRY;
52 regs.edx = drive;
54 /* call GET DISK TYPE */
55 regs.eax = 0x1500;
56 grub_bios_interrupt (0x13, &regs);
57 if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
58 break;
60 /* check if this drive exists */
61 if (!(regs.eax & 0x300))
62 break;
65 return drive;
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.
74 static int
75 grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap)
77 struct grub_bios_int_registers regs;
78 regs.eax = ah << 8;
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);
82 regs.edx = drive;
83 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
85 grub_bios_interrupt (0x13, &regs);
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.
94 static int
95 grub_biosdisk_rw_standard (int ah, int drive, int coff, int hoff,
96 int soff, int nsec, int segment)
98 int ret, i;
100 /* Try 3 times. */
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);
115 /* set %ah to AH */
116 regs.eax = (ah << 8) & 0xff00;
117 /* set %al to NSEC */
118 regs.eax |= nsec & 0xff;
120 regs.ebx = 0;
121 regs.es = segment;
123 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
125 grub_bios_interrupt (0x13, &regs);
126 /* check if successful */
127 if (!(regs.flags & GRUB_CPU_INT_FLAGS_CARRY))
128 return 0;
130 /* save return value */
131 ret = regs.eax >> 8;
133 /* if fail, reset the disk system */
134 regs.eax = 0;
135 regs.edx = (drive & 0xff);
136 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
137 grub_bios_interrupt (0x13, &regs);
139 return ret;
143 * Check if LBA is supported for DRIVE. If it is supported, then return
144 * the major version of extensions, otherwise zero.
146 static int
147 grub_biosdisk_check_int13_extensions (int drive)
149 struct grub_bios_int_registers regs;
151 regs.edx = drive & 0xff;
152 regs.eax = 0x4100;
153 regs.ebx = 0x55aa;
154 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
155 grub_bios_interrupt (0x13, &regs);
157 if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
158 return 0;
160 if ((regs.ebx & 0xffff) != 0xaa55)
161 return 0;
163 /* check if AH=0x42 is supported */
164 if (!(regs.ecx & 1))
165 return 0;
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.
174 static int
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;
182 regs.eax = 0x0800;
183 regs.edx = drive & 0xff;
185 regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
186 grub_bios_interrupt (0x13, &regs);
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 */
197 return 0x60;
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;
203 return 0;
206 static int
207 grub_biosdisk_get_diskinfo_real (int drive, void *drp, grub_uint16_t ax)
209 struct grub_bios_int_registers regs;
211 regs.eax = ax;
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, &regs);
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;
226 return 0;
230 * Return the cdrom information of DRIVE in CDRP. If an error occurs,
231 * then return non-zero, otherwise zero.
233 static int
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.
243 static int
244 grub_biosdisk_get_diskinfo_int13_extensions (int drive, void *drp)
246 return grub_biosdisk_get_diskinfo_real (drive, drp, 0x4800);
249 static int
250 grub_biosdisk_get_drive (const char *name)
252 unsigned long drive;
254 if (name[0] == 'c' && name[1] == 'd' && name[2] == 0 && cd_drive)
255 return cd_drive;
257 if ((name[0] != 'f' && name[0] != 'h') || name[1] != 'd')
258 goto fail;
260 drive = grub_strtoul (name + 2, 0, 10);
261 if (grub_errno != GRUB_ERR_NONE)
262 goto fail;
264 if (name[0] == 'h')
265 drive += 0x80;
267 return (int) drive ;
269 fail:
270 grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a biosdisk");
271 return -1;
274 static int
275 grub_biosdisk_call_hook (int (*hook) (const char *name), int drive)
277 char name[10];
279 if (cd_drive && drive == cd_drive)
280 return hook ("cd");
282 grub_snprintf (name, sizeof (name),
283 (drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80));
284 return hook (name);
287 static int
288 grub_biosdisk_iterate (int (*hook) (const char *name),
289 grub_disk_pull_t pull __attribute__ ((unused)))
291 int num_floppies;
292 int drive;
294 /* For hard disks, attempt to read the MBR. */
295 switch (pull)
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);
304 break;
307 if (grub_biosdisk_call_hook (hook, drive))
308 return 1;
310 return 0;
312 case GRUB_DISK_PULL_REMOVABLE:
313 if (cd_drive)
315 if (grub_biosdisk_call_hook (hook, cd_drive))
316 return 1;
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))
323 return 1;
324 return 0;
325 default:
326 return 0;
328 return 0;
331 static grub_err_t
332 grub_biosdisk_open (const char *name, grub_disk_t disk)
334 grub_uint64_t total_sectors = 0;
335 int drive;
336 struct grub_biosdisk_data *data;
338 drive = grub_biosdisk_get_drive (name);
339 if (drive < 0)
340 return grub_errno;
342 disk->id = drive;
344 data = (struct grub_biosdisk_data *) grub_zalloc (sizeof (*data));
345 if (! data)
346 return grub_errno;
348 data->drive = drive;
350 if ((cd_drive) && (drive == cd_drive))
352 data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM;
353 data->sectors = 8;
354 disk->log_sector_size = 11;
355 /* TODO: get the correct size. */
356 total_sectors = GRUB_DISK_SIZE_UNKNOWN;
358 else
360 /* HDD */
361 int version;
363 disk->log_sector_size = 9;
365 version = grub_biosdisk_check_int13_extensions (drive);
366 if (version)
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;
380 else
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,
401 &data->cylinders,
402 &data->heads,
403 &data->sectors) != 0)
405 if (total_sectors && (data->flags & GRUB_BIOSDISK_FLAG_LBA))
407 data->sectors = 63;
408 data->heads = 255;
409 data->cylinders
410 = grub_divmod64 (total_sectors
411 + data->heads * data->sectors - 1,
412 data->heads * data->sectors, 0);
414 else
416 grub_free (data);
417 return grub_error (GRUB_ERR_BAD_DEVICE, "%s cannot get C/H/S values", disk->name);
421 if (! total_sectors)
422 total_sectors = data->cylinders * data->heads * data->sectors;
425 disk->total_sectors = total_sectors;
426 disk->data = data;
428 return GRUB_ERR_NONE;
431 static void
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
443 static grub_err_t
444 grub_biosdisk_rw (int cmd, grub_disk_t disk,
445 grub_disk_addr_t sector, grub_size_t size,
446 unsigned segment)
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
455 + (data->sectors
456 << disk->log_sector_size));
457 dap->length = sizeof (*dap);
458 dap->reserved = 0;
459 dap->blocks = size;
460 dap->buffer = segment << 16; /* The format SEGMENT:ADDRESS. */
461 dap->block = sector;
463 if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
465 int i;
467 if (cmd)
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))
472 break;
474 if (i == GRUB_BIOSDISK_CDROM_RETRY_COUNT)
475 return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx "
476 "from `%s'"),
477 (unsigned long long) sector,
478 disk->name);
480 else
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);
489 else
491 unsigned coff, hoff, soff;
492 unsigned head;
494 /* It is impossible to reach over 8064 MiB (a bit less than LBA24) with
495 the traditional CHS access. */
496 if (sector >
497 1024 /* cylinders */ *
498 256 /* heads */ *
499 63 /* spt */)
500 return grub_error (GRUB_ERR_OUT_OF_RANGE,
501 N_("attempt to read or write outside of disk `%s'"),
502 disk->name);
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'"),
512 disk->name);
514 if (grub_biosdisk_rw_standard (cmd + 0x02, data->drive,
515 coff, hoff, soff, size, segment))
517 switch (cmd)
519 case GRUB_BIOSDISK_READ:
520 return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx "
521 "from `%s'"),
522 (unsigned long long) sector,
523 disk->name);
524 case GRUB_BIOSDISK_WRITE:
525 return grub_error (GRUB_ERR_WRITE_ERROR, N_("failure writing sector 0x%llx "
526 "to `%s'"),
527 (unsigned long long) sector,
528 disk->name);
533 return GRUB_ERR_NONE;
536 /* Return the number of sectors which can be read safely at a time. */
537 static grub_size_t
538 get_safe_sectors (grub_disk_t disk, grub_disk_addr_t sector)
540 grub_size_t size;
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);
554 return size;
557 static grub_err_t
558 grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
559 grub_size_t size, char *buf)
561 while (size)
563 grub_size_t len;
565 len = get_safe_sectors (disk, sector);
567 if (len > size)
568 len = size;
570 if (grub_biosdisk_rw (GRUB_BIOSDISK_READ, disk, sector, len,
571 GRUB_MEMORY_MACHINE_SCRATCH_SEG))
572 return grub_errno;
574 grub_memcpy (buf, (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR,
575 len << disk->log_sector_size);
577 buf += len << disk->log_sector_size;
578 sector += len;
579 size -= len;
582 return grub_errno;
585 static grub_err_t
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"));
594 while (size)
596 grub_size_t len;
598 len = get_safe_sectors (disk, sector);
599 if (len > size)
600 len = size;
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))
607 return grub_errno;
609 buf += len << disk->log_sector_size;
610 sector += len;
611 size -= len;
614 return grub_errno;
617 static struct grub_disk_dev grub_biosdisk_dev =
619 .name = "biosdisk",
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,
626 .next = 0
629 static void
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."));
645 return;
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
658 cdboot.S
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 ();