xref: /netbsd-src/sys/external/bsd/acpica/dist/common/dmswitch.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /******************************************************************************
2  *
3  * Module Name: adwalk - Disassembler routines for switch statements
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2018, 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 "acpi.h"
45 #include "accommon.h"
46 #include "acparser.h"
47 #include "amlcode.h"
48 #include "acdisasm.h"
49 #include "acdispat.h"
50 #include "acnamesp.h"
51 #include "acapps.h"
52 
53 
54 #define _COMPONENT          ACPI_CA_DISASSEMBLER
55         ACPI_MODULE_NAME    ("dmswitch")
56 
57 static BOOLEAN
58 AcpiDmIsSwitchBlock (
59     ACPI_PARSE_OBJECT       *Op,
60     char                    **Temp);
61 
62 static BOOLEAN
63 AcpiDmIsCaseBlock (
64     ACPI_PARSE_OBJECT       *Op);
65 
66 
67 /*******************************************************************************
68  *
69  * FUNCTION:    AcpiDmProcessSwitch
70  *
71  * PARAMETERS:  Op              - Object to be examined
72  *
73  * RETURN:      ACPI_STATUS
74  *
75  * DESCRIPTION: Walk function to create a list of all temporary (_T_) objects.
76  *              If a While loop is found that can be converted to a Switch, do
77  *              the conversion, remove the temporary name from the list, and
78  *              mark the parse op with an IGNORE flag.
79  *
80  ******************************************************************************/
81 
82 ACPI_STATUS
83 AcpiDmProcessSwitch (
84     ACPI_PARSE_OBJECT       *Op)
85 {
86     char                    *Temp = NULL;
87     ACPI_PARSE_OBJECT_LIST  *NewTemp;
88     ACPI_PARSE_OBJECT_LIST  *Current;
89     ACPI_PARSE_OBJECT_LIST  *Previous;
90     BOOLEAN                 FoundTemp = FALSE;
91 
92 
93     switch (Op->Common.AmlOpcode)
94     {
95     case AML_NAME_OP:
96 
97         Temp = (char *) (&Op->Named.Name);
98 
99         if (!strncmp(Temp, "_T_", 3))
100         {
101             /* Allocate and init a new Temp List node */
102 
103             NewTemp = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_PARSE_OBJECT_LIST));
104             if (!NewTemp)
105             {
106                 return (AE_NO_MEMORY);
107             }
108 
109             if (AcpiGbl_TempListHead)
110             {
111                 Current = AcpiGbl_TempListHead;
112                 AcpiGbl_TempListHead = NewTemp;
113                 AcpiGbl_TempListHead->Op = Op;
114                 AcpiGbl_TempListHead->Next = Current;
115             }
116             else
117             {
118                 AcpiGbl_TempListHead = NewTemp;
119                 AcpiGbl_TempListHead->Op = Op;
120                 AcpiGbl_TempListHead->Next = NULL;
121             }
122         }
123         break;
124 
125     case AML_WHILE_OP:
126 
127         if (!AcpiDmIsSwitchBlock (Op, &Temp))
128         {
129             break;
130         }
131 
132         /* Found a Switch */
133 
134         Op->Common.DisasmOpcode = ACPI_DASM_SWITCH;
135 
136         Previous = Current = AcpiGbl_TempListHead;
137         while (Current)
138         {
139             /* Note, if we get here Temp is not NULL */
140 
141             if (!strncmp(Temp, (char *) (&Current->Op->Named.Name), 4))
142             {
143                 /* Match found. Ignore disassembly */
144 
145                 Current->Op->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
146 
147                 /* Remove from list */
148 
149                 if (Current == AcpiGbl_TempListHead)
150                 {
151                     AcpiGbl_TempListHead = Current->Next;
152                 }
153                 else
154                 {
155                     Previous->Next = Current->Next;
156                 }
157 
158                 Current->Op = NULL;
159                 Current->Next = NULL;
160                 ACPI_FREE (Current);
161                 FoundTemp = TRUE;
162                 break;
163             }
164 
165             Previous = Current;
166             Current = Current->Next;
167         }
168 
169         if (!FoundTemp)
170         {
171             fprintf (stderr,
172                 "Warning: Declaration for temp name %.4s not found\n", Temp);
173         }
174         break;
175 
176     default:
177         break;
178     }
179 
180     return (AE_OK);
181 }
182 
183 
184 /*******************************************************************************
185  *
186  * FUNCTION:    AcpiDmClearTempList
187  *
188  * PARAMETERS:  None
189  *
190  * RETURN:      None
191  *
192  * DESCRIPTION: Removes any remaining temporary objects from global list and
193  *              frees
194  *
195  ******************************************************************************/
196 
197 void
198 AcpiDmClearTempList (
199     void)
200 {
201     ACPI_PARSE_OBJECT_LIST      *Current;
202 
203 
204     while (AcpiGbl_TempListHead)
205     {
206         Current = AcpiGbl_TempListHead;
207         AcpiGbl_TempListHead = AcpiGbl_TempListHead->Next;
208         Current->Op = NULL;
209         Current->Next = NULL;
210         ACPI_FREE (Current);
211     }
212 }
213 
214 
215 /*******************************************************************************
216  *
217  * FUNCTION:    AcpiDmIsSwitchBlock
218  *
219  * PARAMETERS:  Op              - While Object
220  *
221  * RETURN:      TRUE if While block can be converted to a Switch/Case block
222  *
223  * DESCRIPTION: Determines if While block is a Switch/Case statement. Modifies
224  *              parse tree to allow for Switch/Case disassembly during walk.
225  *
226  * EXAMPLE: Example of parse tree to be converted
227  *
228  *    While
229  *        One
230  *        Store
231  *            ByteConst
232  *             -NamePath-
233  *        If
234  *            LEqual
235  *                -NamePath-
236  *                Zero
237  *            Return
238  *                One
239  *        Else
240  *            Return
241  *                WordConst
242  *        Break
243  *
244  ******************************************************************************/
245 
246 BOOLEAN
247 AcpiDmIsSwitchBlock (
248     ACPI_PARSE_OBJECT       *Op,
249     char                    **Temp)
250 {
251     ACPI_PARSE_OBJECT       *OneOp;
252     ACPI_PARSE_OBJECT       *StoreOp;
253     ACPI_PARSE_OBJECT       *NamePathOp;
254     ACPI_PARSE_OBJECT       *PredicateOp;
255     ACPI_PARSE_OBJECT       *CurrentOp;
256     ACPI_PARSE_OBJECT       *TempOp;
257 
258 
259     /* Check for One Op Predicate */
260 
261     OneOp = AcpiPsGetArg (Op, 0);
262     if (!OneOp || (OneOp->Common.AmlOpcode != AML_ONE_OP))
263     {
264         return (FALSE);
265     }
266 
267     /* Check for Store Op */
268 
269     StoreOp = OneOp->Common.Next;
270     if (!StoreOp || (StoreOp->Common.AmlOpcode != AML_STORE_OP))
271     {
272         return (FALSE);
273     }
274 
275     /* Check for Name Op with _T_ string */
276 
277     NamePathOp = AcpiPsGetArg (StoreOp, 1);
278     if (!NamePathOp ||
279         (NamePathOp->Common.AmlOpcode != AML_INT_NAMEPATH_OP))
280     {
281         return (FALSE);
282     }
283 
284     if (strncmp ((char *) (NamePathOp->Common.Value.Name), "_T_", 3))
285     {
286         return (FALSE);
287     }
288 
289     *Temp = (char *) (NamePathOp->Common.Value.Name);
290 
291     /* This is a Switch/Case control block */
292 
293     /* Ignore the One Op Predicate */
294 
295     OneOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
296 
297     /* Ignore the Store Op, but not the children */
298 
299     StoreOp->Common.DisasmOpcode = ACPI_DASM_IGNORE_SINGLE;
300 
301     /*
302      * First arg of Store Op is the Switch condition.
303      * Mark it as a Switch predicate and as a parameter list for paren
304      * closing and correct indentation.
305      */
306     PredicateOp = AcpiPsGetArg (StoreOp, 0);
307     PredicateOp->Common.DisasmOpcode = ACPI_DASM_SWITCH_PREDICATE;
308     PredicateOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
309 
310     /* Ignore the Name Op */
311 
312     NamePathOp->Common.DisasmFlags = ACPI_PARSEOP_IGNORE;
313 
314     /* Remaining opcodes are the Case statements (If/ElseIf's) */
315 
316     CurrentOp = StoreOp->Common.Next;
317     while (AcpiDmIsCaseBlock (CurrentOp))
318     {
319         /* Block is a Case structure */
320 
321         if (CurrentOp->Common.AmlOpcode == AML_ELSE_OP)
322         {
323             /* ElseIf */
324 
325             CurrentOp->Common.DisasmOpcode = ACPI_DASM_CASE;
326             CurrentOp = AcpiPsGetArg (CurrentOp, 0);
327         }
328 
329         /* If */
330 
331         CurrentOp->Common.DisasmOpcode = ACPI_DASM_CASE;
332 
333         /*
334          * Mark the parse tree for Case disassembly. There are two
335          * types of Case statements. The first type of statement begins with
336          * an LEqual. The second starts with an LNot and uses a Match statement
337          * on a Package of constants.
338          */
339         TempOp = AcpiPsGetArg (CurrentOp, 0);
340         switch (TempOp->Common.AmlOpcode)
341         {
342         case (AML_LOGICAL_EQUAL_OP):
343 
344             /* Ignore just the LEqual Op */
345 
346             TempOp->Common.DisasmOpcode = ACPI_DASM_IGNORE_SINGLE;
347 
348             /* Ignore the NamePath Op */
349 
350             TempOp = AcpiPsGetArg (TempOp, 0);
351             TempOp->Common.DisasmFlags = ACPI_PARSEOP_IGNORE;
352 
353             /*
354              * Second arg of LEqual will be the Case predicate.
355              * Mark it as a predicate and also as a parameter list for paren
356              * closing and correct indentation.
357              */
358             PredicateOp = TempOp->Common.Next;
359             PredicateOp->Common.DisasmOpcode = ACPI_DASM_SWITCH_PREDICATE;
360             PredicateOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
361             break;
362 
363         case (AML_LOGICAL_NOT_OP):
364 
365             /*
366              * The Package will be the predicate of the Case statement.
367              * It's under:
368              *            LNOT
369              *                LEQUAL
370              *                    MATCH
371              *                        PACKAGE
372              */
373 
374             /* Get the LEqual Op from LNot */
375 
376             TempOp = AcpiPsGetArg (TempOp, 0);
377 
378             /* Get the Match Op from LEqual */
379 
380             TempOp = AcpiPsGetArg (TempOp, 0);
381 
382             /* Get the Package Op from Match */
383 
384             PredicateOp = AcpiPsGetArg (TempOp, 0);
385 
386             /* Mark as parameter list for paren closing */
387 
388             PredicateOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
389 
390             /*
391              * The Package list would be too deeply indented if we
392              * chose to simply ignore the all the parent opcodes, so
393              * we rearrange the parse tree instead.
394              */
395 
396             /*
397              * Save the second arg of the If/Else Op which is the
398              * block code of code for this Case statement.
399              */
400             TempOp = AcpiPsGetArg (CurrentOp, 1);
401 
402             /*
403              * Move the Package Op to the child (predicate) of the
404              * Case statement.
405              */
406             CurrentOp->Common.Value.Arg = PredicateOp;
407             PredicateOp->Common.Parent = CurrentOp;
408 
409             /* Add the block code */
410 
411             PredicateOp->Common.Next = TempOp;
412             break;
413 
414         default:
415 
416             /* Should never get here */
417             break;
418         }
419 
420         /* Advance to next Case block */
421 
422         CurrentOp = CurrentOp->Common.Next;
423     }
424 
425     /* If CurrentOp is now an Else, then this is a Default block */
426 
427     if (CurrentOp && CurrentOp->Common.AmlOpcode == AML_ELSE_OP)
428     {
429         CurrentOp->Common.DisasmOpcode = ACPI_DASM_DEFAULT;
430     }
431 
432     /*
433      * From the first If advance to the Break op. It's possible to
434      * have an Else (Default) op here when there is only one Case
435      * statement, so check for it.
436      */
437     CurrentOp = StoreOp->Common.Next->Common.Next;
438     if (!CurrentOp)
439     {
440         return (FALSE);
441     }
442     if (CurrentOp->Common.AmlOpcode == AML_ELSE_OP)
443     {
444         CurrentOp = CurrentOp->Common.Next;
445     }
446 
447     /* Ignore the Break Op */
448 
449     CurrentOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
450     return (TRUE);
451 }
452 
453 
454 /*******************************************************************************
455  *
456  * FUNCTION:    AcpiDmIsCaseBlock
457  *
458  * PARAMETERS:  Op              - Object to test
459  *
460  * RETURN:      TRUE if Object is beginning of a Case block.
461  *
462  * DESCRIPTION: Determines if an Object is the beginning of a Case block for a
463  *              Switch/Case statement. Parse tree must be one of the following
464  *              forms:
465  *
466  *              Else (Optional)
467  *                  If
468  *                      LEqual
469  *                          -NamePath- _T_x
470  *
471  *              Else (Optional)
472  *                  If
473  *                      LNot
474  *                          LEqual
475  *                              Match
476  *                                  Package
477  *                                      ByteConst
478  *                                      -NamePath- _T_x
479  *
480  ******************************************************************************/
481 
482 static BOOLEAN
483 AcpiDmIsCaseBlock (
484     ACPI_PARSE_OBJECT       *Op)
485 {
486     ACPI_PARSE_OBJECT       *CurrentOp;
487 
488 
489     if (!Op)
490     {
491         return (FALSE);
492     }
493 
494     /* Look for an If or ElseIf */
495 
496     CurrentOp = Op;
497     if (CurrentOp->Common.AmlOpcode == AML_ELSE_OP)
498     {
499         CurrentOp = AcpiPsGetArg (CurrentOp, 0);
500         if (!CurrentOp)
501         {
502             return (FALSE);
503         }
504     }
505 
506     if (!CurrentOp || CurrentOp->Common.AmlOpcode != AML_IF_OP)
507     {
508         return (FALSE);
509     }
510 
511     /* Child must be LEqual or LNot */
512 
513     CurrentOp = AcpiPsGetArg (CurrentOp, 0);
514     if (!CurrentOp)
515     {
516         return (FALSE);
517     }
518 
519     switch (CurrentOp->Common.AmlOpcode)
520     {
521     case (AML_LOGICAL_EQUAL_OP):
522 
523         /* Next child must be NamePath with string _T_ */
524 
525         CurrentOp = AcpiPsGetArg (CurrentOp, 0);
526         if (!CurrentOp || !CurrentOp->Common.Value.Name ||
527             strncmp(CurrentOp->Common.Value.Name, "_T_", 3))
528         {
529             return (FALSE);
530         }
531         break;
532 
533     case (AML_LOGICAL_NOT_OP):
534 
535         /* Child of LNot must be LEqual op */
536 
537         CurrentOp = AcpiPsGetArg (CurrentOp, 0);
538         if (!CurrentOp || (CurrentOp->Common.AmlOpcode != AML_LOGICAL_EQUAL_OP))
539         {
540             return (FALSE);
541         }
542 
543         /* Child of LNot must be Match op */
544 
545         CurrentOp = AcpiPsGetArg (CurrentOp, 0);
546         if (!CurrentOp || (CurrentOp->Common.AmlOpcode != AML_MATCH_OP))
547         {
548             return (FALSE);
549         }
550 
551         /* First child of Match must be Package op */
552 
553         CurrentOp = AcpiPsGetArg (CurrentOp, 0);
554         if (!CurrentOp || (CurrentOp->Common.AmlOpcode != AML_PACKAGE_OP))
555         {
556             return (FALSE);
557         }
558 
559         /* Third child of Match must be NamePath with string _T_ */
560 
561         CurrentOp = AcpiPsGetArg (CurrentOp->Common.Parent, 2);
562         if (!CurrentOp || !CurrentOp->Common.Value.Name ||
563             strncmp(CurrentOp->Common.Value.Name, "_T_", 3))
564         {
565             return (FALSE);
566         }
567         break;
568 
569     default:
570 
571         return (FALSE);
572     }
573 
574     return (TRUE);
575 }
576