feat: implement core initialization and local APIC setup
This commit is contained in:
parent
ada1859910
commit
beb7fb38ae
19 changed files with 747 additions and 41 deletions
|
|
@ -10,7 +10,6 @@ namespace acpi {
|
|||
struct discovery_result_t {
|
||||
optional<Rsdp> rsdp;
|
||||
optional<Madt> madt;
|
||||
// TODO(wiring): capture the FADT in setupAcpi and store it here
|
||||
optional<Fadt> fadt;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
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
|
||||
24
kernel/include/apic/lapic.h
Normal file
24
kernel/include/apic/lapic.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
58
kernel/include/cpu/gdt.h
Normal file
58
kernel/include/cpu/gdt.h
Normal 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
3
kernel/include/main.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
[[noreturn]] void main();
|
||||
|
|
@ -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<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);
|
||||
bool force(allocation_t ptr, bool free);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,12 +25,12 @@ struct PageTable {
|
|||
};
|
||||
|
||||
static PageTable fromCurrent();
|
||||
static optional<PageTable> allocNew();
|
||||
|
||||
void activate();
|
||||
bool map(void* vaddress, paddr_t paddress, flags_t flags);
|
||||
void unmap(void* vaddress);
|
||||
optional<info_t> get(void* vaddress);
|
||||
|
||||
private:
|
||||
paddr_t l4;
|
||||
};
|
||||
16
kernel/include/util/msr.h
Normal file
16
kernel/include/util/msr.h
Normal 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));
|
||||
}
|
||||
|
|
@ -131,7 +131,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
explicit operator bool() const {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
81
kernel/src/apic/lapic.cpp
Normal 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;
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
218
kernel/src/cpu/gdt.cpp
Normal file
218
kernel/src/cpu/gdt.cpp
Normal 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"
|
||||
);
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
7
kernel/src/main.cpp
Normal file
7
kernel/src/main.cpp
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#include "main.h"
|
||||
|
||||
[[noreturn]] void main() {
|
||||
while (true) {
|
||||
__asm__ volatile("hlt");
|
||||
}
|
||||
}
|
||||
|
|
@ -286,7 +286,7 @@ struct PhysicalAllocatorImpl {
|
|||
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) {
|
||||
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::allocation_t> PhysicalAllocator::allocPages(size_t page_count, size_t alignment_pages) {
|
||||
return PhysicalAllocatorImpl{*this}.allocPages(page_count, alignment_pages);
|
||||
optional<PhysicalAllocator::allocation_t>
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -129,7 +129,8 @@ static optional<frame_entry_t*> walk(level1_table_t* table, void* vaddress, Fn&&
|
|||
struct ascend_t {};
|
||||
|
||||
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>;
|
||||
entry_t& entry = table->entries[table_index<entry_t::level>(vaddress)];
|
||||
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) {
|
||||
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)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -269,6 +271,18 @@ PageTable PageTable::fromCurrent() {
|
|||
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) {
|
||||
size_t target_level = target_level_of(flags.granularity);
|
||||
bool mapped = false;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)");
|
||||
__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
|
||||
)");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue