00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
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
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045 static struct segoff __text16 ( int13_vector );
00046 #define int13_vector __use_text16 ( int13_vector )
00047
00048
00049 extern void int13_wrapper ( void );
00050
00051
00052 static LIST_HEAD ( drives );
00053
00054
00055
00056
00057
00058
00059
00060
00061 static uint8_t num_drives;
00062
00063
00064
00065
00066 static void int13_set_num_drives ( void ) {
00067 struct int13_drive *drive;
00068
00069
00070 get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
00071
00072
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
00079 put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
00080 }
00081
00082
00083
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
00098
00099
00100
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
00110
00111
00112
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
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
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
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
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
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
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
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
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
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
00218
00219
00220
00221
00222
00223
00224
00225
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;
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
00244
00245
00246
00247
00248
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
00264
00265
00266
00267
00268
00269
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
00285
00286
00287
00288
00289
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
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
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
00324
00325
00326
00327
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
00337
00338
00339
00340
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
00350
00351
00352
00353
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, ¶ms,
00371 sizeof ( params ) );
00372 return 0;
00373 }
00374
00375
00376
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
00386 int13_check_num_drives();
00387
00388 list_for_each_entry ( drive, &drives, list ) {
00389
00390 if ( bios_drive != drive->drive ) {
00391
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
00442 drive->last_status = status;
00443
00444
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
00454 ix86->flags |= OF;
00455
00456 return;
00457 }
00458 }
00459
00460
00461
00462
00463
00464 static void hook_int13 ( void ) {
00465
00466
00467
00468
00469 __asm__ __volatile__ (
00470 TEXT16_CODE ( "\nint13_wrapper:\n\t"
00471
00472 "pushw %%bp\n\t"
00473 "movw %%sp, %%bp\n\t"
00474 "pushw %%ax\n\t"
00475 "pushw %%dx\n\t"
00476
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
00483 "jo 1f\n\t"
00484 "pushfw\n\t"
00485 "lcall *%%cs:int13_vector\n\t"
00486 "\n1:\n\t"
00487
00488 "pushfw\n\t"
00489 "popw 6(%%bp)\n\t"
00490
00491
00492
00493
00494
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
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
00519
00520 static void unhook_int13 ( void ) {
00521 unhook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
00522 &int13_vector );
00523 }
00524
00525
00526
00527
00528
00529
00530
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
00542 if ( drive->blockdev->blksize != INT13_BLKSIZE )
00543 return;
00544
00545
00546
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
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
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
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594 void register_int13_drive ( struct int13_drive *drive ) {
00595 uint8_t num_drives;
00596
00597
00598 guess_int13_geometry ( drive );
00599
00600
00601 get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
00602 drive->natural_drive = ( num_drives | 0x80 );
00603
00604
00605 if ( ( drive->drive & 0xff ) == 0xff ) {
00606
00607 drive->drive = drive->natural_drive;
00608 } else {
00609
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
00618 if ( list_empty ( &drives ) )
00619 hook_int13();
00620
00621
00622 list_add ( &drive->list, &drives );
00623
00624
00625 int13_set_num_drives();
00626 }
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637 void unregister_int13_drive ( struct int13_drive *drive ) {
00638
00639 list_del ( &drive->list );
00640
00641
00642
00643
00644
00645 DBG ( "Unregistered INT13 drive %02x\n", drive->drive );
00646
00647
00648 if ( list_empty ( &drives ) )
00649 unhook_int13();
00650 }
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
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
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"
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
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
00701
00702
00703
00704
00705 get_memmap ( &memmap );
00706
00707
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;
00715 }