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 | |
14 | uacpi_namespace_node |
15 | predefined_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 | |
28 | static 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 | |
95 | uacpi_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 | |
135 | uacpi_namespace_node *uacpi_namespace_root(void) |
136 | { |
137 | return &predefined_namespaces[UACPI_PREDEFINED_NAMESPACE_ROOT]; |
138 | } |
139 | |
140 | uacpi_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 | |
152 | uacpi_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 | |
165 | static 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 | |
175 | void 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 | |
180 | uacpi_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 | |
209 | uacpi_bool uacpi_namespace_node_is_dangling(uacpi_namespace_node *node) |
210 | { |
211 | return node->flags & UACPI_NAMESPACE_NODE_FLAG_DANGLING; |
212 | } |
213 | |
214 | void 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 | |
287 | uacpi_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 | |
307 | static 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 | |
330 | enum may_search_above_parent { |
331 | MAY_SEARCH_ABOVE_PARENT_NO, |
332 | MAY_SEARCH_ABOVE_PARENT_YES, |
333 | }; |
334 | |
335 | static 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 | |
427 | out_invalid_path: |
428 | uacpi_warn("invalid path '%s'\n" , path); |
429 | return UACPI_NULL; |
430 | } |
431 | |
432 | uacpi_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 | |
441 | uacpi_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 | |
450 | uacpi_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 | |
458 | uacpi_object_name uacpi_namespace_node_name(const uacpi_namespace_node *node) |
459 | { |
460 | return node->name; |
461 | } |
462 | |
463 | void 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 | |
507 | uacpi_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 | |
519 | const 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 | |
559 | void uacpi_free_absolute_path(const uacpi_char *path) |
560 | { |
561 | uacpi_free_dynamic_string(str: path); |
562 | } |
563 | |