diff options
author | dzwdz | 2022-07-16 13:33:00 +0200 |
---|---|---|
committer | dzwdz | 2022-07-16 13:33:00 +0200 |
commit | 912d2e3c7eb1baa71dda2c0a28aa5809eaa96f27 (patch) | |
tree | 4e27f3538466d5fd63a311d50916039a7a15a485 /src/kernel/arch/amd64/interrupts | |
parent | 1eeb66af44ab335888410d716d604e569f20866e (diff) |
amd64: barely boot into kernel code
Diffstat (limited to 'src/kernel/arch/amd64/interrupts')
-rw-r--r-- | src/kernel/arch/amd64/interrupts/idt.c | 71 | ||||
-rw-r--r-- | src/kernel/arch/amd64/interrupts/idt.h | 3 | ||||
-rw-r--r-- | src/kernel/arch/amd64/interrupts/irq.c | 33 | ||||
-rw-r--r-- | src/kernel/arch/amd64/interrupts/irq.h | 6 | ||||
-rw-r--r-- | src/kernel/arch/amd64/interrupts/isr.c | 48 | ||||
-rw-r--r-- | src/kernel/arch/amd64/interrupts/isr.h | 8 | ||||
-rw-r--r-- | src/kernel/arch/amd64/interrupts/isr_stub.s | 81 |
7 files changed, 250 insertions, 0 deletions
diff --git a/src/kernel/arch/amd64/interrupts/idt.c b/src/kernel/arch/amd64/interrupts/idt.c new file mode 100644 index 0000000..9c71000 --- /dev/null +++ b/src/kernel/arch/amd64/interrupts/idt.c @@ -0,0 +1,71 @@ +#include <kernel/arch/amd64/32/gdt.h> +#include <kernel/arch/amd64/interrupts/idt.h> +#include <kernel/arch/amd64/interrupts/isr.h> +#include <kernel/panic.h> +#include <stdbool.h> +#include <stdint.h> + +struct idt_entry { + uint16_t offset_low; + uint16_t seg; + uint8_t ist; + uint8_t type : 4; // 0xE - interrupt, 0xF - trap + uint8_t zero1 : 1; + uint8_t ring : 2; + uint8_t present : 1; + uint16_t offset_mid; + uint32_t offset_high; + uint32_t zero2; +} __attribute__((packed)); + +// is exactly the same as lgdt_arg, i should combine them into a single struct +// later +struct lidt_arg { + uint16_t limit; + uintptr_t base; +} __attribute__((packed)); + +__attribute__((section(".shared"))) +static struct idt_entry IDT[256]; +static struct lidt_arg lidt_arg; + +static void idt_prepare(void); +static void idt_load(void); +static void idt_test(void); + + +static void idt_prepare(void) { + for (int i = 0; i < 256; i++) { + uintptr_t offset = (uintptr_t) &_isr_stubs + i * 8; + + IDT[i] = (struct idt_entry) { + .offset_low = offset, + .offset_mid = offset >> 16, + .offset_high = offset >> 32, + .seg = SEG_r0code << 3, + .present = 1, + .type = 0xE, + .ist = 1, + }; + } +} + +static void idt_load(void) { + lidt_arg.limit = sizeof(IDT) - 1; + lidt_arg.base = (uintptr_t) &IDT; + asm("lidt (%0)" : : "r" (&lidt_arg) : "memory"); +} + +static void idt_test(void) { + kprintf("idt test?\n"); + asm("xchgw %%bx, %%bx" ::: "memory"); + asm("int $0x34" : : : "memory"); + assert(isr_test_interrupt_called); + kprintf("done.\n"); +} + +void idt_init(void) { + idt_prepare(); + idt_load(); + idt_test(); +} diff --git a/src/kernel/arch/amd64/interrupts/idt.h b/src/kernel/arch/amd64/interrupts/idt.h new file mode 100644 index 0000000..6576cf9 --- /dev/null +++ b/src/kernel/arch/amd64/interrupts/idt.h @@ -0,0 +1,3 @@ +#pragma once + +void idt_init(void); diff --git a/src/kernel/arch/amd64/interrupts/irq.c b/src/kernel/arch/amd64/interrupts/irq.c new file mode 100644 index 0000000..74bc48c --- /dev/null +++ b/src/kernel/arch/amd64/interrupts/irq.c @@ -0,0 +1,33 @@ +#include <kernel/arch/amd64/interrupts/irq.h> +#include <kernel/arch/amd64/port_io.h> +#include <stdint.h> + +static const int PIC1 = 0x20; +static const int PIC2 = 0xA0; + +void irq_init(void) { + port_out8(PIC1, 0x11); /* start init sequence */ + port_out8(PIC2, 0x11); + + port_out8(PIC1+1, 0x20); /* interrupt offsets */ + port_out8(PIC2+1, 0x30); + + port_out8(PIC1+1, 0x4); /* just look at the osdev wiki lol */ + port_out8(PIC2+1, 0x2); + + port_out8(PIC1+1, 0x1); /* 8086 mode */ + port_out8(PIC2+1, 0x1); + + uint8_t mask = 0xff; + mask &= ~(1 << 1); // keyboard + mask &= ~(1 << 4); // COM1 + + port_out8(PIC1+1, mask); + port_out8(PIC2+1, 0xff); +} + +void irq_eoi(uint8_t line) { + port_out8(PIC1, 0x20); + if (line >= 8) + port_out8(PIC2, 0x20); +} diff --git a/src/kernel/arch/amd64/interrupts/irq.h b/src/kernel/arch/amd64/interrupts/irq.h new file mode 100644 index 0000000..f523154 --- /dev/null +++ b/src/kernel/arch/amd64/interrupts/irq.h @@ -0,0 +1,6 @@ +#pragma once +#include <stdbool.h> +#include <stdint.h> + +void irq_init(void); +void irq_eoi(uint8_t line); diff --git a/src/kernel/arch/amd64/interrupts/isr.c b/src/kernel/arch/amd64/interrupts/isr.c new file mode 100644 index 0000000..b55bc8b --- /dev/null +++ b/src/kernel/arch/amd64/interrupts/isr.c @@ -0,0 +1,48 @@ +#include <kernel/arch/amd64/driver/ps2.h> +#include <kernel/arch/amd64/driver/serial.h> +#include <kernel/arch/amd64/interrupts/irq.h> +#include <kernel/arch/amd64/interrupts/isr.h> +#include <kernel/arch/amd64/port_io.h> +#include <kernel/arch/generic.h> +#include <kernel/panic.h> +#include <kernel/proc.h> +#include <stdbool.h> +#include <stdint.h> + +bool isr_test_interrupt_called = false; + +void isr_stage3(int interrupt, uint64_t *stackframe) { + if (interrupt == 0xe) stackframe++; + kprintf("interrupt %x, rip = k/%08x, cs 0x%x\n", interrupt, stackframe[0], stackframe[1]); + switch (interrupt) { + case 0x08: // double fault + kprintf("#DF"); + panic_invalid_state(); + + case 0xe: + uint64_t addr = 0x69; + asm("mov %%cr2, %0" : "=r"(addr)); + kprintf("error code 0x%x, addr 0x%x\n", stackframe[-1], addr); + panic_unimplemented(); + + case 0x34: + asm("nop" ::: "memory"); + isr_test_interrupt_called = true; + return; + + case 0x21: // keyboard irq + ps2_recv(port_in8(0x60)); + irq_eoi(1); + return; + + case 0x24: // COM1 irq + serial_irq(); + irq_eoi(1); + return; + + default: + // TODO check if the exception was in the kernel + process_kill(process_current, interrupt); + process_switch_any(); + } +} diff --git a/src/kernel/arch/amd64/interrupts/isr.h b/src/kernel/arch/amd64/interrupts/isr.h new file mode 100644 index 0000000..01f5236 --- /dev/null +++ b/src/kernel/arch/amd64/interrupts/isr.h @@ -0,0 +1,8 @@ +#pragma once +#include <stdbool.h> +#include <stdint.h> + +extern bool isr_test_interrupt_called; // used in the self-test in idt.c +extern const char _isr_stubs; + +void isr_stage3(int interrupt, uint64_t *stackframe); diff --git a/src/kernel/arch/amd64/interrupts/isr_stub.s b/src/kernel/arch/amd64/interrupts/isr_stub.s new file mode 100644 index 0000000..e45c1c1 --- /dev/null +++ b/src/kernel/arch/amd64/interrupts/isr_stub.s @@ -0,0 +1,81 @@ +.section .shared + +.global _isr_stubs +_isr_stubs: +.rept 256 + .set _stub_start, . + + cli + call _isr_stage2 + + .if . - _stub_start > 8 + .error "isr stubs over maximum size" + .abort + .endif + .align 8 +.endr + +_isr_stage2: + // pushal order, without %esp + push %rax + push %rcx + push %rdx + push %rbx + push %rbp + push %rsi + push %rdi + push %r8 + push %r9 + push %r10 + push %r11 + push %r12 + push %r13 + push %r14 + push %r15 + + // convert the return address into the vector nr + mov 120(%rsp), %rdi + sub $_isr_stubs, %rdi + shr $3, %rdi + + lea 128(%rsp), %rsi // second argument - IRET stack frame + + // load kernel paging + mov %cr3, %rbx + push %rbx + mov $pml4_identity, %rbx + mov %rbx, %cr3 + + mov %rsp, %rbp + mov $_isr_big_stack, %rsp + call isr_stage3 + + mov %rbp, %rsp + pop %rax // restore old cr3 + mov %rax, %cr3 + + // restore registers + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %r11 + pop %r10 + pop %r9 + pop %r8 + pop %rdi + pop %rsi + pop %rbp + pop %rbx + pop %rdx + pop %rcx + pop %rax + + add $8, %rsp // skip call's return address + iretq + +.align 8 +// TODO overflow check +.skip 256 // seems to be enough +.global _isr_mini_stack +_isr_mini_stack: |