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