From 08eeb79db14145d83578025e1f0e7f7af460ee25 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Sat, 17 May 2025 21:56:07 -0400 Subject: kernel: acpi: Add uACPI port See https://github.com/uACPI/uACPI/ Signed-off-by: Ian Moffett --- sys/dev/acpi/uacpi/namespace.c | 1081 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1081 insertions(+) create mode 100644 sys/dev/acpi/uacpi/namespace.c (limited to 'sys/dev/acpi/uacpi/namespace.c') diff --git a/sys/dev/acpi/uacpi/namespace.c b/sys/dev/acpi/uacpi/namespace.c new file mode 100644 index 0000000..e847dea --- /dev/null +++ b/sys/dev/acpi/uacpi/namespace.c @@ -0,0 +1,1081 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 -- cgit v1.2.3