diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2025-12-15 17:13:12 +0100 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2025-12-15 17:13:12 +0100 |
| commit | 7b9482ae637126ac9337876e60f519b493437711 (patch) | |
| tree | 6fc71a253c8b0325d303bd34c95b564ba536ed14 /arch/x86_64/src | |
| parent | 116f9332a206767c45095950f09f7c7447b561cf (diff) | |
| parent | a9eeec745e29d89afd48ee43d09432eb6fc35be7 (diff) | |
| download | kernel-7b9482ae637126ac9337876e60f519b493437711.tar.xz kernel-7b9482ae637126ac9337876e60f519b493437711.zip | |
os: rework kernel architecture
Rework the code structure and architecture of the kernel by separating
platform-dependent and platform-independent code more cleanly. As of
this patchset, full feature parity has not been achieved. Nonetheless, a
sufficient subset of functionality has been ported to the new
architecture to demonstrate the feasibility of the new structure.
Diffstat (limited to 'arch/x86_64/src')
68 files changed, 1357 insertions, 2844 deletions
diff --git a/arch/x86_64/src/boot/boot.s b/arch/x86_64/src/boot/boot.s deleted file mode 100644 index 7932045..0000000 --- a/arch/x86_64/src/boot/boot.s +++ /dev/null @@ -1,368 +0,0 @@ -.extern _end_physical -.extern _init -.extern kernel_main - - -/** - * Uninitialized data for the bootstrapping process. - */ -.section .boot_bss, "aw", @nobits - -/** - * Reserve some space for the Multiboot 2 information pointer. - */ -.global multiboot_information_pointer -multiboot_information_pointer: .skip 4 - -/** - * Align page maps to 4 KiB or the assembler code, will cause crashes when attempting to enable paging. - */ -.align 4096 - -/** - * Reserve space for the page maps we are going to use during startup. - * - * Note: We are going to use large pages to make the initial mapping code - * simpler. - * - * We need: - * - A single PML 4 (since we will only use 4-level paging) - * - 1 PML 3 - * - 1 PML 2 - */ - -.global page_map_level_4 -page_map_level_4: .skip 512 * 8 - -.global page_map_level_3 -page_map_level_3: .skip 512 * 8 - -.global page_map_level_2 -page_map_level_2: .skip 512 * 8 - -/** - * Stack space for the bootstrapping process. - * - * Note: We are going to reserve 1 MiB for now. If/when the kernel requires - * more space to run, it will have to relocate the stack. - */ -.section .boot_stack, "aw", @nobits -.align 16 - -stack_bottom: .skip 1 << 20 -stack_top: - -/** - * Constants for the bootstrapping process. - */ -.section .boot_rodata, "a", @progbits - -/** - * A valid Global Descriptor Table is still required in long mode. However, we - * only need a single entry for the "code segment", so we will setup a single - * segment table below. - * - * Bit 43: "E" in the access byte => mark the segment as executable. - * Bit 44: "S" in the access byte => mark the segment as code or data. - * Bit 47: "P" in the access byte => mark the segment as being present. - * Bit 53: "L" in the flags byte => mark the segment as being for long mode - */ - -global_descriptor_table: .quad 0 -global_descriptor_table_code = . - global_descriptor_table -.quad (1<<43) | (1<<44) | (1<<47) | (1<<53) - -/** - * We also need a pointer that we can load into the GDTR. - * - * The pointer consists of a word describing the size of the table minus 1 and - * the pointer to the actual table. - */ -global_descriptor_table_pointer: -.word . - global_descriptor_table - 1 -.quad global_descriptor_table - -/** - * We are going to print some messages in case we panic during boot, so we are - * going to store them here as well - */ -.global message_prefix_panic -message_prefix_panic: -.string "TeachOS 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." -mesage_long_mode_not_supported: -.string "Long mode is not supported by this platform." - -/** - * Mutable data for the bootstrapping process. - */ -.section .boot_data, "aw", @progbits - -/** - * We need a pointer to our current position in the VGA text buffer. - */ -.global vga_buffer_pointer -vga_buffer_pointer: .long 0xb8000 - -/** - * Code for the bootstrapping process. - */ -.section .boot_text, "ax", @progbits -.align 16 -.code32 - -.global halt -halt: -1: - hlt - jmp 1b - -/** - * Print a given panic message and then halt the machine. - * - * Parameters: - * - [stack - 0] message: the message to print - */ -_panic: - push %ebp - mov %esp, %ebp - - push message_prefix_panic - push $0x4f - call _print - add $8, %esp - - push 8(%ebp) - push 0x4f - call _print - add $8, %esp - - call halt - -/** - * Print a message via the VGA buffer. - * - * Parameters: - * - [stack - 4] message: the message to print - * - [stack - 0] color: the color of the message - */ -_print: - push %ebp - mov %esp, %ebp - - push %ebx - push %esi - mov 8(%ebp), %eax - mov 12(%ebp), %ebx - mov $0, %ecx - mov (vga_buffer_pointer), %esi - -.Lprint_loop: - mov (%ebx, %ecx), %dl - test %dl, %dl - je .Lupdate_vga_buffer_address - mov %dl, (%esi, %ecx, 2) - mov %al, 1(%esi, %ecx, 2) - inc %ecx - jmp .Lprint_loop - -.Lupdate_vga_buffer_address: - shl $1, %ecx - add %ecx, (vga_buffer_pointer) - -.Lprint_end: - pop %esi - pop %ebx - mov %ebp, %esp - pop %ebp - ret - -/** - * This is our entry point after being loaded by the bootloader. - * - * Having this in assembly makes it easier for us to keep things together. - */ -.global _start -_start: - mov $stack_top, %esp - mov %esp, %ebp - - call assert_loaded_by_multiboot2_loader - call assert_cpuid_instruction_is_supported - call assert_cpu_supports_long_mode - call prepare_page_maps - call enable_paging - call enable_sse - - lgdt (global_descriptor_table_pointer) - jmp $global_descriptor_table_code, $_transition_to_long_mode - - call halt - -/** - * Assert that the CPU supports going into long mode. - */ -assert_cpu_supports_long_mode: - mov $0x80000000, %eax - cpuid - cmp $0x80000001, %eax - jb .Llong_mode_assertion_failed - - mov $0x80000001, %eax - cpuid - test $(1 << 29), %edx - jz .Llong_mode_assertion_failed - ret -.Llong_mode_assertion_failed: - push $mesage_long_mode_not_supported - call _panic - -/** - * 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: - 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 - je .Lcpuid_assertion_fail - ret -.Lcpuid_assertion_fail: - push $message_cpuid_instruction_no_supported - call _panic - -/** - * 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: - cmp $0x36d76289, %eax /* Check if we received the - expected magic */ - jne .Lmultiboot2_assertion_fail /* Panic otherwise */ - mov %ebx, multiboot_information_pointer /* Store the MBI pointer */ - ret -.Lmultiboot2_assertion_fail: - push $message_not_loaded_by_multiboot2 - call _panic - -/** - * Enable paging. - * - * Note: This routine expects for there to be a valid set of page maps already - * set up for use. - */ -enable_paging: - mov $page_map_level_4, %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 - - ret - -/** - * Enable use of SSE instructions. - */ -enable_sse: - mov %cr0, %eax - and $0xfffffffb, %eax - or $0x00000002, %eax - mov %eax, %cr0 - - mov %cr4, %eax - or $(3 << 9), %eax - mov %eax, %cr4 - - ret - -/** - * Prepare the page maps. - * - * We map all physical memory we were loaded in plus one additional page. The - * mapping is done in terms of huge pages (2 MiB per page) to save on required - * page map entries. - */ -prepare_page_maps: - /* Map the P4 table recursively */ - mov $page_map_level_4, %eax - or $0b11, %eax /* Write present + writable flags into eax register */ - mov %eax, (page_map_level_4 + 511 * 8) - - /* Add an entry to the PML4, pointing to the PML3 */ - mov $page_map_level_3, %eax - or $0x3, %eax - mov %eax, (page_map_level_4 + ((0x0000000000100000 >> 39) & 0x1ff) * 8) - - /* Add an entry to the PML3, pointing to the PML2 */ - mov $page_map_level_2, %eax - or $0x3, %eax - mov %eax, (page_map_level_3 + ((0x0000000000100000 >> 30) & 0x1ff) * 8) - - xor %ecx, %ecx - - mov $_end_linear, %esi - shr $21, %esi - add $2, %esi - -.Lmap_pages: - mov $(1 << 21), %eax - mul %ecx - or $((1 << 0) | (1 << 1) | (1 << 7)), %eax - mov %eax, page_map_level_2(,%ecx,8) - - inc %ecx - cmp %esi, %ecx - jne .Lmap_pages - - ret - -.section .boot_text, "ax", @progbits -.code64 - -_transition_to_long_mode: - xor %rax, %rax - mov %rax, %ss - mov %rax, %ds - mov %rax, %es - mov %rax, %fs - mov %rax, %gs - - movl $0xb8000, (vga_buffer_pointer) - - call _init - - call kernel_main - call halt diff --git a/arch/x86_64/src/boot/boot32.S b/arch/x86_64/src/boot/boot32.S new file mode 100644 index 0000000..79b3ec7 --- /dev/null +++ b/arch/x86_64/src/boot/boot32.S @@ -0,0 +1,449 @@ +#include "x86_64/boot/boot.hpp" + +/** + * @brief Uninitialized data for the bootstrapping process. + */ +.section .boot_bss, "aw", @nobits + +/** + * @brief Storage for the multiboot2 information pointer. + */ +.global multiboot_information_pointer +multiboot_information_pointer: .skip 8 + +.align 4096 + +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 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 16(%ebp), %eax + mov %eax, 8(%ebp) + 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 recursive 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 + mov %edi, %eax + or $0b11, %eax + mov %eax, (510 * 8)(%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/src/boot/crti.s b/arch/x86_64/src/boot/crti.s deleted file mode 100644 index 26878fe..0000000 --- a/arch/x86_64/src/boot/crti.s +++ /dev/null @@ -1,13 +0,0 @@ -.code64 - -.section .init -.global _init -_init: - push %rbp - movq %rsp, %rbp - -.section .fini -.global _fini -_fini: - push %rbp - movq %rsp, %rbp diff --git a/arch/x86_64/src/boot/crtn.s b/arch/x86_64/src/boot/crtn.s deleted file mode 100644 index 06fb7ce..0000000 --- a/arch/x86_64/src/boot/crtn.s +++ /dev/null @@ -1,9 +0,0 @@ -.code64 - -.section .init - popq %rbp - ret - -.section .fini - popq %rbp - ret diff --git a/arch/x86_64/src/boot/entry64.s b/arch/x86_64/src/boot/entry64.s new file mode 100644 index 0000000..657b0a8 --- /dev/null +++ b/arch/x86_64/src/boot/entry64.s @@ -0,0 +1,60 @@ +.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 + +.align 16 +.global stack_top +stack_bottom: .skip 1 << 20 +stack_top: +stack_size = stack_top - stack_bottom + +.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/src/boot/initialize_runtime.cpp b/arch/x86_64/src/boot/initialize_runtime.cpp new file mode 100644 index 0000000..46dd5e4 --- /dev/null +++ b/arch/x86_64/src/boot/initialize_runtime.cpp @@ -0,0 +1,26 @@ +#include <algorithm> +#include <functional> +#include <span> + +extern "C" +{ + using global_initializer = auto (*)() -> void; + + extern global_initializer __ctors_start; + extern global_initializer __ctors_end; + extern global_initializer __init_array_start; + extern global_initializer __init_array_end; + + auto invoke_global_constructors() -> void + { + auto constructors = std::span{&__ctors_start, &__ctors_end}; + auto initializers = std::span{&__init_array_start, &__init_array_end}; + + auto apply_invoke = [](auto invokable) -> void { + std::invoke(invokable); + }; + + std::ranges::for_each(constructors, apply_invoke); + std::ranges::for_each(initializers, apply_invoke); + } +} diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp deleted file mode 100644 index 28f289c..0000000 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - gate_descriptor::gate_descriptor(uint128_t flags) - : _offset_1(flags) - , _selector(flags >> 19U, flags >> 16U) - , _ist(flags >> 32U) - , _flags(flags >> 40U) - , _offset_2(flags >> 48U) - { - // Nothing to do. - } - - gate_descriptor::gate_descriptor(segment_selector selector, ist_offset ist, idt_flags flags, uint64_t offset) - : _offset_1(offset) - , _selector(selector) - , _ist(ist) - , _flags(flags) - , _offset_2(offset >> 16U) - { - // Nothing to do. - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/idt_flags.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/idt_flags.cpp deleted file mode 100644 index d36a4c1..0000000 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/idt_flags.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/idt_flags.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - idt_flags::idt_flags(uint8_t flags) - : _flags(flags) - { - // Nothing to do. - } - - auto idt_flags::contains_flags(std::bitset<8U> other) const -> bool - { - return (std::bitset<8U>{_flags} & other) == other; - } - - auto idt_flags::operator|=(std::bitset<8U> other) -> void { _flags |= other.to_ulong(); } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp deleted file mode 100644 index 7aa0859..0000000 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp" - -#include "arch/exception_handling/assert.hpp" -#include "arch/interrupt_handling/generic_interrupt_handler.hpp" -#include "arch/kernel/cpu/idtr.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - namespace - { - /// @brief Amount of currently reserved interrupt indicies. - /// See https://wiki.osdev.org/Interrupt_Descriptor_Table#IDT_items for more information. - constexpr uint16_t RESERVED_INTERRUPT_COUNT = 256U; - - auto create_interrupt_descriptor_table() -> interrupt_descriptor_table - { - interrupt_descriptor_table interrupt_descriptor_table{RESERVED_INTERRUPT_COUNT}; - - uint64_t offset = reinterpret_cast<uint64_t>(interrupt_handling::generic_interrupt_handler); - segment_selector selector{1U, segment_selector::REQUEST_LEVEL_KERNEL}; - ist_offset ist{0U}; - idt_flags flags{idt_flags::DESCRIPTOR_LEVEL_KERNEL | idt_flags::INTERRUPT_GATE | idt_flags::PRESENT}; - - for (std::size_t i = 0; i < interrupt_descriptor_table.size(); i++) - { - interrupt_descriptor_table.at(i) = {selector, ist, flags, offset}; - } - - return interrupt_descriptor_table; - } - } // namespace - - auto get_or_create_interrupt_descriptor_table() -> interrupt_descriptor_table & - { - // Interrupt Descriptor Table needs to be kept alive - static interrupt_descriptor_table idt = create_interrupt_descriptor_table(); - return idt; - } - - auto update_interrupt_descriptor_table_register() -> void - { - decltype(auto) idt = get_or_create_interrupt_descriptor_table(); - - interrupt_descriptor_table_pointer idt_pointer{static_cast<uint16_t>((idt.size() * sizeof(gate_descriptor)) - 1), - idt.data()}; - kernel::cpu::load_interrupt_descriptor_table(idt_pointer); - - auto const stored_gdt_pointer = kernel::cpu::store_interrupt_descriptor_table(); - arch::exception_handling::assert( - idt_pointer == stored_gdt_pointer, - "[Interrupt Descriptor Table] Loaded IDTR value is not the same as the stored value."); - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp deleted file mode 100644 index 7bcbae6..0000000 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - interrupt_descriptor_table_pointer::interrupt_descriptor_table_pointer(uint16_t table_length, - gate_descriptor * address) - : table_length(table_length) - , address(address) - { - // Nothing to do. - } - -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/ist_offset.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/ist_offset.cpp deleted file mode 100644 index a70e75d..0000000 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/ist_offset.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/ist_offset.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - ist_offset::ist_offset(uint8_t index) - : _ist(index) - { - // Nothing to do. - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp b/arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp deleted file mode 100644 index 27f0a3b..0000000 --- a/arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - auto segment_selector::contains_flags(std::bitset<3U> other) const -> bool - { - return (std::bitset<3U>{_flags} & other) == other; - } - - auto segment_selector::get_index() const -> uint16_t { return _index; } - - auto segment_selector::operator|=(std::bitset<3U> other) -> void { _flags |= other.to_ulong(); } - - segment_selector::operator uint16_t() const { return *reinterpret_cast<uint16_t const *>(this); } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/src/context_switching/main.cpp b/arch/x86_64/src/context_switching/main.cpp deleted file mode 100644 index 9539428..0000000 --- a/arch/x86_64/src/context_switching/main.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "arch/context_switching/main.hpp" - -#include "arch/boot/pointers.hpp" -#include "arch/context_switching/syscall/syscall_enable.hpp" -#include "arch/kernel/cpu/call.hpp" -#include "arch/kernel/cpu/if.hpp" -#include "arch/kernel/cpu/segment_register.hpp" -#include "arch/kernel/cpu/tr.hpp" -#include "arch/user/main.hpp" - -namespace teachos::arch::context_switching -{ - namespace - { - constexpr interrupt_descriptor_table::segment_selector KERNEL_CODE_SEGMENT_SELECTOR{ - 1U, interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_KERNEL}; - constexpr kernel::cpu::far_pointer KERNEL_CODE_POINTER{&kernel::cpu::reload_data_segment_registers, - KERNEL_CODE_SEGMENT_SELECTOR}; - constexpr context_switching::interrupt_descriptor_table::segment_selector USER_CODE_SEGMENT_SELECTOR{ - 3U, context_switching::interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_USER}; - constexpr context_switching::interrupt_descriptor_table::segment_selector USER_DATA_SEGMENT_SELECTOR{ - 4U, context_switching::interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_USER}; - - auto reload_gdtr() -> void { kernel::cpu::call(KERNEL_CODE_POINTER); } - } // namespace - - auto initialize_descriptor_tables() -> descriptor_tables - { - static bool initalized = false; - - if (!initalized) - { - kernel::cpu::clear_interrupt_flag(); - - segment_descriptor_table::update_gdtr(); - interrupt_descriptor_table::update_interrupt_descriptor_table_register(); - - reload_gdtr(); - segment_descriptor_table::update_tss_register(); - - kernel::cpu::set_interrupt_flag(); - initalized = true; - } - - descriptor_tables tables = {segment_descriptor_table::get_or_create_gdt(), - interrupt_descriptor_table::get_or_create_interrupt_descriptor_table()}; - return tables; - } - - auto switch_to_user_mode() -> void - { - syscall::enable_syscall(); - switch_context(USER_DATA_SEGMENT_SELECTOR, USER_CODE_SEGMENT_SELECTOR, user::main); - } - - auto switch_context(interrupt_descriptor_table::segment_selector data_segment, - interrupt_descriptor_table::segment_selector code_segment, void (*return_function)()) -> void - { - (void)initialize_descriptor_tables(); - kernel::cpu::set_data_segment_registers(data_segment); - kernel::cpu::set_code_segment_register(data_segment, code_segment, reinterpret_cast<uint64_t>(return_function)); - } -} // namespace teachos::arch::context_switching diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/access_byte.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/access_byte.cpp deleted file mode 100644 index e31e021..0000000 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/access_byte.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "arch/context_switching/segment_descriptor_table/access_byte.hpp" - -namespace teachos::arch::context_switching::segment_descriptor_table -{ - access_byte::access_byte(uint8_t flags) - : _flags(flags) - { - // Nothing to do. - } - - auto access_byte::contains_flags(std::bitset<8U> other) const -> bool - { - return (std::bitset<8U>{_flags} & other) == other; - } - - auto access_byte::operator|=(std::bitset<8U> other) -> void { _flags |= other.to_ulong(); } -} // namespace teachos::arch::context_switching::segment_descriptor_table diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/gdt_flags.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/gdt_flags.cpp deleted file mode 100644 index e444a24..0000000 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/gdt_flags.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "arch/context_switching/segment_descriptor_table/gdt_flags.hpp" - -namespace teachos::arch::context_switching::segment_descriptor_table -{ - gdt_flags::gdt_flags(uint8_t flags, std::bitset<20U> limit) - : _limit_2(limit.to_ulong() >> 16U) - , _flags(flags) - { - // Nothing to do. - } - - auto gdt_flags::contains_flags(std::bitset<4U> other) const -> bool - { - return (std::bitset<4U>{_flags} & other) == other; - } - - auto gdt_flags::get_limit() const -> std::bitset<4U> { return std::bitset<4U>{_limit_2}; } - - auto gdt_flags::operator|=(std::bitset<4U> other) -> void { _flags |= other.to_ulong(); } -} // namespace teachos::arch::context_switching::segment_descriptor_table diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp deleted file mode 100644 index bbcee31..0000000 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include "arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp" - -#include "arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp" -#include "arch/exception_handling/assert.hpp" -#include "arch/kernel/cpu/gdtr.hpp" -#include "arch/kernel/cpu/tr.hpp" - -namespace teachos::arch::context_switching::segment_descriptor_table -{ - namespace - { - auto create_segment_descriptor(segment_descriptor_type segment_descriptor_type, access_byte access_level) - -> segment_descriptor_base - { - uint64_t constexpr BASE = 0x0; - std::bitset<20U> constexpr LIMIT{0xFFFFF}; - gdt_flags flags{gdt_flags::GRANULARITY, LIMIT}; - - access_level |= access_byte::PRESENT | access_byte::CODE_OR_DATA_SEGMENT; - if (segment_descriptor_type == segment_descriptor_type::CODE_SEGMENT) - { - flags |= gdt_flags::LONG_MODE; - access_level |= access_byte::CODE_SEGMENT | access_byte::READABLE; - } - else if (segment_descriptor_type == segment_descriptor_type::DATA_SEGMENT) - { - access_level |= access_byte::WRITABLE; - } - - segment_descriptor_base const segment_descriptor_base{access_level, flags, BASE, LIMIT}; - return segment_descriptor_base; - } - - auto create_tss_descriptor(task_state_segment * tss) -> segment_descriptor_extension - { - uint64_t constexpr TSS_LIMIT = sizeof(task_state_segment) - 1; - access_byte const tss_access_byte{access_byte::PRESENT | access_byte::DESCRIPTOR_LEVEL_KERNEL | - access_byte::TASK_STATE_SEGMENT_AVAILABLE}; - gdt_flags const tss_gdt_flags{0U, TSS_LIMIT}; - segment_descriptor_extension const tss_descriptor{tss_access_byte, tss_gdt_flags, reinterpret_cast<uint64_t>(tss), - TSS_LIMIT}; - return tss_descriptor; - } - - auto create_gdt() -> global_descriptor_table - { - segment_descriptor_base const null_segment{0}; - segment_descriptor_base const kernel_code_segment = - create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_byte::DESCRIPTOR_LEVEL_KERNEL); - segment_descriptor_base const kernel_data_segment = - create_segment_descriptor(segment_descriptor_type::DATA_SEGMENT, access_byte::DESCRIPTOR_LEVEL_KERNEL); - segment_descriptor_base const user_code_segment = - create_segment_descriptor(segment_descriptor_type::CODE_SEGMENT, access_byte::DESCRIPTOR_LEVEL_USER); - segment_descriptor_base const user_data_segment = - create_segment_descriptor(segment_descriptor_type::DATA_SEGMENT, access_byte::DESCRIPTOR_LEVEL_USER); - - // Task State Segment needs to be kept alive - static auto tss = new task_state_segment(); - segment_descriptor_extension const tss_descriptor = create_tss_descriptor(tss); - - global_descriptor_table global_descriptor_table{null_segment, - kernel_code_segment, - kernel_data_segment, - user_code_segment, - user_data_segment, - tss_descriptor.get_first_gdt_entry(), - tss_descriptor.get_second_gdt_entry()}; - return global_descriptor_table; - } - } // namespace - - auto get_or_create_gdt() -> global_descriptor_table & - { - // Global Descriptor Table needs to be kept alive - static global_descriptor_table gdt = create_gdt(); - return gdt; - } - - auto update_gdtr() -> void - { - decltype(auto) gdt = get_or_create_gdt(); - - // Calculate the size of the gdt in bytes - 1. This subtraction occurs because the maximum value of Size is 65535, - // while the GDT can be up to 65536 bytes in length (8192 entries). Further, no GDT can have a size of 0 bytes. - uint16_t gdt_size = static_cast<uint16_t>((gdt.size() * sizeof(segment_descriptor_base)) - 1); - global_descriptor_table_pointer gdt_pointer{gdt_size, gdt.data()}; - kernel::cpu::load_global_descriptor_table(gdt_pointer); - - auto const stored_gdt_pointer = kernel::cpu::store_global_descriptor_table(); - arch::exception_handling::assert( - gdt_pointer == stored_gdt_pointer, - "[Global Descriptor Table] Loaded GDTR value is not the same as the stored value."); - } - - auto update_tss_register() -> void - { - decltype(auto) gdt = get_or_create_gdt(); - - // Load task state segment descriptor from the last element in the global descriptor table, done by calculating - // offset in bytes to the start of the segment descriptor (5 * 8) = 40 - uint16_t tss_selector = (gdt.size() * sizeof(segment_descriptor_base)) - sizeof(segment_descriptor_extension); - kernel::cpu::load_task_register(tss_selector); - - auto const stored_task_register = kernel::cpu::store_task_register(); - arch::exception_handling::assert(tss_selector == stored_task_register, - "[Global Descriptor Table] Loaded TR value is not the same as the stored value."); - } - -} // namespace teachos::arch::context_switching::segment_descriptor_table diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp deleted file mode 100644 index 79088b8..0000000 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp" - -namespace teachos::arch::context_switching::segment_descriptor_table -{ - global_descriptor_table_pointer::global_descriptor_table_pointer(uint16_t table_length, uint64_t * address) - : table_length(table_length) - , address(address) - { - // Nothing to do. - } -} // namespace teachos::arch::context_switching::segment_descriptor_table diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp deleted file mode 100644 index 04804d9..0000000 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp" - -namespace teachos::arch::context_switching::segment_descriptor_table -{ - segment_descriptor_base::segment_descriptor_base(uint64_t flags) - : _limit_1(flags) - , _base_1(flags >> 16U) - , _access(flags >> 40U) - , _flag(flags >> 52U, flags >> 48U) - , _base_2(flags >> 56U) - { - // Nothing to do. - } - - segment_descriptor_base::segment_descriptor_base(access_byte access_byte, gdt_flags flags, uint32_t base, - std::bitset<20U> limit) - : _limit_1(limit.to_ulong()) - , _base_1(base) - , _access(access_byte) - , _flag(flags) - , _base_2(base >> 24U) - { - // Nothing to do - } - - auto segment_descriptor_base::get_segment_type() const -> segment_descriptor_type - { - if (!_access.contains_flags(access_byte::CODE_OR_DATA_SEGMENT)) - { - return segment_descriptor_type::SYSTEM_SEGMENT; - } - return _access.contains_flags(access_byte::CODE_SEGMENT) ? segment_descriptor_type::CODE_SEGMENT - : segment_descriptor_type::DATA_SEGMENT; - } - - segment_descriptor_base::operator uint64_t() const { return *reinterpret_cast<uint64_t const *>(this); } - -} // namespace teachos::arch::context_switching::segment_descriptor_table diff --git a/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp b/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp deleted file mode 100644 index a28ec9b..0000000 --- a/arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp" - -namespace teachos::arch::context_switching::segment_descriptor_table -{ - segment_descriptor_extension::segment_descriptor_extension(uint128_t flags) - : _base(flags) - , _base_3(flags >> 64U) - { - // Nothing to do. - } - - segment_descriptor_extension::segment_descriptor_extension(access_byte access_byte, gdt_flags flags, uint64_t base, - std::bitset<20U> limit) - : _base(access_byte, flags, base, limit) - , _base_3(base >> 32U) - { - // Nothing to do - } - - auto segment_descriptor_extension::get_first_gdt_entry() const -> segment_descriptor_base { return _base; } - - auto segment_descriptor_extension::get_second_gdt_entry() const -> uint64_t { return _base_3; } - -} // namespace teachos::arch::context_switching::segment_descriptor_table diff --git a/arch/x86_64/src/context_switching/syscall/main.cpp b/arch/x86_64/src/context_switching/syscall/main.cpp deleted file mode 100644 index e291c10..0000000 --- a/arch/x86_64/src/context_switching/syscall/main.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "arch/context_switching/syscall/main.hpp" - -namespace teachos::arch::context_switching::syscall -{ - auto syscall(type syscall_number, arguments args) -> response - { - asm volatile("mov %[input], %%rax" - : /* no output from call */ - : [input] "m"(syscall_number) - : "memory"); - - asm volatile("mov %[input], %%rdi " : /* no output from call */ : [input] "m"(args.arg_0) : "memory"); - asm volatile("mov %[input], %%rsi" : /* no output from call */ : [input] "m"(args.arg_1) : "memory"); - asm volatile("mov %[input], %%rdx" : /* no output from call */ : [input] "m"(args.arg_2) : "memory"); - asm volatile("mov %[input], %%r10" : /* no output from call */ : [input] "m"(args.arg_3) : "memory"); - asm volatile("mov %[input], %%r8" : /* no output from call */ : [input] "m"(args.arg_4) : "memory"); - asm volatile("mov %[input], %%r9" : /* no output from call */ : [input] "m"(args.arg_5) : "memory"); - - asm volatile("syscall"); - - arguments values{}; - asm volatile("mov %%rdi, %[output]" : [output] "=m"(values.arg_0)); - asm volatile("mov %%rsi, %[output]" : [output] "=m"(values.arg_1)); - asm volatile("mov %%rdx, %[output]" : [output] "=m"(values.arg_2)); - asm volatile("mov %%r10, %[output]" : [output] "=m"(values.arg_3)); - asm volatile("mov %%r8, %[output]" : [output] "=m"(values.arg_4)); - asm volatile("mov %%r9, %[output]" : [output] "=m"(values.arg_5)); - - error error_code{}; - asm volatile("mov %%rax, %[output]" : [output] "=m"(error_code)); - - return {error_code, values}; - } - -} // namespace teachos::arch::context_switching::syscall diff --git a/arch/x86_64/src/context_switching/syscall/syscall_enable.cpp b/arch/x86_64/src/context_switching/syscall/syscall_enable.cpp deleted file mode 100644 index 3c43336..0000000 --- a/arch/x86_64/src/context_switching/syscall/syscall_enable.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "arch/context_switching/syscall/syscall_enable.hpp" - -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" -#include "arch/context_switching/syscall/syscall_handler.hpp" -#include "arch/kernel/cpu/msr.hpp" - -namespace teachos::arch::context_switching::syscall -{ - namespace - { - interrupt_descriptor_table::segment_selector constexpr KERNEL_CODE_SEGMENT_SELECTOR{ - 1U, interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_KERNEL}; - - auto constexpr IA32_STAR_ADDRESS = 0xC0000081; - auto constexpr IA32_LSTAR_ADDRESS = 0xC0000082; - auto constexpr IA32_FMASK_ADDRESS = 0xC0000084; - - } // namespace - - auto enable_syscall() -> void - { - uint64_t const syscall_function = reinterpret_cast<uint64_t>(syscall_handler); - kernel::cpu::write_msr(IA32_LSTAR_ADDRESS, syscall_function); - kernel::cpu::write_msr(IA32_FMASK_ADDRESS, 0U); - - uint64_t const kernel_cs = KERNEL_CODE_SEGMENT_SELECTOR; - uint64_t const star_value = (kernel_cs << 32) | (kernel_cs << 48); - kernel::cpu::write_msr(IA32_STAR_ADDRESS, star_value); - - kernel::cpu::set_efer_bit(kernel::cpu::efer_flags::SCE); - } -} // namespace teachos::arch::context_switching::syscall diff --git a/arch/x86_64/src/context_switching/syscall/syscall_handler.cpp b/arch/x86_64/src/context_switching/syscall/syscall_handler.cpp deleted file mode 100644 index 84dbe5f..0000000 --- a/arch/x86_64/src/context_switching/syscall/syscall_handler.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "arch/context_switching/syscall/syscall_handler.hpp" - -#include "arch/context_switching/syscall/main.hpp" -#include "arch/exception_handling/assert.hpp" -#include "arch/exception_handling/panic.hpp" -#include "arch/memory/heap/global_heap_allocator.hpp" -#include "arch/memory/main.hpp" -#include "arch/video/vga/text.hpp" - -namespace teachos::arch::context_switching::syscall -{ - - namespace - { - auto write_to_vga_buffer(uint64_t buffer) -> response - { - video::vga::text::write(reinterpret_cast<const char *>(buffer), - video::vga::text::common_attributes::green_on_black); - video::vga::text::newline(); - return {error::OK}; - } - - auto expand_user_heap() -> response - { - static auto current_heap_end = memory::heap::USER_HEAP_START; - uint64_t const heap_start = current_heap_end; - memory::remap_heap(heap_start, memory::heap::USER_HEAP_SIZE, memory::paging::entry::USER_ACCESSIBLE); - current_heap_end += memory::heap::USER_HEAP_SIZE; - return {error::OK, {heap_start, memory::heap::USER_HEAP_SIZE}}; - } - } // namespace - - auto syscall_handler() -> void - { - // Saving state of rcx and r11 because it is required by sysretq to function. - // Calls to other functions potentially overwrite these registers, because of - // callee saved calling convention. - uint64_t return_instruction_pointer, rflags = {}; - asm volatile("mov %%rcx, %[output]" : [output] "=m"(return_instruction_pointer)); - asm volatile("mov %%r11, %[output]" : [output] "=m"(rflags)); - - uint64_t syscall_number, arg_0, arg_1, arg_2, arg_3, arg_4, arg_5 = {}; - asm volatile("mov %%rdi, %[output]" : [output] "=m"(arg_0)); - asm volatile("mov %%rsi, %[output]" : [output] "=m"(arg_1)); - asm volatile("mov %%rdx, %[output]" : [output] "=m"(arg_2)); - asm volatile("mov %%r10, %[output]" : [output] "=m"(arg_3)); - asm volatile("mov %%r8, %[output]" : [output] "=m"(arg_4)); - asm volatile("mov %%r9, %[output]" : [output] "=m"(arg_5)); - - // RAX is read last, because paired with our type enum, we can use it to check - // if the register has been written by the compiled code between executing the syscall - // and now. - asm volatile("mov %%rax, %[output]" : [output] "=m"(syscall_number)); - - response result; - switch (static_cast<type>(syscall_number)) - { - case type::WRITE: - result = write_to_vga_buffer(arg_0); - break; - case type::EXPAND_HEAP: - result = expand_user_heap(); - break; - case type::ASSERT: - teachos::arch::exception_handling::assert(arg_0, reinterpret_cast<const char *>(arg_1)); - break; - default: - teachos::arch::exception_handling::panic("[Syscall Handler] Invalid syscall number"); - break; - } - - asm volatile("mov %[input], %%rax" - : /* no output from call */ - : [input] "m"(result.error_code) - : "memory"); - - asm volatile("mov %[input], %%rdi" - : /* no output from call */ - : [input] "m"(result.values.arg_0) - : "memory"); - asm volatile("mov %[input], %%rsi" - : /* no output from call */ - : [input] "m"(result.values.arg_1) - : "memory"); - asm volatile("mov %[input], %%rdx" - : /* no output from call */ - : [input] "m"(result.values.arg_2) - : "memory"); - asm volatile("mov %[input], %%r10" - : /* no output from call */ - : [input] "m"(result.values.arg_3) - : "memory"); - asm volatile("mov %[input], %%r8" - : /* no output from call */ - : [input] "m"(result.values.arg_4) - : "memory"); - asm volatile("mov %[input], %%r9" - : /* no output from call */ - : [input] "m"(result.values.arg_5) - : "memory"); - - asm volatile("mov %[input], %%rcx" - : /* no output from call */ - : [input] "m"(return_instruction_pointer) - : "memory"); - asm volatile("mov %[input], %%r11" - : /* no output from call */ - : [input] "m"(rflags) - : "memory"); - - // Additionally call leave, because x86 allocates stack space for the internal variables. If we do not clean up this - // newly created stack frame the syscall instruction that landed in this syscall_handler, will never return to the - // method that originally called it, because the RIP has not been restored from the previous stack frame. - asm volatile("leave\n" - "sysretq"); - } - -} // namespace teachos::arch::context_switching::syscall diff --git a/arch/x86_64/src/exception_handling/abort.cpp b/arch/x86_64/src/exception_handling/abort.cpp deleted file mode 100644 index e12e4cb..0000000 --- a/arch/x86_64/src/exception_handling/abort.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "arch/exception_handling/panic.hpp" - -#include <cstdlib> - -namespace teachos::arch::exception_handling -{ - /** - * @brief Override for the newlib abort function. - * - * @note newlib defines @p ::abort as a weak symbol, thus allowing implementations to override it by simply providing - * a matching implementation. Since the default implemenatation calls a number of functions the kernel does not - * currently implement, @p ::abort gets overridden to simply panic. - */ - extern "C" auto abort() -> void { panic("Terminate was called, possibly due to an unhandled exception"); } -} // namespace teachos::arch::exception_handling diff --git a/arch/x86_64/src/exception_handling/assert.cpp b/arch/x86_64/src/exception_handling/assert.cpp deleted file mode 100644 index b2963de..0000000 --- a/arch/x86_64/src/exception_handling/assert.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "arch/exception_handling/assert.hpp" - -#include "arch/exception_handling/panic.hpp" - -namespace teachos::arch::exception_handling -{ - auto assert(bool condition, char const * message) -> void - { - if (condition) - { - return; - } - panic("Assertion Violation: ", message); - } -} // namespace teachos::arch::exception_handling diff --git a/arch/x86_64/src/exception_handling/panic.cpp b/arch/x86_64/src/exception_handling/panic.cpp deleted file mode 100644 index 8e3802a..0000000 --- a/arch/x86_64/src/exception_handling/panic.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "arch/exception_handling/panic.hpp" - -#include "arch/kernel/halt.hpp" -#include "arch/video/vga/text.hpp" - -namespace teachos::arch::exception_handling -{ - extern "C" char const message_prefix_panic[]; - - auto panic(char const * reason) -> void { panic(message_prefix_panic, reason); } - - auto panic(char const * prefix, char const * reason) -> void - { - using video::vga::text::common_attributes::white_on_red; - - video::vga::text::newline(); - video::vga::text::write(prefix, white_on_red); - video::vga::text::write(reason, white_on_red); - - kernel::halt(); - }; -} // namespace teachos::arch::exception_handling diff --git a/arch/x86_64/src/exception_handling/pure_virtual.cpp b/arch/x86_64/src/exception_handling/pure_virtual.cpp deleted file mode 100644 index 67772f7..0000000 --- a/arch/x86_64/src/exception_handling/pure_virtual.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "arch/exception_handling/panic.hpp" - -extern "C" auto __cxa_pure_virtual() -> void -{ - teachos::arch::exception_handling::panic("Runtime", "Tried to call a pure virtual function!"); -} diff --git a/arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp b/arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp deleted file mode 100644 index 9d061a8..0000000 --- a/arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "arch/interrupt_handling/generic_interrupt_handler.hpp" - -#include "arch/video/vga/text.hpp" - -namespace teachos::arch::interrupt_handling -{ - auto generic_interrupt_handler(interrupt_frame * frame) -> void - { - (void)frame; - video::vga::text::write("An Interrupt occurred.", video::vga::text::common_attributes::green_on_black); - video::vga::text::newline(); - } -} // namespace teachos::arch::interrupt_handling diff --git a/arch/x86_64/src/kapi/cio.cpp b/arch/x86_64/src/kapi/cio.cpp new file mode 100644 index 0000000..ade02aa --- /dev/null +++ b/arch/x86_64/src/kapi/cio.cpp @@ -0,0 +1,20 @@ +#include "kapi/cio.hpp" + +#include "x86_64/vga/text.hpp" + +#include <optional> + +namespace teachos::cio +{ + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + auto static constinit vga_device = std::optional<vga::x86_64::text::device>{}; + + auto init() -> void + { + vga_device.emplace(); + vga_device->cursor(false); + set_output_device(*vga_device); + } + +} // namespace teachos::cio diff --git a/arch/x86_64/src/kapi/cpu.cpp b/arch/x86_64/src/kapi/cpu.cpp new file mode 100644 index 0000000..22543ee --- /dev/null +++ b/arch/x86_64/src/kapi/cpu.cpp @@ -0,0 +1,12 @@ +#include "kapi/cpu.hpp" + +namespace teachos::cpu +{ + + auto halt() -> void + { + asm volatile("1: hlt\njmp 1b"); + __builtin_unreachable(); + } + +} // namespace teachos::cpu diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp new file mode 100644 index 0000000..8c53c4c --- /dev/null +++ b/arch/x86_64/src/kapi/memory.cpp @@ -0,0 +1,193 @@ +#include "kapi/memory.hpp" + +#include "kapi/boot.hpp" +#include "kapi/cio.hpp" +#include "kapi/system.hpp" + +#include "x86_64/boot/boot.hpp" +#include "x86_64/boot/ld.hpp" +#include "x86_64/cpu/registers.hpp" +#include "x86_64/memory/buffered_allocator.hpp" +#include "x86_64/memory/kernel_mapper.hpp" +#include "x86_64/memory/mmu.hpp" +#include "x86_64/memory/page_table.hpp" +#include "x86_64/memory/page_utilities.hpp" +#include "x86_64/memory/paging_root.hpp" +#include "x86_64/memory/recursive_page_mapper.hpp" +#include "x86_64/memory/region_allocator.hpp" +#include "x86_64/memory/scoped_mapping.hpp" + +#include <multiboot2/information.hpp> + +#include <atomic> +#include <bit> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <optional> +#include <span> +#include <utility> + +namespace teachos::memory +{ + + namespace + { + constexpr auto static unused_page_address = linear_address{0x0000'7fff'cafe'faceuz}; + constexpr auto static recursive_page_map_index = x86_64::page_table::entry_count - 2; + + //! Instantiate a basic, memory region based, early frame allocator for remapping. + auto collect_memory_information() + { + auto memory_map = boot::bootstrap_information.mbi->maybe_memory_map(); + if (!memory_map) + { + system::panic("[x86_64] Failed to create early allocator, no memory map available."); + } + + auto const & mbi = boot::bootstrap_information.mbi; + auto mbi_span = std::span{std::bit_cast<std::byte *>(mbi), mbi->size_bytes()}; + auto image_span = std::span{&boot::x86_64::_start_physical, &boot::x86_64::_end_physical}; + + return x86_64::region_allocator::memory_information{ + .image_range = std::make_pair(physical_address{&image_span.front()}, physical_address{&image_span.back()}), + .mbi_range = std::make_pair(physical_address{&mbi_span.front()}, physical_address{&mbi_span.back()}), + .memory_map = *memory_map, + }; + } + + //! Enable additional CPU protection features, required during later stages of the kernel. + auto enable_cpu_protections() -> void + { + cpu::x86_64::cr0::set(cpu::x86_64::cr0::flags::write_protect); + cpu::x86_64::i32_efer::set(cpu::x86_64::i32_efer::flags::execute_disable_bit_enable); + } + + //! Inject, or graft, a faux recursive PML4 into the active page mapping structure. + auto inject_faux_pml4(frame_allocator & allocator, page_mapper & mapper) + { + using namespace x86_64; + using entry_flags = page_table::entry::flags; + + auto page = page::containing(unused_page_address); + + auto temporary_mapper = scoped_mapping{page, mapper}; + auto new_pml4_frame = allocator.allocate(); + + auto pml4 = std::construct_at(temporary_mapper.map_as<page_table>(*new_pml4_frame, entry_flags::writable)); + (*pml4)[recursive_page_map_index].frame(new_pml4_frame.value(), entry_flags::present | entry_flags::writable); + + auto pml4_index = pml_index<4>(page); + auto old_pml4 = paging_root::get(); + auto pml4_entry = (*old_pml4)[pml4_index]; + + auto pml3_index = pml_index<3>(page); + auto old_pml3 = old_pml4->next(pml4_index); + auto pml3_entry = (**old_pml3)[pml3_index]; + + auto pml2_index = pml_index<2>(page); + auto old_pml2 = (**old_pml3).next(pml3_index); + auto pml2_entry = (**old_pml2)[pml2_index]; + + auto pml1_index = pml_index<1>(page); + auto old_pml1 = (**old_pml2).next(pml2_index); + auto pml1_entry = (**old_pml1)[pml1_index]; + + (*paging_root::get())[recursive_page_map_index].frame(new_pml4_frame.value(), + entry_flags::present | entry_flags::writable); + + tlb_flush_all(); + + auto new_pml4 = paging_root::get(); + (*new_pml4)[pml4_index] = pml4_entry; + + auto new_pml3 = new_pml4->next(pml4_index); + (**new_pml3)[pml3_index] = pml3_entry; + + auto new_pml2 = (**new_pml3).next(pml3_index); + (**new_pml2)[pml2_index] = pml2_entry; + + auto new_pml1 = (**new_pml2).next(pml2_index); + (**new_pml1)[pml1_index] = pml1_entry; + + return *new_pml4_frame; + } + + auto remap_kernel(page_mapper & mapper) -> void + { + auto kernel_mapper = x86_64::kernel_mapper{boot::bootstrap_information.mbi}; + kernel_mapper.remap_kernel(mapper); + } + + auto remap_vga_text_mode_buffer(page_mapper & mapper) -> void + { + constexpr auto vga_base = std::uintptr_t{0xb8000}; + auto vga_physical_start = physical_address{vga_base}; + auto vga_virtual_start = linear_address{vga_base + std::bit_cast<std::uintptr_t>(&boot::x86_64::TEACHOS_VMA)}; + + auto page = page::containing(vga_virtual_start); + auto frame = frame::containing(vga_physical_start); + + mapper.map(page, frame, page_mapper::flags::writable); + } + + auto remap_multiboot_information(page_mapper & mapper) -> void + { + auto mbi_base = std::bit_cast<std::uintptr_t>(boot::bootstrap_information.mbi); + auto mbi_size = boot::bootstrap_information.mbi->size_bytes(); + auto mbi_physical_start = physical_address{mbi_base & ~std::bit_cast<std::uintptr_t>(&boot::x86_64::TEACHOS_VMA)}; + auto mbi_virtual_start = linear_address{mbi_base}; + auto mbi_block_count = (mbi_size + PLATFORM_FRAME_SIZE - 1) / PLATFORM_FRAME_SIZE; + + for (auto i = 0uz; i < mbi_block_count; ++i) + { + auto page = page::containing(mbi_virtual_start) + 1; + auto frame = frame::containing(mbi_physical_start) + 1; + mapper.map(page, frame, page_mapper::flags::empty); + } + } + + } // namespace + + // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) + auto constinit region_based_allocator = std::optional<x86_64::region_allocator>{}; + auto constinit buffered_allocator = std::optional<x86_64::buffered_allocator<4>>{}; + auto constinit recursive_page_mapper = std::optional<x86_64::recursive_page_mapper>{}; + // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) + + auto init() -> void + { + auto static constinit is_initialized = std::atomic_flag{}; + + if (is_initialized.test_and_set()) + { + system::panic("[x86_64] Memory management has already been initialized."); + } + + cio::println("[x86_64:MEM] Enabling additional CPU protection features."); + + enable_cpu_protections(); + + region_based_allocator.emplace(collect_memory_information()); + buffered_allocator.emplace(&*region_based_allocator); + recursive_page_mapper.emplace(*buffered_allocator); + + cio::println("[x86_64:MEM] Preparing new paging hierarchy."); + + auto new_pml4_frame = inject_faux_pml4(*buffered_allocator, *recursive_page_mapper); + + remap_kernel(*recursive_page_mapper); + remap_vga_text_mode_buffer(*recursive_page_mapper); + remap_multiboot_information(*recursive_page_mapper); + + cio::println("[x86_64:MEM] Switching to new paging hierarchy."); + + auto cr3 = cpu::x86_64::cr3::read(); + cr3.address(new_pml4_frame.start_address()); + cpu::x86_64::cr3::write(cr3); + + set_frame_allocator(*buffered_allocator); + set_page_mapper(*recursive_page_mapper); + } + +} // namespace teachos::memory diff --git a/arch/x86_64/src/kernel/cpu/call.cpp b/arch/x86_64/src/kernel/cpu/call.cpp deleted file mode 100644 index 98fa248..0000000 --- a/arch/x86_64/src/kernel/cpu/call.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "arch/kernel/cpu/call.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto call(far_pointer pointer) -> void - { - asm volatile("rex64 lcall *%[input]" : /* no output from call */ : [input] "m"(pointer)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/control_register.cpp b/arch/x86_64/src/kernel/cpu/control_register.cpp deleted file mode 100644 index 41b8cd7..0000000 --- a/arch/x86_64/src/kernel/cpu/control_register.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "arch/kernel/cpu/control_register.hpp" - -#include "arch/exception_handling/panic.hpp" - -#include <type_traits> - -namespace teachos::arch::kernel::cpu -{ - auto read_control_register(control_register cr) -> uint64_t - { - uint64_t current_value; - switch (cr) - { - case control_register::CR0: - asm volatile("mov %%cr0, %[output]" : [output] "=r"(current_value)); - break; - case control_register::CR2: - asm volatile("mov %%cr2, %[output]" : [output] "=r"(current_value)); - break; - case control_register::CR3: - asm volatile("mov %%cr3, %[output]" : [output] "=r"(current_value)); - break; - case control_register::CR4: - asm volatile("mov %%cr4, %[output]" : [output] "=r"(current_value)); - break; - } - return current_value; - } - - auto write_control_register(control_register cr, uint64_t new_value) -> void - { - switch (cr) - { - case control_register::CR0: - asm volatile("mov %[input], %%cr0" - : /* no output from call */ - : [input] "r"(new_value) - : "memory"); - break; - case control_register::CR2: - asm volatile("mov %[input], %%cr2" - : /* no output from call */ - : [input] "r"(new_value) - : "memory"); - break; - case control_register::CR3: - asm volatile("mov %[input], %%cr3" - : /* no output from call */ - : [input] "r"(new_value) - : "memory"); - break; - case control_register::CR4: - asm volatile("mov %[input], %%cr4" - : /* no output from call */ - : [input] "r"(new_value) - : "memory"); - break; - } - } - - auto set_cr0_bit(cr0_flags flag) -> void - { - auto const cr0 = read_control_register(control_register::CR0); - write_control_register(control_register::CR0, static_cast<std::underlying_type<cr0_flags>::type>(flag) | cr0); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/gdtr.cpp b/arch/x86_64/src/kernel/cpu/gdtr.cpp deleted file mode 100644 index 74a4e1c..0000000 --- a/arch/x86_64/src/kernel/cpu/gdtr.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "arch/kernel/cpu/gdtr.hpp" - -#include "arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto store_global_descriptor_table() -> context_switching::segment_descriptor_table::global_descriptor_table_pointer - { - context_switching::segment_descriptor_table::global_descriptor_table_pointer current_value{}; - asm("sgdt %[output]" : [output] "=m"(current_value)); - return current_value; - } - - auto load_global_descriptor_table( - context_switching::segment_descriptor_table::global_descriptor_table_pointer const & gdt_pointer) -> void - { - asm volatile("lgdt %[input]" : /* no output from call */ : [input] "m"(gdt_pointer)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/idtr.cpp b/arch/x86_64/src/kernel/cpu/idtr.cpp deleted file mode 100644 index 7aa20c1..0000000 --- a/arch/x86_64/src/kernel/cpu/idtr.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "arch/kernel/cpu/idtr.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto store_interrupt_descriptor_table() - -> context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer - { - context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer current_value{}; - asm("sidt %[output]" : [output] "=m"(current_value)); - return current_value; - } - - auto load_interrupt_descriptor_table( - context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer const & idt_pointer) -> void - { - asm volatile("lidt %[input]" : /* no output from call */ : [input] "m"(idt_pointer)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/if.cpp b/arch/x86_64/src/kernel/cpu/if.cpp deleted file mode 100644 index 60a90a3..0000000 --- a/arch/x86_64/src/kernel/cpu/if.cpp +++ /dev/null @@ -1,7 +0,0 @@ -namespace teachos::arch::kernel::cpu -{ - auto set_interrupt_flag() -> void { asm volatile("sti"); } - - auto clear_interrupt_flag() -> void { asm volatile("cli"); } - -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/msr.cpp b/arch/x86_64/src/kernel/cpu/msr.cpp deleted file mode 100644 index 9c474a1..0000000 --- a/arch/x86_64/src/kernel/cpu/msr.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "arch/kernel/cpu/msr.hpp" - -namespace teachos::arch::kernel::cpu -{ - namespace - { - auto constexpr IA32_EFER_ADDRESS = 0xC0000080; - } - - auto read_msr(uint32_t msr) -> uint64_t - { - uint32_t low, high; - asm volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(msr)); - return (static_cast<uint64_t>(high) << 32) | low; - } - - auto write_msr(uint32_t msr, uint64_t value) -> void - { - uint32_t low = value; - uint32_t high = value >> 32; - asm volatile("wrmsr" - : /* no output from call */ - : "c"(msr), "a"(low), "d"(high)); - } - - auto set_efer_bit(efer_flags flag) -> void - { - auto const efer = read_msr(IA32_EFER_ADDRESS); - write_msr(IA32_EFER_ADDRESS, static_cast<std::underlying_type<efer_flags>::type>(flag) | efer); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/segment_register.cpp b/arch/x86_64/src/kernel/cpu/segment_register.cpp deleted file mode 100644 index b08c9c4..0000000 --- a/arch/x86_64/src/kernel/cpu/segment_register.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "arch/kernel/cpu/segment_register.hpp" - -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" -#include "arch/exception_handling/assert.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto reload_data_segment_registers() -> void - { - asm volatile("xor %%rax, %%rax\n" - "mov %%rax, %%ss\n" - "mov %%rax, %%ds\n" - "mov %%rax, %%es\n" - "mov %%rax, %%fs\n" - "mov %%rax, %%gs\n" - : /* no output from call */ - : /* no input to call */ - : "rax"); - } - - auto set_data_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment) -> void - { - asm volatile("xor %%rax, %%rax\n" - "mov %[input], %%ax\n" - "mov %%rax, %%ds\n" - "mov %%rax, %%es\n" - "mov %%rax, %%fs\n" - "mov %%rax, %%gs\n" - : /* no output from call */ - : [input] "m"(data_segment) - : "rax"); - } - - auto read_code_segment_register() -> context_switching::interrupt_descriptor_table::segment_selector - { - context_switching::interrupt_descriptor_table::segment_selector current_value{}; - asm volatile("mov %%cs, %[output]" : [output] "=r"(current_value)); - return current_value; - } - - auto validate_data_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment) - -> void - { - context_switching::interrupt_descriptor_table::segment_selector ss{}; - context_switching::interrupt_descriptor_table::segment_selector ds{}; - context_switching::interrupt_descriptor_table::segment_selector es{}; - context_switching::interrupt_descriptor_table::segment_selector fs{}; - context_switching::interrupt_descriptor_table::segment_selector gs{}; - - asm volatile( - "mov %%ss, %[ss_output]\n" - "mov %%ds, %[ds_output]\n" - "mov %%es, %[es_output]\n" - "mov %%fs, %[fs_output]\n" - "mov %%gs, %[gs_output]\n" - : [ss_output] "=r"(ss), [ds_output] "=r"(ds), [es_output] "=r"(es), [fs_output] "=r"(fs), [gs_output] "=r"(gs)); - - auto result = (ss == ds && ss == es && ss == fs && ss == gs); - exception_handling::assert(result, "[Segment Register] Values in data register are not the same."); - result = (ss == data_segment); - exception_handling::assert( - result, "[Segment Register] Expected Data Segment is not the same as the value in the Stack Segment register."); - } - - auto validate_code_segment_register(context_switching::interrupt_descriptor_table::segment_selector code_segment) - -> void - { - auto const cs = read_code_segment_register(); - exception_handling::assert( - cs == code_segment, - "[Segment Register] Expected Code Segment is not the same as the value in the Code Segment register."); - } - - auto validate_segment_registers(context_switching::interrupt_descriptor_table::segment_selector data_segment, - context_switching::interrupt_descriptor_table::segment_selector code_segment) -> void - { - validate_data_segment_registers(data_segment); - validate_code_segment_register(code_segment); - } - - auto set_code_segment_register(context_switching::interrupt_descriptor_table::segment_selector data_segment, - context_switching::interrupt_descriptor_table::segment_selector code_segment, - uint64_t address) -> void - { - asm volatile("mov %%rsp, %%rax\n" - "push %[data_segment]\n" - "push %%rax\n" - "pushfq\n" - "push %[code_segment]\n" - "mov %[return_function], %%rax\n" - "push %%rax\n" - "iretq\n" - : /* no output from call */ - : [data_segment] "m"(data_segment), [code_segment] "m"(code_segment), [return_function] "r"(address) - : "rax"); - } - -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/tlb.cpp b/arch/x86_64/src/kernel/cpu/tlb.cpp deleted file mode 100644 index a09001c..0000000 --- a/arch/x86_64/src/kernel/cpu/tlb.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "arch/kernel/cpu/tlb.hpp" - -#include "arch/kernel/cpu/control_register.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto tlb_flush(memory::paging::virtual_address address) -> void - { - asm volatile("invlpg (%[input])" : /* no output from call */ : [input] "r"(address) : "memory"); - } - - auto tlb_flush_all() -> void - { - write_control_register(cpu::control_register::CR3, read_control_register(cpu::control_register::CR3)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/cpu/tr.cpp b/arch/x86_64/src/kernel/cpu/tr.cpp deleted file mode 100644 index a435540..0000000 --- a/arch/x86_64/src/kernel/cpu/tr.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "arch/kernel/cpu/tr.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto store_task_register() -> uint16_t - { - uint16_t current_value{}; - asm("str %[output]" : [output] "=r"(current_value)); - return current_value; - } - - auto load_task_register(uint16_t gdt_offset) -> void - { - asm volatile("ltr %[input]" : /* no output from call */ : [input] "m"(gdt_offset)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/src/kernel/main.cpp b/arch/x86_64/src/kernel/main.cpp deleted file mode 100644 index 43b5f90..0000000 --- a/arch/x86_64/src/kernel/main.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "arch/kernel/main.hpp" - -#include "arch/boot/pointers.hpp" -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" -#include "arch/context_switching/main.hpp" -#include "arch/kernel/cpu/if.hpp" -#include "arch/kernel/cpu/segment_register.hpp" -#include "arch/memory/heap/bump_allocator.hpp" -#include "arch/memory/heap/global_heap_allocator.hpp" -#include "arch/memory/main.hpp" -#include "arch/memory/multiboot/reader.hpp" -#include "arch/stl/vector.hpp" -#include "arch/video/vga/text.hpp" - -namespace teachos::arch::kernel -{ - auto stack_overflow_test(int count) -> int - { - int test[5000] = {}; - if (test[0] == 0xFFFF) - { - return count; - } - count = stack_overflow_test(count); - return count++; - } - - auto heap_test() -> void - { - auto test2 = new memory::multiboot::memory_information{}; - auto test3 = new memory::multiboot::memory_information{}; - auto test4 = *test2; - auto test5 = *test3; - test4.kernel_end = 5000; - test5.kernel_end = 3000; - auto test6 = test4.kernel_end; - auto test7 = test5.kernel_end; - auto test8 = memory::multiboot::read_multiboot2(); - if (test6 && test7 && test8.kernel_end) - { - video::vga::text::write("Heap test successful", video::vga::text::common_attributes::green_on_black); - } - test2->kernel_end = 2000; - test2->kernel_start = 1000; - test2->multiboot_start = 2000; - delete test2; - delete test3; - - auto test9 = new int(50); - delete test9; - } - - auto main() -> void - { - video::vga::text::clear(); - video::vga::text::cursor(false); - video::vga::text::write("TeachOS is starting up...", video::vga::text::common_attributes::green_on_black); - video::vga::text::newline(); - - memory::initialize_memory_management(); - // stack_overflow_test(0); - - memory::heap::global_heap_allocator::register_heap_allocator(memory::heap::heap_allocator_type::LINKED_LIST); - // heap_test(); - - auto address = memory::heap::global_heap_allocator::kmalloc(8U); - (void)address; - - context_switching::switch_to_user_mode(); - } -} // namespace teachos::arch::kernel diff --git a/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp b/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp deleted file mode 100644 index a5a1b49..0000000 --- a/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "arch/memory/allocator/area_frame_allocator.hpp" - -#include "arch/exception_handling/assert.hpp" - -#include <algorithm> -#include <array> -#include <ranges> - -namespace teachos::arch::memory::allocator -{ - area_frame_allocator::area_frame_allocator(multiboot::memory_information const & mem_info) - : next_free_frame() - , current_area(std::nullopt) - , memory_areas(mem_info.areas) - , kernel_start(physical_frame::containing_address(mem_info.kernel_start)) - , kernel_end(physical_frame::containing_address(mem_info.kernel_end)) - , multiboot_start(physical_frame::containing_address(mem_info.multiboot_start)) - , multiboot_end(physical_frame::containing_address(mem_info.multiboot_end)) - { - choose_next_area(); - } - - auto area_frame_allocator::choose_next_area() -> void - { - current_area = std::nullopt; - auto next_area_with_free_frames = memory_areas | std::views::filter([this](auto const & area) { - auto address = area.base_address + area.area_length - 1; - return physical_frame::containing_address(address) >= next_free_frame; - }); - - auto const lowest_area_with_free_frames = std::ranges::min_element( - next_area_with_free_frames, [](auto const & a, auto const & b) { return a.base_address < b.base_address; }); - - if (lowest_area_with_free_frames != next_area_with_free_frames.end()) - { - current_area = *lowest_area_with_free_frames; - // Update the `next_free_frame` according to the new memory area - auto const start_frame = physical_frame::containing_address(current_area.value().base_address); - if (next_free_frame < start_frame) - { - next_free_frame = start_frame; - } - } - } - - auto area_frame_allocator::allocate_frame() -> std::optional<physical_frame> - { - // Only try to allocate memory if current_area is not null, because - // the current_area is null if there is no more available memory. - if (!current_area.has_value()) - { - return std::nullopt; - } - - auto const address = current_area.value().base_address + current_area.value().area_length - 1; - physical_frame current_area_last_frame = physical_frame::containing_address(address); - - if (next_free_frame > current_area_last_frame) - { - // All frames of current area are used, switch to next area. - choose_next_area(); - } - else if (next_free_frame >= kernel_start && next_free_frame <= kernel_end) - { - // `physical_frame` is used by the kernel or multiboot information structure. - next_free_frame = allocator::physical_frame{kernel_end.frame_number + 1}; - } - else if (next_free_frame >= multiboot_start && next_free_frame <= multiboot_end) - { - // `physical_frame` is used by the kernel or multiboot information structure. - next_free_frame = allocator::physical_frame{multiboot_end.frame_number + 1}; - } - else - { - // Frame is unused, increment `next_free_frame` and return it. - next_free_frame.frame_number += 1; - return next_free_frame; - } - - // `physical_frame` was not valid, try it again with the updated `next_free_frame`. - return allocate_frame(); - } - - auto area_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void { (void)physical_frame; } -} // namespace teachos::arch::memory::allocator diff --git a/arch/x86_64/src/memory/allocator/physical_frame.cpp b/arch/x86_64/src/memory/allocator/physical_frame.cpp deleted file mode 100644 index ec387a1..0000000 --- a/arch/x86_64/src/memory/allocator/physical_frame.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "arch/memory/allocator/physical_frame.hpp" - -namespace teachos::arch::memory::allocator -{ - auto physical_frame::containing_address(physical_address address) -> physical_frame - { - return physical_frame{address / PAGE_FRAME_SIZE}; - } - - auto physical_frame::start_address() const -> physical_address { return frame_number * PAGE_FRAME_SIZE; } - - auto physical_frame::operator++(int) -> physical_frame - { - physical_frame const old_value = *this; - ++frame_number; - return old_value; - } - - auto physical_frame::operator++() -> physical_frame & - { - ++frame_number; - return *this; - } -} // namespace teachos::arch::memory::allocator diff --git a/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp b/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp deleted file mode 100644 index 3cdf9c7..0000000 --- a/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "arch/memory/allocator/tiny_frame_allocator.hpp" - -#include "arch/exception_handling/panic.hpp" - -namespace teachos::arch::memory::allocator -{ - auto tiny_frame_allocator::allocate_frame() -> std::optional<physical_frame> - { - for (auto & frame_option : frames) - { - if (frame_option.has_value()) - { - auto value = frame_option; - frame_option.reset(); - return value; - } - } - return std::nullopt; - } - - auto tiny_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void - { - for (auto & frame_option : frames) - { - if (!frame_option.has_value()) - { - frame_option.emplace(physical_frame); - return; - } - } - exception_handling::panic( - "[Tiny Frame Allocator] Attempted to deallocate more than the 3 frames, that can be held"); - } -} // namespace teachos::arch::memory::allocator diff --git a/arch/x86_64/src/memory/heap/bump_allocator.cpp b/arch/x86_64/src/memory/heap/bump_allocator.cpp deleted file mode 100644 index 525f45c..0000000 --- a/arch/x86_64/src/memory/heap/bump_allocator.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "arch/memory/heap/bump_allocator.hpp" - -#include "arch/exception_handling/assert.hpp" - -#include <limits> -#include <type_traits> - -namespace teachos::arch::memory::heap -{ - namespace - { - template<typename T> - auto saturating_add(T x, T y) -> T - requires std::is_unsigned_v<T> - { - if (x > std::numeric_limits<T>::max() - y) - { - return std::numeric_limits<T>::max(); - } - T result = x + y; - return result; - } - } // namespace - - auto bump_allocator::allocate(std::size_t size) -> void * - { - // Reading the value only has to be done once, because compare_exchange_weak updates the value as well if the - // exchange failed, becuase the value was not the expected one. - auto alloc_start = next.load(std::memory_order::relaxed); - // Repeat allocation until it succeeds, has to be done, because another allocator could overtake it at any time - // causing the value to differ and the calculation to have to be redone. - for (;;) - { - auto const alloc_end = saturating_add(alloc_start, size); - arch::exception_handling::assert(alloc_end <= heap_end, "[Heap Allocator] Out of memory"); - // Check if the atomic value is still the one initally loaded, if it isn't we have been overtaken by another - // thread and need to redo the calculation. Spurious failure by weak can be ignored, because the whole allocation - // is wrapped in an infinite for loop so a failure that wasn't actually one will simply be retried until it works. - auto const updated = next.compare_exchange_weak(alloc_start, alloc_end, std::memory_order::relaxed); - if (updated) - { - return reinterpret_cast<void *>(alloc_start); - } - } - } - - auto bump_allocator::deallocate(void * pointer) noexcept -> void - { - if (pointer) - { - } - } - -} // namespace teachos::arch::memory::heap diff --git a/arch/x86_64/src/memory/heap/global_heap_allocator.cpp b/arch/x86_64/src/memory/heap/global_heap_allocator.cpp deleted file mode 100644 index 35cd623..0000000 --- a/arch/x86_64/src/memory/heap/global_heap_allocator.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "arch/memory/heap/global_heap_allocator.hpp" - -#include "arch/context_switching/syscall/main.hpp" -#include "arch/exception_handling/assert.hpp" -#include "arch/kernel/cpu/segment_register.hpp" -#include "arch/memory/heap/bump_allocator.hpp" -#include "arch/memory/heap/linked_list_allocator.hpp" -#include "arch/memory/heap/user_heap_allocator.hpp" - -namespace teachos::arch::memory::heap -{ - namespace - { - constexpr char NOT_REGISTRED_ERROR_MESSAGE[] = - "Attempted to allocate or deallocate using the global_heap_allocator before " - "register_heap_allocation_type was called."; - constexpr uint16_t KERNEL_CODE_INDEX = 1U; - - [[gnu::section(".user_text")]] - auto os_in_kernel_mode() -> bool - { - auto const cs = teachos::arch::kernel::cpu::read_code_segment_register(); - return cs.get_index() == KERNEL_CODE_INDEX; - } - } // namespace - - heap_allocator * global_heap_allocator::kernel_allocator_instance = nullptr; - user_heap_allocator * global_heap_allocator::user_allocator_instance = nullptr; - - auto global_heap_allocator::kmalloc(std::size_t size) -> void * { return kernel().allocate(size); } - - auto global_heap_allocator::kfree(void * pointer) noexcept -> void { kernel().deallocate(pointer); } - - auto global_heap_allocator::malloc(std::size_t size) -> void * { return user().allocate(size); } - - auto global_heap_allocator::free(void * pointer) noexcept -> void { user().deallocate(pointer); } - - auto global_heap_allocator::register_heap_allocator(heap_allocator_type new_type) -> void - { - exception_handling::assert(kernel_allocator_instance == nullptr, - "Calling register_heap_allocator_type can only be done once."); - - switch (new_type) - { - case heap_allocator_type::NONE: - // Nothing to do - break; - case heap_allocator_type::BUMP: { - static bump_allocator kernel_allocator{KERNEL_HEAP_START, KERNEL_HEAP_START + KERNEL_HEAP_SIZE}; - kernel_allocator_instance = &kernel_allocator; - break; - } - case heap_allocator_type::LINKED_LIST: { - static linked_list_allocator kernel_allocator{KERNEL_HEAP_START, KERNEL_HEAP_START + KERNEL_HEAP_SIZE}; - kernel_allocator_instance = &kernel_allocator; - break; - } - } - - [[gnu::section(".user_data")]] - static user_heap_allocator user_allocator{}; - user_allocator_instance = &user_allocator; - } - - auto global_heap_allocator::kernel() -> heap_allocator & - { - exception_handling::assert(kernel_allocator_instance != nullptr, NOT_REGISTRED_ERROR_MESSAGE); - - return *kernel_allocator_instance; - } - - auto global_heap_allocator::user() -> user_heap_allocator & - { - context_switching::syscall::syscall( - context_switching::syscall::type::ASSERT, - {user_allocator_instance != nullptr, reinterpret_cast<uint64_t>(&NOT_REGISTRED_ERROR_MESSAGE)}); - return *user_allocator_instance; - } -} // namespace teachos::arch::memory::heap - -auto operator new(std::size_t size) -> void * -{ - if (teachos::arch::memory::heap::os_in_kernel_mode()) - { - return teachos::arch::memory::heap::global_heap_allocator::kmalloc(size); - } - return teachos::arch::memory::heap::global_heap_allocator::malloc(size); -} - -auto operator delete(void * pointer) noexcept -> void -{ - if (teachos::arch::memory::heap::os_in_kernel_mode()) - { - teachos::arch::memory::heap::global_heap_allocator::kfree(pointer); - } - teachos::arch::memory::heap::global_heap_allocator::free(pointer); -} - -auto operator delete(void * pointer, std::size_t size) noexcept -> void -{ - (void)size; - if (teachos::arch::memory::heap::os_in_kernel_mode()) - { - teachos::arch::memory::heap::global_heap_allocator::kfree(pointer); - } - teachos::arch::memory::heap::global_heap_allocator::free(pointer); -} - -auto operator new[](std::size_t size) -> void * -{ - if (teachos::arch::memory::heap::os_in_kernel_mode()) - { - return teachos::arch::memory::heap::global_heap_allocator::kmalloc(size); - } - return teachos::arch::memory::heap::global_heap_allocator::malloc(size); -} - -auto operator delete[](void * pointer) noexcept -> void -{ - if (teachos::arch::memory::heap::os_in_kernel_mode()) - { - teachos::arch::memory::heap::global_heap_allocator::kfree(pointer); - } - teachos::arch::memory::heap::global_heap_allocator::free(pointer); -} - -auto operator delete[](void * pointer, std::size_t size) noexcept -> void -{ - (void)size; - if (teachos::arch::memory::heap::os_in_kernel_mode()) - { - teachos::arch::memory::heap::global_heap_allocator::kfree(pointer); - } - teachos::arch::memory::heap::global_heap_allocator::free(pointer); -} diff --git a/arch/x86_64/src/memory/heap/linked_list_allocator.cpp b/arch/x86_64/src/memory/heap/linked_list_allocator.cpp deleted file mode 100644 index 63a6111..0000000 --- a/arch/x86_64/src/memory/heap/linked_list_allocator.cpp +++ /dev/null @@ -1,177 +0,0 @@ -#include "arch/memory/heap/linked_list_allocator.hpp" - -#include "arch/exception_handling/assert.hpp" -#include "arch/exception_handling/panic.hpp" - -#include <algorithm> - -namespace teachos::arch::memory::heap -{ - linked_list_allocator::linked_list_allocator(std::size_t heap_start, std::size_t heap_end) - : first(nullptr) - , mutex{stl::mutex{}} - { - auto const heap_size = heap_end - heap_start; - exception_handling::assert( - heap_size > min_allocatable_size(), - "[Linked List Allocator] Total heap size can not be smaller than minimum of 16 bytes to hold " - "atleast one memory hole entry"); - first = new (reinterpret_cast<void *>(heap_start)) memory_block(heap_size, nullptr); - } - - auto linked_list_allocator::allocate(std::size_t size) -> void * - { - // Add size of size_t to the total allocated size, because we add a header that includes the size of the allocated - // block, to allow for deallocation without the need to call with the corresponding size - auto const total_size = size + sizeof(std::size_t); - mutex.lock(); - - memory_block * previous = nullptr; - auto current = first; - - while (current != nullptr) - { - if (current->size == total_size) - { - auto const memory_address = remove_free_memory_block(previous, current); - new (memory_address) std::size_t(total_size); - mutex.unlock(); - return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t)); - } - else if (current->size >= total_size + min_allocatable_size()) - { - // Ensure that the allocated size block is atleast 16 bytes (required because if we free the hole afterwards - // there needs to be enough space for a memory block). Therefore we allocate more than is actually required if - // the total size was less and simply deallocate it as well - auto const max_size = std::max(total_size, min_allocatable_size()); - auto const memory_address = split_free_memory_block(previous, current, max_size); - new (memory_address) std::size_t(max_size); - mutex.unlock(); - return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t)); - } - - previous = current; - current = current->next; - } - - exception_handling::panic("[Linked List Allocator] Out of memory"); - } - - auto linked_list_allocator::deallocate(void * pointer) noexcept -> void - { - mutex.lock(); - - // Read configured header size of the complete allocated block - auto const header_pointer = reinterpret_cast<void *>(reinterpret_cast<std::size_t>(pointer) - sizeof(std::size_t)); - auto const total_size = *reinterpret_cast<std::size_t *>(header_pointer); - - auto const start_address = reinterpret_cast<std::size_t>(header_pointer); - auto const end_address = start_address + total_size; - - memory_block * previous = nullptr; - auto current = first; - - while (current != nullptr) - { - // Current address of the free memory block now points to an address that is after our block to deallocate in heap - // memory space. - if (reinterpret_cast<std::size_t>(current) >= end_address) - { - break; - } - - previous = current; - current = current->next; - } - - coalesce_free_memory_block(previous, current, header_pointer, total_size); - mutex.unlock(); - } - - auto linked_list_allocator::remove_free_memory_block(memory_block * previous_block, memory_block * current_block) - -> void * - { - return replace_free_memory_block(previous_block, current_block, current_block->next); - } - - auto linked_list_allocator::split_free_memory_block(memory_block * previous_block, memory_block * current_block, - std::size_t size) -> void * - { - auto const end_address = reinterpret_cast<std::size_t>(current_block) + size; - auto const new_block = - new (reinterpret_cast<void *>(end_address)) memory_block(current_block->size - size, current_block->next); - return replace_free_memory_block(previous_block, current_block, new_block); - } - - auto linked_list_allocator::replace_free_memory_block(memory_block * previous_block, memory_block * current_block, - memory_block * new_block) -> void * - { - auto const start_address = reinterpret_cast<std::size_t>(current_block); - // If we want to allocate into the first block that is before any other free block, then there exists no previous - // free block (nullptr). Therefore we have to overwrite the first block instead of overwriting its next value. - if (previous_block == nullptr) - { - first = new_block; - } - else - { - previous_block->next = new_block; - } - current_block->~memory_block(); - return reinterpret_cast<void *>(start_address); - } - - auto linked_list_allocator::coalesce_free_memory_block(memory_block * previous_block, memory_block * current_block, - void * pointer, std::size_t size) -> void - { - auto const start_address = reinterpret_cast<std::size_t>(pointer); - auto const end_address = start_address + size; - - // Inital values if there are no adjacent blocks either before or after, meaning we have to simply create a free - // memory block that is placed in between the previous and next block. - auto block_size = size; - auto next_block = current_block; - - // If the block we want to deallocate is before another free block and we can therefore combine both into one. - // This is done by deleting the current free block and creating a new block at the start address of the block to - // deallocate with both the size of the block to deallcoate and the free block next to it. - if (end_address == reinterpret_cast<std::size_t>(current_block)) - { - block_size += current_block->size; - next_block = current_block->next; - current_block->~memory_block(); - } - - // If the block we want to deallocate is behind another free block and we can therefore combine both into one. - // This is done by simply changin the size of the previous block to include the size of the block to deallocate. - // This is done, because the previous block might still be referencered by the next field of other memory blocks. - if (previous_block != nullptr && - start_address == (reinterpret_cast<std::size_t>(previous_block) + previous_block->size)) - { - block_size += previous_block->size; - - previous_block->size = block_size; - previous_block->next = next_block; - return; - } - - // Check if the block we want to deallocate is contained in the previous block, because if it is it can only mean - // that the block has already been deallocated and we therefore attempted a double free. - exception_handling::assert(previous_block == nullptr || - start_address >= - (reinterpret_cast<std::size_t>(previous_block) + previous_block->size), - "[Linked List Allocator] Attempted double free detected"); - - auto const new_block = new (pointer) memory_block(block_size, next_block); - // If we want to deallocate the first block that is before any other free block, then there exists no previous free - // block (nullptr). Therefore we have to overwrite the first block instead of overwriting its - // next value. - if (previous_block == nullptr) - { - first = new_block; - return; - } - previous_block->next = new_block; - } - -} // namespace teachos::arch::memory::heap diff --git a/arch/x86_64/src/memory/heap/memory_block.cpp b/arch/x86_64/src/memory/heap/memory_block.cpp deleted file mode 100644 index bc97bd6..0000000 --- a/arch/x86_64/src/memory/heap/memory_block.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "arch/memory/heap/memory_block.hpp" - -#include <string.h> - -namespace teachos::arch::memory::heap -{ - memory_block::memory_block(std::size_t size, memory_block * next) - { - memset(static_cast<void *>(this), 0U, size); - this->size = size; - this->next = next; - } - - memory_block::~memory_block() { memset(static_cast<void *>(this), 0U, sizeof(memory_block)); } -} // namespace teachos::arch::memory::heap diff --git a/arch/x86_64/src/memory/heap/user_heap_allocator.cpp b/arch/x86_64/src/memory/heap/user_heap_allocator.cpp deleted file mode 100644 index 427a68a..0000000 --- a/arch/x86_64/src/memory/heap/user_heap_allocator.cpp +++ /dev/null @@ -1,200 +0,0 @@ -#include "arch/memory/heap/user_heap_allocator.hpp" - -#include "arch/context_switching/syscall/main.hpp" - -#include <algorithm> - -namespace teachos::arch::memory::heap -{ - auto user_heap_allocator::allocate(std::size_t size) -> void * - { - // Add size of size_t to the total allocated size, because we add a header that includes the size of the allocated - // block, to allow for deallocation without the need to call with the corresponding size - auto const total_size = size + sizeof(std::size_t); - mutex.lock(); - - memory_block * previous = nullptr; - auto current = first; - - while (current != nullptr) - { - auto memory = allocate_into_memory_block_if_big_enough(current, previous, total_size); - if (memory.has_value()) - { - return memory.value(); - } - - previous = current; - current = current->next; - } - - current = expand_heap_if_full(); - - if (current != nullptr) - { - auto memory = allocate_into_memory_block_if_big_enough(current, previous, total_size); - if (memory.has_value()) - { - return memory.value(); - } - } - - char constexpr OUT_OF_MEMORY_ERROR_MESSAGE[] = "[Linked List Allocator] Out of memory"; - context_switching::syscall::syscall(context_switching::syscall::type::ASSERT, - {false, reinterpret_cast<uint64_t>(&OUT_OF_MEMORY_ERROR_MESSAGE)}); - return nullptr; - } - - auto user_heap_allocator::deallocate(void * pointer) noexcept -> void - { - mutex.lock(); - - // Read configured header size of the complete allocated block - auto const header_pointer = reinterpret_cast<void *>(reinterpret_cast<std::size_t>(pointer) - sizeof(std::size_t)); - auto const total_size = *reinterpret_cast<std::size_t *>(header_pointer); - - auto const start_address = reinterpret_cast<std::size_t>(header_pointer); - auto const end_address = start_address + total_size; - - memory_block * previous = nullptr; - auto current = first; - - while (current != nullptr) - { - // Current address of the free memory block now points to an address that is after our block to deallocate in heap - // memory space. - if (reinterpret_cast<std::size_t>(current) >= end_address) - { - break; - } - - previous = current; - current = current->next; - } - - coalesce_free_memory_block(previous, current, header_pointer, total_size); - mutex.unlock(); - } - - auto user_heap_allocator::allocate_into_memory_block_if_big_enough(memory_block * current, memory_block * previous, - std::size_t total_size) -> std::optional<void *> - { - if (current->size == total_size) - { - auto const memory_address = remove_free_memory_block(previous, current); - new (memory_address) std::size_t(total_size); - mutex.unlock(); - return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t)); - } - else if (current->size >= total_size + min_allocatable_size()) - { - // Ensure that the allocated size block is atleast 16 bytes (required because if we free the hole afterwards - // there needs to be enough space for a memory block). Therefore we allocate more than is actually required if - // the total size was less and simply deallocate it as well - auto const max_size = std::max(total_size, min_allocatable_size()); - auto const memory_address = split_free_memory_block(previous, current, max_size); - new (memory_address) std::size_t(max_size); - mutex.unlock(); - return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t)); - } - return std::nullopt; - } - - auto user_heap_allocator::expand_heap_if_full() -> memory_block * - { - auto const result = context_switching::syscall::syscall(context_switching::syscall::type::EXPAND_HEAP); - - uint64_t const heap_start = result.values.arg_0; - uint64_t const heap_size = result.values.arg_1; - return !result.error_code ? new (reinterpret_cast<void *>(heap_start)) memory_block(heap_size, nullptr) : nullptr; - } - - auto user_heap_allocator::remove_free_memory_block(memory_block * previous_block, memory_block * current_block) - -> void * - { - return replace_free_memory_block(previous_block, current_block, current_block->next); - } - - auto user_heap_allocator::split_free_memory_block(memory_block * previous_block, memory_block * current_block, - std::size_t size) -> void * - { - auto const end_address = reinterpret_cast<std::size_t>(current_block) + size; - auto const new_block = - new (reinterpret_cast<void *>(end_address)) memory_block(current_block->size - size, current_block->next); - return replace_free_memory_block(previous_block, current_block, new_block); - } - - auto user_heap_allocator::replace_free_memory_block(memory_block * previous_block, memory_block * current_block, - memory_block * new_block) -> void * - { - auto const start_address = reinterpret_cast<std::size_t>(current_block); - // If we want to allocate into the first block that is before any other free block, then there exists no previous - // free block (nullptr). Therefore we have to overwrite the first block instead of overwriting its next value. - if (previous_block == nullptr) - { - first = new_block; - } - else - { - previous_block->next = new_block; - } - current_block->~memory_block(); - return reinterpret_cast<void *>(start_address); - } - - auto user_heap_allocator::coalesce_free_memory_block(memory_block * previous_block, memory_block * current_block, - void * pointer, std::size_t size) -> void - { - auto const start_address = reinterpret_cast<std::size_t>(pointer); - auto const end_address = start_address + size; - - // Inital values if there are no adjacent blocks either before or after, meaning we have to simply create a free - // memory block that is placed in between the previous and next block. - auto block_size = size; - auto next_block = current_block; - - // If the block we want to deallocate is before another free block and we can therefore combine both into one. - // This is done by deleting the current free block and creating a new block at the start address of the block to - // deallocate with both the size of the block to deallcoate and the free block next to it. - if (end_address == reinterpret_cast<std::size_t>(current_block)) - { - block_size += current_block->size; - next_block = current_block->next; - current_block->~memory_block(); - } - - // If the block we want to deallocate is behind another free block and we can therefore combine both into one. - // This is done by simply changin the size of the previous block to include the size of the block to deallocate. - // This is done, because the previous block might still be referencered by the next field of other memory blocks. - if (previous_block != nullptr && - start_address == (reinterpret_cast<std::size_t>(previous_block) + previous_block->size)) - { - block_size += previous_block->size; - - previous_block->size = block_size; - previous_block->next = next_block; - return; - } - - // Check if the block we want to deallocate is contained in the previous block, because if it is it can only mean - // that the block has already been deallocated and we therefore attempted a double free. - char constexpr DOUBLE_FREE_ERROR_MESSAGE[] = "[Linked List Allocator] Attempted double free detected"; - context_switching::syscall::syscall( - context_switching::syscall::type::ASSERT, - {previous_block == nullptr || - start_address >= (reinterpret_cast<std::size_t>(previous_block) + previous_block->size), - reinterpret_cast<uint64_t>(&DOUBLE_FREE_ERROR_MESSAGE)}); - - auto const new_block = new (pointer) memory_block(block_size, next_block); - // If we want to deallocate the first block that is before any other free block, then there exists no previous free - // block (nullptr). Therefore we have to overwrite the first block instead of overwriting its - // next value. - if (previous_block == nullptr) - { - first = new_block; - return; - } - previous_block->next = new_block; - } - -} // namespace teachos::arch::memory::heap diff --git a/arch/x86_64/src/memory/kernel_mapper.cpp b/arch/x86_64/src/memory/kernel_mapper.cpp new file mode 100644 index 0000000..4781d64 --- /dev/null +++ b/arch/x86_64/src/memory/kernel_mapper.cpp @@ -0,0 +1,111 @@ +#include "x86_64/memory/kernel_mapper.hpp" + +#include "kapi/cio.hpp" +#include "kapi/memory.hpp" +#include "kapi/system.hpp" + +#include "x86_64/boot/ld.hpp" + +#include <elf/format.hpp> +#include <elf/section_header.hpp> +#include <multiboot2/information.hpp> + +#include <algorithm> +#include <array> +#include <bit> +#include <cstdint> +#include <ranges> +#include <string_view> +#include <utility> + +namespace teachos::memory::x86_64 +{ + + namespace + { + using namespace std::string_view_literals; + + constexpr auto static ignored_section_prefixes = std::array{ + ".boot_"sv, + }; + + constexpr auto static user_accessible_prefixes = std::array{ + ".user"sv, + ".stl"sv, + }; + + } // namespace + + kernel_mapper::kernel_mapper(multiboot2::information_view const * mbi) + : m_mbi{std::move(mbi)} + , m_kernel_load_base{std::bit_cast<std::uintptr_t>(&boot::x86_64::TEACHOS_VMA)} + {} + + auto kernel_mapper::remap_kernel(page_mapper & mapper) -> void + { + auto elf_information = m_mbi->maybe_elf_symbols<elf::format::elf64>(); + if (!elf_information) + { + system::panic("[x86_64:MEM] ELF section information is not available."); + } + + auto sections = *elf_information; + auto allocated_sections = + std::views::all(sections) | std::views::filter(&elf::section_header<elf::format::elf64>::allocated) | + std::views::filter([&](auto const & section) -> auto { + auto name = sections.name(section); + return !std::ranges::any_of(ignored_section_prefixes, + [&](auto const & prefix) -> auto { return name.starts_with(prefix); }); + }); + + if (allocated_sections.empty()) + { + system::panic("[x86_64:MEM] No allocated ELF sections were found."); + } + + std::ranges::for_each(allocated_sections, + [&](auto const & section) -> auto { map_section(section, sections.name(section), mapper); }); + } + + auto kernel_mapper::map_section(section_header_type const & section, std::string_view name, page_mapper & mapper) + -> void + { + cio::print("[x86_64:MEM] mapping "); + cio::println(name); + + auto number_of_pages = (section.size + (PLATFORM_PAGE_SIZE - 1)) / PLATFORM_PAGE_SIZE; + + auto linear_start_address = linear_address{section.virtual_load_address}; + auto physical_start_address = physical_address{section.virtual_load_address & ~m_kernel_load_base}; + + auto first_page = page::containing(linear_start_address); + auto first_frame = frame::containing(physical_start_address); + + auto page_flags = page_mapper::flags::empty; + + if (section.writable()) + { + page_flags |= page_mapper::flags::writable; + } + + if (section.executable()) + { + page_flags |= page_mapper::flags::executable; + } + + auto is_prefix_of_name = [=](auto prefix) -> bool { + return name.starts_with(prefix); + }; + + if (!std::ranges::any_of(user_accessible_prefixes, is_prefix_of_name)) + { + page_flags |= page_mapper::flags::supervisor_only; + } + + for (auto i = 0uz; i < number_of_pages; ++i) + { + mapper.map(first_page + i, first_frame + i, page_flags); + } + } + +} // namespace teachos::memory::x86_64
\ No newline at end of file diff --git a/arch/x86_64/src/memory/main.cpp b/arch/x86_64/src/memory/main.cpp deleted file mode 100644 index 2746a71..0000000 --- a/arch/x86_64/src/memory/main.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "arch/memory/main.hpp" - -#include "arch/exception_handling/assert.hpp" -#include "arch/kernel/cpu/control_register.hpp" -#include "arch/kernel/cpu/msr.hpp" -#include "arch/memory/allocator/area_frame_allocator.hpp" -#include "arch/memory/allocator/concept.hpp" -#include "arch/memory/heap/global_heap_allocator.hpp" -#include "arch/memory/paging/active_page_table.hpp" -#include "arch/memory/paging/kernel_mapper.hpp" - -#include <optional> - -namespace teachos::arch::memory -{ - namespace - { - static std::optional<allocator::area_frame_allocator> frame_allocator; - - auto create_frame_allocator(multiboot::memory_information const & memory_information) - -> allocator::area_frame_allocator & - { - frame_allocator.emplace(memory_information); - return frame_allocator.value(); - } - - auto get_frame_allocator() -> allocator::area_frame_allocator & - { - exception_handling::assert(frame_allocator.has_value(), - "[Initialization] Frame allocator has not been created yet"); - return frame_allocator.value(); - } - } // namespace - - auto remap_heap(std::size_t heap_start, std::size_t heap_size, paging::entry::bitset additional_flags = {}) -> void - { - decltype(auto) allocator = get_frame_allocator(); - decltype(auto) active_table = paging::active_page_table::create_or_get(); - auto const start_page = paging::virtual_page::containing_address(heap_start); - auto const end_page = ++(paging::virtual_page::containing_address(heap_start + heap_size - 1)); - - paging::page_container::iterator const begin{start_page}; - paging::page_container::iterator const end{end_page}; - paging::page_container const pages{begin, end}; - - constexpr auto base_flags = paging::entry::WRITABLE; - auto const flags = base_flags | additional_flags; - - for (auto const & page : pages) - { - active_table.map_page_to_next_free_frame(allocator, page, flags); - } - } - - auto initialize_memory_management() -> void - { - static bool has_been_called = false; - arch::exception_handling::assert(!has_been_called, - "[Initialization] Memory management has already been initialized"); - has_been_called = true; - - auto const memory_information = multiboot::read_multiboot2(); - decltype(auto) allocator = create_frame_allocator(memory_information); - - kernel::cpu::set_cr0_bit(kernel::cpu::cr0_flags::WRITE_PROTECT); - kernel::cpu::set_efer_bit(kernel::cpu::efer_flags::NXE); - - paging::kernel_mapper kernel(allocator, memory_information); - kernel.remap_kernel(); - video::vga::text::write("Kernel remapping successful", video::vga::text::common_attributes::green_on_black); - video::vga::text::newline(); - - remap_heap(heap::KERNEL_HEAP_START, heap::KERNEL_HEAP_SIZE); - video::vga::text::write("Heap remapping successful", video::vga::text::common_attributes::green_on_black); - video::vga::text::newline(); - } -} // namespace teachos::arch::memory diff --git a/arch/x86_64/src/memory/mmu.cpp b/arch/x86_64/src/memory/mmu.cpp new file mode 100644 index 0000000..e15d94e --- /dev/null +++ b/arch/x86_64/src/memory/mmu.cpp @@ -0,0 +1,21 @@ +#include "x86_64/memory/mmu.hpp" + +#include "kapi/memory.hpp" + +#include "x86_64/cpu/registers.hpp" + +namespace teachos::memory::x86_64 +{ + namespace cpu = cpu::x86_64; + + auto tlb_flush(linear_address address) -> void + { + asm volatile("invlpg (%[input])" : /* no output from call */ : [input] "r"(address) : "memory"); + } + + auto tlb_flush_all() -> void + { + auto paging_root = cpu::cr3::read(); + cpu::cr3::write(paging_root); + } +} // namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/memory/multiboot/elf_symbols_section.cpp b/arch/x86_64/src/memory/multiboot/elf_symbols_section.cpp deleted file mode 100644 index f5d126b..0000000 --- a/arch/x86_64/src/memory/multiboot/elf_symbols_section.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "arch/memory/multiboot/elf_symbols_section.hpp" - -namespace teachos::arch::memory::multiboot -{ - auto elf_section_flags::contains_flags(std::bitset<64U> other) const -> bool { return (flags & other) == other; } - - auto elf_section_header::is_null() const -> bool - { - return name_table_index == 0U && type == elf_section_type::INACTIVE && flags == elf_section_flags(0U) && - physical_address == 0U && file_offset == 0U && additional_information == 0U && address_alignment == 0U && - fixed_table_entry_size == 0U; - } -} // namespace teachos::arch::memory::multiboot diff --git a/arch/x86_64/src/memory/multiboot/reader.cpp b/arch/x86_64/src/memory/multiboot/reader.cpp deleted file mode 100644 index 2bf5b25..0000000 --- a/arch/x86_64/src/memory/multiboot/reader.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "arch/memory/multiboot/reader.hpp" - -#include "arch/boot/pointers.hpp" -#include "arch/exception_handling/assert.hpp" -#include "arch/memory/multiboot/elf_symbols_section.hpp" -#include "arch/memory/multiboot/info.hpp" - -#include <algorithm> -#include <ranges> - -namespace teachos::arch::memory::multiboot -{ - namespace - { - template<typename T> - requires std::is_pointer<T>::value - auto align_to_8_byte_boundary(T ptr, uint32_t size) -> T - { - return reinterpret_cast<T>(reinterpret_cast<uint8_t *>(ptr) + ((size + 7) & ~7)); - } - - auto process_memory_map(memory_map_header * mminfo) -> memory_area_container - { - auto const expected_entry_size = mminfo->entry_size; - auto constexpr actual_entry_size = sizeof(memory_area); - exception_handling::assert(expected_entry_size == actual_entry_size, - "[Multiboot Reader] Unexpected memory area entry size"); - - auto const total_size = mminfo->info.size; - auto const total_entries_size = total_size - sizeof(memory_map_header) + actual_entry_size; - auto const number_of_entries = total_entries_size / actual_entry_size; - - auto const begin = memory_area_container::iterator{&mminfo->entries}; - auto const end = begin + number_of_entries; - return memory_area_container{begin, end}; - } - - auto process_elf_sections(elf_symbols_section_header * symbol, std::size_t & kernel_start, - std::size_t & kernel_end) -> elf_section_header_container - { - auto const expected_entry_size = symbol->entry_size; - auto constexpr actual_entry_size = sizeof(elf_section_header); - exception_handling::assert(expected_entry_size == actual_entry_size, - "[Multiboot Reader] Unexpected elf section header entry size"); - - auto const expected_total_size = symbol->info.size; - auto const actual_total_entry_size = actual_entry_size * symbol->number_of_sections; - auto constexpr actual_total_section_size = sizeof(elf_symbols_section_header) - sizeof(uint32_t); - auto const actual_total_size = actual_total_entry_size + actual_total_section_size; - exception_handling::assert(expected_total_size == actual_total_size, - "[Multiboot Reader] Unexpected elf symbols section header total size"); - - auto const begin = elf_section_header_container::iterator{reinterpret_cast<elf_section_header *>(&symbol->end)}; - auto const end = begin + symbol->number_of_sections; - exception_handling::assert(begin->is_null(), - "[Multiboot Reader] Elf symbols section not starting with SHT_NULL section"); - - elf_section_header_container sections{begin, end}; - - auto allocated_sections = sections | std::views::filter([](auto const & section) { - return section.flags.contains_flags(elf_section_flags::OCCUPIES_MEMORY); - }); - - auto const elf_section_with_lowest_physical_address = std::ranges::min_element( - allocated_sections, [](auto const & a, auto const & b) { return a.physical_address < b.physical_address; }); - - auto const elf_section_with_highest_physical_address = - std::ranges::max_element(allocated_sections, [](auto const & a, auto const & b) { - auto a_physical_address_end = a.physical_address + a.section_size; - auto b_physical_address_end = b.physical_address + b.section_size; - return a_physical_address_end < b_physical_address_end; - }); - - auto const symbol_table_section_count = std::ranges::count_if(sections, [](auto const & section) { - return section.type == elf_section_type::DYNAMIC_SYMBOL_TABLE || section.type == elf_section_type::SYMBOL_TABLE; - }); - auto const dynamic_section_count = std::ranges::count_if( - sections, [](auto const & section) { return section.type == elf_section_type::DYNAMIC; }); - - exception_handling::assert( - symbol_table_section_count == 1U, - "[Multiboot Reader] ELF Specifications allows only (1) symbol table section, but got more"); - exception_handling::assert( - dynamic_section_count <= 1U, - "[Multiboot Reader] ELF Specifications allows only (1) or less dynamic sections, but got more"); - - auto const lowest_elf_section = *elf_section_with_lowest_physical_address; - kernel_start = lowest_elf_section.physical_address; - - auto const highest_elf_section = *elf_section_with_highest_physical_address; - kernel_end = highest_elf_section.physical_address + highest_elf_section.section_size; - - return sections; - } - } // namespace - - auto read_multiboot2() -> memory_information - { - memory_information mem_info{UINT64_MAX, - 0U, - elf_section_header_container{}, - boot::multiboot_information_pointer, - 0U, - memory_area_container{}}; - - auto const multiboot_information_pointer = reinterpret_cast<info_header *>(boot::multiboot_information_pointer); - auto const multiboot_tag = &multiboot_information_pointer->tags; - mem_info.multiboot_end = mem_info.multiboot_start + multiboot_information_pointer->total_size; - - for (auto tag = multiboot_tag; tag->type != tag_type::END; tag = align_to_8_byte_boundary(tag, tag->size)) - { - switch (tag->type) - { - case tag_type::ELF_SECTIONS: { - auto const symbol = reinterpret_cast<elf_symbols_section_header *>(tag); - mem_info.sections = process_elf_sections(symbol, mem_info.kernel_start, mem_info.kernel_end); - break; - } - case tag_type::MEMORY_MAP: { - auto const mminfo = reinterpret_cast<memory_map_header *>(tag); - mem_info.areas = process_memory_map(mminfo); - break; - } - default: - // All other cases are not important and can be ignored. - break; - } - } - return mem_info; - } -} // namespace teachos::arch::memory::multiboot diff --git a/arch/x86_64/src/memory/page_table.cpp b/arch/x86_64/src/memory/page_table.cpp new file mode 100644 index 0000000..2de099d --- /dev/null +++ b/arch/x86_64/src/memory/page_table.cpp @@ -0,0 +1,82 @@ +#include "x86_64/memory/page_table.hpp" + +#include "kapi/memory.hpp" + +#include <algorithm> +#include <bit> +#include <cstddef> +#include <cstdint> +#include <optional> +#include <utility> + +namespace teachos::memory::x86_64 +{ + + auto page_table::entry::clear() noexcept -> void + { + m_raw = 0; + } + + auto page_table::entry::present() const noexcept -> bool + { + return (all_flags() & flags::present) != flags::empty; + } + + auto page_table::entry::huge() const noexcept -> bool + { + return (all_flags() & flags::huge_page) != flags::empty; + } + + auto page_table::entry::all_flags() const noexcept -> flags + { + return std::bit_cast<flags>(m_raw & ~frame_number_mask); + } + + auto page_table::entry::all_flags(flags flags) noexcept -> void + { + m_raw = (m_raw & ~frame_number_mask) | std::to_underlying(flags); + } + + auto page_table::entry::operator|=(flags rhs) noexcept -> page_table::entry & + { + auto raw_flags = std::to_underlying(rhs) & ~frame_number_mask; + m_raw |= raw_flags; + return *this; + } + + auto page_table::entry::frame() const noexcept -> std::optional<struct frame> + { + if (present()) + { + return frame::containing(physical_address{m_raw & frame_number_mask}); + } + return std::nullopt; + } + + auto page_table::entry::frame(struct frame frame, flags flags) noexcept -> void + { + m_raw = (frame.start_address().raw() | static_cast<std::uint64_t>(flags)); + }; + + auto page_table::operator[](std::size_t index) -> entry & + { + return m_entries.at(index); + } + + auto page_table::operator[](std::size_t index) const -> entry const & + { + return m_entries.at(index); + } + + auto page_table::clear() noexcept -> void + { + std::ranges::for_each(m_entries, &page_table::entry::clear); + } + + auto page_table::empty() const noexcept -> bool + { + return std::ranges::all_of(m_entries, + [](auto const & entry) -> auto { return entry.all_flags() == entry::flags::empty; }); + } + +} // namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/memory/paging/active_page_table.cpp b/arch/x86_64/src/memory/paging/active_page_table.cpp deleted file mode 100644 index 0113869..0000000 --- a/arch/x86_64/src/memory/paging/active_page_table.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "arch/memory/paging/active_page_table.hpp" - -namespace teachos::arch::memory::paging -{ - namespace - { - paging::virtual_address constexpr PAGE_TABLE_LEVEL_4_ADDRESS = 0xffffffff'fffff000; - } - - auto active_page_table::create_or_get() -> active_page_table & - { - static page_table_handle active_handle{reinterpret_cast<page_table *>(PAGE_TABLE_LEVEL_4_ADDRESS), - page_table_handle::LEVEL4}; - static active_page_table active_page{active_handle}; - return active_page; - } - - auto active_page_table::operator[](std::size_t index) -> entry & { return active_handle[index]; } - - auto active_page_table::translate_address(virtual_address address) -> std::optional<allocator::physical_address> - { - auto const offset = address % allocator::PAGE_FRAME_SIZE; - auto const page = virtual_page::containing_address(address); - auto const frame = translate_page(page); - - if (frame.has_value()) - { - return frame.value().frame_number * allocator::PAGE_FRAME_SIZE + offset; - } - - return std::nullopt; - } - - auto active_page_table::translate_page(virtual_page page) -> std::optional<allocator::physical_frame> - { - auto current_handle = active_handle; - - for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level) - { - auto const next_handle = current_handle.next_table(page.get_level_index(level)); - // If the next table method failed then it is highly likely that it was a huge page and we therefore have to - // parse the table differently. Therefore, we attempt to parse it using the method required by huge pages. - if (!next_handle.has_value()) - { - return translate_huge_page(page); - } - current_handle = next_handle.value(); - } - - auto const level1_index = page.get_level_index(page_table_handle::LEVEL1); - auto const level1_entry = current_handle[level1_index]; - return level1_entry.calculate_pointed_to_frame(); - } - - auto active_page_table::translate_huge_page(virtual_page page) -> std::optional<allocator::physical_frame> - { - auto current_handle = active_handle; - auto level3_handle = current_handle.next_table(page.get_level_index(page_table_handle::LEVEL4)); - - if (!level3_handle.has_value()) - { - return std::nullopt; - } - - auto const level3_entry = level3_handle.value()[page.get_level_index(page_table_handle::LEVEL3)]; - auto const level3_frame = level3_entry.calculate_pointed_to_frame(); - if (level3_frame.has_value() && level3_entry.contains_flags(entry::HUGE_PAGE)) - { - exception_handling::assert( - level3_frame.value().frame_number % (PAGE_TABLE_ENTRY_COUNT * PAGE_TABLE_ENTRY_COUNT) == 0U, - "[Page Mapper] Physical address must be 1 GiB aligned"); - return allocator::physical_frame{level3_frame.value().frame_number + - page.get_level_index(page_table_handle::LEVEL2) * PAGE_TABLE_ENTRY_COUNT + - page.get_level_index(page_table_handle::LEVEL1)}; - } - - auto level2_handle = level3_handle.value().next_table(page.get_level_index(page_table_handle::LEVEL3)); - if (level2_handle.has_value()) - { - auto const level2_entry = level2_handle.value()[page.get_level_index(page_table_handle::LEVEL2)]; - auto const level2_frame = level2_entry.calculate_pointed_to_frame(); - if (level2_frame.has_value() && level2_entry.contains_flags(entry::HUGE_PAGE)) - { - exception_handling::assert(level2_frame.value().frame_number % PAGE_TABLE_ENTRY_COUNT == 0U, - "[Page Mapper] Physical address must be 2 MiB aligned"); - return allocator::physical_frame{level2_frame.value().frame_number + - page.get_level_index(page_table_handle::LEVEL1)}; - } - } - return std::nullopt; - } - - active_page_table::active_page_table(page_table_handle active_handle) - : active_handle(active_handle) - { - // Nothing to do - } -} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/src/memory/paging/inactive_page_table.cpp b/arch/x86_64/src/memory/paging/inactive_page_table.cpp deleted file mode 100644 index 4e0610e..0000000 --- a/arch/x86_64/src/memory/paging/inactive_page_table.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "arch/memory/paging/inactive_page_table.hpp" - -namespace teachos::arch::memory::paging -{ - inactive_page_table::inactive_page_table(allocator::physical_frame frame) - : page_table_level_4_frame{frame} - { - // Nothing to do - } - - inactive_page_table::inactive_page_table(allocator::physical_frame frame, active_page_table & active_page_table, - temporary_page & temporary_page) - : page_table_level_4_frame{frame} - { - auto table = temporary_page.map_table_frame(page_table_level_4_frame, active_page_table); - table.zero_entries(); - table[511].set_entry(page_table_level_4_frame, entry::PRESENT | entry::WRITABLE); - temporary_page.unmap_page(active_page_table); - } -} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/src/memory/paging/page_entry.cpp b/arch/x86_64/src/memory/paging/page_entry.cpp deleted file mode 100644 index 57045ca..0000000 --- a/arch/x86_64/src/memory/paging/page_entry.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "arch/memory/paging/page_entry.hpp" - -#include "arch/exception_handling/assert.hpp" - -namespace teachos::arch::memory::paging -{ - namespace - { - std::size_t constexpr PHYSICAL_ADDRESS_MASK = 0x000fffff'fffff000; - } // namespace - - entry::entry(uint64_t flags) - : flags(flags) - { - // Nothing to do. - } - - entry::entry(multiboot::elf_section_flags elf_flags) - { - if (elf_flags.contains_flags(multiboot::elf_section_flags::OCCUPIES_MEMORY)) - { - flags |= entry::PRESENT; - } - - if (elf_flags.contains_flags(multiboot::elf_section_flags::WRITABLE)) - { - flags |= entry::WRITABLE; - } - - if (!elf_flags.contains_flags(multiboot::elf_section_flags::EXECUTABLE_CODE)) - { - flags |= entry::EXECUTING_CODE_FORBIDDEN; - } - } - - auto entry::is_unused() const -> bool { return flags == 0U; } - - auto entry::set_unused() -> void { flags = 0U; } - - auto entry::set_user_accessible() -> void { flags |= entry::USER_ACCESSIBLE; } - - auto entry::calculate_pointed_to_frame() const -> std::optional<allocator::physical_frame> - { - if (contains_flags(PRESENT)) - { - auto const address = flags.to_ulong() & PHYSICAL_ADDRESS_MASK; - return allocator::physical_frame::containing_address(address); - } - return std::nullopt; - } - - auto entry::contains_flags(std::bitset<64U> other) const -> bool { return (flags & other) == other; } - - auto entry::set_entry(allocator::physical_frame frame, std::bitset<64U> additional_flags) -> void - { - exception_handling::assert((frame.start_address() & ~PHYSICAL_ADDRESS_MASK) == 0, - "[Paging Entry] Start address is not aligned with page"); - - flags = frame.start_address() | additional_flags.to_ulong(); - } - - auto entry::get_flags() const -> std::bitset<64U> { return flags.to_ulong() & ~PHYSICAL_ADDRESS_MASK; } -} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/src/memory/paging/page_table.cpp b/arch/x86_64/src/memory/paging/page_table.cpp deleted file mode 100644 index eb11810..0000000 --- a/arch/x86_64/src/memory/paging/page_table.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "arch/memory/paging/page_table.hpp" - -#include <algorithm> -#include <array> -#include <memory> - -/* - * This is a linker variable reference. This referenc cannot reside inside a namespace, because in - * that case the compiler would try to find arch::memory::paging::_end_of_image inside the ELF file. - */ -extern char _end_of_image; - -namespace teachos::arch::memory::paging -{ - /** - * @brief A Page table containing 512 entries. - */ - struct page_table - { - auto zero_entries() -> void; - - auto is_empty() const -> bool; - - auto next_table(std::size_t table_index) const -> std::optional<page_table *>; - - auto operator[](std::size_t index) -> entry &; - - auto operator[](std::size_t index) const -> entry const &; - - private: - /** - * @brief Calculates the address of the next page table level for the given table index. - * - * @note The next page table address is only valid if the corresponding entry is present and not a huge page. - * Meaning we use an index into a Level 4 page table to get the according Level 3 page table address. - * - * @param table_index Index of this page table in the page table one level higher. - * @return An optional of the address of the next page table or null. - */ - auto next_table_address(std::size_t table_index) const -> std::optional<std::size_t>; - - std::array<entry, PAGE_TABLE_ENTRY_COUNT> entries = - {}; ///< Entries containing addresses to page tables of a level below or - ///< actual virtual addresses for the level 1 page table. - }; - - auto page_table::zero_entries() -> void - { - std::ranges::for_each(entries, [](auto & entry) { entry.set_unused(); }); - } - - auto page_table::is_empty() const -> bool - { - return std::all_of(entries.begin(), entries.end(), [](entry const & entry) { return entry.is_unused(); }); - } - - auto page_table::next_table(std::size_t table_index) const -> std::optional<page_table *> - { - auto const address = next_table_address(table_index); - if (address.has_value()) - { - return reinterpret_cast<page_table *>(address.value()); - } - return std::nullopt; - } - - auto page_table::operator[](std::size_t index) -> entry & - { - exception_handling::assert(index < PAGE_TABLE_ENTRY_COUNT, "[Page Table] Index out of bounds"); - return entries[index]; - } - - auto page_table::operator[](std::size_t index) const -> entry const & - { - exception_handling::assert(index < PAGE_TABLE_ENTRY_COUNT, "[Page Table] Index out of bounds"); - return entries[index]; - } - - auto page_table::next_table_address(std::size_t table_index) const -> std::optional<std::size_t> - { - auto const entry = this->operator[](table_index); - - if (entry.contains_flags(entry::PRESENT) && !entry.contains_flags(entry::HUGE_PAGE)) - { - auto const table_address = reinterpret_cast<std::size_t>(this); - return ((table_address << 9) | (table_index << 12)); - } - return std::nullopt; - } - - page_table_handle::page_table_handle(page_table * table, page_table_handle::level table_level) - : table(table) - , table_level(table_level) - { - exception_handling::assert(table != nullptr, - "[Page Table] Attempted to pass nullptr as table to page table table method"); - } - - auto page_table_handle::zero_entries() -> void { table->zero_entries(); } - - auto page_table_handle::is_empty() const -> bool { return table->is_empty(); } - - auto page_table_handle::next_table(std::size_t table_index) const -> std::optional<page_table_handle> - { - exception_handling::assert(table_level != page_table_handle::LEVEL1, - "[Page Table] Attempted to call next_table on level 1 page table"); - auto const next_table = table->next_table(table_index); - if (next_table.has_value()) - { - auto const new_level = static_cast<page_table_handle::level>(table_level - 1); - return page_table_handle{next_table.value(), new_level}; - } - return std::nullopt; - } - - auto page_table_handle::get_level() const -> page_table_handle::level { return table_level; } - - auto page_table_handle::operator[](std::size_t index) -> entry & { return table->operator[](index); } - - auto operator--(page_table_handle::level & value) -> page_table_handle::level & - { - exception_handling::assert(value != page_table_handle::LEVEL1, - "[Page table] Attempted to decrement enum to value outside of range"); - auto new_value = static_cast<std::underlying_type<page_table_handle::level>::type>(value); - value = static_cast<page_table_handle::level>(--new_value); - return value; - } -} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/src/memory/paging/temporary_page.cpp b/arch/x86_64/src/memory/paging/temporary_page.cpp deleted file mode 100644 index 8e73523..0000000 --- a/arch/x86_64/src/memory/paging/temporary_page.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "arch/memory/paging/temporary_page.hpp" - -#include "arch/memory/paging/page_entry.hpp" - -namespace teachos::arch::memory::paging -{ - auto temporary_page::map_table_frame(allocator::physical_frame frame, active_page_table & active_table) - -> page_table_handle - { - page_table_handle handle{reinterpret_cast<page_table *>(map_to_frame(frame, active_table)), - page_table_handle::LEVEL1}; - return handle; - } - - auto temporary_page::map_to_frame(allocator::physical_frame frame, active_page_table & active_table) - -> virtual_address - { - exception_handling::assert(!active_table.translate_page(page).has_value(), - "[Temporary page] Page is already mapped"); - - active_table.map_page_to_frame(allocator, page, frame, entry::WRITABLE); - return page.start_address(); - } - - auto temporary_page::unmap_page(active_page_table & active_table) -> void - { - active_table.unmap_page(allocator, page); - } -} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/src/memory/paging/virtual_page.cpp b/arch/x86_64/src/memory/paging/virtual_page.cpp deleted file mode 100644 index d374156..0000000 --- a/arch/x86_64/src/memory/paging/virtual_page.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "arch/memory/paging/virtual_page.hpp" - -#include "arch/exception_handling/assert.hpp" - -namespace teachos::arch::memory::paging -{ - auto virtual_page::containing_address(virtual_address address) -> virtual_page - { - exception_handling::assert(address < 0x00008000'00000000 || address >= 0xffff8000'00000000, - "[Virtual Page] Attempted to create virtual page from invalid address"); - return virtual_page{address / allocator::PAGE_FRAME_SIZE}; - } - - auto virtual_page::start_address() const -> virtual_address { return page_number * allocator::PAGE_FRAME_SIZE; } - - auto virtual_page::get_level_index(page_table_handle::level level) const -> size_t - { - return (page_number >> (level * 9U)) & 0x1FF; - } - - auto virtual_page::operator++(int) -> virtual_page - { - virtual_page const old_value = *this; - ++page_number; - return old_value; - } - - auto virtual_page::operator++() -> virtual_page & - { - ++page_number; - return *this; - } -} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/src/memory/paging_root.cpp b/arch/x86_64/src/memory/paging_root.cpp new file mode 100644 index 0000000..d849a82 --- /dev/null +++ b/arch/x86_64/src/memory/paging_root.cpp @@ -0,0 +1,19 @@ +#include "x86_64/memory/paging_root.hpp" + +#include <bit> +#include <cstdint> + +namespace teachos::memory::x86_64 +{ + + namespace + { + constexpr auto recursive_base = std::uintptr_t{0177777'776'776'776'776'0000uz}; + } // namespace + + auto paging_root::get() -> paging_root * + { + return std::bit_cast<paging_root *>(recursive_base); + } + +} // namespace teachos::memory::x86_64
\ No newline at end of file diff --git a/arch/x86_64/src/memory/recursive_page_mapper.cpp b/arch/x86_64/src/memory/recursive_page_mapper.cpp new file mode 100644 index 0000000..c5aabcb --- /dev/null +++ b/arch/x86_64/src/memory/recursive_page_mapper.cpp @@ -0,0 +1,119 @@ +#include "x86_64/memory/recursive_page_mapper.hpp" + +#include "kapi/memory.hpp" +#include "kapi/system.hpp" + +#include "x86_64/memory/page_table.hpp" +#include "x86_64/memory/page_utilities.hpp" +#include "x86_64/memory/paging_root.hpp" + +#include <cstddef> +#include <optional> + +namespace teachos::memory::x86_64 +{ + namespace + { + //! Perform the actual mapping of the page, via the recursive page map. + //! + //! On any level above PML1, the entries need to not be no_execute, because the image is densely packed. The entries + //! also need to be writable, since the mapping is being performed through the recursive page map hierarchy. When + //! setting the final entry in the PML1, the actually desired flags are set as is, with the present bit + //! added, thus still enforcing non-writability and non-execution of the affected page. + template<std::size_t Level> + requires(Level > 1uz && Level <= PLATFORM_PAGING_LEVELS) + auto do_map(recursive_page_table<Level> * pml, page page, frame_allocator & allocator, page_mapper::flags flags) + { + auto index = pml_index<Level>(page); + auto entry_flags = to_table_flags(flags); + + entry_flags = entry_flags & ~page_table::entry::flags::no_execute; + entry_flags = entry_flags | page_table::entry::flags::writable; + if (!(*pml)[index].present()) + { + auto new_table_frame = allocator.allocate(); + (*pml)[index].frame(new_table_frame.value(), page_table::entry::flags::present | entry_flags); + auto new_table = std::optional{std::construct_at(*pml->next(index))}; + return new_table; + } + (*pml)[index] |= entry_flags; + return pml->next(index); + } + + //! Perform the actual PML1 update. + auto do_map(page_table * pml, page page, frame frame, page_mapper::flags flags) -> std::optional<std::byte *> + { + auto index = pml_index<1>(page); + if ((*pml)[index].present()) + { + system::panic("[x86_64:MEM] Tried to map a page that is already mapped"); + } + (*pml)[index].frame(frame, page_table::entry::flags::present | to_table_flags(flags)); + return std::optional{static_cast<std::byte *>(page.start_address())}; + } + + } // namespace + + recursive_page_mapper::recursive_page_mapper(frame_allocator & allocator) + : m_allocator{&allocator} + {} + + auto recursive_page_mapper::map(page page, frame frame, flags flags) -> std::byte * + { + auto pml4 = static_cast<recursive_page_table<4> *>((paging_root::get())); + + return std::optional{pml4} + .and_then([&](auto pml) -> auto { return do_map(pml, page, *m_allocator, flags); }) + .and_then([&](auto pml) -> auto { return do_map(pml, page, *m_allocator, flags); }) + .and_then([&](auto pml) -> auto { return do_map(pml, page, *m_allocator, flags); }) + .and_then([&](auto pml) -> auto { return do_map(pml, page, frame, flags); }) + .value_or(nullptr); + } + + auto recursive_page_mapper::unmap(page page) -> void + { + if (!try_unmap(page)) + { + system::panic("[x86_64:MEM] Tried to unmap a page that was not mapped."); + } + } + + auto recursive_page_mapper::try_unmap(page page) noexcept -> bool + { + if (!paging_root::get()->translate(page)) + { + return false; + } + + auto pml4 = paging_root::get(); + auto pml3 = pml4->next(pml_index<4>(page)).value(); + auto pml2 = pml3->next(pml_index<3>(page)).value(); + auto pml1 = pml2->next(pml_index<2>(page)).value(); + + (*pml1)[pml_index<1>(page)].clear(); + + if (pml1->empty()) + { + auto pml1_frame = (*pml2)[pml_index<2>(page)].frame().value(); + m_allocator->release(pml1_frame); + (*pml2)[pml_index<2>(page)].clear(); + } + + if (pml2->empty()) + { + auto pml2_frame = (*pml3)[pml_index<3>(page)].frame().value(); + m_allocator->release(pml2_frame); + (*pml3)[pml_index<3>(page)].clear(); + } + + if (pml3->empty()) + { + auto pml3_frame = (*pml4)[pml_index<4>(page)].frame().value(); + m_allocator->release(pml3_frame); + (*pml4)[pml_index<4>(page)].clear(); + } + + return true; + } + +} // namespace teachos::memory::x86_64
\ No newline at end of file diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp new file mode 100644 index 0000000..0f65b3a --- /dev/null +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -0,0 +1,92 @@ +#include "x86_64/memory/region_allocator.hpp" + +#include "kapi/memory/address.hpp" +#include "kapi/memory/frame.hpp" + +#include <multiboot2/information.hpp> + +#include <algorithm> +#include <optional> +#include <ranges> +#include <utility> + +namespace teachos::memory::x86_64 +{ + namespace + { + constexpr auto last_frame(multiboot2::memory_map::region const & region) + { + return frame::containing(physical_address{region.base + region.size_in_B - 1}); + } + + constexpr auto falls_within(frame const & candidate, frame const & start, frame const & end) + { + return candidate >= start && candidate <= end; + } + } // namespace + + region_allocator::region_allocator(memory_information const & mem_info) + : m_next_frame{} + , m_current_region{} + , m_memory_map{mem_info.memory_map} + , m_kernel_start(frame::containing(mem_info.image_range.first)) + , m_kernel_end(frame::containing(mem_info.image_range.second)) + , m_multiboot_start(frame::containing(mem_info.mbi_range.first)) + , m_multiboot_end(frame::containing(mem_info.mbi_range.second)) + { + choose_next_area(); + } + + auto region_allocator::choose_next_area() -> void + { + m_current_region.reset(); + auto next_area_with_free_frames = + m_memory_map | std::views::filter(&multiboot2::memory_map::region::available) | + std::views::filter([next = m_next_frame](auto const & region) -> bool { return last_frame(region) >= next; }); + + auto lowest_region_with_free_frames = std::ranges::min_element( + next_area_with_free_frames, [](auto lhs, auto rhs) -> bool { return lhs.base < rhs.base; }); + + if (lowest_region_with_free_frames != next_area_with_free_frames.end()) + { + m_current_region = *lowest_region_with_free_frames; + auto start_frame = frame::containing(physical_address{m_current_region->base}); + if (m_next_frame < start_frame) + { + m_next_frame = start_frame; + } + } + } + + auto region_allocator::allocate() noexcept -> std::optional<frame> + { + if (!m_current_region) + { + return {}; + } + + auto const end_address = physical_address{m_current_region->base + m_current_region->size_in_B - 1}; + auto end_frame = frame::containing(end_address); + + if (m_next_frame > end_frame) + { + choose_next_area(); + } + else if (falls_within(m_next_frame, m_kernel_start, m_kernel_end)) + { + m_next_frame = m_kernel_end + 1; + } + else if (falls_within(m_next_frame, m_multiboot_start, m_multiboot_end)) + { + m_next_frame = m_multiboot_end + 1; + } + else + { + return m_next_frame++; + } + + return allocate(); + } + + auto region_allocator::release(frame) -> void {} +} // namespace teachos::memory::x86_64 diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp new file mode 100644 index 0000000..a86aaed --- /dev/null +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -0,0 +1,69 @@ +#include "x86_64/memory/scoped_mapping.hpp" + +#include "kapi/memory.hpp" +#include "kapi/system.hpp" + +#include "x86_64/memory/mmu.hpp" +#include "x86_64/memory/page_table.hpp" +#include "x86_64/memory/paging_root.hpp" + +#include <cstddef> +#include <utility> + +namespace teachos::memory::x86_64 +{ + + scoped_mapping::scoped_mapping(scoped_mapping && other) noexcept + : m_page{std::exchange(other.m_page, page{})} + , m_mapper{std::exchange(other.m_mapper, nullptr)} + , m_mapped{std::exchange(other.m_mapped, false)} + {} + + scoped_mapping::scoped_mapping(page page, page_mapper & mapper) + : m_page{page} + , m_mapper{&mapper} + , m_mapped{false} + { + if (paging_root::get()->translate(page)) + { + system::panic("[MEM] Tried to map a page that is already mapped!"); + } + } + + scoped_mapping::~scoped_mapping() + { + if (m_mapped) + { + unmap(); + x86_64::tlb_flush(m_page.start_address()); + } + } + + auto scoped_mapping::operator=(scoped_mapping && other) noexcept -> scoped_mapping & + { + swap(*this, other); + return *this; + } + + auto scoped_mapping::map(frame frame, page_table::entry::flags flags) -> std::byte * + { + auto result = m_mapper->map(m_page, frame, to_mapper_flags(flags)); + m_mapped = true; + return result; + } + + auto scoped_mapping::unmap() -> void + { + m_mapper->unmap(m_page); + m_mapped = false; + } + + auto swap(scoped_mapping & lhs, scoped_mapping & rhs) -> void + { + using std::swap; + swap(lhs.m_page, rhs.m_page); + swap(lhs.m_mapper, rhs.m_mapper); + swap(lhs.m_mapped, rhs.m_mapped); + } + +} // namespace teachos::memory::x86_64
\ No newline at end of file diff --git a/arch/x86_64/src/stl/mutex.cpp b/arch/x86_64/src/stl/mutex.cpp deleted file mode 100644 index 232a11c..0000000 --- a/arch/x86_64/src/stl/mutex.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "arch/stl/mutex.hpp" - -namespace teachos::arch::stl -{ - auto mutex::lock() -> void - { - while (!try_lock()) - { - // Nothing to do - } - } - - auto mutex::try_lock() -> bool { return !locked.exchange(true, std::memory_order_acquire); } - - auto mutex::unlock() -> void { locked.store(false, std::memory_order_release); } -} // namespace teachos::arch::stl diff --git a/arch/x86_64/src/user/main.cpp b/arch/x86_64/src/user/main.cpp deleted file mode 100644 index 8b07e4a..0000000 --- a/arch/x86_64/src/user/main.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "arch/user/main.hpp" - -#include "arch/context_switching/syscall/main.hpp" -#include "arch/memory/heap/global_heap_allocator.hpp" - -#include <algorithm> -#include <array> -#include <atomic> -#include <ranges> - -namespace teachos::arch::user -{ - auto main() -> void - { - constexpr char syscall_message[] = "Successfully entered user mode and wrote to VGA buffer via syscall!"; - context_switching::syscall::syscall(context_switching::syscall::type::WRITE, - {reinterpret_cast<uint64_t>(&syscall_message)}); - - // Test C++ standard library - std::array<std::atomic<uint8_t>, 4> array_test = {std::atomic<uint8_t>{5}, std::atomic<uint8_t>{10}, - std::atomic<uint8_t>{15}, std::atomic<uint8_t>{20}}; - std::ranges::for_each(array_test, [](auto & item) { - auto value = item.load(); - uint8_t max_value = std::max(value, uint8_t{10}); - item.exchange(max_value + 2); - }); - - auto address = new uint64_t{10U}; - (void)address; - - for (;;) - { - } - } -} // namespace teachos::arch::user diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp new file mode 100644 index 0000000..c9eee71 --- /dev/null +++ b/arch/x86_64/src/vga/text.cpp @@ -0,0 +1,84 @@ +#include "x86_64/vga/text.hpp" + +#include "kapi/boot.hpp" + +#include "x86_64/boot/boot.hpp" +#include "x86_64/boot/ld.hpp" +#include "x86_64/vga/crtc.hpp" + +#include <algorithm> +#include <bit> +#include <cstddef> +#include <cstdint> +#include <span> +#include <string_view> +#include <utility> + +namespace teachos::vga::x86_64::text +{ + namespace + { + constexpr auto BUFFER_BASE_ADDRESS = std::uintptr_t{0xb8000}; + constexpr auto DEFAULT_TEXT_BUFFER_WIDTH = 80U; + constexpr auto DEFAULT_TEXT_BUFFER_HEIGHT = 25U; + constexpr auto CURSOR_ENABLED_BIT = 5U; + } // namespace + + std::span<device::glyph> const device::buffer = + std::span{std::bit_cast<device::glyph *>(BUFFER_BASE_ADDRESS + + std::bit_cast<std::uintptr_t>(&teachos::boot::x86_64::TEACHOS_VMA)), + DEFAULT_TEXT_BUFFER_WIDTH * DEFAULT_TEXT_BUFFER_HEIGHT}; + + device::device() + : m_position{boot::bootstrap_information.vga_buffer_index} + {} + + auto device::clear(attribute attribute) -> void + { + m_position = 0; + std::ranges::fill(buffer, std::pair{' ', std::bit_cast<std::byte>(attribute)}); + } + + auto device::cursor(bool enabled) -> void + { + auto cursor_disable_byte = std::byte{!enabled} << CURSOR_ENABLED_BIT; + + crtc::address::write(crtc::registers::cursor_start); + crtc::data::write(crtc::data::read() | cursor_disable_byte); + } + + auto device::newline() -> void + { + auto current_line = m_position / DEFAULT_TEXT_BUFFER_WIDTH; + auto next_line = current_line + 1; + + if (std::cmp_greater_equal(next_line, DEFAULT_TEXT_BUFFER_HEIGHT)) + { + auto begin = buffer.begin() + DEFAULT_TEXT_BUFFER_WIDTH; + auto end = buffer.begin() + DEFAULT_TEXT_BUFFER_WIDTH * DEFAULT_TEXT_BUFFER_HEIGHT; + std::ranges::move(begin, end, buffer.begin()); + m_position = current_line * DEFAULT_TEXT_BUFFER_WIDTH; + } + else + { + m_position = next_line * DEFAULT_TEXT_BUFFER_WIDTH; + } + } + + auto device::write(std::string_view code_points, attribute attribute) -> void + { + std::ranges::for_each(code_points, [&](auto code_point) -> void { write(code_point, attribute); }); + } + + auto device::write(char code_point, attribute attribute) -> void + { + buffer[m_position++] = std::pair{code_point, std::bit_cast<std::byte>(attribute)}; + }; + + auto device::writeln(std::string_view code_points, attribute attribute) -> void + { + std::ranges::for_each(code_points, [&](auto code_point) -> void { write(code_point, attribute); }); + newline(); + } + +} // namespace teachos::vga::x86_64::text diff --git a/arch/x86_64/src/video/vga/text.cpp b/arch/x86_64/src/video/vga/text.cpp deleted file mode 100644 index b070a0a..0000000 --- a/arch/x86_64/src/video/vga/text.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "arch/video/vga/text.hpp" - -#include "arch/video/vga/io.hpp" -#include "memory/asm_pointer.hpp" - -#include <algorithm> -#include <string_view> -#include <utility> - -extern "C" std::pair<char, teachos::arch::video::vga::text::attribute> * vga_buffer_pointer; - -namespace teachos::arch::video::vga::text -{ - namespace - { - auto constexpr DEFAULT_TEXT_BUFFER_WIDTH = 80U; - auto constexpr DEFAULT_TEXT_BUFFER_HEIGHT = 25U; - - auto constinit text_buffer = teachos::memory::asm_pointer{vga_buffer_pointer}; - } // namespace - - auto clear(attribute attribute) -> void - { - *text_buffer = reinterpret_cast<decltype(text_buffer)::pointer>(DEFAULT_VGA_TEXT_BUFFER_ADDRESS); - std::ranges::fill_n(*text_buffer, 2000, std::pair{' ', attribute}); - } - - auto cursor(bool enabled) -> void - { - auto cursor_disable_byte = std::byte{!enabled} << 5; - - crtc::address_port::write(crtc::registers::cursor_start); - crtc::data_port::write(vga::crtc::data_port::read() | cursor_disable_byte); - } - - auto newline() -> void - { - auto base = reinterpret_cast<decltype(text_buffer)::pointer>(DEFAULT_VGA_TEXT_BUFFER_ADDRESS); - auto & raw_buffer = *text_buffer; - auto current_line = (raw_buffer - base) / DEFAULT_TEXT_BUFFER_WIDTH; - auto next_line = current_line + 1; - - if (next_line >= DEFAULT_TEXT_BUFFER_HEIGHT) - { - auto begin = base + DEFAULT_TEXT_BUFFER_WIDTH; - auto end = base + DEFAULT_TEXT_BUFFER_WIDTH * DEFAULT_TEXT_BUFFER_HEIGHT; - std::ranges::move(begin, end, base); - raw_buffer = base + current_line * DEFAULT_TEXT_BUFFER_WIDTH; - } - else - { - raw_buffer = base + next_line * DEFAULT_TEXT_BUFFER_WIDTH; - } - } - - auto write_char(char code_point, attribute attribute) -> void - { - auto & p = *text_buffer; - (*p++) = std::pair{code_point, attribute}; - }; - - auto write(std::string_view code_points, attribute attribute) -> void - { - std::ranges::for_each(code_points, [&](auto code_point) { write_char(code_point, attribute); }); - } -} // namespace teachos::arch::video::vga::text |
