feat: add kernel ACPI support for querying battery percentage, add battery indicator to desktop, move log state to 0:/state directory

This commit is contained in:
daniel committed 2026-09-11 07:11:48 +02:00
1 parent 0a18b2cf2d
commit e641500b40
34 files changed
+2461 -100

No files matched your search

+100 -4
View File
@@ -7,6 +7,7 @@
#pragma once
#include "AmlNamespace.hpp"
#include <CppLib/Spinlock.hpp>
#include <cstdint>
namespace Hal {
@@ -105,6 +106,24 @@ namespace Hal {
static constexpr int MaxCallDepth = 16;
static constexpr int MaxLoopIterations = 1024;
// Backstop cap on evaluation nesting. This counter is shared by
// expression evaluation AND block execution, and block nesting is
// legitimately deep -- every If/Else/While body is another level, so a
// long firmware method reaches well past a handful. The real protection
// against running off the stack is HaveStackHeadroom(), which measures
// actual usage; this only catches a decode that recurses without
// consuming input.
static constexpr int MaxExprDepth = 48;
// How much stack the interpreter may consume before it gives up. Note
// this is an ALLOWANCE, not a reserve: frames here are expensive (a
// name-resolving EvalTerm frame carries a 256-byte path buffer plus
// 272-byte Object temporaries, so ~800 bytes each), and a real
// firmware _BIF nests far enough to need tens of them. Sized against
// Sched::StackPages -- keep well under it, since the syscall path below
// has already spent some of that stack.
static constexpr uint64_t StackBudget = 32768;
// ============================================================================
// Interpreter
@@ -138,6 +157,31 @@ namespace Hal {
// Check if the interpreter has been initialized
bool IsInitialized() const { return m_initialized; }
// Log every opcode executed, with its offset, for the next
// `budget` opcodes. The only way to see where a method that runs to
// completion without returning a value actually goes.
void SetTrace(int budget) { m_traceBudget = budget; }
// ============================================================================
// Packages
// ============================================================================
// Read one element of a Package object. Works for both a package
// whose elements were computed at run time (materialized in the
// package store) and one that is still just the constant AML bytes
// it was declared with. Returns false when the object is not a
// package or the index is out of range.
bool GetPackageElement(const Object& package, int index, Object& result);
// Number of elements a Package declares.
int GetPackageCount(const Object& package);
// Give a package stored in a namespace node real per-element
// storage so Index() can be written through. Returns the store
// index, or -1 if the node does not hold a package.
int32_t MaterializeNodePackage(int32_t nodeIndex);
private:
// ============================================================================
// Parsing (table load)
@@ -158,8 +202,17 @@ namespace Hal {
// ============================================================================
// Read a NameString from AML and produce an absolute path.
// Advances *pos past the name.
// Produces the absolute path (rebased onto scopeNode for a
// relative name). outRelative, when given, receives the name as it
// actually appeared in the AML, and *outSearchable says whether the
// ACPI search rule applies to it -- true only when the name carried
// no root or parent prefix. Resolution must fall back to
// Namespace::SearchUpward() in that case: the absolute path is a
// guess at one scope, and the object usually lives in a parent.
int ReadNameString(const uint8_t* aml, uint32_t* pos, int32_t scopeNode,
char* outPath, int maxLen);
char* outPath, int maxLen,
char* outRelative = nullptr, int relLen = 0,
bool* outSearchable = nullptr);
// Read a single 4-char NameSeg from AML. Advances *pos.
void ReadNameSeg(const uint8_t* aml, uint32_t* pos, char* outSeg);
@@ -170,6 +223,13 @@ namespace Hal {
// ============================================================================
uint32_t DecodePkgLength(const uint8_t* aml, uint32_t* pos);
// Decode one constant package element out of raw AML bytes and
// advance *pos past it. Non-constant elements (name references,
// locals) decode to integer 0, which is what they were worth
// before the package store existed.
bool DecodeConstElement(const uint8_t* data, uint32_t length,
uint32_t* pos, Object& out);
uint64_t DecodeInteger(const uint8_t* aml, uint32_t* pos);
// ============================================================================
@@ -187,7 +247,16 @@ namespace Hal {
Object ReturnValue;
bool Returned;
bool Broken;
int Depth;
int Depth; // method-call nesting
int ExprDepth; // expression/block nesting
};
// Increments a depth counter for the duration of a scope; pairs
// with the MaxExprDepth check at each recursive entry point.
struct DepthGuard {
int& Value;
explicit DepthGuard(int& v) : Value(v) { Value++; }
~DepthGuard() { Value--; }
};
bool ExecuteBlock(ExecContext& ctx, uint32_t offset, uint32_t endOffset);
@@ -199,14 +268,19 @@ namespace Hal {
// Evaluate a "SuperName" target for Store operations.
// Returns the node index for named targets, or handles locals/args.
// If isLocal/isArg is set, localIdx/argIdx contains the index.
// A target may also be Index(package, n), which names a package
// element rather than a node, local or arg. pkgStore/pkgElement
// carry that case (pkgStore > 0 when it applies).
bool EvalTarget(ExecContext& ctx, uint32_t* pos,
int32_t& nodeIndex, bool& isLocal, int& localIdx,
bool& isArg, int& argIdx);
bool& isArg, int& argIdx,
int32_t& pkgStore, int& pkgElement);
// Store a value to a target (node, local, or arg).
void StoreToTarget(ExecContext& ctx, const Object& value,
int32_t nodeIndex, bool isLocal, int localIdx,
bool isArg, int argIdx);
bool isArg, int argIdx,
int32_t pkgStore = -1, int pkgElement = 0);
// ============================================================================
@@ -221,10 +295,32 @@ namespace Hal {
// State
// ============================================================================
// ExecContext is ~4.4 KB (sixteen Objects, each carrying a
// 256-byte buffer inline). One per nested method call on a 16 KiB
// kernel stack overflows after three levels, so they come from a
// pool instead. The pool is only safe because evaluation is
// serialized -- see m_evalLock.
// Stack-exhaustion check; `frame` is the address of a local in
// the calling frame.
bool HaveStackHeadroom(const void* frame) const;
// One-shot warning when the nesting backstop trips.
static void ReportDepthLimit();
ExecContext* AcquireContext();
void ReleaseContext();
Namespace m_ns;
const uint8_t* m_dsdt;
uint32_t m_dsdtLength;
bool m_initialized;
ExecContext* m_ctxPool; // MaxCallDepth + 1 entries, heap
int m_ctxInUse;
int m_evalNesting; // top-level evaluations in progress
uint64_t m_stackFloor; // stack address the interpreter stops at
int m_traceBudget; // opcodes left to log, 0 = off
kcp::Mutex m_evalLock;
};
// ============================================================================