diff --git a/kernel/include/kernel/ACPI/ACPI.h b/kernel/include/kernel/ACPI/ACPI.h index 1fd2f76e8a..e43600df15 100644 --- a/kernel/include/kernel/ACPI/ACPI.h +++ b/kernel/include/kernel/ACPI/ACPI.h @@ -14,6 +14,9 @@ namespace Kernel::ACPI static BAN::ErrorOr initialize(); static ACPI& get(); + static void acquire_global_lock(); + static void release_global_lock(); + const SDTHeader* get_header(BAN::StringView signature, uint32_t index); // mode diff --git a/kernel/include/kernel/ACPI/AML/Method.h b/kernel/include/kernel/ACPI/AML/Method.h index d5715efe6a..1aeadc0bd7 100644 --- a/kernel/include/kernel/ACPI/AML/Method.h +++ b/kernel/include/kernel/ACPI/AML/Method.h @@ -69,20 +69,26 @@ namespace Kernel::ACPI::AML return ParseResult::Success; } - BAN::Optional> evaluate(Arguments args, uint8_t old_sync_level = 0) + BAN::Optional> evaluate(Arguments args, BAN::Vector& 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_level = old_sync_level; + context.sync_stack = BAN::move(current_sync_stack); for (auto& local : context.method_locals) local = MUST(BAN::RefPtr::create()); if (serialized) { mutex.lock(); - context.sync_level = BAN::Math::max(sync_level, old_sync_level); + MUST(context.sync_stack.push_back(sync_level)); } BAN::Optional> return_value; @@ -108,7 +114,12 @@ namespace Kernel::ACPI::AML } if (serialized) + { + context.sync_stack.pop_back(); mutex.unlock(); + } + + current_sync_stack = BAN::move(context.sync_stack); return return_value; } diff --git a/kernel/include/kernel/ACPI/AML/Mutex.h b/kernel/include/kernel/ACPI/AML/Mutex.h index e228d195d0..100cd2408f 100644 --- a/kernel/include/kernel/ACPI/AML/Mutex.h +++ b/kernel/include/kernel/ACPI/AML/Mutex.h @@ -1,14 +1,18 @@ #pragma once #include +#include #include #include +#include +#include namespace Kernel::ACPI::AML { struct Mutex : public AML::NamedObject { + Kernel::Mutex mutex; uint8_t sync_level; Mutex(NameSeg name, uint8_t sync_level) @@ -17,6 +21,33 @@ namespace Kernel::ACPI::AML {} static ParseResult parse(ParseContext& context) + { + ASSERT(context.aml_data.size() >= 2); + ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); + + switch (static_cast(context.aml_data[1])) + { + case AML::ExtOp::MutexOp: + return parse_mutex(context); + case AML::ExtOp::AcquireOp: + return parse_acquire(context); + case AML::ExtOp::ReleaseOp: + return parse_release(context); + default: + ASSERT_NOT_REACHED(); + } + } + + virtual void debug_print(int indent) const override + { + AML_DEBUG_PRINT_INDENT(indent); + AML_DEBUG_PRINT("Mutex "); + name.debug_print(); + AML_DEBUG_PRINT(" (SyncLevel: {})", sync_level); + } + + private: + static ParseResult parse_mutex(ParseContext& context) { ASSERT(context.aml_data.size() >= 2); ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); @@ -50,13 +81,86 @@ namespace Kernel::ACPI::AML return ParseResult::Success; } - virtual void debug_print(int indent) const override + static ParseResult parse_acquire(ParseContext& context) { - AML_DEBUG_PRINT_INDENT(indent); - AML_DEBUG_PRINT("Mutex "); - name.debug_print(); - AML_DEBUG_PRINT(" (SyncLevel: {})", sync_level); + ASSERT(context.aml_data.size() >= 2); + ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); + ASSERT(static_cast(context.aml_data[1]) == AML::ExtOp::AcquireOp); + context.aml_data = context.aml_data.slice(2); + + auto 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(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(MUST(BAN::RefPtr::create(AML::Integer::Ones))); + SystemTimer::get().sleep(1); + } + } + + MUST(context.sync_stack.push_back(mutex->sync_level)); + return ParseResult(MUST(BAN::RefPtr::create(0))); } + + static ParseResult parse_release(ParseContext& context) + { + ASSERT(context.aml_data.size() >= 2); + ASSERT(static_cast(context.aml_data[0]) == AML::Byte::ExtOpPrefix); + ASSERT(static_cast(context.aml_data[1]) == AML::ExtOp::ReleaseOp); + context.aml_data = context.aml_data.slice(2); + + auto 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(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; + } + }; } diff --git a/kernel/include/kernel/ACPI/AML/Namespace.h b/kernel/include/kernel/ACPI/AML/Namespace.h index e795bb7040..a4c429c218 100644 --- a/kernel/include/kernel/ACPI/AML/Namespace.h +++ b/kernel/include/kernel/ACPI/AML/Namespace.h @@ -9,8 +9,6 @@ namespace Kernel::ACPI::AML struct Namespace : public AML::Scope { - Mutex global_lock; - static BAN::RefPtr root_namespace(); Namespace(NameSeg name) : AML::Scope(Node::Type::Namespace, name) {} diff --git a/kernel/include/kernel/ACPI/AML/ParseContext.h b/kernel/include/kernel/ACPI/AML/ParseContext.h index 3440ad5bba..c02daa46b1 100644 --- a/kernel/include/kernel/ACPI/AML/ParseContext.h +++ b/kernel/include/kernel/ACPI/AML/ParseContext.h @@ -20,7 +20,9 @@ namespace Kernel::ACPI::AML // we don't really need large contiguous memory BAN::LinkedList created_objects; - uint8_t sync_level { 0 }; + uint8_t sync_level() const { return !sync_stack.empty() ? sync_stack.back() : 0; } + BAN::Vector sync_stack; + BAN::Array, 7> method_args; BAN::Array, 8> method_locals; }; diff --git a/kernel/include/kernel/ACPI/Headers.h b/kernel/include/kernel/ACPI/Headers.h index 66ebd8bb48..880de38c91 100644 --- a/kernel/include/kernel/ACPI/Headers.h +++ b/kernel/include/kernel/ACPI/Headers.h @@ -71,7 +71,7 @@ namespace Kernel::ACPI uint8_t reset_value; uint16_t arm_boot_arch; uint8_t fadt_minor_version; - uint64_t x_firmware_version; + uint64_t x_firmware_ctrl; uint64_t x_dsdt; uint8_t x_pm1a_evt_blk[12]; uint8_t x_pm1b_evt_blk[12]; @@ -100,6 +100,22 @@ namespace Kernel::ACPI uint8_t page_protection_and_oem_attribute; } __attribute__((packed)); + struct FACS + { + uint8_t signature[4]; + uint32_t length; + uint32_t hardware_signature; + uint32_t firmware_waking_vector; + uint32_t global_lock; + uint32_t flags; + uint64_t x_firmware_waking_vector; + uint8_t version; + uint8_t reserved[3]; + uint32_t ospm_flags; + uint8_t reserved2[24]; + }; + static_assert(sizeof(FACS) == 64); + } namespace BAN::Formatter diff --git a/kernel/kernel/ACPI/ACPI.cpp b/kernel/kernel/ACPI/ACPI.cpp index c0a27c2e37..1761f96c93 100644 --- a/kernel/kernel/ACPI/ACPI.cpp +++ b/kernel/kernel/ACPI/ACPI.cpp @@ -17,6 +17,64 @@ 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, @@ -58,6 +116,22 @@ namespace Kernel::ACPI return BAN::Error::from_errno(ENOMEM); TRY(s_instance->initialize_impl()); + { + ASSERT(!s_global_lock); + const auto* fadt = static_cast(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_addr); + s_global_lock = &facs->global_lock; + } + } + s_instance->m_namespace = AML::initialize_namespace(); return {}; @@ -318,7 +392,8 @@ namespace Kernel::ACPI AML::Method::Arguments args; args[0] = MUST(BAN::RefPtr::create(MUST(BAN::RefPtr::create(5)))); - if (!method->evaluate(args).has_value()) + BAN::Vector sync_stack; + if (!method->evaluate(args, sync_stack).has_value()) { dwarnln("Failed to evaluate \\_PTS"); return; @@ -397,7 +472,8 @@ namespace Kernel::ACPI dwarnln("Method \\_SB._INI has {} arguments, expected 0", method->arg_count); return BAN::Error::from_errno(EINVAL); } - method->evaluate({}); + BAN::Vector sync_stack; + method->evaluate({}, sync_stack); } // Initialize devices @@ -423,7 +499,8 @@ namespace Kernel::ACPI AML::Method::Arguments args; args[0] = MUST(BAN::RefPtr::create(MUST(BAN::RefPtr::create(mode)))); - method->evaluate(args); + BAN::Vector sync_stack; + method->evaluate(args, sync_stack); } dprintln("Devices are initialized"); diff --git a/kernel/kernel/ACPI/AML/Device.cpp b/kernel/kernel/ACPI/AML/Device.cpp index 482a6a6369..efbfc2a0eb 100644 --- a/kernel/kernel/ACPI/AML/Device.cpp +++ b/kernel/kernel/ACPI/AML/Device.cpp @@ -21,7 +21,8 @@ namespace Kernel::ACPI AML_ERROR("Method {}._STA has {} arguments, expected 0", scope, method->arg_count); return false; } - auto result = method->evaluate({}); + BAN::Vector sync_stack; + auto result = method->evaluate({}, sync_stack); if (!result.has_value()) { AML_ERROR("Failed to evaluate {}._STA", scope); @@ -55,7 +56,8 @@ namespace Kernel::ACPI AML_ERROR("Method {}._INI has {} arguments, expected 0", scope, method->arg_count); return false; } - method->evaluate({}); + BAN::Vector sync_stack; + method->evaluate({}, sync_stack); } } diff --git a/kernel/kernel/ACPI/AML/Field.cpp b/kernel/kernel/ACPI/AML/Field.cpp index 07dc370b8f..5c17331455 100644 --- a/kernel/kernel/ACPI/AML/Field.cpp +++ b/kernel/kernel/ACPI/AML/Field.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -436,10 +437,10 @@ namespace Kernel::ACPI } if (access_rules.lock_rule == FieldRules::LockRule::Lock) - Namespace::root_namespace()->global_lock.lock(); + ACPI::acquire_global_lock(); BAN::ScopeGuard unlock_guard([&] { if (access_rules.lock_rule == FieldRules::LockRule::Lock) - Namespace::root_namespace()->global_lock.unlock(); + ACPI::release_global_lock(); }); return store_internal(source_integer.value()); @@ -539,10 +540,10 @@ namespace Kernel::ACPI }; if (access_rules.lock_rule == FieldRules::LockRule::Lock) - Namespace::root_namespace()->global_lock.lock(); + ACPI::acquire_global_lock(); BAN::ScopeGuard unlock_guard([&] { if (access_rules.lock_rule == FieldRules::LockRule::Lock) - Namespace::root_namespace()->global_lock.unlock(); + ACPI::release_global_lock(); }); auto result = perform_read_general(0, bit_count, bit_offset, access_size.value(), read_func); @@ -582,10 +583,10 @@ namespace Kernel::ACPI }; if (access_rules.lock_rule == FieldRules::LockRule::Lock) - Namespace::root_namespace()->global_lock.lock(); + ACPI::acquire_global_lock(); BAN::ScopeGuard unlock_guard([&] { if (access_rules.lock_rule == FieldRules::LockRule::Lock) - Namespace::root_namespace()->global_lock.unlock(); + 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)) diff --git a/kernel/kernel/ACPI/AML/Node.cpp b/kernel/kernel/ACPI/AML/Node.cpp index cf6e4e8cb3..5dbe97c9af 100644 --- a/kernel/kernel/ACPI/AML/Node.cpp +++ b/kernel/kernel/ACPI/AML/Node.cpp @@ -56,6 +56,8 @@ namespace Kernel::ACPI case AML::ExtOp::DeviceOp: return AML::Device::parse(context); case AML::ExtOp::MutexOp: + case AML::ExtOp::AcquireOp: + case AML::ExtOp::ReleaseOp: return AML::Mutex::parse(context); case AML::ExtOp::ProcessorOp: return AML::Processor::parse(context); @@ -183,7 +185,7 @@ namespace Kernel::ACPI args[i] = MUST(BAN::RefPtr::create(arg.node())); } - auto result = method->evaluate(args, context.sync_level); + auto result = method->evaluate(args, context.sync_stack); if (!result.has_value()) { AML_ERROR("Failed to evaluate {}", name_string.value());