/* * Copyright (c) 2012-2014 Richard Braun. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * * XXX Many traps have not been tested. Some, such as NMIs, are known to need * additional configuration and resources to be properly handled. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct trap_cpu_data { alignas(CPU_DATA_ALIGN) unsigned char intr_stack[TRAP_STACK_SIZE]; }; static struct trap_cpu_data trap_cpu_data __percpu; /* * Type for interrupt service routines and trap handler functions. */ typedef void (*trap_isr_fn_t)(void); /* * Trap handler flags. */ #define TRAP_HF_INTR 0x1 /* Enter interrupt context */ /* * Properties of a trap handler. */ struct trap_handler { int flags; trap_handler_fn_t fn; }; /* * Table of ISR addresses. */ extern trap_isr_fn_t trap_isr_table[CPU_IDT_SIZE]; /* * Array of trap handlers. */ static struct trap_handler trap_handlers[CPU_IDT_SIZE] __read_mostly; /* * Global trap lock. * * This lock is currently only used to serialize concurrent trap handler * updates. * * Interrupts must be disabled when holding this lock. */ static struct spinlock trap_lock; static struct trap_handler * trap_handler_get(unsigned int vector) { assert(vector < ARRAY_SIZE(trap_handlers)); return &trap_handlers[vector]; } static void __init trap_handler_init(struct trap_handler *handler, int flags, trap_handler_fn_t fn) { handler->flags = flags; atomic_store(&handler->fn, fn, ATOMIC_RELAXED); } static void __init trap_install(unsigned int vector, int flags, trap_handler_fn_t fn) { assert(vector < ARRAY_SIZE(trap_handlers)); trap_handler_init(trap_handler_get(vector), flags, fn); } static void trap_show_thread(void) { struct thread *thread; thread = thread_self(); printf("trap: interrupted thread: %p (%s)\n", thread, thread->name); } static void trap_double_fault(struct trap_frame *frame) { cpu_halt_broadcast(); #ifndef __LP64__ struct trap_frame frame_store; struct cpu *cpu; /* * Double faults are catched through a task gate, which makes the given * frame useless. The interrupted state is automatically saved in the * main TSS by the processor. Build a proper trap frame from there. */ frame = &frame_store; cpu = cpu_current(); frame->eax = cpu->tss.eax; frame->ebx = cpu->tss.ebx; frame->ecx = cpu->tss.ecx; frame->edx = cpu->tss.edx; frame->ebp = cpu->tss.ebp; frame->esi = cpu->tss.esi; frame->edi = cpu->tss.edi; frame->ds = cpu->tss.ds; frame->es = cpu->tss.es; frame->fs = cpu->tss.fs; frame->gs = cpu->tss.gs; frame->vector = TRAP_DF; frame->error = 0; frame->eip = cpu->tss.eip; frame->cs = cpu->tss.cs; frame->eflags = cpu->tss.eflags; frame->esp = cpu->tss.esp; frame->ss = cpu->tss.ss; #endif /* __LP64__ */ printf("trap: double fault (cpu%u):\n", cpu_id()); trap_show_thread(); trap_frame_show(frame); trap_stack_show(frame); cpu_halt(); } static void __init trap_install_double_fault(void) { trap_install(TRAP_DF, TRAP_HF_INTR, trap_double_fault); cpu_idt_set_double_fault(trap_isr_table[TRAP_DF]); } static void trap_default(struct trap_frame *frame) { cpu_halt_broadcast(); printf("trap: unhandled interrupt or exception (cpu%u):\n", cpu_id()); trap_show_thread(); trap_frame_show(frame); trap_stack_show(frame); cpu_halt(); } static int __init trap_setup(void) { size_t i; spinlock_init(&trap_lock); for (i = 0; i < ARRAY_SIZE(trap_isr_table); i++) { cpu_idt_set_gate(i, trap_isr_table[i]); } for (i = 0; i < ARRAY_SIZE(trap_handlers); i++) { trap_install(i, TRAP_HF_INTR, trap_default); } /* Architecture defined traps */ trap_install(TRAP_DE, 0, trap_default); trap_install(TRAP_DB, 0, trap_default); trap_install(TRAP_NMI, TRAP_HF_INTR, trap_default); trap_install(TRAP_BP, 0, trap_default); trap_install(TRAP_OF, 0, trap_default); trap_install(TRAP_BR, 0, trap_default); trap_install(TRAP_UD, 0, trap_default); trap_install(TRAP_NM, 0, trap_default); trap_install_double_fault(); trap_install(TRAP_TS, 0, trap_default); trap_install(TRAP_NP, 0, trap_default); trap_install(TRAP_SS, 0, trap_default); trap_install(TRAP_GP, 0, trap_default); trap_install(TRAP_PF, 0, trap_default); trap_install(TRAP_MF, 0, trap_default); trap_install(TRAP_AC, 0, trap_default); trap_install(TRAP_MC, TRAP_HF_INTR, trap_default); trap_install(TRAP_XM, 0, trap_default); /* System defined traps */ trap_install(TRAP_XCALL, TRAP_HF_INTR, cpu_xcall_intr); trap_install(TRAP_THREAD_SCHEDULE, TRAP_HF_INTR, cpu_thread_schedule_intr); trap_install(TRAP_CPU_HALT, TRAP_HF_INTR, cpu_halt_intr); trap_install(TRAP_LAPIC_PMC_OF, TRAP_HF_INTR, trap_default); trap_install(TRAP_LAPIC_TIMER, TRAP_HF_INTR, lapic_timer_intr); trap_install(TRAP_LAPIC_ERROR, TRAP_HF_INTR, lapic_error_intr); trap_install(TRAP_LAPIC_SPURIOUS, TRAP_HF_INTR, lapic_spurious_intr); return 0; } INIT_OP_DEFINE(trap_setup); void trap_main(struct trap_frame *frame) { struct trap_handler *handler; trap_handler_fn_t fn; assert(!cpu_intr_enabled()); handler = trap_handler_get(frame->vector); if (handler->flags & TRAP_HF_INTR) { thread_intr_enter(); } fn = atomic_load(&handler->fn, ATOMIC_RELAXED); fn(frame); if (handler->flags & TRAP_HF_INTR) { thread_intr_leave(); } assert(!cpu_intr_enabled()); } void trap_register(unsigned int vector, trap_handler_fn_t handler_fn) { unsigned long flags; spinlock_lock_intr_save(&trap_lock, &flags); trap_install(vector, TRAP_HF_INTR, handler_fn); spinlock_unlock_intr_restore(&trap_lock, flags); } #ifdef __LP64__ void trap_frame_show(struct trap_frame *frame) { printf("trap: rax: %016lx rbx: %016lx rcx: %016lx\n" "trap: rdx: %016lx rbp: %016lx rsi: %016lx\n" "trap: rdi: %016lx r8: %016lx r9: %016lx\n" "trap: r10: %016lx r11: %016lx r12: %016lx\n" "trap: r13: %016lx r14: %016lx r15: %016lx\n" "trap: vector: %lu error: %08lx\n" "trap: rip: %016lx cs: %lu rflags: %016lx\n" "trap: rsp: %016lx ss: %lu\n", (unsigned long)frame->rax, (unsigned long)frame->rbx, (unsigned long)frame->rcx, (unsigned long)frame->rdx, (unsigned long)frame->rbp, (unsigned long)frame->rsi, (unsigned long)frame->rdi, (unsigned long)frame->r8, (unsigned long)frame->r9, (unsigned long)frame->r10, (unsigned long)frame->r11, (unsigned long)frame->r12, (unsigned long)frame->r13, (unsigned long)frame->r14, (unsigned long)frame->r15, (unsigned long)frame->vector, (unsigned long)frame->error, (unsigned long)frame->rip, (unsigned long)frame->cs, (unsigned long)frame->rflags, (unsigned long)frame->rsp, (unsigned long)frame->ss); /* XXX Until the page fault handler is written */ if (frame->vector == 14) { printf("trap: cr2: %016lx\n", (unsigned long)cpu_get_cr2()); } } #else /* __LP64__ */ void trap_frame_show(struct trap_frame *frame) { unsigned long esp, ss; if ((frame->cs & CPU_PL_USER) || (frame->vector == TRAP_DF)) { esp = frame->esp; ss = frame->ss; } else { esp = 0; ss = 0; } printf("trap: eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n" "trap: ebp: %08lx esi: %08lx edi: %08lx\n" "trap: ds: %hu es: %hu fs: %hu gs: %hu\n" "trap: vector: %lu error: %08lx\n" "trap: eip: %08lx cs: %lu eflags: %08lx\n" "trap: esp: %08lx ss: %lu\n", (unsigned long)frame->eax, (unsigned long)frame->ebx, (unsigned long)frame->ecx, (unsigned long)frame->edx, (unsigned long)frame->ebp, (unsigned long)frame->esi, (unsigned long)frame->edi, (unsigned short)frame->ds, (unsigned short)frame->es, (unsigned short)frame->fs, (unsigned short)frame->gs, (unsigned long)frame->vector, (unsigned long)frame->error, (unsigned long)frame->eip, (unsigned long)frame->cs, (unsigned long)frame->eflags, (unsigned long)esp, (unsigned long)ss); /* XXX Until the page fault handler is written */ if (frame->vector == 14) { printf("trap: cr2: %08lx\n", (unsigned long)cpu_get_cr2()); } } #endif /* __LP64__ */ void trap_stack_show(struct trap_frame *frame) { #ifdef __LP64__ strace_show(frame->rip, frame->rbp); #else /* __LP64__ */ strace_show(frame->eip, frame->ebp); #endif /* __LP64__ */ } void * trap_get_interrupt_stack(const struct trap_frame *frame) { struct trap_cpu_data *cpu_data; struct trap_handler *handler; handler = trap_handler_get(frame->vector); if ((handler->flags & TRAP_HF_INTR) && !thread_interrupted()) { cpu_data = cpu_local_ptr(trap_cpu_data); return cpu_data->intr_stack + sizeof(cpu_data->intr_stack); } else { return NULL; } }