mirror of https://github.com/ipxe/ipxe.git
[comboot] Use built-in interrupt reflector
We now have the ability to handle interrupts while in protected mode, and so no longer need to set up a dedicated interrupt descriptor table while running COM32 executables. Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/17/head
parent
23b671daf4
commit
aaf276ccd4
|
@ -41,13 +41,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
#include <ipxe/init.h>
|
#include <ipxe/init.h>
|
||||||
#include <ipxe/io.h>
|
#include <ipxe/io.h>
|
||||||
|
|
||||||
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
|
* Execute COMBOOT image
|
||||||
*
|
*
|
||||||
|
@ -95,8 +88,6 @@ static int com32_exec_loop ( struct image *image ) {
|
||||||
unregister_image ( image );
|
unregister_image ( image );
|
||||||
|
|
||||||
__asm__ __volatile__ (
|
__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 %%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) */
|
"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 */
|
"call _virt_to_phys\n\t" /* Switch to flat physical address space */
|
||||||
|
@ -111,7 +102,6 @@ static int com32_exec_loop ( struct image *image ) {
|
||||||
"call *%6\n\t" /* Execute image */
|
"call *%6\n\t" /* Execute image */
|
||||||
"cli\n\t" /* Disable interrupts */
|
"cli\n\t" /* Disable interrupts */
|
||||||
"call _phys_to_virt\n\t" /* Switch back to internal virtual address space */
|
"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 */
|
"movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */
|
||||||
:
|
:
|
||||||
:
|
:
|
||||||
|
@ -201,55 +191,25 @@ static int com32_identify ( struct image *image ) {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load COM32 image into memory and set up the IDT
|
* Load COM32 image into memory
|
||||||
* @v image COM32 image
|
* @v image COM32 image
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int com32_load_image ( struct image *image ) {
|
static int com32_load_image ( struct image *image ) {
|
||||||
physaddr_t com32_irq_wrapper_phys;
|
|
||||||
struct idt_descriptor *idt;
|
|
||||||
struct ijb_entry *ijb;
|
|
||||||
size_t filesz, memsz;
|
size_t filesz, memsz;
|
||||||
userptr_t buffer;
|
userptr_t buffer;
|
||||||
int rc, i;
|
int rc;
|
||||||
|
|
||||||
/* The interrupt descriptor table, interrupt jump buffer, and
|
filesz = image->len;
|
||||||
* 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;
|
memsz = filesz;
|
||||||
buffer = phys_to_user ( COM32_IDT );
|
buffer = phys_to_user ( COM32_START_PHYS );
|
||||||
if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
|
if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) {
|
||||||
DBGC ( image, "COM32 %p: could not prepare segment: %s\n",
|
DBGC ( image, "COM32 %p: could not prepare segment: %s\n",
|
||||||
image, strerror ( rc ) );
|
image, strerror ( rc ) );
|
||||||
return 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 */
|
/* Copy image to segment */
|
||||||
buffer = phys_to_user ( COM32_START_PHYS );
|
|
||||||
memcpy_user ( buffer, 0, image->data, 0, filesz );
|
memcpy_user ( buffer, 0, image->data, 0, filesz );
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -13,50 +13,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <ipxe/in.h>
|
#include <ipxe/in.h>
|
||||||
|
|
||||||
/** Descriptor in a 32-bit IDT */
|
|
||||||
struct idt_descriptor {
|
|
||||||
uint16_t offset_low;
|
|
||||||
uint16_t selector;
|
|
||||||
uint16_t flags;
|
|
||||||
uint16_t offset_high;
|
|
||||||
} __attribute__ (( packed ));
|
|
||||||
|
|
||||||
/** Operand for the LIDT instruction */
|
|
||||||
struct idt_register {
|
|
||||||
uint16_t limit;
|
|
||||||
uint32_t base;
|
|
||||||
} __attribute__ (( packed ));
|
|
||||||
|
|
||||||
/** Entry in the interrupt jump buffer */
|
|
||||||
struct ijb_entry {
|
|
||||||
uint8_t pusha_instruction;
|
|
||||||
uint8_t mov_instruction;
|
|
||||||
uint8_t mov_value;
|
|
||||||
uint8_t jump_instruction;
|
|
||||||
uint32_t jump_destination;
|
|
||||||
} __attribute__ (( packed ));
|
|
||||||
|
|
||||||
/** The x86 opcode for "pushal" */
|
|
||||||
#define IJB_PUSHA 0x60
|
|
||||||
|
|
||||||
/** The x86 opcode for "movb $imm8,%al" */
|
|
||||||
#define IJB_MOV_AL_IMM8 0xB0
|
|
||||||
|
|
||||||
/** The x86 opcode for "jmp rel32" */
|
|
||||||
#define IJB_JMP_REL32 0xE9
|
|
||||||
|
|
||||||
/** Flags that specify a 32-bit interrupt gate with DPL=0 */
|
|
||||||
#define IDT_INTERRUPT_GATE_FLAGS 0x8E00
|
|
||||||
|
|
||||||
/** Address of COM32 interrupt descriptor table */
|
|
||||||
#define COM32_IDT 0x100000
|
|
||||||
|
|
||||||
/** Number of entries in a fully populated IDT */
|
|
||||||
#define COM32_NUM_IDT_ENTRIES 256
|
|
||||||
|
|
||||||
/** Address of COM32 interrupt jump buffer */
|
|
||||||
#define COM32_IJB 0x100800
|
|
||||||
|
|
||||||
/** Segment used for COMBOOT PSP and image */
|
/** Segment used for COMBOOT PSP and image */
|
||||||
#define COMBOOT_PSP_SEG 0x07C0
|
#define COMBOOT_PSP_SEG 0x07C0
|
||||||
|
|
||||||
|
@ -153,7 +109,6 @@ extern void unhook_comboot_interrupts ( );
|
||||||
extern void com32_intcall_wrapper ( );
|
extern void com32_intcall_wrapper ( );
|
||||||
extern void com32_farcall_wrapper ( );
|
extern void com32_farcall_wrapper ( );
|
||||||
extern void com32_cfarcall_wrapper ( );
|
extern void com32_cfarcall_wrapper ( );
|
||||||
extern void com32_irq_wrapper ( );
|
|
||||||
|
|
||||||
/* Resolve a hostname to an (IPv4) address */
|
/* Resolve a hostname to an (IPv4) address */
|
||||||
extern int comboot_resolv ( const char *name, struct in_addr *address );
|
extern int comboot_resolv ( const char *name, struct in_addr *address );
|
||||||
|
|
|
@ -189,20 +189,3 @@ int __asmcall com32_cfarcall ( uint32_t proc, physaddr_t stack, size_t stacksz )
|
||||||
|
|
||||||
return eax;
|
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 ) );
|
|
||||||
}
|
|
||||||
|
|
|
@ -23,26 +23,6 @@ FILE_LICENCE ( GPL2_OR_LATER )
|
||||||
.arch i386
|
.arch i386
|
||||||
.code32
|
.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
|
.globl com32_farcall_wrapper
|
||||||
com32_farcall_wrapper:
|
com32_farcall_wrapper:
|
||||||
|
|
||||||
|
@ -69,9 +49,6 @@ com32_wrapper:
|
||||||
/* Switch to internal virtual address space */
|
/* Switch to internal virtual address space */
|
||||||
call _phys_to_virt
|
call _phys_to_virt
|
||||||
|
|
||||||
/* Switch to internal IDT (if we have one for debugging) */
|
|
||||||
lidt com32_internal_idtr
|
|
||||||
|
|
||||||
mov %eax, (com32_helper_function)
|
mov %eax, (com32_helper_function)
|
||||||
|
|
||||||
/* Save external COM32 stack pointer */
|
/* Save external COM32 stack pointer */
|
||||||
|
@ -99,9 +76,6 @@ com32_wrapper:
|
||||||
movl %esp, (com32_internal_esp)
|
movl %esp, (com32_internal_esp)
|
||||||
movl (com32_external_esp), %esp
|
movl (com32_external_esp), %esp
|
||||||
|
|
||||||
/* Switch to com32 IDT */
|
|
||||||
lidt com32_external_idtr
|
|
||||||
|
|
||||||
/* Switch to external flat physical address space */
|
/* Switch to external flat physical address space */
|
||||||
call _virt_to_phys
|
call _virt_to_phys
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue