int13.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020 
00021 #include <stdint.h>
00022 #include <limits.h>
00023 #include <byteswap.h>
00024 #include <errno.h>
00025 #include <assert.h>
00026 #include <gpxe/list.h>
00027 #include <gpxe/blockdev.h>
00028 #include <gpxe/memmap.h>
00029 #include <realmode.h>
00030 #include <bios.h>
00031 #include <biosint.h>
00032 #include <bootsector.h>
00033 #include <int13.h>
00034 
00035 /** @file
00036  *
00037  * INT 13 emulation
00038  *
00039  * This module provides a mechanism for exporting block devices via
00040  * the BIOS INT 13 disk interrupt interface.  
00041  *
00042  */
00043 
00044 /** Vector for chaining to other INT 13 handlers */
00045 static struct segoff __text16 ( int13_vector );
00046 #define int13_vector __use_text16 ( int13_vector )
00047 
00048 /** Assembly wrapper */
00049 extern void int13_wrapper ( void );
00050 
00051 /** List of registered emulated drives */
00052 static LIST_HEAD ( drives );
00053 
00054 /**
00055  * Number of BIOS drives
00056  *
00057  * Note that this is the number of drives in the system as a whole
00058  * (i.e. a mirror of the counter at 40:75), rather than a count of the
00059  * number of emulated drives.
00060  */
00061 static uint8_t num_drives;
00062 
00063 /**
00064  * Update BIOS drive count
00065  */
00066 static void int13_set_num_drives ( void ) {
00067         struct int13_drive *drive;
00068 
00069         /* Get current drive count */
00070         get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
00071 
00072         /* Ensure count is large enough to cover all of our emulated drives */
00073         list_for_each_entry ( drive, &drives, list ) {
00074                 if ( num_drives <= ( drive->drive & 0x7f ) )
00075                         num_drives = ( ( drive->drive & 0x7f ) + 1 );
00076         }
00077 
00078         /* Update current drive count */
00079         put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
00080 }
00081 
00082 /**
00083  * Check number of drives
00084  */
00085 static void int13_check_num_drives ( void ) {
00086         uint8_t check_num_drives;
00087 
00088         get_real ( check_num_drives, BDA_SEG, BDA_NUM_DRIVES );
00089         if ( check_num_drives != num_drives ) {
00090                 int13_set_num_drives();
00091                 DBG ( "INT13 fixing up number of drives from %d to %d\n",
00092                       check_num_drives, num_drives );
00093         }
00094 }
00095 
00096 /**
00097  * INT 13, 00 - Reset disk system
00098  *
00099  * @v drive             Emulated drive
00100  * @ret status          Status code
00101  */
00102 static int int13_reset ( struct int13_drive *drive __unused,
00103                          struct i386_all_regs *ix86 __unused ) {
00104         DBG ( "Reset drive\n" );
00105         return 0;
00106 }
00107 
00108 /**
00109  * INT 13, 01 - Get status of last operation
00110  *
00111  * @v drive             Emulated drive
00112  * @ret status          Status code
00113  */
00114 static int int13_get_last_status ( struct int13_drive *drive,
00115                                    struct i386_all_regs *ix86 __unused ) {
00116         DBG ( "Get status of last operation\n" );
00117         return drive->last_status;
00118 }
00119 
00120 /**
00121  * Read / write sectors
00122  *
00123  * @v drive             Emulated drive
00124  * @v al                Number of sectors to read or write (must be nonzero)
00125  * @v ch                Low bits of cylinder number
00126  * @v cl (bits 7:6)     High bits of cylinder number
00127  * @v cl (bits 5:0)     Sector number
00128  * @v dh                Head number
00129  * @v es:bx             Data buffer
00130  * @v io                Read / write method
00131  * @ret status          Status code
00132  * @ret al              Number of sectors read or written
00133  */
00134 static int int13_rw_sectors ( struct int13_drive *drive,
00135                               struct i386_all_regs *ix86,
00136                               int ( * io ) ( struct block_device *blockdev,
00137                                              uint64_t block,
00138                                              unsigned long count,
00139                                              userptr_t buffer ) ) {
00140         struct block_device *blockdev = drive->blockdev;
00141         unsigned int cylinder, head, sector;
00142         unsigned long lba;
00143         unsigned int count;
00144         userptr_t buffer;
00145         int rc;
00146 
00147         /* Validate blocksize */
00148         if ( blockdev->blksize != INT13_BLKSIZE ) {
00149                 DBG ( "Invalid blocksize (%zd) for non-extended read/write\n",
00150                       blockdev->blksize );
00151                 return -INT13_STATUS_INVALID;
00152         }
00153         
00154         /* Calculate parameters */
00155         cylinder = ( ( ( ix86->regs.cl & 0xc0 ) << 2 ) | ix86->regs.ch );
00156         assert ( cylinder < drive->cylinders );
00157         head = ix86->regs.dh;
00158         assert ( head < drive->heads );
00159         sector = ( ix86->regs.cl & 0x3f );
00160         assert ( ( sector >= 1 ) && ( sector <= drive->sectors_per_track ) );
00161         lba = ( ( ( ( cylinder * drive->heads ) + head )
00162                   * drive->sectors_per_track ) + sector - 1 );
00163         count = ix86->regs.al;
00164         buffer = real_to_user ( ix86->segs.es, ix86->regs.bx );
00165 
00166         DBG ( "C/H/S %d/%d/%d = LBA %#lx <-> %04x:%04x (count %d)\n", cylinder,
00167               head, sector, lba, ix86->segs.es, ix86->regs.bx, count );
00168 
00169         /* Read from / write to block device */
00170         if ( ( rc = io ( blockdev, lba, count, buffer ) ) != 0 ) {
00171                 DBG ( "INT 13 failed: %s\n", strerror ( rc ) );
00172                 return -INT13_STATUS_READ_ERROR;
00173         }
00174 
00175         return 0;
00176 }
00177 
00178 /**
00179  * INT 13, 02 - Read sectors
00180  *
00181  * @v drive             Emulated drive
00182  * @v al                Number of sectors to read (must be nonzero)
00183  * @v ch                Low bits of cylinder number
00184  * @v cl (bits 7:6)     High bits of cylinder number
00185  * @v cl (bits 5:0)     Sector number
00186  * @v dh                Head number
00187  * @v es:bx             Data buffer
00188  * @ret status          Status code
00189  * @ret al              Number of sectors read
00190  */
00191 static int int13_read_sectors ( struct int13_drive *drive,
00192                                 struct i386_all_regs *ix86 ) {
00193         DBG ( "Read: " );
00194         return int13_rw_sectors ( drive, ix86, drive->blockdev->op->read );
00195 }
00196 
00197 /**
00198  * INT 13, 03 - Write sectors
00199  *
00200  * @v drive             Emulated drive
00201  * @v al                Number of sectors to write (must be nonzero)
00202  * @v ch                Low bits of cylinder number
00203  * @v cl (bits 7:6)     High bits of cylinder number
00204  * @v cl (bits 5:0)     Sector number
00205  * @v dh                Head number
00206  * @v es:bx             Data buffer
00207  * @ret status          Status code
00208  * @ret al              Number of sectors written
00209  */
00210 static int int13_write_sectors ( struct int13_drive *drive,
00211                                  struct i386_all_regs *ix86 ) {
00212         DBG ( "Write: " );
00213         return int13_rw_sectors ( drive, ix86, drive->blockdev->op->write );
00214 }
00215 
00216 /**
00217  * INT 13, 08 - Get drive parameters
00218  *
00219  * @v drive             Emulated drive
00220  * @ret status          Status code
00221  * @ret ch              Low bits of maximum cylinder number
00222  * @ret cl (bits 7:6)   High bits of maximum cylinder number
00223  * @ret cl (bits 5:0)   Maximum sector number
00224  * @ret dh              Maximum head number
00225  * @ret dl              Number of drives
00226  */
00227 static int int13_get_parameters ( struct int13_drive *drive,
00228                                   struct i386_all_regs *ix86 ) {
00229         unsigned int max_cylinder = drive->cylinders - 1;
00230         unsigned int max_head = drive->heads - 1;
00231         unsigned int max_sector = drive->sectors_per_track; /* sic */
00232 
00233         DBG ( "Get drive parameters\n" );
00234 
00235         ix86->regs.ch = ( max_cylinder & 0xff );
00236         ix86->regs.cl = ( ( ( max_cylinder >> 8 ) << 6 ) | max_sector );
00237         ix86->regs.dh = max_head;
00238         get_real ( ix86->regs.dl, BDA_SEG, BDA_NUM_DRIVES );
00239         return 0;
00240 }
00241 
00242 /**
00243  * INT 13, 15 - Get disk type
00244  *
00245  * @v drive             Emulated drive
00246  * @ret ah              Type code
00247  * @ret cx:dx           Sector count
00248  * @ret status          Status code / disk type
00249  */
00250 static int int13_get_disk_type ( struct int13_drive *drive,
00251                                  struct i386_all_regs *ix86 ) {
00252         uint32_t blocks;
00253 
00254         DBG ( "Get disk type\n" );
00255         blocks = ( ( drive->blockdev->blocks <= 0xffffffffUL ) ?
00256                    drive->blockdev->blocks : 0xffffffffUL );
00257         ix86->regs.cx = ( blocks >> 16 );
00258         ix86->regs.dx = ( blocks & 0xffff );
00259         return INT13_DISK_TYPE_HDD;
00260 }
00261 
00262 /**
00263  * INT 13, 41 - Extensions installation check
00264  *
00265  * @v drive             Emulated drive
00266  * @v bx                0x55aa
00267  * @ret bx              0xaa55
00268  * @ret cx              Extensions API support bitmap
00269  * @ret status          Status code / API version
00270  */
00271 static int int13_extension_check ( struct int13_drive *drive __unused,
00272                                    struct i386_all_regs *ix86 ) {
00273         if ( ix86->regs.bx == 0x55aa ) {
00274                 DBG ( "INT 13 extensions installation check\n" );
00275                 ix86->regs.bx = 0xaa55;
00276                 ix86->regs.cx = INT13_EXTENSION_LINEAR;
00277                 return INT13_EXTENSION_VER_1_X;
00278         } else {
00279                 return -INT13_STATUS_INVALID;
00280         }
00281 }
00282 
00283 /**
00284  * Extended read / write
00285  *
00286  * @v drive             Emulated drive
00287  * @v ds:si             Disk address packet
00288  * @v io                Read / write method
00289  * @ret status          Status code
00290  */
00291 static int int13_extended_rw ( struct int13_drive *drive,
00292                                struct i386_all_regs *ix86,
00293                                int ( * io ) ( struct block_device *blockdev,
00294                                               uint64_t block,
00295                                               unsigned long count,
00296                                               userptr_t buffer ) ) {
00297         struct block_device *blockdev = drive->blockdev;
00298         struct int13_disk_address addr;
00299         uint64_t lba;
00300         unsigned long count;
00301         userptr_t buffer;
00302         int rc;
00303 
00304         /* Read parameters from disk address structure */
00305         copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, sizeof ( addr ));
00306         lba = addr.lba;
00307         count = addr.count;
00308         buffer = real_to_user ( addr.buffer.segment, addr.buffer.offset );
00309 
00310         DBG ( "LBA %#llx <-> %04x:%04x (count %ld)\n", (unsigned long long)lba,
00311               addr.buffer.segment, addr.buffer.offset, count );
00312         
00313         /* Read from / write to block device */
00314         if ( ( rc = io ( blockdev, lba, count, buffer ) ) != 0 ) {
00315                 DBG ( "INT 13 failed: %s\n", strerror ( rc ) );
00316                 return -INT13_STATUS_READ_ERROR;
00317         }
00318 
00319         return 0;
00320 }
00321 
00322 /**
00323  * INT 13, 42 - Extended read
00324  *
00325  * @v drive             Emulated drive
00326  * @v ds:si             Disk address packet
00327  * @ret status          Status code
00328  */
00329 static int int13_extended_read ( struct int13_drive *drive,
00330                                  struct i386_all_regs *ix86 ) {
00331         DBG ( "Extended read: " );
00332         return int13_extended_rw ( drive, ix86, drive->blockdev->op->read );
00333 }
00334 
00335 /**
00336  * INT 13, 43 - Extended write
00337  *
00338  * @v drive             Emulated drive
00339  * @v ds:si             Disk address packet
00340  * @ret status          Status code
00341  */
00342 static int int13_extended_write ( struct int13_drive *drive,
00343                                   struct i386_all_regs *ix86 ) {
00344         DBG ( "Extended write: " );
00345         return int13_extended_rw ( drive, ix86, drive->blockdev->op->write );
00346 }
00347 
00348 /**
00349  * INT 13, 48 - Get extended parameters
00350  *
00351  * @v drive             Emulated drive
00352  * @v ds:si             Drive parameter table
00353  * @ret status          Status code
00354  */
00355 static int int13_get_extended_parameters ( struct int13_drive *drive,
00356                                            struct i386_all_regs *ix86 ) {
00357         struct int13_disk_parameters params = {
00358                 .bufsize = sizeof ( params ),
00359                 .flags = INT13_FL_DMA_TRANSPARENT,
00360                 .cylinders = drive->cylinders,
00361                 .heads = drive->heads,
00362                 .sectors_per_track = drive->sectors_per_track,
00363                 .sectors = drive->blockdev->blocks,
00364                 .sector_size = drive->blockdev->blksize,
00365         };
00366         
00367         DBG ( "Get extended drive parameters to %04x:%04x\n",
00368               ix86->segs.ds, ix86->regs.si );
00369 
00370         copy_to_real ( ix86->segs.ds, ix86->regs.si, &params,
00371                        sizeof ( params ) );
00372         return 0;
00373 }
00374 
00375 /**
00376  * INT 13 handler
00377  *
00378  */
00379 static __asmcall void int13 ( struct i386_all_regs *ix86 ) {
00380         int command = ix86->regs.ah;
00381         unsigned int bios_drive = ix86->regs.dl;
00382         struct int13_drive *drive;
00383         int status;
00384 
00385         /* Check BIOS hasn't killed off our drive */
00386         int13_check_num_drives();
00387 
00388         list_for_each_entry ( drive, &drives, list ) {
00389 
00390                 if ( bios_drive != drive->drive ) {
00391                         /* Remap any accesses to this drive's natural number */
00392                         if ( bios_drive == drive->natural_drive ) {
00393                                 DBG ( "INT 13,%04x (%02x) remapped to "
00394                                       "(%02x)\n", ix86->regs.ax,
00395                                       bios_drive, drive->drive );
00396                                 ix86->regs.dl = drive->drive;
00397                                 return;
00398                         }
00399                         continue;
00400                 }
00401                 
00402                 DBG ( "INT 13,%04x (%02x): ", ix86->regs.ax, drive->drive );
00403 
00404                 switch ( command ) {
00405                 case INT13_RESET:
00406                         status = int13_reset ( drive, ix86 );
00407                         break;
00408                 case INT13_GET_LAST_STATUS:
00409                         status = int13_get_last_status ( drive, ix86 );
00410                         break;
00411                 case INT13_READ_SECTORS:
00412                         status = int13_read_sectors ( drive, ix86 );
00413                         break;
00414                 case INT13_WRITE_SECTORS:
00415                         status = int13_write_sectors ( drive, ix86 );
00416                         break;
00417                 case INT13_GET_PARAMETERS:
00418                         status = int13_get_parameters ( drive, ix86 );
00419                         break;
00420                 case INT13_GET_DISK_TYPE:
00421                         status = int13_get_disk_type ( drive, ix86 );
00422                         break;
00423                 case INT13_EXTENSION_CHECK:
00424                         status = int13_extension_check ( drive, ix86 );
00425                         break;
00426                 case INT13_EXTENDED_READ:
00427                         status = int13_extended_read ( drive, ix86 );
00428                         break;
00429                 case INT13_EXTENDED_WRITE:
00430                         status = int13_extended_write ( drive, ix86 );
00431                         break;
00432                 case INT13_GET_EXTENDED_PARAMETERS:
00433                         status = int13_get_extended_parameters ( drive, ix86 );
00434                         break;
00435                 default:
00436                         DBG ( "*** Unrecognised INT 13 ***\n" );
00437                         status = -INT13_STATUS_INVALID;
00438                         break;
00439                 }
00440 
00441                 /* Store status for INT 13,01 */
00442                 drive->last_status = status;
00443 
00444                 /* Negative status indicates an error */
00445                 if ( status < 0 ) {
00446                         status = -status;
00447                         DBG ( "INT 13 returning failure status %x\n", status );
00448                 } else {
00449                         ix86->flags &= ~CF;
00450                 }
00451                 ix86->regs.ah = status;
00452 
00453                 /* Set OF to indicate to wrapper not to chain this call */
00454                 ix86->flags |= OF;
00455 
00456                 return;
00457         }
00458 }
00459 
00460 /**
00461  * Hook INT 13 handler
00462  *
00463  */
00464 static void hook_int13 ( void ) {
00465         /* Assembly wrapper to call int13().  int13() sets OF if we
00466          * should not chain to the previous handler.  (The wrapper
00467          * clears CF and OF before calling int13()).
00468          */
00469         __asm__  __volatile__ (
00470                TEXT16_CODE ( "\nint13_wrapper:\n\t"
00471                              /* Preserve %ax and %dx for future reference */
00472                              "pushw %%bp\n\t"
00473                              "movw %%sp, %%bp\n\t"                           
00474                              "pushw %%ax\n\t"
00475                              "pushw %%dx\n\t"
00476                              /* Clear OF, set CF, call int13() */
00477                              "orb $0, %%al\n\t" 
00478                              "stc\n\t"
00479                              "pushl %0\n\t"
00480                              "pushw %%cs\n\t"
00481                              "call prot_call\n\t"
00482                              /* Chain if OF not set */
00483                              "jo 1f\n\t"
00484                              "pushfw\n\t"
00485                              "lcall *%%cs:int13_vector\n\t"
00486                              "\n1:\n\t"
00487                              /* Overwrite flags for iret */
00488                              "pushfw\n\t"
00489                              "popw 6(%%bp)\n\t"
00490                              /* Fix up %dl:
00491                               *
00492                               * INT 13,15 : do nothing
00493                               * INT 13,08 : load with number of drives
00494                               * all others: restore original value
00495                               */
00496                              "cmpb $0x15, -1(%%bp)\n\t"
00497                              "je 2f\n\t"
00498                              "movb -4(%%bp), %%dl\n\t"
00499                              "cmpb $0x08, -1(%%bp)\n\t"
00500                              "jne 2f\n\t"
00501                              "pushw %%ds\n\t"
00502                              "pushw %1\n\t"
00503                              "popw %%ds\n\t"
00504                              "movb %c2, %%dl\n\t"
00505                              "popw %%ds\n\t"
00506                              /* Return */
00507                              "\n2:\n\t"
00508                              "movw %%bp, %%sp\n\t"
00509                              "popw %%bp\n\t"
00510                              "iret\n\t" )
00511                : : "i" ( int13 ), "i" ( BDA_SEG ), "i" ( BDA_NUM_DRIVES ) );
00512 
00513         hook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
00514                               &int13_vector );
00515 }
00516 
00517 /**
00518  * Unhook INT 13 handler
00519  */
00520 static void unhook_int13 ( void ) {
00521         unhook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
00522                                 &int13_vector );
00523 }
00524 
00525 /**
00526  * Guess INT 13 drive geometry
00527  *
00528  * @v drive             Emulated drive
00529  *
00530  * Guesses the drive geometry by inspecting the partition table.
00531  */
00532 static void guess_int13_geometry ( struct int13_drive *drive ) {
00533         struct master_boot_record mbr;
00534         struct partition_table_entry *partition;
00535         unsigned int guessed_heads = 255;
00536         unsigned int guessed_sectors_per_track = 63;
00537         unsigned long blocks;
00538         unsigned long blocks_per_cyl;
00539         unsigned int i;
00540 
00541         /* Don't even try when the blksize is invalid for C/H/S access */
00542         if ( drive->blockdev->blksize != INT13_BLKSIZE )
00543                 return;
00544 
00545         /* Scan through partition table and modify guesses for heads
00546          * and sectors_per_track if we find any used partitions.
00547          */
00548         if ( drive->blockdev->op->read ( drive->blockdev, 0, 1,
00549                                          virt_to_user ( &mbr ) ) == 0 ) {
00550                 for ( i = 0 ; i < 4 ; i++ ) {
00551                         partition = &mbr.partitions[i];
00552                         if ( ! partition->type )
00553                                 continue;
00554                         guessed_heads =
00555                                 ( PART_HEAD ( partition->chs_end ) + 1 );
00556                         guessed_sectors_per_track = 
00557                                 PART_SECTOR ( partition->chs_end );
00558                         DBG ( "Guessing C/H/S xx/%d/%d based on partition "
00559                               "%d\n", guessed_heads,
00560                               guessed_sectors_per_track, ( i + 1 ) );
00561                 }
00562         } else {
00563                 DBG ( "Could not read partition table to guess geometry\n" );
00564         }
00565 
00566         /* Apply guesses if no geometry already specified */
00567         if ( ! drive->heads )
00568                 drive->heads = guessed_heads;
00569         if ( ! drive->sectors_per_track )
00570                 drive->sectors_per_track = guessed_sectors_per_track;
00571         if ( ! drive->cylinders ) {
00572                 /* Avoid attempting a 64-bit divide on a 32-bit system */
00573                 blocks = ( ( drive->blockdev->blocks <= ULONG_MAX ) ?
00574                            drive->blockdev->blocks : ULONG_MAX );
00575                 blocks_per_cyl = ( drive->heads * drive->sectors_per_track );
00576                 assert ( blocks_per_cyl != 0 );
00577                 drive->cylinders = ( blocks / blocks_per_cyl );
00578                 if ( drive->cylinders > 1024 )
00579                         drive->cylinders = 1024;
00580         }
00581 }
00582 
00583 /**
00584  * Register INT 13 emulated drive
00585  *
00586  * @v drive             Emulated drive
00587  *
00588  * Registers the drive with the INT 13 emulation subsystem, and hooks
00589  * the INT 13 interrupt vector (if not already hooked).
00590  *
00591  * The underlying block device must be valid.  A drive number and
00592  * geometry will be assigned if left blank.
00593  */
00594 void register_int13_drive ( struct int13_drive *drive ) {
00595         uint8_t num_drives;
00596 
00597         /* Give drive a default geometry if none specified */
00598         guess_int13_geometry ( drive );
00599 
00600         /* Assign natural drive number */
00601         get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
00602         drive->natural_drive = ( num_drives | 0x80 );
00603 
00604         /* Assign drive number */
00605         if ( ( drive->drive & 0xff ) == 0xff ) {
00606                 /* Drive number == -1 => use natural drive number */
00607                 drive->drive = drive->natural_drive;
00608         } else {
00609                 /* Use specified drive number (+0x80 if necessary) */
00610                 drive->drive |= 0x80;
00611         }
00612 
00613         DBG ( "Registered INT13 drive %02x (naturally %02x) with C/H/S "
00614               "geometry %d/%d/%d\n", drive->drive, drive->natural_drive,
00615               drive->cylinders, drive->heads, drive->sectors_per_track );
00616 
00617         /* Hook INT 13 vector if not already hooked */
00618         if ( list_empty ( &drives ) )
00619                 hook_int13();
00620 
00621         /* Add to list of emulated drives */
00622         list_add ( &drive->list, &drives );
00623 
00624         /* Update BIOS drive count */
00625         int13_set_num_drives();
00626 }
00627 
00628 /**
00629  * Unregister INT 13 emulated drive
00630  *
00631  * @v drive             Emulated drive
00632  *
00633  * Unregisters the drive from the INT 13 emulation subsystem.  If this
00634  * is the last emulated drive, the INT 13 vector is unhooked (if
00635  * possible).
00636  */
00637 void unregister_int13_drive ( struct int13_drive *drive ) {
00638         /* Remove from list of emulated drives */
00639         list_del ( &drive->list );
00640 
00641         /* Should adjust BIOS drive count, but it's difficult to do so
00642          * reliably.
00643          */
00644 
00645         DBG ( "Unregistered INT13 drive %02x\n", drive->drive );
00646 
00647         /* Unhook INT 13 vector if no more drives */
00648         if ( list_empty ( &drives ) )
00649                 unhook_int13();
00650 }
00651 
00652 /**
00653  * Attempt to boot from an INT 13 drive
00654  *
00655  * @v drive             Drive number
00656  * @ret rc              Return status code
00657  *
00658  * This boots from the specified INT 13 drive by loading the Master
00659  * Boot Record to 0000:7c00 and jumping to it.  INT 18 is hooked to
00660  * capture an attempt by the MBR to boot the next device.  (This is
00661  * the closest thing to a return path from an MBR).
00662  *
00663  * Note that this function can never return success, by definition.
00664  */
00665 int int13_boot ( unsigned int drive ) {
00666         struct memory_map memmap;
00667         int status, signature;
00668         int discard_c, discard_d;
00669         int rc;
00670 
00671         DBG ( "Booting from INT 13 drive %02x\n", drive );
00672 
00673         /* Use INT 13 to read the boot sector */
00674         __asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t"
00675                                            "pushw $0\n\t"
00676                                            "popw %%es\n\t"
00677                                            "stc\n\t"
00678                                            "sti\n\t"
00679                                            "int $0x13\n\t"
00680                                            "sti\n\t" /* BIOS bugs */
00681                                            "jc 1f\n\t"
00682                                            "xorl %%eax, %%eax\n\t"
00683                                            "\n1:\n\t"
00684                                            "movzwl %%es:0x7dfe, %%ebx\n\t"
00685                                            "popw %%es\n\t" )
00686                                : "=a" ( status ), "=b" ( signature ),
00687                                  "=c" ( discard_c ), "=d" ( discard_d )
00688                                : "a" ( 0x0201 ), "b" ( 0x7c00 ),
00689                                  "c" ( 1 ), "d" ( drive ) );
00690         if ( status )
00691                 return -EIO;
00692 
00693         /* Check signature is correct */
00694         if ( signature != be16_to_cpu ( 0x55aa ) ) {
00695                 DBG ( "Invalid disk signature %#04x (should be 0x55aa)\n",
00696                       cpu_to_be16 ( signature ) );
00697                 return -ENOEXEC;
00698         }
00699 
00700         /* Dump out memory map prior to boot, if memmap debugging is
00701          * enabled.  Not required for program flow, but we have so
00702          * many problems that turn out to be memory-map related that
00703          * it's worth doing.
00704          */
00705         get_memmap ( &memmap );
00706 
00707         /* Jump to boot sector */
00708         if ( ( rc = call_bootsector ( 0x0, 0x7c00, drive ) ) != 0 ) {
00709                 DBG ( "INT 13 drive %02x boot returned: %s\n",
00710                       drive, strerror ( rc ) );
00711                 return rc;
00712         }
00713 
00714         return -ECANCELED; /* -EIMPOSSIBLE */
00715 }

Generated on Tue Apr 6 20:00:50 2010 for gPXE by  doxygen 1.5.7.1