@@ -33,6 +33,9 @@
extern struct exception_table_entry __start___kvm_ex_table;
extern struct exception_table_entry __stop___kvm_ex_table;
+int kvm_handle_pvm_sys64(struct kvm_vcpu *vcpu);
+int kvm_handle_pvm_restricted(struct kvm_vcpu *vcpu);
+
/* Check whether the FP regs were dirtied while in the host-side run loop: */
static inline bool update_fp_enabled(struct kvm_vcpu *vcpu)
{
@@ -34,12 +34,63 @@ DEFINE_PER_CPU(struct kvm_host_data, kvm_host_data);
DEFINE_PER_CPU(struct kvm_cpu_context, kvm_hyp_ctxt);
DEFINE_PER_CPU(unsigned long, kvm_hyp_vector);
+/*
+ * Set EL2 configuration registers to trap restricted register accesses and
+ * instructions for protected VMs.
+ *
+ * Should be called right before vcpu entry to restrict its impact only to the
+ * protected guest.
+ */
+static void __activate_traps_pvm(struct kvm_vcpu *vcpu)
+{
+ u64 mdcr;
+ u64 hcr;
+ u64 cptr;
+
+ if (!kvm_vm_is_protected(kern_hyp_va(vcpu->kvm)))
+ return;
+
+ mdcr = read_sysreg(mdcr_el2);
+ hcr = read_sysreg(hcr_el2);
+ cptr = read_sysreg(cptr_el2);
+
+ hcr |= HCR_TID3 | /* Feature Registers */
+ HCR_TLOR | /* LOR */
+ HCR_RW | /* AArch64 EL1 only */
+ HCR_TERR | /* RAS */
+ HCR_ATA | HCR_TID5 | /* Memory Tagging */
+ HCR_TACR | HCR_TIDCP | HCR_TID1; /* Implementation defined */
+
+ hcr &= ~(HCR_DCT | /* Memory Tagging */
+ HCR_FIEN | /* RAS */
+ HCR_AMVOFFEN); /* Disables AMU registers virtualization */
+
+ /* Debug and Trace */
+ mdcr |= MDCR_EL2_TDRA | MDCR_EL2_TDA | MDCR_EL2_TDE |
+ MDCR_EL2_TDOSA | MDCR_EL2_TDCC | MDCR_EL2_TTRF |
+ MDCR_EL2_TPM | MDCR_EL2_TPMCR |
+ MDCR_EL2_TPMS; /* SPE */
+
+ mdcr &= ~(MDCR_EL2_HPME | MDCR_EL2_MTPME | /* PMU */
+ (MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT)); /* SPE */
+
+ cptr |= CPTR_EL2_TTA | /* Trace */
+ CPTR_EL2_TAM | /* AMU */
+ CPTR_EL2_TZ; /* SVE */
+
+ /* __deactivate_traps() restores these registers. */
+ write_sysreg(mdcr, mdcr_el2);
+ write_sysreg(hcr, hcr_el2);
+ write_sysreg(cptr, cptr_el2);
+}
+
static void __activate_traps(struct kvm_vcpu *vcpu)
{
u64 val;
___activate_traps(vcpu);
__activate_traps_common(vcpu);
+ __activate_traps_pvm(vcpu);
val = CPTR_EL2_DEFAULT;
val |= CPTR_EL2_TTA | CPTR_EL2_TAM;
@@ -165,30 +216,56 @@ static void __pmu_switch_to_host(struct kvm_cpu_context *host_ctxt)
write_sysreg(pmu->events_host, pmcntenset_el0);
}
+/**
+ * Handle system register accesses for protected VMs.
+ *
+ * Return 1 if handled, or 0 if not.
+ */
+static int handle_pvm_sys64(struct kvm_vcpu *vcpu)
+{
+ if (kvm_vm_is_protected(kern_hyp_va(vcpu->kvm)))
+ return kvm_handle_pvm_sys64(vcpu);
+ else
+ return 0;
+}
+
+/**
+ * Handle restricted feature accesses for protected VMs.
+ *
+ * Return 1 if handled, or 0 if not.
+ */
+static int handle_pvm_restricted(struct kvm_vcpu *vcpu)
+{
+ if (kvm_vm_is_protected(kern_hyp_va(vcpu->kvm)))
+ return kvm_handle_pvm_restricted(vcpu);
+ else
+ return 0;
+}
+
typedef int (*exit_handle_fn)(struct kvm_vcpu *);
static exit_handle_fn hyp_exit_handlers[] = {
- [0 ... ESR_ELx_EC_MAX] = NULL,
+ [0 ... ESR_ELx_EC_MAX] = handle_pvm_restricted,
[ESR_ELx_EC_WFx] = NULL,
- [ESR_ELx_EC_CP15_32] = NULL,
- [ESR_ELx_EC_CP15_64] = NULL,
- [ESR_ELx_EC_CP14_MR] = NULL,
- [ESR_ELx_EC_CP14_LS] = NULL,
- [ESR_ELx_EC_CP14_64] = NULL,
+ [ESR_ELx_EC_CP15_32] = handle_pvm_restricted,
+ [ESR_ELx_EC_CP15_64] = handle_pvm_restricted,
+ [ESR_ELx_EC_CP14_MR] = handle_pvm_restricted,
+ [ESR_ELx_EC_CP14_LS] = handle_pvm_restricted,
+ [ESR_ELx_EC_CP14_64] = handle_pvm_restricted,
[ESR_ELx_EC_HVC32] = NULL,
[ESR_ELx_EC_SMC32] = NULL,
[ESR_ELx_EC_HVC64] = NULL,
[ESR_ELx_EC_SMC64] = NULL,
- [ESR_ELx_EC_SYS64] = NULL,
- [ESR_ELx_EC_SVE] = NULL,
+ [ESR_ELx_EC_SYS64] = handle_pvm_sys64,
+ [ESR_ELx_EC_SVE] = handle_pvm_restricted,
[ESR_ELx_EC_IABT_LOW] = NULL,
[ESR_ELx_EC_DABT_LOW] = NULL,
- [ESR_ELx_EC_SOFTSTP_LOW] = NULL,
- [ESR_ELx_EC_WATCHPT_LOW] = NULL,
- [ESR_ELx_EC_BREAKPT_LOW] = NULL,
- [ESR_ELx_EC_BKPT32] = NULL,
- [ESR_ELx_EC_BRK64] = NULL,
- [ESR_ELx_EC_FP_ASIMD] = NULL,
+ [ESR_ELx_EC_SOFTSTP_LOW] = handle_pvm_restricted,
+ [ESR_ELx_EC_WATCHPT_LOW] = handle_pvm_restricted,
+ [ESR_ELx_EC_BREAKPT_LOW] = handle_pvm_restricted,
+ [ESR_ELx_EC_BKPT32] = handle_pvm_restricted,
+ [ESR_ELx_EC_BRK64] = handle_pvm_restricted,
+ [ESR_ELx_EC_FP_ASIMD] = handle_pvm_restricted,
[ESR_ELx_EC_PAC] = NULL,
};
Trap accesses to restricted features for VMs running in protected mode. Access to feature registers are emulated, and only supported features are exposed to protected VMs. Accesses to restricted registers as well as restricted instructions are trapped, and an undefined exception is injected into the protected guest. Only affects the functionality of protected VMs. Otherwise, should not affect non-protected VMs when KVM is running in protected mode. Signed-off-by: Fuad Tabba <tabba@google.com> --- arch/arm64/kvm/hyp/include/hyp/switch.h | 3 + arch/arm64/kvm/hyp/nvhe/switch.c | 105 ++++++++++++++++++++---- 2 files changed, 94 insertions(+), 14 deletions(-)