summaryrefslogtreecommitdiffstats
path: root/src/arch/i386/core
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/core
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/core')
-rw-r--r--src/arch/i386/core/gdbidt.S151
-rw-r--r--src/arch/i386/core/gdbmach.c28
-rw-r--r--src/arch/i386/core/virtaddr.S62
3 files changed, 118 insertions, 123 deletions
diff --git a/src/arch/i386/core/gdbidt.S b/src/arch/i386/core/gdbidt.S
index cd8b38a9e..a1e309d7c 100644
--- a/src/arch/i386/core/gdbidt.S
+++ b/src/arch/i386/core/gdbidt.S
@@ -1,102 +1,11 @@
/*
- * Interrupt Descriptor Table (IDT) setup and interrupt handlers for GDB stub.
+ * Interrupt handlers for GDB stub
*/
-#include <librm.h>
-
#define SIZEOF_I386_REGS 32
#define SIZEOF_I386_FLAGS 4
/****************************************************************************
- * Interrupt Descriptor Table
- ****************************************************************************
- */
- .section ".data16", "aw", @progbits
- .globl idtr
-idtr:
-idt_limit:
- .word idt_length - 1
-idt_base:
- .long 0
-
-/* IDT entries have the following format:
- * offset_lo, segment selector, flags, offset_hi
- *
- * Since it is not possible to specify relocations in arbitrary
- * expressions like (int_overflow & 0xffff), we initialise the
- * IDT with entries in an incorrect format.
- *
- * The entries are shuffled into the correct format in init_librm().
- */
-#define IDT_ENTRY_EMPTY(name) .word 0, 0, 0, 0
-#define IDT_ENTRY_PRESENT(name) \
- .long int_##name; \
- .word 0x8e00, VIRTUAL_CS
-
-.align 16
-idt:
- IDT_ENTRY_PRESENT(divide_error)
- IDT_ENTRY_PRESENT(debug_trap)
- IDT_ENTRY_EMPTY(non_maskable_interrupt)
- IDT_ENTRY_PRESENT(breakpoint)
- IDT_ENTRY_PRESENT(overflow)
- IDT_ENTRY_PRESENT(bound_range_exceeded)
- IDT_ENTRY_PRESENT(invalid_opcode)
- IDT_ENTRY_EMPTY(device_not_available)
- IDT_ENTRY_PRESENT(double_fault)
- IDT_ENTRY_EMPTY(coprocessor_segment_overrun)
- IDT_ENTRY_PRESENT(invalid_tss)
- IDT_ENTRY_PRESENT(segment_not_present)
- IDT_ENTRY_PRESENT(stack_segment_fault)
- IDT_ENTRY_PRESENT(general_protection)
- IDT_ENTRY_PRESENT(page_fault)
-idt_end:
- .equ idt_length, idt_end - idt
-
-/* The IDT entries are fixed up (once) in init_librm() */
-idt_fixed:
- .byte 0
-
-/****************************************************************************
- * idt_init (real-mode near call, 16-bit real-mode near return address)
- *
- * Initialise the IDT, called from init_librm.
- *
- * Parameters:
- * %eax : IDT base address
- *
- * Destroys %ax, %bx, and %di.
- ****************************************************************************
- */
- .section ".text16", "ax", @progbits
- .code16
- .globl idt_init
-idt_init:
- movl %eax, idt_base
- addl $idt, idt_base
-
- /* IDT entries are only fixed up once */
- movb idt_fixed, %al
- orb %al, %al
- jnz 2f
- movb $1, idt_fixed
-
- /* Shuffle IDT entries into the correct format */
- movb $(idt_length / 8), %al
- movw $idt, %bx
- or %al, %al
- jz 2f
-1:
- movw 2(%bx), %di
- xchg %di, 6(%bx)
- movw %di, 2(%bx)
- addw $8, %bx
- dec %al
- jnz 1b
-2:
- ret
-
-/****************************************************************************
* Interrupt handlers
****************************************************************************
*/
@@ -111,35 +20,35 @@ idt_init:
#define SIGSEGV 11
#define SIGSTKFLT 16
-int_divide_error:
+ .globl gdbmach_nocode_sigfpe
+gdbmach_nocode_sigfpe:
pushl $SIGFPE
- jmp do_interrupt
+ jmp gdbmach_interrupt
-int_debug_trap:
-int_breakpoint:
+ .globl gdbmach_nocode_sigtrap
+gdbmach_nocode_sigtrap:
pushl $SIGTRAP
- jmp do_interrupt
+ jmp gdbmach_interrupt
-int_overflow:
-int_bound_range_exceeded:
+ .globl gdbmach_nocode_sigstkflt
+gdbmach_nocode_sigstkflt:
pushl $SIGSTKFLT
- jmp do_interrupt
+ jmp gdbmach_interrupt
-int_invalid_opcode:
+ .globl gdbmach_nocode_sigill
+gdbmach_nocode_sigill:
pushl $SIGILL
- jmp do_interrupt
+ jmp gdbmach_interrupt
-int_double_fault:
+ .globl gdbmach_withcode_sigbus
+gdbmach_withcode_sigbus:
movl $SIGBUS, (%esp)
- jmp do_interrupt
+ jmp gdbmach_interrupt
-int_invalid_tss:
-int_segment_not_present:
-int_stack_segment_fault:
-int_general_protection:
-int_page_fault:
+ .globl gdbmach_withcode_sigsegv
+gdbmach_withcode_sigsegv:
movl $SIGSEGV, (%esp)
- jmp do_interrupt
+ jmp gdbmach_interrupt
/* When invoked, the stack contains: eflags, cs, eip, signo. */
#define IH_OFFSET_GDB_REGS ( 0 )
@@ -161,7 +70,7 @@ int_page_fault:
#define IH_OFFSET_FLUX_OLD_EFLAGS ( IH_OFFSET_OLD_EFLAGS - 40 )
#define IH_OFFSET_FLUX_OLD_EIP ( IH_OFFSET_OLD_EIP - 36 )
#define IH_OFFSET_FLUX_END ( IH_OFFSET_END - 20 )
-do_interrupt:
+gdbmach_interrupt:
/* Store CPU state in GDB register snapshot */
pushw $0
pushw %gs
@@ -187,25 +96,41 @@ do_interrupt:
pushl %ecx
pushl %eax
+ /* Switch to virtual addressing */
+ call _intr_to_virt
+
/* Call GDB stub exception handler */
pushl %esp
pushl (IH_OFFSET_SIGNO + 4)(%esp)
call gdbmach_handler
addl $8, %esp
+ /* Copy register snapshot to new stack and switch to new stack */
+ movl %esp, %esi
+ movl (IH_OFFSET_GDB_SEG_REGS + 4)(%esp), %eax
+ movl %eax, %es
+ movl (IH_OFFSET_GDB_REGS + 16)(%esp), %edi
+ subl $IH_OFFSET_END, %edi
+ movl $(IH_OFFSET_END / 4), %ecx
+ pushl %edi
+ ss rep movsl
+ popl %edi
+ movl %eax, %ss
+ movl %edi, %esp
+
/* Restore CPU state from GDB register snapshot */
popl %eax
popl %ecx
popl %edx
popl %ebx
- addl $4, %esp /* Changing ESP currently not supported */
+ popl %ebp /* Skip %esp: already loaded */
popl %ebp
popl %esi
popl %edi
popl IH_OFFSET_FLUX_OLD_EIP(%esp)
popl IH_OFFSET_FLUX_OLD_EFLAGS(%esp)
popl IH_OFFSET_FLUX_OLD_CS(%esp)
- popl %ss
+ popl %ds /* Skip %ss: already loaded */
popl %ds
popl %es
popl %fs
diff --git a/src/arch/i386/core/gdbmach.c b/src/arch/i386/core/gdbmach.c
index 4232c7553..4d6897f7d 100644
--- a/src/arch/i386/core/gdbmach.c
+++ b/src/arch/i386/core/gdbmach.c
@@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <assert.h>
#include <ipxe/uaccess.h>
#include <ipxe/gdbstub.h>
+#include <librm.h>
#include <gdbmach.h>
/** @file
@@ -150,3 +151,30 @@ __asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) {
gdbstub_handler ( signo, regs );
gdbmach_enable_hwbps();
}
+
+static void * gdbmach_interrupt_vectors[] = {
+ gdbmach_nocode_sigfpe, /* Divide by zero */
+ gdbmach_nocode_sigtrap, /* Debug trap */
+ NULL, /* Non-maskable interrupt */
+ gdbmach_nocode_sigtrap, /* Breakpoint */
+ gdbmach_nocode_sigstkflt, /* Overflow */
+ gdbmach_nocode_sigstkflt, /* Bound range exceeded */
+ gdbmach_nocode_sigill, /* Invalid opcode */
+ NULL, /* Device not available */
+ gdbmach_withcode_sigbus, /* Double fault */
+ NULL, /* Coprocessor segment overrun */
+ gdbmach_withcode_sigsegv, /* Invalid TSS */
+ gdbmach_withcode_sigsegv, /* Segment not present */
+ gdbmach_withcode_sigsegv, /* Stack segment fault */
+ gdbmach_withcode_sigsegv, /* General protection fault */
+ gdbmach_withcode_sigsegv, /* Page fault */
+};
+
+void gdbmach_init ( void ) {
+ unsigned int i;
+
+ for ( i = 0 ; i < ( sizeof ( gdbmach_interrupt_vectors ) /
+ sizeof ( gdbmach_interrupt_vectors[0] ) ) ; i++ ) {
+ set_interrupt_vector ( i, gdbmach_interrupt_vectors[i] );
+ }
+}
diff --git a/src/arch/i386/core/virtaddr.S b/src/arch/i386/core/virtaddr.S
index aae1e1edd..5e5d77352 100644
--- a/src/arch/i386/core/virtaddr.S
+++ b/src/arch/i386/core/virtaddr.S
@@ -36,6 +36,7 @@ _virt_to_phys:
addl %ebp, 12(%esp)
/* Switch to physical code segment */
+ cli
pushl $PHYSICAL_CS
leal 1f(%ebp), %eax
pushl %eax
@@ -44,10 +45,10 @@ _virt_to_phys:
/* Reload other segment registers and adjust %esp */
movl $PHYSICAL_DS, %eax
movl %eax, %ds
- movl %eax, %es
- movl %eax, %fs
+ movl %eax, %es
+ movl %eax, %fs
movl %eax, %gs
- movl %eax, %ss
+ movl %eax, %ss
addl %ebp, %esp
/* Restore registers and flags, and return */
@@ -64,9 +65,6 @@ _virt_to_phys:
* selectors. All other registers are preserved. Flags are
* preserved.
*
- * Note that this depends on the GDT already being correctly set up
- * (e.g. by a call to run_here()).
- *
* Parameters: none
* Returns: none
****************************************************************************
@@ -79,18 +77,19 @@ _phys_to_virt:
pushl %ebp
/* Switch to virtual code segment */
+ cli
ljmp $VIRTUAL_CS, $1f
-1:
+1:
/* Reload data segment registers */
movl $VIRTUAL_DS, %eax
movl %eax, %ds
- movl %eax, %es
- movl %eax, %fs
+ movl %eax, %es
+ movl %eax, %fs
movl %eax, %gs
/* Reload stack segment and adjust %esp */
movl virt_offset, %ebp
- movl %eax, %ss
+ movl %eax, %ss
subl %ebp, %esp
/* Change the return address to a virtual address */
@@ -101,3 +100,46 @@ _phys_to_virt:
popl %eax
popfl
ret
+
+/****************************************************************************
+ * _intr_to_virt (virtual code segment, virtual or physical stack segment)
+ *
+ * Switch from virtual code segment with either a virtual or physical
+ * stack segment to using virtual addressing. %esp is adjusted if
+ * necessary to a virtual value. Segment registers are set to virtual
+ * selectors. All other registers are preserved. Flags are
+ * preserved.
+ *
+ * Parameters: none
+ * Returns: none
+ ****************************************************************************
+ */
+ .globl _intr_to_virt
+_intr_to_virt:
+ /* Preserve registers and flags */
+ pushfl
+ pushl %eax
+ pushl %ebp
+
+ /* Check whether stack segment is physical or virtual */
+ movl %ss, %eax
+ cmpw $VIRTUAL_DS, %ax
+ movl $VIRTUAL_DS, %eax
+
+ /* Reload data segment registers */
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %fs
+ movl %eax, %gs
+
+ /* Reload stack segment and adjust %esp if necessary */
+ je 1f
+ movl virt_offset, %ebp
+ movl %eax, %ss
+ subl %ebp, %esp
+1:
+ /* Restore registers and flags, and return */
+ popl %ebp
+ popl %eax
+ popfl
+ ret