Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 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