xref: /openbmc/linux/drivers/acpi/acpica/dbxface.c (revision f220d3eb)
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 /*******************************************************************************
28  *
29  * FUNCTION:    acpi_db_start_command
30  *
31  * PARAMETERS:  walk_state      - Current walk
32  *              op              - Current executing Op, from AML interpreter
33  *
34  * RETURN:      Status
35  *
36  * DESCRIPTION: Enter debugger command loop
37  *
38  ******************************************************************************/
39 
40 static acpi_status
41 acpi_db_start_command(struct acpi_walk_state *walk_state,
42 		      union acpi_parse_object *op)
43 {
44 	acpi_status status;
45 
46 	/* TBD: [Investigate] are there namespace locking issues here? */
47 
48 	/* acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); */
49 
50 	/* Go into the command loop and await next user command */
51 
52 	acpi_gbl_method_executing = TRUE;
53 	status = AE_CTRL_TRUE;
54 
55 	while (status == AE_CTRL_TRUE) {
56 
57 		/* Notify the completion of the command */
58 
59 		status = acpi_os_notify_command_complete();
60 		if (ACPI_FAILURE(status)) {
61 			goto error_exit;
62 		}
63 
64 		/* Wait the readiness of the command */
65 
66 		status = acpi_os_wait_command_ready();
67 		if (ACPI_FAILURE(status)) {
68 			goto error_exit;
69 		}
70 
71 		status =
72 		    acpi_db_command_dispatch(acpi_gbl_db_line_buf, walk_state,
73 					     op);
74 	}
75 
76 	/* acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); */
77 
78 error_exit:
79 	if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
80 		ACPI_EXCEPTION((AE_INFO, status,
81 				"While parsing/handling command line"));
82 	}
83 	return (status);
84 }
85 
86 /*******************************************************************************
87  *
88  * FUNCTION:    acpi_db_signal_break_point
89  *
90  * PARAMETERS:  walk_state      - Current walk
91  *
92  * RETURN:      Status
93  *
94  * DESCRIPTION: Called for AML_BREAKPOINT_OP
95  *
96  ******************************************************************************/
97 
98 void acpi_db_signal_break_point(struct acpi_walk_state *walk_state)
99 {
100 
101 #ifndef ACPI_APPLICATION
102 	if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
103 		return;
104 	}
105 #endif
106 
107 	/*
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.
111 	 */
112 	acpi_gbl_cm_single_step = TRUE;
113 	acpi_os_printf("**break** Executed AML BreakPoint opcode\n");
114 }
115 
116 /*******************************************************************************
117  *
118  * FUNCTION:    acpi_db_single_step
119  *
120  * PARAMETERS:  walk_state      - Current walk
121  *              op              - Current executing op (from aml interpreter)
122  *              opcode_class    - Class of the current AML Opcode
123  *
124  * RETURN:      Status
125  *
126  * DESCRIPTION: Called just before execution of an AML opcode.
127  *
128  ******************************************************************************/
129 
130 acpi_status
131 acpi_db_single_step(struct acpi_walk_state *walk_state,
132 		    union acpi_parse_object *op, u32 opcode_class)
133 {
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;
139 	u32 aml_offset;
140 
141 	ACPI_FUNCTION_ENTRY();
142 
143 #ifndef ACPI_APPLICATION
144 	if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
145 		return (AE_OK);
146 	}
147 #endif
148 
149 	/* Check the abort flag */
150 
151 	if (acpi_gbl_abort_method) {
152 		acpi_gbl_abort_method = FALSE;
153 		return (AE_ABORT_METHOD);
154 	}
155 
156 	aml_offset = (u32)ACPI_PTR_DIFF(op->common.aml,
157 					walk_state->parser_state.aml_start);
158 
159 	/* Check for single-step breakpoint */
160 
161 	if (walk_state->method_breakpoint &&
162 	    (walk_state->method_breakpoint <= aml_offset)) {
163 
164 		/* Check if the breakpoint has been reached or passed */
165 		/* Hit the breakpoint, resume single step, reset breakpoint */
166 
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;
171 	}
172 
173 	/* Check for user breakpoint (Must be on exact Aml offset) */
174 
175 	else if (walk_state->user_breakpoint &&
176 		 (walk_state->user_breakpoint == aml_offset)) {
177 		acpi_os_printf("***UserBreakpoint*** at AML offset %X\n",
178 			       aml_offset);
179 		acpi_gbl_cm_single_step = TRUE;
180 		acpi_gbl_step_to_next_call = FALSE;
181 		walk_state->method_breakpoint = 0;
182 	}
183 
184 	/*
185 	 * Check if this is an opcode that we are interested in --
186 	 * namely, opcodes that have arguments
187 	 */
188 	if (op->common.aml_opcode == AML_INT_NAMEDFIELD_OP) {
189 		return (AE_OK);
190 	}
191 
192 	switch (opcode_class) {
193 	case AML_CLASS_UNKNOWN:
194 	case AML_CLASS_ARGUMENT:	/* constants, literals, etc. do nothing */
195 
196 		return (AE_OK);
197 
198 	default:
199 
200 		/* All other opcodes -- continue */
201 		break;
202 	}
203 
204 	/*
205 	 * Under certain debug conditions, display this opcode and its operands
206 	 */
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)) {
211 			acpi_os_printf
212 			    ("\nAML Debug: Next AML Opcode to execute:\n");
213 		}
214 
215 		/*
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)
219 		 */
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;
224 
225 		display_op = op;
226 		parent_op = op->common.parent;
227 		if (parent_op) {
228 			if ((walk_state->control_state) &&
229 			    (walk_state->control_state->common.state ==
230 			     ACPI_CONTROL_PREDICATE_EXECUTING)) {
231 				/*
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.
235 				 */
236 				while (parent_op) {
237 					if ((parent_op->common.aml_opcode ==
238 					     AML_IF_OP)
239 					    || (parent_op->common.aml_opcode ==
240 						AML_WHILE_OP)) {
241 						display_op = parent_op;
242 						break;
243 					}
244 					parent_op = parent_op->common.parent;
245 				}
246 			} else {
247 				while (parent_op) {
248 					if ((parent_op->common.aml_opcode ==
249 					     AML_IF_OP)
250 					    || (parent_op->common.aml_opcode ==
251 						AML_ELSE_OP)
252 					    || (parent_op->common.aml_opcode ==
253 						AML_SCOPE_OP)
254 					    || (parent_op->common.aml_opcode ==
255 						AML_METHOD_OP)
256 					    || (parent_op->common.aml_opcode ==
257 						AML_WHILE_OP)) {
258 						break;
259 					}
260 					display_op = parent_op;
261 					parent_op = parent_op->common.parent;
262 				}
263 			}
264 		}
265 
266 		/* Now we can disassemble and display it */
267 
268 #ifdef ACPI_DISASSEMBLER
269 		acpi_dm_disassemble(walk_state, display_op, ACPI_UINT32_MAX);
270 #else
271 		/*
272 		 * The AML Disassembler is not configured - at least we can
273 		 * display the opcode value and name
274 		 */
275 		acpi_os_printf("AML Opcode: %4.4X %s\n", op->common.aml_opcode,
276 			       acpi_ps_get_opcode_name(op->common.aml_opcode));
277 #endif
278 
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) {
282 				acpi_os_printf
283 				    ("Predicate = [True], IF block was executed\n");
284 			} else {
285 				acpi_os_printf
286 				    ("Predicate = [False], Skipping IF block\n");
287 			}
288 		} else if (op->common.aml_opcode == AML_ELSE_OP) {
289 			acpi_os_printf
290 			    ("Predicate = [False], ELSE block was executed\n");
291 		}
292 
293 		/* Restore everything */
294 
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");
300 		}
301 		acpi_dbg_level = original_debug_level;
302 	}
303 
304 	/* If we are not single stepping, just continue executing the method */
305 
306 	if (!acpi_gbl_cm_single_step) {
307 		return (AE_OK);
308 	}
309 
310 	/*
311 	 * If we are executing a step-to-call command,
312 	 * Check if this is a method call.
313 	 */
314 	if (acpi_gbl_step_to_next_call) {
315 		if (op->common.aml_opcode != AML_INT_METHODCALL_OP) {
316 
317 			/* Not a method call, just keep executing */
318 
319 			return (AE_OK);
320 		}
321 
322 		/* Found a method call, stop executing */
323 
324 		acpi_gbl_step_to_next_call = FALSE;
325 	}
326 
327 	/*
328 	 * If the next opcode is a method call, we will "step over" it
329 	 * by default.
330 	 */
331 	if (op->common.aml_opcode == AML_INT_METHODCALL_OP) {
332 
333 		/* Force no more single stepping while executing called method */
334 
335 		acpi_gbl_cm_single_step = FALSE;
336 
337 		/*
338 		 * Set the breakpoint on/before the call, it will stop execution
339 		 * as soon as we return
340 		 */
341 		walk_state->method_breakpoint = 1;	/* Must be non-zero! */
342 	}
343 
344 	acpi_ex_exit_interpreter();
345 	status = acpi_db_start_command(walk_state, op);
346 	acpi_ex_enter_interpreter();
347 
348 	/* User commands complete, continue execution of the interrupted method */
349 
350 	return (status);
351 }
352 
353 /*******************************************************************************
354  *
355  * FUNCTION:    acpi_initialize_debugger
356  *
357  * PARAMETERS:  None
358  *
359  * RETURN:      Status
360  *
361  * DESCRIPTION: Init and start debugger
362  *
363  ******************************************************************************/
364 
365 acpi_status acpi_initialize_debugger(void)
366 {
367 	acpi_status status;
368 
369 	ACPI_FUNCTION_TRACE(acpi_initialize_debugger);
370 
371 	/* Init globals */
372 
373 	acpi_gbl_db_buffer = NULL;
374 	acpi_gbl_db_filename = NULL;
375 	acpi_gbl_db_output_to_file = FALSE;
376 
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;
380 
381 	acpi_gbl_db_opt_no_ini_methods = FALSE;
382 
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);
386 	}
387 	memset(acpi_gbl_db_buffer, 0, ACPI_DEBUG_BUFFER_SIZE);
388 
389 	/* Initial scope is the root */
390 
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;
394 
395 	/* Initialize user commands loop */
396 
397 	acpi_gbl_db_terminate_loop = FALSE;
398 
399 	/*
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.
403 	 */
404 	if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) {
405 
406 		/* These were created with one unit, grab it */
407 
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);
412 		}
413 
414 		/* Create the debug execution thread to execute commands */
415 
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);
424 		}
425 	} else {
426 		acpi_gbl_db_thread_id = acpi_os_get_thread_id();
427 	}
428 
429 	return_ACPI_STATUS(AE_OK);
430 }
431 
432 ACPI_EXPORT_SYMBOL(acpi_initialize_debugger)
433 
434 /*******************************************************************************
435  *
436  * FUNCTION:    acpi_terminate_debugger
437  *
438  * PARAMETERS:  None
439  *
440  * RETURN:      None
441  *
442  * DESCRIPTION: Stop debugger
443  *
444  ******************************************************************************/
445 void acpi_terminate_debugger(void)
446 {
447 
448 	/* Terminate the AML Debugger */
449 
450 	acpi_gbl_db_terminate_loop = TRUE;
451 
452 	if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) {
453 
454 		/* Wait the AML Debugger threads */
455 
456 		while (!acpi_gbl_db_threads_terminated) {
457 			acpi_os_sleep(100);
458 		}
459 
460 		acpi_os_terminate_debugger();
461 	}
462 
463 	if (acpi_gbl_db_buffer) {
464 		acpi_os_free(acpi_gbl_db_buffer);
465 		acpi_gbl_db_buffer = NULL;
466 	}
467 
468 	/* Ensure that debug output is now disabled */
469 
470 	acpi_gbl_db_output_flags = ACPI_DB_DISABLE_OUTPUT;
471 }
472 
473 ACPI_EXPORT_SYMBOL(acpi_terminate_debugger)
474 
475 /*******************************************************************************
476  *
477  * FUNCTION:    acpi_set_debugger_thread_id
478  *
479  * PARAMETERS:  thread_id       - Debugger thread ID
480  *
481  * RETURN:      None
482  *
483  * DESCRIPTION: Set debugger thread ID
484  *
485  ******************************************************************************/
486 void acpi_set_debugger_thread_id(acpi_thread_id thread_id)
487 {
488 	acpi_gbl_db_thread_id = thread_id;
489 }
490 
491 ACPI_EXPORT_SYMBOL(acpi_set_debugger_thread_id)
492