// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.01/*******************************************************************************2*3* Module Name: dbxface - AML Debugger external interfaces4*5******************************************************************************/67#include <acpi/acpi.h>8#include "accommon.h"9#include "amlcode.h"10#include "acdebug.h"11#include "acinterp.h"12#include "acparser.h"1314#define _COMPONENT ACPI_CA_DEBUGGER15ACPI_MODULE_NAME("dbxface")1617/* Local prototypes */18static acpi_status19acpi_db_start_command(struct acpi_walk_state *walk_state,20union acpi_parse_object *op);2122#ifdef ACPI_OBSOLETE_FUNCTIONS23void acpi_db_method_end(struct acpi_walk_state *walk_state);24#endif2526#ifdef ACPI_DISASSEMBLER27static union acpi_parse_object *acpi_db_get_display_op(struct acpi_walk_state28*walk_state,29union acpi_parse_object30*op);31#endif3233/*******************************************************************************34*35* FUNCTION: acpi_db_start_command36*37* PARAMETERS: walk_state - Current walk38* op - Current executing Op, from AML interpreter39*40* RETURN: Status41*42* DESCRIPTION: Enter debugger command loop43*44******************************************************************************/4546static acpi_status47acpi_db_start_command(struct acpi_walk_state *walk_state,48union acpi_parse_object *op)49{50acpi_status status;5152/* TBD: [Investigate] are there namespace locking issues here? */5354/* acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); */5556/* Go into the command loop and await next user command */5758acpi_gbl_method_executing = TRUE;59status = AE_CTRL_TRUE;6061while (status == AE_CTRL_TRUE) {6263/* Notify the completion of the command */6465status = acpi_os_notify_command_complete();66if (ACPI_FAILURE(status)) {67goto error_exit;68}6970/* Wait the readiness of the command */7172status = acpi_os_wait_command_ready();73if (ACPI_FAILURE(status)) {74goto error_exit;75}7677status =78acpi_db_command_dispatch(acpi_gbl_db_line_buf, walk_state,79op);80}8182/* acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); */8384error_exit:85if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {86ACPI_EXCEPTION((AE_INFO, status,87"While parsing/handling command line"));88}89return (status);90}9192/*******************************************************************************93*94* FUNCTION: acpi_db_signal_break_point95*96* PARAMETERS: walk_state - Current walk97*98* RETURN: Status99*100* DESCRIPTION: Called for AML_BREAKPOINT_OP101*102******************************************************************************/103104void acpi_db_signal_break_point(struct acpi_walk_state *walk_state)105{106107#ifndef ACPI_APPLICATION108if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {109return;110}111#endif112113/*114* Set the single-step flag. This will cause the debugger (if present)115* to break to the console within the AML debugger at the start of the116* next AML instruction.117*/118acpi_gbl_cm_single_step = TRUE;119acpi_os_printf("**break** Executed AML BreakPoint opcode\n");120}121122#ifdef ACPI_DISASSEMBLER123/*******************************************************************************124*125* FUNCTION: acpi_db_get_display_op126*127* PARAMETERS: walk_state - Current walk128* op - Current executing op (from aml interpreter)129*130* RETURN: Opcode to display131*132* DESCRIPTION: Find the opcode to display during single stepping133*134******************************************************************************/135136static union acpi_parse_object *acpi_db_get_display_op(struct acpi_walk_state137*walk_state,138union acpi_parse_object139*op)140{141union acpi_parse_object *display_op;142union acpi_parse_object *parent_op;143144display_op = op;145parent_op = op->common.parent;146if (parent_op) {147if ((walk_state->control_state) &&148(walk_state->control_state->common.state ==149ACPI_CONTROL_PREDICATE_EXECUTING)) {150/*151* We are executing the predicate of an IF or WHILE statement152* Search upwards for the containing IF or WHILE so that the153* entire predicate can be displayed.154*/155while (parent_op) {156if ((parent_op->common.aml_opcode == AML_IF_OP)157|| (parent_op->common.aml_opcode ==158AML_WHILE_OP)) {159display_op = parent_op;160break;161}162parent_op = parent_op->common.parent;163}164} else {165while (parent_op) {166if ((parent_op->common.aml_opcode == AML_IF_OP)167|| (parent_op->common.aml_opcode ==168AML_ELSE_OP)169|| (parent_op->common.aml_opcode ==170AML_SCOPE_OP)171|| (parent_op->common.aml_opcode ==172AML_METHOD_OP)173|| (parent_op->common.aml_opcode ==174AML_WHILE_OP)) {175break;176}177display_op = parent_op;178parent_op = parent_op->common.parent;179}180}181}182return display_op;183}184#endif185186/*******************************************************************************187*188* FUNCTION: acpi_db_single_step189*190* PARAMETERS: walk_state - Current walk191* op - Current executing op (from aml interpreter)192* opcode_class - Class of the current AML Opcode193*194* RETURN: Status195*196* DESCRIPTION: Called just before execution of an AML opcode.197*198******************************************************************************/199200acpi_status201acpi_db_single_step(struct acpi_walk_state *walk_state,202union acpi_parse_object *op, u32 opcode_class)203{204union acpi_parse_object *next;205acpi_status status = AE_OK;206u32 original_debug_level;207u32 aml_offset;208209ACPI_FUNCTION_ENTRY();210211#ifndef ACPI_APPLICATION212if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {213return (AE_OK);214}215#endif216217/* Check the abort flag */218219if (acpi_gbl_abort_method) {220acpi_gbl_abort_method = FALSE;221return (AE_ABORT_METHOD);222}223224aml_offset = (u32)ACPI_PTR_DIFF(op->common.aml,225walk_state->parser_state.aml_start);226227/* Check for single-step breakpoint */228229if (walk_state->method_breakpoint &&230(walk_state->method_breakpoint <= aml_offset)) {231232/* Check if the breakpoint has been reached or passed */233/* Hit the breakpoint, resume single step, reset breakpoint */234235acpi_os_printf("***Break*** at AML offset %X\n", aml_offset);236acpi_gbl_cm_single_step = TRUE;237acpi_gbl_step_to_next_call = FALSE;238walk_state->method_breakpoint = 0;239}240241/* Check for user breakpoint (Must be on exact Aml offset) */242243else if (walk_state->user_breakpoint &&244(walk_state->user_breakpoint == aml_offset)) {245acpi_os_printf("***UserBreakpoint*** at AML offset %X\n",246aml_offset);247acpi_gbl_cm_single_step = TRUE;248acpi_gbl_step_to_next_call = FALSE;249walk_state->method_breakpoint = 0;250}251252/*253* Check if this is an opcode that we are interested in --254* namely, opcodes that have arguments255*/256if (op->common.aml_opcode == AML_INT_NAMEDFIELD_OP) {257return (AE_OK);258}259260switch (opcode_class) {261case AML_CLASS_UNKNOWN:262case AML_CLASS_ARGUMENT: /* constants, literals, etc. do nothing */263264return (AE_OK);265266default:267268/* All other opcodes -- continue */269break;270}271272/*273* Under certain debug conditions, display this opcode and its operands274*/275if ((acpi_gbl_db_output_to_file) ||276(acpi_gbl_cm_single_step) || (acpi_dbg_level & ACPI_LV_PARSE)) {277if ((acpi_gbl_db_output_to_file) ||278(acpi_dbg_level & ACPI_LV_PARSE)) {279acpi_os_printf280("\nAML Debug: Next AML Opcode to execute:\n");281}282283/*284* Display this op (and only this op - zero out the NEXT field285* temporarily, and disable parser trace output for the duration of286* the display because we don't want the extraneous debug output)287*/288original_debug_level = acpi_dbg_level;289acpi_dbg_level &= ~(ACPI_LV_PARSE | ACPI_LV_FUNCTIONS);290next = op->common.next;291op->common.next = NULL;292293/* Now we can disassemble and display it */294295#ifdef ACPI_DISASSEMBLER296acpi_dm_disassemble(walk_state,297acpi_db_get_display_op(walk_state, op),298ACPI_UINT32_MAX);299#else300/*301* The AML Disassembler is not configured - at least we can302* display the opcode value and name303*/304acpi_os_printf("AML Opcode: %4.4X %s\n", op->common.aml_opcode,305acpi_ps_get_opcode_name(op->common.aml_opcode));306#endif307308if ((op->common.aml_opcode == AML_IF_OP) ||309(op->common.aml_opcode == AML_WHILE_OP)) {310if (walk_state->control_state->common.value) {311acpi_os_printf312("Predicate = [True], IF block was executed\n");313} else {314acpi_os_printf315("Predicate = [False], Skipping IF block\n");316}317} else if (op->common.aml_opcode == AML_ELSE_OP) {318acpi_os_printf319("Predicate = [False], ELSE block was executed\n");320}321322/* Restore everything */323324op->common.next = next;325acpi_os_printf("\n");326if ((acpi_gbl_db_output_to_file) ||327(acpi_dbg_level & ACPI_LV_PARSE)) {328acpi_os_printf("\n");329}330acpi_dbg_level = original_debug_level;331}332333/* If we are not single stepping, just continue executing the method */334335if (!acpi_gbl_cm_single_step) {336return (AE_OK);337}338339/*340* If we are executing a step-to-call command,341* Check if this is a method call.342*/343if (acpi_gbl_step_to_next_call) {344if (op->common.aml_opcode != AML_INT_METHODCALL_OP) {345346/* Not a method call, just keep executing */347348return (AE_OK);349}350351/* Found a method call, stop executing */352353acpi_gbl_step_to_next_call = FALSE;354}355356/*357* If the next opcode is a method call, we will "step over" it358* by default.359*/360if (op->common.aml_opcode == AML_INT_METHODCALL_OP) {361362/* Force no more single stepping while executing called method */363364acpi_gbl_cm_single_step = FALSE;365366/*367* Set the breakpoint on/before the call, it will stop execution368* as soon as we return369*/370walk_state->method_breakpoint = 1; /* Must be non-zero! */371}372373acpi_ex_exit_interpreter();374status = acpi_db_start_command(walk_state, op);375acpi_ex_enter_interpreter();376377/* User commands complete, continue execution of the interrupted method */378379return (status);380}381382/*******************************************************************************383*384* FUNCTION: acpi_initialize_debugger385*386* PARAMETERS: None387*388* RETURN: Status389*390* DESCRIPTION: Init and start debugger391*392******************************************************************************/393394acpi_status acpi_initialize_debugger(void)395{396acpi_status status;397398ACPI_FUNCTION_TRACE(acpi_initialize_debugger);399400/* Init globals */401402acpi_gbl_db_buffer = NULL;403acpi_gbl_db_filename = NULL;404acpi_gbl_db_output_to_file = FALSE;405406acpi_gbl_db_debug_level = ACPI_LV_VERBOSITY2;407acpi_gbl_db_console_debug_level = ACPI_NORMAL_DEFAULT | ACPI_LV_TABLES;408acpi_gbl_db_output_flags = ACPI_DB_CONSOLE_OUTPUT;409410acpi_gbl_db_opt_no_ini_methods = FALSE;411acpi_gbl_db_opt_no_region_support = FALSE;412413acpi_gbl_db_buffer = acpi_os_allocate(ACPI_DEBUG_BUFFER_SIZE);414if (!acpi_gbl_db_buffer) {415return_ACPI_STATUS(AE_NO_MEMORY);416}417memset(acpi_gbl_db_buffer, 0, ACPI_DEBUG_BUFFER_SIZE);418419/* Initial scope is the root */420421acpi_gbl_db_scope_buf[0] = AML_ROOT_PREFIX;422acpi_gbl_db_scope_buf[1] = 0;423acpi_gbl_db_scope_node = acpi_gbl_root_node;424425/* Initialize user commands loop */426427acpi_gbl_db_terminate_loop = FALSE;428429/*430* If configured for multi-thread support, the debug executor runs in431* a separate thread so that the front end can be in another address432* space, environment, or even another machine.433*/434if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) {435436/* These were created with one unit, grab it */437438status = acpi_os_initialize_debugger();439if (ACPI_FAILURE(status)) {440acpi_os_printf("Could not get debugger mutex\n");441return_ACPI_STATUS(status);442}443444/* Create the debug execution thread to execute commands */445446acpi_gbl_db_threads_terminated = FALSE;447status = acpi_os_execute(OSL_DEBUGGER_MAIN_THREAD,448acpi_db_execute_thread, NULL);449if (ACPI_FAILURE(status)) {450ACPI_EXCEPTION((AE_INFO, status,451"Could not start debugger thread"));452acpi_gbl_db_threads_terminated = TRUE;453return_ACPI_STATUS(status);454}455} else {456acpi_gbl_db_thread_id = acpi_os_get_thread_id();457}458459return_ACPI_STATUS(AE_OK);460}461462ACPI_EXPORT_SYMBOL(acpi_initialize_debugger)463464/*******************************************************************************465*466* FUNCTION: acpi_terminate_debugger467*468* PARAMETERS: None469*470* RETURN: None471*472* DESCRIPTION: Stop debugger473*474******************************************************************************/475void acpi_terminate_debugger(void)476{477478/* Terminate the AML Debugger */479480acpi_gbl_db_terminate_loop = TRUE;481482if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) {483484/* Wait the AML Debugger threads */485486while (!acpi_gbl_db_threads_terminated) {487acpi_os_sleep(100);488}489490acpi_os_terminate_debugger();491}492493if (acpi_gbl_db_buffer) {494acpi_os_free(acpi_gbl_db_buffer);495acpi_gbl_db_buffer = NULL;496}497498/* Ensure that debug output is now disabled */499500acpi_gbl_db_output_flags = ACPI_DB_DISABLE_OUTPUT;501}502503ACPI_EXPORT_SYMBOL(acpi_terminate_debugger)504505/*******************************************************************************506*507* FUNCTION: acpi_set_debugger_thread_id508*509* PARAMETERS: thread_id - Debugger thread ID510*511* RETURN: None512*513* DESCRIPTION: Set debugger thread ID514*515******************************************************************************/516void acpi_set_debugger_thread_id(acpi_thread_id thread_id)517{518acpi_gbl_db_thread_id = thread_id;519}520521ACPI_EXPORT_SYMBOL(acpi_set_debugger_thread_id)522523524