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

280 lines
8.0 KiB
C++

#include <kernel/ACPI/AML/Device.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Method.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Region.h>
#include <kernel/ACPI/AML/String.h>
#include <kernel/Lock/LockGuard.h>
namespace Kernel::ACPI
{
static BAN::RefPtr<AML::Namespace> s_root_namespace;
static BAN::Vector<uint8_t> s_osi_aml_data;
BAN::RefPtr<AML::Integer> AML::Integer::Constants::Zero;
BAN::RefPtr<AML::Integer> AML::Integer::Constants::One;
BAN::RefPtr<AML::Integer> AML::Integer::Constants::Ones;
struct DebugNode : AML::Node
{
DebugNode() : AML::Node(AML::Node::Type::Debug) {}
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> node)
{
node->debug_print(0);
AML_DEBUG_PRINTLN("");
return node;
}
void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("DEBUG");
}
};
BAN::RefPtr<AML::Node> AML::Namespace::debug_node;
BAN::RefPtr<AML::Namespace> AML::Namespace::root_namespace()
{
ASSERT(s_root_namespace);
return s_root_namespace;
}
void AML::Namespace::debug_print(int indent) const
{
LockGuard _(m_object_mutex);
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINTLN("Namespace {} {", name);
for_each_child(scope, [&](const auto&, const auto& child) {
child->debug_print(indent + 1);
AML_DEBUG_PRINTLN("");
});
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("}");
}
BAN::Optional<BAN::String> AML::Namespace::resolve_path(const AML::NameString& relative_base, const AML::NameString& relative_path, FindMode mode, bool check_existence) const
{
LockGuard _(m_object_mutex);
// Base must be non-empty absolute path
ASSERT(relative_base.prefix == "\\"_sv || relative_base.path.empty());
// Do absolute path lookup
if (!relative_path.prefix.empty() || relative_path.path.size() != 1 || mode == FindMode::ForceAbsolute)
{
BAN::String absolute_path;
MUST(absolute_path.push_back('\\'));
// Resolve root and parent references
if (relative_path.prefix == "\\"_sv)
;
else
{
if (relative_path.prefix.size() > relative_base.path.size())
{
AML_ERROR("Trying to resolve parent of root object");
return {};
}
for (size_t i = 0; i < relative_base.path.size() - relative_path.prefix.size(); i++)
{
MUST(absolute_path.append(relative_base.path[i].sv()));
MUST(absolute_path.push_back('.'));
}
}
// Append relative path
for (const auto& seg : relative_path.path)
{
MUST(absolute_path.append(seg.sv()));
MUST(absolute_path.push_back('.'));
}
if (absolute_path.back() == '.')
absolute_path.pop_back();
if (!check_existence || absolute_path == "\\"_sv || m_objects.contains(absolute_path))
return absolute_path;
return {};
}
// Resolve with namespace search rules (ACPI Spec 6.4 - Section 5.3)
AML::NameSeg target_seg = relative_path.path.back();
BAN::String last_match_path;
BAN::String current_path;
MUST(current_path.push_back('\\'));
// Check root namespace
{
BAN::String tmp;
MUST(tmp.append(current_path));
MUST(tmp.append(target_seg.sv()));
if (m_objects.contains(tmp))
last_match_path = BAN::move(tmp);
}
// Check base base path
for (const auto& seg : relative_base.path)
{
MUST(current_path.append(seg.sv()));
MUST(current_path.push_back('.'));
BAN::String tmp;
MUST(tmp.append(current_path));
MUST(tmp.append(target_seg.sv()));
if (m_objects.contains(tmp))
last_match_path = BAN::move(tmp);
}
if (!last_match_path.empty())
return last_match_path;
return {};
}
BAN::RefPtr<AML::NamedObject> AML::Namespace::find_object(const AML::NameString& relative_base, const AML::NameString& relative_path, FindMode mode)
{
LockGuard _(m_object_mutex);
auto canonical_path = resolve_path(relative_base, relative_path, mode);
if (!canonical_path.has_value())
return nullptr;
if (canonical_path->sv() == "\\"_sv)
return this;
auto it = m_objects.find(canonical_path.value());
if (it == m_objects.end())
return {};
return it->value;
}
bool AML::Namespace::add_named_object(ParseContext& parse_context, const AML::NameString& object_path, BAN::RefPtr<NamedObject> object)
{
LockGuard _(m_object_mutex);
ASSERT(!object_path.path.empty());
ASSERT(object_path.path.back() == object->name);
auto canonical_path = resolve_path(parse_context.scope, object_path, FindMode::ForceAbsolute, false);
ASSERT(canonical_path.has_value());
ASSERT(!canonical_path->empty());
if (m_objects.contains(canonical_path.value()))
{
AML_PRINT("Object '{}' already exists", canonical_path.value());
return false;
}
auto canonical_scope = AML::NameString(canonical_path.value());
MUST(m_objects.insert(canonical_path.value(), object));
if (object->is_scope())
{
auto* scope = static_cast<Scope*>(object.ptr());
scope->scope = canonical_scope;
}
MUST(parse_context.created_objects.push_back(canonical_scope));
return true;
}
bool AML::Namespace::remove_named_object(const AML::NameString& absolute_path)
{
LockGuard _(m_object_mutex);
auto canonical_path = resolve_path({}, absolute_path, FindMode::ForceAbsolute);
if (!canonical_path.has_value())
{
AML_ERROR("Trying to delete non-existent object '{}'", absolute_path);
return false;
}
if (canonical_path->empty())
{
AML_ERROR("Trying to remove root namespace");
return false;
}
ASSERT(m_objects.contains(canonical_path.value()));
m_objects.remove(canonical_path.value());
return true;
}
BAN::RefPtr<AML::Namespace> AML::Namespace::create_root_namespace()
{
ASSERT(!s_root_namespace);
s_root_namespace = MUST(BAN::RefPtr<Namespace>::create(NameSeg("\\"_sv)));
s_root_namespace->scope = AML::NameString("\\"_sv);
ASSERT(!Namespace::debug_node);
Namespace::debug_node = MUST(BAN::RefPtr<DebugNode>::create());
Integer::Constants::Zero = MUST(BAN::RefPtr<Integer>::create(0, true));
Integer::Constants::One = MUST(BAN::RefPtr<Integer>::create(1, true));
Integer::Constants::Ones = MUST(BAN::RefPtr<Integer>::create(0xFFFFFFFFFFFFFFFF, true));
AML::ParseContext context;
context.scope = AML::NameString("\\"_sv);
// Add predefined namespaces
#define ADD_PREDEFIED_NAMESPACE(NAME) \
ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\" NAME), MUST(BAN::RefPtr<AML::Device>::create(NameSeg(NAME)))));
ADD_PREDEFIED_NAMESPACE("_GPE"_sv);
ADD_PREDEFIED_NAMESPACE("_PR"_sv);
ADD_PREDEFIED_NAMESPACE("_SB"_sv);
ADD_PREDEFIED_NAMESPACE("_SI"_sv);
ADD_PREDEFIED_NAMESPACE("_TZ"_sv);
#undef ADD_PREDEFIED_NAMESPACE
// Add \_OSI that returns true for Linux compatibility
auto osi = MUST(BAN::RefPtr<AML::Method>::create(NameSeg("_OSI"_sv), 1, false, 0));
osi->override_function = [](AML::ParseContext& context) -> BAN::RefPtr<AML::Node> {
ASSERT(context.method_args[0]);
auto arg = context.method_args[0]->convert(AML::Node::ConvString);
if (!arg || arg->type != AML::Node::Type::String)
{
AML_ERROR("Invalid _OSI argument");
return {};
}
auto string = static_cast<AML::String*>(arg.ptr());
return string->string_view() == "Linux"_sv ? AML::Integer::Constants::Ones : AML::Integer::Constants::Zero;
};
ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\_OSI"), osi));
auto os_string = MUST(BAN::RefPtr<AML::String>::create("banan-os"_sv));
auto os = MUST(BAN::RefPtr<AML::Name>::create("_OS"_sv, os_string));
ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\_OS"), os));
return s_root_namespace;
}
bool AML::Namespace::parse(const SDTHeader& header)
{
ASSERT(this == s_root_namespace.ptr());
AML::ParseContext context;
context.scope = AML::NameString("\\"_sv);
context.aml_data = BAN::ConstByteSpan(reinterpret_cast<const uint8_t*>(&header), header.length).slice(sizeof(header));
while (context.aml_data.size() > 0)
{
auto result = AML::parse_object(context);
if (!result.success())
{
AML_ERROR("Failed to parse object");
return false;
}
}
return true;
}
}