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