linux/drivers/acpi/acpica/dbxface.c
<<
>>
Prefs
   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
  16ACPI_MODULE_NAME("dbxface")
  17
  18/* Local prototypes */
  19static acpi_status
  20acpi_db_start_command(struct acpi_walk_state *walk_state,
  21                      union acpi_parse_object *op);
  22
  23#ifdef ACPI_OBSOLETE_FUNCTIONS
  24void acpi_db_method_end(struct acpi_walk_state *walk_state);
  25#endif
  26
  27#ifdef ACPI_DISASSEMBLER
  28static 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
  47static acpi_status
  48acpi_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_point
  96 *
  97 * PARAMETERS:  walk_state      - Current walk
  98 *
  99 * RETURN:      Status
 100 *
 101 * DESCRIPTION: Called for AML_BREAKPOINT_OP
 102 *
 103 ******************************************************************************/
 104
 105void 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
 137static 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
 201acpi_status
 202acpi_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
 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 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
 463ACPI_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 ******************************************************************************/
 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_id
 509 *
 510 * PARAMETERS:  thread_id       - Debugger thread ID
 511 *
 512 * RETURN:      None
 513 *
 514 * DESCRIPTION: Set debugger thread ID
 515 *
 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