/* * AmlNamespace.cpp * ACPI namespace tree implementation * Copyright (c) 2026 Daniel Hammer */ #include "AmlNamespace.hpp" #include #include #include #include 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; } }; };