1 /******************************************************************************
2  *
3  * Module Name: aslanalyze.c - Support functions for parse tree walks
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2022, 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 MERCHANTABILITY AND FITNESS FOR
34  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41  * POSSIBILITY OF SUCH DAMAGES.
42  */
43 
44 #include "aslcompiler.h"
45 #include "aslcompiler.y.h"
46 #include "acnamesp.h"
47 #include <string.h>
48 
49 
50 #define _COMPONENT          ACPI_COMPILER
51         ACPI_MODULE_NAME    ("aslanalyze")
52 
53 
54 /* Local Prototypes */
55 
56 static ACPI_STATUS
57 ApDeviceSubtreeWalk (
58     ACPI_PARSE_OBJECT       *Op,
59     UINT32                  Level,
60     void                    *Context);
61 
62 
63 /*******************************************************************************
64  *
65  * FUNCTION:    AnIsInternalMethod
66  *
67  * PARAMETERS:  Op                  - Current op
68  *
69  * RETURN:      Boolean
70  *
71  * DESCRIPTION: Check for an internal control method.
72  *
73  ******************************************************************************/
74 
75 BOOLEAN
AnIsInternalMethod(ACPI_PARSE_OBJECT * Op)76 AnIsInternalMethod (
77     ACPI_PARSE_OBJECT       *Op)
78 {
79 
80     if ((!strcmp (Op->Asl.ExternalName, "\\_OSI")) ||
81         (!strcmp (Op->Asl.ExternalName, "_OSI")))
82     {
83         return (TRUE);
84     }
85 
86     return (FALSE);
87 }
88 
89 
90 /*******************************************************************************
91  *
92  * FUNCTION:    AnGetInternalMethodReturnType
93  *
94  * PARAMETERS:  Op                  - Current op
95  *
96  * RETURN:      Btype
97  *
98  * DESCRIPTION: Get the return type of an internal method
99  *
100  ******************************************************************************/
101 
102 UINT32
AnGetInternalMethodReturnType(ACPI_PARSE_OBJECT * Op)103 AnGetInternalMethodReturnType (
104     ACPI_PARSE_OBJECT       *Op)
105 {
106 
107     if ((!strcmp (Op->Asl.ExternalName, "\\_OSI")) ||
108         (!strcmp (Op->Asl.ExternalName, "_OSI")))
109     {
110         return (ACPI_BTYPE_STRING);
111     }
112 
113     return (0);
114 }
115 
116 
117 /*******************************************************************************
118  *
119  * FUNCTION:    AnCheckId
120  *
121  * PARAMETERS:  Op                  - Current parse op
122  *              Type                - HID or CID
123  *
124  * RETURN:      None
125  *
126  * DESCRIPTION: Perform various checks on _HID and _CID strings. Only limited
127  *              checks can be performed on _CID strings.
128  *
129  ******************************************************************************/
130 
131 void
AnCheckId(ACPI_PARSE_OBJECT * Op,ACPI_NAME Type)132 AnCheckId (
133     ACPI_PARSE_OBJECT       *Op,
134     ACPI_NAME               Type)
135 {
136     UINT32                  i;
137     ACPI_SIZE               Length;
138 
139 
140     /* Only care about string versions of _HID/_CID (integers are legal) */
141 
142     if (Op->Asl.ParseOpcode != PARSEOP_STRING_LITERAL)
143     {
144         return;
145     }
146 
147     /* For both _HID and _CID, the string must be non-null */
148 
149     Length = strlen (Op->Asl.Value.String);
150     if (!Length)
151     {
152         AslError (ASL_ERROR, ASL_MSG_NULL_STRING, Op, NULL);
153         return;
154     }
155 
156     /*
157      * One of the things we want to catch here is the use of a leading
158      * asterisk in the string -- an odd construct that certain platform
159      * manufacturers are fond of. Technically, a leading asterisk is OK
160      * for _CID, but a valid use of this has not been seen.
161      */
162     if (*Op->Asl.Value.String == '*')
163     {
164         AslError (ASL_ERROR, ASL_MSG_LEADING_ASTERISK,
165             Op, Op->Asl.Value.String);
166         return;
167     }
168 
169     /* _CID strings are bus-specific, no more checks can be performed */
170 
171     if (Type == ASL_TYPE_CID)
172     {
173         return;
174     }
175 
176     /* For _HID, all characters must be alphanumeric */
177 
178     for (i = 0; Op->Asl.Value.String[i]; i++)
179     {
180         if (!isalnum ((int) Op->Asl.Value.String[i]))
181         {
182             AslError (ASL_ERROR, ASL_MSG_ALPHANUMERIC_STRING,
183                 Op, Op->Asl.Value.String);
184             return;
185         }
186     }
187 
188     /*
189      * _HID String must be one of these forms:
190      *
191      * "AAA####"    A is an uppercase letter and # is a hex digit
192      * "ACPI####"   # is a hex digit
193      * "NNNN####"   N is an uppercase letter or decimal digit (0-9)
194      *              # is a hex digit (ACPI 5.0)
195      */
196     if ((Length < 7) || (Length > 8))
197     {
198         AslError (ASL_ERROR, ASL_MSG_HID_LENGTH,
199             Op, Op->Asl.Value.String);
200         return;
201     }
202 
203     /* _HID Length is valid (7 or 8), now check prefix (first 3 or 4 chars) */
204 
205     if (Length == 7)
206     {
207         /* AAA####: Ensure the alphabetic prefix is all uppercase */
208 
209         for (i = 0; i < 3; i++)
210         {
211             if (!isupper ((int) Op->Asl.Value.String[i]))
212             {
213                 AslError (ASL_ERROR, ASL_MSG_UPPER_CASE,
214                     Op, &Op->Asl.Value.String[i]);
215                 return;
216             }
217         }
218     }
219     else /* Length == 8 */
220     {
221         /*
222          * ACPI#### or NNNN####:
223          * Ensure the prefix contains only uppercase alpha or decimal digits
224          */
225         for (i = 0; i < 4; i++)
226         {
227             if (!isupper ((int) Op->Asl.Value.String[i]) &&
228                 !isdigit ((int) Op->Asl.Value.String[i]))
229             {
230                 AslError (ASL_ERROR, ASL_MSG_HID_PREFIX,
231                     Op, &Op->Asl.Value.String[i]);
232                 return;
233             }
234         }
235     }
236 
237     /* Remaining characters (suffix) must be hex digits */
238 
239     for (; i < Length; i++)
240     {
241         if (!isxdigit ((int) Op->Asl.Value.String[i]))
242         {
243             AslError (ASL_ERROR, ASL_MSG_HID_SUFFIX,
244                 Op, &Op->Asl.Value.String[i]);
245             break;
246         }
247     }
248 }
249 
250 
251 /*******************************************************************************
252  *
253  * FUNCTION:    AnLastStatementIsReturn
254  *
255  * PARAMETERS:  Op                  - A method parse node
256  *
257  * RETURN:      TRUE if last statement is an ASL RETURN. False otherwise
258  *
259  * DESCRIPTION: Walk down the list of top level statements within a method
260  *              to find the last one. Check if that last statement is in
261  *              fact a RETURN statement.
262  *
263  ******************************************************************************/
264 
265 BOOLEAN
AnLastStatementIsReturn(ACPI_PARSE_OBJECT * Op)266 AnLastStatementIsReturn (
267     ACPI_PARSE_OBJECT       *Op)
268 {
269     ACPI_PARSE_OBJECT       *Next;
270 
271 
272     /* Check if last statement is a return */
273 
274     Next = ASL_GET_CHILD_NODE (Op);
275     while (Next)
276     {
277         if ((!Next->Asl.Next) &&
278             (Next->Asl.ParseOpcode == PARSEOP_RETURN))
279         {
280             return (TRUE);
281         }
282 
283         Next = ASL_GET_PEER_NODE (Next);
284     }
285 
286     return (FALSE);
287 }
288 
289 
290 /*******************************************************************************
291  *
292  * FUNCTION:    AnCheckMethodReturnValue
293  *
294  * PARAMETERS:  Op                  - Parent
295  *              OpInfo              - Parent info
296  *              ArgOp               - Method invocation op
297  *              RequiredBtypes      - What caller requires
298  *              ThisNodeBtype       - What this node returns (if anything)
299  *
300  * RETURN:      None
301  *
302  * DESCRIPTION: Check a method invocation for 1) A return value and if it does
303  *              in fact return a value, 2) check the type of the return value.
304  *
305  ******************************************************************************/
306 
307 void
AnCheckMethodReturnValue(ACPI_PARSE_OBJECT * Op,const ACPI_OPCODE_INFO * OpInfo,ACPI_PARSE_OBJECT * ArgOp,UINT32 RequiredBtypes,UINT32 ThisNodeBtype)308 AnCheckMethodReturnValue (
309     ACPI_PARSE_OBJECT       *Op,
310     const ACPI_OPCODE_INFO  *OpInfo,
311     ACPI_PARSE_OBJECT       *ArgOp,
312     UINT32                  RequiredBtypes,
313     UINT32                  ThisNodeBtype)
314 {
315     ACPI_PARSE_OBJECT       *OwningOp;
316     ACPI_NAMESPACE_NODE     *Node;
317     char                    *ExternalPath;
318 
319 
320     Node = ArgOp->Asl.Node;
321 
322     if (!Node)
323     {
324         /* No error message, this can happen and is OK */
325 
326         return;
327     }
328 
329     /* Examine the parent op of this method */
330 
331     OwningOp = Node->Op;
332     ExternalPath = AcpiNsGetNormalizedPathname (Node, TRUE);
333 
334     if (OwningOp->Asl.CompileFlags & OP_METHOD_NO_RETVAL)
335     {
336         /* Method NEVER returns a value */
337 
338         AslError (ASL_ERROR, ASL_MSG_NO_RETVAL, Op, ExternalPath);
339     }
340     else if (OwningOp->Asl.CompileFlags & OP_METHOD_SOME_NO_RETVAL)
341     {
342         /* Method SOMETIMES returns a value, SOMETIMES not */
343 
344         AslError (ASL_WARNING, ASL_MSG_SOME_NO_RETVAL, Op, ExternalPath);
345     }
346     else if (!(ThisNodeBtype & RequiredBtypes))
347     {
348         /* Method returns a value, but the type is wrong */
349 
350         AnFormatBtype (AslGbl_StringBuffer, ThisNodeBtype);
351         AnFormatBtype (AslGbl_StringBuffer2, RequiredBtypes);
352 
353         /*
354          * The case where the method does not return any value at all
355          * was already handled in the namespace cross reference
356          * -- Only issue an error if the method in fact returns a value,
357          * but it is of the wrong type
358          */
359         if (ThisNodeBtype != 0)
360         {
361             snprintf (AslGbl_MsgBuffer, sizeof(AslGbl_MsgBuffer),
362                 "Method returns [%s], %s operator requires [%s]",
363                 AslGbl_StringBuffer, OpInfo->Name, AslGbl_StringBuffer2);
364 
365             AslError (ASL_WARNING, ASL_MSG_INVALID_TYPE, ArgOp, AslGbl_MsgBuffer);
366         }
367     }
368 
369     if (ExternalPath)
370     {
371         ACPI_FREE (ExternalPath);
372     }
373 }
374 
375 
376 /*******************************************************************************
377  *
378  * FUNCTION:    AnIsResultUsed
379  *
380  * PARAMETERS:  Op                  - Parent op for the operator
381  *
382  * RETURN:      TRUE if result from this operation is actually consumed
383  *
384  * DESCRIPTION: Determine if the function result value from an operator is
385  *              used.
386  *
387  ******************************************************************************/
388 
389 BOOLEAN
AnIsResultUsed(ACPI_PARSE_OBJECT * Op)390 AnIsResultUsed (
391     ACPI_PARSE_OBJECT       *Op)
392 {
393     ACPI_PARSE_OBJECT       *Parent;
394 
395 
396     switch (Op->Asl.ParseOpcode)
397     {
398     case PARSEOP_INCREMENT:
399     case PARSEOP_DECREMENT:
400 
401         /* These are standalone operators, no return value */
402 
403         return (TRUE);
404 
405     default:
406 
407         break;
408     }
409 
410     /* Examine parent to determine if the return value is used */
411 
412     Parent = Op->Asl.Parent;
413     switch (Parent->Asl.ParseOpcode)
414     {
415     /* If/While - check if the operator is the predicate */
416 
417     case PARSEOP_IF:
418     case PARSEOP_WHILE:
419 
420         /* First child is the predicate */
421 
422         if (Parent->Asl.Child == Op)
423         {
424             return (TRUE);
425         }
426 
427         return (FALSE);
428 
429     /* Not used if one of these is the parent */
430 
431     case PARSEOP_METHOD:
432     case PARSEOP_DEFINITION_BLOCK:
433     case PARSEOP_ELSE:
434 
435         return (FALSE);
436 
437     default:
438 
439         /* Any other type of parent means that the result is used */
440 
441         return (TRUE);
442     }
443 }
444 
445 
446 /*******************************************************************************
447  *
448  * FUNCTION:    ApCheckForGpeNameConflict
449  *
450  * PARAMETERS:  Op                  - Current parse op
451  *
452  * RETURN:      None
453  *
454  * DESCRIPTION: Check for a conflict between GPE names within this scope.
455  *              Conflict means two GPE names with the same GPE number, but
456  *              different types -- such as _L1C and _E1C.
457  *
458  ******************************************************************************/
459 
460 void
ApCheckForGpeNameConflict(ACPI_PARSE_OBJECT * Op)461 ApCheckForGpeNameConflict (
462     ACPI_PARSE_OBJECT       *Op)
463 {
464     ACPI_PARSE_OBJECT       *NextOp;
465     UINT32                  GpeNumber;
466     char                    Name[ACPI_NAMESEG_SIZE + 1];
467     char                    Target[ACPI_NAMESEG_SIZE];
468 
469 
470     /* Need a null-terminated string version of NameSeg */
471 
472     ACPI_MOVE_32_TO_32 (Name, Op->Asl.NameSeg);
473     Name[ACPI_NAMESEG_SIZE] = 0;
474 
475     /*
476      * For a GPE method:
477      * 1st char must be underscore
478      * 2nd char must be L or E
479      * 3rd/4th chars must be a hex number
480      */
481     if ((Name[0] != '_') ||
482        ((Name[1] != 'L') && (Name[1] != 'E')))
483     {
484         return;
485     }
486 
487     /* Verify 3rd/4th chars are a valid hex value */
488 
489     GpeNumber = strtoul (&Name[2], NULL, 16);
490     if (GpeNumber == ACPI_UINT32_MAX)
491     {
492         return;
493     }
494 
495     /*
496      * We are now sure we have an _Lxx or _Exx.
497      * Create the target name that would cause collision (Flip E/L)
498      */
499     ACPI_MOVE_32_TO_32 (Target, Name);
500 
501     /* Inject opposite letter ("L" versus "E") */
502 
503     if (Name[1] == 'L')
504     {
505         Target[1] = 'E';
506     }
507     else /* Name[1] == 'E' */
508     {
509         Target[1] = 'L';
510     }
511 
512     /* Search all peers (objects within this scope) for target match */
513 
514     NextOp = Op->Asl.Next;
515     while (NextOp)
516     {
517         /*
518          * We mostly care about methods, but check Name() constructs also,
519          * even though they will get another error for not being a method.
520          * All GPE names must be defined as control methods.
521          */
522         if ((NextOp->Asl.ParseOpcode == PARSEOP_METHOD) ||
523             (NextOp->Asl.ParseOpcode == PARSEOP_NAME))
524         {
525             if (ACPI_COMPARE_NAMESEG (Target, NextOp->Asl.NameSeg))
526             {
527                 /* Found both _Exy and _Lxy in the same scope, error */
528 
529                 AslError (ASL_ERROR, ASL_MSG_GPE_NAME_CONFLICT, NextOp,
530                     Name);
531                 return;
532             }
533         }
534 
535         NextOp = NextOp->Asl.Next;
536     }
537 
538     /* OK, no conflict found */
539 
540     return;
541 }
542 
543 
544 /*******************************************************************************
545  *
546  * FUNCTION:    ApCheckRegMethod
547  *
548  * PARAMETERS:  Op                  - Current parse op
549  *
550  * RETURN:      None
551  *
552  * DESCRIPTION: Ensure that a _REG method has a corresponding Operation
553  *              Region declaration within the same scope. Note: _REG is defined
554  *              to have two arguments and must therefore be defined as a
555  *              control method.
556  *
557  ******************************************************************************/
558 
559 void
ApCheckRegMethod(ACPI_PARSE_OBJECT * Op)560 ApCheckRegMethod (
561     ACPI_PARSE_OBJECT       *Op)
562 {
563     ACPI_PARSE_OBJECT       *Next;
564     ACPI_PARSE_OBJECT       *Parent;
565 
566 
567     /* We are only interested in _REG methods */
568 
569     if (!ACPI_COMPARE_NAMESEG (METHOD_NAME__REG, &Op->Asl.NameSeg))
570     {
571         return;
572     }
573 
574     /* Get the start of the current scope */
575 
576     Parent = Op->Asl.Parent;
577     Next = Parent->Asl.Child;
578 
579     /* Search entire scope for an operation region declaration */
580 
581     while (Next)
582     {
583         if (Next->Asl.ParseOpcode == PARSEOP_OPERATIONREGION)
584         {
585             return; /* Found region, OK */
586         }
587 
588         Next = Next->Asl.Next;
589     }
590 
591     /* No region found, issue warning */
592 
593     AslError (ASL_WARNING, ASL_MSG_NO_REGION, Op, NULL);
594 }
595 
596 
597 /*******************************************************************************
598  *
599  * FUNCTION:    ApFindNameInDeviceTree
600  *
601  * PARAMETERS:  Name                - Name to search for
602  *              Op                  - Current parse op
603  *
604  * RETURN:      TRUE if name found in the same scope as Op.
605  *
606  * DESCRIPTION: Determine if a name appears in the same scope as Op, as either
607  *              a Method() or a Name(). "Same scope" can mean under an If or
608  *              Else statement.
609  *
610  * NOTE: Detects _HID/_ADR in this type of construct (legal in ACPI 6.1+)
611  *
612  * Scope (\_SB.PCI0)
613  * {
614  *     Device (I2C0)
615  *     {
616  *         If (SMD0 != 4) {
617  *             Name (_HID, "INT3442")
618  *         } Else {
619  *             Name (_ADR, 0x400)
620  *         }
621  *     }
622  * }
623  ******************************************************************************/
624 
625 BOOLEAN
ApFindNameInDeviceTree(char * Name,ACPI_PARSE_OBJECT * Op)626 ApFindNameInDeviceTree (
627     char                    *Name,
628     ACPI_PARSE_OBJECT       *Op)
629 {
630     ACPI_STATUS             Status;
631 
632 
633     Status = TrWalkParseTree (Op, ASL_WALK_VISIT_DOWNWARD,
634         ApDeviceSubtreeWalk, NULL, Name);
635 
636     if (Status == AE_CTRL_TRUE)
637     {
638         return (TRUE);  /* Found a match */
639     }
640 
641     return (FALSE);
642 }
643 
644 
645 /* Callback function for interface above */
646 
647 static ACPI_STATUS
ApDeviceSubtreeWalk(ACPI_PARSE_OBJECT * Op,UINT32 Level,void * Context)648 ApDeviceSubtreeWalk (
649     ACPI_PARSE_OBJECT       *Op,
650     UINT32                  Level,
651     void                    *Context)
652 {
653     char                    *Name = ACPI_CAST_PTR (char, Context);
654 
655 
656     switch (Op->Asl.ParseOpcode)
657     {
658     case PARSEOP_DEVICE:
659 
660         /* Level 0 is the starting device, ignore it */
661 
662         if (Level > 0)
663         {
664             /* Ignore sub-devices */
665 
666             return (AE_CTRL_DEPTH);
667         }
668         break;
669 
670     case PARSEOP_NAME:
671     case PARSEOP_METHOD:
672 
673         /* These are what we are looking for */
674 
675         if (ACPI_COMPARE_NAMESEG (Name, Op->Asl.NameSeg))
676         {
677             return (AE_CTRL_TRUE);
678         }
679         return (AE_CTRL_DEPTH);
680 
681     case PARSEOP_SCOPE:
682     case PARSEOP_FIELD:
683     case PARSEOP_OPERATIONREGION:
684 
685         /*
686          * We want to ignore these, because either they can be large
687          * subtrees or open a scope to somewhere else.
688          */
689         return (AE_CTRL_DEPTH);
690 
691     default:
692         break;
693     }
694 
695     return (AE_OK);
696 }
697 
698 
699 /*******************************************************************************
700  *
701  * FUNCTION:    ApFindNameInScope
702  *
703  * PARAMETERS:  Name                - Name to search for
704  *              Op                  - Current parse op
705  *
706  * RETURN:      TRUE if name found in the same scope as Op.
707  *
708  * DESCRIPTION: Determine if a name appears in the same scope as Op, as either
709  *              a Method() or a Name().
710  *
711  ******************************************************************************/
712 
713 BOOLEAN
ApFindNameInScope(char * Name,ACPI_PARSE_OBJECT * Op)714 ApFindNameInScope (
715     char                    *Name,
716     ACPI_PARSE_OBJECT       *Op)
717 {
718     ACPI_PARSE_OBJECT       *Next;
719     ACPI_PARSE_OBJECT       *Parent;
720 
721 
722     /* Get the start of the current scope */
723 
724     Parent = Op->Asl.Parent;
725     Next = Parent->Asl.Child;
726 
727     /* Search entire scope for a match to the name */
728 
729     while (Next)
730     {
731         if ((Next->Asl.ParseOpcode == PARSEOP_METHOD) ||
732             (Next->Asl.ParseOpcode == PARSEOP_NAME))
733         {
734             if (ACPI_COMPARE_NAMESEG (Name, Next->Asl.NameSeg))
735             {
736                 return (TRUE);
737             }
738         }
739 
740         Next = Next->Asl.Next;
741     }
742 
743     return (FALSE);
744 }
745