1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /******************************************************************************* |
3 | * |
4 | * Module Name: dbcmds - Miscellaneous debug commands and output routines |
5 | * |
6 | ******************************************************************************/ |
7 | |
8 | #include <acpi/acpi.h> |
9 | #include "accommon.h" |
10 | #include "acevents.h" |
11 | #include "acdebug.h" |
12 | #include "acnamesp.h" |
13 | #include "acresrc.h" |
14 | #include "actables.h" |
15 | |
16 | #define _COMPONENT ACPI_CA_DEBUGGER |
17 | ACPI_MODULE_NAME("dbcmds" ) |
18 | |
19 | /* Local prototypes */ |
20 | static void |
21 | acpi_dm_compare_aml_resources(u8 *aml1_buffer, |
22 | acpi_rsdesc_size aml1_buffer_length, |
23 | u8 *aml2_buffer, |
24 | acpi_rsdesc_size aml2_buffer_length); |
25 | |
26 | static acpi_status |
27 | acpi_dm_test_resource_conversion(struct acpi_namespace_node *node, char *name); |
28 | |
29 | static acpi_status |
30 | acpi_db_resource_callback(struct acpi_resource *resource, void *context); |
31 | |
32 | static acpi_status |
33 | acpi_db_device_resources(acpi_handle obj_handle, |
34 | u32 nesting_level, void *context, void **return_value); |
35 | |
36 | static void acpi_db_do_one_sleep_state(u8 sleep_state); |
37 | |
38 | static char *acpi_db_trace_method_name = NULL; |
39 | |
40 | /******************************************************************************* |
41 | * |
42 | * FUNCTION: acpi_db_convert_to_node |
43 | * |
44 | * PARAMETERS: in_string - String to convert |
45 | * |
46 | * RETURN: Pointer to a NS node |
47 | * |
48 | * DESCRIPTION: Convert a string to a valid NS pointer. Handles numeric or |
49 | * alphanumeric strings. |
50 | * |
51 | ******************************************************************************/ |
52 | |
53 | struct acpi_namespace_node *acpi_db_convert_to_node(char *in_string) |
54 | { |
55 | struct acpi_namespace_node *node; |
56 | acpi_size address; |
57 | |
58 | if ((*in_string >= 0x30) && (*in_string <= 0x39)) { |
59 | |
60 | /* Numeric argument, convert */ |
61 | |
62 | address = strtoul(in_string, NULL, 16); |
63 | node = ACPI_TO_POINTER(address); |
64 | if (!acpi_os_readable(pointer: node, length: sizeof(struct acpi_namespace_node))) { |
65 | acpi_os_printf(format: "Address %p is invalid" , node); |
66 | return (NULL); |
67 | } |
68 | |
69 | /* Make sure pointer is valid NS node */ |
70 | |
71 | if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) { |
72 | acpi_os_printf |
73 | (format: "Address %p is not a valid namespace node [%s]\n" , |
74 | node, acpi_ut_get_descriptor_name(object: node)); |
75 | return (NULL); |
76 | } |
77 | } else { |
78 | /* |
79 | * Alpha argument: The parameter is a name string that must be |
80 | * resolved to a Namespace object. |
81 | */ |
82 | node = acpi_db_local_ns_lookup(name: in_string); |
83 | if (!node) { |
84 | acpi_os_printf |
85 | (format: "Could not find [%s] in namespace, defaulting to root node\n" , |
86 | in_string); |
87 | node = acpi_gbl_root_node; |
88 | } |
89 | } |
90 | |
91 | return (node); |
92 | } |
93 | |
94 | /******************************************************************************* |
95 | * |
96 | * FUNCTION: acpi_db_sleep |
97 | * |
98 | * PARAMETERS: object_arg - Desired sleep state (0-5). NULL means |
99 | * invoke all possible sleep states. |
100 | * |
101 | * RETURN: Status |
102 | * |
103 | * DESCRIPTION: Simulate sleep/wake sequences |
104 | * |
105 | ******************************************************************************/ |
106 | |
107 | acpi_status acpi_db_sleep(char *object_arg) |
108 | { |
109 | u8 sleep_state; |
110 | u32 i; |
111 | |
112 | ACPI_FUNCTION_TRACE(acpi_db_sleep); |
113 | |
114 | /* Null input (no arguments) means to invoke all sleep states */ |
115 | |
116 | if (!object_arg) { |
117 | acpi_os_printf(format: "Invoking all possible sleep states, 0-%d\n" , |
118 | ACPI_S_STATES_MAX); |
119 | |
120 | for (i = 0; i <= ACPI_S_STATES_MAX; i++) { |
121 | acpi_db_do_one_sleep_state(sleep_state: (u8)i); |
122 | } |
123 | |
124 | return_ACPI_STATUS(AE_OK); |
125 | } |
126 | |
127 | /* Convert argument to binary and invoke the sleep state */ |
128 | |
129 | sleep_state = (u8)strtoul(object_arg, NULL, 0); |
130 | acpi_db_do_one_sleep_state(sleep_state); |
131 | return_ACPI_STATUS(AE_OK); |
132 | } |
133 | |
134 | /******************************************************************************* |
135 | * |
136 | * FUNCTION: acpi_db_do_one_sleep_state |
137 | * |
138 | * PARAMETERS: sleep_state - Desired sleep state (0-5) |
139 | * |
140 | * RETURN: None |
141 | * |
142 | * DESCRIPTION: Simulate a sleep/wake sequence |
143 | * |
144 | ******************************************************************************/ |
145 | |
146 | static void acpi_db_do_one_sleep_state(u8 sleep_state) |
147 | { |
148 | acpi_status status; |
149 | u8 sleep_type_a; |
150 | u8 sleep_type_b; |
151 | |
152 | /* Validate parameter */ |
153 | |
154 | if (sleep_state > ACPI_S_STATES_MAX) { |
155 | acpi_os_printf(format: "Sleep state %d out of range (%d max)\n" , |
156 | sleep_state, ACPI_S_STATES_MAX); |
157 | return; |
158 | } |
159 | |
160 | acpi_os_printf(format: "\n---- Invoking sleep state S%d (%s):\n" , |
161 | sleep_state, acpi_gbl_sleep_state_names[sleep_state]); |
162 | |
163 | /* Get the values for the sleep type registers (for display only) */ |
164 | |
165 | status = |
166 | acpi_get_sleep_type_data(sleep_state, slp_typ_a: &sleep_type_a, slp_typ_b: &sleep_type_b); |
167 | if (ACPI_FAILURE(status)) { |
168 | acpi_os_printf(format: "Could not evaluate [%s] method, %s\n" , |
169 | acpi_gbl_sleep_state_names[sleep_state], |
170 | acpi_format_exception(exception: status)); |
171 | return; |
172 | } |
173 | |
174 | acpi_os_printf |
175 | (format: "Register values for sleep state S%d: Sleep-A: %.2X, Sleep-B: %.2X\n" , |
176 | sleep_state, sleep_type_a, sleep_type_b); |
177 | |
178 | /* Invoke the various sleep/wake interfaces */ |
179 | |
180 | acpi_os_printf(format: "**** Sleep: Prepare to sleep (S%d) ****\n" , |
181 | sleep_state); |
182 | status = acpi_enter_sleep_state_prep(sleep_state); |
183 | if (ACPI_FAILURE(status)) { |
184 | goto error_exit; |
185 | } |
186 | |
187 | acpi_os_printf(format: "**** Sleep: Going to sleep (S%d) ****\n" , sleep_state); |
188 | status = acpi_enter_sleep_state(sleep_state); |
189 | if (ACPI_FAILURE(status)) { |
190 | goto error_exit; |
191 | } |
192 | |
193 | acpi_os_printf(format: "**** Wake: Prepare to return from sleep (S%d) ****\n" , |
194 | sleep_state); |
195 | status = acpi_leave_sleep_state_prep(sleep_state); |
196 | if (ACPI_FAILURE(status)) { |
197 | goto error_exit; |
198 | } |
199 | |
200 | acpi_os_printf(format: "**** Wake: Return from sleep (S%d) ****\n" , |
201 | sleep_state); |
202 | status = acpi_leave_sleep_state(sleep_state); |
203 | if (ACPI_FAILURE(status)) { |
204 | goto error_exit; |
205 | } |
206 | |
207 | return; |
208 | |
209 | error_exit: |
210 | ACPI_EXCEPTION((AE_INFO, status, "During invocation of sleep state S%d" , |
211 | sleep_state)); |
212 | } |
213 | |
214 | /******************************************************************************* |
215 | * |
216 | * FUNCTION: acpi_db_display_locks |
217 | * |
218 | * PARAMETERS: None |
219 | * |
220 | * RETURN: None |
221 | * |
222 | * DESCRIPTION: Display information about internal mutexes. |
223 | * |
224 | ******************************************************************************/ |
225 | |
226 | void acpi_db_display_locks(void) |
227 | { |
228 | u32 i; |
229 | |
230 | for (i = 0; i < ACPI_MAX_MUTEX; i++) { |
231 | acpi_os_printf(format: "%26s : %s\n" , acpi_ut_get_mutex_name(mutex_id: i), |
232 | acpi_gbl_mutex_info[i].thread_id == |
233 | ACPI_MUTEX_NOT_ACQUIRED ? "Locked" : "Unlocked" ); |
234 | } |
235 | } |
236 | |
237 | /******************************************************************************* |
238 | * |
239 | * FUNCTION: acpi_db_display_table_info |
240 | * |
241 | * PARAMETERS: table_arg - Name of table to be displayed |
242 | * |
243 | * RETURN: None |
244 | * |
245 | * DESCRIPTION: Display information about loaded tables. Current |
246 | * implementation displays all loaded tables. |
247 | * |
248 | ******************************************************************************/ |
249 | |
250 | void acpi_db_display_table_info(char *table_arg) |
251 | { |
252 | u32 i; |
253 | struct acpi_table_desc *table_desc; |
254 | acpi_status status; |
255 | |
256 | /* Header */ |
257 | |
258 | acpi_os_printf(format: "Idx ID Status Type " |
259 | "TableHeader (Sig, Address, Length, Misc)\n" ); |
260 | |
261 | /* Walk the entire root table list */ |
262 | |
263 | for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) { |
264 | table_desc = &acpi_gbl_root_table_list.tables[i]; |
265 | |
266 | /* Index and Table ID */ |
267 | |
268 | acpi_os_printf(format: "%3u %.2u " , i, table_desc->owner_id); |
269 | |
270 | /* Decode the table flags */ |
271 | |
272 | if (!(table_desc->flags & ACPI_TABLE_IS_LOADED)) { |
273 | acpi_os_printf(format: "NotLoaded " ); |
274 | } else { |
275 | acpi_os_printf(format: " Loaded " ); |
276 | } |
277 | |
278 | switch (table_desc->flags & ACPI_TABLE_ORIGIN_MASK) { |
279 | case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL: |
280 | |
281 | acpi_os_printf(format: "External/virtual " ); |
282 | break; |
283 | |
284 | case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL: |
285 | |
286 | acpi_os_printf(format: "Internal/physical " ); |
287 | break; |
288 | |
289 | case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL: |
290 | |
291 | acpi_os_printf(format: "Internal/virtual " ); |
292 | break; |
293 | |
294 | default: |
295 | |
296 | acpi_os_printf(format: "INVALID TYPE " ); |
297 | break; |
298 | } |
299 | |
300 | /* Make sure that the table is mapped */ |
301 | |
302 | status = acpi_tb_validate_table(table_desc); |
303 | if (ACPI_FAILURE(status)) { |
304 | return; |
305 | } |
306 | |
307 | /* Dump the table header */ |
308 | |
309 | if (table_desc->pointer) { |
310 | acpi_tb_print_table_header(address: table_desc->address, |
311 | header: table_desc->pointer); |
312 | } else { |
313 | /* If the pointer is null, the table has been unloaded */ |
314 | |
315 | ACPI_INFO(("%4.4s - Table has been unloaded" , |
316 | table_desc->signature.ascii)); |
317 | } |
318 | } |
319 | } |
320 | |
321 | /******************************************************************************* |
322 | * |
323 | * FUNCTION: acpi_db_unload_acpi_table |
324 | * |
325 | * PARAMETERS: object_name - Namespace pathname for an object that |
326 | * is owned by the table to be unloaded |
327 | * |
328 | * RETURN: None |
329 | * |
330 | * DESCRIPTION: Unload an ACPI table, via any namespace node that is owned |
331 | * by the table. |
332 | * |
333 | ******************************************************************************/ |
334 | |
335 | void acpi_db_unload_acpi_table(char *object_name) |
336 | { |
337 | struct acpi_namespace_node *node; |
338 | acpi_status status; |
339 | |
340 | /* Translate name to an Named object */ |
341 | |
342 | node = acpi_db_convert_to_node(in_string: object_name); |
343 | if (!node) { |
344 | return; |
345 | } |
346 | |
347 | status = acpi_unload_parent_table(ACPI_CAST_PTR(acpi_handle, node)); |
348 | if (ACPI_SUCCESS(status)) { |
349 | acpi_os_printf(format: "Parent of [%s] (%p) unloaded and uninstalled\n" , |
350 | object_name, node); |
351 | } else { |
352 | acpi_os_printf(format: "%s, while unloading parent table of [%s]\n" , |
353 | acpi_format_exception(exception: status), object_name); |
354 | } |
355 | } |
356 | |
357 | /******************************************************************************* |
358 | * |
359 | * FUNCTION: acpi_db_send_notify |
360 | * |
361 | * PARAMETERS: name - Name of ACPI object where to send notify |
362 | * value - Value of the notify to send. |
363 | * |
364 | * RETURN: None |
365 | * |
366 | * DESCRIPTION: Send an ACPI notification. The value specified is sent to the |
367 | * named object as an ACPI notify. |
368 | * |
369 | ******************************************************************************/ |
370 | |
371 | void acpi_db_send_notify(char *name, u32 value) |
372 | { |
373 | struct acpi_namespace_node *node; |
374 | acpi_status status; |
375 | |
376 | /* Translate name to an Named object */ |
377 | |
378 | node = acpi_db_convert_to_node(in_string: name); |
379 | if (!node) { |
380 | return; |
381 | } |
382 | |
383 | /* Dispatch the notify if legal */ |
384 | |
385 | if (acpi_ev_is_notify_object(node)) { |
386 | status = acpi_ev_queue_notify_request(node, notify_value: value); |
387 | if (ACPI_FAILURE(status)) { |
388 | acpi_os_printf(format: "Could not queue notify\n" ); |
389 | } |
390 | } else { |
391 | acpi_os_printf(format: "Named object [%4.4s] Type %s, " |
392 | "must be Device/Thermal/Processor type\n" , |
393 | acpi_ut_get_node_name(object: node), |
394 | acpi_ut_get_type_name(type: node->type)); |
395 | } |
396 | } |
397 | |
398 | /******************************************************************************* |
399 | * |
400 | * FUNCTION: acpi_db_display_interfaces |
401 | * |
402 | * PARAMETERS: action_arg - Null, "install", or "remove" |
403 | * interface_name_arg - Name for install/remove options |
404 | * |
405 | * RETURN: None |
406 | * |
407 | * DESCRIPTION: Display or modify the global _OSI interface list |
408 | * |
409 | ******************************************************************************/ |
410 | |
411 | void acpi_db_display_interfaces(char *action_arg, char *interface_name_arg) |
412 | { |
413 | struct acpi_interface_info *next_interface; |
414 | char *sub_string; |
415 | acpi_status status; |
416 | |
417 | /* If no arguments, just display current interface list */ |
418 | |
419 | if (!action_arg) { |
420 | (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, |
421 | ACPI_WAIT_FOREVER); |
422 | |
423 | next_interface = acpi_gbl_supported_interfaces; |
424 | while (next_interface) { |
425 | if (!(next_interface->flags & ACPI_OSI_INVALID)) { |
426 | acpi_os_printf(format: "%s\n" , next_interface->name); |
427 | } |
428 | |
429 | next_interface = next_interface->next; |
430 | } |
431 | |
432 | acpi_os_release_mutex(acpi_gbl_osi_mutex); |
433 | return; |
434 | } |
435 | |
436 | /* If action_arg exists, so must interface_name_arg */ |
437 | |
438 | if (!interface_name_arg) { |
439 | acpi_os_printf(format: "Missing Interface Name argument\n" ); |
440 | return; |
441 | } |
442 | |
443 | /* Uppercase the action for match below */ |
444 | |
445 | acpi_ut_strupr(src_string: action_arg); |
446 | |
447 | /* install - install an interface */ |
448 | |
449 | sub_string = strstr("INSTALL" , action_arg); |
450 | if (sub_string) { |
451 | status = acpi_install_interface(interface_name: interface_name_arg); |
452 | if (ACPI_FAILURE(status)) { |
453 | acpi_os_printf(format: "%s, while installing \"%s\"\n" , |
454 | acpi_format_exception(exception: status), |
455 | interface_name_arg); |
456 | } |
457 | return; |
458 | } |
459 | |
460 | /* remove - remove an interface */ |
461 | |
462 | sub_string = strstr("REMOVE" , action_arg); |
463 | if (sub_string) { |
464 | status = acpi_remove_interface(interface_name: interface_name_arg); |
465 | if (ACPI_FAILURE(status)) { |
466 | acpi_os_printf(format: "%s, while removing \"%s\"\n" , |
467 | acpi_format_exception(exception: status), |
468 | interface_name_arg); |
469 | } |
470 | return; |
471 | } |
472 | |
473 | /* Invalid action_arg */ |
474 | |
475 | acpi_os_printf(format: "Invalid action argument: %s\n" , action_arg); |
476 | return; |
477 | } |
478 | |
479 | /******************************************************************************* |
480 | * |
481 | * FUNCTION: acpi_db_display_template |
482 | * |
483 | * PARAMETERS: buffer_arg - Buffer name or address |
484 | * |
485 | * RETURN: None |
486 | * |
487 | * DESCRIPTION: Dump a buffer that contains a resource template |
488 | * |
489 | ******************************************************************************/ |
490 | |
491 | void acpi_db_display_template(char *buffer_arg) |
492 | { |
493 | struct acpi_namespace_node *node; |
494 | acpi_status status; |
495 | struct acpi_buffer return_buffer; |
496 | |
497 | /* Translate buffer_arg to an Named object */ |
498 | |
499 | node = acpi_db_convert_to_node(in_string: buffer_arg); |
500 | if (!node || (node == acpi_gbl_root_node)) { |
501 | acpi_os_printf(format: "Invalid argument: %s\n" , buffer_arg); |
502 | return; |
503 | } |
504 | |
505 | /* We must have a buffer object */ |
506 | |
507 | if (node->type != ACPI_TYPE_BUFFER) { |
508 | acpi_os_printf |
509 | (format: "Not a Buffer object, cannot be a template: %s\n" , |
510 | buffer_arg); |
511 | return; |
512 | } |
513 | |
514 | return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; |
515 | return_buffer.pointer = acpi_gbl_db_buffer; |
516 | |
517 | /* Attempt to convert the raw buffer to a resource list */ |
518 | |
519 | status = acpi_rs_create_resource_list(aml_buffer: node->object, output_buffer: &return_buffer); |
520 | |
521 | acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); |
522 | acpi_dbg_level |= ACPI_LV_RESOURCES; |
523 | |
524 | if (ACPI_FAILURE(status)) { |
525 | acpi_os_printf |
526 | (format: "Could not convert Buffer to a resource list: %s, %s\n" , |
527 | buffer_arg, acpi_format_exception(exception: status)); |
528 | goto dump_buffer; |
529 | } |
530 | |
531 | /* Now we can dump the resource list */ |
532 | |
533 | acpi_rs_dump_resource_list(ACPI_CAST_PTR(struct acpi_resource, |
534 | return_buffer.pointer)); |
535 | |
536 | dump_buffer: |
537 | acpi_os_printf(format: "\nRaw data buffer:\n" ); |
538 | acpi_ut_debug_dump_buffer(buffer: (u8 *)node->object->buffer.pointer, |
539 | count: node->object->buffer.length, |
540 | DB_BYTE_DISPLAY, ACPI_UINT32_MAX); |
541 | |
542 | acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); |
543 | return; |
544 | } |
545 | |
546 | /******************************************************************************* |
547 | * |
548 | * FUNCTION: acpi_dm_compare_aml_resources |
549 | * |
550 | * PARAMETERS: aml1_buffer - Contains first resource list |
551 | * aml1_buffer_length - Length of first resource list |
552 | * aml2_buffer - Contains second resource list |
553 | * aml2_buffer_length - Length of second resource list |
554 | * |
555 | * RETURN: None |
556 | * |
557 | * DESCRIPTION: Compare two AML resource lists, descriptor by descriptor (in |
558 | * order to isolate a miscompare to an individual resource) |
559 | * |
560 | ******************************************************************************/ |
561 | |
562 | static void |
563 | acpi_dm_compare_aml_resources(u8 *aml1_buffer, |
564 | acpi_rsdesc_size aml1_buffer_length, |
565 | u8 *aml2_buffer, |
566 | acpi_rsdesc_size aml2_buffer_length) |
567 | { |
568 | u8 *aml1; |
569 | u8 *aml2; |
570 | u8 *aml1_end; |
571 | u8 *aml2_end; |
572 | acpi_rsdesc_size aml1_length; |
573 | acpi_rsdesc_size aml2_length; |
574 | acpi_rsdesc_size offset = 0; |
575 | u8 resource_type; |
576 | u32 count = 0; |
577 | u32 i; |
578 | |
579 | /* Compare overall buffer sizes (may be different due to size rounding) */ |
580 | |
581 | if (aml1_buffer_length != aml2_buffer_length) { |
582 | acpi_os_printf(format: "**** Buffer length mismatch in converted " |
583 | "AML: Original %X, New %X ****\n" , |
584 | aml1_buffer_length, aml2_buffer_length); |
585 | } |
586 | |
587 | aml1 = aml1_buffer; |
588 | aml2 = aml2_buffer; |
589 | aml1_end = aml1_buffer + aml1_buffer_length; |
590 | aml2_end = aml2_buffer + aml2_buffer_length; |
591 | |
592 | /* Walk the descriptor lists, comparing each descriptor */ |
593 | |
594 | while ((aml1 < aml1_end) && (aml2 < aml2_end)) { |
595 | |
596 | /* Get the lengths of each descriptor */ |
597 | |
598 | aml1_length = acpi_ut_get_descriptor_length(aml: aml1); |
599 | aml2_length = acpi_ut_get_descriptor_length(aml: aml2); |
600 | resource_type = acpi_ut_get_resource_type(aml: aml1); |
601 | |
602 | /* Check for descriptor length match */ |
603 | |
604 | if (aml1_length != aml2_length) { |
605 | acpi_os_printf |
606 | (format: "**** Length mismatch in descriptor [%.2X] type %2.2X, " |
607 | "Offset %8.8X Len1 %X, Len2 %X ****\n" , count, |
608 | resource_type, offset, aml1_length, aml2_length); |
609 | } |
610 | |
611 | /* Check for descriptor byte match */ |
612 | |
613 | else if (memcmp(p: aml1, q: aml2, size: aml1_length)) { |
614 | acpi_os_printf |
615 | (format: "**** Data mismatch in descriptor [%.2X] type %2.2X, " |
616 | "Offset %8.8X ****\n" , count, resource_type, |
617 | offset); |
618 | |
619 | for (i = 0; i < aml1_length; i++) { |
620 | if (aml1[i] != aml2[i]) { |
621 | acpi_os_printf |
622 | (format: "Mismatch at byte offset %.2X: is %2.2X, " |
623 | "should be %2.2X\n" , i, aml2[i], |
624 | aml1[i]); |
625 | } |
626 | } |
627 | } |
628 | |
629 | /* Exit on end_tag descriptor */ |
630 | |
631 | if (resource_type == ACPI_RESOURCE_NAME_END_TAG) { |
632 | return; |
633 | } |
634 | |
635 | /* Point to next descriptor in each buffer */ |
636 | |
637 | count++; |
638 | offset += aml1_length; |
639 | aml1 += aml1_length; |
640 | aml2 += aml2_length; |
641 | } |
642 | } |
643 | |
644 | /******************************************************************************* |
645 | * |
646 | * FUNCTION: acpi_dm_test_resource_conversion |
647 | * |
648 | * PARAMETERS: node - Parent device node |
649 | * name - resource method name (_CRS) |
650 | * |
651 | * RETURN: Status |
652 | * |
653 | * DESCRIPTION: Compare the original AML with a conversion of the AML to |
654 | * internal resource list, then back to AML. |
655 | * |
656 | ******************************************************************************/ |
657 | |
658 | static acpi_status |
659 | acpi_dm_test_resource_conversion(struct acpi_namespace_node *node, char *name) |
660 | { |
661 | acpi_status status; |
662 | struct acpi_buffer return_buffer; |
663 | struct acpi_buffer resource_buffer; |
664 | struct acpi_buffer new_aml; |
665 | union acpi_object *original_aml; |
666 | |
667 | acpi_os_printf(format: "Resource Conversion Comparison:\n" ); |
668 | |
669 | new_aml.length = ACPI_ALLOCATE_LOCAL_BUFFER; |
670 | return_buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; |
671 | resource_buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; |
672 | |
673 | /* Get the original _CRS AML resource template */ |
674 | |
675 | status = acpi_evaluate_object(object: node, pathname: name, NULL, return_object_buffer: &return_buffer); |
676 | if (ACPI_FAILURE(status)) { |
677 | acpi_os_printf(format: "Could not obtain %s: %s\n" , |
678 | name, acpi_format_exception(exception: status)); |
679 | return (status); |
680 | } |
681 | |
682 | /* Get the AML resource template, converted to internal resource structs */ |
683 | |
684 | status = acpi_get_current_resources(device: node, ret_buffer: &resource_buffer); |
685 | if (ACPI_FAILURE(status)) { |
686 | acpi_os_printf(format: "AcpiGetCurrentResources failed: %s\n" , |
687 | acpi_format_exception(exception: status)); |
688 | goto exit1; |
689 | } |
690 | |
691 | /* Convert internal resource list to external AML resource template */ |
692 | |
693 | status = acpi_rs_create_aml_resources(resource_list: &resource_buffer, output_buffer: &new_aml); |
694 | if (ACPI_FAILURE(status)) { |
695 | acpi_os_printf(format: "AcpiRsCreateAmlResources failed: %s\n" , |
696 | acpi_format_exception(exception: status)); |
697 | goto exit2; |
698 | } |
699 | |
700 | /* Compare original AML to the newly created AML resource list */ |
701 | |
702 | original_aml = return_buffer.pointer; |
703 | |
704 | acpi_dm_compare_aml_resources(aml1_buffer: original_aml->buffer.pointer, |
705 | aml1_buffer_length: (acpi_rsdesc_size)original_aml->buffer. |
706 | length, aml2_buffer: new_aml.pointer, |
707 | aml2_buffer_length: (acpi_rsdesc_size)new_aml.length); |
708 | |
709 | /* Cleanup and exit */ |
710 | |
711 | ACPI_FREE(new_aml.pointer); |
712 | exit2: |
713 | ACPI_FREE(resource_buffer.pointer); |
714 | exit1: |
715 | ACPI_FREE(return_buffer.pointer); |
716 | return (status); |
717 | } |
718 | |
719 | /******************************************************************************* |
720 | * |
721 | * FUNCTION: acpi_db_resource_callback |
722 | * |
723 | * PARAMETERS: acpi_walk_resource_callback |
724 | * |
725 | * RETURN: Status |
726 | * |
727 | * DESCRIPTION: Simple callback to exercise acpi_walk_resources and |
728 | * acpi_walk_resource_buffer. |
729 | * |
730 | ******************************************************************************/ |
731 | |
732 | static acpi_status |
733 | acpi_db_resource_callback(struct acpi_resource *resource, void *context) |
734 | { |
735 | |
736 | return (AE_OK); |
737 | } |
738 | |
739 | /******************************************************************************* |
740 | * |
741 | * FUNCTION: acpi_db_device_resources |
742 | * |
743 | * PARAMETERS: acpi_walk_callback |
744 | * |
745 | * RETURN: Status |
746 | * |
747 | * DESCRIPTION: Display the _PRT/_CRS/_PRS resources for a device object. |
748 | * |
749 | ******************************************************************************/ |
750 | |
751 | static acpi_status |
752 | acpi_db_device_resources(acpi_handle obj_handle, |
753 | u32 nesting_level, void *context, void **return_value) |
754 | { |
755 | struct acpi_namespace_node *node; |
756 | struct acpi_namespace_node *prt_node = NULL; |
757 | struct acpi_namespace_node *crs_node = NULL; |
758 | struct acpi_namespace_node *prs_node = NULL; |
759 | struct acpi_namespace_node *aei_node = NULL; |
760 | char *parent_path; |
761 | struct acpi_buffer return_buffer; |
762 | acpi_status status; |
763 | |
764 | node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle); |
765 | parent_path = acpi_ns_get_normalized_pathname(node, TRUE); |
766 | if (!parent_path) { |
767 | return (AE_NO_MEMORY); |
768 | } |
769 | |
770 | /* Get handles to the resource methods for this device */ |
771 | |
772 | (void)acpi_get_handle(parent: node, METHOD_NAME__PRT, |
773 | ACPI_CAST_PTR(acpi_handle, &prt_node)); |
774 | (void)acpi_get_handle(parent: node, METHOD_NAME__CRS, |
775 | ACPI_CAST_PTR(acpi_handle, &crs_node)); |
776 | (void)acpi_get_handle(parent: node, METHOD_NAME__PRS, |
777 | ACPI_CAST_PTR(acpi_handle, &prs_node)); |
778 | (void)acpi_get_handle(parent: node, METHOD_NAME__AEI, |
779 | ACPI_CAST_PTR(acpi_handle, &aei_node)); |
780 | |
781 | if (!prt_node && !crs_node && !prs_node && !aei_node) { |
782 | goto cleanup; /* Nothing to do */ |
783 | } |
784 | |
785 | acpi_os_printf(format: "\nDevice: %s\n" , parent_path); |
786 | |
787 | /* Prepare for a return object of arbitrary size */ |
788 | |
789 | return_buffer.pointer = acpi_gbl_db_buffer; |
790 | return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; |
791 | |
792 | /* _PRT */ |
793 | |
794 | if (prt_node) { |
795 | acpi_os_printf(format: "Evaluating _PRT\n" ); |
796 | |
797 | status = |
798 | acpi_evaluate_object(object: prt_node, NULL, NULL, return_object_buffer: &return_buffer); |
799 | if (ACPI_FAILURE(status)) { |
800 | acpi_os_printf(format: "Could not evaluate _PRT: %s\n" , |
801 | acpi_format_exception(exception: status)); |
802 | goto get_crs; |
803 | } |
804 | |
805 | return_buffer.pointer = acpi_gbl_db_buffer; |
806 | return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; |
807 | |
808 | status = acpi_get_irq_routing_table(device: node, ret_buffer: &return_buffer); |
809 | if (ACPI_FAILURE(status)) { |
810 | acpi_os_printf(format: "GetIrqRoutingTable failed: %s\n" , |
811 | acpi_format_exception(exception: status)); |
812 | goto get_crs; |
813 | } |
814 | |
815 | acpi_rs_dump_irq_list(ACPI_CAST_PTR(u8, acpi_gbl_db_buffer)); |
816 | } |
817 | |
818 | /* _CRS */ |
819 | |
820 | get_crs: |
821 | if (crs_node) { |
822 | acpi_os_printf(format: "Evaluating _CRS\n" ); |
823 | |
824 | return_buffer.pointer = acpi_gbl_db_buffer; |
825 | return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; |
826 | |
827 | status = |
828 | acpi_evaluate_object(object: crs_node, NULL, NULL, return_object_buffer: &return_buffer); |
829 | if (ACPI_FAILURE(status)) { |
830 | acpi_os_printf(format: "Could not evaluate _CRS: %s\n" , |
831 | acpi_format_exception(exception: status)); |
832 | goto get_prs; |
833 | } |
834 | |
835 | /* This code exercises the acpi_walk_resources interface */ |
836 | |
837 | status = acpi_walk_resources(device: node, METHOD_NAME__CRS, |
838 | user_function: acpi_db_resource_callback, NULL); |
839 | if (ACPI_FAILURE(status)) { |
840 | acpi_os_printf(format: "AcpiWalkResources failed: %s\n" , |
841 | acpi_format_exception(exception: status)); |
842 | goto get_prs; |
843 | } |
844 | |
845 | /* Get the _CRS resource list (test ALLOCATE buffer) */ |
846 | |
847 | return_buffer.pointer = NULL; |
848 | return_buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; |
849 | |
850 | status = acpi_get_current_resources(device: node, ret_buffer: &return_buffer); |
851 | if (ACPI_FAILURE(status)) { |
852 | acpi_os_printf(format: "AcpiGetCurrentResources failed: %s\n" , |
853 | acpi_format_exception(exception: status)); |
854 | goto get_prs; |
855 | } |
856 | |
857 | /* This code exercises the acpi_walk_resource_buffer interface */ |
858 | |
859 | status = acpi_walk_resource_buffer(buffer: &return_buffer, |
860 | user_function: acpi_db_resource_callback, |
861 | NULL); |
862 | if (ACPI_FAILURE(status)) { |
863 | acpi_os_printf(format: "AcpiWalkResourceBuffer failed: %s\n" , |
864 | acpi_format_exception(exception: status)); |
865 | goto end_crs; |
866 | } |
867 | |
868 | /* Dump the _CRS resource list */ |
869 | |
870 | acpi_rs_dump_resource_list(ACPI_CAST_PTR(struct acpi_resource, |
871 | return_buffer. |
872 | pointer)); |
873 | |
874 | /* |
875 | * Perform comparison of original AML to newly created AML. This |
876 | * tests both the AML->Resource conversion and the Resource->AML |
877 | * conversion. |
878 | */ |
879 | (void)acpi_dm_test_resource_conversion(node, METHOD_NAME__CRS); |
880 | |
881 | /* Execute _SRS with the resource list */ |
882 | |
883 | acpi_os_printf(format: "Evaluating _SRS\n" ); |
884 | |
885 | status = acpi_set_current_resources(device: node, in_buffer: &return_buffer); |
886 | if (ACPI_FAILURE(status)) { |
887 | acpi_os_printf(format: "AcpiSetCurrentResources failed: %s\n" , |
888 | acpi_format_exception(exception: status)); |
889 | goto end_crs; |
890 | } |
891 | |
892 | end_crs: |
893 | ACPI_FREE(return_buffer.pointer); |
894 | } |
895 | |
896 | /* _PRS */ |
897 | |
898 | get_prs: |
899 | if (prs_node) { |
900 | acpi_os_printf(format: "Evaluating _PRS\n" ); |
901 | |
902 | return_buffer.pointer = acpi_gbl_db_buffer; |
903 | return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; |
904 | |
905 | status = |
906 | acpi_evaluate_object(object: prs_node, NULL, NULL, return_object_buffer: &return_buffer); |
907 | if (ACPI_FAILURE(status)) { |
908 | acpi_os_printf(format: "Could not evaluate _PRS: %s\n" , |
909 | acpi_format_exception(exception: status)); |
910 | goto get_aei; |
911 | } |
912 | |
913 | return_buffer.pointer = acpi_gbl_db_buffer; |
914 | return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; |
915 | |
916 | status = acpi_get_possible_resources(device: node, ret_buffer: &return_buffer); |
917 | if (ACPI_FAILURE(status)) { |
918 | acpi_os_printf(format: "AcpiGetPossibleResources failed: %s\n" , |
919 | acpi_format_exception(exception: status)); |
920 | goto get_aei; |
921 | } |
922 | |
923 | acpi_rs_dump_resource_list(ACPI_CAST_PTR |
924 | (struct acpi_resource, |
925 | acpi_gbl_db_buffer)); |
926 | } |
927 | |
928 | /* _AEI */ |
929 | |
930 | get_aei: |
931 | if (aei_node) { |
932 | acpi_os_printf(format: "Evaluating _AEI\n" ); |
933 | |
934 | return_buffer.pointer = acpi_gbl_db_buffer; |
935 | return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; |
936 | |
937 | status = |
938 | acpi_evaluate_object(object: aei_node, NULL, NULL, return_object_buffer: &return_buffer); |
939 | if (ACPI_FAILURE(status)) { |
940 | acpi_os_printf(format: "Could not evaluate _AEI: %s\n" , |
941 | acpi_format_exception(exception: status)); |
942 | goto cleanup; |
943 | } |
944 | |
945 | return_buffer.pointer = acpi_gbl_db_buffer; |
946 | return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; |
947 | |
948 | status = acpi_get_event_resources(device_handle: node, ret_buffer: &return_buffer); |
949 | if (ACPI_FAILURE(status)) { |
950 | acpi_os_printf(format: "AcpiGetEventResources failed: %s\n" , |
951 | acpi_format_exception(exception: status)); |
952 | goto cleanup; |
953 | } |
954 | |
955 | acpi_rs_dump_resource_list(ACPI_CAST_PTR |
956 | (struct acpi_resource, |
957 | acpi_gbl_db_buffer)); |
958 | } |
959 | |
960 | cleanup: |
961 | ACPI_FREE(parent_path); |
962 | return (AE_OK); |
963 | } |
964 | |
965 | /******************************************************************************* |
966 | * |
967 | * FUNCTION: acpi_db_display_resources |
968 | * |
969 | * PARAMETERS: object_arg - String object name or object pointer. |
970 | * NULL or "*" means "display resources for |
971 | * all devices" |
972 | * |
973 | * RETURN: None |
974 | * |
975 | * DESCRIPTION: Display the resource objects associated with a device. |
976 | * |
977 | ******************************************************************************/ |
978 | |
979 | void acpi_db_display_resources(char *object_arg) |
980 | { |
981 | struct acpi_namespace_node *node; |
982 | |
983 | acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); |
984 | acpi_dbg_level |= ACPI_LV_RESOURCES; |
985 | |
986 | /* Asterisk means "display resources for all devices" */ |
987 | |
988 | if (!object_arg || (!strcmp(object_arg, "*" ))) { |
989 | (void)acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, |
990 | ACPI_UINT32_MAX, |
991 | descending_callback: acpi_db_device_resources, NULL, NULL, |
992 | NULL); |
993 | } else { |
994 | /* Convert string to object pointer */ |
995 | |
996 | node = acpi_db_convert_to_node(in_string: object_arg); |
997 | if (node) { |
998 | if (node->type != ACPI_TYPE_DEVICE) { |
999 | acpi_os_printf |
1000 | (format: "%4.4s: Name is not a device object (%s)\n" , |
1001 | node->name.ascii, |
1002 | acpi_ut_get_type_name(type: node->type)); |
1003 | } else { |
1004 | (void)acpi_db_device_resources(obj_handle: node, nesting_level: 0, NULL, |
1005 | NULL); |
1006 | } |
1007 | } |
1008 | } |
1009 | |
1010 | acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); |
1011 | } |
1012 | |
1013 | /******************************************************************************* |
1014 | * |
1015 | * FUNCTION: acpi_db_generate_ged |
1016 | * |
1017 | * PARAMETERS: ged_arg - Raw GED number, ascii string |
1018 | * |
1019 | * RETURN: None |
1020 | * |
1021 | * DESCRIPTION: Simulate firing of a GED |
1022 | * |
1023 | ******************************************************************************/ |
1024 | |
1025 | void acpi_db_generate_interrupt(char *gsiv_arg) |
1026 | { |
1027 | u32 gsiv_number; |
1028 | struct acpi_ged_handler_info *ged_info = acpi_gbl_ged_handler_list; |
1029 | |
1030 | if (!ged_info) { |
1031 | acpi_os_printf(format: "No GED handling present\n" ); |
1032 | } |
1033 | |
1034 | gsiv_number = strtoul(gsiv_arg, NULL, 0); |
1035 | |
1036 | while (ged_info) { |
1037 | |
1038 | if (ged_info->int_id == gsiv_number) { |
1039 | struct acpi_object_list arg_list; |
1040 | union acpi_object arg0; |
1041 | acpi_handle evt_handle = ged_info->evt_method; |
1042 | acpi_status status; |
1043 | |
1044 | acpi_os_printf(format: "Evaluate GED _EVT (GSIV=%d)\n" , |
1045 | gsiv_number); |
1046 | |
1047 | if (!evt_handle) { |
1048 | acpi_os_printf(format: "Undefined _EVT method\n" ); |
1049 | return; |
1050 | } |
1051 | |
1052 | arg0.integer.type = ACPI_TYPE_INTEGER; |
1053 | arg0.integer.value = gsiv_number; |
1054 | |
1055 | arg_list.count = 1; |
1056 | arg_list.pointer = &arg0; |
1057 | |
1058 | status = |
1059 | acpi_evaluate_object(object: evt_handle, NULL, parameter_objects: &arg_list, |
1060 | NULL); |
1061 | if (ACPI_FAILURE(status)) { |
1062 | acpi_os_printf(format: "Could not evaluate _EVT\n" ); |
1063 | return; |
1064 | } |
1065 | |
1066 | } |
1067 | ged_info = ged_info->next; |
1068 | } |
1069 | } |
1070 | |
1071 | #if (!ACPI_REDUCED_HARDWARE) |
1072 | /******************************************************************************* |
1073 | * |
1074 | * FUNCTION: acpi_db_generate_gpe |
1075 | * |
1076 | * PARAMETERS: gpe_arg - Raw GPE number, ascii string |
1077 | * block_arg - GPE block number, ascii string |
1078 | * 0 or 1 for FADT GPE blocks |
1079 | * |
1080 | * RETURN: None |
1081 | * |
1082 | * DESCRIPTION: Simulate firing of a GPE |
1083 | * |
1084 | ******************************************************************************/ |
1085 | |
1086 | void acpi_db_generate_gpe(char *gpe_arg, char *block_arg) |
1087 | { |
1088 | u32 block_number = 0; |
1089 | u32 gpe_number; |
1090 | struct acpi_gpe_event_info *gpe_event_info; |
1091 | |
1092 | gpe_number = strtoul(gpe_arg, NULL, 0); |
1093 | |
1094 | /* |
1095 | * If no block arg, or block arg == 0 or 1, use the FADT-defined |
1096 | * GPE blocks. |
1097 | */ |
1098 | if (block_arg) { |
1099 | block_number = strtoul(block_arg, NULL, 0); |
1100 | if (block_number == 1) { |
1101 | block_number = 0; |
1102 | } |
1103 | } |
1104 | |
1105 | gpe_event_info = |
1106 | acpi_ev_get_gpe_event_info(ACPI_TO_POINTER(block_number), |
1107 | gpe_number); |
1108 | if (!gpe_event_info) { |
1109 | acpi_os_printf("Invalid GPE\n" ); |
1110 | return; |
1111 | } |
1112 | |
1113 | (void)acpi_ev_gpe_dispatch(NULL, gpe_event_info, gpe_number); |
1114 | } |
1115 | |
1116 | /******************************************************************************* |
1117 | * |
1118 | * FUNCTION: acpi_db_generate_sci |
1119 | * |
1120 | * PARAMETERS: None |
1121 | * |
1122 | * RETURN: None |
1123 | * |
1124 | * DESCRIPTION: Simulate an SCI -- just call the SCI dispatch. |
1125 | * |
1126 | ******************************************************************************/ |
1127 | |
1128 | void acpi_db_generate_sci(void) |
1129 | { |
1130 | acpi_ev_sci_dispatch(); |
1131 | } |
1132 | |
1133 | #endif /* !ACPI_REDUCED_HARDWARE */ |
1134 | |
1135 | /******************************************************************************* |
1136 | * |
1137 | * FUNCTION: acpi_db_trace |
1138 | * |
1139 | * PARAMETERS: enable_arg - ENABLE/AML to enable tracer |
1140 | * DISABLE to disable tracer |
1141 | * method_arg - Method to trace |
1142 | * once_arg - Whether trace once |
1143 | * |
1144 | * RETURN: None |
1145 | * |
1146 | * DESCRIPTION: Control method tracing facility |
1147 | * |
1148 | ******************************************************************************/ |
1149 | |
1150 | void acpi_db_trace(char *enable_arg, char *method_arg, char *once_arg) |
1151 | { |
1152 | u32 debug_level = 0; |
1153 | u32 debug_layer = 0; |
1154 | u32 flags = 0; |
1155 | |
1156 | acpi_ut_strupr(src_string: enable_arg); |
1157 | acpi_ut_strupr(src_string: once_arg); |
1158 | |
1159 | if (method_arg) { |
1160 | if (acpi_db_trace_method_name) { |
1161 | ACPI_FREE(acpi_db_trace_method_name); |
1162 | acpi_db_trace_method_name = NULL; |
1163 | } |
1164 | |
1165 | acpi_db_trace_method_name = |
1166 | ACPI_ALLOCATE(strlen(method_arg) + 1); |
1167 | if (!acpi_db_trace_method_name) { |
1168 | acpi_os_printf(format: "Failed to allocate method name (%s)\n" , |
1169 | method_arg); |
1170 | return; |
1171 | } |
1172 | |
1173 | strcpy(p: acpi_db_trace_method_name, q: method_arg); |
1174 | } |
1175 | |
1176 | if (!strcmp(enable_arg, "ENABLE" ) || |
1177 | !strcmp(enable_arg, "METHOD" ) || !strcmp(enable_arg, "OPCODE" )) { |
1178 | if (!strcmp(enable_arg, "ENABLE" )) { |
1179 | |
1180 | /* Inherit current console settings */ |
1181 | |
1182 | debug_level = acpi_gbl_db_console_debug_level; |
1183 | debug_layer = acpi_dbg_layer; |
1184 | } else { |
1185 | /* Restrict console output to trace points only */ |
1186 | |
1187 | debug_level = ACPI_LV_TRACE_POINT; |
1188 | debug_layer = ACPI_EXECUTER; |
1189 | } |
1190 | |
1191 | flags = ACPI_TRACE_ENABLED; |
1192 | |
1193 | if (!strcmp(enable_arg, "OPCODE" )) { |
1194 | flags |= ACPI_TRACE_OPCODE; |
1195 | } |
1196 | |
1197 | if (once_arg && !strcmp(once_arg, "ONCE" )) { |
1198 | flags |= ACPI_TRACE_ONESHOT; |
1199 | } |
1200 | } |
1201 | |
1202 | (void)acpi_debug_trace(name: acpi_db_trace_method_name, |
1203 | debug_level, debug_layer, flags); |
1204 | } |
1205 | |