00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 FILE_LICENCE ( GPL2_OR_LATER );
00025
00026 #include <errno.h>
00027 #include <realmode.h>
00028 #include <biosint.h>
00029 #include <console.h>
00030 #include <stdlib.h>
00031 #include <comboot.h>
00032 #include <bzimage.h>
00033 #include <pxe_call.h>
00034 #include <setjmp.h>
00035 #include <string.h>
00036 #include <gpxe/posix_io.h>
00037 #include <gpxe/process.h>
00038 #include <gpxe/serial.h>
00039 #include <gpxe/init.h>
00040 #include <gpxe/image.h>
00041 #include <usr/imgmgmt.h>
00042 #include "config/console.h"
00043 #include "config/serial.h"
00044
00045
00046 static char __data16_array ( syslinux_version, [] ) = "\r\ngPXE " VERSION;
00047 #define syslinux_version __use_data16 ( syslinux_version )
00048
00049
00050 static char __data16_array ( syslinux_copyright, [] ) = " http://etherboot.org";
00051 #define syslinux_copyright __use_data16 ( syslinux_copyright )
00052
00053 static char __data16_array ( syslinux_configuration_file, [] ) = "";
00054 #define syslinux_configuration_file __use_data16 ( syslinux_configuration_file )
00055
00056
00057 static uint8_t __data16 ( comboot_feature_flags ) = COMBOOT_FEATURE_IDLE_LOOP;
00058 #define comboot_feature_flags __use_data16 ( comboot_feature_flags )
00059
00060 typedef union {
00061 syslinux_pm_regs pm; syslinux_rm_regs rm;
00062 } syslinux_regs;
00063
00064
00065 static syslinux_regs __text16 ( comboot_initial_regs );
00066 #define comboot_initial_regs __use_text16 ( comboot_initial_regs )
00067
00068 static struct segoff __text16 ( int20_vector );
00069 #define int20_vector __use_text16 ( int20_vector )
00070
00071 static struct segoff __text16 ( int21_vector );
00072 #define int21_vector __use_text16 ( int21_vector )
00073
00074 static struct segoff __text16 ( int22_vector );
00075 #define int22_vector __use_text16 ( int22_vector )
00076
00077 extern void int20_wrapper ( void );
00078 extern void int21_wrapper ( void );
00079 extern void int22_wrapper ( void );
00080
00081
00082 rmjmp_buf comboot_return;
00083
00084
00085 struct image *comboot_replacement_image;
00086
00087
00088 static uint16_t comboot_graphics_mode = 0;
00089
00090
00091
00092
00093
00094 static void print_user_string ( unsigned int segment, unsigned int offset, char terminator ) {
00095 int i = 0;
00096 char c;
00097 userptr_t str = real_to_user ( segment, offset );
00098 for ( ; ; ) {
00099 copy_from_user ( &c, str, i, 1 );
00100 if ( c == terminator ) break;
00101 putchar ( c );
00102 i++;
00103 }
00104 }
00105
00106
00107
00108
00109
00110 static void shuffle ( unsigned int list_segment, unsigned int list_offset, unsigned int count )
00111 {
00112 comboot_shuffle_descriptor shuf[COMBOOT_MAX_SHUFFLE_DESCRIPTORS];
00113 unsigned int i;
00114
00115
00116 copy_from_user ( shuf, real_to_user ( list_segment, list_offset ), 0,
00117 count * sizeof( comboot_shuffle_descriptor ) );
00118
00119
00120 for ( i = 0; i < count; i++ ) {
00121 userptr_t src_u = phys_to_user ( shuf[ i ].src );
00122 userptr_t dest_u = phys_to_user ( shuf[ i ].dest );
00123
00124 if ( shuf[ i ].src == 0xFFFFFFFF ) {
00125
00126 memset_user ( dest_u, 0, 0, shuf[ i ].len );
00127 } else if ( shuf[ i ].dest == 0xFFFFFFFF ) {
00128
00129 count = shuf[ i ].len / sizeof( comboot_shuffle_descriptor );
00130 assert ( count <= COMBOOT_MAX_SHUFFLE_DESCRIPTORS );
00131 copy_from_user ( shuf, src_u, 0, shuf[ i ].len );
00132 i = -1;
00133 } else {
00134
00135 memmove_user ( dest_u, 0, src_u, 0, shuf[ i ].len );
00136 }
00137 }
00138 }
00139
00140
00141
00142
00143
00144 void comboot_force_text_mode ( void ) {
00145 if ( comboot_graphics_mode & COMBOOT_VIDEO_VESA ) {
00146
00147 __asm__ __volatile__ (
00148 REAL_CODE (
00149 "mov $0x4F02, %%ax\n\t"
00150 "mov $0x03, %%bx\n\t"
00151 "int $0x10\n\t"
00152 )
00153 : : );
00154 } else if ( comboot_graphics_mode & COMBOOT_VIDEO_GRAPHICS ) {
00155
00156 __asm__ __volatile__ (
00157 REAL_CODE (
00158 "mov $0x03, %%ax\n\t"
00159 "int $0x10\n\t"
00160 )
00161 : : );
00162 }
00163
00164 comboot_graphics_mode = 0;
00165 }
00166
00167
00168
00169
00170
00171 static int comboot_fetch_kernel ( char *kernel_file, char *cmdline ) {
00172 struct image *kernel = NULL;
00173 struct image *initrd = NULL;
00174 char *initrd_file;
00175 int rc;
00176
00177
00178 if ( ( initrd_file = strstr ( cmdline, "initrd=" ) ) != NULL ) {
00179 char *initrd_end;
00180
00181
00182 initrd_file += 7;
00183
00184
00185 initrd_end = strchr ( initrd_file, ' ' );
00186 if ( initrd_end )
00187 *initrd_end = '\0';
00188
00189 DBG ( "COMBOOT: fetching initrd '%s'\n", initrd_file );
00190
00191
00192 initrd = alloc_image();
00193 if ( ! initrd ) {
00194 DBG ( "COMBOOT: could not allocate initrd\n" );
00195 rc = -ENOMEM;
00196 goto out;
00197 }
00198 if ( ( rc = imgfetch ( initrd, initrd_file,
00199 register_image ) ) != 0 ) {
00200 DBG ( "COMBOOT: could not fetch initrd: %s\n",
00201 strerror ( rc ) );
00202 goto out;
00203 }
00204
00205
00206 if ( initrd_end )
00207 *initrd_end = ' ';
00208 }
00209
00210 DBG ( "COMBOOT: fetching kernel '%s'\n", kernel_file );
00211
00212
00213 kernel = alloc_image();
00214 if ( ! kernel ) {
00215 DBG ( "COMBOOT: could not allocate kernel\n" );
00216 rc = -ENOMEM;
00217 goto out;
00218 }
00219 if ( ( rc = imgfetch ( kernel, kernel_file,
00220 register_image ) ) != 0 ) {
00221 DBG ( "COMBOOT: could not fetch kernel: %s\n",
00222 strerror ( rc ) );
00223 goto out;
00224 }
00225 if ( ( rc = image_set_cmdline ( kernel, cmdline ) ) != 0 ) {
00226 DBG ( "COMBOOT: could not set kernel command line: %s\n",
00227 strerror ( rc ) );
00228 goto out;
00229 }
00230
00231
00232 assert ( comboot_replacement_image == NULL );
00233 comboot_replacement_image = image_get ( kernel );
00234
00235 out:
00236
00237
00238
00239
00240 image_put ( kernel );
00241 image_put ( initrd );
00242 return rc;
00243 }
00244
00245
00246
00247
00248
00249 static __asmcall void int20 ( struct i386_all_regs *ix86 __unused ) {
00250 rmlongjmp ( comboot_return, COMBOOT_EXIT );
00251 }
00252
00253
00254
00255
00256
00257 static __asmcall void int21 ( struct i386_all_regs *ix86 ) {
00258 ix86->flags |= CF;
00259
00260 switch ( ix86->regs.ah ) {
00261 case 0x00:
00262 case 0x4C:
00263 rmlongjmp ( comboot_return, COMBOOT_EXIT );
00264 break;
00265
00266 case 0x01:
00267 case 0x08:
00268
00269 ix86->regs.al = getchar( );
00270
00271
00272 if ( ix86->regs.al == 0x0A )
00273 ix86->regs.al = 0x0D;
00274
00275 if ( ix86->regs.ah == 0x01 )
00276 putchar ( ix86->regs.al );
00277
00278 ix86->flags &= ~CF;
00279 break;
00280
00281 case 0x02:
00282 putchar ( ix86->regs.dl );
00283 ix86->flags &= ~CF;
00284 break;
00285
00286 case 0x04:
00287 serial_putc ( ix86->regs.dl );
00288 ix86->flags &= ~CF;
00289 break;
00290
00291 case 0x09:
00292 print_user_string ( ix86->segs.ds, ix86->regs.dx, '$' );
00293 ix86->flags &= ~CF;
00294 break;
00295
00296 case 0x0B:
00297 if ( iskey() )
00298 ix86->regs.al = 0xFF;
00299 else
00300 ix86->regs.al = 0x00;
00301
00302 ix86->flags &= ~CF;
00303 break;
00304
00305 case 0x30:
00306
00307 ix86->regs.eax = 0x59530000;
00308 ix86->regs.ebx = 0x4C530000;
00309 ix86->regs.ecx = 0x4E490000;
00310 ix86->regs.edx = 0x58550000;
00311 ix86->flags &= ~CF;
00312 break;
00313
00314 default:
00315 DBG ( "COMBOOT unknown int21 function %02x\n", ix86->regs.ah );
00316 break;
00317 }
00318 }
00319
00320
00321
00322
00323
00324 static __asmcall void int22 ( struct i386_all_regs *ix86 ) {
00325 ix86->flags |= CF;
00326
00327 switch ( ix86->regs.ax ) {
00328 case 0x0001:
00329
00330
00331 ix86->regs.ax = 0x001D;
00332
00333
00334 ix86->regs.ch = 0;
00335 ix86->regs.cl = 0;
00336
00337
00338 ix86->regs.dl = BZI_LOADER_TYPE_GPXE;
00339
00340
00341 ix86->segs.es = rm_ds;
00342 ix86->regs.si = ( ( unsigned ) __from_data16 ( syslinux_version ) );
00343 ix86->regs.di = ( ( unsigned ) __from_data16 ( syslinux_copyright ) );
00344
00345 ix86->flags &= ~CF;
00346 break;
00347
00348 case 0x0002:
00349 print_user_string ( ix86->segs.es, ix86->regs.bx, '\0' );
00350 ix86->flags &= ~CF;
00351 break;
00352
00353 case 0x0003:
00354 {
00355 userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx );
00356 int len = strlen_user ( cmd_u, 0 );
00357 char cmd[len + 1];
00358 copy_from_user ( cmd, cmd_u, 0, len + 1 );
00359 DBG ( "COMBOOT: executing command '%s'\n", cmd );
00360 system ( cmd );
00361 DBG ( "COMBOOT: exiting after executing command...\n" );
00362 rmlongjmp ( comboot_return, COMBOOT_EXIT_COMMAND );
00363 }
00364 break;
00365
00366 case 0x0004:
00367
00368 rmlongjmp ( comboot_return, COMBOOT_EXIT_COMMAND );
00369 break;
00370
00371 case 0x0005:
00372 comboot_force_text_mode ( );
00373 ix86->flags &= ~CF;
00374 break;
00375
00376 case 0x0006:
00377 {
00378 int fd;
00379 userptr_t file_u = real_to_user ( ix86->segs.es, ix86->regs.si );
00380 int len = strlen_user ( file_u, 0 );
00381 char file[len + 1];
00382
00383 copy_from_user ( file, file_u, 0, len + 1 );
00384
00385 if ( file[0] == '\0' ) {
00386 DBG ( "COMBOOT: attempted open with empty file name\n" );
00387 break;
00388 }
00389
00390 DBG ( "COMBOOT: opening file '%s'\n", file );
00391
00392 fd = open ( file );
00393
00394 if ( fd < 0 ) {
00395 DBG ( "COMBOOT: error opening file %s\n", file );
00396 break;
00397 }
00398
00399
00400
00401
00402 #if (POSIX_FD_MAX > 65535)
00403 #error POSIX_FD_MAX too large
00404 #endif
00405 ix86->regs.si = (uint16_t) fd;
00406
00407 ix86->regs.cx = COMBOOT_FILE_BLOCKSZ;
00408 ix86->regs.eax = fsize ( fd );
00409 ix86->flags &= ~CF;
00410 }
00411 break;
00412
00413 case 0x0007:
00414 {
00415 int fd = ix86->regs.si;
00416 int len = ix86->regs.cx * COMBOOT_FILE_BLOCKSZ;
00417 int rc;
00418 fd_set fds;
00419 userptr_t buf = real_to_user ( ix86->segs.es, ix86->regs.bx );
00420
00421
00422 FD_ZERO ( &fds );
00423 FD_SET ( fd, &fds );
00424
00425 select ( &fds, 1 );
00426
00427 rc = read_user ( fd, buf, 0, len );
00428 if ( rc < 0 ) {
00429 DBG ( "COMBOOT: read failed\n" );
00430 ix86->regs.si = 0;
00431 break;
00432 }
00433
00434 ix86->regs.ecx = rc;
00435 ix86->flags &= ~CF;
00436 }
00437 break;
00438
00439 case 0x0008:
00440 {
00441 int fd = ix86->regs.si;
00442 close ( fd );
00443 ix86->flags &= ~CF;
00444 }
00445 break;
00446
00447 case 0x0009:
00448 if ( pxe_api_call_weak ( ix86 ) != 0 )
00449 ix86->flags |= CF;
00450 else
00451 ix86->flags &= ~CF;
00452 break;
00453
00454 case 0x000A:
00455
00456
00457
00458 ix86->regs.al = BZI_LOADER_TYPE_GPXE;
00459 ix86->flags &= ~CF;
00460 break;
00461
00462 case 0x000B:
00463 #if defined(CONSOLE_SERIAL) && !defined(COMPRESERVE)
00464 ix86->regs.dx = COMCONSOLE;
00465 ix86->regs.cx = 115200 / COMSPEED;
00466 ix86->regs.bx = 0;
00467 #else
00468 ix86->regs.dx = 0;
00469 #endif
00470
00471 ix86->flags &= ~CF;
00472 break;
00473
00474 case 0x000E:
00475
00476 ix86->segs.es = rm_ds;
00477 ix86->regs.bx = ( ( unsigned ) __from_data16 ( syslinux_configuration_file ) );
00478 ix86->flags &= ~CF;
00479 break;
00480
00481 case 0x000F:
00482
00483 ix86->regs.cx = 0;
00484 ix86->segs.es = 0;
00485 ix86->regs.bx = 0;
00486 ix86->flags &= ~CF;
00487 break;
00488
00489 case 0x0010:
00490 {
00491 userptr_t hostname_u = real_to_user ( ix86->segs.es, ix86->regs.bx );
00492 int len = strlen_user ( hostname_u, 0 );
00493 char hostname[len];
00494 struct in_addr addr;
00495
00496 copy_from_user ( hostname, hostname_u, 0, len + 1 );
00497
00498
00499
00500
00501
00502
00503 comboot_resolv ( hostname, &addr );
00504
00505 ix86->regs.eax = addr.s_addr;
00506 ix86->flags &= ~CF;
00507 }
00508 break;
00509
00510 case 0x0011:
00511 ix86->regs.cx = COMBOOT_MAX_SHUFFLE_DESCRIPTORS;
00512 ix86->flags &= ~CF;
00513 break;
00514
00515 case 0x0012:
00516 if ( ix86->regs.cx > COMBOOT_MAX_SHUFFLE_DESCRIPTORS )
00517 break;
00518
00519
00520 shutdown ( SHUTDOWN_BOOT );
00521
00522
00523 shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx );
00524
00525
00526 __asm__ __volatile__ (
00527 REAL_CODE (
00528 "pushw %0\n\t"
00529 "popw %%ds\n\t"
00530 "pushl %1\n\t"
00531 "lret\n\t"
00532 )
00533 :
00534 : "r" ( ix86->segs.ds ),
00535 "r" ( ix86->regs.ebp ),
00536 "d" ( ix86->regs.ebx ),
00537 "S" ( ix86->regs.esi ) );
00538
00539 assert ( 0 );
00540
00541 break;
00542
00543 case 0x0013:
00544 step ( );
00545 ix86->flags &= ~CF;
00546 break;
00547
00548 case 0x0015:
00549 ix86->segs.es = rm_ds;
00550 ix86->regs.bx = ( ( unsigned ) __from_data16 ( &comboot_feature_flags ) );
00551 ix86->regs.cx = 1;
00552 ix86->flags &= ~CF;
00553 break;
00554
00555 case 0x0016:
00556 {
00557 userptr_t file_u = real_to_user ( ix86->segs.ds, ix86->regs.si );
00558 userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx );
00559 int file_len = strlen_user ( file_u, 0 );
00560 int cmd_len = strlen_user ( cmd_u, 0 );
00561 char file[file_len + 1];
00562 char cmd[cmd_len + 1];
00563
00564 copy_from_user ( file, file_u, 0, file_len + 1 );
00565 copy_from_user ( cmd, cmd_u, 0, cmd_len + 1 );
00566
00567 DBG ( "COMBOOT: run kernel %s %s\n", file, cmd );
00568 comboot_fetch_kernel ( file, cmd );
00569
00570
00571
00572
00573
00574 DBG ( "COMBOOT: exiting to run kernel...\n" );
00575 rmlongjmp ( comboot_return, COMBOOT_EXIT_RUN_KERNEL );
00576 }
00577 break;
00578
00579 case 0x0017:
00580 comboot_graphics_mode = ix86->regs.bx;
00581 ix86->flags &= ~CF;
00582 break;
00583
00584 case 0x0018:
00585
00586 ix86->regs.al = 0;
00587 ix86->segs.es = 0;
00588 ix86->regs.bx = 0;
00589 ix86->flags &= ~CF;
00590 break;
00591
00592 case 0x001B:
00593 if ( ix86->regs.cx > COMBOOT_MAX_SHUFFLE_DESCRIPTORS )
00594 break;
00595
00596
00597 shutdown ( SHUTDOWN_BOOT );
00598
00599
00600 shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx );
00601
00602
00603 memcpy_user ( real_to_user ( rm_cs, (unsigned) __from_text16 ( &comboot_initial_regs ) ), 0,
00604 real_to_user ( ix86->segs.ds, ix86->regs.si ), 0,
00605 sizeof(syslinux_rm_regs) );
00606
00607
00608 __asm__ __volatile__ (
00609 REAL_CODE (
00610
00611 "pushw %%cs\n\t"
00612 "popw %%ss\n\t"
00613 "movw $comboot_initial_regs, %%sp\n\t"
00614
00615
00616 "popw %%es\n\t"
00617 "popw %%ax\n\t"
00618 "popw %%ds\n\t"
00619 "popw %%ax\n\t"
00620 "popw %%fs\n\t"
00621 "popw %%gs\n\t"
00622
00623
00624 "popl %%eax\n\t"
00625 "popl %%ecx\n\t"
00626 "popl %%edx\n\t"
00627 "popl %%ebx\n\t"
00628 "popl %%ebp\n\t"
00629 "popl %%ebp\n\t"
00630 "popl %%esi\n\t"
00631 "popl %%edi\n\t"
00632
00633
00634 "movw $(comboot_initial_regs + 6), %%sp\n\t"
00635 "popw %%ss\n\t"
00636 "movl %%cs:(comboot_initial_regs + 28), %%esp\n\t"
00637
00638 "ljmp *%%cs:(comboot_initial_regs + 44)\n\t"
00639 )
00640 : : );
00641
00642 break;
00643
00644 case 0x001C:
00645
00646 ix86->regs.cx = 0;
00647 ix86->flags &= ~CF;
00648 break;
00649
00650 case 0x001D:
00651
00652 ix86->flags &= ~CF;
00653 break;
00654
00655 default:
00656 DBG ( "COMBOOT unknown int22 function %04x\n", ix86->regs.ax );
00657 break;
00658 }
00659 }
00660
00661
00662
00663
00664 void hook_comboot_interrupts ( ) {
00665
00666 __asm__ __volatile__ (
00667 TEXT16_CODE ( "\nint20_wrapper:\n\t"
00668 "pushl %0\n\t"
00669 "pushw %%cs\n\t"
00670 "call prot_call\n\t"
00671 "addw $4, %%sp\n\t"
00672 "iret\n\t" )
00673 : : "i" ( int20 ) );
00674
00675 hook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper,
00676 &int20_vector );
00677
00678 __asm__ __volatile__ (
00679 TEXT16_CODE ( "\nint21_wrapper:\n\t"
00680 "pushl %0\n\t"
00681 "pushw %%cs\n\t"
00682 "call prot_call\n\t"
00683 "addw $4, %%sp\n\t"
00684 "iret\n\t" )
00685 : : "i" ( int21 ) );
00686
00687 hook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper,
00688 &int21_vector );
00689
00690 __asm__ __volatile__ (
00691 TEXT16_CODE ( "\nint22_wrapper:\n\t"
00692 "pushl %0\n\t"
00693 "pushw %%cs\n\t"
00694 "call prot_call\n\t"
00695 "addw $4, %%sp\n\t"
00696 "iret\n\t" )
00697 : : "i" ( int22) );
00698
00699 hook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper,
00700 &int22_vector );
00701 }
00702
00703
00704
00705
00706 void unhook_comboot_interrupts ( ) {
00707
00708 unhook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper,
00709 &int20_vector );
00710
00711 unhook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper,
00712 &int21_vector );
00713
00714 unhook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper,
00715 &int22_vector );
00716 }