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 {
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
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};
|
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
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;
|
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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
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;
|
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;
|
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
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/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
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;
|
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) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
)");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue