feat: power down dGPUs on hybrid graphics laptops

This commit is contained in:
daniel committed 2026-10-01 13:09:26 +02:00
1 parent 7f12e1a150
commit 24f88ec4c6
23 files changed
+4325 -1130

No files matched your search

+204 -30
View File
@@ -97,6 +97,35 @@ namespace Hal {
static constexpr uint8_t ConcatResOp = 0x84;
static constexpr uint8_t ToHexStringOp = 0x98;
static constexpr uint8_t ToDecimalStringOp = 0x97;
static constexpr uint8_t ExternalOp = 0x15;
static constexpr uint8_t RefOfOp = 0x71;
static constexpr uint8_t FindSetLeftBitOp = 0x81;
static constexpr uint8_t FindSetRightBitOp = 0x82;
static constexpr uint8_t NotifyOp = 0x86;
static constexpr uint8_t MatchOp = 0x89;
static constexpr uint8_t ObjectTypeOp = 0x8E;
static constexpr uint8_t CreateQWordFieldOp = 0x8F;
static constexpr uint8_t ToStringOp = 0x9C;
static constexpr uint8_t CopyObjectOp = 0x9D;
static constexpr uint8_t MidOp = 0x9E;
static constexpr uint8_t ContinueOp = 0x9F;
static constexpr uint8_t BreakPointOp = 0xCC;
// More extended opcodes (after ExtOpPrefix)
static constexpr uint8_t CondRefOfOp = 0x12;
static constexpr uint8_t CreateFieldOp = 0x13;
static constexpr uint8_t LoadTableOp = 0x1F;
static constexpr uint8_t LoadOp = 0x20;
static constexpr uint8_t SignalOp = 0x24;
static constexpr uint8_t WaitOp = 0x25;
static constexpr uint8_t ResetOp = 0x26;
static constexpr uint8_t FromBCDOp = 0x28;
static constexpr uint8_t ToBCDOp = 0x29;
static constexpr uint8_t UnloadOp = 0x2A;
static constexpr uint8_t DebugOp = 0x31;
static constexpr uint8_t FatalOp = 0x32;
static constexpr uint8_t TimerOp = 0x33;
static constexpr uint8_t DataRegionOp = 0x88;
// ============================================================================
@@ -104,7 +133,11 @@ namespace Hal {
// ============================================================================
static constexpr int MaxCallDepth = 16;
static constexpr int MaxLoopIterations = 1024;
// ACPICA's ceiling. Firmware polls hardware in While loops with a
// Sleep or Stall in the body (link training, power-good, L2/L3 ready)
// and expects more than a token number of passes.
static constexpr int MaxLoopIterations = 0xFFFF;
// Backstop cap on evaluation nesting. This counter is shared by
// expression evaluation AND block execution, and block nesting is
@@ -133,8 +166,10 @@ namespace Hal {
public:
Interpreter();
// Parse a DSDT or SSDT table into the namespace.
// tableData points to the CommonSDTHeader (HHDM-mapped).
// Parse a DSDT or SSDT table into the namespace. Tables may be
// loaded one after another; each method remembers which table
// its body lives in. tableData points to the CommonSDTHeader
// (HHDM-mapped) and must stay mapped.
bool LoadTable(void* tableData);
// Evaluate a named object, returning its value.
@@ -144,6 +179,28 @@ namespace Hal {
// Evaluate a method with arguments.
bool EvaluateMethod(const char* path, const Object* args, int argCount, Object& result);
// Evaluate a node: a method is run with `args`, a field is read,
// anything else hands back its value (non-data objects as a
// Reference to the node).
bool EvaluateNode(int32_t node, const Object* args, int argCount, Object& result);
// Find the child of `parent` named `seg` ("_PS3", "_ON"), -1 if
// there is none. Short names are padded with '_'.
int32_t FindChild(int32_t parent, const char* seg) const;
// EvaluateNode() on a named child. False when it does not exist.
bool EvaluateChild(int32_t parent, const char* seg, const Object* args,
int argCount, Object& result);
// Turn a Reference, or a NameRef/String naming an object relative
// to `scopeNode`, into a node index. -1 when it names nothing.
int32_t ResolveReference(const Object& obj, int32_t scopeNode) const;
// The PCI function a Device node describes: its _ADR on the bus
// behind its parent (a root bridge's _BBN, or a PCI-to-PCI
// bridge's secondary bus). False when it cannot be placed.
bool GetPciAddress(int32_t deviceNode, uint8_t& bus, uint8_t& dev, uint8_t& fn);
// Read a field value. Returns integer.
bool ReadField(int32_t nodeIndex, uint64_t& value);
@@ -157,6 +214,10 @@ namespace Hal {
// Check if the interpreter has been initialized
bool IsInitialized() const { return m_initialized; }
// Nodes [0, DsdtNodeCount()) came from the DSDT. Nodes past it were
// added by SSDTs (or by methods run since).
int32_t DsdtNodeCount() const { return m_dsdtNodeCount; }
// 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.
@@ -170,8 +231,10 @@ namespace Hal {
// 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.
// it was declared with. A name in a constant package comes back as
// a NameRef unless it resolves (from the root) to plain data, in
// which case its value is returned. 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.
@@ -183,17 +246,35 @@ namespace Hal {
int32_t MaterializeNodePackage(int32_t nodeIndex);
private:
struct ExecContext;
// ============================================================================
// Parsing (table load)
// ============================================================================
// ctx is the method being executed when a definition appears in
// a method body, nullptr at table load. Operands that are TermArgs
// (region offsets, buffer sizes, bank values) are evaluated in it.
bool ParseBlock(const uint8_t* aml, uint32_t offset, uint32_t endOffset,
int32_t scopeNode);
bool ParseNamedObject(const uint8_t* aml, uint32_t* pos, uint32_t endOffset,
int32_t scopeNode);
int32_t scopeNode, ExecContext* ctx = nullptr);
bool ParseExtendedOp(const uint8_t* aml, uint32_t* pos, uint32_t endOffset,
int32_t scopeNode);
int32_t scopeNode, ExecContext* ctx = nullptr);
// The FieldList shared by Field, IndexField and BankField.
void ParseFieldList(const uint8_t* aml, uint32_t* pos, uint32_t fieldEnd,
int32_t scopeNode, uint8_t fieldFlags, const Object& proto);
// Evaluate a TermArg operand of a definition: in the running
// method, or in the table-load context.
bool EvalOperand(ExecContext* ctx, const uint8_t* aml, uint32_t* pos,
uint32_t endOffset, int32_t scopeNode, Object& result);
// CreateBitField .. CreateQWordField and CreateField.
bool CreateBufferField(ExecContext& ctx, uint32_t* pos, uint32_t endOffset,
uint8_t op);
// ============================================================================
@@ -214,6 +295,10 @@ namespace Hal {
char* outRelative = nullptr, int relLen = 0,
bool* outSearchable = nullptr);
// Read a NameString and resolve it with the search rule. -1 when
// it names nothing.
int32_t ReadAndResolveName(const uint8_t* aml, uint32_t* pos, int32_t scopeNode);
// Read a single 4-char NameSeg from AML. Advances *pos.
void ReadNameSeg(const uint8_t* aml, uint32_t* pos, char* outSeg);
@@ -225,11 +310,10 @@ 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.
// advance *pos past it. Names come back as NameRef; locals and
// anything else that needs a running method decode to integer 0.
bool DecodeConstElement(const uint8_t* data, uint32_t length,
uint32_t* pos, Object& out);
uint32_t* pos, Object& out) const;
uint64_t DecodeInteger(const uint8_t* aml, uint32_t* pos);
// ============================================================================
@@ -247,6 +331,8 @@ namespace Hal {
Object ReturnValue;
bool Returned;
bool Broken;
bool Continued;
bool Aborted; // undecodable AML: unwind the method
int Depth; // method-call nesting
int ExprDepth; // expression/block nesting
};
@@ -259,36 +345,117 @@ namespace Hal {
~DepthGuard() { Value--; }
};
// Where a Store lands.
struct Target {
enum class Kind : uint8_t { None, Node, Local, Arg, PkgElement, BufByte };
Kind What = Kind::None;
int32_t Node = -1;
int Index = 0; // local/arg number, or element index
int32_t PkgStore = -1;
Object* Buf = nullptr; // BufByte: the buffer being indexed
};
bool ExecuteBlock(ExecContext& ctx, uint32_t offset, uint32_t endOffset);
bool ExecuteOpcode(ExecContext& ctx, uint32_t* pos, uint32_t endOffset);
// Evaluate a term (expression that produces a value) within an execution context.
bool EvalTerm(ExecContext& ctx, uint32_t* pos, uint32_t endOffset, Object& result);
// 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,
int32_t& pkgStore, int& pkgElement);
// Extended (0x5B-prefixed) opcodes in expression or statement position.
bool EvalExtOp(ExecContext& ctx, uint32_t* pos, uint32_t endOffset, Object& result);
// 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,
int32_t pkgStore = -1, int pkgElement = 0);
// EvalTerm's opcode handlers. They live out of line so the
// recursive path only carries the locals of the one running.
bool EvalBufferOp(ExecContext& ctx, uint32_t* pos, uint32_t endOffset, Object& result);
bool EvalPackageOp(ExecContext& ctx, uint32_t* pos, uint32_t endOffset, Object& result);
bool EvalBinary(ExecContext& ctx, uint32_t* pos, uint32_t endOffset, Object& result);
bool EvalUnary(ExecContext& ctx, uint32_t* pos, uint32_t endOffset, Object& result);
bool EvalDivide(ExecContext& ctx, uint32_t* pos, uint32_t endOffset, Object& result);
bool EvalStoreOp(ExecContext& ctx, uint32_t* pos, uint32_t endOffset, Object& result);
bool EvalReference(ExecContext& ctx, uint32_t* pos, uint32_t endOffset, Object& result);
bool EvalIndexOp(ExecContext& ctx, uint32_t* pos, uint32_t endOffset, Object& result);
bool EvalMatchOp(ExecContext& ctx, uint32_t* pos, uint32_t endOffset, Object& result);
bool EvalMidOp(ExecContext& ctx, uint32_t* pos, uint32_t endOffset, Object& result);
bool EvalNameRef(ExecContext& ctx, uint32_t* pos, uint32_t endOffset, Object& result);
bool CallMethod(ExecContext& ctx, int32_t node, uint32_t* pos, uint32_t endOffset,
Object& result);
bool IndexElement(const Object& source, uint64_t idx, Object& out);
void ReportUnresolved(ExecContext& ctx, uint32_t namePos);
// Target forms that need operand evaluation of their own.
bool EvalIndexTarget(ExecContext& ctx, uint32_t* pos, Target& target);
bool EvalDerefTarget(ExecContext& ctx, uint32_t* pos, Target& target);
// A package element: like EvalTerm, except that a name is a
// reference to the object rather than a call or a read.
bool EvalPackageElement(ExecContext& ctx, uint32_t* pos, uint32_t endOffset,
Object& result);
// Parse a SuperName/Target operand.
bool EvalTarget(ExecContext& ctx, uint32_t* pos, Target& target);
// Store a value to a target, with ACPI's implicit conversion for
// named data objects.
void StoreToTarget(ExecContext& ctx, const Object& value, const Target& target);
// Parse a Target operand and store `value` there.
void StoreResult(ExecContext& ctx, uint32_t* pos, const Object& value);
// The current value behind a target (for Increment and friends).
Object ReadTarget(ExecContext& ctx, const Target& target);
// Run a method node. Caller holds m_evalLock.
bool InvokeMethodNode(int32_t node, const Object* args, int argCount,
Object& result, int depth, int exprDepth);
// Run a method in a context already acquired and holding its
// arguments; releases the context.
bool RunMethodIn(ExecContext* ctx, int32_t node, int depth, int exprDepth,
Object& result);
// The value of a node as an operand: fields read, methods run
// without arguments, non-data objects become References.
bool NodeValue(int32_t node, Object& result, int depth, int exprDepth);
// Log an opcode we cannot decode and stop the method: carrying on
// past it would execute operand bytes as code.
bool Abort(ExecContext& ctx, uint32_t pos, const char* why);
// Native methods (\_OSI).
bool RunNativeMethod(uint8_t which, const Object* args, int argCount, Object& result);
// Install the OS-defined objects (\_OSI, \_REV, \_OS_, \_GL_).
void InstallPredefined();
// Device identification (for PCI_Config regions).
bool IntegerChild(int32_t device, const char* seg, uint64_t fallback,
uint64_t& value, int depth);
bool IsPciRootBridge(int32_t device, int depth);
bool ResolvePciAddress(int32_t node, uint8_t& bus, uint8_t& dev,
uint8_t& fn, int depth);
// ============================================================================
// Field I/O
// ============================================================================
bool ReadRegion(RegionSpace space, uint64_t address, uint32_t bitWidth, uint64_t& value);
bool WriteRegion(RegionSpace space, uint64_t address, uint32_t bitWidth, uint64_t value);
// Access `bits` (little-endian, bitLength long) of a field unit.
bool AccessField(int32_t fieldNode, bool write, uint8_t* bits);
bool ReadFieldObject(int32_t fieldNode, Object& value);
bool WriteFieldObject(int32_t fieldNode, const Object& value);
// One aligned access unit of a field's backing store.
bool FieldUnitIo(const Object& field, uint64_t byteOffset, uint32_t width,
bool write, uint64_t& value);
// Region access at byte `offset` into the region, `width` bytes.
bool ReadRegion(int32_t regionNode, uint64_t offset, uint32_t width, uint64_t& value);
bool WriteRegion(int32_t regionNode, uint64_t offset, uint32_t width, uint64_t value);
bool RegionIo(int32_t regionNode, uint64_t offset, uint32_t width,
bool write, uint64_t& value);
bool ReadBufferField(const Object& field, Object& value);
bool WriteBufferField(const Object& field, const Object& value);
// ============================================================================
@@ -310,16 +477,23 @@ namespace Hal {
ExecContext* AcquireContext();
void ReleaseContext();
// Run `body` as a top-level evaluation: serialized, with the stack
// budget anchored here.
template<typename Fn> bool TopLevel(Fn body);
Namespace m_ns;
const uint8_t* m_dsdt;
uint32_t m_dsdtLength;
bool m_initialized;
bool m_predefined;
int32_t m_dsdtNodeCount;
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
ExecContext* m_loadCtx; // table-load operand context
uint64_t m_delayedUs; // total Sleep/Stall time requested
int m_methodScan; // >0 while scanning a method body at load
kcp::Mutex m_evalLock;
};