Files
MontaukOS/kernel/src/Api/Power.hpp
T

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;
}
};