diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec
index 14f16b85169a..1d896892dafc 100644
--- a/ArmPkg/ArmPkg.dec
+++ b/ArmPkg/ArmPkg.dec
@@ -143,6 +143,11 @@
   # ARM Architectural Timer Interrupt(GIC PPI) number
   gArmTokenSpaceGuid.PcdArmArchTimerSecIntrNum|29|UINT32|0x00000035
   gArmTokenSpaceGuid.PcdArmArchTimerIntrNum|30|UINT32|0x00000036
+  gArmTokenSpaceGuid.PcdArmArchTimerVirtIntrNum|27|UINT32|0x00000040
+  gArmTokenSpaceGuid.PcdArmArchTimerHypIntrNum|26|UINT32|0x00000041
+
+  # MC: not really sure what the last value is except I think it needs to be unique
+  gArmTokenSpaceGuid.PcdArmArchTimerUseVirtual|FALSE|BOOLEAN|0x0000003F
 
 [PcdsFixedAtBuild.ARM]
   #
diff --git a/ArmPkg/Drivers/TimerDxe/TimerDxe.c b/ArmPkg/Drivers/TimerDxe/TimerDxe.c
index a43a10f48cee..be346bf89c51 100644
--- a/ArmPkg/Drivers/TimerDxe/TimerDxe.c
+++ b/ArmPkg/Drivers/TimerDxe/TimerDxe.c
@@ -347,6 +347,12 @@ TimerInitialize (
   // Note: Because it is not possible to determine the security state of the
   // CPU dynamically, we just install interrupt handler for both sec and non-sec
   // timer PPI
+  Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerVirtIntrNum), TimerInterruptHandler);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerHypIntrNum), TimerInterruptHandler);
+  ASSERT_EFI_ERROR (Status);
+
   Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerSecIntrNum), TimerInterruptHandler);
   ASSERT_EFI_ERROR (Status);
 
diff --git a/ArmPkg/Drivers/TimerDxe/TimerDxe.inf b/ArmPkg/Drivers/TimerDxe/TimerDxe.inf
index 061fcbc688d9..fa48e569a17d 100644
--- a/ArmPkg/Drivers/TimerDxe/TimerDxe.inf
+++ b/ArmPkg/Drivers/TimerDxe/TimerDxe.inf
@@ -52,6 +52,9 @@
   gEmbeddedTokenSpaceGuid.PcdTimerPeriod
   gArmTokenSpaceGuid.PcdArmArchTimerSecIntrNum  
   gArmTokenSpaceGuid.PcdArmArchTimerIntrNum
+  gArmTokenSpaceGuid.PcdArmArchTimerVirtIntrNum
+  gArmTokenSpaceGuid.PcdArmArchTimerHypIntrNum
+
   gArmTokenSpaceGuid.PcdArmArchTimerFreqInHz  
 
 [Depex]
diff --git a/ArmPkg/Library/ArmLib/AArch64/AArch64ArchTimer.c b/ArmPkg/Library/ArmLib/AArch64/AArch64ArchTimer.c
index fa4f7c741b15..232d745b3d06 100644
--- a/ArmPkg/Library/ArmLib/AArch64/AArch64ArchTimer.c
+++ b/ArmPkg/Library/ArmLib/AArch64/AArch64ArchTimer.c
@@ -175,9 +175,17 @@ ArmArchTimerEnableTimer (
 {
   UINTN TimerCtrlReg;
 
-  ArmArchTimerReadReg (CntpCtl, (VOID *)&TimerCtrlReg);
-  TimerCtrlReg |= ARM_ARCH_TIMER_ENABLE;
-  ArmArchTimerWriteReg (CntpCtl, (VOID *)&TimerCtrlReg);
+  if (PcdGetBool(PcdArmArchTimerUseVirtual) == TRUE) {
+    ArmArchTimerReadReg (CntvCtl, (VOID *)&TimerCtrlReg);
+    /* FIXME: We need to clear IMASK when under KVM because KVM sets it. Unclear if this is a bug ... */
+    TimerCtrlReg |= ARM_ARCH_TIMER_ENABLE;
+    TimerCtrlReg &= ~ARM_ARCH_TIMER_IMASK;
+    ArmArchTimerWriteReg (CntvCtl, (VOID *)&TimerCtrlReg);
+  } else {
+    ArmArchTimerReadReg (CntpCtl, (VOID *)&TimerCtrlReg);
+    TimerCtrlReg |= ARM_ARCH_TIMER_ENABLE;
+    ArmArchTimerWriteReg (CntpCtl, (VOID *)&TimerCtrlReg);
+  }
 }
 
 VOID
@@ -188,9 +196,15 @@ ArmArchTimerDisableTimer (
 {
   UINTN TimerCtrlReg;
 
-  ArmArchTimerReadReg (CntpCtl, (VOID *)&TimerCtrlReg);
-  TimerCtrlReg &= ~ARM_ARCH_TIMER_ENABLE;
-  ArmArchTimerWriteReg (CntpCtl, (VOID *)&TimerCtrlReg);
+  if (PcdGetBool(PcdArmArchTimerUseVirtual) == TRUE) {
+    ArmArchTimerReadReg (CntvCtl, (VOID *)&TimerCtrlReg);
+    TimerCtrlReg &= ~ARM_ARCH_TIMER_ENABLE;
+    ArmArchTimerWriteReg (CntvCtl, (VOID *)&TimerCtrlReg);
+  } else {
+    ArmArchTimerReadReg (CntpCtl, (VOID *)&TimerCtrlReg);
+    TimerCtrlReg &= ~ARM_ARCH_TIMER_ENABLE;
+    ArmArchTimerWriteReg (CntpCtl, (VOID *)&TimerCtrlReg);
+  }
 }
 
 VOID
@@ -220,7 +234,12 @@ ArmArchTimerGetTimerVal (
     )
 {
   UINTN ArchTimerVal;
-  ArmArchTimerReadReg (CntpTval, (VOID *)&ArchTimerVal);
+  if (PcdGetBool(PcdArmArchTimerUseVirtual) == TRUE) {
+    ArmArchTimerReadReg (CntvTval, (VOID *)&ArchTimerVal);
+  } else {
+    ArmArchTimerReadReg (CntpTval, (VOID *)&ArchTimerVal);
+  }
+
   return ArchTimerVal;
 }
 
@@ -231,7 +250,11 @@ ArmArchTimerSetTimerVal (
     IN   UINTN   Val
     )
 {
-  ArmArchTimerWriteReg (CntpTval, (VOID *)&Val);
+  if (PcdGetBool(PcdArmArchTimerUseVirtual) == TRUE) {
+    ArmArchTimerWriteReg (CntvTval, (VOID *)&Val);
+  } else {
+    ArmArchTimerWriteReg (CntpTval, (VOID *)&Val);
+  }
 }
 
 UINT64
@@ -241,7 +264,12 @@ ArmArchTimerGetSystemCount (
     )
 {
   UINT64 SystemCount;
-  ArmArchTimerReadReg (CntPct, (VOID *)&SystemCount);
+  if (PcdGetBool(PcdArmArchTimerUseVirtual) == TRUE) {
+    ArmArchTimerReadReg (CntvCt, (VOID *)&SystemCount);
+  } else {
+    ArmArchTimerReadReg (CntPct, (VOID *)&SystemCount);
+  }
+
   return SystemCount;
 }
 
@@ -252,7 +280,13 @@ ArmArchTimerGetTimerCtrlReg (
     )
 {
   UINTN  Val;
-  ArmArchTimerReadReg (CntpCtl, (VOID *)&Val);
+
+  if (PcdGetBool(PcdArmArchTimerUseVirtual) == TRUE) {
+    ArmArchTimerReadReg (CntvCtl, (VOID *)&Val);
+  } else {
+    ArmArchTimerReadReg (CntpCtl, (VOID *)&Val);
+  }
+
   return Val;
 }
 
@@ -262,7 +296,11 @@ ArmArchTimerSetTimerCtrlReg (
     UINTN Val
     )
 {
-  ArmArchTimerWriteReg (CntpCtl, (VOID *)&Val);
+  if (PcdGetBool(PcdArmArchTimerUseVirtual) == TRUE) {
+    ArmArchTimerWriteReg (CntvCtl, (VOID *)&Val);
+  } else {
+    ArmArchTimerWriteReg (CntpCtl, (VOID *)&Val);
+  }
 }
 
 VOID
@@ -271,5 +309,9 @@ ArmArchTimerSetCompareVal (
     IN   UINT64   Val
     )
 {
-  ArmArchTimerWriteReg (CntpCval, (VOID *)&Val);
+  if (PcdGetBool(PcdArmArchTimerUseVirtual) == TRUE) {
+    ArmArchTimerWriteReg (CntvCval, (VOID *)&Val);
+  } else {
+    ArmArchTimerWriteReg (CntpCval, (VOID *)&Val);
+  }
 }
diff --git a/ArmPkg/Library/ArmLib/AArch64/AArch64Lib.inf b/ArmPkg/Library/ArmLib/AArch64/AArch64Lib.inf
index e5247848b549..f7d50b9bd54c 100644
--- a/ArmPkg/Library/ArmLib/AArch64/AArch64Lib.inf
+++ b/ArmPkg/Library/ArmLib/AArch64/AArch64Lib.inf
@@ -44,3 +44,5 @@
 
 [FixedPcd]
   gArmTokenSpaceGuid.PcdArmCacheOperationThreshold
+  gArmTokenSpaceGuid.PcdArmArchTimerUseVirtual
+
diff --git a/ArmPkg/Library/ArmLib/AArch64/AArch64LibPrePi.inf b/ArmPkg/Library/ArmLib/AArch64/AArch64LibPrePi.inf
index 3a99e1b713cc..698de2979f97 100644
--- a/ArmPkg/Library/ArmLib/AArch64/AArch64LibPrePi.inf
+++ b/ArmPkg/Library/ArmLib/AArch64/AArch64LibPrePi.inf
@@ -46,3 +46,4 @@
 
 [FixedPcd]
   gArmTokenSpaceGuid.PcdArmCacheOperationThreshold
+  gArmTokenSpaceGuid.PcdArmArchTimerUseVirtual
diff --git a/ArmPkg/Library/ArmLib/AArch64/AArch64LibSec.inf b/ArmPkg/Library/ArmLib/AArch64/AArch64LibSec.inf
index 57ac694cd733..0eba4c04d404 100644
--- a/ArmPkg/Library/ArmLib/AArch64/AArch64LibSec.inf
+++ b/ArmPkg/Library/ArmLib/AArch64/AArch64LibSec.inf
@@ -41,3 +41,4 @@
 
 [FixedPcd]
   gArmTokenSpaceGuid.PcdArmCacheOperationThreshold
+  gArmTokenSpaceGuid.PcdArmArchTimerUseVirtual
