aboutsummaryrefslogtreecommitdiffstats
path: root/src/arch/i386/transitions
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2014-04-28 20:17:15 +0100
committerMichael Brown <mcb30@ipxe.org>2014-04-29 18:24:04 +0100
commit23b671daf490acaec6fdad55f2bfa44021200a63 (patch)
treed457aaccd7b8764494b932fbf59b412a85878298 /src/arch/i386/transitions
parent4413ab4f5acbeba9a42f9567a687ffc5661ae190 (diff)
downloadipxe-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.S55
-rw-r--r--src/arch/i386/transitions/librm_mgmt.c83
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 );