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
*
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 */
"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 */
:
:
/**
- * 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;
-
- filesz = image->len;
+ 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 );
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;
#include <setjmp.h>
#include <gpxe/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 */
#define COMBOOT_PSP_SEG 0x07C0
extern void com32_intcall_wrapper ( );
extern void com32_farcall_wrapper ( );
extern void com32_cfarcall_wrapper ( );
+extern void com32_irq_wrapper ( );
/* Resolve a hostname to an (IPv4) address */
extern int comboot_resolv ( const char *name, struct in_addr *address );
.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:
/*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 */
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