xref: /freebsd/sys/contrib/dev/acpica/components/debugger/dbxface.c (revision 3fc36ee018bb836bd1796067cf4ef8683f166ebc)
1 /*******************************************************************************
2  *
3  * Module Name: dbxface - AML Debugger external interfaces
4  *
5  ******************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2016, 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 MERCHANTIBILITY 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 <contrib/dev/acpica/include/acpi.h>
45 #include <contrib/dev/acpica/include/accommon.h>
46 #include <contrib/dev/acpica/include/amlcode.h>
47 #include <contrib/dev/acpica/include/acdebug.h>
48 
49 
50 #define _COMPONENT          ACPI_CA_DEBUGGER
51         ACPI_MODULE_NAME    ("dbxface")
52 
53 
54 /* Local prototypes */
55 
56 static ACPI_STATUS
57 AcpiDbStartCommand (
58     ACPI_WALK_STATE         *WalkState,
59     ACPI_PARSE_OBJECT       *Op);
60 
61 #ifdef ACPI_OBSOLETE_FUNCTIONS
62 void
63 AcpiDbMethodEnd (
64     ACPI_WALK_STATE         *WalkState);
65 #endif
66 
67 
68 /*******************************************************************************
69  *
70  * FUNCTION:    AcpiDbStartCommand
71  *
72  * PARAMETERS:  WalkState       - Current walk
73  *              Op              - Current executing Op, from AML interpreter
74  *
75  * RETURN:      Status
76  *
77  * DESCRIPTION: Enter debugger command loop
78  *
79  ******************************************************************************/
80 
81 static ACPI_STATUS
82 AcpiDbStartCommand (
83     ACPI_WALK_STATE         *WalkState,
84     ACPI_PARSE_OBJECT       *Op)
85 {
86     ACPI_STATUS             Status;
87 
88 
89     /* TBD: [Investigate] are there namespace locking issues here? */
90 
91     /* AcpiUtReleaseMutex (ACPI_MTX_NAMESPACE); */
92 
93     /* Go into the command loop and await next user command */
94 
95 
96     AcpiGbl_MethodExecuting = TRUE;
97     Status = AE_CTRL_TRUE;
98     while (Status == AE_CTRL_TRUE)
99     {
100         if (AcpiGbl_DebuggerConfiguration == DEBUGGER_MULTI_THREADED)
101         {
102             /* Handshake with the front-end that gets user command lines */
103 
104             AcpiOsReleaseMutex (AcpiGbl_DbCommandComplete);
105 
106             Status = AcpiOsAcquireMutex (AcpiGbl_DbCommandReady,
107                 ACPI_WAIT_FOREVER);
108             if (ACPI_FAILURE (Status))
109             {
110                 return (Status);
111             }
112         }
113         else
114         {
115             /* Single threaded, we must get a command line ourselves */
116 
117             /* Force output to console until a command is entered */
118 
119             AcpiDbSetOutputDestination (ACPI_DB_CONSOLE_OUTPUT);
120 
121             /* Different prompt if method is executing */
122 
123             if (!AcpiGbl_MethodExecuting)
124             {
125                 AcpiOsPrintf ("%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
126             }
127             else
128             {
129                 AcpiOsPrintf ("%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
130             }
131 
132             /* Get the user input line */
133 
134             Status = AcpiOsGetLine (AcpiGbl_DbLineBuf,
135                 ACPI_DB_LINE_BUFFER_SIZE, NULL);
136             if (ACPI_FAILURE (Status))
137             {
138                 ACPI_EXCEPTION ((AE_INFO, Status,
139                     "While parsing command line"));
140                 return (Status);
141             }
142         }
143 
144         Status = AcpiDbCommandDispatch (AcpiGbl_DbLineBuf, WalkState, Op);
145     }
146 
147     /* AcpiUtAcquireMutex (ACPI_MTX_NAMESPACE); */
148 
149     return (Status);
150 }
151 
152 
153 /*******************************************************************************
154  *
155  * FUNCTION:    AcpiDbSignalBreakPoint
156  *
157  * PARAMETERS:  WalkState       - Current walk
158  *
159  * RETURN:      Status
160  *
161  * DESCRIPTION: Called for AML_BREAK_POINT_OP
162  *
163  ******************************************************************************/
164 
165 void
166 AcpiDbSignalBreakPoint (
167     ACPI_WALK_STATE         *WalkState)
168 {
169 
170 #ifndef ACPI_APPLICATION
171     if (AcpiGbl_DbThreadId != AcpiOsGetThreadId ())
172     {
173         return;
174     }
175 #endif
176 
177     /*
178      * Set the single-step flag. This will cause the debugger (if present)
179      * to break to the console within the AML debugger at the start of the
180      * next AML instruction.
181      */
182     AcpiGbl_CmSingleStep = TRUE;
183     AcpiOsPrintf ("**break** Executed AML BreakPoint opcode\n");
184 }
185 
186 
187 /*******************************************************************************
188  *
189  * FUNCTION:    AcpiDbSingleStep
190  *
191  * PARAMETERS:  WalkState       - Current walk
192  *              Op              - Current executing op (from aml interpreter)
193  *              OpcodeClass     - Class of the current AML Opcode
194  *
195  * RETURN:      Status
196  *
197  * DESCRIPTION: Called just before execution of an AML opcode.
198  *
199  ******************************************************************************/
200 
201 ACPI_STATUS
202 AcpiDbSingleStep (
203     ACPI_WALK_STATE         *WalkState,
204     ACPI_PARSE_OBJECT       *Op,
205     UINT32                  OpcodeClass)
206 {
207     ACPI_PARSE_OBJECT       *Next;
208     ACPI_STATUS             Status = AE_OK;
209     UINT32                  OriginalDebugLevel;
210     ACPI_PARSE_OBJECT       *DisplayOp;
211     ACPI_PARSE_OBJECT       *ParentOp;
212     UINT32                  AmlOffset;
213 
214 
215     ACPI_FUNCTION_ENTRY ();
216 
217 
218 #ifndef ACPI_APPLICATION
219     if (AcpiGbl_DbThreadId != AcpiOsGetThreadId ())
220     {
221         return (AE_OK);
222     }
223 #endif
224 
225     /* Check the abort flag */
226 
227     if (AcpiGbl_AbortMethod)
228     {
229         AcpiGbl_AbortMethod = FALSE;
230         return (AE_ABORT_METHOD);
231     }
232 
233     AmlOffset = (UINT32) ACPI_PTR_DIFF (Op->Common.Aml,
234         WalkState->ParserState.AmlStart);
235 
236     /* Check for single-step breakpoint */
237 
238     if (WalkState->MethodBreakpoint &&
239        (WalkState->MethodBreakpoint <= AmlOffset))
240     {
241         /* Check if the breakpoint has been reached or passed */
242         /* Hit the breakpoint, resume single step, reset breakpoint */
243 
244         AcpiOsPrintf ("***Break*** at AML offset %X\n", AmlOffset);
245         AcpiGbl_CmSingleStep = TRUE;
246         AcpiGbl_StepToNextCall = FALSE;
247         WalkState->MethodBreakpoint = 0;
248     }
249 
250     /* Check for user breakpoint (Must be on exact Aml offset) */
251 
252     else if (WalkState->UserBreakpoint &&
253             (WalkState->UserBreakpoint == AmlOffset))
254     {
255         AcpiOsPrintf ("***UserBreakpoint*** at AML offset %X\n",
256             AmlOffset);
257         AcpiGbl_CmSingleStep = TRUE;
258         AcpiGbl_StepToNextCall = FALSE;
259         WalkState->MethodBreakpoint = 0;
260     }
261 
262     /*
263      * Check if this is an opcode that we are interested in --
264      * namely, opcodes that have arguments
265      */
266     if (Op->Common.AmlOpcode == AML_INT_NAMEDFIELD_OP)
267     {
268         return (AE_OK);
269     }
270 
271     switch (OpcodeClass)
272     {
273     case AML_CLASS_UNKNOWN:
274     case AML_CLASS_ARGUMENT:    /* constants, literals, etc. do nothing */
275 
276         return (AE_OK);
277 
278     default:
279 
280         /* All other opcodes -- continue */
281         break;
282     }
283 
284     /*
285      * Under certain debug conditions, display this opcode and its operands
286      */
287     if ((AcpiGbl_DbOutputToFile)            ||
288         (AcpiGbl_CmSingleStep)              ||
289         (AcpiDbgLevel & ACPI_LV_PARSE))
290     {
291         if ((AcpiGbl_DbOutputToFile)        ||
292             (AcpiDbgLevel & ACPI_LV_PARSE))
293         {
294             AcpiOsPrintf ("\n[AmlDebug] Next AML Opcode to execute:\n");
295         }
296 
297         /*
298          * Display this op (and only this op - zero out the NEXT field
299          * temporarily, and disable parser trace output for the duration of
300          * the display because we don't want the extraneous debug output)
301          */
302         OriginalDebugLevel = AcpiDbgLevel;
303         AcpiDbgLevel &= ~(ACPI_LV_PARSE | ACPI_LV_FUNCTIONS);
304         Next = Op->Common.Next;
305         Op->Common.Next = NULL;
306 
307 
308         DisplayOp = Op;
309         ParentOp = Op->Common.Parent;
310         if (ParentOp)
311         {
312             if ((WalkState->ControlState) &&
313                 (WalkState->ControlState->Common.State ==
314                     ACPI_CONTROL_PREDICATE_EXECUTING))
315             {
316                 /*
317                  * We are executing the predicate of an IF or WHILE statement
318                  * Search upwards for the containing IF or WHILE so that the
319                  * entire predicate can be displayed.
320                  */
321                 while (ParentOp)
322                 {
323                     if ((ParentOp->Common.AmlOpcode == AML_IF_OP) ||
324                         (ParentOp->Common.AmlOpcode == AML_WHILE_OP))
325                     {
326                         DisplayOp = ParentOp;
327                         break;
328                     }
329                     ParentOp = ParentOp->Common.Parent;
330                 }
331             }
332             else
333             {
334                 while (ParentOp)
335                 {
336                     if ((ParentOp->Common.AmlOpcode == AML_IF_OP)     ||
337                         (ParentOp->Common.AmlOpcode == AML_ELSE_OP)   ||
338                         (ParentOp->Common.AmlOpcode == AML_SCOPE_OP)  ||
339                         (ParentOp->Common.AmlOpcode == AML_METHOD_OP) ||
340                         (ParentOp->Common.AmlOpcode == AML_WHILE_OP))
341                     {
342                         break;
343                     }
344                     DisplayOp = ParentOp;
345                     ParentOp = ParentOp->Common.Parent;
346                 }
347             }
348         }
349 
350         /* Now we can display it */
351 
352 #ifdef ACPI_DISASSEMBLER
353         AcpiDmDisassemble (WalkState, DisplayOp, ACPI_UINT32_MAX);
354 #endif
355 
356         if ((Op->Common.AmlOpcode == AML_IF_OP) ||
357             (Op->Common.AmlOpcode == AML_WHILE_OP))
358         {
359             if (WalkState->ControlState->Common.Value)
360             {
361                 AcpiOsPrintf ("Predicate = [True], IF block was executed\n");
362             }
363             else
364             {
365                 AcpiOsPrintf ("Predicate = [False], Skipping IF block\n");
366             }
367         }
368         else if (Op->Common.AmlOpcode == AML_ELSE_OP)
369         {
370             AcpiOsPrintf ("Predicate = [False], ELSE block was executed\n");
371         }
372 
373         /* Restore everything */
374 
375         Op->Common.Next = Next;
376         AcpiOsPrintf ("\n");
377         if ((AcpiGbl_DbOutputToFile)        ||
378             (AcpiDbgLevel & ACPI_LV_PARSE))
379         {
380             AcpiOsPrintf ("\n");
381         }
382         AcpiDbgLevel = OriginalDebugLevel;
383     }
384 
385     /* If we are not single stepping, just continue executing the method */
386 
387     if (!AcpiGbl_CmSingleStep)
388     {
389         return (AE_OK);
390     }
391 
392     /*
393      * If we are executing a step-to-call command,
394      * Check if this is a method call.
395      */
396     if (AcpiGbl_StepToNextCall)
397     {
398         if (Op->Common.AmlOpcode != AML_INT_METHODCALL_OP)
399         {
400             /* Not a method call, just keep executing */
401 
402             return (AE_OK);
403         }
404 
405         /* Found a method call, stop executing */
406 
407         AcpiGbl_StepToNextCall = FALSE;
408     }
409 
410     /*
411      * If the next opcode is a method call, we will "step over" it
412      * by default.
413      */
414     if (Op->Common.AmlOpcode == AML_INT_METHODCALL_OP)
415     {
416         /* Force no more single stepping while executing called method */
417 
418         AcpiGbl_CmSingleStep = FALSE;
419 
420         /*
421          * Set the breakpoint on/before the call, it will stop execution
422          * as soon as we return
423          */
424         WalkState->MethodBreakpoint = 1;  /* Must be non-zero! */
425     }
426 
427 
428     Status = AcpiDbStartCommand (WalkState, Op);
429 
430     /* User commands complete, continue execution of the interrupted method */
431 
432     return (Status);
433 }
434 
435 
436 /*******************************************************************************
437  *
438  * FUNCTION:    AcpiInitializeDebugger
439  *
440  * PARAMETERS:  None
441  *
442  * RETURN:      Status
443  *
444  * DESCRIPTION: Init and start debugger
445  *
446  ******************************************************************************/
447 
448 ACPI_STATUS
449 AcpiInitializeDebugger (
450     void)
451 {
452     ACPI_STATUS             Status;
453 
454 
455     ACPI_FUNCTION_TRACE (AcpiInitializeDebugger);
456 
457 
458     /* Init globals */
459 
460     AcpiGbl_DbBuffer            = NULL;
461     AcpiGbl_DbFilename          = NULL;
462     AcpiGbl_DbOutputToFile      = FALSE;
463 
464     AcpiGbl_DbDebugLevel        = ACPI_LV_VERBOSITY2;
465     AcpiGbl_DbConsoleDebugLevel = ACPI_NORMAL_DEFAULT | ACPI_LV_TABLES;
466     AcpiGbl_DbOutputFlags       = ACPI_DB_CONSOLE_OUTPUT;
467 
468     AcpiGbl_DbOpt_NoIniMethods  = FALSE;
469 
470     AcpiGbl_DbBuffer = AcpiOsAllocate (ACPI_DEBUG_BUFFER_SIZE);
471     if (!AcpiGbl_DbBuffer)
472     {
473         return_ACPI_STATUS (AE_NO_MEMORY);
474     }
475     memset (AcpiGbl_DbBuffer, 0, ACPI_DEBUG_BUFFER_SIZE);
476 
477     /* Initial scope is the root */
478 
479     AcpiGbl_DbScopeBuf [0] = AML_ROOT_PREFIX;
480     AcpiGbl_DbScopeBuf [1] =  0;
481     AcpiGbl_DbScopeNode = AcpiGbl_RootNode;
482 
483     /* Initialize user commands loop */
484 
485     AcpiGbl_DbTerminateLoop = FALSE;
486 
487     /*
488      * If configured for multi-thread support, the debug executor runs in
489      * a separate thread so that the front end can be in another address
490      * space, environment, or even another machine.
491      */
492     if (AcpiGbl_DebuggerConfiguration & DEBUGGER_MULTI_THREADED)
493     {
494         /* These were created with one unit, grab it */
495 
496         Status = AcpiOsAcquireMutex (AcpiGbl_DbCommandComplete,
497             ACPI_WAIT_FOREVER);
498         if (ACPI_FAILURE (Status))
499         {
500             AcpiOsPrintf ("Could not get debugger mutex\n");
501             return_ACPI_STATUS (Status);
502         }
503 
504         Status = AcpiOsAcquireMutex (AcpiGbl_DbCommandReady,
505             ACPI_WAIT_FOREVER);
506         if (ACPI_FAILURE (Status))
507         {
508             AcpiOsPrintf ("Could not get debugger mutex\n");
509             return_ACPI_STATUS (Status);
510         }
511 
512         /* Create the debug execution thread to execute commands */
513 
514         AcpiGbl_DbThreadsTerminated = FALSE;
515         Status = AcpiOsExecute (OSL_DEBUGGER_MAIN_THREAD,
516             AcpiDbExecuteThread, NULL);
517         if (ACPI_FAILURE (Status))
518         {
519             ACPI_EXCEPTION ((AE_INFO, Status,
520                 "Could not start debugger thread"));
521             AcpiGbl_DbThreadsTerminated = TRUE;
522             return_ACPI_STATUS (Status);
523         }
524     }
525     else
526     {
527         AcpiGbl_DbThreadId = AcpiOsGetThreadId ();
528     }
529 
530     return_ACPI_STATUS (AE_OK);
531 }
532 
533 ACPI_EXPORT_SYMBOL (AcpiInitializeDebugger)
534 
535 
536 /*******************************************************************************
537  *
538  * FUNCTION:    AcpiTerminateDebugger
539  *
540  * PARAMETERS:  None
541  *
542  * RETURN:      None
543  *
544  * DESCRIPTION: Stop debugger
545  *
546  ******************************************************************************/
547 
548 void
549 AcpiTerminateDebugger (
550     void)
551 {
552 
553     /* Terminate the AML Debugger */
554 
555     AcpiGbl_DbTerminateLoop = TRUE;
556 
557     if (AcpiGbl_DebuggerConfiguration & DEBUGGER_MULTI_THREADED)
558     {
559         AcpiOsReleaseMutex (AcpiGbl_DbCommandReady);
560 
561         /* Wait the AML Debugger threads */
562 
563         while (!AcpiGbl_DbThreadsTerminated)
564         {
565             AcpiOsSleep (100);
566         }
567     }
568 
569     if (AcpiGbl_DbBuffer)
570     {
571         AcpiOsFree (AcpiGbl_DbBuffer);
572         AcpiGbl_DbBuffer = NULL;
573     }
574 
575     /* Ensure that debug output is now disabled */
576 
577     AcpiGbl_DbOutputFlags = ACPI_DB_DISABLE_OUTPUT;
578 }
579 
580 ACPI_EXPORT_SYMBOL (AcpiTerminateDebugger)
581 
582 
583 /*******************************************************************************
584  *
585  * FUNCTION:    AcpiSetDebuggerThreadId
586  *
587  * PARAMETERS:  ThreadId        - Debugger thread ID
588  *
589  * RETURN:      None
590  *
591  * DESCRIPTION: Set debugger thread ID
592  *
593  ******************************************************************************/
594 
595 void
596 AcpiSetDebuggerThreadId (
597     ACPI_THREAD_ID          ThreadId)
598 {
599     AcpiGbl_DbThreadId = ThreadId;
600 }
601 
602 ACPI_EXPORT_SYMBOL (AcpiSetDebuggerThreadId)
603