feat: add support for shutdown/reboot via UEFI Runtime Services
This commit is contained in:
+20
-10
@@ -6,23 +6,33 @@
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <Efi/UEFI.hpp>
|
||||
#include <Memory/Paging.hpp>
|
||||
|
||||
namespace Zenith {
|
||||
|
||||
static void Sys_Reset() {
|
||||
/*
|
||||
Triple fault for now; TODO: implement UEFI runtime function for clean reboot.
|
||||
if (Efi::g_ResetSystem) {
|
||||
/* Switch to kernel PML4 which has identity-mapped UEFI runtime regions */
|
||||
Memory::VMM::LoadCR3(Memory::VMM::g_paging->PML4);
|
||||
Efi::g_ResetSystem(Efi::EfiResetCold, 0, 0, nullptr);
|
||||
}
|
||||
|
||||
We implement the triple fault by loading a null IDT into the IDT register,
|
||||
and then immediately triggering an interrupt.
|
||||
|
||||
This technique should pretty much work across the board but it's of course
|
||||
better to use the UEFI runtime API as it has a method for this purpose,
|
||||
along with shutdown.
|
||||
*/
|
||||
|
||||
/* Fallback: triple fault via null IDT */
|
||||
struct [[gnu::packed]] { uint16_t limit; uint64_t base; } nullIdt = {0, 0};
|
||||
asm volatile("lidt %0; int $0x03" :: "m"(nullIdt));
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static void Sys_Shutdown() {
|
||||
if (Efi::g_ResetSystem) {
|
||||
/* Switch to kernel PML4 which has identity-mapped UEFI runtime regions */
|
||||
Memory::VMM::LoadCR3(Memory::VMM::g_paging->PML4);
|
||||
Efi::g_ResetSystem(Efi::EfiResetShutdown, 0, 0, nullptr);
|
||||
}
|
||||
|
||||
/* No fallback for shutdown; halt the CPU */
|
||||
asm volatile("cli; hlt");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -109,8 +109,8 @@ namespace Zenith {
|
||||
Sys_Reset();
|
||||
return 0;
|
||||
case SYS_SHUTDOWN:
|
||||
/* Unimplemented */
|
||||
return -1;
|
||||
Sys_Shutdown();
|
||||
return 0;
|
||||
case SYS_GETTIME:
|
||||
Sys_GetTime((DateTime*)frame->arg1);
|
||||
return 0;
|
||||
|
||||
+13
-1
@@ -5,7 +5,9 @@
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <limine.h>
|
||||
#include <Memory/HHDM.hpp>
|
||||
#include <Memory/Paging.hpp>
|
||||
#include <Timekeeping/Time.hpp>
|
||||
|
||||
namespace Efi {
|
||||
@@ -269,7 +271,9 @@ namespace Efi {
|
||||
void *ConfigurationTable;
|
||||
};
|
||||
|
||||
inline void Init(SystemTable* ST) {
|
||||
inline EFI_RESET_SYSTEM g_ResetSystem = nullptr;
|
||||
|
||||
inline void Init(SystemTable* ST, limine_efi_memmap_response* efiMemmap) {
|
||||
Kt::KernelLogStream(Kt::OK, "UEFI") << "ST Minor Revision: " << ST->Header.Revision.MinorRevision;
|
||||
Kt::KernelLogStream(Kt::OK, "UEFI") << "ST Major Revision: " << ST->Header.Revision.MajorRevision;
|
||||
|
||||
@@ -278,6 +282,12 @@ namespace Efi {
|
||||
if (ST->RuntimeServices != nullptr) {
|
||||
Kt::KernelLogStream(Kt::OK, "UEFI") << "EFI Runtime Service API is available.";
|
||||
|
||||
/* Identity-map EFI runtime service regions so firmware code
|
||||
can reference its own data at physical addresses */
|
||||
if (Memory::VMM::g_paging) {
|
||||
Memory::VMM::g_paging->MapEfiRuntime(efiMemmap);
|
||||
}
|
||||
|
||||
EFI_TIME Time;
|
||||
EFI_TIME_CAPABILITIES TimeCapabilities;
|
||||
|
||||
@@ -285,6 +295,8 @@ namespace Efi {
|
||||
_GetTime(&Time, &TimeCapabilities);
|
||||
|
||||
Timekeeping::Init(Time.Year, Time.Month, Time.Day, Time.Hour, Time.Minute, Time.Second);
|
||||
|
||||
g_ResetSystem = (EFI_RESET_SYSTEM)Memory::HHDM((void*)RT->ResetSystem);
|
||||
}
|
||||
}
|
||||
};
|
||||
+2
-2
@@ -166,7 +166,7 @@ extern "C" void kmain() {
|
||||
g_paging.MapWC(phys, Memory::HHDM(phys));
|
||||
}
|
||||
|
||||
asm volatile("mov %%cr3, %%rax; mov %%rax, %%cr3" ::: "rax", "memory");
|
||||
Memory::VMM::FlushTLB();
|
||||
|
||||
Kt::KernelLogStream(OK, "Graphics") << "Framebuffer mapped as Write-Combining ("
|
||||
<< kcp::dec << numPages << " pages)";
|
||||
@@ -211,7 +211,7 @@ extern "C" void kmain() {
|
||||
#endif
|
||||
|
||||
Efi::SystemTable* ST = (Efi::SystemTable*)Memory::HHDM(system_table_request.response->address);
|
||||
Efi::Init(ST);
|
||||
Efi::Init(ST, efi_memmap_request.response);
|
||||
|
||||
// Initialize ramdisk from Limine modules
|
||||
if (module_request.response != nullptr && module_request.response->module_count > 0) {
|
||||
|
||||
@@ -267,4 +267,34 @@ namespace Memory::VMM {
|
||||
std::uint64_t Paging::GetPhysAddr(std::uint64_t virtualAddress) {
|
||||
return GetPhysAddr((std::uint64_t)PML4, virtualAddress, false);
|
||||
}
|
||||
|
||||
void Paging::MapEfiRuntime(limine_efi_memmap_response* efiMemmap) {
|
||||
if (!efiMemmap) return;
|
||||
|
||||
auto* base = (uint8_t*)efiMemmap->memmap;
|
||||
uint64_t descSize = efiMemmap->desc_size;
|
||||
uint64_t count = efiMemmap->memmap_size / descSize;
|
||||
|
||||
struct EfiMemDesc {
|
||||
uint32_t Type;
|
||||
uint64_t PhysicalStart;
|
||||
uint64_t VirtualStart;
|
||||
uint64_t NumberOfPages;
|
||||
uint64_t Attribute;
|
||||
};
|
||||
|
||||
static constexpr uint64_t EFI_MEMORY_RUNTIME = 0x8000000000000000ULL;
|
||||
|
||||
for (uint64_t i = 0; i < count; i++) {
|
||||
auto* desc = (EfiMemDesc*)(base + i * descSize);
|
||||
if (desc->Attribute & EFI_MEMORY_RUNTIME) {
|
||||
for (uint64_t p = 0; p < desc->NumberOfPages; p++) {
|
||||
uint64_t addr = desc->PhysicalStart + p * 0x1000;
|
||||
Map(addr, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlushTLB();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -108,10 +108,18 @@ public:
|
||||
|
||||
// Map a page into an arbitrary PML4 with User + Write-Combining attributes.
|
||||
static void MapUserInWC(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
||||
|
||||
// Identity-map EFI runtime service regions so firmware code can
|
||||
// reference its own data at physical addresses.
|
||||
void MapEfiRuntime(limine_efi_memmap_response* efiMemmap);
|
||||
};
|
||||
|
||||
extern Paging* g_paging;
|
||||
|
||||
extern "C" uint64_t GetCR3();
|
||||
extern "C" void LoadCR3(PageTable* PML4);
|
||||
|
||||
inline void FlushTLB() {
|
||||
asm volatile("mov %%cr3, %%rax; mov %%rax, %%cr3" ::: "rax", "memory");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -52,6 +52,13 @@ namespace {
|
||||
.response = nullptr
|
||||
};
|
||||
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
volatile limine_efi_memmap_request efi_memmap_request = {
|
||||
.id = LIMINE_EFI_MEMMAP_REQUEST,
|
||||
.revision = 0,
|
||||
.response = nullptr
|
||||
};
|
||||
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
volatile limine_rsdp_request rsdp_request = {
|
||||
.id = LIMINE_RSDP_REQUEST,
|
||||
|
||||
Reference in New Issue
Block a user