This patch introduces CXL Dxe, which primarily discovers a PCIe device
with CXL capability and configures the same.

This patch creates and registers event notifier based on
gEfiPciEnumerationCompleteProtocolGuid. Once PCIe enumeration is
completed then CXL Dxe triggers function to discover CXL capable device
with memory expander capability and reads out memory range details.

In addition to find out a PCIe device with CXL Mem extended capability,
the module also looks for CXL device with DOE capability. Once DOE
capability is found, then execute DOE operation to fetch CDAT
structures(DSMAS), which carry information about CXL Device Memory
range, type of memory etc. It stores the remote CXL memory details in
local data structure.

Later it installs CXL Protocol interface, which will trigger
AcpiTableGenerator module to fetch CXL memory details using CXL protocol
interfaces for preparation of ACPI tables.

Signed-off-by: Sayanta Pattanayak <sayanta.pattana...@arm.com>
---
 Platform/ARM/Drivers/CxlDxe/CxlDxe.dec |  21 +
 Platform/ARM/Drivers/CxlDxe/CxlDxe.inf |  50 ++
 Platform/ARM/Drivers/CxlDxe/CxlDxe.h   | 163 ++++++
 Platform/ARM/Include/Protocol/Cxl.h    |  62 +++
 Platform/ARM/Drivers/CxlDxe/CxlDxe.c   | 575 ++++++++++++++++++++
 5 files changed, 871 insertions(+)

diff --git a/Platform/ARM/Drivers/CxlDxe/CxlDxe.dec 
b/Platform/ARM/Drivers/CxlDxe/CxlDxe.dec
new file mode 100644
index 0000000000..930b0cc9da
--- /dev/null
+++ b/Platform/ARM/Drivers/CxlDxe/CxlDxe.dec
@@ -0,0 +1,21 @@
+## @file
+#
+# Copyright (c) 2023, Arm Limited. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  DEC_SPECIFICATION              = 0x0001001B
+  PACKAGE_NAME                   = CxlDxe
+  PACKAGE_GUID                   = cbeba70a-4351-4ff6-a074-6854337b39be
+  PACKAGE_VERSION                = 0.1
+
+[Guids.common]
+  gArmRemoteCxlMemDescriptorGuid = { 0x29e54f5e, 0x365a, 0x4235, { 0xba, 0x62, 
0xc8, 0x32, 0xd7, 0x66, 0x08, 0x52 } }
+
+[Protocols]
+  gCxlPlatformProtocolGuid = { 0x88c7bb9c, 0x6175, 0x4bfb, { 0x96, 0x40, 0xd2, 
0x53, 0xd3, 0xd8, 0x7c, 0x17 } }
+
+[Includes.common]
+  Platform/ARM/Include
diff --git a/Platform/ARM/Drivers/CxlDxe/CxlDxe.inf 
b/Platform/ARM/Drivers/CxlDxe/CxlDxe.inf
new file mode 100644
index 0000000000..f2416a0768
--- /dev/null
+++ b/Platform/ARM/Drivers/CxlDxe/CxlDxe.inf
@@ -0,0 +1,50 @@
+## @file
+# Discovers CXL capable device and reads out device properties.
+#
+# Copyright (c) 2023, Arm Limited. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x0001001B
+  BASE_NAME                      = CxlDxe
+  FILE_GUID                      = 29e54f5e-365a-4235-ba62-c832d7660852
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = CxlDxeEntryPoint
+
+[Sources.common]
+  CxlDxe.c
+
+[Guids]
+  gArmRemoteCxlMemDescriptorGuid
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  ArmPkg/ArmPkg.dec
+  Platform/ARM/ARM.dec
+  Platform/ARM/Drivers/CxlDxe/CxlDxe.dec
+
+[LibraryClasses]
+  ArmMmuLib
+  ArmLib
+  BaseLib
+  DebugLib
+  DevicePathLib
+  DxeServicesTableLib
+  PcdLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+  UefiRuntimeServicesTableLib
+
+[Protocols]
+  gCxlPlatformProtocolGuid   ## PRODUCES
+  gEfiPciIoProtocolGuid      ##CONSUMES
+  gEfiPciEnumerationCompleteProtocolGuid
+
+[Depex]
+  TRUE
diff --git a/Platform/ARM/Drivers/CxlDxe/CxlDxe.h 
b/Platform/ARM/Drivers/CxlDxe/CxlDxe.h
new file mode 100644
index 0000000000..02c60fa784
--- /dev/null
+++ b/Platform/ARM/Drivers/CxlDxe/CxlDxe.h
@@ -0,0 +1,163 @@
+/** @file
+  CXL driver file.
+
+  Defines CXL specific structures and macros.
+
+  Copyright (c) 2023, Arm Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Specification Reference:
+    - CXL Specificiation Revision 3.0, Version 0.7
+**/
+
+#ifndef CXL_DXE_H_
+#define CXL_DXE_H_
+
+#define PCIE_EXTENDED_CAP_OFFSET             0x100
+#define PCIE_EXTENDED_CAP_ID_MASK            0xFFFF
+#define PCIE_EXTENDED_NEXT_CAP_OFFSET_MASK   0xFFF
+#define PCIE_EXTENDED_NEXT_CAP_OFFSET_SHIFT  20
+
+#define PCIE_EXT_CAP_DOE_ID                  0x2E
+
+#define PCIE_EXTENDED_CAP_NEXT(n)  ((n)>>(PCIE_EXTENDED_NEXT_CAP_OFFSET_SHIFT))
+#define IS_CXL_DVSEC(n) (((n)&(0xFFFF)) == 0x1E98)
+
+#define DOE_DATA_OBJECT_VID                  0x0000ffff
+#define DOE_DATA_OBJECT_TYPE                 0x00ff0000
+#define DOE_DATA_OBJECT_LENGTH               0x0003ffff
+
+#define CXL_DOE_TABLE_ENTRY_HANDLE           0xffff0000
+
+#define CXL_DOE_TABLE_ENTRY_HANDLE_LAST      0xffff
+
+#define DVSEC_CXL_VENDOR_ID                  0x1E98
+
+#define DOE_DATA_OBJ_HEADER_1                0x0
+#define DOE_DATA_OBJ_HEADER_2                0x4
+
+#define DOE_CAPABILITIES_REG                 0x4
+#define DOE_CONTROL_REG                      0x8
+#define DOE_STATUS_REG                       0xC
+#define DOE_WRITE_DATA_MAILBOX_REG           0x10
+#define DOE_READ_DATA_MAILBOX_REG            0x14
+
+#define DOE_STAT_DOE_BUSY                    0x1
+#define DOE_STAT_DATA_OBJ_READY              ((0x1) << 31)
+#define DOE_CTRL_DOE_GO                      ((0x1) << 31)
+
+#define IS_DOE_SUPPORTED(n)  \
+  (((n)&(PCIE_EXTENDED_CAP_ID_MASK))==(PCIE_EXT_CAP_DOE_ID))
+
+typedef enum {
+  PCIE_EXT_CAP_HEADER,
+  PCIE_DVSEC_HEADER_1,
+  PCIE_DVSEC_HEADER_2,
+  PCIE_DVSEC_HEADER_MAX
+} PCIE_DVSEC_HEADER_ENUM;
+
+/**
+  * Data Object Header
+  *
+  * Data Object Exchange(DOE) Header1 and Header2 put together.
+  * Reference taken from PCI-SIG ECN and
+  * CXL Specification (Revision 3.0, Version 0.7).
+**/
+typedef struct {
+  UINT16  VendorId;
+  UINT8   DataObjType;
+  UINT8   Reserved;
+  UINT32  Length;
+} DOE_HEADER;
+
+#define DOE_DATA_OBJ_TYPE_COMPLIANCE   0x0
+#define DOE_DATA_OBJ_TYPE_CDAT         0x2
+
+/**
+  * DOE read request data
+  *
+  * DOE read request data structure. For CXL DOE requests are made
+  * to read CDAT tables.
+  * Reference taken from CXL Specification (Revision 3.0, Version 0.7).
+**/
+typedef struct {
+  DOE_HEADER  Header;
+  UINT8       ReqCode;
+  UINT8       TableType;
+  UINT16      EntryHandle;
+} CXL_CDAT_READ_ENTRY_REQ;
+
+#define CXL_CDAT_DOE_ENTRYHANDLE_LAST_ENTRY  0xFFFF
+
+/* Size in DW(4 Bytes) */
+#define CDAT_READ_ENTRY_REQ_SIZE    3
+
+/**
+  * DOE read response data
+  *
+  * DOE read response data structure. For CXL, DOE responses hold
+  * information about CDAT tables.
+  * Reference taken from CXL Specification (Revision 3.0, Version 0.7).
+**/
+typedef struct {
+  DOE_HEADER  Header;
+  UINT8       RspCode;
+  UINT8       TableType;
+  UINT16      EntryHandle;
+  UINT32      CdatTable[32];
+} CXL_CDAT_READ_ENTRY_RESP;
+
+/* Size in DW(4 Bytes) */
+#define CDAT_READ_ENTRY_RESP_SIZE    3
+
+/**
+  * Coherent Device Attribute Table(CDAT) Header
+  *
+  * CDAT header, which is followed by variable number of CDAT structures.
+  * Reference taken from CDAT Specification (Revision 1.02).
+**/
+typedef struct {
+  UINT32  Length;
+  UINT8   Revision;
+  UINT8   Checksum;
+  UINT8   Reserved[6];
+  UINT32  Sequence;
+} CDAT_TABLE_HEADER;
+
+/* Size in DW(4 Bytes) */
+#define CDAT_TABLE_HEADER_SIZE    4
+
+/* Total CDAT table size. It can be increased further. */
+#define TOTAL_CDAT_ENTRY          24
+
+/**
+  * Device Scoped Memory Affinity Structure (DSMAS)
+  *
+  * DSMAS returns Device Physical Address(DPA) range and it's attributes.
+  * Reference taken from CDAT Specification (Revision 1.02).
+**/
+typedef struct {
+  UINT8   Type;
+  UINT8   Reserved_1;
+  UINT16  Length;
+  UINT8   DsmadHandle;
+  UINT8   Flags;
+  UINT16  Reserved_2;
+  UINT64  DpaBase;
+  UINT64  DpaLength;
+} CDAT_DSMAS;
+
+/* Size in DW(4 Bytes) */
+#define CDAT_STRUCTURE_DSMAS_SIZE   6
+
+typedef enum {
+  CDAT_STRUCTURE_DSMAS,
+  CDAT_STRUCTURE_DSLBIS,
+  CDAT_STRUCTURE_DSMSCIS,
+  CDAT_STRUCTURE_DSIS,
+  CDAT_STRUCTURE_DSEMTS,
+  CDAT_STRUCTURE_SSLBIS,
+  CDAT_STRUCTURE_COUNT
+} CDAT_STRUCTURE_TYPE;
+
+#endif /* CXL_DXE_H_ */
diff --git a/Platform/ARM/Include/Protocol/Cxl.h 
b/Platform/ARM/Include/Protocol/Cxl.h
new file mode 100644
index 0000000000..b2bd9d95e9
--- /dev/null
+++ b/Platform/ARM/Include/Protocol/Cxl.h
@@ -0,0 +1,62 @@
+/** @file
+  Interface API of CXL Platform protocol.
+
+  Declares the CXL Platform protocol interfaces, which are used by other
+  platform drivers for collecting information regarding discovered remote
+  memory nodes.
+
+  Copyright (c) 2023, Arm Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef CXL_H_
+#define CXL_H_
+
+/** Remote memory details
+  *
+  *  Remote memory region address in device address space and length of the
+  *  region. These informations are passed using ACPI tables, where addressbase
+  *  will be mapped to Host system address space.
+**/
+typedef struct {
+  UINT64  DpaAddress;      /// Remote memory base in device address space
+  UINT64  DpaLength;       /// Remote memory length lower bytes
+} REMOTE_MEMORY_CONFIG;
+
+/**
+  Update Remote memory information
+
+  Update the Remote memory details, Base address and Length, for number of
+  Remote memory nodes discovered from the CXL devices.
+
+  @param[out] RemoteMem    Array for updating Remote memory config.
+  @param[in,out] MemCount  Number of supported remote memory nodes.
+
+  @retval EFI_SUCCES    Memory is updated successfully
+**/
+typedef
+EFI_STATUS
+(EFIAPI *CXL_GET_REMOTE_MEM) (
+  OUT REMOTE_MEMORY_CONFIG  *RemoteMem,
+  IN OUT UINT32   *MemCount
+  );
+
+/**
+  Return number of remote memory nodes discovered from CXL Mem devices.
+
+  @retval UINT32    Number of supported remote memory nodes.
+**/
+typedef UINT32 (EFIAPI *CXL_GET_REMOTE_MEM_COUNT) (VOID);
+
+/**
+  * CXL Platform Protocol
+  *
+  * This protocol enables platform drivers to get number of memory range count
+  * and associated memory configurations.
+**/
+typedef struct {
+  CXL_GET_REMOTE_MEM CxlGetRemoteMem;
+  CXL_GET_REMOTE_MEM_COUNT CxlGetRemoteMemCount;
+} CXL_PLATFORM_PROTOCOL;
+
+#endif /* CXL_H_ */
diff --git a/Platform/ARM/Drivers/CxlDxe/CxlDxe.c 
b/Platform/ARM/Drivers/CxlDxe/CxlDxe.c
new file mode 100644
index 0000000000..409602189e
--- /dev/null
+++ b/Platform/ARM/Drivers/CxlDxe/CxlDxe.c
@@ -0,0 +1,575 @@
+/** @file
+  Discovers CXL capable device and reads out device capabilities.
+
+  This driver locates PciIo Protocol and discovers PCIe devices with CXL.Mem
+  capability. If a device with CXL.Mem capability is found then DOE capability
+  is looked for. Once DOE capability is found, CDAT structures are fetched from
+  the respective device.
+  It also installs CXL Platform portocol, which can be used by other
+  Platform drivers for capturing remote memory configurations and attributes.
+
+  Copyright (c) 2023, Arm Limited. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Specification Reference:
+    - CXL Specificiation Revision 3.0, Version 0.7, Chapter 8.1.11
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Protocol/Cxl.h>
+#include <Protocol/PciIo.h>
+#include <IndustryStandard/Pci.h>
+#include <IndustryStandard/Pci22.h>
+
+#include "CxlDxe.h"
+
+STATIC EFI_EVENT CxlEvent;
+VOID *mPciRegistration;
+STATIC UINT32 RemoteMemCount;
+//TODO: For now considered maximum 5 remote memory ranges.
+//      In future it will be made dynamic.
+STATIC REMOTE_MEMORY_CONFIG RemoteMemConfig[5];
+
+/**
+  Check whether device is ready to receive new data through DOE request.
+
+  @param[in] Pci      PCI IO Protocol handle.
+  @param[in] DoeBase  Base offset of DOE status registers in PCIe device
+                      config space.
+
+  @retval  EFI_SUCCESS   Successful read operation.
+  @retval  Other         Device not ready or failed to check device status.
+**/
+STATIC
+EFI_STATUS
+IsDoeBusy (
+  IN EFI_PCI_IO_PROTOCOL *Pci,
+  IN UINT32 DoeBase
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      DoeStatVal;
+
+  Status = Pci->Pci.Read (Pci, EfiPciIoWidthUint32, DoeBase, 1, &DoeStatVal);
+  if (EFI_ERROR (Status))
+    return Status;
+
+  if (DoeStatVal & DOE_STAT_DOE_BUSY)
+    return EFI_ALREADY_STARTED;
+
+  return Status;
+}
+
+/**
+  Read out CDAT structure data for host memory configuration.
+
+  From the DOE response data, various CDAT structure data are filtered out
+  for host platform configuration.
+
+  @param[in]  DoeRespCdatDat    Response data from DOE operation.
+  @param[in]  Length            DOE response data length in bytes.
+**/
+STATIC
+VOID
+UpdateCdatData (
+  IN UINT32 *DoeRespCdatData,
+  IN UINT16 Length
+  )
+{
+    UINT32 Index;
+    CDAT_DSMAS *Dsmas;
+
+    // Skipping the CDAT header.
+    Index = CDAT_TABLE_HEADER_SIZE;
+
+    while (Index < Length) {
+      switch (DoeRespCdatData[Index] & 0xff) {
+      case CDAT_STRUCTURE_DSMAS:
+        Dsmas = (CDAT_DSMAS *)(& (DoeRespCdatData [Index]));
+        RemoteMemConfig[RemoteMemCount].DpaAddress = Dsmas->DpaBase;
+        RemoteMemConfig[RemoteMemCount].DpaLength = Dsmas->DpaLength;
+        RemoteMemCount ++;
+        Index += CDAT_STRUCTURE_DSMAS_SIZE;
+        break;
+      default:
+        break;
+      }
+    }
+
+  return;
+}
+
+/**
+  Receive DOE response.
+
+  For CXL, DOE responses carry CDAT structures that hold information about
+  remote memory ranges and associated attributes.
+  System firmware polls the Data Object Ready bit and, provided it is Set, 
reads
+  data from the DOE Read Data Mailbox and writes 1 to the DOE Read Data Mailbox
+  to indicate a successful read.In the read process, a DWORD is read at a time.
+  Data Object Header2 holds number of DW to be transferred for capturing the
+  entire DOE response.
+
+  @param[in]  Pci          PCI IO Protocol handle.
+  @param[in]  DoeBase      Base offset of DOE registers in PCIe device config
+                           space.
+  @param[out] EntryHandle  Value of next entry in table. For CXL, table type
+                           is CDAT.
+
+  @retval  EFI_SUCCESS     Successful receiving of DOE response.
+  @retval  Other           Failed receiving of DOE response.
+**/
+STATIC
+EFI_STATUS
+DoeReceiveResponse (
+  IN EFI_PCI_IO_PROTOCOL *Pci,
+  IN UINT32 DoeBase,
+  OUT UINT16 *EntryHandle
+  )
+{
+  EFI_STATUS Status;
+  UINT32 DoeReadMbData = 1;
+  UINT32 DoeRespCdatData[TOTAL_CDAT_ENTRY] = {};
+  UINT32 DoeStat;
+  UINT32 Index = 0;
+  UINT16 Length;
+  UINT64 Reg;
+  UINT32 Val;
+
+  Reg = DoeBase + DOE_STATUS_REG;
+  Status = Pci->Pci.Read ( Pci, EfiPciIoWidthUint32, Reg, 1, &DoeStat);
+  if (EFI_ERROR (Status))
+    return Status;
+
+  if ((DoeStat & DOE_STAT_DATA_OBJ_READY) != 0) {
+    Index = 0;
+    Reg = DoeBase + DOE_READ_DATA_MAILBOX_REG;
+
+    // Read the DOE header.
+    Status = Pci->Pci.Read (Pci, EfiPciIoWidthUint32, Reg, 1, &Val);
+    if (EFI_ERROR (Status))
+      return Status;
+
+    // Write 1 to DOE Read Data Mailbox to indicate successful Read.
+    Status = Pci->Pci.Write (Pci, EfiPciIoWidthUint32, Reg, 1, &DoeReadMbData);
+    if (EFI_ERROR (Status))
+      return Status;
+
+    // Read the DOE Header 2 for data length.
+    Status = Pci->Pci.Read (Pci, EfiPciIoWidthUint32, Reg, 1, &Val);
+    if (EFI_ERROR (Status))
+      return Status;
+
+    Length = Val & DOE_DATA_OBJECT_LENGTH;
+    if (Length < 2) {
+      DEBUG ((DEBUG_ERROR, " DOE data read error\n"));
+      return EFI_PROTOCOL_ERROR;
+    }
+
+    // Write 1 to DOE Read Data Mailbox to indicate successful Read.
+    Status = Pci->Pci.Write (Pci, EfiPciIoWidthUint32, Reg, 1, &DoeReadMbData);
+    if (EFI_ERROR (Status))
+      return Status;
+
+    // Read DOE read entry response header.
+    Status = Pci->Pci.Read (Pci, EfiPciIoWidthUint32, Reg, 1, &Val);
+    if (EFI_ERROR (Status))
+      return Status;
+
+    *EntryHandle = ((Val & CXL_DOE_TABLE_ENTRY_HANDLE) >> 16);
+    // Write 1 to DOE Read Data Mailbox to indicate successful Read.
+    Status = Pci->Pci.Write (Pci, EfiPciIoWidthUint32, Reg, 1, &DoeReadMbData);
+    if (EFI_ERROR (Status))
+      return Status;
+
+    // Discount the length of 2DW DOE Header and 1DW Read entry response
+    Length -= 3;
+    while (Index < Length) {
+      Status = Pci->Pci.Read (Pci, EfiPciIoWidthUint32, Reg, 1, &Val);
+      if (EFI_ERROR (Status))
+        return Status;
+
+      DoeRespCdatData[Index] = Val;
+      Index++;
+      // Write 1 to DOE Read Data Mailbox to indicate successful Read.
+      Status = Pci->Pci.Write (
+                          Pci,
+                          EfiPciIoWidthUint32,
+                          Reg,
+                          1,
+                          &DoeReadMbData
+                          );
+      if (EFI_ERROR (Status))
+        return Status;
+    }
+
+    UpdateCdatData (DoeRespCdatData, Length);
+  }
+
+  return Status;
+}
+
+/**
+  Make DOE request to fetch CDAT structures and receive DOE response.
+
+  This function requests for DOE objects and receives response for the same.
+  The steps include -
+  1. System firmware checks that the DOE Busy bit is Clear.
+  2. System firmware writes entire data object a DWORD(4 Bytes) at a time via
+     DOE Write Data Mailbox register.
+  3. System firmware writes 1b to the DOE Go bit.
+  4. The DOE instance consumes the DOE request from the DOE mailbox.
+  5. The DOE instance generates a DOE Response and Sets Data Object Ready bit.
+  6. System firmware polls the Data Object Ready bit and, provided it is Set,
+     reads data from the DOE Read Data Mailbox and writes 1 to the DOE Read
+     Data Mailbox to indicate a successful read, a DWORD at a time until the
+     entire DOE Response is read.
+  7: DOE requests are made until response for last CDAT table entry is 
received.
+
+  @param[in] Pci      PCI IO Protocol handle.
+  @param[in] DoeBase  Base offset of DOE registers in PCIe device config space.
+
+  @retval  EFI_SUCCESS   Successful DOE request and response receive.
+  @retval  Other         Failed DOE request or response receive.
+**/
+STATIC
+EFI_STATUS
+SendDoeCommand (
+  IN EFI_PCI_IO_PROTOCOL *Pci,
+  IN UINT32 DoeBase
+  )
+{
+  EFI_STATUS Status;
+  UINT32 Val;
+  UINT64 Reg;
+  CXL_CDAT_READ_ENTRY_REQ CxlDoeReq;
+  UINT32 Index = 0;
+
+  // CDAT DOE Request header & Read entry request object.
+  CxlDoeReq.Header.VendorId = DVSEC_CXL_VENDOR_ID;
+  CxlDoeReq.Header.DataObjType = DOE_DATA_OBJ_TYPE_CDAT;
+  CxlDoeReq.Header.Length = CDAT_READ_ENTRY_REQ_SIZE;
+
+  // 0 indicates that it's a read request.
+  CxlDoeReq.ReqCode = 0;
+
+  // 0 indicates that table type is CDAT.
+  CxlDoeReq.TableType = 0;
+
+  // 0 represents very first entry in the table.
+  CxlDoeReq.EntryHandle = 0;
+
+  Reg = DoeBase + DOE_WRITE_DATA_MAILBOX_REG;
+
+  do {
+    Status = IsDoeBusy (Pci, DoeBase + DOE_STATUS_REG);
+
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_WARN, "Device busy or DOE request can't be made\n"));
+      return Status;
+    }
+
+    while (Index < CDAT_READ_ENTRY_REQ_SIZE) {
+      Val = *((UINT32 *) (&CxlDoeReq) + Index);
+      Status = Pci->Pci.Write (Pci, EfiPciIoWidthUint32, Reg, 1, &Val);
+      if (EFI_ERROR (Status)) {
+        DEBUG ((DEBUG_WARN, "Error while making DOE request\n"));
+        return Status;
+      }
+      Index++;
+    }
+
+    Reg = DoeBase + DOE_CONTROL_REG;
+    Status = Pci->Pci.Read (Pci, EfiPciIoWidthUint32, Reg, 1, &Val);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_WARN, "Error while reading DOE control reg\n"));
+      return Status;
+    }
+
+    Val |= DOE_CTRL_DOE_GO;
+    Status = Pci->Pci.Write (Pci, EfiPciIoWidthUint32, Reg, 1, &Val);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_WARN, "Error while writing into DOE control reg\n"));
+      return Status;
+    }
+
+    Status = DoeReceiveResponse (Pci, DoeBase, &CxlDoeReq.EntryHandle);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_WARN, "Error while receiving DOE response\n"));
+      return Status;
+    }
+  } while (CxlDoeReq.EntryHandle < CXL_DOE_TABLE_ENTRY_HANDLE_LAST);
+
+  return Status;
+}
+
+/**
+  Return number of remote memory nodes discovered from CXL Mem devices.
+
+  @retval UINT32   Number of supported remote memory nodes
+**/
+STATIC UINT32 EFIAPI CxlGetRemoteMemCount (VOID)
+{
+  return RemoteMemCount;
+}
+
+/**
+  Update Remote memory information
+
+  Update the Remote memory details, Base address and Length, for number of
+  Remote memory nodes discovered from the CXL devices. If the update request
+  for number of memory nodes is more than discovered remote memory nodes 
number,
+  then MemCount is modified to number of discovered remote memory nodes.
+
+  @param[out] RemoteMem    Array for updating Remote memory config.
+  @param[in,out] MemCount  Number of supported remote memory nodes.
+
+  @retval EFI_SUCCES    Memory is updated successfully
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CxlGetRemoteMem (
+  OUT REMOTE_MEMORY_CONFIG *RemoteMemInfo,
+  IN OUT UINT32 *MemCount
+  )
+{
+
+  if ((*MemCount) > RemoteMemCount) {
+    DEBUG ((DEBUG_WARN, "Requested for more than max. Remote Memory node\n"));
+    *MemCount = RemoteMemCount;
+  }
+
+  CopyMem (
+    RemoteMemInfo,
+    RemoteMemConfig,
+    sizeof (REMOTE_MEMORY_CONFIG) * (*MemCount)
+    );
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Installs the CXL platform protocol.
+
+  CXL platform protocol has interfaces for providing CXL mem device
+  configurations. A Platform driver can fetch such configurations
+  using these protocl interfaces.
+
+  @retval EFI_SUCCESS  On successful installation of protocol interfaces.
+  @retval Other        On failure of protocol installation.
+**/
+STATIC
+EFI_STATUS
+CxlInstallProtocol (VOID)
+{
+  EFI_STATUS Status;
+  EFI_HANDLE *CxlPlatformHandle;
+  CXL_PLATFORM_PROTOCOL *CxlPlatformProtocol;
+  STATIC BOOLEAN CxlProtocolInstalled = FALSE;
+
+  if (CxlProtocolInstalled == TRUE) {
+    DEBUG ((DEBUG_INFO, "Protocol already installed. \n"));
+    return EFI_SUCCESS;
+  }
+
+  CxlPlatformHandle = (EFI_HANDLE *) AllocateZeroPool (sizeof(EFI_HANDLE));
+
+  CxlPlatformProtocol =
+    (CXL_PLATFORM_PROTOCOL *) AllocateZeroPool (sizeof(CXL_PLATFORM_PROTOCOL));
+
+  if (!CxlPlatformProtocol) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "CxlInstallProtocol: Failed to allocate memory for CxlPlatformProtocol\n"
+      ));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  CxlPlatformProtocol->CxlGetRemoteMem = CxlGetRemoteMem;
+  CxlPlatformProtocol->CxlGetRemoteMemCount = CxlGetRemoteMemCount;
+
+  Status = gBS->InstallProtocolInterface (
+                  CxlPlatformHandle,
+                  &gCxlPlatformProtocolGuid,
+                  EFI_NATIVE_INTERFACE,
+                  CxlPlatformProtocol
+                  );
+
+  if (EFI_ERROR(Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "CxlInstallProtocol: Failed to install CxlPlatformProtocol: 0x%08x\n",
+      Status
+      ));
+
+    return Status;
+  }
+
+  CxlProtocolInstalled = TRUE;
+
+  DEBUG ((DEBUG_INFO, "Installed protocol: %p\n", CxlPlatformProtocol));
+  return EFI_SUCCESS;
+}
+
+VOID
+EFIAPI
+PciBusEvent (
+  IN EFI_EVENT Event,
+  IN VOID* Context
+  )
+{
+
+  EFI_STATUS Status;
+  EFI_PCI_IO_PROTOCOL *Pci;
+  UINTN Seg, Bus, Dev, Func;
+  EFI_HANDLE *HandleBuffer;
+  UINTN HandleCount, Index;
+  UINT32 ExtCapOffset, NextExtCapOffset;
+  UINT32 NextDoeExtCapOffset;
+  UINT32 PcieExtCapAndDvsecHeader[3];
+  UINT32 PcieExtCapHeader;
+
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiPciIoProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  if (EFI_ERROR(Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed to locate any PciIo protocols\n"));
+    return;
+  }
+
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiPciIoProtocolGuid,
+                    (VOID **)&Pci
+                    );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "Failed to get Pci handle\n"));
+      return;
+    }
+
+    Pci->GetLocation (Pci, &Seg, &Bus, &Dev, &Func);
+    NextExtCapOffset = PCIE_EXTENDED_CAP_OFFSET;
+
+    do {
+      ExtCapOffset = NextExtCapOffset;
+      Status = Pci->Pci.Read (
+                          Pci,
+                          EfiPciIoWidthUint32,
+                          ExtCapOffset,
+                          PCIE_DVSEC_HEADER_MAX,
+                          PcieExtCapAndDvsecHeader
+                          );
+      if (EFI_ERROR (Status)) {
+        DEBUG ((DEBUG_ERROR, "Failed to read PCI IO for Ext. capability\n"));
+        return;
+      }
+
+      /* Check whether this is a CXL device */
+      if (IS_CXL_DVSEC (PcieExtCapAndDvsecHeader[PCIE_DVSEC_HEADER_1])) {
+        DEBUG ((DEBUG_INFO, "Found CXL Device\n"));
+
+        NextDoeExtCapOffset = PCIE_EXTENDED_CAP_OFFSET;
+        do {
+          ExtCapOffset = NextDoeExtCapOffset;
+          Status = Pci->Pci.Read (
+                              Pci,
+                              EfiPciIoWidthUint32,
+                              ExtCapOffset,
+                              1,
+                              &PcieExtCapHeader
+                              );
+          if (EFI_ERROR (Status)) {
+            DEBUG ((DEBUG_ERROR, "Failed to read PCI Ext. capability\n"));
+            return;
+          }
+
+          if (IS_DOE_SUPPORTED (PcieExtCapHeader)) {
+            DEBUG ((DEBUG_INFO, "Found DOE Capability\n"));
+            Status = SendDoeCommand (Pci, ExtCapOffset);
+
+            if (EFI_ERROR (Status)) {
+              DEBUG ((DEBUG_WARN, "Not Found DOE Capability\n"));
+            } else {
+              Status = CxlInstallProtocol();
+              if (EFI_ERROR (Status))
+                return;
+            }
+
+            NextExtCapOffset = 0;
+            break;
+          }
+
+          NextDoeExtCapOffset = PCIE_EXTENDED_CAP_NEXT (PcieExtCapHeader);
+        } while(NextDoeExtCapOffset);
+      }
+
+      if (NextExtCapOffset == 0)
+        break;
+
+      NextExtCapOffset = PCIE_EXTENDED_CAP_NEXT (
+                           PcieExtCapAndDvsecHeader[PCIE_EXT_CAP_HEADER]
+                           );
+
+    } while (NextExtCapOffset);
+  }
+
+  gBS->CloseEvent (CxlEvent);
+  CxlEvent = NULL;
+
+  return;
+}
+
+/**
+  Entry point for CXL DXE.
+
+  This Dxe depends on gEfiPciEnumerationCompleteProtocolGuid. It locates
+  PciIo Protocol and discovers PCIe devices with CXL.Mem capability. If a
+  device with CXL.Mem capability is found then DOE capability is looked for.
+  After that, CXL.Mem device configurations are fetched, and thereafter CXL
+  Platform portocol is installed.
+
+  @param[in] ImageHandle  Handle to the Efi image.
+  @param[in] SystemTable  A pointer to the Efi System Table.
+
+  @retval EFI_SUCCESS  On successful execution of mentioned functionlities.
+  @retval Other        On failure.
+**/
+EFI_STATUS
+EFIAPI
+CxlDxeEntryPoint (
+  IN EFI_HANDLE ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  EFI_STATUS Status;
+
+  Status = gBS->CreateEvent (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_CALLBACK,
+                  PciBusEvent,
+                  NULL,
+                  &CxlEvent
+                  );
+
+  //
+  // Register for protocol notifications on this event
+  //
+  Status = gBS->RegisterProtocolNotify (
+                  &gEfiPciEnumerationCompleteProtocolGuid,
+                  CxlEvent,
+                  &mPciRegistration
+                  );
+
+  return Status;
+}
-- 
2.25.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#109491): https://edk2.groups.io/g/devel/message/109491
Mute This Topic: https://groups.io/mt/101872996/21656
Group Owner: devel+ow...@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to