mirror of
https://github.com/acpica/acpica/
synced 2025-02-25 18:04:08 +03:00
Merge pull request #198 from debox1/switch_case
Disassembler: Add Switch/Case disassembly support
This commit is contained in:
commit
832c3c4d08
@ -140,6 +140,13 @@ static void
|
||||
AcpiDmPromoteSubtree (
|
||||
ACPI_PARSE_OBJECT *StartOp);
|
||||
|
||||
static BOOLEAN
|
||||
AcpiDmIsSwitchBlock (
|
||||
ACPI_PARSE_OBJECT *Op);
|
||||
|
||||
static BOOLEAN
|
||||
AcpiDmIsCaseBlock (
|
||||
ACPI_PARSE_OBJECT *Op);
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
@ -1040,6 +1047,28 @@ AcpiDmDisassembleOneOp (
|
||||
AcpiDmNamestring (Op->Common.Value.Name);
|
||||
break;
|
||||
|
||||
case AML_WHILE_OP:
|
||||
|
||||
if (AcpiDmIsSwitchBlock(Op))
|
||||
{
|
||||
AcpiOsPrintf ("%s", "Switch");
|
||||
break;
|
||||
}
|
||||
|
||||
AcpiOsPrintf ("%s", OpInfo->Name);
|
||||
break;
|
||||
|
||||
case AML_IF_OP:
|
||||
|
||||
if (Op->Common.DisasmOpcode == ACPI_DASM_CASE)
|
||||
{
|
||||
AcpiOsPrintf ("%s", "Case");
|
||||
break;
|
||||
}
|
||||
|
||||
AcpiOsPrintf ("%s", OpInfo->Name);
|
||||
break;
|
||||
|
||||
case AML_ELSE_OP:
|
||||
|
||||
AcpiDmConvertToElseIf (Op);
|
||||
@ -1150,6 +1179,12 @@ AcpiDmConvertToElseIf (
|
||||
{
|
||||
/* Not a proper Else..If sequence, cannot convert to ElseIf */
|
||||
|
||||
if (OriginalElseOp->Common.DisasmOpcode == ACPI_DASM_DEFAULT)
|
||||
{
|
||||
AcpiOsPrintf ("%s", "Default");
|
||||
return;
|
||||
}
|
||||
|
||||
AcpiOsPrintf ("%s", "Else");
|
||||
return;
|
||||
}
|
||||
@ -1159,13 +1194,42 @@ AcpiDmConvertToElseIf (
|
||||
ElseOp = IfOp->Common.Next;
|
||||
if (ElseOp && ElseOp->Common.Next)
|
||||
{
|
||||
if (OriginalElseOp->Common.DisasmOpcode == ACPI_DASM_DEFAULT)
|
||||
{
|
||||
AcpiOsPrintf ("%s", "Default");
|
||||
return;
|
||||
}
|
||||
|
||||
AcpiOsPrintf ("%s", "Else");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Emit ElseIf, mark the IF as now an ELSEIF */
|
||||
if (OriginalElseOp->Common.DisasmOpcode == ACPI_DASM_DEFAULT)
|
||||
{
|
||||
/*
|
||||
* There is an ElseIf but in this case the Else is actually
|
||||
* a Default block for a Switch/Case statement. No conversion.
|
||||
*/
|
||||
AcpiOsPrintf ("%s", "Default");
|
||||
return;
|
||||
}
|
||||
|
||||
if (OriginalElseOp->Common.DisasmOpcode == ACPI_DASM_CASE)
|
||||
{
|
||||
/*
|
||||
* This ElseIf is actually a Case block for a Switch/Case
|
||||
* statement. Print Case but do not return so that we can
|
||||
* promote the subtree and keep the indentation level.
|
||||
*/
|
||||
AcpiOsPrintf ("%s", "Case");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Emit ElseIf, mark the IF as now an ELSEIF */
|
||||
|
||||
AcpiOsPrintf ("%s", "ElseIf");
|
||||
}
|
||||
|
||||
AcpiOsPrintf ("%s", "ElseIf");
|
||||
IfOp->Common.DisasmFlags |= ACPI_PARSEOP_ELSEIF;
|
||||
|
||||
/* The IF parent will now be the same as the original ELSE parent */
|
||||
@ -1256,3 +1320,400 @@ AcpiDmPromoteSubtree (
|
||||
Op = Op->Common.Next;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* FUNCTION: AcpiDmIsTempName
|
||||
*
|
||||
* PARAMETERS: Op - Object to be examined
|
||||
*
|
||||
* RETURN: TRUE if object is a temporary (_T_x) name
|
||||
*
|
||||
* DESCRIPTION: Determine if an object is a temporary name and ignore it.
|
||||
* Temporary names are only used for Switch statements. This
|
||||
* function depends on this restriced usage.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
BOOLEAN
|
||||
AcpiDmIsTempName (
|
||||
ACPI_PARSE_OBJECT *Op)
|
||||
{
|
||||
char *Temp;
|
||||
|
||||
if (Op->Common.AmlOpcode != AML_NAME_OP)
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
Temp = (char *)(Op->Common.Aml);
|
||||
++Temp;
|
||||
|
||||
if (strncmp(Temp, "_T_", 3))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
/* Ignore Op */
|
||||
|
||||
Op->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
|
||||
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* FUNCTION: AcpiDmIsSwitchBlock
|
||||
*
|
||||
* PARAMETERS: Op - While Object
|
||||
*
|
||||
* RETURN: TRUE if While block can be converted to a Switch/Case block
|
||||
*
|
||||
* DESCRIPTION: Determines if While block is a Switch/Case statement. Modifies
|
||||
* parse tree to allow for Switch/Case disassembly during walk.
|
||||
*
|
||||
* EXAMPLE: Example of parse tree to be converted
|
||||
*
|
||||
* While
|
||||
* One
|
||||
* Store
|
||||
* ByteConst
|
||||
* -NamePath-
|
||||
* If
|
||||
* LEqual
|
||||
* -NamePath-
|
||||
* Zero
|
||||
* Return
|
||||
* One
|
||||
* Else
|
||||
* Return
|
||||
* WordConst
|
||||
* Break
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
static BOOLEAN
|
||||
AcpiDmIsSwitchBlock (
|
||||
ACPI_PARSE_OBJECT *Op)
|
||||
{
|
||||
ACPI_PARSE_OBJECT *OneOp;
|
||||
ACPI_PARSE_OBJECT *StoreOp;
|
||||
ACPI_PARSE_OBJECT *NamePathOp;
|
||||
ACPI_PARSE_OBJECT *PredicateOp;
|
||||
ACPI_PARSE_OBJECT *CurrentOp;
|
||||
ACPI_PARSE_OBJECT *TempOp;
|
||||
|
||||
/* Check for One Op Predicate */
|
||||
|
||||
OneOp = AcpiPsGetArg (Op, 0);
|
||||
if (!OneOp || (OneOp->Common.AmlOpcode != AML_ONE_OP))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
/* Check for Store Op */
|
||||
|
||||
StoreOp = OneOp->Common.Next;
|
||||
if (!StoreOp || (StoreOp->Common.AmlOpcode != AML_STORE_OP))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
/* Check for Name Op with _T_ string */
|
||||
|
||||
NamePathOp = AcpiPsGetArg (StoreOp, 1);
|
||||
if (!NamePathOp || (NamePathOp->Common.AmlOpcode != AML_INT_NAMEPATH_OP))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
if (strncmp((char *)(NamePathOp->Common.Aml), "_T_", 3))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
/* This is a Switch/Case control block */
|
||||
|
||||
/* Ignore the One Op Predicate */
|
||||
|
||||
OneOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
|
||||
|
||||
/* Ignore the Store Op, but not the children */
|
||||
|
||||
StoreOp->Common.DisasmOpcode = ACPI_DASM_IGNORE_SINGLE;
|
||||
|
||||
/*
|
||||
* First arg of Store Op is the Switch condition.
|
||||
* Mark it as a Switch predicate and as a parameter list for paren
|
||||
* closing and correct indentation.
|
||||
*/
|
||||
PredicateOp = AcpiPsGetArg (StoreOp, 0);
|
||||
PredicateOp->Common.DisasmOpcode = ACPI_DASM_SWITCH_PREDICATE;
|
||||
PredicateOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
|
||||
|
||||
/* Ignore the Name Op */
|
||||
|
||||
NamePathOp->Common.DisasmFlags = ACPI_PARSEOP_IGNORE;
|
||||
|
||||
/* Remaining opcodes are the Case statements (If/ElseIf's) */
|
||||
|
||||
CurrentOp = StoreOp->Common.Next;
|
||||
while (AcpiDmIsCaseBlock (CurrentOp))
|
||||
{
|
||||
/* Block is a Case structure */
|
||||
|
||||
if (CurrentOp->Common.AmlOpcode == AML_ELSE_OP)
|
||||
{
|
||||
/* ElseIf */
|
||||
|
||||
CurrentOp->Common.DisasmOpcode = ACPI_DASM_CASE;
|
||||
CurrentOp = AcpiPsGetArg (CurrentOp, 0);
|
||||
}
|
||||
|
||||
/* If */
|
||||
|
||||
CurrentOp->Common.DisasmOpcode = ACPI_DASM_CASE;
|
||||
|
||||
/*
|
||||
* Mark the parse tree for Case disassembly. There are two
|
||||
* types of Case statements. The first type of statement begins with
|
||||
* an LEqual. The second starts with an LNot and uses a Match statement
|
||||
* on a Package of constants.
|
||||
*/
|
||||
TempOp = AcpiPsGetArg (CurrentOp, 0);
|
||||
switch (TempOp->Common.AmlOpcode)
|
||||
{
|
||||
case (AML_LEQUAL_OP):
|
||||
|
||||
/* Ignore just the LEqual Op */
|
||||
|
||||
TempOp->Common.DisasmOpcode = ACPI_DASM_IGNORE_SINGLE;
|
||||
|
||||
/* Ignore the NamePath Op */
|
||||
|
||||
TempOp = AcpiPsGetArg (TempOp, 0);
|
||||
TempOp->Common.DisasmFlags = ACPI_PARSEOP_IGNORE;
|
||||
|
||||
/*
|
||||
* Second arg of LEqual will be the Case predicate.
|
||||
* Mark it as a predicate and also as a parameter list for paren
|
||||
* closing and correct indentation.
|
||||
*/
|
||||
PredicateOp = TempOp->Common.Next;
|
||||
PredicateOp->Common.DisasmOpcode = ACPI_DASM_SWITCH_PREDICATE;
|
||||
PredicateOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
|
||||
|
||||
break;
|
||||
|
||||
case (AML_LNOT_OP):
|
||||
|
||||
/*
|
||||
* The Package will be the predicate of the Case statement.
|
||||
* It's under:
|
||||
* LNOT
|
||||
* LEQUAL
|
||||
* MATCH
|
||||
* PACKAGE
|
||||
*/
|
||||
|
||||
/* Get the LEqual Op from LNot */
|
||||
|
||||
TempOp = AcpiPsGetArg (TempOp, 0);
|
||||
|
||||
/* Get the Match Op from LEqual */
|
||||
|
||||
TempOp = AcpiPsGetArg (TempOp, 0);
|
||||
|
||||
/* Get the Package Op from Match */
|
||||
|
||||
PredicateOp = AcpiPsGetArg (TempOp, 0);
|
||||
|
||||
/* Mark as parameter list for paren closing */
|
||||
|
||||
PredicateOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMETER_LIST;
|
||||
|
||||
/*
|
||||
* The Package list would be too deeply indented if we
|
||||
* chose to simply ignore the all the parent opcodes, so
|
||||
* we rearrange the parse tree instead.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Save the second arg of the If/Else Op which is the
|
||||
* block code of code for this Case statement.
|
||||
*/
|
||||
TempOp = AcpiPsGetArg (CurrentOp, 1);
|
||||
|
||||
/*
|
||||
* Move the Package Op to the child (predicate) of the
|
||||
* Case statement.
|
||||
*/
|
||||
CurrentOp->Common.Value.Arg = PredicateOp;
|
||||
PredicateOp->Common.Parent = CurrentOp;
|
||||
|
||||
/* Add the block code */
|
||||
|
||||
PredicateOp->Common.Next = TempOp;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
/* Should never get here */
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Advance to next Case block */
|
||||
|
||||
CurrentOp = CurrentOp->Common.Next;
|
||||
}
|
||||
|
||||
/* If CurrentOp is now an Else, then this is a Default block */
|
||||
|
||||
if (CurrentOp && CurrentOp->Common.AmlOpcode == AML_ELSE_OP)
|
||||
{
|
||||
CurrentOp->Common.DisasmOpcode = ACPI_DASM_DEFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* From the first If advance to the Break op. It's possible to
|
||||
* have an Else (Default) op here when there is only one Case
|
||||
* statement, so check for it.
|
||||
*/
|
||||
CurrentOp = StoreOp->Common.Next->Common.Next;
|
||||
if (CurrentOp->Common.AmlOpcode == AML_ELSE_OP)
|
||||
{
|
||||
CurrentOp = CurrentOp->Common.Next;
|
||||
}
|
||||
|
||||
/* Ignore the Break Op */
|
||||
|
||||
CurrentOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
|
||||
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* FUNCTION: AcpiDmIsCaseBlock
|
||||
*
|
||||
* PARAMETERS: Op - Object to test
|
||||
*
|
||||
* RETURN: TRUE if Object is beginning of a Case block.
|
||||
*
|
||||
* DESCRIPTION: Determines if an Object is the beginning of a Case block for a
|
||||
* Switch/Case statement. Parse tree must be one of the following
|
||||
* forms:
|
||||
*
|
||||
* Else (Optional)
|
||||
* If
|
||||
* LEqual
|
||||
* -NamePath- _T_x
|
||||
*
|
||||
* Else (Optional)
|
||||
* If
|
||||
* LNot
|
||||
* LEqual
|
||||
* Match
|
||||
* Package
|
||||
* ByteConst
|
||||
* -NamePath- _T_x
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
static BOOLEAN
|
||||
AcpiDmIsCaseBlock (
|
||||
ACPI_PARSE_OBJECT *Op)
|
||||
{
|
||||
ACPI_PARSE_OBJECT *CurrentOp;
|
||||
|
||||
if (!Op)
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
/* Look for an If or ElseIf */
|
||||
|
||||
CurrentOp = Op;
|
||||
if (CurrentOp->Common.AmlOpcode == AML_ELSE_OP)
|
||||
{
|
||||
CurrentOp = AcpiPsGetArg (CurrentOp, 0);
|
||||
if (!CurrentOp)
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
if (!CurrentOp || CurrentOp->Common.AmlOpcode != AML_IF_OP)
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
/* Child must be LEqual or LNot */
|
||||
|
||||
CurrentOp = AcpiPsGetArg (CurrentOp, 0);
|
||||
if (!CurrentOp)
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
switch (CurrentOp->Common.AmlOpcode)
|
||||
{
|
||||
case (AML_LEQUAL_OP):
|
||||
|
||||
/* Next child must be NamePath with string _T_ */
|
||||
|
||||
CurrentOp = AcpiPsGetArg (CurrentOp, 0);
|
||||
if (!CurrentOp || !CurrentOp->Common.Value.Name ||
|
||||
strncmp(CurrentOp->Common.Value.Name, "_T_", 3))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case (AML_LNOT_OP):
|
||||
|
||||
/* Child of LNot must be LEqual op */
|
||||
|
||||
CurrentOp = AcpiPsGetArg (CurrentOp, 0);
|
||||
if (!CurrentOp || (CurrentOp->Common.AmlOpcode != AML_LEQUAL_OP))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
/* Child of LNot must be Match op */
|
||||
|
||||
CurrentOp = AcpiPsGetArg (CurrentOp, 0);
|
||||
if (!CurrentOp || (CurrentOp->Common.AmlOpcode != AML_MATCH_OP))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
/* First child of Match must be Package op */
|
||||
|
||||
CurrentOp = AcpiPsGetArg (CurrentOp, 0);
|
||||
if (!CurrentOp || (CurrentOp->Common.AmlOpcode != AML_PACKAGE_OP))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
/* Third child of Match must be NamePath with string _T_ */
|
||||
|
||||
CurrentOp = AcpiPsGetArg (CurrentOp->Common.Parent, 2);
|
||||
if (!CurrentOp || !CurrentOp->Common.Value.Name ||
|
||||
strncmp(CurrentOp->Common.Value.Name, "_T_", 3))
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
return (TRUE);
|
||||
}
|
||||
|
@ -527,6 +527,20 @@ AcpiDmDescendingOp (
|
||||
return (AE_CTRL_DEPTH);
|
||||
}
|
||||
|
||||
if (AcpiDmIsTempName(Op))
|
||||
{
|
||||
/* Ignore compiler generated temporary names */
|
||||
|
||||
return (AE_CTRL_DEPTH);
|
||||
}
|
||||
|
||||
if (Op->Common.DisasmOpcode == ACPI_DASM_IGNORE_SINGLE)
|
||||
{
|
||||
/* Ignore this op, but not it's children */
|
||||
|
||||
return (AE_OK);
|
||||
}
|
||||
|
||||
if (Op->Common.AmlOpcode == AML_IF_OP)
|
||||
{
|
||||
NextOp = AcpiPsGetDepthNext (NULL, Op);
|
||||
@ -961,7 +975,8 @@ AcpiDmAscendingOp (
|
||||
ACPI_PARSE_OBJECT *ParentOp;
|
||||
|
||||
|
||||
if (Op->Common.DisasmFlags & ACPI_PARSEOP_IGNORE)
|
||||
if (Op->Common.DisasmFlags & ACPI_PARSEOP_IGNORE ||
|
||||
Op->Common.DisasmOpcode == ACPI_DASM_IGNORE_SINGLE)
|
||||
{
|
||||
/* Ignore this op -- it was handled elsewhere */
|
||||
|
||||
@ -1121,9 +1136,12 @@ AcpiDmAscendingOp (
|
||||
|
||||
/*
|
||||
* Just completed a parameter node for something like "Buffer (param)".
|
||||
* Close the paren and open up the term list block with a brace
|
||||
* Close the paren and open up the term list block with a brace.
|
||||
*
|
||||
* Switch predicates don't have a Next node but require a closing paren
|
||||
* and opening brace.
|
||||
*/
|
||||
if (Op->Common.Next)
|
||||
if (Op->Common.Next || Op->Common.DisasmOpcode == ACPI_DASM_SWITCH_PREDICATE)
|
||||
{
|
||||
AcpiOsPrintf (")");
|
||||
|
||||
@ -1138,6 +1156,13 @@ AcpiDmAscendingOp (
|
||||
AcpiDmPredefinedDescription (ParentOp);
|
||||
}
|
||||
|
||||
/* Correct the indentation level for Switch and Case predicates */
|
||||
|
||||
if (Op->Common.DisasmOpcode == ACPI_DASM_SWITCH_PREDICATE)
|
||||
{
|
||||
--Level;
|
||||
}
|
||||
|
||||
AcpiOsPrintf ("\n");
|
||||
AcpiDmIndent (Level - 1);
|
||||
AcpiOsPrintf ("{\n");
|
||||
|
@ -723,6 +723,10 @@ AcpiDmDisassembleOneOp (
|
||||
ACPI_OP_WALK_INFO *Info,
|
||||
ACPI_PARSE_OBJECT *Op);
|
||||
|
||||
BOOLEAN
|
||||
AcpiDmIsTempName (
|
||||
ACPI_PARSE_OBJECT *Op);
|
||||
|
||||
UINT32
|
||||
AcpiDmListType (
|
||||
ACPI_PARSE_OBJECT *Op);
|
||||
|
@ -995,7 +995,7 @@ typedef union acpi_parse_value
|
||||
char AmlOpName[16]) /* Op name (debug only) */
|
||||
|
||||
|
||||
/* Flags for DisasmFlags field above */
|
||||
/* Internal opcodes for DisasmOpcode field above */
|
||||
|
||||
#define ACPI_DASM_BUFFER 0x00 /* Buffer is a simple data buffer */
|
||||
#define ACPI_DASM_RESOURCE 0x01 /* Buffer is a Resource Descriptor */
|
||||
@ -1008,7 +1008,10 @@ typedef union acpi_parse_value
|
||||
#define ACPI_DASM_LNOT_PREFIX 0x08 /* Start of a LNotEqual (etc.) pair of opcodes */
|
||||
#define ACPI_DASM_LNOT_SUFFIX 0x09 /* End of a LNotEqual (etc.) pair of opcodes */
|
||||
#define ACPI_DASM_HID_STRING 0x0A /* String is a _HID or _CID */
|
||||
#define ACPI_DASM_IGNORE 0x0B /* Not used at this time */
|
||||
#define ACPI_DASM_IGNORE_SINGLE 0x0B /* Ignore the opcode but not it's children */
|
||||
#define ACPI_DASM_SWITCH_PREDICATE 0x0C /* Object is a predicate for a Switch or Case block */
|
||||
#define ACPI_DASM_CASE 0x0D /* If/Else is a Case in a Switch/Case block */
|
||||
#define ACPI_DASM_DEFAULT 0x0E /* Else is a Default in a Switch/Case block */
|
||||
|
||||
/*
|
||||
* Generic operation (for example: If, While, Store)
|
||||
|
Loading…
x
Reference in New Issue
Block a user