1#include <uacpi/namespace.h>
2#include <uacpi/internal/namespace.h>
3#include <uacpi/internal/types.h>
4#include <uacpi/internal/stdlib.h>
5#include <uacpi/internal/interpreter.h>
6#include <uacpi/internal/opregion.h>
7#include <uacpi/internal/log.h>
8#include <uacpi/internal/utilities.h>
9#include <uacpi/kernel_api.h>
10
11#define UACPI_REV_VALUE 2
12#define UACPI_OS_VALUE "Microsoft Windows NT"
13
14uacpi_namespace_node
15predefined_namespaces[UACPI_PREDEFINED_NAMESPACE_MAX + 1] = {
16 [UACPI_PREDEFINED_NAMESPACE_ROOT] = { .name.text = "\\" },
17 [UACPI_PREDEFINED_NAMESPACE_GPE] = { .name.text = "_GPE" },
18 [UACPI_PREDEFINED_NAMESPACE_PR] = { .name.text = "_PR_" },
19 [UACPI_PREDEFINED_NAMESPACE_SB] = { .name.text = "_SB_" },
20 [UACPI_PREDEFINED_NAMESPACE_SI] = { .name.text = "_SI_" },
21 [UACPI_PREDEFINED_NAMESPACE_TZ] = { .name.text = "_TZ_" },
22 [UACPI_PREDEFINED_NAMESPACE_GL] = { .name.text = "_GL_" },
23 [UACPI_PREDEFINED_NAMESPACE_OS] = { .name.text = "_OS_" },
24 [UACPI_PREDEFINED_NAMESPACE_OSI] = { .name.text = "_OSI" },
25 [UACPI_PREDEFINED_NAMESPACE_REV] = { .name.text = "_REV" },
26};
27
28static uacpi_object *make_object_for_predefined(
29 enum uacpi_predefined_namespace ns
30)
31{
32 uacpi_object *obj;
33
34 switch (ns) {
35 case UACPI_PREDEFINED_NAMESPACE_ROOT:
36 obj = uacpi_create_object(type: UACPI_OBJECT_DEVICE);
37 if (uacpi_unlikely(obj == UACPI_NULL))
38 return obj;
39
40 /*
41 * Erase the type here so that code like ObjectType(\) returns
42 * the spec-compliant result of 0. We still create it as device
43 * so that it is able to store global address space & notify handlers.
44 */
45 obj->type = UACPI_OBJECT_UNINITIALIZED;
46 break;
47
48 case UACPI_PREDEFINED_NAMESPACE_OS:
49 obj = uacpi_create_object(type: UACPI_OBJECT_STRING);
50 if (uacpi_unlikely(obj == UACPI_NULL))
51 return obj;
52
53 obj->buffer->text = uacpi_kernel_alloc(size: sizeof(UACPI_OS_VALUE));
54 if (uacpi_unlikely(obj->buffer->text == UACPI_NULL)) {
55 uacpi_object_unref(obj);
56 return UACPI_NULL;
57 }
58
59 obj->buffer->size = sizeof(UACPI_OS_VALUE);
60 uacpi_memcpy(dest: obj->buffer->text, UACPI_OS_VALUE, count: obj->buffer->size);
61 break;
62
63 case UACPI_PREDEFINED_NAMESPACE_REV:
64 obj = uacpi_create_object(type: UACPI_OBJECT_INTEGER);
65 if (uacpi_unlikely(obj == UACPI_NULL))
66 return obj;
67
68 obj->integer = UACPI_REV_VALUE;
69 break;
70
71 case UACPI_PREDEFINED_NAMESPACE_GL:
72 obj = uacpi_create_object(type: UACPI_OBJECT_MUTEX);
73 if (uacpi_likely(obj != UACPI_NULL))
74 g_uacpi_rt_ctx.global_lock_mutex = obj->mutex->handle;
75 break;
76
77 case UACPI_PREDEFINED_NAMESPACE_OSI:
78 obj = uacpi_create_object(type: UACPI_OBJECT_METHOD);
79 if (uacpi_unlikely(obj == UACPI_NULL))
80 return obj;
81
82 obj->method->native_call = UACPI_TRUE;
83 obj->method->handler = uacpi_osi;
84 obj->method->args = 1;
85 break;
86
87 default:
88 obj = uacpi_create_object(type: UACPI_OBJECT_UNINITIALIZED);
89 break;
90 }
91
92 return obj;
93}
94
95uacpi_status uacpi_namespace_initialize_predefined(void)
96{
97 enum uacpi_predefined_namespace ns;
98 uacpi_object *obj;
99 uacpi_namespace_node *node;
100
101 for (ns = 0; ns <= UACPI_PREDEFINED_NAMESPACE_MAX; ns++) {
102 node = &predefined_namespaces[ns];
103 uacpi_shareable_init(node);
104
105 obj = make_object_for_predefined(ns);
106 if (uacpi_unlikely(obj == UACPI_NULL))
107 return UACPI_STATUS_OUT_OF_MEMORY;
108
109 node->object = uacpi_create_internal_reference(
110 kind: UACPI_REFERENCE_KIND_NAMED, child: obj
111 );
112 if (uacpi_unlikely(node->object == UACPI_NULL)) {
113 uacpi_object_unref(obj);
114 return UACPI_STATUS_OUT_OF_MEMORY;
115 }
116 }
117
118 for (ns = UACPI_PREDEFINED_NAMESPACE_GPE;
119 ns <= UACPI_PREDEFINED_NAMESPACE_MAX; ns++) {
120
121 /*
122 * Skip the installation of \_OSI if it was disabled by user.
123 * We still create the object, but it's not attached to the namespace.
124 */
125 if (ns == UACPI_PREDEFINED_NAMESPACE_OSI &&
126 uacpi_check_flag(UACPI_FLAG_NO_OSI))
127 continue;
128
129 uacpi_node_install(parent: uacpi_namespace_root(), node: &predefined_namespaces[ns]);
130 }
131
132 return UACPI_STATUS_OK;
133}
134
135uacpi_namespace_node *uacpi_namespace_root(void)
136{
137 return &predefined_namespaces[UACPI_PREDEFINED_NAMESPACE_ROOT];
138}
139
140uacpi_namespace_node *uacpi_namespace_get_predefined(
141 enum uacpi_predefined_namespace ns
142)
143{
144 if (uacpi_unlikely(ns > UACPI_PREDEFINED_NAMESPACE_MAX)) {
145 uacpi_warn("requested invalid predefined namespace %d\n", ns);
146 return UACPI_NULL;
147 }
148
149 return &predefined_namespaces[ns];
150}
151
152uacpi_namespace_node *uacpi_namespace_node_alloc(uacpi_object_name name)
153{
154 uacpi_namespace_node *ret;
155
156 ret = uacpi_kernel_calloc(count: 1, size: sizeof(*ret));
157 if (uacpi_unlikely(ret == UACPI_NULL))
158 return ret;
159
160 uacpi_shareable_init(ret);
161 ret->name = name;
162 return ret;
163}
164
165static void free_namespace_node(uacpi_handle handle)
166{
167 uacpi_namespace_node *node = handle;
168
169 if (node->object)
170 uacpi_object_unref(obj: node->object);
171
172 uacpi_free(node, sizeof(*node));
173}
174
175void uacpi_namespace_node_unref(uacpi_namespace_node *node)
176{
177 uacpi_shareable_unref_and_delete_if_last(node, do_free: free_namespace_node);
178}
179
180uacpi_status uacpi_node_install(
181 uacpi_namespace_node *parent,
182 uacpi_namespace_node *node
183)
184{
185 if (parent == UACPI_NULL)
186 parent = uacpi_namespace_root();
187
188 if (uacpi_unlikely(uacpi_namespace_node_is_dangling(node))) {
189 uacpi_warn("attempting to install a dangling namespace node %.4s\n",
190 node->name.text);
191 return UACPI_STATUS_NAMESPACE_NODE_DANGLING;
192 }
193
194 if (parent->child == UACPI_NULL) {
195 parent->child = node;
196 } else {
197 uacpi_namespace_node *prev = parent->child;
198
199 while (prev->next != UACPI_NULL)
200 prev = prev->next;
201
202 prev->next = node;
203 }
204
205 node->parent = parent;
206 return UACPI_STATUS_OK;
207}
208
209uacpi_bool uacpi_namespace_node_is_dangling(uacpi_namespace_node *node)
210{
211 return node->flags & UACPI_NAMESPACE_NODE_FLAG_DANGLING;
212}
213
214void uacpi_node_uninstall(uacpi_namespace_node *node)
215{
216 uacpi_namespace_node *prev;
217 uacpi_object *object;
218
219 if (uacpi_unlikely(uacpi_namespace_node_is_dangling(node))) {
220 uacpi_warn("attempting to uninstall a dangling namespace node %.4s\n",
221 node->name.text);
222 return;
223 }
224
225 /*
226 * Even though namespace_node is reference-counted it still has an 'invalid'
227 * state that is entered after it is uninstalled from the global namespace.
228 *
229 * Reference counting is only needed to combat dangling pointer issues
230 * whereas bad AML might try to prolong a local object lifetime by
231 * returning it from a method, or CopyObject it somewhere. In that case the
232 * namespace node object itself is still alive, but no longer has a valid
233 * object associated with it.
234 *
235 * Example:
236 * Method (BAD) {
237 * OperationRegion(REG, SystemMemory, 0xDEADBEEF, 4)
238 * Field (REG, AnyAcc, NoLock) {
239 * FILD, 8,
240 * }
241 *
242 * Return (RefOf(FILD))
243 * }
244 *
245 * // Local0 is now the sole owner of the 'FILD' object that under the
246 * // hood is still referencing the 'REG' operation region object from
247 * // the 'BAD' method.
248 * Local0 = DerefOf(BAD())
249 *
250 * This is done to prevent potential very deep recursion where an object
251 * frees a namespace node that frees an attached object that frees a
252 * namespace node as well as potential infinite cycles between a namespace
253 * node and an object.
254 */
255 object = uacpi_namespace_node_get_object(node);
256 if (object != UACPI_NULL) {
257 if (object->type == UACPI_OBJECT_OPERATION_REGION)
258 uacpi_opregion_uninstall_handler(node);
259
260 uacpi_object_unref(obj: node->object);
261 node->object = UACPI_NULL;
262 }
263
264 prev = node->parent ? node->parent->child : UACPI_NULL;
265
266 if (prev == node) {
267 node->parent->child = node->next;
268 } else {
269 while (uacpi_likely(prev != UACPI_NULL) && prev->next != node)
270 prev = prev->next;
271
272 if (uacpi_unlikely(prev == UACPI_NULL)) {
273 uacpi_warn(
274 "trying to uninstall a node %.4s (%p) not linked to any peer\n",
275 node->name.text, node
276 );
277 return;
278 }
279
280 prev->next = node->next;
281 }
282
283 node->flags |= UACPI_NAMESPACE_NODE_FLAG_DANGLING;
284 uacpi_namespace_node_unref(node);
285}
286
287uacpi_namespace_node *uacpi_namespace_node_find_sub_node(
288 uacpi_namespace_node *parent,
289 uacpi_object_name name
290)
291{
292 if (parent == UACPI_NULL)
293 parent = uacpi_namespace_root();
294
295 uacpi_namespace_node *node = parent->child;
296
297 while (node) {
298 if (node->name.id == name.id)
299 return node;
300
301 node = node->next;
302 }
303
304 return UACPI_NULL;
305}
306
307static uacpi_object_name segment_to_name(
308 const uacpi_char **string, uacpi_size *in_out_size
309)
310{
311 uacpi_object_name out_name;
312 const uacpi_char *cursor = *string;
313 uacpi_size offset, bytes_left = *in_out_size;
314
315 for (offset = 0; offset < 4; offset++) {
316 if (bytes_left < 1 || *cursor == '.') {
317 out_name.text[offset] = '_';
318 continue;
319 }
320
321 out_name.text[offset] = *cursor++;
322 bytes_left--;
323 }
324
325 *string = cursor;
326 *in_out_size = bytes_left;
327 return out_name;
328}
329
330enum may_search_above_parent {
331 MAY_SEARCH_ABOVE_PARENT_NO,
332 MAY_SEARCH_ABOVE_PARENT_YES,
333};
334
335static uacpi_namespace_node *uacpi_namespace_node_do_find(
336 uacpi_namespace_node *parent, const uacpi_char *path,
337 enum may_search_above_parent may_search_above_parent
338)
339{
340 uacpi_namespace_node *cur_node = parent;
341 const uacpi_char *cursor = path;
342 uacpi_size bytes_left;
343 uacpi_char prev_char = 0;
344 uacpi_bool single_nameseg = UACPI_TRUE;
345
346 if (cur_node == UACPI_NULL)
347 cur_node = uacpi_namespace_root();
348
349 bytes_left = uacpi_strlen(str: path);
350
351 for (;;) {
352 if (bytes_left == 0)
353 return cur_node;
354
355 switch (*cursor) {
356 case '\\':
357 single_nameseg = UACPI_FALSE;
358
359 if (prev_char == '^')
360 goto out_invalid_path;
361
362 cur_node = uacpi_namespace_root();
363 break;
364 case '^':
365 single_nameseg = UACPI_FALSE;
366
367 // Tried to go behind root
368 if (uacpi_unlikely(cur_node == uacpi_namespace_root()))
369 goto out_invalid_path;
370
371 cur_node = cur_node->parent;
372 break;
373 default:
374 break;
375 }
376
377 prev_char = *cursor;
378
379 switch (prev_char) {
380 case '^':
381 case '\\':
382 cursor++;
383 bytes_left--;
384 break;
385 default:
386 break;
387 }
388
389 if (prev_char != '^')
390 break;
391 }
392
393 while (bytes_left != 0) {
394 uacpi_object_name nameseg;
395
396 if (*cursor == '.') {
397 cursor++;
398 bytes_left--;
399 }
400
401 nameseg = segment_to_name(string: &cursor, in_out_size: &bytes_left);
402 if (bytes_left != 0 && single_nameseg)
403 single_nameseg = UACPI_FALSE;
404
405 cur_node = uacpi_namespace_node_find_sub_node(parent: cur_node, name: nameseg);
406 if (cur_node == UACPI_NULL) {
407 if (may_search_above_parent == MAY_SEARCH_ABOVE_PARENT_NO ||
408 !single_nameseg)
409 return cur_node;
410
411 parent = parent->parent;
412
413 while (parent) {
414 cur_node = uacpi_namespace_node_find_sub_node(parent, name: nameseg);
415 if (cur_node != UACPI_NULL)
416 return cur_node;
417
418 parent = parent->parent;
419 }
420
421 return cur_node;
422 }
423 }
424
425 return cur_node;
426
427out_invalid_path:
428 uacpi_warn("invalid path '%s'\n", path);
429 return UACPI_NULL;
430}
431
432uacpi_namespace_node *uacpi_namespace_node_find(
433 uacpi_namespace_node *parent, const uacpi_char *path
434)
435{
436 return uacpi_namespace_node_do_find(
437 parent, path, may_search_above_parent: MAY_SEARCH_ABOVE_PARENT_NO
438 );
439}
440
441uacpi_namespace_node *uacpi_namespace_node_resolve_from_aml_namepath(
442 uacpi_namespace_node *scope, const uacpi_char *path
443)
444{
445 return uacpi_namespace_node_do_find(
446 parent: scope, path, may_search_above_parent: MAY_SEARCH_ABOVE_PARENT_YES
447 );
448}
449
450uacpi_object *uacpi_namespace_node_get_object(const uacpi_namespace_node *node)
451{
452 if (node == UACPI_NULL || node->object == UACPI_NULL)
453 return UACPI_NULL;
454
455 return uacpi_unwrap_internal_reference(object: node->object);
456}
457
458uacpi_object_name uacpi_namespace_node_name(const uacpi_namespace_node *node)
459{
460 return node->name;
461}
462
463void uacpi_namespace_for_each_node_depth_first(
464 uacpi_namespace_node *node,
465 uacpi_iteration_callback callback,
466 void *user
467)
468{
469 uacpi_bool walking_up = UACPI_FALSE;
470 uacpi_u32 depth = 1;
471
472 if (node == UACPI_NULL)
473 return;
474
475 while (depth) {
476 if (walking_up) {
477 if (node->next) {
478 node = node->next;
479 walking_up = UACPI_FALSE;
480 continue;
481 }
482
483 depth--;
484 node = node->parent;
485 continue;
486 }
487
488 switch (callback(user, node)) {
489 case UACPI_NS_ITERATION_DECISION_CONTINUE:
490 if (node->child) {
491 node = node->child;
492 depth++;
493 continue;
494 }
495 UACPI_FALLTHROUGH;
496 case UACPI_NS_ITERATION_DECISION_NEXT_PEER:
497 walking_up = UACPI_TRUE;
498 continue;
499
500 case UACPI_NS_ITERATION_DECISION_BREAK:
501 default:
502 return;
503 }
504 }
505}
506
507uacpi_size uacpi_namespace_node_depth(const uacpi_namespace_node *node)
508{
509 uacpi_size depth = 0;
510
511 while (node->parent) {
512 depth++;
513 node = node->parent;
514 }
515
516 return depth;
517}
518
519const uacpi_char *uacpi_namespace_node_generate_absolute_path(
520 const uacpi_namespace_node *node
521)
522{
523 uacpi_size depth, offset;
524 uacpi_size bytes_needed;
525 uacpi_char *path;
526
527 depth = uacpi_namespace_node_depth(node) + 1;
528
529 // \ only needs 1 byte, the rest is 4 bytes
530 bytes_needed = 1 + (depth - 1) * sizeof(uacpi_object_name);
531
532 // \ and the first NAME don't need a '.', every other segment does
533 bytes_needed += depth > 2 ? depth - 2 : 0;
534
535 // Null terminator
536 bytes_needed += 1;
537
538 path = uacpi_kernel_alloc(size: bytes_needed);
539 if (uacpi_unlikely(path == UACPI_NULL))
540 return path;
541
542 path[0] = '\\';
543
544 offset = bytes_needed - 1;
545 path[offset] = '\0';
546
547 while (node != uacpi_namespace_root()) {
548 offset -= sizeof(uacpi_object_name);
549 uacpi_memcpy(dest: &path[offset], src: node->name.text, count: sizeof(uacpi_object_name));
550
551 node = node->parent;
552 if (node != uacpi_namespace_root())
553 path[--offset] = '.';
554 }
555
556 return path;
557}
558
559void uacpi_free_absolute_path(const uacpi_char *path)
560{
561 uacpi_free_dynamic_string(str: path);
562}
563