#include #include #include #include #define STA_PRESENT 0x01 #define STA_FUNCTIONAL 0x08 #include namespace Kernel::ACPI::AML { static Namespace s_root_namespace; static constexpr BAN::StringView s_supported_osi_strings[] { "Windows 2000"_sv, "Windows 2001"_sv, "Windows 2001 SP1"_sv, "Windows 2001.1"_sv, "Windows 2001 SP2"_sv, "Windows 2001.1 SP1"_sv, "Windows 2006.1"_sv, "Windows 2006 SP1"_sv, "Windows 2006 SP2"_sv, "Windows 2009"_sv, "Windows 2012"_sv, "Windows 2013"_sv, "Windows 2015"_sv, "Windows 2016"_sv, "Windows 2017"_sv, "Windows 2017.2"_sv, "Windows 2018"_sv, "Windows 2018.2"_sv, "Windows 2019"_sv, "Extended Address Space Descriptor"_sv, // just to pass osi test from uACPI :D "AnotherTestString"_sv, }; Namespace::~Namespace() { for (auto& [_, reference] : m_named_objects) if (--reference->ref_count == 0) delete reference; } BAN::ErrorOr Namespace::prepare_root_namespace() { // https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html?highlight=predefined#predefined-root-namespaces const auto add_predefined_root_namespace = [](const char* name) -> BAN::ErrorOr { ParseContext dummy_context {}; Node predefined {}; predefined.type = Node::Type::PredefinedScope; TRY(s_root_namespace.add_named_object(dummy_context, TRY(NameString::from_string(name)), BAN::move(predefined))); return {}; }; TRY(add_predefined_root_namespace("\\")); TRY(add_predefined_root_namespace("\\_GPE")); TRY(add_predefined_root_namespace("\\_PR_")); TRY(add_predefined_root_namespace("\\_SB_")); TRY(add_predefined_root_namespace("\\_SI_")); TRY(add_predefined_root_namespace("\\_TZ_")); ParseContext dummy_context {}; { Node revision; revision.type = Node::Type::Integer; revision.as.integer.value = 2; TRY(s_root_namespace.add_named_object(dummy_context, TRY(NameString::from_string("_REV")), BAN::move(revision))); } { auto osi_string = TRY(NameString::from_string("\\_OSI")); Node method {}; method.type = Node::Type::Method; new (method.as.method.storage) Kernel::Mutex(); method.as.method.arg_count = 1; method.as.method.override_func = [](const BAN::Array& args) -> BAN::ErrorOr { ASSERT(args[0]); if (args[0]->node.type != Node::Type::String) { dwarnln("_OSI called with {}", args[0]->node); return BAN::Error::from_errno(EINVAL); } Node result {}; result.type = Node::Type::Integer; result.as.integer.value = 0; const auto arg0 = args[0]->node.as.str_buf->as_sv(); for (auto supported : s_supported_osi_strings) { if (supported != arg0) continue; result.as.integer.value = 0xFFFFFFFFFFFFFFFF; break; } return result; }; TRY(s_root_namespace.add_named_object(dummy_context, osi_string, BAN::move(method))); } { auto os_string = TRY(NameString::from_string("\\_OS_")); Node os_node = TRY(Node::create_string("Microsoft Windows NT"_sv)); TRY(s_root_namespace.add_named_object(dummy_context, os_string, BAN::move(os_node))); } { auto rev_string = TRY(NameString::from_string("\\_REV")); Node rev_node {}; rev_node.type = Node::Type::Integer; rev_node.as.integer.value = 2; TRY(s_root_namespace.add_named_object(dummy_context, rev_string, BAN::move(rev_node))); } { auto gl_string = TRY(NameString::from_string("\\_GL_")); Node mutex {}; mutex.type = Node::Type::Mutex; mutex.as.mutex = new Mutex(); mutex.as.mutex->ref_count = 1; mutex.as.mutex->sync_level = 0; mutex.as.mutex->global_lock = true; TRY(s_root_namespace.add_named_object(dummy_context, gl_string, BAN::move(mutex))); } return {}; } Namespace& Namespace::root_namespace() { return s_root_namespace; } BAN::ErrorOr Namespace::evaluate_sta(const Scope& scope) { auto [child_path, child_ref] = TRY(find_named_object(scope, TRY(NameString::from_string("_STA"_sv)))); if (child_ref == nullptr) return 0x0F; return TRY(convert_node(TRY(evaluate_node(child_path, child_ref->node)), ConvInteger, sizeof(uint64_t))).as.integer.value; } BAN::ErrorOr Namespace::evaluate_ini(const Scope& scope) { auto [child_path, child_ref] = TRY(find_named_object(scope, TRY(NameString::from_string("_INI"_sv)))); if (child_ref == nullptr) return {}; TRY(evaluate_node(child_path, child_ref->node)); return {}; } BAN::ErrorOr Namespace::post_load_initialize() { auto [sb_path, sb_ref] = TRY(find_named_object({}, TRY(NameString::from_string("\\_SB_"_sv)))); if (sb_ref != nullptr) TRY(evaluate_ini(sb_path)); BAN::Vector to_init; TRY(to_init.push_back({})); while (!to_init.empty()) { BAN::Vector to_init_next; for (const Scope& current : to_init) { TRY(for_each_child(current, [&](const Scope& child_path, Reference* child_ref) -> BAN::Iteration { if (m_aliases.contains(child_path)) return BAN::Iteration::Continue; switch (child_ref->node.type) { case Node::Type::Device: case Node::Type::Processor: case Node::Type::ThermalZone: case Node::Type::PredefinedScope: break; default: return BAN::Iteration::Continue; } (void)for_each_child(current, [&](const Scope& opregion_path, Reference* opregion_ref) -> BAN::Iteration { if (opregion_ref->node.type == Node::Type::OpRegion) (void)opregion_call_reg(opregion_path, opregion_ref->node); return BAN::Iteration::Continue; } ); auto sta_ret = evaluate_sta(child_path); if (sta_ret.is_error()) return BAN::Iteration::Continue; if (sta_ret.value() & STA_PRESENT) (void)evaluate_ini(child_path); if ((sta_ret.value() & STA_PRESENT) || (sta_ret.value() & STA_FUNCTIONAL)) { auto child_path_copy = child_path.copy(); if (!child_path_copy.is_error()) (void)to_init_next.push_back(child_path_copy.release_value()); } return BAN::Iteration::Continue; } )); } to_init = BAN::move(to_init_next); } m_has_initialized_namespace = true; if (auto ret = initialize_op_regions(); ret.is_error()) dwarnln("Failed to initialize all opregions: {}", ret.error()); return {}; } BAN::ErrorOr Namespace::initialize_op_regions() { for (const auto& [obj_path, obj_ref] : m_named_objects) { if (obj_ref->node.type != Node::Type::OpRegion) continue; // FIXME: if _REG adds stuff to namespace, iterators are invalidated (void)opregion_call_reg(obj_path, obj_ref->node); } return {}; } BAN::ErrorOr Namespace::opregion_call_reg(const Scope& scope, const Node& opregion) { ASSERT(opregion.type == Node::Type::OpRegion); const auto address_space = opregion.as.opregion.address_space; if (address_space == GAS::AddressSpaceID::SystemIO || address_space == GAS::AddressSpaceID::SystemMemory) return {}; const uint32_t address_space_u32 = static_cast(address_space); if (address_space_u32 >= 32) return {}; const uint32_t mask = static_cast(1) << address_space_u32; auto parent = TRY(scope.copy()); parent.parts.pop_back(); auto it = m_called_reg_bitmaps.find(parent); if (it != m_called_reg_bitmaps.end()) if (it->value & mask) return {}; auto [reg_path, reg_obj] = TRY(find_named_object(parent, TRY(NameString::from_string("_REG"_sv)), true)); if (reg_obj != nullptr) { if (reg_obj->node.type != Node::Type::Method) dwarnln("{} is not an method", reg_path); else { if (reg_obj->node.as.method.arg_count != 2) dwarnln("{} takes {} arguments", reg_obj->node.as.method.arg_count); else { Reference arg0; arg0.node.type = Node::Type::Integer; arg0.node.as.integer.value = address_space_u32; arg0.ref_count = 2; // evaluate should not delete Reference arg1; arg1.node.type = Node::Type::Integer; arg1.node.as.integer.value = 1; arg1.ref_count = 2; // evaluate should not delete BAN::Array args(nullptr); args[0] = &arg0; args[1] = &arg1; if (auto ret = method_call(reg_path, reg_obj->node, BAN::move(args)); ret.is_error()) dwarnln("Failed to evaluate {}: {}", reg_path, ret.error()); } } } if (it != m_called_reg_bitmaps.end()) it->value |= mask; else TRY(m_called_reg_bitmaps.insert(BAN::move(parent), mask)); return {}; } BAN::ErrorOr Namespace::resolve_path(const Scope& scope, const NameString& name_string) { Scope resolved_path; if (name_string.base == NameString::base_root) { TRY(resolved_path.parts.reserve(name_string.parts.size())); for (uint32_t part : name_string.parts) TRY(resolved_path.parts.push_back(part)); } else { const uint32_t scope_segment_count = (name_string.base < scope.parts.size()) ? scope.parts.size() - name_string.base : 0; TRY(resolved_path.parts.reserve(scope_segment_count + name_string.parts.size())); for (size_t i = 0; i < scope_segment_count; i++) TRY(resolved_path.parts.push_back(scope.parts[i])); for (uint32_t part : name_string.parts) TRY(resolved_path.parts.push_back(part)); } return resolved_path; } BAN::ErrorOr Namespace::add_named_object(ParseContext& context, const NameString& name_string, Node&& node) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_named_object('{}', '{}', {})", context.scope, name_string, node); auto resolved_path = TRY(resolve_path(context.scope, name_string)); if (m_named_objects.contains(resolved_path)) return Scope(); auto* reference = new Reference(); if (reference == nullptr) return BAN::Error::from_errno(ENOMEM); reference->node = BAN::move(node); reference->ref_count = 1; TRY(m_named_objects.insert(TRY(resolved_path.copy()), reference)); if (m_has_initialized_namespace && reference->node.type == Node::Type::OpRegion) (void)opregion_call_reg(resolved_path, reference->node); TRY(context.created_nodes.push_back(TRY(resolved_path.copy()))); return resolved_path; } BAN::ErrorOr Namespace::add_alias(ParseContext& context, const NameString& name_string, Reference* reference) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_alias('{}', '{}', {})", context.scope, name_string, reference->node); auto resolved_path = TRY(resolve_path(context.scope, name_string)); if (m_named_objects.contains(resolved_path)) return Scope(); ASSERT(reference->ref_count >= 1); reference->ref_count++; TRY(m_named_objects.insert(TRY(resolved_path.copy()), reference)); TRY(m_aliases.insert(TRY(resolved_path.copy()))); TRY(context.created_nodes.push_back(TRY(resolved_path.copy()))); return resolved_path; } BAN::ErrorOr Namespace::remove_named_object(const Scope& absolute_path) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "remove_named_object('{}')", absolute_path); auto it = m_named_objects.find(absolute_path); if (it == m_named_objects.end()) return BAN::Error::from_errno(ENOENT); if (--it->value->ref_count == 0) delete it->value; m_named_objects.remove(it); return {}; } BAN::ErrorOr Namespace::find_named_object(const Scope& scope, const NameString& name_string, bool force_absolute) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "find_named_object('{}', '{}')", scope, name_string); if (force_absolute || name_string.base != 0) { // Absolute path auto resolved_path = TRY(resolve_path(scope, name_string)); auto it = m_named_objects.find(resolved_path); if (it != m_named_objects.end()) { return FindResult { .path = BAN::move(resolved_path), .node = it->value, }; } return FindResult { .path = {}, .node = nullptr, }; } // Relative path Scope path_guess; TRY(path_guess.parts.reserve(scope.parts.size() + name_string.parts.size())); for (const auto& part : scope.parts) TRY(path_guess.parts.push_back(part)); for (const auto& part : name_string.parts) TRY(path_guess.parts.push_back(part)); auto it = m_named_objects.find(path_guess); if (it != m_named_objects.end()) { return FindResult { .path = BAN::move(path_guess), .node = it->value, }; } for (size_t i = 0; i < scope.parts.size(); i++) { path_guess.parts.remove(scope.parts.size() - i - 1); auto it = m_named_objects.find(path_guess); if (it != m_named_objects.end()) { return FindResult { .path = BAN::move(path_guess), .node = it->value, }; } } return FindResult { .path = {}, .node = nullptr, }; } BAN::ErrorOr Namespace::find_reference_scope(const Reference* reference) { for (const auto& [obj_path, obj_ref] : m_named_objects) { if (obj_ref != reference) continue; return obj_path.copy(); } return BAN::Error::from_errno(ENOENT); } BAN::ErrorOr Namespace::for_each_child(const Scope& scope, const BAN::Function& callback) { for (const auto& [obj_path, obj_ref] : m_named_objects) { if (obj_path.parts.size() != scope.parts.size() + 1) continue; bool match = true; for (size_t i = 0; i < scope.parts.size() && match; i++) match = obj_path.parts[i] == scope.parts[i]; if (!match) continue; auto name = BAN::StringView(reinterpret_cast(&obj_path.parts.back()), 4); auto iteration = callback(name, obj_ref); if (iteration == BAN::Iteration::Break) break; ASSERT(iteration == BAN::Iteration::Continue); } return {}; } BAN::ErrorOr Namespace::for_each_child(const Scope& scope, const BAN::Function& callback) { for (const auto& [obj_path, obj_ref] : m_named_objects) { if (obj_path.parts.size() != scope.parts.size() + 1) continue; bool match = true; for (size_t i = 0; i < scope.parts.size() && match; i++) match = obj_path.parts[i] == scope.parts[i]; if (!match) continue; auto iteration = callback(obj_path, obj_ref); if (iteration == BAN::Iteration::Break) break; ASSERT(iteration == BAN::Iteration::Continue); } return {}; } static bool is_valid_eisa_id(BAN::StringView eisa_id) { if (eisa_id.size() != 7) return false; for (size_t i = 0; i < 3; i++) if (!isupper(eisa_id[i])) return false; for (size_t i = 3; i < 7; i++) if (!isxdigit(eisa_id[i])) return false; return true; } static uint32_t encode_eisa_id(BAN::StringView eisa_id) { constexpr auto char_to_hex = [](char ch) -> uint32_t { if (isdigit(ch)) return ch - '0'; return tolower(ch) - 'a' + 10; }; uint16_t code = 0; code |= (eisa_id[2] - 0x40) << 0; code |= (eisa_id[1] - 0x40) << 5; code |= (eisa_id[0] - 0x40) << 10; uint32_t encoded = 0; encoded |= code >> 8; encoded |= (code & 0xFF) << 8; encoded |= (char_to_hex(eisa_id[3]) << 20) | (char_to_hex(eisa_id[4]) << 16); encoded |= (char_to_hex(eisa_id[5]) << 28) | (char_to_hex(eisa_id[6]) << 24); return encoded; } BAN::ErrorOr> Namespace::find_device_with_eisa_id(BAN::StringView eisa_id) { if (!is_valid_eisa_id(eisa_id)) { dwarnln("Invalid EISA id '{}'", eisa_id); return BAN::Error::from_errno(EINVAL); } const uint32_t encoded = encode_eisa_id(eisa_id); BAN::Vector result; for (const auto& [obj_path, obj_ref] : m_named_objects) { if (obj_ref->node.type != Node::Type::Device) continue; auto [_, hid] = TRY(find_named_object(obj_path, TRY(NameString::from_string("_HID"_sv)), true)); if (hid == nullptr) continue; uint32_t device_hid = 0; if (hid->node.type == Node::Type::Integer) device_hid = hid->node.as.integer.value; else if (hid->node.type == Node::Type::String && is_valid_eisa_id(hid->node.as.str_buf->as_sv())) device_hid = encode_eisa_id(hid->node.as.str_buf->as_sv()); else continue; if (device_hid != encoded) continue; TRY(result.push_back(TRY(obj_path.copy()))); } return result; } BAN::ErrorOr Namespace::evaluate(const Scope& scope, BAN::StringView path) { auto name_string = TRY(NameString::from_string(path)); auto [object_path, object] = TRY(find_named_object(scope, name_string)); if (object == nullptr) return BAN::Error::from_errno(ENOENT); return evaluate_node(object_path, object->node); } BAN::ErrorOr Namespace::parse(BAN::ConstByteSpan aml_data) { if (aml_data.size() < sizeof(SDTHeader)) return BAN::Error::from_errno(EINVAL); const auto& sdt_header = aml_data.as(); if (aml_data.size() < sdt_header.length) return BAN::Error::from_errno(EINVAL); aml_data = aml_data.slice(sizeof(SDTHeader)); ParseContext context; context.aml_data = aml_data; TRY(context.allocate_locals()); while (!context.aml_data.empty()) { auto parse_result = parse_node_or_execution_flow(context); if (parse_result.is_error()) { dwarnln("Failed to parse root namespace: {}", parse_result.error()); return parse_result.release_error(); } auto [execution_flow, node] = parse_result.release_value(); if (execution_flow == ExecutionFlow::Normal) continue; if (execution_flow == ExecutionFlow::Return) break; dwarnln("Root namespace got execution flow {}", static_cast(execution_flow)); return BAN::Error::from_errno(EINVAL); } return {}; } }