diff options
author | Michael Brown <mcb30@ipxe.org> | 2014-04-28 20:17:15 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2014-04-29 18:24:04 +0100 |
commit | 23b671daf490acaec6fdad55f2bfa44021200a63 (patch) | |
tree | d457aaccd7b8764494b932fbf59b412a85878298 /src/arch/i386/transitions | |
parent | 4413ab4f5acbeba9a42f9567a687ffc5661ae190 (diff) | |
download | ipxe-23b671daf490acaec6fdad55f2bfa44021200a63.tar.gz |
[librm] Allow interrupts in protected mode
When running in a virtual machine, switching to real mode may be
expensive. Allow interrupts to be enabled while in protected mode and
reflected down to the real-mode interrupt handlers.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/arch/i386/transitions')
-rw-r--r-- | src/arch/i386/transitions/librm.S | 55 | ||||
-rw-r--r-- | src/arch/i386/transitions/librm_mgmt.c | 83 |
2 files changed, 126 insertions, 12 deletions
diff --git a/src/arch/i386/transitions/librm.S b/src/arch/i386/transitions/librm.S index b5affdb8..0e550def 100644 --- a/src/arch/i386/transitions/librm.S +++ b/src/arch/i386/transitions/librm.S @@ -128,10 +128,15 @@ init_librm: addr32 leal (%eax, %edi), %ebx movl %ebx, rm_data16 - /* Set GDT and IDT base */ + /* Set GDT base */ movl %eax, gdt_base addl $gdt, gdt_base - call idt_init + + /* Initialise IDT */ + pushl $init_idt + pushw %cs + call prot_call + popl %eax /* discard */ /* Restore registers */ negl %edi @@ -141,14 +146,12 @@ init_librm: .section ".text16", "ax", @progbits .code16 - .weak idt_init set_seg_base: 1: movw %ax, 2(%bx) rorl $16, %eax movb %al, 4(%bx) movb %ah, 7(%bx) roll $16, %eax -idt_init: /* Reuse the return opcode here */ ret /**************************************************************************** @@ -237,10 +240,8 @@ real_to_prot: /* Return to virtual address */ ret - /* Default IDTR with no interrupts */ + /* Default real-mode interrupt descriptor table */ .section ".data16", "aw", @progbits - .weak idtr -idtr: rm_idtr: .word 0xffff /* limit */ .long 0 /* base */ @@ -537,6 +538,46 @@ flatten_dummy: ret /**************************************************************************** + * Interrupt wrapper + * + * Used by the protected-mode interrupt vectors to call the + * interrupt() function. + * + * May be entered with either physical or virtual stack segment. + **************************************************************************** + */ + .globl interrupt_wrapper +interrupt_wrapper: + /* Preserve segment registers and original %esp */ + pushl %ds + pushl %es + pushl %fs + pushl %gs + pushl %ss + pushl %esp + + /* Switch to virtual addressing */ + call _intr_to_virt + + /* Expand IRQ number to whole %eax register */ + movzbl %al, %eax + + /* Call interrupt handler */ + call interrupt + + /* Restore original stack and segment registers */ + lss (%esp), %esp + popl %ss + popl %gs + popl %fs + popl %es + popl %ds + + /* Restore registers and return */ + popal + iret + +/**************************************************************************** * Stored real-mode and protected-mode stack pointers * * The real-mode stack pointer is stored here whenever real_to_prot diff --git a/src/arch/i386/transitions/librm_mgmt.c b/src/arch/i386/transitions/librm_mgmt.c index f00be811..dee14357 100644 --- a/src/arch/i386/transitions/librm_mgmt.c +++ b/src/arch/i386/transitions/librm_mgmt.c @@ -9,19 +9,35 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <stdint.h> #include <realmode.h> +#include <pic8259.h> /* * This file provides functions for managing librm. * */ +/** The interrupt wrapper */ +extern char interrupt_wrapper[]; + +/** The interrupt vectors */ +static struct interrupt_vector intr_vec[ IRQ_MAX + 1 ]; + +/** The interrupt descriptor table */ +struct interrupt_descriptor idt[NUM_INT] __attribute__ (( aligned ( 16 ) )); + +/** The interrupt descriptor table register */ +struct idtr __data16 ( idtr ) = { + .limit = ( sizeof ( idt ) - 1 ), +}; +#define idtr __use_data16 ( idtr ) + /** * Allocate space on the real-mode stack and copy data there from a * user buffer * - * @v data User buffer - * @v size Size of stack data - * @ret sp New value of real-mode stack pointer + * @v data User buffer + * @v size Size of stack data + * @ret sp New value of real-mode stack pointer */ uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) { userptr_t rm_stack; @@ -35,8 +51,8 @@ uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) { * Deallocate space on the real-mode stack, optionally copying back * data to a user buffer. * - * @v data User buffer - * @v size Size of stack data + * @v data User buffer + * @v size Size of stack data */ void remove_user_from_rm_stack ( userptr_t data, size_t size ) { if ( data ) { @@ -46,6 +62,63 @@ void remove_user_from_rm_stack ( userptr_t data, size_t size ) { rm_sp += size; }; +/** + * Set interrupt vector + * + * @v intr Interrupt number + * @v vector Interrupt vector, or NULL to disable + */ +void set_interrupt_vector ( unsigned int intr, void *vector ) { + struct interrupt_descriptor *idte; + + idte = &idt[intr]; + idte->segment = VIRTUAL_CS; + idte->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 ); + idte->low = ( ( ( uint32_t ) vector ) & 0xffff ); + idte->high = ( ( ( uint32_t ) vector ) >> 16 ); +} + +/** + * Initialise interrupt descriptor table + * + */ +void init_idt ( void ) { + struct interrupt_vector *vec; + unsigned int irq; + unsigned int intr; + + /* Initialise the interrupt descriptor table and interrupt vectors */ + for ( irq = 0 ; irq <= IRQ_MAX ; irq++ ) { + intr = IRQ_INT ( irq ); + vec = &intr_vec[irq]; + vec->pushal = PUSHAL_INSN; + vec->movb = MOVB_INSN; + vec->intr = intr; + vec->jmp = JMP_INSN; + vec->offset = ( ( uint32_t ) interrupt_wrapper - + ( uint32_t ) vec->next ); + set_interrupt_vector ( intr, vec ); + } + + /* Initialise the interrupt descriptor table register */ + idtr.base = virt_to_phys ( idt ); +} + +/** + * Interrupt handler + * + * @v irq Interrupt number + */ +void __attribute__ (( cdecl, regparm ( 1 ) )) interrupt ( int irq ) { + uint32_t discard_eax; + + /* Reissue interrupt in real mode */ + __asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t" + "\n1:\n\t" + "int $0x00\n\t" ) + : "=a" ( discard_eax ) : "0" ( irq ) ); +} + PROVIDE_UACCESS_INLINE ( librm, phys_to_user ); PROVIDE_UACCESS_INLINE ( librm, user_to_phys ); PROVIDE_UACCESS_INLINE ( librm, virt_to_user ); |