From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from EUR03-DB5-obe.outbound.protection.outlook.com (EUR03-DB5-obe.outbound.protection.outlook.com [40.107.4.41]) by mx.groups.io with SMTP id smtpd.web11.17174.1597245813542456829 for ; Wed, 12 Aug 2020 08:23:34 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@armh.onmicrosoft.com header.s=selector2-armh-onmicrosoft-com header.b=Rk4gi3UX; spf=pass (domain: arm.com, ip: 40.107.4.41, mailfrom: sami.mujawar@arm.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=armh.onmicrosoft.com; s=selector2-armh-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=qoUG7sYiElmZNNRdn6JkUv6nczDQjjKTeFTY48wUmIg=; b=Rk4gi3UXD675e8Ukz6OBKl1Jin7xy7isxOp3amJkSCVZYBzbRq+m+r8tmSYPwQ7oDFu3QPoJFlupTLVYdnpeh+tBtxvDOwV5ikilc8HsXvljh6OOZTPUntnGKkRXErEF5jUTWwRVIgyJ0SuFoakv9RRUienvUFGCunsN8vt1Rs0= Received: from AM6P193CA0124.EURP193.PROD.OUTLOOK.COM (2603:10a6:209:85::29) by VI1PR08MB3357.eurprd08.prod.outlook.com (2603:10a6:803:45::31) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3261.19; Wed, 12 Aug 2020 15:23:29 +0000 Received: from VE1EUR03FT056.eop-EUR03.prod.protection.outlook.com (2603:10a6:209:85:cafe::9d) by AM6P193CA0124.outlook.office365.com (2603:10a6:209:85::29) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3283.15 via Frontend Transport; Wed, 12 Aug 2020 15:23:29 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 63.35.35.123) smtp.mailfrom=arm.com; edk2.groups.io; dkim=pass (signature was verified) header.d=armh.onmicrosoft.com;edk2.groups.io; dmarc=bestguesspass action=none header.from=arm.com; Received-SPF: Pass (protection.outlook.com: domain of arm.com designates 63.35.35.123 as permitted sender) receiver=protection.outlook.com; client-ip=63.35.35.123; helo=64aa7808-outbound-1.mta.getcheckrecipient.com; Received: from 64aa7808-outbound-1.mta.getcheckrecipient.com (63.35.35.123) by VE1EUR03FT056.mail.protection.outlook.com (10.152.19.28) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3283.16 via Frontend Transport; Wed, 12 Aug 2020 15:23:29 +0000 Received: ("Tessian outbound bac899b43a54:v64"); Wed, 12 Aug 2020 15:23:29 +0000 X-CheckRecipientChecked: true X-CR-MTA-CID: 1e320eb185b62973 X-CR-MTA-TID: 64aa7808 Received: from c2bd16e94ae6.1 by 64aa7808-outbound-1.mta.getcheckrecipient.com id 51293C7F-BBE6-43AA-B028-F5D9A0D81FBF.1; Wed, 12 Aug 2020 15:23:23 +0000 Received: from EUR05-VI1-obe.outbound.protection.outlook.com by 64aa7808-outbound-1.mta.getcheckrecipient.com with ESMTPS id c2bd16e94ae6.1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384); Wed, 12 Aug 2020 15:23:23 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Z2pXLW0N2AG0TmeWf1SZiw/rVUVmOndIyka5TTPXF7KFC23x8QxV+C6AVmw+l/m8gVVloMlFI31G31ypEhhlIMhL0FWOsLuK371dUfweNd0b3jQWtgz/o7Y8YLmrfgIwicfmShP8UR/obtM2t4YkpTHSm4p5Byqc0xGZFYfxOQL0KEziY96ND8iq9caF0J5inPfrPoYf6eVFLIOxOVu4EV4n677qfh+RrpSprZysIBCA1Ux07Uqvlz2cN2DgMKDqLYDEX8ZCh7Uha22kvuhJiglgJZwcZOCkTGyl9FZf5il2hC9mqCyk0LfjNq9iF0gP+Hu1sfOKjV7qqX6ZEpZ99w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=qoUG7sYiElmZNNRdn6JkUv6nczDQjjKTeFTY48wUmIg=; b=Xb69+XRfHGzbdn8s4yU+IC1JvIJpT3uNnZR/eUcQ7IqZXhpp8QGhX1MGvKhICG9rKav7nJ5twzYrOt8HCc1kMH6HtbK2eMmWVfpDMRC/hdTCO12gkzFM0j0F4JvBYAC86u4znwoq8B93FycUV2by6zlsY/xeBEU37al02l15DUEz0/CK14sQbaMgElZWuViEPpKJY7kM++19CVNmjpzpi3ju8nZ8Cb3Qr4FqzYRdPoMVZfL2bfKtz1ZvuQK9JX1suk+4qD2W7u0VwI+mFl9UtXu7OgmkLq0bx0uNs54ghEDzPLcbcC8G8ODTY91XpjFu0PChfpiGU79RGSK9FQJz/A== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 40.67.248.234) smtp.rcpttodomain=edk2.groups.io smtp.mailfrom=arm.com; dmarc=bestguesspass action=none header.from=arm.com; dkim=none (message not signed); arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=armh.onmicrosoft.com; s=selector2-armh-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=qoUG7sYiElmZNNRdn6JkUv6nczDQjjKTeFTY48wUmIg=; b=Rk4gi3UXD675e8Ukz6OBKl1Jin7xy7isxOp3amJkSCVZYBzbRq+m+r8tmSYPwQ7oDFu3QPoJFlupTLVYdnpeh+tBtxvDOwV5ikilc8HsXvljh6OOZTPUntnGKkRXErEF5jUTWwRVIgyJ0SuFoakv9RRUienvUFGCunsN8vt1Rs0= Received: from DB6P193CA0008.EURP193.PROD.OUTLOOK.COM (2603:10a6:6:29::18) by AM0PR08MB3873.eurprd08.prod.outlook.com (2603:10a6:208:10c::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3261.18; Wed, 12 Aug 2020 15:23:20 +0000 Received: from DB5EUR03FT018.eop-EUR03.prod.protection.outlook.com (2603:10a6:6:29:cafe::6a) by DB6P193CA0008.outlook.office365.com (2603:10a6:6:29::18) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3283.15 via Frontend Transport; Wed, 12 Aug 2020 15:23:20 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 40.67.248.234) smtp.mailfrom=arm.com; edk2.groups.io; dkim=none (message not signed) header.d=none;edk2.groups.io; dmarc=bestguesspass action=none header.from=arm.com; Received-SPF: Pass (protection.outlook.com: domain of arm.com designates 40.67.248.234 as permitted sender) receiver=protection.outlook.com; client-ip=40.67.248.234; helo=nebula.arm.com; Received: from nebula.arm.com (40.67.248.234) by DB5EUR03FT018.mail.protection.outlook.com (10.152.20.69) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.3283.16 via Frontend Transport; Wed, 12 Aug 2020 15:23:20 +0000 Received: from AZ-NEU-EX04.Arm.com (10.251.24.32) by AZ-NEU-EX04.Arm.com (10.251.24.32) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2044.4; Wed, 12 Aug 2020 15:23:16 +0000 Received: from E107187.Arm.com (10.57.41.222) by mail.arm.com (10.251.24.32) with Microsoft SMTP Server id 15.1.2044.4 via Frontend Transport; Wed, 12 Aug 2020 15:23:16 +0000 From: "Sami Mujawar" To: CC: Sami Mujawar , , , , , , Subject: [PATCH v1 10/30] DynamicTablesPkg: AML utility interfaces Date: Wed, 12 Aug 2020 16:22:16 +0100 Message-ID: <20200812152236.31164-11-sami.mujawar@arm.com> X-Mailer: git-send-email 2.11.0.windows.3 In-Reply-To: <20200812152236.31164-1-sami.mujawar@arm.com> References: <20200812152236.31164-1-sami.mujawar@arm.com> MIME-Version: 1.0 X-EOPAttributedMessage: 1 X-MS-Office365-Filtering-HT: Tenant X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 627a6e05-e754-4af7-c201-08d83ed3aaa0 X-MS-TrafficTypeDiagnostic: AM0PR08MB3873:|VI1PR08MB3357: X-Microsoft-Antispam-PRVS: x-checkrecipientrouted: true NoDisclaimer: true X-MS-Oob-TLC-OOBClassifiers: OLM:9508;OLM:9508; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam-Untrusted: BCL:0; X-Microsoft-Antispam-Message-Info-Original: H8eXnrmgDweZzdrY/51n1XNZw/esKt5sz6blw6pXuu/Owm9f4S/WlvBpwb+jaLHXlF0X9djGICSfIpCSlovJX2zphptfdACBZG4Ae+p9RGiY4N9zlsJItGkgoTqoeVjiNT1MDT3XHxABhX8lSpSWECFD2goxNZgIVT9Skc/kqLZKllx5QwggYXPI6j04xQkYfaOPVovcxSd1MNXyFMS/s4DvbHz7lzCek/UY2U+Iv1nv2nGIq3Fk8RYJrEPZEREL/Q5u+Tw1QbgkKGDp27tcvnpZpljwbqv9kubmxHx01SspKncg+vxxvgRH9kCtQdJKa1WXOpAWtouY8wjOU131ab/W4Iz3Xs6wNbkUazjddWI8wXz9ILF3t2EsgQm9sW2NEXX1ARajZVjm+vjDw77cGQ== X-Forefront-Antispam-Report-Untrusted: CIP:40.67.248.234;CTRY:IE;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:nebula.arm.com;PTR:InfoDomainNonexistent;CAT:NONE;SFTY:;SFS:(4636009)(396003)(376002)(136003)(346002)(39860400002)(46966005)(7696005)(2906002)(19627235002)(478600001)(2616005)(70586007)(70206006)(83380400001)(1076003)(356005)(44832011)(82310400002)(30864003)(336012)(82740400003)(426003)(5660300002)(36756003)(186003)(4326008)(316002)(8936002)(6916009)(86362001)(8676002)(54906003)(47076004)(81166007)(26005);DIR:OUT;SFP:1101; X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR08MB3873 Return-Path: Sami.Mujawar@arm.com X-MS-Exchange-Transport-CrossTenantHeadersStripped: VE1EUR03FT056.eop-EUR03.prod.protection.outlook.com X-MS-Office365-Filtering-Correlation-Id-Prvs: b92b5439-2600-49a6-2cbd-08d83ed3a578 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 8pT9R0ccYYIB2Dq0Ks/khJX2xT8E9aMgBi33jxbh/oMYxcLL+y0lQqX1t3oWwCLcnT+CHIZEJgJcLm26hWKpS/hLYI+QxMop/P/OSDrgtkLO3uvJ9k2ig8gOJZP55VTqDl7YcrB3dIeYVmA8Nto6DJoHiV/oOkLfaYD53tkAEdZuORWIJ75kuUvM9lEDLqZbL8ln8Raa1KQUFCGVCY08gliGXnq3/8pK8W9XXVQB20wQ3xokroXi5sL2h3R9RviU0HwP1QPilqXbd9SNZ+QcsawkJky9EVScHIknPij2f+x13I/P/XgaXgb3/lDWYCWrrzgUma1p0+KjePN2DmZuB9VE2rQYAzKw/GH6fisNv/IVOc8ZYNDIltGn0oxWLhMTtKcBaTPnwLq4A90dICGmEA== X-Forefront-Antispam-Report: CIP:63.35.35.123;CTRY:IE;LANG:en;SCL:1;SRV:;IPV:CAL;SFV:NSPM;H:64aa7808-outbound-1.mta.getcheckrecipient.com;PTR:ec2-63-35-35-123.eu-west-1.compute.amazonaws.com;CAT:NONE;SFTY:;SFS:(4636009)(39860400002)(396003)(346002)(376002)(136003)(46966005)(30864003)(8936002)(81166007)(1076003)(336012)(54906003)(4326008)(2616005)(44832011)(82310400002)(186003)(82740400003)(316002)(6916009)(70206006)(70586007)(478600001)(86362001)(2906002)(36756003)(26005)(8676002)(36906005)(83380400001)(47076004)(5660300002)(19627235002)(426003)(7696005);DIR:OUT;SFP:1101; X-OriginatorOrg: arm.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Aug 2020 15:23:29.3500 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 627a6e05-e754-4af7-c201-08d83ed3aaa0 X-MS-Exchange-CrossTenant-Id: f34e5979-57d9-4aaa-ad4d-b122a662184d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=f34e5979-57d9-4aaa-ad4d-b122a662184d;Ip=[63.35.35.123];Helo=[64aa7808-outbound-1.mta.getcheckrecipient.com] X-MS-Exchange-CrossTenant-AuthSource: VE1EUR03FT056.eop-EUR03.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR08MB3357 Content-Type: text/plain From: Pierre Gondois The AML utility interfaces are a collection of helper functions that assist in computing the checksum, size and to propagate the node information as a result of addition or update of AML nodes. Signed-off-by: Pierre Gondois Signed-off-by: Sami Mujawar --- DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c | 906 ++++++++++++++++++++ DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h | 95 ++ 2 files changed, 1001 insertions(+) diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c b/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c new file mode 100644 index 0000000000000000000000000000000000000000..7ebd08f945c0c5ae87a3408152aefa6cc1e78234 --- /dev/null +++ b/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.c @@ -0,0 +1,906 @@ +/** @file + AML Utility. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include + +/** This function computes and updates the ACPI table checksum. + + @param [in] AcpiTable Pointer to an Acpi table. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AcpiPlatformChecksum ( + IN EFI_ACPI_DESCRIPTION_HEADER * AcpiTable + ) +{ + UINT8 * Ptr; + UINT8 Sum; + UINT32 Size; + + if (AcpiTable == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Ptr = (UINT8*)AcpiTable; + Size = AcpiTable->Length; + Sum = 0; + + // Set the checksum field to 0 first. + AcpiTable->Checksum = 0; + + // Compute the checksum. + while ((Size--) != 0) { + Sum = (UINT8)(Sum + (*Ptr++)); + } + + // Set the checksum. + AcpiTable->Checksum = (UINT8)(0xFF - Sum + 1); + + return EFI_SUCCESS; +} + +/** A callback function that computes the size of a Node and adds it to the + Size pointer stored in the Context. + Calling this function on the root node will compute the total size of the + AML bytestream. + + @param [in] Node Node to compute the size. + @param [in, out] Context Pointer holding the computed size. + (UINT32 *) Context. + @param [in, out] Status Pointer holding: + - At entry, the Status returned by the + last call to this exact function during + the enumeration; + - At exit, he returned status of the + call to this function. + Optional, can be NULL. + + @retval TRUE if the enumeration can continue or has finished without + interruption. + @retval FALSE if the enumeration needs to stopped or has stopped. +**/ +STATIC +BOOLEAN +EFIAPI +AmlComputeSizeCallback ( + IN AML_NODE_HEADER * Node, + IN OUT VOID * Context, + IN OUT EFI_STATUS * Status OPTIONAL + ) +{ + UINT32 Size; + EAML_PARSE_INDEX IndexPtr; + CONST AML_OBJECT_NODE * ParentNode; + + if (!IS_AML_NODE_VALID (Node) || + (Context == NULL)) { + ASSERT (0); + if (Status != NULL) { + *Status = EFI_INVALID_PARAMETER; + } + return FALSE; + } + + // Ignore the second fixed argument of method invocation nodes + // as the information stored there (the argument count) is not in the + // ACPI specification. + ParentNode = (CONST AML_OBJECT_NODE*)AmlGetParent (Node); + if (IS_AML_OBJECT_NODE (ParentNode) && + AmlNodeCompareOpCode (ParentNode, AML_METHOD_INVOC_OP, 0) && + AmlIsNodeFixedArgument (Node, &IndexPtr)) { + if (IndexPtr == EAmlParseIndexTerm1) { + if (Status != NULL) { + *Status = EFI_SUCCESS; + } + return TRUE; + } + } + + Size = *((UINT32*)Context); + + if (IS_AML_DATA_NODE (Node)) { + Size += ((AML_DATA_NODE*)Node)->Size; + } else if (IS_AML_OBJECT_NODE (Node) && + !AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)Node, + AML_IS_PSEUDO_OPCODE)) { + // Ignore pseudo-opcodes as they are not part of the + // ACPI specification. + + Size += (((AML_OBJECT_NODE*)Node)->AmlByteEncoding->OpCode == + AML_EXT_OP) ? 2 : 1; + + // Add the size of the PkgLen. + if (AmlNodeHasAttribute ( + (AML_OBJECT_NODE*)Node, + AML_HAS_PKG_LENGTH)) { + Size += AmlComputePkgLengthWidth (((AML_OBJECT_NODE*)Node)->PkgLen); + } + } + + // Check for overflow. + // The root node has a null size, thus the strict comparison. + if (*((UINT32*)Context) > Size) { + ASSERT (0); + *Status = EFI_INVALID_PARAMETER; + return FALSE; + } + + *((UINT32*)Context) = Size; + + if (Status != NULL) { + *Status = EFI_SUCCESS; + } + + return TRUE; +} + +/** Compute the size of a tree/sub-tree. + + @param [in] Node Node to compute the size. + @param [in, out] Size Pointer holding the computed size. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlComputeSize ( + IN CONST AML_NODE_HEADER * Node, + IN OUT UINT32 * Size + ) +{ + EFI_STATUS Status; + + if (!IS_AML_NODE_VALID (Node) || + (Size == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *Size = 0; + + AmlEnumTree ( + (AML_NODE_HEADER*)Node, + AmlComputeSizeCallback, + (VOID*)Size, + &Status + ); + + return Status; +} + +/** Get the value contained in an integer node. + + @param [in] Node Pointer to an integer node. + Must be an object node. + @param [out] Value Value contained in the integer node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlNodeGetIntegerValue ( + IN AML_OBJECT_NODE * Node, + OUT UINT64 * Value + ) +{ + AML_DATA_NODE * DataNode; + + if ((!IsIntegerNode (Node) && + !IsSpecialIntegerNode (Node)) || + (Value == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // For ZeroOp and OneOp, there is no data node. + if (IsSpecialIntegerNode (Node)) { + if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) { + *Value = 0; + } else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) { + *Value = 1; + } else { + // OnesOp cannot be handled: it represents a maximum value. + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; + } + + // For integer nodes, the value is in the first fixed argument. + DataNode = (AML_DATA_NODE*)Node->FixedArgs[EAmlParseIndexTerm0]; + if (!IS_AML_DATA_NODE (DataNode) || + (DataNode->DataType != EAmlNodeDataTypeUInt)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + switch (DataNode->Size) { + case 1: + { + *Value = *((UINT8*)(DataNode->Buffer)); + break; + } + case 2: + { + *Value = *((UINT16*)(DataNode->Buffer)); + break; + } + case 4: + { + *Value = *((UINT32*)(DataNode->Buffer)); + break; + } + case 8: + { + *Value = *((UINT64*)(DataNode->Buffer)); + break; + } + default: + { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } // switch + + return EFI_SUCCESS; +} + +/** Replace a Zero (AML_ZERO_OP) or One (AML_ONE_OP) object node + with a byte integer (AML_BYTE_PREFIX) object node having the same value. + + @param [in] Node Pointer to an integer node. + Must be an object node having ZeroOp or OneOp. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlUnwindSpecialInteger ( + IN AML_OBJECT_NODE * Node + ) +{ + EFI_STATUS Status; + + AML_DATA_NODE * NewDataNode; + UINT8 Value; + CONST AML_BYTE_ENCODING * ByteEncoding; + + if (!IsSpecialIntegerNode (Node)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Find the value. + if (AmlNodeCompareOpCode (Node, AML_ZERO_OP, 0)) { + Value = 0; + } else if (AmlNodeCompareOpCode (Node, AML_ONE_OP, 0)) { + Value = 1; + } else { + // OnesOp cannot be handled: it represents a maximum value. + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlCreateDataNode ( + EAmlNodeDataTypeUInt, + &Value, + sizeof (UINT8), + (AML_DATA_NODE**)&NewDataNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Change the encoding of the special node to a ByteOp encoding. + ByteEncoding = AmlGetByteEncodingByOpCode (AML_BYTE_PREFIX, 0); + if (ByteEncoding == NULL) { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto error_handler; + } + + // Update the ByteEncoding from ZERO_OP/ONE_OP to AML_BYTE_PREFIX. + Node->AmlByteEncoding = ByteEncoding; + + // Add the data node as the first fixed argument of the ByteOp object. + Status = AmlSetFixedArgument ( + (AML_OBJECT_NODE*)Node, + EAmlParseIndexTerm0, + (AML_NODE_HEADER*)NewDataNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + return Status; + +error_handler: + AmlDeleteTree ((AML_NODE_HEADER*)NewDataNode); + return Status; +} + +/** Set the value contained in an integer node. + + The OpCode is updated accordingly to the new value + (e.g.: If the original value was a UINT8 value, then the OpCode + would be AML_BYTE_PREFIX. If it the new value is a UINT16 + value then the OpCode will be updated to AML_WORD_PREFIX). + + @param [in] Node Pointer to an integer node. + Must be an object node. + @param [in] NewValue New value to write in the integer node. + @param [out] ValueWidthDiff Difference in number of bytes used to store + the new value. + Can be negative. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlNodeSetIntegerValue ( + IN AML_OBJECT_NODE * Node, + IN UINT64 NewValue, + OUT INT8 * ValueWidthDiff + ) +{ + EFI_STATUS Status; + AML_DATA_NODE * DataNode; + + UINT8 NewOpCode; + UINT8 NumberOfBytes; + + if ((!IsIntegerNode (Node) && + !IsSpecialIntegerNode (Node)) || + (ValueWidthDiff == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *ValueWidthDiff = 0; + // For ZeroOp and OneOp, there is no data node. + // Thus the object node is converted to a byte object node holding 0 or 1. + if (IsSpecialIntegerNode (Node)) { + switch (NewValue) { + case AML_ZERO_OP: + Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ZERO_OP, 0); + return EFI_SUCCESS; + case AML_ONE_OP: + Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (AML_ONE_OP, 0); + return EFI_SUCCESS; + default: + { + Status = AmlUnwindSpecialInteger (Node); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + // The AmlUnwindSpecialInteger functions converts a special integer + // node to a UInt8/Byte data node. Thus, the size increments by one: + // special integer are encoded as one byte (the opcode only) while byte + // integers are encoded as two bytes (the opcode + the value). + *ValueWidthDiff += sizeof (UINT8); + } + } // switch + } // IsSpecialIntegerNode (Node) + + // For integer nodes, the value is in the first fixed argument. + DataNode = (AML_DATA_NODE*)Node->FixedArgs[EAmlParseIndexTerm0]; + if (!IS_AML_DATA_NODE (DataNode) || + (DataNode->DataType != EAmlNodeDataTypeUInt)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // The value can be encoded with a special 0 or 1 OpCode. + // The AML_ONES_OP is not handled. + if (NewValue <= 1) { + NewOpCode = (NewValue == 0) ? AML_ZERO_OP : AML_ONE_OP; + Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0); + + // The value is encoded with a AML_ZERO_OP or AML_ONE_OP. + // This means there is no need for a DataNode containing the value. + // The change in size is equal to the size of the DataNode's buffer. + *ValueWidthDiff = -((INT8)DataNode->Size); + + // Detach and free the DataNode containing the integer value. + DataNode->NodeHeader.Parent = NULL; + Node->FixedArgs[EAmlParseIndexTerm0] = NULL; + Status = AmlDeleteNode ((AML_NODE_HEADER*)DataNode); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + return EFI_SUCCESS; + } + + // Check the number of bits needed to represent the value. + if (NewValue > MAX_UINT32) { + // Value is 64 bits. + NewOpCode = AML_QWORD_PREFIX; + NumberOfBytes = 8; + } else if (NewValue > MAX_UINT16) { + // Value is 32 bits. + NewOpCode = AML_DWORD_PREFIX; + NumberOfBytes = 4; + } else if (NewValue > MAX_UINT8) { + // Value is 16 bits. + NewOpCode = AML_WORD_PREFIX; + NumberOfBytes = 2; + } else { + // Value is 8 bits. + NewOpCode = AML_BYTE_PREFIX; + NumberOfBytes = 1; + } + + *ValueWidthDiff += (INT8)(NumberOfBytes - DataNode->Size); + + // Update the ByteEncoding as it may have changed between [8 .. 64] bits. + Node->AmlByteEncoding = AmlGetByteEncodingByOpCode (NewOpCode, 0); + if (Node->AmlByteEncoding == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Free the old DataNode buffer and allocate a buffer with the right size + // to store the new data. + if (*ValueWidthDiff != 0) { + FreePool (DataNode->Buffer); + DataNode->Buffer = AllocateZeroPool (NumberOfBytes); + if (DataNode->Buffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + DataNode->Size = NumberOfBytes; + } + + // Write the new value. + CopyMem (DataNode->Buffer, &NewValue, NumberOfBytes); + + return EFI_SUCCESS; +} + +/** Increment/decrement the value contained in the IntegerNode. + + @param [in] IntegerNode Pointer to an object node containing + an integer. + @param [in] IsIncrement Choose the operation to do: + - TRUE: Increment the Node's size and + the Node's count; + - FALSE: Decrement the Node's size and + the Node's count. + @param [in] Diff Value to add/subtract to the integer. + @param [out] ValueWidthDiff When modifying the integer, it can be + promoted/demoted, e.g. from UINT8 to UINT16. + Stores the change in width. + Can be negative. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlNodeUpdateIntegerValue ( + IN AML_OBJECT_NODE * IntegerNode, + IN BOOLEAN IsIncrement, + IN UINT64 Diff, + OUT INT8 * ValueWidthDiff + ) +{ + EFI_STATUS Status; + UINT64 Value; + + if (ValueWidthDiff == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the current value. + // Checks on the IntegerNode are done in the call. + Status = AmlNodeGetIntegerValue (IntegerNode, &Value); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Check for UINT64 over/underflow. + if ((IsIncrement && (Value > (MAX_UINT64 - Diff))) || + (!IsIncrement && (Value < Diff))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Compute the new value. + if (IsIncrement) { + Value += Diff; + } else { + Value -= Diff; + } + + Status = AmlNodeSetIntegerValue ( + IntegerNode, + Value, + ValueWidthDiff + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** Propagate the size information up the tree. + + The length of the ACPI table is updated in the RootNode, + but not the checksum. + + @param [in] Node Pointer to a node. + Must be a root node or an object node. + @param [in] IsIncrement Choose the operation to do: + - TRUE: Increment the Node's size and + the Node's count; + - FALSE: Decrement the Node's size and + the Node's count. + @param [in] Diff Value to add/subtract to the Node's size. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlPropagateSize ( + IN AML_NODE_HEADER * Node, + IN BOOLEAN IsIncrement, + IN UINT32 * Diff + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE * ObjectNode; + AML_NODE_HEADER * ParentNode; + + UINT32 Value; + UINT32 InitialPkgLenWidth; + UINT32 NewPkgLenWidth; + UINT32 ReComputedPkgLenWidth; + INT8 FieldWidthChange; + + if (!IS_AML_OBJECT_NODE (Node) && + !IS_AML_ROOT_NODE (Node)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (IS_AML_OBJECT_NODE (Node)) { + ObjectNode = (AML_OBJECT_NODE*)Node; + + // For BufferOp, the buffer size is stored in BufferSize. Therefore, + // BufferOp needs special handling to update the BufferSize. + // BufferSize must be updated before the PkgLen to accommodate any + // increment resulting from the update of the BufferSize. + // DefBuffer := BufferOp PkgLength BufferSize ByteList + // BufferOp := 0x11 + // BufferSize := TermArg => Integer + if (AmlNodeCompareOpCode (ObjectNode, AML_BUFFER_OP, 0)) { + // First fixed argument of BufferOp is an integer (BufferSize) + // (can be a BYTE, WORD, DWORD or QWORD). + // BufferSize is an object node. + Status = AmlNodeUpdateIntegerValue ( + (AML_OBJECT_NODE*)AmlGetFixedArgument ( + ObjectNode, + EAmlParseIndexTerm0 + ), + IsIncrement, + (UINT64)(*Diff), + &FieldWidthChange + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // FieldWidthChange is an integer. + // It must be positive if IsIncrement is TRUE, negative otherwise. + if ((IsIncrement && + (FieldWidthChange < 0)) || + (!IsIncrement && + (FieldWidthChange > 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Check for UINT32 overflow. + if (*Diff > (MAX_UINT32 - (UINT32)ABS (FieldWidthChange))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update Diff if the field width changed. + *Diff = (UINT32)(*Diff + ABS (FieldWidthChange)); + } // AML_BUFFER_OP node. + + // Update the PgkLen. + // Needs to be done at last to reflect the potential field width changes. + if (AmlNodeHasAttribute (ObjectNode, AML_HAS_PKG_LENGTH)) { + Value = ObjectNode->PkgLen; + + // Subtract the size of the PkgLen encoding. The size of the PkgLen + // encoding must be computed after having updated Value. + InitialPkgLenWidth = AmlComputePkgLengthWidth (Value); + Value -= InitialPkgLenWidth; + + // Check for an over/underflows. + // PkgLen is a 28 bit value, cf 20.2.4 Package Length Encoding + // i.e. the maximum value is (2^28 - 1) = ((BIT0 << 28) - 1). + if ((IsIncrement && ((((BIT0 << 28) - 1) - Value) < *Diff)) || + (!IsIncrement && (Value < *Diff))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update the size. + if (IsIncrement) { + Value += *Diff; + } else { + Value -= *Diff; + } + + // Compute the new PkgLenWidth. + NewPkgLenWidth = AmlComputePkgLengthWidth (Value); + if (NewPkgLenWidth == 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Add it to the Value. + Value += NewPkgLenWidth; + + // Check that adding the PkgLenWidth didn't trigger a domino effect, + // increasing the encoding width of the PkgLen again. + // The PkgLen is encoded on at most 4 bytes. It is possible to increase + // the PkgLen width if its encoding is on less than 3 bytes. + ReComputedPkgLenWidth = AmlComputePkgLengthWidth (Value); + if (ReComputedPkgLenWidth != NewPkgLenWidth) { + if ((ReComputedPkgLenWidth != 0) && + (ReComputedPkgLenWidth < 4)) { + // No need to recompute the PkgLen since a new threshold cannot + // be reached by incrementing the value by one. + Value += 1; + } else { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } + + *Diff += (InitialPkgLenWidth > ReComputedPkgLenWidth) ? + (InitialPkgLenWidth - ReComputedPkgLenWidth) : + (ReComputedPkgLenWidth - InitialPkgLenWidth); + ObjectNode->PkgLen = Value; + } // PkgLen update. + + // During CodeGeneration, the tree is incomplete and + // there is no root node at the top of the tree. Stop + // propagating the new size when finding a root node + // OR when a NULL parent is found. + ParentNode = AmlGetParent ((AML_NODE_HEADER*)Node); + if (ParentNode != NULL) { + // Propagate the size up the tree. + Status = AmlPropagateSize ( + Node->Parent, + IsIncrement, + Diff + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + } else if (IS_AML_ROOT_NODE (Node)) { + // Update the length field in the SDT header. + Value = ((AML_ROOT_NODE*)Node)->SdtHeader->Length; + + // Check for an over/underflows. + if ((IsIncrement && (Value > (MAX_UINT32 - *Diff))) || + (!IsIncrement && (Value < *Diff))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update the size. + if (IsIncrement) { + Value += *Diff; + } else { + Value -= *Diff; + } + + ((AML_ROOT_NODE*)Node)->SdtHeader->Length = Value; + } + + return EFI_SUCCESS; +} + +/** Propagate the node count information up the tree. + + @param [in] ObjectNode Pointer to an object node. + @param [in] IsIncrement Choose the operation to do: + - TRUE: Increment the Node's size and + the Node's count; + - FALSE: Decrement the Node's size and + the Node's count. + @param [in] NodeCount Number of nodes added/removed (depends on the + value of Operation). + @param [out] FieldWidthChange When modifying the integer, it can be + promoted/demoted, e.g. from UINT8 to UINT16. + Stores the change in width. + Can be negative. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlPropagateNodeCount ( + IN AML_OBJECT_NODE * ObjectNode, + IN BOOLEAN IsIncrement, + IN UINT8 NodeCount, + OUT INT8 * FieldWidthChange + ) +{ + EFI_STATUS Status; + + AML_NODE_HEADER * NodeCountArg; + UINT8 CurrNodeCount; + + // Currently there is no use case where (NodeCount > 1). + if (!IS_AML_OBJECT_NODE (ObjectNode) || + (FieldWidthChange == NULL) || + (NodeCount > 1)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *FieldWidthChange = 0; + + // Update the number of elements stored in PackageOp and VarPackageOp. + // The number of elements is stored as the first fixed argument. + // DefPackage := PackageOp PkgLength NumElements PackageElementList + // PackageOp := 0x12 + // DefVarPackage := VarPackageOp PkgLength VarNumElements PackageElementList + // VarPackageOp := 0x13 + // NumElements := ByteData + // VarNumElements := TermArg => Integer + NodeCountArg = AmlGetFixedArgument (ObjectNode, EAmlParseIndexTerm0); + if (AmlNodeCompareOpCode (ObjectNode, AML_PACKAGE_OP, 0)) { + // First fixed argument of PackageOp stores the number of elements + // in the package. It is an UINT8. + + // Check for over/underflow. + CurrNodeCount = *(((AML_DATA_NODE*)NodeCountArg)->Buffer); + if ((IsIncrement && (CurrNodeCount == MAX_UINT8)) || + (!IsIncrement && (CurrNodeCount == 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update the node count in the DataNode. + CurrNodeCount = IsIncrement ? (CurrNodeCount + 1) : (CurrNodeCount - 1); + *(((AML_DATA_NODE*)NodeCountArg)->Buffer) = CurrNodeCount; + } else if (AmlNodeCompareOpCode (ObjectNode, AML_VAR_PACKAGE_OP, 0)) { + // First fixed argument of PackageOp stores the number of elements + // in the package. It is an integer (can be a BYTE, WORD, DWORD, QWORD). + Status = AmlNodeUpdateIntegerValue ( + (AML_OBJECT_NODE*)NodeCountArg, + IsIncrement, + NodeCount, + FieldWidthChange + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + return EFI_SUCCESS; +} + +/** Propagate information up the tree. + + The information can be a new size, a new number of arguments. + + @param [in] Node Pointer to a node. + Must be a root node or an object node. + @param [in] IsIncrement Choose the operation to do: + - TRUE: Increment the Node's size and + the Node's count; + - FALSE: Decrement the Node's size and + the Node's count. + @param [in] Diff Value to add/subtract to the Node's size. + @param [in] NodeCount Number of nodes added/removed. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlPropagateInformation ( + IN AML_NODE_HEADER * Node, + IN BOOLEAN IsIncrement, + IN UINT32 Diff, + IN UINT8 NodeCount + ) +{ + EFI_STATUS Status; + INT8 FieldWidthChange; + + // Currently there is no use case where (NodeCount > 1). + if ((!IS_AML_ROOT_NODE (Node) && + !IS_AML_OBJECT_NODE (Node)) || + (NodeCount > 1)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Propagate the node count first as it may change the number of bytes + // needed to store the node count, and then impact FieldWidthChange. + if ((NodeCount != 0) && + IS_AML_OBJECT_NODE (Node)) { + Status = AmlPropagateNodeCount ( + (AML_OBJECT_NODE*)Node, + IsIncrement, + NodeCount, + &FieldWidthChange + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Propagate the potential field width change. + // Maximum change is between UINT8/UINT64: 8 bytes. + if ((ABS (FieldWidthChange) > 8) || + (IsIncrement && + ((FieldWidthChange < 0) || + ((Diff + (UINT8)FieldWidthChange) > MAX_UINT32))) || + (!IsIncrement && + ((FieldWidthChange > 0) || + (Diff < (UINT32)ABS (FieldWidthChange))))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + Diff = (UINT32)(Diff + (UINT8)ABS (FieldWidthChange)); + } + + // Diff can be zero if some data is updated without modifying the data size. + if (Diff != 0) { + Status = AmlPropagateSize (Node, IsIncrement, &Diff); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + return EFI_SUCCESS; +} diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h b/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h new file mode 100644 index 0000000000000000000000000000000000000000..c57d780140d4f053a225032fb6fc4e7b9991896d --- /dev/null +++ b/DynamicTablesPkg/Library/Common/AmlLib/Utils/AmlUtility.h @@ -0,0 +1,95 @@ +/** @file + AML Utility. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_UTILITY_H_ +#define AML_UTILITY_H_ + +#include + +/** This function computes and updates the ACPI table checksum. + + @param [in] AcpiTable Pointer to an Acpi table. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AcpiPlatformChecksum ( + IN EFI_ACPI_DESCRIPTION_HEADER * AcpiTable + ); + +/** Compute the size of a tree/sub-tree. + + @param [in] Node Node to compute the size. + @param [in, out] Size Pointer holding the computed size. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlComputeSize ( + IN CONST AML_NODE_HEADER * Node, + IN OUT UINT32 * Size + ); + +/** Set the value contained in an integer node. + + The OpCode is updated accordingly to the new value + (e.g.: If the original value was a UINT8 value, then the OpCode + would be AML_BYTE_PREFIX. If it the new value is a UINT16 + value then the OpCode will be updated to AML_WORD_PREFIX). + + @param [in] Node Pointer to an integer node. + Must be an object node. + @param [in] NewValue New value to write in the integer node. + @param [out] ValueWidthDiff Difference in number of bytes used to store + the new value. + Can be negative. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlNodeSetIntegerValue ( + IN AML_OBJECT_NODE * Node, + IN UINT64 NewValue, + OUT INT8 * ValueWidthDiff + ); + +/** Propagate information up the tree. + + The information can be a new size, a new number of arguments. + + @param [in] Node Pointer to a node. + Must be a root node or an object node. + @param [in] IsIncrement Choose the operation to do: + - TRUE: Increment the Node's size and + the Node's count; + - FALSE: Decrement the Node's size and + the Node's count. + @param [in] Diff Value to add/subtract to the Node's size. + @param [in] NodeCount Number of nodes added/removed. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlPropagateInformation ( + IN AML_NODE_HEADER * Node, + IN BOOLEAN IsIncrement, + IN UINT32 Diff, + IN UINT8 NodeCount + ); + +#endif // AML_UTILITY_H_ + -- 'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'