Files
serenity/Userland/Libraries/LibACPI/OpcodeFrame.cpp
T
UndefineandRatcheT2497 c9e5fa6ada 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]>
2026-11-01 15:08:28 +01:00

183 lines
5.4 KiB
C++

/*
* 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 };
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) {
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::OpRegionOp:
return OP_REGION_OPERANDS.span();
case Opcode::FieldOp:
return FIELD_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()
{
auto operand = m_operand_template[m_operand_template_index];
if (operand == OperandType::MarkAsCount || operand == OperandType::RepeatNextBytes)
operand = m_operand_template[m_operand_template_index + 1];
return operand;
}
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:
case OperandType::FieldElement:
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);
case OperandType::MarkAsCount:
case OperandType::RepeatNextBytes:
VERIFY_NOT_REACHED();
}
VERIFY_NOT_REACHED();
}
ErrorOr<OperandType> OpcodeFrame::begin_operand(size_t current_offset)
{
auto operand = m_operand_template[m_operand_template_index];
if (operand == OperandType::MarkAsCount) {
m_counter_value_index = m_operands.size();
} else if (operand == OperandType::RepeatNextBytes) {
if (!m_counter.has_value()) {
m_counter = TRY(m_operands.at(m_counter_value_index).as<u64>());
check_repeat_opcodes(current_offset);
}
}
return peek_next_operand();
}
ErrorOr<void> OpcodeFrame::finish_operand(size_t current_offset)
{
auto operand_type = m_operand_template[m_operand_template_index];
if (operand_type == OperandType::MarkAsCount) {
m_operand_template_index += 2;
return {};
}
if (!check_repeat_opcodes(current_offset))
m_operand_template_index++;
return {};
}
ErrorOr<void> OpcodeFrame::finish_operand(Object const& operand, size_t current_offset)
{
TRY(m_operands.try_append(operand));
return finish_operand(current_offset);
}
bool OpcodeFrame::check_repeat_opcodes(size_t current_offset)
{
auto operand_type = m_operand_template[m_operand_template_index];
if (operand_type == OperandType::RepeatNextBytes) {
if (current_offset >= m_start + m_counter.value()) {
m_counter = {};
m_operand_template_index += 2;
return true;
}
return true;
}
return false;
}
}