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