Files
MontaukOS/kernel/src/ACPI/AML/AmlNamespace.cpp
T

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;
}
};
};