[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

@@ -188,3 +188,20 @@ int __asmcall com32_cfarcall ( uint32_t proc, physaddr_t stack, size_t stacksz )
return eax;
}
/**
* IRQ handler
*/
void __asmcall com32_irq ( uint32_t vector ) {
uint32_t *ivt_entry = phys_to_virt( vector * 4 );
__asm__ __volatile__ (
REAL_CODE ( "pushfw\n\t"
"pushw %%cs\n\t"
"pushw $com32_irq_return\n\t"
"pushl %0\n\t"
"lret\n"
"com32_irq_return:\n\t" )
: /* no outputs */
: "r" ( *ivt_entry ) );
}

View File

@@ -22,6 +22,26 @@ FILE_LICENCE ( GPL2_OR_LATER )
.arch i386
.code32
/*
* This code is entered after running the following sequence out of
* the interrupt jump buffer:
*
* pushal
* movb $vector, %al
* jmp com32_irq_wrapper
*/
.globl com32_irq_wrapper
com32_irq_wrapper:
movzbl %al,%eax
pushl %eax
movl $com32_irq, %eax
call com32_wrapper
popl %eax
popal
iret
.globl com32_farcall_wrapper
com32_farcall_wrapper:
@@ -43,10 +63,14 @@ com32_intcall_wrapper:
/*jmp com32_wrapper*/ /* fall through */
com32_wrapper:
cli
/* Switch to internal virtual address space */
call _phys_to_virt
/* Switch to internal IDT (if we have one for debugging) */
lidt com32_internal_idtr
mov %eax, (com32_helper_function)
/* Save external COM32 stack pointer */
@@ -74,9 +98,13 @@ com32_wrapper:
movl %esp, (com32_internal_esp)
movl (com32_external_esp), %esp
/* Switch to com32 IDT */
lidt com32_external_idtr
/* Switch to external flat physical address space */
call _virt_to_phys
sti
ret