Sync ACPICA with Intel's version 20161117.
[dragonfly.git] / sys / contrib / dev / acpica / source / compiler / aslexternal.c
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     char                    *ExternalName;
329     ACPI_OBJECT_TYPE        ObjType;
330     UINT32                  i;
331
332
333     if (!Gbl_ExternalsListHead)
334     {
335         return;
336     }
337
338     /* Remove the External nodes from the tree */
339
340     NextOp = Gbl_ExternalsListHead;
341     while (NextOp)
342     {
343         /*
344          * The External is stored in child pointer of each node in the
345          * list
346          */
347         ExternalOp = NextOp->Asl.Child;
348
349         /* Get/set the fully qualified name */
350
351         ExternalName = AcpiNsGetNormalizedPathname (ExternalOp->Asl.Node, TRUE);
352         ExternalOp->Asl.ExternalName = ExternalName;
353         ExternalOp->Asl.Namepath = ExternalName;
354
355         /* Set line numbers (for listings, etc.) */
356
357         ExternalOp->Asl.LineNumber = 0;
358         ExternalOp->Asl.LogicalLineNumber = 0;
359
360         Next = ExternalOp->Asl.Child;
361         Next->Asl.LineNumber = 0;
362         Next->Asl.LogicalLineNumber = 0;
363
364         if (Next->Asl.ParseOpcode == PARSEOP_NAMESEG)
365         {
366             Next->Asl.ParseOpcode = PARSEOP_NAMESTRING;
367         }
368         Next->Asl.ExternalName = ExternalName;
369         UtInternalizeName (ExternalName, &Next->Asl.Value.String);
370         Next->Asl.AmlLength = strlen (Next->Asl.Value.String);
371
372         Next = Next->Asl.Next;
373         Next->Asl.LineNumber = 0;
374         Next->Asl.LogicalLineNumber = 0;
375
376         Next = Next->Asl.Next;
377         Next->Asl.LineNumber = 0;
378         Next->Asl.LogicalLineNumber = 0;
379
380         Next = Next->Asl.Next;
381         Next->Asl.LineNumber = 0;
382         Next->Asl.LogicalLineNumber = 0;
383
384         ParentOp = ExternalOp->Asl.Parent;
385         Prev = Next = ParentOp->Asl.Child;
386
387         /* Now find the External node's position in parse tree */
388
389         while (Next != ExternalOp)
390         {
391             Prev = Next;
392             Next = Next->Asl.Next;
393         }
394
395         /* Remove the External from the parse tree */
396
397         if (Prev == ExternalOp)
398         {
399             /* External was the first child node */
400
401             ParentOp->Asl.Child = ExternalOp->Asl.Next;
402         }
403
404         Prev->Asl.Next = ExternalOp->Asl.Next;
405         ExternalOp->Asl.Next = NULL;
406         ExternalOp->Asl.Parent = Gbl_ExternalsListHead;
407
408         /* Point the External to the next in the list */
409
410         if (NextOp->Asl.Next)
411         {
412             ExternalOp->Asl.Next = NextOp->Asl.Next->Asl.Child;
413         }
414
415         NextOp = NextOp->Asl.Next;
416     }
417
418     /*
419      * Loop again to remove MethodObj Externals for which
420      * a MethodCall was not found (dead external reference)
421      */
422     Prev = Gbl_ExternalsListHead->Asl.Child;
423     Next = Prev;
424     while (Next)
425     {
426         ObjType = (ACPI_OBJECT_TYPE)
427             Next->Asl.Child->Asl.Next->Asl.Value.Integer;
428
429         if (ObjType == ACPI_TYPE_METHOD &&
430             !(Next->Asl.CompileFlags & NODE_VISITED))
431         {
432             if (Next == Prev)
433             {
434                 Gbl_ExternalsListHead->Asl.Child = Next->Asl.Next;
435                 Next->Asl.Next = NULL;
436                 Prev = Gbl_ExternalsListHead->Asl.Child;
437                 Next = Prev;
438                 continue;
439             }
440             else
441             {
442                 Prev->Asl.Next = Next->Asl.Next;
443                 Next->Asl.Next = NULL;
444                 Next = Prev->Asl.Next;
445                 continue;
446             }
447         }
448
449         Prev = Next;
450         Next = Next->Asl.Next;
451     }
452
453     /* If list is now empty, don't bother to make If (0) block */
454
455     if (!Gbl_ExternalsListHead->Asl.Child)
456     {
457         return;
458     }
459
460     /* Convert Gbl_ExternalsListHead parent to If(). */
461
462     Gbl_ExternalsListHead->Asl.ParseOpcode = PARSEOP_IF;
463     Gbl_ExternalsListHead->Asl.AmlOpcode = AML_IF_OP;
464     Gbl_ExternalsListHead->Asl.CompileFlags = NODE_AML_PACKAGE;
465     UtSetParseOpName (Gbl_ExternalsListHead);
466
467     /* Create a Zero op for the If predicate */
468
469     PredicateOp = TrAllocateNode (PARSEOP_ZERO);
470     PredicateOp->Asl.AmlOpcode = AML_ZERO_OP;
471
472     PredicateOp->Asl.Parent = Gbl_ExternalsListHead;
473     PredicateOp->Asl.Child = NULL;
474     PredicateOp->Asl.Next = Gbl_ExternalsListHead->Asl.Child;
475     Gbl_ExternalsListHead->Asl.Child = PredicateOp;
476
477     /* Set line numbers (for listings, etc.) */
478
479     Gbl_ExternalsListHead->Asl.LineNumber = 0;
480     Gbl_ExternalsListHead->Asl.LogicalLineNumber = 0;
481
482     PredicateOp->Asl.LineNumber = 0;
483     PredicateOp->Asl.LogicalLineNumber = 0;
484
485     /* Insert block back in the list */
486
487     Prev = DefinitionBlockOp->Asl.Child;
488     Next = Prev;
489
490     /* Find last default arg */
491
492     for (i = 0; i < 6; i++)
493     {
494         Prev = Next;
495         Next = Prev->Asl.Next;
496     }
497
498     if (Next)
499     {
500         /* Definition Block is not empty */
501
502         Gbl_ExternalsListHead->Asl.Next = Next;
503     }
504     else
505     {
506         /* Definition Block is empty. */
507
508         Gbl_ExternalsListHead->Asl.Next = NULL;
509     }
510
511     Prev->Asl.Next = Gbl_ExternalsListHead;
512     Gbl_ExternalsListHead->Asl.Parent = Prev->Asl.Parent;
513 }