diff --git a/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.c b/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.c
new file mode 100644
index 000000000000..25152e70b0c8
--- /dev/null
+++ b/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.c
@@ -0,0 +1,282 @@
+/** @file
+*  Device tree enumeration DXE driver for ARM Virtual Machines
+*
+*  Copyright (c) 2014, Linaro Ltd. All rights reserved.<BR>
+*  This program and the accompanying materials are
+*  licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/VirtioMmioDeviceLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PcdLib.h>
+#include <Library/DxeServicesLib.h>
+#include <libfdt.h>
+
+#include <Guid/Fdt.h>
+
+CONST UINT32 mUint32Max = 0xFFFFFFFFU;
+
+#pragma pack (1)
+typedef struct {
+  VENDOR_DEVICE_PATH                  Vendor;
+  UINT64                              PhysBase;
+  EFI_DEVICE_PATH_PROTOCOL            End;
+} VIRTIO_TRANSPORT_DEVICE_PATH;
+#pragma pack ()
+
+typedef enum {
+  PropertyTypeUnknown,
+  PropertyTypeGic,
+  PropertyTypeRtc,
+  PropertyTypeVirtio,
+  PropertyTypeUart,
+  PropertyTypeTimer,
+  PropertyTypePsci,
+} PROPERTY_TYPE;
+
+typedef struct {
+  PROPERTY_TYPE Type;
+  CHAR8         Compatible[20];
+} PROPERTY;
+
+STATIC CONST PROPERTY CompatibleProperties[] = {
+  { PropertyTypeGic,     "arm,cortex-a15-gic"  },
+  { PropertyTypeRtc,     "arm,pl031"           },
+  { PropertyTypeVirtio,  "virtio,mmio"         },
+  { PropertyTypeUart,    "arm,pl011"           },
+  { PropertyTypeTimer,   "arm,armv7-timer"     },
+  { PropertyTypeTimer,   "arm,armv8-timer"     },
+  { PropertyTypePsci,    "arm,psci-0.2"        },
+  { PropertyTypeUnknown, ""                    }
+};
+
+typedef struct {
+  UINT32  Type;
+  UINT32  Number;
+  UINT32  Flags;
+} INTERRUPT_PROP;
+
+STATIC
+PROPERTY_TYPE
+GetTypeFromNode (
+  IN CONST CHAR8 *NodeType,
+  IN UINTN       Size
+  )
+{
+  CONST CHAR8 *Compatible;
+
+  //
+  // A 'compatible' node may contain a sequence of NULL terminated
+  // compatible strings so check each one
+  //
+  for (Compatible = NodeType; Compatible < NodeType + Size && *Compatible;
+       Compatible += 1 + AsciiStrLen (Compatible)) {
+
+    CONST PROPERTY *CompProp;
+
+    for (CompProp = CompatibleProperties; CompProp->Compatible[0]; CompProp++) {
+      if (AsciiStrCmp (CompProp->Compatible, Compatible) == 0) {
+        return CompProp->Type;
+      }
+    }
+  }
+  return PropertyTypeUnknown;
+}
+
+EFI_STATUS
+EFIAPI
+InitializeVirtFdtDxe (
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable
+  )
+{
+  VOID         *DeviceTreeBase;
+  INT32        Node, Prev;
+  EFI_STATUS   Status;
+
+  DeviceTreeBase = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeBaseAddress);
+  ASSERT (DeviceTreeBase != NULL);
+
+  if (fdt_check_header (DeviceTreeBase) != 0) {
+    DEBUG ((EFI_D_ERROR, "%a: No DTB found @ 0x%p\n",
+           __FUNCTION__, DeviceTreeBase));
+    return EFI_NOT_FOUND;
+  }
+
+  Status = gBS->InstallConfigurationTable (&gFdtTableGuid, DeviceTreeBase);
+  ASSERT_EFI_ERROR (Status);
+
+  DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, DeviceTreeBase));
+
+  //
+  // Now enumerate the nodes and install peripherals that we are interested in,
+  // i.e., GIC, RTC and virtio MMIO nodes
+  //
+  for (Prev = 0;; Prev = Node) {
+    CONST CHAR8                    *Type;
+    INT32                          Len;
+    PROPERTY_TYPE                  PropType;
+    CONST VOID                     *RegProp;
+    VIRTIO_TRANSPORT_DEVICE_PATH   *DevicePath;
+    EFI_HANDLE                     Handle;
+    UINT64                         RegBase;
+    UINT64                         DistBase, CpuBase;
+    CONST INTERRUPT_PROP           *InterruptProp;
+    INT32                          SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum;
+    CONST CHAR8                    *PsciMethod;
+
+    Node = fdt_next_node (DeviceTreeBase, Prev, NULL);
+    if (Node < 0) {
+      break;
+    }
+
+    Type = fdt_getprop (DeviceTreeBase, Node, "compatible", &Len);
+    if (Type == NULL) {
+      continue;
+    }
+
+    PropType = GetTypeFromNode (Type, Len);
+    if (PropType == PropertyTypeUnknown) {
+      continue;
+    }
+
+    //
+    // Get the 'reg' property of this node. For now, we will assume
+    // 8 byte quantities for base and size, respectively.
+    // TODO use #cells root properties instead
+    //
+    RegProp = fdt_getprop (DeviceTreeBase, Node, "reg", &Len);
+    ASSERT (RegProp != NULL || PropType == PropertyTypeTimer ||
+      PropType == PropertyTypePsci);
+
+    switch (PropType) {
+
+    case PropertyTypeVirtio:
+      ASSERT (Len == 16);
+      //
+      // Create a unique device path for this transport on the fly
+      //
+      RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
+      DevicePath = (VIRTIO_TRANSPORT_DEVICE_PATH *)CreateDeviceNode (
+                                    HARDWARE_DEVICE_PATH,
+                                    HW_VENDOR_DP,
+                                    sizeof (VIRTIO_TRANSPORT_DEVICE_PATH));
+      if (DevicePath == NULL) {
+        DEBUG ((EFI_D_ERROR, "%a: Out of memory\n", __FUNCTION__));
+        break;
+      }
+
+      CopyMem (&DevicePath->Vendor.Guid, &gEfiCallerIdGuid, sizeof (EFI_GUID));
+      DevicePath->PhysBase = RegBase;
+      SetDevicePathNodeLength (&DevicePath->Vendor,
+                               sizeof (*DevicePath) - sizeof (DevicePath->End));
+      SetDevicePathEndNode (&DevicePath->End);
+
+      Handle = NULL;
+      Status = gBS->InstallProtocolInterface (&Handle,
+                     &gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE,
+                     DevicePath);
+      if (EFI_ERROR (Status)) {
+        DEBUG ((EFI_D_ERROR, "%a: Failed to install the EFI_DEVICE_PATH "
+          "protocol on a new handle (Status == %r)\n",
+          __FUNCTION__, Status));
+        FreePool (DevicePath);
+        break;
+      }
+
+      Status = VirtioMmioInstallDevice (RegBase, Handle);
+      if (EFI_ERROR (Status)) {
+        DEBUG ((EFI_D_ERROR, "%a: Failed to install VirtIO transport @ 0x%Lx "
+          "on handle %p (Status == %r)\n", __FUNCTION__, RegBase,
+          Handle, Status));
+
+        Status = gBS->UninstallProtocolInterface (Handle,
+                        &gEfiDevicePathProtocolGuid, DevicePath);
+        ASSERT_EFI_ERROR (Status);
+        FreePool (DevicePath);
+      }
+      break;
+
+    case PropertyTypeGic:
+      ASSERT (Len == 32);
+
+      DistBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
+      CpuBase = fdt64_to_cpu (((UINT64 *)RegProp)[2]);
+      ASSERT (DistBase < mUint32Max);
+      ASSERT (CpuBase < mUint32Max);
+
+      PcdSet32 (PcdGicDistributorBase, (UINT32)DistBase);
+      PcdSet32 (PcdGicInterruptInterfaceBase, (UINT32)CpuBase);
+
+      DEBUG ((EFI_D_INFO, "Found GIC @ 0x%Lx/0x%Lx\n", DistBase, CpuBase));
+      break;
+
+    case PropertyTypeRtc:
+      ASSERT (Len == 16);
+
+      RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);
+      ASSERT (RegBase < mUint32Max);
+
+      PcdSet32 (PcdPL031RtcBase, (UINT32)RegBase);
+
+      DEBUG ((EFI_D_INFO, "Found PL031 RTC @ 0x%Lx\n", RegBase));
+      break;
+
+    case PropertyTypeTimer:
+
+      //
+      // - interrupts : Interrupt list for secure, non-secure, virtual and
+      //  hypervisor timers, in that order.
+      //
+      InterruptProp = fdt_getprop (DeviceTreeBase, Node, "interrupts", &Len);
+      ASSERT (Len == 48);
+
+      SecIntrNum = fdt32_to_cpu (InterruptProp[0].Number)
+                   + (InterruptProp[0].Type ? 16 : 0);
+      IntrNum = fdt32_to_cpu (InterruptProp[1].Number)
+                + (InterruptProp[1].Type ? 16 : 0);
+      VirtIntrNum = fdt32_to_cpu (InterruptProp[2].Number)
+                    + (InterruptProp[2].Type ? 16 : 0);
+      HypIntrNum = fdt32_to_cpu (InterruptProp[3].Number)
+                   + (InterruptProp[3].Type ? 16 : 0);
+
+      DEBUG ((EFI_D_INFO, "Found Timer interrupts %d, %d, %d, %d\n",
+        SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum));
+
+      PcdSet32 (PcdArmArchTimerSecIntrNum, SecIntrNum);
+      PcdSet32 (PcdArmArchTimerIntrNum, IntrNum);
+      PcdSet32 (PcdArmArchTimerVirtIntrNum, VirtIntrNum);
+      PcdSet32 (PcdArmArchTimerHypIntrNum, HypIntrNum);
+      break;
+
+    case PropertyTypePsci:
+
+      PsciMethod = fdt_getprop (DeviceTreeBase, Node, "method", &Len);
+
+      if (AsciiStrnCmp (PsciMethod, "hvc", 3) == 0) {
+        PcdSet32 (PcdArmPsciMethod, 1);
+      } else if (AsciiStrnCmp (PsciMethod, "smc", 3) == 0) {
+        PcdSet32 (PcdArmPsciMethod, 2);
+      } else {
+        DEBUG ((EFI_D_ERROR, "%a: Unknown PSCI method \"%s\"\n", __FUNCTION__,
+          PsciMethod));
+      }
+    default:
+      break;
+    }
+  }
+  return EFI_SUCCESS;
+}
diff --git a/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.inf b/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.inf
new file mode 100644
index 000000000000..dc5c6d08e525
--- /dev/null
+++ b/ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.inf
@@ -0,0 +1,61 @@
+## @file
+#  Device tree enumeration DXE driver for ARM Virtual Machines
+#
+#  Copyright (c) 2014, Linaro Ltd. All rights reserved.<BR>
+#  This program and the accompanying materials are
+#  licensed and made available under the terms and conditions of the BSD License
+#  which accompanies this distribution.  The full text of the license may be found at
+#  http://opensource.org/licenses/bsd-license.php
+#
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = VirtFdtDxe
+  FILE_GUID                      = 837DCA9E-E874-4D82-B29A-23FE0E23D1E2
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = InitializeVirtFdtDxe
+
+[Sources]
+  VirtFdtDxe.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  ArmPkg/ArmPkg.dec
+  ArmPlatformPkg/ArmPlatformPkg.dec
+  ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationPkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  PcdLib
+  UefiDriverEntryPoint
+  DxeServicesLib
+  FdtLib
+  VirtioMmioDeviceLib
+
+[Guids]
+  gFdtTableGuid
+
+[Pcd]
+  gArmVirtualizationTokenSpaceGuid.PcdDeviceTreeBaseAddress
+  gArmTokenSpaceGuid.PcdGicDistributorBase
+  gArmTokenSpaceGuid.PcdGicInterruptInterfaceBase
+  gArmTokenSpaceGuid.PcdArmArchTimerSecIntrNum
+  gArmTokenSpaceGuid.PcdArmArchTimerIntrNum
+  gArmTokenSpaceGuid.PcdArmArchTimerVirtIntrNum
+  gArmTokenSpaceGuid.PcdArmArchTimerHypIntrNum
+  gArmPlatformTokenSpaceGuid.PcdPL031RtcBase
+  gArmTokenSpaceGuid.PcdArmPsciMethod
+
+[Protocols]
+  gEfiDevicePathProtocolGuid
+
+[Depex]
+  TRUE
