From f1b2d7530df5a71146987f9cdcbb405c51aff5a8 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Tue, 16 Apr 2024 16:23:12 +0300 Subject: [PATCH] Kernel: Rework AML namespace and object hierarchy Remove tree-like structure from AML. This allows more spec compliant parsing of named objects inside not yet declared devices. This also allows AML to be run thread safely. All object adds/removes are now guarded by a mutex. --- kernel/include/kernel/ACPI/AML/Device.h | 12 +- kernel/include/kernel/ACPI/AML/Method.h | 2 +- kernel/include/kernel/ACPI/AML/Names.h | 8 + kernel/include/kernel/ACPI/AML/Namespace.h | 29 +++- kernel/include/kernel/ACPI/AML/Scope.h | 3 - kernel/kernel/ACPI/AML/Namespace.cpp | 171 ++++++++++----------- kernel/kernel/ACPI/AML/Scope.cpp | 97 ++++++------ 7 files changed, 179 insertions(+), 143 deletions(-) diff --git a/kernel/include/kernel/ACPI/AML/Device.h b/kernel/include/kernel/ACPI/AML/Device.h index 02b62d6c..8fa68738 100644 --- a/kernel/include/kernel/ACPI/AML/Device.h +++ b/kernel/include/kernel/ACPI/AML/Device.h @@ -42,11 +42,13 @@ namespace Kernel::ACPI::AML AML_DEBUG_PRINT("Device "); name.debug_print(); AML_DEBUG_PRINTLN(" {"); - for (const auto& [name, object] : objects) - { - object->debug_print(indent + 1); - AML_DEBUG_PRINTLN(""); - } + Namespace::root_namespace()->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("}"); } diff --git a/kernel/include/kernel/ACPI/AML/Method.h b/kernel/include/kernel/ACPI/AML/Method.h index edc058d1..605973e4 100644 --- a/kernel/include/kernel/ACPI/AML/Method.h +++ b/kernel/include/kernel/ACPI/AML/Method.h @@ -59,7 +59,7 @@ namespace Kernel::ACPI::AML if (!method_scope.has_value()) return ParseResult::Failure; method->term_list = method_pkg.value(); - method->scope = method_scope.release_value(); + method->scope = AML::NameString(method_scope.release_value()); #if AML_DEBUG_LEVEL >= 2 method->debug_print(0); diff --git a/kernel/include/kernel/ACPI/AML/Names.h b/kernel/include/kernel/ACPI/AML/Names.h index 98cbda00..94a015fe 100644 --- a/kernel/include/kernel/ACPI/AML/Names.h +++ b/kernel/include/kernel/ACPI/AML/Names.h @@ -36,6 +36,14 @@ namespace Kernel::ACPI::AML aml_data = aml_data.slice(4); } + BAN::StringView sv() const + { + size_t len = 4; + while (len > 0 && chars[len - 1] == '_') + len--; + return BAN::StringView(chars, len); + } + static BAN::Optional parse(BAN::ConstByteSpan& aml_data) { if (aml_data.size() < 4) diff --git a/kernel/include/kernel/ACPI/AML/Namespace.h b/kernel/include/kernel/ACPI/AML/Namespace.h index a4c429c2..cf6dbe7f 100644 --- a/kernel/include/kernel/ACPI/AML/Namespace.h +++ b/kernel/include/kernel/ACPI/AML/Namespace.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -11,12 +12,34 @@ namespace Kernel::ACPI::AML { static BAN::RefPtr root_namespace(); + template + static void for_each_child(const AML::NameString& scope, const F& callback) + { + auto canonical_path = root_namespace()->resolve_path({}, scope); + ASSERT(canonical_path.has_value()); + + for (auto& [path, child] : root_namespace()->m_objects) + { + if (path.size() < canonical_path->size() + 1) + continue; + if (path[canonical_path->size()] != '.') + continue; + if (path.sv().substring(0, canonical_path->size()) != canonical_path->sv()) + continue; + if (path.sv().substring(canonical_path->size() + 1).contains('.')) + continue; + callback(path, child); + } + } + Namespace(NameSeg name) : AML::Scope(Node::Type::Namespace, name) {} static BAN::RefPtr create_root_namespace(); bool parse(const SDTHeader& header); - BAN::Optional resolve_path(const AML::NameString& relative_base, const AML::NameString& relative_path); + void debug_print(int indent) const override; + + BAN::Optional resolve_path(const AML::NameString& relative_base, const AML::NameString& relative_path, bool allow_nonexistent = false); // Find an object in the namespace. Returns nullptr if the object is not found. BAN::RefPtr find_object(const AML::NameString& relative_base, const AML::NameString& relative_path); @@ -26,6 +49,10 @@ namespace Kernel::ACPI::AML // Remove an object from the namespace. Returns false if the object could not be removed. bool remove_named_object(const AML::NameString& absolute_path); + + private: + BAN::HashMap> m_objects; + mutable Mutex m_object_mutex; }; } diff --git a/kernel/include/kernel/ACPI/AML/Scope.h b/kernel/include/kernel/ACPI/AML/Scope.h index dd47cf00..5e927145 100644 --- a/kernel/include/kernel/ACPI/AML/Scope.h +++ b/kernel/include/kernel/ACPI/AML/Scope.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -9,7 +8,6 @@ namespace Kernel::ACPI::AML struct Scope : public AML::NamedObject { - BAN::HashMap> objects; AML::NameString scope; Scope(Node::Type type, NameSeg name) @@ -19,7 +17,6 @@ namespace Kernel::ACPI::AML virtual bool is_scope() const override { return true; } static ParseResult parse(ParseContext& context); - virtual void debug_print(int indent) const override; protected: ParseResult enter_context_and_parse_term_list(ParseContext& outer_context, const AML::NameString& name, BAN::ConstByteSpan aml_data); diff --git a/kernel/kernel/ACPI/AML/Namespace.cpp b/kernel/kernel/ACPI/AML/Namespace.cpp index dcec78a1..18015c4d 100644 --- a/kernel/kernel/ACPI/AML/Namespace.cpp +++ b/kernel/kernel/ACPI/AML/Namespace.cpp @@ -1,8 +1,10 @@ +#include #include #include #include #include #include +#include namespace Kernel::ACPI { @@ -20,16 +22,33 @@ namespace Kernel::ACPI return s_root_namespace; } - BAN::Optional AML::Namespace::resolve_path(const AML::NameString& relative_base, const AML::NameString& relative_path) + void AML::Namespace::debug_print(int indent) const { + LockGuard _(m_object_mutex); + AML_DEBUG_PRINT_INDENT(indent); + AML_DEBUG_PRINTLN("Namespace {} {", name); + for (auto& [path, child] : m_objects) + { + child->debug_print(indent + 1); + AML_DEBUG_PRINTLN(""); + } + AML_DEBUG_PRINT_INDENT(indent); + AML_DEBUG_PRINT("}"); + + } + + BAN::Optional AML::Namespace::resolve_path(const AML::NameString& relative_base, const AML::NameString& relative_path, bool allow_nonexistent) + { + 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) + if (!relative_path.prefix.empty() || relative_path.path.size() != 1 || allow_nonexistent) { - AML::NameString absolute_path; - MUST(absolute_path.prefix.push_back('\\')); + BAN::String absolute_path; + MUST(absolute_path.push_back('\\')); // Resolve root and parent references if (relative_path.prefix == "\\"sv) @@ -42,158 +61,132 @@ namespace Kernel::ACPI return {}; } for (size_t i = 0; i < relative_base.path.size() - relative_path.prefix.size(); i++) - MUST(absolute_path.path.push_back(relative_base.path[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.path.push_back(seg)); - - // Validate path - BAN::RefPtr current_node = this; - for (const auto& seg : absolute_path.path) { - if (!current_node->is_scope()) - return {}; - - auto* current_scope = static_cast(current_node.ptr()); - auto it = current_scope->objects.find(seg); - if (it == current_scope->objects.end()) - return {}; - - current_node = it->value; + MUST(absolute_path.append(seg.sv())); + MUST(absolute_path.push_back('.')); } - return absolute_path; + + if (absolute_path.back() == '.') + absolute_path.pop_back(); + + if (allow_nonexistent || m_objects.contains(absolute_path)) + return absolute_path; + return {}; } // Resolve with namespace search rules (ACPI Spec 6.4 - Section 5.3) - AML::NameString last_match_path; AML::NameSeg target_seg = relative_path.path.back(); - BAN::RefPtr current_scope = this; - AML::NameString current_path; + BAN::String last_match_path; + BAN::String current_path; + MUST(current_path.push_back('\\')); // Check root namespace { - // If scope contains object with the same name as the segment, update last match - if (current_scope->objects.contains(target_seg)) - { - last_match_path = current_path; - MUST(last_match_path.path.push_back(target_seg)); - } + 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) { - auto next_node = current_scope->objects[seg]; - ASSERT(next_node && next_node->is_scope()); + MUST(current_path.append(seg.sv())); + MUST(current_path.push_back('.')); - current_scope = static_cast(next_node.ptr()); - MUST(current_path.path.push_back(seg)); - - // If scope contains object with the same name as the segment, update last match - if (current_scope->objects.contains(target_seg)) - { - last_match_path = current_path; - MUST(last_match_path.path.push_back(target_seg)); - } + 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.path.empty()) - { - MUST(last_match_path.prefix.push_back('\\')); + if (!last_match_path.empty()) return last_match_path; - } - return {}; } BAN::RefPtr AML::Namespace::find_object(const AML::NameString& relative_base, const AML::NameString& relative_path) { + LockGuard _(m_object_mutex); + auto canonical_path = resolve_path(relative_base, relative_path); if (!canonical_path.has_value()) return nullptr; - if (canonical_path->path.empty()) - return this; - BAN::RefPtr node = this; - for (const auto& seg : canonical_path->path) - { - // Resolve path validates that all nodes are scopes - ASSERT(node->is_scope()); - node = static_cast(node.ptr())->objects[seg]; - } - - return node; + 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 object) { + LockGuard _(m_object_mutex); + ASSERT(!object_path.path.empty()); ASSERT(object_path.path.back() == object->name); - auto parent_path = object_path; - parent_path.path.pop_back(); + auto canonical_path = resolve_path(parse_context.scope, object_path, true); + ASSERT(canonical_path.has_value()); - auto parent_object = find_object(parse_context.scope, parent_path); - if (!parent_object) + if (canonical_path->empty()) { - AML_ERROR("Parent object not found"); + AML_ERROR("Trying to add root namespace"); return false; } - if (!parent_object->is_scope()) + if (m_objects.contains(canonical_path.value())) { - AML_ERROR("Parent object is not a scope"); + AML_ERROR("Object '{}' already exists", canonical_path.value()); return false; } - auto* parent_scope = static_cast(parent_object.ptr()); - if (parent_scope->objects.contains(object->name)) - { - AML_ERROR("Object already exists"); - return false; - } + auto canonical_scope = AML::NameString(canonical_path.value()); - object->parent = parent_scope; - - MUST(parent_scope->objects.insert(object->name, object)); - - auto canonical_scope = resolve_path(parse_context.scope, object_path); - ASSERT(canonical_scope.has_value()); + MUST(m_objects.insert(canonical_path.value(), object)); if (object->is_scope()) { auto* scope = static_cast(object.ptr()); - scope->scope = canonical_scope.value(); + scope->scope = canonical_scope; } - MUST(parse_context.created_objects.push_back(BAN::move(canonical_scope.release_value()))); + + MUST(parse_context.created_objects.push_back(canonical_scope)); return true; } bool AML::Namespace::remove_named_object(const AML::NameString& absolute_path) { - auto object = find_object({}, absolute_path); - if (!object) + LockGuard _(m_object_mutex); + + auto canonical_path = resolve_path({}, absolute_path); + if (!canonical_path.has_value()) { - AML_ERROR("Object {} not found", absolute_path); + AML_ERROR("Trying to delete non-existent object '{}'", absolute_path); return false; } - if (object.ptr() == this) + if (canonical_path->empty()) { - AML_ERROR("Trying to remove root object"); + AML_ERROR("Trying to remove root namespace"); return false; } - auto parent = object->parent; - ASSERT(parent->is_scope()); - - auto* parent_scope = static_cast(parent.ptr()); - parent_scope->objects.remove(object->name); + ASSERT(m_objects.contains(canonical_path.value())); + m_objects.remove(canonical_path.value()); return true; } @@ -202,6 +195,8 @@ namespace Kernel::ACPI { ASSERT(!s_root_namespace); s_root_namespace = MUST(BAN::RefPtr::create(NameSeg("\\"sv))); + s_root_namespace->scope = AML::NameString("\\"sv); + MUST(s_root_namespace->m_objects.insert("\\"sv, s_root_namespace)); Integer::Constants::Zero = MUST(BAN::RefPtr::create(0, true)); Integer::Constants::One = MUST(BAN::RefPtr::create(1, true)); @@ -212,7 +207,7 @@ namespace Kernel::ACPI // Add predefined namespaces #define ADD_PREDEFIED_NAMESPACE(NAME) \ - ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\" NAME), MUST(BAN::RefPtr::create(NameSeg(NAME))))); + ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\" NAME), MUST(BAN::RefPtr::create(NameSeg(NAME))))); ADD_PREDEFIED_NAMESPACE("_GPE"sv); ADD_PREDEFIED_NAMESPACE("_PR"sv); ADD_PREDEFIED_NAMESPACE("_SB"sv); diff --git a/kernel/kernel/ACPI/AML/Scope.cpp b/kernel/kernel/ACPI/AML/Scope.cpp index 903e85ea..690fc4c2 100644 --- a/kernel/kernel/ACPI/AML/Scope.cpp +++ b/kernel/kernel/ACPI/AML/Scope.cpp @@ -23,12 +23,12 @@ namespace Kernel::ACPI auto named_object = Namespace::root_namespace()->find_object(context.scope, name_string.value()); if (!named_object) { - AML_ERROR("Scope name {} not found in namespace", name_string.value()); + AML_ERROR("Scope '{}' not found in namespace", name_string.value()); return ParseResult::Failure; } if (!named_object->is_scope()) { - AML_ERROR("Scope name {} does not name a namespace", name_string.value()); + AML_ERROR("Scope '{}' does not name a namespace", name_string.value()); return ParseResult::Failure; } @@ -43,7 +43,7 @@ namespace Kernel::ACPI return ParseResult::Failure; ParseContext scope_context; - scope_context.scope = resolved_scope.release_value(); + 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) @@ -64,19 +64,27 @@ namespace Kernel::ACPI return ParseResult::Success; } - void AML::Scope::debug_print(int indent) const + static BAN::Optional evaluate_or_invoke(BAN::RefPtr object) { - AML_DEBUG_PRINT_INDENT(indent); - AML_DEBUG_PRINT("Scope "); - name.debug_print(); - AML_DEBUG_PRINTLN(" {"); - for (const auto& [name, object] : objects) + if (object->type != AML::Node::Type::Method) + return object->as_integer(); + + auto* method = static_cast(object.ptr()); + if (method->arg_count != 0) { - object->debug_print(indent + 1); - AML_DEBUG_PRINTLN(""); + AML_ERROR("Method has {} arguments, expected 0", method->arg_count); + return {}; } - AML_DEBUG_PRINT_INDENT(indent); - AML_DEBUG_PRINT("}"); + + BAN::Vector sync_stack; + auto result = method->evaluate({}, sync_stack); + if (!result.has_value()) + { + AML_ERROR("Failed to evaluate method"); + return {}; + } + + return result.value() ? result.value()->as_integer() : BAN::Optional(); } bool AML::initialize_scope(BAN::RefPtr scope) @@ -88,61 +96,60 @@ namespace Kernel::ACPI bool run_ini = true; bool init_children = true; - auto it = scope->objects.find(NameSeg("_STA"sv)); - if (scope->type != AML::Node::Type::Namespace && it != scope->objects.end() && it->value->type == Node::Type::Method) + if (auto sta = Namespace::root_namespace()->find_object(scope->scope, AML::NameString("_STA"sv))) { - auto* method = static_cast(it->value.ptr()); - if (method->arg_count != 0) - { - AML_ERROR("Method {}._STA has {} arguments, expected 0", scope->scope, method->arg_count); - return false; - } - BAN::Vector sync_stack; - auto result = method->evaluate({}, sync_stack); + auto result = evaluate_or_invoke(sta); if (!result.has_value()) - { - AML_ERROR("Failed to evaluate {}._STA, ignoring device", scope->scope); - return true; - } - auto result_value = result.has_value() ? result.value()->as_integer() : BAN::Optional(); - if (!result_value.has_value()) { AML_ERROR("Failed to evaluate {}._STA, return value could not be resolved to integer", scope->scope); - AML_ERROR(" Return value: "); - result.value()->debug_print(0); return false; } - run_ini = (result_value.value() & 0x01); - init_children = run_ini || (result_value.value() & 0x02); + + run_ini = (result.value() & 0x01); + init_children = run_ini || (result.value() & 0x02); } if (run_ini) { - auto it = scope->objects.find(NameSeg("_STA"sv)); - if (it != scope->objects.end() && it->value->type == Node::Type::Method) + auto ini = Namespace::root_namespace()->find_object(scope->scope, AML::NameString("_INI"sv)); + if (ini) { - auto* method = static_cast(it->value.ptr()); + if (ini->type != AML::Node::Type::Method) + { + AML_ERROR("Object {}._INI is not a method", scope->scope); + return false; + } + + auto* method = static_cast(ini.ptr()); if (method->arg_count != 0) { AML_ERROR("Method {}._INI has {} arguments, expected 0", scope->scope, method->arg_count); return false; } + BAN::Vector sync_stack; - method->evaluate({}, sync_stack); + auto result = method->evaluate({}, sync_stack); + if (!result.has_value()) + { + AML_ERROR("Failed to evaluate {}._INI, ignoring device", scope->scope); + return true; + } } } bool success = true; if (init_children) { - for (auto& [_, child] : scope->objects) - { - if (!child->is_scope()) - continue; - auto* child_scope = static_cast(child.ptr()); - if (!initialize_scope(child_scope)) - success = false; - } + Namespace::root_namespace()->for_each_child(scope->scope, + [&](const auto&, auto& child) + { + if (!child->is_scope()) + return; + auto* child_scope = static_cast(child.ptr()); + if (!initialize_scope(child_scope)) + success = false; + } + ); } return success; }