feat: implement core initialization and local APIC setup

This commit is contained in:
Katharina 2026-07-04 16:14:36 +02:00
parent ada1859910
commit beb7fb38ae
19 changed files with 747 additions and 41 deletions

View file

@ -10,7 +10,6 @@ namespace acpi {
struct discovery_result_t { struct discovery_result_t {
optional<Rsdp> rsdp; optional<Rsdp> rsdp;
optional<Madt> madt; optional<Madt> madt;
// TODO(wiring): capture the FADT in setupAcpi and store it here
optional<Fadt> fadt; optional<Fadt> fadt;
}; };

View file

@ -2,6 +2,28 @@
#include "memory/pointer.h" #include "memory/pointer.h"
#include "acpi/discovery.h"
#include "memory/pagetable.h"
namespace apic { namespace apic {
void startupCore(paddr_t code, paddr_t stack);
} 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<Core> initCore(acpi::Madt madt, core_id id);
void initCores(acpi::discovery_result_t);
void transitionStartupCore();
} // namespace apic

View file

@ -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

View file

@ -33,6 +33,26 @@ constexpr duration_t operator*(uint64_t scalar, duration_t duration) {
return duration_t{duration.timer_count * scalar}; 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) { constexpr timestamp_t operator+(timestamp_t timestamp, duration_t duration) {
return timestamp_t{timestamp.timer_count + duration.timer_count}; return timestamp_t{timestamp.timer_count + duration.timer_count};
} }
@ -62,9 +82,4 @@ void busyWait(duration_t duration);
timestamp_t now(); timestamp_t now();
// TODO: when dynamic allocation and lists are available
// struct Timer {};
//
// Timer getCoreTimerInstance();
} // namespace apic } // namespace apic

58
kernel/include/cpu/gdt.h Normal file
View file

@ -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<Gdt> allocNew();
void activate();
optional<selector_t> add(segment_t segment);
optional<segment_t> 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);

3
kernel/include/main.h Normal file
View file

@ -0,0 +1,3 @@
#pragma once
[[noreturn]] void main();

View file

@ -12,10 +12,17 @@ struct PhysicalAllocator {
size_t page_count; size_t page_count;
}; };
enum class address_range_t : uint8_t {
any,
below_4gib,
};
static void init(const multiboot::tag_mmap* multiboot_info); static void init(const multiboot::tag_mmap* multiboot_info);
static PhysicalAllocator& getInstance(); static PhysicalAllocator& getInstance();
optional<allocation_t> allocPages(size_t page_count, size_t alignment_pages = 1); optional<allocation_t> allocPages(
size_t page_count, size_t alignment_pages = 1, address_range_t range = address_range_t::any
);
void free(allocation_t ptr); void free(allocation_t ptr);
bool force(allocation_t ptr, bool free); bool force(allocation_t ptr, bool free);

View file

@ -25,12 +25,12 @@ struct PageTable {
}; };
static PageTable fromCurrent(); static PageTable fromCurrent();
static optional<PageTable> allocNew();
void activate(); void activate();
bool map(void* vaddress, paddr_t paddress, flags_t flags); bool map(void* vaddress, paddr_t paddress, flags_t flags);
void unmap(void* vaddress); void unmap(void* vaddress);
optional<info_t> get(void* vaddress); optional<info_t> get(void* vaddress);
private:
paddr_t l4; paddr_t l4;
}; };

16
kernel/include/util/msr.h Normal file
View file

@ -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<uint64_t>(high) << 32) | low;
}
inline void msr_write(uint32_t msr, uint64_t value) {
uint32_t low = static_cast<uint32_t>(value);
uint32_t high = static_cast<uint32_t>(value >> 32);
__asm__ volatile("wrmsr" : : "c"(msr), "a"(low), "d"(high));
}

View file

@ -131,7 +131,7 @@ public:
} }
} }
operator bool() const { explicit operator bool() const {
return initialized; return initialized;
} }

View file

@ -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<Gdt> createSecondaryGdt() {
optional<Gdt> 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<Gdt::selector_t> codeSelector = gdt.add(codeSegment);
optional<Gdt::selector_t> 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<volatile uint64_t>();
auto stack_ptr = paddr_t{stack_ptr_s}.access<volatile uint64_t>();
auto page_table_ptr = paddr_t{page_table_ptr_s}.access<volatile uint64_t>();
auto gdt_addr_ptr = paddr_t{gdt_addr_ptr_s}.access<volatile uint64_t>();
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<uint64_t>(&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<void*>(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<void*>(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<void*>(vaddr), paddr_t{paddr}, uncachedFlags);
}
return pageTable;
}
optional<apic::Core> apic::initCore(acpi::Madt madt, core_id id) {
optional<uint8_t> 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> 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");
}
}

81
kernel/src/apic/lapic.cpp Normal file
View file

@ -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<volatile uint32_t>();
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;
}

View file

@ -11,11 +11,11 @@ constexpr uint32_t processor_frequency_leaf = 0x16;
constexpr uint32_t feature_edx_tsc = 1u << 4; constexpr uint32_t feature_edx_tsc = 1u << 4;
struct lapic_timer_calibration_t { struct timer_calibration_t {
uint64_t tsc_frequency_hz; uint64_t tsc_frequency_hz;
}; };
lapic_timer_calibration_t timer_calibration{}; timer_calibration_t timer_calibration{};
bool isTscSupported() { bool isTscSupported() {
return cpuid(feature_info_leaf) return cpuid(feature_info_leaf)

218
kernel/src/cpu/gdt.cpp Normal file
View file

@ -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<size_t>(limit) + 1) / sizeof(uint64_t);
}
static bool is_present(uint64_t entry) {
const auto* descriptor = reinterpret_cast<const segment_descriptor_t*>(&entry);
return descriptor->present;
}
static bool is_task_state_low_half(uint64_t entry) {
const auto* descriptor = reinterpret_cast<const system_descriptor_t*>(&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<uint64_t>(descriptor->base_low) | (static_cast<uint64_t>(descriptor->base_high) << 24) |
(static_cast<uint64_t>(descriptor->base_upper) << 32);
segment.limit = static_cast<uint32_t>(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<uint64_t>(descriptor->base_low) | (static_cast<uint64_t>(descriptor->base_high) << 24);
segment.limit = static_cast<uint32_t>(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> Gdt::allocNew() {
auto allocation =
PhysicalAllocator::getInstance().allocPages(1, 1, PhysicalAllocator::address_range_t::below_4gib);
if (!allocation.has_value()) {
return {};
}
memset(allocation->ptr.access<uint8_t>(), 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<uint64_t>(base.access<uint8_t>());
asm volatile("lgdt %0" : : "m"(gdt_register));
}
optional<Gdt::selector_t> Gdt::add(segment_t segment) {
uint64_t* entries = base.access<uint64_t>();
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<system_descriptor_t*>(&entries[index]) = encode_task_state(segment);
} else {
*reinterpret_cast<segment_descriptor_t*>(&entries[index]) = encode_segment(segment);
}
return static_cast<selector_t>(index * sizeof(uint64_t));
}
return {};
}
optional<Gdt::segment_t> 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<uint64_t>();
if (is_task_state_low_half(entries[index])) {
if (index + 2 > entry_count) {
return {};
}
return decode_task_state(reinterpret_cast<const system_descriptor_t*>(&entries[index]));
}
if (!is_present(entries[index])) {
return {};
}
return decode_segment(reinterpret_cast<const segment_descriptor_t*>(&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"
);
}

View file

@ -3,6 +3,8 @@
#include "acpi/discovery.h" #include "acpi/discovery.h"
#include "acpi/fadt.h" #include "acpi/fadt.h"
#include "acpi/madt.h" #include "acpi/madt.h"
#include "apic/core.h"
#include "apic/lapic.h"
#include "apic/timer.h" #include "apic/timer.h"
#include "init/multiboot2.h" #include "init/multiboot2.h"
#include "init/print.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() { [[maybe_unused]] [[noreturn]] __attribute__((used)) void init() {
initFromLow(); initFromLow();
print("Reached init\n"); print("Reached init\n");
step("memory", setupMemory); step("memory", setupMemory);
auto acpi = step("acpi", setupAcpi); 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)); step("timer", partial(calibrateTimer, acpi));
demonstateTimer(); step("cpu startup", partial(apic::initCores, acpi));
halt(); halt();
} }

7
kernel/src/main.cpp Normal file
View file

@ -0,0 +1,7 @@
#include "main.h"
[[noreturn]] void main() {
while (true) {
__asm__ volatile("hlt");
}
}

View file

@ -286,7 +286,7 @@ struct PhysicalAllocatorImpl {
return true; return true;
} }
optional<allocation_t> allocPages(size_t page_count, size_t alignment_pages) { optional<allocation_t> allocPages(size_t page_count, size_t alignment_pages, PhysicalAllocator::address_range_t range) {
if (page_count == 0) { if (page_count == 0) {
return {}; return {};
} }
@ -297,6 +297,11 @@ struct PhysicalAllocatorImpl {
return {}; 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); Cursor cursor = cursor_at(0);
uint64_t run_start = 0; uint64_t run_start = 0;
size_t i = 0; size_t i = 0;
@ -306,9 +311,16 @@ struct PhysicalAllocatorImpl {
if (length == 0 && prev_zero) { if (length == 0 && prev_zero) {
break; break;
} }
if (run_start >= page_limit) {
break;
}
if ((i & 1) && length > 0) { 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; 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); set_range(aligned_start, page_count, false);
reclaim_nodes(); reclaim_nodes();
return allocation_t{paddr_t{aligned_start * page_size}, page_count}; 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); return PhysicalAllocatorImpl{*this}.force(ptr, free);
} }
optional<PhysicalAllocator::allocation_t> PhysicalAllocator::allocPages(size_t page_count, size_t alignment_pages) { optional<PhysicalAllocator::allocation_t>
return PhysicalAllocatorImpl{*this}.allocPages(page_count, alignment_pages); 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) { void PhysicalAllocator::free(allocation_t ptr) {

View file

@ -129,7 +129,8 @@ static optional<frame_entry_t*> walk(level1_table_t* table, void* vaddress, Fn&&
struct ascend_t {}; struct ascend_t {};
template<typename NextEntryType, typename Fn> template<typename NextEntryType, typename Fn>
static optional<frame_entry_t*> walk(entry_table_t<table_or_huge_entry_t<NextEntryType>>* table, void* vaddress, Fn&& fn) { static optional<frame_entry_t*>
walk(entry_table_t<table_or_huge_entry_t<NextEntryType>>* table, void* vaddress, Fn&& fn) {
using entry_t = table_or_huge_entry_t<NextEntryType>; using entry_t = table_or_huge_entry_t<NextEntryType>;
entry_t& entry = table->entries[table_index<entry_t::level>(vaddress)]; entry_t& entry = table->entries[table_index<entry_t::level>(vaddress)];
if constexpr (requires { fn(entry); }) { if constexpr (requires { fn(entry); }) {
@ -176,7 +177,8 @@ static void reclaim_if_empty(table_or_huge_entry_t<NextEntryType>& entry) {
if (entry.subtable.huge_page) { if (entry.subtable.huge_page) {
return; return;
} }
entry_table_t<NextEntryType>* child_table = address_of(entry.subtable).template access<entry_table_t<NextEntryType>>(); entry_table_t<NextEntryType>* child_table =
address_of(entry.subtable).template access<entry_table_t<NextEntryType>>();
if (!is_table_empty(child_table)) { if (!is_table_empty(child_table)) {
return; return;
} }
@ -269,6 +271,18 @@ PageTable PageTable::fromCurrent() {
return table; return table;
} }
optional<PageTable> 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<uint8_t>(), 0, page_size);
PageTable table{};
table.l4 = addr;
return table;
}
bool PageTable::map(void* vaddress, paddr_t paddress, flags_t flags) { bool PageTable::map(void* vaddress, paddr_t paddress, flags_t flags) {
size_t target_level = target_level_of(flags.granularity); size_t target_level = target_level_of(flags.granularity);
bool mapped = false; bool mapped = false;

View file

@ -1,5 +1,90 @@
asm(R"( 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 .align 0x1000
.code16 .code16
)"); __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
)");