446 lines
16 KiB
C++
446 lines
16 KiB
C++
/*
|
|
* AmlNamespace.cpp
|
|
* ACPI namespace tree implementation
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#include "AmlNamespace.hpp"
|
|
#include <Libraries/Memory.hpp>
|
|
#include <Memory/Heap.hpp>
|
|
#include <Terminal/Terminal.hpp>
|
|
#include <CppLib/Stream.hpp>
|
|
|
|
using namespace Kt;
|
|
|
|
namespace Hal {
|
|
namespace AML {
|
|
|
|
// ============================================================================
|
|
|
|
// Package element store
|
|
|
|
// ============================================================================
|
|
namespace PackageStore {
|
|
|
|
struct Entry {
|
|
Object* Elements;
|
|
int Count;
|
|
};
|
|
|
|
static Entry s_persistent[MaxPersistent];
|
|
static Entry s_transient[MaxTransient];
|
|
static int s_persistentUsed = 0;
|
|
static int s_transientNext = 0;
|
|
|
|
// Indices handed out are 1-based so that a zeroed Object (nodes are
|
|
// memset before Clear() runs, and callers zero-init freely) never
|
|
// looks like a reference to entry 0.
|
|
static constexpr int32_t TransientBase = MaxPersistent;
|
|
|
|
static Object* AllocElements(int count) {
|
|
auto* mem = (Object*)Memory::g_heap->Request(sizeof(Object) * count);
|
|
if (!mem) return nullptr;
|
|
for (int i = 0; i < count; i++)
|
|
mem[i] = Object{};
|
|
return mem;
|
|
}
|
|
|
|
int32_t AllocPersistent(int count) {
|
|
if (count <= 0 || count > MaxPackageElements) return -1;
|
|
if (s_persistentUsed >= MaxPersistent) return -1;
|
|
|
|
Entry& e = s_persistent[s_persistentUsed];
|
|
e.Elements = AllocElements(count);
|
|
if (!e.Elements) return -1;
|
|
e.Count = count;
|
|
return (int32_t)(s_persistentUsed++) + 1;
|
|
}
|
|
|
|
int32_t AllocTransient(int count) {
|
|
if (count <= 0 || count > MaxPackageElements) return -1;
|
|
|
|
int slot = s_transientNext;
|
|
if (slot >= MaxTransient) {
|
|
// The ring is only rewound between top-level evaluations, so
|
|
// a method that builds more packages than this reuses the
|
|
// oldest slots rather than failing outright.
|
|
slot = 0;
|
|
s_transientNext = 0;
|
|
}
|
|
s_transientNext++;
|
|
|
|
Entry& e = s_transient[slot];
|
|
// Element arrays are sized on first use and reused afterwards;
|
|
// a larger request reallocates.
|
|
if (e.Elements == nullptr || e.Count < count) {
|
|
Object* mem = AllocElements(count);
|
|
if (!mem) return -1;
|
|
if (e.Elements) Memory::g_heap->Free(e.Elements);
|
|
e.Elements = mem;
|
|
} else {
|
|
for (int i = 0; i < count; i++)
|
|
e.Elements[i] = Object{};
|
|
}
|
|
e.Count = count;
|
|
return TransientBase + slot + 1;
|
|
}
|
|
|
|
void ResetTransient() {
|
|
s_transientNext = 0;
|
|
}
|
|
|
|
bool IsPersistent(int32_t index) {
|
|
return index > 0 && index <= MaxPersistent;
|
|
}
|
|
|
|
static Entry* Lookup(int32_t index) {
|
|
if (index <= 0) return nullptr;
|
|
int32_t i = index - 1;
|
|
|
|
if (i < MaxPersistent) {
|
|
if (i >= s_persistentUsed) return nullptr;
|
|
return s_persistent[i].Elements ? &s_persistent[i] : nullptr;
|
|
}
|
|
|
|
i -= TransientBase;
|
|
if (i < 0 || i >= MaxTransient) return nullptr;
|
|
return s_transient[i].Elements ? &s_transient[i] : nullptr;
|
|
}
|
|
|
|
Object* Element(int32_t index, int element) {
|
|
Entry* e = Lookup(index);
|
|
if (!e || element < 0 || element >= e->Count) return nullptr;
|
|
return &e->Elements[element];
|
|
}
|
|
|
|
int Count(int32_t index) {
|
|
Entry* e = Lookup(index);
|
|
return e ? e->Count : 0;
|
|
}
|
|
};
|
|
|
|
Namespace::Namespace() : m_chunkCount(0), m_nodeCount(0) {
|
|
for (int i = 0; i < MaxChunks; i++)
|
|
m_chunks[i] = nullptr;
|
|
// Root node is created lazily by the first CreateNode/AllocNode call,
|
|
// which happens during LoadTable -- after the kernel heap is ready.
|
|
}
|
|
|
|
void Namespace::EnsureRoot() {
|
|
if (m_nodeCount > 0) return;
|
|
int32_t root = AllocNode();
|
|
auto* rootNode = GetNode(root);
|
|
if (rootNode) {
|
|
rootNode->Name[0] = '\\';
|
|
rootNode->Name[1] = '\0';
|
|
rootNode->ParentIndex = -1;
|
|
}
|
|
}
|
|
|
|
bool Namespace::AllocChunk() {
|
|
if (m_chunkCount >= MaxChunks) {
|
|
static bool warned = false;
|
|
if (!warned) {
|
|
warned = true;
|
|
KernelLogStream(WARNING, "AML")
|
|
<< "Namespace node pool full at " << base::dec
|
|
<< (uint64_t)(MaxChunks * NodesPerChunk)
|
|
<< " nodes; part of the namespace will be missing";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
auto* chunk = (NamespaceNode*)Memory::g_heap->Request(
|
|
sizeof(NamespaceNode) * NodesPerChunk);
|
|
if (!chunk) return false;
|
|
|
|
// Zero-initialize the chunk
|
|
memset(chunk, 0, sizeof(NamespaceNode) * NodesPerChunk);
|
|
for (int i = 0; i < NodesPerChunk; i++)
|
|
chunk[i].Clear();
|
|
|
|
m_chunks[m_chunkCount++] = chunk;
|
|
return true;
|
|
}
|
|
|
|
bool Namespace::AppendChild(NamespaceNode* parent, int32_t child) {
|
|
if (parent->ChildCount >= parent->ChildCapacity) {
|
|
int32_t newCap = parent->ChildCapacity ? parent->ChildCapacity * 2
|
|
: InitialChildren;
|
|
|
|
auto* grown = (int32_t*)Memory::g_heap->Request(sizeof(int32_t) * newCap);
|
|
if (!grown) {
|
|
// Losing a child silently costs whole devices, so say it
|
|
// once rather than leaving a hole nobody can see.
|
|
static bool warned = false;
|
|
if (!warned) {
|
|
warned = true;
|
|
KernelLogStream(WARNING, "AML")
|
|
<< "Out of heap growing a namespace node's child list;"
|
|
<< " part of the namespace will be missing";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
for (int32_t i = 0; i < parent->ChildCount; i++)
|
|
grown[i] = parent->ChildIndices[i];
|
|
for (int32_t i = parent->ChildCount; i < newCap; i++)
|
|
grown[i] = -1;
|
|
|
|
if (parent->ChildIndices)
|
|
Memory::g_heap->Free(parent->ChildIndices);
|
|
|
|
parent->ChildIndices = grown;
|
|
parent->ChildCapacity = newCap;
|
|
}
|
|
|
|
parent->ChildIndices[parent->ChildCount++] = child;
|
|
return true;
|
|
}
|
|
|
|
int32_t Namespace::AllocNode() {
|
|
// Check if we need a new chunk
|
|
int32_t capacity = m_chunkCount * NodesPerChunk;
|
|
if (m_nodeCount >= capacity) {
|
|
if (!AllocChunk()) return -1;
|
|
}
|
|
|
|
int32_t idx = m_nodeCount++;
|
|
auto* node = GetNode(idx);
|
|
if (node) node->Clear();
|
|
return idx;
|
|
}
|
|
|
|
NamespaceNode* Namespace::GetNode(int32_t index) {
|
|
if (index < 0 || index >= m_nodeCount) return nullptr;
|
|
int chunk = index / NodesPerChunk;
|
|
int offset = index % NodesPerChunk;
|
|
if (chunk >= m_chunkCount || !m_chunks[chunk]) return nullptr;
|
|
return &m_chunks[chunk][offset];
|
|
}
|
|
|
|
const NamespaceNode* Namespace::GetNode(int32_t index) const {
|
|
if (index < 0 || index >= m_nodeCount) return nullptr;
|
|
int chunk = index / NodesPerChunk;
|
|
int offset = index % NodesPerChunk;
|
|
if (chunk >= m_chunkCount || !m_chunks[chunk]) return nullptr;
|
|
return &m_chunks[chunk][offset];
|
|
}
|
|
|
|
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];
|
|
auto* child = GetNode(ci);
|
|
if (!child) continue;
|
|
if (SegmentEqual(child->Name, seg))
|
|
return ci;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int32_t Namespace::FindChild(int32_t parentIndex, const char* seg) const {
|
|
char padded[MaxNameSegLen + 1];
|
|
PadSegment(seg, padded);
|
|
return FindChildByName(parentIndex, padded);
|
|
}
|
|
|
|
int32_t Namespace::CreateNode(const char* absolutePath) {
|
|
EnsureRoot();
|
|
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;
|
|
|
|
auto* childNode = GetNode(child);
|
|
if (!childNode) return -1;
|
|
memcpy(childNode->Name, segments[i], MaxNameSegLen + 1);
|
|
childNode->ParentIndex = current;
|
|
|
|
// Add to parent's children
|
|
auto* parent = GetNode(current);
|
|
if (!parent) return -1;
|
|
if (!AppendChild(parent, child)) return -1;
|
|
}
|
|
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::SearchUpward(const char* relative, int32_t scopeNodeIndex) const {
|
|
char segments[MaxPathDepth][MaxNameSegLen + 1];
|
|
int segCount = 0;
|
|
|
|
// Split on '.', padding each segment to four characters the way
|
|
// node names are stored.
|
|
int i = 0;
|
|
while (relative[i] && segCount < MaxPathDepth) {
|
|
char seg[MaxNameSegLen + 1];
|
|
int n = 0;
|
|
while (relative[i] && relative[i] != '.' && n < MaxNameSegLen)
|
|
seg[n++] = relative[i++];
|
|
seg[n] = '\0';
|
|
while (relative[i] && relative[i] != '.') i++; // overlong segment
|
|
if (relative[i] == '.') i++;
|
|
|
|
PadSegment(seg, segments[segCount]);
|
|
segCount++;
|
|
}
|
|
|
|
if (segCount == 0) return -1;
|
|
|
|
// Walk up from the current scope looking for the first segment.
|
|
int32_t scope = scopeNodeIndex;
|
|
while (scope >= 0) {
|
|
int32_t found = FindChildByName(scope, segments[0]);
|
|
if (found >= 0) {
|
|
// Descend the rest. A scope that matches the first segment
|
|
// but not the remainder does not win -- keep walking up.
|
|
int32_t cur = found;
|
|
bool ok = true;
|
|
for (int k = 1; k < segCount; k++) {
|
|
cur = FindChildByName(cur, segments[k]);
|
|
if (cur < 0) { ok = false; break; }
|
|
}
|
|
if (ok) return cur;
|
|
}
|
|
|
|
auto* node = GetNode(scope);
|
|
scope = node ? node->ParentIndex : -1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
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;
|
|
auto* node = GetNode(scope);
|
|
scope = node ? node->ParentIndex : -1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
};
|
|
};
|