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.
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.
*/
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);
}
/* 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,
+1
View File
@@ -72,6 +72,7 @@ struct DesktopState {
SvgIcon icon_settings;
SvgIcon icon_reboot;
SvgIcon icon_shutdown;
SvgIcon icon_weather;
+1
View File
@@ -174,4 +174,5 @@ void open_devexplorer(DesktopState* ds);
void open_settings(DesktopState* ds);
void open_doom(DesktopState* ds);
void open_reboot_dialog(DesktopState* ds);
void open_shutdown_dialog(DesktopState* ds);
void desktop_poll_external_windows(DesktopState* ds);
+123 -5
View File
@@ -60,6 +60,7 @@ void gui::desktop_init(DesktopState* ds) {
ds->icon_settings = svg_load("0:/icons/help-about.svg", 20, 20, defColor);
ds->icon_reboot = svg_load("0:/icons/system-reboot.svg", 20, 20, defColor);
ds->icon_shutdown = svg_load("0:/icons/system-shutdown.svg", 20, 20, defColor);
ds->icon_weather = svg_load("0:/icons/weather-widget.svg", 20, 20, defColor);
@@ -424,7 +425,7 @@ struct MenuRow {
int app_id; // -1 for category headers / dividers
};
static constexpr int MENU_ROW_COUNT = 19;
static constexpr int MENU_ROW_COUNT = 20;
static const MenuRow menu_rows[MENU_ROW_COUNT] = {
{ true, "Applications", -1 },
{ false, "Terminal", 0 },
@@ -445,6 +446,7 @@ static const MenuRow menu_rows[MENU_ROW_COUNT] = {
{ true, "", -1 }, // divider
{ false, "Settings", 11 },
{ false, "Reboot", 12 },
{ false, "Shutdown", 14 },
};
static int menu_row_height(const MenuRow& row) {
@@ -474,7 +476,7 @@ static void desktop_draw_app_menu(DesktopState* ds) {
draw_rect(fb, menu_x, menu_y, MENU_W, menu_h, colors::BORDER);
// Icon lookup by app_id
SvgIcon* icons[14] = {
SvgIcon* icons[15] = {
&ds->icon_terminal, // 0
&ds->icon_filemanager, // 1
&ds->icon_sysinfo, // 2
@@ -489,6 +491,7 @@ static void desktop_draw_app_menu(DesktopState* ds) {
&ds->icon_settings, // 11
&ds->icon_reboot, // 12
&ds->icon_weather, // 13
&ds->icon_shutdown, // 14
};
int mx = ds->mouse.x;
@@ -524,7 +527,7 @@ static void desktop_draw_app_menu(DesktopState* ds) {
// Icon
int icon_x = item_rect.x + 8;
int icon_y = item_rect.y + (row_h - 20) / 2;
if (row.app_id >= 0 && row.app_id < 14) {
if (row.app_id >= 0 && row.app_id < 15) {
SvgIcon* icon = icons[row.app_id];
if (icon && icon->pixels) {
fb.blit_alpha(icon_x, icon_y, icon->width, icon->height, icon->pixels);
@@ -709,6 +712,118 @@ void open_reboot_dialog(DesktopState* ds) {
win->on_close = reboot_dialog_on_close;
}
// ============================================================================
// Shutdown Dialog
// ============================================================================
struct ShutdownDialogState {
DesktopState* ds;
int btn_w, btn_h, btn_y, shutdown_x, cancel_x;
bool hover_shutdown, hover_cancel;
};
static void shutdown_dialog_on_draw(Window* win, Framebuffer& fb) {
ShutdownDialogState* ss = (ShutdownDialogState*)win->app_data;
if (!ss) return;
Canvas c(win);
c.fill(colors::WINDOW_BG);
const char* msg = "Shut down the system?";
int tw = text_width(msg);
c.text((c.w - tw) / 2, 30, msg, colors::TEXT_COLOR);
int btn_w = 100;
int btn_h = 32;
int btn_y = c.h - btn_h - 20;
int gap = 20;
int total_w = btn_w * 2 + gap;
int bx = (c.w - total_w) / 2;
ss->btn_w = btn_w;
ss->btn_h = btn_h;
ss->btn_y = btn_y;
ss->shutdown_x = bx;
ss->cancel_x = bx + btn_w + gap;
Color shutdown_bg = ss->hover_shutdown
? Color::from_rgb(0xDD, 0x44, 0x44)
: Color::from_rgb(0xCC, 0x33, 0x33);
c.button(ss->shutdown_x, btn_y, btn_w, btn_h, "Shut Down", shutdown_bg, colors::WHITE, 4);
Color cancel_bg = ss->hover_cancel
? Color::from_rgb(0x99, 0x99, 0x99)
: Color::from_rgb(0x88, 0x88, 0x88);
c.button(ss->cancel_x, btn_y, btn_w, btn_h, "Cancel", cancel_bg, colors::WHITE, 4);
}
static void shutdown_dialog_on_mouse(Window* win, MouseEvent& ev) {
ShutdownDialogState* ss = (ShutdownDialogState*)win->app_data;
if (!ss) return;
Rect cr = win->content_rect();
int lx = ev.x - cr.x;
int ly = ev.y - cr.y;
Rect sb = {ss->shutdown_x, ss->btn_y, ss->btn_w, ss->btn_h};
Rect cb = {ss->cancel_x, ss->btn_y, ss->btn_w, ss->btn_h};
ss->hover_shutdown = sb.contains(lx, ly);
ss->hover_cancel = cb.contains(lx, ly);
if (ev.left_pressed()) {
if (ss->hover_shutdown) zenith::shutdown();
if (ss->hover_cancel) {
for (int i = 0; i < ss->ds->window_count; i++) {
if (ss->ds->windows[i].app_data == ss) {
desktop_close_window(ss->ds, i);
return;
}
}
}
}
}
static void shutdown_dialog_on_key(Window* win, const Zenith::KeyEvent& key) {
ShutdownDialogState* ss = (ShutdownDialogState*)win->app_data;
if (!ss || !key.pressed) return;
if (key.ascii == '\n' || key.ascii == '\r') {
zenith::shutdown();
}
if (key.scancode == 0x01) { // Escape
for (int i = 0; i < ss->ds->window_count; i++) {
if (ss->ds->windows[i].app_data == ss) {
desktop_close_window(ss->ds, i);
return;
}
}
}
}
static void shutdown_dialog_on_close(Window* win) {
if (win->app_data) {
zenith::mfree(win->app_data);
win->app_data = nullptr;
}
}
void open_shutdown_dialog(DesktopState* ds) {
int wx = (ds->screen_w - 300) / 2;
int wy = (ds->screen_h - 150) / 2;
int idx = desktop_create_window(ds, "Shut Down", wx, wy, 300, 150);
if (idx < 0) return;
Window* win = &ds->windows[idx];
ShutdownDialogState* ss = (ShutdownDialogState*)zenith::malloc(sizeof(ShutdownDialogState));
zenith::memset(ss, 0, sizeof(ShutdownDialogState));
ss->ds = ds;
win->app_data = ss;
win->on_draw = shutdown_dialog_on_draw;
win->on_mouse = shutdown_dialog_on_mouse;
win->on_key = shutdown_dialog_on_key;
win->on_close = shutdown_dialog_on_close;
}
static gui::ResizeEdge hit_test_resize_edge(const gui::Rect& f, int mx, int my) {
using namespace gui;
int G = RESIZE_GRAB;
@@ -808,7 +923,7 @@ void gui::desktop_compose(DesktopState* ds) {
if (ds->ctx_menu_open) {
static constexpr int CTX_MENU_W = 180;
static constexpr int CTX_ITEM_H = 36;
static constexpr int CTX_ITEM_COUNT = 4;
static constexpr int CTX_ITEM_COUNT = 5;
int cmx = ds->ctx_menu_x;
int cmy = ds->ctx_menu_y;
int cmh = CTX_ITEM_H * CTX_ITEM_COUNT + 8;
@@ -827,6 +942,7 @@ void gui::desktop_compose(DesktopState* ds) {
{ "Files", &ds->icon_filemanager },
{ "About", &ds->icon_settings },
{ "Reboot", &ds->icon_reboot },
{ "Shutdown", &ds->icon_shutdown },
};
int mmx = ds->mouse.x;
@@ -913,7 +1029,7 @@ void gui::desktop_handle_mouse(DesktopState* ds) {
if (left_pressed) {
static constexpr int CTX_MENU_W = 180;
static constexpr int CTX_ITEM_H = 36;
static constexpr int CTX_ITEM_COUNT = 4;
static constexpr int CTX_ITEM_COUNT = 5;
int cmx = ds->ctx_menu_x;
int cmy = ds->ctx_menu_y;
int cmh = CTX_ITEM_H * CTX_ITEM_COUNT + 8;
@@ -931,6 +1047,7 @@ void gui::desktop_handle_mouse(DesktopState* ds) {
case 1: open_filemanager(ds); break;
case 2: open_settings(ds); break;
case 3: open_reboot_dialog(ds); break;
case 4: open_shutdown_dialog(ds); break;
}
return;
}
@@ -1105,6 +1222,7 @@ void gui::desktop_handle_mouse(DesktopState* ds) {
case 11: open_settings(ds); break;
case 12: open_reboot_dialog(ds); break;
case 13: open_weather(ds); break;
case 14: open_shutdown_dialog(ds); break;
}
ds->app_menu_open = false;
}
BIN
View File
Binary file not shown.
+1
View File
@@ -52,6 +52,7 @@ ICONS=(
"mimetypes/scalable/application-x-executable.svg"
"categories/scalable/help-about.svg"
"categories/scalable/system-reboot.svg"
"categories/scalable/system-shutdown.svg"
"apps/scalable/doom.svg"
"apps/scalable/system-monitor.svg"
"apps/scalable/applications-science.svg"