From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from EUR05-DB8-obe.outbound.protection.outlook.com (EUR05-DB8-obe.outbound.protection.outlook.com [40.107.20.65]) by mx.groups.io with SMTP id smtpd.web12.16984.1597245834305695257 for ; Wed, 12 Aug 2020 08:23:54 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@armh.onmicrosoft.com header.s=selector2-armh-onmicrosoft-com header.b=8FH6vH04; spf=pass (domain: arm.com, ip: 40.107.20.65, 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=Au1UZhyONIm0A1c8SSuHnzy/w+9C5XvpxMuoqpGyQg8=; b=8FH6vH04JpaWCieRfeJ6HwQmIlh5jt76jUja7HtaaUdq5XlLi65CEaROWjI5HMLCCg4/5q2D1i+nri9Z7nA9ic4Dg7tTaETQ6LCmFHE8X0Vx2SxIy4o2svfSsm4YEWFowhfkFMr8YFlABGqPdlLjHz/3IM6fBFo122mUqt0ET7A= Received: from AM6P193CA0124.EURP193.PROD.OUTLOOK.COM (2603:10a6:209:85::29) by HE1PR0802MB2538.eurprd08.prod.outlook.com (2603:10a6:3:e1::15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3261.16; Wed, 12 Aug 2020 15:23:50 +0000 Received: from AM5EUR03FT023.eop-EUR03.prod.protection.outlook.com (2603:10a6:209:85:cafe::6) 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:49 +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 AM5EUR03FT023.mail.protection.outlook.com (10.152.16.169) 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:49 +0000 Received: ("Tessian outbound 195a290eb161:v64"); Wed, 12 Aug 2020 15:23:49 +0000 X-CheckRecipientChecked: true X-CR-MTA-CID: 8efa3f7f5850ac0e X-CR-MTA-TID: 64aa7808 Received: from 0a744d12b6d6.1 by 64aa7808-outbound-1.mta.getcheckrecipient.com id 22C121D8-B975-442A-B154-65B350749D66.1; Wed, 12 Aug 2020 15:23:44 +0000 Received: from EUR05-DB8-obe.outbound.protection.outlook.com by 64aa7808-outbound-1.mta.getcheckrecipient.com with ESMTPS id 0a744d12b6d6.1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384); Wed, 12 Aug 2020 15:23:44 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=e+w+m6qExR9r+rMHdhSnabOsv87nfY0fGlaKfbu5YnQQh/fLDacJIsyeO26hnT5+dNNoQGiTHoxC6EN5yPDDsby0MhNXv24usP9rzWNR0adHa/I1ZkB9/45StuuUNwNZdQSPnaUgWIq/iBbtU6h7DKk3u/Oc0nUDrYS/I7truWGb8iSQFjvLRNfRTcB8oVkMm/vuPYoYBmF0rl/LrJV/kPQ2HV7q3IdLAcOzUPMWLIO0ImUL6TBXBuToU+b39jNo+65HfvDIlOjjlmLi2niXmMHAx8rwNM9sadDaCHXOpOysYH0JEn5eoBk743SbK8SY395rM0ZKaH+MMpDmUw7I/g== 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=Au1UZhyONIm0A1c8SSuHnzy/w+9C5XvpxMuoqpGyQg8=; b=fLiFQrqYPTo93bax1HDmxzPGEdNu1Ea2PbP7HqnZdid0bhJKINThyIiuJuq2j3cb5LW7NLt3qLLQ1igQq5a/tki23W7Q+7TbHQTs1nIefHLdr3vtOnRJKVy15l0lxTmYHfvFH+e4byD6JfjStCgC0eCdoszmP8Y4vskR5ZwUo1Ao7H7aI7p0UZOkbaelJZ4dleccbStfG8CiiLh2lAOvUoDowkAsZTS2lyGDkdKvHFejzY3raVvF7RZuvuoDGLkrfuALAJpILxXJsEyzRULKv96VaNBDjQdyyq0QhJXeyeiMrDJqRdA241BDdPZJEGyNegrCfDp/TMmKXdPF1zm3YQ== 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=Au1UZhyONIm0A1c8SSuHnzy/w+9C5XvpxMuoqpGyQg8=; b=8FH6vH04JpaWCieRfeJ6HwQmIlh5jt76jUja7HtaaUdq5XlLi65CEaROWjI5HMLCCg4/5q2D1i+nri9Z7nA9ic4Dg7tTaETQ6LCmFHE8X0Vx2SxIy4o2svfSsm4YEWFowhfkFMr8YFlABGqPdlLjHz/3IM6fBFo122mUqt0ET7A= Received: from DB6PR07CA0062.eurprd07.prod.outlook.com (2603:10a6:6:2a::24) by PR3PR08MB5673.eurprd08.prod.outlook.com (2603:10a6:102:86::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3283.15; Wed, 12 Aug 2020 15:23:41 +0000 Received: from DB5EUR03FT059.eop-EUR03.prod.protection.outlook.com (2603:10a6:6:2a:cafe::12) by DB6PR07CA0062.outlook.office365.com (2603:10a6:6:2a::24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3283.7 via Frontend Transport; Wed, 12 Aug 2020 15:23:41 +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 DB5EUR03FT059.mail.protection.outlook.com (10.152.21.175) 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:41 +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:39 +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:38 +0000 From: "Sami Mujawar" To: CC: Sami Mujawar , , , , , , Subject: [PATCH v1 24/30] DynamicTablesPkg: AmlLib APIs Date: Wed, 12 Aug 2020 16:22:30 +0100 Message-ID: <20200812152236.31164-25-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: 29ffc730-7f9c-47da-3b8f-08d83ed3b6d4 X-MS-TrafficTypeDiagnostic: PR3PR08MB5673:|HE1PR0802MB2538: X-Microsoft-Antispam-PRVS: x-checkrecipientrouted: true NoDisclaimer: true X-MS-Oob-TLC-OOBClassifiers: OLM:2000;OLM:2000; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam-Untrusted: BCL:0; X-Microsoft-Antispam-Message-Info-Original: d7xkJtq7wQMMukR2KjjwocD6Hjq0UccbulwZV8VLUwW8LmVi2rOeugPSS69AxUzVXUlhBFWM40k8z3HdUhCiuNvCBElX5P6M4fxT+R71lvcEZFU7ZYdBcMeJKpaJyMvduNA4J0MUEt3I4YYhFP6/zu3qUaywHARfH39UF+7QB+C/5iAKmcNQgxwyBnwp9CbxgD9gwsMWS07RidG4LW6dz5O7Vr4/stJIiyJ5A1f0xPvgWMhKhDioaQHn0/6h3j37LtA9QkSpmY6CY9Zegstqh1kGJuHBZkWdPNR846IIlEAzy7KazBCT5uFBrKWxBvUkfE7AzISt/OU8qSyTmIBLmkpGhUv1qJr7woiwu3DXCiSHoSLCKv6HoC2tWkOdXn4TpfHUk9pnzWP5nztM8A8y3g== 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;SFS:(4636009)(396003)(136003)(376002)(39860400002)(346002)(46966005)(30864003)(6916009)(2906002)(1076003)(86362001)(54906003)(19627235002)(4326008)(8676002)(83380400001)(5660300002)(26005)(8936002)(336012)(316002)(7696005)(47076004)(82310400002)(70206006)(36756003)(70586007)(356005)(478600001)(44832011)(6666004)(82740400003)(186003)(81166007)(426003)(2616005)(579004)(559001);DIR:OUT;SFP:1101; X-MS-Exchange-Transport-CrossTenantHeadersStamped: PR3PR08MB5673 Return-Path: Sami.Mujawar@arm.com X-MS-Exchange-Transport-CrossTenantHeadersStripped: AM5EUR03FT023.eop-EUR03.prod.protection.outlook.com X-MS-Office365-Filtering-Correlation-Id-Prvs: 06f2cae2-ee0b-4285-eb2e-08d83ed3b1dc X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: bBw7OmnPqjHK3dU/VCunBH6XcjLFU5CzwesGjJ/pwUcx74yK2C5MoMtxTfgzDqLtTk/BtXW96ySqhxk2rjJFNqccD9Sj9z8PhJCgfHYW/bmUkjt3KOd/qDi/S3I4jwcPS/uvq+nkCdpxS4xDG5B/0jD48GlxepvSLZPNu2Q7TV5J2OtIiEAlQdVDkSRj7+0hFoZ5fgYurxnFOaihECLegF1xeoyhpti6r7KCfgjDi/hSLSOKnhTDsWoyO1eO/ef4cazat1cjippEa6p5Co6W7/fqbm8MjiBN4AaGIvRP4gG4pue0NzYpCWdx71GNYWutVrhQMAz0oJbhk9KlPqBfmrK65GQSrl1ph3JIFuO58sn4jPybSkIRLHZF5aMqnpb0B1LHOXCHT2gkIQQM7rhbyg== 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)(346002)(136003)(376002)(39860400002)(396003)(46966005)(4326008)(81166007)(30864003)(8676002)(19627235002)(36756003)(47076004)(82740400003)(478600001)(83380400001)(44832011)(82310400002)(5660300002)(6666004)(54906003)(8936002)(26005)(2906002)(186003)(7696005)(86362001)(316002)(2616005)(336012)(426003)(1076003)(36906005)(70206006)(6916009)(70586007)(559001)(579004);DIR:OUT;SFP:1101; X-OriginatorOrg: arm.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Aug 2020 15:23:49.8559 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 29ffc730-7f9c-47da-3b8f-08d83ed3b6d4 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: AM5EUR03FT023.eop-EUR03.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: HE1PR0802MB2538 Content-Type: text/plain From: Pierre Gondois AmlLib library implements an AML parser, AML tree interface, serialiser, code generator and other interfaces to generate Definition Block tables. The AmlLib APIs are a collection of interfaces that enable parsing, iterating, modifying, adding, and serialising AML data to generate a Definition Block table. The AmlLib APIs are declared in Include\AmlLib\AmlLib.h Signed-off-by: Pierre Gondois Signed-off-by: Sami Mujawar --- DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h | 631 ++++++++++++++++++++ DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c | 382 ++++++++++++ DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c | 219 +++++++ DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h | 93 +++ DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c | 320 ++++++++++ 5 files changed, 1645 insertions(+) diff --git a/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h b/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h new file mode 100644 index 0000000000000000000000000000000000000000..1dcb93861436851e848f9cb5fd69621ccd7bf7a1 --- /dev/null +++ b/DynamicTablesPkg/Include/Library/AmlLib/AmlLib.h @@ -0,0 +1,631 @@ +/** @file + AML Lib. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_LIB_H_ +#define AML_LIB_H_ + +/** + @mainpage Dynamic AML Generation + @{ + @par Summary + @{ + ACPI tables are categorized as data tables and definition block + tables. Dynamic Tables Framework currently supports generation of ACPI + data tables. Generation of definition block tables is difficult as these + tables are encoded in ACPI Machine Language (AML), which has a complex + grammar. + + Dynamic AML Generation is an extension to the Dynamic tables Framework. + One of the techniques used to simplify definition block generation is to + fixup a template SSDT table. + + Dynamic AML aims to provide a framework that allows fixing up of an ACPI + SSDT template with appropriate information about the hardware. + + This framework consists of an: + - AMLLib core that implements a rich set of interfaces to parse, traverse + and update AML data. + - AMLLib library APIs that provides interfaces to search and updates nodes + in the AML namespace. + @} + @} +*/ + +#include + +#ifndef AML_HANDLE + +/** Node handle. +*/ +typedef void* AML_NODE_HANDLE; + +/** Root Node handle. +*/ +typedef void* AML_ROOT_NODE_HANDLE; + +/** Object Node handle. +*/ +typedef void* AML_OBJECT_NODE_HANDLE; + +/** Data Node handle. +*/ +typedef void* AML_DATA_NODE_HANDLE; + +#endif // AML_HANDLE + +/** Parse the definition block. + + The function parses the whole AML blob. It starts with the ACPI DSDT/SSDT + header and then parses the AML bytestream. + A tree structure is returned via the RootPtr. + The tree must be deleted with the AmlDeleteTree function. + + @ingroup UserApis + + @param [in] DefinitionBlock Pointer to the definition block. + @param [out] RootPtr Pointer to the root node of the AML tree. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlParseDefinitionBlock ( + IN CONST EFI_ACPI_DESCRIPTION_HEADER * DefinitionBlock, + OUT AML_ROOT_NODE_HANDLE * RootPtr + ); + +/** Serialize an AML definition block. + + This functions allocates memory with the "AllocateZeroPool ()" + function. This memory is used to serialize the AML tree and is + returned in the Table. + + @ingroup UserApis + + @param [in] RootNode Root node of the tree. + @param [out] Table On return, hold the serialized + definition block. + + @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 +AmlSerializeDefinitionBlock ( + IN AML_ROOT_NODE_HANDLE RootNode, + OUT EFI_ACPI_DESCRIPTION_HEADER ** Table + ); + +/** Clone a node and its children (clone a tree branch). + + The cloned branch returned is not attached to any tree. + + @ingroup UserApis + + @param [in] Node Pointer to a node. + Node is the head of the branch to clone. + @param [out] ClonedNode Pointer holding the head of the created cloned + branch. + + @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 +AmlCloneTree ( + IN AML_NODE_HANDLE Node, + OUT AML_NODE_HANDLE * ClonedNode + ); + +/** Delete a Node and its children. + + The Node must be removed from the tree first, + or must be the root node. + + @ingroup UserApis + + @param [in] Node Pointer to the node to delete. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeleteTree ( + IN AML_NODE_HANDLE Node + ); + +/** Detach the Node from the tree. + + The function will fail if the Node is in its parent's fixed + argument list. + The Node is not deleted. The deletion is done separately + from the removal. + + @ingroup UserApis + + @param [in] Node Pointer to a Node. + Must be a data node or an object node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDetachNode ( + IN AML_NODE_HANDLE Node + ); + +/** Find a node in the AML namespace, given an ASL path and a reference Node. + + - The AslPath can be an absolute path, or a relative path from the + reference Node; + - Node must be a root node or a namespace node; + - A root node is expected to be at the top of the tree. + + E.g.: + For the following AML namespace, with the ReferenceNode being the node with + the name "AAAA": + - the node with the name "BBBB" can be found by looking for the ASL + path "BBBB"; + - the root node can be found by looking for the ASL relative path "^", + or the absolute path "\\". + + AML namespace: + \ + \-AAAA <- ReferenceNode + \-BBBB + + @ingroup NameSpaceApis + + @param [in] ReferenceNode Reference node. + If a relative path is given, the + search is done from this node. If + an absolute path is given, the + search is done from the root node. + Must be a root node or an object + node which is part of the + namespace. + @param [in] AslPath ASL path to the searched node in + the namespace. An ASL path name is + NULL terminated. Can be a relative + or absolute path. + E.g.: "\\_SB.CLU0.CPU0" or "^CPU0" + @param [out] OutNode Pointer to the found node. + Contains NULL if not found. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of memory. +**/ +EFI_STATUS +EFIAPI +AmlFindNode ( + IN AML_NODE_HANDLE ReferenceNode, + IN CHAR8 * AslPath, + OUT AML_NODE_HANDLE * OutNode + ); + +/** + @defgroup UserApis User APIs + @{ + User APIs are implemented to ease most common actions that might be done + using the AmlLib. They allow to find specific objects like "_UID" or + "_CRS" and to update their value. It also shows what can be done using + AmlLib functions. + @} +*/ + +/** Update the name of a DeviceOp object node. + + @ingroup UserApis + + @param [in] DeviceOpNode Object node representing a Device. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + OpCode/SubOpCode. + DeviceOp object nodes are defined in ASL + using the "Device ()" function. + @param [in] NewNameString The new Device's name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + The input string is copied. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeviceOpUpdateName ( + IN AML_OBJECT_NODE_HANDLE DeviceOpNode, + IN CHAR8 * NewNameString + ); + +/** Update an integer value defined by a NameOp object node. + + For compatibility reasons, the NameOpNode must initially + contain an integer. + + @ingroup UserApis + + @param [in] NameOpNode NameOp object node. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] NewInt New Integer value to assign. + Must be a UINT64. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpUpdateInteger ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN UINT64 NewInt + ); + +/** Update a string value defined by a NameOp object node. + + The NameOpNode must initially contain a string. + The EISAID ASL macro converts a string to an integer. This, it is + not accepted. + + @ingroup UserApis + + @param [in] NameOpNode NameOp object node. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] NewName New NULL terminated string to assign to + the NameOpNode. + The input string is copied. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpUpdateString ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN CONST CHAR8 * NewName + ); + +/** Get the first Resource Data element contained in a "_CRS" object. + + In the following ASL code, the function will return the Resource Data + node corresponding to the "QWordMemory ()" ASL macro. + Name (_CRS, ResourceTemplate() { + QWordMemory (...) {...}, + Interrupt (...) {...} + } + ) + + Note: + - The "_CRS" object must be declared using ASL "Name (Declare Named Object)". + - "_CRS" declared using ASL "Method (Declare Control Method)" is not + supported. + + @ingroup UserApis + + @param [in] NameOpCrsNode NameOp object node defining a "_CRS" object. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [out] OutRdNode Pointer to the first Resource Data element of + the "_CRS" object. A Resource Data element + is stored in a data node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpCrsGetFirstRdNode ( + IN AML_OBJECT_NODE_HANDLE NameOpCrsNode, + OUT AML_DATA_NODE_HANDLE * OutRdNode + ); + +/** Get the Resource Data element following the CurrRdNode Resource Data. + + In the following ASL code, if CurrRdNode corresponds to the first + "QWordMemory ()" ASL macro, the function will return the Resource Data + node corresponding to the "Interrupt ()" ASL macro. + Name (_CRS, ResourceTemplate() { + QwordMemory (...) {...}, + Interrupt (...) {...} + } + ) + + The CurrRdNode Resource Data node must be defined in an object named "_CRS" + and defined by a "Name ()" ASL function. + + @ingroup UserApis + + @param [in] CurrRdNode Pointer to the current Resource Data element of + the "_CRS" variable. + @param [out] OutRdNode Pointer to the Resource Data element following + the CurrRdNode. + Contain a NULL pointer if CurrRdNode is the + last Resource Data element in the list. + The "End Tag" is not considered as a resource + data element and is not returned. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpCrsGetNextRdNode ( + IN AML_DATA_NODE_HANDLE CurrRdNode, + OUT AML_DATA_NODE_HANDLE * OutRdNode + ); + +/** Update the first interrupt of an Interrupt resource data node. + + The flags of the Interrupt resource data are left unchanged. + + The InterruptRdNode corresponds to the Resource Data created by the + "Interrupt ()" ASL macro. It is an Extended Interrupt Resource Data. + See ACPI 6.3 specification, s6.4.3.6 "Extended Interrupt Descriptor" + for more information about Extended Interrupt Resource Data. + + @ingroup UserApis + + @param [in] InterruptRdNode Pointer to the an extended interrupt + resource data node. + @param [in] Irq Interrupt value to update. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of resources. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRdInterrupt ( + IN AML_DATA_NODE_HANDLE InterruptRdNode, + IN UINT32 Irq + ); + +/** Update the base address and length of a QWord resource data node. + + @ingroup UserApis + + @param [in] QWordRdNode Pointer a QWord resource data + node. + @param [in] BaseAddress Base address. + @param [in] BaseAddressLength Base address length. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of resources. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRdQWord ( + IN AML_DATA_NODE_HANDLE QWordRdNode, + IN UINT64 BaseAddress, + IN UINT64 BaseAddressLength + ); + +/** Add an Interrupt Resource Data node. + + This function creates a Resource Data element corresponding to the + "Interrupt ()" ASL function, stores it in an AML Data Node. + + It then adds it after the input CurrRdNode in the list of resource data + element. + + The Resource Data effectively created is an Extended Interrupt Resource + Data. See ACPI 6.3 specification, s6.4.3.6 "Extended Interrupt Descriptor" + for more information about Extended Interrupt Resource Data. + + The Extended Interrupt contains one single interrupt. + + This function allocates memory to create a data node. It is the caller's + responsibility to either: + - attach this node to an AML tree; + - delete this node. + + Note: The _CRS node must be defined using the ASL Name () function. + e.g. Name (_CRS, ResourceTemplate () { + ... + } + + @ingroup UserApis + + @param [in] NameOpCrsNode NameOp object node defining a "_CRS" object. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] ResourceConsumer The device consumes the specified interrupt + or produces it for use by a child device. + @param [in] EdgeTriggered The interrupt is edge triggered or + level triggered. + @param [in] ActiveLow The interrupt is active-high or active-low. + @param [in] Shared The interrupt can be shared with other + devices or not (Exclusive). + @param [in] IrqList Interrupt list. Must be non-NULL. + @param [in] IrqCount Interrupt count. Must be non-zero. + + + @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 +AmlCodeGenCrsAddRdInterrupt ( + IN AML_OBJECT_NODE_HANDLE NameOpCrsNode, + IN BOOLEAN ResourceConsumer, + IN BOOLEAN EdgeTriggered, + IN BOOLEAN ActiveLow, + IN BOOLEAN Shared, + IN UINT32 * IrqList, + IN UINT8 IrqCount + ); + +/** AML code generation for DefinitionBlock. + + Create a Root Node handle. + It is the caller's responsibility to free the allocated memory + with the AmlDeleteTree function. + + AmlCodeGenDefinitionBlock (TableSignature, OemId, TableID, OEMRevision) is + equivalent to the following ASL code: + DefinitionBlock (AMLFileName, TableSignature, ComplianceRevision, + OemId, TableID, OEMRevision) {} + with the ComplianceRevision set to 2 and the AMLFileName is ignored. + + @ingroup CodeGenApis + + @param[in] TableSignature 4-character ACPI signature. + Must be 'DSDT' or 'SSDT'. + @param[in] OemId 6-character string OEM identifier. + @param[in] OemTableId 8-character string OEM table identifier. + @param[in] OemRevision OEM revision number. + @param[out] DefinitionBlockTerm The ASL Term handle representing a + Definition Block. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCodeGenDefinitionBlock ( + IN CONST CHAR8 * TableSignature, + IN CONST CHAR8 * OemId, + IN CONST CHAR8 * OemTableId, + IN UINT32 OemRevision, + OUT AML_ROOT_NODE_HANDLE * NewRootNode + ); + +/** AML code generation for a Name object node, containing a String. + + AmlCodeGenNameString ("_HID", "HID0000", ParentNode, NewObjectNode) is + equivalent of the following ASL code: + Name(_HID, "HID0000") + + @ingroup CodeGenApis + + @param [in] NameString The new variable name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + The input string is copied. + @param [in] String NULL terminated String to associate to the + NameString. + @param [in] ParentNode If provided, set ParentNode as the parent + of the node created. + @param [out] NewObjectNode If success, contains the created node. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCodeGenNameString ( + IN CONST CHAR8 * NameString, + IN CHAR8 * String, + IN AML_NODE_HANDLE ParentNode, OPTIONAL + OUT AML_OBJECT_NODE_HANDLE * NewObjectNode OPTIONAL + ); + +/** AML code generation for a Name object node, containing an Integer. + + AmlCodeGenNameInteger ("_UID", 1, ParentNode, NewObjectNode) is + equivalent of the following ASL code: + Name(_UID, One) + + @ingroup CodeGenApis + + @param [in] NameString The new variable name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + The input string is copied. + @param [in] Integer Integer to associate to the NameString. + @param [in] ParentNode If provided, set ParentNode as the parent + of the node created. + @param [out] NewObjectNode If success, contains the created node. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCodeGenNameInteger ( + IN CONST CHAR8 * NameString, + IN UINT64 Integer, + IN AML_NODE_HANDLE ParentNode, OPTIONAL + OUT AML_OBJECT_NODE_HANDLE * NewObjectNode OPTIONAL + ); + +/** AML code generation for a Device object node. + + AmlCodeGenDevice ("COM0", ParentNode, NewObjectNode) is + equivalent of the following ASL code: + Device(COM0) {} + + @ingroup CodeGenApis + + @param [in] NameString The new Device's name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + The input string is copied. + @param [in] ParentNode If provided, set ParentNode as the parent + of the node created. + @param [out] NewObjectNode If success, contains the created node. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCodeGenDevice ( + IN CONST CHAR8 * NameString, + IN AML_NODE_HANDLE ParentNode, OPTIONAL + OUT AML_OBJECT_NODE_HANDLE * NewObjectNode OPTIONAL + ); + +/** AML code generation for a Scope object node. + + AmlCodeGenScope ("_SB", ParentNode, NewObjectNode) is + equivalent of the following ASL code: + Scope(_SB) {} + + @ingroup CodeGenApis + + @param [in] NameString The new Scope's name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + The input string is copied. + @param [in] ParentNode If provided, set ParentNode as the parent + of the node created. + @param [out] NewObjectNode If success, contains the created node. + + @retval EFI_SUCCESS Success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +AmlCodeGenScope ( + IN CONST CHAR8 * NameString, + IN AML_NODE_HANDLE ParentNode, OPTIONAL + OUT AML_OBJECT_NODE_HANDLE * NewObjectNode OPTIONAL + ); + +#endif // AML_LIB_H_ diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c new file mode 100644 index 0000000000000000000000000000000000000000..fdf04acc6212f9d6b6f691f30a60fea6f0b43e6f --- /dev/null +++ b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApi.c @@ -0,0 +1,382 @@ +/** @file + AML Api. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +/* Even though this file has access to the internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. Only the external node + handle types should be used, i.e. AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, + etc. + Indeed, the functions in the "Api" folder should be implemented only + using the "safe" functions available in the "Include" folder. This + makes the functions available in the "Api" folder easy to export. +*/ +#include + +#include +#include +#include +#include + +/** Update the name of a DeviceOp object node. + + @param [in] DeviceOpNode Object node representing a Device. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + OpCode/SubOpCode. + DeviceOp object nodes are defined in ASL + using the "Device ()" function. + @param [in] NewNameString The new Device's name. + Must be a NULL-terminated ASL NameString + e.g.: "DEV0", "DV15.DEV0", etc. + The input string is copied. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlDeviceOpUpdateName ( + IN AML_OBJECT_NODE_HANDLE DeviceOpNode, + IN CHAR8 * NewNameString + ) +{ + EFI_STATUS Status; + + AML_DATA_NODE_HANDLE DeviceNameDataNode; + CHAR8 * NewAmlNameString; + UINT32 NewAmlNameStringSize; + + // Check the input node is an object node. + if ((DeviceOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)DeviceOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (DeviceOpNode, AML_EXT_OP, AML_EXT_DEVICE_OP)) || + (NewNameString == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the Device's name, being a data node + // which is the 1st fixed argument (i.e. index 0). + DeviceNameDataNode = (AML_DATA_NODE_HANDLE)AmlGetFixedArgument ( + DeviceOpNode, + EAmlParseIndexTerm0 + ); + if ((DeviceNameDataNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)DeviceNameDataNode) != EAmlNodeData) || + (!AmlNodeHasDataType (DeviceNameDataNode, EAmlNodeDataTypeNameString))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = ConvertAslNameToAmlName (NewNameString, &NewAmlNameString); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlGetNameStringSize (NewAmlNameString, &NewAmlNameStringSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Update the Device's name node. + Status = AmlUpdateDataNode ( + DeviceNameDataNode, + EAmlNodeDataTypeNameString, + (UINT8*)NewAmlNameString, + NewAmlNameStringSize + ); + ASSERT_EFI_ERROR (Status); + +exit_handler: + FreePool (NewAmlNameString); + return Status; +} + +/** Update an integer value defined by a NameOp object node. + + For compatibility reasons, the NameOpNode must initially + contain an integer. + + @param [in] NameOpNode NameOp object node. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] NewInt New Integer value to assign. + Must be a UINT64. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpUpdateInteger ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN UINT64 NewInt + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE_HANDLE IntegerOpNode; + + if ((NameOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (NameOpNode, AML_NAME_OP, 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the Integer object node defined by the "Name ()" function: + // it must have an Integer OpCode (Byte/Word/DWord/QWord). + // It is the 2nd fixed argument (i.e. index 1) of the NameOp node. + // This can also be a ZeroOp or OneOp node. + IntegerOpNode = (AML_OBJECT_NODE_HANDLE)AmlGetFixedArgument ( + NameOpNode, + EAmlParseIndexTerm1 + ); + if ((IntegerOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)IntegerOpNode) != EAmlNodeObject)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update the Integer value. + Status = AmlUpdateInteger (IntegerOpNode, NewInt); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Update a string value defined by a NameOp object node. + + The NameOpNode must initially contain a string. + The EISAID ASL macro converts a string to an integer. This, it is + not accepted. + + @param [in] NameOpNode NameOp object node. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] NewName New NULL terminated string to assign to + the NameOpNode. + The input string is copied. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpUpdateString ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN CONST CHAR8 * NewName + ) +{ + EFI_STATUS Status; + AML_OBJECT_NODE_HANDLE StringOpNode; + AML_DATA_NODE_HANDLE StringDataNode; + + if ((NameOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (NameOpNode, AML_NAME_OP, 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the String object node defined by the "Name ()" function: + // it must have a string OpCode. + // It is the 2nd fixed argument (i.e. index 1) of the NameOp node. + StringOpNode = (AML_OBJECT_NODE_HANDLE)AmlGetFixedArgument ( + NameOpNode, + EAmlParseIndexTerm1 + ); + if ((StringOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)StringOpNode) != EAmlNodeObject)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the string data node. + // It is the 1st fixed argument (i.e. index 0) of the StringOpNode node. + StringDataNode = (AML_DATA_NODE_HANDLE)AmlGetFixedArgument ( + StringOpNode, + EAmlParseIndexTerm0 + ); + if ((StringDataNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)StringDataNode) != EAmlNodeData)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Update the string value. + Status = AmlUpdateDataNode ( + StringDataNode, + EAmlNodeDataTypeString, + (UINT8*)NewName, + (UINT32)AsciiStrLen (NewName) + 1 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** Get the first Resource Data element contained in a "_CRS" object. + + In the following ASL code, the function will return the Resource Data + node corresponding to the "QWordMemory ()" ASL macro. + Name (_CRS, ResourceTemplate() { + QWordMemory (...) {...}, + Interrupt (...) {...} + } + ) + + Note: + - The "_CRS" object must be declared using ASL "Name (Declare Named Object)". + - "_CRS" declared using ASL "Method (Declare Control Method)" is not + supported. + + @param [in] NameOpCrsNode NameOp object node defining a "_CRS" object. + Must have an OpCode=AML_NAME_OP, SubOpCode=0. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [out] OutRdNode Pointer to the first Resource Data element of + the "_CRS" object. A Resource Data element + is stored in a data node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpCrsGetFirstRdNode ( + IN AML_OBJECT_NODE_HANDLE NameOpCrsNode, + OUT AML_DATA_NODE_HANDLE * OutRdNode + ) +{ + AML_OBJECT_NODE_HANDLE BufferOpNode; + AML_DATA_NODE_HANDLE FirstRdNode; + + if ((NameOpCrsNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameOpCrsNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (NameOpCrsNode, AML_NAME_OP, 0)) || + (!AmlNameOpCompareName (NameOpCrsNode, "_CRS")) || + (OutRdNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutRdNode = NULL; + + // Get the _CRS value which is represented as a BufferOp object node + // which is the 2nd fixed argument (i.e. index 1). + BufferOpNode = (AML_OBJECT_NODE_HANDLE)AmlGetFixedArgument ( + NameOpCrsNode, + EAmlParseIndexTerm1 + ); + if ((BufferOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)BufferOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (BufferOpNode, AML_BUFFER_OP, 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the first Resource data node in the variable list of + // argument of the BufferOp node. + FirstRdNode = (AML_DATA_NODE_HANDLE)AmlGetNextVariableArgument ( + (AML_NODE_HANDLE)BufferOpNode, + NULL + ); + if ((FirstRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)FirstRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType (FirstRdNode, EAmlNodeDataTypeResourceData))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutRdNode = FirstRdNode; + return EFI_SUCCESS; +} + +/** Get the Resource Data element following the CurrRdNode Resource Data. + + In the following ASL code, if CurrRdNode corresponds to the first + "QWordMemory ()" ASL macro, the function will return the Resource Data + node corresponding to the "Interrupt ()" ASL macro. + Name (_CRS, ResourceTemplate() { + QwordMemory (...) {...}, + Interrupt (...) {...} + } + ) + + The CurrRdNode Resource Data node must be defined in an object named "_CRS" + and defined by a "Name ()" ASL function. + + @param [in] CurrRdNode Pointer to the current Resource Data element of + the "_CRS" object. + @param [out] OutRdNode Pointer to the Resource Data element following + the CurrRdNode. + Contain a NULL pointer if CurrRdNode is the + last Resource Data element in the list. + The "End Tag" is not considered as a resource + data element and is not returned. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlNameOpCrsGetNextRdNode ( + IN AML_DATA_NODE_HANDLE CurrRdNode, + OUT AML_DATA_NODE_HANDLE * OutRdNode + ) +{ + AML_OBJECT_NODE_HANDLE NameOpCrsNode; + AML_OBJECT_NODE_HANDLE BufferOpNode; + + if ((CurrRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)CurrRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType (CurrRdNode, EAmlNodeDataTypeResourceData)) || + (OutRdNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutRdNode = NULL; + + // The parent of the CurrRdNode must be a BufferOp node. + BufferOpNode = (AML_OBJECT_NODE_HANDLE)AmlGetParent ( + (AML_NODE_HANDLE)CurrRdNode + ); + if ((BufferOpNode == NULL) || + (!AmlNodeHasOpCode (BufferOpNode, AML_BUFFER_OP, 0))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // The parent of the BufferOpNode must be a NameOp node. + NameOpCrsNode = (AML_OBJECT_NODE_HANDLE)AmlGetParent ( + (AML_NODE_HANDLE)BufferOpNode + ); + if ((NameOpCrsNode == NULL) || + (!AmlNodeHasOpCode (NameOpCrsNode, AML_NAME_OP, 0)) || + (!AmlNameOpCompareName (NameOpCrsNode, "_CRS"))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutRdNode = (AML_DATA_NODE_HANDLE)AmlGetNextVariableArgument ( + (AML_NODE_HANDLE)BufferOpNode, + (AML_NODE_HANDLE)CurrRdNode + ); + + // If the Resource Data is an End Tag, return NULL. + if (AmlNodeHasRdDataType ( + *OutRdNode, + AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) { + *OutRdNode = NULL; + } + + return EFI_SUCCESS; +} diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c new file mode 100644 index 0000000000000000000000000000000000000000..9693f28b543f2008d17d09031ed3b53577935a5d --- /dev/null +++ b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.c @@ -0,0 +1,219 @@ +/** @file + AML Helper. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +/* Even though this file has access to the internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. Only the external node + handle types should be used, i.e. AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, + etc. + Indeed, the functions in the "Api" folder should be implemented only + using the "safe" functions available in the "Include" folder. This + makes the functions available in the "Api" folder easy to export. +*/ +#include + +#include +#include +#include + +/** Compare the NameString defined by the "Name ()" ASL function, + and stored in the NameOpNode, with the input NameString. + + An ASL NameString is expected to be NULL terminated, and can be composed + of NameSegs that have less that 4 chars, like "DEV". "DEV" will be expanded + as "DEV_". + + An AML NameString is not NULL terminated and is is only composed of + 4 chars long NameSegs. + + @param [in] NameOpNode NameOp object node defining a variable. + Must have an AML_NAME_OP/0 OpCode/SubOpCode. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] AslName ASL NameString to compare the NameOp's name with. + Must be NULL terminated. + + @retval TRUE If the AslName and the AmlName defined by the NameOp node + are similar. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNameOpCompareName ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN CHAR8 * AslName + ) +{ + EFI_STATUS Status; + AML_DATA_NODE_HANDLE NameDataNode; + + CHAR8 * AmlName; + UINT32 AmlNameSize; + + BOOLEAN RetVal; + + if ((NameOpNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameOpNode) != EAmlNodeObject) || + (!AmlNodeHasOpCode (NameOpNode, AML_NAME_OP, 0)) || + (AslName == NULL)) { + ASSERT (0); + return FALSE; + } + + // Get the NameOp name, being in a data node + // which is the first fixed argument (i.e. index 0). + NameDataNode = (AML_DATA_NODE_HANDLE)AmlGetFixedArgument ( + NameOpNode, + EAmlParseIndexTerm0 + ); + if ((NameDataNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)NameDataNode) != EAmlNodeData) || + (!AmlNodeHasDataType (NameDataNode, EAmlNodeDataTypeNameString))) { + ASSERT (0); + return FALSE; + } + + // Get the size of the name. + Status = AmlGetDataNodeBuffer (NameDataNode, NULL, &AmlNameSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // Allocate memory to fetch the name. + AmlName = AllocateZeroPool (AmlNameSize); + if (AmlName == NULL) { + ASSERT (0); + return FALSE; + } + + // Fetch the name. + Status = AmlGetDataNodeBuffer (NameDataNode, (UINT8*)AmlName, &AmlNameSize); + if (EFI_ERROR (Status)) { + FreePool (AmlName); + ASSERT (0); + return FALSE; + } + + // Compare the input AslName and the AmlName stored in the NameOp node. + RetVal = CompareAmlWithAslNameString (AmlName, AslName); + + // Free the string buffer. + FreePool (AmlName); + return RetVal; +} + +/** Check whether ObjectNode has the input OpCode/SubOpcode couple. + + @param [in] ObjectNode Pointer to an object node. + @param [in] OpCode OpCode to check + @param [in] SubOpCode SubOpCode to check + + @retval TRUE The node is an object node and + the Opcode and SubOpCode match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasOpCode ( + IN AML_OBJECT_NODE_HANDLE ObjectNode, + IN UINT8 OpCode, + IN UINT8 SubOpCode + ) +{ + EFI_STATUS Status; + UINT8 NodeOpCode; + UINT8 NodeSubOpCode; + + // Get the Node information. + Status = AmlGetObjectNodeInfo ( + ObjectNode, + &NodeOpCode, + &NodeSubOpCode, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // Check the OpCode and SubOpCode. + if ((OpCode != NodeOpCode) || + (SubOpCode != NodeSubOpCode)) { + return FALSE; + } + + return TRUE; +} + +/** Check whether DataNode has the input DataType. + + @param [in] DataNode Pointer to a data node. + @param [in] DataType DataType to check. + + @retval TRUE The node is a data node and + the DataType match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasDataType ( + IN AML_DATA_NODE_HANDLE DataNode, + IN EAML_NODE_DATA_TYPE DataType + ) +{ + EFI_STATUS Status; + EAML_NODE_DATA_TYPE NodeDataType; + + // Get the data type. + Status = AmlGetNodeDataType (DataNode, &NodeDataType); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // Check the data type. + if (NodeDataType != DataType) { + return FALSE; + } + + return TRUE; +} + +/** Check whether RdNode has the input RdDataType. + + @param [in] RdNode Pointer to a data node. + @param [in] RdDataType DataType to check. + + @retval TRUE The node is a Resource Data node and + the RdDataType match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasRdDataType ( + IN AML_DATA_NODE_HANDLE RdNode, + IN AML_RD_HEADER RdDataType + ) +{ + EFI_STATUS Status; + AML_RD_HEADER NodeRdDataType; + + // Get the resource data type. + Status = AmlGetResourceDataType ( + RdNode, + &NodeRdDataType + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return FALSE; + } + + // Check the RdDataType. + return AmlRdCompareDescId (&NodeRdDataType, RdDataType); +} diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h new file mode 100644 index 0000000000000000000000000000000000000000..9872adddc36739559c86268564483480a6d65294 --- /dev/null +++ b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlApiHelper.h @@ -0,0 +1,93 @@ +/** @file + AML Helper. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_HELPER_H_ +#define AML_HELPER_H_ + +#include +#include + +/** Compare the NameString defined by the "Name ()" ASL function, + and stored in the NameOpNode, with the input NameString. + + An ASL NameString is expected to be NULL terminated, and can be composed + of NameSegs that have less that 4 chars, like "DEV". "DEV" will be expanded + as "DEV_". + + An AML NameString is not NULL terminated and is is only composed of + 4 chars long NameSegs. + + @param [in] NameOpNode NameOp object node defining a variable. + Must have an AML_NAME_OP/0 OpCode/SubOpCode. + NameOp object nodes are defined in ASL + using the "Name ()" function. + @param [in] AslName ASL NameString to compare the NameOp's name with. + Must be NULL terminated. + + @retval TRUE If the AslName and the AmlName defined by the NameOp node + are similar. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNameOpCompareName ( + IN AML_OBJECT_NODE_HANDLE NameOpNode, + IN CHAR8 * AslName + ); + +/** Check whether ObjectNode has the input OpCode/SubOpcode couple. + + @param [in] ObjectNode Pointer to an object node. + @param [in] OpCode OpCode to check + @param [in] SubOpCode SubOpCode to check + + @retval TRUE The node is an object node and + the Opcode and SubOpCode match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasOpCode ( + IN AML_OBJECT_NODE_HANDLE ObjectNode, + IN UINT8 OpCode, + IN UINT8 SubOpCode + ); + +/** Check whether DataNode has the input DataType. + + @param [in] DataNode Pointer to a data node. + @param [in] DataType DataType to check. + + @retval TRUE The node is a data node and + the DataType match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasDataType ( + IN AML_DATA_NODE_HANDLE DataNode, + IN EAML_NODE_DATA_TYPE DataType + ); + +/** Check whether RdNode has the input RdDataType. + + @param [in] RdNode Pointer to a data node. + @param [in] RdDataType DataType to check. + + @retval TRUE The node is a Resource Data node and + the RdDataType match. + @retval FALSE Otherwise. +**/ +BOOLEAN +EFIAPI +AmlNodeHasRdDataType ( + IN AML_DATA_NODE_HANDLE RdNode, + IN AML_RD_HEADER RdDataType + ); + +#endif // AML_HELPER_H_ diff --git a/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c new file mode 100644 index 0000000000000000000000000000000000000000..913c8dcdb0c04d7180a0732cfc6c5f495105f8e9 --- /dev/null +++ b/DynamicTablesPkg/Library/Common/AmlLib/Api/AmlResourceDataApi.c @@ -0,0 +1,320 @@ +/** @file + AML Update Resource Data. + + Copyright (c) 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +/* Even though this file has access to the internal Node definition, + i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. Only the external node + handle types should be used, i.e. AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, + etc. + Indeed, the functions in the "Api" folder should be implemented only + using the "safe" functions available in the "Include" folder. This + makes the functions available in the "Api" folder easy to export. +*/ +#include + +#include +#include +#include +#include + +/** Update the first interrupt of an Interrupt resource data node. + + The flags of the Interrupt resource data are left unchanged. + + The InterruptRdNode corresponds to the Resource Data created by the + "Interrupt ()" ASL macro. It is an Extended Interrupt Resource Data. + See ACPI 6.3 specification, s6.4.3.6 "Extended Interrupt Descriptor" + for more information about Extended Interrupt Resource Data. + + @param [in] InterruptRdNode Pointer to the an extended interrupt + resource data node. + @param [in] Irq Interrupt value to update. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of resources. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRdInterrupt ( + IN AML_DATA_NODE_HANDLE InterruptRdNode, + IN UINT32 Irq + ) +{ + EFI_STATUS Status; + UINT32 * FirstInterrupt; + UINT8 * QueryBuffer; + UINT32 QueryBufferSize; + + if ((InterruptRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)InterruptRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType ( + InterruptRdNode, + EAmlNodeDataTypeResourceData)) || + (!AmlNodeHasRdDataType ( + InterruptRdNode, + AML_RD_BUILD_LARGE_DESC_ID ( + ACPI_LARGE_EXTENDED_IRQ_DESCRIPTOR_NAME)))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + QueryBuffer = NULL; + + // Get the size of the InterruptRdNode buffer. + Status = AmlGetDataNodeBuffer ( + InterruptRdNode, + NULL, + &QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Check the Buffer is large enough. + if (QueryBufferSize < sizeof (EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Allocate a buffer to fetch the data. + QueryBuffer = AllocatePool (QueryBufferSize); + if (QueryBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + // Get the data. + Status = AmlGetDataNodeBuffer ( + InterruptRdNode, + QueryBuffer, + &QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + // Get the address of the first interrupt field. + FirstInterrupt = + ((EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR*)QueryBuffer)->InterruptNumber; + + *FirstInterrupt = Irq; + + // Update the InterruptRdNode buffer. + Status = AmlUpdateDataNode ( + InterruptRdNode, + EAmlNodeDataTypeResourceData, + QueryBuffer, + QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + +error_handler: + if (QueryBuffer != NULL) { + FreePool (QueryBuffer); + } + return Status; +} + +/** Update the interrupt list of an interrupt resource data node. + + The InterruptRdNode corresponds to the Resource Data created by the + "Interrupt ()" ASL function. It is an Extended Interrupt Resource Data. + See ACPI 6.3 specification, s6.4.3.6 "Extended Interrupt Descriptor" + for more information about Extended Interrupt Resource Data. + + @param [in] InterruptRdNode Pointer to the an extended interrupt + resource data node. + @param [in] ResourceConsumer The device consumes the specified interrupt + or produces it for use by a child device. + @param [in] EdgeTriggered The interrupt is edge triggered or + level triggered. + @param [in] ActiveLow The interrupt is active-high or active-low. + @param [in] Shared The interrupt can be shared with other + devices or not (Exclusive). + @param [in] IrqList Interrupt list. Must be non-NULL. + @param [in] IrqCount Interrupt count. Must be non-zero. + + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of resources. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRdInterruptEx ( + IN AML_DATA_NODE_HANDLE InterruptRdNode, + IN BOOLEAN ResourceConsumer, + IN BOOLEAN EdgeTriggered, + IN BOOLEAN ActiveLow, + IN BOOLEAN Shared, + IN UINT32 * IrqList, + IN UINT8 IrqCount + ) +{ + EFI_STATUS Status; + + EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR * RdInterrupt; + UINT32 * FirstInterrupt; + UINT8 * UpdateBuffer; + UINT16 UpdateBufferSize; + + if ((InterruptRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)InterruptRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType ( + InterruptRdNode, + EAmlNodeDataTypeResourceData)) || + (!AmlNodeHasRdDataType ( + InterruptRdNode, + AML_RD_BUILD_LARGE_DESC_ID ( + ACPI_LARGE_EXTENDED_IRQ_DESCRIPTOR_NAME))) || + (IrqList == NULL) || + (IrqCount == 0)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + UpdateBuffer = NULL; + UpdateBufferSize = sizeof (EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR) + + ((IrqCount - 1) * sizeof (UINT32)); + + // Allocate a buffer to update the data. + UpdateBuffer = AllocatePool (UpdateBufferSize); + if (UpdateBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + // Update the Resource Data information (structure size, interrupt count). + RdInterrupt = (EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR*)UpdateBuffer; + RdInterrupt->Header.Header.Byte = + AML_RD_BUILD_LARGE_DESC_ID (ACPI_LARGE_EXTENDED_IRQ_DESCRIPTOR_NAME); + RdInterrupt->Header.Length = + UpdateBufferSize - sizeof (ACPI_LARGE_RESOURCE_HEADER); + RdInterrupt->InterruptTableLength = IrqCount; + RdInterrupt->InterruptVectorFlags = (ResourceConsumer ? BIT0 : 0) | + (EdgeTriggered ? BIT1 : 0) | + (ActiveLow ? BIT2 : 0) | + (Shared ? BIT3 : 0); + + // Get the address of the first interrupt field. + FirstInterrupt = + ((EFI_ACPI_EXTENDED_INTERRUPT_DESCRIPTOR*)UpdateBuffer)->InterruptNumber; + + // Copy the input list of interrupts. + CopyMem (FirstInterrupt, IrqList, (sizeof (UINT32) * IrqCount)); + + // Update the InterruptRdNode buffer. + Status = AmlUpdateDataNode ( + InterruptRdNode, + EAmlNodeDataTypeResourceData, + UpdateBuffer, + UpdateBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + + // Cleanup + FreePool (UpdateBuffer); + + return Status; +} + +/** Update the base address and length of a QWord resource data node. + + @param [in] QWordRdNode Pointer a QWord resource data + node. + @param [in] BaseAddress Base address. + @param [in] BaseAddressLength Base address length. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_OUT_OF_RESOURCES Out of resources. +**/ +EFI_STATUS +EFIAPI +AmlUpdateRdQWord ( + IN AML_DATA_NODE_HANDLE QWordRdNode, + IN UINT64 BaseAddress, + IN UINT64 BaseAddressLength + ) +{ + EFI_STATUS Status; + EFI_ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR * RdQWord; + + UINT8 * QueryBuffer; + UINT32 QueryBufferSize; + + if ((QWordRdNode == NULL) || + (AmlGetNodeType ((AML_NODE_HANDLE)QWordRdNode) != EAmlNodeData) || + (!AmlNodeHasDataType (QWordRdNode, EAmlNodeDataTypeResourceData)) || + (!AmlNodeHasRdDataType ( + QWordRdNode, + AML_RD_BUILD_LARGE_DESC_ID ( + ACPI_LARGE_QWORD_ADDRESS_SPACE_DESCRIPTOR_NAME)))) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Get the size of the QWordRdNode's buffer. + Status = AmlGetDataNodeBuffer ( + QWordRdNode, + NULL, + &QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Allocate a buffer to fetch the data. + QueryBuffer = AllocatePool (QueryBufferSize); + if (QueryBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + // Get the data. + Status = AmlGetDataNodeBuffer ( + QWordRdNode, + QueryBuffer, + &QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto error_handler; + } + + RdQWord = (EFI_ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR*)QueryBuffer; + + // Update the Base Address and Length. + RdQWord->AddrRangeMin = BaseAddress; + RdQWord->AddrRangeMax = BaseAddress + BaseAddressLength - 1; + RdQWord->AddrLen = BaseAddressLength; + + // Update Base Address Resource Data node. + Status = AmlUpdateDataNode ( + QWordRdNode, + EAmlNodeDataTypeResourceData, + QueryBuffer, + QueryBufferSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + +error_handler: + if (QueryBuffer != NULL) { + FreePool (QueryBuffer); + } + return Status; +} -- 'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'