banan-os/kernel/kernel/ACPI/AML/Node.cpp

3351 lines
98 KiB
C++

#include <BAN/Assert.h>
#include <BAN/String.h>
#include <kernel/ACPI/ACPI.h>
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/OpRegion.h>
#include <kernel/Processor.h>
#include <kernel/Timer/Timer.h>
#include <ctype.h>
namespace Kernel::ACPI::AML
{
static constexpr uint64_t ONES = BAN::numeric_limits<uint64_t>::max();
static constexpr uint64_t ZERO = BAN::numeric_limits<uint64_t>::min();
BAN::ErrorOr<NameString> parse_name_string(BAN::ConstByteSpan& aml_data)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_name_string");
if (aml_data.empty())
return BAN::Error::from_errno(ENODATA);
NameString name {};
switch (aml_data[0])
{
case '^':
while (!aml_data.empty() && aml_data[0] == '^') {
name.base++;
aml_data = aml_data.slice(1);
}
break;
case '\\':
name.base = NameString::base_root;
aml_data = aml_data.slice(1);
break;
}
if (aml_data.empty())
return BAN::Error::from_errno(ENODATA);
size_t name_seg_count = 1;
switch (aml_data[0])
{
case 0:
name_seg_count = 0;
aml_data = aml_data.slice(1);
break;
case '.':
name_seg_count = 2;
aml_data = aml_data.slice(1);
break;
case '/':
if (aml_data.size() < 2)
return BAN::Error::from_errno(ENODATA);
name_seg_count = aml_data[1];
aml_data = aml_data.slice(2);
break;
}
if (aml_data.size() < name_seg_count * 4)
return BAN::Error::from_errno(ENODATA);
TRY(name.parts.resize(name_seg_count));
for (size_t i = 0; i < name_seg_count; i++)
{
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]))
{
dwarnln("Invalid NameSeg {2H}, {2H}, {2H}, {2H}",
aml_data[0], aml_data[1], aml_data[2], aml_data[3]
);
return BAN::Error::from_errno(EINVAL);
}
name.parts[i] = aml_data.as<const uint32_t>();
aml_data = aml_data.slice(4);
}
return name;
}
static BAN::ErrorOr<Node> parse_integer(BAN::ConstByteSpan& aml_data)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_integer");
ASSERT(!aml_data.empty());
Node result {};
result.type = Node::Type::Integer;
size_t byte_count = 0;
switch (static_cast<AML::Byte>(aml_data[0]))
{
case AML::Byte::ZeroOp:
result.as.integer.value = ZERO;
break;
case AML::Byte::OneOp:
result.as.integer.value = 1;
break;
case AML::Byte::OnesOp:
result.as.integer.value = ONES;
break;
case AML::Byte::BytePrefix:
byte_count = 1;
break;
case AML::Byte::WordPrefix:
byte_count = 2;
break;
case AML::Byte::DWordPrefix:
byte_count = 4;
break;
case AML::Byte::QWordPrefix:
byte_count = 8;
break;
default:
ASSERT_NOT_REACHED();
}
aml_data = aml_data.slice(1);
if (byte_count)
{
if (aml_data.size() < byte_count)
return BAN::Error::from_errno(ENODATA);
result.as.integer.value = 0;
for (size_t i = 0; i < byte_count; i++)
result.as.integer.value |= static_cast<uint64_t>(aml_data[i]) << (i * 8);
aml_data = aml_data.slice(byte_count);
}
return result;
}
static BAN::ErrorOr<Node> parse_string(BAN::ConstByteSpan& aml_data)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_string");
ASSERT(!aml_data.empty());
ASSERT(static_cast<AML::Byte>(aml_data[0]) == AML::Byte::StringPrefix);
aml_data = aml_data.slice(1);
size_t len = 0;
for (; len < aml_data.size() && aml_data[len]; len++)
{
if (!(0x01 <= aml_data[len] && aml_data[len] <= 0x7F))
{
dwarnln("Invalid byte {2H} in a string", aml_data[len]);
return BAN::Error::from_errno(EINVAL);
}
}
if (len >= aml_data.size())
return BAN::Error::from_errno(ENODATA);
Node result {};
result.type = Node::Type::String;
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + len));
result.as.str_buf->size = len;
result.as.str_buf->ref_count = 1;
if (result.as.str_buf == nullptr)
return BAN::Error::from_errno(ENOMEM);
memcpy(result.as.str_buf->bytes, aml_data.as_span<const uint8_t>().data(), len);
aml_data = aml_data.slice(len + 1);
return result;
}
BAN::ErrorOr<BAN::ConstByteSpan> parse_pkg(BAN::ConstByteSpan& aml_data)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_pkg");
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;
}
if (aml_data.size() < pkg_length)
return BAN::Error::from_errno(ENODATA);
auto result = aml_data.slice(0, pkg_length).slice(encoding_length);
aml_data = aml_data.slice(pkg_length);
return result;
}
static BAN::ErrorOr<void> resolve_package_element(Package::Element& element, bool error_if_not_exists)
{
if (element.resolved)
{
if (element.value.node)
return {};
element.value.node = new Node();
if (element.value.node == nullptr)
return BAN::Error::from_errno(ENOMEM);
element.value.node->type = Node::Type::Uninitialized;
return {};
}
ASSERT(element.value.location);
auto [_, resolved_obj] = TRY(Namespace::root_namespace().find_named_object(element.value.location->scope, element.value.location->name));
if (resolved_obj == nullptr)
{
if (!error_if_not_exists)
return {};
dwarnln("Could not resolve '{}'.'{}'");
return BAN::Error::from_errno(ENOENT);
}
Node* new_node = new Node();
if (new_node == nullptr)
return BAN::Error::from_errno(ENOMEM);
switch (resolved_obj->node.type)
{
case Node::Type::Device:
case Node::Type::Event:
case Node::Type::Method:
case Node::Type::Mutex:
case Node::Type::OpRegion:
case Node::Type::PowerResource:
case Node::Type::Processor:
case Node::Type::ThermalZone:
case Node::Type::PredefinedScope:
new_node->type = Node::Type::Reference;
new_node->as.reference = resolved_obj;
new_node->as.reference->ref_count++;
break;
default:
*new_node = TRY(resolved_obj->node.copy());
break;
}
delete element.value.location;
element.resolved = true;
element.value.node = new_node;
return {};
}
static BAN::ErrorOr<Node> parse_package_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_package_op");
ASSERT(!context.aml_data.empty());
const auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
context.aml_data = context.aml_data.slice(1);
auto package_pkg = TRY(parse_pkg(context.aml_data));
if (package_pkg.empty())
return BAN::Error::from_errno(ENODATA);
auto old_aml_data = context.aml_data;
context.aml_data = package_pkg;
uint64_t num_elements = 0;
switch (opcode)
{
case AML::Byte::PackageOp:
num_elements = context.aml_data[0];
context.aml_data = context.aml_data.slice(1);
break;
case AML::Byte::VarPackageOp:
{
auto node = TRY(parse_node(context));
node = TRY(convert_node(BAN::move(node), ConvInteger, sizeof(uint64_t)));
num_elements = node.as.integer.value;
break;
}
default:
ASSERT_NOT_REACHED();
}
Node result {};
result.type = Node::Type::Package;
result.as.package = static_cast<Package*>(kmalloc(sizeof(Package) + num_elements * sizeof(Package::Element)));
if (result.as.package == nullptr)
return BAN::Error::from_errno(ENOMEM);
result.as.package->num_elements = num_elements;
result.as.package->ref_count = 1;
size_t i = 0;
for (; i < num_elements && !context.aml_data.empty(); i++)
{
auto& element = result.as.package->elements[i];
if (is_name_string_start(context.aml_data[0]))
{
auto name_string = TRY(parse_name_string(context.aml_data));
element.resolved = false;
element.value.location = new Package::Element::Location();
if (element.value.location == nullptr)
return BAN::Error::from_errno(ENOMEM);
element.value.location->name = BAN::move(name_string);
element.value.location->scope = TRY(context.scope.copy());
TRY(resolve_package_element(element, false));
continue;
}
element.resolved = true;
element.value.node = new Node();
element.value.node->type = Node::Type::Uninitialized;
if (element.value.node == nullptr)
return BAN::Error::from_errno(ENOMEM);
*element.value.node = TRY(parse_node(context));
}
for (; i < num_elements; i++)
{
result.as.package->elements[i].resolved = true;
result.as.package->elements[i].value.node = nullptr;
}
context.aml_data = old_aml_data;
return result;
}
static BAN::ErrorOr<Node> parse_buffer_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_buffer_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::BufferOp);
context.aml_data = context.aml_data.slice(1);
auto buffer_pkg = TRY(parse_pkg(context.aml_data));
if (buffer_pkg.empty())
return BAN::Error::from_errno(ENODATA);
auto old_aml_data = context.aml_data;
context.aml_data = buffer_pkg;
auto buffer_size_node = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
const uint64_t buffer_size = BAN::Math::max<uint64_t>(buffer_size_node.as.integer.value, context.aml_data.size());
Node result {};
result.type = Node::Type::Buffer;
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + buffer_size));
if (result.as.str_buf == nullptr)
return BAN::Error::from_errno(ENOMEM);
result.as.str_buf->size = buffer_size;
result.as.str_buf->ref_count = 1;
if (context.aml_data.size() > 0)
memcpy(result.as.str_buf->bytes, context.aml_data.data(), context.aml_data.size());
if (context.aml_data.size() < buffer_size)
memset(result.as.str_buf->bytes + context.aml_data.size(), 0, buffer_size - context.aml_data.size());
context.aml_data = old_aml_data;
return result;
}
static BAN::ErrorOr<Node> parse_logical_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_logical_op");
ASSERT(!context.aml_data.empty());
const auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
context.aml_data = context.aml_data.slice(1);
Node dummy_integer {};
dummy_integer.type = Node::Type::Integer;
dummy_integer.as.integer.value = 0;
auto lhs = TRY(parse_node(context));
if (opcode == AML::Byte::LNotOp)
{
lhs = TRY(convert_node(BAN::move(lhs), ConvInteger, sizeof(uint64_t)));
Node result {};
result.type = Node::Type::Integer;
result.as.integer.value = lhs.as.integer.value ? ZERO : ONES;
return result;
}
auto rhs = TRY(parse_node(context));
bool (*compare)(int) = nullptr;
switch (opcode)
{
case AML::Byte::LAndOp:
case AML::Byte::LOrOp:
{
lhs = TRY(convert_node(BAN::move(lhs), ConvInteger, sizeof(uint64_t)));
rhs = TRY(convert_node(BAN::move(rhs), ConvInteger, sizeof(uint64_t)));
Node result {};
result.type = Node::Type::Integer;
if (opcode == AML::Byte::LAndOp)
result.as.integer.value = (lhs.as.integer.value && rhs.as.integer.value) ? ONES : ZERO;
else
result.as.integer.value = (lhs.as.integer.value || rhs.as.integer.value) ? ONES : ZERO;
return result;
}
case AML::Byte::LEqualOp:
compare = [](int val) { return val == 0; };
break;
case AML::Byte::LLessOp:
compare = [](int val) { return val < 0; };
break;
case AML::Byte::LGreaterOp:
compare = [](int val) { return val > 0; };
break;
default:
ASSERT_NOT_REACHED();
}
lhs = TRY(convert_node(BAN::move(lhs), ConvInteger | ConvString | ConvBuffer, ONES));
int (*normalize)(const Node&, const Node&) = nullptr;
switch (lhs.type)
{
case Node::Type::Integer:
normalize = [](const Node& a, const Node& b) -> int {
if (a.as.integer.value == b.as.integer.value)
return 0;
return a.as.integer.value < b.as.integer.value ? -1 : 1;
};
break;
case Node::Type::String:
case Node::Type::Buffer:
normalize = [](const Node& a, const Node& b) -> int {
const size_t bytes = BAN::Math::min(a.as.str_buf->size, b.as.str_buf->size);
if (int ret = memcmp(a.as.str_buf->bytes, b.as.str_buf->bytes, bytes))
return ret;
return a.as.str_buf->size < b.as.str_buf->size;
};
break;
default:
ASSERT_NOT_REACHED();
}
rhs = TRY(convert_node(BAN::move(rhs), lhs));
Node result {};
result.type = Node::Type::Integer;
result.as.integer.value = compare(normalize(lhs, rhs)) ? ONES : ZERO;
return result;
}
static BAN::ErrorOr<Node> parse_index_op(ParseContext& context);
enum class TargetType { Reference, Local, Arg, Debug };
using SuperNameResult = Pair<TargetType, Reference*>;
static BAN::ErrorOr<SuperNameResult> parse_super_name(ParseContext& context, bool error_if_not_exists)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_super_name");
auto& aml_data = context.aml_data;
const auto opcode = static_cast<AML::Byte>(aml_data[0]);
const auto ext_opcode = static_cast<AML::ExtOp>(aml_data.size() >= 2 ? aml_data[1] : 0);
if (opcode == AML::Byte::IndexOp)
{
// FIXME: memory leak
Reference* reference = new Reference();
if (reference == nullptr)
return BAN::Error::from_errno(ENOMEM);
reference->node = TRY(parse_index_op(context));
reference->ref_count = 1;
return SuperNameResult { TargetType::Reference, reference };
}
else if (is_name_string_start(aml_data[0]))
{
auto target = TRY(parse_name_string(aml_data));
auto [_, named_object] = TRY(Namespace::root_namespace().find_named_object(context.scope, target));
if (error_if_not_exists && named_object == nullptr)
{
dwarnln("SuperName '{}'.'{}' not found", context.scope, target);
return BAN::Error::from_errno(ENOENT);
}
return SuperNameResult { TargetType::Reference, named_object };
}
else if (opcode == AML::Byte::ExtOpPrefix && ext_opcode == AML::ExtOp::DebugOp)
{
aml_data = aml_data.slice(2);
return SuperNameResult { TargetType::Debug, nullptr };
}
else if (AML::Byte::Local0 <= opcode && opcode <= AML::Byte::Local7)
{
const uint8_t local_index = aml_data[0] - static_cast<uint8_t>(AML::Byte::Local0);
aml_data = aml_data.slice(1);
ASSERT(context.locals[local_index]);
return SuperNameResult { TargetType::Local, context.locals[local_index] };
}
else if (AML::Byte::Arg0 <= opcode && opcode <= AML::Byte::Arg6)
{
const uint8_t arg_index = aml_data[0] - static_cast<uint8_t>(AML::Byte::Arg0);
aml_data = aml_data.slice(1);
if (context.args[arg_index] == nullptr) {
dwarnln("Trying to reference uninitialized arg");
return BAN::Error::from_errno(EINVAL);
}
return SuperNameResult { TargetType::Local, context.args[arg_index] };
}
const bool is_ext = (opcode == AML::Byte::ExtOpPrefix) && (aml_data.size() >= 2);
dwarnln("TODO: SuperName {2H}{}", aml_data[is_ext ? 1 : 0], is_ext ? "e" : "");
return BAN::Error::from_errno(ENOTSUP);
}
template<typename T> requires (BAN::is_same_v<T, Node> || BAN::is_same_v<T, const Node>)
static BAN::ErrorOr<T&> underlying_node(T& node)
{
switch (node.type)
{
case Node::Type::Index:
dwarnln("TODO: Underlying node of index");
return BAN::Error::from_errno(ENOTSUP);
case Node::Type::Reference:
return node.as.reference->node;
default:
return node;
}
}
BAN::ErrorOr<Node> convert_node(Node&& source, uint8_t conversion, size_t max_length)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "convert_node {} -> 0b{3b}", source, conversion);
if ((source.type == Node::Type::Integer) && (conversion & Conversion::ConvInteger))
return source;
if ((source.type == Node::Type::Buffer) && (conversion & Conversion::ConvBuffer))
return source;
if ((source.type == Node::Type::String) && (conversion & Conversion::ConvString))
return source;
if (source.type == Node::Type::FieldUnit)
return convert_from_field_unit(source, conversion, max_length);
if (conversion & Conversion::ConvInteger)
{
Node result {};
result.type = Node::Type::Integer;
result.as.integer.value = 0;
switch (source.type)
{
case Node::Type::String:
case Node::Type::Buffer:
memcpy(
&result.as.integer.value,
source.as.str_buf->bytes,
BAN::Math::min<size_t>(source.as.str_buf->size, sizeof(uint64_t))
);
return result;
case Node::Type::BufferField:
for (size_t i = 0; i < BAN::Math::min<size_t>(source.as.buffer_field.bit_count, 64); i++)
{
const uint64_t idx = source.as.buffer_field.bit_offset + i;
const uint64_t bit = (source.as.buffer_field.buffer->bytes[idx / 8] >> (idx % 8)) & 1;
result.as.integer.value |= bit << i;
}
return result;
default:
break;
}
}
if (conversion & Conversion::ConvBuffer)
{
Node result {};
result.type = Node::Type::Buffer;
result.as.str_buf = nullptr;
switch (source.type)
{
case Node::Type::Integer:
if (source.as.integer.value)
max_length = BAN::Math::min<size_t>(max_length, BAN::Math::ilog2(source.as.integer.value) / 4 + 1);
else
max_length = 1;
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + max_length));
if (result.as.str_buf == nullptr)
return BAN::Error::from_errno(ENOMEM);
memcpy(result.as.str_buf->bytes, &source.as.integer.value, max_length);
result.as.str_buf->size = max_length;
result.as.str_buf->ref_count = 1;
return result;
case Node::Type::String:
{
max_length = BAN::Math::min<size_t>(max_length, source.as.str_buf->size + 1);
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + max_length));
if (result.as.str_buf == nullptr)
return BAN::Error::from_errno(ENOMEM);
if (max_length <= source.as.str_buf->size)
memcpy(result.as.str_buf->bytes, source.as.str_buf->bytes, max_length);
else
{
memcpy(result.as.str_buf->bytes, source.as.str_buf->bytes, max_length - 1);
result.as.str_buf->bytes[max_length - 1] = 0x00;
}
result.as.str_buf->size = max_length;
result.as.str_buf->ref_count = 1;
return result;
}
case Node::Type::BufferField:
dwarnln("TODO: buffer field to buffer");
return BAN::Error::from_errno(ENOTSUP);
default:
break;
}
}
if (conversion & Conversion::ConvString)
{
Node result {};
result.type = Node::Type::String;
result.as.str_buf = nullptr;
switch (source.type)
{
case Node::Type::Integer:
{
auto string = TRY(BAN::String::formatted("{}", source.as.integer.value));
max_length = BAN::Math::min<size_t>(max_length, string.size());
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + max_length));
if (result.as.str_buf == nullptr)
return BAN::Error::from_errno(ENOMEM);
memcpy(result.as.str_buf->bytes, string.data(), max_length);
result.as.str_buf->size = max_length;
result.as.str_buf->ref_count = 1;
return result;
}
case Node::Type::Buffer:
result = BAN::move(source);
result.type = Node::Type::String;
return result;
case Node::Type::BufferField:
dwarnln("TODO: buffer field to string");
return BAN::Error::from_errno(ENOTSUP);
default:
break;
}
}
dwarnln("Invalid conversion from {} to 0b{3b}", source, conversion);
return BAN::Error::from_errno(EINVAL);
}
BAN::ErrorOr<Node> convert_node(Node&& source, const Node& target)
{
if (target.type == Node::Type::Uninitialized)
return source;
if (target.type == Node::Type::Integer)
return convert_node(BAN::move(source), ConvInteger, 8);
if (target.type == Node::Type::String)
return convert_node(BAN::move(source), ConvString, target.as.str_buf->size);
if (target.type == Node::Type::Buffer)
return convert_node(BAN::move(source), ConvBuffer, target.as.str_buf->size);
dwarnln("Invalid conversion from {} to {}", source, target);
return BAN::Error::from_errno(EINVAL);
}
static BAN::ErrorOr<void> store_to_buffer_field(const Node& source, Node& target)
{
ASSERT(target.type == Node::Type::BufferField);
const uint64_t bit_count = target.as.buffer_field.bit_count;
const uint64_t bit_offset = target.as.buffer_field.bit_offset;
const uint8_t* src_buf = nullptr;
uint64_t src_len = 0;
switch (source.type)
{
case Node::Type::String:
case Node::Type::Buffer:
src_buf = source.as.str_buf->bytes;
src_len = source.as.str_buf->size;
break;
case Node::Type::Integer:
src_buf = reinterpret_cast<const uint8_t*>(&source.as.integer.value);
src_len = sizeof(uint64_t);
break;
default:
{
Node dummy = TRY(convert_node(TRY(source.copy()), ConvInteger | ConvBuffer, sizeof(uint64_t)));
return store_to_buffer_field(dummy, target);
}
}
uint64_t i = 0;
while (i < bit_count)
{
const uint64_t j = bit_offset + i;
const uint8_t i_mod = i % 8;
const uint8_t j_mod = j % 8;
const uint8_t max_src_bits = BAN::Math::min<uint64_t>(8 - i_mod, bit_count - i);
const uint8_t max_dst_bits = BAN::Math::min<uint64_t>(8 - j_mod, bit_count - i);
const uint8_t bits = BAN::Math::min(max_src_bits, max_dst_bits);
const uint8_t mask = (1 << bits) - 1;
const uint8_t src_byte = (i / 8 < src_len) ? src_buf[i / 8] : 0x00;
uint8_t& dst_byte = target.as.buffer_field.buffer->bytes[j / 8];
dst_byte &= ~(mask << j_mod);
dst_byte |= ((src_byte >> i_mod) & mask) << j_mod;
i += bits;
}
return {};
}
static BAN::ErrorOr<void> perform_store(const Node& source, Reference* target, TargetType target_type)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "perform_store");
if (target_type == TargetType::Debug)
{
dprintln_if(AML_ENABLE_DEBUG, "DEBUG: {}", source);
return {};
}
ASSERT(target);
if (target->node.type == Node::Type::BufferField)
return store_to_buffer_field(source, target->node);
if (target->node.type == Node::Type::FieldUnit)
return store_to_field_unit(source, target->node);
auto source_copy = TRY(source.copy());
Node* index_node = nullptr;
if (target->node.type == Node::Type::Reference)
{
auto& ref_target = target->node.as.reference->node;
if (ref_target.type == Node::Type::Index)
index_node = &ref_target;
else
{
ASSERT(ref_target.type != Node::Type::Reference);
ref_target = TRY(convert_node(BAN::move(source_copy), ref_target));
return {};
}
}
if (target->node.type == Node::Type::Index)
index_node = &target->node;
if (index_node)
{
auto& index = index_node->as.index;
switch (index.type)
{
case Node::Type::String:
case Node::Type::Buffer:
ASSERT(index.index < index.as.str_buf->size);
index.as.str_buf->bytes[index.index] = TRY(convert_node(BAN::move(source_copy), ConvInteger, sizeof(uint64_t))).as.integer.value;
break;
case Node::Type::Package:
{
ASSERT(index.index < index.as.package->num_elements);
auto& pkg_element = index.as.package->elements[index.index];
TRY(resolve_package_element(pkg_element, true));
ASSERT(pkg_element.value.node);
if (pkg_element.value.node->type == Node::Type::Reference)
*pkg_element.value.node = TRY(convert_node(BAN::move(source_copy), pkg_element.value.node->as.reference->node));
else
*pkg_element.value.node = TRY(convert_node(BAN::move(source_copy), *pkg_element.value.node));
break;
}
default:
ASSERT_NOT_REACHED();
}
return {};
}
if (target_type == TargetType::Reference)
target->node = TRY(convert_node(BAN::move(source_copy), target->node));
else
{
if (source_copy.type == Node::Type::FieldUnit)
source_copy = TRY(convert_from_field_unit(source_copy, ConvInteger | ConvBuffer, ONES));
target->node = BAN::move(source_copy);
}
return {};
}
static BAN::ErrorOr<void> store_into_target(ParseContext& context, const Node& node)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "store_into_target");
auto& aml_data = context.aml_data;
if (aml_data.empty())
return BAN::Error::from_errno(ENODATA);
if (aml_data[0] == 0)
{
aml_data = aml_data.slice(1);
return {};
}
auto [target_type, target] = TRY(parse_super_name(context, true));
return perform_store(node, target, target_type);
}
static BAN::ErrorOr<Node> parse_store_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_store_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::StoreOp);
context.aml_data = context.aml_data.slice(1);
auto source = TRY(parse_node(context));
if (context.aml_data.empty())
return BAN::Error::from_errno(ENODATA);
if (context.aml_data[0] == 0)
{
dwarnln("StoreOp in to null target");
return BAN::Error::from_errno(EINVAL);
}
TRY(store_into_target(context, source));
return source;
}
static BAN::ErrorOr<Node> parse_copy_object_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_copy_object_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::CopyObjectOp);
context.aml_data = context.aml_data.slice(1);
auto source = TRY(parse_node(context));
if (context.aml_data.empty())
return BAN::Error::from_errno(ENODATA);
auto [target_type, target] = TRY(parse_super_name(context, true));
switch (target_type)
{
case TargetType::Arg:
case TargetType::Local:
case TargetType::Reference:
break;
case TargetType::Debug:
dwarnln("CopyObjectOp target is Debug");
return BAN::Error::from_errno(EINVAL);
}
target->node = TRY(source.copy());
return source;
}
static BAN::ErrorOr<Node> parse_index_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_index_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::IndexOp);
context.aml_data = context.aml_data.slice(1);
auto source_dummy = TRY(parse_node(context, true));
auto index = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
auto& source = TRY_REF(underlying_node(source_dummy));
Node result {};
switch (source.type)
{
case Node::Type::String:
case Node::Type::Buffer:
if (index.as.integer.value >= source.as.str_buf->size)
{
dwarnln("Invalid index {} to buffer of size {}", index.as.integer.value, source.as.str_buf->size);
return BAN::Error::from_errno(EINVAL);
}
result.as.index.as.str_buf = source.as.str_buf;
result.as.index.as.str_buf->ref_count++;
break;
case Node::Type::Package:
if (index.as.integer.value >= source.as.package->num_elements)
{
dwarnln("Invalid index {} to package of size {}", index.as.integer.value, source.as.package->num_elements);
return BAN::Error::from_errno(EINVAL);
}
result.as.index.as.package = source.as.package;
result.as.index.as.package->ref_count++;
break;
default:
dwarnln("Invalid IndexOp({}, {})", source, index);
return BAN::Error::from_errno(EINVAL);
}
result.type = Node::Type::Index;
result.as.index.type = source.type;
result.as.index.index = index.as.integer.value;
TRY(store_into_target(context, result));
return result;
}
static BAN::ErrorOr<Node> parse_object_type_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_object_type_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ObjectTypeOp);
context.aml_data = context.aml_data.slice(1);
auto [object_type, object] = TRY(parse_super_name(context, true));
uint64_t value = 0;
if (object_type == TargetType::Debug)
value = 16;
else
{
ASSERT(object);
auto* node = &object->node;
while (node->type == Node::Type::Reference)
if (node->type == Node::Type::Reference)
node = &node->as.reference->node;
auto node_type = node->type;
if (node_type == Node::Type::Index)
{
const auto& index = node->as.index;
switch (index.type)
{
case Node::Type::String:
case Node::Type::Buffer:
node_type = Node::Type::BufferField;
break;
case Node::Type::Package:
ASSERT(index.index < index.as.package->num_elements);
TRY(resolve_package_element(index.as.package->elements[index.index], true));
node_type = index.as.package->elements[index.index].value.node->type;
break;
default:
ASSERT_NOT_REACHED();
}
}
switch (node_type)
{
case Node::Type::Uninitialized:
case Node::Type::PredefinedScope: value = 0; break;
case Node::Type::Integer: value = 1; break;
case Node::Type::String: value = 2; break;
case Node::Type::Buffer: value = 3; break;
case Node::Type::Package: value = 4; break;
case Node::Type::FieldUnit: value = 5; break;
case Node::Type::Device: value = 6; break;
case Node::Type::Event: value = 7; break;
case Node::Type::Method: value = 8; break;
case Node::Type::Mutex: value = 9; break;
case Node::Type::OpRegion: value = 10; break;
case Node::Type::PowerResource: value = 11; break;
case Node::Type::Processor: value = 12; break;
case Node::Type::ThermalZone: value = 13; break;
case Node::Type::BufferField: value = 14; break;
case Node::Type::Debug: value = 16; break;
case Node::Type::Index:
case Node::Type::Reference:
case Node::Type::Count: ASSERT_NOT_REACHED();
}
}
Node result;
result.type = Node::Type::Integer;
result.as.integer.value = value;
return result;
}
static BAN::ErrorOr<Node> sizeof_impl(const Node& node)
{
Node result {};
result.type = Node::Type::Integer;
result.as.integer.value = 0;
switch (node.type)
{
case Node::Type::String:
case Node::Type::Buffer:
result.as.integer.value = node.as.str_buf->size;
break;
case Node::Type::Package:
result.as.integer.value = node.as.package->num_elements;
break;
case Node::Type::Reference:
return sizeof_impl(node.as.reference->node);
default:
dwarnln("SizeofOp on {}", node);
return BAN::Error::from_errno(EINVAL);
}
return result;
}
static BAN::ErrorOr<void> parse_createfield_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_createfield_op");
ASSERT(!context.aml_data.empty());
const auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
context.aml_data = context.aml_data.slice(1);
uint64_t bit_count = 0;
bool index_in_bits = false;
if (opcode == AML::Byte::ExtOpPrefix)
{
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::ExtOp>(context.aml_data[0]) == AML::ExtOp::CreateFieldOp);
context.aml_data = context.aml_data.slice(1);
bit_count = 0;
index_in_bits = true;
}
else
{
switch (opcode)
{
case AML::Byte::CreateBitFieldOp:
bit_count = 1;
index_in_bits = true;
break;
case AML::Byte::CreateByteFieldOp:
bit_count = 8;
index_in_bits = false;
break;
case AML::Byte::CreateWordFieldOp:
bit_count = 16;
index_in_bits = false;
break;
case AML::Byte::CreateDWordFieldOp:
bit_count = 32;
index_in_bits = false;
break;
case AML::Byte::CreateQWordFieldOp:
bit_count = 64;
index_in_bits = false;
break;
default:
ASSERT_NOT_REACHED();
}
}
auto buffer_node = TRY(parse_node(context));
if (buffer_node.type != Node::Type::Buffer)
{
dwarnln("CreateField buffer is {}", buffer_node);
return BAN::Error::from_errno(EINVAL);
}
Node dummy_integer {};
dummy_integer.type = Node::Type::Integer;
dummy_integer.as.integer.value = 0;
auto index_node = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
if (bit_count == 0)
{
auto bit_count_node = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
bit_count = bit_count_node.as.integer.value;
if (bit_count == 0)
{
dwarnln("CreateField bit count 0");
return BAN::Error::from_errno(EINVAL);
}
}
auto field_name_string = TRY(parse_name_string(context.aml_data));
Node buffer_field;
buffer_field.type = Node::Type::BufferField;
buffer_field.as.buffer_field.buffer = buffer_node.as.str_buf;
buffer_field.as.buffer_field.buffer->ref_count++;
buffer_field.as.buffer_field.bit_count = bit_count;
buffer_field.as.buffer_field.bit_offset = index_in_bits ? index_node.as.integer.value : index_node.as.integer.value * 8;
TRY(Namespace::root_namespace().add_named_object(context.scope, field_name_string, BAN::move(buffer_field)));
return {};
}
static BAN::ErrorOr<Node> parse_sizeof_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_sizeof_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::SizeOfOp);
context.aml_data = context.aml_data.slice(1);
auto [object_type, object] = TRY(parse_super_name(context, true));
if (object_type == TargetType::Debug)
{
dwarnln("SizeofOp on debug object");
return BAN::Error::from_errno(EINVAL);
}
ASSERT(object);
return TRY(sizeof_impl(object->node));
}
static BAN::ErrorOr<Node> derefof_impl(const Node& source)
{
switch (source.type)
{
case Node::Type::Reference:
return TRY(source.as.reference->node.copy());
case Node::Type::Index:
{
switch (source.as.index.type)
{
case Node::Type::String:
case Node::Type::Buffer:
{
ASSERT(source.as.index.index < source.as.index.as.str_buf->size);
Node result;
result.type = Node::Type::Integer;
result.as.integer.value = source.as.index.as.str_buf->bytes[source.as.index.index];
return result;
}
case Node::Type::Package:
{
ASSERT(source.as.index.index < source.as.index.as.package->num_elements);
TRY(resolve_package_element(source.as.index.as.package->elements[source.as.index.index], true));
return TRY(source.as.index.as.package->elements[source.as.index.index].value.node->copy());
}
default: ASSERT_NOT_REACHED();
}
}
default:
dwarnln("DerefOf of non-reference {}", source);
return BAN::Error::from_errno(EINVAL);
}
}
static BAN::ErrorOr<Node> parse_inc_dec_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_inc_dec_op");
ASSERT(!context.aml_data.empty());
const uint8_t opcode = context.aml_data[0];
context.aml_data = context.aml_data.slice(1);
uint64_t (*function)(uint64_t) = nullptr;
switch (static_cast<AML::Byte>(opcode))
{
case AML::Byte::IncrementOp:
function = [](uint64_t x) { return x + 1; };
break;
case AML::Byte::DecrementOp:
function = [](uint64_t x) { return x - 1; };
break;
default:
ASSERT_NOT_REACHED();
}
auto [type, addend_ref] = TRY(parse_super_name(context, true));
if (type == TargetType::Debug)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "UnaryIntegerOp on Debug object");
Node result {};
result.type = Node::Type::Integer;
result.as.integer.value = 0;
return result;
}
ASSERT(addend_ref);
Node addend_node = TRY(addend_ref->node.copy());
if (addend_node.type == Node::Type::Reference || addend_node.type == Node::Type::Index)
addend_node = TRY(derefof_impl(addend_node));
auto current = TRY(convert_node(BAN::move(addend_node), ConvInteger, sizeof(uint64_t)));
Node result {};
result.type = Node::Type::Integer;
result.as.integer.value = function(current.as.integer.value);
TRY(perform_store(result, addend_ref, type));
return result;
}
static BAN::ErrorOr<Node> parse_unary_integer_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_unary_integer_op");
ASSERT(!context.aml_data.empty());
const auto opcode = context.aml_data[0];
context.aml_data = context.aml_data.slice(1);
uint64_t (*function)(uint64_t) = nullptr;
switch (static_cast<AML::Byte>(opcode))
{
case AML::Byte::NotOp:
function = [](uint64_t a) { return ~a; };
break;
default:
ASSERT_NOT_REACHED();
}
auto value = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
Node result {};
result.type = Node::Type::Integer;
result.as.integer.value = function(value.as.integer.value);
TRY(store_into_target(context, result));
return result;
}
static BAN::ErrorOr<Node> parse_binary_integer_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_binary_integer_op");
ASSERT(!context.aml_data.empty());
const auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
context.aml_data = context.aml_data.slice(1);
uint64_t (*function)(uint64_t, uint64_t) = nullptr;
switch (opcode)
{
case AML::Byte::AddOp:
function = [](uint64_t a, uint64_t b) { return a + b; };
break;
case AML::Byte::SubtractOp:
function = [](uint64_t a, uint64_t b) { return a - b; };
break;
case AML::Byte::MultiplyOp:
function = [](uint64_t a, uint64_t b) { return a * b; };
break;
case AML::Byte::ShiftLeftOp:
function = [](uint64_t a, uint64_t b) { return a << b; };
break;
case AML::Byte::ShiftRightOp:
function = [](uint64_t a, uint64_t b) { return a >> b; };
break;
case AML::Byte::AndOp:
function = [](uint64_t a, uint64_t b) { return a & b; };
break;
case AML::Byte::NandOp:
function = [](uint64_t a, uint64_t b) { return ~(a & b); };
break;
case AML::Byte::OrOp:
function = [](uint64_t a, uint64_t b) { return a | b; };
break;
case AML::Byte::NorOp:
function = [](uint64_t a, uint64_t b) { return ~(a | b); };
break;
case AML::Byte::XorOp:
function = [](uint64_t a, uint64_t b) { return a ^ b; };
break;
case AML::Byte::DivideOp:
function = [](uint64_t a, uint64_t b) { return a / b; };
break;
case AML::Byte::ModOp:
function = [](uint64_t a, uint64_t b) { return a % b; };
break;
default:
ASSERT_NOT_REACHED();
}
auto lhs = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
auto rhs = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
if (opcode == AML::Byte::DivideOp || opcode == AML::Byte::ModOp)
{
if (rhs.as.integer.value == 0)
{
dwarnln("DivideOp or ModOp divisor is zero");
return BAN::Error::from_errno(EINVAL);
}
}
Node result {};
result.type = Node::Type::Integer;
result.as.integer.value = function(lhs.as.integer.value, rhs.as.integer.value);
TRY(store_into_target(context, result));
if (opcode == AML::Byte::DivideOp)
{
Node remainder {};
remainder.type = Node::Type::Integer;
remainder.as.integer.value = function(lhs.as.integer.value, rhs.as.integer.value);
TRY(store_into_target(context, remainder));
}
return result;
}
static BAN::ErrorOr<Node> parse_mid_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_mid_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::MidOp);
context.aml_data = context.aml_data.slice(1);
auto source = TRY(convert_node(TRY(parse_node(context)), ConvBuffer | ConvString, ONES));
const uint64_t index = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;
const uint64_t length = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;
const uint64_t source_len = source.as.str_buf->size;
uint64_t result_size = 0;
if (index + length <= source_len)
result_size = length;
else if (index < source_len)
result_size = source_len - index;
Node result;
result.type = source.type;
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + result_size));
if (result.as.str_buf == nullptr)
return BAN::Error::from_errno(ENOTSUP);
memcpy(result.as.str_buf->bytes, source.as.str_buf->bytes + index, result_size);
result.as.str_buf->size = result_size;
result.as.str_buf->ref_count = 1;
return result;
}
static BAN::ErrorOr<Node> parse_concat_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_concat_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ConcatOp);
context.aml_data = context.aml_data.slice(1);
auto source1 = TRY(convert_node(TRY(parse_node(context)), ConvInteger | ConvString | ConvBuffer, ONES));
auto source2 = TRY(convert_node(TRY(parse_node(context)), source1));
Node result {};
switch (source1.type)
{
case AML::Node::Type::Integer:
dwarnln("TODO: ConcatOp with integers");
return BAN::Error::from_errno(ENOTSUP);
case AML::Node::Type::String:
case AML::Node::Type::Buffer:
result.type = source1.type;
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + source1.as.str_buf->size + source2.as.str_buf->size));
if (result.as.str_buf == nullptr)
return BAN::Error::from_errno(ENOMEM);
result.as.str_buf->size = source1.as.str_buf->size + source2.as.str_buf->size;
result.as.str_buf->ref_count = 1;
memcpy(result.as.str_buf->bytes, source1.as.str_buf->bytes, source1.as.str_buf->size);
memcpy(result.as.str_buf->bytes + source1.as.str_buf->size, source2.as.str_buf->bytes, source2.as.str_buf->size);
break;
default:
ASSERT_NOT_REACHED();
}
TRY(store_into_target(context, result));
return result;
}
static BAN::ErrorOr<Node> parse_explicit_conversion(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_explicit_conversion");
ASSERT(!context.aml_data.empty());
const uint8_t opcode = context.aml_data[0];
context.aml_data = context.aml_data.slice(1);
auto source = TRY(parse_node(context));
if (source.type == Node::Type::FieldUnit || source.type == Node::Type::BufferField)
source = TRY(convert_node(BAN::move(source), ConvInteger | ConvBuffer, ONES));
switch (source.type)
{
case Node::Type::Buffer:
case Node::Type::String:
case Node::Type::Integer:
break;
default:
dwarnln("Explicit conversion source is {}", source);
return BAN::Error::from_errno(EINVAL);
}
Node result {};
switch (static_cast<AML::Byte>(opcode))
{
case AML::Byte::ToDecimalStringOp:
if (source.type == Node::Type::String)
result = BAN::move(source);
else
{
BAN::String ban_string;
if (source.type == Node::Type::Integer)
ban_string = TRY(BAN::String::formatted("{}", source.as.integer.value));
else if (source.type == Node::Type::Buffer)
for (size_t i = 0; i < source.as.str_buf->size; i++)
TRY(ban_string.append(TRY(BAN::String::formatted("{},", source.as.str_buf->bytes[i]))));
else
ASSERT_NOT_REACHED();
if (!ban_string.empty() && ban_string.back() == ',')
ban_string.pop_back();
result.type = Node::Type::String;
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + ban_string.size()));
if (result.as.str_buf == nullptr)
return BAN::Error::from_errno(EINVAL);
memcpy(result.as.str_buf->bytes, ban_string.data(), ban_string.size());
result.as.str_buf->size = ban_string.size();
result.as.str_buf->ref_count = 1;
}
break;
case AML::Byte::ToHexStringOp:
if (source.type == Node::Type::String)
result = BAN::move(source);
else
{
BAN::String ban_string;
if (source.type == Node::Type::Integer)
ban_string = TRY(BAN::String::formatted("0x{H}", source.as.integer.value));
else if (source.type == Node::Type::Buffer)
for (size_t i = 0; i < source.as.str_buf->size; i++)
TRY(ban_string.append(TRY(BAN::String::formatted("0x{2H},", source.as.str_buf->bytes[i]))));
else
ASSERT_NOT_REACHED();
if (!ban_string.empty() && ban_string.back() == ',')
ban_string.pop_back();
result.type = Node::Type::String;
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + ban_string.size()));
if (result.as.str_buf == nullptr)
return BAN::Error::from_errno(EINVAL);
memcpy(result.as.str_buf->bytes, ban_string.data(), ban_string.size());
result.as.str_buf->size = ban_string.size();
result.as.str_buf->ref_count = 1;
}
break;
case AML::Byte::ToIntegerOp:
if (source.type != Node::Type::String)
result = TRY(convert_node(BAN::move(source), ConvInteger, sizeof(uint64_t)));
else
{
const auto get_base_val =
[](char c, uint8_t base) -> BAN::Optional<uint8_t>
{
if (isdigit(c) && c - '0' < base)
return c - '0';
if (isalpha(c) && tolower(c) - 'a' + 10 < base)
return tolower(c) - 'a' + 10;
return {};
};
auto source_sv = source.as.str_buf->as_sv();
while (!source_sv.empty() && !isdigit(source_sv[0]) && source_sv[0] != '-' && source_sv[0] != '+')
source_sv = source_sv.substring(1);
const bool is_negative = source_sv.empty() ? false : source_sv[0] == '-';
if (!source_sv.empty() && (source_sv[0] == '-' || source_sv[0] == '+'))
source_sv = source_sv.substring(1);
uint8_t base = 10;
if (!source_sv.empty() && source_sv[0] == '0')
{
base = 8;
source_sv = source_sv.substring(1);
if (!source_sv.empty() && tolower(source_sv[0]) == 'x')
{
base = 16;
source_sv = source_sv.substring(1);
}
}
uint64_t value = 0;
for (size_t i = 0; i < source_sv.size(); i++)
{
const auto to_add = get_base_val(source_sv[i], base);
if (!to_add.has_value())
break;
if (BAN::Math::will_multiplication_overflow<uint64_t>(value, base) ||
BAN::Math::will_addition_overflow<uint64_t>(value * base, to_add.value()))
{
value = BAN::numeric_limits<uint64_t>::max();
break;
}
value = (value * base) + to_add.value();
}
if (is_negative)
value = -value;
result.type = Node::Type::Integer;
result.as.integer.value = value;
}
break;
case AML::Byte::ToBufferOp:
result = TRY(convert_node(BAN::move(source), ConvBuffer, ONES));
break;
default:
ASSERT_NOT_REACHED();
}
TRY(store_into_target(context, result));
return result;
}
static BAN::ErrorOr<Node> parse_to_string_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_to_string_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ToStringOp);
context.aml_data = context.aml_data.slice(1);
auto source = TRY(convert_node(TRY(parse_node(context)), ConvBuffer, ONES));
auto length = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;
if (source.as.str_buf->size < length)
length = source.as.str_buf->size;
for (size_t i = 0; i < length; i++)
{
if (source.as.str_buf->bytes[i] != 0x00)
continue;
length = i;
break;
}
Node result;
result.type = Node::Type::String;
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + length));
if (result.as.str_buf == nullptr)
return BAN::Error::from_errno(ENOMEM);
memcpy(result.as.str_buf->bytes, source.as.str_buf->bytes, length);
result.as.str_buf->size = length;
result.as.str_buf->ref_count = 1;
TRY(store_into_target(context, source));
return result;
}
static BAN::ErrorOr<void> parse_alias_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_alias_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::AliasOp);
context.aml_data = context.aml_data.slice(1);
auto source_name_string = TRY(parse_name_string(context.aml_data));
auto object_name_string = TRY(parse_name_string(context.aml_data));
auto [_, source_ref] = TRY(Namespace::root_namespace().find_named_object(context.scope, source_name_string));
if (source_ref == nullptr)
{
dwarnln("AliasOp source does not exists");
return {};
}
TRY(Namespace::root_namespace().add_alias(context.scope, object_name_string, source_ref));
return {};
}
static BAN::ErrorOr<void> parse_name_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_name_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::NameOp);
context.aml_data = context.aml_data.slice(1);
auto name_string = TRY(parse_name_string(context.aml_data));
auto object = TRY(parse_node(context));
auto path = TRY(Namespace::root_namespace().add_named_object(context.scope, name_string, BAN::move(object)));
if (!path.parts.empty())
TRY(context.created_nodes.push_back(BAN::move(path)));
return {};
}
static BAN::ErrorOr<Node> parse_refof_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_refof_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::RefOfOp);
context.aml_data = context.aml_data.slice(1);
auto [type, target] = TRY(parse_super_name(context, true));
if (type == TargetType::Debug)
{
dwarnln("TODO: RefOf debug");
return BAN::Error::from_errno(ENOTSUP);
}
Reference* to_reference = target;
if (target->node.type == Node::Type::Reference)
to_reference = target->node.as.reference;
Node result {};
result.type = Node::Type::Reference;
result.as.reference = to_reference;
result.as.reference->ref_count++;
return result;
}
static BAN::ErrorOr<Node> parse_condrefof_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_cond_refof_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::CondRefOfOp);
context.aml_data = context.aml_data.slice(2);
auto [source_type, source] = TRY(parse_super_name(context, false));
if (source_type == TargetType::Debug)
{
dwarnln("TODO: CondRefOf debug");
return BAN::Error::from_errno(ENOTSUP);
}
if (context.aml_data.empty())
return BAN::Error::from_errno(ENODATA);
if (context.aml_data[0] == 0x00)
context.aml_data = context.aml_data.slice(1);
else
{
auto [target_type, target] = TRY(parse_super_name(context, true));
if (source)
{
Node reference {};
reference.type = Node::Type::Reference;
reference.as.reference = source;
reference.as.reference->ref_count++;
TRY(perform_store(reference, target, target_type));
}
}
Node result {};
result.type = Node::Type::Integer;
result.as.integer.value = source ? ONES : ZERO;
return result;
}
static BAN::ErrorOr<Node> parse_derefof_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_derefof_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::DerefOfOp);
context.aml_data = context.aml_data.slice(1);
return derefof_impl(TRY(parse_node(context)));
}
static BAN::ErrorOr<void> parse_scope_contents(Scope&& scope, BAN::ConstByteSpan aml_data)
{
ParseContext scope_context;
scope_context.scope = BAN::move(scope);
scope_context.aml_data = aml_data;
scope_context.call_depth = 0;
while (!scope_context.aml_data.empty())
{
auto parse_result = parse_node_or_execution_flow(scope_context);
if (parse_result.is_error())
{
dwarnln("Failed to parse scope {}: {}", scope_context.scope, parse_result.error());
return parse_result.release_error();
}
auto [execution_flow, node] = parse_result.release_value();
if (execution_flow != ExecutionFlow::Normal)
{
dwarnln("Scope got execution flow {}", static_cast<int>(execution_flow));
return BAN::Error::from_errno(EINVAL);
}
}
return {};
}
static BAN::ErrorOr<void> parse_scope_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_scope_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ScopeOp);
context.aml_data = context.aml_data.slice(1);
auto scope_pkg = TRY(parse_pkg(context.aml_data));
auto scope_name = TRY(parse_name_string(scope_pkg));
auto [object_path, object] = TRY(Namespace::root_namespace().find_named_object(context.scope, scope_name));
if (object == nullptr)
{
dwarnln("ScopeOp scope '{}'.'{}' does not exists", context.scope, scope_name);
return {};
}
switch (object->node.type)
{
case Node::Type::Device:
case Node::Type::Processor:
case Node::Type::PowerResource:
case Node::Type::ThermalZone:
case Node::Type::PredefinedScope:
break;
default:
dwarnln("ScopeOp on non-scope object");
return BAN::Error::from_errno(EINVAL);
}
TRY(parse_scope_contents(BAN::move(object_path), scope_pkg));
return {};
}
static BAN::ErrorOr<void> parse_notify_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_scope_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::NotifyOp);
context.aml_data = context.aml_data.slice(1);
auto object = TRY(parse_super_name(context, true));
auto value = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
dwarnln("TODO: handle notify({}, {})", object.elem2->node, value);
return {};
}
static BAN::ErrorOr<void> parse_event_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_device_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::EventOp);
context.aml_data = context.aml_data.slice(2);
auto event_name = TRY(parse_name_string(context.aml_data));
Node event_node {};
event_node.type = Node::Type::Event;
event_node.as.event.signal_count = 0;
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context.scope, event_name, BAN::move(event_node)));
if (absolute_path.parts.empty())
{
dwarnln("Could not add Device '{}'.'{}' to namespace", context.scope, event_name);
return {};
}
return {};
}
static BAN::ErrorOr<void> parse_reset_signal_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_reset_signal_op");
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
const auto opcode = static_cast<AML::ExtOp>(context.aml_data[1]);
context.aml_data = context.aml_data.slice(2);
auto [_, event_ref] = TRY(parse_super_name(context, true));
if (event_ref == nullptr)
{
dwarnln("ResetOp/SignalOp event is null");
return BAN::Error::from_errno(EINVAL);
}
if (event_ref->node.type != Node::Type::Event)
{
dwarnln("ResetOp/SignalOp Object '{}' is not an event", event_ref->node);
return BAN::Error::from_errno(EINVAL);
}
switch (opcode)
{
case AML::ExtOp::ResetOp:
event_ref->node.as.event.signal_count = 0;
break;
case AML::ExtOp::SignalOp:
event_ref->node.as.event.signal_count++;
break;
default:
ASSERT_NOT_REACHED();
}
return {};
}
static BAN::ErrorOr<Node> parse_wait_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_wait_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::WaitOp);
context.aml_data = context.aml_data.slice(2);
auto [_, event_ref] = TRY(parse_super_name(context, true));
if (event_ref == nullptr)
{
dwarnln("WaitOp event is null");
return BAN::Error::from_errno(EINVAL);
}
if (event_ref->node.type != Node::Type::Event)
{
dwarnln("WaitOp Object '{}' is not an event", event_ref->node);
return BAN::Error::from_errno(EINVAL);
}
const uint64_t timeout_ms = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;
const uint64_t wake_time_ms = (timeout_ms >= 0xFFFF) ? BAN::numeric_limits<uint64_t>::max() : SystemTimer::get().ms_since_boot() + timeout_ms;
uint64_t return_value = 0;
for (;;)
{
if (event_ref->node.as.event.signal_count > 0)
{
event_ref->node.as.event.signal_count--;
return_value = ZERO;
break;
}
if (wake_time_ms >= SystemTimer::get().ms_since_boot())
{
return_value = ONES;
break;
}
Processor::yield();
}
Node result;
result.type = Node::Type::Integer;
result.as.integer.value = return_value;
return result;
}
static BAN::ErrorOr<void> parse_sleep_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_sleep_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::SleepOp);
context.aml_data = context.aml_data.slice(2);
auto milliseconds = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
const uint64_t wakeup_ms = SystemTimer::get().ms_since_boot() + milliseconds.as.integer.value;
for (uint64_t curr_ms = SystemTimer::get().ms_since_boot(); curr_ms < wakeup_ms; curr_ms = SystemTimer::get().ms_since_boot())
SystemTimer::get().sleep_ms(wakeup_ms - curr_ms);
return {};
}
static BAN::ErrorOr<void> parse_stall_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_stall_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::StallOp);
context.aml_data = context.aml_data.slice(2);
const auto microseconds = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;
if (microseconds > 100)
{
dwarnln("Stall for {} us", microseconds);
return BAN::Error::from_errno(EINVAL);
}
const uint64_t wakeup_ns = SystemTimer::get().ns_since_boot() + microseconds * 1000;
while (SystemTimer::get().ns_since_boot() < wakeup_ns)
Processor::pause();
return {};
}
static BAN::ErrorOr<void> parse_device_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_device_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::DeviceOp);
context.aml_data = context.aml_data.slice(2);
auto device_pkg = TRY(parse_pkg(context.aml_data));
auto device_name = TRY(parse_name_string(device_pkg));
Node device_node {};
device_node.type = Node::Type::Device;
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context.scope, device_name, BAN::move(device_node)));
if (absolute_path.parts.empty())
{
dwarnln("Could not add Device '{}'.'{}' to namespace", context.scope, device_name);
return {};
}
TRY(parse_scope_contents(BAN::move(absolute_path), device_pkg));
return {};
}
static BAN::ErrorOr<void> parse_processor_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_processor_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::ProcessorOp);
context.aml_data = context.aml_data.slice(2);
auto processor_pkg = TRY(parse_pkg(context.aml_data));
auto processor_name = TRY(parse_name_string(processor_pkg));
// FIXME: do something with ProcID, PblkAddr, PblkLen?
if (processor_pkg.size() < 6)
return BAN::Error::from_errno(ENODATA);
processor_pkg = processor_pkg.slice(6);
Node processor_node {};
processor_node.type = Node::Type::Processor;
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context.scope, processor_name, BAN::move(processor_node)));
if (absolute_path.parts.empty())
{
dwarnln("Could not add Processor '{}'.'{}' to namespace", context.scope, processor_name);
return {};
}
TRY(parse_scope_contents(BAN::move(absolute_path), processor_pkg));
return {};
}
static BAN::ErrorOr<void> parse_power_resource_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_power_resource_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::PowerResOp);
context.aml_data = context.aml_data.slice(2);
auto power_resource_pkg = TRY(parse_pkg(context.aml_data));
auto power_resource_name = TRY(parse_name_string(power_resource_pkg));
// FIXME: do something with SystemLevel, ResourceOrder?
if (power_resource_pkg.size() < 3)
return BAN::Error::from_errno(ENODATA);
power_resource_pkg = power_resource_pkg.slice(3);
Node power_resource_node {};
power_resource_node.type = Node::Type::PowerResource;
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context.scope, power_resource_name, BAN::move(power_resource_node)));
if (absolute_path.parts.empty())
{
dwarnln("Could not add Processor '{}'.'{}' to namespace", context.scope, power_resource_name);
return {};
}
TRY(parse_scope_contents(BAN::move(absolute_path), power_resource_pkg));
return {};
}
static BAN::ErrorOr<void> parse_thermal_zone_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_thermal_zone_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::ThermalZoneOp);
context.aml_data = context.aml_data.slice(2);
auto thermal_zone_pkg = TRY(parse_pkg(context.aml_data));
auto thermal_zone_name = TRY(parse_name_string(thermal_zone_pkg));
Node thermal_zone_node {};
thermal_zone_node.type = Node::Type::ThermalZone;
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context.scope, thermal_zone_name, BAN::move(thermal_zone_node)));
if (absolute_path.parts.empty())
{
dwarnln("Could not add Thermal Zone '{}'.'{}' to namespace", context.scope, thermal_zone_name);
return {};
}
TRY(parse_scope_contents(BAN::move(absolute_path), thermal_zone_pkg));
return {};
}
static BAN::ErrorOr<void> parse_mutex_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_mutex_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::MutexOp);
context.aml_data = context.aml_data.slice(2);
auto mutex_name = TRY(parse_name_string(context.aml_data));
if (context.aml_data.empty())
return BAN::Error::from_errno(ENODATA);
const uint8_t mutex_flags = context.aml_data[0];
context.aml_data = context.aml_data.slice(1);
if (mutex_flags & 0xF0)
{
dwarnln("MutexOp flags has bits in top nibble set");
return BAN::Error::from_errno(EINVAL);
}
Node mutex;
mutex.type = Node::Type::Mutex;
mutex.as.mutex = new Mutex();
if (mutex.as.mutex == nullptr)
return BAN::Error::from_errno(ENOMEM);
mutex.as.mutex->sync_level = mutex_flags;
mutex.as.mutex->global_lock = false;
TRY(Namespace::root_namespace().add_named_object(context.scope, mutex_name, BAN::move(mutex)));
return {};
}
static BAN::ErrorOr<void> parse_fatal_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_fatal_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::FatalOp);
context.aml_data = context.aml_data.slice(2);
if (context.aml_data.size() < 5)
return BAN::Error::from_errno(ENODATA);
const uint8_t fatal_type = context.aml_data[0];
const uint32_t fatal_code = 0
| ((uint32_t)context.aml_data[1] << 0)
| ((uint32_t)context.aml_data[2] << 8)
| ((uint32_t)context.aml_data[3] << 16)
| ((uint32_t)context.aml_data[4] << 24);
context.aml_data = context.aml_data.slice(5);
const auto fatal_arg = TRY(parse_node(context));
derrorln("FATAL: type {2H}, code {8H}, arg {}", fatal_type, fatal_code, fatal_arg);
return {};
}
static BAN::ErrorOr<Node> parse_acquire_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_acquire_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::AcquireOp);
context.aml_data = context.aml_data.slice(2);
auto [type, sync_object] = TRY(parse_super_name(context, true));
if (type == TargetType::Debug)
{
dwarnln("AquireOp sync object is Debug");
return BAN::Error::from_errno(EINVAL);
}
ASSERT(sync_object);
if (sync_object->node.type != Node::Type::Mutex)
{
dwarnln("AquireOp sync object is {}", sync_object->node);
return BAN::Error::from_errno(EINVAL);
}
if (context.aml_data.size() < 2)
return BAN::Error::from_errno(ENODATA);
const uint16_t timeout_ms = context.aml_data.as<const uint16_t>();
context.aml_data = context.aml_data.slice(2);
Node result;
result.type = Node::Type::Integer;
result.as.integer.value = BAN::numeric_limits<uint64_t>::max();
const uint64_t wake_time_ms = (timeout_ms >= 0xFFFF)
? BAN::numeric_limits<uint64_t>::max()
: SystemTimer::get().ms_since_boot() + timeout_ms;
result.as.integer.value = 0;
while (true)
{
if (sync_object->node.as.mutex->mutex.try_lock())
break;
if (SystemTimer::get().ms_since_boot() >= wake_time_ms)
return result;
SystemTimer::get().sleep_ms(1);
}
if (sync_object->node.as.mutex->global_lock)
ACPI::acquire_global_lock();
result.as.integer.value = 0;
return result;
}
static BAN::ErrorOr<void> parse_release_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_release_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::ReleaseOp);
context.aml_data = context.aml_data.slice(2);
auto [type, sync_object] = TRY(parse_super_name(context, true));
if (type == TargetType::Debug)
{
dwarnln("AquireOp sync object is Debug");
return BAN::Error::from_errno(EINVAL);
}
ASSERT(sync_object);
if (sync_object->node.type != Node::Type::Mutex)
{
dwarnln("AquireOp sync object is {}", sync_object->node);
return BAN::Error::from_errno(EINVAL);
}
if (sync_object->node.as.mutex->global_lock)
ACPI::release_global_lock();
sync_object->node.as.mutex->mutex.unlock();
return {};
}
static BAN::ErrorOr<void> parse_method_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_method_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::MethodOp);
context.aml_data = context.aml_data.slice(1);
auto method_pkg = TRY(parse_pkg(context.aml_data));
auto method_name = TRY(parse_name_string(method_pkg));
if (method_pkg.empty())
return BAN::Error::from_errno(ENODATA);
const uint8_t method_flags = method_pkg[0];
method_pkg = method_pkg.slice(1);
Node method_node {};
method_node.type = Node::Type::Method;
method_node.as.method.start = method_pkg.data();
method_node.as.method.length = method_pkg.size();
method_node.as.method.arg_count = method_flags & 0x07;
method_node.as.method.serialized = !!(method_flags & 0x80);
method_node.as.method.mutex = nullptr;
if (method_node.as.method.serialized)
{
method_node.as.method.mutex = new Mutex();
method_node.as.method.mutex->sync_level = method_flags >> 4;
method_node.as.method.mutex->global_lock = false;
method_node.as.method.mutex->ref_count = 1;
}
auto path = TRY(Namespace::root_namespace().add_named_object(context.scope, method_name, BAN::move(method_node)));
if (!path.parts.empty())
TRY(context.created_nodes.push_back(BAN::move(path)));
return {};
}
static BAN::ErrorOr<ExecutionFlowResult> parse_if_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_if_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::IfOp);
context.aml_data = context.aml_data.slice(1);
auto if_pkg = TRY(parse_pkg(context.aml_data));
auto old_aml_data = context.aml_data;
context.aml_data = if_pkg;
auto predicate_node = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
const bool predicate = predicate_node.as.integer.value;
BAN::ConstByteSpan code_path;
if (predicate)
code_path = context.aml_data;
if (!old_aml_data.empty() && static_cast<AML::Byte>(old_aml_data[0]) == AML::Byte::ElseOp)
{
old_aml_data = old_aml_data.slice(1);
auto else_pkg = TRY(parse_pkg(old_aml_data));
if (!predicate)
code_path = else_pkg;
}
Node return_value {};
ExecutionFlow execution_flow = ExecutionFlow::Normal;
context.aml_data = code_path;
while (!context.aml_data.empty() && execution_flow == ExecutionFlow::Normal)
{
auto parse_result = parse_node_or_execution_flow(context);
if (parse_result.is_error())
{
dwarnln("Failed to parse if body in {}: {}", context.scope, parse_result.error());
return parse_result.release_error();
}
auto [new_execution_flow, node] = parse_result.release_value();
execution_flow = new_execution_flow;
switch (execution_flow)
{
case ExecutionFlow::Normal:
break;
case ExecutionFlow::Break:
break;
case ExecutionFlow::Continue:
break;
case ExecutionFlow::Return:
ASSERT(node.has_value());
return_value = node.release_value();
break;
}
}
context.aml_data = old_aml_data;
return ExecutionFlowResult {
.elem1 = execution_flow,
.elem2 = BAN::move(return_value),
};
}
static BAN::ErrorOr<ExecutionFlowResult> parse_while_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_while_op");
ASSERT(!context.aml_data.empty());
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::WhileOp);
context.aml_data = context.aml_data.slice(1);
auto while_pkg = TRY(parse_pkg(context.aml_data));
auto old_aml_data = context.aml_data;
Node return_value {};
ExecutionFlow execution_flow = ExecutionFlow::Normal;
const uint64_t while_loop_start_ms = SystemTimer::get().ms_since_boot();
while (execution_flow == ExecutionFlow::Normal)
{
if (auto current_ms = SystemTimer::get().ms_since_boot(); current_ms >= while_loop_start_ms + 5000)
{
dwarnln("While loop terminated after {} ms", current_ms - while_loop_start_ms);
break;
}
context.aml_data = while_pkg;
auto predicate = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
if (predicate.as.integer.value == 0)
break;
while (!context.aml_data.empty() && execution_flow == ExecutionFlow::Normal)
{
auto parse_result = parse_node_or_execution_flow(context);
if (parse_result.is_error())
{
dwarnln("Failed to parse while body in {}: {}", context.scope, parse_result.error());
return parse_result.release_error();
}
auto [new_execution_flow, node] = parse_result.release_value();
execution_flow = new_execution_flow;
switch (execution_flow)
{
case ExecutionFlow::Normal:
break;
case ExecutionFlow::Break:
break;
case ExecutionFlow::Continue:
break;
case ExecutionFlow::Return:
ASSERT(node.has_value());
return_value = node.release_value();
break;
}
}
if (execution_flow == ExecutionFlow::Continue)
execution_flow = ExecutionFlow::Normal;
}
context.aml_data = old_aml_data;
if (execution_flow == ExecutionFlow::Break)
execution_flow = ExecutionFlow::Normal;
return ExecutionFlowResult {
.elem1 = execution_flow,
.elem2 = BAN::move(return_value),
};
}
static BAN::ErrorOr<Node> parse_load_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_load_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::LoadOp);
context.aml_data = context.aml_data.slice(2);
auto load_name = TRY(parse_name_string(context.aml_data));
auto [named_object_path, named_object] = TRY(Namespace::root_namespace().find_named_object(context.scope, load_name));
Node result;
result.type = Node::Type::Integer;
result.as.integer.value = 0;
if (named_object == nullptr)
dwarnln("Load target does not exist");
else
{
switch (named_object->node.type)
{
case Node::Type::Buffer:
{
auto data = BAN::ConstByteSpan(named_object->node.as.str_buf->bytes, named_object->node.as.str_buf->size);
if (auto ret = Namespace::root_namespace().parse(data); ret.is_error())
dwarnln("Failed to load {}: {}", named_object->node, ret.error());
else
result.as.integer.value = BAN::numeric_limits<uint64_t>::max();
break;
}
default:
dwarnln("TODO: Load({}): {}", named_object_path, named_object->node);
return BAN::Error::from_errno(ENOTSUP);
}
}
TRY(store_into_target(context, result));
return result;
}
BAN::ErrorOr<Node> parse_timer_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_load_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::TimerOp);
context.aml_data = context.aml_data.slice(2);
Node result;
result.type = Node::Type::Integer;
result.as.integer.value = SystemTimer::get().ns_since_boot() / 100;
return result;
}
BAN::ErrorOr<Node> evaluate_node(const Scope& node_path, const Node& node)
{
switch (node.type)
{
case Node::Type::Uninitialized:
case Node::Type::Debug:
case Node::Type::OpRegion:
case Node::Type::Event:
case Node::Type::Device:
case Node::Type::Processor:
case Node::Type::PowerResource:
case Node::Type::ThermalZone:
case Node::Type::Mutex:
case Node::Type::PredefinedScope:
case Node::Type::Count:
break;
case Node::Type::Integer:
case Node::Type::String:
case Node::Type::Package:
case Node::Type::Buffer:
case Node::Type::Index:
case Node::Type::Reference:
return TRY(node.copy());
case Node::Type::BufferField:
dwarnln("TODO: evaluate BufferField");
return BAN::Error::from_errno(ENOTSUP);
case Node::Type::FieldUnit:
return convert_from_field_unit(node, ConvInteger | ConvBuffer, sizeof(uint64_t));
case Node::Type::Method:
if (node.as.method.arg_count != 0)
return BAN::Error::from_errno(EFAULT);
return TRY(method_call(node_path, node, {}));
}
dwarnln("evaluate {}", node);
return BAN::Error::from_errno(EINVAL);
}
static BAN::ErrorOr<Node> method_call_impl(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "method_call '{}'", context.scope);
if (context.call_depth >= 128)
{
dwarnln("Terminating recursive method call");
Node return_value {};
return_value.type = Node::Type::Integer;
return_value.as.integer.value = 0;
return return_value;
}
TRY(context.allocate_locals());
Node return_value {};
context.call_depth++;
while (!context.aml_data.empty() && return_value.type == Node::Type::Uninitialized)
{
auto [execution_flow, node] = TRY(parse_node_or_execution_flow(context));
switch (execution_flow)
{
case ExecutionFlow::Normal:
break;
case ExecutionFlow::Break:
dwarnln("BreakOp in method");
return BAN::Error::from_errno(EINVAL);
case ExecutionFlow::Continue:
dwarnln("ContinueOp in method");
return BAN::Error::from_errno(EINVAL);
case ExecutionFlow::Return:
ASSERT(node.has_value());
return_value = node.release_value();
break;
}
}
context.call_depth--;
while (!context.created_nodes.empty())
{
TRY(Namespace::root_namespace().remove_named_object(context.created_nodes.back()));
context.created_nodes.pop_back();
}
// In the absence of an explicit Return () statement, the return value to the caller is undefined.
// We will just return 0 to keep things simple :)
if (return_value.type == Node::Type::Uninitialized)
{
return_value.type = Node::Type::Integer;
return_value.as.integer.value = 0;
}
return return_value;
}
BAN::ErrorOr<Node> method_call(const Scope& scope, const Node& method_node, BAN::Array<Reference*, 7>&& args, uint32_t call_depth)
{
ASSERT(method_node.type == Node::Type::Method);
size_t argc = 0;
for (argc = 0; argc < args.size(); argc++)
if (args[argc] == nullptr)
break;
for (size_t i = argc; i < args.size(); i++)
ASSERT(args[i] == nullptr);
const auto& method = method_node.as.method;
if (argc != method.arg_count)
{
dwarnln("{} takes {} arguments but {} were provided", scope, method.arg_count, argc);
return BAN::Error::from_errno(EINVAL);
}
if (method.override_func)
return method.override_func(args);
if (method.serialized)
{
ASSERT(method.mutex);
method.mutex->mutex.lock();
}
// FIXME: I'm pretty sure arguments are leaking memory
ParseContext context;
context.scope = TRY(scope.copy());
context.aml_data = BAN::ConstByteSpan(method.start, method.length);
context.call_depth = call_depth;
for (size_t i = 0; i < args.size(); i++)
context.args[i] = args[i];
auto result = method_call_impl(context);
if (method.serialized)
method.mutex->mutex.unlock();
return result;
}
BAN::ErrorOr<Node> parse_node(ParseContext& context, bool return_ref)
{
if (context.aml_data.empty())
return BAN::Error::from_errno(ENODATA);
if (context.aml_data[0] == static_cast<uint8_t>(AML::Byte::ExtOpPrefix))
{
if (context.aml_data.size() < 2)
return BAN::Error::from_errno(ENODATA);
const uint8_t opcode = context.aml_data[1];
switch (static_cast<AML::ExtOp>(opcode))
{
case AML::ExtOp::CondRefOfOp:
return TRY(parse_condrefof_op(context));
case AML::ExtOp::AcquireOp:
return TRY(parse_acquire_op(context));
case AML::ExtOp::LoadOp:
return TRY(parse_load_op(context));
case AML::ExtOp::TimerOp:
return TRY(parse_timer_op(context));
case AML::ExtOp::WaitOp:
return TRY(parse_wait_op(context));
case AML::ExtOp::DebugOp:
{
context.aml_data = context.aml_data.slice(2);
Node debug;
debug.type = Node::Type::Debug;
return BAN::move(debug);
}
default:
break;
}
dwarnln("TODO: AML opcode {2H}e", opcode);
return BAN::Error::from_errno(ENOTSUP);
}
const uint8_t opcode = context.aml_data[0];
switch (static_cast<AML::Byte>(opcode))
{
case AML::Byte::ZeroOp:
case AML::Byte::OneOp:
case AML::Byte::OnesOp:
case AML::Byte::BytePrefix:
case AML::Byte::WordPrefix:
case AML::Byte::DWordPrefix:
case AML::Byte::QWordPrefix:
return TRY(parse_integer(context.aml_data));
case AML::Byte::StringPrefix:
return TRY(parse_string(context.aml_data));
case AML::Byte::BufferOp:
return TRY(parse_buffer_op(context));
case AML::Byte::PackageOp:
case AML::Byte::VarPackageOp:
return TRY(parse_package_op(context));
case AML::Byte::SizeOfOp:
return TRY(parse_sizeof_op(context));
case AML::Byte::RefOfOp:
return TRY(parse_refof_op(context));
case AML::Byte::DerefOfOp:
return TRY(parse_derefof_op(context));
case AML::Byte::StoreOp:
return TRY(parse_store_op(context));
case AML::Byte::CopyObjectOp:
return TRY(parse_copy_object_op(context));
case AML::Byte::ConcatOp:
return TRY(parse_concat_op(context));
case AML::Byte::MidOp:
return TRY(parse_mid_op(context));
case AML::Byte::IndexOp:
return TRY(parse_index_op(context));
case AML::Byte::ObjectTypeOp:
return TRY(parse_object_type_op(context));
case AML::Byte::ToBufferOp:
case AML::Byte::ToDecimalStringOp:
case AML::Byte::ToHexStringOp:
case AML::Byte::ToIntegerOp:
return TRY(parse_explicit_conversion(context));
case AML::Byte::ToStringOp:
return TRY(parse_to_string_op(context));
case AML::Byte::IncrementOp:
case AML::Byte::DecrementOp:
return TRY(parse_inc_dec_op(context));
case AML::Byte::NotOp:
return TRY(parse_unary_integer_op(context));
case AML::Byte::AddOp:
case AML::Byte::SubtractOp:
case AML::Byte::MultiplyOp:
case AML::Byte::DivideOp:
case AML::Byte::ShiftLeftOp:
case AML::Byte::ShiftRightOp:
case AML::Byte::AndOp:
case AML::Byte::NandOp:
case AML::Byte::OrOp:
case AML::Byte::NorOp:
case AML::Byte::XorOp:
case AML::Byte::ModOp:
return TRY(parse_binary_integer_op(context));
case AML::Byte::LAndOp:
case AML::Byte::LEqualOp:
case AML::Byte::LGreaterOp:
case AML::Byte::LLessOp:
case AML::Byte::LNotOp:
case AML::Byte::LOrOp:
return TRY(parse_logical_op(context));
case AML::Byte::Local0:
case AML::Byte::Local1:
case AML::Byte::Local2:
case AML::Byte::Local3:
case AML::Byte::Local4:
case AML::Byte::Local5:
case AML::Byte::Local6:
case AML::Byte::Local7:
{
const uint8_t local_index = opcode - static_cast<uint8_t>(AML::Byte::Local0);
context.aml_data = context.aml_data.slice(1);
if (context.locals[local_index]->node.type == Node::Type::Uninitialized)
{
dwarnln("Trying to access uninitialized local");
return BAN::Error::from_errno(EINVAL);
}
if (!return_ref)
return TRY(context.locals[local_index]->node.copy());
Node reference;
reference.type = Node::Type::Reference;
reference.as.reference = context.locals[local_index];
reference.as.reference->ref_count++;
return reference;
}
case AML::Byte::Arg0:
case AML::Byte::Arg1:
case AML::Byte::Arg2:
case AML::Byte::Arg3:
case AML::Byte::Arg4:
case AML::Byte::Arg5:
case AML::Byte::Arg6:
{
const uint8_t arg_index = opcode - static_cast<uint8_t>(AML::Byte::Arg0);
context.aml_data = context.aml_data.slice(1);
if (context.args[arg_index] == nullptr || context.args[arg_index]->node.type == Node::Type::Uninitialized)
{
dwarnln("Trying to access uninitialized arg");
return BAN::Error::from_errno(EINVAL);
}
if (!return_ref)
return TRY(context.args[arg_index]->node.copy());
Node reference;
reference.type = Node::Type::Reference;
reference.as.reference = context.args[arg_index];
reference.as.reference->ref_count++;
return reference;
}
default:
break;
}
if (!is_name_string_start(context.aml_data[0]))
{
dwarnln("TODO: AML opcode {2H}", opcode);
return BAN::Error::from_errno(ENOTSUP);
}
auto name_string = TRY(parse_name_string(context.aml_data));
auto [object_scope, named_object] = TRY(Namespace::root_namespace().find_named_object(context.scope, name_string));
if (named_object == nullptr)
{
dwarnln("could not find '{}'.'{}'", context.scope, name_string);
return BAN::Error::from_errno(ENOENT);
}
if (named_object->node.type == Node::Type::Method)
{
BAN::Array<Reference*, 7> args;
for (size_t i = 0; i < named_object->node.as.method.arg_count; i++)
{
auto temp = TRY(parse_node(context));
if (temp.type == Node::Type::Reference)
{
args[i] = temp.as.reference;
args[i]->ref_count++;
}
else
{
args[i] = new Reference();
if (args[i] == nullptr)
return BAN::Error::from_errno(ENOMEM);
args[i]->node = BAN::move(temp);
args[i]->ref_count = 1;
}
}
return TRY(method_call(BAN::move(object_scope), named_object->node, BAN::move(args), context.call_depth));
}
if (!return_ref)
return TRY(named_object->node.copy());
Node reference;
reference.type = Node::Type::Reference;
reference.as.reference = named_object;
reference.as.reference->ref_count++;
return reference;
}
// FIXME: WHY TF IS THIS USING ALMOST 2 KiB of stack
#pragma GCC diagnostic push
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wstack-usage="
#endif
BAN::ErrorOr<ExecutionFlowResult> parse_node_or_execution_flow(ParseContext& context)
{
if (context.aml_data.empty())
return BAN::Error::from_errno(ENODATA);
auto dummy_return = ExecutionFlowResult {
.elem1 = ExecutionFlow::Normal,
.elem2 = BAN::Optional<Node>(),
};
if (context.aml_data[0] == static_cast<uint8_t>(AML::Byte::ExtOpPrefix))
{
if (context.aml_data.size() < 2)
return BAN::Error::from_errno(ENODATA);
switch (static_cast<AML::ExtOp>(context.aml_data[1]))
{
case AML::ExtOp::MutexOp:
TRY(parse_mutex_op(context));
return dummy_return;
case AML::ExtOp::FatalOp:
TRY(parse_fatal_op(context));
return dummy_return;
case AML::ExtOp::EventOp:
TRY(parse_event_op(context));
return dummy_return;
case AML::ExtOp::ResetOp:
case AML::ExtOp::SignalOp:
TRY(parse_reset_signal_op(context));
return dummy_return;
case AML::ExtOp::CreateFieldOp:
TRY(parse_createfield_op(context));
return dummy_return;
case AML::ExtOp::SleepOp:
TRY(parse_sleep_op(context));
return dummy_return;
case AML::ExtOp::StallOp:
TRY(parse_stall_op(context));
return dummy_return;
case AML::ExtOp::ReleaseOp:
TRY(parse_release_op(context));
return dummy_return;
case AML::ExtOp::OpRegionOp:
TRY(parse_opregion_op(context));
return dummy_return;
case AML::ExtOp::FieldOp:
TRY(parse_field_op(context));
return dummy_return;
case AML::ExtOp::IndexFieldOp:
TRY(parse_index_field_op(context));
return dummy_return;
case AML::ExtOp::BankFieldOp:
TRY(parse_bank_field_op(context));
return dummy_return;
case AML::ExtOp::DeviceOp:
TRY(parse_device_op(context));
return dummy_return;
case AML::ExtOp::ProcessorOp:
TRY(parse_processor_op(context));
return dummy_return;
case AML::ExtOp::PowerResOp:
TRY(parse_power_resource_op(context));
return dummy_return;
case AML::ExtOp::ThermalZoneOp:
TRY(parse_thermal_zone_op(context));
return dummy_return;
default:
break;
}
}
switch (static_cast<AML::Byte>(context.aml_data[0]))
{
case AML::Byte::AliasOp:
TRY(parse_alias_op(context));
return dummy_return;
case AML::Byte::NameOp:
TRY(parse_name_op(context));
return dummy_return;
case AML::Byte::MethodOp:
TRY(parse_method_op(context));
return dummy_return;
case AML::Byte::NoopOp:
case AML::Byte::BreakPointOp:
context.aml_data = context.aml_data.slice(1);
return dummy_return;
case AML::Byte::ScopeOp:
TRY(parse_scope_op(context));
return dummy_return;
case AML::Byte::NotifyOp:
TRY(parse_notify_op(context));
return dummy_return;
case AML::Byte::CreateBitFieldOp:
case AML::Byte::CreateByteFieldOp:
case AML::Byte::CreateWordFieldOp:
case AML::Byte::CreateDWordFieldOp:
case AML::Byte::CreateQWordFieldOp:
TRY(parse_createfield_op(context));
return dummy_return;
case AML::Byte::IfOp:
return parse_if_op(context);
case AML::Byte::WhileOp:
return parse_while_op(context);
case AML::Byte::BreakOp:
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_break_op");
context.aml_data = context.aml_data.slice(1);
return ExecutionFlowResult {
.elem1 = ExecutionFlow::Break,
.elem2 = BAN::Optional<Node>(),
};
case AML::Byte::ContinueOp:
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_continue_op");
context.aml_data = context.aml_data.slice(1);
return ExecutionFlowResult {
.elem1 = ExecutionFlow::Continue,
.elem2 = BAN::Optional<Node>(),
};
case AML::Byte::ReturnOp:
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_return_op");
context.aml_data = context.aml_data.slice(1);
return ExecutionFlowResult {
.elem1 = ExecutionFlow::Return,
.elem2 = TRY(parse_node(context)),
};
}
default:
break;
}
auto node = TRY(parse_node(context));
return ExecutionFlowResult {
.elem1 = ExecutionFlow::Normal,
.elem2 = BAN::move(node)
};
}
#pragma GCC diagnostic pop
BAN::ErrorOr<NameString> NameString::from_string(BAN::StringView name)
{
NameString result;
if (name.front() == '\\')
{
result.base = base_root;
name = name.substring(1);
}
else
{
for (size_t i = 0; i < name.size() && name[i] == '^'; i++)
result.base++;
name = name.substring(result.base);
}
if (name.empty())
return result;
ASSERT((name.size() % 5) == 4);
TRY(result.parts.reserve((name.size() / 5) + 1));
while (!name.empty())
{
const uint32_t name_seg {
(static_cast<uint32_t>(name[0]) << 0) |
(static_cast<uint32_t>(name[1]) << 8) |
(static_cast<uint32_t>(name[2]) << 16) |
(static_cast<uint32_t>(name[3]) << 24)
};
TRY(result.parts.push_back(name_seg));
name = name.substring(4);
if (!name.empty())
{
ASSERT(name.front() == '.');
name = name.substring(1);
}
}
return result;
}
ParseContext::~ParseContext()
{
for (auto*& local : locals)
{
if (local && --local->ref_count == 0)
delete local;
local = nullptr;
}
for (auto*& arg : args)
{
if (arg && --arg->ref_count == 0)
delete arg;
arg = nullptr;
}
}
BAN::ErrorOr<void> ParseContext::allocate_locals()
{
for (auto*& local : locals)
{
ASSERT(local == nullptr);
local = new Reference();
if (local == nullptr)
return BAN::Error::from_errno(ENOMEM);
local->ref_count = 1;
}
return {};
}
BAN::ErrorOr<Node> Node::copy() const
{
Node result {};
result.type = this->type;
switch (this->type)
{
case Type::Uninitialized:
break;
case Type::Debug:
break;
case Type::Integer:
result.as.integer.value = this->as.integer.value;
break;
case Type::String:
case Type::Buffer:
ASSERT(this->as.str_buf);
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + this->as.str_buf->size));
if (result.as.str_buf == nullptr)
return BAN::Error::from_errno(ENOMEM);
memcpy(result.as.str_buf->bytes, this->as.str_buf->bytes, this->as.str_buf->size);
result.as.str_buf->size = this->as.str_buf->size;
result.as.str_buf->ref_count = 1;
break;
case Type::Package:
ASSERT(this->as.package);
result.as.package = static_cast<Package*>(kmalloc(sizeof(Package) + this->as.package->num_elements * sizeof(Package::Element)));
if (result.as.package == nullptr)
return BAN::Error::from_errno(ENOMEM);
result.as.package->num_elements = this->as.package->num_elements;
result.as.package->ref_count = 1;
for (size_t i = 0; i < result.as.package->num_elements; i++)
{
auto& dst_elem = result.as.package->elements[i];
const auto& src_elem = this->as.package->elements[i];
if (src_elem.resolved)
{
dst_elem.resolved = true;
dst_elem.value.node = nullptr;
if (src_elem.value.node)
{
dst_elem.value.node = new Node();
if (dst_elem.value.node == nullptr)
return BAN::Error::from_errno(ENOMEM);
*dst_elem.value.node = TRY(src_elem.value.node->copy());
}
}
else
{
dst_elem.resolved = false;
dst_elem.value.location = nullptr;
if (src_elem.value.location)
{
dst_elem.value.location = new Package::Element::Location();
if (dst_elem.value.location == nullptr)
return BAN::Error::from_errno(ENOMEM);
dst_elem.value.location->name = TRY(src_elem.value.location->name.copy());
dst_elem.value.location->scope = TRY(src_elem.value.location->scope.copy());
}
}
}
break;
case Type::BufferField:
result.as.buffer_field = this->as.buffer_field;
result.as.buffer_field.buffer->ref_count++;
break;
case Type::OpRegion:
result.as.opregion = this->as.opregion;
break;
case Type::FieldUnit:
result.as.field_unit = this->as.field_unit;
switch (result.as.field_unit.type)
{
case FieldUnit::Type::Field:
break;
case FieldUnit::Type::IndexField:
result.as.field_unit.as.index_field.index->ref_count++;
result.as.field_unit.as.index_field.data->ref_count++;
break;
case FieldUnit::Type::BankField:
result.as.field_unit.as.bank_field.bank_selector->ref_count++;
break;
default:
ASSERT_NOT_REACHED();
}
break;
case Type::Method:
result.as.method = this->as.method;
if (result.as.method.mutex)
result.as.method.mutex->ref_count++;
break;
case Type::Index:
switch (this->as.index.type)
{
case Node::Type::String:
case Node::Type::Buffer:
ASSERT(this->as.index.as.str_buf);
result.as.index.as.str_buf = this->as.index.as.str_buf;
result.as.index.as.str_buf->ref_count++;
break;
case Node::Type::Package:
ASSERT(this->as.index.as.package);
result.as.index.as.package = this->as.index.as.package;
result.as.index.as.package->ref_count++;
break;
default: ASSERT_NOT_REACHED();
}
result.as.index.type = this->as.index.type;
result.as.index.index = this->as.index.index;
break;
case Type::Reference:
ASSERT(this->as.reference);
result.as.reference = this->as.reference;
result.as.reference->ref_count++;
break;
case Type::Event:
dwarnln("Copy Event");
return BAN::Error::from_errno(EINVAL);
case Type::Device:
dwarnln("Copy Device");
return BAN::Error::from_errno(EINVAL);
case Type::Processor:
dwarnln("Copy Processor");
return BAN::Error::from_errno(EINVAL);
case Type::PowerResource:
dwarnln("Copy PowerResource");
return BAN::Error::from_errno(EINVAL);
case Type::ThermalZone:
dwarnln("Copy ThremalZone");
return BAN::Error::from_errno(EINVAL);
case Type::Mutex:
dwarnln("Copy Mutex");
return BAN::Error::from_errno(EINVAL);
case Type::PredefinedScope:
dwarnln("Copy Scope");
return BAN::Error::from_errno(EINVAL);
case Type::Count:
ASSERT_NOT_REACHED();
}
return result;
}
Node& Node::operator=(Node&& other)
{
clear();
switch (other.type)
{
case Type::Uninitialized:
break;
case Type::Debug:
break;
case Type::Integer:
this->as.integer = other.as.integer;
other.as.integer = {};
break;
case Type::String:
case Type::Buffer:
this->as.str_buf = other.as.str_buf;
other.as.str_buf = {};
break;
case Type::Package:
this->as.package = other.as.package;
other.as.package = {};
break;
case Type::BufferField:
this->as.buffer_field = other.as.buffer_field;
other.as.buffer_field = {};
break;
case Type::OpRegion:
this->as.opregion = other.as.opregion;
other.as.opregion = {};
break;
case Type::FieldUnit:
this->as.field_unit = other.as.field_unit;
other.as.field_unit = {};
break;
case Type::Event:
break;
case Type::Device:
break;
case Type::Processor:
break;
case Type::PowerResource:
break;
case Type::ThermalZone:
break;
case Type::Method:
this->as.method = other.as.method;
other.as.method = {};
break;
case Type::Mutex:
this->as.mutex = other.as.mutex;
other.as.mutex = {};
break;
case Type::Index:
this->as.index = other.as.index;
other.as.index = {};
break;
case Type::Reference:
this->as.reference = other.as.reference;
other.as.reference = {};
break;
case Type::PredefinedScope:
break;
case Type::Count:
ASSERT_NOT_REACHED();
}
this->type = other.type;
other.type = Node::Type::Uninitialized;
return *this;
}
static void deref_package(Package* package)
{
if (package == nullptr || --package->ref_count != 0)
return;
for (size_t i = 0; i < package->num_elements; i++)
{
auto& elem = package->elements[i];
if (elem.resolved)
{
if (elem.value.node)
delete elem.value.node;
elem.value.node = nullptr;
}
else
{
if (elem.value.location)
delete elem.value.location;
elem.value.location = nullptr;
}
}
kfree(package);
}
void Node::clear()
{
switch (this->type)
{
case Type::Uninitialized:
break;
case Type::Debug:
break;
case Type::Integer:
break;
case Type::String:
case Type::Buffer:
if (this->as.str_buf && --this->as.str_buf->ref_count == 0)
kfree(this->as.str_buf);
this->as.str_buf = {};
break;
case Type::Package:
deref_package(this->as.package);
this->as.package = {};
break;
case Type::BufferField:
if (this->as.buffer_field.buffer && --this->as.buffer_field.buffer->ref_count == 0)
delete this->as.buffer_field.buffer;
this->as.buffer_field = {};
break;
case Type::OpRegion:
this->as.opregion = {};
break;
case Type::FieldUnit:
switch (this->as.field_unit.type)
{
case FieldUnit::Type::Field:
break;
case FieldUnit::Type::IndexField:
if (--this->as.field_unit.as.index_field.index->ref_count == 0)
delete this->as.field_unit.as.index_field.index;
if (--this->as.field_unit.as.index_field.data->ref_count == 0)
delete this->as.field_unit.as.index_field.data;
break;
case FieldUnit::Type::BankField:
if (--this->as.field_unit.as.bank_field.bank_selector->ref_count == 0)
delete this->as.field_unit.as.bank_field.bank_selector;
break;
default:
ASSERT_NOT_REACHED();
}
this->as.field_unit = {};
break;
case Type::Event:
break;
case Type::Device:
break;
case Type::Processor:
break;
case Type::PowerResource:
break;
case Type::ThermalZone:
break;
case Type::Method:
if (this->as.method.mutex && --this->as.method.mutex->ref_count == 0)
delete this->as.method.mutex;
this->as.method = {};
break;
case Type::Mutex:
if (this->as.mutex && --this->as.mutex->ref_count == 0)
delete this->as.mutex;
this->as.mutex = {};
break;
case Type::Index:
switch (this->as.index.type)
{
case Type::Uninitialized:
break;
case Type::String:
case Type::Buffer:
if (this->as.index.as.str_buf && --this->as.index.as.str_buf->ref_count == 0)
kfree(this->as.index.as.str_buf);
break;
case Type::Package:
deref_package(this->as.index.as.package);
break;
default: ASSERT_NOT_REACHED();
}
this->as.index = {};
break;
case Type::Reference:
if (this->as.reference && --this->as.reference->ref_count == 0)
delete this->as.reference;
this->as.reference = {};
break;
case Type::PredefinedScope:
break;
case Type::Count:
ASSERT_NOT_REACHED();
}
this->type = Type::Uninitialized;
}
}