Kernel: Implement poweroff with my AML interpreter

This can succesfully poweroff qemu!
This commit is contained in:
Bananymous 2024-04-10 04:32:35 +03:00
parent 5be38d0702
commit 7a2be05c69
4 changed files with 101 additions and 0 deletions

View File

@ -22,6 +22,10 @@ namespace Kernel::ACPI
// 2: SAPIC
BAN::ErrorOr<void> enter_acpi_mode(uint8_t mode);
// This function will power off the system
// This function will return only if there was an error
void poweroff();
private:
ACPI() = default;
BAN::ErrorOr<void> initialize_impl();

View File

@ -22,6 +22,13 @@ namespace Kernel::ACPI::AML
: NamedObject(Node::Type::Name, name), object(BAN::move(object))
{}
BAN::RefPtr<AML::Node> evaluate() override
{
if (!object)
return {};
return object->evaluate();
}
static ParseResult parse(ParseContext& context);
virtual void debug_print(int indent) const override;
};

View File

@ -14,6 +14,25 @@ namespace Kernel::ACPI::AML
Package() : Node(Node::Type::Package) {}
BAN::RefPtr<AML::Node> evaluate() override
{
BAN::Vector<BAN::RefPtr<AML::Node>> evaluated_elements;
for (auto& element : elements)
{
auto evaluated = element->evaluate();
if (!evaluated)
{
AML_ERROR("Failed to evaluate element in package");
return {};
}
evaluated_elements.push_back(evaluated);
}
auto package = MUST(BAN::RefPtr<Package>::create());
package->elements = BAN::move(evaluated_elements);
return package;
}
static ParseResult parse(AML::ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);

View File

@ -5,7 +5,9 @@
#include <kernel/ACPI/AML/Device.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Method.h>
#include <kernel/ACPI/AML/Package.h>
#include <kernel/BootInfo.h>
#include <kernel/IO.h>
#include <kernel/Memory/PageTable.h>
#include <lai/core.h>
@ -252,6 +254,75 @@ namespace Kernel::ACPI
return nullptr;
}
void ACPI::poweroff()
{
if (!m_namespace)
{
dwarnln("ACPI namespace not initialized");
return;
}
auto s5_object = m_namespace->find_object({}, AML::NameString("\\_S5"));
if (!s5_object)
{
dwarnln("\\_S5 not found");
return;
}
auto s5_evaluated = s5_object->evaluate();
if (!s5_evaluated)
{
dwarnln("Failed to evaluate \\_S5");
return;
}
if (s5_evaluated->type != AML::Node::Type::Package)
{
dwarnln("\\_S5 is not a package");
return;
}
auto* s5_package = static_cast<AML::Package*>(s5_evaluated.ptr());
if (s5_package->elements.size() != 4)
{
dwarnln("\\_S5 package has {} elements, expected 4", s5_package->elements.size());
return;
}
auto pts_object = m_namespace->find_object({}, AML::NameString("\\_PTS"));
if (pts_object && pts_object->type == AML::Node::Type::Method)
{
auto* method = static_cast<AML::Method*>(pts_object.ptr());
if (method->arg_count != 1)
{
dwarnln("Method \\_PTS has {} arguments, expected 1", method->arg_count);
return;
}
AML::Method::Arguments args;
args[0] = MUST(BAN::RefPtr<AML::Register>::create(MUST(BAN::RefPtr<AML::Integer>::create(5))));
if (!method->evaluate(args).has_value())
{
dwarnln("Failed to evaluate \\_PTS");
return;
}
dprintln("Executed \\_PTS");
}
auto* fadt = static_cast<const FADT*>(get_header("FACP", 0));
uint16_t SLP_EN = 1 << 13;
uint16_t PM1a_CNT = fadt->pm1a_cnt_blk;
uint16_t PM1b_CNT = fadt->pm1b_cnt_blk;
uint32_t SLP_TYPa = s5_package->elements[0];
uint32_t SLP_TYPb = s5_package->elements[1];
dprintln("Entering sleep state S5");
IO::outw(PM1a_CNT, SLP_TYPa | SLP_EN);
if (PM1b_CNT != 0)
IO::outw(PM1b_CNT, SLP_TYPb | SLP_EN);
}
BAN::ErrorOr<void> ACPI::enter_acpi_mode(uint8_t mode)
{
if (!m_namespace)