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