#include #include #include #include #include #include #include #include #include #include namespace Kernel::ACPI::AML { static constexpr uint64_t ONES = BAN::numeric_limits::max(); static constexpr uint64_t ZERO = BAN::numeric_limits::min(); BAN::ErrorOr 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(); aml_data = aml_data.slice(4); } return name; } static BAN::ErrorOr 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_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(aml_data[i]) << (i * 8); aml_data = aml_data.slice(byte_count); } return result; } static BAN::ErrorOr parse_string(BAN::ConstByteSpan& aml_data) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_string"); ASSERT(!aml_data.empty()); ASSERT(static_cast(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(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().data(), len); aml_data = aml_data.slice(len + 1); return result; } BAN::ErrorOr 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; } BAN::ErrorOr 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 parse_package_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_package_op"); ASSERT(!context.aml_data.empty()); const auto opcode = static_cast(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: if (context.aml_data.empty()) return BAN::Error::from_errno(ENODATA); num_elements = context.aml_data[0]; context.aml_data = context.aml_data.slice(1); break; case AML::Byte::VarPackageOp: num_elements = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value; break; default: ASSERT_NOT_REACHED(); } Node result {}; result.type = Node::Type::Package; result.as.package = static_cast(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 parse_buffer_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_buffer_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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(buffer_size_node.as.integer.value, context.aml_data.size()); Node result {}; result.type = Node::Type::Buffer; result.as.str_buf = static_cast(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; } // FIXME: WHY TF IS THIS USING OVER 1 KiB of stack #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstack-usage=" static BAN::ErrorOr parse_logical_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_logical_op"); ASSERT(!context.aml_data.empty()); const auto opcode = static_cast(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; } #pragma GCC diagnostic pop static BAN::ErrorOr parse_index_op(ParseContext& context); enum class TargetType { Reference, Local, Arg, Debug }; using SuperNameResult = Pair; static BAN::ErrorOr 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_data[0]); const auto ext_opcode = static_cast(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(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(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 requires (BAN::is_same_v || BAN::is_same_v) static BAN::ErrorOr 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 convert_node(Node&& source, uint8_t conversion, uint64_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(source.as.str_buf->size, sizeof(uint64_t)) ); return result; case Node::Type::BufferField: for (size_t i = 0; i < BAN::Math::min(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(max_length, BAN::Math::ilog2(source.as.integer.value) / 4 + 1); else max_length = 1; result.as.str_buf = static_cast(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(max_length, source.as.str_buf->size + 1); result.as.str_buf = static_cast(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(max_length, string.size()); result.as.str_buf = static_cast(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 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 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(&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(8 - i_mod, bit_count - i); const uint8_t max_dst_bits = BAN::Math::min(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 {}; } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstack-usage=" static BAN::ErrorOr 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 {}; } #pragma GCC diagnostic pop static BAN::ErrorOr 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 parse_store_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_store_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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 parse_copy_object_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_copy_object_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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 parse_index_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_index_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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 parse_object_type_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_object_type_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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 bool match_compare(const Package::Element& element, uint8_t opcode, uint64_t target) { if (!element.resolved || element.value.node == nullptr) return false; auto element_copy = element.value.node->copy(); if (element_copy.is_error()) return false; auto element_conv = convert_node(element_copy.release_value(), ConvInteger, sizeof(uint64_t)); if (element_conv.is_error()) return false; switch (opcode) { case 0: return true; case 1: return element_conv.value().as.integer.value == target; case 2: return element_conv.value().as.integer.value <= target; case 3: return element_conv.value().as.integer.value < target; case 4: return element_conv.value().as.integer.value >= target; case 5: return element_conv.value().as.integer.value > target; } return false; } static BAN::ErrorOr parse_match_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_match_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::MatchOp); context.aml_data = context.aml_data.slice(1); auto search = TRY(parse_node(context)); if (search.type != Node::Type::Package) { dwarnln("Match search package is {}", search); return BAN::Error::from_errno(EINVAL); } if (context.aml_data.empty()) return BAN::Error::from_errno(ENODATA); const uint8_t opcode1 = context.aml_data[0]; const uint64_t operand1 = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value; if (context.aml_data.empty()) return BAN::Error::from_errno(ENODATA); const uint8_t opcode2 = context.aml_data[0]; const uint64_t operand2 = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value; const uint64_t start_idx = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value; Node result; result.type = Node::Type::Integer; result.as.integer.value = ONES; for (uint64_t i = start_idx; i < search.as.package->num_elements; i++) { auto& element = search.as.package->elements[i]; if (!element.resolved) TRY(resolve_package_element(element, false)); if (!match_compare(element, opcode1, operand1)) continue; if (!match_compare(element, opcode2, operand2)) continue; result.as.integer.value = i; break; } return result; } static BAN::ErrorOr 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 parse_createfield_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_createfield_op"); ASSERT(!context.aml_data.empty()); const auto opcode = static_cast(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(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_ref = TRY(parse_node(context, true)); auto* buffer_node = &buffer_node_ref; if (buffer_node->type == Node::Type::Reference) buffer_node = &buffer_node->as.reference->node; if (buffer_node->type != Node::Type::Buffer) { dwarnln("CreateField buffer is {}", buffer_node); return BAN::Error::from_errno(EINVAL); } 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; auto absolte_path = TRY(Namespace::root_namespace().add_named_object(context, field_name_string, BAN::move(buffer_field))); if (absolte_path.parts.empty()) { dwarnln("Could not add Buffer Field '{}'.'{}' to namespace", context.scope, field_name_string); return {}; } return {}; } static BAN::ErrorOr parse_sizeof_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_sizeof_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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 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 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(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 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(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 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(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 parse_find_set_bit_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_find_set_bit_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(opcode)) { case AML::Byte::FindSetLeftBitOp: function = [](uint64_t a) -> uint64_t { for (int i = 63; i >= 0; i--) if ((a >> i) & 1) return i + 1; return 0; }; break; case AML::Byte::FindSetRightBitOp: function = [](uint64_t a) -> uint64_t { for (int i = 0; i < 64; i++) if ((a >> i) & 1) return i + 1; return 0; }; 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 parse_mid_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_mid_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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(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 parse_concat_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_concat_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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(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; } // FIXME: WHY TF IS THIS USING OVER 1 KiB of stack #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstack-usage=" static BAN::ErrorOr 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(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(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(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 { 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(value, base) || BAN::Math::will_addition_overflow(value * base, to_add.value())) { value = BAN::numeric_limits::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; } #pragma GCC diagnostic pop static BAN::ErrorOr parse_to_string_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_to_string_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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(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 parse_alias_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_alias_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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, object_name_string, source_ref)); return {}; } static BAN::ErrorOr parse_name_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_name_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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 absolute_path = TRY(Namespace::root_namespace().add_named_object(context, name_string, BAN::move(object))); if (absolute_path.parts.empty()) { dwarnln("Could not add Name '{}'.'{}' to namespace", context.scope, name_string); return {}; } return {}; } static BAN::ErrorOr parse_refof_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_refof_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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 parse_condrefof_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_cond_refof_op"); ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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 parse_derefof_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_derefof_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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 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(execution_flow)); return BAN::Error::from_errno(EINVAL); } } return {}; } static BAN::ErrorOr parse_scope_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_scope_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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 parse_notify_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_scope_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::NotifyOp); context.aml_data = context.aml_data.slice(1); auto [obj_path, obj_ref] = TRY(parse_super_name(context, true)); auto value = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))); if (obj_ref == nullptr) return {}; dwarnln("TODO: handle notify({}, {})", obj_ref->node, value); return {}; } static BAN::ErrorOr parse_event_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_device_op"); ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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, event_name, BAN::move(event_node))); if (absolute_path.parts.empty()) { dwarnln("Could not add Event '{}'.'{}' to namespace", context.scope, event_name); return {}; } return {}; } static BAN::ErrorOr 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(context.aml_data[0]) == AML::Byte::ExtOpPrefix); const auto opcode = static_cast(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 parse_wait_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_wait_op"); ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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::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 parse_sleep_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_sleep_op"); ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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 parse_stall_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_stall_op"); ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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 parse_device_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_device_op"); ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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, 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 parse_processor_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_processor_op"); ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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, 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 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(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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, power_resource_name, BAN::move(power_resource_node))); if (absolute_path.parts.empty()) { dwarnln("Could not add Power Resource '{}'.'{}' to namespace", context.scope, power_resource_name); return {}; } TRY(parse_scope_contents(BAN::move(absolute_path), power_resource_pkg)); return {}; } static BAN::ErrorOr 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(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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, 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 parse_mutex_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_mutex_op"); ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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; auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, mutex_name, BAN::move(mutex))); if (absolute_path.parts.empty()) { dwarnln("Could not add Mutex '{}'.'{}' to namespace", context.scope, mutex); return {}; } return {}; } static BAN::ErrorOr parse_fatal_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_fatal_op"); ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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 parse_acquire_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_acquire_op"); ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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(); context.aml_data = context.aml_data.slice(2); Node result; result.type = Node::Type::Integer; result.as.integer.value = BAN::numeric_limits::max(); const uint64_t wake_time_ms = (timeout_ms >= 0xFFFF) ? BAN::numeric_limits::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 parse_release_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_release_op"); ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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 parse_method_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_method_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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 absolute_path = TRY(Namespace::root_namespace().add_named_object(context, method_name, BAN::move(method_node))); if (absolute_path.parts.empty()) { dwarnln("Could not add Method '{}'.'{}' to namespace", context.scope, method_name); return {}; } return {}; } static BAN::ErrorOr parse_if_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_if_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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(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 parse_while_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_while_op"); ASSERT(!context.aml_data.empty()); ASSERT(static_cast(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 parse_load_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_load_op"); ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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::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 parse_timer_op(ParseContext& context) { dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_load_op"); ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); ASSERT(static_cast(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 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, BAN::Array{})); } dwarnln("evaluate {}", node); return BAN::Error::from_errno(EINVAL); } static BAN::ErrorOr 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 method_call(const Scope& scope, const Node& method_node, BAN::Array&& 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 method_call(const Scope& scope, const Node& method, Node&& arg0, Node&& arg1, Node&& arg2, Node&& arg3, Node&& arg4, Node&& arg5, Node&& arg6) { BAN::Array args(nullptr); #define INIT_ARGn(n) \ if (arg ## n.type != Node::Type::Uninitialized) \ { \ args[n] = new Reference(); \ if (args[n] == nullptr) \ return BAN::Error::from_errno(ENOMEM); \ args[n]->ref_count = 1; \ args[n]->node = BAN::move(arg ## n); \ } INIT_ARGn(0); INIT_ARGn(1); INIT_ARGn(2); INIT_ARGn(3); INIT_ARGn(4); INIT_ARGn(5); INIT_ARGn(6); #undef INIT_ARGn return method_call(scope, method, BAN::move(args)); } // FIXME: WHY TF IS THIS USING OVER 2 KiB of stack #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstack-usage=" BAN::ErrorOr 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(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(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(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::MatchOp: return TRY(parse_match_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::FindSetLeftBitOp: case AML::Byte::FindSetRightBitOp: return TRY(parse_find_set_bit_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(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(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 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; } #pragma GCC diagnostic pop // FIXME: WHY TF IS THIS USING ALMOST 2 KiB of stack #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstack-usage=" BAN::ErrorOr 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(), }; if (context.aml_data[0] == static_cast(AML::Byte::ExtOpPrefix)) { if (context.aml_data.size() < 2) return BAN::Error::from_errno(ENODATA); switch (static_cast(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(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(), }; 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(), }; 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::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(name[0]) << 0) | (static_cast(name[1]) << 8) | (static_cast(name[2]) << 16) | (static_cast(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 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::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(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(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; } }