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.51]) by mx.groups.io with SMTP id smtpd.web12.16979.1597245821256683518 for ; Wed, 12 Aug 2020 08:23:41 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@armh.onmicrosoft.com header.s=selector2-armh-onmicrosoft-com header.b=TBlf0dNd; spf=pass (domain: arm.com, ip: 40.107.20.51, 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=LK+FQxlF8SePvcHimhyC25DW/7lhe13RrbSV2YZ0lZc=; b=TBlf0dNdRVAiqGcGo3mqZmowGqfxs5osUm5e4COIswdLupOsuypEu+3KDSH+anDvT0TV0ZanewSOjzTZpS27aOGTp2QOzuLEyQfHMYPE9k8DYUjDTJ0W+Dq1nRuUltaGWQFkWlMABBvAM6z4TOXMGSbkRfxEcLKD1NIrNuX4nWI= Received: from AM5P194CA0003.EURP194.PROD.OUTLOOK.COM (2603:10a6:203:8f::13) by AM8PR08MB5748.eurprd08.prod.outlook.com (2603:10a6:20b:1df::10) 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:37 +0000 Received: from VE1EUR03FT044.eop-EUR03.prod.protection.outlook.com (2603:10a6:203:8f:cafe::90) by AM5P194CA0003.outlook.office365.com (2603:10a6:203:8f::13) 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:37 +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 VE1EUR03FT044.mail.protection.outlook.com (10.152.19.106) 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:37 +0000 Received: ("Tessian outbound bac899b43a54:v64"); Wed, 12 Aug 2020 15:23:37 +0000 X-CheckRecipientChecked: true X-CR-MTA-CID: 5bc09c2383faf209 X-CR-MTA-TID: 64aa7808 Received: from 56d36f88ac5d.1 by 64aa7808-outbound-1.mta.getcheckrecipient.com id 9BA7A9EF-79D0-49FD-AB55-95B07466ECA0.1; Wed, 12 Aug 2020 15:23:32 +0000 Received: from EUR01-VE1-obe.outbound.protection.outlook.com by 64aa7808-outbound-1.mta.getcheckrecipient.com with ESMTPS id 56d36f88ac5d.1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384); Wed, 12 Aug 2020 15:23:32 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=V5tmrxn87YesSwnJg/l7iowPxXjYGEmMssz+OLadRN58E5Wk8LrVssM4FtAh/3JTtUEWry+pztkvOkyeoqbfMdN1Lcpua1jYPvIwj+Q1UDBkar9WzY2qy+TTOJNzioFMFjanKu+yd+XozVmdlkAynkEO2RRlcDNwQmWCCN/HoSYvEfcj/IYxRwpZ/AMktom8012EquH6IUhYM46Ttl0W6pvLHToOG/u0ZAw/vqXtt1OzX+YVis6/oyHuJaTskDBjOUiyzASW6QQyVYhMqccxiRaqwBloC/3mYqBsQ4ZuD8f3sqoy7xvmlBoylKG75rYo2vx8vBHBb4JVqHKPf9/Hrg== 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=LK+FQxlF8SePvcHimhyC25DW/7lhe13RrbSV2YZ0lZc=; b=hkiLp2QlSK+Xehcw4L0XI2xnTL4QwD0a57MGwuyAmIVVhYY5ybRlsBys+1qOPpXvZAewcgrEnkXDkzvfO66NGmJIxj+bl9vq6/BQ7cmmf7cT2IkOGKoFS9ZLv2TQN2Hio4zPftSQDxXb5E4kh6L0AN8SiVZ+NfUnHQnNM1vEpZXvF+m9aB4DqgUj26t4wAUPMDhyCdqnjEVGaUi10fY7aD0LrUa/qHfcfFIqUATt/OEFttlAB8zn8ycmZfvFPCKucQgZHTciKiqC+H3JK/OPNEa7AtRK1BXTzou3qivlvPKDXy5vaXe4rmHeOynDXJ33D0HsBrA7qQKNDdJw6/zFpg== 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=LK+FQxlF8SePvcHimhyC25DW/7lhe13RrbSV2YZ0lZc=; b=TBlf0dNdRVAiqGcGo3mqZmowGqfxs5osUm5e4COIswdLupOsuypEu+3KDSH+anDvT0TV0ZanewSOjzTZpS27aOGTp2QOzuLEyQfHMYPE9k8DYUjDTJ0W+Dq1nRuUltaGWQFkWlMABBvAM6z4TOXMGSbkRfxEcLKD1NIrNuX4nWI= Received: from AM6P193CA0111.EURP193.PROD.OUTLOOK.COM (2603:10a6:209:85::16) by VI1PR08MB4175.eurprd08.prod.outlook.com (2603:10a6:803:ea::29) 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:28 +0000 Received: from AM5EUR03FT050.eop-EUR03.prod.protection.outlook.com (2603:10a6:209:85:cafe::d7) by AM6P193CA0111.outlook.office365.com (2603:10a6:209:85::16) 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:28 +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 AM5EUR03FT050.mail.protection.outlook.com (10.152.17.47) 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:28 +0000 Received: from AZ-NEU-EX01.Emea.Arm.com (10.251.26.4) by AZ-NEU-EX03.Arm.com (10.251.24.31) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.1.2044.4; Wed, 12 Aug 2020 15:23:27 +0000 Received: from AZ-NEU-EX04.Arm.com (10.251.24.32) by AZ-NEU-EX01.Emea.Arm.com (10.251.26.4) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1779.2; Wed, 12 Aug 2020 15:23:25 +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:24 +0000 From: "Sami Mujawar" To: CC: Sami Mujawar , , , , , , Subject: [PATCH v1 15/30] DynamicTablesPkg: AML ACPI Namespace interface Date: Wed, 12 Aug 2020 16:22:21 +0100 Message-ID: <20200812152236.31164-16-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: db0fbb74-8af3-44d1-2db7-08d83ed3af93 X-MS-TrafficTypeDiagnostic: VI1PR08MB4175:|AM8PR08MB5748: X-Microsoft-Antispam-PRVS: x-checkrecipientrouted: true NoDisclaimer: true X-MS-Oob-TLC-OOBClassifiers: OLM:10000;OLM:10000; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam-Untrusted: BCL:0; X-Microsoft-Antispam-Message-Info-Original: K3f4PUQ/FCMti90Q6P4lSr1N+qmSFBPICw4qMOMRwTYFkAyeZlrecxNZGYQ2DgFbtKXh1ZeoOq8zmQFtXbpjndetrbUwHnrtOqdwb3URsyPYiBRck1VUPuQNJJCLhib14H8RUp42MW7J99VaEiR7f0C2J28uDKBrxTQYXvUWTKF9TczhqYxHcbj27YbtA+Gz5GvZofyi6frInwTafraS6eWIJcZu7CUaxriIsNJfwIpZX8XZKdhGeUiohxTcCGannQPnpW+yXJkWIFJvuTTLmpLUWIpkYvjpqXGpGEDEEsQ4xsh/qSmwvSYHWv7HrvrzJGOtBdU8uO6nX310Vasv1SZcV/wCoqkLI4gBORdqba86cw93+w1T43g6mbqNTPdr4MO8CLVb4Uv9GpyU4M8b7A== 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)(376002)(346002)(136003)(39860400002)(396003)(46966005)(478600001)(83380400001)(356005)(82310400002)(1076003)(4326008)(70206006)(81166007)(86362001)(2906002)(5660300002)(70586007)(6666004)(30864003)(426003)(316002)(44832011)(8676002)(8936002)(2616005)(26005)(82740400003)(7696005)(54906003)(336012)(186003)(6916009)(36756003)(47076004)(559001)(579004);DIR:OUT;SFP:1101; X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR08MB4175 Return-Path: Sami.Mujawar@arm.com X-MS-Exchange-Transport-CrossTenantHeadersStripped: VE1EUR03FT044.eop-EUR03.prod.protection.outlook.com X-MS-Office365-Filtering-Correlation-Id-Prvs: b6d2f6e8-d437-4f26-ec55-08d83ed3aa29 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: l4Ye6nhq/+XTjoE8980I/CdaIzM33nDIRfI1l4rhCUFBg0JDrm2AyeFUgekQwxVsTBv9A1YTIFgPHaxhdMSSoHaSn/hfLW81ME/EvnEAZb+adtem5dzlSqCemteRdhKOtlE67dFgvaxlO+vratMJx96j289lhP/JBqoa6S6yxWsBbWpQ3Yh04dkoem+jqsF5GB3vD4CmyhE8nNuowBvc26KIMq8lz5rAFX6HVgSF5It0rlmSnv++GMXd9Gj3t36zh2gS/yyC324fLoFGzsvGel+icj7J8XkmcjvJ3ss4X76j+UWSPG8m/DSXTAC+adn5lZja8n2Nsd7sH0lBnxowMfEpgNjS5cGHvEnHq6rU23JuUfQTEN/xJbx90YY4Nk+181temCmArLpQXzO1EHZEvw== 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;SFS:(4636009)(376002)(39860400002)(346002)(396003)(136003)(46966005)(7696005)(83380400001)(47076004)(26005)(8936002)(1076003)(82310400002)(4326008)(186003)(81166007)(30864003)(70586007)(82740400003)(5660300002)(336012)(36756003)(2906002)(6666004)(54906003)(44832011)(2616005)(8676002)(426003)(316002)(36906005)(86362001)(478600001)(70206006)(6916009)(579004)(559001);DIR:OUT;SFP:1101; X-OriginatorOrg: arm.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Aug 2020 15:23:37.6147 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: db0fbb74-8af3-44d1-2db7-08d83ed3af93 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: VE1EUR03FT044.eop-EUR03.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM8PR08MB5748 Content-Type: text/plain From: Pierre Gondois AML is a declarative language that is processed by the ACPI AML interpreter. The ACPI AML interpreter will compile the set of declarations into the ACPI Namespace at definition block load time. The hardware information described in AML is effectively mapped in the ACPI Namespace. The AML ACPI namespace interface implement the functionality to search the ACPI Namespace. Example: The AmlFindNode() can be used to locate a device node in the ACPI namespace using an ASL path as the search input. Signed-off-by: Pierre Gondois Signed-off-by: Sami Mujawar --- DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c | 1501 ++++++++++++++++++++ DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h | 74 + 2 files changed, 1575 insertions(+) diff --git a/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c b/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c new file mode 100644 index 0000000000000000000000000000000000000000..dc373748903dd55fa4492874329f2b433c698c02 --- /dev/null +++ b/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.c @@ -0,0 +1,1501 @@ +/** @file + AML NameSpace. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +/* Lexicon: + + NameSeg: + - An ASL NameSeg is a name made of at most 4 chars. + Cf. ACPI 6.3 specification, s19.2.2 'Name and Pathname Terms'. + - An AML NameSeg is a name made of 4 chars. + Cf. ACPI 6.3 specification, s20.2.2 'Name Objects Encoding'. + + NameString: + A NameString is analogous to a pathname. It is made of 0 to 255 NameSegs. + A NameString can be prefixed by a root char ('\') or 0 to 255 carets ('^'). + + A NameString can be ASL or AML encoded. + AML NameStrings can have a NameString prefix (dual or multi-name prefix) + between the root/carets and the list of NameSegs. If the prefix is the + multi-name prefix, then the number of NameSegs is encoded on one single byte. + Cf. ACPI 6.3 specification, s19.2.2 'Name and Pathname Terms'. + Cf. ACPI 6.3 specification, s20.2.2 'Name Objects Encoding'. + + Namespace level: + One level in the AML Namespace level corresponds to one NameSeg. In ASL, + objects names are NameStrings. This means a device can have a name which + spans multiple levels. + E.g.: The ASL code: Device (CLU0.CPU0) corresponds to 2 levels. + + Namespace node: + A namespace node is an object node which has an associated name, and which + changes the current scope. + E.g.: + 1. The "Device ()" ASL statement adds a name to the AML namespace and + changes the current scope to the device scope, this is a namespace node. + 2. The "Scope ()" ASL statement changes the current scope, this is a + namespace node. + 3. A method invocation has a name, but does not add nor change the current + AML scope. This is not a namespace node. + + - Object nodes with the AML_IN_NAMESPACE attribute are namespace nodes. + Buffers (), Packages (), etc. are not part of the namespace. It is however + possible to associate them with a name with the Name () ASL statement. + - The root node is considered as being part of the namespace. + - Some resource data elements can have a name when defining them in + an ASL statement. However, this name is stripped by the ASL compiler. + Thus, they don't have a name in the AML bytestream, and are therefore + not part of the AML namespace. + - Field list elements are part of the namespace. + Fields created by an CreateXXXField () ASL statement are part of the + namespace. The name of these node can be found in the third or fourth + fixed argument. The exact index of the name can be found in the NameIndex + field of the AML_BYTE_ENCODING array. + Field are at the same level as their ASL statement in the namespace. + E.g: + Scope (\) { + OperationRegion (REG0, SystemIO, 0x100, 0x100) + Field (REG0, ByteAcc, NoLock, Preserve) { + FIE0, 1, + FIE1, 5 + } + + Name (BUF0, Buffer (100) {}) + CreateField (BUF0, 5, 2, MEM0) + } + + produces this namespace: + \ (Root) + \-REG0 + \-FIE0 + \-FIE1 + \-BUF0 + \-MEM0 + + Raw AML pathname or Raw AML NameString: + In order to easily manipulate AML NameStrings, the non-NameSegs chars are + removed in raw pathnames/NameStrings. Non-NameSegs chars are the + root char ('\'), carets ('^') and NameString prefixes (Dual/Multi name char). + E.g. The following terminology is defined in this AML Library. + ASL absolute path: "[RootChar]AAAA.BBBB.CCCC\0" + AML absolute path: "[RootChar][MultiNamePrefix][3(NameSegs)]AAAABBBBCCCC" + Raw absolute path: "AAAABBBBCCCC" + + Multi-name: + A NameString with at least 2 NameSegs. A node can have a name which spans + multiple namespace levels. +*/ + +#include + +#include +#include +#include +#include +#include +#include + +/** Context of the path search callback function. + + The function finding a node from a path and a reference node enumerates + the namespace nodes in the tree and compares their absolute path with the + searched path. The enumeration function uses a callback function that can + receive a context. + This structure is used to store the context information required in the + callback function. +*/ +typedef struct AmlPathSearchContext { + /// Backward stream holding the raw AML absolute searched path. + AML_STREAM * SearchPathBStream; + + /// An empty backward stream holding a pre-allocated buffer. This prevents + /// from having to do multiple allocations during the search. + /// This stream is used to query the raw AML absolute path of the node + /// currently being probed. + AML_STREAM * CurrNodePathBStream; + + /// If the node being visited is the node being searched, + /// i.e. its path and the searched path match, + /// save its reference in this pointer. + AML_NODE_HEADER * OutNode; +} AML_PATH_SEARCH_CONTEXT; + +/** Return the first AML namespace node up in the parent hierarchy. + + Return the root node if no namespace node is found is the hierarchy. + + @param [in] Node Node to look at the parents from. + If Node is the root node, OutNode is NULL. + @param [out] OutNode If a namespace node is found, pointer to the + first namespace node of Node's parents. + Stop at the root node otherwise. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + **/ +EFI_STATUS +EFIAPI +AmlGetFirstAncestorNameSpaceNode ( + IN CONST AML_NODE_HEADER * Node, + OUT AML_NODE_HEADER ** OutNode + ) +{ + if (!IS_AML_NODE_VALID (Node) || + (OutNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // If Node is the root node, return NULL. + if (IS_AML_ROOT_NODE (Node)) { + *OutNode = NULL; + return EFI_SUCCESS; + } else { + // Else, get the parent node. + Node = AmlGetParent ((AML_NODE_HEADER*)Node); + if (!IS_AML_NODE_VALID (Node)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } + + // Continue getting the parent node while no namespace node is encountered. + while (TRUE) { + if (IS_AML_ROOT_NODE (Node)) { + break; + } else if (AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)Node, + AML_IN_NAMESPACE + )) { + break; + } else { + Node = AmlGetParent ((AML_NODE_HEADER*)Node); + if (!IS_AML_NODE_VALID (Node)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } + } // while + + *OutNode = (AML_NODE_HEADER*)Node; + return EFI_SUCCESS; +} + +/** Climb up the AML namespace hierarchy. + + This function get the ancestor namespace node in the AML namespace. + If Levels is not zero, skip Levels namespace nodes in the AML namespace. + If Levels is zero, return the first ancestor namespace node. + I.e. if Levels = n, this function returns the (n + 1) ancestor. + + @param [in] Node Pointer to an object node. + @param [in, out] Levels Pointer holding a number of AML namespace levels: + - At entry, the number of levels to go up in + the AML namespace; + - At exit, the number of levels that still need + to be climbed in case of a multi-named node. + Indeed, if a node with a multi-name is found, + and Levels is less than the number of NameSegs + in this name, then the function returns with + the number of levels that still need to be + climbed. + E.g.: If the first ancestor node's name is + "AAAA.BBBB.CCCC" and + Levels = 2 -> i.e go up 3 levels + \ + ... + \-"AAAA.BBBB.CCCC" <----- OutNode + \-"DDDD" <----- Node (Input) + + The function should ideally return a node + with the name "AAAA". However, it is not + possible to split the node name + "AAAA.BBBB.CCCC" to "AAAA". + Thus, OutNode is set to the input node, + and Levels = 2. + In most cases the number of levels to climb + correspond to non multi-name node, and therefore + Levels = 0 at exit. + @param [out] HasRoot The returned node in OutNode has an AML absolute + name, starting with a root char ('\'), or if OutNode + is the root node. + @param [out] OutNode The Levels+1 namespace ancestor of the input node in + the AML namespace. Must be the root node or a + namespace node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlGetAncestorNameSpaceNode ( + IN CONST AML_OBJECT_NODE * Node, + IN OUT UINT32 * Levels, + OUT UINT32 * HasRoot, + OUT CONST AML_NODE_HEADER ** OutNode + ) +{ + EFI_STATUS Status; + + CONST AML_NODE_HEADER * NameSpaceNode; + CHAR8 * NodeName; + UINT32 ParentCnt; + + UINT32 Root; + UINT32 ParentPrefix; + UINT32 SegCount; + + if (!IS_AML_OBJECT_NODE (Node) || + (Levels == NULL) || + (HasRoot == NULL) || + (OutNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ParentCnt = *Levels; + *HasRoot = 0; + + // ParentCnt namespace levels need to be climbed. + do { + // Get the next namespace node in the hierarchy. + Status = AmlGetFirstAncestorNameSpaceNode ( + (CONST AML_NODE_HEADER*)Node, + (AML_NODE_HEADER**)&NameSpaceNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Node = (CONST AML_OBJECT_NODE*)NameSpaceNode; + + if (IS_AML_ROOT_NODE (Node)) { + // Node is the root node. It is not possible to go beyond. + if (ParentCnt != 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + *HasRoot = 1; + break; + } + + NodeName = AmlNodeGetName ((CONST AML_OBJECT_NODE*)Node); + if (NodeName == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Analyze the node name. + Status = AmlParseNameStringInfo ( + NodeName, + &Root, + &ParentPrefix, + &SegCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + if (Root != 0) { + // NodeName is an absolute pathname. + *HasRoot = Root; + + // If the node has Root then it cannot have ParentPrefixes (Carets). + if (ParentPrefix != 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (SegCount == ParentCnt) { + // There are exactly enough AML namespace levels to consume. + // This means the root node was the searched node. + Node = (CONST AML_OBJECT_NODE*)AmlGetRootNode ( + (CONST AML_NODE_HEADER*)Node + ); + if (!IS_AML_ROOT_NODE (Node)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + ParentCnt = 0; + break; + } else if (ParentCnt < SegCount) { + // There are too many AML namespace levels in this name. + // ParentCnt has the right value, just return. + break; + } else { + // ParentCnt > SegCount + // Return error as there must be at least ParentCnt AML namespace + // levels left in the absolute path. + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } else { + // Root is 0. + if (ParentCnt < SegCount) { + // NodeName is a relative path. + // NodeName has enough levels to consume all the ParentCnt. + // Exit. + break; + } else if (SegCount == ParentCnt) { + // There are exactly enough AML namespace levels to consume. + if (ParentPrefix == 0) { + // The node name doesn't have any carets. Get the next namespace + // node and return. + Status = AmlGetFirstAncestorNameSpaceNode ( + (CONST AML_NODE_HEADER*)Node, + (AML_NODE_HEADER**)&NameSpaceNode + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + Node = (CONST AML_OBJECT_NODE*)NameSpaceNode; + ParentCnt = 0; + break; + } else { + // The node name has carets. Need to continue climbing the + // AML namespace. + ParentCnt = ParentPrefix; + } + } else { + // ParentCnt > SegCount + // NodeName doesn't have enough levels to consume all the ParentCnt. + // Update ParentCnt: Consume SegCount levels and add ParentPrefix + // levels. Continue climbing the tree. + ParentCnt = ParentCnt + ParentPrefix - SegCount; + } + } + } while (ParentCnt != 0); + + *OutNode = (CONST AML_NODE_HEADER*)Node; + *Levels = ParentCnt; + + return EFI_SUCCESS; +} + +/** Build the raw absolute AML pathname to Node and write it to a stream. + + A raw AML pathname is an AML pathname where the root char ('\'), + prefix chars ('^') and NameString prefix byte (e.g.: DualNamePrefix) + have been removed. A raw AML pathname is a list of concatenated + NameSegs. + + E.g.: + ASL absolute path: "[RootChar]AAAA.BBBB.CCCC\0" + AML absolute path: "[RootChar][MultiNamePrefix][3(NameSegs)]AAAABBBBCCCC" + Raw absolute path: "AAAABBBBCCCC" + + @param [in] Node Node to build the raw absolute path to + Must be a root node, or a namespace node. + @param [in] InputParent Skip InputParent AML namespace levels before + starting building the raw absolute pathname. + E.g.: - Node's name being "^AAAA.BBBB.CCCC"; + - InputParent = 2; + "BBBB.CCCC" will be skipped (2 + levels), and "^AAAA" will remain. The + first caret is not related to InputParent. + @param [out] RawAbsPathBStream Backward stream to write the raw + pathname to. + If Node is the root node, the Stream data + Buffer will stay empty. + The stream must not be at its end. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetRawNameSpacePath ( + IN CONST AML_NODE_HEADER * Node, + IN UINT32 InputParent, + OUT AML_STREAM * RawAbsPathBStream + ) +{ + EFI_STATUS Status; + + AML_NODE_HEADER * ParentNode; + CHAR8 * NodeName; + + UINT32 Root; + UINT32 ParentPrefix; + UINT32 SegCount; + CONST CHAR8 * NameSeg; + + if ((!IS_AML_ROOT_NODE (Node) && + !AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)Node, + AML_IN_NAMESPACE)) || + !IS_STREAM (RawAbsPathBStream) || + IS_END_OF_STREAM (RawAbsPathBStream) || + !IS_STREAM_BACKWARD (RawAbsPathBStream) || + (InputParent > MAX_UINT8)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + while (1) { + if (IS_AML_ROOT_NODE (Node)) { + break; + } + + NodeName = AmlNodeGetName ((CONST AML_OBJECT_NODE*)Node); + if (NodeName == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlParseNameStringInfo ( + NodeName, + &Root, + &ParentPrefix, + &SegCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + if (SegCount > InputParent) { + // 1.1. If the Node's name has enough levels to consume all the + // InputParent carets, write the levels that are left. + NameSeg = AmlGetFirstNameSeg (NodeName, Root, ParentPrefix); + Status = AmlStreamWrite ( + RawAbsPathBStream, + (CONST UINT8*)NameSeg, + (SegCount - InputParent) * AML_NAME_SEG_SIZE + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + InputParent = 0; + } else { + // (SegCount <= InputParent) + // 1.2. Else save the InputParent in TotalParent to climb + // them later. + InputParent -= SegCount; + } + + InputParent += ParentPrefix; + + if (Root != 0) { + // 2. The Node's name is an absolute path. + // Exit, the root has been reached. + if (InputParent != 0) { + ASSERT (0); + return EFI_NOT_FOUND; + } + break; + } + + Status = AmlGetAncestorNameSpaceNode ( + (CONST AML_OBJECT_NODE*)Node, + &InputParent, + &Root, + (CONST AML_NODE_HEADER**)&ParentNode + ); + if (EFI_ERROR (Status) || + (!IS_AML_NODE_VALID (ParentNode))) { + ASSERT (0); + return Status; + } + + Node = ParentNode; + + if (IS_AML_ROOT_NODE (Node)) { + // 3.1. If the root node has been found while climbing, + // no need to write NameSegs. + // Exit. + break; + } else if (Root != 0) { + // 3.2. An absolute path has been found while climbing the tree. + // If (InputParent != 0), the raw pathname is not the root. + // Write the first [SegCount - InputParent] NameSegs of this + // absolute path. + // Then exit. + if (InputParent != 0) { + // Get the absolute pathname. + NodeName = AmlNodeGetName ((CONST AML_OBJECT_NODE*)Node); + if (NodeName == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Analyze the absolute pathname. + Status = AmlParseNameStringInfo ( + NodeName, + &Root, + &ParentPrefix, + &SegCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + // Writing the n first NameSegs. + // n = SegCount - InputParent + NameSeg = AmlGetFirstNameSeg (NodeName, Root, ParentPrefix); + Status = AmlStreamWrite ( + RawAbsPathBStream, + (CONST UINT8*)NameSeg, + (SegCount - InputParent) * AML_NAME_SEG_SIZE + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + break; + } // (InputParent != 0) + + } + } // while + + return EFI_SUCCESS; +} + +/** Add the RootChar and prefix byte to the raw AML NameString in the + input Stream to create a valid absolute path. + + The prefix byte can be AML_DUAL_NAME_PREFIX, AML_MULTI_NAME_PREFIX + or nothing. + + @param [in, out] AmlPathBStream The Stream initially contains a raw + NameString (i.e. a list of NameSegs). + The Stream can be empty (e.g.: for the + root path). + The stream must not be at its end. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlAddPrefix ( + IN OUT AML_STREAM * AmlPathBStream + ) +{ + EFI_STATUS Status; + UINT32 NameSegCount; + UINT32 NameSegSize; + + // At most 3 bytes are needed for: RootChar + MultiNamePrefix + SegCount. + CHAR8 Prefix[3]; + UINT32 PrefixSize; + + // The Stream contains concatenated NameSegs. + if (!IS_STREAM (AmlPathBStream) || + IS_END_OF_STREAM (AmlPathBStream) || + !IS_STREAM_BACKWARD (AmlPathBStream)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Its size should be a multiple of AML_NAME_SEG_SIZE. + // AML_NAME_SEG_SIZE = 4. Check the 2 lowest bits. + NameSegSize = AmlStreamGetIndex (AmlPathBStream); + if ((NameSegSize & (AML_NAME_SEG_SIZE - 1)) != 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Each NameSeg is 4 bytes so divide the NameSegSize by 4. + NameSegCount = NameSegSize >> 2; + if (NameSegCount > MAX_UINT8) { + // There can be at most 255 NameSegs. + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Prefix[0] = AML_ROOT_CHAR; + + switch (NameSegCount) { + case 0: + { + // Root and parents only NameString (no NameSeg(s)) end with '\0'. + Prefix[1] = AML_ZERO_OP; + PrefixSize = 2; + break; + } + case 1: + { + PrefixSize = 1; + break; + } + case 2: + { + Prefix[1] = AML_DUAL_NAME_PREFIX; + PrefixSize = 2; + break; + } + default: + { + Prefix[1] = AML_MULTI_NAME_PREFIX; + Prefix[2] = (UINT8)NameSegCount; + PrefixSize = 3; + break; + } + } + + // Add the RootChar + prefix (if needed) at the beginning of the pathname. + Status = AmlStreamWrite (AmlPathBStream, (CONST UINT8*)Prefix, PrefixSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + return Status; +} + +/** Remove the prefix bytes of an AML NameString stored in a backward stream + to get a raw NameString. + + The AML encoding for '\', '^', Dual name or multi-name prefix are + stripped off. + E.g: If the ASL path was "\AAAA.BBBB", the AML equivalent would be + "{RootChar}{DualNamePrefix}AAAABBBB". So resultant raw NameString + is "AAAABBBB". + + @param [in, out] AmlPathBStream Backward stream containing an AML + NameString. + The stream must not be at its end. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +*/ +STATIC +EFI_STATUS +EFIAPI +AmlRemovePrefix ( + IN OUT AML_STREAM * AmlPathBStream + ) +{ + EFI_STATUS Status; + + UINT32 TotalSize; + UINT32 RewindSize; + + UINT32 Root; + UINT32 ParentPrefix; + UINT32 SegCount; + + if (!IS_STREAM (AmlPathBStream) || + IS_END_OF_STREAM (AmlPathBStream) || + !IS_STREAM_BACKWARD (AmlPathBStream)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Status = AmlParseNameStringInfo ( + (CHAR8*)AmlStreamGetCurrPos (AmlPathBStream), + &Root, + &ParentPrefix, + &SegCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + TotalSize = AmlComputeNameStringSize (Root, ParentPrefix, SegCount); + if (TotalSize == 0) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // Rewind the stream of all the bytes that are not SegCounts + // to drop the prefix. + RewindSize = TotalSize - (SegCount * AML_NAME_SEG_SIZE); + if (RewindSize != 0) { + Status = AmlStreamRewind (AmlPathBStream, RewindSize); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + } + + return EFI_SUCCESS; +} + +/** Build the absolute ASL pathname to Node. + + BufferSize is always updated to the size of the pathname. + + If: + - the content of BufferSize is >= to the size of the pathname AND; + - Buffer is not NULL. + then copy the pathname in the Buffer. A buffer of the size + MAX_ASL_NAMESTRING_SIZE is big enough to receive any ASL pathname. + + @param [in] Node Node to build the absolute path to. + Must be a root node, or a namespace node. + @param [out] Buffer Buffer to write the path to. + If NULL, only update *BufferSize. + @param [in, out] BufferSize Pointer holding: + - At entry, the size of the Buffer; + - At exit, the size of the pathname. + + @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 +AmlGetAslPathName ( + IN AML_NODE_HEADER * Node, + OUT CHAR8 * Buffer, + IN OUT UINT32 * BufferSize + ) +{ + EFI_STATUS Status; + + // Backward stream used to build the raw AML absolute path to the node. + AML_STREAM RawAmlAbsPathBStream; + CHAR8 * RawAmlAbsPathBuffer; + UINT32 RawAmlAbsPathBufferSize; + + CHAR8 * AmlPathName; + CHAR8 * AslPathName; + UINT32 AslPathNameSize; + + UINT32 Root; + UINT32 ParentPrefix; + UINT32 SegCount; + + if ((!IS_AML_ROOT_NODE (Node) && + !AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)Node, + AML_IN_NAMESPACE)) || + (BufferSize == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + AslPathName = NULL; + + // Allocate a Stream to get the raw AML absolute pathname. + RawAmlAbsPathBufferSize = MAX_AML_NAMESTRING_SIZE; + RawAmlAbsPathBuffer = AllocateZeroPool (RawAmlAbsPathBufferSize); + if (RawAmlAbsPathBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + Status = AmlStreamInit ( + &RawAmlAbsPathBStream, + (UINT8*)RawAmlAbsPathBuffer, + RawAmlAbsPathBufferSize, + EAmlStreamDirectionBackward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Get the raw pathname of the Node. The raw pathname being an + // AML NameString without the RootChar and prefix byte. + // It is a list of concatenated NameSegs. + Status = AmlGetRawNameSpacePath (Node, 0, &RawAmlAbsPathBStream); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Add the RootChar and prefix byte. + Status = AmlAddPrefix (&RawAmlAbsPathBStream); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + AmlPathName = (CHAR8*)AmlStreamGetCurrPos (&RawAmlAbsPathBStream); + + // Analyze the NameString. + Status = AmlParseNameStringInfo ( + (CONST CHAR8*)AmlPathName, + &Root, + &ParentPrefix, + &SegCount + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Compute the size the ASL pathname will take. + AslPathNameSize = AslComputeNameStringSize (Root, ParentPrefix, SegCount); + if (AslPathNameSize == 0) { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto exit_handler; + } + + // Input Buffer is large enough. Copy the pathname if the Buffer is valid. + if ((Buffer != NULL) && (AslPathNameSize <= *BufferSize)) { + Status = ConvertAmlNameToAslName (AmlPathName, &AslPathName); + if (EFI_ERROR (Status)) { + ASSERT (0); + Status = EFI_OUT_OF_RESOURCES; + goto exit_handler; + } + + CopyMem (Buffer, AslPathName, AslPathNameSize); + } + + *BufferSize = AslPathNameSize; + +exit_handler: + // Free allocated memory. + FreePool (RawAmlAbsPathBuffer); + if (AslPathName != NULL) { + FreePool (AslPathName); + } + + return Status; +} + +#if !defined (MDEPKG_NDEBUG) + +/** Recursively print the pathnames in the AML namespace in Node's branch. + + @param [in] Node Pointer to a node. + @param [in] Context An empty forward stream holding a pre-allocated + buffer. This prevents from having to do multiple + allocations during the enumeration. + @param [in, out] Status At entry, contains the status returned by the + last call to this exact function during the + enumeration. + As exit, contains the 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 +AmlDbgPrintNameSpaceCallback ( + IN AML_NODE_HEADER * Node, + IN VOID * Context, + IN OUT EFI_STATUS * Status OPTIONAL + ) +{ + BOOLEAN ContinueEnum; + EFI_STATUS Status1; + + AML_STREAM * CurrNodePathFStream; + CHAR8 * CurrNodePathBuffer; + UINT32 CurrNodePathBufferSize; + + ContinueEnum = TRUE; + Status1 = EFI_SUCCESS; + + if (!IS_AML_NODE_VALID (Node) || + (Context == NULL)) { + ASSERT (0); + Status1 = EFI_INVALID_PARAMETER; + ContinueEnum = FALSE; + goto exit_handler; + } + + if (!IS_AML_ROOT_NODE (Node) && + !AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)Node, + AML_IN_NAMESPACE)) { + // Skip this node and continue enumeration. + goto exit_handler; + } + + if (IS_AML_ROOT_NODE (Node)) { + DEBUG ((DEBUG_INFO, "\\\n")); + } else if (AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)Node, + AML_IN_NAMESPACE)) { + + CurrNodePathFStream = (AML_STREAM*)Context; + + // Check the Context's content. + if (!IS_STREAM (CurrNodePathFStream) || + IS_END_OF_STREAM (CurrNodePathFStream) || + !IS_STREAM_FORWARD (CurrNodePathFStream)) { + ASSERT (0); + Status1 = EFI_INVALID_PARAMETER; + ContinueEnum = FALSE; + goto exit_handler; + } + + CurrNodePathBuffer = (CHAR8*)AmlStreamGetBuffer (CurrNodePathFStream); + CurrNodePathBufferSize = AmlStreamGetMaxBufferSize (CurrNodePathFStream); + + Status1 = AmlGetAslPathName ( + (AML_NODE_HEADER*)Node, + CurrNodePathBuffer, + &CurrNodePathBufferSize + ); + if (EFI_ERROR (Status1)) { + ASSERT (0); + ContinueEnum = FALSE; + goto exit_handler; + } + + DEBUG ((DEBUG_INFO, "%a\n", CurrNodePathBuffer)); + + } else { + ASSERT (0); + Status1 = EFI_INVALID_PARAMETER; + ContinueEnum = FALSE; + } + +exit_handler: + if (Status != NULL) { + *Status = Status1; + } + + return ContinueEnum; +} + +/** Print the absolute pathnames in the AML namespace of + all the nodes in the tree starting from the Root node. + + @param [in] RootNode Pointer to a root node. + + @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 +AmlDbgPrintNameSpace ( + IN AML_ROOT_NODE * RootNode + ) +{ + EFI_STATUS Status; + + AML_STREAM CurrNodePathFStream; + CHAR8 * CurrNodePathBuffer; + UINT32 CurrNodePathBufferSize; + + if (!IS_AML_ROOT_NODE (RootNode)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + DEBUG ((DEBUG_INFO, "AmlNameSpace: AML namespace:\n")); + + // Allocate memory to build the absolute ASL path to each node. + CurrNodePathBufferSize = MAX_AML_NAMESTRING_SIZE; + CurrNodePathBuffer = AllocateZeroPool (CurrNodePathBufferSize); + if (CurrNodePathBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + // An empty forward stream holding a pre-allocated buffer is used + // to avoid multiple allocations during the enumeration. + Status = AmlStreamInit ( + &CurrNodePathFStream, + (UINT8*)CurrNodePathBuffer, + CurrNodePathBufferSize, + EAmlStreamDirectionForward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + AmlEnumTree ( + (AML_NODE_HEADER*)RootNode, + AmlDbgPrintNameSpaceCallback, + (VOID*)&CurrNodePathFStream, + &Status + ); + ASSERT_EFI_ERROR (Status); + +exit_handler: + FreePool (CurrNodePathBuffer); + + return Status; +} + +#endif // MDEPKG_NDEBUG + +/** Callback function to find the node corresponding to an absolute pathname. + + For each namespace node, build its raw AML absolute path. Then compare this + path with the raw AML absolute path of the search node available in the + Context. + + @param [in] Node Pointer to the node to whose pathname is being + tested. + @param [in, out] Context A pointer to AML_PATH_SEARCH_CONTEXT that has: + - The searched path stored in a stream; + - An empty stream to query the pathname of the + probed node; + - A node pointer to store the searched node + if found. + @param [in, out] Status At entry, contains the status returned by the + last call to this exact function during the + enumeration. + As exit, contains the 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 +AmlEnumeratePathCallback ( + IN AML_NODE_HEADER * Node, + IN OUT VOID * Context, + IN OUT EFI_STATUS * Status OPTIONAL +) +{ + BOOLEAN ContinueEnum; + EFI_STATUS Status1; + + AML_PATH_SEARCH_CONTEXT * PathSearchContext; + + AML_STREAM * SearchPathBStream; + CHAR8 * SearchedPath; + + AML_STREAM * CurrNodePathBStream; + CHAR8 * CurrNodePath; + UINT32 CurrNodePathSize; + + ContinueEnum = TRUE; + Status1 = EFI_SUCCESS; + + if (!IS_AML_NODE_VALID (Node) || + (Context == NULL)) { + ASSERT (0); + Status1 = EFI_INVALID_PARAMETER; + ContinueEnum = FALSE; + goto exit_handler; + } + + if (!AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)Node, + AML_IN_NAMESPACE)) { + goto exit_handler; + } + + PathSearchContext = (AML_PATH_SEARCH_CONTEXT*)Context; + SearchPathBStream = PathSearchContext->SearchPathBStream; + CurrNodePathBStream = PathSearchContext->CurrNodePathBStream; + + // Check the Context's content. + if (!IS_STREAM (SearchPathBStream) || + IS_END_OF_STREAM (SearchPathBStream) || + !IS_STREAM_BACKWARD (SearchPathBStream) || + !IS_STREAM (CurrNodePathBStream) || + IS_END_OF_STREAM (CurrNodePathBStream) || + !IS_STREAM_BACKWARD (CurrNodePathBStream)) { + ASSERT (0); + Status1 = EFI_INVALID_PARAMETER; + ContinueEnum = FALSE; + goto exit_handler; + } + + CurrNodePathSize = AmlStreamGetMaxBufferSize (CurrNodePathBStream); + if (CurrNodePathSize == 0) { + ASSERT (0); + Status1 = EFI_INVALID_PARAMETER; + ContinueEnum = FALSE; + goto exit_handler; + } + + SearchedPath = (CHAR8*)AmlStreamGetCurrPos (SearchPathBStream); + CurrNodePath = (CHAR8*)AmlStreamGetCurrPos (CurrNodePathBStream); + + // Get the raw AML absolute pathname of the current node. + Status1 = AmlGetRawNameSpacePath (Node, 0, CurrNodePathBStream); + if (EFI_ERROR (Status1)) { + ASSERT (0); + ContinueEnum = FALSE; + goto exit_handler; + } + + DEBUG (( + DEBUG_VERBOSE, + "AmlNameSpace: " + "Comparing search path with current node path.\n" + )); + DEBUG ((DEBUG_VERBOSE, "Search path:")); + AmlDbgPrintChars ( + DEBUG_VERBOSE, + (CHAR8*)AmlStreamGetCurrPos (SearchPathBStream), + AmlStreamGetIndex (SearchPathBStream) + ); + DEBUG ((DEBUG_VERBOSE, "\nPath of the current node: ")); + AmlDbgPrintChars ( + DEBUG_VERBOSE, + (CHAR8*)AmlStreamGetCurrPos (CurrNodePathBStream), + AmlStreamGetIndex (CurrNodePathBStream) + ); + DEBUG ((DEBUG_VERBOSE, "\n")); + + // Compare the searched path and Node's path. + if ((AmlStreamGetIndex (CurrNodePathBStream) == + AmlStreamGetIndex (SearchPathBStream)) && + (CompareMem ( + AmlStreamGetCurrPos (CurrNodePathBStream), + AmlStreamGetCurrPos (SearchPathBStream), + AmlStreamGetIndex (SearchPathBStream)) == 0)) { + Status1 = EFI_SUCCESS; + ContinueEnum = FALSE; + PathSearchContext->OutNode = Node; + } else { + // If the paths don't match, reset the CurrNodePathStream's content. + Status1 = AmlStreamReset (CurrNodePathBStream); + if (EFI_ERROR (Status1)) { + ASSERT (0); + ContinueEnum = FALSE; + } + } + +exit_handler: + if (Status != NULL) { + *Status = Status1; + } + + return ContinueEnum; +} + +/** Build a raw AML absolute path from a reference node and a relative + ASL path. + + The AslPath can be a relative path or an absolute path. + Node must be a root node or a namespace node. + A root node is expected to be at the top of the tree. + + @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". + @param [in, out] RawAmlAbsSearchPathBStream Backward stream to write the + raw absolute AML path of the + searched node. + The stream must not be at + its end. + + @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. +**/ +STATIC +EFI_STATUS +EFIAPI +AmlBuildAbsoluteAmlPath ( + IN AML_NODE_HEADER * ReferenceNode, + IN CHAR8 * AslPath, + IN OUT AML_STREAM * RawAmlAbsSearchPathBStream + ) +{ + EFI_STATUS Status; + CHAR8 * AmlPath; + + UINT32 AmlNameStringSize; + UINT32 Root; + UINT32 ParentPrefix; + UINT32 SegCount; + + if ((!IS_AML_ROOT_NODE (ReferenceNode) && + !AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)ReferenceNode, + AML_IN_NAMESPACE)) || + (AslPath == NULL) || + !IS_STREAM (RawAmlAbsSearchPathBStream) || + IS_END_OF_STREAM (RawAmlAbsSearchPathBStream) || + !IS_STREAM_BACKWARD (RawAmlAbsSearchPathBStream)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + // 1. Validate, analyze and convert the AslPath to an AmlPath. + Status = ConvertAslNameToAmlName (AslPath, &AmlPath); + if (EFI_ERROR (Status)) { + ASSERT (0); + return Status; + } + + Status = AmlParseNameStringInfo (AmlPath, &Root, &ParentPrefix, &SegCount); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // Not possible to go beyond the root. + if (IS_AML_ROOT_NODE (ReferenceNode) && (ParentPrefix != 0)) { + Status = EFI_INVALID_PARAMETER; + ASSERT (0); + goto exit_handler; + } + + AmlNameStringSize = AmlComputeNameStringSize (Root, ParentPrefix, SegCount); + if (AmlNameStringSize == 0) { + Status = EFI_INVALID_PARAMETER; + ASSERT (0); + goto exit_handler; + } + + // 2.1. Write the AML path to the stream. + Status = AmlStreamWrite ( + RawAmlAbsSearchPathBStream, + (CONST UINT8*)AmlPath, + AmlNameStringSize + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // 2.2. Then remove the AML prefix (root char, parent prefix, etc.) + // to obtain a raw AML NameString. Raw AML NameString are easier + // to manipulate. + Status = AmlRemovePrefix (RawAmlAbsSearchPathBStream); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // 3. If AslPath is a relative path and the reference Node is not + // the root node, fill the Stream with the absolute path to the + // reference node. + if ((Root == 0) && !IS_AML_ROOT_NODE (ReferenceNode)) { + Status = AmlGetRawNameSpacePath ( + ReferenceNode, + ParentPrefix, + RawAmlAbsSearchPathBStream + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + } + } + +exit_handler: + // Free allocated memory. + FreePool (AmlPath); + + return Status; +} + +/** 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 + + @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_HEADER * ReferenceNode, + IN CHAR8 * AslPath, + OUT AML_NODE_HEADER ** OutNode + ) +{ + EFI_STATUS Status; + + AML_PATH_SEARCH_CONTEXT PathSearchContext; + AML_ROOT_NODE * RootNode; + + // Backward stream used to build the raw AML absolute path to the searched + // node. + AML_STREAM RawAmlAbsSearchPathBStream; + CHAR8 * RawAmlAbsSearchPathBuffer; + UINT32 RawAmlAbsSearchPathBufferSize; + + // Backward stream used to store the raw AML absolute path of the node + // currently enumerated in the tree. This path can then be compared to the + // RawAmlAbsSearchPath. + AML_STREAM RawAmlAbsCurrNodePathBStream; + CHAR8 * RawAmlAbsCurrNodePathBuffer; + UINT32 RawAmlAbsCurrNodePathBufferSize; + + if ((!IS_AML_ROOT_NODE (ReferenceNode) && + !AmlNodeHasAttribute ( + (CONST AML_OBJECT_NODE*)ReferenceNode, + AML_IN_NAMESPACE)) || + (AslPath == NULL) || + (OutNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *OutNode = NULL; + RawAmlAbsCurrNodePathBuffer = NULL; + + // 1. Build a raw absolute AML path from the reference node and the ASL + // path. For this: + // 1.1. First initialize a backward stream. + RawAmlAbsSearchPathBufferSize = MAX_AML_NAMESTRING_SIZE; + RawAmlAbsSearchPathBuffer = AllocateZeroPool (RawAmlAbsSearchPathBufferSize); + if (RawAmlAbsSearchPathBuffer == NULL) { + ASSERT (0); + return EFI_OUT_OF_RESOURCES; + } + + Status = AmlStreamInit ( + &RawAmlAbsSearchPathBStream, + (UINT8*)RawAmlAbsSearchPathBuffer, + RawAmlAbsSearchPathBufferSize, + EAmlStreamDirectionBackward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // 1.2. Then build the raw AML absolute path. + Status = AmlBuildAbsoluteAmlPath ( + ReferenceNode, + AslPath, + &RawAmlAbsSearchPathBStream + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // 2. Find the root node by climbing up the tree from the reference node. + RootNode = AmlGetRootNode (ReferenceNode); + if (RootNode == NULL) { + ASSERT (0); + Status = EFI_INVALID_PARAMETER; + goto exit_handler; + } + + // 3. If the searched node is the root node, return. + // For the Root Node there is no NameSegs so the length of + // the stream will be zero. + if (AmlStreamGetIndex (&RawAmlAbsSearchPathBStream) == 0) { + *OutNode = (AML_NODE_HEADER*)RootNode; + Status = EFI_SUCCESS; + goto exit_handler; + } + + // 4. Create a backward stream large enough to hold the current node path + // during enumeration. This prevents from doing multiple allocation/free + // operations. + RawAmlAbsCurrNodePathBufferSize = MAX_ASL_NAMESTRING_SIZE; + RawAmlAbsCurrNodePathBuffer = AllocateZeroPool ( + RawAmlAbsCurrNodePathBufferSize + ); + if (RawAmlAbsCurrNodePathBuffer == NULL) { + ASSERT (0); + Status = EFI_OUT_OF_RESOURCES; + goto exit_handler; + } + + Status = AmlStreamInit ( + &RawAmlAbsCurrNodePathBStream, + (UINT8*)RawAmlAbsCurrNodePathBuffer, + RawAmlAbsCurrNodePathBufferSize, + EAmlStreamDirectionBackward + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + // 5. Fill a path search context structure with: + // - SearchPathStream: backward stream containing the raw absolute AML + // path to the searched node; + // - CurrNodePathStream: backward stream containing the raw absolute AML + // of the node currently being enumerated; + // - OutNode: node pointer to the store the potentially found node. + PathSearchContext.SearchPathBStream = &RawAmlAbsSearchPathBStream; + PathSearchContext.CurrNodePathBStream = &RawAmlAbsCurrNodePathBStream; + PathSearchContext.OutNode = NULL; + + // 6. Iterate through the namespace nodes of the tree. + // For each namespace node, build its raw AML absolute path. Then compare + // it with the search path. + AmlEnumTree ( + (AML_NODE_HEADER*)RootNode, + AmlEnumeratePathCallback, + (VOID*)&PathSearchContext, + &Status + ); + if (EFI_ERROR (Status)) { + ASSERT (0); + goto exit_handler; + } + + *OutNode = PathSearchContext.OutNode; + if (*OutNode == NULL) { + Status = EFI_NOT_FOUND; + } + +exit_handler: + // Free allocated memory. + FreePool (RawAmlAbsSearchPathBuffer); + if (RawAmlAbsCurrNodePathBuffer != NULL) { + FreePool (RawAmlAbsCurrNodePathBuffer); + } + + return Status; +} diff --git a/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h b/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h new file mode 100644 index 0000000000000000000000000000000000000000..3d8ebc8b182983606af7e7ddfe3078fd729fb5b6 --- /dev/null +++ b/DynamicTablesPkg/Library/Common/AmlLib/NameSpace/AmlNameSpace.h @@ -0,0 +1,74 @@ +/** @file + AML NameSpace. + + Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef AML_NAMESPACE_H_ +#define AML_NAMESPACE_H_ + +#include +#include + +/** Return the first AML namespace node up in the parent hierarchy. + + Return the root node if no namespace node is found is the hierarchy. + + @param [in] Node Node to look at the parents from. + If Node is the root node, OutNode is NULL. + @param [out] OutNode If a namespace node is found, pointer to the + first namespace node of Node's parents. + Stop at the root node otherwise. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + **/ +EFI_STATUS +EFIAPI +AmlGetFirstAncestorNameSpaceNode ( + IN CONST AML_NODE_HEADER * Node, + OUT AML_NODE_HEADER ** OutNode + ); + +/** Build the raw absolute AML pathname to Node and write it to a stream. + + A raw AML pathname is an AML pathname where the root char ('\'), + prefix chars ('^') and NameString prefix byte (e.g.: DualNamePrefix) + have been removed. A raw AML pathname is a list of concatenated + NameSegs. + + E.g.: + ASL absolute path: "[RootChar]AAAA.BBBB.CCCC\0" + AML absolute path: "[RootChar][MultiNamePrefix][3(NameSegs)]AAAABBBBCCCC" + Raw absolute path: "AAAABBBBCCCC" + + @param [in] Node Node to build the raw absolute path to + Must be a root node, or a namespace node. + @param [in] InputParent Skip InputParent AML namespace levels before + starting building the raw absolute pathname. + E.g.: - Node's name being "^AAAA.BBBB.CCCC"; + - InputParent = 2; + "BBBB.CCCC" will be skipped (2 + levels), and "^AAAA" will remain. The + first caret is not related to InputParent. + @param [out] RawAbsPathBStream Backward stream to write the raw + pathname to. + If Node is the root node, the Stream data + Buffer will stay empty. + The stream must not be at its end. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_BUFFER_TOO_SMALL No space left in the buffer. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +AmlGetRawNameSpacePath ( + IN CONST AML_NODE_HEADER * Node, + IN UINT32 InputParent, + OUT AML_STREAM * RawAbsPathBStream + ); + +#endif // AML_NAMESPACE_H_ -- 'Guid(CE165669-3EF3-493F-B85D-6190EE5B9759)'