1 /******************************************************************************* 2 * 3 * Module Name: dbxface - AML Debugger external interfaces 4 * 5 ******************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2022, Intel Corp. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions, and the following disclaimer, 16 * without modification. 17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 18 * substantially similar to the "NO WARRANTY" disclaimer below 19 * ("Disclaimer") and any redistribution must be conditioned upon 20 * including a substantially similar Disclaimer requirement for further 21 * binary redistribution. 22 * 3. Neither the names of the above-listed copyright holders nor the names 23 * of any contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * Alternatively, this software may be distributed under the terms of the 27 * GNU General Public License ("GPL") version 2 as published by the Free 28 * Software Foundation. 29 * 30 * NO WARRANTY 31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 * POSSIBILITY OF SUCH DAMAGES. 42 */ 43 44 #include "acpi.h" 45 #include "accommon.h" 46 #include "amlcode.h" 47 #include "acdebug.h" 48 #include "acinterp.h" 49 #include "acparser.h" 50 51 52 #define _COMPONENT ACPI_CA_DEBUGGER 53 ACPI_MODULE_NAME ("dbxface") 54 55 56 /* Local prototypes */ 57 58 static ACPI_STATUS 59 AcpiDbStartCommand ( 60 ACPI_WALK_STATE *WalkState, 61 ACPI_PARSE_OBJECT *Op); 62 63 #ifdef ACPI_OBSOLETE_FUNCTIONS 64 void 65 AcpiDbMethodEnd ( 66 ACPI_WALK_STATE *WalkState); 67 #endif 68 69 #ifdef ACPI_DISASSEMBLER 70 static ACPI_PARSE_OBJECT * 71 AcpiDbGetDisplayOp ( 72 ACPI_WALK_STATE *WalkState, 73 ACPI_PARSE_OBJECT *Op); 74 #endif 75 76 /******************************************************************************* 77 * 78 * FUNCTION: AcpiDbStartCommand 79 * 80 * PARAMETERS: WalkState - Current walk 81 * Op - Current executing Op, from AML interpreter 82 * 83 * RETURN: Status 84 * 85 * DESCRIPTION: Enter debugger command loop 86 * 87 ******************************************************************************/ 88 89 static ACPI_STATUS 90 AcpiDbStartCommand ( 91 ACPI_WALK_STATE *WalkState, 92 ACPI_PARSE_OBJECT *Op) 93 { 94 ACPI_STATUS Status; 95 96 97 /* TBD: [Investigate] are there namespace locking issues here? */ 98 99 /* AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE); */ 100 101 /* Go into the command loop and await next user command */ 102 103 104 AcpiGbl_MethodExecuting = TRUE; 105 Status = AE_CTRL_TRUE; 106 107 while (Status == AE_CTRL_TRUE) 108 { 109 /* Notify the completion of the command */ 110 111 Status = AcpiOsNotifyCommandComplete (); 112 if (ACPI_FAILURE (Status)) 113 { 114 goto ErrorExit; 115 } 116 117 /* Wait the readiness of the command */ 118 119 Status = AcpiOsWaitCommandReady (); 120 if (ACPI_FAILURE (Status)) 121 { 122 goto ErrorExit; 123 } 124 125 Status = AcpiDbCommandDispatch (AcpiGbl_DbLineBuf, WalkState, Op); 126 } 127 128 /* AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE); */ 129 130 ErrorExit: 131 if (ACPI_FAILURE (Status) && Status != AE_CTRL_TERMINATE) 132 { 133 ACPI_EXCEPTION ((AE_INFO, Status, 134 "While parsing/handling command line")); 135 } 136 return (Status); 137 } 138 139 140 /******************************************************************************* 141 * 142 * FUNCTION: AcpiDbSignalBreakPoint 143 * 144 * PARAMETERS: WalkState - Current walk 145 * 146 * RETURN: Status 147 * 148 * DESCRIPTION: Called for AML_BREAKPOINT_OP 149 * 150 ******************************************************************************/ 151 152 void 153 AcpiDbSignalBreakPoint ( 154 ACPI_WALK_STATE *WalkState) 155 { 156 157 #ifndef ACPI_APPLICATION 158 if (AcpiGbl_DbThreadId != AcpiOsGetThreadId ()) 159 { 160 return; 161 } 162 #endif 163 164 /* 165 * Set the single-step flag. This will cause the debugger (if present) 166 * to break to the console within the AML debugger at the start of the 167 * next AML instruction. 168 */ 169 AcpiGbl_CmSingleStep = TRUE; 170 AcpiOsPrintf ("**break** Executed AML BreakPoint opcode\n"); 171 } 172 173 174 #ifdef ACPI_DISASSEMBLER 175 /******************************************************************************* 176 * 177 * FUNCTION: AcpiDbGetDisplayOp 178 * 179 * PARAMETERS: WalkState - Current walk 180 * Op - Current executing op (from aml interpreter) 181 * 182 * RETURN: Opcode to display 183 * 184 * DESCRIPTION: Find the opcode to display during single stepping 185 * 186 ******************************************************************************/ 187 188 static ACPI_PARSE_OBJECT * 189 AcpiDbGetDisplayOp ( 190 ACPI_WALK_STATE *WalkState, 191 ACPI_PARSE_OBJECT *Op) 192 { 193 ACPI_PARSE_OBJECT *DisplayOp; 194 ACPI_PARSE_OBJECT *ParentOp; 195 196 DisplayOp = Op; 197 ParentOp = Op->Common.Parent; 198 if (ParentOp) 199 { 200 if ((WalkState->ControlState) && 201 (WalkState->ControlState->Common.State == 202 ACPI_CONTROL_PREDICATE_EXECUTING)) 203 { 204 /* 205 * We are executing the predicate of an IF or WHILE statement 206 * Search upwards for the containing IF or WHILE so that the 207 * entire predicate can be displayed. 208 */ 209 while (ParentOp) 210 { 211 if ((ParentOp->Common.AmlOpcode == AML_IF_OP) || 212 (ParentOp->Common.AmlOpcode == AML_WHILE_OP)) 213 { 214 DisplayOp = ParentOp; 215 break; 216 } 217 ParentOp = ParentOp->Common.Parent; 218 } 219 } 220 else 221 { 222 while (ParentOp) 223 { 224 if ((ParentOp->Common.AmlOpcode == AML_IF_OP) || 225 (ParentOp->Common.AmlOpcode == AML_ELSE_OP) || 226 (ParentOp->Common.AmlOpcode == AML_SCOPE_OP) || 227 (ParentOp->Common.AmlOpcode == AML_METHOD_OP) || 228 (ParentOp->Common.AmlOpcode == AML_WHILE_OP)) 229 { 230 break; 231 } 232 DisplayOp = ParentOp; 233 ParentOp = ParentOp->Common.Parent; 234 } 235 } 236 } 237 return DisplayOp; 238 } 239 #endif 240 241 242 /******************************************************************************* 243 * 244 * FUNCTION: AcpiDbSingleStep 245 * 246 * PARAMETERS: WalkState - Current walk 247 * Op - Current executing op (from aml interpreter) 248 * OpcodeClass - Class of the current AML Opcode 249 * 250 * RETURN: Status 251 * 252 * DESCRIPTION: Called just before execution of an AML opcode. 253 * 254 ******************************************************************************/ 255 256 ACPI_STATUS 257 AcpiDbSingleStep ( 258 ACPI_WALK_STATE *WalkState, 259 ACPI_PARSE_OBJECT *Op, 260 UINT32 OpcodeClass) 261 { 262 ACPI_PARSE_OBJECT *Next; 263 ACPI_STATUS Status = AE_OK; 264 UINT32 OriginalDebugLevel; 265 UINT32 AmlOffset; 266 267 268 ACPI_FUNCTION_ENTRY (); 269 270 271 #ifndef ACPI_APPLICATION 272 if (AcpiGbl_DbThreadId != AcpiOsGetThreadId ()) 273 { 274 return (AE_OK); 275 } 276 #endif 277 278 /* Check the abort flag */ 279 280 if (AcpiGbl_AbortMethod) 281 { 282 AcpiGbl_AbortMethod = FALSE; 283 return (AE_ABORT_METHOD); 284 } 285 286 AmlOffset = (UINT32) ACPI_PTR_DIFF (Op->Common.Aml, 287 WalkState->ParserState.AmlStart); 288 289 /* Check for single-step breakpoint */ 290 291 if (WalkState->MethodBreakpoint && 292 (WalkState->MethodBreakpoint <= AmlOffset)) 293 { 294 /* Check if the breakpoint has been reached or passed */ 295 /* Hit the breakpoint, resume single step, reset breakpoint */ 296 297 AcpiOsPrintf ("***Break*** at AML offset %X\n", AmlOffset); 298 AcpiGbl_CmSingleStep = TRUE; 299 AcpiGbl_StepToNextCall = FALSE; 300 WalkState->MethodBreakpoint = 0; 301 } 302 303 /* Check for user breakpoint (Must be on exact Aml offset) */ 304 305 else if (WalkState->UserBreakpoint && 306 (WalkState->UserBreakpoint == AmlOffset)) 307 { 308 AcpiOsPrintf ("***UserBreakpoint*** at AML offset %X\n", 309 AmlOffset); 310 AcpiGbl_CmSingleStep = TRUE; 311 AcpiGbl_StepToNextCall = FALSE; 312 WalkState->MethodBreakpoint = 0; 313 } 314 315 /* 316 * Check if this is an opcode that we are interested in -- 317 * namely, opcodes that have arguments 318 */ 319 if (Op->Common.AmlOpcode == AML_INT_NAMEDFIELD_OP) 320 { 321 return (AE_OK); 322 } 323 324 switch (OpcodeClass) 325 { 326 case AML_CLASS_UNKNOWN: 327 case AML_CLASS_ARGUMENT: /* constants, literals, etc. do nothing */ 328 329 return (AE_OK); 330 331 default: 332 333 /* All other opcodes -- continue */ 334 break; 335 } 336 337 /* 338 * Under certain debug conditions, display this opcode and its operands 339 */ 340 if ((AcpiGbl_DbOutputToFile) || 341 (AcpiGbl_CmSingleStep) || 342 (AcpiDbgLevel & ACPI_LV_PARSE)) 343 { 344 if ((AcpiGbl_DbOutputToFile) || 345 (AcpiDbgLevel & ACPI_LV_PARSE)) 346 { 347 AcpiOsPrintf ("\nAML Debug: Next AML Opcode to execute:\n"); 348 } 349 350 /* 351 * Display this op (and only this op - zero out the NEXT field 352 * temporarily, and disable parser trace output for the duration of 353 * the display because we don't want the extraneous debug output) 354 */ 355 OriginalDebugLevel = AcpiDbgLevel; 356 AcpiDbgLevel &= ~(ACPI_LV_PARSE | ACPI_LV_FUNCTIONS); 357 Next = Op->Common.Next; 358 Op->Common.Next = NULL; 359 360 /* Now we can disassemble and display it */ 361 362 #ifdef ACPI_DISASSEMBLER 363 AcpiDmDisassemble (WalkState, AcpiDbGetDisplayOp (WalkState, Op), 364 ACPI_UINT32_MAX); 365 #else 366 /* 367 * The AML Disassembler is not configured - at least we can 368 * display the opcode value and name 369 */ 370 AcpiOsPrintf ("AML Opcode: %4.4X %s\n", Op->Common.AmlOpcode, 371 AcpiPsGetOpcodeName (Op->Common.AmlOpcode)); 372 #endif 373 374 if ((Op->Common.AmlOpcode == AML_IF_OP) || 375 (Op->Common.AmlOpcode == AML_WHILE_OP)) 376 { 377 if (WalkState->ControlState->Common.Value) 378 { 379 AcpiOsPrintf ("Predicate = [True], IF block was executed\n"); 380 } 381 else 382 { 383 AcpiOsPrintf ("Predicate = [False], Skipping IF block\n"); 384 } 385 } 386 else if (Op->Common.AmlOpcode == AML_ELSE_OP) 387 { 388 AcpiOsPrintf ("Predicate = [False], ELSE block was executed\n"); 389 } 390 391 /* Restore everything */ 392 393 Op->Common.Next = Next; 394 AcpiOsPrintf ("\n"); 395 if ((AcpiGbl_DbOutputToFile) || 396 (AcpiDbgLevel & ACPI_LV_PARSE)) 397 { 398 AcpiOsPrintf ("\n"); 399 } 400 AcpiDbgLevel = OriginalDebugLevel; 401 } 402 403 /* If we are not single stepping, just continue executing the method */ 404 405 if (!AcpiGbl_CmSingleStep) 406 { 407 return (AE_OK); 408 } 409 410 /* 411 * If we are executing a step-to-call command, 412 * Check if this is a method call. 413 */ 414 if (AcpiGbl_StepToNextCall) 415 { 416 if (Op->Common.AmlOpcode != AML_INT_METHODCALL_OP) 417 { 418 /* Not a method call, just keep executing */ 419 420 return (AE_OK); 421 } 422 423 /* Found a method call, stop executing */ 424 425 AcpiGbl_StepToNextCall = FALSE; 426 } 427 428 /* 429 * If the next opcode is a method call, we will "step over" it 430 * by default. 431 */ 432 if (Op->Common.AmlOpcode == AML_INT_METHODCALL_OP) 433 { 434 /* Force no more single stepping while executing called method */ 435 436 AcpiGbl_CmSingleStep = FALSE; 437 438 /* 439 * Set the breakpoint on/before the call, it will stop execution 440 * as soon as we return 441 */ 442 WalkState->MethodBreakpoint = 1; /* Must be non-zero! */ 443 } 444 445 446 AcpiExExitInterpreter (); 447 Status = AcpiDbStartCommand (WalkState, Op); 448 AcpiExEnterInterpreter (); 449 450 /* User commands complete, continue execution of the interrupted method */ 451 452 return (Status); 453 } 454 455 456 /******************************************************************************* 457 * 458 * FUNCTION: AcpiInitializeDebugger 459 * 460 * PARAMETERS: None 461 * 462 * RETURN: Status 463 * 464 * DESCRIPTION: Init and start debugger 465 * 466 ******************************************************************************/ 467 468 ACPI_STATUS 469 AcpiInitializeDebugger ( 470 void) 471 { 472 ACPI_STATUS Status; 473 474 475 ACPI_FUNCTION_TRACE (AcpiInitializeDebugger); 476 477 478 /* Init globals */ 479 480 AcpiGbl_DbBuffer = NULL; 481 AcpiGbl_DbFilename = NULL; 482 AcpiGbl_DbOutputToFile = FALSE; 483 484 AcpiGbl_DbDebugLevel = ACPI_LV_VERBOSITY2; 485 AcpiGbl_DbConsoleDebugLevel = ACPI_NORMAL_DEFAULT | ACPI_LV_TABLES; 486 AcpiGbl_DbOutputFlags = ACPI_DB_CONSOLE_OUTPUT; 487 488 AcpiGbl_DbOpt_NoIniMethods = FALSE; 489 AcpiGbl_DbOpt_NoRegionSupport = FALSE; 490 491 AcpiGbl_DbBuffer = AcpiOsAllocate (ACPI_DEBUG_BUFFER_SIZE); 492 if (!AcpiGbl_DbBuffer) 493 { 494 return_ACPI_STATUS (AE_NO_MEMORY); 495 } 496 memset (AcpiGbl_DbBuffer, 0, ACPI_DEBUG_BUFFER_SIZE); 497 498 /* Initial scope is the root */ 499 500 AcpiGbl_DbScopeBuf [0] = AML_ROOT_PREFIX; 501 AcpiGbl_DbScopeBuf [1] = 0; 502 AcpiGbl_DbScopeNode = AcpiGbl_RootNode; 503 504 /* Initialize user commands loop */ 505 506 AcpiGbl_DbTerminateLoop = FALSE; 507 508 /* 509 * If configured for multi-thread support, the debug executor runs in 510 * a separate thread so that the front end can be in another address 511 * space, environment, or even another machine. 512 */ 513 if (AcpiGbl_DebuggerConfiguration & DEBUGGER_MULTI_THREADED) 514 { 515 /* These were created with one unit, grab it */ 516 517 Status = AcpiOsInitializeDebugger (); 518 if (ACPI_FAILURE (Status)) 519 { 520 AcpiOsPrintf ("Could not get debugger mutex\n"); 521 return_ACPI_STATUS (Status); 522 } 523 524 /* Create the debug execution thread to execute commands */ 525 526 AcpiGbl_DbThreadsTerminated = FALSE; 527 Status = AcpiOsExecute (OSL_DEBUGGER_MAIN_THREAD, 528 AcpiDbExecuteThread, NULL); 529 if (ACPI_FAILURE (Status)) 530 { 531 ACPI_EXCEPTION ((AE_INFO, Status, 532 "Could not start debugger thread")); 533 AcpiGbl_DbThreadsTerminated = TRUE; 534 return_ACPI_STATUS (Status); 535 } 536 } 537 else 538 { 539 AcpiGbl_DbThreadId = AcpiOsGetThreadId (); 540 } 541 542 return_ACPI_STATUS (AE_OK); 543 } 544 545 ACPI_EXPORT_SYMBOL (AcpiInitializeDebugger) 546 547 548 /******************************************************************************* 549 * 550 * FUNCTION: AcpiTerminateDebugger 551 * 552 * PARAMETERS: None 553 * 554 * RETURN: None 555 * 556 * DESCRIPTION: Stop debugger 557 * 558 ******************************************************************************/ 559 560 void 561 AcpiTerminateDebugger ( 562 void) 563 { 564 565 /* Terminate the AML Debugger */ 566 567 AcpiGbl_DbTerminateLoop = TRUE; 568 569 if (AcpiGbl_DebuggerConfiguration & DEBUGGER_MULTI_THREADED) 570 { 571 /* Wait the AML Debugger threads */ 572 573 while (!AcpiGbl_DbThreadsTerminated) 574 { 575 AcpiOsSleep (100); 576 } 577 578 AcpiOsTerminateDebugger (); 579 } 580 581 if (AcpiGbl_DbBuffer) 582 { 583 AcpiOsFree (AcpiGbl_DbBuffer); 584 AcpiGbl_DbBuffer = NULL; 585 } 586 587 /* Ensure that debug output is now disabled */ 588 589 AcpiGbl_DbOutputFlags = ACPI_DB_DISABLE_OUTPUT; 590 } 591 592 ACPI_EXPORT_SYMBOL (AcpiTerminateDebugger) 593 594 595 /******************************************************************************* 596 * 597 * FUNCTION: AcpiSetDebuggerThreadId 598 * 599 * PARAMETERS: ThreadId - Debugger thread ID 600 * 601 * RETURN: None 602 * 603 * DESCRIPTION: Set debugger thread ID 604 * 605 ******************************************************************************/ 606 607 void 608 AcpiSetDebuggerThreadId ( 609 ACPI_THREAD_ID ThreadId) 610 { 611 AcpiGbl_DbThreadId = ThreadId; 612 } 613 614 ACPI_EXPORT_SYMBOL (AcpiSetDebuggerThreadId) 615