182 lines
6.4 KiB
Zig
182 lines
6.4 KiB
Zig
//! The ACPI namespace the AML parser builds: a tree of named nodes, plus the name
|
|
//! resolution rules the parser needs while it walks (so a method invocation can be
|
|
//! resolved to its declaration to learn its argument count).
|
|
//!
|
|
//! Nodes are individually allocated and linked intrusively (first-child /
|
|
//! next-sibling), the same shape as the device tree in `device.zig`.
|
|
|
|
const std = @import("std");
|
|
|
|
pub const NodeKind = enum {
|
|
root,
|
|
scope,
|
|
device,
|
|
method,
|
|
name,
|
|
region, // OperationRegion
|
|
field, // a Field unit
|
|
mutex,
|
|
event,
|
|
processor,
|
|
power_resource,
|
|
thermal_zone,
|
|
alias,
|
|
external,
|
|
other,
|
|
};
|
|
|
|
pub const Node = struct {
|
|
/// The 4-byte NameSeg identifying this node within its parent. The root uses
|
|
/// all-zero.
|
|
segment: [4]u8 = .{ 0, 0, 0, 0 },
|
|
kind: NodeKind = .other,
|
|
/// For Method / External: the declared argument count (0..7). Used to resolve
|
|
/// how many TermArgs a method invocation consumes.
|
|
arg_count: u8 = 0,
|
|
/// For Name: the AML bytes of its DataReferenceObject (so a value like a sleep
|
|
/// state's (`_Sx`) Package can be parsed on demand). For Method: the AML bytes of the body,
|
|
/// interpreted on demand by the evaluator. Empty otherwise.
|
|
value: []const u8 = &.{},
|
|
|
|
// OperationRegion metadata (kind == .region): the address space, plus the AML
|
|
// of the offset/length expressions (evaluated lazily, usually constants).
|
|
region_space: u8 = 0,
|
|
region_offset_aml: []const u8 = &.{},
|
|
region_len_aml: []const u8 = &.{},
|
|
|
|
// Field-unit metadata (kind == .field): which region it lives in and its bit
|
|
// position/width/access, so the evaluator can read/write it.
|
|
region: ?*Node = null,
|
|
bit_offset: u32 = 0,
|
|
bit_width: u32 = 0,
|
|
access_type: u8 = 0,
|
|
|
|
parent: ?*Node = null,
|
|
first_child: ?*Node = null,
|
|
next_sibling: ?*Node = null,
|
|
|
|
/// Depth-first count of this node and everything under it.
|
|
pub fn subtreeCount(self: *const Node) usize {
|
|
var n: usize = 1;
|
|
var c = self.first_child;
|
|
while (c) |child| : (c = child.next_sibling) n += child.subtreeCount();
|
|
return n;
|
|
}
|
|
};
|
|
|
|
pub const Namespace = struct {
|
|
allocator: std.mem.Allocator,
|
|
root: *Node,
|
|
|
|
pub fn init(allocator: std.mem.Allocator) !Namespace {
|
|
const root = try allocator.create(Node);
|
|
root.* = .{ .kind = .root };
|
|
return .{ .allocator = allocator, .root = root };
|
|
}
|
|
|
|
pub fn nodeCount(self: *const Namespace) usize {
|
|
return self.root.subtreeCount();
|
|
}
|
|
|
|
fn findChild(parent: *Node, segment: [4]u8) ?*Node {
|
|
var c = parent.first_child;
|
|
while (c) |child| : (c = child.next_sibling) {
|
|
if (std.mem.eql(u8, &child.segment, &segment)) return child;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// The direct child of `node` named `segment`, or null. Unlike `resolve`, this does
|
|
/// not apply the search-rule walk-up — it looks only at immediate children (for
|
|
/// reading a device's own hardware ID (`_HID`) / current resource settings (`_CRS`)).
|
|
pub fn childOf(node: *Node, segment: [4]u8) ?*Node {
|
|
return findChild(node, segment);
|
|
}
|
|
|
|
fn newChild(self: *Namespace, parent: *Node, segment: [4]u8, kind: NodeKind) !*Node {
|
|
const n = try self.allocator.create(Node);
|
|
n.* = .{ .segment = segment, .kind = kind, .parent = parent };
|
|
// Append at the tail so a dump reads in declaration order.
|
|
if (parent.first_child == null) {
|
|
parent.first_child = n;
|
|
} else {
|
|
var current = parent.first_child.?;
|
|
while (current.next_sibling) |sib| current = sib;
|
|
current.next_sibling = n;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/// Create a Field unit node directly under `scope` (field units live in the
|
|
/// scope of the Field/IndexField/BankField, not under the region).
|
|
pub fn newFieldUnit(self: *Namespace, scope: *Node, segment: [4]u8) !*Node {
|
|
return self.findOrCreate(scope, segment, .field);
|
|
}
|
|
|
|
fn findOrCreate(self: *Namespace, parent: *Node, segment: [4]u8, kind: NodeKind) !*Node {
|
|
if (findChild(parent, segment)) |existing| {
|
|
// Reopening a scope (e.g. Scope(\_SB) after Device \_SB) keeps the more
|
|
// specific kind rather than downgrading to a plain scope.
|
|
if (existing.kind == .scope and kind != .scope) existing.kind = kind;
|
|
return existing;
|
|
}
|
|
return self.newChild(parent, segment, kind);
|
|
}
|
|
|
|
/// The node a definition's NameString names, creating any intermediate scopes.
|
|
/// The final segment is created (or found) with `kind`; intermediates are
|
|
/// scopes. Returns the namespace root for a NullName (empty path).
|
|
pub fn place(
|
|
self: *Namespace,
|
|
current: *Node,
|
|
rooted: bool,
|
|
parents: u8,
|
|
segments: []const [4]u8,
|
|
kind: NodeKind,
|
|
) !*Node {
|
|
var base = startNode(self, current, rooted, parents);
|
|
if (segments.len == 0) return base;
|
|
var i: usize = 0;
|
|
while (i + 1 < segments.len) : (i += 1) {
|
|
base = try self.findOrCreate(base, segments[i], .scope);
|
|
}
|
|
return self.findOrCreate(base, segments[segments.len - 1], kind);
|
|
}
|
|
|
|
/// Resolve a NameString *reference* to an existing node, or null. A single
|
|
/// relative segment uses the ACPI search rule (walk up the ancestors); any
|
|
/// rooted, parented, or multi-segment path is resolved exactly.
|
|
pub fn resolve(
|
|
self: *Namespace,
|
|
current: *Node,
|
|
rooted: bool,
|
|
parents: u8,
|
|
segments: []const [4]u8,
|
|
) ?*Node {
|
|
if (segments.len == 0) return null;
|
|
|
|
if (!rooted and parents == 0 and segments.len == 1) {
|
|
// Search rule: this scope, then each ancestor up to the root.
|
|
var scope: ?*Node = current;
|
|
while (scope) |s| : (scope = s.parent) {
|
|
if (findChild(s, segments[0])) |n| return n;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
var base = startNode(self, current, rooted, parents);
|
|
for (segments) |segment| {
|
|
base = findChild(base, segment) orelse return null;
|
|
}
|
|
return base;
|
|
}
|
|
|
|
fn startNode(self: *Namespace, current: *Node, rooted: bool, parents: u8) *Node {
|
|
if (rooted) return self.root;
|
|
var base = current;
|
|
var up = parents;
|
|
while (up > 0) : (up -= 1) base = base.parent orelse self.root;
|
|
return base;
|
|
}
|
|
};
|