1 | #include <uacpi/internal/opregion.h> |
2 | #include <uacpi/kernel_api.h> |
3 | #include <uacpi/internal/namespace.h> |
4 | #include <uacpi/uacpi.h> |
5 | #include <uacpi/internal/stdlib.h> |
6 | #include <uacpi/internal/log.h> |
7 | #include <uacpi/internal/utilities.h> |
8 | |
9 | void uacpi_trace_region_error( |
10 | uacpi_namespace_node *node, uacpi_char *message, uacpi_status ret |
11 | ) |
12 | { |
13 | const uacpi_char *path; |
14 | uacpi_operation_region *op_region; |
15 | |
16 | path = uacpi_namespace_node_generate_absolute_path(node); |
17 | op_region = uacpi_namespace_node_get_object(node)->op_region; |
18 | |
19 | uacpi_error( |
20 | "%s (%s) operation region %s: %s\n" , |
21 | message, uacpi_address_space_to_string(op_region->space), |
22 | path, uacpi_status_to_string(ret) |
23 | ); |
24 | uacpi_free_dynamic_string(str: path); |
25 | } |
26 | |
27 | #define UACPI_TRACE_REGION_IO |
28 | |
29 | void uacpi_trace_region_io( |
30 | uacpi_namespace_node *node, uacpi_region_op op, |
31 | uacpi_u64 offset, uacpi_u8 byte_size, uacpi_u64 ret |
32 | ) |
33 | { |
34 | #ifdef UACPI_TRACE_REGION_IO |
35 | const uacpi_char *path; |
36 | uacpi_operation_region *op_region; |
37 | const uacpi_char *type_str; |
38 | |
39 | if (!uacpi_should_log(lvl: UACPI_LOG_TRACE)) |
40 | return; |
41 | |
42 | switch (op) { |
43 | case UACPI_REGION_OP_READ: |
44 | type_str = "read from" ; |
45 | break; |
46 | case UACPI_REGION_OP_WRITE: |
47 | type_str = "write to" ; |
48 | break; |
49 | default: |
50 | type_str = "<INVALID-OP>" ; |
51 | } |
52 | |
53 | op_region = uacpi_namespace_node_get_object(node)->op_region; |
54 | path = uacpi_namespace_node_generate_absolute_path(node); |
55 | |
56 | uacpi_trace( |
57 | "%s [%s] (%d bytes) %s[0x%016" UACPI_PRIX64"] = 0x%" UACPI_PRIX64"\n" , |
58 | type_str, path, byte_size, |
59 | uacpi_address_space_to_string(op_region->space), |
60 | UACPI_FMT64(offset), UACPI_FMT64(ret) |
61 | ); |
62 | |
63 | uacpi_free_dynamic_string(str: path); |
64 | #else |
65 | UACPI_UNUSED(op); |
66 | UACPI_UNUSED(node); |
67 | UACPI_UNUSED(offset); |
68 | UACPI_UNUSED(byte_size); |
69 | UACPI_UNUSED(ret); |
70 | #endif |
71 | } |
72 | |
73 | static uacpi_bool space_needs_reg(enum uacpi_address_space space) |
74 | { |
75 | if (space == UACPI_ADDRESS_SPACE_SYSTEM_MEMORY || |
76 | space == UACPI_ADDRESS_SPACE_SYSTEM_IO || |
77 | space == UACPI_ADDRESS_SPACE_TABLE_DATA) |
78 | return UACPI_FALSE; |
79 | |
80 | return UACPI_TRUE; |
81 | } |
82 | |
83 | static uacpi_status region_run_reg( |
84 | uacpi_namespace_node *node, uacpi_u8 connection_code |
85 | ) |
86 | { |
87 | uacpi_status ret; |
88 | uacpi_args method_args; |
89 | uacpi_object *args[2]; |
90 | |
91 | args[0] = uacpi_create_object(type: UACPI_OBJECT_INTEGER); |
92 | if (uacpi_unlikely(args[0] == UACPI_NULL)) |
93 | return UACPI_STATUS_OUT_OF_MEMORY; |
94 | |
95 | args[1] = uacpi_create_object(type: UACPI_OBJECT_INTEGER); |
96 | if (uacpi_unlikely(args[1] == UACPI_NULL)) { |
97 | uacpi_object_unref(obj: args[0]); |
98 | return UACPI_STATUS_OUT_OF_MEMORY; |
99 | } |
100 | |
101 | args[0]->integer = uacpi_namespace_node_get_object(node)->op_region->space; |
102 | args[1]->integer = connection_code; |
103 | method_args.objects = args; |
104 | method_args.count = 2; |
105 | |
106 | ret = uacpi_eval(parent: node->parent, path: "_REG" , args: &method_args, UACPI_NULL); |
107 | if (uacpi_unlikely_error(ret && ret != UACPI_STATUS_NOT_FOUND)) |
108 | uacpi_trace_region_error(node, message: "error during _REG execution for" , ret); |
109 | |
110 | uacpi_object_unref(obj: args[0]); |
111 | uacpi_object_unref(obj: args[1]); |
112 | return ret; |
113 | } |
114 | |
115 | uacpi_address_space_handlers *uacpi_node_get_address_space_handlers( |
116 | uacpi_namespace_node *node |
117 | ) |
118 | { |
119 | uacpi_object *object; |
120 | |
121 | object = uacpi_namespace_node_get_object(node); |
122 | if (uacpi_unlikely(object == UACPI_NULL)) |
123 | return UACPI_NULL; |
124 | |
125 | switch (object->type) { |
126 | default: |
127 | /* |
128 | * Even though the '\' object doesn't have its type set to |
129 | * UACPI_OBJECT_DEVICE, it is one. |
130 | * See namespace.c:make_object_for_predefined for reasoning. |
131 | */ |
132 | if (node != uacpi_namespace_root() || |
133 | object->type != UACPI_OBJECT_UNINITIALIZED) |
134 | return UACPI_NULL; |
135 | UACPI_FALLTHROUGH; |
136 | case UACPI_OBJECT_DEVICE: |
137 | case UACPI_OBJECT_PROCESSOR: |
138 | case UACPI_OBJECT_THERMAL_ZONE: |
139 | return object->address_space_handlers; |
140 | } |
141 | } |
142 | |
143 | static uacpi_address_space_handler *find_handler( |
144 | uacpi_address_space_handlers *handlers, |
145 | enum uacpi_address_space space |
146 | ) |
147 | { |
148 | uacpi_address_space_handler *handler = handlers->head; |
149 | |
150 | while (handler) { |
151 | if (handler->space == space) |
152 | return handler; |
153 | |
154 | handler = handler->next; |
155 | } |
156 | |
157 | return UACPI_NULL; |
158 | } |
159 | |
160 | static uacpi_operation_region *find_previous_region_link( |
161 | uacpi_operation_region *region |
162 | ) |
163 | { |
164 | uacpi_address_space_handler *handler = region->handler; |
165 | uacpi_operation_region *parent = handler->regions; |
166 | |
167 | if (parent == region) |
168 | // This is the last attached region, it has no previous link |
169 | return region; |
170 | |
171 | while (parent->next != region) { |
172 | parent = parent->next; |
173 | |
174 | if (uacpi_unlikely(parent == UACPI_NULL)) |
175 | return UACPI_NULL; |
176 | } |
177 | |
178 | return parent; |
179 | } |
180 | |
181 | uacpi_status uacpi_opregion_attach(uacpi_namespace_node *node) |
182 | { |
183 | uacpi_operation_region *region; |
184 | uacpi_address_space_handler *handler; |
185 | uacpi_status ret; |
186 | uacpi_region_attach_data attach_data = { 0 }; |
187 | |
188 | if (uacpi_namespace_node_is_dangling(node)) |
189 | return UACPI_STATUS_NAMESPACE_NODE_DANGLING; |
190 | |
191 | region = uacpi_namespace_node_get_object(node)->op_region; |
192 | if (region->handler == UACPI_NULL) |
193 | return UACPI_STATUS_NO_HANDLER; |
194 | if (region->state_flags & UACPI_OP_REGION_STATE_ATTACHED) |
195 | return UACPI_STATUS_OK; |
196 | |
197 | handler = region->handler; |
198 | attach_data.region_node = node; |
199 | attach_data.handler_context = handler->user_context; |
200 | |
201 | ret = handler->callback(UACPI_REGION_OP_ATTACH, &attach_data); |
202 | if (uacpi_unlikely_error(ret)) { |
203 | uacpi_trace_region_error(node, message: "failed to attach a handler to" , ret); |
204 | return ret; |
205 | } |
206 | |
207 | region->state_flags |= UACPI_OP_REGION_STATE_ATTACHED; |
208 | region->user_context = attach_data.out_region_context; |
209 | return ret; |
210 | } |
211 | |
212 | static void region_install_handler(uacpi_namespace_node *node, |
213 | uacpi_address_space_handler *handler) |
214 | { |
215 | uacpi_operation_region *region; |
216 | |
217 | region = uacpi_namespace_node_get_object(node)->op_region; |
218 | region->handler = handler; |
219 | uacpi_shareable_ref(handler); |
220 | |
221 | region->next = handler->regions; |
222 | handler->regions = region; |
223 | } |
224 | |
225 | void uacpi_opregion_uninstall_handler(uacpi_namespace_node *node) |
226 | { |
227 | uacpi_address_space_handler *handler; |
228 | uacpi_operation_region *region, *link; |
229 | |
230 | region = uacpi_namespace_node_get_object(node)->op_region; |
231 | handler = region->handler; |
232 | |
233 | if (handler == UACPI_NULL) |
234 | return; |
235 | |
236 | link = find_previous_region_link(region); |
237 | if (uacpi_unlikely(link == UACPI_NULL)) { |
238 | uacpi_error("operation region @%p not in the handler@%p list(?)\n" , |
239 | region, handler); |
240 | goto out; |
241 | } else if (link == region) { |
242 | link = link->next; |
243 | handler->regions = link; |
244 | } else { |
245 | link->next = region->next; |
246 | } |
247 | |
248 | out: |
249 | if (region->state_flags & UACPI_OP_REGION_STATE_ATTACHED) { |
250 | uacpi_status ret; |
251 | uacpi_region_detach_data detach_data = { |
252 | .region_node = node, |
253 | .region_context = region->user_context, |
254 | .handler_context = handler->user_context, |
255 | }; |
256 | |
257 | ret = handler->callback(UACPI_REGION_OP_DETACH, &detach_data); |
258 | if (uacpi_unlikely_error(ret)) { |
259 | uacpi_trace_region_error( |
260 | node, message: "error during handler detach for" , ret |
261 | ); |
262 | } |
263 | } |
264 | |
265 | if (region->state_flags & UACPI_OP_REGION_STATE_REG_EXECUTED) |
266 | region_run_reg(node, ACPI_REG_DISCONNECT); |
267 | |
268 | uacpi_address_space_handler_unref(handler: region->handler); |
269 | region->handler = UACPI_NULL; |
270 | region->state_flags &= ~(UACPI_OP_REGION_STATE_ATTACHED | |
271 | UACPI_OP_REGION_STATE_REG_EXECUTED); |
272 | } |
273 | |
274 | enum opregion_iter_action { |
275 | OPREGION_ITER_ACTION_UNINSTALL, |
276 | OPREGION_ITER_ACTION_INSTALL, |
277 | }; |
278 | |
279 | struct opregion_iter_ctx { |
280 | enum opregion_iter_action action; |
281 | uacpi_address_space_handler *handler; |
282 | }; |
283 | |
284 | static enum uacpi_ns_iteration_decision do_install_or_uninstall_handler( |
285 | uacpi_handle opaque, uacpi_namespace_node *node |
286 | ) |
287 | { |
288 | struct opregion_iter_ctx *ctx = opaque; |
289 | uacpi_address_space_handlers *handlers; |
290 | uacpi_object *object; |
291 | |
292 | object = uacpi_namespace_node_get_object(node); |
293 | if (object->type == UACPI_OBJECT_OPERATION_REGION) { |
294 | uacpi_operation_region *region = object->op_region; |
295 | |
296 | if (region->space != ctx->handler->space) |
297 | return UACPI_NS_ITERATION_DECISION_CONTINUE; |
298 | |
299 | if (ctx->action == OPREGION_ITER_ACTION_INSTALL) { |
300 | if (region->handler) |
301 | uacpi_opregion_uninstall_handler(node); |
302 | |
303 | region_install_handler(node, handler: ctx->handler); |
304 | } else { |
305 | if (uacpi_unlikely(region->handler != ctx->handler)) { |
306 | uacpi_trace_region_error( |
307 | node, message: "handler mismatch for" , |
308 | ret: UACPI_STATUS_INTERNAL_ERROR |
309 | ); |
310 | return UACPI_NS_ITERATION_DECISION_CONTINUE; |
311 | } |
312 | |
313 | uacpi_opregion_uninstall_handler(node); |
314 | } |
315 | |
316 | return UACPI_NS_ITERATION_DECISION_CONTINUE; |
317 | } |
318 | |
319 | handlers = uacpi_node_get_address_space_handlers(node); |
320 | if (handlers == UACPI_NULL) |
321 | return UACPI_NS_ITERATION_DECISION_CONTINUE; |
322 | |
323 | // Device already has a handler for this space installed |
324 | if (find_handler(handlers, space: ctx->handler->space) != UACPI_NULL) |
325 | return UACPI_NS_ITERATION_DECISION_NEXT_PEER; |
326 | |
327 | return UACPI_NS_ITERATION_DECISION_CONTINUE; |
328 | } |
329 | |
330 | void uacpi_opregion_reg(uacpi_namespace_node *node) |
331 | { |
332 | uacpi_operation_region *region; |
333 | |
334 | region = uacpi_namespace_node_get_object(node)->op_region; |
335 | if (region->state_flags & UACPI_OP_REGION_STATE_REG_EXECUTED) |
336 | return; |
337 | |
338 | if (!space_needs_reg(space: region->space)) |
339 | return; |
340 | |
341 | if (region_run_reg(node, ACPI_REG_CONNECT) == UACPI_STATUS_OK) |
342 | region->state_flags |= UACPI_OP_REGION_STATE_REG_EXECUTED; |
343 | } |
344 | |
345 | struct reg_run_ctx { |
346 | uacpi_u8 space; |
347 | uacpi_u8 connection_code; |
348 | uacpi_size reg_executed; |
349 | uacpi_size reg_errors; |
350 | }; |
351 | |
352 | enum uacpi_ns_iteration_decision do_run_reg( |
353 | void *opaque, uacpi_namespace_node *node |
354 | ) |
355 | { |
356 | struct reg_run_ctx *ctx = opaque; |
357 | uacpi_object *object; |
358 | uacpi_operation_region *region; |
359 | uacpi_status ret; |
360 | |
361 | object = uacpi_namespace_node_get_object(node); |
362 | if (object->type != UACPI_OBJECT_OPERATION_REGION) |
363 | return UACPI_NS_ITERATION_DECISION_CONTINUE; |
364 | |
365 | region = object->op_region; |
366 | |
367 | if (region->space != ctx->space || |
368 | (region->state_flags & UACPI_OP_REGION_STATE_REG_EXECUTED)) |
369 | return UACPI_NS_ITERATION_DECISION_CONTINUE; |
370 | |
371 | if (region->handler == UACPI_NULL && |
372 | ctx->connection_code != ACPI_REG_DISCONNECT) |
373 | return UACPI_NS_ITERATION_DECISION_CONTINUE; |
374 | |
375 | ret = region_run_reg(node, connection_code: ctx->connection_code); |
376 | if (ret == UACPI_STATUS_NOT_FOUND) |
377 | return UACPI_NS_ITERATION_DECISION_CONTINUE; |
378 | |
379 | ctx->reg_executed++; |
380 | |
381 | if (uacpi_unlikely_error(ret)) { |
382 | ctx->reg_errors++; |
383 | return UACPI_NS_ITERATION_DECISION_CONTINUE; |
384 | } |
385 | |
386 | region->state_flags |= UACPI_OP_REGION_STATE_REG_EXECUTED; |
387 | return UACPI_NS_ITERATION_DECISION_CONTINUE; |
388 | } |
389 | |
390 | uacpi_status uacpi_reg_all_opregions( |
391 | uacpi_namespace_node *device_node, |
392 | enum uacpi_address_space space |
393 | ) |
394 | { |
395 | uacpi_address_space_handlers *handlers; |
396 | uacpi_address_space_handler *this_handler; |
397 | struct reg_run_ctx ctx = { |
398 | .space = space, |
399 | .connection_code = ACPI_REG_CONNECT, |
400 | }; |
401 | |
402 | if (!space_needs_reg(space)) |
403 | return UACPI_STATUS_OK; |
404 | |
405 | handlers = uacpi_node_get_address_space_handlers(node: device_node); |
406 | if (uacpi_unlikely(handlers == UACPI_NULL)) |
407 | return UACPI_STATUS_INVALID_ARGUMENT; |
408 | |
409 | this_handler = find_handler(handlers, space); |
410 | if (uacpi_unlikely(this_handler == UACPI_NULL)) |
411 | return UACPI_STATUS_NO_HANDLER; |
412 | |
413 | uacpi_namespace_for_each_node_depth_first( |
414 | parent: device_node->child, callback: do_run_reg, user: &ctx |
415 | ); |
416 | |
417 | uacpi_trace( |
418 | "activated all '%s' opregions controlled by '%.4s', " |
419 | "%zu _REG() calls (%zu errors)\n" , uacpi_address_space_to_string(space), |
420 | device_node->name.text, ctx.reg_executed, ctx.reg_errors |
421 | ); |
422 | return UACPI_STATUS_OK; |
423 | } |
424 | |
425 | uacpi_status uacpi_install_address_space_handler( |
426 | uacpi_namespace_node *device_node, enum uacpi_address_space space, |
427 | uacpi_region_handler handler, uacpi_handle handler_context |
428 | ) |
429 | { |
430 | uacpi_address_space_handlers *handlers; |
431 | uacpi_address_space_handler *this_handler, *new_handler; |
432 | struct opregion_iter_ctx iter_ctx; |
433 | |
434 | handlers = uacpi_node_get_address_space_handlers(node: device_node); |
435 | if (uacpi_unlikely(handlers == UACPI_NULL)) |
436 | return UACPI_STATUS_INVALID_ARGUMENT; |
437 | |
438 | this_handler = find_handler(handlers, space); |
439 | if (this_handler != UACPI_NULL) |
440 | return UACPI_STATUS_ALREADY_EXISTS; |
441 | |
442 | new_handler = uacpi_kernel_alloc(size: sizeof(*new_handler)); |
443 | if (new_handler == UACPI_NULL) |
444 | return UACPI_STATUS_OUT_OF_MEMORY; |
445 | uacpi_shareable_init(new_handler); |
446 | |
447 | new_handler->next = handlers->head; |
448 | new_handler->space = space; |
449 | new_handler->user_context = handler_context; |
450 | new_handler->callback = handler; |
451 | new_handler->regions = UACPI_NULL; |
452 | handlers->head = new_handler; |
453 | |
454 | iter_ctx.handler = new_handler; |
455 | iter_ctx.action = OPREGION_ITER_ACTION_INSTALL; |
456 | |
457 | uacpi_namespace_for_each_node_depth_first( |
458 | parent: device_node->child, callback: do_install_or_uninstall_handler, user: &iter_ctx |
459 | ); |
460 | |
461 | if (!space_needs_reg(space)) |
462 | return UACPI_STATUS_OK; |
463 | |
464 | /* |
465 | * Installing an early address space handler, obviously not possible to |
466 | * execute any _REG methods here. Just return and hope that it is either |
467 | * a global address space handler, or a handler installed by a user who |
468 | * will run uacpi_reg_all_opregions manually after loading/initializing |
469 | * the namespace. |
470 | */ |
471 | if (g_uacpi_rt_ctx.init_level < UACPI_INIT_LEVEL_NAMESPACE_LOADED) |
472 | return UACPI_STATUS_OK; |
473 | |
474 | /* |
475 | * _REG methods for global address space handlers (installed to root) |
476 | * get called during the namespace initialization, no reason |
477 | * to call them here manually as that will be done later by init code |
478 | * anyway. Just delay that work until later. |
479 | */ |
480 | if (device_node == uacpi_namespace_root() && |
481 | g_uacpi_rt_ctx.init_level == UACPI_INIT_LEVEL_NAMESPACE_LOADED) |
482 | return UACPI_STATUS_OK; |
483 | |
484 | // Init level is NAMESPACE_INITIALIZED, so we can safely run _REG now |
485 | return uacpi_reg_all_opregions(device_node, space); |
486 | } |
487 | |
488 | uacpi_status uacpi_uninstall_address_space_handler( |
489 | uacpi_namespace_node *device_node, |
490 | enum uacpi_address_space space |
491 | ) |
492 | { |
493 | uacpi_address_space_handlers *handlers; |
494 | uacpi_address_space_handler *handler, *prev_handler; |
495 | struct opregion_iter_ctx iter_ctx; |
496 | |
497 | handlers = uacpi_node_get_address_space_handlers(node: device_node); |
498 | if (uacpi_unlikely(handlers == UACPI_NULL)) |
499 | return UACPI_STATUS_INVALID_ARGUMENT; |
500 | |
501 | handler = find_handler(handlers, space); |
502 | if (uacpi_unlikely(handler == UACPI_NULL)) |
503 | return UACPI_STATUS_NO_HANDLER; |
504 | |
505 | iter_ctx.handler = handler; |
506 | iter_ctx.action = OPREGION_ITER_ACTION_UNINSTALL; |
507 | |
508 | uacpi_namespace_for_each_node_depth_first( |
509 | parent: device_node->child, callback: do_install_or_uninstall_handler, user: &iter_ctx |
510 | ); |
511 | |
512 | prev_handler = handlers->head; |
513 | |
514 | // Are we the last linked handler? |
515 | if (prev_handler == handler) { |
516 | handlers->head = handler->next; |
517 | goto out; |
518 | } |
519 | |
520 | // Nope, we're somewhere in the middle. Do a search. |
521 | while (prev_handler) { |
522 | if (prev_handler->next == handler) { |
523 | prev_handler->next = handler->next; |
524 | goto out; |
525 | } |
526 | |
527 | prev_handler = prev_handler->next; |
528 | } |
529 | |
530 | out: |
531 | uacpi_address_space_handler_unref(handler); |
532 | return UACPI_STATUS_OK; |
533 | } |
534 | |
535 | uacpi_status uacpi_opregion_find_and_install_handler( |
536 | uacpi_namespace_node *node |
537 | ) |
538 | { |
539 | uacpi_namespace_node *parent = node->parent; |
540 | uacpi_address_space_handlers *handlers; |
541 | uacpi_address_space_handler *handler; |
542 | uacpi_u8 space; |
543 | |
544 | space = uacpi_namespace_node_get_object(node)->op_region->space; |
545 | |
546 | while (parent) { |
547 | handlers = uacpi_node_get_address_space_handlers(node: parent); |
548 | if (handlers != UACPI_NULL) { |
549 | handler = find_handler(handlers, space); |
550 | |
551 | if (handler != UACPI_NULL) { |
552 | region_install_handler(node, handler); |
553 | return UACPI_STATUS_OK; |
554 | } |
555 | } |
556 | |
557 | parent = parent->parent; |
558 | } |
559 | |
560 | return UACPI_STATUS_NOT_FOUND; |
561 | } |
562 | |