1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /*******************************************************************************
4 * Module Name: dbxface - AML Debugger external interfaces
6 ******************************************************************************/
15 #define _COMPONENT ACPI_CA_DEBUGGER
16 ACPI_MODULE_NAME("dbxface")
18 /* Local prototypes */
20 acpi_db_start_command(struct acpi_walk_state
*walk_state
,
21 union acpi_parse_object
*op
);
23 #ifdef ACPI_OBSOLETE_FUNCTIONS
24 void acpi_db_method_end(struct acpi_walk_state
*walk_state
);
27 /*******************************************************************************
29 * FUNCTION: acpi_db_start_command
31 * PARAMETERS: walk_state - Current walk
32 * op - Current executing Op, from AML interpreter
36 * DESCRIPTION: Enter debugger command loop
38 ******************************************************************************/
41 acpi_db_start_command(struct acpi_walk_state
*walk_state
,
42 union acpi_parse_object
*op
)
46 /* TBD: [Investigate] are there namespace locking issues here? */
48 /* acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); */
50 /* Go into the command loop and await next user command */
52 acpi_gbl_method_executing
= TRUE
;
53 status
= AE_CTRL_TRUE
;
55 while (status
== AE_CTRL_TRUE
) {
57 /* Notify the completion of the command */
59 status
= acpi_os_notify_command_complete();
60 if (ACPI_FAILURE(status
)) {
64 /* Wait the readiness of the command */
66 status
= acpi_os_wait_command_ready();
67 if (ACPI_FAILURE(status
)) {
72 acpi_db_command_dispatch(acpi_gbl_db_line_buf
, walk_state
,
76 /* acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); */
79 if (ACPI_FAILURE(status
) && status
!= AE_CTRL_TERMINATE
) {
80 ACPI_EXCEPTION((AE_INFO
, status
,
81 "While parsing/handling command line"));
86 /*******************************************************************************
88 * FUNCTION: acpi_db_signal_break_point
90 * PARAMETERS: walk_state - Current walk
94 * DESCRIPTION: Called for AML_BREAKPOINT_OP
96 ******************************************************************************/
98 void acpi_db_signal_break_point(struct acpi_walk_state
*walk_state
)
101 #ifndef ACPI_APPLICATION
102 if (acpi_gbl_db_thread_id
!= acpi_os_get_thread_id()) {
108 * Set the single-step flag. This will cause the debugger (if present)
109 * to break to the console within the AML debugger at the start of the
110 * next AML instruction.
112 acpi_gbl_cm_single_step
= TRUE
;
113 acpi_os_printf("**break** Executed AML BreakPoint opcode\n");
116 /*******************************************************************************
118 * FUNCTION: acpi_db_single_step
120 * PARAMETERS: walk_state - Current walk
121 * op - Current executing op (from aml interpreter)
122 * opcode_class - Class of the current AML Opcode
126 * DESCRIPTION: Called just before execution of an AML opcode.
128 ******************************************************************************/
131 acpi_db_single_step(struct acpi_walk_state
*walk_state
,
132 union acpi_parse_object
*op
, u32 opcode_class
)
134 union acpi_parse_object
*next
;
135 acpi_status status
= AE_OK
;
136 u32 original_debug_level
;
137 union acpi_parse_object
*display_op
;
138 union acpi_parse_object
*parent_op
;
141 ACPI_FUNCTION_ENTRY();
143 #ifndef ACPI_APPLICATION
144 if (acpi_gbl_db_thread_id
!= acpi_os_get_thread_id()) {
149 /* Check the abort flag */
151 if (acpi_gbl_abort_method
) {
152 acpi_gbl_abort_method
= FALSE
;
153 return (AE_ABORT_METHOD
);
156 aml_offset
= (u32
)ACPI_PTR_DIFF(op
->common
.aml
,
157 walk_state
->parser_state
.aml_start
);
159 /* Check for single-step breakpoint */
161 if (walk_state
->method_breakpoint
&&
162 (walk_state
->method_breakpoint
<= aml_offset
)) {
164 /* Check if the breakpoint has been reached or passed */
165 /* Hit the breakpoint, resume single step, reset breakpoint */
167 acpi_os_printf("***Break*** at AML offset %X\n", aml_offset
);
168 acpi_gbl_cm_single_step
= TRUE
;
169 acpi_gbl_step_to_next_call
= FALSE
;
170 walk_state
->method_breakpoint
= 0;
173 /* Check for user breakpoint (Must be on exact Aml offset) */
175 else if (walk_state
->user_breakpoint
&&
176 (walk_state
->user_breakpoint
== aml_offset
)) {
177 acpi_os_printf("***UserBreakpoint*** at AML offset %X\n",
179 acpi_gbl_cm_single_step
= TRUE
;
180 acpi_gbl_step_to_next_call
= FALSE
;
181 walk_state
->method_breakpoint
= 0;
185 * Check if this is an opcode that we are interested in --
186 * namely, opcodes that have arguments
188 if (op
->common
.aml_opcode
== AML_INT_NAMEDFIELD_OP
) {
192 switch (opcode_class
) {
193 case AML_CLASS_UNKNOWN
:
194 case AML_CLASS_ARGUMENT
: /* constants, literals, etc. do nothing */
200 /* All other opcodes -- continue */
205 * Under certain debug conditions, display this opcode and its operands
207 if ((acpi_gbl_db_output_to_file
) ||
208 (acpi_gbl_cm_single_step
) || (acpi_dbg_level
& ACPI_LV_PARSE
)) {
209 if ((acpi_gbl_db_output_to_file
) ||
210 (acpi_dbg_level
& ACPI_LV_PARSE
)) {
212 ("\nAML Debug: Next AML Opcode to execute:\n");
216 * Display this op (and only this op - zero out the NEXT field
217 * temporarily, and disable parser trace output for the duration of
218 * the display because we don't want the extraneous debug output)
220 original_debug_level
= acpi_dbg_level
;
221 acpi_dbg_level
&= ~(ACPI_LV_PARSE
| ACPI_LV_FUNCTIONS
);
222 next
= op
->common
.next
;
223 op
->common
.next
= NULL
;
226 parent_op
= op
->common
.parent
;
228 if ((walk_state
->control_state
) &&
229 (walk_state
->control_state
->common
.state
==
230 ACPI_CONTROL_PREDICATE_EXECUTING
)) {
232 * We are executing the predicate of an IF or WHILE statement
233 * Search upwards for the containing IF or WHILE so that the
234 * entire predicate can be displayed.
237 if ((parent_op
->common
.aml_opcode
==
239 || (parent_op
->common
.aml_opcode
==
241 display_op
= parent_op
;
244 parent_op
= parent_op
->common
.parent
;
248 if ((parent_op
->common
.aml_opcode
==
250 || (parent_op
->common
.aml_opcode
==
252 || (parent_op
->common
.aml_opcode
==
254 || (parent_op
->common
.aml_opcode
==
256 || (parent_op
->common
.aml_opcode
==
260 display_op
= parent_op
;
261 parent_op
= parent_op
->common
.parent
;
266 /* Now we can disassemble and display it */
268 #ifdef ACPI_DISASSEMBLER
269 acpi_dm_disassemble(walk_state
, display_op
, ACPI_UINT32_MAX
);
272 * The AML Disassembler is not configured - at least we can
273 * display the opcode value and name
275 acpi_os_printf("AML Opcode: %4.4X %s\n", op
->common
.aml_opcode
,
276 acpi_ps_get_opcode_name(op
->common
.aml_opcode
));
279 if ((op
->common
.aml_opcode
== AML_IF_OP
) ||
280 (op
->common
.aml_opcode
== AML_WHILE_OP
)) {
281 if (walk_state
->control_state
->common
.value
) {
283 ("Predicate = [True], IF block was executed\n");
286 ("Predicate = [False], Skipping IF block\n");
288 } else if (op
->common
.aml_opcode
== AML_ELSE_OP
) {
290 ("Predicate = [False], ELSE block was executed\n");
293 /* Restore everything */
295 op
->common
.next
= next
;
296 acpi_os_printf("\n");
297 if ((acpi_gbl_db_output_to_file
) ||
298 (acpi_dbg_level
& ACPI_LV_PARSE
)) {
299 acpi_os_printf("\n");
301 acpi_dbg_level
= original_debug_level
;
304 /* If we are not single stepping, just continue executing the method */
306 if (!acpi_gbl_cm_single_step
) {
311 * If we are executing a step-to-call command,
312 * Check if this is a method call.
314 if (acpi_gbl_step_to_next_call
) {
315 if (op
->common
.aml_opcode
!= AML_INT_METHODCALL_OP
) {
317 /* Not a method call, just keep executing */
322 /* Found a method call, stop executing */
324 acpi_gbl_step_to_next_call
= FALSE
;
328 * If the next opcode is a method call, we will "step over" it
331 if (op
->common
.aml_opcode
== AML_INT_METHODCALL_OP
) {
333 /* Force no more single stepping while executing called method */
335 acpi_gbl_cm_single_step
= FALSE
;
338 * Set the breakpoint on/before the call, it will stop execution
339 * as soon as we return
341 walk_state
->method_breakpoint
= 1; /* Must be non-zero! */
344 acpi_ex_exit_interpreter();
345 status
= acpi_db_start_command(walk_state
, op
);
346 acpi_ex_enter_interpreter();
348 /* User commands complete, continue execution of the interrupted method */
353 /*******************************************************************************
355 * FUNCTION: acpi_initialize_debugger
361 * DESCRIPTION: Init and start debugger
363 ******************************************************************************/
365 acpi_status
acpi_initialize_debugger(void)
369 ACPI_FUNCTION_TRACE(acpi_initialize_debugger
);
373 acpi_gbl_db_buffer
= NULL
;
374 acpi_gbl_db_filename
= NULL
;
375 acpi_gbl_db_output_to_file
= FALSE
;
377 acpi_gbl_db_debug_level
= ACPI_LV_VERBOSITY2
;
378 acpi_gbl_db_console_debug_level
= ACPI_NORMAL_DEFAULT
| ACPI_LV_TABLES
;
379 acpi_gbl_db_output_flags
= ACPI_DB_CONSOLE_OUTPUT
;
381 acpi_gbl_db_opt_no_ini_methods
= FALSE
;
383 acpi_gbl_db_buffer
= acpi_os_allocate(ACPI_DEBUG_BUFFER_SIZE
);
384 if (!acpi_gbl_db_buffer
) {
385 return_ACPI_STATUS(AE_NO_MEMORY
);
387 memset(acpi_gbl_db_buffer
, 0, ACPI_DEBUG_BUFFER_SIZE
);
389 /* Initial scope is the root */
391 acpi_gbl_db_scope_buf
[0] = AML_ROOT_PREFIX
;
392 acpi_gbl_db_scope_buf
[1] = 0;
393 acpi_gbl_db_scope_node
= acpi_gbl_root_node
;
395 /* Initialize user commands loop */
397 acpi_gbl_db_terminate_loop
= FALSE
;
400 * If configured for multi-thread support, the debug executor runs in
401 * a separate thread so that the front end can be in another address
402 * space, environment, or even another machine.
404 if (acpi_gbl_debugger_configuration
& DEBUGGER_MULTI_THREADED
) {
406 /* These were created with one unit, grab it */
408 status
= acpi_os_initialize_debugger();
409 if (ACPI_FAILURE(status
)) {
410 acpi_os_printf("Could not get debugger mutex\n");
411 return_ACPI_STATUS(status
);
414 /* Create the debug execution thread to execute commands */
416 acpi_gbl_db_threads_terminated
= FALSE
;
417 status
= acpi_os_execute(OSL_DEBUGGER_MAIN_THREAD
,
418 acpi_db_execute_thread
, NULL
);
419 if (ACPI_FAILURE(status
)) {
420 ACPI_EXCEPTION((AE_INFO
, status
,
421 "Could not start debugger thread"));
422 acpi_gbl_db_threads_terminated
= TRUE
;
423 return_ACPI_STATUS(status
);
426 acpi_gbl_db_thread_id
= acpi_os_get_thread_id();
429 return_ACPI_STATUS(AE_OK
);
432 ACPI_EXPORT_SYMBOL(acpi_initialize_debugger
)
434 /*******************************************************************************
436 * FUNCTION: acpi_terminate_debugger
442 * DESCRIPTION: Stop debugger
444 ******************************************************************************/
445 void acpi_terminate_debugger(void)
448 /* Terminate the AML Debugger */
450 acpi_gbl_db_terminate_loop
= TRUE
;
452 if (acpi_gbl_debugger_configuration
& DEBUGGER_MULTI_THREADED
) {
454 /* Wait the AML Debugger threads */
456 while (!acpi_gbl_db_threads_terminated
) {
460 acpi_os_terminate_debugger();
463 if (acpi_gbl_db_buffer
) {
464 acpi_os_free(acpi_gbl_db_buffer
);
465 acpi_gbl_db_buffer
= NULL
;
468 /* Ensure that debug output is now disabled */
470 acpi_gbl_db_output_flags
= ACPI_DB_DISABLE_OUTPUT
;
473 ACPI_EXPORT_SYMBOL(acpi_terminate_debugger
)
475 /*******************************************************************************
477 * FUNCTION: acpi_set_debugger_thread_id
479 * PARAMETERS: thread_id - Debugger thread ID
483 * DESCRIPTION: Set debugger thread ID
485 ******************************************************************************/
486 void acpi_set_debugger_thread_id(acpi_thread_id thread_id
)
488 acpi_gbl_db_thread_id
= thread_id
;
491 ACPI_EXPORT_SYMBOL(acpi_set_debugger_thread_id
)