1/* 2 * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>. 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 19/** 20 * @file SYSLINUX COMBOOT API 21 * 22 */ 23 24FILE_LICENCE ( GPL2_OR_LATER ); 25 26#include <errno.h> 27#include <realmode.h> 28#include <biosint.h> 29#include <console.h> 30#include <stdlib.h> 31#include <comboot.h> 32#include <bzimage.h> 33#include <pxe_call.h> 34#include <setjmp.h> 35#include <string.h> 36#include <gpxe/posix_io.h> 37#include <gpxe/process.h> 38#include <gpxe/serial.h> 39#include <gpxe/init.h> 40#include <gpxe/image.h> 41#include <usr/imgmgmt.h> 42#include "config/console.h" 43#include "config/serial.h" 44 45/** The "SYSLINUX" version string */ 46static char __data16_array ( syslinux_version, [] ) = "gPXE " VERSION; 47#define syslinux_version __use_data16 ( syslinux_version ) 48 49/** The "SYSLINUX" copyright string */ 50static char __data16_array ( syslinux_copyright, [] ) = "http://etherboot.org"; 51#define syslinux_copyright __use_data16 ( syslinux_copyright ) 52 53static char __data16_array ( syslinux_configuration_file, [] ) = ""; 54#define syslinux_configuration_file __use_data16 ( syslinux_configuration_file ) 55 56/** Feature flags */ 57static uint8_t __data16 ( comboot_feature_flags ) = COMBOOT_FEATURE_IDLE_LOOP; 58#define comboot_feature_flags __use_data16 ( comboot_feature_flags ) 59 60typedef union { 61 syslinux_pm_regs pm; syslinux_rm_regs rm; 62} syslinux_regs; 63 64/** Initial register values for INT 22h AX=1Ah and 1Bh */ 65static syslinux_regs __text16 ( comboot_initial_regs ); 66#define comboot_initial_regs __use_text16 ( comboot_initial_regs ) 67 68static struct segoff __text16 ( int20_vector ); 69#define int20_vector __use_text16 ( int20_vector ) 70 71static struct segoff __text16 ( int21_vector ); 72#define int21_vector __use_text16 ( int21_vector ) 73 74static struct segoff __text16 ( int22_vector ); 75#define int22_vector __use_text16 ( int22_vector ) 76 77extern void int20_wrapper ( void ); 78extern void int21_wrapper ( void ); 79extern void int22_wrapper ( void ); 80 81/* setjmp/longjmp context buffer used to return after loading an image */ 82rmjmp_buf comboot_return; 83 84/* Replacement image when exiting with COMBOOT_EXIT_RUN_KERNEL */ 85struct image *comboot_replacement_image; 86 87/* Mode flags set by INT 22h AX=0017h */ 88static uint16_t comboot_graphics_mode = 0; 89 90 91/** 92 * Print a string with a particular terminator 93 */ 94static void print_user_string ( unsigned int segment, unsigned int offset, char terminator ) { 95 int i = 0; 96 char c; 97 userptr_t str = real_to_user ( segment, offset ); 98 for ( ; ; ) { 99 copy_from_user ( &c, str, i, 1 ); 100 if ( c == terminator ) break; 101 putchar ( c ); 102 i++; 103 } 104} 105 106 107/** 108 * Perform a series of memory copies from a list in low memory 109 */ 110static void shuffle ( unsigned int list_segment, unsigned int list_offset, unsigned int count ) 111{ 112 comboot_shuffle_descriptor shuf[COMBOOT_MAX_SHUFFLE_DESCRIPTORS]; 113 unsigned int i; 114 115 /* Copy shuffle descriptor list so it doesn't get overwritten */ 116 copy_from_user ( shuf, real_to_user ( list_segment, list_offset ), 0, 117 count * sizeof( comboot_shuffle_descriptor ) ); 118 119 /* Do the copies */ 120 for ( i = 0; i < count; i++ ) { 121 userptr_t src_u = phys_to_user ( shuf[ i ].src ); 122 userptr_t dest_u = phys_to_user ( shuf[ i ].dest ); 123 124 if ( shuf[ i ].src == 0xFFFFFFFF ) { 125 /* Fill with 0 instead of copying */ 126 memset_user ( dest_u, 0, 0, shuf[ i ].len ); 127 } else if ( shuf[ i ].dest == 0xFFFFFFFF ) { 128 /* Copy new list of descriptors */ 129 count = shuf[ i ].len / sizeof( comboot_shuffle_descriptor ); 130 assert ( count <= COMBOOT_MAX_SHUFFLE_DESCRIPTORS ); 131 copy_from_user ( shuf, src_u, 0, shuf[ i ].len ); 132 i = -1; 133 } else { 134 /* Regular copy */ 135 memmove_user ( dest_u, 0, src_u, 0, shuf[ i ].len ); 136 } 137 } 138} 139 140 141/** 142 * Set default text mode 143 */ 144void comboot_force_text_mode ( void ) { 145 if ( comboot_graphics_mode & COMBOOT_VIDEO_VESA ) { 146 /* Set VGA mode 3 via VESA VBE mode set */ 147 __asm__ __volatile__ ( 148 REAL_CODE ( 149 "mov $0x4F02, %%ax\n\t" 150 "mov $0x03, %%bx\n\t" 151 "int $0x10\n\t" 152 ) 153 : : ); 154 } else if ( comboot_graphics_mode & COMBOOT_VIDEO_GRAPHICS ) { 155 /* Set VGA mode 3 via standard VGA mode set */ 156 __asm__ __volatile__ ( 157 REAL_CODE ( 158 "mov $0x03, %%ax\n\t" 159 "int $0x10\n\t" 160 ) 161 : : ); 162 } 163 164 comboot_graphics_mode = 0; 165} 166 167 168/** 169 * Fetch kernel and optional initrd 170 */ 171static int comboot_fetch_kernel ( char *kernel_file, char *cmdline ) { 172 struct image *kernel = NULL; 173 struct image *initrd = NULL; 174 char *initrd_file; 175 int rc; 176 177 /* Find initrd= parameter, if any */ 178 if ( ( initrd_file = strstr ( cmdline, "initrd=" ) ) != NULL ) { 179 char *initrd_end; 180 181 /* skip "initrd=" */ 182 initrd_file += 7; 183 184 /* Find terminating space, if any, and replace with NUL */ 185 initrd_end = strchr ( initrd_file, ' ' ); 186 if ( initrd_end ) 187 *initrd_end = '\0'; 188 189 DBG ( "COMBOOT: fetching initrd '%s'\n", initrd_file ); 190 191 /* Allocate and fetch initrd */ 192 initrd = alloc_image(); 193 if ( ! initrd ) { 194 DBG ( "COMBOOT: could not allocate initrd\n" ); 195 rc = -ENOMEM; 196 goto out; 197 } 198 if ( ( rc = imgfetch ( initrd, initrd_file, 199 register_image ) ) != 0 ) { 200 DBG ( "COMBOOT: could not fetch initrd: %s\n", 201 strerror ( rc ) ); 202 goto out; 203 } 204 205 /* Restore space after initrd name, if applicable */ 206 if ( initrd_end ) 207 *initrd_end = ' '; 208 } 209 210 DBG ( "COMBOOT: fetching kernel '%s'\n", kernel_file ); 211 212 /* Allocate and fetch kernel */ 213 kernel = alloc_image(); 214 if ( ! kernel ) { 215 DBG ( "COMBOOT: could not allocate kernel\n" ); 216 rc = -ENOMEM; 217 goto out; 218 } 219 if ( ( rc = imgfetch ( kernel, kernel_file, 220 register_image ) ) != 0 ) { 221 DBG ( "COMBOOT: could not fetch kernel: %s\n", 222 strerror ( rc ) ); 223 goto out; 224 } 225 if ( ( rc = image_set_cmdline ( kernel, cmdline ) ) != 0 ) { 226 DBG ( "COMBOOT: could not set kernel command line: %s\n", 227 strerror ( rc ) ); 228 goto out; 229 } 230 231 /* Store kernel as replacement image */ 232 assert ( comboot_replacement_image == NULL ); 233 comboot_replacement_image = image_get ( kernel ); 234 235 out: 236 /* Drop image references unconditionally; either we want to 237 * discard them, or they have been registered and we should 238 * drop out local reference. 239 */ 240 image_put ( kernel ); 241 image_put ( initrd ); 242 return rc; 243} 244 245 246/** 247 * Terminate program interrupt handler 248 */ 249static __asmcall void int20 ( struct i386_all_regs *ix86 __unused ) { 250 rmlongjmp ( comboot_return, COMBOOT_EXIT ); 251} 252 253 254/** 255 * DOS-compatible API 256 */ 257static __asmcall void int21 ( struct i386_all_regs *ix86 ) { 258 ix86->flags |= CF; 259 260 switch ( ix86->regs.ah ) { 261 case 0x00: 262 case 0x4C: /* Terminate program */ 263 rmlongjmp ( comboot_return, COMBOOT_EXIT ); 264 break; 265 266 case 0x01: /* Get Key with Echo */ 267 case 0x08: /* Get Key without Echo */ 268 /* TODO: handle extended characters? */ 269 ix86->regs.al = getchar( ); 270 271 /* Enter */ 272 if ( ix86->regs.al == 0x0A ) 273 ix86->regs.al = 0x0D; 274 275 if ( ix86->regs.ah == 0x01 ) 276 putchar ( ix86->regs.al ); 277 278 ix86->flags &= ~CF; 279 break; 280 281 case 0x02: /* Write Character */ 282 putchar ( ix86->regs.dl ); 283 ix86->flags &= ~CF; 284 break; 285 286 case 0x04: /* Write Character to Serial Port */ 287 serial_putc ( ix86->regs.dl ); 288 ix86->flags &= ~CF; 289 break; 290 291 case 0x09: /* Write DOS String to Console */ 292 print_user_string ( ix86->segs.ds, ix86->regs.dx, '$' ); 293 ix86->flags &= ~CF; 294 break; 295 296 case 0x0B: /* Check Keyboard */ 297 if ( iskey() ) 298 ix86->regs.al = 0xFF; 299 else 300 ix86->regs.al = 0x00; 301 302 ix86->flags &= ~CF; 303 break; 304 305 case 0x30: /* Check DOS Version */ 306 /* Bottom halves all 0; top halves spell "SYSLINUX" */ 307 ix86->regs.eax = 0x59530000; 308 ix86->regs.ebx = 0x4C530000; 309 ix86->regs.ecx = 0x4E490000; 310 ix86->regs.edx = 0x58550000; 311 ix86->flags &= ~CF; 312 break; 313 314 default: 315 DBG ( "COMBOOT unknown int21 function %02x\n", ix86->regs.ah ); 316 break; 317 } 318} 319 320 321/** 322 * SYSLINUX API 323 */ 324static __asmcall void int22 ( struct i386_all_regs *ix86 ) { 325 ix86->flags |= CF; 326 327 switch ( ix86->regs.ax ) { 328 case 0x0001: /* Get Version */ 329 330 /* Number of INT 22h API functions available */ 331 ix86->regs.ax = 0x001D; 332 333 /* SYSLINUX version number */ 334 ix86->regs.ch = 0; /* major */ 335 ix86->regs.cl = 0; /* minor */ 336 337 /* SYSLINUX derivative ID */ 338 ix86->regs.dl = BZI_LOADER_TYPE_GPXE; 339 340 /* SYSLINUX version and copyright strings */ 341 ix86->segs.es = rm_ds; 342 ix86->regs.si = ( ( unsigned ) __from_data16 ( syslinux_version ) ); 343 ix86->regs.di = ( ( unsigned ) __from_data16 ( syslinux_copyright ) ); 344 345 ix86->flags &= ~CF; 346 break; 347 348 case 0x0002: /* Write String */ 349 print_user_string ( ix86->segs.es, ix86->regs.bx, '\0' ); 350 ix86->flags &= ~CF; 351 break; 352 353 case 0x0003: /* Run command */ 354 { 355 userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx ); 356 int len = strlen_user ( cmd_u, 0 ); 357 char cmd[len + 1]; 358 copy_from_user ( cmd, cmd_u, 0, len + 1 ); 359 DBG ( "COMBOOT: executing command '%s'\n", cmd ); 360 system ( cmd ); 361 DBG ( "COMBOOT: exiting after executing command...\n" ); 362 rmlongjmp ( comboot_return, COMBOOT_EXIT_COMMAND ); 363 } 364 break; 365 366 case 0x0004: /* Run default command */ 367 /* FIXME: just exit for now */ 368 rmlongjmp ( comboot_return, COMBOOT_EXIT_COMMAND ); 369 break; 370 371 case 0x0005: /* Force text mode */ 372 comboot_force_text_mode ( ); 373 ix86->flags &= ~CF; 374 break; 375 376 case 0x0006: /* Open file */ 377 { 378 int fd; 379 userptr_t file_u = real_to_user ( ix86->segs.es, ix86->regs.si ); 380 int len = strlen_user ( file_u, 0 ); 381 char file[len + 1]; 382 383 copy_from_user ( file, file_u, 0, len + 1 ); 384 385 if ( file[0] == '\0' ) { 386 DBG ( "COMBOOT: attempted open with empty file name\n" ); 387 break; 388 } 389 390 DBG ( "COMBOOT: opening file '%s'\n", file ); 391 392 fd = open ( file ); 393 394 if ( fd < 0 ) { 395 DBG ( "COMBOOT: error opening file %s\n", file ); 396 break; 397 } 398 399 /* This relies on the fact that a gPXE POSIX fd will 400 * always fit in 16 bits. 401 */ 402#if (POSIX_FD_MAX > 65535) 403#error POSIX_FD_MAX too large 404#endif 405 ix86->regs.si = (uint16_t) fd; 406 407 ix86->regs.cx = COMBOOT_FILE_BLOCKSZ; 408 ix86->regs.eax = fsize ( fd ); 409 ix86->flags &= ~CF; 410 } 411 break; 412 413 case 0x0007: /* Read file */ 414 { 415 int fd = ix86->regs.si; 416 int len = ix86->regs.cx * COMBOOT_FILE_BLOCKSZ; 417 int rc; 418 fd_set fds; 419 userptr_t buf = real_to_user ( ix86->segs.es, ix86->regs.bx ); 420 421 /* Wait for data ready to read */ 422 FD_ZERO ( &fds ); 423 FD_SET ( fd, &fds ); 424 425 select ( &fds, 1 ); 426 427 rc = read_user ( fd, buf, 0, len ); 428 if ( rc < 0 ) { 429 DBG ( "COMBOOT: read failed\n" ); 430 ix86->regs.si = 0; 431 break; 432 } 433 434 ix86->regs.ecx = rc; 435 ix86->flags &= ~CF; 436 } 437 break; 438 439 case 0x0008: /* Close file */ 440 { 441 int fd = ix86->regs.si; 442 close ( fd ); 443 ix86->flags &= ~CF; 444 } 445 break; 446 447 case 0x0009: /* Call PXE Stack */ 448 if ( pxe_api_call_weak ( ix86 ) != 0 ) 449 ix86->flags |= CF; 450 else 451 ix86->flags &= ~CF; 452 break; 453 454 case 0x000A: /* Get Derivative-Specific Information */ 455 456 /* gPXE has its own derivative ID, so there is no defined 457 * output here; just return AL for now */ 458 ix86->regs.al = BZI_LOADER_TYPE_GPXE; 459 ix86->flags &= ~CF; 460 break; 461 462 case 0x000B: /* Get Serial Console Configuration */ 463#if defined(CONSOLE_SERIAL) && !defined(COMPRESERVE) 464 ix86->regs.dx = COMCONSOLE; 465 ix86->regs.cx = 115200 / COMSPEED; 466 ix86->regs.bx = 0; 467#else 468 ix86->regs.dx = 0; 469#endif 470 471 ix86->flags &= ~CF; 472 break; 473 474 case 0x000E: /* Get configuration file name */ 475 /* FIXME: stub */ 476 ix86->segs.es = rm_ds; 477 ix86->regs.bx = ( ( unsigned ) __from_data16 ( syslinux_configuration_file ) ); 478 ix86->flags &= ~CF; 479 break; 480 481 case 0x000F: /* Get IPAPPEND strings */ 482 /* FIXME: stub */ 483 ix86->regs.cx = 0; 484 ix86->segs.es = 0; 485 ix86->regs.bx = 0; 486 ix86->flags &= ~CF; 487 break; 488 489 case 0x0010: /* Resolve hostname */ 490 { 491 userptr_t hostname_u = real_to_user ( ix86->segs.es, ix86->regs.bx ); 492 int len = strlen_user ( hostname_u, 0 ); 493 char hostname[len]; 494 struct in_addr addr; 495 496 copy_from_user ( hostname, hostname_u, 0, len + 1 ); 497 498 /* TODO: 499 * "If the hostname does not contain a dot (.), the 500 * local domain name is automatically appended." 501 */ 502 503 comboot_resolv ( hostname, &addr ); 504 505 ix86->regs.eax = addr.s_addr; 506 ix86->flags &= ~CF; 507 } 508 break; 509 510 case 0x0011: /* Maximum number of shuffle descriptors */ 511 ix86->regs.cx = COMBOOT_MAX_SHUFFLE_DESCRIPTORS; 512 ix86->flags &= ~CF; 513 break; 514 515 case 0x0012: /* Cleanup, shuffle and boot */ 516 if ( ix86->regs.cx > COMBOOT_MAX_SHUFFLE_DESCRIPTORS ) 517 break; 518 519 /* Perform final cleanup */ 520 shutdown ( SHUTDOWN_BOOT ); 521 522 /* Perform sequence of copies */ 523 shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx ); 524 525 /* Jump to real-mode entry point */ 526 __asm__ __volatile__ ( 527 REAL_CODE ( 528 "pushw %0\n\t" 529 "popw %%ds\n\t" 530 "pushl %1\n\t" 531 "lret\n\t" 532 ) 533 : 534 : "r" ( ix86->segs.ds ), 535 "r" ( ix86->regs.ebp ), 536 "d" ( ix86->regs.ebx ), 537 "S" ( ix86->regs.esi ) ); 538 539 assert ( 0 ); /* Execution should never reach this point */ 540 541 break; 542 543 case 0x0013: /* Idle loop call */ 544 step ( ); 545 ix86->flags &= ~CF; 546 break; 547 548 case 0x0015: /* Get feature flags */ 549 ix86->segs.es = rm_ds; 550 ix86->regs.bx = ( ( unsigned ) __from_data16 ( &comboot_feature_flags ) ); 551 ix86->regs.cx = 1; /* Number of feature flag bytes */ 552 ix86->flags &= ~CF; 553 break; 554 555 case 0x0016: /* Run kernel image */ 556 { 557 userptr_t file_u = real_to_user ( ix86->segs.ds, ix86->regs.si ); 558 userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx ); 559 int file_len = strlen_user ( file_u, 0 ); 560 int cmd_len = strlen_user ( cmd_u, 0 ); 561 char file[file_len + 1]; 562 char cmd[cmd_len + 1]; 563 564 copy_from_user ( file, file_u, 0, file_len + 1 ); 565 copy_from_user ( cmd, cmd_u, 0, cmd_len + 1 ); 566 567 DBG ( "COMBOOT: run kernel %s %s\n", file, cmd ); 568 comboot_fetch_kernel ( file, cmd ); 569 /* Technically, we should return if we 570 * couldn't load the kernel, but it's not safe 571 * to do that since we have just overwritten 572 * part of the COMBOOT program's memory space. 573 */ 574 DBG ( "COMBOOT: exiting to run kernel...\n" ); 575 rmlongjmp ( comboot_return, COMBOOT_EXIT_RUN_KERNEL ); 576 } 577 break; 578 579 case 0x0017: /* Report video mode change */ 580 comboot_graphics_mode = ix86->regs.bx; 581 ix86->flags &= ~CF; 582 break; 583 584 case 0x0018: /* Query custom font */ 585 /* FIXME: stub */ 586 ix86->regs.al = 0; 587 ix86->segs.es = 0; 588 ix86->regs.bx = 0; 589 ix86->flags &= ~CF; 590 break; 591 592 case 0x001B: /* Cleanup, shuffle and boot to real mode */ 593 if ( ix86->regs.cx > COMBOOT_MAX_SHUFFLE_DESCRIPTORS ) 594 break; 595 596 /* Perform final cleanup */ 597 shutdown ( SHUTDOWN_BOOT ); 598 599 /* Perform sequence of copies */ 600 shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx ); 601 602 /* Copy initial register values to .text16 */ 603 memcpy_user ( real_to_user ( rm_cs, (unsigned) __from_text16 ( &comboot_initial_regs ) ), 0, 604 real_to_user ( ix86->segs.ds, ix86->regs.si ), 0, 605 sizeof(syslinux_rm_regs) ); 606 607 /* Load initial register values */ 608 __asm__ __volatile__ ( 609 REAL_CODE ( 610 /* Point SS:SP at the register value structure */ 611 "pushw %%cs\n\t" 612 "popw %%ss\n\t" 613 "movw $comboot_initial_regs, %%sp\n\t" 614 615 /* Segment registers */ 616 "popw %%es\n\t" 617 "popw %%ax\n\t" /* Skip CS */ 618 "popw %%ds\n\t" 619 "popw %%ax\n\t" /* Skip SS for now */ 620 "popw %%fs\n\t" 621 "popw %%gs\n\t" 622 623 /* GP registers */ 624 "popl %%eax\n\t" 625 "popl %%ecx\n\t" 626 "popl %%edx\n\t" 627 "popl %%ebx\n\t" 628 "popl %%ebp\n\t" /* Skip ESP for now */ 629 "popl %%ebp\n\t" 630 "popl %%esi\n\t" 631 "popl %%edi\n\t" 632 633 /* Load correct SS:ESP */ 634 "movw $(comboot_initial_regs + 6), %%sp\n\t" 635 "popw %%ss\n\t" 636 "movl %%cs:(comboot_initial_regs + 28), %%esp\n\t" 637 638 "ljmp *%%cs:(comboot_initial_regs + 44)\n\t" 639 ) 640 : : ); 641 642 break; 643 644 case 0x001C: /* Get pointer to auxilliary data vector */ 645 /* FIXME: stub */ 646 ix86->regs.cx = 0; /* Size of the ADV */ 647 ix86->flags &= ~CF; 648 break; 649 650 case 0x001D: /* Write auxilliary data vector */ 651 /* FIXME: stub */ 652 ix86->flags &= ~CF; 653 break; 654 655 default: 656 DBG ( "COMBOOT unknown int22 function %04x\n", ix86->regs.ax ); 657 break; 658 } 659} 660 661/** 662 * Hook BIOS interrupts related to COMBOOT API (INT 20h, 21h, 22h) 663 */ 664void hook_comboot_interrupts ( ) { 665 666 __asm__ __volatile__ ( 667 TEXT16_CODE ( "\nint20_wrapper:\n\t" 668 "pushl %0\n\t" 669 "pushw %%cs\n\t" 670 "call prot_call\n\t" 671 "addw $4, %%sp\n\t" 672 "iret\n\t" ) 673 : : "i" ( int20 ) ); 674 675 hook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper, 676 &int20_vector ); 677 678 __asm__ __volatile__ ( 679 TEXT16_CODE ( "\nint21_wrapper:\n\t" 680 "pushl %0\n\t" 681 "pushw %%cs\n\t" 682 "call prot_call\n\t" 683 "addw $4, %%sp\n\t" 684 "iret\n\t" ) 685 : : "i" ( int21 ) ); 686 687 hook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper, 688 &int21_vector ); 689 690 __asm__ __volatile__ ( 691 TEXT16_CODE ( "\nint22_wrapper:\n\t" 692 "pushl %0\n\t" 693 "pushw %%cs\n\t" 694 "call prot_call\n\t" 695 "addw $4, %%sp\n\t" 696 "iret\n\t" ) 697 : : "i" ( int22) ); 698 699 hook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper, 700 &int22_vector ); 701} 702 703/** 704 * Unhook BIOS interrupts related to COMBOOT API (INT 20h, 21h, 22h) 705 */ 706void unhook_comboot_interrupts ( ) { 707 708 unhook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper, 709 &int20_vector ); 710 711 unhook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper, 712 &int21_vector ); 713 714 unhook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper, 715 &int22_vector ); 716} 717