iPXE
comboot.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  */
00019 
00020 /**
00021  * @file
00022  *
00023  * SYSLINUX COMBOOT (16-bit) image format
00024  *
00025  */
00026 
00027 FILE_LICENCE ( GPL2_OR_LATER );
00028 
00029 #include <stdint.h>
00030 #include <stdlib.h>
00031 #include <string.h>
00032 #include <strings.h>
00033 #include <errno.h>
00034 #include <assert.h>
00035 #include <realmode.h>
00036 #include <basemem.h>
00037 #include <comboot.h>
00038 #include <ipxe/uaccess.h>
00039 #include <ipxe/image.h>
00040 #include <ipxe/segment.h>
00041 #include <ipxe/init.h>
00042 #include <ipxe/features.h>
00043 #include <ipxe/console.h>
00044 
00045 FEATURE ( FEATURE_IMAGE, "COMBOOT", DHCP_EB_FEATURE_COMBOOT, 1 );
00046 
00047 /**
00048  * COMBOOT PSP, copied to offset 0 of code segment
00049  */
00050 struct comboot_psp {
00051         /** INT 20 instruction, executed if COMBOOT image returns with RET */
00052         uint16_t int20;
00053         /** Segment of first non-free paragraph of memory */
00054         uint16_t first_non_free_para;
00055 };
00056 
00057 /** Offset in PSP of command line */
00058 #define COMBOOT_PSP_CMDLINE_OFFSET 0x81
00059 
00060 /** Maximum length of command line in PSP
00061  * (127 bytes minus space and CR) */
00062 #define COMBOOT_MAX_CMDLINE_LEN    125
00063 
00064 
00065 /**
00066  * Copy command line to PSP
00067  *
00068  * @v image             COMBOOT image
00069  */
00070 static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) {
00071         const char *cmdline = ( image->cmdline ? image->cmdline : "" );
00072         int cmdline_len = strlen ( cmdline );
00073         if( cmdline_len > COMBOOT_MAX_CMDLINE_LEN )
00074                 cmdline_len = COMBOOT_MAX_CMDLINE_LEN;
00075         uint8_t len_byte = cmdline_len;
00076         char spc = ' ', cr = '\r';
00077 
00078         /* Copy length to byte before command line */
00079         copy_to_user ( seg_userptr, COMBOOT_PSP_CMDLINE_OFFSET - 1,
00080                        &len_byte, 1 );
00081 
00082         /* Command line starts with space */
00083         copy_to_user ( seg_userptr,
00084                        COMBOOT_PSP_CMDLINE_OFFSET,
00085                        &spc, 1 );
00086 
00087         /* Copy command line */
00088         copy_to_user ( seg_userptr,
00089                        COMBOOT_PSP_CMDLINE_OFFSET + 1,
00090                        cmdline, cmdline_len );
00091 
00092         /* Command line ends with CR */
00093         copy_to_user ( seg_userptr,
00094                        COMBOOT_PSP_CMDLINE_OFFSET + cmdline_len + 1,
00095                        &cr, 1 );
00096 }
00097 
00098 /**
00099  * Initialize PSP
00100  *
00101  * @v image             COMBOOT image
00102  * @v seg_userptr       segment to initialize
00103  */
00104 static void comboot_init_psp ( struct image * image, userptr_t seg_userptr ) {
00105         struct comboot_psp psp;
00106 
00107         /* Fill PSP */
00108 
00109         /* INT 20h instruction, byte order reversed */
00110         psp.int20 = 0x20CD;
00111 
00112         /* get_fbms() returns BIOS free base memory counter, which is in
00113          * kilobytes; x * 1024 / 16 == x * 64 == x << 6 */
00114         psp.first_non_free_para = get_fbms() << 6;
00115 
00116         DBGC ( image, "COMBOOT %p: first non-free paragraph = 0x%x\n",
00117                image, psp.first_non_free_para );
00118 
00119         /* Copy the PSP to offset 0 of segment.
00120          * The rest of the PSP was already zeroed by
00121          * comboot_prepare_segment. */
00122         copy_to_user ( seg_userptr, 0, &psp, sizeof( psp ) );
00123 
00124         /* Copy the command line to the PSP */
00125         comboot_copy_cmdline ( image, seg_userptr );
00126 }
00127 
00128 /**
00129  * Execute COMBOOT image
00130  *
00131  * @v image             COMBOOT image
00132  * @ret rc              Return status code
00133  */
00134 static int comboot_exec_loop ( struct image *image ) {
00135         userptr_t seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
00136         int state;
00137 
00138         state = rmsetjmp ( comboot_return );
00139 
00140         switch ( state ) {
00141         case 0: /* First time through; invoke COMBOOT program */
00142 
00143                 /* Initialize PSP */
00144                 comboot_init_psp ( image, seg_userptr );
00145 
00146                 /* Hook COMBOOT API interrupts */
00147                 hook_comboot_interrupts();
00148 
00149                 DBGC ( image, "executing 16-bit COMBOOT image at %4x:0100\n",
00150                        COMBOOT_PSP_SEG );
00151 
00152                 /* Unregister image, so that a "boot" command doesn't
00153                  * throw us into an execution loop.  We never
00154                  * reregister ourselves; COMBOOT images expect to be
00155                  * removed on exit.
00156                  */
00157                 unregister_image ( image );
00158 
00159                 /* Store stack segment at 0x38 and stack pointer at 0x3A
00160                  * in the PSP and jump to the image */
00161                 __asm__ __volatile__ (
00162                     REAL_CODE ( /* Save return address with segment on old stack */
00163                                     "popw %%ax\n\t"
00164                                     "pushw %%cs\n\t"
00165                                     "pushw %%ax\n\t"
00166                                     /* Set DS=ES=segment with image */
00167                                     "movw %w0, %%ds\n\t"
00168                                     "movw %w0, %%es\n\t"
00169                                     /* Set SS:SP to new stack (end of image segment) */
00170                                     "movw %w0, %%ss\n\t"
00171                                     "xor %%sp, %%sp\n\t"
00172                                     "pushw $0\n\t"
00173                                     "pushw %w0\n\t"
00174                                     "pushw $0x100\n\t"
00175                                     /* Zero registers (some COM files assume GP regs are 0) */
00176                                     "xorw %%ax, %%ax\n\t"
00177                                     "xorw %%bx, %%bx\n\t"
00178                                     "xorw %%cx, %%cx\n\t"
00179                                     "xorw %%dx, %%dx\n\t"
00180                                     "xorw %%si, %%si\n\t"
00181                                     "xorw %%di, %%di\n\t"
00182                                     "xorw %%bp, %%bp\n\t"
00183                                     "lret\n\t" )
00184                                          : : "r" ( COMBOOT_PSP_SEG ) : "eax" );
00185                 DBGC ( image, "COMBOOT %p: returned\n", image );
00186                 break;
00187 
00188         case COMBOOT_EXIT:
00189                 DBGC ( image, "COMBOOT %p: exited\n", image );
00190                 break;
00191 
00192         case COMBOOT_EXIT_RUN_KERNEL:
00193                 assert ( image->replacement );
00194                 DBGC ( image, "COMBOOT %p: exited to run kernel %s\n",
00195                        image, image->replacement->name );
00196                 break;
00197 
00198         case COMBOOT_EXIT_COMMAND:
00199                 DBGC ( image, "COMBOOT %p: exited after executing command\n",
00200                        image );
00201                 break;
00202 
00203         default:
00204                 assert ( 0 );
00205                 break;
00206         }
00207 
00208         unhook_comboot_interrupts();
00209         comboot_force_text_mode();
00210 
00211         return 0;
00212 }
00213 
00214 /**
00215  * Check image name extension
00216  *
00217  * @v image             COMBOOT image
00218  * @ret rc              Return status code
00219  */
00220 static int comboot_identify ( struct image *image ) {
00221         const char *ext;
00222 
00223         ext = strrchr( image->name, '.' );
00224 
00225         if ( ! ext ) {
00226                 DBGC ( image, "COMBOOT %p: no extension\n",
00227                        image );
00228                 return -ENOEXEC;
00229         }
00230 
00231         ++ext;
00232 
00233         if ( strcasecmp( ext, "cbt" ) ) {
00234                 DBGC ( image, "COMBOOT %p: unrecognized extension %s\n",
00235                        image, ext );
00236                 return -ENOEXEC;
00237         }
00238 
00239         return 0;
00240 }
00241 
00242 /**
00243  * Load COMBOOT image into memory, preparing a segment and returning it
00244  * @v image             COMBOOT image
00245  * @ret rc              Return status code
00246  */
00247 static int comboot_prepare_segment ( struct image *image )
00248 {
00249         userptr_t seg_userptr;
00250         size_t filesz, memsz;
00251         int rc;
00252 
00253         /* Load image in segment */
00254         seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
00255 
00256         /* Allow etra 0x100 bytes before image for PSP */
00257         filesz = image->len + 0x100;
00258 
00259         /* Ensure the entire 64k segment is free */
00260         memsz = 0xFFFF;
00261 
00262         /* Prepare, verify, and load the real-mode segment */
00263         if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) {
00264                 DBGC ( image, "COMBOOT %p: could not prepare segment: %s\n",
00265                        image, strerror ( rc ) );
00266                 return rc;
00267         }
00268 
00269         /* Zero PSP */
00270         memset_user ( seg_userptr, 0, 0, 0x100 );
00271 
00272         /* Copy image to segment:0100 */
00273         memcpy_user ( seg_userptr, 0x100, image->data, 0, image->len );
00274 
00275         return 0;
00276 }
00277 
00278 /**
00279  * Probe COMBOOT image
00280  *
00281  * @v image             COMBOOT image
00282  * @ret rc              Return status code
00283  */
00284 static int comboot_probe ( struct image *image ) {
00285         int rc;
00286 
00287         DBGC ( image, "COMBOOT %p: name '%s'\n",
00288                image, image->name );
00289 
00290         /* Check if this is a COMBOOT image */
00291         if ( ( rc = comboot_identify ( image ) ) != 0 ) {
00292 
00293                 return rc;
00294         }
00295 
00296         return 0;
00297 }
00298 
00299 /**
00300  * Execute COMBOOT image
00301  *
00302  * @v image             COMBOOT image
00303  * @ret rc              Return status code
00304  */
00305 static int comboot_exec ( struct image *image ) {
00306         int rc;
00307 
00308         /* Sanity check for filesize */
00309         if( image->len >= 0xFF00 ) {
00310                 DBGC( image, "COMBOOT %p: image too large\n",
00311                       image );
00312                 return -ENOEXEC;
00313         }
00314 
00315         /* Prepare segment and load image */
00316         if ( ( rc = comboot_prepare_segment ( image ) ) != 0 ) {
00317                 return rc;
00318         }
00319 
00320         /* Reset console */
00321         console_reset();
00322 
00323         return comboot_exec_loop ( image );
00324 }
00325 
00326 /** SYSLINUX COMBOOT (16-bit) image type */
00327 struct image_type comboot_image_type __image_type ( PROBE_NORMAL ) = {
00328         .name = "COMBOOT",
00329         .probe = comboot_probe,
00330         .exec = comboot_exec,
00331 };