aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src/boot/boot32.S
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2025-12-15 17:13:12 +0100
committerFelix Morgner <felix.morgner@ost.ch>2025-12-15 17:13:12 +0100
commit7b9482ae637126ac9337876e60f519b493437711 (patch)
tree6fc71a253c8b0325d303bd34c95b564ba536ed14 /arch/x86_64/src/boot/boot32.S
parent116f9332a206767c45095950f09f7c7447b561cf (diff)
parenta9eeec745e29d89afd48ee43d09432eb6fc35be7 (diff)
downloadteachos-7b9482ae637126ac9337876e60f519b493437711.tar.xz
teachos-7b9482ae637126ac9337876e60f519b493437711.zip
os: rework kernel architecture
Rework the code structure and architecture of the kernel by separating platform-dependent and platform-independent code more cleanly. As of this patchset, full feature parity has not been achieved. Nonetheless, a sufficient subset of functionality has been ported to the new architecture to demonstrate the feasibility of the new structure.
Diffstat (limited to 'arch/x86_64/src/boot/boot32.S')
-rw-r--r--arch/x86_64/src/boot/boot32.S449
1 files changed, 449 insertions, 0 deletions
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