diff options
author | Michael Brown | 2014-04-28 21:17:15 +0200 |
---|---|---|
committer | Michael Brown | 2014-04-29 19:24:04 +0200 |
commit | 23b671daf490acaec6fdad55f2bfa44021200a63 (patch) | |
tree | d457aaccd7b8764494b932fbf59b412a85878298 /src/arch/i386/transitions/librm_mgmt.c | |
parent | [build] Allow for a debug level of zero (diff) | |
download | ipxe-23b671daf490acaec6fdad55f2bfa44021200a63.tar.gz ipxe-23b671daf490acaec6fdad55f2bfa44021200a63.tar.xz ipxe-23b671daf490acaec6fdad55f2bfa44021200a63.zip |
[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/librm_mgmt.c')
-rw-r--r-- | src/arch/i386/transitions/librm_mgmt.c | 83 |
1 files changed, 78 insertions, 5 deletions
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 ); |