Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ce772a7
commit 5899247
Showing
2 changed files
with
319 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,269 @@ | ||
#include <arch.h> | ||
#include <cpu.h> | ||
#include <paging.h> | ||
#include <pit.h> | ||
#include <printk.h> | ||
#include <sched.h> | ||
#include <smp.h> | ||
#include <task.h> | ||
|
||
// implementation of the x86 interrupt request handling system | ||
extern u32 idt_block[]; | ||
extern void *vectors[]; // in vectors.S: array of 256 entry pointers | ||
|
||
// Set up a normal interrupt/trap gate descriptor. | ||
// - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate. | ||
// interrupt gate clears FL_IF, trap gate leaves FL_IF alone | ||
// - sel: Code segment selector for interrupt/trap handler | ||
// - off: Offset in code segment for interrupt/trap handler | ||
// - dpl: Descriptor Privilege Level - | ||
// the privilege level required for software to invoke | ||
// this interrupt/trap gate explicitly using an int instruction. | ||
#define SETGATE(gate, istrap, sel, off, d) \ | ||
{ \ | ||
(gate).off_15_0 = (u32)(off)&0xffff; \ | ||
(gate).cs = (sel); \ | ||
(gate).args = 0; \ | ||
(gate).rsv1 = 0; \ | ||
(gate).type = (istrap) ? STS_TG32 : STS_IG32; \ | ||
(gate).s = 0; \ | ||
(gate).dpl = (d); \ | ||
(gate).p = 1; \ | ||
(gate).off_31_16 = (u32)(off) >> 16; \ | ||
} | ||
|
||
// Processor-defined: | ||
#define TRAP_DIVIDE 0 // divide error | ||
#define TRAP_DEBUG 1 // debug exception | ||
#define TRAP_NMI 2 // non-maskable interrupt | ||
#define TRAP_BRKPT 3 // breakpoint | ||
#define TRAP_OFLOW 4 // overflow | ||
#define TRAP_BOUND 5 // bounds check | ||
#define TRAP_ILLOP 6 // illegal opcode | ||
#define TRAP_DEVICE 7 // device not available | ||
#define TRAP_DBLFLT 8 // double fault | ||
#define TRAP_TSS 10 // invalid task switch segment | ||
#define TRAP_SEGNP 11 // segment not present | ||
#define TRAP_STACK 12 // stack exception | ||
#define TRAP_GPFLT 13 // general protection fault | ||
#define TRAP_PGFLT 14 // page fault | ||
// #define TRAP_RES 15 // reserved | ||
#define TRAP_FPERR 16 // floating point error | ||
#define TRAP_ALIGN 17 // aligment check | ||
#define TRAP_MCHK 18 // machine check | ||
#define TRAP_SIMDERR 19 // SIMD floating point error | ||
|
||
#define EXCP_NAME 0 | ||
#define EXCP_MNEMONIC 1 | ||
const char *excp_codes[128][2] = { | ||
{"Divide By Zero", "#DE"}, | ||
{"Debug", "#DB"}, | ||
{"Non-maskable Interrupt", "N/A"}, | ||
{"Breakpoint Exception", "#BP"}, | ||
{"Overflow Exception", "#OF"}, | ||
{"Bound Range Exceeded", "#BR"}, | ||
{"Invalid Opcode", "#UD"}, | ||
{"Device Not Available", "#NM"}, | ||
{"Double Fault", "#DF"}, | ||
{"Coproc Segment Overrun", "N/A"}, | ||
{"Invalid TSS", "#TS"}, | ||
{"Segment Not Present", "#NP"}, | ||
{"Stack Segment Fault", "#SS"}, | ||
{"General Protection Fault", "#GP"}, | ||
{"Page Fault", "#PF"}, | ||
{"Reserved", "N/A"}, | ||
{"x86 FP Exception", "#MF"}, | ||
{"Alignment Check", "#AC"}, | ||
{"Machine Check", "#MC"}, | ||
{"SIMD FP Exception", "#XM"}, | ||
{"Virtualization Exception", "#VE"}, | ||
{"Reserved", "N/A"}, | ||
{"Reserved", "N/A"}, | ||
{"Reserved", "N/A"}, | ||
{"Reserved", "N/A"}, | ||
{"Reserved", "N/A"}, | ||
{"Reserved", "N/A"}, | ||
{"Reserved", "N/A"}, | ||
{"Reserved", "N/A"}, | ||
{"Reserved", "N/A"}, | ||
{"Security Exception", "#SX"}, | ||
{"Reserved", "N/A"}, | ||
}; | ||
|
||
void arch::irq::eoi(int i) { | ||
if (i >= 32) { | ||
} | ||
|
||
smp::lapic_eoi(); | ||
} | ||
|
||
void arch::irq::enable(int num) { | ||
// if the interrupt is larger than 32, enable in the ioapic | ||
if (num >= 32) { | ||
smp::ioapicenable(num - 32, /* TODO */ 0); | ||
pic_enable(num - 32); | ||
} | ||
} | ||
|
||
void arch::irq::disable(int num) { | ||
// if the interrupt is larger than 32, disable in the ioapic | ||
if (num >= 32) { | ||
// smp::ioapicdisable(num); | ||
pic_disable(num - 32); | ||
} | ||
} | ||
|
||
static void mkgate(u32 *idt, u32 n, void *kva, u32 pl, u32 trap) { | ||
u64 addr = (u64)kva; | ||
n *= 4; | ||
trap = trap ? 0x8F00 : 0x8E00; // TRAP vs INTERRUPT gate; | ||
idt[n + 0] = (addr & 0xFFFF) | ((SEG_KCODE << 3) << 16); | ||
idt[n + 1] = (addr & 0xFFFF0000) | trap | ((pl & 0b11) << 13); // P=1 DPL=pl | ||
idt[n + 2] = addr >> 32; | ||
idt[n + 3] = 0; | ||
} | ||
|
||
// TODO: move to sched.cpp | ||
static void tick_handle(int i, struct task_regs *tf) { | ||
auto &cpu = cpu::current(); | ||
// increment the number of ticks | ||
cpu.ticks++; | ||
sched::handle_tick(cpu.ticks); | ||
return; | ||
} | ||
|
||
static void dbl_flt_handler(int i, struct task_regs *tf) { | ||
printk("DOUBLE FAULT!\n"); | ||
while (1) { | ||
} | ||
} | ||
|
||
static void unknown_exception(int i, struct task_regs *tf) { | ||
KERR("KERNEL PANIC\n"); | ||
KERR("CPU EXCEPTION: %s\n", excp_codes[tf->trapno][EXCP_NAME]); | ||
KERR("the system was running for %d ticks\n"); | ||
|
||
KERR("\n"); | ||
KERR("Stats for nerds:\n"); | ||
KERR("INT=%016zx ERR=%016zx\n", tf->trapno, tf->err); | ||
KERR("ESP=%016zx EIP=%016zx\n", tf->esp, tf->eip); | ||
KERR("CR2=%016zx CR3=%016zx\n", read_cr2(), read_cr3()); | ||
KERR("\n"); | ||
KERR("SYSTEM HALTED. File a bug report please:\n"); | ||
KERR(" repo: github.com/nickwanninger/chariot\n"); | ||
|
||
KERR("\n"); | ||
KERR("git = %s\n", GIT_REVISION); | ||
|
||
lidt(0, 0); // die | ||
while (1) { | ||
}; | ||
} | ||
|
||
static void unknown_hardware(int i, struct task_regs *tf) { | ||
printk("unknown! %d\n", i); | ||
} | ||
|
||
static void gpf_handler(int i, struct task_regs *tf) { | ||
// TODO: die | ||
KERR("pid %d, tid %d died from GPF @ %p (err=%p)\n", cpu::task()->pid, | ||
cpu::task()->tid, tf->eip, tf->err); | ||
sched::block(); | ||
} | ||
|
||
static void illegal_instruction_handler(int i, struct task_regs *tf) { | ||
// TODO: die | ||
auto pa = paging::get_physical(tf->eip & ~0xFFF); | ||
if (pa == 0) { | ||
return; | ||
} | ||
|
||
KERR("pid %d, tid %d died from illegal instruction @ %p\n", cpu::task()->pid, | ||
cpu::task()->tid, tf->eip); | ||
KERR(" ESP=%p\n", tf->esp); | ||
sched::block(); | ||
} | ||
|
||
extern "C" void syscall_handle(int i, struct task_regs *tf); | ||
|
||
#define PGFLT_PRESENT (1 << 0) | ||
#define PGFLT_WRITE (1 << 1) | ||
#define PGFLT_USER (1 << 2) | ||
#define PGFLT_RESERVED (1 << 3) | ||
#define PGFLT_INSTR (1 << 4) | ||
|
||
static void pgfault_handle(int i, struct task_regs *tf) { | ||
void *page = (void *)(read_cr2() & ~0xFFF); | ||
|
||
auto proc = cpu::proc(); | ||
|
||
if (!proc) { | ||
KERR("not in a proc while pagefaulting\n"); | ||
// lookup the kernel proc if we aren't in one! | ||
proc = task_process::lookup(0); | ||
} | ||
|
||
cpu::task()->tf = tf; | ||
|
||
if (proc) { | ||
int res = proc->mm.handle_pagefault((off_t)page, tf->err); | ||
if (res == -1) { | ||
// TODO: | ||
KERR("pid %d, tid %d segfaulted @ %p\n", proc->pid, cpu::task()->tid, | ||
tf->eip); | ||
KERR(" bad address = %p\n", read_cr2()); | ||
// XXX: just block, cause its an easy way to get the proc to stop running | ||
sched::block(); | ||
} | ||
|
||
} else { | ||
panic("page fault in kernel code (no proc)\n"); | ||
} | ||
} | ||
|
||
int arch::irq::init(void) { | ||
u32 *idt = (u32 *)&idt_block; | ||
|
||
// fill up the idt with the correct trap vectors. | ||
for (int n = 0; n < 130; n++) mkgate(idt, n, vectors[n], 0, 0); | ||
|
||
int i; | ||
for (i = 32; i < 48; i++) irq::disable(i); | ||
|
||
// handle all the <32 irqs as unknown | ||
for (i = 0; i < 32; i++) { | ||
::irq::install(i, unknown_exception, "Unknown Exception"); | ||
} | ||
|
||
for (i = 32; i < 48; i++) { | ||
// ::irq::install(i, unknown_hardware, "Unknown Hardware"); | ||
} | ||
|
||
::irq::install(TRAP_DBLFLT, dbl_flt_handler, "Double Fault"); | ||
::irq::install(TRAP_PGFLT, pgfault_handle, "Page Fault"); | ||
::irq::install(TRAP_GPFLT, gpf_handler, "General Protection Fault"); | ||
::irq::install(TRAP_ILLOP, illegal_instruction_handler, | ||
"Illegal Instruction Handler"); | ||
|
||
pic_disable(34); | ||
|
||
mkgate(idt, 32, vectors[32], 0, 0); | ||
::irq::install(32, tick_handle, "Preemption Tick"); | ||
|
||
mkgate(idt, 0x80, vectors[0x80], 3, 1); | ||
::irq::install(0x80, syscall_handle, "System Call"); | ||
|
||
KINFO("Registered basic interrupt handlers\n"); | ||
|
||
// and load the idt into the processor. It is a page in memory | ||
lidt(idt, 4096); | ||
|
||
return 0; | ||
} | ||
|
||
// just forward the trap on to the irq subsystem | ||
// This function is called from arch/x86/trap.asm | ||
extern "C" void trap(struct task_regs *regs) { | ||
irq::dispatch(regs->trapno, regs); | ||
irq::eoi(regs->trapno); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#include <arch.h> | ||
#include <cpu.h> | ||
#include <map.h> | ||
#include <string.h> | ||
|
||
#define NIRQS 130 | ||
|
||
static irq::handler irq_handlers[NIRQS]; | ||
|
||
// install and remove handlers | ||
int irq::install(int irq, irq::handler handler, const char *name) { | ||
if (irq < 0 || irq > NIRQS) return -1; | ||
irq_handlers[irq] = handler; | ||
arch::irq::enable(irq); | ||
|
||
return 0; | ||
} | ||
|
||
irq::handler irq::uninstall(int irq) { | ||
if (irq < 0 || irq > NIRQS) return nullptr; | ||
|
||
irq::handler old = irq_handlers[irq]; | ||
|
||
if (old != nullptr) { | ||
arch::irq::disable(irq); | ||
irq_handlers[irq] = nullptr; | ||
} | ||
return old; | ||
|
||
return old; | ||
} | ||
|
||
int irq::init(void) { | ||
if (!arch::irq::init()) return -1; | ||
return 0; | ||
} | ||
|
||
// cause an interrupt to be handled by the kernel's interrupt dispatcher | ||
void irq::dispatch(int irq, struct task_regs *regs) { | ||
// store the current register state in the CPU for introspection | ||
if (cpu::in_thread()) { | ||
cpu::task()->tf = regs; | ||
} | ||
|
||
auto handler = irq_handlers[irq]; | ||
|
||
if (handler != nullptr) { | ||
handler(irq, regs); | ||
} | ||
} |