From a133a7717e380eb90bce8c23198356471406b64e Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Fri, 31 Jan 2014 13:19:27 +0100 Subject: [PATCH] Refactor i386 and x86_64 interrupt support. --- kernel/Makefile | 2 + kernel/include/sortix/kernel/interrupt.h | 120 ++++---- kernel/include/sortix/kernel/scheduler.h | 14 +- kernel/include/sortix/kernel/signal.h | 2 +- kernel/include/sortix/kernel/syscall.h | 4 +- kernel/interrupt.cpp | 285 +----------------- kernel/scheduler.cpp | 16 +- kernel/signal.cpp | 6 +- kernel/x64/interrupt.S | 62 ---- kernel/x64/syscall.S | 7 +- kernel/x86-family/idt.cpp | 69 +++-- kernel/x86-family/idt.h | 32 +- kernel/x86-family/interrupt.cpp | 355 +++++++++++++++++++++++ kernel/x86-family/pic.cpp | 122 ++++++++ kernel/x86-family/pic.h | 43 +++ kernel/x86/interrupt.S | 62 ---- kernel/x86/syscall.S | 7 +- 17 files changed, 655 insertions(+), 553 deletions(-) create mode 100644 kernel/x86-family/interrupt.cpp create mode 100644 kernel/x86-family/pic.cpp create mode 100644 kernel/x86-family/pic.h diff --git a/kernel/Makefile b/kernel/Makefile index 24128bbd..f97c4ca8 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -45,6 +45,8 @@ ifdef X86FAMILY $(CPU)/memorymanagement.o \ x86-family/memorymanagement.o \ $(CPU)/interrupt.o \ + x86-family/interrupt.o \ + x86-family/pic.o \ x86-family/gdt.o \ x86-family/idt.o \ $(CPU)/syscall.o \ diff --git a/kernel/include/sortix/kernel/interrupt.h b/kernel/include/sortix/kernel/interrupt.h index 5247c5f1..c6b681d0 100644 --- a/kernel/include/sortix/kernel/interrupt.h +++ b/kernel/include/sortix/kernel/interrupt.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. @@ -18,7 +18,7 @@ Sortix. If not, see . sortix/kernel/interrupt.h - High level interrupt service routines and interrupt request handlers. + High level interrupt services. *******************************************************************************/ @@ -30,13 +30,17 @@ #include namespace Sortix { - namespace CPU { -struct InterruptRegisters; -} // namespace CPU +struct InterruptRegisters; + +} // namespace CPU +} // namespace Sortix + +namespace Sortix { namespace Interrupt { +#if defined(__i386__) || defined(__x86_64__) const unsigned int IRQ0 = 32; const unsigned int IRQ1 = 33; const unsigned int IRQ2 = 34; @@ -53,27 +57,60 @@ const unsigned int IRQ12 = 44; const unsigned int IRQ13 = 45; const unsigned int IRQ14 = 46; const unsigned int IRQ15 = 47; +#endif -extern "C" unsigned long asm_interrupts_are_enabled(); extern "C" unsigned long asm_is_cpu_interrupted; -inline bool IsEnabled() { return asm_interrupts_are_enabled(); } -inline void Enable() { asm volatile("sti"); } -inline void Disable() { asm volatile("cli"); } -inline bool IsCPUInterrupted() { return asm_is_cpu_interrupted != 0; } -inline bool SetEnabled(bool isenabled) +inline bool IsEnabled() +{ +#if defined(__i386__) || defined(__x86_64__) + unsigned long is_enabled; + asm("pushf\t\n" + "pop %0\t\n" + "and $0x000200, %0" : "=r"(is_enabled)); + return is_enabled != 0; +#else +#warning "You need to implement checking if interrupts are on" +#endif +} + +inline void Enable() +{ +#if defined(__i386__) || defined(__x86_64__) + asm volatile("sti"); +#else +#warning "You need to implement enabling interrupts" +#endif +} + +inline void Disable() +{ +#if defined(__i386__) || defined(__x86_64__) + asm volatile("cli"); +#else +#warning "You need to implement disabling interrupts" +#endif +} + +inline bool IsCPUInterrupted() +{ + return asm_is_cpu_interrupted != 0; +} + +inline bool SetEnabled(bool is_enabled) { bool wasenabled = IsEnabled(); - if ( isenabled ) { Enable(); } else { Disable(); } + if ( is_enabled ) + Enable(); + else + Disable(); return wasenabled; } + typedef void (*Handler)(CPU::InterruptRegisters* regs, void* user); void RegisterHandler(unsigned int index, Handler handler, void* user); -typedef void (*RawHandler)(void); -void RegisterRawHandler(unsigned int index, RawHandler handler, bool userspace); - void Init(); void InitWorker(); void WorkerThread(void* user); @@ -84,57 +121,4 @@ bool ScheduleWork(WorkHandler handler, void* payload, size_t payloadsize); } // namespace Interrupt } // namespace Sortix -extern "C" void isr0(); -extern "C" void isr1(); -extern "C" void isr2(); -extern "C" void isr3(); -extern "C" void isr4(); -extern "C" void isr5(); -extern "C" void isr6(); -extern "C" void isr7(); -extern "C" void isr8(); -extern "C" void isr9(); -extern "C" void isr10(); -extern "C" void isr11(); -extern "C" void isr12(); -extern "C" void isr13(); -extern "C" void isr14(); -extern "C" void isr15(); -extern "C" void isr16(); -extern "C" void isr17(); -extern "C" void isr18(); -extern "C" void isr19(); -extern "C" void isr20(); -extern "C" void isr21(); -extern "C" void isr22(); -extern "C" void isr23(); -extern "C" void isr24(); -extern "C" void isr25(); -extern "C" void isr26(); -extern "C" void isr27(); -extern "C" void isr28(); -extern "C" void isr29(); -extern "C" void isr30(); -extern "C" void isr31(); -extern "C" void isr128(); -extern "C" void isr130(); -extern "C" void isr131(); -extern "C" void irq0(); -extern "C" void irq1(); -extern "C" void irq2(); -extern "C" void irq3(); -extern "C" void irq4(); -extern "C" void irq5(); -extern "C" void irq6(); -extern "C" void irq7(); -extern "C" void irq8(); -extern "C" void irq9(); -extern "C" void irq10(); -extern "C" void irq11(); -extern "C" void irq12(); -extern "C" void irq13(); -extern "C" void irq14(); -extern "C" void irq15(); -extern "C" void interrupt_handler_null(); - #endif diff --git a/kernel/include/sortix/kernel/scheduler.h b/kernel/include/sortix/kernel/scheduler.h index f4972e77..7475e688 100644 --- a/kernel/include/sortix/kernel/scheduler.h +++ b/kernel/include/sortix/kernel/scheduler.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. @@ -28,20 +28,26 @@ #include namespace Sortix { - class Process; class Thread; +} // namespace Sortix +namespace Sortix { namespace CPU { struct InterruptRegisters; } // namespace CPU +} // namespace Sortix +namespace Sortix { enum ThreadState { NONE, RUNNABLE, BLOCKING, DEAD }; +} // namespace Sortix +namespace Sortix { namespace Scheduler { void Init(); void Switch(CPU::InterruptRegisters* regs); +#if defined(__i386__) || defined(__x86_64__) inline static void Yield() { asm volatile ("int $129"); } __attribute__ ((noreturn)) inline static void ExitThread() @@ -49,6 +55,7 @@ inline static void ExitThread() asm volatile ("int $132"); __builtin_unreachable(); } +#endif void SetThreadState(Thread* thread, ThreadState state); ThreadState GetThreadState(Thread* thread); void SetIdleThread(Thread* thread); @@ -56,9 +63,10 @@ void SetDummyThreadOwner(Process* process); void SetInitProcess(Process* init); Process* GetInitProcess(); Process* GetKernelProcess(); +void InterruptYieldCPU(CPU::InterruptRegisters* regs, void* user); +void ThreadExitCPU(CPU::InterruptRegisters* regs, void* user); } // namespace Scheduler - } // namespace Sortix #endif diff --git a/kernel/include/sortix/kernel/signal.h b/kernel/include/sortix/kernel/signal.h index a70d4f00..98c05fbc 100644 --- a/kernel/include/sortix/kernel/signal.h +++ b/kernel/include/sortix/kernel/signal.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. diff --git a/kernel/include/sortix/kernel/syscall.h b/kernel/include/sortix/kernel/syscall.h index fc7976cc..dfe06f13 100644 --- a/kernel/include/sortix/kernel/syscall.h +++ b/kernel/include/sortix/kernel/syscall.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. @@ -38,6 +38,4 @@ void Register(size_t index, void* funcptr); } // namespace Syscall } // namespace Sortix -extern "C" void syscall_handler(); - #endif diff --git a/kernel/interrupt.cpp b/kernel/interrupt.cpp index 4cc1d53f..4fc28f89 100644 --- a/kernel/interrupt.cpp +++ b/kernel/interrupt.cpp @@ -18,7 +18,7 @@ Sortix. If not, see . interrupt.cpp - High level interrupt service routines and interrupt request handlers. + High level interrupt services. *******************************************************************************/ @@ -37,292 +37,9 @@ #include #include -#include "x86-family/idt.h" - namespace Sortix { - namespace Interrupt { -const uint16_t PIC_MASTER = 0x20; -const uint16_t PIC_SLAVE = 0xA0; -const uint16_t PIC_COMMAND = 0x00; -const uint16_t PIC_DATA = 0x01; -const uint8_t PIC_CMD_ENDINTR = 0x20; -const uint8_t PIC_ICW1_ICW4 = 0x01; // ICW4 (not) needed -const uint8_t PIC_ICW1_SINGLE = 0x02; // Single (cascade) mode -const uint8_t PIC_ICW1_INTERVAL4 = 0x04; // Call address interval 4 (8) -const uint8_t PIC_ICW1_LEVEL = 0x08; // Level triggered (edge) mode -const uint8_t PIC_CMD_INIT = 0x10; -const uint8_t PIC_MODE_8086 = 0x01; // 8086/88 (MCS-80/85) mode -const uint8_t PIC_MODE_AUTO = 0x02; // Auto (normal) EOI -const uint8_t PIC_MODE_BUF_SLAVE = 0x08; // Buffered mode/slave -const uint8_t PIC_MODE_BUF_MASTER = 0x0C; // Buffered mode/master -const uint8_t PIC_MODE_SFNM = 0x10; // Special fully nested (not) - -extern "C" { unsigned long asm_is_cpu_interrupted = 0; } -const bool DEBUG_EXCEPTION = true; -const bool DEBUG_IRQ = false; -const bool DEBUG_ISR = false; -const bool CALLTRACE_KERNEL = false; -const bool CALLTRACE_USER = false; -const bool RUN_DEBUGGER_ON_CRASH = false; - -const size_t NUM_KNOWN_EXCEPTIONS = 20; -const char* exceptions[] = -{ - "Divide by zero", /* 0, 0x0 */ - "Debug", /* 1, 0x1 */ - "Non maskable interrupt", /* 2, 0x2 */ - "Breakpoint", /* 3, 0x3 */ - "Into detected overflow", /* 4, 0x4 */ - "Out of bounds", /* 5, 0x5 */ - "Invalid opcode", /* 6, 0x6 */ - "No coprocessor", /* 7, 0x7 */ - "Double fault", /* 8, 0x8 */ - "Coprocessor segment overrun", /* 9, 0x9 */ - "Bad TSS", /* 10, 0xA */ - "Segment not present", /* 11, 0xB */ - "Stack fault", /* 12, 0xC */ - "General protection fault", /* 13, 0xD */ - "Page fault", /* 14, 0xE */ - "Unknown interrupt", /* 15, 0xF */ - "Coprocessor fault", /* 16, 0x10 */ - "Alignment check", /* 17, 0x11 */ - "Machine check", /* 18, 0x12 */ - "SIMD Floating-Point", /* 19, 0x13 */ -}; - -const unsigned int NUM_INTERRUPTS = 256UL; -static Handler interrupt_handlers[NUM_INTERRUPTS]; -static void* interrupt_handler_params[NUM_INTERRUPTS]; - -extern "C" void ReprogramPIC() -{ - uint8_t master_mask = 0; - uint8_t slave_mask = 0; - CPU::OutPortB(PIC_MASTER + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4); - CPU::OutPortB(PIC_SLAVE + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4); - CPU::OutPortB(PIC_MASTER + PIC_DATA, IRQ0); - CPU::OutPortB(PIC_SLAVE + PIC_DATA, IRQ8); - CPU::OutPortB(PIC_MASTER + PIC_DATA, 0x04); // Slave PIC at IRQ2 - CPU::OutPortB(PIC_SLAVE + PIC_DATA, 0x02); // Cascade Identity - CPU::OutPortB(PIC_MASTER + PIC_DATA, PIC_MODE_8086); - CPU::OutPortB(PIC_SLAVE + PIC_DATA, PIC_MODE_8086); - CPU::OutPortB(PIC_MASTER + PIC_DATA, master_mask); - CPU::OutPortB(PIC_SLAVE + PIC_DATA, slave_mask); -} - -extern "C" void DeprogramPIC() -{ - uint8_t master_mask = 0; - uint8_t slave_mask = 0; - CPU::OutPortB(PIC_MASTER + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4); - CPU::OutPortB(PIC_SLAVE + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4); - CPU::OutPortB(PIC_MASTER + PIC_DATA, 0x08); - CPU::OutPortB(PIC_SLAVE + PIC_DATA, 0x70); - CPU::OutPortB(PIC_MASTER + PIC_DATA, 0x04); // Slave PIC at IRQ2 - CPU::OutPortB(PIC_SLAVE + PIC_DATA, 0x02); // Cascade Identity - CPU::OutPortB(PIC_MASTER + PIC_DATA, PIC_MODE_8086); - CPU::OutPortB(PIC_SLAVE + PIC_DATA, PIC_MODE_8086); - CPU::OutPortB(PIC_MASTER + PIC_DATA, master_mask); - CPU::OutPortB(PIC_SLAVE + PIC_DATA, slave_mask); -} - -void Init() -{ - IDT::Init(); - - for ( unsigned int i = 0; i < NUM_INTERRUPTS; i++ ) - { - interrupt_handlers[i] = NULL; - interrupt_handler_params[i] = NULL; - RegisterRawHandler(i, interrupt_handler_null, false); - } - - // Remap the IRQ table on the PICs. - ReprogramPIC(); - - RegisterRawHandler(0, isr0, false); - RegisterRawHandler(1, isr1, false); - RegisterRawHandler(2, isr2, false); - RegisterRawHandler(3, isr3, false); - RegisterRawHandler(4, isr4, false); - RegisterRawHandler(5, isr5, false); - RegisterRawHandler(6, isr6, false); - RegisterRawHandler(7, isr7, false); - RegisterRawHandler(8, isr8, false); - RegisterRawHandler(9, isr9, false); - RegisterRawHandler(10, isr10, false); - RegisterRawHandler(11, isr11, false); - RegisterRawHandler(12, isr12, false); - RegisterRawHandler(13, isr13, false); - RegisterRawHandler(14, isr14, false); - RegisterRawHandler(15, isr15, false); - RegisterRawHandler(16, isr16, false); - RegisterRawHandler(17, isr17, false); - RegisterRawHandler(18, isr18, false); - RegisterRawHandler(19, isr19, false); - RegisterRawHandler(20, isr20, false); - RegisterRawHandler(21, isr21, false); - RegisterRawHandler(22, isr22, false); - RegisterRawHandler(23, isr23, false); - RegisterRawHandler(24, isr24, false); - RegisterRawHandler(25, isr25, false); - RegisterRawHandler(26, isr26, false); - RegisterRawHandler(27, isr27, false); - RegisterRawHandler(28, isr28, false); - RegisterRawHandler(29, isr29, false); - RegisterRawHandler(30, isr30, false); - RegisterRawHandler(31, isr31, false); - RegisterRawHandler(32, irq0, false); - RegisterRawHandler(33, irq1, false); - RegisterRawHandler(34, irq2, false); - RegisterRawHandler(35, irq3, false); - RegisterRawHandler(36, irq4, false); - RegisterRawHandler(37, irq5, false); - RegisterRawHandler(38, irq6, false); - RegisterRawHandler(39, irq7, false); - RegisterRawHandler(40, irq8, false); - RegisterRawHandler(41, irq9, false); - RegisterRawHandler(42, irq10, false); - RegisterRawHandler(43, irq11, false); - RegisterRawHandler(44, irq12, false); - RegisterRawHandler(45, irq13, false); - RegisterRawHandler(46, irq14, false); - RegisterRawHandler(47, irq15, false); - - // TODO: Let the syscall.cpp code register this. - RegisterRawHandler(128, syscall_handler, true); - - Interrupt::Enable(); -} - -void RegisterHandler(unsigned int index, Interrupt::Handler handler, void* user) -{ - interrupt_handlers[index] = handler; - interrupt_handler_params[index] = user; -} - -// TODO: This function contains magic IDT-related values! -void RegisterRawHandler(unsigned int index, RawHandler handler, bool userspace) -{ - addr_t handler_entry = (addr_t) handler; - uint16_t sel = KCS; - uint8_t flags = 0x8E; - if ( userspace ) - flags |= 0x60; - IDT::SetEntry(index, handler_entry, sel, flags); -} - -void CrashHandler(CPU::InterruptRegisters* regs) -{ - CurrentThread()->SaveRegisters(regs); - - const char* message = regs->int_no < NUM_KNOWN_EXCEPTIONS - ? exceptions[regs->int_no] : "Unknown"; - - if ( DEBUG_EXCEPTION ) - { - regs->LogRegisters(); - Log::Print("\n"); - } - -#if defined(__x86_64__) - addr_t ip = regs->rip; -#elif defined(__i386__) - addr_t ip = regs->eip; -#endif - - // Halt and catch fire if we are the kernel. - unsigned code_mode = regs->cs & 0x3; - bool is_in_kernel = !code_mode; - bool is_in_user = !is_in_kernel; - - if ( (is_in_kernel && CALLTRACE_KERNEL) || (is_in_user && CALLTRACE_USER) ) - #if defined(__x86_64__) - Calltrace::Perform(regs->rbp); - #elif defined(__i386__) - Calltrace::Perform(regs->ebp); - #else - #error Please provide a calltrace implementation for your CPU. - #endif - - if ( RUN_DEBUGGER_ON_CRASH ) - Debugger::Run(); - - if ( is_in_kernel ) - { - PanicF("Unhandled CPU Exception id %zu '%s' at ip=0x%zx " - "(cr2=0x%p, err_code=0x%p)", regs->int_no, message, - ip, regs->cr2, regs->err_code); - } - - Interrupt::Enable(); - - Log::PrintF("The current program (pid %ji %s) has crashed and was terminated:\n", - (intmax_t) CurrentProcess()->pid, CurrentProcess()->program_image_path); - Log::PrintF("%s exception at ip=0x%zx (cr2=0x%p, err_code=0x%p)\n", - message, ip, regs->cr2, regs->err_code); - - // Exit the process with the right error code. - // TODO: Sent a SIGINT, SIGBUS, or whatever instead. - CurrentProcess()->Exit(139); - - Interrupt::Disable(); - Signal::Dispatch(regs); -} - -void ISRHandler(CPU::InterruptRegisters* regs) -{ - unsigned int int_no = regs->int_no; - - if ( DEBUG_ISR ) - { - Log::PrintF("ISR%u ", int_no); - regs->LogRegisters(); - Log::Print("\n"); - } - - // Run the desired interrupt handler. - if ( int_no < 32 && int_no != 7 ) - CrashHandler(regs); - else if ( interrupt_handlers[regs->int_no] != NULL ) - interrupt_handlers[int_no](regs, interrupt_handler_params[int_no]); -} - -void IRQHandler(CPU::InterruptRegisters* regs) -{ - // TODO: IRQ 7 and 15 might be spurious and might need to be ignored. - // See http://wiki.osdev.org/PIC for details (section Spurious IRQs). - if ( regs->int_no == 32 + 7 || regs->int_no == 32 + 15 ) - return; - - if ( DEBUG_IRQ ) - { - Log::PrintF("IRQ%u ", regs->int_no-32); - regs->LogRegisters(); - Log::Print("\n"); - } - - unsigned int int_no = regs->int_no; - - // Send an EOI (end of interrupt) signal to the PICs. - if ( IRQ8 <= int_no ) - CPU::OutPortB(PIC_SLAVE, PIC_CMD_ENDINTR); - CPU::OutPortB(PIC_MASTER, PIC_CMD_ENDINTR); - - if ( interrupt_handlers[int_no] ) - interrupt_handlers[int_no](regs, interrupt_handler_params[int_no]); -} - -extern "C" void interrupt_handler(CPU::InterruptRegisters* regs) -{ - if ( 32 <= regs->int_no && regs->int_no < 48 ) - IRQHandler(regs); - else - ISRHandler(regs); -} - // TODO: This implementation is a bit hacky and can be optimized. uint8_t* queue; diff --git a/kernel/scheduler.cpp b/kernel/scheduler.cpp index feee120b..e72331bc 100644 --- a/kernel/scheduler.cpp +++ b/kernel/scheduler.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. @@ -187,12 +187,12 @@ void LogEndSwitch(Thread* current, const CPU::InterruptRegisters* regs) } } -static void InterruptYieldCPU(CPU::InterruptRegisters* regs, void* /*user*/) +void InterruptYieldCPU(CPU::InterruptRegisters* regs, void* /*user*/) { Switch(regs); } -static void ThreadExitCPU(CPU::InterruptRegisters* regs, void* /*user*/) +void ThreadExitCPU(CPU::InterruptRegisters* regs, void* /*user*/) { // Can't use floating point instructions from now. Float::NofityTaskExit(currentthread); @@ -298,9 +298,6 @@ static int sys_usleep(size_t usecs) return 0; } -extern "C" void yield_cpu_handler(); -extern "C" void thread_exit_handler(); - void Init() { premagic = postmagic = SCHED_MAGIC; @@ -317,13 +314,6 @@ void Init() firstsleepingthread = NULL; idlethread = NULL; - // Register our raw handler with user-space access. It calls our real - // handler after common interrupt preparation stuff has occured. - Interrupt::RegisterRawHandler(129, yield_cpu_handler, true); - Interrupt::RegisterHandler(129, InterruptYieldCPU, NULL); - Interrupt::RegisterRawHandler(132, thread_exit_handler, true); - Interrupt::RegisterHandler(132, ThreadExitCPU, NULL); - Syscall::Register(SYSCALL_SLEEP, (void*) sys_sleep); Syscall::Register(SYSCALL_USLEEP, (void*) sys_usleep); } diff --git a/kernel/signal.cpp b/kernel/signal.cpp index 91f05358..bceb15d6 100644 --- a/kernel/signal.cpp +++ b/kernel/signal.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. @@ -127,10 +127,6 @@ void Return(CPU::InterruptRegisters* regs, void* /*user*/) void Init() { - Interrupt::RegisterRawHandler(130, isr130, true); - Interrupt::RegisterHandler(130, Dispatch, NULL); - Interrupt::RegisterRawHandler(131, isr131, true); - Interrupt::RegisterHandler(131, Return, NULL); } } // namespace Signal diff --git a/kernel/x64/interrupt.S b/kernel/x64/interrupt.S index 1df22946..72aed0fa 100644 --- a/kernel/x64/interrupt.S +++ b/kernel/x64/interrupt.S @@ -27,371 +27,318 @@ .global isr0 .type isr0, @function isr0: - cli pushq $0 # err_code pushq $0 # int_no jmp interrupt_handler_prepare .global isr1 .type isr1, @function isr1: - cli pushq $0 # err_code pushq $1 # int_no jmp interrupt_handler_prepare .global isr2 .type isr2, @function isr2: - cli pushq $0 # err_code pushq $2 # int_no jmp interrupt_handler_prepare .global isr3 .type isr3, @function isr3: - cli pushq $0 # err_code pushq $3 # int_no jmp interrupt_handler_prepare .global isr4 .type isr4, @function isr4: - cli pushq $0 # err_code pushq $4 # int_no jmp interrupt_handler_prepare .global isr5 .type isr5, @function isr5: - cli pushq $0 # err_code pushq $5 # int_no jmp interrupt_handler_prepare .global isr6 .type isr6, @function isr6: - cli pushq $0 # err_code pushq $6 # int_no jmp interrupt_handler_prepare .global isr7 .type isr7, @function isr7: - cli pushq $0 # err_code pushq $7 # int_no jmp interrupt_handler_prepare .global isr8 .type isr8, @function isr8: - cli # pushq $0 # err_code pushed by CPU pushq $8 # int_no jmp interrupt_handler_prepare .global isr9 .type isr9, @function isr9: - cli pushq $0 # err_code pushq $9 # int_no jmp interrupt_handler_prepare .global isr10 .type isr10, @function isr10: - cli # pushq $0 # err_code pushed by CPU pushq $10 # int_no jmp interrupt_handler_prepare .global isr11 .type isr11, @function isr11: - cli # pushq $0 # err_code pushed by CPU pushq $11 # int_no jmp interrupt_handler_prepare .global isr12 .type isr12, @function isr12: - cli # pushq $0 # err_code pushed by CPU pushq $12 # int_no jmp interrupt_handler_prepare .global isr13 .type isr13, @function isr13: - cli # pushq $0 # err_code pushed by CPU pushq $13 # int_no jmp interrupt_handler_prepare .global isr14 .type isr14, @function isr14: - cli # pushq $0 # err_code pushed by CPU pushq $14 # int_no jmp interrupt_handler_prepare .global isr15 .type isr15, @function isr15: - cli pushq $0 # err_code pushq $15 # int_no jmp interrupt_handler_prepare .global isr16 .type isr16, @function isr16: - cli pushq $0 # err_code pushq $16 # int_no jmp interrupt_handler_prepare .global isr17 .type isr17, @function isr17: - cli pushq $0 # err_code pushq $17 # int_no jmp interrupt_handler_prepare .global isr18 .type isr18, @function isr18: - cli pushq $0 # err_code pushq $18 # int_no jmp interrupt_handler_prepare .global isr19 .type isr19, @function isr19: - cli pushq $0 # err_code pushq $19 # int_no jmp interrupt_handler_prepare .global isr20 .type isr20, @function isr20: - cli pushq $0 # err_code pushq $20 # int_no jmp interrupt_handler_prepare .global isr21 .type isr21, @function isr21: - cli pushq $0 # err_code pushq $21 # int_no jmp interrupt_handler_prepare .global isr22 .type isr22, @function isr22: - cli pushq $0 # err_code pushq $22 # int_no jmp interrupt_handler_prepare .global isr23 .type isr23, @function isr23: - cli pushq $0 # err_code pushq $23 # int_no jmp interrupt_handler_prepare .global isr24 .type isr24, @function isr24: - cli pushq $0 # err_code pushq $24 # int_no jmp interrupt_handler_prepare .global isr25 .type isr25, @function isr25: - cli pushq $0 # err_code pushq $25 # int_no jmp interrupt_handler_prepare .global isr26 .type isr26, @function isr26: - cli pushq $0 # err_code pushq $26 # int_no jmp interrupt_handler_prepare .global isr27 .type isr27, @function isr27: - cli pushq $0 # err_code pushq $27 # int_no jmp interrupt_handler_prepare .global isr28 .type isr28, @function isr28: - cli pushq $0 # err_code pushq $28 # int_no jmp interrupt_handler_prepare .global isr29 .type isr29, @function isr29: - cli pushq $0 # err_code pushq $29 # int_no jmp interrupt_handler_prepare .global isr30 .type isr30, @function isr30: - cli pushq $0 # err_code pushq $30 # int_no jmp interrupt_handler_prepare .global isr31 .type isr31, @function isr31: - cli pushq $0 # err_code pushq $31 # int_no jmp interrupt_handler_prepare .global isr128 .type isr128, @function isr128: - cli pushq $0 # err_code pushq $128 # int_no jmp interrupt_handler_prepare .global isr130 .type isr130, @function isr130: - cli pushq $0 # err_code pushq $130 # int_no jmp interrupt_handler_prepare .global isr131 .type isr131, @function isr131: - cli pushq $0 # err_code pushq $131 # int_no jmp interrupt_handler_prepare .global irq0 .type irq0, @function irq0: - cli pushq $0 # err_code pushq $32 # int_no jmp interrupt_handler_prepare .global irq1 .type irq1, @function irq1: - cli pushq $0 # err_code pushq $33 # int_no jmp interrupt_handler_prepare .global irq2 .type irq2, @function irq2: - cli pushq $0 # err_code pushq $34 # int_no jmp interrupt_handler_prepare .global irq3 .type irq3, @function irq3: - cli pushq $0 # err_code pushq $35 # int_no jmp interrupt_handler_prepare .global irq4 .type irq4, @function irq4: - cli pushq $0 # err_code pushq $36 # int_no jmp interrupt_handler_prepare .global irq5 .type irq5, @function irq5: - cli pushq $0 # err_code pushq $37 # int_no jmp interrupt_handler_prepare .global irq6 .type irq6, @function irq6: - cli pushq $0 # err_code pushq $38 # int_no jmp interrupt_handler_prepare .global irq7 .type irq7, @function irq7: - cli pushq $0 # err_code pushq $39 # int_no jmp interrupt_handler_prepare .global irq8 .type irq8, @function irq8: - cli pushq $0 # err_code pushq $40 # int_no jmp interrupt_handler_prepare .global irq9 .type irq9, @function irq9: - cli pushq $0 # err_code pushq $41 # int_no jmp interrupt_handler_prepare .global irq10 .type irq10, @function irq10: - cli pushq $0 # err_code pushq $42 # int_no jmp interrupt_handler_prepare .global irq11 .type irq11, @function irq11: - cli pushq $0 # err_code pushq $43 # int_no jmp interrupt_handler_prepare .global irq12 .type irq12, @function irq12: - cli pushq $0 # err_code pushq $44 # int_no jmp interrupt_handler_prepare .global irq13 .type irq13, @function irq13: - cli pushq $0 # err_code pushq $45 # int_no jmp interrupt_handler_prepare .global irq14 .type irq14, @function irq14: - cli pushq $0 # err_code pushq $46 # int_no jmp interrupt_handler_prepare .global irq15 .type irq15, @function irq15: - cli pushq $0 # err_code pushq $47 # int_no jmp interrupt_handler_prepare .global yield_cpu_handler .type yield_cpu_handler, @function yield_cpu_handler: - cli pushq $0 # err_code pushq $129 # int_no jmp interrupt_handler_prepare .global thread_exit_handler .type thread_exit_handler, @function thread_exit_handler: - cli pushq $0 # err_code pushq $132 # int_no jmp interrupt_handler_prepare @@ -494,15 +441,6 @@ interrupt_handler_null: iretq .size interrupt_handler_null, . - interrupt_handler_null -.global asm_interrupts_are_enabled -.type asm_interrupts_are_enabled, @function -asm_interrupts_are_enabled: - pushfq - popq %rax - andq $0x000200, %rax # FLAGS_INTERRUPT - retq -.size asm_interrupts_are_enabled, . - asm_interrupts_are_enabled - .global load_registers .type load_registers, @function load_registers: diff --git a/kernel/x64/syscall.S b/kernel/x64/syscall.S index 05f87c1a..6b24bc83 100644 --- a/kernel/x64/syscall.S +++ b/kernel/x64/syscall.S @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. @@ -27,11 +27,6 @@ .section .text .type syscall_handler, @function syscall_handler: - # The processor disabled interrupts during the int $0x80 instruction, - # however Sortix system calls runs with interrupts enabled such that they - # can be pre-empted. - sti - movl $0, global_errno # Reset errno pushq %rbp diff --git a/kernel/x86-family/idt.cpp b/kernel/x86-family/idt.cpp index d986e018..a1e32f98 100644 --- a/kernel/x86-family/idt.cpp +++ b/kernel/x86-family/idt.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. @@ -30,52 +30,49 @@ namespace Sortix { namespace IDT { -struct idt_entry -{ - uint16_t handler_low; - uint16_t sel; - uint8_t reserved0; - uint8_t flags; - uint16_t handler_high; -#if defined(__x86_64__) - uint32_t handler_highest; - uint32_t reserved1; -#endif -}; - -struct idt_ptr -{ - uint16_t limit; -#if defined(__x86_64__) - uint64_t idt_ptr; -#else - uint32_t idt_ptr; -#endif -} __attribute__((packed)); - static struct idt_entry idt_entries[256]; void Init() { - volatile struct idt_ptr ptr; - ptr.limit = sizeof(idt_entries) - 1; - ptr.idt_ptr = (unsigned long) &idt_entries; - asm volatile ("lidt (%0)" : : "r"(&ptr)); memset(&idt_entries, 0, sizeof(idt_entries)); + Set(idt_entries, 256); } -void SetEntry(uint8_t num, uintptr_t handler, uint16_t sel, uint8_t flags) +void Set(struct idt_entry* table, size_t length) { - idt_entries[num].flags = flags; - idt_entries[num].reserved0 = 0; - idt_entries[num].sel = sel; - idt_entries[num].handler_low = handler >> 0 & 0xFFFF; - idt_entries[num].handler_high = handler >> 16 & 0xFFFF; + size_t limit = sizeof(idt_entry) * length - 1; #if defined(__x86_64__) - idt_entries[num].handler_highest = handler >> 32 & 0xFFFFFFFFU; - idt_entries[num].reserved1 = 0; + asm volatile ("subq $10, %%rsp\n\t" + "movw %w0, 0(%%rsp)\n\t" + "movq %1, 2(%%rsp)\n\t" + "lidt (%%rsp)\n\t" + "addq $10, %%rsp" : : "rN"(limit), "r"(table)); +#else + asm volatile ("subl $6, %%esp\n\t" + "movw %w0, 0(%%esp)\n\t" + "movl %1, 2(%%esp)\n\t" + "lidt (%%esp)\n\t" + "addl $6, %%esp" : : "rN"(limit), "r"(table)); #endif } +void SetEntry(struct idt_entry* entry, uintptr_t handler, uint16_t selector, uint8_t flags, uint8_t ist) +{ + entry->flags = flags; + entry->ist = ist; + entry->selector = selector; + entry->handler_low = handler >> 0 & 0xFFFF; + entry->handler_high = handler >> 16 & 0xFFFF; +#if defined(__x86_64__) + entry->handler_highest = handler >> 32 & 0xFFFFFFFFU; + entry->reserved1 = 0; +#endif +} + +void SetEntry(uint8_t num, uintptr_t handler, uint16_t selector, uint8_t flags, uint8_t ist) +{ + SetEntry(idt_entries + num, handler, selector, flags, ist); +} + } // namespace IDT } // namespace Sortix diff --git a/kernel/x86-family/idt.h b/kernel/x86-family/idt.h index aa6d6981..6ba8cfaf 100644 --- a/kernel/x86-family/idt.h +++ b/kernel/x86-family/idt.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. @@ -25,12 +25,36 @@ #ifndef SORTIX_X86_FAMILY_IDT_H #define SORTIX_X86_FAMILY_IDT_H +#include + namespace Sortix { namespace IDT { -void Init(); -void SetEntry(uint8_t num, uintptr_t handler, uint16_t sel, uint8_t flags); -void Flush(); +struct idt_entry +{ + uint16_t handler_low; + uint16_t selector; + uint8_t ist; + uint8_t flags; + uint16_t handler_high; +#if defined(__x86_64__) + uint32_t handler_highest; + uint32_t reserved1; +#endif +}; + +static const uint8_t FLAG_PRESENT = 1 << 7; +static const uint8_t FLAG_DPL_SHIFT = 5; +static const uint8_t FLAG_DPL_BITS = 2; +static const uint8_t FLAG_TYPE_SHIFT = 0; +static const uint8_t FLAG_TYPE_BITS = 4; + +static const uint8_t TYPE_INTERRUPT = 0xE; +static const uint8_t TYPE_TRAP = 0xF; + +void Set(struct idt_entry* table, size_t length); +void SetEntry(struct idt_entry* entry, uintptr_t handler, uint16_t selector, + uint8_t flags, uint8_t ist); } // namespace IDT } // namespace Sortix diff --git a/kernel/x86-family/interrupt.cpp b/kernel/x86-family/interrupt.cpp new file mode 100644 index 00000000..232d9918 --- /dev/null +++ b/kernel/x86-family/interrupt.cpp @@ -0,0 +1,355 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. + + This file is part of Sortix. + + Sortix 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. + + Sortix 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 + Sortix. If not, see . + + x86-family/interrupt.cpp + Interrupt support for i386 and x86_64 systems. + +*******************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "idt.h" +#include "pic.h" + +extern "C" void isr0(); +extern "C" void isr1(); +extern "C" void isr2(); +extern "C" void isr3(); +extern "C" void isr4(); +extern "C" void isr5(); +extern "C" void isr6(); +extern "C" void isr7(); +extern "C" void isr8(); +extern "C" void isr9(); +extern "C" void isr10(); +extern "C" void isr11(); +extern "C" void isr12(); +extern "C" void isr13(); +extern "C" void isr14(); +extern "C" void isr15(); +extern "C" void isr16(); +extern "C" void isr17(); +extern "C" void isr18(); +extern "C" void isr19(); +extern "C" void isr20(); +extern "C" void isr21(); +extern "C" void isr22(); +extern "C" void isr23(); +extern "C" void isr24(); +extern "C" void isr25(); +extern "C" void isr26(); +extern "C" void isr27(); +extern "C" void isr28(); +extern "C" void isr29(); +extern "C" void isr30(); +extern "C" void isr31(); +extern "C" void isr128(); +extern "C" void isr130(); +extern "C" void isr131(); +extern "C" void irq0(); +extern "C" void irq1(); +extern "C" void irq2(); +extern "C" void irq3(); +extern "C" void irq4(); +extern "C" void irq5(); +extern "C" void irq6(); +extern "C" void irq7(); +extern "C" void irq8(); +extern "C" void irq9(); +extern "C" void irq10(); +extern "C" void irq11(); +extern "C" void irq12(); +extern "C" void irq13(); +extern "C" void irq14(); +extern "C" void irq15(); +extern "C" void interrupt_handler_null(); +extern "C" void syscall_handler(); +extern "C" void yield_cpu_handler(); +extern "C" void thread_exit_handler(); + +namespace Sortix { +namespace Interrupt { + +extern "C" { unsigned long asm_is_cpu_interrupted = 0; } + +const bool CALLTRACE_KERNEL = false; +const bool CALLTRACE_USER = false; +const bool RUN_DEBUGGER_ON_KERNEL_CRASH = false; +const bool RUN_DEBUGGER_ON_USER_CRASH = false; + +const size_t NUM_KNOWN_EXCEPTIONS = 20; +const char* exception_names[] = +{ + "Divide by zero", /* 0, 0x0 */ + "Debug", /* 1, 0x1 */ + "Non maskable interrupt", /* 2, 0x2 */ + "Breakpoint", /* 3, 0x3 */ + "Into detected overflow", /* 4, 0x4 */ + "Out of bounds", /* 5, 0x5 */ + "Invalid opcode", /* 6, 0x6 */ + "No coprocessor", /* 7, 0x7 */ + "Double fault", /* 8, 0x8 */ + "Coprocessor segment overrun", /* 9, 0x9 */ + "Bad TSS", /* 10, 0xA */ + "Segment not present", /* 11, 0xB */ + "Stack fault", /* 12, 0xC */ + "General protection fault", /* 13, 0xD */ + "Page fault", /* 14, 0xE */ + "Unknown interrupt", /* 15, 0xF */ + "Coprocessor fault", /* 16, 0x10 */ + "Alignment check", /* 17, 0x11 */ + "Machine check", /* 18, 0x12 */ + "SIMD Floating-Point", /* 19, 0x13 */ +}; + +const unsigned int NUM_INTERRUPTS = 256U; + +static struct IDT::idt_entry interrupt_table[NUM_INTERRUPTS]; +static Handler interrupt_handlers[NUM_INTERRUPTS]; +static void* interrupt_handler_context[NUM_INTERRUPTS]; + +void RegisterHandler(unsigned int index, + Interrupt::Handler handler, + void* context) +{ + assert(index < NUM_INTERRUPTS); + interrupt_handlers[index] = handler; + interrupt_handler_context[index] = context; +} + +static void RegisterRawHandler(unsigned int index, + void (*handler)(void), + bool userspace, + bool preemptive) +{ + assert(index < NUM_INTERRUPTS); + addr_t handler_entry = (addr_t) handler; + uint16_t selector = KCS; + uint8_t rpl = userspace ? URPL : KRPL; + uint8_t type = preemptive ? IDT::TYPE_TRAP : IDT::TYPE_INTERRUPT; + uint8_t ist = 0; + uint8_t flags = IDT::FLAG_PRESENT + | type << IDT::FLAG_TYPE_SHIFT + | rpl << IDT::FLAG_DPL_SHIFT; + IDT::SetEntry(&interrupt_table[index], handler_entry, selector, flags, ist); +} + +void Init() +{ + // Initialize the interrupt table entries to the null interrupt handler. + memset(&interrupt_table, 0, sizeof(interrupt_table)); + for ( unsigned int i = 0; i < NUM_INTERRUPTS; i++ ) + { + interrupt_handlers[i] = NULL; + interrupt_handler_context[i] = NULL; + RegisterRawHandler(i, interrupt_handler_null, false, false); + } + + // Remap the IRQ table on the PICs. + PIC::ReprogramPIC(); + + RegisterRawHandler(0, isr0, false, false); + RegisterRawHandler(1, isr1, false, false); + RegisterRawHandler(2, isr2, false, false); + RegisterRawHandler(3, isr3, false, false); + RegisterRawHandler(4, isr4, false, false); + RegisterRawHandler(5, isr5, false, false); + RegisterRawHandler(6, isr6, false, false); + RegisterRawHandler(7, isr7, false, false); + RegisterRawHandler(8, isr8, false, false); + RegisterRawHandler(9, isr9, false, false); + RegisterRawHandler(10, isr10, false, false); + RegisterRawHandler(11, isr11, false, false); + RegisterRawHandler(12, isr12, false, false); + RegisterRawHandler(13, isr13, false, false); + RegisterRawHandler(14, isr14, false, false); + RegisterRawHandler(15, isr15, false, false); + RegisterRawHandler(16, isr16, false, false); + RegisterRawHandler(17, isr17, false, false); + RegisterRawHandler(18, isr18, false, false); + RegisterRawHandler(19, isr19, false, false); + RegisterRawHandler(20, isr20, false, false); + RegisterRawHandler(21, isr21, false, false); + RegisterRawHandler(22, isr22, false, false); + RegisterRawHandler(23, isr23, false, false); + RegisterRawHandler(24, isr24, false, false); + RegisterRawHandler(25, isr25, false, false); + RegisterRawHandler(26, isr26, false, false); + RegisterRawHandler(27, isr27, false, false); + RegisterRawHandler(28, isr28, false, false); + RegisterRawHandler(29, isr29, false, false); + RegisterRawHandler(30, isr30, false, false); + RegisterRawHandler(31, isr31, false, false); + RegisterRawHandler(32, irq0, false, false); + RegisterRawHandler(33, irq1, false, false); + RegisterRawHandler(34, irq2, false, false); + RegisterRawHandler(35, irq3, false, false); + RegisterRawHandler(36, irq4, false, false); + RegisterRawHandler(37, irq5, false, false); + RegisterRawHandler(38, irq6, false, false); + RegisterRawHandler(39, irq7, false, false); + RegisterRawHandler(40, irq8, false, false); + RegisterRawHandler(41, irq9, false, false); + RegisterRawHandler(42, irq10, false, false); + RegisterRawHandler(43, irq11, false, false); + RegisterRawHandler(44, irq12, false, false); + RegisterRawHandler(45, irq13, false, false); + RegisterRawHandler(46, irq14, false, false); + RegisterRawHandler(47, irq15, false, false); + RegisterRawHandler(128, syscall_handler, true, true); + RegisterRawHandler(129, yield_cpu_handler, true, false); + RegisterRawHandler(130, isr130, true, false); + RegisterRawHandler(131, isr131, true, false); + RegisterRawHandler(132, thread_exit_handler, true, false); + + RegisterHandler(129, Scheduler::InterruptYieldCPU, NULL); + RegisterHandler(130, Signal::Dispatch, NULL); + RegisterHandler(131, Signal::Return, NULL); + RegisterHandler(132, Scheduler::ThreadExitCPU, NULL); + + IDT::Set(interrupt_table, NUM_INTERRUPTS); + + Interrupt::Enable(); +} + +const char* ExceptionName(const CPU::InterruptRegisters* regs) +{ + if ( regs->int_no < NUM_KNOWN_EXCEPTIONS ) + return exception_names[regs->int_no]; + return "Unknown"; +} + +uintptr_t ExceptionLocation(const CPU::InterruptRegisters* regs) +{ +#if defined(__x86_64__) + return regs->rip; +#elif defined(__i386__) + return regs->eip; +#endif +} + +void CrashCalltrace(const CPU::InterruptRegisters* regs) +{ +#if defined(__x86_64__) + Calltrace::Perform(regs->rbp); +#elif defined(__i386__) + Calltrace::Perform(regs->ebp); +#else + #warning "Please provide a calltrace implementation for your CPU." +#endif +} + +__attribute__((noreturn)) +void KernelCrashHandler(CPU::InterruptRegisters* regs) +{ + CurrentThread()->SaveRegisters(regs); + + // Walk and print the stack frames if this is a debug build. + if ( CALLTRACE_KERNEL ) + CrashCalltrace(regs); + + // Possibly switch to the kernel debugger in event of a crash. + if ( RUN_DEBUGGER_ON_KERNEL_CRASH ) + Debugger::Run(); + + // Panic the kernel with a diagnostic message. + PanicF("Unhandled CPU Exception id %zu `%s' at ip=0x%zx (cr2=0x%zx, " + "err_code=0x%zx)", regs->int_no, ExceptionName(regs), + ExceptionLocation(regs), regs->cr2, regs->err_code); +} + +void UserCrashHandler(CPU::InterruptRegisters* regs) +{ + CurrentThread()->SaveRegisters(regs); + + // Execute this crash handler with preemption on. + Interrupt::Enable(); + + // Walk and print the stack frames if this is a debug build. + if ( CALLTRACE_USER ) + CrashCalltrace(regs); + + // Possibly switch to the kernel debugger in event of a crash. + if ( RUN_DEBUGGER_ON_USER_CRASH ) + Debugger::Run(); + + // Issue a diagnostic message to the kernel log concerning the crash. + Log::PrintF("The current process (pid %ji `%s') crashed and was terminated:\n", + (intmax_t) CurrentProcess()->pid, CurrentProcess()->program_image_path); + Log::PrintF("%s exception at ip=0x%zx (cr2=0x%zx, err_code=0x%zx)\n", + ExceptionName(regs), ExceptionLocation(regs), regs->cr2, + regs->err_code); + + // Exit the process with the right error code. + // TODO: Send a SIGINT, SIGBUS, or whatever instead. + CurrentProcess()->Exit(139); + + // TODO: Is it strictly needed or even desirable to disable preemption here? + Interrupt::Disable(); + + // Deliver signals to this thread so it can exit correctly. + Signal::Dispatch(regs); +} + +extern "C" void interrupt_handler(CPU::InterruptRegisters* regs) +{ + unsigned int int_no = regs->int_no; + + // IRQ 7 and 15 might be spurious and might need to be ignored. + if ( int_no == IRQ7 && !(PIC::ReadISR() & (1 << 7)) ) + return; + if ( int_no == IRQ15 && !(PIC::ReadISR() & (1 << 15)) ) + { + PIC::SendMasterEOI(); + return; + } + + bool is_in_kernel = (regs->cs & 0x3) == KRPL; + bool is_in_user = !is_in_kernel; + bool is_crash = int_no < 32 && int_no != 7; + + // Invoke the appropriate interrupt handler. + if ( is_crash && is_in_kernel ) + KernelCrashHandler(regs); + else if ( is_crash && is_in_user ) + UserCrashHandler(regs); + else if ( interrupt_handlers[int_no] ) + interrupt_handlers[int_no](regs, interrupt_handler_context[int_no]); + + // Send an end of interrupt signal to the PICs if we got an IRQ. + if ( IRQ0 <= int_no && int_no <= IRQ15 ) + PIC::SendEOI(int_no - IRQ0); +} + +} // namespace Interrupt +} // namespace Sortix diff --git a/kernel/x86-family/pic.cpp b/kernel/x86-family/pic.cpp new file mode 100644 index 00000000..029572e5 --- /dev/null +++ b/kernel/x86-family/pic.cpp @@ -0,0 +1,122 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. + + This file is part of Sortix. + + Sortix 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. + + Sortix 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 + Sortix. If not, see . + + x86-family/pic.cpp + Driver for the Programmable Interrupt Controller. + +*******************************************************************************/ + +#include + +#include +#include +#include + +#include "pic.h" + +namespace Sortix { +namespace PIC { + +const uint16_t PIC_MASTER = 0x20; +const uint16_t PIC_SLAVE = 0xA0; +const uint16_t PIC_COMMAND = 0x00; +const uint16_t PIC_DATA = 0x01; +const uint8_t PIC_CMD_ENDINTR = 0x20; +const uint8_t PIC_ICW1_ICW4 = 0x01; // ICW4 (not) needed +const uint8_t PIC_ICW1_SINGLE = 0x02; // Single (cascade) mode +const uint8_t PIC_ICW1_INTERVAL4 = 0x04; // Call address interval 4 (8) +const uint8_t PIC_ICW1_LEVEL = 0x08; // Level triggered (edge) mode +const uint8_t PIC_CMD_INIT = 0x10; +const uint8_t PIC_MODE_8086 = 0x01; // 8086/88 (MCS-80/85) mode +const uint8_t PIC_MODE_AUTO = 0x02; // Auto (normal) EOI +const uint8_t PIC_MODE_BUF_SLAVE = 0x08; // Buffered mode/slave +const uint8_t PIC_MODE_BUF_MASTER = 0x0C; // Buffered mode/master +const uint8_t PIC_MODE_SFNM = 0x10; // Special fully nested (not) +const uint8_t PIC_READ_IRR = 0x0A; +const uint8_t PIC_READ_ISR = 0x0B; + +static uint16_t ReadRegister(uint8_t ocw3) +{ + CPU::OutPortB(PIC_MASTER + PIC_COMMAND, ocw3); + CPU::OutPortB(PIC_SLAVE + PIC_COMMAND, ocw3); + return CPU::InPortB(PIC_MASTER + PIC_COMMAND) << 0 | + CPU::InPortB(PIC_SLAVE + PIC_COMMAND) << 8; +} + +uint16_t ReadIRR() +{ + return ReadRegister(PIC_READ_IRR); +} + +uint16_t ReadISR() +{ + return ReadRegister(PIC_READ_ISR); +} + +extern "C" void ReprogramPIC() +{ + uint8_t master_mask = 0; + uint8_t slave_mask = 0; + CPU::OutPortB(PIC_MASTER + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4); + CPU::OutPortB(PIC_SLAVE + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4); + CPU::OutPortB(PIC_MASTER + PIC_DATA, Interrupt::IRQ0); + CPU::OutPortB(PIC_SLAVE + PIC_DATA, Interrupt::IRQ8); + CPU::OutPortB(PIC_MASTER + PIC_DATA, 0x04); // Slave PIC at IRQ2 + CPU::OutPortB(PIC_SLAVE + PIC_DATA, 0x02); // Cascade Identity + CPU::OutPortB(PIC_MASTER + PIC_DATA, PIC_MODE_8086); + CPU::OutPortB(PIC_SLAVE + PIC_DATA, PIC_MODE_8086); + CPU::OutPortB(PIC_MASTER + PIC_DATA, master_mask); + CPU::OutPortB(PIC_SLAVE + PIC_DATA, slave_mask); +} + +extern "C" void DeprogramPIC() +{ + uint8_t master_mask = 0; + uint8_t slave_mask = 0; + CPU::OutPortB(PIC_MASTER + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4); + CPU::OutPortB(PIC_SLAVE + PIC_COMMAND, PIC_CMD_INIT | PIC_ICW1_ICW4); + CPU::OutPortB(PIC_MASTER + PIC_DATA, 0x08); + CPU::OutPortB(PIC_SLAVE + PIC_DATA, 0x70); + CPU::OutPortB(PIC_MASTER + PIC_DATA, 0x04); // Slave PIC at IRQ2 + CPU::OutPortB(PIC_SLAVE + PIC_DATA, 0x02); // Cascade Identity + CPU::OutPortB(PIC_MASTER + PIC_DATA, PIC_MODE_8086); + CPU::OutPortB(PIC_SLAVE + PIC_DATA, PIC_MODE_8086); + CPU::OutPortB(PIC_MASTER + PIC_DATA, master_mask); + CPU::OutPortB(PIC_SLAVE + PIC_DATA, slave_mask); +} + +void SendMasterEOI() +{ + CPU::OutPortB(PIC_MASTER, PIC_CMD_ENDINTR); +} + +void SendSlaveEOI() +{ + CPU::OutPortB(PIC_SLAVE, PIC_CMD_ENDINTR); +} + +void SendEOI(unsigned int irq) +{ + if ( 8 <= irq ) + SendSlaveEOI(); + SendMasterEOI(); +} + +} // namespace PIC +} // namespace Sortix diff --git a/kernel/x86-family/pic.h b/kernel/x86-family/pic.h new file mode 100644 index 00000000..02b568f2 --- /dev/null +++ b/kernel/x86-family/pic.h @@ -0,0 +1,43 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. + + This file is part of Sortix. + + Sortix 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. + + Sortix 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 + Sortix. If not, see . + + x86-family/pic.h + Driver for the Programmable Interrupt Controller. + +*******************************************************************************/ + +#ifndef SORTIX_X86_FAMILY_PIC_H +#define SORTIX_X86_FAMILY_PIC_H + +namespace Sortix { +namespace PIC { + +uint16_t ReadIRR(); +uint16_t ReadISR(); +bool IsSpuriousIRQ(unsigned int irq); +extern "C" void ReprogramPIC(); +extern "C" void DeprogramPIC(); +void SendMasterEOI(); +void SendSlaveEOI(); +void SendEOI(unsigned int irq); + +} // namespace PIC +} // namespace Sortix + +#endif diff --git a/kernel/x86/interrupt.S b/kernel/x86/interrupt.S index b58c5ff6..45380311 100644 --- a/kernel/x86/interrupt.S +++ b/kernel/x86/interrupt.S @@ -27,371 +27,318 @@ .global isr0 .type isr0, @function isr0: - cli pushl $0 # err_code pushl $0 # int_no jmp interrupt_handler_prepare .global isr1 .type isr1, @function isr1: - cli pushl $0 # err_code pushl $1 # int_no jmp interrupt_handler_prepare .global isr2 .type isr2, @function isr2: - cli pushl $0 # err_code pushl $2 # int_no jmp interrupt_handler_prepare .global isr3 .type isr3, @function isr3: - cli pushl $0 # err_code pushl $3 # int_no jmp interrupt_handler_prepare .global isr4 .type isr4, @function isr4: - cli pushl $0 # err_code pushl $4 # int_no jmp interrupt_handler_prepare .global isr5 .type isr5, @function isr5: - cli pushl $0 # err_code pushl $5 # int_no jmp interrupt_handler_prepare .global isr6 .type isr6, @function isr6: - cli pushl $0 # err_code pushl $6 # int_no jmp interrupt_handler_prepare .global isr7 .type isr7, @function isr7: - cli pushl $0 # err_code pushl $7 # int_no jmp interrupt_handler_prepare .global isr8 .type isr8, @function isr8: - cli # pushl $0 # err_code pushed by CPU pushl $8 # int_no jmp interrupt_handler_prepare .global isr9 .type isr9, @function isr9: - cli pushl $0 # err_code pushl $9 # int_no jmp interrupt_handler_prepare .global isr10 .type isr10, @function isr10: - cli # pushl $0 # err_code pushed by CPU pushl $10 # int_no jmp interrupt_handler_prepare .global isr11 .type isr11, @function isr11: - cli # pushl $0 # err_code pushed by CPU pushl $11 # int_no jmp interrupt_handler_prepare .global isr12 .type isr12, @function isr12: - cli # pushl $0 # err_code pushed by CPU pushl $12 # int_no jmp interrupt_handler_prepare .global isr13 .type isr13, @function isr13: - cli # pushl $0 # err_code pushed by CPU pushl $13 # int_no jmp interrupt_handler_prepare .global isr14 .type isr14, @function isr14: - cli # pushl $0 # err_code pushed by CPU pushl $14 # int_no jmp interrupt_handler_prepare .global isr15 .type isr15, @function isr15: - cli pushl $0 # err_code pushl $15 # int_no jmp interrupt_handler_prepare .global isr16 .type isr16, @function isr16: - cli pushl $0 # err_code pushl $16 # int_no jmp interrupt_handler_prepare .global isr17 .type isr17, @function isr17: - cli pushl $0 # err_code pushl $17 # int_no jmp interrupt_handler_prepare .global isr18 .type isr18, @function isr18: - cli pushl $0 # err_code pushl $18 # int_no jmp interrupt_handler_prepare .global isr19 .type isr19, @function isr19: - cli pushl $0 # err_code pushl $19 # int_no jmp interrupt_handler_prepare .global isr20 .type isr20, @function isr20: - cli pushl $0 # err_code pushl $20 # int_no jmp interrupt_handler_prepare .global isr21 .type isr21, @function isr21: - cli pushl $0 # err_code pushl $21 # int_no jmp interrupt_handler_prepare .global isr22 .type isr22, @function isr22: - cli pushl $0 # err_code pushl $22 # int_no jmp interrupt_handler_prepare .global isr23 .type isr23, @function isr23: - cli pushl $0 # err_code pushl $23 # int_no jmp interrupt_handler_prepare .global isr24 .type isr24, @function isr24: - cli pushl $0 # err_code pushl $24 # int_no jmp interrupt_handler_prepare .global isr25 .type isr25, @function isr25: - cli pushl $0 # err_code pushl $25 # int_no jmp interrupt_handler_prepare .global isr26 .type isr26, @function isr26: - cli pushl $0 # err_code pushl $26 # int_no jmp interrupt_handler_prepare .global isr27 .type isr27, @function isr27: - cli pushl $0 # err_code pushl $27 # int_no jmp interrupt_handler_prepare .global isr28 .type isr28, @function isr28: - cli pushl $0 # err_code pushl $28 # int_no jmp interrupt_handler_prepare .global isr29 .type isr29, @function isr29: - cli pushl $0 # err_code pushl $29 # int_no jmp interrupt_handler_prepare .global isr30 .type isr30, @function isr30: - cli pushl $0 # err_code pushl $30 # int_no jmp interrupt_handler_prepare .global isr31 .type isr31, @function isr31: - cli pushl $0 # err_code pushl $31 # int_no jmp interrupt_handler_prepare .global isr128 .type isr128, @function isr128: - cli pushl $0 # err_code pushl $128 # int_no jmp interrupt_handler_prepare .global isr130 .type isr130, @function isr130: - cli pushl $0 # err_code pushl $130 # int_no jmp interrupt_handler_prepare .global isr131 .type isr131, @function isr131: - cli pushl $0 # err_code pushl $131 # int_no jmp interrupt_handler_prepare .global irq0 .type irq0, @function irq0: - cli pushl $0 # err_code pushl $32 # int_no jmp interrupt_handler_prepare .global irq1 .type irq1, @function irq1: - cli pushl $0 # err_code pushl $33 # int_no jmp interrupt_handler_prepare .global irq2 .type irq2, @function irq2: - cli pushl $0 # err_code pushl $34 # int_no jmp interrupt_handler_prepare .global irq3 .type irq3, @function irq3: - cli pushl $0 # err_code pushl $35 # int_no jmp interrupt_handler_prepare .global irq4 .type irq4, @function irq4: - cli pushl $0 # err_code pushl $36 # int_no jmp interrupt_handler_prepare .global irq5 .type irq5, @function irq5: - cli pushl $0 # err_code pushl $37 # int_no jmp interrupt_handler_prepare .global irq6 .type irq6, @function irq6: - cli pushl $0 # err_code pushl $38 # int_no jmp interrupt_handler_prepare .global irq7 .type irq7, @function irq7: - cli pushl $0 # err_code pushl $39 # int_no jmp interrupt_handler_prepare .global irq8 .type irq8, @function irq8: - cli pushl $0 # err_code pushl $40 # int_no jmp interrupt_handler_prepare .global irq9 .type irq9, @function irq9: - cli pushl $0 # err_code pushl $41 # int_no jmp interrupt_handler_prepare .global irq10 .type irq10, @function irq10: - cli pushl $0 # err_code pushl $42 # int_no jmp interrupt_handler_prepare .global irq11 .type irq11, @function irq11: - cli pushl $0 # err_code pushl $43 # int_no jmp interrupt_handler_prepare .global irq12 .type irq12, @function irq12: - cli pushl $0 # err_code pushl $44 # int_no jmp interrupt_handler_prepare .global irq13 .type irq13, @function irq13: - cli pushl $0 # err_code pushl $45 # int_no jmp interrupt_handler_prepare .global irq14 .type irq14, @function irq14: - cli pushl $0 # err_code pushl $46 # int_no jmp interrupt_handler_prepare .global irq15 .type irq15, @function irq15: - cli pushl $0 # err_code pushl $47 # int_no jmp interrupt_handler_prepare .global yield_cpu_handler .type yield_cpu_handler, @function yield_cpu_handler: - cli pushl $0 # err_code pushl $129 # int_no jmp interrupt_handler_prepare .global thread_exit_handler .type thread_exit_handler, @function thread_exit_handler: - cli pushl $0 # err_code pushl $132 # int_no jmp interrupt_handler_prepare @@ -580,15 +527,6 @@ interrupt_handler_null: iret .size interrupt_handler_null, . - interrupt_handler_null -.global asm_interrupts_are_enabled -.type asm_interrupts_are_enabled, @function -asm_interrupts_are_enabled: - pushfl - popl %eax - andl $0x000200, %eax # FLAGS_INTERRUPT - retl -.size asm_interrupts_are_enabled, . - asm_interrupts_are_enabled - .global load_registers .type load_registers, @function load_registers: diff --git a/kernel/x86/syscall.S b/kernel/x86/syscall.S index 8f63f10a..e0d1ce38 100644 --- a/kernel/x86/syscall.S +++ b/kernel/x86/syscall.S @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. @@ -27,11 +27,6 @@ .section .text .type syscall_handler, @function syscall_handler: - # The processor disabled interrupts during the int $0x80 instruction, - # however Sortix system calls runs with interrupts enabled such that they - # can be pre-empted. - sti - movl $0, global_errno # Reset errno pushl %ebp