[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>
This commit is contained in:
Geoff Lywood
2010-07-07 15:35:01 -07:00
committed by Michael Brown
parent 315524e703
commit a4023e02e8
4 changed files with 137 additions and 5 deletions

View File

@@ -42,6 +42,13 @@ FILE_LICENCE ( GPL2_OR_LATER );
struct image_type com32_image_type __image_type ( PROBE_NORMAL );
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;
/**
* Execute COMBOOT image
*
@@ -89,9 +96,12 @@ static int com32_exec ( struct image *image ) {
unregister_image ( image );
__asm__ __volatile__ (
"sidt com32_internal_idtr\n\t"
"lidt com32_external_idtr\n\t" /* Set up IDT */
"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 */
"sti\n\t" /* Enable interrupts */
"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 */
@@ -100,7 +110,9 @@ static int com32_exec ( struct image *image ) {
"pushl %5\n\t" /* Pointer to the command line arguments */
"pushl $6\n\t" /* Number of additional arguments */
"call *%6\n\t" /* Execute image */
"call _phys_to_virt\n\t" /* Switch back to internal virtual address space */
"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) */
"movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */
:
:
@@ -191,25 +203,55 @@ static int com32_identify ( struct image *image ) {
/**
* Load COM32 image into memory
* Load COM32 image into memory and set up the IDT
* @v image COM32 image
* @ret rc Return status code
*/
static int comboot_load_image ( struct image *image ) {
physaddr_t com32_irq_wrapper_phys;
struct idt_descriptor *idt;
struct ijb_entry *ijb;
size_t filesz, memsz;
userptr_t buffer;
int rc;
int rc, i;
filesz = image->len;
/* 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 );
memsz = filesz;
buffer = phys_to_user ( COM32_START_PHYS );
buffer = phys_to_user ( COM32_IDT );
if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
DBGC ( image, "COM32 %p: could not prepare segment: %s\n",
image, strerror ( rc ) );
return rc;
}
/* 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] );
}
/* Copy image to segment */
buffer = phys_to_user ( COM32_START_PHYS );
memcpy_user ( buffer, 0, image->data, 0, filesz );
return 0;