Compare commits
17 Commits
b16e65168f
...
8f2f98b7b4
Author | SHA1 | Date |
---|---|---|
|
8f2f98b7b4 | |
|
6b43d12469 | |
|
74940ed33c | |
|
17871bb3ca | |
|
89c4abc07a | |
|
46b5a7697c | |
|
dd8060d64f | |
|
afb1d7ef0c | |
|
93ddee5956 | |
|
0184e5beb5 | |
|
3f2e110eab | |
|
0ff68b7d66 | |
|
cdbdc1a822 | |
|
7a2be05c69 | |
|
5be38d0702 | |
|
ff203d8d34 | |
|
23fa39121c |
|
@ -1,4 +0,0 @@
|
||||||
[submodule "kernel/lai"]
|
|
||||||
path = kernel/lai
|
|
||||||
url = https://github.com/managarm/lai.git
|
|
||||||
ignore = untracked
|
|
|
@ -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] Linear framebuffer (VESA and GOP)
|
||||||
- [x] Network stack
|
- [x] Network stack
|
||||||
- [x] ELF executable loading
|
- [x] ELF executable loading
|
||||||
|
- [x] AML interpreter (partial)
|
||||||
- [ ] ELF dynamic linking
|
- [ ] ELF dynamic linking
|
||||||
- [ ] Graphical desktop
|
- [ ] Graphical desktop
|
||||||
- [ ] AML interpreter (currenly using [lai](https://github.com/managarm/lai))
|
|
||||||
|
|
||||||
#### Drivers
|
#### Drivers
|
||||||
- [x] NVMe disks
|
- [x] NVMe disks
|
||||||
|
|
|
@ -119,13 +119,6 @@ if("${BANAN_ARCH}" STREQUAL "x86_64")
|
||||||
arch/x86_64/Syscall.S
|
arch/x86_64/Syscall.S
|
||||||
arch/x86_64/Thread.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")
|
elseif("${BANAN_ARCH}" STREQUAL "i686")
|
||||||
set(KERNEL_SOURCES
|
set(KERNEL_SOURCES
|
||||||
${KERNEL_SOURCES}
|
${KERNEL_SOURCES}
|
||||||
|
@ -197,7 +190,6 @@ set_source_files_properties(${LAI_SOURCES} PROPERTIES COMPILE_FLAGS -Wno-stack-u
|
||||||
|
|
||||||
add_custom_target(kernel-headers
|
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}/include/ ${BANAN_INCLUDE}/
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/lai/include/ ${BANAN_INCLUDE}/
|
|
||||||
DEPENDS sysroot
|
DEPENDS sysroot
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,21 @@ namespace Kernel::ACPI
|
||||||
static BAN::ErrorOr<void> initialize();
|
static BAN::ErrorOr<void> initialize();
|
||||||
static ACPI& get();
|
static ACPI& get();
|
||||||
|
|
||||||
|
static void acquire_global_lock();
|
||||||
|
static void release_global_lock();
|
||||||
|
|
||||||
const SDTHeader* get_header(BAN::StringView signature, uint32_t index);
|
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:
|
private:
|
||||||
ACPI() = default;
|
ACPI() = default;
|
||||||
BAN::ErrorOr<void> initialize_impl();
|
BAN::ErrorOr<void> initialize_impl();
|
||||||
|
|
|
@ -3,18 +3,9 @@
|
||||||
#include <kernel/ACPI/Headers.h>
|
#include <kernel/ACPI/Headers.h>
|
||||||
#include <kernel/ACPI/AML/Namespace.h>
|
#include <kernel/ACPI/AML/Namespace.h>
|
||||||
|
|
||||||
namespace Kernel::ACPI
|
namespace Kernel::ACPI::AML
|
||||||
{
|
{
|
||||||
|
|
||||||
class AMLParser
|
BAN::RefPtr<AML::Namespace> initialize_namespace();
|
||||||
{
|
|
||||||
public:
|
|
||||||
~AMLParser();
|
|
||||||
|
|
||||||
static BAN::RefPtr<AML::Namespace> parse_table(const SDTHeader& header);
|
|
||||||
|
|
||||||
private:
|
|
||||||
AMLParser();
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <kernel/ACPI/AML/Bytes.h>
|
#include <kernel/ACPI/AML/Bytes.h>
|
||||||
|
#include <kernel/ACPI/AML/Integer.h>
|
||||||
#include <kernel/ACPI/AML/Node.h>
|
#include <kernel/ACPI/AML/Node.h>
|
||||||
#include <kernel/ACPI/AML/ParseContext.h>
|
#include <kernel/ACPI/AML/ParseContext.h>
|
||||||
#include <kernel/ACPI/AML/Pkg.h>
|
#include <kernel/ACPI/AML/Pkg.h>
|
||||||
|
@ -12,7 +13,14 @@ namespace Kernel::ACPI::AML
|
||||||
{
|
{
|
||||||
BAN::Vector<uint8_t> buffer;
|
BAN::Vector<uint8_t> buffer;
|
||||||
|
|
||||||
Buffer() : AML::Node(Node::Type::Buffer) {}
|
Buffer()
|
||||||
|
: AML::Node(Node::Type::Buffer)
|
||||||
|
{}
|
||||||
|
|
||||||
|
BAN::RefPtr<AML::Node> evaluate() override
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
static ParseResult parse(AML::ParseContext& context)
|
static ParseResult parse(AML::ParseContext& context)
|
||||||
{
|
{
|
||||||
|
@ -57,4 +65,147 @@ 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(" }");
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <kernel/ACPI/AML/Method.h>
|
||||||
#include <kernel/ACPI/AML/ParseContext.h>
|
#include <kernel/ACPI/AML/ParseContext.h>
|
||||||
#include <kernel/ACPI/AML/Pkg.h>
|
#include <kernel/ACPI/AML/Pkg.h>
|
||||||
#include <kernel/ACPI/AML/Scope.h>
|
#include <kernel/ACPI/AML/Scope.h>
|
||||||
|
@ -9,7 +10,9 @@ namespace Kernel::ACPI::AML
|
||||||
|
|
||||||
struct Device : public AML::Scope
|
struct Device : public AML::Scope
|
||||||
{
|
{
|
||||||
Device(NameSeg name) : Scope(name, Node::Type::Device) {}
|
Device(NameSeg name)
|
||||||
|
: Scope(Node::Type::Device, name)
|
||||||
|
{}
|
||||||
|
|
||||||
static ParseResult parse(ParseContext& context)
|
static ParseResult parse(ParseContext& context)
|
||||||
{
|
{
|
||||||
|
@ -27,7 +30,7 @@ namespace Kernel::ACPI::AML
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
auto device = MUST(BAN::RefPtr<Device>::create(name_string->path.back()));
|
auto device = MUST(BAN::RefPtr<Device>::create(name_string->path.back()));
|
||||||
if (!context.root_namespace->add_named_object(context.scope.span(), name_string.value(), device))
|
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), device))
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
return device->enter_context_and_parse_term_list(context, name_string.value(), device_pkg.value());
|
return device->enter_context_and_parse_term_list(context, name_string.value(), device_pkg.value());
|
||||||
|
|
|
@ -0,0 +1,214 @@
|
||||||
|
#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()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -35,25 +35,44 @@ namespace Kernel::ACPI::AML
|
||||||
WriteAsZeros = 2,
|
WriteAsZeros = 2,
|
||||||
};
|
};
|
||||||
UpdateRule update_rule;
|
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
|
struct FieldElement : public NamedObject
|
||||||
{
|
{
|
||||||
uint64_t bit_offset;
|
uint64_t bit_offset;
|
||||||
uint32_t bit_count;
|
uint64_t bit_count;
|
||||||
|
|
||||||
FieldRules access_rules;
|
FieldRules access_rules;
|
||||||
|
|
||||||
OpRegion* op_region = nullptr;
|
BAN::RefPtr<OpRegion> op_region;
|
||||||
|
|
||||||
FieldElement(NameSeg name, uint64_t bit_offset, uint32_t bit_count, FieldRules access_rules)
|
FieldElement(NameSeg name, uint64_t bit_offset, uint64_t bit_count, FieldRules access_rules)
|
||||||
: NamedObject(Node::Type::FieldElement, name)
|
: NamedObject(Node::Type::FieldElement, name)
|
||||||
, bit_offset(bit_offset)
|
, bit_offset(bit_offset)
|
||||||
, bit_count(bit_count)
|
, bit_count(bit_count)
|
||||||
, access_rules(access_rules)
|
, access_rules(access_rules)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
BAN::RefPtr<Node> evaluate() override;
|
||||||
|
bool store(BAN::RefPtr<Node> source) override;
|
||||||
|
|
||||||
void debug_print(int indent) const 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
|
struct Field
|
||||||
|
@ -64,20 +83,23 @@ namespace Kernel::ACPI::AML
|
||||||
struct IndexFieldElement : public NamedObject
|
struct IndexFieldElement : public NamedObject
|
||||||
{
|
{
|
||||||
uint64_t bit_offset;
|
uint64_t bit_offset;
|
||||||
uint32_t bit_count;
|
uint64_t bit_count;
|
||||||
|
|
||||||
FieldRules access_rules;
|
FieldRules access_rules;
|
||||||
|
|
||||||
FieldElement* index_element = nullptr;
|
BAN::RefPtr<FieldElement> index_element;
|
||||||
FieldElement* data_element = nullptr;
|
BAN::RefPtr<FieldElement> data_element;
|
||||||
|
|
||||||
IndexFieldElement(NameSeg name, uint64_t bit_offset, uint32_t bit_count, FieldRules access_rules)
|
IndexFieldElement(NameSeg name, uint64_t bit_offset, uint64_t bit_count, FieldRules access_rules)
|
||||||
: NamedObject(Node::Type::IndexFieldElement, name)
|
: NamedObject(Node::Type::IndexFieldElement, name)
|
||||||
, bit_offset(bit_offset)
|
, bit_offset(bit_offset)
|
||||||
, bit_count(bit_count)
|
, bit_count(bit_count)
|
||||||
, access_rules(access_rules)
|
, access_rules(access_rules)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
BAN::RefPtr<Node> evaluate() override;
|
||||||
|
bool store(BAN::RefPtr<Node> source) override;
|
||||||
|
|
||||||
void debug_print(int indent) const override;
|
void debug_print(int indent) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,4 +108,30 @@ namespace Kernel::ACPI::AML
|
||||||
static ParseResult parse(ParseContext& context);
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -13,14 +13,43 @@ namespace Kernel::ACPI::AML
|
||||||
|
|
||||||
struct Integer : public Node
|
struct Integer : public Node
|
||||||
{
|
{
|
||||||
static constexpr uint64_t Ones = -1;
|
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;
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
|
|
||||||
Integer(uint64_t value) : Node(Node::Type::Integer), value(value) {}
|
Integer(uint64_t value, bool constant = false)
|
||||||
|
: Node(Node::Type::Integer)
|
||||||
|
, value(value)
|
||||||
|
, constant(constant)
|
||||||
|
{}
|
||||||
|
|
||||||
BAN::Optional<uint64_t> as_integer() const override
|
BAN::RefPtr<AML::Node> evaluate() override
|
||||||
{
|
{
|
||||||
return value;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ParseResult parse(BAN::ConstByteSpan& aml_data)
|
static ParseResult parse(BAN::ConstByteSpan& aml_data)
|
||||||
|
@ -29,13 +58,13 @@ namespace Kernel::ACPI::AML
|
||||||
{
|
{
|
||||||
case AML::Byte::ZeroOp:
|
case AML::Byte::ZeroOp:
|
||||||
aml_data = aml_data.slice(1);
|
aml_data = aml_data.slice(1);
|
||||||
return ParseResult(MUST(BAN::RefPtr<Integer>::create(0)));
|
return ParseResult(Constants::Zero);
|
||||||
case AML::Byte::OneOp:
|
case AML::Byte::OneOp:
|
||||||
aml_data = aml_data.slice(1);
|
aml_data = aml_data.slice(1);
|
||||||
return ParseResult(MUST(BAN::RefPtr<Integer>::create(1)));
|
return ParseResult(Constants::One);
|
||||||
case AML::Byte::OnesOp:
|
case AML::Byte::OnesOp:
|
||||||
aml_data = aml_data.slice(1);
|
aml_data = aml_data.slice(1);
|
||||||
return ParseResult(MUST(BAN::RefPtr<Integer>::create(Ones)));
|
return ParseResult(Constants::Ones);
|
||||||
case AML::Byte::BytePrefix:
|
case AML::Byte::BytePrefix:
|
||||||
{
|
{
|
||||||
if (aml_data.size() < 2)
|
if (aml_data.size() < 2)
|
||||||
|
@ -82,10 +111,20 @@ namespace Kernel::ACPI::AML
|
||||||
void debug_print(int indent) const override
|
void debug_print(int indent) const override
|
||||||
{
|
{
|
||||||
AML_DEBUG_PRINT_INDENT(indent);
|
AML_DEBUG_PRINT_INDENT(indent);
|
||||||
if (value == Ones)
|
if (!constant)
|
||||||
AML_DEBUG_PRINT("Ones");
|
|
||||||
else
|
|
||||||
AML_DEBUG_PRINT("0x{H}", value);
|
AML_DEBUG_PRINT("0x{H}", value);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AML_DEBUG_PRINT("Const ");
|
||||||
|
if (value == Constants::Zero->value)
|
||||||
|
AML_DEBUG_PRINT("Zero");
|
||||||
|
else if (value == Constants::One->value)
|
||||||
|
AML_DEBUG_PRINT("One");
|
||||||
|
else if (value == Constants::Ones->value)
|
||||||
|
AML_DEBUG_PRINT("Ones");
|
||||||
|
else
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,30 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <kernel/ACPI/AML/Bytes.h>
|
#include <kernel/ACPI/AML/Bytes.h>
|
||||||
#include <kernel/ACPI/AML/NamedObject.h>
|
#include <kernel/ACPI/AML/Namespace.h>
|
||||||
#include <kernel/ACPI/AML/ParseContext.h>
|
#include <kernel/ACPI/AML/ParseContext.h>
|
||||||
#include <kernel/ACPI/AML/Pkg.h>
|
#include <kernel/ACPI/AML/Pkg.h>
|
||||||
|
#include <kernel/ACPI/AML/Scope.h>
|
||||||
|
|
||||||
namespace Kernel::ACPI::AML
|
namespace Kernel::ACPI::AML
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Method : public AML::NamedObject
|
struct Method : public AML::Scope
|
||||||
{
|
{
|
||||||
|
using Arguments = BAN::Array<BAN::RefPtr<AML::Register>, 7>;
|
||||||
|
|
||||||
|
Mutex mutex;
|
||||||
uint8_t arg_count;
|
uint8_t arg_count;
|
||||||
bool serialized;
|
bool serialized;
|
||||||
uint8_t sync_level;
|
uint8_t sync_level;
|
||||||
|
|
||||||
BAN::ConstByteSpan term_list;
|
BAN::ConstByteSpan term_list;
|
||||||
|
|
||||||
Method(AML::NameSeg name, 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::NamedObject(Node::Type::Method, name)
|
: AML::Scope(Node::Type::Method, name)
|
||||||
, arg_count(arg_count)
|
, arg_count(arg_count)
|
||||||
, serialized(serialized)
|
, serialized(serialized)
|
||||||
, sync_level(sync_level)
|
, sync_level(sync_level)
|
||||||
, term_list(term_list)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
static ParseResult parse(AML::ParseContext& context)
|
static ParseResult parse(AML::ParseContext& context)
|
||||||
|
@ -47,13 +50,17 @@ namespace Kernel::ACPI::AML
|
||||||
name_string.value().path.back(),
|
name_string.value().path.back(),
|
||||||
method_flags & 0x07,
|
method_flags & 0x07,
|
||||||
(method_flags >> 3) & 0x01,
|
(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))
|
||||||
if (!context.root_namespace->add_named_object(context.scope.span(), name_string.value(), method))
|
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
|
auto method_scope = Namespace::root_namespace()->resolve_path(context.scope, name_string.value());
|
||||||
|
if (!method_scope.has_value())
|
||||||
|
return ParseResult::Failure;
|
||||||
|
method->term_list = method_pkg.value();
|
||||||
|
method->scope = method_scope.release_value();
|
||||||
|
|
||||||
#if AML_DEBUG_LEVEL >= 2
|
#if AML_DEBUG_LEVEL >= 2
|
||||||
method->debug_print(0);
|
method->debug_print(0);
|
||||||
AML_DEBUG_PRINTLN("");
|
AML_DEBUG_PRINTLN("");
|
||||||
|
@ -62,6 +69,67 @@ namespace Kernel::ACPI::AML
|
||||||
return ParseResult::Success;
|
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
|
virtual void debug_print(int indent) const override
|
||||||
{
|
{
|
||||||
AML_DEBUG_PRINT_INDENT(indent);
|
AML_DEBUG_PRINT_INDENT(indent);
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <kernel/ACPI/AML/Bytes.h>
|
#include <kernel/ACPI/AML/Bytes.h>
|
||||||
|
#include <kernel/ACPI/AML/Integer.h>
|
||||||
#include <kernel/ACPI/AML/NamedObject.h>
|
#include <kernel/ACPI/AML/NamedObject.h>
|
||||||
#include <kernel/ACPI/AML/ParseContext.h>
|
#include <kernel/ACPI/AML/ParseContext.h>
|
||||||
|
#include <kernel/Lock/Mutex.h>
|
||||||
|
#include <kernel/Timer/Timer.h>
|
||||||
|
|
||||||
namespace Kernel::ACPI::AML
|
namespace Kernel::ACPI::AML
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Mutex : public AML::NamedObject
|
struct Mutex : public AML::NamedObject
|
||||||
{
|
{
|
||||||
|
Kernel::Mutex mutex;
|
||||||
uint8_t sync_level;
|
uint8_t sync_level;
|
||||||
|
|
||||||
Mutex(NameSeg name, uint8_t sync_level)
|
Mutex(NameSeg name, uint8_t sync_level)
|
||||||
|
@ -17,6 +21,33 @@ namespace Kernel::ACPI::AML
|
||||||
{}
|
{}
|
||||||
|
|
||||||
static ParseResult parse(ParseContext& context)
|
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(context.aml_data.size() >= 2);
|
||||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||||
|
@ -39,7 +70,7 @@ namespace Kernel::ACPI::AML
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mutex = MUST(BAN::RefPtr<Mutex>::create(name->path.back(), sync_level));
|
auto mutex = MUST(BAN::RefPtr<Mutex>::create(name->path.back(), sync_level));
|
||||||
if (!context.root_namespace->add_named_object(context.scope.span(), name.value(), mutex))
|
if (!Namespace::root_namespace()->add_named_object(context, name.value(), mutex))
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
#if AML_DEBUG_LEVEL >= 2
|
#if AML_DEBUG_LEVEL >= 2
|
||||||
|
@ -50,13 +81,86 @@ namespace Kernel::ACPI::AML
|
||||||
return ParseResult::Success;
|
return ParseResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void debug_print(int indent) const override
|
static ParseResult parse_acquire(ParseContext& context)
|
||||||
{
|
{
|
||||||
AML_DEBUG_PRINT_INDENT(indent);
|
ASSERT(context.aml_data.size() >= 2);
|
||||||
AML_DEBUG_PRINT("Mutex ");
|
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
|
||||||
name.debug_print();
|
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::AcquireOp);
|
||||||
AML_DEBUG_PRINT(" (SyncLevel: {})", sync_level);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace Kernel::ACPI::AML
|
||||||
|
|
||||||
struct NamedObject : public Node
|
struct NamedObject : public Node
|
||||||
{
|
{
|
||||||
|
BAN::RefPtr<NamedObject> parent;
|
||||||
NameSeg name;
|
NameSeg name;
|
||||||
|
|
||||||
NamedObject(Node::Type type, NameSeg name) : Node(type), name(name) {}
|
NamedObject(Node::Type type, NameSeg name) : Node(type), name(name) {}
|
||||||
|
@ -21,6 +22,19 @@ namespace Kernel::ACPI::AML
|
||||||
: NamedObject(Node::Type::Name, name), object(BAN::move(object))
|
: 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);
|
static ParseResult parse(ParseContext& context);
|
||||||
virtual void debug_print(int indent) const override;
|
virtual void debug_print(int indent) const override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -70,6 +70,59 @@ namespace Kernel::ACPI::AML
|
||||||
BAN::String prefix;
|
BAN::String prefix;
|
||||||
BAN::Vector<NameSeg> path;
|
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)
|
static BAN::Optional<NameString> parse(BAN::ConstByteSpan& aml_data)
|
||||||
{
|
{
|
||||||
if (aml_data.size() == 0)
|
if (aml_data.size() == 0)
|
||||||
|
@ -154,4 +207,27 @@ 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], {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,31 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <kernel/ACPI/AML/Scope.h>
|
#include <kernel/ACPI/AML/Scope.h>
|
||||||
|
#include <kernel/ACPI/Headers.h>
|
||||||
|
#include <kernel/Lock/Mutex.h>
|
||||||
|
|
||||||
namespace Kernel::ACPI::AML
|
namespace Kernel::ACPI::AML
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Namespace : public AML::Scope
|
struct Namespace : public AML::Scope
|
||||||
{
|
{
|
||||||
Namespace() : AML::Scope(NameSeg("\\"sv)) {}
|
static BAN::RefPtr<AML::Namespace> root_namespace();
|
||||||
|
|
||||||
static BAN::RefPtr<Namespace> parse(BAN::ConstByteSpan aml);
|
Namespace(NameSeg name) : AML::Scope(Node::Type::Namespace, name) {}
|
||||||
|
|
||||||
BAN::Optional<BAN::Vector<AML::NameSeg>> resolve_path(BAN::Span<const AML::NameSeg> parsing_scope, const AML::NameString& relative_path);
|
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);
|
||||||
|
|
||||||
// Find an object in the namespace. Returns nullptr if the object is not found.
|
// Find an object in the namespace. Returns nullptr if the object is not found.
|
||||||
BAN::RefPtr<NamedObject> find_object(BAN::Span<const AML::NameSeg> parsing_scope, const AML::NameString& relative_path);
|
BAN::RefPtr<NamedObject> find_object(const AML::NameString& relative_base, const AML::NameString& relative_path);
|
||||||
|
|
||||||
// Add an object to the namespace. Returns false if the parent object could not be added.
|
// Add an object to the namespace. Returns false if the parent object could not be added.
|
||||||
bool add_named_object(BAN::Span<const AML::NameSeg> parsing_scope, const AML::NameString& object_path, BAN::RefPtr<NamedObject> object);
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,20 @@
|
||||||
#include <BAN/Optional.h>
|
#include <BAN/Optional.h>
|
||||||
#include <BAN/RefPtr.h>
|
#include <BAN/RefPtr.h>
|
||||||
#include <BAN/Vector.h>
|
#include <BAN/Vector.h>
|
||||||
|
#include <kernel/ACPI/AML/Utils.h>
|
||||||
|
|
||||||
namespace Kernel::ACPI::AML
|
namespace Kernel::ACPI::AML
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Node : public BAN::RefCounted<Node>
|
struct Node : public BAN::RefCounted<Node>
|
||||||
{
|
{
|
||||||
|
static uint64_t total_node_count;
|
||||||
|
|
||||||
enum class Type
|
enum class Type
|
||||||
{
|
{
|
||||||
|
BankFieldElement,
|
||||||
Buffer,
|
Buffer,
|
||||||
|
BufferField,
|
||||||
Device,
|
Device,
|
||||||
FieldElement,
|
FieldElement,
|
||||||
IndexFieldElement,
|
IndexFieldElement,
|
||||||
|
@ -20,19 +25,26 @@ namespace Kernel::ACPI::AML
|
||||||
Method,
|
Method,
|
||||||
Mutex,
|
Mutex,
|
||||||
Name,
|
Name,
|
||||||
|
Namespace,
|
||||||
OpRegion,
|
OpRegion,
|
||||||
Package,
|
Package,
|
||||||
|
PowerResource,
|
||||||
Processor,
|
Processor,
|
||||||
Scope,
|
Reference,
|
||||||
|
Register,
|
||||||
String,
|
String,
|
||||||
|
ThermalZone,
|
||||||
};
|
};
|
||||||
const Type type;
|
const Type type;
|
||||||
|
|
||||||
Node(Type type) : type(type) {}
|
Node(Type type) : type(type) { total_node_count++; }
|
||||||
virtual ~Node() = default;
|
virtual ~Node() { total_node_count--; }
|
||||||
|
|
||||||
virtual bool is_scope() const { return false; }
|
virtual bool is_scope() const { return false; }
|
||||||
virtual BAN::Optional<uint64_t> as_integer() const { return {}; }
|
|
||||||
|
[[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 void debug_print(int indent) const = 0;
|
virtual void debug_print(int indent) const = 0;
|
||||||
};
|
};
|
||||||
|
@ -47,12 +59,25 @@ namespace Kernel::ACPI::AML
|
||||||
{
|
{
|
||||||
Success,
|
Success,
|
||||||
Failure,
|
Failure,
|
||||||
|
Returned,
|
||||||
};
|
};
|
||||||
|
|
||||||
ParseResult(Result success) : m_result(success) {}
|
ParseResult(Result success)
|
||||||
ParseResult(BAN::RefPtr<Node> node) : m_result(Result::Success), m_node(BAN::move(node)) { ASSERT(m_node); }
|
: 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);
|
||||||
|
}
|
||||||
|
|
||||||
bool success() const { return m_result == Result::Success; }
|
bool success() const { return m_result == Result::Success; }
|
||||||
|
bool returned() const { return m_result == Result::Returned; }
|
||||||
|
|
||||||
BAN::RefPtr<Node> node()
|
BAN::RefPtr<Node> node()
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,9 +10,41 @@ namespace Kernel::ACPI::AML
|
||||||
|
|
||||||
struct Package : public AML::Node
|
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;
|
BAN::Vector<BAN::RefPtr<AML::Node>> elements;
|
||||||
|
|
||||||
Package() : Node(Node::Type::Package) {}
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
static ParseResult parse(AML::ParseContext& context)
|
static ParseResult parse(AML::ParseContext& context)
|
||||||
{
|
{
|
||||||
|
@ -33,18 +65,33 @@ namespace Kernel::ACPI::AML
|
||||||
package_context.aml_data = package_context.aml_data.slice(1);
|
package_context.aml_data = package_context.aml_data.slice(1);
|
||||||
|
|
||||||
BAN::Vector<BAN::RefPtr<AML::Node>> elements;
|
BAN::Vector<BAN::RefPtr<AML::Node>> elements;
|
||||||
|
BAN::Vector<UnresolvedReference> unresolved_references;
|
||||||
while (elements.size() < num_elements && package_context.aml_data.size() > 0)
|
while (elements.size() < num_elements && package_context.aml_data.size() > 0)
|
||||||
{
|
{
|
||||||
auto element_result = AML::parse_object(package_context);
|
BAN::RefPtr<AML::Node> element;
|
||||||
if (!element_result.success())
|
|
||||||
return ParseResult::Failure;
|
// Store name strings as references
|
||||||
MUST(elements.push_back(element_result.node()));
|
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));
|
||||||
}
|
}
|
||||||
while (elements.size() < num_elements)
|
while (elements.size() < num_elements)
|
||||||
MUST(elements.push_back(BAN::RefPtr<AML::Node>()));
|
MUST(elements.push_back(BAN::RefPtr<AML::Node>()));
|
||||||
|
|
||||||
auto package = MUST(BAN::RefPtr<Package>::create());
|
auto package = MUST(BAN::RefPtr<Package>::create(BAN::move(elements), BAN::move(unresolved_references), context.scope));
|
||||||
package->elements = BAN::move(elements);
|
|
||||||
return ParseResult(package);
|
return ParseResult(package);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +102,11 @@ namespace Kernel::ACPI::AML
|
||||||
AML_DEBUG_PRINTLN("");
|
AML_DEBUG_PRINTLN("");
|
||||||
for (const auto& element : elements)
|
for (const auto& element : elements)
|
||||||
{
|
{
|
||||||
element->debug_print(indent + 1);
|
AML_DEBUG_PRINT_INDENT(indent + 1);
|
||||||
|
if (element)
|
||||||
|
element->debug_print(0);
|
||||||
|
else
|
||||||
|
AML_DEBUG_PRINT("Uninitialized");
|
||||||
AML_DEBUG_PRINTLN("");
|
AML_DEBUG_PRINTLN("");
|
||||||
}
|
}
|
||||||
AML_DEBUG_PRINT_INDENT(indent);
|
AML_DEBUG_PRINT_INDENT(indent);
|
||||||
|
|
|
@ -1,18 +1,30 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Array.h>
|
||||||
#include <BAN/ByteSpan.h>
|
#include <BAN/ByteSpan.h>
|
||||||
#include <BAN/Vector.h>
|
#include <BAN/LinkedList.h>
|
||||||
#include <kernel/ACPI/AML/NamedObject.h>
|
#include <kernel/ACPI/AML/NamedObject.h>
|
||||||
#include <kernel/ACPI/AML/Namespace.h>
|
#include <kernel/ACPI/AML/Namespace.h>
|
||||||
|
#include <kernel/ACPI/AML/Register.h>
|
||||||
|
|
||||||
namespace Kernel::ACPI::AML
|
namespace Kernel::ACPI::AML
|
||||||
{
|
{
|
||||||
|
|
||||||
struct ParseContext
|
struct ParseContext
|
||||||
{
|
{
|
||||||
BAN::ConstByteSpan aml_data;
|
BAN::ConstByteSpan aml_data;
|
||||||
BAN::Vector<AML::NameSeg> scope;
|
AML::NameString scope;
|
||||||
struct Namespace* root_namespace;
|
|
||||||
|
// 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ namespace Kernel::ACPI::AML
|
||||||
uint8_t pblk_len;
|
uint8_t pblk_len;
|
||||||
|
|
||||||
Processor(NameSeg name, uint8_t id, uint32_t pblk_addr, uint8_t pblk_len)
|
Processor(NameSeg name, uint8_t id, uint32_t pblk_addr, uint8_t pblk_len)
|
||||||
: Scope(name, Node::Type::Processor)
|
: Scope(Node::Type::Processor, name)
|
||||||
, id(id)
|
, id(id)
|
||||||
, pblk_addr(pblk_addr)
|
, pblk_addr(pblk_addr)
|
||||||
, pblk_len(pblk_len)
|
, pblk_len(pblk_len)
|
||||||
|
@ -29,42 +29,46 @@ namespace Kernel::ACPI::AML
|
||||||
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::ProcessorOp);
|
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::ProcessorOp);
|
||||||
context.aml_data = context.aml_data.slice(2);
|
context.aml_data = context.aml_data.slice(2);
|
||||||
|
|
||||||
auto processor_pkg = AML::parse_pkg(context.aml_data);
|
auto opt_processor_pkg = AML::parse_pkg(context.aml_data);
|
||||||
if (!processor_pkg.has_value())
|
if (!opt_processor_pkg.has_value())
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
auto processor_pkg = opt_processor_pkg.value();
|
||||||
|
|
||||||
auto name = NameString::parse(processor_pkg.value());
|
auto name = NameString::parse(processor_pkg);
|
||||||
if (!name.has_value())
|
if (!name.has_value())
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
if (processor_pkg->size() < 1)
|
if (processor_pkg.size() < 1)
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
uint8_t id = processor_pkg.value()[0];
|
uint8_t id = processor_pkg[0];
|
||||||
processor_pkg = processor_pkg->slice(1);
|
processor_pkg = processor_pkg.slice(1);
|
||||||
|
|
||||||
if (processor_pkg->size() < 4)
|
if (processor_pkg.size() < 4)
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
uint32_t pblk_addr = BAN::little_endian_to_host<uint32_t>(*reinterpret_cast<const uint32_t*>(processor_pkg->data()));
|
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);
|
processor_pkg = processor_pkg.slice(4);
|
||||||
|
|
||||||
if (processor_pkg->size() < 1)
|
if (processor_pkg.size() < 1)
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
uint8_t pblk_len = processor_pkg.value()[0];
|
uint8_t pblk_len = processor_pkg[0];
|
||||||
processor_pkg = processor_pkg->slice(1);
|
processor_pkg = processor_pkg.slice(1);
|
||||||
|
|
||||||
auto processor = MUST(BAN::RefPtr<Processor>::create(name->path.back(), id, pblk_addr, pblk_len));
|
auto processor = MUST(BAN::RefPtr<Processor>::create(name->path.back(), id, pblk_addr, pblk_len));
|
||||||
if (!context.root_namespace->add_named_object(context.scope.span(), name.value(), processor))
|
if (!Namespace::root_namespace()->add_named_object(context, name.value(), processor))
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
return processor->enter_context_and_parse_term_list(context, name.value(), processor_pkg.value());
|
#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);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void debug_print(int indent) const override
|
virtual void debug_print(int indent) const override
|
||||||
{
|
{
|
||||||
AML_DEBUG_PRINT_INDENT(indent);
|
AML_DEBUG_PRINT_INDENT(indent);
|
||||||
AML_DEBUG_PRINT("Processor ");
|
AML_DEBUG_PRINT("Processor {} (ID: {}, PBlkAddr: 0x{H}, PBlkLen: {})", name, id, pblk_addr, pblk_len);
|
||||||
name.debug_print();
|
|
||||||
AML_DEBUG_PRINT(" (ID: {}, PBlkAddr: 0x{H}, PBlkLen: {})", id, pblk_addr, pblk_len);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
#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("}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -23,12 +23,15 @@ namespace Kernel::ACPI::AML
|
||||||
GenericSerialBus = 9,
|
GenericSerialBus = 9,
|
||||||
PCC = 10,
|
PCC = 10,
|
||||||
};
|
};
|
||||||
RegionSpace space;
|
RegionSpace region_space;
|
||||||
uint64_t offset;
|
uint64_t region_offset;
|
||||||
uint64_t length;
|
uint64_t region_length;
|
||||||
|
|
||||||
OpRegion(NameSeg name, 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), space(space), offset(offset), length(length)
|
: NamedObject(Node::Type::OpRegion, name)
|
||||||
|
, region_space(region_space)
|
||||||
|
, region_offset(region_offset)
|
||||||
|
, region_length(region_length)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
static ParseResult parse(AML::ParseContext& context)
|
static ParseResult parse(AML::ParseContext& context)
|
||||||
|
@ -74,7 +77,7 @@ namespace Kernel::ACPI::AML
|
||||||
length.value()
|
length.value()
|
||||||
));
|
));
|
||||||
|
|
||||||
if (!context.root_namespace->add_named_object(context.scope.span(), name.value(), op_region))
|
if (!Namespace::root_namespace()->add_named_object(context, name.value(), op_region))
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
#if AML_DEBUG_LEVEL >= 2
|
#if AML_DEBUG_LEVEL >= 2
|
||||||
|
@ -88,7 +91,7 @@ namespace Kernel::ACPI::AML
|
||||||
virtual void debug_print(int indent) const override
|
virtual void debug_print(int indent) const override
|
||||||
{
|
{
|
||||||
BAN::StringView region_space_name;
|
BAN::StringView region_space_name;
|
||||||
switch (space)
|
switch (region_space)
|
||||||
{
|
{
|
||||||
case RegionSpace::SystemMemory: region_space_name = "SystemMemory"sv; break;
|
case RegionSpace::SystemMemory: region_space_name = "SystemMemory"sv; break;
|
||||||
case RegionSpace::SystemIO: region_space_name = "SystemIO"sv; break;
|
case RegionSpace::SystemIO: region_space_name = "SystemIO"sv; break;
|
||||||
|
@ -106,7 +109,7 @@ namespace Kernel::ACPI::AML
|
||||||
AML_DEBUG_PRINT_INDENT(indent);
|
AML_DEBUG_PRINT_INDENT(indent);
|
||||||
AML_DEBUG_PRINT("OperationRegion(");
|
AML_DEBUG_PRINT("OperationRegion(");
|
||||||
name.debug_print();
|
name.debug_print();
|
||||||
AML_DEBUG_PRINT(", {}, 0x{H}, 0x{H})", region_space_name, offset, length);
|
AML_DEBUG_PRINT(", {}, 0x{H}, 0x{H})", region_space_name, region_offset, region_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
#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(" }");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -10,8 +10,11 @@ namespace Kernel::ACPI::AML
|
||||||
struct Scope : public AML::NamedObject
|
struct Scope : public AML::NamedObject
|
||||||
{
|
{
|
||||||
BAN::HashMap<NameSeg, BAN::RefPtr<NamedObject>> objects;
|
BAN::HashMap<NameSeg, BAN::RefPtr<NamedObject>> objects;
|
||||||
|
AML::NameString scope;
|
||||||
|
|
||||||
Scope(NameSeg name, Node::Type type = Node::Type::Scope) : NamedObject(type, name) {}
|
Scope(Node::Type type, NameSeg name)
|
||||||
|
: NamedObject(type, name)
|
||||||
|
{}
|
||||||
|
|
||||||
virtual bool is_scope() const override { return true; }
|
virtual bool is_scope() const override { return true; }
|
||||||
|
|
||||||
|
@ -22,4 +25,6 @@ namespace Kernel::ACPI::AML
|
||||||
ParseResult enter_context_and_parse_term_list(ParseContext& outer_context, const AML::NameString& name, BAN::ConstByteSpan aml_data);
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -71,7 +71,7 @@ namespace Kernel::ACPI
|
||||||
uint8_t reset_value;
|
uint8_t reset_value;
|
||||||
uint16_t arm_boot_arch;
|
uint16_t arm_boot_arch;
|
||||||
uint8_t fadt_minor_version;
|
uint8_t fadt_minor_version;
|
||||||
uint64_t x_firmware_version;
|
uint64_t x_firmware_ctrl;
|
||||||
uint64_t x_dsdt;
|
uint64_t x_dsdt;
|
||||||
uint8_t x_pm1a_evt_blk[12];
|
uint8_t x_pm1a_evt_blk[12];
|
||||||
uint8_t x_pm1b_evt_blk[12];
|
uint8_t x_pm1b_evt_blk[12];
|
||||||
|
@ -100,6 +100,22 @@ namespace Kernel::ACPI
|
||||||
uint8_t page_protection_and_oem_attribute;
|
uint8_t page_protection_and_oem_attribute;
|
||||||
} __attribute__((packed));
|
} __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
|
namespace BAN::Formatter
|
||||||
|
|
|
@ -30,8 +30,6 @@ namespace Kernel
|
||||||
|
|
||||||
bool is_using_apic() const { return m_using_apic; }
|
bool is_using_apic() const { return m_using_apic; }
|
||||||
|
|
||||||
void enter_acpi_mode();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_using_apic { false };
|
bool m_using_apic { false };
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,10 +2,14 @@
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/StringView.h>
|
||||||
#include <kernel/ACPI/ACPI.h>
|
#include <kernel/ACPI/ACPI.h>
|
||||||
#include <kernel/ACPI/AML.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/BootInfo.h>
|
||||||
|
#include <kernel/IO.h>
|
||||||
#include <kernel/Memory/PageTable.h>
|
#include <kernel/Memory/PageTable.h>
|
||||||
|
#include <kernel/Timer/Timer.h>
|
||||||
#include <lai/core.h>
|
|
||||||
|
|
||||||
#define RSPD_SIZE 20
|
#define RSPD_SIZE 20
|
||||||
#define RSPDv2_SIZE 36
|
#define RSPDv2_SIZE 36
|
||||||
|
@ -13,6 +17,85 @@
|
||||||
namespace Kernel::ACPI
|
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
|
struct RSDT : public SDTHeader
|
||||||
{
|
{
|
||||||
uint32_t entries[];
|
uint32_t entries[];
|
||||||
|
@ -33,13 +116,24 @@ namespace Kernel::ACPI
|
||||||
return BAN::Error::from_errno(ENOMEM);
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
TRY(s_instance->initialize_impl());
|
TRY(s_instance->initialize_impl());
|
||||||
|
|
||||||
auto dsdt = s_instance->get_header("DSDT", 0);
|
{
|
||||||
ASSERT(dsdt);
|
ASSERT(!s_global_lock);
|
||||||
s_instance->m_namespace = AMLParser::parse_table(*dsdt);
|
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();
|
||||||
|
|
||||||
#if ARCH(x86_64)
|
|
||||||
lai_create_namespace();
|
|
||||||
#endif
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,9 +195,6 @@ namespace Kernel::ACPI
|
||||||
const RSDP* rsdp = locate_rsdp();
|
const RSDP* rsdp = locate_rsdp();
|
||||||
if (rsdp == nullptr)
|
if (rsdp == nullptr)
|
||||||
return BAN::Error::from_error_code(ErrorCode::ACPI_NoRootSDT);
|
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;
|
uint32_t root_entry_count = 0;
|
||||||
|
|
||||||
|
@ -249,4 +340,156 @@ namespace Kernel::ACPI
|
||||||
return nullptr;
|
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 {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,29 +6,78 @@
|
||||||
namespace Kernel::ACPI
|
namespace Kernel::ACPI
|
||||||
{
|
{
|
||||||
|
|
||||||
AMLParser::AMLParser() = default;
|
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#secondary-system-description-table-ssdt
|
||||||
AMLParser::~AMLParser() = default;
|
// 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)
|
||||||
BAN::RefPtr<AML::Namespace> AMLParser::parse_table(const SDTHeader& header)
|
|
||||||
{
|
{
|
||||||
dprintln("Parsing {}, {} bytes of AML", header, header.length);
|
// 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;
|
||||||
|
|
||||||
auto aml_raw = BAN::ConstByteSpan { reinterpret_cast<const uint8_t*>(&header), header.length };
|
for (uint32_t i = 0;; i++)
|
||||||
aml_raw = aml_raw.slice(sizeof(header));
|
|
||||||
|
|
||||||
auto ns = AML::Namespace::parse(aml_raw);
|
|
||||||
if (!ns)
|
|
||||||
{
|
{
|
||||||
dwarnln("Failed to parse ACPI namespace");
|
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");
|
||||||
return {};
|
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
|
#if AML_DEBUG_LEVEL >= 1
|
||||||
ns->debug_print(0);
|
ns->debug_print(0);
|
||||||
AML_DEBUG_PRINTLN("");
|
AML_DEBUG_PRINTLN("");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dprintln("Parsed ACPI namespace");
|
dprintln("Parsed ACPI namespace, total of {} nodes created", AML::Node::total_node_count);
|
||||||
|
|
||||||
return ns;
|
return ns;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,25 @@
|
||||||
|
#include <BAN/ScopeGuard.h>
|
||||||
|
#include <kernel/ACPI/ACPI.h>
|
||||||
#include <kernel/ACPI/AML/Field.h>
|
#include <kernel/ACPI/AML/Field.h>
|
||||||
|
#include <kernel/ACPI/AML/Integer.h>
|
||||||
|
#include <kernel/IO.h>
|
||||||
|
#include <kernel/PCI.h>
|
||||||
|
|
||||||
namespace Kernel::ACPI
|
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>
|
template<typename Element>
|
||||||
struct ParseFieldElementContext
|
struct ParseFieldElementContext
|
||||||
{
|
{
|
||||||
|
@ -15,6 +32,8 @@ namespace Kernel::ACPI
|
||||||
template<typename Element>
|
template<typename Element>
|
||||||
static bool parse_field_element(ParseFieldElementContext<Element>& context)
|
static bool parse_field_element(ParseFieldElementContext<Element>& context)
|
||||||
{
|
{
|
||||||
|
// FIXME: Validate elements
|
||||||
|
|
||||||
ASSERT(context.field_pkg.size() >= 1);
|
ASSERT(context.field_pkg.size() >= 1);
|
||||||
switch (context.field_pkg[0])
|
switch (context.field_pkg[0])
|
||||||
{
|
{
|
||||||
|
@ -34,10 +53,60 @@ namespace Kernel::ACPI
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 0x01:
|
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:
|
case 0x02:
|
||||||
case 0x03:
|
AML_TODO("Field element Connection", context.field_pkg[0]);
|
||||||
AML_TODO("Field element {2H}", context.field_pkg[0]);
|
|
||||||
return false;
|
return false;
|
||||||
|
case 0x03:
|
||||||
|
{
|
||||||
|
context.field_pkg = context.field_pkg.slice(1);
|
||||||
|
|
||||||
|
if (context.field_pkg.size() < 3)
|
||||||
|
{
|
||||||
|
AML_ERROR("Invalid FieldElement length for extended access field");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.field_rules.access_type = static_cast<AML::FieldRules::AccessType>(context.field_pkg[0] & 0x0F);
|
||||||
|
context.field_rules.lock_rule = static_cast<AML::FieldRules::LockRule>((context.field_pkg[0] >> 4) & 0x01);
|
||||||
|
context.field_rules.update_rule = static_cast<AML::FieldRules::UpdateRule>((context.field_pkg[0] >> 5) & 0x03);
|
||||||
|
context.field_pkg = context.field_pkg.slice(1);
|
||||||
|
|
||||||
|
if (context.field_pkg[0] == 0x0B)
|
||||||
|
context.field_rules.access_attrib = AML::FieldRules::AccessAttrib::Bytes;
|
||||||
|
else if (context.field_pkg[0] == 0x0E)
|
||||||
|
context.field_rules.access_attrib = AML::FieldRules::AccessAttrib::RawBytes;
|
||||||
|
else if (context.field_pkg[0] == 0x0F)
|
||||||
|
context.field_rules.access_attrib = AML::FieldRules::AccessAttrib::RawProcessBytes;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AML_ERROR("Invalid FieldElement extended access field attribute");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
context.field_pkg = context.field_pkg.slice(1);
|
||||||
|
|
||||||
|
context.field_rules.access_length = context.field_pkg[0];
|
||||||
|
context.field_pkg = context.field_pkg.slice(1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
auto element_name = AML::NameSeg::parse(context.field_pkg);
|
auto element_name = AML::NameSeg::parse(context.field_pkg);
|
||||||
|
@ -77,6 +146,248 @@ 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)
|
AML::ParseResult AML::Field::parse(ParseContext& context)
|
||||||
{
|
{
|
||||||
ASSERT(context.aml_data.size() >= 2);
|
ASSERT(context.aml_data.size() >= 2);
|
||||||
|
@ -93,10 +404,10 @@ namespace Kernel::ACPI
|
||||||
if (!name_string.has_value())
|
if (!name_string.has_value())
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
auto op_region = context.root_namespace->find_object(context.scope.span(), name_string.value());
|
auto op_region = Namespace::root_namespace()->find_object(context.scope, name_string.value());
|
||||||
if (!op_region || op_region->type != AML::Node::Type::OpRegion)
|
if (!op_region || op_region->type != AML::Node::Type::OpRegion)
|
||||||
{
|
{
|
||||||
AML_ERROR("Field RegionName does not name a valid OpRegion");
|
AML_ERROR("FieldOp: {} does not name a valid OpRegion", name_string.value());
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +432,7 @@ namespace Kernel::ACPI
|
||||||
|
|
||||||
NameString element_name;
|
NameString element_name;
|
||||||
MUST(element_name.path.push_back(element->name));
|
MUST(element_name.path.push_back(element->name));
|
||||||
if (!context.root_namespace->add_named_object(context.scope.span(), element_name, element))
|
if (!Namespace::root_namespace()->add_named_object(context, element_name, element))
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
#if AML_DEBUG_LEVEL >= 2
|
#if AML_DEBUG_LEVEL >= 2
|
||||||
|
@ -133,14 +444,77 @@ namespace Kernel::ACPI
|
||||||
return ParseResult::Success;
|
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
|
void AML::FieldElement::debug_print(int indent) const
|
||||||
{
|
{
|
||||||
AML_DEBUG_PRINT_INDENT(indent);
|
AML_DEBUG_PRINT_INDENT(indent);
|
||||||
AML_DEBUG_PRINT("FieldElement ");
|
AML_DEBUG_PRINT("FieldElement {} ({}, offset {}, OpRegion {})",
|
||||||
name.debug_print();
|
name,
|
||||||
AML_DEBUG_PRINT("({}, offset {}, OpRegion ", bit_count, bit_offset);
|
bit_count,
|
||||||
op_region->name.debug_print();
|
bit_offset,
|
||||||
AML_DEBUG_PRINT(")");
|
op_region->name
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
AML::ParseResult AML::IndexField::parse(ParseContext& context)
|
AML::ParseResult AML::IndexField::parse(ParseContext& context)
|
||||||
|
@ -158,7 +532,7 @@ namespace Kernel::ACPI
|
||||||
auto index_field_element_name = NameString::parse(field_pkg);
|
auto index_field_element_name = NameString::parse(field_pkg);
|
||||||
if (!index_field_element_name.has_value())
|
if (!index_field_element_name.has_value())
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
auto index_field_element = context.root_namespace->find_object(context.scope.span(), index_field_element_name.value());
|
auto index_field_element = Namespace::root_namespace()->find_object(context.scope, index_field_element_name.value());
|
||||||
if (!index_field_element || index_field_element->type != AML::Node::Type::FieldElement)
|
if (!index_field_element || index_field_element->type != AML::Node::Type::FieldElement)
|
||||||
{
|
{
|
||||||
AML_ERROR("IndexField IndexName does not name a valid FieldElement");
|
AML_ERROR("IndexField IndexName does not name a valid FieldElement");
|
||||||
|
@ -168,7 +542,7 @@ namespace Kernel::ACPI
|
||||||
auto data_field_element_name = NameString::parse(field_pkg);
|
auto data_field_element_name = NameString::parse(field_pkg);
|
||||||
if (!data_field_element_name.has_value())
|
if (!data_field_element_name.has_value())
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
auto data_field_element = context.root_namespace->find_object(context.scope.span(), data_field_element_name.value());
|
auto data_field_element = Namespace::root_namespace()->find_object(context.scope, data_field_element_name.value());
|
||||||
if (!data_field_element || data_field_element->type != AML::Node::Type::FieldElement)
|
if (!data_field_element || data_field_element->type != AML::Node::Type::FieldElement)
|
||||||
{
|
{
|
||||||
AML_ERROR("IndexField DataName does not name a valid FieldElement");
|
AML_ERROR("IndexField DataName does not name a valid FieldElement");
|
||||||
|
@ -197,7 +571,7 @@ namespace Kernel::ACPI
|
||||||
|
|
||||||
NameString element_name;
|
NameString element_name;
|
||||||
MUST(element_name.path.push_back(element->name));
|
MUST(element_name.path.push_back(element->name));
|
||||||
if (!context.root_namespace->add_named_object(context.scope.span(), element_name, element))
|
if (!Namespace::root_namespace()->add_named_object(context, element_name, element))
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
#if AML_DEBUG_LEVEL >= 2
|
#if AML_DEBUG_LEVEL >= 2
|
||||||
|
@ -209,16 +583,194 @@ namespace Kernel::ACPI
|
||||||
return AML::ParseResult::Success;
|
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
|
void AML::IndexFieldElement::debug_print(int indent) const
|
||||||
{
|
{
|
||||||
AML_DEBUG_PRINT_INDENT(indent);
|
AML_DEBUG_PRINT_INDENT(indent);
|
||||||
AML_DEBUG_PRINT("IndexFieldElement ");
|
AML_DEBUG_PRINT("IndexFieldElement {} ({}, offset {}, IndexName {}, DataName {})",
|
||||||
name.debug_print();
|
name,
|
||||||
AML_DEBUG_PRINT("({}, offset {}, IndexName ", bit_count, bit_offset);
|
bit_count,
|
||||||
index_element->name.debug_print();
|
bit_offset,
|
||||||
AML_DEBUG_PRINT(", DataName ");
|
index_element->name,
|
||||||
data_element->name.debug_print();
|
data_element->name
|
||||||
AML_DEBUG_PRINT(")");
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace Kernel::ACPI
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
auto name = MUST(BAN::RefPtr<Name>::create(name_string.value().path.back(), object.node()));
|
auto name = MUST(BAN::RefPtr<Name>::create(name_string.value().path.back(), object.node()));
|
||||||
if (!context.root_namespace->add_named_object(context.scope.span(), name_string.value(), name))
|
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), name))
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
#if AML_DEBUG_LEVEL >= 2
|
#if AML_DEBUG_LEVEL >= 2
|
||||||
|
@ -33,12 +33,8 @@ namespace Kernel::ACPI
|
||||||
void AML::Name::debug_print(int indent) const
|
void AML::Name::debug_print(int indent) const
|
||||||
{
|
{
|
||||||
AML_DEBUG_PRINT_INDENT(indent);
|
AML_DEBUG_PRINT_INDENT(indent);
|
||||||
AML_DEBUG_PRINT("Name ");
|
AML_DEBUG_PRINT("Name {} { ", name);
|
||||||
name.debug_print();
|
object->debug_print(0);
|
||||||
AML_DEBUG_PRINTLN(" {");
|
|
||||||
object->debug_print(indent + 1);
|
|
||||||
AML_DEBUG_PRINTLN("");
|
|
||||||
AML_DEBUG_PRINT_INDENT(indent);
|
|
||||||
AML_DEBUG_PRINT("}");
|
AML_DEBUG_PRINT("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include <kernel/ACPI/AML/Integer.h>
|
||||||
|
#include <kernel/ACPI/AML/Method.h>
|
||||||
#include <kernel/ACPI/AML/Namespace.h>
|
#include <kernel/ACPI/AML/Namespace.h>
|
||||||
#include <kernel/ACPI/AML/ParseContext.h>
|
#include <kernel/ACPI/AML/ParseContext.h>
|
||||||
#include <kernel/ACPI/AML/Region.h>
|
#include <kernel/ACPI/AML/Region.h>
|
||||||
|
@ -5,69 +7,130 @@
|
||||||
namespace Kernel::ACPI
|
namespace Kernel::ACPI
|
||||||
{
|
{
|
||||||
|
|
||||||
BAN::Optional<BAN::Vector<AML::NameSeg>> AML::Namespace::resolve_path(BAN::Span<const AML::NameSeg> parsing_scope, const AML::NameString& relative_path)
|
static BAN::RefPtr<AML::Namespace> s_root_namespace;
|
||||||
{
|
static BAN::Vector<uint8_t> s_osi_aml_data;
|
||||||
BAN::Vector<NameSeg> canonical_path;
|
|
||||||
|
|
||||||
if (!relative_path.prefix.empty())
|
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()
|
||||||
|
{
|
||||||
|
ASSERT(s_root_namespace);
|
||||||
|
return s_root_namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
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[0] == '\\')
|
AML::NameString absolute_path;
|
||||||
|
MUST(absolute_path.prefix.push_back('\\'));
|
||||||
|
|
||||||
|
// Resolve root and parent references
|
||||||
|
if (relative_path.prefix == "\\"sv)
|
||||||
;
|
;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (parsing_scope.size() < relative_path.prefix.size())
|
if (relative_path.prefix.size() > relative_base.path.size())
|
||||||
{
|
{
|
||||||
AML_ERROR("Trying to resolve parent of root object");
|
AML_ERROR("Trying to resolve parent of root object");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < parsing_scope.size() - relative_path.prefix.size(); i++)
|
for (size_t i = 0; i < relative_base.path.size() - relative_path.prefix.size(); i++)
|
||||||
MUST(canonical_path.push_back(parsing_scope[i]));
|
MUST(absolute_path.path.push_back(relative_base.path[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
|
||||||
|
{
|
||||||
|
// 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// Check base base path
|
||||||
|
for (const auto& seg : relative_base.path)
|
||||||
{
|
{
|
||||||
for (auto seg : parsing_scope)
|
auto next_node = current_scope->objects[seg];
|
||||||
MUST(canonical_path.push_back(seg));
|
ASSERT(next_node && next_node->is_scope());
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& seg : relative_path.path)
|
if (!last_match_path.path.empty())
|
||||||
MUST(canonical_path.push_back(seg));
|
{
|
||||||
|
MUST(last_match_path.prefix.push_back('\\'));
|
||||||
|
return last_match_path;
|
||||||
|
}
|
||||||
|
|
||||||
return canonical_path;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::RefPtr<AML::NamedObject> AML::Namespace::find_object(BAN::Span<const AML::NameSeg> parsing_scope, const AML::NameString& relative_path)
|
BAN::RefPtr<AML::NamedObject> AML::Namespace::find_object(const AML::NameString& relative_base, const AML::NameString& relative_path)
|
||||||
{
|
{
|
||||||
auto canonical_path = resolve_path(parsing_scope, relative_path);
|
auto canonical_path = resolve_path(relative_base, relative_path);
|
||||||
if (!canonical_path.has_value())
|
if (!canonical_path.has_value())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (canonical_path->empty())
|
if (canonical_path->path.empty())
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
BAN::RefPtr<NamedObject> parent_object = this;
|
BAN::RefPtr<NamedObject> node = this;
|
||||||
|
for (const auto& seg : canonical_path->path)
|
||||||
for (const auto& seg : canonical_path.value())
|
|
||||||
{
|
{
|
||||||
if (!parent_object->is_scope())
|
// Resolve path validates that all nodes are scopes
|
||||||
{
|
ASSERT(node->is_scope());
|
||||||
AML_ERROR("Parent object is not a scope");
|
node = static_cast<Scope*>(node.ptr())->objects[seg];
|
||||||
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 parent_object;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AML::Namespace::add_named_object(BAN::Span<const NameSeg> parsing_scope, const AML::NameString& object_path, BAN::RefPtr<NamedObject> object)
|
bool AML::Namespace::add_named_object(ParseContext& parse_context, const AML::NameString& object_path, BAN::RefPtr<NamedObject> object)
|
||||||
{
|
{
|
||||||
ASSERT(!object_path.path.empty());
|
ASSERT(!object_path.path.empty());
|
||||||
ASSERT(object_path.path.back() == object->name);
|
ASSERT(object_path.path.back() == object->name);
|
||||||
|
@ -75,7 +138,7 @@ namespace Kernel::ACPI
|
||||||
auto parent_path = object_path;
|
auto parent_path = object_path;
|
||||||
parent_path.path.pop_back();
|
parent_path.path.pop_back();
|
||||||
|
|
||||||
auto parent_object = find_object(parsing_scope, parent_path);
|
auto parent_object = find_object(parse_context.scope, parent_path);
|
||||||
if (!parent_object)
|
if (!parent_object)
|
||||||
{
|
{
|
||||||
AML_ERROR("Parent object not found");
|
AML_ERROR("Parent object not found");
|
||||||
|
@ -95,17 +158,85 @@ namespace Kernel::ACPI
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object->parent = parent_scope;
|
||||||
|
|
||||||
MUST(parent_scope->objects.insert(object->name, object));
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::RefPtr<AML::Namespace> AML::Namespace::parse(BAN::ConstByteSpan aml_data)
|
bool AML::Namespace::remove_named_object(const AML::NameString& absolute_path)
|
||||||
{
|
{
|
||||||
auto result = MUST(BAN::RefPtr<Namespace>::create());
|
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));
|
||||||
|
|
||||||
AML::ParseContext context;
|
AML::ParseContext context;
|
||||||
context.aml_data = aml_data;
|
context.scope = AML::NameString("\\"sv);
|
||||||
context.root_namespace = result.ptr();
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
while (context.aml_data.size() > 0)
|
while (context.aml_data.size() > 0)
|
||||||
{
|
{
|
||||||
|
@ -113,11 +244,11 @@ namespace Kernel::ACPI
|
||||||
if (!result.success())
|
if (!result.success())
|
||||||
{
|
{
|
||||||
AML_ERROR("Failed to parse object");
|
AML_ERROR("Failed to parse object");
|
||||||
return {};
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
#include <kernel/ACPI/AML/Buffer.h>
|
#include <kernel/ACPI/AML/Buffer.h>
|
||||||
#include <kernel/ACPI/AML/Bytes.h>
|
#include <kernel/ACPI/AML/Bytes.h>
|
||||||
#include <kernel/ACPI/AML/Device.h>
|
#include <kernel/ACPI/AML/Device.h>
|
||||||
|
#include <kernel/ACPI/AML/Expression.h>
|
||||||
#include <kernel/ACPI/AML/Field.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/Integer.h>
|
||||||
#include <kernel/ACPI/AML/Method.h>
|
#include <kernel/ACPI/AML/Method.h>
|
||||||
#include <kernel/ACPI/AML/Mutex.h>
|
#include <kernel/ACPI/AML/Mutex.h>
|
||||||
|
@ -9,9 +12,14 @@
|
||||||
#include <kernel/ACPI/AML/Node.h>
|
#include <kernel/ACPI/AML/Node.h>
|
||||||
#include <kernel/ACPI/AML/Package.h>
|
#include <kernel/ACPI/AML/Package.h>
|
||||||
#include <kernel/ACPI/AML/ParseContext.h>
|
#include <kernel/ACPI/AML/ParseContext.h>
|
||||||
|
#include <kernel/ACPI/AML/PowerResource.h>
|
||||||
#include <kernel/ACPI/AML/Processor.h>
|
#include <kernel/ACPI/AML/Processor.h>
|
||||||
|
#include <kernel/ACPI/AML/Reference.h>
|
||||||
#include <kernel/ACPI/AML/Region.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/String.h>
|
||||||
|
#include <kernel/ACPI/AML/ThermalZone.h>
|
||||||
#include <kernel/ACPI/AML/Utils.h>
|
#include <kernel/ACPI/AML/Utils.h>
|
||||||
|
|
||||||
namespace Kernel::ACPI
|
namespace Kernel::ACPI
|
||||||
|
@ -20,6 +28,20 @@ namespace Kernel::ACPI
|
||||||
AML::ParseResult AML::ParseResult::Failure = AML::ParseResult(AML::ParseResult::Result::Failure);
|
AML::ParseResult AML::ParseResult::Failure = AML::ParseResult(AML::ParseResult::Result::Failure);
|
||||||
AML::ParseResult AML::ParseResult::Success = AML::ParseResult(AML::ParseResult::Result::Success);
|
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)
|
AML::ParseResult AML::parse_object(AML::ParseContext& context)
|
||||||
{
|
{
|
||||||
if (context.aml_data.size() < 1)
|
if (context.aml_data.size() < 1)
|
||||||
|
@ -36,14 +58,26 @@ namespace Kernel::ACPI
|
||||||
return AML::Field::parse(context);
|
return AML::Field::parse(context);
|
||||||
case AML::ExtOp::IndexFieldOp:
|
case AML::ExtOp::IndexFieldOp:
|
||||||
return AML::IndexField::parse(context);
|
return AML::IndexField::parse(context);
|
||||||
|
case AML::ExtOp::BankFieldOp:
|
||||||
|
return AML::BankField::parse(context);
|
||||||
case AML::ExtOp::OpRegionOp:
|
case AML::ExtOp::OpRegionOp:
|
||||||
return AML::OpRegion::parse(context);
|
return AML::OpRegion::parse(context);
|
||||||
case AML::ExtOp::DeviceOp:
|
case AML::ExtOp::DeviceOp:
|
||||||
return AML::Device::parse(context);
|
return AML::Device::parse(context);
|
||||||
case AML::ExtOp::MutexOp:
|
case AML::ExtOp::MutexOp:
|
||||||
|
case AML::ExtOp::AcquireOp:
|
||||||
|
case AML::ExtOp::ReleaseOp:
|
||||||
return AML::Mutex::parse(context);
|
return AML::Mutex::parse(context);
|
||||||
case AML::ExtOp::ProcessorOp:
|
case AML::ExtOp::ProcessorOp:
|
||||||
return AML::Processor::parse(context);
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -64,6 +98,59 @@ namespace Kernel::ACPI
|
||||||
return AML::Integer::parse(context.aml_data);
|
return AML::Integer::parse(context.aml_data);
|
||||||
case AML::Byte::StringPrefix:
|
case AML::Byte::StringPrefix:
|
||||||
return AML::String::parse(context);
|
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:
|
case AML::Byte::NameOp:
|
||||||
return AML::Name::parse(context);
|
return AML::Name::parse(context);
|
||||||
case AML::Byte::PackageOp:
|
case AML::Byte::PackageOp:
|
||||||
|
@ -74,6 +161,24 @@ namespace Kernel::ACPI
|
||||||
return AML::Buffer::parse(context);
|
return AML::Buffer::parse(context);
|
||||||
case AML::Byte::ScopeOp:
|
case AML::Byte::ScopeOp:
|
||||||
return AML::Scope::parse(context);
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -86,12 +191,38 @@ namespace Kernel::ACPI
|
||||||
auto name_string = AML::NameString::parse(context.aml_data);
|
auto name_string = AML::NameString::parse(context.aml_data);
|
||||||
if (!name_string.has_value())
|
if (!name_string.has_value())
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
auto aml_object = context.root_namespace->find_object(context.scope.span(), name_string.value());
|
auto aml_object = Namespace::root_namespace()->find_object(context.scope, name_string.value());
|
||||||
if (!aml_object)
|
if (!aml_object)
|
||||||
{
|
{
|
||||||
AML_TODO("NameString not found in namespace");
|
AML_ERROR("NameString {} not found in namespace", name_string.value());
|
||||||
return ParseResult::Failure;
|
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);
|
return ParseResult(aml_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,42 +20,47 @@ namespace Kernel::ACPI
|
||||||
if (!name_string.has_value())
|
if (!name_string.has_value())
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
BAN::RefPtr<Scope> scope;
|
auto named_object = Namespace::root_namespace()->find_object(context.scope, name_string.value());
|
||||||
if (auto named_object = context.root_namespace->find_object(context.scope.span(), name_string.value()))
|
if (!named_object)
|
||||||
{
|
{
|
||||||
if (!named_object->is_scope())
|
AML_ERROR("Scope name {} not found in namespace", name_string.value());
|
||||||
{
|
return ParseResult::Failure;
|
||||||
AML_ERROR("Scope name already exists and is not a scope");
|
|
||||||
return ParseResult::Failure;
|
|
||||||
}
|
|
||||||
scope = static_cast<Scope*>(named_object.ptr());
|
|
||||||
}
|
}
|
||||||
else
|
if (!named_object->is_scope())
|
||||||
{
|
{
|
||||||
scope = MUST(BAN::RefPtr<Scope>::create(name_string->path.back()));
|
AML_ERROR("Scope name {} does not name a namespace", name_string.value());
|
||||||
if (!context.root_namespace->add_named_object(context.scope.span(), name_string.value(), scope))
|
return ParseResult::Failure;
|
||||||
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());
|
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)
|
AML::ParseResult AML::Scope::enter_context_and_parse_term_list(ParseContext& outer_context, const AML::NameString& name_string, BAN::ConstByteSpan aml_data)
|
||||||
{
|
{
|
||||||
auto scope = outer_context.root_namespace->resolve_path(outer_context.scope.span(), name_string);
|
auto resolved_scope = Namespace::root_namespace()->resolve_path(outer_context.scope, name_string);
|
||||||
if (!scope.has_value())
|
if (!resolved_scope.has_value())
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
|
|
||||||
ParseContext scope_context = outer_context;
|
ParseContext scope_context;
|
||||||
scope_context.scope = scope.release_value();
|
scope_context.scope = resolved_scope.release_value();
|
||||||
scope_context.aml_data = aml_data;
|
scope_context.aml_data = aml_data;
|
||||||
|
scope_context.method_args = outer_context.method_args;
|
||||||
while (scope_context.aml_data.size() > 0)
|
while (scope_context.aml_data.size() > 0)
|
||||||
{
|
{
|
||||||
auto object_result = AML::parse_object(scope_context);
|
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())
|
if (!object_result.success())
|
||||||
return ParseResult::Failure;
|
return ParseResult::Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto& name : scope_context.created_objects)
|
||||||
|
MUST(outer_context.created_objects.push_back(BAN::move(name)));
|
||||||
|
|
||||||
return ParseResult::Success;
|
return ParseResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,4 +79,72 @@ namespace Kernel::ACPI
|
||||||
AML_DEBUG_PRINT("}");
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
#include <kernel/APIC.h>
|
#include <kernel/APIC.h>
|
||||||
#include <kernel/PIC.h>
|
#include <kernel/PIC.h>
|
||||||
|
|
||||||
#include <lai/helpers/sci.h>
|
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -45,12 +43,4 @@ namespace Kernel
|
||||||
return s_instance;
|
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include <BAN/ScopeGuard.h>
|
#include <BAN/ScopeGuard.h>
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/StringView.h>
|
||||||
|
#include <kernel/ACPI/ACPI.h>
|
||||||
#include <kernel/FS/DevFS/FileSystem.h>
|
#include <kernel/FS/DevFS/FileSystem.h>
|
||||||
#include <kernel/FS/ProcFS/FileSystem.h>
|
#include <kernel/FS/ProcFS/FileSystem.h>
|
||||||
#include <kernel/FS/VirtualFileSystem.h>
|
#include <kernel/FS/VirtualFileSystem.h>
|
||||||
|
@ -17,8 +18,6 @@
|
||||||
|
|
||||||
#include <LibELF/LoadableELF.h>
|
#include <LibELF/LoadableELF.h>
|
||||||
|
|
||||||
#include <lai/helpers/pm.h>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/banan-os.h>
|
#include <sys/banan-os.h>
|
||||||
|
@ -1191,11 +1190,7 @@ namespace Kernel
|
||||||
|
|
||||||
[[noreturn]] static void reset_system()
|
[[noreturn]] static void reset_system()
|
||||||
{
|
{
|
||||||
#if ARCH(x86_64)
|
// TODO: ACPI reset
|
||||||
lai_acpi_reset();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// acpi reset did not work
|
|
||||||
|
|
||||||
dwarnln("Could not reset with ACPI, crashing the cpu");
|
dwarnln("Could not reset with ACPI, crashing the cpu");
|
||||||
|
|
||||||
|
@ -1215,13 +1210,7 @@ namespace Kernel
|
||||||
if (command == POWEROFF_REBOOT)
|
if (command == POWEROFF_REBOOT)
|
||||||
reset_system();
|
reset_system();
|
||||||
|
|
||||||
#if ARCH(x86_64)
|
ACPI::ACPI::get().poweroff();
|
||||||
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);
|
return BAN::Error::from_errno(EUNKNOWN);
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,12 +174,13 @@ static void init2(void*)
|
||||||
|
|
||||||
dprintln("Scheduler started");
|
dprintln("Scheduler started");
|
||||||
|
|
||||||
InterruptController::get().enter_acpi_mode();
|
|
||||||
|
|
||||||
auto console = MUST(DevFileSystem::get().root_inode()->find_inode(cmdline.console));
|
auto console = MUST(DevFileSystem::get().root_inode()->find_inode(cmdline.console));
|
||||||
ASSERT(console->is_tty());
|
ASSERT(console->is_tty());
|
||||||
((TTY*)console.ptr())->set_as_current();
|
((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();
|
DevFileSystem::get().initialize_device_updater();
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
|
|
@ -1,149 +0,0 @@
|
||||||
#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 +0,0 @@
|
||||||
Subproject commit a228465314ee3a542f62d4bdefeb8fbe2b48da41
|
|
Loading…
Reference in New Issue