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