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