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