#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