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