Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / arch / i386 / image / com32.c
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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19
20 /**
21  * @file
22  *
23  * SYSLINUX COM32 image format
24  *
25  */
26
27 FILE_LICENCE ( GPL2_OR_LATER );
28
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <strings.h>
33 #include <errno.h>
34 #include <assert.h>
35 #include <realmode.h>
36 #include <basemem.h>
37 #include <comboot.h>
38 #include <ipxe/uaccess.h>
39 #include <ipxe/image.h>
40 #include <ipxe/segment.h>
41 #include <ipxe/init.h>
42 #include <ipxe/io.h>
43
44 /**
45  * Execute COMBOOT image
46  *
47  * @v image             COM32 image
48  * @ret rc              Return status code
49  */
50 static int com32_exec_loop ( struct image *image ) {
51         struct memory_map memmap;
52         unsigned int i;
53         int state;
54         uint32_t avail_mem_top;
55
56         state = rmsetjmp ( comboot_return );
57
58         switch ( state ) {
59         case 0: /* First time through; invoke COM32 program */
60
61                 /* Get memory map */
62                 get_memmap ( &memmap );
63
64                 /* Find end of block covering COM32 image loading area */
65                 for ( i = 0, avail_mem_top = 0 ; i < memmap.count ; i++ ) {
66                         if ( (memmap.regions[i].start <= COM32_START_PHYS) &&
67                              (memmap.regions[i].end > COM32_START_PHYS + image->len) ) {
68                                 avail_mem_top = memmap.regions[i].end;
69                                 break;
70                         }
71                 }
72
73                 DBGC ( image, "COM32 %p: available memory top = 0x%x\n",
74                        image, avail_mem_top );
75
76                 assert ( avail_mem_top != 0 );
77
78                 com32_external_esp = phys_to_virt ( avail_mem_top );
79
80                 /* Hook COMBOOT API interrupts */
81                 hook_comboot_interrupts();
82
83                 /* Unregister image, so that a "boot" command doesn't
84                  * throw us into an execution loop.  We never
85                  * reregister ourselves; COMBOOT images expect to be
86                  * removed on exit.
87                  */
88                 unregister_image ( image );
89
90                 __asm__ __volatile__ (
91                         "movl %%esp, (com32_internal_esp)\n\t" /* Save internal virtual address space ESP */
92                         "movl (com32_external_esp), %%esp\n\t" /* Switch to COM32 ESP (top of available memory) */
93                         "call _virt_to_phys\n\t"               /* Switch to flat physical address space */
94                         "sti\n\t"                              /* Enable interrupts */
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                         "cli\n\t"                              /* Disable interrupts */
104                         "call _phys_to_virt\n\t"               /* Switch back to internal virtual address space */
105                         "movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */
106                 :
107                 :
108                         /* %0 */ "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ),
109                         /* %1 */ "r" ( virt_to_phys ( com32_farcall_wrapper ) ),
110                         /* %2 */ "r" ( get_fbms() * 1024 - (COM32_BOUNCE_SEG << 4) ),
111                         /* %3 */ "i" ( COM32_BOUNCE_SEG << 4 ),
112                         /* %4 */ "r" ( virt_to_phys ( com32_intcall_wrapper ) ),
113                         /* %5 */ "r" ( virt_to_phys ( image->cmdline ?
114                                                       image->cmdline : "" ) ),
115                         /* %6 */ "r" ( COM32_START_PHYS )
116                 :
117                         "memory" );
118                 DBGC ( image, "COM32 %p: returned\n", image );
119                 break;
120
121         case COMBOOT_EXIT:
122                 DBGC ( image, "COM32 %p: exited\n", image );
123                 break;
124
125         case COMBOOT_EXIT_RUN_KERNEL:
126                 assert ( image->replacement );
127                 DBGC ( image, "COM32 %p: exited to run kernel %s\n",
128                        image, image->replacement->name );
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  */
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  */
198 static int com32_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  */
223 static int com32_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  * Probe COM32 image
248  *
249  * @v image             COM32 image
250  * @ret rc              Return status code
251  */
252 static int com32_probe ( struct image *image ) {
253         int rc;
254
255         DBGC ( image, "COM32 %p: name '%s'\n", image, image->name );
256
257         /* Check if this is a COMBOOT image */
258         if ( ( rc = com32_identify ( image ) ) != 0 ) {
259                 return rc;
260         }
261
262         return 0;
263 }
264
265 /**
266  * Execute COMBOOT image
267  *
268  * @v image             COM32 image
269  * @ret rc              Return status code
270  */
271 static int com32_exec ( struct image *image ) {
272         int rc;
273
274         /* Load image */
275         if ( ( rc = com32_load_image ( image ) ) != 0 ) {
276                 return rc;
277         }
278
279         /* Prepare bounce buffer segment */
280         if ( ( rc = com32_prepare_bounce_buffer ( image ) ) != 0 ) {
281                 return rc;
282         }
283
284         return com32_exec_loop ( image );
285 }
286
287 /** SYSLINUX COM32 image type */
288 struct image_type com32_image_type __image_type ( PROBE_NORMAL ) = {
289         .name = "COM32",
290         .probe = com32_probe,
291         .exec = com32_exec,
292 };