mirror of
https://github.com/SerenityOS/serenity.git
synced 2026-11-07 04:29:30 -10:00
LibACPI: Add a skeleton of a AML interpreter
This a simple skeleton for a non recursive AML parser + interpreter. For now it support very little stuff, only a couple of opcodes but that is enough to load a very very simple AML table. Co-authored-by: RatcheT2497 <[email protected]>
This commit is contained in:
1 parent
7af126f893
commit
95ab8a7e1a
18 files changed
+1775
No files matched your search
@@ -10,6 +10,10 @@
|
||||
# cmakedefine01 AFLACLOADER_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef AML_DEBUG
|
||||
# cmakedefine01 AML_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef AUDIO_DEBUG
|
||||
# cmakedefine01 AUDIO_DEBUG
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,7 @@ set(AC97_DEBUG ON)
|
||||
set(ACPI_DEBUG ON)
|
||||
set(AFLACLOADER_DEBUG ON)
|
||||
set(AHCI_DEBUG ON)
|
||||
set(AML_DEBUG ON)
|
||||
set(APIC_DEBUG ON)
|
||||
set(APIC_SMP_DEBUG ON)
|
||||
set(ARP_DEBUG ON)
|
||||
@@ -282,3 +283,5 @@ set(XML_PARSER_DEBUG ON)
|
||||
# set(IA32_DEBUG_INTERFACE ON)
|
||||
# False positive: DEBUG_STRIPPED is a PE characteristic, not a debug flag
|
||||
# set(DEBUG_STRIPPED ON)
|
||||
# False positive: DEBUG_LOG is a macro in the ACPI interpreter
|
||||
# set(DEBUG_LOG ON)
|
||||
@@ -1,4 +1,5 @@
|
||||
add_subdirectory(LibAccelGfx)
|
||||
add_subdirectory(LibACPI)
|
||||
add_subdirectory(LibArchive)
|
||||
add_subdirectory(LibAudio)
|
||||
add_subdirectory(LibC)
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
set(SOURCES
|
||||
Interpreter.cpp
|
||||
NameString.cpp
|
||||
Node.cpp
|
||||
OpcodeFilters.cpp
|
||||
OpcodeFrame.cpp
|
||||
Table.cpp
|
||||
TableStream.cpp
|
||||
)
|
||||
serenity_lib(LibACPI acpi)
|
||||
@@ -7,7 +7,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <AK/EnumBits.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Traits.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace ACPI {
|
||||
@@ -336,4 +338,156 @@ struct [[gnu::packed]] DSDT {
|
||||
|
||||
}
|
||||
|
||||
namespace AML {
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#aml-byte-stream-byte-values
|
||||
constexpr u8 EXT_OP_PREFIX = 0x5B;
|
||||
constexpr u8 DUAL_NAME_PREFIX = 0x2E;
|
||||
constexpr u8 MULTI_NAME_PREFIX = 0x2F;
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#aml-byte-stream-byte-values
|
||||
enum class Opcode : u16 {
|
||||
ZeroOp = 0x0000,
|
||||
OneOp = 0x0001,
|
||||
OnesOp = 0x00FF,
|
||||
|
||||
BytePrefix = 0x000A,
|
||||
WordPrefix = 0x000B,
|
||||
DWordPrefix = 0x000C,
|
||||
QWordPrefix = 0x000E,
|
||||
StringPrefix = 0x000D,
|
||||
|
||||
RevisionOp = 0x5B30,
|
||||
DebugOp = 0x5B31,
|
||||
|
||||
AliasOp = 0x0006,
|
||||
NameOp = 0x0008,
|
||||
ScopeOp = 0x0010,
|
||||
BufferOp = 0x0011,
|
||||
PackageOp = 0x0012,
|
||||
VarPackageOp = 0x0013,
|
||||
MethodOp = 0x0014,
|
||||
ExternalOp = 0x0015,
|
||||
MutexOp = 0x5B01,
|
||||
EventOp = 0x5B02,
|
||||
TimerOp = 0x5B33,
|
||||
OpRegionOp = 0x5B80,
|
||||
FieldOp = 0x5B81,
|
||||
DeviceOp = 0x5B82,
|
||||
// NOTE: This opcode is missing in the latest spec but is still being used.
|
||||
ProcessorOp = 0x5B83,
|
||||
PowerResOp = 0x5B84,
|
||||
ThermalZoneOp = 0x5B85,
|
||||
IndexFieldOp = 0x5B86,
|
||||
BankFieldOp = 0x5B87,
|
||||
DataRegionOp = 0x5B88,
|
||||
|
||||
CondRefOfOp = 0x5B12,
|
||||
CreateFieldOp = 0x5B13,
|
||||
LoadTableOp = 0x5B1F,
|
||||
LoadOp = 0x5B20,
|
||||
StallOp = 0x5B21,
|
||||
SleepOp = 0x5B22,
|
||||
AcquireOp = 0x5B23,
|
||||
SignalOp = 0x5B24,
|
||||
WaitOp = 0x5B25,
|
||||
ResetOp = 0x5B26,
|
||||
ReleaseOp = 0x5B27,
|
||||
FromBCDOp = 0x5B28,
|
||||
ToBCDOp = 0x5B29,
|
||||
FatalOp = 0x5B32,
|
||||
StoreOp = 0x70,
|
||||
RefOfOp = 0x71,
|
||||
|
||||
AddOp = 0x72,
|
||||
ConcatOp = 0x73,
|
||||
SubtractOp = 0x74,
|
||||
IncrementOp = 0x75,
|
||||
DecrementOp = 0x76,
|
||||
MultiplyOp = 0x77,
|
||||
DivideOp = 0x78,
|
||||
ShiftLeftOp = 0x79,
|
||||
ShiftRightOp = 0x7A,
|
||||
AndOp = 0x7B,
|
||||
NandOp = 0x7C,
|
||||
OrOp = 0x7D,
|
||||
NorOp = 0x7E,
|
||||
XorOp = 0x7F,
|
||||
|
||||
NotOp = 0x80,
|
||||
FindSetLeftBitOp = 0x81,
|
||||
FindSetRightBitOp = 0x82,
|
||||
|
||||
DerefOfOp = 0x83,
|
||||
ConcatResOp = 0x84,
|
||||
ModOp = 0x85,
|
||||
NotifyOp = 0x86,
|
||||
SizeOfOp = 0x87,
|
||||
IndexOp = 0x88,
|
||||
MatchOp = 0x89,
|
||||
ObjectTypeOp = 0x8E,
|
||||
|
||||
LAndOp = 0x90,
|
||||
LOrOp = 0x91,
|
||||
LNotOp = 0x92,
|
||||
LEqualOp = 0x93,
|
||||
LGreaterOp = 0x94,
|
||||
LLessOp = 0x95,
|
||||
|
||||
ToBufferOp = 0x96,
|
||||
ToDecimalStringOp = 0x97,
|
||||
ToHexStringOp = 0x98,
|
||||
ToIntegerOp = 0x99,
|
||||
ToStringOp = 0x9C,
|
||||
|
||||
CreateBitFieldOp = 0x8D,
|
||||
CreateByteFieldOp = 0x8C,
|
||||
CreateWordFieldOp = 0x8B,
|
||||
CreateDWordFieldOp = 0x8A,
|
||||
CreateQWordFieldOp = 0x8F,
|
||||
|
||||
CopyObjectOp = 0x9D,
|
||||
MidOp = 0x9E,
|
||||
ContinueOp = 0x9F,
|
||||
IfOp = 0xA0,
|
||||
ElseOp = 0xA1,
|
||||
WhileOp = 0xA2,
|
||||
NoopOp = 0xA3,
|
||||
ReturnOp = 0xA4,
|
||||
BreakOp = 0xA5,
|
||||
BreakPointOp = 0xCC,
|
||||
|
||||
Local0Op = 0x60,
|
||||
Local1Op = 0x61,
|
||||
Local2Op = 0x62,
|
||||
Local3Op = 0x63,
|
||||
Local4Op = 0x64,
|
||||
Local5Op = 0x65,
|
||||
Local6Op = 0x66,
|
||||
Local7Op = 0x67,
|
||||
Arg0Op = 0x68,
|
||||
Arg1Op = 0x69,
|
||||
Arg2Op = 0x6A,
|
||||
Arg3Op = 0x6B,
|
||||
Arg4Op = 0x6C,
|
||||
Arg5Op = 0x6D,
|
||||
Arg6Op = 0x6E,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
class AK::Traits<::ACPI::Structures::SDTHeader> : public DefaultTraits<::ACPI::Structures::SDTHeader> {
|
||||
public:
|
||||
static constexpr bool is_trivially_serializable() { return true; }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Formatter<::ACPI::AML::Opcode> : AK::Formatter<FormatString> {
|
||||
ErrorOr<void> format(FormatBuilder& builder, ::ACPI::AML::Opcode opcode)
|
||||
{
|
||||
return AK::Formatter<FormatString>::format(builder, "{:04X}"sv, static_cast<u16>(opcode));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* Copyright (c) 2025, RatcheT2497 <[email protected]>
|
||||
* Copyright (c) 2026, Undefine <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <LibACPI/Definitions.h>
|
||||
#include <LibACPI/Interpreter.h>
|
||||
#include <LibACPI/Node.h>
|
||||
#include <LibACPI/Object.h>
|
||||
#include <LibACPI/OpcodeFilters.h>
|
||||
#include <LibACPI/Table.h>
|
||||
|
||||
#ifdef KERNEL
|
||||
# define warnln dbgln
|
||||
# define outln dbgln
|
||||
# define DEBUG_LOG(...)
|
||||
#else
|
||||
# define DEBUG_LOG(...) warnln_if(AML_DEBUG, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define INTERPRETER_ERROR(simple, error, message, ...) \
|
||||
do { \
|
||||
warnln("ACPI: Interpreter::{}: " message, __func__ __VA_OPT__(, ) __VA_ARGS__); \
|
||||
return Error::from_string_view_or_print_error_and_return_errno(simple##sv, error); \
|
||||
} while (0);
|
||||
|
||||
namespace ACPI::AML {
|
||||
|
||||
ErrorOr<void> ScopeFrame::insert_node(NameString const& path, NonnullRefPtr<Node> node)
|
||||
{
|
||||
auto target = path.type() == NameString::Type::Relative ? scope() : root();
|
||||
DEBUG_LOG("ACPI: Inserting node with name {} at path {}", node->name().to_string_view(), path.to_string());
|
||||
|
||||
if (path.segment_count() < 1)
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Path must have at least one segment!"sv, EINVAL);
|
||||
|
||||
for (size_t i = 0; i < path.segment_count() - 1; i++) {
|
||||
auto name = TRY(path.segment(i));
|
||||
target = TRY(target->find_child(name));
|
||||
}
|
||||
|
||||
auto basename = TRY(path.basename());
|
||||
TRY(target->insert_child(basename, node));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<Interpreter>> Interpreter::create()
|
||||
{
|
||||
auto root = TRY(Interpreter::create_root_namespace());
|
||||
return TRY(adopt_nonnull_own_or_enomem(new (nothrow) Interpreter(root)));
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Node>> Interpreter::find_node(NameString const& path, NonnullRefPtr<Node> scope) const
|
||||
{
|
||||
RefPtr<Node> current_scope = scope;
|
||||
|
||||
bool search_rules_apply = path.type() == NameString::Type::Relative && path.segment_count() == 1 && path.parent_index() == 0;
|
||||
|
||||
if (path.type() == NameString::Type::Relative && path.parent_index() > 0) {
|
||||
for (size_t i = 0; i < path.parent_index(); i++) {
|
||||
if (current_scope == m_namespace_root)
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Can't go higher than root!"sv, EINVAL);
|
||||
|
||||
if (auto strong = current_scope->parent().strong_ref())
|
||||
current_scope = strong;
|
||||
else
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Reached a dangling node!"sv, EINVAL);
|
||||
}
|
||||
} else if (path.type() == NameString::Type::Absolute) {
|
||||
current_scope = m_namespace_root;
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/05_ACPI_Software_Programming_Model.html#acpi-namespace
|
||||
if (search_rules_apply) {
|
||||
// Path is a single name segment, is relative and has no depth.
|
||||
// Crawl upwards from the current scope and try finding a node with the given name.
|
||||
auto node_name = TRY(path.segment(0));
|
||||
while (!current_scope.is_null()) {
|
||||
auto found = current_scope->find_child(node_name);
|
||||
if (!found.is_error())
|
||||
return found.release_value();
|
||||
|
||||
if (current_scope == m_namespace_root) {
|
||||
warnln("ACPI: Could not find node with name {}!", node_name.to_string_view());
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Node not found!"sv, EINVAL);
|
||||
}
|
||||
|
||||
if (auto strong = current_scope->parent().strong_ref())
|
||||
current_scope = strong;
|
||||
else
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Reached a dangling node!"sv, EINVAL);
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// Search crawls down from root via name segments.
|
||||
for (size_t i = 0; i < path.segment_count(); i++) {
|
||||
auto node_name = TRY(path.segment(i));
|
||||
auto found = current_scope->find_child(node_name);
|
||||
if (found.is_error()) {
|
||||
// FIXME: Warn without allocating any string.
|
||||
warnln("ACPI: Could not find node at path {}!", path.to_string());
|
||||
return found.release_error();
|
||||
}
|
||||
|
||||
current_scope = found.release_value();
|
||||
}
|
||||
|
||||
return current_scope.release_nonnull();
|
||||
}
|
||||
|
||||
ErrorOr<void> Interpreter::walk_tree(NonnullRefPtr<Node> tree_root, Function<ErrorOr<void>(NonnullRefPtr<Node>, u32)> callback) const
|
||||
{
|
||||
struct Entry {
|
||||
NonnullRefPtr<Node> node;
|
||||
size_t depth;
|
||||
};
|
||||
|
||||
Vector<Entry> stack;
|
||||
TRY(stack.try_append({ move(tree_root), 0 }));
|
||||
|
||||
while (!stack.is_empty()) {
|
||||
auto entry = stack.take_last();
|
||||
|
||||
TRY(callback(entry.node, entry.depth));
|
||||
|
||||
auto const& children = entry.node->children();
|
||||
for (size_t i = children.size(); i-- > 0;)
|
||||
TRY(stack.try_append({ children[i], entry.depth + 1 }));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> Interpreter::print_namespace() const
|
||||
{
|
||||
TRY(walk_tree(m_namespace_root, [&](NonnullRefPtr<Node> node, u32 depth) -> ErrorOr<void> {
|
||||
StringBuilder sb;
|
||||
for (u32 i = 0; i < depth; i++)
|
||||
TRY(sb.try_appendff("| "));
|
||||
|
||||
auto name = node->name().to_string_view();
|
||||
TRY(sb.try_appendff("{}: ", name));
|
||||
TRY(node->write_description(sb));
|
||||
outln("{}", sb.string_view());
|
||||
|
||||
return {};
|
||||
}));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void Interpreter::debug_print_header(TableStream& stream, size_t operand_index)
|
||||
{
|
||||
#ifndef KERNEL
|
||||
if constexpr (AML_DEBUG) {
|
||||
warn("ACPI: {:08X} ", stream.offset());
|
||||
for (size_t i = 0; i < (m_opcode_frames.size() - 1) - (m_state == State::FinalizingOpcode ? 1 : 0); i++) {
|
||||
warn("| ");
|
||||
}
|
||||
|
||||
switch (m_state) {
|
||||
case State::ReadingOpcode:
|
||||
warn("ini");
|
||||
break;
|
||||
case State::ReadingOperand:
|
||||
warn("opr {}", operand_index);
|
||||
break;
|
||||
case State::FinalizingOpcode:
|
||||
warn("fin");
|
||||
break;
|
||||
}
|
||||
warn(" ");
|
||||
}
|
||||
#else
|
||||
(void)stream;
|
||||
(void)operand_index;
|
||||
#endif
|
||||
}
|
||||
|
||||
ErrorOr<Object> Interpreter::finalize_value_opcode(OpcodeFrame& frame)
|
||||
{
|
||||
switch (frame.opcode()) {
|
||||
case Opcode::ZeroOp:
|
||||
return Object(u64(0));
|
||||
case Opcode::OneOp:
|
||||
return Object(u64(1));
|
||||
|
||||
case Opcode::BytePrefix:
|
||||
case Opcode::WordPrefix:
|
||||
case Opcode::DWordPrefix:
|
||||
case Opcode::QWordPrefix:
|
||||
VERIFY(frame.operand(0).is<u64>());
|
||||
return frame.operand(0);
|
||||
|
||||
default:
|
||||
INTERPRETER_ERROR("Unimplemented!", EINVAL, "Unimplemented value opcode {}!", frame.opcode());
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<void> Interpreter::finalize_term_opcode(OpcodeFrame& frame)
|
||||
{
|
||||
switch (frame.opcode()) {
|
||||
case Opcode::ScopeOp: {
|
||||
auto length = TRY(frame.operand(0).as<u64>());
|
||||
auto path = TRY(frame.operand(1).as<NameString>());
|
||||
auto scope = TRY(find_node(path, m_scope_frames.last().scope()));
|
||||
|
||||
auto scope_frame = ScopeFrame(m_namespace_root, scope, frame.start(), frame.start() + length + 1, m_loaded_tables.size() - 1);
|
||||
TRY(m_scope_frames.try_append(scope_frame));
|
||||
return {};
|
||||
}
|
||||
|
||||
case Opcode::NameOp: {
|
||||
auto path = TRY(frame.operand(0).as<NameString>());
|
||||
auto data = frame.operand(1);
|
||||
|
||||
auto node = TRY(try_make_ref_counted<NameNode>(data));
|
||||
TRY(m_scope_frames.last().insert_node(path, node));
|
||||
return {};
|
||||
}
|
||||
|
||||
default:
|
||||
INTERPRETER_ERROR("Unimplemented!", EINVAL, "Unimplemented term opcode {}!", frame.opcode());
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<IterationDecision> Interpreter::read_operand(TableStream& stream, OperandType operand)
|
||||
{
|
||||
auto& top_opcode_frame = m_opcode_frames.last();
|
||||
|
||||
switch (operand) {
|
||||
case OperandType::NameString: {
|
||||
auto value = TRY(stream.read_name_string());
|
||||
DEBUG_LOG("Reading namestring... ({})", value.to_string());
|
||||
TRY(top_opcode_frame.finish_operand(value));
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
case OperandType::PackageLength: {
|
||||
auto length = TRY(stream.read_package_length());
|
||||
DEBUG_LOG("Reading package length... ({:#X})", length);
|
||||
TRY(top_opcode_frame.finish_operand(static_cast<u64>(length)));
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
case OperandType::ByteData: {
|
||||
auto value = TRY(stream.read_value<u8>());
|
||||
DEBUG_LOG("Reading byte... ({})", value);
|
||||
TRY(top_opcode_frame.finish_operand(static_cast<u64>(value)));
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
case OperandType::WordData: {
|
||||
auto value = TRY(stream.read_value<u16>());
|
||||
DEBUG_LOG("Reading word... ({})", value);
|
||||
TRY(top_opcode_frame.finish_operand(static_cast<u64>(value)));
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
case OperandType::DWordData: {
|
||||
auto value = TRY(stream.read_value<u32>());
|
||||
DEBUG_LOG("Reading dword... ({})", value);
|
||||
TRY(top_opcode_frame.finish_operand(static_cast<u64>(value)));
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
case OperandType::QWordData: {
|
||||
auto value = TRY(stream.read_value<u64>());
|
||||
DEBUG_LOG("Reading qword... ({})", value);
|
||||
TRY(top_opcode_frame.finish_operand(value));
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
case OperandType::TermArg:
|
||||
case OperandType::DataRefObject:
|
||||
DEBUG_LOG("Reading complex object {}...", to_underlying(operand));
|
||||
|
||||
m_state = State::ReadingOpcode;
|
||||
return IterationDecision::Continue;
|
||||
|
||||
default:
|
||||
DEBUG_LOG("Failure!");
|
||||
INTERPRETER_ERROR("Unimplemented operand type!", EINVAL, "Operand type {} unimplemented!", to_underlying(operand));
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<IterationDecision> Interpreter::cycle(TableStream& stream)
|
||||
{
|
||||
// Stacks have already been initialized.
|
||||
auto& top_opcode_frame = m_opcode_frames.last();
|
||||
|
||||
switch (m_state) {
|
||||
case Interpreter::State::ReadingOpcode: {
|
||||
// Pop all scope frames that we've exited.
|
||||
// Do it here in case any pending opcodes need to be finalized at EOF.
|
||||
while (m_scope_frames.size() != 0) {
|
||||
auto& scope_frame = m_scope_frames.last();
|
||||
if (stream.offset() < scope_frame.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
m_scope_frames.take_last();
|
||||
}
|
||||
|
||||
if (stream.is_eof())
|
||||
return IterationDecision::Break;
|
||||
|
||||
debug_print_header(stream);
|
||||
|
||||
auto start = stream.offset();
|
||||
if (is_namestring_start(TRY(stream.peek_byte()))) {
|
||||
auto name = TRY(stream.read_name_string());
|
||||
DEBUG_LOG("{}", name.to_string());
|
||||
|
||||
warnln("ACPI: Reading name string as value is not yet supported");
|
||||
return Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
auto opcode = TRY(stream.read_opcode());
|
||||
if (!top_opcode_frame.filter_opcode_for_next_operand(opcode)) {
|
||||
DEBUG_LOG("Failure!");
|
||||
INTERPRETER_ERROR("Filtered opcode!", EINVAL, "Argument opcode {} filtered by opcode {}!", opcode, top_opcode_frame.opcode());
|
||||
}
|
||||
|
||||
DEBUG_LOG("Opcode: {}", opcode);
|
||||
|
||||
auto opcode_frame = TRY(OpcodeFrame::from_opcode(static_cast<Opcode>(opcode), start));
|
||||
TRY(m_opcode_frames.try_append(move(opcode_frame)));
|
||||
m_state = State::ReadingOperand;
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
case Interpreter::State::ReadingOperand: {
|
||||
auto operand = TRY(top_opcode_frame.begin_operand());
|
||||
if (operand == OperandType::End) {
|
||||
m_state = State::FinalizingOpcode;
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
debug_print_header(stream, top_opcode_frame.operand_count());
|
||||
return read_operand(stream, operand);
|
||||
}
|
||||
|
||||
case Interpreter::State::FinalizingOpcode: {
|
||||
debug_print_header(stream);
|
||||
|
||||
auto top_frame_value = m_opcode_frames.take_last();
|
||||
if (m_opcode_frames.size() > 1) {
|
||||
DEBUG_LOG("Finalizing argument {}", top_frame_value.opcode());
|
||||
|
||||
auto value = TRY(finalize_value_opcode(top_frame_value));
|
||||
TRY(m_opcode_frames.last().finish_operand(value));
|
||||
|
||||
m_state = State::ReadingOperand;
|
||||
} else {
|
||||
DEBUG_LOG("Finalizing term {}", top_frame_value.opcode());
|
||||
|
||||
TRY(finalize_term_opcode(top_frame_value));
|
||||
|
||||
m_state = State::ReadingOpcode;
|
||||
}
|
||||
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<Object> Interpreter::evaluate(NameString const& path)
|
||||
{
|
||||
auto node = TRY(find_node(path, m_namespace_root));
|
||||
return evaluate(node);
|
||||
}
|
||||
|
||||
ErrorOr<Object> Interpreter::evaluate(NonnullRefPtr<Node> node)
|
||||
{
|
||||
if (node->type() == 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());
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Node>> Interpreter::create_root_namespace()
|
||||
{
|
||||
auto namespace_root = TRY(try_make_ref_counted<ScopeNode>());
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/05_ACPI_Software_Programming_Model.html#predefined-root-namespaces
|
||||
TRY(namespace_root->insert_child("_GPE"sv, TRY(try_make_ref_counted<ScopeNode>())));
|
||||
TRY(namespace_root->insert_child("_PR_"sv, TRY(try_make_ref_counted<ScopeNode>())));
|
||||
TRY(namespace_root->insert_child("_SB_"sv, TRY(try_make_ref_counted<ScopeNode>())));
|
||||
TRY(namespace_root->insert_child("_SI_"sv, TRY(try_make_ref_counted<ScopeNode>())));
|
||||
TRY(namespace_root->insert_child("_TZ_"sv, TRY(try_make_ref_counted<ScopeNode>())));
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/05_ACPI_Software_Programming_Model.html#predefined-objects
|
||||
TRY(namespace_root->insert_child("_REV"sv, TRY(try_make_ref_counted<NameNode>(Object(u64(2))))));
|
||||
// FIXME: ACPI spec also mendates we insert _GL, _OSI, _OS, _DLM
|
||||
|
||||
return namespace_root;
|
||||
}
|
||||
|
||||
ErrorOr<void> Interpreter::load(ReadonlyBytes bytes)
|
||||
{
|
||||
TRY(m_loaded_tables.try_append(TableStream(bytes)));
|
||||
auto& stream = m_loaded_tables.last();
|
||||
|
||||
if (!validate_table(*reinterpret_cast<Structures::SDTHeader const*>(bytes.data())))
|
||||
INTERPRETER_ERROR("Incorrect checksum!", EINVAL, "Table checksum incorrect!");
|
||||
|
||||
auto block_header = TRY(stream.read_sdt_header());
|
||||
outln("ACPI: Interpreter::load[{}]: Loading table '{}' by OEM '{}'", m_loaded_tables.size() - 1, StringView(block_header.oem_table_id, 8), StringView(block_header.oem_id, 6));
|
||||
|
||||
auto root_scope_frame = ScopeFrame(m_namespace_root, m_namespace_root, stream.offset(), TRY(stream.size()), m_loaded_tables.size() - 1);
|
||||
TRY(m_scope_frames.try_append(move(root_scope_frame)));
|
||||
|
||||
auto root_opcode_frame = TRY(OpcodeFrame::root_frame());
|
||||
TRY(m_opcode_frames.try_append(move(root_opcode_frame)));
|
||||
|
||||
while (m_opcode_frames.size() > 0 && m_scope_frames.size() > 0) {
|
||||
if (TRY(cycle(stream)) == IterationDecision::Break) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_scope_frames.size() != 0 || m_opcode_frames.size() != 1)
|
||||
return Error::from_string_view_or_print_error_and_return_errno("ACPI: Interpreter::load: Unbalanced scope or opcode frames!"sv, EINVAL);
|
||||
|
||||
m_opcode_frames.take_last();
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2025, RatcheT2497 <[email protected]>
|
||||
* Copyright (c) 2026, Undefine <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibACPI/Node.h>
|
||||
#include <LibACPI/OpcodeFrame.h>
|
||||
#include <LibACPI/TableStream.h>
|
||||
|
||||
namespace ACPI::AML {
|
||||
|
||||
class ScopeFrame {
|
||||
public:
|
||||
ScopeFrame(NonnullRefPtr<Node> root, NonnullRefPtr<Node> scope, size_t start, size_t end, size_t table_index)
|
||||
: m_root(move(root))
|
||||
, m_scope(move(scope))
|
||||
, m_start(start)
|
||||
, m_end(end)
|
||||
, m_table_index(table_index)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<void> insert_node(NameString const& path, NonnullRefPtr<Node> node);
|
||||
|
||||
NonnullRefPtr<Node> root() { return m_root; }
|
||||
NonnullRefPtr<Node> scope() { return m_scope; }
|
||||
size_t start() const { return m_start; }
|
||||
size_t end() const { return m_end; }
|
||||
size_t size() const { return m_end - m_start; }
|
||||
|
||||
private:
|
||||
NonnullRefPtr<Node> m_root;
|
||||
NonnullRefPtr<Node> m_scope;
|
||||
|
||||
size_t m_start { 0 };
|
||||
size_t m_end { 0 };
|
||||
size_t m_table_index { 0 };
|
||||
};
|
||||
|
||||
class Interpreter {
|
||||
public:
|
||||
static ErrorOr<NonnullOwnPtr<Interpreter>> create();
|
||||
|
||||
ErrorOr<void> load(ReadonlyBytes bytes);
|
||||
ErrorOr<void> print_namespace() const;
|
||||
|
||||
ErrorOr<Object> evaluate(NameString const& path);
|
||||
|
||||
private:
|
||||
enum class State : u8 {
|
||||
ReadingOpcode,
|
||||
ReadingOperand,
|
||||
FinalizingOpcode,
|
||||
};
|
||||
|
||||
Interpreter(NonnullRefPtr<Node> namespace_root)
|
||||
: m_namespace_root(move(namespace_root))
|
||||
{
|
||||
}
|
||||
|
||||
static ErrorOr<NonnullRefPtr<Node>> create_root_namespace();
|
||||
|
||||
ErrorOr<NonnullRefPtr<Node>> find_node(NameString const& path, NonnullRefPtr<Node> scope) const;
|
||||
ErrorOr<void> walk_tree(NonnullRefPtr<Node> tree_root, Function<ErrorOr<void>(NonnullRefPtr<Node>, u32)> callback) const;
|
||||
|
||||
ErrorOr<Object> evaluate(NonnullRefPtr<Node> node);
|
||||
|
||||
ErrorOr<IterationDecision> cycle(TableStream& stream);
|
||||
ErrorOr<IterationDecision> read_operand(TableStream& stream, OperandType operand);
|
||||
|
||||
ErrorOr<Object> finalize_value_opcode(OpcodeFrame& frame);
|
||||
ErrorOr<void> finalize_term_opcode(OpcodeFrame& frame);
|
||||
|
||||
void debug_print_header(TableStream& stream, size_t operand_index = 0);
|
||||
|
||||
State m_state { State::ReadingOpcode };
|
||||
|
||||
AK::Vector<TableStream> m_loaded_tables;
|
||||
Vector<ScopeFrame> m_scope_frames;
|
||||
Vector<OpcodeFrame> m_opcode_frames;
|
||||
|
||||
NonnullRefPtr<Node> m_namespace_root;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (c) 2025, RatcheT2497 <[email protected]>
|
||||
* Copyright (c) 2026, Undefine <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <Kernel/Library/KString.h>
|
||||
#include <LibACPI/Definitions.h>
|
||||
#include <LibACPI/NameString.h>
|
||||
#include <LibACPI/OpcodeFilters.h>
|
||||
#include <LibACPI/TableStream.h>
|
||||
|
||||
#ifdef KERNEL
|
||||
# define warnln dbgln
|
||||
#endif
|
||||
|
||||
namespace ACPI::AML {
|
||||
|
||||
NameSegment::NameSegment(Array<char, 4> value)
|
||||
: m_data(value)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<NameSegment> NameSegment::from_string_view(StringView view)
|
||||
{
|
||||
return NameSegment::from_bytes(view.bytes());
|
||||
}
|
||||
|
||||
ErrorOr<NameSegment> NameSegment::from_bytes(ReadonlyBytes span)
|
||||
{
|
||||
if (span.size() != 4)
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Name segment has invalid length"sv, EINVAL);
|
||||
|
||||
if (!is_lead_name_char(span[0]) || !is_name_char(span[1]) || !is_name_char(span[2]) || !is_name_char(span[3])) {
|
||||
warnln("ACPI: Invalid character found inside name segment: '{}'", span);
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Invalid character found inside name segment."sv, EINVAL);
|
||||
}
|
||||
|
||||
return NameSegment({ (char)span[0], (char)span[1], (char)span[2], (char)span[3] });
|
||||
}
|
||||
|
||||
StringView NameSegment::to_string_view() const
|
||||
{
|
||||
StringView view(m_data.data(), 4);
|
||||
return view;
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/19_ASL_Reference.html#asl-name-and-pathname-terms
|
||||
ErrorOr<NameString> NameString::from_string_view(StringView input)
|
||||
{
|
||||
Type type = Type::Relative;
|
||||
size_t depth = 0;
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
if (input[i] == '\\') {
|
||||
type = NameString::Type::Absolute;
|
||||
if (i >= input.length())
|
||||
return NameString(type, depth);
|
||||
|
||||
i++;
|
||||
} else if (input[i] == '^') {
|
||||
type = NameString::Type::Relative;
|
||||
while (i < input.length() && input[i] == '^') {
|
||||
depth++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= input.length())
|
||||
return NameString(type, depth);
|
||||
|
||||
// Validate segments, alongside separators.
|
||||
Vector<NameSegment, 4> segments;
|
||||
for (; i < input.length(); i += 5) {
|
||||
if (i + 4 > input.length())
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Incomplete name segment."sv, EINVAL);
|
||||
|
||||
TRY(segments.try_append(TRY(NameSegment::from_string_view(input.substring_view(i, 4)))));
|
||||
|
||||
if (i + 4 < input.length()) {
|
||||
if (input[i + 4] != '.')
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Invalid name segment continuation."sv, EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
return NameString(type, depth, move(segments));
|
||||
}
|
||||
|
||||
ErrorOr<NameString> NameString::clone() const
|
||||
{
|
||||
return NameString(m_type, m_parent_index, TRY(m_segments.clone()));
|
||||
}
|
||||
|
||||
ErrorOr<NameSegment> NameString::basename() const
|
||||
{
|
||||
if (m_segments.is_empty())
|
||||
return Error::from_string_view_or_print_error_and_return_errno("NullNameString has no base name!"sv, EINVAL);
|
||||
|
||||
return segment(m_segments.size() - 1);
|
||||
}
|
||||
|
||||
#ifdef KERNEL
|
||||
ErrorOr<NonnullOwnPtr<Kernel::KString>> NameString::to_string() const
|
||||
#else
|
||||
ErrorOr<String> NameString::to_string() const
|
||||
#endif
|
||||
{
|
||||
StringBuilder builder;
|
||||
if (m_type == NameString::Type::Absolute) {
|
||||
TRY(builder.try_append('\\'));
|
||||
} else if (m_parent_index > 0) {
|
||||
TRY(builder.try_append_repeated('^', m_parent_index));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_segments.size(); i++) {
|
||||
TRY(builder.try_append(TRY(segment(i)).to_string_view()));
|
||||
|
||||
if (i != m_segments.size() - 1) {
|
||||
TRY(builder.try_append('.'));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef KERNEL
|
||||
return Kernel::KString::try_create(builder.string_view());
|
||||
#else
|
||||
return builder.to_string();
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2025, RatcheT2497 <[email protected]>
|
||||
* Copyright (c) 2026, Undefine <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Vector.h>
|
||||
#ifdef KERNEL
|
||||
# include <Kernel/Library/KString.h>
|
||||
#else
|
||||
# include <AK/String.h>
|
||||
#endif
|
||||
|
||||
namespace ACPI::AML {
|
||||
|
||||
class TableStream;
|
||||
|
||||
class NameSegment {
|
||||
public:
|
||||
NameSegment() = default;
|
||||
|
||||
static ErrorOr<NameSegment> from_string_view(StringView view);
|
||||
static ErrorOr<NameSegment> from_bytes(ReadonlyBytes span);
|
||||
|
||||
friend bool operator==(NameSegment const& l, NameSegment const& r) { return l.m_data == r.m_data; }
|
||||
friend bool operator!=(NameSegment const& l, NameSegment const& r) { return l.m_data != r.m_data; }
|
||||
|
||||
StringView to_string_view() const;
|
||||
|
||||
private:
|
||||
NameSegment(Array<char, 4> value);
|
||||
|
||||
Array<char, 4> m_data { '_', '_', '_', '_' };
|
||||
};
|
||||
|
||||
class NameString {
|
||||
public:
|
||||
enum class Type : u8 {
|
||||
Absolute,
|
||||
Relative,
|
||||
};
|
||||
|
||||
static ErrorOr<NameString> from_string_view(StringView input);
|
||||
|
||||
NameString(Type type, size_t depth)
|
||||
: m_type(type)
|
||||
, m_parent_index(depth)
|
||||
{
|
||||
}
|
||||
|
||||
NameString(Type type, size_t depth, Vector<NameSegment, 4> segments)
|
||||
: m_type(type)
|
||||
, m_parent_index(depth)
|
||||
, m_segments(move(segments))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<NameString> clone() const;
|
||||
|
||||
ErrorOr<NameSegment> basename() const;
|
||||
|
||||
#ifdef KERNEL
|
||||
ErrorOr<NonnullOwnPtr<Kernel::KString>> to_string() const;
|
||||
#else
|
||||
ErrorOr<String> to_string() const;
|
||||
#endif
|
||||
|
||||
ErrorOr<NameSegment> segment(size_t index) const { return m_segments[index]; }
|
||||
size_t segment_count() const { return m_segments.size(); }
|
||||
|
||||
Type type() const { return m_type; }
|
||||
size_t parent_index() const { return m_parent_index; }
|
||||
|
||||
private:
|
||||
Type m_type { Type::Relative };
|
||||
size_t m_parent_index { 0 };
|
||||
|
||||
Vector<NameSegment, 4> m_segments;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2025, RatcheT2497 <[email protected]>
|
||||
* Copyright (c) 2026, Undefine <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <LibACPI/NameString.h>
|
||||
#include <LibACPI/Node.h>
|
||||
|
||||
#ifdef KERNEL
|
||||
# define warnln dbgln
|
||||
#endif
|
||||
|
||||
namespace ACPI::AML {
|
||||
|
||||
ErrorOr<NonnullRefPtr<Node>> Node::find_child(NameSegment name) const
|
||||
{
|
||||
for (auto const& child : m_children) {
|
||||
if (child->name() == name)
|
||||
return child;
|
||||
}
|
||||
|
||||
warnln("ACPI: Node::find_child: Child {} does not exist relative to node {}!", name.to_string_view(), m_name.to_string_view());
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Child does not exist."sv, EINVAL);
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Node>> Node::find_child(StringView name) const
|
||||
{
|
||||
if (name.length() != 4)
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Name must have a length of 4."sv, EINVAL);
|
||||
|
||||
return find_child(TRY(NameSegment::from_string_view(name)));
|
||||
}
|
||||
|
||||
ErrorOr<void> Node::insert_child(NameSegment name, NonnullRefPtr<Node> node)
|
||||
{
|
||||
/// FIXME: Duplicate name detection.
|
||||
node->m_name = name;
|
||||
node->m_parent = this;
|
||||
TRY(m_children.try_append(node));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> Node::insert_child(StringView name, NonnullRefPtr<Node> node)
|
||||
{
|
||||
if (name.length() != 4)
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Name must have a length of 4."sv, EINVAL);
|
||||
|
||||
return insert_child(TRY(NameSegment::from_string_view(name)), node);
|
||||
}
|
||||
|
||||
ErrorOr<void> Node::write_description(StringBuilder& builder)
|
||||
{
|
||||
TRY(builder.try_append(kind()));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> NameNode::write_description(StringBuilder& builder)
|
||||
{
|
||||
if (m_data.is<u64>()) {
|
||||
auto value = MUST(m_data.as<u64>());
|
||||
TRY(builder.try_appendff("(Decimal: {}, Hexadecimal: {:X})", value, value));
|
||||
} else if (m_data.is<NameString>()) {
|
||||
auto value = MUST(m_data.as<NameString>());
|
||||
TRY(builder.try_appendff("{}"sv, TRY(value.to_string())));
|
||||
} else {
|
||||
TRY(builder.try_append("Invalid"sv));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2025, RatcheT2497 <[email protected]>
|
||||
* Copyright (c) 2026, Undefine <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <AK/AtomicRefCounted.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <LibACPI/Definitions.h>
|
||||
#include <LibACPI/NameString.h>
|
||||
#include <LibACPI/Object.h>
|
||||
|
||||
namespace ACPI::AML {
|
||||
|
||||
enum class NodeType : u8 {
|
||||
Untyped,
|
||||
Scope,
|
||||
Name,
|
||||
};
|
||||
|
||||
class Node : public AtomicRefCounted<Node>
|
||||
, public Weakable<Node> {
|
||||
public:
|
||||
ErrorOr<NonnullRefPtr<Node>> find_child(NameSegment name) const;
|
||||
ErrorOr<NonnullRefPtr<Node>> find_child(StringView name) const;
|
||||
|
||||
ErrorOr<void> insert_child(NameSegment name, NonnullRefPtr<Node> node);
|
||||
ErrorOr<void> insert_child(StringView name, NonnullRefPtr<Node> node);
|
||||
|
||||
NameSegment const& name() const { return m_name; }
|
||||
|
||||
WeakPtr<Node> parent() const { return m_parent; }
|
||||
Vector<NonnullRefPtr<Node>> const& children() const { return m_children; }
|
||||
|
||||
virtual ErrorOr<void> write_description(StringBuilder& builder);
|
||||
|
||||
virtual NodeType type() const { return NodeType::Untyped; }
|
||||
virtual StringView kind() const { return "Node"sv; }
|
||||
virtual ~Node() = default;
|
||||
|
||||
protected:
|
||||
Node(RefPtr<Node> parent)
|
||||
: m_parent(move(parent))
|
||||
{
|
||||
}
|
||||
|
||||
NameSegment m_name;
|
||||
|
||||
WeakPtr<Node> m_parent;
|
||||
Vector<NonnullRefPtr<Node>> m_children;
|
||||
};
|
||||
|
||||
class ScopeNode : public Node {
|
||||
public:
|
||||
ScopeNode()
|
||||
: Node(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual NodeType type() const override { return NodeType::Scope; }
|
||||
virtual StringView kind() const override { return "Scope"sv; }
|
||||
};
|
||||
|
||||
class NameNode : public Node {
|
||||
public:
|
||||
NameNode(Object value)
|
||||
: Node(nullptr)
|
||||
, m_data(move(value))
|
||||
{
|
||||
}
|
||||
|
||||
virtual ErrorOr<void> write_description(StringBuilder& builder) override;
|
||||
|
||||
virtual NodeType type() const override { return NodeType::Name; }
|
||||
virtual StringView kind() const override { return "Name"sv; }
|
||||
|
||||
Object& data() { return m_data; }
|
||||
|
||||
protected:
|
||||
Object m_data;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2025, RatcheT2497 <[email protected]>
|
||||
* Copyright (c) 2026, Undefine <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibACPI/NameString.h>
|
||||
|
||||
namespace ACPI::AML {
|
||||
|
||||
template<typename T>
|
||||
concept IsNodeData = OneOf<T, u64, NameString>;
|
||||
|
||||
class Object {
|
||||
public:
|
||||
constexpr Object()
|
||||
: m_data(AK::Empty {})
|
||||
{
|
||||
}
|
||||
|
||||
template<IsNodeData T>
|
||||
constexpr Object(T const& value)
|
||||
: m_data(value)
|
||||
{
|
||||
}
|
||||
|
||||
template<IsNodeData T>
|
||||
constexpr bool is() const
|
||||
{
|
||||
return m_data.has<T>();
|
||||
}
|
||||
|
||||
template<IsNodeData T>
|
||||
constexpr ErrorOr<T> as() const
|
||||
{
|
||||
if (m_data.has<T>())
|
||||
return m_data.get<T>();
|
||||
|
||||
// TODO: Implement casting according both to ACPI spec and whatever Windows does.
|
||||
// https://uefi.org/specs/ACPI/6.6/19_ASL_Reference.html#implicit-source-operand-conversion
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Can not cast given type!"sv, EINVAL);
|
||||
}
|
||||
|
||||
protected:
|
||||
Variant<AK::Empty, u64, NameString> m_data;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (c) 2025, RatcheT2497 <[email protected]>
|
||||
* Copyright (c) 2026, Undefine <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibACPI/OpcodeFilters.h>
|
||||
|
||||
namespace ACPI::AML {
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#namespacemodifierobj
|
||||
bool namespace_modifier_object_filter(Opcode opcode)
|
||||
{
|
||||
// NameSpaceModifierObj := DefAlias | DefName | DefScope
|
||||
switch (opcode) {
|
||||
case Opcode::AliasOp:
|
||||
case Opcode::NameOp:
|
||||
case Opcode::ScopeOp:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#namedobj
|
||||
bool named_object_filter(Opcode opcode)
|
||||
{
|
||||
// NamedObj := DefBankField | DefCreateBitField | DefCreateByteField | DefCreateDWordField | DefCreateField | DefCreateQWordField | DefCreateWordField | DefDataRegion | DefExternal | DefOpRegion | DefPowerRes | DefThermalZone
|
||||
|
||||
// FieldOp missing from 6.6 docs, but given the presence of the DefXYZField
|
||||
// opcodes here, I'm adding it in here.
|
||||
// DeviceOp is also missing. Adding it here too.
|
||||
// MutexOp likewise missing. It's named and creates an object, so here it be.
|
||||
|
||||
// ProcessorOp found here in 6.3 docs.
|
||||
switch (opcode) {
|
||||
case Opcode::BankFieldOp:
|
||||
case Opcode::CreateBitFieldOp:
|
||||
case Opcode::CreateByteFieldOp:
|
||||
case Opcode::CreateDWordFieldOp:
|
||||
case Opcode::CreateFieldOp:
|
||||
case Opcode::CreateQWordFieldOp:
|
||||
case Opcode::CreateWordFieldOp:
|
||||
case Opcode::DataRegionOp:
|
||||
case Opcode::ExternalOp:
|
||||
case Opcode::OpRegionOp:
|
||||
case Opcode::PowerResOp:
|
||||
case Opcode::ThermalZoneOp:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#object
|
||||
bool object_filter(Opcode opcode)
|
||||
{
|
||||
// Object := NameSpaceModifierObj | NamedObj
|
||||
return namespace_modifier_object_filter(opcode) || named_object_filter(opcode);
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#statementopcode
|
||||
bool statement_filter(Opcode opcode)
|
||||
{
|
||||
// StatementOpcode := DefBreak | DefBreakPoint | DefContinue | DefFatal | DefIfElse | DefNoop | DefNotify | DefRelease | DefReset | DefReturn | DefSignal | DefSleep | DefStall | DefWhile
|
||||
switch (opcode) {
|
||||
case Opcode::BreakOp:
|
||||
case Opcode::BreakPointOp:
|
||||
case Opcode::ContinueOp:
|
||||
case Opcode::FatalOp:
|
||||
case Opcode::IfOp:
|
||||
case Opcode::ElseOp:
|
||||
case Opcode::NoopOp:
|
||||
case Opcode::NotifyOp:
|
||||
case Opcode::ReleaseOp:
|
||||
case Opcode::ResetOp:
|
||||
case Opcode::ReturnOp:
|
||||
case Opcode::SignalOp:
|
||||
case Opcode::SleepOp:
|
||||
case Opcode::StallOp:
|
||||
case Opcode::WhileOp:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#expressionopcode
|
||||
bool expression_filter(Opcode opcode)
|
||||
{
|
||||
// ExpressionOpcode := DefAcquire | DefAdd | DefAnd | DefBuffer | DefConcat | DefConcatRes | DefCondRefOf | DefCopyObject | DefDecrement | DefDerefOf | DefDivide | DefFindSetLeftBit | DefFindSetRightBit | DefFromBCD | DefIncrement | DefIndex | DefLAnd | DefLEqual | DefLGreater | DefLGreaterEqual | DefLLess | DefLLessEqual | DefMid | DefLNot | DefLNotEqual | DefLoadTable | DefLOr | DefMatch | DefMod | DefMultiply | DefNAnd | DefNOr | DefNot | DefObjectType | DefOr | DefPackage | DefVarPackage | DefRefOf | DefShiftLeft | DefShiftRight | DefSizeOf | DefStore | DefSubtract | DefTimer | DefToBCD | DefToBuffer | DefToDecimalString | DefToHexString | DefToInteger | DefToString | DefWait | DefXOr | MethodInvocation
|
||||
// FIXME: Add support for MethodInvocation.
|
||||
|
||||
switch (opcode) {
|
||||
case Opcode::AcquireOp:
|
||||
case Opcode::AddOp:
|
||||
case Opcode::AndOp:
|
||||
case Opcode::BufferOp:
|
||||
case Opcode::ConcatOp:
|
||||
case Opcode::ConcatResOp:
|
||||
case Opcode::CondRefOfOp:
|
||||
case Opcode::CopyObjectOp:
|
||||
case Opcode::DecrementOp:
|
||||
case Opcode::DerefOfOp:
|
||||
case Opcode::DivideOp:
|
||||
case Opcode::FindSetLeftBitOp:
|
||||
case Opcode::FindSetRightBitOp:
|
||||
case Opcode::FromBCDOp:
|
||||
case Opcode::IncrementOp:
|
||||
case Opcode::IndexOp:
|
||||
case Opcode::LAndOp:
|
||||
case Opcode::LEqualOp:
|
||||
case Opcode::LGreaterOp:
|
||||
case Opcode::LLessOp:
|
||||
case Opcode::LNotOp:
|
||||
case Opcode::LoadTableOp:
|
||||
case Opcode::LOrOp:
|
||||
case Opcode::MatchOp:
|
||||
case Opcode::MidOp:
|
||||
case Opcode::ModOp:
|
||||
case Opcode::MultiplyOp:
|
||||
case Opcode::NandOp:
|
||||
case Opcode::NorOp:
|
||||
case Opcode::NotOp:
|
||||
case Opcode::ObjectTypeOp:
|
||||
case Opcode::OrOp:
|
||||
case Opcode::PackageOp:
|
||||
case Opcode::VarPackageOp:
|
||||
case Opcode::RefOfOp:
|
||||
case Opcode::ShiftLeftOp:
|
||||
case Opcode::ShiftRightOp:
|
||||
case Opcode::SizeOfOp:
|
||||
case Opcode::StoreOp:
|
||||
case Opcode::SubtractOp:
|
||||
case Opcode::TimerOp:
|
||||
case Opcode::ToBCDOp:
|
||||
case Opcode::ToBufferOp:
|
||||
case Opcode::ToDecimalStringOp:
|
||||
case Opcode::ToHexStringOp:
|
||||
case Opcode::ToIntegerOp:
|
||||
case Opcode::ToStringOp:
|
||||
case Opcode::WaitOp:
|
||||
case Opcode::XorOp:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#constobj
|
||||
bool const_object_filter(Opcode opcode)
|
||||
{
|
||||
// ConstObj := ZeroOp | OneOp | OnesOp
|
||||
switch (opcode) {
|
||||
case Opcode::ZeroOp:
|
||||
case Opcode::OneOp:
|
||||
case Opcode::OnesOp:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#computationaldata
|
||||
bool computational_data_filter(Opcode opcode)
|
||||
{
|
||||
// ComputationalData := ByteConst | WordConst | DWordConst | QWordConst | String | ConstObj | RevisionOp | DefBuffer
|
||||
switch (opcode) {
|
||||
case Opcode::BytePrefix:
|
||||
case Opcode::WordPrefix:
|
||||
case Opcode::DWordPrefix:
|
||||
case Opcode::QWordPrefix:
|
||||
case Opcode::StringPrefix:
|
||||
case Opcode::RevisionOp:
|
||||
case Opcode::BufferOp:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return const_object_filter(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#dataobject
|
||||
bool data_object_filter(Opcode opcode)
|
||||
{
|
||||
// DataObject := ComputationalData | DefPackage | DefVarPackage
|
||||
switch (opcode) {
|
||||
case Opcode::PackageOp:
|
||||
case Opcode::VarPackageOp:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return computational_data_filter(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#datarefobject
|
||||
bool data_ref_object_filter(Opcode opcode)
|
||||
{
|
||||
// DataRefObject := DataObject | ObjectReference
|
||||
// FIXME: Add support for ObjectReference.
|
||||
return data_object_filter(opcode);
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#termobj
|
||||
bool term_filter(Opcode opcode)
|
||||
{
|
||||
// TermObj := Object | StatementOpcode | ExpressionOpcode
|
||||
return object_filter(opcode) || statement_filter(opcode) || expression_filter(opcode);
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#termarg
|
||||
bool term_arg_filter(Opcode opcode)
|
||||
{
|
||||
// TermArg := ExpressionOpcode | DataObject | ArgObj | LocalObj
|
||||
return expression_filter(opcode) || data_object_filter(opcode) || arg_obj_filter(opcode) || local_obj_filter(opcode);
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#argobj
|
||||
bool arg_obj_filter(Opcode opcode)
|
||||
{
|
||||
// ArgObj := Arg0Op | Arg1Op | Arg2Op | Arg3Op | Arg4Op | Arg5Op | Arg6Op
|
||||
return opcode >= Opcode::Arg0Op && opcode <= Opcode::Arg6Op;
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#localobj
|
||||
bool local_obj_filter(Opcode opcode)
|
||||
{
|
||||
// LocalObj := Local0Op | Local1Op | Local2Op | Local3Op | Local4Op | Local5Op | Local6Op | Local7Op
|
||||
return opcode >= Opcode::Local0Op && opcode <= Opcode::Local7Op;
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#leadnamechar
|
||||
bool is_lead_name_char(u8 c)
|
||||
{
|
||||
// LeadNameChar := ‘A’-‘Z’ | ‘_’
|
||||
return (c == '_') || (c >= 'A' && c <= 'Z');
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#namechar
|
||||
bool is_name_char(u8 c)
|
||||
{
|
||||
// NameChar := DigitChar | LeadNameChar
|
||||
return is_lead_name_char(c) || (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#namestring
|
||||
bool is_namestring_start(u8 c)
|
||||
{
|
||||
return is_lead_name_char(c) || (c == '^') || (c == '\\') || (c == DUAL_NAME_PREFIX) || (c == MULTI_NAME_PREFIX);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2025, RatcheT2497 <[email protected]>
|
||||
* Copyright (c) 2026, Undefine <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibACPI/Definitions.h>
|
||||
|
||||
namespace ACPI::AML {
|
||||
|
||||
bool namespace_modifier_object_filter(Opcode opcode);
|
||||
bool named_object_filter(Opcode opcode);
|
||||
bool object_filter(Opcode opcode);
|
||||
bool statement_filter(Opcode opcode);
|
||||
bool expression_filter(Opcode opcode);
|
||||
bool const_object_filter(Opcode opcode);
|
||||
bool computational_data_filter(Opcode opcode);
|
||||
bool data_object_filter(Opcode opcode);
|
||||
bool data_ref_object_filter(Opcode opcode);
|
||||
bool term_filter(Opcode opcode);
|
||||
bool term_arg_filter(Opcode opcode);
|
||||
bool arg_obj_filter(Opcode opcode);
|
||||
bool local_obj_filter(Opcode opcode);
|
||||
|
||||
bool is_namestring_start(u8 c);
|
||||
bool is_lead_name_char(u8 c);
|
||||
bool is_name_char(u8 c);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2025, RatcheT2497 <[email protected]>
|
||||
* Copyright (c) 2026, Undefine <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibACPI/Definitions.h>
|
||||
#include <LibACPI/OpcodeFilters.h>
|
||||
#include <LibACPI/OpcodeFrame.h>
|
||||
|
||||
#ifdef KERNEL
|
||||
# define warnln dbgln
|
||||
#endif
|
||||
|
||||
namespace ACPI::AML {
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#aml-byte-stream-byte-values
|
||||
constexpr static Array<OperandType, 1> EMPTY_OPERANDS = { OperandType::End };
|
||||
|
||||
constexpr static Array<OperandType, 3> SCOPE_OPERANDS = { OperandType::PackageLength, OperandType::NameString, OperandType::End };
|
||||
constexpr static Array<OperandType, 3> NAME_OPERANDS = { OperandType::NameString, OperandType::DataRefObject, OperandType::End };
|
||||
|
||||
constexpr static Array<OperandType, 2> BYTE_CONST_OPERANDS = { OperandType::ByteData, OperandType::End };
|
||||
constexpr static Array<OperandType, 2> WORD_CONST_OPERANDS = { OperandType::WordData, OperandType::End };
|
||||
constexpr static Array<OperandType, 2> DWORD_CONST_OPERANDS = { OperandType::DWordData, OperandType::End };
|
||||
constexpr static Array<OperandType, 2> QWORD_CONST_OPERANDS = { OperandType::QWordData, OperandType::End };
|
||||
|
||||
static ErrorOr<ReadonlySpan<OperandType>> get_opcode_operand_template(Opcode opcode)
|
||||
{
|
||||
switch (opcode) {
|
||||
case Opcode::BytePrefix:
|
||||
return BYTE_CONST_OPERANDS.span();
|
||||
case Opcode::WordPrefix:
|
||||
return WORD_CONST_OPERANDS.span();
|
||||
case Opcode::DWordPrefix:
|
||||
return DWORD_CONST_OPERANDS.span();
|
||||
case Opcode::QWordPrefix:
|
||||
return QWORD_CONST_OPERANDS.span();
|
||||
|
||||
case Opcode::ScopeOp:
|
||||
return SCOPE_OPERANDS.span();
|
||||
case Opcode::NameOp:
|
||||
return NAME_OPERANDS.span();
|
||||
|
||||
case Opcode::ZeroOp:
|
||||
case Opcode::OneOp:
|
||||
return EMPTY_OPERANDS.span();
|
||||
|
||||
default:
|
||||
warnln("ACPI: get_opcode_operand_template: Unknown opcode template: {}!", opcode);
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Unknown opcode template!"sv, EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOr<OpcodeFrame> OpcodeFrame::from_opcode(Opcode opcode, size_t start)
|
||||
{
|
||||
auto operand_template = TRY(get_opcode_operand_template(opcode));
|
||||
return OpcodeFrame(opcode, TRY(Vector<OperandType>::from_span(operand_template)), start);
|
||||
}
|
||||
|
||||
ErrorOr<OpcodeFrame> OpcodeFrame::root_frame()
|
||||
{
|
||||
return OpcodeFrame({}, TRY(Vector<OperandType>::from_span(EMPTY_OPERANDS)), 0);
|
||||
}
|
||||
|
||||
OperandType OpcodeFrame::peek_next_operand()
|
||||
{
|
||||
return m_operand_template[m_operand_template_index];
|
||||
}
|
||||
|
||||
bool OpcodeFrame::filter_opcode_for_next_operand(Opcode opcode)
|
||||
{
|
||||
auto pending_operand = peek_next_operand();
|
||||
|
||||
switch (pending_operand) {
|
||||
case OperandType::NameString:
|
||||
case OperandType::PackageLength:
|
||||
case OperandType::ByteData:
|
||||
case OperandType::WordData:
|
||||
case OperandType::DWordData:
|
||||
case OperandType::QWordData:
|
||||
return false;
|
||||
|
||||
case OperandType::TermArg:
|
||||
return term_arg_filter(opcode);
|
||||
|
||||
case OperandType::DataRefObject:
|
||||
return data_ref_object_filter(opcode);
|
||||
|
||||
case OperandType::End:
|
||||
return term_filter(opcode);
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<OperandType> OpcodeFrame::begin_operand()
|
||||
{
|
||||
return peek_next_operand();
|
||||
}
|
||||
|
||||
ErrorOr<void> OpcodeFrame::finish_operand(Object const& operand)
|
||||
{
|
||||
TRY(m_operands.try_append(operand));
|
||||
|
||||
m_operand_template_index++;
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2025, RatcheT2497 <[email protected]>
|
||||
* Copyright (c) 2026, Undefine <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Vector.h>
|
||||
#include <LibACPI/Definitions.h>
|
||||
#include <LibACPI/Object.h>
|
||||
|
||||
namespace ACPI::AML {
|
||||
|
||||
enum class OperandType : u8 {
|
||||
NameString,
|
||||
PackageLength,
|
||||
ByteData,
|
||||
WordData,
|
||||
DWordData,
|
||||
QWordData,
|
||||
|
||||
TermArg,
|
||||
|
||||
DataRefObject,
|
||||
|
||||
End,
|
||||
};
|
||||
|
||||
class OpcodeFrame {
|
||||
public:
|
||||
static ErrorOr<OpcodeFrame> from_opcode(Opcode opcode, size_t start);
|
||||
static ErrorOr<OpcodeFrame> root_frame();
|
||||
|
||||
bool filter_opcode_for_next_operand(Opcode opcode);
|
||||
|
||||
ErrorOr<OperandType> begin_operand();
|
||||
ErrorOr<void> finish_operand(Object const& operand);
|
||||
|
||||
Object const& operand(size_t index) const { return m_operands[index]; }
|
||||
size_t operand_count() const { return m_operands.size(); }
|
||||
|
||||
// NOTE: This should never get called on the root frame
|
||||
Opcode opcode() const { return m_opcode.value(); }
|
||||
size_t start() const { return m_start; }
|
||||
|
||||
private:
|
||||
OpcodeFrame(Optional<Opcode> opcode, Vector<OperandType> operand_template, size_t start)
|
||||
: m_opcode(opcode)
|
||||
, m_operand_template(move(operand_template))
|
||||
, m_start(start)
|
||||
{
|
||||
}
|
||||
|
||||
OperandType peek_next_operand();
|
||||
|
||||
Optional<Opcode> m_opcode;
|
||||
Vector<Object> m_operands;
|
||||
|
||||
Vector<OperandType> m_operand_template;
|
||||
size_t m_start;
|
||||
|
||||
size_t m_operand_template_index { 0 };
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) 2025, RatcheT2497 <[email protected]>
|
||||
* Copyright (c) 2026, Undefine <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibACPI/OpcodeFilters.h>
|
||||
#include <LibACPI/TableStream.h>
|
||||
|
||||
namespace ACPI::AML {
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#defblockheader
|
||||
ErrorOr<Structures::SDTHeader> TableStream::read_sdt_header()
|
||||
{
|
||||
return *TRY(read_in_place<Structures::SDTHeader const>());
|
||||
}
|
||||
|
||||
ErrorOr<Opcode> TableStream::read_opcode()
|
||||
{
|
||||
u16 opcode = TRY(read_value<u8>());
|
||||
if (opcode == EXT_OP_PREFIX)
|
||||
opcode = (opcode << 8) | TRY(read_value<u8>());
|
||||
|
||||
return static_cast<Opcode>(opcode);
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#package-length-encoding
|
||||
ErrorOr<u32> TableStream::read_package_length()
|
||||
{
|
||||
u8 initial = TRY(read_value<u8>());
|
||||
if ((initial & 0b1100'0000) == 0) {
|
||||
return initial;
|
||||
}
|
||||
|
||||
u8 additional_byte_count = initial >> 6;
|
||||
|
||||
u32 value = (initial & 0b1111);
|
||||
int bits = 4;
|
||||
for (auto i = 0; i < additional_byte_count; i++) {
|
||||
value |= TRY(read_value<u8>()) << bits;
|
||||
bits += 8;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// https://uefi.org/specs/ACPI/6.6/20_AML_Specification.html#namestring
|
||||
ErrorOr<NameString> TableStream::read_name_string()
|
||||
{
|
||||
NameString::Type type = NameString::Type::Relative;
|
||||
size_t depth = 0;
|
||||
size_t count = 0;
|
||||
u8 initial = 0;
|
||||
|
||||
// Figure out path type.
|
||||
initial = TRY(read_value<u8>());
|
||||
if (initial == '\\') {
|
||||
type = NameString::Type::Absolute;
|
||||
} else if (initial == '^') {
|
||||
while (initial == '^') {
|
||||
depth++;
|
||||
if (is_eof())
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Unexpected EOF in NameString."sv, EINVAL);
|
||||
initial = TRY(read_value<u8>());
|
||||
}
|
||||
|
||||
TRY(seek(offset() - 1));
|
||||
} else {
|
||||
TRY(seek(offset() - 1));
|
||||
}
|
||||
|
||||
// Figure out how many segments are in the path.
|
||||
// NamePath := NameSeg | DualNamePath | MultiNamePath | NullName
|
||||
initial = TRY(read_value<u8>());
|
||||
switch (initial) {
|
||||
case 0:
|
||||
return NameString(type, depth);
|
||||
|
||||
case MULTI_NAME_PREFIX:
|
||||
count = TRY(read_value<u8>());
|
||||
|
||||
if (count == 0) {
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Multiname path must have at least 1 item."sv, EINVAL);
|
||||
}
|
||||
break;
|
||||
|
||||
case DUAL_NAME_PREFIX:
|
||||
count = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (is_lead_name_char(initial)) {
|
||||
TRY(seek(offset() - 1));
|
||||
count = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
warnln("ACPI: Invalid character: '{:X}' at offset {:X}", (char)initial, offset());
|
||||
return Error::from_string_view_or_print_error_and_return_errno("Invalid character found."sv, EINVAL);
|
||||
}
|
||||
|
||||
Vector<NameSegment, 4> segments;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto item = TRY(read_in_place<u8 const>(4));
|
||||
TRY(segments.try_append(TRY(NameSegment::from_bytes(item))));
|
||||
}
|
||||
|
||||
return NameString(type, depth, move(segments));
|
||||
}
|
||||
|
||||
ErrorOr<u8> TableStream::peek_byte()
|
||||
{
|
||||
u8 value = TRY(read_value<u8>());
|
||||
TRY(seek(offset() - 1));
|
||||
return value;
|
||||
}
|
||||
|
||||
ErrorOr<Opcode> TableStream::peek_opcode()
|
||||
{
|
||||
auto previous = offset();
|
||||
Opcode value = TRY(read_opcode());
|
||||
TRY(seek(previous));
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2025, RatcheT2497 <[email protected]>
|
||||
* Copyright (c) 2026, Undefine <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibACPI/Definitions.h>
|
||||
#include <LibACPI/NameString.h>
|
||||
|
||||
namespace ACPI::AML {
|
||||
|
||||
class TableStream : public FixedMemoryStream {
|
||||
public:
|
||||
explicit TableStream(ReadonlyBytes bytes)
|
||||
: FixedMemoryStream(bytes)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<Structures::SDTHeader> read_sdt_header();
|
||||
|
||||
ErrorOr<Opcode> read_opcode();
|
||||
ErrorOr<u32> read_package_length();
|
||||
ErrorOr<NameString> read_name_string();
|
||||
|
||||
ErrorOr<u8> peek_byte();
|
||||
ErrorOr<Opcode> peek_opcode();
|
||||
};
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user