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

1082 lines
29 KiB
C

#include <uacpi/namespace.h>
#include <uacpi/internal/namespace.h>
#include <uacpi/internal/types.h>
#include <uacpi/internal/stdlib.h>
#include <uacpi/internal/interpreter.h>
#include <uacpi/internal/opregion.h>
#include <uacpi/internal/log.h>
#include <uacpi/internal/utilities.h>
#include <uacpi/internal/mutex.h>
#include <uacpi/kernel_api.h>
#ifndef UACPI_BAREBONES_MODE
#define UACPI_REV_VALUE 2
#define UACPI_OS_VALUE "Microsoft Windows NT"
#define MAKE_PREDEFINED(c0, c1, c2, c3) \
{ \
.name.text = { c0, c1, c2, c3 }, \
.flags = UACPI_NAMESPACE_NODE_PREDEFINED \
}
static uacpi_namespace_node
predefined_namespaces[UACPI_PREDEFINED_NAMESPACE_MAX + 1] = {
[UACPI_PREDEFINED_NAMESPACE_ROOT] = MAKE_PREDEFINED('\\', 0, 0, 0),
[UACPI_PREDEFINED_NAMESPACE_GPE] = MAKE_PREDEFINED('_', 'G', 'P', 'E'),
[UACPI_PREDEFINED_NAMESPACE_PR] = MAKE_PREDEFINED('_', 'P', 'R', '_'),
[UACPI_PREDEFINED_NAMESPACE_SB] = MAKE_PREDEFINED('_', 'S', 'B', '_'),
[UACPI_PREDEFINED_NAMESPACE_SI] = MAKE_PREDEFINED('_', 'S', 'I', '_'),
[UACPI_PREDEFINED_NAMESPACE_TZ] = MAKE_PREDEFINED('_', 'T', 'Z', '_'),
[UACPI_PREDEFINED_NAMESPACE_GL] = MAKE_PREDEFINED('_', 'G', 'L', '_'),
[UACPI_PREDEFINED_NAMESPACE_OS] = MAKE_PREDEFINED('_', 'O', 'S', '_'),
[UACPI_PREDEFINED_NAMESPACE_OSI] = MAKE_PREDEFINED('_', 'O', 'S', 'I'),
[UACPI_PREDEFINED_NAMESPACE_REV] = MAKE_PREDEFINED('_', 'R', 'E', 'V'),
};
static struct uacpi_rw_lock namespace_lock;
uacpi_status uacpi_namespace_read_lock(void)
{
return uacpi_rw_lock_read(&namespace_lock);
}
uacpi_status uacpi_namespace_read_unlock(void)
{
return uacpi_rw_unlock_read(&namespace_lock);
}
uacpi_status uacpi_namespace_write_lock(void)
{
return uacpi_rw_lock_write(&namespace_lock);
}
uacpi_status uacpi_namespace_write_unlock(void)
{
return uacpi_rw_unlock_write(&namespace_lock);
}
static uacpi_object *make_object_for_predefined(
enum uacpi_predefined_namespace ns
)
{
uacpi_object *obj;
switch (ns) {
case UACPI_PREDEFINED_NAMESPACE_ROOT:
/*
* The real root object is stored in the global context, whereas the \
* node gets a placeholder uninitialized object instead. This is to
* protect against CopyObject(JUNK, \), so that all of the opregion and
* notify handlers are preserved if AML decides to do that.
*/
g_uacpi_rt_ctx.root_object = uacpi_create_object(UACPI_OBJECT_DEVICE);
if (uacpi_unlikely(g_uacpi_rt_ctx.root_object == UACPI_NULL))
return UACPI_NULL;
obj = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
break;
case UACPI_PREDEFINED_NAMESPACE_OS:
obj = uacpi_create_object(UACPI_OBJECT_STRING);
if (uacpi_unlikely(obj == UACPI_NULL))
return obj;
obj->buffer->text = uacpi_kernel_alloc(sizeof(UACPI_OS_VALUE));
if (uacpi_unlikely(obj->buffer->text == UACPI_NULL)) {
uacpi_object_unref(obj);
return UACPI_NULL;
}
obj->buffer->size = sizeof(UACPI_OS_VALUE);
uacpi_memcpy(obj->buffer->text, UACPI_OS_VALUE, obj->buffer->size);
break;
case UACPI_PREDEFINED_NAMESPACE_REV:
obj = uacpi_create_object(UACPI_OBJECT_INTEGER);
if (uacpi_unlikely(obj == UACPI_NULL))
return obj;
obj->integer = UACPI_REV_VALUE;
break;
case UACPI_PREDEFINED_NAMESPACE_GL:
obj = uacpi_create_object(UACPI_OBJECT_MUTEX);
if (uacpi_likely(obj != UACPI_NULL)) {
uacpi_shareable_ref(obj->mutex);
g_uacpi_rt_ctx.global_lock_mutex = obj->mutex;
}
break;
case UACPI_PREDEFINED_NAMESPACE_OSI:
obj = uacpi_create_object(UACPI_OBJECT_METHOD);
if (uacpi_unlikely(obj == UACPI_NULL))
return obj;
obj->method->native_call = UACPI_TRUE;
obj->method->handler = uacpi_osi;
obj->method->args = 1;
break;
default:
obj = uacpi_create_object(UACPI_OBJECT_UNINITIALIZED);
break;
}
return obj;
}
static void namespace_node_detach_object(uacpi_namespace_node *node)
{
uacpi_object *object;
object = uacpi_namespace_node_get_object(node);
if (object != UACPI_NULL) {
if (object->type == UACPI_OBJECT_OPERATION_REGION)
uacpi_opregion_uninstall_handler(node);
uacpi_object_unref(node->object);
node->object = UACPI_NULL;
}
}
static void free_namespace_node(uacpi_handle handle)
{
uacpi_namespace_node *node = handle;
if (uacpi_likely(!uacpi_namespace_node_is_predefined(node))) {
uacpi_free(node, sizeof(*node));
return;
}
node->flags = UACPI_NAMESPACE_NODE_PREDEFINED;
node->object = UACPI_NULL;
node->parent = UACPI_NULL;
node->child = UACPI_NULL;
node->next = UACPI_NULL;
}
uacpi_status uacpi_initialize_namespace(void)
{
enum uacpi_predefined_namespace ns;
uacpi_object *obj;
uacpi_namespace_node *node;
uacpi_status ret;
ret = uacpi_rw_lock_init(&namespace_lock);
if (uacpi_unlikely_error(ret))
return ret;
for (ns = 0; ns <= UACPI_PREDEFINED_NAMESPACE_MAX; ns++) {
node = &predefined_namespaces[ns];
uacpi_shareable_init(node);
obj = make_object_for_predefined(ns);
if (uacpi_unlikely(obj == UACPI_NULL))
return UACPI_STATUS_OUT_OF_MEMORY;
node->object = uacpi_create_internal_reference(
UACPI_REFERENCE_KIND_NAMED, obj
);
if (uacpi_unlikely(node->object == UACPI_NULL)) {
uacpi_object_unref(obj);
return UACPI_STATUS_OUT_OF_MEMORY;
}
uacpi_object_unref(obj);
}
for (ns = UACPI_PREDEFINED_NAMESPACE_GPE;
ns <= UACPI_PREDEFINED_NAMESPACE_MAX; ns++) {
/*
* Skip the installation of \_OSI if it was disabled by user.
* We still create the object, but it's not attached to the namespace.
*/
if (ns == UACPI_PREDEFINED_NAMESPACE_OSI &&
uacpi_check_flag(UACPI_FLAG_NO_OSI))
continue;
uacpi_namespace_node_install(
uacpi_namespace_root(), &predefined_namespaces[ns]
);
}
return UACPI_STATUS_OK;
}
void uacpi_deinitialize_namespace(void)
{
uacpi_status ret;
uacpi_namespace_node *current, *next = UACPI_NULL;
uacpi_u32 depth = 1;
current = uacpi_namespace_root();
ret = uacpi_namespace_write_lock();
while (depth) {
next = next == UACPI_NULL ? current->child : next->next;
/*
* The previous value of 'next' was the last child of this subtree,
* we can now remove the entire scope of 'current->child'
*/
if (next == UACPI_NULL) {
depth--;
// Wipe the subtree
while (current->child != UACPI_NULL)
uacpi_namespace_node_uninstall(current->child);
// Reset the pointers back as if this iteration never happened
next = current;
current = current->parent;
continue;
}
/*
* We have more nodes to process, proceed to the next one, either the
* child of the 'next' node, if one exists, or its peer
*/
if (next->child) {
depth++;
current = next;
next = UACPI_NULL;
}
// This node has no children, move on to its peer
}
namespace_node_detach_object(uacpi_namespace_root());
free_namespace_node(uacpi_namespace_root());
if (ret == UACPI_STATUS_OK)
uacpi_namespace_write_unlock();
uacpi_object_unref(g_uacpi_rt_ctx.root_object);
g_uacpi_rt_ctx.root_object = UACPI_NULL;
uacpi_mutex_unref(g_uacpi_rt_ctx.global_lock_mutex);
g_uacpi_rt_ctx.global_lock_mutex = UACPI_NULL;
uacpi_rw_lock_deinit(&namespace_lock);
}
uacpi_namespace_node *uacpi_namespace_root(void)
{
return &predefined_namespaces[UACPI_PREDEFINED_NAMESPACE_ROOT];
}
uacpi_namespace_node *uacpi_namespace_get_predefined(
enum uacpi_predefined_namespace ns
)
{
if (uacpi_unlikely(ns > UACPI_PREDEFINED_NAMESPACE_MAX)) {
uacpi_warn("requested invalid predefined namespace %d\n", ns);
return UACPI_NULL;
}
return &predefined_namespaces[ns];
}
uacpi_namespace_node *uacpi_namespace_node_alloc(uacpi_object_name name)
{
uacpi_namespace_node *ret;
ret = uacpi_kernel_alloc_zeroed(sizeof(*ret));
if (uacpi_unlikely(ret == UACPI_NULL))
return ret;
uacpi_shareable_init(ret);
ret->name = name;
return ret;
}
void uacpi_namespace_node_unref(uacpi_namespace_node *node)
{
uacpi_shareable_unref_and_delete_if_last(node, free_namespace_node);
}
uacpi_status uacpi_namespace_node_install(
uacpi_namespace_node *parent,
uacpi_namespace_node *node
)
{
if (parent == UACPI_NULL)
parent = uacpi_namespace_root();
if (uacpi_unlikely(uacpi_namespace_node_is_dangling(node))) {
uacpi_warn("attempting to install a dangling namespace node %.4s\n",
node->name.text);
return UACPI_STATUS_NAMESPACE_NODE_DANGLING;
}
if (parent->child == UACPI_NULL) {
parent->child = node;
} else {
uacpi_namespace_node *prev = parent->child;
while (prev->next != UACPI_NULL)
prev = prev->next;
prev->next = node;
}
node->parent = parent;
return UACPI_STATUS_OK;
}
uacpi_bool uacpi_namespace_node_is_alias(uacpi_namespace_node *node)
{
return node->flags & UACPI_NAMESPACE_NODE_FLAG_ALIAS;
}
uacpi_bool uacpi_namespace_node_is_dangling(uacpi_namespace_node *node)
{
return node->flags & UACPI_NAMESPACE_NODE_FLAG_DANGLING;
}
uacpi_bool uacpi_namespace_node_is_temporary(uacpi_namespace_node *node)
{
return node->flags & UACPI_NAMESPACE_NODE_FLAG_TEMPORARY;
}
uacpi_bool uacpi_namespace_node_is_predefined(uacpi_namespace_node *node)
{
return node->flags & UACPI_NAMESPACE_NODE_PREDEFINED;
}
uacpi_status uacpi_namespace_node_uninstall(uacpi_namespace_node *node)
{
uacpi_namespace_node *prev;
if (uacpi_unlikely(uacpi_namespace_node_is_dangling(node))) {
uacpi_warn("attempting to uninstall a dangling namespace node %.4s\n",
node->name.text);
return UACPI_STATUS_INTERNAL_ERROR;
}
/*
* The way to trigger this is as follows:
*
* Method (FOO) {
* // Temporary device, will be deleted upon returning from FOO
* Device (\BAR) {
* }
*
* //
* // Load TBL where TBL is:
* // Scope (\BAR) {
* // Name (TEST, 123)
* // }
* //
* Load(TBL)
* }
*
* In the above example, TEST is a permanent node attached by bad AML to a
* temporary node created inside the FOO method at \BAR. The cleanup code
* will attempt to remove the \BAR device upon exit from FOO, but that is
* no longer possible as there's now a permanent child attached to it.
*/
if (uacpi_unlikely(node->child != UACPI_NULL)) {
uacpi_warn(
"refusing to uninstall node %.4s with a child (%.4s)\n",
node->name.text, node->child->name.text
);
return UACPI_STATUS_DENIED;
}
/*
* Even though namespace_node is reference-counted it still has an 'invalid'
* state that is entered after it is uninstalled from the global namespace.
*
* Reference counting is only needed to combat dangling pointer issues
* whereas bad AML might try to prolong a local object lifetime by
* returning it from a method, or CopyObject it somewhere. In that case the
* namespace node object itself is still alive, but no longer has a valid
* object associated with it.
*
* Example:
* Method (BAD) {
* OperationRegion(REG, SystemMemory, 0xDEADBEEF, 4)
* Field (REG, AnyAcc, NoLock) {
* FILD, 8,
* }
*
* Return (RefOf(FILD))
* }
*
* // Local0 is now the sole owner of the 'FILD' object that under the
* // hood is still referencing the 'REG' operation region object from
* // the 'BAD' method.
* Local0 = DerefOf(BAD())
*
* This is done to prevent potential very deep recursion where an object
* frees a namespace node that frees an attached object that frees a
* namespace node as well as potential infinite cycles between a namespace
* node and an object.
*/
namespace_node_detach_object(node);
prev = node->parent ? node->parent->child : UACPI_NULL;
if (prev == node) {
node->parent->child = node->next;
} else {
while (uacpi_likely(prev != UACPI_NULL) && prev->next != node)
prev = prev->next;
if (uacpi_unlikely(prev == UACPI_NULL)) {
uacpi_warn(
"trying to uninstall a node %.4s (%p) not linked to any peer\n",
node->name.text, node
);
return UACPI_STATUS_INTERNAL_ERROR;
}
prev->next = node->next;
}
node->flags |= UACPI_NAMESPACE_NODE_FLAG_DANGLING;
uacpi_namespace_node_unref(node);
return UACPI_STATUS_OK;
}
uacpi_namespace_node *uacpi_namespace_node_find_sub_node(
uacpi_namespace_node *parent,
uacpi_object_name name
)
{
uacpi_namespace_node *node;
if (parent == UACPI_NULL)
parent = uacpi_namespace_root();
node = parent->child;
while (node) {
if (node->name.id == name.id)
return node;
node = node->next;
}
return UACPI_NULL;
}
static uacpi_object_name segment_to_name(
const uacpi_char **string, uacpi_size *in_out_size
)
{
uacpi_object_name out_name;
const uacpi_char *cursor = *string;
uacpi_size offset, bytes_left = *in_out_size;
for (offset = 0; offset < 4; offset++) {
if (bytes_left < 1 || *cursor == '.') {
out_name.text[offset] = '_';
continue;
}
out_name.text[offset] = *cursor++;
bytes_left--;
}
*string = cursor;
*in_out_size = bytes_left;
return out_name;
}
uacpi_status uacpi_namespace_node_resolve(
uacpi_namespace_node *parent, const uacpi_char *path,
enum uacpi_should_lock should_lock,
enum uacpi_may_search_above_parent may_search_above_parent,
enum uacpi_permanent_only permanent_only,
uacpi_namespace_node **out_node
)
{
uacpi_namespace_node *cur_node = parent;
uacpi_status ret = UACPI_STATUS_OK;
const uacpi_char *cursor = path;
uacpi_size bytes_left;
uacpi_char prev_char = 0;
uacpi_bool single_nameseg = UACPI_TRUE;
if (cur_node == UACPI_NULL)
cur_node = uacpi_namespace_root();
bytes_left = uacpi_strlen(path);
if (should_lock == UACPI_SHOULD_LOCK_YES) {
ret = uacpi_namespace_read_lock();
if (uacpi_unlikely_error(ret))
return ret;
}
for (;;) {
if (bytes_left == 0)
goto out;
switch (*cursor) {
case '\\':
single_nameseg = UACPI_FALSE;
if (prev_char == '^') {
ret = UACPI_STATUS_INVALID_ARGUMENT;
goto out;
}
cur_node = uacpi_namespace_root();
break;
case '^':
single_nameseg = UACPI_FALSE;
// Tried to go behind root
if (uacpi_unlikely(cur_node == uacpi_namespace_root())) {
ret = UACPI_STATUS_INVALID_ARGUMENT;
goto out;
}
cur_node = cur_node->parent;
break;
default:
break;
}
prev_char = *cursor;
switch (prev_char) {
case '^':
case '\\':
cursor++;
bytes_left--;
break;
default:
break;
}
if (prev_char != '^')
break;
}
while (bytes_left != 0) {
uacpi_object_name nameseg;
if (*cursor == '.') {
cursor++;
bytes_left--;
}
nameseg = segment_to_name(&cursor, &bytes_left);
if (bytes_left != 0 && single_nameseg)
single_nameseg = UACPI_FALSE;
cur_node = uacpi_namespace_node_find_sub_node(cur_node, nameseg);
if (cur_node == UACPI_NULL) {
if (may_search_above_parent == UACPI_MAY_SEARCH_ABOVE_PARENT_NO ||
!single_nameseg)
goto out;
parent = parent->parent;
while (parent) {
cur_node = uacpi_namespace_node_find_sub_node(parent, nameseg);
if (cur_node != UACPI_NULL)
goto out;
parent = parent->parent;
}
goto out;
}
}
out:
if (uacpi_unlikely(ret == UACPI_STATUS_INVALID_ARGUMENT)) {
uacpi_warn("invalid path '%s'\n", path);
goto out_read_unlock;
}
if (cur_node == UACPI_NULL) {
ret = UACPI_STATUS_NOT_FOUND;
goto out_read_unlock;
}
if (uacpi_namespace_node_is_temporary(cur_node) &&
permanent_only == UACPI_PERMANENT_ONLY_YES) {
uacpi_warn("denying access to temporary namespace node '%.4s'\n",
cur_node->name.text);
ret = UACPI_STATUS_DENIED;
goto out_read_unlock;
}
if (out_node != UACPI_NULL)
*out_node = cur_node;
out_read_unlock:
if (should_lock == UACPI_SHOULD_LOCK_YES)
uacpi_namespace_read_unlock();
return ret;
}
uacpi_status uacpi_namespace_node_find(
uacpi_namespace_node *parent, const uacpi_char *path,
uacpi_namespace_node **out_node
)
{
return uacpi_namespace_node_resolve(
parent, path, UACPI_SHOULD_LOCK_YES, UACPI_MAY_SEARCH_ABOVE_PARENT_NO,
UACPI_PERMANENT_ONLY_YES, out_node
);
}
uacpi_status uacpi_namespace_node_resolve_from_aml_namepath(
uacpi_namespace_node *scope,
const uacpi_char *path,
uacpi_namespace_node **out_node
)
{
return uacpi_namespace_node_resolve(
scope, path, UACPI_SHOULD_LOCK_YES, UACPI_MAY_SEARCH_ABOVE_PARENT_YES,
UACPI_PERMANENT_ONLY_YES, out_node
);
}
uacpi_object *uacpi_namespace_node_get_object(const uacpi_namespace_node *node)
{
if (node == UACPI_NULL || node->object == UACPI_NULL)
return UACPI_NULL;
return uacpi_unwrap_internal_reference(node->object);
}
uacpi_object *uacpi_namespace_node_get_object_typed(
const uacpi_namespace_node *node, uacpi_object_type_bits type_mask
)
{
uacpi_object *obj;
obj = uacpi_namespace_node_get_object(node);
if (uacpi_unlikely(obj == UACPI_NULL))
return obj;
if (!uacpi_object_is_one_of(obj, type_mask))
return UACPI_NULL;
return obj;
}
uacpi_status uacpi_namespace_node_acquire_object_typed(
const uacpi_namespace_node *node, uacpi_object_type_bits type_mask,
uacpi_object **out_obj
)
{
uacpi_status ret;
uacpi_object *obj;
ret = uacpi_namespace_read_lock();
if (uacpi_unlikely_error(ret))
return ret;
obj = uacpi_namespace_node_get_object(node);
if (uacpi_unlikely(obj == UACPI_NULL) ||
!uacpi_object_is_one_of(obj, type_mask)) {
ret = UACPI_STATUS_INVALID_ARGUMENT;
goto out;
}
uacpi_object_ref(obj);
*out_obj = obj;
out:
uacpi_namespace_read_unlock();
return ret;
}
uacpi_status uacpi_namespace_node_acquire_object(
const uacpi_namespace_node *node, uacpi_object **out_obj
)
{
return uacpi_namespace_node_acquire_object_typed(
node, UACPI_OBJECT_ANY_BIT, out_obj
);
}
enum action {
ACTION_REACQUIRE,
ACTION_PUT,
};
static uacpi_status object_mutate_refcount(
uacpi_object *obj, void (*cb)(uacpi_object*)
)
{
uacpi_status ret = UACPI_STATUS_OK;
if (uacpi_likely(!uacpi_object_is(obj, UACPI_OBJECT_REFERENCE))) {
cb(obj);
return ret;
}
/*
* Reference objects must be (un)referenced under at least a read lock, as
* this requires walking down the entire reference chain and dropping each
* object ref-count by 1. This might race with the interpreter and
* object_replace_child in case an object in the chain is CopyObject'ed
* into.
*/
ret = uacpi_namespace_read_lock();
if (uacpi_unlikely_error(ret))
return ret;
cb(obj);
uacpi_namespace_read_unlock();
return ret;
}
uacpi_status uacpi_namespace_node_reacquire_object(
uacpi_object *obj
)
{
return object_mutate_refcount(obj, uacpi_object_ref);
}
uacpi_status uacpi_namespace_node_release_object(uacpi_object *obj)
{
return object_mutate_refcount(obj, uacpi_object_unref);
}
uacpi_object_name uacpi_namespace_node_name(const uacpi_namespace_node *node)
{
return node->name;
}
uacpi_status uacpi_namespace_node_type_unlocked(
const uacpi_namespace_node *node, uacpi_object_type *out_type
)
{
uacpi_object *obj;
if (uacpi_unlikely(node == UACPI_NULL))
return UACPI_STATUS_INVALID_ARGUMENT;
obj = uacpi_namespace_node_get_object(node);
if (uacpi_unlikely(obj == UACPI_NULL))
return UACPI_STATUS_NOT_FOUND;
*out_type = obj->type;
return UACPI_STATUS_OK;
}
uacpi_status uacpi_namespace_node_type(
const uacpi_namespace_node *node, uacpi_object_type *out_type
)
{
uacpi_status ret;
ret = uacpi_namespace_read_lock();
if (uacpi_unlikely_error(ret))
return ret;
ret = uacpi_namespace_node_type_unlocked(node, out_type);
uacpi_namespace_read_unlock();
return ret;
}
uacpi_status uacpi_namespace_node_is_one_of_unlocked(
const uacpi_namespace_node *node, uacpi_object_type_bits type_mask, uacpi_bool *out
)
{
uacpi_object *obj;
if (uacpi_unlikely(node == UACPI_NULL))
return UACPI_STATUS_INVALID_ARGUMENT;
obj = uacpi_namespace_node_get_object(node);
if (uacpi_unlikely(obj == UACPI_NULL))
return UACPI_STATUS_NOT_FOUND;
*out = uacpi_object_is_one_of(obj, type_mask);
return UACPI_STATUS_OK;
}
uacpi_status uacpi_namespace_node_is_one_of(
const uacpi_namespace_node *node, uacpi_object_type_bits type_mask,
uacpi_bool *out
)
{
uacpi_status ret;
ret = uacpi_namespace_read_lock();
if (uacpi_unlikely_error(ret))
return ret;
ret = uacpi_namespace_node_is_one_of_unlocked(node,type_mask, out);
uacpi_namespace_read_unlock();
return ret;
}
uacpi_status uacpi_namespace_node_is(
const uacpi_namespace_node *node, uacpi_object_type type, uacpi_bool *out
)
{
return uacpi_namespace_node_is_one_of(
node, 1u << type, out
);
}
uacpi_status uacpi_namespace_do_for_each_child(
uacpi_namespace_node *node, uacpi_iteration_callback descending_callback,
uacpi_iteration_callback ascending_callback,
uacpi_object_type_bits type_mask, uacpi_u32 max_depth,
enum uacpi_should_lock should_lock,
enum uacpi_permanent_only permanent_only, void *user
)
{
uacpi_status ret = UACPI_STATUS_OK;
uacpi_iteration_decision decision;
uacpi_iteration_callback cb;
uacpi_bool walking_up = UACPI_FALSE, matches = UACPI_FALSE;
uacpi_u32 depth = 1;
UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
if (uacpi_unlikely(descending_callback == UACPI_NULL &&
ascending_callback == UACPI_NULL))
return UACPI_STATUS_INVALID_ARGUMENT;
if (uacpi_unlikely(node == UACPI_NULL || max_depth == 0))
return UACPI_STATUS_INVALID_ARGUMENT;
if (should_lock == UACPI_SHOULD_LOCK_YES) {
ret = uacpi_namespace_read_lock();
if (uacpi_unlikely_error(ret))
return ret;
}
if (node->child == UACPI_NULL)
goto out;
node = node->child;
while (depth) {
uacpi_namespace_node_is_one_of_unlocked(node, type_mask, &matches);
if (!matches) {
decision = UACPI_ITERATION_DECISION_CONTINUE;
goto do_next;
}
if (permanent_only == UACPI_PERMANENT_ONLY_YES &&
uacpi_namespace_node_is_temporary(node)) {
decision = UACPI_ITERATION_DECISION_NEXT_PEER;
goto do_next;
}
cb = walking_up ? ascending_callback : descending_callback;
if (cb != UACPI_NULL) {
if (should_lock == UACPI_SHOULD_LOCK_YES) {
ret = uacpi_namespace_read_unlock();
if (uacpi_unlikely_error(ret))
return ret;
}
decision = cb(user, node, depth);
if (decision == UACPI_ITERATION_DECISION_BREAK)
return ret;
if (should_lock == UACPI_SHOULD_LOCK_YES) {
ret = uacpi_namespace_read_lock();
if (uacpi_unlikely_error(ret))
return ret;
}
} else {
decision = UACPI_ITERATION_DECISION_CONTINUE;
}
do_next:
if (walking_up) {
if (node->next) {
node = node->next;
walking_up = UACPI_FALSE;
continue;
}
depth--;
node = node->parent;
continue;
}
switch (decision) {
case UACPI_ITERATION_DECISION_CONTINUE:
if ((depth != max_depth) && (node->child != UACPI_NULL)) {
node = node->child;
depth++;
continue;
}
UACPI_FALLTHROUGH;
case UACPI_ITERATION_DECISION_NEXT_PEER:
walking_up = UACPI_TRUE;
continue;
default:
ret = UACPI_STATUS_INVALID_ARGUMENT;
goto out;
}
}
out:
if (should_lock == UACPI_SHOULD_LOCK_YES)
uacpi_namespace_read_unlock();
return ret;
}
uacpi_status uacpi_namespace_for_each_child_simple(
uacpi_namespace_node *parent, uacpi_iteration_callback callback, void *user
)
{
return uacpi_namespace_do_for_each_child(
parent, callback, UACPI_NULL, UACPI_OBJECT_ANY_BIT, UACPI_MAX_DEPTH_ANY,
UACPI_SHOULD_LOCK_YES, UACPI_PERMANENT_ONLY_YES, user
);
}
uacpi_status uacpi_namespace_for_each_child(
uacpi_namespace_node *parent, uacpi_iteration_callback descending_callback,
uacpi_iteration_callback ascending_callback,
uacpi_object_type_bits type_mask, uacpi_u32 max_depth, void *user
)
{
return uacpi_namespace_do_for_each_child(
parent, descending_callback, ascending_callback, type_mask, max_depth,
UACPI_SHOULD_LOCK_YES, UACPI_PERMANENT_ONLY_YES, user
);
}
uacpi_status uacpi_namespace_node_next_typed(
uacpi_namespace_node *parent, uacpi_namespace_node **iter,
uacpi_object_type_bits type_mask
)
{
uacpi_status ret;
uacpi_bool is_one_of;
uacpi_namespace_node *node;
UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
if (uacpi_unlikely(parent == UACPI_NULL && *iter == UACPI_NULL))
return UACPI_STATUS_INVALID_ARGUMENT;
ret = uacpi_namespace_read_lock();
if (uacpi_unlikely_error(ret))
return ret;
node = *iter;
if (node == UACPI_NULL)
node = parent->child;
else
node = node->next;
for (; node != UACPI_NULL; node = node->next) {
if (uacpi_namespace_node_is_temporary(node))
continue;
ret = uacpi_namespace_node_is_one_of_unlocked(
node, type_mask, &is_one_of
);
if (uacpi_unlikely_error(ret))
break;
if (is_one_of)
break;
}
uacpi_namespace_read_unlock();
if (node == UACPI_NULL)
return UACPI_STATUS_NOT_FOUND;
if (uacpi_likely_success(ret))
*iter = node;
return ret;
}
uacpi_status uacpi_namespace_node_next(
uacpi_namespace_node *parent, uacpi_namespace_node **iter
)
{
return uacpi_namespace_node_next_typed(
parent, iter, UACPI_OBJECT_ANY_BIT
);
}
uacpi_size uacpi_namespace_node_depth(const uacpi_namespace_node *node)
{
uacpi_size depth = 0;
while (node->parent) {
depth++;
node = node->parent;
}
return depth;
}
uacpi_namespace_node *uacpi_namespace_node_parent(
uacpi_namespace_node *node
)
{
return node->parent;
}
const uacpi_char *uacpi_namespace_node_generate_absolute_path(
const uacpi_namespace_node *node
)
{
uacpi_size depth, offset;
uacpi_size bytes_needed;
uacpi_char *path;
depth = uacpi_namespace_node_depth(node) + 1;
// \ only needs 1 byte, the rest is 4 bytes
bytes_needed = 1 + (depth - 1) * sizeof(uacpi_object_name);
// \ and the first NAME don't need a '.', every other segment does
bytes_needed += depth > 2 ? depth - 2 : 0;
// Null terminator
bytes_needed += 1;
path = uacpi_kernel_alloc(bytes_needed);
if (uacpi_unlikely(path == UACPI_NULL))
return path;
path[0] = '\\';
offset = bytes_needed - 1;
path[offset] = '\0';
while (node != uacpi_namespace_root()) {
offset -= sizeof(uacpi_object_name);
uacpi_memcpy(&path[offset], node->name.text, sizeof(uacpi_object_name));
node = node->parent;
if (node != uacpi_namespace_root())
path[--offset] = '.';
}
return path;
}
void uacpi_free_absolute_path(const uacpi_char *path)
{
uacpi_free_dynamic_string(path);
}
#endif // !UACPI_BAREBONES_MODE