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.
22 * SYSLINUX COM32 image format
26 FILE_LICENCE ( GPL2_OR_LATER
);
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,
50 struct idt_register com32_internal_idtr
;
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
;
62 uint32_t avail_mem_top
;
64 state
= rmsetjmp ( comboot_return
);
67 case 0: /* First time through; invoke COM32 program */
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
;
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
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
)
128 DBGC ( image
, "COM32 %p: returned\n", image
);
132 DBGC ( image
, "COM32 %p: exited\n", image
);
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
);
143 case COMBOOT_EXIT_COMMAND
:
144 DBGC ( image
, "COM32 %p: exited after executing command\n",
153 unhook_comboot_interrupts();
154 comboot_force_text_mode();
160 * Check image name extension
162 * @v image COM32 image
163 * @ret rc Return status code
165 static int com32_identify ( struct image
*image
) {
167 static const uint8_t magic
[] = { 0xB8, 0xFF, 0x4C, 0xCD, 0x21 };
170 if ( image
->len
>= 5 ) {
171 /* Check for magic number
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",
183 /* Magic number not found; check filename extension */
185 ext
= strrchr( image
->name
, '.' );
188 DBGC ( image
, "COM32 %p: no extension\n",
195 if ( strcasecmp( ext
, "c32" ) ) {
196 DBGC ( image
, "COM32 %p: unrecognized extension %s\n",
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
;
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
);
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
) );
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
);
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
) {
267 userptr_t seg_userptr
;
268 size_t filesz
, memsz
;
271 seg
= COM32_BOUNCE_SEG
;
272 seg_userptr
= real_to_user ( seg
, 0 );
274 /* Ensure the entire 64k segment is free */
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
) );
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
) {
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 ) {
305 /* This is a COM32 image, valid or otherwise */
307 image
->type
= &com32_image_type
;
310 if ( ( rc
= comboot_load_image ( image
) ) != 0 ) {
314 /* Prepare bounce buffer segment */
315 if ( ( rc
= comboot_prepare_bounce_buffer ( image
) ) != 0 ) {
322 /** SYSLINUX COM32 image type */
323 struct image_type com32_image_type
__image_type ( PROBE_NORMAL
) = {