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
9void 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
29void 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
73static 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
83static 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
115uacpi_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
143static 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
160static 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
181uacpi_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
212static 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
225void 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
248out:
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
274enum opregion_iter_action {
275 OPREGION_ITER_ACTION_UNINSTALL,
276 OPREGION_ITER_ACTION_INSTALL,
277};
278
279struct opregion_iter_ctx {
280 enum opregion_iter_action action;
281 uacpi_address_space_handler *handler;
282};
283
284static 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
330void 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
345struct reg_run_ctx {
346 uacpi_u8 space;
347 uacpi_u8 connection_code;
348 uacpi_size reg_executed;
349 uacpi_size reg_errors;
350};
351
352enum 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
390uacpi_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
425uacpi_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
488uacpi_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
530out:
531 uacpi_address_space_handler_unref(handler);
532 return UACPI_STATUS_OK;
533}
534
535uacpi_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