[contrib] Allow Network Protocol header to display in rom-o-matic
[gpxe.git] / src / arch / i386 / image / com32.c
blob5e65c0ab0c18298e100ff8d4b92a1b2a34d90fd5
1 /*
2 * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>.
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.
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.
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.
19 /**
20 * @file
22 * SYSLINUX COM32 image format
26 FILE_LICENCE ( GPL2_OR_LATER );
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>
43 struct image_type com32_image_type __image_type ( PROBE_NORMAL );
45 struct idt_register com32_external_idtr = {
46 .limit = COM32_NUM_IDT_ENTRIES * sizeof ( struct idt_descriptor ) - 1,
47 .base = COM32_IDT
50 struct idt_register com32_internal_idtr;
52 /**
53 * Execute COMBOOT image
55 * @v image COM32 image
56 * @ret rc Return status code
58 static int com32_exec ( struct image *image ) {
59 struct memory_map memmap;
60 unsigned int i;
61 int state;
62 uint32_t avail_mem_top;
64 state = rmsetjmp ( comboot_return );
66 switch ( state ) {
67 case 0: /* First time through; invoke COM32 program */
69 /* Get memory map */
70 get_memmap ( &memmap );
72 /* Find end of block covering COM32 image loading area */
73 for ( i = 0, avail_mem_top = 0 ; i < memmap.count ; i++ ) {
74 if ( (memmap.regions[i].start <= COM32_START_PHYS) &&
75 (memmap.regions[i].end > COM32_START_PHYS + image->len) ) {
76 avail_mem_top = memmap.regions[i].end;
77 break;
81 DBGC ( image, "COM32 %p: available memory top = 0x%x\n",
82 image, avail_mem_top );
84 assert ( avail_mem_top != 0 );
86 com32_external_esp = phys_to_virt ( avail_mem_top );
88 /* Hook COMBOOT API interrupts */
89 hook_comboot_interrupts();
91 /* Unregister image, so that a "boot" command doesn't
92 * throw us into an execution loop. We never
93 * reregister ourselves; COMBOOT images expect to be
94 * removed on exit.
96 unregister_image ( image );
98 __asm__ __volatile__ (
99 "sidt com32_internal_idtr\n\t"
100 "lidt com32_external_idtr\n\t" /* Set up IDT */
101 "movl %%esp, (com32_internal_esp)\n\t" /* Save internal virtual address space ESP */
102 "movl (com32_external_esp), %%esp\n\t" /* Switch to COM32 ESP (top of available memory) */
103 "call _virt_to_phys\n\t" /* Switch to flat physical address space */
104 "sti\n\t" /* Enable interrupts */
105 "pushl %0\n\t" /* Pointer to CDECL helper function */
106 "pushl %1\n\t" /* Pointer to FAR call helper function */
107 "pushl %2\n\t" /* Size of low memory bounce buffer */
108 "pushl %3\n\t" /* Pointer to low memory bounce buffer */
109 "pushl %4\n\t" /* Pointer to INT call helper function */
110 "pushl %5\n\t" /* Pointer to the command line arguments */
111 "pushl $6\n\t" /* Number of additional arguments */
112 "call *%6\n\t" /* Execute image */
113 "cli\n\t" /* Disable interrupts */
114 "call _phys_to_virt\n\t" /* Switch back to internal virtual address space */
115 "lidt com32_internal_idtr\n\t" /* Switch back to internal IDT (for debugging) */
116 "movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */
119 /* %0 */ "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ),
120 /* %1 */ "r" ( virt_to_phys ( com32_farcall_wrapper ) ),
121 /* %2 */ "r" ( get_fbms() * 1024 - (COM32_BOUNCE_SEG << 4) ),
122 /* %3 */ "i" ( COM32_BOUNCE_SEG << 4 ),
123 /* %4 */ "r" ( virt_to_phys ( com32_intcall_wrapper ) ),
124 /* %5 */ "r" ( virt_to_phys ( image->cmdline ) ),
125 /* %6 */ "r" ( COM32_START_PHYS )
127 "memory" );
128 DBGC ( image, "COM32 %p: returned\n", image );
129 break;
131 case COMBOOT_EXIT:
132 DBGC ( image, "COM32 %p: exited\n", image );
133 break;
135 case COMBOOT_EXIT_RUN_KERNEL:
136 DBGC ( image, "COM32 %p: exited to run kernel %p\n",
137 image, comboot_replacement_image );
138 image->replacement = comboot_replacement_image;
139 comboot_replacement_image = NULL;
140 image_autoload ( image->replacement );
141 break;
143 case COMBOOT_EXIT_COMMAND:
144 DBGC ( image, "COM32 %p: exited after executing command\n",
145 image );
146 break;
148 default:
149 assert ( 0 );
150 break;
153 unhook_comboot_interrupts();
154 comboot_force_text_mode();
156 return 0;
160 * Check image name extension
162 * @v image COM32 image
163 * @ret rc Return status code
165 static int com32_identify ( struct image *image ) {
166 const char *ext;
167 static const uint8_t magic[] = { 0xB8, 0xFF, 0x4C, 0xCD, 0x21 };
168 uint8_t buf[5];
170 if ( image->len >= 5 ) {
171 /* Check for magic number
172 * mov eax,21cd4cffh
173 * B8 FF 4C CD 21
175 copy_from_user ( buf, image->data, 0, sizeof(buf) );
176 if ( ! memcmp ( buf, magic, sizeof(buf) ) ) {
177 DBGC ( image, "COM32 %p: found magic number\n",
178 image );
179 return 0;
183 /* Magic number not found; check filename extension */
185 ext = strrchr( image->name, '.' );
187 if ( ! ext ) {
188 DBGC ( image, "COM32 %p: no extension\n",
189 image );
190 return -ENOEXEC;
193 ++ext;
195 if ( strcasecmp( ext, "c32" ) ) {
196 DBGC ( image, "COM32 %p: unrecognized extension %s\n",
197 image, ext );
198 return -ENOEXEC;
201 return 0;
206 * Load COM32 image into memory and set up the IDT
207 * @v image COM32 image
208 * @ret rc Return status code
210 static int comboot_load_image ( struct image *image ) {
211 physaddr_t com32_irq_wrapper_phys;
212 struct idt_descriptor *idt;
213 struct ijb_entry *ijb;
214 size_t filesz, memsz;
215 userptr_t buffer;
216 int rc, i;
218 /* The interrupt descriptor table, interrupt jump buffer, and
219 * image data are all contiguous in memory. Prepare them all at once.
221 filesz = image->len +
222 COM32_NUM_IDT_ENTRIES * sizeof ( struct idt_descriptor ) +
223 COM32_NUM_IDT_ENTRIES * sizeof ( struct ijb_entry );
224 memsz = filesz;
225 buffer = phys_to_user ( COM32_IDT );
226 if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
227 DBGC ( image, "COM32 %p: could not prepare segment: %s\n",
228 image, strerror ( rc ) );
229 return rc;
232 /* Write the IDT and IJB */
233 idt = phys_to_virt ( COM32_IDT );
234 ijb = phys_to_virt ( COM32_IJB );
235 com32_irq_wrapper_phys = virt_to_phys ( com32_irq_wrapper );
237 for ( i = 0; i < COM32_NUM_IDT_ENTRIES; i++ ) {
238 uint32_t ijb_address = virt_to_phys ( &ijb[i] );
240 idt[i].offset_low = ijb_address & 0xFFFF;
241 idt[i].selector = PHYSICAL_CS;
242 idt[i].flags = IDT_INTERRUPT_GATE_FLAGS;
243 idt[i].offset_high = ijb_address >> 16;
245 ijb[i].pusha_instruction = IJB_PUSHA;
246 ijb[i].mov_instruction = IJB_MOV_AL_IMM8;
247 ijb[i].mov_value = i;
248 ijb[i].jump_instruction = IJB_JMP_REL32;
249 ijb[i].jump_destination = com32_irq_wrapper_phys -
250 virt_to_phys ( &ijb[i + 1] );
253 /* Copy image to segment */
254 buffer = phys_to_user ( COM32_START_PHYS );
255 memcpy_user ( buffer, 0, image->data, 0, filesz );
257 return 0;
261 * Prepare COM32 low memory bounce buffer
262 * @v image COM32 image
263 * @ret rc Return status code
265 static int comboot_prepare_bounce_buffer ( struct image * image ) {
266 unsigned int seg;
267 userptr_t seg_userptr;
268 size_t filesz, memsz;
269 int rc;
271 seg = COM32_BOUNCE_SEG;
272 seg_userptr = real_to_user ( seg, 0 );
274 /* Ensure the entire 64k segment is free */
275 memsz = 0xFFFF;
276 filesz = 0;
278 /* Prepare, verify, and load the real-mode segment */
279 if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) {
280 DBGC ( image, "COM32 %p: could not prepare bounce buffer segment: %s\n",
281 image, strerror ( rc ) );
282 return rc;
285 return 0;
289 * Load COM32 image into memory
291 * @v image COM32 image
292 * @ret rc Return status code
294 static int com32_load ( struct image *image ) {
295 int rc;
297 DBGC ( image, "COM32 %p: name '%s', cmdline '%s'\n",
298 image, image->name, image->cmdline );
300 /* Check if this is a COMBOOT image */
301 if ( ( rc = com32_identify ( image ) ) != 0 ) {
302 return rc;
305 /* This is a COM32 image, valid or otherwise */
306 if ( ! image->type )
307 image->type = &com32_image_type;
309 /* Load image */
310 if ( ( rc = comboot_load_image ( image ) ) != 0 ) {
311 return rc;
314 /* Prepare bounce buffer segment */
315 if ( ( rc = comboot_prepare_bounce_buffer ( image ) ) != 0 ) {
316 return rc;
319 return 0;
322 /** SYSLINUX COM32 image type */
323 struct image_type com32_image_type __image_type ( PROBE_NORMAL ) = {
324 .name = "COM32",
325 .load = com32_load,
326 .exec = com32_exec,