From 295c11e107450d5649d9e23adf8e232279601030 Mon Sep 17 00:00:00 2001 From: Rick Rongen Date: Sun, 1 Oct 2023 19:49:56 +0200 Subject: [PATCH] feat: setup gdt and idt and handle basic ISRs --- run-qemu.sh | 7 +- yak-kernel/CMakeLists.txt | 19 ++- .../include/yak/platform/x86_64/cpu/cpu.h | 12 ++ .../include/yak/platform/x86_64/cpu/gdt.h | 60 +++++++ .../include/yak/platform/x86_64/cpu/idt.h | 25 +++ yak-kernel/src/platform/x86_64/cpu/gdt.asm | 24 +++ yak-kernel/src/platform/x86_64/cpu/gdt.c | 92 +++++++++++ yak-kernel/src/platform/x86_64/cpu/idt.asm | 156 ++++++++++++++++++ yak-kernel/src/platform/x86_64/cpu/idt.c | 94 +++++++++++ .../platform/x86_64/cpu/idt_stub.generator.py | 21 +++ yak-kernel/src/platform/x86_64/platform.c | 4 + 11 files changed, 511 insertions(+), 3 deletions(-) create mode 100644 yak-kernel/include/yak/platform/x86_64/cpu/cpu.h create mode 100644 yak-kernel/include/yak/platform/x86_64/cpu/gdt.h create mode 100644 yak-kernel/include/yak/platform/x86_64/cpu/idt.h create mode 100644 yak-kernel/src/platform/x86_64/cpu/gdt.asm create mode 100644 yak-kernel/src/platform/x86_64/cpu/gdt.c create mode 100644 yak-kernel/src/platform/x86_64/cpu/idt.asm create mode 100644 yak-kernel/src/platform/x86_64/cpu/idt.c create mode 100644 yak-kernel/src/platform/x86_64/cpu/idt_stub.generator.py diff --git a/run-qemu.sh b/run-qemu.sh index 4bab7ec..3d78b2a 100755 --- a/run-qemu.sh +++ b/run-qemu.sh @@ -2,4 +2,9 @@ ISO=${1} -qemu-system-x86_64 -bios /usr/share/ovmf/x64/OVMF.fd -cdrom "${ISO}" -S -s -d guest_errors \ No newline at end of file +qemu-system-x86_64 \ + -cpu qemu64 \ + -m 8G \ + -drive if=pflash,format=raw,readonly=on,file=/usr/share/ovmf/x64/OVMF_CODE.fd \ + -cdrom "${ISO}" \ + -S -s -d guest_errors,pcall,cpu_reset \ No newline at end of file diff --git a/yak-kernel/CMakeLists.txt b/yak-kernel/CMakeLists.txt index b33b3cd..b27d676 100644 --- a/yak-kernel/CMakeLists.txt +++ b/yak-kernel/CMakeLists.txt @@ -75,12 +75,26 @@ if (NOT IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/${CMAKE_SYSTEM_PRO message(FATAL_ERROR "Unknown architecture ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/${CMAKE_SYSTEM_PROCESSOR}") endif () -file(GLOB PLATFORM_SPECIFIC_SOURCES src/platform/${CMAKE_SYSTEM_PROCESSOR}/*.c) -file(GLOB PLATFORM_SPECIFIC_ASM_SOURCES src/platform/${CMAKE_SYSTEM_PROCESSOR}/*.asm) +file(GLOB_RECURSE PLATFORM_SPECIFIC_SOURCES src/platform/${CMAKE_SYSTEM_PROCESSOR}/*.c) +file(GLOB_RECURSE PLATFORM_SPECIFIC_ASM_SOURCES src/platform/${CMAKE_SYSTEM_PROCESSOR}/*.asm) file(GLOB CRTI src/platform/generic/crt/crti.c) file(GLOB CRTN src/platform/generic/crt/crtn.c) +file(GLOB_RECURSE PYTHON_GENERATORS src/**/*.generator.py) + + +foreach (GENERATOR IN LISTS PYTHON_GENERATORS) + get_filename_component(GENERATED_BARE ${GENERATOR} NAME_WE) + add_custom_command( + COMMAND python3 ${GENERATOR} + OUTPUT ${GENERATED_BARE}.c + COMMENT "Generates ${GENERATED_BARE}.c" + ) + list(APPEND GENERATED_SOURCES ${GENERATED_BARE}.c) +endforeach () + + # Define aggregate sources set(KERNEL_SOURCE_FILES ${KERNEL_SOURCES} @@ -89,6 +103,7 @@ set(KERNEL_SOURCE_FILES ${PLATFORM_GENERIC_SOURCES} ${PLATFORM_SPECIFIC_SOURCES} ${PLATFORM_SPECIFIC_ASM_SOURCES} + ${GENERATED_SOURCES} ) # Define executable diff --git a/yak-kernel/include/yak/platform/x86_64/cpu/cpu.h b/yak-kernel/include/yak/platform/x86_64/cpu/cpu.h new file mode 100644 index 0000000..863d238 --- /dev/null +++ b/yak-kernel/include/yak/platform/x86_64/cpu/cpu.h @@ -0,0 +1,12 @@ +// +// Created by rick on 30-9-23. +// + +#ifndef YAK_CPU_H +#define YAK_CPU_H + +void gdt_init(); + +void idt_init(); + +#endif //YAK_CPU_H diff --git a/yak-kernel/include/yak/platform/x86_64/cpu/gdt.h b/yak-kernel/include/yak/platform/x86_64/cpu/gdt.h new file mode 100644 index 0000000..82eebfc --- /dev/null +++ b/yak-kernel/include/yak/platform/x86_64/cpu/gdt.h @@ -0,0 +1,60 @@ +// +// Created by rick on 22-3-23. +// + +#ifndef YAK_GDT_H +#define YAK_GDT_H + +#include + +typedef struct { + uint64_t base; + uint32_t limit; + uint8_t access; + uint8_t flags; +} gdt_segment_c_t; + +typedef struct gdt_segment { + uint16_t limit1; + uint16_t base1; + uint8_t base2; + uint8_t access_byte; + uint8_t limit2: 4; + uint8_t flags: 4; + uint8_t base3; + uint32_t base4; + uint32_t: 32; // reserved +} __attribute__((packed)) gdt_segment_t; +_Static_assert(sizeof(gdt_segment_t) == 16, "GDT must be 128 bits/16 bytes"); + +typedef struct gdtr { + uint16_t limit; + uint64_t pointer; +} __attribute__((packed)) gdtr_t; + +#define ACCESS_BYTE_PRESENT (0b1<<7) +#define ACCESS_BYTE_DPL_0 (0b00 << 5) +#define ACCESS_BYTE_DPL_1 (0b01 << 5) +#define ACCESS_BYTE_DPL_2 (0b10 << 5) +#define ACCESS_BYTE_DPL_3 (0b11 << 5) +#define ACCESS_BYTE_CODE_DATA (0b1 << 4) +#define ACCESS_BYTE_EXECUTABLE (0b1 << 3) +#define ACCESS_BYTE_DC (0b1 << 2) +#define ACCESS_BYTE_RW (0b1 << 1) +#define ACCESS_BYTE_ACCESSED (0b1 << 0) + +#define GDT_FLAG_GRANULARITY (1 << 3) +#define GDT_FLAG_SIZE_32 (1 << 2) +#define GDT_FLAG_LONG (1 << 1) +#define GDT_FLAG_RESERVED (1 << 0) + +#define GDT_OFFSET_NULL 0x00 +#define GDT_OFFSET_KERNEL_CODE 0x10 +#define GDT_OFFSET_KERNEL_DATA 0x20 +#define GDT_OFFSET_USER_32_CODE 0x30 +#define GDT_OFFSET_USER_32_DATA 0x40 +#define GDT_OFFSET_USER_64_CODE 0x50 +#define GDT_OFFSET_USER_64_DATA 0x60 +#define GDT_OFFSET_TSS 0x38 + +#endif //YAK_GDT_H diff --git a/yak-kernel/include/yak/platform/x86_64/cpu/idt.h b/yak-kernel/include/yak/platform/x86_64/cpu/idt.h new file mode 100644 index 0000000..3c06419 --- /dev/null +++ b/yak-kernel/include/yak/platform/x86_64/cpu/idt.h @@ -0,0 +1,25 @@ +#include + +#define IDT_MAX_DESCRIPTORS 256 + +typedef struct { + uint16_t isr_low; // The lower 16 bits of the ISR's address + uint16_t kernel_cs; // The GDT segment selector that the CPU will load into CS before calling the ISR + uint8_t ist; // The IST in the TSS that the CPU will load into RSP; set to zero for now + uint8_t attributes; // Type and attributes; see the IDT page + uint16_t isr_mid; // The higher 16 bits of the lower 32 bits of the ISR's address + uint32_t isr_high; // The higher 32 bits of the ISR's address + uint32_t reserved; // Set to zero +} __attribute__((packed)) idt_entry_t; + +typedef struct { + uint16_t limit; + uint64_t base; +} __attribute((packed)) idtr_t; + +typedef struct { + uint64_t ds; // pushed in common handler + uint64_t r15, r14, r13, r12, r11, r10, r9, r8, rdi, rsi, rbx, rdx, rcx, rax; // pusha/popa + uint64_t int_no, err_code; // pushed by handlers + uint64_t rip, cs, rflags, userrsp, ss; // isr/iret +} isr_registers_t; diff --git a/yak-kernel/src/platform/x86_64/cpu/gdt.asm b/yak-kernel/src/platform/x86_64/cpu/gdt.asm new file mode 100644 index 0000000..7552f5e --- /dev/null +++ b/yak-kernel/src/platform/x86_64/cpu/gdt.asm @@ -0,0 +1,24 @@ +.code64 + +.global gdt_reload_segments +gdt_reload_segments: + pushq %rbp + movq %rsp, %rbp + + # Reload data segment registers + movw $0x20,%ax # 0x10 is a stand-in for your data segment + movw %ax,%ds + movw %ax,%es + movw %ax,%fs + movw %ax,%gs + movw %ax,%ss + + # Reload CS register: + push $0x10 # Push code segment to stack, 0x08 is a stand-in for your code segment + leaq .reload_CS, %rax + pushq %rax + lretq +.reload_CS: + + popq %rbp + ret diff --git a/yak-kernel/src/platform/x86_64/cpu/gdt.c b/yak-kernel/src/platform/x86_64/cpu/gdt.c new file mode 100644 index 0000000..e6cace9 --- /dev/null +++ b/yak-kernel/src/platform/x86_64/cpu/gdt.c @@ -0,0 +1,92 @@ +// +// Created by rick on 22-3-23. +// +#include +#include + +#define NO_GDT_ENTRIES 8 + +gdt_segment_t gdt_table[NO_GDT_ENTRIES] = { + {0}, // 0x0 + { // 0x8 + .base1 = 0, + .base2 = 0, + .base3 = 0, + .base4 = 0, + .limit1 = 0x0, + .limit2 = 0x0, + .flags = GDT_FLAG_LONG, + .access_byte = ACCESS_BYTE_PRESENT | ACCESS_BYTE_DPL_0 | ACCESS_BYTE_CODE_DATA | + ACCESS_BYTE_EXECUTABLE | ACCESS_BYTE_RW | ACCESS_BYTE_ACCESSED, + }, + { // 0x10 + .base1 = 0, + .base2 = 0, + .base3 = 0, + .base4 = 0, + .limit1 = 0x0, + .limit2 = 0x0000, + .flags = 0, + .access_byte = ACCESS_BYTE_PRESENT | ACCESS_BYTE_DPL_0 | ACCESS_BYTE_CODE_DATA | ACCESS_BYTE_RW | + ACCESS_BYTE_ACCESSED, + }, + { // 0x18 + .base1 = 0, + .base2 = 0, + .base3 = 0, + .base4 = 0, + .limit1 = 0xFFFF, + .limit2 = 0xF, + .flags = GDT_FLAG_SIZE_32 | GDT_FLAG_GRANULARITY, + .access_byte = ACCESS_BYTE_PRESENT | ACCESS_BYTE_DPL_0 | ACCESS_BYTE_CODE_DATA | + ACCESS_BYTE_EXECUTABLE | ACCESS_BYTE_RW | ACCESS_BYTE_ACCESSED, + }, + { // 0x20 + .base1 = 0, + .base2 = 0, + .base3 = 0, + .base4 = 0, + .limit1 = 0xFFFF, + .limit2 = 0xF, + .flags = GDT_FLAG_SIZE_32 | GDT_FLAG_GRANULARITY, + .access_byte = ACCESS_BYTE_PRESENT | ACCESS_BYTE_DPL_0 | ACCESS_BYTE_CODE_DATA | ACCESS_BYTE_RW | + ACCESS_BYTE_ACCESSED, + }, + { // 0x28 + .base1 = 0, + .base2 = 0, + .base3 = 0, + .base4 = 0, + .limit1 = 0x0, + .limit2 = 0x0, + .flags = GDT_FLAG_LONG, + .access_byte = ACCESS_BYTE_PRESENT | ACCESS_BYTE_DPL_0 | ACCESS_BYTE_CODE_DATA | + ACCESS_BYTE_EXECUTABLE | ACCESS_BYTE_RW | ACCESS_BYTE_ACCESSED, + }, + { // 0x30 + .base1 = 0, + .base2 = 0, + .base3 = 0, + .base4 = 0, + .limit1 = 0x0, + .limit2 = 0x0, + .flags = GDT_FLAG_LONG, + .access_byte = ACCESS_BYTE_PRESENT | ACCESS_BYTE_DPL_0 | ACCESS_BYTE_CODE_DATA | ACCESS_BYTE_RW | + ACCESS_BYTE_ACCESSED, + }, + {0} + // todo tss +}; + +gdtr_t gdtr = { + .limit = sizeof(gdt_table) - 1, + .pointer = (uintptr_t) gdt_table, +}; + +extern void gdt_reload_segments(); + +void gdt_init() { + __asm__ volatile ("lgdt %0" : : "m"(gdtr)); + gdt_reload_segments(); + // todo map virtual address to physical +} \ No newline at end of file diff --git a/yak-kernel/src/platform/x86_64/cpu/idt.asm b/yak-kernel/src/platform/x86_64/cpu/idt.asm new file mode 100644 index 0000000..6cd2faf --- /dev/null +++ b/yak-kernel/src/platform/x86_64/cpu/idt.asm @@ -0,0 +1,156 @@ +.code64 + +.extern isr_handler + +# TODO SSE(2), AVX, FP, ... +.macro save_registers_stack + pushq %rax + pushq %rbx + pushq %rcx + pushq %rdx + pushq %rsi + pushq %rdi + # skip RSP and RBP + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 +.endm + +.macro restore_registers_stack + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %r11 + popq %r10 + popq %r9 + popq %r8 + # skip RSP and RBP + popq %rdi + popq %rsi + popq %rdx + popq %rcx + popq %rbx + popq %rax +.endm + +.macro isr_common_stub + save_registers_stack + + # Push DS + xor %rax, %rax + movw %ds, %ax + pushq %rax + # switch to kernel DS + movw $0x20,%ax + movw %ax,%ds + movw %ax,%es + movw %ax,%fs + movw %ax,%gs + + call isr_handler + + popq %rax + movw %ax,%ds + movw %ax,%es + movw %ax,%fs + movw %ax,%gs + + restore_registers_stack + + # Removes Error Code and ISR number + addq $8,%rsp + + sti + iretq +.endm + +.macro isr_err_stub isr_num +.global isr_stub_\isr_num +isr_stub_\isr_num: + cli + push $\isr_num + isr_common_stub +.endm +.macro isr_no_err_stub isr_num +.global isr_stub_\isr_num +isr_stub_\isr_num: + cli + pushq $0 + push $\isr_num + isr_common_stub +.endm + +isr_no_err_stub 0 +isr_no_err_stub 1 +isr_no_err_stub 2 +isr_no_err_stub 3 +isr_no_err_stub 4 +isr_no_err_stub 5 +isr_no_err_stub 6 +isr_no_err_stub 7 +isr_err_stub 8 +isr_no_err_stub 9 +isr_err_stub 10 +isr_err_stub 11 +isr_err_stub 12 +isr_err_stub 13 +isr_err_stub 14 +isr_no_err_stub 15 +isr_no_err_stub 16 +isr_err_stub 17 +isr_no_err_stub 18 +isr_no_err_stub 19 +isr_no_err_stub 20 +isr_err_stub 21 +isr_no_err_stub 22 +isr_no_err_stub 23 +isr_no_err_stub 24 +isr_no_err_stub 25 +isr_no_err_stub 26 +isr_no_err_stub 27 +isr_no_err_stub 28 +isr_err_stub 29 +isr_err_stub 30 +isr_no_err_stub 31 + +# python3 -c "print('\n'.join(f'.quad isr_stub_{i}' for i in range(32)))" +.global isr_stub_table +isr_stub_table: +.quad isr_stub_0 +.quad isr_stub_1 +.quad isr_stub_2 +.quad isr_stub_3 +.quad isr_stub_4 +.quad isr_stub_5 +.quad isr_stub_6 +.quad isr_stub_7 +.quad isr_stub_8 +.quad isr_stub_9 +.quad isr_stub_10 +.quad isr_stub_11 +.quad isr_stub_12 +.quad isr_stub_13 +.quad isr_stub_14 +.quad isr_stub_15 +.quad isr_stub_16 +.quad isr_stub_17 +.quad isr_stub_18 +.quad isr_stub_19 +.quad isr_stub_20 +.quad isr_stub_21 +.quad isr_stub_22 +.quad isr_stub_23 +.quad isr_stub_24 +.quad isr_stub_25 +.quad isr_stub_26 +.quad isr_stub_27 +.quad isr_stub_28 +.quad isr_stub_29 +.quad isr_stub_30 +.quad isr_stub_31 \ No newline at end of file diff --git a/yak-kernel/src/platform/x86_64/cpu/idt.c b/yak-kernel/src/platform/x86_64/cpu/idt.c new file mode 100644 index 0000000..31c9035 --- /dev/null +++ b/yak-kernel/src/platform/x86_64/cpu/idt.c @@ -0,0 +1,94 @@ +// +// Created by rick on 29-9-23. +// + +#include +#include +#include + +#include + +#include +#include +#include + +#define NO_ISR 256 + + +extern void* isr_entries[]; + +__attribute((aligned(0x10))) static idt_entry_t idt[NO_ISR]; +static idtr_t idtr; +void *isr_vectors[NO_ISR] = {0}; + +char *exception_messages[] = { + "Division By Zero", + "Debug", + "Non Maskable Interrupt", + "Breakpoint", + "Into Detected Overflow", + "Out of Bounds", + "Invalid Opcode", + "No Coprocessor", + "Double Fault", + "Coprocessor Segment Overrun", + "Bad TSS", + "Segment Not Present", + "Stack Fault", + "General Protection Fault", + "Page Fault", + "Reserved", + "Coprocessor Fault", + "Alignment Check", + "Machine Check", + "SIMD Floating Point Exception", + "Virtualization Exception", + "Control Protection Exception", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Hypervisor Injection Exception", + "VMM Communication Exception", + "Security Exception", + "Reserved", +}; + +__attribute__((unused)) void isr_handler(isr_registers_t r) { + if (r.int_no <= 31) { + char buffer[256]; + snprintf(buffer, 256, "ISR Received: %s af 0x%16lx\n", exception_messages[r.int_no], r.rip); + panic(buffer); + } + panic("ISR Received"); +} + +void idt_set_descriptor(uint8_t vector, void *isr, uint8_t flags) { + idt_entry_t *descriptor = &idt[vector]; + + descriptor->isr_low = (uint64_t) isr & 0xFFFF; + descriptor->kernel_cs = GDT_OFFSET_KERNEL_CODE; + descriptor->ist = 0; + descriptor->attributes = flags; + descriptor->isr_mid = ((uint64_t) isr >> 16) & 0xFFFF; + descriptor->isr_high = ((uint64_t) isr >> 32) & 0xFFFFFFFF; + descriptor->reserved = 0; +} + +void idt_init() { + idtr.base = (uint64_t) idt; + idtr.limit = sizeof(idt_entry_t) * IDT_MAX_DESCRIPTORS - 1; + + memset(idt, 0, sizeof(idt_entry_t) * NO_ISR); + + // register + for (int i = 0; i < 32; ++i) { + idt_set_descriptor(i, isr_entries[i], 0x8E); + isr_vectors[i] = NULL; + } + + __asm__ volatile ("lidt %0" : : "m"(idtr)); + __asm__ volatile ("sti"); +} \ No newline at end of file diff --git a/yak-kernel/src/platform/x86_64/cpu/idt_stub.generator.py b/yak-kernel/src/platform/x86_64/cpu/idt_stub.generator.py new file mode 100644 index 0000000..a8109d3 --- /dev/null +++ b/yak-kernel/src/platform/x86_64/cpu/idt_stub.generator.py @@ -0,0 +1,21 @@ + +NO_ISRS = 32 + + +def main(): + with open('idt_stub.c', 'w') as f: + for stub in range(NO_ISRS): + f.write(f"extern void isr_stub_{stub}();\n") + + f.write("\n") + f.write("void* isr_entries[] = {\n") + + for stub in range(NO_ISRS): + f.write(f" isr_stub_{stub},\n") + + f.write("};\n") + pass + + +if __name__ == '__main__': + main() diff --git a/yak-kernel/src/platform/x86_64/platform.c b/yak-kernel/src/platform/x86_64/platform.c index 565514b..015bab2 100644 --- a/yak-kernel/src/platform/x86_64/platform.c +++ b/yak-kernel/src/platform/x86_64/platform.c @@ -3,9 +3,13 @@ // #include #include +#include void platform_init() { printf("Init X86_64\n"); + __asm__("cli"); + gdt_init(); + idt_init(); } void __attribute__((noreturn)) halt_forever() {