summaryrefslogtreecommitdiffstats
path: root/src/arch/i386/transitions/librm_mgmt.c
diff options
context:
space:
mode:
authorMichael Brown2014-04-28 21:17:15 +0200
committerMichael Brown2014-04-29 19:24:04 +0200
commit23b671daf490acaec6fdad55f2bfa44021200a63 (patch)
treed457aaccd7b8764494b932fbf59b412a85878298 /src/arch/i386/transitions/librm_mgmt.c
parent[build] Allow for a debug level of zero (diff)
downloadipxe-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.c83
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 );