xref: /netbsd-src/sys/external/bsd/acpica/dist/debugger/dbxface.c (revision 046a29855e04359424fd074e8313af6b6be8cfb6)
1 /*******************************************************************************
2  *
3  * Module Name: dbxface - AML Debugger external interfaces
4  *
5  ******************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2023, 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
AcpiDbStartCommand(ACPI_WALK_STATE * WalkState,ACPI_PARSE_OBJECT * Op)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
AcpiDbSignalBreakPoint(ACPI_WALK_STATE * WalkState)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 *
AcpiDbGetDisplayOp(ACPI_WALK_STATE * WalkState,ACPI_PARSE_OBJECT * Op)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
AcpiDbSingleStep(ACPI_WALK_STATE * WalkState,ACPI_PARSE_OBJECT * Op,UINT32 OpcodeClass)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
AcpiInitializeDebugger(void)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 
ACPI_EXPORT_SYMBOL(AcpiInitializeDebugger)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 
ACPI_EXPORT_SYMBOL(AcpiTerminateDebugger)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