diff options
Diffstat (limited to 'sys/dev/acpi/uacpi/uacpi.c')
-rw-r--r-- | sys/dev/acpi/uacpi/uacpi.c | 998 |
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 |