1 /******************************************************************************
2  *
3  * Module Name: aslexternal - ASL External opcode compiler support
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 "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 
88 
89     ArgCountOp = Op->Asl.Child->Asl.Next->Asl.Next;
90     ArgCountOp->Asl.AmlOpcode = AML_RAW_DATA_BYTE;
91     ArgCountOp->Asl.ParseOpcode = PARSEOP_BYTECONST;
92     ArgCountOp->Asl.Value.Integer = 0;
93     UtSetParseOpName (ArgCountOp);
94 
95     /* Create new list node of arbitrary type */
96 
97     ListOp = TrAllocateNode (PARSEOP_DEFAULT_ARG);
98 
99     /* Store External node as child */
100 
101     ListOp->Asl.Child = Op;
102     ListOp->Asl.Next = NULL;
103 
104     if (Gbl_ExternalsListHead)
105     {
106         /* Link new External to end of list */
107 
108         Prev = Gbl_ExternalsListHead;
109         Next = Prev;
110         while (Next)
111         {
112             Prev = Next;
113             Next = Next->Asl.Next;
114         }
115 
116         Prev->Asl.Next = ListOp;
117     }
118     else
119     {
120         Gbl_ExternalsListHead = ListOp;
121     }
122 }
123 
124 
125 /*******************************************************************************
126  *
127  * FUNCTION:    ExInsertArgCount
128  *
129  * PARAMETERS:  Op              - Op for a method invocation
130  *
131  * RETURN:      None
132  *
133  * DESCRIPTION: Obtain the number of arguments for a control method -- from
134  *              the actual invocation.
135  *
136  ******************************************************************************/
137 
138 static void
139 ExInsertArgCount (
140     ACPI_PARSE_OBJECT       *Op)
141 {
142     ACPI_PARSE_OBJECT       *Next;
143     ACPI_PARSE_OBJECT       *NameOp;
144     ACPI_PARSE_OBJECT       *Child;
145     ACPI_PARSE_OBJECT       *ArgCountOp;
146     char *                  ExternalName;
147     char *                  CallName;
148     UINT16                  ArgCount = 0;
149     ACPI_STATUS             Status;
150 
151 
152     CallName = AcpiNsGetNormalizedPathname (Op->Asl.Node, TRUE);
153 
154     Next = Gbl_ExternalsListHead;
155     while (Next)
156     {
157         ArgCount = 0;
158 
159         /* Skip if External node already handled */
160 
161         if (Next->Asl.Child->Asl.CompileFlags & NODE_VISITED)
162         {
163             Next = Next->Asl.Next;
164             continue;
165         }
166 
167         NameOp = Next->Asl.Child->Asl.Child;
168         ExternalName = AcpiNsGetNormalizedPathname (NameOp->Asl.Node, TRUE);
169 
170         if (strcmp (CallName, ExternalName))
171         {
172             ACPI_FREE (ExternalName);
173             Next = Next->Asl.Next;
174             continue;
175         }
176 
177         Next->Asl.Child->Asl.CompileFlags |= NODE_VISITED;
178 
179         /*
180          * Since we will reposition Externals to the Root, set Namepath
181          * to the fully qualified name and recalculate the aml length
182          */
183         Status = UtInternalizeName (ExternalName,
184             &NameOp->Asl.Value.String);
185 
186         ACPI_FREE (ExternalName);
187         if (ACPI_FAILURE (Status))
188         {
189             AslError (ASL_ERROR, ASL_MSG_COMPILER_INTERNAL,
190                 NULL, "- Could not Internalize External");
191             break;
192         }
193 
194         NameOp->Asl.AmlLength = strlen (NameOp->Asl.Value.String);
195 
196         /* Get argument count */
197 
198         Child = Op->Asl.Child;
199         while (Child)
200         {
201             ArgCount++;
202             Child = Child->Asl.Next;
203         }
204 
205         /* Setup ArgCount operand */
206 
207         ArgCountOp = Next->Asl.Child->Asl.Child->Asl.Next->Asl.Next;
208         ArgCountOp->Asl.Value.Integer = ArgCount;
209         break;
210     }
211 
212     ACPI_FREE (CallName);
213 }
214 
215 
216 /*******************************************************************************
217  *
218  * FUNCTION:    ExAmlExternalWalkBegin
219  *
220  * PARAMETERS:  ASL_WALK_CALLBACK
221  *
222  * RETURN:      None
223  *
224  * DESCRIPTION: Parse tree walk to create external opcode list for methods.
225  *
226  ******************************************************************************/
227 
228 ACPI_STATUS
229 ExAmlExternalWalkBegin (
230     ACPI_PARSE_OBJECT       *Op,
231     UINT32                  Level,
232     void                    *Context)
233 {
234 
235     /* External list head saved in the definition block op */
236 
237     if (Op->Asl.ParseOpcode == PARSEOP_DEFINITION_BLOCK)
238     {
239         Gbl_ExternalsListHead = Op->Asl.Value.Arg;
240     }
241 
242     if (!Gbl_ExternalsListHead)
243     {
244         return (AE_OK);
245     }
246 
247     if (Op->Asl.ParseOpcode != PARSEOP_METHODCALL)
248     {
249         return (AE_OK);
250     }
251 
252     /*
253      * The NameOp child under an ExternalOp gets turned into PARSE_METHODCALL
254      * by XfNamespaceLocateBegin(). Ignore these.
255      */
256     if (Op->Asl.Parent &&
257         Op->Asl.Parent->Asl.ParseOpcode == PARSEOP_EXTERNAL)
258     {
259         return (AE_OK);
260     }
261 
262     ExInsertArgCount (Op);
263     return (AE_OK);
264 }
265 
266 
267 /*******************************************************************************
268  *
269  * FUNCTION:    ExAmlExternalWalkEnd
270  *
271  * PARAMETERS:  ASL_WALK_CALLBACK
272  *
273  * RETURN:      None
274  *
275  * DESCRIPTION: Parse tree walk to create external opcode list for methods.
276  *              Here, we just want to catch the case where a definition block
277  *              has been completed. Then we move all of the externals into
278  *              a single block in the parse tree and thus the AML code.
279  *
280  ******************************************************************************/
281 
282 ACPI_STATUS
283 ExAmlExternalWalkEnd (
284     ACPI_PARSE_OBJECT       *Op,
285     UINT32                  Level,
286     void                    *Context)
287 {
288 
289     if (Op->Asl.ParseOpcode == PARSEOP_DEFINITION_BLOCK)
290     {
291         /*
292          * Process any existing external list. (Support for
293          * multiple definition blocks in a single file/compile)
294          */
295         ExMoveExternals (Op);
296         Gbl_ExternalsListHead = NULL;
297     }
298 
299     return (AE_OK);
300 }
301 
302 
303 /*******************************************************************************
304  *
305  * FUNCTION:    ExMoveExternals
306  *
307  * PARAMETERS:  DefinitionBlockOp       - Op for current definition block
308  *
309  * RETURN:      None
310  *
311  * DESCRIPTION: Move all externals present in the source file into a single
312  *              block of AML code, surrounded by an "If (0)" to prevent
313  *              AML interpreters from attempting to execute the External
314  *              opcodes.
315  *
316  ******************************************************************************/
317 
318 static void
319 ExMoveExternals (
320     ACPI_PARSE_OBJECT       *DefinitionBlockOp)
321 {
322     ACPI_PARSE_OBJECT       *ParentOp;
323     ACPI_PARSE_OBJECT       *ExternalOp;
324     ACPI_PARSE_OBJECT       *PredicateOp;
325     ACPI_PARSE_OBJECT       *NextOp;
326     ACPI_PARSE_OBJECT       *Prev;
327     ACPI_PARSE_OBJECT       *Next;
328     ACPI_OBJECT_TYPE        ObjType;
329     UINT32                  i;
330 
331 
332     if (!Gbl_ExternalsListHead)
333     {
334         return;
335     }
336 
337     /* Remove the External nodes from the tree */
338 
339     NextOp = Gbl_ExternalsListHead;
340     while (NextOp)
341     {
342         /*
343          * The External is stored in child pointer of each node in the
344          * list
345          */
346         ExternalOp = NextOp->Asl.Child;
347 
348         /* Set line numbers (for listings, etc.) */
349 
350         ExternalOp->Asl.LineNumber = 0;
351         ExternalOp->Asl.LogicalLineNumber = 0;
352 
353         Next = ExternalOp->Asl.Child;
354         Next->Asl.LineNumber = 0;
355         Next->Asl.LogicalLineNumber = 0;
356 
357         Next = Next->Asl.Next;
358         Next->Asl.LineNumber = 0;
359         Next->Asl.LogicalLineNumber = 0;
360 
361         Next = Next->Asl.Next;
362         Next->Asl.LineNumber = 0;
363         Next->Asl.LogicalLineNumber = 0;
364 
365         Next = Next->Asl.Next;
366         Next->Asl.LineNumber = 0;
367         Next->Asl.LogicalLineNumber = 0;
368 
369         ParentOp = ExternalOp->Asl.Parent;
370         Prev = Next = ParentOp->Asl.Child;
371 
372         /* Now find the External node's position in parse tree */
373 
374         while (Next != ExternalOp)
375         {
376             Prev = Next;
377             Next = Next->Asl.Next;
378         }
379 
380         /* Remove the External from the parse tree */
381 
382         if (Prev == ExternalOp)
383         {
384             /* External was the first child node */
385 
386             ParentOp->Asl.Child = ExternalOp->Asl.Next;
387         }
388 
389         Prev->Asl.Next = ExternalOp->Asl.Next;
390         ExternalOp->Asl.Next = NULL;
391         ExternalOp->Asl.Parent = Gbl_ExternalsListHead;
392 
393         /* Point the External to the next in the list */
394 
395         if (NextOp->Asl.Next)
396         {
397             ExternalOp->Asl.Next = NextOp->Asl.Next->Asl.Child;
398         }
399 
400         NextOp = NextOp->Asl.Next;
401     }
402 
403     /*
404      * Loop again to remove MethodObj Externals for which
405      * a MethodCall was not found (dead external reference)
406      */
407     Prev = Gbl_ExternalsListHead->Asl.Child;
408     Next = Prev;
409     while (Next)
410     {
411         ObjType = (ACPI_OBJECT_TYPE)
412             Next->Asl.Child->Asl.Next->Asl.Value.Integer;
413 
414         if (ObjType == ACPI_TYPE_METHOD &&
415             !(Next->Asl.CompileFlags & NODE_VISITED))
416         {
417             if (Next == Prev)
418             {
419                 Gbl_ExternalsListHead->Asl.Child = Next->Asl.Next;
420                 Next->Asl.Next = NULL;
421                 Prev = Gbl_ExternalsListHead->Asl.Child;
422                 Next = Prev;
423                 continue;
424             }
425             else
426             {
427                 Prev->Asl.Next = Next->Asl.Next;
428                 Next->Asl.Next = NULL;
429                 Next = Prev->Asl.Next;
430                 continue;
431             }
432         }
433 
434         Prev = Next;
435         Next = Next->Asl.Next;
436     }
437 
438     /* If list is now empty, don't bother to make If (0) block */
439 
440     if (!Gbl_ExternalsListHead->Asl.Child)
441     {
442         return;
443     }
444 
445     /* Convert Gbl_ExternalsListHead parent to If(). */
446 
447     Gbl_ExternalsListHead->Asl.ParseOpcode = PARSEOP_IF;
448     Gbl_ExternalsListHead->Asl.AmlOpcode = AML_IF_OP;
449     Gbl_ExternalsListHead->Asl.CompileFlags = NODE_AML_PACKAGE;
450     UtSetParseOpName (Gbl_ExternalsListHead);
451 
452     /* Create a Zero op for the If predicate */
453 
454     PredicateOp = TrAllocateNode (PARSEOP_ZERO);
455     PredicateOp->Asl.AmlOpcode = AML_ZERO_OP;
456 
457     PredicateOp->Asl.Parent = Gbl_ExternalsListHead;
458     PredicateOp->Asl.Child = NULL;
459     PredicateOp->Asl.Next = Gbl_ExternalsListHead->Asl.Child;
460     Gbl_ExternalsListHead->Asl.Child = PredicateOp;
461 
462     /* Set line numbers (for listings, etc.) */
463 
464     Gbl_ExternalsListHead->Asl.LineNumber = 0;
465     Gbl_ExternalsListHead->Asl.LogicalLineNumber = 0;
466 
467     PredicateOp->Asl.LineNumber = 0;
468     PredicateOp->Asl.LogicalLineNumber = 0;
469 
470     /* Insert block back in the list */
471 
472     Prev = DefinitionBlockOp->Asl.Child;
473     Next = Prev;
474 
475     /* Find last default arg */
476 
477     for (i = 0; i < 6; i++)
478     {
479         Prev = Next;
480         Next = Prev->Asl.Next;
481     }
482 
483     if (Next)
484     {
485         /* Definition Block is not empty */
486 
487         Gbl_ExternalsListHead->Asl.Next = Next;
488     }
489     else
490     {
491         /* Definition Block is empty. */
492 
493         Gbl_ExternalsListHead->Asl.Next = NULL;
494     }
495 
496     Prev->Asl.Next = Gbl_ExternalsListHead;
497     Gbl_ExternalsListHead->Asl.Parent = Prev->Asl.Parent;
498 }
499