From 912d2e3c7eb1baa71dda2c0a28aa5809eaa96f27 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Sat, 16 Jul 2022 13:33:00 +0200 Subject: amd64: barely boot into kernel code --- src/kernel/arch/amd64/32/32 | 130 +++++++++++++++++ src/kernel/arch/amd64/32/boot.s | 86 +++++++++++ src/kernel/arch/amd64/32/farjump.s | 8 ++ src/kernel/arch/amd64/32/gdt.c | 118 +++++++++++++++ src/kernel/arch/amd64/32/gdt.h | 17 +++ src/kernel/arch/amd64/32/paging.c | 131 +++++++++++++++++ src/kernel/arch/amd64/ata.c | 151 ++++++++++++++++++++ src/kernel/arch/amd64/ata.h | 7 + src/kernel/arch/amd64/boot.c | 51 +++++++ src/kernel/arch/amd64/boot.h | 4 + src/kernel/arch/amd64/boot64.s | 13 ++ src/kernel/arch/amd64/debug.c | 18 +++ src/kernel/arch/amd64/driver/fsroot.c | 146 +++++++++++++++++++ src/kernel/arch/amd64/driver/fsroot.h | 2 + src/kernel/arch/amd64/driver/ps2.c | 61 ++++++++ src/kernel/arch/amd64/driver/ps2.h | 5 + src/kernel/arch/amd64/driver/serial.c | 114 +++++++++++++++ src/kernel/arch/amd64/driver/serial.h | 10 ++ src/kernel/arch/amd64/interrupts/idt.c | 71 ++++++++++ src/kernel/arch/amd64/interrupts/idt.h | 3 + src/kernel/arch/amd64/interrupts/irq.c | 33 +++++ src/kernel/arch/amd64/interrupts/irq.h | 6 + src/kernel/arch/amd64/interrupts/isr.c | 48 +++++++ src/kernel/arch/amd64/interrupts/isr.h | 8 ++ src/kernel/arch/amd64/interrupts/isr_stub.s | 81 +++++++++++ src/kernel/arch/amd64/multiboot.h | 29 ++++ src/kernel/arch/amd64/multiboot.s | 12 ++ src/kernel/arch/amd64/pagedir.c | 213 ++++++++++++++++++++++++++++ src/kernel/arch/amd64/port_io.h | 22 +++ src/kernel/arch/amd64/registers.h | 18 +++ src/kernel/arch/amd64/sysenter.c | 27 ++++ src/kernel/arch/amd64/sysenter.h | 8 ++ src/kernel/arch/amd64/sysenter.s | 93 ++++++++++++ src/kernel/arch/amd64/tty/tty.c | 26 ++++ src/kernel/arch/amd64/tty/tty.h | 7 + src/kernel/arch/amd64/tty/vga.c | 33 +++++ src/kernel/arch/generic.h | 4 +- src/kernel/arch/i386/ata.c | 151 -------------------- src/kernel/arch/i386/ata.h | 7 - src/kernel/arch/i386/boot.c | 53 ------- src/kernel/arch/i386/boot.h | 4 - src/kernel/arch/i386/boot.s | 34 ----- src/kernel/arch/i386/debug.c | 18 --- src/kernel/arch/i386/driver/fsroot.c | 146 ------------------- src/kernel/arch/i386/driver/fsroot.h | 2 - src/kernel/arch/i386/driver/ps2.c | 61 -------- src/kernel/arch/i386/driver/ps2.h | 5 - src/kernel/arch/i386/driver/serial.c | 114 --------------- src/kernel/arch/i386/driver/serial.h | 10 -- src/kernel/arch/i386/gdt.h | 16 --- src/kernel/arch/i386/gdt/farjump.s | 8 -- src/kernel/arch/i386/gdt/gdt.c | 130 ----------------- src/kernel/arch/i386/interrupts/idt.c | 68 --------- src/kernel/arch/i386/interrupts/idt.h | 3 - src/kernel/arch/i386/interrupts/irq.c | 33 ----- src/kernel/arch/i386/interrupts/irq.h | 6 - src/kernel/arch/i386/interrupts/isr.c | 38 ----- src/kernel/arch/i386/interrupts/isr.h | 7 - src/kernel/arch/i386/interrupts/isr_stub.s | 64 --------- src/kernel/arch/i386/multiboot.h | 34 ----- src/kernel/arch/i386/multiboot.s | 12 -- src/kernel/arch/i386/pagedir.c | 206 --------------------------- src/kernel/arch/i386/port_io.h | 22 --- src/kernel/arch/i386/registers.h | 21 --- src/kernel/arch/i386/sysenter.c | 26 ---- src/kernel/arch/i386/sysenter.h | 8 -- src/kernel/arch/i386/sysenter.s | 89 ------------ src/kernel/arch/i386/tty/tty.c | 26 ---- src/kernel/arch/i386/tty/tty.h | 7 - src/kernel/arch/i386/tty/vga.c | 33 ----- src/kernel/panic.h | 2 +- src/kernel/proc.c | 1 + src/kernel/vfs/request.c | 2 +- 73 files changed, 1815 insertions(+), 1466 deletions(-) create mode 100644 src/kernel/arch/amd64/32/32 create mode 100644 src/kernel/arch/amd64/32/boot.s create mode 100644 src/kernel/arch/amd64/32/farjump.s create mode 100644 src/kernel/arch/amd64/32/gdt.c create mode 100644 src/kernel/arch/amd64/32/gdt.h create mode 100644 src/kernel/arch/amd64/32/paging.c create mode 100644 src/kernel/arch/amd64/ata.c create mode 100644 src/kernel/arch/amd64/ata.h create mode 100644 src/kernel/arch/amd64/boot.c create mode 100644 src/kernel/arch/amd64/boot.h create mode 100644 src/kernel/arch/amd64/boot64.s create mode 100644 src/kernel/arch/amd64/debug.c create mode 100644 src/kernel/arch/amd64/driver/fsroot.c create mode 100644 src/kernel/arch/amd64/driver/fsroot.h create mode 100644 src/kernel/arch/amd64/driver/ps2.c create mode 100644 src/kernel/arch/amd64/driver/ps2.h create mode 100644 src/kernel/arch/amd64/driver/serial.c create mode 100644 src/kernel/arch/amd64/driver/serial.h create mode 100644 src/kernel/arch/amd64/interrupts/idt.c create mode 100644 src/kernel/arch/amd64/interrupts/idt.h create mode 100644 src/kernel/arch/amd64/interrupts/irq.c create mode 100644 src/kernel/arch/amd64/interrupts/irq.h create mode 100644 src/kernel/arch/amd64/interrupts/isr.c create mode 100644 src/kernel/arch/amd64/interrupts/isr.h create mode 100644 src/kernel/arch/amd64/interrupts/isr_stub.s create mode 100644 src/kernel/arch/amd64/multiboot.h create mode 100644 src/kernel/arch/amd64/multiboot.s create mode 100644 src/kernel/arch/amd64/pagedir.c create mode 100644 src/kernel/arch/amd64/port_io.h create mode 100644 src/kernel/arch/amd64/registers.h create mode 100644 src/kernel/arch/amd64/sysenter.c create mode 100644 src/kernel/arch/amd64/sysenter.h create mode 100644 src/kernel/arch/amd64/sysenter.s create mode 100644 src/kernel/arch/amd64/tty/tty.c create mode 100644 src/kernel/arch/amd64/tty/tty.h create mode 100644 src/kernel/arch/amd64/tty/vga.c delete mode 100644 src/kernel/arch/i386/ata.c delete mode 100644 src/kernel/arch/i386/ata.h delete mode 100644 src/kernel/arch/i386/boot.c delete mode 100644 src/kernel/arch/i386/boot.h delete mode 100644 src/kernel/arch/i386/boot.s delete mode 100644 src/kernel/arch/i386/debug.c delete mode 100644 src/kernel/arch/i386/driver/fsroot.c delete mode 100644 src/kernel/arch/i386/driver/fsroot.h delete mode 100644 src/kernel/arch/i386/driver/ps2.c delete mode 100644 src/kernel/arch/i386/driver/ps2.h delete mode 100644 src/kernel/arch/i386/driver/serial.c delete mode 100644 src/kernel/arch/i386/driver/serial.h delete mode 100644 src/kernel/arch/i386/gdt.h delete mode 100644 src/kernel/arch/i386/gdt/farjump.s delete mode 100644 src/kernel/arch/i386/gdt/gdt.c delete mode 100644 src/kernel/arch/i386/interrupts/idt.c delete mode 100644 src/kernel/arch/i386/interrupts/idt.h delete mode 100644 src/kernel/arch/i386/interrupts/irq.c delete mode 100644 src/kernel/arch/i386/interrupts/irq.h delete mode 100644 src/kernel/arch/i386/interrupts/isr.c delete mode 100644 src/kernel/arch/i386/interrupts/isr.h delete mode 100644 src/kernel/arch/i386/interrupts/isr_stub.s delete mode 100644 src/kernel/arch/i386/multiboot.h delete mode 100644 src/kernel/arch/i386/multiboot.s delete mode 100644 src/kernel/arch/i386/pagedir.c delete mode 100644 src/kernel/arch/i386/port_io.h delete mode 100644 src/kernel/arch/i386/registers.h delete mode 100644 src/kernel/arch/i386/sysenter.c delete mode 100644 src/kernel/arch/i386/sysenter.h delete mode 100644 src/kernel/arch/i386/sysenter.s delete mode 100644 src/kernel/arch/i386/tty/tty.c delete mode 100644 src/kernel/arch/i386/tty/tty.h delete mode 100644 src/kernel/arch/i386/tty/vga.c (limited to 'src/kernel') diff --git a/src/kernel/arch/amd64/32/32 b/src/kernel/arch/amd64/32/32 new file mode 100644 index 0000000..9bd97e6 --- /dev/null +++ b/src/kernel/arch/amd64/32/32 @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include + +extern char _isr_mini_stack; + +struct gdt_entry { + uint64_t limit_low : 16; + uint64_t base_low : 24; + uint64_t accessed : 1; // set by the processor + // CODE | DATA + uint64_t rw : 1; // readable? | writeable? + uint64_t conforming : 1; // conforming? | expands down? + uint64_t code : 1; // 1 | 0 + + uint64_t codeordata : 1; // 1 for everything other than TSS and LDT + uint64_t ring : 2; + uint64_t present : 1; // always 1 + uint64_t limit_high : 4; + uint64_t available : 1; // ??? + uint64_t long_mode : 1; + uint64_t x32 : 1; + uint64_t gran : 1; // 1 - 4kb, 0 - 1b + uint64_t base_high : 8; +} __attribute__((packed)); + +struct tss_entry { + uint32_t _unused0; + uint32_t esp0; // kernel mode stack pointer + uint32_t ss0; // kernel mode stack segment + uint8_t _unused1[0x5c]; +} __attribute__((packed)); + +struct lgdt_arg { + uint16_t limit; + uint32_t base; +} __attribute__((packed)); + +__attribute__((section(".shared"))) +static struct gdt_entry GDT[SEG_end]; +__attribute__((section(".shared"))) +static struct tss_entry TSS; +static struct lgdt_arg lgdt_arg; // probably doesn't need to be global + +static void gdt_fillout(struct gdt_entry* entry, uint8_t ring, bool code); +static void gdt_prepare(void); +static void gdt_load(void); + + +static void gdt_fillout(struct gdt_entry* entry, uint8_t ring, bool code) { + *entry = (struct gdt_entry) { + // set up the identity mapping + .limit_low = 0xFFFF, + .limit_high = 0xF, + .gran = 1, // 4KB * 0xFFFFF = (almost) 4GB + .base_low = 0, + .base_high = 0, + + .ring = ring, + .code = code, + + .accessed = 0, + .rw = 1, + .conforming = 0, + .codeordata = 1, + .present = 1, + .long_mode = 0, // ??? + .available = 1, // ??? + .x32 = 1, + }; +} + +static void gdt_prepare(void) { + GDT[SEG_null].present = 0; + + gdt_fillout(&GDT[SEG_r0code], 0, true); + gdt_fillout(&GDT[SEG_r0data], 0, false); + gdt_fillout(&GDT[SEG_r3code], 3, true); + gdt_fillout(&GDT[SEG_r3data], 3, false); + + // tss + memset(&TSS, 0, sizeof(TSS)); + TSS.ss0 = SEG_r0data << 3; // kernel data segment + TSS.esp0 = (uintptr_t) &_isr_mini_stack; + + GDT[SEG_TSS] = (struct gdt_entry) { + .limit_low = sizeof(TSS), + .limit_high = sizeof(TSS) >> 16, + .gran = 0, + .base_low = (uintptr_t) &TSS, + .base_high = ((uintptr_t) &TSS) >> 24, + + .accessed = 1, // 1 for TSS + .rw = 0, // 1 busy / 0 not busy + .conforming = 0, // 0 for TSS + .code = 1, // 32bit + .codeordata = 0, // is a system entry + .ring = 3, + .present = 1, + .available = 0, // 0 for TSS + .long_mode = 0, + .x32 = 0, // idk + }; +} + +static void gdt_load(void) { + lgdt_arg.limit = sizeof(GDT) - 1; + lgdt_arg.base = (uintptr_t) &GDT; + asm("lgdt (%0)" + : : "r" (&lgdt_arg) : "memory"); + + asm("ltr %%ax" + : : "a" (SEG_TSS << 3 | 3) : "memory"); + + // update all segment registers + gdt_farjump(SEG_r0code << 3); + asm("mov %0, %%ds;" + "mov %0, %%ss;" + "mov %0, %%es;" + "mov %0, %%fs;" + "mov %0, %%gs;" + : : "r" (SEG_r0data << 3) : "memory"); +} + +void gdt_init(void) { + gdt_prepare(); + gdt_load(); +} diff --git a/src/kernel/arch/amd64/32/boot.s b/src/kernel/arch/amd64/32/boot.s new file mode 100644 index 0000000..eb33c28 --- /dev/null +++ b/src/kernel/arch/amd64/32/boot.s @@ -0,0 +1,86 @@ +.section .text +.global _start +.type _start, @function +_start: + mov $_stack_top, %esp + push %ebx // save the address of the multiboot struct + + mov $0x80000000, %eax // check CPUID extended functions + cpuid + cmp $0x80000001, %eax + jb panic_early + + mov $0x80000001, %eax + cpuid + test $(1<<29), %edx // check long mode support + jz panic_early + + mov %cr4, %eax + or $(1<<5), %eax // PAE + mov %eax, %cr4 + + call pml4_identity_init + mov $pml4_identity, %eax + mov %eax, %cr3 + + mov $0xC0000080, %ecx // EFER MSR + rdmsr + or $(1 | 1<<8 | 1<<11), %eax // sysenter | long mode | NX + wrmsr + + mov %cr0, %eax + or $0x80000000, %eax + mov %eax, %cr0 + + call gdt_init + lgdt (lgdt_arg) + + pop %edi + + // TODO import gdt.h for globals + mov $(2<<3), %eax + mov %eax, %ds // SEG_r0data + mov %eax, %ss + mov %eax, %es + mov %eax, %fs + mov %eax, %gs + + ljmp $(1<<3), $boot64 // SEG_r0code + +panic_early: + // output a vga Fuck + movl $0x4F754F46, 0xB872A + movl $0x4F6B4F63, 0xB872E + jmp cpu_halt + +// TODO not part of anything yet + call sysenter_setup + // TODO will fail + push %ebx // address of the Multiboot struct + call kmain_early + +.global cpu_shutdown +.type cpu_shutdown, @function +cpu_shutdown: +/* This quits QEMU. While I couldn't find this officially documented anywhere, + * it is used by QEMU in tests/tcg/i386/system/boot.S (as of commit 40d6ee), so + * I assume that this is safe-ish to use */ + mov $0x604, %edx + mov $0x2000, %eax + outw %ax, %dx + +.global cpu_halt +.type cpu_halt, @function +cpu_halt: + cli +1: hlt + jmp 1b + + +.global cpu_pause +.type cpu_pause, @function +cpu_pause: + sti + hlt + cli + ret diff --git a/src/kernel/arch/amd64/32/farjump.s b/src/kernel/arch/amd64/32/farjump.s new file mode 100644 index 0000000..2885d2b --- /dev/null +++ b/src/kernel/arch/amd64/32/farjump.s @@ -0,0 +1,8 @@ +.section .text +.global gdt_farjump +.type gdt_farjump, @function +gdt_farjump: + /* retf pops off the return address and code segment off the stack. + * it turns out that in the i386 cdecl calling convention they're in + * the correct place already. */ + retf diff --git a/src/kernel/arch/amd64/32/gdt.c b/src/kernel/arch/amd64/32/gdt.c new file mode 100644 index 0000000..2229330 --- /dev/null +++ b/src/kernel/arch/amd64/32/gdt.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include + +extern char _isr_mini_stack; + +struct gdt_entry { + uint64_t limit_low : 16; + uint64_t base_low : 24; + uint64_t accessed : 1; // set by the processor + // CODE | DATA + uint64_t rw : 1; // readable? | writeable? + uint64_t conforming : 1; // conforming? | expands down? + uint64_t code : 1; // 1 | 0 + + uint64_t codeordata : 1; // 1 for everything other than TSS and LDT + uint64_t ring : 2; + uint64_t present : 1; // always 1 + uint64_t limit_high : 4; + uint64_t available : 1; // ??? + uint64_t long_mode : 1; + uint64_t x32 : 1; + uint64_t gran : 1; // 1 - 4kb, 0 - 1b + uint64_t base_high : 8; +} __attribute__((packed)); + +struct tss_entry { + uint32_t reserved0; + uint64_t rsp[3]; + uint64_t ist[8]; + uint64_t reserved2; + uint16_t reserved3; + uint16_t iopb; +} __attribute__((packed)); + +struct lgdt_arg { + uint16_t limit; + uint32_t base; +} __attribute__((packed)); + +__attribute__((section(".shared"))) +struct gdt_entry GDT[SEG_end]; +__attribute__((section(".shared"))) +struct tss_entry TSS; + +struct lgdt_arg lgdt_arg; + + +static void *memset32(void *s, int c, size_t n) { + uint8_t *s2 = s; + for (size_t i = 0; i < n; i++) + s2[i] = c; + return s; +} + + +static void gdt_fillout(struct gdt_entry* entry, uint8_t ring, bool code) { + *entry = (struct gdt_entry) { + // set up the identity mapping + .limit_low = 0xFFFF, + .limit_high = 0xF, + .gran = 1, + .base_low = 0, + .base_high = 0, + + .ring = ring, + .code = code, + + .accessed = 0, + .rw = 1, + .conforming = 0, + .codeordata = 1, + .present = 1, + .long_mode = 1, + .available = 1, + .x32 = 0, + }; +} + +void gdt_init(void) { + GDT[SEG_null].present = 0; + + gdt_fillout(&GDT[SEG_r0code], 0, true); + gdt_fillout(&GDT[SEG_r0data], 0, false); + gdt_fillout(&GDT[SEG_r3code], 3, true); + gdt_fillout(&GDT[SEG_r3data], 3, false); + + lgdt_arg.limit = sizeof(GDT) - 1; + lgdt_arg.base = (uint64_t) &GDT; + + + memset32(&TSS, 0, sizeof(TSS)); + for (int i = 0; i < 3; i++) + TSS.rsp[i] = (uint64_t) &_isr_mini_stack; + TSS.ist[1] = (uint64_t) &_isr_mini_stack; + + uint64_t tss_addr = (uint64_t) &TSS; + GDT[SEG_TSS] = (struct gdt_entry) { + .limit_low = sizeof(TSS), + .limit_high = sizeof(TSS) >> 16, + .gran = 0, + .base_low = tss_addr, + .base_high = tss_addr >> 24, + + .accessed = 1, + .rw = 0, + .conforming = 0, + .code = 1, + .codeordata = 0, + .ring = 0, // was 3 pre-port + .present = 1, + .available = 1, + .long_mode = 0, + .x32 = 0, + }; + *((uint64_t*)&GDT[SEG_TSS2]) = (tss_addr >> 32); +} diff --git a/src/kernel/arch/amd64/32/gdt.h b/src/kernel/arch/amd64/32/gdt.h new file mode 100644 index 0000000..fbaf681 --- /dev/null +++ b/src/kernel/arch/amd64/32/gdt.h @@ -0,0 +1,17 @@ +#pragma once + +enum { + SEG_null, + // order dictated by SYSENTER + SEG_r0code, + SEG_r0data, + SEG_r3code, + SEG_r3data, + SEG_TSS, + SEG_TSS2, + + SEG_end +}; + +void gdt_init(void); +void gdt_farjump(int segment); diff --git a/src/kernel/arch/amd64/32/paging.c b/src/kernel/arch/amd64/32/paging.c new file mode 100644 index 0000000..975dd98 --- /dev/null +++ b/src/kernel/arch/amd64/32/paging.c @@ -0,0 +1,131 @@ +#include +#include + +struct pml4e { + uint64_t present : 1; + uint64_t writeable : 1; + uint64_t user : 1; + uint64_t writethru : 1; + + uint64_t uncached : 1; + uint64_t accessed : 1; + uint64_t _unused1 : 1; + uint64_t reserved : 1; // always 0 + + uint64_t _unused2 : 3; + uint64_t _unused3 : 1; // HLAT thing + + uint64_t address : 40; + + uint64_t _unused4 : 11; + uint64_t noexec : 1; +} __attribute__((packed)); + +struct pdpte { // page directory pointer table entry, 1gb page | 512 * pde + uint64_t present : 1; + uint64_t writeable : 1; + uint64_t user : 1; + uint64_t writethru : 1; + + uint64_t uncached : 1; + uint64_t accessed : 1; + uint64_t _unused1 : 1; + uint64_t large : 1; // 1gb page + + uint64_t _unused2 : 3; + uint64_t _unused3 : 1; // HLAT + + uint64_t address : 40; + + uint64_t _unused4 : 11; + uint64_t noexec : 1; +} __attribute__((packed)); + +struct pde { // page directory entry, 2mb page | 512 * pte + uint64_t present : 1; + uint64_t writeable : 1; + uint64_t user : 1; + uint64_t writethru : 1; + + uint64_t uncached : 1; + uint64_t accessed : 1; + uint64_t dirty : 1; // only if large + uint64_t large : 1; // 2mb + + uint64_t global : 1; // only if large ; TODO enable CR4.PGE + uint64_t _unused2 : 2; + uint64_t _unused3 : 1; // HLAT + + uint64_t address : 40; // highest bit - PAT + + uint64_t _unused4 : 7; + uint64_t pke : 4; + uint64_t noexec : 1; +} __attribute__((packed)); + +struct pte { // page table entry, 4kb page + uint64_t present : 1; + uint64_t writeable : 1; + uint64_t user : 1; + uint64_t writethru : 1; + + uint64_t uncached : 1; + uint64_t accessed : 1; + uint64_t dirty : 1; + uint64_t pat : 1; + + uint64_t global : 1; // TODO enable CR4.PGE + uint64_t _unused2 : 2; + uint64_t _unused3 : 1; // HLAT + + uint64_t address : 40; + + uint64_t _unused4 : 7; + uint64_t pke : 4; + uint64_t noexec : 1; +} __attribute__((packed)); + +__attribute__((aligned(4096))) +struct pml4e pml4_identity[512]; + +__attribute__((aligned(4096))) +struct pdpte pdpte_low[512]; // 0-512gb + +__attribute__((aligned(4096))) +struct pde pde_low[512]; // 0-1gb + + +static void *memset32(void *s, int c, size_t n) { + uint8_t *s2 = s; + for (size_t i = 0; i < n; i++) + s2[i] = c; + return s; +} + + +void pml4_identity_init(void) { + memset32(pml4_identity, 0, sizeof pml4_identity); + memset32(pdpte_low, 0, sizeof pdpte_low); + memset32(pde_low, 0, sizeof pde_low); + + pml4_identity[0] = (struct pml4e) { + .present = 1, + .writeable = 1, + .address = ((uintptr_t)pdpte_low) >> 12, + }; + + pdpte_low[0] = (struct pdpte) { + .present = 1, + .writeable = 1, + .address = ((uintptr_t)pde_low) >> 12, + }; + + for (int i = 0; i < 512; i++) { + pde_low[i] = (struct pde) { + .present = 1, + .writeable = 1, + .large = 1, + .address = (i * 2 * 1024 * 1024) >> 12, + }; + } +} diff --git a/src/kernel/arch/amd64/ata.c b/src/kernel/arch/amd64/ata.c new file mode 100644 index 0000000..371730e --- /dev/null +++ b/src/kernel/arch/amd64/ata.c @@ -0,0 +1,151 @@ +#include +#include +#include +#include + +static struct { + enum { + DEV_UNKNOWN, + DEV_PATA, + DEV_PATAPI, + } type; + uint32_t sectors; +} ata_drives[4]; + +enum { + DATA = 0, + FEAT = 1, + SCNT = 2, + LBAlo = 3, + LBAmid = 4, + LBAhi = 5, + DRV = 6, + CMD = 7, + STATUS = 7, + + /* note: the OSDev wiki uses a different base port for the control port + * however i can just use this offset and stuff will just work tm */ + CTRL = 0x206, +}; // offsets + +// get I/O port base for drive +static uint16_t ata_iobase(int drive) { + bool secondary = drive&2; + return secondary ? 0x170 : 0x1F0; +} + +static void ata_400ns(void) { + uint16_t base = ata_iobase(0); // doesn't matter + for (int i = 0; i < 4; i++) + port_in8(base + STATUS); +} + +static void ata_driveselect(int drive, int block) { + uint8_t v = 0xE0; + if (drive&1) // slave? + v |= 0x10; // set drive number bit + // TODO account for block + port_out8(ata_iobase(drive) + DRV, v); +} + +static void ata_softreset(int drive) { + uint16_t iobase = ata_iobase(drive); + port_out8(iobase + CTRL, 4); + port_out8(iobase + CTRL, 0); + ata_400ns(); + + uint16_t timeout = 10000; + while (--timeout) { // TODO separate polling function + uint8_t v = port_in8(iobase + STATUS); + if (v & 0x80) continue; // still BSY, continue + if (v & 0x40) break; // RDY, break + // TODO check for ERR + } +} + +static void ata_detecttype(int drive) { + ata_softreset(drive); + ata_driveselect(drive, 0); + ata_400ns(); + switch (port_in8(ata_iobase(drive) + LBAmid)) { + case 0: + ata_drives[drive].type = DEV_PATA; + break; + case 0x14: + ata_drives[drive].type = DEV_PATAPI; + break; + default: + ata_drives[drive].type = DEV_UNKNOWN; + break; + } +} + +static bool ata_identify(int drive) { + uint16_t iobase = ata_iobase(drive); + uint16_t data[256]; + uint8_t v; + + ata_driveselect(drive, 0); + for (int i = 2; i < 6; i++) + port_out8(iobase + i, 0); + switch (ata_drives[drive].type) { + case DEV_PATA: + port_out8(iobase + CMD, 0xEC); // IDENTIFY + break; + case DEV_PATAPI: + port_out8(iobase + CMD, 0xA1); // IDENTIFY PACKET DEVICE + break; + default: panic_invalid_state(); + } + + v = port_in8(iobase + STATUS); + if (v == 0) return false; // nonexistent drive + while (port_in8(iobase + STATUS) & 0x80); + + /* pool until bit 3 (DRQ) or 0 (ERR) is set */ + while (!((v = port_in8(iobase + STATUS) & 0x9))); + if (v & 1) return false; /* ERR was set, bail */ + + for (int i = 0; i < 256; i++) + data[i] = port_in16(iobase); + ata_drives[drive].sectors = data[60] | (data[61] << 16); + return true; +} + +void ata_init(void) { + for (int i = 0; i < 4; i++) { + ata_detecttype(i); + if (ata_drives[i].type == DEV_PATA) + ata_identify(i); + } +} + +bool ata_available(int drive) { + return ata_drives[drive].type != DEV_UNKNOWN; +} + +int ata_read(int drive, uint32_t lba, void *buf) { + assert(ata_drives[drive].type == DEV_PATA); + int iobase = ata_iobase(drive); + + ata_driveselect(drive, lba); + port_out8(iobase + FEAT, 0); // supposedly pointless + port_out8(iobase + SCNT, 1); // sector count + port_out8(iobase + LBAlo, lba); + port_out8(iobase + LBAmid, lba >> 8); + port_out8(iobase + LBAhi, lba >> 16); + port_out8(iobase + CMD, 0x20); // READ SECTORS + + for (;;) { // TODO separate polling function + uint8_t v = port_in8(iobase + STATUS); + if (v & 0x80) continue; // still BSY, continue + if (v & 0x40) break; // RDY, break + // TODO check for ERR + } + + uint16_t *b = buf; + for (int i = 0; i < 256; i++) + b[i] = port_in16(iobase); + + return 512; +} diff --git a/src/kernel/arch/amd64/ata.h b/src/kernel/arch/amd64/ata.h new file mode 100644 index 0000000..82f4f81 --- /dev/null +++ b/src/kernel/arch/amd64/ata.h @@ -0,0 +1,7 @@ +#pragma once +#include +#include + +void ata_init(void); +bool ata_available(int drive); +int ata_read(int drive, uint32_t lba, void *buf); diff --git a/src/kernel/arch/amd64/boot.c b/src/kernel/arch/amd64/boot.c new file mode 100644 index 0000000..0e61ed3 --- /dev/null +++ b/src/kernel/arch/amd64/boot.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void find_init(struct multiboot_info *multiboot, struct kmain_info *info) +{ + struct multiboot_mod *module = (void*)multiboot->mods; + kprintf("mods count 0x%x", multiboot->mods_count); + if (multiboot->mods_count < 1) { + kprintf("can't find init! "); + panic_invalid_state(); + } + info->init.at = module->start; + info->init.size = module->end - module->start; +} + +void kmain_early(struct multiboot_info *multiboot) { + struct kmain_info info; + + tty_init(); + kprintf("idt..."); + idt_init(); + kprintf("irq..."); + irq_init(); + + info.memtop = (void*) (multiboot->mem_upper * 1024); + find_init(multiboot, &info); + kprintf("mem...\n"); + mem_init(&info); + + kprintf("rootfs..."); + vfs_root_init(); + ps2_init(); + serial_init(); + + kprintf("ata..."); + ata_init(); + + kmain(info); +} diff --git a/src/kernel/arch/amd64/boot.h b/src/kernel/arch/amd64/boot.h new file mode 100644 index 0000000..98adff5 --- /dev/null +++ b/src/kernel/arch/amd64/boot.h @@ -0,0 +1,4 @@ +#pragma once +#include + +void kmain_early(struct multiboot_info *multiboot); diff --git a/src/kernel/arch/amd64/boot64.s b/src/kernel/arch/amd64/boot64.s new file mode 100644 index 0000000..1dca0ca --- /dev/null +++ b/src/kernel/arch/amd64/boot64.s @@ -0,0 +1,13 @@ +.global boot64 +boot64: + lgdt (lgdt_arg) // try reloading gdt again + mov $(5 << 3 | 3), %ax // SEG_TSS + ltr %ax + + xchgw %bx, %bx + push %rdi // preserve multiboot struct + call sysenter_setup + pop %rdi + + // multiboot struct in %rdi + jmp kmain_early diff --git a/src/kernel/arch/amd64/debug.c b/src/kernel/arch/amd64/debug.c new file mode 100644 index 0000000..52127da --- /dev/null +++ b/src/kernel/arch/amd64/debug.c @@ -0,0 +1,18 @@ +#include + +void *debug_caller(size_t depth) { + void **rbp; + asm("mov %%rbp, %0" + : "=r" (rbp)); + while (depth--) { + if (!rbp) return NULL; + rbp = *rbp; + } + return rbp[1]; +} + +void debug_stacktrace(void) { + for (size_t i = 0; i < 16; i++) { + kprintf(" k/%08x\n", (uintptr_t)debug_caller(i)); + } +} diff --git a/src/kernel/arch/amd64/driver/fsroot.c b/src/kernel/arch/amd64/driver/fsroot.c new file mode 100644 index 0000000..02a4a21 --- /dev/null +++ b/src/kernel/arch/amd64/driver/fsroot.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + HANDLE_ROOT, + HANDLE_VGA, + HANDLE_ATA_ROOT, + HANDLE_ATA, + _SKIP = HANDLE_ATA + 4, +}; + +static bool exacteq(struct vfs_request *req, const char *str) { + size_t len = strlen(str); + assert(req->input.kern); + return req->input.len == len && !memcmp(req->input.buf_kern, str, len); +} + +/* truncates the length */ +static void req_preprocess(struct vfs_request *req, size_t max_len) { + if (req->offset < 0) { + // TODO negative offsets + req->offset = 0; + } + + if (req->offset >= capped_cast32(max_len)) { + req->input.len = 0; + req->output.len = 0; + req->offset = max_len; + return; + } + + req->input.len = min(req->input.len, max_len - req->offset); + req->output.len = min(req->output.len, max_len - req->offset); + + assert(req->input.len + req->offset <= max_len); + assert(req->input.len + req->offset <= max_len); +} + + +static int handle(struct vfs_request *req) { + assert(req->caller); + int id = (int)req->id; + switch (req->type) { + case VFSOP_OPEN: + if (exacteq(req, "/")) return HANDLE_ROOT; + if (exacteq(req, "/vga")) return HANDLE_VGA; + + if (exacteq(req, "/ata/")) return HANDLE_ATA_ROOT; + if (exacteq(req, "/ata/0")) + return ata_available(0) ? HANDLE_ATA+0 : -1; + if (exacteq(req, "/ata/1")) + return ata_available(1) ? HANDLE_ATA+1 : -1; + if (exacteq(req, "/ata/2")) + return ata_available(2) ? HANDLE_ATA+2 : -1; + if (exacteq(req, "/ata/3")) + return ata_available(3) ? HANDLE_ATA+3 : -1; + + return -1; + + case VFSOP_READ: + switch (id) { + case HANDLE_ROOT: { + // TODO document directory read format + const char src[] = + "vga\0" + "com1\0" + "ps2\0" + "ata/"; + req_preprocess(req, sizeof src); + virt_cpy_to(req->caller->pages, req->output.buf, + src + req->offset, req->output.len); + return req->output.len; + } + case HANDLE_VGA: { + char *vga = (void*)0xB8000; + req_preprocess(req, 80*25*2); + virt_cpy_to(req->caller->pages, req->output.buf, + vga + req->offset, req->output.len); + return req->output.len; + } + case HANDLE_ATA_ROOT: { + char list[8] = {}; + size_t len = 0; + for (int i = 0; i < 4; i++) { + if (ata_available(i)) { + list[len] = '0' + i; + len += 2; + } + } + req_preprocess(req, len); + virt_cpy_to(req->caller->pages, req->output.buf, + list + req->offset, req->output.len); + return req->output.len; + } + case HANDLE_ATA: case HANDLE_ATA+1: + case HANDLE_ATA+2: case HANDLE_ATA+3: { + if (req->offset < 0) return 0; + char buf[512]; + uint32_t sector = req->offset / 512; + size_t len = min(req->output.len, 512 - ((size_t)req->offset & 511)); + ata_read(id - HANDLE_ATA, sector, buf); + virt_cpy_to(req->caller->pages, req->output.buf, buf, len); + return len; + } + default: panic_invalid_state(); + } + + case VFSOP_WRITE: + switch (id) { + case HANDLE_VGA: { + void *vga = (void*)0xB8000; + req_preprocess(req, 80*25*2); + virt_cpy_from(req->caller->pages, vga + req->offset, + req->input.buf, req->input.len); + return req->input.len; + } + default: return -1; + } + + case VFSOP_CLOSE: + return 0; + + default: panic_invalid_state(); + } +} + +static void accept(struct vfs_request *req) { + if (req->caller) { + vfsreq_finish_short(req, handle(req)); + } else { + vfsreq_finish_short(req, -1); + } +} + +static bool is_ready(struct vfs_backend __attribute__((unused)) *self) { + return true; +} + +static struct vfs_backend backend = BACKEND_KERN(is_ready, accept); +void vfs_root_init(void) { vfs_mount_root_register("", &backend); } diff --git a/src/kernel/arch/amd64/driver/fsroot.h b/src/kernel/arch/amd64/driver/fsroot.h new file mode 100644 index 0000000..dd72202 --- /dev/null +++ b/src/kernel/arch/amd64/driver/fsroot.h @@ -0,0 +1,2 @@ +#pragma once +void vfs_root_init(void); diff --git a/src/kernel/arch/amd64/driver/ps2.c b/src/kernel/arch/amd64/driver/ps2.c new file mode 100644 index 0000000..8a5a078 --- /dev/null +++ b/src/kernel/arch/amd64/driver/ps2.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include + +#define BACKLOG_CAPACITY 64 +static volatile uint8_t backlog_buf[BACKLOG_CAPACITY]; +static volatile ring_t backlog = {(void*)backlog_buf, BACKLOG_CAPACITY, 0, 0}; + +static void accept(struct vfs_request *req); +static bool is_ready(struct vfs_backend *self); + +static struct vfs_request *blocked_on = NULL; +static struct vfs_backend backend = BACKEND_KERN(is_ready, accept); +void ps2_init(void) { vfs_mount_root_register("/ps2", &backend); } + + +void ps2_recv(uint8_t s) { + ring_put1b((void*)&backlog, s); + if (blocked_on) { + accept(blocked_on); + blocked_on = NULL; + vfs_backend_tryaccept(&backend); + } +} + +static void accept(struct vfs_request *req) { + // when you fix something here go also fix it in the COM1 driver + static uint8_t buf[32]; // pretty damn stupid + int ret; + bool valid; + switch (req->type) { + case VFSOP_OPEN: + valid = req->input.len == 0; + vfsreq_finish_short(req, valid ? 0 : -1); + break; + case VFSOP_READ: + if (ring_size((void*)&backlog) == 0) { + // nothing to read + blocked_on = req; + } else if (req->caller) { + ret = clamp(0, req->output.len, sizeof buf); + ret = ring_get((void*)&backlog, buf, ret); + virt_cpy_to(req->caller->pages, req->output.buf, buf, ret); + vfsreq_finish_short(req, ret); + } else { + vfsreq_finish_short(req, -1); + } + break; + default: + vfsreq_finish_short(req, -1); + break; + } +} + +static bool is_ready(struct vfs_backend __attribute__((unused)) *self) { + return blocked_on == NULL; +} diff --git a/src/kernel/arch/amd64/driver/ps2.h b/src/kernel/arch/amd64/driver/ps2.h new file mode 100644 index 0000000..54e8fb2 --- /dev/null +++ b/src/kernel/arch/amd64/driver/ps2.h @@ -0,0 +1,5 @@ +#pragma once +#include + +void ps2_recv(uint8_t s); +void ps2_init(void); diff --git a/src/kernel/arch/amd64/driver/serial.c b/src/kernel/arch/amd64/driver/serial.c new file mode 100644 index 0000000..6dda657 --- /dev/null +++ b/src/kernel/arch/amd64/driver/serial.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define BACKLOG_CAPACITY 64 +static volatile uint8_t backlog_buf[BACKLOG_CAPACITY]; +static volatile ring_t backlog = {(void*)backlog_buf, BACKLOG_CAPACITY, 0, 0}; + +static const int COM1 = 0x3f8; + +static void accept(struct vfs_request *req); +static bool is_ready(struct vfs_backend *self); + +static struct vfs_request *blocked_on = NULL; +static struct vfs_backend backend = BACKEND_KERN(is_ready, accept); +void serial_init(void) { vfs_mount_root_register("/com1", &backend); } + + +static void serial_selftest(void) { + char b = 0x69; + port_out8(COM1 + 4, 0b00011110); // enable loopback mode + port_out8(COM1, b); + assert(port_in8(COM1) == b); +} + +void serial_preinit(void) { + // see https://www.sci.muni.cz/docs/pc/serport.txt + // set baud rate divisor + port_out8(COM1 + 3, 0b10000000); // enable DLAB + port_out8(COM1 + 0, 0x01); // divisor = 1 (low byte) + port_out8(COM1 + 1, 0x00); // (high byte) + + port_out8(COM1 + 3, 0b00000011); // 8 bits, no parity, one stop bit + port_out8(COM1 + 1, 0x01); // enable the Data Ready IRQ + port_out8(COM1 + 2, 0b11000111); // enable FIFO with 14-bit trigger level (???) + + serial_selftest(); + + port_out8(COM1 + 4, 0b00001111); // enable everything in the MCR +} + + +void serial_irq(void) { + ring_put1b((void*)&backlog, port_in8(COM1)); + if (blocked_on) { + accept(blocked_on); + blocked_on = NULL; + vfs_backend_tryaccept(&backend); + } +} + + +static void serial_putchar(char c) { + while ((port_in8(COM1 + 5) & 0x20) == 0); // wait for THRE + port_out8(COM1, c); +} + +void serial_write(const char *buf, size_t len) { + for (size_t i = 0; i < len; i++) + serial_putchar(buf[i]); +} + + +static void accept(struct vfs_request *req) { + static char buf[32]; + int ret; + bool valid; + switch (req->type) { + case VFSOP_OPEN: + valid = req->input.len == 0; + vfsreq_finish_short(req, valid ? 0 : -1); + break; + case VFSOP_READ: + if (ring_size((void*)&backlog) == 0) { + /* nothing to read, join queue */ + assert(!req->postqueue_next); + struct vfs_request **slot = &blocked_on; + while (*slot) + slot = &(*slot)->postqueue_next; + *slot = req; + } else if (req->caller) { + ret = clamp(0, req->output.len, sizeof buf); + ret = ring_get((void*)&backlog, buf, ret); + virt_cpy_to(req->caller->pages, req->output.buf, buf, ret); + vfsreq_finish_short(req, ret); + } else { + vfsreq_finish_short(req, -1); + } + break; + case VFSOP_WRITE: + if (req->caller) { + struct virt_iter iter; + virt_iter_new(&iter, req->input.buf, req->input.len, + req->caller->pages, true, false); + while (virt_iter_next(&iter)) + serial_write(iter.frag, iter.frag_len); + ret = iter.prior; + } else ret = -1; + vfsreq_finish_short(req, ret); + break; + default: + vfsreq_finish_short(req, -1); + break; + } +} + +static bool is_ready(struct vfs_backend __attribute__((unused)) *self) { + return true; +} diff --git a/src/kernel/arch/amd64/driver/serial.h b/src/kernel/arch/amd64/driver/serial.h new file mode 100644 index 0000000..6a4876e --- /dev/null +++ b/src/kernel/arch/amd64/driver/serial.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include +#include + +void serial_preinit(void); +void serial_irq(void); +void serial_write(const char *buf, size_t len); + +void serial_init(void); 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 +#include +#include +#include +#include +#include + +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 +#include +#include + +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 +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include + +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: diff --git a/src/kernel/arch/amd64/multiboot.h b/src/kernel/arch/amd64/multiboot.h new file mode 100644 index 0000000..c6a2650 --- /dev/null +++ b/src/kernel/arch/amd64/multiboot.h @@ -0,0 +1,29 @@ +#pragma once +#include + +struct multiboot_mod { + uint32_t start; + uint32_t end; + uint32_t str; + uint32_t _reserved; +} __attribute__((packed)); + +struct multiboot_info { + uint32_t flag_mem : 1; + uint32_t flag_boot_device : 1; + uint32_t flag_cmdline : 1; + uint32_t flag_mods : 1; + uint32_t _flag_other : 28; // unimplemented + + uint32_t mem_lower; + uint32_t mem_upper; + + uint32_t boot_device; + + uint32_t cmdline; + + uint32_t mods_count; + uint32_t mods; + + // [...] +} __attribute__((packed)); diff --git a/src/kernel/arch/amd64/multiboot.s b/src/kernel/arch/amd64/multiboot.s new file mode 100644 index 0000000..dc19b36 --- /dev/null +++ b/src/kernel/arch/amd64/multiboot.s @@ -0,0 +1,12 @@ +.set MAGIC, 0x1BADB002 + +/* 1<<0 - align modules on page boundaries. */ +.set FLAGS, 1<<0 +.set CHECKSUM, -(MAGIC + FLAGS) + +.section .multiboot +.align 4 +multiboot_header: + .long MAGIC + .long FLAGS + .long CHECKSUM diff --git a/src/kernel/arch/amd64/pagedir.c b/src/kernel/arch/amd64/pagedir.c new file mode 100644 index 0000000..334cfc2 --- /dev/null +++ b/src/kernel/arch/amd64/pagedir.c @@ -0,0 +1,213 @@ +#include +#include +#include +#include + +/* nitpick: I highly recommend you dont use bitfields for paging + * structures + * you can't change them atomically and the way they're layed out + * in memory is implementation defined iirc + */ +struct pagetable_entry { + uint32_t present : 1; + uint32_t writeable : 1; + uint32_t user : 1; + uint32_t writethru : 1; + uint32_t uncached : 1; + uint32_t dirty : 1; + uint32_t always0 : 1; // memory type thing? + uint32_t global : 1; + uint32_t _unused : 3; + uint32_t address : 21; +}; + +struct pagedir_entry { + uint32_t present : 1; + uint32_t _writeable: 1; // don't use! not checked by multiple functions here + uint32_t _user : 1; // ^ + uint32_t writethru : 1; + uint32_t uncached : 1; + uint32_t accessed : 1; + uint32_t always0 : 1; + uint32_t large : 1; // 4 MiB instead of 4 KiB + uint32_t _unused : 3; + uint32_t address : 21; +} __attribute__((packed)); + +struct pagedir { + struct pagedir_entry e[1024]; +} __attribute__((packed)); + + +struct pagedir *pagedir_new(void) { + struct pagedir *dir = page_alloc(1); + for (int i = 0; i < 1024; i++) + dir->e[i].present = 0; + return dir; +} + +void pagedir_free(struct pagedir *dir) { + // assumes all user pages are unique and can be freed + struct pagetable_entry *pt; + void *page; + + for (int i = 0; i < 1024; i++) { + if (!dir->e[i].present) continue; + + pt = (void*)(dir->e[i].address << 11); + + for (int j = 0; j < 1024; j++) { + if (!pt[j].present) continue; + if (!pt[j].user) continue; + + page = (void*)(pt[j].address << 11); + page_free(page, 1); + } + page_free(pt, 1); + } + page_free(dir, 1); +} + +static struct pagetable_entry* +get_entry(struct pagedir *dir, const void __user *virt) { + uint32_t pd_idx = ((uintptr_t)virt) >> 22; + uint32_t pt_idx = ((uintptr_t)virt) >> 12 & 0x03FF; + struct pagetable_entry *pagetable; + + if (!dir->e[pd_idx].present) return NULL; + + pagetable = (void*)(dir->e[pd_idx].address << 11); + return &pagetable[pt_idx]; +} + +void *pagedir_unmap(struct pagedir *dir, void __user *virt) { + void *phys = pagedir_virt2phys(dir, virt, false, false); + struct pagetable_entry *page = get_entry(dir, virt); + page->present = false; + return phys; +} + +void pagedir_map(struct pagedir *dir, void __user *virt, void *phys, + bool user, bool writeable) +{ + kprintf("in pagedir_map, dir 0x%x\n", dir); + uintptr_t virt_cast = (uintptr_t) virt; + uint32_t pd_idx = virt_cast >> 22; + uint32_t pt_idx = virt_cast >> 12 & 0x03FF; + struct pagetable_entry *pagetable; + + kprintf("pre-if, accessing 0x%x because 0x%x\n", &dir->e[pd_idx], pd_idx); + if (dir->e[pd_idx].present) { + kprintf("present already\n"); + pagetable = (void*) (dir->e[pd_idx].address << 11); + } else { + kprintf("allocing\n"); + pagetable = page_alloc(1); + kprintf("alloc successful\n"); + for (int i = 0; i < 1024; i++) + pagetable[i].present = 0; + + dir->e[pd_idx] = (struct pagedir_entry) { + .present = 1, + ._writeable= 1, + ._user = 1, + .writethru = 1, + .uncached = 0, + .accessed = 0, + .always0 = 0, + .large = 0, + ._unused = 0, + .address = (uintptr_t) pagetable >> 11 + }; + } + + pagetable[pt_idx] = (struct pagetable_entry) { + .present = 1, + .writeable = writeable, + .user = user, + .writethru = 1, + .uncached = 0, + .dirty = 0, + .always0 = 0, + .global = 0, + ._unused = 0, + .address = (uintptr_t) phys >> 11 + }; + kprintf("out pagedir_map\n"); +} + +extern void *pagedir_current; +void pagedir_switch(struct pagedir *dir) { + pagedir_current = dir; +} + +// creates a new pagedir with exact copies of the user pages +struct pagedir *pagedir_copy(const struct pagedir *orig) { + struct pagedir *clone = page_alloc(1); + struct pagetable_entry *orig_pt, *clone_pt; + void *orig_page, *clone_page; + + for (int i = 0; i < 1024; i++) { + clone->e[i] = orig->e[i]; + if (!orig->e[i].present) continue; + + orig_pt = (void*)(orig->e[i].address << 11); + clone_pt = page_alloc(1); + clone->e[i].address = (uintptr_t) clone_pt >> 11; + + for (int j = 0; j < 1024; j++) { + clone_pt[j] = orig_pt[j]; + if (!orig_pt[j].present) continue; + if (!orig_pt[j].user) continue; + // i could use .global? + + orig_page = (void*)(orig_pt[j].address << 11); + clone_page = page_alloc(1); + clone_pt[j].address = (uintptr_t) clone_page >> 11; + + memcpy(clone_page, orig_page, PAGE_SIZE); + } + } + + return clone; +} + +bool pagedir_iskern(struct pagedir *dir, const void __user *virt) { + struct pagetable_entry *page = get_entry(dir, virt); + return page && page->present && !page->user; +} + +void *pagedir_virt2phys(struct pagedir *dir, const void __user *virt, + bool user, bool writeable) +{ + struct pagetable_entry *page; + uintptr_t phys; + page = get_entry(dir, virt); + if (!page || !page->present) return NULL; + if (user && !page->user) return NULL; + if (writeable && !page->writeable) return NULL; + + phys = page->address << 11; + phys |= ((uintptr_t)virt) & 0xFFF; + return (void*)phys; +} + +void __user *pagedir_findfree(struct pagedir *dir, char __user *start, size_t len) { + struct pagetable_entry *page; + char __user *iter; + start = (userptr_t)(((uintptr_t __force)start + PAGE_MASK) & ~PAGE_MASK); // round up to next page + iter = start; + + while (iter < (char __user *)0xFFF00000) { // TODO better boundary + page = get_entry(dir, iter); + if (page && page->present) { + start = iter + PAGE_SIZE; + } else { + if ((size_t)(iter + PAGE_SIZE - start) >= len) + return start; + } + iter += PAGE_SIZE; + } + + return NULL; +} diff --git a/src/kernel/arch/amd64/port_io.h b/src/kernel/arch/amd64/port_io.h new file mode 100644 index 0000000..eac9331 --- /dev/null +++ b/src/kernel/arch/amd64/port_io.h @@ -0,0 +1,22 @@ +#include + +static inline void port_out8(uint16_t port, uint8_t val) { + asm volatile("outb %0, %1" : : "a" (val), "Nd" (port)); +} + +static inline void port_out16(uint16_t port, uint16_t val) { + asm volatile("outw %0, %1" : : "a" (val), "Nd" (port)); +} + +static inline uint8_t port_in8(uint16_t port) { + uint8_t val; + asm volatile("inb %1, %0" : "=a" (val) : "Nd" (port)); + return val; +} + +static inline uint16_t port_in16(uint16_t port) { + uint16_t val; + asm volatile("inw %1, %0" : "=a" (val) : "Nd" (port)); + return val; +} + diff --git a/src/kernel/arch/amd64/registers.h b/src/kernel/arch/amd64/registers.h new file mode 100644 index 0000000..e365e2c --- /dev/null +++ b/src/kernel/arch/amd64/registers.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +struct registers { + uint64_t edi, esi; + userptr_t ebp, esp; + uint64_t ebx, edx, ecx, eax; + + userptr_t eip; +} __attribute__((__packed__)); + +// saves a return value according to the SysV ABI +static inline uint64_t regs_savereturn(struct registers *regs, uint64_t value) { + regs->eax = value; + regs->edx = value >> 32; // TODO check ABI + return value; +} diff --git a/src/kernel/arch/amd64/sysenter.c b/src/kernel/arch/amd64/sysenter.c new file mode 100644 index 0000000..e42ec7d --- /dev/null +++ b/src/kernel/arch/amd64/sysenter.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +struct registers _sysexit_regs; + +void sysexit(struct registers regs) { + _sysexit_regs = regs; + _sysexit_regs.ecx = (uintptr_t) regs.esp; + _sysexit_regs.edx = (uintptr_t) regs.eip; + _sysexit_real(); + __builtin_unreachable(); +} + +_Noreturn void sysenter_stage2(void) { + kprintf("ring0 again!\n"); + struct registers *regs = &process_current->regs; + + *regs = _sysexit_regs; // save the registers + regs->esp = (userptr_t) regs->ecx; // fix them up + regs->eip = (userptr_t) regs->edx; + + _syscall(regs->eax, regs->ebx, + regs->esi, regs->edi, (uintptr_t)regs->ebp); + process_switch_any(); +} diff --git a/src/kernel/arch/amd64/sysenter.h b/src/kernel/arch/amd64/sysenter.h new file mode 100644 index 0000000..b88c186 --- /dev/null +++ b/src/kernel/arch/amd64/sysenter.h @@ -0,0 +1,8 @@ +#pragma once + +// sysenter.c +extern struct registers _sysexit_regs; +_Noreturn void sysenter_stage2(void); + +// sysenter.s +void _sysexit_real(void); diff --git a/src/kernel/arch/amd64/sysenter.s b/src/kernel/arch/amd64/sysenter.s new file mode 100644 index 0000000..4dfe19d --- /dev/null +++ b/src/kernel/arch/amd64/sysenter.s @@ -0,0 +1,93 @@ +/* TODO include gdt.h */ +.set SEG_r0code, 1 +.set SEG_r3code, 3 +.set SEG_r3data, 4 + +.set IA32_SYSENTER_CS, 0x174 +.set IA32_SYSENTER_ESP, 0x175 +.set IA32_SYSENTER_EIP, 0x176 + +.section .text +.global sysenter_setup +.type sysenter_setup, @function +sysenter_setup: + xor %rdx, %rdx + + mov $(SEG_r0code << 3), %rax + mov $IA32_SYSENTER_CS, %rcx + wrmsr + + mov $IA32_SYSENTER_ESP, %rcx + mov $0, %rax // unused + wrmsr + + mov $IA32_SYSENTER_EIP, %rcx + mov $sysenter_stage1, %rax + wrmsr + + ret + + +.section .shared + +.global stored_eax +stored_eax: +.long 0 + +.global pagedir_current +// a hack to maintain compat with the old arch api, TODO +pagedir_current: +.long 0 + +.global _sysexit_real +.type _sysexit_real, @function +_sysexit_real: + xchgw %bx, %bx + mov $(SEG_r3data << 3 | 3), %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + + // restore the registers + mov $_sysexit_regs, %rsp + pop %rdi + pop %rsi + pop %rbp + add $8, %rsp + pop %rbx + pop %rdx + pop %rcx + pop %rax + + // enable paging + // %rsp used as a scratch register + mov (pagedir_current), %rsp + mov %rsp, %cr3 + sysexit + +sysenter_stage1: + cli /* prevent random IRQs in the middle of kernel code */ + xchgw %bx, %bx + + // disable paging + // I don't want to damage any of the registers passed in by the user, + // so i'm using ESP as a temporary register. At this point there's nothing + // useful in it, it's == _bss_end. + mov %cr0, %rsp + and $0x7FFFFFFF, %rsp // disable paging + mov %rsp, %cr0 + + // save the registers + mov $(_sysexit_regs + 64), %rsp + push %rax + push %rcx + push %rdx + push %rbx + push $0x0 // pushal pushes %rsp here, but that's worthless + push %rbp + push %rsi + push %rdi + + mov $_bss_end, %rsp + jmp sysenter_stage2 diff --git a/src/kernel/arch/amd64/tty/tty.c b/src/kernel/arch/amd64/tty/tty.c new file mode 100644 index 0000000..6593ef6 --- /dev/null +++ b/src/kernel/arch/amd64/tty/tty.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +void tty_init(void) { + vga_clear(); + serial_preinit(); + + vga_write("\x03 ", 2); // cp437 heart + serial_write("<3 ", 3); +} + +static void backend(void __attribute__((unused)) *arg, const char *buf, size_t len) { + vga_write(buf, len); + serial_write(buf, len); +} + +int kprintf(const char *fmt, ...) { + int ret; + va_list argp; + va_start(argp, fmt); + ret = __printf_internal(fmt, argp, backend, NULL); + va_end(argp); + return ret; +} diff --git a/src/kernel/arch/amd64/tty/tty.h b/src/kernel/arch/amd64/tty/tty.h new file mode 100644 index 0000000..b96003d --- /dev/null +++ b/src/kernel/arch/amd64/tty/tty.h @@ -0,0 +1,7 @@ +#pragma once +#include + +void vga_write(const char *buf, size_t len); +void vga_clear(void); + +void tty_init(void); diff --git a/src/kernel/arch/amd64/tty/vga.c b/src/kernel/arch/amd64/tty/vga.c new file mode 100644 index 0000000..e5f2274 --- /dev/null +++ b/src/kernel/arch/amd64/tty/vga.c @@ -0,0 +1,33 @@ +#include + +struct vga_cell { + unsigned char c; + unsigned char style; +} __attribute__((__packed__)); + +static const size_t vga_len = 80 * 25; +static struct vga_cell *vga = (void*) 0xB8000; +static size_t vga_pos = 0; + +static void vga_scroll(void) { + for (size_t i = 0; i < vga_len - 80; i++) + vga[i] = vga[i + 80]; + vga_pos -= 80; +} + +static void vga_putchar(char c) { + if (vga_pos >= vga_len - 80) + vga_scroll(); + vga[vga_pos++].c = c; +} + +void vga_write(const char *buf, size_t len) { + for (size_t i = 0; i < len; i++) + vga_putchar(buf[i]); +} + +void vga_clear(void) { + for (size_t i = 0; i < vga_len; i++) + vga[i].c = ' '; + vga_pos = 0; +} diff --git a/src/kernel/arch/generic.h b/src/kernel/arch/generic.h index a9e1ac9..d1ff292 100644 --- a/src/kernel/arch/generic.h +++ b/src/kernel/arch/generic.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include #include @@ -15,7 +15,7 @@ extern char _bss_end; __attribute__((noreturn)) -void halt_cpu(void); +void cpu_halt(void); __attribute__((noreturn)) void cpu_shutdown(void); diff --git a/src/kernel/arch/i386/ata.c b/src/kernel/arch/i386/ata.c deleted file mode 100644 index 56344e4..0000000 --- a/src/kernel/arch/i386/ata.c +++ /dev/null @@ -1,151 +0,0 @@ -#include -#include -#include -#include - -static struct { - enum { - DEV_UNKNOWN, - DEV_PATA, - DEV_PATAPI, - } type; - uint32_t sectors; -} ata_drives[4]; - -enum { - DATA = 0, - FEAT = 1, - SCNT = 2, - LBAlo = 3, - LBAmid = 4, - LBAhi = 5, - DRV = 6, - CMD = 7, - STATUS = 7, - - /* note: the OSDev wiki uses a different base port for the control port - * however i can just use this offset and stuff will just work tm */ - CTRL = 0x206, -}; // offsets - -// get I/O port base for drive -static uint16_t ata_iobase(int drive) { - bool secondary = drive&2; - return secondary ? 0x170 : 0x1F0; -} - -static void ata_400ns(void) { - uint16_t base = ata_iobase(0); // doesn't matter - for (int i = 0; i < 4; i++) - port_in8(base + STATUS); -} - -static void ata_driveselect(int drive, int block) { - uint8_t v = 0xE0; - if (drive&1) // slave? - v |= 0x10; // set drive number bit - // TODO account for block - port_out8(ata_iobase(drive) + DRV, v); -} - -static void ata_softreset(int drive) { - uint16_t iobase = ata_iobase(drive); - port_out8(iobase + CTRL, 4); - port_out8(iobase + CTRL, 0); - ata_400ns(); - - uint16_t timeout = 10000; - while (--timeout) { // TODO separate polling function - uint8_t v = port_in8(iobase + STATUS); - if (v & 0x80) continue; // still BSY, continue - if (v & 0x40) break; // RDY, break - // TODO check for ERR - } -} - -static void ata_detecttype(int drive) { - ata_softreset(drive); - ata_driveselect(drive, 0); - ata_400ns(); - switch (port_in8(ata_iobase(drive) + LBAmid)) { - case 0: - ata_drives[drive].type = DEV_PATA; - break; - case 0x14: - ata_drives[drive].type = DEV_PATAPI; - break; - default: - ata_drives[drive].type = DEV_UNKNOWN; - break; - } -} - -static bool ata_identify(int drive) { - uint16_t iobase = ata_iobase(drive); - uint16_t data[256]; - uint8_t v; - - ata_driveselect(drive, 0); - for (int i = 2; i < 6; i++) - port_out8(iobase + i, 0); - switch (ata_drives[drive].type) { - case DEV_PATA: - port_out8(iobase + CMD, 0xEC); // IDENTIFY - break; - case DEV_PATAPI: - port_out8(iobase + CMD, 0xA1); // IDENTIFY PACKET DEVICE - break; - default: panic_invalid_state(); - } - - v = port_in8(iobase + STATUS); - if (v == 0) return false; // nonexistent drive - while (port_in8(iobase + STATUS) & 0x80); - - /* pool until bit 3 (DRQ) or 0 (ERR) is set */ - while (!((v = port_in8(iobase + STATUS) & 0x9))); - if (v & 1) return false; /* ERR was set, bail */ - - for (int i = 0; i < 256; i++) - data[i] = port_in16(iobase); - ata_drives[drive].sectors = data[60] | (data[61] << 16); - return true; -} - -void ata_init(void) { - for (int i = 0; i < 4; i++) { - ata_detecttype(i); - if (ata_drives[i].type == DEV_PATA) - ata_identify(i); - } -} - -bool ata_available(int drive) { - return ata_drives[drive].type != DEV_UNKNOWN; -} - -int ata_read(int drive, uint32_t lba, void *buf) { - assert(ata_drives[drive].type == DEV_PATA); - int iobase = ata_iobase(drive); - - ata_driveselect(drive, lba); - port_out8(iobase + FEAT, 0); // supposedly pointless - port_out8(iobase + SCNT, 1); // sector count - port_out8(iobase + LBAlo, lba); - port_out8(iobase + LBAmid, lba >> 8); - port_out8(iobase + LBAhi, lba >> 16); - port_out8(iobase + CMD, 0x20); // READ SECTORS - - for (;;) { // TODO separate polling function - uint8_t v = port_in8(iobase + STATUS); - if (v & 0x80) continue; // still BSY, continue - if (v & 0x40) break; // RDY, break - // TODO check for ERR - } - - uint16_t *b = buf; - for (int i = 0; i < 256; i++) - b[i] = port_in16(iobase); - - return 512; -} diff --git a/src/kernel/arch/i386/ata.h b/src/kernel/arch/i386/ata.h deleted file mode 100644 index 82f4f81..0000000 --- a/src/kernel/arch/i386/ata.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include -#include - -void ata_init(void); -bool ata_available(int drive); -int ata_read(int drive, uint32_t lba, void *buf); diff --git a/src/kernel/arch/i386/boot.c b/src/kernel/arch/i386/boot.c deleted file mode 100644 index 754a327..0000000 --- a/src/kernel/arch/i386/boot.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void find_init(struct multiboot_info *multiboot, struct kmain_info *info) -{ - struct multiboot_mod *module = &multiboot->mods[0]; - if (multiboot->mods_count < 1) { - kprintf("can't find init! "); - panic_invalid_state(); - } - info->init.at = module->start; - info->init.size = module->end - module->start; - -} - -void kmain_early(struct multiboot_info *multiboot) { - struct kmain_info info; - - tty_init(); - kprintf("gdt..."); - gdt_init(); - kprintf("idt..."); - idt_init(); - kprintf("irq..."); - irq_init(); - - info.memtop = (void*) (multiboot->mem_upper * 1024); - find_init(multiboot, &info); - kprintf("mem...\n"); - mem_init(&info); - - kprintf("rootfs..."); - vfs_root_init(); - ps2_init(); - serial_init(); - - kprintf("ata..."); - ata_init(); - - kmain(info); -} diff --git a/src/kernel/arch/i386/boot.h b/src/kernel/arch/i386/boot.h deleted file mode 100644 index 544f02d..0000000 --- a/src/kernel/arch/i386/boot.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -void kmain_early(struct multiboot_info *multiboot); diff --git a/src/kernel/arch/i386/boot.s b/src/kernel/arch/i386/boot.s deleted file mode 100644 index 743c6d6..0000000 --- a/src/kernel/arch/i386/boot.s +++ /dev/null @@ -1,34 +0,0 @@ -.section .text -.global _start -.type _start, @function -_start: - mov $_stack_top, %esp - call sysenter_setup - push %ebx // address of the Multiboot struct - call kmain_early - -.global cpu_shutdown -.type cpu_shutdown, @function -cpu_shutdown: -/* This quits QEMU. While I couldn't find this officially documented anywhere, - * it is used by QEMU in tests/tcg/i386/system/boot.S (as of commit 40d6ee), so - * I assume that this is safe-ish to use */ - mov $0x604, %edx - mov $0x2000, %eax - outw %ax, %dx - -.global halt_cpu -.type halt_cpu, @function -halt_cpu: - cli -1: hlt - jmp 1b - - -.global cpu_pause -.type cpu_pause, @function -cpu_pause: - sti - hlt - cli - ret diff --git a/src/kernel/arch/i386/debug.c b/src/kernel/arch/i386/debug.c deleted file mode 100644 index b1920db..0000000 --- a/src/kernel/arch/i386/debug.c +++ /dev/null @@ -1,18 +0,0 @@ -#include - -void *debug_caller(size_t depth) { - void **ebp; - asm("mov %%ebp, %0" - : "=r" (ebp)); - while (depth--) { - if (!ebp) return NULL; - ebp = *ebp; - } - return ebp[1]; -} - -void debug_stacktrace(void) { - for (size_t i = 0; i < 16; i++) { - kprintf(" k/%08x\n", (uintptr_t)debug_caller(i)); - } -} diff --git a/src/kernel/arch/i386/driver/fsroot.c b/src/kernel/arch/i386/driver/fsroot.c deleted file mode 100644 index 8b1b307..0000000 --- a/src/kernel/arch/i386/driver/fsroot.c +++ /dev/null @@ -1,146 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -enum { - HANDLE_ROOT, - HANDLE_VGA, - HANDLE_ATA_ROOT, - HANDLE_ATA, - _SKIP = HANDLE_ATA + 4, -}; - -static bool exacteq(struct vfs_request *req, const char *str) { - size_t len = strlen(str); - assert(req->input.kern); - return req->input.len == len && !memcmp(req->input.buf_kern, str, len); -} - -/* truncates the length */ -static void req_preprocess(struct vfs_request *req, size_t max_len) { - if (req->offset < 0) { - // TODO negative offsets - req->offset = 0; - } - - if (req->offset >= capped_cast32(max_len)) { - req->input.len = 0; - req->output.len = 0; - req->offset = max_len; - return; - } - - req->input.len = min(req->input.len, max_len - req->offset); - req->output.len = min(req->output.len, max_len - req->offset); - - assert(req->input.len + req->offset <= max_len); - assert(req->input.len + req->offset <= max_len); -} - - -static int handle(struct vfs_request *req) { - assert(req->caller); - int id = (int)req->id; - switch (req->type) { - case VFSOP_OPEN: - if (exacteq(req, "/")) return HANDLE_ROOT; - if (exacteq(req, "/vga")) return HANDLE_VGA; - - if (exacteq(req, "/ata/")) return HANDLE_ATA_ROOT; - if (exacteq(req, "/ata/0")) - return ata_available(0) ? HANDLE_ATA+0 : -1; - if (exacteq(req, "/ata/1")) - return ata_available(1) ? HANDLE_ATA+1 : -1; - if (exacteq(req, "/ata/2")) - return ata_available(2) ? HANDLE_ATA+2 : -1; - if (exacteq(req, "/ata/3")) - return ata_available(3) ? HANDLE_ATA+3 : -1; - - return -1; - - case VFSOP_READ: - switch (id) { - case HANDLE_ROOT: { - // TODO document directory read format - const char src[] = - "vga\0" - "com1\0" - "ps2\0" - "ata/"; - req_preprocess(req, sizeof src); - virt_cpy_to(req->caller->pages, req->output.buf, - src + req->offset, req->output.len); - return req->output.len; - } - case HANDLE_VGA: { - char *vga = (void*)0xB8000; - req_preprocess(req, 80*25*2); - virt_cpy_to(req->caller->pages, req->output.buf, - vga + req->offset, req->output.len); - return req->output.len; - } - case HANDLE_ATA_ROOT: { - char list[8] = {}; - size_t len = 0; - for (int i = 0; i < 4; i++) { - if (ata_available(i)) { - list[len] = '0' + i; - len += 2; - } - } - req_preprocess(req, len); - virt_cpy_to(req->caller->pages, req->output.buf, - list + req->offset, req->output.len); - return req->output.len; - } - case HANDLE_ATA: case HANDLE_ATA+1: - case HANDLE_ATA+2: case HANDLE_ATA+3: { - if (req->offset < 0) return 0; - char buf[512]; - uint32_t sector = req->offset / 512; - size_t len = min(req->output.len, 512 - ((size_t)req->offset & 511)); - ata_read(id - HANDLE_ATA, sector, buf); - virt_cpy_to(req->caller->pages, req->output.buf, buf, len); - return len; - } - default: panic_invalid_state(); - } - - case VFSOP_WRITE: - switch (id) { - case HANDLE_VGA: { - void *vga = (void*)0xB8000; - req_preprocess(req, 80*25*2); - virt_cpy_from(req->caller->pages, vga + req->offset, - req->input.buf, req->input.len); - return req->input.len; - } - default: return -1; - } - - case VFSOP_CLOSE: - return 0; - - default: panic_invalid_state(); - } -} - -static void accept(struct vfs_request *req) { - if (req->caller) { - vfsreq_finish_short(req, handle(req)); - } else { - vfsreq_finish_short(req, -1); - } -} - -static bool is_ready(struct vfs_backend __attribute__((unused)) *self) { - return true; -} - -static struct vfs_backend backend = BACKEND_KERN(is_ready, accept); -void vfs_root_init(void) { vfs_mount_root_register("", &backend); } diff --git a/src/kernel/arch/i386/driver/fsroot.h b/src/kernel/arch/i386/driver/fsroot.h deleted file mode 100644 index dd72202..0000000 --- a/src/kernel/arch/i386/driver/fsroot.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -void vfs_root_init(void); diff --git a/src/kernel/arch/i386/driver/ps2.c b/src/kernel/arch/i386/driver/ps2.c deleted file mode 100644 index 341a7d3..0000000 --- a/src/kernel/arch/i386/driver/ps2.c +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#define BACKLOG_CAPACITY 64 -static volatile uint8_t backlog_buf[BACKLOG_CAPACITY]; -static volatile ring_t backlog = {(void*)backlog_buf, BACKLOG_CAPACITY, 0, 0}; - -static void accept(struct vfs_request *req); -static bool is_ready(struct vfs_backend *self); - -static struct vfs_request *blocked_on = NULL; -static struct vfs_backend backend = BACKEND_KERN(is_ready, accept); -void ps2_init(void) { vfs_mount_root_register("/ps2", &backend); } - - -void ps2_recv(uint8_t s) { - ring_put1b((void*)&backlog, s); - if (blocked_on) { - accept(blocked_on); - blocked_on = NULL; - vfs_backend_tryaccept(&backend); - } -} - -static void accept(struct vfs_request *req) { - // when you fix something here go also fix it in the COM1 driver - static uint8_t buf[32]; // pretty damn stupid - int ret; - bool valid; - switch (req->type) { - case VFSOP_OPEN: - valid = req->input.len == 0; - vfsreq_finish_short(req, valid ? 0 : -1); - break; - case VFSOP_READ: - if (ring_size((void*)&backlog) == 0) { - // nothing to read - blocked_on = req; - } else if (req->caller) { - ret = clamp(0, req->output.len, sizeof buf); - ret = ring_get((void*)&backlog, buf, ret); - virt_cpy_to(req->caller->pages, req->output.buf, buf, ret); - vfsreq_finish_short(req, ret); - } else { - vfsreq_finish_short(req, -1); - } - break; - default: - vfsreq_finish_short(req, -1); - break; - } -} - -static bool is_ready(struct vfs_backend __attribute__((unused)) *self) { - return blocked_on == NULL; -} diff --git a/src/kernel/arch/i386/driver/ps2.h b/src/kernel/arch/i386/driver/ps2.h deleted file mode 100644 index 54e8fb2..0000000 --- a/src/kernel/arch/i386/driver/ps2.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include - -void ps2_recv(uint8_t s); -void ps2_init(void); diff --git a/src/kernel/arch/i386/driver/serial.c b/src/kernel/arch/i386/driver/serial.c deleted file mode 100644 index dac5b75..0000000 --- a/src/kernel/arch/i386/driver/serial.c +++ /dev/null @@ -1,114 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#define BACKLOG_CAPACITY 64 -static volatile uint8_t backlog_buf[BACKLOG_CAPACITY]; -static volatile ring_t backlog = {(void*)backlog_buf, BACKLOG_CAPACITY, 0, 0}; - -static const int COM1 = 0x3f8; - -static void accept(struct vfs_request *req); -static bool is_ready(struct vfs_backend *self); - -static struct vfs_request *blocked_on = NULL; -static struct vfs_backend backend = BACKEND_KERN(is_ready, accept); -void serial_init(void) { vfs_mount_root_register("/com1", &backend); } - - -static void serial_selftest(void) { - char b = 0x69; - port_out8(COM1 + 4, 0b00011110); // enable loopback mode - port_out8(COM1, b); - assert(port_in8(COM1) == b); -} - -void serial_preinit(void) { - // see https://www.sci.muni.cz/docs/pc/serport.txt - // set baud rate divisor - port_out8(COM1 + 3, 0b10000000); // enable DLAB - port_out8(COM1 + 0, 0x01); // divisor = 1 (low byte) - port_out8(COM1 + 1, 0x00); // (high byte) - - port_out8(COM1 + 3, 0b00000011); // 8 bits, no parity, one stop bit - port_out8(COM1 + 1, 0x01); // enable the Data Ready IRQ - port_out8(COM1 + 2, 0b11000111); // enable FIFO with 14-bit trigger level (???) - - serial_selftest(); - - port_out8(COM1 + 4, 0b00001111); // enable everything in the MCR -} - - -void serial_irq(void) { - ring_put1b((void*)&backlog, port_in8(COM1)); - if (blocked_on) { - accept(blocked_on); - blocked_on = NULL; - vfs_backend_tryaccept(&backend); - } -} - - -static void serial_putchar(char c) { - while ((port_in8(COM1 + 5) & 0x20) == 0); // wait for THRE - port_out8(COM1, c); -} - -void serial_write(const char *buf, size_t len) { - for (size_t i = 0; i < len; i++) - serial_putchar(buf[i]); -} - - -static void accept(struct vfs_request *req) { - static char buf[32]; - int ret; - bool valid; - switch (req->type) { - case VFSOP_OPEN: - valid = req->input.len == 0; - vfsreq_finish_short(req, valid ? 0 : -1); - break; - case VFSOP_READ: - if (ring_size((void*)&backlog) == 0) { - /* nothing to read, join queue */ - assert(!req->postqueue_next); - struct vfs_request **slot = &blocked_on; - while (*slot) - slot = &(*slot)->postqueue_next; - *slot = req; - } else if (req->caller) { - ret = clamp(0, req->output.len, sizeof buf); - ret = ring_get((void*)&backlog, buf, ret); - virt_cpy_to(req->caller->pages, req->output.buf, buf, ret); - vfsreq_finish_short(req, ret); - } else { - vfsreq_finish_short(req, -1); - } - break; - case VFSOP_WRITE: - if (req->caller) { - struct virt_iter iter; - virt_iter_new(&iter, req->input.buf, req->input.len, - req->caller->pages, true, false); - while (virt_iter_next(&iter)) - serial_write(iter.frag, iter.frag_len); - ret = iter.prior; - } else ret = -1; - vfsreq_finish_short(req, ret); - break; - default: - vfsreq_finish_short(req, -1); - break; - } -} - -static bool is_ready(struct vfs_backend __attribute__((unused)) *self) { - return true; -} diff --git a/src/kernel/arch/i386/driver/serial.h b/src/kernel/arch/i386/driver/serial.h deleted file mode 100644 index 6a4876e..0000000 --- a/src/kernel/arch/i386/driver/serial.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include -#include -#include - -void serial_preinit(void); -void serial_irq(void); -void serial_write(const char *buf, size_t len); - -void serial_init(void); diff --git a/src/kernel/arch/i386/gdt.h b/src/kernel/arch/i386/gdt.h deleted file mode 100644 index 5e40cd7..0000000 --- a/src/kernel/arch/i386/gdt.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -enum { - SEG_null, - // order dictated by SYSENTER - SEG_r0code, - SEG_r0data, - SEG_r3code, - SEG_r3data, - SEG_TSS, - - SEG_end -}; - -void gdt_init(void); -void gdt_farjump(int segment); diff --git a/src/kernel/arch/i386/gdt/farjump.s b/src/kernel/arch/i386/gdt/farjump.s deleted file mode 100644 index 2885d2b..0000000 --- a/src/kernel/arch/i386/gdt/farjump.s +++ /dev/null @@ -1,8 +0,0 @@ -.section .text -.global gdt_farjump -.type gdt_farjump, @function -gdt_farjump: - /* retf pops off the return address and code segment off the stack. - * it turns out that in the i386 cdecl calling convention they're in - * the correct place already. */ - retf diff --git a/src/kernel/arch/i386/gdt/gdt.c b/src/kernel/arch/i386/gdt/gdt.c deleted file mode 100644 index 3521052..0000000 --- a/src/kernel/arch/i386/gdt/gdt.c +++ /dev/null @@ -1,130 +0,0 @@ -#include -#include -#include -#include -#include - -extern char _isr_mini_stack; - -struct gdt_entry { - uint64_t limit_low : 16; - uint64_t base_low : 24; - uint64_t accessed : 1; // set by the processor - // CODE | DATA - uint64_t rw : 1; // readable? | writeable? - uint64_t conforming : 1; // conforming? | expands down? - uint64_t code : 1; // 1 | 0 - - uint64_t codeordata : 1; // 1 for everything other than TSS and LDT - uint64_t ring : 2; - uint64_t present : 1; // always 1 - uint64_t limit_high : 4; - uint64_t available : 1; // ??? - uint64_t long_mode : 1; - uint64_t x32 : 1; - uint64_t gran : 1; // 1 - 4kb, 0 - 1b - uint64_t base_high : 8; -} __attribute__((packed)); - -struct tss_entry { - uint32_t _unused0; - uint32_t esp0; // kernel mode stack pointer - uint32_t ss0; // kernel mode stack segment - uint8_t _unused1[0x5c]; -} __attribute__((packed)); - -struct lgdt_arg { - uint16_t limit; - uint32_t base; -} __attribute__((packed)); - -__attribute__((section(".shared"))) -static struct gdt_entry GDT[SEG_end]; -__attribute__((section(".shared"))) -static struct tss_entry TSS; -static struct lgdt_arg lgdt_arg; // probably doesn't need to be global - -static void gdt_fillout(struct gdt_entry* entry, uint8_t ring, bool code); -static void gdt_prepare(void); -static void gdt_load(void); - - -static void gdt_fillout(struct gdt_entry* entry, uint8_t ring, bool code) { - *entry = (struct gdt_entry) { - // set up the identity mapping - .limit_low = 0xFFFF, - .limit_high = 0xF, - .gran = 1, // 4KB * 0xFFFFF = (almost) 4GB - .base_low = 0, - .base_high = 0, - - .ring = ring, - .code = code, - - .accessed = 0, - .rw = 1, - .conforming = 0, - .codeordata = 1, - .present = 1, - .long_mode = 0, // ??? - .available = 1, // ??? - .x32 = 1, - }; -} - -static void gdt_prepare(void) { - GDT[SEG_null].present = 0; - - gdt_fillout(&GDT[SEG_r0code], 0, true); - gdt_fillout(&GDT[SEG_r0data], 0, false); - gdt_fillout(&GDT[SEG_r3code], 3, true); - gdt_fillout(&GDT[SEG_r3data], 3, false); - - // tss - memset(&TSS, 0, sizeof(TSS)); - TSS.ss0 = SEG_r0data << 3; // kernel data segment - TSS.esp0 = (uintptr_t) &_isr_mini_stack; - - GDT[SEG_TSS] = (struct gdt_entry) { - .limit_low = sizeof(TSS), - .limit_high = sizeof(TSS) >> 16, - .gran = 0, - .base_low = (uintptr_t) &TSS, - .base_high = ((uintptr_t) &TSS) >> 24, - - .accessed = 1, // 1 for TSS - .rw = 0, // 1 busy / 0 not busy - .conforming = 0, // 0 for TSS - .code = 1, // 32bit - .codeordata = 0, // is a system entry - .ring = 3, - .present = 1, - .available = 0, // 0 for TSS - .long_mode = 0, - .x32 = 0, // idk - }; -} - -static void gdt_load(void) { - lgdt_arg.limit = sizeof(GDT) - 1; - lgdt_arg.base = (uintptr_t) &GDT; - asm("lgdt (%0)" - : : "r" (&lgdt_arg) : "memory"); - - asm("ltr %%ax" - : : "a" (SEG_TSS << 3 | 3) : "memory"); - - // update all segment registers - gdt_farjump(SEG_r0code << 3); - asm("mov %0, %%ds;" - "mov %0, %%ss;" - "mov %0, %%es;" - "mov %0, %%fs;" - "mov %0, %%gs;" - : : "r" (SEG_r0data << 3) : "memory"); -} - -void gdt_init(void) { - gdt_prepare(); - gdt_load(); -} diff --git a/src/kernel/arch/i386/interrupts/idt.c b/src/kernel/arch/i386/interrupts/idt.c deleted file mode 100644 index cd2adfd..0000000 --- a/src/kernel/arch/i386/interrupts/idt.c +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include -#include -#include -#include -#include - -struct idt_entry { - uint16_t offset_low ; - uint16_t code_seg ; - uint8_t zero ; // unused, has to be 0 - uint8_t type : 4; // 16/32 bit, task/interrupt/task gate - uint8_t storage : 1; // 0 for interrupt/trap gates - uint8_t ring : 2; - uint8_t present : 1; - uint16_t offset_high ; -} __attribute__((packed)); - -// is exactly the same as lgdt_arg, i should combine them into a single struct -// later -struct lidt_arg { - uint16_t limit; - uint32_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) { - uintptr_t offset; - for (int i = 0; i < 256; i++) { - offset = (uintptr_t) &_isr_stubs + i * 8; - - IDT[i] = (struct idt_entry) { - .offset_low = offset, - .offset_high = offset >> 16, - .code_seg = SEG_r0code << 3, - .zero = 0, - .present = 1, - .ring = 0, - .storage = 0, - .type = 0xE, // 32-bit interrupt gate - }; - } -} - -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) { - asm("int $0x34" : : : "memory"); - assert(isr_test_interrupt_called); -} - -void idt_init(void) { - idt_prepare(); - idt_load(); - idt_test(); -} diff --git a/src/kernel/arch/i386/interrupts/idt.h b/src/kernel/arch/i386/interrupts/idt.h deleted file mode 100644 index 6576cf9..0000000 --- a/src/kernel/arch/i386/interrupts/idt.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void idt_init(void); diff --git a/src/kernel/arch/i386/interrupts/irq.c b/src/kernel/arch/i386/interrupts/irq.c deleted file mode 100644 index 5918169..0000000 --- a/src/kernel/arch/i386/interrupts/irq.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include - -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/i386/interrupts/irq.h b/src/kernel/arch/i386/interrupts/irq.h deleted file mode 100644 index f523154..0000000 --- a/src/kernel/arch/i386/interrupts/irq.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include -#include - -void irq_init(void); -void irq_eoi(uint8_t line); diff --git a/src/kernel/arch/i386/interrupts/isr.c b/src/kernel/arch/i386/interrupts/isr.c deleted file mode 100644 index 2d893cb..0000000 --- a/src/kernel/arch/i386/interrupts/isr.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -bool isr_test_interrupt_called = false; - -void isr_stage3(int interrupt) { - switch (interrupt) { - case 0x08: // double fault - kprintf("#DF"); - panic_invalid_state(); - case 0x34: - 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/i386/interrupts/isr.h b/src/kernel/arch/i386/interrupts/isr.h deleted file mode 100644 index 3189538..0000000 --- a/src/kernel/arch/i386/interrupts/isr.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include - -extern bool isr_test_interrupt_called; // used in the self-test in idt.c -extern const char _isr_stubs; - -void isr_stage3(int interrupt); diff --git a/src/kernel/arch/i386/interrupts/isr_stub.s b/src/kernel/arch/i386/interrupts/isr_stub.s deleted file mode 100644 index fdbae6f..0000000 --- a/src/kernel/arch/i386/interrupts/isr_stub.s +++ /dev/null @@ -1,64 +0,0 @@ -.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 %eax - push %ecx - push %edx - push %ebx - push %ebp - push %esi - push %edi - - // convert the return address into the vector nr - mov 28(%esp), %eax - add $-_isr_stubs, %eax - shr $3, %eax - - // disable paging, if present - mov %cr0, %ebx - push %ebx // push original cr0 - and $0x7FFFFFFF, %ebx - mov %ebx, %cr0 - - mov %esp, %ebp - mov $_isr_big_stack, %esp - push %eax // push the vector nr - call isr_stage3 - - mov %ebp, %esp - pop %eax // restore old cr0 - mov %eax, %cr0 - - // restore registers - pop %edi - pop %esi - pop %ebp - pop %ebx - pop %edx - pop %ecx - pop %eax - - add $4, %esp // skip call's return address - iret - -.align 8 -// TODO overflow check -.skip 64 // seems to be enough -.global _isr_mini_stack -_isr_mini_stack: diff --git a/src/kernel/arch/i386/multiboot.h b/src/kernel/arch/i386/multiboot.h deleted file mode 100644 index 1d6718f..0000000 --- a/src/kernel/arch/i386/multiboot.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include - -#ifndef __CHECKER__ -_Static_assert(sizeof(void*) == 4, - "this code assumes that pointers have 4 bytes"); -#endif - -struct multiboot_mod { - void *start; - void *end; - const char *str; - uint32_t _reserved; -} __attribute__((packed)); - -struct multiboot_info { - uint32_t flag_mem : 1; - uint32_t flag_boot_device : 1; - uint32_t flag_cmdline : 1; - uint32_t flag_mods : 1; - uint32_t _flag_other : 28; // unimplemented - - uint32_t mem_lower; - uint32_t mem_upper; - - uint32_t boot_device; - - const char *cmdline; - - uint32_t mods_count; - struct multiboot_mod *mods; - - // [...] -} __attribute__((packed)); diff --git a/src/kernel/arch/i386/multiboot.s b/src/kernel/arch/i386/multiboot.s deleted file mode 100644 index dc19b36..0000000 --- a/src/kernel/arch/i386/multiboot.s +++ /dev/null @@ -1,12 +0,0 @@ -.set MAGIC, 0x1BADB002 - -/* 1<<0 - align modules on page boundaries. */ -.set FLAGS, 1<<0 -.set CHECKSUM, -(MAGIC + FLAGS) - -.section .multiboot -.align 4 -multiboot_header: - .long MAGIC - .long FLAGS - .long CHECKSUM diff --git a/src/kernel/arch/i386/pagedir.c b/src/kernel/arch/i386/pagedir.c deleted file mode 100644 index e607cc7..0000000 --- a/src/kernel/arch/i386/pagedir.c +++ /dev/null @@ -1,206 +0,0 @@ -#include -#include -#include -#include - -/* nitpick: I highly recommend you dont use bitfields for paging - * structures - * you can't change them atomically and the way they're layed out - * in memory is implementation defined iirc - */ -struct pagetable_entry { - uint32_t present : 1; - uint32_t writeable : 1; - uint32_t user : 1; - uint32_t writethru : 1; - uint32_t uncached : 1; - uint32_t dirty : 1; - uint32_t always0 : 1; // memory type thing? - uint32_t global : 1; - uint32_t _unused : 3; - uint32_t address : 21; -}; - -struct pagedir_entry { - uint32_t present : 1; - uint32_t _writeable: 1; // don't use! not checked by multiple functions here - uint32_t _user : 1; // ^ - uint32_t writethru : 1; - uint32_t uncached : 1; - uint32_t accessed : 1; - uint32_t always0 : 1; - uint32_t large : 1; // 4 MiB instead of 4 KiB - uint32_t _unused : 3; - uint32_t address : 21; -} __attribute__((packed)); - -struct pagedir { - struct pagedir_entry e[1024]; -} __attribute__((packed)); - - -struct pagedir *pagedir_new(void) { - struct pagedir *dir = page_alloc(1); - for (int i = 0; i < 1024; i++) - dir->e[i].present = 0; - return dir; -} - -void pagedir_free(struct pagedir *dir) { - // assumes all user pages are unique and can be freed - struct pagetable_entry *pt; - void *page; - - for (int i = 0; i < 1024; i++) { - if (!dir->e[i].present) continue; - - pt = (void*)(dir->e[i].address << 11); - - for (int j = 0; j < 1024; j++) { - if (!pt[j].present) continue; - if (!pt[j].user) continue; - - page = (void*)(pt[j].address << 11); - page_free(page, 1); - } - page_free(pt, 1); - } - page_free(dir, 1); -} - -static struct pagetable_entry* -get_entry(struct pagedir *dir, const void __user *virt) { - uint32_t pd_idx = ((uintptr_t)virt) >> 22; - uint32_t pt_idx = ((uintptr_t)virt) >> 12 & 0x03FF; - struct pagetable_entry *pagetable; - - if (!dir->e[pd_idx].present) return NULL; - - pagetable = (void*)(dir->e[pd_idx].address << 11); - return &pagetable[pt_idx]; -} - -void *pagedir_unmap(struct pagedir *dir, void __user *virt) { - void *phys = pagedir_virt2phys(dir, virt, false, false); - struct pagetable_entry *page = get_entry(dir, virt); - page->present = false; - return phys; -} - -void pagedir_map(struct pagedir *dir, void __user *virt, void *phys, - bool user, bool writeable) -{ - uintptr_t virt_cast = (uintptr_t) virt; - uint32_t pd_idx = virt_cast >> 22; - uint32_t pt_idx = virt_cast >> 12 & 0x03FF; - struct pagetable_entry *pagetable; - - if (dir->e[pd_idx].present) { - pagetable = (void*) (dir->e[pd_idx].address << 11); - } else { - pagetable = page_alloc(1); - for (int i = 0; i < 1024; i++) - pagetable[i].present = 0; - - dir->e[pd_idx] = (struct pagedir_entry) { - .present = 1, - ._writeable= 1, - ._user = 1, - .writethru = 1, - .uncached = 0, - .accessed = 0, - .always0 = 0, - .large = 0, - ._unused = 0, - .address = (uintptr_t) pagetable >> 11 - }; - } - - pagetable[pt_idx] = (struct pagetable_entry) { - .present = 1, - .writeable = writeable, - .user = user, - .writethru = 1, - .uncached = 0, - .dirty = 0, - .always0 = 0, - .global = 0, - ._unused = 0, - .address = (uintptr_t) phys >> 11 - }; -} - -void pagedir_switch(struct pagedir *dir) { - asm volatile("mov %0, %%cr3;" : : "r" (dir) : "memory"); -} - -// creates a new pagedir with exact copies of the user pages -struct pagedir *pagedir_copy(const struct pagedir *orig) { - struct pagedir *clone = page_alloc(1); - struct pagetable_entry *orig_pt, *clone_pt; - void *orig_page, *clone_page; - - for (int i = 0; i < 1024; i++) { - clone->e[i] = orig->e[i]; - if (!orig->e[i].present) continue; - - orig_pt = (void*)(orig->e[i].address << 11); - clone_pt = page_alloc(1); - clone->e[i].address = (uintptr_t) clone_pt >> 11; - - for (int j = 0; j < 1024; j++) { - clone_pt[j] = orig_pt[j]; - if (!orig_pt[j].present) continue; - if (!orig_pt[j].user) continue; - // i could use .global? - - orig_page = (void*)(orig_pt[j].address << 11); - clone_page = page_alloc(1); - clone_pt[j].address = (uintptr_t) clone_page >> 11; - - memcpy(clone_page, orig_page, PAGE_SIZE); - } - } - - return clone; -} - -bool pagedir_iskern(struct pagedir *dir, const void __user *virt) { - struct pagetable_entry *page = get_entry(dir, virt); - return page && page->present && !page->user; -} - -void *pagedir_virt2phys(struct pagedir *dir, const void __user *virt, - bool user, bool writeable) -{ - struct pagetable_entry *page; - uintptr_t phys; - page = get_entry(dir, virt); - if (!page || !page->present) return NULL; - if (user && !page->user) return NULL; - if (writeable && !page->writeable) return NULL; - - phys = page->address << 11; - phys |= ((uintptr_t)virt) & 0xFFF; - return (void*)phys; -} - -void __user *pagedir_findfree(struct pagedir *dir, char __user *start, size_t len) { - struct pagetable_entry *page; - char __user *iter; - start = (userptr_t)(((uintptr_t __force)start + PAGE_MASK) & ~PAGE_MASK); // round up to next page - iter = start; - - while (iter < (char __user *)0xFFF00000) { // TODO better boundary - page = get_entry(dir, iter); - if (page && page->present) { - start = iter + PAGE_SIZE; - } else { - if ((size_t)(iter + PAGE_SIZE - start) >= len) - return start; - } - iter += PAGE_SIZE; - } - - return NULL; -} diff --git a/src/kernel/arch/i386/port_io.h b/src/kernel/arch/i386/port_io.h deleted file mode 100644 index eac9331..0000000 --- a/src/kernel/arch/i386/port_io.h +++ /dev/null @@ -1,22 +0,0 @@ -#include - -static inline void port_out8(uint16_t port, uint8_t val) { - asm volatile("outb %0, %1" : : "a" (val), "Nd" (port)); -} - -static inline void port_out16(uint16_t port, uint16_t val) { - asm volatile("outw %0, %1" : : "a" (val), "Nd" (port)); -} - -static inline uint8_t port_in8(uint16_t port) { - uint8_t val; - asm volatile("inb %1, %0" : "=a" (val) : "Nd" (port)); - return val; -} - -static inline uint16_t port_in16(uint16_t port) { - uint16_t val; - asm volatile("inw %1, %0" : "=a" (val) : "Nd" (port)); - return val; -} - diff --git a/src/kernel/arch/i386/registers.h b/src/kernel/arch/i386/registers.h deleted file mode 100644 index 9f481d3..0000000 --- a/src/kernel/arch/i386/registers.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include - -struct registers { - /* those are in the order of pushad/popad - so you can load/save this - * struct in (almost) one instruction */ - uint32_t edi, esi; - userptr_t ebp, esp; - uint32_t ebx, edx, ecx, eax; - - // not part of pushad/popad, but still useful - userptr_t eip; -} __attribute__((__packed__)); - -// saves a return value according to the SysV ABI -static inline uint64_t regs_savereturn(struct registers *regs, uint64_t value) { - regs->eax = value; - regs->edx = value >> 32; - return value; -} diff --git a/src/kernel/arch/i386/sysenter.c b/src/kernel/arch/i386/sysenter.c deleted file mode 100644 index 5837474..0000000 --- a/src/kernel/arch/i386/sysenter.c +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include -#include -#include - -struct registers _sysexit_regs; - -void sysexit(struct registers regs) { - _sysexit_regs = regs; - _sysexit_regs.ecx = (uintptr_t) regs.esp; - _sysexit_regs.edx = (uintptr_t) regs.eip; - _sysexit_real(); - __builtin_unreachable(); -} - -_Noreturn void sysenter_stage2(void) { - struct registers *regs = &process_current->regs; - - *regs = _sysexit_regs; // save the registers - regs->esp = (userptr_t) regs->ecx; // fix them up - regs->eip = (userptr_t) regs->edx; - - _syscall(regs->eax, regs->ebx, - regs->esi, regs->edi, (uintptr_t)regs->ebp); - process_switch_any(); -} diff --git a/src/kernel/arch/i386/sysenter.h b/src/kernel/arch/i386/sysenter.h deleted file mode 100644 index b88c186..0000000 --- a/src/kernel/arch/i386/sysenter.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -// sysenter.c -extern struct registers _sysexit_regs; -_Noreturn void sysenter_stage2(void); - -// sysenter.s -void _sysexit_real(void); diff --git a/src/kernel/arch/i386/sysenter.s b/src/kernel/arch/i386/sysenter.s deleted file mode 100644 index d04f839..0000000 --- a/src/kernel/arch/i386/sysenter.s +++ /dev/null @@ -1,89 +0,0 @@ -/* arch/i386/gdt.c */ -.set SEG_r0code, 1 -.set SEG_r3code, 3 -.set SEG_r3data, 4 - -.set IA32_SYSENTER_CS, 0x174 -.set IA32_SYSENTER_ESP, 0x175 -.set IA32_SYSENTER_EIP, 0x176 - -.section .text -.global sysenter_setup -.type sysenter_setup, @function -sysenter_setup: - xor %edx, %edx - - mov $(SEG_r0code << 3), %eax - mov $IA32_SYSENTER_CS, %ecx - wrmsr - - mov $IA32_SYSENTER_ESP, %ecx - mov $0, %eax // unused - wrmsr - - mov $IA32_SYSENTER_EIP, %ecx - mov $sysenter_stage1, %eax - wrmsr - - ret - - -.section .shared - -.global stored_eax -stored_eax: -.long 0 - -.global _sysexit_real -.type _sysexit_real, @function -_sysexit_real: - mov $(SEG_r3data << 3 | 3), %ax - mov %ax, %ds - mov %ax, %es - mov %ax, %fs - mov %ax, %gs - - // restore the registers - mov $_sysexit_regs, %esp - pop %edi - pop %esi - pop %ebp - add $4, %esp - pop %ebx - pop %edx - pop %ecx - pop %eax - - // enable paging - mov %eax, stored_eax - mov %cr0, %eax - or $0x80000000, %eax - mov %eax, %cr0 - mov stored_eax, %eax - - sysexit - -sysenter_stage1: - cli /* prevent random IRQs in the middle of kernel code */ - - // disable paging - // I don't want to damage any of the registers passed in by the user, - // so i'm using ESP as a temporary register. At this point there's nothing - // useful in it, it's == _bss_end. - mov %cr0, %esp - and $0x7FFFFFFF, %esp // disable paging - mov %esp, %cr0 - - // save the registers - mov $(_sysexit_regs + 32), %esp - push %eax - push %ecx - push %edx - push %ebx - push $0x0 // pushal pushes %esp here, but that's worthless - push %ebp - push %esi - push %edi - - mov $_bss_end, %esp - jmp sysenter_stage2 diff --git a/src/kernel/arch/i386/tty/tty.c b/src/kernel/arch/i386/tty/tty.c deleted file mode 100644 index 9451059..0000000 --- a/src/kernel/arch/i386/tty/tty.c +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include -#include -#include - -void tty_init(void) { - vga_clear(); - serial_preinit(); - - vga_write("\x03 ", 2); // cp437 heart - serial_write("<3 ", 3); -} - -static void backend(void __attribute__((unused)) *arg, const char *buf, size_t len) { - vga_write(buf, len); - serial_write(buf, len); -} - -int kprintf(const char *fmt, ...) { - int ret; - va_list argp; - va_start(argp, fmt); - ret = __printf_internal(fmt, argp, backend, NULL); - va_end(argp); - return ret; -} diff --git a/src/kernel/arch/i386/tty/tty.h b/src/kernel/arch/i386/tty/tty.h deleted file mode 100644 index b96003d..0000000 --- a/src/kernel/arch/i386/tty/tty.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include - -void vga_write(const char *buf, size_t len); -void vga_clear(void); - -void tty_init(void); diff --git a/src/kernel/arch/i386/tty/vga.c b/src/kernel/arch/i386/tty/vga.c deleted file mode 100644 index 9efd326..0000000 --- a/src/kernel/arch/i386/tty/vga.c +++ /dev/null @@ -1,33 +0,0 @@ -#include - -struct vga_cell { - unsigned char c; - unsigned char style; -} __attribute__((__packed__)); - -static const size_t vga_len = 80 * 25; -static struct vga_cell *vga = (void*) 0xB8000; -static size_t vga_pos = 0; - -static void vga_scroll(void) { - for (size_t i = 0; i < vga_len - 80; i++) - vga[i] = vga[i + 80]; - vga_pos -= 80; -} - -static void vga_putchar(char c) { - if (vga_pos >= vga_len - 80) - vga_scroll(); - vga[vga_pos++].c = c; -} - -void vga_write(const char *buf, size_t len) { - for (size_t i = 0; i < len; i++) - vga_putchar(buf[i]); -} - -void vga_clear(void) { - for (size_t i = 0; i < vga_len; i++) - vga[i].c = ' '; - vga_pos = 0; -} diff --git a/src/kernel/panic.h b/src/kernel/panic.h index 8cb32f4..4e591f1 100644 --- a/src/kernel/panic.h +++ b/src/kernel/panic.h @@ -7,7 +7,7 @@ kprintf(__func__); \ kprintf(" (" __FILE__ ":" NUM2STR(__LINE__) ")\n"); \ debug_stacktrace(); \ - halt_cpu(); \ + cpu_halt(); \ } while (0) #define panic_invalid_state() _panic("invalid state") diff --git a/src/kernel/proc.c b/src/kernel/proc.c index 1cb6eb0..616bd79 100644 --- a/src/kernel/proc.c +++ b/src/kernel/proc.c @@ -196,6 +196,7 @@ void process_forget(struct process *p) { } static _Noreturn void process_switch(struct process *proc) { + kprintf("process_switch\n"); assert(proc->state == PS_RUNNING); process_current = proc; pagedir_switch(proc->pages); diff --git a/src/kernel/vfs/request.c b/src/kernel/vfs/request.c index 7c677cc..97c71a7 100644 --- a/src/kernel/vfs/request.c +++ b/src/kernel/vfs/request.c @@ -1,4 +1,4 @@ -#include +#include #include #include #include -- cgit v1.2.3