feat: add support for shutdown/reboot via UEFI Runtime Services

This commit is contained in:
2026-02-26 18:08:05 +01:00
parent f5e2baabf5
commit 30e73fcdcc
12 changed files with 208 additions and 20 deletions
+20 -10
View File
@@ -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();
}
};
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
+30
View File
@@ -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();
}
};
+8
View File
@@ -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");
}
};
+7
View File
@@ -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,