xref: /netbsd-src/sys/external/bsd/acpica/dist/compiler/aslexternal.c (revision e6c7e151de239c49d2e38720a061ed9d1fa99309)
1 /******************************************************************************
2  *
3  * Module Name: aslexternal - ASL External opcode compiler support
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2020, 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 "aslcompiler.h"
45 #include "aslcompiler.y.h"
46 #include "acparser.h"
47 #include "amlcode.h"
48 #include "acnamesp.h"
49 
50 
51 #define _COMPONENT          ACPI_COMPILER
52         ACPI_MODULE_NAME    ("aslexternal")
53 
54 
55 /* Local prototypes */
56 
57 static void
58 ExInsertArgCount (
59     ACPI_PARSE_OBJECT       *Op);
60 
61 static void
62 ExMoveExternals (
63     ACPI_PARSE_OBJECT       *DefinitionBlockOp);
64 
65 
66 /*******************************************************************************
67  *
68  * FUNCTION:    ExDoExternal
69  *
70  * PARAMETERS:  Op                  - Current Parse node
71  *
72  * RETURN:      None
73  *
74  * DESCRIPTION: Add an External() definition to the global list. This list
75  *              is used to generate External opcodes.
76  *
77  ******************************************************************************/
78 
79 void
80 ExDoExternal (
81     ACPI_PARSE_OBJECT       *Op)
82 {
83     ACPI_PARSE_OBJECT       *ListOp;
84     ACPI_PARSE_OBJECT       *Prev;
85     ACPI_PARSE_OBJECT       *Next;
86     ACPI_PARSE_OBJECT       *ArgCountOp;
87     ACPI_PARSE_OBJECT       *TypeOp;
88     ACPI_PARSE_OBJECT       *ExternTypeOp = Op->Asl.Child->Asl.Next;
89     UINT32                  ExternType;
90     UINT8                   ParamCount = ASL_EXTERNAL_METHOD_UNKNOWN_PARAMS;
91     UINT32                  ParamTypes[ACPI_METHOD_NUM_ARGS];
92 
93 
94     ExternType = AnMapObjTypeToBtype (ExternTypeOp);
95 
96     /*
97      * The parser allows optional parameter return types regardless of the
98      * type. Check object type keyword emit error if optional parameter/return
99      * types exist.
100      *
101      * Check the parameter return type
102      */
103     TypeOp = ExternTypeOp->Asl.Next;
104     if (TypeOp->Asl.Child)
105     {
106         /* Ignore the return type for now. */
107 
108         (void) MtProcessTypeOp (TypeOp->Asl.Child);
109         if (ExternType != ACPI_BTYPE_METHOD)
110         {
111             sprintf (AslGbl_MsgBuffer, "Found type [%s]", AcpiUtGetTypeName(ExternType));
112             AslError (ASL_ERROR, ASL_MSG_EXTERN_INVALID_RET_TYPE, TypeOp,
113                 AslGbl_MsgBuffer);
114         }
115     }
116 
117     /* Check the parameter types */
118 
119     TypeOp = TypeOp->Asl.Next;
120     if (TypeOp->Asl.Child)
121     {
122         ParamCount = MtProcessParameterTypeList (TypeOp->Asl.Child, ParamTypes);
123         if (ExternType != ACPI_BTYPE_METHOD)
124         {
125             sprintf (AslGbl_MsgBuffer, "Found type [%s]", AcpiUtGetTypeName(ExternType));
126             AslError (ASL_ERROR, ASL_MSG_EXTERN_INVALID_PARAM_TYPE, TypeOp,
127                 AslGbl_MsgBuffer);
128         }
129     }
130 
131     ArgCountOp = Op->Asl.Child->Asl.Next->Asl.Next;
132     ArgCountOp->Asl.AmlOpcode = AML_RAW_DATA_BYTE;
133     ArgCountOp->Asl.ParseOpcode = PARSEOP_BYTECONST;
134     ArgCountOp->Asl.Value.Integer = ParamCount;
135     UtSetParseOpName (ArgCountOp);
136 
137     /* Create new list node of arbitrary type */
138 
139     ListOp = TrAllocateOp (PARSEOP_DEFAULT_ARG);
140 
141     /* Store External node as child */
142 
143     ListOp->Asl.Child = Op;
144     ListOp->Asl.Next = NULL;
145 
146     if (AslGbl_ExternalsListHead)
147     {
148         /* Link new External to end of list */
149 
150         Prev = AslGbl_ExternalsListHead;
151         Next = Prev;
152         while (Next)
153         {
154             Prev = Next;
155             Next = Next->Asl.Next;
156         }
157 
158         Prev->Asl.Next = ListOp;
159     }
160     else
161     {
162         AslGbl_ExternalsListHead = ListOp;
163     }
164 }
165 
166 
167 /*******************************************************************************
168  *
169  * FUNCTION:    ExInsertArgCount
170  *
171  * PARAMETERS:  Op              - Op for a method invocation
172  *
173  * RETURN:      None
174  *
175  * DESCRIPTION: Obtain the number of arguments for a control method -- from
176  *              the actual invocation.
177  *
178  ******************************************************************************/
179 
180 static void
181 ExInsertArgCount (
182     ACPI_PARSE_OBJECT       *Op)
183 {
184     ACPI_PARSE_OBJECT       *Next;
185     ACPI_PARSE_OBJECT       *NameOp;
186     ACPI_PARSE_OBJECT       *Child;
187     ACPI_PARSE_OBJECT       *ArgCountOp;
188     char *                  ExternalName;
189     char *                  CallName;
190     UINT16                  ArgCount = 0;
191     ACPI_STATUS             Status;
192 
193 
194     CallName = AcpiNsGetNormalizedPathname (Op->Asl.Node, TRUE);
195 
196     Next = AslGbl_ExternalsListHead;
197     while (Next)
198     {
199         ArgCount = 0;
200 
201         /* Skip if External node already handled */
202 
203         if (Next->Asl.Child->Asl.CompileFlags & OP_VISITED)
204         {
205             Next = Next->Asl.Next;
206             continue;
207         }
208 
209         NameOp = Next->Asl.Child->Asl.Child;
210         ExternalName = AcpiNsGetNormalizedPathname (NameOp->Asl.Node, TRUE);
211 
212         if (strcmp (CallName, ExternalName))
213         {
214             ACPI_FREE (ExternalName);
215             Next = Next->Asl.Next;
216             continue;
217         }
218 
219         Next->Asl.Child->Asl.CompileFlags |= OP_VISITED;
220 
221         /*
222          * Since we will reposition Externals to the Root, set Namepath
223          * to the fully qualified name and recalculate the aml length
224          */
225         Status = UtInternalizeName (ExternalName,
226             &NameOp->Asl.Value.String);
227 
228         ACPI_FREE (ExternalName);
229         if (ACPI_FAILURE (Status))
230         {
231             AslError (ASL_ERROR, ASL_MSG_COMPILER_INTERNAL,
232                 NULL, "- Could not Internalize External");
233             break;
234         }
235 
236         NameOp->Asl.AmlLength = strlen (NameOp->Asl.Value.String);
237 
238         /* Get argument count */
239 
240         Child = Op->Asl.Child;
241         while (Child)
242         {
243             ArgCount++;
244             Child = Child->Asl.Next;
245         }
246 
247         /* Setup ArgCount operand */
248 
249         ArgCountOp = Next->Asl.Child->Asl.Child->Asl.Next->Asl.Next;
250         ArgCountOp->Asl.Value.Integer = ArgCount;
251         break;
252     }
253 
254     ACPI_FREE (CallName);
255 }
256 
257 
258 /*******************************************************************************
259  *
260  * FUNCTION:    ExAmlExternalWalkBegin
261  *
262  * PARAMETERS:  ASL_WALK_CALLBACK
263  *
264  * RETURN:      None
265  *
266  * DESCRIPTION: Parse tree walk to create external opcode list for methods.
267  *
268  ******************************************************************************/
269 
270 ACPI_STATUS
271 ExAmlExternalWalkBegin (
272     ACPI_PARSE_OBJECT       *Op,
273     UINT32                  Level,
274     void                    *Context)
275 {
276 
277     /* External list head saved in the definition block op */
278 
279     if (Op->Asl.ParseOpcode == PARSEOP_DEFINITION_BLOCK)
280     {
281         AslGbl_ExternalsListHead = Op->Asl.Value.Arg;
282     }
283 
284     if (!AslGbl_ExternalsListHead)
285     {
286         return (AE_OK);
287     }
288 
289     if (Op->Asl.ParseOpcode != PARSEOP_METHODCALL)
290     {
291         return (AE_OK);
292     }
293 
294     /*
295      * The NameOp child under an ExternalOp gets turned into PARSE_METHODCALL
296      * by XfNamespaceLocateBegin(). Ignore these.
297      */
298     if (Op->Asl.Parent &&
299         Op->Asl.Parent->Asl.ParseOpcode == PARSEOP_EXTERNAL)
300     {
301         return (AE_OK);
302     }
303 
304     ExInsertArgCount (Op);
305     return (AE_OK);
306 }
307 
308 
309 /*******************************************************************************
310  *
311  * FUNCTION:    ExAmlExternalWalkEnd
312  *
313  * PARAMETERS:  ASL_WALK_CALLBACK
314  *
315  * RETURN:      None
316  *
317  * DESCRIPTION: Parse tree walk to create external opcode list for methods.
318  *              Here, we just want to catch the case where a definition block
319  *              has been completed. Then we move all of the externals into
320  *              a single block in the parse tree and thus the AML code.
321  *
322  ******************************************************************************/
323 
324 ACPI_STATUS
325 ExAmlExternalWalkEnd (
326     ACPI_PARSE_OBJECT       *Op,
327     UINT32                  Level,
328     void                    *Context)
329 {
330 
331     if (Op->Asl.ParseOpcode == PARSEOP_DEFINITION_BLOCK)
332     {
333         /*
334          * Process any existing external list. (Support for
335          * multiple definition blocks in a single file/compile)
336          */
337         ExMoveExternals (Op);
338         AslGbl_ExternalsListHead = NULL;
339     }
340 
341     return (AE_OK);
342 }
343 
344 
345 /*******************************************************************************
346  *
347  * FUNCTION:    ExMoveExternals
348  *
349  * PARAMETERS:  DefinitionBlockOp       - Op for current definition block
350  *
351  * RETURN:      None
352  *
353  * DESCRIPTION: Move all externals present in the source file into a single
354  *              block of AML code, surrounded by an "If (0)" to prevent
355  *              AML interpreters from attempting to execute the External
356  *              opcodes.
357  *
358  ******************************************************************************/
359 
360 static void
361 ExMoveExternals (
362     ACPI_PARSE_OBJECT       *DefinitionBlockOp)
363 {
364     ACPI_PARSE_OBJECT       *ParentOp;
365     ACPI_PARSE_OBJECT       *ExternalOp;
366     ACPI_PARSE_OBJECT       *PredicateOp;
367     ACPI_PARSE_OBJECT       *NextOp;
368     ACPI_PARSE_OBJECT       *Prev;
369     ACPI_PARSE_OBJECT       *Next;
370     char                    *ExternalName;
371     ACPI_OBJECT_TYPE        ObjType;
372     ACPI_STATUS             Status;
373     UINT32                  i;
374 
375 
376     if (!AslGbl_ExternalsListHead)
377     {
378         return;
379     }
380 
381     /* Remove the External nodes from the tree */
382 
383     NextOp = AslGbl_ExternalsListHead;
384     while (NextOp)
385     {
386         /*
387          * The External is stored in child pointer of each node in the
388          * list
389          */
390         ExternalOp = NextOp->Asl.Child;
391 
392         /* Get/set the fully qualified name */
393 
394         ExternalName = AcpiNsGetNormalizedPathname (ExternalOp->Asl.Node, TRUE);
395         ExternalOp->Asl.ExternalName = ExternalName;
396         ExternalOp->Asl.Namepath = ExternalName;
397 
398         /* Set line numbers (for listings, etc.) */
399 
400         ExternalOp->Asl.LineNumber = 0;
401         ExternalOp->Asl.LogicalLineNumber = 0;
402 
403         Next = ExternalOp->Asl.Child;
404         Next->Asl.LineNumber = 0;
405         Next->Asl.LogicalLineNumber = 0;
406 
407         if (Next->Asl.ParseOpcode == PARSEOP_NAMESEG)
408         {
409             Next->Asl.ParseOpcode = PARSEOP_NAMESTRING;
410         }
411 
412         Next->Asl.ExternalName = ExternalName;
413         Status = UtInternalizeName (ExternalName, &Next->Asl.Value.String);
414         if (ACPI_FAILURE (Status))
415         {
416             AslError (ASL_ERROR, ASL_MSG_COMPILER_INTERNAL,
417                 Next, "Could not internalize namestring");
418             return;
419         }
420 
421         Next->Asl.AmlLength = strlen (Next->Asl.Value.String);
422 
423         Next = Next->Asl.Next;
424         Next->Asl.LineNumber = 0;
425         Next->Asl.LogicalLineNumber = 0;
426 
427         Next = Next->Asl.Next;
428         Next->Asl.LineNumber = 0;
429         Next->Asl.LogicalLineNumber = 0;
430 
431         Next = Next->Asl.Next;
432         Next->Asl.LineNumber = 0;
433         Next->Asl.LogicalLineNumber = 0;
434 
435         ParentOp = ExternalOp->Asl.Parent;
436         Prev = Next = ParentOp->Asl.Child;
437 
438         /* Now find the External node's position in parse tree */
439 
440         while (Next != ExternalOp)
441         {
442             Prev = Next;
443             Next = Next->Asl.Next;
444         }
445 
446         /* Remove the External from the parse tree */
447 
448         if (Prev == ExternalOp)
449         {
450             /* External was the first child node */
451 
452             ParentOp->Asl.Child = ExternalOp->Asl.Next;
453         }
454 
455         Prev->Asl.Next = ExternalOp->Asl.Next;
456         ExternalOp->Asl.Next = NULL;
457         ExternalOp->Asl.Parent = AslGbl_ExternalsListHead;
458 
459         /* Point the External to the next in the list */
460 
461         if (NextOp->Asl.Next)
462         {
463             ExternalOp->Asl.Next = NextOp->Asl.Next->Asl.Child;
464         }
465 
466         NextOp = NextOp->Asl.Next;
467     }
468 
469     /*
470      * Loop again to remove MethodObj Externals for which
471      * a MethodCall was not found (dead external reference)
472      */
473     Prev = AslGbl_ExternalsListHead->Asl.Child;
474     Next = Prev;
475     while (Next)
476     {
477         ObjType = (ACPI_OBJECT_TYPE)
478             Next->Asl.Child->Asl.Next->Asl.Value.Integer;
479 
480         if (ObjType == ACPI_TYPE_METHOD &&
481             !(Next->Asl.CompileFlags & OP_VISITED))
482         {
483             if (Next == Prev)
484             {
485                 AslGbl_ExternalsListHead->Asl.Child = Next->Asl.Next;
486                 Next->Asl.Next = NULL;
487                 Prev = AslGbl_ExternalsListHead->Asl.Child;
488                 Next = Prev;
489                 continue;
490             }
491             else
492             {
493                 Prev->Asl.Next = Next->Asl.Next;
494                 Next->Asl.Next = NULL;
495                 Next = Prev->Asl.Next;
496                 continue;
497             }
498         }
499 
500         Prev = Next;
501         Next = Next->Asl.Next;
502     }
503 
504     /* If list is now empty, don't bother to make If (0) block */
505 
506     if (!AslGbl_ExternalsListHead->Asl.Child)
507     {
508         return;
509     }
510 
511     /* Convert Gbl_ExternalsListHead parent to If(). */
512 
513     AslGbl_ExternalsListHead->Asl.ParseOpcode = PARSEOP_IF;
514     AslGbl_ExternalsListHead->Asl.AmlOpcode = AML_IF_OP;
515     AslGbl_ExternalsListHead->Asl.CompileFlags = OP_AML_PACKAGE;
516     UtSetParseOpName (AslGbl_ExternalsListHead);
517 
518     /* Create a Zero op for the If predicate */
519 
520     PredicateOp = TrAllocateOp (PARSEOP_ZERO);
521     PredicateOp->Asl.AmlOpcode = AML_ZERO_OP;
522 
523     PredicateOp->Asl.Parent = AslGbl_ExternalsListHead;
524     PredicateOp->Asl.Child = NULL;
525     PredicateOp->Asl.Next = AslGbl_ExternalsListHead->Asl.Child;
526     AslGbl_ExternalsListHead->Asl.Child = PredicateOp;
527 
528     /* Set line numbers (for listings, etc.) */
529 
530     AslGbl_ExternalsListHead->Asl.LineNumber = 0;
531     AslGbl_ExternalsListHead->Asl.LogicalLineNumber = 0;
532 
533     PredicateOp->Asl.LineNumber = 0;
534     PredicateOp->Asl.LogicalLineNumber = 0;
535 
536     /* Insert block back in the list */
537 
538     Prev = DefinitionBlockOp->Asl.Child;
539     Next = Prev;
540 
541     /* Find last default arg */
542 
543     for (i = 0; i < 6; i++)
544     {
545         Prev = Next;
546         Next = Prev->Asl.Next;
547     }
548 
549     if (Next)
550     {
551         /* Definition Block is not empty */
552 
553         AslGbl_ExternalsListHead->Asl.Next = Next;
554     }
555     else
556     {
557         /* Definition Block is empty. */
558 
559         AslGbl_ExternalsListHead->Asl.Next = NULL;
560     }
561 
562     Prev->Asl.Next = AslGbl_ExternalsListHead;
563     AslGbl_ExternalsListHead->Asl.Parent = Prev->Asl.Parent;
564 }
565