aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86_64/src')
-rw-r--r--arch/x86_64/src/boot/boot.s368
-rw-r--r--arch/x86_64/src/boot/boot32.S449
-rw-r--r--arch/x86_64/src/boot/crti.s13
-rw-r--r--arch/x86_64/src/boot/crtn.s9
-rw-r--r--arch/x86_64/src/boot/entry64.s60
-rw-r--r--arch/x86_64/src/boot/initialize_runtime.cpp26
-rw-r--r--arch/x86_64/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp24
-rw-r--r--arch/x86_64/src/context_switching/interrupt_descriptor_table/idt_flags.cpp17
-rw-r--r--arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp53
-rw-r--r--arch/x86_64/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp13
-rw-r--r--arch/x86_64/src/context_switching/interrupt_descriptor_table/ist_offset.cpp10
-rw-r--r--arch/x86_64/src/context_switching/interrupt_descriptor_table/segment_selector.cpp15
-rw-r--r--arch/x86_64/src/context_switching/main.cpp63
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/access_byte.cpp17
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/gdt_flags.cpp20
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table.cpp109
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/global_descriptor_table_pointer.cpp11
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_base.cpp38
-rw-r--r--arch/x86_64/src/context_switching/segment_descriptor_table/segment_descriptor_extension.cpp24
-rw-r--r--arch/x86_64/src/context_switching/syscall/main.cpp35
-rw-r--r--arch/x86_64/src/context_switching/syscall/syscall_enable.cpp32
-rw-r--r--arch/x86_64/src/context_switching/syscall/syscall_handler.cpp118
-rw-r--r--arch/x86_64/src/exception_handling/abort.cpp15
-rw-r--r--arch/x86_64/src/exception_handling/assert.cpp15
-rw-r--r--arch/x86_64/src/exception_handling/panic.cpp22
-rw-r--r--arch/x86_64/src/exception_handling/pure_virtual.cpp6
-rw-r--r--arch/x86_64/src/interrupt_handling/generic_interrupt_handler.cpp13
-rw-r--r--arch/x86_64/src/kapi/cio.cpp20
-rw-r--r--arch/x86_64/src/kapi/cpu.cpp12
-rw-r--r--arch/x86_64/src/kapi/memory.cpp193
-rw-r--r--arch/x86_64/src/kernel/cpu/call.cpp9
-rw-r--r--arch/x86_64/src/kernel/cpu/control_register.cpp66
-rw-r--r--arch/x86_64/src/kernel/cpu/gdtr.cpp19
-rw-r--r--arch/x86_64/src/kernel/cpu/idtr.cpp18
-rw-r--r--arch/x86_64/src/kernel/cpu/if.cpp7
-rw-r--r--arch/x86_64/src/kernel/cpu/msr.cpp31
-rw-r--r--arch/x86_64/src/kernel/cpu/segment_register.cpp98
-rw-r--r--arch/x86_64/src/kernel/cpu/tlb.cpp16
-rw-r--r--arch/x86_64/src/kernel/cpu/tr.cpp16
-rw-r--r--arch/x86_64/src/kernel/main.cpp71
-rw-r--r--arch/x86_64/src/memory/allocator/area_frame_allocator.cpp85
-rw-r--r--arch/x86_64/src/memory/allocator/physical_frame.cpp24
-rw-r--r--arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp34
-rw-r--r--arch/x86_64/src/memory/heap/bump_allocator.cpp54
-rw-r--r--arch/x86_64/src/memory/heap/global_heap_allocator.cpp135
-rw-r--r--arch/x86_64/src/memory/heap/linked_list_allocator.cpp177
-rw-r--r--arch/x86_64/src/memory/heap/memory_block.cpp15
-rw-r--r--arch/x86_64/src/memory/heap/user_heap_allocator.cpp200
-rw-r--r--arch/x86_64/src/memory/kernel_mapper.cpp111
-rw-r--r--arch/x86_64/src/memory/main.cpp77
-rw-r--r--arch/x86_64/src/memory/mmu.cpp21
-rw-r--r--arch/x86_64/src/memory/multiboot/elf_symbols_section.cpp13
-rw-r--r--arch/x86_64/src/memory/multiboot/reader.cpp131
-rw-r--r--arch/x86_64/src/memory/page_table.cpp82
-rw-r--r--arch/x86_64/src/memory/paging/active_page_table.cpp98
-rw-r--r--arch/x86_64/src/memory/paging/inactive_page_table.cpp20
-rw-r--r--arch/x86_64/src/memory/paging/page_entry.cpp63
-rw-r--r--arch/x86_64/src/memory/paging/page_table.cpp128
-rw-r--r--arch/x86_64/src/memory/paging/temporary_page.cpp29
-rw-r--r--arch/x86_64/src/memory/paging/virtual_page.cpp33
-rw-r--r--arch/x86_64/src/memory/paging_root.cpp19
-rw-r--r--arch/x86_64/src/memory/recursive_page_mapper.cpp119
-rw-r--r--arch/x86_64/src/memory/region_allocator.cpp92
-rw-r--r--arch/x86_64/src/memory/scoped_mapping.cpp69
-rw-r--r--arch/x86_64/src/stl/mutex.cpp16
-rw-r--r--arch/x86_64/src/user/main.cpp35
-rw-r--r--arch/x86_64/src/vga/text.cpp84
-rw-r--r--arch/x86_64/src/video/vga/text.cpp66
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