523 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/*******************************************************************************3 *4 * Module Name: dbxface - AML Debugger external interfaces5 *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_DEBUGGER16ACPI_MODULE_NAME("dbxface")17 18/* Local prototypes */19static acpi_status20acpi_db_start_command(struct acpi_walk_state *walk_state,21 union acpi_parse_object *op);22 23#ifdef ACPI_OBSOLETE_FUNCTIONS24void acpi_db_method_end(struct acpi_walk_state *walk_state);25#endif26 27#ifdef ACPI_DISASSEMBLER28static union acpi_parse_object *acpi_db_get_display_op(struct acpi_walk_state29 *walk_state,30 union acpi_parse_object31 *op);32#endif33 34/*******************************************************************************35 *36 * FUNCTION: acpi_db_start_command37 *38 * PARAMETERS: walk_state - Current walk39 * op - Current executing Op, from AML interpreter40 *41 * RETURN: Status42 *43 * DESCRIPTION: Enter debugger command loop44 *45 ******************************************************************************/46 47static acpi_status48acpi_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 85error_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_point96 *97 * PARAMETERS: walk_state - Current walk98 *99 * RETURN: Status100 *101 * DESCRIPTION: Called for AML_BREAKPOINT_OP102 *103 ******************************************************************************/104 105void acpi_db_signal_break_point(struct acpi_walk_state *walk_state)106{107 108#ifndef ACPI_APPLICATION109 if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {110 return;111 }112#endif113 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 the117 * 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_DISASSEMBLER124/*******************************************************************************125 *126 * FUNCTION: acpi_db_get_display_op127 *128 * PARAMETERS: walk_state - Current walk129 * op - Current executing op (from aml interpreter)130 *131 * RETURN: Opcode to display132 *133 * DESCRIPTION: Find the opcode to display during single stepping134 *135 ******************************************************************************/136 137static union acpi_parse_object *acpi_db_get_display_op(struct acpi_walk_state138 *walk_state,139 union acpi_parse_object140 *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 statement153 * Search upwards for the containing IF or WHILE so that the154 * 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#endif186 187/*******************************************************************************188 *189 * FUNCTION: acpi_db_single_step190 *191 * PARAMETERS: walk_state - Current walk192 * op - Current executing op (from aml interpreter)193 * opcode_class - Class of the current AML Opcode194 *195 * RETURN: Status196 *197 * DESCRIPTION: Called just before execution of an AML opcode.198 *199 ******************************************************************************/200 201acpi_status202acpi_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_APPLICATION213 if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {214 return (AE_OK);215 }216#endif217 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 arguments256 */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 operands275 */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_printf281 ("\nAML Debug: Next AML Opcode to execute:\n");282 }283 284 /*285 * Display this op (and only this op - zero out the NEXT field286 * temporarily, and disable parser trace output for the duration of287 * 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_DISASSEMBLER297 acpi_dm_disassemble(walk_state,298 acpi_db_get_display_op(walk_state, op),299 ACPI_UINT32_MAX);300#else301 /*302 * The AML Disassembler is not configured - at least we can303 * display the opcode value and name304 */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#endif308 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_printf313 ("Predicate = [True], IF block was executed\n");314 } else {315 acpi_os_printf316 ("Predicate = [False], Skipping IF block\n");317 }318 } else if (op->common.aml_opcode == AML_ELSE_OP) {319 acpi_os_printf320 ("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" it359 * 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 execution369 * as soon as we return370 */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_debugger386 *387 * PARAMETERS: None388 *389 * RETURN: Status390 *391 * DESCRIPTION: Init and start debugger392 *393 ******************************************************************************/394 395acpi_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 in432 * a separate thread so that the front end can be in another address433 * 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 463ACPI_EXPORT_SYMBOL(acpi_initialize_debugger)464 465/*******************************************************************************466 *467 * FUNCTION: acpi_terminate_debugger468 *469 * PARAMETERS: None470 *471 * RETURN: None472 *473 * DESCRIPTION: Stop debugger474 *475 ******************************************************************************/476void 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 504ACPI_EXPORT_SYMBOL(acpi_terminate_debugger)505 506/*******************************************************************************507 *508 * FUNCTION: acpi_set_debugger_thread_id509 *510 * PARAMETERS: thread_id - Debugger thread ID511 *512 * RETURN: None513 *514 * DESCRIPTION: Set debugger thread ID515 *516 ******************************************************************************/517void acpi_set_debugger_thread_id(acpi_thread_id thread_id)518{519 acpi_gbl_db_thread_id = thread_id;520}521 522ACPI_EXPORT_SYMBOL(acpi_set_debugger_thread_id)523