xref: /freebsd/sys/contrib/dev/acpica/components/parser/psloop.c (revision bd18fd57db1df29da1a3adf94d47924a977a29c2)
1 /******************************************************************************
2  *
3  * Module Name: psloop - Main AML parse loop
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 /*
45  * Parse the AML and build an operation tree as most interpreters, (such as
46  * Perl) do. Parsing is done by hand rather than with a YACC generated parser
47  * to tightly constrain stack and dynamic memory usage. Parsing is kept
48  * flexible and the code fairly compact by parsing based on a list of AML
49  * opcode templates in AmlOpInfo[].
50  */
51 
52 #include <contrib/dev/acpica/include/acpi.h>
53 #include <contrib/dev/acpica/include/accommon.h>
54 #include <contrib/dev/acpica/include/acinterp.h>
55 #include <contrib/dev/acpica/include/acparser.h>
56 #include <contrib/dev/acpica/include/acdispat.h>
57 #include <contrib/dev/acpica/include/amlcode.h>
58 
59 #define _COMPONENT          ACPI_PARSER
60         ACPI_MODULE_NAME    ("psloop")
61 
62 
63 /* Local prototypes */
64 
65 static ACPI_STATUS
66 AcpiPsGetArguments (
67     ACPI_WALK_STATE         *WalkState,
68     UINT8                   *AmlOpStart,
69     ACPI_PARSE_OBJECT       *Op);
70 
71 static void
72 AcpiPsLinkModuleCode (
73     ACPI_PARSE_OBJECT       *ParentOp,
74     UINT8                   *AmlStart,
75     UINT32                  AmlLength,
76     ACPI_OWNER_ID           OwnerId);
77 
78 
79 /*******************************************************************************
80  *
81  * FUNCTION:    AcpiPsGetArguments
82  *
83  * PARAMETERS:  WalkState           - Current state
84  *              AmlOpStart          - Op start in AML
85  *              Op                  - Current Op
86  *
87  * RETURN:      Status
88  *
89  * DESCRIPTION: Get arguments for passed Op.
90  *
91  ******************************************************************************/
92 
93 static ACPI_STATUS
94 AcpiPsGetArguments (
95     ACPI_WALK_STATE         *WalkState,
96     UINT8                   *AmlOpStart,
97     ACPI_PARSE_OBJECT       *Op)
98 {
99     ACPI_STATUS             Status = AE_OK;
100     ACPI_PARSE_OBJECT       *Arg = NULL;
101     const ACPI_OPCODE_INFO  *OpInfo;
102 
103 
104     ACPI_FUNCTION_TRACE_PTR (PsGetArguments, WalkState);
105 
106 
107     switch (Op->Common.AmlOpcode)
108     {
109     case AML_BYTE_OP:       /* AML_BYTEDATA_ARG */
110     case AML_WORD_OP:       /* AML_WORDDATA_ARG */
111     case AML_DWORD_OP:      /* AML_DWORDATA_ARG */
112     case AML_QWORD_OP:      /* AML_QWORDATA_ARG */
113     case AML_STRING_OP:     /* AML_ASCIICHARLIST_ARG */
114 
115         /* Fill in constant or string argument directly */
116 
117         AcpiPsGetNextSimpleArg (&(WalkState->ParserState),
118             GET_CURRENT_ARG_TYPE (WalkState->ArgTypes), Op);
119         break;
120 
121     case AML_INT_NAMEPATH_OP:   /* AML_NAMESTRING_ARG */
122 
123         Status = AcpiPsGetNextNamepath (WalkState, &(WalkState->ParserState), Op, 1);
124         if (ACPI_FAILURE (Status))
125         {
126             return_ACPI_STATUS (Status);
127         }
128 
129         WalkState->ArgTypes = 0;
130         break;
131 
132     default:
133         /*
134          * Op is not a constant or string, append each argument to the Op
135          */
136         while (GET_CURRENT_ARG_TYPE (WalkState->ArgTypes) && !WalkState->ArgCount)
137         {
138             WalkState->Aml = WalkState->ParserState.Aml;
139 
140             Status = AcpiPsGetNextArg (WalkState, &(WalkState->ParserState),
141                         GET_CURRENT_ARG_TYPE (WalkState->ArgTypes), &Arg);
142             if (ACPI_FAILURE (Status))
143             {
144                 return_ACPI_STATUS (Status);
145             }
146 
147             if (Arg)
148             {
149                 AcpiPsAppendArg (Op, Arg);
150             }
151 
152             INCREMENT_ARG_LIST (WalkState->ArgTypes);
153         }
154 
155 
156         /*
157          * Handle executable code at "module-level". This refers to
158          * executable opcodes that appear outside of any control method.
159          */
160         if ((WalkState->PassNumber <= ACPI_IMODE_LOAD_PASS2) &&
161             ((WalkState->ParseFlags & ACPI_PARSE_DISASSEMBLE) == 0))
162         {
163             /*
164              * We want to skip If/Else/While constructs during Pass1 because we
165              * want to actually conditionally execute the code during Pass2.
166              *
167              * Except for disassembly, where we always want to walk the
168              * If/Else/While packages
169              */
170             switch (Op->Common.AmlOpcode)
171             {
172             case AML_IF_OP:
173             case AML_ELSE_OP:
174             case AML_WHILE_OP:
175                 /*
176                  * Currently supported module-level opcodes are:
177                  * IF/ELSE/WHILE. These appear to be the most common,
178                  * and easiest to support since they open an AML
179                  * package.
180                  */
181                 if (WalkState->PassNumber == ACPI_IMODE_LOAD_PASS1)
182                 {
183                     AcpiPsLinkModuleCode (Op->Common.Parent, AmlOpStart,
184                         (UINT32) (WalkState->ParserState.PkgEnd - AmlOpStart),
185                         WalkState->OwnerId);
186                 }
187 
188                 ACPI_DEBUG_PRINT ((ACPI_DB_PARSE,
189                     "Pass1: Skipping an If/Else/While body\n"));
190 
191                 /* Skip body of if/else/while in pass 1 */
192 
193                 WalkState->ParserState.Aml = WalkState->ParserState.PkgEnd;
194                 WalkState->ArgCount = 0;
195                 break;
196 
197             default:
198                 /*
199                  * Check for an unsupported executable opcode at module
200                  * level. We must be in PASS1, the parent must be a SCOPE,
201                  * The opcode class must be EXECUTE, and the opcode must
202                  * not be an argument to another opcode.
203                  */
204                 if ((WalkState->PassNumber == ACPI_IMODE_LOAD_PASS1) &&
205                     (Op->Common.Parent->Common.AmlOpcode == AML_SCOPE_OP))
206                 {
207                     OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
208                     if ((OpInfo->Class == AML_CLASS_EXECUTE) &&
209                         (!Arg))
210                     {
211                         ACPI_WARNING ((AE_INFO,
212                             "Unsupported module-level executable opcode "
213                             "0x%.2X at table offset 0x%.4X",
214                             Op->Common.AmlOpcode,
215                             (UINT32) (ACPI_PTR_DIFF (AmlOpStart,
216                                 WalkState->ParserState.AmlStart) +
217                                 sizeof (ACPI_TABLE_HEADER))));
218                     }
219                 }
220                 break;
221             }
222         }
223 
224         /* Special processing for certain opcodes */
225 
226         switch (Op->Common.AmlOpcode)
227         {
228         case AML_METHOD_OP:
229             /*
230              * Skip parsing of control method because we don't have enough
231              * info in the first pass to parse it correctly.
232              *
233              * Save the length and address of the body
234              */
235             Op->Named.Data = WalkState->ParserState.Aml;
236             Op->Named.Length = (UINT32)
237                 (WalkState->ParserState.PkgEnd - WalkState->ParserState.Aml);
238 
239             /* Skip body of method */
240 
241             WalkState->ParserState.Aml = WalkState->ParserState.PkgEnd;
242             WalkState->ArgCount = 0;
243             break;
244 
245         case AML_BUFFER_OP:
246         case AML_PACKAGE_OP:
247         case AML_VAR_PACKAGE_OP:
248 
249             if ((Op->Common.Parent) &&
250                 (Op->Common.Parent->Common.AmlOpcode == AML_NAME_OP) &&
251                 (WalkState->PassNumber <= ACPI_IMODE_LOAD_PASS2))
252             {
253                 /*
254                  * Skip parsing of Buffers and Packages because we don't have
255                  * enough info in the first pass to parse them correctly.
256                  */
257                 Op->Named.Data = AmlOpStart;
258                 Op->Named.Length = (UINT32)
259                     (WalkState->ParserState.PkgEnd - AmlOpStart);
260 
261                 /* Skip body */
262 
263                 WalkState->ParserState.Aml = WalkState->ParserState.PkgEnd;
264                 WalkState->ArgCount = 0;
265             }
266             break;
267 
268         case AML_WHILE_OP:
269 
270             if (WalkState->ControlState)
271             {
272                 WalkState->ControlState->Control.PackageEnd =
273                     WalkState->ParserState.PkgEnd;
274             }
275             break;
276 
277         default:
278 
279             /* No action for all other opcodes */
280 
281             break;
282         }
283 
284         break;
285     }
286 
287     return_ACPI_STATUS (AE_OK);
288 }
289 
290 
291 /*******************************************************************************
292  *
293  * FUNCTION:    AcpiPsLinkModuleCode
294  *
295  * PARAMETERS:  ParentOp            - Parent parser op
296  *              AmlStart            - Pointer to the AML
297  *              AmlLength           - Length of executable AML
298  *              OwnerId             - OwnerId of module level code
299  *
300  * RETURN:      None.
301  *
302  * DESCRIPTION: Wrap the module-level code with a method object and link the
303  *              object to the global list. Note, the mutex field of the method
304  *              object is used to link multiple module-level code objects.
305  *
306  ******************************************************************************/
307 
308 static void
309 AcpiPsLinkModuleCode (
310     ACPI_PARSE_OBJECT       *ParentOp,
311     UINT8                   *AmlStart,
312     UINT32                  AmlLength,
313     ACPI_OWNER_ID           OwnerId)
314 {
315     ACPI_OPERAND_OBJECT     *Prev;
316     ACPI_OPERAND_OBJECT     *Next;
317     ACPI_OPERAND_OBJECT     *MethodObj;
318     ACPI_NAMESPACE_NODE     *ParentNode;
319 
320 
321     ACPI_FUNCTION_TRACE (PsLinkModuleCode);
322 
323 
324     /* Get the tail of the list */
325 
326     Prev = Next = AcpiGbl_ModuleCodeList;
327     while (Next)
328     {
329         Prev = Next;
330         Next = Next->Method.Mutex;
331     }
332 
333     /*
334      * Insert the module level code into the list. Merge it if it is
335      * adjacent to the previous element.
336      */
337     if (!Prev ||
338        ((Prev->Method.AmlStart + Prev->Method.AmlLength) != AmlStart))
339     {
340         /* Create, initialize, and link a new temporary method object */
341 
342         MethodObj = AcpiUtCreateInternalObject (ACPI_TYPE_METHOD);
343         if (!MethodObj)
344         {
345             return_VOID;
346         }
347 
348         ACPI_DEBUG_PRINT ((ACPI_DB_PARSE,
349             "Create/Link new code block: %p\n", MethodObj));
350 
351         if (ParentOp->Common.Node)
352         {
353             ParentNode = ParentOp->Common.Node;
354         }
355         else
356         {
357             ParentNode = AcpiGbl_RootNode;
358         }
359 
360         MethodObj->Method.AmlStart = AmlStart;
361         MethodObj->Method.AmlLength = AmlLength;
362         MethodObj->Method.OwnerId = OwnerId;
363         MethodObj->Method.InfoFlags |= ACPI_METHOD_MODULE_LEVEL;
364 
365         /*
366          * Save the parent node in NextObject. This is cheating, but we
367          * don't want to expand the method object.
368          */
369         MethodObj->Method.NextObject =
370             ACPI_CAST_PTR (ACPI_OPERAND_OBJECT, ParentNode);
371 
372         if (!Prev)
373         {
374             AcpiGbl_ModuleCodeList = MethodObj;
375         }
376         else
377         {
378             Prev->Method.Mutex = MethodObj;
379         }
380     }
381     else
382     {
383         ACPI_DEBUG_PRINT ((ACPI_DB_PARSE,
384             "Appending to existing code block: %p\n", Prev));
385 
386         Prev->Method.AmlLength += AmlLength;
387     }
388 
389     return_VOID;
390 }
391 
392 /*******************************************************************************
393  *
394  * FUNCTION:    AcpiPsParseLoop
395  *
396  * PARAMETERS:  WalkState           - Current state
397  *
398  * RETURN:      Status
399  *
400  * DESCRIPTION: Parse AML (pointed to by the current parser state) and return
401  *              a tree of ops.
402  *
403  ******************************************************************************/
404 
405 ACPI_STATUS
406 AcpiPsParseLoop (
407     ACPI_WALK_STATE         *WalkState)
408 {
409     ACPI_STATUS             Status = AE_OK;
410     ACPI_PARSE_OBJECT       *Op = NULL;     /* current op */
411     ACPI_PARSE_STATE        *ParserState;
412     UINT8                   *AmlOpStart = NULL;
413 
414 
415     ACPI_FUNCTION_TRACE_PTR (PsParseLoop, WalkState);
416 
417 
418     if (WalkState->DescendingCallback == NULL)
419     {
420         return_ACPI_STATUS (AE_BAD_PARAMETER);
421     }
422 
423     ParserState = &WalkState->ParserState;
424     WalkState->ArgTypes = 0;
425 
426 #if (!defined (ACPI_NO_METHOD_EXECUTION) && !defined (ACPI_CONSTANT_EVAL_ONLY))
427 
428     if (WalkState->WalkType & ACPI_WALK_METHOD_RESTART)
429     {
430         /* We are restarting a preempted control method */
431 
432         if (AcpiPsHasCompletedScope (ParserState))
433         {
434             /*
435              * We must check if a predicate to an IF or WHILE statement
436              * was just completed
437              */
438             if ((ParserState->Scope->ParseScope.Op) &&
439                ((ParserState->Scope->ParseScope.Op->Common.AmlOpcode == AML_IF_OP) ||
440                 (ParserState->Scope->ParseScope.Op->Common.AmlOpcode == AML_WHILE_OP)) &&
441                 (WalkState->ControlState) &&
442                 (WalkState->ControlState->Common.State ==
443                     ACPI_CONTROL_PREDICATE_EXECUTING))
444             {
445                 /*
446                  * A predicate was just completed, get the value of the
447                  * predicate and branch based on that value
448                  */
449                 WalkState->Op = NULL;
450                 Status = AcpiDsGetPredicateValue (WalkState, ACPI_TO_POINTER (TRUE));
451                 if (ACPI_FAILURE (Status) &&
452                     ((Status & AE_CODE_MASK) != AE_CODE_CONTROL))
453                 {
454                     if (Status == AE_AML_NO_RETURN_VALUE)
455                     {
456                         ACPI_EXCEPTION ((AE_INFO, Status,
457                             "Invoked method did not return a value"));
458                     }
459 
460                     ACPI_EXCEPTION ((AE_INFO, Status, "GetPredicate Failed"));
461                     return_ACPI_STATUS (Status);
462                 }
463 
464                 Status = AcpiPsNextParseState (WalkState, Op, Status);
465             }
466 
467             AcpiPsPopScope (ParserState, &Op,
468                 &WalkState->ArgTypes, &WalkState->ArgCount);
469             ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Popped scope, Op=%p\n", Op));
470         }
471         else if (WalkState->PrevOp)
472         {
473             /* We were in the middle of an op */
474 
475             Op = WalkState->PrevOp;
476             WalkState->ArgTypes = WalkState->PrevArgTypes;
477         }
478     }
479 #endif
480 
481     /* Iterative parsing loop, while there is more AML to process: */
482 
483     while ((ParserState->Aml < ParserState->AmlEnd) || (Op))
484     {
485         AmlOpStart = ParserState->Aml;
486         if (!Op)
487         {
488             Status = AcpiPsCreateOp (WalkState, AmlOpStart, &Op);
489             if (ACPI_FAILURE (Status))
490             {
491                 if (Status == AE_CTRL_PARSE_CONTINUE)
492                 {
493                     continue;
494                 }
495 
496                 if (Status == AE_CTRL_PARSE_PENDING)
497                 {
498                     Status = AE_OK;
499                 }
500 
501                 if (Status == AE_CTRL_TERMINATE)
502                 {
503                     return_ACPI_STATUS (Status);
504                 }
505 
506                 Status = AcpiPsCompleteOp (WalkState, &Op, Status);
507                 if (ACPI_FAILURE (Status))
508                 {
509                     return_ACPI_STATUS (Status);
510                 }
511 
512                 continue;
513             }
514 
515             AcpiExStartTraceOpcode (Op, WalkState);
516         }
517 
518 
519         /*
520          * Start ArgCount at zero because we don't know if there are
521          * any args yet
522          */
523         WalkState->ArgCount  = 0;
524 
525         /* Are there any arguments that must be processed? */
526 
527         if (WalkState->ArgTypes)
528         {
529             /* Get arguments */
530 
531             Status = AcpiPsGetArguments (WalkState, AmlOpStart, Op);
532             if (ACPI_FAILURE (Status))
533             {
534                 Status = AcpiPsCompleteOp (WalkState, &Op, Status);
535                 if (ACPI_FAILURE (Status))
536                 {
537                     return_ACPI_STATUS (Status);
538                 }
539 
540                 continue;
541             }
542         }
543 
544         /* Check for arguments that need to be processed */
545 
546         if (WalkState->ArgCount)
547         {
548             /*
549              * There are arguments (complex ones), push Op and
550              * prepare for argument
551              */
552             Status = AcpiPsPushScope (ParserState, Op,
553                         WalkState->ArgTypes, WalkState->ArgCount);
554             if (ACPI_FAILURE (Status))
555             {
556                 Status = AcpiPsCompleteOp (WalkState, &Op, Status);
557                 if (ACPI_FAILURE (Status))
558                 {
559                     return_ACPI_STATUS (Status);
560                 }
561 
562                 continue;
563             }
564 
565             Op = NULL;
566             continue;
567         }
568 
569         /*
570          * All arguments have been processed -- Op is complete,
571          * prepare for next
572          */
573         WalkState->OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
574         if (WalkState->OpInfo->Flags & AML_NAMED)
575         {
576             if (Op->Common.AmlOpcode == AML_REGION_OP ||
577                 Op->Common.AmlOpcode == AML_DATA_REGION_OP)
578             {
579                 /*
580                  * Skip parsing of control method or opregion body,
581                  * because we don't have enough info in the first pass
582                  * to parse them correctly.
583                  *
584                  * Completed parsing an OpRegion declaration, we now
585                  * know the length.
586                  */
587                 Op->Named.Length = (UINT32) (ParserState->Aml - Op->Named.Data);
588             }
589         }
590 
591         if (WalkState->OpInfo->Flags & AML_CREATE)
592         {
593             /*
594              * Backup to beginning of CreateXXXfield declaration (1 for
595              * Opcode)
596              *
597              * BodyLength is unknown until we parse the body
598              */
599             Op->Named.Length = (UINT32) (ParserState->Aml - Op->Named.Data);
600         }
601 
602         if (Op->Common.AmlOpcode == AML_BANK_FIELD_OP)
603         {
604             /*
605              * Backup to beginning of BankField declaration
606              *
607              * BodyLength is unknown until we parse the body
608              */
609             Op->Named.Length = (UINT32) (ParserState->Aml - Op->Named.Data);
610         }
611 
612         /* This op complete, notify the dispatcher */
613 
614         if (WalkState->AscendingCallback != NULL)
615         {
616             WalkState->Op = Op;
617             WalkState->Opcode = Op->Common.AmlOpcode;
618 
619             Status = WalkState->AscendingCallback (WalkState);
620             Status = AcpiPsNextParseState (WalkState, Op, Status);
621             if (Status == AE_CTRL_PENDING)
622             {
623                 Status = AE_OK;
624             }
625         }
626 
627         Status = AcpiPsCompleteOp (WalkState, &Op, Status);
628         if (ACPI_FAILURE (Status))
629         {
630             return_ACPI_STATUS (Status);
631         }
632 
633     } /* while ParserState->Aml */
634 
635     Status = AcpiPsCompleteFinalOp (WalkState, Op, Status);
636     return_ACPI_STATUS (Status);
637 }
638