xref: /freebsd/sys/contrib/dev/acpica/compiler/aslmethod.c (revision b7579f77d18196a58ff700756c84dc9a302a7f67)
1 /******************************************************************************
2  *
3  * Module Name: aslmethod.c - Control method analysis walk
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2013, 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 #include <contrib/dev/acpica/compiler/aslcompiler.h>
46 #include "aslcompiler.y.h"
47 
48 
49 #define _COMPONENT          ACPI_COMPILER
50         ACPI_MODULE_NAME    ("aslmethod")
51 
52 
53 /*******************************************************************************
54  *
55  * FUNCTION:    MtMethodAnalysisWalkBegin
56  *
57  * PARAMETERS:  ASL_WALK_CALLBACK
58  *
59  * RETURN:      Status
60  *
61  * DESCRIPTION: Descending callback for the analysis walk. Check methods for:
62  *              1) Initialized local variables
63  *              2) Valid arguments
64  *              3) Return types
65  *
66  ******************************************************************************/
67 
68 ACPI_STATUS
69 MtMethodAnalysisWalkBegin (
70     ACPI_PARSE_OBJECT       *Op,
71     UINT32                  Level,
72     void                    *Context)
73 {
74     ASL_ANALYSIS_WALK_INFO  *WalkInfo = (ASL_ANALYSIS_WALK_INFO *) Context;
75     ASL_METHOD_INFO         *MethodInfo = WalkInfo->MethodStack;
76     ACPI_PARSE_OBJECT       *Next;
77     UINT32                  RegisterNumber;
78     UINT32                  i;
79     char                    LocalName[] = "Local0";
80     char                    ArgName[] = "Arg0";
81     ACPI_PARSE_OBJECT       *ArgNode;
82     ACPI_PARSE_OBJECT       *NextType;
83     ACPI_PARSE_OBJECT       *NextParamType;
84     UINT8                   ActualArgs = 0;
85 
86 
87     switch (Op->Asl.ParseOpcode)
88     {
89     case PARSEOP_METHOD:
90 
91         TotalMethods++;
92 
93         /* Create and init method info */
94 
95         MethodInfo       = UtLocalCalloc (sizeof (ASL_METHOD_INFO));
96         MethodInfo->Next = WalkInfo->MethodStack;
97         MethodInfo->Op = Op;
98 
99         WalkInfo->MethodStack = MethodInfo;
100 
101         /* Get the name node, ignored here */
102 
103         Next = Op->Asl.Child;
104 
105         /* Get the NumArguments node */
106 
107         Next = Next->Asl.Next;
108         MethodInfo->NumArguments = (UINT8)
109             (((UINT8) Next->Asl.Value.Integer) & 0x07);
110 
111         /* Get the SerializeRule and SyncLevel nodes, ignored here */
112 
113         Next = Next->Asl.Next;
114         Next = Next->Asl.Next;
115         ArgNode = Next;
116 
117         /* Get the ReturnType node */
118 
119         Next = Next->Asl.Next;
120 
121         NextType = Next->Asl.Child;
122         while (NextType)
123         {
124             /* Get and map each of the ReturnTypes */
125 
126             MethodInfo->ValidReturnTypes |= AnMapObjTypeToBtype (NextType);
127             NextType->Asl.ParseOpcode = PARSEOP_DEFAULT_ARG;
128             NextType = NextType->Asl.Next;
129         }
130 
131         /* Get the ParameterType node */
132 
133         Next = Next->Asl.Next;
134 
135         NextType = Next->Asl.Child;
136         while (NextType)
137         {
138             if (NextType->Asl.ParseOpcode == PARSEOP_DEFAULT_ARG)
139             {
140                 NextParamType = NextType->Asl.Child;
141                 while (NextParamType)
142                 {
143                     MethodInfo->ValidArgTypes[ActualArgs] |= AnMapObjTypeToBtype (NextParamType);
144                     NextParamType->Asl.ParseOpcode = PARSEOP_DEFAULT_ARG;
145                     NextParamType = NextParamType->Asl.Next;
146                 }
147             }
148             else
149             {
150                 MethodInfo->ValidArgTypes[ActualArgs] =
151                     AnMapObjTypeToBtype (NextType);
152                 NextType->Asl.ParseOpcode = PARSEOP_DEFAULT_ARG;
153                 ActualArgs++;
154             }
155 
156             NextType = NextType->Asl.Next;
157         }
158 
159         if ((MethodInfo->NumArguments) &&
160             (MethodInfo->NumArguments != ActualArgs))
161         {
162             /* error: Param list did not match number of args */
163         }
164 
165         /* Allow numarguments == 0 for Function() */
166 
167         if ((!MethodInfo->NumArguments) && (ActualArgs))
168         {
169             MethodInfo->NumArguments = ActualArgs;
170             ArgNode->Asl.Value.Integer |= ActualArgs;
171         }
172 
173         /*
174          * Actual arguments are initialized at method entry.
175          * All other ArgX "registers" can be used as locals, so we
176          * track their initialization.
177          */
178         for (i = 0; i < MethodInfo->NumArguments; i++)
179         {
180             MethodInfo->ArgInitialized[i] = TRUE;
181         }
182         break;
183 
184 
185     case PARSEOP_METHODCALL:
186 
187         if (MethodInfo &&
188            (Op->Asl.Node == MethodInfo->Op->Asl.Node))
189         {
190             AslError (ASL_REMARK, ASL_MSG_RECURSION, Op, Op->Asl.ExternalName);
191         }
192         break;
193 
194 
195     case PARSEOP_LOCAL0:
196     case PARSEOP_LOCAL1:
197     case PARSEOP_LOCAL2:
198     case PARSEOP_LOCAL3:
199     case PARSEOP_LOCAL4:
200     case PARSEOP_LOCAL5:
201     case PARSEOP_LOCAL6:
202     case PARSEOP_LOCAL7:
203 
204         if (!MethodInfo)
205         {
206             /*
207              * Local was used outside a control method, or there was an error
208              * in the method declaration.
209              */
210             AslError (ASL_REMARK, ASL_MSG_LOCAL_OUTSIDE_METHOD, Op, Op->Asl.ExternalName);
211             return (AE_ERROR);
212         }
213 
214         RegisterNumber = (Op->Asl.AmlOpcode & 0x000F);
215 
216         /*
217          * If the local is being used as a target, mark the local
218          * initialized
219          */
220         if (Op->Asl.CompileFlags & NODE_IS_TARGET)
221         {
222             MethodInfo->LocalInitialized[RegisterNumber] = TRUE;
223         }
224 
225         /*
226          * Otherwise, this is a reference, check if the local
227          * has been previously initialized.
228          *
229          * The only operator that accepts an uninitialized value is ObjectType()
230          */
231         else if ((!MethodInfo->LocalInitialized[RegisterNumber]) &&
232                  (Op->Asl.Parent->Asl.ParseOpcode != PARSEOP_OBJECTTYPE))
233         {
234             LocalName[strlen (LocalName) -1] = (char) (RegisterNumber + 0x30);
235             AslError (ASL_ERROR, ASL_MSG_LOCAL_INIT, Op, LocalName);
236         }
237         break;
238 
239 
240     case PARSEOP_ARG0:
241     case PARSEOP_ARG1:
242     case PARSEOP_ARG2:
243     case PARSEOP_ARG3:
244     case PARSEOP_ARG4:
245     case PARSEOP_ARG5:
246     case PARSEOP_ARG6:
247 
248         if (!MethodInfo)
249         {
250             /*
251              * Arg was used outside a control method, or there was an error
252              * in the method declaration.
253              */
254             AslError (ASL_REMARK, ASL_MSG_LOCAL_OUTSIDE_METHOD, Op, Op->Asl.ExternalName);
255             return (AE_ERROR);
256         }
257 
258         RegisterNumber = (Op->Asl.AmlOpcode & 0x000F) - 8;
259         ArgName[strlen (ArgName) -1] = (char) (RegisterNumber + 0x30);
260 
261         /*
262          * If the Arg is being used as a target, mark the local
263          * initialized
264          */
265         if (Op->Asl.CompileFlags & NODE_IS_TARGET)
266         {
267             MethodInfo->ArgInitialized[RegisterNumber] = TRUE;
268         }
269 
270         /*
271          * Otherwise, this is a reference, check if the Arg
272          * has been previously initialized.
273          *
274          * The only operator that accepts an uninitialized value is ObjectType()
275          */
276         else if ((!MethodInfo->ArgInitialized[RegisterNumber]) &&
277                  (Op->Asl.Parent->Asl.ParseOpcode != PARSEOP_OBJECTTYPE))
278         {
279             AslError (ASL_ERROR, ASL_MSG_ARG_INIT, Op, ArgName);
280         }
281 
282         /* Flag this arg if it is not a "real" argument to the method */
283 
284         if (RegisterNumber >= MethodInfo->NumArguments)
285         {
286             AslError (ASL_REMARK, ASL_MSG_NOT_PARAMETER, Op, ArgName);
287         }
288         break;
289 
290 
291     case PARSEOP_RETURN:
292 
293         if (!MethodInfo)
294         {
295             /*
296              * Probably was an error in the method declaration,
297              * no additional error here
298              */
299             ACPI_WARNING ((AE_INFO, "%p, No parent method", Op));
300             return (AE_ERROR);
301         }
302 
303         /*
304          * A child indicates a possible return value. A simple Return or
305          * Return() is marked with NODE_IS_NULL_RETURN by the parser so
306          * that it is not counted as a "real" return-with-value, although
307          * the AML code that is actually emitted is Return(0). The AML
308          * definition of Return has a required parameter, so we are
309          * forced to convert a null return to Return(0).
310          */
311         if ((Op->Asl.Child) &&
312             (Op->Asl.Child->Asl.ParseOpcode != PARSEOP_DEFAULT_ARG) &&
313             (!(Op->Asl.Child->Asl.CompileFlags & NODE_IS_NULL_RETURN)))
314         {
315             MethodInfo->NumReturnWithValue++;
316         }
317         else
318         {
319             MethodInfo->NumReturnNoValue++;
320         }
321         break;
322 
323 
324     case PARSEOP_BREAK:
325     case PARSEOP_CONTINUE:
326 
327         Next = Op->Asl.Parent;
328         while (Next)
329         {
330             if (Next->Asl.ParseOpcode == PARSEOP_WHILE)
331             {
332                 break;
333             }
334             Next = Next->Asl.Parent;
335         }
336 
337         if (!Next)
338         {
339             AslError (ASL_ERROR, ASL_MSG_NO_WHILE, Op, NULL);
340         }
341         break;
342 
343 
344     case PARSEOP_STALL:
345 
346         /* We can range check if the argument is an integer */
347 
348         if ((Op->Asl.Child->Asl.ParseOpcode == PARSEOP_INTEGER) &&
349             (Op->Asl.Child->Asl.Value.Integer > ACPI_UINT8_MAX))
350         {
351             AslError (ASL_ERROR, ASL_MSG_INVALID_TIME, Op, NULL);
352         }
353         break;
354 
355 
356     case PARSEOP_DEVICE:
357     case PARSEOP_EVENT:
358     case PARSEOP_MUTEX:
359     case PARSEOP_OPERATIONREGION:
360     case PARSEOP_POWERRESOURCE:
361     case PARSEOP_PROCESSOR:
362     case PARSEOP_THERMALZONE:
363 
364         /*
365          * The first operand is a name to be created in the namespace.
366          * Check against the reserved list.
367          */
368         i = ApCheckForPredefinedName (Op, Op->Asl.NameSeg);
369         if (i < ACPI_VALID_RESERVED_NAME_MAX)
370         {
371             AslError (ASL_ERROR, ASL_MSG_RESERVED_USE, Op, Op->Asl.ExternalName);
372         }
373         break;
374 
375 
376     case PARSEOP_NAME:
377 
378         /* Typecheck any predefined names statically defined with Name() */
379 
380         ApCheckForPredefinedObject (Op, Op->Asl.NameSeg);
381 
382         /* Special typechecking for _HID */
383 
384         if (!ACPI_STRCMP (METHOD_NAME__HID, Op->Asl.NameSeg))
385         {
386             Next = Op->Asl.Child->Asl.Next;
387             AnCheckId (Next, ASL_TYPE_HID);
388         }
389 
390         /* Special typechecking for _CID */
391 
392         else if (!ACPI_STRCMP (METHOD_NAME__CID, Op->Asl.NameSeg))
393         {
394             Next = Op->Asl.Child->Asl.Next;
395 
396             if ((Next->Asl.ParseOpcode == PARSEOP_PACKAGE) ||
397                 (Next->Asl.ParseOpcode == PARSEOP_VAR_PACKAGE))
398             {
399                 Next = Next->Asl.Child;
400                 while (Next)
401                 {
402                     AnCheckId (Next, ASL_TYPE_CID);
403                     Next = Next->Asl.Next;
404                 }
405             }
406             else
407             {
408                 AnCheckId (Next, ASL_TYPE_CID);
409             }
410         }
411         break;
412 
413 
414     default:
415         break;
416     }
417 
418     return (AE_OK);
419 }
420 
421 
422 /*******************************************************************************
423  *
424  * FUNCTION:    MtMethodAnalysisWalkEnd
425  *
426  * PARAMETERS:  ASL_WALK_CALLBACK
427  *
428  * RETURN:      Status
429  *
430  * DESCRIPTION: Ascending callback for analysis walk. Complete method
431  *              return analysis.
432  *
433  ******************************************************************************/
434 
435 ACPI_STATUS
436 MtMethodAnalysisWalkEnd (
437     ACPI_PARSE_OBJECT       *Op,
438     UINT32                  Level,
439     void                    *Context)
440 {
441     ASL_ANALYSIS_WALK_INFO  *WalkInfo = (ASL_ANALYSIS_WALK_INFO *) Context;
442     ASL_METHOD_INFO         *MethodInfo = WalkInfo->MethodStack;
443 
444 
445     switch (Op->Asl.ParseOpcode)
446     {
447     case PARSEOP_METHOD:
448     case PARSEOP_RETURN:
449         if (!MethodInfo)
450         {
451             printf ("No method info for method! [%s]\n", Op->Asl.Namepath);
452             AslError (ASL_ERROR, ASL_MSG_COMPILER_INTERNAL, Op,
453                 "No method info for this method");
454 
455             CmCleanupAndExit ();
456             return (AE_AML_INTERNAL);
457         }
458         break;
459 
460     default:
461         break;
462     }
463 
464     switch (Op->Asl.ParseOpcode)
465     {
466     case PARSEOP_METHOD:
467 
468         WalkInfo->MethodStack = MethodInfo->Next;
469 
470         /*
471          * Check if there is no return statement at the end of the
472          * method AND we can actually get there -- i.e., the execution
473          * of the method can possibly terminate without a return statement.
474          */
475         if ((!AnLastStatementIsReturn (Op)) &&
476             (!(Op->Asl.CompileFlags & NODE_HAS_NO_EXIT)))
477         {
478             /*
479              * No return statement, and execution can possibly exit
480              * via this path. This is equivalent to Return ()
481              */
482             MethodInfo->NumReturnNoValue++;
483         }
484 
485         /*
486          * Check for case where some return statements have a return value
487          * and some do not. Exit without a return statement is a return with
488          * no value
489          */
490         if (MethodInfo->NumReturnNoValue &&
491             MethodInfo->NumReturnWithValue)
492         {
493             AslError (ASL_WARNING, ASL_MSG_RETURN_TYPES, Op,
494                 Op->Asl.ExternalName);
495         }
496 
497         /*
498          * If there are any RETURN() statements with no value, or there is a
499          * control path that allows the method to exit without a return value,
500          * we mark the method as a method that does not return a value. This
501          * knowledge can be used to check method invocations that expect a
502          * returned value.
503          */
504         if (MethodInfo->NumReturnNoValue)
505         {
506             if (MethodInfo->NumReturnWithValue)
507             {
508                 Op->Asl.CompileFlags |= NODE_METHOD_SOME_NO_RETVAL;
509             }
510             else
511             {
512                 Op->Asl.CompileFlags |= NODE_METHOD_NO_RETVAL;
513             }
514         }
515 
516         /*
517          * Check predefined method names for correct return behavior
518          * and correct number of arguments. Also, some special checks
519          * For GPE and _REG methods.
520          */
521         if (ApCheckForPredefinedMethod (Op, MethodInfo))
522         {
523             /* Special check for two names like _L01 and _E01 in same scope */
524 
525             ApCheckForGpeNameConflict (Op);
526 
527             /*
528              * Special check for _REG: Must have an operation region definition
529              * within the same scope!
530              */
531             ApCheckRegMethod (Op);
532         }
533 
534         ACPI_FREE (MethodInfo);
535         break;
536 
537 
538     case PARSEOP_NAME:
539 
540          /* Special check for two names like _L01 and _E01 in same scope */
541 
542         ApCheckForGpeNameConflict (Op);
543         break;
544 
545 
546     case PARSEOP_RETURN:
547 
548         /*
549          * If the parent is a predefined method name, attempt to typecheck
550          * the return value. Only static types can be validated.
551          */
552         ApCheckPredefinedReturnValue (Op, MethodInfo);
553 
554         /*
555          * The parent block does not "exit" and continue execution -- the
556          * method is terminated here with the Return() statement.
557          */
558         Op->Asl.Parent->Asl.CompileFlags |= NODE_HAS_NO_EXIT;
559 
560         /* Used in the "typing" pass later */
561 
562         Op->Asl.ParentMethod = MethodInfo->Op;
563 
564         /*
565          * If there is a peer node after the return statement, then this
566          * node is unreachable code -- i.e., it won't be executed because of
567          * the preceding Return() statement.
568          */
569         if (Op->Asl.Next)
570         {
571             AslError (ASL_WARNING, ASL_MSG_UNREACHABLE_CODE, Op->Asl.Next, NULL);
572         }
573         break;
574 
575 
576     case PARSEOP_IF:
577 
578         if ((Op->Asl.CompileFlags & NODE_HAS_NO_EXIT) &&
579             (Op->Asl.Next) &&
580             (Op->Asl.Next->Asl.ParseOpcode == PARSEOP_ELSE))
581         {
582             /*
583              * This IF has a corresponding ELSE. The IF block has no exit,
584              * (it contains an unconditional Return)
585              * mark the ELSE block to remember this fact.
586              */
587             Op->Asl.Next->Asl.CompileFlags |= NODE_IF_HAS_NO_EXIT;
588         }
589         break;
590 
591 
592     case PARSEOP_ELSE:
593 
594         if ((Op->Asl.CompileFlags & NODE_HAS_NO_EXIT) &&
595             (Op->Asl.CompileFlags & NODE_IF_HAS_NO_EXIT))
596         {
597             /*
598              * This ELSE block has no exit and the corresponding IF block
599              * has no exit either. Therefore, the parent node has no exit.
600              */
601             Op->Asl.Parent->Asl.CompileFlags |= NODE_HAS_NO_EXIT;
602         }
603         break;
604 
605 
606     default:
607 
608         if ((Op->Asl.CompileFlags & NODE_HAS_NO_EXIT) &&
609             (Op->Asl.Parent))
610         {
611             /* If this node has no exit, then the parent has no exit either */
612 
613             Op->Asl.Parent->Asl.CompileFlags |= NODE_HAS_NO_EXIT;
614         }
615         break;
616     }
617 
618     return (AE_OK);
619 }
620