• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
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  */
comboot_copy_cmdline(struct image * image,userptr_t seg_userptr)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  */
comboot_init_psp(struct image * image,userptr_t seg_userptr)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  */
comboot_exec(struct image * image)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  */
comboot_identify(struct image * image)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  */
comboot_prepare_segment(struct image * image)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  */
comboot_load(struct image * image)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