From 633f5364041cd4aec7fe486dc3524c0f96006ef6 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Fri, 17 Jan 2025 17:08:49 +1000 Subject: [PATCH 01/22] Implement usermode (WIP) --- kernel/CMakeLists.txt | 1 + kernel/src/arch/x86_64/usermode/usermode.c | 86 +++++++++++++++++ kernel/src/arch/x86_64/usermode/usermode.h | 103 +++++++++++++++++++++ kernel/src/kernel_main.c | 15 ++- 4 files changed, 200 insertions(+), 5 deletions(-) create mode 100644 kernel/src/arch/x86_64/usermode/usermode.c create mode 100644 kernel/src/arch/x86_64/usermode/usermode.h diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index d3f2c0e..4b03a54 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable(kernel src/arch/x86_64/klib/klib.c src/arch/x86_64/klib/malloc.c src/arch/x86_64/serial/serial.c + src/arch/x86_64/usermode/usermode.c src/arch/x86_64/vga/vga.c src/kernel_main.c src/io.c diff --git a/kernel/src/arch/x86_64/usermode/usermode.c b/kernel/src/arch/x86_64/usermode/usermode.c new file mode 100644 index 0000000..947207f --- /dev/null +++ b/kernel/src/arch/x86_64/usermode/usermode.c @@ -0,0 +1,86 @@ +#include "usermode.h" + +static struct gdt gdt = {0}; +static struct tss_entry tss = {0}; + +static uint8_t ist1_stack[4096]; +static uint8_t ist2_stack[4096]; +static uint8_t rsp0_stack[4096]; + +void enter_usermode(void *function_address) { + struct gdt_entry *ring3_code = &gdt.code; + struct gdt_entry *ring3_data = &gdt.data; + + // ! EVERYTHING NOT SET IS ZERO ! + ring3_code->limit_low = 0xFFFF; + + ring3_code->read_write = 1; + ring3_code->code = 1; + ring3_code->code_data_segment = 1; + // ring 3 + ring3_code->DPL = 3; + ring3_code->present = 1; + ring3_code->limit_high = 0xF; + ring3_code->available = 1; + ring3_code->long_mode = 1; + ring3_code->big = 1; + ring3_code->gran = 1; + + // data is basically the same besides the code bit + *ring3_data = *ring3_code; + ring3_data->code = 0; + + uint64_t base = (uint64_t) &tss; + uint32_t limit = sizeof tss; + + // tss descriptor + struct gdt_system_entry *ring3_tss = &gdt.tss; + ring3_tss->limit_low = limit; + ring3_tss->base_low = base & 0xFFFFFF; + ring3_tss->accessed = 1; + + // code bit indicates whether its 32 bit or 16 bit + ring3_tss->code = 1; + // tss descriptor is ring 0 + // tss available flag = 0 + ring3_tss->base_high = (base >> 27) & 0xFF; + ring3_tss->limit_low = limit & 0xFFFFFFF; + ring3_tss->limit_high = (limit >> 27) & 0xf; + + tss.ist1 = (uint64_t) &ist1_stack[4095];// this one is for traps + tss.ist2 = (uint64_t) &ist2_stack[4095];// for double faults + tss.rsp0 = (uint64_t) &rsp0_stack[4095];// for kernel stack + + struct { + uint16_t limit; + uint64_t base; + } __attribute__((packed)) gdt_descriptor = { 0 }; + + gdt_descriptor.limit = sizeof(gdt) - 1; + gdt_descriptor.base = (uint64_t)&gdt; + + // ring 3 time + asm volatile( + "lgdt %0\n" + + // not too sure if this is allowed for x86_64 + // "mov ax, $0x23\n" + // "mov ds, ax\n" + // "mov es, ax\n" + // "mov fs, ax\n" + // "mov gs, ax\n" + + "mov %%rax, %%rsp\n"// get the stack + "push $0x23\n" // 0x23 | 3 (data selector) + "push %%rax\n" // stack address + // pushf is sufficient but just in case + "push $0x202\n" // rflag (interrupt enable flag on) + "push $0x1B\n"// 0x18 | 3 (code selector) + "push %1\n" // function + "iretq\n" ::"m"(gdt), "m"(function_address) ); +} + +// osdev said I needed this +void tss_set_kernel_stack(uint64_t stack) { + tss.rsp0 = stack; +} \ No newline at end of file diff --git a/kernel/src/arch/x86_64/usermode/usermode.h b/kernel/src/arch/x86_64/usermode/usermode.h new file mode 100644 index 0000000..3382bb5 --- /dev/null +++ b/kernel/src/arch/x86_64/usermode/usermode.h @@ -0,0 +1,103 @@ +#pragma once + +#include + +// Shamelessly stolen from: https://wiki.osdev.org/Getting_to_Ring_3 +struct gdt_entry { + // segment limit (limit of the segments size) + uint64_t limit_low : 16; + + // base address = virtual address to start of segment + uint64_t base_low : 24; + + uint64_t accessed : 1; + // code is readable + // data is writable + uint64_t read_write : 1; + + // conforming for code, expand down for data + uint64_t conforming_expand_down : 1; + // 1 for code, 0 for data + uint64_t code : 1; + + // should be 1 for everything but TSS and LDT + uint64_t code_data_segment : 1; + + // privilege level + uint64_t DPL : 2; + // indicates that segment referenced by the descriptor is loaded in memory + uint64_t present : 1; + + uint64_t limit_high : 4; + + // only used in software; has no effect on hardware + uint64_t available : 1; + // self explaintory (it enables long mode!!!) + uint64_t long_mode : 1; + // 32-bit opcodes for code, uint32_t stack for data + uint64_t big : 1; + // 1 to use 4k page addressing, 0 for byte addressing + uint64_t gran : 1; + + uint64_t base_high : 8; + +} __attribute__((packed)); + +struct gdt_system_entry { + uint64_t limit_low : 16; + uint64_t base_low : 24; + + // types + uint64_t accessed : 1; + uint64_t read_write : 1; + uint64_t conforming_expand_down : 1; + uint64_t code : 1; + + // always 0 + uint64_t code_data_segment : 1; + + uint64_t DPL : 2; + uint64_t present : 1; + uint64_t limit_high : 4; + uint64_t available : 1; + uint64_t unused : 2; + uint64_t gran : 1; + + uint64_t base_mid : 8; + + // * end of first 64 bits + + uint64_t base_high : 32; + uint64_t reserved_low : 8; + uint64_t must_be_zero : 5; + uint64_t reerved_high : 19; +} __attribute__((packed)); + +// Refer to page 377 (Figure 12-8) in AMD manual +struct tss_entry { + uint32_t reserved_low; + uint64_t rsp0; + uint64_t rsp1; + uint64_t rsp2; + uint64_t reserved_mid; + uint64_t ist1; + uint64_t ist2; + uint64_t ist3; + uint64_t ist4; + uint64_t ist5; + uint64_t ist6; + uint64_t ist7; + uint64_t reserved_high; + uint16_t reserved_higher; + uint16_t io_base_address; +} __attribute__((packed)); + +struct gdt { + struct gdt_entry null; + struct gdt_entry code; + struct gdt_entry data; + struct gdt_system_entry tss; +} __attribute__((packed)); + +void enter_usermode(void *function_address); +void tss_set_kernel_stack(uint64_t stack); \ No newline at end of file diff --git a/kernel/src/kernel_main.c b/kernel/src/kernel_main.c index 852db97..defd0df 100644 --- a/kernel/src/kernel_main.c +++ b/kernel/src/kernel_main.c @@ -13,6 +13,7 @@ #include "arch/x86_64/klib/klib.h" #include "arch/x86_64/memory/paging.h" #include "arch/x86_64/serial/serial.h" +#include "arch/x86_64/usermode/usermode.h" #include "arch/x86_64/vga/vga.h" #include "io.h" @@ -29,6 +30,10 @@ void exception_handler(void) { _die(); } +void _Noreturn user_main(void) { + while (1) puts("Hello, world!\n"); +} + /* * The entry point after the bootloader finishes setting up x86 32-bit protected * mode. @@ -86,7 +91,6 @@ void _Noreturn kernel_main(struct boot_parameters parameters) { puts("Successfully loaded idt\n"); char message[] = "X Hello world!\n"; - unsigned int i = 0; uint64_t test_virtual_address = (KERNEL_MAPPING_ADDRESS | 0xFFFFFFF); map_page(test_virtual_address, 0xFFFFFFF, PAGE_PRESENT); @@ -102,7 +106,7 @@ void _Noreturn kernel_main(struct boot_parameters parameters) { puts("Successfully freed virtual address!\n"); - while (1) { + for (size_t i = 0; i < 2; i++) { message[0] = '0' + i; i = (i + 1) % 10; @@ -112,13 +116,14 @@ void _Noreturn kernel_main(struct boot_parameters parameters) { get_timer_ticks(), parameters.address_range_count); vga_set_color(1 + (i % 6), VGA_COLOR_BLACK); - memmap_print_entries(parameters.address_range_count, - parameters.address_ranges); - + //memmap_print_entries(parameters.address_range_count, + // parameters.address_ranges); +// asm volatile("int %0" : : "i"(0x80) : "memory"); ksleep(276447232); } + enter_usermode((void *) user_main); } static void read_acpi(void) { From c0720f1c34ff3a1a5a263a5a415d0877988ba53f Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Fri, 17 Jan 2025 18:01:23 +1000 Subject: [PATCH 02/22] Fix gdt (WIP) --- CMakeLists.txt | 2 +- kernel/src/arch/x86_64/usermode/usermode.c | 80 +++++++++++++--------- kernel/src/arch/x86_64/usermode/usermode.h | 50 +++++++------- kernel/src/kernel_main.c | 4 +- 4 files changed, 77 insertions(+), 59 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1487383..42a0756 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,4 +21,4 @@ add_custom_command( add_custom_target(os ALL DEPENDS "os.img") add_dependencies(os bootloader kernel) -add_custom_target(run COMMAND qemu-system-x86_64 -cpu qemu64 -drive file=os.img,format=raw -serial stdio DEPENDS os) +add_custom_target(run COMMAND qemu-system-x86_64 -cpu qemu64 -drive file=os.img,format=raw -serial stdio -no-reboot -d int -D qemu.log DEPENDS os) diff --git a/kernel/src/arch/x86_64/usermode/usermode.c b/kernel/src/arch/x86_64/usermode/usermode.c index 947207f..b6d141f 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.c +++ b/kernel/src/arch/x86_64/usermode/usermode.c @@ -1,15 +1,25 @@ #include "usermode.h" +#include "../../../io.h" + static struct gdt gdt = {0}; + +struct { + uint16_t limit; + uint64_t base; +} __attribute__((packed)) gdt_descriptor = { + .limit = sizeof(gdt) - 1, + .base = (uint64_t) &gdt}; + static struct tss_entry tss = {0}; -static uint8_t ist1_stack[4096]; -static uint8_t ist2_stack[4096]; -static uint8_t rsp0_stack[4096]; +static uint8_t ist1_stack[4096] __attribute__((aligned(16))); +static uint8_t ist2_stack[4096] __attribute__((aligned(16))); +static uint8_t rsp0_stack[4096] __attribute__((aligned(16))); void enter_usermode(void *function_address) { - struct gdt_entry *ring3_code = &gdt.code; - struct gdt_entry *ring3_data = &gdt.data; + struct gdt_entry *ring3_code = &gdt.user_code; + struct gdt_entry *ring3_data = &gdt.user_data; // ! EVERYTHING NOT SET IS ZERO ! ring3_code->limit_low = 0xFFFF; @@ -29,55 +39,61 @@ void enter_usermode(void *function_address) { // data is basically the same besides the code bit *ring3_data = *ring3_code; ring3_data->code = 0; + ring3_data->long_mode = 0; uint64_t base = (uint64_t) &tss; - uint32_t limit = sizeof tss; + uint32_t limit = sizeof(tss) - 1; // tss descriptor struct gdt_system_entry *ring3_tss = &gdt.tss; - ring3_tss->limit_low = limit; ring3_tss->base_low = base & 0xFFFFFF; + ring3_tss->limit_low = limit & 0xFFFFFF; + ring3_tss->accessed = 1; + ring3_tss->present = 1; // code bit indicates whether its 32 bit or 16 bit ring3_tss->code = 1; + // tss descriptor is ring 0 // tss available flag = 0 + ring3_tss->base_high = (base >> 27) & 0xFF; - ring3_tss->limit_low = limit & 0xFFFFFFF; - ring3_tss->limit_high = (limit >> 27) & 0xf; + ring3_tss->limit_high = (limit >> 27) & 0xF; tss.ist1 = (uint64_t) &ist1_stack[4095];// this one is for traps tss.ist2 = (uint64_t) &ist2_stack[4095];// for double faults tss.rsp0 = (uint64_t) &rsp0_stack[4095];// for kernel stack - struct { - uint16_t limit; - uint64_t base; - } __attribute__((packed)) gdt_descriptor = { 0 }; + // ring 3 time + // triple faults here sometimes? + asm volatile("lgdt %0\n" ::"m"(gdt_descriptor)); - gdt_descriptor.limit = sizeof(gdt) - 1; - gdt_descriptor.base = (uint64_t)&gdt; + krintf("GDT LOADED\n"); - // ring 3 time + // triple faults here sometimes too? asm volatile( - "lgdt %0\n" - // not too sure if this is allowed for x86_64 - // "mov ax, $0x23\n" - // "mov ds, ax\n" - // "mov es, ax\n" - // "mov fs, ax\n" - // "mov gs, ax\n" - - "mov %%rax, %%rsp\n"// get the stack - "push $0x23\n" // 0x23 | 3 (data selector) - "push %%rax\n" // stack address - // pushf is sufficient but just in case - "push $0x202\n" // rflag (interrupt enable flag on) - "push $0x1B\n"// 0x18 | 3 (code selector) - "push %1\n" // function - "iretq\n" ::"m"(gdt), "m"(function_address) ); + "mov $0x23, %%ax\n" + "mov %%ax, %%ds\n" + "mov %%ax, %%es\n" + "mov %%ax, %%fs\n" + "mov %%ax, %%gs\n" ::); + + krintf("SETUP SEGMENTS %x\n", (uint64_t) function_address); + + // ! triple faults here (General protection fault) + asm volatile( + "mov %0, %%rax\n"// get the stack + "push $0x23\n" // 0x23 | 3 (user data selector) + "push %%rax\n" // stack address + // pushfq is sufficient but just in case + "push $0x202\n"// rflag (interrupt enable flag on) + "push $0x1B\n" // 0x18 | 3 (user code selector) + "push %1\n" // function + "iretq\n" :: + "r"((uint64_t) &rsp0_stack[4095]), + "m"(function_address) : "rax"); } // osdev said I needed this diff --git a/kernel/src/arch/x86_64/usermode/usermode.h b/kernel/src/arch/x86_64/usermode/usermode.h index 3382bb5..2e7954b 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.h +++ b/kernel/src/arch/x86_64/usermode/usermode.h @@ -8,54 +8,54 @@ struct gdt_entry { uint64_t limit_low : 16; // base address = virtual address to start of segment - uint64_t base_low : 24; + uint64_t base_low : 24; - uint64_t accessed : 1; + uint64_t accessed : 1; // code is readable // data is writable uint64_t read_write : 1; // conforming for code, expand down for data - uint64_t conforming_expand_down : 1; + uint64_t conforming_expand_down : 1; // 1 for code, 0 for data - uint64_t code : 1; + uint64_t code : 1; // should be 1 for everything but TSS and LDT - uint64_t code_data_segment : 1; + uint64_t code_data_segment : 1; // privilege level - uint64_t DPL : 2; - // indicates that segment referenced by the descriptor is loaded in memory - uint64_t present : 1; + uint64_t DPL : 2; + // indicates that segment referenced by the descriptor is loaded in memory + uint64_t present : 1; - uint64_t limit_high : 4; + uint64_t limit_high : 4; // only used in software; has no effect on hardware - uint64_t available : 1; + uint64_t available : 1; // self explaintory (it enables long mode!!!) - uint64_t long_mode : 1; + uint64_t long_mode : 1; // 32-bit opcodes for code, uint32_t stack for data - uint64_t big : 1; + uint64_t big : 1; // 1 to use 4k page addressing, 0 for byte addressing - uint64_t gran : 1; - - uint64_t base_high : 8; + uint64_t gran : 1; + + uint64_t base_high : 8; } __attribute__((packed)); struct gdt_system_entry { uint64_t limit_low : 16; uint64_t base_low : 24; - + // types - uint64_t accessed : 1; + uint64_t accessed : 1; uint64_t read_write : 1; - uint64_t conforming_expand_down : 1; - uint64_t code : 1; + uint64_t conforming_expand_down : 1; + uint64_t code : 1; // always 0 - uint64_t code_data_segment : 1; - + uint64_t code_data_segment : 1; + uint64_t DPL : 2; uint64_t present : 1; uint64_t limit_high : 4; @@ -64,7 +64,7 @@ struct gdt_system_entry { uint64_t gran : 1; uint64_t base_mid : 8; - + // * end of first 64 bits uint64_t base_high : 32; @@ -94,8 +94,10 @@ struct tss_entry { struct gdt { struct gdt_entry null; - struct gdt_entry code; - struct gdt_entry data; + struct gdt_entry kernel_code; + struct gdt_entry kernel_data; + struct gdt_entry user_code; + struct gdt_entry user_data; struct gdt_system_entry tss; } __attribute__((packed)); diff --git a/kernel/src/kernel_main.c b/kernel/src/kernel_main.c index 95bded2..91e521c 100644 --- a/kernel/src/kernel_main.c +++ b/kernel/src/kernel_main.c @@ -31,7 +31,7 @@ void exception_handler(void) { } void _Noreturn user_main(void) { - while (1) puts("Hello, world!\n"); + while(1) krintf("sigma balls!\n"); } /* @@ -107,7 +107,7 @@ void _Noreturn kernel_main(struct boot_parameters parameters) { puts("Successfully freed virtual address!\n"); - for (size_t i = 0; i < 2; i++) { + for (size_t i = 0; i < 1; i++) { message[0] = '0' + i; i = (i + 1) % 10; From 7c3aada3c92b2dfbd67cc1d6ab229ba9cdf66270 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Sat, 18 Jan 2025 13:03:18 +1000 Subject: [PATCH 03/22] Make interrupts executable in gdt (wip) --- kernel/src/arch/x86_64/cpu/idt.h | 8 --- kernel/src/arch/x86_64/klib/klib.h | 42 +++++++++++++++- kernel/src/arch/x86_64/klib/trap.c | 6 +++ kernel/src/arch/x86_64/usermode/usermode.c | 57 +++++++++++++++++----- kernel/src/arch/x86_64/usermode/usermode.h | 4 +- kernel/src/kernel_main.c | 12 ++--- 6 files changed, 98 insertions(+), 31 deletions(-) diff --git a/kernel/src/arch/x86_64/cpu/idt.h b/kernel/src/arch/x86_64/cpu/idt.h index 74db166..bed760e 100644 --- a/kernel/src/arch/x86_64/cpu/idt.h +++ b/kernel/src/arch/x86_64/cpu/idt.h @@ -24,14 +24,6 @@ enum gate_type { TRAP_64_GATE = 0xF }; -struct interrupt_frame { - uint32_t ip; - uint32_t cs; - uint32_t flags; - uint32_t sp; - uint32_t ss; -}; - void setup_interrupt_gate(uint32_t irq, void *base, enum gate_type type, uint8_t privilege_level, uint8_t ist); void load_idt(void); diff --git a/kernel/src/arch/x86_64/klib/klib.h b/kernel/src/arch/x86_64/klib/klib.h index f026923..38a652f 100644 --- a/kernel/src/arch/x86_64/klib/klib.h +++ b/kernel/src/arch/x86_64/klib/klib.h @@ -6,7 +6,45 @@ // Sleep for given ticks void ksleep(int ticks); -struct interrupt_frame; +struct interrupt_frame { + // --- 1) General-purpose registers pushed by our assembly stub --- + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rsi; + uint64_t rdi; + uint64_t rbp; + uint64_t rdx; + uint64_t rcx; + uint64_t rbx; + uint64_t rax; + + // Interrupt/exception number (pushed by the stub) + uint64_t int_no; + + // Error code: + // - Automatically pushed by CPU for some exceptions (e.g., page fault), + // or + // - Pushed as 0 by our assembly stub for exceptions that do not push an error code + uint64_t err_code; + + // --- 2) IRET frame (pushed automatically by CPU) --- + // RIP (instruction pointer) at time of interrupt/exception + uint64_t rip; + // Code segment selector at time of interrupt/exception + uint64_t cs; + // Saved RFLAGS + uint64_t rflags; + // Only pushed by CPU if privilege-level change (e.g., user->kernel) + uint64_t rsp; + // Only pushed by CPU if privilege-level change + uint64_t ss; +} __attribute__((packed)); // Trap gate void trap(struct interrupt_frame *frame); @@ -17,3 +55,5 @@ void heap_init(void); void *kmalloc(size_t size); // Frees the memory allocated from heap. void free(void *address); + +void exception_handler(struct interrupt_frame *frame); \ No newline at end of file diff --git a/kernel/src/arch/x86_64/klib/trap.c b/kernel/src/arch/x86_64/klib/trap.c index fda1d73..4b92f9f 100644 --- a/kernel/src/arch/x86_64/klib/trap.c +++ b/kernel/src/arch/x86_64/klib/trap.c @@ -5,3 +5,9 @@ __attribute__((interrupt)) void trap(struct interrupt_frame *frame) { puts("Trap\n"); } + +__attribute__((interrupt)) void exception_handler(struct interrupt_frame *frame) { + krintf("Fatal Error Occurred! Error Code: %x\n", frame->err_code); + while (1) asm volatile("cli\nhlt" ::); +} + diff --git a/kernel/src/arch/x86_64/usermode/usermode.c b/kernel/src/arch/x86_64/usermode/usermode.c index b6d141f..5f5d4ae 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.c +++ b/kernel/src/arch/x86_64/usermode/usermode.c @@ -2,6 +2,8 @@ #include "../../../io.h" +#include "../memory/paging.h" + static struct gdt gdt = {0}; struct { @@ -9,7 +11,8 @@ struct { uint64_t base; } __attribute__((packed)) gdt_descriptor = { .limit = sizeof(gdt) - 1, - .base = (uint64_t) &gdt}; + .base = (uint64_t) &gdt +}; static struct tss_entry tss = {0}; @@ -18,7 +21,21 @@ static uint8_t ist2_stack[4096] __attribute__((aligned(16))); static uint8_t rsp0_stack[4096] __attribute__((aligned(16))); void enter_usermode(void *function_address) { + + struct gdt_entry *ring3_kcode = &gdt.kernel_code; + struct gdt_entry *ring3_kdata = &gdt.kernel_data; + ring3_kcode->read_write = 1; + ring3_kcode->code_data_segment = 1; + ring3_kcode->present = 1; + ring3_kcode->long_mode = 1; + ring3_kcode->code = 1; + + *ring3_kdata = *ring3_kcode; + ring3_kdata->code = 0; + ring3_kdata->long_mode = 0; + struct gdt_entry *ring3_code = &gdt.user_code; + struct gdt_entry *ring3_data = &gdt.user_data; // ! EVERYTHING NOT SET IS ZERO ! @@ -39,10 +56,11 @@ void enter_usermode(void *function_address) { // data is basically the same besides the code bit *ring3_data = *ring3_code; ring3_data->code = 0; + // long mode not used as attribute ring3_data->long_mode = 0; uint64_t base = (uint64_t) &tss; - uint32_t limit = sizeof(tss) - 1; + uint32_t limit = sizeof(tss); // tss descriptor struct gdt_system_entry *ring3_tss = &gdt.tss; @@ -54,10 +72,10 @@ void enter_usermode(void *function_address) { // code bit indicates whether its 32 bit or 16 bit ring3_tss->code = 1; - + // tss descriptor is ring 0 // tss available flag = 0 - + ring3_tss->base_high = (base >> 27) & 0xFF; ring3_tss->limit_high = (limit >> 27) & 0xF; @@ -65,38 +83,53 @@ void enter_usermode(void *function_address) { tss.ist2 = (uint64_t) &ist2_stack[4095];// for double faults tss.rsp0 = (uint64_t) &rsp0_stack[4095];// for kernel stack + // ring 3 time // triple faults here sometimes? asm volatile("lgdt %0\n" ::"m"(gdt_descriptor)); krintf("GDT LOADED\n"); - // triple faults here sometimes too? + // flush tss + asm volatile( + "mov $40, %%ax\n" + "ltr %%ax" ::: "ax"); + + // triple faults here sometimes too? A asm volatile( // not too sure if this is allowed for x86_64 "mov $0x23, %%ax\n" "mov %%ax, %%ds\n" "mov %%ax, %%es\n" - "mov %%ax, %%fs\n" - "mov %%ax, %%gs\n" ::); + // "mov %%ax, %%fs\n" + // "mov %%ax, %%gs\n" ::); + ::); krintf("SETUP SEGMENTS %x\n", (uint64_t) function_address); // ! triple faults here (General protection fault) + // ! not paged ? + uint64_t new = (KERNEL_MAPPING_ADDRESS | (uint64_t)&rsp0_stack[4095]); + map_page(new, (uint64_t)&rsp0_stack[4095], PAGE_PRESENT | PAGE_WRITE | PAGE_USER); + + asm volatile( "mov %0, %%rax\n"// get the stack - "push $0x23\n" // 0x23 | 3 (user data selector) + + "push $0x23\n" // 0x20 | 3 (user data selector) "push %%rax\n" // stack address + // pushfq is sufficient but just in case "push $0x202\n"// rflag (interrupt enable flag on) - "push $0x1B\n" // 0x18 | 3 (user code selector) + + "push $0x1b\n" // 0x18 | 3 (user code selector) "push %1\n" // function "iretq\n" :: - "r"((uint64_t) &rsp0_stack[4095]), - "m"(function_address) : "rax"); + "r"((uint64_t) new), + "r"((uint64_t)function_address) : "rax"); } // osdev said I needed this void tss_set_kernel_stack(uint64_t stack) { tss.rsp0 = stack; -} \ No newline at end of file +} diff --git a/kernel/src/arch/x86_64/usermode/usermode.h b/kernel/src/arch/x86_64/usermode/usermode.h index 2e7954b..cdfe405 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.h +++ b/kernel/src/arch/x86_64/usermode/usermode.h @@ -71,7 +71,7 @@ struct gdt_system_entry { uint64_t reserved_low : 8; uint64_t must_be_zero : 5; uint64_t reerved_high : 19; -} __attribute__((packed)); +} __attribute__((packed)); // Refer to page 377 (Figure 12-8) in AMD manual struct tss_entry { @@ -102,4 +102,4 @@ struct gdt { } __attribute__((packed)); void enter_usermode(void *function_address); -void tss_set_kernel_stack(uint64_t stack); \ No newline at end of file +void tss_set_kernel_stack(uint64_t stack); diff --git a/kernel/src/kernel_main.c b/kernel/src/kernel_main.c index 91e521c..a3c184a 100644 --- a/kernel/src/kernel_main.c +++ b/kernel/src/kernel_main.c @@ -24,14 +24,10 @@ void _Noreturn _die(void) { while (1) asm volatile("cli\nhlt" ::); } -// todo: move this to its own file later -void exception_handler(void) { - puts("Fatal Error Occurred!"); - _die(); -} - void _Noreturn user_main(void) { - while(1) krintf("sigma balls!\n"); + _die(); + puts("test\n"); + while(1); } /* @@ -124,7 +120,7 @@ void _Noreturn kernel_main(struct boot_parameters parameters) { ksleep(276447232); } - enter_usermode((void *) user_main); + enter_usermode((void *)user_main); } static void read_acpi(void) { From c7e88102c55ff8d63c63a2d27f047fc6a63b61e2 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Sat, 18 Jan 2025 21:08:45 +1000 Subject: [PATCH 04/22] Implement user mode (working) --- bootloader/src/lm.asm | 9 ++++--- kernel/src/arch/x86_64/klib/klib.h | 43 +++++------------------------- kernel/src/arch/x86_64/klib/trap.c | 2 +- kernel/src/kernel_main.c | 3 +-- 4 files changed, 13 insertions(+), 44 deletions(-) diff --git a/bootloader/src/lm.asm b/bootloader/src/lm.asm index 06dff81..fd6f0a9 100644 --- a/bootloader/src/lm.asm +++ b/bootloader/src/lm.asm @@ -2,6 +2,7 @@ PAGE_PRESENT equ (1 << 0) PAGE_WRITE equ (1 << 1) +PAGE_USER equ (1 << 2) ; check if it collides with anything later on paging_table_buffer equ 0x9000 @@ -79,7 +80,7 @@ switch_to_lm: ; address of page directory pointer table lea eax, [di + 0x1000] - or eax, PAGE_PRESENT | PAGE_WRITE + or eax, PAGE_PRESENT | PAGE_WRITE | PAGE_USER mov [di], eax ; point the page directory pointer table [0] @@ -87,7 +88,7 @@ switch_to_lm: ; address of page directory lea eax, [di + 0x2000] - or eax, PAGE_PRESENT | PAGE_WRITE + or eax, PAGE_PRESENT | PAGE_WRITE | PAGE_USER mov [di + 0x1000], eax ; point the page directory [0] @@ -95,13 +96,13 @@ switch_to_lm: ; address of page table lea eax, [di + 0x3000] - or eax, PAGE_PRESENT | PAGE_WRITE + or eax, PAGE_PRESENT | PAGE_WRITE | PAGE_USER mov [di + 0x2000], eax ; initialise variables before build push di lea di, [di + 0x3000] - mov eax, PAGE_PRESENT | PAGE_WRITE + mov eax, PAGE_PRESENT | PAGE_WRITE | PAGE_USER ; builds page table ; it points each entry to a address diff --git a/kernel/src/arch/x86_64/klib/klib.h b/kernel/src/arch/x86_64/klib/klib.h index 38a652f..be4b496 100644 --- a/kernel/src/arch/x86_64/klib/klib.h +++ b/kernel/src/arch/x86_64/klib/klib.h @@ -7,43 +7,12 @@ void ksleep(int ticks); struct interrupt_frame { - // --- 1) General-purpose registers pushed by our assembly stub --- - uint64_t r15; - uint64_t r14; - uint64_t r13; - uint64_t r12; - uint64_t r11; - uint64_t r10; - uint64_t r9; - uint64_t r8; - uint64_t rsi; - uint64_t rdi; - uint64_t rbp; - uint64_t rdx; - uint64_t rcx; - uint64_t rbx; - uint64_t rax; - - // Interrupt/exception number (pushed by the stub) - uint64_t int_no; - - // Error code: - // - Automatically pushed by CPU for some exceptions (e.g., page fault), - // or - // - Pushed as 0 by our assembly stub for exceptions that do not push an error code - uint64_t err_code; - - // --- 2) IRET frame (pushed automatically by CPU) --- - // RIP (instruction pointer) at time of interrupt/exception - uint64_t rip; - // Code segment selector at time of interrupt/exception - uint64_t cs; - // Saved RFLAGS - uint64_t rflags; - // Only pushed by CPU if privilege-level change (e.g., user->kernel) - uint64_t rsp; - // Only pushed by CPU if privilege-level change - uint64_t ss; + uint64_t one; + uint64_t two; + uint64_t three; + uint64_t four; + uint64_t five; + uint64_t six; } __attribute__((packed)); // Trap gate diff --git a/kernel/src/arch/x86_64/klib/trap.c b/kernel/src/arch/x86_64/klib/trap.c index 4b92f9f..28a4236 100644 --- a/kernel/src/arch/x86_64/klib/trap.c +++ b/kernel/src/arch/x86_64/klib/trap.c @@ -7,7 +7,7 @@ __attribute__((interrupt)) void trap(struct interrupt_frame *frame) { } __attribute__((interrupt)) void exception_handler(struct interrupt_frame *frame) { - krintf("Fatal Error Occurred! Error Code: %x\n", frame->err_code); + krintf("Fatal Error Occurred! Error Code: %x\n", frame->one); while (1) asm volatile("cli\nhlt" ::); } diff --git a/kernel/src/kernel_main.c b/kernel/src/kernel_main.c index a3c184a..9cd3494 100644 --- a/kernel/src/kernel_main.c +++ b/kernel/src/kernel_main.c @@ -25,8 +25,7 @@ void _Noreturn _die(void) { } void _Noreturn user_main(void) { - _die(); - puts("test\n"); + //puts("test\n"); while(1); } From c33c2c491215d64a3456da4b188ff8647b394517 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Sat, 18 Jan 2025 21:10:34 +1000 Subject: [PATCH 05/22] Fix style --- kernel/src/arch/x86_64/klib/klib.h | 2 +- kernel/src/arch/x86_64/usermode/usermode.c | 12 ++++++------ kernel/src/arch/x86_64/usermode/usermode.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/kernel/src/arch/x86_64/klib/klib.h b/kernel/src/arch/x86_64/klib/klib.h index be4b496..d9d73a5 100644 --- a/kernel/src/arch/x86_64/klib/klib.h +++ b/kernel/src/arch/x86_64/klib/klib.h @@ -25,4 +25,4 @@ void *kmalloc(size_t size); // Frees the memory allocated from heap. void free(void *address); -void exception_handler(struct interrupt_frame *frame); \ No newline at end of file +void exception_handler(struct interrupt_frame *frame); diff --git a/kernel/src/arch/x86_64/usermode/usermode.c b/kernel/src/arch/x86_64/usermode/usermode.c index 5f5d4ae..6c7ffdc 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.c +++ b/kernel/src/arch/x86_64/usermode/usermode.c @@ -33,9 +33,9 @@ void enter_usermode(void *function_address) { *ring3_kdata = *ring3_kcode; ring3_kdata->code = 0; ring3_kdata->long_mode = 0; - + struct gdt_entry *ring3_code = &gdt.user_code; - + struct gdt_entry *ring3_data = &gdt.user_data; // ! EVERYTHING NOT SET IS ZERO ! @@ -111,17 +111,17 @@ void enter_usermode(void *function_address) { // ! not paged ? uint64_t new = (KERNEL_MAPPING_ADDRESS | (uint64_t)&rsp0_stack[4095]); map_page(new, (uint64_t)&rsp0_stack[4095], PAGE_PRESENT | PAGE_WRITE | PAGE_USER); - + asm volatile( "mov %0, %%rax\n"// get the stack - + "push $0x23\n" // 0x20 | 3 (user data selector) "push %%rax\n" // stack address - + // pushfq is sufficient but just in case "push $0x202\n"// rflag (interrupt enable flag on) - + "push $0x1b\n" // 0x18 | 3 (user code selector) "push %1\n" // function "iretq\n" :: diff --git a/kernel/src/arch/x86_64/usermode/usermode.h b/kernel/src/arch/x86_64/usermode/usermode.h index cdfe405..d9be0d0 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.h +++ b/kernel/src/arch/x86_64/usermode/usermode.h @@ -71,7 +71,7 @@ struct gdt_system_entry { uint64_t reserved_low : 8; uint64_t must_be_zero : 5; uint64_t reerved_high : 19; -} __attribute__((packed)); +} __attribute__((packed)); // Refer to page 377 (Figure 12-8) in AMD manual struct tss_entry { From a937819e9c78f5570b0e3b8b9c3b102ec7a4efb6 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Wed, 22 Jan 2025 20:36:14 +1100 Subject: [PATCH 06/22] Fix clang style --- kernel/src/arch/x86_64/klib/trap.c | 1 - kernel/src/arch/x86_64/usermode/usermode.c | 27 ++- kernel/src/kernel_main.c | 6 +- llvm.sh | 184 +++++++++++++++++++++ 4 files changed, 197 insertions(+), 21 deletions(-) create mode 100644 llvm.sh diff --git a/kernel/src/arch/x86_64/klib/trap.c b/kernel/src/arch/x86_64/klib/trap.c index 28a4236..f7fbf1f 100644 --- a/kernel/src/arch/x86_64/klib/trap.c +++ b/kernel/src/arch/x86_64/klib/trap.c @@ -10,4 +10,3 @@ __attribute__((interrupt)) void exception_handler(struct interrupt_frame *frame) krintf("Fatal Error Occurred! Error Code: %x\n", frame->one); while (1) asm volatile("cli\nhlt" ::); } - diff --git a/kernel/src/arch/x86_64/usermode/usermode.c b/kernel/src/arch/x86_64/usermode/usermode.c index 6c7ffdc..f28952b 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.c +++ b/kernel/src/arch/x86_64/usermode/usermode.c @@ -11,8 +11,7 @@ struct { uint64_t base; } __attribute__((packed)) gdt_descriptor = { .limit = sizeof(gdt) - 1, - .base = (uint64_t) &gdt -}; + .base = (uint64_t) &gdt}; static struct tss_entry tss = {0}; @@ -101,32 +100,26 @@ void enter_usermode(void *function_address) { "mov $0x23, %%ax\n" "mov %%ax, %%ds\n" "mov %%ax, %%es\n" - // "mov %%ax, %%fs\n" - // "mov %%ax, %%gs\n" ::); - ::); + // "mov %%ax, %%fs\n" + // "mov %%ax, %%gs\n" ::); + ::); krintf("SETUP SEGMENTS %x\n", (uint64_t) function_address); - // ! triple faults here (General protection fault) - // ! not paged ? - uint64_t new = (KERNEL_MAPPING_ADDRESS | (uint64_t)&rsp0_stack[4095]); - map_page(new, (uint64_t)&rsp0_stack[4095], PAGE_PRESENT | PAGE_WRITE | PAGE_USER); - - asm volatile( "mov %0, %%rax\n"// get the stack - "push $0x23\n" // 0x20 | 3 (user data selector) - "push %%rax\n" // stack address + "push $0x23\n"// 0x20 | 3 (user data selector) + "push %%rax\n"// stack address // pushfq is sufficient but just in case "push $0x202\n"// rflag (interrupt enable flag on) - "push $0x1b\n" // 0x18 | 3 (user code selector) - "push %1\n" // function + "push $0x1b\n"// 0x18 | 3 (user code selector) + "push %1\n" // function "iretq\n" :: - "r"((uint64_t) new), - "r"((uint64_t)function_address) : "rax"); + "r"((uint64_t) &rsp0_stack[4095]), + "r"((uint64_t) function_address) : "rax"); } // osdev said I needed this diff --git a/kernel/src/kernel_main.c b/kernel/src/kernel_main.c index 9cd3494..9dde68f 100644 --- a/kernel/src/kernel_main.c +++ b/kernel/src/kernel_main.c @@ -26,7 +26,7 @@ void _Noreturn _die(void) { void _Noreturn user_main(void) { //puts("test\n"); - while(1); + while (1); } /* @@ -114,12 +114,12 @@ void _Noreturn kernel_main(struct boot_parameters parameters) { vga_set_color(1 + (i % 6), VGA_COLOR_BLACK); //memmap_print_entries(parameters.address_range_count, // parameters.address_ranges); -// + // asm volatile("int %0" : : "i"(0x80) : "memory"); ksleep(276447232); } - enter_usermode((void *)user_main); + enter_usermode((void *) user_main); } static void read_acpi(void) { diff --git a/llvm.sh b/llvm.sh new file mode 100644 index 0000000..b27ffd5 --- /dev/null +++ b/llvm.sh @@ -0,0 +1,184 @@ +#!/bin/bash +################################################################################ +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +################################################################################ +# +# This script will install the llvm toolchain on the different +# Debian and Ubuntu versions + +set -eux + +usage() { + set +x + echo "Usage: $0 [llvm_major_version] [all] [OPTIONS]" 1>&2 + echo -e "all\t\t\tInstall all packages." 1>&2 + echo -e "-n=code_name\t\tSpecifies the distro codename, for example bionic" 1>&2 + echo -e "-h\t\t\tPrints this help." 1>&2 + echo -e "-m=repo_base_url\tSpecifies the base URL from which to download." 1>&2 + exit 1; +} + +CURRENT_LLVM_STABLE=18 +BASE_URL="http://apt.llvm.org" + +# Check for required tools +needed_binaries=(lsb_release wget add-apt-repository gpg) +missing_binaries=() +for binary in "${needed_binaries[@]}"; do + if ! which $binary &>/dev/null ; then + missing_binaries+=($binary) + fi +done +if [[ ${#missing_binaries[@]} -gt 0 ]] ; then + echo "You are missing some tools this script requires: ${missing_binaries[@]}" + echo "(hint: apt install lsb-release wget software-properties-common gnupg)" + exit 4 +fi + +# Set default values for commandline arguments +# We default to the current stable branch of LLVM +LLVM_VERSION=$CURRENT_LLVM_STABLE +ALL=0 +DISTRO=$(lsb_release -is) +VERSION=$(lsb_release -sr) +UBUNTU_CODENAME="" +CODENAME_FROM_ARGUMENTS="" +# Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) +source /etc/os-release +DISTRO=${DISTRO,,} +case ${DISTRO} in + debian) + # Debian Trixie has a workaround because of + # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1038383 + if [[ "${VERSION}" == "unstable" ]] || [[ "${VERSION}" == "testing" ]] || [[ "${VERSION_CODENAME}" == "trixie" ]]; then + CODENAME=unstable + LINKNAME= + else + # "stable" Debian release + CODENAME=${VERSION_CODENAME} + LINKNAME=-${CODENAME} + fi + ;; + *) + # ubuntu and its derivatives + if [[ -n "${UBUNTU_CODENAME}" ]]; then + CODENAME=${UBUNTU_CODENAME} + if [[ -n "${CODENAME}" ]]; then + LINKNAME=-${CODENAME} + fi + fi + ;; +esac + +# read optional command line arguments +if [ "$#" -ge 1 ] && [ "${1::1}" != "-" ]; then + if [ "$1" != "all" ]; then + LLVM_VERSION=$1 + else + # special case for ./llvm.sh all + ALL=1 + fi + OPTIND=2 + if [ "$#" -ge 2 ]; then + if [ "$2" == "all" ]; then + # Install all packages + ALL=1 + OPTIND=3 + fi + fi +fi + +while getopts ":hm:n:" arg; do + case $arg in + h) + usage + ;; + m) + BASE_URL=${OPTARG} + ;; + n) + CODENAME=${OPTARG} + if [[ "${CODENAME}" == "unstable" ]]; then + # link name does not apply to unstable repository + LINKNAME= + else + LINKNAME=-${CODENAME} + fi + CODENAME_FROM_ARGUMENTS="true" + ;; + esac +done + +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root!" + exit 1 +fi + +declare -A LLVM_VERSION_PATTERNS +LLVM_VERSION_PATTERNS[9]="-9" +LLVM_VERSION_PATTERNS[10]="-10" +LLVM_VERSION_PATTERNS[11]="-11" +LLVM_VERSION_PATTERNS[12]="-12" +LLVM_VERSION_PATTERNS[13]="-13" +LLVM_VERSION_PATTERNS[14]="-14" +LLVM_VERSION_PATTERNS[15]="-15" +LLVM_VERSION_PATTERNS[16]="-16" +LLVM_VERSION_PATTERNS[17]="-17" +LLVM_VERSION_PATTERNS[18]="-18" +LLVM_VERSION_PATTERNS[19]="-19" +LLVM_VERSION_PATTERNS[20]="" + +if [ ! ${LLVM_VERSION_PATTERNS[$LLVM_VERSION]+_} ]; then + echo "This script does not support LLVM version $LLVM_VERSION" + exit 3 +fi + +LLVM_VERSION_STRING=${LLVM_VERSION_PATTERNS[$LLVM_VERSION]} + +# join the repository name +if [[ -n "${CODENAME}" ]]; then + REPO_NAME="deb ${BASE_URL}/${CODENAME}/ llvm-toolchain${LINKNAME}${LLVM_VERSION_STRING} main" + + # check if the repository exists for the distro and version + if ! wget -q --method=HEAD ${BASE_URL}/${CODENAME} &> /dev/null; then + if [[ -n "${CODENAME_FROM_ARGUMENTS}" ]]; then + echo "Specified codename '${CODENAME}' is not supported by this script." + else + echo "Distribution '${DISTRO}' in version '${VERSION}' is not supported by this script." + fi + exit 2 + fi +fi + + +# install everything + +if [[ ! -f /etc/apt/trusted.gpg.d/apt.llvm.org.asc ]]; then + # download GPG key once + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc +fi + +if [[ -z "`apt-key list 2> /dev/null | grep -i llvm`" ]]; then + # Delete the key in the old format + apt-key del AF4F7421 || true +fi +if [[ "${VERSION_CODENAME}" == "bookworm" ]]; then + # add it twice to workaround: + # https://github.com/llvm/llvm-project/issues/62475 + add-apt-repository -y "${REPO_NAME}" +fi + +add-apt-repository -y "${REPO_NAME}" +apt-get update +PKG="clang-$LLVM_VERSION lldb-$LLVM_VERSION lld-$LLVM_VERSION clangd-$LLVM_VERSION" +if [[ $ALL -eq 1 ]]; then + # same as in test-install.sh + # No worries if we have dups + PKG="$PKG clang-tidy-$LLVM_VERSION clang-format-$LLVM_VERSION clang-tools-$LLVM_VERSION llvm-$LLVM_VERSION-dev lld-$LLVM_VERSION lldb-$LLVM_VERSION llvm-$LLVM_VERSION-tools libomp-$LLVM_VERSION-dev libc++-$LLVM_VERSION-dev libc++abi-$LLVM_VERSION-dev libclang-common-$LLVM_VERSION-dev libclang-$LLVM_VERSION-dev libclang-cpp$LLVM_VERSION-dev libunwind-$LLVM_VERSION-dev" + if test $LLVM_VERSION -gt 14; then + PKG="$PKG libclang-rt-$LLVM_VERSION-dev libpolly-$LLVM_VERSION-dev" + fi +fi +apt-get install -y $PKG From ac37c2b5c1b26c6071030541279c5489c190bcc5 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Wed, 22 Jan 2025 20:53:50 +1100 Subject: [PATCH 07/22] Remove llvm.sh --- llvm.sh | 184 -------------------------------------------------------- 1 file changed, 184 deletions(-) delete mode 100644 llvm.sh diff --git a/llvm.sh b/llvm.sh deleted file mode 100644 index b27ffd5..0000000 --- a/llvm.sh +++ /dev/null @@ -1,184 +0,0 @@ -#!/bin/bash -################################################################################ -# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -# See https://llvm.org/LICENSE.txt for license information. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -################################################################################ -# -# This script will install the llvm toolchain on the different -# Debian and Ubuntu versions - -set -eux - -usage() { - set +x - echo "Usage: $0 [llvm_major_version] [all] [OPTIONS]" 1>&2 - echo -e "all\t\t\tInstall all packages." 1>&2 - echo -e "-n=code_name\t\tSpecifies the distro codename, for example bionic" 1>&2 - echo -e "-h\t\t\tPrints this help." 1>&2 - echo -e "-m=repo_base_url\tSpecifies the base URL from which to download." 1>&2 - exit 1; -} - -CURRENT_LLVM_STABLE=18 -BASE_URL="http://apt.llvm.org" - -# Check for required tools -needed_binaries=(lsb_release wget add-apt-repository gpg) -missing_binaries=() -for binary in "${needed_binaries[@]}"; do - if ! which $binary &>/dev/null ; then - missing_binaries+=($binary) - fi -done -if [[ ${#missing_binaries[@]} -gt 0 ]] ; then - echo "You are missing some tools this script requires: ${missing_binaries[@]}" - echo "(hint: apt install lsb-release wget software-properties-common gnupg)" - exit 4 -fi - -# Set default values for commandline arguments -# We default to the current stable branch of LLVM -LLVM_VERSION=$CURRENT_LLVM_STABLE -ALL=0 -DISTRO=$(lsb_release -is) -VERSION=$(lsb_release -sr) -UBUNTU_CODENAME="" -CODENAME_FROM_ARGUMENTS="" -# Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) -source /etc/os-release -DISTRO=${DISTRO,,} -case ${DISTRO} in - debian) - # Debian Trixie has a workaround because of - # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1038383 - if [[ "${VERSION}" == "unstable" ]] || [[ "${VERSION}" == "testing" ]] || [[ "${VERSION_CODENAME}" == "trixie" ]]; then - CODENAME=unstable - LINKNAME= - else - # "stable" Debian release - CODENAME=${VERSION_CODENAME} - LINKNAME=-${CODENAME} - fi - ;; - *) - # ubuntu and its derivatives - if [[ -n "${UBUNTU_CODENAME}" ]]; then - CODENAME=${UBUNTU_CODENAME} - if [[ -n "${CODENAME}" ]]; then - LINKNAME=-${CODENAME} - fi - fi - ;; -esac - -# read optional command line arguments -if [ "$#" -ge 1 ] && [ "${1::1}" != "-" ]; then - if [ "$1" != "all" ]; then - LLVM_VERSION=$1 - else - # special case for ./llvm.sh all - ALL=1 - fi - OPTIND=2 - if [ "$#" -ge 2 ]; then - if [ "$2" == "all" ]; then - # Install all packages - ALL=1 - OPTIND=3 - fi - fi -fi - -while getopts ":hm:n:" arg; do - case $arg in - h) - usage - ;; - m) - BASE_URL=${OPTARG} - ;; - n) - CODENAME=${OPTARG} - if [[ "${CODENAME}" == "unstable" ]]; then - # link name does not apply to unstable repository - LINKNAME= - else - LINKNAME=-${CODENAME} - fi - CODENAME_FROM_ARGUMENTS="true" - ;; - esac -done - -if [[ $EUID -ne 0 ]]; then - echo "This script must be run as root!" - exit 1 -fi - -declare -A LLVM_VERSION_PATTERNS -LLVM_VERSION_PATTERNS[9]="-9" -LLVM_VERSION_PATTERNS[10]="-10" -LLVM_VERSION_PATTERNS[11]="-11" -LLVM_VERSION_PATTERNS[12]="-12" -LLVM_VERSION_PATTERNS[13]="-13" -LLVM_VERSION_PATTERNS[14]="-14" -LLVM_VERSION_PATTERNS[15]="-15" -LLVM_VERSION_PATTERNS[16]="-16" -LLVM_VERSION_PATTERNS[17]="-17" -LLVM_VERSION_PATTERNS[18]="-18" -LLVM_VERSION_PATTERNS[19]="-19" -LLVM_VERSION_PATTERNS[20]="" - -if [ ! ${LLVM_VERSION_PATTERNS[$LLVM_VERSION]+_} ]; then - echo "This script does not support LLVM version $LLVM_VERSION" - exit 3 -fi - -LLVM_VERSION_STRING=${LLVM_VERSION_PATTERNS[$LLVM_VERSION]} - -# join the repository name -if [[ -n "${CODENAME}" ]]; then - REPO_NAME="deb ${BASE_URL}/${CODENAME}/ llvm-toolchain${LINKNAME}${LLVM_VERSION_STRING} main" - - # check if the repository exists for the distro and version - if ! wget -q --method=HEAD ${BASE_URL}/${CODENAME} &> /dev/null; then - if [[ -n "${CODENAME_FROM_ARGUMENTS}" ]]; then - echo "Specified codename '${CODENAME}' is not supported by this script." - else - echo "Distribution '${DISTRO}' in version '${VERSION}' is not supported by this script." - fi - exit 2 - fi -fi - - -# install everything - -if [[ ! -f /etc/apt/trusted.gpg.d/apt.llvm.org.asc ]]; then - # download GPG key once - wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc -fi - -if [[ -z "`apt-key list 2> /dev/null | grep -i llvm`" ]]; then - # Delete the key in the old format - apt-key del AF4F7421 || true -fi -if [[ "${VERSION_CODENAME}" == "bookworm" ]]; then - # add it twice to workaround: - # https://github.com/llvm/llvm-project/issues/62475 - add-apt-repository -y "${REPO_NAME}" -fi - -add-apt-repository -y "${REPO_NAME}" -apt-get update -PKG="clang-$LLVM_VERSION lldb-$LLVM_VERSION lld-$LLVM_VERSION clangd-$LLVM_VERSION" -if [[ $ALL -eq 1 ]]; then - # same as in test-install.sh - # No worries if we have dups - PKG="$PKG clang-tidy-$LLVM_VERSION clang-format-$LLVM_VERSION clang-tools-$LLVM_VERSION llvm-$LLVM_VERSION-dev lld-$LLVM_VERSION lldb-$LLVM_VERSION llvm-$LLVM_VERSION-tools libomp-$LLVM_VERSION-dev libc++-$LLVM_VERSION-dev libc++abi-$LLVM_VERSION-dev libclang-common-$LLVM_VERSION-dev libclang-$LLVM_VERSION-dev libclang-cpp$LLVM_VERSION-dev libunwind-$LLVM_VERSION-dev" - if test $LLVM_VERSION -gt 14; then - PKG="$PKG libclang-rt-$LLVM_VERSION-dev libpolly-$LLVM_VERSION-dev" - fi -fi -apt-get install -y $PKG From c5d8f74ef56727e3332fbd568b4eca6efa813dc6 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Sun, 26 Jan 2025 11:28:26 +1100 Subject: [PATCH 08/22] Use higher free memory region --- CMakeLists.txt | 2 +- kernel/src/kernel.ld | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3633fb5..60277c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ add_custom_command( COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tools/bind_kernel.py --bootloader "bootloader/boot.bin" --kernel $ - --entry 0x2000 + --entry 0xD000 --output "os.img" DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/bind_kernel.py "bootloader/boot.bin" diff --git a/kernel/src/kernel.ld b/kernel/src/kernel.ld index c7a41c6..268f119 100644 --- a/kernel/src/kernel.ld +++ b/kernel/src/kernel.ld @@ -7,7 +7,7 @@ SECTIONS { /* Define the memory layout and where sections are placed */ /* Place the code starting at 0x2000 */ - . = 0x2000; /* Set the starting address for code */ + . = 0xD000; /* Set the starting address for code */ /* kmain must be absolutely first */ .text.kernel_main : { From aeaa7848a3a8e7fcf5208c34259d20e90c5b1823 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Sun, 26 Jan 2025 20:47:20 +1100 Subject: [PATCH 09/22] Add documentation and usermode test --- kernel/CMakeLists.txt | 1 + kernel/src/arch/x86_64/tests/usermode_tests.c | 16 ++++++++++++++++ kernel/src/arch/x86_64/usermode/usermode.c | 6 +----- kernel/src/arch/x86_64/usermode/usermode.h | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 kernel/src/arch/x86_64/tests/usermode_tests.c diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 8915fd6..f6f9246 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -27,6 +27,7 @@ if(KAOLIN_ENABLE_TESTS) src/arch/x86_64/tests/test.c src/arch/x86_64/tests/memory_tests.c src/tests/heap_tests.c + src/arch/x86_64/tests/usermode_tests.c ) endif() diff --git a/kernel/src/arch/x86_64/tests/usermode_tests.c b/kernel/src/arch/x86_64/tests/usermode_tests.c new file mode 100644 index 0000000..afe18c6 --- /dev/null +++ b/kernel/src/arch/x86_64/tests/usermode_tests.c @@ -0,0 +1,16 @@ +#include "../../../io.h" +#include "../../../test.h" +#include "../usermode/usermode.h" +#include "../serial/serial.h" + +// ! THIS NEEDS TO BE THE LAST TEST ! +static void usermode_function(void) { + outw(0xf4, 0x10); + while(1); +} + +static void usermode_test(void) { + enter_usermode(usermode_function); +} + +TEST("Enter usermode", usermode_test) diff --git a/kernel/src/arch/x86_64/usermode/usermode.c b/kernel/src/arch/x86_64/usermode/usermode.c index f28952b..2186499 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.c +++ b/kernel/src/arch/x86_64/usermode/usermode.c @@ -120,9 +120,5 @@ void enter_usermode(void *function_address) { "iretq\n" :: "r"((uint64_t) &rsp0_stack[4095]), "r"((uint64_t) function_address) : "rax"); -} - -// osdev said I needed this -void tss_set_kernel_stack(uint64_t stack) { - tss.rsp0 = stack; + puts("foo\n"); } diff --git a/kernel/src/arch/x86_64/usermode/usermode.h b/kernel/src/arch/x86_64/usermode/usermode.h index d9be0d0..6124024 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.h +++ b/kernel/src/arch/x86_64/usermode/usermode.h @@ -101,5 +101,5 @@ struct gdt { struct gdt_system_entry tss; } __attribute__((packed)); +// Enters usermode void enter_usermode(void *function_address); -void tss_set_kernel_stack(uint64_t stack); From e33d366f6098dc4c4366467e8c5d0e82a9e98f79 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Mon, 27 Jan 2025 14:16:40 +1100 Subject: [PATCH 10/22] Implement print for software interrupt --- kernel/CMakeLists.txt | 3 +- kernel/src/arch/x86_64/klib/klib.c | 6 ++++ kernel/src/arch/x86_64/klib/klib.h | 3 ++ kernel/src/arch/x86_64/klib/trap.c | 33 ++++++++++++++++++- kernel/src/arch/x86_64/tests/usermode_tests.c | 9 ++++- kernel/src/arch/x86_64/usermode/usermode.c | 11 +------ kernel/src/kernel_main.c | 22 +------------ 7 files changed, 53 insertions(+), 34 deletions(-) diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index f6f9246..1dbdd2d 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,4 +1,5 @@ -option(KAOLIN_ENABLE_TESTS "Enable tests" OFF) +unset(KAOLIN_ENABLE_TESTS CACHE) +option(KAOLIN_ENABLE_TESTS "Enable tests" ON) set(LINKER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/src/kernel.ld") diff --git a/kernel/src/arch/x86_64/klib/klib.c b/kernel/src/arch/x86_64/klib/klib.c index 47c184b..d144e9a 100644 --- a/kernel/src/arch/x86_64/klib/klib.c +++ b/kernel/src/arch/x86_64/klib/klib.c @@ -6,3 +6,9 @@ void ksleep(int ticks) { init_apic_timer(ticks, 0x20); while (get_apic_timer_current()); } + +_Noreturn void _die(void) { + while (1) { + asm volatile("cli\nhlt" ::); + } +} diff --git a/kernel/src/arch/x86_64/klib/klib.h b/kernel/src/arch/x86_64/klib/klib.h index d9d73a5..ac0a27d 100644 --- a/kernel/src/arch/x86_64/klib/klib.h +++ b/kernel/src/arch/x86_64/klib/klib.h @@ -26,3 +26,6 @@ void *kmalloc(size_t size); void free(void *address); void exception_handler(struct interrupt_frame *frame); + +// Stops the kernel +void _die(void); diff --git a/kernel/src/arch/x86_64/klib/trap.c b/kernel/src/arch/x86_64/klib/trap.c index f7fbf1f..9b40d82 100644 --- a/kernel/src/arch/x86_64/klib/trap.c +++ b/kernel/src/arch/x86_64/klib/trap.c @@ -3,7 +3,38 @@ #include "../../../io.h" __attribute__((interrupt)) void trap(struct interrupt_frame *frame) { - puts("Trap\n"); + // rax is the system call number + // args are in rdi, rsi, rdx + + uint64_t system_call; + uint64_t arg_1; + uint64_t arg_2; + uint64_t arg_3; + + asm volatile("mov %%rax, %0\n" + "mov %%rdi, %1\n" + "mov %%rsi, %2\n" + "mov %%rdx, %3\n" + : "=r"(system_call), "=r"(arg_1), "=r"(arg_2), "=r"(arg_3) + :); + + switch (system_call) { + case 0: + puts((const char *) arg_1); + break; + case 1: + putc((char) (arg_1 & 0xFF)); + break; + case 2: + puti((int) (arg_1 & 0xFFFFFFFF)); + break; + default: + // todo: return a sentinel value to indicate invalids + puts("Invalid system call!\n"); + } + + + //puts("Trap\n"); } __attribute__((interrupt)) void exception_handler(struct interrupt_frame *frame) { diff --git a/kernel/src/arch/x86_64/tests/usermode_tests.c b/kernel/src/arch/x86_64/tests/usermode_tests.c index afe18c6..83d6355 100644 --- a/kernel/src/arch/x86_64/tests/usermode_tests.c +++ b/kernel/src/arch/x86_64/tests/usermode_tests.c @@ -1,10 +1,17 @@ #include "../../../io.h" #include "../../../test.h" -#include "../usermode/usermode.h" #include "../serial/serial.h" +#include "../usermode/usermode.h" // ! THIS NEEDS TO BE THE LAST TEST ! static void usermode_function(void) { + char *finish = "Usermode test successful\n"; + + asm volatile( + "mov %0, %%rax\n" + "mov %1, %%rdi\n" + "int %2" + : : "r"((unsigned long) 0), "r"(finish), "i"(0x80) : "memory"); outw(0xf4, 0x10); while(1); } diff --git a/kernel/src/arch/x86_64/usermode/usermode.c b/kernel/src/arch/x86_64/usermode/usermode.c index 2186499..9a8c9b2 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.c +++ b/kernel/src/arch/x86_64/usermode/usermode.c @@ -1,7 +1,5 @@ #include "usermode.h" -#include "../../../io.h" - #include "../memory/paging.h" static struct gdt gdt = {0}; @@ -84,17 +82,13 @@ void enter_usermode(void *function_address) { // ring 3 time - // triple faults here sometimes? asm volatile("lgdt %0\n" ::"m"(gdt_descriptor)); - krintf("GDT LOADED\n"); - // flush tss asm volatile( "mov $40, %%ax\n" "ltr %%ax" ::: "ax"); - // triple faults here sometimes too? A asm volatile( // not too sure if this is allowed for x86_64 "mov $0x23, %%ax\n" @@ -103,9 +97,7 @@ void enter_usermode(void *function_address) { // "mov %%ax, %%fs\n" // "mov %%ax, %%gs\n" ::); ::); - - krintf("SETUP SEGMENTS %x\n", (uint64_t) function_address); - + asm volatile( "mov %0, %%rax\n"// get the stack @@ -120,5 +112,4 @@ void enter_usermode(void *function_address) { "iretq\n" :: "r"((uint64_t) &rsp0_stack[4095]), "r"((uint64_t) function_address) : "rax"); - puts("foo\n"); } diff --git a/kernel/src/kernel_main.c b/kernel/src/kernel_main.c index 8f69415..b2cc0cd 100644 --- a/kernel/src/kernel_main.c +++ b/kernel/src/kernel_main.c @@ -28,12 +28,6 @@ static void read_acpi(void); static void setup_idt(void); -_Noreturn static void _die(void) { - while (1) { - asm volatile("cli\nhlt" ::); - } -} - void _Noreturn user_main(void) { //puts("test\n"); while (1); @@ -99,20 +93,6 @@ void _Noreturn kernel_main(struct boot_parameters parameters) { #else char message[] = "X Hello world!\n"; - uint64_t test_virtual_address = (KERNEL_MAPPING_ADDRESS | 0xFFFFFFF); - map_page(test_virtual_address, 0xFFFFFFF, PAGE_PRESENT); - if (!verify_mapping(test_virtual_address)) { - puts("Could not page correctly!\n"); - _die(); - } - free_address(test_virtual_address); - if (verify_mapping(test_virtual_address)) { - puts("Could not free virtual address correctly!\n"); - _die(); - } - - puts("Successfully freed virtual address!\n"); - for (size_t i = 0; i < 1; i++) { message[0] = '0' + i; i = (i + 1) % 10; @@ -207,7 +187,7 @@ static void setup_idt(void) { // we have interrupt after 31 since 0-31 are reserved for errors setup_interrupt_gate(0x20, timer_handler, INTERRUPT_64_GATE, 0, 0); setup_interrupt_gate(0x21, keyboard_handler, INTERRUPT_64_GATE, 0, 0); - setup_interrupt_gate(0x80, trap, TRAP_64_GATE, 0, 0); + setup_interrupt_gate(0x80, trap, TRAP_64_GATE, 3, 0); load_idt(); } From 91f04518e431a298ac234b7093980c0ab3addc58 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Mon, 27 Jan 2025 14:40:53 +1100 Subject: [PATCH 11/22] Fix merge conflict from rebase --- kernel/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 1dbdd2d..f6f9246 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,5 +1,4 @@ -unset(KAOLIN_ENABLE_TESTS CACHE) -option(KAOLIN_ENABLE_TESTS "Enable tests" ON) +option(KAOLIN_ENABLE_TESTS "Enable tests" OFF) set(LINKER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/src/kernel.ld") From a9634a16320c5116ad1d4d5959a1f2553dcda2c9 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Mon, 27 Jan 2025 14:24:58 +1100 Subject: [PATCH 12/22] Fix style --- kernel/src/arch/x86_64/tests/usermode_tests.c | 2 +- kernel/src/arch/x86_64/usermode/usermode.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/src/arch/x86_64/tests/usermode_tests.c b/kernel/src/arch/x86_64/tests/usermode_tests.c index 83d6355..7df2ba9 100644 --- a/kernel/src/arch/x86_64/tests/usermode_tests.c +++ b/kernel/src/arch/x86_64/tests/usermode_tests.c @@ -13,7 +13,7 @@ static void usermode_function(void) { "int %2" : : "r"((unsigned long) 0), "r"(finish), "i"(0x80) : "memory"); outw(0xf4, 0x10); - while(1); + while (1); } static void usermode_test(void) { diff --git a/kernel/src/arch/x86_64/usermode/usermode.c b/kernel/src/arch/x86_64/usermode/usermode.c index 9a8c9b2..1d24b95 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.c +++ b/kernel/src/arch/x86_64/usermode/usermode.c @@ -97,7 +97,7 @@ void enter_usermode(void *function_address) { // "mov %%ax, %%fs\n" // "mov %%ax, %%gs\n" ::); ::); - + asm volatile( "mov %0, %%rax\n"// get the stack From ca053f9cef35ef85cf3fb32e6fe85e6a264d1b77 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Mon, 27 Jan 2025 14:35:07 +1100 Subject: [PATCH 13/22] Fix offset and add msg to user_main --- bootloader/src/stage0.asm | 10 +++++----- kernel/src/kernel_main.c | 8 ++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/bootloader/src/stage0.asm b/bootloader/src/stage0.asm index 8764b49..083c78d 100644 --- a/bootloader/src/stage0.asm +++ b/bootloader/src/stage0.asm @@ -19,18 +19,18 @@ call disk_load_lba ; temporarily load kernel image metadata - ; 0x2000..0x2004 : uintptr_t entry - ; 0x2000..0x2006 : uint16_t sectors - mov dword [dap_dest], 0x2000 + ; 0xD000..0xD004 : uintptr_t entry + ; 0xD000..0xD006 : uint16_t sectors + mov dword [dap_dest], 0xD000 mov word [dap_sectors], 1 mov dword [dap_lba], 0x2 call disk_load_lba - mov eax, dword [0x2000] + mov eax, dword [0xD000] mov dword [dap_dest], eax mov dword [kernel_entry], eax - mov ax, word [0x2004] + mov ax, word [0xD004] mov word [dap_sectors], ax ; load actual kernel image diff --git a/kernel/src/kernel_main.c b/kernel/src/kernel_main.c index b2cc0cd..eb62512 100644 --- a/kernel/src/kernel_main.c +++ b/kernel/src/kernel_main.c @@ -29,7 +29,12 @@ static void read_acpi(void); static void setup_idt(void); void _Noreturn user_main(void) { - //puts("test\n"); + const char *msg = "Entered usermode!\n"; + asm volatile( + "mov %0, %%rax\n" + "mov %1, %%rdi\n" + "int %2" + : : "r"((unsigned long) 0), "r"(msg), "i"(0x80) : "memory"); while (1); } @@ -106,7 +111,6 @@ void _Noreturn kernel_main(struct boot_parameters parameters) { //memmap_print_entries(parameters.address_range_count, // parameters.address_ranges); // - asm volatile("int %0" : : "i"(0x80) : "memory"); ksleep(276447232); } From f6cb35f24f1c703d4aced10193d9d7468bf83629 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Mon, 27 Jan 2025 14:36:29 +1100 Subject: [PATCH 14/22] Fix style --- kernel/src/kernel_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/src/kernel_main.c b/kernel/src/kernel_main.c index eb62512..29b8d54 100644 --- a/kernel/src/kernel_main.c +++ b/kernel/src/kernel_main.c @@ -30,7 +30,7 @@ static void setup_idt(void); void _Noreturn user_main(void) { const char *msg = "Entered usermode!\n"; - asm volatile( + asm volatile( "mov %0, %%rax\n" "mov %1, %%rdi\n" "int %2" From 6d215b10634d00d9e720079a627a7726f761b608 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Wed, 29 Jan 2025 11:15:13 +1100 Subject: [PATCH 15/22] Fix style --- kernel/src/arch/x86_64/apic/lapic.c | 2 +- kernel/src/arch/x86_64/klib/klib.h | 7 +++--- kernel/src/arch/x86_64/klib/trap.c | 21 +++++++++-------- kernel/src/arch/x86_64/usermode/usermode.c | 27 +++++++++++----------- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/kernel/src/arch/x86_64/apic/lapic.c b/kernel/src/arch/x86_64/apic/lapic.c index 3e6e66b..0e0f780 100644 --- a/kernel/src/arch/x86_64/apic/lapic.c +++ b/kernel/src/arch/x86_64/apic/lapic.c @@ -24,7 +24,7 @@ int apic_get_id(void) { static void cpu_set_apic_base(uintptr_t apic) { uint32_t edx = 0; // osdev says 0xfffff0000 but thats 36 bits - might be a typo? - uint32_t eax = (apic & 0xFFFFFF000) | IA32_APIC_BASE_MSR_ENABLE; + uint32_t eax = (apic & 0xFFFFF000) | IA32_APIC_BASE_MSR_ENABLE; cpu_set_msr(IA32_APIC_BASE_MSR, eax, edx); } diff --git a/kernel/src/arch/x86_64/klib/klib.h b/kernel/src/arch/x86_64/klib/klib.h index ac0a27d..8298fc6 100644 --- a/kernel/src/arch/x86_64/klib/klib.h +++ b/kernel/src/arch/x86_64/klib/klib.h @@ -16,7 +16,7 @@ struct interrupt_frame { } __attribute__((packed)); // Trap gate -void trap(struct interrupt_frame *frame); +__attribute__((interrupt)) void trap(struct interrupt_frame *frame); // Initialises the heap by mapping it in virtual memory. void heap_init(void); @@ -25,7 +25,8 @@ void *kmalloc(size_t size); // Frees the memory allocated from heap. void free(void *address); -void exception_handler(struct interrupt_frame *frame); +// Exception Handler +__attribute__((interrupt)) void exception_handler(struct interrupt_frame *frame); // Stops the kernel -void _die(void); +_Noreturn void _die(void); diff --git a/kernel/src/arch/x86_64/klib/trap.c b/kernel/src/arch/x86_64/klib/trap.c index 9b40d82..6940f2c 100644 --- a/kernel/src/arch/x86_64/klib/trap.c +++ b/kernel/src/arch/x86_64/klib/trap.c @@ -2,7 +2,7 @@ #include "../../../io.h" -__attribute__((interrupt)) void trap(struct interrupt_frame *frame) { +void trap(struct interrupt_frame *frame) { // rax is the system call number // args are in rdi, rsi, rdx @@ -29,15 +29,18 @@ __attribute__((interrupt)) void trap(struct interrupt_frame *frame) { puti((int) (arg_1 & 0xFFFFFFFF)); break; default: - // todo: return a sentinel value to indicate invalids - puts("Invalid system call!\n"); + asm volatile("mov $0, %%rax\n" : : "r"((uint64_t) -1)); } - - - //puts("Trap\n"); } -__attribute__((interrupt)) void exception_handler(struct interrupt_frame *frame) { - krintf("Fatal Error Occurred! Error Code: %x\n", frame->one); - while (1) asm volatile("cli\nhlt" ::); +void exception_handler(struct interrupt_frame *frame) { + puts("Exception Occurred!\n"); + krintf("Error Code/RIP: %x\n", frame->six); + krintf("RIP/CS: %x\n", frame->five); + krintf("CS/RFLAGS: %x\n", frame->four); + krintf("RFLAGS/RSP: %x\n", frame->three); + krintf("RSP/SS: %x\n", frame->two); + krintf("SS: %x\n", frame->one); + + _die(); } diff --git a/kernel/src/arch/x86_64/usermode/usermode.c b/kernel/src/arch/x86_64/usermode/usermode.c index 1d24b95..97eae7f 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.c +++ b/kernel/src/arch/x86_64/usermode/usermode.c @@ -13,9 +13,11 @@ struct { static struct tss_entry tss = {0}; -static uint8_t ist1_stack[4096] __attribute__((aligned(16))); -static uint8_t ist2_stack[4096] __attribute__((aligned(16))); -static uint8_t rsp0_stack[4096] __attribute__((aligned(16))); +#define STACK_SIZE 4096 + +static uint8_t ist1_stack[STACK_SIZE] __attribute__((aligned(16))); +static uint8_t ist2_stack[STACK_SIZE] __attribute__((aligned(16))); +static uint8_t rsp0_stack[STACK_SIZE] __attribute__((aligned(16))); void enter_usermode(void *function_address) { @@ -76,9 +78,9 @@ void enter_usermode(void *function_address) { ring3_tss->base_high = (base >> 27) & 0xFF; ring3_tss->limit_high = (limit >> 27) & 0xF; - tss.ist1 = (uint64_t) &ist1_stack[4095];// this one is for traps - tss.ist2 = (uint64_t) &ist2_stack[4095];// for double faults - tss.rsp0 = (uint64_t) &rsp0_stack[4095];// for kernel stack + tss.ist1 = (uint64_t) &ist1_stack[STACK_SIZE - 1];// this one is for traps + tss.ist2 = (uint64_t) &ist2_stack[STACK_SIZE - 1];// for double faults + tss.rsp0 = (uint64_t) &rsp0_stack[STACK_SIZE - 1];// for kernel stack // ring 3 time @@ -93,10 +95,7 @@ void enter_usermode(void *function_address) { // not too sure if this is allowed for x86_64 "mov $0x23, %%ax\n" "mov %%ax, %%ds\n" - "mov %%ax, %%es\n" - // "mov %%ax, %%fs\n" - // "mov %%ax, %%gs\n" ::); - ::); + "mov %%ax, %%es\n" ::); asm volatile( "mov %0, %%rax\n"// get the stack @@ -109,7 +108,9 @@ void enter_usermode(void *function_address) { "push $0x1b\n"// 0x18 | 3 (user code selector) "push %1\n" // function - "iretq\n" :: - "r"((uint64_t) &rsp0_stack[4095]), - "r"((uint64_t) function_address) : "rax"); + "iretq\n" ::"r"((uint64_t) &rsp0_stack[STACK_SIZE - 1]), + "r"((uint64_t) function_address) + : "rax"); + + __builtin_unreachable(); } From c3f73cbb6fb0fd8c05daae634b0b405d5caddb96 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Wed, 29 Jan 2025 11:23:22 +1100 Subject: [PATCH 16/22] Small change --- kernel/src/arch/x86_64/klib/klib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/src/arch/x86_64/klib/klib.h b/kernel/src/arch/x86_64/klib/klib.h index 8298fc6..8948644 100644 --- a/kernel/src/arch/x86_64/klib/klib.h +++ b/kernel/src/arch/x86_64/klib/klib.h @@ -29,4 +29,4 @@ void free(void *address); __attribute__((interrupt)) void exception_handler(struct interrupt_frame *frame); // Stops the kernel -_Noreturn void _die(void); +void _Noreturn _die(void); From b4026c40bc73e97a591f815f7374a33a0fbf116d Mon Sep 17 00:00:00 2001 From: Ramid Khan Date: Wed, 29 Jan 2025 12:49:35 +1100 Subject: [PATCH 17/22] Update kernel/src/arch/x86_64/usermode/usermode.c --- kernel/src/arch/x86_64/usermode/usermode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/src/arch/x86_64/usermode/usermode.c b/kernel/src/arch/x86_64/usermode/usermode.c index 97eae7f..e66633f 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.c +++ b/kernel/src/arch/x86_64/usermode/usermode.c @@ -9,7 +9,8 @@ struct { uint64_t base; } __attribute__((packed)) gdt_descriptor = { .limit = sizeof(gdt) - 1, - .base = (uint64_t) &gdt}; + .base = (uint64_t) &gdt, +}; static struct tss_entry tss = {0}; From 0806b5e170ce0749a45b3310be643cd5bfff61ca Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Wed, 29 Jan 2025 18:56:25 +1100 Subject: [PATCH 18/22] Implement keyboard driver --- .../arch/x86_64/drivers/keyboard/keyboard.c | 142 +++++++++++++++++- .../arch/x86_64/drivers/keyboard/keyboard.h | 10 +- kernel/src/arch/x86_64/usermode/usermode.h | 2 +- kernel/src/arch/x86_64/vga/vga.c | 53 +++++++ kernel/src/arch/x86_64/vga/vga.h | 9 ++ kernel/src/kernel_main.c | 18 ++- 6 files changed, 224 insertions(+), 10 deletions(-) diff --git a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c index abbc827..10e2435 100644 --- a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c +++ b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c @@ -2,12 +2,148 @@ #include "../../../../io.h" #include "../../apic/lapic.h" +#include "../../apic/ioapic.h" +#include "../../cpu/idt.h" #include "../../serial/serial.h" -__attribute__((interrupt)) void keyboard_handler(struct interrupt_frame *frame) { +#include + +// clang-format off +enum scan_set_one { + // todo: abbreviate names + S1_ESC = 1, S1_1, S1_2, S1_3, S1_4, + S1_5, S1_6, S1_7, S1_8, S1_9, + S1_0, S1_MINUS, S1_EQUAL, S1_BACKSPACE, S1_TAB, + S1_Q, S1_W, S1_E, S1_R, S1_T, + S1_Y, S1_U, S1_I, S1_O, S1_P, S1_LEFT_BRACKET, + S1_RIGHT_BRACKET, S1_ENTER, S1_LEFT_CTRL, S1_A, S1_S, + S1_D, S1_F, S1_G, S1_H, S1_J, + S1_K, S1_L, S1_SEMI, S1_SINGLE_QUOTE, S1_BACK_TICK, + S1_LEFT_SHIFT, S1_LEFT_SLASH, S1_Z, S1_X, S1_C, + S1_V, S1_B, S1_N, S1_M, S1_COMMA, + S1_DOT, S1_RIGHT_SLASH, S1_RIGHT_SHIFT, S1_KEYPAD_STAR, + S1_LEFT_ALT, S1_SPACE, S1_CAPSLOCK, S1_F1, S1_F2, + S1_F3, S1_F4, S1_F5, S1_F6, S1_F7, + S1_F8, S1_F9, S1_F10, S1_NUMLOCK, S1_SCROLLLOCK, + S1_KEYPAD_7, S1_KEYPAD_8, S1_KEYPAD_9, S1_KEYPAD_MINUS, + S1_KEYPAD_4, S1_KEYPAD_5, S1_KEYPAD_6, S1_KEYPAD_PLUS, + S1_KEYPAD_1, S1_KEYPAD_2, S1_KEYPAD_3, S1_KEYPAD_0, + S1_KEYPAD_DOT, S1_F11, S1_F12, + // RELEASED + // todo: make a map for each key held down + S1_ESC_RELEASED = 0x81, + S1_1_RELEASED, S1_2_RELEASED, S1_3_RELEASED, S1_4_RELEASED, + S1_5_RELEASED, S1_6_RELEASED, S1_7_RELEASED, S1_8_RELEASED, + S1_9_RELEASED, S1_0_RELEASED, S1_MINUS_RELEASED, S1_EQUALS_RELEASED, + S1_BACKSPACE_RELEASED, S1_TAB_RELEASED, S1_Q_RELEASED, S1_W_RELEASED, + S1_E_RELEASED, S1_R_RELEASED, S1_T_RELEASED, S1_Y_RELEASED, S1_U_RELEASED, + S1_I_RELEASED, S1_O_RELEASED, S1_P_RELEASED, S1_LEFT_BRACKET_RELEASED, + S1_RIGHT_BRACKET_RELEASED, S1_ENTER_RELEASED, S1_LEFT_CTRL_RELEASED, S1_A_RELEASED, + S1_S_RELEASED, S1_D_RELEASED, S1_F_RELEASED, S1_G_RELEASED, S1_H_RELEASED, + S1_J_RELEASED, S1_K_RELEASED, S1_L_RELEASED, S1_SEMI_RELEASED, S1_SINGLE_QUOTE_RELEASED, + S1_BACK_TICK_RELEASED, S1_LEFT_SHIFT_RELEASED, S1_LEFT_SLASH_RELEASED, S1_Z_RELEASED, + S1_X_RELEASED, S1_C_RELEASED, S1_V_RELEASED, S1_B_RELEASED, S1_N_RELEASED, + S1_M_RELEASED, S1_COMMA_RELEASED, S1_DOT_RELEASED, S1_RIGHT_SLASH_RELEASED, + S1_RIGHT_SHIFT_RELEASED, S1_KEYPAD_STAR_RELEASED, S1_LEFT_ALT_RELEASED, S1_SPACE_RELEASED, + S1_CAPSLOCK_RELEASED, S1_F1_RELEASED, S1_F2_RELEASED, S1_F3_RELEASED, S1_F4_RELEASED, + S1_F5_RELEASED, S1_F6_RELEASED, S1_F7_RELEASED, S1_F8_RELEASED, S1_F9_RELEASED, + S1_F10_RELEASED, S1_NUMLOCK_RELEASED, S1_SCROLLLOCK_RELEASED, S1_KEYPAD_7_RELEASED, + S1_KEYPAD_8_RELEASED, S1_KEYPAD_9_RELEASED, S1_KEYPAD_MINUS_RELEASED, + S1_KEYPAD_4_RELEASED, S1_KEYPAD_5_RELEASED, S1_KEYPAD_6_RELEASED, S1_KEYPAD_PLUS_RELEASED, + S1_KEYPAD_1_RELEASED, S1_KEYPAD_2_RELEASED, S1_KEYPAD_3_RELEASED, S1_KEYPAD_0_RELEASED, + S1_KEYPAD_DOT_RELEASED, S1_F11_RELEASED, S1_F12_RELEASED +}; +// clang-format on + +#define MAX_BUFFER_SIZE 512 +static char buffer[MAX_BUFFER_SIZE]; +static size_t buffer_pos = 0; + +static __attribute__((interrupt)) void keyboard_handler(struct interrupt_frame *frame); + +static uint8_t keyboard_code_set_1(const uint8_t data); + +static bool caps_lock = 0; + +// TODO: use acpi to check if 8042 ps/2 controller is supported +// todo: poll for keyboard status +// todo: check keyboard type (1, 2, 3) +// todo: detect if key is capital or nah (need to detect caps lock and shift) + +void keyboard_init(void) { + // Disable any devices connected to ps/2 ports + //outb(0x64, 0xAD); + //outb(0x64, 0xA7); + // todo: do the steps specified by osdev later + + int apic_id = apic_get_id(); + ioapic_set_redirect((uintptr_t *) IOAPIC_VIRTUAL_ADDRESS, 1, 0x21, apic_id); + setup_interrupt_gate(0x21, keyboard_handler, INTERRUPT_64_GATE, 0, 0); +} + +// Keycodes from: https://wiki.osdev.org/PS/2_Keyboard +// CODE SET 1 +// ! for some reason 2,3,4 in qemu r different keys +static uint8_t keyboard_code_set_1(const uint8_t data) { + if (data >= S1_1 && data <= S1_EQUAL) { + return "1234567890-="[data - S1_1]; + } else if (data >= S1_Q && data <= S1_ENTER) { + return "QWERTYUIOP[]\n"[data - S1_Q]; + } else if (data >= S1_A && data <= S1_BACK_TICK) { + return "ASDFGHJKL;'`"[data - S1_A]; + } else if (data >= S1_LEFT_SLASH && data <= S1_RIGHT_SLASH) { + return "\\ZXCVBNM,./"[data - S1_LEFT_SLASH]; + } else if (data >= S1_KEYPAD_7 && data <= S1_KEYPAD_DOT) { + return "789-456+1230."[data - S1_KEYPAD_7]; + } else if (data == S1_SPACE) { + return ' '; + } else if (data == S1_KEYPAD_STAR) { + return '*'; + } else if (data == S1_CAPSLOCK) { + caps_lock = !caps_lock; + // enable caps lock LED + if (caps_lock) { + outb(0xED, inb(0xED) | (1 << 2)); + } else { + outb(0xED, inb(0xED) & ~(1 << 2)); + } + } + + return data; +} + +// TODO: have switch for different code sets +static char keyboard_convert_code(char data) { + return keyboard_code_set_1(data); +} + +// ! figure out how to make it pause/wait for getchar(); + +// todo: WRITE A FRONTEND FUNCTION FOR KLIB +char getc(void) { + while (buffer_pos == 0) { + __builtin_ia32_pause(); + } + char data = buffer[--buffer_pos]; + putc(data); + return data; +} + +void getstr(char *str, size_t len) { + for (size_t i = 0; i < len; i++) { + str[i] = getc(); + } +} + +static void keyboard_handler(struct interrupt_frame *frame) { // Consume a key, not doing this will only let // the interrupt trigger once - inb(0x60); - puts("Keyboard pressed"); + uint8_t data = keyboard_code_set_1(inb(0x60)); + if (data >= 'A' && data <= 'Z' && !caps_lock) data += 32; + if (data >= ' ' && data <= '~') buffer[buffer_pos++] = data; + // ! minor bug: could loop over and getc wont be able to get the prev 512 chars + buffer_pos %= MAX_BUFFER_SIZE; + //if (data >= ' ' && data <= '~') + // krintf("Keyboard pressed %c\n", data); send_apic_eoi(); } diff --git a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.h b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.h index 2f0e0a6..b3e0334 100644 --- a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.h +++ b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.h @@ -1,6 +1,12 @@ #pragma once +#include + struct interrupt_frame; -// Keyboard Handler -void keyboard_handler(struct interrupt_frame *frame); +// Initialise the keyboard driver +void keyboard_init(void); + +char getc(void); + +void getstr(char *str, size_t len); diff --git a/kernel/src/arch/x86_64/usermode/usermode.h b/kernel/src/arch/x86_64/usermode/usermode.h index 6124024..ee0fc24 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.h +++ b/kernel/src/arch/x86_64/usermode/usermode.h @@ -102,4 +102,4 @@ struct gdt { } __attribute__((packed)); // Enters usermode -void enter_usermode(void *function_address); +void _Noreturn enter_usermode(void *function_address); diff --git a/kernel/src/arch/x86_64/vga/vga.c b/kernel/src/arch/x86_64/vga/vga.c index fc15c3a..88da778 100644 --- a/kernel/src/arch/x86_64/vga/vga.c +++ b/kernel/src/arch/x86_64/vga/vga.c @@ -1,5 +1,7 @@ #include "vga.h" +#include "../serial/serial.h" + #include #include @@ -91,6 +93,7 @@ void vga_putchar(char c) { break; } } + vga_cursor_update(terminal_column, terminal_row); } void vga_write_string(const char *data) { @@ -98,3 +101,53 @@ void vga_write_string(const char *data) { vga_putchar(*data++); } } + +// Found names here: http://www.osdever.net/FreeVGA/vga/crtcreg.htm +#define CURSOR_ADDRESS_REGISTER 0x3D4 +#define CURSOR_INDEX_REGISTER 0x3D5 + +// Cursor address +#define CURSOR_START 0x0A +#define CURSOR_END 0x0B + +#define CURSOR_LOCATION_HIGH 0x0E +#define CURSOR_LOCATION_LOW 0x0F + +void vga_cursor_enable(size_t start, size_t end) { + // Writes to CURSOR_ADDRESS_REGISTER first + // to specify address + + // start determines start of cursor height + // end determines end of cursor height + + outb(CURSOR_ADDRESS_REGISTER, CURSOR_START); + // retain bits 6 and 7 because they are reserved. + outb(CURSOR_INDEX_REGISTER, inb(CURSOR_INDEX_REGISTER) & 0xC0 | (uint8_t) (start & 0x3F)); + + outb(CURSOR_ADDRESS_REGISTER, CURSOR_END); + // retain bits 5, 6, 7 because they are reserved. + outb(CURSOR_INDEX_REGISTER, inb(CURSOR_INDEX_REGISTER) & 0xE0 | (uint8_t) (end & 0x1f)); +} + +void vga_cursor_disable(void) { + outb(CURSOR_ADDRESS_REGISTER, CURSOR_START); + // set 5th bit to 1 to disable + outb(CURSOR_INDEX_REGISTER, inb(CURSOR_INDEX_REGISTER) & 0xC0 | 0x20); +} + +void vga_cursor_update(size_t x, size_t y) { + // todo: ensure 8 bits + uint16_t pos = y * VGA_WIDTH + x; + outb(CURSOR_ADDRESS_REGISTER, CURSOR_LOCATION_LOW); + outb(CURSOR_INDEX_REGISTER, (uint8_t) (pos & 0xFF)); + outb(CURSOR_ADDRESS_REGISTER, CURSOR_LOCATION_HIGH); + outb(CURSOR_INDEX_REGISTER, (uint8_t) ((pos >> 8) & 0xFF)); +} + +uint16_t vga_cursor_position(void) { + uint16_t pos = 0; + outb(CURSOR_ADDRESS_REGISTER, CURSOR_LOCATION_LOW); + pos |= inb(CURSOR_INDEX_REGISTER); + outb(CURSOR_ADDRESS_REGISTER, CURSOR_LOCATION_HIGH); + pos |= inb(CURSOR_INDEX_REGISTER); +} diff --git a/kernel/src/arch/x86_64/vga/vga.h b/kernel/src/arch/x86_64/vga/vga.h index ff7a2d0..9d6d231 100644 --- a/kernel/src/arch/x86_64/vga/vga.h +++ b/kernel/src/arch/x86_64/vga/vga.h @@ -36,3 +36,12 @@ void vga_putchar(char c); // Prints a null-terminated string void vga_write_string(const char *data); + +// Enables text cursor +void vga_cursor_enable(size_t start, size_t end); + +// Disables text cursor +void vga_cursor_disable(void); + +// Updates text cursor +void vga_cursor_update(size_t x, size_t y); diff --git a/kernel/src/kernel_main.c b/kernel/src/kernel_main.c index 29b8d54..c3626e5 100644 --- a/kernel/src/kernel_main.c +++ b/kernel/src/kernel_main.c @@ -96,9 +96,21 @@ void _Noreturn kernel_main(struct boot_parameters parameters) { outw(0xf4, 0x10); _die(); #else + puts("Enter a character: "); + char c = getc(); + krintf("Char: %c\n", c); + + puts("Enter a string: "); + char *str = kmalloc(10); + getstr(str, 9); + str[9] = '\0'; + krintf("String: %s\n", str); + free(str); + + char message[] = "X Hello world!\n"; - for (size_t i = 0; i < 1; i++) { + for (size_t i = 0; i < 5; i++) { message[0] = '0' + i; i = (i + 1) % 10; @@ -180,8 +192,6 @@ static void setup_idt(void) { // system timer ioapic_set_redirect((uintptr_t *) IOAPIC_VIRTUAL_ADDRESS, 0, 0x20, apic_id); - // keyboard - ioapic_set_redirect((uintptr_t *) IOAPIC_VIRTUAL_ADDRESS, 1, 0x21, apic_id); for (size_t vector = 0; vector < 32; vector++) { setup_interrupt_gate(vector, exception_handler, INTERRUPT_64_GATE, 0, @@ -190,7 +200,7 @@ static void setup_idt(void) { // we have interrupt after 31 since 0-31 are reserved for errors setup_interrupt_gate(0x20, timer_handler, INTERRUPT_64_GATE, 0, 0); - setup_interrupt_gate(0x21, keyboard_handler, INTERRUPT_64_GATE, 0, 0); + keyboard_init(); setup_interrupt_gate(0x80, trap, TRAP_64_GATE, 3, 0); load_idt(); From fff51d273765344b745f8de33cce13a97817df80 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Wed, 29 Jan 2025 21:01:01 +1100 Subject: [PATCH 19/22] Implement backspace and shift --- .../arch/x86_64/drivers/keyboard/keyboard.c | 63 ++++++++++++++++--- .../arch/x86_64/drivers/keyboard/keyboard.h | 2 +- kernel/src/arch/x86_64/usermode/usermode.h | 4 -- kernel/src/arch/x86_64/vga/vga.c | 20 ++++-- kernel/src/kernel_main.c | 7 +-- 5 files changed, 75 insertions(+), 21 deletions(-) diff --git a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c index 10e2435..e734ca9 100644 --- a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c +++ b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c @@ -1,8 +1,8 @@ #include "keyboard.h" #include "../../../../io.h" -#include "../../apic/lapic.h" #include "../../apic/ioapic.h" +#include "../../apic/lapic.h" #include "../../cpu/idt.h" #include "../../serial/serial.h" @@ -62,8 +62,10 @@ static size_t buffer_pos = 0; static __attribute__((interrupt)) void keyboard_handler(struct interrupt_frame *frame); static uint8_t keyboard_code_set_1(const uint8_t data); +static uint8_t keyboard_shift_conversion(uint8_t data); -static bool caps_lock = 0; +static bool caps_lock = false; +static bool shift_pressed = false; // TODO: use acpi to check if 8042 ps/2 controller is supported // todo: poll for keyboard status @@ -85,8 +87,8 @@ void keyboard_init(void) { // CODE SET 1 // ! for some reason 2,3,4 in qemu r different keys static uint8_t keyboard_code_set_1(const uint8_t data) { - if (data >= S1_1 && data <= S1_EQUAL) { - return "1234567890-="[data - S1_1]; + if (data >= S1_1 && data <= S1_BACKSPACE) { + return "1234567890-=\b"[data - S1_1]; } else if (data >= S1_Q && data <= S1_ENTER) { return "QWERTYUIOP[]\n"[data - S1_Q]; } else if (data >= S1_A && data <= S1_BACK_TICK) { @@ -107,6 +109,10 @@ static uint8_t keyboard_code_set_1(const uint8_t data) { } else { outb(0xED, inb(0xED) & ~(1 << 2)); } + } else if (data == S1_LEFT_SHIFT || data == S1_RIGHT_SHIFT) { + shift_pressed = true; + } else if (data == S1_LEFT_SHIFT_RELEASED || data == S1_RIGHT_SHIFT_RELEASED) { + shift_pressed = false; } return data; @@ -129,9 +135,49 @@ char getc(void) { return data; } -void getstr(char *str, size_t len) { - for (size_t i = 0; i < len; i++) { - str[i] = getc(); +size_t getstr(char *str, size_t len) { + size_t i = 0; + while (i < len) { + char data = getc(); + if (data == '\n') return i; + if (data == '\b') { + i--; + continue; + } + str[i] = data; + i++; + } + return i; +} + +static uint8_t keyboard_shift_conversion(uint8_t data) { + if (!shift_pressed) return data; + + // Inverse because of caps lock + if (data >= 'A' && data <= 'Z') return data + 32; + if (data >= 'a' && data <= 'z') return data - 32; + + if (data >= '0' && data <= '9') return ")!@#$%^&*("[data - '0']; + + switch (data) { + case '[': + return '{'; + case ']': + return '}'; + case '-': + return '_'; + case '=': + return '+'; + case '\\': + return '|'; + case ';': + return ':'; + case ',': + return '<'; + case '.': + return '>'; + default: + return data; } } @@ -140,7 +186,8 @@ static void keyboard_handler(struct interrupt_frame *frame) { // the interrupt trigger once uint8_t data = keyboard_code_set_1(inb(0x60)); if (data >= 'A' && data <= 'Z' && !caps_lock) data += 32; - if (data >= ' ' && data <= '~') buffer[buffer_pos++] = data; + data = keyboard_shift_conversion(data); + if ((data >= ' ' && data <= '~') || data == '\b' || data == '\t') buffer[buffer_pos++] = data; // ! minor bug: could loop over and getc wont be able to get the prev 512 chars buffer_pos %= MAX_BUFFER_SIZE; //if (data >= ' ' && data <= '~') diff --git a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.h b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.h index b3e0334..374f6fc 100644 --- a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.h +++ b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.h @@ -9,4 +9,4 @@ void keyboard_init(void); char getc(void); -void getstr(char *str, size_t len); +size_t getstr(char *str, size_t len); diff --git a/kernel/src/arch/x86_64/usermode/usermode.h b/kernel/src/arch/x86_64/usermode/usermode.h index 5d22b67..ee0fc24 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.h +++ b/kernel/src/arch/x86_64/usermode/usermode.h @@ -102,8 +102,4 @@ struct gdt { } __attribute__((packed)); // Enters usermode -<<<<<<< HEAD void _Noreturn enter_usermode(void *function_address); -======= -void enter_usermode(void *function_address); ->>>>>>> fbda0e9466d73581987f2fd6a5025a7e75b383c7 diff --git a/kernel/src/arch/x86_64/vga/vga.c b/kernel/src/arch/x86_64/vga/vga.c index 88da778..fe0e741 100644 --- a/kernel/src/arch/x86_64/vga/vga.c +++ b/kernel/src/arch/x86_64/vga/vga.c @@ -76,6 +76,18 @@ void vga_putchar(char c) { terminal_row = 0; break; } + case '\b': { + if (terminal_row == 0 && terminal_column == 0) return; + + terminal_column--; + if (terminal_column < 0) { + terminal_column = VGA_WIDTH - 1; + terminal_row--; + } + + terminal_putentryat('\0', terminal_color, terminal_column, terminal_row); + break; + } case 0x20 ... 0x7E: { terminal_putentryat(c, terminal_color, terminal_column, terminal_row); @@ -104,14 +116,14 @@ void vga_write_string(const char *data) { // Found names here: http://www.osdever.net/FreeVGA/vga/crtcreg.htm #define CURSOR_ADDRESS_REGISTER 0x3D4 -#define CURSOR_INDEX_REGISTER 0x3D5 +#define CURSOR_INDEX_REGISTER 0x3D5 // Cursor address #define CURSOR_START 0x0A -#define CURSOR_END 0x0B +#define CURSOR_END 0x0B #define CURSOR_LOCATION_HIGH 0x0E -#define CURSOR_LOCATION_LOW 0x0F +#define CURSOR_LOCATION_LOW 0x0F void vga_cursor_enable(size_t start, size_t end) { // Writes to CURSOR_ADDRESS_REGISTER first @@ -119,7 +131,7 @@ void vga_cursor_enable(size_t start, size_t end) { // start determines start of cursor height // end determines end of cursor height - + outb(CURSOR_ADDRESS_REGISTER, CURSOR_START); // retain bits 6 and 7 because they are reserved. outb(CURSOR_INDEX_REGISTER, inb(CURSOR_INDEX_REGISTER) & 0xC0 | (uint8_t) (start & 0x3F)); diff --git a/kernel/src/kernel_main.c b/kernel/src/kernel_main.c index c3626e5..d022440 100644 --- a/kernel/src/kernel_main.c +++ b/kernel/src/kernel_main.c @@ -102,12 +102,11 @@ void _Noreturn kernel_main(struct boot_parameters parameters) { puts("Enter a string: "); char *str = kmalloc(10); - getstr(str, 9); - str[9] = '\0'; + str[getstr(str, 9)] = '\0'; krintf("String: %s\n", str); free(str); - - + + char message[] = "X Hello world!\n"; for (size_t i = 0; i < 5; i++) { From a767235dafe3308c6252b9da7bb8e4bbde792964 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Wed, 29 Jan 2025 21:17:25 +1100 Subject: [PATCH 20/22] Fix caps lock led --- .../src/arch/x86_64/drivers/keyboard/keyboard.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c index e734ca9..e2d9fab 100644 --- a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c +++ b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c @@ -4,10 +4,17 @@ #include "../../apic/ioapic.h" #include "../../apic/lapic.h" #include "../../cpu/idt.h" +#include "../../klib/klib.h" #include "../../serial/serial.h" #include +#define DATA_PORT 0x60 +#define STATUS_REGISTER 0x64 +#define COMMAND_REGISTER STATUS_REGISTER + +#define SET_LEDS 0xED + // clang-format off enum scan_set_one { // todo: abbreviate names @@ -105,9 +112,13 @@ static uint8_t keyboard_code_set_1(const uint8_t data) { caps_lock = !caps_lock; // enable caps lock LED if (caps_lock) { - outb(0xED, inb(0xED) | (1 << 2)); + outb(DATA_PORT, SET_LEDS); + ksleep(5); + outb(DATA_PORT, 1 << 2); } else { - outb(0xED, inb(0xED) & ~(1 << 2)); + outb(DATA_PORT, SET_LEDS); + ksleep(5); + outb(DATA_PORT, 0x0); } } else if (data == S1_LEFT_SHIFT || data == S1_RIGHT_SHIFT) { shift_pressed = true; @@ -184,7 +195,7 @@ static uint8_t keyboard_shift_conversion(uint8_t data) { static void keyboard_handler(struct interrupt_frame *frame) { // Consume a key, not doing this will only let // the interrupt trigger once - uint8_t data = keyboard_code_set_1(inb(0x60)); + uint8_t data = keyboard_code_set_1(inb(DATA_PORT)); if (data >= 'A' && data <= 'Z' && !caps_lock) data += 32; data = keyboard_shift_conversion(data); if ((data >= ' ' && data <= '~') || data == '\b' || data == '\t') buffer[buffer_pos++] = data; From b03c9578519497c5a6bdaa3177b8d6c94da91f61 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Sat, 1 Feb 2025 02:39:59 +1100 Subject: [PATCH 21/22] Add mouse driver --- kernel/CMakeLists.txt | 1 + kernel/src/arch/x86_64/cpu/idt.c | 4 +- kernel/src/arch/x86_64/cpu/idt.h | 2 +- .../arch/x86_64/drivers/keyboard/keyboard.c | 9 +- kernel/src/arch/x86_64/drivers/mouse/mouse.c | 122 ++++++++++++++++++ kernel/src/arch/x86_64/drivers/mouse/mouse.h | 3 + kernel/src/kernel_main.c | 3 + 7 files changed, 136 insertions(+), 8 deletions(-) create mode 100644 kernel/src/arch/x86_64/drivers/mouse/mouse.c create mode 100644 kernel/src/arch/x86_64/drivers/mouse/mouse.h diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 7e54f4b..c2e2cab 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -14,6 +14,7 @@ set(COMPILE_OPTIONS add_library(drivers OBJECT src/arch/x86_64/drivers/keyboard/keyboard.c + src/arch/x86_64/drivers/mouse/mouse.c src/arch/x86_64/drivers/timer/timer.c src/arch/x86_64/klib/trap.c ) diff --git a/kernel/src/arch/x86_64/cpu/idt.c b/kernel/src/arch/x86_64/cpu/idt.c index cc93867..3c6abc2 100644 --- a/kernel/src/arch/x86_64/cpu/idt.c +++ b/kernel/src/arch/x86_64/cpu/idt.c @@ -15,8 +15,8 @@ static uint16_t idt_segment_descriptor(uint16_t index) { return ((index & 0xFFF) << 3); } -void setup_interrupt_gate(uint32_t irq, void *base, enum gate_type type, uint8_t privilege_level, uint8_t ist) { - struct interrupt_descriptor *interrupt_descriptor = &interrupt_descriptors[irq]; +void setup_interrupt_gate(uint32_t vector, void *base, enum gate_type type, uint8_t privilege_level, uint8_t ist) { + struct interrupt_descriptor *interrupt_descriptor = &interrupt_descriptors[vector]; // present bit must be set for descriptor to be valid interrupt_descriptor->type_attributes |= 1 << 15; diff --git a/kernel/src/arch/x86_64/cpu/idt.h b/kernel/src/arch/x86_64/cpu/idt.h index bed760e..c2f1d9c 100644 --- a/kernel/src/arch/x86_64/cpu/idt.h +++ b/kernel/src/arch/x86_64/cpu/idt.h @@ -24,6 +24,6 @@ enum gate_type { TRAP_64_GATE = 0xF }; -void setup_interrupt_gate(uint32_t irq, void *base, enum gate_type type, uint8_t privilege_level, uint8_t ist); +void setup_interrupt_gate(uint32_t vector, void *base, enum gate_type type, uint8_t privilege_level, uint8_t ist); void load_idt(void); diff --git a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c index e2d9fab..112c99e 100644 --- a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c +++ b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c @@ -64,7 +64,7 @@ enum scan_set_one { #define MAX_BUFFER_SIZE 512 static char buffer[MAX_BUFFER_SIZE]; -static size_t buffer_pos = 0; +static volatile size_t buffer_pos = 0; static __attribute__((interrupt)) void keyboard_handler(struct interrupt_frame *frame); @@ -77,7 +77,6 @@ static bool shift_pressed = false; // TODO: use acpi to check if 8042 ps/2 controller is supported // todo: poll for keyboard status // todo: check keyboard type (1, 2, 3) -// todo: detect if key is capital or nah (need to detect caps lock and shift) void keyboard_init(void) { // Disable any devices connected to ps/2 ports @@ -138,7 +137,7 @@ static char keyboard_convert_code(char data) { // todo: WRITE A FRONTEND FUNCTION FOR KLIB char getc(void) { - while (buffer_pos == 0) { + while (!buffer_pos) { __builtin_ia32_pause(); } char data = buffer[--buffer_pos]; @@ -198,10 +197,10 @@ static void keyboard_handler(struct interrupt_frame *frame) { uint8_t data = keyboard_code_set_1(inb(DATA_PORT)); if (data >= 'A' && data <= 'Z' && !caps_lock) data += 32; data = keyboard_shift_conversion(data); - if ((data >= ' ' && data <= '~') || data == '\b' || data == '\t') buffer[buffer_pos++] = data; + if ((data >= ' ' && data <= '~') || data == '\b' || data == '\t' || data == '\n') buffer[buffer_pos++] = data; // ! minor bug: could loop over and getc wont be able to get the prev 512 chars buffer_pos %= MAX_BUFFER_SIZE; //if (data >= ' ' && data <= '~') - // krintf("Keyboard pressed %c\n", data); + // krintf("Keyboard pressed %c %d\n", data, buffer_pos); send_apic_eoi(); } diff --git a/kernel/src/arch/x86_64/drivers/mouse/mouse.c b/kernel/src/arch/x86_64/drivers/mouse/mouse.c new file mode 100644 index 0000000..6e94ef2 --- /dev/null +++ b/kernel/src/arch/x86_64/drivers/mouse/mouse.c @@ -0,0 +1,122 @@ +#include "mouse.h" + + +#include "../../../../io.h" +#include "../../apic/ioapic.h" +#include "../../apic/lapic.h" +#include "../../cpu/idt.h" +#include "../../klib/klib.h" +#include "../../serial/serial.h" + +#define DATA_PORT 0x60 +#define STATUS_REGISTER 0x64 +#define COMMAND_REGISTER STATUS_REGISTER + + +#define MOUSE_COMMAND 0xD4 +#define MOUSE_BIT 0x20 +#define GET_COMPAQ_STATUS_BYTE 0x20 + +static bool right_button = false; +static bool left_button = false; +static bool middle_button = false; +static bool x_sign = false; +static bool y_sign = false; +static uint8_t x_movement = 0; +static uint8_t y_movement = 0; + +static __attribute__((interrupt)) void mouse_handler(struct interrupt_frame *frame); +static void wait_mouse(bool is_output); + +void mouse_init(void) { + // enable mouse + wait_mouse(true); + outb(COMMAND_REGISTER, 0xA8); + + // enable aux port to generate irq 12 (according to osdev) + wait_mouse(true); + outb(COMMAND_REGISTER, GET_COMPAQ_STATUS_BYTE); + + wait_mouse(false); + uint8_t status = inb(STATUS_REGISTER); + status |= 0x10; // Enable IRQ12 + status ^= ~0x20;// Disable mouse clock + + // todo: bundle wait_mouse and outb/inb into one function + + // update status with compaq byte + wait_mouse(true); + outb(COMMAND_REGISTER, 0x60); + wait_mouse(true); + outb(DATA_PORT, status); + + // set defaults for mouse + wait_mouse(true); + outb(COMMAND_REGISTER, MOUSE_COMMAND); + wait_mouse(true); + outb(DATA_PORT, 0xF6); + + // enable packet streaming + wait_mouse(true); + outb(COMMAND_REGISTER, MOUSE_COMMAND); + wait_mouse(true); + outb(DATA_PORT, 0xF4); + + int apic_id = apic_get_id(); + ioapic_set_redirect((uintptr_t *) IOAPIC_VIRTUAL_ADDRESS, 12, 0x22, apic_id); + setup_interrupt_gate(0x22, mouse_handler, INTERRUPT_64_GATE, 0, 0); +} + + +// See: https://wiki.osdev.org/Mouse_Input#Waiting_to_Send_Bytes_to_Port_0x60_and_0x64 +static void wait_mouse(bool is_output) { + if (is_output) { + while (inb(STATUS_REGISTER) & 0x2) { + __builtin_ia32_pause(); + } + } else { + while (!(inb(DATA_PORT) & 0x1)) { + __builtin_ia32_pause(); + } + } +} + +static void mouse_handler(struct interrupt_frame *frame) { + uint8_t status = inb(STATUS_REGISTER); + puts("Mouse interrupt\n"); + size_t nth_byte = 0; + + while (status & MOUSE_BIT) { + uint8_t data = inb(DATA_PORT); + switch (nth_byte) { + case 0: + right_button = data & 0x1; + left_button = data & 0x2; + middle_button = data & 0x4; + x_sign = data & 0x10; + y_sign = data & 0x20; + break; + case 1: + x_movement = data; + break; + case 2: + y_movement = data; + break; + } + nth_byte++; + nth_byte %= 3; + krintf( + "Mouse info\n" + "right button: %d\n" + "left button: %d\n" + "middle button: %d\n" + "x sign: %d\n" + "y sign: %d\n" + "x movement: %d\n" + "y movement: %d\n", + right_button, left_button, middle_button, x_sign, y_sign, x_movement, y_movement); + status = inb(STATUS_REGISTER); + } + puts("Finished mouse interrupt\n"); + send_apic_eoi(); +} diff --git a/kernel/src/arch/x86_64/drivers/mouse/mouse.h b/kernel/src/arch/x86_64/drivers/mouse/mouse.h new file mode 100644 index 0000000..7a24b3c --- /dev/null +++ b/kernel/src/arch/x86_64/drivers/mouse/mouse.h @@ -0,0 +1,3 @@ +#pragma once + +void mouse_init(void); diff --git a/kernel/src/kernel_main.c b/kernel/src/kernel_main.c index d022440..cc84f0a 100644 --- a/kernel/src/kernel_main.c +++ b/kernel/src/kernel_main.c @@ -9,6 +9,7 @@ #include "arch/x86_64/cpu/idt.h" #include "arch/x86_64/cpu/msr.h" #include "arch/x86_64/drivers/keyboard/keyboard.h" +#include "arch/x86_64/drivers/mouse/mouse.h" #include "arch/x86_64/drivers/timer/timer.h" #include "arch/x86_64/klib/klib.h" #include "arch/x86_64/memory/paging.h" @@ -200,6 +201,8 @@ static void setup_idt(void) { // we have interrupt after 31 since 0-31 are reserved for errors setup_interrupt_gate(0x20, timer_handler, INTERRUPT_64_GATE, 0, 0); keyboard_init(); + // as of now mouse breaks keyboard and it doesnt work + mouse_init(); setup_interrupt_gate(0x80, trap, TRAP_64_GATE, 3, 0); load_idt(); From 31a145f2634f8beb0f22620a678385d9e2358102 Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Mon, 24 Feb 2025 15:23:15 +1100 Subject: [PATCH 22/22] (WIP) Refactor code for timer --- CMakeLists.txt | 2 + kernel/CMakeLists.txt | 3 +- kernel/src/arch/x86_64/acpi/acpi.c | 95 +++++++++++---- kernel/src/arch/x86_64/acpi/acpi.h | 29 +++++ kernel/src/arch/x86_64/apic/lapic.c | 22 ---- kernel/src/arch/x86_64/apic/lapic.h | 10 +- kernel/src/arch/x86_64/drivers/pci/pci.c | 76 ++++++++++++ kernel/src/arch/x86_64/drivers/pci/pci.h | 109 ++++++++++++++++++ .../src/arch/x86_64/drivers/speaker/speaker.c | 14 +++ .../src/arch/x86_64/drivers/speaker/speaker.h | 3 + kernel/src/arch/x86_64/drivers/timer/apic.c | 33 ++++++ kernel/src/arch/x86_64/drivers/timer/pit.c | 8 ++ kernel/src/arch/x86_64/drivers/timer/timer.c | 13 --- kernel/src/arch/x86_64/drivers/timer/timer.h | 10 +- kernel/src/arch/x86_64/klib/klib.c | 6 +- kernel/src/arch/x86_64/serial/serial.c | 10 ++ kernel/src/arch/x86_64/serial/serial.h | 6 + kernel/src/kernel_main.c | 16 ++- 18 files changed, 395 insertions(+), 70 deletions(-) create mode 100644 kernel/src/arch/x86_64/drivers/pci/pci.c create mode 100644 kernel/src/arch/x86_64/drivers/pci/pci.h create mode 100644 kernel/src/arch/x86_64/drivers/speaker/speaker.c create mode 100644 kernel/src/arch/x86_64/drivers/speaker/speaker.h create mode 100644 kernel/src/arch/x86_64/drivers/timer/apic.c create mode 100644 kernel/src/arch/x86_64/drivers/timer/pit.c delete mode 100644 kernel/src/arch/x86_64/drivers/timer/timer.c diff --git a/CMakeLists.txt b/CMakeLists.txt index d448ce8..5ef2837 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,8 @@ add_custom_target(run -drive file=os.img,format=raw -serial stdio -device isa-debug-exit,iobase=0xf4,iosize=0x04 + -netdev user,id=net0 + -device virtio-net-pci,netdev=net0 -d int -D qemu.log DEPENDS os) diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index c2e2cab..c45ed28 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -15,7 +15,8 @@ set(COMPILE_OPTIONS add_library(drivers OBJECT src/arch/x86_64/drivers/keyboard/keyboard.c src/arch/x86_64/drivers/mouse/mouse.c - src/arch/x86_64/drivers/timer/timer.c + src/arch/x86_64/drivers/pci/pci.c + src/arch/x86_64/drivers/timer/apic.c src/arch/x86_64/klib/trap.c ) diff --git a/kernel/src/arch/x86_64/acpi/acpi.c b/kernel/src/arch/x86_64/acpi/acpi.c index e0127dd..38b59ca 100644 --- a/kernel/src/arch/x86_64/acpi/acpi.c +++ b/kernel/src/arch/x86_64/acpi/acpi.c @@ -67,44 +67,59 @@ bool rsdt_map(void) { return verify_mapping(KERNEL_MAPPING_ADDRESS | address); } -bool rsdt_verify(void) { - struct rsdt *rsdt = - (struct rsdt *) (KERNEL_MAPPING_ADDRESS | rsdp->rsdt_address); +static bool rsdt_entry_verify(void *entry) { uint8_t sum = 0; - for (size_t i = 0; i < rsdt->length; i++) { - sum += ((uint8_t *) rsdt)[i]; + struct description_header *header = (struct description_header *) entry; + uint8_t *addr = (uint8_t *) entry; + for (size_t i = 0; i < header->length; i++) { + sum += addr[i]; } return !sum; } +bool rsdt_verify(void) { + struct rsdt *rsdt = + (struct rsdt *) (KERNEL_MAPPING_ADDRESS | rsdp->rsdt_address); + return rsdt_entry_verify(rsdt); +} + static size_t rsdt_get_entry_count(void) { struct rsdt *rsdt = (struct rsdt *) (KERNEL_MAPPING_ADDRESS | rsdp->rsdt_address); return (rsdt->length - sizeof(struct rsdt)) / sizeof(uint32_t); } -#define MADT_SIGNATURE "APIC" - -static struct madt *madt = NULL; - -bool madt_find(void) { +static void *rsdt_get_entry(char *signature) { size_t length = rsdt_get_entry_count(); struct rsdt *rsdt = (struct rsdt *) (KERNEL_MAPPING_ADDRESS | rsdp->rsdt_address); for (size_t i = 0; i < length; i++) { + // todo: pre map map_page(KERNEL_MAPPING_ADDRESS | (uint64_t) rsdt->entry[i], (uint64_t) rsdt->entry[i], PAGE_PRESENT); struct description_header *description_header = (struct description_header *) (KERNEL_MAPPING_ADDRESS | (uint64_t) rsdt->entry[i]); - if (cmp_signature(description_header->signature, MADT_SIGNATURE)) { - madt = (struct madt *) (uint64_t) rsdt->entry[i]; - return true; + if (cmp_signature(description_header->signature, signature)) { + return (void *) rsdt->entry[i]; } } - return false; + return NULL; +} + +#define MADT_SIGNATURE "APIC" + +static struct madt *madt = NULL; + +bool madt_find(void) { + madt = rsdt_get_entry(MADT_SIGNATURE); + return madt != NULL; +} + +bool madt_verify(void) { + return rsdt_entry_verify(madt); } bool madt_map(void) { @@ -114,15 +129,6 @@ bool madt_map(void) { return verify_mapping(KERNEL_MAPPING_ADDRESS | address); } -bool madt_verify(void) { - uint8_t sum = 0; - uint8_t *addr = (uint8_t *) madt; - for (size_t i = 0; i < madt->length; i++) { - sum += addr[i]; - } - return !sum; -} - uint32_t madt_get_lapic_address(void) { return madt->local_interrupt_controller_address; } @@ -131,6 +137,8 @@ static struct madt_entry **madt_entries = NULL; static size_t max_madt_entries = 0; size_t ioapic_count_entries(void) { + if (madt_entries != NULL) return max_madt_entries; + // all entries are after madt struct madt_entry *madt_entry = (struct madt_entry *) ((uint8_t *) madt + sizeof(struct madt)); @@ -156,3 +164,44 @@ size_t ioapic_count_entries(void) { struct madt_entry **get_madt_entries(void) { return madt_entries; } + +#define MCFG_SIGNATURE "MCFG" + +static struct mcfg *mcfg = NULL; + +bool mcfg_find(void) { + mcfg = rsdt_get_entry(MCFG_SIGNATURE); + return mcfg != NULL; +} + +bool mcfg_verify(void) { + return rsdt_entry_verify(mcfg); +} + +bool mcfg_map(void) { + uint64_t address = (uint64_t) mcfg; + map_page(KERNEL_MAPPING_ADDRESS | address, address, PAGE_PRESENT); + mcfg = (struct mcfg *) (KERNEL_MAPPING_ADDRESS | address); + return verify_mapping(KERNEL_MAPPING_ADDRESS | address); +} + +static struct mcfg_entry **mcfg_entries = NULL; +static size_t max_mcfg_entries = 0; + +size_t mcfg_count_entries(void) { + if (mcfg_entries != NULL) return max_madt_entries; + + struct mcfg_entry *mcfg_entry = + (struct mcfg_entry *) ((uint8_t *) mcfg + sizeof(struct mcfg)); + + uint8_t *end = (uint8_t *) mcfg + mcfg->length; + max_mcfg_entries = (end - (uint8_t *) mcfg_entry) / sizeof(struct mcfg_entry); + + mcfg_entries = kmalloc(sizeof(*mcfg_entries) * max_mcfg_entries); + + for (size_t i = 0; (uint8_t *) mcfg_entry < end; i++) { + mcfg_entries[i] = mcfg_entry; + mcfg_entry = (struct mcfg_entry *) ((uint8_t *) mcfg_entry + sizeof(struct mcfg_entry)); + } + return max_madt_entries; +} diff --git a/kernel/src/arch/x86_64/acpi/acpi.h b/kernel/src/arch/x86_64/acpi/acpi.h index b88d6b8..5cde896 100644 --- a/kernel/src/arch/x86_64/acpi/acpi.h +++ b/kernel/src/arch/x86_64/acpi/acpi.h @@ -67,6 +67,23 @@ struct madt_entry_apicio { uint32_t global_system_interrupt_base; } __attribute__((packed)); +struct mcfg_entry { + uint64_t address; + // PCI segment group number + uint16_t pci_group; + uint8_t start_PCI_bus; + uint8_t end_PCI_bus; + uint32_t reserved; +} __attribute__((packed)); + +// from here: https://wiki.osdev.org/PCI_Express +// not specified in ACPI spec (probs needs to find it in pcie spec) +struct mcfg { + DESCRIPTION_HEADER + uint8_t reserved[8]; + struct mcfg_entry data[]; +} __attribute__((packed)); + // Finds the rsdp and sets the address bool rsdp_find(void); @@ -102,3 +119,15 @@ size_t ioapic_count_entries(void); // Returns a array of addresses to madts struct madt_entry **get_madt_entries(void); + +// Finds the mcfg +bool mcfg_find(void); + +// Verifies the mcfg +bool mcfg_verify(void); + +// Maps the mcfg to a virtual memory address +bool mcfg_map(void); + +// Counts the entries of the mcfg +size_t mcfg_count_entries(void); diff --git a/kernel/src/arch/x86_64/apic/lapic.c b/kernel/src/arch/x86_64/apic/lapic.c index 0e0f780..fd1e835 100644 --- a/kernel/src/arch/x86_64/apic/lapic.c +++ b/kernel/src/arch/x86_64/apic/lapic.c @@ -2,7 +2,6 @@ #include "../cpu/cpuid.h" #include "../cpu/msr.h" -#include "../memory/paging.h" #include "../serial/serial.h" #define IA32_APIC_BASE_MSR 0x1B @@ -39,8 +38,6 @@ static uintptr_t cpu_get_apic_base(void) { // todo: read from acpi to figure out ioapicbase // todo: since its not guaranteed to be same in all systems -#define BASE_LAPIC 0xFEE00000 - void write_reg(void *lapicbase, uint32_t reg, uint32_t value) { uint32_t volatile *lapic = (uint32_t volatile *) lapicbase; lapic[reg] = value; @@ -56,8 +53,6 @@ uint32_t read_reg(void *lapicbase, uint32_t reg) { #define PIC1_DATA (PIC1 + 1) #define PIC2_DATA (PIC2 + 1) -#define LAPIC_VIRTUAL_ADDRESS (KERNEL_MAPPING_ADDRESS | BASE_LAPIC) - bool map_apic(void) { map_page(LAPIC_VIRTUAL_ADDRESS, BASE_LAPIC, PAGE_PRESENT | PAGE_WRITE | PAGE_CACHE_DISABLE); @@ -78,20 +73,3 @@ void enable_apic(void) { void send_apic_eoi(void) { *(uint32_t volatile *) (LAPIC_VIRTUAL_ADDRESS + 0xB0) = 0; } - -// Refer to Intel IA-32 Volume 3 12.5.4 APIC Timer -void init_apic_timer(uint32_t initial_count, uint8_t vector) { - // be divisble by 1 (every 10ms) (Figure 12-10) - *(uint32_t volatile *) (LAPIC_VIRTUAL_ADDRESS + 0x3E0) = 1011; - // set initial count (Figure 12-11) - *(uint32_t volatile *) (LAPIC_VIRTUAL_ADDRESS + 0x380) = initial_count; -// Refer to 12.5.1 (Timer mode and vector in Figure 12-8) -// 0 = one shot, 1 = periodic, 2 = TSC-deadline -#define APIC_TIMER_MODE 0 - *(uint32_t volatile *) (LAPIC_VIRTUAL_ADDRESS + 0x320) = - vector | (APIC_TIMER_MODE << 17); -} - -uint32_t get_apic_timer_current(void) { - return *(uint32_t volatile *) (LAPIC_VIRTUAL_ADDRESS + 0x390); -} diff --git a/kernel/src/arch/x86_64/apic/lapic.h b/kernel/src/arch/x86_64/apic/lapic.h index 4b89871..51098aa 100644 --- a/kernel/src/arch/x86_64/apic/lapic.h +++ b/kernel/src/arch/x86_64/apic/lapic.h @@ -3,6 +3,11 @@ #include #include +#include "../memory/paging.h" + +#define BASE_LAPIC 0xFEE00000 +#define LAPIC_VIRTUAL_ADDRESS (KERNEL_MAPPING_ADDRESS | BASE_LAPIC) + // Checks whether apic is supported bool apic_is_supported(void); @@ -24,8 +29,3 @@ void write_reg(void *ioapicaddr, uint32_t reg, uint32_t value); // Signal end of interrupt to apic void send_apic_eoi(void); -// Initialises the apic timer -void init_apic_timer(uint32_t initial_count, uint8_t vector); - -// Get the current count of apic timer -uint32_t get_apic_timer_current(void); diff --git a/kernel/src/arch/x86_64/drivers/pci/pci.c b/kernel/src/arch/x86_64/drivers/pci/pci.c new file mode 100644 index 0000000..ce8f24a --- /dev/null +++ b/kernel/src/arch/x86_64/drivers/pci/pci.c @@ -0,0 +1,76 @@ +#include "pci.h" + +#include + +#include "../../klib/klib.h" +#include "../../serial/serial.h" + +#include "../../../../io.h" + +#define CONFIG_ADDRESS 0xCF8 +#define CONFIG_DATA 0xCFC + +static struct common_header *devices = NULL; +static size_t i = 0; + +// todo: support PCIE once I figure out a way +// todo: to enable pcie in qemu + +// ! THIS ASSUMES WE ARE USING MECHANISM 1 +// ! MECHANISM 2 is deprecated + +// See: https://wiki.osdev.org/PCI +uint32_t pci_config_readl(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) { + uint32_t address = 0; + address |= bus << 16; + address |= slot << 11; + address |= func << 8; + address |= offset & 0xFC; + address |= 1 << 31; + outl(CONFIG_ADDRESS, address); + return inl(CONFIG_DATA); +} + +struct common_header pci_get_config(uint8_t bus, uint8_t slot, uint8_t func) { + struct common_header header = {0}; + + // abuses little endian + ((uint32_t *) &header)[offsetof(struct common_header, vendor_ID) / sizeof(uint32_t)] = + pci_config_readl(bus, slot, func, 0x0); + ((uint32_t *) &header)[offsetof(struct common_header, command) / sizeof(uint32_t)] = + pci_config_readl(bus, slot, func, 0x4); + ((uint32_t *) &header)[offsetof(struct common_header, revision_ID) / sizeof(uint32_t)] = + pci_config_readl(bus, slot, func, 0x8); + ((uint32_t *) &header)[offsetof(struct common_header, cache_line_size) / sizeof(uint32_t)] = + pci_config_readl(bus, slot, func, 0xC); + + return header; +} + +void pci_check_slot(uint8_t bus, uint8_t slot) { + // todo: expand upon this + for (size_t func = 0; func < 8; func++) { + struct common_header header = pci_get_config(bus, slot, func); + if (header.vendor_ID == 0xFFFF) return; + if ((header.header_type & 0x80) == 0) + if (func != 0) continue; + + krintf("Found device at bus %d slot %d function %d\n" + "Vendor ID: %d, Device ID: %d\n", + bus, slot, func, + header.vendor_ID, header.device_ID); + devices[i++] = header; + } +} + +void pci_scan(void) { + // bruteforce method - hopefully we dont have more than 32 devices connected + // todo: have a method to figure out how many devices are connected + devices = kmalloc(32 * sizeof(struct common_header)); + for (size_t bus = 0; bus < 256; bus++) { + for (size_t slot = 0; slot < 32; slot++) { + pci_check_slot(bus, slot); + } + } + i = 0; +} diff --git a/kernel/src/arch/x86_64/drivers/pci/pci.h b/kernel/src/arch/x86_64/drivers/pci/pci.h new file mode 100644 index 0000000..9e14042 --- /dev/null +++ b/kernel/src/arch/x86_64/drivers/pci/pci.h @@ -0,0 +1,109 @@ +#pragma once + +#include +#include + +#define COMMON_HEADER \ + uint16_t vendor_ID; \ + uint16_t device_ID; \ + uint16_t command; \ + uint16_t status; \ + uint8_t revision_ID; \ + uint8_t prog_IF; \ + uint8_t subclass; \ + uint8_t class_code; \ + uint8_t cache_line_size; \ + uint8_t latency_timer; \ + uint8_t header_type; \ + uint8_t BIST; + + +struct common_header { + COMMON_HEADER + // uint8_t data[]; +} __attribute__((packed)); + +struct device_header { + COMMON_HEADER + uint32_t base_addresses[6]; + uint32_t cardbus_cis_pointer; + uint16_t subsystem_vendor_ID; + uint16_t subsystem_device_ID; + uint32_t expansion_rom_address; + uint8_t capabilities_pointer; + uint8_t reserved0; + uint16_t reserved1; + uint32_t reserved2; + uint8_t interrupt_line; + uint8_t interrupt_pin; + uint8_t min_grant; + uint8_t max_latency; +} __attribute__((packed)); + +// pci to pci +struct pci_bridge_header { + COMMON_HEADER + uint32_t base_addresses[2]; + uint8_t primary_bus_number; + uint8_t secondary_bus_number; + uint8_t subordinate_bus_number; + uint8_t secondary_latency_timer; + uint8_t io_base; + uint8_t io_limit; + uint8_t secondary_status; + uint16_t memory_base; + uint16_t memory_limit; + uint16_t prefetchable_memory_base; + uint16_t prefetchable_memory_limit; + uint32_t prefetchable_memory_base_upper; + uint32_t prefetchable_memory_limit_upper; + uint16_t io_base_upper; + uint16_t io_limit_upper; + uint8_t capabilities_pointer; + uint8_t reserved0; + uint16_t reserved1; + uint32_t expansion_ROM_base_address; + uint8_t interrupt_line; + uint8_t interrupt_pin; + uint16_t bridge_control; +} __attribute__((packed)); + +// pci to cardbus +struct cardbus_bridge_header { + COMMON_HEADER + uint32_t base_address; + uint8_t offset_capabilities_list; + uint8_t reserved0; + uint16_t secondary_status; + uint8_t pci_bus_number; + uint8_t cardbus_bus_number; + uint8_t subordinate_bus_number; + uint8_t cardbus_latency_timer; + uint32_t memory_base_address0; + uint32_t memory_limit0; + uint32_t memory_base_address1; + uint32_t memory_limit1; + uint32_t io_base_address0; + uint32_t io_limit0; + uint32_t io_base_address1; + uint32_t io_limit1; + uint8_t interrupt_line; + uint8_t interrupt_pin; + uint16_t bridge_control; + uint16_t subsystem_device_ID; + uint16_t subsystem_vendor_ID; + // 16 bit pc card + uint32_t legacy_mode_base_address; +} __attribute__((packed)); + +uint32_t pci_config_readl(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset); + +void pci_scan(void); + +struct common_header pci_get_config(uint8_t bus, uint8_t slot, uint8_t func); + +void pci_check_slot(uint8_t bus, uint8_t slot); + +void pci_init(void); + +bool pci_msi_supported(void); diff --git a/kernel/src/arch/x86_64/drivers/speaker/speaker.c b/kernel/src/arch/x86_64/drivers/speaker/speaker.c new file mode 100644 index 0000000..30b994f --- /dev/null +++ b/kernel/src/arch/x86_64/drivers/speaker/speaker.c @@ -0,0 +1,14 @@ +#include + +#include "../../serial/serial.h" + +// frequency of PIT oscillator +#define FREQUENCY_BASE 1193180 + +#define SPEAKER_PORT 0x61 + + +static void speaker_play_sound(uint32_t frequency) { + uint32_t div = FREQUENCY_BASE / frequency; + +} diff --git a/kernel/src/arch/x86_64/drivers/speaker/speaker.h b/kernel/src/arch/x86_64/drivers/speaker/speaker.h new file mode 100644 index 0000000..20bf192 --- /dev/null +++ b/kernel/src/arch/x86_64/drivers/speaker/speaker.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/kernel/src/arch/x86_64/drivers/timer/apic.c b/kernel/src/arch/x86_64/drivers/timer/apic.c new file mode 100644 index 0000000..4359c7c --- /dev/null +++ b/kernel/src/arch/x86_64/drivers/timer/apic.c @@ -0,0 +1,33 @@ +#include "timer.h" + +#include "../../apic/lapic.h" + +static uint32_t timer_ticks = 0; + +// Refer to Intel IA-32 Volume 3 12.5.4 APIC Timer +void apic_timer_init(uint32_t initial_count, uint8_t vector) { + // be divisble by 1 (every 10ms) (Figure 12-10) + *(uint32_t volatile *) (LAPIC_VIRTUAL_ADDRESS + 0x3E0) = 1011; + // set initial count (Figure 12-11) + *(uint32_t volatile *) (LAPIC_VIRTUAL_ADDRESS + 0x380) = initial_count; +// Refer to 12.5.1 (Timer mode and vector in Figure 12-8) +// 0 = one shot, 1 = periodic, 2 = TSC-deadline +#define APIC_TIMER_MODE 0 + *(uint32_t volatile *) (LAPIC_VIRTUAL_ADDRESS + 0x320) = + vector | (APIC_TIMER_MODE << 17); +} + +// returns time left from countdown iirc +uint32_t apic_timer_get_current(void) { + return *(uint32_t volatile *) (LAPIC_VIRTUAL_ADDRESS + 0x390); +} + + +uint32_t apic_timer_get_ticks(void) { + return timer_ticks; +} + +__attribute__((interrupt)) void apic_timer_handler(struct interrupt_frame *frame) { + timer_ticks++; + send_apic_eoi(); +} diff --git a/kernel/src/arch/x86_64/drivers/timer/pit.c b/kernel/src/arch/x86_64/drivers/timer/pit.c new file mode 100644 index 0000000..1f0680d --- /dev/null +++ b/kernel/src/arch/x86_64/drivers/timer/pit.c @@ -0,0 +1,8 @@ +#include "timer.h" + +#include "../../apic/ioapic.h" + +__attribute__((interrupt)) void pit_handler(struct interrupt_frame *frame) { + + send_apic_eoi(); +} diff --git a/kernel/src/arch/x86_64/drivers/timer/timer.c b/kernel/src/arch/x86_64/drivers/timer/timer.c deleted file mode 100644 index 1b4bc31..0000000 --- a/kernel/src/arch/x86_64/drivers/timer/timer.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "timer.h" - -#include "../../apic/lapic.h" - -static int timer_ticks = 0; - -uint32_t get_timer_ticks(void) { - return timer_ticks; -} - -__attribute__((interrupt)) void timer_handler(struct interrupt_frame *frame) { - send_apic_eoi(); -} diff --git a/kernel/src/arch/x86_64/drivers/timer/timer.h b/kernel/src/arch/x86_64/drivers/timer/timer.h index ece4711..c22a780 100644 --- a/kernel/src/arch/x86_64/drivers/timer/timer.h +++ b/kernel/src/arch/x86_64/drivers/timer/timer.h @@ -3,9 +3,15 @@ #include // Returns the timer -uint32_t get_timer_ticks(void); +uint32_t apic_timer_get_ticks(void); struct interrupt_frame; // Timer handler -void timer_handler(struct interrupt_frame *frame); +void apic_timer_handler(struct interrupt_frame *frame); + +// Initialises the apic timer +void apic_timer_init(uint32_t initial_count, uint8_t vector); + +// Get the current count of apic timer +uint32_t apic_timer_get_current(void); diff --git a/kernel/src/arch/x86_64/klib/klib.c b/kernel/src/arch/x86_64/klib/klib.c index d144e9a..97c4342 100644 --- a/kernel/src/arch/x86_64/klib/klib.c +++ b/kernel/src/arch/x86_64/klib/klib.c @@ -1,10 +1,10 @@ #include "klib.h" -#include "../apic/lapic.h" +#include "../drivers/timer/timer.h" void ksleep(int ticks) { - init_apic_timer(ticks, 0x20); - while (get_apic_timer_current()); + apic_timer_init(ticks, 0x20); + while (apic_timer_get_current()); } _Noreturn void _die(void) { diff --git a/kernel/src/arch/x86_64/serial/serial.c b/kernel/src/arch/x86_64/serial/serial.c index d06ca72..083518a 100644 --- a/kernel/src/arch/x86_64/serial/serial.c +++ b/kernel/src/arch/x86_64/serial/serial.c @@ -8,8 +8,18 @@ void outw(uint16_t port, uint16_t value) { asm volatile("outw %w0, %w1" : : "a"(value), "Nd"(port) : "memory"); } +void outl(uint16_t port, uint32_t value) { + asm volatile("outl %d0, %d1" : : "a"(value), "Nd"(port) : "memory"); +} + uint8_t inb(uint16_t port) { uint8_t value; asm volatile("inb %w1, %b0" : "=a"(value) : "Nd"(port) : "memory"); return value; } + +uint32_t inl(uint16_t port) { + uint32_t value; + asm volatile("inl %d1, %d0" : "=a"(value) : "Nd"(port) : "memory"); + return value; +} diff --git a/kernel/src/arch/x86_64/serial/serial.h b/kernel/src/arch/x86_64/serial/serial.h index 1b612a4..65209d5 100644 --- a/kernel/src/arch/x86_64/serial/serial.h +++ b/kernel/src/arch/x86_64/serial/serial.h @@ -8,5 +8,11 @@ void outb(uint16_t port, uint8_t value); // Writes a word to the serial port void outw(uint16_t port, uint16_t value); +// Writes a long to the serial port +void outl(uint16_t port, uint32_t value); + // Reads a byte from the serial port uint8_t inb(uint16_t port); + +// Reads a long from the serial port +uint32_t inl(uint16_t port); diff --git a/kernel/src/kernel_main.c b/kernel/src/kernel_main.c index cc84f0a..cc90fd0 100644 --- a/kernel/src/kernel_main.c +++ b/kernel/src/kernel_main.c @@ -10,6 +10,7 @@ #include "arch/x86_64/cpu/msr.h" #include "arch/x86_64/drivers/keyboard/keyboard.h" #include "arch/x86_64/drivers/mouse/mouse.h" +#include "arch/x86_64/drivers/pci/pci.h" #include "arch/x86_64/drivers/timer/timer.h" #include "arch/x86_64/klib/klib.h" #include "arch/x86_64/memory/paging.h" @@ -107,6 +108,9 @@ void _Noreturn kernel_main(struct boot_parameters parameters) { krintf("String: %s\n", str); free(str); + pci_scan(); + puts("Finished scanning for PCI devices\n"); + getc(); char message[] = "X Hello world!\n"; @@ -117,7 +121,7 @@ void _Noreturn kernel_main(struct boot_parameters parameters) { // ! DONT USE FLOATS - THEY DONT WORK FOR SOME REASON krintf("%sThe number is: %d, ticks: %d, entry count: %d\n", message, 5, - get_timer_ticks(), parameters.address_range_count); + apic_timer_get_ticks(), parameters.address_range_count); vga_set_color(1 + (i % 6), VGA_COLOR_BLACK); //memmap_print_entries(parameters.address_range_count, @@ -184,6 +188,16 @@ static void read_acpi(void) { krintf("APICIO address: %x\n", apicio_entry->ioapic_address); } } + + // Currently I don't know how to get pcie + // working with the qemu so we are falling back to pci + /* + if (!mcfg_find()) { + puts("Could not find mcfg\n"); + _die(); + } + puts("Found mcfg\n"); + */ } static void setup_idt(void) {