LibACPI: Support reading OpRegionOp and FieldOp

Those two always come in pairs so lets do it in one commit. This
doesn't yet support evaluating fields as it's not yet needed and would
require setting up callbacks from the interpreter.

Co-authored-by: RatcheT2497 <[email protected]>
This commit is contained in:
UndefineandRatcheT2497 authored and Sönke Holz committed 2026-11-01 15:08:28 +01:00
1 parent cc1e5ef63b
commit c9e5fa6ada
6 files changed
+174 -2

No files matched your search

+74 -2
View File
@@ -227,6 +227,22 @@ ErrorOr<void> Interpreter::finalize_term_opcode(OpcodeFrame& frame)
return {};
}
case Opcode::OpRegionOp: {
auto path = TRY(frame.operand(0).as<NameString>());
auto region_space = TRY(frame.operand(1).as<u64>());
auto region_offset = TRY(frame.operand(2).as<u64>());
auto region_length = TRY(frame.operand(3).as<u64>());
auto node = TRY(try_make_ref_counted<OperationRegionNode>(region_space, region_offset, region_length));
TRY(m_scope_frames.last().insert_node(path, node));
return {};
}
case Opcode::FieldOp:
// NOTE: All the field elements are read as operands, so all we need to do is reset the field offset.
m_current_field_offset = 0;
return {};
default:
INTERPRETER_ERROR("Unimplemented!", EINVAL, "Unimplemented term opcode {}!", frame.opcode());
}
@@ -276,6 +292,54 @@ ErrorOr<IterationDecision> Interpreter::read_operand(TableStream& stream, Operan
return IterationDecision::Continue;
}
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#fieldelement
case OperandType::FieldElement: {
#ifndef KERNEL
if constexpr (AML_DEBUG)
warn("Reading field element...");
#endif
if (is_lead_name_char(TRY(stream.peek_byte()))) {
// NamedField := NameSeg PkgLength
auto name_segment_bytes = TRY(stream.read_in_place<u8 const>(4));
auto name_segment = TRY(NameSegment::from_bytes(name_segment_bytes));
auto package_length = TRY(stream.read_package_length());
auto operation_region_node = TRY(find_node(TRY(top_opcode_frame.operand(1).as<NameString>()), m_scope_frames.last().scope()));
if (operation_region_node->type() != NodeType::OperationRegion)
INTERPRETER_ERROR("Expected operation region node!", EINVAL, "Expected operation region node, got {}", operation_region_node->kind());
auto flags = TRY(top_opcode_frame.operand(2).as<u64>());
DEBUG_LOG(" ({}, {})", name_segment.to_string_view(), package_length);
auto node = TRY(try_make_ref_counted<FieldNode>(static_ptr_cast<OperationRegionNode>(operation_region_node), flags, m_current_field_offset, package_length));
TRY(m_scope_frames.last().scope()->insert_child(name_segment, node));
m_current_field_offset += package_length;
TRY(top_opcode_frame.finish_operand(stream.offset()));
return IterationDecision::Continue;
}
auto type = TRY(stream.read_value<u8>());
DEBUG_LOG(" Type: {:02X}", type);
switch (type) {
case 0x00: {
// ReservedField := 0x00 PkgLength
m_current_field_offset += TRY(stream.read_package_length());
TRY(top_opcode_frame.finish_operand(stream.offset()));
return IterationDecision::Continue;
}
default:
INTERPRETER_ERROR("Unimplemented field type!", EINVAL, "Unimplemented field type {:02X}!", type);
}
VERIFY_NOT_REACHED();
}
case OperandType::TermArg:
case OperandType::DataRefObject:
DEBUG_LOG("Reading complex object {}...", to_underlying(operand));
@@ -384,12 +448,20 @@ ErrorOr<Object> Interpreter::evaluate(NameString const& path)
ErrorOr<Object> Interpreter::evaluate(NonnullRefPtr<Node> node)
{
if (node->type() == NodeType::Name) {
switch (node->type()) {
case NodeType::Name: {
auto name_node = static_ptr_cast<NameNode>(node);
return name_node->data();
}
INTERPRETER_ERROR("Cannot evaluate node type!", EINVAL, "Node type {} not supported for evaluation!", node->kind());
case NodeType::Field:
INTERPRETER_ERROR("Field evaluation not supported!", EINVAL, "Evaluating fields is not supported yet!");
default:
INTERPRETER_ERROR("Cannot evaluate node type!", EINVAL, "Node type {} not supported for evaluation!", node->kind());
}
VERIFY_NOT_REACHED();
}
ErrorOr<NonnullRefPtr<Node>> Interpreter::create_root_namespace()
+2
View File
@@ -83,6 +83,8 @@ private:
State m_state { State::ReadingOpcode };
u64 m_current_field_offset { 0 };
AK::Vector<TableStream> m_loaded_tables;
Vector<ScopeFrame> m_scope_frames;
Vector<OpcodeFrame> m_opcode_frames;
+12
View File
@@ -65,4 +65,16 @@ ErrorOr<void> NameNode::write_description(StringBuilder& builder)
return {};
}
ErrorOr<void> OperationRegionNode::write_description(StringBuilder& builder)
{
TRY(builder.try_appendff("OperationRegion(Space: {}, Offset: {}, Length: {})", m_space_raw, m_offset, m_length));
return {};
}
ErrorOr<void> FieldNode::write_description(StringBuilder& builder)
{
TRY(builder.try_appendff("Field(Flags: {}, Offset: {}, Length: {})", m_flags, m_offset, m_length));
return {};
}
}
+62
View File
@@ -24,6 +24,8 @@ enum class NodeType : u8 {
Untyped,
Scope,
Name,
OperationRegion,
Field,
};
class Node : public AtomicRefCounted<Node>
@@ -77,4 +79,64 @@ protected:
Object m_data;
};
class OperationRegionNode : public Node {
public:
OperationRegionNode(u8 region_space, u64 region_offset, u64 region_length)
: m_space_raw(region_space)
, m_offset(region_offset)
, m_length(region_length)
{
}
virtual ErrorOr<void> write_description(StringBuilder& builder) override;
virtual NodeType type() const override { return NodeType::OperationRegion; }
virtual StringView kind() const override { return "Operation Region"sv; }
GenericAddressStructure::AddressSpace space() const
{
if (m_space_raw >= 0x80)
return GenericAddressStructure::AddressSpace::OEMDefined;
return static_cast<GenericAddressStructure::AddressSpace>(m_space_raw);
}
u64 offset() const { return m_offset; }
u64 length() const { return m_length; }
private:
u8 m_space_raw;
u64 m_offset;
u64 m_length;
};
class FieldNode : public Node {
public:
FieldNode(NonnullRefPtr<OperationRegionNode> const& operation_region, u8 flags, size_t offset, size_t size)
: m_operation_region(operation_region)
, m_flags(flags)
, m_offset(offset)
, m_length(size)
{
}
virtual ErrorOr<void> write_description(StringBuilder& builder) override;
virtual NodeType type() const override { return NodeType::Field; }
virtual StringView kind() const override { return "Field"sv; }
NonnullRefPtr<OperationRegionNode> const& operation_region() const { return m_operation_region; }
u8 flags() const { return m_flags; }
u64 offset() const { return m_offset; }
u64 length() const { return m_length; }
private:
NonnullRefPtr<OperationRegionNode> m_operation_region;
u8 m_flags;
u64 m_offset;
u64 m_length;
};
}
@@ -26,6 +26,24 @@ constexpr static Array<OperandType, 2> WORD_CONST_OPERANDS = { OperandType::Word
constexpr static Array<OperandType, 2> DWORD_CONST_OPERANDS = { OperandType::DWordData, OperandType::End };
constexpr static Array<OperandType, 2> QWORD_CONST_OPERANDS = { OperandType::QWordData, OperandType::End };
constexpr static Array<OperandType, 5> OP_REGION_OPERANDS = {
OperandType::NameString,
OperandType::ByteData,
OperandType::TermArg,
OperandType::TermArg,
OperandType::End,
};
constexpr static Array<OperandType, 7> FIELD_OPERANDS = {
OperandType::MarkAsCount,
OperandType::PackageLength,
OperandType::NameString,
OperandType::ByteData,
OperandType::RepeatNextBytes,
OperandType::FieldElement,
OperandType::End,
};
static ErrorOr<ReadonlySpan<OperandType>> get_opcode_operand_template(Opcode opcode)
{
switch (opcode) {
@@ -42,6 +60,10 @@ static ErrorOr<ReadonlySpan<OperandType>> get_opcode_operand_template(Opcode opc
return SCOPE_OPERANDS.span();
case Opcode::NameOp:
return NAME_OPERANDS.span();
case Opcode::OpRegionOp:
return OP_REGION_OPERANDS.span();
case Opcode::FieldOp:
return FIELD_OPERANDS.span();
case Opcode::ZeroOp:
case Opcode::OneOp:
@@ -84,6 +106,7 @@ bool OpcodeFrame::filter_opcode_for_next_operand(Opcode opcode)
case OperandType::WordData:
case OperandType::DWordData:
case OperandType::QWordData:
case OperandType::FieldElement:
return false;
case OperandType::TermArg:
+1
View File
@@ -21,6 +21,7 @@ enum class OperandType : u8 {
DWordData,
QWordData,
FieldElement,
TermArg,
DataRefObject,