Home | History | Annotate | Line # | Download | only in common
      1 /******************************************************************************
      2  *
      3  * Module Name: adwalk - Disassembler routines for switch statements
      4  *
      5  *****************************************************************************/
      6 
      7 /*
      8  * Copyright (C) 2000 - 2025, 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
     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  *              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
    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
    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