Files
banan-os/kernel/include/kernel/ACPI/AML/Buffer.h

213 lines
5.7 KiB
C++

#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>
namespace Kernel::ACPI::AML
{
struct Buffer : public AML::Node
{
BAN::Vector<uint8_t> buffer;
Buffer()
: AML::Node(Node::Type::Buffer)
{}
BAN::RefPtr<AML::Node> evaluate() override
{
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_object = AML::parse_object(buffer_context);
if (!buffer_size_object.success())
return ParseResult::Failure;
auto buffer_size = buffer_size_object.node()->as_integer();
if (!buffer_size.has_value())
return ParseResult::Failure;
uint32_t actual_buffer_size = BAN::Math::max<uint32_t>(buffer_size.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());
}
};
struct BufferField : AML::NamedObject
{
BAN::RefPtr<Buffer> buffer;
size_t field_bit_offset;
size_t field_bit_size;
BufferField(AML::NameSeg name, BAN::RefPtr<Buffer> 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> evaluate() override
{
ASSERT(buffer);
ASSERT(field_bit_offset + field_bit_size <= buffer->buffer.size() * 8);
uint64_t value = 0;
const size_t byte_offset = field_bit_offset / 8;
const size_t bit_offset = field_bit_offset % 8;
if (field_bit_size == 1)
{
value = (buffer->buffer[byte_offset] >> bit_offset) & 1;
}
else
{
ASSERT(bit_offset == 0);
for (size_t byte = 0; byte < field_bit_size / 8; byte++)
value |= buffer->buffer[byte_offset + byte] << byte;
}
return MUST(BAN::RefPtr<AML::Integer>::create(value));
}
bool store(BAN::RefPtr<AML::Node> node) override
{
ASSERT(buffer);
ASSERT(field_bit_offset + field_bit_size <= buffer->buffer.size() * 8);
auto value = node->as_integer();
if (!value.has_value())
return false;
const size_t byte_offset = field_bit_offset / 8;
const size_t bit_offset = field_bit_offset % 8;
if (field_bit_size == 1)
{
buffer->buffer[byte_offset] &= ~(1 << bit_offset);
buffer->buffer[byte_offset] |= (value.value() & 1) << bit_offset;
}
else
{
ASSERT(bit_offset == 0);
for (size_t byte = 0; byte < field_bit_size / 8; byte++)
buffer->buffer[byte_offset + byte] = (value.value() >> (byte * 8)) & 0xFF;
}
return true;
}
static ParseResult parse(AML::ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
size_t field_bit_size = 0;
switch (static_cast<Byte>(context.aml_data[0]))
{
case AML::Byte::CreateBitFieldOp:
field_bit_size = 1;
break;
case AML::Byte::CreateByteFieldOp:
field_bit_size = 8;
break;
case AML::Byte::CreateWordFieldOp:
field_bit_size = 16;
break;
case AML::Byte::CreateDWordFieldOp:
field_bit_size = 32;
break;
case AML::Byte::CreateQWordFieldOp:
field_bit_size = 64;
break;
default:
ASSERT_NOT_REACHED();
}
context.aml_data = context.aml_data.slice(1);
auto buffer_result = AML::parse_object(context);
if (!buffer_result.success())
return ParseResult::Failure;
auto buffer_node = buffer_result.node() ? buffer_result.node()->evaluate() : nullptr;
if (!buffer_node || buffer_node->type != Node::Type::Buffer)
{
AML_ERROR("Buffer source does not evaluate to a Buffer");
return ParseResult::Failure;
}
auto buffer = static_cast<Buffer*>(buffer_node.ptr());
auto index_result = AML::parse_object(context);
if (!index_result.success())
return ParseResult::Failure;
auto index = index_result.node() ? index_result.node()->as_integer() : BAN::Optional<uint64_t>();
if (!index.has_value())
{
AML_ERROR("Failed to parse index for BufferField");
return ParseResult::Failure;
}
size_t field_bit_offset = index.value();
if (field_bit_size != 1)
field_bit_offset *= 8;
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;
}
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::Failure;
#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(" }");
}
};
}