1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /******************************************************************************* |
3 | * |
4 | * Module Name: dbxface - AML Debugger external interfaces |
5 | * |
6 | ******************************************************************************/ |
7 | |
8 | #include <acpi/acpi.h> |
9 | #include "accommon.h" |
10 | #include "amlcode.h" |
11 | #include "acdebug.h" |
12 | #include "acinterp.h" |
13 | #include "acparser.h" |
14 | |
15 | #define _COMPONENT ACPI_CA_DEBUGGER |
16 | ACPI_MODULE_NAME("dbxface" ) |
17 | |
18 | /* Local prototypes */ |
19 | static acpi_status |
20 | acpi_db_start_command(struct acpi_walk_state *walk_state, |
21 | union acpi_parse_object *op); |
22 | |
23 | #ifdef ACPI_OBSOLETE_FUNCTIONS |
24 | void acpi_db_method_end(struct acpi_walk_state *walk_state); |
25 | #endif |
26 | |
27 | #ifdef ACPI_DISASSEMBLER |
28 | static union acpi_parse_object *acpi_db_get_display_op(struct acpi_walk_state |
29 | *walk_state, |
30 | union acpi_parse_object |
31 | *op); |
32 | #endif |
33 | |
34 | /******************************************************************************* |
35 | * |
36 | * FUNCTION: acpi_db_start_command |
37 | * |
38 | * PARAMETERS: walk_state - Current walk |
39 | * op - Current executing Op, from AML interpreter |
40 | * |
41 | * RETURN: Status |
42 | * |
43 | * DESCRIPTION: Enter debugger command loop |
44 | * |
45 | ******************************************************************************/ |
46 | |
47 | static acpi_status |
48 | acpi_db_start_command(struct acpi_walk_state *walk_state, |
49 | union acpi_parse_object *op) |
50 | { |
51 | acpi_status status; |
52 | |
53 | /* TBD: [Investigate] are there namespace locking issues here? */ |
54 | |
55 | /* acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); */ |
56 | |
57 | /* Go into the command loop and await next user command */ |
58 | |
59 | acpi_gbl_method_executing = TRUE; |
60 | status = AE_CTRL_TRUE; |
61 | |
62 | while (status == AE_CTRL_TRUE) { |
63 | |
64 | /* Notify the completion of the command */ |
65 | |
66 | status = acpi_os_notify_command_complete(); |
67 | if (ACPI_FAILURE(status)) { |
68 | goto error_exit; |
69 | } |
70 | |
71 | /* Wait the readiness of the command */ |
72 | |
73 | status = acpi_os_wait_command_ready(); |
74 | if (ACPI_FAILURE(status)) { |
75 | goto error_exit; |
76 | } |
77 | |
78 | status = |
79 | acpi_db_command_dispatch(input_buffer: acpi_gbl_db_line_buf, walk_state, |
80 | op); |
81 | } |
82 | |
83 | /* acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); */ |
84 | |
85 | error_exit: |
86 | if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) { |
87 | ACPI_EXCEPTION((AE_INFO, status, |
88 | "While parsing/handling command line" )); |
89 | } |
90 | return (status); |
91 | } |
92 | |
93 | /******************************************************************************* |
94 | * |
95 | * FUNCTION: acpi_db_signal_break_point |
96 | * |
97 | * PARAMETERS: walk_state - Current walk |
98 | * |
99 | * RETURN: Status |
100 | * |
101 | * DESCRIPTION: Called for AML_BREAKPOINT_OP |
102 | * |
103 | ******************************************************************************/ |
104 | |
105 | void acpi_db_signal_break_point(struct acpi_walk_state *walk_state) |
106 | { |
107 | |
108 | #ifndef ACPI_APPLICATION |
109 | if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) { |
110 | return; |
111 | } |
112 | #endif |
113 | |
114 | /* |
115 | * Set the single-step flag. This will cause the debugger (if present) |
116 | * to break to the console within the AML debugger at the start of the |
117 | * next AML instruction. |
118 | */ |
119 | acpi_gbl_cm_single_step = TRUE; |
120 | acpi_os_printf(format: "**break** Executed AML BreakPoint opcode\n" ); |
121 | } |
122 | |
123 | #ifdef ACPI_DISASSEMBLER |
124 | /******************************************************************************* |
125 | * |
126 | * FUNCTION: acpi_db_get_display_op |
127 | * |
128 | * PARAMETERS: walk_state - Current walk |
129 | * op - Current executing op (from aml interpreter) |
130 | * |
131 | * RETURN: Opcode to display |
132 | * |
133 | * DESCRIPTION: Find the opcode to display during single stepping |
134 | * |
135 | ******************************************************************************/ |
136 | |
137 | static union acpi_parse_object *acpi_db_get_display_op(struct acpi_walk_state |
138 | *walk_state, |
139 | union acpi_parse_object |
140 | *op) |
141 | { |
142 | union acpi_parse_object *display_op; |
143 | union acpi_parse_object *parent_op; |
144 | |
145 | display_op = op; |
146 | parent_op = op->common.parent; |
147 | if (parent_op) { |
148 | if ((walk_state->control_state) && |
149 | (walk_state->control_state->common.state == |
150 | ACPI_CONTROL_PREDICATE_EXECUTING)) { |
151 | /* |
152 | * We are executing the predicate of an IF or WHILE statement |
153 | * Search upwards for the containing IF or WHILE so that the |
154 | * entire predicate can be displayed. |
155 | */ |
156 | while (parent_op) { |
157 | if ((parent_op->common.aml_opcode == AML_IF_OP) |
158 | || (parent_op->common.aml_opcode == |
159 | AML_WHILE_OP)) { |
160 | display_op = parent_op; |
161 | break; |
162 | } |
163 | parent_op = parent_op->common.parent; |
164 | } |
165 | } else { |
166 | while (parent_op) { |
167 | if ((parent_op->common.aml_opcode == AML_IF_OP) |
168 | || (parent_op->common.aml_opcode == |
169 | AML_ELSE_OP) |
170 | || (parent_op->common.aml_opcode == |
171 | AML_SCOPE_OP) |
172 | || (parent_op->common.aml_opcode == |
173 | AML_METHOD_OP) |
174 | || (parent_op->common.aml_opcode == |
175 | AML_WHILE_OP)) { |
176 | break; |
177 | } |
178 | display_op = parent_op; |
179 | parent_op = parent_op->common.parent; |
180 | } |
181 | } |
182 | } |
183 | return display_op; |
184 | } |
185 | #endif |
186 | |
187 | /******************************************************************************* |
188 | * |
189 | * FUNCTION: acpi_db_single_step |
190 | * |
191 | * PARAMETERS: walk_state - Current walk |
192 | * op - Current executing op (from aml interpreter) |
193 | * opcode_class - Class of the current AML Opcode |
194 | * |
195 | * RETURN: Status |
196 | * |
197 | * DESCRIPTION: Called just before execution of an AML opcode. |
198 | * |
199 | ******************************************************************************/ |
200 | |
201 | acpi_status |
202 | acpi_db_single_step(struct acpi_walk_state *walk_state, |
203 | union acpi_parse_object *op, u32 opcode_class) |
204 | { |
205 | union acpi_parse_object *next; |
206 | acpi_status status = AE_OK; |
207 | u32 original_debug_level; |
208 | u32 aml_offset; |
209 | |
210 | ACPI_FUNCTION_ENTRY(); |
211 | |
212 | #ifndef ACPI_APPLICATION |
213 | if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) { |
214 | return (AE_OK); |
215 | } |
216 | #endif |
217 | |
218 | /* Check the abort flag */ |
219 | |
220 | if (acpi_gbl_abort_method) { |
221 | acpi_gbl_abort_method = FALSE; |
222 | return (AE_ABORT_METHOD); |
223 | } |
224 | |
225 | aml_offset = (u32)ACPI_PTR_DIFF(op->common.aml, |
226 | walk_state->parser_state.aml_start); |
227 | |
228 | /* Check for single-step breakpoint */ |
229 | |
230 | if (walk_state->method_breakpoint && |
231 | (walk_state->method_breakpoint <= aml_offset)) { |
232 | |
233 | /* Check if the breakpoint has been reached or passed */ |
234 | /* Hit the breakpoint, resume single step, reset breakpoint */ |
235 | |
236 | acpi_os_printf(format: "***Break*** at AML offset %X\n" , aml_offset); |
237 | acpi_gbl_cm_single_step = TRUE; |
238 | acpi_gbl_step_to_next_call = FALSE; |
239 | walk_state->method_breakpoint = 0; |
240 | } |
241 | |
242 | /* Check for user breakpoint (Must be on exact Aml offset) */ |
243 | |
244 | else if (walk_state->user_breakpoint && |
245 | (walk_state->user_breakpoint == aml_offset)) { |
246 | acpi_os_printf(format: "***UserBreakpoint*** at AML offset %X\n" , |
247 | aml_offset); |
248 | acpi_gbl_cm_single_step = TRUE; |
249 | acpi_gbl_step_to_next_call = FALSE; |
250 | walk_state->method_breakpoint = 0; |
251 | } |
252 | |
253 | /* |
254 | * Check if this is an opcode that we are interested in -- |
255 | * namely, opcodes that have arguments |
256 | */ |
257 | if (op->common.aml_opcode == AML_INT_NAMEDFIELD_OP) { |
258 | return (AE_OK); |
259 | } |
260 | |
261 | switch (opcode_class) { |
262 | case AML_CLASS_UNKNOWN: |
263 | case AML_CLASS_ARGUMENT: /* constants, literals, etc. do nothing */ |
264 | |
265 | return (AE_OK); |
266 | |
267 | default: |
268 | |
269 | /* All other opcodes -- continue */ |
270 | break; |
271 | } |
272 | |
273 | /* |
274 | * Under certain debug conditions, display this opcode and its operands |
275 | */ |
276 | if ((acpi_gbl_db_output_to_file) || |
277 | (acpi_gbl_cm_single_step) || (acpi_dbg_level & ACPI_LV_PARSE)) { |
278 | if ((acpi_gbl_db_output_to_file) || |
279 | (acpi_dbg_level & ACPI_LV_PARSE)) { |
280 | acpi_os_printf |
281 | (format: "\nAML Debug: Next AML Opcode to execute:\n" ); |
282 | } |
283 | |
284 | /* |
285 | * Display this op (and only this op - zero out the NEXT field |
286 | * temporarily, and disable parser trace output for the duration of |
287 | * the display because we don't want the extraneous debug output) |
288 | */ |
289 | original_debug_level = acpi_dbg_level; |
290 | acpi_dbg_level &= ~(ACPI_LV_PARSE | ACPI_LV_FUNCTIONS); |
291 | next = op->common.next; |
292 | op->common.next = NULL; |
293 | |
294 | /* Now we can disassemble and display it */ |
295 | |
296 | #ifdef ACPI_DISASSEMBLER |
297 | acpi_dm_disassemble(walk_state, |
298 | acpi_db_get_display_op(walk_state, op), |
299 | ACPI_UINT32_MAX); |
300 | #else |
301 | /* |
302 | * The AML Disassembler is not configured - at least we can |
303 | * display the opcode value and name |
304 | */ |
305 | acpi_os_printf(format: "AML Opcode: %4.4X %s\n" , op->common.aml_opcode, |
306 | acpi_ps_get_opcode_name(opcode: op->common.aml_opcode)); |
307 | #endif |
308 | |
309 | if ((op->common.aml_opcode == AML_IF_OP) || |
310 | (op->common.aml_opcode == AML_WHILE_OP)) { |
311 | if (walk_state->control_state->common.value) { |
312 | acpi_os_printf |
313 | (format: "Predicate = [True], IF block was executed\n" ); |
314 | } else { |
315 | acpi_os_printf |
316 | (format: "Predicate = [False], Skipping IF block\n" ); |
317 | } |
318 | } else if (op->common.aml_opcode == AML_ELSE_OP) { |
319 | acpi_os_printf |
320 | (format: "Predicate = [False], ELSE block was executed\n" ); |
321 | } |
322 | |
323 | /* Restore everything */ |
324 | |
325 | op->common.next = next; |
326 | acpi_os_printf(format: "\n" ); |
327 | if ((acpi_gbl_db_output_to_file) || |
328 | (acpi_dbg_level & ACPI_LV_PARSE)) { |
329 | acpi_os_printf(format: "\n" ); |
330 | } |
331 | acpi_dbg_level = original_debug_level; |
332 | } |
333 | |
334 | /* If we are not single stepping, just continue executing the method */ |
335 | |
336 | if (!acpi_gbl_cm_single_step) { |
337 | return (AE_OK); |
338 | } |
339 | |
340 | /* |
341 | * If we are executing a step-to-call command, |
342 | * Check if this is a method call. |
343 | */ |
344 | if (acpi_gbl_step_to_next_call) { |
345 | if (op->common.aml_opcode != AML_INT_METHODCALL_OP) { |
346 | |
347 | /* Not a method call, just keep executing */ |
348 | |
349 | return (AE_OK); |
350 | } |
351 | |
352 | /* Found a method call, stop executing */ |
353 | |
354 | acpi_gbl_step_to_next_call = FALSE; |
355 | } |
356 | |
357 | /* |
358 | * If the next opcode is a method call, we will "step over" it |
359 | * by default. |
360 | */ |
361 | if (op->common.aml_opcode == AML_INT_METHODCALL_OP) { |
362 | |
363 | /* Force no more single stepping while executing called method */ |
364 | |
365 | acpi_gbl_cm_single_step = FALSE; |
366 | |
367 | /* |
368 | * Set the breakpoint on/before the call, it will stop execution |
369 | * as soon as we return |
370 | */ |
371 | walk_state->method_breakpoint = 1; /* Must be non-zero! */ |
372 | } |
373 | |
374 | acpi_ex_exit_interpreter(); |
375 | status = acpi_db_start_command(walk_state, op); |
376 | acpi_ex_enter_interpreter(); |
377 | |
378 | /* User commands complete, continue execution of the interrupted method */ |
379 | |
380 | return (status); |
381 | } |
382 | |
383 | /******************************************************************************* |
384 | * |
385 | * FUNCTION: acpi_initialize_debugger |
386 | * |
387 | * PARAMETERS: None |
388 | * |
389 | * RETURN: Status |
390 | * |
391 | * DESCRIPTION: Init and start debugger |
392 | * |
393 | ******************************************************************************/ |
394 | |
395 | acpi_status acpi_initialize_debugger(void) |
396 | { |
397 | acpi_status status; |
398 | |
399 | ACPI_FUNCTION_TRACE(acpi_initialize_debugger); |
400 | |
401 | /* Init globals */ |
402 | |
403 | acpi_gbl_db_buffer = NULL; |
404 | acpi_gbl_db_filename = NULL; |
405 | acpi_gbl_db_output_to_file = FALSE; |
406 | |
407 | acpi_gbl_db_debug_level = ACPI_LV_VERBOSITY2; |
408 | acpi_gbl_db_console_debug_level = ACPI_NORMAL_DEFAULT | ACPI_LV_TABLES; |
409 | acpi_gbl_db_output_flags = ACPI_DB_CONSOLE_OUTPUT; |
410 | |
411 | acpi_gbl_db_opt_no_ini_methods = FALSE; |
412 | acpi_gbl_db_opt_no_region_support = FALSE; |
413 | |
414 | acpi_gbl_db_buffer = acpi_os_allocate(ACPI_DEBUG_BUFFER_SIZE); |
415 | if (!acpi_gbl_db_buffer) { |
416 | return_ACPI_STATUS(AE_NO_MEMORY); |
417 | } |
418 | memset(acpi_gbl_db_buffer, 0, ACPI_DEBUG_BUFFER_SIZE); |
419 | |
420 | /* Initial scope is the root */ |
421 | |
422 | acpi_gbl_db_scope_buf[0] = AML_ROOT_PREFIX; |
423 | acpi_gbl_db_scope_buf[1] = 0; |
424 | acpi_gbl_db_scope_node = acpi_gbl_root_node; |
425 | |
426 | /* Initialize user commands loop */ |
427 | |
428 | acpi_gbl_db_terminate_loop = FALSE; |
429 | |
430 | /* |
431 | * If configured for multi-thread support, the debug executor runs in |
432 | * a separate thread so that the front end can be in another address |
433 | * space, environment, or even another machine. |
434 | */ |
435 | if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) { |
436 | |
437 | /* These were created with one unit, grab it */ |
438 | |
439 | status = acpi_os_initialize_debugger(); |
440 | if (ACPI_FAILURE(status)) { |
441 | acpi_os_printf(format: "Could not get debugger mutex\n" ); |
442 | return_ACPI_STATUS(status); |
443 | } |
444 | |
445 | /* Create the debug execution thread to execute commands */ |
446 | |
447 | acpi_gbl_db_threads_terminated = FALSE; |
448 | status = acpi_os_execute(type: OSL_DEBUGGER_MAIN_THREAD, |
449 | function: acpi_db_execute_thread, NULL); |
450 | if (ACPI_FAILURE(status)) { |
451 | ACPI_EXCEPTION((AE_INFO, status, |
452 | "Could not start debugger thread" )); |
453 | acpi_gbl_db_threads_terminated = TRUE; |
454 | return_ACPI_STATUS(status); |
455 | } |
456 | } else { |
457 | acpi_gbl_db_thread_id = acpi_os_get_thread_id(); |
458 | } |
459 | |
460 | return_ACPI_STATUS(AE_OK); |
461 | } |
462 | |
463 | ACPI_EXPORT_SYMBOL(acpi_initialize_debugger) |
464 | |
465 | /******************************************************************************* |
466 | * |
467 | * FUNCTION: acpi_terminate_debugger |
468 | * |
469 | * PARAMETERS: None |
470 | * |
471 | * RETURN: None |
472 | * |
473 | * DESCRIPTION: Stop debugger |
474 | * |
475 | ******************************************************************************/ |
476 | void acpi_terminate_debugger(void) |
477 | { |
478 | |
479 | /* Terminate the AML Debugger */ |
480 | |
481 | acpi_gbl_db_terminate_loop = TRUE; |
482 | |
483 | if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) { |
484 | |
485 | /* Wait the AML Debugger threads */ |
486 | |
487 | while (!acpi_gbl_db_threads_terminated) { |
488 | acpi_os_sleep(milliseconds: 100); |
489 | } |
490 | |
491 | acpi_os_terminate_debugger(); |
492 | } |
493 | |
494 | if (acpi_gbl_db_buffer) { |
495 | acpi_os_free(memory: acpi_gbl_db_buffer); |
496 | acpi_gbl_db_buffer = NULL; |
497 | } |
498 | |
499 | /* Ensure that debug output is now disabled */ |
500 | |
501 | acpi_gbl_db_output_flags = ACPI_DB_DISABLE_OUTPUT; |
502 | } |
503 | |
504 | ACPI_EXPORT_SYMBOL(acpi_terminate_debugger) |
505 | |
506 | /******************************************************************************* |
507 | * |
508 | * FUNCTION: acpi_set_debugger_thread_id |
509 | * |
510 | * PARAMETERS: thread_id - Debugger thread ID |
511 | * |
512 | * RETURN: None |
513 | * |
514 | * DESCRIPTION: Set debugger thread ID |
515 | * |
516 | ******************************************************************************/ |
517 | void acpi_set_debugger_thread_id(acpi_thread_id thread_id) |
518 | { |
519 | acpi_gbl_db_thread_id = thread_id; |
520 | } |
521 | |
522 | ACPI_EXPORT_SYMBOL(acpi_set_debugger_thread_id) |
523 | |