banan-os/kernel/kernel/ACPI/AML/Scope.cpp

206 lines
6.0 KiB
C++

#include <kernel/ACPI/AML/Device.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/Region.h>
#include <kernel/ACPI/AML/Scope.h>
namespace Kernel::ACPI
{
AML::ParseResult AML::Scope::parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ScopeOp);
context.aml_data = context.aml_data.slice(1);
auto scope_pkg = AML::parse_pkg(context.aml_data);
if (!scope_pkg.has_value())
return ParseResult::Failure;
auto name_string = AML::NameString::parse(scope_pkg.value());
if (!name_string.has_value())
return ParseResult::Failure;
auto named_object = Namespace::root_namespace()->find_object(context.scope, name_string.value(), Namespace::FindMode::Normal);
if (!named_object)
{
AML_PRINT("Scope '{}' not found in namespace", name_string.value());
return ParseResult::Success;
}
if (!named_object->is_scope())
{
AML_ERROR("Scope '{}' does not name a namespace", name_string.value());
return ParseResult::Failure;
}
auto* scope = static_cast<Scope*>(named_object.ptr());
return scope->enter_context_and_parse_term_list(context, name_string.value(), scope_pkg.value());
}
AML::ParseResult AML::Scope::enter_context_and_parse_term_list(ParseContext& outer_context, const AML::NameString& name_string, BAN::ConstByteSpan aml_data)
{
auto resolved_scope = Namespace::root_namespace()->resolve_path(outer_context.scope, name_string, Namespace::FindMode::Normal);
if (!resolved_scope.has_value())
return ParseResult::Failure;
ParseContext scope_context;
scope_context.scope = AML::NameString(resolved_scope.release_value());
scope_context.aml_data = aml_data;
scope_context.method_args = outer_context.method_args;
while (scope_context.aml_data.size() > 0)
{
auto object_result = AML::parse_object(scope_context);
if (object_result.returned())
{
AML_ERROR("Unexpected return from scope {}", scope_context.scope);
return ParseResult::Failure;
}
if (!object_result.success())
return ParseResult::Failure;
}
for (auto& name : scope_context.created_objects)
MUST(outer_context.created_objects.push_back(BAN::move(name)));
return ParseResult::Success;
}
static BAN::RefPtr<AML::Integer> evaluate_or_invoke(BAN::RefPtr<AML::Node> object)
{
if (object->type != AML::Node::Type::Method)
{
auto converted = object->convert(AML::Node::ConvInteger);
if (!converted)
return {};
return static_cast<AML::Integer*>(converted.ptr());
}
auto* method = static_cast<AML::Method*>(object.ptr());
if (method->arg_count != 0)
{
AML_ERROR("Method has {} arguments, expected 0", method->arg_count);
return {};
}
auto result = method->invoke();
if (!result.has_value())
{
AML_ERROR("Failed to evaluate method");
return {};
}
auto result_integer = result.value()
? result.value()->convert(AML::Node::ConvInteger)
: BAN::RefPtr<AML::Node>();
if (!result_integer)
return {};
return static_cast<AML::Integer*>(result_integer.ptr());
}
bool AML::initialize_scope(BAN::RefPtr<AML::Scope> scope)
{
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINTLN("Initializing {}", scope->scope);
#endif
if (auto reg = Namespace::root_namespace()->find_object(scope->scope, AML::NameString("_REG"_sv), Namespace::FindMode::ForceAbsolute))
{
bool embedded_controller = false;
Namespace::for_each_child(scope->scope,
[&](const auto&, auto& child)
{
if (child->type != AML::Node::Type::OpRegion)
return;
auto* region = static_cast<AML::OpRegion*>(child.ptr());
if (region->region_space == AML::OpRegion::RegionSpace::EmbeddedController)
embedded_controller = true;
}
);
if (embedded_controller)
{
if (reg->type != AML::Node::Type::Method)
{
AML_ERROR("Object {}._REG is not a method", scope->scope);
return false;
}
auto* method = static_cast<Method*>(reg.ptr());
if (method->arg_count != 2)
{
AML_ERROR("Method {}._REG has {} arguments, expected 2", scope->scope, method->arg_count);
return false;
}
BAN::RefPtr<AML::Node> embedded_controller = MUST(BAN::RefPtr<AML::Integer>::create(static_cast<uint64_t>(AML::OpRegion::RegionSpace::EmbeddedController)));
if (!method->invoke(embedded_controller, AML::Integer::Constants::One).has_value())
{
AML_ERROR("Failed to evaluate {}._REG(EmbeddedController, 1), ignoring device", scope->scope);
return false;
}
}
}
bool run_ini = true;
bool init_children = true;
if (auto sta = Namespace::root_namespace()->find_object(scope->scope, AML::NameString("_STA"_sv), Namespace::FindMode::ForceAbsolute))
{
auto result = evaluate_or_invoke(sta);
if (!result)
{
AML_ERROR("Failed to evaluate {}._STA, return value could not be resolved to integer", scope->scope);
return false;
}
run_ini = (result->value & 0x01);
init_children = run_ini || (result->value & 0x02);
}
if (run_ini)
{
auto ini = Namespace::root_namespace()->find_object(scope->scope, AML::NameString("_INI"_sv), Namespace::FindMode::ForceAbsolute);
if (ini)
{
if (ini->type != AML::Node::Type::Method)
{
AML_ERROR("Object {}._INI is not a method", scope->scope);
return false;
}
auto* method = static_cast<Method*>(ini.ptr());
if (method->arg_count != 0)
{
AML_ERROR("Method {}._INI has {} arguments, expected 0", scope->scope, method->arg_count);
return false;
}
auto result = method->invoke();
if (!result.has_value())
{
AML_ERROR("Failed to evaluate {}._INI, ignoring device", scope->scope);
return true;
}
}
}
bool success = true;
if (init_children)
{
Namespace::root_namespace()->for_each_child(scope->scope,
[&](const auto&, auto& child)
{
if (!child->is_scope())
return;
auto* child_scope = static_cast<Scope*>(child.ptr());
if (!initialize_scope(child_scope))
success = false;
}
);
}
return success;
}
}