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/core | |
| 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/core')
| -rw-r--r-- | src/arch/i386/core/gdbidt.S | 151 | ||||
| -rw-r--r-- | src/arch/i386/core/gdbmach.c | 28 | ||||
| -rw-r--r-- | src/arch/i386/core/virtaddr.S | 62 |
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 |
