diff --git a/kernel/src/Api/Power.hpp b/kernel/src/Api/Power.hpp index 2f4d104..1a1d01e 100644 --- a/kernel/src/Api/Power.hpp +++ b/kernel/src/Api/Power.hpp @@ -6,23 +6,33 @@ #pragma once #include +#include +#include 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(); + } }; diff --git a/kernel/src/Api/Syscall.cpp b/kernel/src/Api/Syscall.cpp index 29e9b6f..ec32daa 100644 --- a/kernel/src/Api/Syscall.cpp +++ b/kernel/src/Api/Syscall.cpp @@ -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; diff --git a/kernel/src/Efi/UEFI.hpp b/kernel/src/Efi/UEFI.hpp index 30cf7f2..c1b4a0d 100644 --- a/kernel/src/Efi/UEFI.hpp +++ b/kernel/src/Efi/UEFI.hpp @@ -5,7 +5,9 @@ #pragma once #include +#include #include +#include #include 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); } } }; \ No newline at end of file diff --git a/kernel/src/Main.cpp b/kernel/src/Main.cpp index 4d157fd..8f8f667 100644 --- a/kernel/src/Main.cpp +++ b/kernel/src/Main.cpp @@ -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) { diff --git a/kernel/src/Memory/Paging.cpp b/kernel/src/Memory/Paging.cpp index 9016586..b0177b5 100644 --- a/kernel/src/Memory/Paging.cpp +++ b/kernel/src/Memory/Paging.cpp @@ -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(); + } }; diff --git a/kernel/src/Memory/Paging.hpp b/kernel/src/Memory/Paging.hpp index e96bfac..cb7d0c6 100644 --- a/kernel/src/Memory/Paging.hpp +++ b/kernel/src/Memory/Paging.hpp @@ -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"); + } }; diff --git a/kernel/src/Platform/Limine.hpp b/kernel/src/Platform/Limine.hpp index 1497c38..ea2e7de 100644 --- a/kernel/src/Platform/Limine.hpp +++ b/kernel/src/Platform/Limine.hpp @@ -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, diff --git a/programs/include/gui/desktop.hpp b/programs/include/gui/desktop.hpp index 25b439b..adc39c4 100644 --- a/programs/include/gui/desktop.hpp +++ b/programs/include/gui/desktop.hpp @@ -72,6 +72,7 @@ struct DesktopState { SvgIcon icon_settings; SvgIcon icon_reboot; + SvgIcon icon_shutdown; SvgIcon icon_weather; diff --git a/programs/src/desktop/apps_common.hpp b/programs/src/desktop/apps_common.hpp index ef493c9..672b53a 100644 --- a/programs/src/desktop/apps_common.hpp +++ b/programs/src/desktop/apps_common.hpp @@ -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); diff --git a/programs/src/desktop/main.cpp b/programs/src/desktop/main.cpp index 6e8b78a..8075563 100644 --- a/programs/src/desktop/main.cpp +++ b/programs/src/desktop/main.cpp @@ -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; } diff --git a/ramdisk.tar b/ramdisk.tar index cca5647..9c3fc07 100644 Binary files a/ramdisk.tar and b/ramdisk.tar differ diff --git a/scripts/copy_icons.sh b/scripts/copy_icons.sh index 9f71328..03c8357 100755 --- a/scripts/copy_icons.sh +++ b/scripts/copy_icons.sh @@ -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"