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