summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/arch/x86/include/librm.h44
-rw-r--r--src/arch/x86/transitions/librm.S33
-rw-r--r--src/arch/x86/transitions/librm_mgmt.c92
3 files changed, 162 insertions, 7 deletions
diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h
index 311748be..597c65e6 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 c31daad8..9d3eff95 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 8144e767..f9e1d261 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 <ipxe/profile.h>
#include <realmode.h>
#include <pic8259.h>
+#include <ipxe/shell.h>
/*
* 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" };
@@ -160,14 +164,100 @@ 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"