Nucleus
Barry Improved context switching and interrupt handling d46e09a (3 years, 2 months ago)
diff --git a/include/nucleus/cpu.h b/include/nucleus/cpu.h
index b8293b8..b1e4cae 100644
--- a/include/nucleus/cpu.h
+++ b/include/nucleus/cpu.h
@@ -7,10 +7,13 @@ typedef unsigned int cpu_t;
/* Structure for an Interrupt Frame */
struct InterruptFrame {
- uint32_t eip, cs, eflags;
+ uint32_t ds;
+ uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
+ uint32_t intnum, err;
+ uint32_t eip, cs, eflags, useresp, ss;
};
-typedef void (*exc_handler_t)(struct InterruptFrame *, uint32_t);
+typedef void (*exc_handler_t)(struct InterruptFrame *);
typedef void (*int_handler_t)(struct InterruptFrame *);
extern int apic;
diff --git a/include/nucleus/task.h b/include/nucleus/task.h
index 17b5c09..1e91a33 100644
--- a/include/nucleus/task.h
+++ b/include/nucleus/task.h
@@ -104,6 +104,5 @@ void unblock_task(Task *task);
Task *find_task(pid_t tid);
void schedule(void);
pid_t clone(int flags);
-int syscall_handler(int num, uintptr_t args);
#endif
diff --git a/kernel/idt.c b/kernel/idt.c
index d835d15..a9f7c79 100644
--- a/kernel/idt.c
+++ b/kernel/idt.c
@@ -21,7 +21,7 @@ static struct IDTEntry {
uint16_t offsetHigher;
} __attribute__((packed)) *IDT;
-void (**exceptions)(struct InterruptFrame *, uint32_t);
+void (**exceptions)(struct InterruptFrame *);
void (**interrupts)(struct InterruptFrame *);
/* Install an IDT Entry */
@@ -35,294 +35,57 @@ install_idt_entry(uint8_t num, void *addr)
IDT[num].offsetHigher = (uintptr_t) addr >> 16;
}
-/* First level generic exception handler */
-static void
-exc_handler(int num, struct InterruptFrame *frame, uint32_t err)
-{
- asm volatile("cli");
-
- if (!exceptions[num]) {
- panic("Failed to handle exception %d (%#.8x)",
- num, err);
- }
-
- ASSERT(exceptions[num]);
- exceptions[num](frame, err);
-
- /* Send APIC EOI */
- if (apic)
- LAPIC(0xB0) = 0;
-
- asm volatile("sti");
-}
-
-/* First level generic interrupt handler */
-static void
-int_handler(int num, struct InterruptFrame *frame)
+/* Generic ISR handler */
+void
+isr_handler(struct InterruptFrame frame)
{
- asm volatile("cli");
+ if (!exceptions[frame.intnum] && frame.intnum < 32)
+ panic("[CPU#%d] Failed to handle exception %d (%#.8x) @ %#.8x",
+ CPUID, frame.intnum, frame.err, frame.eip);
- if (interrupts[num - 32])
- interrupts[num - 32](frame);
+ /* Run registered handler */
+ if (exceptions[frame.intnum])
+ exceptions[frame.intnum](&frame);
/* Send EOI */
- if (num >= 32 + 8)
+ if (frame.intnum >= 40 && frame.intnum < 48)
outb(0xA0, 0x20);
- outb(0x20, 0x20);
- /* Send APIC EOI*/
- if (apic)
- LAPIC(0xB0) = 0;
-
- asm volatile("sti");
-}
-
-/* Exceptions */
-__attribute__((interrupt))
-static void
-exc0(struct InterruptFrame *frame)
-{
- exc_handler(0, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc1(struct InterruptFrame *frame)
-{
- exc_handler(1, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc2(struct InterruptFrame *frame)
-{
- exc_handler(2, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc3(struct InterruptFrame *frame)
-{
- exc_handler(3, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc4(struct InterruptFrame *frame)
-{
- exc_handler(4, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc5(struct InterruptFrame *frame)
-{
- exc_handler(5, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc6(struct InterruptFrame *frame)
-{
- exc_handler(6, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc7(struct InterruptFrame *frame)
-{
- exc_handler(7, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc8(struct InterruptFrame *frame, uint32_t err)
-{
- exc_handler(8, frame, err);
-}
-__attribute__((interrupt))
-static void
-exc9(struct InterruptFrame *frame)
-{
- exc_handler(9, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc10(struct InterruptFrame *frame, uint32_t err)
-{
- exc_handler(10, frame, err);
-}
-__attribute__((interrupt))
-static void
-exc11(struct InterruptFrame *frame, uint32_t err)
-{
- exc_handler(11, frame, err);
-}
-__attribute__((interrupt))
-static void
-exc12(struct InterruptFrame *frame, uint32_t err)
-{
- exc_handler(12, frame, err);
-}
-__attribute__((interrupt))
-static void
-exc13(struct InterruptFrame *frame, uint32_t err)
-{
- exc_handler(13, frame, err);
-}
-__attribute__((interrupt))
-static void
-exc14(struct InterruptFrame *frame, uint32_t err)
-{
- exc_handler(14, frame, err);
-}
-__attribute__((interrupt))
-static void
-exc15(struct InterruptFrame *frame)
-{
- exc_handler(15, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc16(struct InterruptFrame *frame)
-{
- exc_handler(16, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc17(struct InterruptFrame *frame, uint32_t err)
-{
- exc_handler(17, frame, err);
-}
-__attribute__((interrupt))
-static void
-exc18(struct InterruptFrame *frame)
-{
- exc_handler(18, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc19(struct InterruptFrame *frame)
-{
- exc_handler(19, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc20(struct InterruptFrame *frame)
-{
- exc_handler(20, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc30(struct InterruptFrame *frame)
-{
- exc_handler(30, frame, 0);
-}
-__attribute__((interrupt))
-static void
-exc128(struct InterruptFrame *frame)
-{
- uintptr_t esi, *eax;
- asm volatile(
- "mov %%esi, %0;"
- "lea -12(%%ebp), %1;"
- : "=r" (esi), "=r" (eax));
- *eax = syscall_handler(*eax, esi);
+ if (frame.intnum >= 32 && frame.intnum < 48)
+ outb(0x20, 0x20);
/* Send APIC EOI */
if (apic)
LAPIC(0xB0) = 0;
}
-/* Interrupts */
-__attribute__((interrupt))
-static void
-int0(struct InterruptFrame *frame)
-{
- int_handler(32, frame);
-}
-__attribute__((interrupt))
-static void
-int1(struct InterruptFrame *frame)
-{
- int_handler(33, frame);
-}
-__attribute__((interrupt))
-static void
-int2(struct InterruptFrame *frame)
-{
- int_handler(34, frame);
-}
-__attribute__((interrupt))
-static void
-int3(struct InterruptFrame *frame)
-{
- int_handler(35, frame);
-}
-__attribute__((interrupt))
-static void
-int4(struct InterruptFrame *frame)
-{
- int_handler(36, frame);
-}
-__attribute__((interrupt))
-static void
-int5(struct InterruptFrame *frame)
-{
- int_handler(37, frame);
-}
-__attribute__((interrupt))
-static void
-int6(struct InterruptFrame *frame)
-{
- int_handler(38, frame);
-}
-__attribute__((interrupt))
-static void
-int7(struct InterruptFrame *frame)
-{
- int_handler(39, frame);
-}
-__attribute__((interrupt))
-static void
-int8(struct InterruptFrame *frame)
-{
- int_handler(40, frame);
-}
-__attribute__((interrupt))
-static void
-int9(struct InterruptFrame *frame)
-{
- int_handler(41, frame);
-}
-__attribute__((interrupt))
-static void
-int10(struct InterruptFrame *frame)
-{
- int_handler(42, frame);
-}
-__attribute__((interrupt))
-static void
-int11(struct InterruptFrame *frame)
-{
- int_handler(43, frame);
-}
-__attribute__((interrupt))
-static void
-int12(struct InterruptFrame *frame)
-{
- int_handler(44, frame);
-}
-__attribute__((interrupt))
-static void
-int13(struct InterruptFrame *frame)
-{
- int_handler(45, frame);
-}
-__attribute__((interrupt))
-static void
-int14(struct InterruptFrame *frame)
+/* Register an exception handler */
+void
+register_exception(int num, exc_handler_t addr)
{
- int_handler(46, frame);
+ if ((num >= 0 && num < 32) || num >= 48)
+ exceptions[num] = addr;
}
-__attribute__((interrupt))
-static void
-int15(struct InterruptFrame *frame)
+
+/* Register an interrupt handler */
+void
+register_interrupt(int num, int_handler_t addr)
{
- int_handler(47, frame);
+ if (num >= 0 && num < 16)
+ interrupts[num] = addr;
}
+/* Exceptions */
+extern char exc0[], exc1[], exc2[], exc3[], exc4[], exc5[], exc6[],
+ exc7[], exc8[], exc9[], exc10[], exc11[], exc12[], exc13[],
+ exc14[], exc15[], exc16[], exc17[], exc18[], exc19[], exc20[],
+ exc30[];
+/* Interrutps */
+extern char exc32[], exc33[], exc34[], exc35[], exc36[], exc37[], exc38[],
+ exc39[], exc40[], exc41[], exc42[], exc43[], exc44[], exc45[],
+ exc46[], exc47[];
+/* System Call */
+extern char exc128[];
+
/* Initialise the IDT */
void
init_idt(void)
@@ -354,22 +117,22 @@ init_idt(void)
install_idt_entry(30, exc30);
/* Install interrupts */
- install_idt_entry(32, int0);
- install_idt_entry(33, int1);
- install_idt_entry(34, int2);
- install_idt_entry(35, int3);
- install_idt_entry(36, int4);
- install_idt_entry(37, int5);
- install_idt_entry(38, int6);
- install_idt_entry(39, int7);
- install_idt_entry(40, int8);
- install_idt_entry(41, int9);
- install_idt_entry(42, int10);
- install_idt_entry(43, int11);
- install_idt_entry(44, int12);
- install_idt_entry(45, int13);
- install_idt_entry(46, int14);
- install_idt_entry(47, int15);
+ install_idt_entry(32, exc32);
+ install_idt_entry(33, exc33);
+ install_idt_entry(34, exc34);
+ install_idt_entry(35, exc35);
+ install_idt_entry(36, exc36);
+ install_idt_entry(37, exc37);
+ install_idt_entry(38, exc38);
+ install_idt_entry(39, exc39);
+ install_idt_entry(40, exc40);
+ install_idt_entry(41, exc41);
+ install_idt_entry(42, exc42);
+ install_idt_entry(43, exc43);
+ install_idt_entry(44, exc44);
+ install_idt_entry(45, exc45);
+ install_idt_entry(46, exc46);
+ install_idt_entry(47, exc47);
/* Install system call handler */
install_idt_entry(128, exc128);
@@ -389,19 +152,3 @@ cpu_load_idt(void)
};
asm volatile("lidt %0" :: "m" (ptr));
}
-
-/* Register an exception handler */
-void
-register_exception(int num, exc_handler_t addr)
-{
- if (num >= 0 && num < 32)
- exceptions[num] = addr;
-}
-
-/* Register an interrupt handler */
-void
-register_interrupt(int num, int_handler_t addr)
-{
- if (num >= 0 && num < 224)
- interrupts[num] = addr;
-}
diff --git a/kernel/isr.S b/kernel/isr.S
new file mode 100644
index 0000000..c807e7b
--- /dev/null
+++ b/kernel/isr.S
@@ -0,0 +1,94 @@
+/*
+ * This file contains the interrupt service routine stubs. For the most part
+ * they just call into the generic handlers after setting up the stack for them.
+ */
+
+.extern isr_handler
+
+/* Exception with an error */
+.macro exc_err num
+.globl exc\num
+exc\num:
+ cli
+ push $\num
+ jmp isr_stub
+.endm
+/* Exception without an error */
+.macro exc_noerr num
+.globl exc\num
+exc\num:
+ cli
+ push $0
+ push $\num
+ jmp isr_stub
+.endm
+
+/* Exceptions */
+EXC_NOERR 0
+EXC_NOERR 1
+EXC_NOERR 2
+EXC_NOERR 3
+EXC_NOERR 4
+EXC_NOERR 5
+EXC_NOERR 6
+EXC_NOERR 7
+EXC_ERR 8
+EXC_NOERR 9
+EXC_ERR 10
+EXC_ERR 11
+EXC_ERR 12
+EXC_ERR 13
+EXC_ERR 14
+EXC_NOERR 15
+EXC_NOERR 16
+EXC_ERR 17
+EXC_NOERR 18
+EXC_NOERR 19
+EXC_NOERR 20
+EXC_NOERR 30
+/* Interrupts */
+EXC_NOERR 32
+EXC_NOERR 33
+EXC_NOERR 34
+EXC_NOERR 35
+EXC_NOERR 36
+EXC_NOERR 37
+EXC_NOERR 38
+EXC_NOERR 39
+EXC_NOERR 40
+EXC_NOERR 41
+EXC_NOERR 42
+EXC_NOERR 43
+EXC_NOERR 44
+EXC_NOERR 45
+EXC_NOERR 46
+EXC_NOERR 47
+/* System Call */
+EXC_NOERR 128
+
+/* Generic ISR stub */
+isr_stub:
+ pusha
+ mov %ds, %ax
+ pushl %eax
+
+ /* Switch to kernel segments */
+ mov $0x10, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+
+ call isr_handler
+
+ /* Restore original segments */
+ popl %ebx
+ mov %bx, %ds
+ mov %bx, %es
+ mov %bx, %fs
+ mov %bx, %gs
+
+ popa
+ addl $8, %esp
+ sti
+ iret
diff --git a/kernel/main.c b/kernel/main.c
index 5d95c4a..bf1fd8c 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -18,7 +18,7 @@
#include "acpi/acpi.h"
extern char _bss[], _end[];
-void page_fault_handler(struct InterruptFrame *frame, uint32_t err);
+void page_fault_handler(struct InterruptFrame *frame);
/* Per-CPU Setup */
void
diff --git a/kernel/panic.c b/kernel/panic.c
index a489a0a..2c07d24 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -30,8 +30,6 @@ panic(char *fmt, ...)
outb(0xE9, '\n');
/* Halt processor */
- asm volatile("cli");
- while (1)
- asm volatile("hlt");
+ while (1) asm volatile("cli; hlt");
__builtin_unreachable();
}
diff --git a/memory/fault.c b/memory/fault.c
index e916368..5f94b55 100644
--- a/memory/fault.c
+++ b/memory/fault.c
@@ -44,9 +44,6 @@ copy_on_write(VMRegion *region, uintptr_t addr)
inode = back->inode;
page = find_page(inode->pages, offset);
}
- if (!page) {
- panic("no page! %#.8x = %#.8x", addr, get_page(addr));
- }
ASSERT(page);
/* Determine what to do */
@@ -174,21 +171,21 @@ not_present_write(VMRegion *region, uintptr_t addr)
/* Page fault handler */
void
-page_fault_handler(struct InterruptFrame *frame, uint32_t err)
+page_fault_handler(struct InterruptFrame *frame)
{
uintptr_t addr;
asm volatile("mov %%cr2, %0" : "=r" (addr));
- uint8_t present = err & (1 << 0);
- uint8_t write = err & (1 << 1);
- uint8_t user = err & (1 << 2);
+ uint8_t present = frame->err & (1 << 0);
+ uint8_t write = frame->err & (1 << 1);
+ uint8_t user = frame->err & (1 << 2);
/* Iterate VM Regions */
VMRegion *region = find_region(addr);
/* Not in a region */
if (__builtin_expect(!region, 0)) {
page_t pg = get_page(addr);
- panic("Page Fault [%d:%d] (%#.8x -> %#.8x [tbl:%d, pg:%d][%#.8x], %s, %s, %s)",
- current->tgid, current->tid, frame->eip,
+ panic("[CPU#%d] Page Fault [%d:%d] (%#.8x -> %#.8x [tbl:%d, pg:%d][%#.8x], %s, %s, %s)",
+ CPUID, current->tgid, current->tid, frame->eip,
addr, (addr >> 12) / 1024, (addr >> 12) % 1024, pg,
present ? "present" : "not present",
write ? "write" : "read",
@@ -208,13 +205,13 @@ page_fault_handler(struct InterruptFrame *frame, uint32_t err)
/* Early (pre-VFS/tasking) page fault handler */
void
-early_page_fault_handler(struct InterruptFrame *frame, uint32_t err)
+early_page_fault_handler(struct InterruptFrame *frame)
{
uintptr_t addr;
asm volatile("mov %%cr2, %0" : "=r" (addr));
- uint8_t present = err & (1 << 0);
- uint8_t write = err & (1 << 1);
- uint8_t user = err & (1 << 2);
+ uint8_t present = frame->err & (1 << 0);
+ uint8_t write = frame->err & (1 << 1);
+ uint8_t user = frame->err & (1 << 2);
if (!PAGE_ADDR(addr))
panic("Null dereference @ %#.8x (CPU#%d)", frame->eip, CPUID);
ASSERT(!present);
diff --git a/memory/paging.h b/memory/paging.h
index 284d0e3..eb7c75f 100644
--- a/memory/paging.h
+++ b/memory/paging.h
@@ -7,6 +7,6 @@
extern page_t zeroFrame;
-void early_page_fault_handler(struct InterruptFrame *frame, uint32_t err);
+void early_page_fault_handler(struct InterruptFrame *frame);
#endif
diff --git a/task/clone.c b/task/clone.c
index fe0ad88..93c0b94 100644
--- a/task/clone.c
+++ b/task/clone.c
@@ -62,9 +62,12 @@ clone(int flags)
child->pageDir = clone_dir();
/* Split tasks here */
- child->eip = (uintptr_t) &&end;
+ asm volatile("mov %%esi, %0" : "=r" (child->esi));
+ asm volatile("mov %%edi, %0" : "=r" (child->edi));
+ asm volatile("mov %%ebx, %0" : "=r" (child->ebx));
asm volatile("mov %%esp, %0" : "=r" (child->esp));
asm volatile("mov %%ebp, %0" : "=r" (child->ebp));
+ child->eip = (uintptr_t) &&end;
add(readyQueue[child->priority], child);
tid = child->tid;
put(child);
diff --git a/task/scheduler.c b/task/scheduler.c
index 485301e..b35192c 100644
--- a/task/scheduler.c
+++ b/task/scheduler.c
@@ -11,6 +11,10 @@
#define PRIORITY_COUNT 6
+void context_switch(uintptr_t eip, page_dir_t pagedir,
+ uintptr_t esi, uintptr_t edi, uintptr_t ebx,
+ uintptr_t ebp, uintptr_t esp);
+
ObjectList *readyQueue[PRIORITY_COUNT];
/* Switch to a task */
@@ -20,6 +24,9 @@ switch_to_task(Task *task)
/* Save current task state */
if (__builtin_expect(!!current, 1)) {
lock(current);
+ asm volatile("mov %%esi, %0" : "=r" (current->esi));
+ asm volatile("mov %%edi, %0" : "=r" (current->edi));
+ asm volatile("mov %%ebx, %0" : "=r" (current->ebx));
asm volatile("mov %%esp, %0" : "=r" (current->esp));
asm volatile("mov %%ebp, %0" : "=r" (current->ebp));
current->eip = (uintptr_t) &&end;
@@ -28,22 +35,9 @@ switch_to_task(Task *task)
}
/* Switch to new context */
- uintptr_t eip, esp, ebp;
current = task; /* Given reference, so no get() */
- eip = current->eip;
- esp = current->esp;
- ebp = current->ebp;
- asm volatile (
- "cli;"
- "movl %0, %%ecx;"
- "movl %1, %%esp;"
- "movl %2, %%ebp;"
- "movl %3, %%cr3;"
- "sti;"
- "jmp *%%ecx"
- :: "g" (eip), "g" (esp),
- "g" (ebp), "g" (current->pageDir)
- );
+ context_switch(current->eip, current->pageDir, current->esi,
+ current->edi, current->ebx, current->ebp, current->esp);
end:
/* This prevents GCC from optimising the jump to be after the return */
asm volatile("":::"memory");
diff --git a/task/switch.S b/task/switch.S
new file mode 100644
index 0000000..5ef5437
--- /dev/null
+++ b/task/switch.S
@@ -0,0 +1,21 @@
+/*
+ * This file contains the context switch routine. This routine will switch the
+ * current context, but will not save any state, so it should only be called by
+ * the scheduler. Since the stack and page directory will get switched, care
+ * must be taken to read and set the registers in the correct order.
+ */
+
+/* Perform a context switch */
+.globl context_switch
+context_switch:
+ cli
+ mov 4(%esp), %ecx
+ mov 8(%esp), %eax
+ mov 12(%esp), %esi
+ mov 16(%esp), %edi
+ mov 20(%esp), %ebx
+ mov 24(%esp), %ebp
+ mov 28(%esp), %esp
+ mov %eax, %cr3
+ sti
+ jmp *%ecx
diff --git a/task/syscall.c b/task/syscall.c
index e0b2d23..8e33ef4 100644
--- a/task/syscall.c
+++ b/task/syscall.c
@@ -87,9 +87,11 @@ struct SyscallEntry syscalls[] = {
};
/* Handle a syscall */
-int
-syscall_handler(int num, uintptr_t args)
+void
+syscall_handler(struct InterruptFrame *frame)
{
+ int num = frame->eax;
+ uintptr_t args = frame->esi;
int ret = -EINVAL;
enter_syscall_context(num);
@@ -115,5 +117,5 @@ syscall_handler(int num, uintptr_t args)
end:
exit_syscall_context();
- return ret;
+ frame->eax = ret;
}
diff --git a/task/task.c b/task/task.c
index 597df30..69af014 100644
--- a/task/task.c
+++ b/task/task.c
@@ -14,6 +14,7 @@
void init_scheduler(void);
void timer_handler(struct InterruptFrame *frame);
+void syscall_handler(struct InterruptFrame *frame);
static void task_new(Object *);
static void task_delete(Object *);
@@ -110,7 +111,7 @@ init_tasking(void)
init_scheduler();
register_interrupt(0, timer_handler);
-
+ register_exception(128, syscall_handler);
}
/* Get the current task's PID */