2008-08-29 00:52:19 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License as
|
|
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
|
|
* License, or any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
2012-07-20 20:55:45 +02:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
* 02110-1301, USA.
|
2008-08-29 00:52:19 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file
|
|
|
|
*
|
|
|
|
* SYSLINUX COM32 image format
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2009-05-01 16:41:06 +02:00
|
|
|
FILE_LICENCE ( GPL2_OR_LATER );
|
|
|
|
|
2008-08-29 00:52:19 +02:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <strings.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <realmode.h>
|
|
|
|
#include <basemem.h>
|
|
|
|
#include <comboot.h>
|
2010-04-19 21:16:01 +02:00
|
|
|
#include <ipxe/uaccess.h>
|
|
|
|
#include <ipxe/image.h>
|
|
|
|
#include <ipxe/segment.h>
|
|
|
|
#include <ipxe/init.h>
|
2010-07-27 20:37:55 +02:00
|
|
|
#include <ipxe/io.h>
|
2008-08-29 00:52:19 +02:00
|
|
|
|
[comboot] Run com32 programs with a valid IDT
COM32 binaries generally expect to run with interrupts
enabled. Syslinux does so, and COM32 programs will execute cli/sti
pairs when running a critical section, to provide mutual exclusion
against BIOS interrupt handlers. Previously, under iPXE, the IDT was
not valid, so any interrupt (e.g. a timer tick) would generally cause
the machine to triple fault.
This change introduces code to:
- Create a valid IDT at the same location that syslinux uses
- Create an "interrupt jump buffer", which contains small pieces of
code that simply record the vector number and jump to a common
handler
- Thunk down to real mode and execute the BIOS's interrupt handler
whenever an interrupt is received in a COM32 program
- Switch IDTs and enable/disable interrupts when context switching to
and from COM32 binaries
Testing done:
- Booted VMware ESX using a COM32 multiboot loader (mboot.c32)
- Built with GDBSERIAL enabled, and tested breakpoints on int22 and
com32_irq
- Put the following code in a COM32 program:
asm volatile ( "sti" );
while ( 1 );
Before this change, the machine would triple fault
immediately. After this change, it hangs as expected. Under Bochs,
it is possible to see the interrupt handler run, and the current
time in the BIOS data area gets incremented.
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-07-08 00:35:01 +02:00
|
|
|
struct idt_register com32_external_idtr = {
|
|
|
|
.limit = COM32_NUM_IDT_ENTRIES * sizeof ( struct idt_descriptor ) - 1,
|
|
|
|
.base = COM32_IDT
|
|
|
|
};
|
|
|
|
|
|
|
|
struct idt_register com32_internal_idtr;
|
|
|
|
|
2008-08-29 00:52:19 +02:00
|
|
|
/**
|
|
|
|
* Execute COMBOOT image
|
|
|
|
*
|
|
|
|
* @v image COM32 image
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
2011-03-07 01:37:50 +01:00
|
|
|
static int com32_exec_loop ( struct image *image ) {
|
2008-08-29 00:52:19 +02:00
|
|
|
struct memory_map memmap;
|
|
|
|
unsigned int i;
|
|
|
|
int state;
|
|
|
|
uint32_t avail_mem_top;
|
|
|
|
|
2009-02-17 04:33:26 +01:00
|
|
|
state = rmsetjmp ( comboot_return );
|
2008-08-29 00:52:19 +02:00
|
|
|
|
|
|
|
switch ( state ) {
|
|
|
|
case 0: /* First time through; invoke COM32 program */
|
|
|
|
|
|
|
|
/* Get memory map */
|
|
|
|
get_memmap ( &memmap );
|
|
|
|
|
|
|
|
/* Find end of block covering COM32 image loading area */
|
|
|
|
for ( i = 0, avail_mem_top = 0 ; i < memmap.count ; i++ ) {
|
|
|
|
if ( (memmap.regions[i].start <= COM32_START_PHYS) &&
|
|
|
|
(memmap.regions[i].end > COM32_START_PHYS + image->len) ) {
|
|
|
|
avail_mem_top = memmap.regions[i].end;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DBGC ( image, "COM32 %p: available memory top = 0x%x\n",
|
2009-02-17 01:47:35 +01:00
|
|
|
image, avail_mem_top );
|
2008-08-29 00:52:19 +02:00
|
|
|
|
|
|
|
assert ( avail_mem_top != 0 );
|
|
|
|
|
|
|
|
com32_external_esp = phys_to_virt ( avail_mem_top );
|
|
|
|
|
|
|
|
/* Hook COMBOOT API interrupts */
|
2009-02-17 01:47:35 +01:00
|
|
|
hook_comboot_interrupts();
|
2008-08-29 00:52:19 +02:00
|
|
|
|
2009-02-17 01:47:35 +01:00
|
|
|
/* Unregister image, so that a "boot" command doesn't
|
|
|
|
* throw us into an execution loop. We never
|
|
|
|
* reregister ourselves; COMBOOT images expect to be
|
|
|
|
* removed on exit.
|
2008-08-29 00:52:19 +02:00
|
|
|
*/
|
|
|
|
unregister_image ( image );
|
|
|
|
|
|
|
|
__asm__ __volatile__ (
|
[comboot] Run com32 programs with a valid IDT
COM32 binaries generally expect to run with interrupts
enabled. Syslinux does so, and COM32 programs will execute cli/sti
pairs when running a critical section, to provide mutual exclusion
against BIOS interrupt handlers. Previously, under iPXE, the IDT was
not valid, so any interrupt (e.g. a timer tick) would generally cause
the machine to triple fault.
This change introduces code to:
- Create a valid IDT at the same location that syslinux uses
- Create an "interrupt jump buffer", which contains small pieces of
code that simply record the vector number and jump to a common
handler
- Thunk down to real mode and execute the BIOS's interrupt handler
whenever an interrupt is received in a COM32 program
- Switch IDTs and enable/disable interrupts when context switching to
and from COM32 binaries
Testing done:
- Booted VMware ESX using a COM32 multiboot loader (mboot.c32)
- Built with GDBSERIAL enabled, and tested breakpoints on int22 and
com32_irq
- Put the following code in a COM32 program:
asm volatile ( "sti" );
while ( 1 );
Before this change, the machine would triple fault
immediately. After this change, it hangs as expected. Under Bochs,
it is possible to see the interrupt handler run, and the current
time in the BIOS data area gets incremented.
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-07-08 00:35:01 +02:00
|
|
|
"sidt com32_internal_idtr\n\t"
|
|
|
|
"lidt com32_external_idtr\n\t" /* Set up IDT */
|
2008-08-29 00:52:19 +02:00
|
|
|
"movl %%esp, (com32_internal_esp)\n\t" /* Save internal virtual address space ESP */
|
|
|
|
"movl (com32_external_esp), %%esp\n\t" /* Switch to COM32 ESP (top of available memory) */
|
|
|
|
"call _virt_to_phys\n\t" /* Switch to flat physical address space */
|
[comboot] Run com32 programs with a valid IDT
COM32 binaries generally expect to run with interrupts
enabled. Syslinux does so, and COM32 programs will execute cli/sti
pairs when running a critical section, to provide mutual exclusion
against BIOS interrupt handlers. Previously, under iPXE, the IDT was
not valid, so any interrupt (e.g. a timer tick) would generally cause
the machine to triple fault.
This change introduces code to:
- Create a valid IDT at the same location that syslinux uses
- Create an "interrupt jump buffer", which contains small pieces of
code that simply record the vector number and jump to a common
handler
- Thunk down to real mode and execute the BIOS's interrupt handler
whenever an interrupt is received in a COM32 program
- Switch IDTs and enable/disable interrupts when context switching to
and from COM32 binaries
Testing done:
- Booted VMware ESX using a COM32 multiboot loader (mboot.c32)
- Built with GDBSERIAL enabled, and tested breakpoints on int22 and
com32_irq
- Put the following code in a COM32 program:
asm volatile ( "sti" );
while ( 1 );
Before this change, the machine would triple fault
immediately. After this change, it hangs as expected. Under Bochs,
it is possible to see the interrupt handler run, and the current
time in the BIOS data area gets incremented.
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-07-08 00:35:01 +02:00
|
|
|
"sti\n\t" /* Enable interrupts */
|
2008-08-29 00:52:19 +02:00
|
|
|
"pushl %0\n\t" /* Pointer to CDECL helper function */
|
|
|
|
"pushl %1\n\t" /* Pointer to FAR call helper function */
|
|
|
|
"pushl %2\n\t" /* Size of low memory bounce buffer */
|
|
|
|
"pushl %3\n\t" /* Pointer to low memory bounce buffer */
|
|
|
|
"pushl %4\n\t" /* Pointer to INT call helper function */
|
|
|
|
"pushl %5\n\t" /* Pointer to the command line arguments */
|
|
|
|
"pushl $6\n\t" /* Number of additional arguments */
|
|
|
|
"call *%6\n\t" /* Execute image */
|
[comboot] Run com32 programs with a valid IDT
COM32 binaries generally expect to run with interrupts
enabled. Syslinux does so, and COM32 programs will execute cli/sti
pairs when running a critical section, to provide mutual exclusion
against BIOS interrupt handlers. Previously, under iPXE, the IDT was
not valid, so any interrupt (e.g. a timer tick) would generally cause
the machine to triple fault.
This change introduces code to:
- Create a valid IDT at the same location that syslinux uses
- Create an "interrupt jump buffer", which contains small pieces of
code that simply record the vector number and jump to a common
handler
- Thunk down to real mode and execute the BIOS's interrupt handler
whenever an interrupt is received in a COM32 program
- Switch IDTs and enable/disable interrupts when context switching to
and from COM32 binaries
Testing done:
- Booted VMware ESX using a COM32 multiboot loader (mboot.c32)
- Built with GDBSERIAL enabled, and tested breakpoints on int22 and
com32_irq
- Put the following code in a COM32 program:
asm volatile ( "sti" );
while ( 1 );
Before this change, the machine would triple fault
immediately. After this change, it hangs as expected. Under Bochs,
it is possible to see the interrupt handler run, and the current
time in the BIOS data area gets incremented.
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-07-08 00:35:01 +02:00
|
|
|
"cli\n\t" /* Disable interrupts */
|
|
|
|
"call _phys_to_virt\n\t" /* Switch back to internal virtual address space */
|
|
|
|
"lidt com32_internal_idtr\n\t" /* Switch back to internal IDT (for debugging) */
|
2008-08-29 00:52:19 +02:00
|
|
|
"movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */
|
|
|
|
:
|
|
|
|
:
|
|
|
|
/* %0 */ "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ),
|
|
|
|
/* %1 */ "r" ( virt_to_phys ( com32_farcall_wrapper ) ),
|
|
|
|
/* %2 */ "r" ( get_fbms() * 1024 - (COM32_BOUNCE_SEG << 4) ),
|
|
|
|
/* %3 */ "i" ( COM32_BOUNCE_SEG << 4 ),
|
|
|
|
/* %4 */ "r" ( virt_to_phys ( com32_intcall_wrapper ) ),
|
2011-03-07 03:38:26 +01:00
|
|
|
/* %5 */ "r" ( virt_to_phys ( image->cmdline ?
|
|
|
|
image->cmdline : "" ) ),
|
2008-08-29 00:52:19 +02:00
|
|
|
/* %6 */ "r" ( COM32_START_PHYS )
|
|
|
|
:
|
|
|
|
"memory" );
|
2009-02-17 01:47:35 +01:00
|
|
|
DBGC ( image, "COM32 %p: returned\n", image );
|
2008-08-29 00:52:19 +02:00
|
|
|
break;
|
|
|
|
|
2009-02-17 01:47:35 +01:00
|
|
|
case COMBOOT_EXIT:
|
|
|
|
DBGC ( image, "COM32 %p: exited\n", image );
|
2008-08-29 00:52:19 +02:00
|
|
|
break;
|
|
|
|
|
2009-02-17 01:47:35 +01:00
|
|
|
case COMBOOT_EXIT_RUN_KERNEL:
|
2011-03-09 17:55:51 +01:00
|
|
|
assert ( image->replacement );
|
|
|
|
DBGC ( image, "COM32 %p: exited to run kernel %s\n",
|
|
|
|
image, image->replacement->name );
|
2008-08-29 00:52:19 +02:00
|
|
|
break;
|
|
|
|
|
2009-02-17 01:47:35 +01:00
|
|
|
case COMBOOT_EXIT_COMMAND:
|
|
|
|
DBGC ( image, "COM32 %p: exited after executing command\n",
|
|
|
|
image );
|
|
|
|
break;
|
2008-08-29 00:52:19 +02:00
|
|
|
|
2009-02-17 01:47:35 +01:00
|
|
|
default:
|
|
|
|
assert ( 0 );
|
|
|
|
break;
|
|
|
|
}
|
2008-08-29 00:52:19 +02:00
|
|
|
|
2009-02-17 03:03:16 +01:00
|
|
|
unhook_comboot_interrupts();
|
2009-02-17 01:47:35 +01:00
|
|
|
comboot_force_text_mode();
|
2008-08-29 00:52:19 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check image name extension
|
|
|
|
*
|
|
|
|
* @v image COM32 image
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int com32_identify ( struct image *image ) {
|
|
|
|
const char *ext;
|
|
|
|
static const uint8_t magic[] = { 0xB8, 0xFF, 0x4C, 0xCD, 0x21 };
|
|
|
|
uint8_t buf[5];
|
|
|
|
|
|
|
|
if ( image->len >= 5 ) {
|
|
|
|
/* Check for magic number
|
|
|
|
* mov eax,21cd4cffh
|
|
|
|
* B8 FF 4C CD 21
|
|
|
|
*/
|
|
|
|
copy_from_user ( buf, image->data, 0, sizeof(buf) );
|
|
|
|
if ( ! memcmp ( buf, magic, sizeof(buf) ) ) {
|
|
|
|
DBGC ( image, "COM32 %p: found magic number\n",
|
|
|
|
image );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Magic number not found; check filename extension */
|
|
|
|
|
|
|
|
ext = strrchr( image->name, '.' );
|
|
|
|
|
|
|
|
if ( ! ext ) {
|
|
|
|
DBGC ( image, "COM32 %p: no extension\n",
|
|
|
|
image );
|
|
|
|
return -ENOEXEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
++ext;
|
|
|
|
|
|
|
|
if ( strcasecmp( ext, "c32" ) ) {
|
|
|
|
DBGC ( image, "COM32 %p: unrecognized extension %s\n",
|
|
|
|
image, ext );
|
|
|
|
return -ENOEXEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
[comboot] Run com32 programs with a valid IDT
COM32 binaries generally expect to run with interrupts
enabled. Syslinux does so, and COM32 programs will execute cli/sti
pairs when running a critical section, to provide mutual exclusion
against BIOS interrupt handlers. Previously, under iPXE, the IDT was
not valid, so any interrupt (e.g. a timer tick) would generally cause
the machine to triple fault.
This change introduces code to:
- Create a valid IDT at the same location that syslinux uses
- Create an "interrupt jump buffer", which contains small pieces of
code that simply record the vector number and jump to a common
handler
- Thunk down to real mode and execute the BIOS's interrupt handler
whenever an interrupt is received in a COM32 program
- Switch IDTs and enable/disable interrupts when context switching to
and from COM32 binaries
Testing done:
- Booted VMware ESX using a COM32 multiboot loader (mboot.c32)
- Built with GDBSERIAL enabled, and tested breakpoints on int22 and
com32_irq
- Put the following code in a COM32 program:
asm volatile ( "sti" );
while ( 1 );
Before this change, the machine would triple fault
immediately. After this change, it hangs as expected. Under Bochs,
it is possible to see the interrupt handler run, and the current
time in the BIOS data area gets incremented.
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-07-08 00:35:01 +02:00
|
|
|
* Load COM32 image into memory and set up the IDT
|
2008-08-29 00:52:19 +02:00
|
|
|
* @v image COM32 image
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
2011-03-07 01:37:50 +01:00
|
|
|
static int com32_load_image ( struct image *image ) {
|
[comboot] Run com32 programs with a valid IDT
COM32 binaries generally expect to run with interrupts
enabled. Syslinux does so, and COM32 programs will execute cli/sti
pairs when running a critical section, to provide mutual exclusion
against BIOS interrupt handlers. Previously, under iPXE, the IDT was
not valid, so any interrupt (e.g. a timer tick) would generally cause
the machine to triple fault.
This change introduces code to:
- Create a valid IDT at the same location that syslinux uses
- Create an "interrupt jump buffer", which contains small pieces of
code that simply record the vector number and jump to a common
handler
- Thunk down to real mode and execute the BIOS's interrupt handler
whenever an interrupt is received in a COM32 program
- Switch IDTs and enable/disable interrupts when context switching to
and from COM32 binaries
Testing done:
- Booted VMware ESX using a COM32 multiboot loader (mboot.c32)
- Built with GDBSERIAL enabled, and tested breakpoints on int22 and
com32_irq
- Put the following code in a COM32 program:
asm volatile ( "sti" );
while ( 1 );
Before this change, the machine would triple fault
immediately. After this change, it hangs as expected. Under Bochs,
it is possible to see the interrupt handler run, and the current
time in the BIOS data area gets incremented.
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-07-08 00:35:01 +02:00
|
|
|
physaddr_t com32_irq_wrapper_phys;
|
|
|
|
struct idt_descriptor *idt;
|
|
|
|
struct ijb_entry *ijb;
|
2008-08-29 00:52:19 +02:00
|
|
|
size_t filesz, memsz;
|
|
|
|
userptr_t buffer;
|
[comboot] Run com32 programs with a valid IDT
COM32 binaries generally expect to run with interrupts
enabled. Syslinux does so, and COM32 programs will execute cli/sti
pairs when running a critical section, to provide mutual exclusion
against BIOS interrupt handlers. Previously, under iPXE, the IDT was
not valid, so any interrupt (e.g. a timer tick) would generally cause
the machine to triple fault.
This change introduces code to:
- Create a valid IDT at the same location that syslinux uses
- Create an "interrupt jump buffer", which contains small pieces of
code that simply record the vector number and jump to a common
handler
- Thunk down to real mode and execute the BIOS's interrupt handler
whenever an interrupt is received in a COM32 program
- Switch IDTs and enable/disable interrupts when context switching to
and from COM32 binaries
Testing done:
- Booted VMware ESX using a COM32 multiboot loader (mboot.c32)
- Built with GDBSERIAL enabled, and tested breakpoints on int22 and
com32_irq
- Put the following code in a COM32 program:
asm volatile ( "sti" );
while ( 1 );
Before this change, the machine would triple fault
immediately. After this change, it hangs as expected. Under Bochs,
it is possible to see the interrupt handler run, and the current
time in the BIOS data area gets incremented.
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-07-08 00:35:01 +02:00
|
|
|
int rc, i;
|
|
|
|
|
|
|
|
/* The interrupt descriptor table, interrupt jump buffer, and
|
|
|
|
* image data are all contiguous in memory. Prepare them all at once.
|
|
|
|
*/
|
|
|
|
filesz = image->len +
|
|
|
|
COM32_NUM_IDT_ENTRIES * sizeof ( struct idt_descriptor ) +
|
|
|
|
COM32_NUM_IDT_ENTRIES * sizeof ( struct ijb_entry );
|
2008-08-29 00:52:19 +02:00
|
|
|
memsz = filesz;
|
[comboot] Run com32 programs with a valid IDT
COM32 binaries generally expect to run with interrupts
enabled. Syslinux does so, and COM32 programs will execute cli/sti
pairs when running a critical section, to provide mutual exclusion
against BIOS interrupt handlers. Previously, under iPXE, the IDT was
not valid, so any interrupt (e.g. a timer tick) would generally cause
the machine to triple fault.
This change introduces code to:
- Create a valid IDT at the same location that syslinux uses
- Create an "interrupt jump buffer", which contains small pieces of
code that simply record the vector number and jump to a common
handler
- Thunk down to real mode and execute the BIOS's interrupt handler
whenever an interrupt is received in a COM32 program
- Switch IDTs and enable/disable interrupts when context switching to
and from COM32 binaries
Testing done:
- Booted VMware ESX using a COM32 multiboot loader (mboot.c32)
- Built with GDBSERIAL enabled, and tested breakpoints on int22 and
com32_irq
- Put the following code in a COM32 program:
asm volatile ( "sti" );
while ( 1 );
Before this change, the machine would triple fault
immediately. After this change, it hangs as expected. Under Bochs,
it is possible to see the interrupt handler run, and the current
time in the BIOS data area gets incremented.
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-07-08 00:35:01 +02:00
|
|
|
buffer = phys_to_user ( COM32_IDT );
|
2008-08-29 00:52:19 +02:00
|
|
|
if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
|
|
|
|
DBGC ( image, "COM32 %p: could not prepare segment: %s\n",
|
|
|
|
image, strerror ( rc ) );
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
[comboot] Run com32 programs with a valid IDT
COM32 binaries generally expect to run with interrupts
enabled. Syslinux does so, and COM32 programs will execute cli/sti
pairs when running a critical section, to provide mutual exclusion
against BIOS interrupt handlers. Previously, under iPXE, the IDT was
not valid, so any interrupt (e.g. a timer tick) would generally cause
the machine to triple fault.
This change introduces code to:
- Create a valid IDT at the same location that syslinux uses
- Create an "interrupt jump buffer", which contains small pieces of
code that simply record the vector number and jump to a common
handler
- Thunk down to real mode and execute the BIOS's interrupt handler
whenever an interrupt is received in a COM32 program
- Switch IDTs and enable/disable interrupts when context switching to
and from COM32 binaries
Testing done:
- Booted VMware ESX using a COM32 multiboot loader (mboot.c32)
- Built with GDBSERIAL enabled, and tested breakpoints on int22 and
com32_irq
- Put the following code in a COM32 program:
asm volatile ( "sti" );
while ( 1 );
Before this change, the machine would triple fault
immediately. After this change, it hangs as expected. Under Bochs,
it is possible to see the interrupt handler run, and the current
time in the BIOS data area gets incremented.
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-07-08 00:35:01 +02:00
|
|
|
/* Write the IDT and IJB */
|
|
|
|
idt = phys_to_virt ( COM32_IDT );
|
|
|
|
ijb = phys_to_virt ( COM32_IJB );
|
|
|
|
com32_irq_wrapper_phys = virt_to_phys ( com32_irq_wrapper );
|
|
|
|
|
|
|
|
for ( i = 0; i < COM32_NUM_IDT_ENTRIES; i++ ) {
|
|
|
|
uint32_t ijb_address = virt_to_phys ( &ijb[i] );
|
|
|
|
|
|
|
|
idt[i].offset_low = ijb_address & 0xFFFF;
|
|
|
|
idt[i].selector = PHYSICAL_CS;
|
|
|
|
idt[i].flags = IDT_INTERRUPT_GATE_FLAGS;
|
|
|
|
idt[i].offset_high = ijb_address >> 16;
|
|
|
|
|
|
|
|
ijb[i].pusha_instruction = IJB_PUSHA;
|
|
|
|
ijb[i].mov_instruction = IJB_MOV_AL_IMM8;
|
|
|
|
ijb[i].mov_value = i;
|
|
|
|
ijb[i].jump_instruction = IJB_JMP_REL32;
|
|
|
|
ijb[i].jump_destination = com32_irq_wrapper_phys -
|
|
|
|
virt_to_phys ( &ijb[i + 1] );
|
|
|
|
}
|
|
|
|
|
2008-08-29 00:52:19 +02:00
|
|
|
/* Copy image to segment */
|
[comboot] Run com32 programs with a valid IDT
COM32 binaries generally expect to run with interrupts
enabled. Syslinux does so, and COM32 programs will execute cli/sti
pairs when running a critical section, to provide mutual exclusion
against BIOS interrupt handlers. Previously, under iPXE, the IDT was
not valid, so any interrupt (e.g. a timer tick) would generally cause
the machine to triple fault.
This change introduces code to:
- Create a valid IDT at the same location that syslinux uses
- Create an "interrupt jump buffer", which contains small pieces of
code that simply record the vector number and jump to a common
handler
- Thunk down to real mode and execute the BIOS's interrupt handler
whenever an interrupt is received in a COM32 program
- Switch IDTs and enable/disable interrupts when context switching to
and from COM32 binaries
Testing done:
- Booted VMware ESX using a COM32 multiboot loader (mboot.c32)
- Built with GDBSERIAL enabled, and tested breakpoints on int22 and
com32_irq
- Put the following code in a COM32 program:
asm volatile ( "sti" );
while ( 1 );
Before this change, the machine would triple fault
immediately. After this change, it hangs as expected. Under Bochs,
it is possible to see the interrupt handler run, and the current
time in the BIOS data area gets incremented.
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2010-07-08 00:35:01 +02:00
|
|
|
buffer = phys_to_user ( COM32_START_PHYS );
|
2008-08-29 00:52:19 +02:00
|
|
|
memcpy_user ( buffer, 0, image->data, 0, filesz );
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepare COM32 low memory bounce buffer
|
|
|
|
* @v image COM32 image
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
2011-03-07 01:37:50 +01:00
|
|
|
static int com32_prepare_bounce_buffer ( struct image * image ) {
|
2008-08-29 00:52:19 +02:00
|
|
|
unsigned int seg;
|
|
|
|
userptr_t seg_userptr;
|
|
|
|
size_t filesz, memsz;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
seg = COM32_BOUNCE_SEG;
|
|
|
|
seg_userptr = real_to_user ( seg, 0 );
|
|
|
|
|
|
|
|
/* Ensure the entire 64k segment is free */
|
|
|
|
memsz = 0xFFFF;
|
|
|
|
filesz = 0;
|
|
|
|
|
|
|
|
/* Prepare, verify, and load the real-mode segment */
|
|
|
|
if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) {
|
|
|
|
DBGC ( image, "COM32 %p: could not prepare bounce buffer segment: %s\n",
|
|
|
|
image, strerror ( rc ) );
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-03-07 01:37:50 +01:00
|
|
|
* Probe COM32 image
|
2008-08-29 00:52:19 +02:00
|
|
|
*
|
|
|
|
* @v image COM32 image
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
2011-03-07 01:37:50 +01:00
|
|
|
static int com32_probe ( struct image *image ) {
|
2008-08-29 00:52:19 +02:00
|
|
|
int rc;
|
|
|
|
|
2011-03-07 03:38:26 +01:00
|
|
|
DBGC ( image, "COM32 %p: name '%s'\n", image, image->name );
|
2008-08-29 00:52:19 +02:00
|
|
|
|
|
|
|
/* Check if this is a COMBOOT image */
|
|
|
|
if ( ( rc = com32_identify ( image ) ) != 0 ) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2011-03-07 01:37:50 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Execute COMBOOT image
|
|
|
|
*
|
|
|
|
* @v image COM32 image
|
|
|
|
* @ret rc Return status code
|
|
|
|
*/
|
|
|
|
static int com32_exec ( struct image *image ) {
|
|
|
|
int rc;
|
2008-08-29 00:52:19 +02:00
|
|
|
|
|
|
|
/* Load image */
|
2011-03-07 01:37:50 +01:00
|
|
|
if ( ( rc = com32_load_image ( image ) ) != 0 ) {
|
2008-08-29 00:52:19 +02:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare bounce buffer segment */
|
2011-03-07 01:37:50 +01:00
|
|
|
if ( ( rc = com32_prepare_bounce_buffer ( image ) ) != 0 ) {
|
2008-08-29 00:52:19 +02:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2011-03-07 01:37:50 +01:00
|
|
|
return com32_exec_loop ( image );
|
2008-08-29 00:52:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** SYSLINUX COM32 image type */
|
|
|
|
struct image_type com32_image_type __image_type ( PROBE_NORMAL ) = {
|
|
|
|
.name = "COM32",
|
2011-03-07 01:37:50 +01:00
|
|
|
.probe = com32_probe,
|
2008-08-29 00:52:19 +02:00
|
|
|
.exec = com32_exec,
|
|
|
|
};
|