Files
my-os-project2/kernel/hal/x86_64/uACPI/source/uacpi.c
2025-08-17 18:37:57 +02:00

999 lines
28 KiB
C

#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