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:
1 parent
0a18b2cf2d
commit
e641500b40
34 files changed
+2461
-100
No files matched your search
@@ -6,10 +6,13 @@
|
||||
|
||||
#include "AmlInterpreter.hpp"
|
||||
#include <ACPI/ACPI.hpp>
|
||||
#include <ACPI/EmbeddedController.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
#include <Io/IoPort.hpp>
|
||||
#include <Memory/HHDM.hpp>
|
||||
#include <Memory/Paging.hpp>
|
||||
#include <Memory/Heap.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
|
||||
using namespace Kt;
|
||||
@@ -41,13 +44,78 @@ namespace Hal {
|
||||
return IsLeadNameChar(c) || (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
// The interpreter runs on whatever stack its caller has -- the boot
|
||||
// stack early on, a thread's 16 KiB kernel stack once userspace is up.
|
||||
// m_stackFloor is anchored at the top-level entry; a frame below it has
|
||||
// spent the budget and must unwind rather than keep recursing.
|
||||
bool Interpreter::HaveStackHeadroom(const void* frame) const {
|
||||
if (m_stackFloor == 0) return true; // no evaluation in progress
|
||||
if ((uint64_t)frame > m_stackFloor) return true;
|
||||
|
||||
// Say so once. A method that quietly gives up here returns a
|
||||
// default value that looks like a hardware answer -- a battery
|
||||
// reporting itself absent, say -- and nothing upstream can tell
|
||||
// the difference.
|
||||
static bool warned = false;
|
||||
if (!warned) {
|
||||
warned = true;
|
||||
KernelLogStream(WARNING, "AML")
|
||||
<< "Evaluation stopped: spent the " << base::dec
|
||||
<< (uint64_t)(StackBudget / 1024) << " KiB stack allowance";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Interpreter::ReportDepthLimit() {
|
||||
static bool warned = false;
|
||||
if (!warned) {
|
||||
warned = true;
|
||||
KernelLogStream(WARNING, "AML") << "Evaluation stopped: nesting past "
|
||||
<< base::dec << (uint64_t)MaxExprDepth << " levels";
|
||||
}
|
||||
}
|
||||
|
||||
// True when [address, address+bytes) is inside the contiguous HHDM
|
||||
// mapping and so safe to touch at phys+hhdmBase.
|
||||
static bool HhdmReadable(uint64_t address, uint32_t bytes) {
|
||||
uint64_t limit = Memory::VMM::Paging::HhdmLimit();
|
||||
if (limit == 0) return false;
|
||||
if (address >= limit) return false;
|
||||
return (limit - address) >= bytes;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// Constructor
|
||||
|
||||
// ============================================================================
|
||||
Interpreter::Interpreter()
|
||||
: m_dsdt(nullptr), m_dsdtLength(0), m_initialized(false) {}
|
||||
: m_dsdt(nullptr), m_dsdtLength(0), m_initialized(false),
|
||||
m_ctxPool(nullptr), m_ctxInUse(0), m_evalNesting(0), m_stackFloor(0),
|
||||
m_traceBudget(0) {}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// Execution context pool
|
||||
|
||||
// ============================================================================
|
||||
Interpreter::ExecContext* Interpreter::AcquireContext() {
|
||||
if (!m_ctxPool) {
|
||||
m_ctxPool = (ExecContext*)Memory::g_heap->Request(
|
||||
sizeof(ExecContext) * (MaxCallDepth + 1));
|
||||
if (!m_ctxPool) return nullptr;
|
||||
}
|
||||
|
||||
if (m_ctxInUse > MaxCallDepth) return nullptr;
|
||||
|
||||
ExecContext* ctx = &m_ctxPool[m_ctxInUse++];
|
||||
*ctx = ExecContext{};
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void Interpreter::ReleaseContext() {
|
||||
if (m_ctxInUse > 0) m_ctxInUse--;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -148,7 +216,9 @@ namespace Hal {
|
||||
// Reads a NameString (which may include root prefix, parent prefixes,
|
||||
// dual/multi name prefix) and produces an absolute path.
|
||||
int Interpreter::ReadNameString(const uint8_t* aml, uint32_t* pos, int32_t scopeNode,
|
||||
char* outPath, int maxLen) {
|
||||
char* outPath, int maxLen,
|
||||
char* outRelative, int relLen,
|
||||
bool* outSearchable) {
|
||||
int pathPos = 0;
|
||||
bool isAbsolute = false;
|
||||
int parentPrefixCount = 0;
|
||||
@@ -220,6 +290,22 @@ namespace Hal {
|
||||
}
|
||||
|
||||
outPath[pathPos] = '\0';
|
||||
|
||||
// Hand back the name as written, for callers that must apply the
|
||||
// ACPI search rule when the rebased path does not exist.
|
||||
if (outSearchable)
|
||||
*outSearchable = !isAbsolute && parentPrefixCount == 0 && segCount > 0;
|
||||
|
||||
if (outRelative && relLen > 0) {
|
||||
int rp = 0;
|
||||
for (int i = 0; i < segCount; i++) {
|
||||
if (i > 0 && rp < relLen - 1) outRelative[rp++] = '.';
|
||||
for (int j = 0; j < 4 && rp < relLen - 1; j++)
|
||||
outRelative[rp++] = segments[i][j];
|
||||
}
|
||||
outRelative[rp] = '\0';
|
||||
}
|
||||
|
||||
return pathPos;
|
||||
}
|
||||
|
||||
@@ -582,13 +668,17 @@ namespace Hal {
|
||||
uint32_t fieldEnd = pkgStart + pkgLen;
|
||||
if (fieldEnd > endOffset) fieldEnd = endOffset;
|
||||
|
||||
// Region name
|
||||
// Region name. A Field almost always names a region
|
||||
// declared in an enclosing scope, so the search rule is not
|
||||
// optional here.
|
||||
char regionPath[256];
|
||||
ReadNameString(aml, pos, scopeNode, regionPath, 256);
|
||||
char regionRel[64];
|
||||
bool regionSearchable = false;
|
||||
ReadNameString(aml, pos, scopeNode, regionPath, 256,
|
||||
regionRel, sizeof(regionRel), ®ionSearchable);
|
||||
int32_t regionNode = m_ns.FindNode(regionPath);
|
||||
if (regionNode < 0) {
|
||||
regionNode = m_ns.ResolveName(regionPath, scopeNode);
|
||||
}
|
||||
if (regionNode < 0 && regionSearchable)
|
||||
regionNode = m_ns.SearchUpward(regionRel, scopeNode);
|
||||
|
||||
// Field flags
|
||||
uint8_t fieldFlags = aml[*pos];
|
||||
@@ -833,34 +923,74 @@ namespace Hal {
|
||||
auto* nsNode = m_ns.GetNode(node);
|
||||
if (!nsNode || nsNode->Obj.Type != ObjectType::Method) return false;
|
||||
|
||||
ExecContext ctx{};
|
||||
ctx.Aml = m_dsdt;
|
||||
ctx.AmlBase = nsNode->Obj.Method.AmlOffset;
|
||||
ctx.AmlLength = nsNode->Obj.Method.AmlLength;
|
||||
ctx.ScopeNode = node;
|
||||
ctx.Returned = false;
|
||||
ctx.Broken = false;
|
||||
ctx.Depth = 0;
|
||||
// Packages built inside the method come out of the transient ring.
|
||||
// Rewinding it here bounds the store to one evaluation's worth,
|
||||
// which is what makes repeated polling (_BST every few seconds)
|
||||
// safe. The caller must read the returned package before the next
|
||||
// evaluation, which every caller in the kernel does.
|
||||
// Evaluation is serialized: the execution contexts come from a
|
||||
// shared pool, and the battery indicator now polls from a user
|
||||
// thread while other subsystems may evaluate AML of their own.
|
||||
bool topLevel = false;
|
||||
m_evalLock.Acquire();
|
||||
if (m_evalNesting == 0) {
|
||||
topLevel = true;
|
||||
PackageStore::ResetTransient();
|
||||
|
||||
// Anchor the stack budget to this frame. Everything below
|
||||
// measures against it, so the same limit works whether we were
|
||||
// called on the large boot stack or a thread's 16 KiB one.
|
||||
char stackMarker = 0;
|
||||
m_stackFloor = (uint64_t)&stackMarker - StackBudget;
|
||||
}
|
||||
m_evalNesting++;
|
||||
|
||||
ExecContext* ctx = AcquireContext();
|
||||
if (!ctx) {
|
||||
m_evalNesting--;
|
||||
m_evalLock.Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx->Aml = m_dsdt;
|
||||
ctx->AmlBase = nsNode->Obj.Method.AmlOffset;
|
||||
ctx->AmlLength = nsNode->Obj.Method.AmlLength;
|
||||
ctx->ScopeNode = node;
|
||||
ctx->Returned = false;
|
||||
ctx->Broken = false;
|
||||
ctx->Depth = 0;
|
||||
ctx->ExprDepth = 0;
|
||||
|
||||
// Set up arguments
|
||||
for (int i = 0; i < argCount && i < MaxMethodArgs; i++) {
|
||||
ctx.Args[i] = args[i];
|
||||
ctx->Args[i] = args[i];
|
||||
}
|
||||
|
||||
// Initialize locals to zero
|
||||
for (int i = 0; i < MaxMethodLocals; i++) {
|
||||
ctx.Locals[i].Type = ObjectType::Integer;
|
||||
ctx.Locals[i].Integer = 0;
|
||||
ctx->Locals[i].Type = ObjectType::Integer;
|
||||
ctx->Locals[i].Integer = 0;
|
||||
}
|
||||
|
||||
bool ok = ExecuteBlock(ctx, ctx.AmlBase, ctx.AmlBase + ctx.AmlLength);
|
||||
bool ok = ExecuteBlock(*ctx, ctx->AmlBase, ctx->AmlBase + ctx->AmlLength);
|
||||
|
||||
result = ctx.ReturnValue;
|
||||
result = ctx->ReturnValue;
|
||||
if (result.Type == ObjectType::None) {
|
||||
result.Type = ObjectType::Integer;
|
||||
result.Integer = 0;
|
||||
}
|
||||
|
||||
ReleaseContext();
|
||||
m_evalNesting--;
|
||||
|
||||
// The floor refers to a frame on this call's stack. Leaving it set
|
||||
// would have the next evaluation -- possibly on a different thread's
|
||||
// stack entirely -- compare against an address that no longer means
|
||||
// anything, and bail out for no reason.
|
||||
if (topLevel) m_stackFloor = 0;
|
||||
|
||||
m_evalLock.Release();
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -870,6 +1000,12 @@ namespace Hal {
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::ExecuteBlock(ExecContext& ctx, uint32_t offset, uint32_t endOffset) {
|
||||
char stackMarker = 0;
|
||||
if (ctx.ExprDepth >= MaxExprDepth) ReportDepthLimit();
|
||||
if (ctx.ExprDepth >= MaxExprDepth || !HaveStackHeadroom(&stackMarker))
|
||||
return false;
|
||||
DepthGuard depthGuard(ctx.ExprDepth);
|
||||
|
||||
uint32_t pos = offset;
|
||||
|
||||
while (pos < endOffset && !ctx.Returned && !ctx.Broken) {
|
||||
@@ -890,6 +1026,19 @@ namespace Hal {
|
||||
|
||||
uint8_t op = ctx.Aml[*pos];
|
||||
|
||||
if (m_traceBudget > 0) {
|
||||
m_traceBudget--;
|
||||
// Offsets are absolute into the DSDT, so they line up with a
|
||||
// disassembly of the same table.
|
||||
if (op == ExtOpPrefix && *pos + 1 < endOffset) {
|
||||
KernelLogStream(INFO, "AML") << " @" << base::hex << (uint64_t)*pos
|
||||
<< " ext op " << (uint64_t)ctx.Aml[*pos + 1];
|
||||
} else {
|
||||
KernelLogStream(INFO, "AML") << " @" << base::hex << (uint64_t)*pos
|
||||
<< " op " << (uint64_t)op;
|
||||
}
|
||||
}
|
||||
|
||||
// Return
|
||||
if (op == ReturnOp) {
|
||||
(*pos)++;
|
||||
@@ -988,8 +1137,11 @@ namespace Hal {
|
||||
int32_t targetNode = -1;
|
||||
bool isLocal = false, isArg = false;
|
||||
int localIdx = 0, argIdx = 0;
|
||||
EvalTarget(ctx, pos, targetNode, isLocal, localIdx, isArg, argIdx);
|
||||
StoreToTarget(ctx, value, targetNode, isLocal, localIdx, isArg, argIdx);
|
||||
int32_t pkgStore = -1; int pkgElement = 0;
|
||||
EvalTarget(ctx, pos, targetNode, isLocal, localIdx, isArg, argIdx,
|
||||
pkgStore, pkgElement);
|
||||
StoreToTarget(ctx, value, targetNode, isLocal, localIdx, isArg, argIdx,
|
||||
pkgStore, pkgElement);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -999,7 +1151,9 @@ namespace Hal {
|
||||
int32_t targetNode = -1;
|
||||
bool isLocal = false, isArg = false;
|
||||
int localIdx = 0, argIdx = 0;
|
||||
EvalTarget(ctx, pos, targetNode, isLocal, localIdx, isArg, argIdx);
|
||||
int32_t pkgStore = -1; int pkgElement = 0;
|
||||
EvalTarget(ctx, pos, targetNode, isLocal, localIdx, isArg, argIdx,
|
||||
pkgStore, pkgElement);
|
||||
|
||||
Object current{};
|
||||
if (isLocal) current = ctx.Locals[localIdx];
|
||||
@@ -1012,7 +1166,8 @@ namespace Hal {
|
||||
if (current.Type == ObjectType::Integer) {
|
||||
current.Integer += (op == IncrementOp) ? 1 : -1;
|
||||
}
|
||||
StoreToTarget(ctx, current, targetNode, isLocal, localIdx, isArg, argIdx);
|
||||
StoreToTarget(ctx, current, targetNode, isLocal, localIdx, isArg, argIdx,
|
||||
pkgStore, pkgElement);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1083,6 +1238,16 @@ namespace Hal {
|
||||
// Evaluate an AML term that produces a value.
|
||||
bool Interpreter::EvalTerm(ExecContext& ctx, uint32_t* pos, uint32_t endOffset,
|
||||
Object& result) {
|
||||
char stackMarker = 0;
|
||||
if (ctx.ExprDepth >= MaxExprDepth) ReportDepthLimit();
|
||||
if (ctx.ExprDepth >= MaxExprDepth || !HaveStackHeadroom(&stackMarker)) {
|
||||
result = Object{};
|
||||
result.Type = ObjectType::Integer;
|
||||
result.Integer = 0;
|
||||
return false;
|
||||
}
|
||||
DepthGuard depthGuard(ctx.ExprDepth);
|
||||
|
||||
if (*pos >= endOffset) return false;
|
||||
|
||||
uint8_t op = ctx.Aml[*pos];
|
||||
@@ -1136,6 +1301,14 @@ namespace Hal {
|
||||
return true;
|
||||
}
|
||||
|
||||
// DerefOf. Index() already yields the element's value here, so a
|
||||
// dereference of one is just the value again; anything else passes
|
||||
// through unchanged.
|
||||
if (op == DerefOfOp) {
|
||||
(*pos)++;
|
||||
return EvalTerm(ctx, pos, endOffset, result);
|
||||
}
|
||||
|
||||
// Package
|
||||
if (op == PackageOp) {
|
||||
(*pos)++;
|
||||
@@ -1144,13 +1317,38 @@ namespace Hal {
|
||||
uint32_t pkgEnd = pkgStart + pkgLen;
|
||||
if (pkgEnd > endOffset) pkgEnd = endOffset;
|
||||
|
||||
// Store raw package as buffer for now
|
||||
// Retain the raw bytes (constant elements are read straight
|
||||
// out of them) and, in parallel, evaluate each element into the
|
||||
// package store so computed ones -- Package(){Local0, Local1},
|
||||
// the shape _BST and friends are built with -- survive.
|
||||
result.Type = ObjectType::Package;
|
||||
uint32_t dataLen = pkgEnd - *pos;
|
||||
if (dataLen > MaxBufferLen) dataLen = MaxBufferLen;
|
||||
result.Buffer.Length = dataLen;
|
||||
memcpy(result.Buffer.Data, &ctx.Aml[*pos], dataLen);
|
||||
|
||||
uint32_t elemPos = *pos;
|
||||
int count = (elemPos < pkgEnd) ? (int)ctx.Aml[elemPos] : 0;
|
||||
elemPos++;
|
||||
if (count > MaxPackageElements) count = MaxPackageElements;
|
||||
|
||||
if (count > 0) {
|
||||
int32_t store = PackageStore::AllocTransient(count);
|
||||
if (store > 0) {
|
||||
for (int i = 0; i < count && elemPos < pkgEnd; i++) {
|
||||
Object elem{};
|
||||
uint32_t before = elemPos;
|
||||
if (!EvalTerm(ctx, &elemPos, pkgEnd, elem)) break;
|
||||
// An element that consumed nothing would spin here
|
||||
// forever on a stream we decoded wrongly.
|
||||
if (elemPos <= before) break;
|
||||
Object* slot = PackageStore::Element(store, i);
|
||||
if (slot) *slot = elem;
|
||||
}
|
||||
result.PkgIndex = store;
|
||||
}
|
||||
}
|
||||
|
||||
*pos = pkgEnd;
|
||||
return true;
|
||||
}
|
||||
@@ -1259,14 +1457,16 @@ namespace Hal {
|
||||
int32_t tn = -1;
|
||||
bool il = false, ia = false;
|
||||
int li = 0, ai = 0;
|
||||
EvalTarget(ctx, pos, tn, il, li, ia, ai);
|
||||
StoreToTarget(ctx, result, tn, il, li, ia, ai);
|
||||
int32_t ps = -1; int pe = 0;
|
||||
EvalTarget(ctx, pos, tn, il, li, ia, ai, ps, pe);
|
||||
StoreToTarget(ctx, result, tn, il, li, ia, ai, ps, pe);
|
||||
} else if (IsLeadNameChar(next) || next == '\\' || next == '^') {
|
||||
int32_t tn = -1;
|
||||
bool il = false, ia = false;
|
||||
int li = 0, ai = 0;
|
||||
EvalTarget(ctx, pos, tn, il, li, ia, ai);
|
||||
StoreToTarget(ctx, result, tn, il, li, ia, ai);
|
||||
int32_t ps = -1; int pe = 0;
|
||||
EvalTarget(ctx, pos, tn, il, li, ia, ai, ps, pe);
|
||||
StoreToTarget(ctx, result, tn, il, li, ia, ai, ps, pe);
|
||||
}
|
||||
// ZeroOp as "no target" — skip
|
||||
else if (next == ZeroOp) {
|
||||
@@ -1289,8 +1489,9 @@ namespace Hal {
|
||||
int32_t tn = -1;
|
||||
bool il = false, ia = false;
|
||||
int li = 0, ai = 0;
|
||||
EvalTarget(ctx, pos, tn, il, li, ia, ai);
|
||||
StoreToTarget(ctx, result, tn, il, li, ia, ai);
|
||||
int32_t ps = -1; int pe = 0;
|
||||
EvalTarget(ctx, pos, tn, il, li, ia, ai, ps, pe);
|
||||
StoreToTarget(ctx, result, tn, il, li, ia, ai, ps, pe);
|
||||
} else if (*pos < endOffset && ctx.Aml[*pos] == ZeroOp) {
|
||||
(*pos)++;
|
||||
}
|
||||
@@ -1321,16 +1522,18 @@ namespace Hal {
|
||||
int32_t tn = -1;
|
||||
bool il = false, ia = false;
|
||||
int li = 0, ai = 0;
|
||||
EvalTarget(ctx, pos, tn, il, li, ia, ai);
|
||||
StoreToTarget(ctx, remainder, tn, il, li, ia, ai);
|
||||
int32_t ps = -1; int pe = 0;
|
||||
EvalTarget(ctx, pos, tn, il, li, ia, ai, ps, pe);
|
||||
StoreToTarget(ctx, remainder, tn, il, li, ia, ai, ps, pe);
|
||||
}
|
||||
// Result target
|
||||
if (*pos < endOffset) {
|
||||
int32_t tn = -1;
|
||||
bool il = false, ia = false;
|
||||
int li = 0, ai = 0;
|
||||
EvalTarget(ctx, pos, tn, il, li, ia, ai);
|
||||
StoreToTarget(ctx, result, tn, il, li, ia, ai);
|
||||
int32_t ps = -1; int pe = 0;
|
||||
EvalTarget(ctx, pos, tn, il, li, ia, ai, ps, pe);
|
||||
StoreToTarget(ctx, result, tn, il, li, ia, ai, ps, pe);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1345,6 +1548,8 @@ namespace Hal {
|
||||
result.Integer = obj.String.Length;
|
||||
else if (obj.Type == ObjectType::Buffer)
|
||||
result.Integer = obj.Buffer.Length;
|
||||
else if (obj.Type == ObjectType::Package)
|
||||
result.Integer = (uint64_t)GetPackageCount(obj);
|
||||
else
|
||||
result.Integer = 0;
|
||||
return true;
|
||||
@@ -1357,12 +1562,23 @@ namespace Hal {
|
||||
EvalTerm(ctx, pos, endOffset, source);
|
||||
EvalTerm(ctx, pos, endOffset, index);
|
||||
|
||||
result.Type = ObjectType::Integer;
|
||||
uint64_t idx = index.Integer;
|
||||
|
||||
if (source.Type == ObjectType::Buffer && idx < source.Buffer.Length) {
|
||||
if (source.Type == ObjectType::Package) {
|
||||
// ACPI defines Index() as producing a reference; we hand
|
||||
// back the element's value instead, which is what every
|
||||
// operand context here goes on to use. Writes through
|
||||
// Index() are handled separately, in EvalTarget.
|
||||
if (!GetPackageElement(source, (int)idx, result)) {
|
||||
result = Object{};
|
||||
result.Type = ObjectType::Integer;
|
||||
result.Integer = 0;
|
||||
}
|
||||
} else if (source.Type == ObjectType::Buffer && idx < source.Buffer.Length) {
|
||||
result.Type = ObjectType::Integer;
|
||||
result.Integer = source.Buffer.Data[idx];
|
||||
} else {
|
||||
result.Type = ObjectType::Integer;
|
||||
result.Integer = 0;
|
||||
}
|
||||
|
||||
@@ -1371,8 +1587,9 @@ namespace Hal {
|
||||
int32_t tn = -1;
|
||||
bool il = false, ia = false;
|
||||
int li = 0, ai = 0;
|
||||
EvalTarget(ctx, pos, tn, il, li, ia, ai);
|
||||
StoreToTarget(ctx, result, tn, il, li, ia, ai);
|
||||
int32_t ps = -1; int pe = 0;
|
||||
EvalTarget(ctx, pos, tn, il, li, ia, ai, ps, pe);
|
||||
StoreToTarget(ctx, result, tn, il, li, ia, ai, ps, pe);
|
||||
} else if (*pos < endOffset && ctx.Aml[*pos] == ZeroOp) {
|
||||
(*pos)++;
|
||||
}
|
||||
@@ -1405,12 +1622,14 @@ namespace Hal {
|
||||
if (IsLeadNameChar(op) || op == '\\' || op == '^' ||
|
||||
op == DualNamePrefix || op == MultiNamePrefix) {
|
||||
char path[256];
|
||||
ReadNameString(ctx.Aml, pos, ctx.ScopeNode, path, 256);
|
||||
char relative[64];
|
||||
bool searchable = false;
|
||||
ReadNameString(ctx.Aml, pos, ctx.ScopeNode, path, 256,
|
||||
relative, sizeof(relative), &searchable);
|
||||
|
||||
int32_t node = m_ns.FindNode(path);
|
||||
if (node < 0) {
|
||||
node = m_ns.ResolveName(path, ctx.ScopeNode);
|
||||
}
|
||||
if (node < 0 && searchable)
|
||||
node = m_ns.SearchUpward(relative, ctx.ScopeNode);
|
||||
|
||||
if (node >= 0) {
|
||||
auto* nsNode = m_ns.GetNode(node);
|
||||
@@ -1430,7 +1649,13 @@ namespace Hal {
|
||||
return true;
|
||||
}
|
||||
|
||||
ExecContext subCtx{};
|
||||
ExecContext* sub = AcquireContext();
|
||||
if (!sub) {
|
||||
result.Type = ObjectType::Integer;
|
||||
result.Integer = 0;
|
||||
return true;
|
||||
}
|
||||
ExecContext& subCtx = *sub;
|
||||
subCtx.Aml = ctx.Aml;
|
||||
subCtx.AmlBase = nsNode->Obj.Method.AmlOffset;
|
||||
subCtx.AmlLength = nsNode->Obj.Method.AmlLength;
|
||||
@@ -1438,6 +1663,7 @@ namespace Hal {
|
||||
subCtx.Returned = false;
|
||||
subCtx.Broken = false;
|
||||
subCtx.Depth = ctx.Depth + 1;
|
||||
subCtx.ExprDepth = ctx.ExprDepth;
|
||||
for (int i = 0; i < argc; i++)
|
||||
subCtx.Args[i] = args[i];
|
||||
for (int i = 0; i < MaxMethodLocals; i++) {
|
||||
@@ -1451,6 +1677,7 @@ namespace Hal {
|
||||
result.Type = ObjectType::Integer;
|
||||
result.Integer = 0;
|
||||
}
|
||||
ReleaseContext();
|
||||
} else if (nsNode->Obj.Type == ObjectType::Field) {
|
||||
uint64_t val = 0;
|
||||
ReadField(node, val);
|
||||
@@ -1483,12 +1710,25 @@ namespace Hal {
|
||||
// ============================================================================
|
||||
bool Interpreter::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) {
|
||||
nodeIndex = -1;
|
||||
char stackMarker = 0;
|
||||
if (ctx.ExprDepth >= MaxExprDepth) ReportDepthLimit();
|
||||
if (ctx.ExprDepth >= MaxExprDepth || !HaveStackHeadroom(&stackMarker)) {
|
||||
isLocal = false; isArg = false;
|
||||
localIdx = 0; argIdx = 0;
|
||||
pkgStore = -1; pkgElement = 0;
|
||||
return false;
|
||||
}
|
||||
DepthGuard depthGuard(ctx.ExprDepth);
|
||||
|
||||
isLocal = false;
|
||||
isArg = false;
|
||||
localIdx = 0;
|
||||
argIdx = 0;
|
||||
pkgStore = -1;
|
||||
pkgElement = 0;
|
||||
|
||||
if (*pos >= ctx.AmlBase + ctx.AmlLength) return false;
|
||||
|
||||
@@ -1520,10 +1760,73 @@ namespace Hal {
|
||||
if (IsLeadNameChar(op) || op == '\\' || op == '^' ||
|
||||
op == DualNamePrefix || op == MultiNamePrefix) {
|
||||
char path[256];
|
||||
ReadNameString(ctx.Aml, pos, ctx.ScopeNode, path, 256);
|
||||
char relative[64];
|
||||
bool searchable = false;
|
||||
ReadNameString(ctx.Aml, pos, ctx.ScopeNode, path, 256,
|
||||
relative, sizeof(relative), &searchable);
|
||||
nodeIndex = m_ns.FindNode(path);
|
||||
if (nodeIndex < 0)
|
||||
nodeIndex = m_ns.ResolveName(path, ctx.ScopeNode);
|
||||
if (nodeIndex < 0 && searchable)
|
||||
nodeIndex = m_ns.SearchUpward(relative, ctx.ScopeNode);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Index(source, n) names a package element. This is how firmware
|
||||
// fills a result package -- Store(x, Index(BST0, 1)) -- so the
|
||||
// element has to be addressable, not just readable.
|
||||
if (op == IndexOp) {
|
||||
(*pos)++;
|
||||
|
||||
// Resolve the source by name where we can, so the write lands
|
||||
// in the namespace object rather than in a copy of it.
|
||||
int32_t srcNode = -1;
|
||||
uint8_t srcOp = (*pos < ctx.AmlBase + ctx.AmlLength) ? ctx.Aml[*pos] : 0;
|
||||
if (IsLeadNameChar(srcOp) || srcOp == '\\' || srcOp == '^' ||
|
||||
srcOp == DualNamePrefix || srcOp == MultiNamePrefix) {
|
||||
char path[256];
|
||||
char relative[64];
|
||||
bool searchable = false;
|
||||
ReadNameString(ctx.Aml, pos, ctx.ScopeNode, path, 256,
|
||||
relative, sizeof(relative), &searchable);
|
||||
srcNode = m_ns.FindNode(path);
|
||||
if (srcNode < 0 && searchable)
|
||||
srcNode = m_ns.SearchUpward(relative, ctx.ScopeNode);
|
||||
} else {
|
||||
// Locals and arguments can hold a package too.
|
||||
Object src{};
|
||||
EvalTerm(ctx, pos, ctx.AmlBase + ctx.AmlLength, src);
|
||||
if (src.Type == ObjectType::Package && src.PkgIndex > 0)
|
||||
pkgStore = src.PkgIndex;
|
||||
}
|
||||
|
||||
Object idx{};
|
||||
EvalTerm(ctx, pos, ctx.AmlBase + ctx.AmlLength, idx);
|
||||
pkgElement = (int)idx.Integer;
|
||||
|
||||
if (srcNode >= 0) {
|
||||
auto* srcObj = m_ns.GetNode(srcNode);
|
||||
if (srcObj && srcObj->Obj.Type == ObjectType::Package)
|
||||
pkgStore = MaterializeNodePackage(srcNode);
|
||||
}
|
||||
|
||||
// Index() always carries a target operand of its own -- ZeroOp
|
||||
// when there is none. It has to be consumed either way, or the
|
||||
// rest of the method decodes off by however long it was.
|
||||
if (*pos < ctx.AmlBase + ctx.AmlLength) {
|
||||
int32_t innerNode = -1;
|
||||
bool innerLocal = false, innerArg = false;
|
||||
int innerLocalIdx = 0, innerArgIdx = 0;
|
||||
int32_t innerStore = -1; int innerElement = 0;
|
||||
EvalTarget(ctx, pos, innerNode, innerLocal, innerLocalIdx,
|
||||
innerArg, innerArgIdx, innerStore, innerElement);
|
||||
}
|
||||
|
||||
if (pkgStore <= 0) {
|
||||
// Not a package we can write through (a buffer, or an
|
||||
// unresolvable source): discard the store rather than
|
||||
// corrupting an unrelated object.
|
||||
pkgStore = -1;
|
||||
nodeIndex = -1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1539,7 +1842,14 @@ namespace Hal {
|
||||
// ============================================================================
|
||||
void Interpreter::StoreToTarget(ExecContext& ctx, const Object& value,
|
||||
int32_t nodeIndex, bool isLocal, int localIdx,
|
||||
bool isArg, int argIdx) {
|
||||
bool isArg, int argIdx,
|
||||
int32_t pkgStore, int pkgElement) {
|
||||
if (pkgStore > 0) {
|
||||
Object* slot = PackageStore::Element(pkgStore, pkgElement);
|
||||
if (slot) *slot = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLocal && localIdx >= 0 && localIdx < MaxMethodLocals) {
|
||||
ctx.Locals[localIdx] = value;
|
||||
} else if (isArg && argIdx >= 0 && argIdx < MaxMethodArgs) {
|
||||
@@ -1558,6 +1868,171 @@ namespace Hal {
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// Package elements
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::DecodeConstElement(const uint8_t* data, uint32_t length,
|
||||
uint32_t* pos, Object& out) {
|
||||
out = Object{};
|
||||
if (*pos >= length) return false;
|
||||
|
||||
uint8_t op = data[(*pos)++];
|
||||
|
||||
switch (op) {
|
||||
case ZeroOp:
|
||||
out.Type = ObjectType::Integer;
|
||||
out.Integer = 0;
|
||||
return true;
|
||||
case OneOp:
|
||||
out.Type = ObjectType::Integer;
|
||||
out.Integer = 1;
|
||||
return true;
|
||||
case OnesOp:
|
||||
out.Type = ObjectType::Integer;
|
||||
out.Integer = ~0ULL;
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Fixed-width integer prefixes
|
||||
int width = 0;
|
||||
if (op == BytePrefix) width = 1;
|
||||
else if (op == WordPrefix) width = 2;
|
||||
else if (op == DWordPrefix) width = 4;
|
||||
else if (op == QWordPrefix) width = 8;
|
||||
|
||||
if (width > 0) {
|
||||
if (*pos + (uint32_t)width > length) return false;
|
||||
uint64_t value = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
value |= (uint64_t)data[*pos + i] << (i * 8);
|
||||
*pos += (uint32_t)width;
|
||||
out.Type = ObjectType::Integer;
|
||||
out.Integer = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (op == StringPrefix) {
|
||||
out.Type = ObjectType::String;
|
||||
uint16_t n = 0;
|
||||
while (*pos < length && data[*pos] != 0) {
|
||||
if (n < MaxStringLen - 1)
|
||||
out.String.Data[n++] = (char)data[*pos];
|
||||
(*pos)++;
|
||||
}
|
||||
if (*pos < length) (*pos)++; // trailing NUL
|
||||
out.String.Data[n] = '\0';
|
||||
out.String.Length = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (op == BufferOp || op == PackageOp || op == VarPackageOp) {
|
||||
uint32_t start = *pos;
|
||||
uint32_t pkgLen = DecodePkgLength(data, pos);
|
||||
uint32_t end = start + pkgLen;
|
||||
if (end > length) end = length;
|
||||
if (end < *pos) return false;
|
||||
|
||||
uint32_t dataLen = end - *pos;
|
||||
if (dataLen > MaxBufferLen) dataLen = MaxBufferLen;
|
||||
|
||||
out.Type = (op == BufferOp) ? ObjectType::Buffer : ObjectType::Package;
|
||||
out.Buffer.Length = dataLen;
|
||||
memcpy(out.Buffer.Data, &data[*pos], dataLen);
|
||||
*pos = end;
|
||||
return true;
|
||||
}
|
||||
|
||||
// A name reference, local, or anything else we cannot resolve from
|
||||
// the bytes alone. Skip it and hand back zero.
|
||||
if (IsLeadNameChar(op) || op == '\\' || op == '^' ||
|
||||
op == DualNamePrefix || op == MultiNamePrefix) {
|
||||
(*pos)--;
|
||||
uint32_t p = *pos;
|
||||
// Walk the NameString by hand: prefixes, then 4-char segments.
|
||||
while (p < length && (data[p] == '\\' || data[p] == '^')) p++;
|
||||
if (p < length && data[p] == DualNamePrefix) {
|
||||
p += 1 + 8;
|
||||
} else if (p < length && data[p] == MultiNamePrefix) {
|
||||
uint8_t segs = (p + 1 < length) ? data[p + 1] : 0;
|
||||
p += 2 + (uint32_t)segs * 4;
|
||||
} else if (p < length && data[p] == 0x00) {
|
||||
p += 1; // NullName
|
||||
} else {
|
||||
p += 4;
|
||||
}
|
||||
*pos = (p > length) ? length : p;
|
||||
}
|
||||
|
||||
out.Type = ObjectType::Integer;
|
||||
out.Integer = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int Interpreter::GetPackageCount(const Object& package) {
|
||||
if (package.Type != ObjectType::Package) return 0;
|
||||
if (package.PkgIndex > 0)
|
||||
return PackageStore::Count(package.PkgIndex);
|
||||
// Raw form: the first byte of the retained AML is NumElements.
|
||||
if (package.Buffer.Length < 1) return 0;
|
||||
return (int)package.Buffer.Data[0];
|
||||
}
|
||||
|
||||
bool Interpreter::GetPackageElement(const Object& package, int index, Object& result) {
|
||||
result = Object{};
|
||||
if (package.Type != ObjectType::Package || index < 0) return false;
|
||||
|
||||
if (package.PkgIndex > 0) {
|
||||
Object* elem = PackageStore::Element(package.PkgIndex, index);
|
||||
if (!elem) return false;
|
||||
result = *elem;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t length = package.Buffer.Length;
|
||||
if (length < 1) return false;
|
||||
if (index >= (int)package.Buffer.Data[0]) return false;
|
||||
|
||||
uint32_t pos = 1; // past NumElements
|
||||
for (int i = 0; i <= index; i++) {
|
||||
if (!DecodeConstElement(package.Buffer.Data, length, &pos, result))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t Interpreter::MaterializeNodePackage(int32_t nodeIndex) {
|
||||
auto* node = m_ns.GetNode(nodeIndex);
|
||||
if (!node || node->Obj.Type != ObjectType::Package) return -1;
|
||||
if (node->Obj.PkgIndex > 0) return node->Obj.PkgIndex;
|
||||
|
||||
int count = GetPackageCount(node->Obj);
|
||||
if (count <= 0) return -1;
|
||||
if (count > MaxPackageElements) count = MaxPackageElements;
|
||||
|
||||
int32_t store = PackageStore::AllocPersistent(count);
|
||||
if (store < 0) return -1;
|
||||
|
||||
// Seed the elements from the constant bytes the package was
|
||||
// declared with, so a package that is only partly written through
|
||||
// Index() keeps its declared values everywhere else.
|
||||
uint32_t pos = 1;
|
||||
for (int i = 0; i < count; i++) {
|
||||
Object elem{};
|
||||
if (!DecodeConstElement(node->Obj.Buffer.Data, node->Obj.Buffer.Length,
|
||||
&pos, elem))
|
||||
break;
|
||||
Object* slot = PackageStore::Element(store, i);
|
||||
if (slot) *slot = elem;
|
||||
}
|
||||
|
||||
node->Obj.PkgIndex = store;
|
||||
return store;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// ReadField
|
||||
|
||||
// ============================================================================
|
||||
@@ -1578,7 +2053,19 @@ namespace Hal {
|
||||
uint64_t byteAddr = regBase + (bitOff / 8);
|
||||
uint32_t bitShift = bitOff % 8;
|
||||
|
||||
return ReadRegion(regionNode->Obj.Region.Space, byteAddr, bitLen + bitShift, value);
|
||||
uint64_t raw = 0;
|
||||
if (!ReadRegion(regionNode->Obj.Region.Space, byteAddr, bitLen + bitShift, raw))
|
||||
return false;
|
||||
|
||||
// ReadRegion hands back whole access units starting at byteAddr;
|
||||
// narrow that to the field's own bits so sub-byte flags (common in
|
||||
// EC and PM register layouts) don't carry their neighbours along.
|
||||
raw >>= bitShift;
|
||||
if (bitLen < 64)
|
||||
raw &= (1ULL << bitLen) - 1;
|
||||
|
||||
value = raw;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -1639,6 +2126,13 @@ namespace Hal {
|
||||
}
|
||||
|
||||
if (space == RegionSpace::SystemMemory) {
|
||||
// The offset comes from firmware AML and may be nonsense: a
|
||||
// region we mis-decoded, or one whose base is computed by code
|
||||
// we do not fully execute. Dereferencing that through the HHDM
|
||||
// is how a bad table takes the kernel down with a #GP, so the
|
||||
// address has to be inside what Init() actually mapped.
|
||||
if (!HhdmReadable(address, accessBytes)) return false;
|
||||
|
||||
volatile uint8_t* mmio = (volatile uint8_t*)Memory::HHDM(address);
|
||||
if (accessBytes <= 1)
|
||||
value = *mmio;
|
||||
@@ -1651,6 +2145,19 @@ namespace Hal {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (space == RegionSpace::EmbeddedControl) {
|
||||
// The EC address space is a flat 256-byte array reached one
|
||||
// byte at a time over the command/data port pair, so a wider
|
||||
// field is assembled little-endian from consecutive reads.
|
||||
for (uint32_t i = 0; i < accessBytes; i++) {
|
||||
uint8_t byte = 0;
|
||||
if (!EmbeddedController::Read((uint8_t)(address + i), byte))
|
||||
return false;
|
||||
value |= (uint64_t)byte << (i * 8);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (space == RegionSpace::PciConfig) {
|
||||
// PCI config space access -- not yet implemented
|
||||
return false;
|
||||
@@ -1681,6 +2188,8 @@ namespace Hal {
|
||||
}
|
||||
|
||||
if (space == RegionSpace::SystemMemory) {
|
||||
if (!HhdmReadable(address, accessBytes)) return false;
|
||||
|
||||
volatile uint8_t* mmio = (volatile uint8_t*)Memory::HHDM(address);
|
||||
if (accessBytes <= 1)
|
||||
*mmio = (uint8_t)value;
|
||||
@@ -1693,6 +2202,15 @@ namespace Hal {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (space == RegionSpace::EmbeddedControl) {
|
||||
for (uint32_t i = 0; i < accessBytes; i++) {
|
||||
if (!EmbeddedController::Write((uint8_t)(address + i),
|
||||
(uint8_t)(value >> (i * 8))))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user