450 lines
11 KiB
C++
450 lines
11 KiB
C++
#pragma once
|
|
|
|
#include <BAN/Array.h>
|
|
#include <BAN/ByteSpan.h>
|
|
#include <BAN/LinkedList.h>
|
|
#include <BAN/NoCopyMove.h>
|
|
#include <BAN/Optional.h>
|
|
#include <BAN/StringView.h>
|
|
#include <BAN/Vector.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 NameString
|
|
{
|
|
BAN_NON_COPYABLE(NameString);
|
|
public:
|
|
NameString() = default;
|
|
NameString(NameString&& other)
|
|
: base(other.base)
|
|
, parts(BAN::move(other.parts))
|
|
{}
|
|
NameString& operator=(NameString&& other)
|
|
{
|
|
base = other.base;
|
|
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
|
|
{
|
|
BAN::StringView as_sv() const
|
|
{
|
|
return BAN::StringView(reinterpret_cast<const char*>(bytes), size);
|
|
}
|
|
|
|
uint64_t size;
|
|
uint32_t ref_count;
|
|
uint8_t bytes[];
|
|
};
|
|
|
|
struct OpRegion
|
|
{
|
|
GAS::AddressSpaceID address_space;
|
|
uint64_t offset;
|
|
uint64_t length;
|
|
alignas(Scope) uint8_t scope_storage[sizeof(Scope)];
|
|
Scope& scope() { return *reinterpret_cast<Scope*>(scope_storage); }
|
|
const Scope& scope() const { return *reinterpret_cast<const Scope*>(scope_storage); }
|
|
};
|
|
|
|
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;
|
|
};
|
|
|
|
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&&);
|
|
|
|
static BAN::ErrorOr<Node> create_string(BAN::StringView string)
|
|
{
|
|
const auto* u8_data = reinterpret_cast<const uint8_t*>(string.data());
|
|
auto result = TRY(create_buffer({ u8_data, string.size() }));
|
|
result.type = Node::Type::String;
|
|
return result;
|
|
}
|
|
|
|
static BAN::ErrorOr<Node> create_buffer(BAN::ConstByteSpan buffer)
|
|
{
|
|
Node node;
|
|
node.type = Node::Type::Buffer;
|
|
node.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + buffer.size()));
|
|
if (node.as.str_buf == nullptr)
|
|
return BAN::Error::from_errno(ENOMEM);
|
|
node.as.str_buf->ref_count = 1;
|
|
node.as.str_buf->size = buffer.size();
|
|
memcpy(node.as.str_buf->bytes, buffer.data(), buffer.size());
|
|
return node;
|
|
}
|
|
|
|
enum class Type
|
|
{
|
|
Uninitialized,
|
|
Debug,
|
|
Integer,
|
|
String,
|
|
Buffer,
|
|
Package,
|
|
BufferField,
|
|
OpRegion,
|
|
FieldUnit,
|
|
Event,
|
|
Device,
|
|
Processor,
|
|
PowerResource,
|
|
ThermalZone,
|
|
Method,
|
|
Mutex,
|
|
// FIXME: Index should not be its own type
|
|
// parsing index should return references
|
|
Index,
|
|
Reference,
|
|
PredefinedScope,
|
|
Count
|
|
} type { Type::Uninitialized };
|
|
|
|
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();
|
|
}
|
|
|
|
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;
|
|
|
|
BAN::ErrorOr<Node> copy() const;
|
|
|
|
void clear();
|
|
};
|
|
|
|
struct Reference
|
|
{
|
|
Node node {};
|
|
uint32_t ref_count { 1 };
|
|
};
|
|
|
|
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, uint64_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);
|
|
BAN::ErrorOr<Node> method_call(const Scope& scope, const Node& method,
|
|
Node&& arg0 = {}, Node&& arg1 = {}, Node&& arg2 = {}, Node&& arg3 = {}, Node&& arg4 = {}, Node&& arg5 = {}, Node&& arg6 = {});
|
|
|
|
BAN::ErrorOr<void> resolve_package_element(Package::Element& element, bool error_if_not_exists);
|
|
|
|
}
|
|
|
|
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 '{}'>", node.as.str_buf->as_sv());
|
|
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();
|
|
}
|
|
}
|
|
|
|
}
|