選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

int13.c 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. /*
  2. * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
  3. *
  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.
  8. *
  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.
  13. *
  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.
  17. */
  18. #include <stdint.h>
  19. #include <limits.h>
  20. #include <byteswap.h>
  21. #include <errno.h>
  22. #include <assert.h>
  23. #include <gpxe/list.h>
  24. #include <gpxe/blockdev.h>
  25. #include <realmode.h>
  26. #include <bios.h>
  27. #include <biosint.h>
  28. #include <int13.h>
  29. /** @file
  30. *
  31. * INT 13 emulation
  32. *
  33. * This module provides a mechanism for exporting block devices via
  34. * the BIOS INT 13 disk interrupt interface.
  35. *
  36. */
  37. /** Vector for chaining to other INT 13 handlers */
  38. static struct segoff __text16 ( int13_vector );
  39. #define int13_vector __use_text16 ( int13_vector )
  40. /** Assembly wrapper */
  41. extern void int13_wrapper ( void );
  42. /** Vector for storing original INT 18 handler
  43. *
  44. * We do not chain to this vector, so there is no need to place it in
  45. * .text16.
  46. */
  47. static struct segoff int18_vector;
  48. /** Vector for storing original INT 19 handler
  49. *
  50. * We do not chain to this vector, so there is no need to place it in
  51. * .text16.
  52. */
  53. static struct segoff int19_vector;
  54. /** Restart point for INT 18 or 19 */
  55. extern void int13_exec_fail ( void );
  56. /** List of registered emulated drives */
  57. static LIST_HEAD ( drives );
  58. /**
  59. * Convert CHS address to linear address
  60. *
  61. * @v drive Emulated drive
  62. * @v ch Low bits of cylinder number
  63. * @v cl (bits 7:6) High bits of cylinder number
  64. * @v cl (bits 5:0) Sector number
  65. * @v dh Head number
  66. * @ret lba LBA address
  67. *
  68. */
  69. static unsigned long chs_to_lba ( struct int13_drive *drive,
  70. struct i386_all_regs *ix86 ) {
  71. unsigned int cylinder;
  72. unsigned int head;
  73. unsigned int sector;
  74. unsigned long lba;
  75. cylinder = ( ( ( ix86->regs.cl & 0xc0 ) << 8 ) | ix86->regs.ch );
  76. head = ix86->regs.dh;
  77. sector = ( ix86->regs.cl & 0x3f );
  78. assert ( cylinder < drive->cylinders );
  79. assert ( head < drive->heads );
  80. assert ( ( sector >= 1 ) && ( sector <= drive->sectors_per_track ) );
  81. lba = ( ( ( ( cylinder * drive->heads ) + head )
  82. * drive->sectors_per_track ) + sector - 1 );
  83. DBG ( "C/H/S address %x/%x/%x -> LBA %x\n",
  84. cylinder, head, sector, lba );
  85. return lba;
  86. }
  87. /**
  88. * Read from drive to real-mode data buffer
  89. *
  90. * @v drive Emulated drive
  91. * @v lba LBA starting sector number
  92. * @v data Data buffer
  93. * @v count Number of sectors to read
  94. * @ret status Status code
  95. */
  96. static int int13_read ( struct int13_drive *drive, uint64_t lba,
  97. struct segoff data, unsigned long count ) {
  98. struct block_device *blockdev = drive->blockdev;
  99. size_t blksize = blockdev->blksize;
  100. uint8_t buffer[blksize];
  101. int rc;
  102. DBG ( "Read %lx sectors from %llx to %04x:%04x\n", count,
  103. ( unsigned long long ) lba, data.segment, data.offset );
  104. while ( count-- ) {
  105. if ( ( rc = blockdev->read ( blockdev, lba, buffer ) ) != 0 )
  106. return INT13_STATUS_READ_ERROR;
  107. copy_to_real ( data.segment, data.offset, buffer, blksize );
  108. data.offset += blksize;
  109. lba++;
  110. }
  111. return 0;
  112. }
  113. /**
  114. * Write from real-mode data buffer to drive
  115. *
  116. * @v drive Emulated drive
  117. * @v lba LBA starting sector number
  118. * @v data Data buffer
  119. * @v count Number of sectors to read
  120. * @ret status Status code
  121. */
  122. static int int13_write ( struct int13_drive *drive, uint64_t lba,
  123. struct segoff data, unsigned long count ) {
  124. struct block_device *blockdev = drive->blockdev;
  125. size_t blksize = blockdev->blksize;
  126. uint8_t buffer[blksize];
  127. int rc;
  128. DBG ( "Write %lx sectors from %04x:%04x to %llx\n", count,
  129. data.segment, data.offset, ( unsigned long long ) lba );
  130. while ( count-- ) {
  131. copy_from_real ( buffer, data.segment, data.offset, blksize );
  132. if ( ( rc = blockdev->write ( blockdev, lba, buffer ) ) != 0 )
  133. return INT13_STATUS_WRITE_ERROR;
  134. data.offset += blksize;
  135. lba++;
  136. }
  137. return 0;
  138. }
  139. /**
  140. * INT 13, 00 - Reset disk system
  141. *
  142. * @v drive Emulated drive
  143. * @ret status Status code
  144. */
  145. static int int13_reset ( struct int13_drive *drive __unused,
  146. struct i386_all_regs *ix86 __unused ) {
  147. DBG ( "Reset drive\n" );
  148. return 0;
  149. }
  150. /**
  151. * INT 13, 01 - Get status of last operation
  152. *
  153. * @v drive Emulated drive
  154. * @ret status Status code
  155. */
  156. static int int13_get_last_status ( struct int13_drive *drive,
  157. struct i386_all_regs *ix86 __unused ) {
  158. DBG ( "Get status of last operation\n" );
  159. return drive->last_status;
  160. }
  161. /**
  162. * INT 13, 02 - Read sectors
  163. *
  164. * @v drive Emulated drive
  165. * @v al Number of sectors to read (must be nonzero)
  166. * @v ch Low bits of cylinder number
  167. * @v cl (bits 7:6) High bits of cylinder number
  168. * @v cl (bits 5:0) Sector number
  169. * @v dh Head number
  170. * @v es:bx Data buffer
  171. * @ret status Status code
  172. * @ret al Number of sectors read
  173. */
  174. static int int13_read_sectors ( struct int13_drive *drive,
  175. struct i386_all_regs *ix86 ) {
  176. unsigned long lba = chs_to_lba ( drive, ix86 );
  177. unsigned int count = ix86->regs.al;
  178. struct segoff data = {
  179. .segment = ix86->segs.es,
  180. .offset = ix86->regs.bx,
  181. };
  182. if ( drive->blockdev->blksize != INT13_BLKSIZE ) {
  183. DBG ( "Invalid blocksize (%d) for non-extended read\n",
  184. drive->blockdev->blksize );
  185. return INT13_STATUS_INVALID;
  186. }
  187. return int13_read ( drive, lba, data, count );
  188. }
  189. /**
  190. * INT 13, 03 - Write sectors
  191. *
  192. * @v drive Emulated drive
  193. * @v al Number of sectors to write (must be nonzero)
  194. * @v ch Low bits of cylinder number
  195. * @v cl (bits 7:6) High bits of cylinder number
  196. * @v cl (bits 5:0) Sector number
  197. * @v dh Head number
  198. * @v es:bx Data buffer
  199. * @ret status Status code
  200. * @ret al Number of sectors written
  201. */
  202. static int int13_write_sectors ( struct int13_drive *drive,
  203. struct i386_all_regs *ix86 ) {
  204. unsigned long lba = chs_to_lba ( drive, ix86 );
  205. unsigned int count = ix86->regs.al;
  206. struct segoff data = {
  207. .segment = ix86->segs.es,
  208. .offset = ix86->regs.bx,
  209. };
  210. if ( drive->blockdev->blksize != INT13_BLKSIZE ) {
  211. DBG ( "Invalid blocksize (%d) for non-extended write\n",
  212. drive->blockdev->blksize );
  213. return INT13_STATUS_INVALID;
  214. }
  215. return int13_write ( drive, lba, data, count );
  216. }
  217. /**
  218. * INT 13, 08 - Get drive parameters
  219. *
  220. * @v drive Emulated drive
  221. * @ret status Status code
  222. * @ret ch Low bits of maximum cylinder number
  223. * @ret cl (bits 7:6) High bits of maximum cylinder number
  224. * @ret cl (bits 5:0) Maximum sector number
  225. * @ret dh Maximum head number
  226. * @ret dl Number of drives
  227. */
  228. static int int13_get_parameters ( struct int13_drive *drive,
  229. struct i386_all_regs *ix86 ) {
  230. unsigned int max_cylinder = drive->cylinders - 1;
  231. unsigned int max_head = drive->heads - 1;
  232. unsigned int max_sector = drive->sectors_per_track; /* sic */
  233. DBG ( "Get drive parameters\n" );
  234. ix86->regs.ch = ( max_cylinder & 0xff );
  235. ix86->regs.cl = ( ( ( max_cylinder >> 8 ) << 6 ) | max_sector );
  236. ix86->regs.dh = max_head;
  237. get_real ( ix86->regs.dl, BDA_SEG, BDA_NUM_DRIVES );
  238. return 0;
  239. }
  240. /**
  241. * INT 13, 42 - Extended read
  242. *
  243. * @v drive Emulated drive
  244. * @v ds:si Disk address packet
  245. * @ret status Status code
  246. */
  247. static int int13_extended_read ( struct int13_drive *drive,
  248. struct i386_all_regs *ix86 ) {
  249. struct int13_disk_address addr;
  250. copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
  251. sizeof ( addr ) );
  252. return int13_read ( drive, addr.lba, addr.buffer, addr.count );
  253. }
  254. /**
  255. * INT 13, 43 - Extended write
  256. *
  257. * @v drive Emulated drive
  258. * @v ds:si Disk address packet
  259. * @ret status Status code
  260. */
  261. static int int13_extended_write ( struct int13_drive *drive,
  262. struct i386_all_regs *ix86 ) {
  263. struct int13_disk_address addr;
  264. copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
  265. sizeof ( addr ) );
  266. return int13_write ( drive, addr.lba, addr.buffer, addr.count );
  267. }
  268. /**
  269. * INT 13, 48 - Get extended parameters
  270. *
  271. * @v drive Emulated drive
  272. * @v ds:si Drive parameter table
  273. * @ret status Status code
  274. */
  275. static int int13_get_extended_parameters ( struct int13_drive *drive,
  276. struct i386_all_regs *ix86 ) {
  277. struct int13_disk_parameters params = {
  278. .bufsize = sizeof ( params ),
  279. .flags = INT13_FL_DMA_TRANSPARENT,
  280. .cylinders = drive->cylinders,
  281. .heads = drive->heads,
  282. .sectors_per_track = drive->sectors_per_track,
  283. .sectors = drive->blockdev->blocks,
  284. .sector_size = drive->blockdev->blksize,
  285. };
  286. DBG ( "Get extended drive parameters to %04x:%04x\n",
  287. ix86->segs.ds, ix86->regs.si );
  288. copy_to_real ( ix86->segs.ds, ix86->regs.si, &params,
  289. sizeof ( params ) );
  290. return 0;
  291. }
  292. /**
  293. * INT 13 handler
  294. *
  295. */
  296. static void int13 ( struct i386_all_regs *ix86 ) {
  297. struct int13_drive *drive;
  298. int status;
  299. list_for_each_entry ( drive, &drives, list ) {
  300. if ( drive->drive != ix86->regs.dl )
  301. continue;
  302. DBG ( "INT 13, %02x on drive %02x\n", ix86->regs.ah,
  303. ix86->regs.dl );
  304. switch ( ix86->regs.ah ) {
  305. case INT13_RESET:
  306. status = int13_reset ( drive, ix86 );
  307. break;
  308. case INT13_GET_LAST_STATUS:
  309. status = int13_get_last_status ( drive, ix86 );
  310. break;
  311. case INT13_READ_SECTORS:
  312. status = int13_read_sectors ( drive, ix86 );
  313. break;
  314. case INT13_WRITE_SECTORS:
  315. status = int13_write_sectors ( drive, ix86 );
  316. break;
  317. case INT13_GET_PARAMETERS:
  318. status = int13_get_parameters ( drive, ix86 );
  319. break;
  320. case INT13_EXTENDED_READ:
  321. status = int13_extended_read ( drive, ix86 );
  322. break;
  323. case INT13_EXTENDED_WRITE:
  324. status = int13_extended_write ( drive, ix86 );
  325. break;
  326. case INT13_GET_EXTENDED_PARAMETERS:
  327. status = int13_get_extended_parameters ( drive, ix86 );
  328. break;
  329. default:
  330. DBG ( "Unrecognised INT 13\n" );
  331. status = INT13_STATUS_INVALID;
  332. break;
  333. }
  334. /* Store status for INT 13,01 */
  335. drive->last_status = status;
  336. /* All functions return status via %ah and CF */
  337. ix86->regs.ah = status;
  338. if ( status ) {
  339. ix86->flags |= CF;
  340. DBG ( "INT13 failed with status %x\n", status );
  341. }
  342. /* Set OF to indicate to wrapper not to chain this call */
  343. ix86->flags |= OF;
  344. }
  345. }
  346. /**
  347. * Hook INT 13 handler
  348. *
  349. */
  350. static void hook_int13 ( void ) {
  351. /* Assembly wrapper to call int13(). int13() sets OF if we
  352. * should not chain to the previous handler. (The wrapper
  353. * clears CF and OF before calling int13()).
  354. */
  355. __asm__ __volatile__ ( ".section \".text16\", \"ax\", @progbits\n\t"
  356. ".code16\n\t"
  357. "\nint13_wrapper:\n\t"
  358. "orb $0, %%al\n\t" /* clear CF and OF */
  359. "pushl %0\n\t" /* call int13() */
  360. "data32 call prot_call\n\t"
  361. "jo 1f\n\t" /* chain if OF not set */
  362. "pushfw\n\t"
  363. "lcall *%%cs:int13_vector\n\t"
  364. "\n1:\n\t"
  365. "call 2f\n\t" /* return with flags intact */
  366. "lret $2\n\t"
  367. "\n2:\n\t"
  368. "ret $4\n\t"
  369. ".previous\n\t"
  370. ".code32\n\t" : :
  371. "i" ( int13 ) );
  372. hook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
  373. &int13_vector );
  374. }
  375. /**
  376. * Unhook INT 13 handler
  377. */
  378. static void unhook_int13 ( void ) {
  379. unhook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
  380. &int13_vector );
  381. }
  382. /**
  383. * Register INT 13 emulated drive
  384. *
  385. * @v drive Emulated drive
  386. *
  387. * Registers the drive with the INT 13 emulation subsystem, and hooks
  388. * the INT 13 interrupt vector (if not already hooked).
  389. *
  390. * The underlying block device must be valid. A drive number and
  391. * geometry will be assigned if left blank.
  392. */
  393. void register_int13_drive ( struct int13_drive *drive ) {
  394. uint8_t num_drives;
  395. unsigned long blocks;
  396. unsigned long blocks_per_cyl;
  397. /* Give drive a default geometry if none specified */
  398. if ( ! drive->heads )
  399. drive->heads = 255;
  400. if ( ! drive->sectors_per_track )
  401. drive->sectors_per_track = 63;
  402. if ( ! drive->cylinders ) {
  403. /* Avoid attempting a 64-bit divide on a 32-bit system */
  404. blocks = ( ( drive->blockdev->blocks <= ULONG_MAX ) ?
  405. drive->blockdev->blocks : ULONG_MAX );
  406. blocks_per_cyl = ( drive->heads * drive->sectors_per_track );
  407. assert ( blocks_per_cyl != 0 );
  408. drive->cylinders = ( blocks / blocks_per_cyl );
  409. }
  410. /* Assign drive number if none specified, update BIOS drive count */
  411. get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
  412. if ( ! drive->drive )
  413. drive->drive = ( num_drives | 0x80 );
  414. if ( num_drives <= ( drive->drive & 0x7f ) )
  415. num_drives = ( ( drive->drive & 0x7f ) + 1 );
  416. put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
  417. DBG ( "Registered INT13 drive %02x with C/H/S geometry %d/%d/%d\n",
  418. drive->drive, drive->cylinders, drive->heads,
  419. drive->sectors_per_track );
  420. /* Hook INT 13 vector if not already hooked */
  421. if ( list_empty ( &drives ) )
  422. hook_int13();
  423. /* Add to list of emulated drives */
  424. list_add ( &drive->list, &drives );
  425. }
  426. /**
  427. * Unregister INT 13 emulated drive
  428. *
  429. * @v drive Emulated drive
  430. *
  431. * Unregisters the drive from the INT 13 emulation subsystem. If this
  432. * is the last emulated drive, the INT 13 vector is unhooked (if
  433. * possible).
  434. */
  435. void unregister_int13_drive ( struct int13_drive *drive ) {
  436. /* Remove from list of emulated drives */
  437. list_del ( &drive->list );
  438. DBG ( "Unregistered INT13 drive %02x\n", drive->drive );
  439. /* Unhook INT 13 vector if no more drives */
  440. if ( list_empty ( &drives ) )
  441. unhook_int13();
  442. }
  443. /**
  444. * Attempt to boot from an INT 13 drive
  445. *
  446. * @v drive Drive number
  447. * @ret rc Return status code
  448. *
  449. * This boots from the specified INT 13 drive by loading the Master
  450. * Boot Record to 0000:7c00 and jumping to it. INT 18 is hooked to
  451. * capture an attempt by the MBR to boot the next device. (This is
  452. * the closest thing to a return path from an MBR).
  453. *
  454. * Note that this function can never return success, by definition.
  455. */
  456. int int13_boot ( unsigned int drive ) {
  457. int status, signature;
  458. int d0, d1;
  459. DBG ( "Booting from INT 13 drive %02x\n", drive );
  460. /* Use INT 13 to read the boot sector */
  461. REAL_EXEC ( rm_int13_boot,
  462. "pushw $0\n\t"
  463. "popw %%es\n\t"
  464. "int $0x13\n\t"
  465. "jc 1f\n\t"
  466. "xorl %%eax, %%eax\n\t"
  467. "\n1:\n\t"
  468. "movzwl %%es:0x7dfe, %%ebx\n\t",
  469. 4,
  470. OUT_CONSTRAINTS ( "=a" ( status ), "=b" ( signature ),
  471. "=c" ( d0 ), "=d" ( drive ) ),
  472. IN_CONSTRAINTS ( "0" ( 0x0201 ), "1" ( 0x7c00 ),
  473. "2" ( 0x0001 ), "3" ( drive ) ),
  474. CLOBBER ( "ebp" ) );
  475. if ( status )
  476. return -EIO;
  477. /* Check signature is correct */
  478. if ( signature != be16_to_cpu ( 0x55aa ) ) {
  479. DBG ( "Invalid disk signature %#04x (should be 0x55aa)\n",
  480. cpu_to_be16 ( signature ) );
  481. return -ENOEXEC;
  482. }
  483. /* Hook INTs 18 and 19 to capture failure paths */
  484. hook_bios_interrupt ( 0x18, ( unsigned int ) int13_exec_fail,
  485. &int18_vector );
  486. hook_bios_interrupt ( 0x19, ( unsigned int ) int13_exec_fail,
  487. &int19_vector );
  488. /* Boot the loaded sector */
  489. REAL_EXEC ( rm_int13_exec,
  490. "movw %%ss, %%ax\n\t" /* Preserve stack pointer */
  491. "movw %%ax, %%cs:int13_exec_saved_ss\n\t"
  492. "movw %%sp, %%cs:int13_exec_saved_sp\n\t"
  493. "ljmp $0, $0x7c00\n\t"
  494. "\nint13_exec_saved_ss: .word 0\n\t"
  495. "\nint13_exec_saved_sp: .word 0\n\t"
  496. "\nint13_exec_fail:\n\t"
  497. "movw %%cs:int13_exec_saved_ss, %%ax\n\t"
  498. "movw %%ax, %%ss\n\t"
  499. "movw %%cs:int13_exec_saved_sp, %%sp\n\t"
  500. "\n99:\n\t",
  501. 1,
  502. OUT_CONSTRAINTS ( "=d" ( d1 ) ),
  503. IN_CONSTRAINTS ( "d" ( drive ) ),
  504. CLOBBER ( "eax", "ebx", "ecx", "esi", "edi", "ebp" ) );
  505. DBG ( "Booted disk returned via INT 18\n" );
  506. /* Unhook INTs 18 and 19 */
  507. unhook_bios_interrupt ( 0x18, ( unsigned int ) int13_exec_fail,
  508. &int18_vector );
  509. unhook_bios_interrupt ( 0x19, ( unsigned int ) int13_exec_fail,
  510. &int19_vector );
  511. return -ECANCELED;
  512. }