feat: expanded ACPI support, initial support for S3 sleep
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* AmlInterpreter.hpp
|
||||
* AML bytecode interpreter — parses DSDT/SSDT into the ACPI namespace
|
||||
* and evaluates methods, fields, and device status
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "AmlNamespace.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
// ── Extended AML Opcodes ────────────────────────────────────────
|
||||
// Single-byte opcodes
|
||||
static constexpr uint8_t ZeroOp = 0x00;
|
||||
static constexpr uint8_t OneOp = 0x01;
|
||||
static constexpr uint8_t AliasOp = 0x06;
|
||||
static constexpr uint8_t NameOp = 0x08;
|
||||
static constexpr uint8_t BytePrefix = 0x0A;
|
||||
static constexpr uint8_t WordPrefix = 0x0B;
|
||||
static constexpr uint8_t DWordPrefix = 0x0C;
|
||||
static constexpr uint8_t StringPrefix = 0x0D;
|
||||
static constexpr uint8_t QWordPrefix = 0x0E;
|
||||
static constexpr uint8_t ScopeOp = 0x10;
|
||||
static constexpr uint8_t BufferOp = 0x11;
|
||||
static constexpr uint8_t PackageOp = 0x12;
|
||||
static constexpr uint8_t VarPackageOp = 0x13;
|
||||
static constexpr uint8_t MethodOp = 0x14;
|
||||
static constexpr uint8_t DualNamePrefix = 0x2E;
|
||||
static constexpr uint8_t MultiNamePrefix = 0x2F;
|
||||
static constexpr uint8_t LocalPrefix = 0x60; // Local0..Local7 = 0x60..0x67
|
||||
static constexpr uint8_t ArgPrefix = 0x68; // Arg0..Arg6 = 0x68..0x6E
|
||||
static constexpr uint8_t StoreOp = 0x70;
|
||||
static constexpr uint8_t AddOp = 0x72;
|
||||
static constexpr uint8_t SubtractOp = 0x74;
|
||||
static constexpr uint8_t MultiplyOp = 0x77;
|
||||
static constexpr uint8_t ShiftLeftOp = 0x79;
|
||||
static constexpr uint8_t ShiftRightOp = 0x7A;
|
||||
static constexpr uint8_t AndOp = 0x7B;
|
||||
static constexpr uint8_t NandOp = 0x7C;
|
||||
static constexpr uint8_t OrOp = 0x7D;
|
||||
static constexpr uint8_t NorOp = 0x7E;
|
||||
static constexpr uint8_t XorOp = 0x7F;
|
||||
static constexpr uint8_t NotOp = 0x80;
|
||||
static constexpr uint8_t DerefOfOp = 0x83;
|
||||
static constexpr uint8_t SizeOfOp = 0x87;
|
||||
static constexpr uint8_t IndexOp = 0x88;
|
||||
static constexpr uint8_t CreateDWordFieldOp = 0x8A;
|
||||
static constexpr uint8_t CreateWordFieldOp = 0x8B;
|
||||
static constexpr uint8_t CreateByteFieldOp = 0x8C;
|
||||
static constexpr uint8_t CreateBitFieldOp = 0x8D;
|
||||
static constexpr uint8_t OnesOp = 0xFF;
|
||||
static constexpr uint8_t ReturnOp = 0xA4;
|
||||
static constexpr uint8_t BreakOp = 0xA5;
|
||||
static constexpr uint8_t IfOp = 0xA0;
|
||||
static constexpr uint8_t ElseOp = 0xA1;
|
||||
static constexpr uint8_t WhileOp = 0xA2;
|
||||
static constexpr uint8_t NoopOp = 0xA3;
|
||||
static constexpr uint8_t ConcatOp = 0x73;
|
||||
static constexpr uint8_t ToIntegerOp = 0x99;
|
||||
static constexpr uint8_t ToBufferOp = 0x96;
|
||||
static constexpr uint8_t RevisionOp = 0x30; // not a real AML op, used internally
|
||||
|
||||
// ExtOp prefix (0x5B) followed by second byte
|
||||
static constexpr uint8_t ExtOpPrefix = 0x5B;
|
||||
static constexpr uint8_t MutexOp = 0x01; // after ExtOpPrefix
|
||||
static constexpr uint8_t EventOp = 0x02;
|
||||
static constexpr uint8_t OpRegionOp = 0x80;
|
||||
static constexpr uint8_t FieldOp = 0x81;
|
||||
static constexpr uint8_t DeviceOp = 0x82;
|
||||
static constexpr uint8_t ProcessorOp = 0x83;
|
||||
static constexpr uint8_t PowerResOp = 0x84;
|
||||
static constexpr uint8_t ThermalZoneOp = 0x85;
|
||||
static constexpr uint8_t IndexFieldOp = 0x86;
|
||||
static constexpr uint8_t BankFieldOp = 0x87;
|
||||
static constexpr uint8_t AcquireOp = 0x23;
|
||||
static constexpr uint8_t ReleaseOp = 0x27;
|
||||
static constexpr uint8_t SleepOp = 0x22;
|
||||
static constexpr uint8_t StallOp = 0x21;
|
||||
static constexpr uint8_t LNotOp = 0x92; // single-byte, actually
|
||||
static constexpr uint8_t LEqualOp = 0x93; // single-byte
|
||||
static constexpr uint8_t LGreaterOp = 0x94; // single-byte
|
||||
static constexpr uint8_t LLessOp = 0x95; // single-byte
|
||||
static constexpr uint8_t LAndOp = 0x90; // single-byte
|
||||
static constexpr uint8_t LOrOp = 0x91; // single-byte
|
||||
static constexpr uint8_t IncrementOp = 0x75;
|
||||
static constexpr uint8_t DecrementOp = 0x76;
|
||||
static constexpr uint8_t DivideOp = 0x78;
|
||||
static constexpr uint8_t ModOp = 0x85;
|
||||
static constexpr uint8_t ConcatResOp = 0x84;
|
||||
static constexpr uint8_t ToHexStringOp = 0x98;
|
||||
static constexpr uint8_t ToDecimalStringOp = 0x97;
|
||||
|
||||
// ── Interpreter Configuration ───────────────────────────────────
|
||||
static constexpr int MaxCallDepth = 16;
|
||||
static constexpr int MaxLoopIterations = 1024;
|
||||
|
||||
// ── Interpreter ─────────────────────────────────────────────────
|
||||
class Interpreter {
|
||||
public:
|
||||
Interpreter();
|
||||
|
||||
// Parse a DSDT or SSDT table into the namespace.
|
||||
// tableData points to the CommonSDTHeader (HHDM-mapped).
|
||||
bool LoadTable(void* tableData);
|
||||
|
||||
// Evaluate a named object, returning its value.
|
||||
// For methods, executes them with no arguments.
|
||||
bool EvaluateObject(const char* path, Object& result);
|
||||
|
||||
// Evaluate a method with arguments.
|
||||
bool EvaluateMethod(const char* path, const Object* args, int argCount, Object& result);
|
||||
|
||||
// Read a field value. Returns integer.
|
||||
bool ReadField(int32_t nodeIndex, uint64_t& value);
|
||||
|
||||
// Write a field value.
|
||||
bool WriteField(int32_t nodeIndex, uint64_t value);
|
||||
|
||||
// Get the namespace for direct queries.
|
||||
Namespace& GetNamespace() { return m_ns; }
|
||||
const Namespace& GetNamespace() const { return m_ns; }
|
||||
|
||||
// Check if the interpreter has been initialized
|
||||
bool IsInitialized() const { return m_initialized; }
|
||||
|
||||
private:
|
||||
// ── Parsing (table load) ────────────────────────────────────
|
||||
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);
|
||||
|
||||
bool ParseExtendedOp(const uint8_t* aml, uint32_t* pos, uint32_t endOffset,
|
||||
int32_t scopeNode);
|
||||
|
||||
// ── Name resolution ─────────────────────────────────────────
|
||||
// Read a NameString from AML and produce an absolute path.
|
||||
// Advances *pos past the name.
|
||||
int ReadNameString(const uint8_t* aml, uint32_t* pos, int32_t scopeNode,
|
||||
char* outPath, int maxLen);
|
||||
|
||||
// Read a single 4-char NameSeg from AML. Advances *pos.
|
||||
void ReadNameSeg(const uint8_t* aml, uint32_t* pos, char* outSeg);
|
||||
|
||||
// ── Value decoding ──────────────────────────────────────────
|
||||
uint32_t DecodePkgLength(const uint8_t* aml, uint32_t* pos);
|
||||
uint64_t DecodeInteger(const uint8_t* aml, uint32_t* pos);
|
||||
|
||||
// ── Method execution ────────────────────────────────────────
|
||||
struct ExecContext {
|
||||
const uint8_t* Aml;
|
||||
uint32_t AmlBase; // start of the block within the table
|
||||
uint32_t AmlLength;
|
||||
int32_t ScopeNode;
|
||||
Object Locals[MaxMethodLocals];
|
||||
Object Args[MaxMethodArgs];
|
||||
Object ReturnValue;
|
||||
bool Returned;
|
||||
bool Broken;
|
||||
int Depth;
|
||||
};
|
||||
|
||||
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.
|
||||
bool EvalTarget(ExecContext& ctx, uint32_t* pos,
|
||||
int32_t& nodeIndex, bool& isLocal, int& localIdx,
|
||||
bool& isArg, int& argIdx);
|
||||
|
||||
// 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);
|
||||
|
||||
// ── 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);
|
||||
|
||||
// ── State ───────────────────────────────────────────────────
|
||||
Namespace m_ns;
|
||||
const uint8_t* m_dsdt;
|
||||
uint32_t m_dsdtLength;
|
||||
bool m_initialized;
|
||||
};
|
||||
|
||||
// ── Global interpreter instance ─────────────────────────────────
|
||||
Interpreter& GetInterpreter();
|
||||
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* AmlNamespace.cpp
|
||||
* ACPI namespace tree implementation
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "AmlNamespace.hpp"
|
||||
#include <Libraries/Memory.hpp>
|
||||
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
Namespace::Namespace() : m_nodeCount(0) {
|
||||
for (int i = 0; i < MaxNamespaceNodes; i++)
|
||||
m_nodes[i].Clear();
|
||||
|
||||
// Create root node "\"
|
||||
int32_t root = AllocNode();
|
||||
m_nodes[root].Name[0] = '\\';
|
||||
m_nodes[root].Name[1] = '\0';
|
||||
m_nodes[root].ParentIndex = -1;
|
||||
}
|
||||
|
||||
int32_t Namespace::AllocNode() {
|
||||
if (m_nodeCount >= MaxNamespaceNodes)
|
||||
return -1;
|
||||
int32_t idx = m_nodeCount++;
|
||||
m_nodes[idx].Clear();
|
||||
return idx;
|
||||
}
|
||||
|
||||
bool Namespace::SegmentEqual(const char* a, const char* b) {
|
||||
for (int i = 0; i < MaxNameSegLen; i++) {
|
||||
char ca = a[i] ? a[i] : '_';
|
||||
char cb = b[i] ? b[i] : '_';
|
||||
if (ca != cb) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Namespace::PadSegment(const char* src, char* dst) {
|
||||
int i = 0;
|
||||
while (i < MaxNameSegLen && src[i] != '\0') {
|
||||
dst[i] = src[i];
|
||||
i++;
|
||||
}
|
||||
while (i < MaxNameSegLen) {
|
||||
dst[i] = '_';
|
||||
i++;
|
||||
}
|
||||
dst[MaxNameSegLen] = '\0';
|
||||
}
|
||||
|
||||
int Namespace::ParsePath(const char* path, char segments[][MaxNameSegLen + 1], int maxSegments) {
|
||||
if (!path || !*path) return 0;
|
||||
|
||||
const char* p = path;
|
||||
|
||||
// Skip leading backslash (root prefix)
|
||||
if (*p == '\\') p++;
|
||||
|
||||
int count = 0;
|
||||
while (*p && count < maxSegments) {
|
||||
// Skip dots (parent prefix / dual/multi name prefix separator)
|
||||
if (*p == '.') { p++; continue; }
|
||||
// Skip caret (parent prefix) — we don't handle relative paths here
|
||||
if (*p == '^') { p++; continue; }
|
||||
|
||||
// Read up to 4 characters for a name segment
|
||||
int i = 0;
|
||||
while (i < MaxNameSegLen && *p && *p != '.' && *p != '\\') {
|
||||
segments[count][i] = *p;
|
||||
i++;
|
||||
p++;
|
||||
}
|
||||
// Pad with underscores
|
||||
while (i < MaxNameSegLen) {
|
||||
segments[count][i] = '_';
|
||||
i++;
|
||||
}
|
||||
segments[count][MaxNameSegLen] = '\0';
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int32_t Namespace::FindChildByName(int32_t parentIndex, const char* seg) const {
|
||||
auto* parent = GetNode(parentIndex);
|
||||
if (!parent) return -1;
|
||||
|
||||
for (int32_t i = 0; i < parent->ChildCount; i++) {
|
||||
int32_t ci = parent->ChildIndices[i];
|
||||
if (ci < 0 || ci >= m_nodeCount) continue;
|
||||
if (SegmentEqual(m_nodes[ci].Name, seg))
|
||||
return ci;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t Namespace::CreateNode(const char* absolutePath) {
|
||||
char segments[MaxPathDepth][MaxNameSegLen + 1];
|
||||
int segCount = ParsePath(absolutePath, segments, MaxPathDepth);
|
||||
|
||||
int32_t current = 0; // root
|
||||
for (int i = 0; i < segCount; i++) {
|
||||
int32_t child = FindChildByName(current, segments[i]);
|
||||
if (child < 0) {
|
||||
// Create the node
|
||||
child = AllocNode();
|
||||
if (child < 0) return -1;
|
||||
|
||||
memcpy(m_nodes[child].Name, segments[i], MaxNameSegLen + 1);
|
||||
m_nodes[child].ParentIndex = current;
|
||||
|
||||
// Add to parent's children
|
||||
auto* parent = &m_nodes[current];
|
||||
if (parent->ChildCount < MaxChildren) {
|
||||
parent->ChildIndices[parent->ChildCount++] = child;
|
||||
} else {
|
||||
return -1; // too many children
|
||||
}
|
||||
}
|
||||
current = child;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
int32_t Namespace::FindNode(const char* absolutePath) const {
|
||||
char segments[MaxPathDepth][MaxNameSegLen + 1];
|
||||
int segCount = ParsePath(absolutePath, segments, MaxPathDepth);
|
||||
|
||||
int32_t current = 0; // root
|
||||
for (int i = 0; i < segCount; i++) {
|
||||
current = FindChildByName(current, segments[i]);
|
||||
if (current < 0) return -1;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
int32_t Namespace::ResolveName(const char* name, int32_t scopeNodeIndex) const {
|
||||
// If it starts with '\', it's absolute
|
||||
if (name[0] == '\\')
|
||||
return FindNode(name);
|
||||
|
||||
// Try to find relative to the current scope, walking up
|
||||
char padded[MaxNameSegLen + 1];
|
||||
PadSegment(name, padded);
|
||||
|
||||
int32_t scope = scopeNodeIndex;
|
||||
while (scope >= 0) {
|
||||
int32_t found = FindChildByName(scope, padded);
|
||||
if (found >= 0) return found;
|
||||
scope = m_nodes[scope].ParentIndex;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
NamespaceNode* Namespace::GetNode(int32_t index) {
|
||||
if (index < 0 || index >= m_nodeCount) return nullptr;
|
||||
return &m_nodes[index];
|
||||
}
|
||||
|
||||
const NamespaceNode* Namespace::GetNode(int32_t index) const {
|
||||
if (index < 0 || index >= m_nodeCount) return nullptr;
|
||||
return &m_nodes[index];
|
||||
}
|
||||
|
||||
char* Namespace::GetNodePath(int32_t index, char* outBuf, int maxLen) const {
|
||||
if (!outBuf || maxLen < 2) return outBuf;
|
||||
|
||||
// Build path by walking up to root
|
||||
// Collect segments in reverse
|
||||
char segments[MaxPathDepth][MaxNameSegLen + 1];
|
||||
int segCount = 0;
|
||||
|
||||
int32_t cur = index;
|
||||
while (cur > 0 && segCount < MaxPathDepth) { // stop at root (index 0)
|
||||
auto* node = GetNode(cur);
|
||||
if (!node) break;
|
||||
memcpy(segments[segCount], node->Name, MaxNameSegLen + 1);
|
||||
segCount++;
|
||||
cur = node->ParentIndex;
|
||||
}
|
||||
|
||||
// Write root prefix
|
||||
int pos = 0;
|
||||
outBuf[pos++] = '\\';
|
||||
|
||||
// Write segments in reverse order
|
||||
for (int i = segCount - 1; i >= 0 && pos < maxLen - 1; i--) {
|
||||
if (i < segCount - 1 && pos < maxLen - 1)
|
||||
outBuf[pos++] = '.';
|
||||
for (int j = 0; j < MaxNameSegLen && pos < maxLen - 1; j++)
|
||||
outBuf[pos++] = segments[i][j];
|
||||
}
|
||||
|
||||
outBuf[pos] = '\0';
|
||||
return outBuf;
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* AmlNamespace.hpp
|
||||
* AML object types and ACPI namespace tree
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <Libraries/Memory.hpp>
|
||||
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
// ── AML Object Types ────────────────────────────────────────────
|
||||
enum class ObjectType : uint8_t {
|
||||
None = 0,
|
||||
Integer,
|
||||
String,
|
||||
Buffer,
|
||||
Package,
|
||||
Device,
|
||||
Method,
|
||||
OperationRegion,
|
||||
Field,
|
||||
Mutex,
|
||||
Processor,
|
||||
ThermalZone,
|
||||
PowerResource,
|
||||
BufferField,
|
||||
};
|
||||
|
||||
// ── Region address spaces (OperationRegion) ─────────────────────
|
||||
enum class RegionSpace : uint8_t {
|
||||
SystemMemory = 0x00,
|
||||
SystemIO = 0x01,
|
||||
PciConfig = 0x02,
|
||||
EmbeddedControl = 0x03,
|
||||
SMBus = 0x04,
|
||||
CMOS = 0x05,
|
||||
PciBarTarget = 0x06,
|
||||
};
|
||||
|
||||
// ── Constants ───────────────────────────────────────────────────
|
||||
static constexpr int MaxNameSegLen = 4;
|
||||
static constexpr int MaxPathDepth = 16;
|
||||
static constexpr int MaxChildren = 32;
|
||||
static constexpr int MaxStringLen = 64;
|
||||
static constexpr int MaxBufferLen = 256;
|
||||
static constexpr int MaxPackageElements = 16;
|
||||
static constexpr int MaxMethodArgs = 7;
|
||||
static constexpr int MaxMethodLocals = 8;
|
||||
static constexpr int MaxNamespaceNodes = 256;
|
||||
|
||||
// ── AML Object ──────────────────────────────────────────────────
|
||||
// Tagged union representing any AML value. Kept small for kernel use.
|
||||
struct Object {
|
||||
ObjectType Type = ObjectType::None;
|
||||
|
||||
union {
|
||||
uint64_t Integer;
|
||||
|
||||
struct {
|
||||
char Data[MaxStringLen];
|
||||
uint16_t Length;
|
||||
} String;
|
||||
|
||||
struct {
|
||||
uint8_t Data[MaxBufferLen];
|
||||
uint32_t Length;
|
||||
} Buffer;
|
||||
|
||||
struct {
|
||||
uint8_t ArgCount; // bits 0-2 of method flags
|
||||
bool Serialized; // bit 3
|
||||
uint32_t AmlOffset; // offset into DSDT AML where the method body starts
|
||||
uint32_t AmlLength; // length of the method body
|
||||
} Method;
|
||||
|
||||
struct {
|
||||
RegionSpace Space;
|
||||
uint64_t Offset;
|
||||
uint64_t Length;
|
||||
} Region;
|
||||
|
||||
struct {
|
||||
uint32_t RegionNodeIndex; // index of the parent OperationRegion node
|
||||
uint32_t BitOffset;
|
||||
uint32_t BitLength;
|
||||
uint8_t AccessType; // 0=Any, 1=Byte, 2=Word, 3=DWord, 4=QWord, 5=Buffer
|
||||
} Field;
|
||||
|
||||
struct {
|
||||
uint8_t ProcId;
|
||||
uint32_t PblkAddr;
|
||||
uint8_t PblkLen;
|
||||
} Processor;
|
||||
};
|
||||
|
||||
Object() : Type(ObjectType::None), Integer(0) {}
|
||||
};
|
||||
|
||||
// ── Namespace Node ──────────────────────────────────────────────
|
||||
// Each node has a 4-char name segment and an associated object.
|
||||
struct NamespaceNode {
|
||||
char Name[MaxNameSegLen + 1]; // null-terminated 4-char segment
|
||||
Object Obj;
|
||||
int32_t ParentIndex; // -1 for root
|
||||
int32_t ChildIndices[MaxChildren];
|
||||
int32_t ChildCount;
|
||||
|
||||
void Clear() {
|
||||
Name[0] = 0;
|
||||
Obj = Object{};
|
||||
ParentIndex = -1;
|
||||
ChildCount = 0;
|
||||
for (int i = 0; i < MaxChildren; i++)
|
||||
ChildIndices[i] = -1;
|
||||
}
|
||||
};
|
||||
|
||||
// ── Namespace ───────────────────────────────────────────────────
|
||||
// Flat array of nodes forming a tree via parent/child indices.
|
||||
class Namespace {
|
||||
public:
|
||||
Namespace();
|
||||
|
||||
// Create or find a node at the given absolute path (e.g. "\\_SB_.PCI0").
|
||||
// Returns the node index, or -1 on failure.
|
||||
int32_t CreateNode(const char* absolutePath);
|
||||
|
||||
// Find a node by absolute path. Returns index or -1.
|
||||
int32_t FindNode(const char* absolutePath) const;
|
||||
|
||||
// Find a node relative to a scope. Tries:
|
||||
// 1. scopePath + name
|
||||
// 2. Walk up parent scopes
|
||||
// 3. Root scope
|
||||
int32_t ResolveName(const char* name, int32_t scopeNodeIndex) const;
|
||||
|
||||
// Get a node by index.
|
||||
NamespaceNode* GetNode(int32_t index);
|
||||
const NamespaceNode* GetNode(int32_t index) const;
|
||||
|
||||
// Get the root node index (always 0).
|
||||
int32_t RootIndex() const { return 0; }
|
||||
|
||||
// Build the absolute path of a node into outBuf. Returns outBuf.
|
||||
char* GetNodePath(int32_t index, char* outBuf, int maxLen) const;
|
||||
|
||||
// Get the number of nodes in the namespace.
|
||||
int32_t NodeCount() const { return m_nodeCount; }
|
||||
|
||||
// Iterate children of a node matching a given object type.
|
||||
// callback returns true to continue, false to stop.
|
||||
// Returns the index of the node that stopped iteration, or -1.
|
||||
template<typename Fn>
|
||||
int32_t ForEachChild(int32_t parentIndex, ObjectType type, Fn callback) const {
|
||||
auto* parent = GetNode(parentIndex);
|
||||
if (!parent) return -1;
|
||||
for (int32_t i = 0; i < parent->ChildCount; i++) {
|
||||
int32_t ci = parent->ChildIndices[i];
|
||||
auto* child = GetNode(ci);
|
||||
if (!child) continue;
|
||||
if (type != ObjectType::None && child->Obj.Type != type) continue;
|
||||
if (!callback(ci, child)) return ci;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Recursively find all descendants of a given type.
|
||||
template<typename Fn>
|
||||
void WalkDescendants(int32_t nodeIndex, ObjectType type, Fn callback) const {
|
||||
auto* node = GetNode(nodeIndex);
|
||||
if (!node) return;
|
||||
for (int32_t i = 0; i < node->ChildCount; i++) {
|
||||
int32_t ci = node->ChildIndices[i];
|
||||
auto* child = GetNode(ci);
|
||||
if (!child) continue;
|
||||
if (type == ObjectType::None || child->Obj.Type == type)
|
||||
callback(ci, child);
|
||||
WalkDescendants(ci, type, callback);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t AllocNode();
|
||||
int32_t FindChildByName(int32_t parentIndex, const char* seg) const;
|
||||
|
||||
// Parse an absolute path into segments. Returns number of segments.
|
||||
static int ParsePath(const char* path, char segments[][MaxNameSegLen + 1], int maxSegments);
|
||||
static bool SegmentEqual(const char* a, const char* b);
|
||||
static void PadSegment(const char* src, char* dst); // pad to 4 chars with '_'
|
||||
|
||||
NamespaceNode m_nodes[MaxNamespaceNodes];
|
||||
int32_t m_nodeCount;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
@@ -1,10 +1,11 @@
|
||||
/*
|
||||
* AmlParser.cpp
|
||||
* Primitive AML bytecode parser for extracting ACPI sleep state values
|
||||
* AML bytecode parser — S5 extraction (brute-force) and interpreter init
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "AmlParser.hpp"
|
||||
#include "AmlInterpreter.hpp"
|
||||
#include <ACPI/ACPI.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
@@ -14,19 +15,28 @@ using namespace Kt;
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
// Decode a PkgLength field and return its value.
|
||||
// Advances *pos past the PkgLength bytes.
|
||||
// ── Legacy S5 extraction (brute-force scan) ─────────────────────
|
||||
// Kept for fast S5 extraction during early boot before the full
|
||||
// interpreter is loaded.
|
||||
|
||||
static constexpr uint8_t NameOp_ = 0x08;
|
||||
static constexpr uint8_t PackageOp_ = 0x12;
|
||||
static constexpr uint8_t ZeroOp_ = 0x00;
|
||||
static constexpr uint8_t OneOp_ = 0x01;
|
||||
static constexpr uint8_t OnesOp_ = 0xFF;
|
||||
static constexpr uint8_t BytePrefix_ = 0x0A;
|
||||
static constexpr uint8_t WordPrefix_ = 0x0B;
|
||||
static constexpr uint8_t DWordPrefix_= 0x0C;
|
||||
|
||||
static uint32_t DecodePkgLength(const uint8_t* aml, uint32_t* pos) {
|
||||
uint8_t lead = aml[*pos];
|
||||
uint32_t byteCount = (lead >> 6) & 0x03;
|
||||
|
||||
if (byteCount == 0) {
|
||||
// Single byte encoding: bits 0-5 are the length
|
||||
(*pos)++;
|
||||
return lead & 0x3F;
|
||||
}
|
||||
|
||||
// Multi-byte: lead bits 0-3 are low nibble, followed by byteCount bytes
|
||||
uint32_t length = lead & 0x0F;
|
||||
(*pos)++;
|
||||
|
||||
@@ -38,35 +48,32 @@ namespace Hal {
|
||||
return length;
|
||||
}
|
||||
|
||||
// Decode an AML integer at position *pos.
|
||||
// Handles ZeroOp, OneOp, OnesOp, BytePrefix, WordPrefix, DWordPrefix.
|
||||
// Returns the decoded value and advances *pos.
|
||||
static uint32_t DecodeInteger(const uint8_t* aml, uint32_t* pos) {
|
||||
static uint32_t DecodeIntegerLegacy(const uint8_t* aml, uint32_t* pos) {
|
||||
uint8_t op = aml[*pos];
|
||||
|
||||
switch (op) {
|
||||
case ZeroOp:
|
||||
case ZeroOp_:
|
||||
(*pos)++;
|
||||
return 0;
|
||||
case OneOp:
|
||||
case OneOp_:
|
||||
(*pos)++;
|
||||
return 1;
|
||||
case OnesOp:
|
||||
case OnesOp_:
|
||||
(*pos)++;
|
||||
return 0xFFFFFFFF;
|
||||
case BytePrefix: {
|
||||
case BytePrefix_: {
|
||||
(*pos)++;
|
||||
uint8_t val = aml[*pos];
|
||||
(*pos)++;
|
||||
return val;
|
||||
}
|
||||
case WordPrefix: {
|
||||
case WordPrefix_: {
|
||||
(*pos)++;
|
||||
uint16_t val = aml[*pos] | ((uint16_t)aml[*pos + 1] << 8);
|
||||
*pos += 2;
|
||||
return val;
|
||||
}
|
||||
case DWordPrefix: {
|
||||
case DWordPrefix_: {
|
||||
(*pos)++;
|
||||
uint32_t val = aml[*pos]
|
||||
| ((uint32_t)aml[*pos + 1] << 8)
|
||||
@@ -76,7 +83,6 @@ namespace Hal {
|
||||
return val;
|
||||
}
|
||||
default:
|
||||
// Unknown encoding — treat as zero and skip
|
||||
(*pos)++;
|
||||
return 0;
|
||||
}
|
||||
@@ -93,23 +99,17 @@ namespace Hal {
|
||||
return result;
|
||||
}
|
||||
|
||||
// The AML bytecode starts right after the CommonSDTHeader
|
||||
const uint8_t* aml = (const uint8_t*)dsdtData;
|
||||
uint32_t amlLength = header->Length;
|
||||
uint32_t dataStart = sizeof(ACPI::CommonSDTHeader);
|
||||
|
||||
// Scan for the \_S5_ name in the AML stream.
|
||||
// We look for the 4-byte sequence '_S5_' preceded by a NameOp (0x08)
|
||||
// or preceded by a scope path like '\' (0x5C).
|
||||
for (uint32_t i = dataStart; i + 4 < amlLength; i++) {
|
||||
if (aml[i] == '_' && aml[i+1] == 'S' && aml[i+2] == '5' && aml[i+3] == '_') {
|
||||
// Verify a valid AML context: either NameOp before it,
|
||||
// or '\' + NameOp pattern, or just the name in a scope
|
||||
bool validContext = false;
|
||||
|
||||
if (i >= 1 && aml[i-1] == NameOp) {
|
||||
if (i >= 1 && aml[i-1] == NameOp_) {
|
||||
validContext = true;
|
||||
} else if (i >= 2 && aml[i-2] == NameOp && aml[i-1] == '\\') {
|
||||
} else if (i >= 2 && aml[i-2] == NameOp_ && aml[i-1] == '\\') {
|
||||
validContext = true;
|
||||
}
|
||||
|
||||
@@ -118,21 +118,16 @@ namespace Hal {
|
||||
|
||||
KernelLogStream(OK, "AML") << "Found \\_S5_ object at offset " << base::hex << (uint64_t)i;
|
||||
|
||||
// Move past the name
|
||||
uint32_t pos = i + 4;
|
||||
|
||||
// Expect PackageOp
|
||||
if (pos >= amlLength || aml[pos] != PackageOp) {
|
||||
if (pos >= amlLength || aml[pos] != PackageOp_) {
|
||||
KernelLogStream(ERROR, "AML") << "Expected PackageOp after \\_S5_, got " << base::hex << (uint64_t)aml[pos];
|
||||
continue;
|
||||
}
|
||||
pos++;
|
||||
|
||||
// Decode package length (we don't actually need the value,
|
||||
// but must advance past it)
|
||||
DecodePkgLength(aml, &pos);
|
||||
|
||||
// Number of elements in the package
|
||||
if (pos >= amlLength) continue;
|
||||
uint8_t numElements = aml[pos];
|
||||
pos++;
|
||||
@@ -142,12 +137,10 @@ namespace Hal {
|
||||
continue;
|
||||
}
|
||||
|
||||
// First element: SLP_TYPa
|
||||
result.SLP_TYPa = (uint16_t)DecodeInteger(aml, &pos);
|
||||
result.SLP_TYPa = (uint16_t)DecodeIntegerLegacy(aml, &pos);
|
||||
|
||||
// Second element: SLP_TYPb (if present)
|
||||
if (numElements >= 2 && pos < amlLength) {
|
||||
result.SLP_TYPb = (uint16_t)DecodeInteger(aml, &pos);
|
||||
result.SLP_TYPb = (uint16_t)DecodeIntegerLegacy(aml, &pos);
|
||||
} else {
|
||||
result.SLP_TYPb = 0;
|
||||
}
|
||||
@@ -165,5 +158,81 @@ namespace Hal {
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── Generalized brute-force sleep state scanner ──────────────────
|
||||
SleepObject FindSleepState(void* dsdtData, int state) {
|
||||
SleepObject result{};
|
||||
result.Valid = false;
|
||||
|
||||
if (state < 0 || state > 5) return result;
|
||||
|
||||
auto* header = (ACPI::CommonSDTHeader*)dsdtData;
|
||||
|
||||
if (!ACPI::TestChecksum(header)) {
|
||||
KernelLogStream(ERROR, "AML") << "DSDT checksum failed";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Build the 4-char name we're looking for: _S0_ through _S5_
|
||||
char target[4] = { '_', 'S', (char)('0' + state), '_' };
|
||||
|
||||
const uint8_t* aml = (const uint8_t*)dsdtData;
|
||||
uint32_t amlLength = header->Length;
|
||||
uint32_t dataStart = sizeof(ACPI::CommonSDTHeader);
|
||||
|
||||
for (uint32_t i = dataStart; i + 4 < amlLength; i++) {
|
||||
if (aml[i] == target[0] && aml[i+1] == target[1] &&
|
||||
aml[i+2] == target[2] && aml[i+3] == target[3]) {
|
||||
|
||||
bool validContext = false;
|
||||
if (i >= 1 && aml[i-1] == NameOp_)
|
||||
validContext = true;
|
||||
else if (i >= 2 && aml[i-2] == NameOp_ && aml[i-1] == '\\')
|
||||
validContext = true;
|
||||
|
||||
if (!validContext) continue;
|
||||
|
||||
uint32_t pos = i + 4;
|
||||
|
||||
if (pos >= amlLength || aml[pos] != PackageOp_) continue;
|
||||
pos++;
|
||||
|
||||
DecodePkgLength(aml, &pos);
|
||||
|
||||
if (pos >= amlLength) continue;
|
||||
uint8_t numElements = aml[pos];
|
||||
pos++;
|
||||
|
||||
if (numElements < 1) continue;
|
||||
|
||||
result.SLP_TYPa = (uint16_t)DecodeIntegerLegacy(aml, &pos);
|
||||
|
||||
if (numElements >= 2 && pos < amlLength)
|
||||
result.SLP_TYPb = (uint16_t)DecodeIntegerLegacy(aml, &pos);
|
||||
else
|
||||
result.SLP_TYPb = 0;
|
||||
|
||||
result.Valid = true;
|
||||
|
||||
KernelLogStream(OK, "AML") << "\\_S" << base::dec << (uint64_t)state
|
||||
<< "_ found: SLP_TYPa=" << base::hex << (uint64_t)result.SLP_TYPa
|
||||
<< " SLP_TYPb=" << base::hex << (uint64_t)result.SLP_TYPb;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
KernelLogStream(INFO, "AML") << "\\_S" << base::dec << (uint64_t)state
|
||||
<< "_ not found in DSDT";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── Full interpreter initialization ─────────────────────────────
|
||||
void InitializeInterpreter(void* dsdtData) {
|
||||
auto& interp = GetInterpreter();
|
||||
if (!interp.LoadTable(dsdtData)) {
|
||||
KernelLogStream(ERROR, "AML") << "Failed to load DSDT into AML interpreter";
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* AmlParser.hpp
|
||||
* Primitive AML bytecode parser for extracting ACPI sleep state values
|
||||
* AML bytecode parser — S5 extraction and interpreter initialization
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
@@ -10,26 +10,28 @@
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
// AML opcodes used during \_S5_ parsing
|
||||
static constexpr uint8_t NameOp = 0x08;
|
||||
static constexpr uint8_t PackageOp = 0x12;
|
||||
static constexpr uint8_t ZeroOp = 0x00;
|
||||
static constexpr uint8_t OneOp = 0x01;
|
||||
static constexpr uint8_t OnesOp = 0xFF;
|
||||
static constexpr uint8_t BytePrefix = 0x0A;
|
||||
static constexpr uint8_t WordPrefix = 0x0B;
|
||||
static constexpr uint8_t DWordPrefix = 0x0C;
|
||||
|
||||
struct S5Object {
|
||||
struct SleepObject {
|
||||
uint16_t SLP_TYPa;
|
||||
uint16_t SLP_TYPb;
|
||||
bool Valid;
|
||||
};
|
||||
|
||||
// Legacy compat alias
|
||||
using S5Object = SleepObject;
|
||||
|
||||
// Parse a DSDT (or SSDT) AML block to find the \_S5_ object.
|
||||
// dsdtData points to the CommonSDTHeader of the DSDT (HHDM-mapped).
|
||||
// Returns the parsed S5 values on success.
|
||||
S5Object FindS5(void* dsdtData);
|
||||
|
||||
// Parse a DSDT to find any \_Sx_ object (x = 0-5) via brute-force scan.
|
||||
// Works on any DSDT regardless of complexity — does not require the
|
||||
// interpreter or namespace.
|
||||
SleepObject FindSleepState(void* dsdtData, int state);
|
||||
|
||||
// Initialize the AML interpreter with the DSDT.
|
||||
// This loads the full table into the namespace and enables
|
||||
// method evaluation, device enumeration, and field access.
|
||||
// Should be called during boot after ACPI table discovery.
|
||||
void InitializeInterpreter(void* dsdtData);
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* AmlResource.cpp
|
||||
* ACPI resource descriptor parsing
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "AmlResource.hpp"
|
||||
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
// ── Small Resource Tags (bits 6:3 of the tag byte) ──────────────
|
||||
static constexpr uint8_t SmallIrqTag = 0x04; // IRQ descriptor
|
||||
static constexpr uint8_t SmallDmaTag = 0x05; // DMA descriptor
|
||||
static constexpr uint8_t SmallIoPortTag = 0x08; // I/O port descriptor
|
||||
static constexpr uint8_t SmallFixedIoTag = 0x09; // Fixed I/O port descriptor
|
||||
static constexpr uint8_t SmallEndTag = 0x0F; // End tag
|
||||
|
||||
// ── Large Resource Tags (byte following the large tag prefix) ───
|
||||
static constexpr uint8_t LargeMemory24Tag = 0x01;
|
||||
static constexpr uint8_t LargeVendorTag = 0x04;
|
||||
static constexpr uint8_t LargeMemory32Tag = 0x05;
|
||||
static constexpr uint8_t LargeMem32FixedTag = 0x06;
|
||||
static constexpr uint8_t LargeDWordAddrTag = 0x07;
|
||||
static constexpr uint8_t LargeWordAddrTag = 0x08;
|
||||
static constexpr uint8_t LargeExtIrqTag = 0x09;
|
||||
static constexpr uint8_t LargeQWordAddrTag = 0x0A;
|
||||
static constexpr uint8_t LargeGpioTag = 0x0C;
|
||||
|
||||
static uint16_t Read16(const uint8_t* p) {
|
||||
return (uint16_t)p[0] | ((uint16_t)p[1] << 8);
|
||||
}
|
||||
|
||||
static uint32_t Read32(const uint8_t* p) {
|
||||
return (uint32_t)p[0] | ((uint32_t)p[1] << 8)
|
||||
| ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
|
||||
}
|
||||
|
||||
static uint64_t Read64(const uint8_t* p) {
|
||||
uint64_t val = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
val |= (uint64_t)p[i] << (i * 8);
|
||||
return val;
|
||||
}
|
||||
|
||||
// Find the lowest set bit in a mask. Returns the bit number, or -1.
|
||||
static int FirstSetBit(uint16_t mask) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (mask & (1 << i)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ParseResourceTemplate(const uint8_t* data, uint32_t length, ResourceList& result) {
|
||||
result.Count = 0;
|
||||
uint32_t pos = 0;
|
||||
|
||||
while (pos < length && result.Count < MaxResources) {
|
||||
uint8_t tag = data[pos];
|
||||
|
||||
// End tag
|
||||
if ((tag & 0x80) == 0 && ((tag >> 3) & 0x0F) == SmallEndTag)
|
||||
break;
|
||||
|
||||
if (tag & 0x80) {
|
||||
// ── Large resource descriptor ───────────────────────
|
||||
uint8_t largeType = tag & 0x7F;
|
||||
if (pos + 3 > length) break;
|
||||
uint16_t resLen = Read16(&data[pos + 1]);
|
||||
uint32_t dataStart = pos + 3;
|
||||
uint32_t dataEnd = dataStart + resLen;
|
||||
if (dataEnd > length) break;
|
||||
|
||||
auto& res = result.Resources[result.Count];
|
||||
|
||||
switch (largeType) {
|
||||
case LargeExtIrqTag: {
|
||||
if (resLen < 2) break;
|
||||
res.Type = ResourceType::ExtendedIrq;
|
||||
uint8_t flags = data[dataStart];
|
||||
res.ExtendedIrq.Flags = flags;
|
||||
res.ExtendedIrq.Shareable = (flags >> 3) & 1;
|
||||
uint8_t irqCount = data[dataStart + 1];
|
||||
if (irqCount > 0 && resLen >= 6) {
|
||||
res.ExtendedIrq.Interrupt = Read32(&data[dataStart + 2]);
|
||||
} else {
|
||||
res.ExtendedIrq.Interrupt = 0;
|
||||
}
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case LargeMemory32Tag: {
|
||||
if (resLen < 17) break;
|
||||
res.Type = ResourceType::Memory32;
|
||||
res.Memory32.ReadWrite = data[dataStart] & 1;
|
||||
res.Memory32.Base = Read32(&data[dataStart + 1]);
|
||||
// Max = data[dataStart + 5..8]
|
||||
// Alignment = data[dataStart + 9..12]
|
||||
res.Memory32.Length = Read32(&data[dataStart + 13]);
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case LargeMem32FixedTag: {
|
||||
if (resLen < 9) break;
|
||||
res.Type = ResourceType::Memory32;
|
||||
res.Memory32.ReadWrite = data[dataStart] & 1;
|
||||
res.Memory32.Base = Read32(&data[dataStart + 1]);
|
||||
res.Memory32.Length = Read32(&data[dataStart + 5]);
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case LargeDWordAddrTag: {
|
||||
if (resLen < 23) break;
|
||||
res.Type = ResourceType::DWordAddress;
|
||||
// ResourceType at dataStart+0, GenFlags at +1, TypeFlags at +2
|
||||
res.AddressSpace.GranularityMin = Read32(&data[dataStart + 3]);
|
||||
res.AddressSpace.GranularityMax = Read32(&data[dataStart + 7]);
|
||||
// Min at +7, Max at +11, Translation at +15, Length at +19
|
||||
res.AddressSpace.Base = Read32(&data[dataStart + 7]);
|
||||
res.AddressSpace.Length = Read32(&data[dataStart + 19]);
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case LargeQWordAddrTag: {
|
||||
if (resLen < 43) break;
|
||||
res.Type = ResourceType::QWordAddress;
|
||||
res.AddressSpace.GranularityMin = Read64(&data[dataStart + 3]);
|
||||
res.AddressSpace.GranularityMax = Read64(&data[dataStart + 11]);
|
||||
res.AddressSpace.Base = Read64(&data[dataStart + 11]);
|
||||
res.AddressSpace.Length = Read64(&data[dataStart + 35]);
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case LargeWordAddrTag: {
|
||||
if (resLen < 13) break;
|
||||
res.Type = ResourceType::WordAddress;
|
||||
res.AddressSpace.GranularityMin = Read16(&data[dataStart + 3]);
|
||||
res.AddressSpace.GranularityMax = Read16(&data[dataStart + 5]);
|
||||
res.AddressSpace.Base = Read16(&data[dataStart + 5]);
|
||||
res.AddressSpace.Length = Read16(&data[dataStart + 11]);
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Unknown large descriptor — skip
|
||||
break;
|
||||
}
|
||||
|
||||
pos = dataEnd;
|
||||
} else {
|
||||
// ── Small resource descriptor ───────────────────────
|
||||
uint8_t smallType = (tag >> 3) & 0x0F;
|
||||
uint8_t resLen = tag & 0x07;
|
||||
uint32_t dataStart = pos + 1;
|
||||
uint32_t dataEnd = dataStart + resLen;
|
||||
if (dataEnd > length) break;
|
||||
|
||||
auto& res = result.Resources[result.Count];
|
||||
|
||||
switch (smallType) {
|
||||
case SmallIrqTag: {
|
||||
if (resLen < 2) break;
|
||||
res.Type = ResourceType::Irq;
|
||||
res.Irq.Mask = Read16(&data[dataStart]);
|
||||
res.Irq.Flags = (resLen >= 3) ? data[dataStart + 2] : 0;
|
||||
int irq = FirstSetBit(res.Irq.Mask);
|
||||
res.Irq.Irq = (irq >= 0) ? (uint8_t)irq : 0;
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case SmallDmaTag: {
|
||||
if (resLen < 2) break;
|
||||
res.Type = ResourceType::Dma;
|
||||
res.Dma.Mask = data[dataStart];
|
||||
res.Dma.Flags = data[dataStart + 1];
|
||||
int ch = FirstSetBit(res.Dma.Mask);
|
||||
res.Dma.Channel = (ch >= 0) ? (uint8_t)ch : 0;
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case SmallIoPortTag: {
|
||||
if (resLen < 7) break;
|
||||
res.Type = ResourceType::IoPort;
|
||||
res.IoPort.Decode16Bit = data[dataStart] & 1;
|
||||
res.IoPort.Base = Read16(&data[dataStart + 1]);
|
||||
// Max = data[dataStart + 3..4]
|
||||
res.IoPort.Alignment = data[dataStart + 5];
|
||||
res.IoPort.Length = data[dataStart + 6];
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case SmallFixedIoTag: {
|
||||
if (resLen < 3) break;
|
||||
res.Type = ResourceType::FixedIoPort;
|
||||
res.FixedIoPort.Base = Read16(&data[dataStart]);
|
||||
res.FixedIoPort.Length = data[dataStart + 2];
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pos = dataEnd;
|
||||
}
|
||||
}
|
||||
|
||||
return result.Count > 0;
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* AmlResource.hpp
|
||||
* ACPI resource descriptor parsing (_CRS, _PRS, _SRS buffers)
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
// ── Resource Types ──────────────────────────────────────────────
|
||||
enum class ResourceType : uint8_t {
|
||||
None = 0,
|
||||
Irq,
|
||||
Dma,
|
||||
IoPort,
|
||||
FixedIoPort,
|
||||
Memory16,
|
||||
Memory32,
|
||||
Memory32Fixed,
|
||||
QWordAddress,
|
||||
DWordAddress,
|
||||
WordAddress,
|
||||
ExtendedIrq,
|
||||
GpioConnection,
|
||||
};
|
||||
|
||||
// ── Single Resource Descriptor ──────────────────────────────────
|
||||
struct ResourceDescriptor {
|
||||
ResourceType Type;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint16_t Mask; // bitmask of supported IRQs
|
||||
uint8_t Flags;
|
||||
uint8_t Irq; // decoded first IRQ number
|
||||
} Irq;
|
||||
|
||||
struct {
|
||||
uint32_t Interrupt; // GSI number
|
||||
uint8_t Flags; // edge/level, active high/low
|
||||
bool Shareable;
|
||||
} ExtendedIrq;
|
||||
|
||||
struct {
|
||||
uint8_t Mask; // bitmask of supported DMA channels
|
||||
uint8_t Flags;
|
||||
uint8_t Channel; // decoded first channel
|
||||
} Dma;
|
||||
|
||||
struct {
|
||||
uint16_t Base;
|
||||
uint16_t Length;
|
||||
uint8_t Alignment;
|
||||
bool Decode16Bit; // true = 16-bit decode, false = 10-bit
|
||||
} IoPort;
|
||||
|
||||
struct {
|
||||
uint16_t Base;
|
||||
uint8_t Length;
|
||||
} FixedIoPort;
|
||||
|
||||
struct {
|
||||
uint32_t Base;
|
||||
uint32_t Length;
|
||||
bool ReadWrite; // true = R/W, false = read-only
|
||||
} Memory32;
|
||||
|
||||
struct {
|
||||
uint64_t Base;
|
||||
uint64_t Length;
|
||||
uint64_t GranularityMin;
|
||||
uint64_t GranularityMax;
|
||||
} AddressSpace;
|
||||
};
|
||||
|
||||
ResourceDescriptor() : Type(ResourceType::None) {
|
||||
// Zero the largest union member
|
||||
AddressSpace = {};
|
||||
}
|
||||
};
|
||||
|
||||
// ── Parsed Resource List ────────────────────────────────────────
|
||||
static constexpr int MaxResources = 16;
|
||||
|
||||
struct ResourceList {
|
||||
ResourceDescriptor Resources[MaxResources];
|
||||
int Count;
|
||||
|
||||
ResourceList() : Count(0) {}
|
||||
|
||||
// Find the first resource of a given type. Returns nullptr if not found.
|
||||
const ResourceDescriptor* FindFirst(ResourceType type) const {
|
||||
for (int i = 0; i < Count; i++) {
|
||||
if (Resources[i].Type == type)
|
||||
return &Resources[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the IRQ number from the first IRQ or ExtendedIrq resource.
|
||||
// Returns -1 if no IRQ found.
|
||||
int GetIrq() const {
|
||||
for (int i = 0; i < Count; i++) {
|
||||
if (Resources[i].Type == ResourceType::Irq)
|
||||
return Resources[i].Irq.Irq;
|
||||
if (Resources[i].Type == ResourceType::ExtendedIrq)
|
||||
return (int)Resources[i].ExtendedIrq.Interrupt;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the first IO port base and length.
|
||||
// Returns false if no IO port found.
|
||||
bool GetIoPort(uint16_t& base, uint16_t& length) const {
|
||||
for (int i = 0; i < Count; i++) {
|
||||
if (Resources[i].Type == ResourceType::IoPort) {
|
||||
base = Resources[i].IoPort.Base;
|
||||
length = Resources[i].IoPort.Length;
|
||||
return true;
|
||||
}
|
||||
if (Resources[i].Type == ResourceType::FixedIoPort) {
|
||||
base = Resources[i].FixedIoPort.Base;
|
||||
length = Resources[i].FixedIoPort.Length;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the first memory base and length.
|
||||
bool GetMemory(uint64_t& base, uint64_t& length) const {
|
||||
for (int i = 0; i < Count; i++) {
|
||||
if (Resources[i].Type == ResourceType::Memory32) {
|
||||
base = Resources[i].Memory32.Base;
|
||||
length = Resources[i].Memory32.Length;
|
||||
return true;
|
||||
}
|
||||
if (Resources[i].Type == ResourceType::QWordAddress ||
|
||||
Resources[i].Type == ResourceType::DWordAddress ||
|
||||
Resources[i].Type == ResourceType::WordAddress) {
|
||||
base = Resources[i].AddressSpace.Base;
|
||||
length = Resources[i].AddressSpace.Length;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Parse a resource template buffer (as returned by _CRS evaluation)
|
||||
// into a structured ResourceList.
|
||||
bool ParseResourceTemplate(const uint8_t* data, uint32_t length, ResourceList& result);
|
||||
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user