Kernel: Replace the old AML interpreter by a new, better one
The old AML interpreter was trash and did not follow value/reference semantics at all. It was also super slow, one of my machines taking over 7 seconds to parse ACPI namespace and call _INI and _STA.
This commit is contained in:
parent
463bb72d24
commit
869f4011a1
|
@ -1,16 +1,10 @@
|
|||
set(KERNEL_SOURCES
|
||||
font/prefs.psf.o
|
||||
kernel/ACPI/ACPI.cpp
|
||||
kernel/ACPI/AML.cpp
|
||||
kernel/ACPI/AML/Field.cpp
|
||||
kernel/ACPI/AML/Integer.cpp
|
||||
kernel/ACPI/AML/NamedObject.cpp
|
||||
kernel/ACPI/AML/Namespace.cpp
|
||||
kernel/ACPI/AML/Node.cpp
|
||||
kernel/ACPI/AML/Package.cpp
|
||||
kernel/ACPI/AML/Register.cpp
|
||||
kernel/ACPI/AML/OpRegion.cpp
|
||||
kernel/ACPI/AML/Scope.cpp
|
||||
kernel/ACPI/AML/String.cpp
|
||||
kernel/APIC.cpp
|
||||
kernel/BootInfo.cpp
|
||||
kernel/CPUID.cpp
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Vector.h>
|
||||
#include <kernel/ACPI/AML/Method.h>
|
||||
#include <kernel/ACPI/AML/Namespace.h>
|
||||
#include <kernel/ACPI/Headers.h>
|
||||
#include <kernel/Memory/Types.h>
|
||||
|
@ -29,13 +28,8 @@ 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();
|
||||
|
||||
// This function will reset the system
|
||||
// This function will return only if there was an error
|
||||
void reset();
|
||||
BAN::ErrorOr<void> poweroff();
|
||||
BAN::ErrorOr<void> reset();
|
||||
|
||||
void handle_irq() override;
|
||||
|
||||
|
@ -45,9 +39,11 @@ namespace Kernel::ACPI
|
|||
|
||||
FADT& fadt() { return *m_fadt; }
|
||||
|
||||
bool prepare_sleep(uint8_t sleep_state);
|
||||
BAN::ErrorOr<void> prepare_sleep(uint8_t sleep_state);
|
||||
void acpi_event_task();
|
||||
|
||||
BAN::ErrorOr<void> load_aml_tables(BAN::StringView name, bool all);
|
||||
|
||||
private:
|
||||
paddr_t m_header_table_paddr = 0;
|
||||
vaddr_t m_header_table_vaddr = 0;
|
||||
|
@ -65,10 +61,12 @@ namespace Kernel::ACPI
|
|||
FADT* m_fadt { nullptr };
|
||||
|
||||
ThreadBlocker m_event_thread_blocker;
|
||||
BAN::Array<BAN::RefPtr<AML::Method>, 0xFF> m_gpe_methods;
|
||||
|
||||
AML::Scope m_gpe_scope;
|
||||
BAN::Array<AML::Reference*, 0xFF> m_gpe_methods { nullptr };
|
||||
|
||||
bool m_hardware_reduced { false };
|
||||
BAN::RefPtr<AML::Namespace> m_namespace;
|
||||
AML::Namespace* m_namespace { nullptr };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/Headers.h>
|
||||
#include <kernel/ACPI/AML/Namespace.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
BAN::RefPtr<AML::Namespace> initialize_namespace();
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/NamedObject.h>
|
||||
#include <kernel/ACPI/AML/Names.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Alias
|
||||
{
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::AliasOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto source_string = AML::NameString::parse(context.aml_data);
|
||||
if (!source_string.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto source_object = AML::Namespace::root_namespace()->find_object(context.scope, source_string.value(), AML::Namespace::FindMode::Normal);
|
||||
auto alias_string = AML::NameString::parse(context.aml_data);
|
||||
if (!alias_string.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
if (!source_object)
|
||||
{
|
||||
AML_PRINT("Alias target could not be found");
|
||||
return ParseResult::Success;
|
||||
}
|
||||
|
||||
if (!Namespace::root_namespace()->add_named_object(context, alias_string.value(), source_object))
|
||||
return ParseResult::Success;
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
AML_DEBUG_PRINT("Alias \"");
|
||||
alias_string->debug_print();
|
||||
AML_DEBUG_PRINT("\" => ");
|
||||
source_object->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,312 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Pkg.h>
|
||||
#include <kernel/ACPI/AML/String.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Buffer final : public AML::Node
|
||||
{
|
||||
BAN::Vector<uint8_t> buffer;
|
||||
|
||||
Buffer()
|
||||
: AML::Node(Node::Type::Buffer)
|
||||
{}
|
||||
|
||||
BAN::Optional<bool> logical_compare(BAN::RefPtr<AML::Node> node, AML::Byte binaryop)
|
||||
{
|
||||
auto rhs_node = node ? node->convert(AML::Node::ConvBuffer) : BAN::RefPtr<AML::Node>();
|
||||
if (!rhs_node)
|
||||
{
|
||||
AML_ERROR("Buffer logical compare RHS cannot be converted to buffer");
|
||||
return {};
|
||||
}
|
||||
|
||||
(void)binaryop;
|
||||
AML_TODO("Logical compare buffer");
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
|
||||
{
|
||||
if (mask & AML::Node::ConvBuffer)
|
||||
return this;
|
||||
if (mask & AML::Node::ConvInteger)
|
||||
return as_integer();
|
||||
if (mask & AML::Node::ConvString)
|
||||
{
|
||||
AML_TODO("Convert BufferField to String");
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> node) override
|
||||
{
|
||||
ASSERT(node);
|
||||
auto conv_node = node->convert(AML::Node::ConvBuffer);
|
||||
if (!conv_node)
|
||||
{
|
||||
AML_ERROR("Buffer store could not convert to buffer");
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& conv_buffer = static_cast<AML::Buffer*>(conv_node.ptr())->buffer;
|
||||
MUST(buffer.resize(conv_buffer.size()));
|
||||
for (size_t i = 0; i < buffer.size(); i++)
|
||||
buffer[i] = conv_buffer[i];
|
||||
return this;
|
||||
}
|
||||
|
||||
static ParseResult parse(AML::ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::BufferOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto buffer_pkg = AML::parse_pkg(context.aml_data);
|
||||
if (!buffer_pkg.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto buffer_context = context;
|
||||
buffer_context.aml_data = buffer_pkg.value();
|
||||
|
||||
auto buffer_size_result = AML::parse_object(buffer_context);
|
||||
if (!buffer_size_result.success())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto buffer_size_node = buffer_size_result.node() ? buffer_size_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!buffer_size_node)
|
||||
{
|
||||
AML_ERROR("Buffer size is not an integer");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
const uint32_t actual_buffer_size = BAN::Math::max<uint32_t>(
|
||||
static_cast<AML::Integer*>(buffer_size_node.ptr())->value,
|
||||
buffer_context.aml_data.size()
|
||||
);
|
||||
|
||||
auto buffer = MUST(BAN::RefPtr<Buffer>::create());
|
||||
MUST(buffer->buffer.resize(actual_buffer_size, 0));
|
||||
for (uint32_t i = 0; i < buffer_context.aml_data.size(); i++)
|
||||
buffer->buffer[i] = buffer_context.aml_data[i];
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
buffer->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
return ParseResult(buffer);
|
||||
}
|
||||
|
||||
virtual void debug_print(int indent) const override
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("Buffer ({} bytes)", buffer.size());
|
||||
}
|
||||
|
||||
private:
|
||||
BAN::RefPtr<AML::Integer> as_integer()
|
||||
{
|
||||
uint64_t value = 0;
|
||||
for (size_t i = 0; i < BAN::Math::min<size_t>(buffer.size(), 8); i++)
|
||||
value |= static_cast<uint64_t>(buffer[i]) << (8 * i);
|
||||
return MUST(BAN::RefPtr<Integer>::create(value));
|
||||
}
|
||||
};
|
||||
|
||||
struct BufferField final : AML::NamedObject
|
||||
{
|
||||
BAN::RefPtr<AML::Node> buffer;
|
||||
size_t field_bit_offset;
|
||||
size_t field_bit_size;
|
||||
|
||||
template<typename T> requires BAN::is_same_v<T, AML::Buffer> || BAN::is_same_v<T, AML::String>
|
||||
BufferField(AML::NameSeg name, BAN::RefPtr<T> buffer, size_t field_bit_offset, size_t field_bit_size)
|
||||
: AML::NamedObject(Node::Type::BufferField, name)
|
||||
, buffer(buffer)
|
||||
, field_bit_offset(field_bit_offset)
|
||||
, field_bit_size(field_bit_size)
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
|
||||
{
|
||||
if (mask & AML::Node::ConvBufferField)
|
||||
return this;
|
||||
if (mask & AML::Node::ConvInteger)
|
||||
return as_integer();
|
||||
if (mask & AML::Node::ConvBuffer)
|
||||
{
|
||||
AML_TODO("Convert BufferField to Buffer");
|
||||
return {};
|
||||
}
|
||||
if (mask & AML::Node::ConvString)
|
||||
{
|
||||
AML_TODO("Convert BufferField to String");
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> node) override
|
||||
{
|
||||
ASSERT(buffer);
|
||||
ASSERT(buffer->type == AML::Node::Type::Buffer || buffer->type == AML::Node::Type::String);
|
||||
auto& buffer = (this->buffer->type == AML::Node::Type::Buffer)
|
||||
? static_cast<AML::Buffer*>(this->buffer.ptr())->buffer
|
||||
: static_cast<AML::String*>(this->buffer.ptr())->string;
|
||||
ASSERT(field_bit_offset + field_bit_size <= buffer.size() * 8);
|
||||
|
||||
auto value_node = node ? node->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!value_node)
|
||||
return {};
|
||||
const auto value = static_cast<AML::Integer*>(value_node.ptr())->value;
|
||||
|
||||
// TODO: optimize for whole byte accesses
|
||||
for (size_t i = 0; i < field_bit_size; i++)
|
||||
{
|
||||
const size_t bit = field_bit_offset + i;
|
||||
buffer[bit / 8] &= ~(1 << (bit % 8));
|
||||
buffer[bit / 8] |= ((value >> i) & 1) << (bit % 8);
|
||||
}
|
||||
|
||||
return value_node;
|
||||
}
|
||||
|
||||
static ParseResult parse(AML::ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
|
||||
bool offset_in_bytes;
|
||||
size_t field_bit_size;
|
||||
switch (static_cast<AML::Byte>(context.aml_data[0]))
|
||||
{
|
||||
case AML::Byte::CreateBitFieldOp:
|
||||
field_bit_size = 1;
|
||||
offset_in_bytes = false;
|
||||
break;
|
||||
case AML::Byte::CreateByteFieldOp:
|
||||
field_bit_size = 8;
|
||||
offset_in_bytes = true;
|
||||
break;
|
||||
case AML::Byte::CreateWordFieldOp:
|
||||
field_bit_size = 16;
|
||||
offset_in_bytes = true;
|
||||
break;
|
||||
case AML::Byte::CreateDWordFieldOp:
|
||||
field_bit_size = 32;
|
||||
offset_in_bytes = true;
|
||||
break;
|
||||
case AML::Byte::CreateQWordFieldOp:
|
||||
field_bit_size = 64;
|
||||
offset_in_bytes = true;
|
||||
break;
|
||||
case AML::Byte::ExtOpPrefix:
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::CreateFieldOp);
|
||||
field_bit_size = 0;
|
||||
offset_in_bytes = false;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
context.aml_data = context.aml_data.slice(1 + (static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix));
|
||||
|
||||
auto buffer_result = AML::parse_object(context);
|
||||
if (!buffer_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto buffer_node = buffer_result.node() ? buffer_result.node()->convert(AML::Node::ConvBuffer) : BAN::RefPtr<AML::Node>();
|
||||
if (!buffer_node || buffer_node->type != Node::Type::Buffer)
|
||||
{
|
||||
AML_ERROR("Buffer source does not evaluate to a Buffer");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
auto buffer = BAN::RefPtr<AML::Buffer>(static_cast<AML::Buffer*>(buffer_node.ptr()));
|
||||
|
||||
auto index_result = AML::parse_object(context);
|
||||
if (!index_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto index_node = index_result.node() ? index_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!index_node)
|
||||
{
|
||||
AML_ERROR("Failed to parse index for BufferField");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
if (field_bit_size == 0)
|
||||
{
|
||||
auto bit_count_result = AML::parse_object(context);
|
||||
if (!bit_count_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto bit_count_node = bit_count_result.node() ? bit_count_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!bit_count_node)
|
||||
{
|
||||
AML_ERROR("Failed to parse bit count for BufferField");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
field_bit_size = static_cast<AML::Integer*>(bit_count_node.ptr())->value;
|
||||
}
|
||||
|
||||
auto field_name = AML::NameString::parse(context.aml_data);
|
||||
if (!field_name.has_value())
|
||||
return ParseResult::Failure;
|
||||
if (field_name->path.empty())
|
||||
{
|
||||
AML_ERROR("Empty field name for BufferField");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
size_t field_bit_offset = static_cast<AML::Integer*>(index_node.ptr())->value;
|
||||
if (offset_in_bytes)
|
||||
field_bit_offset *= 8;
|
||||
|
||||
auto field = MUST(BAN::RefPtr<BufferField>::create(field_name->path.back(), buffer, field_bit_offset, field_bit_size));
|
||||
if (!Namespace::root_namespace()->add_named_object(context, field_name.value(), field))
|
||||
return ParseResult::Success;
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
field->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
|
||||
virtual void debug_print(int indent) const override
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("BufferField {} at bit offset {} ({} bits) to { ", name, field_bit_offset, field_bit_size);
|
||||
buffer->debug_print(0);
|
||||
AML_DEBUG_PRINT(" }");
|
||||
}
|
||||
|
||||
private:
|
||||
BAN::RefPtr<AML::Integer> as_integer()
|
||||
{
|
||||
ASSERT(buffer);
|
||||
ASSERT(buffer->type == AML::Node::Type::Buffer || buffer->type == AML::Node::Type::String);
|
||||
|
||||
const auto& buffer = (this->buffer->type == AML::Node::Type::Buffer)
|
||||
? static_cast<AML::Buffer*>(this->buffer.ptr())->buffer
|
||||
: static_cast<AML::String*>(this->buffer.ptr())->string;
|
||||
|
||||
uint64_t value = 0;
|
||||
|
||||
// TODO: optimize for whole byte accesses
|
||||
for (size_t i = 0; i < BAN::Math::min<size_t>(field_bit_size, 64); i++)
|
||||
{
|
||||
const size_t bit = field_bit_offset + i;
|
||||
value |= static_cast<uint64_t>((buffer[bit / 8] >> (bit % 8)) & 1) << i;
|
||||
}
|
||||
|
||||
return MUST(BAN::RefPtr<Integer>::create(value));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -145,19 +145,35 @@ namespace Kernel::ACPI::AML
|
|||
DataRegionOp = 0x88,
|
||||
};
|
||||
|
||||
static constexpr bool is_digit_char(uint8_t ch)
|
||||
inline constexpr bool is_digit_char(uint8_t ch)
|
||||
{
|
||||
return '0' <= ch && ch <= '9';
|
||||
}
|
||||
|
||||
static constexpr bool is_lead_name_char(uint8_t ch)
|
||||
inline constexpr bool is_lead_name_char(uint8_t ch)
|
||||
{
|
||||
return ('A' <= ch && ch <= 'Z') || ch == '_';
|
||||
}
|
||||
|
||||
static constexpr bool is_name_char(uint8_t ch)
|
||||
inline constexpr bool is_name_char(uint8_t ch)
|
||||
{
|
||||
return is_lead_name_char(ch) || is_digit_char(ch);
|
||||
}
|
||||
|
||||
inline constexpr bool is_name_string_start(uint8_t ch)
|
||||
{
|
||||
if (is_lead_name_char(ch))
|
||||
return true;
|
||||
switch (static_cast<AML::Byte>(ch))
|
||||
{
|
||||
case AML::Byte::RootChar:
|
||||
case AML::Byte::ParentPrefixChar:
|
||||
case AML::Byte::MultiNamePrefix:
|
||||
case AML::Byte::DualNamePrefix:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Buffer.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/String.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Concat
|
||||
{
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::ConcatOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto source1_result = AML::parse_object(context);
|
||||
if (!source1_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto source1 = source1_result.node() ? source1_result.node()->to_underlying() : BAN::RefPtr<AML::Node>();
|
||||
|
||||
auto source2_result = AML::parse_object(context);
|
||||
if (!source2_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto source2 = source1_result.node() ? source1_result.node()->to_underlying() : BAN::RefPtr<AML::Node>();
|
||||
|
||||
if (!source1 || !source2)
|
||||
{
|
||||
AML_ERROR("ConcatOp sources could not be parsed");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
switch (source1->type)
|
||||
{
|
||||
case AML::Node::Type::Integer:
|
||||
source1 = source1->convert(AML::Node::ConvBuffer);
|
||||
source2 = source2->convert(AML::Node::ConvBuffer);
|
||||
break;
|
||||
case AML::Node::Type::String:
|
||||
source2 = source2->convert(AML::Node::ConvString);
|
||||
break;
|
||||
case AML::Node::Type::Buffer:
|
||||
source2 = source2->convert(AML::Node::ConvBuffer);
|
||||
break;
|
||||
default:
|
||||
source1 = source1->convert(AML::Node::ConvString);
|
||||
source2 = source2->convert(AML::Node::ConvString);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!source1 || !source2)
|
||||
{
|
||||
AML_ERROR("ConcatOp sources could not be converted");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
ASSERT(source1->type == source2->type);
|
||||
|
||||
BAN::RefPtr<AML::Node> result;
|
||||
BAN::Vector<uint8_t>* result_data = nullptr;
|
||||
BAN::Vector<uint8_t>* source1_data = nullptr;
|
||||
BAN::Vector<uint8_t>* source2_data = nullptr;
|
||||
|
||||
switch (source1->type)
|
||||
{
|
||||
case AML::Node::Type::String:
|
||||
result = MUST(BAN::RefPtr<AML::String>::create());
|
||||
result_data = &static_cast<AML::String*>(result.ptr())->string;
|
||||
source1_data = &static_cast<AML::String*>(source1.ptr())->string;
|
||||
source2_data = &static_cast<AML::String*>(source2.ptr())->string;
|
||||
break;
|
||||
case AML::Node::Type::Buffer:
|
||||
result = MUST(BAN::RefPtr<AML::Buffer>::create());
|
||||
result_data = &static_cast<AML::Buffer*>(result.ptr())->buffer;
|
||||
source1_data = &static_cast<AML::Buffer*>(source1.ptr())->buffer;
|
||||
source2_data = &static_cast<AML::Buffer*>(source2.ptr())->buffer;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
ASSERT(result_data && source1_data && source2_data);
|
||||
|
||||
MUST(result_data->resize(source1_data->size() + source2_data->size()));
|
||||
for (size_t i = 0; i < source1_data->size(); i++)
|
||||
(*result_data)[i] = (*source1_data)[i];
|
||||
for (size_t i = 0; i < source2_data->size(); i++)
|
||||
(*result_data)[source1_data->size() + i] = (*source2_data)[i];
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
AML_DEBUG_PRINT("Concat ");
|
||||
source1->debug_print(0);
|
||||
AML_DEBUG_PRINT(", ");
|
||||
source2->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
if (context.aml_data.size() < 1)
|
||||
return ParseResult::Failure;
|
||||
|
||||
if (context.aml_data[0] == 0x00)
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
else
|
||||
{
|
||||
auto destination_result = AML::parse_object(context);
|
||||
if (!destination_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto destination = destination_result.node();
|
||||
if (!destination)
|
||||
{
|
||||
AML_ERROR("IndexOp failed to resolve destination");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
if (!destination->store(result))
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
return ParseResult(result);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Buffer.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/String.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Conversion
|
||||
{
|
||||
static ParseResult parse(AML::ParseContext& context)
|
||||
{
|
||||
const auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case AML::Byte::ToBufferOp:
|
||||
case AML::Byte::ToHexStringOp:
|
||||
case AML::Byte::ToIntegerOp:
|
||||
case AML::Byte::ToStringOp:
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
auto data_result = AML::parse_object(context);
|
||||
if (!data_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto data_node = data_result.node() ? data_result.node()->to_underlying() : BAN::RefPtr<AML::Node>();
|
||||
if (!data_node)
|
||||
{
|
||||
AML_ERROR("Conversion {2H} data could not be evaluated", static_cast<uint8_t>(opcode));
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
if (context.aml_data.size() < 1)
|
||||
{
|
||||
AML_ERROR("Conversion {2H} missing target", static_cast<uint8_t>(opcode));
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> target_node;
|
||||
if (context.aml_data[0] == 0x00)
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
else
|
||||
{
|
||||
auto target_result = AML::parse_object(context);
|
||||
if (!target_result.success())
|
||||
return ParseResult::Failure;
|
||||
target_node = target_result.node();
|
||||
if (!target_node)
|
||||
{
|
||||
AML_ERROR("Conversion {2H} target invalid", static_cast<uint8_t>(opcode));
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> converted;
|
||||
switch (opcode)
|
||||
{
|
||||
case AML::Byte::ToBufferOp:
|
||||
converted = data_node->convert(AML::Node::ConvBuffer);
|
||||
break;
|
||||
case AML::Byte::ToIntegerOp:
|
||||
converted = data_node->convert(AML::Node::ConvInteger);
|
||||
break;
|
||||
case AML::Byte::ToStringOp:
|
||||
converted = data_node->convert(AML::Node::ConvString);
|
||||
break;
|
||||
case AML::Byte::ToHexStringOp:
|
||||
{
|
||||
switch (data_node->type)
|
||||
{
|
||||
case AML::Node::Type::Integer:
|
||||
converted = MUST(BAN::RefPtr<AML::String>::create(
|
||||
MUST(BAN::String::formatted("0x{H}", static_cast<AML::Integer*>(data_node.ptr())->value))
|
||||
));
|
||||
break;
|
||||
case AML::Node::Type::String:
|
||||
converted = data_node->copy();
|
||||
break;
|
||||
case AML::Node::Type::Buffer:
|
||||
{
|
||||
const auto& buffer = static_cast<AML::Buffer*>(data_node.ptr())->buffer;
|
||||
|
||||
BAN::String temp;
|
||||
for (size_t i = 0; i < buffer.size(); i++)
|
||||
{
|
||||
const char* format = (i == 0) ? "0x{H}" : ", 0x{H}";
|
||||
MUST(temp.append(MUST(BAN::String::formatted(format, buffer[i]))));
|
||||
}
|
||||
|
||||
converted = MUST(BAN::RefPtr<AML::String>::create(temp));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
if (!converted)
|
||||
{
|
||||
AML_ERROR("Conversion {2H} could not convert from node type {}", static_cast<uint8_t>(opcode), static_cast<uint8_t>(data_node->type));
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
if (target_node && !target_node->store(converted))
|
||||
{
|
||||
AML_ERROR("Conversion {2H} failed to store converted value", static_cast<uint8_t>(opcode));
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
return ParseResult(converted);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Alias.h>
|
||||
#include <kernel/ACPI/AML/NamedObject.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Register.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct CopyObject
|
||||
{
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::CopyObjectOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto source_result = AML::parse_object(context);
|
||||
if (!source_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto source = source_result.node()
|
||||
? source_result.node()->to_underlying()
|
||||
: BAN::RefPtr<AML::Node>();
|
||||
if (!source)
|
||||
{
|
||||
AML_ERROR("CopyObject source is null");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto destination_result = AML::parse_object(context);
|
||||
if (!destination_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto destination = destination_result.node();
|
||||
if (!destination)
|
||||
{
|
||||
AML_ERROR("CopyObject destination is null");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
AML_DEBUG_PRINTLN("CopyObject {");
|
||||
source->debug_print(1);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
AML_DEBUG_PRINTLN("} to {");
|
||||
destination->debug_print(1);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
AML_DEBUG_PRINTLN("}");
|
||||
#endif
|
||||
|
||||
switch (destination->type)
|
||||
{
|
||||
case AML::Node::Type::Name:
|
||||
static_cast<AML::Name*>(destination.ptr())->object = source->copy();
|
||||
return source;
|
||||
case AML::Node::Type::Register:
|
||||
static_cast<AML::Register*>(destination.ptr())->value = source->copy();
|
||||
return source;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Method.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Pkg.h>
|
||||
#include <kernel/ACPI/AML/Scope.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Device final : public AML::Scope
|
||||
{
|
||||
Device(NameSeg name)
|
||||
: Scope(Node::Type::Device, name)
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
|
||||
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::DeviceOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto device_pkg = AML::parse_pkg(context.aml_data);
|
||||
if (!device_pkg.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto name_string = AML::NameString::parse(device_pkg.value());
|
||||
if (!name_string.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto device = MUST(BAN::RefPtr<Device>::create(name_string->path.back()));
|
||||
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), device))
|
||||
return ParseResult::Success;
|
||||
|
||||
return device->enter_context_and_parse_term_list(context, name_string.value(), device_pkg.value());
|
||||
}
|
||||
|
||||
virtual void debug_print(int indent) const override
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("Device ");
|
||||
name.debug_print();
|
||||
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("}");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/NamedObject.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ThreadBlocker.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Event final : public AML::NamedObject
|
||||
{
|
||||
BAN::Atomic<uint32_t> signal_count { 0 };
|
||||
ThreadBlocker thread_blocker;
|
||||
|
||||
Event(NameSeg name)
|
||||
: NamedObject(Node::Type::Event, name)
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
|
||||
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
|
||||
const auto ext_op = static_cast<AML::ExtOp>(context.aml_data[1]);
|
||||
switch (ext_op)
|
||||
{
|
||||
case AML::ExtOp::EventOp:
|
||||
return parse_event(context);
|
||||
case AML::ExtOp::ResetOp:
|
||||
case AML::ExtOp::SignalOp:
|
||||
case AML::ExtOp::WaitOp:
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto event_result = parse_object(context);
|
||||
if (!event_result.success())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto general_node = event_result.node();
|
||||
if (!general_node || general_node->type != Node::Type::Event)
|
||||
{
|
||||
AML_ERROR("Release, Wait or Signal does not name an event");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto* event_node = static_cast<AML::Event*>(general_node.ptr());
|
||||
|
||||
if (ext_op == AML::ExtOp::WaitOp)
|
||||
{
|
||||
auto timeout_result = parse_object(context);
|
||||
if (!timeout_result.success())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto timeout_node = timeout_result.node()
|
||||
? timeout_result.node()->convert(AML::Node::ConvInteger)
|
||||
: BAN::RefPtr<AML::Node>();
|
||||
if (!timeout_node)
|
||||
{
|
||||
AML_ERROR("Wait timeout does not evaluate to integer");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
const auto timeout_value = static_cast<AML::Integer*>(timeout_node.ptr())->value;
|
||||
|
||||
const uint64_t start_ms = SystemTimer::get().ms_since_boot();
|
||||
while (true)
|
||||
{
|
||||
auto expected = event_node->signal_count.load();
|
||||
while (true)
|
||||
{
|
||||
if (expected == 0)
|
||||
break;
|
||||
if (event_node->signal_count.compare_exchange(expected, expected - 1))
|
||||
return ParseResult(Integer::Constants::Zero);
|
||||
}
|
||||
|
||||
if (timeout_value >= 0xFFFF)
|
||||
event_node->thread_blocker.block_indefinite();
|
||||
else
|
||||
{
|
||||
const uint64_t current_ms = SystemTimer::get().ms_since_boot();
|
||||
if (current_ms >= start_ms + timeout_value)
|
||||
return ParseResult(Integer::Constants::Ones);
|
||||
event_node->thread_blocker.block_with_timeout_ms(start_ms + timeout_value - current_ms);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
switch (ext_op)
|
||||
{
|
||||
case AML::ExtOp::ResetOp:
|
||||
event_node->signal_count = 0;
|
||||
break;
|
||||
case AML::ExtOp::SignalOp:
|
||||
event_node->signal_count++;
|
||||
event_node->thread_blocker.unblock();
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
|
||||
virtual void debug_print(int indent) const override
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("Event ");
|
||||
name.debug_print();
|
||||
AML_DEBUG_PRINT(" (Signals: {})", signal_count.load());
|
||||
}
|
||||
|
||||
private:
|
||||
static ParseResult parse_event(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::EventOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto name_string = NameString::parse(context.aml_data);
|
||||
if (!name_string.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto event = MUST(BAN::RefPtr<Event>::create(name_string->path.back()));
|
||||
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), event))
|
||||
return ParseResult::Success;
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
event->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,283 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Buffer.h>
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/String.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Expression
|
||||
{
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
switch (static_cast<Byte>(context.aml_data[0]))
|
||||
{
|
||||
// unary
|
||||
case AML::Byte::IncrementOp:
|
||||
case AML::Byte::DecrementOp:
|
||||
{
|
||||
auto opcode = (static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::IncrementOp) ? AML::Byte::AddOp : AML::Byte::SubtractOp;
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto source_result = AML::parse_object(context);
|
||||
if (!source_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto conv_node = source_result.node() ? source_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!conv_node)
|
||||
{
|
||||
AML_ERROR("UnaryOp source not integer, type {}", static_cast<uint8_t>(source_result.node()->type));
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto source_node = static_cast<AML::Integer*>(conv_node.ptr());
|
||||
if (source_node->constant)
|
||||
{
|
||||
AML_ERROR("UnaryOp source is constant");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
source_node->value += (opcode == AML::Byte::AddOp) ? 1 : -1;
|
||||
return ParseResult(source_node);
|
||||
}
|
||||
case AML::Byte::NotOp:
|
||||
{
|
||||
auto source_result = AML::parse_object(context);
|
||||
if (!source_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto source_conv = source_result.node() ? source_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!source_conv)
|
||||
{
|
||||
AML_ERROR("NotOp source not an integer, type {}",
|
||||
static_cast<uint8_t>(source_result.node()->type)
|
||||
);
|
||||
if (source_result.node())
|
||||
source_result.node()->debug_print(1);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
const auto source_value = static_cast<AML::Integer*>(source_conv.ptr())->value;
|
||||
|
||||
BAN::RefPtr<AML::Node> target_node;
|
||||
if (context.aml_data[0] == 0x00)
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
else
|
||||
{
|
||||
auto target_result = AML::parse_object(context);
|
||||
if (!target_result.success())
|
||||
return ParseResult::Failure;
|
||||
target_node = target_result.node();
|
||||
if (!target_node)
|
||||
{
|
||||
AML_ERROR("NotOp target invalid");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
}
|
||||
|
||||
auto result_node = MUST(BAN::RefPtr<AML::Integer>::create(~source_value));
|
||||
if (target_node && !target_node->store(result_node))
|
||||
{
|
||||
AML_ERROR("NotOp failed to store result");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
return ParseResult(result_node);
|
||||
}
|
||||
case AML::Byte::LNotOp:
|
||||
{
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto source_result = AML::parse_object(context);
|
||||
if (!source_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto conv_node = source_result.node() ? source_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!conv_node)
|
||||
{
|
||||
AML_ERROR("UnaryOp source not integer, type {}", static_cast<uint8_t>(source_result.node()->type));
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
auto source_node = static_cast<AML::Integer*>(conv_node.ptr());
|
||||
if (!source_node)
|
||||
{
|
||||
AML_ERROR("Logical NotOp source is not integer");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto result = source_node->value ? Integer::Constants::Zero : Integer::Constants::Ones;
|
||||
return ParseResult(result);
|
||||
}
|
||||
case AML::Byte::AddOp:
|
||||
case AML::Byte::AndOp:
|
||||
case AML::Byte::ModOp:
|
||||
case AML::Byte::MultiplyOp:
|
||||
case AML::Byte::NandOp:
|
||||
case AML::Byte::NorOp:
|
||||
case AML::Byte::OrOp:
|
||||
case AML::Byte::ShiftLeftOp:
|
||||
case AML::Byte::ShiftRightOp:
|
||||
case AML::Byte::SubtractOp:
|
||||
case AML::Byte::XorOp:
|
||||
return parse_binary_op(context);
|
||||
case AML::Byte::LAndOp:
|
||||
case AML::Byte::LEqualOp:
|
||||
case AML::Byte::LGreaterOp:
|
||||
case AML::Byte::LLessOp:
|
||||
case AML::Byte::LOrOp:
|
||||
return parse_logical_binary_op(context);
|
||||
case AML::Byte::DivideOp:
|
||||
AML_TODO("DivideOp");
|
||||
return ParseResult::Failure;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static ParseResult parse_binary_op(ParseContext& context)
|
||||
{
|
||||
auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto lhs_result = AML::parse_object(context);
|
||||
if (!lhs_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto lhs_conv = lhs_result.node() ? lhs_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!lhs_conv)
|
||||
{
|
||||
AML_ERROR("BinaryOP {2H} LHS not an integer, type {}",
|
||||
static_cast<uint8_t>(opcode),
|
||||
static_cast<uint8_t>(lhs_result.node()->type)
|
||||
);
|
||||
if (lhs_result.node())
|
||||
lhs_result.node()->debug_print(1);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
const auto lhs_value = static_cast<AML::Integer*>(lhs_conv.ptr())->value;
|
||||
|
||||
auto rhs_result = AML::parse_object(context);
|
||||
if (!rhs_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto rhs_conv = rhs_result.node() ? rhs_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!rhs_conv)
|
||||
{
|
||||
AML_ERROR("BinaryOP {2H} RHS not an integer", static_cast<uint8_t>(opcode));
|
||||
if (rhs_result.node())
|
||||
rhs_result.node()->debug_print(1);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
const auto rhs_value = static_cast<AML::Integer*>(rhs_conv.ptr())->value;
|
||||
|
||||
if (context.aml_data.size() < 1)
|
||||
{
|
||||
AML_ERROR("BinaryOP {2H} missing target", static_cast<uint8_t>(opcode));
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> target_node;
|
||||
if (context.aml_data[0] == 0x00)
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
else
|
||||
{
|
||||
auto target_result = AML::parse_object(context);
|
||||
if (!target_result.success())
|
||||
return ParseResult::Failure;
|
||||
target_node = target_result.node();
|
||||
if (!target_node)
|
||||
{
|
||||
AML_ERROR("BinaryOP {2H} target invalid", static_cast<uint8_t>(opcode));
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t (*func)(uint64_t, uint64_t) = nullptr;
|
||||
switch (opcode)
|
||||
{
|
||||
case AML::Byte::AddOp: func = [](uint64_t a, uint64_t b) { return a + b; }; break;
|
||||
case AML::Byte::AndOp: func = [](uint64_t a, uint64_t b) { return a & b; }; break;
|
||||
case AML::Byte::ModOp: func = [](uint64_t a, uint64_t b) { return a % b; }; break;
|
||||
case AML::Byte::MultiplyOp: func = [](uint64_t a, uint64_t b) { return a * b; }; break;
|
||||
case AML::Byte::NandOp: func = [](uint64_t a, uint64_t b) { return ~(a & b); }; break;
|
||||
case AML::Byte::NorOp: func = [](uint64_t a, uint64_t b) { return ~(a | b); }; break;
|
||||
case AML::Byte::OrOp: func = [](uint64_t a, uint64_t b) { return a | b; }; break;
|
||||
case AML::Byte::ShiftLeftOp: func = [](uint64_t a, uint64_t b) { return a << b; }; break;
|
||||
case AML::Byte::ShiftRightOp: func = [](uint64_t a, uint64_t b) { return a >> b; }; break;
|
||||
case AML::Byte::SubtractOp: func = [](uint64_t a, uint64_t b) { return a - b; }; break;
|
||||
case AML::Byte::XorOp: func = [](uint64_t a, uint64_t b) { return a ^ b; }; break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
auto result_node = MUST(BAN::RefPtr<AML::Integer>::create(func(lhs_value, rhs_value)));
|
||||
if (target_node && !target_node->store(result_node))
|
||||
{
|
||||
AML_ERROR("BinaryOp {2H} failed to store result", static_cast<uint8_t>(opcode));
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
return ParseResult(result_node);
|
||||
}
|
||||
|
||||
static ParseResult parse_logical_binary_op(ParseContext& context)
|
||||
{
|
||||
auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
uint8_t mask;
|
||||
switch (opcode)
|
||||
{
|
||||
case AML::Byte::LAndOp:
|
||||
case AML::Byte::LOrOp:
|
||||
mask = AML::Node::ConvInteger;
|
||||
break;
|
||||
case AML::Byte::LEqualOp:
|
||||
case AML::Byte::LGreaterOp:
|
||||
case AML::Byte::LLessOp:
|
||||
mask = AML::Node::ConvInteger | AML::Node::ConvString | AML::Node::ConvBuffer;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
auto lhs_result = AML::parse_object(context);
|
||||
if (!lhs_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto lhs_node = lhs_result.node() ? lhs_result.node()->convert(mask) : BAN::RefPtr<AML::Node>();
|
||||
if (!lhs_node)
|
||||
{
|
||||
AML_TODO("Logical BinaryOP {2H} LHS evaluated to nothing", static_cast<uint8_t>(opcode));
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto rhs_result = AML::parse_object(context);
|
||||
if (!rhs_result.success())
|
||||
return ParseResult::Failure;
|
||||
|
||||
BAN::Optional<bool> result = false;
|
||||
switch (lhs_node->type)
|
||||
{
|
||||
case AML::Node::Type::Integer:
|
||||
result = static_cast<AML::Integer*>(lhs_node.ptr())->logical_compare(rhs_result.node(), opcode);
|
||||
break;
|
||||
case AML::Node::Type::Buffer:
|
||||
result = static_cast<AML::Buffer*>(lhs_node.ptr())->logical_compare(rhs_result.node(), opcode);
|
||||
break;
|
||||
case AML::Node::Type::String:
|
||||
result = static_cast<AML::String*>(lhs_node.ptr())->logical_compare(rhs_result.node(), opcode);
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
if (!result.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
return ParseResult(result.value() ? AML::Integer::Constants::Ones : AML::Integer::Constants::Zero);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,199 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/NamedObject.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Pkg.h>
|
||||
#include <kernel/ACPI/AML/Region.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct FieldRules
|
||||
{
|
||||
enum class AccessType
|
||||
{
|
||||
Any = 0,
|
||||
Byte = 1,
|
||||
Word = 2,
|
||||
DWord = 3,
|
||||
QWord = 4,
|
||||
Buffer = 5,
|
||||
};
|
||||
AccessType access_type;
|
||||
|
||||
enum class LockRule
|
||||
{
|
||||
NoLock = 0,
|
||||
Lock = 1,
|
||||
};
|
||||
LockRule lock_rule;
|
||||
|
||||
enum class UpdateRule
|
||||
{
|
||||
Preserve = 0,
|
||||
WriteAsOnes = 1,
|
||||
WriteAsZeros = 2,
|
||||
};
|
||||
UpdateRule update_rule;
|
||||
|
||||
enum class AccessAttrib
|
||||
{
|
||||
Normal = 0,
|
||||
Bytes = 1,
|
||||
RawBytes = 2,
|
||||
RawProcessBytes = 3,
|
||||
};
|
||||
AccessAttrib access_attrib = AccessAttrib::Normal;
|
||||
uint8_t access_length = 0;
|
||||
};
|
||||
|
||||
struct FieldElement final : public AML::NamedObject
|
||||
{
|
||||
uint64_t bit_offset;
|
||||
uint64_t bit_count;
|
||||
|
||||
FieldRules access_rules;
|
||||
|
||||
BAN::RefPtr<OpRegion> op_region;
|
||||
|
||||
FieldElement(NameSeg name, uint64_t bit_offset, uint64_t bit_count, FieldRules access_rules)
|
||||
: NamedObject(Node::Type::FieldElement, name)
|
||||
, bit_offset(bit_offset)
|
||||
, bit_count(bit_count)
|
||||
, access_rules(access_rules)
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
|
||||
{
|
||||
if (mask & AML::Node::ConvInteger)
|
||||
return as_integer();
|
||||
if (mask & AML::Node::ConvBuffer)
|
||||
{
|
||||
AML_TODO("Convert BankFieldElement to Buffer");
|
||||
return {};
|
||||
}
|
||||
if (mask & AML::Node::ConvString)
|
||||
{
|
||||
AML_TODO("Convert BankFieldElement to String");
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> store(BAN::RefPtr<Node> source) override;
|
||||
|
||||
void debug_print(int indent) const override;
|
||||
|
||||
private:
|
||||
BAN::RefPtr<AML::Integer> as_integer();
|
||||
|
||||
BAN::Optional<uint64_t> evaluate_internal();
|
||||
bool store_internal(uint64_t value);
|
||||
|
||||
friend struct IndexFieldElement;
|
||||
friend struct BankFieldElement;
|
||||
};
|
||||
|
||||
struct Field
|
||||
{
|
||||
static ParseResult parse(ParseContext& context);
|
||||
};
|
||||
|
||||
struct IndexFieldElement final : public AML::NamedObject
|
||||
{
|
||||
uint64_t bit_offset;
|
||||
uint64_t bit_count;
|
||||
|
||||
FieldRules access_rules;
|
||||
|
||||
BAN::RefPtr<FieldElement> index_element;
|
||||
BAN::RefPtr<FieldElement> data_element;
|
||||
|
||||
IndexFieldElement(NameSeg name, uint64_t bit_offset, uint64_t bit_count, FieldRules access_rules)
|
||||
: NamedObject(Node::Type::IndexFieldElement, name)
|
||||
, bit_offset(bit_offset)
|
||||
, bit_count(bit_count)
|
||||
, access_rules(access_rules)
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
|
||||
{
|
||||
if (mask & AML::Node::ConvInteger)
|
||||
if (auto node = as_integer())
|
||||
return node;
|
||||
if (mask & AML::Node::ConvBuffer)
|
||||
{
|
||||
AML_TODO("convert BankFieldElement to Buffer");
|
||||
return {};
|
||||
}
|
||||
if (mask & AML::Node::ConvString)
|
||||
{
|
||||
AML_TODO("convert BankFieldElement to String");
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> store(BAN::RefPtr<Node> source) override;
|
||||
|
||||
void debug_print(int indent) const override;
|
||||
|
||||
private:
|
||||
BAN::RefPtr<AML::Integer> as_integer();
|
||||
};
|
||||
|
||||
struct IndexField
|
||||
{
|
||||
static ParseResult parse(ParseContext& context);
|
||||
};
|
||||
|
||||
struct BankFieldElement final : public AML::NamedObject
|
||||
{
|
||||
uint64_t bit_offset;
|
||||
uint64_t bit_count;
|
||||
|
||||
FieldRules access_rules;
|
||||
|
||||
BAN::RefPtr<OpRegion> op_region;
|
||||
BAN::RefPtr<FieldElement> bank_selector;
|
||||
uint64_t bank_value;
|
||||
|
||||
BankFieldElement(NameSeg name, uint64_t bit_offset, uint64_t bit_count, FieldRules access_rules)
|
||||
: NamedObject(Node::Type::BankFieldElement, name)
|
||||
, bit_offset(bit_offset)
|
||||
, bit_count(bit_count)
|
||||
, access_rules(access_rules)
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
|
||||
{
|
||||
if (mask & AML::Node::ConvInteger)
|
||||
if (auto node = as_integer())
|
||||
return node;
|
||||
if (mask & AML::Node::ConvBuffer)
|
||||
{
|
||||
AML_TODO("convert BankFieldElement to Buffer");
|
||||
return {};
|
||||
}
|
||||
if (mask & AML::Node::ConvString)
|
||||
{
|
||||
AML_TODO("convert BankFieldElement to String");
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> store(BAN::RefPtr<Node> source) override;
|
||||
|
||||
void debug_print(int indent) const override;
|
||||
|
||||
private:
|
||||
BAN::RefPtr<AML::Integer> as_integer();
|
||||
};
|
||||
|
||||
struct BankField
|
||||
{
|
||||
static ParseResult parse(ParseContext& context);
|
||||
};
|
||||
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/HashMap.h>
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Pkg.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct IfElse
|
||||
{
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::IfOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto if_pkg = AML::parse_pkg(context.aml_data);
|
||||
if (!if_pkg.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto outer_aml_data = context.aml_data;
|
||||
context.aml_data = if_pkg.value();
|
||||
|
||||
auto predicate_result = AML::parse_object(context);
|
||||
if (!predicate_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto predicate_node = predicate_result.node() ? predicate_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!predicate_node)
|
||||
{
|
||||
AML_ERROR("If predicate is not an integer");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
const auto predicate_value = static_cast<AML::Integer*>(predicate_node.ptr())->value;
|
||||
|
||||
// Else
|
||||
BAN::ConstByteSpan else_pkg;
|
||||
if (outer_aml_data.size() >= 1 && static_cast<AML::Byte>(outer_aml_data[0]) == Byte::ElseOp)
|
||||
{
|
||||
outer_aml_data = outer_aml_data.slice(1);
|
||||
auto else_pkg_result = AML::parse_pkg(outer_aml_data);
|
||||
if (!else_pkg_result.has_value())
|
||||
return ParseResult::Failure;
|
||||
else_pkg = else_pkg_result.value();
|
||||
}
|
||||
if (predicate_value == 0)
|
||||
context.aml_data = else_pkg;
|
||||
|
||||
while (context.aml_data.size() > 0)
|
||||
{
|
||||
auto object_result = AML::parse_object(context);
|
||||
if (object_result.returned() || object_result.breaked() || object_result.continued())
|
||||
return object_result;
|
||||
if (!object_result.success())
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
context.aml_data = outer_aml_data;
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Buffer.h>
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
#include <kernel/ACPI/AML/Package.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Reference.h>
|
||||
#include <kernel/ACPI/AML/Register.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Index
|
||||
{
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::IndexOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto source_result = AML::parse_object(context);
|
||||
if (!source_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto source = source_result.node() ? source_result.node()->to_underlying() : BAN::RefPtr<AML::Node>();
|
||||
if (source && source->type != AML::Node::Type::Package)
|
||||
source = source->convert(AML::Node::ConvBuffer | AML::Node::ConvInteger | AML::Node::ConvString);
|
||||
if (!source)
|
||||
{
|
||||
AML_ERROR("IndexOp source could not be converted");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto index_result = AML::parse_object(context);
|
||||
if (!index_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto index_node = index_result.node()
|
||||
? index_result.node()->convert(AML::Node::ConvInteger)
|
||||
: BAN::RefPtr<AML::Node>();
|
||||
if (!index_node)
|
||||
{
|
||||
AML_ERROR("IndexOp index is not an integer");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
const auto index = static_cast<AML::Integer*>(index_node.ptr())->value;
|
||||
|
||||
BAN::RefPtr<AML::Reference> result;
|
||||
switch (source->type)
|
||||
{
|
||||
case AML::Node::Type::Buffer:
|
||||
{
|
||||
auto buffer = BAN::RefPtr<AML::Buffer>(static_cast<AML::Buffer*>(source.ptr()));
|
||||
if (index >= buffer->buffer.size())
|
||||
{
|
||||
AML_ERROR("IndexOp index is out of buffer bounds");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
auto buffer_field = MUST(BAN::RefPtr<BufferField>::create(NameSeg(""_sv), buffer, index * 8, 8));
|
||||
result = MUST(BAN::RefPtr<AML::Reference>::create(buffer_field));
|
||||
break;
|
||||
}
|
||||
case AML::Node::Type::Package:
|
||||
{
|
||||
auto package = static_cast<AML::Package*>(source.ptr());
|
||||
if (index >= package->elements.size())
|
||||
{
|
||||
AML_ERROR("IndexOp index is out of package bounds");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
auto package_element = package->elements[index];
|
||||
if (!package_element)
|
||||
{
|
||||
AML_ERROR("IndexOp target is null package element");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
result = MUST(BAN::RefPtr<AML::Reference>::create(package_element));
|
||||
break;
|
||||
}
|
||||
case AML::Node::Type::String:
|
||||
{
|
||||
auto string = BAN::RefPtr<AML::String>(static_cast<AML::String*>(source.ptr()));
|
||||
if (index >= string->string.size())
|
||||
{
|
||||
AML_ERROR("IndexOp index is out of string bounds");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
auto buffer_field = MUST(BAN::RefPtr<BufferField>::create(NameSeg(""_sv), string, index * 8, 8));
|
||||
result = MUST(BAN::RefPtr<AML::Reference>::create(buffer_field));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
AML_ERROR("IndexOp source is not a Buffer, Package, or String");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
AML_DEBUG_PRINT("Index {}, ", index);
|
||||
source->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
if (context.aml_data.size() < 1)
|
||||
return ParseResult::Failure;
|
||||
|
||||
if (context.aml_data[0] == 0x00)
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
else
|
||||
{
|
||||
auto destination_result = AML::parse_object(context);
|
||||
if (!destination_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto destination = destination_result.node();
|
||||
if (!destination)
|
||||
{
|
||||
AML_ERROR("IndexOp failed to resolve destination");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
if (!destination->store(result))
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
return ParseResult(result);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Optional.h>
|
||||
#include <BAN/String.h>
|
||||
#include <BAN/Vector.h>
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
#include <kernel/ACPI/AML/Utils.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Integer final : public AML::Node
|
||||
{
|
||||
struct Constants
|
||||
{
|
||||
// Initialized in Namespace::create_root_namespace
|
||||
static BAN::RefPtr<Integer> Zero;
|
||||
static BAN::RefPtr<Integer> One;
|
||||
static BAN::RefPtr<Integer> Ones;
|
||||
};
|
||||
|
||||
uint64_t value;
|
||||
const bool constant;
|
||||
|
||||
Integer(uint64_t value, bool constant = false);
|
||||
|
||||
BAN::Optional<bool> logical_compare(BAN::RefPtr<AML::Node> node, AML::Byte binaryop);
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t mask) override;
|
||||
BAN::RefPtr<Node> copy() override;
|
||||
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> store_node) override;
|
||||
|
||||
static ParseResult parse(BAN::ConstByteSpan& aml_data);
|
||||
|
||||
void debug_print(int indent) const override;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Function.h>
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/Namespace.h>
|
||||
#include <kernel/ACPI/AML/Namespace.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Pkg.h>
|
||||
#include <kernel/ACPI/AML/Register.h>
|
||||
#include <kernel/ACPI/AML/Scope.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Method final : public AML::Scope
|
||||
{
|
||||
Kernel::Mutex mutex;
|
||||
uint8_t arg_count;
|
||||
bool serialized;
|
||||
uint8_t sync_level;
|
||||
|
||||
BAN::Function<BAN::RefPtr<AML::Node>(ParseContext&)> override_function;
|
||||
BAN::ConstByteSpan term_list;
|
||||
|
||||
Method(AML::NameSeg name, uint8_t arg_count, bool serialized, uint8_t sync_level)
|
||||
: AML::Scope(Node::Type::Method, name)
|
||||
, arg_count(arg_count)
|
||||
, serialized(serialized)
|
||||
, sync_level(sync_level)
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
|
||||
|
||||
static ParseResult parse(AML::ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::MethodOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto method_pkg = AML::parse_pkg(context.aml_data);
|
||||
if (!method_pkg.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto name_string = AML::NameString::parse(method_pkg.value());
|
||||
if (!name_string.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
if (method_pkg->size() < 1)
|
||||
return ParseResult::Failure;
|
||||
auto method_flags = method_pkg.value()[0];
|
||||
method_pkg = method_pkg.value().slice(1);
|
||||
|
||||
auto method = MUST(BAN::RefPtr<Method>::create(
|
||||
name_string.value().path.back(),
|
||||
method_flags & 0x07,
|
||||
(method_flags >> 3) & 0x01,
|
||||
method_flags >> 4
|
||||
));
|
||||
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), method))
|
||||
return ParseResult::Success;
|
||||
method->term_list = method_pkg.value();
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
method->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
|
||||
BAN::Optional<BAN::RefPtr<AML::Node>> invoke(
|
||||
BAN::RefPtr<AML::Node> arg0 = {},
|
||||
BAN::RefPtr<AML::Node> arg1 = {},
|
||||
BAN::RefPtr<AML::Node> arg2 = {},
|
||||
BAN::RefPtr<AML::Node> arg3 = {},
|
||||
BAN::RefPtr<AML::Node> arg4 = {},
|
||||
BAN::RefPtr<AML::Node> arg5 = {},
|
||||
BAN::RefPtr<AML::Node> arg6 = {}
|
||||
)
|
||||
{
|
||||
BAN::Vector<uint8_t> sync_stack;
|
||||
return invoke_with_sync_stack(sync_stack, arg0, arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
|
||||
BAN::Optional<BAN::RefPtr<AML::Node>> invoke_with_sync_stack(
|
||||
BAN::Vector<uint8_t>& current_sync_stack,
|
||||
BAN::RefPtr<AML::Node> arg0 = {},
|
||||
BAN::RefPtr<AML::Node> arg1 = {},
|
||||
BAN::RefPtr<AML::Node> arg2 = {},
|
||||
BAN::RefPtr<AML::Node> arg3 = {},
|
||||
BAN::RefPtr<AML::Node> arg4 = {},
|
||||
BAN::RefPtr<AML::Node> arg5 = {},
|
||||
BAN::RefPtr<AML::Node> arg6 = {}
|
||||
)
|
||||
{
|
||||
if (serialized && !current_sync_stack.empty() && sync_level < current_sync_stack.back())
|
||||
{
|
||||
AML_ERROR("Trying to evaluate method {} with lower sync level than current sync level", scope);
|
||||
return {};
|
||||
}
|
||||
|
||||
ParseContext context;
|
||||
context.aml_data = term_list;
|
||||
context.scope = scope;
|
||||
context.method_args[0] = MUST(BAN::RefPtr<AML::Register>::create(arg0));
|
||||
context.method_args[1] = MUST(BAN::RefPtr<AML::Register>::create(arg1));
|
||||
context.method_args[2] = MUST(BAN::RefPtr<AML::Register>::create(arg2));
|
||||
context.method_args[3] = MUST(BAN::RefPtr<AML::Register>::create(arg3));
|
||||
context.method_args[4] = MUST(BAN::RefPtr<AML::Register>::create(arg4));
|
||||
context.method_args[5] = MUST(BAN::RefPtr<AML::Register>::create(arg5));
|
||||
context.method_args[6] = MUST(BAN::RefPtr<AML::Register>::create(arg6));
|
||||
context.sync_stack = BAN::move(current_sync_stack);
|
||||
for (auto& local : context.method_locals)
|
||||
local = MUST(BAN::RefPtr<AML::Register>::create());
|
||||
|
||||
if (serialized)
|
||||
{
|
||||
mutex.lock();
|
||||
MUST(context.sync_stack.push_back(sync_level));
|
||||
}
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
AML_DEBUG_PRINTLN("Evaluating {}", scope);
|
||||
#endif
|
||||
|
||||
BAN::Optional<BAN::RefPtr<AML::Node>> return_value = BAN::RefPtr<AML::Node>();
|
||||
|
||||
if (override_function)
|
||||
return_value = override_function(context);
|
||||
else
|
||||
{
|
||||
while (context.aml_data.size() > 0)
|
||||
{
|
||||
auto parse_result = AML::parse_object(context);
|
||||
if (parse_result.returned())
|
||||
{
|
||||
return_value = parse_result.node();
|
||||
break;
|
||||
}
|
||||
if (!parse_result.success())
|
||||
{
|
||||
AML_ERROR("Method {} evaluate failed", scope);
|
||||
return_value = {};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (return_value.has_value() && return_value.value() && return_value.value()->type == AML::Node::Type::Register)
|
||||
return_value.value() = static_cast<AML::Register*>(return_value.value().ptr())->value;
|
||||
|
||||
while (!context.created_objects.empty())
|
||||
{
|
||||
Namespace::root_namespace()->remove_named_object(context.created_objects.back());
|
||||
context.created_objects.pop_back();
|
||||
}
|
||||
|
||||
if (serialized)
|
||||
{
|
||||
context.sync_stack.pop_back();
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
current_sync_stack = BAN::move(context.sync_stack);
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
virtual void debug_print(int indent) const override
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("Method ");
|
||||
name.debug_print();
|
||||
AML_DEBUG_PRINTLN("({} args, {}Serialized, 0x{H}) {", arg_count, serialized ? "" : "Not", sync_level);
|
||||
AML_DEBUG_PRINT_INDENT(indent + 1);
|
||||
AML_DEBUG_PRINTLN("TermList: {} bytes", term_list.size());
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("}");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/NamedObject.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/Lock/Mutex.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Mutex final : public AML::NamedObject
|
||||
{
|
||||
Kernel::Mutex mutex;
|
||||
uint8_t sync_level;
|
||||
bool global;
|
||||
|
||||
Mutex(NameSeg name, uint8_t sync_level, bool global = false)
|
||||
: NamedObject(Node::Type::Mutex, name)
|
||||
, sync_level(sync_level)
|
||||
, global(global)
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
|
||||
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
|
||||
switch (static_cast<AML::ExtOp>(context.aml_data[1]))
|
||||
{
|
||||
case AML::ExtOp::MutexOp:
|
||||
return parse_mutex(context);
|
||||
case AML::ExtOp::AcquireOp:
|
||||
return parse_acquire(context);
|
||||
case AML::ExtOp::ReleaseOp:
|
||||
return parse_release(context);
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void debug_print(int indent) const override
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("Mutex ");
|
||||
name.debug_print();
|
||||
AML_DEBUG_PRINT(" (SyncLevel: {})", sync_level);
|
||||
}
|
||||
|
||||
private:
|
||||
static ParseResult parse_mutex(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::MutexOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto name = NameString::parse(context.aml_data);
|
||||
if (!name.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
if (context.aml_data.size() < 1)
|
||||
return ParseResult::Failure;
|
||||
auto sync_level = context.aml_data[0];
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
if (sync_level & 0xF0)
|
||||
{
|
||||
AML_ERROR("Invalid sync level {}", sync_level);
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto mutex = MUST(BAN::RefPtr<Mutex>::create(name->path.back(), sync_level));
|
||||
if (!Namespace::root_namespace()->add_named_object(context, name.value(), mutex))
|
||||
return ParseResult::Success;
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
mutex->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
|
||||
static ParseResult parse_acquire(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::AcquireOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto mutex_result = AML::parse_object(context);
|
||||
if (!mutex_result.success() || !mutex_result.node() || mutex_result.node()->type != AML::Node::Type::Mutex)
|
||||
{
|
||||
AML_ERROR("Acquire does not name a valid mutex");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto* mutex = static_cast<AML::Mutex*>(mutex_result.node().ptr());
|
||||
if (mutex->sync_level < context.sync_level())
|
||||
{
|
||||
AML_ERROR("Trying to acquire mutex with lower sync level than current sync level");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
if (context.aml_data.size() < 2)
|
||||
{
|
||||
AML_ERROR("Missing timeout value");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
uint16_t timeout = context.aml_data[0] | (context.aml_data[1] << 8);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
if (timeout >= 0xFFFF)
|
||||
mutex->mutex.lock();
|
||||
else
|
||||
{
|
||||
// FIXME: This is a very inefficient way to wait for a mutex
|
||||
uint64_t wake_time = SystemTimer::get().ms_since_boot() + timeout;
|
||||
while (!mutex->mutex.try_lock())
|
||||
{
|
||||
if (SystemTimer::get().ms_since_boot() >= wake_time)
|
||||
return ParseResult(Integer::Constants::Ones);
|
||||
Processor::yield();
|
||||
}
|
||||
}
|
||||
|
||||
MUST(context.sync_stack.push_back(mutex->sync_level));
|
||||
return ParseResult(Integer::Constants::Zero);
|
||||
}
|
||||
|
||||
static ParseResult parse_release(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::ReleaseOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto mutex_result = AML::parse_object(context);
|
||||
if (!mutex_result.success() || !mutex_result.node() || mutex_result.node()->type != AML::Node::Type::Mutex)
|
||||
{
|
||||
AML_ERROR("Release does not name a valid mutex");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
if (context.sync_stack.empty())
|
||||
{
|
||||
AML_ERROR("Trying to release mutex without having acquired it");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto* mutex = static_cast<AML::Mutex*>(mutex_result.node().ptr());
|
||||
if (mutex->sync_level != context.sync_level())
|
||||
{
|
||||
AML_ERROR("Trying to release mutex with different sync level than current sync level");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
mutex->mutex.unlock();
|
||||
context.sync_stack.pop_back();
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/HashMap.h>
|
||||
#include <kernel/ACPI/AML/Names.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct NamedObject : public AML::Node
|
||||
{
|
||||
BAN::RefPtr<NamedObject> parent;
|
||||
NameSeg name;
|
||||
|
||||
NamedObject(Node::Type type, NameSeg name) : Node(type), name(name) {}
|
||||
};
|
||||
|
||||
struct Name final : public AML::NamedObject
|
||||
{
|
||||
BAN::RefPtr<AML::Node> object;
|
||||
|
||||
Name(NameSeg name, BAN::RefPtr<AML::Node> object)
|
||||
: NamedObject(Node::Type::Name, name), object(BAN::move(object))
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> to_underlying() override { return object; }
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
|
||||
{
|
||||
ASSERT(object);
|
||||
return object->convert(mask);
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> node) override
|
||||
{
|
||||
ASSERT(object);
|
||||
ASSERT(object->type != AML::Node::Type::Reference);
|
||||
return object->store(node);
|
||||
}
|
||||
|
||||
static ParseResult parse(ParseContext& context);
|
||||
virtual void debug_print(int indent) const override;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,244 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Optional.h>
|
||||
#include <BAN/String.h>
|
||||
#include <BAN/Vector.h>
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
#include <kernel/ACPI/AML/Utils.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct NameSeg
|
||||
{
|
||||
union {
|
||||
char chars[4];
|
||||
uint32_t u32;
|
||||
};
|
||||
|
||||
NameSeg() = default;
|
||||
|
||||
NameSeg(BAN::StringView name)
|
||||
{
|
||||
ASSERT(name.size() <= 4);
|
||||
for (size_t i = 0; i < name.size(); i++)
|
||||
chars[i] = static_cast<char>(name[i]);
|
||||
for (size_t i = name.size(); i < 4; i++)
|
||||
chars[i] = '_';
|
||||
}
|
||||
|
||||
NameSeg(BAN::ConstByteSpan& aml_data)
|
||||
{
|
||||
ASSERT(aml_data.size() >= 4);
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
chars[i] = static_cast<char>(aml_data[i]);
|
||||
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<NameSeg> parse(BAN::ConstByteSpan& aml_data)
|
||||
{
|
||||
if (aml_data.size() < 4)
|
||||
return {};
|
||||
|
||||
if (!is_lead_name_char(aml_data[0])
|
||||
|| !is_name_char(aml_data[1])
|
||||
|| !is_name_char(aml_data[2])
|
||||
|| !is_name_char(aml_data[3]))
|
||||
return {};
|
||||
|
||||
return NameSeg(aml_data);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const NameSeg& other) const
|
||||
{
|
||||
return u32 == other.u32;
|
||||
}
|
||||
|
||||
void debug_print() const
|
||||
{
|
||||
size_t len = 4;
|
||||
while (len > 0 && chars[len - 1] == '_')
|
||||
len--;
|
||||
for (size_t i = 0; i < len; i++)
|
||||
AML_DEBUG_PUTC(chars[i]);
|
||||
}
|
||||
};
|
||||
|
||||
struct NameString
|
||||
{
|
||||
BAN::String prefix;
|
||||
BAN::Vector<NameSeg> path;
|
||||
|
||||
NameString() = default;
|
||||
NameString(BAN::StringView str)
|
||||
{
|
||||
if (!str.empty() && str.front() == '\\')
|
||||
{
|
||||
MUST(prefix.push_back('\\'));
|
||||
str = str.substring(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (str.size() > 0 && str.front() == '^')
|
||||
{
|
||||
MUST(prefix.push_back('^'));
|
||||
str = str.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
while (!str.empty())
|
||||
{
|
||||
ASSERT(str[0] != '.');
|
||||
size_t len = 1;
|
||||
while (len < str.size() && str[len] != '.')
|
||||
len++;
|
||||
ASSERT(len <= 4);
|
||||
|
||||
MUST(path.push_back(NameSeg(str.substring(0, len))));
|
||||
str = str.substring(len);
|
||||
|
||||
if (!str.empty())
|
||||
{
|
||||
ASSERT(str[0] == '.');
|
||||
str = str.substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool can_parse(BAN::ConstByteSpan aml_data)
|
||||
{
|
||||
if (aml_data.size() == 0)
|
||||
return false;
|
||||
switch (static_cast<AML::Byte>(aml_data[0]))
|
||||
{
|
||||
case AML::Byte::RootChar:
|
||||
case AML::Byte::ParentPrefixChar:
|
||||
case AML::Byte::NullName:
|
||||
case AML::Byte::DualNamePrefix:
|
||||
case AML::Byte::MultiNamePrefix:
|
||||
return true;
|
||||
default:
|
||||
return is_lead_name_char(aml_data[0]);
|
||||
}
|
||||
}
|
||||
|
||||
static BAN::Optional<NameString> parse(BAN::ConstByteSpan& aml_data)
|
||||
{
|
||||
if (aml_data.size() == 0)
|
||||
return {};
|
||||
|
||||
NameString result;
|
||||
|
||||
if (static_cast<AML::Byte>(aml_data[0]) == AML::Byte::RootChar)
|
||||
{
|
||||
MUST(result.prefix.push_back('\\'));
|
||||
aml_data = aml_data.slice(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (aml_data.size() > 0 && static_cast<AML::Byte>(aml_data[0]) == AML::Byte::ParentPrefixChar)
|
||||
{
|
||||
MUST(result.prefix.push_back(aml_data[0]));
|
||||
aml_data = aml_data.slice(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (aml_data.size() == 0)
|
||||
return {};
|
||||
|
||||
size_t name_count = 1;
|
||||
switch (static_cast<AML::Byte>(aml_data[0]))
|
||||
{
|
||||
case AML::Byte::NullName:
|
||||
name_count = 0;
|
||||
aml_data = aml_data.slice(1);
|
||||
break;
|
||||
case AML::Byte::DualNamePrefix:
|
||||
name_count = 2;
|
||||
aml_data = aml_data.slice(1);
|
||||
break;
|
||||
case AML::Byte::MultiNamePrefix:
|
||||
if (aml_data.size() < 2)
|
||||
return {};
|
||||
name_count = aml_data[1];
|
||||
aml_data = aml_data.slice(2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < name_count; i++)
|
||||
{
|
||||
auto name_seg = NameSeg::parse(aml_data);
|
||||
if (!name_seg.has_value())
|
||||
return {};
|
||||
MUST(result.path.push_back(name_seg.release_value()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void debug_print() const
|
||||
{
|
||||
for (size_t i = 0; i < prefix.size(); i++)
|
||||
AML_DEBUG_PUTC(prefix[i]);
|
||||
if (!path.empty())
|
||||
path.front().debug_print();
|
||||
for (size_t i = 1; i < path.size(); i++)
|
||||
{
|
||||
AML_DEBUG_PUTC('.');
|
||||
path[i].debug_print();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<>
|
||||
struct hash<Kernel::ACPI::AML::NameSeg>
|
||||
{
|
||||
constexpr hash_t operator()(Kernel::ACPI::AML::NameSeg name) const
|
||||
{
|
||||
return hash<uint32_t>()(name.u32);
|
||||
}
|
||||
};
|
||||
|
||||
namespace Formatter
|
||||
{
|
||||
template<typename F>
|
||||
void print_argument(F putc, const Kernel::ACPI::AML::NameSeg& name_seg, const ValueFormat&)
|
||||
{
|
||||
size_t len = 4;
|
||||
while (len > 0 && name_seg.chars[len - 1] == '_')
|
||||
len--;
|
||||
for (size_t i = 0; i < len; i++)
|
||||
putc(name_seg.chars[i]);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void print_argument(F putc, const Kernel::ACPI::AML::NameString& name_string, const ValueFormat&)
|
||||
{
|
||||
print_argument(putc, name_string.prefix, {});
|
||||
if (!name_string.path.empty())
|
||||
print_argument(putc, name_string.path.front(), {});
|
||||
for (size_t i = 1; i < name_string.path.size(); i++)
|
||||
{
|
||||
putc('.');
|
||||
print_argument(putc, name_string.path[i], {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,66 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Bitcast.h>
|
||||
#include <BAN/ByteSpan.h>
|
||||
#include <BAN/Function.h>
|
||||
#include <BAN/HashMap.h>
|
||||
#include <kernel/ACPI/AML/Scope.h>
|
||||
#include <kernel/ACPI/Headers.h>
|
||||
#include <kernel/Lock/Mutex.h>
|
||||
#include <BAN/Iteration.h>
|
||||
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Namespace final : public AML::Scope
|
||||
struct Namespace
|
||||
{
|
||||
static BAN::RefPtr<AML::Namespace> create_root_namespace();
|
||||
static BAN::RefPtr<AML::Namespace> root_namespace();
|
||||
static BAN::RefPtr<AML::Node> debug_node;
|
||||
BAN_NON_COPYABLE(Namespace);
|
||||
BAN_NON_MOVABLE(Namespace);
|
||||
public:
|
||||
Namespace() = default;
|
||||
~Namespace();
|
||||
|
||||
template<typename F>
|
||||
static void for_each_child(const AML::NameString& scope, const F& callback)
|
||||
static BAN::ErrorOr<void> initialize_root_namespace();
|
||||
static Namespace& root_namespace();
|
||||
|
||||
// this has to be called after initalizing ACPI namespace
|
||||
BAN::ErrorOr<void> initalize_op_regions();
|
||||
|
||||
BAN::ErrorOr<void> parse(BAN::ConstByteSpan);
|
||||
|
||||
BAN::ErrorOr<Node> evaluate(BAN::StringView);
|
||||
|
||||
// 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<void> remove_named_object(const Scope& absolute_path);
|
||||
|
||||
BAN::ErrorOr<void> initialize_devices();
|
||||
|
||||
// node is nullptr if it is not found
|
||||
struct FindResult
|
||||
{
|
||||
auto canonical_path = root_namespace()->resolve_path(scope, {}, FindMode::ForceAbsolute);
|
||||
ASSERT(canonical_path.has_value());
|
||||
|
||||
for (auto& [path, child] : root_namespace()->m_objects)
|
||||
{
|
||||
if (path.size() < canonical_path->size() + 1)
|
||||
continue;
|
||||
if (canonical_path->size() != 1 && 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) {}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
|
||||
|
||||
bool parse(const SDTHeader& header);
|
||||
|
||||
void debug_print(int indent) const override;
|
||||
|
||||
enum class FindMode
|
||||
{
|
||||
Normal,
|
||||
ForceAbsolute,
|
||||
Scope path;
|
||||
Reference* node { nullptr };
|
||||
};
|
||||
BAN::Optional<BAN::String> resolve_path(const AML::NameString& relative_base, const AML::NameString& relative_path, FindMode mode, bool check_existence = true) const;
|
||||
BAN::ErrorOr<FindResult> find_named_object(const Scope& scope, const NameString& name_string, bool force_absolute = false);
|
||||
|
||||
// Find an object in the namespace. Returns nullptr if the object is not found.
|
||||
BAN::RefPtr<NamedObject> find_object(const AML::NameString& relative_base, const AML::NameString& relative_path, FindMode mode);
|
||||
|
||||
// Add an object to the namespace. Returns false if the parent object could not be added.
|
||||
bool add_named_object(ParseContext&, const AML::NameString& object_path, BAN::RefPtr<NamedObject> object);
|
||||
|
||||
// Remove an object from the namespace. Returns false if the object could not be removed.
|
||||
bool remove_named_object(const AML::NameString& absolute_path);
|
||||
BAN::ErrorOr<void> for_each_child(const Scope&, const BAN::Function<BAN::Iteration(BAN::StringView, Reference*)>&);
|
||||
BAN::ErrorOr<void> for_each_child(const Scope&, const BAN::Function<BAN::Iteration(const Scope&, Reference*)>&);
|
||||
|
||||
private:
|
||||
BAN::HashMap<BAN::String, BAN::RefPtr<NamedObject>> m_objects;
|
||||
mutable Mutex m_object_mutex;
|
||||
BAN::ErrorOr<Scope> resolve_path(const Scope& scope, const NameString& name_string);
|
||||
|
||||
BAN::ErrorOr<void> initialize_scope(const Scope& scope);
|
||||
|
||||
BAN::ErrorOr<void> opregion_call_reg(const Scope& scope, const Node& opregion);
|
||||
|
||||
private:
|
||||
bool m_has_parsed_namespace { false };
|
||||
BAN::HashMap<Scope, Reference*> m_named_objects;
|
||||
BAN::HashMap<Scope, uint32_t> m_called_reg_bitmaps;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,115 +1,419 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Array.h>
|
||||
#include <BAN/ByteSpan.h>
|
||||
#include <BAN/LinkedList.h>
|
||||
#include <BAN/NoCopyMove.h>
|
||||
#include <BAN/Optional.h>
|
||||
#include <BAN/RefPtr.h>
|
||||
#include <BAN/StringView.h>
|
||||
#include <BAN/Vector.h>
|
||||
#include <kernel/ACPI/AML/Utils.h>
|
||||
|
||||
#include <kernel/ACPI/AML/Scope.h>
|
||||
#include <kernel/ACPI/Headers.h>
|
||||
#include <kernel/Lock/Mutex.h>
|
||||
|
||||
#define AML_DUMP_FUNCTION_CALLS 0
|
||||
#define AML_ENABLE_DEBUG 1
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Buffer;
|
||||
struct Integer;
|
||||
struct String;
|
||||
|
||||
struct Node : public BAN::RefCounted<Node>
|
||||
struct NameString
|
||||
{
|
||||
static uint64_t total_node_count;
|
||||
|
||||
enum Conversion : uint8_t
|
||||
BAN_NON_COPYABLE(NameString);
|
||||
public:
|
||||
NameString() = default;
|
||||
NameString(NameString&& other)
|
||||
: parts(BAN::move(other.parts))
|
||||
{}
|
||||
NameString& operator=(NameString&& other)
|
||||
{
|
||||
ConvBuffer = 1 << 0,
|
||||
ConvBufferField = 1 << 1,
|
||||
ConvFieldUnit = 1 << 2,
|
||||
ConvInteger = 1 << 3,
|
||||
ConvString = 1 << 4,
|
||||
parts = BAN::move(other.parts);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<NameString> from_string(BAN::StringView name);
|
||||
|
||||
BAN::ErrorOr<NameString> copy() const
|
||||
{
|
||||
NameString result;
|
||||
result.base = this->base;
|
||||
|
||||
TRY(result.parts.resize(parts.size()));
|
||||
for (size_t i = 0; i < parts.size(); i++)
|
||||
result.parts[i] = parts[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static constexpr uint32_t base_root = -1;
|
||||
uint32_t base { 0 };
|
||||
BAN::Vector<uint32_t> parts;
|
||||
};
|
||||
|
||||
template<typename T1, typename T2>
|
||||
struct Pair
|
||||
{
|
||||
T1 elem1;
|
||||
T2 elem2;
|
||||
};
|
||||
|
||||
struct Node;
|
||||
struct ParseContext;
|
||||
struct Reference;
|
||||
|
||||
struct Mutex
|
||||
{
|
||||
Kernel::Mutex mutex;
|
||||
uint8_t sync_level;
|
||||
bool global_lock;
|
||||
uint32_t ref_count;
|
||||
};
|
||||
|
||||
struct Buffer
|
||||
{
|
||||
uint64_t size;
|
||||
uint32_t ref_count;
|
||||
uint8_t bytes[];
|
||||
};
|
||||
|
||||
struct OpRegion
|
||||
{
|
||||
GAS::AddressSpaceID address_space;
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
};
|
||||
|
||||
struct FieldUnit
|
||||
{
|
||||
enum class Type {
|
||||
Field,
|
||||
IndexField,
|
||||
BankField,
|
||||
};
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
uint8_t flags;
|
||||
Type type;
|
||||
|
||||
union {
|
||||
struct {
|
||||
OpRegion opregion;
|
||||
} field;
|
||||
struct {
|
||||
Reference* index;
|
||||
Reference* data;
|
||||
} index_field;
|
||||
struct {
|
||||
OpRegion opregion;
|
||||
Reference* bank_selector;
|
||||
uint64_t bank_value;
|
||||
} bank_field;
|
||||
} as;
|
||||
};
|
||||
|
||||
struct Package
|
||||
{
|
||||
struct Element
|
||||
{
|
||||
struct Location {
|
||||
NameString name;
|
||||
Scope scope;
|
||||
};
|
||||
|
||||
bool resolved { true };
|
||||
union {
|
||||
Node* node { nullptr };
|
||||
Location* location;
|
||||
} value;
|
||||
};
|
||||
|
||||
enum class Type : uint8_t
|
||||
uint64_t num_elements;
|
||||
uint32_t ref_count;
|
||||
Element elements[];
|
||||
};
|
||||
|
||||
struct Node
|
||||
{
|
||||
BAN_NON_COPYABLE(Node);
|
||||
public:
|
||||
Node() = default;
|
||||
~Node() { clear(); }
|
||||
|
||||
Node(Node&& other) { *this = BAN::move(other); }
|
||||
Node& operator=(Node&&);
|
||||
|
||||
enum class Type
|
||||
{
|
||||
None,
|
||||
BankFieldElement,
|
||||
Buffer,
|
||||
BufferField,
|
||||
Uninitialized,
|
||||
Debug,
|
||||
Device,
|
||||
Event,
|
||||
FieldElement,
|
||||
IndexFieldElement,
|
||||
Integer,
|
||||
String,
|
||||
Buffer,
|
||||
Package,
|
||||
BufferField,
|
||||
OpRegion,
|
||||
FieldUnit,
|
||||
Event,
|
||||
Device,
|
||||
Processor,
|
||||
PowerResource,
|
||||
ThermalZone,
|
||||
Method,
|
||||
Mutex,
|
||||
Name,
|
||||
Namespace,
|
||||
OpRegion,
|
||||
Package,
|
||||
PackageElement,
|
||||
PowerResource,
|
||||
Processor,
|
||||
// FIXME: Index should not be its own type
|
||||
// parsing index should return references
|
||||
Index,
|
||||
Reference,
|
||||
Register,
|
||||
String,
|
||||
ThermalZone,
|
||||
};
|
||||
const Type type;
|
||||
PredefinedScope,
|
||||
Count
|
||||
} type { Type::Uninitialized };
|
||||
|
||||
Node(Type type) : type(type) { total_node_count++; }
|
||||
virtual ~Node() { total_node_count--; }
|
||||
inline bool is_scope() const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Type::Device:
|
||||
case Type::Processor:
|
||||
case Type::PowerResource:
|
||||
case Type::ThermalZone:
|
||||
case Type::PredefinedScope:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
virtual bool is_scope() const { return false; }
|
||||
union
|
||||
{
|
||||
struct {
|
||||
uint64_t value;
|
||||
} integer;
|
||||
Package* package;
|
||||
Buffer* str_buf;
|
||||
struct {
|
||||
Buffer* buffer;
|
||||
uint64_t bit_offset;
|
||||
uint64_t bit_count;
|
||||
} buffer_field;
|
||||
OpRegion opregion;
|
||||
FieldUnit field_unit;
|
||||
struct {
|
||||
uint32_t signal_count;
|
||||
} event;
|
||||
struct {
|
||||
uint8_t storage[sizeof(Kernel::Mutex)];
|
||||
const uint8_t* start;
|
||||
size_t length;
|
||||
uint8_t arg_count;
|
||||
BAN::ErrorOr<Node> (*override_func)(const BAN::Array<Reference*, 7>&);
|
||||
bool serialized;
|
||||
Mutex* mutex;
|
||||
} method;
|
||||
Mutex* mutex;
|
||||
struct {
|
||||
Node::Type type;
|
||||
union {
|
||||
Buffer* str_buf;
|
||||
Package* package;
|
||||
} as;
|
||||
uint64_t index;
|
||||
} index;
|
||||
Reference* reference;
|
||||
} as;
|
||||
|
||||
[[nodiscard]] virtual BAN::RefPtr<AML::Node> to_underlying() { return this; }
|
||||
[[nodiscard]] virtual BAN::RefPtr<AML::Node> convert(uint8_t mask) = 0;
|
||||
[[nodiscard]] virtual BAN::RefPtr<Node> copy() { return this; }
|
||||
[[nodiscard]] virtual BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node>) { AML_TODO("store, type {}", static_cast<uint8_t>(type)); return {}; }
|
||||
BAN::ErrorOr<Node> copy() const;
|
||||
|
||||
virtual void debug_print(int indent) const = 0;
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct ParseContext;
|
||||
struct ParseResult
|
||||
struct Reference
|
||||
{
|
||||
static ParseResult Failure;
|
||||
static ParseResult Success;
|
||||
|
||||
enum class Result
|
||||
{
|
||||
Success,
|
||||
Failure,
|
||||
Returned,
|
||||
Breaked,
|
||||
Continued,
|
||||
};
|
||||
|
||||
ParseResult(Result success)
|
||||
: m_result(success)
|
||||
{}
|
||||
ParseResult(Result success, BAN::RefPtr<Node> node)
|
||||
: m_result(success)
|
||||
, m_node(BAN::move(node))
|
||||
{}
|
||||
ParseResult(BAN::RefPtr<Node> node)
|
||||
: m_result(Result::Success)
|
||||
, m_node(BAN::move(node))
|
||||
{
|
||||
ASSERT(m_node);
|
||||
}
|
||||
|
||||
bool success() const { return m_result == Result::Success; }
|
||||
bool returned() const { return m_result == Result::Returned; }
|
||||
bool breaked() const { return m_result == Result::Breaked; }
|
||||
bool continued() const { return m_result == Result::Continued; }
|
||||
|
||||
BAN::RefPtr<Node> node()
|
||||
{
|
||||
return m_node;
|
||||
}
|
||||
|
||||
private:
|
||||
Result m_result = Result::Failure;
|
||||
BAN::RefPtr<Node> m_node;
|
||||
Node node {};
|
||||
uint32_t ref_count { 1 };
|
||||
};
|
||||
ParseResult parse_object(ParseContext& context);
|
||||
|
||||
struct ParseContext
|
||||
{
|
||||
Scope scope;
|
||||
BAN::ConstByteSpan aml_data;
|
||||
|
||||
uint32_t call_depth { 0 };
|
||||
BAN::Array<Reference*, 8> locals;
|
||||
BAN::Array<Reference*, 7> args;
|
||||
|
||||
BAN::LinkedList<Scope> created_nodes;
|
||||
|
||||
~ParseContext();
|
||||
BAN::ErrorOr<void> allocate_locals();
|
||||
};
|
||||
|
||||
enum class ExecutionFlow
|
||||
{
|
||||
Normal,
|
||||
Break,
|
||||
Continue,
|
||||
Return,
|
||||
};
|
||||
using ExecutionFlowResult = Pair<ExecutionFlow, BAN::Optional<Node>>;
|
||||
|
||||
enum Conversion : uint8_t
|
||||
{
|
||||
ConvInteger = 1,
|
||||
ConvBuffer = 2,
|
||||
ConvString = 4,
|
||||
};
|
||||
|
||||
BAN::ErrorOr<Node> parse_node(ParseContext& context, bool return_ref = false);
|
||||
BAN::ErrorOr<ExecutionFlowResult> parse_node_or_execution_flow(ParseContext& context);
|
||||
|
||||
BAN::ErrorOr<NameString> parse_name_string(BAN::ConstByteSpan& aml_data);
|
||||
BAN::ErrorOr<BAN::ConstByteSpan> parse_pkg(BAN::ConstByteSpan& aml_data);
|
||||
|
||||
BAN::ErrorOr<Node> convert_node(Node&& source, uint8_t conversion, size_t max_length);
|
||||
BAN::ErrorOr<Node> convert_node(Node&& source, const Node& target);
|
||||
|
||||
BAN::ErrorOr<Node> evaluate_node(const Scope& node_path, const Node& node);
|
||||
|
||||
// If method has no return, it will return <integer 0>
|
||||
BAN::ErrorOr<Node> method_call(const Scope& scope, const Node& method, BAN::Array<Reference*, 7>&& args, uint32_t call_depth = 0);
|
||||
|
||||
}
|
||||
|
||||
namespace BAN::Formatter
|
||||
{
|
||||
|
||||
template<typename F>
|
||||
void print_argument(F putc, const Kernel::ACPI::AML::NameString& name_string, const ValueFormat&)
|
||||
{
|
||||
if (name_string.base == Kernel::ACPI::AML::NameString::base_root)
|
||||
putc('\\');
|
||||
else for (uint32_t i = 0; i < name_string.base; i++)
|
||||
putc('^');
|
||||
for (size_t i = 0; i < name_string.parts.size(); i++) {
|
||||
if (i != 0)
|
||||
putc('.');
|
||||
const char* name_seg = reinterpret_cast<const char*>(&name_string.parts[i]);
|
||||
putc(name_seg[0]); putc(name_seg[1]); putc(name_seg[2]); putc(name_seg[3]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void print_argument(F putc, const Kernel::ACPI::AML::Buffer& buffer, const ValueFormat&)
|
||||
{
|
||||
static constexpr size_t max_elements { 16 };
|
||||
|
||||
print(putc, "<buffer '");
|
||||
if (buffer.size)
|
||||
print(putc, "{2H}", buffer.bytes[0]);
|
||||
for (size_t i = 1; i < buffer.size && i < max_elements; i++)
|
||||
print(putc, " {2H}", buffer.bytes[i]);
|
||||
if (buffer.size > max_elements)
|
||||
print(putc, "...");
|
||||
print(putc, "'>");
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void print_argument(F putc, const Kernel::ACPI::AML::Package& package, const ValueFormat&)
|
||||
{
|
||||
print(putc, "<package '{} elements'>", package.num_elements);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void print_argument(F putc, const Kernel::ACPI::AML::Node& node, const ValueFormat&)
|
||||
{
|
||||
switch (node.type)
|
||||
{
|
||||
case Kernel::ACPI::AML::Node::Type::Uninitialized:
|
||||
print(putc, "<uninitialized>");
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Debug:
|
||||
print(putc, "<debug>");
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Integer:
|
||||
print(putc, "<integer 0x{H}>", node.as.integer.value);
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::String:
|
||||
print(putc, "<string '{}'>",
|
||||
BAN::StringView(
|
||||
reinterpret_cast<const char*>(node.as.str_buf->bytes),
|
||||
node.as.str_buf->size
|
||||
)
|
||||
);
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Package:
|
||||
print(putc, "{}", *node.as.package);
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Buffer:
|
||||
print(putc, "{}", *node.as.str_buf);
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::BufferField:
|
||||
print(putc, "<buffer field '{} bytes, offset 0x{H}, bit count {}'>",
|
||||
node.as.buffer_field.buffer->size,
|
||||
node.as.buffer_field.bit_offset,
|
||||
node.as.buffer_field.bit_count
|
||||
);
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::OpRegion:
|
||||
print(putc, "<opregion 'type {2H}, offset 0x{H}, length 0x{H}'>",
|
||||
static_cast<uint8_t>(node.as.opregion.address_space),
|
||||
node.as.opregion.offset,
|
||||
node.as.opregion.length
|
||||
);
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::FieldUnit:
|
||||
print(putc, "<field unit ({}), 'offset 0x{H}, length 0x{H}'>",
|
||||
static_cast<uint8_t>(node.as.field_unit.type),
|
||||
node.as.field_unit.offset,
|
||||
node.as.field_unit.length
|
||||
);
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Event:
|
||||
print(putc, "<event '{} signals'>", node.as.event.signal_count);
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Device:
|
||||
print(putc, "<device>");
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Processor:
|
||||
print(putc, "<processor>");
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::PowerResource:
|
||||
print(putc, "<power resouce>");
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::ThermalZone:
|
||||
print(putc, "<thermal zone>");
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Method:
|
||||
print(putc, "<method '{} bytes'>", node.as.method.length);
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Mutex:
|
||||
print(putc, "<mutex>");
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Index:
|
||||
switch (node.as.index.type)
|
||||
{
|
||||
case Kernel::ACPI::AML::Node::Type::String:
|
||||
case Kernel::ACPI::AML::Node::Type::Buffer:
|
||||
print(putc, "<index {}, {}>", *node.as.index.as.str_buf, node.as.index.index);
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Package:
|
||||
print(putc, "<index {}, {}>", *node.as.index.as.package, node.as.index.index);
|
||||
break;
|
||||
default:
|
||||
print(putc, "<index {}??, {}>", (uint32_t)node.as.index.type, node.as.index.index);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Reference:
|
||||
print(putc, "<reference {}, {} refs>", node.as.reference->node, node.as.reference->ref_count);
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::PredefinedScope:
|
||||
print(putc, "<scope>");
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Count:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Device.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Processor.h>
|
||||
#include <kernel/ACPI/AML/ThermalZone.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Notify
|
||||
{
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::NotifyOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto object_result = AML::parse_object(context);
|
||||
if (!object_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto object = object_result.node();
|
||||
if (!object)
|
||||
{
|
||||
AML_ERROR("Notify object is null");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto value_result = AML::parse_object(context);
|
||||
if (!value_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto value_node = value_result.node() ? value_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!value_node)
|
||||
{
|
||||
AML_ERROR("Notify value is not an integer");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
const auto value = static_cast<AML::Integer*>(value_node.ptr())->value;
|
||||
|
||||
BAN::StringView object_type_sv;
|
||||
BAN::StringView object_name_sv;
|
||||
switch (object->type)
|
||||
{
|
||||
case AML::Node::Type::Device:
|
||||
object_type_sv = "Device"_sv;
|
||||
object_name_sv = static_cast<AML::Device*>(object.ptr())->name.sv();
|
||||
break;
|
||||
case AML::Node::Type::Processor:
|
||||
object_type_sv = "Processor"_sv;
|
||||
object_name_sv = static_cast<AML::Processor*>(object.ptr())->name.sv();
|
||||
break;
|
||||
case AML::Node::Type::ThermalZone:
|
||||
object_type_sv = "ThermalZone"_sv;
|
||||
object_name_sv = static_cast<AML::ThermalZone*>(object.ptr())->name.sv();
|
||||
break;
|
||||
default:
|
||||
object_type_sv = "Unknown"_sv;
|
||||
object_name_sv = "????"_sv;
|
||||
break;
|
||||
}
|
||||
|
||||
AML_TODO("Notify: {} {}: {2H}", object_type_sv, object_name_sv, value);
|
||||
return ParseResult::Success;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Reference.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct ObjectType
|
||||
{
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::ObjectTypeOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto object_result = AML::parse_object(context);
|
||||
if (!object_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto object = object_result.node()
|
||||
? object_result.node()->to_underlying()
|
||||
: BAN::RefPtr<AML::Node>();
|
||||
|
||||
if (object && object->type == AML::Node::Type::Reference)
|
||||
object = static_cast<AML::Reference*>(object.ptr())->node->to_underlying();
|
||||
|
||||
uint64_t value = 0;
|
||||
if (object)
|
||||
{
|
||||
switch (object->type)
|
||||
{
|
||||
case AML::Node::Type::None:
|
||||
case AML::Node::Type::Name:
|
||||
case AML::Node::Type::PackageElement:
|
||||
case AML::Node::Type::Reference:
|
||||
case AML::Node::Type::Register:
|
||||
ASSERT_NOT_REACHED();
|
||||
case AML::Node::Type::Namespace:
|
||||
value = 0;
|
||||
break;
|
||||
case AML::Node::Type::Integer:
|
||||
value = 1;
|
||||
break;
|
||||
case AML::Node::Type::String:
|
||||
value = 2;
|
||||
break;
|
||||
case AML::Node::Type::Buffer:
|
||||
value = 3;
|
||||
break;
|
||||
case AML::Node::Type::Package:
|
||||
value = 4;
|
||||
break;
|
||||
case AML::Node::Type::FieldElement:
|
||||
case AML::Node::Type::BankFieldElement:
|
||||
case AML::Node::Type::IndexFieldElement:
|
||||
value = 5;
|
||||
break;
|
||||
case AML::Node::Type::Device:
|
||||
value = 6;
|
||||
break;
|
||||
case AML::Node::Type::Event:
|
||||
value = 7;
|
||||
break;
|
||||
case AML::Node::Type::Method:
|
||||
value = 8;
|
||||
break;
|
||||
case AML::Node::Type::Mutex:
|
||||
value = 9;
|
||||
break;
|
||||
case AML::Node::Type::OpRegion:
|
||||
value = 10;
|
||||
break;
|
||||
case AML::Node::Type::PowerResource:
|
||||
value = 11;
|
||||
break;
|
||||
case AML::Node::Type::Processor:
|
||||
value = 12;
|
||||
break;
|
||||
case AML::Node::Type::ThermalZone:
|
||||
value = 13;
|
||||
break;
|
||||
case AML::Node::Type::BufferField:
|
||||
value = 14;
|
||||
break;
|
||||
case AML::Node::Type::Debug:
|
||||
value = 16;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
if (!object)
|
||||
AML_DEBUG_PRINTLN("ObjectType { null }");
|
||||
else
|
||||
{
|
||||
AML_DEBUG_PRINTLN("ObjectType {");
|
||||
object->debug_print(1);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
}
|
||||
#endif
|
||||
|
||||
return ParseResult(MUST(BAN::RefPtr<AML::Integer>::create(value)));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
BAN::ErrorOr<void> parse_opregion_op(ParseContext& context);
|
||||
BAN::ErrorOr<void> parse_field_op(ParseContext& context);
|
||||
BAN::ErrorOr<void> parse_index_field_op(ParseContext& context);
|
||||
BAN::ErrorOr<void> parse_bank_field_op(ParseContext& context);
|
||||
|
||||
BAN::ErrorOr<Node> convert_from_field_unit(const Node& node, uint8_t conversion, size_t max_length);
|
||||
BAN::ErrorOr<void> store_to_field_unit(const Node& source, const Node& target);
|
||||
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Pkg.h>
|
||||
#include <kernel/ACPI/AML/Reference.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct PackageElement;
|
||||
|
||||
struct Package final : public AML::Node
|
||||
{
|
||||
BAN::Vector<BAN::RefPtr<PackageElement>> elements;
|
||||
AML::NameString scope;
|
||||
|
||||
Package(AML::NameString scope)
|
||||
: Node(Node::Type::Package)
|
||||
, elements(BAN::move(elements))
|
||||
, scope(scope)
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
|
||||
|
||||
static ParseResult parse(AML::ParseContext& context);
|
||||
virtual void debug_print(int indent) const override;
|
||||
};
|
||||
|
||||
struct PackageElement final : public AML::Node
|
||||
{
|
||||
BAN::RefPtr<AML::Package> parent;
|
||||
BAN::RefPtr<AML::Node> element;
|
||||
AML::NameString unresolved_name;
|
||||
bool resolved = false;
|
||||
bool initialized = false;
|
||||
|
||||
PackageElement(BAN::RefPtr<AML::Package> parent, BAN::RefPtr<AML::Node> element)
|
||||
: Node(Node::Type::PackageElement)
|
||||
, parent(parent)
|
||||
, element(element)
|
||||
{
|
||||
ASSERT(element);
|
||||
resolved = true;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
PackageElement(BAN::RefPtr<AML::Package> parent, AML::NameString unresolved_name)
|
||||
: Node(Node::Type::PackageElement)
|
||||
, parent(parent)
|
||||
, unresolved_name(unresolved_name)
|
||||
{
|
||||
resolved = false;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
PackageElement(BAN::RefPtr<AML::Package> parent)
|
||||
: Node(Node::Type::PackageElement)
|
||||
, parent(parent)
|
||||
{
|
||||
resolved = false;
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
bool resolve()
|
||||
{
|
||||
ASSERT(!resolved);
|
||||
|
||||
auto object = Namespace::root_namespace()->find_object(parent->scope, unresolved_name, Namespace::FindMode::Normal);
|
||||
if (!object)
|
||||
{
|
||||
AML_ERROR("Failed to resolve reference {} in package {}", unresolved_name, parent->scope);
|
||||
return false;
|
||||
}
|
||||
element = object;
|
||||
resolved = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
AML_ERROR("Trying to convert uninitialized PackageElement");
|
||||
return {};
|
||||
}
|
||||
if (!resolved && !resolve())
|
||||
return {};
|
||||
return element->convert(mask);
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> to_underlying() override
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
AML_ERROR("Trying to read uninitialized PackageElement");
|
||||
return {};
|
||||
}
|
||||
if (!resolved && !resolve())
|
||||
return {};
|
||||
return element;
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> node) override
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
initialized = true;
|
||||
resolved = true;
|
||||
}
|
||||
if (!resolved && !resolve())
|
||||
return {};
|
||||
ASSERT(!element || element->type != AML::Node::Type::Reference);
|
||||
if (node->type == AML::Node::Type::Reference)
|
||||
element = static_cast<AML::Reference*>(node.ptr())->node;
|
||||
else
|
||||
element = node->copy();
|
||||
return node;
|
||||
}
|
||||
|
||||
static ParseResult parse(AML::ParseContext& context, BAN::RefPtr<AML::Package> package)
|
||||
{
|
||||
BAN::RefPtr<AML::PackageElement> element;
|
||||
if (context.aml_data[0] != 0x00 && AML::NameString::can_parse(context.aml_data))
|
||||
{
|
||||
auto name = AML::NameString::parse(context.aml_data);
|
||||
if (!name.has_value())
|
||||
return ParseResult::Failure;
|
||||
element = MUST(BAN::RefPtr<PackageElement>::create(package, name.value()));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto element_result = AML::parse_object(context);
|
||||
if (!element_result.success())
|
||||
return ParseResult::Failure;
|
||||
element = MUST(BAN::RefPtr<PackageElement>::create(package, element_result.node()));
|
||||
}
|
||||
return ParseResult(element);
|
||||
}
|
||||
|
||||
virtual void debug_print(int indent) const override
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINTLN("PackageElement {");
|
||||
if (!initialized)
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent + 1);
|
||||
AML_DEBUG_PRINT("Uninitialized");
|
||||
}
|
||||
else if (!resolved)
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent + 1);
|
||||
AML_DEBUG_PRINT("Unresolved {}", unresolved_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
element->debug_print(indent + 1);
|
||||
}
|
||||
AML_DEBUG_PRINTLN("");
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("}");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Array.h>
|
||||
#include <BAN/ByteSpan.h>
|
||||
#include <BAN/LinkedList.h>
|
||||
#include <kernel/ACPI/AML/NamedObject.h>
|
||||
#include <kernel/ACPI/AML/Namespace.h>
|
||||
#include <kernel/ACPI/AML/Register.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct ParseContext
|
||||
{
|
||||
BAN::ConstByteSpan aml_data;
|
||||
AML::NameString scope;
|
||||
|
||||
// Used for cleaning up on method exit
|
||||
// NOTE: This uses linked list instead of vector because
|
||||
// we don't really need large contiguous memory
|
||||
BAN::LinkedList<AML::NameString> created_objects;
|
||||
|
||||
uint8_t sync_level() const { return !sync_stack.empty() ? sync_stack.back() : 0; }
|
||||
BAN::Vector<uint8_t> sync_stack;
|
||||
|
||||
BAN::Array<BAN::RefPtr<Register>, 7> method_args;
|
||||
BAN::Array<BAN::RefPtr<Register>, 8> method_locals;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/ByteSpan.h>
|
||||
#include <BAN/Optional.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
static BAN::Optional<uint32_t> parse_pkg_length(BAN::ConstByteSpan aml_data)
|
||||
{
|
||||
if (aml_data.size() < 1)
|
||||
return {};
|
||||
|
||||
uint8_t lead_byte = aml_data[0];
|
||||
if ((lead_byte & 0xC0) && (lead_byte & 0x30))
|
||||
return {};
|
||||
|
||||
uint32_t pkg_length = lead_byte & 0x3F;
|
||||
uint8_t byte_count = (lead_byte >> 6) + 1;
|
||||
|
||||
if (aml_data.size() < byte_count)
|
||||
return {};
|
||||
|
||||
for (uint8_t i = 1; i < byte_count; i++)
|
||||
pkg_length |= aml_data[i] << (i * 8 - 4);
|
||||
|
||||
return pkg_length;
|
||||
}
|
||||
|
||||
static void trim_pkg_length(BAN::ConstByteSpan& aml_data)
|
||||
{
|
||||
ASSERT(aml_data.size() >= 1);
|
||||
uint8_t byte_count = (aml_data[0] >> 6) + 1;
|
||||
aml_data = aml_data.slice(byte_count);
|
||||
}
|
||||
|
||||
static BAN::Optional<BAN::ConstByteSpan> parse_pkg(BAN::ConstByteSpan& aml_data)
|
||||
{
|
||||
auto pkg_length = parse_pkg_length(aml_data);
|
||||
if (!pkg_length.has_value())
|
||||
return {};
|
||||
|
||||
auto result = aml_data.slice(0, pkg_length.value());
|
||||
trim_pkg_length(result);
|
||||
|
||||
aml_data = aml_data.slice(pkg_length.value());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Endianness.h>
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Pkg.h>
|
||||
#include <kernel/ACPI/AML/Scope.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct PowerResource final : public AML::Scope
|
||||
{
|
||||
uint8_t system_level;
|
||||
uint16_t resource_order;
|
||||
|
||||
PowerResource(NameSeg name, uint8_t system_level, uint16_t resource_order)
|
||||
: Scope(Node::Type::PowerResource, name)
|
||||
, system_level(system_level)
|
||||
, resource_order(resource_order)
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
|
||||
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::PowerResOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto opt_power_res_pkg = AML::parse_pkg(context.aml_data);
|
||||
if (!opt_power_res_pkg.has_value())
|
||||
return ParseResult::Failure;
|
||||
auto power_res_pkg = opt_power_res_pkg.value();
|
||||
|
||||
auto name = NameString::parse(power_res_pkg);
|
||||
if (!name.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
if (power_res_pkg.size() < 1)
|
||||
return ParseResult::Failure;
|
||||
uint8_t system_level = power_res_pkg[0];
|
||||
power_res_pkg = power_res_pkg.slice(1);
|
||||
|
||||
if (power_res_pkg.size() < 2)
|
||||
return ParseResult::Failure;
|
||||
uint16_t resource_order = BAN::little_endian_to_host<uint16_t>(*reinterpret_cast<const uint16_t*>(power_res_pkg.data()));
|
||||
power_res_pkg = power_res_pkg.slice(2);
|
||||
|
||||
auto power_res = MUST(BAN::RefPtr<PowerResource>::create(name->path.back(), system_level, resource_order));
|
||||
if (!Namespace::root_namespace()->add_named_object(context, name.value(), power_res))
|
||||
return ParseResult::Failure;
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
power_res->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
return power_res->enter_context_and_parse_term_list(context, name.value(), power_res_pkg);
|
||||
}
|
||||
|
||||
virtual void debug_print(int indent) const override
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("PowerResource {} (SystemLevel {}, ResourceOrder {})", name, system_level, resource_order);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Endianness.h>
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Pkg.h>
|
||||
#include <kernel/ACPI/AML/Scope.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Processor final : public AML::Scope
|
||||
{
|
||||
uint8_t id;
|
||||
uint32_t pblk_addr;
|
||||
uint8_t pblk_len;
|
||||
|
||||
Processor(NameSeg name, uint8_t id, uint32_t pblk_addr, uint8_t pblk_len)
|
||||
: Scope(Node::Type::Processor, name)
|
||||
, id(id)
|
||||
, pblk_addr(pblk_addr)
|
||||
, pblk_len(pblk_len)
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
|
||||
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::ProcessorOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto opt_processor_pkg = AML::parse_pkg(context.aml_data);
|
||||
if (!opt_processor_pkg.has_value())
|
||||
return ParseResult::Failure;
|
||||
auto processor_pkg = opt_processor_pkg.value();
|
||||
|
||||
auto name = NameString::parse(processor_pkg);
|
||||
if (!name.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
if (processor_pkg.size() < 1)
|
||||
return ParseResult::Failure;
|
||||
uint8_t id = processor_pkg[0];
|
||||
processor_pkg = processor_pkg.slice(1);
|
||||
|
||||
if (processor_pkg.size() < 4)
|
||||
return ParseResult::Failure;
|
||||
uint32_t pblk_addr = BAN::little_endian_to_host<uint32_t>(*reinterpret_cast<const uint32_t*>(processor_pkg.data()));
|
||||
processor_pkg = processor_pkg.slice(4);
|
||||
|
||||
if (processor_pkg.size() < 1)
|
||||
return ParseResult::Failure;
|
||||
uint8_t pblk_len = processor_pkg[0];
|
||||
processor_pkg = processor_pkg.slice(1);
|
||||
|
||||
auto processor = MUST(BAN::RefPtr<Processor>::create(name->path.back(), id, pblk_addr, pblk_len));
|
||||
if (!Namespace::root_namespace()->add_named_object(context, name.value(), processor))
|
||||
return ParseResult::Success;
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
processor->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
return processor->enter_context_and_parse_term_list(context, name.value(), processor_pkg);
|
||||
}
|
||||
|
||||
virtual void debug_print(int indent) const override
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINTLN("Processor {} (ID: {}, PBlkAddr: 0x{H}, PBlkLen: {}) {", name, id, pblk_addr, pblk_len);
|
||||
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("}");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,186 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
#include <kernel/ACPI/AML/Names.h>
|
||||
#include <kernel/ACPI/AML/String.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Reference final : public AML::Node
|
||||
{
|
||||
BAN::RefPtr<AML::Node> node;
|
||||
|
||||
Reference(BAN::RefPtr<AML::Node> node)
|
||||
: Node(AML::Node::Type::Reference)
|
||||
, node(node)
|
||||
{
|
||||
ASSERT(node);
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
|
||||
{
|
||||
ASSERT(node);
|
||||
return node->convert(mask);
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> value) override
|
||||
{
|
||||
ASSERT(node);
|
||||
return node->store(value);
|
||||
}
|
||||
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
|
||||
bool conditional = false;
|
||||
switch (static_cast<AML::Byte>(context.aml_data[0]))
|
||||
{
|
||||
case AML::Byte::DerefOfOp:
|
||||
return parse_dereference(context);
|
||||
case AML::Byte::RefOfOp:
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
conditional = false;
|
||||
break;
|
||||
case AML::Byte::ExtOpPrefix:
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::CondRefOfOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
conditional = true;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> object;
|
||||
if (NameString::can_parse(context.aml_data))
|
||||
{
|
||||
auto name = NameString::parse(context.aml_data);
|
||||
if (!name.has_value())
|
||||
return ParseResult::Failure;
|
||||
object = Namespace::root_namespace()->find_object(context.scope, name.value(), Namespace::FindMode::Normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto parse_result = AML::parse_object(context);
|
||||
if (!parse_result.success())
|
||||
return ParseResult::Failure;
|
||||
object = parse_result.node();
|
||||
}
|
||||
|
||||
if (object && object->type == AML::Node::Type::Reference)
|
||||
object = static_cast<AML::Reference*>(object.ptr())->node;
|
||||
if (object && object->type == AML::Node::Type::Register)
|
||||
object = static_cast<AML::Register*>(object.ptr())->value;
|
||||
|
||||
if (!conditional)
|
||||
{
|
||||
if (!object)
|
||||
{
|
||||
AML_ERROR("RefOf failed to resolve reference");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
auto reference = MUST(BAN::RefPtr<Reference>::create(object));
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
reference->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
return ParseResult(reference);
|
||||
}
|
||||
|
||||
if (context.aml_data.size() < 1)
|
||||
{
|
||||
AML_ERROR("CondRefOf missing target");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> target_node;
|
||||
if (context.aml_data[0] == 0x00)
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
else
|
||||
{
|
||||
auto target_result = AML::parse_object(context);
|
||||
if (!target_result.success())
|
||||
return ParseResult::Failure;
|
||||
target_node = target_result.node();
|
||||
if (!target_node)
|
||||
{
|
||||
AML_ERROR("CondRefOf failed to resolve target");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
}
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
AML_DEBUG_PRINT("CondRefOf ");
|
||||
if (object)
|
||||
object->debug_print(0);
|
||||
else
|
||||
AML_DEBUG_PRINT("null");
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
if (!object)
|
||||
return AML::ParseResult(Integer::Constants::Zero);
|
||||
|
||||
if (target_node && !target_node->store(object))
|
||||
{
|
||||
AML_ERROR("CondRefOf failed to store into target");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
return AML::ParseResult(Integer::Constants::Ones);
|
||||
}
|
||||
|
||||
static ParseResult parse_dereference(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::DerefOfOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
if (context.aml_data.size() >= 1 && static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::StringPrefix)
|
||||
{
|
||||
auto string_result = AML::String::parse(context);
|
||||
if (!string_result.success())
|
||||
return ParseResult::Failure;
|
||||
ASSERT(string_result.node());
|
||||
auto string = static_cast<AML::String*>(string_result.node().ptr());
|
||||
AML_TODO("DerefOf String ({})", string->string);
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto parse_result = AML::parse_object(context);
|
||||
if (!parse_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto node = parse_result.node();
|
||||
if (node && node->type == AML::Node::Type::Register)
|
||||
node = static_cast<AML::Register*>(node.ptr())->value;
|
||||
if (!node || node->type != AML::Node::Type::Reference)
|
||||
{
|
||||
AML_TODO("DerefOf source is not a Reference, but a {}", node ? static_cast<uint8_t>(node->type) : 999);
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
AML_DEBUG_PRINT("DerefOf ");
|
||||
node->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
return ParseResult(static_cast<Reference*>(node.ptr())->node);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void debug_print(int indent) const override
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINTLN("Reference {");
|
||||
node->debug_print(indent + 1);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("}");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/NamedObject.h>
|
||||
#include <kernel/ACPI/AML/Namespace.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct OpRegion final : public AML::NamedObject
|
||||
{
|
||||
using RegionSpace = GAS::AddressSpaceID;
|
||||
RegionSpace region_space;
|
||||
uint64_t region_offset;
|
||||
uint64_t region_length;
|
||||
|
||||
Kernel::Mutex mutex;
|
||||
|
||||
OpRegion(NameSeg name, RegionSpace region_space, uint64_t region_offset, uint64_t region_length)
|
||||
: NamedObject(Node::Type::OpRegion, name)
|
||||
, region_space(region_space)
|
||||
, region_offset(region_offset)
|
||||
, region_length(region_length)
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
|
||||
|
||||
static ParseResult parse(AML::ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() > 2);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<ExtOp>(context.aml_data[1]) == ExtOp::OpRegionOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto name = NameString::parse(context.aml_data);
|
||||
if (!name.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
if (context.aml_data.size() < 1)
|
||||
return ParseResult::Failure;
|
||||
auto region_space = static_cast<RegionSpace>(context.aml_data[0]);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto offset_result = AML::parse_object(context);
|
||||
if (!offset_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto offset_node = offset_result.node()
|
||||
? offset_result.node()->convert(AML::Node::ConvInteger)
|
||||
: BAN::RefPtr<AML::Node>();
|
||||
if (!offset_node)
|
||||
{
|
||||
AML_ERROR("OpRegion offset must be an integer");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto length_result = AML::parse_object(context);
|
||||
if (!length_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto length_node = length_result.node()
|
||||
? length_result.node()->convert(AML::Node::ConvInteger)
|
||||
: BAN::RefPtr<AML::Node>();
|
||||
if (!length_node)
|
||||
{
|
||||
AML_ERROR("OpRegion length must be an integer");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
const auto offset = static_cast<AML::Integer*>(offset_node.ptr())->value;
|
||||
const auto length = static_cast<AML::Integer*>(length_node.ptr())->value;
|
||||
|
||||
auto op_region = MUST(BAN::RefPtr<OpRegion>::create(
|
||||
name->path.back(),
|
||||
region_space,
|
||||
offset,
|
||||
length
|
||||
));
|
||||
|
||||
if (!Namespace::root_namespace()->add_named_object(context, name.value(), op_region))
|
||||
return ParseResult::Success;
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
op_region->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
|
||||
virtual void debug_print(int indent) const override
|
||||
{
|
||||
BAN::StringView region_space_name;
|
||||
switch (region_space)
|
||||
{
|
||||
case RegionSpace::SystemMemory: region_space_name = "SystemMemory"_sv; break;
|
||||
case RegionSpace::SystemIO: region_space_name = "SystemIO"_sv; break;
|
||||
case RegionSpace::PCIConfig: region_space_name = "PCIConfig"_sv; break;
|
||||
case RegionSpace::EmbeddedController: region_space_name = "EmbeddedController"_sv; break;
|
||||
case RegionSpace::SMBus: region_space_name = "SMBus"_sv; break;
|
||||
case RegionSpace::SystemCMOS: region_space_name = "SystemCMOS"_sv; break;
|
||||
case RegionSpace::PCIBarTarget: region_space_name = "PCIBarTarget"_sv; break;
|
||||
case RegionSpace::IPMI: region_space_name = "IPMI"_sv; break;
|
||||
case RegionSpace::GeneralPurposeIO: region_space_name = "GeneralPurposeIO"_sv; break;
|
||||
case RegionSpace::GenericSerialBus: region_space_name = "GenericSerialBus"_sv; break;
|
||||
case RegionSpace::PlatformCommunicationChannel: region_space_name = "PlatformCommunicationChannel"_sv; break;
|
||||
default: region_space_name = "Unknown"_sv; break;
|
||||
}
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("OperationRegion(");
|
||||
name.debug_print();
|
||||
AML_DEBUG_PRINT(", {}, 0x{H}, 0x{H})", region_space_name, region_offset, region_length);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Register final : public AML::Node
|
||||
{
|
||||
BAN::RefPtr<AML::Node> value;
|
||||
|
||||
Register();
|
||||
Register(BAN::RefPtr<AML::Node> node);
|
||||
|
||||
BAN::RefPtr<AML::Node> to_underlying() override { return value->to_underlying(); }
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t mask) override;
|
||||
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> source) override;
|
||||
|
||||
void debug_print(int indent) const override;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,27 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/NamedObject.h>
|
||||
#include <kernel/ACPI/AML/Names.h>
|
||||
#include <BAN/Hash.h>
|
||||
#include <BAN/NoCopyMove.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Scope : public AML::NamedObject
|
||||
struct Scope
|
||||
{
|
||||
AML::NameString scope;
|
||||
BAN_NON_COPYABLE(Scope);
|
||||
public:
|
||||
Scope() = default;
|
||||
Scope(Scope&& other) { *this = BAN::move(other); }
|
||||
Scope& operator=(Scope&& other) { parts = BAN::move(other.parts); return *this; }
|
||||
|
||||
Scope(Node::Type type, NameSeg name)
|
||||
: NamedObject(type, name)
|
||||
{}
|
||||
BAN::Vector<uint32_t> parts;
|
||||
|
||||
virtual bool is_scope() const override { return true; }
|
||||
BAN::ErrorOr<Scope> copy() const
|
||||
{
|
||||
Scope result;
|
||||
TRY(result.parts.reserve(parts.size()));
|
||||
for (uint32_t part : parts)
|
||||
TRY(result.parts.push_back(part));
|
||||
return result;
|
||||
}
|
||||
|
||||
static ParseResult parse(ParseContext& context);
|
||||
|
||||
protected:
|
||||
ParseResult enter_context_and_parse_term_list(ParseContext& outer_context, const AML::NameString& name, BAN::ConstByteSpan aml_data);
|
||||
bool operator==(const Scope& other) const
|
||||
{
|
||||
if (parts.size() != other.parts.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < parts.size(); i++)
|
||||
if (parts[i] != other.parts[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
bool initialize_scope(BAN::RefPtr<Scope> scope);
|
||||
BAN::ErrorOr<void> initialize_scope(const Scope&);
|
||||
|
||||
}
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<>
|
||||
struct hash<Kernel::ACPI::AML::Scope>
|
||||
{
|
||||
hash_t operator()(const Kernel::ACPI::AML::Scope& scope) const
|
||||
{
|
||||
hash_t hash { 0 };
|
||||
for (uint32_t part : scope.parts)
|
||||
hash ^= u32_hash(part);
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace BAN::Formatter
|
||||
{
|
||||
|
||||
template<typename F>
|
||||
void print_argument(F putc, const Kernel::ACPI::AML::Scope& scope, const ValueFormat&)
|
||||
{
|
||||
putc('\\');
|
||||
for (size_t i = 0; i < scope.parts.size(); i++) {
|
||||
if (i != 0)
|
||||
putc('.');
|
||||
const char* name_seg = reinterpret_cast<const char*>(&scope.parts[i]);
|
||||
putc(name_seg[0]); putc(name_seg[1]); putc(name_seg[2]); putc(name_seg[3]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <kernel/ACPI/AML/Buffer.h>
|
||||
#include <kernel/ACPI/AML/Names.h>
|
||||
#include <kernel/ACPI/AML/Package.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Reference.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct SizeOf
|
||||
{
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::SizeOfOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto object_result = AML::parse_object(context);
|
||||
if (!object_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto object_node = object_result.node();
|
||||
if (object_node)
|
||||
object_node = object_node->to_underlying();
|
||||
if (!object_node)
|
||||
{
|
||||
AML_ERROR("SizeOf object is null");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
if (object_node->type != AML::Node::Type::Package)
|
||||
object_node = object_node->convert(AML::Node::ConvBuffer | AML::Node::ConvString);
|
||||
if (!object_node)
|
||||
{
|
||||
AML_ERROR("SizeOf object is not Buffer, String or Package");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
uint64_t size = 0;
|
||||
switch (object_node->type)
|
||||
{
|
||||
case AML::Node::Type::Buffer:
|
||||
size = static_cast<AML::Buffer*>(object_node.ptr())->buffer.size();
|
||||
break;
|
||||
case AML::Node::Type::String:
|
||||
size = static_cast<AML::String*>(object_node.ptr())->string.size();
|
||||
break;
|
||||
case AML::Node::Type::Package:
|
||||
size = static_cast<AML::Package*>(object_node.ptr())->elements.size();
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
return ParseResult(MUST(BAN::RefPtr<Integer>::create(size)));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Sleep
|
||||
{
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::SleepOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto sleep_time_result = AML::parse_object(context);
|
||||
if (!sleep_time_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto sleep_time_node = sleep_time_result.node()
|
||||
? sleep_time_result.node()->convert(AML::Node::ConvInteger)
|
||||
: BAN::RefPtr<AML::Node>();
|
||||
if (!sleep_time_node)
|
||||
{
|
||||
AML_ERROR("Sleep time cannot be evaluated to an integer");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
const auto sleep_time_value = static_cast<AML::Integer*>(sleep_time_node.ptr())->value;
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
AML_DEBUG_PRINTLN("Sleeping for {} ms", sleep_time_value);
|
||||
#endif
|
||||
|
||||
SystemTimer::get().sleep_ms(sleep_time_value);
|
||||
return ParseResult::Success;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct Store
|
||||
{
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::StoreOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto source_result = AML::parse_object(context);
|
||||
if (!source_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto source = source_result.node();
|
||||
if (!source)
|
||||
{
|
||||
AML_ERROR("Store source is null");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto destination_result = AML::parse_object(context);
|
||||
if (!destination_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto destination = destination_result.node();
|
||||
if (!destination)
|
||||
{
|
||||
AML_ERROR("Store destination is null");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
AML_DEBUG_PRINTLN("Storing {");
|
||||
source->debug_print(1);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
AML_DEBUG_PRINTLN("} to {");
|
||||
destination->debug_print(1);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
AML_DEBUG_PRINTLN("}");
|
||||
#endif
|
||||
|
||||
if (auto stored = destination->store(source))
|
||||
return ParseResult(stored);
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct String final : public AML::Node
|
||||
{
|
||||
BAN::Vector<uint8_t> string;
|
||||
|
||||
String() : Node(Node::Type::String) {}
|
||||
String(BAN::StringView string)
|
||||
: Node(Node::Type::String)
|
||||
{
|
||||
MUST(this->string.resize(string.size()));
|
||||
for (size_t i = 0; i < string.size(); i++)
|
||||
this->string[i] = string[i];
|
||||
}
|
||||
|
||||
BAN::Optional<bool> logical_compare(BAN::RefPtr<AML::Node> node, AML::Byte binaryop);
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t mask) override;
|
||||
|
||||
BAN::RefPtr<AML::Node> copy() override
|
||||
{
|
||||
auto new_string = MUST(BAN::RefPtr<AML::String>::create());
|
||||
MUST(new_string->string.resize(this->string.size()));
|
||||
for (size_t i = 0; i < this->string.size(); i++)
|
||||
new_string->string[i] = this->string[i];
|
||||
return new_string;
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> node) override
|
||||
{
|
||||
ASSERT(node);
|
||||
auto conv_node = node->convert(AML::Node::ConvString);
|
||||
if (!conv_node)
|
||||
{
|
||||
AML_ERROR("Could not convert to String");
|
||||
return {};
|
||||
}
|
||||
auto* string_node = static_cast<AML::String*>(conv_node.ptr());
|
||||
MUST(string.resize(string_node->string.size()));
|
||||
for (size_t i = 0; i < string.size(); i++)
|
||||
string[i] = string_node->string[i];
|
||||
return string_node->copy();
|
||||
}
|
||||
|
||||
BAN::StringView string_view() const
|
||||
{
|
||||
return BAN::StringView(reinterpret_cast<const char*>(string.data()), string.size());
|
||||
}
|
||||
|
||||
static ParseResult parse(ParseContext& context);
|
||||
|
||||
virtual void debug_print(int indent) const override
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("String \"{}\"", string_view());
|
||||
}
|
||||
|
||||
private:
|
||||
BAN::RefPtr<AML::Buffer> as_buffer();
|
||||
};
|
||||
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Endianness.h>
|
||||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Pkg.h>
|
||||
#include <kernel/ACPI/AML/Scope.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct ThermalZone final : public AML::Scope
|
||||
{
|
||||
ThermalZone(NameSeg name)
|
||||
: Scope(Node::Type::ThermalZone, name)
|
||||
{}
|
||||
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
|
||||
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::ThermalZoneOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto opt_thermal_zone_pkg = AML::parse_pkg(context.aml_data);
|
||||
if (!opt_thermal_zone_pkg.has_value())
|
||||
return ParseResult::Failure;
|
||||
auto thermal_zone_pkg = opt_thermal_zone_pkg.value();
|
||||
|
||||
auto name = NameString::parse(thermal_zone_pkg);
|
||||
if (!name.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto thermal_zone = MUST(BAN::RefPtr<ThermalZone>::create(name->path.back()));
|
||||
if (!Namespace::root_namespace()->add_named_object(context, name.value(), thermal_zone))
|
||||
return ParseResult::Success;
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
thermal_zone->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
return thermal_zone->enter_context_and_parse_term_list(context, name.value(), thermal_zone_pkg);
|
||||
}
|
||||
|
||||
virtual void debug_print(int indent) const override
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("ThermalZone {} {", name);
|
||||
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("}");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Formatter.h>
|
||||
#include <kernel/Debug.h>
|
||||
|
||||
// AML_DEBUG_LEVEL:
|
||||
// 0: No debug output
|
||||
// 1: Dump AML after parsing
|
||||
// 2: Dump AML while parsing
|
||||
#define AML_DEBUG_LEVEL 0
|
||||
|
||||
#define AML_TODO(...) \
|
||||
do { \
|
||||
BAN::Formatter::print(Debug::putchar, "\e[33mTODO: "); \
|
||||
BAN::Formatter::print(Debug::putchar, __VA_ARGS__); \
|
||||
BAN::Formatter::println(Debug::putchar, "\e[m"); \
|
||||
} while (0)
|
||||
|
||||
#define AML_ERROR(...) \
|
||||
do { \
|
||||
BAN::Formatter::print(Debug::putchar, "\e[31mERROR: "); \
|
||||
BAN::Formatter::print(Debug::putchar, __VA_ARGS__); \
|
||||
BAN::Formatter::println(Debug::putchar, "\e[m"); \
|
||||
} while (0)
|
||||
|
||||
#define AML_PRINT(...) BAN::Formatter::println(Debug::putchar, __VA_ARGS__)
|
||||
|
||||
#define AML_DEBUG_PRINT_INDENT(indent) \
|
||||
do { \
|
||||
for (int i = 0; i < (indent) * 2; i++) \
|
||||
AML_DEBUG_PUTC(' '); \
|
||||
} while (0)
|
||||
|
||||
#define AML_DEBUG_PUTC(c) Debug::putchar(c)
|
||||
#define AML_DEBUG_PRINT(...) BAN::Formatter::print(Debug::putchar, __VA_ARGS__)
|
||||
#define AML_DEBUG_PRINTLN(...) BAN::Formatter::println(Debug::putchar, __VA_ARGS__)
|
|
@ -1,77 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Pkg.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
struct While
|
||||
{
|
||||
static ParseResult parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
|
||||
AML::Byte opcode = static_cast<Byte>(context.aml_data[0]);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case AML::Byte::BreakOp:
|
||||
return ParseResult(ParseResult::Result::Breaked);
|
||||
case AML::Byte::ContinueOp:
|
||||
return ParseResult(ParseResult::Result::Continued);
|
||||
case AML::Byte::WhileOp:
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
auto while_pkg = AML::parse_pkg(context.aml_data);
|
||||
if (!while_pkg.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto outer_aml_data = context.aml_data;
|
||||
|
||||
bool breaked = false;
|
||||
while (!breaked)
|
||||
{
|
||||
context.aml_data = while_pkg.value();
|
||||
|
||||
auto predicate_result = AML::parse_object(context);
|
||||
if (!predicate_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto predicate_node = predicate_result.node()
|
||||
? predicate_result.node()->convert(AML::Node::ConvInteger)
|
||||
: BAN::RefPtr<AML::Node>();
|
||||
if (!predicate_node)
|
||||
{
|
||||
AML_ERROR("While predicate is not an integer");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
if (!static_cast<AML::Integer*>(predicate_node.ptr())->value)
|
||||
break;
|
||||
|
||||
while (context.aml_data.size() > 0)
|
||||
{
|
||||
auto object_result = AML::parse_object(context);
|
||||
if (object_result.returned())
|
||||
return ParseResult(ParseResult::Result::Returned, object_result.node());
|
||||
if (object_result.breaked())
|
||||
breaked = true;
|
||||
if (object_result.breaked() || object_result.continued())
|
||||
break;
|
||||
if (!object_result.success())
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
}
|
||||
|
||||
context.aml_data = outer_aml_data;
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Optional.h>
|
||||
|
||||
namespace Kernel::ACPI
|
||||
|
@ -22,8 +23,8 @@ namespace Kernel::ACPI
|
|||
PlatformCommunicationChannel = 0x0A,
|
||||
};
|
||||
|
||||
BAN::Optional<uint64_t> read();
|
||||
bool write(uint64_t value);
|
||||
BAN::ErrorOr<uint64_t> read();
|
||||
BAN::ErrorOr<void> write(uint64_t value);
|
||||
|
||||
AddressSpaceID address_space_id;
|
||||
uint8_t register_bit_width;
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
#include <BAN/ScopeGuard.h>
|
||||
#include <BAN/StringView.h>
|
||||
#include <kernel/ACPI/ACPI.h>
|
||||
#include <kernel/ACPI/AML.h>
|
||||
#include <kernel/ACPI/AML/Alias.h>
|
||||
#include <kernel/ACPI/AML/Device.h>
|
||||
#include <kernel/ACPI/AML/Field.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/Method.h>
|
||||
#include <kernel/ACPI/AML/Package.h>
|
||||
#include <kernel/ACPI/AML/OpRegion.h>
|
||||
#include <kernel/BootInfo.h>
|
||||
#include <kernel/InterruptController.h>
|
||||
#include <kernel/IO.h>
|
||||
|
@ -112,64 +106,62 @@ acpi_release_global_lock:
|
|||
ASSERT(!acpi_release_global_lock(s_global_lock));
|
||||
}
|
||||
|
||||
static BAN::Optional<AML::FieldRules::AccessType> get_access_type(uint8_t access_size)
|
||||
static BAN::ErrorOr<uint8_t> get_access_type(uint8_t access_size)
|
||||
{
|
||||
switch (access_size)
|
||||
{
|
||||
case 0: return AML::FieldRules::AccessType::Any;
|
||||
case 1: return AML::FieldRules::AccessType::Byte;
|
||||
case 2: return AML::FieldRules::AccessType::Word;
|
||||
case 3: return AML::FieldRules::AccessType::DWord;
|
||||
case 4: return AML::FieldRules::AccessType::QWord;
|
||||
case 0: return 0;
|
||||
case 1: return 1;
|
||||
case 2: return 2;
|
||||
case 3: return 3;
|
||||
case 4: return 4;
|
||||
default:
|
||||
dwarnln("Unknown access size {}", access_size);
|
||||
return {};
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
BAN::Optional<uint64_t> GAS::read()
|
||||
BAN::ErrorOr<uint64_t> GAS::read()
|
||||
{
|
||||
auto access_type = get_access_type(access_size);
|
||||
if (!access_type.has_value())
|
||||
return {};
|
||||
AML::OpRegion opregion;
|
||||
opregion.address_space = address_space_id;
|
||||
opregion.offset = address;
|
||||
opregion.length = 0xFFFFFFFF;
|
||||
|
||||
auto op_region = MUST(BAN::RefPtr<AML::OpRegion>::create(""_sv, address_space_id, (uint64_t)address, 0xFFFFFFFF));
|
||||
AML::Node field_unit;
|
||||
field_unit.type = AML::Node::Type::FieldUnit;
|
||||
field_unit.as.field_unit.type = AML::FieldUnit::Type::Field;
|
||||
field_unit.as.field_unit.as.field.opregion = opregion;
|
||||
field_unit.as.field_unit.length = register_bit_width;
|
||||
field_unit.as.field_unit.offset = register_bit_offset;
|
||||
field_unit.as.field_unit.flags = TRY(get_access_type(access_size));
|
||||
|
||||
auto field_rules = AML::FieldRules {
|
||||
.access_type = access_type.value(),
|
||||
.lock_rule = AML::FieldRules::LockRule::NoLock,
|
||||
.update_rule = AML::FieldRules::UpdateRule::Preserve,
|
||||
.access_attrib = AML::FieldRules::AccessAttrib::Normal,
|
||||
.access_length = 0
|
||||
};
|
||||
auto field_element = MUST(BAN::RefPtr<AML::FieldElement>::create(""_sv, register_bit_offset, register_bit_width, field_rules));
|
||||
field_element->op_region = op_region;
|
||||
|
||||
auto result = field_element->convert(AML::Node::ConvInteger);
|
||||
if (!result)
|
||||
return {};
|
||||
return static_cast<AML::Integer*>(result.ptr())->value;
|
||||
auto result = TRY(AML::convert_from_field_unit(field_unit, AML::ConvInteger, sizeof(uint64_t)));
|
||||
return result.as.integer.value;
|
||||
}
|
||||
|
||||
bool GAS::write(uint64_t value)
|
||||
BAN::ErrorOr<void> GAS::write(uint64_t value)
|
||||
{
|
||||
auto access_type = get_access_type(access_size);
|
||||
if (!access_type.has_value())
|
||||
return {};
|
||||
AML::OpRegion opregion;
|
||||
opregion.address_space = address_space_id;
|
||||
opregion.offset = address;
|
||||
opregion.length = 0xFFFFFFFF;
|
||||
|
||||
auto op_region = MUST(BAN::RefPtr<AML::OpRegion>::create(""_sv, address_space_id, (uint64_t)address, 0xFFFFFFFF));
|
||||
AML::Node field_unit;
|
||||
field_unit.type = AML::Node::Type::FieldUnit;
|
||||
field_unit.as.field_unit.type = AML::FieldUnit::Type::Field;
|
||||
field_unit.as.field_unit.as.field.opregion = opregion;
|
||||
field_unit.as.field_unit.length = register_bit_width;
|
||||
field_unit.as.field_unit.offset = register_bit_offset;
|
||||
field_unit.as.field_unit.flags = TRY(get_access_type(access_size));
|
||||
|
||||
auto field_rules = AML::FieldRules {
|
||||
.access_type = access_type.value(),
|
||||
.lock_rule = AML::FieldRules::LockRule::NoLock,
|
||||
.update_rule = AML::FieldRules::UpdateRule::Preserve,
|
||||
.access_attrib = AML::FieldRules::AccessAttrib::Normal,
|
||||
.access_length = 0
|
||||
};
|
||||
auto field_element = MUST(BAN::RefPtr<AML::FieldElement>::create(""_sv, register_bit_offset, register_bit_width, field_rules));
|
||||
field_element->op_region = op_region;
|
||||
AML::Node source;
|
||||
source.type = AML::Node::Type::Integer;
|
||||
source.as.integer.value = value;
|
||||
|
||||
return !!field_element->store(MUST(BAN::RefPtr<AML::Integer>::create(value)));
|
||||
TRY(AML::store_to_field_unit(source, field_unit));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
enum PM1Event : uint16_t
|
||||
|
@ -474,77 +466,81 @@ acpi_release_global_lock:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool ACPI::prepare_sleep(uint8_t sleep_state)
|
||||
BAN::ErrorOr<void> ACPI::prepare_sleep(uint8_t sleep_state)
|
||||
{
|
||||
auto pts_object = m_namespace->find_object({}, AML::NameString("_PTS"), AML::Namespace::FindMode::ForceAbsolute);
|
||||
if (pts_object && pts_object->type == AML::Node::Type::Method)
|
||||
auto [pts_path, pts_object] = TRY(m_namespace->find_named_object({}, MUST(AML::NameString::from_string("\\_PTS"))));
|
||||
if (pts_object == nullptr)
|
||||
return {};
|
||||
|
||||
auto& pts_node = pts_object->node;
|
||||
if (pts_node.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 false;
|
||||
}
|
||||
|
||||
if (!method->invoke(MUST(BAN::RefPtr<AML::Integer>::create(sleep_state))).has_value())
|
||||
{
|
||||
dwarnln("Failed to evaluate \\_PTS");
|
||||
return false;
|
||||
}
|
||||
|
||||
dprintln("Executed \\_PTS");
|
||||
dwarnln("Object \\_PTS is not a method");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
return true;
|
||||
if (pts_node.as.method.arg_count != 1)
|
||||
{
|
||||
dwarnln("Method \\_PTS has {} arguments, expected 1", pts_node.as.method.arg_count);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
AML::Reference arg_ref;
|
||||
arg_ref.node.type = AML::Node::Type::Integer;
|
||||
arg_ref.node.as.integer.value = sleep_state;
|
||||
arg_ref.ref_count = 2;
|
||||
|
||||
BAN::Array<AML::Reference*, 7> arguments(nullptr);
|
||||
arguments[0] = &arg_ref; // method call should not delete argument
|
||||
TRY(AML::method_call(pts_path, pts_node, BAN::move(arguments)));
|
||||
|
||||
dprintln("Executed \\_PTS({})", sleep_state);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void ACPI::poweroff()
|
||||
BAN::ErrorOr<void> ACPI::poweroff()
|
||||
{
|
||||
if (!m_namespace)
|
||||
{
|
||||
dwarnln("ACPI namespace not initialized");
|
||||
return;
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
auto s5_object = m_namespace->find_object({}, AML::NameString("_S5"), AML::Namespace::FindMode::ForceAbsolute);
|
||||
auto [_, s5_object] = TRY(m_namespace->find_named_object({}, TRY(AML::NameString::from_string("\\_S5_"_sv))));
|
||||
if (!s5_object)
|
||||
{
|
||||
dwarnln("\\_S5 not found");
|
||||
return;
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
auto s5_evaluated = s5_object->to_underlying();
|
||||
if (!s5_evaluated)
|
||||
{
|
||||
dwarnln("Failed to evaluate \\_S5");
|
||||
return;
|
||||
}
|
||||
if (s5_evaluated->type != AML::Node::Type::Package)
|
||||
|
||||
auto& s5_node = s5_object->node;
|
||||
if (s5_node.type != AML::Node::Type::Package)
|
||||
{
|
||||
dwarnln("\\_S5 is not a package");
|
||||
return;
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
auto* s5_package = static_cast<AML::Package*>(s5_evaluated.ptr());
|
||||
if (s5_package->elements.size() < 2)
|
||||
if (s5_node.as.package->num_elements < 2)
|
||||
{
|
||||
dwarnln("\\_S5 package has {} elements, expected atleast 2", s5_package->elements.size());
|
||||
return;
|
||||
dwarnln("\\_S5 package has {} elements, expected atleast 2", s5_node.as.package->num_elements);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
auto slp_typa_node = s5_package->elements[0]->convert(AML::Node::ConvInteger);
|
||||
auto slp_typb_node = s5_package->elements[1]->convert(AML::Node::ConvInteger);
|
||||
if (!slp_typa_node || !slp_typb_node)
|
||||
if (!s5_node.as.package->elements[0].resolved || !s5_node.as.package->elements[1].resolved)
|
||||
{
|
||||
dwarnln("Failed to get SLP_TYPx values");
|
||||
return;
|
||||
dwarnln("TODO: lazy evaluate package \\_S5 elements");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
if (!prepare_sleep(5))
|
||||
return;
|
||||
auto slp_typa_node = TRY(AML::convert_node(TRY(s5_node.as.package->elements[0].value.node->copy()), AML::ConvInteger, sizeof(uint64_t)));
|
||||
auto slp_typb_node = TRY(AML::convert_node(TRY(s5_node.as.package->elements[1].value.node->copy()), AML::ConvInteger, sizeof(uint64_t)));
|
||||
|
||||
TRY(prepare_sleep(5));
|
||||
|
||||
dprintln("Entering sleep state S5");
|
||||
|
||||
const auto slp_typa_value = static_cast<AML::Integer*>(slp_typa_node.ptr())->value;
|
||||
const auto slp_typb_value = static_cast<AML::Integer*>(slp_typb_node.ptr())->value;
|
||||
const auto slp_typa_value = slp_typa_node.as.integer.value;
|
||||
const auto slp_typb_value = slp_typb_node.as.integer.value;
|
||||
|
||||
uint16_t pm1a_data = IO::inw(fadt().pm1a_cnt_blk);
|
||||
pm1a_data &= ~(PM1_CNT_SLP_TYP_MASK << PM1_CNT_SLP_TYP_SHIFT);
|
||||
|
@ -562,11 +558,10 @@ acpi_release_global_lock:
|
|||
}
|
||||
|
||||
// system must not execute after sleep registers are written
|
||||
g_paniced = true;
|
||||
asm volatile("ud2");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
void ACPI::reset()
|
||||
BAN::ErrorOr<void> ACPI::reset()
|
||||
{
|
||||
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/04_ACPI_Hardware_Specification/ACPI_Hardware_Specification.html#reset-register
|
||||
|
||||
|
@ -579,36 +574,64 @@ acpi_release_global_lock:
|
|||
break;
|
||||
default:
|
||||
dwarnln("Reset register has invalid address space ID ({})", static_cast<uint8_t>(reset_reg.address_space_id));
|
||||
return;
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
if (reset_reg.register_bit_offset != 0 || reset_reg.register_bit_width != 8)
|
||||
{
|
||||
dwarnln("Reset register has invalid location ({} bits at bit offset {})", reset_reg.register_bit_width, reset_reg.register_bit_offset);
|
||||
return;
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
if (!prepare_sleep(5))
|
||||
return;
|
||||
TRY(prepare_sleep(5));
|
||||
|
||||
dprintln("Resetting system");
|
||||
|
||||
if (!reset_reg.write(fadt().reset_value))
|
||||
{
|
||||
dwarnln("Could not write reset value");
|
||||
return;
|
||||
}
|
||||
TRY(reset_reg.write(fadt().reset_value));
|
||||
|
||||
// system must not execute after reset register is written
|
||||
g_paniced = true;
|
||||
asm volatile("ud2");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> ACPI::load_aml_tables(BAN::StringView name, bool all)
|
||||
{
|
||||
BAN::ErrorOr<void> result {};
|
||||
|
||||
for (uint32_t i = 0;; i++)
|
||||
{
|
||||
auto* header = get_header(name, i);
|
||||
if (header == nullptr)
|
||||
break;
|
||||
|
||||
if (all)
|
||||
dprintln("Parsing {}{}, {} bytes", name, i + 1, header->length);
|
||||
else
|
||||
dprintln("Parsing {}, {} bytes", name, header->length);
|
||||
|
||||
auto header_span = BAN::ConstByteSpan(reinterpret_cast<const uint8_t*>(header), header->length);
|
||||
if (auto parse_ret = m_namespace->parse(header_span); parse_ret.is_error())
|
||||
result = parse_ret.release_error();
|
||||
|
||||
if (!all)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> ACPI::enter_acpi_mode(uint8_t mode)
|
||||
{
|
||||
ASSERT(!m_namespace);
|
||||
m_namespace = AML::initialize_namespace();
|
||||
if (!m_namespace)
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
|
||||
TRY(AML::Namespace::initialize_root_namespace());
|
||||
m_namespace = &AML::Namespace::root_namespace();
|
||||
|
||||
if (auto ret = load_aml_tables("DSDT"_sv, false); ret.is_error())
|
||||
dwarnln("Could not load DSDT: {}", ret.error());
|
||||
if (auto ret = load_aml_tables("SSDT"_sv, true); ret.is_error())
|
||||
dwarnln("Could not load all SSDTs: {}", ret.error());
|
||||
if (auto ret = load_aml_tables("PSDT"_sv, true); ret.is_error())
|
||||
dwarnln("Could not load all PSDTs: {}", ret.error());
|
||||
|
||||
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/16_Waking_and_Sleeping/initialization.html#placing-the-system-in-acpi-mode
|
||||
|
||||
|
@ -639,30 +662,43 @@ acpi_release_global_lock:
|
|||
|
||||
dprintln("Entered ACPI mode");
|
||||
|
||||
dprintln("Initializing devices");
|
||||
dprintln("Calling opregion _REG methods");
|
||||
|
||||
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 = m_namespace->find_object({}, AML::NameString("_SB"), AML::Namespace::FindMode::ForceAbsolute);
|
||||
if (_sb && _sb->is_scope())
|
||||
{
|
||||
auto* scope = static_cast<AML::Scope*>(_sb.ptr());
|
||||
AML::initialize_scope(scope);
|
||||
}
|
||||
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 = m_namespace->find_object({}, AML::NameString("_PIC"), AML::Namespace::FindMode::ForceAbsolute);
|
||||
if (_pic && _pic->type == AML::Node::Type::Method)
|
||||
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)
|
||||
{
|
||||
auto* method = static_cast<AML::Method*>(_pic.ptr());
|
||||
if (method->arg_count != 1)
|
||||
auto& pic_node = pic_obj->node;
|
||||
if (pic_node.as.method.arg_count != 1)
|
||||
{
|
||||
dwarnln("Method \\_PIC has {} arguments, expected 1", method->arg_count);
|
||||
dwarnln("Method \\_PIC has {} arguments, expected 1", pic_node.as.method.arg_count);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
method->invoke(MUST(BAN::RefPtr<AML::Integer>::create(mode)));
|
||||
|
||||
AML::Reference arg_ref;
|
||||
arg_ref.node.type = AML::Node::Type::Integer;
|
||||
arg_ref.node.as.integer.value = mode;
|
||||
arg_ref.ref_count = 2;
|
||||
|
||||
BAN::Array<AML::Reference*, 7> arguments(nullptr);
|
||||
arguments[0] = &arg_ref; // method call should not delete argument
|
||||
TRY(AML::method_call(pic_path, pic_node, BAN::move(arguments)));
|
||||
}
|
||||
|
||||
dprintln("Devices are initialized");
|
||||
dprintln("Initializing ACPI interrupts");
|
||||
|
||||
uint8_t irq = fadt().sci_int;
|
||||
if (auto ret = InterruptController::get().reserve_irq(irq); ret.is_error())
|
||||
|
@ -690,34 +726,43 @@ acpi_release_global_lock:
|
|||
|
||||
if (fadt().gpe0_blk)
|
||||
{
|
||||
// Enable all events in _GPE (_Lxx or _Exx)
|
||||
m_namespace->for_each_child(AML::NameString("\\_GPE"),
|
||||
[&](const auto& path, auto& node)
|
||||
{
|
||||
if (node->type != AML::Node::Type::Method)
|
||||
return;
|
||||
if (path.size() < 4)
|
||||
return;
|
||||
auto [gpe_scope, gpe_obj] = TRY(m_namespace->find_named_object({}, TRY(AML::NameString::from_string("\\_GPE"))));
|
||||
if (gpe_obj && gpe_obj->node.is_scope())
|
||||
{
|
||||
m_gpe_scope = BAN::move(gpe_scope);
|
||||
|
||||
auto name = path.sv().substring(path.size() - 4);
|
||||
if (name.substring(0, 2) != "_L"_sv && name.substring(0, 2) != "_E"_sv)
|
||||
return;
|
||||
// Enable all events in _GPE (_Lxx or _Exx)
|
||||
TRY(m_namespace->for_each_child(m_gpe_scope,
|
||||
[&](BAN::StringView name, AML::Reference* node_ref) -> BAN::Iteration
|
||||
{
|
||||
if (node_ref->node.type != AML::Node::Type::Method)
|
||||
return BAN::Iteration::Continue;
|
||||
|
||||
auto index = hex_sv_to_int(name.substring(2));
|
||||
if (!index.has_value())
|
||||
return;
|
||||
ASSERT(name.size() == 4);
|
||||
if (!name.starts_with("_L"_sv) && !name.starts_with("_E"_sv))
|
||||
return BAN::Iteration::Continue;
|
||||
|
||||
auto byte = index.value() / 8;
|
||||
auto bit = index.value() % 8;
|
||||
auto gpe0_en_port = fadt().gpe0_blk + (fadt().gpe0_blk_len / 2) + byte;
|
||||
IO::outb(gpe0_en_port, IO::inb(gpe0_en_port) | (1 << bit));
|
||||
auto index = hex_sv_to_int(name.substring(2));
|
||||
if (!index.has_value())
|
||||
{
|
||||
dwarnln("invalid GPE number '{}'", name);
|
||||
return BAN::Iteration::Continue;
|
||||
}
|
||||
|
||||
auto* method = static_cast<AML::Method*>(node.ptr());
|
||||
m_gpe_methods[index.value()] = method;
|
||||
auto byte = index.value() / 8;
|
||||
auto bit = index.value() % 8;
|
||||
auto gpe0_en_port = fadt().gpe0_blk + (fadt().gpe0_blk_len / 2) + byte;
|
||||
IO::outb(gpe0_en_port, IO::inb(gpe0_en_port) | (1 << bit));
|
||||
|
||||
dprintln("Enabled GPE {}", index.value(), byte, bit);
|
||||
}
|
||||
);
|
||||
m_gpe_methods[index.value()] = node_ref;
|
||||
node_ref->ref_count++;
|
||||
|
||||
dprintln("Enabled GPE {}", index.value(), byte, bit);
|
||||
|
||||
return BAN::Iteration::Continue;
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
set_irq(irq);
|
||||
|
@ -769,7 +814,8 @@ acpi_release_global_lock:
|
|||
|
||||
auto index = i * 8 + (pending & ~(pending - 1));
|
||||
if (m_gpe_methods[index])
|
||||
m_gpe_methods[index]->invoke();
|
||||
if (auto ret = AML::method_call(m_gpe_scope, m_gpe_methods[index]->node, {}); ret.is_error())
|
||||
dwarnln("Failed to evaluate _GPE {}: ", index, ret.error());
|
||||
|
||||
handled_event = true;
|
||||
IO::outb(fadt().gpe0_blk + i, 1 << index);
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
#include <BAN/ByteSpan.h>
|
||||
#include <BAN/Variant.h>
|
||||
#include <kernel/ACPI/ACPI.h>
|
||||
#include <kernel/ACPI/AML.h>
|
||||
|
||||
namespace Kernel::ACPI
|
||||
{
|
||||
|
||||
static void load_all(AML::Namespace& ns, BAN::StringView signature)
|
||||
{
|
||||
for (uint32_t i = 0;; i++)
|
||||
{
|
||||
auto* header = ACPI::ACPI::get().get_header(signature, i);
|
||||
if (!header)
|
||||
break;
|
||||
|
||||
dprintln("Parsing {}{} ({} bytes)", signature, i, header->length);
|
||||
if (!ns.parse(*header))
|
||||
{
|
||||
dwarnln("Failed to parse {}", signature);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Namespace> AML::initialize_namespace()
|
||||
{
|
||||
auto ns = AML::Namespace::create_root_namespace();
|
||||
|
||||
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#differentiated-system-description-table-dsdt
|
||||
auto* dsdt = ACPI::ACPI::get().get_header("DSDT", 0);
|
||||
if (!dsdt)
|
||||
{
|
||||
dwarnln("Failed to get DSDT");
|
||||
return {};
|
||||
}
|
||||
dprintln("Parsing DSDT ({} bytes)", dsdt->length);
|
||||
if (!ns->parse(*dsdt))
|
||||
{
|
||||
dwarnln("Failed to parse DSDT");
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#secondary-system-description-table-ssdt
|
||||
load_all(*ns, "SSDT");
|
||||
|
||||
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#persistent-system-description-table-psdt
|
||||
load_all(*ns, "PSDT");
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 1
|
||||
ns->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
dprintln("Parsed ACPI namespace, total of {} nodes created", AML::Node::total_node_count);
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,881 +0,0 @@
|
|||
#include <BAN/ScopeGuard.h>
|
||||
#include <kernel/ACPI/ACPI.h>
|
||||
#include <kernel/ACPI/AML/Field.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/IO.h>
|
||||
#include <kernel/PCI.h>
|
||||
|
||||
namespace Kernel::ACPI
|
||||
{
|
||||
|
||||
template<typename Func>
|
||||
concept ReadFunc = requires(Func func, uint64_t offset)
|
||||
{
|
||||
requires BAN::is_same_v<decltype(func(offset)), BAN::Optional<uint64_t>>;
|
||||
};
|
||||
|
||||
template<typename Func>
|
||||
concept WriteFunc = requires(Func func, uint64_t offset, uint64_t value)
|
||||
{
|
||||
requires BAN::is_same_v<decltype(func(offset, value)), bool>;
|
||||
};
|
||||
|
||||
template<typename Element>
|
||||
struct ParseFieldElementContext
|
||||
{
|
||||
AML::FieldRules field_rules;
|
||||
uint64_t field_bit_offset;
|
||||
BAN::ConstByteSpan field_pkg;
|
||||
BAN::HashMap<AML::NameSeg, BAN::RefPtr<Element>> elements;
|
||||
};
|
||||
|
||||
template<typename Element>
|
||||
static bool parse_field_element(ParseFieldElementContext<Element>& context)
|
||||
{
|
||||
// FIXME: Validate elements
|
||||
|
||||
ASSERT(context.field_pkg.size() >= 1);
|
||||
switch (context.field_pkg[0])
|
||||
{
|
||||
case 0x00:
|
||||
{
|
||||
context.field_pkg = context.field_pkg.slice(1);
|
||||
|
||||
auto reserved_length = AML::parse_pkg_length(context.field_pkg);
|
||||
if (!reserved_length.has_value())
|
||||
{
|
||||
AML_ERROR("Invalid FieldElement length for reserved field");
|
||||
return false;
|
||||
}
|
||||
AML::trim_pkg_length(context.field_pkg);
|
||||
|
||||
context.field_bit_offset += reserved_length.value();
|
||||
return true;
|
||||
}
|
||||
case 0x01:
|
||||
{
|
||||
context.field_pkg = context.field_pkg.slice(1);
|
||||
|
||||
if (context.field_pkg.size() < 2)
|
||||
{
|
||||
AML_ERROR("Invalid FieldElement length for access field");
|
||||
return false;
|
||||
}
|
||||
|
||||
context.field_rules.access_type = static_cast<AML::FieldRules::AccessType>(context.field_pkg[0] & 0x0F);
|
||||
context.field_rules.access_attrib = static_cast<AML::FieldRules::AccessAttrib>((context.field_pkg[0] >> 6) & 0x03);
|
||||
context.field_pkg = context.field_pkg.slice(1);
|
||||
|
||||
context.field_rules.access_length = context.field_pkg[0];
|
||||
context.field_pkg = context.field_pkg.slice(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
case 0x02:
|
||||
AML_TODO("Field element Connection", context.field_pkg[0]);
|
||||
return false;
|
||||
case 0x03:
|
||||
{
|
||||
context.field_pkg = context.field_pkg.slice(1);
|
||||
|
||||
if (context.field_pkg.size() < 3)
|
||||
{
|
||||
AML_ERROR("Invalid FieldElement length for extended access field");
|
||||
return false;
|
||||
}
|
||||
|
||||
context.field_rules.access_type = static_cast<AML::FieldRules::AccessType>(context.field_pkg[0] & 0x0F);
|
||||
context.field_rules.lock_rule = static_cast<AML::FieldRules::LockRule>((context.field_pkg[0] >> 4) & 0x01);
|
||||
context.field_rules.update_rule = static_cast<AML::FieldRules::UpdateRule>((context.field_pkg[0] >> 5) & 0x03);
|
||||
context.field_pkg = context.field_pkg.slice(1);
|
||||
|
||||
if (context.field_pkg[0] == 0x0B)
|
||||
context.field_rules.access_attrib = AML::FieldRules::AccessAttrib::Bytes;
|
||||
else if (context.field_pkg[0] == 0x0E)
|
||||
context.field_rules.access_attrib = AML::FieldRules::AccessAttrib::RawBytes;
|
||||
else if (context.field_pkg[0] == 0x0F)
|
||||
context.field_rules.access_attrib = AML::FieldRules::AccessAttrib::RawProcessBytes;
|
||||
else
|
||||
{
|
||||
AML_ERROR("Invalid FieldElement extended access field attribute");
|
||||
return false;
|
||||
}
|
||||
context.field_pkg = context.field_pkg.slice(1);
|
||||
|
||||
context.field_rules.access_length = context.field_pkg[0];
|
||||
context.field_pkg = context.field_pkg.slice(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
auto element_name = AML::NameSeg::parse(context.field_pkg);
|
||||
if (!element_name.has_value())
|
||||
{
|
||||
AML_ERROR("Invalid FieldElement name for named field");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto element_length = AML::parse_pkg_length(context.field_pkg);
|
||||
if (!element_length.has_value())
|
||||
{
|
||||
AML_ERROR("Invalid FieldElement length for named field");
|
||||
return false;
|
||||
}
|
||||
AML::trim_pkg_length(context.field_pkg);
|
||||
|
||||
if (context.elements.contains(element_name.value()))
|
||||
{
|
||||
AML_ERROR("Field element already exists");
|
||||
return false;
|
||||
}
|
||||
|
||||
MUST(context.elements.emplace(
|
||||
element_name.value(),
|
||||
MUST(BAN::RefPtr<Element>::create(
|
||||
element_name.value(),
|
||||
context.field_bit_offset,
|
||||
element_length.value(),
|
||||
context.field_rules
|
||||
))
|
||||
));
|
||||
context.field_bit_offset += element_length.value();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static BAN::Optional<uint32_t> determine_access_size(const AML::FieldRules::AccessType& access_type)
|
||||
{
|
||||
switch (access_type)
|
||||
{
|
||||
case AML::FieldRules::AccessType::Any:
|
||||
case AML::FieldRules::AccessType::Byte:
|
||||
return 1;
|
||||
case AML::FieldRules::AccessType::Word:
|
||||
return 2;
|
||||
case AML::FieldRules::AccessType::DWord:
|
||||
return 4;
|
||||
case AML::FieldRules::AccessType::QWord:
|
||||
return 8;
|
||||
case AML::FieldRules::AccessType::Buffer:
|
||||
AML_TODO("FieldElement with access type Buffer");
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static BAN::Optional<uint64_t> perform_read(AML::OpRegion::RegionSpace region_space, uint64_t access_offset, uint32_t access_size)
|
||||
{
|
||||
switch (region_space)
|
||||
{
|
||||
case AML::OpRegion::RegionSpace::SystemMemory:
|
||||
{
|
||||
uint64_t result = 0;
|
||||
size_t index_in_page = (access_offset % PAGE_SIZE) / access_size;
|
||||
PageTable::with_fast_page(access_offset & PAGE_ADDR_MASK, [&] {
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: result = PageTable::fast_page_as_sized<uint8_t> (index_in_page); break;
|
||||
case 2: result = PageTable::fast_page_as_sized<uint16_t>(index_in_page); break;
|
||||
case 4: result = PageTable::fast_page_as_sized<uint32_t>(index_in_page); break;
|
||||
case 8: result = PageTable::fast_page_as_sized<uint64_t>(index_in_page); break;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
case AML::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 AML::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;
|
||||
|
||||
uint64_t result = 0;
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: result = PCI::PCIManager::get().read_config_byte(0, device, function, offset); break;
|
||||
case 2: result = PCI::PCIManager::get().read_config_word(0, device, function, offset); break;
|
||||
case 4: result = PCI::PCIManager::get().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 read_field with region space {}", static_cast<uint8_t>(region_space));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
static bool perform_write(AML::OpRegion::RegionSpace region_space, uint64_t access_offset, uint32_t access_size, uint64_t value)
|
||||
{
|
||||
switch (region_space)
|
||||
{
|
||||
case AML::OpRegion::RegionSpace::SystemMemory:
|
||||
{
|
||||
size_t index_in_page = (access_offset % PAGE_SIZE) / access_size;
|
||||
PageTable::with_fast_page(access_offset & PAGE_ADDR_MASK, [&] {
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: PageTable::fast_page_as_sized<uint8_t> (index_in_page) = value; break;
|
||||
case 2: PageTable::fast_page_as_sized<uint16_t>(index_in_page) = value; break;
|
||||
case 4: PageTable::fast_page_as_sized<uint32_t>(index_in_page) = value; break;
|
||||
case 8: PageTable::fast_page_as_sized<uint64_t>(index_in_page) = value; break;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
case AML::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 AML::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::get().write_config_byte(0, device, function, offset, value); break;
|
||||
case 2: PCI::PCIManager::get().write_config_word(0, device, function, offset, value); break;
|
||||
case 4: PCI::PCIManager::get().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>(region_space));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<ReadFunc ReadFunc>
|
||||
static BAN::Optional<uint64_t> perform_read_general(
|
||||
uint64_t base_byte_offset,
|
||||
uint64_t bit_count,
|
||||
uint64_t bit_offset,
|
||||
uint32_t access_size,
|
||||
ReadFunc& read_func
|
||||
)
|
||||
{
|
||||
if (bit_count > 64)
|
||||
{
|
||||
AML_TODO("Field read with bit_count > 64");
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t access_bytes = access_size;
|
||||
uint32_t access_bits = access_bytes * 8;
|
||||
|
||||
uint64_t result = 0;
|
||||
uint32_t bits_read = 0;
|
||||
while (bits_read < bit_count)
|
||||
{
|
||||
uint64_t byte_offset = base_byte_offset + ((bit_offset + bits_read) / 8);
|
||||
if (auto rem = byte_offset % access_bytes)
|
||||
byte_offset -= rem;
|
||||
|
||||
auto partial = read_func(byte_offset);
|
||||
if (!partial.has_value())
|
||||
return {};
|
||||
|
||||
uint32_t shift = (bit_offset + bits_read) % access_bits;
|
||||
uint32_t valid_bits = BAN::Math::min<uint32_t>(access_bits - shift, bit_count - bits_read);
|
||||
uint64_t mask = ((uint64_t)1 << valid_bits) - 1;
|
||||
|
||||
result |= ((partial.value() >> shift) & mask) << bits_read;
|
||||
bits_read += valid_bits;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<ReadFunc ReadFunc, WriteFunc WriteFunc>
|
||||
static bool perform_write_general(
|
||||
uint64_t base_byte_offset,
|
||||
uint64_t bit_count,
|
||||
uint64_t bit_offset,
|
||||
uint32_t access_size,
|
||||
uint64_t value,
|
||||
AML::FieldRules::UpdateRule update_rule,
|
||||
ReadFunc& read_func,
|
||||
WriteFunc& write_func
|
||||
)
|
||||
{
|
||||
if (bit_count > 64)
|
||||
{
|
||||
AML_TODO("Field write with bit_count > 64");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t access_bytes = access_size;
|
||||
uint32_t access_bits = access_bytes * 8;
|
||||
|
||||
uint32_t bits_written = 0;
|
||||
while (bits_written < bit_count)
|
||||
{
|
||||
uint64_t byte_offset = base_byte_offset + ((bit_offset + bits_written) / 8);
|
||||
if (auto rem = byte_offset % access_bytes)
|
||||
byte_offset -= rem;
|
||||
|
||||
uint32_t shift = (bit_offset + bits_written) % access_bits;
|
||||
uint32_t valid_bits = BAN::Math::min<uint32_t>(access_bits - shift, bit_count - bits_written);
|
||||
uint64_t mask = ((uint64_t)1 << valid_bits) - 1;
|
||||
|
||||
uint64_t to_write = 0;
|
||||
if (valid_bits != access_bits)
|
||||
{
|
||||
switch (update_rule)
|
||||
{
|
||||
case AML::FieldRules::UpdateRule::Preserve:
|
||||
{
|
||||
auto read_result = read_func(byte_offset);
|
||||
if (!read_result.has_value())
|
||||
return false;
|
||||
to_write = read_result.value() & ~(mask << shift);
|
||||
break;
|
||||
}
|
||||
case AML::FieldRules::UpdateRule::WriteAsOnes:
|
||||
to_write = ~(mask << shift);
|
||||
break;
|
||||
case AML::FieldRules::UpdateRule::WriteAsZeros:
|
||||
to_write = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
to_write |= ((value >> bits_written) & mask) << shift;
|
||||
|
||||
if (!write_func(byte_offset, to_write))
|
||||
return false;
|
||||
|
||||
bits_written += valid_bits;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AML::ParseResult AML::Field::parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::FieldOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto opt_field_pkg = AML::parse_pkg(context.aml_data);
|
||||
if (!opt_field_pkg.has_value())
|
||||
return ParseResult::Failure;
|
||||
auto field_pkg = opt_field_pkg.release_value();
|
||||
|
||||
auto name_string = NameString::parse(field_pkg);
|
||||
if (!name_string.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto op_region = Namespace::root_namespace()->find_object(context.scope, name_string.value(), Namespace::FindMode::Normal);
|
||||
if (!op_region || op_region->type != AML::Node::Type::OpRegion)
|
||||
{
|
||||
AML_ERROR("FieldOp: {} does not name a valid OpRegion", name_string.value());
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
if (field_pkg.size() < 1)
|
||||
return ParseResult::Failure;
|
||||
auto field_flags = field_pkg[0];
|
||||
field_pkg = field_pkg.slice(1);
|
||||
|
||||
ParseFieldElementContext<FieldElement> field_context;
|
||||
field_context.field_rules.access_type = static_cast<FieldRules::AccessType>(field_flags & 0x0F);
|
||||
field_context.field_rules.lock_rule = static_cast<FieldRules::LockRule>((field_flags >> 4) & 0x01);
|
||||
field_context.field_rules.update_rule = static_cast<FieldRules::UpdateRule>((field_flags >> 5) & 0x03);
|
||||
field_context.field_bit_offset = 0;
|
||||
field_context.field_pkg = field_pkg;
|
||||
while (field_context.field_pkg.size() > 0)
|
||||
if (!parse_field_element(field_context))
|
||||
return ParseResult::Failure;
|
||||
|
||||
for (auto& [_, element] : field_context.elements)
|
||||
{
|
||||
element->op_region = static_cast<OpRegion*>(op_region.ptr());
|
||||
|
||||
NameString element_name;
|
||||
MUST(element_name.path.push_back(element->name));
|
||||
if (!Namespace::root_namespace()->add_named_object(context, element_name, element))
|
||||
return ParseResult::Failure;
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
element->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
}
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
|
||||
BAN::Optional<uint64_t> AML::FieldElement::evaluate_internal()
|
||||
{
|
||||
if (access_rules.access_attrib != FieldRules::AccessAttrib::Normal)
|
||||
{
|
||||
AML_TODO("FieldElement with access attribute {}", static_cast<uint8_t>(access_rules.access_attrib));
|
||||
return {};
|
||||
}
|
||||
auto access_size = determine_access_size(access_rules.access_type);
|
||||
if (!access_size.has_value())
|
||||
return {};
|
||||
auto read_func = [&](uint64_t byte_offset) -> BAN::Optional<uint64_t> {
|
||||
return perform_read(op_region->region_space, byte_offset, access_size.value());
|
||||
};
|
||||
return perform_read_general(op_region->region_offset, bit_count, bit_offset, access_size.value(), read_func);
|
||||
}
|
||||
|
||||
bool AML::FieldElement::store_internal(uint64_t value)
|
||||
{
|
||||
if (access_rules.access_attrib != FieldRules::AccessAttrib::Normal)
|
||||
{
|
||||
AML_TODO("FieldElement with access attribute {}", static_cast<uint8_t>(access_rules.access_attrib));
|
||||
return {};
|
||||
}
|
||||
auto access_size = determine_access_size(access_rules.access_type);
|
||||
if (!access_size.has_value())
|
||||
return false;
|
||||
auto read_func = [&](uint64_t byte_offset) -> BAN::Optional<uint64_t> {
|
||||
return perform_read(op_region->region_space, byte_offset, access_size.value());
|
||||
};
|
||||
auto write_func = [&](uint64_t byte_offset, uint64_t value) -> bool {
|
||||
return perform_write(op_region->region_space, byte_offset, access_size.value(), value);
|
||||
};
|
||||
return perform_write_general(op_region->region_offset, bit_count, bit_offset, access_size.value(), value, access_rules.update_rule, read_func, write_func);
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Integer> AML::FieldElement::as_integer()
|
||||
{
|
||||
op_region->mutex.lock();
|
||||
BAN::ScopeGuard unlock_guard([&] {
|
||||
op_region->mutex.unlock();
|
||||
});
|
||||
|
||||
auto result = evaluate_internal();
|
||||
if (!result.has_value())
|
||||
return {};
|
||||
return MUST(BAN::RefPtr<Integer>::create(result.value()));
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> AML::FieldElement::store(BAN::RefPtr<AML::Node> source)
|
||||
{
|
||||
ASSERT(source);
|
||||
auto source_integer = source->convert(AML::Node::ConvInteger);
|
||||
if (!source_integer)
|
||||
{
|
||||
AML_TODO("FieldElement store with non-integer source, type {}", static_cast<uint8_t>(source->type));
|
||||
return {};
|
||||
}
|
||||
|
||||
op_region->mutex.lock();
|
||||
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
|
||||
ACPI::acquire_global_lock();
|
||||
BAN::ScopeGuard unlock_guard([&] {
|
||||
op_region->mutex.unlock();
|
||||
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
|
||||
ACPI::release_global_lock();
|
||||
});
|
||||
|
||||
if (!store_internal(static_cast<AML::Integer*>(source_integer.ptr())->value))
|
||||
return {};
|
||||
return source_integer;
|
||||
}
|
||||
|
||||
void AML::FieldElement::debug_print(int indent) const
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("FieldElement {} ({}, offset {}, OpRegion {})",
|
||||
name,
|
||||
bit_count,
|
||||
bit_offset,
|
||||
op_region->name
|
||||
);
|
||||
}
|
||||
|
||||
AML::ParseResult AML::IndexField::parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::IndexFieldOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto opt_field_pkg = AML::parse_pkg(context.aml_data);
|
||||
if (!opt_field_pkg.has_value())
|
||||
return ParseResult::Failure;
|
||||
auto field_pkg = opt_field_pkg.release_value();
|
||||
|
||||
auto index_field_element_name = NameString::parse(field_pkg);
|
||||
if (!index_field_element_name.has_value())
|
||||
return ParseResult::Failure;
|
||||
auto index_field_element = Namespace::root_namespace()->find_object(context.scope, index_field_element_name.value(), Namespace::FindMode::Normal);
|
||||
if (!index_field_element || index_field_element->type != AML::Node::Type::FieldElement)
|
||||
{
|
||||
AML_ERROR("IndexField IndexName does not name a valid FieldElement");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto data_field_element_name = NameString::parse(field_pkg);
|
||||
if (!data_field_element_name.has_value())
|
||||
return ParseResult::Failure;
|
||||
auto data_field_element = Namespace::root_namespace()->find_object(context.scope, data_field_element_name.value(), Namespace::FindMode::Normal);
|
||||
if (!data_field_element || data_field_element->type != AML::Node::Type::FieldElement)
|
||||
{
|
||||
AML_ERROR("IndexField DataName does not name a valid FieldElement");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
if (field_pkg.size() < 1)
|
||||
return ParseResult::Failure;
|
||||
auto field_flags = field_pkg[0];
|
||||
field_pkg = field_pkg.slice(1);
|
||||
|
||||
ParseFieldElementContext<IndexFieldElement> field_context;
|
||||
field_context.field_rules.access_type = static_cast<FieldRules::AccessType>(field_flags & 0x0F);
|
||||
field_context.field_rules.lock_rule = static_cast<FieldRules::LockRule>((field_flags >> 4) & 0x01);
|
||||
field_context.field_rules.update_rule = static_cast<FieldRules::UpdateRule>((field_flags >> 5) & 0x03);
|
||||
field_context.field_bit_offset = 0;
|
||||
field_context.field_pkg = field_pkg;
|
||||
while (field_context.field_pkg.size() > 0)
|
||||
if (!parse_field_element(field_context))
|
||||
return ParseResult::Failure;
|
||||
|
||||
for (auto& [_, element] : field_context.elements)
|
||||
{
|
||||
element->index_element = static_cast<FieldElement*>(index_field_element.ptr());
|
||||
element->data_element = static_cast<FieldElement*>(data_field_element.ptr());
|
||||
|
||||
NameString element_name;
|
||||
MUST(element_name.path.push_back(element->name));
|
||||
if (!Namespace::root_namespace()->add_named_object(context, element_name, element))
|
||||
return ParseResult::Failure;
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
element->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
}
|
||||
|
||||
return AML::ParseResult::Success;
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Integer> AML::IndexFieldElement::as_integer()
|
||||
{
|
||||
if (access_rules.access_attrib != FieldRules::AccessAttrib::Normal)
|
||||
{
|
||||
AML_TODO("IndexFieldElement with access attribute {}", static_cast<uint8_t>(access_rules.access_attrib));
|
||||
return {};
|
||||
}
|
||||
auto access_size = determine_access_size(access_rules.access_type);
|
||||
if (!access_size.has_value())
|
||||
return {};
|
||||
if (access_size.value() > data_element->bit_count)
|
||||
{
|
||||
AML_ERROR("IndexFieldElement read_field with access size {} > data element bit count {}", access_size.value(), data_element->bit_count);
|
||||
return {};
|
||||
}
|
||||
auto read_func = [&](uint64_t byte_offset) -> BAN::Optional<uint64_t> {
|
||||
if (!index_element->store_internal(byte_offset))
|
||||
return {};
|
||||
return data_element->evaluate_internal();
|
||||
};
|
||||
|
||||
index_element->op_region->mutex.lock();
|
||||
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
|
||||
ACPI::acquire_global_lock();
|
||||
BAN::ScopeGuard unlock_guard([&] {
|
||||
index_element->op_region->mutex.unlock();
|
||||
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
|
||||
ACPI::release_global_lock();
|
||||
});
|
||||
|
||||
auto result = perform_read_general(0, bit_count, bit_offset, access_size.value(), read_func);
|
||||
if (!result.has_value())
|
||||
return {};
|
||||
return MUST(BAN::RefPtr<Integer>::create(result.value()));
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> AML::IndexFieldElement::store(BAN::RefPtr<Node> source)
|
||||
{
|
||||
if (access_rules.access_attrib != FieldRules::AccessAttrib::Normal)
|
||||
{
|
||||
AML_TODO("FieldElement with access attribute {}", static_cast<uint8_t>(access_rules.access_attrib));
|
||||
return {};
|
||||
}
|
||||
|
||||
ASSERT(source);
|
||||
auto source_integer = source->convert(AML::Node::ConvInteger);
|
||||
if (!source_integer)
|
||||
{
|
||||
AML_TODO("IndexFieldElement store with non-integer source, type {}", static_cast<uint8_t>(source->type));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto access_size = determine_access_size(access_rules.access_type);
|
||||
if (!access_size.has_value())
|
||||
return {};
|
||||
|
||||
if (access_size.value() > data_element->bit_count)
|
||||
{
|
||||
AML_ERROR("IndexFieldElement write_field with access size {} > data element bit count {}", access_size.value(), data_element->bit_count);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto read_func = [&](uint64_t byte_offset) -> BAN::Optional<uint64_t> {
|
||||
if (!index_element->store_internal(byte_offset))
|
||||
return {};
|
||||
return data_element->evaluate_internal();
|
||||
};
|
||||
auto write_func = [&](uint64_t byte_offset, uint64_t value) -> bool {
|
||||
if (!index_element->store_internal(byte_offset))
|
||||
return {};
|
||||
return data_element->store_internal(value);
|
||||
};
|
||||
|
||||
index_element->op_region->mutex.lock();
|
||||
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
|
||||
ACPI::acquire_global_lock();
|
||||
BAN::ScopeGuard unlock_guard([&] {
|
||||
index_element->op_region->mutex.unlock();
|
||||
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
|
||||
ACPI::release_global_lock();
|
||||
});
|
||||
|
||||
const auto source_value = static_cast<AML::Integer*>(source_integer.ptr())->value;
|
||||
if (!perform_write_general(0, bit_count, bit_offset, access_size.value(), source_value, access_rules.update_rule, read_func, write_func))
|
||||
return {};
|
||||
return source_integer;
|
||||
}
|
||||
|
||||
void AML::IndexFieldElement::debug_print(int indent) const
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("IndexFieldElement {} ({}, offset {}, IndexName {}, DataName {})",
|
||||
name,
|
||||
bit_count,
|
||||
bit_offset,
|
||||
index_element->name,
|
||||
data_element->name
|
||||
);
|
||||
}
|
||||
|
||||
AML::ParseResult AML::BankField::parse(ParseContext& context)
|
||||
{
|
||||
// BankFieldOp PkgLength NameString NameString BankValue FieldFlags FieldList
|
||||
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::BankFieldOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto opt_field_pkg = AML::parse_pkg(context.aml_data);
|
||||
if (!opt_field_pkg.has_value())
|
||||
return ParseResult::Failure;
|
||||
auto field_pkg = opt_field_pkg.release_value();
|
||||
|
||||
auto op_region_name = NameString::parse(field_pkg);
|
||||
if (!op_region_name.has_value())
|
||||
return ParseResult::Failure;
|
||||
auto op_region = Namespace::root_namespace()->find_object(context.scope, op_region_name.value(), Namespace::FindMode::Normal);
|
||||
if (!op_region || op_region->type != AML::Node::Type::OpRegion)
|
||||
{
|
||||
AML_ERROR("BankField RegionName {} does not name a valid OpRegion", op_region_name.value());
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto bank_selector_name = NameString::parse(field_pkg);
|
||||
if (!bank_selector_name.has_value())
|
||||
return ParseResult::Failure;
|
||||
auto bank_selector = Namespace::root_namespace()->find_object(context.scope, bank_selector_name.value(), Namespace::FindMode::Normal);
|
||||
if (!bank_selector)
|
||||
{
|
||||
AML_ERROR("BankField BankSelector {} does not name a valid object", bank_selector_name.value());
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
if (bank_selector->type != AML::Node::Type::FieldElement)
|
||||
{
|
||||
AML_TODO("BankField BankSelector {} type {2H}", static_cast<uint8_t>(bank_selector->type));
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto temp_aml_data = context.aml_data;
|
||||
context.aml_data = field_pkg;
|
||||
auto bank_value_result = AML::parse_object(context);
|
||||
field_pkg = context.aml_data;
|
||||
context.aml_data = temp_aml_data;
|
||||
if (!bank_value_result.success())
|
||||
return ParseResult::Failure;
|
||||
auto bank_value_node = bank_value_result.node() ? bank_value_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!bank_value_node)
|
||||
{
|
||||
AML_ERROR("BankField BankValue is not an integer");
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
if (field_pkg.size() < 1)
|
||||
return ParseResult::Failure;
|
||||
auto field_flags = field_pkg[0];
|
||||
field_pkg = field_pkg.slice(1);
|
||||
|
||||
ParseFieldElementContext<BankFieldElement> field_context;
|
||||
field_context.field_rules.access_type = static_cast<FieldRules::AccessType>(field_flags & 0x0F);
|
||||
field_context.field_rules.lock_rule = static_cast<FieldRules::LockRule>((field_flags >> 4) & 0x01);
|
||||
field_context.field_rules.update_rule = static_cast<FieldRules::UpdateRule>((field_flags >> 5) & 0x03);
|
||||
field_context.field_bit_offset = 0;
|
||||
field_context.field_pkg = field_pkg;
|
||||
while (field_context.field_pkg.size() > 0)
|
||||
if (!parse_field_element(field_context))
|
||||
return ParseResult::Failure;
|
||||
|
||||
const auto bank_value = static_cast<AML::Integer*>(bank_value_node.ptr())->value;
|
||||
for (auto& [_, element] : field_context.elements)
|
||||
{
|
||||
element->op_region = static_cast<OpRegion*>(op_region.ptr());
|
||||
element->bank_selector = static_cast<FieldElement*>(bank_selector.ptr());
|
||||
element->bank_value = bank_value;
|
||||
|
||||
NameString element_name;
|
||||
MUST(element_name.path.push_back(element->name));
|
||||
if (!Namespace::root_namespace()->add_named_object(context, element_name, element))
|
||||
return ParseResult::Failure;
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
element->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
}
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Integer> AML::BankFieldElement::as_integer()
|
||||
{
|
||||
if (access_rules.access_attrib != FieldRules::AccessAttrib::Normal)
|
||||
{
|
||||
AML_TODO("BankFieldElement with access attribute {}", static_cast<uint8_t>(access_rules.access_attrib));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto access_size = determine_access_size(access_rules.access_type);
|
||||
if (!access_size.has_value())
|
||||
return {};
|
||||
auto read_func = [&](uint64_t byte_offset) -> BAN::Optional<uint64_t> {
|
||||
return perform_read(op_region->region_space, byte_offset, access_size.value());
|
||||
};
|
||||
|
||||
bank_selector->op_region->mutex.lock();
|
||||
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
|
||||
ACPI::acquire_global_lock();
|
||||
BAN::ScopeGuard unlock_guard([&] {
|
||||
bank_selector->op_region->mutex.unlock();
|
||||
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
|
||||
ACPI::release_global_lock();
|
||||
});
|
||||
|
||||
if (!bank_selector->store_internal(bank_value))
|
||||
{
|
||||
AML_ERROR("BankFieldElement failed to store BankValue");
|
||||
return {};
|
||||
}
|
||||
|
||||
auto result = perform_read_general(op_region->region_offset, bit_count, bit_offset, access_size.value(), read_func);
|
||||
if (!result.has_value())
|
||||
return {};
|
||||
return MUST(BAN::RefPtr<Integer>::create(result.value()));
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> AML::BankFieldElement::store(BAN::RefPtr<AML::Node> source)
|
||||
{
|
||||
if (access_rules.access_attrib != FieldRules::AccessAttrib::Normal)
|
||||
{
|
||||
AML_TODO("BankFieldElement with access attribute {}", static_cast<uint8_t>(access_rules.access_attrib));
|
||||
return {};
|
||||
}
|
||||
|
||||
ASSERT(source);
|
||||
auto source_integer = source->convert(AML::Node::ConvInteger);
|
||||
if (!source_integer)
|
||||
{
|
||||
AML_TODO("BankFieldElement store with non-integer source, type {}", static_cast<uint8_t>(source->type));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto access_size = determine_access_size(access_rules.access_type);
|
||||
if (!access_size.has_value())
|
||||
return {};
|
||||
auto read_func = [&](uint64_t byte_offset) -> BAN::Optional<uint64_t> {
|
||||
return perform_read(op_region->region_space, byte_offset, access_size.value());
|
||||
};
|
||||
auto write_func = [&](uint64_t byte_offset, uint64_t value) -> bool {
|
||||
return perform_write(op_region->region_space, byte_offset, access_size.value(), value);
|
||||
};
|
||||
|
||||
bank_selector->op_region->mutex.lock();
|
||||
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
|
||||
ACPI::acquire_global_lock();
|
||||
BAN::ScopeGuard unlock_guard([&] {
|
||||
bank_selector->op_region->mutex.unlock();
|
||||
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
|
||||
ACPI::release_global_lock();
|
||||
});
|
||||
|
||||
if (!bank_selector->store_internal(bank_value))
|
||||
{
|
||||
AML_ERROR("BankFieldElement failed to store BankValue");
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto source_value = static_cast<AML::Integer*>(source_integer.ptr())->value;
|
||||
if (!perform_write_general(op_region->region_offset, bit_count, bit_offset, access_size.value(), source_value, access_rules.update_rule, read_func, write_func))
|
||||
return {};
|
||||
return source_integer;
|
||||
}
|
||||
|
||||
void AML::BankFieldElement::debug_print(int indent) const
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("BankFieldElement {} ({}, offset {}, OpRegion {}, BankSelector {}, BankValue {H})",
|
||||
name,
|
||||
bit_count,
|
||||
bit_offset,
|
||||
op_region->name,
|
||||
bank_selector->name,
|
||||
bank_value
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
#include <kernel/ACPI/AML/Buffer.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/String.h>
|
||||
|
||||
namespace Kernel::ACPI
|
||||
{
|
||||
|
||||
AML::Integer::Integer(uint64_t value, bool constant)
|
||||
: Node(Node::Type::Integer)
|
||||
, value(value)
|
||||
, constant(constant)
|
||||
{}
|
||||
|
||||
BAN::Optional<bool> AML::Integer::logical_compare(BAN::RefPtr<AML::Node> node, AML::Byte binaryop)
|
||||
{
|
||||
auto rhs_node = node ? node->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!rhs_node)
|
||||
{
|
||||
AML_ERROR("Integer logical compare RHS cannot be converted to");
|
||||
return {};
|
||||
}
|
||||
const auto rhs_value = static_cast<AML::Integer*>(rhs_node.ptr())->value;
|
||||
|
||||
switch (binaryop)
|
||||
{
|
||||
case AML::Byte::LAndOp: return value && rhs_value;
|
||||
case AML::Byte::LEqualOp: return value == rhs_value;
|
||||
case AML::Byte::LGreaterOp: return value > rhs_value;
|
||||
case AML::Byte::LLessOp: return value < rhs_value;
|
||||
case AML::Byte::LOrOp: return value || rhs_value;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> AML::Integer::convert(uint8_t mask)
|
||||
{
|
||||
if (mask & AML::Node::ConvInteger)
|
||||
return this;
|
||||
if (mask & AML::Node::ConvBuffer)
|
||||
{
|
||||
auto buffer = MUST(BAN::RefPtr<AML::Buffer>::create());
|
||||
MUST(buffer->buffer.resize(8));
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
buffer->buffer[i] = (value >> (56 - i * 8)) & 0xFF;
|
||||
return buffer;
|
||||
}
|
||||
if (mask & AML::Node::ConvBufferField)
|
||||
{
|
||||
AML_TODO("Convert Integer to BufferField");
|
||||
return {};
|
||||
}
|
||||
if (mask & AML::Node::ConvFieldUnit)
|
||||
{
|
||||
AML_TODO("Convert Integer to FieldUnit");
|
||||
return {};
|
||||
}
|
||||
if (mask & AML::Node::ConvString)
|
||||
{
|
||||
constexpr auto get_hex_char =
|
||||
[](uint8_t nibble)
|
||||
{
|
||||
return (nibble < 10 ? '0' : 'A' - 10) + nibble;
|
||||
};
|
||||
|
||||
auto string = MUST(BAN::RefPtr<AML::String>::create());
|
||||
MUST(string->string.resize(16));
|
||||
for (size_t i = 0; i < 16; i++)
|
||||
string->string[i] = get_hex_char((value >> (60 - i * 4)) & 0xF);
|
||||
return string;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> AML::Integer::copy()
|
||||
{
|
||||
return MUST(BAN::RefPtr<Integer>::create(value));
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> AML::Integer::store(BAN::RefPtr<AML::Node> store_node)
|
||||
{
|
||||
if (constant)
|
||||
{
|
||||
AML_ERROR("Cannot store to constant integer");
|
||||
return {};
|
||||
}
|
||||
auto conv_node = store_node ? store_node->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
|
||||
if (!conv_node)
|
||||
{
|
||||
AML_ERROR("Cannot store non-integer to integer");
|
||||
return {};
|
||||
}
|
||||
value = static_cast<AML::Integer*>(conv_node.ptr())->value;
|
||||
return MUST(BAN::RefPtr<AML::Integer>::create(value));
|
||||
}
|
||||
|
||||
AML::ParseResult AML::Integer::parse(BAN::ConstByteSpan& aml_data)
|
||||
{
|
||||
switch (static_cast<AML::Byte>(aml_data[0]))
|
||||
{
|
||||
case AML::Byte::ZeroOp:
|
||||
aml_data = aml_data.slice(1);
|
||||
// FIXME: no copy
|
||||
return ParseResult(Constants::Zero->copy());
|
||||
case AML::Byte::OneOp:
|
||||
aml_data = aml_data.slice(1);
|
||||
// FIXME: no copy
|
||||
return ParseResult(Constants::One->copy());
|
||||
case AML::Byte::OnesOp:
|
||||
aml_data = aml_data.slice(1);
|
||||
// FIXME: no copy
|
||||
return ParseResult(Constants::Ones->copy());
|
||||
case AML::Byte::BytePrefix:
|
||||
{
|
||||
if (aml_data.size() < 2)
|
||||
return ParseResult::Failure;
|
||||
const uint8_t value = aml_data[1];
|
||||
aml_data = aml_data.slice(2);
|
||||
return ParseResult(MUST(BAN::RefPtr<Integer>::create(value)));
|
||||
}
|
||||
case AML::Byte::WordPrefix:
|
||||
{
|
||||
if (aml_data.size() < 3)
|
||||
return ParseResult::Failure;
|
||||
uint16_t value = 0;
|
||||
value |= aml_data[1] << 0;
|
||||
value |= aml_data[2] << 8;
|
||||
aml_data = aml_data.slice(3);
|
||||
return ParseResult(MUST(BAN::RefPtr<Integer>::create(value)));
|
||||
}
|
||||
case AML::Byte::DWordPrefix:
|
||||
{
|
||||
if (aml_data.size() < 5)
|
||||
return ParseResult::Failure;
|
||||
uint32_t value = 0;
|
||||
value |= static_cast<uint32_t>(aml_data[1]) << 0;
|
||||
value |= static_cast<uint32_t>(aml_data[2]) << 8;
|
||||
value |= static_cast<uint32_t>(aml_data[3]) << 16;
|
||||
value |= static_cast<uint32_t>(aml_data[4]) << 24;
|
||||
aml_data = aml_data.slice(5);
|
||||
return ParseResult(MUST(BAN::RefPtr<Integer>::create(value)));
|
||||
}
|
||||
case AML::Byte::QWordPrefix:
|
||||
{
|
||||
if (aml_data.size() < 9)
|
||||
return ParseResult::Failure;
|
||||
uint64_t value = 0;
|
||||
value |= static_cast<uint64_t>(aml_data[1]) << 0;
|
||||
value |= static_cast<uint64_t>(aml_data[2]) << 8;
|
||||
value |= static_cast<uint64_t>(aml_data[3]) << 16;
|
||||
value |= static_cast<uint64_t>(aml_data[4]) << 24;
|
||||
value |= static_cast<uint64_t>(aml_data[5]) << 32;
|
||||
value |= static_cast<uint64_t>(aml_data[6]) << 40;
|
||||
value |= static_cast<uint64_t>(aml_data[7]) << 48;
|
||||
value |= static_cast<uint64_t>(aml_data[8]) << 56;
|
||||
aml_data = aml_data.slice(9);
|
||||
return ParseResult(MUST(BAN::RefPtr<Integer>::create(value)));
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
void AML::Integer::debug_print(int indent) const
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
if (!constant)
|
||||
AML_DEBUG_PRINT("0x{H}", value);
|
||||
else
|
||||
{
|
||||
AML_DEBUG_PRINT("Const ");
|
||||
if (value == Constants::Zero->value)
|
||||
AML_DEBUG_PRINT("Zero");
|
||||
else if (value == Constants::One->value)
|
||||
AML_DEBUG_PRINT("One");
|
||||
else if (value == Constants::Ones->value)
|
||||
AML_DEBUG_PRINT("Ones");
|
||||
else
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
#include <kernel/ACPI/AML/Buffer.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/NamedObject.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/String.h>
|
||||
|
||||
namespace Kernel::ACPI
|
||||
{
|
||||
|
||||
AML::ParseResult AML::Name::parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::NameOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto name_string = AML::NameString::parse(context.aml_data);
|
||||
if (!name_string.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto object = AML::parse_object(context);
|
||||
if (!object.success())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto name = MUST(BAN::RefPtr<Name>::create(name_string.value().path.back(), object.node()));
|
||||
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), name))
|
||||
return ParseResult::Success;
|
||||
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
name->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
#endif
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
|
||||
void AML::Name::debug_print(int indent) const
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINTLN("Name {} { ", name);
|
||||
object->debug_print(indent + 1);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("}");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,311 +1,438 @@
|
|||
#include <kernel/ACPI/AML/Device.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/Method.h>
|
||||
#include <kernel/ACPI/AML/Mutex.h>
|
||||
#include <kernel/ACPI/AML/Namespace.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Region.h>
|
||||
#include <kernel/ACPI/AML/String.h>
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
#include <BAN/Bitcast.h>
|
||||
|
||||
namespace Kernel::ACPI
|
||||
#include <kernel/ACPI/AML/Namespace.h>
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
#include <kernel/ACPI/Headers.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
static BAN::RefPtr<AML::Namespace> s_root_namespace;
|
||||
static BAN::Vector<uint8_t> s_osi_aml_data;
|
||||
static Namespace s_root_namespace;
|
||||
|
||||
BAN::RefPtr<AML::Integer> AML::Integer::Constants::Zero;
|
||||
BAN::RefPtr<AML::Integer> AML::Integer::Constants::One;
|
||||
BAN::RefPtr<AML::Integer> AML::Integer::Constants::Ones;
|
||||
|
||||
struct DebugNode : AML::Node
|
||||
{
|
||||
DebugNode() : AML::Node(AML::Node::Type::Debug) {}
|
||||
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
|
||||
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> node)
|
||||
{
|
||||
node->debug_print(0);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
return node;
|
||||
}
|
||||
void debug_print(int indent) const override
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("DEBUG");
|
||||
}
|
||||
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,
|
||||
};
|
||||
|
||||
BAN::RefPtr<AML::Node> AML::Namespace::debug_node;
|
||||
|
||||
BAN::RefPtr<AML::Namespace> AML::Namespace::root_namespace()
|
||||
Namespace::~Namespace()
|
||||
{
|
||||
ASSERT(s_root_namespace);
|
||||
return s_root_namespace;
|
||||
for (auto& [_, reference] : m_named_objects)
|
||||
if (--reference->ref_count == 0)
|
||||
delete reference;
|
||||
}
|
||||
|
||||
void AML::Namespace::debug_print(int indent) const
|
||||
BAN::ErrorOr<void> Namespace::initialize_root_namespace()
|
||||
{
|
||||
LockGuard _(m_object_mutex);
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINTLN("Namespace {} {", name);
|
||||
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("}");
|
||||
}
|
||||
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html?highlight=predefined#predefined-root-namespaces
|
||||
|
||||
BAN::Optional<BAN::String> AML::Namespace::resolve_path(const AML::NameString& relative_base, const AML::NameString& relative_path, FindMode mode, bool check_existence) const
|
||||
{
|
||||
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 || mode == FindMode::ForceAbsolute)
|
||||
{
|
||||
BAN::String absolute_path;
|
||||
MUST(absolute_path.push_back('\\'));
|
||||
|
||||
// Resolve root and parent references
|
||||
if (relative_path.prefix == "\\"_sv)
|
||||
;
|
||||
else
|
||||
const auto add_predefined_root_namespace =
|
||||
[](const char* name) -> BAN::ErrorOr<void>
|
||||
{
|
||||
if (relative_path.prefix.size() > relative_base.path.size())
|
||||
{
|
||||
AML_ERROR("Trying to resolve parent of root object");
|
||||
return {};
|
||||
}
|
||||
for (size_t i = 0; i < relative_base.path.size() - relative_path.prefix.size(); i++)
|
||||
{
|
||||
MUST(absolute_path.append(relative_base.path[i].sv()));
|
||||
MUST(absolute_path.push_back('.'));
|
||||
}
|
||||
}
|
||||
Node predefined {};
|
||||
predefined.type = Node::Type::PredefinedScope;
|
||||
TRY(s_root_namespace.add_named_object({}, TRY(NameString::from_string(name)), BAN::move(predefined)));
|
||||
return {};
|
||||
};
|
||||
|
||||
// Append relative path
|
||||
for (const auto& seg : relative_path.path)
|
||||
{
|
||||
MUST(absolute_path.append(seg.sv()));
|
||||
MUST(absolute_path.push_back('.'));
|
||||
}
|
||||
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_"));
|
||||
|
||||
if (absolute_path.back() == '.')
|
||||
absolute_path.pop_back();
|
||||
|
||||
if (!check_existence || absolute_path == "\\"_sv || m_objects.contains(absolute_path))
|
||||
return absolute_path;
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
// Resolve with namespace search rules (ACPI Spec 6.4 - Section 5.3)
|
||||
|
||||
AML::NameSeg target_seg = relative_path.path.back();
|
||||
|
||||
BAN::String last_match_path;
|
||||
BAN::String current_path;
|
||||
MUST(current_path.push_back('\\'));
|
||||
|
||||
// Check root namespace
|
||||
{
|
||||
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);
|
||||
Node revision;
|
||||
revision.type = Node::Type::Integer;
|
||||
revision.as.integer.value = 2;
|
||||
TRY(s_root_namespace.add_named_object({}, TRY(NameString::from_string("_REV")), BAN::move(revision)));
|
||||
}
|
||||
|
||||
// Check base base path
|
||||
for (const auto& seg : relative_base.path)
|
||||
{
|
||||
MUST(current_path.append(seg.sv()));
|
||||
MUST(current_path.push_back('.'));
|
||||
auto osi_string = TRY(NameString::from_string("\\_OSI"));
|
||||
|
||||
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);
|
||||
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<Reference*, 7>& args) -> BAN::ErrorOr<Node>
|
||||
{
|
||||
ASSERT(args[0]);
|
||||
|
||||
if (args[0]->node.type != Node::Type::String)
|
||||
{
|
||||
dwarnln("_OSI called with {}", args[0]->node);
|
||||
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;
|
||||
|
||||
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({}, osi_string, BAN::move(method)));
|
||||
}
|
||||
|
||||
{
|
||||
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({}, gl_string, BAN::move(mutex)));
|
||||
}
|
||||
|
||||
if (!last_match_path.empty())
|
||||
return last_match_path;
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::NamedObject> AML::Namespace::find_object(const AML::NameString& relative_base, const AML::NameString& relative_path, FindMode mode)
|
||||
Namespace& Namespace::root_namespace()
|
||||
{
|
||||
LockGuard _(m_object_mutex);
|
||||
|
||||
auto canonical_path = resolve_path(relative_base, relative_path, mode);
|
||||
if (!canonical_path.has_value())
|
||||
return nullptr;
|
||||
|
||||
if (canonical_path->sv() == "\\"_sv)
|
||||
return this;
|
||||
|
||||
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<NamedObject> object)
|
||||
{
|
||||
LockGuard _(m_object_mutex);
|
||||
|
||||
ASSERT(!object_path.path.empty());
|
||||
|
||||
auto canonical_path = resolve_path(parse_context.scope, object_path, FindMode::ForceAbsolute, false);
|
||||
ASSERT(canonical_path.has_value());
|
||||
ASSERT(!canonical_path->empty());
|
||||
|
||||
if (m_objects.contains(canonical_path.value()))
|
||||
{
|
||||
AML_PRINT("Object '{}' already exists", canonical_path.value());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto canonical_scope = AML::NameString(canonical_path.value());
|
||||
|
||||
MUST(m_objects.insert(canonical_path.value(), object));
|
||||
if (object->is_scope())
|
||||
{
|
||||
auto* scope = static_cast<Scope*>(object.ptr());
|
||||
scope->scope = canonical_scope;
|
||||
}
|
||||
|
||||
MUST(parse_context.created_objects.push_back(canonical_scope));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AML::Namespace::remove_named_object(const AML::NameString& absolute_path)
|
||||
{
|
||||
LockGuard _(m_object_mutex);
|
||||
|
||||
auto canonical_path = resolve_path({}, absolute_path, FindMode::ForceAbsolute);
|
||||
if (!canonical_path.has_value())
|
||||
{
|
||||
AML_ERROR("Trying to delete non-existent object '{}'", absolute_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (canonical_path->empty())
|
||||
{
|
||||
AML_ERROR("Trying to remove root namespace");
|
||||
return false;
|
||||
}
|
||||
|
||||
ASSERT(m_objects.contains(canonical_path.value()));
|
||||
m_objects.remove(canonical_path.value());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Namespace> AML::Namespace::create_root_namespace()
|
||||
{
|
||||
ASSERT(!s_root_namespace);
|
||||
s_root_namespace = MUST(BAN::RefPtr<Namespace>::create(NameSeg("\\"_sv)));
|
||||
s_root_namespace->scope = AML::NameString("\\"_sv);
|
||||
|
||||
ASSERT(!Namespace::debug_node);
|
||||
Namespace::debug_node = MUST(BAN::RefPtr<DebugNode>::create());
|
||||
|
||||
Integer::Constants::Zero = MUST(BAN::RefPtr<Integer>::create(0, true));
|
||||
Integer::Constants::One = MUST(BAN::RefPtr<Integer>::create(1, true));
|
||||
Integer::Constants::Ones = MUST(BAN::RefPtr<Integer>::create(0xFFFFFFFFFFFFFFFF, true));
|
||||
|
||||
AML::ParseContext context;
|
||||
context.scope = AML::NameString("\\"_sv);
|
||||
|
||||
// Add predefined namespaces
|
||||
#define ADD_PREDEFIED_NAMESPACE(NAME) \
|
||||
ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\" NAME), MUST(BAN::RefPtr<AML::Device>::create(NameSeg(NAME)))));
|
||||
ADD_PREDEFIED_NAMESPACE("_GPE"_sv);
|
||||
ADD_PREDEFIED_NAMESPACE("_PR"_sv);
|
||||
ADD_PREDEFIED_NAMESPACE("_SB"_sv);
|
||||
ADD_PREDEFIED_NAMESPACE("_SI"_sv);
|
||||
ADD_PREDEFIED_NAMESPACE("_TZ"_sv);
|
||||
#undef ADD_PREDEFIED_NAMESPACE
|
||||
|
||||
auto gl = MUST(BAN::RefPtr<AML::Mutex>::create(NameSeg("_GL"_sv), 0, true));
|
||||
ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\_GL"), gl));
|
||||
|
||||
// Add \_OSI that returns true for Linux compatibility
|
||||
auto osi = MUST(BAN::RefPtr<AML::Method>::create(NameSeg("_OSI"_sv), 1, false, 0));
|
||||
osi->override_function = [](AML::ParseContext& context) -> BAN::RefPtr<AML::Node> {
|
||||
ASSERT(context.method_args[0]);
|
||||
auto arg = context.method_args[0]->convert(AML::Node::ConvString);
|
||||
if (!arg || arg->type != AML::Node::Type::String)
|
||||
{
|
||||
AML_ERROR("Invalid _OSI argument");
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr BAN::StringView valid_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,
|
||||
};
|
||||
|
||||
auto string = static_cast<AML::String*>(arg.ptr())->string_view();
|
||||
for (auto valid_string : valid_strings)
|
||||
if (string == valid_string)
|
||||
return AML::Integer::Constants::Ones;
|
||||
return AML::Integer::Constants::Zero;
|
||||
};
|
||||
ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\_OSI"), osi));
|
||||
|
||||
auto os_string = MUST(BAN::RefPtr<AML::String>::create("banan-os"_sv));
|
||||
auto os = MUST(BAN::RefPtr<AML::Name>::create("_OS"_sv, os_string));
|
||||
ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\_OS"), os));
|
||||
|
||||
return s_root_namespace;
|
||||
}
|
||||
|
||||
bool AML::Namespace::parse(const SDTHeader& header)
|
||||
BAN::ErrorOr<void> Namespace::initalize_op_regions()
|
||||
{
|
||||
ASSERT(this == s_root_namespace.ptr());
|
||||
m_has_parsed_namespace = true;
|
||||
|
||||
AML::ParseContext context;
|
||||
context.scope = AML::NameString("\\"_sv);
|
||||
context.aml_data = BAN::ConstByteSpan(reinterpret_cast<const uint8_t*>(&header), header.length).slice(sizeof(header));
|
||||
|
||||
while (context.aml_data.size() > 0)
|
||||
for (const auto& [obj_path, obj_ref] : m_named_objects)
|
||||
{
|
||||
auto result = AML::parse_object(context);
|
||||
if (!result.success())
|
||||
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<void> 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<uint32_t>(address_space);
|
||||
if (address_space_u32 >= 32)
|
||||
return {};
|
||||
const uint32_t mask = static_cast<uint32_t>(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
|
||||
{
|
||||
AML_ERROR("Failed to parse object");
|
||||
return false;
|
||||
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<Reference*, 7> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
if (it != m_called_reg_bitmaps.end())
|
||||
it->value |= mask;
|
||||
else
|
||||
TRY(m_called_reg_bitmaps.insert(BAN::move(parent), mask));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<Scope> 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<Scope> Namespace::add_named_object(const Scope& scope, const NameString& name_string, Node&& node)
|
||||
{
|
||||
dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_named_object('{}', '{}', {})", scope, name_string, node);
|
||||
|
||||
auto resolved_path = TRY(resolve_path(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_parsed_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)
|
||||
{
|
||||
dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_named_object('{}', '{}', {})", scope, name_string, reference->node);
|
||||
|
||||
auto resolved_path = TRY(resolve_path(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));
|
||||
return resolved_path;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> 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::FindResult> 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<void> Namespace::for_each_child(const Scope& scope, const BAN::Function<BAN::Iteration(BAN::StringView, Reference*)>& 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<const char*>(&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<void> Namespace::for_each_child(const Scope& scope, const BAN::Function<BAN::Iteration(const Scope&, Reference*)>& 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 {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<Node> Namespace::evaluate(BAN::StringView path)
|
||||
{
|
||||
Scope root_scope;
|
||||
auto name_string = TRY(NameString::from_string(path));
|
||||
auto [object_path, object] = TRY(find_named_object(root_scope, name_string));
|
||||
if (object == nullptr)
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
return evaluate_node(object_path, object->node);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> 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<const SDTHeader>();
|
||||
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<int>(execution_flow));
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Namespace::initialize_devices()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,806 @@
|
|||
#include <kernel/ACPI/AML/Bytes.h>
|
||||
#include <kernel/ACPI/AML/Namespace.h>
|
||||
#include <kernel/ACPI/AML/OpRegion.h>
|
||||
#include <kernel/IO.h>
|
||||
#include <kernel/Memory/PageTable.h>
|
||||
#include <kernel/PCI.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
static BAN::ErrorOr<size_t> parse_pkg_length(BAN::ConstByteSpan& aml_data)
|
||||
{
|
||||
if (aml_data.empty())
|
||||
return BAN::Error::from_errno(ENODATA);
|
||||
|
||||
const uint32_t encoding_length = (aml_data[0] >> 6) + 1;
|
||||
if (aml_data.size() < encoding_length)
|
||||
return BAN::Error::from_errno(ENODATA);
|
||||
|
||||
uint32_t pkg_length = 0;
|
||||
switch (encoding_length)
|
||||
{
|
||||
case 1:
|
||||
pkg_length |= aml_data[0] & 0x3F;
|
||||
break;
|
||||
case 2:
|
||||
pkg_length |= aml_data[0] & 0x0F;
|
||||
pkg_length |= aml_data[1] << 4;
|
||||
break;
|
||||
case 3:
|
||||
pkg_length |= aml_data[0] & 0x0F;
|
||||
pkg_length |= aml_data[1] << 4;
|
||||
pkg_length |= aml_data[2] << 12;
|
||||
break;
|
||||
case 4:
|
||||
pkg_length |= aml_data[0] & 0x0F;
|
||||
pkg_length |= aml_data[1] << 4;
|
||||
pkg_length |= aml_data[2] << 12;
|
||||
pkg_length |= aml_data[3] << 20;
|
||||
break;
|
||||
}
|
||||
|
||||
aml_data = aml_data.slice(encoding_length);
|
||||
return pkg_length;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> parse_opregion_op(ParseContext& context)
|
||||
{
|
||||
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_opregion_op");
|
||||
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::OpRegionOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto region_name = TRY(parse_name_string(context.aml_data));
|
||||
|
||||
if (context.aml_data.empty())
|
||||
return BAN::Error::from_errno(ENODATA);
|
||||
auto region_space = context.aml_data[0];
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
switch (static_cast<GAS::AddressSpaceID>(region_space))
|
||||
{
|
||||
case GAS::AddressSpaceID::SystemMemory:
|
||||
case GAS::AddressSpaceID::SystemIO:
|
||||
case GAS::AddressSpaceID::PCIConfig:
|
||||
case GAS::AddressSpaceID::EmbeddedController:
|
||||
case GAS::AddressSpaceID::SMBus:
|
||||
case GAS::AddressSpaceID::SystemCMOS:
|
||||
case GAS::AddressSpaceID::PCIBarTarget:
|
||||
case GAS::AddressSpaceID::IPMI:
|
||||
case GAS::AddressSpaceID::GeneralPurposeIO:
|
||||
case GAS::AddressSpaceID::GenericSerialBus:
|
||||
case GAS::AddressSpaceID::PlatformCommunicationChannel:
|
||||
break;
|
||||
default:
|
||||
if (region_space < 0x80)
|
||||
{
|
||||
dwarnln("OpRegion invalid region space {2H}", region_space);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
auto region_offset = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
|
||||
auto region_length = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
|
||||
|
||||
Node opregion;
|
||||
opregion.type = Node::Type::OpRegion;
|
||||
opregion.as.opregion.address_space = static_cast<GAS::AddressSpaceID>(region_space);
|
||||
opregion.as.opregion.offset = region_offset.as.integer.value;
|
||||
opregion.as.opregion.length = region_length.as.integer.value;
|
||||
|
||||
TRY(Namespace::root_namespace().add_named_object(context.scope, region_name, BAN::move(opregion)));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
static BAN::ErrorOr<void> parse_field_list(const Scope& scope, BAN::ConstByteSpan field_list_pkg, const F& create_element, uint8_t field_flags)
|
||||
{
|
||||
uint64_t offset = 0;
|
||||
while (!field_list_pkg.empty())
|
||||
{
|
||||
switch (field_list_pkg[0])
|
||||
{
|
||||
case 0x00:
|
||||
field_list_pkg = field_list_pkg.slice(1);
|
||||
offset += TRY(parse_pkg_length(field_list_pkg));
|
||||
break;
|
||||
case 0x01:
|
||||
// FIXME: do something with
|
||||
if (field_list_pkg.size() < 3)
|
||||
return BAN::Error::from_errno(ENODATA);
|
||||
field_flags &= 0xF0;
|
||||
field_flags |= field_list_pkg[1] & 0x0F;
|
||||
field_list_pkg = field_list_pkg.slice(3);
|
||||
break;
|
||||
case 0x02:
|
||||
dwarnln("TODO: connect field");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case 0x03:
|
||||
dwarnln("TODO: extended access field");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
default:
|
||||
{
|
||||
if (field_list_pkg.size() < 4)
|
||||
return BAN::Error::from_errno(ENODATA);
|
||||
if (!is_lead_name_char(field_list_pkg[0]) || !is_name_char(field_list_pkg[1]) || !is_name_char(field_list_pkg[2]) || !is_name_char(field_list_pkg[3]))
|
||||
{
|
||||
dwarnln("Invalid NameSeg {2H}, {2H}, {2H}, {2H}",
|
||||
field_list_pkg[0], field_list_pkg[1], field_list_pkg[2], field_list_pkg[3]
|
||||
);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
const uint32_t name_seg = field_list_pkg.as<const uint32_t>();
|
||||
field_list_pkg = field_list_pkg.slice(4);
|
||||
const auto field_length = TRY(parse_pkg_length(field_list_pkg));
|
||||
|
||||
NameString field_name;
|
||||
field_name.base = 0;
|
||||
TRY(field_name.parts.push_back(name_seg));
|
||||
|
||||
Node field_node = create_element(offset, field_length, field_flags);
|
||||
|
||||
TRY(Namespace::root_namespace().add_named_object(scope, field_name, BAN::move(field_node)));
|
||||
|
||||
offset += field_length;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> parse_field_op(ParseContext& context)
|
||||
{
|
||||
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_field_op");
|
||||
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::FieldOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto field_pkg = TRY(parse_pkg(context.aml_data));
|
||||
auto opregion_name = TRY(parse_name_string(field_pkg));
|
||||
if (field_pkg.empty())
|
||||
return BAN::Error::from_errno(ENODATA);
|
||||
auto default_flags = field_pkg[0];
|
||||
field_pkg = field_pkg.slice(1);
|
||||
|
||||
auto [_, opregion] = TRY(Namespace::root_namespace().find_named_object(context.scope, opregion_name));
|
||||
if (opregion == nullptr)
|
||||
{
|
||||
dwarnln("could not find '{}'.'{}'", context.scope, opregion_name);
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
}
|
||||
if (opregion->node.type != Node::Type::OpRegion)
|
||||
{
|
||||
dwarnln("Field source is {}", opregion->node);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
const auto create_element =
|
||||
[&](uint64_t offset, uint64_t length, uint8_t field_flags) -> Node
|
||||
{
|
||||
Node field_node {};
|
||||
field_node.type = Node::Type::FieldUnit;
|
||||
field_node.as.field_unit.type = FieldUnit::Type::Field;
|
||||
field_node.as.field_unit.as.field.opregion = opregion->node.as.opregion;
|
||||
field_node.as.field_unit.length = length;
|
||||
field_node.as.field_unit.offset = offset;
|
||||
field_node.as.field_unit.flags = field_flags;
|
||||
return field_node;
|
||||
};
|
||||
|
||||
TRY(parse_field_list(context.scope, field_pkg, create_element, default_flags));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> parse_index_field_op(ParseContext& context)
|
||||
{
|
||||
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_index_field_op");
|
||||
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::IndexFieldOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto field_pkg = TRY(parse_pkg(context.aml_data));
|
||||
|
||||
auto index_name = TRY(parse_name_string(field_pkg));
|
||||
auto data_name = TRY(parse_name_string(field_pkg));
|
||||
if (field_pkg.empty())
|
||||
return BAN::Error::from_errno(ENODATA);
|
||||
auto default_flags = field_pkg[0];
|
||||
field_pkg = field_pkg.slice(1);
|
||||
|
||||
auto [_1, index_obj] = TRY(Namespace::root_namespace().find_named_object(context.scope, index_name));
|
||||
if (index_obj == nullptr)
|
||||
{
|
||||
dwarnln("could not find '{}'.'{}'", context.scope, index_name);
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
}
|
||||
if (index_obj->node.type != Node::Type::FieldUnit)
|
||||
{
|
||||
dwarnln("IndexField source is {}", index_obj->node);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
auto [_2, data_obj] = TRY(Namespace::root_namespace().find_named_object(context.scope, data_name));
|
||||
if (data_obj == nullptr)
|
||||
{
|
||||
dwarnln("could not find '{}'.'{}'", context.scope, data_name);
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
}
|
||||
if (data_obj->node.type != Node::Type::FieldUnit)
|
||||
{
|
||||
dwarnln("IndexField source is {}", data_obj->node);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
const auto create_element =
|
||||
[&](uint64_t offset, uint64_t length, uint8_t field_flags) -> Node
|
||||
{
|
||||
Node field_node {};
|
||||
field_node.type = Node::Type::FieldUnit;
|
||||
field_node.as.field_unit.type = FieldUnit::Type::IndexField;
|
||||
field_node.as.field_unit.as.index_field.index = index_obj;
|
||||
field_node.as.field_unit.as.index_field.index->ref_count++;
|
||||
field_node.as.field_unit.as.index_field.data = data_obj;
|
||||
field_node.as.field_unit.as.index_field.data->ref_count++;
|
||||
field_node.as.field_unit.length = length;
|
||||
field_node.as.field_unit.offset = offset;
|
||||
field_node.as.field_unit.flags = field_flags;
|
||||
return field_node;
|
||||
};
|
||||
|
||||
TRY(parse_field_list(context.scope, field_pkg, create_element, default_flags));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> parse_bank_field_op(ParseContext& context)
|
||||
{
|
||||
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_bank_field_op");
|
||||
|
||||
ASSERT(context.aml_data.size() >= 2);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::BankFieldOp);
|
||||
context.aml_data = context.aml_data.slice(2);
|
||||
|
||||
auto field_pkg = TRY(parse_pkg(context.aml_data));
|
||||
auto opregion_name = TRY(parse_name_string(field_pkg));
|
||||
auto bank_selector_name = TRY(parse_name_string(field_pkg));
|
||||
|
||||
auto temp_aml_data = context.aml_data;
|
||||
context.aml_data = field_pkg;
|
||||
auto bank_value_node = TRY(parse_node(context));
|
||||
field_pkg = context.aml_data;
|
||||
context.aml_data = temp_aml_data;
|
||||
|
||||
if (field_pkg.empty())
|
||||
return BAN::Error::from_errno(ENODATA);
|
||||
auto default_flags = field_pkg[0];
|
||||
field_pkg = field_pkg.slice(1);
|
||||
|
||||
auto [_1, opregion] = TRY(Namespace::root_namespace().find_named_object(context.scope, opregion_name));
|
||||
if (opregion == nullptr)
|
||||
{
|
||||
dwarnln("could not find '{}'.'{}'", context.scope, opregion_name);
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
}
|
||||
if (opregion->node.type != Node::Type::OpRegion)
|
||||
{
|
||||
dwarnln("Field source is {}", opregion->node);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
auto [_2, bank_selector] = TRY(Namespace::root_namespace().find_named_object(context.scope, bank_selector_name));
|
||||
if (bank_selector == nullptr)
|
||||
{
|
||||
dwarnln("could not find '{}'.'{}'", context.scope, bank_selector_name);
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
}
|
||||
if (bank_selector->node.type != Node::Type::FieldUnit)
|
||||
{
|
||||
dwarnln("BankField bank selector is {}", bank_selector->node);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
const uint64_t bank_value = TRY(convert_node(BAN::move(bank_value_node), ConvInteger, sizeof(uint64_t))).as.integer.value;
|
||||
|
||||
const auto create_element =
|
||||
[&](uint64_t offset, uint64_t length, uint8_t field_flags) -> Node
|
||||
{
|
||||
Node field_node {};
|
||||
field_node.type = Node::Type::FieldUnit;
|
||||
field_node.as.field_unit.type = FieldUnit::Type::BankField;
|
||||
field_node.as.field_unit.as.bank_field.opregion = opregion->node.as.opregion;
|
||||
field_node.as.field_unit.as.bank_field.bank_selector = bank_selector;
|
||||
field_node.as.field_unit.as.bank_field.bank_selector->ref_count++;
|
||||
field_node.as.field_unit.as.bank_field.bank_value = bank_value;
|
||||
field_node.as.field_unit.length = length;
|
||||
field_node.as.field_unit.offset = offset;
|
||||
field_node.as.field_unit.flags = field_flags;
|
||||
return field_node;
|
||||
};
|
||||
|
||||
TRY(parse_field_list(context.scope, field_pkg, create_element, default_flags));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
struct AccessRule
|
||||
{
|
||||
uint8_t access_bits;
|
||||
bool lock;
|
||||
enum {
|
||||
Preserve,
|
||||
WriteZeros,
|
||||
WriteOnes,
|
||||
} update_rule;
|
||||
};
|
||||
|
||||
static AccessRule parse_access_rule(uint8_t flags)
|
||||
{
|
||||
AccessRule rule;
|
||||
|
||||
switch (flags & 0x0F)
|
||||
{
|
||||
case 0: rule.access_bits = 8; break;
|
||||
case 1: rule.access_bits = 8; break;
|
||||
case 2: rule.access_bits = 16; break;
|
||||
case 3: rule.access_bits = 32; break;
|
||||
case 4: rule.access_bits = 64; break;
|
||||
case 5: rule.access_bits = 8; break;
|
||||
default: ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
rule.lock = !!(flags & 0x10);
|
||||
|
||||
switch ((flags >> 5) & 0x03)
|
||||
{
|
||||
case 0: rule.update_rule = AccessRule::Preserve; break;
|
||||
case 1: rule.update_rule = AccessRule::WriteOnes; break;
|
||||
case 2: rule.update_rule = AccessRule::WriteZeros; break;
|
||||
default: ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<uint64_t> perform_opregion_read(const OpRegion& opregion, uint8_t access_size, uint64_t offset)
|
||||
{
|
||||
ASSERT(offset % access_size == 0);
|
||||
|
||||
const uint64_t byte_offset = opregion.offset + offset;
|
||||
|
||||
switch (opregion.address_space)
|
||||
{
|
||||
case GAS::AddressSpaceID::SystemMemory:
|
||||
{
|
||||
uint64_t result;
|
||||
PageTable::with_fast_page(byte_offset & PAGE_ADDR_MASK, [&]() {
|
||||
void* addr = PageTable::fast_page_as_ptr(byte_offset % PAGE_SIZE);
|
||||
switch (access_size) {
|
||||
case 1: result = *static_cast<uint8_t* >(addr); break;
|
||||
case 2: result = *static_cast<uint16_t*>(addr); break;
|
||||
case 4: result = *static_cast<uint32_t*>(addr); break;
|
||||
case 8: result = *static_cast<uint64_t*>(addr); break;
|
||||
default: ASSERT_NOT_REACHED();
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
case GAS::AddressSpaceID::SystemIO:
|
||||
if (byte_offset + access_size > 0x10000)
|
||||
{
|
||||
dwarnln("{} byte read from IO port 0x{H}", access_size, byte_offset);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: return IO::inb(byte_offset);
|
||||
case 2: return IO::inw(byte_offset);
|
||||
case 4: return IO::inl(byte_offset);
|
||||
default:
|
||||
dwarnln("{} byte read from IO port", access_size);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
case GAS::AddressSpaceID::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
|
||||
|
||||
const uint16_t device = (byte_offset >> 32) & 0xFFFF;
|
||||
const uint16_t function = (byte_offset >> 16) & 0xFFFF;
|
||||
const uint16_t offset = byte_offset & 0xFFFF;
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: return PCI::PCIManager::get().read_config_byte (0, device, function, offset);
|
||||
case 2: return PCI::PCIManager::get().read_config_word (0, device, function, offset);
|
||||
case 4: return PCI::PCIManager::get().read_config_dword(0, device, function, offset);
|
||||
default:
|
||||
dwarnln("{} byte read from PCI {2H}:{2H}:{2H}", device, function, offset);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
case GAS::AddressSpaceID::EmbeddedController:
|
||||
case GAS::AddressSpaceID::SMBus:
|
||||
case GAS::AddressSpaceID::SystemCMOS:
|
||||
case GAS::AddressSpaceID::PCIBarTarget:
|
||||
case GAS::AddressSpaceID::IPMI:
|
||||
case GAS::AddressSpaceID::GeneralPurposeIO:
|
||||
case GAS::AddressSpaceID::GenericSerialBus:
|
||||
case GAS::AddressSpaceID::PlatformCommunicationChannel:
|
||||
dwarnln("TODO: Read from address space {}", static_cast<uint8_t>(opregion.address_space));
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<void> perform_opregion_write(const OpRegion& opregion, uint8_t access_size, uint64_t offset, uint64_t value)
|
||||
{
|
||||
ASSERT(offset % access_size == 0);
|
||||
|
||||
const uint64_t byte_offset = opregion.offset + offset;
|
||||
|
||||
switch (opregion.address_space)
|
||||
{
|
||||
case GAS::AddressSpaceID::SystemMemory:
|
||||
PageTable::with_fast_page(byte_offset & PAGE_ADDR_MASK, [&]() {
|
||||
void* addr = PageTable::fast_page_as_ptr(byte_offset % PAGE_SIZE);
|
||||
switch (access_size) {
|
||||
case 1: *static_cast<uint8_t* >(addr) = value; break;
|
||||
case 2: *static_cast<uint16_t*>(addr) = value; break;
|
||||
case 4: *static_cast<uint32_t*>(addr) = value; break;
|
||||
case 8: *static_cast<uint64_t*>(addr) = value; break;
|
||||
default: ASSERT_NOT_REACHED();
|
||||
}
|
||||
});
|
||||
return {};
|
||||
case GAS::AddressSpaceID::SystemIO:
|
||||
if (byte_offset + access_size > 0x10000)
|
||||
{
|
||||
dwarnln("{} byte write to IO port 0x{H}", access_size, byte_offset);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: IO::outb(byte_offset, value); break;
|
||||
case 2: IO::outw(byte_offset, value); break;
|
||||
case 4: IO::outl(byte_offset, value); break;
|
||||
default:
|
||||
dwarnln("{} byte write to IO port", access_size);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
return {};
|
||||
case GAS::AddressSpaceID::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
|
||||
|
||||
const uint16_t device = (byte_offset >> 32) & 0xFFFF;
|
||||
const uint16_t function = (byte_offset >> 16) & 0xFFFF;
|
||||
const uint16_t offset = byte_offset & 0xFFFF;
|
||||
switch (access_size)
|
||||
{
|
||||
case 1: PCI::PCIManager::get().write_config_byte (0, device, function, offset, value); break;
|
||||
case 2: PCI::PCIManager::get().write_config_word (0, device, function, offset, value); break;
|
||||
case 4: PCI::PCIManager::get().write_config_dword(0, device, function, offset, value); break;
|
||||
default:
|
||||
dwarnln("{} byte write to PCI {2H}:{2H}:{2H}", device, function, offset);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case GAS::AddressSpaceID::EmbeddedController:
|
||||
case GAS::AddressSpaceID::SMBus:
|
||||
case GAS::AddressSpaceID::SystemCMOS:
|
||||
case GAS::AddressSpaceID::PCIBarTarget:
|
||||
case GAS::AddressSpaceID::IPMI:
|
||||
case GAS::AddressSpaceID::GeneralPurposeIO:
|
||||
case GAS::AddressSpaceID::GenericSerialBus:
|
||||
case GAS::AddressSpaceID::PlatformCommunicationChannel:
|
||||
dwarnln("TODO: Write to address space {}", static_cast<uint8_t>(opregion.address_space));
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
struct BufferInfo
|
||||
{
|
||||
const uint8_t* buffer;
|
||||
const uint64_t bytes;
|
||||
};
|
||||
|
||||
static BAN::ErrorOr<BufferInfo> extract_buffer_info(const Node& source)
|
||||
{
|
||||
switch (source.type)
|
||||
{
|
||||
case Node::Type::String:
|
||||
case Node::Type::Buffer:
|
||||
return BufferInfo {
|
||||
.buffer = source.as.str_buf->bytes,
|
||||
.bytes = source.as.str_buf->size,
|
||||
};
|
||||
case Node::Type::Integer:
|
||||
return BufferInfo {
|
||||
.buffer = reinterpret_cast<const uint8_t*>(&source.as.integer.value),
|
||||
.bytes = sizeof(uint64_t),
|
||||
};
|
||||
default:
|
||||
dwarnln("Invalid store of {} to FieldUnit", source);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<Node> allocate_destination(const Node& source, Node::Type type, size_t max_bytes)
|
||||
{
|
||||
ASSERT(source.type == Node::Type::FieldUnit);
|
||||
|
||||
const uint64_t source_bit_length = source.as.field_unit.length;
|
||||
|
||||
Node result;
|
||||
result.type = type;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Node::Type::Buffer:
|
||||
{
|
||||
const size_t dst_bits = BAN::Math::max<size_t>(max_bytes * 8, source_bit_length);
|
||||
const size_t bytes = BAN::Math::div_round_up<size_t>(dst_bits, 8);
|
||||
|
||||
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + bytes));
|
||||
if (result.as.str_buf == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
result.as.str_buf->size = bytes;
|
||||
result.as.str_buf->ref_count = 1;
|
||||
memset(result.as.str_buf->bytes, 0, bytes);
|
||||
|
||||
break;
|
||||
}
|
||||
case Node::Type::Integer:
|
||||
if (source_bit_length > 64)
|
||||
{
|
||||
dwarnln("Convert field unit of {} bits to an integer", source_bit_length);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
result.as.integer.value = 0;
|
||||
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void write_bits_to_buffer(uint8_t* buffer, size_t bit_offset, uint64_t value, size_t bit_count)
|
||||
{
|
||||
size_t bits_done = 0;
|
||||
while (bits_done < bit_count) {
|
||||
const size_t acc_bit_offset = (bit_offset + bits_done) % 8;
|
||||
const size_t acc_size = BAN::Math::min<size_t>(bit_count - bits_done, 8 - acc_bit_offset);
|
||||
const size_t mask = (1 << acc_size) - 1;
|
||||
|
||||
buffer[(bit_offset + bits_done) / 8] |= ((value >> bits_done) & mask) << acc_bit_offset;
|
||||
|
||||
bits_done += acc_size;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t read_bits_from_buffer(const uint8_t* buffer, size_t bit_offset, size_t bit_count)
|
||||
{
|
||||
uint64_t result = 0;
|
||||
|
||||
size_t bits_done = 0;
|
||||
while (bits_done < bit_count) {
|
||||
const size_t acc_bit_offset = (bit_offset + bits_done) % 8;
|
||||
const size_t acc_size = BAN::Math::min<size_t>(bit_count - bits_done, 8 - acc_bit_offset);
|
||||
const size_t mask = (1 << acc_size) - 1;
|
||||
|
||||
result |= static_cast<uint64_t>((buffer[(bit_offset + bits_done) / 8] >> acc_bit_offset) & mask) << bits_done;
|
||||
|
||||
bits_done += acc_size;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<uint64_t> perform_index_field_read(const Node& source, uint64_t acc_byte_offset)
|
||||
{
|
||||
ASSERT(source.type == Node::Type::FieldUnit);
|
||||
ASSERT(source.as.field_unit.type == FieldUnit::Type::IndexField);
|
||||
|
||||
Node index_node;
|
||||
index_node.type = Node::Type::Integer;
|
||||
index_node.as.integer.value = acc_byte_offset;
|
||||
TRY(store_to_field_unit(index_node, source.as.field_unit.as.index_field.index->node));
|
||||
|
||||
auto value = TRY(convert_from_field_unit(source.as.field_unit.as.index_field.data->node, ConvInteger, sizeof(uint64_t)));
|
||||
return value.as.integer.value;
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<void> perform_index_field_write(const Node& source, uint64_t acc_byte_offset, uint64_t value)
|
||||
{
|
||||
ASSERT(source.type == Node::Type::FieldUnit);
|
||||
ASSERT(source.as.field_unit.type == FieldUnit::Type::IndexField);
|
||||
|
||||
Node index_node;
|
||||
index_node.type = Node::Type::Integer;
|
||||
index_node.as.integer.value = acc_byte_offset;
|
||||
TRY(store_to_field_unit(index_node, source.as.field_unit.as.index_field.index->node));
|
||||
|
||||
Node data_node;
|
||||
data_node.type = Node::Type::Integer;
|
||||
data_node.as.integer.value = value;
|
||||
TRY(store_to_field_unit(data_node, source.as.field_unit.as.index_field.data->node));
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<Node> convert_from_field_unit(const Node& source, uint8_t conversion, size_t max_length)
|
||||
{
|
||||
ASSERT(source.type == Node::Type::FieldUnit);
|
||||
|
||||
const bool can_be_integer = source.as.field_unit.length <= 64;
|
||||
|
||||
auto target_type = Node::Type::Uninitialized;
|
||||
if (can_be_integer && (conversion & Conversion::ConvInteger))
|
||||
target_type = Node::Type::Integer;
|
||||
else if (conversion & Conversion::ConvBuffer)
|
||||
target_type = Node::Type::Buffer;
|
||||
|
||||
if (target_type == Node::Type::Uninitialized)
|
||||
{
|
||||
dwarnln("Invalid conversion from field unit to 0b{3b}", conversion);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (source.as.field_unit.type == FieldUnit::Type::BankField)
|
||||
{
|
||||
Node bank_node;
|
||||
bank_node.type = Node::Type::Integer;
|
||||
bank_node.as.integer.value = source.as.field_unit.as.bank_field.bank_value;
|
||||
TRY(store_to_field_unit(bank_node, source.as.field_unit.as.bank_field.bank_selector->node));
|
||||
}
|
||||
|
||||
Node result = TRY(allocate_destination(source, target_type, max_length));
|
||||
const auto [dst_buf, dst_bytes] = TRY(extract_buffer_info(result));
|
||||
const auto [max_acc_bits, lock, _] = parse_access_rule(source.as.field_unit.flags);
|
||||
|
||||
const size_t transfer_bits = BAN::Math::min<size_t>(source.as.field_unit.length, dst_bytes * 8);
|
||||
|
||||
size_t bits_done = 0;
|
||||
while (bits_done < transfer_bits)
|
||||
{
|
||||
const size_t acc_bit_offset = (source.as.field_unit.offset + bits_done) & (max_acc_bits - 1);
|
||||
const size_t acc_bit_count = max_acc_bits - acc_bit_offset;
|
||||
const size_t acc_byte_offset = ((source.as.field_unit.offset + bits_done) & ~(max_acc_bits - 1)) / 8;
|
||||
|
||||
uint64_t value = 0;
|
||||
switch (source.as.field_unit.type)
|
||||
{
|
||||
case FieldUnit::Type::Field:
|
||||
value = TRY(perform_opregion_read(source.as.field_unit.as.field.opregion, max_acc_bits / 8, acc_byte_offset));
|
||||
break;
|
||||
case FieldUnit::Type::IndexField:
|
||||
value = TRY(perform_index_field_read(source, acc_byte_offset));
|
||||
break;
|
||||
case FieldUnit::Type::BankField:
|
||||
value = TRY(perform_opregion_read(source.as.field_unit.as.bank_field.opregion, max_acc_bits / 8, acc_byte_offset));
|
||||
break;
|
||||
}
|
||||
|
||||
write_bits_to_buffer(const_cast<uint8_t*>(dst_buf), bits_done, value >> acc_bit_offset, acc_bit_count);
|
||||
|
||||
bits_done += acc_bit_count;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> store_to_field_unit(const Node& source, const Node& target)
|
||||
{
|
||||
ASSERT(target.type == Node::Type::FieldUnit);
|
||||
|
||||
switch (source.type)
|
||||
{
|
||||
case Node::Type::Integer:
|
||||
case Node::Type::Buffer:
|
||||
case Node::Type::String:
|
||||
break;
|
||||
default:
|
||||
return store_to_field_unit(
|
||||
TRY(convert_node(TRY(source.copy()), ConvInteger | ConvBuffer | ConvString, 0xFFFFFFFFFFFFFFFF)),
|
||||
target
|
||||
);
|
||||
}
|
||||
|
||||
if (target.as.field_unit.type == FieldUnit::Type::BankField)
|
||||
{
|
||||
Node bank_node;
|
||||
bank_node.type = Node::Type::Integer;
|
||||
bank_node.as.integer.value = target.as.field_unit.as.bank_field.bank_value;
|
||||
TRY(store_to_field_unit(bank_node, target.as.field_unit.as.bank_field.bank_selector->node));
|
||||
}
|
||||
|
||||
const auto [src_buf, src_bytes] = TRY(extract_buffer_info(source));
|
||||
const auto [max_acc_bits, lock, update_rule] = parse_access_rule(target.as.field_unit.flags);
|
||||
|
||||
const size_t transfer_bits = BAN::Math::min<size_t>(target.as.field_unit.length, src_bytes * 8);
|
||||
|
||||
size_t bits_done = 0;
|
||||
while (bits_done < transfer_bits)
|
||||
{
|
||||
const size_t acc_bit_offset = (target.as.field_unit.offset + bits_done) & (max_acc_bits - 1);
|
||||
const size_t acc_bit_count = BAN::Math::min<size_t>(max_acc_bits - acc_bit_offset, transfer_bits - bits_done);
|
||||
const size_t acc_byte_offset = ((target.as.field_unit.offset + bits_done) & ~(max_acc_bits - 1)) / 8;
|
||||
|
||||
uint64_t value;
|
||||
switch (update_rule)
|
||||
{
|
||||
case AccessRule::Preserve:
|
||||
switch (target.as.field_unit.type)
|
||||
{
|
||||
case FieldUnit::Type::Field:
|
||||
value = TRY(perform_opregion_read(target.as.field_unit.as.field.opregion, max_acc_bits / 8, acc_byte_offset));
|
||||
break;
|
||||
case FieldUnit::Type::IndexField:
|
||||
value = TRY(perform_index_field_read(target, acc_byte_offset));
|
||||
break;
|
||||
case FieldUnit::Type::BankField:
|
||||
value = TRY(perform_opregion_read(target.as.field_unit.as.bank_field.opregion, max_acc_bits / 8, acc_byte_offset));
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
break;
|
||||
case AccessRule::WriteZeros:
|
||||
value = 0x0000000000000000;
|
||||
break;
|
||||
case AccessRule::WriteOnes:
|
||||
value = 0xFFFFFFFFFFFFFFFF;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
value &= ~(((static_cast<uint64_t>(1) << acc_bit_count) - 1) << acc_bit_offset);
|
||||
value |= read_bits_from_buffer(src_buf, bits_done, acc_bit_count) << acc_bit_offset;
|
||||
|
||||
switch (target.as.field_unit.type)
|
||||
{
|
||||
case FieldUnit::Type::Field:
|
||||
TRY(perform_opregion_write(target.as.field_unit.as.field.opregion, max_acc_bits / 8, acc_byte_offset, value));
|
||||
break;
|
||||
case FieldUnit::Type::IndexField:
|
||||
TRY(perform_index_field_write(target, acc_byte_offset, value));
|
||||
break;
|
||||
case FieldUnit::Type::BankField:
|
||||
TRY(perform_opregion_write(target.as.field_unit.as.bank_field.opregion, max_acc_bits / 8, acc_byte_offset, value));
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
bits_done += acc_bit_count;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
#include <kernel/ACPI/AML/Package.h>
|
||||
|
||||
namespace Kernel::ACPI
|
||||
{
|
||||
|
||||
AML::ParseResult AML::Package::parse(AML::ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::PackageOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto package_pkg = AML::parse_pkg(context.aml_data);
|
||||
if (!package_pkg.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto package_context = context;
|
||||
package_context.aml_data = package_pkg.value();
|
||||
|
||||
if (package_pkg->size() < 1)
|
||||
return ParseResult::Failure;
|
||||
uint8_t num_elements = package_context.aml_data[0];
|
||||
package_context.aml_data = package_context.aml_data.slice(1);
|
||||
|
||||
auto package = MUST(BAN::RefPtr<Package>::create(context.scope));
|
||||
while (package->elements.size() < num_elements && package_context.aml_data.size() > 0)
|
||||
{
|
||||
auto element_result = PackageElement::parse(package_context, package);
|
||||
if (!element_result.success())
|
||||
return ParseResult::Failure;
|
||||
ASSERT(element_result.node() && element_result.node()->type == Node::Type::PackageElement);
|
||||
auto element = static_cast<PackageElement*>(element_result.node().ptr());
|
||||
MUST(package->elements.push_back(element));
|
||||
}
|
||||
while (package->elements.size() < num_elements)
|
||||
{
|
||||
auto uninitialized = MUST(BAN::RefPtr<PackageElement>::create(package));
|
||||
MUST(package->elements.push_back(uninitialized));
|
||||
}
|
||||
|
||||
return ParseResult(package);
|
||||
}
|
||||
|
||||
void AML::Package::debug_print(int indent) const
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("Package {");
|
||||
AML_DEBUG_PRINTLN("");
|
||||
for (const auto& element : elements)
|
||||
{
|
||||
element->debug_print(indent + 1);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
}
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT("}");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
#include <kernel/ACPI/AML/Reference.h>
|
||||
#include <kernel/ACPI/AML/Register.h>
|
||||
|
||||
namespace Kernel::ACPI
|
||||
{
|
||||
|
||||
AML::Register::Register()
|
||||
: Node(Node::Type::Register)
|
||||
{}
|
||||
AML::Register::Register(BAN::RefPtr<AML::Node> node)
|
||||
: Node(Node::Type::Register)
|
||||
{
|
||||
if (!node)
|
||||
{
|
||||
value = node;
|
||||
return;
|
||||
}
|
||||
|
||||
while (node)
|
||||
{
|
||||
if (node->type == AML::Node::Type::Reference)
|
||||
node = static_cast<AML::Reference*>(node.ptr())->node;
|
||||
else if (node->type != AML::Node::Type::Buffer && node->type != AML::Node::Type::Package)
|
||||
node = node->copy();
|
||||
if (node->type == AML::Node::Type::Register)
|
||||
{
|
||||
node = static_cast<AML::Register*>(node.ptr())->value;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ASSERT(node);
|
||||
value = node;
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> AML::Register::convert(uint8_t mask)
|
||||
{
|
||||
if (!value)
|
||||
{
|
||||
AML_ERROR("Trying to convert null Register");
|
||||
return {};
|
||||
}
|
||||
return value->convert(mask);
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> AML::Register::store(BAN::RefPtr<AML::Node> source)
|
||||
{
|
||||
if (source && source->type == AML::Node::Type::Register)
|
||||
source = static_cast<AML::Register*>(source.ptr())->value;
|
||||
if (value && value->type == AML::Node::Type::Reference)
|
||||
return value->store(source);
|
||||
value = source->copy();
|
||||
return value;
|
||||
}
|
||||
|
||||
void AML::Register::debug_print(int indent) const
|
||||
{
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
if (!value)
|
||||
AML_DEBUG_PRINT("Register { No value }");
|
||||
else
|
||||
{
|
||||
AML_DEBUG_PRINTLN("Register { ");
|
||||
value->debug_print(indent + 1);
|
||||
AML_DEBUG_PRINTLN("");
|
||||
AML_DEBUG_PRINT_INDENT(indent);
|
||||
AML_DEBUG_PRINT(" }");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,205 +1,65 @@
|
|||
#include <kernel/ACPI/AML/Device.h>
|
||||
#include <kernel/ACPI/AML/Integer.h>
|
||||
#include <kernel/ACPI/AML/ParseContext.h>
|
||||
#include <kernel/ACPI/AML/Pkg.h>
|
||||
#include <kernel/ACPI/AML/Region.h>
|
||||
#include <kernel/ACPI/AML/Namespace.h>
|
||||
#include <kernel/ACPI/AML/Scope.h>
|
||||
|
||||
namespace Kernel::ACPI
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
AML::ParseResult AML::Scope::parse(ParseContext& context)
|
||||
BAN::ErrorOr<void> initialize_scope(const Scope& scope)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ScopeOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto scope_pkg = AML::parse_pkg(context.aml_data);
|
||||
if (!scope_pkg.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto name_string = AML::NameString::parse(scope_pkg.value());
|
||||
if (!name_string.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
auto named_object = Namespace::root_namespace()->find_object(context.scope, name_string.value(), Namespace::FindMode::Normal);
|
||||
if (!named_object)
|
||||
{
|
||||
AML_PRINT("Scope '{}' not found in namespace", name_string.value());
|
||||
return ParseResult::Success;
|
||||
}
|
||||
if (!named_object->is_scope())
|
||||
{
|
||||
AML_ERROR("Scope '{}' does not name a namespace", name_string.value());
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
auto* scope = static_cast<Scope*>(named_object.ptr());
|
||||
return scope->enter_context_and_parse_term_list(context, name_string.value(), scope_pkg.value());
|
||||
}
|
||||
|
||||
AML::ParseResult AML::Scope::enter_context_and_parse_term_list(ParseContext& outer_context, const AML::NameString& name_string, BAN::ConstByteSpan aml_data)
|
||||
{
|
||||
auto resolved_scope = Namespace::root_namespace()->resolve_path(outer_context.scope, name_string, Namespace::FindMode::Normal);
|
||||
if (!resolved_scope.has_value())
|
||||
return ParseResult::Failure;
|
||||
|
||||
ParseContext scope_context;
|
||||
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)
|
||||
{
|
||||
auto object_result = AML::parse_object(scope_context);
|
||||
if (object_result.returned())
|
||||
{
|
||||
AML_ERROR("Unexpected return from scope {}", scope_context.scope);
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
if (!object_result.success())
|
||||
return ParseResult::Failure;
|
||||
}
|
||||
|
||||
for (auto& name : scope_context.created_objects)
|
||||
MUST(outer_context.created_objects.push_back(BAN::move(name)));
|
||||
|
||||
return ParseResult::Success;
|
||||
}
|
||||
|
||||
static BAN::RefPtr<AML::Integer> evaluate_or_invoke(BAN::RefPtr<AML::Node> object)
|
||||
{
|
||||
if (object->type != AML::Node::Type::Method)
|
||||
{
|
||||
auto converted = object->convert(AML::Node::ConvInteger);
|
||||
if (!converted)
|
||||
return {};
|
||||
return static_cast<AML::Integer*>(converted.ptr());
|
||||
}
|
||||
|
||||
auto* method = static_cast<AML::Method*>(object.ptr());
|
||||
if (method->arg_count != 0)
|
||||
{
|
||||
AML_ERROR("Method has {} arguments, expected 0", method->arg_count);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto result = method->invoke();
|
||||
if (!result.has_value())
|
||||
{
|
||||
AML_ERROR("Failed to evaluate method");
|
||||
return {};
|
||||
}
|
||||
|
||||
auto result_integer = result.value()
|
||||
? result.value()->convert(AML::Node::ConvInteger)
|
||||
: BAN::RefPtr<AML::Node>();
|
||||
if (!result_integer)
|
||||
return {};
|
||||
return static_cast<AML::Integer*>(result_integer.ptr());
|
||||
}
|
||||
|
||||
bool AML::initialize_scope(BAN::RefPtr<AML::Scope> scope)
|
||||
{
|
||||
#if AML_DEBUG_LEVEL >= 2
|
||||
AML_DEBUG_PRINTLN("Initializing {}", scope->scope);
|
||||
#endif
|
||||
|
||||
if (auto reg = Namespace::root_namespace()->find_object(scope->scope, AML::NameString("_REG"_sv), Namespace::FindMode::ForceAbsolute))
|
||||
{
|
||||
bool embedded_controller = false;
|
||||
Namespace::for_each_child(scope->scope,
|
||||
[&](const auto&, auto& child)
|
||||
{
|
||||
if (child->type != AML::Node::Type::OpRegion)
|
||||
return;
|
||||
auto* region = static_cast<AML::OpRegion*>(child.ptr());
|
||||
if (region->region_space == AML::OpRegion::RegionSpace::EmbeddedController)
|
||||
embedded_controller = true;
|
||||
}
|
||||
);
|
||||
if (embedded_controller)
|
||||
{
|
||||
if (reg->type != AML::Node::Type::Method)
|
||||
{
|
||||
AML_ERROR("Object {}._REG is not a method", scope->scope);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* method = static_cast<Method*>(reg.ptr());
|
||||
if (method->arg_count != 2)
|
||||
{
|
||||
AML_ERROR("Method {}._REG has {} arguments, expected 2", scope->scope, method->arg_count);
|
||||
return false;
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> embedded_controller = MUST(BAN::RefPtr<AML::Integer>::create(static_cast<uint64_t>(AML::OpRegion::RegionSpace::EmbeddedController)));
|
||||
|
||||
if (!method->invoke(embedded_controller, AML::Integer::Constants::One).has_value())
|
||||
{
|
||||
AML_ERROR("Failed to evaluate {}._REG(EmbeddedController, 1), ignoring device", scope->scope);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool run_ini = true;
|
||||
bool init_children = true;
|
||||
|
||||
if (auto sta = Namespace::root_namespace()->find_object(scope->scope, AML::NameString("_STA"_sv), Namespace::FindMode::ForceAbsolute))
|
||||
if (auto [sta_path, sta_obj] = TRY(Namespace::root_namespace().find_named_object(scope, TRY(NameString::from_string("_STA"_sv)), true)); sta_obj)
|
||||
{
|
||||
auto result = evaluate_or_invoke(sta);
|
||||
if (!result)
|
||||
auto sta_result = TRY(evaluate_node(sta_path, sta_obj->node));
|
||||
if (sta_result.type != Node::Type::Integer)
|
||||
{
|
||||
AML_ERROR("Failed to evaluate {}._STA, return value could not be resolved to integer", scope->scope);
|
||||
return false;
|
||||
dwarnln("Object {} evaluated to {}", sta_path, sta_result);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
run_ini = (result->value & 0x01);
|
||||
init_children = run_ini || (result->value & 0x02);
|
||||
run_ini = (sta_result.as.integer.value & 0x01);
|
||||
init_children = run_ini || (sta_result.as.integer.value & 0x02);
|
||||
}
|
||||
|
||||
if (run_ini)
|
||||
{
|
||||
auto ini = Namespace::root_namespace()->find_object(scope->scope, AML::NameString("_INI"_sv), Namespace::FindMode::ForceAbsolute);
|
||||
if (ini)
|
||||
if (auto [ini_path, ini_obj] = TRY(Namespace::root_namespace().find_named_object(scope, TRY(NameString::from_string("_INI"_sv)), true)); ini_obj)
|
||||
{
|
||||
if (ini->type != AML::Node::Type::Method)
|
||||
auto& ini_node = ini_obj->node;
|
||||
|
||||
if (ini_node.type != Node::Type::Method)
|
||||
{
|
||||
AML_ERROR("Object {}._INI is not a method", scope->scope);
|
||||
return false;
|
||||
dwarnln("Object {} is not a method", ini_path);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
auto* method = static_cast<Method*>(ini.ptr());
|
||||
if (method->arg_count != 0)
|
||||
if (ini_node.as.method.arg_count != 0)
|
||||
{
|
||||
AML_ERROR("Method {}._INI has {} arguments, expected 0", scope->scope, method->arg_count);
|
||||
return false;
|
||||
dwarnln("Method {} takes {} arguments, expected 0", ini_path, ini_node.as.method.arg_count);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
auto result = method->invoke();
|
||||
if (!result.has_value())
|
||||
{
|
||||
AML_ERROR("Failed to evaluate {}._INI, ignoring device", scope->scope);
|
||||
return true;
|
||||
}
|
||||
TRY(method_call(ini_path, ini_node, {}));
|
||||
}
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
BAN::ErrorOr<void> result {};
|
||||
if (init_children)
|
||||
{
|
||||
Namespace::root_namespace()->for_each_child(scope->scope,
|
||||
[&](const auto&, auto& child)
|
||||
TRY(Namespace::root_namespace().for_each_child(scope,
|
||||
[&result](const Scope& child_path, Reference* child) -> BAN::Iteration
|
||||
{
|
||||
if (!child->is_scope())
|
||||
return;
|
||||
auto* child_scope = static_cast<Scope*>(child.ptr());
|
||||
if (!initialize_scope(child_scope))
|
||||
success = false;
|
||||
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 success;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
#include <kernel/ACPI/AML/Buffer.h>
|
||||
#include <kernel/ACPI/AML/String.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
BAN::Optional<bool> String::logical_compare(BAN::RefPtr<AML::Node> node, AML::Byte binaryop)
|
||||
{
|
||||
auto rhs = node ? node->convert(AML::Node::ConvString) : BAN::RefPtr<AML::Node>();
|
||||
if (!rhs)
|
||||
{
|
||||
AML_ERROR("String logical compare RHS is not string");
|
||||
return {};
|
||||
}
|
||||
|
||||
(void)binaryop;
|
||||
AML_TODO("Logical compare string");
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Buffer> String::as_buffer()
|
||||
{
|
||||
auto buffer = MUST(BAN::RefPtr<AML::Buffer>::create());
|
||||
MUST(buffer->buffer.resize(string.size()));
|
||||
for (size_t i = 0; i < string.size(); i++)
|
||||
buffer->buffer[i] = string[i];
|
||||
return buffer;
|
||||
}
|
||||
|
||||
BAN::RefPtr<AML::Node> String::convert(uint8_t mask)
|
||||
{
|
||||
if (mask & AML::Node::ConvString)
|
||||
return this;
|
||||
if (mask & AML::Node::ConvInteger)
|
||||
{
|
||||
// Apparently this is what NT does, but its definitely not spec compliant :D
|
||||
uint64_t value = 0;
|
||||
const size_t bytes = BAN::Math::min<size_t>(string.size(), sizeof(value));
|
||||
memcpy(&value, string.data(), bytes);
|
||||
return MUST(BAN::RefPtr<AML::Integer>::create(value));
|
||||
}
|
||||
if (mask & AML::Node::ConvBuffer)
|
||||
return as_buffer();
|
||||
return {};
|
||||
}
|
||||
|
||||
ParseResult String::parse(ParseContext& context)
|
||||
{
|
||||
ASSERT(context.aml_data.size() >= 1);
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::StringPrefix);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
BAN::Vector<uint8_t> string;
|
||||
|
||||
while (context.aml_data.size() > 0)
|
||||
{
|
||||
if (context.aml_data[0] == 0x00)
|
||||
break;
|
||||
MUST(string.push_back(context.aml_data[0]));
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
}
|
||||
|
||||
if (context.aml_data.size() == 0)
|
||||
return ParseResult::Failure;
|
||||
if (context.aml_data[0] != 0x00)
|
||||
return ParseResult::Failure;
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto string_node = MUST(BAN::RefPtr<String>::create());
|
||||
string_node->string = BAN::move(string);
|
||||
|
||||
return ParseResult(string_node);
|
||||
}
|
||||
|
||||
}
|
|
@ -1588,7 +1588,7 @@ namespace Kernel
|
|||
|
||||
[[noreturn]] static void reset_system()
|
||||
{
|
||||
ACPI::ACPI::get().reset();
|
||||
(void)ACPI::ACPI::get().reset();
|
||||
|
||||
dwarnln("Could not reset with ACPI, crashing the cpu");
|
||||
|
||||
|
@ -1608,9 +1608,8 @@ namespace Kernel
|
|||
if (command == POWEROFF_REBOOT)
|
||||
reset_system();
|
||||
|
||||
ACPI::ACPI::get().poweroff();
|
||||
|
||||
return BAN::Error::from_errno(EUNKNOWN);
|
||||
TRY(ACPI::ACPI::get().poweroff());
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<long> Process::sys_poweroff(int command)
|
||||
|
|
Loading…
Reference in New Issue