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(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("**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("***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 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 413 acpi_gbl_db_buffer = acpi_os_allocate(ACPI_DEBUG_BUFFER_SIZE); 414 if (!acpi_gbl_db_buffer) { 415 return_ACPI_STATUS(AE_NO_MEMORY); 416 } 417 memset(acpi_gbl_db_buffer, 0, ACPI_DEBUG_BUFFER_SIZE); 418 419 /* Initial scope is the root */ 420 421 acpi_gbl_db_scope_buf[0] = AML_ROOT_PREFIX; 422 acpi_gbl_db_scope_buf[1] = 0; 423 acpi_gbl_db_scope_node = acpi_gbl_root_node; 424 425 /* Initialize user commands loop */ 426 427 acpi_gbl_db_terminate_loop = FALSE; 428 429 /* 430 * If configured for multi-thread support, the debug executor runs in 431 * a separate thread so that the front end can be in another address 432 * space, environment, or even another machine. 433 */ 434 if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) { 435 436 /* These were created with one unit, grab it */ 437 438 status = acpi_os_initialize_debugger(); 439 if (ACPI_FAILURE(status)) { 440 acpi_os_printf("Could not get debugger mutex\n"); 441 return_ACPI_STATUS(status); 442 } 443 444 /* Create the debug execution thread to execute commands */ 445 446 acpi_gbl_db_threads_terminated = FALSE; 447 status = acpi_os_execute(OSL_DEBUGGER_MAIN_THREAD, 448 acpi_db_execute_thread, NULL); 449 if (ACPI_FAILURE(status)) { 450 ACPI_EXCEPTION((AE_INFO, status, 451 "Could not start debugger thread")); 452 acpi_gbl_db_threads_terminated = TRUE; 453 return_ACPI_STATUS(status); 454 } 455 } else { 456 acpi_gbl_db_thread_id = acpi_os_get_thread_id(); 457 } 458 459 return_ACPI_STATUS(AE_OK); 460 } 461 462 ACPI_EXPORT_SYMBOL(acpi_initialize_debugger) 463 464 /******************************************************************************* 465 * 466 * FUNCTION: acpi_terminate_debugger 467 * 468 * PARAMETERS: None 469 * 470 * RETURN: None 471 * 472 * DESCRIPTION: Stop debugger 473 * 474 ******************************************************************************/ 475 void acpi_terminate_debugger(void) 476 { 477 478 /* Terminate the AML Debugger */ 479 480 acpi_gbl_db_terminate_loop = TRUE; 481 482 if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) { 483 484 /* Wait the AML Debugger threads */ 485 486 while (!acpi_gbl_db_threads_terminated) { 487 acpi_os_sleep(100); 488 } 489 490 acpi_os_terminate_debugger(); 491 } 492 493 if (acpi_gbl_db_buffer) { 494 acpi_os_free(acpi_gbl_db_buffer); 495 acpi_gbl_db_buffer = NULL; 496 } 497 498 /* Ensure that debug output is now disabled */ 499 500 acpi_gbl_db_output_flags = ACPI_DB_DISABLE_OUTPUT; 501 } 502 503 ACPI_EXPORT_SYMBOL(acpi_terminate_debugger) 504 505 /******************************************************************************* 506 * 507 * FUNCTION: acpi_set_debugger_thread_id 508 * 509 * PARAMETERS: thread_id - Debugger thread ID 510 * 511 * RETURN: None 512 * 513 * DESCRIPTION: Set debugger thread ID 514 * 515 ******************************************************************************/ 516 void acpi_set_debugger_thread_id(acpi_thread_id thread_id) 517 { 518 acpi_gbl_db_thread_id = thread_id; 519 } 520 521 ACPI_EXPORT_SYMBOL(acpi_set_debugger_thread_id) 522