KVM: arm64: Prefix patches for NV support
Subsystems (4)
Marc Zyngier
maintainercover KVM: arm64: Prefix patches for NV support
As a bunch of the NV patches have had a decent amount of review, and given that they do very little on their own, I've put together a prefix series that gets the most mundane stuff out of the way.
Of course, nothing is functional, but nothing gets used either. In a way, this is pretty similar to the current state of pKVM! ;-)
Thanks,
M.
Christoffer Dall (6):
KVM: arm64: nv: Introduce nested virtualization VCPU feature
KVM: arm64: nv: Reset VCPU to EL2 registers if VCPU nested virt is set
KVM: arm64: nv: Allow userspace to set PSR_MODE_EL2x
KVM: arm64: nv: Add nested virt VCPU primitives for vEL2 VCPU state
KVM: arm64: nv: Handle trapped ERET from virtual EL2
KVM: arm64: nv: Only toggle cache for virtual EL2 when SCTLR_EL2
changes
Jintack Lim (7):
arm64: Add ARM64_HAS_NESTED_VIRT cpufeature
KVM: arm64: nv: Handle HCR_EL2.NV system register traps
KVM: arm64: nv: Support virtual EL2 exceptions
KVM: arm64: nv: Inject HVC exceptions to the virtual EL2
KVM: arm64: nv: Handle PSCI call via smc from the guest
KVM: arm64: nv: Add accessors for SPSR_EL1, ELR_EL1 and VBAR_EL1 from
virtual EL2
KVM: arm64: nv: Emulate EL12 register accesses from the virtual EL2
Marc Zyngier (5):
KVM: arm64: Use the S2 MMU context to iterate over S2 table
KVM: arm64: nv: Add EL2 system registers to vcpu context
KVM: arm64: nv: Emulate PSTATE.M for a guest hypervisor
KVM: arm64: nv: Allow a sysreg to be hidden from userspace only
KVM: arm64: nv: Filter out unsupported features from ID regs
.../admin-guide/kernel-parameters.txt | 7 +- arch/arm64/include/asm/esr.h | 4 + arch/arm64/include/asm/kvm_arm.h | 19 +- arch/arm64/include/asm/kvm_emulate.h | 66 ++++++ arch/arm64/include/asm/kvm_host.h | 42 +++- arch/arm64/include/asm/kvm_mmu.h | 11 +- arch/arm64/include/asm/kvm_nested.h | 20 ++ arch/arm64/include/asm/sysreg.h | 38 +++- arch/arm64/include/uapi/asm/kvm.h | 1 + arch/arm64/kernel/cpufeature.c | 25 +++ arch/arm64/kvm/Makefile | 2 +- arch/arm64/kvm/arm.c | 5 + arch/arm64/kvm/emulate-nested.c | 203 ++++++++++++++++++ arch/arm64/kvm/guest.c | 6 + arch/arm64/kvm/handle_exit.c | 45 +++- arch/arm64/kvm/hyp/exception.c | 48 +++-- arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h | 19 +- arch/arm64/kvm/hyp/vhe/switch.c | 24 +++ arch/arm64/kvm/inject_fault.c | 61 +++++- arch/arm64/kvm/mmu.c | 16 +- arch/arm64/kvm/nested.c | 162 ++++++++++++++ arch/arm64/kvm/reset.c | 16 ++ arch/arm64/kvm/sys_regs.c | 174 ++++++++++++++- arch/arm64/kvm/sys_regs.h | 14 +- arch/arm64/kvm/trace_arm.h | 59 +++++ arch/arm64/tools/cpucaps | 1 + 26 files changed, 1041 insertions(+), 47 deletions(-) create mode 100644 arch/arm64/include/asm/kvm_nested.h create mode 100644 arch/arm64/kvm/emulate-nested.c create mode 100644 arch/arm64/kvm/nested.c
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 1/18 arm64: Add ARM64_HAS_NESTED_VIRT cpufeature
From: Jintack Lim <[email protected]>
Add a new ARM64_HAS_NESTED_VIRT feature to indicate that the CPU has the ARMv8.3 nested virtualization capability, together with the 'kvm-arm.mode=nested' command line option.
This will be used to support nested virtualization in KVM.
--- .../admin-guide/kernel-parameters.txt | 7 +++++- arch/arm64/include/asm/kvm_host.h | 5 ++++ arch/arm64/kernel/cpufeature.c | 25 +++++++++++++++++++ arch/arm64/kvm/arm.c | 5 ++++ arch/arm64/tools/cpucaps | 1 + 5 files changed, 42 insertions(+), 1 deletion(-)
Documentation/admin-guide/kernel-parameters.txt +6 -1
@@ -2553,9 +2553,14 @@ protected: nVHE-based mode with support for guests whose state is kept private from the host. + nested: VHE-based mode with support for nested + virtualization. Requires at least ARMv8.3 + hardware. + Defaults to VHE/nVHE based on hardware support. Setting mode to "protected" will disable kexec and hibernation - for the host. + for the host. "nested" is experimental and should be + used with extreme caution. kvm-arm.vgic_v3_group0_trap= [KVM,ARM] Trap guest accesses to GICv3 group-0
arch/arm64/include/asm/kvm_host.h +5 -0
@@ -60,9 +60,14 @@enum kvm_mode {KVM_MODE_DEFAULT ,KVM_MODE_PROTECTED , +KVM_MODE_NV ,KVM_MODE_NONE , }; +#ifdef CONFIG_KVM enum kvm_mode kvm_get_mode (void ); +#else +static inline enum kvm_mode kvm_get_mode (void ) {return KVM_MODE_NONE ; }; +#endif DECLARE_STATIC_KEY_FALSE (userspace_irqchip_in_use);
arch/arm64/kernel/cpufeature.c +25 -0
@@ -1956,6 +1956,20 @@ static void cpu_copy_el2regs(const struct arm64_cpu_capabilities *__unused)write_sysreg (read_sysreg (tpidr_el1), tpidr_el2); } +static bool has_nested_virt_support (const struct arm64_cpu_capabilities *cap, +int scope) +{ +if (kvm_get_mode () !=KVM_MODE_NV ) +return false; + +if (!has_cpuid_feature (cap, scope)) { +pr_warn ("unavailable: %s\n" , cap->desc); +return false; + } + +return true; +} +#ifdef CONFIG_ARM64_PAN static void cpu_enable_pan (const struct arm64_cpu_capabilities *__unused) {
@@ -2215,6 +2229,17 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = runs_at_el2, .cpu_enable = cpu_copy_el2regs, }, + { + .desc = "Nested Virtualization Support", + .capability = ARM64_HAS_NESTED_VIRT, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_nested_virt_support, + .sys_reg = SYS_ID_AA64MMFR2_EL1, + .sign = FTR_UNSIGNED, + .field_pos = ID_AA64MMFR2_EL1_NV_SHIFT, + .field_width = 4, + .min_field_value = ID_AA64MMFR2_EL1_NV_IMP, + }, { .capability = ARM64_HAS_32BIT_EL0_DO_NOT_USE, .type = ARM64_CPUCAP_SYSTEM_FEATURE,
arch/arm64/kvm/arm.c +5 -0
@@ -2310,6 +2310,11 @@ static int __init early_kvm_mode_cfg(char *arg)return 0 ; } +if (strcmp (arg,"nested" ) ==0 && !WARN_ON (!is_kernel_in_hyp_mode ())) { + kvm_mode =KVM_MODE_NV ; +return 0 ; + } +return -EINVAL ; }early_param ("kvm-arm.mode" , early_kvm_mode_cfg);
arch/arm64/tools/cpucaps +1 -0
@@ -31,6 +31,7 @@ HAS_GENERIC_AUTH_IMP_DEF HAS_IRQ_PRIO_MASKING HAS_LDAPR HAS_LSE_ATOMICS +HAS_NESTED_VIRT HAS_NO_FPSIMD HAS_NO_HW_PREFETCH HAS_PAN
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 2/18 KVM: arm64: Use the S2 MMU context to iterate over S2 table
In reply to Marc ZyngierMost of our S2 helpers take a kvm_s2_mmu pointer, but quickly revert back to using the kvm structure. By doing so, we lose track of which S2 MMU context we were initially using, and fallback to the "canonical" context.
If we were trying to unmap a S2 context managed by a guest hypervisor, we end-up parsing the wrong set of page tables, and bad stuff happens (as this is often happening on the back of a trapped TLBI from the guest hypervisor).
Instead, make sure we always use the provided MMU context all the way. This has no impact on non-NV, as we always pass the canonical MMU context.
--- arch/arm64/kvm/mmu.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
arch/arm64/kvm/mmu.c +8 -8
@@ -46,16 +46,17 @@ static phys_addr_t stage2_range_addr_end(phys_addr_t addr, phys_addr_t end)* long will also starve other vCPUs. We have to also make sure that the page * tables are not freed while we released the lock. */ -static int stage2_apply_range (struct kvm *kvm,phys_addr_t addr, +static int stage2_apply_range (struct kvm_s2_mmu *mmu,phys_addr_t addr,phys_addr_t end,int (*fn)(struct kvm_pgtable *,u64 ,u64 ),bool resched) { +struct kvm *kvm =kvm_s2_mmu_to_kvm (mmu);int ret;u64 next; do { - struct kvm_pgtable *pgt = kvm->arch.mmu.pgt; + struct kvm_pgtable *pgt = mmu->pgt; if (!pgt) return -EINVAL;
@@ -71,8 +72,8 @@ static int stage2_apply_range(struct kvm *kvm, phys_addr_t addr,return ret; } -#define stage2_apply_range_resched (kvm, addr, end, fn) \ - stage2_apply_range(kvm, addr, end, fn, true) +#define stage2_apply_range_resched (mmu, addr, end, fn) \ + stage2_apply_range(mmu, addr, end, fn, true)static bool memslot_is_logging (struct kvm_memory_slot *memslot) {
@@ -235,7 +236,7 @@ static void __unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t start, u64lockdep_assert_held_write (&kvm->mmu_lock);WARN_ON (size & ~PAGE_MASK ); -WARN_ON (stage2_apply_range (kvm, start, end, kvm_pgtable_stage2_unmap, +WARN_ON (stage2_apply_range (mmu, start, end, kvm_pgtable_stage2_unmap, may_block)); }
@@ -250,7 +251,7 @@ static void stage2_flush_memslot(struct kvm *kvm,phys_addr_t addr = memslot->base_gfn <<PAGE_SHIFT ;phys_addr_t end = addr +PAGE_SIZE * memslot->npages; -stage2_apply_range_resched (kvm, addr, end, kvm_pgtable_stage2_flush); +stage2_apply_range_resched (&kvm->arch.mmu, addr, end, kvm_pgtable_stage2_flush); }/**
@@ -934,8 +935,7 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,*/ static void stage2_wp_range (struct kvm_s2_mmu *mmu,phys_addr_t addr,phys_addr_t end) { -struct kvm *kvm =kvm_s2_mmu_to_kvm (mmu); -stage2_apply_range_resched (kvm, addr, end, kvm_pgtable_stage2_wrprotect); +stage2_apply_range_resched (mmu, addr, end, kvm_pgtable_stage2_wrprotect); }/**
Signature · 1 line
2.34.1
Re: [PATCH 02/18] KVM: arm64: Use the S2 MMU context to iterate over S2 table
Quoted 5 lines · On Thu, 9 Feb 2023 17:58:04 +0000 Marc Zyngier <[email protected]> wrote:
Most of our S2 helpers take a
kvm_s2_mmupointer, but quickly revert back to using the kvm structure. By doing so, we lose track of which S2 MMU context we were initially using, and fallback to the "canonical" context.
Quoted 8 lines
If we were trying to unmap a S2 context managed by a guest hypervisor, we end-up parsing the wrong set of page tables, and bad stuff happens (as this is often happening on the back of a trapped TLBI from the guest hypervisor).
Instead, make sure we always use the provided MMU context all the way. This has no impact on non-NV, as we always pass the canonical MMU context.
Indeed this just changes stage2_apply_range() and all its callers, in a manner that shouldn't change the current behaviour, but preserves the S2 MMU passed in:
Quoted 1 line
Signed-off-by Marc Zyngier <[email protected]>
Cheers,
Andre
Quoted 67 lines
--- arch/arm64/kvm/mmu.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)arch/arm64/kvm/mmu.c +8
-8@@ -46,16 +46,17 @@ static phys_addr_t stage2_range_addr_end(phys_addr_t addr, phys_addr_t end)* long will also starve other vCPUs. We have to also make sure that the page * tables are not freed while we released the lock. */ -static int stage2_apply_range (struct kvm *kvm,phys_addr_t addr, +static int stage2_apply_range (struct kvm_s2_mmu *mmu,phys_addr_t addr,phys_addr_t end,int (*fn)(struct kvm_pgtable *,u64 ,u64 ),bool resched) { +struct kvm *kvm =kvm_s2_mmu_to_kvm (mmu);int ret;u64 next; do { - struct kvm_pgtable *pgt = kvm->arch.mmu.pgt; + struct kvm_pgtable *pgt = mmu->pgt; if (!pgt) return -EINVAL;@@ -71,8 +72,8 @@ static int stage2_apply_range(struct kvm *kvm, phys_addr_t addr,return ret; } -#define stage2_apply_range_resched (kvm, addr, end, fn) \ - stage2_apply_range(kvm, addr, end, fn, true) +#define stage2_apply_range_resched (mmu, addr, end, fn) \ + stage2_apply_range(mmu, addr, end, fn, true)static bool memslot_is_logging (struct kvm_memory_slot *memslot) {@@ -235,7 +236,7 @@ static void __unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t start, u64lockdep_assert_held_write (&kvm->mmu_lock);WARN_ON (size & ~PAGE_MASK ); -WARN_ON (stage2_apply_range (kvm, start, end, kvm_pgtable_stage2_unmap, +WARN_ON (stage2_apply_range (mmu, start, end, kvm_pgtable_stage2_unmap, may_block)); }@@ -250,7 +251,7 @@ static void stage2_flush_memslot(struct kvm *kvm,phys_addr_t addr = memslot->base_gfn <<PAGE_SHIFT ;phys_addr_t end = addr +PAGE_SIZE * memslot->npages; -stage2_apply_range_resched (kvm, addr, end, kvm_pgtable_stage2_flush); +stage2_apply_range_resched (&kvm->arch.mmu, addr, end, kvm_pgtable_stage2_flush); }/** @@ -934,8 +935,7 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,*/ static void stage2_wp_range (struct kvm_s2_mmu *mmu,phys_addr_t addr,phys_addr_t end) { -struct kvm *kvm =kvm_s2_mmu_to_kvm (mmu); -stage2_apply_range_resched (kvm, addr, end, kvm_pgtable_stage2_wrprotect); +stage2_apply_range_resched (mmu, addr, end, kvm_pgtable_stage2_wrprotect); }/**
Marc Zyngier
maintainerPATCH 3/18 KVM: arm64: nv: Introduce nested virtualization VCPU feature
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
Introduce the feature bit and a primitive that checks if the feature is set behind a static key check based on the cpus_have_const_cap check.
Checking vcpu_has_nv() on systems without nested virt enabled should have negligible overhead.
We don't yet allow userspace to actually set this feature.
--- arch/arm64/include/asm/kvm_nested.h | 14 ++++++++++++++ arch/arm64/include/uapi/asm/kvm.h | 1 + 2 files changed, 15 insertions(+) create mode 100644 arch/arm64/include/asm/kvm_nested.h
arch/arm64/include/asm/kvm_nested.h new file +14 -0
@@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ARM64_KVM_NESTED_H +#define __ARM64_KVM_NESTED_H + +#include <linux/kvm_host.h> + +static inline bool vcpu_has_nv (const struct kvm_vcpu *vcpu) +{ +return (!__is_defined (__KVM_NVHE_HYPERVISOR__) && +cpus_have_final_cap (ARM64_HAS_NESTED_VIRT ) && +test_bit (KVM_ARM_VCPU_HAS_EL2 , vcpu->arch.features)); +} + +#endif /* __ARM64_KVM_NESTED_H */
arch/arm64/include/uapi/asm/kvm.h +1 -0
@@ -109,6 +109,7 @@ struct kvm_regs {#define KVM_ARM_VCPU_SVE 4/* enable SVE for this CPU */ #define KVM_ARM_VCPU_PTRAUTH_ADDRESS 5/* VCPU uses address authentication */ #define KVM_ARM_VCPU_PTRAUTH_GENERIC 6/* VCPU uses generic authentication */ +#define KVM_ARM_VCPU_HAS_EL2 7/* Support nested virtualization */ struct kvm_vcpu_init {__u32 target;
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 4/18 KVM: arm64: nv: Reset VCPU to EL2 registers if VCPU nested virt is set
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
Reset the VCPU with PSTATE.M = EL2h when the nested virtualization feature is enabled on the VCPU.
--- arch/arm64/kvm/reset.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
arch/arm64/kvm/reset.c +16 -0
@@ -27,6 +27,7 @@#include <asm/kvm_asm.h> #include <asm/kvm_emulate.h> #include <asm/kvm_mmu.h> +#include <asm/kvm_nested.h> #include <asm/virt.h> /* Maximum phys_shift supported for any VM on this host */
@@ -38,6 +39,9 @@ static u32 kvm_ipa_limit;#define VCPU_RESET_PSTATE_EL1 (PSR_MODE_EL1h | PSR_A_BIT | PSR_I_BIT | \ PSR_F_BIT | PSR_D_BIT) +#define VCPU_RESET_PSTATE_EL2 (PSR_MODE_EL2h | PSR_A_BIT | PSR_I_BIT | \ + PSR_F_BIT | PSR_D_BIT) +#define VCPU_RESET_PSTATE_SVC (PSR_AA32_MODE_SVC | PSR_AA32_A_BIT | \ PSR_AA32_I_BIT | PSR_AA32_F_BIT)
@@ -220,6 +224,10 @@ static int kvm_set_vm_width(struct kvm_vcpu *vcpu)if (kvm_has_mte (kvm) && is32bit)return -EINVAL ; +/* NV is incompatible with AArch32 */ +if (vcpu_has_nv (vcpu) && is32bit) +return -EINVAL ; +if (is32bit)set_bit (KVM_ARCH_FLAG_EL1_32BIT , &kvm->arch.flags);
@@ -272,6 +280,12 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)if (loaded)kvm_arch_vcpu_put (vcpu); +/* Disallow NV+SVE for the time being */ +if (vcpu_has_nv (vcpu) &&vcpu_has_feature (vcpu,KVM_ARM_VCPU_SVE )) { + ret = -EINVAL ; + goto out; + } +if (!kvm_arm_vcpu_sve_finalized (vcpu)) {if (test_bit (KVM_ARM_VCPU_SVE , vcpu->arch.features)) { ret =kvm_vcpu_enable_sve (vcpu);
@@ -294,6 +308,8 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)default :if (vcpu_el1_is_32bit (vcpu)) { pstate =VCPU_RESET_PSTATE_SVC ; + }else if (vcpu_has_nv (vcpu)) { + pstate =VCPU_RESET_PSTATE_EL2 ; }else { pstate =VCPU_RESET_PSTATE_EL1 ; }
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 5/18 KVM: arm64: nv: Allow userspace to set PSR_MODE_EL2x
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
We were not allowing userspace to set a more privileged mode for the VCPU than EL1, but we should allow this when nested virtualization is enabled for the VCPU.
--- arch/arm64/kvm/guest.c | 6 ++++++ 1 file changed, 6 insertions(+)
arch/arm64/kvm/guest.c +6 -0
@@ -24,6 +24,7 @@#include <asm/fpsimd.h> #include <asm/kvm.h> #include <asm/kvm_emulate.h> +#include <asm/kvm_nested.h> #include <asm/sigcontext.h> #include "trace.h"
@@ -253,6 +254,11 @@ static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)if (!vcpu_el1_is_32bit (vcpu))return -EINVAL ;break ; +case PSR_MODE_EL2h: +case PSR_MODE_EL2t: +if (!vcpu_has_nv (vcpu)) +return -EINVAL ; + fallthrough;case PSR_MODE_EL0t:case PSR_MODE_EL1t:case PSR_MODE_EL1h:
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 6/18 KVM: arm64: nv: Add EL2 system registers to vcpu context
In reply to Marc ZyngierAdd the minimal set of EL2 system registers to the vcpu context. Nothing uses them just yet.
--- arch/arm64/include/asm/kvm_host.h | 33 ++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kvm_host.h +32 -1
@@ -325,12 +325,43 @@ enum vcpu_sysreg {TFSR_EL1 ,/* Tag Fault Status Register (EL1) */ TFSRE0_EL1 ,/* Tag Fault Status Register (EL0) */ -/* 32bit specific registers. Keep them at the end of the range */ +/* 32bit specific registers. */ DACR32_EL2 ,/* Domain Access Control Register */ IFSR32_EL2 ,/* Instruction Fault Status Register */ FPEXC32_EL2 ,/* Floating-Point Exception Control Register */ DBGVCR32_EL2 ,/* Debug Vector Catch Register */ +/* EL2 registers */ +VPIDR_EL2 ,/* Virtualization Processor ID Register */ +VMPIDR_EL2 ,/* Virtualization Multiprocessor ID Register */ +SCTLR_EL2 ,/* System Control Register (EL2) */ +ACTLR_EL2 ,/* Auxiliary Control Register (EL2) */ +HCR_EL2 ,/* Hypervisor Configuration Register */ +MDCR_EL2 ,/* Monitor Debug Configuration Register (EL2) */ +CPTR_EL2 ,/* Architectural Feature Trap Register (EL2) */ +HSTR_EL2 ,/* Hypervisor System Trap Register */ +HACR_EL2 ,/* Hypervisor Auxiliary Control Register */ +TTBR0_EL2 ,/* Translation Table Base Register 0 (EL2) */ +TTBR1_EL2 ,/* Translation Table Base Register 1 (EL2) */ +TCR_EL2 ,/* Translation Control Register (EL2) */ +VTTBR_EL2 ,/* Virtualization Translation Table Base Register */ +VTCR_EL2 ,/* Virtualization Translation Control Register */ +SPSR_EL2 ,/* EL2 saved program status register */ +ELR_EL2 ,/* EL2 exception link register */ +AFSR0_EL2 ,/* Auxiliary Fault Status Register 0 (EL2) */ +AFSR1_EL2 ,/* Auxiliary Fault Status Register 1 (EL2) */ +ESR_EL2 ,/* Exception Syndrome Register (EL2) */ +FAR_EL2 ,/* Fault Address Register (EL2) */ +HPFAR_EL2 ,/* Hypervisor IPA Fault Address Register */ +MAIR_EL2 ,/* Memory Attribute Indirection Register (EL2) */ +AMAIR_EL2 ,/* Auxiliary Memory Attribute Indirection Register (EL2) */ +VBAR_EL2 ,/* Vector Base Address Register (EL2) */ +RVBAR_EL2 ,/* Reset Vector Base Address Register */ +CONTEXTIDR_EL2 ,/* Context ID Register (EL2) */ +TPIDR_EL2 ,/* EL2 Software Thread ID Register */ +CNTHCTL_EL2 ,/* Counter-timer Hypervisor Control register */ +SP_EL2 ,/* EL2 Stack Pointer */ +NR_SYS_REGS /* Nothing after this line! */ };
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 7/18 KVM: arm64: nv: Add nested virt VCPU primitives for vEL2 VCPU state
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
When running a nested hypervisor we commonly have to figure out if the VCPU mode is running in the context of a guest hypervisor or guest guest, or just a normal guest.
Add convenient primitives for this.
--- arch/arm64/include/asm/kvm_emulate.h | 56 ++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+)
arch/arm64/include/asm/kvm_emulate.h +56 -0
@@ -183,6 +183,62 @@ static __always_inline void vcpu_set_reg(struct kvm_vcpu *vcpu, u8 reg_num,vcpu_gp_regs (vcpu)->regs[reg_num] = val; } +static inline bool vcpu_is_el2_ctxt (const struct kvm_cpu_context *ctxt) +{ +switch (ctxt->regs.pstate & (PSR_MODE32_BIT |PSR_MODE_MASK )) { +case PSR_MODE_EL2h: +case PSR_MODE_EL2t: +return true; +default : +return false; + } +} + +static inline bool vcpu_is_el2 (const struct kvm_vcpu *vcpu) +{ +return vcpu_is_el2_ctxt (&vcpu->arch.ctxt); +} + +static inline bool __vcpu_el2_e2h_is_set (const struct kvm_cpu_context *ctxt) +{ +return ctxt_sys_reg (ctxt,HCR_EL2 ) &HCR_E2H ; +} + +static inline bool vcpu_el2_e2h_is_set (const struct kvm_vcpu *vcpu) +{ +return __vcpu_el2_e2h_is_set (&vcpu->arch.ctxt); +} + +static inline bool __vcpu_el2_tge_is_set (const struct kvm_cpu_context *ctxt) +{ +return ctxt_sys_reg (ctxt,HCR_EL2 ) &HCR_TGE ; +} + +static inline bool vcpu_el2_tge_is_set (const struct kvm_vcpu *vcpu) +{ +return __vcpu_el2_tge_is_set (&vcpu->arch.ctxt); +} + +static inline bool __is_hyp_ctxt (const struct kvm_cpu_context *ctxt) +{ +/* +* We are in a hypervisor context if the vcpu mode is EL2 or +* E2H and TGE bits are set. The latter means we are in the user space +* of the VHE kernel. ARMv8.1 ARM describes this as 'InHost' +* +* Note that the HCR_EL2.{E2H,TGE}={0,1} isn't really handled in the +* rest of the KVM code, and will result in a misbehaving guest. +*/ +return vcpu_is_el2_ctxt (ctxt) || + (__vcpu_el2_e2h_is_set (ctxt) &&__vcpu_el2_tge_is_set (ctxt)) || +__vcpu_el2_tge_is_set (ctxt); +} + +static inline bool is_hyp_ctxt (const struct kvm_vcpu *vcpu) +{ +return __is_hyp_ctxt (&vcpu->arch.ctxt); +} +/* * The layout of SPSR for an AArch32 state is different when observed from an * AArch64 SPSR_ELx or an AArch32 SPSR_*. This function generates the AArch32
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 8/18 KVM: arm64: nv: Handle HCR_EL2.NV system register traps
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
ARM v8.3 introduces a new bit in the HCR_EL2, which is the NV bit. When this bit is set, accessing EL2 registers in EL1 traps to EL2. In addition, executing the following instructions in EL1 will trap to EL2:
tlbi, at, eret, and msr/mrs instructions to access SP_EL1. Most of the instructions that trap to EL2 with the NV bit were undef at EL1 prior to ARM v8.3. The only instruction that was not undef is eret.
This patch sets up a handler for EL2 registers and SP_EL1 register accesses at EL1. The host hypervisor keeps those register values in memory, and will emulate their behavior.
This patch doesn't set the NV bit yet. It will be set in a later patch once nested virtualization support is completed.
--- arch/arm64/include/asm/sysreg.h | 38 ++++++++++++- arch/arm64/kvm/sys_regs.c | 99 +++++++++++++++++++++++++++++++-- 2 files changed, 131 insertions(+), 6 deletions(-)
arch/arm64/include/asm/sysreg.h +37 -1
@@ -490,23 +490,51 @@#define SYS_PMCCFILTR_EL0 sys_reg(3, 3, 14, 15, 7) +#define SYS_VPIDR_EL2 sys_reg(3, 4, 0, 0, 0) +#define SYS_VMPIDR_EL2 sys_reg(3, 4, 0, 0, 5) +#define SYS_SCTLR_EL2 sys_reg(3, 4, 1, 0, 0) +#define SYS_ACTLR_EL2 sys_reg(3, 4, 1, 0, 1) +#define SYS_HCR_EL2 sys_reg(3, 4, 1, 1, 0) +#define SYS_MDCR_EL2 sys_reg(3, 4, 1, 1, 1) +#define SYS_CPTR_EL2 sys_reg(3, 4, 1, 1, 2) +#define SYS_HSTR_EL2 sys_reg(3, 4, 1, 1, 3)#define SYS_HFGRTR_EL2 sys_reg(3, 4, 1, 1, 4)#define SYS_HFGWTR_EL2 sys_reg(3, 4, 1, 1, 5)#define SYS_HFGITR_EL2 sys_reg(3, 4, 1, 1, 6) +#define SYS_HACR_EL2 sys_reg(3, 4, 1, 1, 7) + +#define SYS_TTBR0_EL2 sys_reg(3, 4, 2, 0, 0) +#define SYS_TTBR1_EL2 sys_reg(3, 4, 2, 0, 1) +#define SYS_TCR_EL2 sys_reg(3, 4, 2, 0, 2) +#define SYS_VTTBR_EL2 sys_reg(3, 4, 2, 1, 0) +#define SYS_VTCR_EL2 sys_reg(3, 4, 2, 1, 2) +#define SYS_TRFCR_EL2 sys_reg(3, 4, 1, 2, 1)#define SYS_HDFGRTR_EL2 sys_reg(3, 4, 3, 1, 4)#define SYS_HDFGWTR_EL2 sys_reg(3, 4, 3, 1, 5)#define SYS_HAFGRTR_EL2 sys_reg(3, 4, 3, 1, 6)#define SYS_SPSR_EL2 sys_reg(3, 4, 4, 0, 0)#define SYS_ELR_EL2 sys_reg(3, 4, 4, 0, 1) +#define SYS_SP_EL1 sys_reg(3, 4, 4, 1, 0)#define SYS_IFSR32_EL2 sys_reg(3, 4, 5, 0, 1) +#define SYS_AFSR0_EL2 sys_reg(3, 4, 5, 1, 0) +#define SYS_AFSR1_EL2 sys_reg(3, 4, 5, 1, 1)#define SYS_ESR_EL2 sys_reg(3, 4, 5, 2, 0)#define SYS_VSESR_EL2 sys_reg(3, 4, 5, 2, 3)#define SYS_FPEXC32_EL2 sys_reg(3, 4, 5, 3, 0)#define SYS_TFSR_EL2 sys_reg(3, 4, 5, 6, 0) -#define SYS_VDISR_EL2 sys_reg(3, 4, 12, 1, 1) +#define SYS_FAR_EL2 sys_reg(3, 4, 6, 0, 0) +#define SYS_HPFAR_EL2 sys_reg(3, 4, 6, 0, 4) + +#define SYS_MAIR_EL2 sys_reg(3, 4, 10, 2, 0) +#define SYS_AMAIR_EL2 sys_reg(3, 4, 10, 3, 0) + +#define SYS_VBAR_EL2 sys_reg(3, 4, 12, 0, 0) +#define SYS_RVBAR_EL2 sys_reg(3, 4, 12, 0, 1) +#define SYS_RMR_EL2 sys_reg(3, 4, 12, 0, 2) +#define SYS_VDISR_EL2 sys_reg(3, 4, 12, 1, 1)#define __SYS__AP0Rx_EL2 (x) sys_reg(3, 4, 12, 8, x)#define SYS_ICH_AP0R0_EL2 __SYS__AP0Rx_EL2(0)#define SYS_ICH_AP0R1_EL2 __SYS__AP0Rx_EL2(1)
@@ -548,6 +576,12 @@#define SYS_ICH_LR14_EL2 __SYS__LR8_EL2(6)#define SYS_ICH_LR15_EL2 __SYS__LR8_EL2(7) +#define SYS_CONTEXTIDR_EL2 sys_reg(3, 4, 13, 0, 1) +#define SYS_TPIDR_EL2 sys_reg(3, 4, 13, 0, 2) + +#define SYS_CNTVOFF_EL2 sys_reg(3, 4, 14, 0, 3) +#define SYS_CNTHCTL_EL2 sys_reg(3, 4, 14, 1, 0) +/* VHE encodings for architectural EL0/1 system registers */ #define SYS_SCTLR_EL12 sys_reg(3, 5, 1, 0, 0)#define SYS_TTBR0_EL12 sys_reg(3, 5, 2, 0, 0)
@@ -570,6 +604,8 @@#define SYS_CNTV_CTL_EL02 sys_reg(3, 5, 14, 3, 1)#define SYS_CNTV_CVAL_EL02 sys_reg(3, 5, 14, 3, 2) +#define SYS_SP_EL2 sys_reg(3, 6, 4, 1, 0) +/* Common SCTLR_ELx flags. */ #define SCTLR_ELx_ENTP2 (BIT(60))#define SCTLR_ELx_DSSBS (BIT(44))
arch/arm64/kvm/sys_regs.c +94 -5
145 lines not on this page · raw message
Signature · 1 line
2.34.1
Joey Gouly
reviewerRe: [PATCH 08/18] KVM: arm64: nv: Handle HCR_EL2.NV system register traps
Hi Marc,
Quoted 20 lines · Marc Zyngier
From: Jintack Lim <[email protected]>
ARM v8.3 introduces a new bit in the
HCR_EL2, which is the NV bit. When this bit is set, accessing EL2 registers in EL1 traps to EL2. In addition, executing the following instructions in EL1 will trap to EL2:
tlbi, at, eret, and msr/mrs instructions to accessSP_EL1. Most of the instructions that trap to EL2 with the NV bit were undef at EL1 prior to ARM v8.3. The only instruction that was not undef is eret.This patch sets up a handler for EL2 registers and
SP_EL1register accesses at EL1. The host hypervisor keeps those register values in memory, and will emulate their behavior.This patch doesn't set the NV bit yet. It will be set in a later patch once nested virtualization support is completed.
Reviewed-by Russell King (Oracle) <[email protected]>Signed-off-by Jintack Lim <[email protected]>[maz: EL2_REG() macros]Signed-off-by Marc Zyngier <[email protected]>
[..]
Quoted 160 lines
arch/arm64/kvm/sys_regs.c +94
-5@@ -24,6 +24,7 @@#include <asm/kvm_emulate.h> #include <asm/kvm_hyp.h> #include <asm/kvm_mmu.h> +#include <asm/kvm_nested.h> #include <asm/perf_event.h> #include <asm/sysreg.h> @@ -102,6 +103,18 @@ static u32 get_ccsidr(u32 csselr)return ccsidr; } +static bool access_rw (struct kvm_vcpu *vcpu, +struct sys_reg_params *p, +const struct sys_reg_desc *r) +{ +if (p->is_write) +vcpu_write_sys_reg (vcpu, p->regval, r->reg); +else + p->regval =vcpu_read_sys_reg (vcpu, r->reg); + +return true; +} +/* * See note at ARMv7 ARM B1.14.4 (TL;DR: S/W ops are not easily virtualized). */ @@ -260,6 +273,14 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,return read_zero (vcpu, p); } +static bool trap_undef (struct kvm_vcpu *vcpu, +struct sys_reg_params *p, +const struct sys_reg_desc *r) +{ +kvm_inject_undefined (vcpu); +return false; +} +/* * ARMv8.1 mandates at least a trivial LORegion implementation, where all the * RW registers are RES0 (which we can implement as RAZ/WI). On an ARMv8.0 @@ -370,12 +391,9 @@ static bool trap_debug_regs(struct kvm_vcpu *vcpu,struct sys_reg_params *p, const struct sys_reg_desc *r) { -if (p->is_write) { -vcpu_write_sys_reg (vcpu, p->regval, r->reg); +access_rw (vcpu ,p ,r ); +if (p->is_write)vcpu_set_flag (vcpu,DEBUG_DIRTY ); - }else { - p->regval =vcpu_read_sys_reg (vcpu, r->reg); - }trace_trap_reg (__func__, r->reg, p->is_write, p->regval);@@ -1446,6 +1464,24 @@ static unsigned int mte_visibility(const struct kvm_vcpu *vcpu, .visibility = mte_visibility, \ } +static unsigned int el2_visibility (const struct kvm_vcpu *vcpu, +const struct sys_reg_desc *rd) +{ +if (vcpu_has_nv (vcpu)) +return 0 ; + +return REG_HIDDEN ; +} + +#define EL2_REG (name, acc, rst, v) { \ + SYS_DESC(SYS_##name), \ + .access = acc, \ + .reset = rst, \ + .reg = name, \ + .visibility = el2_visibility, \ + .val = v, \ +} +/* sys_reg_desc initialiser for known cpufeature ID registers */ #define ID_SANITISED (name) { \ SYS_DESC(SYS_##name), \@@ -1490,6 +1526,18 @@ static unsigned int mte_visibility(const struct kvm_vcpu *vcpu, .visibility = raz_visibility, \ } +static bool access_sp_el1 (struct kvm_vcpu *vcpu, +struct sys_reg_params *p, +const struct sys_reg_desc *r) +{ +if (p->is_write) +__vcpu_sys_reg (vcpu,SP_EL1 ) = p->regval; +else + p->regval =__vcpu_sys_reg (vcpu,SP_EL1 ); + +return true; +} +/* * Architected system registers. * Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2 @@ -1913,9 +1961,50 @@ static const struct sys_reg_desc sys_reg_descs[] = { { PMU_SYS_REG(SYS_PMCCFILTR_EL0), .access = access_pmu_evtyper, .reset = reset_val, .reg = PMCCFILTR_EL0, .val = 0 }, +EL2_REG (VPIDR_EL2 , access_rw, reset_unknown,0 ), +EL2_REG (VMPIDR_EL2 , access_rw, reset_unknown,0 ), +EL2_REG (SCTLR_EL2 , access_rw, reset_val,SCTLR_EL2_RES1 ), +EL2_REG (ACTLR_EL2 , access_rw, reset_val,0 ), +EL2_REG (HCR_EL2 , access_rw, reset_val,0 ), +EL2_REG (MDCR_EL2 , access_rw, reset_val,0 ), +EL2_REG (CPTR_EL2 , access_rw, reset_val,CPTR_EL2_DEFAULT ), +EL2_REG (HSTR_EL2 , access_rw, reset_val,0 ), +EL2_REG (HACR_EL2 , access_rw, reset_val,0 ), + +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 (VTTBR_EL2 , access_rw, reset_val,0 ), +EL2_REG (VTCR_EL2 , access_rw, reset_val,0 ), + { SYS_DESC(SYS_DACR32_EL2), NULL, reset_unknown, DACR32_EL2 }, +EL2_REG (SPSR_EL2 , access_rw, reset_val,0 ), +EL2_REG (ELR_EL2 , access_rw, reset_val,0 ), + { SYS_DESC(SYS_SP_EL1), access_sp_el1}, + { SYS_DESC(SYS_IFSR32_EL2), NULL, reset_unknown, IFSR32_EL2 }, +EL2_REG (AFSR0_EL2 , access_rw, reset_val,0 ), +EL2_REG (AFSR1_EL2 , access_rw, reset_val,0 ), +EL2_REG (ESR_EL2 , access_rw, reset_val,0 ), { SYS_DESC(SYS_FPEXC32_EL2), NULL, reset_val, FPEXC32_EL2, 0x700 }, + +EL2_REG (FAR_EL2 , access_rw, reset_val,0 ), +EL2_REG (HPFAR_EL2 , access_rw, reset_val,0 ), + +EL2_REG (MAIR_EL2 , access_rw, reset_val,0 ), +EL2_REG (AMAIR_EL2 , access_rw, reset_val,0 ), + +EL2_REG (VBAR_EL2 , access_rw, reset_val,0 ), +EL2_REG (RVBAR_EL2 , access_rw, reset_val,0 ), + { SYS_DESC(SYS_RMR_EL2), trap_undef }, + +EL2_REG (CONTEXTIDR_EL2 , access_rw, reset_val,0 ), +EL2_REG (TPIDR_EL2 , access_rw, reset_val,0 ), + +EL2_REG (CNTVOFF_EL2 , access_rw, reset_val,0 ), +EL2_REG (CNTHCTL_EL2 , access_rw, reset_val,0 ), + + EL2_REG(SP_EL2, NULL, reset_unknown, 0), }; static bool trap_dbgdidr(struct kvm_vcpu *vcpu,Signature · 1 line
2.34.1
I'm having an issue with this commit where a VCPU is getting a CNTVOFF_EL2 set to 0, so it sees the same time as the host system, and the other VCPU has the correct offset.
This is due to the new line added in this commit:
EL2_REG(CNTVOFF_EL2, access_rw, reset_val, 0),
Here is an excerpt of the dmesg:
[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x000f0510] [ 0.000000] Linux version 5.14.0-rc1-00011-gc2b59ec4a322-dirty SMP PREEMPT [ 0.000000] Machine model: linux,dummy-virt [ 0.000000] efi: UEFI not found. [ 0.000000] earlycon: ns16550a0 at MMIO 0x0000000001000000 (options '') [ 0.000000] printk: bootconsole [ns16550a0] enabled [ 0.000000] NUMA: No NUMA configuration found [ 0.000000] NUMA: Faking a node at [mem 0x0000000080000000-0x0000000093ffffff] [ 0.000000] NUMA: NODE_DATA [mem 0x93f60c00-0x93f62fff] [ 0.000000] Zone ranges: [ 0.000000] DMA [mem 0x0000000080000000-0x0000000093ffffff] [ 0.000000] DMA32 empty [ 0.000000] Normal empty [ 0.000000] Movable zone start for each node [ 0.000000] Early memory node ranges [ 0.000000] node 0: [mem 0x0000000080000000-0x0000000093ffffff] [ 0.000000] Initmem setup node 0 [mem 0x0000000080000000-0x0000000093ffffff] [ 0.000000] On node 0, zone DMA: 16384 pages in unavailable ranges [ 0.000000] cma: Reserved 32 MiB at 0x0000000091800000 [ 0.000000] psci: probing for conduit method from DT. [ 0.000000] psci: PSCIv1.1 detected in firmware. [ 0.000000] psci: Using standard PSCI v0.2 function IDs [ 0.000000] psci: Trusted OS migration not required [ 0.000000] psci: SMC Calling Convention v1.1 [ 0.000000] smccc: KVM: hypervisor services detected (0x00000000 0x00000000 0x00000000 0x00000003) [ 0.000000] percpu: Embedded 33 pages/cpu s96088 r8192 d30888 u135168 [ 0.000000] Detected PIPT I-cache on CPU0 [ 0.000000] CPU features: detected: Branch Target Identification [ 0.000000] CPU features: detected: GIC system register CPU interface [ 0.000000] CPU features: detected: Spectre-v4 [ 0.000000] CPU features: kernel page table isolation forced ON by KASLR [ 0.000000] CPU features: detected: Kernel page table isolation (KPTI) [..] [ 0.516017] fsl-mc MSI: msic domain created [ 0.544449] EFI services will not be available. [ 0.575385] smp: Bringing up secondary CPUs ... [ 0.658517] smp: Brought up 1 node, 2 CPUs [ 0.849724] SMP: Total of 2 processors activated. [ 0.893825] CPU features: detected: 32-bit EL0 Support [ 0.935321] CPU features: detected: 32-bit EL1 Support [ 0.989719] CPU features: detected: ARMv8.4 Translation Table Level [ 1.049576] CPU features: detected: Data cache clean to the PoU not required for I/D coherence [ 1.108269] CPU features: detected: Common not Private translations [ 1.151863] CPU features: detected: CRC32 instructions [ 1.186088] CPU features: detected: RCpc load-acquire (LDAPR) [ 1.225008] CPU features: detected: LSE atomic instructions [ 1.262173] CPU features: detected: Privileged Access Never [ 1.299687] CPU features: detected: RAS Extension Support [ 1.337388] CPU features: detected: Random Number Generator [ 1.374957] CPU features: detected: Speculation barrier (SB) [ 1.412860] CPU features: detected: Stage-2 Force Write-Back [ 1.458967] CPU features: detected: TLB range maintenance instructions [ 1.503504] CPU features: detected: Speculative Store Bypassing Safe (SSBS) [ 6.740431] CPU: All CPU(s) started at EL1 [ 6.779526] alternatives: patching kernel code [ 1685.522538] devtmpfs: initialized [ 1685.591049] KASLR enabled [ 1685.614942] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns [ 1685.686698] futex hash table entries: 512 (order: 3, 32768 bytes, linear) [ 1685.748071] pinctrl core: initialized pinctrl subsystem [ 1685.809348] DMI not present or invalid. [ 1685.854306] NET: Registered PF_NETLINK/PF_ROUTE protocol family [ 1685.924942] DMA: preallocated 128 KiB GFP_KERNEL pool for atomic allocations [ 1685.981774] DMA: preallocated 128 KiB GFP_KERNEL|GFP_DMA pool for atomic allocations [ 1686.042387] DMA: preallocated 128 KiB GFP_KERNEL|GFP_DMA32 pool for atomic allocations [ 1686.099864] audit: initializing netlink subsys (disabled) [ 1686.152647] audit: type=2000 audit(5.488:1): state=initialized audit_enabled=0 res=1 [ 7.525335] thermal_sys: Registered thermal governor 'step_wise' [ 7.565113] thermal_sys: Registered thermal governor 'power_allocator' [ 7.609143] cpuidle: using governor menu [ 7.681526] NET: Registered PF_QIPCRTR protocol family [ 7.720877] hw-breakpoint: found 6 breakpoint and 4 watchpoint registers. [ 7.770844] ASID allocator initialised with 32768 entries [ 7.816662] Serial: AMBA PL011 UART driver [ 1686.613910] HugeTLB registered 1.00 GiB page size, pre-allocated 0 pages [ 1686.666303] HugeTLB registered 32.0 MiB page size, pre-allocated 0 pages [ 1686.725358] HugeTLB registered 2.00 MiB page size, pre-allocated 0 pages [ 1686.772979] HugeTLB registered 64.0 KiB page size, pre-allocated 0 pages
[..]
The flow of execution looks like this:
KVM_CREATE_VCPU 0 (VMM) -> kvm_timer_vcpu_init -> update_vtimer_cntvoff (VCPU0.CNTVOFF_EL2=kvm_phys_timer_read) KVM_ARM_VCPU_INIT (VMM) -> kvm_arch_vcpu_ioctl_vcpu_init -> kvm_vcpu_set_target -> kvm_reset_vcpu -> kvm_reset_sys_regs (VCPU0.CNTVOFF_EL2=0) KVM_CREATE_VCPU 1 (VMM) -> kvm_timer_vcpu_init -> update_vtimer_cntvoff (VCPU0.CNTVOFF_EL2=VCPU1.CNTVOFF_EL2=kvm_phys_timer_read) KVM_ARM_VCPU_INIT (VMM) -> kvm_arch_vcpu_ioctl_vcpu_init -> kvm_vcpu_set_target -> kvm_reset_vcpu -> kvm_reset_sys_regs (VCPU1.CNTVOFF_EL2=0) At this point VCPU0 has CNTVOFF_EL2 == kvm_phys_timer_read, and VCPU1 has CNTVOFF_EL2 == 0.
The VCPUs having different CNTVOFF_EL2 valuess is just a symptom of the fact that CNTVOFF_EL2 is now reset in kvm_reset_sys_regs.
This is with linux kvmarm/next [1], kvmtool [2], qemu [3] (I also saw similar behaviour on FVP).
qemu-system-aarch64 -M virt \
-machine virtualization=true \
-machine virt,gic-version=3 \
-machine mte=off \
-cpu max,pauth=off -smp 2 -m 12144 \
-drive if=none,format=qcow2,file=disk.img,id=blk0 \
-device virtio-blk-pci,drive=blk0 \
-nographic \
-device virtio-net-pci,netdev=net0 \
-netdev user,id=net0,hostfwd=tcp::8024-:22 \
-kernel ../linux/arch/arm64/boot/Image \
-append "root=/dev/vda2" And then running kvmtool:
lkvm run -k Image -p earlycon
Hopefully that is all that is needed to reproduce it.
The following patch gets it booting again, but I'm sure it's not the right fix.
arch/arm64/kvm/sys_regs.c +9 -1
@@ -1587,6 +1587,14 @@ static bool access_ccsidr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,return true; } +static void nv_reset_val (struct kvm_vcpu *vcpu,const struct sys_reg_desc *r) +{ +BUG_ON (!r->reg); +BUG_ON (r->reg >=NR_SYS_REGS ); +if (vcpu_has_nv (vcpu)) +__vcpu_sys_reg (vcpu, r->reg) = r->val; +} +static unsigned int mte_visibility (const struct kvm_vcpu *vcpu,const struct sys_reg_desc *rd) {
@@ -2190,7 +2198,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {EL2_REG (CONTEXTIDR_EL2 , access_rw, reset_val,0 ),EL2_REG (TPIDR_EL2 , access_rw, reset_val,0 ), -EL2_REG (CNTVOFF_EL2 , access_rw, reset_val,0 ), +EL2_REG (CNTVOFF_EL2 , access_rw, nv_reset_val,0 ),EL2_REG (CNTHCTL_EL2 , access_rw, reset_val,0 ), EL12_REG(SCTLR, access_vm_reg, reset_val, 0x00C50078),
Thanks,
Joey
[1] https://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm.git/commit/?h=next&id=96a4627dbbd48144a65af936b321701c70876026
[2] e17d182ad3f797f01947fc234d95c96c050c534b
[3] 99d6b11b5b44d7dd64f4cb1973184e40a4a174f8
Re: [PATCH 08/18] KVM: arm64: nv: Handle HCR_EL2.NV system register traps
Hi Joey,
Quoted 3 lines · Joey Gouly
I'm having an issue with this commit where a VCPU is getting a
CNTVOFF_EL2set to 0, so it sees the same time as the host system, and the other VCPU has the correct offset.
Yikes!
Quoted 22 lines
The flow of execution looks like this:
KVM_CREATE_VCPU 0 (VMM) -> kvm_timer_vcpu_init -> update_vtimer_cntvoff (VCPU0.CNTVOFF_EL2=kvm_phys_timer_read) KVM_ARM_VCPU_INIT (VMM) -> kvm_arch_vcpu_ioctl_vcpu_init -> kvm_vcpu_set_target -> kvm_reset_vcpu -> kvm_reset_sys_regs (VCPU0.CNTVOFF_EL2=0) KVM_CREATE_VCPU 1 (VMM) -> kvm_timer_vcpu_init -> update_vtimer_cntvoff (VCPU0.CNTVOFF_EL2=VCPU1.CNTVOFF_EL2=kvm_phys_timer_read) KVM_ARM_VCPU_INIT (VMM) -> kvm_arch_vcpu_ioctl_vcpu_init -> kvm_vcpu_set_target -> kvm_reset_vcpu -> kvm_reset_sys_regs (VCPU1.CNTVOFF_EL2=0) At this point VCPU0 has CNTVOFF_EL2 == kvm_phys_timer_read, and VCPU1 has CNTVOFF_EL2 == 0.
Quoted 2 lines
The VCPUs having different
CNTVOFF_EL2valuess is just a symptom of the fact thatCNTVOFF_EL2is now reset inkvm_reset_sys_regs.
Right, and the fundamental problem at hand is that we used CNTVOFF_EL2 to stash the _host's_ offset even though we are trying to change the meaning of it to be part of the virtual EL2's context.
Quoted 1 line
The following patch gets it booting again, but I'm sure it's not the right fix.
I'd rather we just break the host away from using the shadow reg altogether and separately track the host offset. As it so happens Marc has a patch that does exactly that [*].
Marc, do you want to resend that patch in isolation addressing the feedback and link to this bug report?
[*] https://lore.kernel.org/kvmarm/[email protected]/
Signature · 2 lines
Thanks, Oliver
Marc Zyngier
maintainerRe: [PATCH 08/18] KVM: arm64: nv: Handle HCR_EL2.NV system register traps
Quoted 32 lines · Oliver Upton
Hi Joey,
Quoted 3 lines · Joey Gouly
I'm having an issue with this commit where a VCPU is getting a
CNTVOFF_EL2set to 0, so it sees the same time as the host system, and the other VCPU has the correct offset.Yikes!
Quoted 22 lines
The flow of execution looks like this:
KVM_CREATE_VCPU 0 (VMM) -> kvm_timer_vcpu_init -> update_vtimer_cntvoff (VCPU0.CNTVOFF_EL2=kvm_phys_timer_read) KVM_ARM_VCPU_INIT (VMM) -> kvm_arch_vcpu_ioctl_vcpu_init -> kvm_vcpu_set_target -> kvm_reset_vcpu -> kvm_reset_sys_regs (VCPU0.CNTVOFF_EL2=0) KVM_CREATE_VCPU 1 (VMM) -> kvm_timer_vcpu_init -> update_vtimer_cntvoff (VCPU0.CNTVOFF_EL2=VCPU1.CNTVOFF_EL2=kvm_phys_timer_read) KVM_ARM_VCPU_INIT (VMM) -> kvm_arch_vcpu_ioctl_vcpu_init -> kvm_vcpu_set_target -> kvm_reset_vcpu -> kvm_reset_sys_regs (VCPU1.CNTVOFF_EL2=0) At this point VCPU0 has CNTVOFF_EL2 == kvm_phys_timer_read, and VCPU1 has CNTVOFF_EL2 == 0.
Quoted 6 lines
Quoted 2 lines
The VCPUs having different
CNTVOFF_EL2valuess is just a symptom of the fact thatCNTVOFF_EL2is now reset inkvm_reset_sys_regs.Right, and the fundamental problem at hand is that we used
CNTVOFF_EL2to stash the _host's_ offset even though we are trying to change the meaning of it to be part of the virtual EL2's context.
What is driving me nuts is that I have never managed to reproduce the problem so far. I guess I have too much crap in my tree to spot it...
Quoted 6 lines
Quoted 2 lines
The following patch gets it booting again, but I'm sure it's not the right fix.
I'd rather we just break the host away from using the shadow reg altogether and separately track the host offset. As it so happens Marc has a patch that does exactly that [*].
Yup, might as well fix that *and* kill the lock inversion issue in one go...
Quoted 4 lines
Marc, do you want to resend that patch in isolation addressing the feedback and link to this bug report?
Joey, could you please give that patch a go, just to be on the safe side?
Thanks,
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Marc Zyngier
maintainerPATCH 9/18 KVM: arm64: nv: Support virtual EL2 exceptions
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
Support injecting exceptions and performing exception returns to and from virtual EL2. This must be done entirely in software except when taking an exception from vEL0 to vEL2 when the virtual HCR_EL2.{E2H,TGE} == {1,1} (a VHE guest hypervisor).
[maz: switch to common exception injection framework, illegal exeption
return handling]
--- arch/arm64/include/asm/kvm_arm.h | 17 +++ arch/arm64/include/asm/kvm_emulate.h | 10 ++ arch/arm64/include/asm/kvm_host.h | 2 +- arch/arm64/kvm/Makefile | 2 +- arch/arm64/kvm/emulate-nested.c | 203 +++++++++++++++++++++++++++ arch/arm64/kvm/hyp/exception.c | 48 +++++-- arch/arm64/kvm/inject_fault.c | 61 +++++++- arch/arm64/kvm/trace_arm.h | 59 ++++++++ 8 files changed, 382 insertions(+), 20 deletions(-) create mode 100644 arch/arm64/kvm/emulate-nested.c
arch/arm64/include/asm/kvm_arm.h +17 -0
@@ -350,4 +350,21 @@#define CPACR_EL1_DEFAULT (CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN |\ CPACR_EL1_ZEN_EL1EN) +#define kvm_mode_names \ + { PSR_MODE_EL0t, "EL0t" }, \ + { PSR_MODE_EL1t, "EL1t" }, \ + { PSR_MODE_EL1h, "EL1h" }, \ + { PSR_MODE_EL2t, "EL2t" }, \ + { PSR_MODE_EL2h, "EL2h" }, \ + { PSR_MODE_EL3t, "EL3t" }, \ + { PSR_MODE_EL3h, "EL3h" }, \ + { PSR_AA32_MODE_USR, "32-bit USR" }, \ + { PSR_AA32_MODE_FIQ, "32-bit FIQ" }, \ + { PSR_AA32_MODE_IRQ, "32-bit IRQ" }, \ + { PSR_AA32_MODE_SVC, "32-bit SVC" }, \ + { PSR_AA32_MODE_ABT, "32-bit ABT" }, \ + { PSR_AA32_MODE_HYP, "32-bit HYP" }, \ + { PSR_AA32_MODE_UND, "32-bit UND" }, \ + { PSR_AA32_MODE_SYS, "32-bit SYS" } +#endif /* __ARM64_KVM_ARM_H__ */
arch/arm64/include/asm/kvm_emulate.h +10 -0
@@ -33,6 +33,12 @@ enum exception_type { except_type_serror = 0x180, }; +#define kvm_exception_type_names \ + { except_type_sync, "SYNC" }, \ + { except_type_irq, "IRQ" }, \ + { except_type_fiq, "FIQ" }, \ + { except_type_serror, "SERROR" } +bool kvm_condition_valid32 (const struct kvm_vcpu *vcpu);void kvm_skip_instr32 (struct kvm_vcpu *vcpu);
@@ -44,6 +50,10 @@ void kvm_inject_size_fault(struct kvm_vcpu *vcpu);void kvm_vcpu_wfi (struct kvm_vcpu *vcpu); +void kvm_emulate_nested_eret (struct kvm_vcpu *vcpu); +int kvm_inject_nested_sync (struct kvm_vcpu *vcpu,u64 esr_el2); +int kvm_inject_nested_irq (struct kvm_vcpu *vcpu); +#if defined(__KVM_VHE_HYPERVISOR__) || defined(__KVM_NVHE_HYPERVISOR__) static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu) {
arch/arm64/include/asm/kvm_host.h +1 -1
@@ -634,7 +634,7 @@ struct kvm_vcpu_arch {#define EXCEPT_AA64_EL1_IRQ __vcpu_except_flags(1)#define EXCEPT_AA64_EL1_FIQ __vcpu_except_flags(2)#define EXCEPT_AA64_EL1_SERR __vcpu_except_flags(3) -/* For AArch64 with NV (one day): */ +/* For AArch64 with NV: */ #define EXCEPT_AA64_EL2_SYNC __vcpu_except_flags(4)#define EXCEPT_AA64_EL2_IRQ __vcpu_except_flags(5)#define EXCEPT_AA64_EL2_FIQ __vcpu_except_flags(6)
arch/arm64/kvm/Makefile +1 -1
@@ -14,7 +14,7 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \ inject_fault.o va_layout.o handle_exit.o \ guest.o debug.o reset.o sys_regs.o stacktrace.o \ vgic-sys-reg-v3.o fpsimd.o pkvm.o \ - arch_timer.o trng.o vmid.o \ + arch_timer.o trng.o vmid.o emulate-nested.o \ vgic/vgic.o vgic/vgic-init.o \ vgic/vgic-irqfd.o vgic/vgic-v2.o \ vgic/vgic-v3.o vgic/vgic-v4.o \
arch/arm64/kvm/emulate-nested.c new file +203 -0
203 lines not on this page · raw message
arch/arm64/kvm/hyp/exception.c +36 -12
91 lines not on this page · raw message
arch/arm64/kvm/inject_fault.c +55 -6
93 lines not on this page · raw message
arch/arm64/kvm/trace_arm.h +59 -0
71 lines not on this page · raw message
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 10/18 KVM: arm64: nv: Inject HVC exceptions to the virtual EL2
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
As we expect all PSCI calls from the L1 hypervisor to be performed using SMC when nested virtualization is enabled, it is clear that all HVC instruction from the VM (including from the virtual EL2) are supposed to handled in the virtual EL2.
Forward these to EL2 as required.
--- arch/arm64/kvm/handle_exit.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
arch/arm64/kvm/handle_exit.c +11 -0
@@ -16,6 +16,7 @@#include <asm/kvm_asm.h> #include <asm/kvm_emulate.h> #include <asm/kvm_mmu.h> +#include <asm/kvm_nested.h> #include <asm/debug-monitors.h> #include <asm/stacktrace/nvhe.h> #include <asm/traps.h>
@@ -41,6 +42,16 @@ static int handle_hvc(struct kvm_vcpu *vcpu) kvm_vcpu_hvc_get_imm(vcpu)); vcpu->stat.hvc_exit_stat++; +/* Forward hvc instructions to the virtual EL2 if the guest has EL2. */ +if (vcpu_has_nv (vcpu)) { +if (vcpu_read_sys_reg (vcpu,HCR_EL2 ) &HCR_HCD ) +kvm_inject_undefined (vcpu); +else +kvm_inject_nested_sync (vcpu,kvm_vcpu_get_esr (vcpu)); + +return 1 ; + } + ret =kvm_hvc_call_handler (vcpu);if (ret <0 ) {vcpu_set_reg (vcpu,0 , ~0UL );
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 11/18 KVM: arm64: nv: Handle trapped ERET from virtual EL2
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
When a guest hypervisor running virtual EL2 in EL1 executes an ERET instruction, we will have set HCR_EL2.NV which traps ERET to EL2, so that we can emulate the exception return in software.
--- arch/arm64/include/asm/esr.h | 4 ++++ arch/arm64/include/asm/kvm_arm.h | 2 +- arch/arm64/kvm/handle_exit.c | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-)
arch/arm64/include/asm/esr.h +4 -0
@@ -272,6 +272,10 @@ (((e) & ESR_ELx_SYS64_ISS_OP2_MASK) >> \ ESR_ELx_SYS64_ISS_OP2_SHIFT)) +/* ISS field definitions for ERET/ERETAA/ERETAB trapping */ +#define ESR_ELx_ERET_ISS_ERET 0x2 +#define ESR_ELx_ERET_ISS_ERETA 0x1 +/* * ISS field definitions for floating-point exception traps * (FP_EXC_32/FP_EXC_64).
arch/arm64/include/asm/kvm_arm.h +1 -1
@@ -344,7 +344,7 @@ECN (SP_ALIGN ),ECN (FP_EXC32 ),ECN (FP_EXC64 ),ECN (SERROR ), \ECN (BREAKPT_LOW ),ECN (BREAKPT_CUR ),ECN (SOFTSTP_LOW ), \ECN (SOFTSTP_CUR ),ECN (WATCHPT_LOW ),ECN (WATCHPT_CUR ), \ - ECN(BKPT32), ECN(VECTOR32), ECN(BRK64) + ECN(BKPT32), ECN(VECTOR32), ECN(BRK64), ECN(ERET)#define CPACR_EL1_TTA (1 << 28)#define CPACR_EL1_DEFAULT (CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN |\
arch/arm64/kvm/handle_exit.c +10 -0
@@ -207,6 +207,15 @@ static int kvm_handle_ptrauth(struct kvm_vcpu *vcpu)return 1 ; } +static int kvm_handle_eret (struct kvm_vcpu *vcpu) +{ +if (kvm_vcpu_get_esr (vcpu) & ESR_ELx_ERET_ISS_ERET) +return kvm_handle_ptrauth (vcpu); + +kvm_emulate_nested_eret (vcpu); +return 1 ; +} +static exit_handle_fn arm_exit_handlers[] = { [0 ... ESR_ELx_EC_MAX] = kvm_handle_unknown_ec, [ESR_ELx_EC_WFx] = kvm_handle_wfx,
@@ -222,6 +231,7 @@ static exit_handle_fn arm_exit_handlers[] = { [ESR_ELx_EC_SMC64] = handle_smc, [ESR_ELx_EC_SYS64] = kvm_handle_sys_reg, [ESR_ELx_EC_SVE] = handle_sve, + [ESR_ELx_EC_ERET] = kvm_handle_eret, [ESR_ELx_EC_IABT_LOW] = kvm_handle_guest_abort, [ESR_ELx_EC_DABT_LOW] = kvm_handle_guest_abort, [ESR_ELx_EC_SOFTSTP_LOW]= kvm_handle_guest_debug,
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 12/18 KVM: arm64: nv: Handle PSCI call via smc from the guest
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
VMs used to execute hvc #0 for the psci call if EL3 is not implemented. However, when we come to provide the virtual EL2 mode to the VM, the host OS inside the VM calls kvm_call_hyp() which is also hvc #0. So, it's hard to differentiate between them from the host hypervisor's point of view.
So, let the VM execute smc instruction for the psci call. On ARMv8.3, even if EL3 is not implemented, a smc instruction executed at non-secure EL1 is trapped to EL2 if HCR_EL2.TSC==1, rather than being treated as UNDEFINED. So, the host hypervisor can handle this psci call without any confusion.
--- arch/arm64/kvm/handle_exit.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)
arch/arm64/kvm/handle_exit.c +22 -2
@@ -63,6 +63,8 @@ static int handle_hvc(struct kvm_vcpu *vcpu)static int handle_smc (struct kvm_vcpu *vcpu) { +int ret; +/* * "If an SMC instruction executed at Non-secure EL1 is * trapped to EL2 because HCR_EL2.TSC is 1, the exception is a
@@ -70,10 +72,28 @@ static int handle_smc(struct kvm_vcpu *vcpu)* * We need to advance the PC after the trap, as it would * otherwise return to the same address... +* +* If imm is non-zero, it's not defined, so just skip it. +*/ +if (kvm_vcpu_hvc_get_imm (vcpu)) { +vcpu_set_reg (vcpu,0 , ~0UL ); +kvm_incr_pc (vcpu); +return 1 ; + } + +/* +* If imm is zero, it's a psci call. +* Note that on ARMv8.3, even if EL3 is not implemented, SMC executed +* at Non-secure EL1 is trapped to EL2 if HCR_EL2.TSC==1, rather than +* being treated as UNDEFINED. */ -vcpu_set_reg (vcpu,0 , ~0UL ); + ret =kvm_hvc_call_handler (vcpu); +if (ret <0 ) +vcpu_set_reg (vcpu,0 , ~0UL ); +kvm_incr_pc (vcpu); -return 1 ; + +return ret; }/*
Signature · 1 line
2.34.1
Re: [PATCH 12/18] KVM: arm64: nv: Handle PSCI call via smc from the guest
Hi Marc,
Quoted 8 lines · Marc Zyngier
From: Jintack Lim <[email protected]>
VMs used to execute hvc #0 for the psci call if EL3 is not implemented. However, when we come to provide the virtual EL2 mode to the VM, the host OS inside the VM calls
kvm_call_hyp()which is also hvc #0. So, it's hard to differentiate between them from the host hypervisor's point of view.
Quoted 5 lines
So, let the VM execute smc instruction for the psci call. On ARMv8.3, even if EL3 is not implemented, a smc instruction executed at non-secure EL1 is trapped to EL2 if HCR_EL2.TSC==1, rather than being treated as UNDEFINED. So, the host hypervisor can handle this psci call without any confusion.
I think this commit message is rather stale to the point of being rather misleading. This lets the vEL2 get at the entire gamut of SMCCC calls we have in KVM, not just PSCI.
Of course, no problem with that since it is a requirement, but for posterity the commit message should reflect the current state of KVM.
If I may suggest:
Non-nested guests have used the hvc instruction to initiate SMCCC calls into KVM. This is quite a poor fit for NV as hvc exceptions are always taken to EL2. In other words, KVM needs to unconditionally forward the hvc exception back into vEL2 to uphold the architecture.
Instead, treat the smc instruction from vEL2 as we would a guest hypercall, thereby allowing the vEL2 to interact with KVM's hypercall surface. Note that on NV-capable hardware HCR_EL2.TSC causes smc instructions executed in non-secure EL1 to trap to EL2, even if EL3 is not implemented.
Quoted 34 lines
Reviewed-by Alexandru Elisei <[email protected]>Signed-off-by Jintack Lim <[email protected]>Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/kvm/handle_exit.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)arch/arm64/kvm/handle_exit.c +12
-0@@ -63,6 +63,8 @@ static int handle_hvc(struct kvm_vcpu *vcpu)static int handle_smc (struct kvm_vcpu *vcpu) { +int ret; +/* * "If an SMC instruction executed at Non-secure EL1 is * trapped to EL2 because HCR_EL2.TSC is 1, the exception is a @@ -70,10 +72,28 @@ static int handle_smc(struct kvm_vcpu *vcpu)* * We need to advance the PC after the trap, as it would * otherwise return to the same address... +* +* If imm is non-zero, it's not defined, so just skip it. +*/ +if (kvm_vcpu_hvc_get_imm (vcpu)) { +vcpu_set_reg (vcpu,0 , ~0UL ); +kvm_incr_pc (vcpu); +return 1 ; + } + +/*
arch/arm64/kvm/handle_exit.c static int handle_smc(struct kvm_vcpu *vcpu) · line 93
Quoted 10 lines
arch/arm64/kvm/handle_exit.c +8
-1+* If imm is zero, it's a psci call. +* Note that on ARMv8.3, even if EL3 is not implemented, SMC executed +* at Non-secure EL1 is trapped to EL2 if HCR_EL2.TSC==1, rather than +* being treated as UNDEFINED. */ -vcpu_set_reg (vcpu,0 , ~0UL ); + ret =kvm_hvc_call_handler (vcpu); +if (ret <0 ) +vcpu_set_reg (vcpu,0 , ~0UL ); +
This also has the subtle effect of allowing smc instructions from a non-nested guest to hit our hypercall surface too. I think we should avoid this and only handle smcs that actually come from a vEL2. What do you think about the following?
I can squash in all of the changes I've asked for here.
arch/arm64/kvm/handle_exit.c +5 -3
@@ -73,16 +73,18 @@ static int handle_smc(struct kvm_vcpu *vcpu)* We need to advance the PC after the trap, as it would * otherwise return to the same address... * -* If imm is non-zero, it's not defined, so just skip it. +* Only handle SMCs from the virtual EL2 with an immediate of zero and +* skip it otherwise. */ -if (kvm_vcpu_hvc_get_imm (vcpu)) { +if (!vcpu_is_el2 (vcpu) ||kvm_vcpu_hvc_get_imm (vcpu)) {vcpu_set_reg (vcpu,0 , ~0UL );kvm_incr_pc (vcpu);return 1 ; }/* -* If imm is zero, it's a psci call. +* If imm is zero then it is likely an SMCCC call. +* * Note that on ARMv8.3, even if EL3 is not implemented, SMC executed * at Non-secure EL1 is trapped to EL2 if HCR_EL2.TSC==1, rather than * being treated as UNDEFINED.
Signature · 2 lines
Thanks, Oliver
Marc Zyngier
maintainerRe: [PATCH 12/18] KVM: arm64: nv: Handle PSCI call via smc from the guest
Quoted 32 lines · Oliver Upton
Hi Marc,
Quoted 8 lines · Marc Zyngier
From: Jintack Lim <[email protected]>
VMs used to execute hvc #0 for the psci call if EL3 is not implemented. However, when we come to provide the virtual EL2 mode to the VM, the host OS inside the VM calls
kvm_call_hyp()which is also hvc #0. So, it's hard to differentiate between them from the host hypervisor's point of view.Quoted 5 lines
So, let the VM execute smc instruction for the psci call. On ARMv8.3, even if EL3 is not implemented, a smc instruction executed at non-secure EL1 is trapped to EL2 if HCR_EL2.TSC==1, rather than being treated as UNDEFINED. So, the host hypervisor can handle this psci call without any confusion.
I think this commit message is rather stale to the point of being rather misleading. This lets the vEL2 get at the entire gamut of SMCCC calls we have in KVM, not just PSCI.
Of course, no problem with that since it is a requirement, but for posterity the commit message should reflect the current state of KVM.
If I may suggest:
Non-nested guests have used the hvc instruction to initiate SMCCC calls into KVM. This is quite a poor fit for NV as hvc exceptions are always taken to EL2. In other words, KVM needs to unconditionally forward the hvc exception back into vEL2 to uphold the architecture.
Quoted 5 lines
Instead, treat the smc instruction from vEL2 as we would a guest hypercall, thereby allowing the vEL2 to interact with KVM's hypercall surface. Note that on NV-capable hardware HCR_EL2.TSC causes smc instructions executed in non-secure EL1 to trap to EL2, even if EL3 is not implemented.
Very nice!
Quoted 40 lines
Quoted 40 lines
Reviewed-by Alexandru Elisei <[email protected]>Signed-off-by Jintack Lim <[email protected]>Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/kvm/handle_exit.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)arch/arm64/kvm/handle_exit.c +16
-0@@ -63,6 +63,8 @@ static int handle_hvc(struct kvm_vcpu *vcpu)static int handle_smc (struct kvm_vcpu *vcpu) { +int ret; +/* * "If an SMC instruction executed at Non-secure EL1 is * trapped to EL2 because HCR_EL2.TSC is 1, the exception is a @@ -70,10 +72,28 @@ static int handle_smc(struct kvm_vcpu *vcpu)* * We need to advance the PC after the trap, as it would * otherwise return to the same address... +* +* If imm is non-zero, it's not defined, so just skip it. +*/ +if (kvm_vcpu_hvc_get_imm (vcpu)) { +vcpu_set_reg (vcpu,0 , ~0UL ); +kvm_incr_pc (vcpu); +return 1 ; + } + +/* +* If imm is zero, it's a psci call. +* Note that on ARMv8.3, even if EL3 is not implemented, SMC executed +* at Non-secure EL1 is trapped to EL2 if HCR_EL2.TSC==1, rather than +* being treated as UNDEFINED. */
Quoted 8 lines
arch/arm64/kvm/handle_exit.c static int handle_smc(struct kvm_vcpu *vcpu) · line 93
Quoted 5 lines
arch/arm64/kvm/handle_exit.c +4
-1-vcpu_set_reg (vcpu,0 , ~0UL ); + ret =kvm_hvc_call_handler (vcpu); +if (ret <0 ) +vcpu_set_reg (vcpu,0 , ~0UL ); +This also has the subtle effect of allowing smc instructions from a non-nested guest to hit our hypercall surface too.
I think we'll have to eventually allow that (see the TRNG spec which we blatantly deviate from by requiring an HVC), but we don't have to cross that bridge just yet.
Quoted 22 lines
I think we should avoid this and only handle smcs that actually come from a vEL2. What do you think about the following?
I can squash in all of the changes I've asked for here.
arch/arm64/kvm/handle_exit.c +3
-2@@ -73,16 +73,18 @@ static int handle_smc(struct kvm_vcpu *vcpu)* We need to advance the PC after the trap, as it would * otherwise return to the same address... * -* If imm is non-zero, it's not defined, so just skip it. +* Only handle SMCs from the virtual EL2 with an immediate of zero and +* skip it otherwise. */ -if (kvm_vcpu_hvc_get_imm (vcpu)) { +if (!vcpu_is_el2 (vcpu) ||kvm_vcpu_hvc_get_imm (vcpu)) {vcpu_set_reg (vcpu,0 , ~0UL );kvm_incr_pc (vcpu);
arch/arm64/kvm/handle_exit.c static int handle_smc(struct kvm_vcpu *vcpu) · line 90
Quoted 10 lines
arch/arm64/kvm/handle_exit.c +2
-1return 1 ; }/* -* If imm is zero, it's a psci call. +* If imm is zero then it is likely an SMCCC call. +* * Note that on ARMv8.3, even if EL3 is not implemented, SMC executed * at Non-secure EL1 is trapped to EL2 if HCR_EL2.TSC==1, rather than * being treated as UNDEFINED.
Looks good to me. Thanks for the suggestions!
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Re: [PATCH 12/18] KVM: arm64: nv: Handle PSCI call via smc from the guest
Quoted 1 line · Marc Zyngier
On Sat, 11 Feb 2023 10:07:41 +0000, Oliver Upton <[email protected]> wrote:
[...]
Quoted 6 lines
Quoted 2 lines
This also has the subtle effect of allowing smc instructions from a non-nested guest to hit our hypercall surface too.
I think we'll have to eventually allow that (see the TRNG spec which we blatantly deviate from by requiring an HVC), but we don't have to cross that bridge just yet.
Perhaps I'll continue to bury my head in the sand and act like you didn't say that :)
I seem to recall that the SMCCC suggests either the SMC or HVC instruction could be used if both EL2 and EL3 are implemented. So we've messed that up too.
My only worry is if we open up the use of SMCs and userspace does something silly in ACPI/DT and unconditionally picks SMCs over HVCs. The VM won't get far on pre-NV hardware w/o EL3...
We could always just hide the presence of EL3 for non-NV guests :)
Signature · 2 lines
Thanks, Oliver
Marc Zyngier
maintainerPATCH 13/18 KVM: arm64: nv: Add accessors for SPSR_EL1, ELR_EL1 and VBAR_EL1 from virtual EL2
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
For the same reason we trap virtual memory register accesses at virtual EL2, we need to trap SPSR_EL1, ELR_EL1 and VBAR_EL1 accesses. ARM v8.3 introduces the HCR_EL2.NV1 bit to be able to trap on those register accesses in EL1. Do not set this bit until the whole nesting support is completed, which happens further down the line...
--- arch/arm64/kvm/sys_regs.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-)
arch/arm64/kvm/sys_regs.c +28 -1
@@ -1538,6 +1538,30 @@ static bool access_sp_el1(struct kvm_vcpu *vcpu,return true; } +static bool access_elr (struct kvm_vcpu *vcpu, +struct sys_reg_params *p, +const struct sys_reg_desc *r) +{ +if (p->is_write) +vcpu_write_sys_reg (vcpu, p->regval,ELR_EL1 ); +else + p->regval =vcpu_read_sys_reg (vcpu,ELR_EL1 ); + +return true; +} + +static bool access_spsr (struct kvm_vcpu *vcpu, +struct sys_reg_params *p, +const struct sys_reg_desc *r) +{ +if (p->is_write) +__vcpu_sys_reg (vcpu,SPSR_EL1 ) = p->regval; +else + p->regval =__vcpu_sys_reg (vcpu,SPSR_EL1 ); + +return true; +} +/* * Architected system registers. * Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
@@ -1694,6 +1718,9 @@ static const struct sys_reg_desc sys_reg_descs[] = {PTRAUTH_KEY (APDB ),PTRAUTH_KEY (APGA ), + { SYS_DESC(SYS_SPSR_EL1), access_spsr}, + { SYS_DESC(SYS_ELR_EL1), access_elr}, + { SYS_DESC(SYS_AFSR0_EL1), access_vm_reg, reset_unknown, AFSR0_EL1 }, { SYS_DESC(SYS_AFSR1_EL1), access_vm_reg, reset_unknown, AFSR1_EL1 }, { SYS_DESC(SYS_ESR_EL1), access_vm_reg, reset_unknown, ESR_EL1 },
@@ -1741,7 +1768,7 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_LORC_EL1), trap_loregion }, { SYS_DESC(SYS_LORID_EL1), trap_loregion }, - { SYS_DESC(SYS_VBAR_EL1), NULL, reset_val, VBAR_EL1, 0 }, + { SYS_DESC(SYS_VBAR_EL1), access_rw, reset_val, VBAR_EL1, 0 }, { SYS_DESC(SYS_DISR_EL1), NULL, reset_val, DISR_EL1, 0 }, { SYS_DESC(SYS_ICC_IAR0_EL1), write_to_read_only },
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 14/18 KVM: arm64: nv: Emulate PSTATE.M for a guest hypervisor
In reply to Marc ZyngierWe can no longer blindly copy the VCPU's PSTATE into SPSR_EL2 and return to the guest and vice versa when taking an exception to the hypervisor, because we emulate virtual EL2 in EL1 and therefore have to translate the mode field from EL2 to EL1 and vice versa.
This requires keeping track of the state we enter the guest, for which we transiently use a dedicated flag.
--- arch/arm64/include/asm/kvm_host.h | 2 ++ arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h | 19 ++++++++++++++++- arch/arm64/kvm/hyp/vhe/switch.c | 24 ++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kvm_host.h +2 -0
@@ -645,6 +645,8 @@ struct kvm_vcpu_arch {#define DEBUG_STATE_SAVE_SPE __vcpu_single_flag(iflags, BIT(5))/* Save TRBE context if active */ #define DEBUG_STATE_SAVE_TRBE __vcpu_single_flag(iflags, BIT(6)) +/* vcpu running in HYP context */ +#define VCPU_HYP_CONTEXT __vcpu_single_flag(iflags, BIT(7))/* SVE enabled for host EL0 */ #define HOST_SVE_ENABLED __vcpu_single_flag(sflags, BIT(0))
arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h +18 -1
@@ -156,9 +156,26 @@ static inline void __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)write_sysreg_el1 (ctxt_sys_reg (ctxt,SPSR_EL1 ),SYS_SPSR ); } +/* Read the VCPU state's PSTATE, but translate (v)EL2 to EL1. */ +static inline u64 to_hw_pstate (const struct kvm_cpu_context *ctxt) +{ +u64 mode = ctxt->regs.pstate & (PSR_MODE_MASK |PSR_MODE32_BIT ); + +switch (mode) { +case PSR_MODE_EL2t: + mode = PSR_MODE_EL1t; +break ; +case PSR_MODE_EL2h: + mode = PSR_MODE_EL1h; +break ; + } + +return (ctxt->regs.pstate & ~(PSR_MODE_MASK |PSR_MODE32_BIT )) | mode; +} +static inline void __sysreg_restore_el2_return_state (struct kvm_cpu_context *ctxt) { -u64 pstate = ctxt->regs.pstate; +u64 pstate =to_hw_pstate (ctxt);u64 mode = pstate &PSR_AA32_MODE_MASK ;/*
arch/arm64/kvm/hyp/vhe/switch.c +24 -0
@@ -120,6 +120,25 @@ static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu)static void early_exit_filter (struct kvm_vcpu *vcpu,u64 *exit_code) { +/* +* If we were in HYP context on entry, adjust the PSTATE view +* so that the usual helpers work correctly. +*/ +if (unlikely (vcpu_get_flag (vcpu,VCPU_HYP_CONTEXT ))) { +u64 mode = *vcpu_cpsr (vcpu) & (PSR_MODE_MASK |PSR_MODE32_BIT ); + +switch (mode) { +case PSR_MODE_EL1t: + mode = PSR_MODE_EL2t; +break ; +case PSR_MODE_EL1h: + mode = PSR_MODE_EL2h; +break ; + } + + *vcpu_cpsr (vcpu) &= ~(PSR_MODE_MASK |PSR_MODE32_BIT ); + *vcpu_cpsr (vcpu) |= mode; + } }/* Switch to the guest for VHE systems running in EL2 */
@@ -154,6 +173,11 @@ static int __kvm_vcpu_run_vhe(struct kvm_vcpu *vcpu)sysreg_restore_guest_state_vhe (guest_ctxt);__debug_switch_to_guest (vcpu); +if (is_hyp_ctxt (vcpu)) +vcpu_set_flag (vcpu,VCPU_HYP_CONTEXT ); +else +vcpu_clear_flag (vcpu,VCPU_HYP_CONTEXT ); + do { /* Jump in the fire! */ exit_code = __guest_enter(vcpu);
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 15/18 KVM: arm64: nv: Allow a sysreg to be hidden from userspace only
In reply to Marc ZyngierSo far, we never needed to distinguish between registers hidden from userspace and being hidden from a guest (they are always either visible to both, or hidden from both).
With NV, we have the ugly case of the EL02 and EL12 registers, which are only a view on the EL0 and EL1 registers. It makes absolutely no sense to expose them to userspace, since it already has the canonical view.
Add a new visibility flag (REG_HIDDEN_USER) and a new helper that checks for it and REG_HIDDEN when checking whether to expose a sysreg to userspace. Subsequent patches will make use of it.
--- arch/arm64/kvm/sys_regs.c | 6 +++--- arch/arm64/kvm/sys_regs.h | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-)
arch/arm64/kvm/sys_regs.c +3 -3
@@ -2980,7 +2980,7 @@ int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,int ret; r =id_to_sys_reg_desc (vcpu, reg->id, table, num); -if (!r) +if (!r ||sysreg_hidden_user (vcpu, r))return -ENOENT ;if (r->get_user) {
@@ -3024,7 +3024,7 @@ int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,return -EFAULT ; r =id_to_sys_reg_desc (vcpu, reg->id, table, num); -if (!r) +if (!r ||sysreg_hidden_user (vcpu, r))return -ENOENT ;if (sysreg_user_write_ignore (vcpu, r))
@@ -3118,7 +3118,7 @@ static int walk_one_sys_reg(const struct kvm_vcpu *vcpu,if (!(rd->reg || rd->get_user))return 0 ; -if (sysreg_hidden (vcpu, rd)) +if (sysreg_hidden_user (vcpu, rd))return 0 ;if (!copy_reg_to_user (rd, uind))
arch/arm64/kvm/sys_regs.h +12 -2
@@ -85,8 +85,9 @@ struct sys_reg_desc { };#define REG_HIDDEN (1 << 0)/* hidden from userspace and guest */ -#define REG_RAZ (1 << 1)/* RAZ from userspace and guest */ -#define REG_USER_WI (1 << 2)/* WI from userspace only */ +#define REG_HIDDEN_USER (1 << 1)/* hidden from userspace only */ +#define REG_RAZ (1 << 2)/* RAZ from userspace and guest */ +#define REG_USER_WI (1 << 3)/* WI from userspace only */ static __printf(2, 3) inline void print_sys_reg_msg(const struct sys_reg_params *p,
@@ -152,6 +153,15 @@ static inline bool sysreg_hidden(const struct kvm_vcpu *vcpu,return sysreg_visibility (vcpu, r) ®_HIDDEN ; } +static inline bool sysreg_hidden_user (const struct kvm_vcpu *vcpu, +const struct sys_reg_desc *r) +{ +if (likely (!r->visibility)) +return false; + +return r->visibility (vcpu, r) & (REG_HIDDEN |REG_HIDDEN_USER ); +} +static inline bool sysreg_visible_as_raz (const struct kvm_vcpu *vcpu,const struct sys_reg_desc *r) {
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 16/18 KVM: arm64: nv: Emulate EL12 register accesses from the virtual EL2
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
With HCR_EL2.NV bit set, accesses to EL12 registers in the virtual EL2 trap to EL2. Handle those traps just like we do for EL1 registers.
One exception is CNTKCTL_EL12. We don't trap on CNTKCTL_EL1 for non-VHE virtual EL2 because we don't have to. However, accessing CNTKCTL_EL12 will trap since it's one of the EL12 registers controlled by HCR_EL2.NV bit. Therefore, add a handler for it and don't treat it as a non-trap-registers when preparing a shadow context.
These registers, being only a view on their EL1 counterpart, are permanently hidden from userspace.
--- arch/arm64/kvm/sys_regs.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
arch/arm64/kvm/sys_regs.c +37 -0
@@ -1482,6 +1482,26 @@ static unsigned int el2_visibility(const struct kvm_vcpu *vcpu, .val = v, \ } +/* +* EL{0,1}2 registers are the EL2 view on an EL0 or EL1 register when +* HCR_EL2.E2H==1, and only in the sysreg table for convenience of +* handling traps. Given that, they are always hidden from userspace. +*/ +static unsigned int elx2_visibility (const struct kvm_vcpu *vcpu, +const struct sys_reg_desc *rd) +{ +return REG_HIDDEN_USER ; +} + +#define EL12_REG (name, acc, rst, v) { \ + SYS_DESC(SYS_##name##_EL12), \ + .access = acc, \ + .reset = rst, \ + .reg = name##_EL1, \ + .val = v, \ + .visibility = elx2_visibility, \ +} +/* sys_reg_desc initialiser for known cpufeature ID registers */ #define ID_SANITISED (name) { \ SYS_DESC(SYS_##name), \
@@ -2031,6 +2051,23 @@ static const struct sys_reg_desc sys_reg_descs[] = {EL2_REG (CNTVOFF_EL2 , access_rw, reset_val,0 ),EL2_REG (CNTHCTL_EL2 , access_rw, reset_val,0 ), +EL12_REG (SCTLR , access_vm_reg, reset_val,0x00C50078 ), +EL12_REG (CPACR , access_rw, reset_val,0 ), +EL12_REG (TTBR0 , access_vm_reg, reset_unknown,0 ), +EL12_REG (TTBR1 , access_vm_reg, reset_unknown,0 ), +EL12_REG (TCR , access_vm_reg, reset_val,0 ), + { SYS_DESC(SYS_SPSR_EL12), access_spsr}, + { SYS_DESC(SYS_ELR_EL12), access_elr}, +EL12_REG (AFSR0 , access_vm_reg, reset_unknown,0 ), +EL12_REG (AFSR1 , access_vm_reg, reset_unknown,0 ), +EL12_REG (ESR , access_vm_reg, reset_unknown,0 ), +EL12_REG (FAR , access_vm_reg, reset_unknown,0 ), +EL12_REG (MAIR , access_vm_reg, reset_unknown,0 ), +EL12_REG (AMAIR , access_vm_reg, reset_amair_el1,0 ), +EL12_REG (VBAR , access_rw, reset_val,0 ), +EL12_REG (CONTEXTIDR , access_vm_reg, reset_val,0 ), +EL12_REG (CNTKCTL , access_rw, reset_val,0 ), + EL2_REG(SP_EL2, NULL, reset_unknown, 0), };
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 17/18 KVM: arm64: nv: Filter out unsupported features from ID regs
In reply to Marc ZyngierAs there is a number of features that we either can't support, or don't want to support right away with NV, let's add some basic filtering so that we don't advertize silly things to the EL2 guest.
Whilst we are at it, advertize FEAT_TTL as well as FEAT_GTG, which the NV implementation will implement.
--- arch/arm64/include/asm/kvm_nested.h | 6 ++ arch/arm64/kvm/Makefile | 2 +- arch/arm64/kvm/nested.c | 162 ++++++++++++++++++++++++++++ arch/arm64/kvm/sys_regs.c | 3 + 4 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/kvm/nested.c
arch/arm64/include/asm/kvm_nested.h +6 -0
@@ -11,4 +11,10 @@ static inline bool vcpu_has_nv(const struct kvm_vcpu *vcpu) test_bit(KVM_ARM_VCPU_HAS_EL2, vcpu->arch.features)); } +struct sys_reg_params ; +struct sys_reg_desc ; + +void access_nested_id_reg (struct kvm_vcpu *v,struct sys_reg_params *p, +const struct sys_reg_desc *r); +#endif /* __ARM64_KVM_NESTED_H */
arch/arm64/kvm/Makefile +1 -1
@@ -14,7 +14,7 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \ inject_fault.o va_layout.o handle_exit.o \ guest.o debug.o reset.o sys_regs.o stacktrace.o \ vgic-sys-reg-v3.o fpsimd.o pkvm.o \ - arch_timer.o trng.o vmid.o emulate-nested.o \ + arch_timer.o trng.o vmid.o emulate-nested.o nested.o \ vgic/vgic.o vgic/vgic-init.o \ vgic/vgic-irqfd.o vgic/vgic-v2.o \ vgic/vgic-v3.o vgic/vgic-v4.o \
arch/arm64/kvm/nested.c new file +162 -0
162 lines not on this page · raw message
arch/arm64/kvm/sys_regs.c +3 -0
@@ -1223,6 +1223,9 @@ static bool access_id_reg(struct kvm_vcpu *vcpu,return write_to_read_only (vcpu, p, r); p->regval =read_id_reg (vcpu, r); +if (vcpu_has_nv (vcpu)) +access_nested_id_reg (vcpu, p, r); +return true; }
Signature · 1 line
2.34.1
Marc Zyngier
maintainerPATCH 18/18 KVM: arm64: nv: Only toggle cache for virtual EL2 when SCTLR_EL2 changes
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
So far we were flushing almost the entire universe whenever a VM would load/unload the SCTLR_EL1 and the two versions of that register had different MMU enabled settings. This turned out to be so slow that it prevented forward progress for a nested VM, because a scheduler timer tick interrupt would always be pending when we reached the nested VM.
To avoid this problem, we consider the SCTLR_EL2 when evaluating if caches are on or off when entering virtual EL2 (because this is the value that we end up shadowing onto the hardware EL1 register).
--- arch/arm64/include/asm/kvm_mmu.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kvm_mmu.h +10 -1
@@ -115,6 +115,7 @@ alternative_cb_end#include <asm/cache.h> #include <asm/cacheflush.h> #include <asm/mmu_context.h> +#include <asm/kvm_emulate.h> #include <asm/kvm_host.h> void kvm_update_va_mask(struct alt_instr *alt,
@@ -192,7 +193,15 @@ struct kvm;static inline bool vcpu_has_cache_enabled (struct kvm_vcpu *vcpu) { -return (vcpu_read_sys_reg (vcpu,SCTLR_EL1 ) &0b101 ) ==0b101 ; +u64 cache_bits = SCTLR_ELx_M | SCTLR_ELx_C; +int reg; + +if (vcpu_is_el2 (vcpu)) + reg =SCTLR_EL2 ; +else + reg =SCTLR_EL1 ; + +return (vcpu_read_sys_reg (vcpu, reg) & cache_bits) == cache_bits; }static inline void __clean_dcache_guest_page (void *va,size_t size)
Signature · 1 line
2.34.1
Oliver Upton
AppliedQuoted 8 lines · Marc Zyngier
As a bunch of the NV patches have had a decent amount of review, and given that they do very little on their own, I've put together a prefix series that gets the most mundane stuff out of the way.
Of course, nothing is functional, but nothing gets used either. In a way, this is pretty similar to the current state of pKVM! ;-)
[...]
Applied to kvmarm/next, thanks!
[01/18] arm64: Add ARM64_HAS_NESTED_VIRT cpufeature
https://git.kernel.org/kvmarm/kvmarm/c/675cabc89900 [02/18] KVM: arm64: Use the S2 MMU context to iterate over S2 table https://git.kernel.org/kvmarm/kvmarm/c/8531bd63a8dc [03/18] KVM: arm64: nv: Introduce nested virtualization VCPU feature https://git.kernel.org/kvmarm/kvmarm/c/89b0e7de3451 [04/18] KVM: arm64: nv: Reset VCPU to EL2 registers if VCPU nested virt is set https://git.kernel.org/kvmarm/kvmarm/c/2fb32357ae67 [05/18] KVM: arm64: nv: Allow userspace to set PSR_MODE_EL2x https://git.kernel.org/kvmarm/kvmarm/c/1d05d51bac78 [06/18] KVM: arm64: nv: Add EL2 system registers to vcpu context https://git.kernel.org/kvmarm/kvmarm/c/5305cc2c3400 [07/18] KVM: arm64: nv: Add nested virt VCPU primitives for vEL2 VCPU state https://git.kernel.org/kvmarm/kvmarm/c/0043b29038e2 [08/18] KVM: arm64: nv: Handle HCR_EL2.NV system register traps https://git.kernel.org/kvmarm/kvmarm/c/6ff9dc238a53 [09/18] KVM: arm64: nv: Support virtual EL2 exceptions https://git.kernel.org/kvmarm/kvmarm/c/47f3a2fc765a [10/18] KVM: arm64: nv: Inject HVC exceptions to the virtual EL2 https://git.kernel.org/kvmarm/kvmarm/c/93c33702cd2b [11/18] KVM: arm64: nv: Handle trapped ERET from virtual EL2 https://git.kernel.org/kvmarm/kvmarm/c/6898a55ce38c [12/18] KVM: arm64: nv: Handle PSCI call via smc from the guest https://git.kernel.org/kvmarm/kvmarm/c/bd36b1a9eb5a [13/18] KVM: arm64: nv: Add accessors for SPSR_EL1, ELR_EL1 and VBAR_EL1 from virtual EL2 https://git.kernel.org/kvmarm/kvmarm/c/9da117eec924 [14/18] KVM: arm64: nv: Emulate PSTATE.M for a guest hypervisor https://git.kernel.org/kvmarm/kvmarm/c/d9552fe133f9 [15/18] KVM: arm64: nv: Allow a sysreg to be hidden from userspace only https://git.kernel.org/kvmarm/kvmarm/c/e6b367db0f91 [16/18] KVM: arm64: nv: Emulate EL12 register accesses from the virtual EL2 https://git.kernel.org/kvmarm/kvmarm/c/280b748e871e [17/18] KVM: arm64: nv: Filter out unsupported features from ID regs https://git.kernel.org/kvmarm/kvmarm/c/9f75b6d447d7 [18/18] KVM: arm64: nv: Only toggle cache for virtual EL2 when SCTLR_EL2 changes https://git.kernel.org/kvmarm/kvmarm/c/191e0e155521
--
Best,
Oliver