Compare commits

..

No commits in common. "8f2f98b7b4b6457278741daef448ca3d8ebabbe6" and "b16e65168f1fad166ce44819cc35c6a3e19305c6" have entirely different histories.

43 changed files with 397 additions and 2823 deletions

4
.gitmodules vendored
View File

@ -0,0 +1,4 @@
[submodule "kernel/lai"]
path = kernel/lai
url = https://github.com/managarm/lai.git
ignore = untracked

View File

@ -14,9 +14,9 @@ This is my hobby operating system written in C++. Currently supports x86\_64 and
- [x] Linear framebuffer (VESA and GOP)
- [x] Network stack
- [x] ELF executable loading
- [x] AML interpreter (partial)
- [ ] ELF dynamic linking
- [ ] Graphical desktop
- [ ] AML interpreter (currenly using [lai](https://github.com/managarm/lai))
#### Drivers
- [x] NVMe disks

View File

@ -119,6 +119,13 @@ if("${BANAN_ARCH}" STREQUAL "x86_64")
arch/x86_64/Syscall.S
arch/x86_64/Thread.S
)
file(GLOB_RECURSE LAI_SOURCES
lai/*.c
)
set(LAI_SOURCES
${LAI_SOURCES}
kernel/lai_host.cpp
)
elseif("${BANAN_ARCH}" STREQUAL "i686")
set(KERNEL_SOURCES
${KERNEL_SOURCES}
@ -190,6 +197,7 @@ set_source_files_properties(${LAI_SOURCES} PROPERTIES COMPILE_FLAGS -Wno-stack-u
add_custom_target(kernel-headers
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/lai/include/ ${BANAN_INCLUDE}/
DEPENDS sysroot
)

View File

@ -14,21 +14,8 @@ namespace Kernel::ACPI
static BAN::ErrorOr<void> initialize();
static ACPI& get();
static void acquire_global_lock();
static void release_global_lock();
const SDTHeader* get_header(BAN::StringView signature, uint32_t index);
// mode
// 0: PIC
// 1: APIC
// 2: SAPIC
BAN::ErrorOr<void> enter_acpi_mode(uint8_t mode);
// This function will power off the system
// This function will return only if there was an error
void poweroff();
private:
ACPI() = default;
BAN::ErrorOr<void> initialize_impl();

View File

@ -3,9 +3,18 @@
#include <kernel/ACPI/Headers.h>
#include <kernel/ACPI/AML/Namespace.h>
namespace Kernel::ACPI::AML
namespace Kernel::ACPI
{
BAN::RefPtr<AML::Namespace> initialize_namespace();
class AMLParser
{
public:
~AMLParser();
static BAN::RefPtr<AML::Namespace> parse_table(const SDTHeader& header);
private:
AMLParser();
};
}

View File

@ -1,7 +1,6 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
@ -13,14 +12,7 @@ namespace Kernel::ACPI::AML
{
BAN::Vector<uint8_t> buffer;
Buffer()
: AML::Node(Node::Type::Buffer)
{}
BAN::RefPtr<AML::Node> evaluate() override
{
return this;
}
Buffer() : AML::Node(Node::Type::Buffer) {}
static ParseResult parse(AML::ParseContext& context)
{
@ -65,147 +57,4 @@ namespace Kernel::ACPI::AML
}
};
struct BufferField : AML::NamedObject
{
BAN::RefPtr<Buffer> buffer;
size_t field_bit_offset;
size_t field_bit_size;
BufferField(AML::NameSeg name, BAN::RefPtr<Buffer> buffer, size_t field_bit_offset, size_t field_bit_size)
: AML::NamedObject(Node::Type::BufferField, name)
, buffer(buffer)
, field_bit_offset(field_bit_offset)
, field_bit_size(field_bit_size)
{}
BAN::RefPtr<AML::Node> evaluate() override
{
ASSERT(buffer);
ASSERT(field_bit_offset + field_bit_size <= buffer->buffer.size() * 8);
uint64_t value = 0;
if (field_bit_size == 1)
{
size_t byte_offset = field_bit_offset / 8;
size_t bit_offset = field_bit_offset % 8;
value = (buffer->buffer[byte_offset] >> bit_offset) & 1;
}
else
{
ASSERT(field_bit_size % 8 == 0);
for (size_t byte = 0; byte < field_bit_size / 8; byte++)
value |= buffer->buffer[byte] << byte;
}
return MUST(BAN::RefPtr<AML::Integer>::create(value));
}
bool store(BAN::RefPtr<AML::Node> node) override
{
ASSERT(buffer);
ASSERT(field_bit_offset + field_bit_size <= buffer->buffer.size() * 8);
auto value = node->as_integer();
if (!value.has_value())
return false;
if (field_bit_size == 1)
{
size_t byte_offset = field_bit_offset / 8;
size_t bit_offset = field_bit_offset % 8;
buffer->buffer[byte_offset] &= ~(1 << bit_offset);
buffer->buffer[byte_offset] |= (value.value() & 1) << bit_offset;
}
else
{
ASSERT(field_bit_size % 8 == 0);
for (size_t byte = 0; byte < field_bit_size / 8; byte++)
buffer->buffer[byte] = (value.value() >> (byte * 8)) & 0xFF;
}
return true;
}
static ParseResult parse(AML::ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
size_t field_bit_size = 0;
switch (static_cast<Byte>(context.aml_data[0]))
{
case AML::Byte::CreateBitFieldOp:
field_bit_size = 1;
break;
case AML::Byte::CreateByteFieldOp:
field_bit_size = 8;
break;
case AML::Byte::CreateWordFieldOp:
field_bit_size = 16;
break;
case AML::Byte::CreateDWordFieldOp:
field_bit_size = 32;
break;
case AML::Byte::CreateQWordFieldOp:
field_bit_size = 64;
break;
default:
ASSERT_NOT_REACHED();
}
context.aml_data = context.aml_data.slice(1);
auto buffer_result = AML::parse_object(context);
if (!buffer_result.success())
return ParseResult::Failure;
auto buffer_node = buffer_result.node() ? buffer_result.node()->evaluate() : nullptr;
if (!buffer_node || buffer_node->type != Node::Type::Buffer)
{
AML_ERROR("Buffer source does not evaluate to a Buffer");
return ParseResult::Failure;
}
auto buffer = static_cast<Buffer*>(buffer_node.ptr());
auto index_result = AML::parse_object(context);
if (!index_result.success())
return ParseResult::Failure;
auto index = index_result.node() ? index_result.node()->as_integer() : BAN::Optional<uint64_t>();
if (!index.has_value())
{
AML_ERROR("Failed to parse index for BufferField");
return ParseResult::Failure;
}
size_t field_bit_offset = index.value();
if (field_bit_size != 1)
field_bit_offset *= 8;
auto field_name = AML::NameString::parse(context.aml_data);
if (!field_name.has_value())
return ParseResult::Failure;
if (field_name->path.empty())
{
AML_ERROR("Empty field name for BufferField");
return ParseResult::Failure;
}
auto field = MUST(BAN::RefPtr<BufferField>::create(field_name->path.back(), buffer, field_bit_offset, field_bit_size));
if (!Namespace::root_namespace()->add_named_object(context, field_name.value(), field))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
field->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return ParseResult::Success;
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("BufferField {} at bit offset {} ({} bits) to { ", name, field_bit_offset, field_bit_size);
buffer->debug_print(0);
AML_DEBUG_PRINT(" }");
}
};
}

View File

@ -1,6 +1,5 @@
#pragma once
#include <kernel/ACPI/AML/Method.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/Scope.h>
@ -10,9 +9,7 @@ namespace Kernel::ACPI::AML
struct Device : public AML::Scope
{
Device(NameSeg name)
: Scope(Node::Type::Device, name)
{}
Device(NameSeg name) : Scope(name, Node::Type::Device) {}
static ParseResult parse(ParseContext& context)
{
@ -30,7 +27,7 @@ namespace Kernel::ACPI::AML
return ParseResult::Failure;
auto device = MUST(BAN::RefPtr<Device>::create(name_string->path.back()));
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), device))
if (!context.root_namespace->add_named_object(context.scope.span(), name_string.value(), device))
return ParseResult::Failure;
return device->enter_context_and_parse_term_list(context, name_string.value(), device_pkg.value());

View File

@ -1,214 +0,0 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/ParseContext.h>
namespace Kernel::ACPI::AML
{
struct Expression
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
switch (static_cast<Byte>(context.aml_data[0]))
{
// unary
case AML::Byte::IncrementOp:
case AML::Byte::DecrementOp:
{
auto opcode = (static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::IncrementOp) ? AML::Byte::AddOp : AML::Byte::SubtractOp;
context.aml_data = context.aml_data.slice(1);
auto source_result = AML::parse_object(context);
if (!source_result.success())
return ParseResult::Failure;
auto source_node = source_result.node() ? source_result.node()->evaluate() : BAN::RefPtr<AML::Node>();
if (!source_node || source_node->type != AML::Node::Type::Integer)
{
AML_ERROR("UnaryOp source not integer");
return ParseResult::Failure;
}
auto source_integer = static_cast<AML::Integer*>(source_node.ptr());
if (source_integer->constant)
{
AML_ERROR("UnaryOp source is constant");
return ParseResult::Failure;
}
source_integer->value += (opcode == AML::Byte::AddOp) ? 1 : -1;
return ParseResult(source_integer);
}
case AML::Byte::NotOp:
AML_TODO("NotOp", context.aml_data[0]);
return ParseResult::Failure;
case AML::Byte::LNotOp:
{
context.aml_data = context.aml_data.slice(1);
auto node_result = AML::parse_object(context);
if (!node_result.success())
return ParseResult::Failure;
auto value = node_result.node() ? node_result.node()->as_integer() : BAN::Optional<uint64_t>();
if (!value.has_value())
{
AML_ERROR("Logical NotOp source is not integer");
return ParseResult::Failure;
}
auto result = value.value() ? Integer::Constants::Zero : Integer::Constants::Ones;
return ParseResult(result);
}
case AML::Byte::AddOp:
case AML::Byte::AndOp:
case AML::Byte::ModOp:
case AML::Byte::MultiplyOp:
case AML::Byte::NandOp:
case AML::Byte::NorOp:
case AML::Byte::OrOp:
case AML::Byte::ShiftLeftOp:
case AML::Byte::ShiftRightOp:
case AML::Byte::SubtractOp:
case AML::Byte::XorOp:
return parse_binary_op(context);
case AML::Byte::LAndOp:
case AML::Byte::LEqualOp:
case AML::Byte::LGreaterOp:
case AML::Byte::LLessOp:
case AML::Byte::LOrOp:
return parse_logical_binary_op(context);
case AML::Byte::DivideOp:
AML_TODO("DivideOp");
return ParseResult::Failure;
default:
ASSERT_NOT_REACHED();
}
}
private:
static ParseResult parse_binary_op(ParseContext& context)
{
auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
context.aml_data = context.aml_data.slice(1);
auto lhs_result = AML::parse_object(context);
if (!lhs_result.success())
return ParseResult::Failure;
auto lhs_value = lhs_result.node() ? lhs_result.node()->as_integer() : BAN::Optional<uint64_t>();
if (!lhs_value.has_value())
{
AML_ERROR("BinaryOP {2H} LHS not an integer", static_cast<uint8_t>(opcode));
if (lhs_result.node())
lhs_result.node()->debug_print(1);
return ParseResult::Failure;
}
auto rhs_result = AML::parse_object(context);
if (!rhs_result.success())
return ParseResult::Failure;
auto rhs_value = lhs_result.node() ? rhs_result.node()->as_integer() : BAN::Optional<uint64_t>();
if (!rhs_value.has_value())
{
AML_ERROR("BinaryOP {2H} RHS not an integer", static_cast<uint8_t>(opcode));
if (rhs_result.node())
rhs_result.node()->debug_print(1);
return ParseResult::Failure;
}
if (context.aml_data.size() < 1)
{
AML_ERROR("BinaryOP {2H} missing target", static_cast<uint8_t>(opcode));
return ParseResult::Failure;
}
BAN::RefPtr<AML::Node> target_node;
if (context.aml_data[0] == 0x00)
context.aml_data = context.aml_data.slice(1);
else
{
auto target_result = AML::parse_object(context);
if (!target_result.success())
return ParseResult::Failure;
target_node = target_result.node();
if (!target_node)
{
AML_ERROR("BinaryOP {2H} target invalid", static_cast<uint8_t>(opcode));
return ParseResult::Failure;
}
}
uint64_t (*func)(uint64_t, uint64_t) = nullptr;
switch (opcode)
{
case AML::Byte::AddOp: func = [](uint64_t a, uint64_t b) { return a + b; }; break;
case AML::Byte::AndOp: func = [](uint64_t a, uint64_t b) { return a & b; }; break;
case AML::Byte::ModOp: func = [](uint64_t a, uint64_t b) { return a % b; }; break;
case AML::Byte::MultiplyOp: func = [](uint64_t a, uint64_t b) { return a * b; }; break;
case AML::Byte::NandOp: func = [](uint64_t a, uint64_t b) { return ~(a & b); }; break;
case AML::Byte::NorOp: func = [](uint64_t a, uint64_t b) { return ~(a | b); }; break;
case AML::Byte::OrOp: func = [](uint64_t a, uint64_t b) { return a | b; }; break;
case AML::Byte::ShiftLeftOp: func = [](uint64_t a, uint64_t b) { return a << b; }; break;
case AML::Byte::ShiftRightOp: func = [](uint64_t a, uint64_t b) { return a >> b; }; break;
case AML::Byte::SubtractOp: func = [](uint64_t a, uint64_t b) { return a - b; }; break;
case AML::Byte::XorOp: func = [](uint64_t a, uint64_t b) { return a ^ b; }; break;
default:
ASSERT_NOT_REACHED();
}
uint64_t result = func(lhs_value.value(), rhs_value.value());
auto result_node = MUST(BAN::RefPtr<AML::Integer>::create(result));
if (target_node && !target_node->store(result_node))
{
AML_ERROR("BinaryOp {2H} failed to store result", static_cast<uint8_t>(opcode));
return ParseResult::Failure;
}
return ParseResult(result_node);
}
static ParseResult parse_logical_binary_op(ParseContext& context)
{
auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
context.aml_data = context.aml_data.slice(1);
auto lhs_result = AML::parse_object(context);
if (!lhs_result.success())
return ParseResult::Failure;
auto lhs_value = lhs_result.node() ? lhs_result.node()->as_integer() : BAN::Optional<uint64_t>();
if (!lhs_value.has_value())
{
AML_TODO("Logical BinaryOP {2H} LHS not integer", static_cast<uint8_t>(opcode));
return ParseResult::Failure;
}
auto rhs_result = AML::parse_object(context);
if (!rhs_result.success())
return ParseResult::Failure;
auto rhs_value = rhs_result.node() ? rhs_result.node()->as_integer() : BAN::Optional<uint64_t>();
if (!rhs_value.has_value())
{
AML_TODO("Logical BinaryOP {2H} RHS not integer", static_cast<uint8_t>(opcode));
return ParseResult::Failure;
}
BAN::RefPtr<AML::Integer> (*func)(uint64_t, uint64_t) = nullptr;
switch (opcode)
{
case AML::Byte::LAndOp: func = [](uint64_t a, uint64_t b) { return a && b ? Integer::Constants::Ones : Integer::Constants::Zero; }; break;
case AML::Byte::LEqualOp: func = [](uint64_t a, uint64_t b) { return a == b ? Integer::Constants::Ones : Integer::Constants::Zero; }; break;
case AML::Byte::LGreaterOp: func = [](uint64_t a, uint64_t b) { return a > b ? Integer::Constants::Ones : Integer::Constants::Zero; }; break;
case AML::Byte::LLessOp: func = [](uint64_t a, uint64_t b) { return a < b ? Integer::Constants::Ones : Integer::Constants::Zero; }; break;
case AML::Byte::LOrOp: func = [](uint64_t a, uint64_t b) { return a || b ? Integer::Constants::Ones : Integer::Constants::Zero; }; break;
default:
ASSERT_NOT_REACHED();
}
return ParseResult(func(lhs_value.value(), rhs_value.value()));
}
};
}

View File

@ -35,44 +35,25 @@ namespace Kernel::ACPI::AML
WriteAsZeros = 2,
};
UpdateRule update_rule;
enum class AccessAttrib
{
Normal = 0,
Bytes = 1,
RawBytes = 2,
RawProcessBytes = 3,
};
AccessAttrib access_attrib = AccessAttrib::Normal;
uint8_t access_length = 0;
};
struct FieldElement : public NamedObject
{
uint64_t bit_offset;
uint64_t bit_count;
uint32_t bit_count;
FieldRules access_rules;
BAN::RefPtr<OpRegion> op_region;
OpRegion* op_region = nullptr;
FieldElement(NameSeg name, uint64_t bit_offset, uint64_t bit_count, FieldRules access_rules)
FieldElement(NameSeg name, uint64_t bit_offset, uint32_t bit_count, FieldRules access_rules)
: NamedObject(Node::Type::FieldElement, name)
, bit_offset(bit_offset)
, bit_count(bit_count)
, access_rules(access_rules)
{}
BAN::RefPtr<Node> evaluate() override;
bool store(BAN::RefPtr<Node> source) override;
void debug_print(int indent) const override;
private:
BAN::Optional<uint64_t> evaluate_internal();
bool store_internal(uint64_t value);
friend struct IndexFieldElement;
};
struct Field
@ -83,23 +64,20 @@ namespace Kernel::ACPI::AML
struct IndexFieldElement : public NamedObject
{
uint64_t bit_offset;
uint64_t bit_count;
uint32_t bit_count;
FieldRules access_rules;
BAN::RefPtr<FieldElement> index_element;
BAN::RefPtr<FieldElement> data_element;
FieldElement* index_element = nullptr;
FieldElement* data_element = nullptr;
IndexFieldElement(NameSeg name, uint64_t bit_offset, uint64_t bit_count, FieldRules access_rules)
IndexFieldElement(NameSeg name, uint64_t bit_offset, uint32_t bit_count, FieldRules access_rules)
: NamedObject(Node::Type::IndexFieldElement, name)
, bit_offset(bit_offset)
, bit_count(bit_count)
, access_rules(access_rules)
{}
BAN::RefPtr<Node> evaluate() override;
bool store(BAN::RefPtr<Node> source) override;
void debug_print(int indent) const override;
};
@ -108,30 +86,4 @@ namespace Kernel::ACPI::AML
static ParseResult parse(ParseContext& context);
};
struct BankFieldElement : public NamedObject
{
uint64_t bit_offset;
uint64_t bit_count;
FieldRules access_rules;
BAN::RefPtr<OpRegion> op_region;
BAN::RefPtr<NamedObject> bank_selector;
uint64_t bank_value;
BankFieldElement(NameSeg name, uint64_t bit_offset, uint64_t bit_count, FieldRules access_rules)
: NamedObject(Node::Type::BankFieldElement, name)
, bit_offset(bit_offset)
, bit_count(bit_count)
, access_rules(access_rules)
{}
void debug_print(int indent) const override;
};
struct BankField
{
static ParseResult parse(ParseContext& context);
};
}

View File

@ -1,72 +0,0 @@
#pragma once
#include <BAN/HashMap.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
namespace Kernel::ACPI::AML
{
struct IfElse
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::IfOp);
context.aml_data = context.aml_data.slice(1);
auto if_pkg = AML::parse_pkg(context.aml_data);
if (!if_pkg.has_value())
return ParseResult::Failure;
auto outer_aml_data = context.aml_data;
context.aml_data = if_pkg.value();
auto predicate = AML::parse_object(context);
if (!predicate.success())
return ParseResult::Failure;
auto predicate_node = predicate.node();
if (!predicate_node)
{
AML_ERROR("If predicate is not an integer");
return ParseResult::Failure;
}
auto predicate_integer = predicate_node->as_integer();
if (!predicate_integer.has_value())
{
AML_ERROR("If predicate is not an integer");
return ParseResult::Failure;
}
// Else
if (!predicate_integer.value())
{
if (outer_aml_data.size() < 1 || static_cast<Byte>(outer_aml_data[0]) != Byte::ElseOp)
context.aml_data = BAN::ConstByteSpan();
else
{
outer_aml_data = outer_aml_data.slice(1);
auto else_pkg = AML::parse_pkg(outer_aml_data);
if (!else_pkg.has_value())
return ParseResult::Failure;
context.aml_data = else_pkg.value();
}
}
while (context.aml_data.size() > 0)
{
auto object_result = AML::parse_object(context);
if (object_result.returned())
return ParseResult(ParseResult::Result::Returned, object_result.node());
if (!object_result.success())
return ParseResult::Failure;
}
context.aml_data = outer_aml_data;
return ParseResult::Success;
}
};
}

View File

@ -1,96 +0,0 @@
#pragma once
#include <kernel/ACPI/AML/Buffer.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Reference.h>
namespace Kernel::ACPI::AML
{
struct Index
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::IndexOp);
context.aml_data = context.aml_data.slice(1);
auto source_result = AML::parse_object(context);
if (!source_result.success())
return ParseResult::Failure;
auto source = source_result.node() ? source_result.node()->evaluate() : BAN::RefPtr<AML::Node>();
if (!source)
{
AML_ERROR("IndexOp source is null");
return ParseResult::Failure;
}
auto index_result = AML::parse_object(context);
if (!index_result.success())
return ParseResult::Failure;
auto index = index_result.node() ? index_result.node()->as_integer() : BAN::Optional<uint64_t>();
if (!index.has_value())
{
AML_ERROR("IndexOp index is not an integer");
return ParseResult::Failure;
}
BAN::RefPtr<AML::Reference> result;
switch (source->type)
{
case AML::Node::Type::Buffer:
{
auto buffer = static_cast<AML::Buffer*>(source.ptr());
if (index.value() >= buffer->buffer.size())
{
AML_ERROR("IndexOp index is out of buffer bounds");
return ParseResult::Failure;
}
auto buffer_field = MUST(BAN::RefPtr<BufferField>::create(NameSeg(""sv), buffer, index.value() * 8, 8));
result = MUST(BAN::RefPtr<AML::Reference>::create(buffer_field));
break;
}
case AML::Node::Type::Package:
AML_TODO("IndexOp source Package");
return ParseResult::Failure;
case AML::Node::Type::String:
AML_TODO("IndexOp source String");
return ParseResult::Failure;
default:
AML_ERROR("IndexOp source is not a Buffer, Package, or String");
return ParseResult::Failure;
}
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINT("Index {}, ", index.value());
source->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
if (context.aml_data.size() < 1)
return ParseResult::Failure;
if (context.aml_data[0] == 0x00)
context.aml_data = context.aml_data.slice(1);
else
{
auto destination_result = AML::parse_object(context);
if (!destination_result.success())
return ParseResult::Failure;
auto destination = destination_result.node();
if (!destination)
{
AML_ERROR("IndexOp failed to resolve destination");
return ParseResult::Failure;
}
if (!destination->store(result))
return ParseResult::Failure;
}
return ParseResult(result);
}
};
}

View File

@ -13,43 +13,14 @@ namespace Kernel::ACPI::AML
struct Integer : public Node
{
struct Constants
{
// Initialized in Namespace::create_root_namespace
static BAN::RefPtr<Integer> Zero;
static BAN::RefPtr<Integer> One;
static BAN::RefPtr<Integer> Ones;
};
const bool constant;
static constexpr uint64_t Ones = -1;
uint64_t value;
Integer(uint64_t value, bool constant = false)
: Node(Node::Type::Integer)
, value(value)
, constant(constant)
{}
Integer(uint64_t value) : Node(Node::Type::Integer), value(value) {}
BAN::RefPtr<AML::Node> evaluate() override
BAN::Optional<uint64_t> as_integer() const override
{
return this;
}
bool store(BAN::RefPtr<AML::Node> store_node) override
{
if (constant)
{
AML_ERROR("Cannot store to constant integer");
return false;
}
auto store_value = store_node->as_integer();
if (!store_value.has_value())
{
AML_ERROR("Cannot store non-integer to integer");
return false;
}
value = store_value.value();
return true;
return value;
}
static ParseResult parse(BAN::ConstByteSpan& aml_data)
@ -58,13 +29,13 @@ namespace Kernel::ACPI::AML
{
case AML::Byte::ZeroOp:
aml_data = aml_data.slice(1);
return ParseResult(Constants::Zero);
return ParseResult(MUST(BAN::RefPtr<Integer>::create(0)));
case AML::Byte::OneOp:
aml_data = aml_data.slice(1);
return ParseResult(Constants::One);
return ParseResult(MUST(BAN::RefPtr<Integer>::create(1)));
case AML::Byte::OnesOp:
aml_data = aml_data.slice(1);
return ParseResult(Constants::Ones);
return ParseResult(MUST(BAN::RefPtr<Integer>::create(Ones)));
case AML::Byte::BytePrefix:
{
if (aml_data.size() < 2)
@ -111,20 +82,10 @@ namespace Kernel::ACPI::AML
void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
if (!constant)
AML_DEBUG_PRINT("0x{H}", value);
if (value == Ones)
AML_DEBUG_PRINT("Ones");
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();
}
AML_DEBUG_PRINT("0x{H}", value);
}
};

View File

@ -1,30 +1,27 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/NamedObject.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/Scope.h>
namespace Kernel::ACPI::AML
{
struct Method : public AML::Scope
struct Method : public AML::NamedObject
{
using Arguments = BAN::Array<BAN::RefPtr<AML::Register>, 7>;
Mutex mutex;
uint8_t arg_count;
bool serialized;
uint8_t sync_level;
BAN::ConstByteSpan term_list;
Method(AML::NameSeg name, uint8_t arg_count, bool serialized, uint8_t sync_level)
: AML::Scope(Node::Type::Method, name)
Method(AML::NameSeg name, uint8_t arg_count, bool serialized, uint8_t sync_level, BAN::ConstByteSpan term_list)
: AML::NamedObject(Node::Type::Method, name)
, arg_count(arg_count)
, serialized(serialized)
, sync_level(sync_level)
, term_list(term_list)
{}
static ParseResult parse(AML::ParseContext& context)
@ -50,16 +47,12 @@ namespace Kernel::ACPI::AML
name_string.value().path.back(),
method_flags & 0x07,
(method_flags >> 3) & 0x01,
method_flags >> 4
method_flags >> 4,
method_pkg.value()
));
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), method))
return ParseResult::Failure;
auto method_scope = Namespace::root_namespace()->resolve_path(context.scope, name_string.value());
if (!method_scope.has_value())
if (!context.root_namespace->add_named_object(context.scope.span(), name_string.value(), method))
return ParseResult::Failure;
method->term_list = method_pkg.value();
method->scope = method_scope.release_value();
#if AML_DEBUG_LEVEL >= 2
method->debug_print(0);
@ -69,67 +62,6 @@ namespace Kernel::ACPI::AML
return ParseResult::Success;
}
BAN::Optional<BAN::RefPtr<AML::Node>> evaluate(Arguments args, BAN::Vector<uint8_t>& current_sync_stack)
{
if (serialized && !current_sync_stack.empty() && sync_level < current_sync_stack.back())
{
AML_ERROR("Trying to evaluate method {} with lower sync level than current sync level", scope);
return {};
}
ParseContext context;
context.aml_data = term_list;
context.scope = scope;
context.method_args = args;
context.sync_stack = BAN::move(current_sync_stack);
for (auto& local : context.method_locals)
local = MUST(BAN::RefPtr<AML::Register>::create());
if (serialized)
{
mutex.lock();
MUST(context.sync_stack.push_back(sync_level));
}
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINTLN("Evaluating {}", scope);
#endif
BAN::Optional<BAN::RefPtr<AML::Node>> return_value = BAN::RefPtr<AML::Node>();
while (context.aml_data.size() > 0)
{
auto parse_result = AML::parse_object(context);
if (parse_result.returned())
{
return_value = parse_result.node();
break;
}
if (!parse_result.success())
{
AML_ERROR("Method {} evaluate failed", scope);
return_value = {};
break;
}
}
while (!context.created_objects.empty())
{
Namespace::root_namespace()->remove_named_object(context.created_objects.back());
context.created_objects.pop_back();
}
if (serialized)
{
context.sync_stack.pop_back();
mutex.unlock();
}
current_sync_stack = BAN::move(context.sync_stack);
return return_value;
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);

View File

@ -1,18 +1,14 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/NamedObject.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/Lock/Mutex.h>
#include <kernel/Timer/Timer.h>
namespace Kernel::ACPI::AML
{
struct Mutex : public AML::NamedObject
{
Kernel::Mutex mutex;
uint8_t sync_level;
Mutex(NameSeg name, uint8_t sync_level)
@ -21,33 +17,6 @@ namespace Kernel::ACPI::AML
{}
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
switch (static_cast<AML::ExtOp>(context.aml_data[1]))
{
case AML::ExtOp::MutexOp:
return parse_mutex(context);
case AML::ExtOp::AcquireOp:
return parse_acquire(context);
case AML::ExtOp::ReleaseOp:
return parse_release(context);
default:
ASSERT_NOT_REACHED();
}
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("Mutex ");
name.debug_print();
AML_DEBUG_PRINT(" (SyncLevel: {})", sync_level);
}
private:
static ParseResult parse_mutex(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
@ -70,7 +39,7 @@ namespace Kernel::ACPI::AML
}
auto mutex = MUST(BAN::RefPtr<Mutex>::create(name->path.back(), sync_level));
if (!Namespace::root_namespace()->add_named_object(context, name.value(), mutex))
if (!context.root_namespace->add_named_object(context.scope.span(), name.value(), mutex))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
@ -81,86 +50,13 @@ namespace Kernel::ACPI::AML
return ParseResult::Success;
}
static ParseResult parse_acquire(ParseContext& context)
virtual void debug_print(int indent) const override
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::AcquireOp);
context.aml_data = context.aml_data.slice(2);
auto mutex_result = AML::parse_object(context);
if (!mutex_result.success() || !mutex_result.node() || mutex_result.node()->type != AML::Node::Type::Mutex)
{
AML_ERROR("Acquire does not name a valid mutex");
return ParseResult::Failure;
}
auto* mutex = static_cast<AML::Mutex*>(mutex_result.node().ptr());
if (mutex->sync_level < context.sync_level())
{
AML_ERROR("Trying to acquire mutex with lower sync level than current sync level");
return ParseResult::Failure;
}
if (context.aml_data.size() < 2)
{
AML_ERROR("Missing timeout value");
return ParseResult::Failure;
}
uint16_t timeout = context.aml_data[0] | (context.aml_data[1] << 8);
context.aml_data = context.aml_data.slice(2);
if (timeout >= 0xFFFF)
mutex->mutex.lock();
else
{
// FIXME: This is a very inefficient way to wait for a mutex
uint64_t wake_time = SystemTimer::get().ms_since_boot() + timeout;
while (!mutex->mutex.try_lock())
{
if (SystemTimer::get().ms_since_boot() >= wake_time)
return ParseResult(Integer::Constants::Ones);
SystemTimer::get().sleep(1);
}
}
MUST(context.sync_stack.push_back(mutex->sync_level));
return ParseResult(Integer::Constants::Zero);
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("Mutex ");
name.debug_print();
AML_DEBUG_PRINT(" (SyncLevel: {})", sync_level);
}
static ParseResult parse_release(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::ReleaseOp);
context.aml_data = context.aml_data.slice(2);
auto mutex_result = AML::parse_object(context);
if (!mutex_result.success() || !mutex_result.node() || mutex_result.node()->type != AML::Node::Type::Mutex)
{
AML_ERROR("Release does not name a valid mutex");
return ParseResult::Failure;
}
if (context.sync_stack.empty())
{
AML_ERROR("Trying to release mutex without having acquired it");
return ParseResult::Failure;
}
auto* mutex = static_cast<AML::Mutex*>(mutex_result.node().ptr());
if (mutex->sync_level != context.sync_level())
{
AML_ERROR("Trying to release mutex with different sync level than current sync level");
return ParseResult::Failure;
}
mutex->mutex.unlock();
context.sync_stack.pop_back();
return ParseResult::Success;
}
};
}

View File

@ -8,7 +8,6 @@ namespace Kernel::ACPI::AML
struct NamedObject : public Node
{
BAN::RefPtr<NamedObject> parent;
NameSeg name;
NamedObject(Node::Type type, NameSeg name) : Node(type), name(name) {}
@ -22,19 +21,6 @@ namespace Kernel::ACPI::AML
: NamedObject(Node::Type::Name, name), object(BAN::move(object))
{}
BAN::RefPtr<AML::Node> evaluate() override
{
ASSERT(object);
return object->evaluate();
}
bool store(BAN::RefPtr<AML::Node> node) override
{
ASSERT(object);
object = node;
return true;
}
static ParseResult parse(ParseContext& context);
virtual void debug_print(int indent) const override;
};

View File

@ -70,59 +70,6 @@ namespace Kernel::ACPI::AML
BAN::String prefix;
BAN::Vector<NameSeg> path;
NameString() = default;
NameString(BAN::StringView str)
{
if (!str.empty() && str.front() == '\\')
{
MUST(prefix.push_back('\\'));
str = str.substring(1);
}
else
{
while (str.size() > 0 && str.front() == '^')
{
MUST(prefix.push_back('^'));
str = str.substring(1);
}
}
while (!str.empty())
{
ASSERT(str[0] != '.');
size_t len = 1;
while (len < str.size() && str[len] != '.')
len++;
ASSERT(len <= 4);
MUST(path.push_back(NameSeg(str.substring(0, len))));
str = str.substring(len);
if (!str.empty())
{
ASSERT(str[0] == '.');
str = str.substring(1);
}
}
}
static bool can_parse(BAN::ConstByteSpan aml_data)
{
if (aml_data.size() == 0)
return false;
switch (static_cast<AML::Byte>(aml_data[0]))
{
case AML::Byte::RootChar:
case AML::Byte::ParentPrefixChar:
case AML::Byte::NullName:
case AML::Byte::DualNamePrefix:
case AML::Byte::MultiNamePrefix:
return true;
default:
return is_lead_name_char(aml_data[0]);
}
}
static BAN::Optional<NameString> parse(BAN::ConstByteSpan& aml_data)
{
if (aml_data.size() == 0)
@ -207,27 +154,4 @@ namespace BAN
}
};
template<typename F>
void Formatter::print_argument(F putc, const Kernel::ACPI::AML::NameSeg& name_seg, const ValueFormat&)
{
size_t len = 4;
while (len > 0 && name_seg.chars[len - 1] == '_')
len--;
for (size_t i = 0; i < len; i++)
putc(name_seg.chars[i]);
}
template<typename F>
void Formatter::print_argument(F putc, const Kernel::ACPI::AML::NameString& name_string, const ValueFormat&)
{
print_argument(putc, name_string.prefix, {});
if (!name_string.path.empty())
print_argument(putc, name_string.path.front(), {});
for (size_t i = 1; i < name_string.path.size(); i++)
{
putc('.');
print_argument(putc, name_string.path[i], {});
}
}
}

View File

@ -1,31 +1,23 @@
#pragma once
#include <kernel/ACPI/AML/Scope.h>
#include <kernel/ACPI/Headers.h>
#include <kernel/Lock/Mutex.h>
namespace Kernel::ACPI::AML
{
struct Namespace : public AML::Scope
{
static BAN::RefPtr<AML::Namespace> root_namespace();
Namespace() : AML::Scope(NameSeg("\\"sv)) {}
Namespace(NameSeg name) : AML::Scope(Node::Type::Namespace, name) {}
static BAN::RefPtr<Namespace> parse(BAN::ConstByteSpan aml);
static BAN::RefPtr<AML::Namespace> create_root_namespace();
bool parse(const SDTHeader& header);
BAN::Optional<AML::NameString> resolve_path(const AML::NameString& relative_base, const AML::NameString& relative_path);
BAN::Optional<BAN::Vector<AML::NameSeg>> resolve_path(BAN::Span<const AML::NameSeg> parsing_scope, const AML::NameString& relative_path);
// Find an object in the namespace. Returns nullptr if the object is not found.
BAN::RefPtr<NamedObject> find_object(const AML::NameString& relative_base, const AML::NameString& relative_path);
BAN::RefPtr<NamedObject> find_object(BAN::Span<const AML::NameSeg> parsing_scope, const AML::NameString& relative_path);
// Add an object to the namespace. Returns false if the parent object could not be added.
bool add_named_object(ParseContext&, const AML::NameString& object_path, BAN::RefPtr<NamedObject> object);
// Remove an object from the namespace. Returns false if the object could not be removed.
bool remove_named_object(const AML::NameString& absolute_path);
bool add_named_object(BAN::Span<const AML::NameSeg> parsing_scope, const AML::NameString& object_path, BAN::RefPtr<NamedObject> object);
};
}

View File

@ -4,20 +4,15 @@
#include <BAN/Optional.h>
#include <BAN/RefPtr.h>
#include <BAN/Vector.h>
#include <kernel/ACPI/AML/Utils.h>
namespace Kernel::ACPI::AML
{
struct Node : public BAN::RefCounted<Node>
{
static uint64_t total_node_count;
enum class Type
{
BankFieldElement,
Buffer,
BufferField,
Device,
FieldElement,
IndexFieldElement,
@ -25,26 +20,19 @@ namespace Kernel::ACPI::AML
Method,
Mutex,
Name,
Namespace,
OpRegion,
Package,
PowerResource,
Processor,
Reference,
Register,
Scope,
String,
ThermalZone,
};
const Type type;
Node(Type type) : type(type) { total_node_count++; }
virtual ~Node() { total_node_count--; }
Node(Type type) : type(type) {}
virtual ~Node() = default;
virtual bool is_scope() const { return false; }
[[nodiscard]] BAN::Optional<uint64_t> as_integer();
[[nodiscard]] virtual BAN::RefPtr<AML::Node> evaluate() { AML_TODO("evaluate, type {}", static_cast<uint8_t>(type)); return nullptr; }
[[nodiscard]] virtual bool store(BAN::RefPtr<AML::Node> source) { AML_TODO("store, type {}", static_cast<uint8_t>(type)); return false; }
virtual BAN::Optional<uint64_t> as_integer() const { return {}; }
virtual void debug_print(int indent) const = 0;
};
@ -59,25 +47,12 @@ namespace Kernel::ACPI::AML
{
Success,
Failure,
Returned,
};
ParseResult(Result success)
: m_result(success)
{}
ParseResult(Result success, BAN::RefPtr<Node> node)
: m_result(success)
, m_node(BAN::move(node))
{}
ParseResult(BAN::RefPtr<Node> node)
: m_result(Result::Success)
, m_node(BAN::move(node))
{
ASSERT(m_node);
}
ParseResult(Result success) : m_result(success) {}
ParseResult(BAN::RefPtr<Node> node) : m_result(Result::Success), m_node(BAN::move(node)) { ASSERT(m_node); }
bool success() const { return m_result == Result::Success; }
bool returned() const { return m_result == Result::Returned; }
BAN::RefPtr<Node> node()
{

View File

@ -10,41 +10,9 @@ namespace Kernel::ACPI::AML
struct Package : public AML::Node
{
struct UnresolvedReference
{
AML::NameString name;
size_t index;
};
BAN::Vector<UnresolvedReference> unresolved_references;
AML::NameString scope; // Used for resolving references
BAN::Vector<BAN::RefPtr<AML::Node>> elements;
Package(BAN::Vector<BAN::RefPtr<AML::Node>>&& elements, BAN::Vector<UnresolvedReference>&& unresolved_references, AML::NameString scope)
: Node(Node::Type::Package)
, elements(BAN::move(elements))
, unresolved_references(BAN::move(unresolved_references))
, scope(scope)
{}
BAN::RefPtr<AML::Node> evaluate() override
{
// resolve references
for (auto& reference : unresolved_references)
{
auto object = Namespace::root_namespace()->find_object(scope, reference.name);
if (!object)
{
AML_ERROR("Failed to resolve reference {} in package", reference.name);
return {};
}
ASSERT(!elements[reference.index]);
elements[reference.index] = object;
}
unresolved_references.clear();
return this;
}
Package() : Node(Node::Type::Package) {}
static ParseResult parse(AML::ParseContext& context)
{
@ -65,33 +33,18 @@ namespace Kernel::ACPI::AML
package_context.aml_data = package_context.aml_data.slice(1);
BAN::Vector<BAN::RefPtr<AML::Node>> elements;
BAN::Vector<UnresolvedReference> unresolved_references;
while (elements.size() < num_elements && package_context.aml_data.size() > 0)
{
BAN::RefPtr<AML::Node> element;
// Store name strings as references
if (package_context.aml_data[0] != 0x00 && AML::NameString::can_parse(package_context.aml_data))
{
auto name = AML::NameString::parse(package_context.aml_data);
if (!name.has_value())
return ParseResult::Failure;
MUST(unresolved_references.push_back(UnresolvedReference { .name = name.value(), .index = elements.size() }));
}
else
{
auto element_result = AML::parse_object(package_context);
if (!element_result.success())
return ParseResult::Failure;
element = element_result.node();
}
MUST(elements.push_back(element));
auto element_result = AML::parse_object(package_context);
if (!element_result.success())
return ParseResult::Failure;
MUST(elements.push_back(element_result.node()));
}
while (elements.size() < num_elements)
MUST(elements.push_back(BAN::RefPtr<AML::Node>()));
auto package = MUST(BAN::RefPtr<Package>::create(BAN::move(elements), BAN::move(unresolved_references), context.scope));
auto package = MUST(BAN::RefPtr<Package>::create());
package->elements = BAN::move(elements);
return ParseResult(package);
}
@ -102,11 +55,7 @@ namespace Kernel::ACPI::AML
AML_DEBUG_PRINTLN("");
for (const auto& element : elements)
{
AML_DEBUG_PRINT_INDENT(indent + 1);
if (element)
element->debug_print(0);
else
AML_DEBUG_PRINT("Uninitialized");
element->debug_print(indent + 1);
AML_DEBUG_PRINTLN("");
}
AML_DEBUG_PRINT_INDENT(indent);

View File

@ -1,30 +1,18 @@
#pragma once
#include <BAN/Array.h>
#include <BAN/ByteSpan.h>
#include <BAN/LinkedList.h>
#include <BAN/Vector.h>
#include <kernel/ACPI/AML/NamedObject.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/Register.h>
namespace Kernel::ACPI::AML
{
struct ParseContext
{
BAN::ConstByteSpan aml_data;
AML::NameString scope;
// Used for cleaning up on method exit
// NOTE: This uses linked list instead of vector because
// we don't really need large contiguous memory
BAN::LinkedList<AML::NameString> created_objects;
uint8_t sync_level() const { return !sync_stack.empty() ? sync_stack.back() : 0; }
BAN::Vector<uint8_t> sync_stack;
BAN::Array<BAN::RefPtr<Register>, 7> method_args;
BAN::Array<BAN::RefPtr<Register>, 8> method_locals;
BAN::ConstByteSpan aml_data;
BAN::Vector<AML::NameSeg> scope;
struct Namespace* root_namespace;
};
}

View File

@ -1,68 +0,0 @@
#pragma once
#include <BAN/Endianness.h>
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/Scope.h>
namespace Kernel::ACPI::AML
{
struct PowerResource : public AML::Scope
{
uint8_t system_level;
uint16_t resource_order;
PowerResource(NameSeg name, uint8_t system_level, uint16_t resource_order)
: Scope(Node::Type::PowerResource, name)
, system_level(system_level)
, resource_order(resource_order)
{}
static ParseResult 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::PowerResOp);
context.aml_data = context.aml_data.slice(2);
auto opt_power_res_pkg = AML::parse_pkg(context.aml_data);
if (!opt_power_res_pkg.has_value())
return ParseResult::Failure;
auto power_res_pkg = opt_power_res_pkg.value();
auto name = NameString::parse(power_res_pkg);
if (!name.has_value())
return ParseResult::Failure;
if (power_res_pkg.size() < 1)
return ParseResult::Failure;
uint8_t system_level = power_res_pkg[0];
power_res_pkg = power_res_pkg.slice(1);
if (power_res_pkg.size() < 2)
return ParseResult::Failure;
uint16_t resource_order = BAN::little_endian_to_host<uint16_t>(*reinterpret_cast<const uint16_t*>(power_res_pkg.data()));
power_res_pkg = power_res_pkg.slice(2);
auto power_res = MUST(BAN::RefPtr<PowerResource>::create(name->path.back(), system_level, resource_order));
if (!Namespace::root_namespace()->add_named_object(context, name.value(), power_res))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
power_res->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return power_res->enter_context_and_parse_term_list(context, name.value(), power_res_pkg);
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("PowerResource {} (SystemLevel {}, ResourceOrder {})", name, system_level, resource_order);
}
};
}

View File

@ -16,7 +16,7 @@ namespace Kernel::ACPI::AML
uint8_t pblk_len;
Processor(NameSeg name, uint8_t id, uint32_t pblk_addr, uint8_t pblk_len)
: Scope(Node::Type::Processor, name)
: Scope(name, Node::Type::Processor)
, id(id)
, pblk_addr(pblk_addr)
, pblk_len(pblk_len)
@ -29,46 +29,42 @@ namespace Kernel::ACPI::AML
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::ProcessorOp);
context.aml_data = context.aml_data.slice(2);
auto opt_processor_pkg = AML::parse_pkg(context.aml_data);
if (!opt_processor_pkg.has_value())
auto processor_pkg = AML::parse_pkg(context.aml_data);
if (!processor_pkg.has_value())
return ParseResult::Failure;
auto processor_pkg = opt_processor_pkg.value();
auto name = NameString::parse(processor_pkg);
auto name = NameString::parse(processor_pkg.value());
if (!name.has_value())
return ParseResult::Failure;
if (processor_pkg.size() < 1)
if (processor_pkg->size() < 1)
return ParseResult::Failure;
uint8_t id = processor_pkg[0];
processor_pkg = processor_pkg.slice(1);
uint8_t id = processor_pkg.value()[0];
processor_pkg = processor_pkg->slice(1);
if (processor_pkg.size() < 4)
if (processor_pkg->size() < 4)
return ParseResult::Failure;
uint32_t pblk_addr = BAN::little_endian_to_host<uint32_t>(*reinterpret_cast<const uint32_t*>(processor_pkg.data()));
processor_pkg = processor_pkg.slice(4);
uint32_t pblk_addr = BAN::little_endian_to_host<uint32_t>(*reinterpret_cast<const uint32_t*>(processor_pkg->data()));
processor_pkg = processor_pkg->slice(4);
if (processor_pkg.size() < 1)
if (processor_pkg->size() < 1)
return ParseResult::Failure;
uint8_t pblk_len = processor_pkg[0];
processor_pkg = processor_pkg.slice(1);
uint8_t pblk_len = processor_pkg.value()[0];
processor_pkg = processor_pkg->slice(1);
auto processor = MUST(BAN::RefPtr<Processor>::create(name->path.back(), id, pblk_addr, pblk_len));
if (!Namespace::root_namespace()->add_named_object(context, name.value(), processor))
if (!context.root_namespace->add_named_object(context.scope.span(), name.value(), processor))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
processor->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return processor->enter_context_and_parse_term_list(context, name.value(), processor_pkg);
return processor->enter_context_and_parse_term_list(context, name.value(), processor_pkg.value());
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("Processor {} (ID: {}, PBlkAddr: 0x{H}, PBlkLen: {})", name, id, pblk_addr, pblk_len);
AML_DEBUG_PRINT("Processor ");
name.debug_print();
AML_DEBUG_PRINT(" (ID: {}, PBlkAddr: 0x{H}, PBlkLen: {})", id, pblk_addr, pblk_len);
}
};

View File

@ -1,155 +0,0 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/Names.h>
#include <kernel/ACPI/AML/String.h>
#include <kernel/ACPI/AML/ParseContext.h>
namespace Kernel::ACPI::AML
{
struct Reference : public AML::Node
{
BAN::RefPtr<AML::Node> node;
Reference(BAN::RefPtr<AML::Node> node)
: Node(AML::Node::Type::Reference)
, node(node)
{
ASSERT(node);
}
BAN::RefPtr<AML::Node> evaluate() override
{
return this;
}
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
bool conditional = false;
switch (static_cast<AML::Byte>(context.aml_data[0]))
{
case AML::Byte::DerefOfOp:
return parse_dereference(context);
case AML::Byte::RefOfOp:
context.aml_data = context.aml_data.slice(1);
conditional = false;
break;
case AML::Byte::ExtOpPrefix:
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::CondRefOfOp);
context.aml_data = context.aml_data.slice(2);
conditional = true;
break;
default:
ASSERT_NOT_REACHED();
}
BAN::RefPtr<AML::Node> object;
if (NameString::can_parse(context.aml_data))
{
auto name = NameString::parse(context.aml_data);
if (!name.has_value())
return ParseResult::Failure;
object = Namespace::root_namespace()->find_object(context.scope, name.value());
}
else
{
auto parse_result = AML::parse_object(context);
if (!parse_result.success())
return ParseResult::Failure;
object = parse_result.node();
}
if (!conditional)
{
if (!object)
{
AML_ERROR("RefOf failed to resolve reference");
return ParseResult::Failure;
}
auto reference = MUST(BAN::RefPtr<Reference>::create(object));
#if AML_DEBUG_LEVEL >= 2
reference->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return ParseResult(reference);
}
if (context.aml_data.size() >= 1 && context.aml_data[0] != 0x00)
{
auto target_result = AML::parse_object(context);
if (!target_result.success())
return ParseResult::Failure;
auto target_node = target_result.node();
if (!target_node)
{
AML_ERROR("CondRefOf failed to resolve target");
return ParseResult::Failure;
}
target_node->store(MUST(BAN::RefPtr<Reference>::create(object)));
}
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINT("CondRefOf ");
if (object)
object->debug_print(0);
else
AML_DEBUG_PRINT("null");
AML_DEBUG_PRINTLN("");
#endif
auto return_value = object ? Integer::Constants::Ones : Integer::Constants::Zero;
return AML::ParseResult(return_value);
}
static ParseResult parse_dereference(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::DerefOfOp);
context.aml_data = context.aml_data.slice(1);
if (context.aml_data.size() >= 1 && static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::StringPrefix)
{
auto string_result = AML::String::parse(context);
if (!string_result.success())
return ParseResult::Failure;
ASSERT(string_result.node());
auto string = static_cast<AML::String*>(string_result.node().ptr());
AML_TODO("DerefOf String ({})", string->string);
return ParseResult::Failure;
}
else
{
auto parse_result = AML::parse_object(context);
if (!parse_result.success())
return ParseResult::Failure;
auto object = parse_result.node();
if (!object || object->type != AML::Node::Type::Reference)
{
AML_TODO("DerefOf source is not a Reference, but a {}", object ? static_cast<uint8_t>(object->type) : 999);
return ParseResult::Failure;
}
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINT("DerefOf ");
object->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return ParseResult(static_cast<Reference*>(object.ptr())->node);
}
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINTLN("Reference {");
node->debug_print(indent + 1);
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("}");
}
};
}

View File

@ -23,15 +23,12 @@ namespace Kernel::ACPI::AML
GenericSerialBus = 9,
PCC = 10,
};
RegionSpace region_space;
uint64_t region_offset;
uint64_t region_length;
RegionSpace space;
uint64_t offset;
uint64_t length;
OpRegion(NameSeg name, RegionSpace region_space, uint64_t region_offset, uint64_t region_length)
: NamedObject(Node::Type::OpRegion, name)
, region_space(region_space)
, region_offset(region_offset)
, region_length(region_length)
OpRegion(NameSeg name, RegionSpace space, uint64_t offset, uint64_t length)
: NamedObject(Node::Type::OpRegion, name), space(space), offset(offset), length(length)
{}
static ParseResult parse(AML::ParseContext& context)
@ -77,7 +74,7 @@ namespace Kernel::ACPI::AML
length.value()
));
if (!Namespace::root_namespace()->add_named_object(context, name.value(), op_region))
if (!context.root_namespace->add_named_object(context.scope.span(), name.value(), op_region))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
@ -91,7 +88,7 @@ namespace Kernel::ACPI::AML
virtual void debug_print(int indent) const override
{
BAN::StringView region_space_name;
switch (region_space)
switch (space)
{
case RegionSpace::SystemMemory: region_space_name = "SystemMemory"sv; break;
case RegionSpace::SystemIO: region_space_name = "SystemIO"sv; break;
@ -109,7 +106,7 @@ namespace Kernel::ACPI::AML
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("OperationRegion(");
name.debug_print();
AML_DEBUG_PRINT(", {}, 0x{H}, 0x{H})", region_space_name, region_offset, region_length);
AML_DEBUG_PRINT(", {}, 0x{H}, 0x{H})", region_space_name, offset, length);
}
};

View File

@ -1,55 +0,0 @@
#pragma once
#include <kernel/ACPI/AML/Node.h>
namespace Kernel::ACPI::AML
{
struct Register : public AML::Node
{
BAN::RefPtr<AML::Node> value;
Register()
: Node(Node::Type::Register)
{}
Register(BAN::RefPtr<AML::Node> value)
: Node(Node::Type::Register)
, value(value)
{}
BAN::RefPtr<AML::Node> evaluate() override
{
if (value)
return value->evaluate();
return {};
}
bool store(BAN::RefPtr<AML::Node> source) override
{
auto evaluated = source->evaluate();
if (!evaluated)
{
AML_ERROR("Failed to evaluate source for store");
return false;
}
value = evaluated;
return true;
}
void debug_print(int indent) const override
{
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

@ -10,11 +10,8 @@ namespace Kernel::ACPI::AML
struct Scope : public AML::NamedObject
{
BAN::HashMap<NameSeg, BAN::RefPtr<NamedObject>> objects;
AML::NameString scope;
Scope(Node::Type type, NameSeg name)
: NamedObject(type, name)
{}
Scope(NameSeg name, Node::Type type = Node::Type::Scope) : NamedObject(type, name) {}
virtual bool is_scope() const override { return true; }
@ -25,6 +22,4 @@ namespace Kernel::ACPI::AML
ParseResult enter_context_and_parse_term_list(ParseContext& outer_context, const AML::NameString& name, BAN::ConstByteSpan aml_data);
};
bool initialize_scope(BAN::RefPtr<Scope> scope);
}

View File

@ -1,38 +0,0 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/Timer/Timer.h>
namespace Kernel::ACPI::AML
{
struct Sleep
{
static ParseResult 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::SleepOp);
context.aml_data = context.aml_data.slice(2);
auto sleep_time_result = AML::parse_object(context);
if (!sleep_time_result.success())
return ParseResult::Failure;
auto sleep_time = sleep_time_result.node() ? sleep_time_result.node()->as_integer() : BAN::Optional<uint64_t>();
if (!sleep_time.has_value())
{
AML_ERROR("Sleep time cannot be evaluated to an integer");
return ParseResult::Failure;
}
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINTLN("Sleeping for {} ms", sleep_time.value());
#endif
SystemTimer::get().sleep(sleep_time.value());
return ParseResult::Success;
}
};
}

View File

@ -1,53 +0,0 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/ParseContext.h>
namespace Kernel::ACPI::AML
{
struct Store
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::StoreOp);
context.aml_data = context.aml_data.slice(1);
auto source_result = AML::parse_object(context);
if (!source_result.success())
return ParseResult::Failure;
auto source = source_result.node() ? source_result.node()->evaluate() : BAN::RefPtr<AML::Node>();
if (!source)
{
AML_ERROR("Store source cannot be evaluated");
return ParseResult::Failure;
}
auto destination_result = AML::parse_object(context);
if (!destination_result.success())
return ParseResult::Failure;
auto destination = destination_result.node();
if (!destination)
{
AML_ERROR("Store destination is null");
return ParseResult::Failure;
}
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINTLN("Storing {");
source->debug_print(1);
AML_DEBUG_PRINTLN("");
AML_DEBUG_PRINTLN("} to {");
destination->debug_print(1);
AML_DEBUG_PRINTLN("");
AML_DEBUG_PRINTLN("}");
#endif
if (!destination->store(source))
return ParseResult::Failure;
return ParseResult::Success;
}
};
}

View File

@ -1,53 +0,0 @@
#pragma once
#include <BAN/Endianness.h>
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/Scope.h>
namespace Kernel::ACPI::AML
{
struct ThermalZone : public AML::Scope
{
ThermalZone(NameSeg name)
: Scope(Node::Type::ThermalZone, name)
{}
static ParseResult 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::PowerResOp);
context.aml_data = context.aml_data.slice(2);
auto opt_thermal_zone_pkg = AML::parse_pkg(context.aml_data);
if (!opt_thermal_zone_pkg.has_value())
return ParseResult::Failure;
auto thermal_zone_pkg = opt_thermal_zone_pkg.value();
auto name = NameString::parse(thermal_zone_pkg);
if (!name.has_value())
return ParseResult::Failure;
auto thermal_zone = MUST(BAN::RefPtr<ThermalZone>::create(name->path.back()));
if (!Namespace::root_namespace()->add_named_object(context, name.value(), thermal_zone))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
thermal_zone->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return thermal_zone->enter_context_and_parse_term_list(context, name.value(), thermal_zone_pkg);
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("ThermalZone {}", name);
}
};
}

View File

@ -71,7 +71,7 @@ namespace Kernel::ACPI
uint8_t reset_value;
uint16_t arm_boot_arch;
uint8_t fadt_minor_version;
uint64_t x_firmware_ctrl;
uint64_t x_firmware_version;
uint64_t x_dsdt;
uint8_t x_pm1a_evt_blk[12];
uint8_t x_pm1b_evt_blk[12];
@ -100,22 +100,6 @@ namespace Kernel::ACPI
uint8_t page_protection_and_oem_attribute;
} __attribute__((packed));
struct FACS
{
uint8_t signature[4];
uint32_t length;
uint32_t hardware_signature;
uint32_t firmware_waking_vector;
uint32_t global_lock;
uint32_t flags;
uint64_t x_firmware_waking_vector;
uint8_t version;
uint8_t reserved[3];
uint32_t ospm_flags;
uint8_t reserved2[24];
};
static_assert(sizeof(FACS) == 64);
}
namespace BAN::Formatter

View File

@ -30,6 +30,8 @@ namespace Kernel
bool is_using_apic() const { return m_using_apic; }
void enter_acpi_mode();
private:
bool m_using_apic { false };
};

View File

@ -2,14 +2,10 @@
#include <BAN/StringView.h>
#include <kernel/ACPI/ACPI.h>
#include <kernel/ACPI/AML.h>
#include <kernel/ACPI/AML/Device.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Method.h>
#include <kernel/ACPI/AML/Package.h>
#include <kernel/BootInfo.h>
#include <kernel/IO.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/Timer/Timer.h>
#include <lai/core.h>
#define RSPD_SIZE 20
#define RSPDv2_SIZE 36
@ -17,85 +13,6 @@
namespace Kernel::ACPI
{
static uint32_t* s_global_lock { nullptr };
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#global-lock
asm(R"(
.global acpi_acquire_global_lock
acpi_acquire_global_lock:
movl (%rdi), %edx
andl $(~1), %edx
btsl $1, %edx
adcl $0, %edx
lock cmpxchgl %edx, (%rdi)
jnz acpi_acquire_global_lock
cmpb $3, %dl
sbbq %rax, %rax
negq %rax
ret
.global acpi_release_global_lock
acpi_release_global_lock:
movl (%rdi), %eax
movl %eax, %edx
andl $(~3), %edx
lock cmpxchgl %edx, (%rdi)
jnz acpi_release_global_lock
andq $1, %rax
ret
)");
// returns true if lock was acquired successfully
extern "C" bool acpi_acquire_global_lock(uint32_t* lock);
// returns true if lock was pending
extern "C" bool acpi_release_global_lock(uint32_t* lock);
void ACPI::acquire_global_lock()
{
if (!s_global_lock)
return;
derrorln("Acquiring ACPI global lock");
ASSERT(acpi_acquire_global_lock(s_global_lock));
}
void ACPI::release_global_lock()
{
if (!s_global_lock)
return;
derrorln("Releasing ACPI global lock");
ASSERT(!acpi_release_global_lock(s_global_lock));
}
enum PM1Event : uint16_t
{
PM1_EVN_TMR_EN = 1 << 0,
PM1_EVN_GBL_EN = 1 << 5,
PM1_EVN_PWRBTN_EN = 1 << 8,
PM1_EVN_SLPBTN_EN = 1 << 8,
PM1_EVN_RTC_EN = 1 << 10,
PM1_EVN_PCIEXP_WAKE_DIS = 1 << 14,
};
enum PM1Control : uint16_t
{
PM1_CNT_SCI_EN = 1 << 0,
PM1_CNT_BM_RLD = 1 << 1,
PM1_CNT_GBL_RLS = 1 << 2,
PM1_CNT_SLP_EN = 1 << 13,
PM1_CNT_SLP_TYP_MASK = 0b111,
PM1_CNT_SLP_TYP_SHIFT = 10,
};
struct RSDT : public SDTHeader
{
uint32_t entries[];
@ -116,24 +33,13 @@ acpi_release_global_lock:
return BAN::Error::from_errno(ENOMEM);
TRY(s_instance->initialize_impl());
{
ASSERT(!s_global_lock);
const auto* fadt = static_cast<const FADT*>(ACPI::get().get_header("FACP"sv, 0));
ASSERT(fadt);
uintptr_t facs_addr = fadt->firmware_ctrl;
if (fadt->length >= sizeof(FADT) && fadt->x_firmware_ctrl)
facs_addr = fadt->x_firmware_ctrl;
if (facs_addr)
{
auto* facs = reinterpret_cast<FACS*>(facs_addr);
s_global_lock = &facs->global_lock;
}
}
s_instance->m_namespace = AML::initialize_namespace();
auto dsdt = s_instance->get_header("DSDT", 0);
ASSERT(dsdt);
s_instance->m_namespace = AMLParser::parse_table(*dsdt);
#if ARCH(x86_64)
lai_create_namespace();
#endif
return {};
}
@ -195,6 +101,9 @@ acpi_release_global_lock:
const RSDP* rsdp = locate_rsdp();
if (rsdp == nullptr)
return BAN::Error::from_error_code(ErrorCode::ACPI_NoRootSDT);
#if ARCH(x86_64)
lai_set_acpi_revision(rsdp->revision);
#endif
uint32_t root_entry_count = 0;
@ -340,156 +249,4 @@ acpi_release_global_lock:
return nullptr;
}
void ACPI::poweroff()
{
if (!m_namespace)
{
dwarnln("ACPI namespace not initialized");
return;
}
auto s5_object = m_namespace->find_object({}, AML::NameString("\\_S5"));
if (!s5_object)
{
dwarnln("\\_S5 not found");
return;
}
auto s5_evaluated = s5_object->evaluate();
if (!s5_evaluated)
{
dwarnln("Failed to evaluate \\_S5");
return;
}
if (s5_evaluated->type != AML::Node::Type::Package)
{
dwarnln("\\_S5 is not a package");
return;
}
auto* s5_package = static_cast<AML::Package*>(s5_evaluated.ptr());
if (s5_package->elements.size() != 4)
{
dwarnln("\\_S5 package has {} elements, expected 4", s5_package->elements.size());
return;
}
auto slp_typa = s5_package->elements[0]->as_integer();
auto slp_typb = s5_package->elements[1]->as_integer();
if (!slp_typa.has_value() || !slp_typb.has_value())
{
dwarnln("Failed to get SLP_TYPx values");
return;
}
auto pts_object = m_namespace->find_object({}, AML::NameString("\\_PTS"));
if (pts_object && pts_object->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;
}
AML::Method::Arguments args;
args[0] = MUST(BAN::RefPtr<AML::Register>::create(MUST(BAN::RefPtr<AML::Integer>::create(5))));
BAN::Vector<uint8_t> sync_stack;
if (!method->evaluate(args, sync_stack).has_value())
{
dwarnln("Failed to evaluate \\_PTS");
return;
}
dprintln("Executed \\_PTS");
}
dprintln("Entering sleep state S5");
auto* fadt = static_cast<const FADT*>(get_header("FACP", 0));
uint16_t pm1a_data = IO::inw(fadt->pm1a_cnt_blk);
pm1a_data &= ~(PM1_CNT_SLP_TYP_MASK << PM1_CNT_SLP_TYP_SHIFT);
pm1a_data |= (slp_typa.value() & PM1_CNT_SLP_TYP_MASK) << PM1_CNT_SLP_TYP_SHIFT;
pm1a_data |= PM1_CNT_SLP_EN;
IO::outw(fadt->pm1a_cnt_blk, pm1a_data);
if (fadt->pm1b_cnt_blk != 0)
{
uint16_t pm1b_data = IO::inw(fadt->pm1b_cnt_blk);
pm1b_data &= ~(PM1_CNT_SLP_TYP_MASK << PM1_CNT_SLP_TYP_SHIFT);
pm1b_data |= (slp_typb.value() & PM1_CNT_SLP_TYP_MASK) << PM1_CNT_SLP_TYP_SHIFT;
pm1b_data |= PM1_CNT_SLP_EN;
IO::outw(fadt->pm1b_cnt_blk, pm1b_data);
}
}
BAN::ErrorOr<void> ACPI::enter_acpi_mode(uint8_t mode)
{
if (!m_namespace)
{
dwarnln("ACPI namespace not initialized");
return BAN::Error::from_errno(EFAULT);
}
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/16_Waking_and_Sleeping/initialization.html#placing-the-system-in-acpi-mode
auto* fadt = static_cast<const FADT*>(get_header("FACP", 0));
// If not hardware-reduced ACPI and SCI_EN is not set
if (!(fadt->flags & (1 << 20)) && IO::inw(fadt->pm1a_cnt_blk) & PM1_CNT_SCI_EN)
{
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/04_ACPI_Hardware_Specification/ACPI_Hardware_Specification.html#legacy-acpi-select-and-the-sci-interrupt
IO::outb(fadt->smi_cmd, fadt->acpi_enable);
// Spec says to poll until SCI_EN is set, but doesn't specify timeout
for (size_t i = 0; i < 100; i++)
{
if (IO::inw(fadt->pm1a_cnt_blk) & PM1_CNT_SCI_EN)
break;
SystemTimer::get().sleep(10);
}
if (!(IO::inw(fadt->pm1a_cnt_blk) & PM1_CNT_SCI_EN))
{
dwarnln("Failed to enable ACPI mode");
return BAN::Error::from_errno(EINVAL);
}
// Enable power and sleep buttons
IO::outw(fadt->pm1a_evt_blk + fadt->pm1_evt_len / 2, PM1_EVN_PWRBTN_EN | PM1_EVN_SLPBTN_EN);
IO::outw(fadt->pm1b_evt_blk + fadt->pm1_evt_len / 2, PM1_EVN_PWRBTN_EN | PM1_EVN_SLPBTN_EN);
}
dprintln("Entered ACPI mode");
dprintln("Initializing devices");
// Initialize \\_SB
auto _sb = m_namespace->find_object({}, AML::NameString("\\_SB"));
if (_sb && _sb->is_scope())
{
auto* scope = static_cast<AML::Scope*>(_sb.ptr());
AML::initialize_scope(scope);
}
// Evaluate \\_PIC (mode)
auto _pic = m_namespace->find_object({}, AML::NameString("\\_PIC"));
if (_pic && _pic->type == AML::Node::Type::Method)
{
auto* method = static_cast<AML::Method*>(_pic.ptr());
if (method->arg_count != 1)
{
dwarnln("Method \\_PIC has {} arguments, expected 1", method->arg_count);
return BAN::Error::from_errno(EINVAL);
}
AML::Method::Arguments args;
args[0] = MUST(BAN::RefPtr<AML::Register>::create(MUST(BAN::RefPtr<AML::Integer>::create(mode))));
BAN::Vector<uint8_t> sync_stack;
method->evaluate(args, sync_stack);
}
dprintln("Devices are initialized");
return {};
}
}

View File

@ -6,78 +6,29 @@
namespace Kernel::ACPI
{
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#secondary-system-description-table-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
static bool load_all_unique(AML::Namespace& ns, BAN::StringView signature)
AMLParser::AMLParser() = default;
AMLParser::~AMLParser() = default;
BAN::RefPtr<AML::Namespace> AMLParser::parse_table(const SDTHeader& header)
{
// Only SSDT and PSDT that have unique OEM Table ID are loaded in order they appear in the RSDT/XSDT
BAN::Vector<uint64_t> loaded_oem_table_ids;
dprintln("Parsing {}, {} bytes of AML", header, header.length);
for (uint32_t i = 0;; i++)
auto aml_raw = BAN::ConstByteSpan { reinterpret_cast<const uint8_t*>(&header), header.length };
aml_raw = aml_raw.slice(sizeof(header));
auto ns = AML::Namespace::parse(aml_raw);
if (!ns)
{
auto* header = ACPI::ACPI::get().get_header(signature, i);
if (!header)
break;
bool need_to_parse = true;
for (uint64_t id : loaded_oem_table_ids)
{
if (id == header->oem_table_id)
{
need_to_parse = false;
break;
}
}
if (!need_to_parse)
{
dprintln("Skipping {}{} ({} bytes)", signature, i, header->length);
continue;
}
dprintln("Parsing {}{} ({} bytes)", signature, i, header->length);
if (!ns.parse(*header))
{
dwarnln("Failed to parse {}", signature);
return false;
}
MUST(loaded_oem_table_ids.push_back(header->oem_table_id));
}
return true;
}
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");
dwarnln("Failed to parse ACPI namespace");
return {};
}
dprintln("Parsing DSDT ({} bytes)", dsdt->length);
if (!ns->parse(*dsdt))
{
dwarnln("Failed to parse DSDT");
return {};
}
if (!load_all_unique(*ns, "SSDT"))
return {};
if (!load_all_unique(*ns, "PSDT"))
return {};
#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);
dprintln("Parsed ACPI namespace");
return ns;
}

View File

@ -1,25 +1,8 @@
#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
{
@ -32,8 +15,6 @@ namespace Kernel::ACPI
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])
{
@ -53,60 +34,10 @@ namespace Kernel::ACPI
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;
}
AML_TODO("Field element {2H}", context.field_pkg[0]);
return false;
default:
{
auto element_name = AML::NameSeg::parse(context.field_pkg);
@ -146,248 +77,6 @@ namespace Kernel::ACPI
}
}
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::read_config_byte(0, device, function, offset); break;
case 2: result = PCI::PCIManager::read_config_word(0, device, function, offset); break;
case 4: result = PCI::PCIManager::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::write_config_byte(0, device, function, offset, value); break;
case 2: PCI::PCIManager::write_config_word(0, device, function, offset, value); break;
case 4: PCI::PCIManager::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);
@ -404,10 +93,10 @@ namespace Kernel::ACPI
if (!name_string.has_value())
return ParseResult::Failure;
auto op_region = Namespace::root_namespace()->find_object(context.scope, name_string.value());
auto op_region = context.root_namespace->find_object(context.scope.span(), name_string.value());
if (!op_region || op_region->type != AML::Node::Type::OpRegion)
{
AML_ERROR("FieldOp: {} does not name a valid OpRegion", name_string.value());
AML_ERROR("Field RegionName does not name a valid OpRegion");
return ParseResult::Failure;
}
@ -432,7 +121,7 @@ namespace Kernel::ACPI
NameString element_name;
MUST(element_name.path.push_back(element->name));
if (!Namespace::root_namespace()->add_named_object(context, element_name, element))
if (!context.root_namespace->add_named_object(context.scope.span(), element_name, element))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
@ -444,77 +133,14 @@ namespace Kernel::ACPI
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::Node> AML::FieldElement::evaluate()
{
auto result = evaluate_internal();
if (!result.has_value())
return {};
return MUST(BAN::RefPtr<Integer>::create(result.value()));
}
bool AML::FieldElement::store(BAN::RefPtr<AML::Node> source)
{
auto source_integer = source->as_integer();
if (!source_integer.has_value())
{
AML_TODO("FieldElement store with non-integer source, type {}", static_cast<uint8_t>(source->type));
return false;
}
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::acquire_global_lock();
BAN::ScopeGuard unlock_guard([&] {
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::release_global_lock();
});
return store_internal(source_integer.value());
}
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_DEBUG_PRINT("FieldElement ");
name.debug_print();
AML_DEBUG_PRINT("({}, offset {}, OpRegion ", bit_count, bit_offset);
op_region->name.debug_print();
AML_DEBUG_PRINT(")");
}
AML::ParseResult AML::IndexField::parse(ParseContext& context)
@ -532,7 +158,7 @@ namespace Kernel::ACPI
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());
auto index_field_element = context.root_namespace->find_object(context.scope.span(), index_field_element_name.value());
if (!index_field_element || index_field_element->type != AML::Node::Type::FieldElement)
{
AML_ERROR("IndexField IndexName does not name a valid FieldElement");
@ -542,7 +168,7 @@ namespace Kernel::ACPI
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());
auto data_field_element = context.root_namespace->find_object(context.scope.span(), data_field_element_name.value());
if (!data_field_element || data_field_element->type != AML::Node::Type::FieldElement)
{
AML_ERROR("IndexField DataName does not name a valid FieldElement");
@ -571,7 +197,7 @@ namespace Kernel::ACPI
NameString element_name;
MUST(element_name.path.push_back(element->name));
if (!Namespace::root_namespace()->add_named_object(context, element_name, element))
if (!context.root_namespace->add_named_object(context.scope.span(), element_name, element))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
@ -583,194 +209,16 @@ namespace Kernel::ACPI
return AML::ParseResult::Success;
}
BAN::RefPtr<AML::Node> AML::IndexFieldElement::evaluate()
{
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 {};
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();
};
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::acquire_global_lock();
BAN::ScopeGuard unlock_guard([&] {
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()));
}
bool 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 {};
}
auto source_integer = source->as_integer();
if (!source_integer.has_value())
{
AML_TODO("IndexFieldElement store with non-integer source, type {}", static_cast<uint8_t>(source->type));
return false;
}
auto access_size = determine_access_size(access_rules.access_type);
if (!access_size.has_value())
return false;
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 false;
}
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 false;
return data_element->store_internal(value);
};
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::acquire_global_lock();
BAN::ScopeGuard unlock_guard([&] {
if (access_rules.lock_rule == FieldRules::LockRule::Lock)
ACPI::release_global_lock();
});
if (!perform_write_general(0, bit_count, bit_offset, access_size.value(), source_integer.value(), access_rules.update_rule, read_func, write_func))
return false;
return true;
}
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());
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());
if (!bank_selector)
{
AML_ERROR("BankField BankSelector {} does not name a valid object", bank_selector_name.value());
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 = bank_value_result.node() ? bank_value_result.node()->as_integer() : BAN::Optional<uint64_t>();
if (!bank_value.has_value())
{
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;
for (auto& [_, element] : field_context.elements)
{
element->op_region = static_cast<OpRegion*>(op_region.ptr());
element->bank_selector = bank_selector;
element->bank_value = bank_value.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;
}
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
);
AML_DEBUG_PRINT("IndexFieldElement ");
name.debug_print();
AML_DEBUG_PRINT("({}, offset {}, IndexName ", bit_count, bit_offset);
index_element->name.debug_print();
AML_DEBUG_PRINT(", DataName ");
data_element->name.debug_print();
AML_DEBUG_PRINT(")");
}
}

View File

@ -19,7 +19,7 @@ namespace Kernel::ACPI
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))
if (!context.root_namespace->add_named_object(context.scope.span(), name_string.value(), name))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
@ -33,8 +33,12 @@ namespace Kernel::ACPI
void AML::Name::debug_print(int indent) const
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("Name {} { ", name);
object->debug_print(0);
AML_DEBUG_PRINT("Name ");
name.debug_print();
AML_DEBUG_PRINTLN(" {");
object->debug_print(indent + 1);
AML_DEBUG_PRINTLN("");
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("}");
}

View File

@ -1,5 +1,3 @@
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Method.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Region.h>
@ -7,130 +5,69 @@
namespace Kernel::ACPI
{
static BAN::RefPtr<AML::Namespace> s_root_namespace;
static BAN::Vector<uint8_t> s_osi_aml_data;
BAN::RefPtr<AML::Integer> AML::Integer::Constants::Zero;
BAN::RefPtr<AML::Integer> AML::Integer::Constants::One;
BAN::RefPtr<AML::Integer> AML::Integer::Constants::Ones;
BAN::RefPtr<AML::Namespace> AML::Namespace::root_namespace()
BAN::Optional<BAN::Vector<AML::NameSeg>> AML::Namespace::resolve_path(BAN::Span<const AML::NameSeg> parsing_scope, const AML::NameString& relative_path)
{
ASSERT(s_root_namespace);
return s_root_namespace;
}
BAN::Vector<NameSeg> canonical_path;
BAN::Optional<AML::NameString> AML::Namespace::resolve_path(const AML::NameString& relative_base, const AML::NameString& relative_path)
{
// 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)
if (!relative_path.prefix.empty())
{
AML::NameString absolute_path;
MUST(absolute_path.prefix.push_back('\\'));
// Resolve root and parent references
if (relative_path.prefix == "\\"sv)
if (relative_path.prefix[0] == '\\')
;
else
{
if (relative_path.prefix.size() > relative_base.path.size())
if (parsing_scope.size() < relative_path.prefix.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.path.push_back(relative_base.path[i]));
for (size_t i = 0; i < parsing_scope.size() - relative_path.prefix.size(); i++)
MUST(canonical_path.push_back(parsing_scope[i]));
}
// Append relative path
for (const auto& seg : relative_path.path)
MUST(absolute_path.path.push_back(seg));
// Validate path
BAN::RefPtr<AML::NamedObject> current_node = this;
for (const auto& seg : absolute_path.path)
{
if (!current_node->is_scope())
return {};
auto* current_scope = static_cast<AML::Scope*>(current_node.ptr());
auto it = current_scope->objects.find(seg);
if (it == current_scope->objects.end())
return {};
current_node = it->value;
}
return absolute_path;
}
// Resolve with namespace search rules (ACPI Spec 6.4 - Section 5.3)
AML::NameString last_match_path;
AML::NameSeg target_seg = relative_path.path.back();
BAN::RefPtr<AML::Scope> current_scope = this;
AML::NameString current_path;
// Check root namespace
else
{
// If scope contains object with the same name as the segment, update last match
if (current_scope->objects.contains(target_seg))
{
last_match_path = current_path;
MUST(last_match_path.path.push_back(target_seg));
}
for (auto seg : parsing_scope)
MUST(canonical_path.push_back(seg));
}
// Check base base path
for (const auto& seg : relative_base.path)
{
auto next_node = current_scope->objects[seg];
ASSERT(next_node && next_node->is_scope());
for (const auto& seg : relative_path.path)
MUST(canonical_path.push_back(seg));
current_scope = static_cast<AML::Scope*>(next_node.ptr());
MUST(current_path.path.push_back(seg));
// If scope contains object with the same name as the segment, update last match
if (current_scope->objects.contains(target_seg))
{
last_match_path = current_path;
MUST(last_match_path.path.push_back(target_seg));
}
}
if (!last_match_path.path.empty())
{
MUST(last_match_path.prefix.push_back('\\'));
return last_match_path;
}
return {};
return canonical_path;
}
BAN::RefPtr<AML::NamedObject> AML::Namespace::find_object(const AML::NameString& relative_base, const AML::NameString& relative_path)
BAN::RefPtr<AML::NamedObject> AML::Namespace::find_object(BAN::Span<const AML::NameSeg> parsing_scope, const AML::NameString& relative_path)
{
auto canonical_path = resolve_path(relative_base, relative_path);
auto canonical_path = resolve_path(parsing_scope, relative_path);
if (!canonical_path.has_value())
return nullptr;
if (canonical_path->path.empty())
if (canonical_path->empty())
return this;
BAN::RefPtr<NamedObject> node = this;
for (const auto& seg : canonical_path->path)
BAN::RefPtr<NamedObject> parent_object = this;
for (const auto& seg : canonical_path.value())
{
// Resolve path validates that all nodes are scopes
ASSERT(node->is_scope());
node = static_cast<Scope*>(node.ptr())->objects[seg];
if (!parent_object->is_scope())
{
AML_ERROR("Parent object is not a scope");
return nullptr;
}
auto* parent_scope = static_cast<Scope*>(parent_object.ptr());
auto it = parent_scope->objects.find(seg);
if (it == parent_scope->objects.end())
return nullptr;
parent_object = it->value;
ASSERT(parent_object);
}
return node;
return parent_object;
}
bool AML::Namespace::add_named_object(ParseContext& parse_context, const AML::NameString& object_path, BAN::RefPtr<NamedObject> object)
bool AML::Namespace::add_named_object(BAN::Span<const NameSeg> parsing_scope, const AML::NameString& object_path, BAN::RefPtr<NamedObject> object)
{
ASSERT(!object_path.path.empty());
ASSERT(object_path.path.back() == object->name);
@ -138,7 +75,7 @@ namespace Kernel::ACPI
auto parent_path = object_path;
parent_path.path.pop_back();
auto parent_object = find_object(parse_context.scope, parent_path);
auto parent_object = find_object(parsing_scope, parent_path);
if (!parent_object)
{
AML_ERROR("Parent object not found");
@ -158,85 +95,17 @@ namespace Kernel::ACPI
return false;
}
object->parent = parent_scope;
MUST(parent_scope->objects.insert(object->name, object));
auto canonical_scope = resolve_path(parse_context.scope, object_path);
ASSERT(canonical_scope.has_value());
if (object->is_scope())
{
auto* scope = static_cast<Scope*>(object.ptr());
scope->scope = canonical_scope.value();
}
MUST(parse_context.created_objects.push_back(BAN::move(canonical_scope.release_value())));
return true;
}
bool AML::Namespace::remove_named_object(const AML::NameString& absolute_path)
BAN::RefPtr<AML::Namespace> AML::Namespace::parse(BAN::ConstByteSpan aml_data)
{
auto object = find_object({}, absolute_path);
if (!object)
{
AML_ERROR("Object {} not found", absolute_path);
return false;
}
if (object.ptr() == this)
{
AML_ERROR("Trying to remove root object");
return false;
}
auto parent = object->parent;
ASSERT(parent->is_scope());
auto* parent_scope = static_cast<Scope*>(parent.ptr());
parent_scope->objects.remove(object->name);
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)));
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));
auto result = MUST(BAN::RefPtr<Namespace>::create());
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::Namespace>::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
// Add dummy \_OSI
MUST(s_osi_aml_data.push_back(static_cast<uint8_t>(Byte::ReturnOp)));
MUST(s_osi_aml_data.push_back(static_cast<uint8_t>(Byte::ZeroOp)));
auto osi = MUST(BAN::RefPtr<AML::Method>::create(NameSeg("_OSI"sv), 1, false, 0));
osi->term_list = s_osi_aml_data.span();
ASSERT(s_root_namespace->add_named_object(context, AML::NameString("\\_OSI"), osi));
return s_root_namespace;
}
bool AML::Namespace::parse(const SDTHeader& header)
{
ASSERT(this == s_root_namespace.ptr());
AML::ParseContext context;
context.scope = AML::NameString("\\"sv);
context.aml_data = BAN::ConstByteSpan(reinterpret_cast<const uint8_t*>(&header), header.length).slice(sizeof(header));
context.aml_data = aml_data;
context.root_namespace = result.ptr();
while (context.aml_data.size() > 0)
{
@ -244,11 +113,11 @@ namespace Kernel::ACPI
if (!result.success())
{
AML_ERROR("Failed to parse object");
return false;
return {};
}
}
return true;
return result;
}
}

View File

@ -1,10 +1,7 @@
#include <kernel/ACPI/AML/Buffer.h>
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Device.h>
#include <kernel/ACPI/AML/Expression.h>
#include <kernel/ACPI/AML/Field.h>
#include <kernel/ACPI/AML/IfElse.h>
#include <kernel/ACPI/AML/Index.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Method.h>
#include <kernel/ACPI/AML/Mutex.h>
@ -12,14 +9,9 @@
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/Package.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/PowerResource.h>
#include <kernel/ACPI/AML/Processor.h>
#include <kernel/ACPI/AML/Reference.h>
#include <kernel/ACPI/AML/Region.h>
#include <kernel/ACPI/AML/Sleep.h>
#include <kernel/ACPI/AML/Store.h>
#include <kernel/ACPI/AML/String.h>
#include <kernel/ACPI/AML/ThermalZone.h>
#include <kernel/ACPI/AML/Utils.h>
namespace Kernel::ACPI
@ -28,20 +20,6 @@ namespace Kernel::ACPI
AML::ParseResult AML::ParseResult::Failure = AML::ParseResult(AML::ParseResult::Result::Failure);
AML::ParseResult AML::ParseResult::Success = AML::ParseResult(AML::ParseResult::Result::Success);
uint64_t AML::Node::total_node_count = 0;
BAN::Optional<uint64_t> AML::Node::as_integer()
{
if (type == Type::Integer)
return static_cast<const Integer*>(this)->value;
auto evaluated = evaluate();
if (!evaluated)
return {};
if (evaluated->type == Type::Integer)
return static_cast<const Integer*>(evaluated.ptr())->value;
return {};
}
AML::ParseResult AML::parse_object(AML::ParseContext& context)
{
if (context.aml_data.size() < 1)
@ -58,26 +36,14 @@ namespace Kernel::ACPI
return AML::Field::parse(context);
case AML::ExtOp::IndexFieldOp:
return AML::IndexField::parse(context);
case AML::ExtOp::BankFieldOp:
return AML::BankField::parse(context);
case AML::ExtOp::OpRegionOp:
return AML::OpRegion::parse(context);
case AML::ExtOp::DeviceOp:
return AML::Device::parse(context);
case AML::ExtOp::MutexOp:
case AML::ExtOp::AcquireOp:
case AML::ExtOp::ReleaseOp:
return AML::Mutex::parse(context);
case AML::ExtOp::ProcessorOp:
return AML::Processor::parse(context);
case AML::ExtOp::PowerResOp:
return AML::PowerResource::parse(context);
case AML::ExtOp::ThermalZoneOp:
return AML::ThermalZone::parse(context);
case AML::ExtOp::CondRefOfOp:
return AML::Reference::parse(context);
case AML::ExtOp::SleepOp:
return AML::Sleep::parse(context);
default:
break;
}
@ -98,59 +64,6 @@ namespace Kernel::ACPI
return AML::Integer::parse(context.aml_data);
case AML::Byte::StringPrefix:
return AML::String::parse(context);
case AML::Byte::Arg0:
case AML::Byte::Arg1:
case AML::Byte::Arg2:
case AML::Byte::Arg3:
case AML::Byte::Arg4:
case AML::Byte::Arg5:
case AML::Byte::Arg6:
{
uint8_t index = context.aml_data[0] - static_cast<uint8_t>(AML::Byte::Arg0);
context.aml_data = context.aml_data.slice(1);
return ParseResult(context.method_args[index]);
}
case AML::Byte::Local0:
case AML::Byte::Local1:
case AML::Byte::Local2:
case AML::Byte::Local3:
case AML::Byte::Local4:
case AML::Byte::Local5:
case AML::Byte::Local6:
case AML::Byte::Local7:
{
uint8_t index = context.aml_data[0] - static_cast<uint8_t>(AML::Byte::Local0);
context.aml_data = context.aml_data.slice(1);
return ParseResult(context.method_locals[index]);
}
case AML::Byte::AddOp:
case AML::Byte::AndOp:
case AML::Byte::DecrementOp:
case AML::Byte::DivideOp:
case AML::Byte::IncrementOp:
case AML::Byte::LAndOp:
case AML::Byte::LEqualOp:
case AML::Byte::LGreaterOp:
case AML::Byte::LLessOp:
case AML::Byte::LNotOp:
case AML::Byte::LOrOp:
case AML::Byte::ModOp:
case AML::Byte::MultiplyOp:
case AML::Byte::NandOp:
case AML::Byte::NorOp:
case AML::Byte::NotOp:
case AML::Byte::OrOp:
case AML::Byte::ShiftLeftOp:
case AML::Byte::ShiftRightOp:
case AML::Byte::SubtractOp:
case AML::Byte::XorOp:
return AML::Expression::parse(context);
case AML::Byte::CreateBitFieldOp:
case AML::Byte::CreateByteFieldOp:
case AML::Byte::CreateWordFieldOp:
case AML::Byte::CreateDWordFieldOp:
case AML::Byte::CreateQWordFieldOp:
return AML::BufferField::parse(context);
case AML::Byte::NameOp:
return AML::Name::parse(context);
case AML::Byte::PackageOp:
@ -161,24 +74,6 @@ namespace Kernel::ACPI
return AML::Buffer::parse(context);
case AML::Byte::ScopeOp:
return AML::Scope::parse(context);
case AML::Byte::IfOp:
return AML::IfElse::parse(context);
case AML::Byte::StoreOp:
return AML::Store::parse(context);
case AML::Byte::DerefOfOp:
case AML::Byte::RefOfOp:
return AML::Reference::parse(context);
case AML::Byte::IndexOp:
return AML::Index::parse(context);
case AML::Byte::ReturnOp:
{
context.aml_data = context.aml_data.slice(1);
auto result = AML::parse_object(context);
if (result.success())
return ParseResult(ParseResult::Result::Returned, result.node());
AML_ERROR("Failed to parse return value for method {}", context.scope);
return ParseResult::Failure;
}
default:
break;
}
@ -191,38 +86,12 @@ namespace Kernel::ACPI
auto name_string = AML::NameString::parse(context.aml_data);
if (!name_string.has_value())
return ParseResult::Failure;
auto aml_object = Namespace::root_namespace()->find_object(context.scope, name_string.value());
auto aml_object = context.root_namespace->find_object(context.scope.span(), name_string.value());
if (!aml_object)
{
AML_ERROR("NameString {} not found in namespace", name_string.value());
AML_TODO("NameString not found in namespace");
return ParseResult::Failure;
}
if (aml_object->type == AML::Node::Type::Method)
{
auto* method = static_cast<AML::Method*>(aml_object.ptr());
Method::Arguments args;
for (uint8_t i = 0; i < method->arg_count; i++)
{
auto arg = AML::parse_object(context);
if (!arg.success())
{
AML_ERROR("Failed to parse argument {} for method {}", i, name_string.value());
return ParseResult::Failure;
}
args[i] = MUST(BAN::RefPtr<AML::Register>::create(arg.node()));
}
auto result = method->evaluate(args, context.sync_stack);
if (!result.has_value())
{
AML_ERROR("Failed to evaluate {}", name_string.value());
return ParseResult::Failure;
}
if (!result.value())
return ParseResult::Success;
return ParseResult(result.value());
}
return ParseResult(aml_object);
}

View File

@ -20,47 +20,42 @@ namespace Kernel::ACPI
if (!name_string.has_value())
return ParseResult::Failure;
auto named_object = Namespace::root_namespace()->find_object(context.scope, name_string.value());
if (!named_object)
BAN::RefPtr<Scope> scope;
if (auto named_object = context.root_namespace->find_object(context.scope.span(), name_string.value()))
{
AML_ERROR("Scope name {} not found in namespace", name_string.value());
return ParseResult::Failure;
if (!named_object->is_scope())
{
AML_ERROR("Scope name already exists and is not a scope");
return ParseResult::Failure;
}
scope = static_cast<Scope*>(named_object.ptr());
}
if (!named_object->is_scope())
else
{
AML_ERROR("Scope name {} does not name a namespace", name_string.value());
return ParseResult::Failure;
scope = MUST(BAN::RefPtr<Scope>::create(name_string->path.back()));
if (!context.root_namespace->add_named_object(context.scope.span(), name_string.value(), scope))
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);
if (!resolved_scope.has_value())
auto scope = outer_context.root_namespace->resolve_path(outer_context.scope.span(), name_string);
if (!scope.has_value())
return ParseResult::Failure;
ParseContext scope_context;
scope_context.scope = resolved_scope.release_value();
ParseContext scope_context = outer_context;
scope_context.scope = 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;
}
@ -79,72 +74,4 @@ namespace Kernel::ACPI
AML_DEBUG_PRINT("}");
}
bool AML::initialize_scope(BAN::RefPtr<AML::Scope> scope)
{
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINTLN("Initializing {}", scope->scope);
#endif
bool run_ini = true;
bool init_children = true;
auto it = scope->objects.find(NameSeg("_STA"sv));
if (scope->type != AML::Node::Type::Namespace && it != scope->objects.end() && it->value->type == Node::Type::Method)
{
auto* method = static_cast<Method*>(it->value.ptr());
if (method->arg_count != 0)
{
AML_ERROR("Method {}._STA has {} arguments, expected 0", scope->scope, method->arg_count);
return false;
}
BAN::Vector<uint8_t> sync_stack;
auto result = method->evaluate({}, sync_stack);
if (!result.has_value())
{
AML_ERROR("Failed to evaluate {}._STA, ignoring device", scope->scope);
return true;
}
auto result_value = result.has_value() ? result.value()->as_integer() : BAN::Optional<uint64_t>();
if (!result_value.has_value())
{
AML_ERROR("Failed to evaluate {}._STA, return value could not be resolved to integer", scope->scope);
AML_ERROR(" Return value: ");
result.value()->debug_print(0);
return false;
}
run_ini = (result_value.value() & 0x01);
init_children = run_ini || (result_value.value() & 0x02);
}
if (run_ini)
{
auto it = scope->objects.find(NameSeg("_STA"sv));
if (it != scope->objects.end() && it->value->type == Node::Type::Method)
{
auto* method = static_cast<Method*>(it->value.ptr());
if (method->arg_count != 0)
{
AML_ERROR("Method {}._INI has {} arguments, expected 0", scope->scope, method->arg_count);
return false;
}
BAN::Vector<uint8_t> sync_stack;
method->evaluate({}, sync_stack);
}
}
bool success = true;
if (init_children)
{
for (auto& [_, child] : scope->objects)
{
if (!child->is_scope())
continue;
auto* child_scope = static_cast<Scope*>(child.ptr());
if (!initialize_scope(child_scope))
success = false;
}
}
return success;
}
}

View File

@ -3,6 +3,8 @@
#include <kernel/APIC.h>
#include <kernel/PIC.h>
#include <lai/helpers/sci.h>
namespace Kernel
{
@ -43,4 +45,12 @@ namespace Kernel
return s_instance;
}
void InterruptController::enter_acpi_mode()
{
#if ARCH(x86_64)
if (lai_enable_acpi(m_using_apic ? 1 : 0) != 0)
#endif
dwarnln("could not enter acpi mode");
}
}

View File

@ -1,6 +1,5 @@
#include <BAN/ScopeGuard.h>
#include <BAN/StringView.h>
#include <kernel/ACPI/ACPI.h>
#include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/FS/ProcFS/FileSystem.h>
#include <kernel/FS/VirtualFileSystem.h>
@ -18,6 +17,8 @@
#include <LibELF/LoadableELF.h>
#include <lai/helpers/pm.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/banan-os.h>
@ -1190,7 +1191,11 @@ namespace Kernel
[[noreturn]] static void reset_system()
{
// TODO: ACPI reset
#if ARCH(x86_64)
lai_acpi_reset();
#endif
// acpi reset did not work
dwarnln("Could not reset with ACPI, crashing the cpu");
@ -1210,7 +1215,13 @@ namespace Kernel
if (command == POWEROFF_REBOOT)
reset_system();
ACPI::ACPI::get().poweroff();
#if ARCH(x86_64)
auto error = lai_enter_sleep(5);
// If we reach here, there was an error
dprintln("{}", lai_api_error_to_string(error));
#else
dprintln("poweroff available only on x86_64");
#endif
return BAN::Error::from_errno(EUNKNOWN);
}

View File

@ -174,13 +174,12 @@ static void init2(void*)
dprintln("Scheduler started");
InterruptController::get().enter_acpi_mode();
auto console = MUST(DevFileSystem::get().root_inode()->find_inode(cmdline.console));
ASSERT(console->is_tty());
((TTY*)console.ptr())->set_as_current();
if (ACPI::ACPI::get().enter_acpi_mode(InterruptController::get().is_using_apic()).is_error())
dprintln("Failed to enter ACPI mode");
DevFileSystem::get().initialize_device_updater();
#if 0

149
kernel/kernel/lai_host.cpp Normal file
View File

@ -0,0 +1,149 @@
#include <kernel/ACPI/ACPI.h>
#include <kernel/IO.h>
#include <kernel/Memory/kmalloc.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/PCI.h>
#include <kernel/Timer/Timer.h>
#include <lai/host.h>
using namespace Kernel;
void* laihost_malloc(size_t size)
{
return kmalloc(size);
}
void* laihost_realloc(void* ptr, size_t newsize, size_t oldsize)
{
if (ptr == nullptr)
return laihost_malloc(newsize);
void* new_ptr = laihost_malloc(newsize);
if (new_ptr == nullptr)
return nullptr;
memcpy(new_ptr, ptr, BAN::Math::min(newsize, oldsize));
kfree(ptr);
return new_ptr;
}
void laihost_free(void* ptr, size_t)
{
kfree(ptr);
}
void laihost_log(int level, const char* msg)
{
if (level == LAI_DEBUG_LOG)
dprintln(msg);
else if (level == LAI_WARN_LOG)
dwarnln(msg);
else
ASSERT_NOT_REACHED();
}
void laihost_panic(const char* msg)
{
Kernel::panic(msg);
}
void* laihost_scan(const char* sig, size_t index)
{
return (void*)ACPI::ACPI::get().get_header(sig, index);
}
void* laihost_map(size_t address, size_t count)
{
size_t needed_pages = range_page_count(address, count);
vaddr_t vaddr = PageTable::kernel().reserve_free_contiguous_pages(needed_pages, KERNEL_OFFSET);
ASSERT(vaddr);
PageTable::kernel().map_range_at(address & PAGE_ADDR_MASK, vaddr, needed_pages * PAGE_SIZE, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
return (void*)(vaddr + (address % PAGE_SIZE));
}
void laihost_unmap(void* ptr, size_t count)
{
PageTable::kernel().unmap_range((vaddr_t)ptr, count);
}
void laihost_outb(uint16_t port, uint8_t val)
{
IO::outb(port, val);
}
void laihost_outw(uint16_t port, uint16_t val)
{
IO::outw(port, val);
}
void laihost_outd(uint16_t port, uint32_t val)
{
IO::outl(port, val);
}
uint8_t laihost_inb(uint16_t port)
{
return IO::inb(port);
}
uint16_t laihost_inw(uint16_t port)
{
return IO::inw(port);
}
uint32_t laihost_ind(uint16_t port)
{
return IO::inl(port);
}
void laihost_pci_writeb(uint16_t seg, uint8_t bus, uint8_t slot, uint8_t fun, uint16_t offset, uint8_t val)
{
ASSERT(seg == 0);
PCI::PCIManager::write_config_byte(bus, slot, fun, offset, val);
}
void laihost_pci_writew(uint16_t seg, uint8_t bus, uint8_t slot, uint8_t fun, uint16_t offset, uint16_t val)
{
ASSERT(seg == 0);
PCI::PCIManager::write_config_word(bus, slot, fun, offset, val);
}
void laihost_pci_writed(uint16_t seg, uint8_t bus, uint8_t slot, uint8_t fun, uint16_t offset, uint32_t val)
{
ASSERT(seg == 0);
PCI::PCIManager::write_config_dword(bus, slot, fun, offset, val);
}
uint8_t laihost_pci_readb(uint16_t seg, uint8_t bus, uint8_t slot, uint8_t fun, uint16_t offset)
{
ASSERT(seg == 0);
return PCI::PCIManager::read_config_byte(bus, slot, fun, offset);
}
uint16_t laihost_pci_readw(uint16_t seg, uint8_t bus, uint8_t slot, uint8_t fun, uint16_t offset)
{
ASSERT(seg == 0);
return PCI::PCIManager::read_config_word(bus, slot, fun, offset);
}
uint32_t laihost_pci_readd(uint16_t seg, uint8_t bus, uint8_t slot, uint8_t fun, uint16_t offset)
{
ASSERT(seg == 0);
return PCI::PCIManager::read_config_dword(bus, slot, fun, offset);
}
void laihost_sleep(uint64_t ms)
{
SystemTimer::get().sleep(ms);
}
uint64_t laihost_timer(void)
{
auto time = SystemTimer::get().time_since_boot();
return (1'000'000'000ull * time.tv_sec + time.tv_nsec) / 100;
}

1
kernel/lai Submodule

@ -0,0 +1 @@
Subproject commit a228465314ee3a542f62d4bdefeb8fbe2b48da41