* [PATCH v2 01/16] KVM: arm64: nv: Handle CNTHCTL_EL2 specially
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 02/16] KVM: arm64: nv: Save/Restore vEL2 sysregs Marc Zyngier
` (14 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
Accessing CNTHCTL_EL2 is fraught with danger if running with
HCR_EL2.E2H=1: half of the bits are held in CNTKCTL_EL1, and
thus can be changed behind our back, while the rest lives
in the CNTHCTL_EL2 shadow copy that is memory-based.
Yes, this is a lot of fun!
Make sure that we merge the two on read access, while we can
write to CNTKCTL_EL1 in a more straightforward manner.
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/sys_regs.c | 28 ++++++++++++++++++++++++++++
include/kvm/arm_arch_timer.h | 3 +++
2 files changed, 31 insertions(+)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 5ab0b2799393..7563826f286a 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -140,6 +140,21 @@ u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg)
if (!is_hyp_ctxt(vcpu))
goto memory_read;
+ /*
+ * CNTHCTL_EL2 requires some special treatment to
+ * account for the bits that can be set via CNTKCTL_EL1.
+ */
+ switch (reg) {
+ case CNTHCTL_EL2:
+ if (vcpu_el2_e2h_is_set(vcpu)) {
+ val = read_sysreg_el1(SYS_CNTKCTL);
+ val &= CNTKCTL_VALID_BITS;
+ val |= __vcpu_sys_reg(vcpu, reg) & ~CNTKCTL_VALID_BITS;
+ return val;
+ }
+ break;
+ }
+
/*
* If this register does not have an EL1 counterpart,
* then read the stored EL2 version.
@@ -190,6 +205,19 @@ void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, int reg)
*/
__vcpu_sys_reg(vcpu, reg) = val;
+ switch (reg) {
+ case CNTHCTL_EL2:
+ /*
+ * If E2H=0, CNHTCTL_EL2 is a pure shadow register.
+ * Otherwise, some of the bits are backed by
+ * CNTKCTL_EL1, while the rest is kept in memory.
+ * Yes, this is fun stuff.
+ */
+ if (vcpu_el2_e2h_is_set(vcpu))
+ write_sysreg_el1(val, SYS_CNTKCTL);
+ return;
+ }
+
/* No EL1 counterpart? We're done here.? */
if (reg == el1r)
return;
diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h
index c819c5d16613..fd650a8789b9 100644
--- a/include/kvm/arm_arch_timer.h
+++ b/include/kvm/arm_arch_timer.h
@@ -147,6 +147,9 @@ u64 timer_get_cval(struct arch_timer_context *ctxt);
void kvm_timer_cpu_up(void);
void kvm_timer_cpu_down(void);
+/* CNTKCTL_EL1 valid bits as of DDI0487J.a */
+#define CNTKCTL_VALID_BITS (BIT(17) | GENMASK_ULL(9, 0))
+
static inline bool has_cntpoff(void)
{
return (has_vhe() && cpus_have_final_cap(ARM64_HAS_ECV_CNTPOFF));
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 02/16] KVM: arm64: nv: Save/Restore vEL2 sysregs
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 01/16] KVM: arm64: nv: Handle CNTHCTL_EL2 specially Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 03/16] KVM: arm64: Add TCR2_EL2 to the sysreg arrays Marc Zyngier
` (13 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
Whenever we need to restore the guest's system registers to the CPU, we
now need to take care of the EL2 system registers as well. Most of them
are accessed via traps only, but some have an immediate effect and also
a guest running in VHE mode would expect them to be accessible via their
EL1 encoding, which we do not trap.
For vEL2 we write the virtual EL2 registers with an identical format directly
into their EL1 counterpart, and translate the few registers that have a
different format for the same effect on the execution when running a
non-VHE guest guest hypervisor.
Based on an initial patch from Andre Przywara, rewritten many times
since.
Reviewed-by: Alexandru Elisei <alexandru.elisei@arm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h | 5 +-
arch/arm64/kvm/hyp/nvhe/sysreg-sr.c | 2 +-
arch/arm64/kvm/hyp/vhe/sysreg-sr.c | 137 ++++++++++++++++++++-
3 files changed, 139 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h b/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h
index 4c0fdabaf8ae..dfbc0159bf0b 100644
--- a/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h
+++ b/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h
@@ -128,9 +128,10 @@ static inline void __sysreg_restore_user_state(struct kvm_cpu_context *ctxt)
write_sysreg(ctxt_sys_reg(ctxt, TPIDRRO_EL0), tpidrro_el0);
}
-static inline void __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)
+static inline void __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt,
+ u64 mpidr)
{
- write_sysreg(ctxt_sys_reg(ctxt, MPIDR_EL1), vmpidr_el2);
+ write_sysreg(mpidr, vmpidr_el2);
if (has_vhe() ||
!cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
diff --git a/arch/arm64/kvm/hyp/nvhe/sysreg-sr.c b/arch/arm64/kvm/hyp/nvhe/sysreg-sr.c
index 29305022bc04..dba101565de3 100644
--- a/arch/arm64/kvm/hyp/nvhe/sysreg-sr.c
+++ b/arch/arm64/kvm/hyp/nvhe/sysreg-sr.c
@@ -28,7 +28,7 @@ void __sysreg_save_state_nvhe(struct kvm_cpu_context *ctxt)
void __sysreg_restore_state_nvhe(struct kvm_cpu_context *ctxt)
{
- __sysreg_restore_el1_state(ctxt);
+ __sysreg_restore_el1_state(ctxt, ctxt_sys_reg(ctxt, MPIDR_EL1));
__sysreg_restore_common_state(ctxt);
__sysreg_restore_user_state(ctxt);
__sysreg_restore_el2_return_state(ctxt);
diff --git a/arch/arm64/kvm/hyp/vhe/sysreg-sr.c b/arch/arm64/kvm/hyp/vhe/sysreg-sr.c
index e12bd7d6d2dc..6db5b4d0f3a4 100644
--- a/arch/arm64/kvm/hyp/vhe/sysreg-sr.c
+++ b/arch/arm64/kvm/hyp/vhe/sysreg-sr.c
@@ -15,6 +15,108 @@
#include <asm/kvm_hyp.h>
#include <asm/kvm_nested.h>
+static void __sysreg_save_vel2_state(struct kvm_cpu_context *ctxt)
+{
+ /* These registers are common with EL1 */
+ ctxt_sys_reg(ctxt, PAR_EL1) = read_sysreg(par_el1);
+ ctxt_sys_reg(ctxt, TPIDR_EL1) = read_sysreg(tpidr_el1);
+
+ ctxt_sys_reg(ctxt, ESR_EL2) = read_sysreg_el1(SYS_ESR);
+ ctxt_sys_reg(ctxt, AFSR0_EL2) = read_sysreg_el1(SYS_AFSR0);
+ ctxt_sys_reg(ctxt, AFSR1_EL2) = read_sysreg_el1(SYS_AFSR1);
+ ctxt_sys_reg(ctxt, FAR_EL2) = read_sysreg_el1(SYS_FAR);
+ ctxt_sys_reg(ctxt, MAIR_EL2) = read_sysreg_el1(SYS_MAIR);
+ ctxt_sys_reg(ctxt, VBAR_EL2) = read_sysreg_el1(SYS_VBAR);
+ ctxt_sys_reg(ctxt, CONTEXTIDR_EL2) = read_sysreg_el1(SYS_CONTEXTIDR);
+ ctxt_sys_reg(ctxt, AMAIR_EL2) = read_sysreg_el1(SYS_AMAIR);
+
+ /*
+ * In VHE mode those registers are compatible between EL1 and EL2,
+ * and the guest uses the _EL1 versions on the CPU naturally.
+ * So we save them into their _EL2 versions here.
+ * For nVHE mode we trap accesses to those registers, so our
+ * _EL2 copy in sys_regs[] is always up-to-date and we don't need
+ * to save anything here.
+ */
+ if (__vcpu_el2_e2h_is_set(ctxt)) {
+ u64 val;
+
+ /*
+ * We don't save CPTR_EL2, as accesses to CPACR_EL1
+ * are always trapped, ensuring that the in-memory
+ * copy is always up-to-date. A small blessing...
+ */
+ ctxt_sys_reg(ctxt, SCTLR_EL2) = read_sysreg_el1(SYS_SCTLR);
+ ctxt_sys_reg(ctxt, TTBR0_EL2) = read_sysreg_el1(SYS_TTBR0);
+ ctxt_sys_reg(ctxt, TTBR1_EL2) = read_sysreg_el1(SYS_TTBR1);
+ ctxt_sys_reg(ctxt, TCR_EL2) = read_sysreg_el1(SYS_TCR);
+
+ /*
+ * The EL1 view of CNTKCTL_EL1 has a bunch of RES0 bits where
+ * the interesting CNTHCTL_EL2 bits live. So preserve these
+ * bits when reading back the guest-visible value.
+ */
+ val = read_sysreg_el1(SYS_CNTKCTL);
+ val &= CNTKCTL_VALID_BITS;
+ ctxt_sys_reg(ctxt, CNTHCTL_EL2) &= ~CNTKCTL_VALID_BITS;
+ ctxt_sys_reg(ctxt, CNTHCTL_EL2) |= val;
+ }
+
+ ctxt_sys_reg(ctxt, SP_EL2) = read_sysreg(sp_el1);
+ ctxt_sys_reg(ctxt, ELR_EL2) = read_sysreg_el1(SYS_ELR);
+ ctxt_sys_reg(ctxt, SPSR_EL2) = read_sysreg_el1(SYS_SPSR);
+}
+
+static void __sysreg_restore_vel2_state(struct kvm_cpu_context *ctxt)
+{
+ u64 val;
+
+ /* These registers are common with EL1 */
+ write_sysreg(ctxt_sys_reg(ctxt, PAR_EL1), par_el1);
+ write_sysreg(ctxt_sys_reg(ctxt, TPIDR_EL1), tpidr_el1);
+
+ write_sysreg(read_cpuid_id(), vpidr_el2);
+ write_sysreg(ctxt_sys_reg(ctxt, MPIDR_EL1), vmpidr_el2);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, MAIR_EL2), SYS_MAIR);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, VBAR_EL2), SYS_VBAR);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, CONTEXTIDR_EL2),SYS_CONTEXTIDR);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, AMAIR_EL2), SYS_AMAIR);
+
+ if (__vcpu_el2_e2h_is_set(ctxt)) {
+ /*
+ * In VHE mode those registers are compatible between
+ * EL1 and EL2.
+ */
+ write_sysreg_el1(ctxt_sys_reg(ctxt, SCTLR_EL2), SYS_SCTLR);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, CPTR_EL2), SYS_CPACR);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, TTBR0_EL2), SYS_TTBR0);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, TTBR1_EL2), SYS_TTBR1);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, TCR_EL2), SYS_TCR);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, CNTHCTL_EL2), SYS_CNTKCTL);
+ } else {
+ /*
+ * CNTHCTL_EL2 only affects EL1 when running nVHE, so
+ * no need to restore it.
+ */
+ val = translate_sctlr_el2_to_sctlr_el1(ctxt_sys_reg(ctxt, SCTLR_EL2));
+ write_sysreg_el1(val, SYS_SCTLR);
+ val = translate_cptr_el2_to_cpacr_el1(ctxt_sys_reg(ctxt, CPTR_EL2));
+ write_sysreg_el1(val, SYS_CPACR);
+ val = translate_ttbr0_el2_to_ttbr0_el1(ctxt_sys_reg(ctxt, TTBR0_EL2));
+ write_sysreg_el1(val, SYS_TTBR0);
+ val = translate_tcr_el2_to_tcr_el1(ctxt_sys_reg(ctxt, TCR_EL2));
+ write_sysreg_el1(val, SYS_TCR);
+ }
+
+ write_sysreg_el1(ctxt_sys_reg(ctxt, ESR_EL2), SYS_ESR);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, AFSR0_EL2), SYS_AFSR0);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, AFSR1_EL2), SYS_AFSR1);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, FAR_EL2), SYS_FAR);
+ write_sysreg(ctxt_sys_reg(ctxt, SP_EL2), sp_el1);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, ELR_EL2), SYS_ELR);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, SPSR_EL2), SYS_SPSR);
+}
+
/*
* VHE: Host and guest must save mdscr_el1 and sp_el0 (and the PC and
* pstate, which are handled as part of the el2 return state) on every
@@ -66,6 +168,7 @@ void __vcpu_load_switch_sysregs(struct kvm_vcpu *vcpu)
{
struct kvm_cpu_context *guest_ctxt = &vcpu->arch.ctxt;
struct kvm_cpu_context *host_ctxt;
+ u64 mpidr;
host_ctxt = host_data_ptr(host_ctxt);
__sysreg_save_user_state(host_ctxt);
@@ -89,7 +192,29 @@ void __vcpu_load_switch_sysregs(struct kvm_vcpu *vcpu)
*/
__sysreg32_restore_state(vcpu);
__sysreg_restore_user_state(guest_ctxt);
- __sysreg_restore_el1_state(guest_ctxt);
+
+ if (unlikely(__is_hyp_ctxt(guest_ctxt))) {
+ __sysreg_restore_vel2_state(guest_ctxt);
+ } else {
+ if (vcpu_has_nv(vcpu)) {
+ /*
+ * Only set VPIDR_EL2 for nested VMs, as this is the
+ * only time it changes. We'll restore the MIDR_EL1
+ * view on put.
+ */
+ write_sysreg(ctxt_sys_reg(guest_ctxt, VPIDR_EL2), vpidr_el2);
+
+ /*
+ * As we're restoring a nested guest, set the value
+ * provided by the guest hypervisor.
+ */
+ mpidr = ctxt_sys_reg(guest_ctxt, VMPIDR_EL2);
+ } else {
+ mpidr = ctxt_sys_reg(guest_ctxt, MPIDR_EL1);
+ }
+
+ __sysreg_restore_el1_state(guest_ctxt, mpidr);
+ }
vcpu_set_flag(vcpu, SYSREGS_ON_CPU);
}
@@ -112,12 +237,20 @@ void __vcpu_put_switch_sysregs(struct kvm_vcpu *vcpu)
host_ctxt = host_data_ptr(host_ctxt);
- __sysreg_save_el1_state(guest_ctxt);
+ if (unlikely(__is_hyp_ctxt(guest_ctxt)))
+ __sysreg_save_vel2_state(guest_ctxt);
+ else
+ __sysreg_save_el1_state(guest_ctxt);
+
__sysreg_save_user_state(guest_ctxt);
__sysreg32_save_state(vcpu);
/* Restore host user state */
__sysreg_restore_user_state(host_ctxt);
+ /* If leaving a nesting guest, restore MPIDR_EL1 default view */
+ if (vcpu_has_nv(vcpu))
+ write_sysreg(read_cpuid_id(), vpidr_el2);
+
vcpu_clear_flag(vcpu, SYSREGS_ON_CPU);
}
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 03/16] KVM: arm64: Add TCR2_EL2 to the sysreg arrays
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 01/16] KVM: arm64: nv: Handle CNTHCTL_EL2 specially Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 02/16] KVM: arm64: nv: Save/Restore vEL2 sysregs Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 04/16] KVM: arm64: Add save/restore for TCR2_EL2 Marc Zyngier
` (12 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
Add the TCR2_EL2 register to the per-vcpu sysreg register
array, as well as the sysreg descriptor array.
Access to this register is conditional based on ID_AA64MMFR3_EL1.TCRX
being advertised.
Reviewed-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/include/asm/kvm_host.h | 1 +
arch/arm64/kvm/sys_regs.c | 13 +++++++++++++
2 files changed, 14 insertions(+)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index a33f5996ca9f..5a9e0ad35580 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -462,6 +462,7 @@ enum vcpu_sysreg {
TTBR0_EL2, /* Translation Table Base Register 0 (EL2) */
TTBR1_EL2, /* Translation Table Base Register 1 (EL2) */
TCR_EL2, /* Translation Control Register (EL2) */
+ TCR2_EL2, /* Extended Translation Control Register (EL2) */
SPSR_EL2, /* EL2 saved program status register */
ELR_EL2, /* EL2 exception link register */
AFSR0_EL2, /* Auxiliary Fault Status Register 0 (EL2) */
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 7563826f286a..0510e96f732a 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -436,6 +436,18 @@ static bool access_vm_reg(struct kvm_vcpu *vcpu,
return true;
}
+static bool access_tcr2_el2(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *r)
+{
+ if (!kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, TCRX, IMP)) {
+ kvm_inject_undefined(vcpu);
+ return false;
+ }
+
+ return access_rw(vcpu, p, r);
+}
+
static bool access_actlr(struct kvm_vcpu *vcpu,
struct sys_reg_params *p,
const struct sys_reg_desc *r)
@@ -2783,6 +2795,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
EL2_REG(TTBR0_EL2, access_rw, reset_val, 0),
EL2_REG(TTBR1_EL2, access_rw, reset_val, 0),
EL2_REG(TCR_EL2, access_rw, reset_val, TCR_EL2_RES1),
+ EL2_REG(TCR2_EL2, access_tcr2_el2, reset_val, TCR2_EL2_RES1),
EL2_REG_VNCR(VTTBR_EL2, reset_val, 0),
EL2_REG_VNCR(VTCR_EL2, reset_val, 0),
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 04/16] KVM: arm64: Add save/restore for TCR2_EL2
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
` (2 preceding siblings ...)
2024-09-03 15:38 ` [PATCH v2 03/16] KVM: arm64: Add TCR2_EL2 to the sysreg arrays Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 05/16] arm64: Add encoding for PIRE0_EL2 Marc Zyngier
` (11 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
Like its EL1 equivalent, TCR2_EL2 gets context-switched.
This is made conditional on FEAT_TCRX being adversised.
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/hyp/vhe/sysreg-sr.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/kvm/hyp/vhe/sysreg-sr.c b/arch/arm64/kvm/hyp/vhe/sysreg-sr.c
index 6db5b4d0f3a4..7099775cd505 100644
--- a/arch/arm64/kvm/hyp/vhe/sysreg-sr.c
+++ b/arch/arm64/kvm/hyp/vhe/sysreg-sr.c
@@ -51,6 +51,9 @@ static void __sysreg_save_vel2_state(struct kvm_cpu_context *ctxt)
ctxt_sys_reg(ctxt, TTBR1_EL2) = read_sysreg_el1(SYS_TTBR1);
ctxt_sys_reg(ctxt, TCR_EL2) = read_sysreg_el1(SYS_TCR);
+ if (ctxt_has_tcrx(ctxt))
+ ctxt_sys_reg(ctxt, TCR2_EL2) = read_sysreg_el1(SYS_TCR2);
+
/*
* The EL1 view of CNTKCTL_EL1 has a bunch of RES0 bits where
* the interesting CNTHCTL_EL2 bits live. So preserve these
@@ -108,6 +111,9 @@ static void __sysreg_restore_vel2_state(struct kvm_cpu_context *ctxt)
write_sysreg_el1(val, SYS_TCR);
}
+ if (ctxt_has_tcrx(ctxt))
+ write_sysreg_el1(ctxt_sys_reg(ctxt, TCR2_EL2), SYS_TCR2);
+
write_sysreg_el1(ctxt_sys_reg(ctxt, ESR_EL2), SYS_ESR);
write_sysreg_el1(ctxt_sys_reg(ctxt, AFSR0_EL2), SYS_AFSR0);
write_sysreg_el1(ctxt_sys_reg(ctxt, AFSR1_EL2), SYS_AFSR1);
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 05/16] arm64: Add encoding for PIRE0_EL2
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
` (3 preceding siblings ...)
2024-09-03 15:38 ` [PATCH v2 04/16] KVM: arm64: Add save/restore for TCR2_EL2 Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 06/16] arm64: Remove VNCR definition " Marc Zyngier
` (10 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
PIRE0_EL2 is the equivalent of PIRE0_EL1 for the EL2&0 translation
regime, and it is sorely missing from the sysreg file.
Add the sucker.
Reviewed-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/tools/sysreg | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 7ceaa1e0b4bc..8e1aed548e93 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -2853,6 +2853,10 @@ Sysreg PIRE0_EL12 3 5 10 2 2
Fields PIRx_ELx
EndSysreg
+Sysreg PIRE0_EL2 3 4 10 2 2
+Fields PIRx_ELx
+EndSysreg
+
Sysreg PIR_EL1 3 0 10 2 3
Fields PIRx_ELx
EndSysreg
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 06/16] arm64: Remove VNCR definition for PIRE0_EL2
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
` (4 preceding siblings ...)
2024-09-03 15:38 ` [PATCH v2 05/16] arm64: Add encoding for PIRE0_EL2 Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 07/16] KVM: arm64: Add PIR{,E0}_EL2 to the sysreg arrays Marc Zyngier
` (9 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
As of the ARM ARM Known Issues document 102105_K.a_04_en, D22677
fixes a problem with the PIRE0_EL2 register, resulting in its
removal from the VNCR page (it had no purpose being there the
first place).
Follow the architecture update by removing this offset.
Reviewed-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/include/asm/vncr_mapping.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/arch/arm64/include/asm/vncr_mapping.h b/arch/arm64/include/asm/vncr_mapping.h
index df2c47c55972..9e593bb60975 100644
--- a/arch/arm64/include/asm/vncr_mapping.h
+++ b/arch/arm64/include/asm/vncr_mapping.h
@@ -50,7 +50,6 @@
#define VNCR_VBAR_EL1 0x250
#define VNCR_TCR2_EL1 0x270
#define VNCR_PIRE0_EL1 0x290
-#define VNCR_PIRE0_EL2 0x298
#define VNCR_PIR_EL1 0x2A0
#define VNCR_ICH_LR0_EL2 0x400
#define VNCR_ICH_LR1_EL2 0x408
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 07/16] KVM: arm64: Add PIR{,E0}_EL2 to the sysreg arrays
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
` (5 preceding siblings ...)
2024-09-03 15:38 ` [PATCH v2 06/16] arm64: Remove VNCR definition " Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 08/16] KVM: arm64: Add save/restore for PIR{,E0}_EL2 Marc Zyngier
` (8 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
Add the FEAT_S1PIE EL2 registers to the per-vcpu sysreg register
array.
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/include/asm/kvm_host.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 5a9e0ad35580..ab4c675b491d 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -463,6 +463,8 @@ enum vcpu_sysreg {
TTBR1_EL2, /* Translation Table Base Register 1 (EL2) */
TCR_EL2, /* Translation Control Register (EL2) */
TCR2_EL2, /* Extended Translation Control Register (EL2) */
+ PIRE0_EL2, /* Permission Indirection Register 0 (EL2) */
+ PIR_EL2, /* Permission Indirection Register 1 (EL2) */
SPSR_EL2, /* EL2 saved program status register */
ELR_EL2, /* EL2 exception link register */
AFSR0_EL2, /* Auxiliary Fault Status Register 0 (EL2) */
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 08/16] KVM: arm64: Add save/restore for PIR{,E0}_EL2
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
` (6 preceding siblings ...)
2024-09-03 15:38 ` [PATCH v2 07/16] KVM: arm64: Add PIR{,E0}_EL2 to the sysreg arrays Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 09/16] KVM: arm64: Handle PIR{,E0}_EL2 traps Marc Zyngier
` (7 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
Like their EL1 equivalent, the EL2-specific FEAT_S1PIE registers
are context-switched. This is made conditional on both FEAT_TCRX
and FEAT_S1PIE being adversised.
Note that this change only makes sense if read together with the
issue D22677 contained in 102105_K.a_04_en.
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/hyp/vhe/sysreg-sr.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/hyp/vhe/sysreg-sr.c b/arch/arm64/kvm/hyp/vhe/sysreg-sr.c
index 7099775cd505..01551037df08 100644
--- a/arch/arm64/kvm/hyp/vhe/sysreg-sr.c
+++ b/arch/arm64/kvm/hyp/vhe/sysreg-sr.c
@@ -51,9 +51,15 @@ static void __sysreg_save_vel2_state(struct kvm_cpu_context *ctxt)
ctxt_sys_reg(ctxt, TTBR1_EL2) = read_sysreg_el1(SYS_TTBR1);
ctxt_sys_reg(ctxt, TCR_EL2) = read_sysreg_el1(SYS_TCR);
- if (ctxt_has_tcrx(ctxt))
+ if (ctxt_has_tcrx(ctxt)) {
ctxt_sys_reg(ctxt, TCR2_EL2) = read_sysreg_el1(SYS_TCR2);
+ if (ctxt_has_s1pie(ctxt)) {
+ ctxt_sys_reg(ctxt, PIRE0_EL2) = read_sysreg_el1(SYS_PIRE0);
+ ctxt_sys_reg(ctxt, PIR_EL2) = read_sysreg_el1(SYS_PIR);
+ }
+ }
+
/*
* The EL1 view of CNTKCTL_EL1 has a bunch of RES0 bits where
* the interesting CNTHCTL_EL2 bits live. So preserve these
@@ -111,9 +117,15 @@ static void __sysreg_restore_vel2_state(struct kvm_cpu_context *ctxt)
write_sysreg_el1(val, SYS_TCR);
}
- if (ctxt_has_tcrx(ctxt))
+ if (ctxt_has_tcrx(ctxt)) {
write_sysreg_el1(ctxt_sys_reg(ctxt, TCR2_EL2), SYS_TCR2);
+ if (ctxt_has_s1pie(ctxt)) {
+ write_sysreg_el1(ctxt_sys_reg(ctxt, PIR_EL2), SYS_PIR);
+ write_sysreg_el1(ctxt_sys_reg(ctxt, PIRE0_EL2), SYS_PIRE0);
+ }
+ }
+
write_sysreg_el1(ctxt_sys_reg(ctxt, ESR_EL2), SYS_ESR);
write_sysreg_el1(ctxt_sys_reg(ctxt, AFSR0_EL2), SYS_AFSR0);
write_sysreg_el1(ctxt_sys_reg(ctxt, AFSR1_EL2), SYS_AFSR1);
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 09/16] KVM: arm64: Handle PIR{,E0}_EL2 traps
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
` (7 preceding siblings ...)
2024-09-03 15:38 ` [PATCH v2 08/16] KVM: arm64: Add save/restore for PIR{,E0}_EL2 Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 10/16] KVM: arm64: Sanitise ID_AA64MMFR3_EL1 Marc Zyngier
` (6 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
Add the FEAT_S1PIE EL2 registers the sysreg descriptor array so that
they can be handled as a trap.
Access to these registers is conditional based on ID_AA64MMFR3_EL1.S1PIE
being advertised.
Similarly to other other changes, PIRE0_EL2 is guaranteed to trap
thanks to the D22677 update to the architecture.
Reviewed-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/sys_regs.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 0510e96f732a..a6bc20c238bf 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -346,6 +346,18 @@ static bool access_rw(struct kvm_vcpu *vcpu,
return true;
}
+static bool check_s1pie_access_rw(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *r)
+{
+ if (!kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, S1PIE, IMP)) {
+ kvm_inject_undefined(vcpu);
+ return false;
+ }
+
+ return access_rw(vcpu, p, r);
+}
+
/*
* See note at ARMv7 ARM B1.14.4 (TL;DR: S/W ops are not easily virtualized).
*/
@@ -2827,6 +2839,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
EL2_REG(HPFAR_EL2, access_rw, reset_val, 0),
EL2_REG(MAIR_EL2, access_rw, reset_val, 0),
+ EL2_REG(PIRE0_EL2, check_s1pie_access_rw, reset_val, 0),
+ EL2_REG(PIR_EL2, check_s1pie_access_rw, reset_val, 0),
EL2_REG(AMAIR_EL2, access_rw, reset_val, 0),
EL2_REG(VBAR_EL2, access_rw, reset_val, 0),
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 10/16] KVM: arm64: Sanitise ID_AA64MMFR3_EL1
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
` (8 preceding siblings ...)
2024-09-03 15:38 ` [PATCH v2 09/16] KVM: arm64: Handle PIR{,E0}_EL2 traps Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 11/16] KVM: arm64: Split S1 permission evaluation into direct and hierarchical parts Marc Zyngier
` (5 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
Add the missing sanitisation of ID_AA64MMFR3_EL1, making sure we
solely expose S1PIE and TCRX (we currently don't support anything
else).
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/sys_regs.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index a6bc20c238bf..7f4f69351e89 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1608,6 +1608,9 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu,
case SYS_ID_AA64MMFR2_EL1:
val &= ~ID_AA64MMFR2_EL1_CCIDX_MASK;
break;
+ case SYS_ID_AA64MMFR3_EL1:
+ val &= ID_AA64MMFR3_EL1_TCRX | ID_AA64MMFR3_EL1_S1PIE;
+ break;
case SYS_ID_MMFR4_EL1:
val &= ~ARM64_FEATURE_MASK(ID_MMFR4_EL1_CCIDX);
break;
@@ -2470,7 +2473,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
ID_AA64MMFR2_EL1_IDS |
ID_AA64MMFR2_EL1_NV |
ID_AA64MMFR2_EL1_CCIDX)),
- ID_SANITISED(ID_AA64MMFR3_EL1),
+ ID_WRITABLE(ID_AA64MMFR3_EL1, (ID_AA64MMFR3_EL1_TCRX |
+ ID_AA64MMFR3_EL1_S1PIE)),
ID_SANITISED(ID_AA64MMFR4_EL1),
ID_UNALLOCATED(7,5),
ID_UNALLOCATED(7,6),
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 11/16] KVM: arm64: Split S1 permission evaluation into direct and hierarchical parts
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
` (9 preceding siblings ...)
2024-09-03 15:38 ` [PATCH v2 10/16] KVM: arm64: Sanitise ID_AA64MMFR3_EL1 Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 12/16] KVM: arm64: Implement AT S1PIE support Marc Zyngier
` (4 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
The AArch64.S1DirectBasePermissions() pseudocode deals with both
direct and hierarchical S1 permission evaluation. While this is
probably convenient in the pseudocode, we would like a bit more
flexibility to slot things like indirect permissions.
To that effect, split the two permission check parts out of
handle_at_slow() and into their own functions. The permissions
are passed around in a new s1_perms type that contains the
individual permissions across the flow.
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/at.c | 164 ++++++++++++++++++++++++++------------------
1 file changed, 99 insertions(+), 65 deletions(-)
diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c
index 39f0e87a340e..68f5b89598ec 100644
--- a/arch/arm64/kvm/at.c
+++ b/arch/arm64/kvm/at.c
@@ -47,6 +47,10 @@ struct s1_walk_result {
bool failed;
};
+struct s1_perms {
+ bool ur, uw, ux, pr, pw, px;
+};
+
static void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool ptw, bool s2)
{
wr->fst = fst;
@@ -747,111 +751,141 @@ static bool pan3_enabled(struct kvm_vcpu *vcpu, enum trans_regime regime)
return sctlr & SCTLR_EL1_EPAN;
}
-static u64 handle_at_slow(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
+static void compute_s1_direct_permissions(struct kvm_vcpu *vcpu,
+ struct s1_walk_info *wi,
+ struct s1_walk_result *wr,
+ struct s1_perms *s1p)
{
- bool perm_fail, ur, uw, ux, pr, pw, px;
- struct s1_walk_result wr = {};
- struct s1_walk_info wi = {};
- int ret, idx;
-
- ret = setup_s1_walk(vcpu, op, &wi, &wr, vaddr);
- if (ret)
- goto compute_par;
-
- if (wr.level == S1_MMU_DISABLED)
- goto compute_par;
-
- idx = srcu_read_lock(&vcpu->kvm->srcu);
-
- ret = walk_s1(vcpu, &wi, &wr, vaddr);
-
- srcu_read_unlock(&vcpu->kvm->srcu, idx);
-
- if (ret)
- goto compute_par;
-
- /* FIXME: revisit when adding indirect permission support */
- /* AArch64.S1DirectBasePermissions() */
- if (wi.regime != TR_EL2) {
- switch (FIELD_GET(PTE_USER | PTE_RDONLY, wr.desc)) {
+ /* Non-hierarchical part of AArch64.S1DirectBasePermissions() */
+ if (wi->regime != TR_EL2) {
+ switch (FIELD_GET(PTE_USER | PTE_RDONLY, wr->desc)) {
case 0b00:
- pr = pw = true;
- ur = uw = false;
+ s1p->pr = s1p->pw = true;
+ s1p->ur = s1p->uw = false;
break;
case 0b01:
- pr = pw = ur = uw = true;
+ s1p->pr = s1p->pw = s1p->ur = s1p->uw = true;
break;
case 0b10:
- pr = true;
- pw = ur = uw = false;
+ s1p->pr = true;
+ s1p->pw = s1p->ur = s1p->uw = false;
break;
case 0b11:
- pr = ur = true;
- pw = uw = false;
+ s1p->pr = s1p->ur = true;
+ s1p->pw = s1p->uw = false;
break;
}
- switch (wr.APTable) {
+ /* We don't use px for anything yet, but hey... */
+ s1p->px = !((wr->desc & PTE_PXN) || s1p->uw);
+ s1p->ux = !(wr->desc & PTE_UXN);
+ } else {
+ s1p->ur = s1p->uw = s1p->ux = false;
+
+ if (!(wr->desc & PTE_RDONLY)) {
+ s1p->pr = s1p->pw = true;
+ } else {
+ s1p->pr = true;
+ s1p->pw = false;
+ }
+
+ /* XN maps to UXN */
+ s1p->px = !(wr->desc & PTE_UXN);
+ }
+}
+
+static void compute_s1_hierarchical_permissions(struct kvm_vcpu *vcpu,
+ struct s1_walk_info *wi,
+ struct s1_walk_result *wr,
+ struct s1_perms *s1p)
+{
+ /* Hierarchical part of AArch64.S1DirectBasePermissions() */
+ if (wi->regime != TR_EL2) {
+ switch (wr->APTable) {
case 0b00:
break;
case 0b01:
- ur = uw = false;
+ s1p->ur = s1p->uw = false;
break;
case 0b10:
- pw = uw = false;
+ s1p->pw = s1p->uw = false;
break;
case 0b11:
- pw = ur = uw = false;
+ s1p->pw = s1p->ur = s1p->uw = false;
break;
}
- /* We don't use px for anything yet, but hey... */
- px = !((wr.desc & PTE_PXN) || wr.PXNTable || uw);
- ux = !((wr.desc & PTE_UXN) || wr.UXNTable);
-
- if (op == OP_AT_S1E1RP || op == OP_AT_S1E1WP) {
- bool pan;
-
- pan = *vcpu_cpsr(vcpu) & PSR_PAN_BIT;
- pan &= ur || uw || (pan3_enabled(vcpu, wi.regime) && ux);
- pw &= !pan;
- pr &= !pan;
- }
+ s1p->px &= !wr->PXNTable;
+ s1p->ux &= !wr->UXNTable;
} else {
- ur = uw = ux = false;
+ if (wr->APTable & BIT(1))
+ s1p->pw = false;
- if (!(wr.desc & PTE_RDONLY)) {
- pr = pw = true;
- } else {
- pr = true;
- pw = false;
- }
+ /* XN maps to UXN */
+ s1p->px &= !wr->UXNTable;
+ }
+}
- if (wr.APTable & BIT(1))
- pw = false;
+static void compute_s1_permissions(struct kvm_vcpu *vcpu, u32 op,
+ struct s1_walk_info *wi,
+ struct s1_walk_result *wr,
+ struct s1_perms *s1p)
+{
+ compute_s1_direct_permissions(vcpu, wi, wr, s1p);
+ compute_s1_hierarchical_permissions(vcpu, wi, wr, s1p);
- /* XN maps to UXN */
- px = !((wr.desc & PTE_UXN) || wr.UXNTable);
+ if (op == OP_AT_S1E1RP || op == OP_AT_S1E1WP) {
+ bool pan;
+
+ pan = *vcpu_cpsr(vcpu) & PSR_PAN_BIT;
+ pan &= s1p->ur || s1p->uw || (pan3_enabled(vcpu, wi->regime) && s1p->ux);
+ s1p->pw &= !pan;
+ s1p->pr &= !pan;
}
+}
+
+static u64 handle_at_slow(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
+{
+ struct s1_walk_result wr = {};
+ struct s1_walk_info wi = {};
+ struct s1_perms s1p = {};
+ bool perm_fail = false;
+ int ret, idx;
+
+ ret = setup_s1_walk(vcpu, op, &wi, &wr, vaddr);
+ if (ret)
+ goto compute_par;
+
+ if (wr.level == S1_MMU_DISABLED)
+ goto compute_par;
+
+ idx = srcu_read_lock(&vcpu->kvm->srcu);
+
+ ret = walk_s1(vcpu, &wi, &wr, vaddr);
+
+ srcu_read_unlock(&vcpu->kvm->srcu, idx);
+
+ if (ret)
+ goto compute_par;
- perm_fail = false;
+ compute_s1_permissions(vcpu, op, &wi, &wr, &s1p);
switch (op) {
case OP_AT_S1E1RP:
case OP_AT_S1E1R:
case OP_AT_S1E2R:
- perm_fail = !pr;
+ perm_fail = !s1p.pr;
break;
case OP_AT_S1E1WP:
case OP_AT_S1E1W:
case OP_AT_S1E2W:
- perm_fail = !pw;
+ perm_fail = !s1p.pw;
break;
case OP_AT_S1E0R:
- perm_fail = !ur;
+ perm_fail = !s1p.ur;
break;
case OP_AT_S1E0W:
- perm_fail = !uw;
+ perm_fail = !s1p.uw;
break;
case OP_AT_S1E1A:
case OP_AT_S1E2A:
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 12/16] KVM: arm64: Implement AT S1PIE support
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
` (10 preceding siblings ...)
2024-09-03 15:38 ` [PATCH v2 11/16] KVM: arm64: Split S1 permission evaluation into direct and hierarchical parts Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-05 13:58 ` Joey Gouly
2024-09-03 15:38 ` [PATCH v2 13/16] KVM: arm64: Define helper for EL2 registers with custom visibility Marc Zyngier
` (3 subsequent siblings)
15 siblings, 1 reply; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
It doesn't take much effort to imple,emt S1PIE support in AT.
This is only a matter of using the AArch64.S1IndirectBasePermissions()
encodings for the permission, ignoring GCS which has no impact on AT,
and enforce FEAT_PAN3 being enabled as this is a requirement of
FEAT_S1PIE.
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/at.c | 136 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 135 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c
index 68f5b89598ec..bd7e1b32b049 100644
--- a/arch/arm64/kvm/at.c
+++ b/arch/arm64/kvm/at.c
@@ -736,6 +736,23 @@ static u64 compute_par_s1(struct kvm_vcpu *vcpu, struct s1_walk_result *wr,
return par;
}
+static bool s1pie_enabled(struct kvm_vcpu *vcpu, enum trans_regime regime)
+{
+ if (!kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, S1PIE, IMP))
+ return false;
+
+ switch (regime) {
+ case TR_EL2:
+ case TR_EL20:
+ return __vcpu_sys_reg(vcpu, TCR2_EL2) & TCR2_EL2_PIE;
+ case TR_EL10:
+ return (__vcpu_sys_reg(vcpu, HCRX_EL2) & HCRX_EL2_TCR2En) &&
+ (__vcpu_sys_reg(vcpu, TCR2_EL1) & TCR2_EL1x_PIE);
+ default:
+ BUG();
+ }
+}
+
static bool pan3_enabled(struct kvm_vcpu *vcpu, enum trans_regime regime)
{
u64 sctlr;
@@ -743,6 +760,9 @@ static bool pan3_enabled(struct kvm_vcpu *vcpu, enum trans_regime regime)
if (!kvm_has_feat(vcpu->kvm, ID_AA64MMFR1_EL1, PAN, PAN3))
return false;
+ if (s1pie_enabled(vcpu, regime))
+ return true;
+
if (regime == TR_EL10)
sctlr = vcpu_read_sys_reg(vcpu, SCTLR_EL1);
else
@@ -826,12 +846,126 @@ static void compute_s1_hierarchical_permissions(struct kvm_vcpu *vcpu,
}
}
+#define pi_idx(v, r, i) ((__vcpu_sys_reg((v), (r)) >> ((i) * 4)) & 0xf)
+
+#define set_priv_perms(p, r, w, x) \
+ do { \
+ (p)->pr = (r); \
+ (p)->pw = (w); \
+ (p)->px = (x); \
+ } while (0)
+
+#define set_unpriv_perms(p, r, w, x) \
+ do { \
+ (p)->ur = (r); \
+ (p)->uw = (w); \
+ (p)->ux = (x); \
+ } while (0)
+
+/* Similar to AArch64.S1IndirectBasePermissions(), without GCS */
+#define set_perms(w, p, ip) \
+ do { \
+ switch ((ip)) { \
+ case 0b0000: \
+ set_ ## w ## _perms((p), false, false, false); \
+ break; \
+ case 0b0001: \
+ set_ ## w ## _perms((p), true , false, false); \
+ break; \
+ case 0b0010: \
+ set_ ## w ## _perms((p), false, false, true ); \
+ break; \
+ case 0b0011: \
+ set_ ## w ## _perms((p), true , false, true ); \
+ break; \
+ case 0b0100: \
+ set_ ## w ## _perms((p), false, false, false); \
+ break; \
+ case 0b0101: \
+ set_ ## w ## _perms((p), true , true , false); \
+ break; \
+ case 0b0110: \
+ set_ ## w ## _perms((p), true , true , true ); \
+ break; \
+ case 0b0111: \
+ set_ ## w ## _perms((p), true , true , true ); \
+ break; \
+ case 0b1000: \
+ set_ ## w ## _perms((p), true , false, false); \
+ break; \
+ case 0b1001: \
+ set_ ## w ## _perms((p), true , false, false); \
+ break; \
+ case 0b1010: \
+ set_ ## w ## _perms((p), true , false, true ); \
+ break; \
+ case 0b1011: \
+ set_ ## w ## _perms((p), false, false, false); \
+ break; \
+ case 0b1100: \
+ set_ ## w ## _perms((p), true , true , false); \
+ break; \
+ case 0b1101: \
+ set_ ## w ## _perms((p), false, false, false); \
+ break; \
+ case 0b1110: \
+ set_ ## w ## _perms((p), true , true , true ); \
+ break; \
+ case 0b1111: \
+ set_ ## w ## _perms((p), false, false, false); \
+ break; \
+ } \
+ } while (0)
+
+static void compute_s1_indirect_permissions(struct kvm_vcpu *vcpu,
+ struct s1_walk_info *wi,
+ struct s1_walk_result *wr,
+ struct s1_perms *s1p)
+{
+ u8 up, pp, idx;
+
+ idx = (FIELD_GET(GENMASK(54, 53), wr->desc) << 2 |
+ FIELD_GET(BIT(51), wr->desc) << 1 |
+ FIELD_GET(BIT(6), wr->desc));
+
+ switch (wi->regime) {
+ case TR_EL10:
+ pp = pi_idx(vcpu, PIR_EL1, idx);
+ up = pi_idx(vcpu, PIRE0_EL1, idx);
+ break;
+ case TR_EL20:
+ pp = pi_idx(vcpu, PIR_EL2, idx);
+ up = pi_idx(vcpu, PIRE0_EL2, idx);
+ break;
+ case TR_EL2:
+ pp = pi_idx(vcpu, PIR_EL2, idx);
+ up = 0;
+ break;
+ }
+
+ set_perms(priv, s1p, pp);
+
+ if (wi->regime != TR_EL2)
+ set_perms(unpriv, s1p, up);
+ else
+ set_unpriv_perms(s1p, false, false, false);
+
+ if (s1p->px && s1p->uw) {
+ set_priv_perms(s1p, false, false, false);
+ set_unpriv_perms(s1p, false, false, false);
+ }
+}
+
static void compute_s1_permissions(struct kvm_vcpu *vcpu, u32 op,
struct s1_walk_info *wi,
struct s1_walk_result *wr,
struct s1_perms *s1p)
{
- compute_s1_direct_permissions(vcpu, wi, wr, s1p);
+ if (!s1pie_enabled(vcpu, wi->regime))
+ compute_s1_direct_permissions(vcpu, wi, wr, s1p);
+ else
+ compute_s1_indirect_permissions(vcpu, wi, wr, s1p);
+
compute_s1_hierarchical_permissions(vcpu, wi, wr, s1p);
if (op == OP_AT_S1E1RP || op == OP_AT_S1E1WP) {
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v2 12/16] KVM: arm64: Implement AT S1PIE support
2024-09-03 15:38 ` [PATCH v2 12/16] KVM: arm64: Implement AT S1PIE support Marc Zyngier
@ 2024-09-05 13:58 ` Joey Gouly
2024-09-05 14:57 ` Marc Zyngier
0 siblings, 1 reply; 22+ messages in thread
From: Joey Gouly @ 2024-09-05 13:58 UTC (permalink / raw)
To: Marc Zyngier
Cc: kvmarm, linux-arm-kernel, kvm, James Morse, Suzuki K Poulose,
Oliver Upton, Zenghui Yu, Alexandru Elisei, Mark Brown
Hello Marc!
On Tue, Sep 03, 2024 at 04:38:30PM +0100, Marc Zyngier wrote:
> It doesn't take much effort to imple,emt S1PIE support in AT.
> This is only a matter of using the AArch64.S1IndirectBasePermissions()
> encodings for the permission, ignoring GCS which has no impact on AT,
> and enforce FEAT_PAN3 being enabled as this is a requirement of
> FEAT_S1PIE.
>
> Signed-off-by: Marc Zyngier <maz@kernel.org>
> ---
> arch/arm64/kvm/at.c | 136 +++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 135 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c
> index 68f5b89598ec..bd7e1b32b049 100644
> --- a/arch/arm64/kvm/at.c
> +++ b/arch/arm64/kvm/at.c
> @@ -736,6 +736,23 @@ static u64 compute_par_s1(struct kvm_vcpu *vcpu, struct s1_walk_result *wr,
> return par;
> }
>
> +static bool s1pie_enabled(struct kvm_vcpu *vcpu, enum trans_regime regime)
> +{
> + if (!kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, S1PIE, IMP))
> + return false;
> +
> + switch (regime) {
> + case TR_EL2:
> + case TR_EL20:
> + return __vcpu_sys_reg(vcpu, TCR2_EL2) & TCR2_EL2_PIE;
> + case TR_EL10:
> + return (__vcpu_sys_reg(vcpu, HCRX_EL2) & HCRX_EL2_TCR2En) &&
> + (__vcpu_sys_reg(vcpu, TCR2_EL1) & TCR2_EL1x_PIE);
> + default:
> + BUG();
> + }
> +}
> +
> static bool pan3_enabled(struct kvm_vcpu *vcpu, enum trans_regime regime)
> {
> u64 sctlr;
> @@ -743,6 +760,9 @@ static bool pan3_enabled(struct kvm_vcpu *vcpu, enum trans_regime regime)
> if (!kvm_has_feat(vcpu->kvm, ID_AA64MMFR1_EL1, PAN, PAN3))
> return false;
>
> + if (s1pie_enabled(vcpu, regime))
> + return true;
> +
> if (regime == TR_EL10)
> sctlr = vcpu_read_sys_reg(vcpu, SCTLR_EL1);
> else
> @@ -826,12 +846,126 @@ static void compute_s1_hierarchical_permissions(struct kvm_vcpu *vcpu,
> }
> }
>
> +#define pi_idx(v, r, i) ((__vcpu_sys_reg((v), (r)) >> ((i) * 4)) & 0xf)
> +
> +#define set_priv_perms(p, r, w, x) \
> + do { \
> + (p)->pr = (r); \
> + (p)->pw = (w); \
> + (p)->px = (x); \
> + } while (0)
> +
> +#define set_unpriv_perms(p, r, w, x) \
> + do { \
> + (p)->ur = (r); \
> + (p)->uw = (w); \
> + (p)->ux = (x); \
> + } while (0)
> +
> +/* Similar to AArch64.S1IndirectBasePermissions(), without GCS */
> +#define set_perms(w, p, ip) \
> + do { \
> + switch ((ip)) { \
> + case 0b0000: \
> + set_ ## w ## _perms((p), false, false, false); \
> + break; \
> + case 0b0001: \
> + set_ ## w ## _perms((p), true , false, false); \
> + break; \
> + case 0b0010: \
> + set_ ## w ## _perms((p), false, false, true ); \
> + break; \
> + case 0b0011: \
> + set_ ## w ## _perms((p), true , false, true ); \
> + break; \
> + case 0b0100: \
> + set_ ## w ## _perms((p), false, false, false); \
> + break; \
> + case 0b0101: \
> + set_ ## w ## _perms((p), true , true , false); \
> + break; \
> + case 0b0110: \
> + set_ ## w ## _perms((p), true , true , true ); \
> + break; \
> + case 0b0111: \
> + set_ ## w ## _perms((p), true , true , true ); \
> + break; \
> + case 0b1000: \
> + set_ ## w ## _perms((p), true , false, false); \
> + break; \
> + case 0b1001: \
> + set_ ## w ## _perms((p), true , false, false); \
> + break; \
> + case 0b1010: \
> + set_ ## w ## _perms((p), true , false, true ); \
> + break; \
> + case 0b1011: \
> + set_ ## w ## _perms((p), false, false, false); \
> + break; \
> + case 0b1100: \
> + set_ ## w ## _perms((p), true , true , false); \
> + break; \
> + case 0b1101: \
> + set_ ## w ## _perms((p), false, false, false); \
> + break; \
> + case 0b1110: \
> + set_ ## w ## _perms((p), true , true , true ); \
> + break; \
> + case 0b1111: \
> + set_ ## w ## _perms((p), false, false, false); \
> + break; \
> + } \
> + } while (0)
> +
> +static void compute_s1_indirect_permissions(struct kvm_vcpu *vcpu,
> + struct s1_walk_info *wi,
> + struct s1_walk_result *wr,
> + struct s1_perms *s1p)
> +{
> + u8 up, pp, idx;
> +
> + idx = (FIELD_GET(GENMASK(54, 53), wr->desc) << 2 |
> + FIELD_GET(BIT(51), wr->desc) << 1 |
> + FIELD_GET(BIT(6), wr->desc));
> +
> + switch (wi->regime) {
> + case TR_EL10:
> + pp = pi_idx(vcpu, PIR_EL1, idx);
> + up = pi_idx(vcpu, PIRE0_EL1, idx);
> + break;
> + case TR_EL20:
> + pp = pi_idx(vcpu, PIR_EL2, idx);
> + up = pi_idx(vcpu, PIRE0_EL2, idx);
> + break;
> + case TR_EL2:
> + pp = pi_idx(vcpu, PIR_EL2, idx);
> + up = 0;
> + break;
> + }
> +
> + set_perms(priv, s1p, pp);
> +
> + if (wi->regime != TR_EL2)
> + set_perms(unpriv, s1p, up);
> + else
> + set_unpriv_perms(s1p, false, false, false);
> +
> + if (s1p->px && s1p->uw) {
> + set_priv_perms(s1p, false, false, false);
> + set_unpriv_perms(s1p, false, false, false);
> + }
> +}
> +
> static void compute_s1_permissions(struct kvm_vcpu *vcpu, u32 op,
> struct s1_walk_info *wi,
> struct s1_walk_result *wr,
> struct s1_perms *s1p)
> {
> - compute_s1_direct_permissions(vcpu, wi, wr, s1p);
> + if (!s1pie_enabled(vcpu, wi->regime))
> + compute_s1_direct_permissions(vcpu, wi, wr, s1p);
> + else
> + compute_s1_indirect_permissions(vcpu, wi, wr, s1p);
> +
> compute_s1_hierarchical_permissions(vcpu, wi, wr, s1p);
Is this (and the previous patch to split this up) right?
Looking at this from the ARM ARM (ARM DDI 0487K.a):
R JHSVW If Indirect permissions are used, then hierarchical permissions are disabled and TCR_ELx.HPDn are RES 1.
>
> if (op == OP_AT_S1E1RP || op == OP_AT_S1E1WP) {
> --
Thanks,
Joey
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 12/16] KVM: arm64: Implement AT S1PIE support
2024-09-05 13:58 ` Joey Gouly
@ 2024-09-05 14:57 ` Marc Zyngier
2024-09-05 15:37 ` Joey Gouly
0 siblings, 1 reply; 22+ messages in thread
From: Marc Zyngier @ 2024-09-05 14:57 UTC (permalink / raw)
To: Joey Gouly
Cc: kvmarm, linux-arm-kernel, kvm, James Morse, Suzuki K Poulose,
Oliver Upton, Zenghui Yu, Alexandru Elisei, Mark Brown
Hi Joey,
Thanks for having a look.
On Thu, 05 Sep 2024 14:58:20 +0100,
Joey Gouly <joey.gouly@arm.com> wrote:
>
> Hello Marc!
>
> > static void compute_s1_permissions(struct kvm_vcpu *vcpu, u32 op,
> > struct s1_walk_info *wi,
> > struct s1_walk_result *wr,
> > struct s1_perms *s1p)
> > {
> > - compute_s1_direct_permissions(vcpu, wi, wr, s1p);
> > + if (!s1pie_enabled(vcpu, wi->regime))
> > + compute_s1_direct_permissions(vcpu, wi, wr, s1p);
> > + else
> > + compute_s1_indirect_permissions(vcpu, wi, wr, s1p);
> > +
> > compute_s1_hierarchical_permissions(vcpu, wi, wr, s1p);
>
> Is this (and the previous patch to split this up) right?
>
> Looking at this from the ARM ARM (ARM DDI 0487K.a):
>
> R JHSVW If Indirect permissions are used, then hierarchical
> permissions are disabled and TCR_ELx.HPDn are RES 1.
Odd. I was convinced that it was when S1POE is enabled that HPs were
disabled. But you are absolutely right, and it is once more proven
that I can't read. Oh well.
Not to worry, I've since found other issues with this series. I have
forgotten the patch dealing with the fast path on another branch, and
since decided that TCR2_EL2 needed extra care to cope with individual
features being disabled.
The rework is still useful, as I'm looking at POE as well, but I need
to hoist the HP stuff up a notch.
I'll repost things once I've sorted these things up.
Thanks again,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 12/16] KVM: arm64: Implement AT S1PIE support
2024-09-05 14:57 ` Marc Zyngier
@ 2024-09-05 15:37 ` Joey Gouly
2024-09-06 7:04 ` Marc Zyngier
0 siblings, 1 reply; 22+ messages in thread
From: Joey Gouly @ 2024-09-05 15:37 UTC (permalink / raw)
To: Marc Zyngier
Cc: kvmarm, linux-arm-kernel, kvm, James Morse, Suzuki K Poulose,
Oliver Upton, Zenghui Yu, Alexandru Elisei, Mark Brown
On Thu, Sep 05, 2024 at 03:57:13PM +0100, Marc Zyngier wrote:
> Hi Joey,
>
> Thanks for having a look.
>
> On Thu, 05 Sep 2024 14:58:20 +0100,
> Joey Gouly <joey.gouly@arm.com> wrote:
> >
> > Hello Marc!
> >
> > > static void compute_s1_permissions(struct kvm_vcpu *vcpu, u32 op,
> > > struct s1_walk_info *wi,
> > > struct s1_walk_result *wr,
> > > struct s1_perms *s1p)
> > > {
> > > - compute_s1_direct_permissions(vcpu, wi, wr, s1p);
> > > + if (!s1pie_enabled(vcpu, wi->regime))
> > > + compute_s1_direct_permissions(vcpu, wi, wr, s1p);
> > > + else
> > > + compute_s1_indirect_permissions(vcpu, wi, wr, s1p);
> > > +
> > > compute_s1_hierarchical_permissions(vcpu, wi, wr, s1p);
> >
> > Is this (and the previous patch to split this up) right?
> >
> > Looking at this from the ARM ARM (ARM DDI 0487K.a):
> >
> > R JHSVW If Indirect permissions are used, then hierarchical
> > permissions are disabled and TCR_ELx.HPDn are RES 1.
>
> Odd. I was convinced that it was when S1POE is enabled that HPs were
> disabled. But you are absolutely right, and it is once more proven
> that I can't read. Oh well.
For POE there is:
RBVXDG Hierarchical Permissions are disabled and the TCR_ELx.{HPD0, HPD1} bits are RES1 for stage 1 of a translation
regime using VMSAv8-64 if one or more of POE and E0POE (for EL1&0, EL2&0) is enabled for that translation
regime.
>
> Not to worry, I've since found other issues with this series. I have
> forgotten the patch dealing with the fast path on another branch, and
> since decided that TCR2_EL2 needed extra care to cope with individual
> features being disabled.
>
> The rework is still useful, as I'm looking at POE as well, but I need
> to hoist the HP stuff up a notch.
>
> I'll repost things once I've sorted these things up.
I think the rest of this patch looked fine though.
>
> Thanks again,
>
> M.
>
> --
> Without deviation from the norm, progress is not possible.
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 12/16] KVM: arm64: Implement AT S1PIE support
2024-09-05 15:37 ` Joey Gouly
@ 2024-09-06 7:04 ` Marc Zyngier
0 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-06 7:04 UTC (permalink / raw)
To: Joey Gouly
Cc: kvmarm, linux-arm-kernel, kvm, James Morse, Suzuki K Poulose,
Oliver Upton, Zenghui Yu, Alexandru Elisei, Mark Brown
On Thu, 05 Sep 2024 16:37:34 +0100,
Joey Gouly <joey.gouly@arm.com> wrote:
>
> On Thu, Sep 05, 2024 at 03:57:13PM +0100, Marc Zyngier wrote:
> > Hi Joey,
> >
> > Thanks for having a look.
> >
> > On Thu, 05 Sep 2024 14:58:20 +0100,
> > Joey Gouly <joey.gouly@arm.com> wrote:
> > >
> > > Hello Marc!
> > >
> > > > static void compute_s1_permissions(struct kvm_vcpu *vcpu, u32 op,
> > > > struct s1_walk_info *wi,
> > > > struct s1_walk_result *wr,
> > > > struct s1_perms *s1p)
> > > > {
> > > > - compute_s1_direct_permissions(vcpu, wi, wr, s1p);
> > > > + if (!s1pie_enabled(vcpu, wi->regime))
> > > > + compute_s1_direct_permissions(vcpu, wi, wr, s1p);
> > > > + else
> > > > + compute_s1_indirect_permissions(vcpu, wi, wr, s1p);
> > > > +
> > > > compute_s1_hierarchical_permissions(vcpu, wi, wr, s1p);
> > >
> > > Is this (and the previous patch to split this up) right?
> > >
> > > Looking at this from the ARM ARM (ARM DDI 0487K.a):
> > >
> > > R JHSVW If Indirect permissions are used, then hierarchical
> > > permissions are disabled and TCR_ELx.HPDn are RES 1.
> >
> > Odd. I was convinced that it was when S1POE is enabled that HPs were
> > disabled. But you are absolutely right, and it is once more proven
> > that I can't read. Oh well.
>
> For POE there is:
>
> RBVXDG Hierarchical Permissions are disabled and the
> TCR_ELx.{HPD0, HPD1} bits are RES1 for stage 1 of a
> translation regime using VMSAv8-64 if one or more of POE and
> E0POE (for EL1&0, EL2&0) is enabled for that translation
> regime.
Right, this is the one I had at the back of my head.
At the end of the day, it really looks like hierarchical permissions
are as dead as the proverbial dodo, and their sole purpose left is to
antagonise implementations. Hopefully someone realises that and
eventually allows implementations to build TCR_ELx.HPD* as RES1.
> > Not to worry, I've since found other issues with this series. I have
> > forgotten the patch dealing with the fast path on another branch, and
> > since decided that TCR2_EL2 needed extra care to cope with individual
> > features being disabled.
> >
> > The rework is still useful, as I'm looking at POE as well, but I need
> > to hoist the HP stuff up a notch.
> >
> > I'll repost things once I've sorted these things up.
>
> I think the rest of this patch looked fine though.
Cool, thanks for having given it a look!
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v2 13/16] KVM: arm64: Define helper for EL2 registers with custom visibility
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
` (11 preceding siblings ...)
2024-09-03 15:38 ` [PATCH v2 12/16] KVM: arm64: Implement AT S1PIE support Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 14/16] KVM: arm64: Hide TCR2_EL1 from userspace when disabled for guests Marc Zyngier
` (2 subsequent siblings)
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
From: Mark Brown <broonie@kernel.org>
In preparation for adding more visibility filtering for EL2 registers add
a helper macro like EL2_REG() which allows specification of a custom
visibility operation.
Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20240822-kvm-arm64-hide-pie-regs-v2-1-376624fa829c@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/sys_regs.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 7f4f69351e89..4ee74e2d4f08 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -2136,6 +2136,15 @@ static bool bad_redir_trap(struct kvm_vcpu *vcpu,
.val = v, \
}
+#define EL2_REG_FILTERED(name, acc, rst, v, filter) { \
+ SYS_DESC(SYS_##name), \
+ .access = acc, \
+ .reset = rst, \
+ .reg = name, \
+ .visibility = filter, \
+ .val = v, \
+}
+
#define EL2_REG_VNCR(name, rst, v) EL2_REG(name, bad_vncr_trap, rst, v)
#define EL2_REG_REDIR(name, rst, v) EL2_REG(name, bad_redir_trap, rst, v)
@@ -2803,8 +2812,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
EL2_REG_VNCR(HFGITR_EL2, reset_val, 0),
EL2_REG_VNCR(HACR_EL2, reset_val, 0),
- { SYS_DESC(SYS_ZCR_EL2), .access = access_zcr_el2, .reset = reset_val,
- .visibility = sve_el2_visibility, .reg = ZCR_EL2 },
+ EL2_REG_FILTERED(ZCR_EL2, access_zcr_el2, reset_val, 0,
+ sve_el2_visibility),
EL2_REG_VNCR(HCRX_EL2, reset_val, 0),
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 14/16] KVM: arm64: Hide TCR2_EL1 from userspace when disabled for guests
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
` (12 preceding siblings ...)
2024-09-03 15:38 ` [PATCH v2 13/16] KVM: arm64: Define helper for EL2 registers with custom visibility Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 15/16] KVM: arm64: Hide S1PIE registers " Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 16/16] KVM: arm64: Rely on visibility to let PIR*_ELx/TCR2_ELx UNDEF Marc Zyngier
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
From: Mark Brown <broonie@kernel.org>
When the guest does not support FEAT_TCR2 we should not allow any access
to it in order to ensure that we do not create spurious issues with guest
migration. Add a visibility operation for it.
Fixes: fbff56068232 ("KVM: arm64: Save/restore TCR2_EL1")
Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20240822-kvm-arm64-hide-pie-regs-v2-2-376624fa829c@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/include/asm/kvm_host.h | 3 +++
arch/arm64/kvm/sys_regs.c | 29 ++++++++++++++++++++++++++---
2 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index ab4c675b491d..7889e5f4009f 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -1476,4 +1476,7 @@ void kvm_set_vm_id_reg(struct kvm *kvm, u32 reg, u64 val);
(pa + pi + pa3) == 1; \
})
+#define kvm_has_tcr2(k) \
+ (kvm_has_feat((k), ID_AA64MMFR3_EL1, TCRX, IMP))
+
#endif /* __ARM64_KVM_HOST_H__ */
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 4ee74e2d4f08..64562b0c2359 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -2319,6 +2319,27 @@ static bool access_zcr_el2(struct kvm_vcpu *vcpu,
return true;
}
+static unsigned int tcr2_visibility(const struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
+{
+ if (kvm_has_tcr2(vcpu->kvm))
+ return 0;
+
+ return REG_HIDDEN;
+}
+
+static unsigned int tcr2_el2_visibility(const struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
+{
+ unsigned int r;
+
+ r = el2_visibility(vcpu, rd);
+ if (r)
+ return r;
+
+ return tcr2_visibility(vcpu, rd);
+}
+
/*
* Architected system registers.
* Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
@@ -2503,7 +2524,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
{ SYS_DESC(SYS_TTBR0_EL1), access_vm_reg, reset_unknown, TTBR0_EL1 },
{ SYS_DESC(SYS_TTBR1_EL1), access_vm_reg, reset_unknown, TTBR1_EL1 },
{ SYS_DESC(SYS_TCR_EL1), access_vm_reg, reset_val, TCR_EL1, 0 },
- { SYS_DESC(SYS_TCR2_EL1), access_vm_reg, reset_val, TCR2_EL1, 0 },
+ { SYS_DESC(SYS_TCR2_EL1), access_vm_reg, reset_val, TCR2_EL1, 0,
+ .visibility = tcr2_visibility },
PTRAUTH_KEY(APIA),
PTRAUTH_KEY(APIB),
@@ -2820,7 +2842,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
EL2_REG(TTBR0_EL2, access_rw, reset_val, 0),
EL2_REG(TTBR1_EL2, access_rw, reset_val, 0),
EL2_REG(TCR_EL2, access_rw, reset_val, TCR_EL2_RES1),
- EL2_REG(TCR2_EL2, access_tcr2_el2, reset_val, TCR2_EL2_RES1),
+ EL2_REG_FILTERED(TCR2_EL2, access_tcr2_el2, reset_val, TCR2_EL2_RES1,
+ tcr2_el2_visibility),
EL2_REG_VNCR(VTTBR_EL2, reset_val, 0),
EL2_REG_VNCR(VTCR_EL2, reset_val, 0),
@@ -4679,7 +4702,7 @@ void kvm_calculate_traps(struct kvm_vcpu *vcpu)
if (kvm_has_feat(kvm, ID_AA64ISAR2_EL1, MOPS, IMP))
vcpu->arch.hcrx_el2 |= (HCRX_EL2_MSCEn | HCRX_EL2_MCE2);
- if (kvm_has_feat(kvm, ID_AA64MMFR3_EL1, TCRX, IMP))
+ if (kvm_has_tcr2(kvm))
vcpu->arch.hcrx_el2 |= HCRX_EL2_TCR2En;
}
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 15/16] KVM: arm64: Hide S1PIE registers from userspace when disabled for guests
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
` (13 preceding siblings ...)
2024-09-03 15:38 ` [PATCH v2 14/16] KVM: arm64: Hide TCR2_EL1 from userspace when disabled for guests Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:38 ` [PATCH v2 16/16] KVM: arm64: Rely on visibility to let PIR*_ELx/TCR2_ELx UNDEF Marc Zyngier
15 siblings, 0 replies; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
From: Mark Brown <broonie@kernel.org>
When the guest does not support S1PIE we should not allow any access
to the system registers it adds in order to ensure that we do not create
spurious issues with guest migration. Add a visibility operation for these
registers.
Fixes: 86f9de9db178 ("KVM: arm64: Save/restore PIE registers")
Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20240822-kvm-arm64-hide-pie-regs-v2-3-376624fa829c@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/include/asm/kvm_host.h | 3 +++
arch/arm64/kvm/sys_regs.c | 35 ++++++++++++++++++++++++++-----
2 files changed, 33 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 7889e5f4009f..fd161d41df52 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -1479,4 +1479,7 @@ void kvm_set_vm_id_reg(struct kvm *kvm, u32 reg, u64 val);
#define kvm_has_tcr2(k) \
(kvm_has_feat((k), ID_AA64MMFR3_EL1, TCRX, IMP))
+#define kvm_has_s1pie(k) \
+ (kvm_has_feat((k), ID_AA64MMFR3_EL1, S1PIE, IMP))
+
#endif /* __ARM64_KVM_HOST_H__ */
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 64562b0c2359..bcc7c4c6620f 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -2340,6 +2340,27 @@ static unsigned int tcr2_el2_visibility(const struct kvm_vcpu *vcpu,
return tcr2_visibility(vcpu, rd);
}
+static unsigned int s1pie_visibility(const struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
+{
+ if (kvm_has_s1pie(vcpu->kvm))
+ return 0;
+
+ return REG_HIDDEN;
+}
+
+static unsigned int s1pie_el2_visibility(const struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
+{
+ unsigned int r;
+
+ r = el2_visibility(vcpu, rd);
+ if (r)
+ return r;
+
+ return s1pie_visibility(vcpu, rd);
+}
+
/*
* Architected system registers.
* Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
@@ -2577,8 +2598,10 @@ static const struct sys_reg_desc sys_reg_descs[] = {
{ SYS_DESC(SYS_PMMIR_EL1), trap_raz_wi },
{ SYS_DESC(SYS_MAIR_EL1), access_vm_reg, reset_unknown, MAIR_EL1 },
- { SYS_DESC(SYS_PIRE0_EL1), NULL, reset_unknown, PIRE0_EL1 },
- { SYS_DESC(SYS_PIR_EL1), NULL, reset_unknown, PIR_EL1 },
+ { SYS_DESC(SYS_PIRE0_EL1), NULL, reset_unknown, PIRE0_EL1,
+ .visibility = s1pie_visibility },
+ { SYS_DESC(SYS_PIR_EL1), NULL, reset_unknown, PIR_EL1,
+ .visibility = s1pie_visibility },
{ SYS_DESC(SYS_AMAIR_EL1), access_vm_reg, reset_amair_el1, AMAIR_EL1 },
{ SYS_DESC(SYS_LORSA_EL1), trap_loregion },
@@ -2875,8 +2898,10 @@ static const struct sys_reg_desc sys_reg_descs[] = {
EL2_REG(HPFAR_EL2, access_rw, reset_val, 0),
EL2_REG(MAIR_EL2, access_rw, reset_val, 0),
- EL2_REG(PIRE0_EL2, check_s1pie_access_rw, reset_val, 0),
- EL2_REG(PIR_EL2, check_s1pie_access_rw, reset_val, 0),
+ EL2_REG_FILTERED(PIRE0_EL2, check_s1pie_access_rw, reset_val, 0,
+ s1pie_el2_visibility),
+ EL2_REG_FILTERED(PIR_EL2, check_s1pie_access_rw, reset_val, 0,
+ s1pie_el2_visibility),
EL2_REG(AMAIR_EL2, access_rw, reset_val, 0),
EL2_REG(VBAR_EL2, access_rw, reset_val, 0),
@@ -4751,7 +4776,7 @@ void kvm_calculate_traps(struct kvm_vcpu *vcpu)
kvm->arch.fgu[HFGITR_GROUP] |= (HFGITR_EL2_ATS1E1RP |
HFGITR_EL2_ATS1E1WP);
- if (!kvm_has_feat(kvm, ID_AA64MMFR3_EL1, S1PIE, IMP))
+ if (!kvm_has_s1pie(kvm))
kvm->arch.fgu[HFGxTR_GROUP] |= (HFGxTR_EL2_nPIRE0_EL1 |
HFGxTR_EL2_nPIR_EL1);
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v2 16/16] KVM: arm64: Rely on visibility to let PIR*_ELx/TCR2_ELx UNDEF
2024-09-03 15:38 [PATCH v2 00/16] KVM: arm64: Add EL2 support to FEAT_S1PIE Marc Zyngier
` (14 preceding siblings ...)
2024-09-03 15:38 ` [PATCH v2 15/16] KVM: arm64: Hide S1PIE registers " Marc Zyngier
@ 2024-09-03 15:38 ` Marc Zyngier
2024-09-03 15:58 ` Mark Brown
15 siblings, 1 reply; 22+ messages in thread
From: Marc Zyngier @ 2024-09-03 15:38 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel, kvm
Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Joey Gouly, Alexandru Elisei, Mark Brown
With a visibility defined for these registers, there is no need
to check again for S1PIE or TCRX being implemented as perform_access()
already handles it.
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/sys_regs.c | 36 +++---------------------------------
1 file changed, 3 insertions(+), 33 deletions(-)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index bcc7c4c6620f..2783c1d2a13a 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -346,18 +346,6 @@ static bool access_rw(struct kvm_vcpu *vcpu,
return true;
}
-static bool check_s1pie_access_rw(struct kvm_vcpu *vcpu,
- struct sys_reg_params *p,
- const struct sys_reg_desc *r)
-{
- if (!kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, S1PIE, IMP)) {
- kvm_inject_undefined(vcpu);
- return false;
- }
-
- return access_rw(vcpu, p, r);
-}
-
/*
* See note at ARMv7 ARM B1.14.4 (TL;DR: S/W ops are not easily virtualized).
*/
@@ -424,12 +412,6 @@ static bool access_vm_reg(struct kvm_vcpu *vcpu,
bool was_enabled = vcpu_has_cache_enabled(vcpu);
u64 val, mask, shift;
- if (reg_to_encoding(r) == SYS_TCR2_EL1 &&
- !kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, TCRX, IMP)) {
- kvm_inject_undefined(vcpu);
- return false;
- }
-
BUG_ON(!p->is_write);
get_access_mask(r, &mask, &shift);
@@ -448,18 +430,6 @@ static bool access_vm_reg(struct kvm_vcpu *vcpu,
return true;
}
-static bool access_tcr2_el2(struct kvm_vcpu *vcpu,
- struct sys_reg_params *p,
- const struct sys_reg_desc *r)
-{
- if (!kvm_has_feat(vcpu->kvm, ID_AA64MMFR3_EL1, TCRX, IMP)) {
- kvm_inject_undefined(vcpu);
- return false;
- }
-
- return access_rw(vcpu, p, r);
-}
-
static bool access_actlr(struct kvm_vcpu *vcpu,
struct sys_reg_params *p,
const struct sys_reg_desc *r)
@@ -2865,7 +2835,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
EL2_REG(TTBR0_EL2, access_rw, reset_val, 0),
EL2_REG(TTBR1_EL2, access_rw, reset_val, 0),
EL2_REG(TCR_EL2, access_rw, reset_val, TCR_EL2_RES1),
- EL2_REG_FILTERED(TCR2_EL2, access_tcr2_el2, reset_val, TCR2_EL2_RES1,
+ EL2_REG_FILTERED(TCR2_EL2, access_rw, reset_val, TCR2_EL2_RES1,
tcr2_el2_visibility),
EL2_REG_VNCR(VTTBR_EL2, reset_val, 0),
EL2_REG_VNCR(VTCR_EL2, reset_val, 0),
@@ -2898,9 +2868,9 @@ static const struct sys_reg_desc sys_reg_descs[] = {
EL2_REG(HPFAR_EL2, access_rw, reset_val, 0),
EL2_REG(MAIR_EL2, access_rw, reset_val, 0),
- EL2_REG_FILTERED(PIRE0_EL2, check_s1pie_access_rw, reset_val, 0,
+ EL2_REG_FILTERED(PIRE0_EL2, access_rw, reset_val, 0,
s1pie_el2_visibility),
- EL2_REG_FILTERED(PIR_EL2, check_s1pie_access_rw, reset_val, 0,
+ EL2_REG_FILTERED(PIR_EL2, access_rw, reset_val, 0,
s1pie_el2_visibility),
EL2_REG(AMAIR_EL2, access_rw, reset_val, 0),
--
2.39.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v2 16/16] KVM: arm64: Rely on visibility to let PIR*_ELx/TCR2_ELx UNDEF
2024-09-03 15:38 ` [PATCH v2 16/16] KVM: arm64: Rely on visibility to let PIR*_ELx/TCR2_ELx UNDEF Marc Zyngier
@ 2024-09-03 15:58 ` Mark Brown
0 siblings, 0 replies; 22+ messages in thread
From: Mark Brown @ 2024-09-03 15:58 UTC (permalink / raw)
To: Marc Zyngier
Cc: kvmarm, linux-arm-kernel, kvm, James Morse, Suzuki K Poulose,
Oliver Upton, Zenghui Yu, Joey Gouly, Alexandru Elisei
[-- Attachment #1: Type: text/plain, Size: 300 bytes --]
On Tue, Sep 03, 2024 at 04:38:34PM +0100, Marc Zyngier wrote:
> With a visibility defined for these registers, there is no need
> to check again for S1PIE or TCRX being implemented as perform_access()
> already handles it.
Ah, thanks for updating this:
Reviewed-by: Mark Brown <broonie@kernel.org>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 22+ messages in thread