From beb7fb38ae16054bc5b3c245bc8d6fd05bea5ca3 Mon Sep 17 00:00:00 2001 From: Katharina Heidenreich Date: Sat, 4 Jul 2026 16:14:36 +0200 Subject: [PATCH] feat: implement core initialization and local APIC setup --- kernel/include/acpi/discovery.h | 1 - kernel/include/apic/core.h | 26 ++- kernel/include/apic/lapic.h | 24 +++ kernel/include/apic/timer.h | 25 ++- kernel/include/cpu/gdt.h | 58 +++++++ kernel/include/main.h | 3 + kernel/include/memory/allocator.h | 9 +- kernel/include/memory/pagetable.h | 2 +- kernel/include/util/msr.h | 16 ++ kernel/include/util/optional.h | 2 +- kernel/src/apic/core.cpp | 155 +++++++++++++++++ kernel/src/apic/lapic.cpp | 81 +++++++++ kernel/src/apic/timer.cpp | 4 +- kernel/src/cpu/gdt.cpp | 218 ++++++++++++++++++++++++ kernel/src/init/init.cpp | 29 +--- kernel/src/main.cpp | 7 + kernel/src/memory/allocator.cpp | 21 ++- kernel/src/memory/pagetable.cpp | 18 +- kernel/src/startup/secondaryStartup.cpp | 89 +++++++++- 19 files changed, 747 insertions(+), 41 deletions(-) create mode 100644 kernel/include/apic/lapic.h create mode 100644 kernel/include/cpu/gdt.h create mode 100644 kernel/include/main.h create mode 100644 kernel/include/util/msr.h create mode 100644 kernel/src/apic/lapic.cpp create mode 100644 kernel/src/cpu/gdt.cpp create mode 100644 kernel/src/main.cpp diff --git a/kernel/include/acpi/discovery.h b/kernel/include/acpi/discovery.h index 0c133da..23aaa15 100644 --- a/kernel/include/acpi/discovery.h +++ b/kernel/include/acpi/discovery.h @@ -10,7 +10,6 @@ namespace acpi { struct discovery_result_t { optional rsdp; optional madt; - // TODO(wiring): capture the FADT in setupAcpi and store it here optional fadt; }; diff --git a/kernel/include/apic/core.h b/kernel/include/apic/core.h index a86a3bf..1d1da06 100644 --- a/kernel/include/apic/core.h +++ b/kernel/include/apic/core.h @@ -2,6 +2,28 @@ #include "memory/pointer.h" +#include "acpi/discovery.h" +#include "memory/pagetable.h" + namespace apic { -void startupCore(paddr_t code, paddr_t stack); -} \ No newline at end of file + +struct stack_t { + paddr_t ptr; + size_t size; +}; + +using core_id = uint8_t; + +struct Core { + core_id id; + stack_t stack; + PageTable kernelPageTable; +}; + +optional initCore(acpi::Madt madt, core_id id); + +void initCores(acpi::discovery_result_t); + +void transitionStartupCore(); + +} // namespace apic \ No newline at end of file diff --git a/kernel/include/apic/lapic.h b/kernel/include/apic/lapic.h new file mode 100644 index 0000000..b9f8d13 --- /dev/null +++ b/kernel/include/apic/lapic.h @@ -0,0 +1,24 @@ +#pragma once + +#include "acpi/discovery.h" +#include "util/number.h" + +namespace apic { + +using lapic_id_t = uint32_t; + +void initLocalApic(const acpi::discovery_result_t& discovery); + +lapic_id_t localApicId(); + +void sendInit(lapic_id_t destination); + +// startup_page: physical address of the real-mode entry point divided by 4096; +// must point below 1 MiB, so only values < 0x100 are meaningful. +void sendStartup(lapic_id_t destination, uint8_t startup_page); + +void sendInterrupt(lapic_id_t destination, uint8_t vector); + +void signalEndOfInterrupt(); + +} // namespace apic diff --git a/kernel/include/apic/timer.h b/kernel/include/apic/timer.h index 22016b3..afec73e 100644 --- a/kernel/include/apic/timer.h +++ b/kernel/include/apic/timer.h @@ -33,6 +33,26 @@ constexpr duration_t operator*(uint64_t scalar, duration_t duration) { return duration_t{duration.timer_count * scalar}; } +constexpr bool operator==(duration_t left, duration_t right) { + return left.timer_count == right.timer_count; +} + +constexpr bool operator<(duration_t left, duration_t right) { + return left.timer_count < right.timer_count; +} + +constexpr bool operator<=(duration_t left, duration_t right) { + return left.timer_count <= right.timer_count; +} + +constexpr bool operator>(duration_t left, duration_t right) { + return left.timer_count > right.timer_count; +} + +constexpr bool operator>=(duration_t left, duration_t right) { + return left.timer_count >= right.timer_count; +} + constexpr timestamp_t operator+(timestamp_t timestamp, duration_t duration) { return timestamp_t{timestamp.timer_count + duration.timer_count}; } @@ -62,9 +82,4 @@ void busyWait(duration_t duration); timestamp_t now(); -// TODO: when dynamic allocation and lists are available -// struct Timer {}; -// -// Timer getCoreTimerInstance(); - } // namespace apic diff --git a/kernel/include/cpu/gdt.h b/kernel/include/cpu/gdt.h new file mode 100644 index 0000000..054838d --- /dev/null +++ b/kernel/include/cpu/gdt.h @@ -0,0 +1,58 @@ +#pragma once + +#include "memory/pointer.h" +#include "util/optional.h" + +struct [[gnu::packed]] gdt_register_t { + uint16_t limit; + uint64_t base; +}; +static_assert(sizeof(gdt_register_t) == 10); + +struct Gdt { + using selector_t = uint16_t; + + enum class segment_type_t : uint8_t { + code, + data, + task_state, + }; + + struct flags_t { + segment_type_t type; + uint8_t privilege_level; + bool long_mode; + bool default_operand_32bit; + bool granularity_4KiB; + bool readable_writeable; + }; + + struct segment_t { + flags_t flags; + uint64_t base; + uint32_t limit; + }; + + static Gdt fromCurrent(); + static optional allocNew(); + + void activate(); + optional add(segment_t segment); + optional get(selector_t selector); + + paddr_t base; + uint16_t limit; +}; + +struct [[gnu::packed]] tss_t { + uint32_t reserved0; + uint64_t rsp[3]; + uint64_t reserved1; + uint64_t ist[7]; + uint64_t reserved2; + uint16_t reserved3; + uint16_t iomap_base; +}; +static_assert(sizeof(tss_t) == 104); + +void reloadSegments(Gdt::selector_t code_selector, Gdt::selector_t data_selector); diff --git a/kernel/include/main.h b/kernel/include/main.h new file mode 100644 index 0000000..2332afb --- /dev/null +++ b/kernel/include/main.h @@ -0,0 +1,3 @@ +#pragma once + +[[noreturn]] void main(); diff --git a/kernel/include/memory/allocator.h b/kernel/include/memory/allocator.h index 1188352..82e6e79 100644 --- a/kernel/include/memory/allocator.h +++ b/kernel/include/memory/allocator.h @@ -12,10 +12,17 @@ struct PhysicalAllocator { size_t page_count; }; + enum class address_range_t : uint8_t { + any, + below_4gib, + }; + static void init(const multiboot::tag_mmap* multiboot_info); static PhysicalAllocator& getInstance(); - optional allocPages(size_t page_count, size_t alignment_pages = 1); + optional allocPages( + size_t page_count, size_t alignment_pages = 1, address_range_t range = address_range_t::any + ); void free(allocation_t ptr); bool force(allocation_t ptr, bool free); diff --git a/kernel/include/memory/pagetable.h b/kernel/include/memory/pagetable.h index 02e9bfd..8fade15 100644 --- a/kernel/include/memory/pagetable.h +++ b/kernel/include/memory/pagetable.h @@ -25,12 +25,12 @@ struct PageTable { }; static PageTable fromCurrent(); + static optional allocNew(); void activate(); bool map(void* vaddress, paddr_t paddress, flags_t flags); void unmap(void* vaddress); optional get(void* vaddress); -private: paddr_t l4; }; \ No newline at end of file diff --git a/kernel/include/util/msr.h b/kernel/include/util/msr.h new file mode 100644 index 0000000..59c167d --- /dev/null +++ b/kernel/include/util/msr.h @@ -0,0 +1,16 @@ +#pragma once + +#include "util/number.h" + +inline uint64_t msr_read(uint32_t msr) { + uint32_t low = 0; + uint32_t high = 0; + __asm__ volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(msr)); + return (static_cast(high) << 32) | low; +} + +inline void msr_write(uint32_t msr, uint64_t value) { + uint32_t low = static_cast(value); + uint32_t high = static_cast(value >> 32); + __asm__ volatile("wrmsr" : : "c"(msr), "a"(low), "d"(high)); +} diff --git a/kernel/include/util/optional.h b/kernel/include/util/optional.h index 91e1c1d..4826929 100644 --- a/kernel/include/util/optional.h +++ b/kernel/include/util/optional.h @@ -131,7 +131,7 @@ public: } } - operator bool() const { + explicit operator bool() const { return initialized; } diff --git a/kernel/src/apic/core.cpp b/kernel/src/apic/core.cpp index e69de29..7e87df7 100644 --- a/kernel/src/apic/core.cpp +++ b/kernel/src/apic/core.cpp @@ -0,0 +1,155 @@ +#include "apic/core.h" +#include "apic/lapic.h" +#include "apic/timer.h" +#include "cpu/gdt.h" +#include "memory/allocator.h" + +static volatile gdt_register_t secondary_gdt_register; + +static optional createSecondaryGdt() { + optional gdtOpt = Gdt::allocNew(); + if (!gdtOpt.has_value()) { + return {}; + } + Gdt gdt = gdtOpt.value(); + Gdt::segment_t codeSegment{}; + codeSegment.flags.type = Gdt::segment_type_t::code; + codeSegment.flags.privilege_level = 0; + codeSegment.flags.long_mode = true; + codeSegment.flags.readable_writeable = true; + Gdt::segment_t dataSegment{}; + dataSegment.flags.type = Gdt::segment_type_t::data; + dataSegment.flags.privilege_level = 0; + dataSegment.flags.readable_writeable = true; + optional codeSelector = gdt.add(codeSegment); + optional dataSelector = gdt.add(dataSegment); + if (codeSelector.value_or(0) != 0x08 || dataSelector.value_or(0) != 0x10) { + return {}; + } + return gdt; +} + +static bool waitForStartedInfo(volatile uint64_t* started_info, uint64_t expected, apic::duration_t timeout) { + auto start = apic::now(); + while (*started_info < expected) { + if (apic::now() - start > timeout) { + return false; + } + __asm__ volatile("pause"); + } + return true; +} + +bool sendStartupWithTimeout(uint8_t apicId, uint8_t page, volatile uint64_t* started_info) { + apic::sendStartup(apicId, page); + return waitForStartedInfo(started_info, 1, apic::milliseconds(10)); +} + +bool startupCore(uint8_t apicId, paddr_t code, paddr_t stack, PageTable pageTable, Gdt gdt) { + auto started_info_s = read_symbol("__started_info"); + auto stack_ptr_s = read_symbol("__stack_ptr"); + auto page_table_ptr_s = read_symbol("__page_table_ptr"); + auto gdt_addr_ptr_s = read_symbol("__gdt_addr_ptr"); + auto started_info = paddr_t{started_info_s}.access(); + auto stack_ptr = paddr_t{stack_ptr_s}.access(); + auto page_table_ptr = paddr_t{page_table_ptr_s}.access(); + auto gdt_addr_ptr = paddr_t{gdt_addr_ptr_s}.access(); + secondary_gdt_register.limit = gdt.limit; + secondary_gdt_register.base = gdt.base.address; + *started_info = 0; + *stack_ptr = stack.address; + *page_table_ptr = pageTable.l4.address; + *gdt_addr_ptr = reinterpret_cast(&secondary_gdt_register) - high_base; + uint8_t page = code.address / page_size; + apic::sendInit(apicId); + apic::busyWait(apic::milliseconds(10)); + if (!(sendStartupWithTimeout(apicId, page, started_info) // + || sendStartupWithTimeout(apicId, page, started_info))) { + return false; + } + return waitForStartedInfo(started_info, 2, apic::milliseconds(100)); +} + +PageTable createPageTable() { + PageTable pageTable = PageTable::allocNew().value(); + PageTable::flags_t cachedFlags = { + .writeable = 1, + .user_accessible = 0, + .write_through = 0, + .cache_disabled = 0, + .execute_disabled = 0, + .global = 0, + .granularity = PageTable::flags_t::granularity_t::page_1GiB + }; + for (uint64_t i = 0; i < 256; i++) { + auto vaddr = i * (1ull << 30); + auto paddr = i * (1ull << 30); + pageTable.map(reinterpret_cast(vaddr), paddr_t{paddr}, cachedFlags); + } + for (uint64_t i = 0; i < 256; i++) { + auto vaddr = i * (1ull << 30) + high_base; + auto paddr = i * (1ull << 30); + pageTable.map(reinterpret_cast(vaddr), paddr_t{paddr}, cachedFlags); + } + PageTable::flags_t uncachedFlags = { + .writeable = 1, + .user_accessible = 0, + .write_through = 1, + .cache_disabled = 1, + .execute_disabled = 0, + .global = 0, + .granularity = PageTable::flags_t::granularity_t::page_1GiB + }; + for (uint64_t i = 0; i < 256; i++) { + auto vaddr = i * (1ull << 30) + uncached_base; + auto paddr = i * (1ull << 30); + pageTable.map(reinterpret_cast(vaddr), paddr_t{paddr}, uncachedFlags); + } + return pageTable; +} + +optional apic::initCore(acpi::Madt madt, core_id id) { + optional apicId; + madt.for_each_entry([&](const acpi::madt_local_apic_t* apic) { + if (apic->acpi_processor_uid == id) { + apicId = apic->apic_id; + } + }); + if (!apicId.has_value()) { + return {}; + } + if (apicId.value() == apic::localApicId()) { + return {}; + } + apic::Core core{}; + size_t stackPageCount = 256; + paddr_t stackBase = PhysicalAllocator::getInstance().allocPages(stackPageCount).value().ptr; + paddr_t stackPtr = {stackBase.address + stackPageCount * page_size - 8}; + paddr_t code = {read_symbol("__16bitStartup")}; + auto pageTable = createPageTable(); + optional gdt = createSecondaryGdt(); + if (!gdt.has_value()) { + return {}; + } + if (!startupCore(apicId.value(), code, stackPtr, pageTable, gdt.value())) { + return {}; + } + return core; +} + +void apic::initCores(acpi::discovery_result_t discovery) { + if (!discovery.madt.has_value()) { + return; + } + discovery.madt->for_each_entry([&](const acpi::madt_local_apic_t* apic) { + apic::initCore(discovery.madt.value(), apic->acpi_processor_uid); + }); +} + +void apic::transitionStartupCore() {} + +extern "C" [[noreturn]] void __secondary_init() { + while (true) { + __asm__ volatile("hlt"); + } +} \ No newline at end of file diff --git a/kernel/src/apic/lapic.cpp b/kernel/src/apic/lapic.cpp new file mode 100644 index 0000000..01df3bb --- /dev/null +++ b/kernel/src/apic/lapic.cpp @@ -0,0 +1,81 @@ +#include "apic/lapic.h" + +#include "memory/pointer.h" +#include "util/msr.h" + +namespace { + +constexpr uint32_t apic_base_msr = 0x1B; +constexpr uint64_t apic_base_enable = uint64_t{1} << 11; +constexpr uint64_t apic_base_address_mask = 0x000FFFFFFFFFF000; + +constexpr size_t id_register = 0x20; +constexpr size_t eoi_register = 0xB0; +constexpr size_t spurious_vector_register = 0xF0; +constexpr size_t icr_low_register = 0x300; +constexpr size_t icr_high_register = 0x310; + +constexpr uint32_t software_enable = 1u << 8; +constexpr uint32_t spurious_vector = 0xFF; + +constexpr uint32_t delivery_mode_fixed = 0b000u << 8; +constexpr uint32_t delivery_mode_init = 0b101u << 8; +constexpr uint32_t delivery_mode_startup = 0b110u << 8; +constexpr uint32_t delivery_status_pending = 1u << 12; +constexpr uint32_t level_assert = 1u << 14; + +volatile uint32_t* lapic_registers = nullptr; + +volatile uint32_t& lapicRegister(size_t offset) { + return lapic_registers[offset / sizeof(uint32_t)]; +} + +void waitWhileDeliveryPending() { + while ((lapicRegister(icr_low_register) & delivery_status_pending) != 0) { + __asm__ volatile("pause"); + } +} + +void sendIpi(apic::lapic_id_t destination, uint32_t command) { + waitWhileDeliveryPending(); + lapicRegister(icr_high_register) = destination << 24; + lapicRegister(icr_low_register) = command; + waitWhileDeliveryPending(); +} + +paddr_t localApicPhysicalBase(const acpi::discovery_result_t& discovery) { + paddr_t msr_base{msr_read(apic_base_msr) & apic_base_address_mask}; + return discovery.madt + .map([](acpi::Madt madt) { + return madt.local_apic_address(); + }) + .value_or(msr_base); +} + +} // namespace + +void apic::initLocalApic(const acpi::discovery_result_t& discovery) { + msr_write(apic_base_msr, msr_read(apic_base_msr) | apic_base_enable); + lapic_registers = localApicPhysicalBase(discovery).access_uc(); + lapicRegister(spurious_vector_register) = spurious_vector | software_enable; +} + +apic::lapic_id_t apic::localApicId() { + return lapicRegister(id_register) >> 24; +} + +void apic::sendInit(lapic_id_t destination) { + sendIpi(destination, delivery_mode_init | level_assert); +} + +void apic::sendStartup(lapic_id_t destination, uint8_t startup_page) { + sendIpi(destination, delivery_mode_startup | level_assert | startup_page); +} + +void apic::sendInterrupt(lapic_id_t destination, uint8_t vector) { + sendIpi(destination, delivery_mode_fixed | level_assert | vector); +} + +void apic::signalEndOfInterrupt() { + lapicRegister(eoi_register) = 0; +} diff --git a/kernel/src/apic/timer.cpp b/kernel/src/apic/timer.cpp index 45aadcc..b913922 100644 --- a/kernel/src/apic/timer.cpp +++ b/kernel/src/apic/timer.cpp @@ -11,11 +11,11 @@ constexpr uint32_t processor_frequency_leaf = 0x16; constexpr uint32_t feature_edx_tsc = 1u << 4; -struct lapic_timer_calibration_t { +struct timer_calibration_t { uint64_t tsc_frequency_hz; }; -lapic_timer_calibration_t timer_calibration{}; +timer_calibration_t timer_calibration{}; bool isTscSupported() { return cpuid(feature_info_leaf) diff --git a/kernel/src/cpu/gdt.cpp b/kernel/src/cpu/gdt.cpp new file mode 100644 index 0000000..127f763 --- /dev/null +++ b/kernel/src/cpu/gdt.cpp @@ -0,0 +1,218 @@ +#include "cpu/gdt.h" +#include "memory/allocator.h" + +struct segment_descriptor_t { + uint64_t limit_low : 16; + uint64_t base_low : 24; + uint64_t accessed : 1; + uint64_t readable_writeable : 1; + uint64_t conforming_expand_down : 1; + uint64_t executable : 1; + uint64_t user_segment : 1; + uint64_t privilege_level : 2; + uint64_t present : 1; + uint64_t limit_high : 4; + uint64_t available : 1; + uint64_t long_mode : 1; + uint64_t default_operand_32bit : 1; + uint64_t granularity_4KiB : 1; + uint64_t base_high : 8; +}; +static_assert(sizeof(segment_descriptor_t) == sizeof(uint64_t)); + +struct system_descriptor_t { + uint64_t limit_low : 16; + uint64_t base_low : 24; + uint64_t type : 4; + uint64_t user_segment : 1; + uint64_t privilege_level : 2; + uint64_t present : 1; + uint64_t limit_high : 4; + uint64_t available : 1; + uint64_t reserved_long_mode : 1; + uint64_t reserved_default_operand_32bit : 1; + uint64_t granularity_4KiB : 1; + uint64_t base_high : 8; + uint64_t base_upper : 32; + uint64_t reserved : 32; +}; +static_assert(sizeof(system_descriptor_t) == 2 * sizeof(uint64_t)); + +constexpr uint64_t task_state_available_type = 0b1001; +constexpr uint64_t task_state_busy_type = 0b1011; + +static size_t entry_count_of(uint16_t limit) { + return (static_cast(limit) + 1) / sizeof(uint64_t); +} + +static bool is_present(uint64_t entry) { + const auto* descriptor = reinterpret_cast(&entry); + return descriptor->present; +} + +static bool is_task_state_low_half(uint64_t entry) { + const auto* descriptor = reinterpret_cast(&entry); + if (!descriptor->present) { + return false; + } + if (descriptor->user_segment) { + return false; + } + return descriptor->type == task_state_available_type || descriptor->type == task_state_busy_type; +} + +static segment_descriptor_t encode_segment(Gdt::segment_t segment) { + segment_descriptor_t descriptor{}; + descriptor.limit_low = segment.limit & 0xFFFF; + descriptor.limit_high = (segment.limit >> 16) & 0xF; + descriptor.base_low = segment.base & 0xFFFFFF; + descriptor.base_high = (segment.base >> 24) & 0xFF; + descriptor.readable_writeable = segment.flags.readable_writeable; + descriptor.executable = segment.flags.type == Gdt::segment_type_t::code; + descriptor.user_segment = 1; + descriptor.privilege_level = segment.flags.privilege_level; + descriptor.present = 1; + descriptor.long_mode = segment.flags.long_mode; + descriptor.default_operand_32bit = segment.flags.default_operand_32bit; + descriptor.granularity_4KiB = segment.flags.granularity_4KiB; + return descriptor; +} + +static system_descriptor_t encode_task_state(Gdt::segment_t segment) { + system_descriptor_t descriptor{}; + descriptor.limit_low = segment.limit & 0xFFFF; + descriptor.limit_high = (segment.limit >> 16) & 0xF; + descriptor.base_low = segment.base & 0xFFFFFF; + descriptor.base_high = (segment.base >> 24) & 0xFF; + descriptor.base_upper = segment.base >> 32; + descriptor.type = task_state_available_type; + descriptor.user_segment = 0; + descriptor.privilege_level = segment.flags.privilege_level; + descriptor.present = 1; + descriptor.granularity_4KiB = segment.flags.granularity_4KiB; + return descriptor; +} + +static Gdt::segment_t decode_task_state(const system_descriptor_t* descriptor) { + Gdt::segment_t segment{}; + segment.flags.type = Gdt::segment_type_t::task_state; + segment.flags.privilege_level = descriptor->privilege_level; + segment.flags.granularity_4KiB = descriptor->granularity_4KiB; + segment.base = static_cast(descriptor->base_low) | (static_cast(descriptor->base_high) << 24) | + (static_cast(descriptor->base_upper) << 32); + segment.limit = static_cast(descriptor->limit_low | (descriptor->limit_high << 16)); + return segment; +} + +static Gdt::segment_t decode_segment(const segment_descriptor_t* descriptor) { + Gdt::segment_t segment{}; + segment.flags.type = descriptor->executable ? Gdt::segment_type_t::code : Gdt::segment_type_t::data; + segment.flags.privilege_level = descriptor->privilege_level; + segment.flags.long_mode = descriptor->long_mode; + segment.flags.default_operand_32bit = descriptor->default_operand_32bit; + segment.flags.granularity_4KiB = descriptor->granularity_4KiB; + segment.flags.readable_writeable = descriptor->readable_writeable; + segment.base = static_cast(descriptor->base_low) | (static_cast(descriptor->base_high) << 24); + segment.limit = static_cast(descriptor->limit_low | (descriptor->limit_high << 16)); + return segment; +} + +Gdt Gdt::fromCurrent() { + gdt_register_t gdt_register{}; + asm volatile("sgdt %0" : "=m"(gdt_register)); + Gdt gdt{}; + gdt.limit = gdt_register.limit; + if (gdt_register.base >= high_base) { + gdt.base = paddr_t{gdt_register.base - high_base}; + } else { + gdt.base = paddr_t{gdt_register.base}; + } + return gdt; +} + +optional Gdt::allocNew() { + auto allocation = + PhysicalAllocator::getInstance().allocPages(1, 1, PhysicalAllocator::address_range_t::below_4gib); + if (!allocation.has_value()) { + return {}; + } + memset(allocation->ptr.access(), 0, page_size); + Gdt gdt{}; + gdt.base = allocation->ptr; + gdt.limit = page_size - 1; + return gdt; +} + +void Gdt::activate() { + gdt_register_t gdt_register{}; + gdt_register.limit = limit; + gdt_register.base = reinterpret_cast(base.access()); + asm volatile("lgdt %0" : : "m"(gdt_register)); +} + +optional Gdt::add(segment_t segment) { + uint64_t* entries = base.access(); + size_t entry_count = entry_count_of(limit); + size_t needed_slots = segment.flags.type == segment_type_t::task_state ? 2 : 1; + size_t index = 1; + while (index + needed_slots <= entry_count) { + if (is_task_state_low_half(entries[index])) { + index += 2; + continue; + } + if (is_present(entries[index])) { + index += 1; + continue; + } + if (needed_slots == 2 && is_present(entries[index + 1])) { + index += 1; + continue; + } + if (segment.flags.type == segment_type_t::task_state) { + *reinterpret_cast(&entries[index]) = encode_task_state(segment); + } else { + *reinterpret_cast(&entries[index]) = encode_segment(segment); + } + return static_cast(index * sizeof(uint64_t)); + } + return {}; +} + +optional Gdt::get(selector_t selector) { + size_t index = selector / sizeof(uint64_t); + size_t entry_count = entry_count_of(limit); + if (index == 0 || index >= entry_count) { + return {}; + } + uint64_t* entries = base.access(); + if (is_task_state_low_half(entries[index])) { + if (index + 2 > entry_count) { + return {}; + } + return decode_task_state(reinterpret_cast(&entries[index])); + } + if (!is_present(entries[index])) { + return {}; + } + return decode_segment(reinterpret_cast(&entries[index])); +} + +void reloadSegments(Gdt::selector_t code_selector, Gdt::selector_t data_selector) { + uint64_t code = code_selector; + uint16_t data = data_selector; + asm volatile( + "pushq %[code]\n" + "leaq 1f(%%rip), %%rax\n" + "pushq %%rax\n" + "lretq\n" + "1:\n" + "mov %[data], %%ds\n" + "mov %[data], %%es\n" + "mov %[data], %%fs\n" + "mov %[data], %%gs\n" + "mov %[data], %%ss\n" + : + : [code] "r"(code), [data] "r"(data) + : "rax", "memory" + ); +} diff --git a/kernel/src/init/init.cpp b/kernel/src/init/init.cpp index 6feb16d..5b140a6 100644 --- a/kernel/src/init/init.cpp +++ b/kernel/src/init/init.cpp @@ -3,6 +3,8 @@ #include "acpi/discovery.h" #include "acpi/fadt.h" #include "acpi/madt.h" +#include "apic/core.h" +#include "apic/lapic.h" #include "apic/timer.h" #include "init/multiboot2.h" #include "init/print.h" @@ -211,31 +213,18 @@ static void calibrateTimer(acpi::discovery_result_t acpi) { } } -static void demonstateTimer() { - auto time1 = apic::now(); - print("Time 1: "); - print_hex(time1.timer_count); - print("\nTime 2: "); - auto time2 = apic::now(); - print_hex(time2.timer_count); - print("\n"); - auto duration = time2 - time1; - auto readableDuration = apic::toSecondsAndNanoseconds(duration); - print_dec(readableDuration.seconds); - print("s "); - print_dec(readableDuration.nanoseconds); - print("ns\n"); - print("Wait for 5s!\n"); - apic::busyWait(apic::seconds(5)); - print("Waited for 5s!\n"); -} - [[maybe_unused]] [[noreturn]] __attribute__((used)) void init() { initFromLow(); print("Reached init\n"); step("memory", setupMemory); auto acpi = step("acpi", setupAcpi); + step("lapic", [&]() { + apic::initLocalApic(acpi); + }); + print("BSP local APIC id: "); + print_dec(apic::localApicId()); + print("\n"); step("timer", partial(calibrateTimer, acpi)); - demonstateTimer(); + step("cpu startup", partial(apic::initCores, acpi)); halt(); } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp new file mode 100644 index 0000000..49a3652 --- /dev/null +++ b/kernel/src/main.cpp @@ -0,0 +1,7 @@ +#include "main.h" + +[[noreturn]] void main() { + while (true) { + __asm__ volatile("hlt"); + } +} diff --git a/kernel/src/memory/allocator.cpp b/kernel/src/memory/allocator.cpp index 80ec9aa..ccc1747 100644 --- a/kernel/src/memory/allocator.cpp +++ b/kernel/src/memory/allocator.cpp @@ -286,7 +286,7 @@ struct PhysicalAllocatorImpl { return true; } - optional allocPages(size_t page_count, size_t alignment_pages) { + optional allocPages(size_t page_count, size_t alignment_pages, PhysicalAllocator::address_range_t range) { if (page_count == 0) { return {}; } @@ -297,6 +297,11 @@ struct PhysicalAllocatorImpl { return {}; } + uint64_t page_limit = ~uint64_t{0}; + if (range == PhysicalAllocator::address_range_t::below_4gib) { + page_limit = (uint64_t{1} << 32) / page_size; + } + Cursor cursor = cursor_at(0); uint64_t run_start = 0; size_t i = 0; @@ -306,9 +311,16 @@ struct PhysicalAllocatorImpl { if (length == 0 && prev_zero) { break; } + if (run_start >= page_limit) { + break; + } if ((i & 1) && length > 0) { + uint64_t run_end = run_start + length; + if (run_end > page_limit) { + run_end = page_limit; + } uint64_t aligned_start = ((run_start + alignment_pages - 1) / alignment_pages) * alignment_pages; - if (aligned_start + page_count <= run_start + length) { + if (aligned_start + page_count <= run_end) { set_range(aligned_start, page_count, false); reclaim_nodes(); return allocation_t{paddr_t{aligned_start * page_size}, page_count}; @@ -331,8 +343,9 @@ bool PhysicalAllocator::force(allocation_t ptr, bool free) { return PhysicalAllocatorImpl{*this}.force(ptr, free); } -optional PhysicalAllocator::allocPages(size_t page_count, size_t alignment_pages) { - return PhysicalAllocatorImpl{*this}.allocPages(page_count, alignment_pages); +optional +PhysicalAllocator::allocPages(size_t page_count, size_t alignment_pages, address_range_t range) { + return PhysicalAllocatorImpl{*this}.allocPages(page_count, alignment_pages, range); } void PhysicalAllocator::free(allocation_t ptr) { diff --git a/kernel/src/memory/pagetable.cpp b/kernel/src/memory/pagetable.cpp index 90efbd4..d931609 100644 --- a/kernel/src/memory/pagetable.cpp +++ b/kernel/src/memory/pagetable.cpp @@ -129,7 +129,8 @@ static optional walk(level1_table_t* table, void* vaddress, Fn&& struct ascend_t {}; template -static optional walk(entry_table_t>* table, void* vaddress, Fn&& fn) { +static optional +walk(entry_table_t>* table, void* vaddress, Fn&& fn) { using entry_t = table_or_huge_entry_t; entry_t& entry = table->entries[table_index(vaddress)]; if constexpr (requires { fn(entry); }) { @@ -176,7 +177,8 @@ static void reclaim_if_empty(table_or_huge_entry_t& entry) { if (entry.subtable.huge_page) { return; } - entry_table_t* child_table = address_of(entry.subtable).template access>(); + entry_table_t* child_table = + address_of(entry.subtable).template access>(); if (!is_table_empty(child_table)) { return; } @@ -269,6 +271,18 @@ PageTable PageTable::fromCurrent() { return table; } +optional PageTable::allocNew() { + auto alloc = PhysicalAllocator::getInstance().allocPages(1, 1, PhysicalAllocator::address_range_t::below_4gib); + if (!alloc.has_value()) { + return {}; + } + auto addr = alloc->ptr; + memset(addr.access(), 0, page_size); + PageTable table{}; + table.l4 = addr; + return table; +} + bool PageTable::map(void* vaddress, paddr_t paddress, flags_t flags) { size_t target_level = target_level_of(flags.granularity); bool mapped = false; diff --git a/kernel/src/startup/secondaryStartup.cpp b/kernel/src/startup/secondaryStartup.cpp index 4d214d8..76858fc 100644 --- a/kernel/src/startup/secondaryStartup.cpp +++ b/kernel/src/startup/secondaryStartup.cpp @@ -1,5 +1,90 @@ asm(R"( -.section .trampoline_text +.global __16bitStartup +.global __started_info +.global __stack_ptr +.global __page_table_ptr +.global __gdt_addr_ptr + +.extern __secondary_init + +.section .trampoline_text, "ax" .align 0x1000 .code16 -)"); \ No newline at end of file +__16bitStartup: + cli + movw $1, __started_info + mov $__tmp_gdt_ptr, %eax + lgdtl (%eax) + mov %cr0, %eax + or $1, %eax + mov %eax, %cr0 + ljmp $0x08, $__tmp32BitStartup +stop: + hlt + jmp stop + +.code32 +__tmp32BitStartup: + mov $0x10, %eax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + + mov __page_table_ptr, %eax + mov %eax, %cr3 + + mov %cr4, %eax + or $(0x1 << 5), %eax + mov %eax, %cr4 + + mov $0xC0000080, %ecx + rdmsr + or $((0x1 << 8) | (0x1 << 11)), %eax + wrmsr + + mov %cr0, %eax + or $(0x1 << 31), %eax + mov %eax, %cr0 + + mov $__gdt_addr_ptr, %eax + mov (%eax), %eax + lgdt (%eax) + jmp $0b1000, $__tmp64BitStartup +.code64 +__tmp64BitStartup: + mov $0b10000, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + mov __stack_ptr, %rsp + movq $2, __started_info + movabsq $__secondary_init, %rax + jmp* %rax + +.section .trampoline_data, "aw" +.align 0x1000 +__started_info: +.quad 0 # write here 1 as soon as started, 2 as soon as in 64bit mode and all tmp resources released +__stack_ptr: +.quad 0 # pointer to top of stack +__page_table_ptr: +.quad 0 # value of l4 page table +__gdt_addr_ptr: +.quad 0 # pointer to gdt descriptor (limit word + base) to load before entering 64 bit mode +__tmp_stack: + .skip 0x1000 + .align 8 +__tmp_stack_end: + .quad 0 +__tmp_gdt: + .quad 0 # zero + .quad (0xFFFF) | (0xF << 48) | (1 << 43) | (1 << 44) | (1 << 47) | (1 << 54) | (1 << 55) # 32 bit code + .quad (0xFFFF) | (0xF << 48) | (1 << 41) | (1 << 44) | (1 << 47) | (1 << 54) | (1 << 55) # 32 bit data +__tmp_gdt_ptr: + .word __tmp_gdt_ptr - __tmp_gdt - 1 + .long __tmp_gdt +)");