118 lines
4.3 KiB
C++
118 lines
4.3 KiB
C++
/*
|
|
* Power.hpp
|
|
* SYS_RESET, SYS_SHUTDOWN, SYS_SUSPEND syscalls
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#pragma once
|
|
#include <cstdint>
|
|
#include <Efi/UEFI.hpp>
|
|
#include <Memory/Paging.hpp>
|
|
#include <Io/IoPort.hpp>
|
|
#include <Graphics/Framebuffer.hpp>
|
|
#include <ACPI/AcpiShutdown.hpp>
|
|
#include <ACPI/AcpiSleep.hpp>
|
|
#include <ACPI/CpuIdle.hpp>
|
|
#include <Hal/CpuPower.hpp>
|
|
#include <Sched/Scheduler.hpp>
|
|
#include <Terminal/Terminal.hpp>
|
|
#include <CppLib/Stream.hpp>
|
|
|
|
#include "Syscall.hpp"
|
|
|
|
namespace montauk::abi {
|
|
|
|
// Pending graceful power-off request, set by the desktop and consumed by
|
|
// login.elf. Kernel-global IPC channel (one outstanding request system-wide).
|
|
// `inline` so the single definition is shared across translation units.
|
|
inline int g_pendingPowerAction = POWER_REQ_QUERY;
|
|
|
|
// SYS_POWER_REQUEST. action == POWER_REQ_QUERY reads and clears the pending
|
|
// action; any other value records it as the pending action. Returns the
|
|
// pending action for queries, or 0 when recording one.
|
|
static int64_t Sys_PowerRequest(int action) {
|
|
// Polled by the session leader every second; deliberately silent and
|
|
// non-destructive, so it neither floods the log nor races login for
|
|
// the request it is about to hand over by exiting.
|
|
if (action == POWER_REQ_PEEK) return (int64_t)g_pendingPowerAction;
|
|
|
|
if (action == POWER_REQ_QUERY) {
|
|
int pending = g_pendingPowerAction;
|
|
g_pendingPowerAction = POWER_REQ_QUERY;
|
|
Kt::KernelLogStream(Kt::INFO, "Power") << "pid " << kcp::dec
|
|
<< (uint64_t)Sched::GetCurrentPid()
|
|
<< " consumed graceful power request " << (uint64_t)pending;
|
|
return (int64_t)pending;
|
|
}
|
|
g_pendingPowerAction = action;
|
|
Kt::KernelLogStream(Kt::INFO, "Power") << "pid " << kcp::dec
|
|
<< (uint64_t)Sched::GetCurrentPid()
|
|
<< " posted graceful power request " << (uint64_t)action;
|
|
return 0;
|
|
}
|
|
|
|
static void Sys_Reset() {
|
|
Kt::KernelLogStream(Kt::INFO, "Power") << "pid " << kcp::dec
|
|
<< (uint64_t)Sched::GetCurrentPid() << " entered final reboot stage";
|
|
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() {
|
|
Kt::KernelLogStream(Kt::INFO, "Power") << "pid " << kcp::dec
|
|
<< (uint64_t)Sched::GetCurrentPid() << " entered final shutdown stage";
|
|
/* Primary: ACPI S5 shutdown via PM1 control registers */
|
|
if (Hal::AcpiShutdown::IsAvailable()) {
|
|
Hal::AcpiShutdown::Shutdown();
|
|
}
|
|
|
|
/* Fallback: EFI ResetSystem */
|
|
if (Efi::g_ResetSystem) {
|
|
Memory::VMM::LoadCR3(Memory::VMM::g_paging->PML4);
|
|
Efi::g_ResetSystem(Efi::EfiResetShutdown, 0, 0, nullptr);
|
|
}
|
|
|
|
/* Last resort: halt the CPU */
|
|
asm volatile("cli; hlt");
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
static int64_t Sys_Suspend() {
|
|
if (!Hal::AcpiSleep::IsS3Available()) {
|
|
return -1; // S3 not supported
|
|
}
|
|
return (int64_t)Hal::AcpiSleep::Suspend();
|
|
}
|
|
|
|
// SYS_POWERINFO. Fills a CPU power/thermal snapshot. Returns -1 when the
|
|
// CPU exposes no power management features (e.g. under QEMU).
|
|
static int64_t Sys_PowerInfo(PowerInfo* out) {
|
|
Hal::CpuPower::Snapshot s{};
|
|
if (!Hal::CpuPower::GetSnapshot(s)) {
|
|
return -1;
|
|
}
|
|
|
|
out->hwpActive = s.HwpActive ? 1 : 0;
|
|
out->throttling = s.Throttling ? 1 : 0;
|
|
out->tempC = s.TempC;
|
|
out->tjMaxC = s.TjMaxC;
|
|
out->highestPerf = s.HighestPerf;
|
|
out->lowestPerf = s.LowestPerf;
|
|
out->curMaxPerf = s.CurMaxPerf;
|
|
out->epp = s.Epp;
|
|
out->baseMHz = s.BaseMHz;
|
|
out->maxMHz = s.MaxMHz;
|
|
out->effMHz = s.EffMHz;
|
|
out->apIdleHint = Hal::CpuIdle::GetApIdleHint();
|
|
return 0;
|
|
}
|
|
};
|