Compare commits
No commits in common. "beb7fb38ae16054bc5b3c245bc8d6fd05bea5ca3" and "61dfda6a70744ca3dc5861e3c0ac144a9dc4a662" have entirely different histories.
beb7fb38ae
...
61dfda6a70
22 changed files with 47 additions and 1175 deletions
|
|
@ -10,6 +10,7 @@ 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -33,26 +33,6 @@ 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};
|
||||||
}
|
}
|
||||||
|
|
@ -82,4 +62,9 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
[[noreturn]] void main();
|
|
||||||
|
|
@ -12,17 +12,10 @@ 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(
|
optional<allocation_t> allocPages(size_t page_count, size_t alignment_pages = 1);
|
||||||
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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
#include "util/number.h"
|
#include "util/number.h"
|
||||||
|
|
||||||
constexpr uint64_t high_base = 0xFFFF800000000000;
|
constexpr uint64_t high_base = 0xFFFF800000000000;
|
||||||
constexpr uint64_t uncached_base = high_base + (uint64_t{1} << 38);
|
|
||||||
|
|
||||||
#define read_symbol(symbol) \
|
#define read_symbol(symbol) \
|
||||||
[]() { \
|
[]() { \
|
||||||
|
|
@ -19,11 +18,6 @@ struct paddr_t {
|
||||||
T* access() {
|
T* access() {
|
||||||
return reinterpret_cast<T*>(address + high_base);
|
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) {
|
inline void* operator new(size_t, void* p) {
|
||||||
|
|
|
||||||
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
@ -131,7 +131,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit operator bool() const {
|
operator bool() const {
|
||||||
return initialized;
|
return initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,13 @@ SECTIONS
|
||||||
} > START_MEMORY_PV
|
} > START_MEMORY_PV
|
||||||
.text : AT(ADDR(.text) - high_base) {
|
.text : AT(ADDR(.text) - high_base) {
|
||||||
__text_start__ = .;
|
__text_start__ = .;
|
||||||
*(.text .text.*)
|
*(EXCLUDE_FILE(*trampoline*) .text .text.*)
|
||||||
*(.iplt)
|
*(EXCLUDE_FILE(*trampoline*) .iplt)
|
||||||
__text_end__ = .;
|
__text_end__ = .;
|
||||||
} > HIGH_KERNEL_V
|
} > HIGH_KERNEL_V
|
||||||
.rodata : AT(ADDR(.rodata) - high_base) {
|
.rodata : AT(ADDR(.rodata) - high_base) {
|
||||||
__rodata_start__ = .;
|
__rodata_start__ = .;
|
||||||
*(.rodata .rodata.*)
|
*(EXCLUDE_FILE(*trampoline*) .rodata .rodata.*)
|
||||||
. = ALIGN(8);
|
. = ALIGN(8);
|
||||||
__reserved_ranges_start__ = .;
|
__reserved_ranges_start__ = .;
|
||||||
KEEP(*(.reserved_ranges))
|
KEEP(*(.reserved_ranges))
|
||||||
|
|
@ -52,37 +52,37 @@ SECTIONS
|
||||||
} > HIGH_KERNEL_V
|
} > HIGH_KERNEL_V
|
||||||
.data : AT(ADDR(.data) - high_base) {
|
.data : AT(ADDR(.data) - high_base) {
|
||||||
__data_start__ = .;
|
__data_start__ = .;
|
||||||
*(.data .data.*)
|
*(EXCLUDE_FILE(*trampoline*) .data .data.*)
|
||||||
*(.got .got.plt .igot.plt .data.rel.ro .data.rel.ro.*)
|
*(EXCLUDE_FILE(*trampoline*) .got .got.plt .igot.plt .data.rel.ro .data.rel.ro.*)
|
||||||
__data_end__ = .;
|
__data_end__ = .;
|
||||||
} > HIGH_KERNEL_V
|
} > HIGH_KERNEL_V
|
||||||
.bss (NOLOAD) : AT(ADDR(.bss) - high_base) {
|
.bss (NOLOAD) : AT(ADDR(.bss) - high_base) {
|
||||||
__bss_start__ = .;
|
__bss_start__ = .;
|
||||||
*(.bss .bss.*)
|
*(EXCLUDE_FILE(*trampoline*) .bss .bss.*)
|
||||||
__bss_end__ = .;
|
__bss_end__ = .;
|
||||||
} > HIGH_KERNEL_V
|
} > HIGH_KERNEL_V
|
||||||
|
|
||||||
.trampoline_text : {
|
.trampoline_text : {
|
||||||
__trampoline_text_start__ = .;
|
__trampoline_text_start__ = .;
|
||||||
*(.trampoline_text)
|
*trampoline*(.text)
|
||||||
__trampoline_text_end__ = .;
|
__trampoline_text_end__ = .;
|
||||||
} > TRAMPOLINE_PV
|
} > TRAMPOLINE_PV
|
||||||
|
|
||||||
.trampoline_data : {
|
.trampoline_data : {
|
||||||
__trampoline_data_start__ = .;
|
__trampoline_data_start__ = .;
|
||||||
*(.trampoline_data)
|
*trampoline*(.data)
|
||||||
__trampoline_data_end__ = .;
|
__trampoline_data_end__ = .;
|
||||||
} > TRAMPOLINE_PV
|
} > TRAMPOLINE_PV
|
||||||
|
|
||||||
.trampoline_rodata : {
|
.trampoline_rodata : {
|
||||||
__trampoline_rodata_start__ = .;
|
__trampoline_rodata_start__ = .;
|
||||||
*(.trampoline_rodata)
|
*trampoline*(.rodata)
|
||||||
__trampoline_rodata_end__ = .;
|
__trampoline_rodata_end__ = .;
|
||||||
} > TRAMPOLINE_PV
|
} > TRAMPOLINE_PV
|
||||||
|
|
||||||
.trampoline_bss (NOLOAD) : {
|
.trampoline_bss (NOLOAD) : {
|
||||||
__trampoline_bss_start__ = .;
|
__trampoline_bss_start__ = .;
|
||||||
*(.trampoline_bss)
|
*trampoline*(.bss)
|
||||||
__trampoline_bss_end__ = .;
|
__trampoline_bss_end__ = .;
|
||||||
} > TRAMPOLINE_PV
|
} > TRAMPOLINE_PV
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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 timer_calibration_t {
|
struct lapic_timer_calibration_t {
|
||||||
uint64_t tsc_frequency_hz;
|
uint64_t tsc_frequency_hz;
|
||||||
};
|
};
|
||||||
|
|
||||||
timer_calibration_t timer_calibration{};
|
lapic_timer_calibration_t timer_calibration{};
|
||||||
|
|
||||||
bool isTscSupported() {
|
bool isTscSupported() {
|
||||||
return cpuid(feature_info_leaf)
|
return cpuid(feature_info_leaf)
|
||||||
|
|
|
||||||
|
|
@ -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"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
#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"
|
||||||
|
|
@ -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() {
|
[[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));
|
||||||
step("cpu startup", partial(apic::initCores, acpi));
|
demonstateTimer();
|
||||||
halt();
|
halt();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
#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, PhysicalAllocator::address_range_t range) {
|
optional<allocation_t> allocPages(size_t page_count, size_t alignment_pages) {
|
||||||
if (page_count == 0) {
|
if (page_count == 0) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -297,11 +297,6 @@ 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;
|
||||||
|
|
@ -311,16 +306,9 @@ 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_end) {
|
if (aligned_start + page_count <= run_start + length) {
|
||||||
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};
|
||||||
|
|
@ -343,9 +331,8 @@ bool PhysicalAllocator::force(allocation_t ptr, bool free) {
|
||||||
return PhysicalAllocatorImpl{*this}.force(ptr, free);
|
return PhysicalAllocatorImpl{*this}.force(ptr, free);
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<PhysicalAllocator::allocation_t>
|
optional<PhysicalAllocator::allocation_t> PhysicalAllocator::allocPages(size_t page_count, size_t alignment_pages) {
|
||||||
PhysicalAllocator::allocPages(size_t page_count, size_t alignment_pages, address_range_t range) {
|
return PhysicalAllocatorImpl{*this}.allocPages(page_count, alignment_pages);
|
||||||
return PhysicalAllocatorImpl{*this}.allocPages(page_count, alignment_pages, range);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalAllocator::free(allocation_t ptr) {
|
void PhysicalAllocator::free(allocation_t ptr) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -48,11 +48,6 @@ __x64_not_supported:
|
||||||
call __print
|
call __print
|
||||||
jmp __stop32
|
jmp __stop32
|
||||||
|
|
||||||
__nx_not_supported:
|
|
||||||
mov $__nx_not_supported_string, %esi
|
|
||||||
call __print
|
|
||||||
jmp __stop32
|
|
||||||
|
|
||||||
__check_if_x64:
|
__check_if_x64:
|
||||||
pushfl
|
pushfl
|
||||||
pop %eax
|
pop %eax
|
||||||
|
|
@ -75,8 +70,6 @@ __check_if_x64:
|
||||||
cpuid
|
cpuid
|
||||||
test $(1 << 29), %edx
|
test $(1 << 29), %edx
|
||||||
jz __x64_not_supported
|
jz __x64_not_supported
|
||||||
test $(1 << 20), %edx
|
|
||||||
jz __nx_not_supported
|
|
||||||
mov $__x64_supported_string, %esi
|
mov $__x64_supported_string, %esi
|
||||||
call __print
|
call __print
|
||||||
ret
|
ret
|
||||||
|
|
@ -92,7 +85,7 @@ __go_to_x64:
|
||||||
|
|
||||||
mov $0xC0000080, %ecx
|
mov $0xC0000080, %ecx
|
||||||
rdmsr
|
rdmsr
|
||||||
or $((0x1 << 8) | (0x1 << 11)), %eax
|
or $(0x1 << 8), %eax
|
||||||
wrmsr
|
wrmsr
|
||||||
|
|
||||||
mov %cr0, %eax
|
mov %cr0, %eax
|
||||||
|
|
@ -142,8 +135,6 @@ __hello_string:
|
||||||
.string "Starting PrOSess! 32bit start!\n"
|
.string "Starting PrOSess! 32bit start!\n"
|
||||||
__x64_not_supported_string:
|
__x64_not_supported_string:
|
||||||
.string "x64 is not supported!\n"
|
.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:
|
__x64_supported_string:
|
||||||
.string "x64 is supported!\n"
|
.string "x64 is supported!\n"
|
||||||
__multiboot_info:
|
__multiboot_info:
|
||||||
|
|
@ -236,28 +227,18 @@ __page_level_4:
|
||||||
.align 0x1000
|
.align 0x1000
|
||||||
__page_level_3_low_1gb_identity_map:
|
__page_level_3_low_1gb_identity_map:
|
||||||
.set gib_index, 0
|
.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)
|
.quad (1 << 0) | (1 << 1) | (1 << 7) | (gib_index << 30)
|
||||||
.set gib_index, gib_index + 1
|
.set gib_index, gib_index + 1
|
||||||
.endr
|
.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
|
.align 0x1000
|
||||||
__page_level_3_kernel_1gb_identity_map:
|
__page_level_3_kernel_1gb_identity_map:
|
||||||
.set gib_index, 0
|
.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)
|
.quad (1 << 0) | (1 << 1) | (1 << 7) | (gib_index << 30)
|
||||||
.set gib_index, gib_index + 1
|
.set gib_index, gib_index + 1
|
||||||
.endr
|
.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
|
// startup gdt
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
)");
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue