Orion
Barry Importing existing Orion kernel d41a53c (3 years, 2 months ago)
diff --git a/proc/apic.c b/proc/apic.c
new file mode 100644
index 0000000..a784f50
--- /dev/null
+++ b/proc/apic.c
@@ -0,0 +1,258 @@
+/*
+ * This file contains code dealing with the Advanced Programmable Interrupt
+ * Controller system. It should proved a drop-in replacement for the standard
+ * PIC system, abstracting it in the same way. It should also be able to handle
+ * a non-SMP system, and fall back on the PIC.
+ */
+
+#include <stdint.h>
+#include "proc.h"
+#include "../mem/heap.h"
+#include "../mem/mem.h"
+#include "../task/task.h"
+#include "../io.h"
+#include "../screen.h"
+#include "../spinlock.h"
+
+/* RSDP Descriptor */
+typedef struct RSDPDescriptor {
+ char signature[8];
+ uint8_t checksum;
+ char oem[6];
+ uint8_t revision;
+ uint32_t rsdt;
+} __attribute__((packed)) RSDPDescriptor;
+
+/* System Descriptor Table Header */
+typedef struct SDTHeader {
+ char signature[4];
+ uint32_t length;
+ uint8_t revision;
+ uint8_t checksum;
+ char oem[6];
+ char oemTable[8];
+ uint32_t oemRevision;
+ uint32_t creator;
+ uint32_t creatorRevision;
+} SDTHeader;
+
+/* IOAPIC Interrupt Source Override */
+typedef struct IOAPICOverride {
+ uint8_t type, length;
+ uint8_t bus, irq;
+ uint8_t gsi, flags;
+} IOAPICOverride;
+
+void cpu_load(void);
+extern void ap_trampoline(void);
+
+uint8_t numCores;
+uint8_t bspId;
+uint32_t lapicPtr, ioapicPtr;
+uint8_t lapicIds[MAX_CPUS], lapicNums[MAX_CPUS];
+Spinlock timerLock;
+
+/* Enable APIC */
+static void
+enable_apic(void)
+{
+ LAPIC(0xF0) = 0x1FF;
+ LAPIC(0x80) = 0;
+}
+
+/* Read from the IOAPIC */
+static uint32_t
+read_io_apic(uint32_t reg)
+{
+ uint32_t volatile *ioapic = (uint32_t volatile *) ioapicPtr;
+ ioapic[0] = (reg & 0xFF);
+ return ioapic[4];
+}
+
+/* Write to the IOAPIC */
+static void
+write_io_apic(uint32_t reg, uint32_t value)
+{
+ uint32_t volatile *ioapic = (uint32_t volatile *) ioapicPtr;
+ ioapic[0] = (reg & 0xFF);
+ ioapic[4] = value;
+}
+
+/* Delay in gaps of 100 microseconds */
+static void
+delay(uint32_t time)
+{
+ float calc = (1193182 / 10000) * time;
+ uint32_t divisor = (uint32_t) calc;
+ outb(0x43, 0x30);
+ outb(0x40, divisor & 0xFF);
+ outb(0x40, (divisor >> 8) & 0xFF);
+ do outb(0x43, 0xE2); while (!(inb(0x40) & (1 << 7)));
+}
+
+/* Start the APIC timer */
+static void
+apic_start_timer(void)
+{
+ /* Don't run while another timer is calibrating */
+ acquire(&timerLock);
+
+ /* Start LAPIC countdown from -1 */
+ LAPIC(0x3E0) = 3;
+ LAPIC(0x380) = 0xFFFFFFFF;
+ delay(10);
+
+ /* Stop after PIT fires, count LAPIC ticks */
+ LAPIC(0x320) = 0x10000;
+ uint32_t ticks = 0xFFFFFFFF - LAPIC(0x390);
+
+ /* Tick every 1ms */
+ LAPIC(0x320) = 0x20000 | 32;
+ LAPIC(0x3E0) = 3;
+ LAPIC(0x380) = ticks;
+
+ release(&timerLock);
+}
+
+/* Initialise SMP (RSDT method) */
+void
+init_multicore(void *ebda)
+{
+ numCores = 1;
+ memset(lapicIds, 0, MAX_CPUS);
+ memset(lapicNums, 0, MAX_CPUS);
+
+ init_lock(&timerLock);
+
+ /* Search for SMP tables, start from EBDA start */
+ char *signature = (char *) ebda;
+ uint8_t found;
+ while (signature < (char *) 0x100000) {
+ /* RSDT */
+ if (!memcmp(signature, "RSD PTR ", 8)) {
+ found = 1;
+ break;
+ }
+ signature++;
+ }
+ /* Fall back to PIC */
+ if (!found)
+ return;
+ /* RSDT checksum */
+ uint32_t check = 0, i, j;
+ for (i = 0; i < sizeof(RSDPDescriptor); i++)
+ check += signature[i];
+ if (check % 0x100)
+ return;
+
+ uint8_t *ptr, *ptr2;
+ uint8_t *rsdt, len;
+ uint8_t overrides[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+ IOAPICOverride *iso;
+
+ /* Find APIC table */
+ kprintf("Searching for APIC table");
+ asm volatile("mov $1, %%eax; cpuid; shrl $24, %%ebx" : "=b" (bspId));
+ numCores = 0;
+ rsdt = (uint8_t *) ((RSDPDescriptor *) signature)->rsdt;
+ len = *((uint32_t *) (rsdt + 4));
+ for (ptr2 = rsdt + 36; ptr2 < rsdt + len; ptr2 += 4) {
+ ptr = *((uint8_t **) ptr2);
+ if (!memcmp(ptr, "APIC", 4))
+ break;
+ }
+ if (memcmp(ptr, "APIC", 4))
+ return;
+
+ /* Read APIC table */
+ lapicPtr = *((uint32_t *) (ptr + 0x24));
+ ptr2 = ptr + *((uint32_t *) (ptr + 4));
+ for (ptr += 44; ptr < ptr2; ptr += ptr[1]) {
+ /* Entries */
+ switch (ptr[0]) {
+ case 0: /* Processor Local APIC */
+ if (numCores >= MAX_CPUS)
+ continue;
+ if (ptr[4] & 1) {
+ lapicNums[ptr[3]] = numCores;
+ lapicIds[numCores] = ptr[3];
+ numCores++;
+ }
+ break;
+ case 1: /* IOAPIC */
+ ioapicPtr = *((uint32_t *) (ptr + 4));
+ break;
+ case 2: /* IOAPIC ISO */
+ iso = (void *) ptr;
+ overrides[iso->irq] = iso->gsi;
+ kprintf(" IRQ#%.2d -> GSI#%.2d",
+ iso->irq, iso->gsi);
+ break;
+ }
+ }
+
+ /* Send INT#1 to IRQ#33 on CPU#0 */
+ write_io_apic(0x10 + (2 * overrides[1] + 0), 33);
+ write_io_apic(0x10 + (2 * overrides[1] + 1), bspId);
+
+ kprintf("Detected %d CPU%c", numCores, (numCores>1)?'s':'\0');
+
+ /* Initialise APs */
+ uintptr_t apTrampoline = 0x2000;
+ memcpy((void *) apTrampoline, &ap_trampoline, 0x1000);
+ for (i = 0; i < numCores; i++) {
+ if (lapicIds[i] == bspId)
+ continue;
+ /* Give each CPU a separate stack */
+ *((uint32_t *) (apTrampoline + 0xFF0)) = 0x7A000 - (i * 0x2000);
+ /* Send INIT IPI */
+ LAPIC(0x280) = 0;
+ LAPIC(0x310) = (LAPIC(0x310) & 0x00FFFFFF)
+ | (lapicIds[i] << 24);
+ LAPIC(0x300) = (LAPIC(0x300) & 0xFFF00000)
+ | 0xC500;
+ do {
+ asm("pause" ::: "memory");
+ } while (LAPIC(0x300) & (1 << 12));
+ LAPIC(0x310) = (LAPIC(0x310) & 0x00FFFFFF)
+ | (lapicIds[i] << 24);
+ LAPIC(0x300) = (LAPIC(0x300) & 0xFFF00000)
+ | 0x8500;
+ do {
+ asm("pause" ::: "memory");
+ } while (LAPIC(0x300) & (1 << 12));
+ delay(100);
+ /* Send STARTUP IPI (twice) */
+ for (j = 0; j < 2; j++) {
+ LAPIC(0x280) = 0;
+ LAPIC(0x310) = (LAPIC(0x310) & 0x00FFFFFF)
+ | (lapicIds[i] << 24);
+ LAPIC(0x300) = (LAPIC(0x300) & 0xFFF0F800)
+ | (0x0600 + ((apTrampoline >> 12) & 0xFF));
+ delay(2);
+ do {
+ asm("pause" ::: "memory");
+ } while (LAPIC(0x300) & (1 << 12));
+ }
+ }
+
+ /* Enable APIC */
+ enable_apic();
+ /* Disable PIC */
+ outb(0x21, 0xFF);
+ outb(0xA1, 0xFF);
+
+ apic_start_timer();
+}
+
+/* AP initialisation routine */
+void
+ap_startup(void)
+{
+// enable_apic();
+ cpu_load();
+// apic_start_timer();
+ current = NULL;
+ kprintf("CPU#%d idling", CPUID);
+ while (1) asm volatile("hlt");
+}
diff --git a/proc/gdt.c b/proc/gdt.c
new file mode 100644
index 0000000..6d9a7c7
--- /dev/null
+++ b/proc/gdt.c
@@ -0,0 +1,104 @@
+/*
+ * This file deals with the Global Descriptor Table. It creates a simple GDT,
+ * suitable for use in the Kernel, and attaches a TSS to the end. The TSS,
+ * which can be used to switch from ring 3 to ring 0, allows processes access to
+ * all IO ports.
+ */
+
+#include <stdint.h>
+#include "../mem/heap.h"
+#include "../mem/mem.h"
+#include "../mem/frame.h"
+#include "../proc/proc.h"
+#include "../screen.h"
+
+#define GDT_ENTRIES 6
+
+/* Structure for GDT entry */
+static struct GDTEntry {
+ uint16_t limitLower, baseLower;
+ uint8_t baseMiddle, access, gran, baseHigher;
+} __attribute__((packed)) *GDT;
+
+/* Structure for TSS entry */
+static struct TSSEntry {
+ uint32_t prevTSS;
+ uint32_t esp0, ss0;
+ uint32_t esp1, ss1;
+ uint32_t esp2, ss2;
+ uint32_t cr3, eip, eflags;
+ uint32_t eax, ecx, edx, ebx;
+ uint32_t esp, ebp, esi, edi;
+ uint32_t es, cs, ss, ds, fs, gs;
+ uint32_t ldt;
+ uint16_t trap, iomapBase;
+// uint32_t iomap[2048];
+} __attribute__((packed)) *TSS; /* Per CPU */
+
+void load_gdt(uint32_t gdtPtr);
+void load_tss(void);
+
+/* Set a gate of the GDT */
+static void
+gdt_set_gate(uint8_t num, uint32_t base, uint32_t limit,
+ uint8_t access, uint8_t gran)
+{
+ GDT[num].baseLower = (base & 0xFFFF);
+ GDT[num].baseMiddle = (base >> 16) & 0xFF;
+ GDT[num].baseHigher = (base >> 24) & 0xFF;
+ GDT[num].limitLower = (limit & 0xFFFF);
+ GDT[num].gran = (limit >> 16) & 0x0F;
+ GDT[num].gran |= gran & 0xF0;
+ GDT[num].access = access;
+}
+
+/* Create a TSS */
+static void
+write_tss(uint8_t num, uint32_t esp)
+{
+ uint32_t base = (uint32_t) TSS;
+ uint32_t limit = sizeof(struct TSSEntry) - 1;
+
+ gdt_set_gate(num, base, limit, 0xE9, 0x00);
+ num -= 5;
+ memset(&TSS[num], 0, sizeof(struct TSSEntry));
+ TSS[num].ss0 = 0x10;
+ TSS[num].esp0 = esp;
+ TSS[num].cs = 0x08 | 0;
+ TSS[num].ds = TSS[num].es = TSS[num].ss = 0x10 | 0;
+ TSS[num].fs = TSS[num].gs = 0x10 | 3;
+ TSS[num].iomapBase = 104;
+}
+
+/* Initialise the Kernel GDT */
+void
+init_gdt(void)
+{
+ GDT = (void *) alloc_frames(1);
+ TSS = (void *) (GDT + GDT_ENTRIES);
+ kprintf("Loaded GDT @ %#.8x, TSS @ %#.8x", GDT, TSS);
+}
+
+/* Load the Kernel GDT */
+void
+cpu_load_gdt(void)
+{
+ memset(GDT, 0, sizeof(GDT[0]) * GDT_ENTRIES);
+
+ gdt_set_gate(0, 0x00000000, 0x00000000, 0x00, 0x00); /* Null segment */
+ /* Ring 0 */
+ gdt_set_gate(1, 0x00000000, 0xFFFFFFFF, 0x9A, 0xCF); /* Code segment */
+ gdt_set_gate(2, 0x00000000, 0xFFFFFFFF, 0x92, 0xCF); /* Data segment */
+ /* Ring 3 */
+ gdt_set_gate(3, 0x00000000, 0xFFFFFFFF, 0xFA, 0xCF); /* Code segment */
+ gdt_set_gate(4, 0x00000000, 0xFFFFFFFF, 0xF2, 0xCF); /* Data segment */
+ /* TSS */
+ write_tss(5, 0xF0800000 - sizeof(uintptr_t));
+
+ uint16_t gdtPtr[3];
+ gdtPtr[0] = (sizeof(GDT[0]) * GDT_ENTRIES) - 1;
+ gdtPtr[1] = ((uint32_t) GDT) & 0xFFFF;
+ gdtPtr[2] = ((uint32_t) GDT) >> 16;
+ load_gdt((uint32_t) &gdtPtr);
+ load_tss();
+}
diff --git a/proc/idt.c b/proc/idt.c
new file mode 100644
index 0000000..a1552c2
--- /dev/null
+++ b/proc/idt.c
@@ -0,0 +1,148 @@
+/*
+ * This file deals with the Interrupt Descriptor Table. It creates a simple
+ * IDT, with hard-coded handler functions. It is these handler functions that
+ * can run custom handlers, registered at runtime. This file does not deal with
+ * dispatching messages/signals to processes on an interrupt.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "proc.h"
+#include "../mem/heap.h"
+#include "../mem/frame.h"
+#include "../screen.h"
+
+/* Structure for IDT entry */
+static struct IDTEntry {
+ uint16_t offsetLower, selector;
+ uint8_t zero, typeAttr;
+ uint16_t offsetHigher;
+} __attribute__((packed)) *IDT;
+
+void load_idt(uint32_t ptr[2]);
+/* Exceptions */
+void exc0(void);
+void exc1(void);
+void exc2(void);
+void exc3(void);
+void exc4(void);
+void exc5(void);
+void exc6(void);
+void exc7(void);
+void exc8(void);
+void exc9(void);
+void exc10(void);
+void exc11(void);
+void exc12(void);
+void exc13(void);
+void exc14(void);
+void exc15(void);
+void exc16(void);
+void exc17(void);
+void exc18(void);
+void exc19(void);
+void exc20(void);
+void exc30(void);
+void exc128(void); /* syscall */
+/* Interrutps */
+void irq0(void);
+void irq1(void);
+void irq2(void);
+void irq3(void);
+void irq4(void);
+void irq5(void);
+void irq6(void);
+void irq7(void);
+void irq8(void);
+void irq9(void);
+void irq10(void);
+void irq11(void);
+void irq12(void);
+void irq13(void);
+void irq14(void);
+void irq15(void);
+
+extern void (**exceptions)(InterruptFrame *);
+extern void (**interrupts)(InterruptFrame *);
+
+/* Install an IDT entry */
+static void
+install_idt_entry(uint8_t num, uint32_t addr)
+{
+ IDT[num].offsetLower = addr & 0xFFFF;
+ IDT[num].selector = 0x08;
+ IDT[num].zero = 0;
+ IDT[num].typeAttr = 0x8E | 0x60; /* Allowed from ring 3 */
+ IDT[num].offsetHigher = addr >> 16;
+}
+
+/* Initialise the IDT */
+void
+init_idt(void)
+{
+ IDT = (void *) alloc_frames(1);
+
+ /* Install exceptions */
+ install_idt_entry(0, (uint32_t) exc0);
+ install_idt_entry(1, (uint32_t) exc1);
+ install_idt_entry(2, (uint32_t) exc2);
+ install_idt_entry(3, (uint32_t) exc3);
+ install_idt_entry(4, (uint32_t) exc4);
+ install_idt_entry(5, (uint32_t) exc5);
+ install_idt_entry(6, (uint32_t) exc6);
+ install_idt_entry(7, (uint32_t) exc7);
+ install_idt_entry(8, (uint32_t) exc8);
+ install_idt_entry(9, (uint32_t) exc9);
+ install_idt_entry(10, (uint32_t) exc10);
+ install_idt_entry(11, (uint32_t) exc11);
+ install_idt_entry(12, (uint32_t) exc12);
+ install_idt_entry(13, (uint32_t) exc13);
+ install_idt_entry(14, (uint32_t) exc14);
+ install_idt_entry(15, (uint32_t) exc15);
+ install_idt_entry(16, (uint32_t) exc16);
+ install_idt_entry(17, (uint32_t) exc17);
+ install_idt_entry(18, (uint32_t) exc18);
+ install_idt_entry(19, (uint32_t) exc19);
+ install_idt_entry(20, (uint32_t) exc20);
+ install_idt_entry(30, (uint32_t) exc30);
+
+ /* Install interrutps */
+ install_idt_entry(32, (uint32_t) irq0);
+ install_idt_entry(33, (uint32_t) irq1);
+ install_idt_entry(34, (uint32_t) irq2);
+ install_idt_entry(35, (uint32_t) irq3);
+ install_idt_entry(36, (uint32_t) irq4);
+ install_idt_entry(37, (uint32_t) irq5);
+ install_idt_entry(38, (uint32_t) irq6);
+ install_idt_entry(39, (uint32_t) irq7);
+ install_idt_entry(40, (uint32_t) irq8);
+ install_idt_entry(41, (uint32_t) irq9);
+ install_idt_entry(42, (uint32_t) irq10);
+ install_idt_entry(43, (uint32_t) irq11);
+ install_idt_entry(44, (uint32_t) irq12);
+ install_idt_entry(45, (uint32_t) irq13);
+ install_idt_entry(46, (uint32_t) irq14);
+ install_idt_entry(47, (uint32_t) irq15);
+
+ /* Install syscall handler */
+ install_idt_entry(128, (uint32_t) exc128);
+
+ exceptions = (void (**)(InterruptFrame *)) (IDT + 256);
+ interrupts = (void (**)(InterruptFrame *)) (exceptions + 32);
+ memset(exceptions, 0, 1024);
+
+ kprintf("Loaded IDT @ %#.8x, Exceptions @ %#.8x, Interrupts @ %#.8x",
+ IDT, exceptions, interrupts);
+}
+
+/* Load the IDT */
+void
+cpu_load_idt(void)
+{
+ uint32_t idtAddr, idtPtr[2];
+ idtAddr = (uint32_t) IDT;
+ idtPtr[0] = sizeof(struct IDTEntry) * 256;
+ idtPtr[0] += (idtAddr & 0xFFFF) << 16;
+ idtPtr[1] = idtAddr >> 16;
+ load_idt(idtPtr);
+}
diff --git a/proc/irqs.c b/proc/irqs.c
new file mode 100644
index 0000000..f9a523c
--- /dev/null
+++ b/proc/irqs.c
@@ -0,0 +1,80 @@
+/*
+ * This file contains the abstraction for exceptions and interrupts. It
+ * presents a system of registrable exception/interrupt handlers. It also
+ * contains the generic handlers, which pass control to the appropriate
+ * registered handler. This file does not deal with dispatching
+ * messages/signals to processes on an interrupt.
+ */
+
+#include <stdint.h>
+#include "proc.h"
+#include "../proc/proc.h"
+#include "../task/task.h"
+#include "../io.h"
+#include "../screen.h"
+
+void (**exceptions)(InterruptFrame *);
+void (**interrupts)(InterruptFrame *);
+
+/* Register an exception handler */
+void
+register_exception(int num, void (*addr)(InterruptFrame *))
+{
+ if (num >= 0 && num < 32)
+ exceptions[num] = addr;
+}
+
+/* Register an interrupt handler */
+void
+register_interrupt(int num, void (*addr)(InterruptFrame *))
+{
+ if (num >= 0 && num < 224)
+ interrupts[num] = addr;
+}
+
+/* Call the appropriate exception handler */
+uintptr_t
+exc_handler(InterruptFrame frame)
+{
+ uint8_t num = frame.intNo & 0xFF;
+
+ /* System Call */
+ if (num == 0x80)
+ syscall_handler(&frame);
+ /* Other exception */
+ else if (exceptions[num])
+ exceptions[num](&frame);
+ else
+ panic("Unhandled exception %d (%#x) @ %#.8x [CPU#%d]\n"
+ "EFLAGS: %#.8x",
+ num, frame.errCode, frame.eip, CPUID, frame.eflags);
+
+ /* Send EOI */
+ LAPIC(0xB0) = 0;
+
+ if (current)
+ if (current->tls)
+ return current->tls->start + 4;
+ return 0;
+}
+
+/* Call the appropriate interrupt handler */
+uintptr_t
+irq_handler(InterruptFrame frame)
+{
+ uint8_t num = (frame.intNo & 0xFF) - 32;
+
+ if (interrupts[num])
+ interrupts[num](&frame);
+
+ /* Send EOI */
+ outb(0x20, 0x20);
+ if (num >= 8)
+ outb(0xA0, 0x20);
+ LAPIC(0xB0) = 0;
+
+ if (current)
+ if (current->tls)
+ return current->tls->start + 4;
+ return 0;
+}
diff --git a/proc/load.S b/proc/load.S
new file mode 100644
index 0000000..98330a0
--- /dev/null
+++ b/proc/load.S
@@ -0,0 +1,38 @@
+; This file is responsible for loading the tables onto the CPU. The functions
+; will load the GDT, IDT and TSS onto the CPU running the function. They only
+; provide a wrapper for the relevant x86 instruction, and do not do any table
+; creation. See the relevant C files for the table setup routines. The GDT and
+; IDT require a pointer to the table, while the TSS can be loaded from the
+; currently loaded GDT.
+
+[bits 32]
+
+; Load the IDT
+[global load_idt]
+load_idt:
+ mov eax, [esp + 4]
+ lidt [eax]
+ ret
+
+; Load the GDT
+[global load_gdt]
+load_gdt:
+ mov eax, [esp + 4]
+ lgdt [eax]
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ss, ax
+ jmp 0x08:.flush
+.flush:
+ ret
+
+; Load the TSS
+[global load_tss]
+load_tss:
+ mov ax, 0x28 | 3
+ ltr ax
+ ret
diff --git a/proc/pic.c b/proc/pic.c
new file mode 100644
index 0000000..e478921
--- /dev/null
+++ b/proc/pic.c
@@ -0,0 +1,40 @@
+/*
+ * This file deals with the Programmable Interrupt Controller. It is usually
+ * disable and replaced by the APIC.
+ */
+
+#include "../io.h"
+
+/* Initialise the PIC to a specified frequency */
+static void
+init_pit(void)
+{
+ uint32_t divisor = 1193182 / 1000;
+ outb(0x43, 0x36);
+ outb(0x40, divisor & 0xFF);
+ outb(0x40, (divisor >> 8) & 0xFF);
+}
+
+/* Initialise the PIC */
+void
+init_pic(void)
+{
+ /*
+ * By default interrupts and exceptions both start at IRQ#0. To avoid
+ * collision we can map interrupts to start at IRQ#32. Since lower
+ * numbered IRQ lines are higher priority, exceptions have priority.
+ */
+ outb(0x20, 0x11); io_wait();
+ outb(0xA0, 0x11); io_wait();
+ outb(0x21, 0x20); io_wait();
+ outb(0xA1, 0x28); io_wait();
+ outb(0x21, 0x04); io_wait();
+ outb(0xA1, 0x02); io_wait();
+ outb(0x21, 0x01); io_wait();
+ outb(0xA1, 0x01); io_wait();
+ outb(0x21, 0x00); io_wait();
+ outb(0xA1, 0x00); io_wait();
+
+ init_pit();
+}
+
diff --git a/proc/proc.h b/proc/proc.h
new file mode 100644
index 0000000..bba44cf
--- /dev/null
+++ b/proc/proc.h
@@ -0,0 +1,32 @@
+#ifndef KERNEL_PROC_H
+#define KERNEL_PROC_H
+
+#include <stdint.h>
+
+/* Structure pushed on interrupt */
+typedef struct InterruptFrame {
+ uint32_t ds, fs, gs;
+ uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
+ uint32_t intNo, errCode;
+ uint32_t eip, cs, eflags, useresp, ss;
+} InterruptFrame;
+
+extern uint8_t numCores;
+extern uint32_t lapicPtr, ioapicPtr;
+extern uint8_t lapicNums[];
+#define LAPIC(off) (*((uint32_t *) ((uint32_t) lapicPtr + (off))))
+#define IOAPIC(off) (*((uint32_t *) ((uint32_t) ioapicPtr + (off))))
+#define CPUID lapicNums[(uint8_t) (LAPIC(0x20) >> 24)]
+#define MAX_CPUS 2
+
+void init_pic(void);
+void init_multicore(void *ebda);
+
+void init_idt(void);
+void init_gdt(void);
+void cpu_load_idt(void);
+void cpu_load_gdt(void);
+void register_exception(int num, void (*handler)(InterruptFrame *));
+void register_interrupt(int num, void (*handler)(InterruptFrame *));
+
+#endif
diff --git a/proc/stub.S b/proc/stub.S
new file mode 100644
index 0000000..a16d014
--- /dev/null
+++ b/proc/stub.S
@@ -0,0 +1,149 @@
+; This file contains the stubs for the interrupt handlers, which can push more
+; useful information on the the stack, before calling a generic handler. Since
+; each exception/interrupt must have it's own handler, macros are used to create
+; a handler for each of them.
+
+[bits 32]
+
+[extern irq_handler]
+[extern exc_handler]
+
+; Exception without error code
+%macro EXC_NOERRCODE 1
+[global exc%1]
+exc%1:
+ cli
+ push byte 0
+ push %1
+ jmp exc_stub
+%endmacro
+
+; Exception with error code
+%macro EXC_ERRCODE 1
+[global exc%1]
+exc%1:
+ cli
+ push %1
+ jmp exc_stub
+%endmacro
+
+; Interrupt
+%macro IRQ 2
+[global irq%1]
+irq%1:
+ cli
+ push byte 0
+ push byte %2
+ jmp irq_stub
+%endmacro
+
+EXC_NOERRCODE 0
+EXC_NOERRCODE 1
+EXC_NOERRCODE 2
+EXC_NOERRCODE 3
+EXC_NOERRCODE 4
+EXC_NOERRCODE 5
+EXC_NOERRCODE 6
+EXC_NOERRCODE 7
+EXC_ERRCODE 8
+EXC_NOERRCODE 9
+EXC_ERRCODE 10
+EXC_ERRCODE 11
+EXC_ERRCODE 12
+EXC_ERRCODE 13
+EXC_ERRCODE 14
+EXC_NOERRCODE 15
+EXC_NOERRCODE 16
+EXC_ERRCODE 17
+EXC_NOERRCODE 18
+EXC_NOERRCODE 19
+EXC_NOERRCODE 20
+EXC_NOERRCODE 30
+EXC_NOERRCODE 128 ; syscall
+
+IRQ 0, 32
+IRQ 1, 33
+IRQ 2, 34
+IRQ 3, 35
+IRQ 4, 36
+IRQ 5, 37
+IRQ 6, 38
+IRQ 7, 39
+IRQ 8, 40
+IRQ 9, 41
+IRQ 10, 42
+IRQ 11, 43
+IRQ 12, 44
+IRQ 13, 45
+IRQ 14, 46
+IRQ 15, 47
+
+; Handle an exception
+exc_stub:
+ pusha
+ mov ax, gs
+ push eax
+ mov ax, fs
+ push eax
+ mov ax, ds
+ push eax
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ call exc_handler
+
+ pop ebx
+ mov ds, bx
+ mov es, bx
+ pop ebx
+ mov fs, bx
+ pop ebx
+ mov gs, bx
+
+ mov ecx, 0xC0000101
+ xor edx, edx
+ wrmsr
+
+ popa
+ add esp, 8
+ sti
+ iret
+
+; Handle an interrupt
+irq_stub:
+ pusha
+ mov ax, gs
+ push eax
+ mov ax, fs
+ push eax
+ mov ax, ds
+ push eax
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ call irq_handler
+
+ pop ebx
+ mov ds, bx
+ mov es, bx
+ pop ebx
+ mov fs, bx
+ pop ebx
+ mov gs, bx
+
+ mov ecx, 0xC0000101
+ xor edx, edx
+ wrmsr
+
+ popa
+ add esp, 8
+ sti
+ iret
diff --git a/proc/trampoline.S b/proc/trampoline.S
new file mode 100644
index 0000000..8a8656f
--- /dev/null
+++ b/proc/trampoline.S
@@ -0,0 +1,52 @@
+; This file contains the AP trampoline code to bounce it back into protected
+; mode and run the Kernel on them. The location of the stack is set by the
+; calling code. This code runs initially in real (16-bit) mode, at 0x0000 in
+; physical memory. Modifying this code should be done with care, since it makes
+; use of fixed offsets in memory to jump around.
+
+[bits 16]
+
+[extern ap_startup]
+
+; This is the code run by the APs on startup. They need to initialise
+; themselves, which includes setting up a GDT and switching to protected mode.
+; When they're in protected mode they can just run their main C function.
+[global ap_trampoline]
+ap_trampoline:
+ cli
+ cld
+ jmp 0x0000:0x2040 ; continue
+
+align 16
+.gdt_start:
+ dd 0, 0
+ dd 0x0000FFFF, 0x00CF9A00 ; Flat Code
+ dd 0x0000FFFF, 0x00CF9200 ; Flat Data
+ dd 0x00000068, 0x00CF8900 ; TSS
+.gdt_desc:
+ dw .gdt_desc - .gdt_start - 1
+ dd 0x2010 ; gdt_start
+ dd 0, 0
+
+align 64
+.continue:
+ xor ax, ax
+ mov ds, ax
+ lgdt [0x2030] ; gdt_desc
+ mov eax, cr0
+ or eax, 0x01
+ mov cr0, eax
+ jmp 0x08:0x2060 ; pm
+
+align 32
+[bits 32]
+.pm:
+ mov ax, 0x10
+ mov ds, ax
+ mov ss, ax
+ ; Load passed stack
+ mov eax, [0x2FF0]
+ mov esp, eax
+ mov ebp, esp
+ sti
+ jmp 0x08:ap_startup