feat(arch timer): helpers to configure EL1 physical timer

Hafnium exposes partitions to EL1 physical timer (though it actually
traps and emulates them behind the scenes).
Hence, the arch timer helper utilities are enhanced to take care of
EL1 physical timer peripheral.

Complete support for arch timer is added in subsequent patches.

Change-Id: I15aae69b7429b5f7ceb2431ffd49e570585e63b7
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/api.c b/src/api.c
index 79278ce..68558d2 100644
--- a/src/api.c
+++ b/src/api.c
@@ -1442,20 +1442,11 @@
 	 * regs_available was true (and then set it to false) before returning
 	 * true.
 	 */
-	if (arch_timer_pending(&vcpu->regs)) {
+	if (arch_timer_expired(&vcpu->regs)) {
 		/* Make virtual timer interrupt pending. */
 		api_interrupt_inject_locked(vcpu_next_locked,
 					    HF_VIRTUAL_TIMER_INTID,
 					    vcpu_next_locked, NULL);
-
-		/*
-		 * Set the mask bit so the hardware interrupt doesn't fire
-		 * again. Ideally we wouldn't do this because it affects what
-		 * the secondary vCPU sees, but if we don't then we end up with
-		 * a loop of the interrupt firing each time we try to return to
-		 * the secondary vCPU.
-		 */
-		arch_timer_mask(&vcpu->regs);
 	}
 
 	/* Switch to the vCPU. */
@@ -2962,12 +2953,10 @@
 	}
 
 	/* Inject timer interrupt if any pending */
-	if (arch_timer_pending(&receiver_vcpu->regs)) {
+	if (arch_timer_expired(&receiver_vcpu->regs)) {
 		api_interrupt_inject_locked(receiver_vcpu_locked,
 					    HF_VIRTUAL_TIMER_INTID,
 					    current_locked, NULL);
-
-		arch_timer_mask(&receiver_vcpu->regs);
 	}
 
 	/* The receiver vCPU runs upon direct message invocation */
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index 71d4d1e..3fa97b9 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -83,79 +83,20 @@
 }
 
 /**
- * Saves the state of per-vCPU peripherals, such as the virtual timer, and
+ * Saves the state of per-vCPU peripherals, such as the arch timer, and
  * informs the arch-independent sections that registers have been saved.
  */
 void complete_saving_state(struct vcpu *vcpu)
 {
-	if (has_vhe_support()) {
-		vcpu->regs.peripherals.cntv_cval_el0 =
-			read_msr(MSR_CNTV_CVAL_EL02);
-		vcpu->regs.peripherals.cntv_ctl_el0 =
-			read_msr(MSR_CNTV_CTL_EL02);
-	} else {
-		vcpu->regs.peripherals.cntv_cval_el0 = read_msr(cntv_cval_el0);
-		vcpu->regs.peripherals.cntv_ctl_el0 = read_msr(cntv_ctl_el0);
-	}
-
 	api_regs_state_saved(vcpu);
-
-	/*
-	 * If switching away from the primary, copy the current EL0 virtual
-	 * timer registers to the corresponding EL2 physical timer registers.
-	 * This is used to emulate the virtual timer for the primary in case it
-	 * should fire while the secondary is running.
-	 */
-	if (vm_is_primary(vcpu->vm)) {
-		/*
-		 * Clear timer control register before copying compare value, to
-		 * avoid a spurious timer interrupt. This could be a problem if
-		 * the interrupt is configured as edge-triggered, as it would
-		 * then be latched in.
-		 */
-		write_msr(cnthp_ctl_el2, 0);
-
-		if (has_vhe_support()) {
-			write_msr(cnthp_cval_el2, read_msr(MSR_CNTV_CVAL_EL02));
-			write_msr(cnthp_ctl_el2, read_msr(MSR_CNTV_CTL_EL02));
-		} else {
-			write_msr(cnthp_cval_el2, read_msr(cntv_cval_el0));
-			write_msr(cnthp_ctl_el2, read_msr(cntv_ctl_el0));
-		}
-	}
 }
 
 /**
- * Restores the state of per-vCPU peripherals, such as the virtual timer.
+ * Restores the state of per-vCPU peripherals, such as the arch timer.
  */
 void begin_restoring_state(struct vcpu *vcpu)
 {
-	/*
-	 * Clear timer control register before restoring compare value, to avoid
-	 * a spurious timer interrupt. This could be a problem if the interrupt
-	 * is configured as edge-triggered, as it would then be latched in.
-	 */
-	if (has_vhe_support()) {
-		write_msr(MSR_CNTV_CTL_EL02, 0);
-		write_msr(MSR_CNTV_CVAL_EL02,
-			  vcpu->regs.peripherals.cntv_cval_el0);
-		write_msr(MSR_CNTV_CTL_EL02,
-			  vcpu->regs.peripherals.cntv_ctl_el0);
-	} else {
-		write_msr(cntv_ctl_el0, 0);
-		write_msr(cntv_cval_el0, vcpu->regs.peripherals.cntv_cval_el0);
-		write_msr(cntv_ctl_el0, vcpu->regs.peripherals.cntv_ctl_el0);
-	}
-
-	/*
-	 * If we are switching (back) to the primary, disable the EL2 physical
-	 * timer which was being used to emulate the EL0 virtual timer, as the
-	 * virtual timer is now running for the primary again.
-	 */
-	if (vm_is_primary(vcpu->vm)) {
-		write_msr(cnthp_ctl_el2, 0);
-		write_msr(cnthp_cval_el2, 0);
-	}
+	(void)vcpu;
 }
 
 /**
diff --git a/src/arch/aarch64/hypervisor/offsets.c b/src/arch/aarch64/hypervisor/offsets.c
index 17c3509..e0d0f0d 100644
--- a/src/arch/aarch64/hypervisor/offsets.c
+++ b/src/arch/aarch64/hypervisor/offsets.c
@@ -22,7 +22,7 @@
 DEFINE_OFFSETOF(VCPU_LAZY, struct vcpu, regs.lazy)
 DEFINE_OFFSETOF(VCPU_FREGS, struct vcpu, regs.fp)
 DEFINE_OFFSETOF(VCPU_FPSR, struct vcpu, regs.fpsr)
-DEFINE_OFFSETOF(VCPU_TIMER, struct vcpu, regs.peripherals)
+DEFINE_OFFSETOF(VCPU_TIMER, struct vcpu, regs.arch_timer)
 #if BRANCH_PROTECTION
 DEFINE_OFFSETOF(VCPU_PAC, struct vcpu, regs.pac)
 #endif
diff --git a/src/arch/aarch64/inc/hf/arch/types.h b/src/arch/aarch64/inc/hf/arch/types.h
index f755304..e9298da 100644
--- a/src/arch/aarch64/inc/hf/arch/types.h
+++ b/src/arch/aarch64/inc/hf/arch/types.h
@@ -51,6 +51,12 @@
 static_assert(sizeof(struct float_reg) == FLOAT_REG_BYTES,
 	      "Ensure float register type is 128 bits.");
 
+/** Timer CompareValue and control registers. */
+struct timer_state {
+	uintreg_t cval;
+	uintreg_t ctl;
+};
+
 /** Type to represent the register state of a vCPU. */
 struct arch_regs {
 	/* General purpose registers. */
@@ -127,12 +133,9 @@
 #endif
 
 	/*
-	 * Peripheral registers, handled separately from other system registers.
+	 * Timer registers, handled separately from other system registers.
 	 */
-	struct {
-		uintreg_t cntv_cval_el0;
-		uintreg_t cntv_ctl_el0;
-	} peripherals;
+	struct timer_state arch_timer;
 
 #if BRANCH_PROTECTION
 	/* Pointer authentication keys */
diff --git a/src/arch/aarch64/timer.c b/src/arch/aarch64/timer.c
index 49f6b04..df4d25c 100644
--- a/src/arch/aarch64/timer.c
+++ b/src/arch/aarch64/timer.c
@@ -20,27 +20,24 @@
 #include "msr.h"
 #include "sysregs.h"
 
-#define CNTV_CTL_EL0_ENABLE (1u << 0)
-#define CNTV_CTL_EL0_IMASK (1u << 1)
-#define CNTV_CTL_EL0_ISTATUS (1u << 2)
-
-/**
- * Sets the bit to mask virtual timer interrupts.
+/*
+ * As part of support for arch timer functionality, Hafnium exposes partitions
+ * to EL1 Physical Timer (but traps and emulates the access behind the scenes
+ * using host timer).
  */
-void arch_timer_mask(struct arch_regs *regs)
-{
-	regs->peripherals.cntv_ctl_el0 |= CNTV_CTL_EL0_IMASK;
-}
+#define CNTx_CTL_EL0_ENABLE (UINT32_C(1) << 0)
+#define CNTx_CTL_EL0_IMASK (UINT32_C(1) << 1)
+#define CNTx_CTL_EL0_ISTATUS (UINT32_C(1) << 2)
 
 /**
- * Checks whether the virtual timer is enabled and its interrupt not masked.
+ * Checks whether the arch timer is enabled and its interrupt not masked.
  */
 bool arch_timer_enabled(struct arch_regs *regs)
 {
-	uintreg_t cntv_ctl_el0 = regs->peripherals.cntv_ctl_el0;
+	uintreg_t cntx_ctl_el0 = regs->arch_timer.ctl;
 
-	return (cntv_ctl_el0 & CNTV_CTL_EL0_ENABLE) &&
-	       !(cntv_ctl_el0 & CNTV_CTL_EL0_IMASK);
+	return (cntx_ctl_el0 & CNTx_CTL_EL0_ENABLE) &&
+	       !(cntx_ctl_el0 & CNTx_CTL_EL0_IMASK);
 }
 
 /**
@@ -52,28 +49,37 @@
 }
 
 /**
- * Returns the number of ticks remaining on the virtual timer as stored in
+ * Returns the number of ticks remaining on the arch timer as stored in
  * the given `arch_regs`, or 0 if it has already expired. This is undefined if
  * the timer is not enabled.
  */
 static uint64_t arch_timer_remaining_ticks(struct arch_regs *regs)
 {
 	/*
-	 * Calculate the value from the saved CompareValue (cntv_cval_el0) and
-	 * the virtual count value.
+	 * Calculate the value from the saved CompareValue (cntx_cval_el0) and
+	 * the system count value.
 	 */
-	uintreg_t cntv_cval_el0 = regs->peripherals.cntv_cval_el0;
-	uintreg_t cntvct_el0 = read_msr(cntvct_el0);
+	uintreg_t cntx_cval_el0 = regs->arch_timer.cval;
 
-	if (cntv_cval_el0 >= cntvct_el0) {
-		return cntv_cval_el0 - cntvct_el0;
+	/*
+	 * Arm ARM recommends the use of ISB before reading CNTPCT_EL0 since
+	 * it could be read out of order. However, we skip ISB given the
+	 * performance overhead associated with it.
+	 * This does not have an adverse effect on timer functionality as in
+	 * the worst case this function could return a small non-zero value
+	 * even though the timer deadline has expired which is still fine.
+	 */
+	uintreg_t cntpct_el0 = read_msr(cntpct_el0);
+
+	if (cntx_cval_el0 >= cntpct_el0) {
+		return cntx_cval_el0 - cntpct_el0;
 	}
 
 	return 0;
 }
 
 /**
- * Returns the number of nanoseconds remaining on the virtual timer as stored in
+ * Returns the number of nanoseconds remaining on the arch timer as stored in
  * the given `arch_regs`, or 0 if it has already expired. This is undefined if
  * the timer is not enabled.
  */
@@ -86,13 +92,13 @@
  * Returns whether the timer is ready to fire: i.e. it is enabled, not masked,
  * and the condition is met.
  */
-bool arch_timer_pending(struct arch_regs *regs)
+bool arch_timer_expired(struct arch_regs *regs)
 {
 	if (!arch_timer_enabled(regs)) {
 		return false;
 	}
 
-	if (regs->peripherals.cntv_ctl_el0 & CNTV_CTL_EL0_ISTATUS) {
+	if ((regs->arch_timer.ctl & CNTx_CTL_EL0_ISTATUS) != 0U) {
 		return true;
 	}
 
diff --git a/src/arch/fake/inc/hf/arch/types.h b/src/arch/fake/inc/hf/arch/types.h
index 363e1fe..f61634d 100644
--- a/src/arch/fake/inc/hf/arch/types.h
+++ b/src/arch/fake/inc/hf/arch/types.h
@@ -30,11 +30,22 @@
 /** The ID of a physical or virtual CPU. */
 typedef uint32_t cpu_id_t;
 
+/**
+ * Data structure to represent a fake timer peripheral. It has two registers:
+ * - a decrementing counter value and
+ * - a control register.
+ */
+struct timer_state {
+	uintreg_t cval;
+	uintreg_t ctl;
+};
+
 /** Type to represent the register state of a VM. */
 struct arch_regs {
 	uintreg_t arg[8];
 	cpu_id_t vcpu_id;
 	bool virtual_interrupt;
+	struct timer_state arch_timer;
 };
 
 /** Type of interrupts */
diff --git a/src/arch/fake/timer.c b/src/arch/fake/timer.c
index f776695..6a96318 100644
--- a/src/arch/fake/timer.c
+++ b/src/arch/fake/timer.c
@@ -13,29 +13,18 @@
 
 #include "hf/arch/types.h"
 
-bool arch_timer_pending(struct arch_regs *regs)
-{
-	/* TODO */
-	(void)regs;
-	return false;
-}
-
-void arch_timer_mask(struct arch_regs *regs)
-{
-	/* TODO */
-	(void)regs;
-}
-
 bool arch_timer_enabled(struct arch_regs *regs)
 {
-	/* TODO */
-	(void)regs;
-	return false;
+	return regs->arch_timer.ctl == 1;
 }
 
 uint64_t arch_timer_remaining_ns(struct arch_regs *regs)
 {
-	/* TODO */
-	(void)regs;
-	return 0;
+	/* For simplicity, we assume one tick is one nano second. */
+	return regs->arch_timer.cval;
+}
+
+bool arch_timer_expired(struct arch_regs *regs)
+{
+	return regs->arch_timer.cval == 0;
 }