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