Refactor kernel interrupt handling.

This commit is contained in:
Jonas 'Sortie' Termansen 2013-06-20 00:01:04 +02:00
parent 4dad48271a
commit c77d9395cd
7 changed files with 168 additions and 261 deletions

View File

@ -48,7 +48,6 @@ ifdef X86FAMILY
$(CPU)/interrupt.o \
$(CPU)/gdt.o \
x86-family/gdt.o \
$(CPU)/idt.o \
x86-family/idt.o \
$(CPU)/syscall.o \
$(CPU)/thread.o \

View File

@ -1,6 +1,6 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
This file is part of Sortix.
@ -35,22 +35,22 @@ struct InterruptRegisters;
namespace Interrupt {
const unsigned IRQ0 = 32;
const unsigned IRQ1 = 33;
const unsigned IRQ2 = 34;
const unsigned IRQ3 = 35;
const unsigned IRQ4 = 36;
const unsigned IRQ5 = 37;
const unsigned IRQ6 = 38;
const unsigned IRQ7 = 39;
const unsigned IRQ8 = 40;
const unsigned IRQ9 = 41;
const unsigned IRQ10 = 42;
const unsigned IRQ11 = 43;
const unsigned IRQ12 = 44;
const unsigned IRQ13 = 45;
const unsigned IRQ14 = 46;
const unsigned IRQ15 = 47;
const unsigned int IRQ0 = 32;
const unsigned int IRQ1 = 33;
const unsigned int IRQ2 = 34;
const unsigned int IRQ3 = 35;
const unsigned int IRQ4 = 36;
const unsigned int IRQ5 = 37;
const unsigned int IRQ6 = 38;
const unsigned int IRQ7 = 39;
const unsigned int IRQ8 = 40;
const unsigned int IRQ9 = 41;
const unsigned int IRQ10 = 42;
const unsigned int IRQ11 = 43;
const unsigned int IRQ12 = 44;
const unsigned int IRQ13 = 45;
const unsigned int IRQ14 = 46;
const unsigned int IRQ15 = 47;
extern "C" unsigned long asm_interrupts_are_enabled();
extern "C" unsigned long asm_is_cpu_interrupted;
@ -67,10 +67,10 @@ inline bool SetEnabled(bool isenabled)
}
typedef void (*Handler)(CPU::InterruptRegisters* regs, void* user);
void RegisterHandler(unsigned index, Handler handler, void* user);
void RegisterHandler(unsigned int index, Handler handler, void* user);
typedef void (*RawHandler)(void);
void RegisterRawHandler(unsigned index, RawHandler handler, bool userspace);
void RegisterRawHandler(unsigned int index, RawHandler handler, bool userspace);
void Init();
void InitWorker();

View File

@ -22,9 +22,14 @@
*******************************************************************************/
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sortix/kernel/platform.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/interrupt.h>
#include <sortix/kernel/cpu.h>
#include <sortix/kernel/scheduler.h>
#include <sortix/kernel/signal.h>
#include <sortix/kernel/thread.h>
@ -32,10 +37,6 @@
#include <sortix/kernel/calltrace.h>
#include <sortix/kernel/debugger.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include "x86-family/idt.h"
#include "sound.h" // Hack for SIGSEGV
@ -67,42 +68,40 @@ const bool DEBUG_ISR = false;
const bool CALLTRACE_KERNEL = false;
const bool CALLTRACE_USER = false;
const bool RUN_DEBUGGER_ON_CRASH = false;
bool initialized;
const size_t NUM_KNOWN_EXCEPTIONS = 20;
const char* exceptions[] =
{
"Divide by zero",
"Debug",
"Non maskable interrupt",
"Breakpoint",
"Into detected overflow",
"Out of bounds",
"Invalid opcode",
"No coprocessor",
"Double fault",
"Coprocessor segment overrun",
"Bad TSS",
"Segment not present",
"Stack fault",
"General protection fault",
"Page fault",
"Unknown interrupt",
"Coprocessor fault",
"Alignment check",
"Machine check",
"SIMD Floating-Point",
"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 NUM_INTERRUPTS = 256UL;
Handler interrupthandlers[NUM_INTERRUPTS];
void* interrupthandlerptr[NUM_INTERRUPTS];
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 mastermask = 0;
uint8_t slavemask = 0;
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);
@ -111,14 +110,14 @@ extern "C" void ReprogramPIC()
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, mastermask);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, slavemask);
CPU::OutPortB(PIC_MASTER + PIC_DATA, master_mask);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, slave_mask);
}
extern "C" void DeprogramPIC()
{
uint8_t mastermask = 0;
uint8_t slavemask = 0;
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);
@ -127,19 +126,18 @@ extern "C" void DeprogramPIC()
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, mastermask);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, slavemask);
CPU::OutPortB(PIC_MASTER + PIC_DATA, master_mask);
CPU::OutPortB(PIC_SLAVE + PIC_DATA, slave_mask);
}
void Init()
{
initialized = false;
IDT::Init();
for ( unsigned i = 0; i < NUM_INTERRUPTS; i++ )
for ( unsigned int i = 0; i < NUM_INTERRUPTS; i++ )
{
interrupthandlers[i] = NULL;
interrupthandlerptr[i] = NULL;
interrupt_handlers[i] = NULL;
interrupt_handler_params[i] = NULL;
RegisterRawHandler(i, interrupt_handler_null, false);
}
@ -198,37 +196,38 @@ void Init()
// TODO: Let the syscall.cpp code register this.
RegisterRawHandler(128, syscall_handler, true);
IDT::Flush();
initialized = true;
Interrupt::Enable();
}
void RegisterHandler(unsigned n, Interrupt::Handler handler, void* user)
void RegisterHandler(unsigned int index, Interrupt::Handler handler, void* user)
{
interrupthandlers[n] = handler;
interrupthandlerptr[n] = user;
interrupt_handlers[index] = handler;
interrupt_handler_params[index] = user;
}
// TODO: This function contains magic IDT-related values!
void RegisterRawHandler(unsigned index, RawHandler handler, bool userspace)
void RegisterRawHandler(unsigned int index, RawHandler handler, bool userspace)
{
addr_t handlerentry = (addr_t) handler;
uint16_t sel = 0x08;
addr_t handler_entry = (addr_t) handler;
uint16_t sel = KCS;
uint8_t flags = 0x8E;
if ( userspace ) { flags |= 0x60; }
IDT::SetGate(index, handlerentry, sel, flags);
if ( initialized ) { IDT::Flush(); }
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 )
const char* message = regs->int_no < NUM_KNOWN_EXCEPTIONS
? exceptions[regs->int_no] : "Unknown";
if ( DEBUG_EXCEPTION ) { regs->LogRegisters(); Log::Print("\n"); }
if ( DEBUG_EXCEPTION )
{
regs->LogRegisters();
Log::Print("\n");
}
#ifdef PLATFORM_X64
addr_t ip = regs->rip;
@ -237,8 +236,8 @@ void CrashHandler(CPU::InterruptRegisters* regs)
#endif
// Halt and catch fire if we are the kernel.
unsigned codemode = regs->cs & 0x3U;
bool is_in_kernel = !codemode;
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) )
@ -267,9 +266,6 @@ void CrashHandler(CPU::InterruptRegisters* regs)
Log::PrintF("%s exception at ip=0x%zx (cr2=0x%p, err_code=0x%p)\n",
message, ip, regs->cr2, regs->err_code);
//addr_t topofstack = ((size_t*) regs->useresp)[0];
//Log::PrintF("Top of stack is 0x%zx\n", topofstack);
Sound::Mute();
// Exit the process with the right error code.
@ -280,33 +276,30 @@ void CrashHandler(CPU::InterruptRegisters* regs)
Signal::Dispatch(regs);
}
void ISRHandler(Sortix::CPU::InterruptRegisters* regs)
void ISRHandler(CPU::InterruptRegisters* regs)
{
unsigned int int_no = regs->int_no;
if ( DEBUG_ISR )
{
Log::PrintF("ISR%u ", regs->int_no);
Log::PrintF("ISR%u ", int_no);
regs->LogRegisters();
Log::Print("\n");
}
if ( regs->int_no < 32 && regs->int_no != 7 )
{
// Run the desired interrupt handler.
if ( int_no < 32 && int_no != 7 )
CrashHandler(regs);
return;
}
if ( interrupthandlers[regs->int_no] != NULL )
{
void* user = interrupthandlerptr[regs->int_no];
interrupthandlers[regs->int_no](regs, user);
}
else if ( interrupt_handlers[regs->int_no] != NULL )
interrupt_handlers[int_no](regs, interrupt_handler_params[int_no]);
}
void IRQHandler(Sortix::CPU::InterruptRegisters* regs)
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 ( regs->int_no == 32 + 7 || regs->int_no == 32 + 15 )
return;
if ( DEBUG_IRQ )
{
@ -315,24 +308,23 @@ void IRQHandler(Sortix::CPU::InterruptRegisters* regs)
Log::Print("\n");
}
unsigned int_no = regs->int_no;
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); }
if ( IRQ8 <= int_no )
CPU::OutPortB(PIC_SLAVE, PIC_CMD_ENDINTR);
CPU::OutPortB(PIC_MASTER, PIC_CMD_ENDINTR);
if ( interrupthandlers[int_no] )
{
void* user = interrupthandlerptr[int_no];
interrupthandlers[int_no](regs, user);
}
if ( interrupt_handlers[int_no] )
interrupt_handlers[int_no](regs, interrupt_handler_params[int_no]);
}
extern "C" void interrupt_handler(Sortix::CPU::InterruptRegisters* regs)
extern "C" void interrupt_handler(CPU::InterruptRegisters* regs)
{
size_t int_no = regs->int_no;
if ( 32 <= int_no && int_no < 48 ) { IRQHandler(regs); }
else { ISRHandler(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.
@ -357,9 +349,11 @@ void InitWorker()
const size_t QUEUE_SIZE = 4UL*1024UL;
STATIC_ASSERT(QUEUE_SIZE % sizeof(Package) == 0);
queue = new uint8_t[QUEUE_SIZE];
if ( !queue ) { Panic("Can't allocate interrupt worker queue"); }
if ( !queue )
Panic("Can't allocate interrupt worker queue");
storage = new uint8_t[QUEUE_SIZE];
if ( !storage ) { Panic("Can't allocate interrupt worker storage"); }
if ( !storage )
Panic("Can't allocate interrupt worker storage");
queuesize = QUEUE_SIZE;
queueoffset = 0;
queueused = 0;
@ -373,7 +367,8 @@ static void WriteToQueue(const void* src, size_t size)
size_t count = available < size ? available : size;
memcpy(queue + writeat, buf, count);
queueused += count;
if ( count < size ) { WriteToQueue(buf + count, size - count); }
if ( count < size )
WriteToQueue(buf + count, size - count);
}
static void ReadFromQueue(void* dest, size_t size)
@ -384,7 +379,8 @@ static void ReadFromQueue(void* dest, size_t size)
memcpy(buf, queue + queueoffset, count);
queueused -= count;
queueoffset = (queueoffset + count) % queuesize;
if ( count < size ) { ReadFromQueue(buf + count, size - count); }
if ( count < size )
ReadFromQueue(buf + count, size - count);
}
static Package* PopPackage(uint8_t** payloadp, Package* /*prev*/)
@ -393,7 +389,8 @@ static Package* PopPackage(uint8_t** payloadp, Package* /*prev*/)
uint8_t* payload = NULL;
Interrupt::Disable();
if ( !queueused ) { goto out; }
if ( !queueused )
goto out;
package = (Package*) storage;
ReadFromQueue(package, sizeof(*package));
@ -431,7 +428,8 @@ bool ScheduleWork(WorkHandler handler, void* payload, size_t payloadsize)
package.handler = handler;
size_t queuefreespace = queuesize - queueused;
if ( queuefreespace < package.size ) { return false; }
if ( queuefreespace < package.size )
return false;
WriteToQueue(&package, sizeof(package));
WriteToQueue(payload, payloadsize);

View File

@ -1,33 +0,0 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
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 <http://www.gnu.org/licenses/>.
x64/idt.s
Handles initialization of the 64-bit IDT.
*******************************************************************************/
.section .text
.global idt_flush
.type idt_flush, @function
idt_flush:
# Load the IDT pointer.
lidt (%rdi)
ret
.size idt_flush, . - idt_flush

View File

@ -1,6 +1,6 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
This file is part of Sortix.
@ -18,48 +18,64 @@
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/idt.cpp
Initializes and handles the IDT.
Initializes and handles the interrupt descriptor table.
*******************************************************************************/
#include <sortix/kernel/platform.h>
#include <stdint.h>
#include <string.h>
#include "idt.h"
namespace Sortix
namespace Sortix {
namespace IDT {
struct idt_entry
{
namespace IDT
{
extern "C" void idt_flush(addr_t);
idt_entry_t idt_entries[256];
idt_ptr_t idt_ptr;
void Init()
{
idt_ptr.limit = sizeof(idt_entry_t) * 256 - 1;
idt_ptr.base = (addr_t) &idt_entries;
memset(&idt_entries, 0, sizeof(idt_entry_t)*256);
}
void Flush()
{
idt_flush((addr_t) &idt_ptr);
}
void SetGate(uint8_t num, addr_t base, uint16_t sel, uint8_t flags)
{
idt_entries[num].base_low = base & 0xFFFF;
idt_entries[num].base_high = (base >> 16) & 0xFFFF;
#ifdef PLATFORM_X64
idt_entries[num].base_highest = (base >> 32 ) & 0xFFFFFFFFU;
idt_entries[num].zero1 = 0;
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
};
idt_entries[num].sel = sel;
idt_entries[num].always0 = 0;
idt_entries[num].flags = flags;
}
}
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));
}
void SetEntry(uint8_t num, uintptr_t handler, uint16_t sel, uint8_t flags)
{
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;
#if defined(__x86_64__)
idt_entries[num].handler_highest = handler >> 32 & 0xFFFFFFFFU;
idt_entries[num].reserved1 = 0;
#endif
}
} // namespace IDT
} // namespace Sortix

View File

@ -1,6 +1,6 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
This file is part of Sortix.
@ -25,53 +25,14 @@
#ifndef SORTIX_X86_FAMILY_IDT_H
#define SORTIX_X86_FAMILY_IDT_H
namespace Sortix
{
namespace IDT
{
// A struct describing an interrupt gate.
struct idt_entry32_struct
{
uint16_t base_low; // The lower 16 bits of the address to jump to when this interrupt fires.
uint16_t sel; // Kernel segment selector.
uint8_t always0; // This must always be zero.
uint8_t flags; // More flags. See documentation.
uint16_t base_high; // The upper 16 bits of the address to jump to.
} __attribute__((packed));
namespace Sortix {
namespace IDT {
struct idt_entry64_struct
{
uint16_t base_low; // The lower 16 bits of the address to jump to when this interrupt fires.
uint16_t sel; // Kernel segment selector.
uint8_t always0; // This must always be zero.
uint8_t flags; // More flags. See documentation.
uint16_t base_high; // The upper 16 bits of the address to jump to.
uint32_t base_highest;
uint32_t zero1; // Reserved
} __attribute__((packed));
void Init();
void SetEntry(uint8_t num, uintptr_t handler, uint16_t sel, uint8_t flags);
void Flush();
typedef struct idt_entry32_struct idt_entry32_t;
typedef struct idt_entry64_struct idt_entry64_t;
#ifdef PLATFORM_X64
typedef idt_entry64_t idt_entry_t;
#else
typedef idt_entry32_t idt_entry_t;
#endif
// A struct describing a pointer to an array of interrupt handlers.
// This is in a format suitable for giving to 'lidt'.
struct idt_ptr_struct
{
uint16_t limit;
addr_t base; // The address of the first element in our idt_entry_t array.
} __attribute__((packed));
typedef struct idt_ptr_struct idt_ptr_t;
void Init();
void SetGate(uint8_t num, addr_t base, uint16_t sel, uint8_t flags);
void Flush();
}
}
} // namespace IDT
} // namespace Sortix
#endif

View File

@ -1,34 +0,0 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
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 <http://www.gnu.org/licenses/>.
x86/idt.s
Handles initialization of the 32-bit IDT.
*******************************************************************************/
.section .text
.global idt_flush
.type idt_flush, @function
idt_flush:
# Load the IDT pointer.
mov 4(%esp), %eax
lidt (%eax)
ret
.size idt_flush, . - idt_flush