public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [PATCH v1 00/14] Implement a FdtHwInfoParserLib
@ 2021-06-23 12:38 PierreGondois
  2021-06-23 12:38 ` [PATCH v1 01/14] DynamicTablesPkg: Definition for HwInfoParser interface PierreGondois
                   ` (13 more replies)
  0 siblings, 14 replies; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

The generic HwInfoParserLib provides an interface to parse hardware
information stored in certain a data type (e.g.: xml, device tree,
...) and generate objects that can be used by the DynamicTablesPkg
framework to generate another hardware description (e.g.:
SMBIOS tables, ACPI tables, ...).

This patch-set also implements a FdtHwInfoParserLib, parsing
hardware information stored in a device tree. The objects
generated by the library have been used ACPI tables.

The changes can be seen at: https://github.com/PierreARM/edk2/tree/1787_Implement_FdtHwInfoParser
The results of the CI can be seen at: https://github.com/tianocore/edk2/pull/1749

This patch-set is dependent over the following patch-sets:
  [PATCH v1 00/10] Various DynamicTablesPkg modifications 
  https://edk2.groups.io/g/devel/message/76929
and:
  [PATCH v1 00/13] Create a SSDT CPU topology generator 
  https://edk2.groups.io/g/devel/message/76941
and:
  [PATCH v1 0/7] Create a SSDT PCIe generator 
  https://edk2.groups.io/g/devel/message/76958

Pierre Gondois (14):
  DynamicTablesPkg: Definition for HwInfoParser interface
  DynamicTablesPkg: FdtHwInfoParser: CM Object descriptor helper
  DynamicTablesPkg: FdtHwInfoParser: Add FDT utility functions
  DynamicTablesPkg: FdtHwInfoParser: Add Boot Arch parser
  DynamicTablesPkg: FdtHwInfoParser: Generic Timer Parser
  DynamicTablesPkg: FdtHwInfoParser: Add Serial port parser
  DynamicTablesPkg: FdtHwInfoParser: Add GICC parser
  DynamicTablesPkg: FdtHwInfoParser: Add GICD parser
  DynamicTablesPkg: FdtHwInfoParser: Add MSI Frame parser
  DynamicTablesPkg: FdtHwInfoParser: Add ITS parser
  DynamicTablesPkg: FdtHwInfoParser: Add GICR parser
  DynamicTablesPkg: FdtHwInfoParser: Add GIC dispatcher
  DynamicTablesPkg: FdtHwInfoParser: Add PCI config parser
  DynamicTablesPkg: Add FdtHwInfoParser library

 DynamicTablesPkg/DynamicTablesPkg.dec         |   3 +
 DynamicTablesPkg/DynamicTablesPkg.dsc         |   3 +-
 .../Include/Library/HwInfoParserLib.h         |  99 ++
 .../BootArch/ArmBootArchParser.c              | 161 ++++
 .../BootArch/ArmBootArchParser.h              |  45 +
 .../FdtHwInfoParserLib/CmObjectDescUtility.c  | 305 ++++++
 .../FdtHwInfoParserLib/CmObjectDescUtility.h  | 131 +++
 .../FdtHwInfoParserLib/FdtHwInfoParser.c      | 193 ++++
 .../FdtHwInfoParserLib/FdtHwInfoParser.h      |  63 ++
 .../FdtHwInfoParserInclude.h                  |  17 +
 .../FdtHwInfoParserLib/FdtHwInfoParserLib.inf |  56 ++
 .../Library/FdtHwInfoParserLib/FdtUtility.c   | 909 ++++++++++++++++++
 .../Library/FdtHwInfoParserLib/FdtUtility.h   | 458 +++++++++
 .../GenericTimer/ArmGenericTimerParser.c      | 254 +++++
 .../GenericTimer/ArmGenericTimerParser.h      |  66 ++
 .../FdtHwInfoParserLib/Gic/ArmGicCParser.c    | 762 +++++++++++++++
 .../FdtHwInfoParserLib/Gic/ArmGicCParser.h    |  67 ++
 .../FdtHwInfoParserLib/Gic/ArmGicDParser.c    | 171 ++++
 .../FdtHwInfoParserLib/Gic/ArmGicDParser.h    |  50 +
 .../FdtHwInfoParserLib/Gic/ArmGicDispatcher.c | 212 ++++
 .../FdtHwInfoParserLib/Gic/ArmGicDispatcher.h |  72 ++
 .../FdtHwInfoParserLib/Gic/ArmGicItsParser.c  | 215 +++++
 .../FdtHwInfoParserLib/Gic/ArmGicItsParser.h  |  48 +
 .../Gic/ArmGicMsiFrameParser.c                | 214 +++++
 .../Gic/ArmGicMsiFrameParser.h                |  50 +
 .../FdtHwInfoParserLib/Gic/ArmGicRParser.c    | 237 +++++
 .../FdtHwInfoParserLib/Gic/ArmGicRParser.h    |  47 +
 .../Pci/ArmPciConfigSpaceParser.c             | 799 +++++++++++++++
 .../Pci/ArmPciConfigSpaceParser.h             | 142 +++
 .../Serial/ArmSerialPortParser.c              | 621 ++++++++++++
 .../Serial/ArmSerialPortParser.h              |  47 +
 31 files changed, 6516 insertions(+), 1 deletion(-)
 create mode 100644 DynamicTablesPkg/Include/Library/HwInfoParserLib.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/BootArch/ArmBootArchParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/BootArch/ArmBootArchParser.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParser.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserInclude.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserLib.inf
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtUtility.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtUtility.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDParser.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDispatcher.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDispatcher.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicItsParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicItsParser.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicMsiFrameParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicMsiFrameParser.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicRParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicRParser.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h

-- 
2.17.1


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v1 01/14] DynamicTablesPkg: Definition for HwInfoParser interface
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  2021-06-23 12:38 ` [PATCH v1 02/14] DynamicTablesPkg: FdtHwInfoParser: CM Object descriptor helper PierreGondois
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

Hardware information parser is an optional module defined
by the Dynamic Tables Framework. It can either parse an
XML, a Device Tree or a Json file containing the platform
hardware information to populate the platform information
repository.

The Configuration Manager can then utilise this information
to generate ACPI tables for the platform.

Therefore, define an interface for the HwInfoParser library
class.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/DynamicTablesPkg.dec         |  3 +
 .../Include/Library/HwInfoParserLib.h         | 99 +++++++++++++++++++
 2 files changed, 102 insertions(+)
 create mode 100644 DynamicTablesPkg/Include/Library/HwInfoParserLib.h

diff --git a/DynamicTablesPkg/DynamicTablesPkg.dec b/DynamicTablesPkg/DynamicTablesPkg.dec
index 9996bdf6f520..80a61dd2dbac 100644
--- a/DynamicTablesPkg/DynamicTablesPkg.dec
+++ b/DynamicTablesPkg/DynamicTablesPkg.dec
@@ -24,6 +24,9 @@ [LibraryClasses]
   ##  @libraryclass  Defines a set of APIs for Dynamic AML generation.
   AmlLib|Include/Library/AmlLib/AmlLib.h
 
+  ##  @libraryclass  Defines a set of APIs to a hardware information parser.
+  HwInfoParserLib|Include/Library/HwInfoParserLib.h
+
   ##  @libraryclass  Defines a set of methods for fixing up a SSDT Serial Port.
   SsdtSerialPortFixupLib|Include/Library/SsdtSerialPortFixupLib.h
 
diff --git a/DynamicTablesPkg/Include/Library/HwInfoParserLib.h b/DynamicTablesPkg/Include/Library/HwInfoParserLib.h
new file mode 100644
index 000000000000..472fbefcc6b5
--- /dev/null
+++ b/DynamicTablesPkg/Include/Library/HwInfoParserLib.h
@@ -0,0 +1,99 @@
+/** @file
+  Hardware information parser library.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef HW_INFO_PARSER_LIB_H_
+#define HW_INFO_PARSER_LIB_H_
+
+#include <ConfigurationManagerObject.h>
+
+/** A handle to the HwInfoParser instance.
+*/
+typedef VOID * HW_INFO_PARSER_HANDLE;
+
+/** Function pointer called by the parser to add information.
+
+  Callback function that the parser can use to add new
+  CmObj. This function must copy the CmObj data and not rely on
+  the parser preserving the CmObj memory.
+  This function is responsible of the Token allocation.
+
+  @param  [in]  ParserHandle  A handle to the parser instance.
+  @param  [in]  Context       A pointer to the caller's context provided in
+                              HwInfoParserInit ().
+  @param  [in]  CmObjDesc     CM_OBJ_DESCRIPTOR containing the CmObj(s) to add.
+  @param  [out] Token         If provided and success, contain the token
+                              generated for the CmObj.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * HW_INFO_ADD_OBJECT) (
+  IN        HW_INFO_PARSER_HANDLE   ParserHandle,
+  IN        VOID                  * Context,
+  IN  CONST CM_OBJ_DESCRIPTOR     * CmObjDesc,
+  OUT       CM_OBJECT_TOKEN       * Token OPTIONAL
+  );
+
+/** Initialise the HwInfoParser.
+
+  The HwInfoParser shall use the information provided by the HwDataSource
+  to initialise the internal state of the parser or to index the data. This
+  internal state shall be linked to the ParserHandle using an implementation
+  defined mechanism.
+
+  @param [in]   HwDataSource    Pointer to the blob containing the hardware
+                                information. It can be a pointer to a Device
+                                Tree, an XML file, etc. or any other data
+                                structure defined by the HwInfoParser.
+  @param [in]   Context         A pointer to the caller's context.
+  @param [in]   HwInfoAdd       Function pointer called by the parser when
+                                adding information.
+  @param [out]  ParserHandle    A handle to the parser instance.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+HwInfoParserInit (
+  IN    VOID                  * HwDataSource,
+  IN    VOID                  * Context,
+  IN    HW_INFO_ADD_OBJECT      HwInfoAdd,
+  OUT   HW_INFO_PARSER_HANDLE * ParserHandle
+  );
+
+/** Parse the data provided by the HwDataSource.
+
+  @param [in]  ParserHandle    A handle to the parser instance.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
+**/
+EFI_STATUS
+EFIAPI
+HwInfoParse (
+  IN  HW_INFO_PARSER_HANDLE  ParserHandle
+  );
+
+/** Cleanup any internal state and resources that were allocated
+    by the the HwInfoParser.
+
+  @param [in]  ParserHandle    A handle to the parser instance.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+HwInfoParserShutdown (
+  IN  HW_INFO_PARSER_HANDLE   ParserHandle
+  );
+
+#endif // HW_INFO_PARSER_LIB_H_
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v1 02/14] DynamicTablesPkg: FdtHwInfoParser: CM Object descriptor helper
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
  2021-06-23 12:38 ` [PATCH v1 01/14] DynamicTablesPkg: Definition for HwInfoParser interface PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  2021-11-05 14:27   ` Sami Mujawar
  2021-06-23 12:38 ` [PATCH v1 03/14] DynamicTablesPkg: FdtHwInfoParser: Add FDT utility functions PierreGondois
                   ` (11 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

FdtHwInfoParserLib is an instance of the HwInfoParser. The
FdtHwInfoParser parses a platform Device Tree and populates
the Platform Information repository with Configuration
Manager objects that describe the platform hardware.
These Configuration Manager objects are encapsulated in
Configuration Manager Object Descriptors.

Therefore, add helper functions to create and free the
Configuration Manager Object descriptors.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 .../FdtHwInfoParserLib/CmObjectDescUtility.c  | 305 ++++++++++++++++++
 .../FdtHwInfoParserLib/CmObjectDescUtility.h  | 131 ++++++++
 2 files changed, 436 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.c
new file mode 100644
index 000000000000..e471217504fe
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.c
@@ -0,0 +1,305 @@
+/** @file
+  Configuration manager Object Descriptor Utility.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <FdtHwInfoParserInclude.h>
+#include <ConfigurationManagerObject.h>
+
+#include "CmObjectDescUtility.h"
+
+/** Create a CM_OBJ_DESCRIPTOR.
+
+  @param [in]  ObjectId       CM_OBJECT_ID of the node.
+  @param [in]  Count          Number of CmObj stored in the
+                              data field.
+  @param [in]  Data           Pointer to one or more CmObj objects.
+                              The content of this Data buffer is copied.
+  @param [in]  Size           Size of the Data buffer.
+  @param [out] NewCmObjDesc   The created CM_OBJ_DESCRIPTOR.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
+**/
+EFI_STATUS
+EFIAPI
+CreateCmObjDesc (
+  IN  CM_OBJECT_ID          ObjectId,
+  IN  UINT32                Count,
+  IN  VOID                * Data,
+  IN  UINT32                Size,
+  OUT CM_OBJ_DESCRIPTOR  ** NewCmObjDesc
+  )
+{
+  CM_OBJ_DESCRIPTOR   * CmObjDesc;
+  VOID                * DataBuffer;
+
+  if ((Count == 0)      ||
+      (Data == NULL)    ||
+      (Size == 0)       ||
+      (NewCmObjDesc == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CmObjDesc = AllocateZeroPool (sizeof (CM_OBJ_DESCRIPTOR));
+  if (CmObjDesc == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  DataBuffer = AllocateCopyPool (Size, Data);
+  if (DataBuffer == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  CmObjDesc->ObjectId = ObjectId;
+  CmObjDesc->Count = Count;
+  CmObjDesc->Data = DataBuffer;
+  CmObjDesc->Size = Size;
+
+  *NewCmObjDesc = CmObjDesc;
+
+  return EFI_SUCCESS;
+}
+
+/** Free resources allocated for the CM_OBJ_DESCRIPTOR.
+
+  @param [in] CmObjDesc           Pointer to the CM_OBJ_DESCRIPTOR.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FreeCmObjDesc (
+  IN CM_OBJ_DESCRIPTOR  * CmObjDesc
+  )
+{
+  if (CmObjDesc == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (CmObjDesc->Data != NULL) {
+    FreePool (CmObjDesc->Data);
+  }
+
+  FreePool (CmObjDesc);
+  return EFI_SUCCESS;
+}
+
+/** Add a single CmObj to the Configuration Manager.
+
+  @param  [in]  FdtParserHandle   A handle to the parser instance.
+  @param  [in]  ObjectId          CmObj ObjectId.
+  @param  [in]  Data              CmObj Data.
+  @param  [in]  Size              CmObj Size.
+  @param  [out] Token             If provided and success,
+                                  token generated for this CmObj.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AddSingleCmObj (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE   FdtParserHandle,
+  IN        CM_OBJECT_ID                ObjectId,
+  IN        VOID                        *Data,
+  IN        UINT32                      Size,
+  OUT       CM_OBJECT_TOKEN             *Token    OPTIONAL
+  )
+{
+  EFI_STATUS          Status;
+  CM_OBJ_DESCRIPTOR   CmObjDesc;
+
+  if ((FdtParserHandle == NULL)             ||
+      (FdtParserHandle->HwInfoAdd == NULL)  ||
+      (Data == NULL)                        ||
+      (Size == 0)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CmObjDesc.ObjectId = ObjectId;
+  CmObjDesc.Count = 1;
+  CmObjDesc.Data = Data;
+  CmObjDesc.Size = Size;
+
+  // Add the CmObj.
+  // Don't ask for a token.
+  Status = FdtParserHandle->HwInfoAdd (
+                              FdtParserHandle,
+                              FdtParserHandle->Context,
+                              &CmObjDesc,
+                              Token
+                              );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+  return Status;
+}
+
+/** Add multiple CmObj to the Configuration Manager.
+
+  @param  [in]  FdtParserHandle   A handle to the parser instance.
+  @param  [in]  CmObjDesc         CmObjDesc containing multiple CmObj
+                                  to add.
+  @param  [in]  TokenCount        If provided, count of entries in the
+                                  TokenTable.
+  @param  [out] TokenTable        If provided and success,
+                                  token generated for these CmObj.
+                                  Address of an array of CM_OBJECT_TOKEN
+                                  with the same number of elements as the
+                                  CmObjDesc.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AddMultipleCmObj (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE   FdtParserHandle,
+  IN  CONST CM_OBJ_DESCRIPTOR           *CmObjDesc,
+  IN        UINT32                      TokenCount,   OPTIONAL
+  OUT       CM_OBJECT_TOKEN             *TokenTable   OPTIONAL
+  )
+{
+  EFI_STATUS          Status;
+  UINT32              Index;
+  UINT32              Count;
+  VOID              * Data;
+  UINT32              Size;
+  CM_OBJ_DESCRIPTOR   SingleCmObjDesc;
+
+  if ((FdtParserHandle == NULL)             ||
+      (FdtParserHandle->HwInfoAdd == NULL)  ||
+      (CmObjDesc == NULL)                   ||
+      (CmObjDesc->Count == 0)               ||
+      (CmObjDesc->Data == NULL)             ||
+      (CmObjDesc->Size == 0)){
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Count = CmObjDesc->Count;
+  Data = CmObjDesc->Data;
+  Size = CmObjDesc->Size / Count;
+
+  SingleCmObjDesc.ObjectId = CmObjDesc->ObjectId;
+  SingleCmObjDesc.Count = 1;
+  SingleCmObjDesc.Size = Size;
+
+  for (Index = 0; Index < Count; Index++) {
+    SingleCmObjDesc.Data = Data + Index * Size;
+    // Add the CmObj.
+    Status = FdtParserHandle->HwInfoAdd (
+                                FdtParserHandle,
+                                FdtParserHandle->Context,
+                                &SingleCmObjDesc,
+                                (TokenTable != NULL) ?
+                                  &TokenTable[Index] :
+                                  NULL
+                                );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  } // for
+
+  return Status;
+}
+
+/** Add multiple CmObj to the Configuration Manager.
+
+  Get one token referencing a EArmObjCmRef CmObj itself referencing
+  the input CmObj. In the table below, RefToken is returned.
+
+  Token referencing an      Array of tokens             Array of CmObj
+  array of EArmObjCmRef     referencing each            from the input:
+  CmObj:                    CmObj from the input:
+
+  RefToken         --->     CmObjToken[0]        --->   CmObj[0]
+                            CmObjToken[1]        --->   CmObj[1]
+                            CmObjToken[2]        --->   CmObj[2]
+
+  @param  [in]  FdtParserHandle   A handle to the parser instance.
+  @param  [in]  CmObjDesc         CmObjDesc containing multiple CmObj
+                                  to add.
+  @param  [out] Token             If success, token referencing an array
+                                  of EArmObjCmRef CmObj, themselves
+                                  referencing the input CmObjs.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
+**/
+EFI_STATUS
+EFIAPI
+AddMultipleCmObjWithCmObjRef (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE     FdtParserHandle,
+  IN  CM_OBJ_DESCRIPTOR                 * CmObjDesc,
+  OUT CM_OBJECT_TOKEN                   * Token
+  )
+{
+  EFI_STATUS          Status;
+  CM_OBJ_DESCRIPTOR   CmObjRef;
+  CM_OBJECT_TOKEN    *TokenTable;
+  INT32               TokenTableSize;
+
+  if ((FdtParserHandle == NULL)             ||
+      (FdtParserHandle->HwInfoAdd == NULL)  ||
+      (CmObjDesc == NULL)                   ||
+      (CmObjDesc->Count == 0)               ||
+      (CmObjDesc->Data == NULL)             ||
+      (CmObjDesc->Size == 0)                ||
+      (Token == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Allocate a buffer to store the tokens.
+  TokenTableSize = CmObjDesc->Count * sizeof (CM_OBJECT_TOKEN);
+  TokenTable = AllocateZeroPool (TokenTableSize);
+  if (TokenTable == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  // Add the input CmObjs.
+  Status = AddMultipleCmObj (
+             FdtParserHandle,
+             CmObjDesc,
+             CmObjDesc->Count,
+             TokenTable
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  CmObjRef.ObjectId = CREATE_CM_ARM_OBJECT_ID (EArmObjCmRef);
+  CmObjRef.Data = TokenTable;
+  CmObjRef.Count = CmObjDesc->Count;
+  CmObjRef.Size = TokenTableSize;
+
+  // Add the array of EArmObjCmRef CmObjs.
+  Status = FdtParserHandle->HwInfoAdd (
+                              FdtParserHandle,
+                              FdtParserHandle->Context,
+                              &CmObjRef,
+                              Token
+                              );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+
+exit_handler:
+  FreePool (TokenTable);
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.h
new file mode 100644
index 000000000000..34439c716fb3
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.h
@@ -0,0 +1,131 @@
+/** @file
+  Configuration manager Object Descriptor Utility.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef CM_OBJECT_DESC_UTILITY_H_
+#define CM_OBJECT_DESC_UTILITY_H_
+
+#include <ConfigurationManagerObject.h>
+
+#include "FdtHwInfoParser.h"
+
+/** Create a CM_OBJ_DESCRIPTOR.
+
+  @param [in]  ObjectId       CM_OBJECT_ID of the node.
+  @param [in]  Count          Number of CmObj stored in the
+                              data field.
+  @param [in]  Data           Pointer to one or more CmObj objects.
+                              The content of this Data buffer is copied.
+  @param [in]  Size           Size of the Data buffer.
+  @param [out] NewCmObjDesc   The created CM_OBJ_DESCRIPTOR.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
+**/
+EFI_STATUS
+EFIAPI
+CreateCmObjDesc (
+  IN  CM_OBJECT_ID          ObjectId,
+  IN  UINT32                Count,
+  IN  VOID                * Data,
+  IN  UINT32                Size,
+  OUT CM_OBJ_DESCRIPTOR  ** NewCmObjDesc
+  );
+
+/** Free resources allocated for the CM_OBJ_DESCRIPTOR.
+
+  @param [in] CmObjDesc           Pointer to the CM_OBJ_DESCRIPTOR.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FreeCmObjDesc (
+  IN CM_OBJ_DESCRIPTOR  * CmObjDesc
+  );
+
+/** Add a single CmObj to the Configuration Manager.
+
+  @param  [in]  FdtParserHandle   A handle to the parser instance.
+  @param  [in]  ObjectId          CmObj ObjectId.
+  @param  [in]  Data              CmObj Data.
+  @param  [in]  Size              CmObj Size.
+  @param  [out] Token             If provided and success,
+                                  token generated for this CmObj.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AddSingleCmObj (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE   FdtParserHandle,
+  IN        CM_OBJECT_ID                ObjectId,
+  IN        VOID                        *Data,
+  IN        UINT32                      Size,
+  OUT       CM_OBJECT_TOKEN             *Token    OPTIONAL
+  );
+
+/** Add multiple CmObj to the Configuration Manager.
+
+  @param  [in]  FdtParserHandle   A handle to the parser instance.
+  @param  [in]  CmObjDesc         CmObjDesc containing multiple CmObj
+                                  to add.
+  @param  [in]  TokenCount        If provided, count of entries in the
+                                  TokenTable.
+  @param  [out] TokenTable        If provided and success,
+                                  token generated for these CmObj.
+                                  Address of an array of CM_OBJECT_TOKEN
+                                  with the same number of elements as the
+                                  CmObjDesc.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AddMultipleCmObj (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE   FdtParserHandle,
+  IN  CONST CM_OBJ_DESCRIPTOR           *CmObjDesc,
+  IN        UINT32                      TokenCount,   OPTIONAL
+  OUT       CM_OBJECT_TOKEN             *TokenTable   OPTIONAL
+  );
+
+/** Add multiple CmObj to the Configuration Manager.
+
+  Get one token referencing a EArmObjCmRef CmObj itself referencing
+  the input CmObj. In the table below, RefToken is returned.
+
+  Token referencing an      Array of tokens             Array of CmObj
+  array of EArmObjCmRef     referencing each            from the input:
+  CmObj:                    CmObj from the input:
+
+  RefToken         --->     CmObjToken[0]        --->   CmObj[0]
+                            CmObjToken[1]        --->   CmObj[1]
+                            CmObjToken[2]        --->   CmObj[2]
+
+  @param  [in]  FdtParserHandle   A handle to the parser instance.
+  @param  [in]  CmObjDesc         CmObjDesc containing multiple CmObj
+                                  to add.
+  @param  [out] Token             If success, token referencing an array
+                                  of EArmObjCmRef CmObj, themselves
+                                  referencing the input CmObjs.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
+**/
+EFI_STATUS
+EFIAPI
+AddMultipleCmObjWithCmObjRef (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE     FdtParserHandle,
+  IN  CM_OBJ_DESCRIPTOR                 * CmObjDesc,
+  OUT CM_OBJECT_TOKEN                   * Token
+  );
+
+#endif // CM_OBJECT_DESC_UTILITY_H_
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v1 03/14] DynamicTablesPkg: FdtHwInfoParser: Add FDT utility functions
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
  2021-06-23 12:38 ` [PATCH v1 01/14] DynamicTablesPkg: Definition for HwInfoParser interface PierreGondois
  2021-06-23 12:38 ` [PATCH v1 02/14] DynamicTablesPkg: FdtHwInfoParser: CM Object descriptor helper PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  2021-11-05 14:27   ` Sami Mujawar
  2021-06-23 12:38 ` [PATCH v1 04/14] DynamicTablesPkg: FdtHwInfoParser: Add Boot Arch parser PierreGondois
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

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 <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 .../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.<BR>
+  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 <FdtHwInfoParserInclude.h>
+#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<addr>.
+  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.<BR>
+  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


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v1 04/14] DynamicTablesPkg: FdtHwInfoParser: Add Boot Arch parser
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
                   ` (2 preceding siblings ...)
  2021-06-23 12:38 ` [PATCH v1 03/14] DynamicTablesPkg: FdtHwInfoParser: Add FDT utility functions PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  2021-06-23 12:38 ` [PATCH v1 05/14] DynamicTablesPkg: FdtHwInfoParser: Generic Timer Parser PierreGondois
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

The Fixed ACPI Description Table (FADT) is a mandatory table
required for booting a standards-based operating system. The
FADT table has an 'ARM Boot Architecture Flags' field that is
used by an OS at boot time to determine the code path during
boot. This field is used to specify if the platform complies
with the PSCI specification. It is also used to describe the
conduit (SMC/HVC) to be used for PSCI.

The PSCI compliance information for a platform is described
in the platform Device Tree, the bindings for which can be
found at:
 - linux/Documentation/devicetree/bindings/arm/psci.yaml

The FdtHwInfoParser implements a Boot Arch Parser that parses
the platform Device Tree to create a CM_ARM_BOOT_ARCH_INFO
object. The CM_ARM_BOOT_ARCH_INFO object is encapsulated in
a Configuration Manager descriptor object and added to the
platform information repository.

The platform Configuration Manager can then utilise this
information when generating the FADT table.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 .../BootArch/ArmBootArchParser.c              | 161 ++++++++++++++++++
 .../BootArch/ArmBootArchParser.h              |  45 +++++
 2 files changed, 206 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/BootArch/ArmBootArchParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/BootArch/ArmBootArchParser.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/BootArch/ArmBootArchParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/BootArch/ArmBootArchParser.c
new file mode 100644
index 000000000000..b18970f115f2
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/BootArch/ArmBootArchParser.c
@@ -0,0 +1,161 @@
+/** @file
+  Arm boot architecture parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/arm/psci.yaml
+**/
+
+#include "FdtHwInfoParser.h"
+#include "CmObjectDescUtility.h"
+#include "BootArch/ArmBootArchParser.h"
+
+/** List of "compatible" property values for Psci nodes.
+
+  Other "compatible" values are not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR PsciCompatibleStr[] = {
+  {"arm,psci-0.2"},
+  {"arm,psci"}
+};
+
+/** COMPATIBILITY_INFO structure for the PsciCompatibleInfo.
+*/
+STATIC CONST COMPATIBILITY_INFO PsciCompatibleInfo = {
+  ARRAY_SIZE (PsciCompatibleStr),
+  PsciCompatibleStr
+};
+
+/** List of PSCI method strings.
+*/
+STATIC CONST CHAR8  *PsciMethod[] = {
+  "smc",
+  "hvc"
+};
+
+/** Parse a Psci node.
+
+  @param [in]  Fdt            Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  PsciNode       Offset of a Psci node.
+  @param [in]  BootArchInfo   The CM_ARM_BOOT_ARCH_INFO to populate.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+PsciNodeParser (
+  IN  CONST VOID                    * Fdt,
+  IN        INT32                     PsciNode,
+  IN        CM_ARM_BOOT_ARCH_INFO   * BootArchInfo
+  )
+{
+  CONST VOID    * Data;
+  INT32           DataSize;
+
+  if ((Fdt == NULL) ||
+      (BootArchInfo == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Default to parking protocol
+  BootArchInfo->BootArchFlags = 0;
+
+  Data = fdt_getprop (Fdt, PsciNode, "method", &DataSize);
+  if ((Data == NULL) || (DataSize < 0)) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  // Check PSCI conduit.
+  if (AsciiStrnCmp (Data, PsciMethod[0], DataSize) == 0) {
+    BootArchInfo->BootArchFlags = EFI_ACPI_6_3_ARM_PSCI_COMPLIANT;
+
+  } else if (AsciiStrnCmp (Data, PsciMethod[1], DataSize) == 0) {
+    BootArchInfo->BootArchFlags = (EFI_ACPI_6_3_ARM_PSCI_COMPLIANT |
+                                   EFI_ACPI_6_3_ARM_PSCI_USE_HVC);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** CM_ARM_BOOT_ARCH_INFO parser function.
+
+  The following structure is populated:
+  typedef struct CmArmBootArchInfo {
+    UINT16  BootArchFlags;                    // {Populated}
+  } CM_ARM_BOOT_ARCH_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmBootArchInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  )
+{
+  EFI_STATUS               Status;
+  INT32                    PsciNode;
+  CM_ARM_BOOT_ARCH_INFO    BootArchInfo;
+
+  if (FdtParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ZeroMem (&BootArchInfo, sizeof (CM_ARM_BOOT_ARCH_INFO));
+
+  PsciNode = FdtBranch;
+  Status = FdtGetNextCompatNodeInBranch (
+             FdtParserHandle->Fdt,
+             FdtBranch,
+             &PsciCompatibleInfo,
+             &PsciNode
+             );
+  if (EFI_ERROR (Status)) {
+    // Error, or no node found.
+    ASSERT (Status == EFI_NOT_FOUND);
+    return Status;
+  }
+
+  // Parse the psci node.
+  Status = PsciNodeParser (FdtParserHandle->Fdt, PsciNode, &BootArchInfo);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Add the CmObj to the Configuration Manager.
+  Status = AddSingleCmObj (
+             FdtParserHandle,
+             CREATE_CM_ARM_OBJECT_ID (EArmObjBootArchInfo),
+             &BootArchInfo,
+             sizeof (CM_ARM_BOOT_ARCH_INFO),
+             NULL
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/BootArch/ArmBootArchParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/BootArch/ArmBootArchParser.h
new file mode 100644
index 000000000000..959c65a53fb3
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/BootArch/ArmBootArchParser.h
@@ -0,0 +1,45 @@
+/** @file
+  Arm boot architecture parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/arm/psci.yaml
+**/
+
+#ifndef ARM_BOOT_ARCH_PARSER_H_
+#define ARM_BOOT_ARCH_PARSER_H_
+
+/** CM_ARM_BOOT_ARCH_INFO parser function.
+
+  The following structure is populated:
+  typedef struct CmArmBootArchInfo {
+    UINT16  BootArchFlags;                    // {Populated}
+  } CM_ARM_BOOT_ARCH_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmBootArchInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  );
+
+#endif // ARM_BOOT_ARCH_PARSER_H_
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v1 05/14] DynamicTablesPkg: FdtHwInfoParser: Generic Timer Parser
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
                   ` (3 preceding siblings ...)
  2021-06-23 12:38 ` [PATCH v1 04/14] DynamicTablesPkg: FdtHwInfoParser: Add Boot Arch parser PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  2021-06-23 12:38 ` [PATCH v1 06/14] DynamicTablesPkg: FdtHwInfoParser: Add Serial port parser PierreGondois
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

The Generic Timer Description Table (GTDT) is a mandatory table
required for booting a standards-based operating system. It
provides an OSPM with information about a system’s Generic Timer
configuration. The Generic Timer (GT) is a standard timer interface
implemented on ARM processor-based systems. The GTDT provides OSPM
with information about a system’s GT interrupt configurations, for
both per-processor timers, and platform (memory-mapped) timers.

The Generic Timer information is described in the platform Device
Tree. The Device Tree bindings for the Generic timers can be found
at:
 - linux/Documentation/devicetree/bindings/timer/arm,arch_timer.yaml

The FdtHwInfoParser implements a Generic Timer Parser that parses
the platform Device Tree to create a CM_ARM_GENERIC_TIMER_INFO
object. The CM_ARM_GENERIC_TIMER_INFO object is encapsulated in a
Configuration Manager descriptor object and added to the platform
information repository.

The platform Configuration Manager can then utilise this information
when generating the GTDT table.

Note: The Generic Timer Parser currently does not support parsing
of memory-mapped platform timers.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 .../GenericTimer/ArmGenericTimerParser.c      | 254 ++++++++++++++++++
 .../GenericTimer/ArmGenericTimerParser.h      |  66 +++++
 2 files changed, 320 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.c
new file mode 100644
index 000000000000..e7095396a5a8
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.c
@@ -0,0 +1,254 @@
+/** @file
+  Arm generic timer parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/timer/arm,arch_timer.yaml
+**/
+
+#include "FdtHwInfoParser.h"
+#include "CmObjectDescUtility.h"
+#include "GenericTimer/ArmGenericTimerParser.h"
+#include "Gic/ArmGicDispatcher.h"
+
+/** List of "compatible" property values for timer nodes.
+
+  Other "compatible" values are not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR TimerCompatibleStr[] = {
+  {"arm,armv7-timer"},
+  {"arm,armv8-timer"}
+};
+
+/** Timer compatiblity information.
+*/
+STATIC CONST COMPATIBILITY_INFO TimerCompatibleInfo = {
+  ARRAY_SIZE (TimerCompatibleStr),
+  TimerCompatibleStr
+};
+
+/** Parse a timer node.
+
+  @param [in]  Fdt                Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  TimerNode          Offset of a timer node.
+  @param [in]  GenericTimerInfo   The CM_ARM_BOOT_ARCH_INFO to populate.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+TimerNodeParser (
+  IN  CONST VOID                      * Fdt,
+  IN        INT32                       TimerNode,
+  IN        CM_ARM_GENERIC_TIMER_INFO * GenericTimerInfo
+  )
+{
+  EFI_STATUS      Status;
+  CONST UINT32  * Data;
+  INT32           IntcNode;
+  UINT32          GicVersion;
+  INT32           DataSize;
+  INT32           IntCells;
+  BOOLEAN         AlwaysOnTimer;
+
+  if ((Fdt == NULL) ||
+      (GenericTimerInfo == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Data = fdt_getprop (Fdt, TimerNode, "always-on", &DataSize);
+  if ((Data == NULL) || (DataSize < 0)) {
+    AlwaysOnTimer = FALSE;
+  } else {
+    AlwaysOnTimer = TRUE;
+  }
+
+  // Get the associated interrupt-controller.
+  Status = FdtGetIntcParentNode (Fdt, TimerNode, &IntcNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Check that the interrupt-controller node is a Gic.
+  Status = GetGicVersion (Fdt, IntcNode, &GicVersion);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Get the number of cells used to encode an interrupt.
+  Status = FdtGetInterruptCellsInfo (Fdt, IntcNode, &IntCells);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    if (Status == EFI_NOT_FOUND) {
+      // Should have found the node.
+      Status = EFI_ABORTED;
+    }
+    return Status;
+  }
+
+  Data = fdt_getprop (Fdt, TimerNode, "interrupts", &DataSize);
+  if ((Data == NULL) ||
+      (DataSize != (FdtMaxTimerItem * IntCells * sizeof (UINT32)))) {
+    // If error or not FdtMaxTimerItem interrupts.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  GenericTimerInfo->SecurePL1TimerGSIV =
+    FdtGetInterruptId (&Data[FdtSecureTimerIrq * IntCells]);
+  GenericTimerInfo->SecurePL1TimerFlags =
+    FdtGetInterruptFlags (&Data[FdtSecureTimerIrq * IntCells]);
+  GenericTimerInfo->NonSecurePL1TimerGSIV =
+    FdtGetInterruptId (&Data[FdtNonSecureTimerIrq * IntCells]);
+  GenericTimerInfo->NonSecurePL1TimerFlags =
+    FdtGetInterruptFlags (&Data[FdtNonSecureTimerIrq * IntCells]);
+  GenericTimerInfo->VirtualTimerGSIV =
+    FdtGetInterruptId (&Data[FdtVirtualTimerIrq * IntCells]);
+  GenericTimerInfo->VirtualTimerFlags =
+    FdtGetInterruptFlags (&Data[FdtVirtualTimerIrq * IntCells]);
+  GenericTimerInfo->NonSecurePL2TimerGSIV =
+    FdtGetInterruptId (&Data[FdtHypervisorTimerIrq * IntCells]);
+  GenericTimerInfo->NonSecurePL2TimerFlags =
+    FdtGetInterruptFlags (&Data[FdtHypervisorTimerIrq * IntCells]);
+
+  if (AlwaysOnTimer) {
+    GenericTimerInfo->SecurePL1TimerFlags |= BIT2;
+    GenericTimerInfo->NonSecurePL1TimerFlags |= BIT2;
+    GenericTimerInfo->VirtualTimerFlags |= BIT2;
+    GenericTimerInfo->NonSecurePL2TimerFlags |= BIT2;
+  }
+
+  // Setup default values
+  // The CntControlBase & CntReadBase Physical Address are optional if
+  // the system implements EL3 (Security Extensions). So, initialise
+  // these to their default value.
+  GenericTimerInfo->CounterControlBaseAddress = 0xFFFFFFFFFFFFFFFF;
+  GenericTimerInfo->CounterReadBaseAddress = 0xFFFFFFFFFFFFFFFF;
+
+  // For systems not implementing ARMv8.1 VHE, this field is 0.
+  GenericTimerInfo->VirtualPL2TimerGSIV = 0;
+  GenericTimerInfo->VirtualPL2TimerFlags = 0;
+
+  return EFI_SUCCESS;
+}
+
+/** CM_ARM_GENERIC_TIMER_INFO parser function.
+
+  The following structure is populated:
+  typedef struct CmArmGenericTimerInfo {
+    UINT64  CounterControlBaseAddress;        // {default}
+    UINT64  CounterReadBaseAddress;           // {default}
+    UINT32  SecurePL1TimerGSIV;               // {Populated}
+    UINT32  SecurePL1TimerFlags;              // {Populated}
+    UINT32  NonSecurePL1TimerGSIV;            // {Populated}
+    UINT32  NonSecurePL1TimerFlags;           // {Populated}
+    UINT32  VirtualTimerGSIV;                 // {Populated}
+    UINT32  VirtualTimerFlags;                // {Populated}
+    UINT32  NonSecurePL2TimerGSIV;            // {Populated}
+    UINT32  NonSecurePL2TimerFlags;           // {Populated}
+    UINT32  VirtualPL2TimerGSIV;              // {default}
+    UINT32  VirtualPL2TimerFlags;             // {default}
+  } CM_ARM_GENERIC_TIMER_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGenericTimerInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  )
+{
+  EFI_STATUS                  Status;
+  UINT32                      Index;
+  INT32                       TimerNode;
+  UINT32                      TimerNodeCount;
+  CM_ARM_GENERIC_TIMER_INFO   GenericTimerInfo;
+  VOID                      * Fdt;
+
+  if (FdtParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Fdt = FdtParserHandle->Fdt;
+  Status = FdtCountCompatNodeInBranch (
+             Fdt,
+             FdtBranch,
+             &TimerCompatibleInfo,
+             &TimerNodeCount
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  if (TimerNodeCount == 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  // Parse each timer node in the branch.
+  TimerNode = FdtBranch;
+  for (Index = 0; Index < TimerNodeCount; Index++) {
+    ZeroMem (&GenericTimerInfo, sizeof (CM_ARM_GENERIC_TIMER_INFO));
+
+    Status = FdtGetNextCompatNodeInBranch (
+               Fdt,
+               FdtBranch,
+               &TimerCompatibleInfo,
+               &TimerNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      if (Status == EFI_NOT_FOUND) {
+        // Should have found the node.
+        Status = EFI_ABORTED;
+      }
+      return Status;
+    }
+
+    Status = TimerNodeParser (Fdt, TimerNode, &GenericTimerInfo);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // Add the CmObj to the Configuration Manager.
+    Status = AddSingleCmObj (
+               FdtParserHandle,
+               CREATE_CM_ARM_OBJECT_ID (EArmObjGenericTimerInfo),
+               &GenericTimerInfo,
+               sizeof (CM_ARM_GENERIC_TIMER_INFO),
+               NULL
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  } // for
+
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.h
new file mode 100644
index 000000000000..e1d294b3eea9
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/GenericTimer/ArmGenericTimerParser.h
@@ -0,0 +1,66 @@
+/** @file
+  Arm generic timer parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/timer/arm,arch_timer.yaml
+**/
+
+#ifndef ARM_GENERIC_TIMER_PARSER_H_
+#define ARM_GENERIC_TIMER_PARSER_H_
+
+/** An enum listing the FDT interrupt items.
+*/
+typedef enum FdtTimerInterruptItems {
+  FdtSecureTimerIrq,      ///< Secure timer IRQ
+  FdtNonSecureTimerIrq,   ///< Non-secure timer IRQ
+  FdtVirtualTimerIrq,     ///< Virtual timer IRQ
+  FdtHypervisorTimerIrq,  ///< Hypervisor timer IRQ
+  FdtMaxTimerItem         ///< Max timer item
+} FDT_TIMER_INTERRUPT_ITEMS;
+
+/** CM_ARM_BOOT_ARCH_INFO parser function.
+
+  The following structure is populated:
+  typedef struct CmArmGenericTimerInfo {
+    UINT64  CounterControlBaseAddress;        // {default}
+    UINT64  CounterReadBaseAddress;           // {default}
+    UINT32  SecurePL1TimerGSIV;               // {Populated}
+    UINT32  SecurePL1TimerFlags;              // {Populated}
+    UINT32  NonSecurePL1TimerGSIV;            // {Populated}
+    UINT32  NonSecurePL1TimerFlags;           // {Populated}
+    UINT32  VirtualTimerGSIV;                 // {Populated}
+    UINT32  VirtualTimerFlags;                // {Populated}
+    UINT32  NonSecurePL2TimerGSIV;            // {Populated}
+    UINT32  NonSecurePL2TimerFlags;           // {Populated}
+    UINT32  VirtualPL2TimerGSIV;              // {default}
+    UINT32  VirtualPL2TimerFlags;             // {default}
+  } CM_ARM_GENERIC_TIMER_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGenericTimerInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  );
+
+#endif // ARM_GENERIC_TIMER_PARSER_H_
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v1 06/14] DynamicTablesPkg: FdtHwInfoParser: Add Serial port parser
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
                   ` (4 preceding siblings ...)
  2021-06-23 12:38 ` [PATCH v1 05/14] DynamicTablesPkg: FdtHwInfoParser: Generic Timer Parser PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  2021-11-05 14:27   ` Sami Mujawar
  2021-06-23 12:38 ` [PATCH v1 07/14] DynamicTablesPkg: FdtHwInfoParser: Add GICC parser PierreGondois
                   ` (7 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

The Microsoft Debug Port Table 2 (DBG2), the Serial Port Console
Redirector (SPCR) table are mandatory tables required for booting
a standards-based operating system. The DBG2 table is used by the
OS debugger while the SPCR table is used to configure the serial
terminal. Additionally, the serial ports available on a platform
for generic use also need to be described in DSDT/SSDT for an OS
to be able to use the serial ports.

The Arm Base System Architecture 1.0 specification a lists of
supported serial port hardware for Arm Platforms. This list
includes the following serial port UARTs:
 - SBSA/Generic UART
 - a fully 16550 compatible UART.
Along, with these the PL011 UART is the most commonly used serial
port hardware on Arm platforms.

The serial port hardware information is described in the platform
Device Tree, the bindings for which can be found at:
 - linux/Documentation/devicetree/bindings/serial/serial.yaml
 - linux/Documentation/devicetree/bindings/serial/8250.txt
 - linux/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt
 - linux/Documentation/devicetree/bindings/serial/pl011.yaml

The FdtHwInfoParser implements a Serial Port Parser that parses
the platform Device Tree to create CM_ARM_SERIAL_PORT_INFO objects
with the following IDs:
 - EArmObjSerialConsolePortInfo (for use by SPCR)
 - EArmObjSerialDebugPortInfo (for use by DBG2)
 - EArmObjSerialPortInfo (for use as generic Serial Ports)

The Serial Port for use by SPCR is selected by parsing the Device
Tree for the '/chosen' node with the 'stdout-path' property. The
next Serial Port is selected for use as the Debug Serial Port and
the remaining serial ports are used as generic serial ports.

The CM_ARM_SERIAL_PORT_INFO objects are encapsulated in Configuration
Manager descriptor objects with the respective IDs and are added to
the platform information repository.

The platform Configuration Manager can then utilise this information
when generating the DBG2, SPCR and the SSDT serial port tables.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 .../Serial/ArmSerialPortParser.c              | 621 ++++++++++++++++++
 .../Serial/ArmSerialPortParser.h              |  47 ++
 2 files changed, 668 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c
new file mode 100644
index 000000000000..d5db206ae93c
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c
@@ -0,0 +1,621 @@
+/** @file
+  Arm Serial Port Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/serial/serial.yaml
+  - linux/Documentation/devicetree/bindings/serial/8250.txt
+  - linux/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt
+  - linux/Documentation/devicetree/bindings/serial/pl011.yaml
+**/
+
+#include <IndustryStandard/DebugPort2Table.h>
+
+#include "CmObjectDescUtility.h"
+#include "FdtHwInfoParser.h"
+#include "Serial/ArmSerialPortParser.h"
+
+/** List of "compatible" property values for serial port nodes.
+
+  Any other "compatible" value is not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR SerialCompatibleStr[] = {
+  {"ns16550a"},
+  {"arm,sbsa-uart"}
+};
+
+/** COMPATIBILITY_INFO structure for the SerialCompatible.
+*/
+CONST COMPATIBILITY_INFO SerialCompatibleInfo = {
+  ARRAY_SIZE (SerialCompatibleStr),
+  SerialCompatibleStr
+};
+
+/** 16550 UART compatible strings.
+
+  Any string of this list must be part of SerialCompatible.
+*/
+STATIC CONST COMPATIBILITY_STR Serial16550CompatibleStr[] = {
+  {"ns16550a"}
+};
+
+/** COMPATIBILITY_INFO structure for the Serial16550Compatible.
+*/
+CONST COMPATIBILITY_INFO Serial16550CompatibleInfo = {
+  ARRAY_SIZE (Serial16550CompatibleStr),
+  Serial16550CompatibleStr
+};
+
+/** SBSA UART compatible strings.
+
+  Any string of this list must be part of SerialCompatible.
+*/
+STATIC CONST COMPATIBILITY_STR SerialSbsaCompatibleStr[] = {
+  {"arm,sbsa-uart"}
+};
+
+/** COMPATIBILITY_INFO structure for the SerialSbsaCompatible.
+*/
+CONST COMPATIBILITY_INFO SerialSbsaCompatibleInfo = {
+  ARRAY_SIZE (SerialSbsaCompatibleStr),
+  SerialSbsaCompatibleStr
+};
+
+/** Parse a serial port node.
+
+  @param [in]  Fdt               Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  SerialPortNode    Offset of a serial-port node.
+  @param [in]  SerialPortInfo    The CM_ARM_SERIAL_PORT_INFO to populate.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+SerialPortNodeParser (
+  IN  CONST VOID               * Fdt,
+  IN  INT32                      SerialPortNode,
+  IN  CM_ARM_SERIAL_PORT_INFO  * SerialPortInfo
+  )
+{
+  EFI_STATUS     Status;
+  INT32          IntcNode;
+  CONST UINT8  * SizeValue;
+
+  INT32          AddressCells;
+  INT32          SizeCells;
+  INT32          IntCells;
+
+  CONST UINT8  * Data;
+  INT32          DataSize;
+  UINT8          AccessSize;
+
+  if ((Fdt == NULL) ||
+      (SerialPortInfo == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = FdtGetParentAddressInfo (
+             Fdt,
+             SerialPortNode,
+             &AddressCells,
+             &SizeCells
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Don't support more than 64 bits and less than 32 bits addresses.
+  if ((AddressCells < 1)  ||
+      (AddressCells > 2)  ||
+      (SizeCells < 1)     ||
+      (SizeCells > 2)) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  Data = fdt_getprop (Fdt, SerialPortNode, "reg", &DataSize);
+  if ((Data == NULL) ||
+      (DataSize < (INT32)(sizeof (UINT32) *
+         GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, SizeCells)) - 1)) {
+    // If error or not enough space.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  if (AddressCells == 2) {
+    SerialPortInfo->BaseAddress = fdt64_to_cpu (*(UINT64*)Data);
+  } else {
+    SerialPortInfo->BaseAddress = fdt32_to_cpu (*(UINT32*)Data);
+  }
+
+  SizeValue = Data + (sizeof (UINT32) *
+    GET_DT_REG_SIZE_OFFSET (0, AddressCells, SizeCells));
+  if (SizeCells == 2) {
+    SerialPortInfo->BaseAddressLength = fdt64_to_cpu (*(UINT64*)SizeValue);
+  } else {
+    SerialPortInfo->BaseAddressLength = fdt32_to_cpu (*(UINT32*)SizeValue);
+  }
+
+  // Get the associated interrupt-controller.
+  Status= FdtGetIntcParentNode (Fdt, SerialPortNode, &IntcNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    if (Status == EFI_NOT_FOUND) {
+      // Should have found the node.
+      Status = EFI_ABORTED;
+    }
+    return Status;
+  }
+
+  // Get the number of cells used to encode an interrupt.
+  Status = FdtGetInterruptCellsInfo (Fdt, IntcNode, &IntCells);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Data = fdt_getprop (Fdt, SerialPortNode, "interrupts", &DataSize);
+  if ((Data == NULL) || (DataSize != (IntCells * sizeof (UINT32)))) {
+    // If error or not 1 interrupt.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  SerialPortInfo->Interrupt = FdtGetInterruptId ((CONST UINT32*)Data);
+
+  // Note: clock-frequency is optional for SBSA UART.
+  Data = fdt_getprop (Fdt, SerialPortNode, "clock-frequency", &DataSize);
+  if (Data != NULL) {
+    if (DataSize < sizeof (UINT32)) {
+      // If error or not enough space.
+      ASSERT (0);
+      return EFI_ABORTED;
+    } else if (fdt_node_offset_by_phandle (Fdt, fdt32_to_cpu (*Data)) >= 0) {
+      // "clock-frequency" can be a "clocks phandle to refer to the clk used".
+      // This is not supported.
+      ASSERT (0);
+      return EFI_UNSUPPORTED;
+    }
+    SerialPortInfo->Clock = fdt32_to_cpu (*(UINT32*)Data);
+  }
+
+  if (FdtNodeIsCompatible (Fdt, SerialPortNode, &Serial16550CompatibleInfo)) {
+    SerialPortInfo->PortSubtype = EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550;
+
+    /* reg-io-width:
+         description: |
+         The size (in bytes) of the IO accesses that should be performed on the
+         device. There are some systems that require 32-bit accesses to the
+         UART.
+    */
+    Data = fdt_getprop (Fdt, SerialPortNode, "reg-io-width", &DataSize);
+    if (Data != NULL) {
+      if (DataSize < sizeof (UINT32)) {
+        // If error or not enough space.
+        ASSERT (0);
+        return EFI_ABORTED;
+      }
+
+      AccessSize = fdt32_to_cpu (*(UINT32*)Data);
+      if (AccessSize > EFI_ACPI_6_3_QWORD) {
+        ASSERT (0);
+        return EFI_INVALID_PARAMETER;
+      }
+      SerialPortInfo->AccessSize = AccessSize;
+    } else {
+      // 8250/16550 defaults to byte access.
+      SerialPortInfo->AccessSize = EFI_ACPI_6_3_BYTE;
+    }
+  } else if (FdtNodeIsCompatible (
+               Fdt,
+               SerialPortNode,
+               &SerialSbsaCompatibleInfo
+               )) {
+    SerialPortInfo->PortSubtype =
+      EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART;
+  } else {
+    ASSERT (0);
+    return EFI_UNSUPPORTED;
+  }
+
+  // Set Baudrate to 115200 by default
+  SerialPortInfo->BaudRate = 115200;
+  return EFI_SUCCESS;
+}
+
+/** Find the console serial-port node in the DT.
+
+  This function fetches the node referenced in the "stdout-path"
+  property of the "chosen" node.
+
+  @param [in]  Fdt                  Pointer to a Flattened Device Tree (Fdt).
+  @param [out] SerialConsoleNode    If success, contains the node offset
+                                    of the console serial-port node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetSerialConsoleNode (
+  IN  CONST VOID    * Fdt,
+  OUT       INT32   * SerialConsoleNode
+  )
+{
+  CONST CHAR8 * Prop;
+  INT32         PropSize;
+  CONST CHAR8 * Path;
+  INT32         PathLen;
+  INT32         ChosenNode;
+
+  if ((Fdt == NULL) ||
+      (SerialConsoleNode == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // The "chosen" node resides at the the root of the DT. Fetch it.
+  ChosenNode = fdt_path_offset (Fdt, "/chosen");
+  if (ChosenNode < 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  Prop = fdt_getprop (Fdt, ChosenNode, "stdout-path", &PropSize);
+  if ((Prop == NULL) || (PropSize < 0)) {
+    return EFI_NOT_FOUND;
+  }
+
+  // Determine the actual path length, as a colon terminates the path.
+  Path = ScanMem8 (Prop, ':', PropSize);
+  if (Path == NULL) {
+    PathLen = (UINT32)AsciiStrLen (Prop);
+  } else {
+    PathLen = (INT32)(Path - Prop);
+  }
+
+  // Aliases cannot start with a '/', so it must be the actual path.
+  if (Prop[0] == '/') {
+    *SerialConsoleNode = fdt_path_offset_namelen (Fdt, Prop, PathLen);
+    return EFI_SUCCESS;
+  }
+
+  // Lookup the alias, as this contains the actual path.
+  Path = fdt_get_alias_namelen (Fdt, Prop, PathLen);
+  if (Path == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  *SerialConsoleNode = fdt_path_offset (Fdt, Path);
+  return EFI_SUCCESS;
+}
+
+/** CM_ARM_SERIAL_PORT_INFO dispatcher function (for a generic serial-port).
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  GenericSerialInfo  Pointer to a serial port info list.
+  @param [in]  NodeCount          Count of serial ports to dispatch.
+  @param [in]  SerialObjectId     Serial port object ID.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ArmSerialPortInfoDispatch (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE   FdtParserHandle,
+  IN  CM_ARM_SERIAL_PORT_INFO         * GenericSerialInfo,
+  IN  INT32                             NodeCount,
+  IN  EARM_OBJECT_ID                    SerialObjectId
+)
+{
+  EFI_STATUS                Status;
+  CM_OBJ_DESCRIPTOR       * NewCmObjDesc;
+
+  if ((GenericSerialInfo == NULL) || (NodeCount == 0)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((SerialObjectId != EArmObjSerialPortInfo) &&
+      (SerialObjectId != EArmObjSerialDebugPortInfo) &&
+      (SerialObjectId != EArmObjSerialConsolePortInfo)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Dispatch the Generic Serial ports
+  Status = CreateCmObjDesc (
+             CREATE_CM_ARM_OBJECT_ID (SerialObjectId),
+             NodeCount,
+             GenericSerialInfo,
+             sizeof (CM_ARM_SERIAL_PORT_INFO) * NodeCount,
+             &NewCmObjDesc
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Add all the CmObjs to the Configuration Manager.
+  Status = AddMultipleCmObj (FdtParserHandle, NewCmObjDesc, 0, NULL);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+
+  FreeCmObjDesc (NewCmObjDesc);
+  return Status;
+}
+
+/** CM_ARM_SERIAL_PORT_INFO parser function (for debug/console serial-port).
+
+  This parser expects FdtBranch to be the debug serial-port node.
+  At most one CmObj is created.
+  The following structure is populated:
+  typedef struct CmArmSerialPortInfo {
+    UINT64  BaseAddress;                      // {Populated}
+    UINT32  Interrupt;                        // {Populated}
+    UINT64  BaudRate;                         // {default}
+    UINT32  Clock;                            // {Populated}
+    UINT16  PortSubtype;                      // {Populated}
+    UINT64  BaseAddressLength                 // {Populated}
+  } CM_ARM_SERIAL_PORT_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+  @param [in]  SerialObjectId  ArmNamespace Object ID for the serial port.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ArmSerialPortInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch,
+  IN        EARM_OBJECT_ID            SerialObjectId
+  )
+{
+  EFI_STATUS                Status;
+  CM_ARM_SERIAL_PORT_INFO   SerialInfo;
+
+  if ((SerialObjectId != EArmObjSerialDebugPortInfo) &&
+      (SerialObjectId != EArmObjSerialConsolePortInfo)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ZeroMem (&SerialInfo, sizeof (SerialInfo));
+
+  Status = SerialPortNodeParser (
+             FdtParserHandle->Fdt,
+             FdtBranch,
+             &SerialInfo
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Status = ArmSerialPortInfoDispatch (
+             FdtParserHandle,
+             &SerialInfo,
+             1,
+             SerialObjectId
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+
+  return Status;
+}
+
+/** SerialPort dispatcher.
+
+  This disptacher populates the CM_ARM_SERIAL_PORT_INFO structure for
+  the following CM_OBJ_ID:
+   - EArmObjSerialConsolePortInfo
+   - EArmObjSerialDebugPortInfo
+   - EArmObjSerialPortInfo
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+SerialPortDispatcher (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  )
+{
+  EFI_STATUS                Status;
+  INT32                     SerialConsoleNode;
+  INT32                     SerialDebugNode;
+  INT32                     SerialNode;
+  UINT32                    Index;
+  UINT32                    SerialNodeCount;
+  UINT32                    SerialNodesRemaining;
+  CM_ARM_SERIAL_PORT_INFO * GenericSerialInfo;
+  UINT32                    GenericSerialIndex;
+  VOID                    * Fdt;
+
+  if (FdtParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Fdt = FdtParserHandle->Fdt;
+
+  // Count the number of serial-ports.
+  Status = FdtCountCompatNodeInBranch (
+             Fdt,
+             FdtBranch,
+             &SerialCompatibleInfo,
+             &SerialNodeCount
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  if (SerialNodeCount == 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  // Track remaining nodes separately as SerialNodeCount
+  // is used in for loop below and reducing SerialNodeCount
+  // would result in the Generic Serial port nodes not
+  // being found if the serial console port node is among
+  // the first few serial nodes.
+  SerialNodesRemaining = SerialNodeCount;
+
+  // Identify the serial console port.
+  Status = GetSerialConsoleNode (Fdt, &SerialConsoleNode);
+  if (Status == EFI_NOT_FOUND) {
+    // No serial console.
+    SerialConsoleNode = -1;
+  } else if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  } else {
+    // Parse the console serial-port.
+    Status = ArmSerialPortInfoParser (
+               FdtParserHandle,
+               SerialConsoleNode,
+               EArmObjSerialConsolePortInfo
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+    SerialNodesRemaining--;
+  }
+
+  GenericSerialInfo = NULL;
+  if (SerialNodesRemaining > 1) {
+    // We have more than one serial port remaining.
+    // This means that the first serial port will
+    // be reserved as a debug port, and the remaining
+    // will be for general purpose use.
+    SerialNodesRemaining--;
+    GenericSerialInfo = AllocateZeroPool (
+                          SerialNodesRemaining *
+                          sizeof (CM_ARM_SERIAL_PORT_INFO)
+                          );
+    if (GenericSerialInfo == NULL) {
+      ASSERT (0);
+      return EFI_OUT_OF_RESOURCES;
+    }
+  }
+
+  SerialNode = FdtBranch;
+  SerialDebugNode = -1;
+  GenericSerialIndex = 0;
+  for (Index = 0; Index < SerialNodeCount; Index++) {
+    // Search the next serial-port node in the branch.
+    Status = FdtGetNextCompatNodeInBranch (
+               Fdt,
+               FdtBranch,
+               &SerialCompatibleInfo,
+               &SerialNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      if (Status == EFI_NOT_FOUND) {
+        // Should have found the node.
+        Status = EFI_ABORTED;
+      }
+      goto exit_handler;
+    }
+
+    // Ignore the serial console node.
+    if (SerialNode == SerialConsoleNode) {
+      continue;
+    } else if (SerialDebugNode == -1) {
+      // The first serial-port node, not being the console serial-port,
+      // will be the debug serial-port.
+      SerialDebugNode = SerialNode;
+      Status = ArmSerialPortInfoParser (
+                 FdtParserHandle,
+                 SerialDebugNode,
+                 EArmObjSerialDebugPortInfo
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        goto exit_handler;
+      }
+    } else {
+      if (GenericSerialInfo == NULL) {
+        // Should not be possible.
+        ASSERT (0);
+        Status = EFI_ABORTED;
+        goto exit_handler;
+      }
+
+      Status = SerialPortNodeParser (
+                 Fdt,
+                 SerialNode,
+                 &GenericSerialInfo[GenericSerialIndex++]
+                 );
+      if (EFI_ERROR (Status)) {
+        ASSERT (0);
+        goto exit_handler;
+      }
+    }
+  } // for
+
+  if (GenericSerialIndex > 0) {
+    Status = ArmSerialPortInfoDispatch (
+               FdtParserHandle,
+               GenericSerialInfo,
+               GenericSerialIndex,
+               EArmObjSerialPortInfo
+               );
+  }
+
+exit_handler:
+  if (GenericSerialInfo != NULL) {
+    FreePool (GenericSerialInfo);
+  }
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h
new file mode 100644
index 000000000000..5e4707849e39
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h
@@ -0,0 +1,47 @@
+/** @file
+  Arm Serial Port Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/serial/serial.yaml
+  - linux/Documentation/devicetree/bindings/serial/8250.txt
+**/
+
+#ifndef ARM_SERIAL_PORT_PARSER_H_
+#define ARM_SERIAL_PORT_PARSER_H_
+
+/** SerialPort dispatcher.
+
+  This disptacher populates the CM_ARM_SERIAL_PORT_INFO structure for
+  the following CM_OBJ_ID:
+   - EArmObjSerialConsolePortInfo
+   - EArmObjSerialDebugPortInfo
+   - EArmObjSerialPortInfo
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+SerialPortDispatcher (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  );
+
+#endif // ARM_SERIAL_PORT_PARSER_H_
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v1 07/14] DynamicTablesPkg: FdtHwInfoParser: Add GICC parser
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
                   ` (5 preceding siblings ...)
  2021-06-23 12:38 ` [PATCH v1 06/14] DynamicTablesPkg: FdtHwInfoParser: Add Serial port parser PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  2021-11-05 14:28   ` Sami Mujawar
  2021-06-23 12:38 ` [PATCH v1 08/14] DynamicTablesPkg: FdtHwInfoParser: Add GICD parser PierreGondois
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

The GIC CPU Interface (GICC) structure is part of the Multiple
APIC Description Table (MADT) that describes the interrupt model
for the platform. The MADT table is a mandatory table required
for booting a standards-based operating system.

Arm requires the GIC interrupt model, in which the logical
processors are required to have a Processor Device object in
the DSDT, and must convey each processor’s GIC information to
the OS using the GICC structure.

The CPU and GIC information is described in the platform Device
Tree, the bindings for which can be found at:
 - linux/Documentation/devicetree/bindings/arm/cpus.yaml
 - linux/Documentation/devicetree/bindings/interrupt-controller/
   arm,gic.yaml
 - linux/Documentation/devicetree/bindings/interrupt-controller/
   arm,gic-v3.yaml

The FdtHwInfoParser implements a GIC CPU Interface Parser that
parses the platform Device Tree to create CM_ARM_GICC_INFO
objects which are encapsulated in a Configuration Manager
descriptor object and added to the platform information
repository.

The platform Configuration Manager can then utilise this
information when generating the MADT and the SSDT CPU
information tables.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 .../FdtHwInfoParserLib/Gic/ArmGicCParser.c    | 762 ++++++++++++++++++
 .../FdtHwInfoParserLib/Gic/ArmGicCParser.h    |  67 ++
 2 files changed, 829 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.c
new file mode 100644
index 000000000000..2163888c870e
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.c
@@ -0,0 +1,762 @@
+/** @file
+  Arm Gic cpu parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/arm/cpus.yaml
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#include "FdtHwInfoParser.h"
+#include "CmObjectDescUtility.h"
+#include "Gic/ArmGicCParser.h"
+#include "Gic/ArmGicDispatcher.h"
+
+/** List of "compatible" property values for CPU nodes.
+
+  Any other "compatible" value is not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR CpuCompatibleStr[] = {
+  {"arm,arm-v7"},
+  {"arm,arm-v8"},
+  {"arm,cortex-a15"},
+  {"arm,cortex-a7"},
+  {"arm,cortex-a57"}
+};
+
+/** COMPATIBILITY_INFO structure for CPU nodes.
+*/
+STATIC CONST COMPATIBILITY_INFO CpuCompatibleInfo = {
+  ARRAY_SIZE (CpuCompatibleStr),
+  CpuCompatibleStr
+};
+
+/** Parse a "cpu" node.
+
+  @param [in]  Fdt              Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  CpuNode          Offset of a cpu node.
+  @param [in]  GicVersion       Version of the GIC.
+  @param [in]  AddressCells     Number of address cells used for the reg
+                                property.
+  @param [out] GicCInfo         CM_ARM_GICC_INFO structure to populate.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CpuNodeParser (
+  IN  CONST VOID              * Fdt,
+  IN        INT32               CpuNode,
+  IN        UINT32              GicVersion,
+  IN        UINT32              AddressCells,
+  OUT       CM_ARM_GICC_INFO  * GicCInfo
+)
+{
+  UINT32          CpuRegProp;
+  CONST VOID    * Data;
+  INT32           DataSize;
+  UINT64          MpIdr;
+
+  if (GicCInfo == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Data = fdt_getprop (Fdt, CpuNode, "reg", &DataSize);
+  if ((Data == NULL)                  ||
+      ((DataSize != sizeof (UINT32))  &&
+       (DataSize != sizeof (UINT64)))) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  if (AddressCells == 1) {
+    MpIdr = fdt32_to_cpu (*((UINT32*)Data));
+  } else if (AddressCells == 2) {
+    MpIdr = fdt64_to_cpu (*((UINT64*)Data));
+  } else {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Currently we only support 3 affinity levels.
+  if ((MpIdr & ~(ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2)) != 0) {
+    ASSERT (0);
+    return EFI_UNSUPPORTED;
+  }
+
+  CpuRegProp = (MpIdr & (ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2));
+
+  /* ACPI 6.3, s5.2.12.14 GIC CPU Interface (GICC) Structure:
+     GIC 's CPU Interface Number. In GICv1/v2 implementations,
+     this value matches the bit index of the associated processor
+     in the GIC distributor's GICD_ITARGETSR register. For
+     GICv3/4 implementations this field must be provided by the
+     platform, if compatibility mode is supported. If it is not supported
+     by the implementation, then this field must be zero.
+
+     Note: We do not support compatibility mode for GicV3
+  */
+  if (GicVersion == 2) {
+    GicCInfo->CPUInterfaceNumber = CpuRegProp;
+  } else {
+    GicCInfo->CPUInterfaceNumber = 0;
+  }
+
+  GicCInfo->AcpiProcessorUid = CpuRegProp;
+  GicCInfo->Flags = EFI_ACPI_6_3_GIC_ENABLED;
+  GicCInfo->MPIDR= MpIdr;
+
+  return EFI_SUCCESS;
+}
+
+/** Parse a "cpus" node and its children "cpu" nodes.
+
+  Create as many CM_ARM_GICC_INFO structures as "cpu" nodes.
+
+  @param [in]  Fdt              Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  CpusNode         Offset of a cpus node.
+  @param [in]  GicVersion       Version of the GIC.
+  @param [out] NewGicCmObjDesc  If success, CM_OBJ_DESCRIPTOR containing
+                                all the created CM_ARM_GICC_INFO.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CpusNodeParser (
+  IN  CONST VOID                * Fdt,
+  IN        INT32                 CpusNode,
+  IN        UINT32                GicVersion,
+  OUT       CM_OBJ_DESCRIPTOR  ** NewGicCmObjDesc
+  )
+{
+  EFI_STATUS            Status;
+  INT32                 CpuNode;
+  UINT32                CpuNodeCount;
+  INT32                 AddressCells;
+
+  UINT32                Index;
+  CM_ARM_GICC_INFO    * GicCInfoBuffer;
+  UINT32                GicCInfoBufferSize;
+
+  if (NewGicCmObjDesc == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  AddressCells = fdt_address_cells (Fdt, CpusNode);
+  if (AddressCells < 0) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  // Count the number of "cpu" nodes under the "cpus" node.
+  Status = FdtCountNamedNodeInBranch (Fdt, CpusNode, "cpu", &CpuNodeCount);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  if (CpuNodeCount == 0) {
+    ASSERT (0);
+    return EFI_NOT_FOUND;
+  }
+
+  // Allocate memory for CpuNodeCount CM_ARM_GICC_INFO structures.
+  GicCInfoBufferSize = CpuNodeCount * sizeof (CM_ARM_GICC_INFO);
+  GicCInfoBuffer = AllocateZeroPool (GicCInfoBufferSize);
+  if (GicCInfoBuffer == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  CpuNode = CpusNode;
+  for (Index = 0; Index < CpuNodeCount; Index++) {
+    Status = FdtGetNextNamedNodeInBranch (Fdt, CpusNode, "cpu", &CpuNode);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      if (Status == EFI_NOT_FOUND) {
+        // Should have found the node.
+        Status = EFI_ABORTED;
+      }
+      goto exit_handler;
+    }
+
+    // Parse the "cpu" node.
+    if (!FdtNodeIsCompatible (Fdt, CpuNode, &CpuCompatibleInfo)) {
+      ASSERT (0);
+      Status = EFI_UNSUPPORTED;
+      goto exit_handler;
+    }
+
+    Status = CpuNodeParser (
+                Fdt,
+                CpuNode,
+                GicVersion,
+                AddressCells,
+                &GicCInfoBuffer[Index]
+                );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      goto exit_handler;
+    }
+  } // for
+
+  Status = CreateCmObjDesc (
+             CREATE_CM_ARM_OBJECT_ID (EArmObjGicCInfo),
+             CpuNodeCount,
+             GicCInfoBuffer,
+             GicCInfoBufferSize,
+             NewGicCmObjDesc
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+
+exit_handler:
+  FreePool (GicCInfoBuffer);
+  return Status;
+}
+
+/** Parse a Gic compatible interrupt-controller node,
+    extracting GicC information generic to Gic v2 and v3.
+
+  This function modifies a CM_OBJ_DESCRIPTOR object.
+  The following CM_ARM_GICC_INFO fields are patched:
+    - VGICMaintenanceInterrupt;
+    - Flags;
+
+  @param [in]       Fdt              Pointer to a Flattened Device Tree (Fdt).
+  @param [in]       GicIntcNode      Offset of a Gic compatible
+                                     interrupt-controller node.
+  @param [in, out]  GicCCmObjDesc    The CM_ARM_GICC_INFO to patch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicCIntcNodeParser (
+  IN      CONST VOID                * Fdt,
+  IN            INT32                 GicIntcNode,
+  IN  OUT       CM_OBJ_DESCRIPTOR   * GicCCmObjDesc
+  )
+{
+  EFI_STATUS          Status;
+  INT32               IntCells;
+  CM_ARM_GICC_INFO  * GicCInfo;
+
+  CONST UINT8       * Data;
+  INT32               DataSize;
+
+  if (GicCCmObjDesc == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Get the number of cells used to encode an interrupt.
+  Status = FdtGetInterruptCellsInfo (Fdt, GicIntcNode, &IntCells);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Get the GSIV maintenance interrupt.
+  // According to the DT bindings, this could be the:
+  // "Interrupt source of the parent interrupt controller on secondary GICs"
+  // but it is assumed that only one Gic is available.
+  Data = fdt_getprop (Fdt, GicIntcNode, "interrupts", &DataSize);
+  if ((Data != NULL) && (DataSize == (IntCells * sizeof (UINT32)))) {
+    GicCInfo = (CM_ARM_GICC_INFO*)GicCCmObjDesc->Data;
+    GicCInfo->VGICMaintenanceInterrupt =
+      FdtGetInterruptId ((CONST UINT32*)Data);
+    GicCInfo->Flags = DT_IRQ_IS_EDGE_TRIGGERED (
+                          fdt32_to_cpu (((UINT32*)Data)[IRQ_FLAGS_OFFSET])
+                        ) ?
+                        EFI_ACPI_6_3_VGIC_MAINTENANCE_INTERRUPT_MODE_FLAGS :
+                        0;
+    return Status;
+  } else if (DataSize < 0) {
+    // This property is optional and was not found. Just return.
+    return Status;
+  }
+  // The property exists and its size doesn't match for one interrupt.
+  ASSERT (0);
+  return EFI_ABORTED;
+}
+
+/** Parse a Gic compatible interrupt-controller node,
+    extracting GicCv2 information.
+
+  This function modifies a CM_OBJ_DESCRIPTOR object.
+  The following CM_ARM_GICC_INFO fields are patched:
+    - PhysicalAddress;
+    - GICH;
+    - GICV;
+
+  @param [in]       Fdt              Pointer to a Flattened Device Tree (Fdt).
+  @param [in]       Gicv2IntcNode    Offset of a Gicv2 compatible
+                                     interrupt-controller node.
+  @param [in, out]  GicCCmObjDesc    The CM_ARM_GICC_INFO to patch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicCv2IntcNodeParser (
+  IN      CONST VOID                * Fdt,
+  IN            INT32                 Gicv2IntcNode,
+  IN  OUT       CM_OBJ_DESCRIPTOR   * GicCCmObjDesc
+  )
+{
+  EFI_STATUS          Status;
+  UINT32              Index;
+  CM_ARM_GICC_INFO  * GicCInfo;
+  INT32               AddressCells;
+  INT32               SizeCells;
+
+  CONST UINT8       * GicCValue;
+  CONST UINT8       * GicVValue;
+  CONST UINT8       * GicHValue;
+
+  CONST UINT8       * Data;
+  INT32               DataSize;
+  UINT32              RegSize;
+  UINT32              RegCount;
+
+  if (GicCCmObjDesc == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  GicCInfo = (CM_ARM_GICC_INFO*)GicCCmObjDesc->Data;
+  GicVValue = NULL;
+  GicHValue = NULL;
+
+  // Get the #address-cells and #size-cells property values.
+  Status = FdtGetParentAddressInfo (
+             Fdt,
+             Gicv2IntcNode,
+             &AddressCells,
+             &SizeCells
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Don't support more than 64 bits and less than 32 bits addresses.
+  if ((AddressCells < 1)  ||
+      (AddressCells > 2)  ||
+      (SizeCells < 1)     ||
+      (SizeCells > 2)) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
+
+  Data = fdt_getprop (Fdt, Gicv2IntcNode, "reg", &DataSize);
+  if ((Data == NULL)  ||
+      (DataSize < 0)  ||
+      ((DataSize % RegSize) != 0)) {
+    // If error or wrong size.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  RegCount = DataSize/RegSize;
+
+  switch (RegCount) {
+    case 4:
+    {
+      // GicV is at index 3 in the reg property. GicV is optional.
+      GicVValue = Data + (sizeof (UINT32) *
+        GET_DT_REG_ADDRESS_OFFSET (3, AddressCells, SizeCells));
+      // fall-through.
+    }
+    case 3:
+    {
+      // GicH is at index 2 in the reg property. GicH is optional.
+      GicHValue = Data + (sizeof (UINT32) *
+        GET_DT_REG_ADDRESS_OFFSET (2, AddressCells, SizeCells));
+      // fall-through.
+    }
+    case 2:
+    {
+      // GicC is at index 1 in the reg property. GicC is mandatory.
+      GicCValue = Data + (sizeof (UINT32) *
+        GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, SizeCells));
+      break;
+    }
+    default:
+    {
+      // Not enough or too much information.
+      ASSERT (0);
+      return EFI_ABORTED;
+    }
+  }
+
+  // Patch the relevant fields of the CM_ARM_GICC_INFO objects.
+  for (Index = 0; Index < GicCCmObjDesc->Count; Index++) {
+    if (AddressCells == 2) {
+      GicCInfo[Index].PhysicalBaseAddress = fdt64_to_cpu (*(UINT64*)GicCValue);
+      GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
+                              fdt64_to_cpu (*(UINT64*)GicHValue);
+      GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
+                              fdt64_to_cpu (*(UINT64*)GicVValue);
+    } else {
+      GicCInfo[Index].PhysicalBaseAddress = fdt32_to_cpu (*(UINT32*)GicCValue);
+      GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
+                              fdt32_to_cpu (*(UINT32*)GicHValue);
+      GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
+                              fdt32_to_cpu (*(UINT32*)GicVValue);
+    }
+  } // for
+
+  return EFI_SUCCESS;
+}
+
+/** Parse a Gic compatible interrupt-controller node,
+    extracting GicCv3 information.
+
+  This function modifies a CM_OBJ_DESCRIPTOR object.
+  The following CM_ARM_GICC_INFO fields are patched:
+    - PhysicalAddress;
+    - GICH;
+    - GICV;
+
+  @param [in]       Fdt              Pointer to a Flattened Device Tree (Fdt).
+  @param [in]       Gicv3IntcNode    Offset of a Gicv3 compatible
+                                     interrupt-controller node.
+  @param [in, out]  GicCCmObjDesc    The CM_ARM_GICC_INFO to patch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicCv3IntcNodeParser (
+  IN      CONST VOID                * Fdt,
+  IN            INT32                 Gicv3IntcNode,
+  IN  OUT       CM_OBJ_DESCRIPTOR   * GicCCmObjDesc
+  )
+{
+  EFI_STATUS          Status;
+  UINT32              Index;
+  CM_ARM_GICC_INFO  * GicCInfo;
+  INT32               AddressCells;
+  INT32               SizeCells;
+  UINT32              AdditionalRedistReg;
+
+  CONST UINT8       * GicCValue;
+  CONST UINT8       * GicVValue;
+  CONST UINT8       * GicHValue;
+
+  CONST UINT8       * Data;
+  INT32               DataSize;
+  UINT32              RegSize;
+  UINT32              RegCount;
+
+  if (GicCCmObjDesc == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  GicCInfo = (CM_ARM_GICC_INFO*)GicCCmObjDesc->Data;
+  GicCValue = NULL;
+  GicVValue = NULL;
+  GicHValue = NULL;
+
+  // Get the #address-cells and #size-cells property values.
+  Status = FdtGetParentAddressInfo (
+             Fdt,
+             Gicv3IntcNode,
+             &AddressCells,
+             &SizeCells
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Don't support more than 64 bits and less than 32 bits addresses.
+  if ((AddressCells < 1)  ||
+      (AddressCells > 2)  ||
+      (SizeCells < 1)     ||
+      (SizeCells > 2)) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  // The "#redistributor-regions" property is optional.
+  Data = fdt_getprop (Fdt, Gicv3IntcNode, "#redistributor-regions", &DataSize);
+  if ((Data != NULL) && (DataSize == sizeof (UINT32))) {
+    ASSERT (fdt32_to_cpu (*(UINT32*)Data) > 1);
+    AdditionalRedistReg = fdt32_to_cpu (*(UINT32*)Data) - 1;
+  } else {
+    AdditionalRedistReg = 0;
+  }
+
+  RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
+
+  /*
+    Ref: linux/blob/master/Documentation/devicetree/bindings/
+         interrupt-controller/arm%2Cgic-v3.yaml
+
+    reg:
+    description: |
+      Specifies base physical address(s) and size of the GIC
+      registers, in the following order:
+      - GIC Distributor interface (GICD)
+      - GIC Redistributors (GICR), one range per redistributor region
+      - GIC CPU interface (GICC)
+      - GIC Hypervisor interface (GICH)
+      - GIC Virtual CPU interface (GICV)
+      GICC, GICH and GICV are optional.
+    minItems: 2
+    maxItems: 4096
+  */
+  Data = fdt_getprop (Fdt, Gicv3IntcNode, "reg", &DataSize);
+  if ((Data == NULL)  ||
+      (DataSize < 0)  ||
+      ((DataSize % RegSize) != 0)) {
+    // If error or wrong size.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  RegCount = (DataSize / RegSize) - AdditionalRedistReg;
+
+  // The GicD and GicR info is mandatory.
+  switch (RegCount) {
+    case 5:
+    {
+      // GicV is at index 4 in the reg property. GicV is optional.
+      GicVValue = Data + (sizeof (UINT32) *
+        GET_DT_REG_ADDRESS_OFFSET (
+          4 + AdditionalRedistReg,
+          AddressCells,
+          SizeCells
+          ));
+      // fall-through.
+    }
+    case 4:
+    {
+      // GicH is at index 3 in the reg property. GicH is optional.
+      GicHValue = Data + (sizeof (UINT32) *
+        GET_DT_REG_ADDRESS_OFFSET (
+          3 + AdditionalRedistReg,
+          AddressCells,
+          SizeCells
+          ));
+      // fall-through.
+    }
+    case 3:
+    {
+      // GicC is at index 2 in the reg property. GicC is optional.
+      // Even though GicC is optional, it is made mandatory in this parser.
+      GicCValue = Data + (sizeof (UINT32) *
+        GET_DT_REG_ADDRESS_OFFSET (
+          2 + AdditionalRedistReg,
+          AddressCells,
+          SizeCells
+          ));
+      // fall-through
+    }
+    case 2:
+    {
+      // GicR is discribed by the CM_ARM_GIC_REDIST_INFO object.
+      // GicD is described by the CM_ARM_GICD_INFO object.
+      break;
+    }
+    default:
+    {
+      // Not enough or too much information.
+      ASSERT (0);
+      return EFI_ABORTED;
+    }
+  }
+
+  // Patch the relevant fields of the CM_ARM_GICC_INFO objects.
+  if (AddressCells == 2) {
+    for (Index = 0; Index < GicCCmObjDesc->Count; Index++) {
+      // GicR is discribed by the CM_ARM_GIC_REDIST_INFO object.
+      GicCInfo[Index].GICRBaseAddress = 0;
+      GicCInfo[Index].PhysicalBaseAddress = (GicCValue == NULL) ? 0 :
+                              fdt64_to_cpu (*(UINT64*)GicCValue);
+      GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
+                              fdt64_to_cpu (*(UINT64*)GicHValue);
+      GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
+                              fdt64_to_cpu (*(UINT64*)GicVValue);
+    }
+  } else {
+    for (Index = 0; Index < GicCCmObjDesc->Count; Index++) {
+      // GicR is discribed by the CM_ARM_GIC_REDIST_INFO object.
+      GicCInfo[Index].GICRBaseAddress = 0;
+      GicCInfo[Index].PhysicalBaseAddress = (GicCValue == NULL) ? 0 :
+                              fdt32_to_cpu (*(UINT32*)GicCValue);
+      GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
+                              fdt32_to_cpu (*(UINT32*)GicHValue);
+      GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
+                              fdt32_to_cpu (*(UINT32*)GicVValue);
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** CM_ARM_GICC_INFO parser function.
+
+  This parser expects FdtBranch to be the "\cpus" node node.
+  At most one CmObj is created.
+  The following structure is populated:
+  typedef struct CmArmGicCInfo {
+    UINT32  CPUInterfaceNumber;               // {Populated}
+    UINT32  AcpiProcessorUid;                 // {Populated}
+    UINT32  Flags;                            // {Populated}
+    UINT32  ParkingProtocolVersion;           // {default = 0}
+    UINT32  PerformanceInterruptGsiv;         // {default = 0}
+    UINT64  ParkedAddress;                    // {default = 0}
+    UINT64  PhysicalBaseAddress;              // {Populated}
+    UINT64  GICV;                             // {Populated}
+    UINT64  GICH;                             // {Populated}
+    UINT32  VGICMaintenanceInterrupt;         // {Populated}
+    UINT64  GICRBaseAddress;                  // {default = 0}
+    UINT64  MPIDR;                            // {Populated}
+    UINT8   ProcessorPowerEfficiencyClass;    // {default = 0}
+    UINT16  SpeOverflowInterrupt;             // {default = 0}
+    UINT32  ProximityDomain;                  // {default = 0}
+    UINT32  ClockDomain;                      // {default = 0}
+    UINT32  AffinityFlags;                    // {default = 0}
+  } CM_ARM_GICC_INFO;
+
+  The pmu information can be found in the pmu node. There is no support
+  for now.
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicCInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  )
+{
+  EFI_STATUS            Status;
+  INT32                 IntcNode;
+  UINT32                GicVersion;
+  CM_OBJ_DESCRIPTOR   * NewCmObjDesc;
+  VOID                * Fdt;
+
+  if (FdtParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Fdt = FdtParserHandle->Fdt;
+  NewCmObjDesc = NULL;
+
+  // The FdtBranch points to the Cpus Node.
+  // Get the interrupt-controller node associated to the "cpus" node.
+  Status = FdtGetIntcParentNode (Fdt, FdtBranch, &IntcNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    if (Status == EFI_NOT_FOUND) {
+      // Should have found the node.
+      Status = EFI_ABORTED;
+    }
+    goto exit_handler;
+  }
+
+  Status = GetGicVersion (Fdt, IntcNode, &GicVersion);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // Parse the "cpus" nodes and its children "cpu" nodes,
+  // and create a CM_OBJ_DESCRIPTOR.
+  Status = CpusNodeParser (Fdt, FdtBranch, GicVersion, &NewCmObjDesc);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Parse the interrupt-controller node according to the Gic version.
+  switch (GicVersion) {
+    case 2:
+    {
+      Status = GicCv2IntcNodeParser (Fdt, IntcNode, NewCmObjDesc);
+      break;
+    }
+    case 3:
+    {
+      Status = GicCv3IntcNodeParser (Fdt, IntcNode, NewCmObjDesc);
+      break;
+    }
+    default:
+    {
+      // Unsupported Gic version.
+      ASSERT (0);
+      Status = EFI_UNSUPPORTED;
+    }
+  }
+
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // Parse the Gic information common to Gic v2 and v3.
+  Status = GicCIntcNodeParser (Fdt, IntcNode, NewCmObjDesc);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+  // Add all the CmObjs to the Configuration Manager.
+  Status = AddMultipleCmObj (FdtParserHandle, NewCmObjDesc, 0, NULL);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    goto exit_handler;
+  }
+
+exit_handler:
+  FreeCmObjDesc (NewCmObjDesc);
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.h
new file mode 100644
index 000000000000..10e6b03c541f
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.h
@@ -0,0 +1,67 @@
+/** @file
+  Arm Gic cpu parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#ifndef ARM_GICC_PARSER_H_
+#define ARM_GICC_PARSER_H_
+
+/** CM_ARM_GICC_INFO parser function.
+
+  This parser expects FdtBranch to be the "\cpus" node node.
+  At most one CmObj is created.
+  The following structure is populated:
+  typedef struct CmArmGicCInfo {
+    UINT32  CPUInterfaceNumber;               // {Populated}
+    UINT32  AcpiProcessorUid;                 // {Populated}
+    UINT32  Flags;                            // {Populated}
+    UINT32  ParkingProtocolVersion;           // {default = 0}
+    UINT32  PerformanceInterruptGsiv;         // {default = 0}
+    UINT64  ParkedAddress;                    // {default = 0}
+    UINT64  PhysicalBaseAddress;              // {Populated}
+    UINT64  GICV;                             // {Populated}
+    UINT64  GICH;                             // {Populated}
+    UINT32  VGICMaintenanceInterrupt;         // {Populated}
+    UINT64  GICRBaseAddress;                  // {default = 0}
+    UINT64  MPIDR;                            // {Populated}
+    UINT8   ProcessorPowerEfficiencyClass;    // {default = 0}
+    UINT16  SpeOverflowInterrupt;             // {default = 0}
+    UINT32  ProximityDomain;                  // {default = 0}
+    UINT32  ClockDomain;                      // {default = 0}
+    UINT32  AffinityFlags;                    // {default = 0}
+  } CM_ARM_GICC_INFO;
+
+  The pmu information can be found in the pmu node. There is no support
+  for now.
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicCInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  );
+
+#endif // ARM_GICC_PARSER_H_
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v1 08/14] DynamicTablesPkg: FdtHwInfoParser: Add GICD parser
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
                   ` (6 preceding siblings ...)
  2021-06-23 12:38 ` [PATCH v1 07/14] DynamicTablesPkg: FdtHwInfoParser: Add GICC parser PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  2021-06-23 12:38 ` [PATCH v1 09/14] DynamicTablesPkg: FdtHwInfoParser: Add MSI Frame parser PierreGondois
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

On ARM-based systems the Generic Interrupt Controller (GIC)
manages interrupts on the system. Each interrupt is identified
in the GIC by an interrupt identifier (INTID). ACPI GSIVs map
one to one to GIC INTIDs for peripheral interrupts, whether
shared (SPI) or private (PPI). The GIC distributor provides
the routing configuration for the interrupts.

The GIC Distributor (GICD) structure is part of the Multiple
APIC Description Table (MADT) that describes the GIC
distributor to the OS. The MADT table is a mandatory table
required for booting a standards-based operating system.

The GIC Distributor information is described in the platform
Device Tree, the bindings for which can be found at:
 - linux/Documentation/devicetree/bindings/interrupt-controller/
   arm,gic.yaml
 - linux/Documentation/devicetree/bindings/interrupt-controller/
   arm,gic-v3.yaml

The FdtHwInfoParser implements a GIC Distributor Parser that
parses the platform Device Tree to create CM_ARM_GICD_INFO
object which is encapsulated in a Configuration Manager
descriptor object and added to the platform information
repository.

The platform Configuration Manager can then utilise this
information when generating the MADT table.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 .../FdtHwInfoParserLib/Gic/ArmGicDParser.c    | 171 ++++++++++++++++++
 .../FdtHwInfoParserLib/Gic/ArmGicDParser.h    |  50 +++++
 2 files changed, 221 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDParser.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDParser.c
new file mode 100644
index 000000000000..10207a667336
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDParser.c
@@ -0,0 +1,171 @@
+/** @file
+  Arm Gic Distributor Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#include "CmObjectDescUtility.h"
+#include "FdtHwInfoParser.h"
+#include "Gic/ArmGicDispatcher.h"
+#include "Gic/ArmGicDParser.h"
+
+/** Parse a Gic compatible interrupt-controller node,
+    extracting GicD information.
+
+  This parser is valid for Gic v2 and v3.
+
+  @param [in]  Fdt              Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  GicIntcNode      Offset of a Gic compatible
+                                interrupt-controller node.
+  @param [in]  GicDInfo         The CM_ARM_GICD_INFO to populate.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicDIntcNodeParser (
+  IN  CONST VOID        * Fdt,
+  IN  INT32               GicIntcNode,
+  IN  CM_ARM_GICD_INFO  * GicDInfo
+  )
+{
+  EFI_STATUS    Status;
+  INT32         AddressCells;
+  CONST UINT8 * Data;
+  INT32         DataSize;
+
+  if ((Fdt == NULL) ||
+      (GicDInfo == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = FdtGetParentAddressInfo (Fdt, GicIntcNode, &AddressCells, NULL);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Don't support more than 64 bits and less than 32 bits addresses.
+  if ((AddressCells < 1)  ||
+      (AddressCells > 2)) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  Data = fdt_getprop (Fdt, GicIntcNode, "reg", &DataSize);
+  if ((Data == NULL) || (DataSize < (INT32)(AddressCells * sizeof (UINT32)))) {
+    // If error or not enough space.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  if (AddressCells == 2) {
+    GicDInfo->PhysicalBaseAddress = fdt64_to_cpu (*(UINT64*)Data);
+  } else {
+    GicDInfo->PhysicalBaseAddress = fdt32_to_cpu (*(UINT32*)Data);
+  }
+
+  return Status;
+}
+
+/** CM_ARM_GICD_INFO parser function.
+
+  This parser expects FdtBranch to be a Gic interrupt-controller node.
+  At most one CmObj is created.
+  The following structure is populated:
+  typedef struct CmArmGicDInfo {
+    UINT64  PhysicalBaseAddress;              // {Populated}
+    UINT32  SystemVectorBase;
+    UINT8   GicVersion;                       // {Populated}
+  } CM_ARM_GICD_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicDInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  )
+{
+  EFI_STATUS          Status;
+  UINT32              GicVersion;
+  CM_ARM_GICD_INFO    GicDInfo;
+  VOID              * Fdt;
+
+  if (FdtParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Fdt = FdtParserHandle->Fdt;
+
+  if (!FdtNodeHasProperty (Fdt, FdtBranch, "interrupt-controller")) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Get the Gic version of the interrupt-controller.
+  Status = GetGicVersion (Fdt, FdtBranch, &GicVersion);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  ZeroMem (&GicDInfo, sizeof (GicDInfo));
+  GicDInfo.GicVersion = GicVersion;
+
+  // Parse the interrupt-controller depending on its Gic version.
+  switch (GicVersion) {
+    case 2:
+    case 3:
+    {
+      // Set the Gic version, then parse the GicD information.
+      Status = GicDIntcNodeParser (Fdt, FdtBranch, &GicDInfo);
+      break;
+    }
+    default:
+    {
+      // Unsupported Gic version.
+      ASSERT (0);
+      return EFI_UNSUPPORTED;
+    }
+  }
+
+  // Add the CmObj to the Configuration Manager.
+  Status = AddSingleCmObj (
+             FdtParserHandle,
+             CREATE_CM_ARM_OBJECT_ID (EArmObjGicDInfo),
+             &GicDInfo,
+             sizeof (CM_ARM_GICD_INFO),
+             NULL
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDParser.h
new file mode 100644
index 000000000000..a274a4b43070
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDParser.h
@@ -0,0 +1,50 @@
+/** @file
+  Arm Gic Distributor Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#ifndef ARM_GICD_PARSER_H_
+#define ARM_GICD_PARSER_H_
+
+/** CM_ARM_GICD_INFO parser function.
+
+  This parser expects FdtBranch to be a Gic interrupt-controller node.
+  At most one CmObj is created.
+  The following structure is populated:
+  typedef struct CmArmGicDInfo {
+    UINT64  PhysicalBaseAddress;              // {Populated}
+    UINT32  SystemVectorBase;
+    UINT8   GicVersion;                       // {Populated}
+  } CM_ARM_GICD_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicDInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  );
+
+#endif // ARM_GICD_PARSER_H_
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v1 09/14] DynamicTablesPkg: FdtHwInfoParser: Add MSI Frame parser
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
                   ` (7 preceding siblings ...)
  2021-06-23 12:38 ` [PATCH v1 08/14] DynamicTablesPkg: FdtHwInfoParser: Add GICD parser PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  2021-06-23 12:38 ` [PATCH v1 10/14] DynamicTablesPkg: FdtHwInfoParser: Add ITS parser PierreGondois
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

Arm GIC version 2 systems that support Message Signalled Interrupts
implement GICv2m MSI frame(s). Each GICv2m MSI frame consists of a
4k page which includes registers to generate message signalled
interrupts to an associated GIC distributor. The frame also includes
registers to discover the set of distributor lines which may be
signalled by MSIs from that frame. A system may have multiple MSI
frames, and separate frames may be defined for secure and non-secure
access.

A MSI Frame structure is part of the Multiple APIC Description Table
(MADT) and must only be used to describe non-secure MSI frames.

The MSI Frame information is described in the platform Device Tree,
the bindings for which can be found at:
 - linux/Documentation/devicetree/bindings/interrupt-controller/
   arm,gic.yaml
 - linux/Documentation/devicetree/bindings/interrupt-controller/
   arm,gic-v3.yaml

The FdtHwInfoParser implements a MSI Frame Parser that parses
the platform Device Tree to create CM_ARM_GIC_MSI_FRAME_INFO
objects which are encapsulated in a Configuration Manager
descriptor object and added to the platform information
repository.

The platform Configuration Manager can then utilise this
information when generating the MADT table.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 .../Gic/ArmGicMsiFrameParser.c                | 214 ++++++++++++++++++
 .../Gic/ArmGicMsiFrameParser.h                |  50 ++++
 2 files changed, 264 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicMsiFrameParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicMsiFrameParser.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicMsiFrameParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicMsiFrameParser.c
new file mode 100644
index 000000000000..9e6715f69ce7
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicMsiFrameParser.c
@@ -0,0 +1,214 @@
+/** @file
+  Arm Gic Msi frame Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#include "CmObjectDescUtility.h"
+#include "FdtHwInfoParser.h"
+#include "Gic/ArmGicDispatcher.h"
+#include "Gic/ArmGicMsiFrameParser.h"
+
+/** List of "compatible" property values for Msi-frame nodes.
+
+  Any other "compatible" value is not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR MsiFrameCompatibleStr[] = {
+  {"arm,gic-v2m-frame"}
+};
+
+/** COMPATIBILITY_INFO structure for the MSI frame.
+*/
+STATIC CONST COMPATIBILITY_INFO MsiFrameCompatibleInfo = {
+  ARRAY_SIZE (MsiFrameCompatibleStr),
+  MsiFrameCompatibleStr
+};
+
+/** Parse a Msi frame node.
+
+  @param [in]  Fdt            Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  MsiFrameNode   Offset of a Msi frame node.
+  @param [in]  MsiFrameId     Frame ID.
+  @param [out] MsiFrameInfo   The CM_ARM_GIC_MSI_FRAME_INFO to populate.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+MsiFrameNodeParser (
+  IN  CONST VOID                  * Fdt,
+  IN  INT32                         MsiFrameNode,
+  IN  UINT32                        MsiFrameId,
+  OUT CM_ARM_GIC_MSI_FRAME_INFO   * MsiFrameInfo
+  )
+{
+  EFI_STATUS    Status;
+  INT32         AddressCells;
+  CONST UINT8 * Data;
+  INT32         DataSize;
+
+  if ((Fdt == NULL) ||
+      (MsiFrameInfo == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = FdtGetParentAddressInfo (Fdt, MsiFrameNode, &AddressCells, NULL);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Don't support more than 64 bits and less than 32 bits addresses.
+  if ((AddressCells < 1)  ||
+      (AddressCells > 2)) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  Data = fdt_getprop (Fdt, MsiFrameNode, "reg", &DataSize);
+  if ((Data == NULL) || (DataSize < (INT32)(AddressCells * sizeof (UINT32)))) {
+    // If error or not enough space.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  if (AddressCells == 2) {
+    MsiFrameInfo->PhysicalBaseAddress = fdt64_to_cpu (*(UINT64*)Data);
+  } else {
+    MsiFrameInfo->PhysicalBaseAddress = fdt32_to_cpu (*(UINT32*)Data);
+  }
+
+  MsiFrameInfo->GicMsiFrameId = MsiFrameId;
+
+  return EFI_SUCCESS;
+}
+
+/** CM_ARM_GIC_MSI_FRAME_INFO parser function.
+
+  The following structure is populated:
+  typedef struct CmArmGicMsiFrameInfo {
+    UINT32  GicMsiFrameId;                    // {Populated}
+    UINT64  PhysicalBaseAddress;              // {Populated}
+    UINT32  Flags;                            // {default = 0}
+    UINT16  SPICount;
+    UINT16  SPIBase;
+  } CM_ARM_GIC_MSI_FRAME_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicMsiFrameInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  )
+{
+  EFI_STATUS                  Status;
+  INT32                       MsiFrameNode;
+  UINT32                      MsiFrameNodeCount;
+
+  UINT32                      Index;
+  CM_ARM_GIC_MSI_FRAME_INFO   MsiFrameInfo;
+  VOID                      * Fdt;
+
+  if (FdtParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Fdt = FdtParserHandle->Fdt;
+
+  // Count the number of nodes having the "interrupt-controller" property.
+  Status = FdtCountPropNodeInBranch (
+             Fdt,
+             FdtBranch,
+             "msi-controller",
+             &MsiFrameNodeCount
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  if (MsiFrameNodeCount == 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  // Parse each node having the "msi-controller" property.
+  MsiFrameNode = FdtBranch;
+  for (Index = 0; Index < MsiFrameNodeCount; Index++) {
+    ZeroMem (&MsiFrameInfo, sizeof (CM_ARM_GIC_MSI_FRAME_INFO));
+
+    Status = FdtGetNextPropNodeInBranch (
+               Fdt,
+               FdtBranch,
+               "msi-controller",
+               &MsiFrameNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      if (Status == EFI_NOT_FOUND) {
+        // Should have found the node.
+        Status = EFI_ABORTED;
+      }
+      return Status;
+    }
+
+    if (!FdtNodeIsCompatible (Fdt, MsiFrameNode, &MsiFrameCompatibleInfo)) {
+      ASSERT (0);
+      Status = EFI_UNSUPPORTED;
+      return Status;
+    }
+
+    // Parse the Msi information.
+    Status = MsiFrameNodeParser (
+               Fdt,
+               MsiFrameNode,
+               Index,
+               &MsiFrameInfo
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // Add the CmObj to the Configuration Manager.
+    Status = AddSingleCmObj (
+               FdtParserHandle,
+               CREATE_CM_ARM_OBJECT_ID (EArmObjGicMsiFrameInfo),
+               &MsiFrameInfo,
+               sizeof (CM_ARM_GIC_MSI_FRAME_INFO),
+               NULL
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  } // for
+
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicMsiFrameParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicMsiFrameParser.h
new file mode 100644
index 000000000000..40f83010a686
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicMsiFrameParser.h
@@ -0,0 +1,50 @@
+/** @file
+  Arm Gic Msi frame Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#ifndef ARM_GIC_MSI_FRAME_PARSER_H_
+#define ARM_GIC_MSI_FRAME_PARSER_H_
+
+/** CM_ARM_GIC_MSI_FRAME_INFO parser function.
+
+  The following structure is populated:
+  typedef struct CmArmGicMsiFrameInfo {
+    UINT32  GicMsiFrameId;                    // {Populated}
+    UINT64  PhysicalBaseAddress;              // {Populated}
+    UINT32  Flags;                            // {default = 0}
+    UINT16  SPICount;
+    UINT16  SPIBase;
+  } CM_ARM_GIC_MSI_FRAME_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicMsiFrameInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  );
+
+#endif // ARM_GIC_MSI_FRAME_PARSER_H_
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v1 10/14] DynamicTablesPkg: FdtHwInfoParser: Add ITS parser
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
                   ` (8 preceding siblings ...)
  2021-06-23 12:38 ` [PATCH v1 09/14] DynamicTablesPkg: FdtHwInfoParser: Add MSI Frame parser PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  2021-06-23 12:38 ` [PATCH v1 11/14] DynamicTablesPkg: FdtHwInfoParser: Add GICR parser PierreGondois
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

Arm GIC v3/v4 optionally includes support for GIC Interrupt
Translation Service (ITS). The GIC ITS Structure is part of
the Multiple APIC Description Table (MADT) that describes
the GIC Interrupt Translation service to the OS.

The GIC Interrupt Translation Service information is described
in the platform Device Tree, the bindings for which can be
found at:
 - linux/Documentation/devicetree/bindings/interrupt-controller/
   arm,gic-v3.yaml

The FdtHwInfoParser implements a GIC ITS Parser that parses the
platform Device Tree to create CM_ARM_GIC_ITS_INFO objects which
are encapsulated in a Configuration Manager descriptor object and
added to the platform information repository.

The platform Configuration Manager can then utilise this information
when generating the MADT table.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 .../FdtHwInfoParserLib/Gic/ArmGicItsParser.c  | 215 ++++++++++++++++++
 .../FdtHwInfoParserLib/Gic/ArmGicItsParser.h  |  48 ++++
 2 files changed, 263 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicItsParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicItsParser.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicItsParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicItsParser.c
new file mode 100644
index 000000000000..faaa0df1d9be
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicItsParser.c
@@ -0,0 +1,215 @@
+/** @file
+  Arm Gic Interrupt Translation Service Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#include "CmObjectDescUtility.h"
+#include "FdtHwInfoParser.h"
+#include "Gic/ArmGicDispatcher.h"
+#include "Gic/ArmGicItsParser.h"
+
+/** Parse a Gic compatible interrupt-controller node,
+    extracting GicIts information.
+
+  This parser is valid for Gic v3 and higher.
+
+  @param [in]  Fdt              Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  GicIntcNode      Offset of a Gic compatible
+                                interrupt-controller node.
+  @param [in]  GicItsId         Id for the Gic ITS node.
+  @param [in]  GicItsInfo       The CM_ARM_GIC_ITS_INFO to populate.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicItsIntcNodeParser (
+  IN  CONST VOID            * Fdt,
+  IN  INT32                   GicIntcNode,
+  IN  UINT32                  GicItsId,
+  IN  CM_ARM_GIC_ITS_INFO   * GicItsInfo
+  )
+{
+  EFI_STATUS                Status;
+  INT32                     AddressCells;
+  CONST UINT8             * Data;
+  INT32                     DataSize;
+
+  if ((Fdt == NULL) ||
+      (GicItsInfo == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = FdtGetParentAddressInfo (Fdt, GicIntcNode, &AddressCells, NULL);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Don't support more than 64 bits and less than 32 bits addresses.
+  if ((AddressCells < 1)  ||
+      (AddressCells > 2)) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  Data = fdt_getprop (Fdt, GicIntcNode, "reg", &DataSize);
+  if ((Data == NULL) || (DataSize < (INT32)(AddressCells * sizeof (UINT32)))) {
+    // If error or not enough space.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  if (AddressCells == 2) {
+    GicItsInfo->PhysicalBaseAddress = fdt64_to_cpu (*(UINT64*)Data);
+  } else {
+    GicItsInfo->PhysicalBaseAddress = fdt32_to_cpu (*(UINT32*)Data);
+  }
+
+  // Gic Its Id
+  GicItsInfo->GicItsId = GicItsId;
+
+  // {default = 0}
+  GicItsInfo->ProximityDomain = 0;
+  return Status;
+}
+
+/** CM_ARM_GIC_ITS_INFO parser function.
+
+  This parser expects FdtBranch to be a Gic interrupt-controller node.
+  Gic version must be v3 or higher.
+  typedef struct CmArmGicItsInfo {
+    UINT32  GicItsId;                         // {Populated}
+    UINT64  PhysicalBaseAddress;              // {Populated}
+    UINT32  ProximityDomain;                  // {default = 0}
+  } CM_ARM_GIC_ITS_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicItsInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  )
+{
+  EFI_STATUS              Status;
+  UINT32                  GicVersion;
+  CM_ARM_GIC_ITS_INFO     GicItsInfo;
+  UINT32                  Index;
+  INT32                   GicItsNode;
+  UINT32                  GicItsNodeCount;
+  VOID                  * Fdt;
+
+  if (FdtParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Fdt = FdtParserHandle->Fdt;
+
+  if (!FdtNodeHasProperty (Fdt, FdtBranch, "interrupt-controller")) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Get the Gic version of the interrupt-controller.
+  Status = GetGicVersion (Fdt, FdtBranch, &GicVersion);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  if (GicVersion < 3) {
+    ASSERT (0);
+    return EFI_UNSUPPORTED;
+  }
+
+  // Count the nodes with the "msi-controller" property.
+  // The interrupt-controller itself can have this property,
+  // but the first node is skipped in the search.
+  Status = FdtCountPropNodeInBranch (
+             Fdt,
+             FdtBranch,
+             "msi-controller",
+             &GicItsNodeCount
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  if (GicItsNodeCount == 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  GicItsNode = FdtBranch;
+  for (Index = 0; Index < GicItsNodeCount; Index++) {
+    ZeroMem (&GicItsInfo, sizeof (CM_ARM_GIC_ITS_INFO));
+
+    Status = FdtGetNextPropNodeInBranch (
+               Fdt,
+               FdtBranch,
+               "msi-controller",
+               &GicItsNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      if (Status == EFI_NOT_FOUND) {
+        // Should have found the node.
+        Status = EFI_ABORTED;
+      }
+      return Status;
+    }
+
+    Status = GicItsIntcNodeParser (
+               Fdt,
+               GicItsNode,
+               Index,
+               &GicItsInfo
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    // Add the CmObj to the Configuration Manager.
+    Status = AddSingleCmObj (
+               FdtParserHandle,
+               CREATE_CM_ARM_OBJECT_ID (EArmObjGicItsInfo),
+               &GicItsInfo,
+               sizeof (CM_ARM_GIC_ITS_INFO),
+               NULL
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  } // for
+
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicItsParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicItsParser.h
new file mode 100644
index 000000000000..6ea498ff71c9
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicItsParser.h
@@ -0,0 +1,48 @@
+/** @file
+  Arm Gic Interrupt Translation Service Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#ifndef ARM_GIC_ITS_PARSER_H_
+#define ARM_GIC_ITS_PARSER_H_
+
+/** CM_ARM_GIC_ITS_INFO parser function.
+
+  This parser expects FdtBranch to be a Gic interrupt-controller node.
+  Gic version must be v3 or higher.
+  typedef struct CmArmGicItsInfo {
+    UINT32  GicItsId;                         // {Populated}
+    UINT64  PhysicalBaseAddress;              // {Populated}
+    UINT32  ProximityDomain;                  // {default = 0}
+  } CM_ARM_GIC_ITS_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicItsInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  );
+
+#endif // ARM_GIC_ITS_PARSER_H_
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v1 11/14] DynamicTablesPkg: FdtHwInfoParser: Add GICR parser
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
                   ` (9 preceding siblings ...)
  2021-06-23 12:38 ` [PATCH v1 10/14] DynamicTablesPkg: FdtHwInfoParser: Add ITS parser PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  2021-06-23 12:38 ` [PATCH v1 12/14] DynamicTablesPkg: FdtHwInfoParser: Add GIC dispatcher PierreGondois
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

The GIC Redistributor (GICR) structure is part of the Multiple
APIC Description Table (MADT) that enables the discovery of
GIC Redistributor base addresses by providing the Physical Base
Address of a page range containing the GIC Redistributors. More
than one GICR Structure may be presented in the MADT. The GICR
structures should only be used when describing GIC version 3 or
higher.

The GIC Redistributor information is described in the platform
Device Tree, the bindings for which can be found at:
 - linux/Documentation/devicetree/bindings/interrupt-controller/
   arm,gic-v3.yaml

The FdtHwInfoParser implements a GIC Redistributor Parser that
parses the platform Device Tree to create CM_ARM_GIC_REDIST_INFO
objects which are encapsulated in a Configuration Manager
descriptor object and added to the platform information
repository.

The platform Configuration Manager can then utilise this
information when generating the MADT table.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 .../FdtHwInfoParserLib/Gic/ArmGicRParser.c    | 237 ++++++++++++++++++
 .../FdtHwInfoParserLib/Gic/ArmGicRParser.h    |  47 ++++
 2 files changed, 284 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicRParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicRParser.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicRParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicRParser.c
new file mode 100644
index 000000000000..dba4667f722e
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicRParser.c
@@ -0,0 +1,237 @@
+/** @file
+  Arm Gic Redistributor Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#include "CmObjectDescUtility.h"
+#include "FdtHwInfoParser.h"
+#include "Gic/ArmGicDispatcher.h"
+#include "Gic/ArmGicRParser.h"
+
+/** Parse a Gic compatible interrupt-controller node,
+    extracting GicR information.
+
+  This parser is valid for Gic v3 and higher.
+
+  @param [in]  FdtParserHandle  A handle to the parser instance.
+  @param [in]  GicIntcNode      Offset of a Gic compatible
+                                interrupt-controller node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicRIntcNodeParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE   FdtParserHandle,
+  IN        INT32                       GicIntcNode
+  )
+{
+  EFI_STATUS                Status;
+  UINT32                    Index;
+  UINT32                    RedistReg;
+  UINT32                    RegSize;
+  INT32                     AddressCells;
+  INT32                     SizeCells;
+  CONST UINT8             * Data;
+  INT32                     DataSize;
+  CM_ARM_GIC_REDIST_INFO    GicRInfo;
+  VOID                    * Fdt;
+
+  if (FdtParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Fdt = FdtParserHandle->Fdt;
+
+  Status = FdtGetParentAddressInfo (
+             Fdt,
+             GicIntcNode,
+             &AddressCells,
+             &SizeCells
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Don't support more than 64 bits and less than 32 bits addresses.
+  if ((AddressCells < 1)  ||
+      (AddressCells > 2)  ||
+      (SizeCells < 1)     ||
+      (SizeCells > 2)) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  // The "#redistributor-regions" property is optional.
+  // It indicates the number of GicR.
+  Data = fdt_getprop (Fdt, GicIntcNode, "#redistributor-regions", &DataSize);
+  if ((Data != NULL) && (DataSize == sizeof (UINT32))) {
+    // If available, must be on one cell.
+    RedistReg = fdt32_to_cpu (*(UINT32*)Data);
+  } else {
+    // The DT Spec says GicR is mandatory so we will
+    // always have one.
+    RedistReg = 1;
+  }
+
+  /*
+    Ref: linux/blob/master/Documentation/devicetree/bindings/
+         interrupt-controller/arm%2Cgic-v3.yaml
+
+    reg:
+    description: |
+      Specifies base physical address(s) and size of the GIC
+      registers, in the following order:
+      - GIC Distributor interface (GICD)
+      - GIC Redistributors (GICR), one range per redistributor region
+      - GIC CPU interface (GICC)
+      - GIC Hypervisor interface (GICH)
+      - GIC Virtual CPU interface (GICV)
+      GICC, GICH and GICV are optional.
+    minItems: 2
+    maxItems: 4096
+
+    Example:
+      interrupt-controller@2c010000 {
+        compatible = "arm,gic-v3";
+        #interrupt-cells = <4>;
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges;
+        interrupt-controller;
+        redistributor-stride = <0x0 0x40000>;  // 256kB stride
+        #redistributor-regions = <2>;
+        reg = <0x2c010000 0x10000>,  // GICD
+              <0x2d000000 0x800000>,  // GICR 1: CPUs 0-31
+              <0x2e000000 0x800000>,  // GICR 2: CPUs 32-63
+              <0x2c040000 0x2000>,  // GICC
+              <0x2c060000 0x2000>,  // GICH
+              <0x2c080000 0x2000>;  // GICV
+        interrupts = <1 9 4>;
+        ...
+      }
+  */
+  RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
+  Data = fdt_getprop (Fdt, GicIntcNode, "reg", &DataSize);
+  if ((Data == NULL)  ||
+      (DataSize < 0)  ||
+      ((DataSize % RegSize) != 0)) {
+    // If error or wrong size.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  Data += GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, SizeCells)
+            * sizeof (UINT32);
+  for (Index = 0; Index < RedistReg; Index++) {
+    ZeroMem (&GicRInfo, sizeof (CM_ARM_GIC_REDIST_INFO));
+
+    if (AddressCells == 2) {
+      GicRInfo.DiscoveryRangeBaseAddress = fdt64_to_cpu (*(UINT64*)Data);
+    } else {
+      GicRInfo.DiscoveryRangeBaseAddress = fdt32_to_cpu (*(UINT32*)Data);
+    }
+    Data += sizeof (UINT32) * AddressCells;
+
+    if (SizeCells == 2) {
+      GicRInfo.DiscoveryRangeLength = (UINT32)fdt64_to_cpu (*(UINT64*)Data);
+    } else {
+      GicRInfo.DiscoveryRangeLength = fdt32_to_cpu (*(UINT32*)Data);
+    }
+
+    // Add the CmObj to the Configuration Manager.
+    Status = AddSingleCmObj (
+               FdtParserHandle,
+               CREATE_CM_ARM_OBJECT_ID (EArmObjGicRedistributorInfo),
+               &GicRInfo,
+               sizeof (CM_ARM_GIC_REDIST_INFO),
+               NULL
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+
+    Data += sizeof (UINT32) * SizeCells;
+  } // for
+
+  return Status;
+}
+
+/** CM_ARM_GIC_REDIST_INFO parser function.
+
+  This parser expects FdtBranch to be a Gic interrupt-controller node.
+  Gic version must be v3 or higher.
+  typedef struct CmArmGicRedistInfo {
+    UINT64  DiscoveryRangeBaseAddress;        // {Populated}
+    UINT32  DiscoveryRangeLength;             // {Populated}
+  } CM_ARM_GIC_REDIST_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicRInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  )
+{
+  EFI_STATUS                Status;
+  UINT32                    GicVersion;
+  VOID                    * Fdt;
+
+  if (FdtParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Fdt = FdtParserHandle->Fdt;
+
+  if (!FdtNodeHasProperty (Fdt, FdtBranch, "interrupt-controller")) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Get the Gic version of the interrupt-controller.
+  Status = GetGicVersion (Fdt, FdtBranch, &GicVersion);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  if (GicVersion < 3) {
+    ASSERT (0);
+    return EFI_UNSUPPORTED;
+  }
+
+  Status = GicRIntcNodeParser (FdtParserHandle, FdtBranch);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicRParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicRParser.h
new file mode 100644
index 000000000000..ef91c2d32cd9
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicRParser.h
@@ -0,0 +1,47 @@
+/** @file
+  Arm Gic Redistributor Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#ifndef ARM_GICR_PARSER_H_
+#define ARM_GICR_PARSER_H_
+
+/** CM_ARM_GIC_REDIST_INFO parser function.
+
+  This parser expects FdtBranch to be a Gic interrupt-controller node.
+  Gic version must be v3 or higher.
+  typedef struct CmArmGicRedistInfo {
+    UINT64  DiscoveryRangeBaseAddress;        // {Populated}
+    UINT32  DiscoveryRangeLength;             // {Populated}
+  } CM_ARM_GIC_REDIST_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicRInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  );
+
+#endif // ARM_GICR_PARSER_H_
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v1 12/14] DynamicTablesPkg: FdtHwInfoParser: Add GIC dispatcher
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
                   ` (10 preceding siblings ...)
  2021-06-23 12:38 ` [PATCH v1 11/14] DynamicTablesPkg: FdtHwInfoParser: Add GICR parser PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  2021-06-23 12:38 ` [PATCH v1 13/14] DynamicTablesPkg: FdtHwInfoParser: Add PCI config parser PierreGondois
  2021-06-23 12:38 ` [PATCH v1 14/14] DynamicTablesPkg: Add FdtHwInfoParser library PierreGondois
  13 siblings, 0 replies; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

The GIC Dispatcher is the top-level component that is responsible
for invoking the respective parsers for GICC, GICD, GIC MSI Frame,
GIC ITS and the GICR.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 .../FdtHwInfoParserLib/Gic/ArmGicDispatcher.c | 212 ++++++++++++++++++
 .../FdtHwInfoParserLib/Gic/ArmGicDispatcher.h |  72 ++++++
 2 files changed, 284 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDispatcher.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDispatcher.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDispatcher.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDispatcher.c
new file mode 100644
index 000000000000..3e5d7fb0cba6
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDispatcher.c
@@ -0,0 +1,212 @@
+/** @file
+  Arm Gic dispatcher.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#include "FdtHwInfoParser.h"
+#include "Gic/ArmGicCParser.h"
+#include "Gic/ArmGicDispatcher.h"
+#include "Gic/ArmGicDParser.h"
+#include "Gic/ArmGicItsParser.h"
+#include "Gic/ArmGicMsiFrameParser.h"
+#include "Gic/ArmGicRParser.h"
+
+/** List of "compatible" property values for GicV2 interrupt nodes.
+
+  Any other "compatible" value is not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR GicV2CompatibleStr[] = {
+  {"arm,cortex-a15-gic"}
+};
+
+/** COMPATIBILITY_INFO structure for the GICv2.
+*/
+CONST COMPATIBILITY_INFO GicV2CompatibleInfo = {
+  ARRAY_SIZE (GicV2CompatibleStr),
+  GicV2CompatibleStr
+};
+
+/** List of "compatible" property values for GicV3 interrupt nodes.
+
+  Any other "compatible" value is not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR GicV3CompatibleStr[] = {
+  {"arm,gic-v3"}
+};
+
+/** COMPATIBILITY_INFO structure for the GICv3.
+*/
+CONST COMPATIBILITY_INFO GicV3CompatibleInfo = {
+  ARRAY_SIZE (GicV3CompatibleStr),
+  GicV3CompatibleStr
+};
+
+/** Get the Gic version of am interrupt-controller node.
+
+  @param [in]  Fdt          Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  IntcNode     Interrupt-controller node.
+  @param [out] GicVersion   If success, contains the Gic version of the
+                            interrupt-controller node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+GetGicVersion (
+  IN  CONST VOID    * Fdt,
+  IN        INT32     IntcNode,
+  OUT       UINT32  * GicVersion
+  )
+{
+  if ((Fdt == NULL) ||
+      (GicVersion == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (FdtNodeIsCompatible (Fdt, IntcNode, &GicV2CompatibleInfo)) {
+    *GicVersion = 2;
+  } else if (FdtNodeIsCompatible (Fdt, IntcNode, &GicV3CompatibleInfo)) {
+    *GicVersion = 3;
+  } else {
+    // Unsupported Gic version.
+    ASSERT (0);
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** Gic dispatcher.
+
+  This disptacher populates the following structures:
+   - CM_ARM_GICC_INFO
+   - CM_ARM_GICD_INFO
+   - CM_ARM_GIC_MSI_FRAME_INFO
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicDispatcher (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  )
+{
+  EFI_STATUS    Status;
+  INT32         CpusNode;
+  INT32         IntcNode;
+  UINT32        GicVersion;
+  VOID        * Fdt;
+
+  if (FdtParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Fdt = FdtParserHandle->Fdt;
+
+  // The "cpus" node resides at the the root of the DT. Fetch it.
+  CpusNode = fdt_path_offset (Fdt, "/cpus");
+  if (CpusNode < 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  // Get the interrupt-controller node associated to the "cpus" node.
+  Status = FdtGetIntcParentNode (Fdt, CpusNode, &IntcNode);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    if (Status == EFI_NOT_FOUND) {
+      // Should have found the node.
+      Status = EFI_ABORTED;
+    }
+    return Status;
+  }
+
+  Status = GetGicVersion (Fdt, IntcNode, &GicVersion);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Parse the GicC information.
+  Status = ArmGicCInfoParser (FdtParserHandle, CpusNode);
+  if (EFI_ERROR (Status)) {
+    // Don't try to parse GicD and GicMsiFrame information
+    // if no GicC information is found. Return.
+    ASSERT (Status == EFI_NOT_FOUND);
+    return Status;
+  }
+
+  // Parse the GicD information of the "cpus" interrupt-controller node.
+  Status = ArmGicDInfoParser (FdtParserHandle, IntcNode);
+  if (EFI_ERROR (Status)) {
+    // EFI_NOT_FOUND is not tolerated at this point.
+    ASSERT (0);
+    return Status;
+  }
+
+  switch (GicVersion) {
+    case 4:
+    case 3:
+    {
+      // Parse the GicR information of the interrupt-controller node.
+      Status = ArmGicRInfoParser (FdtParserHandle, IntcNode);
+      if (EFI_ERROR (Status)) {
+        // EFI_NOT_FOUND is not tolerated at this point.
+        ASSERT (0);
+        return Status;
+      }
+
+      // Parse the GicIts information of the interrupt-controller node.
+      Status = ArmGicItsInfoParser (FdtParserHandle, IntcNode);
+      if (EFI_ERROR (Status)  &&
+          (Status != EFI_NOT_FOUND)) {
+        ASSERT (0);
+        return Status;
+      }
+      break;
+    }
+    case 2:
+    {
+      // Parse the GicMsiFrame information.
+      Status = ArmGicMsiFrameInfoParser (FdtParserHandle, IntcNode);
+      if (EFI_ERROR (Status)  &&
+          (Status != EFI_NOT_FOUND)) {
+        ASSERT (0);
+        return Status;
+      }
+      break;
+    }
+    default:
+    {
+      ASSERT (0);
+      return EFI_UNSUPPORTED;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDispatcher.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDispatcher.h
new file mode 100644
index 000000000000..a0671cea8a67
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicDispatcher.h
@@ -0,0 +1,72 @@
+/** @file
+  Arm Gic dispatcher.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#ifndef ARM_GIC_DISPATCHER_H_
+#define ARM_GIC_DISPATCHER_H_
+
+#include <FdtHwInfoParserInclude.h>
+#include "FdtUtility.h"
+
+/** COMPATIBILITY_INFO structure for the GICv2.
+*/
+extern CONST COMPATIBILITY_INFO GicV2CompatibleInfo;
+
+/** Get the Gic version of the interrupt-controller node.
+
+  @param [in]  Fdt          Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  IntcNode     Interrupt-controller node.
+  @param [out] GicVersion   If success, contains the Gic version of the
+                            interrupt-controller node.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+GetGicVersion (
+  IN  CONST VOID    * Fdt,
+  IN        INT32     IntcNode,
+  OUT       UINT32  * GicVersion
+  );
+
+/** Gic dispatcher.
+
+  This disptacher populates the following structures:
+   - CM_ARM_GICC_INFO
+   - CM_ARM_GICD_INFO
+   - CM_ARM_GIC_MSI_FRAME_INFO
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicDispatcher (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  );
+
+#endif // ARM_GIC_DISPATCHER_H_
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v1 13/14] DynamicTablesPkg: FdtHwInfoParser: Add PCI config parser
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
                   ` (11 preceding siblings ...)
  2021-06-23 12:38 ` [PATCH v1 12/14] DynamicTablesPkg: FdtHwInfoParser: Add GIC dispatcher PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  2021-06-23 12:38 ` [PATCH v1 14/14] DynamicTablesPkg: Add FdtHwInfoParser library PierreGondois
  13 siblings, 0 replies; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

On platforms that implement PCIe, the PCIe configuration space
information must be described to a standards-based operating
system in the Memory mapped configuration space base address
Description (MCFG) table.

The PCIe information is described in the platform Device Tree,
the bindings for which can be found at:
- linux/Documentation/devicetree/bindings/pci/
  host-generic-pci.yaml

The FdtHwInfoParser implements a PCI configuration space Parser
that parses the platform Device Tree to create
CM_ARM_PCI_CONFIG_SPACE_INFO objects which are encapsulated in a
Configuration Manager descriptor object and added to the platform
information repository.

The platform Configuration Manager can then utilise this
information when generating the MCFG table.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 .../Pci/ArmPciConfigSpaceParser.c             | 799 ++++++++++++++++++
 .../Pci/ArmPciConfigSpaceParser.h             | 142 ++++
 2 files changed, 941 insertions(+)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h

diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c
new file mode 100644
index 000000000000..2b1b0749b903
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.c
@@ -0,0 +1,799 @@
+/** @file
+  Arm PCI Configuration Space Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/pci/host-generic-pci.yaml
+  - PCI Firmware Specification - Revision 3.0
+  - Open Firmware Recommended Practice: Interrupt Mapping, Version 0.9
+  - Devicetree Specification Release v0.3
+  - linux kernel code
+**/
+
+#include "CmObjectDescUtility.h"
+#include <Library/DebugLib.h>
+
+#include "FdtHwInfoParser.h"
+#include "Pci/ArmPciConfigSpaceParser.h"
+#include "Gic/ArmGicDispatcher.h"
+
+/** List of "compatible" property values for host PCIe bridges nodes.
+
+  Any other "compatible" value is not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR PciCompatibleStr[] = {
+  {"pci-host-ecam-generic"}
+};
+
+/** COMPATIBILITY_INFO structure for the PCIe.
+*/
+STATIC CONST COMPATIBILITY_INFO PciCompatibleInfo = {
+  ARRAY_SIZE (PciCompatibleStr),
+  PciCompatibleStr
+};
+
+/** Get the Segment group (also called: Domain Id) of a host-pci node.
+
+  kernel/Documentation/devicetree/bindings/pci/pci.txt:
+  "It is required to either not set this property at all or set it for all
+  host bridges in the system"
+
+  The function checks the "linux,pci-domain" property of the host-pci node.
+  Either all host-pci nodes must have this property, or none of them. If the
+  property is available, read it. Otherwise dynamically assign the Ids.
+
+  @param [in]  Fdt          Pointer to a Flattened Device Tree (Fdt).
+  @param [in]  HostPciNode  Offset of a host-pci node.
+  @param [out] SegGroup     Segment group assigned to the host-pci controller.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetPciSegGroup (
+  IN  CONST VOID   * Fdt,
+  IN        INT32    HostPciNode,
+  OUT       INT32  * SegGroup
+  )
+{
+  CONST UINT8   * Data;
+  INT32           DataSize;
+  STATIC INT32    LocalSegGroup = 0;
+
+  if ((Fdt == NULL) ||
+      (SegGroup == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Data = fdt_getprop (Fdt, HostPciNode, "linux,pci-domain", &DataSize);
+  if ((Data == NULL) || (DataSize < 0)) {
+    // Did not find property, assign the DomainIds ourselves.
+    if (LocalSegGroup < 0) {
+      // "linux,pci-domain" property was defined for another node.
+      ASSERT (0);
+      return EFI_ABORTED;
+    }
+
+    *SegGroup = LocalSegGroup++;
+    return EFI_SUCCESS;
+  }
+
+  if ((DataSize > sizeof (UINT32))  ||
+      (LocalSegGroup > 0)) {
+    // Property on more than 1 cell or
+    // "linux,pci-domain" property was not defined for a node.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  // If one node has the "linux,pci-domain" property, then all the host-pci
+  // nodes must have it.
+  LocalSegGroup = -1;
+
+  *SegGroup = fdt32_to_cpu (*(UINT32*)Data);
+  return EFI_SUCCESS;
+}
+
+/** Parse the bus-range controlled by this host-pci node.
+
+  @param [in]       Fdt           Pointer to a Flattened Device Tree (Fdt).
+  @param [in]       HostPciNode   Offset of a host-pci node.
+  @param [in, out]  PciInfo       PCI_PARSER_TABLE structure storing
+                                  information about the current host-pci.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+PopulateBusRange (
+  IN      CONST VOID                * Fdt,
+  IN            INT32                 HostPciNode,
+  IN OUT        PCI_PARSER_TABLE    * PciInfo
+  )
+{
+  CONST UINT8   * Data;
+  INT32           DataSize;
+  UINT32          StartBus;
+  UINT32          EndBus;
+
+  if ((Fdt == NULL) ||
+      (PciInfo == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Data = fdt_getprop (Fdt, HostPciNode, "bus-range", &DataSize);
+  if ((Data == NULL) || (DataSize < 0)) {
+    // No evidence this property is mandatory. Use default values.
+    StartBus = 0;
+    EndBus = 255;
+  } else if (DataSize == (2 * sizeof (UINT32))) {
+    // If available, the property is on two integers.
+    StartBus = fdt32_to_cpu (((UINT32*)Data)[0]);
+    EndBus = fdt32_to_cpu (((UINT32*)Data)[1]);
+  } else {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  PciInfo->PciConfigSpaceInfo.StartBusNumber = StartBus;
+  PciInfo->PciConfigSpaceInfo.EndBusNumber = EndBus;
+
+  return EFI_SUCCESS;
+}
+
+/** Parse the PCI address map.
+
+  The PCI address map is available in the "ranges" device-tree property.
+
+  @param [in]       Fdt           Pointer to a Flattened Device Tree (Fdt).
+  @param [in]       HostPciNode   Offset of a host-pci node.
+  @param [in]       AddressCells  # of cells used to encode an address on
+                                  the parent bus.
+  @param [in, out]  PciInfo       PCI_PARSER_TABLE structure storing
+                                  information about the current host-pci.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ParseAddressMap (
+  IN      CONST VOID                * Fdt,
+  IN            INT32                 HostPciNode,
+  IN            INT32                 AddressCells,
+  IN OUT        PCI_PARSER_TABLE    * PciInfo
+  )
+{
+  CONST UINT8   * Data;
+  INT32           DataSize;
+  UINT32          Index;
+  UINT32          Offset;
+  UINT32          AddressMapSize;
+  UINT32          Count;
+  UINT32          PciAddressAttr;
+
+  CM_ARM_PCI_ADDRESS_MAP_INFO   * PciAddressMapInfo;
+  UINT32                          BufferSize;
+
+  // The mapping is done on AddressMapSize bytes.
+  AddressMapSize = (PCI_ADDRESS_CELLS + AddressCells + PCI_SIZE_CELLS) *
+    sizeof (UINT32);
+
+  Data = fdt_getprop (Fdt, HostPciNode, "ranges", &DataSize);
+  if ((Data == NULL) ||
+      (DataSize < 0) ||
+      ((DataSize % AddressMapSize) != 0)) {
+    // If error or not on AddressMapSize bytes.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  Count = DataSize / AddressMapSize;
+
+  // Allocate a buffer to store each address mapping.
+  BufferSize = Count * sizeof (CM_ARM_PCI_ADDRESS_MAP_INFO);
+  PciAddressMapInfo = AllocateZeroPool (BufferSize);
+  if (PciAddressMapInfo == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  for (Index = 0; Index < Count; Index++) {
+    Offset = Index * AddressMapSize;
+
+    // Pci address attributes
+    PciAddressAttr = fdt32_to_cpu (*(UINT32*)&Data[Offset]);
+    PciAddressMapInfo[Index].SpaceCode = READ_PCI_SS (PciAddressAttr);
+    Offset += sizeof (UINT32);
+
+    // Pci address
+    PciAddressMapInfo[Index].PciAddress =
+      fdt64_to_cpu (*(UINT64*)&Data[Offset]);
+    Offset += (PCI_ADDRESS_CELLS - 1) * sizeof (UINT32);
+
+    // Cpu address
+    if (AddressCells == 2) {
+      PciAddressMapInfo[Index].CpuAddress =
+        fdt64_to_cpu (*(UINT64*)&Data[Offset]);
+    } else {
+      PciAddressMapInfo[Index].CpuAddress =
+        fdt32_to_cpu (*(UINT32*)&Data[Offset]);
+    }
+    Offset += AddressCells * sizeof (UINT32);
+
+    // Address size
+    PciAddressMapInfo[Index].AddressSize =
+      fdt64_to_cpu (*(UINT64*)&Data[Offset]);
+    Offset += PCI_SIZE_CELLS * sizeof (UINT32);
+  } // for
+
+  PciInfo->Mapping[PciMappingTableAddress].ObjectId =
+    CREATE_CM_ARM_OBJECT_ID (EArmObjPciAddressMapInfo);
+  PciInfo->Mapping[PciMappingTableAddress].Size =
+    sizeof (CM_ARM_PCI_ADDRESS_MAP_INFO) * Count;
+  PciInfo->Mapping[PciMappingTableAddress].Data = PciAddressMapInfo;
+  PciInfo->Mapping[PciMappingTableAddress].Count = Count;
+
+  return EFI_SUCCESS;
+}
+
+/** Parse the PCI interrupt map.
+
+  The PCI interrupt map is available in the "interrupt-map"
+  and "interrupt-map-mask" device-tree properties.
+
+  Cf Devicetree Specification Release v0.3,
+  s2.4.3 Interrupt Nexus Properties
+
+  An interrupt-map must be as:
+  interrupt-map = < [child unit address] [child interrupt specifier]
+                    [interrupt-parent]
+                    [parent unit address] [parent interrupt specifier] >
+
+  @param [in]       Fdt           Pointer to a Flattened Device Tree (Fdt).
+  @param [in]       HostPciNode   Offset of a host-pci node.
+  @param [in, out]  PciInfo       PCI_PARSER_TABLE structure storing
+                                  information about the current host-pci.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ParseIrqMap (
+  IN      CONST VOID                * Fdt,
+  IN            INT32                 HostPciNode,
+  IN OUT        PCI_PARSER_TABLE    * PciInfo
+  )
+{
+  EFI_STATUS      Status;
+  CONST UINT8   * Data;
+  INT32           DataSize;
+  UINT32          Index;
+  UINT32          Offset;
+
+  INT32           IntcNode;
+  INT32           IntcAddressCells;
+  INT32           IntcCells;
+
+  INT32           PciIntCells;
+  INT32           IntcPhandle;
+
+  INT32           IrqMapSize;
+  UINT32          IrqMapCount;
+  CONST UINT8   * IrqMapMask;
+  INT32           IrqMapMaskSize;
+
+  INT32           PHandleOffset;
+  UINT32          GicVersion;
+
+  UINT32          PciAddressAttr;
+
+
+  CM_ARM_PCI_INTERRUPT_MAP_INFO * PciInterruptMapInfo;
+  UINT32                          BufferSize;
+
+  Data = fdt_getprop (Fdt, HostPciNode, "interrupt-map", &DataSize);
+  if ((Data == NULL) || (DataSize < 0)) {
+    // We cannot check the # for now
+    ASSERT (0);
+    return EFI_ABORTED;
+  } else if (DataSize == 0) {
+    // No device described, so no interrupt-mapping.
+    DEBUG ((
+      DEBUG_WARN,
+      "Fdt parser: No Pci device described in the device tree.\n"
+      ));
+    return EFI_SUCCESS;
+  }
+
+  // PCI interrupts are expected to be on 1 cell. Check it.
+  Status = FdtGetInterruptCellsInfo (Fdt, HostPciNode, &PciIntCells);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+  if (PciIntCells != PCI_INTERRUPTS_CELLS) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  IrqMapMask = fdt_getprop (
+                 Fdt,
+                 HostPciNode,
+                 "interrupt-map-mask",
+                 &IrqMapMaskSize
+                 );
+  if ((IrqMapMask == NULL) ||
+      (IrqMapMaskSize !=
+        (PCI_ADDRESS_CELLS + PCI_INTERRUPTS_CELLS) * sizeof (UINT32))) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  // Get the interrupt-controller of the first irq mapping.
+  PHandleOffset = (PCI_ADDRESS_CELLS + PciIntCells) * sizeof (UINT32);
+  if (PHandleOffset > DataSize) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+  IntcPhandle = fdt32_to_cpu (*(UINT32*)&Data[PHandleOffset]);
+  IntcNode = fdt_node_offset_by_phandle (Fdt, IntcPhandle);
+  if (IntcNode < 0) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  // Only support Gic(s) for now.
+  Status = GetGicVersion (Fdt, IntcNode, &GicVersion);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Get the "address-cells" property of the IntcNode.
+  Status = FdtGetAddressInfo (Fdt, IntcNode, &IntcAddressCells, NULL);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Get the "interrupt-cells" property of the IntcNode.
+  Status = FdtGetInterruptCellsInfo (Fdt, IntcNode, &IntcCells);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // An irq mapping is done on IrqMapSize bytes
+  // (which includes 1 cell for the PHandle).
+  IrqMapSize = (PCI_ADDRESS_CELLS + PciIntCells + 1
+                + IntcAddressCells + IntcCells) * sizeof (UINT32);
+  if ((DataSize % IrqMapSize) != 0) {
+    // The mapping is not done on IrqMapSize bytes.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+  IrqMapCount = DataSize / IrqMapSize;
+
+  // We assume the same interrupt-controller is used for all the mappings.
+  // Check this is correct.
+  for (Index = 0; Index < IrqMapCount; Index++) {
+    if (IntcPhandle != fdt32_to_cpu (
+          *(UINT32*)&Data[(Index * IrqMapSize) + PHandleOffset])) {
+      ASSERT (0);
+      return EFI_ABORTED;
+    }
+  }
+
+  // Allocate a buffer to store each interrupt mapping.
+  IrqMapCount = DataSize / IrqMapSize;
+  BufferSize = IrqMapCount * sizeof (CM_ARM_PCI_ADDRESS_MAP_INFO);
+  PciInterruptMapInfo = AllocateZeroPool (BufferSize);
+  if (PciInterruptMapInfo == NULL) {
+    ASSERT (0);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  for (Index = 0; Index < IrqMapCount; Index++) {
+    Offset = Index * IrqMapSize;
+
+    // Pci address attributes
+    PciAddressAttr = fdt32_to_cpu (
+                       (*(UINT32*)&Data[Offset]) &
+                       (*(UINT32*)&IrqMapMask[0])
+                       );
+    PciInterruptMapInfo[Index].PciBus = READ_PCI_BBBBBBBB (PciAddressAttr);
+    PciInterruptMapInfo[Index].PciDevice = READ_PCI_DDDDD (PciAddressAttr);
+    Offset += PCI_ADDRESS_CELLS * sizeof (UINT32);
+
+    // Pci irq
+    PciInterruptMapInfo[Index].PciInterrupt = fdt32_to_cpu (
+                                   (*(UINT32*)&Data[Offset]) &
+                                   (*(UINT32*)&IrqMapMask[3 * sizeof (UINT32)])
+                                   );
+    // -1 to translate from device-tree (INTA=1) to ACPI (INTA=0) irq IDs.
+    PciInterruptMapInfo[Index].PciInterrupt -= 1;
+    Offset += PCI_INTERRUPTS_CELLS * sizeof (UINT32);
+
+    // PHandle (skip it)
+    Offset += sizeof (UINT32);
+
+    // "Parent unit address" (skip it)
+    Offset += IntcAddressCells * sizeof (UINT32);
+
+    // Interrupt controller interrupt and flags
+    PciInterruptMapInfo[Index].IntcInterrupt.Interrupt =
+      FdtGetInterruptId ((UINT32*)&Data[Offset]);
+    PciInterruptMapInfo[Index].IntcInterrupt.Flags =
+      FdtGetInterruptFlags ((UINT32*)&Data[Offset]);
+  } // for
+
+  PciInfo->Mapping[PciMappingTableInterrupt].ObjectId =
+    CREATE_CM_ARM_OBJECT_ID (EArmObjPciInterruptMapInfo);
+  PciInfo->Mapping[PciMappingTableInterrupt].Size =
+    sizeof (CM_ARM_PCI_INTERRUPT_MAP_INFO) * IrqMapCount;
+  PciInfo->Mapping[PciMappingTableInterrupt].Data = PciInterruptMapInfo;
+  PciInfo->Mapping[PciMappingTableInterrupt].Count = IrqMapCount;
+
+  return Status;
+}
+
+/** Parse a Host-pci node.
+
+  @param [in]       Fdt          Pointer to a Flattened Device Tree (Fdt).
+  @param [in]       HostPciNode  Offset of a host-pci node.
+  @param [in, out]  PciInfo      The CM_ARM_PCI_CONFIG_SPACE_INFO to populate.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+PciNodeParser (
+  IN      CONST VOID                * Fdt,
+  IN            INT32                 HostPciNode,
+  IN OUT        PCI_PARSER_TABLE    * PciInfo
+  )
+{
+  EFI_STATUS      Status;
+  INT32           AddressCells;
+  INT32           SizeCells;
+  CONST UINT8   * Data;
+  INT32           DataSize;
+  INT32           SegGroup;
+
+  if ((Fdt == NULL) ||
+      (PciInfo == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Segment Group / DomainId
+  Status = GetPciSegGroup (Fdt, HostPciNode, &SegGroup);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+  PciInfo->PciConfigSpaceInfo.PciSegmentGroupNumber = SegGroup;
+
+  // Bus range
+  Status = PopulateBusRange (Fdt, HostPciNode, PciInfo);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  Status = FdtGetParentAddressInfo (
+             Fdt,
+             HostPciNode,
+             &AddressCells,
+             &SizeCells
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Only support 32/64 bits addresses.
+  if ((AddressCells < 1)  ||
+      (AddressCells > 2)  ||
+      (SizeCells < 1)     ||
+      (SizeCells > 2)) {
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  Data = fdt_getprop (Fdt, HostPciNode, "reg", &DataSize);
+  if ((Data == NULL) ||
+      (DataSize != ((AddressCells + SizeCells) * sizeof (UINT32)))) {
+    // If error or wrong size.
+    ASSERT (0);
+    return EFI_ABORTED;
+  }
+
+  // Base address
+  if (AddressCells == 2) {
+    PciInfo->PciConfigSpaceInfo.BaseAddress = fdt64_to_cpu (*(UINT64*)Data);
+  } else {
+    PciInfo->PciConfigSpaceInfo.BaseAddress = fdt32_to_cpu (*(UINT32*)Data);
+  }
+
+  // Address map
+  Status = ParseAddressMap (
+             Fdt,
+             HostPciNode,
+             AddressCells,
+             PciInfo
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Irq map
+  Status = ParseIrqMap (
+      Fdt,
+      HostPciNode,
+      PciInfo
+      );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+
+  return Status;
+}
+
+/** Add the parsed Pci information to the Configuration Manager.
+
+  CmObj of the following types are concerned:
+   - EArmObjPciConfigSpaceInfo
+   - EArmObjPciAddressMapInfo
+   - EArmObjPciInterruptMapInfo
+
+  @param [in]  FdtParserHandle  A handle to the parser instance.
+  @param [in]  PciTableInfo     PCI_PARSER_TABLE structure containing the
+                                CmObjs to add.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+PciInfoAdd (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE   FdtParserHandle,
+  IN        PCI_PARSER_TABLE            *PciTableInfo
+  )
+{
+  EFI_STATUS                      Status;
+  CM_ARM_PCI_CONFIG_SPACE_INFO    *PciConfigSpaceInfo;
+
+  if ((FdtParserHandle == NULL) ||
+      (PciTableInfo == NULL)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  PciConfigSpaceInfo = &PciTableInfo->PciConfigSpaceInfo;
+
+  // Add the address map space CmObj to the Configuration Manager.
+  Status = AddMultipleCmObjWithCmObjRef (
+             FdtParserHandle,
+             &PciTableInfo->Mapping[PciMappingTableAddress],
+             &PciConfigSpaceInfo->AddressMapToken
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  // Add the interrupt map space CmObj to the Configuration Manager.
+  // Possible to have no device described, and thus no interrupt-mapping.
+  if (PciTableInfo->Mapping[PciMappingTableInterrupt].Count != 0) {
+    Status = AddMultipleCmObjWithCmObjRef (
+               FdtParserHandle,
+               &PciTableInfo->Mapping[PciMappingTableInterrupt],
+               &PciConfigSpaceInfo->InterruptMapToken
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  }
+
+  // Add the configuration space CmObj to the Configuration Manager.
+  Status = AddSingleCmObj (
+             FdtParserHandle,
+             CREATE_CM_ARM_OBJECT_ID (EArmObjPciConfigSpaceInfo),
+             &PciTableInfo->PciConfigSpaceInfo,
+             sizeof (CM_ARM_PCI_CONFIG_SPACE_INFO),
+             NULL
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+  return Status;
+}
+
+/** Free the CmObjDesc of the ParserTable.
+
+  @param [in]  PciTableInfo     PCI_PARSER_TABLE structure containing the
+                                CmObjs to free.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FreeParserTable (
+  IN  PCI_PARSER_TABLE    *PciTableInfo
+  )
+{
+  UINT32       Index;
+  VOID         *Data;
+
+  if (PciTableInfo == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  for (Index = 0; Index < PciMappingTableMax; Index++) {
+    Data = PciTableInfo->Mapping[Index].Data;
+    if (Data != NULL) {
+      FreePool (Data);
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/** CM_ARM_PCI_CONFIG_SPACE_INFO parser function.
+
+  The following structure is populated:
+  typedef struct CmArmPciConfigSpaceInfo {
+    UINT64  BaseAddress;                          // {Populated}
+    UINT16  PciSegmentGroupNumber;                // {Populated}
+    UINT8   StartBusNumber;                       // {Populated}
+    UINT8   EndBusNumber;                         // {Populated}
+  } CM_ARM_PCI_CONFIG_SPACE_INFO;
+
+  typedef struct CmArmPciAddressMapInfo {
+    UINT8                     SpaceCode;          // {Populated}
+    UINT64                    PciAddress;         // {Populated}
+    UINT64                    CpuAddress;         // {Populated}
+    UINT64                    AddressSize;        // {Populated}
+  } CM_ARM_PCI_ADDRESS_MAP_INFO;
+
+  typedef struct CmArmPciInterruptMapInfo {
+    UINT8                       PciBus;           // {Populated}
+    UINT8                       PciDevice;        // {Populated}
+    UINT8                       PciInterrupt;     // {Populated}
+    CM_ARM_GENERIC_INTERRUPT    IntcInterrupt;    // {Populated}
+  } CM_ARM_PCI_INTERRUPT_MAP_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmPciConfigInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  )
+{
+  EFI_STATUS          Status;
+  UINT32              Index;
+  INT32               PciNode;
+  UINT32              PciNodeCount;
+  PCI_PARSER_TABLE    PciTableInfo;
+  VOID                *Fdt;
+
+  if (FdtParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Fdt = FdtParserHandle->Fdt;
+
+  // Only search host-pci devices.
+  // PCI Firmware Specification Revision 3.0, s4.1.2. "MCFG Table Description":
+  // "This table directly refers to PCI Segment Groups defined in the system
+  // via the _SEG object in the ACPI name space for the applicable host bridge
+  // device."
+  Status = FdtCountCompatNodeInBranch (
+             Fdt,
+             FdtBranch,
+             &PciCompatibleInfo,
+             &PciNodeCount
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return Status;
+  }
+
+  if (PciNodeCount == 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  // Parse each host-pci node in the branch.
+  PciNode = FdtBranch;
+  for (Index = 0; Index < PciNodeCount; Index++) {
+    ZeroMem (&PciTableInfo, sizeof (PCI_PARSER_TABLE));
+
+    Status = FdtGetNextCompatNodeInBranch (
+               Fdt,
+               FdtBranch,
+               &PciCompatibleInfo,
+               &PciNode
+               );
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      if (Status == EFI_NOT_FOUND) {
+        // Should have found the node.
+        Status = EFI_ABORTED;
+      }
+      return Status;
+    }
+
+    Status = PciNodeParser (Fdt, PciNode, &PciTableInfo);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      goto error_handler;
+    }
+
+    // Add Pci information to the Configuration Manager.
+    Status = PciInfoAdd (FdtParserHandle, &PciTableInfo);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      goto error_handler;
+    }
+
+    Status = FreeParserTable (&PciTableInfo);
+    if (EFI_ERROR (Status)) {
+      ASSERT (0);
+      return Status;
+    }
+  } // for
+
+  return Status;
+
+error_handler:
+  FreeParserTable (&PciTableInfo);
+  return Status;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h
new file mode 100644
index 000000000000..b260c53a82ab
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Pci/ArmPciConfigSpaceParser.h
@@ -0,0 +1,142 @@
+/** @file
+  Arm PCI Configuration Space Parser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Reference(s):
+  - linux/Documentation/devicetree/bindings/pci/host-generic-pci.yaml
+  - PCI Firmware Specification - Revision 3.0
+  - Open Firmware Recommended Practice: Interrupt Mapping, Version 0.9
+  - Devicetree Specification Release v0.3
+  - linux kernel code
+**/
+
+#ifndef ARM_PCI_CONFIG_SPACE_PARSER_H_
+#define ARM_PCI_CONFIG_SPACE_PARSER_H_
+
+/** Read LEN bits at OFF offsets bits of the ADDR.
+
+  @param [in] ADDR  Address to read the bits from.
+  @param [in] OFF   Offset of the bits to read.
+  @param [in] LEN   Number of bits to read.
+
+  @return The bits read.
+**/
+#define READ_BITS(ADDR, OFF, LEN)    (((ADDR) >> (OFF)) & ((1<<(LEN))-1))
+
+/* Pci address attributes.
+*/
+/// 0 if relocatable.
+#define READ_PCI_N(ADDR)          READ_BITS((ADDR), 31, 1)
+/// 1 if prefetchable.
+#define READ_PCI_P(ADDR)          READ_BITS((ADDR), 30, 1)
+/// 1 if aliased.
+#define READ_PCI_T(ADDR)          READ_BITS((ADDR), 29, 1)
+/** Space code.
+
+  00: Configuration Space
+  01: I/O Space
+  10: 32-bit-address Memory Space
+  11: 64-bit-address Memory Space
+*/
+#define READ_PCI_SS(ADDR)         READ_BITS((ADDR), 24, 2)
+/// Bus number.
+#define READ_PCI_BBBBBBBB(ADDR)   READ_BITS((ADDR), 16, 8)
+/// Device number.
+#define READ_PCI_DDDDD(ADDR)      READ_BITS((ADDR), 11, 5)
+
+/** Number of device-tree cells used for PCI nodes properties.
+
+  Values are well defined, except the "#interrupt-cells" which
+  is assumed to be 1.
+*/
+#define PCI_ADDRESS_CELLS         3U
+#define PCI_SIZE_CELLS            2U
+#define PCI_INTERRUPTS_CELLS      1U
+
+/** PCI interrupt flags for device-tree.
+
+  Local Bus Specification Revision 3.0, s2.2.6., Interrupt Pins:
+   - 'Interrupts on PCI are optional and defined as "level sensitive,"
+      asserted low (negative true)'
+*/
+#define DT_PCI_IRQ_FLAGS(x)        (((x) & 0xF) == BIT0)
+
+/** Indexes in the mapping table.
+*/
+typedef enum PciMappingTable {
+  PciMappingTableAddress,           ///<  0 - Address mapping
+  PciMappingTableInterrupt,         ///<  1 - Interrupt mapping
+  PciMappingTableMax,               ///<  2 - Max
+} PCI_MAPPING_TABLE;
+
+#pragma pack(1)
+
+/** PCI parser table
+
+  Multiple address-map and interrupt map can correspond to
+  one host-pci device. This structure allows to temporarily
+  store the CmObjects created and generate tokens once
+  the whole device tree is parsed.
+*/
+typedef struct PciParserTable {
+  /// PCI Configuration Space Info
+  CM_ARM_PCI_CONFIG_SPACE_INFO    PciConfigSpaceInfo;
+
+  /// Store the address mapping and interrupt mapping as CmObjDesc
+  /// before adding them to the Configuration Manager.
+  CM_OBJ_DESCRIPTOR               Mapping[PciMappingTableMax];
+} PCI_PARSER_TABLE;
+
+#pragma pack()
+
+/** CM_ARM_PCI_CONFIG_SPACE_INFO parser function.
+
+  The following structure is populated:
+  typedef struct CmArmPciConfigSpaceInfo {
+    UINT64  BaseAddress;                          // {Populated}
+    UINT16  PciSegmentGroupNumber;                // {Populated}
+    UINT8   StartBusNumber;                       // {Populated}
+    UINT8   EndBusNumber;                         // {Populated}
+  } CM_ARM_PCI_CONFIG_SPACE_INFO;
+
+  typedef struct CmArmPciAddressMapInfo {
+    UINT8                     SpaceCode;          // {Populated}
+    UINT64                    PciAddress;         // {Populated}
+    UINT64                    CpuAddress;         // {Populated}
+    UINT64                    AddressSize;        // {Populated}
+  } CM_ARM_PCI_ADDRESS_MAP_INFO;
+
+  typedef struct CmArmPciInterruptMapInfo {
+    UINT8                       PciBus;           // {Populated}
+    UINT8                       PciDevice;        // {Populated}
+    UINT8                       PciInterrupt;     // {Populated}
+    CM_ARM_GENERIC_INTERRUPT    IntcInterrupt;    // {Populated}
+  } CM_ARM_PCI_INTERRUPT_MAP_INFO;
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmPciConfigInfoParser (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  );
+
+#endif // ARM_PCI_CONFIG_SPACE_PARSER_H_
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v1 14/14] DynamicTablesPkg: Add FdtHwInfoParser library
  2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
                   ` (12 preceding siblings ...)
  2021-06-23 12:38 ` [PATCH v1 13/14] DynamicTablesPkg: FdtHwInfoParser: Add PCI config parser PierreGondois
@ 2021-06-23 12:38 ` PierreGondois
  13 siblings, 0 replies; 20+ messages in thread
From: PierreGondois @ 2021-06-23 12:38 UTC (permalink / raw)
  To: devel, Sami Mujawar, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei

From: Pierre Gondois <Pierre.Gondois@arm.com>

Hardware information parser is an optional module defined
by the Dynamic Tables Framework. It can either parse an
XML, a Device Tree or a Json file containing the platform
hardware information to populate the platform information
repository.

FdtHwInfoParser library is an instance of a HwInfoParser
that parses a Device Tree and populates the Configuration
Manager Platform information repository.

FdtHwInfoParser library is aimed at providing a solution
for generating ACPI tables for Guest Partitions launched
by virtual machine managers (VMMs). One such use case is
Kvmtool where the Device Tree for the Guest is passed on
to the firmware by Kvmtool. The Configuration Manager for
Kvmtool firmware shall invoke the FdtHwInfoParser to parse
the Device Tree to populate the hardware information in
the Platform Info Repository. The Kvmtool Configuration
Manager can the process this information to generate the
required ACPI tables for the Guest VM.

This approach also scales well if the number of CPUs or
if the hardware configuration of the Guest partition is
varied.

FdtHwInfoParser thereby introduces 'Dynamic Tables for
Virtual Machines'.

Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
---
 DynamicTablesPkg/DynamicTablesPkg.dsc         |   3 +-
 .../FdtHwInfoParserLib/FdtHwInfoParser.c      | 193 ++++++++++++++++++
 .../FdtHwInfoParserLib/FdtHwInfoParser.h      |  63 ++++++
 .../FdtHwInfoParserInclude.h                  |  17 ++
 .../FdtHwInfoParserLib/FdtHwInfoParserLib.inf |  56 +++++
 5 files changed, 331 insertions(+), 1 deletion(-)
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParser.c
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParser.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserInclude.h
 create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserLib.inf

diff --git a/DynamicTablesPkg/DynamicTablesPkg.dsc b/DynamicTablesPkg/DynamicTablesPkg.dsc
index 46b2e667fd25..fd7345891cf1 100644
--- a/DynamicTablesPkg/DynamicTablesPkg.dsc
+++ b/DynamicTablesPkg/DynamicTablesPkg.dsc
@@ -2,7 +2,7 @@
 #  Dsc file for Dynamic Tables Framework.
 #
 #  Copyright (c) 2019, Linaro Limited. All rights reserved.<BR>
-#  Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+#  Copyright (c) 2019 - 2021, Arm Limited. All rights reserved.<BR>
 #
 #  SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -42,6 +42,7 @@ [Components.common]
   DynamicTablesPkg/Library/Common/AmlLib/AmlLib.inf
   DynamicTablesPkg/Library/Common/SsdtSerialPortFixupLib/SsdtSerialPortFixupLib.inf
   DynamicTablesPkg/Library/Common/TableHelperLib/TableHelperLib.inf
+  DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserLib.inf
 
 [BuildOptions]
   *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParser.c
new file mode 100644
index 000000000000..ab3ac58b994e
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParser.c
@@ -0,0 +1,193 @@
+/** @file
+  Flattened Device Tree parser library for KvmTool.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "FdtHwInfoParser.h"
+#include "BootArch/ArmBootArchParser.h"
+#include "GenericTimer/ArmGenericTimerParser.h"
+#include "Gic/ArmGicDispatcher.h"
+#include "Pci/ArmPciConfigSpaceParser.h"
+#include "Serial/ArmSerialPortParser.h"
+
+/** Ordered table of parsers/dispatchers.
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+*/
+STATIC CONST FDT_HW_INFO_PARSER_FUNC HwInfoParserTable[] = {
+  ArmBootArchInfoParser,
+  ArmGenericTimerInfoParser,
+  ArmGicDispatcher,
+  ArmPciConfigInfoParser,
+  SerialPortDispatcher
+};
+
+/** Main dispatcher: sequentially call the parsers/dispatchers
+    of the HwInfoParserTable.
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  FdtParserHandle A handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+MainDispatcher (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+  IN        INT32                     FdtBranch
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Index;
+
+  if (fdt_check_header (FdtParserHandle->Fdt) < 0) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  for (Index = 0; Index < ARRAY_SIZE (HwInfoParserTable); Index++) {
+    Status = HwInfoParserTable[Index] (
+               FdtParserHandle,
+               FdtBranch
+               );
+    if (EFI_ERROR (Status)  &&
+        (Status != EFI_NOT_FOUND)) {
+      // If EFI_NOT_FOUND, the parser didn't find information in the DT.
+      // Don't trigger an error.
+      ASSERT (0);
+      return Status;
+    }
+  } // for
+
+  return EFI_SUCCESS;
+}
+
+/** Initialise the HwInfoParser.
+
+  The HwInfoParser shall use the information provided by the HwDataSource
+  to initialise the internal state of the parser or to index the data. This
+  internal state shall be linked to the ParserHandle using an implementation
+  defined mechanism.
+
+  @param [in]   HwDataSource    Pointer to the blob containing the hardware
+                                information. It can be a pointer to a Device
+                                Tree, an XML file, etc. or any other data
+                                structure defined by the HwInfoParser.
+  @param [in]   Context         A pointer to the caller's context.
+  @param [in]   HwInfoAdd       Function pointer called by the parser when
+                                adding information.
+  @param [out]  ParserHandle    A handle to the parser instance.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+HwInfoParserInit (
+  IN    VOID                  * HwDataSource,
+  IN    VOID                  * Context,
+  IN    HW_INFO_ADD_OBJECT      HwInfoAdd,
+  OUT   HW_INFO_PARSER_HANDLE * ParserHandle
+  )
+{
+  FDT_HW_INFO_PARSER * FdtParserHandle;
+
+  if ((ParserHandle == NULL)  ||
+      (HwInfoAdd == NULL)     ||
+      (HwDataSource == NULL)  ||
+      (fdt_check_header (HwDataSource) < 0)) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  FdtParserHandle = AllocateZeroPool (sizeof (FDT_HW_INFO_PARSER));
+  if (FdtParserHandle == NULL) {
+    *ParserHandle = NULL;
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  // The HwDataSource is a pointer to the FDT data.
+  FdtParserHandle->Fdt = HwDataSource;
+  FdtParserHandle->Context = Context;
+  FdtParserHandle->HwInfoAdd = HwInfoAdd;
+
+  *ParserHandle = (HW_INFO_PARSER_HANDLE)FdtParserHandle;
+  return EFI_SUCCESS;
+}
+
+/** Parse the data provided by the HwDataSource.
+
+  @param [in]  ParserHandle    A handle to the parser instance.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
+**/
+EFI_STATUS
+EFIAPI
+HwInfoParse (
+  IN  HW_INFO_PARSER_HANDLE   ParserHandle
+  )
+{
+  EFI_STATUS    Status;
+
+  if (ParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Call all the parsers from the root node (-1).
+  Status = MainDispatcher (
+             (FDT_HW_INFO_PARSER_HANDLE)ParserHandle,
+             -1
+             );
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+  }
+
+  return Status;
+}
+
+/** Cleanup any internal state and resources that were allocated
+    by the the HwInfoParser.
+
+  @param [in]  ParserHandle    A handle to the parser instance.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+HwInfoParserShutdown (
+  IN  HW_INFO_PARSER_HANDLE   ParserHandle
+)
+{
+  if (ParserHandle == NULL) {
+    ASSERT (0);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  FreePool (ParserHandle);
+
+  return EFI_SUCCESS;
+}
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParser.h
new file mode 100644
index 000000000000..1c40fc5587c9
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParser.h
@@ -0,0 +1,63 @@
+/** @file
+  Flattened Device Tree parser definitions.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef FDT_HW_INFO_PARSER_H_
+#define FDT_HW_INFO_PARSER_H_
+
+#include <FdtHwInfoParserInclude.h>
+
+#include <ConfigurationManagerObject.h>
+#include <Library/HwInfoParserLib.h>
+
+#include "FdtUtility.h"
+
+/** A structure describing the instance of the FdtHwInfoParser.
+*/
+typedef struct FdtHwInfoParser {
+  /// Pointer to the HwDataSource i.e. the
+  /// Flattened Device Tree (Fdt).
+  VOID              * Fdt;
+
+  /// Pointer to the caller's context.
+  VOID              * Context;
+
+  /// Function pointer called by the
+  /// parser when adding information.
+  HW_INFO_ADD_OBJECT  HwInfoAdd;
+} FDT_HW_INFO_PARSER;
+
+/** A pointer type for FDT_HW_INFO_PARSER.
+*/
+typedef FDT_HW_INFO_PARSER*   FDT_HW_INFO_PARSER_HANDLE;
+
+/** Function pointer to a parser function.
+
+  A parser parses a Device Tree to populate a specific CmObj type. None,
+  one or many CmObj can be created by the parser.
+  The created CmObj are then handed to the parser's caller through the
+  HW_INFO_ADD_OBJECT interface.
+  This can also be a dispatcher. I.e. a function that not parsing a
+  Device Tree but calling other parsers.
+
+  @param [in]  ParserHandle    Handle to the parser instance.
+  @param [in]  FdtBranch       When searching for DT node name, restrict
+                               the search to this Device Tree branch.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_ABORTED             An error occurred.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+  @retval EFI_NOT_FOUND           Not found.
+  @retval EFI_UNSUPPORTED         Unsupported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * FDT_HW_INFO_PARSER_FUNC) (
+  IN  CONST FDT_HW_INFO_PARSER_HANDLE ParserHandle,
+  IN        INT32                     FdtBranch
+  );
+
+#endif // FDT_HW_INFO_PARSER_H_
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserInclude.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserInclude.h
new file mode 100644
index 000000000000..583f290095d9
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserInclude.h
@@ -0,0 +1,17 @@
+/** @file
+  Include file for Fdt HwInfoParser.
+
+  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef FDT_HW_INFO_PARSER_INCLUDE_H_
+#define FDT_HW_INFO_PARSER_INCLUDE_H_
+
+#include <Base.h>
+#include <libfdt.h>
+#include <Library/ArmLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#endif // FDT_HW_INFO_PARSER_INCLUDE_H_
diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserLib.inf b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserLib.inf
new file mode 100644
index 000000000000..d2c171accaa5
--- /dev/null
+++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/FdtHwInfoParserLib.inf
@@ -0,0 +1,56 @@
+## @file
+#  Flattened Device Tree information parser.
+#
+#  Copyright (c) 2021, ARM Limited. All rights reserved.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION    = 0x00010019
+  BASE_NAME      = FdtHwInfoParserLib
+  FILE_GUID      = F174A422-BC86-41E2-9E9C-C6D6E437F4AD
+  VERSION_STRING = 1.0
+  MODULE_TYPE    = DXE_DRIVER
+  LIBRARY_CLASS  = HwInfoParserLib
+
+[Sources]
+  CmObjectDescUtility.c
+  CmObjectDescUtility.h
+  FdtHwInfoParserInclude.h
+  FdtHwInfoParser.c
+  FdtHwInfoParser.h
+  FdtUtility.c
+  FdtUtility.h
+  BootArch/ArmBootArchParser.c
+  BootArch/ArmBootArchParser.h
+  GenericTimer/ArmGenericTimerParser.c
+  GenericTimer/ArmGenericTimerParser.h
+  Gic/ArmGicCParser.c
+  Gic/ArmGicCParser.h
+  Gic/ArmGicDispatcher.c
+  Gic/ArmGicDispatcher.h
+  Gic/ArmGicDParser.c
+  Gic/ArmGicDParser.h
+  Gic/ArmGicItsParser.c
+  Gic/ArmGicItsParser.h
+  Gic/ArmGicMsiFrameParser.c
+  Gic/ArmGicMsiFrameParser.h
+  Gic/ArmGicRParser.c
+  Gic/ArmGicRParser.h
+  Pci/ArmPciConfigSpaceParser.c
+  Pci/ArmPciConfigSpaceParser.h
+  Serial/ArmSerialPortParser.c
+  Serial/ArmSerialPortParser.h
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  DynamicTablesPkg/DynamicTablesPkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  DebugLib
+  FdtLib
+  MemoryAllocationLib
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v1 02/14] DynamicTablesPkg: FdtHwInfoParser: CM Object descriptor helper
  2021-06-23 12:38 ` [PATCH v1 02/14] DynamicTablesPkg: FdtHwInfoParser: CM Object descriptor helper PierreGondois
@ 2021-11-05 14:27   ` Sami Mujawar
  0 siblings, 0 replies; 20+ messages in thread
From: Sami Mujawar @ 2021-11-05 14:27 UTC (permalink / raw)
  To: Pierre.Gondois, devel, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei, nd

[-- Attachment #1: Type: text/plain, Size: 17412 bytes --]

Hi Pierre,

Thank you for this patch.

Please find my feedback inline marked [SAMI].

Regards,

Sami Mujawar

On 23/06/2021 01:38 PM, Pierre.Gondois@arm.com wrote:
> From: Pierre Gondois <Pierre.Gondois@arm.com>
>
> FdtHwInfoParserLib is an instance of the HwInfoParser. The
> FdtHwInfoParser parses a platform Device Tree and populates
> the Platform Information repository with Configuration
> Manager objects that describe the platform hardware.
> These Configuration Manager objects are encapsulated in
> Configuration Manager Object Descriptors.
>
> Therefore, add helper functions to create and free the
> Configuration Manager Object descriptors.
>
> Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
> Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
> ---
>   .../FdtHwInfoParserLib/CmObjectDescUtility.c  | 305 ++++++++++++++++++
>   .../FdtHwInfoParserLib/CmObjectDescUtility.h  | 131 ++++++++
>   2 files changed, 436 insertions(+)
>   create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.c
>   create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.h
>
> diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.c
> new file mode 100644
> index 000000000000..e471217504fe
> --- /dev/null
> +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.c
> @@ -0,0 +1,305 @@
> +/** @file
> +  Configuration manager Object Descriptor Utility.
> +
> +  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#include <FdtHwInfoParserInclude.h>
> +#include <ConfigurationManagerObject.h>
> +
> +#include "CmObjectDescUtility.h"
> +
> +/** Create a CM_OBJ_DESCRIPTOR.
> +
> +  @param [in]  ObjectId       CM_OBJECT_ID of the node.
> +  @param [in]  Count          Number of CmObj stored in the
> +                              data field.
> +  @param [in]  Data           Pointer to one or more CmObj objects.
> +                              The content of this Data buffer is copied.
> +  @param [in]  Size           Size of the Data buffer.
> +  @param [out] NewCmObjDesc   The created CM_OBJ_DESCRIPTOR.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CreateCmObjDesc (
> +  IN  CM_OBJECT_ID          ObjectId,
> +  IN  UINT32                Count,
> +  IN  VOID                * Data,
> +  IN  UINT32                Size,
> +  OUT CM_OBJ_DESCRIPTOR  ** NewCmObjDesc
> +  )
> +{
> +  CM_OBJ_DESCRIPTOR   * CmObjDesc;
> +  VOID                * DataBuffer;
> +
> +  if ((Count == 0)      ||
> +      (Data == NULL)    ||
> +      (Size == 0)       ||
> +      (NewCmObjDesc == NULL)) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  CmObjDesc = AllocateZeroPool (sizeof (CM_OBJ_DESCRIPTOR));
> +  if (CmObjDesc == NULL) {
> +    ASSERT (0);
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  DataBuffer = AllocateCopyPool (Size, Data);
> +  if (DataBuffer == NULL) {
> +    ASSERT (0);
> +    return EFI_OUT_OF_RESOURCES;
[SAMI] CmObjDescmust be freed here otherwise there would be a memory leak.
> +  }
> +
> +  CmObjDesc->ObjectId = ObjectId;
> +  CmObjDesc->Count = Count;
> +  CmObjDesc->Data = DataBuffer;
> +  CmObjDesc->Size = Size;
> +
> +  *NewCmObjDesc = CmObjDesc;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/** Free resources allocated for the CM_OBJ_DESCRIPTOR.
> +
> +  @param [in] CmObjDesc           Pointer to the CM_OBJ_DESCRIPTOR.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +**/
> +EFI_STATUS
> +EFIAPI
> +FreeCmObjDesc (
> +  IN CM_OBJ_DESCRIPTOR  * CmObjDesc
> +  )
> +{
> +  if (CmObjDesc == NULL) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (CmObjDesc->Data != NULL) {
> +    FreePool (CmObjDesc->Data);
> +  }
> +
> +  FreePool (CmObjDesc);
> +  return EFI_SUCCESS;
> +}
> +
> +/** Add a single CmObj to the Configuration Manager.
> +
> +  @param  [in]  FdtParserHandle   A handle to the parser instance.
> +  @param  [in]  ObjectId          CmObj ObjectId.
> +  @param  [in]  Data              CmObj Data.
> +  @param  [in]  Size              CmObj Size.
> +  @param  [out] Token             If provided and success,
> +                                  token generated for this CmObj.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +**/
> +EFI_STATUS
> +EFIAPI
> +AddSingleCmObj (
> +  IN  CONST FDT_HW_INFO_PARSER_HANDLE   FdtParserHandle,
> +  IN        CM_OBJECT_ID                ObjectId,
> +  IN        VOID                        *Data,
> +  IN        UINT32                      Size,
> +  OUT       CM_OBJECT_TOKEN             *Token    OPTIONAL
> +  )
> +{
> +  EFI_STATUS          Status;
> +  CM_OBJ_DESCRIPTOR   CmObjDesc;
> +
> +  if ((FdtParserHandle == NULL)             ||
> +      (FdtParserHandle->HwInfoAdd == NULL)  ||
> +      (Data == NULL)                        ||
> +      (Size == 0)) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  CmObjDesc.ObjectId = ObjectId;
> +  CmObjDesc.Count = 1;
> +  CmObjDesc.Data = Data;
> +  CmObjDesc.Size = Size;
> +
> +  // Add the CmObj.
> +  // Don't ask for a token.
> +  Status = FdtParserHandle->HwInfoAdd (
> +                              FdtParserHandle,
> +                              FdtParserHandle->Context,
> +                              &CmObjDesc,
> +                              Token
> +                              );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +  }
[SAMI] I thinkASSERT_EFI_ERROR() can be used here.
> +  return Status;
> +}
> +
> +/** Add multiple CmObj to the Configuration Manager.
> +
> +  @param  [in]  FdtParserHandle   A handle to the parser instance.
> +  @param  [in]  CmObjDesc         CmObjDesc containing multiple CmObj
> +                                  to add.
> +  @param  [in]  TokenCount        If provided, count of entries in the
> +                                  TokenTable.
> +  @param  [out] TokenTable        If provided and success,
> +                                  token generated for these CmObj.
> +                                  Address of an array of CM_OBJECT_TOKEN
> +                                  with the same number of elements as the
> +                                  CmObjDesc.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +**/
> +EFI_STATUS
> +EFIAPI
> +AddMultipleCmObj (
> +  IN  CONST FDT_HW_INFO_PARSER_HANDLE   FdtParserHandle,
> +  IN  CONST CM_OBJ_DESCRIPTOR           *CmObjDesc,
> +  IN        UINT32                      TokenCount,   OPTIONAL
> +  OUT       CM_OBJECT_TOKEN             *TokenTable   OPTIONAL
> +  )
> +{
> +  EFI_STATUS          Status;
> +  UINT32              Index;
> +  UINT32              Count;
> +  VOID              * Data;
> +  UINT32              Size;
> +  CM_OBJ_DESCRIPTOR   SingleCmObjDesc;
> +
> +  if ((FdtParserHandle == NULL)             ||
> +      (FdtParserHandle->HwInfoAdd == NULL)  ||
> +      (CmObjDesc == NULL)                   ||
> +      (CmObjDesc->Count == 0)               ||
> +      (CmObjDesc->Data == NULL)             ||
> +      (CmObjDesc->Size == 0)){
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Count = CmObjDesc->Count;
> +  Data = CmObjDesc->Data;
> +  Size = CmObjDesc->Size / Count;
> +
> +  SingleCmObjDesc.ObjectId = CmObjDesc->ObjectId;
> +  SingleCmObjDesc.Count = 1;
> +  SingleCmObjDesc.Size = Size;
> +
> +  for (Index = 0; Index < Count; Index++) {
> +    SingleCmObjDesc.Data = Data + Index * Size;
[SAMI] Data needs to be typecasted to UINT8 for the pointer arithmetic 
to work. Also, enclose Index * Size in parenthesis.
> +    // Add the CmObj.
> +    Status = FdtParserHandle->HwInfoAdd (
> +                                FdtParserHandle,
> +                                FdtParserHandle->Context,
> +                                &SingleCmObjDesc,
> +                                (TokenTable != NULL) ?
> +                                  &TokenTable[Index] :
> +                                  NULL
> +                                );
> +    if (EFI_ERROR (Status)) {
> +      ASSERT (0);
> +      return Status;
> +    }
> +  } // for
> +
> +  return Status;
> +}
> +
> +/** Add multiple CmObj to the Configuration Manager.
> +
> +  Get one token referencing a EArmObjCmRef CmObj itself referencing
> +  the input CmObj. In the table below, RefToken is returned.
> +
> +  Token referencing an      Array of tokens             Array of CmObj
> +  array of EArmObjCmRef     referencing each            from the input:
> +  CmObj:                    CmObj from the input:
> +
> +  RefToken         --->     CmObjToken[0]        --->   CmObj[0]
> +                            CmObjToken[1]        --->   CmObj[1]
> +                            CmObjToken[2]        --->   CmObj[2]
> +
> +  @param  [in]  FdtParserHandle   A handle to the parser instance.
> +  @param  [in]  CmObjDesc         CmObjDesc containing multiple CmObj
> +                                  to add.
> +  @param  [out] Token             If success, token referencing an array
> +                                  of EArmObjCmRef CmObj, themselves
> +                                  referencing the input CmObjs.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
> +**/
> +EFI_STATUS
> +EFIAPI
> +AddMultipleCmObjWithCmObjRef (
> +  IN  CONST FDT_HW_INFO_PARSER_HANDLE     FdtParserHandle,
> +  IN  CM_OBJ_DESCRIPTOR                 * CmObjDesc,
> +  OUT CM_OBJECT_TOKEN                   * Token
> +  )
> +{
> +  EFI_STATUS          Status;
> +  CM_OBJ_DESCRIPTOR   CmObjRef;
> +  CM_OBJECT_TOKEN    *TokenTable;
> +  INT32               TokenTableSize;
> +
> +  if ((FdtParserHandle == NULL)             ||
> +      (FdtParserHandle->HwInfoAdd == NULL)  ||
> +      (CmObjDesc == NULL)                   ||
> +      (CmObjDesc->Count == 0)               ||
> +      (CmObjDesc->Data == NULL)             ||
> +      (CmObjDesc->Size == 0)                ||
> +      (Token == NULL)) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  // Allocate a buffer to store the tokens.
> +  TokenTableSize = CmObjDesc->Count * sizeof (CM_OBJECT_TOKEN);
> +  TokenTable = AllocateZeroPool (TokenTableSize);
> +  if (TokenTable == NULL) {
> +    ASSERT (0);
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  // Add the input CmObjs.
> +  Status = AddMultipleCmObj (
> +             FdtParserHandle,
> +             CmObjDesc,
> +             CmObjDesc->Count,
> +             TokenTable
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    goto exit_handler;
> +  }
> +
> +  CmObjRef.ObjectId = CREATE_CM_ARM_OBJECT_ID (EArmObjCmRef);
> +  CmObjRef.Data = TokenTable;
> +  CmObjRef.Count = CmObjDesc->Count;
> +  CmObjRef.Size = TokenTableSize;
> +
> +  // Add the array of EArmObjCmRef CmObjs.
> +  Status = FdtParserHandle->HwInfoAdd (
> +                              FdtParserHandle,
> +                              FdtParserHandle->Context,
> +                              &CmObjRef,
> +                              Token
> +                              );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +  }
[SAMI] I thinkASSERT_EFI_ERROR() can be used here.
> +
> +exit_handler:
> +  FreePool (TokenTable);
> +  return Status;
> +}
> diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.h
> new file mode 100644
> index 000000000000..34439c716fb3
> --- /dev/null
> +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/CmObjectDescUtility.h
> @@ -0,0 +1,131 @@
> +/** @file
> +  Configuration manager Object Descriptor Utility.
> +
> +  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#ifndef CM_OBJECT_DESC_UTILITY_H_
> +#define CM_OBJECT_DESC_UTILITY_H_
> +
> +#include <ConfigurationManagerObject.h>
> +
> +#include "FdtHwInfoParser.h"
> +
> +/** Create a CM_OBJ_DESCRIPTOR.
> +
> +  @param [in]  ObjectId       CM_OBJECT_ID of the node.
> +  @param [in]  Count          Number of CmObj stored in the
> +                              data field.
> +  @param [in]  Data           Pointer to one or more CmObj objects.
> +                              The content of this Data buffer is copied.
> +  @param [in]  Size           Size of the Data buffer.
> +  @param [out] NewCmObjDesc   The created CM_OBJ_DESCRIPTOR.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
> +**/
> +EFI_STATUS
> +EFIAPI
> +CreateCmObjDesc (
> +  IN  CM_OBJECT_ID          ObjectId,
> +  IN  UINT32                Count,
> +  IN  VOID                * Data,
> +  IN  UINT32                Size,
> +  OUT CM_OBJ_DESCRIPTOR  ** NewCmObjDesc
> +  );
> +
> +/** Free resources allocated for the CM_OBJ_DESCRIPTOR.
> +
> +  @param [in] CmObjDesc           Pointer to the CM_OBJ_DESCRIPTOR.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +**/
> +EFI_STATUS
> +EFIAPI
> +FreeCmObjDesc (
> +  IN CM_OBJ_DESCRIPTOR  * CmObjDesc
> +  );
> +
> +/** Add a single CmObj to the Configuration Manager.
> +
> +  @param  [in]  FdtParserHandle   A handle to the parser instance.
> +  @param  [in]  ObjectId          CmObj ObjectId.
> +  @param  [in]  Data              CmObj Data.
> +  @param  [in]  Size              CmObj Size.
> +  @param  [out] Token             If provided and success,
> +                                  token generated for this CmObj.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +**/
> +EFI_STATUS
> +EFIAPI
> +AddSingleCmObj (
> +  IN  CONST FDT_HW_INFO_PARSER_HANDLE   FdtParserHandle,
> +  IN        CM_OBJECT_ID                ObjectId,
> +  IN        VOID                        *Data,
> +  IN        UINT32                      Size,
> +  OUT       CM_OBJECT_TOKEN             *Token    OPTIONAL
> +  );
> +
> +/** Add multiple CmObj to the Configuration Manager.
> +
> +  @param  [in]  FdtParserHandle   A handle to the parser instance.
> +  @param  [in]  CmObjDesc         CmObjDesc containing multiple CmObj
> +                                  to add.
> +  @param  [in]  TokenCount        If provided, count of entries in the
> +                                  TokenTable.
> +  @param  [out] TokenTable        If provided and success,
> +                                  token generated for these CmObj.
> +                                  Address of an array of CM_OBJECT_TOKEN
> +                                  with the same number of elements as the
> +                                  CmObjDesc.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +**/
> +EFI_STATUS
> +EFIAPI
> +AddMultipleCmObj (
> +  IN  CONST FDT_HW_INFO_PARSER_HANDLE   FdtParserHandle,
> +  IN  CONST CM_OBJ_DESCRIPTOR           *CmObjDesc,
> +  IN        UINT32                      TokenCount,   OPTIONAL
> +  OUT       CM_OBJECT_TOKEN             *TokenTable   OPTIONAL
> +  );
> +
> +/** Add multiple CmObj to the Configuration Manager.
> +
> +  Get one token referencing a EArmObjCmRef CmObj itself referencing
> +  the input CmObj. In the table below, RefToken is returned.
> +
> +  Token referencing an      Array of tokens             Array of CmObj
> +  array of EArmObjCmRef     referencing each            from the input:
> +  CmObj:                    CmObj from the input:
> +
> +  RefToken         --->     CmObjToken[0]        --->   CmObj[0]
> +                            CmObjToken[1]        --->   CmObj[1]
> +                            CmObjToken[2]        --->   CmObj[2]
> +
> +  @param  [in]  FdtParserHandle   A handle to the parser instance.
> +  @param  [in]  CmObjDesc         CmObjDesc containing multiple CmObj
> +                                  to add.
> +  @param  [out] Token             If success, token referencing an array
> +                                  of EArmObjCmRef CmObj, themselves
> +                                  referencing the input CmObjs.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_OUT_OF_RESOURCES    An allocation has failed.
> +**/
> +EFI_STATUS
> +EFIAPI
> +AddMultipleCmObjWithCmObjRef (
> +  IN  CONST FDT_HW_INFO_PARSER_HANDLE     FdtParserHandle,
> +  IN  CM_OBJ_DESCRIPTOR                 * CmObjDesc,
> +  OUT CM_OBJECT_TOKEN                   * Token
> +  );
> +
> +#endif // CM_OBJECT_DESC_UTILITY_H_


[-- Attachment #2: Type: text/html, Size: 18006 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v1 03/14] DynamicTablesPkg: FdtHwInfoParser: Add FDT utility functions
  2021-06-23 12:38 ` [PATCH v1 03/14] DynamicTablesPkg: FdtHwInfoParser: Add FDT utility functions PierreGondois
@ 2021-11-05 14:27   ` Sami Mujawar
  0 siblings, 0 replies; 20+ messages in thread
From: Sami Mujawar @ 2021-11-05 14:27 UTC (permalink / raw)
  To: Pierre.Gondois, devel, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei, nd

Hi Pierre,

I have a minor comment, otherwise this patch looks good to me.

Regards,

Sami Mujawar

On 23/06/2021 01:38 PM, Pierre.Gondois@arm.com wrote:
> From: Pierre Gondois <Pierre.Gondois@arm.com>
>
> 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 <Pierre.Gondois@arm.com>
> Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
> ---
>   .../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.<BR>
> +  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 <FdtHwInfoParserInclude.h>
> +#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
[SAMI] I think the comment here needs to be updated.
> +  // may contain the node name followed by '@'0x<addr>.
> +  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.<BR>
> +  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_


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v1 06/14] DynamicTablesPkg: FdtHwInfoParser: Add Serial port parser
  2021-06-23 12:38 ` [PATCH v1 06/14] DynamicTablesPkg: FdtHwInfoParser: Add Serial port parser PierreGondois
@ 2021-11-05 14:27   ` Sami Mujawar
  2021-11-18 10:11     ` PierreGondois
  0 siblings, 1 reply; 20+ messages in thread
From: Sami Mujawar @ 2021-11-05 14:27 UTC (permalink / raw)
  To: Pierre.Gondois, devel, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei, nd

[-- Attachment #1: Type: text/plain, Size: 25464 bytes --]

Hi Pierre,

Please find my response inline marked [SAMI].

Regards,

Sami Mujawar


On 23/06/2021 01:38 PM, Pierre.Gondois@arm.com wrote:
> From: Pierre Gondois <Pierre.Gondois@arm.com>
>
> The Microsoft Debug Port Table 2 (DBG2), the Serial Port Console
> Redirector (SPCR) table are mandatory tables required for booting
> a standards-based operating system. The DBG2 table is used by the
> OS debugger while the SPCR table is used to configure the serial
> terminal. Additionally, the serial ports available on a platform
> for generic use also need to be described in DSDT/SSDT for an OS
> to be able to use the serial ports.
>
> The Arm Base System Architecture 1.0 specification a lists of
> supported serial port hardware for Arm Platforms. This list
> includes the following serial port UARTs:
>   - SBSA/Generic UART
>   - a fully 16550 compatible UART.
> Along, with these the PL011 UART is the most commonly used serial
> port hardware on Arm platforms.
>
> The serial port hardware information is described in the platform
> Device Tree, the bindings for which can be found at:
>   - linux/Documentation/devicetree/bindings/serial/serial.yaml
>   - linux/Documentation/devicetree/bindings/serial/8250.txt
>   - linux/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt
>   - linux/Documentation/devicetree/bindings/serial/pl011.yaml
>
> The FdtHwInfoParser implements a Serial Port Parser that parses
> the platform Device Tree to create CM_ARM_SERIAL_PORT_INFO objects
> with the following IDs:
>   - EArmObjSerialConsolePortInfo (for use by SPCR)
>   - EArmObjSerialDebugPortInfo (for use by DBG2)
>   - EArmObjSerialPortInfo (for use as generic Serial Ports)
>
> The Serial Port for use by SPCR is selected by parsing the Device
> Tree for the '/chosen' node with the 'stdout-path' property. The
> next Serial Port is selected for use as the Debug Serial Port and
> the remaining serial ports are used as generic serial ports.
>
> The CM_ARM_SERIAL_PORT_INFO objects are encapsulated in Configuration
> Manager descriptor objects with the respective IDs and are added to
> the platform information repository.
>
> The platform Configuration Manager can then utilise this information
> when generating the DBG2, SPCR and the SSDT serial port tables.
>
> Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
> Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
> ---
>   .../Serial/ArmSerialPortParser.c              | 621 ++++++++++++++++++
>   .../Serial/ArmSerialPortParser.h              |  47 ++
>   2 files changed, 668 insertions(+)
>   create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c
>   create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h
>
> diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c
> new file mode 100644
> index 000000000000..d5db206ae93c
> --- /dev/null
> +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c
> @@ -0,0 +1,621 @@
> +/** @file
> +  Arm Serial Port Parser.
> +
> +  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Reference(s):
> +  - linux/Documentation/devicetree/bindings/serial/serial.yaml
> +  - linux/Documentation/devicetree/bindings/serial/8250.txt
> +  - linux/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt
> +  - linux/Documentation/devicetree/bindings/serial/pl011.yaml
> +**/
> +
> +#include <IndustryStandard/DebugPort2Table.h>
> +
> +#include "CmObjectDescUtility.h"
> +#include "FdtHwInfoParser.h"
> +#include "Serial/ArmSerialPortParser.h"
> +
> +/** List of "compatible" property values for serial port nodes.
> +
> +  Any other "compatible" value is not supported by this module.
> +*/
> +STATIC CONST COMPATIBILITY_STR SerialCompatibleStr[] = {
> +  {"ns16550a"},
> +  {"arm,sbsa-uart"}
> +};
> +
> +/** COMPATIBILITY_INFO structure for the SerialCompatible.
> +*/
> +CONST COMPATIBILITY_INFO SerialCompatibleInfo = {
> +  ARRAY_SIZE (SerialCompatibleStr),
> +  SerialCompatibleStr
> +};
> +
> +/** 16550 UART compatible strings.
> +
> +  Any string of this list must be part of SerialCompatible.
> +*/
> +STATIC CONST COMPATIBILITY_STR Serial16550CompatibleStr[] = {
> +  {"ns16550a"}
> +};
> +
> +/** COMPATIBILITY_INFO structure for the Serial16550Compatible.
> +*/
> +CONST COMPATIBILITY_INFO Serial16550CompatibleInfo = {
> +  ARRAY_SIZE (Serial16550CompatibleStr),
> +  Serial16550CompatibleStr
> +};
> +
> +/** SBSA UART compatible strings.
> +
> +  Any string of this list must be part of SerialCompatible.
> +*/
> +STATIC CONST COMPATIBILITY_STR SerialSbsaCompatibleStr[] = {
> +  {"arm,sbsa-uart"}
> +};
> +
> +/** COMPATIBILITY_INFO structure for the SerialSbsaCompatible.
> +*/
> +CONST COMPATIBILITY_INFO SerialSbsaCompatibleInfo = {
> +  ARRAY_SIZE (SerialSbsaCompatibleStr),
> +  SerialSbsaCompatibleStr
> +};
> +
> +/** Parse a serial port node.
> +
> +  @param [in]  Fdt               Pointer to a Flattened Device Tree (Fdt).
> +  @param [in]  SerialPortNode    Offset of a serial-port node.
> +  @param [in]  SerialPortInfo    The CM_ARM_SERIAL_PORT_INFO to populate.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_ABORTED             An error occurred.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_UNSUPPORTED         Unsupported.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +SerialPortNodeParser (
> +  IN  CONST VOID               * Fdt,
> +  IN  INT32                      SerialPortNode,
> +  IN  CM_ARM_SERIAL_PORT_INFO  * SerialPortInfo
> +  )
> +{
> +  EFI_STATUS     Status;
> +  INT32          IntcNode;
> +  CONST UINT8  * SizeValue;
> +
> +  INT32          AddressCells;
> +  INT32          SizeCells;
> +  INT32          IntCells;
> +
> +  CONST UINT8  * Data;
> +  INT32          DataSize;
> +  UINT8          AccessSize;
> +
> +  if ((Fdt == NULL) ||
> +      (SerialPortInfo == NULL)) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Status = FdtGetParentAddressInfo (
> +             Fdt,
> +             SerialPortNode,
> +             &AddressCells,
> +             &SizeCells
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  // Don't support more than 64 bits and less than 32 bits addresses.
> +  if ((AddressCells < 1)  ||
> +      (AddressCells > 2)  ||
> +      (SizeCells < 1)     ||
> +      (SizeCells > 2)) {
> +    ASSERT (0);
> +    return EFI_ABORTED;
> +  }
> +
> +  Data = fdt_getprop (Fdt, SerialPortNode, "reg", &DataSize);
> +  if ((Data == NULL) ||
> +      (DataSize < (INT32)(sizeof (UINT32) *
> +         GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, SizeCells)) - 1)) {
> +    // If error or not enough space.
> +    ASSERT (0);
> +    return EFI_ABORTED;
> +  }
> +
> +  if (AddressCells == 2) {
> +    SerialPortInfo->BaseAddress = fdt64_to_cpu (*(UINT64*)Data);
> +  } else {
> +    SerialPortInfo->BaseAddress = fdt32_to_cpu (*(UINT32*)Data);
> +  }
> +
> +  SizeValue = Data + (sizeof (UINT32) *
> +    GET_DT_REG_SIZE_OFFSET (0, AddressCells, SizeCells));
> +  if (SizeCells == 2) {
> +    SerialPortInfo->BaseAddressLength = fdt64_to_cpu (*(UINT64*)SizeValue);
> +  } else {
> +    SerialPortInfo->BaseAddressLength = fdt32_to_cpu (*(UINT32*)SizeValue);
> +  }
> +
> +  // Get the associated interrupt-controller.
> +  Status= FdtGetIntcParentNode (Fdt, SerialPortNode, &IntcNode);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    if (Status == EFI_NOT_FOUND) {
> +      // Should have found the node.
> +      Status = EFI_ABORTED;
> +    }
> +    return Status;
> +  }
> +
> +  // Get the number of cells used to encode an interrupt.
> +  Status = FdtGetInterruptCellsInfo (Fdt, IntcNode, &IntCells);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  Data = fdt_getprop (Fdt, SerialPortNode, "interrupts", &DataSize);
> +  if ((Data == NULL) || (DataSize != (IntCells * sizeof (UINT32)))) {
> +    // If error or not 1 interrupt.
> +    ASSERT (0);
> +    return EFI_ABORTED;
> +  }
> +
> +  SerialPortInfo->Interrupt = FdtGetInterruptId ((CONST UINT32*)Data);
> +
> +  // Note: clock-frequency is optional for SBSA UART.
> +  Data = fdt_getprop (Fdt, SerialPortNode, "clock-frequency", &DataSize);
> +  if (Data != NULL) {
> +    if (DataSize < sizeof (UINT32)) {
> +      // If error or not enough space.
> +      ASSERT (0);
> +      return EFI_ABORTED;
> +    } else if (fdt_node_offset_by_phandle (Fdt, fdt32_to_cpu (*Data)) >= 0) {
> +      // "clock-frequency" can be a "clocks phandle to refer to the clk used".
> +      // This is not supported.
> +      ASSERT (0);
> +      return EFI_UNSUPPORTED;
> +    }
> +    SerialPortInfo->Clock = fdt32_to_cpu (*(UINT32*)Data);
> +  }
> +
> +  if (FdtNodeIsCompatible (Fdt, SerialPortNode, &Serial16550CompatibleInfo)) {
> +    SerialPortInfo->PortSubtype = EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550;
[SAMI] I think this needs to be set 
toEFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_16550_WITH_GAS.
> +
> +    /* reg-io-width:
> +         description: |
> +         The size (in bytes) of the IO accesses that should be performed on the
> +         device. There are some systems that require 32-bit accesses to the
> +         UART.
> +    */
> +    Data = fdt_getprop (Fdt, SerialPortNode, "reg-io-width", &DataSize);
> +    if (Data != NULL) {
> +      if (DataSize < sizeof (UINT32)) {
> +        // If error or not enough space.
> +        ASSERT (0);
> +        return EFI_ABORTED;
> +      }
> +
> +      AccessSize = fdt32_to_cpu (*(UINT32*)Data);
> +      if (AccessSize > EFI_ACPI_6_3_QWORD) {
> +        ASSERT (0);
> +        return EFI_INVALID_PARAMETER;
> +      }
> +      SerialPortInfo->AccessSize = AccessSize;
> +    } else {
> +      // 8250/16550 defaults to byte access.
> +      SerialPortInfo->AccessSize = EFI_ACPI_6_3_BYTE;
> +    }
> +  } else if (FdtNodeIsCompatible (
> +               Fdt,
> +               SerialPortNode,
> +               &SerialSbsaCompatibleInfo
> +               )) {
> +    SerialPortInfo->PortSubtype =
> +      EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_ARM_SBSA_GENERIC_UART;
[SAMI] Should PL011 be supported as well?
> +  } else {
> +    ASSERT (0);
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  // Set Baudrate to 115200 by default
> +  SerialPortInfo->BaudRate = 115200;
> +  return EFI_SUCCESS;
> +}
> +
> +/** Find the console serial-port node in the DT.
> +
> +  This function fetches the node referenced in the "stdout-path"
> +  property of the "chosen" node.
> +
> +  @param [in]  Fdt                  Pointer to a Flattened Device Tree (Fdt).
> +  @param [out] SerialConsoleNode    If success, contains the node offset
> +                                    of the console serial-port node.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_ABORTED             An error occurred.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_NOT_FOUND           Not found.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +GetSerialConsoleNode (
> +  IN  CONST VOID    * Fdt,
> +  OUT       INT32   * SerialConsoleNode
> +  )
> +{
> +  CONST CHAR8 * Prop;
> +  INT32         PropSize;
> +  CONST CHAR8 * Path;
> +  INT32         PathLen;
> +  INT32         ChosenNode;
> +
> +  if ((Fdt == NULL) ||
> +      (SerialConsoleNode == NULL)) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  // The "chosen" node resides at the the root of the DT. Fetch it.
> +  ChosenNode = fdt_path_offset (Fdt, "/chosen");
> +  if (ChosenNode < 0) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  Prop = fdt_getprop (Fdt, ChosenNode, "stdout-path", &PropSize);
> +  if ((Prop == NULL) || (PropSize < 0)) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  // Determine the actual path length, as a colon terminates the path.
> +  Path = ScanMem8 (Prop, ':', PropSize);
> +  if (Path == NULL) {
> +    PathLen = (UINT32)AsciiStrLen (Prop);
> +  } else {
> +    PathLen = (INT32)(Path - Prop);
> +  }
> +
> +  // Aliases cannot start with a '/', so it must be the actual path.
> +  if (Prop[0] == '/') {
> +    *SerialConsoleNode = fdt_path_offset_namelen (Fdt, Prop, PathLen);
> +    return EFI_SUCCESS;
> +  }
> +
> +  // Lookup the alias, as this contains the actual path.
> +  Path = fdt_get_alias_namelen (Fdt, Prop, PathLen);
> +  if (Path == NULL) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  *SerialConsoleNode = fdt_path_offset (Fdt, Path);
> +  return EFI_SUCCESS;
> +}
> +
> +/** CM_ARM_SERIAL_PORT_INFO dispatcher function (for a generic serial-port).
> +
> +  @param [in]  FdtParserHandle A handle to the parser instance.
> +  @param [in]  GenericSerialInfo  Pointer to a serial port info list.
> +  @param [in]  NodeCount          Count of serial ports to dispatch.
> +  @param [in]  SerialObjectId     Serial port object ID.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_ABORTED             An error occurred.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_NOT_FOUND           Not found.
> +  @retval EFI_UNSUPPORTED         Unsupported.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +ArmSerialPortInfoDispatch (
> +  IN  CONST FDT_HW_INFO_PARSER_HANDLE   FdtParserHandle,
> +  IN  CM_ARM_SERIAL_PORT_INFO         * GenericSerialInfo,
> +  IN  INT32                             NodeCount,
> +  IN  EARM_OBJECT_ID                    SerialObjectId
> +)
> +{
> +  EFI_STATUS                Status;
> +  CM_OBJ_DESCRIPTOR       * NewCmObjDesc;
> +
> +  if ((GenericSerialInfo == NULL) || (NodeCount == 0)) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if ((SerialObjectId != EArmObjSerialPortInfo) &&
> +      (SerialObjectId != EArmObjSerialDebugPortInfo) &&
> +      (SerialObjectId != EArmObjSerialConsolePortInfo)) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  // Dispatch the Generic Serial ports
> +  Status = CreateCmObjDesc (
> +             CREATE_CM_ARM_OBJECT_ID (SerialObjectId),
> +             NodeCount,
> +             GenericSerialInfo,
> +             sizeof (CM_ARM_SERIAL_PORT_INFO) * NodeCount,
> +             &NewCmObjDesc
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  // Add all the CmObjs to the Configuration Manager.
> +  Status = AddMultipleCmObj (FdtParserHandle, NewCmObjDesc, 0, NULL);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +  }
> +
> +  FreeCmObjDesc (NewCmObjDesc);
> +  return Status;
> +}
> +
> +/** CM_ARM_SERIAL_PORT_INFO parser function (for debug/console serial-port).
> +
> +  This parser expects FdtBranch to be the debug serial-port node.
> +  At most one CmObj is created.
> +  The following structure is populated:
> +  typedef struct CmArmSerialPortInfo {
> +    UINT64  BaseAddress;                      // {Populated}
> +    UINT32  Interrupt;                        // {Populated}
> +    UINT64  BaudRate;                         // {default}
> +    UINT32  Clock;                            // {Populated}
> +    UINT16  PortSubtype;                      // {Populated}
> +    UINT64  BaseAddressLength                 // {Populated}
> +  } CM_ARM_SERIAL_PORT_INFO;
> +
> +  A parser parses a Device Tree to populate a specific CmObj type. None,
> +  one or many CmObj can be created by the parser.
> +  The created CmObj are then handed to the parser's caller through the
> +  HW_INFO_ADD_OBJECT interface.
> +  This can also be a dispatcher. I.e. a function that not parsing a
> +  Device Tree but calling other parsers.
> +
> +  @param [in]  FdtParserHandle A handle to the parser instance.
> +  @param [in]  FdtBranch       When searching for DT node name, restrict
> +                               the search to this Device Tree branch.
> +  @param [in]  SerialObjectId  ArmNamespace Object ID for the serial port.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_ABORTED             An error occurred.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_NOT_FOUND           Not found.
> +  @retval EFI_UNSUPPORTED         Unsupported.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +ArmSerialPortInfoParser (
> +  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
> +  IN        INT32                     FdtBranch,
> +  IN        EARM_OBJECT_ID            SerialObjectId
> +  )
> +{
> +  EFI_STATUS                Status;
> +  CM_ARM_SERIAL_PORT_INFO   SerialInfo;
> +
> +  if ((SerialObjectId != EArmObjSerialDebugPortInfo) &&
> +      (SerialObjectId != EArmObjSerialConsolePortInfo)) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  ZeroMem (&SerialInfo, sizeof (SerialInfo));
> +
> +  Status = SerialPortNodeParser (
> +             FdtParserHandle->Fdt,
> +             FdtBranch,
> +             &SerialInfo
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  Status = ArmSerialPortInfoDispatch (
> +             FdtParserHandle,
> +             &SerialInfo,
> +             1,
> +             SerialObjectId
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +  }
> +
> +  return Status;
> +}
> +
> +/** SerialPort dispatcher.
> +
> +  This disptacher populates the CM_ARM_SERIAL_PORT_INFO structure for
> +  the following CM_OBJ_ID:
> +   - EArmObjSerialConsolePortInfo
> +   - EArmObjSerialDebugPortInfo
> +   - EArmObjSerialPortInfo
> +
> +  A parser parses a Device Tree to populate a specific CmObj type. None,
> +  one or many CmObj can be created by the parser.
> +  The created CmObj are then handed to the parser's caller through the
> +  HW_INFO_ADD_OBJECT interface.
> +  This can also be a dispatcher. I.e. a function that not parsing a
> +  Device Tree but calling other parsers.
> +
> +  @param [in]  FdtParserHandle A handle to the parser instance.
> +  @param [in]  FdtBranch       When searching for DT node name, restrict
> +                               the search to this Device Tree branch.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_ABORTED             An error occurred.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_NOT_FOUND           Not found.
> +  @retval EFI_UNSUPPORTED         Unsupported.
> +**/
> +EFI_STATUS
> +EFIAPI
> +SerialPortDispatcher (
> +  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
> +  IN        INT32                     FdtBranch
> +  )
> +{
> +  EFI_STATUS                Status;
> +  INT32                     SerialConsoleNode;
> +  INT32                     SerialDebugNode;
> +  INT32                     SerialNode;
> +  UINT32                    Index;
> +  UINT32                    SerialNodeCount;
> +  UINT32                    SerialNodesRemaining;
> +  CM_ARM_SERIAL_PORT_INFO * GenericSerialInfo;
> +  UINT32                    GenericSerialIndex;
> +  VOID                    * Fdt;
> +
> +  if (FdtParserHandle == NULL) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Fdt = FdtParserHandle->Fdt;
> +
> +  // Count the number of serial-ports.
> +  Status = FdtCountCompatNodeInBranch (
> +             Fdt,
> +             FdtBranch,
> +             &SerialCompatibleInfo,
> +             &SerialNodeCount
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  if (SerialNodeCount == 0) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  // Track remaining nodes separately as SerialNodeCount
> +  // is used in for loop below and reducing SerialNodeCount
> +  // would result in the Generic Serial port nodes not
> +  // being found if the serial console port node is among
> +  // the first few serial nodes.
> +  SerialNodesRemaining = SerialNodeCount;
> +
> +  // Identify the serial console port.
> +  Status = GetSerialConsoleNode (Fdt, &SerialConsoleNode);
> +  if (Status == EFI_NOT_FOUND) {
> +    // No serial console.
> +    SerialConsoleNode = -1;
> +  } else if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  } else {
> +    // Parse the console serial-port.
> +    Status = ArmSerialPortInfoParser (
> +               FdtParserHandle,
> +               SerialConsoleNode,
> +               EArmObjSerialConsolePortInfo
> +               );
> +    if (EFI_ERROR (Status)) {
> +      ASSERT (0);
> +      return Status;
> +    }
> +    SerialNodesRemaining--;
> +  }
> +
> +  GenericSerialInfo = NULL;
> +  if (SerialNodesRemaining > 1) {
> +    // We have more than one serial port remaining.
> +    // This means that the first serial port will
> +    // be reserved as a debug port, and the remaining
> +    // will be for general purpose use.
> +    SerialNodesRemaining--;
> +    GenericSerialInfo = AllocateZeroPool (
> +                          SerialNodesRemaining *
> +                          sizeof (CM_ARM_SERIAL_PORT_INFO)
> +                          );
> +    if (GenericSerialInfo == NULL) {
> +      ASSERT (0);
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +  }
> +
> +  SerialNode = FdtBranch;
> +  SerialDebugNode = -1;
> +  GenericSerialIndex = 0;
> +  for (Index = 0; Index < SerialNodeCount; Index++) {
> +    // Search the next serial-port node in the branch.
> +    Status = FdtGetNextCompatNodeInBranch (
> +               Fdt,
> +               FdtBranch,
> +               &SerialCompatibleInfo,
> +               &SerialNode
> +               );
> +    if (EFI_ERROR (Status)) {
> +      ASSERT (0);
> +      if (Status == EFI_NOT_FOUND) {
> +        // Should have found the node.
> +        Status = EFI_ABORTED;
> +      }
> +      goto exit_handler;
> +    }
> +
> +    // Ignore the serial console node.
> +    if (SerialNode == SerialConsoleNode) {
> +      continue;
> +    } else if (SerialDebugNode == -1) {
> +      // The first serial-port node, not being the console serial-port,
> +      // will be the debug serial-port.
> +      SerialDebugNode = SerialNode;
> +      Status = ArmSerialPortInfoParser (
> +                 FdtParserHandle,
> +                 SerialDebugNode,
> +                 EArmObjSerialDebugPortInfo
> +                 );
> +      if (EFI_ERROR (Status)) {
> +        ASSERT (0);
> +        goto exit_handler;
> +      }
> +    } else {
> +      if (GenericSerialInfo == NULL) {
> +        // Should not be possible.
> +        ASSERT (0);
> +        Status = EFI_ABORTED;
> +        goto exit_handler;
> +      }
> +
> +      Status = SerialPortNodeParser (
> +                 Fdt,
> +                 SerialNode,
> +                 &GenericSerialInfo[GenericSerialIndex++]
> +                 );
> +      if (EFI_ERROR (Status)) {
> +        ASSERT (0);
> +        goto exit_handler;
> +      }
> +    }
> +  } // for
> +
> +  if (GenericSerialIndex > 0) {
> +    Status = ArmSerialPortInfoDispatch (
> +               FdtParserHandle,
> +               GenericSerialInfo,
> +               GenericSerialIndex,
> +               EArmObjSerialPortInfo
> +               );
> +  }
> +
> +exit_handler:
> +  if (GenericSerialInfo != NULL) {
> +    FreePool (GenericSerialInfo);
> +  }
> +  return Status;
> +}
> diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h
> new file mode 100644
> index 000000000000..5e4707849e39
> --- /dev/null
> +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h
> @@ -0,0 +1,47 @@
> +/** @file
> +  Arm Serial Port Parser.
> +
> +  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Reference(s):
> +  - linux/Documentation/devicetree/bindings/serial/serial.yaml
> +  - linux/Documentation/devicetree/bindings/serial/8250.txt
> +**/
> +
> +#ifndef ARM_SERIAL_PORT_PARSER_H_
> +#define ARM_SERIAL_PORT_PARSER_H_
> +
> +/** SerialPort dispatcher.
> +
> +  This disptacher populates the CM_ARM_SERIAL_PORT_INFO structure for
> +  the following CM_OBJ_ID:
> +   - EArmObjSerialConsolePortInfo
> +   - EArmObjSerialDebugPortInfo
> +   - EArmObjSerialPortInfo
> +
> +  A parser parses a Device Tree to populate a specific CmObj type. None,
> +  one or many CmObj can be created by the parser.
> +  The created CmObj are then handed to the parser's caller through the
> +  HW_INFO_ADD_OBJECT interface.
> +  This can also be a dispatcher. I.e. a function that not parsing a
> +  Device Tree but calling other parsers.
> +
> +  @param [in]  FdtParserHandle A handle to the parser instance.
> +  @param [in]  FdtBranch       When searching for DT node name, restrict
> +                               the search to this Device Tree branch.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_ABORTED             An error occurred.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_NOT_FOUND           Not found.
> +  @retval EFI_UNSUPPORTED         Unsupported.
> +**/
> +EFI_STATUS
> +EFIAPI
> +SerialPortDispatcher (
> +  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
> +  IN        INT32                     FdtBranch
> +  );
> +
> +#endif // ARM_SERIAL_PORT_PARSER_H_


[-- Attachment #2: Type: text/html, Size: 25430 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v1 07/14] DynamicTablesPkg: FdtHwInfoParser: Add GICC parser
  2021-06-23 12:38 ` [PATCH v1 07/14] DynamicTablesPkg: FdtHwInfoParser: Add GICC parser PierreGondois
@ 2021-11-05 14:28   ` Sami Mujawar
  0 siblings, 0 replies; 20+ messages in thread
From: Sami Mujawar @ 2021-11-05 14:28 UTC (permalink / raw)
  To: Pierre.Gondois, devel, Alexei Fedorov; +Cc: Akanksha Jain, Alexandru Elisei, nd

Hi Pierre,

Please find my feedback inline marked [SAMI].

Regards,

Sami Mujawar

On 23/06/2021 01:38 PM, Pierre.Gondois@arm.com wrote:
> From: Pierre Gondois <Pierre.Gondois@arm.com>
>
> The GIC CPU Interface (GICC) structure is part of the Multiple
> APIC Description Table (MADT) that describes the interrupt model
> for the platform. The MADT table is a mandatory table required
> for booting a standards-based operating system.
>
> Arm requires the GIC interrupt model, in which the logical
> processors are required to have a Processor Device object in
> the DSDT, and must convey each processor’s GIC information to
> the OS using the GICC structure.
>
> The CPU and GIC information is described in the platform Device
> Tree, the bindings for which can be found at:
>   - linux/Documentation/devicetree/bindings/arm/cpus.yaml
>   - linux/Documentation/devicetree/bindings/interrupt-controller/
>     arm,gic.yaml
>   - linux/Documentation/devicetree/bindings/interrupt-controller/
>     arm,gic-v3.yaml
>
> The FdtHwInfoParser implements a GIC CPU Interface Parser that
> parses the platform Device Tree to create CM_ARM_GICC_INFO
> objects which are encapsulated in a Configuration Manager
> descriptor object and added to the platform information
> repository.
>
> The platform Configuration Manager can then utilise this
> information when generating the MADT and the SSDT CPU
> information tables.
>
> Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
> Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
> ---
>   .../FdtHwInfoParserLib/Gic/ArmGicCParser.c    | 762 ++++++++++++++++++
>   .../FdtHwInfoParserLib/Gic/ArmGicCParser.h    |  67 ++
>   2 files changed, 829 insertions(+)
>   create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.c
>   create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.h
>
> diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.c b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.c
> new file mode 100644
> index 000000000000..2163888c870e
> --- /dev/null
> +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.c
> @@ -0,0 +1,762 @@
> +/** @file
> +  Arm Gic cpu parser.
> +
> +  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Reference(s):
> +  - linux/Documentation/devicetree/bindings/arm/cpus.yaml
> +  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
> +  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
> +**/
> +
> +#include "FdtHwInfoParser.h"
> +#include "CmObjectDescUtility.h"
> +#include "Gic/ArmGicCParser.h"
> +#include "Gic/ArmGicDispatcher.h"
> +
> +/** List of "compatible" property values for CPU nodes.
> +
> +  Any other "compatible" value is not supported by this module.
> +*/
> +STATIC CONST COMPATIBILITY_STR CpuCompatibleStr[] = {
> +  {"arm,arm-v7"},
> +  {"arm,arm-v8"},
> +  {"arm,cortex-a15"},
> +  {"arm,cortex-a7"},
> +  {"arm,cortex-a57"}
> +};
> +
> +/** COMPATIBILITY_INFO structure for CPU nodes.
> +*/
> +STATIC CONST COMPATIBILITY_INFO CpuCompatibleInfo = {
> +  ARRAY_SIZE (CpuCompatibleStr),
> +  CpuCompatibleStr
> +};
> +
> +/** Parse a "cpu" node.
> +
> +  @param [in]  Fdt              Pointer to a Flattened Device Tree (Fdt).
> +  @param [in]  CpuNode          Offset of a cpu node.
> +  @param [in]  GicVersion       Version of the GIC.
> +  @param [in]  AddressCells     Number of address cells used for the reg
> +                                property.
> +  @param [out] GicCInfo         CM_ARM_GICC_INFO structure to populate.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_ABORTED             An error occurred.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_UNSUPPORTED         Unsupported.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +CpuNodeParser (
> +  IN  CONST VOID              * Fdt,
> +  IN        INT32               CpuNode,
> +  IN        UINT32              GicVersion,
> +  IN        UINT32              AddressCells,
> +  OUT       CM_ARM_GICC_INFO  * GicCInfo
> +)
> +{
> +  UINT32          CpuRegProp;
> +  CONST VOID    * Data;
> +  INT32           DataSize;
> +  UINT64          MpIdr;
> +
> +  if (GicCInfo == NULL) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Data = fdt_getprop (Fdt, CpuNode, "reg", &DataSize);
> +  if ((Data == NULL)                  ||
> +      ((DataSize != sizeof (UINT32))  &&
> +       (DataSize != sizeof (UINT64)))) {
> +    ASSERT (0);
> +    return EFI_ABORTED;
> +  }
> +
> +  if (AddressCells == 1) {
> +    MpIdr = fdt32_to_cpu (*((UINT32*)Data));
> +  } else if (AddressCells == 2) {
> +    MpIdr = fdt64_to_cpu (*((UINT64*)Data));
> +  } else {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  // Currently we only support 3 affinity levels.
> +  if ((MpIdr & ~(ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2)) != 0) {
> +    ASSERT (0);
> +    return EFI_UNSUPPORTED;
> +  }
[SAMI] Is there a reason we are not supporting AFF3?
> +
> +  CpuRegProp = (MpIdr & (ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2));
> +
> +  /* ACPI 6.3, s5.2.12.14 GIC CPU Interface (GICC) Structure:
> +     GIC 's CPU Interface Number. In GICv1/v2 implementations,
> +     this value matches the bit index of the associated processor
> +     in the GIC distributor's GICD_ITARGETSR register. For
> +     GICv3/4 implementations this field must be provided by the
> +     platform, if compatibility mode is supported. If it is not supported
> +     by the implementation, then this field must be zero.
> +
> +     Note: We do not support compatibility mode for GicV3
> +  */
> +  if (GicVersion == 2) {
> +    GicCInfo->CPUInterfaceNumber = CpuRegProp;
> +  } else {
> +    GicCInfo->CPUInterfaceNumber = 0;
> +  }
> +
> +  GicCInfo->AcpiProcessorUid = CpuRegProp;
> +  GicCInfo->Flags = EFI_ACPI_6_3_GIC_ENABLED;
> +  GicCInfo->MPIDR= MpIdr;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/** Parse a "cpus" node and its children "cpu" nodes.
> +
> +  Create as many CM_ARM_GICC_INFO structures as "cpu" nodes.
> +
> +  @param [in]  Fdt              Pointer to a Flattened Device Tree (Fdt).
> +  @param [in]  CpusNode         Offset of a cpus node.
> +  @param [in]  GicVersion       Version of the GIC.
> +  @param [out] NewGicCmObjDesc  If success, CM_OBJ_DESCRIPTOR containing
> +                                all the created CM_ARM_GICC_INFO.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_ABORTED             An error occurred.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_UNSUPPORTED         Unsupported.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +CpusNodeParser (
> +  IN  CONST VOID                * Fdt,
> +  IN        INT32                 CpusNode,
> +  IN        UINT32                GicVersion,
> +  OUT       CM_OBJ_DESCRIPTOR  ** NewGicCmObjDesc
> +  )
> +{
> +  EFI_STATUS            Status;
> +  INT32                 CpuNode;
> +  UINT32                CpuNodeCount;
> +  INT32                 AddressCells;
> +
> +  UINT32                Index;
> +  CM_ARM_GICC_INFO    * GicCInfoBuffer;
> +  UINT32                GicCInfoBufferSize;
> +
> +  if (NewGicCmObjDesc == NULL) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  AddressCells = fdt_address_cells (Fdt, CpusNode);
> +  if (AddressCells < 0) {
> +    ASSERT (0);
> +    return EFI_ABORTED;
> +  }
> +
> +  // Count the number of "cpu" nodes under the "cpus" node.
> +  Status = FdtCountNamedNodeInBranch (Fdt, CpusNode, "cpu", &CpuNodeCount);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  if (CpuNodeCount == 0) {
> +    ASSERT (0);
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  // Allocate memory for CpuNodeCount CM_ARM_GICC_INFO structures.
> +  GicCInfoBufferSize = CpuNodeCount * sizeof (CM_ARM_GICC_INFO);
> +  GicCInfoBuffer = AllocateZeroPool (GicCInfoBufferSize);
> +  if (GicCInfoBuffer == NULL) {
> +    ASSERT (0);
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  CpuNode = CpusNode;
> +  for (Index = 0; Index < CpuNodeCount; Index++) {
> +    Status = FdtGetNextNamedNodeInBranch (Fdt, CpusNode, "cpu", &CpuNode);
> +    if (EFI_ERROR (Status)) {
> +      ASSERT (0);
> +      if (Status == EFI_NOT_FOUND) {
> +        // Should have found the node.
> +        Status = EFI_ABORTED;
> +      }
> +      goto exit_handler;
> +    }
> +
> +    // Parse the "cpu" node.
> +    if (!FdtNodeIsCompatible (Fdt, CpuNode, &CpuCompatibleInfo)) {
> +      ASSERT (0);
> +      Status = EFI_UNSUPPORTED;
> +      goto exit_handler;
> +    }
> +
> +    Status = CpuNodeParser (
> +                Fdt,
> +                CpuNode,
> +                GicVersion,
> +                AddressCells,
> +                &GicCInfoBuffer[Index]
> +                );
[SAMI] Please adjust code alignment.
> +    if (EFI_ERROR (Status)) {
> +      ASSERT (0);
> +      goto exit_handler;
> +    }
> +  } // for
> +
> +  Status = CreateCmObjDesc (
> +             CREATE_CM_ARM_OBJECT_ID (EArmObjGicCInfo),
> +             CpuNodeCount,
> +             GicCInfoBuffer,
> +             GicCInfoBufferSize,
> +             NewGicCmObjDesc
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +  }
> +
> +exit_handler:
> +  FreePool (GicCInfoBuffer);
> +  return Status;
> +}
> +
> +/** Parse a Gic compatible interrupt-controller node,
> +    extracting GicC information generic to Gic v2 and v3.
> +
> +  This function modifies a CM_OBJ_DESCRIPTOR object.
> +  The following CM_ARM_GICC_INFO fields are patched:
> +    - VGICMaintenanceInterrupt;
> +    - Flags;
> +
> +  @param [in]       Fdt              Pointer to a Flattened Device Tree (Fdt).
> +  @param [in]       GicIntcNode      Offset of a Gic compatible
> +                                     interrupt-controller node.
> +  @param [in, out]  GicCCmObjDesc    The CM_ARM_GICC_INFO to patch.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_ABORTED             An error occurred.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +GicCIntcNodeParser (
> +  IN      CONST VOID                * Fdt,
> +  IN            INT32                 GicIntcNode,
> +  IN  OUT       CM_OBJ_DESCRIPTOR   * GicCCmObjDesc
> +  )
> +{
> +  EFI_STATUS          Status;
> +  INT32               IntCells;
> +  CM_ARM_GICC_INFO  * GicCInfo;
> +
> +  CONST UINT8       * Data;
> +  INT32               DataSize;
> +
> +  if (GicCCmObjDesc == NULL) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  // Get the number of cells used to encode an interrupt.
> +  Status = FdtGetInterruptCellsInfo (Fdt, GicIntcNode, &IntCells);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  // Get the GSIV maintenance interrupt.
> +  // According to the DT bindings, this could be the:
> +  // "Interrupt source of the parent interrupt controller on secondary GICs"
> +  // but it is assumed that only one Gic is available.
> +  Data = fdt_getprop (Fdt, GicIntcNode, "interrupts", &DataSize);
> +  if ((Data != NULL) && (DataSize == (IntCells * sizeof (UINT32)))) {
> +    GicCInfo = (CM_ARM_GICC_INFO*)GicCCmObjDesc->Data;
> +    GicCInfo->VGICMaintenanceInterrupt =
> +      FdtGetInterruptId ((CONST UINT32*)Data);
> +    GicCInfo->Flags = DT_IRQ_IS_EDGE_TRIGGERED (
> +                          fdt32_to_cpu (((UINT32*)Data)[IRQ_FLAGS_OFFSET])
> +                        ) ?
> +                        EFI_ACPI_6_3_VGIC_MAINTENANCE_INTERRUPT_MODE_FLAGS :
> +                        0;
> +    return Status;
> +  } else if (DataSize < 0) {
> +    // This property is optional and was not found. Just return.
> +    return Status;
> +  }
> +  // The property exists and its size doesn't match for one interrupt.
> +  ASSERT (0);
> +  return EFI_ABORTED;
> +}
> +
> +/** Parse a Gic compatible interrupt-controller node,
> +    extracting GicCv2 information.
> +
> +  This function modifies a CM_OBJ_DESCRIPTOR object.
> +  The following CM_ARM_GICC_INFO fields are patched:
> +    - PhysicalAddress;
> +    - GICH;
> +    - GICV;
> +
> +  @param [in]       Fdt              Pointer to a Flattened Device Tree (Fdt).
> +  @param [in]       Gicv2IntcNode    Offset of a Gicv2 compatible
> +                                     interrupt-controller node.
> +  @param [in, out]  GicCCmObjDesc    The CM_ARM_GICC_INFO to patch.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_ABORTED             An error occurred.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +GicCv2IntcNodeParser (
> +  IN      CONST VOID                * Fdt,
> +  IN            INT32                 Gicv2IntcNode,
> +  IN  OUT       CM_OBJ_DESCRIPTOR   * GicCCmObjDesc
> +  )
> +{
> +  EFI_STATUS          Status;
> +  UINT32              Index;
> +  CM_ARM_GICC_INFO  * GicCInfo;
> +  INT32               AddressCells;
> +  INT32               SizeCells;
> +
> +  CONST UINT8       * GicCValue;
> +  CONST UINT8       * GicVValue;
> +  CONST UINT8       * GicHValue;
> +
> +  CONST UINT8       * Data;
> +  INT32               DataSize;
> +  UINT32              RegSize;
> +  UINT32              RegCount;
> +
> +  if (GicCCmObjDesc == NULL) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  GicCInfo = (CM_ARM_GICC_INFO*)GicCCmObjDesc->Data;
> +  GicVValue = NULL;
> +  GicHValue = NULL;
> +
> +  // Get the #address-cells and #size-cells property values.
> +  Status = FdtGetParentAddressInfo (
> +             Fdt,
> +             Gicv2IntcNode,
> +             &AddressCells,
> +             &SizeCells
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  // Don't support more than 64 bits and less than 32 bits addresses.
> +  if ((AddressCells < 1)  ||
> +      (AddressCells > 2)  ||
> +      (SizeCells < 1)     ||
> +      (SizeCells > 2)) {
> +    ASSERT (0);
> +    return EFI_ABORTED;
> +  }
> +
> +  RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
> +
> +  Data = fdt_getprop (Fdt, Gicv2IntcNode, "reg", &DataSize);
> +  if ((Data == NULL)  ||
> +      (DataSize < 0)  ||
> +      ((DataSize % RegSize) != 0)) {
> +    // If error or wrong size.
> +    ASSERT (0);
> +    return EFI_ABORTED;
> +  }
> +
> +  RegCount = DataSize/RegSize;
> +
> +  switch (RegCount) {
> +    case 4:
> +    {
> +      // GicV is at index 3 in the reg property. GicV is optional.
> +      GicVValue = Data + (sizeof (UINT32) *
> +        GET_DT_REG_ADDRESS_OFFSET (3, AddressCells, SizeCells));
> +      // fall-through.
> +    }
> +    case 3:
> +    {
> +      // GicH is at index 2 in the reg property. GicH is optional.
> +      GicHValue = Data + (sizeof (UINT32) *
> +        GET_DT_REG_ADDRESS_OFFSET (2, AddressCells, SizeCells));
> +      // fall-through.
> +    }
> +    case 2:
> +    {
> +      // GicC is at index 1 in the reg property. GicC is mandatory.
> +      GicCValue = Data + (sizeof (UINT32) *
> +        GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, SizeCells));
> +      break;
> +    }
> +    default:
> +    {
> +      // Not enough or too much information.
> +      ASSERT (0);
> +      return EFI_ABORTED;
> +    }
> +  }
> +
> +  // Patch the relevant fields of the CM_ARM_GICC_INFO objects.
> +  for (Index = 0; Index < GicCCmObjDesc->Count; Index++) {
> +    if (AddressCells == 2) {
> +      GicCInfo[Index].PhysicalBaseAddress = fdt64_to_cpu (*(UINT64*)GicCValue);
> +      GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
> +                              fdt64_to_cpu (*(UINT64*)GicHValue);
> +      GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
> +                              fdt64_to_cpu (*(UINT64*)GicVValue);
> +    } else {
> +      GicCInfo[Index].PhysicalBaseAddress = fdt32_to_cpu (*(UINT32*)GicCValue);
> +      GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
> +                              fdt32_to_cpu (*(UINT32*)GicHValue);
> +      GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
> +                              fdt32_to_cpu (*(UINT32*)GicVValue);
> +    }
> +  } // for
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/** Parse a Gic compatible interrupt-controller node,
> +    extracting GicCv3 information.
> +
> +  This function modifies a CM_OBJ_DESCRIPTOR object.
> +  The following CM_ARM_GICC_INFO fields are patched:
> +    - PhysicalAddress;
> +    - GICH;
> +    - GICV;
> +
> +  @param [in]       Fdt              Pointer to a Flattened Device Tree (Fdt).
> +  @param [in]       Gicv3IntcNode    Offset of a Gicv3 compatible
> +                                     interrupt-controller node.
> +  @param [in, out]  GicCCmObjDesc    The CM_ARM_GICC_INFO to patch.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_ABORTED             An error occurred.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +GicCv3IntcNodeParser (
> +  IN      CONST VOID                * Fdt,
> +  IN            INT32                 Gicv3IntcNode,
> +  IN  OUT       CM_OBJ_DESCRIPTOR   * GicCCmObjDesc
> +  )
> +{
> +  EFI_STATUS          Status;
> +  UINT32              Index;
> +  CM_ARM_GICC_INFO  * GicCInfo;
> +  INT32               AddressCells;
> +  INT32               SizeCells;
> +  UINT32              AdditionalRedistReg;
> +
> +  CONST UINT8       * GicCValue;
> +  CONST UINT8       * GicVValue;
> +  CONST UINT8       * GicHValue;
> +
> +  CONST UINT8       * Data;
> +  INT32               DataSize;
> +  UINT32              RegSize;
> +  UINT32              RegCount;
> +
> +  if (GicCCmObjDesc == NULL) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  GicCInfo = (CM_ARM_GICC_INFO*)GicCCmObjDesc->Data;
> +  GicCValue = NULL;
> +  GicVValue = NULL;
> +  GicHValue = NULL;
> +
> +  // Get the #address-cells and #size-cells property values.
> +  Status = FdtGetParentAddressInfo (
> +             Fdt,
> +             Gicv3IntcNode,
> +             &AddressCells,
> +             &SizeCells
> +             );
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  // Don't support more than 64 bits and less than 32 bits addresses.
> +  if ((AddressCells < 1)  ||
> +      (AddressCells > 2)  ||
> +      (SizeCells < 1)     ||
> +      (SizeCells > 2)) {
> +    ASSERT (0);
> +    return EFI_ABORTED;
> +  }
> +
> +  // The "#redistributor-regions" property is optional.
> +  Data = fdt_getprop (Fdt, Gicv3IntcNode, "#redistributor-regions", &DataSize);
> +  if ((Data != NULL) && (DataSize == sizeof (UINT32))) {
> +    ASSERT (fdt32_to_cpu (*(UINT32*)Data) > 1);
> +    AdditionalRedistReg = fdt32_to_cpu (*(UINT32*)Data) - 1;
> +  } else {
> +    AdditionalRedistReg = 0;
> +  }
> +
> +  RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
> +
> +  /*
> +    Ref: linux/blob/master/Documentation/devicetree/bindings/
> +         interrupt-controller/arm%2Cgic-v3.yaml
> +
> +    reg:
> +    description: |
> +      Specifies base physical address(s) and size of the GIC
> +      registers, in the following order:
> +      - GIC Distributor interface (GICD)
> +      - GIC Redistributors (GICR), one range per redistributor region
> +      - GIC CPU interface (GICC)
> +      - GIC Hypervisor interface (GICH)
> +      - GIC Virtual CPU interface (GICV)
> +      GICC, GICH and GICV are optional.
> +    minItems: 2
> +    maxItems: 4096
> +  */
> +  Data = fdt_getprop (Fdt, Gicv3IntcNode, "reg", &DataSize);
> +  if ((Data == NULL)  ||
> +      (DataSize < 0)  ||
> +      ((DataSize % RegSize) != 0)) {
> +    // If error or wrong size.
> +    ASSERT (0);
> +    return EFI_ABORTED;
> +  }
> +
> +  RegCount = (DataSize / RegSize) - AdditionalRedistReg;
> +
> +  // The GicD and GicR info is mandatory.
> +  switch (RegCount) {
> +    case 5:
> +    {
> +      // GicV is at index 4 in the reg property. GicV is optional.
> +      GicVValue = Data + (sizeof (UINT32) *
> +        GET_DT_REG_ADDRESS_OFFSET (
> +          4 + AdditionalRedistReg,
> +          AddressCells,
> +          SizeCells
> +          ));
> +      // fall-through.
> +    }
> +    case 4:
> +    {
> +      // GicH is at index 3 in the reg property. GicH is optional.
> +      GicHValue = Data + (sizeof (UINT32) *
> +        GET_DT_REG_ADDRESS_OFFSET (
> +          3 + AdditionalRedistReg,
> +          AddressCells,
> +          SizeCells
> +          ));
> +      // fall-through.
> +    }
> +    case 3:
> +    {
> +      // GicC is at index 2 in the reg property. GicC is optional.
> +      // Even though GicC is optional, it is made mandatory in this parser.
> +      GicCValue = Data + (sizeof (UINT32) *
> +        GET_DT_REG_ADDRESS_OFFSET (
> +          2 + AdditionalRedistReg,
> +          AddressCells,
> +          SizeCells
> +          ));
> +      // fall-through
> +    }
> +    case 2:
> +    {
> +      // GicR is discribed by the CM_ARM_GIC_REDIST_INFO object.
> +      // GicD is described by the CM_ARM_GICD_INFO object.
> +      break;
> +    }
> +    default:
> +    {
> +      // Not enough or too much information.
> +      ASSERT (0);
> +      return EFI_ABORTED;
> +    }
> +  }
> +
> +  // Patch the relevant fields of the CM_ARM_GICC_INFO objects.
> +  if (AddressCells == 2) {
> +    for (Index = 0; Index < GicCCmObjDesc->Count; Index++) {
> +      // GicR is discribed by the CM_ARM_GIC_REDIST_INFO object.
> +      GicCInfo[Index].GICRBaseAddress = 0;
> +      GicCInfo[Index].PhysicalBaseAddress = (GicCValue == NULL) ? 0 :
> +                              fdt64_to_cpu (*(UINT64*)GicCValue);
> +      GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
> +                              fdt64_to_cpu (*(UINT64*)GicHValue);
> +      GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
> +                              fdt64_to_cpu (*(UINT64*)GicVValue);
> +    }
> +  } else {
> +    for (Index = 0; Index < GicCCmObjDesc->Count; Index++) {
> +      // GicR is discribed by the CM_ARM_GIC_REDIST_INFO object.
> +      GicCInfo[Index].GICRBaseAddress = 0;
> +      GicCInfo[Index].PhysicalBaseAddress = (GicCValue == NULL) ? 0 :
> +                              fdt32_to_cpu (*(UINT32*)GicCValue);
> +      GicCInfo[Index].GICH = (GicHValue == NULL) ? 0 :
> +                              fdt32_to_cpu (*(UINT32*)GicHValue);
> +      GicCInfo[Index].GICV = (GicVValue == NULL) ? 0 :
> +                              fdt32_to_cpu (*(UINT32*)GicVValue);
> +    }
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/** CM_ARM_GICC_INFO parser function.
> +
> +  This parser expects FdtBranch to be the "\cpus" node node.
> +  At most one CmObj is created.
> +  The following structure is populated:
> +  typedef struct CmArmGicCInfo {
> +    UINT32  CPUInterfaceNumber;               // {Populated}
> +    UINT32  AcpiProcessorUid;                 // {Populated}
> +    UINT32  Flags;                            // {Populated}
> +    UINT32  ParkingProtocolVersion;           // {default = 0}
> +    UINT32  PerformanceInterruptGsiv;         // {default = 0}
> +    UINT64  ParkedAddress;                    // {default = 0}
> +    UINT64  PhysicalBaseAddress;              // {Populated}
> +    UINT64  GICV;                             // {Populated}
> +    UINT64  GICH;                             // {Populated}
> +    UINT32  VGICMaintenanceInterrupt;         // {Populated}
> +    UINT64  GICRBaseAddress;                  // {default = 0}
> +    UINT64  MPIDR;                            // {Populated}
> +    UINT8   ProcessorPowerEfficiencyClass;    // {default = 0}
> +    UINT16  SpeOverflowInterrupt;             // {default = 0}
> +    UINT32  ProximityDomain;                  // {default = 0}
> +    UINT32  ClockDomain;                      // {default = 0}
> +    UINT32  AffinityFlags;                    // {default = 0}
> +  } CM_ARM_GICC_INFO;
> +
> +  The pmu information can be found in the pmu node. There is no support
> +  for now.
> +
> +  A parser parses a Device Tree to populate a specific CmObj type. None,
> +  one or many CmObj can be created by the parser.
> +  The created CmObj are then handed to the parser's caller through the
> +  HW_INFO_ADD_OBJECT interface.
> +  This can also be a dispatcher. I.e. a function that not parsing a
> +  Device Tree but calling other parsers.
> +
> +  @param [in]  FdtParserHandle A handle to the parser instance.
> +  @param [in]  FdtBranch       When searching for DT node name, restrict
> +                               the search to this Device Tree branch.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_ABORTED             An error occurred.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_NOT_FOUND           Not found.
> +  @retval EFI_UNSUPPORTED         Unsupported.
> +**/
> +EFI_STATUS
> +EFIAPI
> +ArmGicCInfoParser (
> +  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
> +  IN        INT32                     FdtBranch
> +  )
> +{
> +  EFI_STATUS            Status;
> +  INT32                 IntcNode;
> +  UINT32                GicVersion;
> +  CM_OBJ_DESCRIPTOR   * NewCmObjDesc;
> +  VOID                * Fdt;
> +
> +  if (FdtParserHandle == NULL) {
> +    ASSERT (0);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  Fdt = FdtParserHandle->Fdt;
> +  NewCmObjDesc = NULL;
> +
> +  // The FdtBranch points to the Cpus Node.
> +  // Get the interrupt-controller node associated to the "cpus" node.
> +  Status = FdtGetIntcParentNode (Fdt, FdtBranch, &IntcNode);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    if (Status == EFI_NOT_FOUND) {
> +      // Should have found the node.
> +      Status = EFI_ABORTED;
> +    }
> +    goto exit_handler;
[SAMI] Return directly from here as memory for NewCmObjDescis not yet 
allocated.
> +  }
> +
> +  Status = GetGicVersion (Fdt, IntcNode, &GicVersion);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    goto exit_handler;
[SAMI] Return directly from here as memory for NewCmObjDescis not yet 
allocated.
> +  }
> +
> +  // Parse the "cpus" nodes and its children "cpu" nodes,
> +  // and create a CM_OBJ_DESCRIPTOR.
> +  Status = CpusNodeParser (Fdt, FdtBranch, GicVersion, &NewCmObjDesc);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return Status;
> +  }
> +
> +  // Parse the interrupt-controller node according to the Gic version.
> +  switch (GicVersion) {
> +    case 2:
> +    {
> +      Status = GicCv2IntcNodeParser (Fdt, IntcNode, NewCmObjDesc);
> +      break;
> +    }
> +    case 3:
> +    {
> +      Status = GicCv3IntcNodeParser (Fdt, IntcNode, NewCmObjDesc);
> +      break;
> +    }
> +    default:
> +    {
> +      // Unsupported Gic version.
> +      ASSERT (0);
> +      Status = EFI_UNSUPPORTED;
> +    }
> +  }
> +
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    goto exit_handler;
> +  }
> +
> +  // Parse the Gic information common to Gic v2 and v3.
> +  Status = GicCIntcNodeParser (Fdt, IntcNode, NewCmObjDesc);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    goto exit_handler;
> +  }
> +
> +  // Add all the CmObjs to the Configuration Manager.
> +  Status = AddMultipleCmObj (FdtParserHandle, NewCmObjDesc, 0, NULL);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    goto exit_handler;
> +  }
> +
> +exit_handler:
> +  FreeCmObjDesc (NewCmObjDesc);
> +  return Status;
> +}
> diff --git a/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.h b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.h
> new file mode 100644
> index 000000000000..10e6b03c541f
> --- /dev/null
> +++ b/DynamicTablesPkg/Library/FdtHwInfoParserLib/Gic/ArmGicCParser.h
> @@ -0,0 +1,67 @@
> +/** @file
> +  Arm Gic cpu parser.
> +
> +  Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Reference(s):
> +  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
> +  - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
> +**/
> +
> +#ifndef ARM_GICC_PARSER_H_
> +#define ARM_GICC_PARSER_H_
> +
> +/** CM_ARM_GICC_INFO parser function.
> +
> +  This parser expects FdtBranch to be the "\cpus" node node.
> +  At most one CmObj is created.
> +  The following structure is populated:
> +  typedef struct CmArmGicCInfo {
> +    UINT32  CPUInterfaceNumber;               // {Populated}
> +    UINT32  AcpiProcessorUid;                 // {Populated}
> +    UINT32  Flags;                            // {Populated}
> +    UINT32  ParkingProtocolVersion;           // {default = 0}
> +    UINT32  PerformanceInterruptGsiv;         // {default = 0}
> +    UINT64  ParkedAddress;                    // {default = 0}
> +    UINT64  PhysicalBaseAddress;              // {Populated}
> +    UINT64  GICV;                             // {Populated}
> +    UINT64  GICH;                             // {Populated}
> +    UINT32  VGICMaintenanceInterrupt;         // {Populated}
> +    UINT64  GICRBaseAddress;                  // {default = 0}
> +    UINT64  MPIDR;                            // {Populated}
> +    UINT8   ProcessorPowerEfficiencyClass;    // {default = 0}
> +    UINT16  SpeOverflowInterrupt;             // {default = 0}
> +    UINT32  ProximityDomain;                  // {default = 0}
> +    UINT32  ClockDomain;                      // {default = 0}
> +    UINT32  AffinityFlags;                    // {default = 0}
> +  } CM_ARM_GICC_INFO;
> +
> +  The pmu information can be found in the pmu node. There is no support
> +  for now.
> +
> +  A parser parses a Device Tree to populate a specific CmObj type. None,
> +  one or many CmObj can be created by the parser.
> +  The created CmObj are then handed to the parser's caller through the
> +  HW_INFO_ADD_OBJECT interface.
> +  This can also be a dispatcher. I.e. a function that not parsing a
> +  Device Tree but calling other parsers.
> +
> +  @param [in]  FdtParserHandle A handle to the parser instance.
> +  @param [in]  FdtBranch       When searching for DT node name, restrict
> +                               the search to this Device Tree branch.
> +
> +  @retval EFI_SUCCESS             The function completed successfully.
> +  @retval EFI_ABORTED             An error occurred.
> +  @retval EFI_INVALID_PARAMETER   Invalid parameter.
> +  @retval EFI_NOT_FOUND           Not found.
> +  @retval EFI_UNSUPPORTED         Unsupported.
> +**/
> +EFI_STATUS
> +EFIAPI
> +ArmGicCInfoParser (
> +  IN  CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
> +  IN        INT32                     FdtBranch
> +  );
> +
> +#endif // ARM_GICC_PARSER_H_


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v1 06/14] DynamicTablesPkg: FdtHwInfoParser: Add Serial port parser
  2021-11-05 14:27   ` Sami Mujawar
@ 2021-11-18 10:11     ` PierreGondois
  0 siblings, 0 replies; 20+ messages in thread
From: PierreGondois @ 2021-11-18 10:11 UTC (permalink / raw)
  To: Sami Mujawar, devel; +Cc: nd

Hi Sami,

Please find my answer inlined:

On 11/5/21 14:27, Sami Mujawar wrote:
>
> Hi Pierre,
>
> Please find my response inline marked [SAMI].
>
> Regards,
>
> Sami Mujawar
>
>
> On 23/06/2021 01:38 PM, Pierre.Gondois@arm.com wrote:
>> From: Pierre Gondois <Pierre.Gondois@arm.com>
>>
>> The Microsoft Debug Port Table 2 (DBG2), the Serial Port Console
>> Redirector (SPCR) table are mandatory tables required for booting
>> a standards-based operating system. The DBG2 table is used by the
>> OS debugger while the SPCR table is used to configure the serial
>> terminal. Additionally, the serial ports available on a platform
>> for generic use also need to be described in DSDT/SSDT for an OS
>> to be able to use the serial ports.
>>
>> The Arm Base System Architecture 1.0 specification a lists of
>> supported serial port hardware for Arm Platforms. This list
>> includes the following serial port UARTs:
>>  - SBSA/Generic UART
>>  - a fully 16550 compatible UART.
>> Along, with these the PL011 UART is the most commonly used serial
>> port hardware on Arm platforms.
>>
>> The serial port hardware information is described in the platform
>> Device Tree, the bindings for which can be found at:
>>  - linux/Documentation/devicetree/bindings/serial/serial.yaml
>>  - linux/Documentation/devicetree/bindings/serial/8250.txt
>>  - linux/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt
>>  - linux/Documentation/devicetree/bindings/serial/pl011.yaml
>>
>> The FdtHwInfoParser implements a Serial Port Parser that parses
>> the platform Device Tree to create CM_ARM_SERIAL_PORT_INFO objects
>> with the following IDs:
>>  - EArmObjSerialConsolePortInfo (for use by SPCR)
>>  - EArmObjSerialDebugPortInfo (for use by DBG2)
>>  - EArmObjSerialPortInfo (for use as generic Serial Ports)
>>
>> The Serial Port for use by SPCR is selected by parsing the Device
>> Tree for the '/chosen' node with the 'stdout-path' property. The
>> next Serial Port is selected for use as the Debug Serial Port and
>> the remaining serial ports are used as generic serial ports.
>>
>> The CM_ARM_SERIAL_PORT_INFO objects are encapsulated in Configuration
>> Manager descriptor objects with the respective IDs and are added to
>> the platform information repository.
>>
>> The platform Configuration Manager can then utilise this information
>> when generating the DBG2, SPCR and the SSDT serial port tables.
>>
>> Signed-off-by: Pierre Gondois <Pierre.Gondois@arm.com>
>> Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
>> ---
>>  .../Serial/ArmSerialPortParser.c              | 621 ++++++++++++++++++
>>  .../Serial/ArmSerialPortParser.h              |  47 ++
>>  2 files changed, 668 insertions(+)
>>  create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.c
>>  create mode 100644 DynamicTablesPkg/Library/FdtHwInfoParserLib/Serial/ArmSerialPortParser.h
[snip]
>> +
>> +  SerialPortInfo->Interrupt = FdtGetInterruptId ((CONST UINT32*)Data);
>> +
>> +  // Note: clock-frequency is optional for SBSA UART.
>> +  Data = fdt_getprop (Fdt, SerialPortNode, "clock-frequency", &DataSize);
>> +  if (Data != NULL) {
>> +    if (DataSize < sizeof (UINT32)) {
>> +      // If error or not enough space.
>> +      ASSERT (0);
>> +      return EFI_ABORTED;
>> +    } else if (fdt_node_offset_by_phandle (Fdt, fdt32_to_cpu (*Data)) >= 0) {
>> +      // "clock-frequency" can be a "clocks phandle to refer to the clk used".
>> +      // This is not supported.
>> +      ASSERT (0);
>> +      return EFI_UNSUPPORTED;
>> +    }
>> +    SerialPortInfo->Clock = fdt32_to_cpu (*(UINT32*)Data);
>> +  }
>> +
>> +  if (FdtNodeIsCompatible (Fdt, SerialPortNode, &Serial16550CompatibleInfo)) {
>> +    SerialPortInfo->PortSubtype = EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550;
> [SAMI] I think this needs to be set
> toEFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_16550_WITH_GAS.

[Pierre] From what I can found,the
EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_16550_WITH_GAS has only been added
recently in the edk2 and linux. I didn't find to what is correspond. In
the DynamicTablesPkg, we are generating  the
EFI_ACPI_DBG2_PORT_SUBTYPE_SERIAL_FULL_16550 id. So I think we should
keep the non-GAS id.

>> +
>> +    /* reg-io-width:
>> +         description: |
>> +         The size (in bytes) of the IO accesses that should be performed on the
>> +         device. There are some systems that require 32-bit accesses to the
>> +         UART.
>> +    */
>> +    Data = fdt_getprop (Fdt, SerialPortNode, "reg-io-width", &DataSize);
>> +    if (Data != NULL) {
>> +      if (DataSize < sizeof (UINT32)) {
>> +        // If error or not enough space.
>> +        ASSERT (0);
>> +        return EFI_ABORTED;
>> +      }
>>
>>

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2021-11-18 10:11 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-06-23 12:38 [PATCH v1 00/14] Implement a FdtHwInfoParserLib PierreGondois
2021-06-23 12:38 ` [PATCH v1 01/14] DynamicTablesPkg: Definition for HwInfoParser interface PierreGondois
2021-06-23 12:38 ` [PATCH v1 02/14] DynamicTablesPkg: FdtHwInfoParser: CM Object descriptor helper PierreGondois
2021-11-05 14:27   ` Sami Mujawar
2021-06-23 12:38 ` [PATCH v1 03/14] DynamicTablesPkg: FdtHwInfoParser: Add FDT utility functions PierreGondois
2021-11-05 14:27   ` Sami Mujawar
2021-06-23 12:38 ` [PATCH v1 04/14] DynamicTablesPkg: FdtHwInfoParser: Add Boot Arch parser PierreGondois
2021-06-23 12:38 ` [PATCH v1 05/14] DynamicTablesPkg: FdtHwInfoParser: Generic Timer Parser PierreGondois
2021-06-23 12:38 ` [PATCH v1 06/14] DynamicTablesPkg: FdtHwInfoParser: Add Serial port parser PierreGondois
2021-11-05 14:27   ` Sami Mujawar
2021-11-18 10:11     ` PierreGondois
2021-06-23 12:38 ` [PATCH v1 07/14] DynamicTablesPkg: FdtHwInfoParser: Add GICC parser PierreGondois
2021-11-05 14:28   ` Sami Mujawar
2021-06-23 12:38 ` [PATCH v1 08/14] DynamicTablesPkg: FdtHwInfoParser: Add GICD parser PierreGondois
2021-06-23 12:38 ` [PATCH v1 09/14] DynamicTablesPkg: FdtHwInfoParser: Add MSI Frame parser PierreGondois
2021-06-23 12:38 ` [PATCH v1 10/14] DynamicTablesPkg: FdtHwInfoParser: Add ITS parser PierreGondois
2021-06-23 12:38 ` [PATCH v1 11/14] DynamicTablesPkg: FdtHwInfoParser: Add GICR parser PierreGondois
2021-06-23 12:38 ` [PATCH v1 12/14] DynamicTablesPkg: FdtHwInfoParser: Add GIC dispatcher PierreGondois
2021-06-23 12:38 ` [PATCH v1 13/14] DynamicTablesPkg: FdtHwInfoParser: Add PCI config parser PierreGondois
2021-06-23 12:38 ` [PATCH v1 14/14] DynamicTablesPkg: Add FdtHwInfoParser library PierreGondois

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox