1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /****************************************************************************** |
3 | * |
4 | * Module Name: evregion - Operation Region support |
5 | * |
6 | * Copyright (C) 2000 - 2023, Intel Corp. |
7 | * |
8 | *****************************************************************************/ |
9 | |
10 | #include <acpi/acpi.h> |
11 | #include "accommon.h" |
12 | #include "acevents.h" |
13 | #include "acnamesp.h" |
14 | #include "acinterp.h" |
15 | |
16 | #define _COMPONENT ACPI_EVENTS |
17 | ACPI_MODULE_NAME("evregion" ) |
18 | |
19 | extern u8 acpi_gbl_default_address_spaces[]; |
20 | |
21 | /* Local prototypes */ |
22 | |
23 | static void |
24 | acpi_ev_execute_orphan_reg_method(struct acpi_namespace_node *device_node, |
25 | acpi_adr_space_type space_id); |
26 | |
27 | static acpi_status |
28 | acpi_ev_reg_run(acpi_handle obj_handle, |
29 | u32 level, void *context, void **return_value); |
30 | |
31 | /******************************************************************************* |
32 | * |
33 | * FUNCTION: acpi_ev_initialize_op_regions |
34 | * |
35 | * PARAMETERS: None |
36 | * |
37 | * RETURN: Status |
38 | * |
39 | * DESCRIPTION: Execute _REG methods for all Operation Regions that have |
40 | * an installed default region handler. |
41 | * |
42 | ******************************************************************************/ |
43 | |
44 | acpi_status acpi_ev_initialize_op_regions(void) |
45 | { |
46 | acpi_status status; |
47 | u32 i; |
48 | |
49 | ACPI_FUNCTION_TRACE(ev_initialize_op_regions); |
50 | |
51 | status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); |
52 | if (ACPI_FAILURE(status)) { |
53 | return_ACPI_STATUS(status); |
54 | } |
55 | |
56 | /* Run the _REG methods for op_regions in each default address space */ |
57 | |
58 | for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) { |
59 | /* |
60 | * Make sure the installed handler is the DEFAULT handler. If not the |
61 | * default, the _REG methods will have already been run (when the |
62 | * handler was installed) |
63 | */ |
64 | if (acpi_ev_has_default_handler(node: acpi_gbl_root_node, |
65 | space_id: acpi_gbl_default_address_spaces |
66 | [i])) { |
67 | acpi_ev_execute_reg_methods(node: acpi_gbl_root_node, |
68 | space_id: acpi_gbl_default_address_spaces |
69 | [i], ACPI_REG_CONNECT); |
70 | } |
71 | } |
72 | |
73 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
74 | return_ACPI_STATUS(status); |
75 | } |
76 | |
77 | /******************************************************************************* |
78 | * |
79 | * FUNCTION: acpi_ev_address_space_dispatch |
80 | * |
81 | * PARAMETERS: region_obj - Internal region object |
82 | * field_obj - Corresponding field. Can be NULL. |
83 | * function - Read or Write operation |
84 | * region_offset - Where in the region to read or write |
85 | * bit_width - Field width in bits (8, 16, 32, or 64) |
86 | * value - Pointer to in or out value, must be |
87 | * a full 64-bit integer |
88 | * |
89 | * RETURN: Status |
90 | * |
91 | * DESCRIPTION: Dispatch an address space or operation region access to |
92 | * a previously installed handler. |
93 | * |
94 | * NOTE: During early initialization, we always install the default region |
95 | * handlers for Memory, I/O and PCI_Config. This ensures that these operation |
96 | * region address spaces are always available as per the ACPI specification. |
97 | * This is especially needed in order to support the execution of |
98 | * module-level AML code during loading of the ACPI tables. |
99 | * |
100 | ******************************************************************************/ |
101 | |
102 | acpi_status |
103 | acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, |
104 | union acpi_operand_object *field_obj, |
105 | u32 function, |
106 | u32 region_offset, u32 bit_width, u64 *value) |
107 | { |
108 | acpi_status status; |
109 | acpi_adr_space_handler handler; |
110 | acpi_adr_space_setup region_setup; |
111 | union acpi_operand_object *handler_desc; |
112 | union acpi_operand_object *region_obj2; |
113 | void *region_context = NULL; |
114 | struct acpi_connection_info *context; |
115 | acpi_mutex context_mutex; |
116 | u8 context_locked; |
117 | acpi_physical_address address; |
118 | |
119 | ACPI_FUNCTION_TRACE(ev_address_space_dispatch); |
120 | |
121 | region_obj2 = acpi_ns_get_secondary_object(obj_desc: region_obj); |
122 | if (!region_obj2) { |
123 | return_ACPI_STATUS(AE_NOT_EXIST); |
124 | } |
125 | |
126 | /* Ensure that there is a handler associated with this region */ |
127 | |
128 | handler_desc = region_obj->region.handler; |
129 | if (!handler_desc) { |
130 | ACPI_ERROR((AE_INFO, |
131 | "No handler for Region [%4.4s] (%p) [%s]" , |
132 | acpi_ut_get_node_name(region_obj->region.node), |
133 | region_obj, |
134 | acpi_ut_get_region_name(region_obj->region. |
135 | space_id))); |
136 | |
137 | return_ACPI_STATUS(AE_NOT_EXIST); |
138 | } |
139 | |
140 | context = handler_desc->address_space.context; |
141 | context_mutex = handler_desc->address_space.context_mutex; |
142 | context_locked = FALSE; |
143 | |
144 | /* |
145 | * It may be the case that the region has never been initialized. |
146 | * Some types of regions require special init code |
147 | */ |
148 | if (!(region_obj->region.flags & AOPOBJ_SETUP_COMPLETE)) { |
149 | |
150 | /* This region has not been initialized yet, do it */ |
151 | |
152 | region_setup = handler_desc->address_space.setup; |
153 | if (!region_setup) { |
154 | |
155 | /* No initialization routine, exit with error */ |
156 | |
157 | ACPI_ERROR((AE_INFO, |
158 | "No init routine for region(%p) [%s]" , |
159 | region_obj, |
160 | acpi_ut_get_region_name(region_obj->region. |
161 | space_id))); |
162 | return_ACPI_STATUS(AE_NOT_EXIST); |
163 | } |
164 | |
165 | if (region_obj->region.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { |
166 | struct acpi_pcc_info *ctx = |
167 | handler_desc->address_space.context; |
168 | |
169 | ctx->internal_buffer = |
170 | field_obj->field.internal_pcc_buffer; |
171 | ctx->length = (u16)region_obj->region.length; |
172 | ctx->subspace_id = (u8)region_obj->region.address; |
173 | } |
174 | |
175 | if (region_obj->region.space_id == |
176 | ACPI_ADR_SPACE_FIXED_HARDWARE) { |
177 | struct acpi_ffh_info *ctx = |
178 | handler_desc->address_space.context; |
179 | |
180 | ctx->length = region_obj->region.length; |
181 | ctx->offset = region_obj->region.address; |
182 | } |
183 | |
184 | /* |
185 | * We must exit the interpreter because the region setup will |
186 | * potentially execute control methods (for example, the _REG method |
187 | * for this region) |
188 | */ |
189 | acpi_ex_exit_interpreter(); |
190 | |
191 | status = region_setup(region_obj, ACPI_REGION_ACTIVATE, |
192 | context, ®ion_context); |
193 | |
194 | /* Re-enter the interpreter */ |
195 | |
196 | acpi_ex_enter_interpreter(); |
197 | |
198 | /* Check for failure of the Region Setup */ |
199 | |
200 | if (ACPI_FAILURE(status)) { |
201 | ACPI_EXCEPTION((AE_INFO, status, |
202 | "During region initialization: [%s]" , |
203 | acpi_ut_get_region_name(region_obj-> |
204 | region. |
205 | space_id))); |
206 | return_ACPI_STATUS(status); |
207 | } |
208 | |
209 | /* Region initialization may have been completed by region_setup */ |
210 | |
211 | if (!(region_obj->region.flags & AOPOBJ_SETUP_COMPLETE)) { |
212 | region_obj->region.flags |= AOPOBJ_SETUP_COMPLETE; |
213 | |
214 | /* |
215 | * Save the returned context for use in all accesses to |
216 | * the handler for this particular region |
217 | */ |
218 | if (!(region_obj2->extra.region_context)) { |
219 | region_obj2->extra.region_context = |
220 | region_context; |
221 | } |
222 | } |
223 | } |
224 | |
225 | /* We have everything we need, we can invoke the address space handler */ |
226 | |
227 | handler = handler_desc->address_space.handler; |
228 | address = (region_obj->region.address + region_offset); |
229 | |
230 | ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, |
231 | "Handler %p (@%p) Address %8.8X%8.8X [%s]\n" , |
232 | ®ion_obj->region.handler->address_space, handler, |
233 | ACPI_FORMAT_UINT64(address), |
234 | acpi_ut_get_region_name(region_obj->region. |
235 | space_id))); |
236 | |
237 | if (!(handler_desc->address_space.handler_flags & |
238 | ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) { |
239 | /* |
240 | * For handlers other than the default (supplied) handlers, we must |
241 | * exit the interpreter because the handler *might* block -- we don't |
242 | * know what it will do, so we can't hold the lock on the interpreter. |
243 | */ |
244 | acpi_ex_exit_interpreter(); |
245 | } |
246 | |
247 | /* |
248 | * Special handling for generic_serial_bus and general_purpose_io: |
249 | * There are three extra parameters that must be passed to the |
250 | * handler via the context: |
251 | * 1) Connection buffer, a resource template from Connection() op |
252 | * 2) Length of the above buffer |
253 | * 3) Actual access length from the access_as() op |
254 | * |
255 | * Since we pass these extra parameters via the context, which is |
256 | * shared between threads, we must lock the context to avoid these |
257 | * parameters being changed from another thread before the handler |
258 | * has completed running. |
259 | * |
260 | * In addition, for general_purpose_io, the Address and bit_width fields |
261 | * are defined as follows: |
262 | * 1) Address is the pin number index of the field (bit offset from |
263 | * the previous Connection) |
264 | * 2) bit_width is the actual bit length of the field (number of pins) |
265 | */ |
266 | if ((region_obj->region.space_id == ACPI_ADR_SPACE_GSBUS || |
267 | region_obj->region.space_id == ACPI_ADR_SPACE_GPIO) && |
268 | context && field_obj) { |
269 | |
270 | status = |
271 | acpi_os_acquire_mutex(context_mutex, ACPI_WAIT_FOREVER); |
272 | if (ACPI_FAILURE(status)) { |
273 | goto re_enter_interpreter; |
274 | } |
275 | |
276 | context_locked = TRUE; |
277 | |
278 | /* Get the Connection (resource_template) buffer */ |
279 | |
280 | context->connection = field_obj->field.resource_buffer; |
281 | context->length = field_obj->field.resource_length; |
282 | context->access_length = field_obj->field.access_length; |
283 | |
284 | if (region_obj->region.space_id == ACPI_ADR_SPACE_GPIO) { |
285 | address = field_obj->field.pin_number_index; |
286 | bit_width = field_obj->field.bit_length; |
287 | } |
288 | } |
289 | |
290 | /* Call the handler */ |
291 | |
292 | status = handler(function, address, bit_width, value, context, |
293 | region_obj2->extra.region_context); |
294 | |
295 | if (context_locked) { |
296 | acpi_os_release_mutex(context_mutex); |
297 | } |
298 | |
299 | if (ACPI_FAILURE(status)) { |
300 | ACPI_EXCEPTION((AE_INFO, status, "Returned by Handler for [%s]" , |
301 | acpi_ut_get_region_name(region_obj->region. |
302 | space_id))); |
303 | |
304 | /* |
305 | * Special case for an EC timeout. These are seen so frequently |
306 | * that an additional error message is helpful |
307 | */ |
308 | if ((region_obj->region.space_id == ACPI_ADR_SPACE_EC) && |
309 | (status == AE_TIME)) { |
310 | ACPI_ERROR((AE_INFO, |
311 | "Timeout from EC hardware or EC device driver" )); |
312 | } |
313 | } |
314 | |
315 | re_enter_interpreter: |
316 | if (!(handler_desc->address_space.handler_flags & |
317 | ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) { |
318 | /* |
319 | * We just returned from a non-default handler, we must re-enter the |
320 | * interpreter |
321 | */ |
322 | acpi_ex_enter_interpreter(); |
323 | } |
324 | |
325 | return_ACPI_STATUS(status); |
326 | } |
327 | |
328 | /******************************************************************************* |
329 | * |
330 | * FUNCTION: acpi_ev_detach_region |
331 | * |
332 | * PARAMETERS: region_obj - Region Object |
333 | * acpi_ns_is_locked - Namespace Region Already Locked? |
334 | * |
335 | * RETURN: None |
336 | * |
337 | * DESCRIPTION: Break the association between the handler and the region |
338 | * this is a two way association. |
339 | * |
340 | ******************************************************************************/ |
341 | |
342 | void |
343 | acpi_ev_detach_region(union acpi_operand_object *region_obj, |
344 | u8 acpi_ns_is_locked) |
345 | { |
346 | union acpi_operand_object *handler_obj; |
347 | union acpi_operand_object *obj_desc; |
348 | union acpi_operand_object *start_desc; |
349 | union acpi_operand_object **last_obj_ptr; |
350 | acpi_adr_space_setup region_setup; |
351 | void **region_context; |
352 | union acpi_operand_object *region_obj2; |
353 | acpi_status status; |
354 | |
355 | ACPI_FUNCTION_TRACE(ev_detach_region); |
356 | |
357 | region_obj2 = acpi_ns_get_secondary_object(obj_desc: region_obj); |
358 | if (!region_obj2) { |
359 | return_VOID; |
360 | } |
361 | region_context = ®ion_obj2->extra.region_context; |
362 | |
363 | /* Get the address handler from the region object */ |
364 | |
365 | handler_obj = region_obj->region.handler; |
366 | if (!handler_obj) { |
367 | |
368 | /* This region has no handler, all done */ |
369 | |
370 | return_VOID; |
371 | } |
372 | |
373 | /* Find this region in the handler's list */ |
374 | |
375 | obj_desc = handler_obj->address_space.region_list; |
376 | start_desc = obj_desc; |
377 | last_obj_ptr = &handler_obj->address_space.region_list; |
378 | |
379 | while (obj_desc) { |
380 | |
381 | /* Is this the correct Region? */ |
382 | |
383 | if (obj_desc == region_obj) { |
384 | ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, |
385 | "Removing Region %p from address handler %p\n" , |
386 | region_obj, handler_obj)); |
387 | |
388 | /* This is it, remove it from the handler's list */ |
389 | |
390 | *last_obj_ptr = obj_desc->region.next; |
391 | obj_desc->region.next = NULL; /* Must clear field */ |
392 | |
393 | if (acpi_ns_is_locked) { |
394 | status = |
395 | acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
396 | if (ACPI_FAILURE(status)) { |
397 | return_VOID; |
398 | } |
399 | } |
400 | |
401 | /* Now stop region accesses by executing the _REG method */ |
402 | |
403 | status = |
404 | acpi_ev_execute_reg_method(region_obj, |
405 | ACPI_REG_DISCONNECT); |
406 | if (ACPI_FAILURE(status)) { |
407 | ACPI_EXCEPTION((AE_INFO, status, |
408 | "from region _REG, [%s]" , |
409 | acpi_ut_get_region_name |
410 | (region_obj->region.space_id))); |
411 | } |
412 | |
413 | if (acpi_ns_is_locked) { |
414 | status = |
415 | acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); |
416 | if (ACPI_FAILURE(status)) { |
417 | return_VOID; |
418 | } |
419 | } |
420 | |
421 | /* |
422 | * If the region has been activated, call the setup handler with |
423 | * the deactivate notification |
424 | */ |
425 | if (region_obj->region.flags & AOPOBJ_SETUP_COMPLETE) { |
426 | region_setup = handler_obj->address_space.setup; |
427 | status = |
428 | region_setup(region_obj, |
429 | ACPI_REGION_DEACTIVATE, |
430 | handler_obj->address_space. |
431 | context, region_context); |
432 | |
433 | /* |
434 | * region_context should have been released by the deactivate |
435 | * operation. We don't need access to it anymore here. |
436 | */ |
437 | if (region_context) { |
438 | *region_context = NULL; |
439 | } |
440 | |
441 | /* Init routine may fail, Just ignore errors */ |
442 | |
443 | if (ACPI_FAILURE(status)) { |
444 | ACPI_EXCEPTION((AE_INFO, status, |
445 | "from region handler - deactivate, [%s]" , |
446 | acpi_ut_get_region_name |
447 | (region_obj->region. |
448 | space_id))); |
449 | } |
450 | |
451 | region_obj->region.flags &= |
452 | ~(AOPOBJ_SETUP_COMPLETE); |
453 | } |
454 | |
455 | /* |
456 | * Remove handler reference in the region |
457 | * |
458 | * NOTE: this doesn't mean that the region goes away, the region |
459 | * is just inaccessible as indicated to the _REG method |
460 | * |
461 | * If the region is on the handler's list, this must be the |
462 | * region's handler |
463 | */ |
464 | region_obj->region.handler = NULL; |
465 | acpi_ut_remove_reference(object: handler_obj); |
466 | |
467 | return_VOID; |
468 | } |
469 | |
470 | /* Walk the linked list of handlers */ |
471 | |
472 | last_obj_ptr = &obj_desc->region.next; |
473 | obj_desc = obj_desc->region.next; |
474 | |
475 | /* Prevent infinite loop if list is corrupted */ |
476 | |
477 | if (obj_desc == start_desc) { |
478 | ACPI_ERROR((AE_INFO, |
479 | "Circular handler list in region object %p" , |
480 | region_obj)); |
481 | return_VOID; |
482 | } |
483 | } |
484 | |
485 | /* If we get here, the region was not in the handler's region list */ |
486 | |
487 | ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, |
488 | "Cannot remove region %p from address handler %p\n" , |
489 | region_obj, handler_obj)); |
490 | |
491 | return_VOID; |
492 | } |
493 | |
494 | /******************************************************************************* |
495 | * |
496 | * FUNCTION: acpi_ev_attach_region |
497 | * |
498 | * PARAMETERS: handler_obj - Handler Object |
499 | * region_obj - Region Object |
500 | * acpi_ns_is_locked - Namespace Region Already Locked? |
501 | * |
502 | * RETURN: None |
503 | * |
504 | * DESCRIPTION: Create the association between the handler and the region |
505 | * this is a two way association. |
506 | * |
507 | ******************************************************************************/ |
508 | |
509 | acpi_status |
510 | acpi_ev_attach_region(union acpi_operand_object *handler_obj, |
511 | union acpi_operand_object *region_obj, |
512 | u8 acpi_ns_is_locked) |
513 | { |
514 | |
515 | ACPI_FUNCTION_TRACE(ev_attach_region); |
516 | |
517 | /* Install the region's handler */ |
518 | |
519 | if (region_obj->region.handler) { |
520 | return_ACPI_STATUS(AE_ALREADY_EXISTS); |
521 | } |
522 | |
523 | ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, |
524 | "Adding Region [%4.4s] %p to address handler %p [%s]\n" , |
525 | acpi_ut_get_node_name(region_obj->region.node), |
526 | region_obj, handler_obj, |
527 | acpi_ut_get_region_name(region_obj->region. |
528 | space_id))); |
529 | |
530 | /* Link this region to the front of the handler's list */ |
531 | |
532 | region_obj->region.next = handler_obj->address_space.region_list; |
533 | handler_obj->address_space.region_list = region_obj; |
534 | region_obj->region.handler = handler_obj; |
535 | acpi_ut_add_reference(object: handler_obj); |
536 | |
537 | return_ACPI_STATUS(AE_OK); |
538 | } |
539 | |
540 | /******************************************************************************* |
541 | * |
542 | * FUNCTION: acpi_ev_execute_reg_method |
543 | * |
544 | * PARAMETERS: region_obj - Region object |
545 | * function - Passed to _REG: On (1) or Off (0) |
546 | * |
547 | * RETURN: Status |
548 | * |
549 | * DESCRIPTION: Execute _REG method for a region |
550 | * |
551 | ******************************************************************************/ |
552 | |
553 | acpi_status |
554 | acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function) |
555 | { |
556 | struct acpi_evaluate_info *info; |
557 | union acpi_operand_object *args[3]; |
558 | union acpi_operand_object *region_obj2; |
559 | const acpi_name *reg_name_ptr = |
560 | ACPI_CAST_PTR(acpi_name, METHOD_NAME__REG); |
561 | struct acpi_namespace_node *method_node; |
562 | struct acpi_namespace_node *node; |
563 | acpi_status status; |
564 | |
565 | ACPI_FUNCTION_TRACE(ev_execute_reg_method); |
566 | |
567 | if (!acpi_gbl_namespace_initialized || |
568 | region_obj->region.handler == NULL) { |
569 | return_ACPI_STATUS(AE_OK); |
570 | } |
571 | |
572 | region_obj2 = acpi_ns_get_secondary_object(obj_desc: region_obj); |
573 | if (!region_obj2) { |
574 | return_ACPI_STATUS(AE_NOT_EXIST); |
575 | } |
576 | |
577 | /* |
578 | * Find any "_REG" method associated with this region definition. |
579 | * The method should always be updated as this function may be |
580 | * invoked after a namespace change. |
581 | */ |
582 | node = region_obj->region.node->parent; |
583 | status = |
584 | acpi_ns_search_one_scope(entry_name: *reg_name_ptr, node, ACPI_TYPE_METHOD, |
585 | ret_node: &method_node); |
586 | if (ACPI_SUCCESS(status)) { |
587 | /* |
588 | * The _REG method is optional and there can be only one per |
589 | * region definition. This will be executed when the handler is |
590 | * attached or removed. |
591 | */ |
592 | region_obj2->extra.method_REG = method_node; |
593 | } |
594 | if (region_obj2->extra.method_REG == NULL) { |
595 | return_ACPI_STATUS(AE_OK); |
596 | } |
597 | |
598 | /* _REG(DISCONNECT) should be paired with _REG(CONNECT) */ |
599 | |
600 | if ((function == ACPI_REG_CONNECT && |
601 | region_obj->common.flags & AOPOBJ_REG_CONNECTED) || |
602 | (function == ACPI_REG_DISCONNECT && |
603 | !(region_obj->common.flags & AOPOBJ_REG_CONNECTED))) { |
604 | return_ACPI_STATUS(AE_OK); |
605 | } |
606 | |
607 | /* Allocate and initialize the evaluation information block */ |
608 | |
609 | info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); |
610 | if (!info) { |
611 | return_ACPI_STATUS(AE_NO_MEMORY); |
612 | } |
613 | |
614 | info->prefix_node = region_obj2->extra.method_REG; |
615 | info->relative_pathname = NULL; |
616 | info->parameters = args; |
617 | info->flags = ACPI_IGNORE_RETURN_VALUE; |
618 | |
619 | /* |
620 | * The _REG method has two arguments: |
621 | * |
622 | * arg0 - Integer: |
623 | * Operation region space ID Same value as region_obj->Region.space_id |
624 | * |
625 | * arg1 - Integer: |
626 | * connection status 1 for connecting the handler, 0 for disconnecting |
627 | * the handler (Passed as a parameter) |
628 | */ |
629 | args[0] = |
630 | acpi_ut_create_integer_object(value: (u64)region_obj->region.space_id); |
631 | if (!args[0]) { |
632 | status = AE_NO_MEMORY; |
633 | goto cleanup1; |
634 | } |
635 | |
636 | args[1] = acpi_ut_create_integer_object(value: (u64)function); |
637 | if (!args[1]) { |
638 | status = AE_NO_MEMORY; |
639 | goto cleanup2; |
640 | } |
641 | |
642 | args[2] = NULL; /* Terminate list */ |
643 | |
644 | /* Execute the method, no return value */ |
645 | |
646 | ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname |
647 | (ACPI_TYPE_METHOD, info->prefix_node, NULL)); |
648 | |
649 | status = acpi_ns_evaluate(info); |
650 | acpi_ut_remove_reference(object: args[1]); |
651 | |
652 | if (ACPI_FAILURE(status)) { |
653 | goto cleanup2; |
654 | } |
655 | |
656 | if (function == ACPI_REG_CONNECT) { |
657 | region_obj->common.flags |= AOPOBJ_REG_CONNECTED; |
658 | } else { |
659 | region_obj->common.flags &= ~AOPOBJ_REG_CONNECTED; |
660 | } |
661 | |
662 | cleanup2: |
663 | acpi_ut_remove_reference(object: args[0]); |
664 | |
665 | cleanup1: |
666 | ACPI_FREE(info); |
667 | return_ACPI_STATUS(status); |
668 | } |
669 | |
670 | /******************************************************************************* |
671 | * |
672 | * FUNCTION: acpi_ev_execute_reg_methods |
673 | * |
674 | * PARAMETERS: node - Namespace node for the device |
675 | * space_id - The address space ID |
676 | * function - Passed to _REG: On (1) or Off (0) |
677 | * |
678 | * RETURN: None |
679 | * |
680 | * DESCRIPTION: Run all _REG methods for the input Space ID; |
681 | * Note: assumes namespace is locked, or system init time. |
682 | * |
683 | ******************************************************************************/ |
684 | |
685 | void |
686 | acpi_ev_execute_reg_methods(struct acpi_namespace_node *node, |
687 | acpi_adr_space_type space_id, u32 function) |
688 | { |
689 | struct acpi_reg_walk_info info; |
690 | |
691 | ACPI_FUNCTION_TRACE(ev_execute_reg_methods); |
692 | |
693 | /* |
694 | * These address spaces do not need a call to _REG, since the ACPI |
695 | * specification defines them as: "must always be accessible". Since |
696 | * they never change state (never become unavailable), no need to ever |
697 | * call _REG on them. Also, a data_table is not a "real" address space, |
698 | * so do not call _REG. September 2018. |
699 | */ |
700 | if ((space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) || |
701 | (space_id == ACPI_ADR_SPACE_SYSTEM_IO) || |
702 | (space_id == ACPI_ADR_SPACE_DATA_TABLE)) { |
703 | return_VOID; |
704 | } |
705 | |
706 | info.space_id = space_id; |
707 | info.function = function; |
708 | info.reg_run_count = 0; |
709 | |
710 | ACPI_DEBUG_PRINT_RAW((ACPI_DB_NAMES, |
711 | " Running _REG methods for SpaceId %s\n" , |
712 | acpi_ut_get_region_name(info.space_id))); |
713 | |
714 | /* |
715 | * Run all _REG methods for all Operation Regions for this space ID. This |
716 | * is a separate walk in order to handle any interdependencies between |
717 | * regions and _REG methods. (i.e. handlers must be installed for all |
718 | * regions of this Space ID before we can run any _REG methods) |
719 | */ |
720 | (void)acpi_ns_walk_namespace(ACPI_TYPE_ANY, start_object: node, ACPI_UINT32_MAX, |
721 | ACPI_NS_WALK_UNLOCK, descending_callback: acpi_ev_reg_run, NULL, |
722 | context: &info, NULL); |
723 | |
724 | /* |
725 | * Special case for EC and GPIO: handle "orphan" _REG methods with |
726 | * no region. |
727 | */ |
728 | if (space_id == ACPI_ADR_SPACE_EC || space_id == ACPI_ADR_SPACE_GPIO) { |
729 | acpi_ev_execute_orphan_reg_method(device_node: node, space_id); |
730 | } |
731 | |
732 | ACPI_DEBUG_PRINT_RAW((ACPI_DB_NAMES, |
733 | " Executed %u _REG methods for SpaceId %s\n" , |
734 | info.reg_run_count, |
735 | acpi_ut_get_region_name(info.space_id))); |
736 | |
737 | return_VOID; |
738 | } |
739 | |
740 | /******************************************************************************* |
741 | * |
742 | * FUNCTION: acpi_ev_reg_run |
743 | * |
744 | * PARAMETERS: walk_namespace callback |
745 | * |
746 | * DESCRIPTION: Run _REG method for region objects of the requested spaceID |
747 | * |
748 | ******************************************************************************/ |
749 | |
750 | static acpi_status |
751 | acpi_ev_reg_run(acpi_handle obj_handle, |
752 | u32 level, void *context, void **return_value) |
753 | { |
754 | union acpi_operand_object *obj_desc; |
755 | struct acpi_namespace_node *node; |
756 | acpi_status status; |
757 | struct acpi_reg_walk_info *info; |
758 | |
759 | info = ACPI_CAST_PTR(struct acpi_reg_walk_info, context); |
760 | |
761 | /* Convert and validate the device handle */ |
762 | |
763 | node = acpi_ns_validate_handle(handle: obj_handle); |
764 | if (!node) { |
765 | return (AE_BAD_PARAMETER); |
766 | } |
767 | |
768 | /* |
769 | * We only care about regions and objects that are allowed to have |
770 | * address space handlers |
771 | */ |
772 | if ((node->type != ACPI_TYPE_REGION) && (node != acpi_gbl_root_node)) { |
773 | return (AE_OK); |
774 | } |
775 | |
776 | /* Check for an existing internal object */ |
777 | |
778 | obj_desc = acpi_ns_get_attached_object(node); |
779 | if (!obj_desc) { |
780 | |
781 | /* No object, just exit */ |
782 | |
783 | return (AE_OK); |
784 | } |
785 | |
786 | /* Object is a Region */ |
787 | |
788 | if (obj_desc->region.space_id != info->space_id) { |
789 | |
790 | /* This region is for a different address space, just ignore it */ |
791 | |
792 | return (AE_OK); |
793 | } |
794 | |
795 | info->reg_run_count++; |
796 | status = acpi_ev_execute_reg_method(region_obj: obj_desc, function: info->function); |
797 | return (status); |
798 | } |
799 | |
800 | /******************************************************************************* |
801 | * |
802 | * FUNCTION: acpi_ev_execute_orphan_reg_method |
803 | * |
804 | * PARAMETERS: device_node - Namespace node for an ACPI device |
805 | * space_id - The address space ID |
806 | * |
807 | * RETURN: None |
808 | * |
809 | * DESCRIPTION: Execute an "orphan" _REG method that appears under an ACPI |
810 | * device. This is a _REG method that has no corresponding region |
811 | * within the device's scope. ACPI tables depending on these |
812 | * "orphan" _REG methods have been seen for both EC and GPIO |
813 | * Operation Regions. Presumably the Windows ACPI implementation |
814 | * always calls the _REG method independent of the presence of |
815 | * an actual Operation Region with the correct address space ID. |
816 | * |
817 | * MUTEX: Assumes the namespace is locked |
818 | * |
819 | ******************************************************************************/ |
820 | |
821 | static void |
822 | acpi_ev_execute_orphan_reg_method(struct acpi_namespace_node *device_node, |
823 | acpi_adr_space_type space_id) |
824 | { |
825 | acpi_handle reg_method; |
826 | struct acpi_namespace_node *next_node; |
827 | acpi_status status; |
828 | struct acpi_object_list args; |
829 | union acpi_object objects[2]; |
830 | |
831 | ACPI_FUNCTION_TRACE(ev_execute_orphan_reg_method); |
832 | |
833 | if (!device_node) { |
834 | return_VOID; |
835 | } |
836 | |
837 | /* Namespace is currently locked, must release */ |
838 | |
839 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); |
840 | |
841 | /* Get a handle to a _REG method immediately under the EC device */ |
842 | |
843 | status = acpi_get_handle(parent: device_node, METHOD_NAME__REG, ret_handle: ®_method); |
844 | if (ACPI_FAILURE(status)) { |
845 | goto exit; /* There is no _REG method present */ |
846 | } |
847 | |
848 | /* |
849 | * Execute the _REG method only if there is no Operation Region in |
850 | * this scope with the Embedded Controller space ID. Otherwise, it |
851 | * will already have been executed. Note, this allows for Regions |
852 | * with other space IDs to be present; but the code below will then |
853 | * execute the _REG method with the embedded_control space_ID argument. |
854 | */ |
855 | next_node = acpi_ns_get_next_node(parent: device_node, NULL); |
856 | while (next_node) { |
857 | if ((next_node->type == ACPI_TYPE_REGION) && |
858 | (next_node->object) && |
859 | (next_node->object->region.space_id == space_id)) { |
860 | goto exit; /* Do not execute the _REG */ |
861 | } |
862 | |
863 | next_node = acpi_ns_get_next_node(parent: device_node, child: next_node); |
864 | } |
865 | |
866 | /* Evaluate the _REG(space_id,Connect) method */ |
867 | |
868 | args.count = 2; |
869 | args.pointer = objects; |
870 | objects[0].type = ACPI_TYPE_INTEGER; |
871 | objects[0].integer.value = space_id; |
872 | objects[1].type = ACPI_TYPE_INTEGER; |
873 | objects[1].integer.value = ACPI_REG_CONNECT; |
874 | |
875 | (void)acpi_evaluate_object(object: reg_method, NULL, parameter_objects: &args, NULL); |
876 | |
877 | exit: |
878 | /* We ignore all errors from above, don't care */ |
879 | |
880 | (void)acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); |
881 | return_VOID; |
882 | } |
883 | |