summaryrefslogtreecommitdiff
path: root/sys/dev/acpi/uacpi/interpreter.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/interpreter.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/interpreter.c')
-rw-r--r--sys/dev/acpi/uacpi/interpreter.c6053
1 files changed, 6053 insertions, 0 deletions
diff --git a/sys/dev/acpi/uacpi/interpreter.c b/sys/dev/acpi/uacpi/interpreter.c
new file mode 100644
index 0000000..8ffb8d5
--- /dev/null
+++ b/sys/dev/acpi/uacpi/interpreter.c
@@ -0,0 +1,6053 @@
+#include <uacpi/internal/types.h>
+#include <uacpi/internal/interpreter.h>
+#include <uacpi/internal/dynamic_array.h>
+#include <uacpi/internal/opcodes.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/internal/context.h>
+#include <uacpi/internal/shareable.h>
+#include <uacpi/internal/tables.h>
+#include <uacpi/internal/helpers.h>
+#include <uacpi/kernel_api.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/opregion.h>
+#include <uacpi/internal/io.h>
+#include <uacpi/internal/notify.h>
+#include <uacpi/internal/resources.h>
+#include <uacpi/internal/event.h>
+#include <uacpi/internal/mutex.h>
+#include <uacpi/internal/osi.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+enum item_type {
+ ITEM_NONE = 0,
+ ITEM_NAMESPACE_NODE,
+ ITEM_OBJECT,
+ ITEM_EMPTY_OBJECT,
+ ITEM_PACKAGE_LENGTH,
+ ITEM_IMMEDIATE,
+};
+
+struct package_length {
+ uacpi_u32 begin;
+ uacpi_u32 end;
+};
+
+struct item {
+ uacpi_u8 type;
+ union {
+ uacpi_handle handle;
+ uacpi_object *obj;
+ struct uacpi_namespace_node *node;
+ struct package_length pkg;
+ uacpi_u64 immediate;
+ uacpi_u8 immediate_bytes[8];
+ };
+};
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(item_array, struct item, 8)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(item_array, struct item, static)
+
+struct op_context {
+ uacpi_u8 pc;
+ uacpi_bool preempted;
+
+ /*
+ * == 0 -> none
+ * >= 1 -> item[idx - 1]
+ */
+ uacpi_u8 tracked_pkg_idx;
+
+ uacpi_aml_op switched_from;
+
+ const struct uacpi_op_spec *op;
+ struct item_array items;
+};
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(op_context_array, struct op_context, 8)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(
+ op_context_array, struct op_context, static
+)
+
+static struct op_context *op_context_array_one_before_last(
+ struct op_context_array *arr
+)
+{
+ uacpi_size size;
+
+ size = op_context_array_size(arr);
+
+ if (size < 2)
+ return UACPI_NULL;
+
+ return op_context_array_at(arr, size - 2);
+}
+
+enum code_block_type {
+ CODE_BLOCK_IF = 1,
+ CODE_BLOCK_ELSE = 2,
+ CODE_BLOCK_WHILE = 3,
+ CODE_BLOCK_SCOPE = 4,
+};
+
+struct code_block {
+ enum code_block_type type;
+ uacpi_u32 begin, end;
+ union {
+ struct uacpi_namespace_node *node;
+ uacpi_u64 expiration_point;
+ };
+};
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(code_block_array, struct code_block, 8)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(
+ code_block_array, struct code_block, static
+)
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(held_mutexes_array, uacpi_mutex*, 8)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(
+ held_mutexes_array, uacpi_mutex*, static
+)
+
+static uacpi_status held_mutexes_array_push(
+ struct held_mutexes_array *arr, uacpi_mutex *mutex
+)
+{
+ uacpi_mutex **slot;
+
+ slot = held_mutexes_array_alloc(arr);
+ if (uacpi_unlikely(slot == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ *slot = mutex;
+ uacpi_shareable_ref(mutex);
+ return UACPI_STATUS_OK;
+}
+
+static void held_mutexes_array_remove_idx(
+ struct held_mutexes_array *arr, uacpi_size i
+)
+{
+ uacpi_size size;
+
+ size = held_mutexes_array_inline_capacity(arr);
+
+ // Only the dynamic array part is affected
+ if (i >= size) {
+ i -= size;
+ size = arr->size_including_inline - size;
+ size -= i + 1;
+
+ uacpi_memmove(
+ &arr->dynamic_storage[i], &arr->dynamic_storage[i + 1],
+ size * sizeof(arr->inline_storage[0])
+ );
+
+ held_mutexes_array_pop(arr);
+ return;
+ }
+
+ size = UACPI_MIN(held_mutexes_array_inline_capacity(arr),
+ arr->size_including_inline);
+ size -= i + 1;
+ uacpi_memmove(
+ &arr->inline_storage[i], &arr->inline_storage[i + 1],
+ size * sizeof(arr->inline_storage[0])
+ );
+
+ size = held_mutexes_array_size(arr);
+ i = held_mutexes_array_inline_capacity(arr);
+
+ /*
+ * This array has dynamic storage as well, now we have to take the first
+ * dynamic item, move it to the top of inline storage, and then shift all
+ * dynamic items backward by 1 as well.
+ */
+ if (size > i) {
+ arr->inline_storage[i - 1] = arr->dynamic_storage[0];
+ size -= i + 1;
+
+ uacpi_memmove(
+ &arr->dynamic_storage[0], &arr->dynamic_storage[1],
+ size * sizeof(arr->inline_storage[0])
+ );
+ }
+
+ held_mutexes_array_pop(arr);
+}
+
+enum force_release {
+ FORCE_RELEASE_NO,
+ FORCE_RELEASE_YES,
+};
+
+static uacpi_status held_mutexes_array_remove_and_release(
+ struct held_mutexes_array *arr, uacpi_mutex *mutex,
+ enum force_release force
+)
+{
+ uacpi_mutex *item;
+ uacpi_size i;
+
+ if (uacpi_unlikely(held_mutexes_array_size(arr) == 0))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ item = *held_mutexes_array_last(arr);
+
+ if (uacpi_unlikely(item->sync_level != mutex->sync_level &&
+ force != FORCE_RELEASE_YES)) {
+ uacpi_warn(
+ "ignoring mutex @%p release due to sync level mismatch: %d vs %d\n",
+ mutex, mutex->sync_level, item->sync_level
+ );
+
+ // We still return OK because we don't want to abort because of this
+ return UACPI_STATUS_OK;
+ }
+
+ if (mutex->depth > 1 && force == FORCE_RELEASE_NO) {
+ uacpi_release_aml_mutex(mutex);
+ return UACPI_STATUS_OK;
+ }
+
+ // Fast path for well-behaved AML that releases mutexes in descending order
+ if (uacpi_likely(item == mutex)) {
+ held_mutexes_array_pop(arr);
+ goto do_release;
+ }
+
+ /*
+ * The mutex being released is not the last one acquired, although we did
+ * verify that at least it has the same sync level. Anyway, now we have
+ * to search for it and then remove it from the array while shifting
+ * everything backwards.
+ */
+ i = held_mutexes_array_size(arr);
+ for (;;) {
+ item = *held_mutexes_array_at(arr, --i);
+ if (item == mutex)
+ break;
+
+ if (uacpi_unlikely(i == 0))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ held_mutexes_array_remove_idx(arr, i);
+
+do_release:
+ // This is either a force release, or depth was already 1 to begin with
+ mutex->depth = 1;
+ uacpi_release_aml_mutex(mutex);
+
+ uacpi_mutex_unref(mutex);
+ return UACPI_STATUS_OK;
+}
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(
+ temp_namespace_node_array, uacpi_namespace_node*, 8)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(
+ temp_namespace_node_array, uacpi_namespace_node*, static
+)
+
+static uacpi_status temp_namespace_node_array_push(
+ struct temp_namespace_node_array *arr, uacpi_namespace_node *node
+)
+{
+ uacpi_namespace_node **slot;
+
+ slot = temp_namespace_node_array_alloc(arr);
+ if (uacpi_unlikely(slot == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ *slot = node;
+ return UACPI_STATUS_OK;
+}
+
+struct call_frame {
+ struct uacpi_control_method *method;
+
+ uacpi_object *args[7];
+ uacpi_object *locals[8];
+
+ struct op_context_array pending_ops;
+ struct code_block_array code_blocks;
+ struct temp_namespace_node_array temp_nodes;
+ struct code_block *last_while;
+ uacpi_u64 prev_while_expiration;
+ uacpi_u32 prev_while_code_offset;
+
+ uacpi_u32 code_offset;
+
+ struct uacpi_namespace_node *cur_scope;
+
+ // Only used if the method is serialized
+ uacpi_u8 prev_sync_level;
+};
+
+static void *call_frame_cursor(struct call_frame *frame)
+{
+ return frame->method->code + frame->code_offset;
+}
+
+static uacpi_size call_frame_code_bytes_left(struct call_frame *frame)
+{
+ return frame->method->size - frame->code_offset;
+}
+
+static uacpi_bool call_frame_has_code(struct call_frame *frame)
+{
+ return call_frame_code_bytes_left(frame) > 0;
+}
+
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE(call_frame_array, struct call_frame, 4)
+DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(
+ call_frame_array, struct call_frame, static
+)
+
+static struct call_frame *call_frame_array_one_before_last(
+ struct call_frame_array *arr
+)
+{
+ uacpi_size size;
+
+ size = call_frame_array_size(arr);
+
+ if (size < 2)
+ return UACPI_NULL;
+
+ return call_frame_array_at(arr, size - 2);
+}
+
+// NOTE: Try to keep size under 2 pages
+struct execution_context {
+ uacpi_object *ret;
+ struct call_frame_array call_stack;
+ struct held_mutexes_array held_mutexes;
+
+ struct call_frame *cur_frame;
+ struct code_block *cur_block;
+ const struct uacpi_op_spec *cur_op;
+ struct op_context *prev_op_ctx;
+ struct op_context *cur_op_ctx;
+
+ uacpi_u8 sync_level;
+};
+
+#define AML_READ(ptr, offset) (*(((uacpi_u8*)(ptr)) + offset))
+
+static uacpi_status parse_nameseg(uacpi_u8 *cursor,
+ uacpi_object_name *out_name)
+{
+ if (uacpi_unlikely(!uacpi_is_valid_nameseg(cursor)))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ uacpi_memcpy(&out_name->id, cursor, 4);
+ return UACPI_STATUS_OK;
+}
+
+/*
+ * -------------------------------------------------------------
+ * RootChar := ‘\’
+ * ParentPrefixChar := ‘^’
+ * ‘\’ := 0x5C
+ * ‘^’ := 0x5E
+ * ------------------------------------------------------------
+ * NameSeg := <leadnamechar namechar namechar namechar>
+ * NameString := <rootchar namepath> | <prefixpath namepath>
+ * PrefixPath := Nothing | <’^’ prefixpath>
+ * NamePath := NameSeg | DualNamePath | MultiNamePath | NullName
+ * DualNamePath := DualNamePrefix NameSeg NameSeg
+ * MultiNamePath := MultiNamePrefix SegCount NameSeg(SegCount)
+ */
+
+static uacpi_status name_string_to_path(
+ struct call_frame *frame, uacpi_size offset,
+ uacpi_char **out_string, uacpi_size *out_size
+)
+{
+ uacpi_size bytes_left, prefix_bytes, nameseg_bytes = 0, namesegs;
+ uacpi_char *base_cursor, *cursor;
+ uacpi_char prev_char;
+
+ bytes_left = frame->method->size - offset;
+ cursor = (uacpi_char*)frame->method->code + offset;
+ base_cursor = cursor;
+ namesegs = 0;
+
+ prefix_bytes = 0;
+ for (;;) {
+ if (uacpi_unlikely(bytes_left == 0))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ prev_char = *cursor;
+
+ switch (prev_char) {
+ case '^':
+ case '\\':
+ prefix_bytes++;
+ cursor++;
+ bytes_left--;
+ break;
+ default:
+ break;
+ }
+
+ if (prev_char != '^')
+ break;
+ }
+
+ // At least a NullName byte is expected here
+ if (uacpi_unlikely(bytes_left == 0))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ namesegs = 0;
+ bytes_left--;
+ switch (*cursor++)
+ {
+ case UACPI_DUAL_NAME_PREFIX:
+ namesegs = 2;
+ break;
+ case UACPI_MULTI_NAME_PREFIX:
+ if (uacpi_unlikely(bytes_left == 0))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ namesegs = *(uacpi_u8*)cursor;
+ if (uacpi_unlikely(namesegs == 0)) {
+ uacpi_error("MultiNamePrefix but SegCount is 0\n");
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+ }
+
+ cursor++;
+ bytes_left--;
+ break;
+ case UACPI_NULL_NAME:
+ break;
+ default:
+ /*
+ * Might be an invalid byte, but assume single nameseg for now,
+ * the code below will validate it for us.
+ */
+ cursor--;
+ bytes_left++;
+ namesegs = 1;
+ break;
+ }
+
+ if (uacpi_unlikely((namesegs * 4) > bytes_left))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ if (namesegs) {
+ // 4 chars per nameseg
+ nameseg_bytes = namesegs * 4;
+
+ // dot separator for every nameseg
+ nameseg_bytes += namesegs - 1;
+ }
+
+ *out_size = nameseg_bytes + prefix_bytes + 1;
+
+ *out_string = uacpi_kernel_alloc(*out_size);
+ if (*out_string == UACPI_NULL)
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(*out_string, base_cursor, prefix_bytes);
+
+ base_cursor = *out_string;
+ base_cursor += prefix_bytes;
+
+ while (namesegs-- > 0) {
+ uacpi_memcpy(base_cursor, cursor, 4);
+ cursor += 4;
+ base_cursor += 4;
+
+ if (namesegs)
+ *base_cursor++ = '.';
+ }
+
+ *base_cursor = '\0';
+ return UACPI_STATUS_OK;
+}
+
+enum resolve_behavior {
+ RESOLVE_CREATE_LAST_NAMESEG_FAIL_IF_EXISTS,
+ RESOLVE_FAIL_IF_DOESNT_EXIST,
+};
+
+static uacpi_status resolve_name_string(
+ struct call_frame *frame,
+ enum resolve_behavior behavior,
+ struct uacpi_namespace_node **out_node
+)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ uacpi_u8 *cursor;
+ uacpi_size bytes_left, namesegs = 0;
+ struct uacpi_namespace_node *parent, *cur_node = frame->cur_scope;
+ uacpi_char prev_char = 0;
+ uacpi_bool just_one_nameseg = UACPI_TRUE;
+
+ bytes_left = call_frame_code_bytes_left(frame);
+ cursor = call_frame_cursor(frame);
+
+ for (;;) {
+ if (uacpi_unlikely(bytes_left == 0))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ switch (*cursor) {
+ case '\\':
+ if (prev_char == '^')
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ cur_node = uacpi_namespace_root();
+ break;
+ case '^':
+ // Tried to go behind root
+ if (uacpi_unlikely(cur_node == uacpi_namespace_root()))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ cur_node = cur_node->parent;
+ break;
+ default:
+ break;
+ }
+
+ prev_char = *cursor;
+
+ switch (prev_char) {
+ case '^':
+ case '\\':
+ just_one_nameseg = UACPI_FALSE;
+ cursor++;
+ bytes_left--;
+ break;
+ default:
+ break;
+ }
+
+ if (prev_char != '^')
+ break;
+ }
+
+ // At least a NullName byte is expected here
+ if (uacpi_unlikely(bytes_left == 0))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ bytes_left--;
+ switch (*cursor++)
+ {
+ case UACPI_DUAL_NAME_PREFIX:
+ namesegs = 2;
+ just_one_nameseg = UACPI_FALSE;
+ break;
+ case UACPI_MULTI_NAME_PREFIX:
+ if (uacpi_unlikely(bytes_left == 0))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ namesegs = *cursor;
+ if (uacpi_unlikely(namesegs == 0)) {
+ uacpi_error("MultiNamePrefix but SegCount is 0\n");
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+ }
+
+ cursor++;
+ bytes_left--;
+ just_one_nameseg = UACPI_FALSE;
+ break;
+ case UACPI_NULL_NAME:
+ if (behavior == RESOLVE_CREATE_LAST_NAMESEG_FAIL_IF_EXISTS ||
+ just_one_nameseg)
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ goto out;
+ default:
+ /*
+ * Might be an invalid byte, but assume single nameseg for now,
+ * the code below will validate it for us.
+ */
+ cursor--;
+ bytes_left++;
+ namesegs = 1;
+ break;
+ }
+
+ if (uacpi_unlikely((namesegs * 4) > bytes_left))
+ return UACPI_STATUS_AML_INVALID_NAMESTRING;
+
+ for (; namesegs; cursor += 4, namesegs--) {
+ uacpi_object_name name;
+
+ ret = parse_nameseg(cursor, &name);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ parent = cur_node;
+ cur_node = uacpi_namespace_node_find_sub_node(parent, name);
+
+ switch (behavior) {
+ case RESOLVE_CREATE_LAST_NAMESEG_FAIL_IF_EXISTS:
+ if (namesegs == 1) {
+ if (cur_node) {
+ cur_node = UACPI_NULL;
+ ret = UACPI_STATUS_AML_OBJECT_ALREADY_EXISTS;
+ goto out;
+ }
+
+ // Create the node and link to parent but don't install YET
+ cur_node = uacpi_namespace_node_alloc(name);
+ if (uacpi_unlikely(cur_node == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ cur_node->parent = parent;
+ }
+ break;
+ case RESOLVE_FAIL_IF_DOESNT_EXIST:
+ if (just_one_nameseg) {
+ while (!cur_node && parent != uacpi_namespace_root()) {
+ cur_node = parent;
+ parent = cur_node->parent;
+
+ cur_node = uacpi_namespace_node_find_sub_node(parent, name);
+ }
+ }
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (cur_node == UACPI_NULL) {
+ ret = UACPI_STATUS_NOT_FOUND;
+ break;
+ }
+ }
+
+out:
+ cursor += namesegs * 4;
+ frame->code_offset = cursor - frame->method->code;
+
+ if (uacpi_likely_success(ret) && behavior == RESOLVE_FAIL_IF_DOESNT_EXIST)
+ uacpi_shareable_ref(cur_node);
+
+ *out_node = cur_node;
+ return ret;
+}
+
+static uacpi_status do_install_node_item(struct call_frame *frame,
+ struct item *item)
+{
+ uacpi_status ret;
+
+ ret = uacpi_namespace_node_install(item->node->parent, item->node);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (!frame->method->named_objects_persist)
+ ret = temp_namespace_node_array_push(&frame->temp_nodes, item->node);
+
+ if (uacpi_likely_success(ret))
+ item->node = UACPI_NULL;
+
+ return ret;
+}
+
+static uacpi_u8 peek_next_op(struct call_frame *frame, uacpi_aml_op *out_op)
+{
+ uacpi_aml_op op;
+ uacpi_size bytes_left;
+ uacpi_u8 length = 0;
+ uacpi_u8 *cursor;
+ struct code_block *block;
+
+ block = code_block_array_last(&frame->code_blocks);
+ bytes_left = block->end - frame->code_offset;
+ if (bytes_left == 0)
+ return 0;
+
+ cursor = call_frame_cursor(frame);
+
+ op = AML_READ(cursor, length++);
+ if (op == UACPI_EXT_PREFIX) {
+ if (uacpi_unlikely(bytes_left < 2))
+ return 0;
+
+ op <<= 8;
+ op |= AML_READ(cursor, length++);
+ }
+
+ *out_op = op;
+ return length;
+}
+
+static uacpi_status get_op(struct execution_context *ctx)
+{
+ uacpi_aml_op op;
+ uacpi_u8 length;
+
+ length = peek_next_op(ctx->cur_frame, &op);
+ if (uacpi_unlikely(length == 0))
+ return UACPI_STATUS_AML_BAD_ENCODING;
+
+ ctx->cur_frame->code_offset += length;
+ g_uacpi_rt_ctx.opcodes_executed++;
+
+ ctx->cur_op = uacpi_get_op_spec(op);
+ if (uacpi_unlikely(ctx->cur_op->properties & UACPI_OP_PROPERTY_RESERVED)) {
+ uacpi_error(
+ "invalid opcode '%s' encountered in bytestream\n",
+ ctx->cur_op->name
+ );
+ return UACPI_STATUS_AML_INVALID_OPCODE;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_buffer(struct execution_context *ctx)
+{
+ struct package_length *pkg;
+ uacpi_u8 *src;
+ uacpi_object *dst, *declared_size;
+ uacpi_u32 buffer_size, init_size, aml_offset;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ aml_offset = item_array_at(&op_ctx->items, 2)->immediate;
+ src = ctx->cur_frame->method->code;
+ src += aml_offset;
+
+ pkg = &item_array_at(&op_ctx->items, 0)->pkg;
+ init_size = pkg->end - aml_offset;
+
+ // TODO: do package bounds checking at parse time
+ if (uacpi_unlikely(pkg->end > ctx->cur_frame->method->size))
+ return UACPI_STATUS_AML_BAD_ENCODING;
+
+ declared_size = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (uacpi_unlikely(declared_size->integer > 0xE0000000)) {
+ uacpi_error(
+ "buffer is too large (%"UACPI_PRIu64"), assuming corrupted "
+ "bytestream\n", UACPI_FMT64(declared_size->integer)
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ if (uacpi_unlikely(declared_size->integer == 0)) {
+ uacpi_error("attempted to create an empty buffer\n");
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ buffer_size = declared_size->integer;
+ if (uacpi_unlikely(init_size > buffer_size)) {
+ uacpi_error(
+ "too many buffer initializers: %u (size is %u)\n",
+ init_size, buffer_size
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ dst = item_array_at(&op_ctx->items, 3)->obj;
+ dst->buffer->data = uacpi_kernel_alloc(buffer_size);
+ if (uacpi_unlikely(dst->buffer->data == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ dst->buffer->size = buffer_size;
+
+ uacpi_memcpy_zerout(dst->buffer->data, src, buffer_size, init_size);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_string(struct execution_context *ctx)
+{
+ struct call_frame *frame = ctx->cur_frame;
+ uacpi_object *obj;
+
+ uacpi_char *string;
+ uacpi_size length, max_bytes;
+
+ obj = item_array_last(&ctx->cur_op_ctx->items)->obj;
+ string = call_frame_cursor(frame);
+
+ // TODO: sanitize string for valid UTF-8
+ max_bytes = call_frame_code_bytes_left(frame);
+ length = uacpi_strnlen(string, max_bytes);
+
+ if (uacpi_unlikely((length == max_bytes) || (string[length++] != 0x00)))
+ return UACPI_STATUS_AML_BAD_ENCODING;
+
+ obj->buffer->text = uacpi_kernel_alloc(length);
+ if (uacpi_unlikely(obj->buffer->text == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(obj->buffer->text, string, length);
+ obj->buffer->size = length;
+ frame->code_offset += length;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_package(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_package *package;
+ uacpi_u32 num_elements, num_defined_elements, i;
+
+ /*
+ * Layout of items here:
+ * [0] -> Package length, not interesting
+ * [1] -> Immediate or integer object, depending on PackageOp/VarPackageOp
+ * [2..N-2] -> AML pc+Package element pairs
+ * [N-1] -> The resulting package object that we're constructing
+ */
+ package = item_array_last(&op_ctx->items)->obj->package;
+
+ // 1. Detect how many elements we have, do sanity checking
+ if (op_ctx->op->code == UACPI_AML_OP_VarPackageOp) {
+ uacpi_object *var_num_elements;
+
+ var_num_elements = item_array_at(&op_ctx->items, 1)->obj;
+ if (uacpi_unlikely(var_num_elements->integer > 0xE0000000)) {
+ uacpi_error(
+ "package is too large (%"UACPI_PRIu64"), assuming "
+ "corrupted bytestream\n", UACPI_FMT64(var_num_elements->integer)
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+ num_elements = var_num_elements->integer;
+ } else {
+ num_elements = item_array_at(&op_ctx->items, 1)->immediate;
+ }
+
+ num_defined_elements = (item_array_size(&op_ctx->items) - 3) / 2;
+ if (uacpi_unlikely(num_defined_elements > num_elements)) {
+ uacpi_warn(
+ "too many package initializers: %u, truncating to %u\n",
+ num_defined_elements, num_elements
+ );
+
+ num_defined_elements = num_elements;
+ }
+
+ // 2. Create every object in the package, start as uninitialized
+ if (uacpi_unlikely(!uacpi_package_fill(package, num_elements,
+ UACPI_PREALLOC_OBJECTS_YES)))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ // 3. Go through every defined object and copy it into the package
+ for (i = 0; i < num_defined_elements; ++i) {
+ uacpi_size base_pkg_index;
+ uacpi_status ret;
+ struct item *item;
+ uacpi_object *obj;
+
+ base_pkg_index = (i * 2) + 2;
+ item = item_array_at(&op_ctx->items, base_pkg_index + 1);
+ obj = item->obj;
+
+ if (obj != UACPI_NULL && obj->type == UACPI_OBJECT_REFERENCE) {
+ /*
+ * For named objects we don't actually need the object itself, but
+ * simply the path to it. Often times objects referenced by the
+ * package are not defined until later so it's not possible to
+ * resolve them. For uniformity and to follow the behavior of NT,
+ * simply convert the name string to a path string object to be
+ * resolved later when actually needed.
+ */
+ if (obj->flags == UACPI_REFERENCE_KIND_NAMED) {
+ uacpi_object_unref(obj);
+ item->obj = UACPI_NULL;
+ obj = UACPI_NULL;
+ } else {
+ obj = uacpi_unwrap_internal_reference(obj);
+ }
+ }
+
+ if (obj == UACPI_NULL) {
+ uacpi_size length;
+ uacpi_char *path;
+
+ obj = uacpi_create_object(UACPI_OBJECT_STRING);
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ret = name_string_to_path(
+ ctx->cur_frame,
+ item_array_at(&op_ctx->items, base_pkg_index)->immediate,
+ &path, &length
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ obj->flags = UACPI_STRING_KIND_PATH;
+ obj->buffer->text = path;
+ obj->buffer->size = length;
+
+ item->obj = obj;
+ item->type = ITEM_OBJECT;
+ }
+
+ ret = uacpi_object_assign(package->objects[i], obj,
+ UACPI_ASSIGN_BEHAVIOR_DEEP_COPY);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_size sizeof_int(void)
+{
+ return g_uacpi_rt_ctx.is_rev1 ? 4 : 8;
+}
+
+static uacpi_status get_object_storage(
+ uacpi_object *obj, uacpi_data_view *out_buf, uacpi_bool include_null
+)
+{
+ switch (obj->type) {
+ case UACPI_OBJECT_INTEGER:
+ out_buf->length = sizeof_int();
+ out_buf->data = &obj->integer;
+ break;
+ case UACPI_OBJECT_STRING:
+ out_buf->length = obj->buffer->size;
+ if (out_buf->length && !include_null)
+ out_buf->length--;
+
+ out_buf->text = obj->buffer->text;
+ break;
+ case UACPI_OBJECT_BUFFER:
+ if (obj->buffer->size == 0) {
+ out_buf->bytes = UACPI_NULL;
+ out_buf->length = 0;
+ break;
+ }
+
+ out_buf->length = obj->buffer->size;
+ out_buf->bytes = obj->buffer->data;
+ break;
+ case UACPI_OBJECT_REFERENCE:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ default:
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_u8 *buffer_index_cursor(uacpi_buffer_index *buf_idx)
+{
+ uacpi_u8 *out_cursor;
+
+ out_cursor = buf_idx->buffer->data;
+ out_cursor += buf_idx->idx;
+
+ return out_cursor;
+}
+
+static void write_buffer_index(uacpi_buffer_index *buf_idx,
+ uacpi_data_view *src_buf)
+{
+ uacpi_memcpy_zerout(buffer_index_cursor(buf_idx), src_buf->bytes,
+ 1, src_buf->length);
+}
+
+/*
+ * The word "implicit cast" here is only because it's called that in
+ * the specification. In reality, we just copy one buffer to another
+ * because that's what NT does.
+ */
+static uacpi_status object_assign_with_implicit_cast(
+ uacpi_object *dst, uacpi_object *src, uacpi_data_view *wtr_response
+)
+{
+ uacpi_status ret;
+ uacpi_data_view src_buf;
+
+ ret = get_object_storage(src, &src_buf, UACPI_FALSE);
+ if (uacpi_unlikely_error(ret))
+ goto out_bad_cast;
+
+ switch (dst->type) {
+ case UACPI_OBJECT_INTEGER:
+ case UACPI_OBJECT_STRING:
+ case UACPI_OBJECT_BUFFER: {
+ uacpi_data_view dst_buf;
+
+ ret = get_object_storage(dst, &dst_buf, UACPI_FALSE);
+ if (uacpi_unlikely_error(ret))
+ goto out_bad_cast;
+
+ uacpi_memcpy_zerout(
+ dst_buf.bytes, src_buf.bytes, dst_buf.length, src_buf.length
+ );
+ break;
+ }
+
+ case UACPI_OBJECT_BUFFER_FIELD:
+ uacpi_write_buffer_field(
+ &dst->buffer_field, src_buf.bytes, src_buf.length
+ );
+ break;
+
+ case UACPI_OBJECT_FIELD_UNIT:
+ return uacpi_write_field_unit(
+ dst->field_unit, src_buf.bytes, src_buf.length,
+ wtr_response
+ );
+
+ case UACPI_OBJECT_BUFFER_INDEX:
+ write_buffer_index(&dst->buffer_index, &src_buf);
+ break;
+
+ default:
+ ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ goto out_bad_cast;
+ }
+
+ return ret;
+
+out_bad_cast:
+ uacpi_error(
+ "attempted to perform an invalid implicit cast (%s -> %s)\n",
+ uacpi_object_type_to_string(src->type),
+ uacpi_object_type_to_string(dst->type)
+ );
+ return ret;
+}
+
+enum argx_or_localx {
+ ARGX,
+ LOCALX,
+};
+
+static uacpi_status handle_arg_or_local(
+ struct execution_context *ctx,
+ uacpi_size idx, enum argx_or_localx type
+)
+{
+ uacpi_object **src;
+ struct item *dst;
+ enum uacpi_reference_kind kind;
+
+ if (type == ARGX) {
+ src = &ctx->cur_frame->args[idx];
+ kind = UACPI_REFERENCE_KIND_ARG;
+ } else {
+ src = &ctx->cur_frame->locals[idx];
+ kind = UACPI_REFERENCE_KIND_LOCAL;
+ }
+
+ if (*src == UACPI_NULL) {
+ uacpi_object *default_value;
+
+ default_value = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ if (uacpi_unlikely(default_value == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ *src = uacpi_create_internal_reference(kind, default_value);
+ if (uacpi_unlikely(*src == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_object_unref(default_value);
+ }
+
+ dst = item_array_last(&ctx->cur_op_ctx->items);
+ dst->obj = *src;
+ dst->type = ITEM_OBJECT;
+ uacpi_object_ref(dst->obj);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_local(struct execution_context *ctx)
+{
+ uacpi_size idx;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ idx = op_ctx->op->code - UACPI_AML_OP_Local0Op;
+ return handle_arg_or_local(ctx, idx, LOCALX);
+}
+
+static uacpi_status handle_arg(struct execution_context *ctx)
+{
+ uacpi_size idx;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ idx = op_ctx->op->code - UACPI_AML_OP_Arg0Op;
+ return handle_arg_or_local(ctx, idx, ARGX);
+}
+
+static uacpi_status handle_named_object(struct execution_context *ctx)
+{
+ struct uacpi_namespace_node *src;
+ struct item *dst;
+
+ src = item_array_at(&ctx->cur_op_ctx->items, 0)->node;
+ dst = item_array_at(&ctx->cur_op_ctx->items, 1);
+
+ dst->obj = src->object;
+ dst->type = ITEM_OBJECT;
+ uacpi_object_ref(dst->obj);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_create_alias(struct execution_context *ctx)
+{
+ uacpi_namespace_node *src, *dst;
+
+ src = item_array_at(&ctx->cur_op_ctx->items, 0)->node;
+ dst = item_array_at(&ctx->cur_op_ctx->items, 1)->node;
+
+ dst->object = src->object;
+ dst->flags = UACPI_NAMESPACE_NODE_FLAG_ALIAS;
+ uacpi_object_ref(dst->object);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_create_op_region(struct execution_context *ctx)
+{
+ uacpi_namespace_node *node;
+ uacpi_object *obj;
+ uacpi_operation_region *op_region;
+ uacpi_u64 region_end;
+
+ node = item_array_at(&ctx->cur_op_ctx->items, 0)->node;
+ obj = item_array_at(&ctx->cur_op_ctx->items, 4)->obj;
+ op_region = obj->op_region;
+
+ op_region->space = item_array_at(&ctx->cur_op_ctx->items, 1)->immediate;
+ op_region->offset = item_array_at(&ctx->cur_op_ctx->items, 2)->obj->integer;
+ op_region->length = item_array_at(&ctx->cur_op_ctx->items, 3)->obj->integer;
+ region_end = op_region->offset + op_region->length;
+
+ if (uacpi_unlikely(op_region->length == 0)) {
+ // Don't abort here, as long as it's never accessed we don't care
+ uacpi_warn("unusable/empty operation region %.4s\n", node->name.text);
+ } else if (uacpi_unlikely(op_region->offset > region_end)) {
+ uacpi_error(
+ "invalid operation region %.4s bounds: offset=0x%"UACPI_PRIX64
+ " length=0x%"UACPI_PRIX64"\n", node->name.text,
+ UACPI_FMT64(op_region->offset), UACPI_FMT64(op_region->length)
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ if (op_region->space == UACPI_ADDRESS_SPACE_PCC && op_region->offset > 255) {
+ uacpi_warn(
+ "invalid PCC operation region %.4s subspace %"UACPI_PRIX64"\n",
+ node->name.text, UACPI_FMT64(op_region->offset)
+ );
+ }
+
+ node->object = uacpi_create_internal_reference(
+ UACPI_REFERENCE_KIND_NAMED, obj
+ );
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_initialize_opregion_node(node);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status table_id_error(
+ const uacpi_char *opcode, const uacpi_char *arg,
+ uacpi_buffer *str
+)
+{
+ uacpi_error("%s: invalid %s '%s'\n", opcode, arg, str->text);
+ return UACPI_STATUS_AML_BAD_ENCODING;
+}
+
+static void report_table_id_find_error(
+ const uacpi_char *opcode, struct uacpi_table_identifiers *id,
+ uacpi_status ret
+)
+{
+ uacpi_error(
+ "%s: unable to find table '%.4s' (OEM ID '%.6s', "
+ "OEM Table ID '%.8s'): %s\n",
+ opcode, id->signature.text, id->oemid, id->oem_table_id,
+ uacpi_status_to_string(ret)
+ );
+}
+
+static uacpi_status build_table_id(
+ const uacpi_char *opcode,
+ struct uacpi_table_identifiers *out_id,
+ uacpi_buffer *signature, uacpi_buffer *oem_id,
+ uacpi_buffer *oem_table_id
+)
+{
+ if (uacpi_unlikely(signature->size != (sizeof(uacpi_object_name) + 1)))
+ return table_id_error(opcode, "SignatureString", signature);
+
+ uacpi_memcpy(out_id->signature.text, signature->text,
+ sizeof(uacpi_object_name));
+
+ if (uacpi_unlikely(oem_id->size > (sizeof(out_id->oemid) + 1)))
+ return table_id_error(opcode, "OemIDString", oem_id);
+
+ uacpi_memcpy_zerout(
+ out_id->oemid, oem_id->text,
+ sizeof(out_id->oemid), oem_id->size ? oem_id->size - 1 : 0
+ );
+
+ if (uacpi_unlikely(oem_table_id->size > (sizeof(out_id->oem_table_id) + 1)))
+ return table_id_error(opcode, "OemTableIDString", oem_table_id);
+
+ uacpi_memcpy_zerout(
+ out_id->oem_table_id, oem_table_id->text,
+ sizeof(out_id->oem_table_id),
+ oem_table_id->size ? oem_table_id->size - 1 : 0
+ );
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_create_data_region(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct item_array *items = &ctx->cur_op_ctx->items;
+ struct uacpi_table_identifiers table_id;
+ uacpi_table table;
+ uacpi_namespace_node *node;
+ uacpi_object *obj;
+ uacpi_operation_region *op_region;
+
+ node = item_array_at(items, 0)->node;
+
+ ret = build_table_id(
+ "DataTableRegion", &table_id,
+ item_array_at(items, 1)->obj->buffer,
+ item_array_at(items, 2)->obj->buffer,
+ item_array_at(items, 3)->obj->buffer
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = uacpi_table_find(&table_id, &table);
+ if (uacpi_unlikely_error(ret)) {
+ report_table_id_find_error("DataTableRegion", &table_id, ret);
+ return ret;
+ }
+
+ obj = item_array_at(items, 4)->obj;
+ op_region = obj->op_region;
+ op_region->space = UACPI_ADDRESS_SPACE_TABLE_DATA;
+ op_region->offset = table.virt_addr;
+ op_region->length = table.hdr->length;
+ op_region->table_idx = table.index;
+
+ node->object = uacpi_create_internal_reference(
+ UACPI_REFERENCE_KIND_NAMED, obj
+ );
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_initialize_opregion_node(node);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_bool is_dynamic_table_load(enum uacpi_table_load_cause cause)
+{
+ return cause != UACPI_TABLE_LOAD_CAUSE_INIT;
+}
+
+static void prepare_table_load(
+ void *ptr, enum uacpi_table_load_cause cause, uacpi_control_method *in_method
+)
+{
+ struct acpi_dsdt *dsdt = ptr;
+ enum uacpi_log_level log_level = UACPI_LOG_TRACE;
+ const uacpi_char *log_prefix = "load of";
+
+ if (is_dynamic_table_load(cause)) {
+ log_prefix = cause == UACPI_TABLE_LOAD_CAUSE_HOST ?
+ "host-invoked load of" : "dynamic load of";
+ log_level = UACPI_LOG_INFO;
+ }
+
+ uacpi_log_lvl(
+ log_level, "%s "UACPI_PRI_TBL_HDR"\n",
+ log_prefix, UACPI_FMT_TBL_HDR(&dsdt->hdr)
+ );
+
+ in_method->code = dsdt->definition_block;
+ in_method->size = dsdt->hdr.length - sizeof(dsdt->hdr);
+ in_method->named_objects_persist = UACPI_TRUE;
+}
+
+static uacpi_status do_load_table(
+ uacpi_namespace_node *parent, struct acpi_sdt_hdr *tbl,
+ enum uacpi_table_load_cause cause
+)
+{
+ struct uacpi_control_method method = { 0 };
+ uacpi_status ret;
+
+ prepare_table_load(tbl, cause, &method);
+
+ ret = uacpi_execute_control_method(parent, &method, UACPI_NULL, UACPI_NULL);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (is_dynamic_table_load(cause))
+ uacpi_events_match_post_dynamic_table_load();
+
+ return ret;
+}
+
+static uacpi_status handle_load_table(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct item_array *items = &ctx->cur_op_ctx->items;
+ struct item *root_node_item;
+ struct uacpi_table_identifiers table_id;
+ uacpi_table table;
+ uacpi_buffer *root_path, *param_path;
+ uacpi_control_method *method;
+ uacpi_namespace_node *root_node, *param_node = UACPI_NULL;
+
+ /*
+ * If we already have the last true/false object loaded, this is a second
+ * invocation of this handler. For the second invocation we want to detect
+ * new AML GPE handlers that might've been loaded, as well as potentially
+ * remove the target.
+ */
+ if (item_array_size(items) == 12) {
+ uacpi_size idx;
+ struct uacpi_table tmp_table = { 0 };
+
+ idx = item_array_at(items, 2)->immediate;
+ tmp_table.index = idx;
+ uacpi_table_unref(&tmp_table);
+
+ /*
+ * If this load failed, remove the target that was provided via
+ * ParameterPathString so that it doesn't get stored to.
+ */
+ if (uacpi_unlikely(item_array_at(items, 11)->obj->integer == 0)) {
+ uacpi_object *target;
+
+ target = item_array_at(items, 3)->obj;
+ if (target != UACPI_NULL) {
+ uacpi_object_unref(target);
+ item_array_at(items, 3)->obj = UACPI_NULL;
+ }
+
+ return UACPI_STATUS_OK;
+ }
+
+ uacpi_events_match_post_dynamic_table_load();
+ return UACPI_STATUS_OK;
+ }
+
+ ret = build_table_id(
+ "LoadTable", &table_id,
+ item_array_at(items, 5)->obj->buffer,
+ item_array_at(items, 6)->obj->buffer,
+ item_array_at(items, 7)->obj->buffer
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ root_path = item_array_at(items, 8)->obj->buffer;
+ param_path = item_array_at(items, 9)->obj->buffer;
+ root_node_item = item_array_at(items, 0);
+
+ if (root_path->size > 1) {
+ ret = uacpi_namespace_node_resolve(
+ ctx->cur_frame->cur_scope, root_path->text, UACPI_SHOULD_LOCK_NO,
+ UACPI_MAY_SEARCH_ABOVE_PARENT_YES, UACPI_PERMANENT_ONLY_NO,
+ &root_node
+ );
+ if (uacpi_unlikely_error(ret)) {
+ table_id_error("LoadTable", "RootPathString", root_path);
+ if (ret == UACPI_STATUS_NOT_FOUND)
+ ret = UACPI_STATUS_AML_UNDEFINED_REFERENCE;
+ return ret;
+ }
+ } else {
+ root_node = uacpi_namespace_root();
+ }
+
+ root_node_item->node = root_node;
+ root_node_item->type = ITEM_NAMESPACE_NODE;
+ uacpi_shareable_ref(root_node);
+
+ if (param_path->size > 1) {
+ struct item *param_item;
+
+ ret = uacpi_namespace_node_resolve(
+ root_node, param_path->text, UACPI_SHOULD_LOCK_NO,
+ UACPI_MAY_SEARCH_ABOVE_PARENT_YES, UACPI_PERMANENT_ONLY_NO,
+ &param_node
+ );
+ if (uacpi_unlikely_error(ret)) {
+ table_id_error("LoadTable", "ParameterPathString", root_path);
+ if (ret == UACPI_STATUS_NOT_FOUND)
+ ret = UACPI_STATUS_AML_UNDEFINED_REFERENCE;
+ return ret;
+ }
+
+ param_item = item_array_at(items, 3);
+ param_item->obj = param_node->object;
+ uacpi_object_ref(param_item->obj);
+ param_item->type = ITEM_OBJECT;
+ }
+
+ ret = uacpi_table_find(&table_id, &table);
+ if (uacpi_unlikely_error(ret)) {
+ report_table_id_find_error("LoadTable", &table_id, ret);
+ return ret;
+ }
+ uacpi_table_mark_as_loaded(table.index);
+
+ item_array_at(items, 2)->immediate = table.index;
+ method = item_array_at(items, 1)->obj->method;
+ prepare_table_load(table.hdr, UACPI_TABLE_LOAD_CAUSE_LOAD_TABLE_OP, method);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_load(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct item_array *items = &ctx->cur_op_ctx->items;
+ uacpi_table table;
+ uacpi_control_method *method;
+ uacpi_object *src;
+ struct acpi_sdt_hdr *src_table = UACPI_NULL;
+ void *table_buffer;
+ uacpi_size declared_size;
+ uacpi_bool unmap_src = UACPI_FALSE;
+
+ /*
+ * If we already have the last true/false object loaded, this is a second
+ * invocation of this handler. For the second invocation we simply want to
+ * detect new AML GPE handlers that might've been loaded.
+ * We do this only if table load was successful though.
+ */
+ if (item_array_size(items) == 5) {
+ if (item_array_at(items, 4)->obj->integer != 0)
+ uacpi_events_match_post_dynamic_table_load();
+ return UACPI_STATUS_OK;
+ }
+
+ src = item_array_at(items, 2)->obj;
+
+ switch (src->type) {
+ case UACPI_OBJECT_OPERATION_REGION: {
+ uacpi_operation_region *op_region;
+
+ op_region = src->op_region;
+ if (uacpi_unlikely(
+ op_region->space != UACPI_ADDRESS_SPACE_SYSTEM_MEMORY
+ )) {
+ uacpi_error("Load: operation region is not SystemMemory\n");
+ goto error_out;
+ }
+
+ if (uacpi_unlikely(op_region->length < sizeof(struct acpi_sdt_hdr))) {
+ uacpi_error(
+ "Load: operation region is too small: %"UACPI_PRIu64"\n",
+ UACPI_FMT64(op_region->length)
+ );
+ goto error_out;
+ }
+
+ src_table = uacpi_kernel_map(op_region->offset, op_region->length);
+ if (uacpi_unlikely(src_table == UACPI_NULL)) {
+ uacpi_error(
+ "Load: failed to map operation region "
+ "0x%016"UACPI_PRIX64" -> 0x%016"UACPI_PRIX64"\n",
+ UACPI_FMT64(op_region->offset),
+ UACPI_FMT64(op_region->offset + op_region->length)
+ );
+ goto error_out;
+ }
+
+ unmap_src = UACPI_TRUE;
+ declared_size = op_region->length;
+ break;
+ }
+
+ case UACPI_OBJECT_BUFFER: {
+ uacpi_buffer *buffer;
+
+ buffer = src->buffer;
+ if (buffer->size < sizeof(struct acpi_sdt_hdr)) {
+ uacpi_error(
+ "Load: buffer is too small: %zu\n",
+ buffer->size
+ );
+ goto error_out;
+ }
+
+ src_table = buffer->data;
+ declared_size = buffer->size;
+ break;
+ }
+
+ default:
+ uacpi_error(
+ "Load: invalid argument '%s', expected "
+ "Buffer/Field/OperationRegion\n",
+ uacpi_object_type_to_string(src->type)
+ );
+ goto error_out;
+ }
+
+ if (uacpi_unlikely(src_table->length > declared_size)) {
+ uacpi_error(
+ "Load: table size %u is larger than the declared size %zu\n",
+ src_table->length, declared_size
+ );
+ goto error_out;
+ }
+
+ if (uacpi_unlikely(src_table->length < sizeof(struct acpi_sdt_hdr))) {
+ uacpi_error("Load: table size %u is too small\n", src_table->length);
+ goto error_out;
+ }
+
+ table_buffer = uacpi_kernel_alloc(src_table->length);
+ if (uacpi_unlikely(table_buffer == UACPI_NULL))
+ goto error_out;
+
+ uacpi_memcpy(table_buffer, src_table, src_table->length);
+
+ if (unmap_src) {
+ uacpi_kernel_unmap(src_table, declared_size);
+ unmap_src = UACPI_FALSE;
+ }
+
+ ret = uacpi_table_install_with_origin(
+ table_buffer, UACPI_TABLE_ORIGIN_FIRMWARE_VIRTUAL, &table
+ );
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_free(table_buffer, src_table->length);
+
+ if (ret != UACPI_STATUS_OVERRIDDEN)
+ goto error_out;
+ }
+ uacpi_table_mark_as_loaded(table.index);
+
+ item_array_at(items, 0)->node = uacpi_namespace_root();
+
+ method = item_array_at(items, 1)->obj->method;
+ prepare_table_load(table.ptr, UACPI_TABLE_LOAD_CAUSE_LOAD_OP, method);
+
+ return UACPI_STATUS_OK;
+
+error_out:
+ if (unmap_src && src_table)
+ uacpi_kernel_unmap(src_table, declared_size);
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_execute_table(void *tbl, enum uacpi_table_load_cause cause)
+{
+ uacpi_status ret;
+
+ ret = uacpi_namespace_write_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = do_load_table(uacpi_namespace_root(), tbl, cause);
+
+ uacpi_namespace_write_unlock();
+ return ret;
+}
+
+static uacpi_u32 get_field_length(struct item *item)
+{
+ struct package_length *pkg = &item->pkg;
+ return pkg->end - pkg->begin;
+}
+
+struct field_specific_data {
+ uacpi_namespace_node *region;
+ struct uacpi_field_unit *field0;
+ struct uacpi_field_unit *field1;
+ uacpi_u64 value;
+};
+
+static uacpi_status ensure_is_a_field_unit(uacpi_namespace_node *node,
+ uacpi_field_unit **out_field)
+{
+ uacpi_object *obj;
+
+ obj = uacpi_namespace_node_get_object(node);
+ if (obj->type != UACPI_OBJECT_FIELD_UNIT) {
+ uacpi_error(
+ "invalid argument: '%.4s' is not a field unit (%s)\n",
+ node->name.text, uacpi_object_type_to_string(obj->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ *out_field = obj->field_unit;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status ensure_is_an_op_region(uacpi_namespace_node *node,
+ uacpi_namespace_node **out_node)
+{
+ uacpi_object *obj;
+
+ obj = uacpi_namespace_node_get_object(node);
+ if (obj->type != UACPI_OBJECT_OPERATION_REGION) {
+ uacpi_error(
+ "invalid argument: '%.4s' is not an operation region (%s)\n",
+ node->name.text, uacpi_object_type_to_string(obj->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ *out_node = node;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_create_field(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_namespace_node *node;
+ uacpi_object *obj, *connection_obj = UACPI_NULL;
+ struct field_specific_data field_data = { 0 };
+ uacpi_size i = 1, bit_offset = 0;
+ uacpi_u32 length, pin_offset = 0;
+
+ uacpi_u8 raw_value, access_type, lock_rule, update_rule;
+ uacpi_u8 access_attrib = 0, access_length = 0;
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_FieldOp:
+ node = item_array_at(&op_ctx->items, i++)->node;
+ ret = ensure_is_an_op_region(node, &field_data.region);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ break;
+
+ case UACPI_AML_OP_BankFieldOp:
+ node = item_array_at(&op_ctx->items, i++)->node;
+ ret = ensure_is_an_op_region(node, &field_data.region);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ node = item_array_at(&op_ctx->items, i++)->node;
+ ret = ensure_is_a_field_unit(node, &field_data.field0);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ field_data.value = item_array_at(&op_ctx->items, i++)->obj->integer;
+ break;
+
+ case UACPI_AML_OP_IndexFieldOp:
+ node = item_array_at(&op_ctx->items, i++)->node;
+ ret = ensure_is_a_field_unit(node, &field_data.field0);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ node = item_array_at(&op_ctx->items, i++)->node;
+ ret = ensure_is_a_field_unit(node, &field_data.field1);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ break;
+
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ /*
+ * ByteData
+ * bit 0-3: AccessType
+ * 0 AnyAcc
+ * 1 ByteAcc
+ * 2 WordAcc
+ * 3 DWordAcc
+ * 4 QWordAcc
+ * 5 BufferAcc
+ * 6 Reserved
+ * 7-15 Reserved
+ * bit 4: LockRule
+ * 0 NoLock
+ * 1 Lock
+ * bit 5-6: UpdateRule
+ * 0 Preserve
+ * 1 WriteAsOnes
+ * 2 WriteAsZeros
+ * bit 7: Reserved (must be 0)
+ */
+ raw_value = item_array_at(&op_ctx->items, i++)->immediate;
+ access_type = (raw_value >> 0) & 0xF;
+ lock_rule = (raw_value >> 4) & 0x1;
+ update_rule = (raw_value >> 5) & 0x3;
+
+ while (i < item_array_size(&op_ctx->items)) {
+ struct item *item;
+ item = item_array_at(&op_ctx->items, i++);
+
+ // An actual field object
+ if (item->type == ITEM_NAMESPACE_NODE) {
+ uacpi_field_unit *field;
+
+ length = get_field_length(item_array_at(&op_ctx->items, i++));
+ node = item->node;
+
+ obj = item_array_at(&op_ctx->items, i++)->obj;
+ field = obj->field_unit;
+
+ field->update_rule = update_rule;
+ field->lock_rule = lock_rule;
+ field->attributes = access_attrib;
+ field->access_length = access_length;
+
+ /*
+ * 0 AnyAcc
+ * 1 ByteAcc
+ * 2 WordAcc
+ * 3 DWordAcc
+ * 4 QWordAcc
+ * 5 BufferAcc
+ * 6 Reserved
+ * 7-15 Reserved
+ */
+ switch (access_type) {
+ case 0:
+ // TODO: optimize to calculate best access strategy
+ UACPI_FALLTHROUGH;
+ case 1:
+ case 5:
+ field->access_width_bytes = 1;
+ break;
+ case 2:
+ field->access_width_bytes = 2;
+ break;
+ case 3:
+ field->access_width_bytes = 4;
+ break;
+ case 4:
+ field->access_width_bytes = 8;
+ break;
+ default:
+ uacpi_error("invalid field '%.4s' access type %d\n",
+ node->name.text, access_type);
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ field->bit_length = length;
+ field->pin_offset = pin_offset;
+
+ // FIXME: overflow, OOB, etc checks
+ field->byte_offset = UACPI_ALIGN_DOWN(
+ bit_offset / 8,
+ field->access_width_bytes,
+ uacpi_u32
+ );
+
+ field->bit_offset_within_first_byte = bit_offset;
+ field->bit_offset_within_first_byte =
+ bit_offset & ((field->access_width_bytes * 8) - 1);
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_FieldOp:
+ field->region = field_data.region;
+ uacpi_shareable_ref(field->region);
+
+ field->kind = UACPI_FIELD_UNIT_KIND_NORMAL;
+ break;
+
+ case UACPI_AML_OP_BankFieldOp:
+ field->bank_region = field_data.region;
+ uacpi_shareable_ref(field->bank_region);
+
+ field->bank_selection = field_data.field0;
+ uacpi_shareable_ref(field->bank_selection);
+
+ field->bank_value = field_data.value;
+ field->kind = UACPI_FIELD_UNIT_KIND_BANK;
+ break;
+
+ case UACPI_AML_OP_IndexFieldOp:
+ field->index = field_data.field0;
+ uacpi_shareable_ref(field->index);
+
+ field->data = field_data.field1;
+ uacpi_shareable_ref(field->data);
+
+ field->kind = UACPI_FIELD_UNIT_KIND_INDEX;
+ break;
+
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ field->connection = connection_obj;
+ if (field->connection)
+ uacpi_object_ref(field->connection);
+
+ node->object = uacpi_create_internal_reference(
+ UACPI_REFERENCE_KIND_NAMED, obj
+ );
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ret = do_install_node_item(ctx->cur_frame, item);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ bit_offset += length;
+ pin_offset += length;
+ continue;
+ }
+
+ // All other stuff
+ switch ((int)item->immediate) {
+ // ReservedField := 0x00 PkgLength
+ case 0x00:
+ length = get_field_length(item_array_at(&op_ctx->items, i++));
+ bit_offset += length;
+ pin_offset += length;
+ break;
+
+ // AccessField := 0x01 AccessType AccessAttrib
+ // ExtendedAccessField := 0x03 AccessType ExtendedAccessAttrib AccessLength
+ case 0x01:
+ case 0x03:
+ raw_value = item_array_at(&op_ctx->items, i++)->immediate;
+
+ access_type = raw_value & 0xF;
+ access_attrib = (raw_value >> 6) & 0x3;
+
+ raw_value = item_array_at(&op_ctx->items, i++)->immediate;
+
+ /*
+ * Bits 7:6
+ * 0 = AccessAttrib = Normal Access Attributes
+ * 1 = AccessAttrib = AttribBytes (x)
+ * 2 = AccessAttrib = AttribRawBytes (x)
+ * 3 = AccessAttrib = AttribRawProcessBytes (x)
+ * x is encoded as bits 0:7 of the AccessAttrib byte.
+ */
+ if (access_attrib) {
+ switch (access_attrib) {
+ case 1:
+ access_attrib = UACPI_ACCESS_ATTRIBUTE_BYTES;
+ break;
+ case 2:
+ access_attrib = UACPI_ACCESS_ATTRIBUTE_RAW_BYTES;
+ break;
+ case 3:
+ access_attrib = UACPI_ACCESS_ATTRIBUTE_RAW_PROCESS_BYTES;
+ break;
+ }
+
+ access_length = raw_value;
+ } else { // Normal access attributes
+ access_attrib = raw_value;
+ }
+
+ if (item->immediate == 3)
+ access_length = item_array_at(&op_ctx->items, i++)->immediate;
+ break;
+
+ // ConnectField := <0x02 NameString> | <0x02 BufferData>
+ case 0x02:
+ connection_obj = item_array_at(&op_ctx->items, i++)->obj;
+ pin_offset = 0;
+ break;
+
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static void truncate_number_if_needed(uacpi_object *obj)
+{
+ if (!g_uacpi_rt_ctx.is_rev1)
+ return;
+
+ obj->integer &= 0xFFFFFFFF;
+}
+
+static uacpi_u64 ones(void)
+{
+ return g_uacpi_rt_ctx.is_rev1 ? 0xFFFFFFFF : 0xFFFFFFFFFFFFFFFF;
+}
+
+static uacpi_status method_get_ret_target(struct execution_context *ctx,
+ uacpi_object **out_operand)
+{
+ uacpi_size depth;
+
+ // Check if we're targeting the previous call frame
+ depth = call_frame_array_size(&ctx->call_stack);
+ if (depth > 1) {
+ struct op_context *op_ctx;
+ struct call_frame *frame;
+
+ frame = call_frame_array_at(&ctx->call_stack, depth - 2);
+ depth = op_context_array_size(&frame->pending_ops);
+
+ // Ok, no one wants the return value at call site. Discard it.
+ if (!depth) {
+ *out_operand = UACPI_NULL;
+ return UACPI_STATUS_OK;
+ }
+
+ op_ctx = op_context_array_at(&frame->pending_ops, depth - 1);
+
+ /*
+ * Prevent the table being dynamically loaded from attempting to return
+ * a value to the caller. This is unlikely to be ever encountered in the
+ * wild, but we should still guard against the possibility.
+ */
+ if (uacpi_unlikely(op_ctx->op->code == UACPI_AML_OP_LoadOp ||
+ op_ctx->op->code == UACPI_AML_OP_LoadTableOp)) {
+ *out_operand = UACPI_NULL;
+ return UACPI_STATUS_OK;
+ }
+
+ *out_operand = item_array_last(&op_ctx->items)->obj;
+ return UACPI_STATUS_OK;
+ }
+
+ return UACPI_STATUS_NOT_FOUND;
+}
+
+static uacpi_status method_get_ret_object(struct execution_context *ctx,
+ uacpi_object **out_obj)
+{
+ uacpi_status ret;
+
+ ret = method_get_ret_target(ctx, out_obj);
+ if (ret == UACPI_STATUS_NOT_FOUND) {
+ *out_obj = ctx->ret;
+ return UACPI_STATUS_OK;
+ }
+ if (ret != UACPI_STATUS_OK || *out_obj == UACPI_NULL)
+ return ret;
+
+ *out_obj = uacpi_unwrap_internal_reference(*out_obj);
+ return UACPI_STATUS_OK;
+}
+
+static struct code_block *find_last_block(struct code_block_array *blocks,
+ enum code_block_type type)
+{
+ uacpi_size i;
+
+ i = code_block_array_size(blocks);
+ while (i-- > 0) {
+ struct code_block *block;
+
+ block = code_block_array_at(blocks, i);
+ if (block->type == type)
+ return block;
+ }
+
+ return UACPI_NULL;
+}
+
+static void update_scope(struct call_frame *frame)
+{
+ struct code_block *block;
+
+ block = find_last_block(&frame->code_blocks, CODE_BLOCK_SCOPE);
+ if (block == UACPI_NULL) {
+ frame->cur_scope = uacpi_namespace_root();
+ return;
+ }
+
+ frame->cur_scope = block->node;
+}
+
+static uacpi_status begin_block_execution(struct execution_context *ctx)
+{
+ struct call_frame *cur_frame = ctx->cur_frame;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ struct package_length *pkg;
+ struct code_block *block;
+
+ block = code_block_array_alloc(&cur_frame->code_blocks);
+ if (uacpi_unlikely(block == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ pkg = &item_array_at(&op_ctx->items, 0)->pkg;
+
+ // Disarm the tracked package so that we don't skip the Scope
+ op_ctx->tracked_pkg_idx = 0;
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_IfOp:
+ block->type = CODE_BLOCK_IF;
+ break;
+ case UACPI_AML_OP_ElseOp:
+ block->type = CODE_BLOCK_ELSE;
+ break;
+ case UACPI_AML_OP_WhileOp:
+ block->type = CODE_BLOCK_WHILE;
+
+ if (pkg->begin == cur_frame->prev_while_code_offset) {
+ uacpi_u64 cur_ticks;
+
+ cur_ticks = uacpi_kernel_get_nanoseconds_since_boot();
+
+ if (uacpi_unlikely(cur_ticks > block->expiration_point)) {
+ uacpi_error("loop time out after running for %u seconds\n",
+ g_uacpi_rt_ctx.loop_timeout_seconds);
+ code_block_array_pop(&cur_frame->code_blocks);
+ return UACPI_STATUS_AML_LOOP_TIMEOUT;
+ }
+
+ block->expiration_point = cur_frame->prev_while_expiration;
+ } else {
+ /*
+ * Calculate the expiration point for this loop.
+ * If a loop is executed past this point, it will get aborted.
+ */
+ block->expiration_point = uacpi_kernel_get_nanoseconds_since_boot();
+ block->expiration_point +=
+ g_uacpi_rt_ctx.loop_timeout_seconds * UACPI_NANOSECONDS_PER_SEC;
+ }
+ break;
+ case UACPI_AML_OP_ScopeOp:
+ case UACPI_AML_OP_DeviceOp:
+ case UACPI_AML_OP_ProcessorOp:
+ case UACPI_AML_OP_PowerResOp:
+ case UACPI_AML_OP_ThermalZoneOp:
+ block->type = CODE_BLOCK_SCOPE;
+ block->node = item_array_at(&op_ctx->items, 1)->node;
+ break;
+ default:
+ code_block_array_pop(&cur_frame->code_blocks);
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ // -1 because we want to re-evaluate at the start of the op next time
+ block->begin = pkg->begin - 1;
+ block->end = pkg->end;
+ ctx->cur_block = block;
+
+ cur_frame->last_while = find_last_block(&cur_frame->code_blocks,
+ CODE_BLOCK_WHILE);
+ update_scope(cur_frame);
+ return UACPI_STATUS_OK;
+}
+
+static void frame_reset_post_end_block(struct execution_context *ctx,
+ enum code_block_type type)
+{
+ struct call_frame *frame = ctx->cur_frame;
+
+ if (type == CODE_BLOCK_WHILE) {
+ struct code_block *block = ctx->cur_block;
+
+ // + 1 here to skip the WhileOp and get to the PkgLength
+ frame->prev_while_code_offset = block->begin + 1;
+ frame->prev_while_expiration = block->expiration_point;
+ }
+
+ code_block_array_pop(&frame->code_blocks);
+ ctx->cur_block = code_block_array_last(&frame->code_blocks);
+
+ if (type == CODE_BLOCK_WHILE) {
+ frame->last_while = find_last_block(&frame->code_blocks, type);
+ } else if (type == CODE_BLOCK_SCOPE) {
+ update_scope(frame);
+ }
+}
+
+static void debug_store_no_recurse(const uacpi_char *prefix, uacpi_object *src)
+{
+ switch (src->type) {
+ case UACPI_OBJECT_UNINITIALIZED:
+ uacpi_trace("%s Uninitialized\n", prefix);
+ break;
+ case UACPI_OBJECT_STRING:
+ uacpi_trace("%s String => \"%s\"\n", prefix, src->buffer->text);
+ break;
+ case UACPI_OBJECT_INTEGER:
+ if (g_uacpi_rt_ctx.is_rev1) {
+ uacpi_trace(
+ "%s Integer => 0x%08X\n", prefix, (uacpi_u32)src->integer
+ );
+ } else {
+ uacpi_trace(
+ "%s Integer => 0x%016"UACPI_PRIX64"\n", prefix,
+ UACPI_FMT64(src->integer)
+ );
+ }
+ break;
+ case UACPI_OBJECT_REFERENCE:
+ uacpi_trace("%s Reference @%p => %p\n", prefix, src, src->inner_object);
+ break;
+ case UACPI_OBJECT_PACKAGE:
+ uacpi_trace(
+ "%s Package @%p (%p) (%zu elements)\n",
+ prefix, src, src->package, src->package->count
+ );
+ break;
+ case UACPI_OBJECT_BUFFER:
+ uacpi_trace(
+ "%s Buffer @%p (%p) (%zu bytes)\n",
+ prefix, src, src->buffer, src->buffer->size
+ );
+ break;
+ case UACPI_OBJECT_OPERATION_REGION:
+ uacpi_trace(
+ "%s OperationRegion (ASID %d) 0x%016"UACPI_PRIX64
+ " -> 0x%016"UACPI_PRIX64"\n", prefix,
+ src->op_region->space, UACPI_FMT64(src->op_region->offset),
+ UACPI_FMT64(src->op_region->offset + src->op_region->length)
+ );
+ break;
+ case UACPI_OBJECT_POWER_RESOURCE:
+ uacpi_trace(
+ "%s Power Resource %d %d\n",
+ prefix, src->power_resource.system_level,
+ src->power_resource.resource_order
+ );
+ break;
+ case UACPI_OBJECT_PROCESSOR:
+ uacpi_trace(
+ "%s Processor[%d] 0x%08X (%d)\n",
+ prefix, src->processor->id, src->processor->block_address,
+ src->processor->block_length
+ );
+ break;
+ case UACPI_OBJECT_BUFFER_INDEX:
+ uacpi_trace(
+ "%s Buffer Index %p[%zu] => 0x%02X\n",
+ prefix, src->buffer_index.buffer->data, src->buffer_index.idx,
+ *buffer_index_cursor(&src->buffer_index)
+ );
+ break;
+ case UACPI_OBJECT_MUTEX:
+ uacpi_trace(
+ "%s Mutex @%p (%p => %p) sync level %d\n",
+ prefix, src, src->mutex, src->mutex->handle,
+ src->mutex->sync_level
+ );
+ break;
+ case UACPI_OBJECT_METHOD:
+ uacpi_trace("%s Method @%p (%p)\n", prefix, src, src->method);
+ break;
+ default:
+ uacpi_trace(
+ "%s %s @%p\n",
+ prefix, uacpi_object_type_to_string(src->type), src
+ );
+ }
+}
+
+static uacpi_status debug_store(uacpi_object *src)
+{
+ /*
+ * Don't bother running the body if current log level is not set to trace.
+ * All DebugOp logging is done as TRACE exclusively.
+ */
+ if (!uacpi_should_log(UACPI_LOG_TRACE))
+ return UACPI_STATUS_OK;
+
+ src = uacpi_unwrap_internal_reference(src);
+
+ debug_store_no_recurse("[AML DEBUG]", src);
+
+ if (src->type == UACPI_OBJECT_PACKAGE) {
+ uacpi_package *pkg = src->package;
+ uacpi_size i;
+
+ for (i = 0; i < pkg->count; ++i) {
+ uacpi_object *obj = pkg->objects[i];
+ if (obj->type == UACPI_OBJECT_REFERENCE &&
+ obj->flags == UACPI_REFERENCE_KIND_PKG_INDEX)
+ obj = obj->inner_object;
+
+ debug_store_no_recurse("Element:", obj);
+ }
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+/*
+ * NOTE: this function returns the parent object
+ */
+static uacpi_object *reference_unwind(uacpi_object *obj)
+{
+ uacpi_object *parent = obj;
+
+ while (obj) {
+ if (obj->type != UACPI_OBJECT_REFERENCE)
+ return parent;
+
+ parent = obj;
+ obj = parent->inner_object;
+ }
+
+ // This should be unreachable
+ return UACPI_NULL;
+}
+
+static uacpi_iteration_decision opregion_try_detach_from_parent(
+ void *user, uacpi_namespace_node *node, uacpi_u32 node_depth
+)
+{
+ uacpi_object *target_object = user;
+ UACPI_UNUSED(node_depth);
+
+ if (node->object == target_object) {
+ uacpi_opregion_uninstall_handler(node);
+ return UACPI_ITERATION_DECISION_BREAK;
+ }
+
+ return UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+static void object_replace_child(uacpi_object *parent, uacpi_object *new_child)
+{
+ if (parent->flags == UACPI_REFERENCE_KIND_NAMED &&
+ uacpi_object_is(parent->inner_object, UACPI_OBJECT_OPERATION_REGION)) {
+
+ /*
+ * We're doing a CopyObject or similar to a namespace node that is an
+ * operation region. Try to find the parent node and manually detach
+ * the handler.
+ */
+ opregion_try_detach_from_parent(parent, uacpi_namespace_root(), 0);
+ uacpi_namespace_do_for_each_child(
+ uacpi_namespace_root(), opregion_try_detach_from_parent, UACPI_NULL,
+ UACPI_OBJECT_OPERATION_REGION_BIT, UACPI_MAX_DEPTH_ANY,
+ UACPI_SHOULD_LOCK_NO, UACPI_PERMANENT_ONLY_NO, parent
+ );
+ }
+
+ uacpi_object_detach_child(parent);
+ uacpi_object_attach_child(parent, new_child);
+}
+
+/*
+ * Breakdown of what happens here:
+ *
+ * CopyObject(..., Obj) where Obj is:
+ * 1. LocalX -> Overwrite LocalX.
+ * 2. NAME -> Overwrite NAME.
+ * 3. ArgX -> Overwrite ArgX unless ArgX is a reference, in that case
+ * overwrite the referenced object.
+ * 4. RefOf -> Not allowed here.
+ * 5. Index -> Overwrite Object stored at the index.
+ */
+ static uacpi_status copy_object_to_reference(uacpi_object *dst,
+ uacpi_object *src)
+{
+ uacpi_status ret;
+ uacpi_object *src_obj, *new_obj;
+
+ switch (dst->flags) {
+ case UACPI_REFERENCE_KIND_ARG: {
+ uacpi_object *referenced_obj;
+
+ referenced_obj = uacpi_unwrap_internal_reference(dst);
+ if (referenced_obj->type == UACPI_OBJECT_REFERENCE) {
+ dst = reference_unwind(referenced_obj);
+ break;
+ }
+
+ UACPI_FALLTHROUGH;
+ }
+ case UACPI_REFERENCE_KIND_LOCAL:
+ case UACPI_REFERENCE_KIND_PKG_INDEX:
+ case UACPI_REFERENCE_KIND_NAMED:
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ src_obj = uacpi_unwrap_internal_reference(src);
+
+ new_obj = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ if (uacpi_unlikely(new_obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ret = uacpi_object_assign(new_obj, src_obj,
+ UACPI_ASSIGN_BEHAVIOR_DEEP_COPY);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ object_replace_child(dst, new_obj);
+ uacpi_object_unref(new_obj);
+
+ return UACPI_STATUS_OK;
+}
+
+/*
+ * if Store(..., Obj) where Obj is:
+ * 1. LocalX/Index -> OVERWRITE unless the object is a reference, in that
+ * case store to the referenced object _with_ implicit
+ * cast.
+ * 2. ArgX -> OVERWRITE unless the object is a reference, in that
+ * case OVERWRITE the referenced object.
+ * 3. NAME -> Store with implicit cast.
+ * 4. RefOf -> Not allowed here.
+ */
+static uacpi_status store_to_reference(
+ uacpi_object *dst, uacpi_object *src, uacpi_data_view *wtr_response
+)
+{
+ uacpi_object *src_obj;
+ uacpi_bool overwrite = UACPI_FALSE;
+
+ switch (dst->flags) {
+ case UACPI_REFERENCE_KIND_LOCAL:
+ case UACPI_REFERENCE_KIND_ARG:
+ case UACPI_REFERENCE_KIND_PKG_INDEX: {
+ uacpi_object *referenced_obj;
+
+ if (dst->flags == UACPI_REFERENCE_KIND_PKG_INDEX)
+ referenced_obj = dst->inner_object;
+ else
+ referenced_obj = uacpi_unwrap_internal_reference(dst);
+
+ if (referenced_obj->type == UACPI_OBJECT_REFERENCE) {
+ overwrite = dst->flags == UACPI_REFERENCE_KIND_ARG;
+ dst = reference_unwind(referenced_obj);
+ break;
+ }
+
+ overwrite = UACPI_TRUE;
+ break;
+ }
+ case UACPI_REFERENCE_KIND_NAMED:
+ dst = reference_unwind(dst);
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ src_obj = uacpi_unwrap_internal_reference(src);
+ overwrite |= dst->inner_object->type == UACPI_OBJECT_UNINITIALIZED;
+
+ if (overwrite) {
+ uacpi_status ret;
+ uacpi_object *new_obj;
+
+ new_obj = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ if (uacpi_unlikely(new_obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ ret = uacpi_object_assign(new_obj, src_obj,
+ UACPI_ASSIGN_BEHAVIOR_DEEP_COPY);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_object_unref(new_obj);
+ return ret;
+ }
+
+ object_replace_child(dst, new_obj);
+ uacpi_object_unref(new_obj);
+ return UACPI_STATUS_OK;
+ }
+
+ return object_assign_with_implicit_cast(
+ dst->inner_object, src_obj, wtr_response
+ );
+}
+
+static uacpi_status handle_ref_or_deref_of(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *dst, *src;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+
+ if (op_ctx->op->code == UACPI_AML_OP_CondRefOfOp)
+ dst = item_array_at(&op_ctx->items, 2)->obj;
+ else
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (op_ctx->op->code == UACPI_AML_OP_DerefOfOp) {
+ uacpi_bool was_a_reference = UACPI_FALSE;
+
+ if (src->type == UACPI_OBJECT_REFERENCE) {
+ was_a_reference = UACPI_TRUE;
+
+ /*
+ * Explicit dereferencing [DerefOf] behavior:
+ * Simply grabs the bottom-most object that is not a reference.
+ * This mimics the behavior of NT Acpi.sys: any DerfOf fetches
+ * the bottom-most reference. Note that this is different from
+ * ACPICA where DerefOf dereferences one level.
+ */
+ src = reference_unwind(src)->inner_object;
+ }
+
+ if (src->type == UACPI_OBJECT_BUFFER_INDEX) {
+ uacpi_buffer_index *buf_idx = &src->buffer_index;
+
+ dst->type = UACPI_OBJECT_INTEGER;
+ uacpi_memcpy_zerout(
+ &dst->integer, buffer_index_cursor(buf_idx),
+ sizeof(dst->integer), 1
+ );
+ return UACPI_STATUS_OK;
+ }
+
+ if (!was_a_reference) {
+ uacpi_error(
+ "invalid DerefOf argument: %s, expected a reference\n",
+ uacpi_object_type_to_string(src->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return uacpi_object_assign(dst, src,
+ UACPI_ASSIGN_BEHAVIOR_SHALLOW_COPY);
+ }
+
+ dst->type = UACPI_OBJECT_REFERENCE;
+ dst->inner_object = src;
+ uacpi_object_ref(src);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status do_binary_math(
+ uacpi_object *arg0, uacpi_object *arg1,
+ uacpi_object *tgt0, uacpi_object *tgt1,
+ uacpi_aml_op op
+)
+{
+ uacpi_u64 lhs, rhs, res;
+ uacpi_bool should_negate = UACPI_FALSE;
+
+ lhs = arg0->integer;
+ rhs = arg1->integer;
+
+ switch (op)
+ {
+ case UACPI_AML_OP_AddOp:
+ res = lhs + rhs;
+ break;
+ case UACPI_AML_OP_SubtractOp:
+ res = lhs - rhs;
+ break;
+ case UACPI_AML_OP_MultiplyOp:
+ res = lhs * rhs;
+ break;
+ case UACPI_AML_OP_ShiftLeftOp:
+ case UACPI_AML_OP_ShiftRightOp:
+ if (rhs <= (g_uacpi_rt_ctx.is_rev1 ? 31 : 63)) {
+ if (op == UACPI_AML_OP_ShiftLeftOp)
+ res = lhs << rhs;
+ else
+ res = lhs >> rhs;
+ } else {
+ res = 0;
+ }
+ break;
+ case UACPI_AML_OP_NandOp:
+ should_negate = UACPI_TRUE;
+ UACPI_FALLTHROUGH;
+ case UACPI_AML_OP_AndOp:
+ res = rhs & lhs;
+ break;
+ case UACPI_AML_OP_NorOp:
+ should_negate = UACPI_TRUE;
+ UACPI_FALLTHROUGH;
+ case UACPI_AML_OP_OrOp:
+ res = rhs | lhs;
+ break;
+ case UACPI_AML_OP_XorOp:
+ res = rhs ^ lhs;
+ break;
+ case UACPI_AML_OP_DivideOp:
+ if (uacpi_unlikely(rhs == 0)) {
+ uacpi_error("attempted to divide by zero\n");
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+ tgt1->integer = lhs / rhs;
+ res = lhs % rhs;
+ break;
+ case UACPI_AML_OP_ModOp:
+ if (uacpi_unlikely(rhs == 0)) {
+ uacpi_error("attempted to calculate modulo of zero\n");
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+ res = lhs % rhs;
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (should_negate)
+ res = ~res;
+
+ tgt0->integer = res;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_binary_math(struct execution_context *ctx)
+{
+ uacpi_object *arg0, *arg1, *tgt0, *tgt1;
+ struct item_array *items = &ctx->cur_op_ctx->items;
+ uacpi_aml_op op = ctx->cur_op_ctx->op->code;
+
+ arg0 = item_array_at(items, 0)->obj;
+ arg1 = item_array_at(items, 1)->obj;
+
+ if (op == UACPI_AML_OP_DivideOp) {
+ tgt0 = item_array_at(items, 4)->obj;
+ tgt1 = item_array_at(items, 5)->obj;
+ } else {
+ tgt0 = item_array_at(items, 3)->obj;
+ tgt1 = UACPI_NULL;
+ }
+
+ return do_binary_math(arg0, arg1, tgt0, tgt1, op);
+}
+
+static uacpi_status handle_unary_math(struct execution_context *ctx)
+{
+ uacpi_object *arg, *tgt;
+ struct item_array *items = &ctx->cur_op_ctx->items;
+ uacpi_aml_op op = ctx->cur_op_ctx->op->code;
+
+ arg = item_array_at(items, 0)->obj;
+ tgt = item_array_at(items, 2)->obj;
+
+ switch (op) {
+ case UACPI_AML_OP_NotOp:
+ tgt->integer = ~arg->integer;
+ truncate_number_if_needed(tgt);
+ break;
+ case UACPI_AML_OP_FindSetRightBitOp:
+ tgt->integer = uacpi_bit_scan_forward(arg->integer);
+ break;
+ case UACPI_AML_OP_FindSetLeftBitOp:
+ tgt->integer = uacpi_bit_scan_backward(arg->integer);
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status ensure_valid_idx(uacpi_object *obj, uacpi_size idx,
+ uacpi_size src_size)
+{
+ if (uacpi_likely(idx < src_size))
+ return UACPI_STATUS_OK;
+
+ uacpi_error(
+ "invalid index %zu, %s@%p has %zu elements\n",
+ idx, uacpi_object_type_to_string(obj->type), obj, src_size
+ );
+ return UACPI_STATUS_AML_OUT_OF_BOUNDS_INDEX;
+}
+
+static uacpi_status handle_index(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *src;
+ struct item *dst;
+ uacpi_size idx;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ idx = item_array_at(&op_ctx->items, 1)->obj->integer;
+ dst = item_array_at(&op_ctx->items, 3);
+
+ switch (src->type) {
+ case UACPI_OBJECT_BUFFER:
+ case UACPI_OBJECT_STRING: {
+ uacpi_buffer_index *buf_idx;
+ uacpi_data_view buf;
+ get_object_storage(src, &buf, UACPI_FALSE);
+
+ ret = ensure_valid_idx(src, idx, buf.length);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ dst->type = ITEM_OBJECT;
+ dst->obj = uacpi_create_object(UACPI_OBJECT_BUFFER_INDEX);
+ if (uacpi_unlikely(dst->obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ buf_idx = &dst->obj->buffer_index;
+ buf_idx->idx = idx;
+ buf_idx->buffer = src->buffer;
+ uacpi_shareable_ref(buf_idx->buffer);
+
+ break;
+ }
+ case UACPI_OBJECT_PACKAGE: {
+ uacpi_package *pkg = src->package;
+ uacpi_object *obj;
+
+ ret = ensure_valid_idx(src, idx, pkg->count);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ /*
+ * Lazily transform the package element into an internal reference
+ * to itself of type PKG_INDEX. This is needed to support stuff like
+ * CopyObject(..., Index(pkg, X)) where the new object must be
+ * propagated to anyone else with a currently alive index object.
+ *
+ * Sidenote: Yes, IndexOp is not a SimpleName, so technically it is
+ * illegal to CopyObject to it. However, yet again we fall
+ * victim to the NT ACPI driver implementation, which allows
+ * it just fine.
+ */
+ obj = pkg->objects[idx];
+ if (obj->type != UACPI_OBJECT_REFERENCE ||
+ obj->flags != UACPI_REFERENCE_KIND_PKG_INDEX) {
+
+ obj = uacpi_create_internal_reference(
+ UACPI_REFERENCE_KIND_PKG_INDEX, obj
+ );
+ if (uacpi_unlikely(obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ pkg->objects[idx] = obj;
+ uacpi_object_unref(obj->inner_object);
+ }
+
+ dst->obj = obj;
+ dst->type = ITEM_OBJECT;
+ uacpi_object_ref(dst->obj);
+ break;
+ }
+ default:
+ uacpi_error(
+ "invalid argument for Index: %s, "
+ "expected String/Buffer/Package\n",
+ uacpi_object_type_to_string(src->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_u64 object_to_integer(const uacpi_object *obj,
+ uacpi_size max_buffer_bytes)
+{
+ uacpi_u64 dst;
+
+ switch (obj->type) {
+ case UACPI_OBJECT_INTEGER:
+ dst = obj->integer;
+ break;
+ case UACPI_OBJECT_BUFFER: {
+ uacpi_size bytes;
+ bytes = UACPI_MIN(max_buffer_bytes, obj->buffer->size);
+ uacpi_memcpy_zerout(&dst, obj->buffer->data, sizeof(dst), bytes);
+ break;
+ }
+ case UACPI_OBJECT_STRING:
+ uacpi_string_to_integer(
+ obj->buffer->text, obj->buffer->size, UACPI_BASE_AUTO, &dst
+ );
+ break;
+ default:
+ dst = 0;
+ break;
+ }
+
+ return dst;
+}
+
+static uacpi_status integer_to_string(
+ uacpi_u64 integer, uacpi_buffer *str, uacpi_bool is_hex
+)
+{
+ int repr_len;
+ uacpi_char int_buf[21];
+ uacpi_size final_size;
+
+ repr_len = uacpi_snprintf(
+ int_buf, sizeof(int_buf),
+ is_hex ? "%"UACPI_PRIX64 : "%"UACPI_PRIu64,
+ UACPI_FMT64(integer)
+ );
+ if (uacpi_unlikely(repr_len < 0))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ // 0x prefix + repr + \0
+ final_size = (is_hex ? 2 : 0) + repr_len + 1;
+
+ str->data = uacpi_kernel_alloc(final_size);
+ if (uacpi_unlikely(str->data == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ if (is_hex) {
+ str->text[0] = '0';
+ str->text[1] = 'x';
+ }
+ uacpi_memcpy(str->text + (is_hex ? 2 : 0), int_buf, repr_len + 1);
+ str->size = final_size;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status buffer_to_string(
+ uacpi_buffer *buf, uacpi_buffer *str, uacpi_bool is_hex
+)
+{
+ int repr_len;
+ uacpi_char int_buf[5];
+ uacpi_size i, final_size;
+ uacpi_char *cursor;
+
+ if (is_hex) {
+ final_size = 4 * buf->size;
+ } else {
+ final_size = 0;
+
+ for (i = 0; i < buf->size; ++i) {
+ uacpi_u8 value = ((uacpi_u8*)buf->data)[i];
+
+ if (value < 10)
+ final_size += 1;
+ else if (value < 100)
+ final_size += 2;
+ else
+ final_size += 3;
+ }
+ }
+
+ // Comma for every value but one
+ final_size += buf->size - 1;
+
+ // Null terminator
+ final_size += 1;
+
+ str->data = uacpi_kernel_alloc(final_size);
+ if (uacpi_unlikely(str->data == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ cursor = str->data;
+
+ for (i = 0; i < buf->size; ++i) {
+ repr_len = uacpi_snprintf(
+ int_buf, sizeof(int_buf),
+ is_hex ? "0x%02X" : "%d",
+ ((uacpi_u8*)buf->data)[i]
+ );
+ if (uacpi_unlikely(repr_len < 0)) {
+ uacpi_free(str->data, final_size);
+ str->data = UACPI_NULL;
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ uacpi_memcpy(cursor, int_buf, repr_len + 1);
+ cursor += repr_len;
+
+ if (i != buf->size - 1)
+ *cursor++ = ',';
+ }
+
+ str->size = final_size;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status do_make_empty_object(uacpi_buffer *buf,
+ uacpi_bool is_string)
+{
+ buf->text = uacpi_kernel_alloc_zeroed(sizeof(uacpi_char));
+ if (uacpi_unlikely(buf->text == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ if (is_string)
+ buf->size = sizeof(uacpi_char);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status make_null_string(uacpi_buffer *buf)
+{
+ return do_make_empty_object(buf, UACPI_TRUE);
+}
+
+static uacpi_status make_null_buffer(uacpi_buffer *buf)
+{
+ /*
+ * Allocate at least 1 byte just to be safe,
+ * even for empty buffers. We still set the
+ * size to 0 though.
+ */
+ return do_make_empty_object(buf, UACPI_FALSE);
+}
+
+static uacpi_status handle_to(struct execution_context *ctx)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *src, *dst;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ dst = item_array_at(&op_ctx->items, 2)->obj;
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_ToIntegerOp:
+ // NT always takes the first 8 bytes, even for revision 1
+ dst->integer = object_to_integer(src, 8);
+ break;
+
+ case UACPI_AML_OP_ToHexStringOp:
+ case UACPI_AML_OP_ToDecimalStringOp: {
+ uacpi_bool is_hex = op_ctx->op->code == UACPI_AML_OP_ToHexStringOp;
+
+ if (src->type == UACPI_OBJECT_INTEGER) {
+ ret = integer_to_string(src->integer, dst->buffer, is_hex);
+ break;
+ } else if (src->type == UACPI_OBJECT_BUFFER) {
+ if (uacpi_unlikely(src->buffer->size == 0))
+ return make_null_string(dst->buffer);
+
+ ret = buffer_to_string(src->buffer, dst->buffer, is_hex);
+ break;
+ }
+ UACPI_FALLTHROUGH;
+ }
+ case UACPI_AML_OP_ToBufferOp: {
+ uacpi_data_view buf;
+ uacpi_u8 *dst_buf;
+
+ ret = get_object_storage(src, &buf, UACPI_TRUE);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (uacpi_unlikely(buf.length == 0))
+ return make_null_buffer(dst->buffer);
+
+ dst_buf = uacpi_kernel_alloc(buf.length);
+ if (uacpi_unlikely(dst_buf == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(dst_buf, buf.bytes, buf.length);
+ dst->buffer->data = dst_buf;
+ dst->buffer->size = buf.length;
+ break;
+ }
+
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return ret;
+}
+
+static uacpi_status handle_to_string(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_buffer *src_buf, *dst_buf;
+ uacpi_size req_len, len;
+
+ src_buf = item_array_at(&op_ctx->items, 0)->obj->buffer;
+ req_len = item_array_at(&op_ctx->items, 1)->obj->integer;
+ dst_buf = item_array_at(&op_ctx->items, 3)->obj->buffer;
+
+ len = UACPI_MIN(req_len, src_buf->size);
+ if (uacpi_unlikely(len == 0))
+ return make_null_string(dst_buf);
+
+ len = uacpi_strnlen(src_buf->text, len);
+
+ dst_buf->text = uacpi_kernel_alloc(len + 1);
+ if (uacpi_unlikely(dst_buf->text == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(dst_buf->text, src_buf->data, len);
+ dst_buf->text[len] = '\0';
+ dst_buf->size = len + 1;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_mid(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *src, *dst;
+ uacpi_data_view src_buf;
+ uacpi_buffer *dst_buf;
+ uacpi_size idx, len;
+ uacpi_bool is_string;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ if (uacpi_unlikely(src->type != UACPI_OBJECT_STRING &&
+ src->type != UACPI_OBJECT_BUFFER)) {
+ uacpi_error(
+ "invalid argument for Mid: %s, expected String/Buffer\n",
+ uacpi_object_type_to_string(src->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ idx = item_array_at(&op_ctx->items, 1)->obj->integer;
+ len = item_array_at(&op_ctx->items, 2)->obj->integer;
+ dst = item_array_at(&op_ctx->items, 4)->obj;
+ dst_buf = dst->buffer;
+
+ is_string = src->type == UACPI_OBJECT_STRING;
+ get_object_storage(src, &src_buf, UACPI_FALSE);
+
+ if (uacpi_unlikely(src_buf.length == 0 || idx >= src_buf.length ||
+ len == 0)) {
+ if (src->type == UACPI_OBJECT_STRING) {
+ dst->type = UACPI_OBJECT_STRING;
+ return make_null_string(dst_buf);
+ }
+
+ return make_null_buffer(dst_buf);
+ }
+
+ // Guaranteed to be at least 1 here
+ len = UACPI_MIN(len, src_buf.length - idx);
+
+ dst_buf->data = uacpi_kernel_alloc(len + is_string);
+ if (uacpi_unlikely(dst_buf->data == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(dst_buf->data, (uacpi_u8*)src_buf.bytes + idx, len);
+ dst_buf->size = len;
+
+ if (is_string) {
+ dst_buf->text[dst_buf->size++] = '\0';
+ dst->type = UACPI_OBJECT_STRING;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_concatenate(struct execution_context *ctx)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *arg0, *arg1, *dst;
+ uacpi_u8 *dst_buf;
+ uacpi_size buf_size = 0;
+
+ arg0 = item_array_at(&op_ctx->items, 0)->obj;
+ arg1 = item_array_at(&op_ctx->items, 1)->obj;
+ dst = item_array_at(&op_ctx->items, 3)->obj;
+
+ switch (arg0->type) {
+ case UACPI_OBJECT_INTEGER: {
+ uacpi_u64 arg1_as_int;
+ uacpi_size int_size;
+
+ int_size = sizeof_int();
+ buf_size = int_size * 2;
+
+ dst_buf = uacpi_kernel_alloc(buf_size);
+ if (uacpi_unlikely(dst_buf == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ arg1_as_int = object_to_integer(arg1, 8);
+
+ uacpi_memcpy(dst_buf, &arg0->integer, int_size);
+ uacpi_memcpy(dst_buf+ int_size, &arg1_as_int, int_size);
+ break;
+ }
+ case UACPI_OBJECT_BUFFER: {
+ uacpi_buffer *arg0_buf = arg0->buffer;
+ uacpi_data_view arg1_buf = { 0 };
+
+ get_object_storage(arg1, &arg1_buf, UACPI_TRUE);
+ buf_size = arg0_buf->size + arg1_buf.length;
+
+ dst_buf = uacpi_kernel_alloc(buf_size);
+ if (uacpi_unlikely(dst_buf == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(dst_buf, arg0_buf->data, arg0_buf->size);
+ uacpi_memcpy(dst_buf + arg0_buf->size, arg1_buf.bytes, arg1_buf.length);
+ break;
+ }
+ case UACPI_OBJECT_STRING: {
+ uacpi_char int_buf[17];
+ void *arg1_ptr;
+ uacpi_size arg0_size, arg1_size;
+ uacpi_buffer *arg0_buf = arg0->buffer;
+
+ switch (arg1->type) {
+ case UACPI_OBJECT_INTEGER: {
+ int size;
+ size = uacpi_snprintf(int_buf, sizeof(int_buf), "%"UACPI_PRIx64,
+ UACPI_FMT64(arg1->integer));
+ if (size < 0)
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ arg1_ptr = int_buf;
+ arg1_size = size + 1;
+ break;
+ }
+ case UACPI_OBJECT_STRING:
+ arg1_ptr = arg1->buffer->data;
+ arg1_size = arg1->buffer->size;
+ break;
+ case UACPI_OBJECT_BUFFER: {
+ uacpi_buffer tmp_buf;
+
+ ret = buffer_to_string(arg1->buffer, &tmp_buf, UACPI_TRUE);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ arg1_ptr = tmp_buf.data;
+ arg1_size = tmp_buf.size;
+ break;
+ }
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ arg0_size = arg0_buf->size ? arg0_buf->size - 1 : arg0_buf->size;
+ buf_size = arg0_size + arg1_size;
+
+ dst_buf = uacpi_kernel_alloc(buf_size);
+ if (uacpi_unlikely(dst_buf == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto cleanup;
+ }
+
+ uacpi_memcpy(dst_buf, arg0_buf->data, arg0_size);
+ uacpi_memcpy(dst_buf + arg0_size, arg1_ptr, arg1_size);
+ dst->type = UACPI_OBJECT_STRING;
+
+ cleanup:
+ if (arg1->type == UACPI_OBJECT_BUFFER)
+ uacpi_free(arg1_ptr, arg1_size);
+ break;
+ }
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (uacpi_likely_success(ret)) {
+ dst->buffer->data = dst_buf;
+ dst->buffer->size = buf_size;
+ }
+ return ret;
+}
+
+static uacpi_status handle_concatenate_res(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_data_view buffer;
+ uacpi_object *arg0, *arg1, *dst;
+ uacpi_u8 *dst_buf;
+ uacpi_size dst_size, arg0_size, arg1_size;
+
+ arg0 = item_array_at(&op_ctx->items, 0)->obj;
+ arg1 = item_array_at(&op_ctx->items, 1)->obj;
+ dst = item_array_at(&op_ctx->items, 3)->obj;
+
+ uacpi_buffer_to_view(arg0->buffer, &buffer);
+ ret = uacpi_find_aml_resource_end_tag(buffer, &arg0_size);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ uacpi_buffer_to_view(arg1->buffer, &buffer);
+ ret = uacpi_find_aml_resource_end_tag(buffer, &arg1_size);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ dst_size = arg0_size + arg1_size + sizeof(struct acpi_resource_end_tag);
+
+ dst_buf = uacpi_kernel_alloc(dst_size);
+ if (uacpi_unlikely(dst_buf == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ dst->buffer->data = dst_buf;
+ dst->buffer->size = dst_size;
+
+ uacpi_memcpy(dst_buf, arg0->buffer->data, arg0_size);
+ uacpi_memcpy(dst_buf + arg0_size, arg1->buffer->data, arg1_size);
+
+ /*
+ * Small item (0), End Tag (0x0F), length 1
+ * Leave the checksum as 0
+ */
+ dst_buf[dst_size - 2] =
+ (ACPI_RESOURCE_END_TAG << ACPI_SMALL_ITEM_NAME_IDX) |
+ (sizeof(struct acpi_resource_end_tag) - 1);
+ dst_buf[dst_size - 1] = 0;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_sizeof(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *src, *dst;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (uacpi_likely(src->type == UACPI_OBJECT_REFERENCE))
+ src = reference_unwind(src)->inner_object;
+
+ switch (src->type) {
+ case UACPI_OBJECT_STRING:
+ case UACPI_OBJECT_BUFFER: {
+ uacpi_data_view buf;
+ get_object_storage(src, &buf, UACPI_FALSE);
+
+ dst->integer = buf.length;
+ break;
+ }
+
+ case UACPI_OBJECT_PACKAGE:
+ dst->integer = src->package->count;
+ break;
+
+ default:
+ uacpi_error(
+ "invalid argument for Sizeof: %s, "
+ "expected String/Buffer/Package\n",
+ uacpi_object_type_to_string(src->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_object_type(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *src, *dst;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (uacpi_likely(src->type == UACPI_OBJECT_REFERENCE))
+ src = reference_unwind(src)->inner_object;
+
+ dst->integer = src->type;
+ if (dst->integer == UACPI_OBJECT_BUFFER_INDEX)
+ dst->integer = UACPI_OBJECT_BUFFER_FIELD;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_timer(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *dst;
+
+ dst = item_array_at(&op_ctx->items, 0)->obj;
+ dst->integer = uacpi_kernel_get_nanoseconds_since_boot() / 100;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_stall_or_sleep(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_u64 time;
+
+ time = item_array_at(&op_ctx->items, 0)->obj->integer;
+
+ if (op_ctx->op->code == UACPI_AML_OP_SleepOp) {
+ /*
+ * ACPICA doesn't allow sleeps longer than 2 seconds,
+ * so we shouldn't either.
+ */
+ if (time > 2000)
+ time = 2000;
+
+ uacpi_namespace_write_unlock();
+ uacpi_kernel_sleep(time);
+ uacpi_namespace_write_lock();
+ } else {
+ // Spec says this must evaluate to a ByteData
+ if (time > 0xFF)
+ time = 0xFF;
+ uacpi_kernel_stall(time);
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_bcd(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_u64 src, dst = 0;
+ uacpi_size i;
+ uacpi_object *dst_obj;
+
+ src = item_array_at(&op_ctx->items, 0)->obj->integer;
+ dst_obj = item_array_at(&op_ctx->items, 2)->obj;
+ i = 64;
+
+ /*
+ * NOTE: ACPICA just errors out for invalid BCD, but NT allows it just fine.
+ * FromBCD matches NT behavior 1:1 even for invalid BCD, but ToBCD
+ * produces different results when the input is too large.
+ */
+ if (op_ctx->op->code == UACPI_AML_OP_FromBCDOp) {
+ do {
+ i -= 4;
+ dst *= 10;
+ dst += (src >> i) & 0xF;
+ } while (i);
+ } else {
+ while (src != 0) {
+ dst >>= 4;
+ i -= 4;
+ dst |= (src % 10) << 60;
+ src /= 10;
+ }
+
+ dst >>= (i % 64);
+ }
+
+ dst_obj->integer = dst;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_unload(struct execution_context *ctx)
+{
+ UACPI_UNUSED(ctx);
+
+ /*
+ * Technically this doesn't exist in the wild, from the dumps that I have
+ * the only user of the Unload opcode is the Surface Pro 3, which triggers
+ * an unload of some I2C-related table as a response to some event.
+ *
+ * This op has been long deprecated by the specification exactly because
+ * it hasn't really been used by anyone and the fact that it introduces
+ * an enormous layer of complexity, which no driver is really prepared to
+ * deal with (aka namespace nodes disappearing under its feet).
+ *
+ * Just pretend we have actually unloaded whatever the AML asked for, if it
+ * ever tries to re-load this table that will just skip opcodes that create
+ * already existing objects, which should be good enough and mostly
+ * transparent to the AML.
+ */
+ uacpi_warn("refusing to unload a table from AML\n");
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_logical_not(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *src, *dst;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+
+ dst->type = UACPI_OBJECT_INTEGER;
+ dst->integer = src->integer ? 0 : ones();
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_bool handle_logical_equality(uacpi_object *lhs, uacpi_object *rhs)
+{
+ uacpi_bool res = UACPI_FALSE;
+
+ if (lhs->type == UACPI_OBJECT_STRING || lhs->type == UACPI_OBJECT_BUFFER) {
+ res = lhs->buffer->size == rhs->buffer->size;
+
+ if (res && lhs->buffer->size) {
+ res = uacpi_memcmp(
+ lhs->buffer->data,
+ rhs->buffer->data,
+ lhs->buffer->size
+ ) == 0;
+ }
+ } else if (lhs->type == UACPI_OBJECT_INTEGER) {
+ res = lhs->integer == rhs->integer;
+ }
+
+ return res;
+}
+
+static uacpi_bool handle_logical_less_or_greater(
+ uacpi_aml_op op, uacpi_object *lhs, uacpi_object *rhs
+)
+{
+ if (lhs->type == UACPI_OBJECT_STRING || lhs->type == UACPI_OBJECT_BUFFER) {
+ int res;
+ uacpi_buffer *lhs_buf, *rhs_buf;
+
+ lhs_buf = lhs->buffer;
+ rhs_buf = rhs->buffer;
+
+ res = uacpi_memcmp(lhs_buf->data, rhs_buf->data,
+ UACPI_MIN(lhs_buf->size, rhs_buf->size));
+ if (res == 0) {
+ if (lhs_buf->size < rhs_buf->size)
+ res = -1;
+ else if (lhs_buf->size > rhs_buf->size)
+ res = 1;
+ }
+
+ if (op == UACPI_AML_OP_LLessOp)
+ return res < 0;
+
+ return res > 0;
+ }
+
+ if (op == UACPI_AML_OP_LLessOp)
+ return lhs->integer < rhs->integer;
+
+ return lhs->integer > rhs->integer;
+}
+
+static uacpi_status handle_binary_logic(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_aml_op op = op_ctx->op->code;
+ uacpi_object *lhs, *rhs, *dst;
+ uacpi_bool res;
+
+ lhs = item_array_at(&op_ctx->items, 0)->obj;
+ rhs = item_array_at(&op_ctx->items, 1)->obj;
+ dst = item_array_at(&op_ctx->items, 2)->obj;
+
+ switch (op) {
+ case UACPI_AML_OP_LEqualOp:
+ case UACPI_AML_OP_LLessOp:
+ case UACPI_AML_OP_LGreaterOp:
+ // TODO: typecheck at parse time
+ if (lhs->type != rhs->type) {
+ uacpi_error(
+ "don't know how to do a logical comparison of '%s' and '%s'\n",
+ uacpi_object_type_to_string(lhs->type),
+ uacpi_object_type_to_string(rhs->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ if (op == UACPI_AML_OP_LEqualOp)
+ res = handle_logical_equality(lhs, rhs);
+ else
+ res = handle_logical_less_or_greater(op, lhs, rhs);
+ break;
+ default: {
+ uacpi_u64 lhs_int, rhs_int;
+
+ // NT only looks at the first 4 bytes of a buffer
+ lhs_int = object_to_integer(lhs, 4);
+ rhs_int = object_to_integer(rhs, 4);
+
+ if (op == UACPI_AML_OP_LandOp)
+ res = lhs_int && rhs_int;
+ else
+ res = lhs_int || rhs_int;
+ break;
+ }
+ }
+
+ dst->integer = res ? ones() : 0;
+ return UACPI_STATUS_OK;
+}
+
+enum match_op {
+ MTR = 0,
+ MEQ = 1,
+ MLE = 2,
+ MLT = 3,
+ MGE = 4,
+ MGT = 5,
+};
+
+static uacpi_bool match_one(enum match_op op, uacpi_u64 lhs, uacpi_u64 rhs)
+{
+ switch (op) {
+ case MTR:
+ return UACPI_TRUE;
+ case MEQ:
+ return lhs == rhs;
+ case MLE:
+ return lhs <= rhs;
+ case MLT:
+ return lhs < rhs;
+ case MGE:
+ return lhs >= rhs;
+ case MGT:
+ return lhs > rhs;
+ default:
+ return UACPI_FALSE;
+ }
+}
+
+static uacpi_status handle_match(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_package *pkg;
+ uacpi_u64 operand0, operand1, start_idx, i;
+ enum match_op mop0, mop1;
+ uacpi_object *dst;
+
+ pkg = item_array_at(&op_ctx->items, 0)->obj->package;
+ mop0 = item_array_at(&op_ctx->items, 1)->immediate;
+ operand0 = item_array_at(&op_ctx->items, 2)->obj->integer;
+ mop1 = item_array_at(&op_ctx->items, 3)->immediate;
+ operand1 = item_array_at(&op_ctx->items, 4)->obj->integer;
+ start_idx = item_array_at(&op_ctx->items, 5)->obj->integer;
+ dst = item_array_at(&op_ctx->items, 6)->obj;
+
+ for (i = start_idx; i < pkg->count; ++i) {
+ uacpi_object *obj = pkg->objects[i];
+
+ if (obj->type != UACPI_OBJECT_INTEGER)
+ continue;
+
+ if (match_one(mop0, obj->integer, operand0) &&
+ match_one(mop1, obj->integer, operand1))
+ break;
+ }
+
+ if (i < pkg->count)
+ dst->integer = i;
+ else
+ dst->integer = ones();
+
+ return UACPI_STATUS_OK;
+}
+
+/*
+ * PkgLength :=
+ * PkgLeadByte |
+ * <pkgleadbyte bytedata> |
+ * <pkgleadbyte bytedata bytedata> | <pkgleadbyte bytedata bytedata bytedata>
+ * PkgLeadByte :=
+ * <bit 7-6: bytedata count that follows (0-3)>
+ * <bit 5-4: only used if pkglength < 63>
+ * <bit 3-0: least significant package length nybble>
+ */
+static uacpi_status parse_package_length(struct call_frame *frame,
+ struct package_length *out_pkg)
+{
+ uacpi_u32 left, size;
+ uacpi_u8 *data, marker_length;
+
+ out_pkg->begin = frame->code_offset;
+ marker_length = 1;
+
+ left = call_frame_code_bytes_left(frame);
+ if (uacpi_unlikely(left < 1))
+ return UACPI_STATUS_AML_BAD_ENCODING;
+
+ data = call_frame_cursor(frame);
+ marker_length += *data >> 6;
+
+ if (uacpi_unlikely(left < marker_length))
+ return UACPI_STATUS_AML_BAD_ENCODING;
+
+ switch (marker_length) {
+ case 1:
+ size = *data & 0x3F;
+ break;
+ case 2:
+ case 3:
+ case 4: {
+ uacpi_u32 temp_byte = 0;
+
+ size = *data & 0xF;
+ uacpi_memcpy(&temp_byte, data + 1, marker_length - 1);
+
+ // marker_length - 1 is at most 3, so this shift is safe
+ size |= temp_byte << 4;
+ break;
+ }
+ }
+
+ frame->code_offset += marker_length;
+
+ out_pkg->end = out_pkg->begin + size;
+ if (uacpi_unlikely(out_pkg->end < out_pkg->begin)) {
+ uacpi_error(
+ "PkgLength overflow: start=%u, size=%u\n", out_pkg->begin, size
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+/*
+ * ByteData
+ * // bit 0-2: ArgCount (0-7)
+ * // bit 3: SerializeFlag
+ * // 0 NotSerialized
+ * // 1 Serialized
+ * // bit 4-7: SyncLevel (0x00-0x0f)
+ */
+static void init_method_flags(uacpi_control_method *method, uacpi_u8 flags_byte)
+{
+ method->args = flags_byte & 0x7;
+ method->is_serialized = (flags_byte >> 3) & 1;
+ method->sync_level = flags_byte >> 4;
+}
+
+static uacpi_status handle_create_method(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ struct uacpi_control_method *this_method, *method;
+ struct package_length *pkg;
+ struct uacpi_namespace_node *node;
+ struct uacpi_object *dst;
+ uacpi_u32 method_begin_offset, method_size;
+
+ this_method = ctx->cur_frame->method;
+ pkg = &item_array_at(&op_ctx->items, 0)->pkg;
+ node = item_array_at(&op_ctx->items, 1)->node;
+ method_begin_offset = item_array_at(&op_ctx->items, 3)->immediate;
+
+ if (uacpi_unlikely(pkg->end < pkg->begin ||
+ pkg->end < method_begin_offset ||
+ pkg->end > this_method->size)) {
+ uacpi_error(
+ "invalid method %.4s bounds [%u..%u] (parent size is %u)\n",
+ node->name.text, method_begin_offset, pkg->end, this_method->size
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ dst = item_array_at(&op_ctx->items, 4)->obj;
+
+ method = dst->method;
+ method_size = pkg->end - method_begin_offset;
+
+ if (method_size) {
+ method->code = uacpi_kernel_alloc(method_size);
+ if (uacpi_unlikely(method->code == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ uacpi_memcpy(
+ method->code,
+ ctx->cur_frame->method->code + method_begin_offset,
+ method_size
+ );
+ method->size = method_size;
+ method->owns_code = 1;
+ }
+
+ init_method_flags(method, item_array_at(&op_ctx->items, 2)->immediate);
+
+ node->object = uacpi_create_internal_reference(UACPI_REFERENCE_KIND_NAMED,
+ dst);
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_create_mutex_or_event(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_namespace_node *node;
+ uacpi_object *dst;
+
+ node = item_array_at(&op_ctx->items, 0)->node;
+
+ if (op_ctx->op->code == UACPI_AML_OP_MutexOp) {
+ dst = item_array_at(&op_ctx->items, 2)->obj;
+
+ // bits 0-3: SyncLevel (0x00-0x0f), bits 4-7: Reserved (must be 0)
+ dst->mutex->sync_level = item_array_at(&op_ctx->items, 1)->immediate;
+ dst->mutex->sync_level &= 0xF;
+ } else {
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+ }
+
+ node->object = uacpi_create_internal_reference(
+ UACPI_REFERENCE_KIND_NAMED,
+ dst
+ );
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_event_ctl(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *obj;
+
+ obj = uacpi_unwrap_internal_reference(
+ item_array_at(&op_ctx->items, 0)->obj
+ );
+ if (uacpi_unlikely(obj->type != UACPI_OBJECT_EVENT)) {
+ uacpi_error(
+ "%s: invalid argument '%s', expected an Event object\n",
+ op_ctx->op->name, uacpi_object_type_to_string(obj->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ switch (op_ctx->op->code)
+ {
+ case UACPI_AML_OP_SignalOp:
+ uacpi_kernel_signal_event(obj->event->handle);
+ break;
+ case UACPI_AML_OP_ResetOp:
+ uacpi_kernel_reset_event(obj->event->handle);
+ break;
+ case UACPI_AML_OP_WaitOp: {
+ uacpi_u64 timeout;
+ uacpi_bool ret;
+
+ timeout = item_array_at(&op_ctx->items, 1)->obj->integer;
+ if (timeout > 0xFFFF)
+ timeout = 0xFFFF;
+
+ uacpi_namespace_write_unlock();
+ ret = uacpi_kernel_wait_for_event(obj->event->handle, timeout);
+ uacpi_namespace_write_lock();
+
+ /*
+ * The return value here is inverted, we return 0 for success and Ones
+ * for timeout and everything else.
+ */
+ if (ret)
+ item_array_at(&op_ctx->items, 2)->obj->integer = 0;
+ break;
+ }
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_mutex_ctl(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_object *obj;
+
+ obj = uacpi_unwrap_internal_reference(
+ item_array_at(&op_ctx->items, 0)->obj
+ );
+ if (uacpi_unlikely(obj->type != UACPI_OBJECT_MUTEX)) {
+ uacpi_error(
+ "%s: invalid argument '%s', expected a Mutex object\n",
+ op_ctx->op->name, uacpi_object_type_to_string(obj->type)
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ switch (op_ctx->op->code)
+ {
+ case UACPI_AML_OP_AcquireOp: {
+ uacpi_u64 timeout;
+ uacpi_u64 *return_value;
+ uacpi_status ret;
+
+ return_value = &item_array_at(&op_ctx->items, 2)->obj->integer;
+
+ if (uacpi_unlikely(ctx->sync_level > obj->mutex->sync_level)) {
+ uacpi_warn(
+ "ignoring attempt to acquire mutex @%p with a lower sync level "
+ "(%d < %d)\n", obj->mutex, obj->mutex->sync_level,
+ ctx->sync_level
+ );
+ break;
+ }
+
+ timeout = item_array_at(&op_ctx->items, 1)->immediate;
+ if (timeout > 0xFFFF)
+ timeout = 0xFFFF;
+
+ if (uacpi_this_thread_owns_aml_mutex(obj->mutex)) {
+ ret = uacpi_acquire_aml_mutex(obj->mutex, timeout);
+ if (uacpi_likely_success(ret))
+ *return_value = 0;
+ break;
+ }
+
+ ret = uacpi_acquire_aml_mutex(obj->mutex, timeout);
+ if (uacpi_unlikely_error(ret))
+ break;
+
+ ret = held_mutexes_array_push(&ctx->held_mutexes, obj->mutex);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_release_aml_mutex(obj->mutex);
+ return ret;
+ }
+
+ ctx->sync_level = obj->mutex->sync_level;
+ *return_value = 0;
+ break;
+ }
+
+ case UACPI_AML_OP_ReleaseOp: {
+ uacpi_status ret;
+
+ if (!uacpi_this_thread_owns_aml_mutex(obj->mutex)) {
+ uacpi_warn(
+ "attempted to release not-previously-acquired mutex object "
+ "@%p (%p)\n", obj->mutex, obj->mutex->handle
+ );
+ break;
+ }
+
+ ret = held_mutexes_array_remove_and_release(
+ &ctx->held_mutexes, obj->mutex,
+ FORCE_RELEASE_NO
+ );
+ if (uacpi_likely_success(ret)) {
+ uacpi_mutex **last_mutex;
+
+ last_mutex = held_mutexes_array_last(&ctx->held_mutexes);
+ if (last_mutex == UACPI_NULL) {
+ ctx->sync_level = 0;
+ break;
+ }
+
+ ctx->sync_level = (*last_mutex)->sync_level;
+ }
+ break;
+ }
+
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_notify(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ struct uacpi_namespace_node *node;
+ uacpi_u64 value;
+
+ node = item_array_at(&op_ctx->items, 0)->node;
+ value = item_array_at(&op_ctx->items, 1)->obj->integer;
+
+ ret = uacpi_notify_all(node, value);
+ if (uacpi_likely_success(ret))
+ return ret;
+
+ if (ret == UACPI_STATUS_NO_HANDLER) {
+ const uacpi_char *path;
+
+ path = uacpi_namespace_node_generate_absolute_path(node);
+ uacpi_warn(
+ "ignoring firmware Notify(%s, 0x%"UACPI_PRIX64") request, "
+ "no listeners\n", path, UACPI_FMT64(value)
+ );
+ uacpi_free_dynamic_string(path);
+
+ return UACPI_STATUS_OK;
+ }
+
+ if (ret == UACPI_STATUS_INVALID_ARGUMENT) {
+ uacpi_error("Notify() called on an invalid object %.4s\n",
+ node->name.text);
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return ret;
+}
+
+static uacpi_status handle_firmware_request(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_firmware_request req = { 0 };
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_BreakPointOp:
+ req.type = UACPI_FIRMWARE_REQUEST_TYPE_BREAKPOINT;
+ req.breakpoint.ctx = ctx;
+ break;
+ case UACPI_AML_OP_FatalOp:
+ req.type = UACPI_FIRMWARE_REQUEST_TYPE_FATAL;
+ req.fatal.type = item_array_at(&op_ctx->items, 0)->immediate;
+ req.fatal.code = item_array_at(&op_ctx->items, 1)->immediate;
+ req.fatal.arg = item_array_at(&op_ctx->items, 2)->obj->integer;
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ uacpi_namespace_write_unlock();
+ uacpi_kernel_handle_firmware_request(&req);
+ uacpi_namespace_write_lock();
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_create_named(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ struct uacpi_namespace_node *node;
+ uacpi_object *src;
+
+ node = item_array_at(&op_ctx->items, 0)->node;
+ src = item_array_at(&op_ctx->items, 1)->obj;
+
+ node->object = uacpi_create_internal_reference(UACPI_REFERENCE_KIND_NAMED,
+ src);
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_object_type buffer_field_get_read_type(
+ struct uacpi_buffer_field *field
+)
+{
+ if (field->bit_length > (g_uacpi_rt_ctx.is_rev1 ? 32u : 64u) ||
+ field->force_buffer)
+ return UACPI_OBJECT_BUFFER;
+
+ return UACPI_OBJECT_INTEGER;
+}
+
+static uacpi_status field_get_read_type(
+ uacpi_object *obj, uacpi_object_type *out_type
+)
+{
+ if (obj->type == UACPI_OBJECT_BUFFER_FIELD) {
+ *out_type = buffer_field_get_read_type(&obj->buffer_field);
+ return UACPI_STATUS_OK;
+ }
+
+ return uacpi_field_unit_get_read_type(obj->field_unit, out_type);
+}
+
+static uacpi_status field_byte_size(
+ uacpi_object *obj, uacpi_size *out_size
+)
+{
+ uacpi_size bit_length;
+
+ if (obj->type == UACPI_OBJECT_BUFFER_FIELD) {
+ bit_length = obj->buffer_field.bit_length;
+ } else {
+ uacpi_status ret;
+
+ ret = uacpi_field_unit_get_bit_length(obj->field_unit, &bit_length);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ *out_size = uacpi_round_up_bits_to_bytes(bit_length);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_field_read(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ struct uacpi_namespace_node *node;
+ uacpi_object *src_obj, *dst_obj;
+ uacpi_size dst_size;
+ void *dst = UACPI_NULL;
+ uacpi_data_view wtr_response = { 0 };
+
+ node = item_array_at(&op_ctx->items, 0)->node;
+ src_obj = uacpi_namespace_node_get_object(node);
+ dst_obj = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (op_ctx->op->code == UACPI_AML_OP_InternalOpReadFieldAsBuffer) {
+ uacpi_buffer *buf;
+
+ ret = field_byte_size(src_obj, &dst_size);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (dst_size != 0) {
+ buf = dst_obj->buffer;
+
+ dst = uacpi_kernel_alloc_zeroed(dst_size);
+ if (dst == UACPI_NULL)
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ buf->data = dst;
+ buf->size = dst_size;
+ }
+ } else {
+ dst = &dst_obj->integer;
+ dst_size = sizeof(uacpi_u64);
+ }
+
+ if (src_obj->type == UACPI_OBJECT_BUFFER_FIELD) {
+ uacpi_read_buffer_field(&src_obj->buffer_field, dst);
+ return UACPI_STATUS_OK;
+ }
+
+ ret = uacpi_read_field_unit(
+ src_obj->field_unit, dst, dst_size, &wtr_response
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (wtr_response.data != UACPI_NULL) {
+ uacpi_buffer *buf;
+
+ buf = dst_obj->buffer;
+ buf->data = wtr_response.data;
+ buf->size = wtr_response.length;
+ }
+
+ return ret;
+}
+
+static uacpi_status handle_create_buffer_field(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ struct uacpi_namespace_node *node;
+ uacpi_buffer *src_buf;
+ uacpi_object *field_obj;
+ uacpi_buffer_field *field;
+
+ /*
+ * Layout of items here:
+ * [0] -> Type checked source buffer object
+ * [1] -> Byte/bit index integer object
+ * [2] ( if CreateField) -> bit length integer object
+ * [3] (2 if not CreateField) -> the new namespace node
+ * [4] (3 if not CreateField) -> the buffer field object we're creating here
+ */
+ src_buf = item_array_at(&op_ctx->items, 0)->obj->buffer;
+
+ if (op_ctx->op->code == UACPI_AML_OP_CreateFieldOp) {
+ uacpi_object *idx_obj, *len_obj;
+
+ idx_obj = item_array_at(&op_ctx->items, 1)->obj;
+ len_obj = item_array_at(&op_ctx->items, 2)->obj;
+ node = item_array_at(&op_ctx->items, 3)->node;
+ field_obj = item_array_at(&op_ctx->items, 4)->obj;
+ field = &field_obj->buffer_field;
+
+ field->bit_index = idx_obj->integer;
+
+ if (uacpi_unlikely(!len_obj->integer ||
+ len_obj->integer > 0xFFFFFFFF)) {
+ uacpi_error("invalid bit field length (%u)\n", field->bit_length);
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ field->bit_length = len_obj->integer;
+ field->force_buffer = UACPI_TRUE;
+ } else {
+ uacpi_object *idx_obj;
+
+ idx_obj = item_array_at(&op_ctx->items, 1)->obj;
+ node = item_array_at(&op_ctx->items, 2)->node;
+ field_obj = item_array_at(&op_ctx->items, 3)->obj;
+ field = &field_obj->buffer_field;
+
+ field->bit_index = idx_obj->integer;
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_CreateBitFieldOp:
+ field->bit_length = 1;
+ break;
+ case UACPI_AML_OP_CreateByteFieldOp:
+ field->bit_length = 8;
+ break;
+ case UACPI_AML_OP_CreateWordFieldOp:
+ field->bit_length = 16;
+ break;
+ case UACPI_AML_OP_CreateDWordFieldOp:
+ field->bit_length = 32;
+ break;
+ case UACPI_AML_OP_CreateQWordFieldOp:
+ field->bit_length = 64;
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (op_ctx->op->code != UACPI_AML_OP_CreateBitFieldOp)
+ field->bit_index *= 8;
+ }
+
+ if (uacpi_unlikely((field->bit_index + field->bit_length) >
+ src_buf->size * 8)) {
+ uacpi_error(
+ "invalid buffer field: bits [%zu..%zu], buffer size is %zu bytes\n",
+ field->bit_index, field->bit_index + field->bit_length,
+ src_buf->size
+ );
+ return UACPI_STATUS_AML_OUT_OF_BOUNDS_INDEX;
+ }
+
+ field->backing = src_buf;
+ uacpi_shareable_ref(field->backing);
+ node->object = uacpi_create_internal_reference(UACPI_REFERENCE_KIND_NAMED,
+ field_obj);
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_control_flow(struct execution_context *ctx)
+{
+ struct call_frame *frame = ctx->cur_frame;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ if (uacpi_unlikely(frame->last_while == UACPI_NULL)) {
+ uacpi_error(
+ "attempting to %s outside of a While block\n",
+ op_ctx->op->code == UACPI_AML_OP_BreakOp ? "Break" : "Continue"
+ );
+ return UACPI_STATUS_AML_BAD_ENCODING;
+ }
+
+ for (;;) {
+ if (ctx->cur_block != frame->last_while) {
+ frame_reset_post_end_block(ctx, ctx->cur_block->type);
+ continue;
+ }
+
+ if (op_ctx->op->code == UACPI_AML_OP_BreakOp)
+ frame->code_offset = ctx->cur_block->end;
+ else
+ frame->code_offset = ctx->cur_block->begin;
+ frame_reset_post_end_block(ctx, ctx->cur_block->type);
+ break;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status create_named_scope(struct op_context *op_ctx)
+{
+ uacpi_namespace_node *node;
+ uacpi_object *obj;
+
+ node = item_array_at(&op_ctx->items, 1)->node;
+ obj = item_array_last(&op_ctx->items)->obj;
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_ProcessorOp: {
+ uacpi_processor *proc = obj->processor;
+ proc->id = item_array_at(&op_ctx->items, 2)->immediate;
+ proc->block_address = item_array_at(&op_ctx->items, 3)->immediate;
+ proc->block_length = item_array_at(&op_ctx->items, 4)->immediate;
+ break;
+ }
+
+ case UACPI_AML_OP_PowerResOp: {
+ uacpi_power_resource *power_res = &obj->power_resource;
+ power_res->system_level = item_array_at(&op_ctx->items, 2)->immediate;
+ power_res->resource_order = item_array_at(&op_ctx->items, 3)->immediate;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ node->object = uacpi_create_internal_reference(UACPI_REFERENCE_KIND_NAMED,
+ obj);
+ if (uacpi_unlikely(node->object == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status handle_code_block(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ switch (op_ctx->op->code) {
+ case UACPI_AML_OP_ProcessorOp:
+ case UACPI_AML_OP_PowerResOp:
+ case UACPI_AML_OP_ThermalZoneOp:
+ case UACPI_AML_OP_DeviceOp: {
+ uacpi_status ret;
+
+ ret = create_named_scope(op_ctx);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ UACPI_FALLTHROUGH;
+ }
+ case UACPI_AML_OP_ScopeOp:
+ case UACPI_AML_OP_IfOp:
+ case UACPI_AML_OP_ElseOp:
+ case UACPI_AML_OP_WhileOp: {
+ break;
+ }
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ return begin_block_execution(ctx);
+}
+
+static uacpi_status handle_return(struct execution_context *ctx)
+{
+ uacpi_status ret;
+ uacpi_object *dst = UACPI_NULL;
+
+ ctx->cur_frame->code_offset = ctx->cur_frame->method->size;
+ ret = method_get_ret_object(ctx, &dst);
+
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ if (dst == UACPI_NULL)
+ return UACPI_STATUS_OK;
+
+ /*
+ * Should be possible to move here if method returns a literal
+ * like Return(Buffer { ... }), otherwise we have to copy just to
+ * be safe.
+ */
+ return uacpi_object_assign(
+ dst,
+ item_array_at(&ctx->cur_op_ctx->items, 0)->obj,
+ UACPI_ASSIGN_BEHAVIOR_DEEP_COPY
+ );
+}
+
+static void refresh_ctx_pointers(struct execution_context *ctx)
+{
+ struct call_frame *frame = ctx->cur_frame;
+
+ if (frame == UACPI_NULL) {
+ ctx->cur_op_ctx = UACPI_NULL;
+ ctx->prev_op_ctx = UACPI_NULL;
+ ctx->cur_block = UACPI_NULL;
+ return;
+ }
+
+ ctx->cur_op_ctx = op_context_array_last(&frame->pending_ops);
+ ctx->prev_op_ctx = op_context_array_one_before_last(&frame->pending_ops);
+ ctx->cur_block = code_block_array_last(&frame->code_blocks);
+}
+
+static uacpi_bool ctx_has_non_preempted_op(struct execution_context *ctx)
+{
+ return ctx->cur_op_ctx && !ctx->cur_op_ctx->preempted;
+}
+
+enum op_trace_action_type {
+ OP_TRACE_ACTION_BEGIN,
+ OP_TRACE_ACTION_RESUME,
+ OP_TRACE_ACTION_END,
+};
+
+static const uacpi_char *const op_trace_action_types[3] = {
+ [OP_TRACE_ACTION_BEGIN] = "BEGIN",
+ [OP_TRACE_ACTION_RESUME] = "RESUME",
+ [OP_TRACE_ACTION_END] = "END",
+};
+
+static inline void trace_op(
+ const struct uacpi_op_spec *op, enum op_trace_action_type action
+)
+{
+ uacpi_debug(
+ "%s OP '%s' (0x%04X)\n",
+ op_trace_action_types[action], op->name, op->code
+ );
+}
+
+static inline void trace_pop(uacpi_u8 pop)
+{
+ uacpi_debug(" pOP: %s (0x%02X)\n", uacpi_parse_op_to_string(pop), pop);
+}
+
+static uacpi_status frame_push_args(struct call_frame *frame,
+ struct op_context *op_ctx)
+{
+ uacpi_size i;
+
+ /*
+ * MethodCall items:
+ * items[0] -> method namespace node
+ * items[1] -> immediate that was used for parsing the arguments
+ * items[2...nargs-1] -> method arguments
+ * items[-1] -> return value object
+ *
+ * Here we only care about the arguments though.
+ */
+ for (i = 2; i < item_array_size(&op_ctx->items) - 1; i++) {
+ uacpi_object *src, *dst;
+
+ src = item_array_at(&op_ctx->items, i)->obj;
+
+ dst = uacpi_create_internal_reference(UACPI_REFERENCE_KIND_ARG, src);
+ if (uacpi_unlikely(dst == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ frame->args[i - 2] = dst;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status frame_setup_base_scope(struct call_frame *frame,
+ uacpi_namespace_node *scope,
+ uacpi_control_method *method)
+{
+ struct code_block *block;
+
+ block = code_block_array_alloc(&frame->code_blocks);
+ if (uacpi_unlikely(block == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ block->type = CODE_BLOCK_SCOPE;
+ block->node = scope;
+ block->begin = 0;
+ block->end = method->size;
+ frame->method = method;
+ frame->cur_scope = scope;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status push_new_frame(struct execution_context *ctx,
+ struct call_frame **out_frame)
+{
+ struct call_frame_array *call_stack = &ctx->call_stack;
+ struct call_frame *prev_frame;
+
+ *out_frame = call_frame_array_calloc(call_stack);
+ if (uacpi_unlikely(*out_frame == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ /*
+ * Allocating a new frame might have reallocated the dynamic buffer so our
+ * execution_context members might now be pointing to freed memory.
+ * Refresh them here.
+ */
+ prev_frame = call_frame_array_one_before_last(call_stack);
+ ctx->cur_frame = prev_frame;
+ refresh_ctx_pointers(ctx);
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_bool maybe_end_block(struct execution_context *ctx)
+{
+ struct code_block *block = ctx->cur_block;
+ struct call_frame *cur_frame = ctx->cur_frame;
+
+ if (!block)
+ return UACPI_FALSE;
+ if (cur_frame->code_offset != block->end)
+ return UACPI_FALSE;
+
+ if (block->type == CODE_BLOCK_WHILE)
+ cur_frame->code_offset = block->begin;
+
+ frame_reset_post_end_block(ctx, block->type);
+ return UACPI_TRUE;
+}
+
+static uacpi_status store_to_target(
+ uacpi_object *dst, uacpi_object *src, uacpi_data_view *wtr_response
+)
+{
+ uacpi_status ret;
+
+ switch (dst->type) {
+ case UACPI_OBJECT_DEBUG:
+ ret = debug_store(src);
+ break;
+ case UACPI_OBJECT_REFERENCE:
+ ret = store_to_reference(dst, src, wtr_response);
+ break;
+
+ case UACPI_OBJECT_BUFFER_INDEX:
+ src = uacpi_unwrap_internal_reference(src);
+ ret = object_assign_with_implicit_cast(dst, src, wtr_response);
+ break;
+
+ case UACPI_OBJECT_INTEGER:
+ // NULL target
+ if (dst->integer == 0) {
+ ret = UACPI_STATUS_OK;
+ break;
+ }
+ UACPI_FALLTHROUGH;
+ default:
+ uacpi_error("attempted to store to an invalid target: %s\n",
+ uacpi_object_type_to_string(dst->type));
+ ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return ret;
+}
+
+static uacpi_status handle_copy_object_or_store(struct execution_context *ctx)
+{
+ uacpi_object *src, *dst;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (op_ctx->op->code == UACPI_AML_OP_StoreOp) {
+ uacpi_status ret;
+ uacpi_data_view wtr_response = { 0 };
+
+ ret = store_to_target(dst, src, &wtr_response);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ /*
+ * This was a write-then-read field access since we got a response
+ * buffer back from this store. Now we have to return this buffer
+ * as a prvalue from the StoreOp so that it can be used by AML to
+ * retrieve the response.
+ */
+ if (wtr_response.data != UACPI_NULL) {
+ uacpi_object *wtr_response_obj;
+
+ wtr_response_obj = uacpi_create_object(UACPI_OBJECT_BUFFER);
+ if (uacpi_unlikely(wtr_response_obj == UACPI_NULL)) {
+ uacpi_free(wtr_response.data, wtr_response.length);
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ }
+
+ wtr_response_obj->buffer->data = wtr_response.data;
+ wtr_response_obj->buffer->size = wtr_response.length;
+
+ uacpi_object_unref(src);
+ item_array_at(&op_ctx->items, 0)->obj = wtr_response_obj;
+ }
+
+ return ret;
+ }
+
+ if (dst->type != UACPI_OBJECT_REFERENCE)
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+
+ return copy_object_to_reference(dst, src);
+}
+
+static uacpi_status handle_inc_dec(struct execution_context *ctx)
+{
+ uacpi_object *src, *dst;
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+ uacpi_bool field_allowed = UACPI_FALSE;
+ uacpi_object_type true_src_type;
+ uacpi_status ret;
+
+ src = item_array_at(&op_ctx->items, 0)->obj;
+ dst = item_array_at(&op_ctx->items, 1)->obj;
+
+ if (src->type == UACPI_OBJECT_REFERENCE) {
+ /*
+ * Increment/Decrement are the only two operators that modify the value
+ * in-place, thus we need very specific dereference rules here.
+ *
+ * Reading buffer fields & field units is only allowed if we were passed
+ * a namestring directly as opposed to some nested reference chain
+ * containing a field at the bottom.
+ */
+ if (src->flags == UACPI_REFERENCE_KIND_NAMED)
+ field_allowed = src->inner_object->type != UACPI_OBJECT_REFERENCE;
+
+ src = reference_unwind(src)->inner_object;
+ } // else buffer index
+
+ true_src_type = src->type;
+
+ switch (true_src_type) {
+ case UACPI_OBJECT_INTEGER:
+ dst->integer = src->integer;
+ break;
+ case UACPI_OBJECT_FIELD_UNIT:
+ case UACPI_OBJECT_BUFFER_FIELD:
+ if (uacpi_unlikely(!field_allowed))
+ goto out_bad_type;
+
+ ret = field_get_read_type(src, &true_src_type);
+ if (uacpi_unlikely_error(ret))
+ goto out_bad_type;
+ if (true_src_type != UACPI_OBJECT_INTEGER)
+ goto out_bad_type;
+
+ if (src->type == UACPI_OBJECT_FIELD_UNIT) {
+ ret = uacpi_read_field_unit(
+ src->field_unit, &dst->integer, sizeof_int(),
+ UACPI_NULL
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ } else {
+ uacpi_read_buffer_field(&src->buffer_field, &dst->integer);
+ }
+ break;
+ case UACPI_OBJECT_BUFFER_INDEX:
+ dst->integer = *buffer_index_cursor(&src->buffer_index);
+ break;
+ default:
+ goto out_bad_type;
+ }
+
+ if (op_ctx->op->code == UACPI_AML_OP_IncrementOp)
+ dst->integer++;
+ else
+ dst->integer--;
+
+ return UACPI_STATUS_OK;
+
+out_bad_type:
+ uacpi_error("Increment/Decrement: invalid object type '%s'\n",
+ uacpi_object_type_to_string(true_src_type));
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+}
+
+static uacpi_status enter_method(
+ struct execution_context *ctx, struct call_frame *new_frame,
+ uacpi_control_method *method
+)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ uacpi_shareable_ref(method);
+
+ if (!method->is_serialized)
+ return ret;
+
+ if (uacpi_unlikely(ctx->sync_level > method->sync_level)) {
+ uacpi_error(
+ "cannot invoke method @%p, sync level %d is too low "
+ "(current is %d)\n",
+ method, method->sync_level, ctx->sync_level
+ );
+ return UACPI_STATUS_AML_SYNC_LEVEL_TOO_HIGH;
+ }
+
+ if (method->mutex == UACPI_NULL) {
+ method->mutex = uacpi_create_mutex();
+ if (uacpi_unlikely(method->mutex == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ method->mutex->sync_level = method->sync_level;
+ }
+
+ if (!uacpi_this_thread_owns_aml_mutex(method->mutex)) {
+ ret = uacpi_acquire_aml_mutex(method->mutex, 0xFFFF);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = held_mutexes_array_push(&ctx->held_mutexes, method->mutex);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_release_aml_mutex(method->mutex);
+ return ret;
+ }
+ }
+
+ new_frame->prev_sync_level = ctx->sync_level;
+ ctx->sync_level = method->sync_level;
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status push_op(struct execution_context *ctx)
+{
+ struct call_frame *frame = ctx->cur_frame;
+ struct op_context *op_ctx;
+
+ op_ctx = op_context_array_calloc(&frame->pending_ops);
+ if (op_ctx == UACPI_NULL)
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ op_ctx->op = ctx->cur_op;
+ refresh_ctx_pointers(ctx);
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_bool pop_item(struct op_context *op_ctx)
+{
+ struct item *item;
+
+ if (item_array_size(&op_ctx->items) == 0)
+ return UACPI_FALSE;
+
+ item = item_array_last(&op_ctx->items);
+
+ if (item->type == ITEM_OBJECT)
+ uacpi_object_unref(item->obj);
+
+ if (item->type == ITEM_NAMESPACE_NODE)
+ uacpi_namespace_node_unref(item->node);
+
+ item_array_pop(&op_ctx->items);
+ return UACPI_TRUE;
+}
+
+static void pop_op(struct execution_context *ctx)
+{
+ struct call_frame *frame = ctx->cur_frame;
+ struct op_context *cur_op_ctx = ctx->cur_op_ctx;
+
+ while (pop_item(cur_op_ctx));
+
+ item_array_clear(&cur_op_ctx->items);
+ op_context_array_pop(&frame->pending_ops);
+ refresh_ctx_pointers(ctx);
+}
+
+static void call_frame_clear(struct call_frame *frame)
+{
+ uacpi_size i;
+ op_context_array_clear(&frame->pending_ops);
+ code_block_array_clear(&frame->code_blocks);
+
+ while (temp_namespace_node_array_size(&frame->temp_nodes) != 0) {
+ uacpi_namespace_node *node;
+
+ node = *temp_namespace_node_array_last(&frame->temp_nodes);
+ uacpi_namespace_node_uninstall(node);
+ temp_namespace_node_array_pop(&frame->temp_nodes);
+ }
+ temp_namespace_node_array_clear(&frame->temp_nodes);
+
+ for (i = 0; i < 7; ++i)
+ uacpi_object_unref(frame->args[i]);
+ for (i = 0; i < 8; ++i)
+ uacpi_object_unref(frame->locals[i]);
+
+ uacpi_method_unref(frame->method);
+}
+
+static uacpi_u8 parse_op_generates_item[0x100] = {
+ [UACPI_PARSE_OP_SIMPLE_NAME] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_SUPERNAME] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_TERM_ARG] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_OPERAND] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_STRING] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_COMPUTATIONAL_DATA] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_TARGET] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_PKGLEN] = ITEM_PACKAGE_LENGTH,
+ [UACPI_PARSE_OP_TRACKED_PKGLEN] = ITEM_PACKAGE_LENGTH,
+ [UACPI_PARSE_OP_CREATE_NAMESTRING] = ITEM_NAMESPACE_NODE,
+ [UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD] = ITEM_NAMESPACE_NODE,
+ [UACPI_PARSE_OP_EXISTING_NAMESTRING] = ITEM_NAMESPACE_NODE,
+ [UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL] = ITEM_NAMESPACE_NODE,
+ [UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL_IF_LOAD] = ITEM_NAMESPACE_NODE,
+ [UACPI_PARSE_OP_LOAD_INLINE_IMM_AS_OBJECT] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_LOAD_INLINE_IMM] = ITEM_IMMEDIATE,
+ [UACPI_PARSE_OP_LOAD_ZERO_IMM] = ITEM_IMMEDIATE,
+ [UACPI_PARSE_OP_LOAD_IMM] = ITEM_IMMEDIATE,
+ [UACPI_PARSE_OP_LOAD_IMM_AS_OBJECT] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_LOAD_FALSE_OBJECT] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_LOAD_TRUE_OBJECT] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_OBJECT_ALLOC] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_OBJECT_ALLOC_TYPED] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_EMPTY_OBJECT_ALLOC] = ITEM_EMPTY_OBJECT,
+ [UACPI_PARSE_OP_OBJECT_CONVERT_TO_SHALLOW_COPY] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_OBJECT_CONVERT_TO_DEEP_COPY] = ITEM_OBJECT,
+ [UACPI_PARSE_OP_RECORD_AML_PC] = ITEM_IMMEDIATE,
+};
+
+static const uacpi_u8 *op_decode_cursor(const struct op_context *ctx)
+{
+ const struct uacpi_op_spec *spec = ctx->op;
+
+ if (spec->properties & UACPI_OP_PROPERTY_OUT_OF_LINE)
+ return &spec->indirect_decode_ops[ctx->pc];
+
+ return &spec->decode_ops[ctx->pc];
+}
+
+static uacpi_u8 op_decode_byte(struct op_context *ctx)
+{
+ uacpi_u8 byte;
+
+ byte = *op_decode_cursor(ctx);
+ ctx->pc++;
+
+ return byte;
+}
+
+static uacpi_aml_op op_decode_aml_op(struct op_context *op_ctx)
+{
+ uacpi_aml_op op = 0;
+
+ op |= op_decode_byte(op_ctx);
+ op |= op_decode_byte(op_ctx) << 8;
+
+ return op;
+}
+
+// MSVC doesn't support __VA_OPT__ so we do this weirdness
+#define EXEC_OP_DO_LVL(lvl, reason, ...) \
+ uacpi_##lvl("Op 0x%04X ('%s'): "reason"\n", \
+ op_ctx->op->code, op_ctx->op->name __VA_ARGS__)
+
+#define EXEC_OP_DO_ERR(reason, ...) EXEC_OP_DO_LVL(error, reason, __VA_ARGS__)
+#define EXEC_OP_DO_WARN(reason, ...) EXEC_OP_DO_LVL(warn, reason, __VA_ARGS__)
+
+#define EXEC_OP_ERR_2(reason, arg0, arg1) EXEC_OP_DO_ERR(reason, ,arg0, arg1)
+#define EXEC_OP_ERR_1(reason, arg0) EXEC_OP_DO_ERR(reason, ,arg0)
+#define EXEC_OP_ERR(reason) EXEC_OP_DO_ERR(reason)
+
+#define EXEC_OP_WARN(reason) EXEC_OP_DO_WARN(reason)
+
+#define SPEC_SIMPLE_NAME "SimpleName := NameString | ArgObj | LocalObj"
+#define SPEC_SUPER_NAME \
+ "SuperName := SimpleName | DebugObj | ReferenceTypeOpcode"
+#define SPEC_TERM_ARG \
+ "TermArg := ExpressionOpcode | DataObject | ArgObj | LocalObj"
+#define SPEC_OPERAND "Operand := TermArg => Integer"
+#define SPEC_STRING "String := TermArg => String"
+#define SPEC_TARGET "Target := SuperName | NullName"
+
+#define SPEC_COMPUTATIONAL_DATA \
+ "ComputationalData := ByteConst | WordConst | DWordConst | QWordConst " \
+ "| String | ConstObj | RevisionOp | DefBuffer"
+
+static uacpi_bool op_wants_supername(enum uacpi_parse_op op)
+{
+ switch (op) {
+ case UACPI_PARSE_OP_SIMPLE_NAME:
+ case UACPI_PARSE_OP_SUPERNAME:
+ case UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_TARGET:
+ return UACPI_TRUE;
+ default:
+ return UACPI_FALSE;
+ }
+}
+
+static uacpi_bool op_wants_term_arg_or_operand(enum uacpi_parse_op op)
+{
+ switch (op) {
+ case UACPI_PARSE_OP_TERM_ARG:
+ case UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL:
+ case UACPI_PARSE_OP_OPERAND:
+ case UACPI_PARSE_OP_STRING:
+ case UACPI_PARSE_OP_COMPUTATIONAL_DATA:
+ return UACPI_TRUE;
+ default:
+ return UACPI_FALSE;
+ }
+}
+
+static uacpi_bool op_allows_unresolved(enum uacpi_parse_op op)
+{
+ switch (op) {
+ case UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL:
+ return UACPI_TRUE;
+ default:
+ return UACPI_FALSE;
+ }
+}
+
+static uacpi_bool op_allows_unresolved_if_load(enum uacpi_parse_op op)
+{
+ switch (op) {
+ case UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD:
+ case UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL_IF_LOAD:
+ return UACPI_TRUE;
+ default:
+ return UACPI_FALSE;
+ }
+}
+
+static uacpi_status op_typecheck(const struct op_context *op_ctx,
+ const struct op_context *cur_op_ctx)
+{
+ const uacpi_char *expected_type_str;
+ uacpi_u8 ok_mask = 0;
+ uacpi_u8 props = cur_op_ctx->op->properties;
+
+ switch (*op_decode_cursor(op_ctx)) {
+ // SimpleName := NameString | ArgObj | LocalObj
+ case UACPI_PARSE_OP_SIMPLE_NAME:
+ expected_type_str = SPEC_SIMPLE_NAME;
+ ok_mask |= UACPI_OP_PROPERTY_SIMPLE_NAME;
+ break;
+
+ // Target := SuperName | NullName
+ case UACPI_PARSE_OP_TARGET:
+ expected_type_str = SPEC_TARGET;
+ ok_mask |= UACPI_OP_PROPERTY_TARGET | UACPI_OP_PROPERTY_SUPERNAME;
+ break;
+
+ // SuperName := SimpleName | DebugObj | ReferenceTypeOpcode
+ case UACPI_PARSE_OP_SUPERNAME:
+ case UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED:
+ expected_type_str = SPEC_SUPER_NAME;
+ ok_mask |= UACPI_OP_PROPERTY_SUPERNAME;
+ break;
+
+ // TermArg := ExpressionOpcode | DataObject | ArgObj | LocalObj
+ case UACPI_PARSE_OP_TERM_ARG:
+ case UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_OPERAND:
+ case UACPI_PARSE_OP_STRING:
+ case UACPI_PARSE_OP_COMPUTATIONAL_DATA:
+ expected_type_str = SPEC_TERM_ARG;
+ ok_mask |= UACPI_OP_PROPERTY_TERM_ARG;
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (!(props & ok_mask)) {
+ EXEC_OP_ERR_2("invalid argument: '%s', expected a %s",
+ cur_op_ctx->op->name, expected_type_str);
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ return UACPI_STATUS_OK;
+}
+
+static uacpi_status typecheck_obj(
+ const struct op_context *op_ctx,
+ const uacpi_object *obj,
+ enum uacpi_object_type expected_type,
+ const uacpi_char *spec_desc
+)
+{
+ if (uacpi_likely(obj->type == expected_type))
+ return UACPI_STATUS_OK;
+
+ EXEC_OP_ERR_2("invalid argument type: %s, expected a %s",
+ uacpi_object_type_to_string(obj->type), spec_desc);
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+}
+
+static uacpi_status typecheck_operand(
+ const struct op_context *op_ctx,
+ const uacpi_object *obj
+)
+{
+ return typecheck_obj(op_ctx, obj, UACPI_OBJECT_INTEGER, SPEC_OPERAND);
+}
+
+static uacpi_status typecheck_string(
+ const struct op_context *op_ctx,
+ const uacpi_object *obj
+)
+{
+ return typecheck_obj(op_ctx, obj, UACPI_OBJECT_STRING, SPEC_STRING);
+}
+
+static uacpi_status typecheck_computational_data(
+ const struct op_context *op_ctx,
+ const uacpi_object *obj
+)
+{
+ switch (obj->type) {
+ case UACPI_OBJECT_STRING:
+ case UACPI_OBJECT_BUFFER:
+ case UACPI_OBJECT_INTEGER:
+ return UACPI_STATUS_OK;
+ default:
+ EXEC_OP_ERR_2(
+ "invalid argument type: %s, expected a %s",
+ uacpi_object_type_to_string(obj->type),
+ SPEC_COMPUTATIONAL_DATA
+ );
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+}
+
+static void emit_op_skip_warn(const struct op_context *op_ctx)
+{
+ EXEC_OP_WARN("skipping due to previous errors");
+}
+
+static void trace_named_object_lookup_or_creation_failure(
+ struct call_frame *frame, uacpi_size offset, enum uacpi_parse_op op,
+ uacpi_status ret, enum uacpi_log_level level
+)
+{
+ static const uacpi_char *oom_prefix = "<...>";
+ static const uacpi_char *empty_string = "";
+ static const uacpi_char *unknown_path = "<unknown-path>";
+ static const uacpi_char *invalid_path = "<invalid-path>";
+
+ uacpi_status conv_ret;
+ const uacpi_char *action;
+ const uacpi_char *requested_path_to_print;
+ const uacpi_char *middle_part = UACPI_NULL;
+ const uacpi_char *prefix_path = UACPI_NULL;
+ uacpi_char *requested_path = UACPI_NULL;
+ uacpi_size length;
+ uacpi_bool is_create;
+
+ is_create = op == UACPI_PARSE_OP_CREATE_NAMESTRING ||
+ op == UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD;
+
+ if (is_create)
+ action = "create";
+ else
+ action = "lookup";
+
+ conv_ret = name_string_to_path(
+ frame, offset, &requested_path, &length
+ );
+ if (uacpi_unlikely_error(conv_ret)) {
+ if (conv_ret == UACPI_STATUS_OUT_OF_MEMORY)
+ requested_path_to_print = unknown_path;
+ else
+ requested_path_to_print = invalid_path;
+ } else {
+ requested_path_to_print = requested_path;
+ }
+
+ if (requested_path && requested_path[0] != '\\') {
+ prefix_path = uacpi_namespace_node_generate_absolute_path(
+ frame->cur_scope
+ );
+ if (uacpi_unlikely(prefix_path == UACPI_NULL))
+ prefix_path = oom_prefix;
+
+ if (prefix_path[1] != '\0')
+ middle_part = ".";
+ } else {
+ prefix_path = empty_string;
+ }
+
+ if (middle_part == UACPI_NULL)
+ middle_part = empty_string;
+
+ if (length == 5 && !is_create) {
+ uacpi_log_lvl(
+ level,
+ "unable to %s named object '%s' within (or above) "
+ "scope '%s': %s\n", action, requested_path_to_print,
+ prefix_path, uacpi_status_to_string(ret)
+ );
+ } else {
+ uacpi_log_lvl(
+ level,
+ "unable to %s named object '%s%s%s': %s\n",
+ action, prefix_path, middle_part,
+ requested_path_to_print, uacpi_status_to_string(ret)
+ );
+ }
+
+ uacpi_free(requested_path, length);
+ if (prefix_path != oom_prefix && prefix_path != empty_string)
+ uacpi_free_dynamic_string(prefix_path);
+}
+
+static uacpi_status uninstalled_op_handler(struct execution_context *ctx)
+{
+ struct op_context *op_ctx = ctx->cur_op_ctx;
+
+ EXEC_OP_ERR("no dedicated handler installed");
+ return UACPI_STATUS_UNIMPLEMENTED;
+}
+
+enum op_handler {
+ OP_HANDLER_UNINSTALLED = 0,
+ OP_HANDLER_LOCAL,
+ OP_HANDLER_ARG,
+ OP_HANDLER_STRING,
+ OP_HANDLER_BINARY_MATH,
+ OP_HANDLER_CONTROL_FLOW,
+ OP_HANDLER_CODE_BLOCK,
+ OP_HANDLER_RETURN,
+ OP_HANDLER_CREATE_METHOD,
+ OP_HANDLER_COPY_OBJECT_OR_STORE,
+ OP_HANDLER_INC_DEC,
+ OP_HANDLER_REF_OR_DEREF_OF,
+ OP_HANDLER_LOGICAL_NOT,
+ OP_HANDLER_BINARY_LOGIC,
+ OP_HANDLER_NAMED_OBJECT,
+ OP_HANDLER_BUFFER,
+ OP_HANDLER_PACKAGE,
+ OP_HANDLER_CREATE_NAMED,
+ OP_HANDLER_CREATE_BUFFER_FIELD,
+ OP_HANDLER_READ_FIELD,
+ OP_HANDLER_ALIAS,
+ OP_HANDLER_CONCATENATE,
+ OP_HANDLER_CONCATENATE_RES,
+ OP_HANDLER_SIZEOF,
+ OP_HANDLER_UNARY_MATH,
+ OP_HANDLER_INDEX,
+ OP_HANDLER_OBJECT_TYPE,
+ OP_HANDLER_CREATE_OP_REGION,
+ OP_HANDLER_CREATE_DATA_REGION,
+ OP_HANDLER_CREATE_FIELD,
+ OP_HANDLER_TO,
+ OP_HANDLER_TO_STRING,
+ OP_HANDLER_TIMER,
+ OP_HANDLER_MID,
+ OP_HANDLER_MATCH,
+ OP_HANDLER_CREATE_MUTEX_OR_EVENT,
+ OP_HANDLER_BCD,
+ OP_HANDLER_UNLOAD,
+ OP_HANDLER_LOAD_TABLE,
+ OP_HANDLER_LOAD,
+ OP_HANDLER_STALL_OR_SLEEP,
+ OP_HANDLER_EVENT_CTL,
+ OP_HANDLER_MUTEX_CTL,
+ OP_HANDLER_NOTIFY,
+ OP_HANDLER_FIRMWARE_REQUEST,
+};
+
+static uacpi_status (*op_handlers[])(struct execution_context *ctx) = {
+ /*
+ * All OPs that don't have a handler dispatch to here if
+ * UACPI_PARSE_OP_INVOKE_HANDLER is reached.
+ */
+ [OP_HANDLER_UNINSTALLED] = uninstalled_op_handler,
+ [OP_HANDLER_LOCAL] = handle_local,
+ [OP_HANDLER_ARG] = handle_arg,
+ [OP_HANDLER_NAMED_OBJECT] = handle_named_object,
+ [OP_HANDLER_STRING] = handle_string,
+ [OP_HANDLER_BINARY_MATH] = handle_binary_math,
+ [OP_HANDLER_CONTROL_FLOW] = handle_control_flow,
+ [OP_HANDLER_CODE_BLOCK] = handle_code_block,
+ [OP_HANDLER_RETURN] = handle_return,
+ [OP_HANDLER_CREATE_METHOD] = handle_create_method,
+ [OP_HANDLER_CREATE_MUTEX_OR_EVENT] = handle_create_mutex_or_event,
+ [OP_HANDLER_COPY_OBJECT_OR_STORE] = handle_copy_object_or_store,
+ [OP_HANDLER_INC_DEC] = handle_inc_dec,
+ [OP_HANDLER_REF_OR_DEREF_OF] = handle_ref_or_deref_of,
+ [OP_HANDLER_LOGICAL_NOT] = handle_logical_not,
+ [OP_HANDLER_BINARY_LOGIC] = handle_binary_logic,
+ [OP_HANDLER_BUFFER] = handle_buffer,
+ [OP_HANDLER_PACKAGE] = handle_package,
+ [OP_HANDLER_CREATE_NAMED] = handle_create_named,
+ [OP_HANDLER_CREATE_BUFFER_FIELD] = handle_create_buffer_field,
+ [OP_HANDLER_READ_FIELD] = handle_field_read,
+ [OP_HANDLER_TO] = handle_to,
+ [OP_HANDLER_ALIAS] = handle_create_alias,
+ [OP_HANDLER_CONCATENATE] = handle_concatenate,
+ [OP_HANDLER_CONCATENATE_RES] = handle_concatenate_res,
+ [OP_HANDLER_SIZEOF] = handle_sizeof,
+ [OP_HANDLER_UNARY_MATH] = handle_unary_math,
+ [OP_HANDLER_INDEX] = handle_index,
+ [OP_HANDLER_OBJECT_TYPE] = handle_object_type,
+ [OP_HANDLER_CREATE_OP_REGION] = handle_create_op_region,
+ [OP_HANDLER_CREATE_DATA_REGION] = handle_create_data_region,
+ [OP_HANDLER_CREATE_FIELD] = handle_create_field,
+ [OP_HANDLER_TIMER] = handle_timer,
+ [OP_HANDLER_TO_STRING] = handle_to_string,
+ [OP_HANDLER_MID] = handle_mid,
+ [OP_HANDLER_MATCH] = handle_match,
+ [OP_HANDLER_BCD] = handle_bcd,
+ [OP_HANDLER_UNLOAD] = handle_unload,
+ [OP_HANDLER_LOAD_TABLE] = handle_load_table,
+ [OP_HANDLER_LOAD] = handle_load,
+ [OP_HANDLER_STALL_OR_SLEEP] = handle_stall_or_sleep,
+ [OP_HANDLER_EVENT_CTL] = handle_event_ctl,
+ [OP_HANDLER_MUTEX_CTL] = handle_mutex_ctl,
+ [OP_HANDLER_NOTIFY] = handle_notify,
+ [OP_HANDLER_FIRMWARE_REQUEST] = handle_firmware_request,
+};
+
+static uacpi_u8 handler_idx_of_op[0x100] = {
+ [UACPI_AML_OP_Local0Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local1Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local2Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local3Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local4Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local5Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local6Op] = OP_HANDLER_LOCAL,
+ [UACPI_AML_OP_Local7Op] = OP_HANDLER_LOCAL,
+
+ [UACPI_AML_OP_Arg0Op] = OP_HANDLER_ARG,
+ [UACPI_AML_OP_Arg1Op] = OP_HANDLER_ARG,
+ [UACPI_AML_OP_Arg2Op] = OP_HANDLER_ARG,
+ [UACPI_AML_OP_Arg3Op] = OP_HANDLER_ARG,
+ [UACPI_AML_OP_Arg4Op] = OP_HANDLER_ARG,
+ [UACPI_AML_OP_Arg5Op] = OP_HANDLER_ARG,
+ [UACPI_AML_OP_Arg6Op] = OP_HANDLER_ARG,
+
+ [UACPI_AML_OP_StringPrefix] = OP_HANDLER_STRING,
+
+ [UACPI_AML_OP_AddOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_SubtractOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_MultiplyOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_DivideOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_ShiftLeftOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_ShiftRightOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_AndOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_NandOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_OrOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_NorOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_XorOp] = OP_HANDLER_BINARY_MATH,
+ [UACPI_AML_OP_ModOp] = OP_HANDLER_BINARY_MATH,
+
+ [UACPI_AML_OP_IfOp] = OP_HANDLER_CODE_BLOCK,
+ [UACPI_AML_OP_ElseOp] = OP_HANDLER_CODE_BLOCK,
+ [UACPI_AML_OP_WhileOp] = OP_HANDLER_CODE_BLOCK,
+ [UACPI_AML_OP_ScopeOp] = OP_HANDLER_CODE_BLOCK,
+
+ [UACPI_AML_OP_ContinueOp] = OP_HANDLER_CONTROL_FLOW,
+ [UACPI_AML_OP_BreakOp] = OP_HANDLER_CONTROL_FLOW,
+
+ [UACPI_AML_OP_ReturnOp] = OP_HANDLER_RETURN,
+
+ [UACPI_AML_OP_MethodOp] = OP_HANDLER_CREATE_METHOD,
+
+ [UACPI_AML_OP_StoreOp] = OP_HANDLER_COPY_OBJECT_OR_STORE,
+ [UACPI_AML_OP_CopyObjectOp] = OP_HANDLER_COPY_OBJECT_OR_STORE,
+
+ [UACPI_AML_OP_IncrementOp] = OP_HANDLER_INC_DEC,
+ [UACPI_AML_OP_DecrementOp] = OP_HANDLER_INC_DEC,
+
+ [UACPI_AML_OP_RefOfOp] = OP_HANDLER_REF_OR_DEREF_OF,
+ [UACPI_AML_OP_DerefOfOp] = OP_HANDLER_REF_OR_DEREF_OF,
+
+ [UACPI_AML_OP_LnotOp] = OP_HANDLER_LOGICAL_NOT,
+
+ [UACPI_AML_OP_LEqualOp] = OP_HANDLER_BINARY_LOGIC,
+ [UACPI_AML_OP_LandOp] = OP_HANDLER_BINARY_LOGIC,
+ [UACPI_AML_OP_LorOp] = OP_HANDLER_BINARY_LOGIC,
+ [UACPI_AML_OP_LGreaterOp] = OP_HANDLER_BINARY_LOGIC,
+ [UACPI_AML_OP_LLessOp] = OP_HANDLER_BINARY_LOGIC,
+
+ [UACPI_AML_OP_InternalOpNamedObject] = OP_HANDLER_NAMED_OBJECT,
+
+ [UACPI_AML_OP_BufferOp] = OP_HANDLER_BUFFER,
+
+ [UACPI_AML_OP_PackageOp] = OP_HANDLER_PACKAGE,
+ [UACPI_AML_OP_VarPackageOp] = OP_HANDLER_PACKAGE,
+
+ [UACPI_AML_OP_NameOp] = OP_HANDLER_CREATE_NAMED,
+
+ [UACPI_AML_OP_CreateBitFieldOp] = OP_HANDLER_CREATE_BUFFER_FIELD,
+ [UACPI_AML_OP_CreateByteFieldOp] = OP_HANDLER_CREATE_BUFFER_FIELD,
+ [UACPI_AML_OP_CreateWordFieldOp] = OP_HANDLER_CREATE_BUFFER_FIELD,
+ [UACPI_AML_OP_CreateDWordFieldOp] = OP_HANDLER_CREATE_BUFFER_FIELD,
+ [UACPI_AML_OP_CreateQWordFieldOp] = OP_HANDLER_CREATE_BUFFER_FIELD,
+
+ [UACPI_AML_OP_InternalOpReadFieldAsBuffer] = OP_HANDLER_READ_FIELD,
+ [UACPI_AML_OP_InternalOpReadFieldAsInteger] = OP_HANDLER_READ_FIELD,
+
+ [UACPI_AML_OP_ToIntegerOp] = OP_HANDLER_TO,
+ [UACPI_AML_OP_ToBufferOp] = OP_HANDLER_TO,
+ [UACPI_AML_OP_ToDecimalStringOp] = OP_HANDLER_TO,
+ [UACPI_AML_OP_ToHexStringOp] = OP_HANDLER_TO,
+ [UACPI_AML_OP_ToStringOp] = OP_HANDLER_TO_STRING,
+
+ [UACPI_AML_OP_AliasOp] = OP_HANDLER_ALIAS,
+
+ [UACPI_AML_OP_ConcatOp] = OP_HANDLER_CONCATENATE,
+ [UACPI_AML_OP_ConcatResOp] = OP_HANDLER_CONCATENATE_RES,
+
+ [UACPI_AML_OP_SizeOfOp] = OP_HANDLER_SIZEOF,
+
+ [UACPI_AML_OP_NotOp] = OP_HANDLER_UNARY_MATH,
+ [UACPI_AML_OP_FindSetLeftBitOp] = OP_HANDLER_UNARY_MATH,
+ [UACPI_AML_OP_FindSetRightBitOp] = OP_HANDLER_UNARY_MATH,
+
+ [UACPI_AML_OP_IndexOp] = OP_HANDLER_INDEX,
+
+ [UACPI_AML_OP_ObjectTypeOp] = OP_HANDLER_OBJECT_TYPE,
+
+ [UACPI_AML_OP_MidOp] = OP_HANDLER_MID,
+
+ [UACPI_AML_OP_MatchOp] = OP_HANDLER_MATCH,
+
+ [UACPI_AML_OP_NotifyOp] = OP_HANDLER_NOTIFY,
+
+ [UACPI_AML_OP_BreakPointOp] = OP_HANDLER_FIRMWARE_REQUEST,
+};
+
+#define EXT_OP_IDX(op) (op & 0xFF)
+
+static uacpi_u8 handler_idx_of_ext_op[0x100] = {
+ [EXT_OP_IDX(UACPI_AML_OP_CreateFieldOp)] = OP_HANDLER_CREATE_BUFFER_FIELD,
+ [EXT_OP_IDX(UACPI_AML_OP_CondRefOfOp)] = OP_HANDLER_REF_OR_DEREF_OF,
+ [EXT_OP_IDX(UACPI_AML_OP_OpRegionOp)] = OP_HANDLER_CREATE_OP_REGION,
+ [EXT_OP_IDX(UACPI_AML_OP_DeviceOp)] = OP_HANDLER_CODE_BLOCK,
+ [EXT_OP_IDX(UACPI_AML_OP_ProcessorOp)] = OP_HANDLER_CODE_BLOCK,
+ [EXT_OP_IDX(UACPI_AML_OP_PowerResOp)] = OP_HANDLER_CODE_BLOCK,
+ [EXT_OP_IDX(UACPI_AML_OP_ThermalZoneOp)] = OP_HANDLER_CODE_BLOCK,
+ [EXT_OP_IDX(UACPI_AML_OP_TimerOp)] = OP_HANDLER_TIMER,
+ [EXT_OP_IDX(UACPI_AML_OP_MutexOp)] = OP_HANDLER_CREATE_MUTEX_OR_EVENT,
+ [EXT_OP_IDX(UACPI_AML_OP_EventOp)] = OP_HANDLER_CREATE_MUTEX_OR_EVENT,
+
+ [EXT_OP_IDX(UACPI_AML_OP_FieldOp)] = OP_HANDLER_CREATE_FIELD,
+ [EXT_OP_IDX(UACPI_AML_OP_IndexFieldOp)] = OP_HANDLER_CREATE_FIELD,
+ [EXT_OP_IDX(UACPI_AML_OP_BankFieldOp)] = OP_HANDLER_CREATE_FIELD,
+
+ [EXT_OP_IDX(UACPI_AML_OP_FromBCDOp)] = OP_HANDLER_BCD,
+ [EXT_OP_IDX(UACPI_AML_OP_ToBCDOp)] = OP_HANDLER_BCD,
+
+ [EXT_OP_IDX(UACPI_AML_OP_DataRegionOp)] = OP_HANDLER_CREATE_DATA_REGION,
+
+ [EXT_OP_IDX(UACPI_AML_OP_LoadTableOp)] = OP_HANDLER_LOAD_TABLE,
+ [EXT_OP_IDX(UACPI_AML_OP_LoadOp)] = OP_HANDLER_LOAD,
+ [EXT_OP_IDX(UACPI_AML_OP_UnloadOp)] = OP_HANDLER_UNLOAD,
+
+ [EXT_OP_IDX(UACPI_AML_OP_StallOp)] = OP_HANDLER_STALL_OR_SLEEP,
+ [EXT_OP_IDX(UACPI_AML_OP_SleepOp)] = OP_HANDLER_STALL_OR_SLEEP,
+
+ [EXT_OP_IDX(UACPI_AML_OP_SignalOp)] = OP_HANDLER_EVENT_CTL,
+ [EXT_OP_IDX(UACPI_AML_OP_ResetOp)] = OP_HANDLER_EVENT_CTL,
+ [EXT_OP_IDX(UACPI_AML_OP_WaitOp)] = OP_HANDLER_EVENT_CTL,
+
+ [EXT_OP_IDX(UACPI_AML_OP_AcquireOp)] = OP_HANDLER_MUTEX_CTL,
+ [EXT_OP_IDX(UACPI_AML_OP_ReleaseOp)] = OP_HANDLER_MUTEX_CTL,
+
+ [EXT_OP_IDX(UACPI_AML_OP_FatalOp)] = OP_HANDLER_FIRMWARE_REQUEST,
+};
+
+enum method_call_type {
+ METHOD_CALL_NATIVE,
+ METHOD_CALL_AML,
+ METHOD_CALL_TABLE_LOAD,
+};
+
+static uacpi_status prepare_method_call(
+ struct execution_context *ctx, uacpi_namespace_node *node,
+ uacpi_control_method *method, enum method_call_type type,
+ const uacpi_object_array *args
+)
+{
+ uacpi_status ret;
+ struct call_frame *frame;
+
+ if (uacpi_unlikely(call_frame_array_size(&ctx->call_stack) >=
+ g_uacpi_rt_ctx.max_call_stack_depth))
+ return UACPI_STATUS_AML_CALL_STACK_DEPTH_LIMIT;
+
+ ret = push_new_frame(ctx, &frame);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ ret = enter_method(ctx, frame, method);
+ if (uacpi_unlikely_error(ret))
+ goto method_dispatch_error;
+
+ if (type == METHOD_CALL_NATIVE) {
+ uacpi_u8 arg_count;
+
+ arg_count = args ? args->count : 0;
+ if (uacpi_unlikely(arg_count != method->args)) {
+ uacpi_error(
+ "invalid number of arguments %zu to call %.4s, expected %d\n",
+ args ? args->count : 0, node->name.text, method->args
+ );
+
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto method_dispatch_error;
+ }
+
+ if (args != UACPI_NULL) {
+ uacpi_u8 i;
+
+ for (i = 0; i < method->args; ++i) {
+ frame->args[i] = args->objects[i];
+ uacpi_object_ref(args->objects[i]);
+ }
+ }
+ } else if (type == METHOD_CALL_AML) {
+ ret = frame_push_args(frame, ctx->cur_op_ctx);
+ if (uacpi_unlikely_error(ret))
+ goto method_dispatch_error;
+ }
+
+ ret = frame_setup_base_scope(frame, node, method);
+ if (uacpi_unlikely_error(ret))
+ goto method_dispatch_error;
+
+ ctx->cur_frame = frame;
+ ctx->cur_op_ctx = UACPI_NULL;
+ ctx->prev_op_ctx = UACPI_NULL;
+ ctx->cur_block = code_block_array_last(&ctx->cur_frame->code_blocks);
+
+ if (method->native_call) {
+ uacpi_object *retval;
+
+ ret = method_get_ret_object(ctx, &retval);
+ if (uacpi_unlikely_error(ret))
+ goto method_dispatch_error;
+
+ return method->handler(ctx, retval);
+ }
+
+ return UACPI_STATUS_OK;
+
+method_dispatch_error:
+ call_frame_clear(frame);
+ call_frame_array_pop(&ctx->call_stack);
+ return ret;
+}
+
+static void apply_tracked_pkg(
+ struct call_frame *frame, struct op_context *op_ctx
+)
+{
+ struct item *item;
+
+ if (op_ctx->tracked_pkg_idx == 0)
+ return;
+
+ item = item_array_at(&op_ctx->items, op_ctx->tracked_pkg_idx - 1);
+ frame->code_offset = item->pkg.end;
+}
+
+static uacpi_status exec_op(struct execution_context *ctx)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ struct call_frame *frame = ctx->cur_frame;
+ struct op_context *op_ctx;
+ struct item *item = UACPI_NULL;
+ enum uacpi_parse_op prev_op = 0, op;
+
+ /*
+ * Allocate a new op context if previous is preempted (looking for a
+ * dynamic argument), or doesn't exist at all.
+ */
+ if (!ctx_has_non_preempted_op(ctx)) {
+ ret = push_op(ctx);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ } else {
+ trace_op(ctx->cur_op_ctx->op, OP_TRACE_ACTION_RESUME);
+ }
+
+ if (ctx->prev_op_ctx)
+ prev_op = *op_decode_cursor(ctx->prev_op_ctx);
+
+ for (;;) {
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ op_ctx = ctx->cur_op_ctx;
+ frame = ctx->cur_frame;
+
+ if (op_ctx->pc == 0 && ctx->prev_op_ctx) {
+ /*
+ * Type check the current arg type against what is expected by the
+ * preempted op. This check is able to catch most type violations
+ * with the only exception being Operand as we only know whether
+ * that evaluates to an integer after the fact.
+ */
+ ret = op_typecheck(ctx->prev_op_ctx, ctx->cur_op_ctx);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+ }
+
+ op = op_decode_byte(op_ctx);
+ trace_pop(op);
+
+ if (parse_op_generates_item[op] != ITEM_NONE) {
+ item = item_array_alloc(&op_ctx->items);
+ if (uacpi_unlikely(item == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ item->type = parse_op_generates_item[op];
+ if (item->type == ITEM_OBJECT) {
+ enum uacpi_object_type type = UACPI_OBJECT_UNINITIALIZED;
+
+ if (op == UACPI_PARSE_OP_OBJECT_ALLOC_TYPED)
+ type = op_decode_byte(op_ctx);
+
+ item->obj = uacpi_create_object(type);
+ if (uacpi_unlikely(item->obj == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+ } else {
+ uacpi_memzero(&item->immediate, sizeof(item->immediate));
+ }
+ } else if (item == UACPI_NULL) {
+ item = item_array_last(&op_ctx->items);
+ }
+
+ switch (op) {
+ case UACPI_PARSE_OP_END:
+ case UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL: {
+ trace_op(ctx->cur_op_ctx->op, OP_TRACE_ACTION_END);
+
+ if (op == UACPI_PARSE_OP_SKIP_WITH_WARN_IF_NULL) {
+ uacpi_u8 idx;
+
+ idx = op_decode_byte(op_ctx);
+ if (item_array_at(&op_ctx->items, idx)->handle != UACPI_NULL)
+ break;
+
+ emit_op_skip_warn(op_ctx);
+ }
+
+ apply_tracked_pkg(frame, op_ctx);
+
+ pop_op(ctx);
+ if (ctx->cur_op_ctx) {
+ ctx->cur_op_ctx->preempted = UACPI_FALSE;
+ ctx->cur_op_ctx->pc++;
+ }
+
+ return UACPI_STATUS_OK;
+ }
+
+ case UACPI_PARSE_OP_EMIT_SKIP_WARN:
+ emit_op_skip_warn(op_ctx);
+ break;
+
+ case UACPI_PARSE_OP_SIMPLE_NAME:
+ case UACPI_PARSE_OP_SUPERNAME:
+ case UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_TERM_ARG:
+ case UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_OPERAND:
+ case UACPI_PARSE_OP_STRING:
+ case UACPI_PARSE_OP_COMPUTATIONAL_DATA:
+ case UACPI_PARSE_OP_TARGET:
+ /*
+ * Preempt this op parsing for now as we wait for the dynamic arg
+ * to be parsed.
+ */
+ op_ctx->preempted = UACPI_TRUE;
+ op_ctx->pc--;
+ return UACPI_STATUS_OK;
+
+ case UACPI_PARSE_OP_TRACKED_PKGLEN:
+ op_ctx->tracked_pkg_idx = item_array_size(&op_ctx->items);
+ UACPI_FALLTHROUGH;
+ case UACPI_PARSE_OP_PKGLEN:
+ ret = parse_package_length(frame, &item->pkg);
+ break;
+
+ case UACPI_PARSE_OP_LOAD_INLINE_IMM:
+ case UACPI_PARSE_OP_LOAD_INLINE_IMM_AS_OBJECT: {
+ void *dst;
+ uacpi_u8 src_width;
+
+ if (op == UACPI_PARSE_OP_LOAD_INLINE_IMM_AS_OBJECT) {
+ item->obj->type = UACPI_OBJECT_INTEGER;
+ dst = &item->obj->integer;
+ src_width = 8;
+ } else {
+ dst = &item->immediate;
+ src_width = op_decode_byte(op_ctx);
+ }
+
+ uacpi_memcpy_zerout(
+ dst, op_decode_cursor(op_ctx),
+ sizeof(uacpi_u64), src_width
+ );
+ op_ctx->pc += src_width;
+ break;
+ }
+
+ case UACPI_PARSE_OP_LOAD_ZERO_IMM:
+ break;
+
+ case UACPI_PARSE_OP_LOAD_IMM:
+ case UACPI_PARSE_OP_LOAD_IMM_AS_OBJECT: {
+ uacpi_u8 width;
+ void *dst;
+
+ width = op_decode_byte(op_ctx);
+ if (uacpi_unlikely(call_frame_code_bytes_left(frame) < width))
+ return UACPI_STATUS_AML_BAD_ENCODING;
+
+ if (op == UACPI_PARSE_OP_LOAD_IMM_AS_OBJECT) {
+ item->obj->type = UACPI_OBJECT_INTEGER;
+ item->obj->integer = 0;
+ dst = &item->obj->integer;
+ } else {
+ dst = item->immediate_bytes;
+ }
+
+ uacpi_memcpy(dst, call_frame_cursor(frame), width);
+ frame->code_offset += width;
+ break;
+ }
+
+ case UACPI_PARSE_OP_LOAD_FALSE_OBJECT:
+ case UACPI_PARSE_OP_LOAD_TRUE_OBJECT: {
+ uacpi_object *obj = item->obj;
+ obj->type = UACPI_OBJECT_INTEGER;
+ obj->integer = op == UACPI_PARSE_OP_LOAD_FALSE_OBJECT ? 0 : ones();
+ break;
+ }
+
+ case UACPI_PARSE_OP_RECORD_AML_PC:
+ item->immediate = frame->code_offset;
+ break;
+
+ case UACPI_PARSE_OP_TRUNCATE_NUMBER:
+ truncate_number_if_needed(item->obj);
+ break;
+
+ case UACPI_PARSE_OP_TYPECHECK: {
+ enum uacpi_object_type expected_type;
+
+ expected_type = op_decode_byte(op_ctx);
+
+ if (uacpi_unlikely(item->obj->type != expected_type)) {
+ EXEC_OP_ERR_2("bad object type: expected %s, got %s!",
+ uacpi_object_type_to_string(expected_type),
+ uacpi_object_type_to_string(item->obj->type));
+ ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ break;
+ }
+
+ case UACPI_PARSE_OP_BAD_OPCODE:
+ case UACPI_PARSE_OP_UNREACHABLE:
+ EXEC_OP_ERR("invalid/unexpected opcode");
+ ret = UACPI_STATUS_AML_INVALID_OPCODE;
+ break;
+
+ case UACPI_PARSE_OP_AML_PC_DECREMENT:
+ frame->code_offset--;
+ break;
+
+ case UACPI_PARSE_OP_IMM_DECREMENT:
+ item_array_at(&op_ctx->items, op_decode_byte(op_ctx))->immediate--;
+ break;
+
+ case UACPI_PARSE_OP_ITEM_POP:
+ pop_item(op_ctx);
+ item = item_array_last(&op_ctx->items);
+ break;
+
+ case UACPI_PARSE_OP_IF_HAS_DATA: {
+ uacpi_size pkg_idx = op_ctx->tracked_pkg_idx - 1;
+ struct package_length *pkg;
+ uacpi_u8 bytes_skip;
+
+ bytes_skip = op_decode_byte(op_ctx);
+ pkg = &item_array_at(&op_ctx->items, pkg_idx)->pkg;
+
+ if (frame->code_offset >= pkg->end)
+ op_ctx->pc += bytes_skip;
+
+ break;
+ }
+
+ case UACPI_PARSE_OP_IF_NOT_NULL:
+ case UACPI_PARSE_OP_IF_NULL:
+ case UACPI_PARSE_OP_IF_LAST_NULL:
+ case UACPI_PARSE_OP_IF_LAST_NOT_NULL: {
+ uacpi_u8 idx, bytes_skip;
+ uacpi_bool is_null, skip_if_null;
+
+ if (op == UACPI_PARSE_OP_IF_LAST_NULL ||
+ op == UACPI_PARSE_OP_IF_LAST_NOT_NULL) {
+ is_null = item->handle == UACPI_NULL;
+ } else {
+ idx = op_decode_byte(op_ctx);
+ is_null = item_array_at(&op_ctx->items, idx)->handle == UACPI_NULL;
+ }
+
+ bytes_skip = op_decode_byte(op_ctx);
+ skip_if_null = op == UACPI_PARSE_OP_IF_NOT_NULL ||
+ op == UACPI_PARSE_OP_IF_LAST_NOT_NULL;
+
+ if (is_null == skip_if_null)
+ op_ctx->pc += bytes_skip;
+
+ break;
+ }
+
+ case UACPI_PARSE_OP_IF_LAST_EQUALS: {
+ uacpi_u8 value, bytes_skip;
+
+ value = op_decode_byte(op_ctx);
+ bytes_skip = op_decode_byte(op_ctx);
+
+ if (item->immediate != value)
+ op_ctx->pc += bytes_skip;
+
+ break;
+ }
+
+ case UACPI_PARSE_OP_IF_LAST_FALSE:
+ case UACPI_PARSE_OP_IF_LAST_TRUE: {
+ uacpi_u8 bytes_skip;
+ uacpi_bool is_false, skip_if_false;
+
+ bytes_skip = op_decode_byte(op_ctx);
+ is_false = item->obj->integer == 0;
+ skip_if_false = op == UACPI_PARSE_OP_IF_LAST_TRUE;
+
+ if (is_false == skip_if_false)
+ op_ctx->pc += bytes_skip;
+
+ break;
+ }
+
+ case UACPI_PARSE_OP_JMP: {
+ op_ctx->pc = op_decode_byte(op_ctx);
+ break;
+ }
+
+ case UACPI_PARSE_OP_CREATE_NAMESTRING:
+ case UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD:
+ case UACPI_PARSE_OP_EXISTING_NAMESTRING:
+ case UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL:
+ case UACPI_PARSE_OP_EXISTING_NAMESTRING_OR_NULL_IF_LOAD: {
+ uacpi_size offset = frame->code_offset;
+ enum resolve_behavior behavior;
+
+ if (op == UACPI_PARSE_OP_CREATE_NAMESTRING ||
+ op == UACPI_PARSE_OP_CREATE_NAMESTRING_OR_NULL_IF_LOAD)
+ behavior = RESOLVE_CREATE_LAST_NAMESEG_FAIL_IF_EXISTS;
+ else
+ behavior = RESOLVE_FAIL_IF_DOESNT_EXIST;
+
+ ret = resolve_name_string(frame, behavior, &item->node);
+
+ if (ret == UACPI_STATUS_NOT_FOUND) {
+ uacpi_bool is_ok;
+
+ if (prev_op) {
+ is_ok = op_allows_unresolved(prev_op);
+ is_ok &= op_allows_unresolved(op);
+ } else {
+ // This is the only standalone op where we allow unresolved
+ is_ok = op_ctx->op->code == UACPI_AML_OP_ExternalOp;
+ }
+
+ if (is_ok)
+ ret = UACPI_STATUS_OK;
+ }
+
+ if (uacpi_unlikely_error(ret)) {
+ enum uacpi_log_level lvl = UACPI_LOG_ERROR;
+ uacpi_status trace_ret = ret;
+ uacpi_bool abort_whileif = UACPI_FALSE;
+
+ if (frame->method->named_objects_persist &&
+ (ret == UACPI_STATUS_AML_OBJECT_ALREADY_EXISTS ||
+ ret == UACPI_STATUS_NOT_FOUND)) {
+ struct op_context *first_ctx;
+
+ first_ctx = op_context_array_at(&frame->pending_ops, 0);
+ abort_whileif = first_ctx->op->code == UACPI_AML_OP_WhileOp ||
+ first_ctx->op->code == UACPI_AML_OP_IfOp;
+
+ if (op_allows_unresolved_if_load(op) || abort_whileif) {
+ lvl = UACPI_LOG_WARN;
+ ret = UACPI_STATUS_OK;
+ }
+ }
+
+ trace_named_object_lookup_or_creation_failure(
+ frame, offset, op, trace_ret, lvl
+ );
+
+ if (abort_whileif) {
+ while (op_context_array_size(&frame->pending_ops) != 1)
+ pop_op(ctx);
+
+ op_ctx = op_context_array_at(&frame->pending_ops, 0);
+ op_ctx->pc++;
+ op_ctx->preempted = UACPI_FALSE;
+ break;
+ }
+
+ if (ret == UACPI_STATUS_NOT_FOUND)
+ ret = UACPI_STATUS_AML_UNDEFINED_REFERENCE;
+ }
+
+ if (behavior == RESOLVE_CREATE_LAST_NAMESEG_FAIL_IF_EXISTS &&
+ !frame->method->named_objects_persist)
+ item->node->flags |= UACPI_NAMESPACE_NODE_FLAG_TEMPORARY;
+
+ break;
+ }
+
+ case UACPI_PARSE_OP_INVOKE_HANDLER: {
+ uacpi_aml_op code = op_ctx->op->code;
+ uacpi_u8 idx;
+
+ if (code <= 0xFF)
+ idx = handler_idx_of_op[code];
+ else
+ idx = handler_idx_of_ext_op[EXT_OP_IDX(code)];
+
+ ret = op_handlers[idx](ctx);
+ break;
+ }
+
+ case UACPI_PARSE_OP_INSTALL_NAMESPACE_NODE:
+ item = item_array_at(&op_ctx->items, op_decode_byte(op_ctx));
+ ret = do_install_node_item(frame, item);
+ break;
+
+ case UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV:
+ case UACPI_PARSE_OP_OBJECT_COPY_TO_PREV: {
+ uacpi_object *src;
+ struct item *dst;
+
+ if (!ctx->prev_op_ctx)
+ break;
+
+ switch (prev_op) {
+ case UACPI_PARSE_OP_TERM_ARG_UNWRAP_INTERNAL:
+ case UACPI_PARSE_OP_COMPUTATIONAL_DATA:
+ case UACPI_PARSE_OP_OPERAND:
+ case UACPI_PARSE_OP_STRING:
+ src = uacpi_unwrap_internal_reference(item->obj);
+
+ if (prev_op == UACPI_PARSE_OP_OPERAND)
+ ret = typecheck_operand(ctx->prev_op_ctx, src);
+ else if (prev_op == UACPI_PARSE_OP_STRING)
+ ret = typecheck_string(ctx->prev_op_ctx, src);
+ else if (prev_op == UACPI_PARSE_OP_COMPUTATIONAL_DATA)
+ ret = typecheck_computational_data(ctx->prev_op_ctx, src);
+
+ break;
+ case UACPI_PARSE_OP_SUPERNAME:
+ case UACPI_PARSE_OP_SUPERNAME_OR_UNRESOLVED:
+ src = item->obj;
+ break;
+
+ case UACPI_PARSE_OP_SIMPLE_NAME:
+ case UACPI_PARSE_OP_TERM_ARG:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED:
+ case UACPI_PARSE_OP_TARGET:
+ src = item->obj;
+ break;
+
+ default:
+ EXEC_OP_ERR_1("don't know how to copy/transfer object to %d",
+ prev_op);
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ break;
+ }
+
+ if (uacpi_likely_success(ret)) {
+ dst = item_array_last(&ctx->prev_op_ctx->items);
+ dst->type = ITEM_OBJECT;
+
+ if (op == UACPI_PARSE_OP_OBJECT_TRANSFER_TO_PREV) {
+ dst->obj = src;
+ uacpi_object_ref(dst->obj);
+ } else {
+ dst->obj = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ if (uacpi_unlikely(dst->obj == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ break;
+ }
+
+ ret = uacpi_object_assign(dst->obj, src,
+ UACPI_ASSIGN_BEHAVIOR_DEEP_COPY);
+ }
+ }
+ break;
+ }
+
+ case UACPI_PARSE_OP_STORE_TO_TARGET:
+ case UACPI_PARSE_OP_STORE_TO_TARGET_INDIRECT: {
+ uacpi_object *dst, *src;
+
+ dst = item_array_at(&op_ctx->items, op_decode_byte(op_ctx))->obj;
+
+ if (op == UACPI_PARSE_OP_STORE_TO_TARGET_INDIRECT) {
+ src = item_array_at(&op_ctx->items,
+ op_decode_byte(op_ctx))->obj;
+ } else {
+ src = item->obj;
+ }
+
+ ret = store_to_target(dst, src, UACPI_NULL);
+ break;
+ }
+
+ // Nothing to do here, object is allocated automatically
+ case UACPI_PARSE_OP_OBJECT_ALLOC:
+ case UACPI_PARSE_OP_OBJECT_ALLOC_TYPED:
+ case UACPI_PARSE_OP_EMPTY_OBJECT_ALLOC:
+ break;
+
+ case UACPI_PARSE_OP_OBJECT_CONVERT_TO_SHALLOW_COPY:
+ case UACPI_PARSE_OP_OBJECT_CONVERT_TO_DEEP_COPY: {
+ uacpi_object *temp = item->obj;
+ enum uacpi_assign_behavior behavior;
+
+ item_array_pop(&op_ctx->items);
+ item = item_array_last(&op_ctx->items);
+
+ if (op == UACPI_PARSE_OP_OBJECT_CONVERT_TO_SHALLOW_COPY)
+ behavior = UACPI_ASSIGN_BEHAVIOR_SHALLOW_COPY;
+ else
+ behavior = UACPI_ASSIGN_BEHAVIOR_DEEP_COPY;
+
+ ret = uacpi_object_assign(temp, item->obj, behavior);
+ if (uacpi_unlikely_error(ret))
+ break;
+
+ uacpi_object_unref(item->obj);
+ item->obj = temp;
+ break;
+ }
+
+ case UACPI_PARSE_OP_DISPATCH_METHOD_CALL: {
+ struct uacpi_namespace_node *node;
+ struct uacpi_control_method *method;
+
+ node = item_array_at(&op_ctx->items, 0)->node;
+ method = uacpi_namespace_node_get_object(node)->method;
+
+ ret = prepare_method_call(
+ ctx, node, method, METHOD_CALL_AML, UACPI_NULL
+ );
+ return ret;
+ }
+
+ case UACPI_PARSE_OP_DISPATCH_TABLE_LOAD: {
+ struct uacpi_namespace_node *node;
+ struct uacpi_control_method *method;
+
+ node = item_array_at(&op_ctx->items, 0)->node;
+ method = item_array_at(&op_ctx->items, 1)->obj->method;
+
+ ret = prepare_method_call(
+ ctx, node, method, METHOD_CALL_TABLE_LOAD, UACPI_NULL
+ );
+ return ret;
+ }
+
+ case UACPI_PARSE_OP_CONVERT_NAMESTRING: {
+ uacpi_aml_op new_op = UACPI_AML_OP_InternalOpNamedObject;
+ uacpi_object *obj;
+
+ if (item->node == UACPI_NULL) {
+ if (!op_allows_unresolved(prev_op))
+ ret = UACPI_STATUS_NOT_FOUND;
+ break;
+ }
+
+ obj = uacpi_namespace_node_get_object(item->node);
+
+ switch (obj->type) {
+ case UACPI_OBJECT_METHOD: {
+ uacpi_bool should_invoke;
+
+ switch (prev_op) {
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT:
+ case UACPI_PARSE_OP_TERM_ARG_OR_NAMED_OBJECT_OR_UNRESOLVED:
+ should_invoke = UACPI_FALSE;
+ break;
+ default:
+ should_invoke = !op_wants_supername(prev_op);
+ }
+
+ if (!should_invoke)
+ break;
+
+ new_op = UACPI_AML_OP_InternalOpMethodCall0Args;
+ new_op += obj->method->args;
+ break;
+ }
+
+ case UACPI_OBJECT_BUFFER_FIELD:
+ case UACPI_OBJECT_FIELD_UNIT: {
+ uacpi_object_type type;
+
+ if (!op_wants_term_arg_or_operand(prev_op))
+ break;
+
+ ret = field_get_read_type(obj, &type);
+ if (uacpi_unlikely_error(ret)) {
+ const uacpi_char *field_path;
+
+ field_path = uacpi_namespace_node_generate_absolute_path(
+ item->node
+ );
+
+ uacpi_error(
+ "unable to perform a read from field %s: "
+ "parent opregion gone\n", field_path
+ );
+ uacpi_free_absolute_path(field_path);
+ }
+
+ switch (type) {
+ case UACPI_OBJECT_BUFFER:
+ new_op = UACPI_AML_OP_InternalOpReadFieldAsBuffer;
+ break;
+ case UACPI_OBJECT_INTEGER:
+ new_op = UACPI_AML_OP_InternalOpReadFieldAsInteger;
+ break;
+ default:
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ continue;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ op_ctx->pc = 0;
+ op_ctx->op = uacpi_get_op_spec(new_op);
+ break;
+ }
+
+ case UACPI_PARSE_OP_SWITCH_TO_NEXT_IF_EQUALS: {
+ uacpi_aml_op op, target_op;
+ uacpi_u32 cur_offset;
+ uacpi_u8 op_length;
+
+ cur_offset = frame->code_offset;
+ apply_tracked_pkg(frame, op_ctx);
+ op_length = peek_next_op(frame, &op);
+
+ target_op = op_decode_aml_op(op_ctx);
+ if (op_length == 0 || op != target_op) {
+ // Revert tracked package
+ frame->code_offset = cur_offset;
+ break;
+ }
+
+ frame->code_offset += op_length;
+ op_ctx->switched_from = op_ctx->op->code;
+ op_ctx->op = uacpi_get_op_spec(target_op);
+ op_ctx->pc = 0;
+ break;
+ }
+
+ case UACPI_PARSE_OP_IF_SWITCHED_FROM: {
+ uacpi_aml_op target_op;
+ uacpi_u8 skip_bytes;
+
+ target_op = op_decode_aml_op(op_ctx);
+ skip_bytes = op_decode_byte(op_ctx);
+
+ if (op_ctx->switched_from != target_op)
+ op_ctx->pc += skip_bytes;
+ break;
+ }
+
+ default:
+ EXEC_OP_ERR_1("unhandled parser op '%d'", op);
+ ret = UACPI_STATUS_UNIMPLEMENTED;
+ break;
+ }
+ }
+}
+
+static void ctx_reload_post_ret(struct execution_context *ctx)
+{
+ uacpi_control_method *method = ctx->cur_frame->method;
+
+ if (method->is_serialized) {
+ held_mutexes_array_remove_and_release(
+ &ctx->held_mutexes, method->mutex, FORCE_RELEASE_YES
+ );
+ ctx->sync_level = ctx->cur_frame->prev_sync_level;
+ }
+
+ call_frame_clear(ctx->cur_frame);
+ call_frame_array_pop(&ctx->call_stack);
+
+ ctx->cur_frame = call_frame_array_last(&ctx->call_stack);
+ refresh_ctx_pointers(ctx);
+}
+
+static void trace_method_abort(struct code_block *block, uacpi_size depth)
+{
+ static const uacpi_char *unknown_path = "<unknown>";
+ uacpi_char oom_absolute_path[9] = "<?>.";
+
+ const uacpi_char *absolute_path;
+
+ if (block != UACPI_NULL && block->type == CODE_BLOCK_SCOPE) {
+ absolute_path = uacpi_namespace_node_generate_absolute_path(block->node);
+ if (uacpi_unlikely(absolute_path == UACPI_NULL))
+ uacpi_memcpy(oom_absolute_path + 4, block->node->name.text, 4);
+ } else {
+ absolute_path = unknown_path;
+ }
+
+ uacpi_error(" #%zu in %s()\n", depth, absolute_path);
+
+ if (absolute_path != oom_absolute_path && absolute_path != unknown_path)
+ uacpi_free_dynamic_string(absolute_path);
+}
+
+static void stack_unwind(struct execution_context *ctx)
+{
+ uacpi_size depth;
+ uacpi_bool should_stop;
+
+ /*
+ * Non-empty call stack here means the execution was aborted at some point,
+ * probably due to a bytecode error.
+ */
+ depth = call_frame_array_size(&ctx->call_stack);
+
+ if (depth != 0) {
+ uacpi_size idx = 0;
+ uacpi_bool table_level_code;
+
+ do {
+ table_level_code = ctx->cur_frame->method->named_objects_persist;
+
+ if (table_level_code && idx != 0)
+ /*
+ * This isn't the first frame that we are aborting.
+ * If this is table-level code, we have just unwound a call
+ * chain that had triggered an abort. Stop here, no need to
+ * abort table load because of it.
+ */
+ break;
+
+ while (op_context_array_size(&ctx->cur_frame->pending_ops) != 0)
+ pop_op(ctx);
+
+ trace_method_abort(
+ code_block_array_at(&ctx->cur_frame->code_blocks, 0), idx
+ );
+
+ should_stop = idx++ == 0 && table_level_code;
+ ctx_reload_post_ret(ctx);
+ } while (--depth && !should_stop);
+ }
+}
+
+static void execution_context_release(struct execution_context *ctx)
+{
+ if (ctx->ret)
+ uacpi_object_unref(ctx->ret);
+
+ while (held_mutexes_array_size(&ctx->held_mutexes) != 0) {
+ held_mutexes_array_remove_and_release(
+ &ctx->held_mutexes,
+ *held_mutexes_array_last(&ctx->held_mutexes),
+ FORCE_RELEASE_YES
+ );
+ }
+
+ call_frame_array_clear(&ctx->call_stack);
+ held_mutexes_array_clear(&ctx->held_mutexes);
+ uacpi_free(ctx, sizeof(*ctx));
+}
+
+uacpi_status uacpi_execute_control_method(
+ uacpi_namespace_node *scope, uacpi_control_method *method,
+ const uacpi_object_array *args, uacpi_object **out_obj
+)
+{
+ uacpi_status ret = UACPI_STATUS_OK;
+ struct execution_context *ctx;
+
+ ctx = uacpi_kernel_alloc_zeroed(sizeof(*ctx));
+ if (uacpi_unlikely(ctx == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ if (out_obj != UACPI_NULL) {
+ ctx->ret = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ if (uacpi_unlikely(ctx->ret == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+ }
+
+ ret = prepare_method_call(ctx, scope, method, METHOD_CALL_NATIVE, args);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+
+ for (;;) {
+ if (!ctx_has_non_preempted_op(ctx)) {
+ if (ctx->cur_frame == UACPI_NULL)
+ break;
+
+ if (maybe_end_block(ctx))
+ continue;
+
+ if (!call_frame_has_code(ctx->cur_frame)) {
+ ctx_reload_post_ret(ctx);
+ continue;
+ }
+
+ ret = get_op(ctx);
+ if (uacpi_unlikely_error(ret))
+ goto handle_method_abort;
+
+ trace_op(ctx->cur_op, OP_TRACE_ACTION_BEGIN);
+ }
+
+ ret = exec_op(ctx);
+ if (uacpi_unlikely_error(ret))
+ goto handle_method_abort;
+
+ continue;
+
+ handle_method_abort:
+ uacpi_error("aborting %s due to previous error: %s\n",
+ ctx->cur_frame->method->named_objects_persist ?
+ "table load" : "method invocation",
+ uacpi_status_to_string(ret));
+ stack_unwind(ctx);
+
+ /*
+ * Having a frame here implies that we just aborted a dynamic table
+ * load. Signal to the caller that it failed by setting the return
+ * value to false.
+ */
+ if (ctx->cur_frame) {
+ struct item *it;
+
+ it = item_array_last(&ctx->cur_op_ctx->items);
+ if (it != UACPI_NULL && it->obj != UACPI_NULL)
+ it->obj->integer = 0;
+ }
+ }
+
+out:
+ if (ctx->ret != UACPI_NULL) {
+ uacpi_object *ret_obj = UACPI_NULL;
+
+ if (ctx->ret->type != UACPI_OBJECT_UNINITIALIZED) {
+ ret_obj = ctx->ret;
+ uacpi_object_ref(ret_obj);
+ }
+
+ *out_obj = ret_obj;
+ }
+
+ execution_context_release(ctx);
+ return ret;
+}
+
+uacpi_status uacpi_osi(uacpi_handle handle, uacpi_object *retval)
+{
+ struct execution_context *ctx = handle;
+ uacpi_bool is_supported;
+ uacpi_status ret;
+ uacpi_object *arg;
+
+ arg = uacpi_unwrap_internal_reference(ctx->cur_frame->args[0]);
+ if (arg->type != UACPI_OBJECT_STRING) {
+ uacpi_error("_OSI: invalid argument type %s, expected a String\n",
+ uacpi_object_type_to_string(arg->type));
+ return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
+ }
+
+ if (retval == UACPI_NULL)
+ return UACPI_STATUS_OK;
+
+ retval->type = UACPI_OBJECT_INTEGER;
+
+ ret = uacpi_handle_osi(arg->buffer->text, &is_supported);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ retval->integer = is_supported ? ones() : 0;
+
+ uacpi_trace("_OSI(%s) => reporting as %ssupported\n",
+ arg->buffer->text, is_supported ? "" : "un");
+ return UACPI_STATUS_OK;
+}
+
+#endif // !UACPI_BAREBONES_MODE