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 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