diff --git a/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c b/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c
index 303dc520c6eb..34f31517d0fa 100644
--- a/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c
+++ b/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c
@@ -14,12 +14,17 @@
   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 **/
 
+#include <Uefi.h>
+
 #include <Library/BaseLib.h>
 #include <Library/BaseMemoryLib.h>
 #include <Library/DebugLib.h>
 #include <Library/IoLib.h>
 #include <Library/PcdLib.h>
 #include <Library/QemuFwCfgLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include <Protocol/FdtClient.h>
 
 STATIC UINTN mFwCfgSelectorAddress;
 STATIC UINTN mFwCfgDataAddress;
@@ -115,8 +120,59 @@ QemuFwCfgInitialize (
   VOID
   )
 {
-  mFwCfgSelectorAddress = (UINTN)PcdGet64 (PcdFwCfgSelectorAddress);
-  mFwCfgDataAddress     = (UINTN)PcdGet64 (PcdFwCfgDataAddress);
+  EFI_STATUS                    Status;
+  FDT_CLIENT_PROTOCOL           *FdtClient;
+  CONST UINT64                  *Reg;
+  UINT32                        RegElemSize, RegSize;
+  UINT64                        FwCfgSelectorSize;
+  UINT64                        FwCfgDataSize;
+  UINT64                        FwCfgDmaSize;
+
+  Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL,
+                  (VOID **)&FdtClient);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = FdtClient->FindCompatibleNodeReg (FdtClient, "qemu,fw-cfg-mmio",
+                         (CONST VOID **)&Reg, &RegElemSize, &RegSize);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  ASSERT (RegElemSize == sizeof (UINT64));
+  ASSERT (RegSize == 2 * sizeof (UINT64));
+
+  mFwCfgDataAddress     = SwapBytes64 (Reg[0]);
+  FwCfgDataSize         = 8;
+  mFwCfgSelectorAddress = mFwCfgDataAddress + FwCfgDataSize;
+  FwCfgSelectorSize     = 2;
+
+  //
+  // The following ASSERT()s express
+  //
+  //   Address + Size - 1 <= MAX_UINTN
+  //
+  // for both registers, that is, that the last byte in each MMIO range is
+  // expressible as a MAX_UINTN. The form below is mathematically
+  // equivalent, and it also prevents any unsigned overflow before the
+  // comparison.
+  //
+  ASSERT (mFwCfgSelectorAddress <= MAX_UINTN - FwCfgSelectorSize + 1);
+  ASSERT (mFwCfgDataAddress     <= MAX_UINTN - FwCfgDataSize     + 1);
+
+  DEBUG ((EFI_D_INFO, "Found FwCfg @ 0x%Lx/0x%Lx\n", mFwCfgSelectorAddress,
+    mFwCfgDataAddress));
+
+  if (SwapBytes64 (Reg[1]) >= 0x18) {
+    mFwCfgDmaAddress  = mFwCfgDataAddress + 0x10;
+    FwCfgDmaSize      = 0x08;
+
+    //
+    // See explanation above.
+    //
+    ASSERT (mFwCfgDmaAddress <= MAX_UINTN - FwCfgDmaSize + 1);
+
+    DEBUG ((EFI_D_INFO, "Found FwCfg DMA @ 0x%Lx\n", mFwCfgDmaAddress));
+  }
 
   if (InternalQemuFwCfgIsAvailable ()) {
     UINT32 Signature;
@@ -128,13 +184,12 @@ QemuFwCfgInitialize (
       // For DMA support, we require the DTB to advertise the register, and the
       // feature bitmap (which we read without DMA) to confirm the feature.
       //
-      if (PcdGet64 (PcdFwCfgDmaAddress) != 0) {
+      if (mFwCfgDmaAddress != 0) {
         UINT32 Features;
 
         QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
         Features = QemuFwCfgRead32 ();
         if ((Features & BIT1) != 0) {
-          mFwCfgDmaAddress = PcdGet64 (PcdFwCfgDmaAddress);
           InternalQemuFwCfgReadBytes = DmaReadBytes;
         }
       }
diff --git a/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf b/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf
index 298aa6edfb26..cab42e4fd532 100644
--- a/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf
+++ b/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf
@@ -46,9 +46,9 @@ [LibraryClasses]
   BaseMemoryLib
   DebugLib
   IoLib
-  PcdLib
 
-[Pcd]
-  gArmVirtTokenSpaceGuid.PcdFwCfgSelectorAddress
-  gArmVirtTokenSpaceGuid.PcdFwCfgDataAddress
-  gArmVirtTokenSpaceGuid.PcdFwCfgDmaAddress
+[Protocols]
+  gFdtClientProtocolGuid                                ## CONSUMES
+
+[Depex]
+  gFdtClientProtocolGuid
