diff --git a/Makefile b/Makefile index 9e93572..bda8f47 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ fs.img: kernel.bin tools/mkfs image.bin: mbr.bin fs.img cat $^ >$@ -kernel.bin: kernel.o drivers/vga.o string.o drivers/ata.o +kernel.bin: kernel.o console.o drivers/vga.o string.o drivers/ata.o cpu/vectors.o cpu/idt.o $(LD) -m elf_i386 -o $@ -Ttext 0x1000 $^ %.o: %.c @@ -41,7 +41,7 @@ mbr.elf: mbr.o $(LD) -m elf_i386 -Ttext=0x7c00 $^ -o $@ clean: - rm -f *.elf *.bin *.o tools/mkfs + rm -f *.elf *.bin *.o */*.o tools/mkfs tools/%: tools/%.c gcc -Wall -Werror -g $^ -o $@ diff --git a/console.c b/console.c new file mode 100644 index 0000000..d358abe --- /dev/null +++ b/console.c @@ -0,0 +1,6 @@ +#include "console.h" +#include "drivers/vga.h" + +void printk(const char* msg) { + vga_print_string(msg); +} diff --git a/console.h b/console.h new file mode 100644 index 0000000..d115f2a --- /dev/null +++ b/console.h @@ -0,0 +1,3 @@ +#pragma once + +void printk(const char* msg); diff --git a/cpu/idt.c b/cpu/idt.c new file mode 100644 index 0000000..715580a --- /dev/null +++ b/cpu/idt.c @@ -0,0 +1,138 @@ +#include "isr.h" +#include "../drivers/port.h" +#include "../console.h" + +enum { + IDT_HANDLERS = 256, +}; + +typedef struct { + uint16_t low_offset; + uint16_t selector; + uint8_t always0; + uint8_t flags; + uint16_t high_offset; +} __attribute__((packed)) idt_gate_t; + +idt_gate_t idt[IDT_HANDLERS]; + +#define low_16(address) (uint16_t)((address) & 0xFFFF) +#define high_16(address) (uint16_t)(((address) >> 16) & 0xFFFF) + +void set_idt_gate(int n, uint32_t handler) { + idt[n].low_offset = low_16(handler); + idt[n].selector = 0x08; // see GDT + idt[n].always0 = 0; + // 0x8E = 1 00 0 1 110 + // P DPL 0 D Type + idt[n].flags = 0x8E; + idt[n].high_offset = high_16(handler); +} + +// defined in vectors.S +extern uint32_t default_handlers[IDT_HANDLERS]; + +void init_idt() { + for (int i = 0; i < IDT_HANDLERS; i++) { + set_idt_gate(i, default_handlers[i]); + } +} + +const char *exception_messages[] = { + "Division 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", +}; + +#define ARRLEN(a) (sizeof(a) / sizeof(a[0])) + +isr_t interrupt_handlers[IDT_HANDLERS]; + +void trap(registers_t *r) { + if (r->int_no < 32) { + const char* msg = "Reserved"; + if (r->int_no < ARRLEN(exception_messages)) { + msg = exception_messages[r->int_no]; + } + printk(msg); + printk("\n"); + } + + /* Handle the interrupt in a more modular way */ + if (interrupt_handlers[r->int_no] != 0) { + isr_t handler = interrupt_handlers[r->int_no]; + handler(r); + } + + // EOI + if (r->int_no >= 40) { + port_byte_out(0xA0, 0x20); /* follower */ + } + if (r->int_no >= 32) { + port_byte_out(0x20, 0x20); /* leader */ + } +} + +static void init_pic() { + // ICW1 + port_byte_out(0x20, 0x11); + port_byte_out(0xA0, 0x11); + + // ICW2 + port_byte_out(0x21, 0x20); + port_byte_out(0xA1, 0x28); + + // ICW3 + port_byte_out(0x21, 0x04); + port_byte_out(0xA1, 0x02); + + // ICW4 + port_byte_out(0x21, 0x01); + port_byte_out(0xA1, 0x01); + + // OCW1 + port_byte_out(0x21, 0x0); + port_byte_out(0xA1, 0x0); +} + +typedef struct { + uint16_t limit; + void* base; +} __attribute__((packed)) idt_register_t; + +static idt_register_t idt_reg; + +void load_idt() { + init_idt(); + init_pic(); + + idt_reg.base = &idt; + idt_reg.limit = sizeof(idt) - 1; + asm("lidt %0" : : "m"(idt_reg)); +} + +void cli() { + asm("cli"); +} + +void sti() { + asm("sti"); +} diff --git a/cpu/isr.h b/cpu/isr.h new file mode 100644 index 0000000..1175355 --- /dev/null +++ b/cpu/isr.h @@ -0,0 +1,47 @@ +#pragma once +#include + +enum { + IRQ0 = 32, + IRQ1, + IRQ2, + IRQ3, + IRQ4, + IRQ5, + IRQ6, + IRQ7, + IRQ8, + IRQ9, + IRQ10, + IRQ11, + IRQ12, + IRQ13, + IRQ14, + IRQ15, +}; + +/* Struct which aggregates many registers. + * It matches exactly the pushes on interrupt.asm. From the bottom: + * - Pushed by the processor automatically + * - `push byte`s on the isr-specific code: error code, then int number + * - All the registers by pusha + * - `push eax` whose lower 16-bits contain DS + */ +typedef struct { + uint32_t ds; /* Data segment selector */ + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */ + uint32_t int_no, err_code; /* Interrupt number and error code (if applicable) */ + uint32_t eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */ +} registers_t; + +void isr_install(); + +void isr_handler(registers_t *r); + +typedef void (*isr_t)(registers_t *); + +void register_interrupt_handler(uint8_t n, isr_t handler); + +void load_idt(); +void cli(); +void sti(); diff --git a/cpu/vectors.S b/cpu/vectors.S new file mode 100644 index 0000000..9fd80ed --- /dev/null +++ b/cpu/vectors.S @@ -0,0 +1,51 @@ +alltraps: + # Build trap frame. + pushl %ds + pushl %es + pushl %fs + pushl %gs + pushal + + mov $10, %ax + mov %ax, %ds + + # Call trap(tf), where tf=%esp + pushl %esp + call trap + add $4, %esp + + popal + popl %gs + popl %fs + popl %es + popl %ds + addl $0x8, %esp # trapno and errcode + iret + +.macro handler i +vector\i : + .if (!(\i == 8 || (\i >= 10 && \i <= 14) || \i == 17)) + pushl $0 + .endif + pushl $\i + jmp alltraps +.endm + +.altmacro + +.macro irq_insertX number + .section .text + handler \number + + .section .data + .long vector\number +.endm + +.section .data +.global default_handlers +default_handlers: +.set i,0 +.rept 256 + irq_insertX %i + .set i, i+1 +.endr diff --git a/kernel.c b/kernel.c index 7d6f4ed..80ca3a3 100644 --- a/kernel.c +++ b/kernel.c @@ -1,10 +1,13 @@ asm(".asciz \"kernel start\""); +#include "cpu/isr.h" #include "drivers/vga.h" #include "drivers/ata.h" #include "drivers/misc.h" void _start() { + load_idt(); + sti(); char buf[512]; vga_clear_screen();