2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 FILE_LICENCE ( GPL2_OR_LATER );
26 #include <gpxe/list.h>
27 #include <gpxe/blockdev.h>
28 #include <gpxe/memmap.h>
32 #include <bootsector.h>
39 * This module provides a mechanism for exporting block devices via
40 * the BIOS INT 13 disk interrupt interface.
44 /** Vector for chaining to other INT 13 handlers */
45 static struct segoff __text16 ( int13_vector );
46 #define int13_vector __use_text16 ( int13_vector )
48 /** Assembly wrapper */
49 extern void int13_wrapper ( void );
51 /** List of registered emulated drives */
52 static LIST_HEAD ( drives );
55 * INT 13, 00 - Reset disk system
57 * @v drive Emulated drive
58 * @ret status Status code
60 static int int13_reset ( struct int13_drive *drive __unused,
61 struct i386_all_regs *ix86 __unused ) {
62 DBG ( "Reset drive\n" );
67 * INT 13, 01 - Get status of last operation
69 * @v drive Emulated drive
70 * @ret status Status code
72 static int int13_get_last_status ( struct int13_drive *drive,
73 struct i386_all_regs *ix86 __unused ) {
74 DBG ( "Get status of last operation\n" );
75 return drive->last_status;
79 * Read / write sectors
81 * @v drive Emulated drive
82 * @v al Number of sectors to read or write (must be nonzero)
83 * @v ch Low bits of cylinder number
84 * @v cl (bits 7:6) High bits of cylinder number
85 * @v cl (bits 5:0) Sector number
87 * @v es:bx Data buffer
88 * @v io Read / write method
89 * @ret status Status code
90 * @ret al Number of sectors read or written
92 static int int13_rw_sectors ( struct int13_drive *drive,
93 struct i386_all_regs *ix86,
94 int ( * io ) ( struct block_device *blockdev,
97 userptr_t buffer ) ) {
98 struct block_device *blockdev = drive->blockdev;
99 unsigned int cylinder, head, sector;
105 /* Validate blocksize */
106 if ( blockdev->blksize != INT13_BLKSIZE ) {
107 DBG ( "Invalid blocksize (%zd) for non-extended read/write\n",
109 return -INT13_STATUS_INVALID;
112 /* Calculate parameters */
113 cylinder = ( ( ( ix86->regs.cl & 0xc0 ) << 2 ) | ix86->regs.ch );
114 assert ( cylinder < drive->cylinders );
115 head = ix86->regs.dh;
116 assert ( head < drive->heads );
117 sector = ( ix86->regs.cl & 0x3f );
118 assert ( ( sector >= 1 ) && ( sector <= drive->sectors_per_track ) );
119 lba = ( ( ( ( cylinder * drive->heads ) + head )
120 * drive->sectors_per_track ) + sector - 1 );
121 count = ix86->regs.al;
122 buffer = real_to_user ( ix86->segs.es, ix86->regs.bx );
124 DBG ( "C/H/S %d/%d/%d = LBA %#lx <-> %04x:%04x (count %d)\n", cylinder,
125 head, sector, lba, ix86->segs.es, ix86->regs.bx, count );
127 /* Read from / write to block device */
128 if ( ( rc = io ( blockdev, lba, count, buffer ) ) != 0 ) {
129 DBG ( "INT 13 failed: %s\n", strerror ( rc ) );
130 return -INT13_STATUS_READ_ERROR;
137 * INT 13, 02 - Read sectors
139 * @v drive Emulated drive
140 * @v al Number of sectors to read (must be nonzero)
141 * @v ch Low bits of cylinder number
142 * @v cl (bits 7:6) High bits of cylinder number
143 * @v cl (bits 5:0) Sector number
145 * @v es:bx Data buffer
146 * @ret status Status code
147 * @ret al Number of sectors read
149 static int int13_read_sectors ( struct int13_drive *drive,
150 struct i386_all_regs *ix86 ) {
152 return int13_rw_sectors ( drive, ix86, drive->blockdev->op->read );
156 * INT 13, 03 - Write sectors
158 * @v drive Emulated drive
159 * @v al Number of sectors to write (must be nonzero)
160 * @v ch Low bits of cylinder number
161 * @v cl (bits 7:6) High bits of cylinder number
162 * @v cl (bits 5:0) Sector number
164 * @v es:bx Data buffer
165 * @ret status Status code
166 * @ret al Number of sectors written
168 static int int13_write_sectors ( struct int13_drive *drive,
169 struct i386_all_regs *ix86 ) {
171 return int13_rw_sectors ( drive, ix86, drive->blockdev->op->write );
175 * INT 13, 08 - Get drive parameters
177 * @v drive Emulated drive
178 * @ret status Status code
179 * @ret ch Low bits of maximum cylinder number
180 * @ret cl (bits 7:6) High bits of maximum cylinder number
181 * @ret cl (bits 5:0) Maximum sector number
182 * @ret dh Maximum head number
183 * @ret dl Number of drives
185 static int int13_get_parameters ( struct int13_drive *drive,
186 struct i386_all_regs *ix86 ) {
187 unsigned int max_cylinder = drive->cylinders - 1;
188 unsigned int max_head = drive->heads - 1;
189 unsigned int max_sector = drive->sectors_per_track; /* sic */
191 DBG ( "Get drive parameters\n" );
193 ix86->regs.ch = ( max_cylinder & 0xff );
194 ix86->regs.cl = ( ( ( max_cylinder >> 8 ) << 6 ) | max_sector );
195 ix86->regs.dh = max_head;
196 get_real ( ix86->regs.dl, BDA_SEG, BDA_NUM_DRIVES );
201 * INT 13, 15 - Get disk type
203 * @v drive Emulated drive
205 * @ret cx:dx Sector count
206 * @ret status Status code / disk type
208 static int int13_get_disk_type ( struct int13_drive *drive,
209 struct i386_all_regs *ix86 ) {
212 DBG ( "Get disk type\n" );
213 blocks = ( ( drive->blockdev->blocks <= 0xffffffffUL ) ?
214 drive->blockdev->blocks : 0xffffffffUL );
215 ix86->regs.cx = ( blocks >> 16 );
216 ix86->regs.dx = ( blocks & 0xffff );
217 return INT13_DISK_TYPE_HDD;
221 * INT 13, 41 - Extensions installation check
223 * @v drive Emulated drive
226 * @ret cx Extensions API support bitmap
227 * @ret status Status code / API version
229 static int int13_extension_check ( struct int13_drive *drive __unused,
230 struct i386_all_regs *ix86 ) {
231 if ( ix86->regs.bx == 0x55aa ) {
232 DBG ( "INT 13 extensions installation check\n" );
233 ix86->regs.bx = 0xaa55;
234 ix86->regs.cx = INT13_EXTENSION_LINEAR;
235 return INT13_EXTENSION_VER_1_X;
237 return -INT13_STATUS_INVALID;
242 * Extended read / write
244 * @v drive Emulated drive
245 * @v ds:si Disk address packet
246 * @v io Read / write method
247 * @ret status Status code
249 static int int13_extended_rw ( struct int13_drive *drive,
250 struct i386_all_regs *ix86,
251 int ( * io ) ( struct block_device *blockdev,
254 userptr_t buffer ) ) {
255 struct block_device *blockdev = drive->blockdev;
256 struct int13_disk_address addr;
262 /* Read parameters from disk address structure */
263 copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, sizeof ( addr ));
266 buffer = real_to_user ( addr.buffer.segment, addr.buffer.offset );
268 DBG ( "LBA %#llx <-> %04x:%04x (count %ld)\n", (unsigned long long)lba,
269 addr.buffer.segment, addr.buffer.offset, count );
271 /* Read from / write to block device */
272 if ( ( rc = io ( blockdev, lba, count, buffer ) ) != 0 ) {
273 DBG ( "INT 13 failed: %s\n", strerror ( rc ) );
274 return -INT13_STATUS_READ_ERROR;
281 * INT 13, 42 - Extended read
283 * @v drive Emulated drive
284 * @v ds:si Disk address packet
285 * @ret status Status code
287 static int int13_extended_read ( struct int13_drive *drive,
288 struct i386_all_regs *ix86 ) {
289 DBG ( "Extended read: " );
290 return int13_extended_rw ( drive, ix86, drive->blockdev->op->read );
294 * INT 13, 43 - Extended write
296 * @v drive Emulated drive
297 * @v ds:si Disk address packet
298 * @ret status Status code
300 static int int13_extended_write ( struct int13_drive *drive,
301 struct i386_all_regs *ix86 ) {
302 DBG ( "Extended write: " );
303 return int13_extended_rw ( drive, ix86, drive->blockdev->op->write );
307 * INT 13, 48 - Get extended parameters
309 * @v drive Emulated drive
310 * @v ds:si Drive parameter table
311 * @ret status Status code
313 static int int13_get_extended_parameters ( struct int13_drive *drive,
314 struct i386_all_regs *ix86 ) {
315 struct int13_disk_parameters params = {
316 .bufsize = sizeof ( params ),
317 .flags = INT13_FL_DMA_TRANSPARENT,
318 .cylinders = drive->cylinders,
319 .heads = drive->heads,
320 .sectors_per_track = drive->sectors_per_track,
321 .sectors = drive->blockdev->blocks,
322 .sector_size = drive->blockdev->blksize,
325 DBG ( "Get extended drive parameters to %04x:%04x\n",
326 ix86->segs.ds, ix86->regs.si );
328 copy_to_real ( ix86->segs.ds, ix86->regs.si, ¶ms,
337 static __asmcall void int13 ( struct i386_all_regs *ix86 ) {
338 int command = ix86->regs.ah;
339 unsigned int bios_drive = ix86->regs.dl;
340 struct int13_drive *drive;
343 list_for_each_entry ( drive, &drives, list ) {
345 if ( bios_drive != drive->drive ) {
346 /* Remap any accesses to this drive's natural number */
347 if ( bios_drive == drive->natural_drive ) {
348 DBG ( "INT 13,%04x (%02x) remapped to "
349 "(%02x)\n", ix86->regs.ax,
350 bios_drive, drive->drive );
351 ix86->regs.dl = drive->drive;
357 DBG ( "INT 13,%04x (%02x): ", ix86->regs.ax, drive->drive );
361 status = int13_reset ( drive, ix86 );
363 case INT13_GET_LAST_STATUS:
364 status = int13_get_last_status ( drive, ix86 );
366 case INT13_READ_SECTORS:
367 status = int13_read_sectors ( drive, ix86 );
369 case INT13_WRITE_SECTORS:
370 status = int13_write_sectors ( drive, ix86 );
372 case INT13_GET_PARAMETERS:
373 status = int13_get_parameters ( drive, ix86 );
375 case INT13_GET_DISK_TYPE:
376 status = int13_get_disk_type ( drive, ix86 );
378 case INT13_EXTENSION_CHECK:
379 status = int13_extension_check ( drive, ix86 );
381 case INT13_EXTENDED_READ:
382 status = int13_extended_read ( drive, ix86 );
384 case INT13_EXTENDED_WRITE:
385 status = int13_extended_write ( drive, ix86 );
387 case INT13_GET_EXTENDED_PARAMETERS:
388 status = int13_get_extended_parameters ( drive, ix86 );
391 DBG ( "*** Unrecognised INT 13 ***\n" );
392 status = -INT13_STATUS_INVALID;
396 /* Store status for INT 13,01 */
397 drive->last_status = status;
399 /* Negative status indicates an error */
402 DBG ( "INT 13 returning failure status %x\n", status );
406 ix86->regs.ah = status;
408 /* Set OF to indicate to wrapper not to chain this call */
416 * Hook INT 13 handler
419 static void hook_int13 ( void ) {
420 /* Assembly wrapper to call int13(). int13() sets OF if we
421 * should not chain to the previous handler. (The wrapper
422 * clears CF and OF before calling int13()).
424 __asm__ __volatile__ (
425 TEXT16_CODE ( "\nint13_wrapper:\n\t"
426 /* Preserve %ax and %dx for future reference */
428 "movw %%sp, %%bp\n\t"
431 /* Clear OF, set CF, call int13() */
437 /* Chain if OF not set */
440 "lcall *%%cs:int13_vector\n\t"
442 /* Overwrite flags for iret */
447 * INT 13,15 : do nothing
448 * INT 13,08 : load with number of drives
449 * all others: restore original value
451 "cmpb $0x15, -1(%%bp)\n\t"
453 "movb -4(%%bp), %%dl\n\t"
454 "cmpb $0x08, -1(%%bp)\n\t"
463 "movw %%bp, %%sp\n\t"
466 : : "i" ( int13 ), "i" ( BDA_SEG ), "i" ( BDA_NUM_DRIVES ) );
468 hook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
473 * Unhook INT 13 handler
475 static void unhook_int13 ( void ) {
476 unhook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
481 * Guess INT 13 drive geometry
483 * @v drive Emulated drive
485 * Guesses the drive geometry by inspecting the partition table.
487 static void guess_int13_geometry ( struct int13_drive *drive ) {
488 struct master_boot_record mbr;
489 struct partition_table_entry *partition;
490 unsigned int guessed_heads = 255;
491 unsigned int guessed_sectors_per_track = 63;
492 unsigned long blocks;
493 unsigned long blocks_per_cyl;
496 /* Don't even try when the blksize is invalid for C/H/S access */
497 if ( drive->blockdev->blksize != INT13_BLKSIZE )
500 /* Scan through partition table and modify guesses for heads
501 * and sectors_per_track if we find any used partitions.
503 if ( drive->blockdev->op->read ( drive->blockdev, 0, 1,
504 virt_to_user ( &mbr ) ) == 0 ) {
505 for ( i = 0 ; i < 4 ; i++ ) {
506 partition = &mbr.partitions[i];
507 if ( ! partition->type )
510 ( PART_HEAD ( partition->chs_end ) + 1 );
511 guessed_sectors_per_track =
512 PART_SECTOR ( partition->chs_end );
513 DBG ( "Guessing C/H/S xx/%d/%d based on partition "
514 "%d\n", guessed_heads,
515 guessed_sectors_per_track, ( i + 1 ) );
518 DBG ( "Could not read partition table to guess geometry\n" );
521 /* Apply guesses if no geometry already specified */
522 if ( ! drive->heads )
523 drive->heads = guessed_heads;
524 if ( ! drive->sectors_per_track )
525 drive->sectors_per_track = guessed_sectors_per_track;
526 if ( ! drive->cylinders ) {
527 /* Avoid attempting a 64-bit divide on a 32-bit system */
528 blocks = ( ( drive->blockdev->blocks <= ULONG_MAX ) ?
529 drive->blockdev->blocks : ULONG_MAX );
530 blocks_per_cyl = ( drive->heads * drive->sectors_per_track );
531 assert ( blocks_per_cyl != 0 );
532 drive->cylinders = ( blocks / blocks_per_cyl );
533 if ( drive->cylinders > 1024 )
534 drive->cylinders = 1024;
539 * Register INT 13 emulated drive
541 * @v drive Emulated drive
543 * Registers the drive with the INT 13 emulation subsystem, and hooks
544 * the INT 13 interrupt vector (if not already hooked).
546 * The underlying block device must be valid. A drive number and
547 * geometry will be assigned if left blank.
549 void register_int13_drive ( struct int13_drive *drive ) {
552 /* Give drive a default geometry if none specified */
553 guess_int13_geometry ( drive );
555 /* Assign natural drive number */
556 get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
557 drive->natural_drive = ( num_drives | 0x80 );
560 /* Assign drive number */
561 if ( ( drive->drive & 0xff ) == 0xff ) {
562 /* Drive number == -1 => use natural drive number */
563 drive->drive = drive->natural_drive;
565 /* Use specified drive number (+0x80 if necessary) */
566 drive->drive |= 0x80;
567 if ( num_drives <= ( drive->drive & 0x7f ) )
568 num_drives = ( ( drive->drive & 0x7f ) + 1 );
571 /* Update BIOS drive count */
572 put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
574 DBG ( "Registered INT13 drive %02x (naturally %02x) with C/H/S "
575 "geometry %d/%d/%d\n", drive->drive, drive->natural_drive,
576 drive->cylinders, drive->heads, drive->sectors_per_track );
578 /* Hook INT 13 vector if not already hooked */
579 if ( list_empty ( &drives ) )
582 /* Add to list of emulated drives */
583 list_add ( &drive->list, &drives );
587 * Unregister INT 13 emulated drive
589 * @v drive Emulated drive
591 * Unregisters the drive from the INT 13 emulation subsystem. If this
592 * is the last emulated drive, the INT 13 vector is unhooked (if
595 void unregister_int13_drive ( struct int13_drive *drive ) {
596 /* Remove from list of emulated drives */
597 list_del ( &drive->list );
599 /* Should adjust BIOS drive count, but it's difficult to do so
603 DBG ( "Unregistered INT13 drive %02x\n", drive->drive );
605 /* Unhook INT 13 vector if no more drives */
606 if ( list_empty ( &drives ) )
611 * Attempt to boot from an INT 13 drive
613 * @v drive Drive number
614 * @ret rc Return status code
616 * This boots from the specified INT 13 drive by loading the Master
617 * Boot Record to 0000:7c00 and jumping to it. INT 18 is hooked to
618 * capture an attempt by the MBR to boot the next device. (This is
619 * the closest thing to a return path from an MBR).
621 * Note that this function can never return success, by definition.
623 int int13_boot ( unsigned int drive ) {
624 struct memory_map memmap;
625 int status, signature;
626 int discard_c, discard_d;
629 DBG ( "Booting from INT 13 drive %02x\n", drive );
631 /* Use INT 13 to read the boot sector */
632 __asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t"
638 "sti\n\t" /* BIOS bugs */
640 "xorl %%eax, %%eax\n\t"
642 "movzwl %%es:0x7dfe, %%ebx\n\t"
644 : "=a" ( status ), "=b" ( signature ),
645 "=c" ( discard_c ), "=d" ( discard_d )
646 : "a" ( 0x0201 ), "b" ( 0x7c00 ),
647 "c" ( 1 ), "d" ( drive ) );
651 /* Check signature is correct */
652 if ( signature != be16_to_cpu ( 0x55aa ) ) {
653 DBG ( "Invalid disk signature %#04x (should be 0x55aa)\n",
654 cpu_to_be16 ( signature ) );
658 /* Dump out memory map prior to boot, if memmap debugging is
659 * enabled. Not required for program flow, but we have so
660 * many problems that turn out to be memory-map related that
663 get_memmap ( &memmap );
665 /* Jump to boot sector */
666 if ( ( rc = call_bootsector ( 0x0, 0x7c00, drive ) ) != 0 ) {
667 DBG ( "INT 13 drive %02x boot returned: %s\n",
668 drive, strerror ( rc ) );
672 return -ECANCELED; /* -EIMPOSSIBLE */