summaryrefslogtreecommitdiff
path: root/sys/dev/acpi/uacpi/mutex.c
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2025-05-17 21:56:07 -0400
committerIan Moffett <ian@osmora.org>2025-05-17 21:58:44 -0400
commit08eeb79db14145d83578025e1f0e7f7af460ee25 (patch)
treeb6af572a4b8dceb4f044f1e0bf5697f5c18dc0fd /sys/dev/acpi/uacpi/mutex.c
parent9c64c3e69fa60b3657d33e829a411cb37064a169 (diff)
kernel: acpi: Add uACPI portexpt
See https://github.com/uACPI/uACPI/ Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'sys/dev/acpi/uacpi/mutex.c')
-rw-r--r--sys/dev/acpi/uacpi/mutex.c396
1 files changed, 396 insertions, 0 deletions
diff --git a/sys/dev/acpi/uacpi/mutex.c b/sys/dev/acpi/uacpi/mutex.c
new file mode 100644
index 0000000..44cbac3
--- /dev/null
+++ b/sys/dev/acpi/uacpi/mutex.c
@@ -0,0 +1,396 @@
+#include <uacpi/platform/atomic.h>
+#include <uacpi/internal/mutex.h>
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/registers.h>
+#include <uacpi/internal/context.h>
+#include <uacpi/kernel_api.h>
+#include <uacpi/internal/namespace.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+#ifndef UACPI_REDUCED_HARDWARE
+
+#define GLOBAL_LOCK_PENDING (1 << 0)
+
+#define GLOBAL_LOCK_OWNED_BIT 1
+#define GLOBAL_LOCK_OWNED (1 << GLOBAL_LOCK_OWNED_BIT)
+
+#define GLOBAL_LOCK_MASK 3u
+
+static uacpi_bool try_acquire_global_lock_from_firmware(uacpi_u32 *lock)
+{
+ uacpi_u32 value, new_value;
+ uacpi_bool was_owned;
+
+ value = *(volatile uacpi_u32*)lock;
+ do {
+ was_owned = (value & GLOBAL_LOCK_OWNED) >> GLOBAL_LOCK_OWNED_BIT;
+
+ // Clear both owned & pending bits.
+ new_value = value & ~GLOBAL_LOCK_MASK;
+
+ // Set owned unconditionally
+ new_value |= GLOBAL_LOCK_OWNED;
+
+ // Set pending iff the lock was owned at the time of reading
+ if (was_owned)
+ new_value |= GLOBAL_LOCK_PENDING;
+ } while (!uacpi_atomic_cmpxchg32(lock, &value, new_value));
+
+ return !was_owned;
+}
+
+static uacpi_bool do_release_global_lock_to_firmware(uacpi_u32 *lock)
+{
+ uacpi_u32 value, new_value;
+
+ value = *(volatile uacpi_u32*)lock;
+ do {
+ new_value = value & ~GLOBAL_LOCK_MASK;
+ } while (!uacpi_atomic_cmpxchg32(lock, &value, new_value));
+
+ return value & GLOBAL_LOCK_PENDING;
+}
+
+static uacpi_status uacpi_acquire_global_lock_from_firmware(void)
+{
+ uacpi_cpu_flags flags;
+ uacpi_u16 spins = 0;
+ uacpi_bool success;
+
+ if (!g_uacpi_rt_ctx.has_global_lock)
+ return UACPI_STATUS_OK;
+
+ flags = uacpi_kernel_lock_spinlock(g_uacpi_rt_ctx.global_lock_spinlock);
+ for (;;) {
+ spins++;
+ uacpi_trace(
+ "trying to acquire the global lock from firmware... (attempt %u)\n",
+ spins
+ );
+
+ success = try_acquire_global_lock_from_firmware(
+ &g_uacpi_rt_ctx.facs->global_lock
+ );
+ if (success)
+ break;
+
+ if (uacpi_unlikely(spins == 0xFFFF))
+ break;
+
+ g_uacpi_rt_ctx.global_lock_pending = UACPI_TRUE;
+ uacpi_trace(
+ "global lock is owned by firmware, waiting for a release "
+ "notification...\n"
+ );
+ uacpi_kernel_unlock_spinlock(g_uacpi_rt_ctx.global_lock_spinlock, flags);
+
+ uacpi_kernel_wait_for_event(g_uacpi_rt_ctx.global_lock_event, 0xFFFF);
+ flags = uacpi_kernel_lock_spinlock(g_uacpi_rt_ctx.global_lock_spinlock);
+ }
+
+ g_uacpi_rt_ctx.global_lock_pending = UACPI_FALSE;
+ uacpi_kernel_unlock_spinlock(g_uacpi_rt_ctx.global_lock_spinlock, flags);
+
+ if (uacpi_unlikely(!success)) {
+ uacpi_error("unable to acquire global lock after %u attempts\n", spins);
+ return UACPI_STATUS_HARDWARE_TIMEOUT;
+ }
+
+ uacpi_trace("global lock successfully acquired after %u attempt%s\n",
+ spins, spins > 1 ? "s" : "");
+ return UACPI_STATUS_OK;
+}
+
+static void uacpi_release_global_lock_to_firmware(void)
+{
+ if (!g_uacpi_rt_ctx.has_global_lock)
+ return;
+
+ uacpi_trace("releasing the global lock to firmware...\n");
+ if (do_release_global_lock_to_firmware(&g_uacpi_rt_ctx.facs->global_lock)) {
+ uacpi_trace("notifying firmware of the global lock release since the "
+ "pending bit was set\n");
+ uacpi_write_register_field(UACPI_REGISTER_FIELD_GBL_RLS, 1);
+ }
+}
+#endif
+
+UACPI_ALWAYS_OK_FOR_REDUCED_HARDWARE(
+ uacpi_status uacpi_acquire_global_lock_from_firmware(void)
+)
+UACPI_STUB_IF_REDUCED_HARDWARE(
+ void uacpi_release_global_lock_to_firmware(void)
+)
+
+uacpi_status uacpi_acquire_native_mutex_with_timeout(
+ uacpi_handle mtx, uacpi_u16 timeout
+)
+{
+ uacpi_status ret;
+
+ if (uacpi_unlikely(mtx == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_kernel_acquire_mutex(mtx, timeout);
+ if (uacpi_likely_success(ret))
+ return ret;
+
+ if (uacpi_unlikely(ret != UACPI_STATUS_TIMEOUT || timeout == 0xFFFF)) {
+ uacpi_error(
+ "unexpected status %08X (%s) while acquiring %p (timeout=%04X)\n",
+ ret, uacpi_status_to_string(ret), mtx, timeout
+ );
+ }
+
+ return ret;
+}
+
+uacpi_status uacpi_acquire_global_lock(uacpi_u16 timeout, uacpi_u32 *out_seq)
+{
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(out_seq == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_acquire_native_mutex_with_timeout(
+ g_uacpi_rt_ctx.global_lock_mutex->handle, timeout
+ );
+ if (ret != UACPI_STATUS_OK)
+ return ret;
+
+ ret = uacpi_acquire_global_lock_from_firmware();
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_release_native_mutex(g_uacpi_rt_ctx.global_lock_mutex->handle);
+ return ret;
+ }
+
+ if (uacpi_unlikely(g_uacpi_rt_ctx.global_lock_seq_num == 0xFFFFFFFF))
+ g_uacpi_rt_ctx.global_lock_seq_num = 0;
+
+ *out_seq = g_uacpi_rt_ctx.global_lock_seq_num++;
+ g_uacpi_rt_ctx.global_lock_acquired = UACPI_TRUE;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_release_global_lock(uacpi_u32 seq)
+{
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_unlikely(!g_uacpi_rt_ctx.global_lock_acquired ||
+ seq != g_uacpi_rt_ctx.global_lock_seq_num))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ g_uacpi_rt_ctx.global_lock_acquired = UACPI_FALSE;
+ uacpi_release_global_lock_to_firmware();
+ uacpi_release_native_mutex(g_uacpi_rt_ctx.global_lock_mutex->handle);
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_bool uacpi_this_thread_owns_aml_mutex(uacpi_mutex *mutex)
+{
+ uacpi_thread_id id;
+
+ id = UACPI_ATOMIC_LOAD_THREAD_ID(&mutex->owner);
+ return id == uacpi_kernel_get_thread_id();
+}
+
+uacpi_status uacpi_acquire_aml_mutex(uacpi_mutex *mutex, uacpi_u16 timeout)
+{
+ uacpi_thread_id this_id;
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ this_id = uacpi_kernel_get_thread_id();
+ if (UACPI_ATOMIC_LOAD_THREAD_ID(&mutex->owner) == this_id) {
+ if (uacpi_unlikely(mutex->depth == 0xFFFF)) {
+ uacpi_warn(
+ "failing an attempt to acquire mutex @%p, too many recursive "
+ "acquires\n", mutex
+ );
+ return UACPI_STATUS_DENIED;
+ }
+
+ mutex->depth++;
+ return ret;
+ }
+
+ uacpi_namespace_write_unlock();
+ ret = uacpi_acquire_native_mutex_with_timeout(mutex->handle, timeout);
+ if (ret != UACPI_STATUS_OK)
+ goto out;
+
+ if (mutex->handle == g_uacpi_rt_ctx.global_lock_mutex->handle) {
+ ret = uacpi_acquire_global_lock_from_firmware();
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_release_native_mutex(mutex->handle);
+ goto out;
+ }
+ }
+
+ UACPI_ATOMIC_STORE_THREAD_ID(&mutex->owner, this_id);
+ mutex->depth = 1;
+
+out:
+ uacpi_namespace_write_lock();
+ return ret;
+}
+
+uacpi_status uacpi_release_aml_mutex(uacpi_mutex *mutex)
+{
+ if (mutex->depth-- > 1)
+ return UACPI_STATUS_OK;
+
+ if (mutex->handle == g_uacpi_rt_ctx.global_lock_mutex->handle)
+ uacpi_release_global_lock_to_firmware();
+
+ UACPI_ATOMIC_STORE_THREAD_ID(&mutex->owner, UACPI_THREAD_ID_NONE);
+ uacpi_release_native_mutex(mutex->handle);
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_recursive_lock_init(struct uacpi_recursive_lock *lock)
+{
+ lock->mutex = uacpi_kernel_create_mutex();
+ if (uacpi_unlikely(lock->mutex == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ lock->owner = UACPI_THREAD_ID_NONE;
+ lock->depth = 0;
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_recursive_lock_deinit(struct uacpi_recursive_lock *lock)
+{
+ if (uacpi_unlikely(lock->depth)) {
+ uacpi_warn(
+ "de-initializing active recursive lock %p with depth=%zu\n",
+ lock, lock->depth
+ );
+ lock->depth = 0;
+ }
+
+ lock->owner = UACPI_THREAD_ID_NONE;
+
+ if (lock->mutex != UACPI_NULL) {
+ uacpi_kernel_free_mutex(lock->mutex);
+ lock->mutex = UACPI_NULL;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_recursive_lock_acquire(struct uacpi_recursive_lock *lock)
+{
+ uacpi_thread_id this_id;
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ this_id = uacpi_kernel_get_thread_id();
+ if (UACPI_ATOMIC_LOAD_THREAD_ID(&lock->owner) == this_id) {
+ lock->depth++;
+ return ret;
+ }
+
+ ret = uacpi_acquire_native_mutex(lock->mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ UACPI_ATOMIC_STORE_THREAD_ID(&lock->owner, this_id);
+ lock->depth = 1;
+ return ret;
+}
+
+uacpi_status uacpi_recursive_lock_release(struct uacpi_recursive_lock *lock)
+{
+ if (lock->depth-- > 1)
+ return UACPI_STATUS_OK;
+
+ UACPI_ATOMIC_STORE_THREAD_ID(&lock->owner, UACPI_THREAD_ID_NONE);
+ return uacpi_release_native_mutex(lock->mutex);
+}
+
+uacpi_status uacpi_rw_lock_init(struct uacpi_rw_lock *lock)
+{
+ lock->read_mutex = uacpi_kernel_create_mutex();
+ if (uacpi_unlikely(lock->read_mutex == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ lock->write_mutex = uacpi_kernel_create_mutex();
+ if (uacpi_unlikely(lock->write_mutex == UACPI_NULL)) {
+ uacpi_kernel_free_mutex(lock->read_mutex);
+ lock->read_mutex = UACPI_NULL;
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ lock->num_readers = 0;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_rw_lock_deinit(struct uacpi_rw_lock *lock)
+{
+ if (uacpi_unlikely(lock->num_readers)) {
+ uacpi_warn("de-initializing rw_lock %p with %zu active readers\n",
+ lock, lock->num_readers);
+ lock->num_readers = 0;
+ }
+
+ if (lock->read_mutex != UACPI_NULL) {
+ uacpi_kernel_free_mutex(lock->read_mutex);
+ lock->read_mutex = UACPI_NULL;
+ }
+ if (lock->write_mutex != UACPI_NULL) {
+ uacpi_kernel_free_mutex(lock->write_mutex);
+ lock->write_mutex = UACPI_NULL;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_rw_lock_read(struct uacpi_rw_lock *lock)
+{
+ uacpi_status ret;
+
+ ret = uacpi_acquire_native_mutex(lock->read_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (lock->num_readers++ == 0) {
+ ret = uacpi_acquire_native_mutex(lock->write_mutex);
+ if (uacpi_unlikely_error(ret))
+ lock->num_readers = 0;
+ }
+
+ uacpi_kernel_release_mutex(lock->read_mutex);
+ return ret;
+}
+
+uacpi_status uacpi_rw_unlock_read(struct uacpi_rw_lock *lock)
+{
+ uacpi_status ret;
+
+ ret = uacpi_acquire_native_mutex(lock->read_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (lock->num_readers-- == 1)
+ uacpi_release_native_mutex(lock->write_mutex);
+
+ uacpi_kernel_release_mutex(lock->read_mutex);
+ return ret;
+}
+
+uacpi_status uacpi_rw_lock_write(struct uacpi_rw_lock *lock)
+{
+ return uacpi_acquire_native_mutex(lock->write_mutex);
+}
+
+uacpi_status uacpi_rw_unlock_write(struct uacpi_rw_lock *lock)
+{
+ return uacpi_release_native_mutex(lock->write_mutex);
+}
+
+#endif // !UACPI_BAREBONES_MODE