Kernel: Replace the old AML interpreter by a new, better one

The old AML interpreter was trash and did not follow value/reference
semantics at all. It was also super slow, one of my machines taking over
7 seconds to parse ACPI namespace and call _INI and _STA.
This commit is contained in:
2024-12-12 07:03:09 +02:00
parent 463bb72d24
commit 869f4011a1
55 changed files with 5230 additions and 5913 deletions

View File

@@ -1,13 +1,7 @@
#include <BAN/ScopeGuard.h>
#include <BAN/StringView.h>
#include <kernel/ACPI/ACPI.h>
#include <kernel/ACPI/AML.h>
#include <kernel/ACPI/AML/Alias.h>
#include <kernel/ACPI/AML/Device.h>
#include <kernel/ACPI/AML/Field.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Method.h>
#include <kernel/ACPI/AML/Package.h>
#include <kernel/ACPI/AML/OpRegion.h>
#include <kernel/BootInfo.h>
#include <kernel/InterruptController.h>
#include <kernel/IO.h>
@@ -112,64 +106,62 @@ acpi_release_global_lock:
ASSERT(!acpi_release_global_lock(s_global_lock));
}
static BAN::Optional<AML::FieldRules::AccessType> get_access_type(uint8_t access_size)
static BAN::ErrorOr<uint8_t> get_access_type(uint8_t access_size)
{
switch (access_size)
{
case 0: return AML::FieldRules::AccessType::Any;
case 1: return AML::FieldRules::AccessType::Byte;
case 2: return AML::FieldRules::AccessType::Word;
case 3: return AML::FieldRules::AccessType::DWord;
case 4: return AML::FieldRules::AccessType::QWord;
case 0: return 0;
case 1: return 1;
case 2: return 2;
case 3: return 3;
case 4: return 4;
default:
dwarnln("Unknown access size {}", access_size);
return {};
return BAN::Error::from_errno(EFAULT);
}
}
BAN::Optional<uint64_t> GAS::read()
BAN::ErrorOr<uint64_t> GAS::read()
{
auto access_type = get_access_type(access_size);
if (!access_type.has_value())
return {};
AML::OpRegion opregion;
opregion.address_space = address_space_id;
opregion.offset = address;
opregion.length = 0xFFFFFFFF;
auto op_region = MUST(BAN::RefPtr<AML::OpRegion>::create(""_sv, address_space_id, (uint64_t)address, 0xFFFFFFFF));
AML::Node field_unit;
field_unit.type = AML::Node::Type::FieldUnit;
field_unit.as.field_unit.type = AML::FieldUnit::Type::Field;
field_unit.as.field_unit.as.field.opregion = opregion;
field_unit.as.field_unit.length = register_bit_width;
field_unit.as.field_unit.offset = register_bit_offset;
field_unit.as.field_unit.flags = TRY(get_access_type(access_size));
auto field_rules = AML::FieldRules {
.access_type = access_type.value(),
.lock_rule = AML::FieldRules::LockRule::NoLock,
.update_rule = AML::FieldRules::UpdateRule::Preserve,
.access_attrib = AML::FieldRules::AccessAttrib::Normal,
.access_length = 0
};
auto field_element = MUST(BAN::RefPtr<AML::FieldElement>::create(""_sv, register_bit_offset, register_bit_width, field_rules));
field_element->op_region = op_region;
auto result = field_element->convert(AML::Node::ConvInteger);
if (!result)
return {};
return static_cast<AML::Integer*>(result.ptr())->value;
auto result = TRY(AML::convert_from_field_unit(field_unit, AML::ConvInteger, sizeof(uint64_t)));
return result.as.integer.value;
}
bool GAS::write(uint64_t value)
BAN::ErrorOr<void> GAS::write(uint64_t value)
{
auto access_type = get_access_type(access_size);
if (!access_type.has_value())
return {};
AML::OpRegion opregion;
opregion.address_space = address_space_id;
opregion.offset = address;
opregion.length = 0xFFFFFFFF;
auto op_region = MUST(BAN::RefPtr<AML::OpRegion>::create(""_sv, address_space_id, (uint64_t)address, 0xFFFFFFFF));
AML::Node field_unit;
field_unit.type = AML::Node::Type::FieldUnit;
field_unit.as.field_unit.type = AML::FieldUnit::Type::Field;
field_unit.as.field_unit.as.field.opregion = opregion;
field_unit.as.field_unit.length = register_bit_width;
field_unit.as.field_unit.offset = register_bit_offset;
field_unit.as.field_unit.flags = TRY(get_access_type(access_size));
auto field_rules = AML::FieldRules {
.access_type = access_type.value(),
.lock_rule = AML::FieldRules::LockRule::NoLock,
.update_rule = AML::FieldRules::UpdateRule::Preserve,
.access_attrib = AML::FieldRules::AccessAttrib::Normal,
.access_length = 0
};
auto field_element = MUST(BAN::RefPtr<AML::FieldElement>::create(""_sv, register_bit_offset, register_bit_width, field_rules));
field_element->op_region = op_region;
AML::Node source;
source.type = AML::Node::Type::Integer;
source.as.integer.value = value;
return !!field_element->store(MUST(BAN::RefPtr<AML::Integer>::create(value)));
TRY(AML::store_to_field_unit(source, field_unit));
return {};
}
enum PM1Event : uint16_t
@@ -474,77 +466,81 @@ acpi_release_global_lock:
return nullptr;
}
bool ACPI::prepare_sleep(uint8_t sleep_state)
BAN::ErrorOr<void> ACPI::prepare_sleep(uint8_t sleep_state)
{
auto pts_object = m_namespace->find_object({}, AML::NameString("_PTS"), AML::Namespace::FindMode::ForceAbsolute);
if (pts_object && pts_object->type == AML::Node::Type::Method)
auto [pts_path, pts_object] = TRY(m_namespace->find_named_object({}, MUST(AML::NameString::from_string("\\_PTS"))));
if (pts_object == nullptr)
return {};
auto& pts_node = pts_object->node;
if (pts_node.type != AML::Node::Type::Method)
{
auto* method = static_cast<AML::Method*>(pts_object.ptr());
if (method->arg_count != 1)
{
dwarnln("Method \\_PTS has {} arguments, expected 1", method->arg_count);
return false;
}
if (!method->invoke(MUST(BAN::RefPtr<AML::Integer>::create(sleep_state))).has_value())
{
dwarnln("Failed to evaluate \\_PTS");
return false;
}
dprintln("Executed \\_PTS");
dwarnln("Object \\_PTS is not a method");
return BAN::Error::from_errno(EFAULT);
}
return true;
if (pts_node.as.method.arg_count != 1)
{
dwarnln("Method \\_PTS has {} arguments, expected 1", pts_node.as.method.arg_count);
return BAN::Error::from_errno(EFAULT);
}
AML::Reference arg_ref;
arg_ref.node.type = AML::Node::Type::Integer;
arg_ref.node.as.integer.value = sleep_state;
arg_ref.ref_count = 2;
BAN::Array<AML::Reference*, 7> arguments(nullptr);
arguments[0] = &arg_ref; // method call should not delete argument
TRY(AML::method_call(pts_path, pts_node, BAN::move(arguments)));
dprintln("Executed \\_PTS({})", sleep_state);
return {};
}
void ACPI::poweroff()
BAN::ErrorOr<void> ACPI::poweroff()
{
if (!m_namespace)
{
dwarnln("ACPI namespace not initialized");
return;
return BAN::Error::from_errno(EFAULT);
}
auto s5_object = m_namespace->find_object({}, AML::NameString("_S5"), AML::Namespace::FindMode::ForceAbsolute);
auto [_, s5_object] = TRY(m_namespace->find_named_object({}, TRY(AML::NameString::from_string("\\_S5_"_sv))));
if (!s5_object)
{
dwarnln("\\_S5 not found");
return;
return BAN::Error::from_errno(EFAULT);
}
auto s5_evaluated = s5_object->to_underlying();
if (!s5_evaluated)
{
dwarnln("Failed to evaluate \\_S5");
return;
}
if (s5_evaluated->type != AML::Node::Type::Package)
auto& s5_node = s5_object->node;
if (s5_node.type != AML::Node::Type::Package)
{
dwarnln("\\_S5 is not a package");
return;
return BAN::Error::from_errno(EFAULT);
}
auto* s5_package = static_cast<AML::Package*>(s5_evaluated.ptr());
if (s5_package->elements.size() < 2)
if (s5_node.as.package->num_elements < 2)
{
dwarnln("\\_S5 package has {} elements, expected atleast 2", s5_package->elements.size());
return;
dwarnln("\\_S5 package has {} elements, expected atleast 2", s5_node.as.package->num_elements);
return BAN::Error::from_errno(EFAULT);
}
auto slp_typa_node = s5_package->elements[0]->convert(AML::Node::ConvInteger);
auto slp_typb_node = s5_package->elements[1]->convert(AML::Node::ConvInteger);
if (!slp_typa_node || !slp_typb_node)
if (!s5_node.as.package->elements[0].resolved || !s5_node.as.package->elements[1].resolved)
{
dwarnln("Failed to get SLP_TYPx values");
return;
dwarnln("TODO: lazy evaluate package \\_S5 elements");
return BAN::Error::from_errno(ENOTSUP);
}
if (!prepare_sleep(5))
return;
auto slp_typa_node = TRY(AML::convert_node(TRY(s5_node.as.package->elements[0].value.node->copy()), AML::ConvInteger, sizeof(uint64_t)));
auto slp_typb_node = TRY(AML::convert_node(TRY(s5_node.as.package->elements[1].value.node->copy()), AML::ConvInteger, sizeof(uint64_t)));
TRY(prepare_sleep(5));
dprintln("Entering sleep state S5");
const auto slp_typa_value = static_cast<AML::Integer*>(slp_typa_node.ptr())->value;
const auto slp_typb_value = static_cast<AML::Integer*>(slp_typb_node.ptr())->value;
const auto slp_typa_value = slp_typa_node.as.integer.value;
const auto slp_typb_value = slp_typb_node.as.integer.value;
uint16_t pm1a_data = IO::inw(fadt().pm1a_cnt_blk);
pm1a_data &= ~(PM1_CNT_SLP_TYP_MASK << PM1_CNT_SLP_TYP_SHIFT);
@@ -562,11 +558,10 @@ acpi_release_global_lock:
}
// system must not execute after sleep registers are written
g_paniced = true;
asm volatile("ud2");
ASSERT_NOT_REACHED();
}
void ACPI::reset()
BAN::ErrorOr<void> ACPI::reset()
{
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/04_ACPI_Hardware_Specification/ACPI_Hardware_Specification.html#reset-register
@@ -579,36 +574,64 @@ acpi_release_global_lock:
break;
default:
dwarnln("Reset register has invalid address space ID ({})", static_cast<uint8_t>(reset_reg.address_space_id));
return;
return BAN::Error::from_errno(EFAULT);
}
if (reset_reg.register_bit_offset != 0 || reset_reg.register_bit_width != 8)
{
dwarnln("Reset register has invalid location ({} bits at bit offset {})", reset_reg.register_bit_width, reset_reg.register_bit_offset);
return;
return BAN::Error::from_errno(EFAULT);
}
if (!prepare_sleep(5))
return;
TRY(prepare_sleep(5));
dprintln("Resetting system");
if (!reset_reg.write(fadt().reset_value))
{
dwarnln("Could not write reset value");
return;
}
TRY(reset_reg.write(fadt().reset_value));
// system must not execute after reset register is written
g_paniced = true;
asm volatile("ud2");
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<void> ACPI::load_aml_tables(BAN::StringView name, bool all)
{
BAN::ErrorOr<void> result {};
for (uint32_t i = 0;; i++)
{
auto* header = get_header(name, i);
if (header == nullptr)
break;
if (all)
dprintln("Parsing {}{}, {} bytes", name, i + 1, header->length);
else
dprintln("Parsing {}, {} bytes", name, header->length);
auto header_span = BAN::ConstByteSpan(reinterpret_cast<const uint8_t*>(header), header->length);
if (auto parse_ret = m_namespace->parse(header_span); parse_ret.is_error())
result = parse_ret.release_error();
if (!all)
break;
}
return result;
}
BAN::ErrorOr<void> ACPI::enter_acpi_mode(uint8_t mode)
{
ASSERT(!m_namespace);
m_namespace = AML::initialize_namespace();
if (!m_namespace)
return BAN::Error::from_errno(EFAULT);
TRY(AML::Namespace::initialize_root_namespace());
m_namespace = &AML::Namespace::root_namespace();
if (auto ret = load_aml_tables("DSDT"_sv, false); ret.is_error())
dwarnln("Could not load DSDT: {}", ret.error());
if (auto ret = load_aml_tables("SSDT"_sv, true); ret.is_error())
dwarnln("Could not load all SSDTs: {}", ret.error());
if (auto ret = load_aml_tables("PSDT"_sv, true); ret.is_error())
dwarnln("Could not load all PSDTs: {}", ret.error());
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/16_Waking_and_Sleeping/initialization.html#placing-the-system-in-acpi-mode
@@ -639,30 +662,43 @@ acpi_release_global_lock:
dprintln("Entered ACPI mode");
dprintln("Initializing devices");
dprintln("Calling opregion _REG methods");
if (auto ret = m_namespace->initalize_op_regions(); ret.is_error())
dwarnln("failed to call _REG methods: {}", ret.error());
dprintln("Initializing \\_SB");
// Initialize \\_SB
auto _sb = m_namespace->find_object({}, AML::NameString("_SB"), AML::Namespace::FindMode::ForceAbsolute);
if (_sb && _sb->is_scope())
{
auto* scope = static_cast<AML::Scope*>(_sb.ptr());
AML::initialize_scope(scope);
}
auto [sb_path, sb_obj] = TRY(m_namespace->find_named_object({}, TRY(AML::NameString::from_string("\\_SB_"_sv))));
if (sb_obj && sb_obj->node.is_scope())
if (auto ret = AML::initialize_scope(sb_path); ret.is_error())
dwarnln("Failed to initialize \\_SB: {}", ret.error());
dprintln("Evaluating \\_PIC");
// Evaluate \\_PIC (mode)
auto _pic = m_namespace->find_object({}, AML::NameString("_PIC"), AML::Namespace::FindMode::ForceAbsolute);
if (_pic && _pic->type == AML::Node::Type::Method)
auto [pic_path, pic_obj] = TRY(m_namespace->find_named_object({}, TRY(AML::NameString::from_string("\\_PIC"_sv))));
if (pic_obj && pic_obj->node.type == AML::Node::Type::Method)
{
auto* method = static_cast<AML::Method*>(_pic.ptr());
if (method->arg_count != 1)
auto& pic_node = pic_obj->node;
if (pic_node.as.method.arg_count != 1)
{
dwarnln("Method \\_PIC has {} arguments, expected 1", method->arg_count);
dwarnln("Method \\_PIC has {} arguments, expected 1", pic_node.as.method.arg_count);
return BAN::Error::from_errno(EINVAL);
}
method->invoke(MUST(BAN::RefPtr<AML::Integer>::create(mode)));
AML::Reference arg_ref;
arg_ref.node.type = AML::Node::Type::Integer;
arg_ref.node.as.integer.value = mode;
arg_ref.ref_count = 2;
BAN::Array<AML::Reference*, 7> arguments(nullptr);
arguments[0] = &arg_ref; // method call should not delete argument
TRY(AML::method_call(pic_path, pic_node, BAN::move(arguments)));
}
dprintln("Devices are initialized");
dprintln("Initializing ACPI interrupts");
uint8_t irq = fadt().sci_int;
if (auto ret = InterruptController::get().reserve_irq(irq); ret.is_error())
@@ -690,34 +726,43 @@ acpi_release_global_lock:
if (fadt().gpe0_blk)
{
// Enable all events in _GPE (_Lxx or _Exx)
m_namespace->for_each_child(AML::NameString("\\_GPE"),
[&](const auto& path, auto& node)
{
if (node->type != AML::Node::Type::Method)
return;
if (path.size() < 4)
return;
auto [gpe_scope, gpe_obj] = TRY(m_namespace->find_named_object({}, TRY(AML::NameString::from_string("\\_GPE"))));
if (gpe_obj && gpe_obj->node.is_scope())
{
m_gpe_scope = BAN::move(gpe_scope);
auto name = path.sv().substring(path.size() - 4);
if (name.substring(0, 2) != "_L"_sv && name.substring(0, 2) != "_E"_sv)
return;
// Enable all events in _GPE (_Lxx or _Exx)
TRY(m_namespace->for_each_child(m_gpe_scope,
[&](BAN::StringView name, AML::Reference* node_ref) -> BAN::Iteration
{
if (node_ref->node.type != AML::Node::Type::Method)
return BAN::Iteration::Continue;
auto index = hex_sv_to_int(name.substring(2));
if (!index.has_value())
return;
ASSERT(name.size() == 4);
if (!name.starts_with("_L"_sv) && !name.starts_with("_E"_sv))
return BAN::Iteration::Continue;
auto byte = index.value() / 8;
auto bit = index.value() % 8;
auto gpe0_en_port = fadt().gpe0_blk + (fadt().gpe0_blk_len / 2) + byte;
IO::outb(gpe0_en_port, IO::inb(gpe0_en_port) | (1 << bit));
auto index = hex_sv_to_int(name.substring(2));
if (!index.has_value())
{
dwarnln("invalid GPE number '{}'", name);
return BAN::Iteration::Continue;
}
auto* method = static_cast<AML::Method*>(node.ptr());
m_gpe_methods[index.value()] = method;
auto byte = index.value() / 8;
auto bit = index.value() % 8;
auto gpe0_en_port = fadt().gpe0_blk + (fadt().gpe0_blk_len / 2) + byte;
IO::outb(gpe0_en_port, IO::inb(gpe0_en_port) | (1 << bit));
dprintln("Enabled GPE {}", index.value(), byte, bit);
}
);
m_gpe_methods[index.value()] = node_ref;
node_ref->ref_count++;
dprintln("Enabled GPE {}", index.value(), byte, bit);
return BAN::Iteration::Continue;
}
));
}
}
set_irq(irq);
@@ -769,7 +814,8 @@ acpi_release_global_lock:
auto index = i * 8 + (pending & ~(pending - 1));
if (m_gpe_methods[index])
m_gpe_methods[index]->invoke();
if (auto ret = AML::method_call(m_gpe_scope, m_gpe_methods[index]->node, {}); ret.is_error())
dwarnln("Failed to evaluate _GPE {}: ", index, ret.error());
handled_event = true;
IO::outb(fadt().gpe0_blk + i, 1 << index);

View File

@@ -1,60 +0,0 @@
#include <BAN/ByteSpan.h>
#include <BAN/Variant.h>
#include <kernel/ACPI/ACPI.h>
#include <kernel/ACPI/AML.h>
namespace Kernel::ACPI
{
static void load_all(AML::Namespace& ns, BAN::StringView signature)
{
for (uint32_t i = 0;; i++)
{
auto* header = ACPI::ACPI::get().get_header(signature, i);
if (!header)
break;
dprintln("Parsing {}{} ({} bytes)", signature, i, header->length);
if (!ns.parse(*header))
{
dwarnln("Failed to parse {}", signature);
continue;
}
}
}
BAN::RefPtr<AML::Namespace> AML::initialize_namespace()
{
auto ns = AML::Namespace::create_root_namespace();
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#differentiated-system-description-table-dsdt
auto* dsdt = ACPI::ACPI::get().get_header("DSDT", 0);
if (!dsdt)
{
dwarnln("Failed to get DSDT");
return {};
}
dprintln("Parsing DSDT ({} bytes)", dsdt->length);
if (!ns->parse(*dsdt))
{
dwarnln("Failed to parse DSDT");
return {};
}
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#secondary-system-description-table-ssdt
load_all(*ns, "SSDT");
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#persistent-system-description-table-psdt
load_all(*ns, "PSDT");
#if AML_DEBUG_LEVEL >= 1
ns->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
dprintln("Parsed ACPI namespace, total of {} nodes created", AML::Node::total_node_count);
return ns;
}
}

View File

@@ -1,881 +0,0 @@
#include <BAN/ScopeGuard.h>
#include <kernel/ACPI/ACPI.h>
#include <kernel/ACPI/AML/Field.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/IO.h>
#include <kernel/PCI.h>
namespace Kernel::ACPI
{
template<typename Func>
concept ReadFunc = requires(Func func, uint64_t offset)
{
requires BAN::is_same_v<decltype(func(offset)), BAN::Optional<uint64_t>>;
};
template<typename Func>
concept WriteFunc = requires(Func func, uint64_t offset, uint64_t value)
{
requires BAN::is_same_v<decltype(func(offset, value)), bool>;
};
template<typename Element>
struct ParseFieldElementContext
{
AML::FieldRules field_rules;
uint64_t field_bit_offset;
BAN::ConstByteSpan field_pkg;
BAN::HashMap<AML::NameSeg, BAN::RefPtr<Element>> elements;
};
template<typename Element>
static bool parse_field_element(ParseFieldElementContext<Element>& context)
{
// FIXME: Validate elements
ASSERT(context.field_pkg.size() >= 1);
switch (context.field_pkg[0])
{
case 0x00:
{
context.field_pkg = context.field_pkg.slice(1);
auto reserved_length = AML::parse_pkg_length(context.field_pkg);
if (!reserved_length.has_value())
{
AML_ERROR("Invalid FieldElement length for reserved field");
return false;
}
AML::trim_pkg_length(context.field_pkg);
context.field_bit_offset += reserved_length.value();
return true;
}
case 0x01:
{
context.field_pkg = context.field_pkg.slice(1);
if (context.field_pkg.size() < 2)
{
AML_ERROR("Invalid FieldElement length for access field");
return false;
}
context.field_rules.access_type = static_cast<AML::FieldRules::AccessType>(context.field_pkg[0] & 0x0F);
context.field_rules.access_attrib = static_cast<AML::FieldRules::AccessAttrib>((context.field_pkg[0] >> 6) & 0x03);
context.field_pkg = context.field_pkg.slice(1);
context.field_rules.access_length = context.field_pkg[0];
context.field_pkg = context.field_pkg.slice(1);
return true;
}
case 0x02:
AML_TODO("Field element Connection", context.field_pkg[0]);
return false;
case 0x03:
{
context.field_pkg = context.field_pkg.slice(1);
if (context.field_pkg.size() < 3)
{
AML_ERROR("Invalid FieldElement length for extended access field");
return false;
}
context.field_rules.access_type = static_cast<AML::FieldRules::AccessType>(context.field_pkg[0] & 0x0F);
context.field_rules.lock_rule = static_cast<AML::FieldRules::LockRule>((context.field_pkg[0] >> 4) & 0x01);
context.field_rules.update_rule = static_cast<AML::FieldRules::UpdateRule>((context.field_pkg[0] >> 5) & 0x03);
context.field_pkg = context.field_pkg.slice(1);
if (context.field_pkg[0] == 0x0B)
context.field_rules.access_attrib = AML::FieldRules::AccessAttrib::Bytes;
else if (context.field_pkg[0] == 0x0E)
context.field_rules.access_attrib = AML::FieldRules::AccessAttrib::RawBytes;
else if (context.field_pkg[0] == 0x0F)
context.field_rules.access_attrib = AML::FieldRules::AccessAttrib::RawProcessBytes;
else
{
AML_ERROR("Invalid FieldElement extended access field attribute");
return false;
}
context.field_pkg = context.field_pkg.slice(1);
context.field_rules.access_length = context.field_pkg[0];
context.field_pkg = context.field_pkg.slice(1);
return true;
}
default:
{
auto element_name = AML::NameSeg::parse(context.field_pkg);
if (!element_name.has_value())
{
AML_ERROR("Invalid FieldElement name for named field");
return false;
}
auto element_length = AML::parse_pkg_length(context.field_pkg);
if (!element_length.has_value())
{
AML_ERROR("Invalid FieldElement length for named field");
return false;
}
AML::trim_pkg_length(context.field_pkg);
if (context.elements.contains(element_name.value()))
{
AML_ERROR("Field element already exists");
return false;
}
MUST(context.elements.emplace(
element_name.value(),
MUST(BAN::RefPtr<Element>::create(
element_name.value(),
context.field_bit_offset,
element_length.value(),
context.field_rules
))
));
context.field_bit_offset += element_length.value();
return true;
}
}
}
static BAN::Optional<uint32_t> determine_access_size(const AML::FieldRules::AccessType& access_type)
{
switch (access_type)
{
case AML::FieldRules::AccessType::Any:
case AML::FieldRules::AccessType::Byte:
return 1;
case AML::FieldRules::AccessType::Word:
return 2;
case AML::FieldRules::AccessType::DWord:
return 4;
case AML::FieldRules::AccessType::QWord:
return 8;
case AML::FieldRules::AccessType::Buffer:
AML_TODO("FieldElement with access type Buffer");
return {};
}
return {};
}
static BAN::Optional<uint64_t> perform_read(AML::OpRegion::RegionSpace region_space, uint64_t access_offset, uint32_t access_size)
{
switch (region_space)
{
case AML::OpRegion::RegionSpace::SystemMemory:
{
uint64_t result = 0;
size_t index_in_page = (access_offset % PAGE_SIZE) / access_size;
PageTable::with_fast_page(access_offset & PAGE_ADDR_MASK, [&] {
switch (access_size)
{
case 1: result = PageTable::fast_page_as_sized<uint8_t> (index_in_page); break;
case 2: result = PageTable::fast_page_as_sized<uint16_t>(index_in_page); break;
case 4: result = PageTable::fast_page_as_sized<uint32_t>(index_in_page); break;
case 8: result = PageTable::fast_page_as_sized<uint64_t>(index_in_page); break;
}
});
return result;
}
case AML::OpRegion::RegionSpace::SystemIO:
{
uint64_t result = 0;
switch (access_size)
{
case 1: result = IO::inb(access_offset); break;
case 2: result = IO::inw(access_offset); break;
case 4: result = IO::inl(access_offset); break;
default:
AML_ERROR("FieldElement read_field (SystemIO) with access size {}", access_size);
return {};
}
return result;
}
case AML::OpRegion::RegionSpace::PCIConfig:
{
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#address-space-format
// PCI configuration space is confined to segment 0, bus 0
uint16_t device = (access_offset >> 32) & 0xFFFF;
uint16_t function = (access_offset >> 16) & 0xFFFF;
uint16_t offset = access_offset & 0xFFFF;
uint64_t result = 0;
switch (access_size)
{
case 1: result = PCI::PCIManager::get().read_config_byte(0, device, function, offset); break;
case 2: result = PCI::PCIManager::get().read_config_word(0, device, function, offset); break;
case 4: result = PCI::PCIManager::get().read_config_dword(0, device, function, offset); break;
default:
AML_ERROR("FieldElement read_field (PCIConfig) with access size {}", access_size);
return {};
}
return result;
}
default:
AML_TODO("FieldElement read_field with region space {}", static_cast<uint8_t>(region_space));
return {};
}
}
static bool perform_write(AML::OpRegion::RegionSpace region_space, uint64_t access_offset, uint32_t access_size, uint64_t value)
{
switch (region_space)
{
case AML::OpRegion::RegionSpace::SystemMemory:
{
size_t index_in_page = (access_offset % PAGE_SIZE) / access_size;
PageTable::with_fast_page(access_offset & PAGE_ADDR_MASK, [&] {
switch (access_size)
{
case 1: PageTable::fast_page_as_sized<uint8_t> (index_in_page) = value; break;
case 2: PageTable::fast_page_as_sized<uint16_t>(index_in_page) = value; break;
case 4: PageTable::fast_page_as_sized<uint32_t>(index_in_page) = value; break;
case 8: PageTable::fast_page_as_sized<uint64_t>(index_in_page) = value; break;
}
});
return true;
}
case AML::OpRegion::RegionSpace::SystemIO:
{
switch (access_size)
{
case 1: IO::outb(access_offset, value); break;
case 2: IO::outw(access_offset, value); break;
case 4: IO::outl(access_offset, value); break;
default:
AML_ERROR("FieldElement write_field (SystemIO) with access size {}", access_size);
return false;
}
return true;
}
case AML::OpRegion::RegionSpace::PCIConfig:
{
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#address-space-format
// PCI configuration space is confined to segment 0, bus 0
uint16_t device = (access_offset >> 32) & 0xFFFF;
uint16_t function = (access_offset >> 16) & 0xFFFF;
uint16_t offset = access_offset & 0xFFFF;
switch (access_size)
{
case 1: PCI::PCIManager::get().write_config_byte(0, device, function, offset, value); break;
case 2: PCI::PCIManager::get().write_config_word(0, device, function, offset, value); break;
case 4: PCI::PCIManager::get().write_config_dword(0, device, function, offset, value); break;
default:
AML_ERROR("FieldElement write_field (PCIConfig) with access size {}", access_size);
return false;
}
return true;
}
default:
AML_TODO("FieldElement write_field with region space {}", static_cast<uint8_t>(region_space));
return false;
}
}
template<ReadFunc ReadFunc>
static BAN::Optional<uint64_t> perform_read_general(
uint64_t base_byte_offset,
uint64_t bit_count,
uint64_t bit_offset,
uint32_t access_size,
ReadFunc& read_func
)
{
if (bit_count > 64)
{
AML_TODO("Field read with bit_count > 64");
return {};
}
uint32_t access_bytes = access_size;
uint32_t access_bits = access_bytes * 8;
uint64_t result = 0;
uint32_t bits_read = 0;
while (bits_read < bit_count)
{
uint64_t byte_offset = base_byte_offset + ((bit_offset + bits_read) / 8);
if (auto rem = byte_offset % access_bytes)
byte_offset -= rem;
auto partial = read_func(byte_offset);
if (!partial.has_value())
return {};
uint32_t shift = (bit_offset + bits_read) % access_bits;
uint32_t valid_bits = BAN::Math::min<uint32_t>(access_bits - shift, bit_count - bits_read);
uint64_t mask = ((uint64_t)1 << valid_bits) - 1;
result |= ((partial.value() >> shift) & mask) << bits_read;
bits_read += valid_bits;
}
return result;
}
template<ReadFunc ReadFunc, WriteFunc WriteFunc>
static bool perform_write_general(
uint64_t base_byte_offset,
uint64_t bit_count,
uint64_t bit_offset,
uint32_t access_size,
uint64_t value,
AML::FieldRules::UpdateRule update_rule,
ReadFunc& read_func,
WriteFunc& write_func
)
{
if (bit_count > 64)
{
AML_TODO("Field write with bit_count > 64");
return false;
}
uint32_t access_bytes = access_size;
uint32_t access_bits = access_bytes * 8;
uint32_t bits_written = 0;
while (bits_written < bit_count)
{
uint64_t byte_offset = base_byte_offset + ((bit_offset + bits_written) / 8);
if (auto rem = byte_offset % access_bytes)
byte_offset -= rem;
uint32_t shift = (bit_offset + bits_written) % access_bits;
uint32_t valid_bits = BAN::Math::min<uint32_t>(access_bits - shift, bit_count - bits_written);
uint64_t mask = ((uint64_t)1 << valid_bits) - 1;
uint64_t to_write = 0;
if (valid_bits != access_bits)
{
switch (update_rule)
{
case AML::FieldRules::UpdateRule::Preserve:
{
auto read_result = read_func(byte_offset);
if (!read_result.has_value())
return false;
to_write = read_result.value() & ~(mask << shift);
break;
}
case AML::FieldRules::UpdateRule::WriteAsOnes:
to_write = ~(mask << shift);
break;
case AML::FieldRules::UpdateRule::WriteAsZeros:
to_write = 0;
break;
}
}
to_write |= ((value >> bits_written) & mask) << shift;
if (!write_func(byte_offset, to_write))
return false;
bits_written += valid_bits;
}
return true;
}
AML::ParseResult AML::Field::parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::FieldOp);
context.aml_data = context.aml_data.slice(2);
auto opt_field_pkg = AML::parse_pkg(context.aml_data);
if (!opt_field_pkg.has_value())
return ParseResult::Failure;
auto field_pkg = opt_field_pkg.release_value();
auto name_string = NameString::parse(field_pkg);
if (!name_string.has_value())
return ParseResult::Failure;
auto op_region = Namespace::root_namespace()->find_object(context.scope, name_string.value(), Namespace::FindMode::Normal);
if (!op_region || op_region->type != AML::Node::Type::OpRegion)
{
AML_ERROR("FieldOp: {} does not name a valid OpRegion", name_string.value());
return ParseResult::Failure;
}
if (field_pkg.size() < 1)
return ParseResult::Failure;
auto field_flags = field_pkg[0];
field_pkg = field_pkg.slice(1);
ParseFieldElementContext<FieldElement> field_context;
field_context.field_rules.access_type = static_cast<FieldRules::AccessType>(field_flags & 0x0F);
field_context.field_rules.lock_rule = static_cast<FieldRules::LockRule>((field_flags >> 4) & 0x01);
field_context.field_rules.update_rule = static_cast<FieldRules::UpdateRule>((field_flags >> 5) & 0x03);
field_context.field_bit_offset = 0;
field_context.field_pkg = field_pkg;
while (field_context.field_pkg.size() > 0)
if (!parse_field_element(field_context))
return ParseResult::Failure;
for (auto& [_, element] : field_context.elements)
{
element->op_region = static_cast<OpRegion*>(op_region.ptr());
NameString element_name;
MUST(element_name.path.push_back(element->name));
if (!Namespace::root_namespace()->add_named_object(context, element_name, element))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
element->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
}
return ParseResult::Success;
}
BAN::Optional<uint64_t> AML::FieldElement::evaluate_internal()
{
if (access_rules.access_attrib != FieldRules::AccessAttrib::Normal)
{
AML_TODO("FieldElement with access attribute {}", static_cast<uint8_t>(access_rules.access_attrib));
return {};
}
auto access_size = determine_access_size(access_rules.access_type);
if (!access_size.has_value())
return {};
auto read_func = [&](uint64_t byte_offset) -> BAN::Optional<uint64_t> {
return perform_read(op_region->region_space, byte_offset, access_size.value());
};
return perform_read_general(op_region->region_offset, bit_count, bit_offset, access_size.value(), read_func);
}
bool AML::FieldElement::store_internal(uint64_t value)
{
if (access_rules.access_attrib != FieldRules::AccessAttrib::Normal)
{
AML_TODO("FieldElement with access attribute {}", static_cast<uint8_t>(access_rules.access_attrib));
return {};
}
auto access_size = determine_access_size(access_rules.access_type);
if (!access_size.has_value())
return false;
auto read_func = [&](uint64_t byte_offset) -> BAN::Optional<uint64_t> {
return perform_read(op_region->region_space, byte_offset, access_size.value());
};
auto write_func = [&](uint64_t byte_offset, uint64_t value) -> bool {
return perform_write(op_region->region_space, byte_offset, access_size.value(), value);
};
return perform_write_general(op_region->region_offset, bit_count, bit_offset, access_size.value(), value, access_rules.update_rule, read_func, write_func);
}
BAN::RefPtr<AML::Integer> AML::FieldElement::as_integer()
{
op_region->mutex.lock();
BAN::ScopeGuard unlock_guard([&] {
op_region->mutex.unlock();
});
auto result = evaluate_internal();
if (!result.has_value())
return {};
return MUST(BAN::RefPtr<Integer>::create(result.value()));
}
BAN::RefPtr<AML::Node> AML::FieldElement::store(BAN::RefPtr<AML::Node> source)
{
ASSERT(source);
auto source_integer = source->convert(AML::Node::ConvInteger);
if (!source_integer)
{
AML_TODO("FieldElement store with non-integer source, type {}", static_cast<uint8_t>(source->type));
return {};
}
op_region->mutex.lock();
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::acquire_global_lock();
BAN::ScopeGuard unlock_guard([&] {
op_region->mutex.unlock();
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::release_global_lock();
});
if (!store_internal(static_cast<AML::Integer*>(source_integer.ptr())->value))
return {};
return source_integer;
}
void AML::FieldElement::debug_print(int indent) const
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("FieldElement {} ({}, offset {}, OpRegion {})",
name,
bit_count,
bit_offset,
op_region->name
);
}
AML::ParseResult AML::IndexField::parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::IndexFieldOp);
context.aml_data = context.aml_data.slice(2);
auto opt_field_pkg = AML::parse_pkg(context.aml_data);
if (!opt_field_pkg.has_value())
return ParseResult::Failure;
auto field_pkg = opt_field_pkg.release_value();
auto index_field_element_name = NameString::parse(field_pkg);
if (!index_field_element_name.has_value())
return ParseResult::Failure;
auto index_field_element = Namespace::root_namespace()->find_object(context.scope, index_field_element_name.value(), Namespace::FindMode::Normal);
if (!index_field_element || index_field_element->type != AML::Node::Type::FieldElement)
{
AML_ERROR("IndexField IndexName does not name a valid FieldElement");
return ParseResult::Failure;
}
auto data_field_element_name = NameString::parse(field_pkg);
if (!data_field_element_name.has_value())
return ParseResult::Failure;
auto data_field_element = Namespace::root_namespace()->find_object(context.scope, data_field_element_name.value(), Namespace::FindMode::Normal);
if (!data_field_element || data_field_element->type != AML::Node::Type::FieldElement)
{
AML_ERROR("IndexField DataName does not name a valid FieldElement");
return ParseResult::Failure;
}
if (field_pkg.size() < 1)
return ParseResult::Failure;
auto field_flags = field_pkg[0];
field_pkg = field_pkg.slice(1);
ParseFieldElementContext<IndexFieldElement> field_context;
field_context.field_rules.access_type = static_cast<FieldRules::AccessType>(field_flags & 0x0F);
field_context.field_rules.lock_rule = static_cast<FieldRules::LockRule>((field_flags >> 4) & 0x01);
field_context.field_rules.update_rule = static_cast<FieldRules::UpdateRule>((field_flags >> 5) & 0x03);
field_context.field_bit_offset = 0;
field_context.field_pkg = field_pkg;
while (field_context.field_pkg.size() > 0)
if (!parse_field_element(field_context))
return ParseResult::Failure;
for (auto& [_, element] : field_context.elements)
{
element->index_element = static_cast<FieldElement*>(index_field_element.ptr());
element->data_element = static_cast<FieldElement*>(data_field_element.ptr());
NameString element_name;
MUST(element_name.path.push_back(element->name));
if (!Namespace::root_namespace()->add_named_object(context, element_name, element))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
element->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
}
return AML::ParseResult::Success;
}
BAN::RefPtr<AML::Integer> AML::IndexFieldElement::as_integer()
{
if (access_rules.access_attrib != FieldRules::AccessAttrib::Normal)
{
AML_TODO("IndexFieldElement with access attribute {}", static_cast<uint8_t>(access_rules.access_attrib));
return {};
}
auto access_size = determine_access_size(access_rules.access_type);
if (!access_size.has_value())
return {};
if (access_size.value() > data_element->bit_count)
{
AML_ERROR("IndexFieldElement read_field with access size {} > data element bit count {}", access_size.value(), data_element->bit_count);
return {};
}
auto read_func = [&](uint64_t byte_offset) -> BAN::Optional<uint64_t> {
if (!index_element->store_internal(byte_offset))
return {};
return data_element->evaluate_internal();
};
index_element->op_region->mutex.lock();
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::acquire_global_lock();
BAN::ScopeGuard unlock_guard([&] {
index_element->op_region->mutex.unlock();
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::release_global_lock();
});
auto result = perform_read_general(0, bit_count, bit_offset, access_size.value(), read_func);
if (!result.has_value())
return {};
return MUST(BAN::RefPtr<Integer>::create(result.value()));
}
BAN::RefPtr<AML::Node> AML::IndexFieldElement::store(BAN::RefPtr<Node> source)
{
if (access_rules.access_attrib != FieldRules::AccessAttrib::Normal)
{
AML_TODO("FieldElement with access attribute {}", static_cast<uint8_t>(access_rules.access_attrib));
return {};
}
ASSERT(source);
auto source_integer = source->convert(AML::Node::ConvInteger);
if (!source_integer)
{
AML_TODO("IndexFieldElement store with non-integer source, type {}", static_cast<uint8_t>(source->type));
return {};
}
auto access_size = determine_access_size(access_rules.access_type);
if (!access_size.has_value())
return {};
if (access_size.value() > data_element->bit_count)
{
AML_ERROR("IndexFieldElement write_field with access size {} > data element bit count {}", access_size.value(), data_element->bit_count);
return {};
}
auto read_func = [&](uint64_t byte_offset) -> BAN::Optional<uint64_t> {
if (!index_element->store_internal(byte_offset))
return {};
return data_element->evaluate_internal();
};
auto write_func = [&](uint64_t byte_offset, uint64_t value) -> bool {
if (!index_element->store_internal(byte_offset))
return {};
return data_element->store_internal(value);
};
index_element->op_region->mutex.lock();
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::acquire_global_lock();
BAN::ScopeGuard unlock_guard([&] {
index_element->op_region->mutex.unlock();
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::release_global_lock();
});
const auto source_value = static_cast<AML::Integer*>(source_integer.ptr())->value;
if (!perform_write_general(0, bit_count, bit_offset, access_size.value(), source_value, access_rules.update_rule, read_func, write_func))
return {};
return source_integer;
}
void AML::IndexFieldElement::debug_print(int indent) const
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("IndexFieldElement {} ({}, offset {}, IndexName {}, DataName {})",
name,
bit_count,
bit_offset,
index_element->name,
data_element->name
);
}
AML::ParseResult AML::BankField::parse(ParseContext& context)
{
// BankFieldOp PkgLength NameString NameString BankValue FieldFlags FieldList
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::BankFieldOp);
context.aml_data = context.aml_data.slice(2);
auto opt_field_pkg = AML::parse_pkg(context.aml_data);
if (!opt_field_pkg.has_value())
return ParseResult::Failure;
auto field_pkg = opt_field_pkg.release_value();
auto op_region_name = NameString::parse(field_pkg);
if (!op_region_name.has_value())
return ParseResult::Failure;
auto op_region = Namespace::root_namespace()->find_object(context.scope, op_region_name.value(), Namespace::FindMode::Normal);
if (!op_region || op_region->type != AML::Node::Type::OpRegion)
{
AML_ERROR("BankField RegionName {} does not name a valid OpRegion", op_region_name.value());
return ParseResult::Failure;
}
auto bank_selector_name = NameString::parse(field_pkg);
if (!bank_selector_name.has_value())
return ParseResult::Failure;
auto bank_selector = Namespace::root_namespace()->find_object(context.scope, bank_selector_name.value(), Namespace::FindMode::Normal);
if (!bank_selector)
{
AML_ERROR("BankField BankSelector {} does not name a valid object", bank_selector_name.value());
return ParseResult::Failure;
}
if (bank_selector->type != AML::Node::Type::FieldElement)
{
AML_TODO("BankField BankSelector {} type {2H}", static_cast<uint8_t>(bank_selector->type));
return ParseResult::Failure;
}
auto temp_aml_data = context.aml_data;
context.aml_data = field_pkg;
auto bank_value_result = AML::parse_object(context);
field_pkg = context.aml_data;
context.aml_data = temp_aml_data;
if (!bank_value_result.success())
return ParseResult::Failure;
auto bank_value_node = bank_value_result.node() ? bank_value_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
if (!bank_value_node)
{
AML_ERROR("BankField BankValue is not an integer");
return ParseResult::Failure;
}
if (field_pkg.size() < 1)
return ParseResult::Failure;
auto field_flags = field_pkg[0];
field_pkg = field_pkg.slice(1);
ParseFieldElementContext<BankFieldElement> field_context;
field_context.field_rules.access_type = static_cast<FieldRules::AccessType>(field_flags & 0x0F);
field_context.field_rules.lock_rule = static_cast<FieldRules::LockRule>((field_flags >> 4) & 0x01);
field_context.field_rules.update_rule = static_cast<FieldRules::UpdateRule>((field_flags >> 5) & 0x03);
field_context.field_bit_offset = 0;
field_context.field_pkg = field_pkg;
while (field_context.field_pkg.size() > 0)
if (!parse_field_element(field_context))
return ParseResult::Failure;
const auto bank_value = static_cast<AML::Integer*>(bank_value_node.ptr())->value;
for (auto& [_, element] : field_context.elements)
{
element->op_region = static_cast<OpRegion*>(op_region.ptr());
element->bank_selector = static_cast<FieldElement*>(bank_selector.ptr());
element->bank_value = bank_value;
NameString element_name;
MUST(element_name.path.push_back(element->name));
if (!Namespace::root_namespace()->add_named_object(context, element_name, element))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
element->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
}
return ParseResult::Success;
}
BAN::RefPtr<AML::Integer> AML::BankFieldElement::as_integer()
{
if (access_rules.access_attrib != FieldRules::AccessAttrib::Normal)
{
AML_TODO("BankFieldElement with access attribute {}", static_cast<uint8_t>(access_rules.access_attrib));
return {};
}
auto access_size = determine_access_size(access_rules.access_type);
if (!access_size.has_value())
return {};
auto read_func = [&](uint64_t byte_offset) -> BAN::Optional<uint64_t> {
return perform_read(op_region->region_space, byte_offset, access_size.value());
};
bank_selector->op_region->mutex.lock();
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::acquire_global_lock();
BAN::ScopeGuard unlock_guard([&] {
bank_selector->op_region->mutex.unlock();
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::release_global_lock();
});
if (!bank_selector->store_internal(bank_value))
{
AML_ERROR("BankFieldElement failed to store BankValue");
return {};
}
auto result = perform_read_general(op_region->region_offset, bit_count, bit_offset, access_size.value(), read_func);
if (!result.has_value())
return {};
return MUST(BAN::RefPtr<Integer>::create(result.value()));
}
BAN::RefPtr<AML::Node> AML::BankFieldElement::store(BAN::RefPtr<AML::Node> source)
{
if (access_rules.access_attrib != FieldRules::AccessAttrib::Normal)
{
AML_TODO("BankFieldElement with access attribute {}", static_cast<uint8_t>(access_rules.access_attrib));
return {};
}
ASSERT(source);
auto source_integer = source->convert(AML::Node::ConvInteger);
if (!source_integer)
{
AML_TODO("BankFieldElement store with non-integer source, type {}", static_cast<uint8_t>(source->type));
return {};
}
auto access_size = determine_access_size(access_rules.access_type);
if (!access_size.has_value())
return {};
auto read_func = [&](uint64_t byte_offset) -> BAN::Optional<uint64_t> {
return perform_read(op_region->region_space, byte_offset, access_size.value());
};
auto write_func = [&](uint64_t byte_offset, uint64_t value) -> bool {
return perform_write(op_region->region_space, byte_offset, access_size.value(), value);
};
bank_selector->op_region->mutex.lock();
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::acquire_global_lock();
BAN::ScopeGuard unlock_guard([&] {
bank_selector->op_region->mutex.unlock();
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::release_global_lock();
});
if (!bank_selector->store_internal(bank_value))
{
AML_ERROR("BankFieldElement failed to store BankValue");
return {};
}
const auto source_value = static_cast<AML::Integer*>(source_integer.ptr())->value;
if (!perform_write_general(op_region->region_offset, bit_count, bit_offset, access_size.value(), source_value, access_rules.update_rule, read_func, write_func))
return {};
return source_integer;
}
void AML::BankFieldElement::debug_print(int indent) const
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("BankFieldElement {} ({}, offset {}, OpRegion {}, BankSelector {}, BankValue {H})",
name,
bit_count,
bit_offset,
op_region->name,
bank_selector->name,
bank_value
);
}
}

View File

@@ -1,183 +0,0 @@
#include <kernel/ACPI/AML/Buffer.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/String.h>
namespace Kernel::ACPI
{
AML::Integer::Integer(uint64_t value, bool constant)
: Node(Node::Type::Integer)
, value(value)
, constant(constant)
{}
BAN::Optional<bool> AML::Integer::logical_compare(BAN::RefPtr<AML::Node> node, AML::Byte binaryop)
{
auto rhs_node = node ? node->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
if (!rhs_node)
{
AML_ERROR("Integer logical compare RHS cannot be converted to");
return {};
}
const auto rhs_value = static_cast<AML::Integer*>(rhs_node.ptr())->value;
switch (binaryop)
{
case AML::Byte::LAndOp: return value && rhs_value;
case AML::Byte::LEqualOp: return value == rhs_value;
case AML::Byte::LGreaterOp: return value > rhs_value;
case AML::Byte::LLessOp: return value < rhs_value;
case AML::Byte::LOrOp: return value || rhs_value;
default:
ASSERT_NOT_REACHED();
}
}
BAN::RefPtr<AML::Node> AML::Integer::convert(uint8_t mask)
{
if (mask & AML::Node::ConvInteger)
return this;
if (mask & AML::Node::ConvBuffer)
{
auto buffer = MUST(BAN::RefPtr<AML::Buffer>::create());
MUST(buffer->buffer.resize(8));
for (size_t i = 0; i < 8; i++)
buffer->buffer[i] = (value >> (56 - i * 8)) & 0xFF;
return buffer;
}
if (mask & AML::Node::ConvBufferField)
{
AML_TODO("Convert Integer to BufferField");
return {};
}
if (mask & AML::Node::ConvFieldUnit)
{
AML_TODO("Convert Integer to FieldUnit");
return {};
}
if (mask & AML::Node::ConvString)
{
constexpr auto get_hex_char =
[](uint8_t nibble)
{
return (nibble < 10 ? '0' : 'A' - 10) + nibble;
};
auto string = MUST(BAN::RefPtr<AML::String>::create());
MUST(string->string.resize(16));
for (size_t i = 0; i < 16; i++)
string->string[i] = get_hex_char((value >> (60 - i * 4)) & 0xF);
return string;
}
return {};
}
BAN::RefPtr<AML::Node> AML::Integer::copy()
{
return MUST(BAN::RefPtr<Integer>::create(value));
}
BAN::RefPtr<AML::Node> AML::Integer::store(BAN::RefPtr<AML::Node> store_node)
{
if (constant)
{
AML_ERROR("Cannot store to constant integer");
return {};
}
auto conv_node = store_node ? store_node->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
if (!conv_node)
{
AML_ERROR("Cannot store non-integer to integer");
return {};
}
value = static_cast<AML::Integer*>(conv_node.ptr())->value;
return MUST(BAN::RefPtr<AML::Integer>::create(value));
}
AML::ParseResult AML::Integer::parse(BAN::ConstByteSpan& aml_data)
{
switch (static_cast<AML::Byte>(aml_data[0]))
{
case AML::Byte::ZeroOp:
aml_data = aml_data.slice(1);
// FIXME: no copy
return ParseResult(Constants::Zero->copy());
case AML::Byte::OneOp:
aml_data = aml_data.slice(1);
// FIXME: no copy
return ParseResult(Constants::One->copy());
case AML::Byte::OnesOp:
aml_data = aml_data.slice(1);
// FIXME: no copy
return ParseResult(Constants::Ones->copy());
case AML::Byte::BytePrefix:
{
if (aml_data.size() < 2)
return ParseResult::Failure;
const uint8_t value = aml_data[1];
aml_data = aml_data.slice(2);
return ParseResult(MUST(BAN::RefPtr<Integer>::create(value)));
}
case AML::Byte::WordPrefix:
{
if (aml_data.size() < 3)
return ParseResult::Failure;
uint16_t value = 0;
value |= aml_data[1] << 0;
value |= aml_data[2] << 8;
aml_data = aml_data.slice(3);
return ParseResult(MUST(BAN::RefPtr<Integer>::create(value)));
}
case AML::Byte::DWordPrefix:
{
if (aml_data.size() < 5)
return ParseResult::Failure;
uint32_t value = 0;
value |= static_cast<uint32_t>(aml_data[1]) << 0;
value |= static_cast<uint32_t>(aml_data[2]) << 8;
value |= static_cast<uint32_t>(aml_data[3]) << 16;
value |= static_cast<uint32_t>(aml_data[4]) << 24;
aml_data = aml_data.slice(5);
return ParseResult(MUST(BAN::RefPtr<Integer>::create(value)));
}
case AML::Byte::QWordPrefix:
{
if (aml_data.size() < 9)
return ParseResult::Failure;
uint64_t value = 0;
value |= static_cast<uint64_t>(aml_data[1]) << 0;
value |= static_cast<uint64_t>(aml_data[2]) << 8;
value |= static_cast<uint64_t>(aml_data[3]) << 16;
value |= static_cast<uint64_t>(aml_data[4]) << 24;
value |= static_cast<uint64_t>(aml_data[5]) << 32;
value |= static_cast<uint64_t>(aml_data[6]) << 40;
value |= static_cast<uint64_t>(aml_data[7]) << 48;
value |= static_cast<uint64_t>(aml_data[8]) << 56;
aml_data = aml_data.slice(9);
return ParseResult(MUST(BAN::RefPtr<Integer>::create(value)));
}
default:
ASSERT_NOT_REACHED();
}
}
void AML::Integer::debug_print(int indent) const
{
AML_DEBUG_PRINT_INDENT(indent);
if (!constant)
AML_DEBUG_PRINT("0x{H}", value);
else
{
AML_DEBUG_PRINT("Const ");
if (value == Constants::Zero->value)
AML_DEBUG_PRINT("Zero");
else if (value == Constants::One->value)
AML_DEBUG_PRINT("One");
else if (value == Constants::Ones->value)
AML_DEBUG_PRINT("Ones");
else
ASSERT_NOT_REACHED();
}
}
}

View File

@@ -1,46 +0,0 @@
#include <kernel/ACPI/AML/Buffer.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/NamedObject.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/String.h>
namespace Kernel::ACPI
{
AML::ParseResult AML::Name::parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::NameOp);
context.aml_data = context.aml_data.slice(1);
auto name_string = AML::NameString::parse(context.aml_data);
if (!name_string.has_value())
return ParseResult::Failure;
auto object = AML::parse_object(context);
if (!object.success())
return ParseResult::Failure;
auto name = MUST(BAN::RefPtr<Name>::create(name_string.value().path.back(), object.node()));
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), name))
return ParseResult::Success;
#if AML_DEBUG_LEVEL >= 2
name->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return ParseResult::Success;
}
void AML::Name::debug_print(int indent) const
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINTLN("Name {} { ", name);
object->debug_print(indent + 1);
AML_DEBUG_PRINTLN("");
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("}");
}
}

View File

@@ -1,311 +1,438 @@
#include <kernel/ACPI/AML/Device.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Method.h>
#include <kernel/ACPI/AML/Mutex.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Region.h>
#include <kernel/ACPI/AML/String.h>
#include <kernel/Lock/LockGuard.h>
#include <BAN/Bitcast.h>
namespace Kernel::ACPI
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/Headers.h>
namespace Kernel::ACPI::AML
{
static BAN::RefPtr<AML::Namespace> s_root_namespace;
static BAN::Vector<uint8_t> s_osi_aml_data;
static Namespace s_root_namespace;
BAN::RefPtr<AML::Integer> AML::Integer::Constants::Zero;
BAN::RefPtr<AML::Integer> AML::Integer::Constants::One;
BAN::RefPtr<AML::Integer> AML::Integer::Constants::Ones;
struct DebugNode : AML::Node
{
DebugNode() : AML::Node(AML::Node::Type::Debug) {}
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> node)
{
node->debug_print(0);
AML_DEBUG_PRINTLN("");
return node;
}
void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("DEBUG");
}
static constexpr BAN::StringView s_supported_osi_strings[] {
"Windows 2000"_sv,
"Windows 2001"_sv,
"Windows 2001 SP1"_sv,
"Windows 2001.1"_sv,
"Windows 2001 SP2"_sv,
"Windows 2001.1 SP1"_sv,
"Windows 2006.1"_sv,
"Windows 2006 SP1"_sv,
"Windows 2006 SP2"_sv,
"Windows 2009"_sv,
"Windows 2012"_sv,
"Windows 2013"_sv,
"Windows 2015"_sv,
"Windows 2016"_sv,
"Windows 2017"_sv,
"Windows 2017.2"_sv,
"Windows 2018"_sv,
"Windows 2018.2"_sv,
"Windows 2019"_sv,
"Extended Address Space Descriptor"_sv,
// just to pass osi test from uACPI :D
"AnotherTestString"_sv,
};
BAN::RefPtr<AML::Node> AML::Namespace::debug_node;
BAN::RefPtr<AML::Namespace> AML::Namespace::root_namespace()
Namespace::~Namespace()
{
ASSERT(s_root_namespace);
return s_root_namespace;
for (auto& [_, reference] : m_named_objects)
if (--reference->ref_count == 0)
delete reference;
}
void AML::Namespace::debug_print(int indent) const
BAN::ErrorOr<void> Namespace::initialize_root_namespace()
{
LockGuard _(m_object_mutex);
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINTLN("Namespace {} {", name);
for_each_child(scope, [&](const auto&, const auto& child) {
child->debug_print(indent + 1);
AML_DEBUG_PRINTLN("");
});
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("}");
}
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html?highlight=predefined#predefined-root-namespaces
BAN::Optional<BAN::String> AML::Namespace::resolve_path(const AML::NameString& relative_base, const AML::NameString& relative_path, FindMode mode, bool check_existence) const
{
LockGuard _(m_object_mutex);
// Base must be non-empty absolute path
ASSERT(relative_base.prefix == "\\"_sv || relative_base.path.empty());
// Do absolute path lookup
if (!relative_path.prefix.empty() || relative_path.path.size() != 1 || mode == FindMode::ForceAbsolute)
{
BAN::String absolute_path;
MUST(absolute_path.push_back('\\'));
// Resolve root and parent references
if (relative_path.prefix == "\\"_sv)
;
else
const auto add_predefined_root_namespace =
[](const char* name) -> BAN::ErrorOr<void>
{
if (relative_path.prefix.size() > relative_base.path.size())
{
AML_ERROR("Trying to resolve parent of root object");
return {};
}
for (size_t i = 0; i < relative_base.path.size() - relative_path.prefix.size(); i++)
{
MUST(absolute_path.append(relative_base.path[i].sv()));
MUST(absolute_path.push_back('.'));
}
}
Node predefined {};
predefined.type = Node::Type::PredefinedScope;
TRY(s_root_namespace.add_named_object({}, TRY(NameString::from_string(name)), BAN::move(predefined)));
return {};
};
// Append relative path
for (const auto& seg : relative_path.path)
{
MUST(absolute_path.append(seg.sv()));
MUST(absolute_path.push_back('.'));
}
TRY(add_predefined_root_namespace("\\"));
TRY(add_predefined_root_namespace("\\_GPE"));
TRY(add_predefined_root_namespace("\\_PR_"));
TRY(add_predefined_root_namespace("\\_SB_"));
TRY(add_predefined_root_namespace("\\_SI_"));
TRY(add_predefined_root_namespace("\\_TZ_"));
if (absolute_path.back() == '.')
absolute_path.pop_back();
if (!check_existence || absolute_path == "\\"_sv || m_objects.contains(absolute_path))
return absolute_path;
return {};
}
// Resolve with namespace search rules (ACPI Spec 6.4 - Section 5.3)
AML::NameSeg target_seg = relative_path.path.back();
BAN::String last_match_path;
BAN::String current_path;
MUST(current_path.push_back('\\'));
// Check root namespace
{
BAN::String tmp;
MUST(tmp.append(current_path));
MUST(tmp.append(target_seg.sv()));
if (m_objects.contains(tmp))
last_match_path = BAN::move(tmp);
Node revision;
revision.type = Node::Type::Integer;
revision.as.integer.value = 2;
TRY(s_root_namespace.add_named_object({}, TRY(NameString::from_string("_REV")), BAN::move(revision)));
}
// Check base base path
for (const auto& seg : relative_base.path)
{
MUST(current_path.append(seg.sv()));
MUST(current_path.push_back('.'));
auto osi_string = TRY(NameString::from_string("\\_OSI"));
BAN::String tmp;
MUST(tmp.append(current_path));
MUST(tmp.append(target_seg.sv()));
if (m_objects.contains(tmp))
last_match_path = BAN::move(tmp);
Node method {};
method.type = Node::Type::Method;
new (method.as.method.storage) Kernel::Mutex();
method.as.method.arg_count = 1;
method.as.method.override_func =
[](const BAN::Array<Reference*, 7>& args) -> BAN::ErrorOr<Node>
{
ASSERT(args[0]);
if (args[0]->node.type != Node::Type::String)
{
dwarnln("_OSI called with {}", args[0]->node);
return BAN::Error::from_errno(EINVAL);
}
const auto arg0 = BAN::StringView(
reinterpret_cast<const char*>(args[0]->node.as.str_buf->bytes),
args[0]->node.as.str_buf->size
);
Node result {};
result.type = Node::Type::Integer;
result.as.integer.value = 0;
for (auto supported : s_supported_osi_strings)
{
if (supported != arg0)
continue;
result.as.integer.value = 0xFFFFFFFFFFFFFFFF;
break;
}
return result;
};
TRY(s_root_namespace.add_named_object({}, osi_string, BAN::move(method)));
}
{
auto gl_string = TRY(NameString::from_string("\\_GL_"));
Node mutex {};
mutex.type = Node::Type::Mutex;
mutex.as.mutex = new Mutex();
mutex.as.mutex->ref_count = 1;
mutex.as.mutex->sync_level = 0;
mutex.as.mutex->global_lock = true;
TRY(s_root_namespace.add_named_object({}, gl_string, BAN::move(mutex)));
}
if (!last_match_path.empty())
return last_match_path;
return {};
}
BAN::RefPtr<AML::NamedObject> AML::Namespace::find_object(const AML::NameString& relative_base, const AML::NameString& relative_path, FindMode mode)
Namespace& Namespace::root_namespace()
{
LockGuard _(m_object_mutex);
auto canonical_path = resolve_path(relative_base, relative_path, mode);
if (!canonical_path.has_value())
return nullptr;
if (canonical_path->sv() == "\\"_sv)
return this;
auto it = m_objects.find(canonical_path.value());
if (it == m_objects.end())
return {};
return it->value;
}
bool AML::Namespace::add_named_object(ParseContext& parse_context, const AML::NameString& object_path, BAN::RefPtr<NamedObject> object)
{
LockGuard _(m_object_mutex);
ASSERT(!object_path.path.empty());
auto canonical_path = resolve_path(parse_context.scope, object_path, FindMode::ForceAbsolute, false);
ASSERT(canonical_path.has_value());
ASSERT(!canonical_path->empty());
if (m_objects.contains(canonical_path.value()))
{
AML_PRINT("Object '{}' already exists", canonical_path.value());
return false;
}
auto canonical_scope = AML::NameString(canonical_path.value());
MUST(m_objects.insert(canonical_path.value(), object));
if (object->is_scope())
{
auto* scope = static_cast<Scope*>(object.ptr());
scope->scope = canonical_scope;
}
MUST(parse_context.created_objects.push_back(canonical_scope));
return true;
}
bool AML::Namespace::remove_named_object(const AML::NameString& absolute_path)
{
LockGuard _(m_object_mutex);
auto canonical_path = resolve_path({}, absolute_path, FindMode::ForceAbsolute);
if (!canonical_path.has_value())
{
AML_ERROR("Trying to delete non-existent object '{}'", absolute_path);
return false;
}
if (canonical_path->empty())
{
AML_ERROR("Trying to remove root namespace");
return false;
}
ASSERT(m_objects.contains(canonical_path.value()));
m_objects.remove(canonical_path.value());
return true;
}
BAN::RefPtr<AML::Namespace> AML::Namespace::create_root_namespace()
{
ASSERT(!s_root_namespace);
s_root_namespace = MUST(BAN::RefPtr<Namespace>::create(NameSeg("\\"_sv)));
s_root_namespace->scope = AML::NameString("\\"_sv);
ASSERT(!Namespace::debug_node);
Namespace::debug_node = MUST(BAN::RefPtr<DebugNode>::create());
Integer::Constants::Zero = MUST(BAN::RefPtr<Integer>::create(0, true));
Integer::Constants::One = MUST(BAN::RefPtr<Integer>::create(1, true));
Integer::Constants::Ones = MUST(BAN::RefPtr<Integer>::create(0xFFFFFFFFFFFFFFFF, true));
AML::ParseContext context;
context.scope = AML::NameString("\\"_sv);
// Add predefined namespaces
#define ADD_PREDEFIED_NAMESPACE(NAME) \
ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\" NAME), MUST(BAN::RefPtr<AML::Device>::create(NameSeg(NAME)))));
ADD_PREDEFIED_NAMESPACE("_GPE"_sv);
ADD_PREDEFIED_NAMESPACE("_PR"_sv);
ADD_PREDEFIED_NAMESPACE("_SB"_sv);
ADD_PREDEFIED_NAMESPACE("_SI"_sv);
ADD_PREDEFIED_NAMESPACE("_TZ"_sv);
#undef ADD_PREDEFIED_NAMESPACE
auto gl = MUST(BAN::RefPtr<AML::Mutex>::create(NameSeg("_GL"_sv), 0, true));
ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\_GL"), gl));
// Add \_OSI that returns true for Linux compatibility
auto osi = MUST(BAN::RefPtr<AML::Method>::create(NameSeg("_OSI"_sv), 1, false, 0));
osi->override_function = [](AML::ParseContext& context) -> BAN::RefPtr<AML::Node> {
ASSERT(context.method_args[0]);
auto arg = context.method_args[0]->convert(AML::Node::ConvString);
if (!arg || arg->type != AML::Node::Type::String)
{
AML_ERROR("Invalid _OSI argument");
return {};
}
constexpr BAN::StringView valid_strings[] {
"Windows 2000"_sv,
"Windows 2001"_sv,
"Windows 2001 SP1"_sv,
"Windows 2001.1"_sv,
"Windows 2001 SP2"_sv,
"Windows 2001.1 SP1"_sv,
"Windows 2006.1"_sv,
"Windows 2006 SP1"_sv,
"Windows 2006 SP2"_sv,
"Windows 2009"_sv,
"Windows 2012"_sv,
"Windows 2013"_sv,
"Windows 2015"_sv,
"Windows 2016"_sv,
"Windows 2017"_sv,
"Windows 2017.2"_sv,
"Windows 2018"_sv,
"Windows 2018.2"_sv,
"Windows 2019"_sv,
"Extended Address Space Descriptor"_sv,
// just to pass osi test from uACPI :D
"AnotherTestString"_sv,
};
auto string = static_cast<AML::String*>(arg.ptr())->string_view();
for (auto valid_string : valid_strings)
if (string == valid_string)
return AML::Integer::Constants::Ones;
return AML::Integer::Constants::Zero;
};
ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\_OSI"), osi));
auto os_string = MUST(BAN::RefPtr<AML::String>::create("banan-os"_sv));
auto os = MUST(BAN::RefPtr<AML::Name>::create("_OS"_sv, os_string));
ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\_OS"), os));
return s_root_namespace;
}
bool AML::Namespace::parse(const SDTHeader& header)
BAN::ErrorOr<void> Namespace::initalize_op_regions()
{
ASSERT(this == s_root_namespace.ptr());
m_has_parsed_namespace = true;
AML::ParseContext context;
context.scope = AML::NameString("\\"_sv);
context.aml_data = BAN::ConstByteSpan(reinterpret_cast<const uint8_t*>(&header), header.length).slice(sizeof(header));
while (context.aml_data.size() > 0)
for (const auto& [obj_path, obj_ref] : m_named_objects)
{
auto result = AML::parse_object(context);
if (!result.success())
if (obj_ref->node.type != Node::Type::OpRegion)
continue;
// FIXME: if _REG adds stuff to namespace, iterators are invalidated
(void)opregion_call_reg(obj_path, obj_ref->node);
}
return {};
}
BAN::ErrorOr<void> Namespace::opregion_call_reg(const Scope& scope, const Node& opregion)
{
ASSERT(opregion.type == Node::Type::OpRegion);
const auto address_space = opregion.as.opregion.address_space;
if (address_space == GAS::AddressSpaceID::SystemIO || address_space == GAS::AddressSpaceID::SystemMemory)
return {};
const uint32_t address_space_u32 = static_cast<uint32_t>(address_space);
if (address_space_u32 >= 32)
return {};
const uint32_t mask = static_cast<uint32_t>(1) << address_space_u32;
auto parent = TRY(scope.copy());
parent.parts.pop_back();
auto it = m_called_reg_bitmaps.find(parent);
if (it != m_called_reg_bitmaps.end())
if (it->value & mask)
return {};
auto [reg_path, reg_obj] = TRY(find_named_object(parent, TRY(NameString::from_string("_REG"_sv)), true));
if (reg_obj != nullptr)
{
if (reg_obj->node.type != Node::Type::Method)
dwarnln("{} is not an method", reg_path);
else
{
AML_ERROR("Failed to parse object");
return false;
if (reg_obj->node.as.method.arg_count != 2)
dwarnln("{} takes {} arguments", reg_obj->node.as.method.arg_count);
else
{
Reference arg0;
arg0.node.type = Node::Type::Integer;
arg0.node.as.integer.value = address_space_u32;
arg0.ref_count = 2; // evaluate should not delete
Reference arg1;
arg1.node.type = Node::Type::Integer;
arg1.node.as.integer.value = 1;
arg1.ref_count = 2; // evaluate should not delete
BAN::Array<Reference*, 7> args(nullptr);
args[0] = &arg0;
args[1] = &arg1;
if (auto ret = method_call(reg_path, reg_obj->node, BAN::move(args)); ret.is_error())
dwarnln("Failed to evaluate {}: {}", reg_path, ret.error());
}
}
}
return true;
if (it != m_called_reg_bitmaps.end())
it->value |= mask;
else
TRY(m_called_reg_bitmaps.insert(BAN::move(parent), mask));
return {};
}
BAN::ErrorOr<Scope> Namespace::resolve_path(const Scope& scope, const NameString& name_string)
{
Scope resolved_path;
if (name_string.base == NameString::base_root)
{
TRY(resolved_path.parts.reserve(name_string.parts.size()));
for (uint32_t part : name_string.parts)
TRY(resolved_path.parts.push_back(part));
}
else
{
const uint32_t scope_segment_count =
(name_string.base < scope.parts.size())
? scope.parts.size() - name_string.base
: 0;
TRY(resolved_path.parts.reserve(scope_segment_count + name_string.parts.size()));
for (size_t i = 0; i < scope_segment_count; i++)
TRY(resolved_path.parts.push_back(scope.parts[i]));
for (uint32_t part : name_string.parts)
TRY(resolved_path.parts.push_back(part));
}
return resolved_path;
}
BAN::ErrorOr<Scope> Namespace::add_named_object(const Scope& scope, const NameString& name_string, Node&& node)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_named_object('{}', '{}', {})", scope, name_string, node);
auto resolved_path = TRY(resolve_path(scope, name_string));
if (m_named_objects.contains(resolved_path))
return Scope();
auto* reference = new Reference();
if (reference == nullptr)
return BAN::Error::from_errno(ENOMEM);
reference->node = BAN::move(node);
reference->ref_count = 1;
TRY(m_named_objects.insert(TRY(resolved_path.copy()), reference));
if (m_has_parsed_namespace && reference->node.type == Node::Type::OpRegion)
(void)opregion_call_reg(resolved_path, reference->node);
return resolved_path;
}
BAN::ErrorOr<Scope> Namespace::add_named_object(const Scope& scope, const NameString& name_string, Reference* reference)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_named_object('{}', '{}', {})", scope, name_string, reference->node);
auto resolved_path = TRY(resolve_path(scope, name_string));
if (m_named_objects.contains(resolved_path))
return Scope();
ASSERT(reference->ref_count >= 1);
reference->ref_count++;
TRY(m_named_objects.insert(TRY(resolved_path.copy()), reference));
return resolved_path;
}
BAN::ErrorOr<void> Namespace::remove_named_object(const Scope& absolute_path)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "remove_named_object('{}')", absolute_path);
auto it = m_named_objects.find(absolute_path);
if (it == m_named_objects.end())
return BAN::Error::from_errno(ENOENT);
if (--it->value->ref_count == 0)
delete it->value;
m_named_objects.remove(it);
return {};
}
BAN::ErrorOr<Namespace::FindResult> Namespace::find_named_object(const Scope& scope, const NameString& name_string, bool force_absolute)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "find_named_object('{}', '{}')", scope, name_string);
if (force_absolute || name_string.base != 0)
{
// Absolute path
auto resolved_path = TRY(resolve_path(scope, name_string));
auto it = m_named_objects.find(resolved_path);
if (it != m_named_objects.end()) {
return FindResult {
.path = BAN::move(resolved_path),
.node = it->value,
};
}
return FindResult {
.path = {},
.node = nullptr,
};
}
// Relative path
Scope path_guess;
TRY(path_guess.parts.reserve(scope.parts.size() + name_string.parts.size()));
for (const auto& part : scope.parts)
TRY(path_guess.parts.push_back(part));
for (const auto& part : name_string.parts)
TRY(path_guess.parts.push_back(part));
auto it = m_named_objects.find(path_guess);
if (it != m_named_objects.end()) {
return FindResult {
.path = BAN::move(path_guess),
.node = it->value,
};
}
for (size_t i = 0; i < scope.parts.size(); i++)
{
path_guess.parts.remove(scope.parts.size() - i - 1);
auto it = m_named_objects.find(path_guess);
if (it != m_named_objects.end()) {
return FindResult {
.path = BAN::move(path_guess),
.node = it->value,
};
}
}
return FindResult {
.path = {},
.node = nullptr,
};
}
BAN::ErrorOr<void> Namespace::for_each_child(const Scope& scope, const BAN::Function<BAN::Iteration(BAN::StringView, Reference*)>& callback)
{
for (const auto& [obj_path, obj_ref] : m_named_objects)
{
if (obj_path.parts.size() != scope.parts.size() + 1)
continue;
bool match = true;
for (size_t i = 0; i < scope.parts.size() && match; i++)
match = obj_path.parts[i] == scope.parts[i];
if (!match)
continue;
auto name = BAN::StringView(reinterpret_cast<const char*>(&obj_path.parts.back()), 4);
auto iteration = callback(name, obj_ref);
if (iteration == BAN::Iteration::Break)
break;
ASSERT(iteration == BAN::Iteration::Continue);
}
return {};
}
BAN::ErrorOr<void> Namespace::for_each_child(const Scope& scope, const BAN::Function<BAN::Iteration(const Scope&, Reference*)>& callback)
{
for (const auto& [obj_path, obj_ref] : m_named_objects)
{
if (obj_path.parts.size() != scope.parts.size() + 1)
continue;
bool match = true;
for (size_t i = 0; i < scope.parts.size() && match; i++)
match = obj_path.parts[i] == scope.parts[i];
if (!match)
continue;
auto iteration = callback(obj_path, obj_ref);
if (iteration == BAN::Iteration::Break)
break;
ASSERT(iteration == BAN::Iteration::Continue);
}
return {};
}
BAN::ErrorOr<Node> Namespace::evaluate(BAN::StringView path)
{
Scope root_scope;
auto name_string = TRY(NameString::from_string(path));
auto [object_path, object] = TRY(find_named_object(root_scope, name_string));
if (object == nullptr)
return BAN::Error::from_errno(ENOENT);
return evaluate_node(object_path, object->node);
}
BAN::ErrorOr<void> Namespace::parse(BAN::ConstByteSpan aml_data)
{
if (aml_data.size() < sizeof(SDTHeader))
return BAN::Error::from_errno(EINVAL);
const auto& sdt_header = aml_data.as<const SDTHeader>();
if (aml_data.size() < sdt_header.length)
return BAN::Error::from_errno(EINVAL);
aml_data = aml_data.slice(sizeof(SDTHeader));
ParseContext context;
context.aml_data = aml_data;
TRY(context.allocate_locals());
while (!context.aml_data.empty())
{
auto parse_result = parse_node_or_execution_flow(context);
if (parse_result.is_error())
{
dwarnln("Failed to parse root namespace: {}", parse_result.error());
return parse_result.release_error();
}
auto [execution_flow, node] = parse_result.release_value();
if (execution_flow == ExecutionFlow::Normal)
continue;
if (execution_flow == ExecutionFlow::Return)
break;
dwarnln("Root namespace got execution flow {}", static_cast<int>(execution_flow));
return BAN::Error::from_errno(EINVAL);
}
return {};
}
BAN::ErrorOr<void> Namespace::initialize_devices()
{
return {};
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,806 @@
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/OpRegion.h>
#include <kernel/IO.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/PCI.h>
namespace Kernel::ACPI::AML
{
static BAN::ErrorOr<size_t> parse_pkg_length(BAN::ConstByteSpan& aml_data)
{
if (aml_data.empty())
return BAN::Error::from_errno(ENODATA);
const uint32_t encoding_length = (aml_data[0] >> 6) + 1;
if (aml_data.size() < encoding_length)
return BAN::Error::from_errno(ENODATA);
uint32_t pkg_length = 0;
switch (encoding_length)
{
case 1:
pkg_length |= aml_data[0] & 0x3F;
break;
case 2:
pkg_length |= aml_data[0] & 0x0F;
pkg_length |= aml_data[1] << 4;
break;
case 3:
pkg_length |= aml_data[0] & 0x0F;
pkg_length |= aml_data[1] << 4;
pkg_length |= aml_data[2] << 12;
break;
case 4:
pkg_length |= aml_data[0] & 0x0F;
pkg_length |= aml_data[1] << 4;
pkg_length |= aml_data[2] << 12;
pkg_length |= aml_data[3] << 20;
break;
}
aml_data = aml_data.slice(encoding_length);
return pkg_length;
}
BAN::ErrorOr<void> parse_opregion_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_opregion_op");
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::OpRegionOp);
context.aml_data = context.aml_data.slice(2);
auto region_name = TRY(parse_name_string(context.aml_data));
if (context.aml_data.empty())
return BAN::Error::from_errno(ENODATA);
auto region_space = context.aml_data[0];
context.aml_data = context.aml_data.slice(1);
switch (static_cast<GAS::AddressSpaceID>(region_space))
{
case GAS::AddressSpaceID::SystemMemory:
case GAS::AddressSpaceID::SystemIO:
case GAS::AddressSpaceID::PCIConfig:
case GAS::AddressSpaceID::EmbeddedController:
case GAS::AddressSpaceID::SMBus:
case GAS::AddressSpaceID::SystemCMOS:
case GAS::AddressSpaceID::PCIBarTarget:
case GAS::AddressSpaceID::IPMI:
case GAS::AddressSpaceID::GeneralPurposeIO:
case GAS::AddressSpaceID::GenericSerialBus:
case GAS::AddressSpaceID::PlatformCommunicationChannel:
break;
default:
if (region_space < 0x80)
{
dwarnln("OpRegion invalid region space {2H}", region_space);
return BAN::Error::from_errno(EINVAL);
}
break;
}
auto region_offset = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
auto region_length = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
Node opregion;
opregion.type = Node::Type::OpRegion;
opregion.as.opregion.address_space = static_cast<GAS::AddressSpaceID>(region_space);
opregion.as.opregion.offset = region_offset.as.integer.value;
opregion.as.opregion.length = region_length.as.integer.value;
TRY(Namespace::root_namespace().add_named_object(context.scope, region_name, BAN::move(opregion)));
return {};
}
template<typename F>
static BAN::ErrorOr<void> parse_field_list(const Scope& scope, BAN::ConstByteSpan field_list_pkg, const F& create_element, uint8_t field_flags)
{
uint64_t offset = 0;
while (!field_list_pkg.empty())
{
switch (field_list_pkg[0])
{
case 0x00:
field_list_pkg = field_list_pkg.slice(1);
offset += TRY(parse_pkg_length(field_list_pkg));
break;
case 0x01:
// FIXME: do something with
if (field_list_pkg.size() < 3)
return BAN::Error::from_errno(ENODATA);
field_flags &= 0xF0;
field_flags |= field_list_pkg[1] & 0x0F;
field_list_pkg = field_list_pkg.slice(3);
break;
case 0x02:
dwarnln("TODO: connect field");
return BAN::Error::from_errno(ENOTSUP);
case 0x03:
dwarnln("TODO: extended access field");
return BAN::Error::from_errno(ENOTSUP);
default:
{
if (field_list_pkg.size() < 4)
return BAN::Error::from_errno(ENODATA);
if (!is_lead_name_char(field_list_pkg[0]) || !is_name_char(field_list_pkg[1]) || !is_name_char(field_list_pkg[2]) || !is_name_char(field_list_pkg[3]))
{
dwarnln("Invalid NameSeg {2H}, {2H}, {2H}, {2H}",
field_list_pkg[0], field_list_pkg[1], field_list_pkg[2], field_list_pkg[3]
);
return BAN::Error::from_errno(EINVAL);
}
const uint32_t name_seg = field_list_pkg.as<const uint32_t>();
field_list_pkg = field_list_pkg.slice(4);
const auto field_length = TRY(parse_pkg_length(field_list_pkg));
NameString field_name;
field_name.base = 0;
TRY(field_name.parts.push_back(name_seg));
Node field_node = create_element(offset, field_length, field_flags);
TRY(Namespace::root_namespace().add_named_object(scope, field_name, BAN::move(field_node)));
offset += field_length;
break;
}
}
}
return {};
}
BAN::ErrorOr<void> parse_field_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_field_op");
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::FieldOp);
context.aml_data = context.aml_data.slice(2);
auto field_pkg = TRY(parse_pkg(context.aml_data));
auto opregion_name = TRY(parse_name_string(field_pkg));
if (field_pkg.empty())
return BAN::Error::from_errno(ENODATA);
auto default_flags = field_pkg[0];
field_pkg = field_pkg.slice(1);
auto [_, opregion] = TRY(Namespace::root_namespace().find_named_object(context.scope, opregion_name));
if (opregion == nullptr)
{
dwarnln("could not find '{}'.'{}'", context.scope, opregion_name);
return BAN::Error::from_errno(ENOENT);
}
if (opregion->node.type != Node::Type::OpRegion)
{
dwarnln("Field source is {}", opregion->node);
return BAN::Error::from_errno(EINVAL);
}
const auto create_element =
[&](uint64_t offset, uint64_t length, uint8_t field_flags) -> Node
{
Node field_node {};
field_node.type = Node::Type::FieldUnit;
field_node.as.field_unit.type = FieldUnit::Type::Field;
field_node.as.field_unit.as.field.opregion = opregion->node.as.opregion;
field_node.as.field_unit.length = length;
field_node.as.field_unit.offset = offset;
field_node.as.field_unit.flags = field_flags;
return field_node;
};
TRY(parse_field_list(context.scope, field_pkg, create_element, default_flags));
return {};
}
BAN::ErrorOr<void> parse_index_field_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_index_field_op");
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::IndexFieldOp);
context.aml_data = context.aml_data.slice(2);
auto field_pkg = TRY(parse_pkg(context.aml_data));
auto index_name = TRY(parse_name_string(field_pkg));
auto data_name = TRY(parse_name_string(field_pkg));
if (field_pkg.empty())
return BAN::Error::from_errno(ENODATA);
auto default_flags = field_pkg[0];
field_pkg = field_pkg.slice(1);
auto [_1, index_obj] = TRY(Namespace::root_namespace().find_named_object(context.scope, index_name));
if (index_obj == nullptr)
{
dwarnln("could not find '{}'.'{}'", context.scope, index_name);
return BAN::Error::from_errno(ENOENT);
}
if (index_obj->node.type != Node::Type::FieldUnit)
{
dwarnln("IndexField source is {}", index_obj->node);
return BAN::Error::from_errno(EINVAL);
}
auto [_2, data_obj] = TRY(Namespace::root_namespace().find_named_object(context.scope, data_name));
if (data_obj == nullptr)
{
dwarnln("could not find '{}'.'{}'", context.scope, data_name);
return BAN::Error::from_errno(ENOENT);
}
if (data_obj->node.type != Node::Type::FieldUnit)
{
dwarnln("IndexField source is {}", data_obj->node);
return BAN::Error::from_errno(EINVAL);
}
const auto create_element =
[&](uint64_t offset, uint64_t length, uint8_t field_flags) -> Node
{
Node field_node {};
field_node.type = Node::Type::FieldUnit;
field_node.as.field_unit.type = FieldUnit::Type::IndexField;
field_node.as.field_unit.as.index_field.index = index_obj;
field_node.as.field_unit.as.index_field.index->ref_count++;
field_node.as.field_unit.as.index_field.data = data_obj;
field_node.as.field_unit.as.index_field.data->ref_count++;
field_node.as.field_unit.length = length;
field_node.as.field_unit.offset = offset;
field_node.as.field_unit.flags = field_flags;
return field_node;
};
TRY(parse_field_list(context.scope, field_pkg, create_element, default_flags));
return {};
}
BAN::ErrorOr<void> parse_bank_field_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_bank_field_op");
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::BankFieldOp);
context.aml_data = context.aml_data.slice(2);
auto field_pkg = TRY(parse_pkg(context.aml_data));
auto opregion_name = TRY(parse_name_string(field_pkg));
auto bank_selector_name = TRY(parse_name_string(field_pkg));
auto temp_aml_data = context.aml_data;
context.aml_data = field_pkg;
auto bank_value_node = TRY(parse_node(context));
field_pkg = context.aml_data;
context.aml_data = temp_aml_data;
if (field_pkg.empty())
return BAN::Error::from_errno(ENODATA);
auto default_flags = field_pkg[0];
field_pkg = field_pkg.slice(1);
auto [_1, opregion] = TRY(Namespace::root_namespace().find_named_object(context.scope, opregion_name));
if (opregion == nullptr)
{
dwarnln("could not find '{}'.'{}'", context.scope, opregion_name);
return BAN::Error::from_errno(ENOENT);
}
if (opregion->node.type != Node::Type::OpRegion)
{
dwarnln("Field source is {}", opregion->node);
return BAN::Error::from_errno(EINVAL);
}
auto [_2, bank_selector] = TRY(Namespace::root_namespace().find_named_object(context.scope, bank_selector_name));
if (bank_selector == nullptr)
{
dwarnln("could not find '{}'.'{}'", context.scope, bank_selector_name);
return BAN::Error::from_errno(ENOENT);
}
if (bank_selector->node.type != Node::Type::FieldUnit)
{
dwarnln("BankField bank selector is {}", bank_selector->node);
return BAN::Error::from_errno(EINVAL);
}
const uint64_t bank_value = TRY(convert_node(BAN::move(bank_value_node), ConvInteger, sizeof(uint64_t))).as.integer.value;
const auto create_element =
[&](uint64_t offset, uint64_t length, uint8_t field_flags) -> Node
{
Node field_node {};
field_node.type = Node::Type::FieldUnit;
field_node.as.field_unit.type = FieldUnit::Type::BankField;
field_node.as.field_unit.as.bank_field.opregion = opregion->node.as.opregion;
field_node.as.field_unit.as.bank_field.bank_selector = bank_selector;
field_node.as.field_unit.as.bank_field.bank_selector->ref_count++;
field_node.as.field_unit.as.bank_field.bank_value = bank_value;
field_node.as.field_unit.length = length;
field_node.as.field_unit.offset = offset;
field_node.as.field_unit.flags = field_flags;
return field_node;
};
TRY(parse_field_list(context.scope, field_pkg, create_element, default_flags));
return {};
}
struct AccessRule
{
uint8_t access_bits;
bool lock;
enum {
Preserve,
WriteZeros,
WriteOnes,
} update_rule;
};
static AccessRule parse_access_rule(uint8_t flags)
{
AccessRule rule;
switch (flags & 0x0F)
{
case 0: rule.access_bits = 8; break;
case 1: rule.access_bits = 8; break;
case 2: rule.access_bits = 16; break;
case 3: rule.access_bits = 32; break;
case 4: rule.access_bits = 64; break;
case 5: rule.access_bits = 8; break;
default: ASSERT_NOT_REACHED();
}
rule.lock = !!(flags & 0x10);
switch ((flags >> 5) & 0x03)
{
case 0: rule.update_rule = AccessRule::Preserve; break;
case 1: rule.update_rule = AccessRule::WriteOnes; break;
case 2: rule.update_rule = AccessRule::WriteZeros; break;
default: ASSERT_NOT_REACHED();
}
return rule;
}
static BAN::ErrorOr<uint64_t> perform_opregion_read(const OpRegion& opregion, uint8_t access_size, uint64_t offset)
{
ASSERT(offset % access_size == 0);
const uint64_t byte_offset = opregion.offset + offset;
switch (opregion.address_space)
{
case GAS::AddressSpaceID::SystemMemory:
{
uint64_t result;
PageTable::with_fast_page(byte_offset & PAGE_ADDR_MASK, [&]() {
void* addr = PageTable::fast_page_as_ptr(byte_offset % PAGE_SIZE);
switch (access_size) {
case 1: result = *static_cast<uint8_t* >(addr); break;
case 2: result = *static_cast<uint16_t*>(addr); break;
case 4: result = *static_cast<uint32_t*>(addr); break;
case 8: result = *static_cast<uint64_t*>(addr); break;
default: ASSERT_NOT_REACHED();
}
});
return result;
}
case GAS::AddressSpaceID::SystemIO:
if (byte_offset + access_size > 0x10000)
{
dwarnln("{} byte read from IO port 0x{H}", access_size, byte_offset);
return BAN::Error::from_errno(EINVAL);
}
switch (access_size)
{
case 1: return IO::inb(byte_offset);
case 2: return IO::inw(byte_offset);
case 4: return IO::inl(byte_offset);
default:
dwarnln("{} byte read from IO port", access_size);
return BAN::Error::from_errno(EINVAL);
}
ASSERT_NOT_REACHED();
case GAS::AddressSpaceID::PCIConfig:
{
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#address-space-format
// PCI configuration space is confined to segment 0, bus 0
const uint16_t device = (byte_offset >> 32) & 0xFFFF;
const uint16_t function = (byte_offset >> 16) & 0xFFFF;
const uint16_t offset = byte_offset & 0xFFFF;
switch (access_size)
{
case 1: return PCI::PCIManager::get().read_config_byte (0, device, function, offset);
case 2: return PCI::PCIManager::get().read_config_word (0, device, function, offset);
case 4: return PCI::PCIManager::get().read_config_dword(0, device, function, offset);
default:
dwarnln("{} byte read from PCI {2H}:{2H}:{2H}", device, function, offset);
return BAN::Error::from_errno(EINVAL);
}
ASSERT_NOT_REACHED();
}
case GAS::AddressSpaceID::EmbeddedController:
case GAS::AddressSpaceID::SMBus:
case GAS::AddressSpaceID::SystemCMOS:
case GAS::AddressSpaceID::PCIBarTarget:
case GAS::AddressSpaceID::IPMI:
case GAS::AddressSpaceID::GeneralPurposeIO:
case GAS::AddressSpaceID::GenericSerialBus:
case GAS::AddressSpaceID::PlatformCommunicationChannel:
dwarnln("TODO: Read from address space {}", static_cast<uint8_t>(opregion.address_space));
return BAN::Error::from_errno(ENOTSUP);
}
ASSERT_NOT_REACHED();
}
static BAN::ErrorOr<void> perform_opregion_write(const OpRegion& opregion, uint8_t access_size, uint64_t offset, uint64_t value)
{
ASSERT(offset % access_size == 0);
const uint64_t byte_offset = opregion.offset + offset;
switch (opregion.address_space)
{
case GAS::AddressSpaceID::SystemMemory:
PageTable::with_fast_page(byte_offset & PAGE_ADDR_MASK, [&]() {
void* addr = PageTable::fast_page_as_ptr(byte_offset % PAGE_SIZE);
switch (access_size) {
case 1: *static_cast<uint8_t* >(addr) = value; break;
case 2: *static_cast<uint16_t*>(addr) = value; break;
case 4: *static_cast<uint32_t*>(addr) = value; break;
case 8: *static_cast<uint64_t*>(addr) = value; break;
default: ASSERT_NOT_REACHED();
}
});
return {};
case GAS::AddressSpaceID::SystemIO:
if (byte_offset + access_size > 0x10000)
{
dwarnln("{} byte write to IO port 0x{H}", access_size, byte_offset);
return BAN::Error::from_errno(EINVAL);
}
switch (access_size)
{
case 1: IO::outb(byte_offset, value); break;
case 2: IO::outw(byte_offset, value); break;
case 4: IO::outl(byte_offset, value); break;
default:
dwarnln("{} byte write to IO port", access_size);
return BAN::Error::from_errno(EINVAL);
}
return {};
case GAS::AddressSpaceID::PCIConfig:
{
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#address-space-format
// PCI configuration space is confined to segment 0, bus 0
const uint16_t device = (byte_offset >> 32) & 0xFFFF;
const uint16_t function = (byte_offset >> 16) & 0xFFFF;
const uint16_t offset = byte_offset & 0xFFFF;
switch (access_size)
{
case 1: PCI::PCIManager::get().write_config_byte (0, device, function, offset, value); break;
case 2: PCI::PCIManager::get().write_config_word (0, device, function, offset, value); break;
case 4: PCI::PCIManager::get().write_config_dword(0, device, function, offset, value); break;
default:
dwarnln("{} byte write to PCI {2H}:{2H}:{2H}", device, function, offset);
return BAN::Error::from_errno(EINVAL);
}
return {};
}
case GAS::AddressSpaceID::EmbeddedController:
case GAS::AddressSpaceID::SMBus:
case GAS::AddressSpaceID::SystemCMOS:
case GAS::AddressSpaceID::PCIBarTarget:
case GAS::AddressSpaceID::IPMI:
case GAS::AddressSpaceID::GeneralPurposeIO:
case GAS::AddressSpaceID::GenericSerialBus:
case GAS::AddressSpaceID::PlatformCommunicationChannel:
dwarnln("TODO: Write to address space {}", static_cast<uint8_t>(opregion.address_space));
return BAN::Error::from_errno(ENOTSUP);
}
ASSERT_NOT_REACHED();
}
struct BufferInfo
{
const uint8_t* buffer;
const uint64_t bytes;
};
static BAN::ErrorOr<BufferInfo> extract_buffer_info(const Node& source)
{
switch (source.type)
{
case Node::Type::String:
case Node::Type::Buffer:
return BufferInfo {
.buffer = source.as.str_buf->bytes,
.bytes = source.as.str_buf->size,
};
case Node::Type::Integer:
return BufferInfo {
.buffer = reinterpret_cast<const uint8_t*>(&source.as.integer.value),
.bytes = sizeof(uint64_t),
};
default:
dwarnln("Invalid store of {} to FieldUnit", source);
return BAN::Error::from_errno(EINVAL);
}
ASSERT_NOT_REACHED();
}
static BAN::ErrorOr<Node> allocate_destination(const Node& source, Node::Type type, size_t max_bytes)
{
ASSERT(source.type == Node::Type::FieldUnit);
const uint64_t source_bit_length = source.as.field_unit.length;
Node result;
result.type = type;
switch (type)
{
case Node::Type::Buffer:
{
const size_t dst_bits = BAN::Math::max<size_t>(max_bytes * 8, source_bit_length);
const size_t bytes = BAN::Math::div_round_up<size_t>(dst_bits, 8);
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + bytes));
if (result.as.str_buf == nullptr)
return BAN::Error::from_errno(ENOMEM);
result.as.str_buf->size = bytes;
result.as.str_buf->ref_count = 1;
memset(result.as.str_buf->bytes, 0, bytes);
break;
}
case Node::Type::Integer:
if (source_bit_length > 64)
{
dwarnln("Convert field unit of {} bits to an integer", source_bit_length);
return BAN::Error::from_errno(EINVAL);
}
result.as.integer.value = 0;
break;
default:
ASSERT_NOT_REACHED();
}
return result;
}
static void write_bits_to_buffer(uint8_t* buffer, size_t bit_offset, uint64_t value, size_t bit_count)
{
size_t bits_done = 0;
while (bits_done < bit_count) {
const size_t acc_bit_offset = (bit_offset + bits_done) % 8;
const size_t acc_size = BAN::Math::min<size_t>(bit_count - bits_done, 8 - acc_bit_offset);
const size_t mask = (1 << acc_size) - 1;
buffer[(bit_offset + bits_done) / 8] |= ((value >> bits_done) & mask) << acc_bit_offset;
bits_done += acc_size;
}
}
static uint64_t read_bits_from_buffer(const uint8_t* buffer, size_t bit_offset, size_t bit_count)
{
uint64_t result = 0;
size_t bits_done = 0;
while (bits_done < bit_count) {
const size_t acc_bit_offset = (bit_offset + bits_done) % 8;
const size_t acc_size = BAN::Math::min<size_t>(bit_count - bits_done, 8 - acc_bit_offset);
const size_t mask = (1 << acc_size) - 1;
result |= static_cast<uint64_t>((buffer[(bit_offset + bits_done) / 8] >> acc_bit_offset) & mask) << bits_done;
bits_done += acc_size;
}
return result;
}
static BAN::ErrorOr<uint64_t> perform_index_field_read(const Node& source, uint64_t acc_byte_offset)
{
ASSERT(source.type == Node::Type::FieldUnit);
ASSERT(source.as.field_unit.type == FieldUnit::Type::IndexField);
Node index_node;
index_node.type = Node::Type::Integer;
index_node.as.integer.value = acc_byte_offset;
TRY(store_to_field_unit(index_node, source.as.field_unit.as.index_field.index->node));
auto value = TRY(convert_from_field_unit(source.as.field_unit.as.index_field.data->node, ConvInteger, sizeof(uint64_t)));
return value.as.integer.value;
}
static BAN::ErrorOr<void> perform_index_field_write(const Node& source, uint64_t acc_byte_offset, uint64_t value)
{
ASSERT(source.type == Node::Type::FieldUnit);
ASSERT(source.as.field_unit.type == FieldUnit::Type::IndexField);
Node index_node;
index_node.type = Node::Type::Integer;
index_node.as.integer.value = acc_byte_offset;
TRY(store_to_field_unit(index_node, source.as.field_unit.as.index_field.index->node));
Node data_node;
data_node.type = Node::Type::Integer;
data_node.as.integer.value = value;
TRY(store_to_field_unit(data_node, source.as.field_unit.as.index_field.data->node));
return {};
}
BAN::ErrorOr<Node> convert_from_field_unit(const Node& source, uint8_t conversion, size_t max_length)
{
ASSERT(source.type == Node::Type::FieldUnit);
const bool can_be_integer = source.as.field_unit.length <= 64;
auto target_type = Node::Type::Uninitialized;
if (can_be_integer && (conversion & Conversion::ConvInteger))
target_type = Node::Type::Integer;
else if (conversion & Conversion::ConvBuffer)
target_type = Node::Type::Buffer;
if (target_type == Node::Type::Uninitialized)
{
dwarnln("Invalid conversion from field unit to 0b{3b}", conversion);
return BAN::Error::from_errno(EINVAL);
}
if (source.as.field_unit.type == FieldUnit::Type::BankField)
{
Node bank_node;
bank_node.type = Node::Type::Integer;
bank_node.as.integer.value = source.as.field_unit.as.bank_field.bank_value;
TRY(store_to_field_unit(bank_node, source.as.field_unit.as.bank_field.bank_selector->node));
}
Node result = TRY(allocate_destination(source, target_type, max_length));
const auto [dst_buf, dst_bytes] = TRY(extract_buffer_info(result));
const auto [max_acc_bits, lock, _] = parse_access_rule(source.as.field_unit.flags);
const size_t transfer_bits = BAN::Math::min<size_t>(source.as.field_unit.length, dst_bytes * 8);
size_t bits_done = 0;
while (bits_done < transfer_bits)
{
const size_t acc_bit_offset = (source.as.field_unit.offset + bits_done) & (max_acc_bits - 1);
const size_t acc_bit_count = max_acc_bits - acc_bit_offset;
const size_t acc_byte_offset = ((source.as.field_unit.offset + bits_done) & ~(max_acc_bits - 1)) / 8;
uint64_t value = 0;
switch (source.as.field_unit.type)
{
case FieldUnit::Type::Field:
value = TRY(perform_opregion_read(source.as.field_unit.as.field.opregion, max_acc_bits / 8, acc_byte_offset));
break;
case FieldUnit::Type::IndexField:
value = TRY(perform_index_field_read(source, acc_byte_offset));
break;
case FieldUnit::Type::BankField:
value = TRY(perform_opregion_read(source.as.field_unit.as.bank_field.opregion, max_acc_bits / 8, acc_byte_offset));
break;
}
write_bits_to_buffer(const_cast<uint8_t*>(dst_buf), bits_done, value >> acc_bit_offset, acc_bit_count);
bits_done += acc_bit_count;
}
return result;
}
BAN::ErrorOr<void> store_to_field_unit(const Node& source, const Node& target)
{
ASSERT(target.type == Node::Type::FieldUnit);
switch (source.type)
{
case Node::Type::Integer:
case Node::Type::Buffer:
case Node::Type::String:
break;
default:
return store_to_field_unit(
TRY(convert_node(TRY(source.copy()), ConvInteger | ConvBuffer | ConvString, 0xFFFFFFFFFFFFFFFF)),
target
);
}
if (target.as.field_unit.type == FieldUnit::Type::BankField)
{
Node bank_node;
bank_node.type = Node::Type::Integer;
bank_node.as.integer.value = target.as.field_unit.as.bank_field.bank_value;
TRY(store_to_field_unit(bank_node, target.as.field_unit.as.bank_field.bank_selector->node));
}
const auto [src_buf, src_bytes] = TRY(extract_buffer_info(source));
const auto [max_acc_bits, lock, update_rule] = parse_access_rule(target.as.field_unit.flags);
const size_t transfer_bits = BAN::Math::min<size_t>(target.as.field_unit.length, src_bytes * 8);
size_t bits_done = 0;
while (bits_done < transfer_bits)
{
const size_t acc_bit_offset = (target.as.field_unit.offset + bits_done) & (max_acc_bits - 1);
const size_t acc_bit_count = BAN::Math::min<size_t>(max_acc_bits - acc_bit_offset, transfer_bits - bits_done);
const size_t acc_byte_offset = ((target.as.field_unit.offset + bits_done) & ~(max_acc_bits - 1)) / 8;
uint64_t value;
switch (update_rule)
{
case AccessRule::Preserve:
switch (target.as.field_unit.type)
{
case FieldUnit::Type::Field:
value = TRY(perform_opregion_read(target.as.field_unit.as.field.opregion, max_acc_bits / 8, acc_byte_offset));
break;
case FieldUnit::Type::IndexField:
value = TRY(perform_index_field_read(target, acc_byte_offset));
break;
case FieldUnit::Type::BankField:
value = TRY(perform_opregion_read(target.as.field_unit.as.bank_field.opregion, max_acc_bits / 8, acc_byte_offset));
break;
default:
ASSERT_NOT_REACHED();
}
break;
case AccessRule::WriteZeros:
value = 0x0000000000000000;
break;
case AccessRule::WriteOnes:
value = 0xFFFFFFFFFFFFFFFF;
break;
default:
ASSERT_NOT_REACHED();
}
value &= ~(((static_cast<uint64_t>(1) << acc_bit_count) - 1) << acc_bit_offset);
value |= read_bits_from_buffer(src_buf, bits_done, acc_bit_count) << acc_bit_offset;
switch (target.as.field_unit.type)
{
case FieldUnit::Type::Field:
TRY(perform_opregion_write(target.as.field_unit.as.field.opregion, max_acc_bits / 8, acc_byte_offset, value));
break;
case FieldUnit::Type::IndexField:
TRY(perform_index_field_write(target, acc_byte_offset, value));
break;
case FieldUnit::Type::BankField:
TRY(perform_opregion_write(target.as.field_unit.as.bank_field.opregion, max_acc_bits / 8, acc_byte_offset, value));
break;
default:
ASSERT_NOT_REACHED();
}
bits_done += acc_bit_count;
}
return {};
}
}

View File

@@ -1,57 +0,0 @@
#include <kernel/ACPI/AML/Package.h>
namespace Kernel::ACPI
{
AML::ParseResult AML::Package::parse(AML::ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::PackageOp);
context.aml_data = context.aml_data.slice(1);
auto package_pkg = AML::parse_pkg(context.aml_data);
if (!package_pkg.has_value())
return ParseResult::Failure;
auto package_context = context;
package_context.aml_data = package_pkg.value();
if (package_pkg->size() < 1)
return ParseResult::Failure;
uint8_t num_elements = package_context.aml_data[0];
package_context.aml_data = package_context.aml_data.slice(1);
auto package = MUST(BAN::RefPtr<Package>::create(context.scope));
while (package->elements.size() < num_elements && package_context.aml_data.size() > 0)
{
auto element_result = PackageElement::parse(package_context, package);
if (!element_result.success())
return ParseResult::Failure;
ASSERT(element_result.node() && element_result.node()->type == Node::Type::PackageElement);
auto element = static_cast<PackageElement*>(element_result.node().ptr());
MUST(package->elements.push_back(element));
}
while (package->elements.size() < num_elements)
{
auto uninitialized = MUST(BAN::RefPtr<PackageElement>::create(package));
MUST(package->elements.push_back(uninitialized));
}
return ParseResult(package);
}
void AML::Package::debug_print(int indent) const
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("Package {");
AML_DEBUG_PRINTLN("");
for (const auto& element : elements)
{
element->debug_print(indent + 1);
AML_DEBUG_PRINTLN("");
}
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("}");
}
}

View File

@@ -1,71 +0,0 @@
#include <kernel/ACPI/AML/Reference.h>
#include <kernel/ACPI/AML/Register.h>
namespace Kernel::ACPI
{
AML::Register::Register()
: Node(Node::Type::Register)
{}
AML::Register::Register(BAN::RefPtr<AML::Node> node)
: Node(Node::Type::Register)
{
if (!node)
{
value = node;
return;
}
while (node)
{
if (node->type == AML::Node::Type::Reference)
node = static_cast<AML::Reference*>(node.ptr())->node;
else if (node->type != AML::Node::Type::Buffer && node->type != AML::Node::Type::Package)
node = node->copy();
if (node->type == AML::Node::Type::Register)
{
node = static_cast<AML::Register*>(node.ptr())->value;
continue;
}
break;
}
ASSERT(node);
value = node;
}
BAN::RefPtr<AML::Node> AML::Register::convert(uint8_t mask)
{
if (!value)
{
AML_ERROR("Trying to convert null Register");
return {};
}
return value->convert(mask);
}
BAN::RefPtr<AML::Node> AML::Register::store(BAN::RefPtr<AML::Node> source)
{
if (source && source->type == AML::Node::Type::Register)
source = static_cast<AML::Register*>(source.ptr())->value;
if (value && value->type == AML::Node::Type::Reference)
return value->store(source);
value = source->copy();
return value;
}
void AML::Register::debug_print(int indent) const
{
AML_DEBUG_PRINT_INDENT(indent);
if (!value)
AML_DEBUG_PRINT("Register { No value }");
else
{
AML_DEBUG_PRINTLN("Register { ");
value->debug_print(indent + 1);
AML_DEBUG_PRINTLN("");
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT(" }");
}
}
}

View File

@@ -1,205 +1,65 @@
#include <kernel/ACPI/AML/Device.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/Region.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/Scope.h>
namespace Kernel::ACPI
namespace Kernel::ACPI::AML
{
AML::ParseResult AML::Scope::parse(ParseContext& context)
BAN::ErrorOr<void> initialize_scope(const Scope& scope)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ScopeOp);
context.aml_data = context.aml_data.slice(1);
auto scope_pkg = AML::parse_pkg(context.aml_data);
if (!scope_pkg.has_value())
return ParseResult::Failure;
auto name_string = AML::NameString::parse(scope_pkg.value());
if (!name_string.has_value())
return ParseResult::Failure;
auto named_object = Namespace::root_namespace()->find_object(context.scope, name_string.value(), Namespace::FindMode::Normal);
if (!named_object)
{
AML_PRINT("Scope '{}' not found in namespace", name_string.value());
return ParseResult::Success;
}
if (!named_object->is_scope())
{
AML_ERROR("Scope '{}' does not name a namespace", name_string.value());
return ParseResult::Failure;
}
auto* scope = static_cast<Scope*>(named_object.ptr());
return scope->enter_context_and_parse_term_list(context, name_string.value(), scope_pkg.value());
}
AML::ParseResult AML::Scope::enter_context_and_parse_term_list(ParseContext& outer_context, const AML::NameString& name_string, BAN::ConstByteSpan aml_data)
{
auto resolved_scope = Namespace::root_namespace()->resolve_path(outer_context.scope, name_string, Namespace::FindMode::Normal);
if (!resolved_scope.has_value())
return ParseResult::Failure;
ParseContext scope_context;
scope_context.scope = AML::NameString(resolved_scope.release_value());
scope_context.aml_data = aml_data;
scope_context.method_args = outer_context.method_args;
while (scope_context.aml_data.size() > 0)
{
auto object_result = AML::parse_object(scope_context);
if (object_result.returned())
{
AML_ERROR("Unexpected return from scope {}", scope_context.scope);
return ParseResult::Failure;
}
if (!object_result.success())
return ParseResult::Failure;
}
for (auto& name : scope_context.created_objects)
MUST(outer_context.created_objects.push_back(BAN::move(name)));
return ParseResult::Success;
}
static BAN::RefPtr<AML::Integer> evaluate_or_invoke(BAN::RefPtr<AML::Node> object)
{
if (object->type != AML::Node::Type::Method)
{
auto converted = object->convert(AML::Node::ConvInteger);
if (!converted)
return {};
return static_cast<AML::Integer*>(converted.ptr());
}
auto* method = static_cast<AML::Method*>(object.ptr());
if (method->arg_count != 0)
{
AML_ERROR("Method has {} arguments, expected 0", method->arg_count);
return {};
}
auto result = method->invoke();
if (!result.has_value())
{
AML_ERROR("Failed to evaluate method");
return {};
}
auto result_integer = result.value()
? result.value()->convert(AML::Node::ConvInteger)
: BAN::RefPtr<AML::Node>();
if (!result_integer)
return {};
return static_cast<AML::Integer*>(result_integer.ptr());
}
bool AML::initialize_scope(BAN::RefPtr<AML::Scope> scope)
{
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINTLN("Initializing {}", scope->scope);
#endif
if (auto reg = Namespace::root_namespace()->find_object(scope->scope, AML::NameString("_REG"_sv), Namespace::FindMode::ForceAbsolute))
{
bool embedded_controller = false;
Namespace::for_each_child(scope->scope,
[&](const auto&, auto& child)
{
if (child->type != AML::Node::Type::OpRegion)
return;
auto* region = static_cast<AML::OpRegion*>(child.ptr());
if (region->region_space == AML::OpRegion::RegionSpace::EmbeddedController)
embedded_controller = true;
}
);
if (embedded_controller)
{
if (reg->type != AML::Node::Type::Method)
{
AML_ERROR("Object {}._REG is not a method", scope->scope);
return false;
}
auto* method = static_cast<Method*>(reg.ptr());
if (method->arg_count != 2)
{
AML_ERROR("Method {}._REG has {} arguments, expected 2", scope->scope, method->arg_count);
return false;
}
BAN::RefPtr<AML::Node> embedded_controller = MUST(BAN::RefPtr<AML::Integer>::create(static_cast<uint64_t>(AML::OpRegion::RegionSpace::EmbeddedController)));
if (!method->invoke(embedded_controller, AML::Integer::Constants::One).has_value())
{
AML_ERROR("Failed to evaluate {}._REG(EmbeddedController, 1), ignoring device", scope->scope);
return false;
}
}
}
bool run_ini = true;
bool init_children = true;
if (auto sta = Namespace::root_namespace()->find_object(scope->scope, AML::NameString("_STA"_sv), Namespace::FindMode::ForceAbsolute))
if (auto [sta_path, sta_obj] = TRY(Namespace::root_namespace().find_named_object(scope, TRY(NameString::from_string("_STA"_sv)), true)); sta_obj)
{
auto result = evaluate_or_invoke(sta);
if (!result)
auto sta_result = TRY(evaluate_node(sta_path, sta_obj->node));
if (sta_result.type != Node::Type::Integer)
{
AML_ERROR("Failed to evaluate {}._STA, return value could not be resolved to integer", scope->scope);
return false;
dwarnln("Object {} evaluated to {}", sta_path, sta_result);
return BAN::Error::from_errno(EFAULT);
}
run_ini = (result->value & 0x01);
init_children = run_ini || (result->value & 0x02);
run_ini = (sta_result.as.integer.value & 0x01);
init_children = run_ini || (sta_result.as.integer.value & 0x02);
}
if (run_ini)
{
auto ini = Namespace::root_namespace()->find_object(scope->scope, AML::NameString("_INI"_sv), Namespace::FindMode::ForceAbsolute);
if (ini)
if (auto [ini_path, ini_obj] = TRY(Namespace::root_namespace().find_named_object(scope, TRY(NameString::from_string("_INI"_sv)), true)); ini_obj)
{
if (ini->type != AML::Node::Type::Method)
auto& ini_node = ini_obj->node;
if (ini_node.type != Node::Type::Method)
{
AML_ERROR("Object {}._INI is not a method", scope->scope);
return false;
dwarnln("Object {} is not a method", ini_path);
return BAN::Error::from_errno(EFAULT);
}
auto* method = static_cast<Method*>(ini.ptr());
if (method->arg_count != 0)
if (ini_node.as.method.arg_count != 0)
{
AML_ERROR("Method {}._INI has {} arguments, expected 0", scope->scope, method->arg_count);
return false;
dwarnln("Method {} takes {} arguments, expected 0", ini_path, ini_node.as.method.arg_count);
return BAN::Error::from_errno(EFAULT);
}
auto result = method->invoke();
if (!result.has_value())
{
AML_ERROR("Failed to evaluate {}._INI, ignoring device", scope->scope);
return true;
}
TRY(method_call(ini_path, ini_node, {}));
}
}
bool success = true;
BAN::ErrorOr<void> result {};
if (init_children)
{
Namespace::root_namespace()->for_each_child(scope->scope,
[&](const auto&, auto& child)
TRY(Namespace::root_namespace().for_each_child(scope,
[&result](const Scope& child_path, Reference* child) -> BAN::Iteration
{
if (!child->is_scope())
return;
auto* child_scope = static_cast<Scope*>(child.ptr());
if (!initialize_scope(child_scope))
success = false;
if (!child->node.is_scope())
return BAN::Iteration::Continue;
if (auto ret = initialize_scope(child_path); ret.is_error())
result = ret.release_error();
return BAN::Iteration::Continue;
}
);
));
}
return success;
return result;
}
}

View File

@@ -1,75 +0,0 @@
#include <kernel/ACPI/AML/Buffer.h>
#include <kernel/ACPI/AML/String.h>
namespace Kernel::ACPI::AML
{
BAN::Optional<bool> String::logical_compare(BAN::RefPtr<AML::Node> node, AML::Byte binaryop)
{
auto rhs = node ? node->convert(AML::Node::ConvString) : BAN::RefPtr<AML::Node>();
if (!rhs)
{
AML_ERROR("String logical compare RHS is not string");
return {};
}
(void)binaryop;
AML_TODO("Logical compare string");
return {};
}
BAN::RefPtr<AML::Buffer> String::as_buffer()
{
auto buffer = MUST(BAN::RefPtr<AML::Buffer>::create());
MUST(buffer->buffer.resize(string.size()));
for (size_t i = 0; i < string.size(); i++)
buffer->buffer[i] = string[i];
return buffer;
}
BAN::RefPtr<AML::Node> String::convert(uint8_t mask)
{
if (mask & AML::Node::ConvString)
return this;
if (mask & AML::Node::ConvInteger)
{
// Apparently this is what NT does, but its definitely not spec compliant :D
uint64_t value = 0;
const size_t bytes = BAN::Math::min<size_t>(string.size(), sizeof(value));
memcpy(&value, string.data(), bytes);
return MUST(BAN::RefPtr<AML::Integer>::create(value));
}
if (mask & AML::Node::ConvBuffer)
return as_buffer();
return {};
}
ParseResult String::parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::StringPrefix);
context.aml_data = context.aml_data.slice(1);
BAN::Vector<uint8_t> string;
while (context.aml_data.size() > 0)
{
if (context.aml_data[0] == 0x00)
break;
MUST(string.push_back(context.aml_data[0]));
context.aml_data = context.aml_data.slice(1);
}
if (context.aml_data.size() == 0)
return ParseResult::Failure;
if (context.aml_data[0] != 0x00)
return ParseResult::Failure;
context.aml_data = context.aml_data.slice(1);
auto string_node = MUST(BAN::RefPtr<String>::create());
string_node->string = BAN::move(string);
return ParseResult(string_node);
}
}