Save and restore virtual timer registers.

Also emulate the virtual timer for the primary by using the EL2 physical
timer to return to the primary when its virtual timer would fire.

Bug: 117271574
Change-Id: I99107def65fdb98cc77833136df3dbb54dcb9a5b
diff --git a/driver/linux b/driver/linux
index 01f83de..b3ca1dc 160000
--- a/driver/linux
+++ b/driver/linux
@@ -1 +1 @@
-Subproject commit 01f83de57de79b28967cf5e29f2e98385ca4fb7f
+Subproject commit b3ca1dca1efd371052ea3f9c3409a7cad43ed4af
diff --git a/inc/hf/api.h b/inc/hf/api.h
index 95d97ab..5350b6a 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -26,6 +26,7 @@
 int64_t api_vm_get_id(const struct vcpu *current);
 int64_t api_vm_get_count(void);
 int64_t api_vcpu_get_count(uint32_t vm_id, const struct vcpu *current);
+void api_regs_state_saved(struct vcpu *vcpu);
 struct hf_vcpu_run_return api_vcpu_run(uint32_t vm_id, uint32_t vcpu_idx,
 				       const struct vcpu *current,
 				       struct vcpu **next);
diff --git a/src/arch/aarch64/exceptions.S b/src/arch/aarch64/exceptions.S
index 0f06bdd..036ae6b 100644
--- a/src/arch/aarch64/exceptions.S
+++ b/src/arch/aarch64/exceptions.S
@@ -311,9 +311,12 @@
 	/* Save new vcpu pointer in non-volatile register. */
 	mov x19, x0
 
-	/* Inform the arch-independent sections that regs have been saved. */
+	/*
+	 * Save peripheral registers, and inform the arch-independent sections
+	 * that registers have been saved.
+	 */
 	mov x0, x1
-	bl api_regs_state_saved
+	bl complete_saving_state
 	mov x0, x19
 
 	/* Intentional fallthrough. */
@@ -322,6 +325,11 @@
 	/* Update pointer to current vcpu. */
 	msr tpidr_el2, x0
 
+	/* Restore peripheral registers. */
+	mov x19, x0
+	bl begin_restoring_state
+	mov x0, x19
+
 	/* Restore lazy registers. */
 	ldp x24, x25, [x0, #VCPU_LAZY + 16 * 0]
 	msr vmpidr_el2, x24
diff --git a/src/arch/aarch64/handler.c b/src/arch/aarch64/handler.c
index f491227..df7ade3 100644
--- a/src/arch/aarch64/handler.c
+++ b/src/arch/aarch64/handler.c
@@ -39,6 +39,61 @@
 	return (struct vcpu *)read_msr(tpidr_el2);
 }
 
+/**
+ * Saves the state of per-vCPU peripherals, such as the virtual timer, and
+ * informs the arch-independent sections that registers have been saved.
+ */
+void complete_saving_state(struct vcpu *vcpu)
+{
+	vcpu->regs.lazy.cntv_cval_el0 = read_msr(cntv_cval_el0);
+	vcpu->regs.lazy.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 (vcpu->vm->id == HF_PRIMARY_VM_ID) {
+		/*
+		 * 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);
+		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.
+ */
+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.
+	 */
+	write_msr(cntv_ctl_el0, 0);
+	write_msr(cntv_cval_el0, vcpu->regs.lazy.cntv_cval_el0);
+	write_msr(cntv_ctl_el0, vcpu->regs.lazy.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 (vcpu->vm->id == HF_PRIMARY_VM_ID) {
+		write_msr(cnthp_ctl_el2, 0);
+		write_msr(cnthp_cval_el2, 0);
+	}
+}
+
 void irq_current_exception(uintreg_t elr, uintreg_t spsr)
 {
 	(void)elr;
diff --git a/src/arch/aarch64/inc/hf/arch/types.h b/src/arch/aarch64/inc/hf/arch/types.h
index e028adc..fdc55ab 100644
--- a/src/arch/aarch64/inc/hf/arch/types.h
+++ b/src/arch/aarch64/inc/hf/arch/types.h
@@ -40,7 +40,6 @@
 	uintreg_t pc;
 	uintreg_t spsr;
 
-	/* TODO: We need to save virtual timer state. */
 	struct {
 		uintreg_t vmpidr_el2;
 		uintreg_t csselr_el1;
@@ -69,5 +68,7 @@
 		uintreg_t cptr_el2;
 		uintreg_t cnthctl_el2;
 		uintreg_t vttbr_el2;
+		uintreg_t cntv_cval_el0;
+		uintreg_t cntv_ctl_el0;
 	} lazy;
 };
diff --git a/test/vmapi/gicv3/gicv3.c b/test/vmapi/gicv3/gicv3.c
index 03be357..3131fd5 100644
--- a/test/vmapi/gicv3/gicv3.c
+++ b/test/vmapi/gicv3/gicv3.c
@@ -28,6 +28,7 @@
 #define PPI_IRQ_BASE 16
 #define PHYSICAL_TIMER_IRQ (PPI_IRQ_BASE + 14)
 #define VIRTUAL_TIMER_IRQ (PPI_IRQ_BASE + 11)
+#define HYPERVISOR_TIMER_IRQ (PPI_IRQ_BASE + 10)
 
 #define NANOS_PER_UNIT 1000000000
 
@@ -251,6 +252,14 @@
 	interrupt_enable(VIRTUAL_TIMER_IRQ, true);
 	interrupt_set_priority(VIRTUAL_TIMER_IRQ, 0x80);
 	interrupt_set_edge_triggered(VIRTUAL_TIMER_IRQ, true);
+	/*
+	 * Hypervisor timer IRQ is needed for Hafnium to return control to the
+	 * primary if the (emulated) virtual timer fires while the secondary is
+	 * running.
+	 */
+	interrupt_enable(HYPERVISOR_TIMER_IRQ, true);
+	interrupt_set_priority(HYPERVISOR_TIMER_IRQ, 0x80);
+	interrupt_set_edge_triggered(HYPERVISOR_TIMER_IRQ, true);
 	interrupt_set_priority_mask(0xff);
 	arch_irq_enable();