xref: /linux/drivers/acpi/acpica/dbxface.c (revision ead5d1f4d877e92c051e1a1ade623d0d30e71619)
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
acpi_db_start_command(struct acpi_walk_state * walk_state,union acpi_parse_object * op)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(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 
acpi_db_signal_break_point(struct acpi_walk_state * walk_state)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("**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 
acpi_db_get_display_op(struct acpi_walk_state * walk_state,union acpi_parse_object * op)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
acpi_db_single_step(struct acpi_walk_state * walk_state,union acpi_parse_object * op,u32 opcode_class)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("***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("***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 			    ("\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("AML Opcode: %4.4X %s\n", op->common.aml_opcode,
306 			       acpi_ps_get_opcode_name(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 				    ("Predicate = [True], IF block was executed\n");
314 			} else {
315 				acpi_os_printf
316 				    ("Predicate = [False], Skipping IF block\n");
317 			}
318 		} else if (op->common.aml_opcode == AML_ELSE_OP) {
319 			acpi_os_printf
320 			    ("Predicate = [False], ELSE block was executed\n");
321 		}
322 
323 		/* Restore everything */
324 
325 		op->common.next = next;
326 		acpi_os_printf("\n");
327 		if ((acpi_gbl_db_output_to_file) ||
328 		    (acpi_dbg_level & ACPI_LV_PARSE)) {
329 			acpi_os_printf("\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 
acpi_initialize_debugger(void)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("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(OSL_DEBUGGER_MAIN_THREAD,
449 					 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 
ACPI_EXPORT_SYMBOL(acpi_initialize_debugger)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(100);
489 		}
490 
491 		acpi_os_terminate_debugger();
492 	}
493 
494 	if (acpi_gbl_db_buffer) {
495 		acpi_os_free(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 
ACPI_EXPORT_SYMBOL(acpi_terminate_debugger)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