Home | History | Annotate | Download | only in image
      1 /*
      2  * Copyright (C) 2008 Daniel Verkamp <daniel (at) 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
     21  *
     22  * SYSLINUX COMBOOT (16-bit) image format
     23  *
     24  */
     25 
     26 FILE_LICENCE ( GPL2_OR_LATER );
     27 
     28 #include <stdint.h>
     29 #include <stdlib.h>
     30 #include <string.h>
     31 #include <strings.h>
     32 #include <errno.h>
     33 #include <assert.h>
     34 #include <realmode.h>
     35 #include <basemem.h>
     36 #include <comboot.h>
     37 #include <gpxe/uaccess.h>
     38 #include <gpxe/image.h>
     39 #include <gpxe/segment.h>
     40 #include <gpxe/init.h>
     41 #include <gpxe/features.h>
     42 
     43 FEATURE ( FEATURE_IMAGE, "COMBOOT", DHCP_EB_FEATURE_COMBOOT, 1 );
     44 
     45 struct image_type comboot_image_type __image_type ( PROBE_NORMAL );
     46 
     47 /**
     48  * COMBOOT PSP, copied to offset 0 of code segment
     49  */
     50 struct comboot_psp {
     51 	/** INT 20 instruction, executed if COMBOOT image returns with RET */
     52 	uint16_t int20;
     53 	/** Segment of first non-free paragraph of memory */
     54 	uint16_t first_non_free_para;
     55 };
     56 
     57 /** Offset in PSP of command line */
     58 #define COMBOOT_PSP_CMDLINE_OFFSET 0x81
     59 
     60 /** Maximum length of command line in PSP
     61  * (127 bytes minus space and CR) */
     62 #define COMBOOT_MAX_CMDLINE_LEN    125
     63 
     64 
     65 /**
     66  * Copy command line to PSP
     67  *
     68  * @v image		COMBOOT image
     69  */
     70 static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) {
     71 	const char *cmdline = ( image->cmdline ? image->cmdline : "" );
     72 	int cmdline_len = strlen ( cmdline );
     73 	if( cmdline_len > COMBOOT_MAX_CMDLINE_LEN )
     74 		cmdline_len = COMBOOT_MAX_CMDLINE_LEN;
     75 	uint8_t len_byte = cmdline_len;
     76 	char spc = ' ', cr = '\r';
     77 
     78 	/* Copy length to byte before command line */
     79 	copy_to_user ( seg_userptr, COMBOOT_PSP_CMDLINE_OFFSET - 1,
     80 	               &len_byte, 1 );
     81 
     82 	/* Command line starts with space */
     83 	copy_to_user ( seg_userptr,
     84 	               COMBOOT_PSP_CMDLINE_OFFSET,
     85 	               &spc, 1 );
     86 
     87 	/* Copy command line */
     88 	copy_to_user ( seg_userptr,
     89 	               COMBOOT_PSP_CMDLINE_OFFSET + 1,
     90 	               cmdline, cmdline_len );
     91 
     92 	/* Command line ends with CR */
     93 	copy_to_user ( seg_userptr,
     94 	               COMBOOT_PSP_CMDLINE_OFFSET + cmdline_len + 1,
     95 	               &cr, 1 );
     96 }
     97 
     98 /**
     99  * Initialize PSP
    100  *
    101  * @v image		COMBOOT image
    102  * @v seg_userptr	segment to initialize
    103  */
    104 static void comboot_init_psp ( struct image * image, userptr_t seg_userptr ) {
    105 	struct comboot_psp psp;
    106 
    107 	/* Fill PSP */
    108 
    109 	/* INT 20h instruction, byte order reversed */
    110 	psp.int20 = 0x20CD;
    111 
    112 	/* get_fbms() returns BIOS free base memory counter, which is in
    113 	 * kilobytes; x * 1024 / 16 == x * 64 == x << 6 */
    114 	psp.first_non_free_para = get_fbms() << 6;
    115 
    116 	DBGC ( image, "COMBOOT %p: first non-free paragraph = 0x%x\n",
    117 	       image, psp.first_non_free_para );
    118 
    119 	/* Copy the PSP to offset 0 of segment.
    120 	 * The rest of the PSP was already zeroed by
    121 	 * comboot_prepare_segment. */
    122 	copy_to_user ( seg_userptr, 0, &psp, sizeof( psp ) );
    123 
    124 	/* Copy the command line to the PSP */
    125 	comboot_copy_cmdline ( image, seg_userptr );
    126 }
    127 
    128 /**
    129  * Execute COMBOOT image
    130  *
    131  * @v image		COMBOOT image
    132  * @ret rc		Return status code
    133  */
    134 static int comboot_exec ( struct image *image ) {
    135 	userptr_t seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
    136 	int state;
    137 
    138 	state = rmsetjmp ( comboot_return );
    139 
    140 	switch ( state ) {
    141 	case 0: /* First time through; invoke COMBOOT program */
    142 
    143 		/* Initialize PSP */
    144 		comboot_init_psp ( image, seg_userptr );
    145 
    146 		/* Hook COMBOOT API interrupts */
    147 		hook_comboot_interrupts();
    148 
    149 		DBGC ( image, "executing 16-bit COMBOOT image at %4x:0100\n",
    150 		       COMBOOT_PSP_SEG );
    151 
    152 		/* Unregister image, so that a "boot" command doesn't
    153 		 * throw us into an execution loop.  We never
    154 		 * reregister ourselves; COMBOOT images expect to be
    155 		 * removed on exit.
    156 		 */
    157 		unregister_image ( image );
    158 
    159 		/* Store stack segment at 0x38 and stack pointer at 0x3A
    160 		 * in the PSP and jump to the image */
    161 		__asm__ __volatile__ (
    162 		    REAL_CODE ( /* Save return address with segment on old stack */
    163 				    "popw %%ax\n\t"
    164 				    "pushw %%cs\n\t"
    165 				    "pushw %%ax\n\t"
    166 				    /* Set DS=ES=segment with image */
    167 				    "movw %w0, %%ds\n\t"
    168 				    "movw %w0, %%es\n\t"
    169 				    /* Set SS:SP to new stack (end of image segment) */
    170 				    "movw %w0, %%ss\n\t"
    171 				    "xor %%sp, %%sp\n\t"
    172 				    "pushw $0\n\t"
    173 				    "pushw %w0\n\t"
    174 				    "pushw $0x100\n\t"
    175 				    /* Zero registers (some COM files assume GP regs are 0) */
    176 				    "xorw %%ax, %%ax\n\t"
    177 				    "xorw %%bx, %%bx\n\t"
    178 				    "xorw %%cx, %%cx\n\t"
    179 				    "xorw %%dx, %%dx\n\t"
    180 				    "xorw %%si, %%si\n\t"
    181 				    "xorw %%di, %%di\n\t"
    182 				    "xorw %%bp, %%bp\n\t"
    183 				    "lret\n\t" )
    184 					 : : "r" ( COMBOOT_PSP_SEG ) : "eax" );
    185 		DBGC ( image, "COMBOOT %p: returned\n", image );
    186 		break;
    187 
    188 	case COMBOOT_EXIT:
    189 		DBGC ( image, "COMBOOT %p: exited\n", image );
    190 		break;
    191 
    192 	case COMBOOT_EXIT_RUN_KERNEL:
    193 		DBGC ( image, "COMBOOT %p: exited to run kernel %p\n",
    194 		       image, comboot_replacement_image );
    195 		image->replacement = comboot_replacement_image;
    196 		comboot_replacement_image = NULL;
    197 		image_autoload ( image->replacement );
    198 		break;
    199 
    200 	case COMBOOT_EXIT_COMMAND:
    201 		DBGC ( image, "COMBOOT %p: exited after executing command\n",
    202 		       image );
    203 		break;
    204 
    205 	default:
    206 		assert ( 0 );
    207 		break;
    208 	}
    209 
    210 	unhook_comboot_interrupts();
    211 	comboot_force_text_mode();
    212 
    213 	return 0;
    214 }
    215 
    216 /**
    217  * Check image name extension
    218  *
    219  * @v image		COMBOOT image
    220  * @ret rc		Return status code
    221  */
    222 static int comboot_identify ( struct image *image ) {
    223 	const char *ext;
    224 
    225 	ext = strrchr( image->name, '.' );
    226 
    227 	if ( ! ext ) {
    228 		DBGC ( image, "COMBOOT %p: no extension\n",
    229 		       image );
    230 		return -ENOEXEC;
    231 	}
    232 
    233 	++ext;
    234 
    235 	if ( strcasecmp( ext, "com" ) && strcasecmp( ext, "cbt" ) ) {
    236 		DBGC ( image, "COMBOOT %p: unrecognized extension %s\n",
    237 		       image, ext );
    238 		return -ENOEXEC;
    239 	}
    240 
    241 	return 0;
    242 }
    243 
    244 /**
    245  * Load COMBOOT image into memory, preparing a segment and returning it
    246  * @v image		COMBOOT image
    247  * @ret rc		Return status code
    248  */
    249 static int comboot_prepare_segment ( struct image *image )
    250 {
    251 	userptr_t seg_userptr;
    252 	size_t filesz, memsz;
    253 	int rc;
    254 
    255 	/* Load image in segment */
    256 	seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
    257 
    258 	/* Allow etra 0x100 bytes before image for PSP */
    259 	filesz = image->len + 0x100;
    260 
    261 	/* Ensure the entire 64k segment is free */
    262 	memsz = 0xFFFF;
    263 
    264 	/* Prepare, verify, and load the real-mode segment */
    265 	if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) {
    266 		DBGC ( image, "COMBOOT %p: could not prepare segment: %s\n",
    267 		       image, strerror ( rc ) );
    268 		return rc;
    269 	}
    270 
    271 	/* Zero PSP */
    272 	memset_user ( seg_userptr, 0, 0, 0x100 );
    273 
    274 	/* Copy image to segment:0100 */
    275 	memcpy_user ( seg_userptr, 0x100, image->data, 0, image->len );
    276 
    277 	return 0;
    278 }
    279 
    280 /**
    281  * Load COMBOOT image into memory
    282  *
    283  * @v image		COMBOOT image
    284  * @ret rc		Return status code
    285  */
    286 static int comboot_load ( struct image *image ) {
    287 	int rc;
    288 
    289 	DBGC ( image, "COMBOOT %p: name '%s'\n",
    290 	       image, image->name );
    291 
    292 	/* Check if this is a COMBOOT image */
    293 	if ( ( rc = comboot_identify ( image ) ) != 0 ) {
    294 
    295 		return rc;
    296 	}
    297 
    298 	/* This is a 16-bit COMBOOT image, valid or otherwise */
    299 	if ( ! image->type )
    300 		image->type = &comboot_image_type;
    301 
    302 	/* Sanity check for filesize */
    303 	if( image->len >= 0xFF00 ) {
    304 		DBGC( image, "COMBOOT %p: image too large\n",
    305 		      image );
    306 		return -ENOEXEC;
    307 	}
    308 
    309 	/* Prepare segment and load image */
    310 	if ( ( rc = comboot_prepare_segment ( image ) ) != 0 ) {
    311 		return rc;
    312 	}
    313 
    314 	return 0;
    315 }
    316 
    317 /** SYSLINUX COMBOOT (16-bit) image type */
    318 struct image_type comboot_image_type __image_type ( PROBE_NORMAL ) = {
    319 	.name = "COMBOOT",
    320 	.load = comboot_load,
    321 	.exec = comboot_exec,
    322 };
    323