Compare commits

..

No commits in common. "beb7fb38ae16054bc5b3c245bc8d6fd05bea5ca3" and "61dfda6a70744ca3dc5861e3c0ac144a9dc4a662" have entirely different histories.

22 changed files with 47 additions and 1175 deletions

View file

@ -10,6 +10,7 @@ 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;
};

View file

@ -1,29 +0,0 @@
#pragma once
#include "memory/pointer.h"
#include "acpi/discovery.h"
#include "memory/pagetable.h"
namespace apic {
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

@ -1,24 +0,0 @@
#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,26 +33,6 @@ 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};
}
@ -82,4 +62,9 @@ void busyWait(duration_t duration);
timestamp_t now();
// TODO: when dynamic allocation and lists are available
// struct Timer {};
//
// Timer getCoreTimerInstance();
} // namespace apic

View file

@ -1,58 +0,0 @@
#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);

View file

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

View file

@ -12,17 +12,10 @@ 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, address_range_t range = address_range_t::any
);
optional<allocation_t> allocPages(size_t page_count, size_t alignment_pages = 1);
void free(allocation_t ptr);
bool force(allocation_t ptr, bool free);

View file

@ -1,36 +0,0 @@
#pragma once
#include "memory/pointer.h"
#include "util/optional.h"
struct PageTable {
struct flags_t {
enum class granularity_t : uint8_t {
page_4KiB,
page_2MiB,
page_1GiB,
};
bool writeable : 1; // allow writes
bool user_accessible : 1; // allow ring-3 access
bool write_through : 1; // write-through instead of write-back
bool cache_disabled : 1; // uncached (MMIO)
bool execute_disabled : 1; // forbid instruction fetch
bool global : 1; // keep in TLB across CR3 switches
granularity_t granularity : 2; // mapping size
};
struct info_t {
flags_t flags;
paddr_t address;
};
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);
paddr_t l4;
};

View file

@ -3,7 +3,6 @@
#include "util/number.h"
constexpr uint64_t high_base = 0xFFFF800000000000;
constexpr uint64_t uncached_base = high_base + (uint64_t{1} << 38);
#define read_symbol(symbol) \
[]() { \
@ -19,11 +18,6 @@ struct paddr_t {
T* access() {
return reinterpret_cast<T*>(address + high_base);
}
template<typename T>
T* access_uc() {
return reinterpret_cast<T*>(address + uncached_base);
}
};
inline void* operator new(size_t, void* p) {

View file

@ -1,16 +0,0 @@
#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:
}
}
explicit operator bool() const {
operator bool() const {
return initialized;
}

View file

@ -33,13 +33,13 @@ SECTIONS
} > START_MEMORY_PV
.text : AT(ADDR(.text) - high_base) {
__text_start__ = .;
*(.text .text.*)
*(.iplt)
*(EXCLUDE_FILE(*trampoline*) .text .text.*)
*(EXCLUDE_FILE(*trampoline*) .iplt)
__text_end__ = .;
} > HIGH_KERNEL_V
.rodata : AT(ADDR(.rodata) - high_base) {
__rodata_start__ = .;
*(.rodata .rodata.*)
*(EXCLUDE_FILE(*trampoline*) .rodata .rodata.*)
. = ALIGN(8);
__reserved_ranges_start__ = .;
KEEP(*(.reserved_ranges))
@ -52,37 +52,37 @@ SECTIONS
} > HIGH_KERNEL_V
.data : AT(ADDR(.data) - high_base) {
__data_start__ = .;
*(.data .data.*)
*(.got .got.plt .igot.plt .data.rel.ro .data.rel.ro.*)
*(EXCLUDE_FILE(*trampoline*) .data .data.*)
*(EXCLUDE_FILE(*trampoline*) .got .got.plt .igot.plt .data.rel.ro .data.rel.ro.*)
__data_end__ = .;
} > HIGH_KERNEL_V
.bss (NOLOAD) : AT(ADDR(.bss) - high_base) {
__bss_start__ = .;
*(.bss .bss.*)
*(EXCLUDE_FILE(*trampoline*) .bss .bss.*)
__bss_end__ = .;
} > HIGH_KERNEL_V
.trampoline_text : {
__trampoline_text_start__ = .;
*(.trampoline_text)
*trampoline*(.text)
__trampoline_text_end__ = .;
} > TRAMPOLINE_PV
.trampoline_data : {
__trampoline_data_start__ = .;
*(.trampoline_data)
*trampoline*(.data)
__trampoline_data_end__ = .;
} > TRAMPOLINE_PV
.trampoline_rodata : {
__trampoline_rodata_start__ = .;
*(.trampoline_rodata)
*trampoline*(.rodata)
__trampoline_rodata_end__ = .;
} > TRAMPOLINE_PV
.trampoline_bss (NOLOAD) : {
__trampoline_bss_start__ = .;
*(.trampoline_bss)
*trampoline*(.bss)
__trampoline_bss_end__ = .;
} > TRAMPOLINE_PV

View file

@ -1,155 +0,0 @@
#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");
}
}

View file

@ -1,81 +0,0 @@
#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;
struct timer_calibration_t {
struct lapic_timer_calibration_t {
uint64_t tsc_frequency_hz;
};
timer_calibration_t timer_calibration{};
lapic_timer_calibration_t timer_calibration{};
bool isTscSupported() {
return cpuid(feature_info_leaf)

View file

@ -1,218 +0,0 @@
#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,8 +3,6 @@
#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"
@ -213,18 +211,31 @@ 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));
step("cpu startup", partial(apic::initCores, acpi));
demonstateTimer();
halt();
}

View file

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

View file

@ -286,7 +286,7 @@ struct PhysicalAllocatorImpl {
return true;
}
optional<allocation_t> allocPages(size_t page_count, size_t alignment_pages, PhysicalAllocator::address_range_t range) {
optional<allocation_t> allocPages(size_t page_count, size_t alignment_pages) {
if (page_count == 0) {
return {};
}
@ -297,11 +297,6 @@ 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;
@ -311,16 +306,9 @@ 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_end) {
if (aligned_start + page_count <= run_start + length) {
set_range(aligned_start, page_count, false);
reclaim_nodes();
return allocation_t{paddr_t{aligned_start * page_size}, page_count};
@ -343,9 +331,8 @@ 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, address_range_t range) {
return PhysicalAllocatorImpl{*this}.allocPages(page_count, alignment_pages, range);
optional<PhysicalAllocator::allocation_t> PhysicalAllocator::allocPages(size_t page_count, size_t alignment_pages) {
return PhysicalAllocatorImpl{*this}.allocPages(page_count, alignment_pages);
}
void PhysicalAllocator::free(allocation_t ptr) {

View file

@ -1,363 +0,0 @@
#include "memory/pagetable.h"
#include "memory/allocator.h"
#include "util/function.h"
template<typename EntryType>
struct entry_table_t {
EntryType entries[512];
};
struct frame_entry_t {
static constexpr size_t level = 1;
uint64_t present : 1;
uint64_t writeable : 1;
uint64_t user_accessible : 1;
uint64_t write_through : 1;
uint64_t cache_disabled : 1;
uint64_t accessed : 1;
uint64_t dirty : 1;
uint64_t page_attribute_table : 1;
uint64_t global : 1;
uint64_t available_low : 3;
uint64_t frame_address : 40;
uint64_t available_high : 11;
uint64_t execute_disabled : 1;
};
template<typename NextEntryType>
struct subtable_entry_t {
using next_table_t = entry_table_t<NextEntryType>;
static constexpr size_t level = NextEntryType::level + 1;
uint64_t present : 1;
uint64_t writeable : 1;
uint64_t user_accessible : 1;
uint64_t write_through : 1;
uint64_t cache_disabled : 1;
uint64_t accessed : 1;
uint64_t ignored_dirty : 1;
uint64_t huge_page : 1;
uint64_t ignored_global : 1;
uint64_t available_low : 3;
uint64_t table_address : 40;
uint64_t available_high : 11;
uint64_t execute_disabled : 1;
};
template<size_t level_>
struct huge_frame_entry_t {
static constexpr size_t level = level_;
uint64_t present : 1;
uint64_t writeable : 1;
uint64_t user_accessible : 1;
uint64_t write_through : 1;
uint64_t cache_disabled : 1;
uint64_t accessed : 1;
uint64_t dirty : 1;
uint64_t huge_page : 1;
uint64_t global : 1;
uint64_t available_low : 3;
uint64_t page_attribute_table : 1;
uint64_t frame_address : 39;
uint64_t available_high : 11;
uint64_t execute_disabled : 1;
};
using frame_2MiB_entry_t = huge_frame_entry_t<2>;
using frame_1GiB_entry_t = huge_frame_entry_t<3>;
template<typename NextEntryType>
union table_or_huge_entry_t {
static constexpr size_t level = NextEntryType::level + 1;
subtable_entry_t<NextEntryType> subtable;
huge_frame_entry_t<NextEntryType::level + 1> huge;
};
using level1_entry_t = frame_entry_t;
using level2_entry_t = table_or_huge_entry_t<level1_entry_t>;
using level3_entry_t = table_or_huge_entry_t<level2_entry_t>;
using level4_entry_t = table_or_huge_entry_t<level3_entry_t>;
using level1_table_t = entry_table_t<level1_entry_t>;
using level2_table_t = entry_table_t<level2_entry_t>;
using level3_table_t = entry_table_t<level3_entry_t>;
using level4_table_t = entry_table_t<level4_entry_t>;
static_assert(sizeof(frame_2MiB_entry_t) == sizeof(uint64_t));
static_assert(sizeof(frame_1GiB_entry_t) == sizeof(uint64_t));
static_assert(sizeof(level1_entry_t) == sizeof(uint64_t));
static_assert(sizeof(level2_entry_t) == sizeof(uint64_t));
static_assert(sizeof(level3_entry_t) == sizeof(uint64_t));
static_assert(sizeof(level4_entry_t) == sizeof(uint64_t));
static_assert(sizeof(level1_table_t) == page_size);
static_assert(sizeof(level2_table_t) == page_size);
static_assert(sizeof(level3_table_t) == page_size);
static_assert(sizeof(level4_table_t) == page_size);
template<typename NextEntryType>
static paddr_t address_of(subtable_entry_t<NextEntryType> entry) {
return paddr_t{entry.table_address << 12};
}
static paddr_t address_of(frame_entry_t entry) {
return paddr_t{entry.frame_address << 12};
}
template<size_t level_>
static paddr_t address_of(huge_frame_entry_t<level_> entry) {
return paddr_t{entry.frame_address << 13};
}
template<size_t level>
static size_t table_index(void* vaddress) {
uint64_t address = reinterpret_cast<uint64_t>(vaddress);
return (address >> (12 + 9 * (level - 1))) & 0x1FF;
}
template<typename Fn>
static optional<frame_entry_t*> walk(level1_table_t* table, void* vaddress, Fn&& fn) {
frame_entry_t& entry = table->entries[table_index<frame_entry_t::level>(vaddress)];
if constexpr (requires { fn(entry); }) {
fn(entry);
}
return &entry;
}
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) {
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); }) {
fn(entry);
}
optional<frame_entry_t*> leaf{};
if (entry.subtable.present && entry.subtable.huge_page) {
if constexpr (requires { fn(entry.huge); }) {
fn(entry.huge);
}
} else if (entry.subtable.present) {
leaf = walk(address_of(entry.subtable).template access<entry_table_t<NextEntryType>>(), vaddress, fn);
}
if constexpr (requires { fn(entry, ascend_t{}); }) {
fn(entry, ascend_t{});
}
return leaf;
}
static bool is_present(frame_entry_t entry) {
return entry.present;
}
template<typename NextEntryType>
static bool is_present(table_or_huge_entry_t<NextEntryType> entry) {
return entry.subtable.present;
}
template<typename EntryType>
static bool is_table_empty(entry_table_t<EntryType>* table) {
for (EntryType& entry : table->entries) {
if (is_present(entry)) {
return false;
}
}
return true;
}
template<typename NextEntryType>
static void reclaim_if_empty(table_or_huge_entry_t<NextEntryType>& entry) {
if (!entry.subtable.present) {
return;
}
if (entry.subtable.huge_page) {
return;
}
entry_table_t<NextEntryType>* child_table =
address_of(entry.subtable).template access<entry_table_t<NextEntryType>>();
if (!is_table_empty(child_table)) {
return;
}
PhysicalAllocator::getInstance().free({address_of(entry.subtable), 1});
entry = table_or_huge_entry_t<NextEntryType>{};
}
template<typename LeafEntryType>
static PageTable::flags_t::granularity_t granularity_of() {
using granularity_t = PageTable::flags_t::granularity_t;
if constexpr (LeafEntryType::level == 1) {
return granularity_t::page_4KiB;
} else if constexpr (LeafEntryType::level == 2) {
return granularity_t::page_2MiB;
} else {
return granularity_t::page_1GiB;
}
}
template<typename LeafEntryType>
static PageTable::info_t info_of(LeafEntryType entry, void* vaddress) {
PageTable::info_t info{};
info.flags.writeable = entry.writeable;
info.flags.user_accessible = entry.user_accessible;
info.flags.write_through = entry.write_through;
info.flags.cache_disabled = entry.cache_disabled;
info.flags.execute_disabled = entry.execute_disabled;
info.flags.global = entry.global;
info.flags.granularity = granularity_of<LeafEntryType>();
uint64_t offset_mask = (uint64_t{1} << (12 + 9 * (LeafEntryType::level - 1))) - 1;
info.address = paddr_t{address_of(entry).address + (reinterpret_cast<uint64_t>(vaddress) & offset_mask)};
return info;
}
static paddr_t current_l4() {
uint64_t cr3;
asm volatile("mov %%cr3, %0" : "=r"(cr3));
return paddr_t{cr3 & ~uint64_t{0xFFF}};
}
static bool is_active(paddr_t l4) {
return current_l4().address == l4.address;
}
template<typename LeafEntryType>
static void apply_flags(LeafEntryType& entry, PageTable::flags_t flags) {
entry.present = 1;
entry.writeable = flags.writeable;
entry.user_accessible = flags.user_accessible;
entry.write_through = flags.write_through;
entry.cache_disabled = flags.cache_disabled;
entry.execute_disabled = flags.execute_disabled;
entry.global = flags.global;
}
static size_t target_level_of(PageTable::flags_t::granularity_t granularity) {
if (granularity == PageTable::flags_t::granularity_t::page_1GiB) {
return 3;
}
if (granularity == PageTable::flags_t::granularity_t::page_2MiB) {
return 2;
}
return 1;
}
template<typename NextEntryType>
static void ensure_subtable(table_or_huge_entry_t<NextEntryType>& entry) {
if (entry.subtable.present) {
return;
}
optional<PhysicalAllocator::allocation_t> allocation = PhysicalAllocator::getInstance().allocPages(1);
if (!allocation.has_value()) {
return;
}
memset(allocation->ptr.access<uint8_t>(), 0, page_size);
subtable_entry_t<NextEntryType> subtable{};
subtable.present = 1;
subtable.writeable = 1;
subtable.user_accessible = 1;
subtable.table_address = allocation->ptr.address >> 12;
entry.subtable = subtable;
}
void PageTable::activate() {
asm volatile("mov %0, %%cr3" : : "r"(l4.address) : "memory");
}
PageTable PageTable::fromCurrent() {
PageTable table;
table.l4 = current_l4();
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;
walk(
l4.access<level4_table_t>(), vaddress,
overloaded{
[&](frame_entry_t& entry) {
frame_entry_t leaf{};
apply_flags(leaf, flags);
leaf.frame_address = paddress.address >> 12;
entry = leaf;
mapped = true;
},
[&]<typename NextEntryType>(table_or_huge_entry_t<NextEntryType>& entry) {
constexpr size_t level = table_or_huge_entry_t<NextEntryType>::level;
if (level == target_level) {
if (entry.subtable.present && !entry.subtable.huge_page) {
return;
}
huge_frame_entry_t<level> leaf{};
apply_flags(leaf, flags);
leaf.huge_page = 1;
leaf.frame_address = paddress.address >> 13;
entry.huge = leaf;
mapped = true;
} else {
ensure_subtable(entry);
}
}
}
);
if (mapped && is_active(l4)) {
asm volatile("invlpg (%0)" : : "r"(vaddress) : "memory");
}
return mapped;
}
void PageTable::unmap(void* vaddress) {
walk(
l4.access<level4_table_t>(), vaddress,
overloaded{
[](frame_entry_t& entry) {
entry = frame_entry_t{};
},
[](frame_2MiB_entry_t& entry) {
entry = frame_2MiB_entry_t{};
},
[](frame_1GiB_entry_t& entry) {
entry = frame_1GiB_entry_t{};
},
[](auto& entry, ascend_t) {
reclaim_if_empty(entry);
}
}
);
if (is_active(l4)) {
asm volatile("invlpg (%0)" : : "r"(vaddress) : "memory");
}
}
optional<PageTable::info_t> PageTable::get(void* vaddress) {
optional<info_t> result{};
walk(
l4.access<level4_table_t>(), vaddress,
overloaded{
[&](frame_entry_t& entry) {
if (entry.present) {
result = info_of(entry, vaddress);
}
},
[&](frame_2MiB_entry_t& entry) {
result = info_of(entry, vaddress);
},
[&](frame_1GiB_entry_t& entry) {
result = info_of(entry, vaddress);
}
}
);
return result;
}

View file

@ -48,11 +48,6 @@ __x64_not_supported:
call __print
jmp __stop32
__nx_not_supported:
mov $__nx_not_supported_string, %esi
call __print
jmp __stop32
__check_if_x64:
pushfl
pop %eax
@ -75,8 +70,6 @@ __check_if_x64:
cpuid
test $(1 << 29), %edx
jz __x64_not_supported
test $(1 << 20), %edx
jz __nx_not_supported
mov $__x64_supported_string, %esi
call __print
ret
@ -92,7 +85,7 @@ __go_to_x64:
mov $0xC0000080, %ecx
rdmsr
or $((0x1 << 8) | (0x1 << 11)), %eax
or $(0x1 << 8), %eax
wrmsr
mov %cr0, %eax
@ -142,8 +135,6 @@ __hello_string:
.string "Starting PrOSess! 32bit start!\n"
__x64_not_supported_string:
.string "x64 is not supported!\n"
__nx_not_supported_string:
.string "CPU lacks the NX (no-execute) feature, refusing to boot!\n"
__x64_supported_string:
.string "x64 is supported!\n"
__multiboot_info:
@ -236,28 +227,18 @@ __page_level_4:
.align 0x1000
__page_level_3_low_1gb_identity_map:
.set gib_index, 0
.rept 256 # identity-map the first 256 GiB, cached, one 1 GiB huge page each
.rept 512 # identity-map all 512 GiB the PDPT spans, one 1 GiB huge page each
.quad (1 << 0) | (1 << 1) | (1 << 7) | (gib_index << 30)
.set gib_index, gib_index + 1
.endr
.set gib_index, 0
.rept 256 # alias the same 256 GiB uncached (PWT | PCD) for MMIO
.quad (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 7) | (gib_index << 30)
.set gib_index, gib_index + 1
.endr
.align 0x1000
__page_level_3_kernel_1gb_identity_map:
.set gib_index, 0
.rept 256 # identity-map the first 256 GiB, cached, one 1 GiB huge page each
.rept 512 # identity-map all 512 GiB the PDPT spans, one 1 GiB huge page each
.quad (1 << 0) | (1 << 1) | (1 << 7) | (gib_index << 30)
.set gib_index, gib_index + 1
.endr
.set gib_index, 0
.rept 256 # alias the same 256 GiB uncached (PWT | PCD) for MMIO
.quad (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 7) | (gib_index << 30)
.set gib_index, gib_index + 1
.endr
)");
// startup gdt

View file

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