Kernel: Implement more AML method invocation stuff

Method invocation is starting to come together. This implemenetation
can interpret some of the qemu's functions to enter ACPI mode.

PCI config space access is currently the one thing is between
entering ACPI mode.
This commit is contained in:
2024-04-10 01:52:14 +03:00
parent 23fa39121c
commit ff203d8d34
23 changed files with 619 additions and 108 deletions

View File

@@ -3,18 +3,9 @@
#include <kernel/ACPI/Headers.h>
#include <kernel/ACPI/AML/Namespace.h>
namespace Kernel::ACPI
namespace Kernel::ACPI::AML
{
class AMLParser
{
public:
~AMLParser();
static BAN::RefPtr<AML::Namespace> parse_table(const SDTHeader& header);
private:
AMLParser();
};
BAN::RefPtr<AML::Namespace> initialize_namespace(const SDTHeader& header);
}

View File

@@ -30,22 +30,27 @@ namespace Kernel::ACPI::AML
return ParseResult::Failure;
auto device = MUST(BAN::RefPtr<Device>::create(name_string->path.back()));
if (!context.root_namespace->add_named_object(context, name_string.value(), device))
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), device))
return ParseResult::Failure;
return device->enter_context_and_parse_term_list(context, name_string.value(), device_pkg.value());
}
void initialize(BAN::RefPtr<AML::Namespace> root_namespace)
void initialize()
{
bool run_ini = true;
bool init_children = true;
auto _sta = root_namespace->find_object(scope, NameString("_STA"sv));
auto _sta = Namespace::root_namespace()->find_object(scope, NameString("_STA"sv));
if (_sta && _sta->type == Node::Type::Method)
{
auto* method = static_cast<Method*>(_sta.ptr());
auto result = method->evaluate(root_namespace);
if (method->arg_count != 0)
{
AML_ERROR("Method {}._STA has {} arguments, expected 0", scope, method->arg_count);
return;
}
auto result = method->evaluate({});
if (!result.has_value())
{
AML_ERROR("Failed to evaluate {}._STA", scope);
@@ -60,6 +65,8 @@ namespace Kernel::ACPI::AML
if (!result_val.has_value())
{
AML_ERROR("Failed to evaluate {}._STA, return value could not be resolved to integer", scope);
AML_ERROR(" Return value: ");
result.value()->debug_print(0);
return;
}
run_ini = (result_val.value() & 0x01);
@@ -68,11 +75,16 @@ namespace Kernel::ACPI::AML
if (run_ini)
{
auto _ini = root_namespace->find_object(scope, NameString("_INI"sv));
auto _ini = Namespace::root_namespace()->find_object(scope, NameString("_INI"sv));
if (_ini && _ini->type == Node::Type::Method)
{
auto* method = static_cast<Method*>(_ini.ptr());
method->evaluate(root_namespace);
if (method->arg_count != 0)
{
AML_ERROR("Method {}._INI has {} arguments, expected 0", scope, method->arg_count);
return;
}
method->evaluate({});
}
}
@@ -83,7 +95,7 @@ namespace Kernel::ACPI::AML
if (child->type == Node::Type::Device)
{
auto* device = static_cast<Device*>(child.ptr());
device->initialize(root_namespace);
device->initialize();
}
}
}

View File

@@ -0,0 +1,162 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/ParseContext.h>
namespace Kernel::ACPI::AML
{
struct Expression
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
switch (static_cast<Byte>(context.aml_data[0]))
{
// unary
case AML::Byte::IncrementOp:
case AML::Byte::DecrementOp:
case AML::Byte::NotOp:
case AML::Byte::LNotOp:
AML_TODO("Expression {2H}", context.aml_data[0]);
return ParseResult::Failure;
// binary
case AML::Byte::AddOp:
case AML::Byte::AndOp:
case AML::Byte::ModOp:
case AML::Byte::MultiplyOp:
case AML::Byte::NandOp:
case AML::Byte::NorOp:
case AML::Byte::OrOp:
case AML::Byte::ShiftLeftOp:
case AML::Byte::ShiftRightOp:
case AML::Byte::SubtractOp:
case AML::Byte::XorOp:
case AML::Byte::LAndOp:
case AML::Byte::LEqualOp:
case AML::Byte::LGreaterOp:
case AML::Byte::LLessOp:
case AML::Byte::LOrOp:
{
auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
context.aml_data = context.aml_data.slice(1);
auto lhs_result = AML::parse_object(context);
if (!lhs_result.success())
return ParseResult::Failure;
auto lhs_node = lhs_result.node();
if (!lhs_node)
{
AML_ERROR("LHS object is null");
return ParseResult::Failure;
}
auto lhs = lhs_node->evaluate();
if (!lhs)
{
AML_ERROR("Failed to evaluate LHS object");
return ParseResult::Failure;
}
auto rhs_result = AML::parse_object(context);
if (!rhs_result.success())
return ParseResult::Failure;
auto rhs_node = rhs_result.node();
if (!rhs_node)
{
AML_ERROR("RHS object is null");
return ParseResult::Failure;
}
auto rhs = rhs_node->evaluate();
if (!rhs)
{
AML_ERROR("Failed to evaluate RHS object");
return ParseResult::Failure;
}
return parse_binary_op(context, opcode, lhs, rhs);
}
// trinary
case AML::Byte::DivideOp:
AML_TODO("Expression {2H}", context.aml_data[0]);
return ParseResult::Failure;
default:
ASSERT_NOT_REACHED();
}
}
private:
static ParseResult parse_binary_op(ParseContext& context, AML::Byte opcode, BAN::RefPtr<AML::Node> lhs_node, BAN::RefPtr<AML::Node> rhs_node)
{
if (lhs_node->type != AML::Node::Type::Integer)
{
AML_TODO("LHS object is not an integer, type {}", static_cast<uint8_t>(lhs_node->type));
return ParseResult::Failure;
}
if (rhs_node->type != AML::Node::Type::Integer)
{
AML_TODO("RHS object is not an integer, type {}", static_cast<uint8_t>(rhs_node->type));
return ParseResult::Failure;
}
bool logical = false;
uint64_t (*func)(uint64_t, uint64_t) = nullptr;
switch (opcode)
{
case AML::Byte::AddOp: func = [](uint64_t a, uint64_t b) { return a + b; }; break;
case AML::Byte::AndOp: func = [](uint64_t a, uint64_t b) { return a & b; }; break;
case AML::Byte::ModOp: func = [](uint64_t a, uint64_t b) { return a % b; }; break;
case AML::Byte::MultiplyOp: func = [](uint64_t a, uint64_t b) { return a * b; }; break;
case AML::Byte::NandOp: func = [](uint64_t a, uint64_t b) { return ~(a & b); }; break;
case AML::Byte::NorOp: func = [](uint64_t a, uint64_t b) { return ~(a | b); }; break;
case AML::Byte::OrOp: func = [](uint64_t a, uint64_t b) { return a | b; }; break;
case AML::Byte::ShiftLeftOp: func = [](uint64_t a, uint64_t b) { return a << b; }; break;
case AML::Byte::ShiftRightOp: func = [](uint64_t a, uint64_t b) { return a >> b; }; break;
case AML::Byte::SubtractOp: func = [](uint64_t a, uint64_t b) { return a - b; }; break;
case AML::Byte::XorOp: func = [](uint64_t a, uint64_t b) { return a ^ b; }; break;
case AML::Byte::LAndOp: func = [](uint64_t a, uint64_t b) { return a && b ? Integer::Ones : 0; }; logical = true; break;
case AML::Byte::LEqualOp: func = [](uint64_t a, uint64_t b) { return a == b ? Integer::Ones : 0; }; logical = true; break;
case AML::Byte::LGreaterOp: func = [](uint64_t a, uint64_t b) { return a > b ? Integer::Ones : 0; }; logical = true; break;
case AML::Byte::LLessOp: func = [](uint64_t a, uint64_t b) { return a < b ? Integer::Ones : 0; }; logical = true; break;
case AML::Byte::LOrOp: func = [](uint64_t a, uint64_t b) { return a || b ? Integer::Ones : 0; }; logical = true; break;
default:
ASSERT_NOT_REACHED();
}
uint64_t lhs = static_cast<AML::Integer*>(lhs_node.ptr())->value;
uint64_t rhs = static_cast<AML::Integer*>(rhs_node.ptr())->value;
uint64_t result = func(lhs, rhs);
auto result_node = MUST(BAN::RefPtr<AML::Integer>::create(result));
if (!logical)
{
if (context.aml_data.size() < 1)
return ParseResult::Failure;
if (context.aml_data[0] == 0x00)
context.aml_data = context.aml_data.slice(1);
else
{
auto target_result = AML::parse_object(context);
if (!target_result.success())
return ParseResult::Failure;
auto target = target_result.node();
if (!target)
{
AML_ERROR("Target object is null");
return ParseResult::Failure;
}
if (!target->store(result_node))
{
AML_ERROR("Failed to store result");
return ParseResult::Failure;
}
}
}
return ParseResult(result_node);
}
};
}

View File

@@ -44,7 +44,7 @@ namespace Kernel::ACPI::AML
FieldRules access_rules;
OpRegion* op_region = nullptr;
BAN::RefPtr<OpRegion> op_region = nullptr;
FieldElement(NameSeg name, uint64_t bit_offset, uint32_t bit_count, FieldRules access_rules)
: NamedObject(Node::Type::FieldElement, name)
@@ -53,6 +53,8 @@ namespace Kernel::ACPI::AML
, access_rules(access_rules)
{}
BAN::RefPtr<Node> evaluate() override;
void debug_print(int indent) const override;
};
@@ -68,8 +70,8 @@ namespace Kernel::ACPI::AML
FieldRules access_rules;
FieldElement* index_element = nullptr;
FieldElement* data_element = nullptr;
BAN::RefPtr<FieldElement> index_element = nullptr;
BAN::RefPtr<FieldElement> data_element = nullptr;
IndexFieldElement(NameSeg name, uint64_t bit_offset, uint32_t bit_count, FieldRules access_rules)
: NamedObject(Node::Type::IndexFieldElement, name)

View File

@@ -0,0 +1,72 @@
#pragma once
#include <BAN/HashMap.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
namespace Kernel::ACPI::AML
{
struct IfElse
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::IfOp);
context.aml_data = context.aml_data.slice(1);
auto if_pkg = AML::parse_pkg(context.aml_data);
if (!if_pkg.has_value())
return ParseResult::Failure;
auto outer_aml_data = context.aml_data;
context.aml_data = if_pkg.value();
auto predicate = AML::parse_object(context);
if (!predicate.success())
return ParseResult::Failure;
auto predicate_node = predicate.node();
if (!predicate_node)
{
AML_ERROR("If predicate is not an integer");
return ParseResult::Failure;
}
auto predicate_integer = predicate_node->as_integer();
if (!predicate_integer.has_value())
{
AML_ERROR("If predicate is not an integer");
return ParseResult::Failure;
}
// Else
if (!predicate_integer.value())
{
if (outer_aml_data.size() < 1 || static_cast<Byte>(outer_aml_data[0]) != Byte::ElseOp)
context.aml_data = BAN::ConstByteSpan();
else
{
outer_aml_data = outer_aml_data.slice(1);
auto else_pkg = AML::parse_pkg(outer_aml_data);
if (!else_pkg.has_value())
return ParseResult::Failure;
context.aml_data = else_pkg.value();
}
}
while (context.aml_data.size() > 0)
{
auto object_result = AML::parse_object(context);
if (object_result.returned())
return ParseResult(ParseResult::Result::Returned, object_result.node());
if (!object_result.success())
return ParseResult::Failure;
}
context.aml_data = outer_aml_data;
return ParseResult::Success;
}
};
}

View File

@@ -18,9 +18,9 @@ namespace Kernel::ACPI::AML
Integer(uint64_t value) : Node(Node::Type::Integer), value(value) {}
BAN::Optional<uint64_t> as_integer() const override
BAN::RefPtr<AML::Node> evaluate() override
{
return value;
return MUST(BAN::RefPtr<Integer>::create(value));
}
static ParseResult parse(BAN::ConstByteSpan& aml_data)

View File

@@ -11,6 +11,8 @@ namespace Kernel::ACPI::AML
struct Method : public AML::Scope
{
using Arguments = BAN::Array<BAN::RefPtr<AML::Register>, 7>;
uint8_t arg_count;
bool serialized;
uint8_t sync_level;
@@ -49,10 +51,10 @@ namespace Kernel::ACPI::AML
(method_flags >> 3) & 0x01,
method_flags >> 4
));
if (!context.root_namespace->add_named_object(context, name_string.value(), method))
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), method))
return ParseResult::Failure;
auto method_scope = context.root_namespace->resolve_path(context.scope, name_string.value());
auto method_scope = Namespace::root_namespace()->resolve_path(context.scope, name_string.value());
if (!method_scope.has_value())
return ParseResult::Failure;
method->term_list = method_pkg.value();
@@ -66,37 +68,32 @@ namespace Kernel::ACPI::AML
return ParseResult::Success;
}
BAN::Optional<BAN::RefPtr<AML::Node>> evaluate(BAN::RefPtr<AML::Namespace> root_namespace)
BAN::Optional<BAN::RefPtr<AML::Node>> evaluate(Arguments args)
{
ParseContext context;
context.root_namespace = root_namespace.ptr();
context.aml_data = term_list;
context.scope = scope;
AML_DEBUG_PRINTLN("Evaluating method {}", scope);
context.method_args = args;
for (auto& local : context.method_locals)
local = MUST(BAN::RefPtr<AML::Register>::create());
BAN::Optional<BAN::RefPtr<AML::Node>> return_value;
ASSERT(arg_count == 0);
while (context.aml_data.size() > 0)
{
if (static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ReturnOp)
auto parse_result = AML::parse_object(context);
if (parse_result.returned())
{
context.aml_data = context.aml_data.slice(1);
auto result = AML::parse_object(context);
if (result.success())
return_value = result.node();
return_value = parse_result.node();
break;
}
auto object_result = AML::parse_object(context);
if (!object_result.success())
if (!parse_result.success())
break;
}
while (!context.created_objects.empty())
{
root_namespace->remove_named_object(context.created_objects.back());
Namespace::root_namespace()->remove_named_object(context.created_objects.back());
context.created_objects.pop_back();
}

View File

@@ -39,7 +39,7 @@ namespace Kernel::ACPI::AML
}
auto mutex = MUST(BAN::RefPtr<Mutex>::create(name->path.back(), sync_level));
if (!context.root_namespace->add_named_object(context, name.value(), mutex))
if (!Namespace::root_namespace()->add_named_object(context, name.value(), mutex))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2

View File

@@ -8,6 +8,7 @@ namespace Kernel::ACPI::AML
struct NamedObject : public Node
{
BAN::RefPtr<NamedObject> parent;
NameSeg name;
NamedObject(Node::Type type, NameSeg name) : Node(type), name(name) {}

View File

@@ -7,6 +7,8 @@ namespace Kernel::ACPI::AML
struct Namespace : public AML::Scope
{
static BAN::RefPtr<AML::Namespace> root_namespace();
Namespace(NameSeg name) : AML::Scope(Node::Type::Namespace, name) {}
static BAN::RefPtr<Namespace> parse(BAN::ConstByteSpan aml);

View File

@@ -4,6 +4,7 @@
#include <BAN/Optional.h>
#include <BAN/RefPtr.h>
#include <BAN/Vector.h>
#include <kernel/ACPI/AML/Utils.h>
namespace Kernel::ACPI::AML
{
@@ -24,6 +25,7 @@ namespace Kernel::ACPI::AML
OpRegion,
Package,
Processor,
Register,
String,
};
const Type type;
@@ -32,7 +34,10 @@ namespace Kernel::ACPI::AML
virtual ~Node() = default;
virtual bool is_scope() const { return false; }
virtual BAN::Optional<uint64_t> as_integer() const { return {}; }
BAN::Optional<uint64_t> as_integer();
virtual BAN::RefPtr<AML::Node> evaluate() { AML_TODO("evaluate, type {}", static_cast<uint8_t>(type)); return nullptr; }
virtual bool store(BAN::RefPtr<AML::Node> source) { AML_TODO("store, type {}", static_cast<uint8_t>(type)); return false; }
virtual void debug_print(int indent) const = 0;
};
@@ -47,12 +52,25 @@ namespace Kernel::ACPI::AML
{
Success,
Failure,
Returned,
};
ParseResult(Result success) : m_result(success) {}
ParseResult(BAN::RefPtr<Node> node) : m_result(Result::Success), m_node(BAN::move(node)) { ASSERT(m_node); }
ParseResult(Result success)
: m_result(success)
{}
ParseResult(Result success, BAN::RefPtr<Node> node)
: m_result(success)
, m_node(BAN::move(node))
{}
ParseResult(BAN::RefPtr<Node> node)
: m_result(Result::Success)
, m_node(BAN::move(node))
{
ASSERT(m_node);
}
bool success() const { return m_result == Result::Success; }
bool returned() const { return m_result == Result::Returned; }
BAN::RefPtr<Node> node()
{

View File

@@ -1,9 +1,11 @@
#pragma once
#include <BAN/Array.h>
#include <BAN/ByteSpan.h>
#include <BAN/LinkedList.h>
#include <kernel/ACPI/AML/NamedObject.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/Register.h>
namespace Kernel::ACPI::AML
{
@@ -12,12 +14,14 @@ namespace Kernel::ACPI::AML
{
BAN::ConstByteSpan aml_data;
AML::NameString scope;
AML::Namespace* root_namespace;
// Used for cleaning up on method exit
// NOTE: This uses linked list instead of vector because
// we don't really need large contiguous memory
BAN::LinkedList<AML::NameString> created_objects;
BAN::Array<BAN::RefPtr<Register>, 7> method_args;
BAN::Array<BAN::RefPtr<Register>, 8> method_locals;
};
}

View File

@@ -53,7 +53,7 @@ namespace Kernel::ACPI::AML
processor_pkg = processor_pkg->slice(1);
auto processor = MUST(BAN::RefPtr<Processor>::create(name->path.back(), id, pblk_addr, pblk_len));
if (!context.root_namespace->add_named_object(context, name.value(), processor))
if (!Namespace::root_namespace()->add_named_object(context, name.value(), processor))
return ParseResult::Failure;
return processor->enter_context_and_parse_term_list(context, name.value(), processor_pkg.value());

View File

@@ -23,12 +23,15 @@ namespace Kernel::ACPI::AML
GenericSerialBus = 9,
PCC = 10,
};
RegionSpace space;
uint64_t offset;
uint64_t length;
RegionSpace region_space;
uint64_t region_offset;
uint64_t region_length;
OpRegion(NameSeg name, RegionSpace space, uint64_t offset, uint64_t length)
: NamedObject(Node::Type::OpRegion, name), space(space), offset(offset), length(length)
OpRegion(NameSeg name, RegionSpace region_space, uint64_t region_offset, uint64_t region_length)
: NamedObject(Node::Type::OpRegion, name)
, region_space(region_space)
, region_offset(region_offset)
, region_length(region_length)
{}
static ParseResult parse(AML::ParseContext& context)
@@ -74,7 +77,7 @@ namespace Kernel::ACPI::AML
length.value()
));
if (!context.root_namespace->add_named_object(context, name.value(), op_region))
if (!Namespace::root_namespace()->add_named_object(context, name.value(), op_region))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
@@ -88,7 +91,7 @@ namespace Kernel::ACPI::AML
virtual void debug_print(int indent) const override
{
BAN::StringView region_space_name;
switch (space)
switch (region_space)
{
case RegionSpace::SystemMemory: region_space_name = "SystemMemory"sv; break;
case RegionSpace::SystemIO: region_space_name = "SystemIO"sv; break;
@@ -106,7 +109,7 @@ namespace Kernel::ACPI::AML
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("OperationRegion(");
name.debug_print();
AML_DEBUG_PRINT(", {}, 0x{H}, 0x{H})", region_space_name, offset, length);
AML_DEBUG_PRINT(", {}, 0x{H}, 0x{H})", region_space_name, region_offset, region_length);
}
};

View File

@@ -0,0 +1,53 @@
#pragma once
#include <kernel/ACPI/AML/Node.h>
namespace Kernel::ACPI::AML
{
struct Register : public AML::Node
{
BAN::RefPtr<AML::Node> value;
Register()
: Node(Node::Type::Register)
{}
Register(BAN::RefPtr<AML::Node> value)
: Node(Node::Type::Register)
, value(value)
{}
BAN::RefPtr<AML::Node> evaluate() override
{
if (value)
return value->evaluate();
return {};
}
bool store(BAN::RefPtr<AML::Node> source) override
{
auto evaluated = source->evaluate();
if (!evaluated)
{
AML_ERROR("Failed to evaluate source for store");
return false;
}
value = evaluated;
return true;
}
void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("Register\n");
if (value)
value->debug_print(indent + 1);
else
{
AML_DEBUG_PRINT_INDENT(indent + 1);
AML_DEBUG_PRINT("No value\n");
}
}
};
}

View File

@@ -0,0 +1,43 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/ParseContext.h>
namespace Kernel::ACPI::AML
{
struct Store
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::StoreOp);
context.aml_data = context.aml_data.slice(1);
auto source_result = AML::parse_object(context);
if (!source_result.success())
return ParseResult::Failure;
auto source = source_result.node();
if (!source)
{
AML_ERROR("Store source is null");
return ParseResult::Failure;
}
auto destination_result = AML::parse_object(context);
if (!destination_result.success())
return ParseResult::Failure;
auto destination = destination_result.node();
if (!destination)
{
AML_ERROR("Store destination is null");
return ParseResult::Failure;
}
if (!destination->store(source))
return ParseResult::Failure;
return ParseResult::Success;
}
};
}