From 1b964278762dde86b0b737bd9a34fec569339f54 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 16 Apr 2026 10:29:30 +0200 Subject: x86_64: use p1204 project layout --- arch/x86_64/CMakeLists.txt | 50 ++- arch/x86_64/arch/boot/boot.hpp | 56 +++ arch/x86_64/arch/boot/boot32.S | 443 +++++++++++++++++++++ arch/x86_64/arch/boot/entry64.s | 62 +++ arch/x86_64/arch/boot/initialize_runtime.cpp | 22 + arch/x86_64/arch/boot/ld.hpp | 61 +++ arch/x86_64/arch/boot/multiboot.s | 33 ++ arch/x86_64/arch/bus/isa.cpp | 14 + arch/x86_64/arch/bus/isa.hpp | 18 + arch/x86_64/arch/cpu/control_register.hpp | 249 ++++++++++++ arch/x86_64/arch/cpu/global_descriptor_table.hpp | 91 +++++ arch/x86_64/arch/cpu/initialization.cpp | 164 ++++++++ arch/x86_64/arch/cpu/initialization.hpp | 12 + arch/x86_64/arch/cpu/interrupts.S | 112 ++++++ arch/x86_64/arch/cpu/interrupts.cpp | 203 ++++++++++ arch/x86_64/arch/cpu/interrupts.hpp | 116 ++++++ arch/x86_64/arch/cpu/legacy_pic.hpp | 19 + arch/x86_64/arch/cpu/model_specific_register.hpp | 151 +++++++ arch/x86_64/arch/cpu/registers.hpp | 32 ++ arch/x86_64/arch/cpu/segment_descriptor.hpp | 61 +++ arch/x86_64/arch/cpu/segment_selector.hpp | 21 + arch/x86_64/arch/cpu/task_state_segment.hpp | 30 ++ arch/x86_64/arch/debug/qemu_output.cpp | 25 ++ arch/x86_64/arch/debug/qemu_output.hpp | 43 ++ arch/x86_64/arch/device_io/port_io.hpp | 112 ++++++ arch/x86_64/arch/devices/init.cpp | 76 ++++ arch/x86_64/arch/devices/init.hpp | 12 + arch/x86_64/arch/devices/legacy_pit.cpp | 60 +++ arch/x86_64/arch/devices/legacy_pit.hpp | 29 ++ arch/x86_64/arch/devices/local_apic.cpp | 134 +++++++ arch/x86_64/arch/devices/local_apic.hpp | 37 ++ arch/x86_64/arch/memory/higher_half_mapper.cpp | 119 ++++++ arch/x86_64/arch/memory/higher_half_mapper.hpp | 44 ++ arch/x86_64/arch/memory/kernel_mapper.cpp | 117 ++++++ arch/x86_64/arch/memory/kernel_mapper.hpp | 35 ++ arch/x86_64/arch/memory/mmu.cpp | 19 + arch/x86_64/arch/memory/mmu.hpp | 27 ++ arch/x86_64/arch/memory/page_table.cpp | 82 ++++ arch/x86_64/arch/memory/page_table.hpp | 232 +++++++++++ arch/x86_64/arch/memory/page_utilities.hpp | 29 ++ arch/x86_64/arch/memory/region_allocator.cpp | 165 ++++++++ arch/x86_64/arch/memory/region_allocator.hpp | 93 +++++ arch/x86_64/arch/vga/crtc.hpp | 35 ++ arch/x86_64/arch/vga/text.hpp | 10 + arch/x86_64/arch/vga/text/attribute.hpp | 30 ++ arch/x86_64/arch/vga/text/buffer.cpp | 101 +++++ arch/x86_64/arch/vga/text/buffer.hpp | 101 +++++ arch/x86_64/arch/vga/text/color.hpp | 35 ++ arch/x86_64/arch/vga/text/common_attributes.hpp | 37 ++ arch/x86_64/arch/vga/text/device.cpp | 60 +++ arch/x86_64/arch/vga/text/device.hpp | 45 +++ arch/x86_64/arch/vga/text/flags.hpp | 38 ++ arch/x86_64/include/arch/boot/boot.hpp | 56 --- arch/x86_64/include/arch/boot/ld.hpp | 61 --- arch/x86_64/include/arch/bus/isa.hpp | 18 - arch/x86_64/include/arch/cpu/control_register.hpp | 249 ------------ .../include/arch/cpu/global_descriptor_table.hpp | 91 ----- arch/x86_64/include/arch/cpu/initialization.hpp | 12 - arch/x86_64/include/arch/cpu/interrupts.hpp | 116 ------ arch/x86_64/include/arch/cpu/legacy_pic.hpp | 19 - .../include/arch/cpu/model_specific_register.hpp | 151 ------- arch/x86_64/include/arch/cpu/registers.hpp | 32 -- .../x86_64/include/arch/cpu/segment_descriptor.hpp | 61 --- arch/x86_64/include/arch/cpu/segment_selector.hpp | 21 - .../x86_64/include/arch/cpu/task_state_segment.hpp | 30 -- arch/x86_64/include/arch/debug/qemu_output.hpp | 43 -- arch/x86_64/include/arch/device_io/port_io.hpp | 112 ------ arch/x86_64/include/arch/devices/init.hpp | 12 - arch/x86_64/include/arch/devices/legacy_pit.hpp | 29 -- arch/x86_64/include/arch/devices/local_apic.hpp | 37 -- .../include/arch/memory/higher_half_mapper.hpp | 44 -- arch/x86_64/include/arch/memory/kernel_mapper.hpp | 35 -- arch/x86_64/include/arch/memory/mmu.hpp | 27 -- arch/x86_64/include/arch/memory/page_table.hpp | 232 ----------- arch/x86_64/include/arch/memory/page_utilities.hpp | 29 -- .../include/arch/memory/region_allocator.hpp | 93 ----- arch/x86_64/include/arch/vga/crtc.hpp | 35 -- arch/x86_64/include/arch/vga/text.hpp | 10 - arch/x86_64/include/arch/vga/text/attribute.hpp | 30 -- arch/x86_64/include/arch/vga/text/buffer.hpp | 101 ----- arch/x86_64/include/arch/vga/text/color.hpp | 35 -- .../include/arch/vga/text/common_attributes.hpp | 37 -- arch/x86_64/include/arch/vga/text/device.hpp | 45 --- arch/x86_64/include/arch/vga/text/flags.hpp | 38 -- .../pre/include/arch/context_switching/main.hpp | 51 --- .../arch/context_switching/syscall/main.hpp | 91 ----- .../context_switching/syscall/syscall_enable.hpp | 18 - .../context_switching/syscall/syscall_handler.hpp | 18 - arch/x86_64/pre/include/arch/kernel/halt.hpp | 13 - arch/x86_64/pre/include/arch/kernel/main.hpp | 13 - arch/x86_64/pre/include/arch/user/main.hpp | 16 - arch/x86_64/pre/src/context_switching/main.cpp | 66 --- .../pre/src/context_switching/syscall/main.cpp | 35 -- .../context_switching/syscall/syscall_enable.cpp | 32 -- .../context_switching/syscall/syscall_handler.cpp | 121 ------ arch/x86_64/pre/src/kernel/main.cpp | 71 ---- arch/x86_64/pre/src/user/main.cpp | 35 -- arch/x86_64/src/boot/boot32.S | 443 --------------------- arch/x86_64/src/boot/entry64.s | 62 --- arch/x86_64/src/boot/initialize_runtime.cpp | 22 - arch/x86_64/src/boot/multiboot.s | 33 -- arch/x86_64/src/bus/isa.cpp | 14 - arch/x86_64/src/cpu/initialization.cpp | 164 -------- arch/x86_64/src/cpu/interrupt_stubs.S | 112 ------ arch/x86_64/src/cpu/interrupts.cpp | 203 ---------- arch/x86_64/src/debug/qemu_output.cpp | 25 -- arch/x86_64/src/devices/init.cpp | 76 ---- arch/x86_64/src/devices/legacy_pit.cpp | 60 --- arch/x86_64/src/devices/local_apic.cpp | 134 ------- arch/x86_64/src/memory/higher_half_mapper.cpp | 119 ------ arch/x86_64/src/memory/kernel_mapper.cpp | 117 ------ arch/x86_64/src/memory/mmu.cpp | 19 - arch/x86_64/src/memory/page_table.cpp | 82 ---- arch/x86_64/src/memory/region_allocator.cpp | 165 -------- arch/x86_64/src/vga/text/buffer.cpp | 101 ----- arch/x86_64/src/vga/text/device.cpp | 60 --- 116 files changed, 3982 insertions(+), 4552 deletions(-) create mode 100644 arch/x86_64/arch/boot/boot.hpp create mode 100644 arch/x86_64/arch/boot/boot32.S create mode 100644 arch/x86_64/arch/boot/entry64.s create mode 100644 arch/x86_64/arch/boot/initialize_runtime.cpp create mode 100644 arch/x86_64/arch/boot/ld.hpp create mode 100644 arch/x86_64/arch/boot/multiboot.s create mode 100644 arch/x86_64/arch/bus/isa.cpp create mode 100644 arch/x86_64/arch/bus/isa.hpp create mode 100644 arch/x86_64/arch/cpu/control_register.hpp create mode 100644 arch/x86_64/arch/cpu/global_descriptor_table.hpp create mode 100644 arch/x86_64/arch/cpu/initialization.cpp create mode 100644 arch/x86_64/arch/cpu/initialization.hpp create mode 100644 arch/x86_64/arch/cpu/interrupts.S create mode 100644 arch/x86_64/arch/cpu/interrupts.cpp create mode 100644 arch/x86_64/arch/cpu/interrupts.hpp create mode 100644 arch/x86_64/arch/cpu/legacy_pic.hpp create mode 100644 arch/x86_64/arch/cpu/model_specific_register.hpp create mode 100644 arch/x86_64/arch/cpu/registers.hpp create mode 100644 arch/x86_64/arch/cpu/segment_descriptor.hpp create mode 100644 arch/x86_64/arch/cpu/segment_selector.hpp create mode 100644 arch/x86_64/arch/cpu/task_state_segment.hpp create mode 100644 arch/x86_64/arch/debug/qemu_output.cpp create mode 100644 arch/x86_64/arch/debug/qemu_output.hpp create mode 100644 arch/x86_64/arch/device_io/port_io.hpp create mode 100644 arch/x86_64/arch/devices/init.cpp create mode 100644 arch/x86_64/arch/devices/init.hpp create mode 100644 arch/x86_64/arch/devices/legacy_pit.cpp create mode 100644 arch/x86_64/arch/devices/legacy_pit.hpp create mode 100644 arch/x86_64/arch/devices/local_apic.cpp create mode 100644 arch/x86_64/arch/devices/local_apic.hpp create mode 100644 arch/x86_64/arch/memory/higher_half_mapper.cpp create mode 100644 arch/x86_64/arch/memory/higher_half_mapper.hpp create mode 100644 arch/x86_64/arch/memory/kernel_mapper.cpp create mode 100644 arch/x86_64/arch/memory/kernel_mapper.hpp create mode 100644 arch/x86_64/arch/memory/mmu.cpp create mode 100644 arch/x86_64/arch/memory/mmu.hpp create mode 100644 arch/x86_64/arch/memory/page_table.cpp create mode 100644 arch/x86_64/arch/memory/page_table.hpp create mode 100644 arch/x86_64/arch/memory/page_utilities.hpp create mode 100644 arch/x86_64/arch/memory/region_allocator.cpp create mode 100644 arch/x86_64/arch/memory/region_allocator.hpp create mode 100644 arch/x86_64/arch/vga/crtc.hpp create mode 100644 arch/x86_64/arch/vga/text.hpp create mode 100644 arch/x86_64/arch/vga/text/attribute.hpp create mode 100644 arch/x86_64/arch/vga/text/buffer.cpp create mode 100644 arch/x86_64/arch/vga/text/buffer.hpp create mode 100644 arch/x86_64/arch/vga/text/color.hpp create mode 100644 arch/x86_64/arch/vga/text/common_attributes.hpp create mode 100644 arch/x86_64/arch/vga/text/device.cpp create mode 100644 arch/x86_64/arch/vga/text/device.hpp create mode 100644 arch/x86_64/arch/vga/text/flags.hpp delete mode 100644 arch/x86_64/include/arch/boot/boot.hpp delete mode 100644 arch/x86_64/include/arch/boot/ld.hpp delete mode 100644 arch/x86_64/include/arch/bus/isa.hpp delete mode 100644 arch/x86_64/include/arch/cpu/control_register.hpp delete mode 100644 arch/x86_64/include/arch/cpu/global_descriptor_table.hpp delete mode 100644 arch/x86_64/include/arch/cpu/initialization.hpp delete mode 100644 arch/x86_64/include/arch/cpu/interrupts.hpp delete mode 100644 arch/x86_64/include/arch/cpu/legacy_pic.hpp delete mode 100644 arch/x86_64/include/arch/cpu/model_specific_register.hpp delete mode 100644 arch/x86_64/include/arch/cpu/registers.hpp delete mode 100644 arch/x86_64/include/arch/cpu/segment_descriptor.hpp delete mode 100644 arch/x86_64/include/arch/cpu/segment_selector.hpp delete mode 100644 arch/x86_64/include/arch/cpu/task_state_segment.hpp delete mode 100644 arch/x86_64/include/arch/debug/qemu_output.hpp delete mode 100644 arch/x86_64/include/arch/device_io/port_io.hpp delete mode 100644 arch/x86_64/include/arch/devices/init.hpp delete mode 100644 arch/x86_64/include/arch/devices/legacy_pit.hpp delete mode 100644 arch/x86_64/include/arch/devices/local_apic.hpp delete mode 100644 arch/x86_64/include/arch/memory/higher_half_mapper.hpp delete mode 100644 arch/x86_64/include/arch/memory/kernel_mapper.hpp delete mode 100644 arch/x86_64/include/arch/memory/mmu.hpp delete mode 100644 arch/x86_64/include/arch/memory/page_table.hpp delete mode 100644 arch/x86_64/include/arch/memory/page_utilities.hpp delete mode 100644 arch/x86_64/include/arch/memory/region_allocator.hpp delete mode 100644 arch/x86_64/include/arch/vga/crtc.hpp delete mode 100644 arch/x86_64/include/arch/vga/text.hpp delete mode 100644 arch/x86_64/include/arch/vga/text/attribute.hpp delete mode 100644 arch/x86_64/include/arch/vga/text/buffer.hpp delete mode 100644 arch/x86_64/include/arch/vga/text/color.hpp delete mode 100644 arch/x86_64/include/arch/vga/text/common_attributes.hpp delete mode 100644 arch/x86_64/include/arch/vga/text/device.hpp delete mode 100644 arch/x86_64/include/arch/vga/text/flags.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/main.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/syscall/main.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/syscall/syscall_enable.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/syscall/syscall_handler.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/halt.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/main.hpp delete mode 100644 arch/x86_64/pre/include/arch/user/main.hpp delete mode 100644 arch/x86_64/pre/src/context_switching/main.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/syscall/main.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp delete mode 100644 arch/x86_64/pre/src/kernel/main.cpp delete mode 100644 arch/x86_64/pre/src/user/main.cpp delete mode 100644 arch/x86_64/src/boot/boot32.S delete mode 100644 arch/x86_64/src/boot/entry64.s delete mode 100644 arch/x86_64/src/boot/initialize_runtime.cpp delete mode 100644 arch/x86_64/src/boot/multiboot.s delete mode 100644 arch/x86_64/src/bus/isa.cpp delete mode 100644 arch/x86_64/src/cpu/initialization.cpp delete mode 100644 arch/x86_64/src/cpu/interrupt_stubs.S delete mode 100644 arch/x86_64/src/cpu/interrupts.cpp delete mode 100644 arch/x86_64/src/debug/qemu_output.cpp delete mode 100644 arch/x86_64/src/devices/init.cpp delete mode 100644 arch/x86_64/src/devices/legacy_pit.cpp delete mode 100644 arch/x86_64/src/devices/local_apic.cpp delete mode 100644 arch/x86_64/src/memory/higher_half_mapper.cpp delete mode 100644 arch/x86_64/src/memory/kernel_mapper.cpp delete mode 100644 arch/x86_64/src/memory/mmu.cpp delete mode 100644 arch/x86_64/src/memory/page_table.cpp delete mode 100644 arch/x86_64/src/memory/region_allocator.cpp delete mode 100644 arch/x86_64/src/vga/text/buffer.cpp delete mode 100644 arch/x86_64/src/vga/text/device.cpp (limited to 'arch') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 9e346ef..5657010 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -5,8 +5,16 @@ add_library("x86_64" OBJECT) add_library("arch::lib" ALIAS "x86_64") +target_include_directories("x86_64" PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" +) + +target_link_libraries("x86_64" PUBLIC + "os::kapi" + "libs::multiboot2" +) + target_sources("x86_64" PRIVATE - # Platform-dependent KAPI implementation "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" @@ -14,39 +22,41 @@ target_sources("x86_64" PRIVATE "kapi/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" +) +target_sources("x86_64" PRIVATE # CPU Initialization - "src/cpu/initialization.cpp" - "src/cpu/interrupts.cpp" - "src/cpu/interrupt_stubs.S" + "arch/cpu/initialization.cpp" + "arch/cpu/interrupts.cpp" + "arch/cpu/interrupts.S" # Bus Initialization - "src/bus/isa.cpp" + "arch/bus/isa.cpp" # Low-level bootstrap - "src/boot/boot32.S" - "src/boot/entry64.s" - "src/boot/initialize_runtime.cpp" - "src/boot/multiboot.s" + "arch/boot/boot32.S" + "arch/boot/entry64.s" + "arch/boot/initialize_runtime.cpp" + "arch/boot/multiboot.s" # Debug interfaces - "src/debug/qemu_output.cpp" + "arch/debug/qemu_output.cpp" # Devices - "src/devices/init.cpp" - "src/devices/legacy_pit.cpp" - "src/devices/local_apic.cpp" + "arch/devices/init.cpp" + "arch/devices/legacy_pit.cpp" + "arch/devices/local_apic.cpp" # Memory management - "src/memory/kernel_mapper.cpp" - "src/memory/higher_half_mapper.cpp" - "src/memory/mmu.cpp" - "src/memory/page_table.cpp" - "src/memory/region_allocator.cpp" + "arch/memory/kernel_mapper.cpp" + "arch/memory/higher_half_mapper.cpp" + "arch/memory/mmu.cpp" + "arch/memory/page_table.cpp" + "arch/memory/region_allocator.cpp" # VGA text mode - "src/vga/text/buffer.cpp" - "src/vga/text/device.cpp" + "arch/vga/text/buffer.cpp" + "arch/vga/text/device.cpp" ) file(GLOB_RECURSE ARCH_HEADERS diff --git a/arch/x86_64/arch/boot/boot.hpp b/arch/x86_64/arch/boot/boot.hpp new file mode 100644 index 0000000..7df61c4 --- /dev/null +++ b/arch/x86_64/arch/boot/boot.hpp @@ -0,0 +1,56 @@ +#ifndef TEACHOS_X86_64_BOOT_BOOT_HPP +#define TEACHOS_X86_64_BOOT_BOOT_HPP + +#ifdef __ASSEMBLER__ +// clang-format off + +//! The number of huge pages to map during bootstrap. +#define HUGE_PAGES_TO_MAP (16) + +//! The magic value to be set in eax by the multiboot 2 loader. +#define MULTIBOOT2_MAGIC (0x36d76289) + +//! The "A" bit in a GDT entry. +#define GDT_ACCESSED (1 << 40) + +//! The "R/W" bit in a GDT entry +#define GDT_READ_WRITE (1 << 41) + +//! The "E" bit in a GDT entry. +#define GDT_EXECUTABLE (1 << 43) + +//! The "S" bit in a GDT entry. +#define GDT_DESCRIPTOR_TYPE (1 << 44) + +//! The "P" bit in a GDT entry. +#define GDT_PRESENT (1 << 47) + +//! The "L" bit in a GDT entry. +#define GDT_LONG_MODE (1 << 53) + +// clang-format on +#else + +#include // IWYU pragma: export + +#include + +#include + +namespace kapi::boot +{ + + struct information + { + //! A pointer to the loader provided Multiboot2 Information structure. + multiboot2::information_view const * mbi; + + //! The index of the next character to be written in the VGA text buffer after handoff. + std::size_t vga_buffer_index; + }; + +} // namespace kapi::boot + +#endif + +#endif diff --git a/arch/x86_64/arch/boot/boot32.S b/arch/x86_64/arch/boot/boot32.S new file mode 100644 index 0000000..e6fcd85 --- /dev/null +++ b/arch/x86_64/arch/boot/boot32.S @@ -0,0 +1,443 @@ +#include + +/** + * @brief Uninitialized data for the bootstrapping process. + */ +.section .boot_bss, "aw", @nobits + +page_maps_start: +page_map_level_4: .skip 512 * 8 +page_map_level_3_high: .skip 512 * 8 +page_map_level_3_low: .skip 512 * 8 +page_map_level_2: .skip 512 * 8 +page_maps_end = . +page_maps_size = page_maps_end - page_maps_start + +/** + * @brief Storage for the multiboot2 information pointer. + */ +.global multiboot_information_pointer +multiboot_information_pointer: .skip 8 + +/** + * @brief Storage for the bootstrap stack. + */ +.section .boot_stack, "aw", @nobits +.align 16 + +early_stack_bottom: .skip 1 << 8 +early_stack_top: +early_stack_size = early_stack_top - early_stack_bottom + +/** + * @brief Constants for the bootstrapping process. + */ +.section .boot_rodata, "a", @progbits + +.global global_descriptor_table_data + +/** + * @brief A basic GDT for long mode. + */ +global_descriptor_table: +global_descriptor_table_null = . - global_descriptor_table +.quad 0 +global_descriptor_table_code = . - global_descriptor_table +.quad GDT_READ_WRITE | GDT_EXECUTABLE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | GDT_LONG_MODE | (1 << 55) +global_descriptor_table_data = . - global_descriptor_table +.quad GDT_READ_WRITE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | (1 << 54) | (1 << 55) +global_descriptor_table_end: + +message_prefix_panic: .string "Panic: " +message_not_loaded_by_multiboot2: .string "The operating system was not loaded by a Multiboot 2 loader." +message_cpuid_instruction_no_supported: .string "The 'cpuid' instruction is not supported on this platform." +message_long_mode_not_supported: .string "Long mode is not supported by this platform." +message_return_from_kernel_main: .string "Execution returned from kernel main." + +/** + * @brief Initialized data for the bootstrapping process. + */ +.section .boot_data, "aw", @progbits + +/** + * @brief A pointer to the current position within the VGA text buffer. + */ +.global vga_buffer_pointer +vga_buffer_pointer: .quad 0xb8000 + +/** + * @brief Code for the bootstrapping process. + */ +.section .boot_text, "ax", @progbits +.align 16 +.code32 + +.macro pie_base + push %esi + call 0f + 0: + pop %esi +.endm + +.macro function_start + push %ebp + mov %esp, %ebp +.endm + +.macro function_end + leave + ret +.endm + +.macro pie_function_start + function_start + pie_base +.endm + +.macro pie_function_end + pop %esi + function_end +.endm + +/** + * @brief Prepare the environment and start the kernel. + * + * This function performs all necessary checks to ensure the system was loaded + * by the expected loader and supports all features required to run the kernel. + * If successful, it prepares the system by setting up memory virtualization + * and then start the kernel proper. + * + * @param %eax The Multiboot 2 magic marker. + * @param %ebx The Multiboot 2 information pointer. + * @return void This function does not return. + */ +.global _start +_start: + call 0f +0: + pop %esi + + lea (early_stack_top - 0b)(%esi), %ecx + + mov %ecx, %esp + mov %esp, %ebp + + call _assert_loaded_by_multiboot2_loader + call _save_multiboot_information_pointer + + call _assert_cpuid_instruction_is_supported + call _assert_cpu_supports_long_mode + + push $HUGE_PAGES_TO_MAP + call _prepare_page_maps + add $4, %esp + + call _enable_paging + call _enable_sse + call _reload_gdt + + lea (_entry64 - 0b)(%esi), %eax + pushl $global_descriptor_table_code + pushl %eax + lret + +/** + * @brief Halt the system. + * + * This function will instruct the CPU to halt. It will try to keep the CPU + * halted, even if interrupts occur. + * + * @return This function never returns. + */ +_halt: + function_start + +1: + hlt + jmp 1b + + function_end + +/** + * @brief Print a message via the VGA text buffer. + * + * @param ebp+12 The message to print. + * @param ebp+8 The color to print the message in. + */ +_print: + pie_function_start + + push %edi + push %ebx + + mov 8(%ebp), %al + mov 12(%ebp), %edx + mov $0, %ecx + lea (vga_buffer_pointer - 0b)(%esi), %edi + mov (%edi), %edi + +1: + mov (%edx, %ecx), %bl + test %bl, %bl + je 2f + mov %bl, (%edi, %ecx, 2) + mov %al, 1(%edi, %ecx, 2) + inc %ecx + jmp 1b + +2: + shl $1, %ecx + add %ecx, %edi + lea (vga_buffer_pointer - 0b)(%esi), %ecx + mov %edi, (%ecx) + + pop %ebx + pop %edi + + pie_function_end + +/** + * @brief Print a given panic message and then halt the machine as if by calling ::halt() + * + * @param ebp+4 A message to print. + * @return This function does not return. + */ +_panic: + pie_function_start + + lea (message_prefix_panic - 0b)(%esi), %eax + + push %eax + push $0x4f + call _print + + mov 8(%ebp), %eax + mov %eax, 4(%esp) + call _print + add $8, %esp + + call _halt + + pie_function_end + +/** + * Assert that we were loaded by a Multiboot 2 compliant bootloader. + * + * This assertion will panic the system if the magic signature was not found. + * If we were loaded my an appropriate bootloader, this function also saves + * the provided MBI pointer to `multiboot_information_pointer`. + */ +_assert_loaded_by_multiboot2_loader: + pie_function_start + + cmp $MULTIBOOT2_MAGIC, %eax + je 1f + lea (message_not_loaded_by_multiboot2 - 0b)(%esi), %eax + push %eax + call _panic +1: + pie_function_end + +/** + * @brief Store the multiboot 2 information pointer in the global memory. + * + * @return void + */ +_save_multiboot_information_pointer: + pie_function_start + + lea (multiboot_information_pointer - 0b)(%esi), %eax + mov %ebx, (%eax) + + pie_function_end + +/** + * @brief Assert that the CPU supports the CPUID instruction. + * + * The primary way to check for support of the instruction is to flip the ID + * bin in EFLAGS and then check if this changed was accepted. If so, the CPU + * supports the CPUID instruction, otherwise it most-likely doesn't. + */ +_assert_cpuid_instruction_is_supported: + pie_function_start + + pushfl + pop %eax + mov %eax, %ecx + + xor $(1 << 21), %eax /* Flip the ID bit */ + push %eax /* Move the new bitset on the stack for loading */ + popfl /* Load the flags with ID set back into EFLAGS */ + pushfl /* Copy the flags back onto the stack */ + pop %eax /* Load the flags for further checking */ + + push %ecx + popfl + + cmp %ecx, %eax + jne 1f + lea (message_cpuid_instruction_no_supported - 0b)(%esi), %eax + push %eax + call _panic + +1: + pie_function_end + +/** + * @brief Assert that the CPU supports going into long mode. + */ +_assert_cpu_supports_long_mode: + pie_function_start + + mov $0x80000000, %eax + cpuid + cmp $0x80000001, %eax + jb 1f + + mov $0x80000001, %eax + cpuid + test $(1 << 29), %edx + jnz 2f +1: + lea (message_long_mode_not_supported - 0b)(%esi), %eax + push %eax + call _panic +2: + pie_function_end + +/** + * @brief Prepare a basic page map hierarchy + * + * @param ebp+8 The number of huge pages to map + * @return void + */ +_prepare_page_maps: + pie_function_start + + push %edi + + call _clear_page_map_memory + + lea (page_map_level_4 - 0b)(%esi), %edi + lea (page_map_level_3_low - 0b)(%esi), %eax + or $0b11, %eax + mov %eax, (%edi) + + lea (page_map_level_3_high - 0b)(%esi), %eax + or $0b11, %eax + mov %eax, (511 * 8)(%edi) + + lea (page_map_level_3_low - 0b)(%esi), %edi + lea (page_map_level_2 - 0b)(%esi), %eax + or $0b11, %eax + mov %eax, (%edi) + + lea (page_map_level_3_high - 0b)(%esi), %edi + lea (page_map_level_2 - 0b)(%esi), %eax + or $0b11, %eax + mov %eax, (510 * 8)(%edi) + + lea (page_map_level_2 - 0b)(%esi), %edi + mov 8(%ebp), %ecx + +1: + dec %ecx + mov $(1 << 21), %eax + mul %ecx + or $0b10000011, %eax + mov %eax, (%edi, %ecx, 8) + + test %ecx, %ecx + jnz 1b + + pop %edi + + pie_function_end + +/** + * @brief Clear all page map memory by filling it with 0s. + * + * @return void + */ +_clear_page_map_memory: + pie_function_start + + push %edi + + xor %eax, %eax + mov $page_maps_size, %ecx + shr $2, %ecx + lea (page_maps_start - 0b)(%esi), %edi + rep stosl + + pop %edi + + pie_function_end + +/** + * @p Enable memory virtualization via paging. + * + * Note: This routine expects for there to be a valid set of page maps already + * set up for use. + * + * @return void + */ +_enable_paging: + pie_function_start + + lea (page_map_level_4 - 0b)(%esi), %eax + mov %eax, %cr3 + + /* Enable Physical Address Extension */ + mov %cr4, %eax + or $(1 << 5), %eax + mov %eax, %cr4 + + /* Enable long mode support */ + mov $0xC0000080, %ecx + rdmsr + or $(1 << 8), %eax + wrmsr + + /* Enable paging */ + mov %cr0, %eax + or $(1 << 31), %eax + mov %eax, %cr0 + + pie_function_end + +/** + * @brief Enable use of SSE instructions. + */ +_enable_sse: + function_start + + mov %cr0, %eax + and $0xfffffffb, %eax + or $0x00000002, %eax + mov %eax, %cr0 + + mov %cr4, %eax + or $(3 << 9), %eax + mov %eax, %cr4 + + function_end + +/** + * @brief Prepare a new GTD and load make it active. + * + * @return void + */ +_reload_gdt: + pie_function_start + + sub $10, %esp + lea (global_descriptor_table - 0b)(%esi), %eax + movw $(global_descriptor_table_end - global_descriptor_table -1), (%esp) + mov %eax, 2(%esp) + movl $0, 6(%esp) + + lgdt (%esp) + add $10, %esp + + pie_function_end diff --git a/arch/x86_64/arch/boot/entry64.s b/arch/x86_64/arch/boot/entry64.s new file mode 100644 index 0000000..29fb778 --- /dev/null +++ b/arch/x86_64/arch/boot/entry64.s @@ -0,0 +1,62 @@ +.section .stack, "aw", @nobits + +.align 16 +.global stack_top +stack_bottom: .skip 1 << 20 +stack_top: +stack_size = stack_top - stack_bottom + +.section .bss, "aw", @nobits + +//! A structure containing information gathered during the bootstrap process. +//! Expected layout (as described by teachos::boot::information): +//! +//! struct +//! { +//! multiboot2::information_view const * mbi; +//! std::size_t vga_buffer_index; +//! } +.global bootstrap_information +bootstrap_information: .skip 16 + +.section .boot_text, "ax", @progbits +.code64 + +.global _entry64 +_entry64: + mov $global_descriptor_table_data, %rax + mov %rax, %ss + mov %rax, %ds + mov %rax, %es + mov %rax, %fs + mov %rax, %gs + + mov $stack_top, %rsp + mov %rsp, %rbp + + mov multiboot_information_pointer, %rax + or $TEACHOS_VMA, %rax + mov vga_buffer_pointer, %rdx + sub $0xb8000, %rdx + mov %rax, (bootstrap_information) + mov %rdx, (bootstrap_information + 8) + + call invoke_global_constructors + + xor %rax, %rax + mov %rax, %rbp + mov %rax, %rdx + mov %rax, %rsi + + mov $stack_size, %rcx + shr $3, %rcx + lea (stack_bottom), %rdi + rep stosq + + mov %rax, %rdi + + call main + +1: + hlt + jmp 1b diff --git a/arch/x86_64/arch/boot/initialize_runtime.cpp b/arch/x86_64/arch/boot/initialize_runtime.cpp new file mode 100644 index 0000000..b08c13c --- /dev/null +++ b/arch/x86_64/arch/boot/initialize_runtime.cpp @@ -0,0 +1,22 @@ +#include +#include +#include + +namespace arch::boot +{ + + extern "C" + { + using global_initializer = auto (*)() -> void; + + extern global_initializer __init_array_start; + extern global_initializer __init_array_end; + + auto invoke_global_constructors() -> void + { + auto initializers = std::span{&__init_array_start, &__init_array_end}; + std::ranges::for_each(initializers, [](auto invokable) { std::invoke(invokable); }); + } + } + +} // namespace arch::boot \ No newline at end of file diff --git a/arch/x86_64/arch/boot/ld.hpp b/arch/x86_64/arch/boot/ld.hpp new file mode 100644 index 0000000..988723d --- /dev/null +++ b/arch/x86_64/arch/boot/ld.hpp @@ -0,0 +1,61 @@ +//! @file +//! The interface to linker script defined symbols. +//! +//! This header provides declarations for symbols that are defined in the linker script itself. The symbols declared +//! here provide important information, for example the start and end of the kernel image in virtual and physical +//! memory. +//! +//! Any variables defined in this file must not be read themselves, but rather their address shall be taken, yielding a +//! pointer to the memory location the represent. +//! +//! @note The symbols declared in this header are declared using C-language linkage in order to suppress name mangling. +//! +//! @see arch/x86_64/scripts/kernel.ld + +#ifndef TEACHOS_X86_64_BOOT_LD_HPP +#define TEACHOS_X86_64_BOOT_LD_HPP + +#include + +namespace arch::boot +{ + + extern "C" + { + //! The beginning of the kernel image in physical memory + //! + //! This symbol marks the start of the kernel image in physical memory. + //! + //! @see _end_physical + extern std::byte _start_physical; + + //! The first byte after the loaded kernel image. + //! + //! This symbol marks the end of the kernel image in physical memory. + //! + //! @see _start_physical + extern std::byte _end_physical; + + //! The first byte of the loaded kernel image in the virtual address space. + //! + //! This symbol and marks the start of the kernel image in virtual memory. + //! + //! @see _end_virtual + extern std::byte _start_virtual; + + //! The first byte after the loaded kernel image in the virtual address space. + //! + //! This symbol marks the end of the kernel image in virtual memory. + //! + //! @see _start_virtual + extern std::byte _end_virtual; + + //! The first byte of the kernel's virtual address space. + //! + //! This symbol marks beginning of the kernel virtual address space. + extern std::byte TEACHOS_VMA; + } + +} // namespace arch::boot + +#endif diff --git a/arch/x86_64/arch/boot/multiboot.s b/arch/x86_64/arch/boot/multiboot.s new file mode 100644 index 0000000..37d8afe --- /dev/null +++ b/arch/x86_64/arch/boot/multiboot.s @@ -0,0 +1,33 @@ +.section .boot_mbh, "a" +.align 8 + +multiboot_header_start: +.Lmagic: + .long 0xe85250d6 +.Larch: + .long 0 +.Llength: + .long multiboot_header_end - multiboot_header_start +.Lchecksum: + .long 0x100000000 - (0xe85250d6 + 0 + (multiboot_header_end - multiboot_header_start)) +.align 8 +.Lflags_start: + .word 4 + .word 1 + .long .Lflags_end - .Lflags_start + .long 3 +.Lflags_end: +.align 8 +.Linformation_request_start: + .word 1 + .word 0 + .long .Linformation_request_end - .Linformation_request_start + .long 3 +.Linformation_request_end: +.align 8 +.Lend_start: + .word 0 + .word 0 + .long .Lend_end - .Lend_start +.Lend_end: +multiboot_header_end: diff --git a/arch/x86_64/arch/bus/isa.cpp b/arch/x86_64/arch/bus/isa.cpp new file mode 100644 index 0000000..f6cc72d --- /dev/null +++ b/arch/x86_64/arch/bus/isa.cpp @@ -0,0 +1,14 @@ +#include + +#include + +#include + +namespace arch::bus +{ + + isa::isa(std::size_t major) + : kapi::devices::bus{major, 0, "isa"} + {} + +} // namespace arch::bus \ No newline at end of file diff --git a/arch/x86_64/arch/bus/isa.hpp b/arch/x86_64/arch/bus/isa.hpp new file mode 100644 index 0000000..e56f56a --- /dev/null +++ b/arch/x86_64/arch/bus/isa.hpp @@ -0,0 +1,18 @@ +#ifndef TEACHOS_X86_64_BUS_ISA_HPP +#define TEACHOS_X86_64_BUS_ISA_HPP + +#include + +#include + +namespace arch::bus +{ + + struct isa final : public kapi::devices::bus + { + isa(std::size_t major); + }; + +} // namespace arch::bus + +#endif // TEACHOS_X86_64_BUS_ISA_HPP diff --git a/arch/x86_64/arch/cpu/control_register.hpp b/arch/x86_64/arch/cpu/control_register.hpp new file mode 100644 index 0000000..9cedc35 --- /dev/null +++ b/arch/x86_64/arch/cpu/control_register.hpp @@ -0,0 +1,249 @@ +#ifndef TEACHOS_X86_64_CPU_CONTROL_REGISTERS_HPP +#define TEACHOS_X86_64_CPU_CONTROL_REGISTERS_HPP + +// IWYU pragma: private, include + +#include + +#include + +#include +#include +#include +#include + +namespace arch::cpu +{ + namespace impl + { + //! The assembler templates used to access (r/w) CR0; + constexpr auto static cr0_asm = std::pair{"mov %%cr0, %0", "mov %0, %%cr0"}; + + //! The assembler templates used to access (r/w) CR2; + constexpr auto static cr2_asm = std::pair{"mov %%cr2, %0", "mov %0, %%cr2"}; + + //! The assembler templates used to access (r/w) CR3; + constexpr auto static cr3_asm = std::pair{"mov %%cr3, %0", "mov %0, %%cr3"}; + } // namespace impl + + //! The flags that can be set on CR0 configuration register. + enum struct cr0_flags : uint64_t + { + //! Enable protected mode. + protection_enable = 1uz << 0, + //! Enable wait-monitoring of the coprocessor after task switching. + monitor_coprocessor = 1uz << 1, + //! Emulate floating point coprocessor. + emulation = 1uz << 2, + //! Marks that a task switch has occurred. + task_switched = 1uz << 3, + //! Marks Intel 387 DX math coprocessor as available + extension_type = 1uz << 4, + //! Numeric error handling mode. + numeric_error = 1uz << 5, + //! Disable writing to read-only marked memory. + write_protect = 1uz << 16, + //! Enable Ring-3 alignment checks + alignment_check = 1uz << 18, + //! Disable write through + not_write_through = 1uz << 29, + //! Disable caching of memory accesses + cache_disable = 1uz << 30, + //! Enable paging + paging = 1uz << 31 + }; + + enum struct cr3_flags : std::uint64_t + { + page_level_write_through = 1uz << 0, + page_level_cache_disable = 1uz << 1, + }; +} // namespace arch::cpu + +namespace kstd::ext +{ + template<> + struct is_bitfield_enum : std::true_type + { + }; + + template<> + struct is_bitfield_enum : std::true_type + { + }; +} // namespace kstd::ext + +namespace arch::cpu +{ + //! A mixin for flag-oriented control registers. + //! + //! This mixin provides additional functionality for flag-oriented, or partially flag-oriented, control registers. A + //! control register is flag-oriented, if it comprises a bitfield and zero or more additional non-bitfield parts. + //! + //! @tparam Derived The class deriving from this mixin. + //! @tparam ValueType The value type of the class deriving from this mixin. + template + struct control_register_with_flags + { + private: + constexpr control_register_with_flags() noexcept = default; + friend Derived; + }; + + //! @copydoc control_register_with_flags + //! + //! @note This specialization provides the implementation for the case in which the value type of the control register + //! is an enum. + template + struct control_register_with_flags>> + { + //! The type of the flags used by this control register + using flags = ValueType; + + //! Set one or more flags in this control register. + //! + //! @warning This function is to be considered **UNSAFE**. Setting flags in a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value One or a combination of flags to be set in the control register. + auto static set(ValueType value) -> void + { + auto current = Derived::read(); + current |= value; + Derived::write(current); + } + + //! Clear one or more flags in this control register. + //! + //! @warning This function is to be considered **UNSAFE**. Clearing flags in a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value One or a combination of flags to be cleared in the control register. + auto static clear(ValueType value) -> void + { + auto current = Derived::read(); + current &= ~value; + Derived::write(current); + } + }; + + //! A CPU control register. + //! + //! CPU control registers are used to configure builtin features of the CPU, for example memory protection and FPU + //! error reporting. Writing to a control register is inherently dangerous, since a misconfiguration can leave the CPU + //! in an invalid/undefined state. + template + struct control_register : control_register_with_flags, ValueType> + { + //! Read the current value of the control register. + //! + //! @return The currently set value of the control register. + [[nodiscard]] auto static read() -> ValueType + { + auto value = ValueType{}; + asm volatile((AssemblerTemplates->first) : "=r"(value)); + return value; + } + + //! Write a new value to the control register. + //! + //! @warning This function should be considered **UNSAFE**. Writing values to a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value The new value to write to the control register. + auto static write(ValueType value) -> void + { + asm volatile((AssemblerTemplates->second) : : "r"(value)); + } + }; + + //! The value type of the CR3 control register. + //! + //! The CR3 control register holds the root configuration of the virtual memory protection mechanism. It contains the + //! page aligned physical address of the root page map, as well as the root paging configuration flags. + struct cr3_value + { + //! Contstruct a 0-value CR3 value. + constexpr cr3_value() = default; + + //! Construct a CR3 value using the given root page map address and flags. + //! + //! @param address The physical address of the root page map + //! @param flags The root configuration flags of the paging system. + constexpr cr3_value(kapi::memory::physical_address address, cr3_flags flags = static_cast(0)) + : m_flags{static_cast(flags)} + , m_address{static_cast(address.raw())} + {} + + //! Extract the physical address of the root page map from this value. + //! + //! @return The physical address of the root page map. + [[nodiscard]] constexpr auto address() const -> kapi::memory::physical_address + { + constexpr auto address_shift = 12uz; + return kapi::memory::physical_address{m_address << address_shift}; + } + + //! Encode the frame aligned physical address of the root page map into this value. + //! + //! @param frame The frame containing a PML4. + constexpr auto frame(kapi::memory::frame frame) -> void + { + m_address = static_cast(frame.number()); + } + + //! Extract the root paging configuration flags from this value. + //! + //! @return The root paging configuration flags. + [[nodiscard]] constexpr auto flags() const -> cr3_flags + { + return static_cast(m_flags); + } + + //! Encode the root paging configuration flags into this value. + //! + //! @param flags The root paging configuration flags. + constexpr auto flags(cr3_flags flags) -> void + { + m_flags = static_cast(flags); + } + + //! Add the given flags to the current set of encoded root configuration flags of this value. + //! + //! @param flags The root configuration flags to add. + //! @return A reference to this value. + constexpr auto operator|=(cr3_flags flags) -> cr3_value & + { + m_flags |= static_cast(flags); + return *this; + } + + //! Mask the root configuration flags of this value. + //! + //! @param mask The mask to apply to the root configuration flags. + //! @return A reference to this value. + constexpr auto operator&=(cr3_flags mask) -> cr3_value & + { + m_flags &= static_cast(mask); + return *this; + } + + private: + //! Reserved bits. + std::uint64_t : 3; + //! The root paging configuration flags. + std::uint64_t m_flags : 2 {}; + //! Reserved bits. + std::uint64_t : 7; + //! The page aligned physical address of the root page map. + std::uint64_t m_address : 52 {}; + }; + + static_assert(sizeof(cr3_value) == sizeof(std::uint64_t)); + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/cpu/global_descriptor_table.hpp b/arch/x86_64/arch/cpu/global_descriptor_table.hpp new file mode 100644 index 0000000..b17c509 --- /dev/null +++ b/arch/x86_64/arch/cpu/global_descriptor_table.hpp @@ -0,0 +1,91 @@ +#ifndef TEACHOS_X86_64_GLOBAL_DESCRIPTOR_TABLE_HPP +#define TEACHOS_X86_64_GLOBAL_DESCRIPTOR_TABLE_HPP + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace arch::cpu +{ + + template + struct global_descriptor_table; + + struct [[gnu::packed]] global_descriptor_table_pointer + { + template + global_descriptor_table_pointer(global_descriptor_table const & gdt) + : size{GdtSize * sizeof(segment_descriptor) - 1} + , address{kapi::memory::physical_address{std::bit_cast(&gdt)}.raw()} + {} + + auto load() -> void + { + asm volatile("lgdt %0" : : "m"(*this)); + } + + std::uint16_t size{}; + std::uint64_t address{}; + }; + + static_assert(sizeof(global_descriptor_table_pointer) == sizeof(std::uint16_t) + sizeof(std::uint64_t)); + + template + struct global_descriptor_table + { + template... SegmentDescriptors> + constexpr global_descriptor_table(SegmentDescriptors const &... descriptors) + : m_descriptors{} + { + auto descriptor_data = std::array{ + std::pair{std::bit_cast(&descriptors), sizeof(descriptors)} + ... + }; + auto written_size = 0uz; + std::ranges::for_each(descriptor_data, [&written_size, this](auto entry) { + std::ranges::copy(entry.first, entry.first + entry.second, m_descriptors.begin() + written_size); + written_size += entry.second; + }); + } + + auto load(std::size_t code_segment_index, std::size_t data_segment_index) const -> void + { + auto pointer = global_descriptor_table_pointer{*this}; + pointer.load(); + + asm volatile("push %0\n" + "lea 1f(%%rip), %%rax\n" + "push %%rax\n" + "lretq\n" + "1:\n" + "mov %1, %%rax\n" + "mov %%rax, %%ss\n" + "mov %%rax, %%ds\n" + "mov %%rax, %%es\n" + "mov %%rax, %%fs\n" + "mov %%rax, %%gs\n" + : + : "X"(code_segment_index * sizeof(segment_descriptor)), + "X"(data_segment_index * sizeof(segment_descriptor)) + : "rax"); + } + + private: + std::array m_descriptors; + }; + + template... SegmentDescriptors> + global_descriptor_table(SegmentDescriptors const &... descriptors) + -> global_descriptor_table<(sizeof(SegmentDescriptors) + ...)>; + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/cpu/initialization.cpp b/arch/x86_64/arch/cpu/initialization.cpp new file mode 100644 index 0000000..1be9c82 --- /dev/null +++ b/arch/x86_64/arch/cpu/initialization.cpp @@ -0,0 +1,164 @@ +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace arch::cpu +{ + + namespace + { + constexpr auto gdt_null_descriptor = segment_descriptor{}; + + constexpr auto gdt_kernel_code_descriptor = segment_descriptor{ + .limit_low = 0xffff, + .base_low = 0, + .accessed = false, + .read_write = false, + .direction_or_conforming = false, + .executable = true, + .type = segment_type::code_or_data, + .privilege_level = 0, + .present = true, + .limit_high = 0xf, + .long_mode = true, + .protected_mode = false, + .granularity = segment_granularity::page, + .base_high = 0, + }; + + constexpr auto gdt_kernel_data_descriptor = segment_descriptor{ + .limit_low = 0xffff, + .base_low = 0, + .accessed = false, + .read_write = true, + .direction_or_conforming = false, + .executable = false, + .type = segment_type::code_or_data, + .privilege_level = 0, + .present = true, + .limit_high = 0xf, + .long_mode = false, + .protected_mode = true, + .granularity = segment_granularity::page, + .base_high = 0, + }; + + constexpr auto gdt_user_code_descriptor = segment_descriptor{ + .limit_low = 0xffff, + .base_low = 0, + .accessed = false, + .read_write = false, + .direction_or_conforming = false, + .executable = true, + .type = segment_type::code_or_data, + .privilege_level = 3, + .present = true, + .limit_high = 0xf, + .long_mode = true, + .protected_mode = false, + .granularity = segment_granularity::page, + .base_high = 0, + }; + + constexpr auto gdt_user_data_descriptor = segment_descriptor{ + .limit_low = 0xffff, + .base_low = 0, + .accessed = false, + .read_write = true, + .direction_or_conforming = false, + .executable = false, + .type = segment_type::code_or_data, + .privilege_level = 3, + .present = true, + .limit_high = 0xf, + .long_mode = false, + .protected_mode = false, + .granularity = segment_granularity::page, + .base_high = 0, + }; + + constexpr auto make_tss_descriptor(task_state_segment const * tss_ptr) -> system_segment_descriptor + { + auto const address = std::bit_cast(tss_ptr); + auto const limit = sizeof(task_state_segment) - 1; + + return system_segment_descriptor{ + { + .limit_low = limit & 0xffff, // NOLINT(readability-magic-numbers) + .base_low = address & 0xffffff, // NOLINT(readability-magic-numbers) + .accessed = false, + .read_write = false, + .direction_or_conforming = false, + .executable = false, + .type = segment_type::system, + .privilege_level = 0, + .present = true, + .limit_high = (limit >> 16) & 0xf, // NOLINT(readability-magic-numbers) + .long_mode = false, + .protected_mode = false, + .granularity = segment_granularity::byte, + .base_high = (address >> 24) & 0xff, // NOLINT(readability-magic-numbers) + }, + (address >> 32) & 0xffff'ffff, // NOLINT(readability-magic-numbers) + }; + } + } // namespace + + auto initialize_descriptors() -> void + { + auto static tss = task_state_segment{}; + auto static tss_descriptor = make_tss_descriptor(&tss); + + auto static gdt = global_descriptor_table{ + gdt_null_descriptor, gdt_kernel_code_descriptor, gdt_kernel_data_descriptor, + gdt_user_code_descriptor, gdt_user_data_descriptor, tss_descriptor, + }; + + kstd::println("[x86_64:SYS] Reloading Global Descriptor Table."); + gdt.load(1, 2); + + kstd::println("[x86_64:SYS] Initializing Interrupt Descriptor Table."); + auto static idt = interrupt_descriptor_table{}; + idt.load(); + } + + auto initialize_legacy_interrupts() -> void + { + constexpr auto pic_init_command = std::uint8_t{0x11}; + constexpr auto pic_master_offset = std::uint8_t{0x20}; + constexpr auto pic_slave_offset = std::uint8_t{0x28}; + constexpr auto pic_cascade_address = std::uint8_t{0x04}; + constexpr auto pic_cascade_slave_identity = std::uint8_t{0x02}; + constexpr auto pic_use_8086_mode = std::uint8_t{0x01}; + constexpr auto pic_master_mask = std::uint8_t{0x00}; + constexpr auto pic_slave_mask = std::uint8_t{0x00}; + + pic_master_control_port::write(pic_init_command); + pic_slave_control_port::write(pic_init_command); + + pic_master_data_port::write(pic_master_offset); + pic_slave_data_port::write(pic_slave_offset); + + pic_master_data_port::write(pic_cascade_address); + pic_slave_data_port::write(pic_cascade_slave_identity); + + pic_master_data_port::write(pic_use_8086_mode); + pic_slave_data_port::write(pic_use_8086_mode); + + pic_master_data_port::write(pic_master_mask); + pic_slave_data_port::write(pic_slave_mask); + + pic_master_data_port::write(pic_master_mask); + pic_slave_data_port::write(pic_slave_mask); + } + +} // namespace arch::cpu diff --git a/arch/x86_64/arch/cpu/initialization.hpp b/arch/x86_64/arch/cpu/initialization.hpp new file mode 100644 index 0000000..564c544 --- /dev/null +++ b/arch/x86_64/arch/cpu/initialization.hpp @@ -0,0 +1,12 @@ +#ifndef TEACHOS_ARCH_X86_64_CPU_INITIALIZATION_HPP +#define TEACHOS_ARCH_X86_64_CPU_INITIALIZATION_HPP + +namespace arch::cpu +{ + auto initialize_descriptors() -> void; + + auto initialize_legacy_interrupts() -> void; + +} // namespace arch::cpu + +#endif diff --git a/arch/x86_64/arch/cpu/interrupts.S b/arch/x86_64/arch/cpu/interrupts.S new file mode 100644 index 0000000..e59bdd2 --- /dev/null +++ b/arch/x86_64/arch/cpu/interrupts.S @@ -0,0 +1,112 @@ +.altmacro + +.macro ISR_WITHOUT_ERROR_CODE vector + .global isr\vector + isr\vector: + pushq $0 + pushq $\vector + jmp common_interrupt_handler +.endm + +.macro ISR_WITH_ERROR_CODE vector + .global isr\vector + isr\vector: + pushq $\vector + jmp common_interrupt_handler +.endm + +.macro ISR_TABLE_ENTRY vector + .quad isr\vector +.endm + +.section .rodata +.global isr_stub_table +.align 16 + +isr_stub_table: +.set i, 0 +.rept 256 + ISR_TABLE_ENTRY %i + .set i, i + 1 +.endr + + +.section .text + +common_interrupt_handler: + push %rax + push %rbx + push %rcx + push %rdx + push %rbp + push %rsi + push %rdi + push %r8 + push %r9 + push %r10 + push %r11 + push %r12 + push %r13 + push %r14 + push %r15 + + mov %rsp, %rdi + call interrupt_dispatch + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %r11 + pop %r10 + pop %r9 + pop %r8 + pop %rdi + pop %rsi + pop %rbp + pop %rdx + pop %rcx + pop %rbx + pop %rax + + add $16, %rsp + iretq + +ISR_WITHOUT_ERROR_CODE 0 +ISR_WITHOUT_ERROR_CODE 1 +ISR_WITHOUT_ERROR_CODE 2 +ISR_WITHOUT_ERROR_CODE 3 +ISR_WITHOUT_ERROR_CODE 4 +ISR_WITHOUT_ERROR_CODE 5 +ISR_WITHOUT_ERROR_CODE 6 +ISR_WITHOUT_ERROR_CODE 7 +ISR_WITH_ERROR_CODE 8 +ISR_WITHOUT_ERROR_CODE 9 +ISR_WITH_ERROR_CODE 10 +ISR_WITH_ERROR_CODE 11 +ISR_WITH_ERROR_CODE 12 +ISR_WITH_ERROR_CODE 13 +ISR_WITH_ERROR_CODE 14 +ISR_WITHOUT_ERROR_CODE 15 +ISR_WITHOUT_ERROR_CODE 16 +ISR_WITH_ERROR_CODE 17 +ISR_WITHOUT_ERROR_CODE 18 +ISR_WITHOUT_ERROR_CODE 19 +ISR_WITHOUT_ERROR_CODE 20 +ISR_WITH_ERROR_CODE 21 +ISR_WITHOUT_ERROR_CODE 22 +ISR_WITHOUT_ERROR_CODE 23 +ISR_WITHOUT_ERROR_CODE 24 +ISR_WITHOUT_ERROR_CODE 25 +ISR_WITHOUT_ERROR_CODE 26 +ISR_WITHOUT_ERROR_CODE 27 +ISR_WITHOUT_ERROR_CODE 28 +ISR_WITH_ERROR_CODE 29 +ISR_WITH_ERROR_CODE 30 +ISR_WITHOUT_ERROR_CODE 31 + +.set i, 32 +.rept 256 - 32 + ISR_WITHOUT_ERROR_CODE %i + .set i, i + 1 +.endr diff --git a/arch/x86_64/arch/cpu/interrupts.cpp b/arch/x86_64/arch/cpu/interrupts.cpp new file mode 100644 index 0000000..f40422f --- /dev/null +++ b/arch/x86_64/arch/cpu/interrupts.cpp @@ -0,0 +1,203 @@ +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +namespace arch::cpu +{ + + namespace + { + enum struct exception + { + divide_error, + debug_exception, + non_maskable_interrupt, + breakpoint, + overflow, + bound_range_exceeded, + invalid_opcode, + device_not_available, + double_fault, + coprocessor_segment_overrun, + invalid_tss, + segment_not_present, + stack_segment_fault, + general_protection_fault, + page_fault, + x87_fpu_floating_point_error = 16, + alignment_check, + machine_check, + simd_floating_point_error, + virtualization_exception, + control_protection_exception, + hypervisor_injection_exception = 28, + vmm_communication_exception, + security_exception, + }; + + constexpr auto number_of_exception_vectors = 32u; + + constexpr auto pic_master_irq_start = 0x20; + constexpr auto pic_master_irq_end = pic_master_irq_start + 8; + constexpr auto pic_slave_irq_start = pic_master_irq_end; + + constexpr auto to_exception_type(exception e) + { + switch (e) + { + case exception::divide_error: + case exception::x87_fpu_floating_point_error: + case exception::simd_floating_point_error: + return kapi::cpu::exception::type::arithmetic_error; + case exception::breakpoint: + return kapi::cpu::exception::type::breakpoint; + case exception::invalid_opcode: + return kapi::cpu::exception::type::illegal_instruction; + case exception::stack_segment_fault: + return kapi::cpu::exception::type::memory_access_fault; + case exception::general_protection_fault: + return kapi::cpu::exception::type::privilege_violation; + case exception::page_fault: + return kapi::cpu::exception::type::page_fault; + case exception::alignment_check: + return kapi::cpu::exception::type::alignment_fault; + default: + return kapi::cpu::exception::type::unknown; + } + } + + constexpr auto has_error_code(exception e) + { + switch (e) + { + case exception::double_fault: + case exception::invalid_tss: + case exception::segment_not_present: + case exception::stack_segment_fault: + case exception::general_protection_fault: + case exception::page_fault: + case exception::alignment_check: + case exception::control_protection_exception: + case exception::security_exception: + return true; + default: + return false; + } + } + + auto dispatch_exception(interrupt_frame * frame) -> bool + { + auto type = to_exception_type(static_cast(frame->interrupt.number)); + auto fault_address = kapi::memory::linear_address{}; + auto instruction_pointer = frame->cpu_saved.rip; + + switch (type) + { + case kapi::cpu::exception::type::page_fault: + { + asm volatile("mov %%cr2, %0" : "=r"(fault_address)); + auto present = (frame->interrupt.error_code & 0x1) != 0; + auto write = (frame->interrupt.error_code & 0x2) != 0; + auto user = (frame->interrupt.error_code & 0x4) != 0; + + return kapi::cpu::dispatch({type, instruction_pointer, fault_address, present, write, user}); + } + default: + return kapi::cpu::dispatch({type, instruction_pointer}); + } + } + + auto acknowledge_pic_interrupt(interrupt_frame * frame) -> void + { + if (frame->interrupt.number >= pic_slave_irq_start) + { + pic_slave_control_port::write(pic_end_of_interrupt); + } + pic_master_control_port::write(pic_end_of_interrupt); + } + } // namespace + + extern "C" + { + extern std::uintptr_t const isr_stub_table[256]; + + auto interrupt_dispatch(interrupt_frame * frame) -> void + { + auto [number, code] = frame->interrupt; + + if (number < number_of_exception_vectors) + { + if (!dispatch_exception(frame)) + { + if (has_error_code(static_cast(number))) + { + kstd::println(kstd::print_sink::stderr, + "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); + } + else + { + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled exception number {:#04x} received", number); + } + } + } + else + { + auto irq_number = number - number_of_exception_vectors; + + if (kapi::interrupts::dispatch(irq_number) == kapi::interrupts::status::unhandled) + { + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x} (IRQ{})", number, + irq_number); + } + + acknowledge_pic_interrupt(frame); + } + } + } + + interrupt_descriptor_table::interrupt_descriptor_table() noexcept + { + for (auto i = 0uz; i < 256; ++i) + { + m_descriptors[i] = gate_descriptor{ + .offset_low = static_cast(isr_stub_table[i] & 0xffff), // NOLINT(readability-magic-numbers) + .m_code_segment = segment_selector{0, false, 1}, + .interrupt_stack_table_selector = 0, + .gate_type = (i < 32 && i != 2) ? gate_type::trap_gate : gate_type::interrupt_gate, + .descriptor_privilege_level = 0, + .present = true, + .offset_middle = + static_cast((isr_stub_table[i] >> 16) & 0xffff), // NOLINT(readability-magic-numbers) + .offset_high = + static_cast((isr_stub_table[i] >> 32) & 0xffff'ffff), // NOLINT(readability-magic-numbers) + }; + } + } + + auto interrupt_descriptor_table::load() const -> void + { + interrupt_descriptor_table_register{.limit = sizeof(m_descriptors) - 1, .base = m_descriptors.data()}.load(); + } + + auto interrupt_descriptor_table_register::load() const -> void + { + asm volatile("lidt %0" : : "m"(*this)); + } + + auto interrupt_descriptor_table_register::read() -> interrupt_descriptor_table_register + { + interrupt_descriptor_table_register idtr{}; + asm volatile("sidt %0" : : "m"(idtr)); + return idtr; + } + +} // namespace arch::cpu \ No newline at end of file diff --git a/arch/x86_64/arch/cpu/interrupts.hpp b/arch/x86_64/arch/cpu/interrupts.hpp new file mode 100644 index 0000000..6162f56 --- /dev/null +++ b/arch/x86_64/arch/cpu/interrupts.hpp @@ -0,0 +1,116 @@ +#ifndef TEACHOS_X86_64_CPU_INTERRUPTS_HPP +#define TEACHOS_X86_64_CPU_INTERRUPTS_HPP + +#include + +#include + +#include +#include +#include + +namespace arch::cpu +{ + + //! The types of supported gates. + enum struct gate_type : std::uint8_t + { + //! A gate entered through the @p INT instruction. + interrupt_gate = 0b1110, + //! A gate entered via an exception. + trap_gate = 0b1111, + }; + + struct alignas(std::uint64_t) gate_descriptor + { + //! The lowest 16 bits of the address of this gate's handler function. + std::uint16_t offset_low : 16; + //! The code segment used when handling the respective interrupt. + segment_selector m_code_segment; + //! The index into the Interrupt Stack Table naming the stack to use. + std::uint8_t interrupt_stack_table_selector : 3; + //! Reserved + std::uint8_t : 5; + //! The type of this gate. + enum gate_type gate_type : 4; + //! Reserved + std::uint8_t : 1; + //! The privilege level required to enter through this gate. + std::uint8_t descriptor_privilege_level : 2; + //! Whether this gate is present or not. + std::uint8_t present : 1; + //! The middle 16 bits of the address of this gate's handler function. + std::uint16_t offset_middle : 16; + //! The highest 32 bits of the address of this gate's handler function. + std::uint32_t offset_high : 32; + //! Reserved + std::uint32_t : 32; + }; + + static_assert(sizeof(gate_descriptor) == 2 * sizeof(std::uint64_t)); + static_assert(std::is_aggregate_v); + + //! The stack frame as established by the low-level assembly interrupt stubs. + //! + //! @note The layout of this struct is reverse than what would reasonably be expected. The reason for this reversal is + //! that fact that it represents a stack frame. Stack frames on x86_64 grow towards lower addresses, meaning the first + //! item on the stack is the last item in C++ memory layout order. + struct interrupt_frame + { + struct + { + std::uint64_t r15{}; + std::uint64_t r14{}; + std::uint64_t r13{}; + std::uint64_t r12{}; + std::uint64_t r11{}; + std::uint64_t r10{}; + std::uint64_t r9{}; + std::uint64_t r8{}; + std::uint64_t rdi{}; + std::uint64_t rsi{}; + std::uint64_t rbp{}; + std::uint64_t rdx{}; + std::uint64_t rcx{}; + std::uint64_t rbx{}; + std::uint64_t rax{}; + } handler_saved; + + struct + { + std::uint64_t number{}; + std::uint64_t error_code{}; + } interrupt; + + struct + { + kapi::memory::linear_address rip{}; + std::uint64_t cs{