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., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 /**
00020  * @file
00021  *
00022  * SYSLINUX COMBOOT (16-bit) image format
00023  *
00024  */
00025 
00026 FILE_LICENCE ( GPL2_OR_LATER );
00027 
00028 #include <stdint.h>
00029 #include <stdlib.h>
00030 #include <string.h>
00031 #include <strings.h>
00032 #include <errno.h>
00033 #include <assert.h>
00034 #include <realmode.h>
00035 #include <basemem.h>
00036 #include <comboot.h>
00037 #include <gpxe/uaccess.h>
00038 #include <gpxe/image.h>
00039 #include <gpxe/segment.h>
00040 #include <gpxe/init.h>
00041 #include <gpxe/features.h>
00042 
00043 FEATURE ( FEATURE_IMAGE, "COMBOOT", DHCP_EB_FEATURE_COMBOOT, 1 );
00044 
00045 struct image_type comboot_image_type __image_type ( PROBE_NORMAL );
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 ( 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                 DBGC ( image, "COMBOOT %p: exited to run kernel %p\n",
00194                        image, comboot_replacement_image );
00195                 image->replacement = comboot_replacement_image;
00196                 comboot_replacement_image = NULL;
00197                 image_autoload ( image->replacement );
00198                 break;
00199 
00200         case COMBOOT_EXIT_COMMAND:
00201                 DBGC ( image, "COMBOOT %p: exited after executing command\n",
00202                        image );
00203                 break;
00204 
00205         default:
00206                 assert ( 0 );
00207                 break;
00208         }
00209 
00210         unhook_comboot_interrupts();
00211         comboot_force_text_mode();
00212 
00213         return 0;
00214 }
00215 
00216 /**
00217  * Check image name extension
00218  * 
00219  * @v image             COMBOOT image
00220  * @ret rc              Return status code
00221  */
00222 static int comboot_identify ( struct image *image ) {
00223         const char *ext;
00224 
00225         ext = strrchr( image->name, '.' );
00226 
00227         if ( ! ext ) {
00228                 DBGC ( image, "COMBOOT %p: no extension\n",
00229                        image );
00230                 return -ENOEXEC;
00231         }
00232 
00233         ++ext;
00234 
00235         if ( strcasecmp( ext, "com" ) && strcasecmp( ext, "cbt" ) ) {
00236                 DBGC ( image, "COMBOOT %p: unrecognized extension %s\n",
00237                        image, ext );
00238                 return -ENOEXEC;
00239         }
00240 
00241         return 0;
00242 }
00243 
00244 /**
00245  * Load COMBOOT image into memory, preparing a segment and returning it
00246  * @v image             COMBOOT image
00247  * @ret rc              Return status code
00248  */
00249 static int comboot_prepare_segment ( struct image *image )
00250 {
00251         userptr_t seg_userptr;
00252         size_t filesz, memsz;
00253         int rc;
00254 
00255         /* Load image in segment */
00256         seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
00257 
00258         /* Allow etra 0x100 bytes before image for PSP */
00259         filesz = image->len + 0x100; 
00260 
00261         /* Ensure the entire 64k segment is free */
00262         memsz = 0xFFFF;
00263 
00264         /* Prepare, verify, and load the real-mode segment */
00265         if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) {
00266                 DBGC ( image, "COMBOOT %p: could not prepare segment: %s\n",
00267                        image, strerror ( rc ) );
00268                 return rc;
00269         }
00270 
00271         /* Zero PSP */
00272         memset_user ( seg_userptr, 0, 0, 0x100 );
00273 
00274         /* Copy image to segment:0100 */
00275         memcpy_user ( seg_userptr, 0x100, image->data, 0, image->len );
00276 
00277         return 0;
00278 }
00279 
00280 /**
00281  * Load COMBOOT image into memory
00282  *
00283  * @v image             COMBOOT image
00284  * @ret rc              Return status code
00285  */
00286 static int comboot_load ( struct image *image ) {
00287         int rc;
00288 
00289         DBGC ( image, "COMBOOT %p: name '%s'\n",
00290                image, image->name );
00291 
00292         /* Check if this is a COMBOOT image */
00293         if ( ( rc = comboot_identify ( image ) ) != 0 ) {
00294                 
00295                 return rc;
00296         }
00297 
00298         /* This is a 16-bit COMBOOT image, valid or otherwise */
00299         if ( ! image->type )
00300                 image->type = &comboot_image_type;
00301         
00302         /* Sanity check for filesize */
00303         if( image->len >= 0xFF00 ) {
00304                 DBGC( image, "COMBOOT %p: image too large\n",
00305                       image );
00306                 return -ENOEXEC;
00307         }
00308 
00309         /* Prepare segment and load image */
00310         if ( ( rc = comboot_prepare_segment ( image ) ) != 0 ) {
00311                 return rc;
00312         }
00313 
00314         return 0;
00315 }
00316 
00317 /** SYSLINUX COMBOOT (16-bit) image type */
00318 struct image_type comboot_image_type __image_type ( PROBE_NORMAL ) = {
00319         .name = "COMBOOT",
00320         .load = comboot_load,
00321         .exec = comboot_exec,
00322 };

Generated on Tue Apr 6 20:00:50 2010 for gPXE by  doxygen 1.5.7.1