Kernel: Fix ACPI namespace initialization

Now _REG, _STA, _INI are called in the order my laptop expects them to
be called. This was kinda weird because what uACPI was doing did not
work.

\_SB_.PCI0.LPC0.EC0_.BAT0._STA required \_SB_.PCI0.LPC0.EC0_._REG to be
called
\_SB_.PCI0.LPC0.EC0_._REG required \_SB_.PCI0._STA to be called

Now I call all the _REG methods of a device after calling _STA/_INI and
after performing the whole _STA/_INI sequence i call rest of missing
_REG functions
This commit is contained in:
Bananymous 2024-12-18 18:30:45 +02:00
parent c26e347e91
commit 3a6cdfff45
7 changed files with 112 additions and 102 deletions

View File

@ -5,7 +5,6 @@ set(KERNEL_SOURCES
kernel/ACPI/AML/Namespace.cpp
kernel/ACPI/AML/Node.cpp
kernel/ACPI/AML/OpRegion.cpp
kernel/ACPI/AML/Scope.cpp
kernel/APIC.cpp
kernel/BootInfo.cpp
kernel/CPUID.cpp

View File

@ -4,6 +4,7 @@
#include <BAN/ByteSpan.h>
#include <BAN/Function.h>
#include <BAN/HashMap.h>
#include <BAN/HashSet.h>
#include <BAN/Iteration.h>
#include <kernel/ACPI/AML/Node.h>
@ -19,11 +20,10 @@ namespace Kernel::ACPI::AML
Namespace() = default;
~Namespace();
static BAN::ErrorOr<void> initialize_root_namespace();
static BAN::ErrorOr<void> prepare_root_namespace();
static Namespace& root_namespace();
// this has to be called after initalizing ACPI namespace
BAN::ErrorOr<void> initalize_op_regions();
BAN::ErrorOr<void> post_load_initialize();
BAN::ErrorOr<void> parse(BAN::ConstByteSpan);
@ -31,7 +31,7 @@ namespace Kernel::ACPI::AML
// returns empty scope if object already exited
BAN::ErrorOr<Scope> add_named_object(const Scope& scope, const NameString& name_string, Node&& node);
BAN::ErrorOr<Scope> add_named_object(const Scope& scope, const NameString& name_string, Reference* reference);
BAN::ErrorOr<Scope> add_alias(const Scope& scope, const NameString& name_string, Reference* reference);
BAN::ErrorOr<void> remove_named_object(const Scope& absolute_path);
@ -55,10 +55,16 @@ namespace Kernel::ACPI::AML
BAN::ErrorOr<void> opregion_call_reg(const Scope& scope, const Node& opregion);
BAN::ErrorOr<uint64_t> evaluate_sta(const Scope& scope);
BAN::ErrorOr<void> evaluate_ini(const Scope& scope);
BAN::ErrorOr<void> initialize_op_regions();
private:
bool m_has_parsed_namespace { false };
bool m_has_initialized_namespace { false };
BAN::HashMap<Scope, Reference*> m_named_objects;
BAN::HashMap<Scope, uint32_t> m_called_reg_bitmaps;
BAN::HashSet<Scope> m_aliases;
};
}

View File

@ -37,8 +37,6 @@ namespace Kernel::ACPI::AML
}
};
BAN::ErrorOr<void> initialize_scope(const Scope&);
}
namespace BAN

View File

@ -624,7 +624,7 @@ acpi_release_global_lock:
{
ASSERT(!m_namespace);
TRY(AML::Namespace::initialize_root_namespace());
TRY(AML::Namespace::prepare_root_namespace());
m_namespace = &AML::Namespace::root_namespace();
if (auto ret = load_aml_tables("DSDT"_sv, false); ret.is_error())
@ -663,22 +663,9 @@ acpi_release_global_lock:
dprintln("Entered ACPI mode");
dprintln("Calling opregion _REG methods");
if (auto ret = m_namespace->post_load_initialize(); ret.is_error())
dwarnln("Failed to initialize ACPI namespace: {}", ret.error());
if (auto ret = m_namespace->initalize_op_regions(); ret.is_error())
dwarnln("failed to call _REG methods: {}", ret.error());
dprintln("Initializing \\_SB");
// Initialize \\_SB
auto [sb_path, sb_obj] = TRY(m_namespace->find_named_object({}, TRY(AML::NameString::from_string("\\_SB_"_sv))));
if (sb_obj && sb_obj->node.is_scope())
if (auto ret = AML::initialize_scope(sb_path); ret.is_error())
dwarnln("Failed to initialize \\_SB: {}", ret.error());
dprintln("Evaluating \\_PIC");
// Evaluate \\_PIC (mode)
auto [pic_path, pic_obj] = TRY(m_namespace->find_named_object({}, TRY(AML::NameString::from_string("\\_PIC"_sv))));
if (pic_obj && pic_obj->node.type == AML::Node::Type::Method)
{
@ -699,7 +686,7 @@ acpi_release_global_lock:
TRY(AML::method_call(pic_path, pic_node, BAN::move(arguments)));
}
dprintln("Initializing ACPI interrupts");
dprintln("Evaluated \\_PIC({})", mode);
uint8_t irq = fadt().sci_int;
if (auto ret = InterruptController::get().reserve_irq(irq); ret.is_error())
@ -772,6 +759,8 @@ acpi_release_global_lock:
Process::create_kernel([](void*) { get().acpi_event_task(); }, nullptr);
}
dprintln("Initialized ACPI interrupts");
return {};
}

View File

@ -4,6 +4,9 @@
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/Headers.h>
#define STA_PRESENT 0x01
#define STA_FUNCTION 0x08
#include <ctype.h>
namespace Kernel::ACPI::AML
@ -43,7 +46,7 @@ namespace Kernel::ACPI::AML
delete reference;
}
BAN::ErrorOr<void> Namespace::initialize_root_namespace()
BAN::ErrorOr<void> 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
@ -88,15 +91,11 @@ namespace Kernel::ACPI::AML
return BAN::Error::from_errno(EINVAL);
}
const auto arg0 = BAN::StringView(
reinterpret_cast<const char*>(args[0]->node.as.str_buf->bytes),
args[0]->node.as.str_buf->size
);
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)
@ -132,10 +131,92 @@ namespace Kernel::ACPI::AML
return s_root_namespace;
}
BAN::ErrorOr<void> Namespace::initalize_op_regions()
BAN::ErrorOr<uint64_t> Namespace::evaluate_sta(const Scope& scope)
{
m_has_parsed_namespace = true;
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<void> 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<void> Namespace::post_load_initialize()
{
BAN::Vector<Scope> to_init;
TRY(to_init.push_back({}));
while (!to_init.empty())
{
BAN::Vector<Scope> 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;
}
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_FUNCTION))
{
auto child_path_copy = child_path.copy();
if (!child_path_copy.is_error())
(void)to_init_next.push_back(child_path_copy.release_value());
}
(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;
}
);
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<void> Namespace::initialize_op_regions()
{
for (const auto& [obj_path, obj_ref] : m_named_objects)
{
if (obj_ref->node.type != Node::Type::OpRegion)
@ -249,15 +330,15 @@ namespace Kernel::ACPI::AML
TRY(m_named_objects.insert(TRY(resolved_path.copy()), reference));
if (m_has_parsed_namespace && reference->node.type == Node::Type::OpRegion)
if (m_has_initialized_namespace && reference->node.type == Node::Type::OpRegion)
(void)opregion_call_reg(resolved_path, reference->node);
return resolved_path;
}
BAN::ErrorOr<Scope> Namespace::add_named_object(const Scope& scope, const NameString& name_string, Reference* reference)
BAN::ErrorOr<Scope> Namespace::add_alias(const Scope& scope, const NameString& name_string, Reference* reference)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_named_object('{}', '{}', {})", scope, name_string, reference->node);
dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_alias('{}', '{}', {})", scope, name_string, reference->node);
auto resolved_path = TRY(resolve_path(scope, name_string));
if (m_named_objects.contains(resolved_path))
@ -267,6 +348,8 @@ namespace Kernel::ACPI::AML
reference->ref_count++;
TRY(m_named_objects.insert(TRY(resolved_path.copy()), reference));
TRY(m_aliases.insert(TRY(resolved_path.copy())));
return resolved_path;
}

View File

@ -1622,7 +1622,7 @@ namespace Kernel::ACPI::AML
return {};
}
TRY(Namespace::root_namespace().add_named_object(context.scope, object_name_string, source_ref));
TRY(Namespace::root_namespace().add_alias(context.scope, object_name_string, source_ref));
return {};
}

View File

@ -1,65 +0,0 @@
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/Scope.h>
namespace Kernel::ACPI::AML
{
BAN::ErrorOr<void> initialize_scope(const Scope& scope)
{
bool run_ini = true;
bool init_children = true;
if (auto [sta_path, sta_obj] = TRY(Namespace::root_namespace().find_named_object(scope, TRY(NameString::from_string("_STA"_sv)), true)); sta_obj)
{
auto sta_result = TRY(evaluate_node(sta_path, sta_obj->node));
if (sta_result.type != Node::Type::Integer)
{
dwarnln("Object {} evaluated to {}", sta_path, sta_result);
return BAN::Error::from_errno(EFAULT);
}
run_ini = (sta_result.as.integer.value & 0x01);
init_children = run_ini || (sta_result.as.integer.value & 0x02);
}
if (run_ini)
{
if (auto [ini_path, ini_obj] = TRY(Namespace::root_namespace().find_named_object(scope, TRY(NameString::from_string("_INI"_sv)), true)); ini_obj)
{
auto& ini_node = ini_obj->node;
if (ini_node.type != Node::Type::Method)
{
dwarnln("Object {} is not a method", ini_path);
return BAN::Error::from_errno(EFAULT);
}
if (ini_node.as.method.arg_count != 0)
{
dwarnln("Method {} takes {} arguments, expected 0", ini_path, ini_node.as.method.arg_count);
return BAN::Error::from_errno(EFAULT);
}
TRY(method_call(ini_path, ini_node, {}));
}
}
BAN::ErrorOr<void> result {};
if (init_children)
{
TRY(Namespace::root_namespace().for_each_child(scope,
[&result](const Scope& child_path, Reference* child) -> BAN::Iteration
{
if (!child->node.is_scope())
return BAN::Iteration::Continue;
if (auto ret = initialize_scope(child_path); ret.is_error())
result = ret.release_error();
return BAN::Iteration::Continue;
}
));
}
return result;
}
}