1 /******************************************************************************
2  *
3  * Module Name: adwalk - Disassembler routines for switch statements
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2022, 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 "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
AcpiDmProcessSwitch(ACPI_PARSE_OBJECT * Op)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
AcpiDmClearTempList(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  *              Temp            - Where the compiler temp name is returned
221  *                                  (_T_x)
222  *
223  * RETURN:      TRUE if While block can be converted to a Switch/Case block
224  *
225  * DESCRIPTION: Determines if While block is a Switch/Case statement. Modifies
226  *              parse tree to allow for Switch/Case disassembly during walk.
227  *
228  * EXAMPLE: Example of parse tree to be converted
229  *
230  *    While
231  *        One
232  *        Store
233  *            ByteConst
234  *             -NamePath-
235  *        If
236  *            LEqual
237  *                -NamePath-
238  *                Zero
239  *            Return
240  *                One
241  *        Else
242  *            Return
243  *                WordConst
244  *        Break
245  *
246  ******************************************************************************/
247 
248 BOOLEAN
AcpiDmIsSwitchBlock(ACPI_PARSE_OBJECT * Op,char ** Temp)249 AcpiDmIsSwitchBlock (
250     ACPI_PARSE_OBJECT       *Op,
251     char                    **Temp)
252 {
253     ACPI_PARSE_OBJECT       *OneOp;
254     ACPI_PARSE_OBJECT       *StoreOp;
255     ACPI_PARSE_OBJECT       *NamePathOp;
256     ACPI_PARSE_OBJECT       *PredicateOp;
257     ACPI_PARSE_OBJECT       *CurrentOp;
258     ACPI_PARSE_OBJECT       *TempOp;
259 
260 
261     /* Check for One Op Predicate */
262 
263     OneOp = AcpiPsGetArg (Op, 0);
264     if (!OneOp || (OneOp->Common.AmlOpcode != AML_ONE_OP))
265     {
266         return (FALSE);
267     }
268 
269     /* Check for Store Op */
270 
271     StoreOp = OneOp->Common.Next;
272     if (!StoreOp || (StoreOp->Common.AmlOpcode != AML_STORE_OP))
273     {
274         return (FALSE);
275     }
276 
277     /* Check for Name Op with _T_ string */
278 
279     NamePathOp = AcpiPsGetArg (StoreOp, 1);
280     if (!NamePathOp ||
281         (NamePathOp->Common.AmlOpcode != AML_INT_NAMEPATH_OP))
282     {
283         return (FALSE);
284     }
285 
286     if (strncmp ((char *) (NamePathOp->Common.Value.Name), "_T_", 3))
287     {
288         return (FALSE);
289     }
290 
291     *Temp = (char *) (NamePathOp->Common.Value.Name);
292 
293     /* This is a Switch/Case control block */
294 
295     /* Ignore the One Op Predicate */
296 
297     OneOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
298 
299     /* Ignore the Store Op, but not the children */
300 
301     StoreOp->Common.DisasmOpcode = ACPI_DASM_IGNORE_SINGLE;
302 
303     /*
304      * First arg of Store Op is the Switch condition.
305      * Mark it as a Switch predicate and as a parameter list for paren
306      * closing and correct indentation.
307      */
308     PredicateOp = AcpiPsGetArg (StoreOp, 0);
309     PredicateOp->Common.DisasmOpcode = ACPI_DASM_SWITCH_PREDICATE;
310     PredicateOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
311 
312     /* Ignore the Name Op */
313 
314     NamePathOp->Common.DisasmFlags = ACPI_PARSEOP_IGNORE;
315 
316     /* Remaining opcodes are the Case statements (If/ElseIf's) */
317 
318     CurrentOp = StoreOp->Common.Next;
319     while (AcpiDmIsCaseBlock (CurrentOp))
320     {
321         /* Block is a Case structure */
322 
323         if (CurrentOp->Common.AmlOpcode == AML_ELSE_OP)
324         {
325             /* ElseIf */
326 
327             CurrentOp->Common.DisasmOpcode = ACPI_DASM_CASE;
328             CurrentOp = AcpiPsGetArg (CurrentOp, 0);
329         }
330 
331         /* If */
332 
333         CurrentOp->Common.DisasmOpcode = ACPI_DASM_CASE;
334 
335         /*
336          * Mark the parse tree for Case disassembly. There are two
337          * types of Case statements. The first type of statement begins with
338          * an LEqual. The second starts with an LNot and uses a Match statement
339          * on a Package of constants.
340          */
341         TempOp = AcpiPsGetArg (CurrentOp, 0);
342         switch (TempOp->Common.AmlOpcode)
343         {
344         case (AML_LOGICAL_EQUAL_OP):
345 
346             /* Ignore just the LEqual Op */
347 
348             TempOp->Common.DisasmOpcode = ACPI_DASM_IGNORE_SINGLE;
349 
350             /* Ignore the NamePath Op */
351 
352             TempOp = AcpiPsGetArg (TempOp, 0);
353             TempOp->Common.DisasmFlags = ACPI_PARSEOP_IGNORE;
354 
355             /*
356              * Second arg of LEqual will be the Case predicate.
357              * Mark it as a predicate and also as a parameter list for paren
358              * closing and correct indentation.
359              */
360             PredicateOp = TempOp->Common.Next;
361             PredicateOp->Common.DisasmOpcode = ACPI_DASM_SWITCH_PREDICATE;
362             PredicateOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
363             break;
364 
365         case (AML_LOGICAL_NOT_OP):
366 
367             /*
368              * The Package will be the predicate of the Case statement.
369              * It's under:
370              *            LNOT
371              *                LEQUAL
372              *                    MATCH
373              *                        PACKAGE
374              */
375 
376             /* Get the LEqual Op from LNot */
377 
378             TempOp = AcpiPsGetArg (TempOp, 0);
379 
380             /* Get the Match Op from LEqual */
381 
382             TempOp = AcpiPsGetArg (TempOp, 0);
383 
384             /* Get the Package Op from Match */
385 
386             PredicateOp = AcpiPsGetArg (TempOp, 0);
387 
388             /* Mark as parameter list for paren closing */
389 
390             PredicateOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
391 
392             /*
393              * The Package list would be too deeply indented if we
394              * chose to simply ignore the all the parent opcodes, so
395              * we rearrange the parse tree instead.
396              */
397 
398             /*
399              * Save the second arg of the If/Else Op which is the
400              * block code of code for this Case statement.
401              */
402             TempOp = AcpiPsGetArg (CurrentOp, 1);
403 
404             /*
405              * Move the Package Op to the child (predicate) of the
406              * Case statement.
407              */
408             CurrentOp->Common.Value.Arg = PredicateOp;
409             PredicateOp->Common.Parent = CurrentOp;
410 
411             /* Add the block code */
412 
413             PredicateOp->Common.Next = TempOp;
414             break;
415 
416         default:
417 
418             /* Should never get here */
419             break;
420         }
421 
422         /* Advance to next Case block */
423 
424         CurrentOp = CurrentOp->Common.Next;
425     }
426 
427     /* If CurrentOp is now an Else, then this is a Default block */
428 
429     if (CurrentOp && CurrentOp->Common.AmlOpcode == AML_ELSE_OP)
430     {
431         CurrentOp->Common.DisasmOpcode = ACPI_DASM_DEFAULT;
432     }
433 
434     /*
435      * From the first If advance to the Break op. It's possible to
436      * have an Else (Default) op here when there is only one Case
437      * statement, so check for it.
438      */
439     CurrentOp = StoreOp->Common.Next->Common.Next;
440     if (!CurrentOp)
441     {
442         return (FALSE);
443     }
444     if (CurrentOp->Common.AmlOpcode == AML_ELSE_OP)
445     {
446         CurrentOp = CurrentOp->Common.Next;
447         if (!CurrentOp)
448         {
449             return (FALSE);
450         }
451     }
452 
453     /* Ignore the Break Op */
454 
455     CurrentOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
456     return (TRUE);
457 }
458 
459 
460 /*******************************************************************************
461  *
462  * FUNCTION:    AcpiDmIsCaseBlock
463  *
464  * PARAMETERS:  Op              - Object to test
465  *
466  * RETURN:      TRUE if Object is beginning of a Case block.
467  *
468  * DESCRIPTION: Determines if an Object is the beginning of a Case block for a
469  *              Switch/Case statement. Parse tree must be one of the following
470  *              forms:
471  *
472  *              Else (Optional)
473  *                  If
474  *                      LEqual
475  *                          -NamePath- _T_x
476  *
477  *              Else (Optional)
478  *                  If
479  *                      LNot
480  *                          LEqual
481  *                              Match
482  *                                  Package
483  *                                      ByteConst
484  *                                      -NamePath- _T_x
485  *
486  ******************************************************************************/
487 
488 static BOOLEAN
AcpiDmIsCaseBlock(ACPI_PARSE_OBJECT * Op)489 AcpiDmIsCaseBlock (
490     ACPI_PARSE_OBJECT       *Op)
491 {
492     ACPI_PARSE_OBJECT       *CurrentOp;
493 
494 
495     if (!Op)
496     {
497         return (FALSE);
498     }
499 
500     /* Look for an If or ElseIf */
501 
502     CurrentOp = Op;
503     if (CurrentOp->Common.AmlOpcode == AML_ELSE_OP)
504     {
505         CurrentOp = AcpiPsGetArg (CurrentOp, 0);
506         if (!CurrentOp)
507         {
508             return (FALSE);
509         }
510     }
511 
512     if (!CurrentOp || CurrentOp->Common.AmlOpcode != AML_IF_OP)
513     {
514         return (FALSE);
515     }
516 
517     /* Child must be LEqual or LNot */
518 
519     CurrentOp = AcpiPsGetArg (CurrentOp, 0);
520     if (!CurrentOp)
521     {
522         return (FALSE);
523     }
524 
525     switch (CurrentOp->Common.AmlOpcode)
526     {
527     case (AML_LOGICAL_EQUAL_OP):
528 
529         /* Next child must be NamePath with string _T_ */
530 
531         CurrentOp = AcpiPsGetArg (CurrentOp, 0);
532         if (!CurrentOp || !CurrentOp->Common.Value.Name ||
533             strncmp(CurrentOp->Common.Value.Name, "_T_", 3))
534         {
535             return (FALSE);
536         }
537         break;
538 
539     case (AML_LOGICAL_NOT_OP):
540 
541         /* Child of LNot must be LEqual op */
542 
543         CurrentOp = AcpiPsGetArg (CurrentOp, 0);
544         if (!CurrentOp || (CurrentOp->Common.AmlOpcode != AML_LOGICAL_EQUAL_OP))
545         {
546             return (FALSE);
547         }
548 
549         /* Child of LNot must be Match op */
550 
551         CurrentOp = AcpiPsGetArg (CurrentOp, 0);
552         if (!CurrentOp || (CurrentOp->Common.AmlOpcode != AML_MATCH_OP))
553         {
554             return (FALSE);
555         }
556 
557         /* First child of Match must be Package op */
558 
559         CurrentOp = AcpiPsGetArg (CurrentOp, 0);
560         if (!CurrentOp || (CurrentOp->Common.AmlOpcode != AML_PACKAGE_OP))
561         {
562             return (FALSE);
563         }
564 
565         /* Third child of Match must be NamePath with string _T_ */
566 
567         CurrentOp = AcpiPsGetArg (CurrentOp->Common.Parent, 2);
568         if (!CurrentOp || !CurrentOp->Common.Value.Name ||
569             strncmp(CurrentOp->Common.Value.Name, "_T_", 3))
570         {
571             return (FALSE);
572         }
573         break;
574 
575     default:
576 
577         return (FALSE);
578     }
579 
580     return (TRUE);
581 }
582