feat(sve): manage SVE context
Enabling SVE support in Hafnium, save/restore vector and predicate
registers for context switch between NWd and SWd.
It is assumed that SVE vector length is 512b. This will be
platform-specific define in the future.
Signed-off-by: Maksims Svecovs <maksims.svecovs@arm.com>
Change-Id: Ib2833fb0744534a05dbaaa5c836f81c50f1e14db
diff --git a/src/arch/aarch64/hypervisor/exceptions.S b/src/arch/aarch64/hypervisor/exceptions.S
index 54b0deb..4be3380 100644
--- a/src/arch/aarch64/hypervisor/exceptions.S
+++ b/src/arch/aarch64/hypervisor/exceptions.S
@@ -13,6 +13,13 @@
#include "msr.h"
#include "exception_macros.S"
+
+/**
+ * PE feature information about SVE implementation in AArch64 state.
+ */
+#define ID_AA64PFR0_SVE_SHIFT (32)
+#define ID_AA64PFR0_SVE_LENGTH (4)
+
/**
* Saves the volatile registers into the register buffer of the current vCPU.
*/
@@ -106,28 +113,6 @@
.endm
/**
- * Helper macro for SIMD vectors save/restore operations.
- */
-.macro simd_op_vectors op reg
- \op q0, q1, [\reg], #32
- \op q2, q3, [\reg], #32
- \op q4, q5, [\reg], #32
- \op q6, q7, [\reg], #32
- \op q8, q9, [\reg], #32
- \op q10, q11, [\reg], #32
- \op q12, q13, [\reg], #32
- \op q14, q15, [\reg], #32
- \op q16, q17, [\reg], #32
- \op q18, q19, [\reg], #32
- \op q20, q21, [\reg], #32
- \op q22, q23, [\reg], #32
- \op q24, q25, [\reg], #32
- \op q26, q27, [\reg], #32
- \op q28, q29, [\reg], #32
- \op q30, q31, [\reg], #32
-.endm
-
-/**
* The following is the exception table. A pointer to it will be stored in
* register vbar_el2.
*/
@@ -396,9 +381,10 @@
*/
other_world_loop:
- /*
- * x19 holds the other world VM vCPU pointer.
- */
+ /* Check if SVE is implemented. */
+ mrs x0, id_aa64pfr0_el1
+ ubfx x0, x0, ID_AA64PFR0_SVE_SHIFT, ID_AA64PFR0_SVE_LENGTH
+ cbnz x0, sve_context_restore
/* Restore the other world SIMD context to the other world VM vCPU. */
add x18, x19, #VCPU_FREGS
@@ -406,8 +392,32 @@
ldp x0, x1, [x18]
msr fpsr, x0
msr fpcr, x1
+ b sve_skip_context_restore
- /* Prepare arguments from other world VM vCPU. */
+ /* Restore the other world SVE context from internal buffer. */
+sve_context_restore:
+ adrp x18, sve_other_world_context
+ add x18, x18, :lo12: sve_other_world_context
+ ldr x0, [x19, #VCPU_CPU]
+ bl cpu_index
+ mov x20, #SVE_CTX_SIZE
+ madd x18, x0, x20, x18
+
+ /* Restore vector registers. */
+ sve_op_vectors ldr, x18
+ /* Restore FFR register before predicates. */
+ add x20, x18, #SVE_CTX_FFR
+ ldr p0, [x20]
+ wrffr p0.b
+ /* Restore predicate registers. */
+ add x20, x18, #SVE_CTX_PREDICATES
+ sve_predicate_op ldr, x20
+
+ /*
+ * Prepare arguments from other world VM vCPU.
+ * x19 holds the other world VM vCPU pointer.
+ */
+sve_skip_context_restore:
ldp x0, x1, [x19, #VCPU_REGS + 8 * 0]
ldp x2, x3, [x19, #VCPU_REGS + 8 * 2]
ldp x4, x5, [x19, #VCPU_REGS + 8 * 4]
@@ -443,12 +453,39 @@
stp x4, x5, [x19, #VCPU_REGS + 8 * 4]
stp x6, x7, [x19, #VCPU_REGS + 8 * 6]
+ /* Check if SVE is implemented. */
+ mrs x0, id_aa64pfr0_el1
+ ubfx x0, x0, ID_AA64PFR0_SVE_SHIFT, ID_AA64PFR0_SVE_LENGTH
+ cbnz x0, sve_context_save
+
/* Save the other world SIMD context to the other world VM vCPU. */
add x18, x19, #VCPU_FREGS
simd_op_vectors stp, x18
mrs x0, fpsr
mrs x1, fpcr
stp x0, x1, [x18]
+ b sve_skip_context_save
+
+ /* Save the other world SVE context to internal buffer. */
+sve_context_save:
+ adrp x18, sve_other_world_context
+ add x18, x18, :lo12: sve_other_world_context
+ ldr x0, [x19, #VCPU_CPU]
+ bl cpu_index
+ mov x20, #SVE_CTX_SIZE
+ madd x18, x0, x20, x18
+
+ /* Save vector registers. */
+ sve_op_vectors str, x18
+ /* Save predicate registers. */
+ add x20, x18, #SVE_CTX_PREDICATES
+ sve_predicate_op str, x20
+ /* Save FFR register after predicates. */
+ add x20, x18, #SVE_CTX_FFR
+ rdffr p0.b
+ str p0, [x20]
+
+sve_skip_context_save:
#if BRANCH_PROTECTION
pauth_restore_hypervisor_key x0 x1