diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index 311748bec..597c65e6a 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -376,6 +376,50 @@ struct interrupt_vector { /** "jmp" instruction */ #define JMP_INSN 0xe9 +/** 32-bit interrupt wrapper stack frame */ +struct interrupt_frame32 { + uint32_t esp; + uint32_t ss; + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + uint32_t ebp; + uint32_t edi; + uint32_t esi; + uint32_t edx; + uint32_t ecx; + uint32_t ebx; + uint32_t eax; + uint32_t eip; + uint32_t cs; + uint32_t eflags; +} __attribute__ (( packed )); + +/** 64-bit interrupt wrapper stack frame */ +struct interrupt_frame64 { + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rbp; + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + uint64_t rcx; + uint64_t rbx; + uint64_t rax; + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t rsp; + uint64_t ss; +} __attribute__ (( packed )); + extern void set_interrupt_vector ( unsigned int intr, void *vector ); /** A page table */ diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index c31daad84..9d3eff954 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -1363,20 +1363,19 @@ flatten_dummy: .code32 .globl interrupt_wrapper interrupt_wrapper: - /* Preserve registers (excluding already-saved %eax and - * otherwise unused registers which are callee-save for both - * 32-bit and 64-bit ABIs). - */ + /* Preserve registers (excluding already-saved %eax) */ pushl %ebx pushl %ecx pushl %edx pushl %esi pushl %edi + pushl %ebp /* Expand IRQ number to whole %eax register */ movzbl %al, %eax .if64 ; /* Skip transition to long mode, if applicable */ + xorl %edx, %edx movw %cs, %bx cmpw $LONG_CS, %bx je 1f @@ -1391,24 +1390,45 @@ interrupt_wrapper: /* Switch to virtual addressing */ call intr_to_prot + + /* Pass 32-bit interrupt frame pointer in %edx */ + movl %esp, %edx + xorl %ecx, %ecx .if64 /* Switch to long mode */ call prot_to_long .code64 -1: /* Preserve long-mode caller-save registers */ +1: /* Preserve long-mode registers */ pushq %r8 pushq %r9 pushq %r10 pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 /* Expand IRQ number to whole %rdi register */ movl %eax, %edi + + /* Pass 32-bit interrupt frame pointer (if applicable) in %rsi */ + testl %edx, %edx + je 1f + movl %edx, %esi + addl virt_offset, %esi +1: + /* Pass 64-bit interrupt frame pointer in %rdx */ + movq %rsp, %rdx .endif /* Call interrupt handler */ call interrupt .if64 - /* Restore long-mode caller-save registers */ + /* Restore long-mode registers */ + popq %r15 + popq %r14 + popq %r13 + popq %r12 popq %r11 popq %r10 popq %r9 @@ -1432,6 +1452,7 @@ interrupt_wrapper: popl %ds 1: /* Restore registers */ + popl %ebp popl %edi popl %esi popl %edx diff --git a/src/arch/x86/transitions/librm_mgmt.c b/src/arch/x86/transitions/librm_mgmt.c index 8144e7671..f9e1d261a 100644 --- a/src/arch/x86/transitions/librm_mgmt.c +++ b/src/arch/x86/transitions/librm_mgmt.c @@ -13,6 +13,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include /* * This file provides functions for managing librm. @@ -43,6 +44,9 @@ struct idtr64 idtr64 = { .limit = ( sizeof ( idt64 ) - 1 ), }; +/** Length of stack dump */ +#define STACK_DUMP_LEN 128 + /** Timer interrupt profiler */ static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" }; @@ -159,15 +163,101 @@ static struct profiler * interrupt_profiler ( int intr ) { } } +/** + * Display interrupt stack dump (for debugging) + * + * @v intr Interrupt number + * @v frame32 32-bit interrupt wrapper stack frame (or NULL) + * @v frame64 64-bit interrupt wrapper stack frame (or NULL) + */ +static __attribute__ (( unused )) void +interrupt_dump ( int intr, struct interrupt_frame32 *frame32, + struct interrupt_frame64 *frame64 ) { + unsigned long sp; + void *stack; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Print register dump */ + if ( ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) || frame32 ) { + sp = ( frame32->esp + sizeof ( *frame32 ) - + offsetof ( typeof ( *frame32 ), esp ) ); + DBGC ( &intr, "INT%d at %04x:%08x (stack %04x:%08lx):\n", + intr, frame32->cs, frame32->eip, frame32->ss, sp ); + DBGC ( &intr, "cs = %04x ds = %04x es = %04x fs = %04x " + "gs = %04x ss = %04x\n", frame32->cs, frame32->ds, + frame32->es, frame32->fs, frame32->gs, frame32->ss ); + DBGC ( &intr, "eax = %08x ebx = %08x ecx = %08x " + "edx = %08x flg = %08x\n", frame32->eax, frame32->ebx, + frame32->ecx, frame32->edx, frame32->eflags ); + DBGC ( &intr, "esi = %08x edi = %08x ebp = %08x " + "esp = %08lx eip = %08x\n", frame32->esi, frame32->edi, + frame32->ebp, sp, frame32->eip ); + stack = ( ( ( void * ) frame32 ) + sizeof ( *frame32 ) ); + } else { + DBGC ( &intr, "INT%d at %04llx:%016llx (stack " + "%04llx:%016llx):\n", intr, + ( ( unsigned long long ) frame64->cs ), + ( ( unsigned long long ) frame64->rip ), + ( ( unsigned long long ) frame64->ss ), + ( ( unsigned long long ) frame64->rsp ) ); + DBGC ( &intr, "rax = %016llx rbx = %016llx rcx = %016llx\n", + ( ( unsigned long long ) frame64->rax ), + ( ( unsigned long long ) frame64->rbx ), + ( ( unsigned long long ) frame64->rcx ) ); + DBGC ( &intr, "rdx = %016llx rsi = %016llx rdi = %016llx\n", + ( ( unsigned long long ) frame64->rdx ), + ( ( unsigned long long ) frame64->rsi ), + ( ( unsigned long long ) frame64->rdi ) ); + DBGC ( &intr, "rbp = %016llx rsp = %016llx flg = %016llx\n", + ( ( unsigned long long ) frame64->rbp ), + ( ( unsigned long long ) frame64->rsp ), + ( ( unsigned long long ) frame64->rflags ) ); + DBGC ( &intr, "r8 = %016llx r9 = %016llx r10 = %016llx\n", + ( ( unsigned long long ) frame64->r8 ), + ( ( unsigned long long ) frame64->r9 ), + ( ( unsigned long long ) frame64->r10 ) ); + DBGC ( &intr, "r11 = %016llx r12 = %016llx r13 = %016llx\n", + ( ( unsigned long long ) frame64->r11 ), + ( ( unsigned long long ) frame64->r12 ), + ( ( unsigned long long ) frame64->r13 ) ); + DBGC ( &intr, "r14 = %016llx r15 = %016llx\n", + ( ( unsigned long long ) frame64->r14 ), + ( ( unsigned long long ) frame64->r15 ) ); + sp = frame64->rsp; + stack = phys_to_virt ( sp ); + } + + /* Print stack dump */ + DBGC_HDA ( &intr, sp, stack, STACK_DUMP_LEN ); +} + /** * Interrupt handler * * @v intr Interrupt number + * @v frame32 32-bit interrupt wrapper stack frame (or NULL) + * @v frame64 64-bit interrupt wrapper stack frame (or NULL) + * @v frame Interrupt wrapper stack frame */ -void __attribute__ (( regparm ( 1 ) )) interrupt ( int intr ) { +void __attribute__ (( regparm ( 3 ) )) +interrupt ( int intr, struct interrupt_frame32 *frame32, + struct interrupt_frame64 *frame64 ) { struct profiler *profiler = interrupt_profiler ( intr ); uint32_t discard_eax; + /* Trap CPU exceptions if debugging is enabled. Note that we + * cannot treat INT8+ as exceptions, since we are not + * permitted to rebase the PIC. + */ + if ( DBG_LOG && ( intr < IRQ_INT ( 0 ) ) ) { + interrupt_dump ( intr, frame32, frame64 ); + DBG ( "CPU exception: dropping to emergency shell\n" ); + shell(); + } + /* Reissue interrupt in real mode */ profile_start ( profiler ); __asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t"