forked from Bananymous/banan-os
Kernel: My AML parser can now enable ACPI mode on QEMU!
This commit is contained in:
parent
ff203d8d34
commit
5be38d0702
|
@ -16,6 +16,12 @@ namespace Kernel::ACPI
|
|||
|
||||
const SDTHeader* get_header(BAN::StringView signature, uint32_t index);
|
||||
|
||||
// mode
|
||||
// 0: PIC
|
||||
// 1: APIC
|
||||
// 2: SAPIC
|
||||
BAN::ErrorOr<void> enter_acpi_mode(uint8_t mode);
|
||||
|
||||
private:
|
||||
ACPI() = default;
|
||||
BAN::ErrorOr<void> initialize_impl();
|
||||
|
|
|
@ -54,8 +54,21 @@ namespace Kernel::ACPI::AML
|
|||
{}
|
||||
|
||||
BAN::RefPtr<Node> evaluate() override;
|
||||
bool store(BAN::RefPtr<Node> source) override;
|
||||
|
||||
void debug_print(int indent) const override;
|
||||
|
||||
private:
|
||||
struct AccessType
|
||||
{
|
||||
uint64_t offset;
|
||||
uint64_t mask;
|
||||
uint32_t access_size;
|
||||
uint32_t shift;
|
||||
};
|
||||
BAN::Optional<AccessType> determine_access_type() const;
|
||||
BAN::Optional<uint64_t> read_field(uint64_t offset, uint32_t access_size) const;
|
||||
bool write_field(uint64_t offset, uint32_t access_size, uint64_t value) const;
|
||||
};
|
||||
|
||||
struct Field
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Kernel::ACPI::AML
|
|||
{
|
||||
using Arguments = BAN::Array<BAN::RefPtr<AML::Register>, 7>;
|
||||
|
||||
Mutex mutex;
|
||||
uint8_t arg_count;
|
||||
bool serialized;
|
||||
uint8_t sync_level;
|
||||
|
@ -68,17 +69,23 @@ namespace Kernel::ACPI::AML
|
|||
return ParseResult::Success;
|
||||
}
|
||||
|
||||
BAN::Optional<BAN::RefPtr<AML::Node>> evaluate(Arguments args)
|
||||
BAN::Optional<BAN::RefPtr<AML::Node>> evaluate(Arguments args, uint8_t old_sync_level = 0)
|
||||
{
|
||||
ParseContext context;
|
||||
context.aml_data = term_list;
|
||||
context.scope = scope;
|
||||
context.method_args = args;
|
||||
context.sync_level = old_sync_level;
|
||||
for (auto& local : context.method_locals)
|
||||
local = MUST(BAN::RefPtr<AML::Register>::create());
|
||||
|
||||
BAN::Optional<BAN::RefPtr<AML::Node>> return_value;
|
||||
if (serialized)
|
||||
{
|
||||
mutex.lock();
|
||||
context.sync_level = BAN::Math::max(sync_level, old_sync_level);
|
||||
}
|
||||
|
||||
BAN::Optional<BAN::RefPtr<AML::Node>> return_value;
|
||||
while (context.aml_data.size() > 0)
|
||||
{
|
||||
auto parse_result = AML::parse_object(context);
|
||||
|
@ -88,7 +95,10 @@ namespace Kernel::ACPI::AML
|
|||
break;
|
||||
}
|
||||
if (!parse_result.success())
|
||||
{
|
||||
AML_ERROR("Method {} evaluate failed", scope);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (!context.created_objects.empty())
|
||||
|
@ -97,6 +107,9 @@ namespace Kernel::ACPI::AML
|
|||
context.created_objects.pop_back();
|
||||
}
|
||||
|
||||
if (serialized)
|
||||
mutex.unlock();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Scope.h>
|
||||
#include <kernel/Lock/Mutex.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Namespace : public AML::Scope
|
||||
{
|
||||
Mutex global_lock;
|
||||
|
||||
static BAN::RefPtr<AML::Namespace> root_namespace();
|
||||
|
||||
Namespace(NameSeg name) : AML::Scope(Node::Type::Namespace, name) {}
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace Kernel::ACPI::AML
|
|||
// we don't really need large contiguous memory
|
||||
BAN::LinkedList<AML::NameString> created_objects;
|
||||
|
||||
uint8_t sync_level { 0 };
|
||||
BAN::Array<BAN::RefPtr<Register>, 7> method_args;
|
||||
BAN::Array<BAN::RefPtr<Register>, 8> method_locals;
|
||||
};
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
#include <BAN/StringView.h>
|
||||
#include <kernel/ACPI/ACPI.h>
|
||||
#include <kernel/ACPI/AML.h>
|
||||
#include <kernel/ACPI/AML/Device.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/Method.h>
|
||||
#include <kernel/BootInfo.h>
|
||||
#include <kernel/Memory/PageTable.h>
|
||||
|
||||
|
@ -249,4 +252,61 @@ namespace Kernel::ACPI
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> ACPI::enter_acpi_mode(uint8_t mode)
|
||||
{
|
||||
if (!m_namespace)
|
||||
{
|
||||
dwarnln("ACPI namespace not initialized");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
// Evaluate \\_SB._INI
|
||||
auto _sb_ini = m_namespace->find_object({}, AML::NameString("\\_SB._INI"));
|
||||
if (_sb_ini && _sb_ini->type == AML::Node::Type::Method)
|
||||
{
|
||||
auto* method = static_cast<AML::Method*>(_sb_ini.ptr());
|
||||
if (method->arg_count != 0)
|
||||
{
|
||||
dwarnln("Method \\_SB._INI has {} arguments, expected 0", method->arg_count);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
method->evaluate({});
|
||||
}
|
||||
|
||||
// Initialize devices
|
||||
auto _sb = m_namespace->find_object({}, AML::NameString("\\_SB"));
|
||||
if (_sb && _sb->is_scope())
|
||||
{
|
||||
auto* scope = static_cast<AML::Scope*>(_sb.ptr());
|
||||
for (auto& [name, object] : scope->objects)
|
||||
{
|
||||
if (object->type == AML::Node::Type::Device)
|
||||
{
|
||||
auto* device = static_cast<AML::Device*>(object.ptr());
|
||||
device->initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate \\_PIC (mode)
|
||||
auto _pic = m_namespace->find_object({}, AML::NameString("\\_PIC"));
|
||||
if (_pic && _pic->type == AML::Node::Type::Method)
|
||||
{
|
||||
auto* method = static_cast<AML::Method*>(_pic.ptr());
|
||||
if (method->arg_count != 1)
|
||||
{
|
||||
dwarnln("Method \\_PIC has {} arguments, expected 1", method->arg_count);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
AML::Method::Arguments args;
|
||||
args[0] = MUST(BAN::RefPtr<AML::Register>::create(MUST(BAN::RefPtr<AML::Integer>::create(mode))));
|
||||
method->evaluate(args);
|
||||
}
|
||||
|
||||
dprintln("Entered ACPI mode");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include <BAN/ScopeGuard.h>
|
||||
#include <kernel/ACPI/AML/Field.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/IO.h>
|
||||
#include <kernel/PCI.h>
|
||||
|
||||
namespace Kernel::ACPI
|
||||
{
|
||||
|
@ -134,10 +137,9 @@ namespace Kernel::ACPI
|
|||
return ParseResult::Success;
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> AML::FieldElement::evaluate()
|
||||
{
|
||||
// Field LockRule only applies to modifying the field, not reading it
|
||||
|
||||
BAN::Optional<AML::FieldElement::AccessType> AML::FieldElement::determine_access_type() const
|
||||
{
|
||||
uint32_t access_size = 0;
|
||||
switch (access_rules.access_type)
|
||||
{
|
||||
|
@ -155,51 +157,213 @@ namespace Kernel::ACPI
|
|||
access_size = 8;
|
||||
break;
|
||||
case FieldRules::AccessType::Buffer:
|
||||
AML_TODO("FieldElement evaluate with access type Buffer");
|
||||
AML_TODO("FieldElement with access type Buffer");
|
||||
return {};
|
||||
}
|
||||
|
||||
uint64_t byte_offset = op_region->region_offset + (bit_offset / 8);
|
||||
if (auto rem = byte_offset % access_size)
|
||||
byte_offset -= rem;
|
||||
|
||||
if ((bit_offset % access_size) + bit_count > access_size * 8)
|
||||
{
|
||||
AML_ERROR("FieldElement over multiple access sizes");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (byte_offset + access_size > op_region->region_offset + op_region->region_length)
|
||||
{
|
||||
AML_ERROR("FieldElement out of bounds");
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t shift = bit_offset % access_size;
|
||||
uint64_t mask = ((uint64_t)1 << bit_count) - 1;
|
||||
|
||||
return AccessType {
|
||||
.offset = byte_offset,
|
||||
.mask = mask,
|
||||
.access_size = access_size,
|
||||
.shift = shift
|
||||
};
|
||||
}
|
||||
|
||||
BAN::Optional<uint64_t> AML::FieldElement::read_field(uint64_t access_offset, uint32_t access_size) const
|
||||
{
|
||||
switch (op_region->region_space)
|
||||
{
|
||||
case OpRegion::RegionSpace::SystemMemory:
|
||||
{
|
||||
uint64_t byte_offset = op_region->region_offset + (bit_offset / 8);
|
||||
if (auto rem = byte_offset % access_size)
|
||||
byte_offset -= rem;
|
||||
|
||||
if ((bit_offset % access_size) + bit_count > access_size * 8)
|
||||
{
|
||||
AML_ERROR("FieldElement evaluate over multiple access sizes");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (byte_offset + access_size > op_region->region_offset + op_region->region_length)
|
||||
{
|
||||
AML_ERROR("FieldElement evaluate out of bounds");
|
||||
return {};
|
||||
}
|
||||
|
||||
uint64_t result = 0;
|
||||
PageTable::with_fast_page(byte_offset & PAGE_ADDR_MASK, [&] {
|
||||
PageTable::with_fast_page(access_offset & PAGE_ADDR_MASK, [&] {
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: result = PageTable::fast_page_as_sized<uint8_t> ((byte_offset % PAGE_SIZE) / access_size); break;
|
||||
case 2: result = PageTable::fast_page_as_sized<uint16_t>((byte_offset % PAGE_SIZE) / access_size); break;
|
||||
case 4: result = PageTable::fast_page_as_sized<uint32_t>((byte_offset % PAGE_SIZE) / access_size); break;
|
||||
case 8: result = PageTable::fast_page_as_sized<uint64_t>((byte_offset % PAGE_SIZE) / access_size); break;
|
||||
case 1: result = PageTable::fast_page_as_sized<uint8_t> ((access_offset % PAGE_SIZE) / access_size); break;
|
||||
case 2: result = PageTable::fast_page_as_sized<uint16_t>((access_offset % PAGE_SIZE) / access_size); break;
|
||||
case 4: result = PageTable::fast_page_as_sized<uint32_t>((access_offset % PAGE_SIZE) / access_size); break;
|
||||
case 8: result = PageTable::fast_page_as_sized<uint64_t>((access_offset % PAGE_SIZE) / access_size); break;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
case OpRegion::RegionSpace::SystemIO:
|
||||
{
|
||||
uint64_t result = 0;
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: result = IO::inb(access_offset); break;
|
||||
case 2: result = IO::inw(access_offset); break;
|
||||
case 4: result = IO::inl(access_offset); break;
|
||||
default:
|
||||
AML_ERROR("FieldElement read_field (SystemIO) with access size {}", access_size);
|
||||
return {};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case OpRegion::RegionSpace::PCIConfig:
|
||||
{
|
||||
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#address-space-format
|
||||
// PCI configuration space is confined to segment 0, bus 0
|
||||
|
||||
result >>= bit_offset % access_size;
|
||||
result &= ((uint64_t)1 << bit_count) - 1;
|
||||
return MUST(BAN::RefPtr<Integer>::create(result));
|
||||
uint16_t device = (access_offset >> 32) & 0xFFFF;
|
||||
uint16_t function = (access_offset >> 16) & 0xFFFF;
|
||||
uint16_t offset = access_offset & 0xFFFF;
|
||||
|
||||
uint64_t result = 0;
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: result = PCI::PCIManager::read_config_byte(0, device, function, offset); break;
|
||||
case 2: result = PCI::PCIManager::read_config_word(0, device, function, offset); break;
|
||||
case 4: result = PCI::PCIManager::read_config_dword(0, device, function, offset); break;
|
||||
default:
|
||||
AML_ERROR("FieldElement read_field (PCIConfig) with access size {}", access_size);
|
||||
return {};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
default:
|
||||
AML_TODO("FieldElement evaluate with region space {}", static_cast<uint8_t>(op_region->region_space));
|
||||
AML_TODO("FieldElement read_field with region space {}", static_cast<uint8_t>(op_region->region_space));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool AML::FieldElement::write_field(uint64_t access_offset, uint32_t access_size, uint64_t value) const
|
||||
{
|
||||
switch (op_region->region_space)
|
||||
{
|
||||
case OpRegion::RegionSpace::SystemMemory:
|
||||
{
|
||||
PageTable::with_fast_page(access_offset & PAGE_ADDR_MASK, [&] {
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: PageTable::fast_page_as_sized<uint8_t> ((access_offset % PAGE_SIZE) / access_size) = value; break;
|
||||
case 2: PageTable::fast_page_as_sized<uint16_t>((access_offset % PAGE_SIZE) / access_size) = value; break;
|
||||
case 4: PageTable::fast_page_as_sized<uint32_t>((access_offset % PAGE_SIZE) / access_size) = value; break;
|
||||
case 8: PageTable::fast_page_as_sized<uint64_t>((access_offset % PAGE_SIZE) / access_size) = value; break;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
case OpRegion::RegionSpace::SystemIO:
|
||||
{
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: IO::outb(access_offset, value); break;
|
||||
case 2: IO::outw(access_offset, value); break;
|
||||
case 4: IO::outl(access_offset, value); break;
|
||||
default:
|
||||
AML_ERROR("FieldElement write_field (SystemIO) with access size {}", access_size);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case OpRegion::RegionSpace::PCIConfig:
|
||||
{
|
||||
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#address-space-format
|
||||
// PCI configuration space is confined to segment 0, bus 0
|
||||
|
||||
uint16_t device = (access_offset >> 32) & 0xFFFF;
|
||||
uint16_t function = (access_offset >> 16) & 0xFFFF;
|
||||
uint16_t offset = access_offset & 0xFFFF;
|
||||
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: PCI::PCIManager::write_config_byte(0, device, function, offset, value); break;
|
||||
case 2: PCI::PCIManager::write_config_word(0, device, function, offset, value); break;
|
||||
case 4: PCI::PCIManager::write_config_dword(0, device, function, offset, value); break;
|
||||
default:
|
||||
AML_ERROR("FieldElement write_field (PCIConfig) with access size {}", access_size);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
AML_TODO("FieldElement write_field with region space {}", static_cast<uint8_t>(op_region->region_space));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> AML::FieldElement::evaluate()
|
||||
{
|
||||
// Field LockRule only applies to modifying the field, not reading it
|
||||
|
||||
auto access_type = determine_access_type();
|
||||
if (!access_type.has_value())
|
||||
return {};
|
||||
|
||||
auto result = read_field(access_type->offset, access_type->access_size);
|
||||
if (!result.has_value())
|
||||
return {};
|
||||
|
||||
return MUST(BAN::RefPtr<Integer>::create((result.value() >> access_type->shift) & access_type->mask));
|
||||
}
|
||||
|
||||
bool AML::FieldElement::store(BAN::RefPtr<AML::Node> source)
|
||||
{
|
||||
auto source_integer = source->as_integer();
|
||||
if (!source_integer.has_value())
|
||||
{
|
||||
AML_TODO("FieldElement store with non-integer source, type {}", static_cast<uint8_t>(source->type));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto access_type = determine_access_type();
|
||||
if (!access_type.has_value())
|
||||
return false;
|
||||
|
||||
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
|
||||
Namespace::root_namespace()->global_lock.lock();
|
||||
BAN::ScopeGuard unlock_guard([&] {
|
||||
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
|
||||
Namespace::root_namespace()->global_lock.unlock();
|
||||
});
|
||||
|
||||
uint64_t to_write = 0;
|
||||
switch (access_rules.update_rule)
|
||||
{
|
||||
case FieldRules::UpdateRule::Preserve:
|
||||
{
|
||||
auto read_result = read_field(access_type->offset, access_type->access_size);
|
||||
if (!read_result.has_value())
|
||||
return false;
|
||||
to_write = read_result.value();
|
||||
to_write &= ~(access_type->mask << access_type->shift);
|
||||
to_write |= (source_integer.value() & access_type->mask) << access_type->shift;
|
||||
break;
|
||||
}
|
||||
case FieldRules::UpdateRule::WriteAsOnes:
|
||||
to_write = ~(access_type->mask << access_type->shift);
|
||||
to_write |= (source_integer.value() & access_type->mask) << access_type->shift;
|
||||
break;
|
||||
case FieldRules::UpdateRule::WriteAsZeros:
|
||||
to_write = 0;
|
||||
to_write |= (source_integer.value() & access_type->mask) << access_type->shift;
|
||||
break;
|
||||
}
|
||||
|
||||
return write_field(access_type->offset, access_type->access_size, to_write);
|
||||
}
|
||||
|
||||
void AML::FieldElement::debug_print(int indent) const
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
|
|
|
@ -183,7 +183,7 @@ namespace Kernel::ACPI
|
|||
args[i] = MUST(BAN::RefPtr<AML::Register>::create(arg.node()));
|
||||
}
|
||||
|
||||
auto result = method->evaluate(args);
|
||||
auto result = method->evaluate(args, context.sync_level);
|
||||
if (!result.has_value())
|
||||
{
|
||||
AML_ERROR("Failed to evaluate {}", name_string.value());
|
||||
|
|
|
@ -154,6 +154,9 @@ extern "C" void kernel_main(uint32_t boot_magic, uint32_t boot_info)
|
|||
dprintln("Virtual TTY initialized");
|
||||
}
|
||||
|
||||
if (ACPI::ACPI::get().enter_acpi_mode(InterruptController::get().is_using_apic()).is_error())
|
||||
dprintln("Failed to enter ACPI mode");
|
||||
|
||||
Random::initialize();
|
||||
dprintln("RNG initialized");
|
||||
|
||||
|
@ -174,8 +177,6 @@ static void init2(void*)
|
|||
|
||||
dprintln("Scheduler started");
|
||||
|
||||
InterruptController::get().enter_acpi_mode();
|
||||
|
||||
auto console = MUST(DevFileSystem::get().root_inode()->find_inode(cmdline.console));
|
||||
ASSERT(console->is_tty());
|
||||
((TTY*)console.ptr())->set_as_current();
|
||||
|
|
Loading…
Reference in New Issue