From c9e5fa6ada73d992e3aeefb21ff7eb23fc329a7a Mon Sep 17 00:00:00 2001 From: Undefine Date: Wed, 28 Oct 2026 19:46:02 +0100 Subject: [PATCH] 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 --- Userland/Libraries/LibACPI/Interpreter.cpp | 76 +++++++++++++++++++++- Userland/Libraries/LibACPI/Interpreter.h | 2 + Userland/Libraries/LibACPI/Node.cpp | 12 ++++ Userland/Libraries/LibACPI/Node.h | 62 ++++++++++++++++++ Userland/Libraries/LibACPI/OpcodeFrame.cpp | 23 +++++++ Userland/Libraries/LibACPI/OpcodeFrame.h | 1 + 6 files changed, 174 insertions(+), 2 deletions(-) diff --git a/Userland/Libraries/LibACPI/Interpreter.cpp b/Userland/Libraries/LibACPI/Interpreter.cpp index e9b17124711..92a5ef36af7 100644 --- a/Userland/Libraries/LibACPI/Interpreter.cpp +++ b/Userland/Libraries/LibACPI/Interpreter.cpp @@ -227,6 +227,22 @@ ErrorOr Interpreter::finalize_term_opcode(OpcodeFrame& frame) return {}; } + case Opcode::OpRegionOp: { + auto path = TRY(frame.operand(0).as()); + auto region_space = TRY(frame.operand(1).as()); + auto region_offset = TRY(frame.operand(2).as()); + auto region_length = TRY(frame.operand(3).as()); + + auto node = TRY(try_make_ref_counted(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 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(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()), 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()); + + DEBUG_LOG(" ({}, {})", name_segment.to_string_view(), package_length); + + auto node = TRY(try_make_ref_counted(static_ptr_cast(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()); + 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 Interpreter::evaluate(NameString const& path) ErrorOr Interpreter::evaluate(NonnullRefPtr node) { - if (node->type() == NodeType::Name) { + switch (node->type()) { + case NodeType::Name: { auto name_node = static_ptr_cast(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> Interpreter::create_root_namespace() diff --git a/Userland/Libraries/LibACPI/Interpreter.h b/Userland/Libraries/LibACPI/Interpreter.h index a323dba2d82..abe12e8d5b4 100644 --- a/Userland/Libraries/LibACPI/Interpreter.h +++ b/Userland/Libraries/LibACPI/Interpreter.h @@ -83,6 +83,8 @@ private: State m_state { State::ReadingOpcode }; + u64 m_current_field_offset { 0 }; + AK::Vector m_loaded_tables; Vector m_scope_frames; Vector m_opcode_frames; diff --git a/Userland/Libraries/LibACPI/Node.cpp b/Userland/Libraries/LibACPI/Node.cpp index 906a35a1b84..7354c717e0c 100644 --- a/Userland/Libraries/LibACPI/Node.cpp +++ b/Userland/Libraries/LibACPI/Node.cpp @@ -65,4 +65,16 @@ ErrorOr NameNode::write_description(StringBuilder& builder) return {}; } +ErrorOr OperationRegionNode::write_description(StringBuilder& builder) +{ + TRY(builder.try_appendff("OperationRegion(Space: {}, Offset: {}, Length: {})", m_space_raw, m_offset, m_length)); + return {}; +} + +ErrorOr FieldNode::write_description(StringBuilder& builder) +{ + TRY(builder.try_appendff("Field(Flags: {}, Offset: {}, Length: {})", m_flags, m_offset, m_length)); + return {}; +} + } diff --git a/Userland/Libraries/LibACPI/Node.h b/Userland/Libraries/LibACPI/Node.h index eff25916500..cb6e8333a60 100644 --- a/Userland/Libraries/LibACPI/Node.h +++ b/Userland/Libraries/LibACPI/Node.h @@ -24,6 +24,8 @@ enum class NodeType : u8 { Untyped, Scope, Name, + OperationRegion, + Field, }; class Node : public AtomicRefCounted @@ -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 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(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 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 write_description(StringBuilder& builder) override; + + virtual NodeType type() const override { return NodeType::Field; } + virtual StringView kind() const override { return "Field"sv; } + + NonnullRefPtr 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 m_operation_region; + + u8 m_flags; + u64 m_offset; + u64 m_length; +}; + } diff --git a/Userland/Libraries/LibACPI/OpcodeFrame.cpp b/Userland/Libraries/LibACPI/OpcodeFrame.cpp index 78420d294e4..fc2b9a37b18 100644 --- a/Userland/Libraries/LibACPI/OpcodeFrame.cpp +++ b/Userland/Libraries/LibACPI/OpcodeFrame.cpp @@ -26,6 +26,24 @@ constexpr static Array WORD_CONST_OPERANDS = { OperandType::Word constexpr static Array DWORD_CONST_OPERANDS = { OperandType::DWordData, OperandType::End }; constexpr static Array QWORD_CONST_OPERANDS = { OperandType::QWordData, OperandType::End }; +constexpr static Array OP_REGION_OPERANDS = { + OperandType::NameString, + OperandType::ByteData, + OperandType::TermArg, + OperandType::TermArg, + OperandType::End, +}; + +constexpr static Array FIELD_OPERANDS = { + OperandType::MarkAsCount, + OperandType::PackageLength, + OperandType::NameString, + OperandType::ByteData, + OperandType::RepeatNextBytes, + OperandType::FieldElement, + OperandType::End, +}; + static ErrorOr> get_opcode_operand_template(Opcode opcode) { switch (opcode) { @@ -42,6 +60,10 @@ static ErrorOr> 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: diff --git a/Userland/Libraries/LibACPI/OpcodeFrame.h b/Userland/Libraries/LibACPI/OpcodeFrame.h index 8a8120d9f3a..a51445feafe 100644 --- a/Userland/Libraries/LibACPI/OpcodeFrame.h +++ b/Userland/Libraries/LibACPI/OpcodeFrame.h @@ -21,6 +21,7 @@ enum class OperandType : u8 { DWordData, QWordData, + FieldElement, TermArg, DataRefObject,