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