VHE: Update cpu code to allow for el0 partition execution

To run a el0 partitions vcpu the following changes are required:
1) HCR_EL2.TGE bit always needs to be set.
2) SPSR needs to be set to EL0t mode.
3) ttbr0_el2 needs to switched to the appropriate table. To do this a
new vcpu field for ttbr0_el2 is added. For normal VM's, ttbr0_el2 is the
same as the hypervisor page tables and for el0 partition's, the
appropriate page table and asid are used. ttbr0_el2 is now loaded on
every exit from el2.

Change-Id: I92a44874d820080a8e9a66dc0b6a628bf94d0b9d
Signed-off-by: Raghu Krishnamurthy <raghu.ncstate@gmail.com>
diff --git a/src/arch/aarch64/hypervisor/cpu.c b/src/arch/aarch64/hypervisor/cpu.c
index eeb9eb1..a75cff3 100644
--- a/src/arch/aarch64/hypervisor/cpu.c
+++ b/src/arch/aarch64/hypervisor/cpu.c
@@ -99,30 +99,45 @@
 		}
 	}
 
-	r->hcr_el2 = get_hcr_el2_value(vm_id);
+	r->hcr_el2 = get_hcr_el2_value(vm_id, vcpu->vm->el0_partition);
 	r->lazy.cnthctl_el2 = cnthctl;
-	r->lazy.vttbr_el2 = pa_addr(table) | ((uint64_t)vm_id << 48);
-	r->lazy.vmpidr_el2 = vcpu_id;
-	/* Mask (disable) interrupts and run in EL1h mode. */
-	r->spsr = PSR_D | PSR_A | PSR_I | PSR_F | PSR_PE_MODE_EL1H;
+	if (vcpu->vm->el0_partition) {
+		CHECK(has_vhe_support());
+		/*
+		 * AArch64 hafnium only uses 8 bit ASIDs at the moment.
+		 * TCR_EL2.AS is set to 0, and per the Arm ARM, the upper 8 bits
+		 * are ignored and treated as 0. There is no need to mask the
+		 * VMID (used as asid) to only 8 bits.
+		 */
+		r->ttbr0_el2 = pa_addr(table) | ((uint64_t)vm_id << 48);
+		r->spsr = PSR_PE_MODE_EL0T;
+	} else {
+		r->ttbr0_el2 = read_msr(ttbr0_el2);
+		r->lazy.vttbr_el2 = pa_addr(table) | ((uint64_t)vm_id << 48);
+		r->lazy.vmpidr_el2 = vcpu_id;
+		/* Mask (disable) interrupts and run in EL1h mode. */
+		r->spsr = PSR_D | PSR_A | PSR_I | PSR_F | PSR_PE_MODE_EL1H;
 
-	r->lazy.mdcr_el2 = get_mdcr_el2_value();
+		r->lazy.mdcr_el2 = get_mdcr_el2_value();
 
-	/*
-	 * NOTE: It is important that MDSCR_EL1.MDE (bit 15) is set to 0 for
-	 * secondary VMs as long as Hafnium does not support debug register
-	 * access for secondary VMs. If adding Hafnium support for secondary VM
-	 * debug register accesses, then on context switches Hafnium needs to
-	 * save/restore EL1 debug register state that either might change, or
-	 * that needs to be protected.
-	 */
-	r->lazy.mdscr_el1 = 0x0U & ~(0x1U << 15);
+		/*
+		 * NOTE: It is important that MDSCR_EL1.MDE (bit 15) is set to 0
+		 * for secondary VMs as long as Hafnium does not support debug
+		 * register access for secondary VMs. If adding Hafnium support
+		 * for secondary VM debug register accesses, then on context
+		 * switches Hafnium needs to save/restore EL1 debug register
+		 * state that either might change, or that needs to be
+		 * protected.
+		 */
+		r->lazy.mdscr_el1 = 0x0U & ~(0x1U << 15);
 
-	/* Disable cycle counting on initialization. */
-	r->lazy.pmccfiltr_el0 = perfmon_get_pmccfiltr_el0_init_value(vm_id);
+		/* Disable cycle counting on initialization. */
+		r->lazy.pmccfiltr_el0 =
+			perfmon_get_pmccfiltr_el0_init_value(vm_id);
 
-	/* Set feature-specific register values. */
-	feature_set_traps(vcpu->vm, r);
+		/* Set feature-specific register values. */
+		feature_set_traps(vcpu->vm, r);
+	}
 
 #if SECURE_WORLD == 1
 	/*
diff --git a/src/arch/aarch64/hypervisor/exceptions.S b/src/arch/aarch64/hypervisor/exceptions.S
index fb57960..2482d92 100644
--- a/src/arch/aarch64/hypervisor/exceptions.S
+++ b/src/arch/aarch64/hypervisor/exceptions.S
@@ -805,6 +805,11 @@
 
 	ldr x1, [x0, #VCPU_REGS + 8 * 33]
 	msr hcr_el2, x1
+	isb
+
+	ldr x1, [x0, #VCPU_REGS + 8 * 34]
+	msr ttbr0_el2, x1
+	isb
 
 	/* Restore x0..x3, which we have used as scratch before. */
 	ldp x2, x3, [x0, #VCPU_REGS + 8 * 2]
diff --git a/src/arch/aarch64/inc/hf/arch/types.h b/src/arch/aarch64/inc/hf/arch/types.h
index a8cdf46..0248522 100644
--- a/src/arch/aarch64/inc/hf/arch/types.h
+++ b/src/arch/aarch64/inc/hf/arch/types.h
@@ -80,6 +80,7 @@
 	uintreg_t pc;
 	uintreg_t spsr;
 	uintreg_t hcr_el2;
+	uintreg_t ttbr0_el2;
 
 	/*
 	 * System registers.
diff --git a/src/arch/aarch64/sysregs.c b/src/arch/aarch64/sysregs.c
index 6ca8187..124760d 100644
--- a/src/arch/aarch64/sysregs.c
+++ b/src/arch/aarch64/sysregs.c
@@ -27,7 +27,7 @@
  * Returns the value for HCR_EL2 for the particular VM.
  * For now, the primary VM has one value and all secondary VMs share a value.
  */
-uintreg_t get_hcr_el2_value(ffa_vm_id_t vm_id)
+uintreg_t get_hcr_el2_value(ffa_vm_id_t vm_id, bool is_el0_partition)
 {
 	uintreg_t hcr_el2_value = 0;
 
@@ -109,6 +109,9 @@
 	/* Enable VHE, if enabled by build and if HW supports it. */
 	if (has_vhe_support()) {
 		hcr_el2_value |= HCR_EL2_E2H;
+		if (is_el0_partition) {
+			hcr_el2_value |= HCR_EL2_TGE;
+		}
 	}
 
 	return hcr_el2_value;
diff --git a/src/arch/aarch64/sysregs.h b/src/arch/aarch64/sysregs.h
index da8885e..5f7a7c4 100644
--- a/src/arch/aarch64/sysregs.h
+++ b/src/arch/aarch64/sysregs.h
@@ -631,7 +631,7 @@
 #define ID_AA64MMFR1_EL1_VH_MASK UINT64_C(0xf)
 #define ID_AA64MMFR1_EL1_VH_SUPPORTED UINT64_C(0x1)
 
-uintreg_t get_hcr_el2_value(ffa_vm_id_t vm_id);
+uintreg_t get_hcr_el2_value(ffa_vm_id_t vm_id, bool is_el0_partition);
 
 uintreg_t get_mdcr_el2_value(void);