• 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 COM32 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/memmap.h>
42 
43 struct image_type com32_image_type __image_type ( PROBE_NORMAL );
44 
45 /**
46  * Execute COMBOOT image
47  *
48  * @v image		COM32 image
49  * @ret rc		Return status code
50  */
com32_exec(struct image * image)51 static int com32_exec ( struct image *image ) {
52 	struct memory_map memmap;
53 	unsigned int i;
54 	int state;
55 	uint32_t avail_mem_top;
56 
57 	state = rmsetjmp ( comboot_return );
58 
59 	switch ( state ) {
60 	case 0: /* First time through; invoke COM32 program */
61 
62 		/* Get memory map */
63 		get_memmap ( &memmap );
64 
65 		/* Find end of block covering COM32 image loading area */
66 		for ( i = 0, avail_mem_top = 0 ; i < memmap.count ; i++ ) {
67 			if ( (memmap.regions[i].start <= COM32_START_PHYS) &&
68 			     (memmap.regions[i].end > COM32_START_PHYS + image->len) ) {
69 				avail_mem_top = memmap.regions[i].end;
70 				break;
71 			}
72 		}
73 
74 		DBGC ( image, "COM32 %p: available memory top = 0x%x\n",
75 		       image, avail_mem_top );
76 
77 		assert ( avail_mem_top != 0 );
78 
79 		com32_external_esp = phys_to_virt ( avail_mem_top );
80 
81 		/* Hook COMBOOT API interrupts */
82 		hook_comboot_interrupts();
83 
84 		/* Unregister image, so that a "boot" command doesn't
85 		 * throw us into an execution loop.  We never
86 		 * reregister ourselves; COMBOOT images expect to be
87 		 * removed on exit.
88 		 */
89 		unregister_image ( image );
90 
91 		__asm__ __volatile__ (
92 			"movl %%esp, (com32_internal_esp)\n\t" /* Save internal virtual address space ESP */
93 			"movl (com32_external_esp), %%esp\n\t" /* Switch to COM32 ESP (top of available memory) */
94 			"call _virt_to_phys\n\t"               /* Switch to flat physical address space */
95 			"pushl %0\n\t"                         /* Pointer to CDECL helper function */
96 			"pushl %1\n\t"                         /* Pointer to FAR call helper function */
97 			"pushl %2\n\t"                         /* Size of low memory bounce buffer */
98 			"pushl %3\n\t"                         /* Pointer to low memory bounce buffer */
99 			"pushl %4\n\t"                         /* Pointer to INT call helper function */
100 			"pushl %5\n\t"                         /* Pointer to the command line arguments */
101 			"pushl $6\n\t"                         /* Number of additional arguments */
102 			"call *%6\n\t"                         /* Execute image */
103 			"call _phys_to_virt\n\t"               /* Switch back to internal virtual address space */
104 			"movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */
105 		:
106 		:
107 			/* %0 */ "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ),
108 			/* %1 */ "r" ( virt_to_phys ( com32_farcall_wrapper ) ),
109 			/* %2 */ "r" ( get_fbms() * 1024 - (COM32_BOUNCE_SEG << 4) ),
110 			/* %3 */ "i" ( COM32_BOUNCE_SEG << 4 ),
111 			/* %4 */ "r" ( virt_to_phys ( com32_intcall_wrapper ) ),
112 			/* %5 */ "r" ( virt_to_phys ( image->cmdline ) ),
113 			/* %6 */ "r" ( COM32_START_PHYS )
114 		:
115 			"memory" );
116 		DBGC ( image, "COM32 %p: returned\n", image );
117 		break;
118 
119 	case COMBOOT_EXIT:
120 		DBGC ( image, "COM32 %p: exited\n", image );
121 		break;
122 
123 	case COMBOOT_EXIT_RUN_KERNEL:
124 		DBGC ( image, "COM32 %p: exited to run kernel %p\n",
125 		       image, comboot_replacement_image );
126 		image->replacement = comboot_replacement_image;
127 		comboot_replacement_image = NULL;
128 		image_autoload ( image->replacement );
129 		break;
130 
131 	case COMBOOT_EXIT_COMMAND:
132 		DBGC ( image, "COM32 %p: exited after executing command\n",
133 		       image );
134 		break;
135 
136 	default:
137 		assert ( 0 );
138 		break;
139 	}
140 
141 	unhook_comboot_interrupts();
142 	comboot_force_text_mode();
143 
144 	return 0;
145 }
146 
147 /**
148  * Check image name extension
149  *
150  * @v image		COM32 image
151  * @ret rc		Return status code
152  */
com32_identify(struct image * image)153 static int com32_identify ( struct image *image ) {
154 	const char *ext;
155 	static const uint8_t magic[] = { 0xB8, 0xFF, 0x4C, 0xCD, 0x21 };
156 	uint8_t buf[5];
157 
158 	if ( image->len >= 5 ) {
159 		/* Check for magic number
160 		 * mov eax,21cd4cffh
161 		 * B8 FF 4C CD 21
162 		 */
163 		copy_from_user ( buf, image->data, 0, sizeof(buf) );
164 		if ( ! memcmp ( buf, magic, sizeof(buf) ) ) {
165 			DBGC ( image, "COM32 %p: found magic number\n",
166 			       image );
167 			return 0;
168 		}
169 	}
170 
171 	/* Magic number not found; check filename extension */
172 
173 	ext = strrchr( image->name, '.' );
174 
175 	if ( ! ext ) {
176 		DBGC ( image, "COM32 %p: no extension\n",
177 		       image );
178 		return -ENOEXEC;
179 	}
180 
181 	++ext;
182 
183 	if ( strcasecmp( ext, "c32" ) ) {
184 		DBGC ( image, "COM32 %p: unrecognized extension %s\n",
185 		       image, ext );
186 		return -ENOEXEC;
187 	}
188 
189 	return 0;
190 }
191 
192 
193 /**
194  * Load COM32 image into memory
195  * @v image		COM32 image
196  * @ret rc		Return status code
197  */
comboot_load_image(struct image * image)198 static int comboot_load_image ( struct image *image ) {
199 	size_t filesz, memsz;
200 	userptr_t buffer;
201 	int rc;
202 
203 	filesz = image->len;
204 	memsz = filesz;
205 	buffer = phys_to_user ( COM32_START_PHYS );
206 	if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
207 		DBGC ( image, "COM32 %p: could not prepare segment: %s\n",
208 		       image, strerror ( rc ) );
209 		return rc;
210 	}
211 
212 	/* Copy image to segment */
213 	memcpy_user ( buffer, 0, image->data, 0, filesz );
214 
215 	return 0;
216 }
217 
218 /**
219  * Prepare COM32 low memory bounce buffer
220  * @v image		COM32 image
221  * @ret rc		Return status code
222  */
comboot_prepare_bounce_buffer(struct image * image)223 static int comboot_prepare_bounce_buffer ( struct image * image ) {
224 	unsigned int seg;
225 	userptr_t seg_userptr;
226 	size_t filesz, memsz;
227 	int rc;
228 
229 	seg = COM32_BOUNCE_SEG;
230 	seg_userptr = real_to_user ( seg, 0 );
231 
232 	/* Ensure the entire 64k segment is free */
233 	memsz = 0xFFFF;
234 	filesz = 0;
235 
236 	/* Prepare, verify, and load the real-mode segment */
237 	if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) {
238 		DBGC ( image, "COM32 %p: could not prepare bounce buffer segment: %s\n",
239 		       image, strerror ( rc ) );
240 		return rc;
241 	}
242 
243 	return 0;
244 }
245 
246 /**
247  * Load COM32 image into memory
248  *
249  * @v image		COM32 image
250  * @ret rc		Return status code
251  */
com32_load(struct image * image)252 static int com32_load ( struct image *image ) {
253 	int rc;
254 
255 	DBGC ( image, "COM32 %p: name '%s', cmdline '%s'\n",
256 	       image, image->name, image->cmdline );
257 
258 	/* Check if this is a COMBOOT image */
259 	if ( ( rc = com32_identify ( image ) ) != 0 ) {
260 		return rc;
261 	}
262 
263 	/* This is a COM32 image, valid or otherwise */
264 	if ( ! image->type )
265 		image->type = &com32_image_type;
266 
267 	/* Load image */
268 	if ( ( rc = comboot_load_image ( image ) ) != 0 ) {
269 		return rc;
270 	}
271 
272 	/* Prepare bounce buffer segment */
273 	if ( ( rc = comboot_prepare_bounce_buffer ( image ) ) != 0 ) {
274 		return rc;
275 	}
276 
277 	return 0;
278 }
279 
280 /** SYSLINUX COM32 image type */
281 struct image_type com32_image_type __image_type ( PROBE_NORMAL ) = {
282 	.name = "COM32",
283 	.load = com32_load,
284 	.exec = com32_exec,
285 };
286