summaryrefslogtreecommitdiff
path: root/sys/dev/acpi/uacpi/uacpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/acpi/uacpi/uacpi.c')
-rw-r--r--sys/dev/acpi/uacpi/uacpi.c998
1 files changed, 998 insertions, 0 deletions
diff --git a/sys/dev/acpi/uacpi/uacpi.c b/sys/dev/acpi/uacpi/uacpi.c
new file mode 100644
index 0000000..c6c569f
--- /dev/null
+++ b/sys/dev/acpi/uacpi/uacpi.c
@@ -0,0 +1,998 @@
+#include <uacpi/uacpi.h>
+#include <uacpi/acpi.h>
+
+#include <uacpi/internal/log.h>
+#include <uacpi/internal/context.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/tables.h>
+#include <uacpi/internal/interpreter.h>
+#include <uacpi/internal/namespace.h>
+#include <uacpi/internal/opregion.h>
+#include <uacpi/internal/registers.h>
+#include <uacpi/internal/event.h>
+#include <uacpi/internal/notify.h>
+#include <uacpi/internal/osi.h>
+#include <uacpi/internal/registers.h>
+
+struct uacpi_runtime_context g_uacpi_rt_ctx = { 0 };
+
+void uacpi_context_set_log_level(uacpi_log_level lvl)
+{
+ if (lvl == 0)
+ lvl = UACPI_DEFAULT_LOG_LEVEL;
+
+ g_uacpi_rt_ctx.log_level = lvl;
+}
+
+void uacpi_logger_initialize(void)
+{
+ static uacpi_bool version_printed = UACPI_FALSE;
+
+ if (g_uacpi_rt_ctx.log_level == 0)
+ uacpi_context_set_log_level(UACPI_DEFAULT_LOG_LEVEL);
+
+ if (!version_printed) {
+ version_printed = UACPI_TRUE;
+ uacpi_info(
+ "starting uACPI, version %d.%d.%d\n",
+ UACPI_MAJOR, UACPI_MINOR, UACPI_PATCH
+ );
+ }
+}
+
+void uacpi_context_set_proactive_table_checksum(uacpi_bool setting)
+{
+ if (setting)
+ g_uacpi_rt_ctx.flags |= UACPI_FLAG_PROACTIVE_TBL_CSUM;
+ else
+ g_uacpi_rt_ctx.flags &= ~UACPI_FLAG_PROACTIVE_TBL_CSUM;
+}
+
+const uacpi_char *uacpi_status_to_string(uacpi_status st)
+{
+ switch (st) {
+ case UACPI_STATUS_OK:
+ return "no error";
+ case UACPI_STATUS_MAPPING_FAILED:
+ return "failed to map memory";
+ case UACPI_STATUS_OUT_OF_MEMORY:
+ return "out of memory";
+ case UACPI_STATUS_BAD_CHECKSUM:
+ return "bad table checksum";
+ case UACPI_STATUS_INVALID_SIGNATURE:
+ return "invalid table signature";
+ case UACPI_STATUS_INVALID_TABLE_LENGTH:
+ return "invalid table length";
+ case UACPI_STATUS_NOT_FOUND:
+ return "not found";
+ case UACPI_STATUS_INVALID_ARGUMENT:
+ return "invalid argument";
+ case UACPI_STATUS_UNIMPLEMENTED:
+ return "unimplemented";
+ case UACPI_STATUS_ALREADY_EXISTS:
+ return "already exists";
+ case UACPI_STATUS_INTERNAL_ERROR:
+ return "internal error";
+ case UACPI_STATUS_TYPE_MISMATCH:
+ return "object type mismatch";
+ case UACPI_STATUS_INIT_LEVEL_MISMATCH:
+ return "init level too low/high for this action";
+ case UACPI_STATUS_NAMESPACE_NODE_DANGLING:
+ return "attempting to use a dangling namespace node";
+ case UACPI_STATUS_NO_HANDLER:
+ return "no handler found";
+ case UACPI_STATUS_NO_RESOURCE_END_TAG:
+ return "resource template without an end tag";
+ case UACPI_STATUS_COMPILED_OUT:
+ return "this functionality has been compiled out of this build";
+ case UACPI_STATUS_HARDWARE_TIMEOUT:
+ return "timed out waiting for hardware response";
+ case UACPI_STATUS_TIMEOUT:
+ return "wait timed out";
+ case UACPI_STATUS_OVERRIDDEN:
+ return "the requested action has been overridden";
+ case UACPI_STATUS_DENIED:
+ return "the requested action has been denied";
+
+ case UACPI_STATUS_AML_UNDEFINED_REFERENCE:
+ return "AML referenced an undefined object";
+ case UACPI_STATUS_AML_INVALID_NAMESTRING:
+ return "invalid AML name string";
+ case UACPI_STATUS_AML_OBJECT_ALREADY_EXISTS:
+ return "object already exists";
+ case UACPI_STATUS_AML_INVALID_OPCODE:
+ return "invalid AML opcode";
+ case UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE:
+ return "incompatible AML object type";
+ case UACPI_STATUS_AML_BAD_ENCODING:
+ return "bad AML instruction encoding";
+ case UACPI_STATUS_AML_OUT_OF_BOUNDS_INDEX:
+ return "out of bounds AML index";
+ case UACPI_STATUS_AML_SYNC_LEVEL_TOO_HIGH:
+ return "AML attempted to acquire a mutex with a lower sync level";
+ case UACPI_STATUS_AML_INVALID_RESOURCE:
+ return "invalid resource template encoding or type";
+ case UACPI_STATUS_AML_LOOP_TIMEOUT:
+ return "hanging AML while loop";
+ case UACPI_STATUS_AML_CALL_STACK_DEPTH_LIMIT:
+ return "reached maximum AML call stack depth";
+ default:
+ return "<invalid status>";
+ }
+}
+
+void uacpi_state_reset(void)
+{
+#ifndef UACPI_BAREBONES_MODE
+ uacpi_deinitialize_namespace();
+ uacpi_deinitialize_interfaces();
+ uacpi_deinitialize_events();
+ uacpi_deinitialize_notify();
+ uacpi_deinitialize_opregion();
+#endif
+
+ uacpi_deinitialize_tables();
+
+#ifndef UACPI_BAREBONES_MODE
+
+#ifndef UACPI_REDUCED_HARDWARE
+ if (g_uacpi_rt_ctx.was_in_legacy_mode)
+ uacpi_leave_acpi_mode();
+#endif
+
+ uacpi_deinitialize_registers();
+
+#ifndef UACPI_REDUCED_HARDWARE
+ if (g_uacpi_rt_ctx.global_lock_event)
+ uacpi_kernel_free_event(g_uacpi_rt_ctx.global_lock_event);
+ if (g_uacpi_rt_ctx.global_lock_spinlock)
+ uacpi_kernel_free_spinlock(g_uacpi_rt_ctx.global_lock_spinlock);
+#endif
+
+#endif // !UACPI_BAREBONES_MODE
+
+ uacpi_memzero(&g_uacpi_rt_ctx, sizeof(g_uacpi_rt_ctx));
+
+#if defined(UACPI_KERNEL_INITIALIZATION) && !defined(UACPI_BAREBONES_MODE)
+ uacpi_kernel_deinitialize();
+#endif
+}
+
+#ifndef UACPI_BAREBONES_MODE
+
+void uacpi_context_set_loop_timeout(uacpi_u32 seconds)
+{
+ if (seconds == 0)
+ seconds = UACPI_DEFAULT_LOOP_TIMEOUT_SECONDS;
+
+ g_uacpi_rt_ctx.loop_timeout_seconds = seconds;
+}
+
+void uacpi_context_set_max_call_stack_depth(uacpi_u32 depth)
+{
+ if (depth == 0)
+ depth = UACPI_DEFAULT_MAX_CALL_STACK_DEPTH;
+
+ g_uacpi_rt_ctx.max_call_stack_depth = depth;
+}
+
+uacpi_u32 uacpi_context_get_loop_timeout(void)
+{
+ return g_uacpi_rt_ctx.loop_timeout_seconds;
+}
+
+#ifndef UACPI_REDUCED_HARDWARE
+enum hw_mode {
+ HW_MODE_ACPI = 0,
+ HW_MODE_LEGACY = 1,
+};
+
+static enum hw_mode read_mode(void)
+{
+ uacpi_status ret;
+ uacpi_u64 raw_value;
+ struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt;
+
+ if (!fadt->smi_cmd)
+ return HW_MODE_ACPI;
+
+ ret = uacpi_read_register_field(UACPI_REGISTER_FIELD_SCI_EN, &raw_value);
+ if (uacpi_unlikely_error(ret))
+ return HW_MODE_LEGACY;
+
+ return raw_value ? HW_MODE_ACPI : HW_MODE_LEGACY;
+}
+
+static uacpi_status set_mode(enum hw_mode mode)
+{
+ uacpi_status ret;
+ uacpi_u64 raw_value, stalled_time = 0;
+ struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt;
+
+ if (uacpi_unlikely(!fadt->smi_cmd)) {
+ uacpi_error("SMI_CMD is not implemented by the firmware\n");
+ return UACPI_STATUS_NOT_FOUND;
+ }
+
+ if (uacpi_unlikely(!fadt->acpi_enable && !fadt->acpi_disable)) {
+ uacpi_error("mode transition is not implemented by the hardware\n");
+ return UACPI_STATUS_NOT_FOUND;
+ }
+
+ switch (mode) {
+ case HW_MODE_ACPI:
+ raw_value = fadt->acpi_enable;
+ break;
+ case HW_MODE_LEGACY:
+ raw_value = fadt->acpi_disable;
+ break;
+ default:
+ return UACPI_STATUS_INVALID_ARGUMENT;
+ }
+
+ ret = uacpi_write_register(UACPI_REGISTER_SMI_CMD, raw_value);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ // Allow up to 5 seconds for the hardware to enter the desired mode
+ while (stalled_time < (5 * 1000 * 1000)) {
+ if (read_mode() == mode)
+ return UACPI_STATUS_OK;
+
+ uacpi_kernel_stall(100);
+ stalled_time += 100;
+ }
+
+ uacpi_error("hardware time out while changing modes\n");
+ return UACPI_STATUS_HARDWARE_TIMEOUT;
+}
+
+static uacpi_status enter_mode(enum hw_mode mode, uacpi_bool *did_change)
+{
+ uacpi_status ret;
+ const uacpi_char *mode_str;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ if (uacpi_is_hardware_reduced())
+ return UACPI_STATUS_OK;
+
+ mode_str = mode == HW_MODE_LEGACY ? "legacy" : "acpi";
+
+ if (read_mode() == mode) {
+ uacpi_trace("%s mode already enabled\n", mode_str);
+ return UACPI_STATUS_OK;
+ }
+
+ ret = set_mode(mode);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_warn(
+ "unable to enter %s mode: %s\n",
+ mode_str, uacpi_status_to_string(ret)
+ );
+ return ret;
+ }
+
+ uacpi_trace("entered %s mode\n", mode_str);
+ if (did_change != UACPI_NULL)
+ *did_change = UACPI_TRUE;
+
+ return ret;
+}
+
+uacpi_status uacpi_enter_acpi_mode(void)
+{
+ return enter_mode(HW_MODE_ACPI, UACPI_NULL);
+}
+
+uacpi_status uacpi_leave_acpi_mode(void)
+{
+ return enter_mode(HW_MODE_LEGACY, UACPI_NULL);
+}
+
+static void enter_acpi_mode_initial(void)
+{
+ enter_mode(HW_MODE_ACPI, &g_uacpi_rt_ctx.was_in_legacy_mode);
+}
+#else
+static void enter_acpi_mode_initial(void) { }
+#endif
+
+uacpi_init_level uacpi_get_current_init_level(void)
+{
+ return g_uacpi_rt_ctx.init_level;
+}
+
+uacpi_status uacpi_initialize(uacpi_u64 flags)
+{
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_IS(UACPI_INIT_LEVEL_EARLY);
+
+#ifdef UACPI_KERNEL_INITIALIZATION
+ ret = uacpi_kernel_initialize(UACPI_INIT_LEVEL_EARLY);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+#endif
+
+ g_uacpi_rt_ctx.init_level = UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED;
+ g_uacpi_rt_ctx.last_sleep_typ_a = UACPI_SLEEP_TYP_INVALID;
+ g_uacpi_rt_ctx.last_sleep_typ_b = UACPI_SLEEP_TYP_INVALID;
+ g_uacpi_rt_ctx.s0_sleep_typ_a = UACPI_SLEEP_TYP_INVALID;
+ g_uacpi_rt_ctx.s0_sleep_typ_b = UACPI_SLEEP_TYP_INVALID;
+ g_uacpi_rt_ctx.flags = flags;
+
+ uacpi_logger_initialize();
+
+ if (g_uacpi_rt_ctx.loop_timeout_seconds == 0)
+ uacpi_context_set_loop_timeout(UACPI_DEFAULT_LOOP_TIMEOUT_SECONDS);
+ if (g_uacpi_rt_ctx.max_call_stack_depth == 0)
+ uacpi_context_set_max_call_stack_depth(UACPI_DEFAULT_MAX_CALL_STACK_DEPTH);
+
+ ret = uacpi_initialize_tables();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ ret = uacpi_initialize_registers();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ ret = uacpi_initialize_events_early();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ ret = uacpi_initialize_opregion();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ ret = uacpi_initialize_interfaces();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ ret = uacpi_initialize_namespace();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ ret = uacpi_initialize_notify();
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+
+ uacpi_install_default_address_space_handlers();
+
+ if (!uacpi_check_flag(UACPI_FLAG_NO_ACPI_MODE))
+ enter_acpi_mode_initial();
+
+ return UACPI_STATUS_OK;
+
+out_fatal_error:
+ uacpi_state_reset();
+ return ret;
+}
+
+struct table_load_stats {
+ uacpi_u32 load_counter;
+ uacpi_u32 failure_counter;
+};
+
+static void trace_table_load_failure(
+ struct acpi_sdt_hdr *tbl, uacpi_log_level lvl, uacpi_status ret
+)
+{
+ uacpi_log_lvl(
+ lvl,
+ "failed to load "UACPI_PRI_TBL_HDR": %s\n",
+ UACPI_FMT_TBL_HDR(tbl), uacpi_status_to_string(ret)
+ );
+}
+
+static uacpi_bool match_ssdt_or_psdt(struct uacpi_installed_table *tbl)
+{
+ if (tbl->flags & UACPI_TABLE_LOADED)
+ return UACPI_FALSE;
+
+ return uacpi_signatures_match(tbl->hdr.signature, ACPI_SSDT_SIGNATURE) ||
+ uacpi_signatures_match(tbl->hdr.signature, ACPI_PSDT_SIGNATURE);
+}
+
+static uacpi_u64 elapsed_ms(uacpi_u64 begin_ns, uacpi_u64 end_ns)
+{
+ return (end_ns - begin_ns) / (1000ull * 1000ull);
+}
+
+static uacpi_bool warn_on_bad_timesource(uacpi_u64 begin_ts, uacpi_u64 end_ts)
+{
+ const uacpi_char *reason;
+
+ if (uacpi_unlikely(begin_ts == 0 && end_ts == 0)) {
+ reason = "uacpi_kernel_get_nanoseconds_since_boot() appears to be a stub";
+ goto out_bad_timesource;
+ }
+
+ if (uacpi_unlikely(begin_ts == end_ts)) {
+ reason = "poor time source precision detected";
+ goto out_bad_timesource;
+ }
+
+ if (uacpi_unlikely(end_ts < begin_ts)) {
+ reason = "time source backwards drift detected";
+ goto out_bad_timesource;
+ }
+
+ return UACPI_FALSE;
+
+out_bad_timesource:
+ uacpi_warn("%s, this may cause problems\n", reason);
+ return UACPI_TRUE;
+}
+
+uacpi_status uacpi_namespace_load(void)
+{
+ struct uacpi_table tbl;
+ uacpi_status ret;
+ uacpi_u64 begin_ts, end_ts;
+ struct table_load_stats st = { 0 };
+ uacpi_size cur_index;
+
+ UACPI_ENSURE_INIT_LEVEL_IS(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+#ifdef UACPI_KERNEL_INITIALIZATION
+ ret = uacpi_kernel_initialize(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+ if (uacpi_unlikely_error(ret))
+ goto out_fatal_error;
+#endif
+
+ begin_ts = uacpi_kernel_get_nanoseconds_since_boot();
+
+ ret = uacpi_table_find_by_signature(ACPI_DSDT_SIGNATURE, &tbl);
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error("unable to find DSDT: %s\n", uacpi_status_to_string(ret));
+ goto out_fatal_error;
+ }
+
+ ret = uacpi_table_load_with_cause(tbl.index, UACPI_TABLE_LOAD_CAUSE_INIT);
+ if (uacpi_unlikely_error(ret)) {
+ trace_table_load_failure(tbl.hdr, UACPI_LOG_ERROR, ret);
+ st.failure_counter++;
+ }
+ st.load_counter++;
+ uacpi_table_unref(&tbl);
+
+ for (cur_index = 0;; cur_index = tbl.index + 1) {
+ ret = uacpi_table_match(cur_index, match_ssdt_or_psdt, &tbl);
+ if (ret != UACPI_STATUS_OK) {
+ if (uacpi_unlikely(ret != UACPI_STATUS_NOT_FOUND))
+ goto out_fatal_error;
+
+ break;
+ }
+
+ ret = uacpi_table_load_with_cause(tbl.index, UACPI_TABLE_LOAD_CAUSE_INIT);
+ if (uacpi_unlikely_error(ret)) {
+ trace_table_load_failure(tbl.hdr, UACPI_LOG_WARN, ret);
+ st.failure_counter++;
+ }
+ st.load_counter++;
+ uacpi_table_unref(&tbl);
+ }
+
+ end_ts = uacpi_kernel_get_nanoseconds_since_boot();
+ g_uacpi_rt_ctx.bad_timesource = warn_on_bad_timesource(begin_ts, end_ts);
+
+ if (uacpi_unlikely(st.failure_counter != 0 || g_uacpi_rt_ctx.bad_timesource)) {
+ uacpi_info(
+ "loaded %u AML blob%s (%u error%s)\n",
+ st.load_counter, st.load_counter > 1 ? "s" : "", st.failure_counter,
+ st.failure_counter == 1 ? "" : "s"
+ );
+ } else {
+ uacpi_u64 ops = g_uacpi_rt_ctx.opcodes_executed;
+ uacpi_u64 ops_per_sec = ops * UACPI_NANOSECONDS_PER_SEC;
+
+ ops_per_sec /= end_ts - begin_ts;
+
+ uacpi_info(
+ "successfully loaded %u AML blob%s, %"UACPI_PRIu64" ops in "
+ "%"UACPI_PRIu64"ms (avg %"UACPI_PRIu64"/s)\n",
+ st.load_counter, st.load_counter > 1 ? "s" : "",
+ UACPI_FMT64(ops), UACPI_FMT64(elapsed_ms(begin_ts, end_ts)),
+ UACPI_FMT64(ops_per_sec)
+ );
+ }
+
+ ret = uacpi_initialize_events();
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_error("event initialization failed: %s\n",
+ uacpi_status_to_string(ret));
+ goto out_fatal_error;
+ }
+
+ g_uacpi_rt_ctx.init_level = UACPI_INIT_LEVEL_NAMESPACE_LOADED;
+ return UACPI_STATUS_OK;
+
+out_fatal_error:
+ uacpi_state_reset();
+ return ret;
+}
+
+struct ns_init_context {
+ uacpi_size ini_executed;
+ uacpi_size ini_errors;
+ uacpi_size sta_executed;
+ uacpi_size sta_errors;
+ uacpi_size devices;
+ uacpi_size thermal_zones;
+};
+
+static void ini_eval(struct ns_init_context *ctx, uacpi_namespace_node *node)
+{
+ uacpi_status ret;
+
+ ret = uacpi_eval(node, "_INI", UACPI_NULL, UACPI_NULL);
+ if (ret == UACPI_STATUS_NOT_FOUND)
+ return;
+
+ ctx->ini_executed++;
+ if (uacpi_unlikely_error(ret))
+ ctx->ini_errors++;
+}
+
+static uacpi_status sta_eval(
+ struct ns_init_context *ctx, uacpi_namespace_node *node,
+ uacpi_u32 *value
+)
+{
+ uacpi_status ret;
+
+ ret = uacpi_eval_sta(node, value);
+ if (*value == 0xFFFFFFFF)
+ return ret;
+
+ ctx->sta_executed++;
+ if (uacpi_unlikely_error(ret))
+ ctx->sta_errors++;
+
+ return ret;
+}
+
+static uacpi_iteration_decision do_sta_ini(
+ void *opaque, uacpi_namespace_node *node, uacpi_u32 depth
+)
+{
+ struct ns_init_context *ctx = opaque;
+ uacpi_status ret;
+ uacpi_object_type type = UACPI_OBJECT_UNINITIALIZED;
+ uacpi_u32 sta_ret;
+
+ UACPI_UNUSED(depth);
+
+ // We don't care about aliases
+ if (uacpi_namespace_node_is_alias(node))
+ return UACPI_ITERATION_DECISION_NEXT_PEER;
+
+ ret = uacpi_namespace_node_type(node, &type);
+ switch (type) {
+ case UACPI_OBJECT_DEVICE:
+ case UACPI_OBJECT_PROCESSOR:
+ ctx->devices++;
+ break;
+ case UACPI_OBJECT_THERMAL_ZONE:
+ ctx->thermal_zones++;
+ break;
+ default:
+ if (node != uacpi_namespace_get_predefined(UACPI_PREDEFINED_NAMESPACE_TZ))
+ return UACPI_ITERATION_DECISION_CONTINUE;
+ }
+
+ ret = sta_eval(ctx, node, &sta_ret);
+ if (uacpi_unlikely_error(ret))
+ return UACPI_ITERATION_DECISION_CONTINUE;
+
+ if (!(sta_ret & ACPI_STA_RESULT_DEVICE_PRESENT)) {
+ if (!(sta_ret & ACPI_STA_RESULT_DEVICE_FUNCTIONING))
+ return UACPI_ITERATION_DECISION_NEXT_PEER;
+
+ /*
+ * ACPI 6.5 specification:
+ * _STA may return bit 0 clear (not present) with bit [3] set (device
+ * is functional). This case is used to indicate a valid device for
+ * which no device driver should be loaded (for example, a bridge
+ * device.) Children of this device may be present and valid. OSPM
+ * should continue enumeration below a device whose _STA returns this
+ * bit combination.
+ */
+ return UACPI_ITERATION_DECISION_CONTINUE;
+ }
+
+ ini_eval(ctx, node);
+
+ return UACPI_ITERATION_DECISION_CONTINUE;
+}
+
+uacpi_status uacpi_namespace_initialize(void)
+{
+ struct ns_init_context ctx = { 0 };
+ uacpi_namespace_node *root;
+ uacpi_u64 begin_ts, end_ts;
+ uacpi_address_space_handlers *handlers;
+ uacpi_address_space_handler *handler;
+ uacpi_status ret = UACPI_STATUS_OK;
+
+ UACPI_ENSURE_INIT_LEVEL_IS(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+
+#ifdef UACPI_KERNEL_INITIALIZATION
+ ret = uacpi_kernel_initialize(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
+ if (uacpi_unlikely_error(ret))
+ goto out;
+#endif
+
+ /*
+ * Initialization order here is identical to ACPICA because ACPI
+ * specification doesn't really have any detailed steps that explain
+ * how to do it.
+ */
+
+ root = uacpi_namespace_root();
+
+ begin_ts = uacpi_kernel_get_nanoseconds_since_boot();
+
+ // Step 1 - Execute \_INI
+ ini_eval(&ctx, root);
+
+ // Step 2 - Execute \_SB._INI
+ ini_eval(
+ &ctx, uacpi_namespace_get_predefined(UACPI_PREDEFINED_NAMESPACE_SB)
+ );
+
+ /*
+ * Step 3 - Run _REG methods for all globally installed
+ * address space handlers.
+ */
+ handlers = uacpi_node_get_address_space_handlers(root);
+ if (handlers) {
+ handler = handlers->head;
+
+ while (handler) {
+ if (uacpi_address_space_handler_is_default(handler))
+ uacpi_reg_all_opregions(root, handler->space);
+
+ handler = handler->next;
+ }
+ }
+
+ // Step 4 - Run all other _STA and _INI methods
+ uacpi_namespace_for_each_child(
+ root, do_sta_ini, UACPI_NULL,
+ UACPI_OBJECT_ANY_BIT, UACPI_MAX_DEPTH_ANY, &ctx
+ );
+
+ end_ts = uacpi_kernel_get_nanoseconds_since_boot();
+
+ if (uacpi_likely(!g_uacpi_rt_ctx.bad_timesource)) {
+ uacpi_info(
+ "namespace initialization done in %"UACPI_PRIu64"ms: "
+ "%zu devices, %zu thermal zones\n",
+ UACPI_FMT64(elapsed_ms(begin_ts, end_ts)),
+ ctx.devices, ctx.thermal_zones
+ );
+ } else {
+ uacpi_info(
+ "namespace initialization done: %zu devices, %zu thermal zones\n",
+ ctx.devices, ctx.thermal_zones
+ );
+ }
+
+ uacpi_trace(
+ "_STA calls: %zu (%zu errors), _INI calls: %zu (%zu errors)\n",
+ ctx.sta_executed, ctx.sta_errors, ctx.ini_executed,
+ ctx.ini_errors
+ );
+
+ g_uacpi_rt_ctx.init_level = UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED;
+#ifdef UACPI_KERNEL_INITIALIZATION
+ ret = uacpi_kernel_initialize(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED);
+out:
+ if (uacpi_unlikely_error(ret))
+ uacpi_state_reset();
+#endif
+ return ret;
+}
+
+uacpi_status uacpi_eval(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **out_obj
+)
+{
+ struct uacpi_namespace_node *node;
+ uacpi_control_method *method;
+ uacpi_object *obj;
+ uacpi_status ret = UACPI_STATUS_INVALID_ARGUMENT;
+
+ if (uacpi_unlikely(parent == UACPI_NULL && path == UACPI_NULL))
+ return ret;
+
+ ret = uacpi_namespace_read_lock();
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (path != UACPI_NULL) {
+ ret = uacpi_namespace_node_resolve(
+ parent, path, UACPI_SHOULD_LOCK_NO,
+ UACPI_MAY_SEARCH_ABOVE_PARENT_NO, UACPI_PERMANENT_ONLY_YES,
+ &node
+ );
+ if (uacpi_unlikely_error(ret))
+ goto out_read_unlock;
+ } else {
+ node = parent;
+ }
+
+ obj = uacpi_namespace_node_get_object(node);
+ if (uacpi_unlikely(obj == UACPI_NULL)) {
+ ret = UACPI_STATUS_INVALID_ARGUMENT;
+ goto out_read_unlock;
+ }
+
+ if (obj->type != UACPI_OBJECT_METHOD) {
+ uacpi_object *new_obj;
+
+ if (uacpi_unlikely(out_obj == UACPI_NULL))
+ goto out_read_unlock;
+
+ new_obj = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
+ if (uacpi_unlikely(new_obj == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out_read_unlock;
+ }
+
+ ret = uacpi_object_assign(
+ new_obj, obj, UACPI_ASSIGN_BEHAVIOR_DEEP_COPY
+ );
+ if (uacpi_unlikely_error(ret)) {
+ uacpi_object_unref(new_obj);
+ goto out_read_unlock;
+ }
+ *out_obj = new_obj;
+
+ out_read_unlock:
+ uacpi_namespace_read_unlock();
+ return ret;
+ }
+
+ method = obj->method;
+ uacpi_shareable_ref(method);
+ uacpi_namespace_read_unlock();
+
+ // Upgrade to a write-lock since we're about to run a method
+ ret = uacpi_namespace_write_lock();
+ if (uacpi_unlikely_error(ret))
+ goto out_no_write_lock;
+
+ ret = uacpi_execute_control_method(node, method, args, out_obj);
+ uacpi_namespace_write_unlock();
+
+out_no_write_lock:
+ uacpi_method_unref(method);
+ return ret;
+}
+
+uacpi_status uacpi_eval_simple(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+)
+{
+ return uacpi_eval(parent, path, UACPI_NULL, ret);
+}
+
+uacpi_status uacpi_execute(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args
+)
+{
+ return uacpi_eval(parent, path, args, UACPI_NULL);
+}
+
+uacpi_status uacpi_execute_simple(
+ uacpi_namespace_node *parent, const uacpi_char *path
+)
+{
+ return uacpi_eval(parent, path, UACPI_NULL, UACPI_NULL);
+}
+
+#define TRACE_BAD_RET(path_fmt, type, ...) \
+ uacpi_warn( \
+ "unexpected '%s' object returned by method "path_fmt \
+ ", expected type mask: %08X\n", uacpi_object_type_to_string(type), \
+ __VA_ARGS__ \
+ )
+
+#define TRACE_NO_RET(path_fmt, ...) \
+ uacpi_warn( \
+ "no value returned from method "path_fmt", expected type mask: " \
+ "%08X\n", __VA_ARGS__ \
+ )
+
+static void trace_invalid_return_type(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ uacpi_object_type_bits expected_mask, uacpi_object_type actual_type
+)
+{
+ const uacpi_char *abs_path;
+ uacpi_bool dynamic_abs_path = UACPI_FALSE;
+
+ if (parent == UACPI_NULL || (path != UACPI_NULL && path[0] == '\\')) {
+ abs_path = path;
+ } else {
+ abs_path = uacpi_namespace_node_generate_absolute_path(parent);
+ dynamic_abs_path = UACPI_TRUE;
+ }
+
+ if (dynamic_abs_path && path != UACPI_NULL) {
+ if (actual_type == UACPI_OBJECT_UNINITIALIZED)
+ TRACE_NO_RET("%s.%s", abs_path, path, expected_mask);
+ else
+ TRACE_BAD_RET("%s.%s", actual_type, abs_path, path, expected_mask);
+ } else {
+ if (actual_type == UACPI_OBJECT_UNINITIALIZED) {
+ TRACE_NO_RET("%s", abs_path, expected_mask);
+ } else {
+ TRACE_BAD_RET("%s", actual_type, abs_path, expected_mask);
+ }
+ }
+
+ if (dynamic_abs_path)
+ uacpi_free_dynamic_string(abs_path);
+}
+
+uacpi_status uacpi_eval_typed(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object_type_bits ret_mask,
+ uacpi_object **out_obj
+)
+{
+ uacpi_status ret;
+ uacpi_object *obj;
+ uacpi_object_type returned_type = UACPI_OBJECT_UNINITIALIZED;
+
+ if (uacpi_unlikely(out_obj == UACPI_NULL))
+ return UACPI_STATUS_INVALID_ARGUMENT;
+
+ ret = uacpi_eval(parent, path, args, &obj);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (obj != UACPI_NULL)
+ returned_type = obj->type;
+
+ if (ret_mask && (ret_mask & (1 << returned_type)) == 0) {
+ trace_invalid_return_type(parent, path, ret_mask, returned_type);
+ uacpi_object_unref(obj);
+ return UACPI_STATUS_TYPE_MISMATCH;
+ }
+
+ *out_obj = obj;
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_eval_simple_typed(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ uacpi_object_type_bits ret_mask, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(parent, path, UACPI_NULL, ret_mask, ret);
+}
+
+uacpi_status uacpi_eval_integer(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_u64 *out_value
+)
+{
+ uacpi_object *int_obj;
+ uacpi_status ret;
+
+ ret = uacpi_eval_typed(
+ parent, path, args, UACPI_OBJECT_INTEGER_BIT, &int_obj
+ );
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ *out_value = int_obj->integer;
+ uacpi_object_unref(int_obj);
+
+ return UACPI_STATUS_OK;
+}
+
+uacpi_status uacpi_eval_simple_integer(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_u64 *out_value
+)
+{
+ return uacpi_eval_integer(parent, path, UACPI_NULL, out_value);
+}
+
+uacpi_status uacpi_eval_buffer_or_string(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, args,
+ UACPI_OBJECT_BUFFER_BIT | UACPI_OBJECT_STRING_BIT,
+ ret
+ );
+}
+
+uacpi_status uacpi_eval_simple_buffer_or_string(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, UACPI_NULL,
+ UACPI_OBJECT_BUFFER_BIT | UACPI_OBJECT_STRING_BIT,
+ ret
+ );
+}
+
+uacpi_status uacpi_eval_string(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, args, UACPI_OBJECT_STRING_BIT, ret
+ );
+}
+
+uacpi_status uacpi_eval_simple_string(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, UACPI_NULL, UACPI_OBJECT_STRING_BIT, ret
+ );
+}
+
+uacpi_status uacpi_eval_buffer(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, args, UACPI_OBJECT_BUFFER_BIT, ret
+ );
+}
+
+uacpi_status uacpi_eval_simple_buffer(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, UACPI_NULL, UACPI_OBJECT_BUFFER_BIT, ret
+ );
+}
+
+uacpi_status uacpi_eval_package(
+ uacpi_namespace_node *parent, const uacpi_char *path,
+ const uacpi_object_array *args, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, args, UACPI_OBJECT_PACKAGE_BIT, ret
+ );
+}
+
+uacpi_status uacpi_eval_simple_package(
+ uacpi_namespace_node *parent, const uacpi_char *path, uacpi_object **ret
+)
+{
+ return uacpi_eval_typed(
+ parent, path, UACPI_NULL, UACPI_OBJECT_PACKAGE_BIT, ret
+ );
+}
+
+uacpi_status uacpi_get_aml_bitness(uacpi_u8 *out_bitness)
+{
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ *out_bitness = g_uacpi_rt_ctx.is_rev1 ? 32 : 64;
+ return UACPI_STATUS_OK;
+}
+
+#endif // !UACPI_BAREBONES_MODE