From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mx.groups.io with SMTP id smtpd.web11.6913.1624451918712340912 for ; Wed, 23 Jun 2021 05:38:38 -0700 Authentication-Results: mx.groups.io; dkim=missing; spf=pass (domain: arm.com, ip: 217.140.110.172, mailfrom: pierre.gondois@arm.com) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 4D466ED1; Wed, 23 Jun 2021 05:38:38 -0700 (PDT) Received: from e120189.arm.com (unknown [10.57.78.245]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id C92BC3F718; Wed, 23 Jun 2021 05:38:36 -0700 (PDT) From: "PierreGondois" To: devel@edk2.groups.io, Sami Mujawar , Alexei Fedorov Cc: Akanksha Jain , Alexandru Elisei Subject: [PATCH v1 03/14] DynamicTablesPkg: FdtHwInfoParser: Add FDT utility functions Date: Wed, 23 Jun 2021 13:38:17 +0100 Message-Id: <20210623123828.23693-4-Pierre.Gondois@arm.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210623123828.23693-1-Pierre.Gondois@arm.com> References: <20210623123828.23693-1-Pierre.Gondois@arm.com> From: Pierre Gondois The FdtHwInfoParser parses a platform Device Tree and populates the Platform Information repository with Configuration Manager objects. Therefore, add a set of helper functions to simplify parsing of the platform Device Tree. Signed-off-by: Pierre Gondois Signed-off-by: Sami Mujawar --- .../Library/FdtHwInfoParserLib/FdtUtility.c | 909 ++++++++++++++++++ .../Library/FdtHwInfoParserLib/FdtUtility.h | 458 +++++++++ 2 files changed, 1367 insertions(+) create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtUtility.c create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtUtility.h diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtUtility.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtUtility.c new file mode 100644 index 000000000000..0fca82aedf9e --- /dev/null +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtUtility.c @@ -0,0 +1,909 @@ +/** @file + Flattened device tree utility. + + Copyright (c) 2021, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - Device tree Specification - Release v0.3 + - linux/Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic.yaml + - linux//Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic.yaml +**/ + +#include +#include "FdtUtility.h" + +/** Get the interrupt Id of an interrupt described in a fdt. + + Data must describe a GIC interrupt. A GIC interrupt is on at least + 3 UINT32 cells. + This function DOES NOT SUPPORT extended SPI range and extended PPI range. + + @param [in] Data Pointer to the first cell of an "interrupts" property. + + @retval The interrupt id. +**/ +UINT32 +EFIAPI +FdtGetInterruptId ( + UINT32 CONST * Data + ) +{ + UINT32 IrqType; + UINT32 IrqId; + + ASSERT (Data != NULL); + + IrqType = fdt32_to_cpu (Data[IRQ_TYPE_OFFSET]); + IrqId = fdt32_to_cpu (Data[IRQ_NUMBER_OFFSET]); + + switch (IrqType) { + case DT_SPI_IRQ: + IrqId += SPI_OFFSET; + break; + + case DT_PPI_IRQ: + IrqId += PPI_OFFSET; + break; + + default: + ASSERT (0); + IrqId = 0; + } + + return IrqId; +} + +/** Get the ACPI interrupt flags of an interrupt described in a fdt. + + Data must describe a GIC interrupt. A GIC interrupt is on at least + 3 UINT32 cells. + + PPI interrupt cpu mask on bits [15:8] are ignored. + + @param [in] Data Pointer to the first cell of an "interrupts" property. + + @retval The interrupt flags (for ACPI). +**/ +UINT32 +EFIAPI +FdtGetInterruptFlags ( + UINT32 CONST * Data + ) +{ + UINT32 IrqFlags; + UINT32 AcpiIrqFlags; + + ASSERT (Data != NULL); + + IrqFlags = fdt32_to_cpu (Data[IRQ_FLAGS_OFFSET]); + + AcpiIrqFlags = DT_IRQ_IS_EDGE_TRIGGERED (IrqFlags) ? BIT0 : 0; + AcpiIrqFlags |= DT_IRQ_IS_ACTIVE_LOW (IrqFlags) ? BIT1 : 0; + + return AcpiIrqFlags; +} + +/** Check whether a node has the input name. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] Node Offset of the node to check the name. + @param [in] SearchName Node name to search. + This is a NULL terminated string. + + @retval True The node has the input name. + @retval FALSE Otherwise, or error. +**/ +STATIC +BOOLEAN +EFIAPI +FdtNodeHasName ( + IN CONST VOID * Fdt, + IN INT32 Node, + IN CONST VOID * SearchName + ) +{ + CONST CHAR8 * NodeName; + UINT32 Length; + + if ((Fdt == NULL) || + (SearchName == NULL)) { + ASSERT (0); + return FALSE; + } + + // Always compare the whole string. Don't stop at the "@" char. + Length = (UINT32)AsciiStrLen (SearchName); + + // Get the address of the node name. + NodeName = fdt_offset_ptr (Fdt, Node + FDT_TAGSIZE, Length + 1); + if (NodeName == NULL) { + return FALSE; + } + + // SearchName must be longer than the node name. + if (Length > AsciiStrLen (NodeName)) { + return FALSE; + } + + // Use CompareMem here instead of AsciiStrnCmp as the NodeName + // may contain the node name followed by '@'0x. + if (AsciiStrnCmp (NodeName, SearchName, Length) != 0) { + return FALSE; + } + + // The name matches perfectly, or + // the node name is XXX@addr and the XXX matches. + if ((NodeName[Length] == '\0') || + (NodeName[Length] == '@')) { + return TRUE; + } + + return FALSE; +} + +/** Iterate through the list of strings in the Context, + and check whether at least one string is matching the + "compatible" property of the node. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] Node Offset of the node to operate the check on. + @param [in] CompatInfo COMPATIBILITY_INFO containing the list of compatible + strings to compare with the "compatible" property + of the node. + + @retval TRUE At least one string matched, the node is compatible. + @retval FALSE Otherwise, or error. +**/ +BOOLEAN +EFIAPI +FdtNodeIsCompatible ( + IN CONST VOID * Fdt, + IN INT32 Node, + IN CONST VOID * CompatInfo + ) +{ + UINT32 Index; + CONST COMPATIBILITY_STR * CompatibleTable; + UINT32 Count; + CONST VOID * Prop; + INT32 PropLen; + + if ((Fdt == NULL) || + (CompatInfo == NULL)) { + ASSERT (0); + return FALSE; + } + + Count = ((COMPATIBILITY_INFO*)CompatInfo)->Count; + CompatibleTable = ((COMPATIBILITY_INFO*)CompatInfo)->CompatTable; + + // Get the "compatible" property. + Prop = fdt_getprop (Fdt, Node, "compatible", &PropLen); + if ((Prop == NULL) || (PropLen < 0)) { + return FALSE; + } + + for (Index = 0; Index < Count; Index++) { + if (fdt_stringlist_contains ( + Prop, + PropLen, + CompatibleTable[Index].CompatStr + )) { + return TRUE; + } + } // for + + return FALSE; +} + +/** Check whether a node has a property. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] Node Offset of the node to operate the check on. + @param [in] PropertyName Name of the property to search. + This is a NULL terminated string. + + @retval True The node has the property. + @retval FALSE Otherwise, or error. +**/ +BOOLEAN +EFIAPI +FdtNodeHasProperty ( + IN CONST VOID * Fdt, + IN INT32 Node, + IN CONST VOID * PropertyName + ) +{ + INT32 Size; + CONST VOID * Prop; + + if ((Fdt == NULL) || + (PropertyName == NULL)) { + ASSERT (0); + return FALSE; + } + + Prop = fdt_getprop (Fdt, Node, PropertyName, &Size); + if ((Prop == NULL) || (Size < 0)) { + return FALSE; + } + return TRUE; +} + +/** Get the next node in the whole DT fulfilling a condition. + + The condition to fulfill is checked by the NodeChecker function. + Context is passed to NodeChecker. + + The Device tree is traversed in a depth-first search, starting from Node. + The input Node is skipped. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in, out] Node At entry: Node offset to start the search. + This first node is skipped. + Write (-1) to search the whole tree. + At exit: If success, contains the offset of + the next node fulfilling the + condition. + @param [in, out] Depth Depth is incremented/decremented of the depth + difference between the input Node and the + output Node. + E.g.: If the output Node is a child node + of the input Node, contains (+1). + @param [in] NodeChecker Function called to check if the condition + is fulfilled. + @param [in] Context Context for the NodeChecker. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND No matching node found. +**/ +STATIC +EFI_STATUS +EFIAPI +FdtGetNextCondNode ( + IN CONST VOID * Fdt, + IN OUT INT32 * Node, + IN OUT INT32 * Depth, + IN NODE_CHECKER_FUNC NodeChecker, + IN CONST VOID * Context + ) +{ + INT32 CurrNode; + + if ((Fdt == NULL) || + (Node == NULL) || + (Depth == NULL) || + (NodeChecker == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + CurrNode = *Node; + do { + CurrNode = fdt_next_node (Fdt, CurrNode, Depth); + if ((CurrNode == -FDT_ERR_NOTFOUND) || + (*Depth < 0)) { + // End of the tree, no matching node found. + return EFI_NOT_FOUND; + } else if (CurrNode < 0) { + // An error occurred. + ASSERT (0); + return EFI_ABORTED; + } + } while (!NodeChecker (Fdt, CurrNode, Context)); + + // Matching node found. + *Node = CurrNode; + return EFI_SUCCESS; +} + +/** Get the next node in a branch fulfilling a condition. + + The condition to fulfill is checked by the NodeChecker function. + Context is passed to NodeChecker. + + The Device tree is traversed in a depth-first search, starting from Node. + The input Node is skipped. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this + branch. + Write (-1) to search the whole tree. + @param [in] NodeChecker Function called to check if the condition + is fulfilled. + @param [in] Context Context for the NodeChecker. + @param [in, out] Node At entry: Node offset to start the search. + This first node is skipped. + Write (-1) to search the whole tree. + At exit: If success, contains the offset + of the next node in the branch + fulfilling the condition. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND No matching node found. +**/ +STATIC +EFI_STATUS +EFIAPI +FdtGetNextCondNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN NODE_CHECKER_FUNC NodeChecker, + IN CONST VOID * Context, + IN OUT INT32 * Node + ) +{ + EFI_STATUS Status; + INT32 CurrNode; + INT32 Depth; + + if ((Fdt == NULL) || + (Node == NULL) || + (NodeChecker == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + CurrNode = FdtBranch; + Depth = 0; + + // First, check the Node is in the sub-nodes of the branch. + // This allows to find the relative depth of Node in the branch. + if (CurrNode != *Node) { + for (CurrNode = fdt_next_node (Fdt, CurrNode, &Depth); + (CurrNode >= 0) && (Depth > 0); + CurrNode = fdt_next_node (Fdt, CurrNode, &Depth)) { + if (CurrNode == *Node) { + // Node found. + break; + } + } // for + + if ((CurrNode < 0) || (Depth <= 0)) { + // Node is not a node in the branch, or an error occurred. + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + } + + // Get the next node in the tree fulfilling the condition, + // in any branch. + Status = FdtGetNextCondNode ( + Fdt, + Node, + &Depth, + NodeChecker, + Context + ); + if (EFI_ERROR (Status)) { + ASSERT (Status == EFI_NOT_FOUND); + return Status; + } + + if (Depth <= 0) { + // The node found is not in the right branch. + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/** Get the next node in a branch having a matching name. + + The Device tree is traversed in a depth-first search, starting from Node. + The input Node is skipped. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this branch. + Write (-1) to search the whole tree. + @param [in] NodeName The node name to search. + This is a NULL terminated string. + @param [in, out] Node At entry: Node offset to start the search. + This first node is skipped. + Write (-1) to search the whole tree. + At exit: If success, contains the offset of + the next node in the branch + having a matching name. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND No matching node found. +**/ +EFI_STATUS +EFIAPI +FdtGetNextNamedNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN CONST CHAR8 * NodeName, + IN OUT INT32 * Node + ) +{ + return FdtGetNextCondNodeInBranch ( + Fdt, + FdtBranch, + FdtNodeHasName, + NodeName, + Node + ); +} + +/** Get the next node in a branch with at least one compatible property. + + The Device tree is traversed in a depth-first search, starting from Node. + The input Node is skipped. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this branch. + Write (-1) to search the whole tree. + @param [in] CompatNamesInfo Table of compatible strings to compare with + the compatible property of the node. + @param [in, out] Node At entry: Node offset to start the search. + This first node is skipped. + Write (-1) to search the whole tree. + At exit: If success, contains the offset of + the next node in the branch + being compatible. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND No matching node found. +**/ +EFI_STATUS +EFIAPI +FdtGetNextCompatNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN CONST COMPATIBILITY_INFO * CompatNamesInfo, + IN OUT INT32 * Node + ) +{ + return FdtGetNextCondNodeInBranch ( + Fdt, + FdtBranch, + FdtNodeIsCompatible, + (CONST VOID*)CompatNamesInfo, + Node + ); +} + +/** Get the next node in a branch having the PropName property. + + The Device tree is traversed in a depth-first search, starting from Node. + The input Node is skipped. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this branch. + Write (-1) to search the whole tree. + @param [in] PropName Name of the property to search. + This is a NULL terminated string. + @param [in, out] Node At entry: Node offset to start the search. + This first node is skipped. + Write (-1) to search the whole tree. + At exit: If success, contains the offset of + the next node in the branch + being compatible. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND No matching node found. +**/ +EFI_STATUS +EFIAPI +FdtGetNextPropNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN CONST CHAR8 * PropName, + IN OUT INT32 * Node + ) +{ + return FdtGetNextCondNodeInBranch ( + Fdt, + FdtBranch, + FdtNodeHasProperty, + (CONST VOID*)PropName, + Node + ); +} + +/** Count the number of Device Tree nodes fulfilling a condition + in a Device Tree branch. + + The condition to fulfill is checked by the NodeChecker function. + Context is passed to NodeChecker. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this branch. + Write (-1) to search the whole tree. + @param [in] NodeChecker Function called to check the condition is + fulfilled. + @param [in] Context Context for the NodeChecker. + @param [out] NodeCount If success, contains the count of nodes + fulfilling the condition. + Can be 0. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +STATIC +EFI_STATUS +EFIAPI +FdtCountCondNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN NODE_CHECKER_FUNC NodeChecker, + IN CONST VOID * Context, + OUT UINT32 * NodeCount + ) +{ + EFI_STATUS Status; + INT32 CurrNode; + + if ((Fdt == NULL) || + (NodeChecker == NULL) || + (NodeCount == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + *NodeCount = 0; + CurrNode = FdtBranch; + while (TRUE) { + Status = FdtGetNextCondNodeInBranch ( + Fdt, + FdtBranch, + NodeChecker, + Context, + &CurrNode + ); + if (EFI_ERROR (Status) && + (Status != EFI_NOT_FOUND)) { + ASSERT (0); + return Status; + } else if (Status == EFI_NOT_FOUND) { + break; + } + (*NodeCount)++; + } + return EFI_SUCCESS; +} + +/** Count the number of nodes in a branch with the input name. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this branch. + Write (-1) to search the whole tree. + @param [in] NodeName Node name to search. + This is a NULL terminated string. + @param [out] NodeCount If success, contains the count of nodes + fulfilling the condition. + Can be 0. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +FdtCountNamedNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN CONST CHAR8 * NodeName, + OUT UINT32 * NodeCount + ) +{ + return FdtCountCondNodeInBranch ( + Fdt, + FdtBranch, + FdtNodeHasName, + NodeName, + NodeCount + ); +} + +/** Count the number of nodes in a branch with at least + one compatible property. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this branch. + Write (-1) to search the whole tree. + @param [in] CompatNamesInfo Table of compatible strings to + compare with the compatible property + of the node. + @param [out] NodeCount If success, contains the count of nodes + fulfilling the condition. + Can be 0. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +FdtCountCompatNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN CONST COMPATIBILITY_INFO * CompatNamesInfo, + OUT UINT32 * NodeCount + ) +{ + return FdtCountCondNodeInBranch ( + Fdt, + FdtBranch, + FdtNodeIsCompatible, + CompatNamesInfo, + NodeCount + ); +} + +/** Count the number of nodes in a branch having the PropName property. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this branch. + Write (-1) to search the whole tree. + @param [in] PropName Name of the property to search. + This is a NULL terminated string. + @param [out] NodeCount If success, contains the count of nodes + fulfilling the condition. + Can be 0. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +FdtCountPropNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN CONST CHAR8 * PropName, + OUT UINT32 * NodeCount + ) +{ + return FdtCountCondNodeInBranch ( + Fdt, + FdtBranch, + FdtNodeHasProperty, + PropName, + NodeCount + ); +} + +/** Get the interrupt-controller node handling the interrupts of + the input node. + + To do this, recursively search a node with either the "interrupt-controller" + or the "interrupt-parent" property in the parents of Node. + + Devicetree Specification, Release v0.3, + 2.4.1 "Properties for Interrupt Generating Devices": + Because the hierarchy of the nodes in the interrupt tree + might not match the devicetree, the interrupt-parent + property is available to make the definition of an + interrupt parent explicit. The value is the phandle to the + interrupt parent. If this property is missing from a + device, its interrupt parent is assumed to be its devicetree + parent. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] Node Offset of the node to start the search. + @param [out] IntcNode If success, contains the offset of the + interrupt-controller node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND No interrupt-controller node found. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +FdtGetIntcParentNode ( + IN CONST VOID * Fdt, + IN INT32 Node, + OUT INT32 * IntcNode + ) +{ + CONST UINT32 * PHandle; + INT32 Size; + CONST VOID * Prop; + + if ((Fdt == NULL) || + (IntcNode == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + while (TRUE) { + // Check whether the node has the "interrupt-controller" property. + Prop = fdt_getprop (Fdt, Node, "interrupt-controller", &Size); + if ((Prop != NULL) && (Size >= 0)) { + // The interrupt-controller has been found. + *IntcNode = Node; + return EFI_SUCCESS; + } else { + // Check whether the node has the "interrupt-parent" property. + PHandle = fdt_getprop (Fdt, Node, "interrupt-parent", &Size); + if ((PHandle != NULL) && (Size == sizeof (UINT32))) { + // The phandle of the interrupt-controller has been found. + // Search the node having this phandle and return it. + Node = fdt_node_offset_by_phandle (Fdt, fdt32_to_cpu (*PHandle)); + if (Node < 0) { + ASSERT (0); + return EFI_ABORTED; + } + + *IntcNode = Node; + return EFI_SUCCESS; + } else if (Size != -FDT_ERR_NOTFOUND) { + ASSERT (0); + return EFI_ABORTED; + } + } + + if (Node == 0) { + // We are at the root of the tree. Not parent available. + return EFI_NOT_FOUND; + } + + // Get the parent of the node. + Node = fdt_parent_offset (Fdt, Node); + if (Node < 0) { + // An error occurred. + ASSERT (0); + return EFI_ABORTED; + } + } // while +} + +/** Get the "interrupt-cells" property value of the node. + + The "interrupts" property requires to know the number of cells used + to encode an interrupt. This information is stored in the + interrupt-controller of the input Node. + + @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). + @param [in] IntcNode Offset of an interrupt-controller node. + @param [out] IntCells If success, contains the "interrupt-cells" + property of the IntcNode. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_UNSUPPORTED Unsupported. +**/ +EFI_STATUS +EFIAPI +FdtGetInterruptCellsInfo ( + IN CONST VOID * Fdt, + IN INT32 IntcNode, + OUT INT32 * IntCells + ) +{ + CONST UINT32 * Data; + INT32 Size; + + if ((Fdt == NULL) || + (IntCells == NULL)) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Data = fdt_getprop (Fdt, IntcNode, "#interrupt-cells", &Size); + if ((Data == NULL) || (Size != sizeof (UINT32))) { + // If error or not on one UINT32 cell. + ASSERT (0); + return EFI_ABORTED; + } + + *IntCells = fdt32_to_cpu (*Data); + + return EFI_SUCCESS; +} + +/** Get the "#address-cells" and/or "#size-cells" property of the node. + + According to the Device Tree specification, s2.3.5 "#address-cells and + #size-cells": + "If missing, a client program should assume a default value of 2 for + #address-cells, and a value of 1 for #size-cells." + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] Node Offset of the node having to get the + "#address-cells" and "#size-cells" + properties from. + @param [out] AddressCells If success, number of address-cells. + If the property is not available, + default value is 2. + @param [out] SizeCells If success, number of size-cells. + If the property is not available, + default value is 1. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +FdtGetAddressInfo ( + IN CONST VOID * Fdt, + IN INT32 Node, + OUT INT32 * AddressCells, OPTIONAL + OUT INT32 * SizeCells OPTIONAL + ) +{ + if (Fdt == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + if (AddressCells != NULL) { + *AddressCells = fdt_address_cells (Fdt, Node); + if (*AddressCells < 0) { + ASSERT (0); + return EFI_ABORTED; + } + } + + if (SizeCells != NULL) { + *SizeCells = fdt_size_cells (Fdt, Node); + if (*SizeCells < 0) { + ASSERT (0); + return EFI_ABORTED; + } + } + + return EFI_SUCCESS; +} + +/** Get the "#address-cells" and/or "#size-cells" property of the parent node. + + According to the Device Tree specification, s2.3.5 "#address-cells and + #size-cells": + "If missing, a client program should assume a default value of 2 for + #address-cells, and a value of 1 for #size-cells." + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] Node Offset of the node having to get the + "#address-cells" and "#size-cells" + properties from its parent. + @param [out] AddressCells If success, number of address-cells. + If the property is not available, + default value is 2. + @param [out] SizeCells If success, number of size-cells. + If the property is not available, + default value is 1. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +FdtGetParentAddressInfo ( + IN CONST VOID * Fdt, + IN INT32 Node, + OUT INT32 * AddressCells, OPTIONAL + OUT INT32 * SizeCells OPTIONAL + ) +{ + if (Fdt == NULL) { + ASSERT (0); + return EFI_INVALID_PARAMETER; + } + + Node = fdt_parent_offset (Fdt, Node); + if (Node < 0) { + // End of the tree, or an error occurred. + ASSERT (0); + return EFI_ABORTED; + } + + return FdtGetAddressInfo (Fdt, Node, AddressCells, SizeCells); +} diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtUtility.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtUtility.h new file mode 100644 index 000000000000..0076c5066d4c --- /dev/null +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtUtility.h @@ -0,0 +1,458 @@ +/** @file + Flattened device tree utility. + + Copyright (c) 2021, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Reference(s): + - Device tree Specification - Release v0.3 + - linux/Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic.yaml + - linux//Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic.yaml +**/ + +#ifndef FDT_UTILITY_H_ +#define FDT_UTILITY_H_ + +/** Get the offset of an address in a "reg" Device Tree property. + + In a Device Tree, the "reg" property stores address/size couples. + They are stored on N 32-bits cells. + Based on the value of the #address-cells, the #size-cells and the + index in the "reg" property, compute the number of 32-bits cells + to skip. + + @param [in] Index Index in the reg property. + @param [in] AddrCells Number of cells used to store an address. + @param [in] SizeCells Number of cells used to store the size of + an address. + + @retval Number of 32-bits cells to skip to access the address. +*/ +#define GET_DT_REG_ADDRESS_OFFSET(Index, AddrCells, SizeCells) ( \ + (Index) * ((AddrCells) + (SizeCells)) \ + ) + +/** Get the offset of an address size in a "reg" Device Tree property. + + In a Device Tree, the "reg" property stores address/size couples. + They are stored on N 32-bits cells. + Based on the value of the #address-cells, the #size-cells and the + index in the "reg" property, compute the number of 32-bits cells + to skip. + + @param [in] Index Index in the reg property. + @param [in] AddrCells Number of cells used to store an address. + @param [in] SizeCells Number of cells used to store the size of + an address. + + @retval Number of 32-bits cells to skip to access the address size. +*/ +#define GET_DT_REG_SIZE_OFFSET(Index, AddrCells, SizeCells) ( \ + GET_DT_REG_ADDRESS_OFFSET ((Index), (AddrCells), (SizeCells)) + \ + (SizeCells) \ + ) + +/// Maximum string length for compatible names. +#define COMPATIBLE_STR_LEN (32U) + +/// Interrupt macros +#define PPI_OFFSET (16U) +#define SPI_OFFSET (32U) +#define DT_PPI_IRQ (1U) +#define DT_SPI_IRQ (0U) +#define DT_IRQ_IS_EDGE_TRIGGERED(x) ((((x) & (BIT0 | BIT2)) != 0)) +#define DT_IRQ_IS_ACTIVE_LOW(x) ((((x) & (BIT1 | BIT3)) != 0)) +#define IRQ_TYPE_OFFSET (0U) +#define IRQ_NUMBER_OFFSET (1U) +#define IRQ_FLAGS_OFFSET (2U) + +/** Get the interrupt Id of an interrupt described in a fdt. + + Data must describe a GIC interrupt. A GIC interrupt is on at least + 3 UINT32 cells. + This function DOES NOT SUPPORT extended SPI range and extended PPI range. + + @param [in] Data Pointer to the first cell of an "interrupts" property. + + @retval The interrupt id. +**/ +UINT32 +EFIAPI +FdtGetInterruptId ( + UINT32 CONST * Data + ); + +/** Get the ACPI interrupt flags of an interrupt described in a fdt. + + Data must describe a GIC interrupt. A GIC interrupt is on at least + 3 UINT32 cells. + + @param [in] Data Pointer to the first cell of an "interrupts" property. + + @retval The interrupt flags (for ACPI). +**/ +UINT32 +EFIAPI +FdtGetInterruptFlags ( + UINT32 CONST * Data + ); + +/** A structure describing a compatibility string. +*/ +typedef struct CompatStr { + CONST CHAR8 CompatStr[COMPATIBLE_STR_LEN]; +} COMPATIBILITY_STR; + +/** Structure containing a list of compatible names and their count. +*/ +typedef struct CompatibilityInfo { + /// Count of entries in the NAME_TABLE. + UINT32 Count; + + /// Pointer to a table storing the names. + CONST COMPATIBILITY_STR * CompatTable; +} COMPATIBILITY_INFO; + +/** Operate a check on a Device Tree node. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] NodeOffset Offset of the node to compare input string. + @param [in] Context Context to operate the check on the node. + + @retval True The check is correct. + @retval FALSE Otherwise, or error. +**/ +typedef +BOOLEAN +(EFIAPI *NODE_CHECKER_FUNC) ( + IN CONST VOID * Fdt, + IN INT32 NodeOffset, + IN CONST VOID * Context + ); + +/** Iterate through the list of strings in the Context, + and check whether at least one string is matching the + "compatible" property of the node. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] Node Offset of the node to operate the check on. + @param [in] CompatInfo COMPATIBILITY_INFO containing the list of compatible + strings to compare with the "compatible" property + of the node. + + @retval TRUE At least one string matched, the node is compatible. + @retval FALSE Otherwise, or error. +**/ +BOOLEAN +EFIAPI +FdtNodeIsCompatible ( + IN CONST VOID * Fdt, + IN INT32 Node, + IN CONST VOID * CompatInfo + ); + +/** Check whether a node has a property. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] Node Offset of the node to operate the check on. + @param [in] PropertyName Name of the property to search. + This is a NULL terminated string. + + @retval True The node has the property. + @retval FALSE Otherwise, or error. +**/ +BOOLEAN +EFIAPI +FdtNodeHasProperty ( + IN CONST VOID * Fdt, + IN INT32 Node, + IN CONST VOID * PropertyName + ); + +/** Get the next node in a branch having a matching name. + + The Device tree is traversed in a depth-first search, starting from Node. + The input Node is skipped. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this branch. + Write (-1) to search the whole tree. + @param [in] NodeName The node name to search. + This is a NULL terminated string. + @param [in, out] Node At entry: Node offset to start the search. + This first node is skipped. + Write (-1) to search the whole tree. + At exit: If success, contains the offset of + the next node in the branch + having a matching name. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND No matching node found. +**/ +EFI_STATUS +EFIAPI +FdtGetNextNamedNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN CONST CHAR8 * NodeName, + IN OUT INT32 * Node + ); + +/** Get the next node in a branch with at least one compatible property. + + The Device tree is traversed in a depth-first search, starting from Node. + The input Node is skipped. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this branch. + Write (-1) to search the whole tree. + @param [in] CompatNamesInfo Table of compatible strings to compare with + the compatible property of the node. + @param [in, out] Node At entry: Node offset to start the search. + This first node is skipped. + Write (-1) to search the whole tree. + At exit: If success, contains the offset of + the next node in the branch + being compatible. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND No matching node found. +**/ +EFI_STATUS +EFIAPI +FdtGetNextCompatNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN CONST COMPATIBILITY_INFO * CompatNamesInfo, + IN OUT INT32 * Node + ); + +/** Get the next node in a branch having the PropName property. + + The Device tree is traversed in a depth-first search, starting from Node. + The input Node is skipped. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this branch. + Write (-1) to search the whole tree. + @param [in] PropName Name of the property to search. + This is a NULL terminated string. + @param [in, out] Node At entry: Node offset to start the search. + This first node is skipped. + Write (-1) to search the whole tree. + At exit: If success, contains the offset of + the next node in the branch + being compatible. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_NOT_FOUND No matching node found. +**/ +EFI_STATUS +EFIAPI +FdtGetNextPropNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN CONST CHAR8 * PropName, + IN OUT INT32 * Node + ); + +/** Count the number of nodes in a branch with the input name. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this branch. + Write (-1) to search the whole tree. + @param [in] NodeName Node name to search. + This is a NULL terminated string. + @param [out] NodeCount If success, contains the count of nodes + fulfilling the condition. + Can be 0. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +FdtCountNamedNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN CONST CHAR8 * NodeName, + OUT UINT32 * NodeCount + ); + +/** Count the number of nodes in a branch with at least + one compatible property. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this branch. + Write (-1) to search the whole tree. + @param [in] CompatibleTable Table of compatible strings to + compare with the compatible property + of the node. + @param [out] NodeCount If success, contains the count of nodes + fulfilling the condition. + Can be 0. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +FdtCountCompatNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN CONST COMPATIBILITY_INFO * CompatNamesInfo, + OUT UINT32 * NodeCount + ); + +/** Count the number of nodes in a branch having the PropName property. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] FdtBranch Only search in the sub-nodes of this branch. + Write (-1) to search the whole tree. + @param [in] PropName Name of the property to search. + This is a NULL terminated string. + @param [out] NodeCount If success, contains the count of nodes + fulfilling the condition. + Can be 0. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +FdtCountPropNodeInBranch ( + IN CONST VOID * Fdt, + IN INT32 FdtBranch, + IN CONST CHAR8 * PropName, + OUT UINT32 * NodeCount + ); + +/** Get the interrupt-controller node handling the interrupts of + the input node. + + To do this, recursively search a node with either the "interrupt-controller" + or the "interrupt-parent" property in the parents of Node. + + Devicetree Specification, Release v0.3, + 2.4.1 "Properties for Interrupt Generating Devices": + Because the hierarchy of the nodes in the interrupt tree + might not match the devicetree, the interrupt-parent + property is available to make the definition of an + interrupt parent explicit. The value is the phandle to the + interrupt parent. If this property is missing from a + device, its interrupt parent is assumed to be its devicetree + parent. + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] Node Offset of the node to start the search. + @param [out] IntcNode If success, contains the offset of the + interrupt-controller node. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND No interrupt-controller node found. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +FdtGetIntcParentNode ( + IN CONST VOID * Fdt, + IN INT32 Node, + OUT INT32 * IntcNode + ); + +/** Get the "interrupt-cells" property value of the node. + + The "interrupts" property requires to know the number of cells used + to encode an interrupt. This information is stored in the + interrupt-controller of the input Node. + + @param [in] Fdt Pointer to a Flattened Device Tree (Fdt). + @param [in] IntcNode Offset of an interrupt-controller node. + @param [out] IntCells If success, contains the "interrupt-cells" + property of the IntcNode. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_UNSUPPORTED Unsupported. +**/ +EFI_STATUS +EFIAPI +FdtGetInterruptCellsInfo ( + IN CONST VOID * Fdt, + IN INT32 IntcNode, + OUT INT32 * InterruptCells + ); + +/** Get the "#address-cells" and/or "#size-cells" property of the node. + + According to the Device Tree specification, s2.3.5 "#address-cells and + #size-cells": + "If missing, a client program should assume a default value of 2 for + #address-cells, and a value of 1 for #size-cells." + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] Node Offset of the node having to get the + "#address-cells" and "#size-cells" + properties from. + @param [out] AddressCells If success, number of address-cells. + If the property is not available, + default value is 2. + @param [out] SizeCells If success, number of size-cells. + If the property is not available, + default value is 1. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +FdtGetAddressInfo ( + IN CONST VOID * Fdt, + IN INT32 Node, + OUT INT32 * AddressCells, OPTIONAL + OUT INT32 * SizeCells OPTIONAL + ); + +/** Get the "#address-cells" and/or "#size-cells" property of the parent node. + + According to the Device Tree specification, s2.3.5 "#address-cells and + #size-cells": + "If missing, a client program should assume a default value of 2 for + #address-cells, and a value of 1 for #size-cells." + + @param [in] Fdt Pointer to a Flattened Device Tree. + @param [in] Node Offset of the node having to get the + "#address-cells" and "#size-cells" + properties from its parent. + @param [out] AddressCells If success, number of address-cells. + If the property is not available, + default value is 2. + @param [out] SizeCells If success, number of size-cells. + If the property is not available, + default value is 1. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ABORTED An error occurred. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EFIAPI +FdtGetParentAddressInfo ( + IN CONST VOID * Fdt, + IN INT32 Node, + OUT INT32 * AddressCells, OPTIONAL + OUT INT32 * SizeCells OPTIONAL + ); + +#endif // FDT_UTILITY_H_ -- 2.17.1