KVM: arm64: ARMv8.3/8.4 Nested Virtualization support
Subsystems (5)
Marc Zyngier
maintainerv2 cover KVM: arm64: ARMv8.3/8.4 Nested Virtualization support
This is a major rework of the NV series that I posted over 6 months ago[1], and a lot has changed since then:
- Early ARMv8.4-NV support
- ARMv8.4-TTL support in host and guest
- ARMv8.5-GTG support in host and guest
- Lots of comments addressed after the review
- Rebased on v5.6-rc1
- Way too many patches
In my defence, the whole of the NV code is still smaller that the 32bit KVM/arm code I'm about to remove, so I feel less bad inflicting this on everyone! ;-)
From a functionality perspective, you can expect a L2 guest to work, but don't even think of L3, as we only partially emulate the ARMv8.{3,4}-NV extensions themselves. Same thing for vgic, debug, PMU, as well as anything that would require a Stage-1 PTW. What we want to achieve is that with NV disabled, there is no performance overhead and no regression.
The series is roughly divided in 5 parts: exception handling, memory virtualization, interrupts and timers for ARMv8.3, followed by the ARMv8.4 support. There are of course some dependencies, but you'll hopefully get the gist of it.
For the most courageous of you, I've put out a branch[2]. Of course, you'll need some userspace. Andre maintains a hacked version of kvmtool[3] that takes a --nested option, allowing the guest to be started at EL2. You can run the whole stack in the Foundation model. Don't be in a hurry ;-).
[1] https://lore.kernel.org/r/[email protected]
[2] git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms.git kvm-arm64/nv-5.6-rc1
[3] git://linux-arm.org/kvmtool.git nv/nv-wip-5.2-rc5
Andre Przywara (3):
KVM: arm64: nv: Save/Restore vEL2 sysregs
KVM: arm64: nv: Handle traps for timer _EL02 and _EL2 sysregs
accessors
KVM: arm64: nv: vgic: Allow userland to set VGIC maintenance IRQ
Christoffer Dall (17):
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: Emulate PSTATE.M for a guest hypervisor
KVM: arm64: nv: Trap EL1 VM register accesses in virtual EL2
KVM: arm64: nv: Only toggle cache for virtual EL2 when SCTLR_EL2
changes
KVM: arm/arm64: nv: Factor out stage 2 page table data from struct kvm
KVM: arm64: nv: Implement nested Stage-2 page table walk logic
KVM: arm64: nv: Handle shadow stage 2 page faults
KVM: arm64: nv: Unmap/flush shadow stage 2 page tables
KVM: arm64: nv: arch_timer: Support hyp timer emulation
KVM: arm64: nv: vgic-v3: Take cpu_if pointer directly instead of vcpu
KVM: arm64: nv: vgic: Emulate the HW bit in software
KVM: arm64: nv: Add nested GICv3 tracepoints
KVM: arm64: nv: Sync nested timer state with ARMv8.4
Jintack Lim (19):
arm64: Add ARM64_HAS_NESTED_VIRT cpufeature
KVM: arm64: nv: Add EL2 system registers to vcpu context
KVM: arm64: nv: Support virtual EL2 exceptions
KVM: arm64: nv: Inject HVC exceptions to the virtual EL2
KVM: arm64: nv: Trap SPSR_EL1, ELR_EL1 and VBAR_EL1 from virtual EL2
KVM: arm64: nv: Trap CPACR_EL1 access in virtual EL2
KVM: arm64: nv: Handle PSCI call via smc from the guest
KVM: arm64: nv: Respect virtual HCR_EL2.TWX setting
KVM: arm64: nv: Respect virtual CPTR_EL2.{TFP,FPEN} settings
KVM: arm64: nv: Respect the virtual HCR_EL2.NV bit setting
KVM: arm64: nv: Respect virtual HCR_EL2.TVM and TRVM settings
KVM: arm64: nv: Respect the virtual HCR_EL2.NV1 bit setting
KVM: arm64: nv: Emulate EL12 register accesses from the virtual EL2
KVM: arm64: nv: Configure HCR_EL2 for nested virtualization
KVM: arm64: nv: Introduce sys_reg_desc.forward_trap
KVM: arm64: nv: Set a handler for the system instruction traps
KVM: arm64: nv: Trap and emulate AT instructions from virtual EL2
KVM: arm64: nv: Trap and emulate TLBI instructions from virtual EL2
KVM: arm64: nv: Nested GICv3 Support
Marc Zyngier (55):
KVM: arm64: Move __load_guest_stage2 to kvm_mmu.h
KVM: arm64: nv: Reset VMPIDR_EL2 and VPIDR_EL2 to sane values
KVM: arm64: nv: Add EL2->EL1 translation helpers
KVM: arm64: nv: Refactor vcpu_{read,write}_sys_reg
KVM: arm64: nv: Handle virtual EL2 registers in
vcpu_read/write_sys_reg()
KVM: arm64: nv: Handle SPSR_EL2 specially
KVM: arm64: nv: Handle HCR_EL2.E2H specially
KVM: arm64: nv: Forward debug traps to the nested guest
KVM: arm64: nv: Filter out unsupported features from ID regs
KVM: arm64: nv: Hide RAS from nested guests
KVM: arm64: nv: Use ARMv8.5-GTG to advertise supported Stage-2 page
sizes
KVM: arm64: Check advertised Stage-2 page size capability
KVM: arm64: nv: Support multiple nested Stage-2 mmu structures
KVM: arm64: nv: Move last_vcpu_ran to be per s2 mmu
KVM: arm64: nv: Fold guest's HCR_EL2 configuration into the host's
KVM: arm64: nv: Propagate CNTVOFF_EL2 to the virtual EL1 timer
KVM: arm64: nv: Load timer before the GIC
KVM: arm64: nv: Implement maintenance interrupt forwarding
arm64: KVM: nv: Add handling of EL2-specific timer registers
arm64: KVM: nv: Honor SCTLR_EL2.SPAN on entering vEL2
arm64: KVM: nv: Handle SCTLR_EL2 RES0/RES1 bits
arm64: KVM: nv: Restrict S2 RD/WR permissions to match the guest's
arm64: KVM: nv: Allow userspace to request KVM_ARM_VCPU_NESTED_VIRT
arm64: Detect the ARMv8.4 TTL feature
arm64: KVM: nv: Add handling of ARMv8.4-TTL TLB invalidation
arm64: KVM: nv: Invalidate TLBs based on shadow S2 TTL-like
information
arm64: KVM: nv: Tag shadow S2 entries with nested level
arm64: Add SW reserved PTE/PMD bits
arm64: Add level-hinted TLB invalidation helper
arm64: KVM: Add a level hint to __kvm_tlb_flush_vmid_ipa
arm64: KVM: Use TTL hint in when invalidating stage-2 translations
arm64: KVM: nv: Add include containing the VNCR_EL2 offsets
KVM: arm64: Introduce accessor for ctxt->sys_reg
KVM: arm64: sysreg: Use ctxt_sys_reg() instead of raw sys_regs access
KVM: arm64: sve: Use __vcpu_sys_reg() instead of raw sys_regs access
KVM: arm64: pauth: Use ctxt_sys_reg() instead of raw sys_regs access
KVM: arm64: debug: Use ctxt_sys_reg() instead of raw sys_regs access
KVM: arm64: Add missing reset handlers for PMU emulation
KVM: arm64: nv: Move sysreg reset check to boot time
KVM: arm64: Map VNCR-capable registers to a separate page
KVM: arm64: nv: Move nested vgic state into the sysreg file
KVM: arm64: Use accessors for timer ctl/cval/offset
KVM: arm64: Add VNCR-capable timer accessors for arm64
KVM: arm64: Make struct kvm_regs userspace-only
KVM: arm64: VNCR-ize ELR_EL1
KVM: arm64: VNCR-ize SP_EL1
KVM: arm64: Disintegrate SPSR array
KVM: arm64: aarch32: Use __vcpu_sys_reg() instead of raw sys_regs
access
KVM: arm64: VNCR-ize SPSR_EL1
KVM: arm64: Add ARMv8.4 Enhanced Nested Virt cpufeature
KVM: arm64: nv: Synchronize PSTATE early on exit
KVM: arm64: nv: Allocate VNCR page when required
KVM: arm64: nv: Enable ARMv8.4-NV support
KVM: arm64: nv: Fast-track 'InHost' exception returns
KVM: arm64: nv: Fast-track EL1 TLBIs for VHE guests
.../admin-guide/kernel-parameters.txt | 4 + .../virt/kvm/devices/arm-vgic-v3.txt | 8 + arch/arm/include/asm/kvm_asm.h | 6 +- arch/arm/include/asm/kvm_emulate.h | 3 + arch/arm/include/asm/kvm_host.h | 32 +- arch/arm/include/asm/kvm_hyp.h | 12 +- arch/arm/include/asm/kvm_mmu.h | 90 +- arch/arm/include/asm/kvm_nested.h | 11 + arch/arm/include/asm/stage2_pgtable.h | 9 + arch/arm/include/uapi/asm/kvm.h | 1 + arch/arm/kvm/hyp/switch.c | 11 +- arch/arm/kvm/hyp/tlb.c | 18 +- arch/arm64/include/asm/cpucaps.h | 5 +- arch/arm64/include/asm/esr.h | 6 + arch/arm64/include/asm/kvm_arm.h | 28 +- arch/arm64/include/asm/kvm_asm.h | 10 +- arch/arm64/include/asm/kvm_coproc.h | 2 +- arch/arm64/include/asm/kvm_emulate.h | 181 +- arch/arm64/include/asm/kvm_host.h | 212 ++- arch/arm64/include/asm/kvm_hyp.h | 32 +- arch/arm64/include/asm/kvm_mmu.h | 62 +- arch/arm64/include/asm/kvm_nested.h | 94 + arch/arm64/include/asm/pgtable-hwdef.h | 2 + arch/arm64/include/asm/stage2_pgtable.h | 9 + arch/arm64/include/asm/sysreg.h | 126 +- arch/arm64/include/asm/tlbflush.h | 30 + arch/arm64/include/asm/vncr_mapping.h | 73 + arch/arm64/include/uapi/asm/kvm.h | 2 + arch/arm64/kernel/asm-offsets.c | 3 +- arch/arm64/kernel/cpufeature.c | 55 + arch/arm64/kvm/Makefile | 5 + arch/arm64/kvm/emulate-nested.c | 205 +++ arch/arm64/kvm/fpsimd.c | 6 +- arch/arm64/kvm/guest.c | 85 +- arch/arm64/kvm/handle_exit.c | 98 +- arch/arm64/kvm/hyp/Makefile | 1 + arch/arm64/kvm/hyp/at.c | 231 +++ arch/arm64/kvm/hyp/debug-sr.c | 18 +- arch/arm64/kvm/hyp/entry.S | 3 +- arch/arm64/kvm/hyp/switch.c | 241 ++- arch/arm64/kvm/hyp/sysreg-sr.c | 338 +++- arch/arm64/kvm/hyp/tlb.c | 134 +- arch/arm64/kvm/inject_fault.c | 12 - arch/arm64/kvm/nested.c | 899 ++++++++++ arch/arm64/kvm/regmap.c | 37 +- arch/arm64/kvm/reset.c | 72 +- arch/arm64/kvm/sys_regs.c | 1523 +++++++++++++++-- arch/arm64/kvm/sys_regs.h | 6 + arch/arm64/kvm/trace.h | 56 + include/kvm/arm_arch_timer.h | 9 +- include/kvm/arm_vgic.h | 21 +- virt/kvm/arm/arch_timer.c | 271 ++- virt/kvm/arm/arch_timer_nested.c | 95 + virt/kvm/arm/arm.c | 72 +- virt/kvm/arm/hyp/vgic-v3-sr.c | 35 +- virt/kvm/arm/mmio.c | 14 +- virt/kvm/arm/mmu.c | 458 +++-- virt/kvm/arm/trace.h | 6 +- virt/kvm/arm/vgic/vgic-init.c | 30 + virt/kvm/arm/vgic/vgic-kvm-device.c | 22 + virt/kvm/arm/vgic/vgic-nested-trace.h | 137 ++ virt/kvm/arm/vgic/vgic-v2.c | 10 +- virt/kvm/arm/vgic/vgic-v3-nested.c | 240 +++ virt/kvm/arm/vgic/vgic-v3.c | 51 +- virt/kvm/arm/vgic/vgic.c | 74 +- virt/kvm/arm/vgic/vgic.h | 10 + 66 files changed, 5925 insertions(+), 737 deletions(-) create mode 100644 arch/arm/include/asm/kvm_nested.h create mode 100644 arch/arm64/include/asm/kvm_nested.h create mode 100644 arch/arm64/include/asm/vncr_mapping.h create mode 100644 arch/arm64/kvm/emulate-nested.c create mode 100644 arch/arm64/kvm/hyp/at.c create mode 100644 arch/arm64/kvm/nested.c create mode 100644 virt/kvm/arm/arch_timer_nested.c create mode 100644 virt/kvm/arm/vgic/vgic-nested-trace.h create mode 100644 virt/kvm/arm/vgic/vgic-v3-nested.c
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 1/94 KVM: arm64: Move __load_guest_stage2 to kvm_mmu.h
Having __load_guest_stage2 in kvm_hyp.h is quickly going to trigger a circular include problem. In order to avoid this, let's move it to kvm_mmu.h, where it will be a better fit anyway.
In the process, drop the __hyp_text annotation, which doesn't help as the function is marked as __always_inline.
--- arch/arm64/include/asm/kvm_hyp.h | 18 ------------------ arch/arm64/include/asm/kvm_mmu.h | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 18 deletions(-)
arch/arm64/include/asm/kvm_hyp.h +0 -18
@@ -10,7 +10,6 @@#include <linux/compiler.h> #include <linux/kvm_host.h> #include <asm/alternative.h> -#include <asm/kvm_mmu.h> #include <asm/sysreg.h> #define __hyp_text __section(.hyp.text) notrace
@@ -81,22 +80,5 @@ void deactivate_traps_vhe_put(void);u64 __guest_enter (struct kvm_vcpu *vcpu,struct kvm_cpu_context *host_ctxt);void __noreturn __hyp_do_panic (unsigned long , ...); -/* -* Must be called from hyp code running at EL2 with an updated VTTBR -* and interrupts disabled. -*/ -static __always_inline void __hyp_text __load_guest_stage2(struct kvm *kvm) -{ -write_sysreg (kvm->arch.vtcr, vtcr_el2); -write_sysreg (kvm_get_vttbr (kvm), vttbr_el2); - -/* -* ARM errata 1165522 and 1530923 require the actual execution of the -* above before we can switch to the EL1/EL0 translation regime used by -* the guest. -*/ - asm(ALTERNATIVE("nop", "isb", ARM64_WORKAROUND_SPECULATIVE_AT_VHE)); -} -#endif /* __ARM64_KVM_HYP_H__ */
arch/arm64/include/asm/kvm_mmu.h +17 -0
@@ -604,5 +604,22 @@ static __always_inline u64 kvm_get_vttbr(struct kvm *kvm)return kvm_phys_to_vttbr (baddr) | vmid_field | cnp; } +/* +* Must be called from hyp code running at EL2 with an updated VTTBR +* and interrupts disabled. +*/ +static __always_inline void __load_guest_stage2(struct kvm *kvm) +{ +write_sysreg (kvm->arch.vtcr, vtcr_el2); +write_sysreg (kvm_get_vttbr (kvm), vttbr_el2); + +/* +* ARM erratum 1165522 requires the actual execution of the above +* before we can switch to the EL1/EL0 translation regime used by +* the guest. +*/ + asm(ALTERNATIVE("nop", "isb", ARM64_WORKAROUND_SPECULATIVE_AT_VHE)); +} +#endif /* __ASSEMBLY__ */ #endif /* __ARM64_KVM_MMU_H__ */
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 2/94 arm64: Add ARM64_HAS_NESTED_VIRT cpufeature
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
Add a new ARM64_HAS_NESTED_VIRT feature to indicate that the CPU has the ARMv8.3 nested virtualization capability.
This will be used to support nested virtualization in KVM.
--- .../admin-guide/kernel-parameters.txt | 4 +++ arch/arm64/include/asm/cpucaps.h | 3 ++- arch/arm64/include/asm/sysreg.h | 1 + arch/arm64/kernel/cpufeature.c | 26 +++++++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-)
Documentation/admin-guide/kernel-parameters.txt +4 -0
@@ -2151,6 +2151,10 @@ [KVM,ARM] Allow use of GICv4 for direct injection of LPIs. + kvm-arm.nested= + [KVM,ARM] Allow nested virtualization in KVM/ARM. + Default is 0 (disabled) + kvm-intel.ept= [KVM,Intel] Disable extended page tables (virtualized MMU) support on capable Intel chips. Default is 1 (enabled)
arch/arm64/include/asm/cpucaps.h +2 -1
@@ -58,7 +58,8 @@#define ARM64_WORKAROUND_SPECULATIVE_AT_NVHE 48#define ARM64_HAS_E0PD 49#define ARM64_HAS_RNG 50 +#define ARM64_HAS_NESTED_VIRT 51 -#define ARM64_NCAPS 51 +#define ARM64_NCAPS 52#endif /* __ASM_CPUCAPS_H */
arch/arm64/include/asm/sysreg.h +1 -0
@@ -687,6 +687,7 @@#define ID_AA64MMFR2_E0PD_SHIFT 60#define ID_AA64MMFR2_FWB_SHIFT 40#define ID_AA64MMFR2_AT_SHIFT 32 +#define ID_AA64MMFR2_NV_SHIFT 24#define ID_AA64MMFR2_LVA_SHIFT 16#define ID_AA64MMFR2_IESB_SHIFT 12#define ID_AA64MMFR2_LSM_SHIFT 8
arch/arm64/kernel/cpufeature.c +26 -0
@@ -243,6 +243,7 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr2[] = {ARM64_FTR_BITS (FTR_HIDDEN ,FTR_NONSTRICT ,FTR_LOWER_SAFE ,ID_AA64MMFR2_E0PD_SHIFT ,4 ,0 ),ARM64_FTR_BITS (FTR_HIDDEN ,FTR_STRICT ,FTR_LOWER_SAFE ,ID_AA64MMFR2_FWB_SHIFT ,4 ,0 ),ARM64_FTR_BITS (FTR_VISIBLE ,FTR_STRICT ,FTR_LOWER_SAFE ,ID_AA64MMFR2_AT_SHIFT ,4 ,0 ), +ARM64_FTR_BITS (FTR_HIDDEN ,FTR_STRICT ,FTR_LOWER_SAFE ,ID_AA64MMFR2_NV_SHIFT ,4 ,0 ),ARM64_FTR_BITS (FTR_HIDDEN ,FTR_STRICT ,FTR_LOWER_SAFE ,ID_AA64MMFR2_LVA_SHIFT ,4 ,0 ),ARM64_FTR_BITS (FTR_HIDDEN ,FTR_STRICT ,FTR_LOWER_SAFE ,ID_AA64MMFR2_IESB_SHIFT ,4 ,0 ), ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_LSM_SHIFT, 4, 0),
@@ -1241,6 +1242,21 @@ static void cpu_copy_el2regs(const struct arm64_cpu_capabilities *__unused)if (!alternative_is_applied (ARM64_HAS_VIRT_HOST_EXTN )) write_sysreg(read_sysreg(tpidr_el1), tpidr_el2); } + +static bool nested_param; +static bool has_nested_virt_support (const struct arm64_cpu_capabilities *cap, +int scope) +{ +return has_cpuid_feature (cap, scope) && + nested_param; +} + +static int __init kvmarm_nested_cfg (char *buf) +{ +return strtobool (buf, &nested_param); +} + +early_param ("kvm-arm.nested" , kvmarm_nested_cfg);#endif static void cpu_has_fwb (const struct arm64_cpu_capabilities *__unused)
@@ -1419,6 +1435,16 @@ 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_NV_SHIFT, + .min_field_value = 1, + }, #endif /* CONFIG_ARM64_VHE */ { .desc = "32-bit EL0 Support",
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 3/94 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 nested_virt_in_use() on systems without nested virt enabled should have neglgible overhead.
We don't yet allow userspace to actually set this feature.
--- arch/arm/include/asm/kvm_nested.h | 9 +++++++++ arch/arm64/include/asm/kvm_nested.h | 13 +++++++++++++ arch/arm64/include/uapi/asm/kvm.h | 1 + 3 files changed, 23 insertions(+) create mode 100644 arch/arm/include/asm/kvm_nested.h create mode 100644 arch/arm64/include/asm/kvm_nested.h
arch/arm/include/asm/kvm_nested.h new file +9 -0
@@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ARM_KVM_NESTED_H +#define __ARM_KVM_NESTED_H + +#include <linux/kvm_host.h> + +static inline bool nested_virt_in_use (const struct kvm_vcpu *vcpu) {return false; } + +#endif /* __ARM_KVM_NESTED_H */
arch/arm64/include/asm/kvm_nested.h new file +13 -0
@@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ARM64_KVM_NESTED_H +#define __ARM64_KVM_NESTED_H + +#include <linux/kvm_host.h> + +static inline bool nested_virt_in_use (const struct kvm_vcpu *vcpu) +{ +return cpus_have_const_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
@@ -106,6 +106,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 · 6 lines
2.20.1 _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Marc Zyngier
maintainerPATCH v2 4/94 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 | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-)
arch/arm64/kvm/reset.c +20 -12
@@ -28,6 +28,7 @@#include <asm/kvm_coproc.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 */
@@ -36,15 +37,14 @@ static u32 kvm_ipa_limit;/* * ARMv8 Reset Values */ -static const struct kvm_regs default_regs_reset = { - .regs.pstate = (PSR_MODE_EL1h |PSR_A_BIT |PSR_I_BIT | -PSR_F_BIT |PSR_D_BIT ), -}; +#define VCPU_RESET_PSTATE_EL1 (PSR_MODE_EL1h | PSR_A_BIT | PSR_I_BIT | \ + PSR_F_BIT | PSR_D_BIT) -static const struct kvm_regs default_regs_reset32 = { - .regs.pstate = (PSR_AA32_MODE_SVC |PSR_AA32_A_BIT | -PSR_AA32_I_BIT |PSR_AA32_F_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)static bool cpu_has_32bit_el1 (void ) {
@@ -260,6 +260,7 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)const struct kvm_regs *cpu_reset;int ret = -EINVAL ;bool loaded; +u32 pstate;/* Reset PMU outside of the non-preemptible section */ kvm_pmu_vcpu_reset (vcpu);
@@ -288,18 +289,25 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)switch (vcpu->arch.target) {default :if (test_bit (KVM_ARM_VCPU_EL1_32BIT , vcpu->arch.features)) { -if (!cpu_has_32bit_el1 ()) +/* +* The CPU must support 32bit EL1, and 32bit +* NV is just not a thing... +*/ +if (!cpu_has_32bit_el1 () ||nested_virt_in_use (vcpu)) goto out; - cpu_reset = &default_regs_reset32; + pstate =VCPU_RESET_PSTATE_SVC ; + }else if (nested_virt_in_use (vcpu)) { + pstate =VCPU_RESET_PSTATE_EL2 ; }else { - cpu_reset = &default_regs_reset; + pstate =VCPU_RESET_PSTATE_EL1 ; }break ; }/* Reset core registers */ -memcpy (vcpu_gp_regs (vcpu), cpu_reset,sizeof (*cpu_reset)); +memset (vcpu_gp_regs (vcpu),0 ,sizeof (*cpu_reset)); +vcpu_gp_regs (vcpu)->regs.pstate = pstate;/* Reset system registers */ kvm_reset_sys_regs (vcpu);
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 5/94 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
@@ -26,6 +26,7 @@#include <asm/kvm_emulate.h> #include <asm/kvm_coproc.h> #include <asm/kvm_host.h> +#include <asm/kvm_nested.h> #include <asm/sigcontext.h> #include "trace.h"
@@ -194,6 +195,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_el1_is_32bit (vcpu) || !nested_virt_in_use (vcpu)) +return -EINVAL ; +break ;default : err = -EINVAL ; goto out;
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 6/94 KVM: arm64: nv: Add EL2 system registers to vcpu context
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/kvm_host.h | 35 ++++++++++++- arch/arm64/include/asm/sysreg.h | 50 +++++++++++++++++- arch/arm64/kvm/sys_regs.c | 85 +++++++++++++++++++++++++++++-- 3 files changed, 163 insertions(+), 7 deletions(-)
arch/arm64/include/asm/kvm_host.h +34 -1
@@ -170,12 +170,45 @@ enum vcpu_sysreg {APGAKEYLO_EL1 ,APGAKEYHI_EL1 , -/* 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 sorted ascending by Op0, Op1, CRn, CRm, Op2 */ +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 ,/* Hypervisor IPA Fault Address Register */ +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 */ +RMR_EL2 ,/* Reset Management Register */ +CONTEXTIDR_EL2 ,/* Context ID Register (EL2) */ +TPIDR_EL2 ,/* EL2 Software Thread ID Register */ +CNTVOFF_EL2 ,/* Counter-timer Virtual Offset register */ +CNTHCTL_EL2 ,/* Counter-timer Hypervisor Control register */ +SP_EL2 ,/* EL2 Stack Pointer */ +NR_SYS_REGS /* Nothing after this line! */ };
arch/arm64/include/asm/sysreg.h +49 -1
95 lines not on this page · raw message
arch/arm64/kvm/sys_regs.c +80 -5
113 lines not on this page · raw message
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 7/94 KVM: arm64: nv: Reset VMPIDR_EL2 and VPIDR_EL2 to sane values
In reply to Marc ZyngierThe VMPIDR_EL2 and VPIDR_EL2 are architecturally UNKNOWN at reset, but let's be nice to a guest hypervisor behaving foolishly and reset these to something reasonable anyway.
--- arch/arm64/kvm/sys_regs.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-)
arch/arm64/kvm/sys_regs.c +21 -4
@@ -624,7 +624,7 @@ static void reset_amair_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)vcpu_write_sys_reg (vcpu, amair,AMAIR_EL1 ); } -static void reset_mpidr (struct kvm_vcpu *vcpu,const struct sys_reg_desc *r) +static u64 compute_reset_mpidr (struct kvm_vcpu *vcpu) {u64 mpidr;
@@ -638,7 +638,24 @@ static void reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) mpidr = (vcpu->vcpu_id &0x0f ) <<MPIDR_LEVEL_SHIFT (0 ); mpidr |= ((vcpu->vcpu_id >>4 ) &0xff ) <<MPIDR_LEVEL_SHIFT (1 ); mpidr |= ((vcpu->vcpu_id >>12 ) &0xff ) <<MPIDR_LEVEL_SHIFT (2 ); -vcpu_write_sys_reg (vcpu, (1ULL <<31 ) | mpidr,MPIDR_EL1 ); + mpidr |= (1ULL <<31 ); + +return mpidr; +} + +static void reset_mpidr (struct kvm_vcpu *vcpu,const struct sys_reg_desc *r) +{ +vcpu_write_sys_reg (vcpu,compute_reset_mpidr (vcpu),MPIDR_EL1 ); +} + +static void reset_vmpidr (struct kvm_vcpu *vcpu,const struct sys_reg_desc *r) +{ +vcpu_write_sys_reg (vcpu,compute_reset_mpidr (vcpu),VMPIDR_EL2 ); +} + +static void reset_vpidr (struct kvm_vcpu *vcpu,const struct sys_reg_desc *r) +{ +vcpu_write_sys_reg (vcpu,read_cpuid_id (),VPIDR_EL2 ); }static void reset_pmcr (struct kvm_vcpu *vcpu,const struct sys_reg_desc *r)
@@ -1672,8 +1689,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {*/ { SYS_DESC(SYS_PMCCFILTR_EL0), access_pmu_evtyper, reset_val, PMCCFILTR_EL0, 0 }, - { SYS_DESC(SYS_VPIDR_EL2), access_rw, reset_val, VPIDR_EL2, 0 }, - { SYS_DESC(SYS_VMPIDR_EL2), access_rw, reset_val, VMPIDR_EL2, 0 }, + { SYS_DESC(SYS_VPIDR_EL2), access_rw, reset_vpidr, VPIDR_EL2 }, + { SYS_DESC(SYS_VMPIDR_EL2), access_rw, reset_vmpidr, VMPIDR_EL2 }, { SYS_DESC(SYS_SCTLR_EL2), access_rw, reset_val, SCTLR_EL2, 0 }, { SYS_DESC(SYS_ACTLR_EL2), access_rw, reset_val, ACTLR_EL2, 0 },
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 8/94 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 | 55 ++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+)
arch/arm64/include/asm/kvm_emulate.h +55 -0
@@ -194,6 +194,61 @@ static inline void vcpu_set_reg(struct kvm_vcpu *vcpu, u8 reg_num,vcpu_gp_regs (vcpu)->regs.regs[reg_num] = val; } +static inline bool vcpu_mode_el2_ctxt (const struct kvm_cpu_context *ctxt) +{ +unsigned long cpsr = ctxt->gp_regs.regs.pstate; + +switch (cpsr & (PSR_MODE32_BIT |PSR_MODE_MASK )) { +case PSR_MODE_EL2h: +case PSR_MODE_EL2t: +return true; +default : +return false; + } +} + +static inline bool vcpu_mode_el2 (const struct kvm_vcpu *vcpu) +{ +return vcpu_mode_el2_ctxt (&vcpu->arch.ctxt); +} + +static inline bool __vcpu_el2_e2h_is_set (const struct kvm_cpu_context *ctxt) +{ +return ctxt->sys_regs[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_regs[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' +*/ +return vcpu_mode_el2_ctxt (ctxt) || + (__vcpu_el2_e2h_is_set (ctxt) &&__vcpu_el2_tge_is_set (ctxt)) || +WARN_ON (__vcpu_el2_tge_is_set (ctxt)); +} + +static inline bool is_hyp_ctxt (const struct kvm_vcpu *vcpu) +{ +return __is_hyp_ctxt (&vcpu->arch.ctxt); +} +static inline unsigned long vcpu_read_spsr (const struct kvm_vcpu *vcpu) {if (vcpu_mode_is_32bit (vcpu))
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 9/94 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).
--- arch/arm64/include/asm/kvm_arm.h | 17 +++ arch/arm64/include/asm/kvm_emulate.h | 22 ++++ arch/arm64/kvm/Makefile | 2 + arch/arm64/kvm/emulate-nested.c | 183 +++++++++++++++++++++++++++ arch/arm64/kvm/inject_fault.c | 12 -- arch/arm64/kvm/trace.h | 56 ++++++++ 6 files changed, 280 insertions(+), 12 deletions(-) create mode 100644 arch/arm64/kvm/emulate-nested.c
arch/arm64/include/asm/kvm_arm.h +17 -0
@@ -328,4 +328,21 @@#define CPACR_EL1_TTA (1 << 28)#define CPACR_EL1_DEFAULT (CPACR_EL1_FPEN | 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 +22 -0
@@ -21,6 +21,24 @@#include <asm/cputype.h> #include <asm/virt.h> +#define CURRENT_EL_SP_EL0_VECTOR 0x0 +#define CURRENT_EL_SP_ELx_VECTOR 0x200 +#define LOWER_EL_AArch64_VECTOR 0x400 +#define LOWER_EL_AArch32_VECTOR 0x600 + +enum exception_type { + except_type_sync =0 , + except_type_irq =0x80 , + except_type_fiq =0x100 , + 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" } +unsigned long *vcpu_reg32 (const struct kvm_vcpu *vcpu,u8 reg_num);unsigned long vcpu_read_spsr32 (const struct kvm_vcpu *vcpu);void vcpu_write_spsr32 (struct kvm_vcpu *vcpu,unsigned long v);
@@ -36,6 +54,10 @@ void kvm_inject_undef32(struct kvm_vcpu *vcpu);void kvm_inject_dabt32 (struct kvm_vcpu *vcpu,unsigned long addr);void kvm_inject_pabt32 (struct kvm_vcpu *vcpu,unsigned long addr); +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); +static inline bool vcpu_el1_is_32bit (struct kvm_vcpu *vcpu) {return !(vcpu->arch.hcr_el2 &HCR_RW );
arch/arm64/kvm/Makefile +2 -0
@@ -37,3 +37,5 @@ kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-debug.o kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/irqchip.o kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/arch_timer.o kvm-$(CONFIG_KVM_ARM_PMU) += $(KVM)/arm/pmu.o + +kvm-$(CONFIG_KVM_ARM_HOST) += emulate-nested.o
arch/arm64/kvm/emulate-nested.c new file +183 -0
183 lines not on this page · raw message
arch/arm64/kvm/inject_fault.c +0 -12
@@ -14,18 +14,6 @@#include <asm/kvm_emulate.h> #include <asm/esr.h> -#define CURRENT_EL_SP_EL0_VECTOR 0x0 -#define CURRENT_EL_SP_ELx_VECTOR 0x200 -#define LOWER_EL_AArch64_VECTOR 0x400 -#define LOWER_EL_AArch32_VECTOR 0x600 - -enum exception_type { - except_type_sync =0 , - except_type_irq =0x80 , - except_type_fiq =0x100 , - except_type_serror =0x180 , -}; -static u64 get_except_vector (struct kvm_vcpu *vcpu,enum exception_type type) {u64 exc_offset;
arch/arm64/kvm/trace.h +56 -0
63 lines not on this page · raw message
Signature · 1 line
2.20.1
Mark Rutland
reviewerRe: [PATCH v2 09/94] KVM: arm64: nv: Support virtual EL2 exceptions
Quoted 19 lines · Marc Zyngier
From: 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).
Signed-off-by Jintack Lim <[email protected]>Signed-off-by Christoffer Dall <[email protected]>Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/include/asm/kvm_arm.h | 17 +++ arch/arm64/include/asm/kvm_emulate.h | 22 ++++ arch/arm64/kvm/Makefile | 2 + arch/arm64/kvm/emulate-nested.c | 183 +++++++++++++++++++++++++++ arch/arm64/kvm/inject_fault.c | 12 -- arch/arm64/kvm/trace.h | 56 ++++++++ 6 files changed, 280 insertions(+), 12 deletions(-) create mode 100644 arch/arm64/kvm/emulate-nested.c
[...]
Quoted 4 lines
arch/arm64/kvm/emulate-nested.c new file +4
-0+static void enter_el2_exception (struct kvm_vcpu *vcpu,u64 esr_el2, +enum exception_type type) +{ +trace_kvm_inject_nested_exception (vcpu, esr_el2, type);
arch/arm64/kvm/emulate-nested.c line 111
Quoted 10 lines
arch/arm64/kvm/emulate-nested.c new file +10
-0+ +vcpu_write_sys_reg (vcpu, *vcpu_cpsr (vcpu),SPSR_EL2 ); +vcpu_write_sys_reg (vcpu, *vcpu_pc (vcpu),ELR_EL2 ); +vcpu_write_sys_reg (vcpu, esr_el2,ESR_EL2 ); + + *vcpu_pc (vcpu) =get_el2_except_vector (vcpu, type); +/* On an exception, PSTATE.SP becomes 1 */ + *vcpu_cpsr (vcpu) = PSR_MODE_EL2h; + *vcpu_cpsr (vcpu) |=PSR_A_BIT |PSR_F_BIT |PSR_I_BIT |PSR_D_BIT ; +}
This needs to be fixed up to handle the rest of the PSTATE bits.
It should be possible to refactor get_except64_pstate() for that. I *think* the only differences are bits affects by SCTLR controls, but someone should audit that -- good thing we added references. :)
Thanks,
Mark.
Marc Zyngier
maintainerRe: [PATCH v2 09/94] KVM: arm64: nv: Support virtual EL2 exceptions
Quoted 41 lines · Mark Rutland
Quoted 20 lines · Marc Zyngier
From: 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).
Signed-off-by Jintack Lim <[email protected]>Signed-off-by Christoffer Dall <[email protected]>Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/include/asm/kvm_arm.h | 17 +++ arch/arm64/include/asm/kvm_emulate.h | 22 ++++ arch/arm64/kvm/Makefile | 2 + arch/arm64/kvm/emulate-nested.c | 183 +++++++++++++++++++++++++++ arch/arm64/kvm/inject_fault.c | 12 -- arch/arm64/kvm/trace.h | 56 ++++++++ 6 files changed, 280 insertions(+), 12 deletions(-) create mode 100644 arch/arm64/kvm/emulate-nested.c[...]
Quoted 4 lines
arch/arm64/kvm/emulate-nested.c new file +4
-0+static void enter_el2_exception (struct kvm_vcpu *vcpu,u64 esr_el2, +enum exception_type type) +{ +trace_kvm_inject_nested_exception (vcpu, esr_el2, type);arch/arm64/kvm/emulate-nested.c line 111
Quoted 10 lines
arch/arm64/kvm/emulate-nested.c new file +10
-0+ +vcpu_write_sys_reg (vcpu, *vcpu_cpsr (vcpu),SPSR_EL2 ); +vcpu_write_sys_reg (vcpu, *vcpu_pc (vcpu),ELR_EL2 ); +vcpu_write_sys_reg (vcpu, esr_el2,ESR_EL2 ); + + *vcpu_pc (vcpu) =get_el2_except_vector (vcpu, type); +/* On an exception, PSTATE.SP becomes 1 */ + *vcpu_cpsr (vcpu) = PSR_MODE_EL2h; + *vcpu_cpsr (vcpu) |=PSR_A_BIT |PSR_F_BIT |PSR_I_BIT |PSR_D_BIT ; +}This needs to be fixed up to handle the rest of the PSTATE bits.
Quoted 3 lines
It should be possible to refactor
get_except64_pstate()for that. I *think* the only differences are bits affects by SCTLR controls, but someone should audit that -- good thing we added references. :)
Absolutely. It is on my list of things to fix for the next drop. Also, ("arm64: KVM: nv: Honor SCTLR_EL2.SPAN on entering vEL2") should be folded in there (or simply dropped if we can reuse the EL1 stuff).
Thanks,
M.
Signature · 1 line
Jazz is not dead. It just smells funny...
Marc Zyngier
maintainerPATCH v2 10/94 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
@@ -17,6 +17,7 @@#include <asm/kvm_coproc.h> #include <asm/kvm_emulate.h> #include <asm/kvm_mmu.h> +#include <asm/kvm_nested.h> #include <asm/debug-monitors.h> #include <asm/traps.h>
@@ -41,6 +42,16 @@ static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) kvm_vcpu_hvc_get_imm(vcpu)); vcpu->stat.hvc_exit_stat++; +/* Forward hvc instructions to the virtual EL2 if the guest has EL2. */ +if (nested_virt_in_use (vcpu)) { +if (vcpu_read_sys_reg (vcpu,HCR_EL2 ) &HCR_HCD ) +kvm_inject_undefined (vcpu); +else +kvm_inject_nested_sync (vcpu,kvm_vcpu_get_hsr (vcpu)); + +return 1 ; + } + ret =kvm_hvc_call_handler (vcpu);if (ret <0 ) {vcpu_set_reg (vcpu,0 , ~0UL );
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 11/94 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 | 5 +++++ arch/arm64/include/asm/kvm_arm.h | 2 +- arch/arm64/kvm/handle_exit.c | 10 ++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-)
arch/arm64/include/asm/esr.h +5 -0
@@ -249,6 +249,11 @@ (((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_ERETAx 0x2 +#define ESR_ELx_ERET_ISS_ERETA_ERATAB 0x1 +/* * ISS field definitions for floating-point exception traps * (FP_EXC_32/FP_EXC_64).
arch/arm64/include/asm/kvm_arm.h +1 -1
@@ -322,7 +322,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_FPEN (3 << 20)#define CPACR_EL1_TTA (1 << 28)
arch/arm64/kvm/handle_exit.c +10 -0
@@ -210,6 +210,15 @@ static int kvm_handle_ptrauth(struct kvm_vcpu *vcpu, struct kvm_run *run)return 1 ; } +static int kvm_handle_eret (struct kvm_vcpu *vcpu,struct kvm_run *run) +{ +if (kvm_vcpu_get_hsr (vcpu) & ESR_ELx_ERET_ISS_ERET_ERETAx) +return kvm_handle_ptrauth (vcpu, run); + +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,
@@ -224,6 +233,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.20.1
Marc Zyngier
maintainerPATCH v2 12/94 KVM: arm64: nv: Add EL2->EL1 translation helpers
In reply to Marc ZyngierSome EL2 system registers immediately affect the current execution of the system, so we need to use their respective EL1 counterparts. For this we need to define a mapping between the two.
These helpers will get used in subsequent patches.
--- arch/arm64/include/asm/kvm_emulate.h | 6 ++++ arch/arm64/kvm/sys_regs.c | 48 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)
arch/arm64/include/asm/kvm_emulate.h +6 -0
@@ -58,6 +58,12 @@ 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); +u64 translate_tcr (u64 tcr); +u64 translate_cptr (u64 tcr); +u64 translate_sctlr (u64 tcr); +u64 translate_ttbr0 (u64 tcr); +u64 translate_cnthctl (u64 tcr); +static inline bool vcpu_el1_is_32bit (struct kvm_vcpu *vcpu) {return !(vcpu->arch.hcr_el2 &HCR_RW );
arch/arm64/kvm/sys_regs.c +48 -0
54 lines not on this page · raw message
Signature · 1 line
2.20.1
Mark Rutland
reviewerRe: [PATCH v2 12/94] KVM: arm64: nv: Add EL2->EL1 translation helpers
Quoted 19 lines · Marc Zyngier
Some EL2 system registers immediately affect the current execution of the system, so we need to use their respective EL1 counterparts. For this we need to define a mapping between the two.
These helpers will get used in subsequent patches.
Co-developed-by Andre Przywara <[email protected]>Signed-off-by Andre Przywara <[email protected]>Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/include/asm/kvm_emulate.h | 6 ++++ arch/arm64/kvm/sys_regs.c | 48 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)arch/arm64/include/asm/kvm_emulate.h +0
-0
arch/arm64/include/asm/kvm_emulate.h void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu); · line 65
Quoted 8 lines
arch/arm64/include/asm/kvm_emulate.h +5
-0@@ -58,6 +58,12 @@ 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); +u64 translate_tcr (u64 tcr); +u64 translate_cptr (u64 tcr); +u64 translate_sctlr (u64 tcr); +u64 translate_ttbr0 (u64 tcr); +u64 translate_cnthctl (u64 tcr);
Sorry to bikeshed, but could we please make the direction of translation explicit in the name? e.g. tcr_el2_to_tcr_el1(), or tcr_el2_to_el1()?
Quoted 18 lines
arch/arm64/include/asm/kvm_emulate.h +1
-0+static inline bool vcpu_el1_is_32bit (struct kvm_vcpu *vcpu) {return !(vcpu->arch.hcr_el2 &HCR_RW );arch/arm64/kvm/sys_regs.c +6
-0@@ -65,6 +65,54 @@ static bool write_to_read_only(struct kvm_vcpu *vcpu,return false; } +static u64 tcr_el2_ips_to_tcr_el1_ps (u64 tcr_el2) +{ +return ((tcr_el2 &TCR_EL2_PS_MASK ) >>TCR_EL2_PS_SHIFT ) + <<TCR_IPS_SHIFT ; +} +
arch/arm64/kvm/sys_regs.c static bool write_to_read_only(struct kvm_vcpu *vcpu, · line 83
Quoted 10 lines
arch/arm64/kvm/sys_regs.c +10
-0+u64 translate_tcr (u64 tcr) +{ +return TCR_EPD1_MASK |/* disable TTBR1_EL1 */ + ((tcr &TCR_EL2_TBI ) ?TCR_TBI0 :0 ) | +tcr_el2_ips_to_tcr_el1_ps (tcr) | + (tcr &TCR_EL2_TG0_MASK ) | + (tcr &TCR_EL2_ORGN0_MASK ) | + (tcr &TCR_EL2_IRGN0_MASK ) | + (tcr &TCR_EL2_T0SZ_MASK ); +}
I'm guessing this is only meant to cover a !VHE guest EL2 for the moment, so only covers HCR_EL2.E2H=0? It might be worth mentioning in the commit message.
It looks like this is missing some bits (e.g. TBID, HPD, HD, HA) that could apply to the Guest-EL2 Stage-1. Maybe those are added by later patches, but that's not obvious to me at this point in the series.
Quoted 4 lines
arch/arm64/kvm/sys_regs.c +4
-0+ +u64 translate_cptr (u64 cptr_el2) +{ +u64 cpacr_el1 =0 ;
arch/arm64/kvm/sys_regs.c static bool write_to_read_only(struct kvm_vcpu *vcpu, · line 97
Quoted 10 lines
arch/arm64/kvm/sys_regs.c +10
-0+ +if (!(cptr_el2 &CPTR_EL2_TFP )) + cpacr_el1 |=CPACR_EL1_FPEN ; +if (cptr_el2 &CPTR_EL2_TTA ) + cpacr_el1 |=CPACR_EL1_TTA ; +if (!(cptr_el2 &CPTR_EL2_TZ )) + cpacr_el1 |=CPACR_EL1_ZEN ; + +return cpacr_el1; +}
Looking in ARM DDI 0487E.a I also see TCPAC and TAM; I guess we don't need to map those to anthing?
arch/arm64/kvm/sys_regs.c static bool write_to_read_only(struct kvm_vcpu *vcpu, · line 103
Quoted 6 lines
arch/arm64/kvm/sys_regs.c +6
-0+ +u64 translate_sctlr (u64 sctlr) +{ +/* Bit 20 is RES1 in SCTLR_EL1, but RES0 in SCTLR_EL2 */ +return sctlr |BIT (20 ); +}
Looking in ARM DDI 0487E.a section D13.2.105, bit 20 is TSCXT, so this might need to be reconsidered.
arch/arm64/kvm/sys_regs.c static bool write_to_read_only(struct kvm_vcpu *vcpu, · line 109
Quoted 6 lines
arch/arm64/kvm/sys_regs.c +6
-0+ +u64 translate_ttbr0 (u64 ttbr0) +{ +/* Force ASID to 0 (ASID 0 or RES0) */ +return ttbr0 & ~GENMASK_ULL (63 ,48 ); +}
Again, I assume this is only meant to provide a !VHE EL2 as this stands.
arch/arm64/kvm/sys_regs.c static bool write_to_read_only(struct kvm_vcpu *vcpu, · line 114
Quoted 5 lines
arch/arm64/kvm/sys_regs.c +5
-0+ +u64 translate_cnthctl (u64 cnthctl) +{ +return ((cnthctl &0x3 ) <<10 ) | (cnthctl &0xfc ); +}
I assume this yields CNTKCTL_EL1, but I don't entirely follow. For virtual-EL2 don't we have to force EL1P(C)TEN so that virtual-EL2 accesses don't trap?
Thanks,
Mark.
Marc Zyngier
maintainerRe: [PATCH v2 12/94] KVM: arm64: nv: Add EL2->EL1 translation helpers
Hi Mark,
Congratulations, you will now be CC'd on all the subsequent postings of this series! Yes, I'm that nice! ;-)
Quoted 25 lines · Mark Rutland
Quoted 24 lines · Marc Zyngier
Some EL2 system registers immediately affect the current execution of the system, so we need to use their respective EL1 counterparts. For this we need to define a mapping between the two.
These helpers will get used in subsequent patches.
Co-developed-by Andre Przywara <[email protected]>Signed-off-by Andre Przywara <[email protected]>Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/include/asm/kvm_emulate.h | 6 ++++ arch/arm64/kvm/sys_regs.c | 48 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)arch/arm64/include/asm/kvm_emulate.h +0
-0@@ -58,6 +58,12 @@ 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);
Quoted 8 lines
arch/arm64/include/asm/kvm_emulate.h void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu); · line 65
Quoted 5 lines
arch/arm64/include/asm/kvm_emulate.h +5
-0+u64 translate_tcr (u64 tcr); +u64 translate_cptr (u64 tcr); +u64 translate_sctlr (u64 tcr); +u64 translate_ttbr0 (u64 tcr); +u64 translate_cnthctl (u64 tcr);Sorry to bikeshed, but could we please make the direction of translation explicit in the name? e.g.
tcr_el2_to_tcr_el1(), ortcr_el2_to_el1()?
Sure, that's an easy one!
Quoted 26 lines
Quoted 26 lines
arch/arm64/include/asm/kvm_emulate.h +1
-0+static inline bool vcpu_el1_is_32bit (struct kvm_vcpu *vcpu) {arch/arm64/include/asm/kvm_emulate.h +0
-0return !(vcpu->arch.hcr_el2 &HCR_RW );arch/arm64/kvm/sys_regs.c +12
-0@@ -65,6 +65,54 @@ static bool write_to_read_only(struct kvm_vcpu *vcpu, return false; } +static u64 tcr_el2_ips_to_tcr_el1_ps (u64 tcr_el2) +{ +return ((tcr_el2 &TCR_EL2_PS_MASK ) >>TCR_EL2_PS_SHIFT ) + <<TCR_IPS_SHIFT ; +} + +u64 translate_tcr (u64 tcr) +{ +return TCR_EPD1_MASK |/* disable TTBR1_EL1 */ + ((tcr &TCR_EL2_TBI ) ?TCR_TBI0 :0 ) | +tcr_el2_ips_to_tcr_el1_ps (tcr) | + (tcr & TCR_EL2_TG0_MASK) |
Quoted 8 lines
arch/arm64/kvm/sys_regs.c static bool write_to_read_only(struct kvm_vcpu *vcpu, · line 83
Quoted 4 lines
arch/arm64/kvm/sys_regs.c +4
-0+ (tcr &TCR_EL2_ORGN0_MASK ) | + (tcr &TCR_EL2_IRGN0_MASK ) | + (tcr &TCR_EL2_T0SZ_MASK ); +}I'm guessing this is only meant to cover a !VHE guest EL2 for the moment, so only covers HCR_EL2.E2H=0? It might be worth mentioning in the commit message.
Indeed, all the "translate_*" function have a single purpose: converting an !VHE EL2 layout into an EL1 layout.
Quoted 3 lines
It looks like this is missing some bits (e.g. TBID, HPD, HD, HA) that could apply to the Guest-EL2 Stage-1. Maybe those are added by later patches, but that's not obvious to me at this point in the series.
ARMv8.3-PAUTH isn't supported, and ARMv8.1-TTHM cannot be supported at Stage-2, so we don't support it at Stage-1 either (even if we technically could). Maybe worth suggesting to the powers that be...
ARMv8.1-HPD is an oversight though, and we should be able to support it.
Quoted 10 lines
Quoted 10 lines
arch/arm64/kvm/sys_regs.c +9
-0+ +u64 translate_cptr (u64 cptr_el2) +{ +u64 cpacr_el1 =0 ; + +if (!(cptr_el2 &CPTR_EL2_TFP )) + cpacr_el1 |=CPACR_EL1_FPEN ; +if (cptr_el2 &CPTR_EL2_TTA ) + cpacr_el1 |=CPACR_EL1_TTA ;
Quoted 8 lines
arch/arm64/kvm/sys_regs.c static bool write_to_read_only(struct kvm_vcpu *vcpu, · line 97
Quoted 5 lines
arch/arm64/kvm/sys_regs.c +5
-0+if (!(cptr_el2 &CPTR_EL2_TZ )) + cpacr_el1 |=CPACR_EL1_ZEN ; + +return cpacr_el1; +}Looking in ARM DDI 0487E.a I also see TCPAC and TAM; I guess we don't need to map those to anthing?
TCPAC allows us to trap CPACR_EL1, but we always have the physical CPTR_EL2.TCPAC set in this case, so that bit doesn't need to translate into anything.
TAM doesn't seem to translate into anything in CPACR_EL1, and I don't plan to support the AMU any time soon with NV! ;-)
Quoted 10 lines
arch/arm64/kvm/sys_regs.c static bool write_to_read_only(struct kvm_vcpu *vcpu, · line 103
Quoted 7 lines
arch/arm64/kvm/sys_regs.c +6
-0+ +u64 translate_sctlr (u64 sctlr) +{ +/* Bit 20 is RES1 in SCTLR_EL1, but RES0 in SCTLR_EL2 */ +return sctlr |BIT (20 ); +}Looking in ARM DDI 0487E.a section D13.2.105, bit 20 is TSCXT, so this might need to be reconsidered.
Huhuh, nice catch! We need to detect ARMv8.0-CSV2 here, and set the bit accordingly.
Quoted 9 lines
arch/arm64/kvm/sys_regs.c static bool write_to_read_only(struct kvm_vcpu *vcpu, · line 109
Quoted 7 lines
arch/arm64/kvm/sys_regs.c +6
-0+ +u64 translate_ttbr0 (u64 ttbr0) +{ +/* Force ASID to 0 (ASID 0 or RES0) */ +return ttbr0 & ~GENMASK_ULL (63 ,48 ); +}Again, I assume this is only meant to provide a !VHE EL2 as this stands.
Indeed. I guess I need to write a better commit message to make this clear.
Quoted 10 lines
arch/arm64/kvm/sys_regs.c static bool write_to_read_only(struct kvm_vcpu *vcpu, · line 114
Quoted 6 lines
arch/arm64/kvm/sys_regs.c +5
-0+ +u64 translate_cnthctl (u64 cnthctl) +{ +return ((cnthctl &0x3 ) <<10 ) | (cnthctl &0xfc ); +}I assume this yields
CNTKCTL_EL1, but I don't entirely follow. For virtual-EL2 don't we have to force EL1P(C)TEN so that virtual-EL2 accesses don't trap?
A non-VHE guest will use the _EL2 instructions to access its own timer, which will trap. At the same time, we use the EL1 timer to implement the vEL2 timer, meaning we also need to trap the EL1 timer. Yes, !VHE looses on all fronts.
We could treat !VHE specially in the wap we map timers (complete emulation for vEL2, and direct access for guest timers). But this seems pretty complex for very little gain.
Thanks,
M.
Signature · 1 line
Jazz is not dead. It just smells funny...
Marc Zyngier
maintainerPATCH v2 13/94 KVM: arm64: nv: Refactor vcpu_{read,write}_sys_reg
In reply to Marc ZyngierExtract the direct HW accessors for later reuse.
--- arch/arm64/kvm/sys_regs.c | 143 +++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 57 deletions(-)
arch/arm64/kvm/sys_regs.c +86 -57
@@ -113,11 +113,8 @@ u64 translate_cnthctl(u64 cnthctl)return ((cnthctl &0x3 ) <<10 ) | (cnthctl &0xfc ); } -u64 vcpu_read_sys_reg (const struct kvm_vcpu *vcpu,int reg) +static bool __vcpu_read_sys_reg_from_cpu (int reg,u64 *val) { -if (!vcpu->arch.sysregs_loaded_on_cpu) - goto immediate_read; -/* * System registers listed in the switch are not saved on every * exit from the guest but are only saved on vcpu_put.
@@ -128,40 +125,37 @@ u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg)* thread when emulating cross-VCPU communication. */ switch (reg) { -case CSSELR_EL1 :return read_sysreg_s (SYS_CSSELR_EL1 ); -case SCTLR_EL1 :return read_sysreg_s (SYS_SCTLR_EL12 ); -case ACTLR_EL1 :return read_sysreg_s (SYS_ACTLR_EL1 ); -case CPACR_EL1 :return read_sysreg_s (SYS_CPACR_EL12 ); -case TTBR0_EL1 :return read_sysreg_s (SYS_TTBR0_EL12 ); -case TTBR1_EL1 :return read_sysreg_s (SYS_TTBR1_EL12 ); -case TCR_EL1 :return read_sysreg_s (SYS_TCR_EL12 ); -case ESR_EL1 :return read_sysreg_s (SYS_ESR_EL12 ); -case AFSR0_EL1 :return read_sysreg_s (SYS_AFSR0_EL12 ); -case AFSR1_EL1 :return read_sysreg_s (SYS_AFSR1_EL12 ); -case FAR_EL1 :return read_sysreg_s (SYS_FAR_EL12 ); -case MAIR_EL1 :return read_sysreg_s (SYS_MAIR_EL12 ); -case VBAR_EL1 :return read_sysreg_s (SYS_VBAR_EL12 ); -case CONTEXTIDR_EL1 :return read_sysreg_s (SYS_CONTEXTIDR_EL12 ); -case TPIDR_EL0 :return read_sysreg_s (SYS_TPIDR_EL0 ); -case TPIDRRO_EL0 :return read_sysreg_s (SYS_TPIDRRO_EL0 ); -case TPIDR_EL1 :return read_sysreg_s (SYS_TPIDR_EL1 ); -case AMAIR_EL1 :return read_sysreg_s (SYS_AMAIR_EL12 ); -case CNTKCTL_EL1 :return read_sysreg_s (SYS_CNTKCTL_EL12 ); -case PAR_EL1 :return read_sysreg_s (SYS_PAR_EL1 ); -case DACR32_EL2 :return read_sysreg_s (SYS_DACR32_EL2 ); -case IFSR32_EL2 :return read_sysreg_s (SYS_IFSR32_EL2 ); -case DBGVCR32_EL2 :return read_sysreg_s (SYS_DBGVCR32_EL2 ); +case CSSELR_EL1 : *val =read_sysreg_s (SYS_CSSELR_EL1 );break ; +case SCTLR_EL1 : *val =read_sysreg_s (SYS_SCTLR_EL12 );break ; +case ACTLR_EL1 : *val =read_sysreg_s (SYS_ACTLR_EL1 );break ; +case CPACR_EL1 : *val =read_sysreg_s (SYS_CPACR_EL12 );break ; +case TTBR0_EL1 : *val =read_sysreg_s (SYS_TTBR0_EL12 );break ; +case TTBR1_EL1 : *val =read_sysreg_s (SYS_TTBR1_EL12 );break ; +case TCR_EL1 : *val =read_sysreg_s (SYS_TCR_EL12 );break ; +case ESR_EL1 : *val =read_sysreg_s (SYS_ESR_EL12 );break ; +case AFSR0_EL1 : *val =read_sysreg_s (SYS_AFSR0_EL12 );break ; +case AFSR1_EL1 : *val =read_sysreg_s (SYS_AFSR1_EL12 );break ; +case FAR_EL1 : *val =read_sysreg_s (SYS_FAR_EL12 );break ; +case MAIR_EL1 : *val =read_sysreg_s (SYS_MAIR_EL12 );break ; +case VBAR_EL1 : *val =read_sysreg_s (SYS_VBAR_EL12 );break ; +case CONTEXTIDR_EL1 : *val =read_sysreg_s (SYS_CONTEXTIDR_EL12 );break ; +case TPIDR_EL0 : *val =read_sysreg_s (SYS_TPIDR_EL0 );break ; +case TPIDRRO_EL0 : *val =read_sysreg_s (SYS_TPIDRRO_EL0 );break ; +case TPIDR_EL1 : *val =read_sysreg_s (SYS_TPIDR_EL1 );break ; +case AMAIR_EL1 : *val =read_sysreg_s (SYS_AMAIR_EL12 );break ; +case CNTKCTL_EL1 : *val =read_sysreg_s (SYS_CNTKCTL_EL12 );break ; +case PAR_EL1 : *val =read_sysreg_s (SYS_PAR_EL1 );break ; +case DACR32_EL2 : *val =read_sysreg_s (SYS_DACR32_EL2 );break ; +case IFSR32_EL2 : *val =read_sysreg_s (SYS_IFSR32_EL2 );break ; +case DBGVCR32_EL2 : *val =read_sysreg_s (SYS_DBGVCR32_EL2 );break ; +default :return false; } -immediate_read: -return __vcpu_sys_reg (vcpu, reg); +return true; } -void vcpu_write_sys_reg (struct kvm_vcpu *vcpu,u64 val,int reg) +static bool __vcpu_write_sys_reg_to_cpu (u64 val,int reg) { -if (!vcpu->arch.sysregs_loaded_on_cpu) - goto immediate_write; -/* * System registers listed in the switch are not restored on every * entry to the guest but are only restored on vcpu_load.
@@ -171,32 +165,67 @@ void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, int reg)* set once, before running the VCPU, and never changed later. */ switch (reg) { -case CSSELR_EL1 :write_sysreg_s (val,SYS_CSSELR_EL1 );return ; -case SCTLR_EL1 :write_sysreg_s (val,SYS_SCTLR_EL12 );return ; -case ACTLR_EL1 :write_sysreg_s (val,SYS_ACTLR_EL1 );return ; -case CPACR_EL1 :write_sysreg_s (val,SYS_CPACR_EL12 );return ; -case TTBR0_EL1 :write_sysreg_s (val,SYS_TTBR0_EL12 );return ; -case TTBR1_EL1 :write_sysreg_s (val,SYS_TTBR1_EL12 );return ; -case TCR_EL1 :write_sysreg_s (val,SYS_TCR_EL12 );return ; -case ESR_EL1 :write_sysreg_s (val,SYS_ESR_EL12 );return ; -case AFSR0_EL1 :write_sysreg_s (val,SYS_AFSR0_EL12 );return ; -case AFSR1_EL1 :write_sysreg_s (val,SYS_AFSR1_EL12 );return ; -case FAR_EL1 :write_sysreg_s (val,SYS_FAR_EL12 );return ; -case MAIR_EL1 :write_sysreg_s (val,SYS_MAIR_EL12 );return ; -case VBAR_EL1 :write_sysreg_s (val,SYS_VBAR_EL12 );return ; -case CONTEXTIDR_EL1 :write_sysreg_s (val,SYS_CONTEXTIDR_EL12 );return ; -case TPIDR_EL0 :write_sysreg_s (val,SYS_TPIDR_EL0 );return ; -case TPIDRRO_EL0 :write_sysreg_s (val,SYS_TPIDRRO_EL0 );return ; -case TPIDR_EL1 :write_sysreg_s (val,SYS_TPIDR_EL1 );return ; -case AMAIR_EL1 :write_sysreg_s (val,SYS_AMAIR_EL12 );return ; -case CNTKCTL_EL1 :write_sysreg_s (val,SYS_CNTKCTL_EL12 );return ; -case PAR_EL1 :write_sysreg_s (val,SYS_PAR_EL1 );return ; -case DACR32_EL2 :write_sysreg_s (val,SYS_DACR32_EL2 );return ; -case IFSR32_EL2 :write_sysreg_s (val,SYS_IFSR32_EL2 );return ; -case DBGVCR32_EL2 :write_sysreg_s (val,SYS_DBGVCR32_EL2 );return ; +case CSSELR_EL1 :write_sysreg_s (val,SYS_CSSELR_EL1 );break ; +case SCTLR_EL1 :write_sysreg_s (val,SYS_SCTLR_EL12 );break ; +case ACTLR_EL1 :write_sysreg_s (val,SYS_ACTLR_EL1 );break ; +case CPACR_EL1 :write_sysreg_s (val,SYS_CPACR_EL12 );break ; +case TTBR0_EL1 :write_sysreg_s (val,SYS_TTBR0_EL12 );break ; +case TTBR1_EL1 :write_sysreg_s (val,SYS_TTBR1_EL12 );break ; +case TCR_EL1 :write_sysreg_s (val,SYS_TCR_EL12 );break ; +case ESR_EL1 :write_sysreg_s (val,SYS_ESR_EL12 );break ; +case AFSR0_EL1 :write_sysreg_s (val,SYS_AFSR0_EL12 );break ; +case AFSR1_EL1 :write_sysreg_s (val,SYS_AFSR1_EL12 );break ; +case FAR_EL1 :write_sysreg_s (val,SYS_FAR_EL12 );break ; +case MAIR_EL1 :write_sysreg_s (val,SYS_MAIR_EL12 );break ; +case VBAR_EL1 :write_sysreg_s (val,SYS_VBAR_EL12 );break ; +case CONTEXTIDR_EL1 :write_sysreg_s (val,SYS_CONTEXTIDR_EL12 );break ; +case TPIDR_EL0 :write_sysreg_s (val,SYS_TPIDR_EL0 );break ; +case TPIDRRO_EL0 :write_sysreg_s (val,SYS_TPIDRRO_EL0 );break ; +case TPIDR_EL1 :write_sysreg_s (val,SYS_TPIDR_EL1 );break ; +case AMAIR_EL1 :write_sysreg_s (val,SYS_AMAIR_EL12 );break ; +case CNTKCTL_EL1 :write_sysreg_s (val,SYS_CNTKCTL_EL12 );break ; +case PAR_EL1 :write_sysreg_s (val,SYS_PAR_EL1 );break ; +case DACR32_EL2 :write_sysreg_s (val,SYS_DACR32_EL2 );break ; +case IFSR32_EL2 :write_sysreg_s (val,SYS_IFSR32_EL2 );break ; +case DBGVCR32_EL2 :write_sysreg_s (val,SYS_DBGVCR32_EL2 );break ; +default :return false; + } + +return true; +} + +u64 vcpu_read_sys_reg (const struct kvm_vcpu *vcpu,int reg) +{ +u64 val =0x8badf00d8badf00d ; + +if (!vcpu->arch.sysregs_loaded_on_cpu) { + goto memory_read; } -immediate_write: +/* EL1 register can't be on the CPU if the guest is in vEL2. */ +if (unlikely (is_hyp_ctxt (vcpu))) + goto memory_read; + +if (__vcpu_read_sys_reg_from_cpu (reg, &val)) +return val; + +memory_read: +return __vcpu_sys_reg (vcpu, reg); +} + +void vcpu_write_sys_reg (struct kvm_vcpu *vcpu,u64 val,int reg) +{ +if (!vcpu->arch.sysregs_loaded_on_cpu) + goto memory_write; + +/* EL1 register can't be on the CPU if the guest is in vEL2. */ +if (unlikely (is_hyp_ctxt (vcpu))) + goto memory_write; + +if (__vcpu_write_sys_reg_to_cpu (val, reg)) +return ; + +memory_write:__vcpu_sys_reg (vcpu, reg) = val; }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 14/94 KVM: arm64: nv: Handle virtual EL2 registers in vcpu_read/write_sys_reg()
In reply to Marc ZyngierKVM internally uses accessor functions when reading or writing the guest's system registers. This takes care of accessing either the stored copy or using the "live" EL1 system registers when the host uses VHE.
With the introduction of virtual EL2 we add a bunch of EL2 system registers, which now must also be taken care of:
- If the guest is running in vEL2, and we access an EL1 sysreg, we must revert to the stored version of that, and not use the CPU's copy.
- If the guest is running in vEL1, and we access an EL2 sysreg, we must also use the stored version, since the CPU carries the EL1 copy.
- Some EL2 system registers are supposed to affect the current execution of the system, so we need to put them into their respective EL1 counterparts. For this we need to define a mapping between the two. This is done using the newly introduced struct el2_sysreg_map.
- Some EL2 system registers have a different format than their EL1 counterpart, so we need to translate them before writing them to the CPU. This is done using an (optional) translate function in the map.
- There are the three special registers SP_EL2, SPSR_EL2 and ELR_EL2, which need some separate handling (SPSR_EL2 is being handled in a separate patch).
All of these cases are now wrapped into the existing accessor functions, so KVM users wouldn't need to care whether they access EL2 or EL1 registers and also which state the guest is in.
This handles what was formerly known as the "shadow state" dynamically, without requiring a separate copy for each vCPU EL.
--- arch/arm64/kvm/sys_regs.c | 117 +++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-)
arch/arm64/kvm/sys_regs.c +116 -1
@@ -113,6 +113,56 @@ u64 translate_cnthctl(u64 cnthctl)return ((cnthctl &0x3 ) <<10 ) | (cnthctl &0xfc ); } +#define PURE_EL2_SYSREG (el2) \ + case el2: { \ + *el1r = el2; \ + return true; \ + } + +#define MAPPED_EL2_SYSREG (el2, el1, fn) \ + case el2: { \ + *xlate = fn; \ + *el1r = el1; \ + return true; \ + } + +static bool get_el2_mapping (unsigned int reg, +unsigned int *el1r,u64 (**xlate)(u64 )) +{ +switch (reg) { +PURE_EL2_SYSREG (VPIDR_EL2 ); +PURE_EL2_SYSREG (VMPIDR_EL2 ); +PURE_EL2_SYSREG (ACTLR_EL2 ); +PURE_EL2_SYSREG (HCR_EL2 ); +PURE_EL2_SYSREG (MDCR_EL2 ); +PURE_EL2_SYSREG (HSTR_EL2 ); +PURE_EL2_SYSREG (HACR_EL2 ); +PURE_EL2_SYSREG (VTTBR_EL2 ); +PURE_EL2_SYSREG (VTCR_EL2 ); +PURE_EL2_SYSREG (RVBAR_EL2 ); +PURE_EL2_SYSREG (RMR_EL2 ); +PURE_EL2_SYSREG (TPIDR_EL2 ); +PURE_EL2_SYSREG (HPFAR_EL2 ); +PURE_EL2_SYSREG (ELR_EL2 ); +PURE_EL2_SYSREG (SPSR_EL2 ); +MAPPED_EL2_SYSREG (SCTLR_EL2 ,SCTLR_EL1 , translate_sctlr ); +MAPPED_EL2_SYSREG (CPTR_EL2 ,CPACR_EL1 , translate_cptr ); +MAPPED_EL2_SYSREG (TTBR0_EL2 ,TTBR0_EL1 , translate_ttbr0 ); +MAPPED_EL2_SYSREG (TTBR1_EL2 ,TTBR1_EL1 ,NULL ); +MAPPED_EL2_SYSREG (TCR_EL2 ,TCR_EL1 , translate_tcr ); +MAPPED_EL2_SYSREG (VBAR_EL2 ,VBAR_EL1 ,NULL ); +MAPPED_EL2_SYSREG (AFSR0_EL2 ,AFSR0_EL1 ,NULL ); +MAPPED_EL2_SYSREG (AFSR1_EL2 ,AFSR1_EL1 ,NULL ); +MAPPED_EL2_SYSREG (ESR_EL2 ,ESR_EL1 ,NULL ); +MAPPED_EL2_SYSREG (FAR_EL2 ,FAR_EL1 ,NULL ); +MAPPED_EL2_SYSREG (MAIR_EL2 ,MAIR_EL1 ,NULL ); +MAPPED_EL2_SYSREG (AMAIR_EL2 ,AMAIR_EL1 ,NULL ); +MAPPED_EL2_SYSREG (CNTHCTL_EL2 ,CNTKCTL_EL1 , translate_cnthctl); +default : +return false; + } +} +static bool __vcpu_read_sys_reg_from_cpu (int reg,u64 *val) {/*
@@ -197,9 +247,42 @@ static bool __vcpu_write_sys_reg_to_cpu(u64 val, int reg)u64 vcpu_read_sys_reg (const struct kvm_vcpu *vcpu,int reg) {u64 val =0x8badf00d8badf00d ; +u64 (*xlate)(u64) =NULL ; +unsigned int el1r; -if (!vcpu->arch.sysregs_loaded_on_cpu) { +if (!vcpu->arch.sysregs_loaded_on_cpu) goto memory_read; + +if (unlikely (get_el2_mapping (reg, &el1r, &xlate))) { +if (!is_hyp_ctxt (vcpu)) + goto memory_read; + +/* +* ELR_EL2 is special cased for now. +*/ +switch (reg) { +case ELR_EL2 : +return read_sysreg_el1 (SYS_ELR ); + } + +/* +* If this register does not have an EL1 counterpart, +* then read the stored EL2 version. +*/ +if (reg == el1r) + goto memory_read; + +/* +* If we have a non-VHE guest and that the sysreg +* requires translation to be used at EL1, use the +* in-memory copy instead. +*/ +if (!vcpu_el2_e2h_is_set (vcpu) && xlate) + goto memory_read; + +/* Get the current version of the EL1 counterpart. */ +WARN_ON (!__vcpu_read_sys_reg_from_cpu (el1r, &val)); +return val; }/* EL1 register can't be on the CPU if the guest is in vEL2. */
@@ -215,9 +298,41 @@ u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg)void vcpu_write_sys_reg (struct kvm_vcpu *vcpu,u64 val,int reg) { +u64 (*xlate)(u64) =NULL ; +unsigned int el1r; +if (!vcpu->arch.sysregs_loaded_on_cpu) goto memory_write; +if (unlikely (get_el2_mapping (reg, &el1r, &xlate))) { +if (!is_hyp_ctxt (vcpu)) + goto memory_write; + +/* +* Always store a copy of the write to memory to avoid having +* to reverse-translate virtual EL2 system registers for a +* non-VHE guest hypervisor. +*/ +__vcpu_sys_reg (vcpu, reg) = val; + +switch (reg) { +case ELR_EL2 : +write_sysreg_el1 (val,SYS_ELR ); +return ; + } + +/* No EL1 counterpart? We're done here.? */ +if (reg == el1r) +return ; + +if (!vcpu_el2_e2h_is_set (vcpu) && xlate) + val =xlate (val); + +/* Redirect this to the EL1 version of the register. */ +WARN_ON (!__vcpu_write_sys_reg_to_cpu (val, el1r)); +return ; + } +/* EL1 register can't be on the CPU if the guest is in vEL2. */ if (unlikely (is_hyp_ctxt (vcpu))) goto memory_write;
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 15/94 KVM: arm64: nv: Handle SPSR_EL2 specially
In reply to Marc ZyngierSPSR_EL2 needs special attention when running nested on ARMv8.3:
If taking an exception while running at vEL2 (actually EL1), the HW will update the SPSR_EL1 register with the EL1 mode. We need to track this in order to make sure that accesses to the virtual view of SPSR_EL2 is correct.
To do so, we place an illegal value in SPSR_EL1.M, and patch it accordingly if required when accessing it.
--- arch/arm64/include/asm/kvm_emulate.h | 45 ++++++++++++++++++++++++++++ arch/arm64/kvm/sys_regs.c | 23 ++++++++++++-- 2 files changed, 66 insertions(+), 2 deletions(-)
arch/arm64/include/asm/kvm_emulate.h +45 -0
@@ -277,11 +277,51 @@ static inline bool is_hyp_ctxt(const struct kvm_vcpu *vcpu)return __is_hyp_ctxt (&vcpu->arch.ctxt); } +static inline u64 __fixup_spsr_el2_write (struct kvm_cpu_context *ctxt,u64 val) +{ +if (!__vcpu_el2_e2h_is_set (ctxt)) { +/* +* Clear the .M field when writing SPSR to the CPU, so that we +* can detect when the CPU clobbered our SPSR copy during a +* local exception. +*/ + val &= ~0xc ; + } + +return val; +} + +static inline u64 __fixup_spsr_el2_read (const struct kvm_cpu_context *ctxt,u64 val) +{ +if (__vcpu_el2_e2h_is_set (ctxt)) +return val; + +/* +* SPSR.M == 0 means the CPU has not touched the SPSR, so the +* register has still the value we saved on the last write. +*/ +if ((val &0xc ) ==0 ) +return ctxt->sys_regs[SPSR_EL2 ]; + +/* +* Otherwise there was a "local" exception on the CPU, +* which from the guest's point of view was being taken from +* EL2 to EL2, although it actually happened to be from +* EL1 to EL1. +* So we need to fix the .M field in SPSR, to make it look +* like EL2, which is what the guest would expect. +*/ +return (val & ~0x0c ) | CurrentEL_EL2; +} +static inline unsigned long vcpu_read_spsr (const struct kvm_vcpu *vcpu) {if (vcpu_mode_is_32bit (vcpu))return vcpu_read_spsr32 (vcpu); +if (unlikely (vcpu_mode_el2 (vcpu))) +return vcpu_read_sys_reg (vcpu,SPSR_EL2 ); +if (vcpu->arch.sysregs_loaded_on_cpu)return read_sysreg_el1 (SYS_SPSR );else
@@ -295,6 +335,11 @@ static inline void vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long v)return ; } +if (unlikely (vcpu_mode_el2 (vcpu))) { +vcpu_write_sys_reg (vcpu, v,SPSR_EL2 ); +return ; + } +if (vcpu->arch.sysregs_loaded_on_cpu)write_sysreg_el1 (v,SYS_SPSR );else
arch/arm64/kvm/sys_regs.c +21 -2
@@ -258,11 +258,14 @@ u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg) goto memory_read;/* -* ELR_EL2 is special cased for now. +* ELR_EL2 and SPSR_EL2 are special cased for now. */ switch (reg) {case ELR_EL2 :return read_sysreg_el1 (SYS_ELR ); +case SPSR_EL2 : + val =read_sysreg_el1 (SYS_SPSR ); +return __fixup_spsr_el2_read (&vcpu->arch.ctxt, val); }/*
@@ -319,6 +322,10 @@ void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, int reg)case ELR_EL2 :write_sysreg_el1 (val,SYS_ELR );return ; +case SPSR_EL2 : + val =__fixup_spsr_el2_write (&vcpu->arch.ctxt, val); +write_sysreg_el1 (val,SYS_SPSR ); +return ; }/* No EL1 counterpart? We're done here.? */
@@ -1589,6 +1596,18 @@ static bool access_sp_el1(struct kvm_vcpu *vcpu,return true; } +static bool access_spsr_el2 (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,SPSR_EL2 ); +else + p->regval =vcpu_read_sys_reg (vcpu,SPSR_EL2 ); + +return true; +} +/* * Architected system registers. * Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
@@ -1899,7 +1918,7 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_VTCR_EL2), access_rw, reset_val, VTCR_EL2, 0 }, { SYS_DESC(SYS_DACR32_EL2), NULL, reset_unknown, DACR32_EL2 }, - { SYS_DESC(SYS_SPSR_EL2), access_rw, reset_val, SPSR_EL2, 0 }, + { SYS_DESC(SYS_SPSR_EL2), access_spsr_el2, reset_val, SPSR_EL2, 0 }, { SYS_DESC(SYS_ELR_EL2), access_rw, reset_val, ELR_EL2, 0 }, { SYS_DESC(SYS_SP_EL1), access_sp_el1},
Signature · 1 line
2.20.1
Mark Rutland
reviewerRe: [PATCH v2 15/94] KVM: arm64: nv: Handle SPSR_EL2 specially
Quoted 23 lines · Marc Zyngier
SPSR_EL2needs special attention when running nested on ARMv8.3:If taking an exception while running at vEL2 (actually EL1), the HW will update the
SPSR_EL1register with the EL1 mode. We need to track this in order to make sure that accesses to the virtual view ofSPSR_EL2is correct.To do so, we place an illegal value in SPSR_EL1.M, and patch it accordingly if required when accessing it.
Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/include/asm/kvm_emulate.h | 45 ++++++++++++++++++++++++++++ arch/arm64/kvm/sys_regs.c | 23 ++++++++++++-- 2 files changed, 66 insertions(+), 2 deletions(-)arch/arm64/include/asm/kvm_emulate.h +0
-0@@ -277,11 +277,51 @@ static inline bool is_hyp_ctxt(const struct kvm_vcpu *vcpu)return __is_hyp_ctxt (&vcpu->arch.ctxt); }
arch/arm64/include/asm/kvm_emulate.h static inline bool is_hyp_ctxt(const struct kvm_vcpu *vcpu) · line 288
Quoted 10 lines
arch/arm64/include/asm/kvm_emulate.h +9
-0+static inline u64 __fixup_spsr_el2_write (struct kvm_cpu_context *ctxt,u64 val) +{ +if (!__vcpu_el2_e2h_is_set (ctxt)) { +/* +* Clear the .M field when writing SPSR to the CPU, so that we +* can detect when the CPU clobbered our SPSR copy during a +* local exception. +*/ + val &= ~0xc ;
Probably worth making that UL(0xc) so that we consistently treat the SPSRs as 64-bits.
Perhaps:
#define SPSR_EL2_M_3_2 UL(0xc)
... so that we can use it consistently below?
Quoted 9 lines
arch/arm64/include/asm/kvm_emulate.h +9
-0+ } + +return val; +} + +static inline u64 __fixup_spsr_el2_read (const struct kvm_cpu_context *ctxt,u64 val) +{ +if (__vcpu_el2_e2h_is_set (ctxt)) +return val;
arch/arm64/include/asm/kvm_emulate.h static inline bool is_hyp_ctxt(const struct kvm_vcpu *vcpu) · line 307
Quoted 10 lines
arch/arm64/include/asm/kvm_emulate.h +10
-0+ +/* +* SPSR.M == 0 means the CPU has not touched the SPSR, so the +* register has still the value we saved on the last write. +*/ +if ((val &0xc ) ==0 ) +return ctxt->sys_regs[SPSR_EL2 ]; + +/* +* Otherwise there was a "local" exception on the CPU,
Perhaps "exit-less" rather than "local"?
Thanks,
Mark.
Quoted 98 lines
arch/arm64/include/asm/kvm_emulate.h +17
-0+* which from the guest's point of view was being taken from +* EL2 to EL2, although it actually happened to be from +* EL1 to EL1. +* So we need to fix the .M field in SPSR, to make it look +* like EL2, which is what the guest would expect. +*/ +return (val & ~0x0c ) | CurrentEL_EL2; +} +static inline unsigned long vcpu_read_spsr (const struct kvm_vcpu *vcpu) {if (vcpu_mode_is_32bit (vcpu))return vcpu_read_spsr32 (vcpu); +if (unlikely (vcpu_mode_el2 (vcpu))) +return vcpu_read_sys_reg (vcpu,SPSR_EL2 ); +if (vcpu->arch.sysregs_loaded_on_cpu)return read_sysreg_el1 (SYS_SPSR );else @@ -295,6 +335,11 @@ static inline void vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long v)return ; } +if (unlikely (vcpu_mode_el2 (vcpu))) { +vcpu_write_sys_reg (vcpu, v,SPSR_EL2 ); +return ; + } +if (vcpu->arch.sysregs_loaded_on_cpu)write_sysreg_el1 (v,SYS_SPSR );else arch/arm64/kvm/sys_regs.c +21
-2@@ -258,11 +258,14 @@ u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg) goto memory_read;/* -* ELR_EL2 is special cased for now. +* ELR_EL2 and SPSR_EL2 are special cased for now. */ switch (reg) {case ELR_EL2 :return read_sysreg_el1 (SYS_ELR ); +case SPSR_EL2 : + val =read_sysreg_el1 (SYS_SPSR ); +return __fixup_spsr_el2_read (&vcpu->arch.ctxt, val); }/* @@ -319,6 +322,10 @@ void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, int reg)case ELR_EL2 :write_sysreg_el1 (val,SYS_ELR );return ; +case SPSR_EL2 : + val =__fixup_spsr_el2_write (&vcpu->arch.ctxt, val); +write_sysreg_el1 (val,SYS_SPSR ); +return ; }/* No EL1 counterpart? We're done here.? */ @@ -1589,6 +1596,18 @@ static bool access_sp_el1(struct kvm_vcpu *vcpu,return true; } +static bool access_spsr_el2 (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,SPSR_EL2 ); +else + p->regval =vcpu_read_sys_reg (vcpu,SPSR_EL2 ); + +return true; +} +/* * Architected system registers. * Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2 @@ -1899,7 +1918,7 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_VTCR_EL2), access_rw, reset_val, VTCR_EL2, 0 }, { SYS_DESC(SYS_DACR32_EL2), NULL, reset_unknown, DACR32_EL2 }, - { SYS_DESC(SYS_SPSR_EL2), access_rw, reset_val, SPSR_EL2, 0 }, + { SYS_DESC(SYS_SPSR_EL2), access_spsr_el2, reset_val, SPSR_EL2, 0 }, { SYS_DESC(SYS_ELR_EL2), access_rw, reset_val, ELR_EL2, 0 }, { SYS_DESC(SYS_SP_EL1), access_sp_el1},Signature · 6 lines
2.20.1 _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Marc Zyngier
maintainerPATCH v2 16/94 KVM: arm64: nv: Handle HCR_EL2.E2H specially
In reply to Marc ZyngierHCR_EL2.E2H is nasty, as a flip of this bit completely changes the way we deal with a lot of the state. So when the guest flips this bit (sysregs are live), do the put/load dance so that we have a consistent state.
Yes, this is slow. Don't do it.
--- arch/arm64/kvm/sys_regs.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
arch/arm64/kvm/sys_regs.c +20 -0
@@ -308,9 +308,24 @@ void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, int reg) goto memory_write;if (unlikely (get_el2_mapping (reg, &el1r, &xlate))) { +bool need_put_load; +if (!is_hyp_ctxt (vcpu)) goto memory_write; +/* +* HCR_EL2.E2H is nasty: it changes the way we interpret a +* lot of the EL2 state, so treat is as a full state +* transition. +*/ + need_put_load = ((reg ==HCR_EL2 ) && +vcpu_el2_e2h_is_set (vcpu) != !!(val &HCR_E2H )); + +if (need_put_load) { +preempt_disable (); +kvm_arch_vcpu_put (vcpu); + } +/* * Always store a copy of the write to memory to avoid having * to reverse-translate virtual EL2 system registers for a
@@ -318,6 +333,11 @@ void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, int reg)*/ __vcpu_sys_reg (vcpu, reg) = val; +if (need_put_load) { +kvm_arch_vcpu_load (vcpu,smp_processor_id ()); +preempt_enable (); + } +switch (reg) {case ELR_EL2 :write_sysreg_el1 (val,SYS_ELR );
Signature · 6 lines
2.20.1 _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Marc Zyngier
maintainerPATCH v2 17/94 KVM: arm64: nv: Save/Restore vEL2 sysregs
In reply to Marc ZyngierFrom: Andre Przywara <[email protected]>
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.
Split the current __sysreg_{save,restore}_el1_state() functions into handling common sysregs, then differentiate between the guest running in vEL2 and vEL1.
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.
[ Commit message reworked and many bug fixes applied by Marc Zyngier
and Christoffer Dall. ]
--- arch/arm64/kvm/hyp/sysreg-sr.c | 171 +++++++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 7 deletions(-)
arch/arm64/kvm/hyp/sysreg-sr.c +164 -7
@@ -11,6 +11,7 @@#include <asm/kvm_asm.h> #include <asm/kvm_emulate.h> #include <asm/kvm_hyp.h> +#include <asm/kvm_nested.h> /* * Non-VHE: Both host and guest must save everything.
@@ -40,11 +41,9 @@ static void __hyp_text __sysreg_save_user_state(struct kvm_cpu_context *ctxt) ctxt->sys_regs[TPIDRRO_EL0 ] =read_sysreg (tpidrro_el0); } -static void __hyp_text __sysreg_save_el1_state(struct kvm_cpu_context *ctxt) +static void __hyp_text __sysreg_save_vel1_state(struct kvm_cpu_context *ctxt) { - ctxt->sys_regs[CSSELR_EL1 ] =read_sysreg (csselr_el1); ctxt->sys_regs[SCTLR_EL1 ] =read_sysreg_el1 (SYS_SCTLR ); - ctxt->sys_regs[ACTLR_EL1 ] =read_sysreg (actlr_el1); ctxt->sys_regs[CPACR_EL1 ] =read_sysreg_el1 (SYS_CPACR ); ctxt->sys_regs[TTBR0_EL1 ] =read_sysreg_el1 (SYS_TTBR0 ); ctxt->sys_regs[TTBR1_EL1 ] =read_sysreg_el1 (SYS_TTBR1 );
@@ -58,14 +57,58 @@ static void __hyp_text __sysreg_save_el1_state(struct kvm_cpu_context *ctxt) ctxt->sys_regs[CONTEXTIDR_EL1 ] =read_sysreg_el1 (SYS_CONTEXTIDR ); ctxt->sys_regs[AMAIR_EL1 ] =read_sysreg_el1 (SYS_AMAIR ); ctxt->sys_regs[CNTKCTL_EL1 ] =read_sysreg_el1 (SYS_CNTKCTL ); - ctxt->sys_regs[PAR_EL1 ] =read_sysreg (par_el1); - ctxt->sys_regs[TPIDR_EL1 ] =read_sysreg (tpidr_el1); ctxt->gp_regs.sp_el1 =read_sysreg (sp_el1); ctxt->gp_regs.elr_el1 =read_sysreg_el1 (SYS_ELR ); ctxt->gp_regs.spsr[KVM_SPSR_EL1 ]=read_sysreg_el1 (SYS_SPSR ); } +static void __sysreg_save_vel2_state (struct kvm_cpu_context *ctxt) +{ + ctxt->sys_regs[ESR_EL2 ] =read_sysreg_el1 (SYS_ESR ); + ctxt->sys_regs[AFSR0_EL2 ] =read_sysreg_el1 (SYS_AFSR0 ); + ctxt->sys_regs[AFSR1_EL2 ] =read_sysreg_el1 (SYS_AFSR1 ); + ctxt->sys_regs[FAR_EL2 ] =read_sysreg_el1 (SYS_FAR ); + ctxt->sys_regs[MAIR_EL2 ] =read_sysreg_el1 (SYS_MAIR ); + ctxt->sys_regs[VBAR_EL2 ] =read_sysreg_el1 (SYS_VBAR ); + ctxt->sys_regs[CONTEXTIDR_EL2 ] =read_sysreg_el1 (SYS_CONTEXTIDR ); + ctxt->sys_regs[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)) { + ctxt->sys_regs[SCTLR_EL2 ] =read_sysreg_el1 (SYS_SCTLR ); + ctxt->sys_regs[CPTR_EL2 ] =read_sysreg_el1 (SYS_CPACR ); + ctxt->sys_regs[TTBR0_EL2 ] =read_sysreg_el1 (SYS_TTBR0 ); + ctxt->sys_regs[TTBR1_EL2 ] =read_sysreg_el1 (SYS_TTBR1 ); + ctxt->sys_regs[TCR_EL2 ] =read_sysreg_el1 (SYS_TCR ); + ctxt->sys_regs[CNTHCTL_EL2 ] =read_sysreg_el1 (SYS_CNTKCTL ); + } + + ctxt->sys_regs[SP_EL2 ] =read_sysreg (sp_el1); + ctxt->sys_regs[ELR_EL2 ] =read_sysreg_el1 (SYS_ELR ); + ctxt->sys_regs[SPSR_EL2 ] =__fixup_spsr_el2_read (ctxt,read_sysreg_el1 (SYS_SPSR )); +} + +static void __hyp_text __sysreg_save_el1_state(struct kvm_cpu_context *ctxt) +{ + ctxt->sys_regs[CSSELR_EL1 ] =read_sysreg (csselr_el1); + ctxt->sys_regs[ACTLR_EL1 ] =read_sysreg (actlr_el1); + ctxt->sys_regs[PAR_EL1 ] =read_sysreg (par_el1); + ctxt->sys_regs[TPIDR_EL1 ] =read_sysreg (tpidr_el1); + +if (unlikely (__is_hyp_ctxt (ctxt))) +__sysreg_save_vel2_state (ctxt); +else +__sysreg_save_vel1_state (ctxt); +} + static void __hyp_text __sysreg_save_el2_return_state(struct kvm_cpu_context *ctxt) { ctxt->gp_regs.regs.pc =read_sysreg_el2 (SYS_ELR );
@@ -113,10 +156,102 @@ static void __hyp_text __sysreg_restore_user_state(struct kvm_cpu_context *ctxt)write_sysreg (ctxt->sys_regs[TPIDRRO_EL0 ], tpidrro_el0); } -static void __hyp_text __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt) +static void __sysreg_restore_vel2_state (struct kvm_cpu_context *ctxt) { +u64 val; + +write_sysreg (read_cpuid_id (), vpidr_el2);write_sysreg (ctxt->sys_regs[MPIDR_EL1 ], vmpidr_el2); -write_sysreg (ctxt->sys_regs[CSSELR_EL1 ], csselr_el1); +write_sysreg_el1 (ctxt->sys_regs[MAIR_EL2 ],SYS_MAIR ); +write_sysreg_el1 (ctxt->sys_regs[VBAR_EL2 ],SYS_VBAR ); +write_sysreg_el1 (ctxt->sys_regs[CONTEXTIDR_EL2 ],SYS_CONTEXTIDR ); +write_sysreg_el1 (ctxt->sys_regs[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_regs[SCTLR_EL2 ],SYS_SCTLR ); +write_sysreg_el1 (ctxt->sys_regs[CPTR_EL2 ],SYS_CPACR ); +write_sysreg_el1 (ctxt->sys_regs[TTBR0_EL2 ],SYS_TTBR0 ); +write_sysreg_el1 (ctxt->sys_regs[TTBR1_EL2 ],SYS_TTBR1 ); +write_sysreg_el1 (ctxt->sys_regs[TCR_EL2 ],SYS_TCR ); +write_sysreg_el1 (ctxt->sys_regs[CNTHCTL_EL2 ],SYS_CNTKCTL ); + }else { +write_sysreg_el1 (translate_sctlr (ctxt->sys_regs[SCTLR_EL2 ]), +SYS_SCTLR ); +write_sysreg_el1 (translate_cptr (ctxt->sys_regs[CPTR_EL2 ]), +SYS_CPACR ); +write_sysreg_el1 (translate_ttbr0 (ctxt->sys_regs[TTBR0_EL2 ]), +SYS_TTBR0 ); +write_sysreg_el1 (translate_tcr (ctxt->sys_regs[TCR_EL2 ]), +SYS_TCR ); +write_sysreg_el1 (translate_cnthctl (ctxt->sys_regs[CNTHCTL_EL2 ]), +SYS_CNTKCTL ); + } + +/* +* These registers can be modified behind our back by a fault +* taken inside vEL2. Save them, always. +*/ +write_sysreg_el1 (ctxt->sys_regs[ESR_EL2 ],SYS_ESR ); +write_sysreg_el1 (ctxt->sys_regs[AFSR0_EL2 ],SYS_AFSR0 ); +write_sysreg_el1 (ctxt->sys_regs[AFSR1_EL2 ],SYS_AFSR1 ); +write_sysreg_el1 (ctxt->sys_regs[FAR_EL2 ],SYS_FAR ); +write_sysreg (ctxt->sys_regs[SP_EL2 ], sp_el1); +write_sysreg_el1 (ctxt->sys_regs[ELR_EL2 ],SYS_ELR ); + + val =__fixup_spsr_el2_write (ctxt, ctxt->sys_regs[SPSR_EL2 ]); +write_sysreg_el1 (val,SYS_SPSR ); +} + +static void __hyp_text __sysreg_restore_vel1_state(struct kvm_cpu_context *ctxt) +{ +u64 mpidr; + +if (has_vhe ()) { +struct kvm_vcpu *vcpu; + +/* +* We need to go from a context to a vcpu, but this is a +* complicated affair. +* +* On VHE, we should never be here with the host context as +* a parameter, so let's check and bail out if that's the +* case. +*/ +if (WARN_ON_ONCE (ctxt->__hyp_running_vcpu)) +return ; + +/* +* Now that we know for sure this is a guest context, we can +* extract the vcpu... +*/ + vcpu = container_of(ctxt, struct kvm_vcpu, arch.ctxt); + +if (nested_virt_in_use (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_regs[VPIDR_EL2 ], vpidr_el2); + +/* +* As we're restoring a nested guest, set the value +* provided by the guest hypervisor. +*/ + mpidr = ctxt->sys_regs[VMPIDR_EL2 ]; + }else { + mpidr = ctxt->sys_regs[MPIDR_EL1 ]; + } + }else { + mpidr = ctxt->sys_regs[MPIDR_EL1 ]; + } + +write_sysreg (mpidr, vmpidr_el2); +write_sysreg_el1 (ctxt->sys_regs[SCTLR_EL1 ],SYS_SCTLR );if (!cpus_have_const_cap (ARM64_WORKAROUND_SPECULATIVE_AT_NVHE )) {write_sysreg_el1 (ctxt->sys_regs[SCTLR_EL1 ],SYS_SCTLR );
@@ -171,6 +306,19 @@ static void __hyp_text __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)write_sysreg_el1 (ctxt->gp_regs.spsr[KVM_SPSR_EL1 ],SYS_SPSR ); } +static void __hyp_text __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt) +{ +write_sysreg (ctxt->sys_regs[CSSELR_EL1 ], csselr_el1); +write_sysreg (ctxt->sys_regs[ACTLR_EL1 ], actlr_el1); +write_sysreg (ctxt->sys_regs[PAR_EL1 ], par_el1); +write_sysreg (ctxt->sys_regs[TPIDR_EL1 ], tpidr_el1); + +if (__is_hyp_ctxt (ctxt)) +__sysreg_restore_vel2_state (ctxt); +else +__sysreg_restore_vel1_state (ctxt); +} + static void __hyp_text__sysreg_restore_el2_return_state (struct kvm_cpu_context *ctxt) {
@@ -327,6 +475,15 @@ void kvm_vcpu_put_sysregs(struct kvm_vcpu *vcpu)/* Restore host user state */ __sysreg_restore_user_state (host_ctxt); +/* +* If leaving a nesting guest, restore MPIDR_EL1 default view. It is +* slightly ugly to do it here, but the alternative is to penalize +* all non-nesting guests by forcing this on every load. Instead, we +* choose to only penalize nesting VMs. +*/ +if (nested_virt_in_use (vcpu)) +write_sysreg (read_cpuid_id (), vpidr_el2); + vcpu->arch.sysregs_loaded_on_cpu = false; }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 18/94 KVM: arm64: nv: Emulate PSTATE.M for a guest hypervisor
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
We 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.
--- arch/arm64/kvm/hyp/sysreg-sr.c | 43 ++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-)
arch/arm64/kvm/hyp/sysreg-sr.c +41 -2
@@ -109,10 +109,32 @@ static void __hyp_text __sysreg_save_el1_state(struct kvm_cpu_context *ctxt)__sysreg_save_vel1_state (ctxt); } +static u64 __hyp_text from_hw_pstate(const struct kvm_cpu_context *ctxt) +{ +u64 reg =read_sysreg_el2 (SYS_SPSR ); + +if (__is_hyp_ctxt (ctxt)) { +u64 mode = reg & (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 ; + } + +return (reg & ~(PSR_MODE_MASK |PSR_MODE32_BIT )) | mode; + } + +return reg; +} + static void __hyp_text __sysreg_save_el2_return_state(struct kvm_cpu_context *ctxt) { ctxt->gp_regs.regs.pc =read_sysreg_el2 (SYS_ELR ); - ctxt->gp_regs.regs.pstate =read_sysreg_el2 (SYS_SPSR ); + ctxt->gp_regs.regs.pstate =from_hw_pstate (ctxt);if (cpus_have_const_cap (ARM64_HAS_RAS_EXTN )) ctxt->sys_regs[DISR_EL1 ] =read_sysreg_s (SYS_VDISR_EL2 );
@@ -319,10 +341,27 @@ static void __hyp_text __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)__sysreg_restore_vel1_state (ctxt); } +/* Read the VCPU state's PSTATE, but translate (v)EL2 to EL1. */ +static u64 __hyp_text to_hw_pstate(const struct kvm_cpu_context *ctxt) +{ +u64 mode = ctxt->gp_regs.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->gp_regs.regs.pstate & ~(PSR_MODE_MASK |PSR_MODE32_BIT )) | mode; +} + static void __hyp_text__sysreg_restore_el2_return_state (struct kvm_cpu_context *ctxt) { -u64 pstate = ctxt->gp_regs.regs.pstate; +u64 pstate =to_hw_pstate (ctxt);u64 mode = pstate &PSR_AA32_MODE_MASK ;/*
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 19/94 KVM: arm64: nv: Trap EL1 VM register accesses in virtual EL2
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
When running in virtual EL2 mode, we actually run the hardware in EL1 and therefore have to use the EL1 registers to ensure correct operation.
By setting the HCR.TVM and HCR.TVRM we ensure that the virtual EL2 mode doesn't shoot itself in the foot when setting up what it believes to be a different mode's system register state (for example when preparing to switch to a VM).
We can leverage the existing sysregs infrastructure to support trapped accesses to these registers.
--- arch/arm64/kvm/hyp/switch.c | 4 ++++ arch/arm64/kvm/sys_regs.c | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-)
arch/arm64/kvm/hyp/switch.c +4 -0
@@ -149,6 +149,10 @@ static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu)if (cpus_have_const_cap (ARM64_WORKAROUND_CAVIUM_TX2_219_TVM )) hcr |=HCR_TVM ; +/* Trap VM sysreg accesses if an EL2 guest is not using VHE. */ +if (vcpu_mode_el2 (vcpu) && !vcpu_el2_e2h_is_set (vcpu)) + hcr |=HCR_TVM |HCR_TRVM ; +write_sysreg (hcr, hcr_el2);if (cpus_have_const_cap (ARM64_HAS_RAS_EXTN ) && (hcr &HCR_VSE ))
arch/arm64/kvm/sys_regs.c +15 -3
@@ -440,8 +440,15 @@ static bool access_dcsw(struct kvm_vcpu *vcpu,/* * Generic accessor for VM registers. Only called as long as HCR_TVM -* is set. If the guest enables the MMU, we stop trapping the VM -* sys_regs and leave it in complete control of the caches. +* is set. +* +* This is set in two cases: either (1) we're running at vEL2, or (2) +* we're running at EL1 and the guest has its MMU off. +* +* (1) TVM is always set, as we need to virtualise some of the VM +* registers for the guest hypervisor +* (2) Oncethe guest enables the MMU, we stop trapping the VM sys_regs +* and leave it in complete control of the caches. */ static bool access_vm_reg(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
@@ -451,7 +458,12 @@ static bool access_vm_reg(struct kvm_vcpu *vcpu,u64 val;int reg = r->reg; -BUG_ON (!p->is_write); +BUG_ON (!vcpu_mode_el2 (vcpu) && !p->is_write); + +if (!p->is_write) { + p->regval =vcpu_read_sys_reg (vcpu, reg); +return true; + }/* See the 32bit mapping in kvm_host.h */ if (p->is_aarch32)
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 20/94 KVM: arm64: nv: Trap 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.
--- arch/arm64/kvm/sys_regs.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-)
arch/arm64/kvm/sys_regs.c +28 -1
@@ -1628,6 +1628,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->arch.ctxt.gp_regs.elr_el1 = p->regval; +else + p->regval = vcpu->arch.ctxt.gp_regs.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->arch.ctxt.gp_regs.spsr[KVM_SPSR_EL1 ] = p->regval; +else + p->regval = vcpu->arch.ctxt.gp_regs.spsr[KVM_SPSR_EL1 ]; + +return true; +} +static bool access_spsr_el2 (struct kvm_vcpu *vcpu,struct sys_reg_params *p,const struct sys_reg_desc *r)
@@ -1783,6 +1807,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 },
@@ -1811,7 +1838,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.20.1
Marc Zyngier
maintainerPATCH v2 21/94 KVM: arm64: nv: Trap CPACR_EL1 access in virtual EL2
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
For the same reason we trap virtual memory register accesses in virtual EL2, we trap CPACR_EL1 access too; We allow the virtual EL2 mode to access EL1 system register state instead of the virtual EL2 one.
--- arch/arm64/include/asm/kvm_arm.h | 2 +- arch/arm64/kvm/hyp/switch.c | 2 ++ arch/arm64/kvm/sys_regs.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-)
arch/arm64/include/asm/kvm_arm.h +1 -1
@@ -266,7 +266,7 @@#define CPTR_EL2_TFP_SHIFT 10/* Hyp Coprocessor Trap Register */ -#define CPTR_EL2_TCPAC (1 << 31) +#define CPTR_EL2_TCPAC (1U << 31)#define CPTR_EL2_TTA (1 << 20)#define CPTR_EL2_TFP (1 << CPTR_EL2_TFP_SHIFT)#define CPTR_EL2_TZ (1 << 8)
arch/arm64/kvm/hyp/switch.c +2 -0
@@ -105,6 +105,8 @@ static void activate_traps_vhe(struct kvm_vcpu *vcpu) val &= ~CPACR_EL1_FPEN ;__activate_traps_fpsimd32 (vcpu); } +if (vcpu_mode_el2 (vcpu) && !vcpu_el2_e2h_is_set (vcpu)) + val |=CPTR_EL2_TCPAC ;write_sysreg (val, cpacr_el1);
arch/arm64/kvm/sys_regs.c +1 -1
@@ -1795,7 +1795,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {ID_UNALLOCATED (7 ,7 ), { SYS_DESC(SYS_SCTLR_EL1), access_vm_reg, reset_val, SCTLR_EL1, 0x00C50078 }, - { SYS_DESC(SYS_CPACR_EL1), NULL, reset_val, CPACR_EL1, 0 }, + { SYS_DESC(SYS_CPACR_EL1), access_rw, reset_val, CPACR_EL1, 0 }, { SYS_DESC(SYS_ZCR_EL1), NULL, reset_val, ZCR_EL1, 0, .visibility = sve_visibility }, { SYS_DESC(SYS_TTBR0_EL1), access_vm_reg, reset_unknown, TTBR0_EL1 }, { SYS_DESC(SYS_TTBR1_EL1), access_vm_reg, reset_unknown, TTBR1_EL1 },
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 22/94 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, struct kvm_run *run)static int handle_smc (struct kvm_vcpu *vcpu,struct kvm_run *run) { +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, struct kvm_run *run)* * 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_skip_instr (vcpu,kvm_vcpu_trap_il_is32bit (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_skip_instr (vcpu,kvm_vcpu_trap_il_is32bit (vcpu)); -return 1 ; + +return ret; }/*
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 23/94 KVM: arm64: nv: Respect virtual HCR_EL2.TWX setting
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
Forward exceptions due to WFI or WFE instructions to the virtual EL2 if they are not coming from the virtual EL2 and virtual HCR_EL2.TWX is set.
--- arch/arm64/include/asm/kvm_nested.h | 2 ++ arch/arm64/kvm/Makefile | 1 + arch/arm64/kvm/handle_exit.c | 11 +++++++- arch/arm64/kvm/nested.c | 40 +++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/kvm/nested.c
arch/arm64/include/asm/kvm_nested.h +2 -0
@@ -10,4 +10,6 @@ static inline bool nested_virt_in_use(const struct kvm_vcpu *vcpu) test_bit(KVM_ARM_VCPU_HAS_EL2, vcpu->arch.features); } +int handle_wfx_nested (struct kvm_vcpu *vcpu,bool is_wfe); +#endif /* __ARM64_KVM_NESTED_H */
arch/arm64/kvm/Makefile +1 -0
@@ -38,4 +38,5 @@ kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/irqchip.o kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/arch_timer.o kvm-$(CONFIG_KVM_ARM_PMU) += $(KVM)/arm/pmu.o +kvm-$(CONFIG_KVM_ARM_HOST) += nested.o kvm-$(CONFIG_KVM_ARM_HOST) += emulate-nested.o
arch/arm64/kvm/handle_exit.c +10 -1
@@ -120,7 +120,16 @@ static int handle_no_fpsimd(struct kvm_vcpu *vcpu, struct kvm_run *run)*/ static int kvm_handle_wfx (struct kvm_vcpu *vcpu,struct kvm_run *run) { -if (kvm_vcpu_get_hsr (vcpu) & ESR_ELx_WFx_ISS_WFE) { +bool is_wfe = !!(kvm_vcpu_get_hsr (vcpu) & ESR_ELx_WFx_ISS_WFE); + +if (nested_virt_in_use (vcpu)) { +int ret =handle_wfx_nested (vcpu, is_wfe); + +if (ret != -EINVAL ) +return ret; + } + +if (is_wfe) {trace_kvm_wfx_arm64 (*vcpu_pc (vcpu), true); vcpu->stat.wfe_exit_stat++;kvm_vcpu_on_spin (vcpu,vcpu_mode_priv (vcpu));
arch/arm64/kvm/nested.c new file +40 -0
@@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +* Copyright (C) 2017 - Columbia University and Linaro Ltd. +* Author: Jintack Lim <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <linux/kvm.h> +#include <linux/kvm_host.h> + +#include <asm/kvm_emulate.h> + +/* +* Inject wfx to the virtual EL2 if this is not from the virtual EL2 and +* the virtual HCR_EL2.TWX is set. Otherwise, let the host hypervisor +* handle this. +*/ +int handle_wfx_nested (struct kvm_vcpu *vcpu,bool is_wfe) +{ +u64 hcr_el2 =__vcpu_sys_reg (vcpu,HCR_EL2 ); + +if (vcpu_mode_el2 (vcpu)) +return -EINVAL ; + +if ((is_wfe && (hcr_el2 &HCR_TWE )) || (!is_wfe && (hcr_el2 &HCR_TWI ))) +return kvm_inject_nested_sync (vcpu,kvm_vcpu_get_hsr (vcpu)); + +return -EINVAL ; +}
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 24/94 KVM: arm64: nv: Respect virtual CPTR_EL2.{TFP,FPEN} settings
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
Forward traps due to FP/ASIMD register accesses to the virtual EL2 if virtual CPTR_EL2.TFP is set (with HCR_EL2.E2H == 0) or CPTR_EL2.FPEN is configure to do so (with HCR_EL2.E2h == 1).
[maz: account for HCR_EL2.E2H when testing for TFP/FPEN, with
all the hard work actually being done by Chase Conklin]
--- arch/arm64/include/asm/kvm_emulate.h | 26 ++++++++++++++++++++++++++ arch/arm64/kvm/handle_exit.c | 16 ++++++++++++---- arch/arm64/kvm/hyp/switch.c | 11 +++++++++-- 3 files changed, 47 insertions(+), 6 deletions(-)
arch/arm64/include/asm/kvm_emulate.h +26 -0
@@ -11,12 +11,14 @@#ifndef __ARM64_KVM_EMULATE_H__#define __ARM64_KVM_EMULATE_H__ +#include <linux/bitfield.h> #include <linux/kvm_host.h> #include <asm/debug-monitors.h> #include <asm/esr.h> #include <asm/kvm_arm.h> #include <asm/kvm_hyp.h> +#include <asm/kvm_nested.h> #include <asm/ptrace.h> #include <asm/cputype.h> #include <asm/virt.h>
@@ -392,6 +394,30 @@ static inline bool vcpu_mode_priv(const struct kvm_vcpu *vcpu)return mode != PSR_MODE_EL0t; } +static inline bool guest_hyp_fpsimd_traps_enabled (const struct kvm_vcpu *vcpu) +{ +u64 val; + +if (!nested_virt_in_use (vcpu)) +return false; + + val =vcpu_read_sys_reg (vcpu,CPTR_EL2 ); + +if (!vcpu_el2_e2h_is_set (vcpu)) +return (val &CPTR_EL2_TFP ); + +switch (FIELD_GET (CPACR_EL1_FPEN , val)) { +case 0b00 : +case 0b10 : +return true; +case 0b01 : +return vcpu_el2_tge_is_set (vcpu) && !vcpu_mode_el2 (vcpu); +case 0b11 : +default :/* GCC is dumb */ +return false; + } +} +static inline u32 kvm_vcpu_get_hsr (const struct kvm_vcpu *vcpu) {return vcpu->arch.fault.esr_el2;
arch/arm64/kvm/handle_exit.c +12 -4
@@ -97,11 +97,19 @@ static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run) }/* -* Guest access to FP/ASIMD registers are routed to this handler only -* when the system doesn't support FP/ASIMD. +* This handles the cases where the system does not support FP/ASIMD or when +* we are running nested virtualization and the guest hypervisor is trapping +* FP/ASIMD accesses by its guest guest. +* +* All other handling of guest vs. host FP/ASIMD register state is handled in +* fixup_guest_exit(). */ -static int handle_no_fpsimd (struct kvm_vcpu *vcpu,struct kvm_run *run) +static int kvm_handle_fpasimd (struct kvm_vcpu *vcpu,struct kvm_run *run) { +if (guest_hyp_fpsimd_traps_enabled (vcpu)) +return kvm_inject_nested_sync (vcpu,kvm_vcpu_get_hsr (vcpu)); + +/* This is the case when the system doesn't support FP/ASIMD. */ kvm_inject_undefined (vcpu);return 1 ; }
@@ -270,7 +278,7 @@ static exit_handle_fn arm_exit_handlers[] = { [ESR_ELx_EC_BREAKPT_LOW]= kvm_handle_guest_debug, [ESR_ELx_EC_BKPT32] = kvm_handle_guest_debug, [ESR_ELx_EC_BRK64] = kvm_handle_guest_debug, - [ESR_ELx_EC_FP_ASIMD] = handle_no_fpsimd, + [ESR_ELx_EC_FP_ASIMD] = kvm_handle_fpasimd, [ESR_ELx_EC_PAC] = kvm_handle_ptrauth, };
arch/arm64/kvm/hyp/switch.c +9 -2
@@ -371,11 +371,18 @@ static bool __hyp_text __hyp_handle_fpsimd(struct kvm_vcpu *vcpu) hsr_ec != ESR_ELx_EC_SVE)return false; -/* Don't handle SVE traps for non-SVE vcpus here: */ -if (!sve_guest) +/* +* Don't handle SVE traps for non-SVE vcpus here. This +* includes NV guests for the time beeing. +*/ +if (!sve_guest) {if (hsr_ec != ESR_ELx_EC_FP_ASIMD)return false; +if (guest_hyp_fpsimd_traps_enabled (vcpu)) +return false; + } +/* Valid trap. Switch the context: */ if (vhe) {
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 25/94 KVM: arm64: nv: Respect the virtual HCR_EL2.NV bit setting
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
Forward traps due to HCR_EL2.NV bit to the virtual EL2 if they are not coming from the virtual EL2 and the virtual HCR_EL2.NV bit is set.
In addition to EL2 register accesses, setting NV bit will also make EL12 register accesses trap to EL2. To emulate this for the virtual EL2, forword traps due to EL12 register accessses to the virtual EL2 if the virtual HCR_EL2.NV bit is set.
This is for recursive nested virtualization.
--- arch/arm64/include/asm/kvm_arm.h | 1 + arch/arm64/include/asm/kvm_nested.h | 2 ++ arch/arm64/kvm/emulate-nested.c | 28 ++++++++++++++++++++++++++++ arch/arm64/kvm/handle_exit.c | 7 +++++++ arch/arm64/kvm/sys_regs.c | 21 +++++++++++++++++++++ 5 files changed, 59 insertions(+)
arch/arm64/include/asm/kvm_arm.h +1 -0
@@ -13,6 +13,7 @@/* Hyp Configuration Register (HCR) bits */ #define HCR_FWB (UL(1) << 46) +#define HCR_NV (UL(1) << 42)#define HCR_API (UL(1) << 41)#define HCR_APK (UL(1) << 40)#define HCR_TEA (UL(1) << 37)
arch/arm64/include/asm/kvm_nested.h +2 -0
@@ -11,5 +11,7 @@ static inline bool nested_virt_in_use(const struct kvm_vcpu *vcpu) }int handle_wfx_nested (struct kvm_vcpu *vcpu,bool is_wfe); +extern bool forward_traps (struct kvm_vcpu *vcpu,u64 control_bit); +extern bool forward_nv_traps (struct kvm_vcpu *vcpu);#endif /* __ARM64_KVM_NESTED_H */
arch/arm64/kvm/emulate-nested.c +28 -0
@@ -24,6 +24,27 @@#include "trace.h" +bool forward_traps (struct kvm_vcpu *vcpu,u64 control_bit) +{ +bool control_bit_set; + +if (!nested_virt_in_use (vcpu)) +return false; + + control_bit_set =__vcpu_sys_reg (vcpu,HCR_EL2 ) & control_bit; +if (!vcpu_mode_el2 (vcpu) && control_bit_set) { +kvm_inject_nested_sync (vcpu,kvm_vcpu_get_hsr (vcpu)); +return true; + } +return false; +} + +bool forward_nv_traps (struct kvm_vcpu *vcpu) +{ +return forward_traps (vcpu,HCR_NV ); +} + +/* This is borrowed from get_except_vector in inject_fault.c */ static u64 get_el2_except_vector (struct kvm_vcpu *vcpu,enum exception_type type)
@@ -55,6 +76,13 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu)u64 spsr, elr, mode;bool direct_eret; +/* +* Forward this trap to the virtual EL2 if the virtual +* HCR_EL2.NV bit is set and this is coming from !EL2. +*/ +if (forward_nv_traps (vcpu)) +return ; +/* * Going through the whole put/load motions is a waste of time * if this is a VHE guest hypervisor returning to its own
arch/arm64/kvm/handle_exit.c +7 -0
@@ -65,6 +65,13 @@ static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run) {int ret; +/* +* Forward this trapped smc instruction to the virtual EL2 if +* the guest has asked for it. +*/ +if (forward_traps (vcpu,HCR_TSC )) +return 1 ; +/* * "If an SMC instruction executed at Non-secure EL1 is * trapped to EL2 because HCR_EL2.TSC is 1, the exception is a
arch/arm64/kvm/sys_regs.c +21 -0
@@ -392,10 +392,19 @@ static u32 get_ccsidr(u32 csselr)return ccsidr; } +static bool el12_reg (struct sys_reg_params *p) +{ +/* All *_EL12 registers have Op1=5. */ +return (p->Op1 ==5 ); +} +static bool access_rw (struct kvm_vcpu *vcpu,struct sys_reg_params *p,const struct sys_reg_desc *r) { +if (el12_reg (p) &&forward_nv_traps (vcpu)) +return false; +if (p->is_write)vcpu_write_sys_reg (vcpu, p->regval, r->reg);else
@@ -458,6 +467,9 @@ static bool access_vm_reg(struct kvm_vcpu *vcpu,u64 val;int reg = r->reg; +if (el12_reg (p) &&forward_nv_traps (vcpu)) +return false; +BUG_ON (!vcpu_mode_el2 (vcpu) && !p->is_write);if (!p->is_write) {
@@ -1632,6 +1644,9 @@ static bool access_elr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) { + if (el12_reg(p) && forward_nv_traps(vcpu)) + return false; +if (p->is_write) vcpu->arch.ctxt.gp_regs.elr_el1 = p->regval;else
@@ -1644,6 +1659,9 @@ static bool access_spsr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) { + if (el12_reg(p) && forward_nv_traps(vcpu)) + return false; +if (p->is_write) vcpu->arch.ctxt.gp_regs.spsr[KVM_SPSR_EL1 ] = p->regval;else
@@ -1656,6 +1674,9 @@ static bool access_spsr_el2(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) { + if (el12_reg(p) && forward_nv_traps(vcpu)) + return false; +if (p->is_write)vcpu_write_sys_reg (vcpu, p->regval,SPSR_EL2 );else
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 26/94 KVM: arm64: nv: Respect virtual HCR_EL2.TVM and TRVM settings
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
Forward the EL1 virtual memory register traps to the virtual EL2 if they are not coming from the virtual EL2 and the virtual HCR_EL2.TVM or TRVM bit is set.
This is for recursive nested virtualization.
--- arch/arm64/kvm/sys_regs.c | 7 +++++++ 1 file changed, 7 insertions(+)
arch/arm64/kvm/sys_regs.c +7 -0
@@ -470,6 +470,13 @@ static bool access_vm_reg(struct kvm_vcpu *vcpu,if (el12_reg (p) &&forward_nv_traps (vcpu))return false; +if (!el12_reg (p)) { +u64 bit = p->is_write ?HCR_TVM :HCR_TRVM ; + +if (forward_traps (vcpu, bit)) +return false; + } +BUG_ON (!vcpu_mode_el2 (vcpu) && !p->is_write);if (!p->is_write) {
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 27/94 KVM: arm64: nv: Respect the virtual HCR_EL2.NV1 bit setting
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
Forward ELR_EL1, SPSR_EL1 and VBAR_EL1 traps to the virtual EL2 if the virtual HCR_EL2.NV bit is set.
This is for recursive nested virtualization.
--- arch/arm64/include/asm/kvm_arm.h | 1 + arch/arm64/kvm/sys_regs.c | 28 +++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kvm_arm.h +1 -0
@@ -13,6 +13,7 @@/* Hyp Configuration Register (HCR) bits */ #define HCR_FWB (UL(1) << 46) +#define HCR_NV1 (UL(1) << 43)#define HCR_NV (UL(1) << 42)#define HCR_API (UL(1) << 41)#define HCR_APK (UL(1) << 40)
arch/arm64/kvm/sys_regs.c +27 -1
@@ -424,6 +424,22 @@ static bool access_wi(struct kvm_vcpu *vcpu,return true; } +/* This function is to support the recursive nested virtualization */ +static bool forward_nv1_traps (struct kvm_vcpu *vcpu,struct sys_reg_params *p) +{ +return forward_traps (vcpu,HCR_NV1 ); +} + +static bool access_vbar_el1 (struct kvm_vcpu *vcpu, +struct sys_reg_params *p, +const struct sys_reg_desc *r) +{ +if (forward_nv1_traps (vcpu, p)) +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). */
@@ -1647,6 +1663,7 @@ 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)
@@ -1654,6 +1671,9 @@ static bool access_elr(struct kvm_vcpu *vcpu,if (el12_reg (p) &&forward_nv_traps (vcpu))return false; +if (!el12_reg (p) &&forward_nv1_traps (vcpu, p)) +return false; +if (p->is_write) vcpu->arch.ctxt.gp_regs.elr_el1 = p->regval;else
@@ -1669,6 +1689,9 @@ static bool access_spsr(struct kvm_vcpu *vcpu,if (el12_reg (p) &&forward_nv_traps (vcpu))return false; +if (!el12_reg (p) &&forward_nv1_traps (vcpu, p)) +return false; +if (p->is_write) vcpu->arch.ctxt.gp_regs.spsr[KVM_SPSR_EL1 ] = p->regval;else
@@ -1684,6 +1707,9 @@ static bool access_spsr_el2(struct kvm_vcpu *vcpu,if (el12_reg (p) &&forward_nv_traps (vcpu))return false; +if (!el12_reg (p) &&forward_nv1_traps (vcpu, p)) +return false; +if (p->is_write)vcpu_write_sys_reg (vcpu, p->regval,SPSR_EL2 );else
@@ -1866,7 +1892,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), access_rw, reset_val, VBAR_EL1, 0 }, + { SYS_DESC(SYS_VBAR_EL1), access_vbar_el1, 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.20.1
Marc Zyngier
maintainerPATCH v2 28/94 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.
--- arch/arm64/kvm/sys_regs.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
arch/arm64/kvm/sys_regs.c +17 -0
@@ -2057,6 +2057,23 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_CNTVOFF_EL2), access_rw, reset_val, CNTVOFF_EL2, 0 }, { SYS_DESC(SYS_CNTHCTL_EL2), access_rw, reset_val, CNTHCTL_EL2, 0 }, + { SYS_DESC(SYS_SCTLR_EL12), access_vm_reg, reset_val, SCTLR_EL1, 0x00C50078 }, + { SYS_DESC(SYS_CPACR_EL12), access_rw, reset_val, CPACR_EL1, 0 }, + { SYS_DESC(SYS_TTBR0_EL12), access_vm_reg, reset_unknown, TTBR0_EL1 }, + { SYS_DESC(SYS_TTBR1_EL12), access_vm_reg, reset_unknown, TTBR1_EL1 }, + { SYS_DESC(SYS_TCR_EL12), access_vm_reg, reset_val, TCR_EL1, 0 }, + { SYS_DESC(SYS_SPSR_EL12), access_spsr}, + { SYS_DESC(SYS_ELR_EL12), access_elr}, + { SYS_DESC(SYS_AFSR0_EL12), access_vm_reg, reset_unknown, AFSR0_EL1 }, + { SYS_DESC(SYS_AFSR1_EL12), access_vm_reg, reset_unknown, AFSR1_EL1 }, + { SYS_DESC(SYS_ESR_EL12), access_vm_reg, reset_unknown, ESR_EL1 }, + { SYS_DESC(SYS_FAR_EL12), access_vm_reg, reset_unknown, FAR_EL1 }, + { SYS_DESC(SYS_MAIR_EL12), access_vm_reg, reset_unknown, MAIR_EL1 }, + { SYS_DESC(SYS_AMAIR_EL12), access_vm_reg, reset_amair_el1, AMAIR_EL1 }, + { SYS_DESC(SYS_VBAR_EL12), access_rw, reset_val, VBAR_EL1, 0 }, + { SYS_DESC(SYS_CONTEXTIDR_EL12), access_vm_reg, reset_val, CONTEXTIDR_EL1, 0 }, + { SYS_DESC(SYS_CNTKCTL_EL12), access_rw, reset_val, CNTKCTL_EL1, 0 }, + { SYS_DESC(SYS_SP_EL2), NULL, reset_unknown, SP_EL2 }, };
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 29/94 KVM: arm64: nv: Forward debug traps to the nested guest
In reply to Marc ZyngierOn handling a debug trap, check whether we need to forward it to the guest before handling it.
--- arch/arm64/include/asm/kvm_nested.h | 2 ++ arch/arm64/kvm/emulate-nested.c | 9 +++++++-- arch/arm64/kvm/sys_regs.c | 3 +++ 3 files changed, 12 insertions(+), 2 deletions(-)
arch/arm64/include/asm/kvm_nested.h +2 -0
@@ -11,6 +11,8 @@ static inline bool nested_virt_in_use(const struct kvm_vcpu *vcpu) }int handle_wfx_nested (struct kvm_vcpu *vcpu,bool is_wfe); +extern bool __forward_traps (struct kvm_vcpu *vcpu,unsigned int reg, +u64 control_bit);extern bool forward_traps (struct kvm_vcpu *vcpu,u64 control_bit);extern bool forward_nv_traps (struct kvm_vcpu *vcpu);
arch/arm64/kvm/emulate-nested.c +7 -2
@@ -24,14 +24,14 @@#include "trace.h" -bool forward_traps (struct kvm_vcpu *vcpu,u64 control_bit) +bool __forward_traps (struct kvm_vcpu *vcpu,unsigned int reg,u64 control_bit) {bool control_bit_set;if (!nested_virt_in_use (vcpu))return false; - control_bit_set =__vcpu_sys_reg (vcpu,HCR_EL2 ) & control_bit; + control_bit_set =__vcpu_sys_reg (vcpu, reg) & control_bit;if (!vcpu_mode_el2 (vcpu) && control_bit_set) {kvm_inject_nested_sync (vcpu,kvm_vcpu_get_hsr (vcpu));return true;
@@ -39,6 +39,11 @@ bool forward_traps(struct kvm_vcpu *vcpu, u64 control_bit)return false; } +bool forward_traps (struct kvm_vcpu *vcpu,u64 control_bit) +{ +return __forward_traps (vcpu,HCR_EL2 , control_bit); +} +bool forward_nv_traps (struct kvm_vcpu *vcpu) {return forward_traps (vcpu,HCR_NV );
arch/arm64/kvm/sys_regs.c +3 -0
@@ -672,6 +672,9 @@ static bool trap_debug_regs(struct kvm_vcpu *vcpu,struct sys_reg_params *p, const struct sys_reg_desc *r) { + if (__forward_traps(vcpu, MDCR_EL2, MDCR_EL2_TDA | MDCR_EL2_TDE)) + return false; +access_rw (vcpu ,p ,r );if (p->is_write) vcpu->arch.flags |=KVM_ARM64_DEBUG_DIRTY ;
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 30/94 KVM: arm64: nv: Configure HCR_EL2 for nested virtualization
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
We enable nested virtualization by setting the HCR NV and NV1 bit.
When the virtual E2H bit is set, we can support EL2 register accesses via EL1 registers from the virtual EL2 by doing trap-and-emulate. A better alternative, however, is to allow the virtual EL2 to access EL2 register states without trap. This can be easily achieved by not traping EL1 registers since those registers already have EL2 register states.
--- arch/arm64/kvm/hyp/switch.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-)
arch/arm64/kvm/hyp/switch.c +33 -3
@@ -151,9 +151,39 @@ static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu)if (cpus_have_const_cap (ARM64_WORKAROUND_CAVIUM_TX2_219_TVM )) hcr |=HCR_TVM ; -/* Trap VM sysreg accesses if an EL2 guest is not using VHE. */ -if (vcpu_mode_el2 (vcpu) && !vcpu_el2_e2h_is_set (vcpu)) - hcr |=HCR_TVM |HCR_TRVM ; +if (is_hyp_ctxt (vcpu)) { + hcr |=HCR_NV ; + +if (!vcpu_el2_e2h_is_set (vcpu)) { +/* +* For a guest hypervisor on v8.0, trap and emulate +* the EL1 virtual memory control register accesses. +*/ + hcr |=HCR_TVM |HCR_TRVM |HCR_NV1 ; + }else { +/* +* For a guest hypervisor on v8.1 (VHE), allow to +* access the EL1 virtual memory control registers +* natively. These accesses are to access EL2 register +* states. +* Note that we still need to respect the virtual +* HCR_EL2 state. +*/ +u64 vhcr_el2 =__vcpu_sys_reg (vcpu,HCR_EL2 ); + +/* +* We already set TVM to handle set/way cache maint +* ops traps, this somewhat collides with the nested +* virt trapping for nVHE. So turn this off for now +* here, in the hope that VHE guests won't ever do this. +* TODO: find out whether it's worth to support both +* cases at the same time. +*/ + hcr &= ~HCR_TVM ; + + hcr |= vhcr_el2 & (HCR_TVM |HCR_TRVM ); + } + }write_sysreg (hcr, hcr_el2);
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 31/94 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 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kvm_mmu.h +5 -1
@@ -88,6 +88,7 @@ alternative_cb_end#include <asm/cacheflush.h> #include <asm/mmu_context.h> #include <asm/pgtable.h> +#include <asm/kvm_emulate.h> void kvm_update_va_mask (struct alt_instr *alt,__le32 *origptr,__le32 *updptr,int nr_inst);
@@ -305,7 +306,10 @@ struct kvm;static inline bool vcpu_has_cache_enabled (struct kvm_vcpu *vcpu) { -return (vcpu_read_sys_reg (vcpu,SCTLR_EL1 ) &0b101 ) ==0b101 ; +if (vcpu_mode_el2 (vcpu)) +return (__vcpu_sys_reg (vcpu,SCTLR_EL2 ) &0b101 ) ==0b101 ; +else +return (vcpu_read_sys_reg (vcpu,SCTLR_EL1 ) &0b101 ) ==0b101 ; }static inline void __clean_dcache_guest_page (kvm_pfn_t pfn,unsigned long size)
Signature · 1 line
2.20.1
Mark Rutland
reviewerRe: [PATCH v2 31/94] KVM: arm64: nv: Only toggle cache for virtual EL2 when SCTLR_EL2 changes
Quoted 32 lines · Marc Zyngier
From: Christoffer Dall <[email protected]>
So far we were flushing almost the entire universe whenever a VM would load/unload the
SCTLR_EL1and 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_EL2when 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).Signed-off-by Christoffer Dall <[email protected]>Signed-off-by Jintack Lim <[email protected]>Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/include/asm/kvm_mmu.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)arch/arm64/include/asm/kvm_mmu.h +1
-0@@ -88,6 +88,7 @@ alternative_cb_end#include <asm/cacheflush.h> #include <asm/mmu_context.h> #include <asm/pgtable.h> +#include <asm/kvm_emulate.h> void kvm_update_va_mask (struct alt_instr *alt,__le32 *origptr,__le32 *updptr,int nr_inst);
arch/arm64/include/asm/kvm_mmu.h struct kvm; · line 313
Quoted 9 lines
arch/arm64/include/asm/kvm_mmu.h +4
-1@@ -305,7 +306,10 @@ struct kvm;static inline bool vcpu_has_cache_enabled (struct kvm_vcpu *vcpu) { -return (vcpu_read_sys_reg (vcpu,SCTLR_EL1 ) &0b101 ) ==0b101 ; +if (vcpu_mode_el2 (vcpu)) +return (__vcpu_sys_reg (vcpu,SCTLR_EL2 ) &0b101 ) ==0b101 ; +else +return (vcpu_read_sys_reg (vcpu,SCTLR_EL1 ) &0b101 ) ==0b101 ; }
How about:
static bool vcpu_has_cache_enabled(struct kvm_vcpu *vcpu)
{
unsigned long cm = SCTLR_ELx_C | SCTLR_ELx_M;
unsigned long sctlr;
if (vcpu_mode_el2(vcpu))
sctlr = __vcpu_sys_reg(vcpu, SCTLR_EL2);
else
sctlr = vcpu_read_sys_reg(vcpu, SCTLR_EL1);
return (sctlr & cm) == cm;
}... to avoid duplication and make it clearer which fields we're accessing.
Thanks,
Mark.
Quoted 9 lines
arch/arm64/include/asm/kvm_mmu.h +0
-0static inline void __clean_dcache_guest_page (kvm_pfn_t pfn,unsigned long size)Signature · 6 lines
2.20.1 _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Marc Zyngier
maintainerPATCH v2 32/94 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.
--- arch/arm64/include/asm/kvm_nested.h | 6 ++ arch/arm64/include/asm/sysreg.h | 11 +++ arch/arm64/kvm/nested.c | 115 ++++++++++++++++++++++++++++ arch/arm64/kvm/sys_regs.c | 5 +- 4 files changed, 136 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kvm_nested.h +6 -0
@@ -16,4 +16,10 @@ extern bool __forward_traps(struct kvm_vcpu *vcpu, unsigned int reg,extern bool forward_traps (struct kvm_vcpu *vcpu,u64 control_bit);extern bool forward_nv_traps (struct kvm_vcpu *vcpu); +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/include/asm/sysreg.h +11 -0
@@ -646,6 +646,9 @@#define ID_AA64PFR0_CSV3_SHIFT 60#define ID_AA64PFR0_CSV2_SHIFT 56#define ID_AA64PFR0_DIT_SHIFT 48 +#define ID_AA64PFR0_AMU_SHIFT 44 +#define ID_AA64PFR0_MPAM_SHIFT 40 +#define ID_AA64PFR0_SEL2_SHIFT 36#define ID_AA64PFR0_SVE_SHIFT 32#define ID_AA64PFR0_RAS_SHIFT 28#define ID_AA64PFR0_GIC_SHIFT 24
@@ -667,7 +670,9 @@#define ID_AA64PFR0_EL0_32BIT_64BIT 0x2/* id_aa64pfr1 */ +#define ID_AA64PFR1_MTE_SHIFT 8#define ID_AA64PFR1_SSBS_SHIFT 4 +#define ID_AA64PFR1_BT_SHIFT 0#define ID_AA64PFR1_SSBS_PSTATE_NI 0#define ID_AA64PFR1_SSBS_PSTATE_ONLY 1
@@ -721,6 +726,8 @@#endif /* id_aa64mmfr1 */ +#define ID_AA64MMFR1_XNX_SHIFT 28 +#define ID_AA64MMFR1_SpecSEI_SHIFT 24#define ID_AA64MMFR1_PAN_SHIFT 20#define ID_AA64MMFR1_LOR_SHIFT 16#define ID_AA64MMFR1_HPD_SHIFT 12
@@ -733,9 +740,13 @@/* id_aa64mmfr2 */ #define ID_AA64MMFR2_E0PD_SHIFT 60 +#define ID_AA64MMFR2_EVT_SHIFT 56 +#define ID_AA64MMFR2_BBM_SHIFT 52#define ID_AA64MMFR2_FWB_SHIFT 40#define ID_AA64MMFR2_AT_SHIFT 32 +#define ID_AA64MMFR2_ST_SHIFT 28#define ID_AA64MMFR2_NV_SHIFT 24 +#define ID_AA64MMFR2_CCIDX_SHIFT 20#define ID_AA64MMFR2_LVA_SHIFT 16#define ID_AA64MMFR2_IESB_SHIFT 12#define ID_AA64MMFR2_LSM_SHIFT 8
arch/arm64/kvm/nested.c +115 -0
124 lines not on this page · raw message
arch/arm64/kvm/sys_regs.c +4 -1
@@ -1414,7 +1414,10 @@ static bool access_id_reg(struct kvm_vcpu *vcpu,struct sys_reg_params *p, const struct sys_reg_desc *r) { - return __access_id_reg(vcpu, p, r, false); + bool ret = __access_id_reg(vcpu, p, r, false); + +access_nested_id_reg (vcpu, p, r); +return ret; } static bool access_raz_id_reg(struct kvm_vcpu *vcpu,
Signature · 1 line
2.20.1
Mark Rutland
reviewerRe: [PATCH v2 32/94] KVM: arm64: nv: Filter out unsupported features from ID regs
Quoted 13 lines · Marc Zyngier
As 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.
Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/include/asm/kvm_nested.h | 6 ++ arch/arm64/include/asm/sysreg.h | 11 +++ arch/arm64/kvm/nested.c | 115 ++++++++++++++++++++++++++++ arch/arm64/kvm/sys_regs.c | 5 +- 4 files changed, 136 insertions(+), 1 deletion(-)
Quoted 8 lines
+/*
+ * Our emulated CPU doesn't support all the possible features. For the
+ * sake of simplicity (and probably mental sanity), wipe out a number
+ * of feature bits we don't intend to support for the time being.
+ * This list should get updated as new features get added to the NV
+ * support, and new extension to the architecture.
+ *
+ * Revisit: Implement as a whitelist rather than a blacklist?
I certainly think we want this to be a whitelist; blacklists are awful for both forwards-compatibility and backporting.
I realise that's pain up-front, but I think it saves us much much more in the long run.
Thanks,
Mark.
Quoted 123 lines
+ */
arch/arm64/kvm/nested.c +99
-0+void access_nested_id_reg (struct kvm_vcpu *v,struct sys_reg_params *p, +const struct sys_reg_desc *r) +{ +u32 id =sys_reg ((u32 )r->Op0, (u32 )r->Op1, + (u32 )r->CRn, (u32 )r->CRm, (u32 )r->Op2); +u64 val, tmp; + +if (!nested_virt_in_use (v)) +return ; + + val = p->regval; + +switch (id) { +case SYS_ID_AA64DFR0_EL1 : +/* No SPE */ + val &= ~FEATURE (ID_AA64DFR0_PMSVER ); +/* Cap PMU to ARMv8.1 */ + tmp =FIELD_GET (FEATURE (ID_AA64DFR0_PMUVER ), val); +if (tmp >0b0100 ) { + val &=FEATURE (ID_AA64DFR0_PMUVER ); + val |=FIELD_PREP (FEATURE (ID_AA64DFR0_PMUVER ),0b0100 ); + } +/* No trace */ + val &=FEATURE (ID_AA64DFR0_TRACEVER ); +/* Cap Debug to ARMv8.1 */ + tmp =FIELD_GET (FEATURE (ID_AA64DFR0_DEBUGVER ), val); +if (tmp >0b0111 ) { + val &=FEATURE (ID_AA64DFR0_DEBUGVER ); + val |=FIELD_PREP (FEATURE (ID_AA64DFR0_DEBUGVER ),0b0111 ); + } +break ; + +case SYS_ID_AA64ISAR1_EL1 : +/* No PtrAuth */ + val &= ~(FEATURE (ID_AA64ISAR1_APA ) | +FEATURE (ID_AA64ISAR1_API ) | +FEATURE (ID_AA64ISAR1_GPA ) | +FEATURE (ID_AA64ISAR1_GPI )); +break ; + +case SYS_ID_AA64MMFR0_EL1 : +/* Cap PARange to 40bits */ + tmp =FIELD_GET (FEATURE (ID_AA64MMFR0_PARANGE ), val); +if (tmp >0b0010 ) { + val &= ~FEATURE (ID_AA64MMFR0_PARANGE ); + val |=FIELD_PREP (FEATURE (ID_AA64MMFR0_PARANGE ),0b0010 ); + } +break ; + +case SYS_ID_AA64MMFR1_EL1 : +/* No XNX */ + val &= ~FEATURE (ID_AA64MMFR1_XNX ); +/* No RAS */ + val &= ~FEATURE (ID_AA64MMFR1_SpecSEI); +/* No Hierarchical Permission Disable */ + val &= ~FEATURE (ID_AA64MMFR1_HPD ); +/* No Hardward Access flags and Dirty Bit State update */ + val &= ~FEATURE (ID_AA64MMFR1_HADBS ); +break ; + +case SYS_ID_AA64MMFR2_EL1 : +/* No ARMv8.2-EVT */ + val &= ~FEATURE (ID_AA64MMFR2_EVT ); +/* No ARMv8.4-TTRem */ + val &= ~FEATURE (ID_AA64MMFR2_BBM ); +/* No ARMv8.4-TTST */ + val &= ~FEATURE (ID_AA64MMFR2_ST ); +/* No ARMv8.3-CCIDX */ + val &= ~FEATURE (ID_AA64MMFR2_CCIDX ); +/* No ARMv8.2-LVA */ + val &= ~FEATURE (ID_AA64MMFR2_LVA ); +break ; + +case SYS_ID_AA64PFR0_EL1 : +/* No AMU */ + val &= ~FEATURE (ID_AA64PFR0_AMU ); +/* No MPAM */ + val &= ~FEATURE (ID_AA64PFR0_MPAM ); +/* No Secure EL2 */ + val &= ~FEATURE (ID_AA64PFR0_SEL2 ); +/* No RAS */ + val &= ~FEATURE (ID_AA64PFR0_RAS ); +/* No SVE */ + val &= ~FEATURE (ID_AA64PFR0_SVE ); +/* EL2 is AArch64 only */ + val &= ~FEATURE (ID_AA64PFR0_EL2 ); + val |=FIELD_PREP (FEATURE (ID_AA64PFR0_EL2 ),0b0001 ); +break ; + +case SYS_ID_AA64PFR1_EL1 : +/* No MTE */ + val &= ~FEATURE (ID_AA64PFR1_MTE ); +/* No BT */ + val &= ~FEATURE (ID_AA64PFR1_BT ); +break ; + } + + p->regval = val; +}arch/arm64/kvm/sys_regs.c +4
-1@@ -1414,7 +1414,10 @@ static bool access_id_reg(struct kvm_vcpu *vcpu,struct sys_reg_params *p, const struct sys_reg_desc *r) { - return __access_id_reg(vcpu, p, r, false); + bool ret = __access_id_reg(vcpu, p, r, false); + +access_nested_id_reg (vcpu, p, r); +return ret; } static bool access_raz_id_reg(struct kvm_vcpu *vcpu,Signature · 6 lines
2.20.1 _______________________________________________ kvmarm mailing list [email protected] https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
Marc Zyngier
maintainerPATCH v2 33/94 KVM: arm64: nv: Hide RAS from nested guests
In reply to Marc ZyngierWe don't want to expose complicated features to guests until we have a good grasp on the basic CPU emulation. So let's pretend that RAS, doesn't exist in a nested guest. We already hide the feature bits, let's now make sure VDISR_EL1 will UNDEF.
--- arch/arm64/kvm/sys_regs.c | 9 +++++++++ 1 file changed, 9 insertions(+)
arch/arm64/kvm/sys_regs.c +9 -0
@@ -592,6 +592,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
@@ -2056,6 +2064,7 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_VBAR_EL2), access_rw, reset_val, VBAR_EL2, 0 }, { SYS_DESC(SYS_RVBAR_EL2), access_rw, reset_val, RVBAR_EL2, 0 }, { SYS_DESC(SYS_RMR_EL2), access_wi, reset_val, RMR_EL2, 1 }, + { SYS_DESC(SYS_VDISR_EL2), trap_undef }, { SYS_DESC(SYS_CONTEXTIDR_EL2), access_rw, reset_val, CONTEXTIDR_EL2, 0 }, { SYS_DESC(SYS_TPIDR_EL2), access_rw, reset_val, TPIDR_EL2, 0 },
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 34/94 KVM: arm64: nv: Use ARMv8.5-GTG to advertise supported Stage-2 page sizes
In reply to Marc ZyngierARMv8.5-GTG gives the opportunity to advertize the supported Stage-2 page sizes to hypervisors, and allow them to differ from the page sizes supported at Stage-1.
As KVM cannot support guest Stage-2 page sizes that are smaller than PAGE_SIZE (it would break the guest's isolation guarantees), let's use this feature to let the guest know (assuming it has been told about ARMv8.5-GTG).
--- arch/arm64/include/asm/sysreg.h | 3 +++ arch/arm64/kvm/nested.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+)
arch/arm64/include/asm/sysreg.h +3 -0
@@ -701,6 +701,9 @@#define ID_AA64ZFR0_SVEVER_SVE2 0x1/* id_aa64mmfr0 */ +#define ID_AA64MMFR0_TGRAN4_2_SHIFT 40 +#define ID_AA64MMFR0_TGRAN64_2_SHIFT 36 +#define ID_AA64MMFR0_TGRAN16_2_SHIFT 32#define ID_AA64MMFR0_TGRAN4_SHIFT 28#define ID_AA64MMFR0_TGRAN64_SHIFT 24#define ID_AA64MMFR0_TGRAN16_SHIFT 20
arch/arm64/kvm/nested.c +29 -0
@@ -95,6 +95,35 @@ void access_nested_id_reg(struct kvm_vcpu *v, struct sys_reg_params *p,break ;case SYS_ID_AA64MMFR0_EL1 : +/* Hide unsupported S2 page sizes */ +switch (PAGE_SIZE ) { +case SZ_64K : + val &= ~FEATURE (ID_AA64MMFR0_TGRAN16_2 ); + val |=FIELD_PREP (FEATURE (ID_AA64MMFR0_TGRAN16_2 ),0b0001 ); +/* Fall through */ +case SZ_16K : + val &= ~FEATURE (ID_AA64MMFR0_TGRAN4_2 ); + val |=FIELD_PREP (FEATURE (ID_AA64MMFR0_TGRAN4_2 ),0b0001 ); +/* Fall through */ +case SZ_4K : +/* Support everything */ +break ; + } +/* Advertize supported S2 page sizes */ +switch (PAGE_SIZE ) { +case SZ_4K : + val &= ~FEATURE (ID_AA64MMFR0_TGRAN4_2 ); + val |=FIELD_PREP (FEATURE (ID_AA64MMFR0_TGRAN4_2 ),0b0010 ); +/* Fall through */ +case SZ_16K : + val &= ~FEATURE (ID_AA64MMFR0_TGRAN16_2 ); + val |=FIELD_PREP (FEATURE (ID_AA64MMFR0_TGRAN16_2 ),0b0010 ); +/* Fall through */ +case SZ_64K : + val &= ~FEATURE (ID_AA64MMFR0_TGRAN64_2 ); + val |=FIELD_PREP (FEATURE (ID_AA64MMFR0_TGRAN64_2 ),0b0010 ); +break ; + }/* Cap PARange to 40bits */ tmp =FIELD_GET (FEATURE (ID_AA64MMFR0_PARANGE ), val);if (tmp >0b0010 ) {
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 35/94 KVM: arm64: Check advertised Stage-2 page size capability
In reply to Marc ZyngierWith ARMv8.5-GTG, the hardware (or more likely a hypervisor) can advertise the supported Stage-2 page sizes.
Let's check this at boot time.
--- arch/arm/include/asm/kvm_mmu.h | 2 +- arch/arm64/include/asm/kvm_host.h | 2 +- arch/arm64/kernel/cpufeature.c | 8 +++++++ arch/arm64/kvm/reset.c | 39 ++++++++++++++++++++++++++++--- virt/kvm/arm/arm.c | 4 +--- 5 files changed, 47 insertions(+), 8 deletions(-)
arch/arm/include/asm/kvm_mmu.h +1 -1
@@ -418,7 +418,7 @@ static inline int hyp_map_aux_data(void)#define kvm_phys_to_vttbr (addr) (addr) -static inline void kvm_set_ipa_limit (void ) {} +static inline int kvm_set_ipa_limit (void ) {return 0 ; } static __always_inline u64 kvm_get_vttbr(struct kvm *kvm) {
arch/arm64/include/asm/kvm_host.h +1 -1
@@ -734,7 +734,7 @@ static inline int kvm_arm_have_ssbd(void)void kvm_vcpu_load_sysregs (struct kvm_vcpu *vcpu);void kvm_vcpu_put_sysregs (struct kvm_vcpu *vcpu); -void kvm_set_ipa_limit (void ); +int kvm_set_ipa_limit (void );#define __KVM_HAVE_ARCH_VM_ALLOCstruct kvm *kvm_arch_alloc_vm (void );
arch/arm64/kernel/cpufeature.c +8 -0
@@ -205,6 +205,14 @@ static const struct arm64_ftr_bits ftr_id_aa64zfr0[] = { };static const struct arm64_ftr_bits ftr_id_aa64mmfr0[] = { +/* +* Page size not being supported at Stage-2 are not fatal. You +* just give up KVM if PAGE_SIZE isn't supported there. Go fix +* your favourite nesting hypervisor. +*/ +ARM64_FTR_BITS (FTR_HIDDEN ,FTR_NONSTRICT ,FTR_EXACT ,ID_AA64MMFR0_TGRAN4_2_SHIFT ,4 ,1 ), +ARM64_FTR_BITS (FTR_HIDDEN ,FTR_NONSTRICT ,FTR_EXACT ,ID_AA64MMFR0_TGRAN64_2_SHIFT ,4 ,1 ), +ARM64_FTR_BITS (FTR_HIDDEN ,FTR_NONSTRICT ,FTR_EXACT ,ID_AA64MMFR0_TGRAN16_2_SHIFT ,4 ,1 ),/* * We already refuse to boot CPUs that don't support our configured * page size, so we can only detect mismatches for a page size other
arch/arm64/kvm/reset.c +36 -3
@@ -348,11 +348,42 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)return ret; } -void kvm_set_ipa_limit (void ) +int kvm_set_ipa_limit (void ) { -unsigned int ipa_max, pa_max, va_max, parange; +unsigned int ipa_max, pa_max, va_max, parange, tgran_2; +u64 mmfr0 =read_sanitised_ftr_reg (SYS_ID_AA64MMFR0_EL1 ); - parange =read_sanitised_ftr_reg (SYS_ID_AA64MMFR0_EL1 ) &0x7 ; +/* +* Check with ARMv8.5-GTG that our PAGE_SIZE is supported at +* Stage-2. If not, things will stop very quickly. +*/ +switch (PAGE_SIZE ) { +default : +case SZ_4K : + tgran_2 =ID_AA64MMFR0_TGRAN4_2_SHIFT ; +break ; +case SZ_16K : + tgran_2 =ID_AA64MMFR0_TGRAN16_2_SHIFT ; +break ; +case SZ_64K : + tgran_2 =ID_AA64MMFR0_TGRAN64_2_SHIFT ; +break ; + } + +switch (FIELD_GET (0xFUL << tgran_2, mmfr0)) { +default : +case 1 : +kvm_err ("PAGE_SIZE not supported at Stage-2, giving up\n" ); +return -EINVAL ; +case 0 : +kvm_debug ("PAGE_SIZE supported at Stage-2 (default)\n" ); +break ; +case 2 : +kvm_debug ("PAGE_SIZE supported at Stage-2 (advertised)\n" ); +break ; + } + + parange = mmfr0 &0x7 ; pa_max =id_aa64mmfr0_parange_to_phys_shift (parange);/* Clamp the IPA limit to the PA size supported by the kernel */
@@ -386,6 +417,8 @@ void kvm_set_ipa_limit(void) "KVM IPA limit (%d bit) is smaller than default size\n", ipa_max); kvm_ipa_limit = ipa_max;kvm_info ("IPA Size Limit: %dbits\n" , kvm_ipa_limit); + +return 0 ; }/*
virt/kvm/arm/arm.c +1 -3
@@ -1421,9 +1421,7 @@ static inline void hyp_cpu_pm_exit(void)static int init_common_resources (void ) { -kvm_set_ipa_limit (); - -return 0 ; +return kvm_set_ipa_limit (); }static int init_subsystems (void )
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 36/94 KVM: arm/arm64: nv: Factor out stage 2 page table data from struct kvm
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
As we are about to reuse our stage 2 page table manipulation code for shadow stage 2 page tables in the context of nested virtualization, we are going to manage multiple stage 2 page tables for a single VM.
This requires some pretty invasive changes to our data structures, which moves the vmid and pgd pointers into a separate structure and change pretty much all of our mmu code to operate on this structure instead.
The new structre is called struct kvm_s2_mmu.
There is no intended functional change by this patch alone.
--- arch/arm/include/asm/kvm_asm.h | 5 +- arch/arm/include/asm/kvm_host.h | 23 ++- arch/arm/include/asm/kvm_mmu.h | 10 +- arch/arm/kvm/hyp/switch.c | 3 +- arch/arm/kvm/hyp/tlb.c | 13 +- arch/arm64/include/asm/kvm_asm.h | 5 +- arch/arm64/include/asm/kvm_host.h | 24 ++- arch/arm64/include/asm/kvm_mmu.h | 16 +- arch/arm64/kvm/hyp/switch.c | 8 +- arch/arm64/kvm/hyp/tlb.c | 48 +++--- virt/kvm/arm/arm.c | 17 +- virt/kvm/arm/mmu.c | 250 ++++++++++++++++-------------- 12 files changed, 230 insertions(+), 192 deletions(-)
arch/arm/include/asm/kvm_asm.h +3 -2
@@ -49,13 +49,14 @@#ifndef __ASSEMBLY__struct kvm ;struct kvm_vcpu ; +struct kvm_s2_mmu ;extern char __kvm_hyp_init[];extern char __kvm_hyp_init_end[];extern void __kvm_flush_vm_context (void ); -extern void __kvm_tlb_flush_vmid_ipa (struct kvm *kvm,phys_addr_t ipa); -extern void __kvm_tlb_flush_vmid (struct kvm *kvm); +extern void __kvm_tlb_flush_vmid_ipa (struct kvm_s2_mmu *mmu,phys_addr_t ipa); +extern void __kvm_tlb_flush_vmid (struct kvm_s2_mmu *mmu);extern void __kvm_tlb_flush_local_vmid (struct kvm_vcpu *vcpu);extern void __kvm_timer_set_cntvoff (u32 cntvoff_low,u32 cntvoff_high);
arch/arm/include/asm/kvm_host.h +15 -8
@@ -55,18 +55,23 @@ struct kvm_vmid {u32 vmid; }; +struct kvm_s2_mmu { +/* The VMID generation used for the virt. memory system */ +struct kvm_vmid vmid; + +/* Stage-2 page table */ +pgd_t *pgd; +phys_addr_t pgd_phys; + +struct kvm *kvm; +}; +struct kvm_arch { +struct kvm_s2_mmu mmu; +/* The last vcpu id that ran on each physical CPU */ int __percpu *last_vcpu_ran; -/* -* Anything that is not used directly from assembly code goes -* here. -*/ - -/* The VMID generation used for the virt. memory system */ -struct kvm_vmid vmid; -/* Stage-2 page table */ pgd_t *pgd;phys_addr_t pgd_phys;
@@ -171,6 +176,8 @@ struct vcpu_reset_state {struct kvm_vcpu_arch {struct kvm_cpu_context ctxt; +struct kvm_s2_mmu *hw_mmu; +int target;/* Processor target */ DECLARE_BITMAP(features, KVM_VCPU_MAX_FEATURES);
arch/arm/include/asm/kvm_mmu.h +5 -5
@@ -52,8 +52,8 @@ int create_hyp_exec_mappings(phys_addr_t phys_addr, size_t size,void free_hyp_pgds (void );void stage2_unmap_vm (struct kvm *kvm); -int kvm_alloc_stage2_pgd (struct kvm *kvm); -void kvm_free_stage2_pgd (struct kvm *kvm); +int kvm_alloc_stage2_pgd (struct kvm_s2_mmu *mmu); +void kvm_free_stage2_pgd (struct kvm_s2_mmu *mmu);int kvm_phys_addr_ioremap (struct kvm *kvm,phys_addr_t guest_ipa,phys_addr_t pa,unsigned long size,bool writable);
@@ -420,12 +420,12 @@ static inline int hyp_map_aux_data(void)static inline int kvm_set_ipa_limit (void ) {return 0 ; } -static __always_inline u64 kvm_get_vttbr(struct kvm *kvm) +static __always_inline u64 kvm_get_vttbr(struct kvm_s2_mmu *mmu) { -struct kvm_vmid *vmid = &kvm->arch.vmid; +struct kvm_vmid *vmid = &mmu->vmid;u64 vmid_field, baddr; - baddr = kvm->arch.pgd_phys; + baddr = mmu->pgd_phys; vmid_field = (u64 )vmid->vmid <<VTTBR_VMID_SHIFT ;return kvm_phys_to_vttbr (baddr) | vmid_field; }
arch/arm/kvm/hyp/switch.c +1 -2
@@ -65,8 +65,7 @@ static void __hyp_text __deactivate_traps(struct kvm_vcpu *vcpu) static void __hyp_text __activate_vm(struct kvm_vcpu *vcpu) { -struct kvm *kvm =kern_hyp_va (vcpu->kvm); -write_sysreg (kvm_get_vttbr (kvm),VTTBR ); +write_sysreg (kvm_get_vttbr (vcpu->arch.hw_mmu),VTTBR );write_sysreg (vcpu->arch.midr,VPIDR ); }
arch/arm/kvm/hyp/tlb.c +5 -8
@@ -24,13 +24,12 @@* As v7 does not support flushing per IPA, just nuke the whole TLB * instead, ignoring the ipa value. */ -void __hyp_text __kvm_tlb_flush_vmid(struct kvm *kvm) +void __hyp_text __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu) {dsb (ishst);/* Switch to requested VMID */ - kvm =kern_hyp_va (kvm); -write_sysreg (kvm_get_vttbr (kvm),VTTBR ); +write_sysreg (kvm_get_vttbr (mmu),VTTBR );isb ();write_sysreg (0 ,TLBIALLIS );
@@ -40,17 +39,15 @@ void __hyp_text __kvm_tlb_flush_vmid(struct kvm *kvm)write_sysreg (0 ,VTTBR ); } -void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa) +void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu, phys_addr_t ipa) { -__kvm_tlb_flush_vmid (kvm); +__kvm_tlb_flush_vmid (mmu); } void __hyp_text __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu) { -struct kvm *kvm =kern_hyp_va (kern_hyp_va (vcpu)->kvm); -/* Switch to requested VMID */ -write_sysreg (kvm_get_vttbr (kvm),VTTBR ); +write_sysreg (kvm_get_vttbr (vcpu->arch.hw_mmu),VTTBR );isb ();write_sysreg (0 ,TLBIALL );
arch/arm64/include/asm/kvm_asm.h +3 -2
@@ -51,6 +51,7 @@struct kvm ;struct kvm_vcpu ; +struct kvm_s2_mmu ;extern char __kvm_hyp_init[];extern char __kvm_hyp_init_end[];
@@ -58,8 +59,8 @@ extern char __kvm_hyp_init_end[];extern char __kvm_hyp_vector[];extern void __kvm_flush_vm_context (void ); -extern void __kvm_tlb_flush_vmid_ipa (struct kvm *kvm,phys_addr_t ipa); -extern void __kvm_tlb_flush_vmid (struct kvm *kvm); +extern void __kvm_tlb_flush_vmid_ipa (struct kvm_s2_mmu *mmu,phys_addr_t ipa); +extern void __kvm_tlb_flush_vmid (struct kvm_s2_mmu *mmu);extern void __kvm_tlb_flush_local_vmid (struct kvm_vcpu *vcpu);extern void __kvm_timer_set_cntvoff (u32 cntvoff_low,u32 cntvoff_high);
arch/arm64/include/asm/kvm_host.h +20 -4
@@ -62,12 +62,25 @@ struct kvm_vmid {u32 vmid; }; -struct kvm_arch { +struct kvm_s2_mmu {struct kvm_vmid vmid; -/* stage2 entry level table */ -pgd_t *pgd; -phys_addr_t pgd_phys; +/* +* stage2 entry level table +* +* Two kvm_s2_mmu structures in the same VM can point to the same pgd +* here. This happens when running a non-VHE guest hypervisor which +* uses the canonical stage 2 page table for both vEL2 and for vEL1/0 +* with vHCR_EL2.VM == 0. +*/ +pgd_t *pgd; +phys_addr_t pgd_phys; + +struct kvm *kvm; +}; + +struct kvm_arch { +struct kvm_s2_mmu mmu;/* VTCR_EL2 value for this VM */ u64 vtcr;
@@ -287,6 +300,9 @@ struct kvm_vcpu_arch {void *sve_state;unsigned int sve_max_vl; +/* Stage 2 paging state used by the hardware on next switch */ +struct kvm_s2_mmu *hw_mmu; +/* HYP configuration */ u64 hcr_el2;u32 mdcr_el2;
arch/arm64/include/asm/kvm_mmu.h +8 -8
@@ -155,8 +155,8 @@ int create_hyp_exec_mappings(phys_addr_t phys_addr, size_t size,void free_hyp_pgds (void );void stage2_unmap_vm (struct kvm *kvm); -int kvm_alloc_stage2_pgd (struct kvm *kvm); -void kvm_free_stage2_pgd (struct kvm *kvm); +int kvm_alloc_stage2_pgd (struct kvm_s2_mmu *mmu); +void kvm_free_stage2_pgd (struct kvm_s2_mmu *mmu);int kvm_phys_addr_ioremap (struct kvm *kvm,phys_addr_t guest_ipa,phys_addr_t pa,unsigned long size,bool writable);
@@ -597,13 +597,13 @@ static inline u64 kvm_vttbr_baddr_mask(struct kvm *kvm)return vttbr_baddr_mask (kvm_phys_shift (kvm),kvm_stage2_levels (kvm)); } -static __always_inline u64 kvm_get_vttbr(struct kvm *kvm) +static __always_inline u64 kvm_get_vttbr(struct kvm_s2_mmu *mmu) { -struct kvm_vmid *vmid = &kvm->arch.vmid; +struct kvm_vmid *vmid = &mmu->vmid;u64 vmid_field, baddr;u64 cnp =system_supports_cnp () ?VTTBR_CNP_BIT :0 ; - baddr = kvm->arch.pgd_phys; + baddr = mmu->pgd_phys; vmid_field = (u64 )vmid->vmid <<VTTBR_VMID_SHIFT ;return kvm_phys_to_vttbr (baddr) | vmid_field | cnp; }
@@ -612,10 +612,10 @@ static __always_inline u64 kvm_get_vttbr(struct kvm *kvm)* Must be called from hyp code running at EL2 with an updated VTTBR * and interrupts disabled. */ -static __always_inline void __load_guest_stage2(struct kvm *kvm) +static __always_inline void __load_guest_stage2(struct kvm_s2_mmu *mmu) { -write_sysreg (kvm->arch.vtcr, vtcr_el2); -write_sysreg (kvm_get_vttbr (kvm), vttbr_el2); +write_sysreg (kern_hyp_va (mmu->kvm)->arch.vtcr, vtcr_el2); +write_sysreg (kvm_get_vttbr (mmu), vttbr_el2);/* * ARM erratum 1165522 requires the actual execution of the above
arch/arm64/kvm/hyp/switch.c +4 -4
@@ -281,9 +281,9 @@ void deactivate_traps_vhe_put(void)__deactivate_traps_common (); } -static void __hyp_text __activate_vm(struct kvm *kvm) +static void __hyp_text __activate_vm(struct kvm_s2_mmu *mmu) { -__load_guest_stage2 (kvm); +__load_guest_stage2 (mmu); } static void __hyp_text __deactivate_vm(struct kvm_vcpu *vcpu)
@@ -691,7 +691,7 @@ int kvm_vcpu_run_vhe(struct kvm_vcpu *vcpu)* stage 2 translation, and __activate_traps clear HCR_EL2.TGE * (among other things). */ -__activate_vm (vcpu->kvm); +__activate_vm (vcpu->arch.hw_mmu);__activate_traps (vcpu);sysreg_restore_guest_state_vhe (guest_ctxt);
@@ -763,7 +763,7 @@ int __hyp_text __kvm_vcpu_run_nvhe(struct kvm_vcpu *vcpu)__sysreg32_restore_state (vcpu);__sysreg_restore_state_nvhe (guest_ctxt); -__activate_vm (kern_hyp_va (vcpu->kvm)); +__activate_vm (kern_hyp_va (vcpu->arch.hw_mmu));__activate_traps (vcpu);__hyp_vgic_restore_state (vcpu);
arch/arm64/kvm/hyp/tlb.c +23 -25
136 lines not on this page · raw message
virt/kvm/arm/arm.c +10 -7
@@ -114,7 +114,11 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)for_each_possible_cpu (cpu) *per_cpu_ptr (kvm->arch.last_vcpu_ran, cpu) =-1 ; - ret =kvm_alloc_stage2_pgd (kvm); +/* Mark the initial VMID generation invalid */ + kvm->arch.mmu.vmid.vmid_gen =0 ; + kvm->arch.mmu.kvm = kvm; + + ret =kvm_alloc_stage2_pgd (&kvm->arch.mmu);if (ret) goto out_fail_alloc;
@@ -124,16 +128,13 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)kvm_vgic_early_init (kvm); -/* Mark the initial VMID generation invalid */ - kvm->arch.vmid.vmid_gen =0 ; -/* The maximum number of VCPUs is limited by the host's GIC model */ kvm->arch.max_vcpus = vgic_present ?kvm_vgic_get_max_vcpus () :KVM_MAX_VCPUS ;return ret; out_free_stage2_pgd: -kvm_free_stage2_pgd (kvm); +kvm_free_stage2_pgd (&kvm->arch.mmu); out_fail_alloc:free_percpu (kvm->arch.last_vcpu_ran); kvm->arch.last_vcpu_ran =NULL ;
@@ -279,6 +280,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)kvm_arm_pvtime_vcpu_init (&vcpu->arch); + vcpu->arch.hw_mmu = &vcpu->kvm->arch.mmu; + err =kvm_vgic_vcpu_init (vcpu);if (err)return err;
@@ -671,7 +674,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)*/ cond_resched (); -update_vmid (&vcpu->kvm->arch.vmid); +update_vmid (&vcpu->arch.hw_mmu->vmid);check_vcpu_requests (vcpu);
@@ -720,7 +723,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)*/ smp_store_mb (vcpu->mode,IN_GUEST_MODE ); -if (ret <=0 ||need_new_vmid_gen (&vcpu->kvm->arch.vmid) || +if (ret <=0 ||need_new_vmid_gen (&vcpu->arch.hw_mmu->vmid) ||kvm_request_pending (vcpu)) { vcpu->mode =OUTSIDE_GUEST_MODE ;isb ();/* Ensure work in x_flush_hwstate is committed */
virt/kvm/arm/mmu.c +133 -117
770 lines not on this page · raw message
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 37/94 KVM: arm64: nv: Support multiple nested Stage-2 mmu structures
In reply to Marc ZyngierAdd Stage-2 mmu data structures for virtual EL2 and for nested guests. We don't yet populate shadow Stage-2 page tables, but we now have a framework for getting to a shadow Stage-2 pgd.
We allocate twice the number of vcpus as Stage-2 mmu structures because that's sufficient for each vcpu running two translation regimes without having to flush the Stage-2 page tables.
--- arch/arm/include/asm/kvm_host.h | 4 + arch/arm/include/asm/kvm_mmu.h | 3 + arch/arm64/include/asm/kvm_host.h | 29 +++++ arch/arm64/include/asm/kvm_mmu.h | 8 ++ arch/arm64/include/asm/kvm_nested.h | 7 ++ arch/arm64/kvm/nested.c | 188 ++++++++++++++++++++++++++++ virt/kvm/arm/arm.c | 16 ++- virt/kvm/arm/mmu.c | 24 ++-- 8 files changed, 268 insertions(+), 11 deletions(-)
arch/arm/include/asm/kvm_host.h +4 -0
@@ -463,4 +463,8 @@ static inline bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu) return true; } +static inline void kvm_vcpu_load_hw_mmu (struct kvm_vcpu *vcpu) {} +static inline void kvm_vcpu_put_hw_mmu (struct kvm_vcpu *vcpu) {} +static inline int kvm_vcpu_init_nested (struct kvm_vcpu *vcpu) {return 0 ; } +#endif /* __ARM_KVM_HOST_H__ */
arch/arm/include/asm/kvm_mmu.h +3 -0
@@ -420,6 +420,9 @@ static inline int hyp_map_aux_data(void)static inline int kvm_set_ipa_limit (void ) {return 0 ; } +static inline void kvm_init_s2_mmu (struct kvm_s2_mmu *mmu) {} +static inline void kvm_init_nested (struct kvm *kvm) {} + static __always_inline u64 kvm_get_vttbr(struct kvm_s2_mmu *mmu) {struct kvm_vmid *vmid = &mmu->vmid;
arch/arm64/include/asm/kvm_host.h +29 -0
@@ -77,11 +77,40 @@ struct kvm_s2_mmu {phys_addr_t pgd_phys;struct kvm *kvm; + +/* +* For a shadow stage-2 MMU, the virtual vttbr programmed by the guest +* hypervisor. Unused for kvm_arch->mmu. Set to 1 when the structure +* contains no valid information. +*/ +u64 vttbr; + +/* true when this represents a nested context where virtual HCR_EL2.VM == 1 */ +bool nested_stage2_enabled; + +/* +* 0: Nobody is currently using this, check vttbr for validity +* >0: Somebody is actively using this. +*/ +atomic_t refcnt; }; +static inline bool kvm_s2_mmu_valid (struct kvm_s2_mmu *mmu) +{ +return !(mmu->vttbr &1 ); +} +struct kvm_arch {struct kvm_s2_mmu mmu; +/* +* Stage 2 paging stage for VMs with nested virtual using a virtual +* VMID. +*/ +struct kvm_s2_mmu *nested_mmus; +size_t nested_mmus_size; +int nested_mmus_next; +/* VTCR_EL2 value for this VM */ u64 vtcr;
arch/arm64/include/asm/kvm_mmu.h +8 -0
@@ -89,6 +89,7 @@ alternative_cb_end#include <asm/mmu_context.h> #include <asm/pgtable.h> #include <asm/kvm_emulate.h> +#include <asm/kvm_nested.h> void kvm_update_va_mask (struct alt_instr *alt,__le32 *origptr,__le32 *updptr,int nr_inst);
@@ -154,6 +155,7 @@ int create_hyp_exec_mappings(phys_addr_t phys_addr, size_t size, void **haddr);void free_hyp_pgds (void ); +void kvm_unmap_stage2_range (struct kvm_s2_mmu *mmu,phys_addr_t start,u64 size);void stage2_unmap_vm (struct kvm *kvm);int kvm_alloc_stage2_pgd (struct kvm_s2_mmu *mmu);void kvm_free_stage2_pgd (struct kvm_s2_mmu *mmu);
@@ -625,5 +627,11 @@ static __always_inline void __load_guest_stage2(struct kvm_s2_mmu *mmu) asm(ALTERNATIVE("nop", "isb", ARM64_WORKAROUND_SPECULATIVE_AT_VHE)); } +static inline u64 get_vmid (u64 vttbr) +{ +return (vttbr &VTTBR_VMID_MASK (kvm_get_vmid_bits ())) >> +VTTBR_VMID_SHIFT ; +} +#endif /* __ASSEMBLY__ */ #endif /* __ARM64_KVM_MMU_H__ */
arch/arm64/include/asm/kvm_nested.h +7 -0
@@ -10,6 +10,13 @@ static inline bool nested_virt_in_use(const struct kvm_vcpu *vcpu)test_bit (KVM_ARM_VCPU_HAS_EL2 , vcpu->arch.features); } +extern void kvm_init_nested (struct kvm *kvm); +extern int kvm_vcpu_init_nested (struct kvm_vcpu *vcpu); +extern void kvm_init_s2_mmu (struct kvm_s2_mmu *mmu); +extern struct kvm_s2_mmu *lookup_s2_mmu (struct kvm *kvm,u64 vttbr,u64 hcr); +extern void kvm_vcpu_load_hw_mmu (struct kvm_vcpu *vcpu); +extern void kvm_vcpu_put_hw_mmu (struct kvm_vcpu *vcpu); +int handle_wfx_nested (struct kvm_vcpu *vcpu,bool is_wfe);extern bool __forward_traps (struct kvm_vcpu *vcpu,unsigned int reg,u64 control_bit);
arch/arm64/kvm/nested.c +188 -0
206 lines not on this page · raw message
virt/kvm/arm/arm.c +15 -1
@@ -34,6 +34,7 @@#include <asm/kvm_arm.h> #include <asm/kvm_asm.h> #include <asm/kvm_mmu.h> +#include <asm/kvm_nested.h> #include <asm/kvm_emulate.h> #include <asm/kvm_coproc.h> #include <asm/sections.h>
@@ -122,6 +123,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)if (ret) goto out_fail_alloc; +kvm_init_nested (kvm); + ret =create_hyp_mappings (kvm, kvm +1 ,PAGE_HYP );if (ret) goto out_free_stage2_pgd;
@@ -340,6 +343,9 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)int *last_ran;kvm_host_data_t *cpu_data; +if (nested_virt_in_use (vcpu)) +kvm_vcpu_load_hw_mmu (vcpu); + last_ran =this_cpu_ptr (vcpu->kvm->arch.last_vcpu_ran); cpu_data =this_cpu_ptr (&kvm_host_data);
@@ -379,6 +385,9 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)kvm_vgic_put (vcpu);kvm_vcpu_pmu_restore_host (vcpu); +if (nested_virt_in_use (vcpu)) +kvm_vcpu_put_hw_mmu (vcpu); + vcpu->cpu =-1 ; }
@@ -958,8 +967,13 @@ static int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, vcpu->arch.target = phys_target; +/* Prepare for nested if required */ + ret =kvm_vcpu_init_nested (vcpu); +/* Now we know what it is, we can reset it. */ - ret =kvm_reset_vcpu (vcpu); +if (!ret) + ret =kvm_reset_vcpu (vcpu); +if (ret) { vcpu->arch.target =-1 ;bitmap_zero (vcpu->arch.features,KVM_VCPU_MAX_FEATURES );
virt/kvm/arm/mmu.c +14 -10
@@ -329,7 +329,7 @@ static void unmap_stage2_puds(struct kvm_s2_mmu *mmu, pgd_t *pgd, }/** -* unmap_stage2_range -- Clear stage2 page table entries to unmap a range +* kvm_unmap_stage2_range -- Clear stage2 page table entries to unmap a range * @kvm: The VM pointer * @start: The intermediate physical base address of the range to unmap * @size: The size of the area to unmap
@@ -339,7 +339,7 @@ static void unmap_stage2_puds(struct kvm_s2_mmu *mmu, pgd_t *pgd,* destroying the VM), otherwise another faulting VCPU may come in and mess * with things behind our backs. */ -static void unmap_stage2_range (struct kvm_s2_mmu *mmu,phys_addr_t start,u64 size) +void kvm_unmap_stage2_range (struct kvm_s2_mmu *mmu,phys_addr_t start,u64 size) {struct kvm *kvm = mmu->kvm;pgd_t *pgd;
@@ -928,6 +928,10 @@ int kvm_alloc_stage2_pgd(struct kvm_s2_mmu *mmu) mmu->pgd = pgd; mmu->pgd_phys = pgd_phys; + mmu->vmid.vmid_gen =0 ; + +kvm_init_s2_mmu (mmu); +return 0 ; }
@@ -966,7 +970,7 @@ static void stage2_unmap_memslot(struct kvm *kvm,if (!(vma->vm_flags &VM_PFNMAP )) {gpa_t gpa = addr + (vm_start - memslot->userspace_addr); -unmap_stage2_range (&kvm->arch.mmu, gpa, vm_end - vm_start); +kvm_unmap_stage2_range (&kvm->arch.mmu, gpa, vm_end - vm_start); } hva = vm_end; }while (hva < reg_end);
@@ -1005,7 +1009,7 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)spin_lock (&kvm->mmu_lock);if (mmu->pgd) { -unmap_stage2_range (mmu,0 ,kvm_phys_size (kvm)); +kvm_unmap_stage2_range (mmu,0 ,kvm_phys_size (kvm)); pgd =READ_ONCE (mmu->pgd); mmu->pgd =NULL ; }
@@ -1097,7 +1101,7 @@ static int stage2_set_pmd_huge(struct kvm_s2_mmu *mmu,* get handled accordingly. */ if (!pmd_thp_or_huge (old_pmd)) { -unmap_stage2_range (mmu, addr &S2_PMD_MASK ,S2_PMD_SIZE ); +kvm_unmap_stage2_range (mmu, addr &S2_PMD_MASK ,S2_PMD_SIZE ); goto retry; }/*
@@ -1149,7 +1153,7 @@ static int stage2_set_pud_huge(struct kvm_s2_mmu *mmu,* the range for this block and retry. */ if (!stage2_pud_huge (kvm, old_pud)) { -unmap_stage2_range (mmu, addr &S2_PUD_MASK ,S2_PUD_SIZE ); +kvm_unmap_stage2_range (mmu, addr &S2_PUD_MASK ,S2_PUD_SIZE ); goto retry; }
@@ -2051,7 +2055,7 @@ static int handle_hva_to_gpa(struct kvm *kvm,static int kvm_unmap_hva_handler (struct kvm *kvm,gpa_t gpa,u64 size,void *data) { -unmap_stage2_range (&kvm->arch.mmu, gpa, size); +kvm_unmap_stage2_range (&kvm->arch.mmu, gpa, size);return 0 ; }
@@ -2356,7 +2360,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,spin_lock (&kvm->mmu_lock);if (ret) -unmap_stage2_range (&kvm->arch.mmu, mem->guest_phys_addr, mem->memory_size); +kvm_unmap_stage2_range (&kvm->arch.mmu, mem->guest_phys_addr, mem->memory_size);else stage2_flush_memslot (&kvm->arch.mmu, memslot);spin_unlock (&kvm->mmu_lock);
@@ -2380,7 +2384,7 @@ void kvm_arch_memslots_updated(struct kvm *kvm, u64 gen) { } -void kvm_arch_flush_shadow_all (struct kvm *kvm) +__weak void kvm_arch_flush_shadow_all (struct kvm *kvm) {kvm_free_stage2_pgd (&kvm->arch.mmu); }
@@ -2392,7 +2396,7 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,phys_addr_t size = slot->npages <<PAGE_SHIFT ;spin_lock (&kvm->mmu_lock); -unmap_stage2_range (&kvm->arch.mmu, gpa, size); +kvm_unmap_stage2_range (&kvm->arch.mmu, gpa, size);spin_unlock (&kvm->mmu_lock); }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 38/94 KVM: arm64: nv: Implement nested Stage-2 page table walk logic
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
Based on the pseudo-code in the ARM ARM, implement a stage 2 software page table walker.
--- arch/arm64/include/asm/esr.h | 1 + arch/arm64/include/asm/kvm_arm.h | 2 + arch/arm64/include/asm/kvm_nested.h | 13 ++ arch/arm64/kvm/nested.c | 276 ++++++++++++++++++++++++++++ 4 files changed, 292 insertions(+)
arch/arm64/include/asm/esr.h +1 -0
@@ -125,6 +125,7 @@#define ESR_ELx_CM (UL(1) << ESR_ELx_CM_SHIFT)/* ISS field definitions for exceptions taken in to Hyp */ +#define ESR_ELx_FSC_ADDRSZ (0x00)#define ESR_ELx_CV (UL(1) << 24)#define ESR_ELx_COND_SHIFT (20)#define ESR_ELx_COND_MASK (UL(0xF) << ESR_ELx_COND_SHIFT)
arch/arm64/include/asm/kvm_arm.h +2 -0
@@ -261,6 +261,8 @@#define VTTBR_VMID_SHIFT (UL(48))#define VTTBR_VMID_MASK (size) (_AT(u64, (1 << size) - 1) << VTTBR_VMID_SHIFT) +#define SCTLR_EE (UL(1) << 25) +/* Hyp System Trap Register */ #define HSTR_EL2_T (x) (1 << x)
arch/arm64/include/asm/kvm_nested.h +13 -0
@@ -17,6 +17,19 @@ extern struct kvm_s2_mmu *lookup_s2_mmu(struct kvm *kvm, u64 vttbr, u64 hcr);extern void kvm_vcpu_load_hw_mmu (struct kvm_vcpu *vcpu);extern void kvm_vcpu_put_hw_mmu (struct kvm_vcpu *vcpu); +struct kvm_s2_trans { +phys_addr_t output; +unsigned long block_size; +bool writable; +bool readable; +int level; +u32 esr; +u64 upper_attr; +}; + +extern int kvm_walk_nested_s2 (struct kvm_vcpu *vcpu,phys_addr_t gipa, +struct kvm_s2_trans *result); +int handle_wfx_nested (struct kvm_vcpu *vcpu,bool is_wfe);extern bool __forward_traps (struct kvm_vcpu *vcpu,unsigned int reg,u64 control_bit);
arch/arm64/kvm/nested.c +276 -0
282 lines not on this page · raw message
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 39/94 KVM: arm64: nv: Handle shadow stage 2 page faults
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
If we are faulting on a shadow stage 2 translation, we first walk the guest hypervisor's stage 2 page table to see if it has a mapping. If not, we inject a stage 2 page fault to the virtual EL2. Otherwise, we create a mapping in the shadow stage 2 page table.
Note that we have to deal with two IPAs when we got a shadow stage 2 page fault. One is the address we faulted on, and is in the L2 guest phys space. The other is from the guest stage-2 page table walk, and is in the L1 guest phys space. To differentiate them, we rename variables so that fault_ipa is used for the former and ipa is used for the latter.
--- arch/arm/include/asm/kvm_mmu.h | 52 +++++++++++++++ arch/arm64/include/asm/kvm_emulate.h | 6 ++ arch/arm64/include/asm/kvm_nested.h | 18 ++++++ arch/arm64/kvm/nested.c | 39 ++++++++++++ virt/kvm/arm/mmio.c | 14 ++--- virt/kvm/arm/mmu.c | 94 ++++++++++++++++++++++------ 6 files changed, 197 insertions(+), 26 deletions(-)
arch/arm/include/asm/kvm_mmu.h +52 -0
@@ -423,6 +423,58 @@ static inline int kvm_set_ipa_limit(void) { return 0; }static inline void kvm_init_s2_mmu (struct kvm_s2_mmu *mmu) {}static inline void kvm_init_nested (struct kvm *kvm) {} +struct kvm_s2_trans {}; +static inline phys_addr_t kvm_s2_trans_output (struct kvm_s2_trans *trans) +{ +BUG (); +} + +static inline unsigned long kvm_s2_trans_size (struct kvm_s2_trans *trans) +{ +BUG (); +} + +static inline u32 kvm_s2_trans_esr (struct kvm_s2_trans *trans) +{ +BUG (); +} + +static inline int kvm_walk_nested_s2 (struct kvm_vcpu *vcpu,phys_addr_t ipa, +struct kvm_s2_trans *trans) +{ +BUG (); +} + +static inline int kvm_s2_handle_perm_fault (struct kvm_vcpu *vcpu, +struct kvm_s2_trans *trans) +{ +BUG (); +} + +static inline void kvm_inject_s2_fault (struct kvm_vcpu *vcpu,u32 esr) +{ +BUG (); +} + +static inline bool kvm_s2_trans_readable (struct kvm_s2_trans *trans) +{ +BUG (); +} + +static inline bool kvm_s2_trans_writable (struct kvm_s2_trans *trans) +{ +BUG (); +} + +static inline void kvm_nested_s2_flush (struct kvm *kvm) {} +static inline void kvm_nested_s2_wp (struct kvm *kvm) {} +static inline void kvm_nested_s2_clear (struct kvm *kvm) {} + +static inline bool kvm_is_shadow_s2_fault (struct kvm_vcpu *vcpu) +{ +return false; +} + static __always_inline u64 kvm_get_vttbr(struct kvm_s2_mmu *mmu) {struct kvm_vmid *vmid = &mmu->vmid;
arch/arm64/include/asm/kvm_emulate.h +6 -0
@@ -684,4 +684,10 @@ static inline void __hyp_text __kvm_skip_instr(struct kvm_vcpu *vcpu) write_sysreg_el2(*vcpu_pc(vcpu), SYS_ELR); } +static inline bool kvm_is_shadow_s2_fault (struct kvm_vcpu *vcpu) +{ +return (vcpu->arch.hw_mmu != &vcpu->kvm->arch.mmu && + vcpu->arch.hw_mmu->nested_stage2_enabled); +} +#endif /* __ARM64_KVM_EMULATE_H__ */
arch/arm64/include/asm/kvm_nested.h +18 -0
@@ -27,9 +27,27 @@ struct kvm_s2_trans {u64 upper_attr; }; +static inline phys_addr_t kvm_s2_trans_output (struct kvm_s2_trans *trans) +{ +return trans->output; +} + +static inline unsigned long kvm_s2_trans_size (struct kvm_s2_trans *trans) +{ +return trans->block_size; +} + +static inline u32 kvm_s2_trans_esr (struct kvm_s2_trans *trans) +{ +return trans->esr; +} +extern int kvm_walk_nested_s2 (struct kvm_vcpu *vcpu,phys_addr_t gipa,struct kvm_s2_trans *result); +extern int kvm_s2_handle_perm_fault (struct kvm_vcpu *vcpu, +struct kvm_s2_trans *trans); +extern int kvm_inject_s2_fault (struct kvm_vcpu *vcpu,u64 esr_el2);int handle_wfx_nested (struct kvm_vcpu *vcpu,bool is_wfe);extern bool __forward_traps (struct kvm_vcpu *vcpu,unsigned int reg,u64 control_bit);
arch/arm64/kvm/nested.c +39 -0
@@ -471,6 +471,45 @@ void kvm_vcpu_put_hw_mmu(struct kvm_vcpu *vcpu) } } +/* +* Returns non-zero if permission fault is handled by injecting it to the next +* level hypervisor. +*/ +int kvm_s2_handle_perm_fault (struct kvm_vcpu *vcpu,struct kvm_s2_trans *trans) +{ +unsigned long fault_status =kvm_vcpu_trap_get_fault_type (vcpu); +bool forward_fault = false; + + trans->esr =0 ; + +if (fault_status !=FSC_PERM ) +return 0 ; + +if (kvm_vcpu_trap_is_iabt (vcpu)) { + forward_fault = (trans->upper_attr &PTE_S2_XN ); + }else { +bool write_fault =kvm_is_write_fault (vcpu); + + forward_fault = ((write_fault && !trans->writable) || + (!write_fault && !trans->readable)); + } + +if (forward_fault) { + trans->esr =esr_s2_fault (vcpu, trans->level, ESR_ELx_FSC_PERM); +return 1 ; + } + +return 0 ; +} + +int kvm_inject_s2_fault (struct kvm_vcpu *vcpu,u64 esr_el2) +{ +vcpu_write_sys_reg (vcpu, vcpu->arch.fault.far_el2,FAR_EL2 ); +vcpu_write_sys_reg (vcpu, vcpu->arch.fault.hpfar_el2,HPFAR_EL2 ); + +return kvm_inject_nested_sync (vcpu, esr_el2); +} +/* * Inject wfx to the virtual EL2 if this is not from the virtual EL2 and * the virtual HCR_EL2.TWX is set. Otherwise, let the host hypervisor
virt/kvm/arm/mmio.c +7 -7
@@ -120,7 +120,7 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run) }int io_mem_abort (struct kvm_vcpu *vcpu,struct kvm_run *run, -phys_addr_t fault_ipa) +phys_addr_t ipa) {unsigned long data;unsigned long rt;
@@ -137,7 +137,7 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,if (vcpu->kvm->arch.return_nisv_io_abort_to_user) { run->exit_reason =KVM_EXIT_ARM_NISV ; run->arm_nisv.esr_iss =kvm_vcpu_dabt_iss_nisv_sanitized (vcpu); - run->arm_nisv.fault_ipa = fault_ipa; + run->arm_nisv.fault_ipa = ipa;return 0 ; }
@@ -164,22 +164,22 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run, data =vcpu_data_guest_to_host (vcpu,vcpu_get_reg (vcpu, rt), len); -trace_kvm_mmio (KVM_TRACE_MMIO_WRITE , len, fault_ipa, &data); +trace_kvm_mmio (KVM_TRACE_MMIO_WRITE , len, ipa, &data);kvm_mmio_write_buf (data_buf, len, data); - ret =kvm_io_bus_write (vcpu,KVM_MMIO_BUS , fault_ipa, len, + ret =kvm_io_bus_write (vcpu,KVM_MMIO_BUS , ipa, len, data_buf); }else {trace_kvm_mmio (KVM_TRACE_MMIO_READ_UNSATISFIED , len, - fault_ipa,NULL ); + ipa,NULL ); - ret =kvm_io_bus_read (vcpu,KVM_MMIO_BUS , fault_ipa, len, + ret =kvm_io_bus_read (vcpu,KVM_MMIO_BUS , ipa, len, data_buf); }/* Now prepare kvm_run for the potential return to userland. */ run->mmio.is_write = is_write; - run->mmio.phys_addr = fault_ipa; + run->mmio.phys_addr = ipa; run->mmio.len = len; vcpu->mmio_needed =1 ;
virt/kvm/arm/mmu.c +75 -19
188 lines not on this page · raw message
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 40/94 KVM: arm64: nv: Unmap/flush shadow stage 2 page tables
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
Unmap/flush shadow stage 2 page tables for the nested VMs as well as the stage 2 page table for the guest hypervisor.
Note: A bunch of the code in mmu.c relating to MMU notifiers is currently dealt with in an extremely abrupt way, for example by clearing out an entire shadow stage-2 table. This will be handled in a more efficient way using the reverse mapping feature in a later version of the patch series.
--- arch/arm64/include/asm/kvm_mmu.h | 3 ++ arch/arm64/include/asm/kvm_nested.h | 3 ++ arch/arm64/kvm/nested.c | 39 +++++++++++++++++++++++++ virt/kvm/arm/mmu.c | 44 +++++++++++++++++++++++------ 4 files changed, 81 insertions(+), 8 deletions(-)
arch/arm64/include/asm/kvm_mmu.h +3 -0
@@ -153,6 +153,8 @@ int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size, void __iomem **haddr);int create_hyp_exec_mappings (phys_addr_t phys_addr,size_t size,void **haddr); +void kvm_stage2_flush_range (struct kvm_s2_mmu *mmu, +phys_addr_t addr,phys_addr_t end);void free_hyp_pgds (void );void kvm_unmap_stage2_range (struct kvm_s2_mmu *mmu,phys_addr_t start,u64 size);
@@ -161,6 +163,7 @@ int kvm_alloc_stage2_pgd(struct kvm_s2_mmu *mmu);void kvm_free_stage2_pgd (struct kvm_s2_mmu *mmu);int kvm_phys_addr_ioremap (struct kvm *kvm,phys_addr_t guest_ipa,phys_addr_t pa,unsigned long size,bool writable); +void kvm_stage2_wp_range (struct kvm_s2_mmu *mmu,phys_addr_t addr,phys_addr_t end);int kvm_handle_guest_abort (struct kvm_vcpu *vcpu,struct kvm_run *run);
arch/arm64/include/asm/kvm_nested.h +3 -0
@@ -48,6 +48,9 @@ extern int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_addr_t gipa,extern int kvm_s2_handle_perm_fault (struct kvm_vcpu *vcpu,struct kvm_s2_trans *trans);extern int kvm_inject_s2_fault (struct kvm_vcpu *vcpu,u64 esr_el2); +extern void kvm_nested_s2_wp (struct kvm *kvm); +extern void kvm_nested_s2_clear (struct kvm *kvm); +extern void kvm_nested_s2_flush (struct kvm *kvm);int handle_wfx_nested (struct kvm_vcpu *vcpu,bool is_wfe);extern bool __forward_traps (struct kvm_vcpu *vcpu,unsigned int reg,u64 control_bit);
arch/arm64/kvm/nested.c +39 -0
@@ -510,6 +510,45 @@ int kvm_inject_s2_fault(struct kvm_vcpu *vcpu, u64 esr_el2)return kvm_inject_nested_sync (vcpu, esr_el2); } +/* expects kvm->mmu_lock to be held */ +void kvm_nested_s2_wp (struct kvm *kvm) +{ +int i; + +for (i =0 ; i < kvm->arch.nested_mmus_size; i++) { +struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i]; + +if (kvm_s2_mmu_valid (mmu)) +kvm_stage2_wp_range (mmu,0 ,kvm_phys_size (kvm)); + } +} + +/* expects kvm->mmu_lock to be held */ +void kvm_nested_s2_clear (struct kvm *kvm) +{ +int i; + +for (i =0 ; i < kvm->arch.nested_mmus_size; i++) { +struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i]; + +if (kvm_s2_mmu_valid (mmu)) +kvm_unmap_stage2_range (mmu,0 ,kvm_phys_size (kvm)); + } +} + +/* expects kvm->mmu_lock to be held */ +void kvm_nested_s2_flush (struct kvm *kvm) +{ +int i; + +for (i =0 ; i < kvm->arch.nested_mmus_size; i++) { +struct kvm_s2_mmu *mmu = &kvm->arch.nested_mmus[i]; + +if (kvm_s2_mmu_valid (mmu)) +kvm_stage2_flush_range (mmu,0 ,kvm_phys_size (kvm)); + } +} +/* * Inject wfx to the virtual EL2 if this is not from the virtual EL2 and * the virtual HCR_EL2.TWX is set. Otherwise, let the host hypervisor
virt/kvm/arm/mmu.c +36 -8
128 lines not on this page · raw message
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 41/94 KVM: arm64: nv: Move last_vcpu_ran to be per s2 mmu
In reply to Marc Zyngierlast_vcpu_ran has to be per s2 mmu now that we can have multiple S2 per VM. Let's take this opportunity to perform some cleanup.
--- arch/arm/include/asm/kvm_host.h | 6 +++--- arch/arm/include/asm/kvm_mmu.h | 4 ++-- arch/arm64/include/asm/kvm_host.h | 6 +++--- arch/arm64/include/asm/kvm_mmu.h | 2 +- arch/arm64/include/asm/kvm_nested.h | 2 +- arch/arm64/kvm/nested.c | 11 +++-------- virt/kvm/arm/arm.c | 25 ++++--------------------- virt/kvm/arm/mmu.c | 28 +++++++++++++++++++++------- 8 files changed, 38 insertions(+), 46 deletions(-)
arch/arm/include/asm/kvm_host.h +3 -3
@@ -63,15 +63,15 @@ struct kvm_s2_mmu {pgd_t *pgd;phys_addr_t pgd_phys; +/* The last vcpu id that ran on each physical CPU */ +int __percpu *last_vcpu_ran; +struct kvm *kvm; };struct kvm_arch {struct kvm_s2_mmu mmu; -/* The last vcpu id that ran on each physical CPU */ -int __percpu *last_vcpu_ran; -/* Stage-2 page table */ pgd_t *pgd;phys_addr_t pgd_phys;
arch/arm/include/asm/kvm_mmu.h +2 -2
@@ -52,7 +52,7 @@ int create_hyp_exec_mappings(phys_addr_t phys_addr, size_t size,void free_hyp_pgds (void );void stage2_unmap_vm (struct kvm *kvm); -int kvm_alloc_stage2_pgd (struct kvm_s2_mmu *mmu); +int kvm_init_stage2_mmu (struct kvm *kvm,struct kvm_s2_mmu *mmu);void kvm_free_stage2_pgd (struct kvm_s2_mmu *mmu);int kvm_phys_addr_ioremap (struct kvm *kvm,phys_addr_t guest_ipa,phys_addr_t pa,unsigned long size,bool writable);
@@ -420,7 +420,7 @@ static inline int hyp_map_aux_data(void)static inline int kvm_set_ipa_limit (void ) {return 0 ; } -static inline void kvm_init_s2_mmu (struct kvm_s2_mmu *mmu) {} +static inline void kvm_init_nested_s2_mmu (struct kvm_s2_mmu *mmu) {}static inline void kvm_init_nested (struct kvm *kvm) {}struct kvm_s2_trans {};
arch/arm64/include/asm/kvm_host.h +3 -3
@@ -93,6 +93,9 @@ struct kvm_s2_mmu {* >0: Somebody is actively using this. */ atomic_t refcnt; + +/* The last vcpu id that ran on each physical CPU */ +int __percpu *last_vcpu_ran; };static inline bool kvm_s2_mmu_valid (struct kvm_s2_mmu *mmu)
@@ -114,9 +117,6 @@ struct kvm_arch {/* VTCR_EL2 value for this VM */ u64 vtcr; -/* The last vcpu id that ran on each physical CPU */ -int __percpu *last_vcpu_ran; -/* The maximum number of vCPUs depends on the used GIC model */ int max_vcpus;
arch/arm64/include/asm/kvm_mmu.h +1 -1
@@ -159,7 +159,7 @@ void free_hyp_pgds(void);void kvm_unmap_stage2_range (struct kvm_s2_mmu *mmu,phys_addr_t start,u64 size);void stage2_unmap_vm (struct kvm *kvm); -int kvm_alloc_stage2_pgd (struct kvm_s2_mmu *mmu); +int kvm_init_stage2_mmu (struct kvm *kvm,struct kvm_s2_mmu *mmu);void kvm_free_stage2_pgd (struct kvm_s2_mmu *mmu);int kvm_phys_addr_ioremap (struct kvm *kvm,phys_addr_t guest_ipa,phys_addr_t pa,unsigned long size,bool writable);
arch/arm64/include/asm/kvm_nested.h +1 -1
@@ -12,7 +12,7 @@ static inline bool nested_virt_in_use(const struct kvm_vcpu *vcpu)extern void kvm_init_nested (struct kvm *kvm);extern int kvm_vcpu_init_nested (struct kvm_vcpu *vcpu); -extern void kvm_init_s2_mmu (struct kvm_s2_mmu *mmu); +extern void kvm_init_nested_s2_mmu (struct kvm_s2_mmu *mmu);extern struct kvm_s2_mmu *lookup_s2_mmu (struct kvm *kvm,u64 vttbr,u64 hcr);extern void kvm_vcpu_load_hw_mmu (struct kvm_vcpu *vcpu);extern void kvm_vcpu_put_hw_mmu (struct kvm_vcpu *vcpu);
arch/arm64/kvm/nested.c +3 -8
@@ -59,13 +59,8 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu) num_mmus *sizeof (*kvm->arch.nested_mmus), GFP_KERNEL | __GFP_ZERO);if (tmp) { - tmp[num_mmus -1 ].kvm = kvm; -atomic_set (&tmp[num_mmus -1 ].refcnt,0 ); - tmp[num_mmus -2 ].kvm = kvm; -atomic_set (&tmp[num_mmus -2 ].refcnt,0 ); - -if (kvm_alloc_stage2_pgd (&tmp[num_mmus -1 ]) || -kvm_alloc_stage2_pgd (&tmp[num_mmus -2 ])) { +if (kvm_init_stage2_mmu (kvm, &tmp[num_mmus -1 ]) || +kvm_init_stage2_mmu (kvm, &tmp[num_mmus -2 ])) {kvm_free_stage2_pgd (&tmp[num_mmus -1 ]);kvm_free_stage2_pgd (&tmp[num_mmus -2 ]); }else {
@@ -445,7 +440,7 @@ static struct kvm_s2_mmu *get_s2_mmu_nested(struct kvm_vcpu *vcpu)return s2_mmu; } -void kvm_init_s2_mmu (struct kvm_s2_mmu *mmu) +void kvm_init_nested_s2_mmu (struct kvm_s2_mmu *mmu) { mmu->vttbr =1 ; mmu->nested_stage2_enabled = false;
virt/kvm/arm/arm.c +4 -21
@@ -102,26 +102,15 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,*/ int kvm_arch_init_vm (struct kvm *kvm,unsigned long type) { -int ret, cpu; +int ret; ret =kvm_arm_setup_stage2 (kvm, type);if (ret)return ret; - kvm->arch.last_vcpu_ran =alloc_percpu (typeof (*kvm->arch.last_vcpu_ran)); -if (!kvm->arch.last_vcpu_ran) -return -ENOMEM ; - -for_each_possible_cpu (cpu) - *per_cpu_ptr (kvm->arch.last_vcpu_ran, cpu) =-1 ; - -/* Mark the initial VMID generation invalid */ - kvm->arch.mmu.vmid.vmid_gen =0 ; - kvm->arch.mmu.kvm = kvm; - - ret =kvm_alloc_stage2_pgd (&kvm->arch.mmu); + ret =kvm_init_stage2_mmu (kvm, &kvm->arch.mmu);if (ret) - goto out_fail_alloc; +return ret;kvm_init_nested (kvm);
@@ -138,9 +127,6 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)return ret; out_free_stage2_pgd:kvm_free_stage2_pgd (&kvm->arch.mmu); -out_fail_alloc: -free_percpu (kvm->arch.last_vcpu_ran); - kvm->arch.last_vcpu_ran =NULL ;return ret; }
@@ -165,9 +151,6 @@ void kvm_arch_destroy_vm(struct kvm *kvm)kvm_vgic_destroy (kvm); -free_percpu (kvm->arch.last_vcpu_ran); - kvm->arch.last_vcpu_ran =NULL ; -for (i =0 ; i <KVM_MAX_VCPUS ; ++i) {if (kvm->vcpus[i]) {kvm_vcpu_destroy (kvm->vcpus[i]);
@@ -346,7 +329,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)if (nested_virt_in_use (vcpu))kvm_vcpu_load_hw_mmu (vcpu); - last_ran =this_cpu_ptr (vcpu->kvm->arch.last_vcpu_ran); + last_ran =this_cpu_ptr (vcpu->arch.hw_mmu->last_vcpu_ran); cpu_data =this_cpu_ptr (&kvm_host_data);/*
virt/kvm/arm/mmu.c +21 -7
@@ -907,8 +907,9 @@ int create_hyp_exec_mappings(phys_addr_t phys_addr, size_t size, }/** -* kvm_alloc_stage2_pgd - allocate level-1 table for stage-2 translation. -* @mmu: The stage 2 mmu struct pointer +* kvm_init_stage2_mmu - Initialise a S2 MMU strucrure +* @kvm: The pointer to the KVM structure +* @mmu: The pointer to the s2 MMU structure * * Allocates only the stage-2 HW PGD level table(s) of size defined by * stage2_pgd_size(mmu->kvm).
@@ -916,10 +917,11 @@ int create_hyp_exec_mappings(phys_addr_t phys_addr, size_t size,* Note we don't need locking here as this is only called when the VM is * created, which can only be done once. */ -int kvm_alloc_stage2_pgd (struct kvm_s2_mmu *mmu) +int kvm_init_stage2_mmu (struct kvm *kvm,struct kvm_s2_mmu *mmu) {phys_addr_t pgd_phys;pgd_t *pgd; +int cpu;if (mmu->pgd !=NULL ) {kvm_err ("kvm_arch already initialized?\n" );
@@ -927,19 +929,29 @@ int kvm_alloc_stage2_pgd(struct kvm_s2_mmu *mmu) }/* Allocate the HW PGD, making sure that each page gets its own refcount */ - pgd =alloc_pages_exact (stage2_pgd_size (mmu->kvm),GFP_KERNEL | __GFP_ZERO); + pgd =alloc_pages_exact (stage2_pgd_size (kvm),GFP_KERNEL | __GFP_ZERO);if (!pgd)return -ENOMEM ; pgd_phys =virt_to_phys (pgd); -if (WARN_ON (pgd_phys & ~kvm_vttbr_baddr_mask (mmu->kvm))) +if (WARN_ON (pgd_phys & ~kvm_vttbr_baddr_mask (kvm)))return -EINVAL ; + mmu->last_vcpu_ran =alloc_percpu (typeof (*mmu->last_vcpu_ran)); +if (!mmu->last_vcpu_ran) { +free_pages_exact (pgd,stage2_pgd_size (kvm)); +return -ENOMEM ; + } + +for_each_possible_cpu (cpu) + *per_cpu_ptr (mmu->last_vcpu_ran, cpu) =-1 ; + + mmu->kvm = kvm; mmu->pgd = pgd; mmu->pgd_phys = pgd_phys; mmu->vmid.vmid_gen =0 ; -kvm_init_s2_mmu (mmu); +kvm_init_nested_s2_mmu (mmu);return 0 ; }
@@ -1027,8 +1039,10 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)spin_unlock (&kvm->mmu_lock);/* Free the HW pgd, one page at a time */ -if (pgd) +if (pgd) {free_pages_exact (pgd,stage2_pgd_size (kvm)); +free_percpu (mmu->last_vcpu_ran); + } } static pud_t *stage2_get_pud(struct kvm_s2_mmu *mmu, struct kvm_mmu_memory_cache *cache,
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 42/94 KVM: arm64: nv: Introduce sys_reg_desc.forward_trap
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
This introduces a function prototype to determine if we need to forward system instruction traps to the virtual EL2. The implementation of forward_trap functions for each system instruction will be added in later patches.
--- arch/arm64/kvm/sys_regs.c | 9 +++++++++ arch/arm64/kvm/sys_regs.h | 6 ++++++ 2 files changed, 15 insertions(+)
arch/arm64/kvm/sys_regs.c +9 -0
@@ -25,6 +25,7 @@#include <asm/kvm_host.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>
@@ -2491,6 +2492,14 @@ static void perform_access(struct kvm_vcpu *vcpu,*/ BUG_ON (!r->access); +/* +* Forward this trap to the virtual EL2 if the guest hypervisor has +* configured to trap the current instruction. +*/ +if (nested_virt_in_use (vcpu) && r->forward_trap + &&unlikely (r->forward_trap (vcpu))) +return ; +/* Skip instruction if instructed so */ if (likely (r->access (vcpu, params, r)))kvm_skip_instr (vcpu,kvm_vcpu_trap_il_is32bit (vcpu));
arch/arm64/kvm/sys_regs.h +6 -0
@@ -54,6 +54,12 @@ struct sys_reg_desc {int (*set_user)(struct kvm_vcpu *vcpu,const struct sys_reg_desc *rd,const struct kvm_one_reg *reg,void __user *uaddr); +/* +* Forward the trap to the virtual EL2 if the guest hypervisor has +* configured to trap the current instruction. +*/ +bool (*forward_trap)(struct kvm_vcpu *vcpu); +/* Return mask of REG_* runtime visibility overrides */ unsigned int (*visibility)(const struct kvm_vcpu *vcpu,const struct sys_reg_desc *rd);
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 43/94 KVM: arm64: nv: Set a handler for the system instruction traps
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
When HCR.NV bit is set, execution of the EL2 translation regime address aranslation instructions and TLB maintenance instructions are trapped to EL2. In addition, execution of the EL1 translation regime address aranslation instructions and TLB maintenance instructions that are only accessible from EL2 and above are trapped to EL2. In these cases, ESR_EL2.EC will be set to 0x18.
Rework the system instruction emulation framework to handle potentially all system instruction traps other than MSR/MRS instructions. Those system instructions would be AT and TLBI instructions controlled by HCR_EL2.NV, AT, and TTLB bits.
--- arch/arm64/include/asm/kvm_coproc.h | 2 +- arch/arm64/kvm/handle_exit.c | 2 +- arch/arm64/kvm/sys_regs.c | 48 +++++++++++++++++++++++------ 3 files changed, 41 insertions(+), 11 deletions(-)
arch/arm64/include/asm/kvm_coproc.h +1 -1
@@ -32,7 +32,7 @@ int kvm_handle_cp14_32(struct kvm_vcpu *vcpu, struct kvm_run *run);int kvm_handle_cp14_64 (struct kvm_vcpu *vcpu,struct kvm_run *run);int kvm_handle_cp15_32 (struct kvm_vcpu *vcpu,struct kvm_run *run);int kvm_handle_cp15_64 (struct kvm_vcpu *vcpu,struct kvm_run *run); -int kvm_handle_sys_reg (struct kvm_vcpu *vcpu,struct kvm_run *run); +int kvm_handle_sys (struct kvm_vcpu *vcpu,struct kvm_run *run);#define kvm_coproc_table_init kvm_sys_reg_table_initvoid kvm_sys_reg_table_init (void );
arch/arm64/kvm/handle_exit.c +1 -1
@@ -275,7 +275,7 @@ static exit_handle_fn arm_exit_handlers[] = { [ESR_ELx_EC_SMC32] = handle_smc, [ESR_ELx_EC_HVC64] = handle_hvc, [ESR_ELx_EC_SMC64] = handle_smc, - [ESR_ELx_EC_SYS64] = kvm_handle_sys_reg, + [ESR_ELx_EC_SYS64] = kvm_handle_sys, [ESR_ELx_EC_SVE] = handle_sve, [ESR_ELx_EC_ERET] = kvm_handle_eret, [ESR_ELx_EC_IABT_LOW] = kvm_handle_guest_abort,
arch/arm64/kvm/sys_regs.c +39 -9
88 lines not on this page · raw message
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 44/94 KVM: arm64: nv: Trap and emulate AT instructions from virtual EL2
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
When supporting nested virtualization a guest hypervisor executing AT instructions must be trapped and emulated by the host hypervisor, because untrapped AT instructions operating on S1E1 will use the wrong translation regieme (the one used to emulate virtual EL2 in EL1 instead of virtual EL1) and AT instructions operating on S12 will not work from EL1.
This patch does several things.
1. List and define all AT system instructions to emulate and document the emulation design.
2. Implement AT instruction handling logic in EL2. This will be used to emulate AT instructions executed in the virtual EL2.
AT instruction emulation works by loading the proper processor context, which depends on the trapped instruction and the virtual HCR_EL2, to the EL1 virtual memory control registers and executing AT instructions. Note that ctxt->hw_sys_regs is expected to have the proper processor context before calling the handling function(__kvm_at_insn) implemented in this patch.
4. Emulate AT S1E[01] instructions by issuing the same instructions in EL2. We set the physical EL1 registers, NV and NV1 bits as described in the AT instruction emulation overview.
5. Emulate AT A12E[01] instructions in two steps: First, do the stage-1 translation by reusing the existing AT emulation functions. Second, do the stage-2 translation by walking the guest hypervisor's stage-2 page table in software. Record the translation result to PAR_EL1.
6. Emulate AT S1E2 instructions by issuing the corresponding S1E1 instructions in EL2. We set the physical EL1 registers and the HCR_EL2 register as described in the AT instruction emulation overview.
7. Forward system instruction traps to the virtual EL2 if the corresponding virtual AT bit is set in the virtual HCR_EL2.
[ Much logic above has been reworked by Marc Zyngier ]
--- arch/arm64/include/asm/kvm_arm.h | 2 + arch/arm64/include/asm/kvm_asm.h | 2 + arch/arm64/include/asm/sysreg.h | 17 +++ arch/arm64/kvm/hyp/Makefile | 1 + arch/arm64/kvm/hyp/at.c | 231 +++++++++++++++++++++++++++++++ arch/arm64/kvm/hyp/switch.c | 13 +- arch/arm64/kvm/sys_regs.c | 201 ++++++++++++++++++++++++++- 7 files changed, 463 insertions(+), 4 deletions(-) create mode 100644 arch/arm64/kvm/hyp/at.c
arch/arm64/include/asm/kvm_arm.h +2 -0
@@ -13,6 +13,7 @@/* Hyp Configuration Register (HCR) bits */ #define HCR_FWB (UL(1) << 46) +#define HCR_AT (UL(1) << 44)#define HCR_NV1 (UL(1) << 43)#define HCR_NV (UL(1) << 42)#define HCR_API (UL(1) << 41)
@@ -107,6 +108,7 @@#define VTCR_EL2_TG0_16K TCR_TG0_16K#define VTCR_EL2_TG0_64K TCR_TG0_64K#define VTCR_EL2_SH0_MASK TCR_SH0_MASK +#define VTCR_EL2_SH0_SHIFT TCR_SH0_SHIFT#define VTCR_EL2_SH0_INNER TCR_SH0_INNER#define VTCR_EL2_ORGN0_MASK TCR_ORGN0_MASK#define VTCR_EL2_ORGN0_WBWA TCR_ORGN0_WBWA
arch/arm64/include/asm/kvm_asm.h +2 -0
@@ -64,6 +64,8 @@ extern void __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu);extern void __kvm_tlb_flush_local_vmid (struct kvm_vcpu *vcpu);extern void __kvm_timer_set_cntvoff (u32 cntvoff_low,u32 cntvoff_high); +extern void __kvm_at_s1e01 (struct kvm_vcpu *vcpu,u32 op,u64 vaddr); +extern void __kvm_at_s1e2 (struct kvm_vcpu *vcpu,u32 op,u64 vaddr);extern int kvm_vcpu_run_vhe (struct kvm_vcpu *vcpu);
arch/arm64/include/asm/sysreg.h +17 -0
@@ -532,6 +532,23 @@#define SYS_SP_EL2 sys_reg(3, 6, 4, 1, 0) +/* AT instructions */ +#define AT_Op0 1 +#define AT_CRn 7 + +#define OP_AT_S1E1R sys_insn(AT_Op0, 0, AT_CRn, 8, 0) +#define OP_AT_S1E1W sys_insn(AT_Op0, 0, AT_CRn, 8, 1) +#define OP_AT_S1E0R sys_insn(AT_Op0, 0, AT_CRn, 8, 2) +#define OP_AT_S1E0W sys_insn(AT_Op0, 0, AT_CRn, 8, 3) +#define OP_AT_S1E1RP sys_insn(AT_Op0, 0, AT_CRn, 9, 0) +#define OP_AT_S1E1WP sys_insn(AT_Op0, 0, AT_CRn, 9, 1) +#define OP_AT_S1E2R sys_insn(AT_Op0, 4, AT_CRn, 8, 0) +#define OP_AT_S1E2W sys_insn(AT_Op0, 4, AT_CRn, 8, 1) +#define OP_AT_S12E1R sys_insn(AT_Op0, 4, AT_CRn, 8, 4) +#define OP_AT_S12E1W sys_insn(AT_Op0, 4, AT_CRn, 8, 5) +#define OP_AT_S12E0R sys_insn(AT_Op0, 4, AT_CRn, 8, 6) +#define OP_AT_S12E0W sys_insn(AT_Op0, 4, AT_CRn, 8, 7) +/* Common SCTLR_ELx flags. */ #define SCTLR_ELx_DSSBS (BIT(44))#define SCTLR_ELx_ENIA (BIT(31))
arch/arm64/kvm/hyp/Makefile +1 -0
@@ -19,6 +19,7 @@ obj-$(CONFIG_KVM_ARM_HOST) += entry.o obj-$(CONFIG_KVM_ARM_HOST) += switch.o obj-$(CONFIG_KVM_ARM_HOST) += fpsimd.o obj-$(CONFIG_KVM_ARM_HOST) += tlb.o +obj-$(CONFIG_KVM_ARM_HOST) += at.o obj-$(CONFIG_KVM_ARM_HOST) += hyp-entry.o # KVM code is run at a different exception code with a different map, so
arch/arm64/kvm/hyp/at.c new file +231 -0
231 lines not on this page · raw message
arch/arm64/kvm/hyp/switch.c +11 -2
@@ -157,9 +157,10 @@ static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu)if (!vcpu_el2_e2h_is_set (vcpu)) {/* * For a guest hypervisor on v8.0, trap and emulate -* the EL1 virtual memory control register accesses. +* the EL1 virtual memory control register accesses +* as well as the AT S1 operations. */ - hcr |=HCR_TVM |HCR_TRVM |HCR_NV1 ; + hcr |=HCR_TVM |HCR_TRVM |HCR_AT |HCR_NV1 ; }else {/* * For a guest hypervisor on v8.1 (VHE), allow to
@@ -182,6 +183,14 @@ static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu) hcr &= ~HCR_TVM ; hcr |= vhcr_el2 & (HCR_TVM |HCR_TRVM ); + +/* +* If we're using the EL1 translation regime +* (TGE clear, then ensure that AT S1 ops are +* trapped too. +*/ +if (!vcpu_el2_tge_is_set (vcpu)) + hcr |=HCR_AT ; } }
arch/arm64/kvm/sys_regs.c +199 -2
217 lines not on this page · raw message
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 45/94 KVM: arm64: nv: Trap and emulate TLBI instructions from virtual EL2
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
When supporting nested virtualization a guest hypervisor executing TLBI instructions must be trapped and emulated by the host hypervisor, because the guest hypervisor can only affect physical TLB entries relating to its own execution environment (virtual EL2 in EL1) but not to the nested guests as required by the semantics of the instructions and TLBI instructions might also result in updates (invalidations) to shadow page tables.
This patch does several things.
1. List and define all TLBI system instructions to emulate.
2. Emulate TLBI ALLE2(IS) instruction executed in the virtual EL2. Since we emulate the virtual EL2 in the EL1, we invalidate EL1&0 regime stage 1 TLB entries with setting vttbr_el2 having the VMID of the virtual EL2.
3. Emulate TLBI VAE2* instruction executed in the virtual EL2. Based on the same principle as TLBI ALLE2 instruction, we can simply emulate those instructions by executing corresponding VAE1* instructions with the virtual EL2's VMID assigned by the host hypervisor.
Note that we are able to emulate TLBI ALLE2IS precisely by only invalidating stage 1 TLB entries via TLBI VMALL1IS instruction, but to make it simeple, we reuse the existing function, __kvm_tlb_flush_vmid(), which invalidates both of stage 1 and 2 TLB entries.
4. TLBI ALLE1(IS) instruction invalidates all EL1&0 regime stage 1 and 2 TLB entries (on all PEs in the same Inner Shareable domain). To emulate these instructions, we first need to clear all the mappings in the shadow page tables since executing those instructions implies the change of mappings in the stage 2 page tables maintained by the guest hypervisor. We then need to invalidate all EL1&0 regime stage 1 and 2 TLB entries of all VMIDs, which are assigned by the host hypervisor, for this VM.
5. Based on the same principle as TLBI ALLE1(IS) emulation, we clear the mappings in the shadow stage-2 page tables and invalidate TLB entries. But this time we do it only for the current VMID from the guest hypervisor's perspective, not for all VMIDs.
6. Based on the same principle as TLBI ALLE1(IS) and TLBI VMALLS12E1(IS) emulation, we clear the mappings in the shadow stage-2 page tables and invalidate TLB entries. We do it only for one mapping for the current VMID from the guest hypervisor's view.
7. Forward system instruction traps to the virtual EL2 if a corresponding bit in the virtual HCR_EL2 is set.
8. Even though a guest hypervisor can execute TLBI instructions that are accesible at EL1 without trap, it's wrong; All those TLBI instructions work based on current VMID, and when running a guest hypervisor current VMID is the one for itself, not the one from the virtual vttbr_el2. So letting a guest hypervisor execute those TLBI instructions results in invalidating its own TLB entries and leaving invalid TLB entries unhandled.
Therefore we trap and emulate those TLBI instructions. The emulation is simple; we find a shadow VMID mapped to the virtual vttbr_el2, set it in the physical vttbr_el2, then execute the same instruction in EL2.
We don't set HCR_EL2.TTLB bit yet.
[ Changes performed by Marc Zynger:
The TLBI handling code more or less directly execute the same instruction that has been trapped (with an EL2->EL1 conversion in the case of an EL2 TLBI), but that's unfortunately not enough:
- TLBIs must be upgraded to the Inner Shareable domain to account for vcpu migration, just like we already have with HCR_EL2.FB.
- The DSB instruction that synchronises these must thus be on the Inner Shareable domain as well.
- Prior to executing the TLBI, we need another DSB ISHST to make sure that the update to the page tables is now visible.
Ordering of system instructions fixed
- The current TLB invalidation code is pretty buggy, as it assume a page mapping. On the contrary, it is likely that TLB invalidation will cover more than a single page, and the size should be decided by the guests configuration (and not the host's).
Since we don't cache the guest mapping sizes in the shadow PT yet, let's assume the worse case (a block mapping) and invalidate that.
Take this opportunity to fix the decoding of the parameter (it isn't a straight IPA).
- In general, we always emulate local TBL invalidations as being as upgraded to the Inner Shareable domain so that we can easily deal with vcpu migration. This is consistent with the fact that we set HCR_EL2.FB when running non-nested VMs.
So let's emulate TLBI ALLE2 as ALLE2IS.
]
[ Changes performed by Christoffer Dall:
Sometimes when we are invalidating the TLB for a certain S2 MMU context, this context can also have EL2 context associated with it and we have to invalidate this too.
]
--- arch/arm64/include/asm/kvm_asm.h | 2 + arch/arm64/include/asm/sysreg.h | 36 ++++++ arch/arm64/kvm/hyp/switch.c | 4 +- arch/arm64/kvm/hyp/tlb.c | 81 ++++++++++++ arch/arm64/kvm/sys_regs.c | 216 +++++++++++++++++++++++++++++++ virt/kvm/arm/mmu.c | 18 ++- 6 files changed, 354 insertions(+), 3 deletions(-)
arch/arm64/include/asm/kvm_asm.h +2 -0
@@ -62,6 +62,8 @@ extern void __kvm_flush_vm_context(void);extern void __kvm_tlb_flush_vmid_ipa (struct kvm_s2_mmu *mmu,phys_addr_t ipa);extern void __kvm_tlb_flush_vmid (struct kvm_s2_mmu *mmu);extern void __kvm_tlb_flush_local_vmid (struct kvm_vcpu *vcpu); +extern void __kvm_tlb_vae2is (struct kvm_s2_mmu *mmu,u64 va,u64 sys_encoding); +extern void __kvm_tlb_el1_instr (struct kvm_s2_mmu *mmu,u64 val,u64 sys_encoding);extern void __kvm_timer_set_cntvoff (u32 cntvoff_low,u32 cntvoff_high);extern void __kvm_at_s1e01 (struct kvm_vcpu *vcpu,u32 op,u64 vaddr);
arch/arm64/include/asm/sysreg.h +36 -0
@@ -549,6 +549,42 @@#define OP_AT_S12E0R sys_insn(AT_Op0, 4, AT_CRn, 8, 6)#define OP_AT_S12E0W sys_insn(AT_Op0, 4, AT_CRn, 8, 7) +/* TLBI instructions */ +#define TLBI_Op0 1 +#define TLBI_Op1_EL1 0/* Accessible from EL1 or higher */ +#define TLBI_Op1_EL2 4/* Accessible from EL2 or higher */ +#define TLBI_CRn 8 +#define tlbi_insn_el1 (CRm, Op2) sys_insn(TLBI_Op0, TLBI_Op1_EL1, TLBI_CRn, (CRm), (Op2)) +#define tlbi_insn_el2 (CRm, Op2) sys_insn(TLBI_Op0, TLBI_Op1_EL2, TLBI_CRn, (CRm), (Op2)) + +#define OP_TLBI_VMALLE1IS tlbi_insn_el1(3, 0) +#define OP_TLBI_VAE1IS tlbi_insn_el1(3, 1) +#define OP_TLBI_ASIDE1IS tlbi_insn_el1(3, 2) +#define OP_TLBI_VAAE1IS tlbi_insn_el1(3, 3) +#define OP_TLBI_VALE1IS tlbi_insn_el1(3, 5) +#define OP_TLBI_VAALE1IS tlbi_insn_el1(3, 7) +#define OP_TLBI_VMALLE1 tlbi_insn_el1(7, 0) +#define OP_TLBI_VAE1 tlbi_insn_el1(7, 1) +#define OP_TLBI_ASIDE1 tlbi_insn_el1(7, 2) +#define OP_TLBI_VAAE1 tlbi_insn_el1(7, 3) +#define OP_TLBI_VALE1 tlbi_insn_el1(7, 5) +#define OP_TLBI_VAALE1 tlbi_insn_el1(7, 7) + +#define OP_TLBI_IPAS2E1IS tlbi_insn_el2(0, 1) +#define OP_TLBI_IPAS2LE1IS tlbi_insn_el2(0, 5) +#define OP_TLBI_ALLE2IS tlbi_insn_el2(3, 0) +#define OP_TLBI_VAE2IS tlbi_insn_el2(3, 1) +#define OP_TLBI_ALLE1IS tlbi_insn_el2(3, 4) +#define OP_TLBI_VALE2IS tlbi_insn_el2(3, 5) +#define OP_TLBI_VMALLS12E1IS tlbi_insn_el2(3, 6) +#define OP_TLBI_IPAS2E1 tlbi_insn_el2(4, 1) +#define OP_TLBI_IPAS2LE1 tlbi_insn_el2(4, 5) +#define OP_TLBI_ALLE2 tlbi_insn_el2(7, 0) +#define OP_TLBI_VAE2 tlbi_insn_el2(7, 1) +#define OP_TLBI_ALLE1 tlbi_insn_el2(7, 4) +#define OP_TLBI_VALE2 tlbi_insn_el2(7, 5) +#define OP_TLBI_VMALLS12E1 tlbi_insn_el2(7, 6) +/* Common SCTLR_ELx flags. */ #define SCTLR_ELx_DSSBS (BIT(44))#define SCTLR_ELx_ENIA (BIT(31))
arch/arm64/kvm/hyp/switch.c +2 -2
@@ -160,7 +160,7 @@ static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu)* the EL1 virtual memory control register accesses * as well as the AT S1 operations. */ - hcr |=HCR_TVM |HCR_TRVM |HCR_AT |HCR_NV1 ; + hcr |=HCR_TVM |HCR_TRVM |HCR_AT |HCR_TTLB |HCR_NV1 ; }else {/* * For a guest hypervisor on v8.1 (VHE), allow to
@@ -190,7 +190,7 @@ static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu)* trapped too. */ if (!vcpu_el2_tge_is_set (vcpu)) - hcr |=HCR_AT ; + hcr |=HCR_AT |HCR_TTLB ; } }
arch/arm64/kvm/hyp/tlb.c +81 -0
84 lines not on this page · raw message
arch/arm64/kvm/sys_regs.c +216 -0
240 lines not on this page · raw message
virt/kvm/arm/mmu.c +17 -1
@@ -55,7 +55,23 @@ static bool memslot_is_logging(struct kvm_memory_slot *memslot)*/ void kvm_flush_remote_tlbs (struct kvm *kvm) { -kvm_call_hyp (__kvm_tlb_flush_vmid, &kvm->arch.mmu); +struct kvm_s2_mmu *mmu = &kvm->arch.mmu; + +if (mmu == &kvm->arch.mmu) { +/* +* For a normal (i.e. non-nested) guest, flush entries for the +* given VMID * +*/ +kvm_call_hyp (__kvm_tlb_flush_vmid, mmu); + }else { +/* +* When supporting nested virtualization, we can have multiple +* VMIDs in play for each VCPU in the VM, so it's really not +* worth it to try to quiesce the system and flush all the +* VMIDs that may be in use, instead just nuke the whole thing. +*/ +kvm_call_hyp (__kvm_flush_vm_context); + } }static void kvm_tlb_flush_vmid_ipa (struct kvm_s2_mmu *mmu,phys_addr_t ipa)
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 46/94 KVM: arm64: nv: Fold guest's HCR_EL2 configuration into the host's
In reply to Marc ZyngierWhen entering a L2 guest (nested virt enabled, but not in hypervisor context), we need to honor the traps the L1 guest has asked enabled.
For now, just OR the guest's HCR_EL2 into the host's. We may have to do some filtering in the future though.
--- arch/arm64/kvm/hyp/switch.c | 2 ++ 1 file changed, 2 insertions(+)
arch/arm64/kvm/hyp/switch.c +2 -0
@@ -192,6 +192,8 @@ static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu)if (!vcpu_el2_tge_is_set (vcpu)) hcr |=HCR_AT |HCR_TTLB ; } + }else if (nested_virt_in_use (vcpu)) { + hcr |=__vcpu_sys_reg (vcpu,HCR_EL2 ); }write_sysreg (hcr, hcr_el2);
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 47/94 KVM: arm64: nv: Handle traps for timer _EL02 and _EL2 sysregs accessors
In reply to Marc ZyngierFrom: Andre Przywara <[email protected]>
Add trap handlers for the timer system registers accessed from a guest hypervisors using either _EL02 or _EL2 system register access instructions.
--- arch/arm64/include/asm/sysreg.h | 6 ++++++ arch/arm64/kvm/sys_regs.c | 15 +++++++++++++++ 2 files changed, 21 insertions(+)
arch/arm64/include/asm/sysreg.h +6 -0
@@ -496,6 +496,12 @@#define SYS_CNTVOFF_EL2 sys_reg(3, 4, 14, 0, 3)#define SYS_CNTHCTL_EL2 sys_reg(3, 4, 14, 1, 0) +#define SYS_CNTHP_TVAL_EL2 sys_reg(3, 4, 14, 2, 0) +#define SYS_CNTHP_CTL_EL2 sys_reg(3, 4, 14, 2, 1) +#define SYS_CNTHP_CVAL_EL2 sys_reg(3, 4, 14, 2, 2) +#define SYS_CNTHV_TVAL_EL2 sys_reg(3, 4, 14, 3, 0) +#define SYS_CNTHV_CTL_EL2 sys_reg(3, 4, 14, 3, 1) +#define SYS_CNTHV_CVAL_EL2 sys_reg(3, 4, 14, 3, 2)/* VHE encodings for architectural EL0/1 system registers */ #define SYS_SCTLR_EL12 sys_reg(3, 5, 1, 0, 0)
arch/arm64/kvm/sys_regs.c +15 -0
@@ -2078,6 +2078,13 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_CNTVOFF_EL2), access_rw, reset_val, CNTVOFF_EL2, 0 }, { SYS_DESC(SYS_CNTHCTL_EL2), access_rw, reset_val, CNTHCTL_EL2, 0 }, + { SYS_DESC(SYS_CNTHP_TVAL_EL2), access_arch_timer }, + { SYS_DESC(SYS_CNTHP_CTL_EL2), access_arch_timer }, + { SYS_DESC(SYS_CNTHP_CVAL_EL2), access_arch_timer }, + { SYS_DESC(SYS_CNTHV_TVAL_EL2), access_arch_timer }, + { SYS_DESC(SYS_CNTHV_CTL_EL2), access_arch_timer }, + { SYS_DESC(SYS_CNTHV_CVAL_EL2), access_arch_timer }, + { SYS_DESC(SYS_SCTLR_EL12), access_vm_reg, reset_val, SCTLR_EL1, 0x00C50078 }, { SYS_DESC(SYS_CPACR_EL12), access_rw, reset_val, CPACR_EL1, 0 }, { SYS_DESC(SYS_TTBR0_EL12), access_vm_reg, reset_unknown, TTBR0_EL1 },
@@ -2095,6 +2102,14 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_CONTEXTIDR_EL12), access_vm_reg, reset_val, CONTEXTIDR_EL1, 0 }, { SYS_DESC(SYS_CNTKCTL_EL12), access_rw, reset_val, CNTKCTL_EL1, 0 }, + { SYS_DESC(SYS_CNTP_TVAL_EL02), access_arch_timer }, + { SYS_DESC(SYS_CNTP_CTL_EL02), access_arch_timer }, + { SYS_DESC(SYS_CNTP_CVAL_EL02), access_arch_timer }, + + { SYS_DESC(SYS_CNTV_TVAL_EL02), access_arch_timer }, + { SYS_DESC(SYS_CNTV_CTL_EL02), access_arch_timer }, + { SYS_DESC(SYS_CNTV_CVAL_EL02), access_arch_timer }, + { SYS_DESC(SYS_SP_EL2), NULL, reset_unknown, SP_EL2 }, };
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 48/94 KVM: arm64: nv: arch_timer: Support hyp timer emulation
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
Emulating EL2 also means emulating the EL2 timers. To do so, we expand our timer framework to deal with at most 4 timers. At any given time, two timers are using the HW timers, and the two others are purely emulated.
The role of deciding which is which at any given time is left to a mapping function which is called every time we need to make such a decision.
--- arch/arm/include/asm/kvm_emulate.h | 2 + include/kvm/arm_arch_timer.h | 5 ++ include/kvm/arm_vgic.h | 1 + virt/kvm/arm/arch_timer.c | 124 ++++++++++++++++++++++++++++- virt/kvm/arm/trace.h | 6 +- virt/kvm/arm/vgic/vgic.c | 15 ++++ 6 files changed, 149 insertions(+), 4 deletions(-)
arch/arm/include/asm/kvm_emulate.h +2 -0
@@ -369,4 +369,6 @@ static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,static inline void vcpu_ptrauth_setup_lazy (struct kvm_vcpu *vcpu) {} +static inline bool is_hyp_ctxt (struct kvm_vcpu *vcpu) {return false; } +#endif /* __ARM_KVM_EMULATE_H__ */
include/kvm/arm_arch_timer.h +5 -0
@@ -13,6 +13,8 @@enum kvm_arch_timers {TIMER_PTIMER ,TIMER_VTIMER , +TIMER_HVTIMER , +TIMER_HPTIMER ,NR_KVM_TIMERS };
@@ -54,6 +56,7 @@ struct arch_timer_context {struct timer_map {struct arch_timer_context *direct_vtimer;struct arch_timer_context *direct_ptimer; +struct arch_timer_context *emul_vtimer;struct arch_timer_context *emul_ptimer; };
@@ -98,6 +101,8 @@ bool kvm_arch_timer_get_input_level(int vintid);#define vcpu_get_timer (v,t) (&vcpu_timer(v)->timers[(t)])#define vcpu_vtimer (v) (&(v)->arch.timer_cpu.timers[TIMER_VTIMER])#define vcpu_ptimer (v) (&(v)->arch.timer_cpu.timers[TIMER_PTIMER]) +#define vcpu_hvtimer (v) (&(v)->arch.timer_cpu.timers[TIMER_HVTIMER]) +#define vcpu_hptimer (v) (&(v)->arch.timer_cpu.timers[TIMER_HPTIMER])#define arch_timer_ctx_index (ctx) ((ctx) - vcpu_timer((ctx)->vcpu)->timers)
include/kvm/arm_vgic.h +1 -0
@@ -346,6 +346,7 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,int kvm_vgic_map_phys_irq (struct kvm_vcpu *vcpu,unsigned int host_irq,u32 vintid,bool (*get_input_level)(int vindid));int kvm_vgic_unmap_phys_irq (struct kvm_vcpu *vcpu,unsigned int vintid); +int kvm_vgic_get_map (struct kvm_vcpu *vcpu,unsigned int vintid);bool kvm_vgic_map_is_active (struct kvm_vcpu *vcpu,unsigned int vintid);int kvm_vgic_vcpu_pending_irq (struct kvm_vcpu *vcpu);
virt/kvm/arm/arch_timer.c +121 -3
300 lines not on this page · raw message
virt/kvm/arm/trace.h +5 -1
@@ -271,6 +271,7 @@ TRACE_EVENT(kvm_get_timer_map, __field( unsigned long, vcpu_id ) __field( int, direct_vtimer ) __field( int, direct_ptimer ) + __field( int, emul_vtimer ) __field( int, emul_ptimer ) ),
@@ -279,14 +280,17 @@ TRACE_EVENT(kvm_get_timer_map, __entry->direct_vtimer =arch_timer_ctx_index (map->direct_vtimer); __entry->direct_ptimer = (map->direct_ptimer) ?arch_timer_ctx_index (map->direct_ptimer) :-1 ; + __entry->emul_vtimer = + (map->emul_vtimer) ?arch_timer_ctx_index (map->emul_vtimer) :-1 ; __entry->emul_ptimer = (map->emul_ptimer) ?arch_timer_ctx_index (map->emul_ptimer) :-1 ; ), -TP_printk ("VCPU: %ld, dv: %d, dp: %d, ep: %d" , +TP_printk ("VCPU: %ld, dv: %d, dp: %d, ev: %d, ep: %d" , __entry->vcpu_id, __entry->direct_vtimer, __entry->direct_ptimer, + __entry->emul_vtimer, __entry->emul_ptimer) );
virt/kvm/arm/vgic/vgic.c +15 -0
@@ -573,6 +573,21 @@ int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid)return 0 ; } +int kvm_vgic_get_map (struct kvm_vcpu *vcpu,unsigned int vintid) +{ +struct vgic_irq *irq =vgic_get_irq (vcpu->kvm, vcpu, vintid); +unsigned long flags; +int ret =-1 ; + +raw_spin_lock_irqsave (&irq->irq_lock, flags); +if (irq->hw) + ret = irq->hwintid; +raw_spin_unlock_irqrestore (&irq->irq_lock, flags); + +vgic_put_irq (vcpu->kvm, irq); +return ret; +} +/** * kvm_vgic_set_owner - Set the owner of an interrupt for a VM *
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 49/94 KVM: arm64: nv: Propagate CNTVOFF_EL2 to the virtual EL1 timer
In reply to Marc ZyngierWe need to allow a guest hypervisor to virtualize the virtual timer. FOr that, let's propagate CNTVOFF_EL2 to the guest's view of that timer.
--- arch/arm64/include/asm/kvm_host.h | 1 - arch/arm64/kvm/sys_regs.c | 7 ++++++- include/kvm/arm_arch_timer.h | 1 + virt/kvm/arm/arch_timer.c | 8 ++++++++ 4 files changed, 15 insertions(+), 2 deletions(-)
arch/arm64/include/asm/kvm_host.h +0 -1
@@ -247,7 +247,6 @@ enum vcpu_sysreg {RMR_EL2 ,/* Reset Management Register */ CONTEXTIDR_EL2 ,/* Context ID Register (EL2) */ TPIDR_EL2 ,/* EL2 Software Thread ID Register */ -CNTVOFF_EL2 ,/* Counter-timer Virtual Offset register */ CNTHCTL_EL2 ,/* Counter-timer Hypervisor Control register */ SP_EL2, /* EL2 Stack Pointer */
arch/arm64/kvm/sys_regs.c +6 -1
@@ -1373,6 +1373,11 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu, tmr =TIMER_PTIMER ; treg =TIMER_REG_CVAL ;break ; +case SYS_CNTVOFF_EL2 : + tmr =TIMER_VTIMER ; + treg =TIMER_REG_VOFF ; +break ; +default :BUG (); }
@@ -2075,7 +2080,7 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_CONTEXTIDR_EL2), access_rw, reset_val, CONTEXTIDR_EL2, 0 }, { SYS_DESC(SYS_TPIDR_EL2), access_rw, reset_val, TPIDR_EL2, 0 }, - { SYS_DESC(SYS_CNTVOFF_EL2), access_rw, reset_val, CNTVOFF_EL2, 0 }, + { SYS_DESC(SYS_CNTVOFF_EL2), access_arch_timer }, { SYS_DESC(SYS_CNTHCTL_EL2), access_rw, reset_val, CNTHCTL_EL2, 0 }, { SYS_DESC(SYS_CNTHP_TVAL_EL2), access_arch_timer },
include/kvm/arm_arch_timer.h +1 -0
@@ -23,6 +23,7 @@ enum kvm_arch_timer_regs {TIMER_REG_CVAL ,TIMER_REG_TVAL ,TIMER_REG_CTL , + TIMER_REG_VOFF, }; struct arch_timer_context {
virt/kvm/arm/arch_timer.c +8 -0
@@ -917,6 +917,10 @@ static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu, val =kvm_phys_timer_read () - timer->cntvoff;break ; +case TIMER_REG_VOFF : + val = timer->cntvoff; +break ; +default :BUG (); }
@@ -959,6 +963,10 @@ static void kvm_arm_timer_write(struct kvm_vcpu *vcpu, timer->cnt_cval = val;break ; +case TIMER_REG_VOFF : + timer->cntvoff = val; +break ; +default :BUG (); }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 50/94 KVM: arm64: nv: Load timer before the GIC
In reply to Marc ZyngierIn order for vgic_v3_load_nested to be able to observe which timer interrupts have the HW bit set for the current context, the timers must have been loaded in the new mode and the right timer mapped to their corresponding HW IRQs.
At the moment, we load the GIC first, meaning that timer interrupts injected to an L2 guest will never have the HW bit set (we see the old configuration).
Swapping the two loads solves this particular problem.
--- virt/kvm/arm/arm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
virt/kvm/arm/arm.c +1 -1
@@ -344,8 +344,8 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) vcpu->cpu = cpu; vcpu->arch.host_cpu_context = &cpu_data->host_ctxt; -kvm_vgic_load (vcpu);kvm_timer_vcpu_load (vcpu); +kvm_vgic_load (vcpu);kvm_vcpu_load_sysregs (vcpu);kvm_arch_vcpu_load_fp (vcpu);kvm_vcpu_pmu_restore_guest (vcpu);
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 51/94 KVM: arm64: nv: vgic-v3: Take cpu_if pointer directly instead of vcpu
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
If we move the used_lrs field to the version-specific cpu interface structure, the following functions only operate on the struct vgic_v3_cpu_if and not the full vcpu:
__vgic_v3_save_state
__vgic_v3_restore_state
__vgic_v3_activate_traps
__vgic_v3_deactivate_traps
__vgic_v3_save_aprs
__vgic_v3_restore_aprs
This is going to be very useful for nested virt, so move the used_lrs field and change the prototypes and implementations of these functions to take the cpu_if parameter directly.
No functional change.
--- arch/arm/include/asm/kvm_hyp.h | 12 ++++++------ arch/arm/kvm/hyp/switch.c | 8 ++++---- arch/arm64/include/asm/kvm_hyp.h | 12 ++++++------ arch/arm64/kvm/hyp/switch.c | 8 ++++---- include/kvm/arm_vgic.h | 5 ++++- virt/kvm/arm/hyp/vgic-v3-sr.c | 33 ++++++++++---------------------- virt/kvm/arm/vgic/vgic-v2.c | 10 +++++----- virt/kvm/arm/vgic/vgic-v3.c | 14 ++++++++------ virt/kvm/arm/vgic/vgic.c | 25 ++++++++++++++++-------- 9 files changed, 64 insertions(+), 63 deletions(-)
arch/arm/include/asm/kvm_hyp.h +6 -6
@@ -103,12 +103,12 @@ void __vgic_v2_restore_state(struct kvm_vcpu *vcpu);void __sysreg_save_state (struct kvm_cpu_context *ctxt);void __sysreg_restore_state (struct kvm_cpu_context *ctxt); -void __vgic_v3_save_state (struct kvm_vcpu *vcpu); -void __vgic_v3_restore_state (struct kvm_vcpu *vcpu); -void __vgic_v3_activate_traps (struct kvm_vcpu *vcpu); -void __vgic_v3_deactivate_traps (struct kvm_vcpu *vcpu); -void __vgic_v3_save_aprs (struct kvm_vcpu *vcpu); -void __vgic_v3_restore_aprs (struct kvm_vcpu *vcpu); +void __vgic_v3_save_state (struct vgic_v3_cpu_if *cpu_if); +void __vgic_v3_restore_state (struct vgic_v3_cpu_if *cpu_if); +void __vgic_v3_activate_traps (struct vgic_v3_cpu_if *cpu_if); +void __vgic_v3_deactivate_traps (struct vgic_v3_cpu_if *cpu_if); +void __vgic_v3_save_aprs (struct vgic_v3_cpu_if *cpu_if); +void __vgic_v3_restore_aprs (struct vgic_v3_cpu_if *cpu_if); asmlinkage void __vfp_save_state(struct vfp_hard_struct *vfp); asmlinkage void __vfp_restore_state(struct vfp_hard_struct *vfp);
arch/arm/kvm/hyp/switch.c +4 -4
@@ -79,16 +79,16 @@ static void __hyp_text __deactivate_vm(struct kvm_vcpu *vcpu) static void __hyp_text __vgic_save_state(struct kvm_vcpu *vcpu) {if (static_branch_unlikely (&kvm_vgic_global_state.gicv3_cpuif)) { -__vgic_v3_save_state (vcpu); -__vgic_v3_deactivate_traps (vcpu); +__vgic_v3_save_state (&vcpu->arch.vgic_cpu.vgic_v3); +__vgic_v3_deactivate_traps (&vcpu->arch.vgic_cpu.vgic_v3); } } static void __hyp_text __vgic_restore_state(struct kvm_vcpu *vcpu) {if (static_branch_unlikely (&kvm_vgic_global_state.gicv3_cpuif)) { -__vgic_v3_activate_traps (vcpu); -__vgic_v3_restore_state (vcpu); +__vgic_v3_activate_traps (&vcpu->arch.vgic_cpu.vgic_v3); +__vgic_v3_restore_state (&vcpu->arch.vgic_cpu.vgic_v3); } }
arch/arm64/include/asm/kvm_hyp.h +6 -6
@@ -48,12 +48,12 @@int __vgic_v2_perform_cpuif_access (struct kvm_vcpu *vcpu); -void __vgic_v3_save_state (struct kvm_vcpu *vcpu); -void __vgic_v3_restore_state (struct kvm_vcpu *vcpu); -void __vgic_v3_activate_traps (struct kvm_vcpu *vcpu); -void __vgic_v3_deactivate_traps (struct kvm_vcpu *vcpu); -void __vgic_v3_save_aprs (struct kvm_vcpu *vcpu); -void __vgic_v3_restore_aprs (struct kvm_vcpu *vcpu); +void __vgic_v3_save_state (struct vgic_v3_cpu_if *cpu_if); +void __vgic_v3_restore_state (struct vgic_v3_cpu_if *cpu_if); +void __vgic_v3_activate_traps (struct vgic_v3_cpu_if *cpu_if); +void __vgic_v3_deactivate_traps (struct vgic_v3_cpu_if *cpu_if); +void __vgic_v3_save_aprs (struct vgic_v3_cpu_if *cpu_if); +void __vgic_v3_restore_aprs (struct vgic_v3_cpu_if *cpu_if);int __vgic_v3_perform_cpuif_access (struct kvm_vcpu *vcpu);void __timer_enable_traps (struct kvm_vcpu *vcpu);
arch/arm64/kvm/hyp/switch.c +4 -4
@@ -306,8 +306,8 @@ static void __hyp_text __deactivate_vm(struct kvm_vcpu *vcpu) static void __hyp_text __hyp_vgic_save_state(struct kvm_vcpu *vcpu) {if (static_branch_unlikely (&kvm_vgic_global_state.gicv3_cpuif)) { -__vgic_v3_save_state (vcpu); -__vgic_v3_deactivate_traps (vcpu); +__vgic_v3_save_state (&vcpu->arch.vgic_cpu.vgic_v3); +__vgic_v3_deactivate_traps (&vcpu->arch.vgic_cpu.vgic_v3); } }
@@ -315,8 +315,8 @@ static void __hyp_text __hyp_vgic_save_state(struct kvm_vcpu *vcpu) static void __hyp_text __hyp_vgic_restore_state(struct kvm_vcpu *vcpu) {if (static_branch_unlikely (&kvm_vgic_global_state.gicv3_cpuif)) { -__vgic_v3_activate_traps (vcpu); -__vgic_v3_restore_state (vcpu); +__vgic_v3_activate_traps (&vcpu->arch.vgic_cpu.vgic_v3); +__vgic_v3_restore_state (&vcpu->arch.vgic_cpu.vgic_v3); } }
include/kvm/arm_vgic.h +4 -1
@@ -270,6 +270,8 @@ struct vgic_v2_cpu_if {u32 vgic_vmcr;u32 vgic_apr;u32 vgic_lr[VGIC_V2_MAX_LRS ]; + +unsigned int used_lrs; };struct vgic_v3_cpu_if {
@@ -287,6 +289,8 @@ struct vgic_v3_cpu_if {* linking the Linux IRQ subsystem and the ITS together. */ struct its_vpe its_vpe; + +unsigned int used_lrs; };struct vgic_cpu {
@@ -296,7 +300,6 @@ struct vgic_cpu {struct vgic_v3_cpu_if vgic_v3; }; -unsigned int used_lrs;struct vgic_irq private_irqs[VGIC_NR_PRIVATE_IRQS ];raw_spinlock_t ap_list_lock;/* Protects the ap_list */
virt/kvm/arm/hyp/vgic-v3-sr.c +10 -23
@@ -194,10 +194,9 @@ static u32 __hyp_text __vgic_v3_read_ap1rn(int n)return val; } -void __hyp_text __vgic_v3_save_state(struct kvm_vcpu *vcpu) +void __hyp_text __vgic_v3_save_state(struct vgic_v3_cpu_if *cpu_if) { -struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; -u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs; +u64 used_lrs = cpu_if->used_lrs;/* * Make sure stores to the GIC via the memory mapped interface
@@ -230,10 +229,9 @@ void __hyp_text __vgic_v3_save_state(struct kvm_vcpu *vcpu) } } -void __hyp_text __vgic_v3_restore_state(struct kvm_vcpu *vcpu) +void __hyp_text __vgic_v3_restore_state(struct vgic_v3_cpu_if *cpu_if) { -struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; -u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs; +u64 used_lrs = cpu_if->used_lrs;int i;if (used_lrs || cpu_if->its_vpe.its_vm) {
@@ -257,10 +255,8 @@ void __hyp_text __vgic_v3_restore_state(struct kvm_vcpu *vcpu) } } -void __hyp_text __vgic_v3_activate_traps(struct kvm_vcpu *vcpu) +void __hyp_text __vgic_v3_activate_traps(struct vgic_v3_cpu_if *cpu_if) { -struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; -/* * VFIQEn is RES1 if ICC_SRE_EL1.SRE is 1. This causes a * Group0 interrupt (as generated in GICv2 mode) to be
@@ -306,9 +302,8 @@ void __hyp_text __vgic_v3_activate_traps(struct kvm_vcpu *vcpu)write_gicreg (cpu_if->vgic_hcr,ICH_HCR_EL2 ); } -void __hyp_text __vgic_v3_deactivate_traps(struct kvm_vcpu *vcpu) +void __hyp_text __vgic_v3_deactivate_traps(struct vgic_v3_cpu_if *cpu_if) { -struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;u64 val;if (!cpu_if->vgic_sre) {
@@ -333,15 +328,11 @@ void __hyp_text __vgic_v3_deactivate_traps(struct kvm_vcpu *vcpu)write_gicreg (0 ,ICH_HCR_EL2 ); } -void __hyp_text __vgic_v3_save_aprs(struct kvm_vcpu *vcpu) +void __hyp_text __vgic_v3_save_aprs(struct vgic_v3_cpu_if *cpu_if) { -struct vgic_v3_cpu_if *cpu_if;u64 val;u32 nr_pre_bits; - vcpu =kern_hyp_va (vcpu); - cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; - val =read_gicreg (ICH_VTR_EL2 ); nr_pre_bits =vtr_to_nr_pre_bits (val);
@@ -370,15 +361,11 @@ void __hyp_text __vgic_v3_save_aprs(struct kvm_vcpu *vcpu) } } -void __hyp_text __vgic_v3_restore_aprs(struct kvm_vcpu *vcpu) +void __hyp_text __vgic_v3_restore_aprs(struct vgic_v3_cpu_if *cpu_if) { -struct vgic_v3_cpu_if *cpu_if;u64 val;u32 nr_pre_bits; - vcpu =kern_hyp_va (vcpu); - cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; - val =read_gicreg (ICH_VTR_EL2 ); nr_pre_bits =vtr_to_nr_pre_bits (val);
@@ -453,7 +440,7 @@ static int __hyp_text __vgic_v3_highest_priority_lr(struct kvm_vcpu *vcpu,u32 vmcr, u64 *lr_val) { - unsigned int used_lrs = vcpu->arch.vgic_cpu.used_lrs; + unsigned int used_lrs = vcpu->arch.vgic_cpu.vgic_v3.used_lrs;u8 priority = GICv3_IDLE_PRIORITY;int i, lr =-1 ;
@@ -492,7 +479,7 @@ static int __hyp_text __vgic_v3_highest_priority_lr(struct kvm_vcpu *vcpu, static int __hyp_text __vgic_v3_find_active_lr(struct kvm_vcpu *vcpu,int intid,u64 *lr_val) { -unsigned int used_lrs = vcpu->arch.vgic_cpu.used_lrs; +unsigned int used_lrs = vcpu->arch.vgic_cpu.vgic_v3.used_lrs;int i;for (i =0 ; i < used_lrs; i++) {
virt/kvm/arm/vgic/vgic-v2.c +5 -5
@@ -56,7 +56,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu) cpuif->vgic_hcr &= ~GICH_HCR_UIE ; -for (lr =0 ; lr < vgic_cpu->used_lrs; lr++) { +for (lr =0 ; lr < vgic_cpu->vgic_v2.used_lrs; lr++) {u32 val = cpuif->vgic_lr[lr];u32 cpuid, intid = val &GICH_LR_VIRTUALID ;struct vgic_irq *irq;
@@ -120,7 +120,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)vgic_put_irq (vcpu->kvm, irq); } - vgic_cpu->used_lrs =0 ; + cpuif->used_lrs =0 ; }/*
@@ -427,7 +427,7 @@ int vgic_v2_probe(const struct gic_kvm_info *info)static void save_lrs (struct kvm_vcpu *vcpu,void __iomem *base) {struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2; -u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs; +u64 used_lrs = cpu_if->used_lrs;u64 elrsr;int i;
@@ -448,7 +448,7 @@ static void save_lrs(struct kvm_vcpu *vcpu, void __iomem *base)void vgic_v2_save_state (struct kvm_vcpu *vcpu) {void __iomem *base = kvm_vgic_global_state.vctrl_base; -u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs; +u64 used_lrs = vcpu->arch.vgic_cpu.vgic_v2.used_lrs;if (!base)return ;
@@ -463,7 +463,7 @@ void vgic_v2_restore_state(struct kvm_vcpu *vcpu) {struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;void __iomem *base = kvm_vgic_global_state.vctrl_base; -u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs; +u64 used_lrs = cpu_if->used_lrs;int i;if (!base)
virt/kvm/arm/vgic/vgic-v3.c +8 -6
@@ -39,7 +39,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu) cpuif->vgic_hcr &= ~ICH_HCR_UIE ; -for (lr =0 ; lr < vgic_cpu->used_lrs; lr++) { +for (lr =0 ; lr < cpuif->used_lrs; lr++) {u64 val = cpuif->vgic_lr[lr];u32 intid, cpuid;struct vgic_irq *irq;
@@ -111,7 +111,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)vgic_put_irq (vcpu->kvm, irq); } - vgic_cpu->used_lrs =0 ; + cpuif->used_lrs =0 ; }/* Requires the irq to be locked already */
@@ -660,10 +660,10 @@ void vgic_v3_load(struct kvm_vcpu *vcpu)if (likely (cpu_if->vgic_sre))kvm_call_hyp (__vgic_v3_write_vmcr, cpu_if->vgic_vmcr); -kvm_call_hyp (__vgic_v3_restore_aprs, vcpu); +kvm_call_hyp (__vgic_v3_restore_aprs,kern_hyp_va (cpu_if));if (has_vhe ()) -__vgic_v3_activate_traps (vcpu); +__vgic_v3_activate_traps (cpu_if);WARN_ON (vgic_v4_load (vcpu)); }
@@ -678,12 +678,14 @@ void vgic_v3_vmcr_sync(struct kvm_vcpu *vcpu)void vgic_v3_put (struct kvm_vcpu *vcpu) { +struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; +WARN_ON (vgic_v4_put (vcpu, false));vgic_v3_vmcr_sync (vcpu); -kvm_call_hyp (__vgic_v3_save_aprs, vcpu); +kvm_call_hyp (__vgic_v3_save_aprs,kern_hyp_va (cpu_if));if (has_vhe ()) -__vgic_v3_deactivate_traps (vcpu); +__vgic_v3_deactivate_traps (cpu_if); }
virt/kvm/arm/vgic/vgic.c +17 -8
@@ -801,6 +801,7 @@ static void vgic_flush_lr_state(struct kvm_vcpu *vcpu)int count;bool multi_sgi;u8 prio =0xff ; +int i =0 ;lockdep_assert_held (&vgic_cpu->ap_list_lock);
@@ -842,11 +843,14 @@ static void vgic_flush_lr_state(struct kvm_vcpu *vcpu) } } - vcpu->arch.vgic_cpu.used_lrs = count; -/* Nuke remaining LRs */ -for ( ; count < kvm_vgic_global_state.nr_lr; count++) -vgic_clear_lr (vcpu, count); +for (i = count ; i < kvm_vgic_global_state.nr_lr; i++) +vgic_clear_lr (vcpu, i); + +if (!static_branch_unlikely (&kvm_vgic_global_state.gicv3_cpuif)) + vcpu->arch.vgic_cpu.vgic_v2.used_lrs = count; +else + vcpu->arch.vgic_cpu.vgic_v3.used_lrs = count; }static inline bool can_access_vgic_from_kernel (void )
@@ -864,13 +868,13 @@ static inline void vgic_save_state(struct kvm_vcpu *vcpu)if (!static_branch_unlikely (&kvm_vgic_global_state.gicv3_cpuif))vgic_v2_save_state (vcpu);else -__vgic_v3_save_state (vcpu); +__vgic_v3_save_state (&vcpu->arch.vgic_cpu.vgic_v3); }/* Sync back the hardware VGIC state into our emulation after a guest's run. */ void kvm_vgic_sync_hwstate (struct kvm_vcpu *vcpu) { -struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; +int used_lrs;/* An empty ap_list_head implies used_lrs == 0 */ if (list_empty (&vcpu->arch.vgic_cpu.ap_list_head))
@@ -879,7 +883,12 @@ void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)if (can_access_vgic_from_kernel ())vgic_save_state (vcpu); -if (vgic_cpu->used_lrs) +if (!static_branch_unlikely (&kvm_vgic_global_state.gicv3_cpuif)) + used_lrs = vcpu->arch.vgic_cpu.vgic_v2.used_lrs; +else + used_lrs = vcpu->arch.vgic_cpu.vgic_v3.used_lrs; + +if (used_lrs)vgic_fold_lr_state (vcpu);vgic_prune_ap_list (vcpu); }
@@ -889,7 +898,7 @@ static inline void vgic_restore_state(struct kvm_vcpu *vcpu)if (!static_branch_unlikely (&kvm_vgic_global_state.gicv3_cpuif))vgic_v2_restore_state (vcpu);else -__vgic_v3_restore_state (vcpu); +__vgic_v3_restore_state (&vcpu->arch.vgic_cpu.vgic_v3); }/* Flush our emulation state into the GIC hardware before entering the guest. */
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 52/94 KVM: arm64: nv: Nested GICv3 Support
In reply to Marc ZyngierFrom: Jintack Lim <[email protected]>
When entering a nested VM, we set up the hypervisor control interface based on what the guest hypervisor has set. Especially, we investigate each list register written by the guest hypervisor whether HW bit is set. If so, we translate hw irq number from the guest's point of view to the real hardware irq number if there is a mapping.
--- arch/arm/include/asm/kvm_emulate.h | 1 + arch/arm/include/asm/kvm_host.h | 7 +- arch/arm/include/asm/kvm_nested.h | 1 + arch/arm64/include/asm/kvm_host.h | 7 +- arch/arm64/include/asm/kvm_nested.h | 1 + arch/arm64/kvm/Makefile | 1 + arch/arm64/kvm/nested.c | 16 +++ arch/arm64/kvm/sys_regs.c | 178 +++++++++++++++++++++++++++- include/kvm/arm_vgic.h | 18 +++ virt/kvm/arm/arm.c | 13 +- virt/kvm/arm/vgic/vgic-v3-nested.c | 178 ++++++++++++++++++++++++++++ virt/kvm/arm/vgic/vgic-v3.c | 31 +++++ virt/kvm/arm/vgic/vgic.c | 32 +++++ 13 files changed, 475 insertions(+), 9 deletions(-) create mode 100644 virt/kvm/arm/vgic/vgic-v3-nested.c
arch/arm/include/asm/kvm_emulate.h +1 -0
@@ -370,5 +370,6 @@ static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,static inline void vcpu_ptrauth_setup_lazy (struct kvm_vcpu *vcpu) {}static inline bool is_hyp_ctxt (struct kvm_vcpu *vcpu) {return false; } +static inline int kvm_inject_nested_irq (struct kvm_vcpu *vcpu) {BUG (); }#endif /* __ARM_KVM_EMULATE_H__ */
arch/arm/include/asm/kvm_host.h +4 -3
@@ -34,11 +34,12 @@#define KVM_MAX_VCPUS VGIC_V2_MAX_CPUS#endif +/* KVM_REQ_GUEST_HYP_IRQ_PENDING is actually unused */ #define KVM_REQ_SLEEP \ KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) -#define KVM_REQ_IRQ_PENDING KVM_ARCH_REQ(1) -#define KVM_REQ_VCPU_RESET KVM_ARCH_REQ(2) -#define KVM_REQ_RECORD_STEAL KVM_ARCH_REQ(3) +#define KVM_REQ_IRQ_PENDING KVM_ARCH_REQ(1) +#define KVM_REQ_VCPU_RESET KVM_ARCH_REQ(2) +#define KVM_REQ_RECORD_STEAL KVM_ARCH_REQ(3)DECLARE_STATIC_KEY_FALSE (userspace_irqchip_in_use);
arch/arm/include/asm/kvm_nested.h +1 -0
@@ -5,5 +5,6 @@#include <linux/kvm_host.h> static inline bool nested_virt_in_use (const struct kvm_vcpu *vcpu) {return false; } +static inline void check_nested_vcpu_requests (struct kvm_vcpu *vcpu) {}#endif /* __ARM_KVM_NESTED_H */
arch/arm64/include/asm/kvm_host.h +4 -3
@@ -41,9 +41,10 @@#define KVM_REQ_SLEEP \ KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) -#define KVM_REQ_IRQ_PENDING KVM_ARCH_REQ(1) -#define KVM_REQ_VCPU_RESET KVM_ARCH_REQ(2) -#define KVM_REQ_RECORD_STEAL KVM_ARCH_REQ(3) +#define KVM_REQ_IRQ_PENDING KVM_ARCH_REQ(1) +#define KVM_REQ_VCPU_RESET KVM_ARCH_REQ(2) +#define KVM_REQ_RECORD_STEAL KVM_ARCH_REQ(3) +#define KVM_REQ_GUEST_HYP_IRQ_PENDING KVM_ARCH_REQ(4)DECLARE_STATIC_KEY_FALSE (userspace_irqchip_in_use);
arch/arm64/include/asm/kvm_nested.h +1 -0
@@ -16,6 +16,7 @@ extern void kvm_init_nested_s2_mmu(struct kvm_s2_mmu *mmu);extern struct kvm_s2_mmu *lookup_s2_mmu (struct kvm *kvm,u64 vttbr,u64 hcr);extern void kvm_vcpu_load_hw_mmu (struct kvm_vcpu *vcpu);extern void kvm_vcpu_put_hw_mmu (struct kvm_vcpu *vcpu); +extern void check_nested_vcpu_requests (struct kvm_vcpu *vcpu);struct kvm_s2_trans {phys_addr_t output;
arch/arm64/kvm/Makefile +1 -0
@@ -40,3 +40,4 @@ kvm-$(CONFIG_KVM_ARM_PMU) += $(KVM)/arm/pmu.o kvm-$(CONFIG_KVM_ARM_HOST) += nested.o kvm-$(CONFIG_KVM_ARM_HOST) += emulate-nested.o +kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-v3-nested.o
arch/arm64/kvm/nested.c +16 -0
@@ -580,6 +580,22 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm)kvm_free_stage2_pgd (&kvm->arch.mmu); } +bool vgic_state_is_nested (struct kvm_vcpu *vcpu) +{ +bool imo =__vcpu_sys_reg (vcpu,HCR_EL2 ) &HCR_IMO ; +bool fmo =__vcpu_sys_reg (vcpu,HCR_EL2 ) &HCR_FMO ; + +WARN_ONCE (imo != fmo,"Separate virtual IRQ/FIQ settings not supported\n" ); + +return nested_virt_in_use (vcpu) && imo && fmo && !is_hyp_ctxt (vcpu); +} + +void check_nested_vcpu_requests (struct kvm_vcpu *vcpu) +{ +if (kvm_check_request (KVM_REQ_GUEST_HYP_IRQ_PENDING , vcpu)) +kvm_inject_nested_irq (vcpu); +} +#define FEATURE (x) (GENMASK_ULL(x##_SHIFT + 3, x##_SHIFT))/*
arch/arm64/kvm/sys_regs.c +177 -1
214 lines not on this page · raw message
include/kvm/arm_vgic.h +18 -0
@@ -302,6 +302,15 @@ struct vgic_cpu {struct vgic_irq private_irqs[VGIC_NR_PRIVATE_IRQS ]; +/* CPU vif control registers for the virtual GICH interface */ +struct vgic_v3_cpu_if nested_vgic_v3; + +/* +* The shadow vif control register loaded to the hardware when +* running a nested L2 guest with the virtual IMO/FMO bit set. +*/ +struct vgic_v3_cpu_if shadow_vgic_v3; +raw_spinlock_t ap_list_lock;/* Protects the ap_list */ /*
@@ -358,6 +367,13 @@ void kvm_vgic_load(struct kvm_vcpu *vcpu);void kvm_vgic_put (struct kvm_vcpu *vcpu);void kvm_vgic_vmcr_sync (struct kvm_vcpu *vcpu); +void vgic_v3_load_nested (struct kvm_vcpu *vcpu); +void vgic_v3_put_nested (struct kvm_vcpu *vcpu); +void vgic_v3_handle_nested_maint_irq (struct kvm_vcpu *vcpu); +u16 vgic_v3_get_eisr (struct kvm_vcpu *vcpu); +u16 vgic_v3_get_elrsr (struct kvm_vcpu *vcpu); +u64 vgic_v3_get_misr (struct kvm_vcpu *vcpu); +#define irqchip_in_kernel (k) (!!((k)->arch.vgic.in_kernel))#define vgic_initialized (k) ((k)->arch.vgic.initialized)#define vgic_ready (k) ((k)->arch.vgic.ready)
@@ -401,4 +417,6 @@ int kvm_vgic_v4_unset_forwarding(struct kvm *kvm, int irq,int vgic_v4_load (struct kvm_vcpu *vcpu);int vgic_v4_put (struct kvm_vcpu *vcpu,bool need_db); +bool vgic_state_is_nested (struct kvm_vcpu *vcpu); +#endif /* __KVM_ARM_VGIC_H */
virt/kvm/arm/arm.c +11 -2
@@ -620,6 +620,8 @@ static void check_vcpu_requests(struct kvm_vcpu *vcpu)if (kvm_check_request (KVM_REQ_RECORD_STEAL , vcpu))kvm_update_stolen_time (vcpu); + +check_nested_vcpu_requests (vcpu); } }
@@ -666,9 +668,16 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)*/ cond_resched (); -update_vmid (&vcpu->arch.hw_mmu->vmid); - +/* +* A nested exeption triggered by a vcpu request (such +* as an interrupt injected in a guest hypervisor) can +* change the currently used VMID (by switching to a +* different translation regime. It is thus necesary +* to update the VMID *after* all requests have been +* processed. +*/ check_vcpu_requests (vcpu); +update_vmid (&vcpu->arch.hw_mmu->vmid);/* * Preparing the interrupts to be injected also
virt/kvm/arm/vgic/vgic-v3-nested.c new file +178 -0
178 lines not on this page · raw message
virt/kvm/arm/vgic/vgic-v3.c +31 -0
@@ -6,6 +6,7 @@#include <kvm/arm_vgic.h> #include <asm/kvm_hyp.h> #include <asm/kvm_mmu.h> +#include <asm/kvm_nested.h> #include <asm/kvm_asm.h> #include "vgic.h"
@@ -289,6 +290,12 @@ void vgic_v3_enable(struct kvm_vcpu *vcpu) vgic_v3->vgic_sre = (ICC_SRE_EL1_DIB |ICC_SRE_EL1_DFB |ICC_SRE_EL1_SRE ); +/* +* If nesting is allowed, force GICv3 onto the nested +* guests as well. +*/ +if (nested_virt_in_use (vcpu)) + vcpu->arch.vgic_cpu.nested_vgic_v3.vgic_sre = vgic_v3->vgic_sre; vcpu->arch.vgic_cpu.pendbaser =INITIAL_PENDBASER_VALUE ; }else { vgic_v3->vgic_sre =0 ;
@@ -652,6 +659,13 @@ void vgic_v3_load(struct kvm_vcpu *vcpu) {struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; +/* +* vgic_v3_load_nested only affects the LRs in the shadow +* state, so it is fine to pass the nested state around. +*/ +if (vgic_state_is_nested (vcpu)) + cpu_if = &vcpu->arch.vgic_cpu.nested_vgic_v3; +/* * If dealing with a GICv2 emulation on GICv3, VMCR_EL2.VFIQen * is dependent on ICC_SRE_EL1.SRE, and we have to perform the
@@ -665,6 +679,9 @@ void vgic_v3_load(struct kvm_vcpu *vcpu)if (has_vhe ())__vgic_v3_activate_traps (cpu_if); +if (vgic_state_is_nested (vcpu)) +vgic_v3_load_nested (vcpu); +WARN_ON (vgic_v4_load (vcpu)); }
@@ -672,6 +689,9 @@ void vgic_v3_vmcr_sync(struct kvm_vcpu *vcpu) {struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; +if (vgic_state_is_nested (vcpu)) + cpu_if = &vcpu->arch.vgic_cpu.shadow_vgic_v3; +if (likely (cpu_if->vgic_sre)) cpu_if->vgic_vmcr =kvm_call_hyp_ret (__vgic_v3_read_vmcr); }
@@ -684,8 +704,19 @@ void vgic_v3_put(struct kvm_vcpu *vcpu)vgic_v3_vmcr_sync (vcpu); +if (vgic_state_is_nested (vcpu)) + cpu_if = &vcpu->arch.vgic_cpu.shadow_vgic_v3; +kvm_call_hyp (__vgic_v3_save_aprs,kern_hyp_va (cpu_if));if (has_vhe ())__vgic_v3_deactivate_traps (cpu_if); + +if (vgic_state_is_nested (vcpu)) +vgic_v3_put_nested (vcpu); } + +__weak void vgic_v3_sync_nested (struct kvm_vcpu *vcpu) {} +__weak void vgic_v3_handle_nested_maint_irq (struct kvm_vcpu *vcpu) {} +__weak void vgic_v3_load_nested (struct kvm_vcpu *vcpu) {} +__weak void vgic_v3_put_nested (struct kvm_vcpu *vcpu) {}
virt/kvm/arm/vgic/vgic.c +32 -0
@@ -876,6 +876,10 @@ void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) {int used_lrs; +/* If nesting, this is a load/put affair, not flush/sync. */ +if (vgic_state_is_nested (vcpu)) +return ; +/* An empty ap_list_head implies used_lrs == 0 */ if (list_empty (&vcpu->arch.vgic_cpu.ap_list_head))return ;
@@ -920,6 +924,29 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) !vgic_supports_direct_msis(vcpu->kvm))return ; +/* +* If in a nested state, we must return early. Two possibilities: +* +* - If we have any pending IRQ for the guest and the guest +* expects IRQs to be handled in its virtual EL2 mode (the +* virtual IMO bit is set) and it is not already running in +* virtual EL2 mode, then we have to emulate an IRQ +* exception to virtual EL2. +* +* We do that by placing a request to ourselves which will +* abort the entry procedure and inject the exception at the +* beginning of the run loop. +* +* - Otherwise, do exactly *NOTHING*. The guest state is +* already loaded, and we can carry on with running it. +*/ +if (vgic_state_is_nested (vcpu)) { +if (kvm_vgic_vcpu_pending_irq (vcpu)) +kvm_make_request (KVM_REQ_GUEST_HYP_IRQ_PENDING , vcpu); + +return ; + } +DEBUG_SPINLOCK_BUG_ON (!irqs_disabled ());if (!list_empty (&vcpu->arch.vgic_cpu.ap_list_head)) {
@@ -1033,3 +1060,8 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int vintid)return map_is_active; } + +__weak bool vgic_state_is_nested (struct kvm_vcpu *vcpu) +{ +return false; +}
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 53/94 KVM: arm64: nv: vgic: Emulate the HW bit in software
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
Should the guest hypervisor use the HW bit in the LRs, we need to emulate the deactivation from the L2 guest into the L1 distributor emulation, which is handled by L0.
It's all good fun.
--- arch/arm64/include/asm/kvm_hyp.h | 2 ++ include/kvm/arm_vgic.h | 1 + virt/kvm/arm/hyp/vgic-v3-sr.c | 2 +- virt/kvm/arm/vgic/vgic-v3-nested.c | 32 ++++++++++++++++++++++++++++++ virt/kvm/arm/vgic/vgic.c | 6 ++++-- 5 files changed, 40 insertions(+), 3 deletions(-)
arch/arm64/include/asm/kvm_hyp.h +2 -0
@@ -48,6 +48,8 @@int __vgic_v2_perform_cpuif_access (struct kvm_vcpu *vcpu); +u64 __hyp_text __gic_v3_get_lr(unsigned int lr); +void __vgic_v3_save_state (struct vgic_v3_cpu_if *cpu_if);void __vgic_v3_restore_state (struct vgic_v3_cpu_if *cpu_if);void __vgic_v3_activate_traps (struct vgic_v3_cpu_if *cpu_if);
include/kvm/arm_vgic.h +1 -0
@@ -367,6 +367,7 @@ void kvm_vgic_load(struct kvm_vcpu *vcpu);void kvm_vgic_put (struct kvm_vcpu *vcpu);void kvm_vgic_vmcr_sync (struct kvm_vcpu *vcpu); +void vgic_v3_sync_nested (struct kvm_vcpu *vcpu);void vgic_v3_load_nested (struct kvm_vcpu *vcpu);void vgic_v3_put_nested (struct kvm_vcpu *vcpu);void vgic_v3_handle_nested_maint_irq (struct kvm_vcpu *vcpu);
virt/kvm/arm/hyp/vgic-v3-sr.c +1 -1
@@ -16,7 +16,7 @@#define vtr_to_nr_pre_bits (v) ((((u32)(v) >> 26) & 7) + 1)#define vtr_to_nr_apr_regs (v) (1 << (vtr_to_nr_pre_bits(v) - 5)) -static u64 __hyp_text __gic_v3_get_lr(unsigned int lr) +u64 __hyp_text __gic_v3_get_lr(unsigned int lr) {switch (lr &0xf ) {case 0 :
virt/kvm/arm/vgic/vgic-v3-nested.c +32 -0
@@ -138,6 +138,38 @@ static void vgic_v3_fixup_shadow_lr_state(struct kvm_vcpu *vcpu) } } +void vgic_v3_sync_nested (struct kvm_vcpu *vcpu) +{ +struct vgic_v3_cpu_if *cpu_if =vcpu_nested_if (vcpu); +struct vgic_v3_cpu_if *s_cpu_if =vcpu_shadow_if (vcpu); +struct vgic_irq *irq; +int i; + +for (i =0 ; i < s_cpu_if->used_lrs; i++) { +u64 lr = cpu_if->vgic_lr[i]; +int l1_irq; + +if (!(lr &ICH_LR_HW ) || !(lr &ICH_LR_STATE )) +continue ; + +/* +* If we had a HW lr programmed by the guest hypervisor, we +* need to emulate the HW effect between the guest hypervisor +* and the nested guest. +*/ + l1_irq = (lr &ICH_LR_PHYS_ID_MASK ) >>ICH_LR_PHYS_ID_SHIFT ; + irq =vgic_get_irq (vcpu->kvm, vcpu, l1_irq); +if (!irq) +continue ;/* oh well, the guest hyp is broken */ + + lr =__gic_v3_get_lr (i); +if (!(lr &ICH_LR_STATE )) + irq->active = false; + +vgic_put_irq (vcpu->kvm, irq); + } +} +void vgic_v3_load_nested (struct kvm_vcpu *vcpu) {struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
virt/kvm/arm/vgic/vgic.c +4 -2
@@ -876,9 +876,11 @@ void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) {int used_lrs; -/* If nesting, this is a load/put affair, not flush/sync. */ -if (vgic_state_is_nested (vcpu)) +/* If nesting, emulate the HW effect from L0 to L1 */ +if (vgic_state_is_nested (vcpu)) { +vgic_v3_sync_nested (vcpu);return ; + }/* An empty ap_list_head implies used_lrs == 0 */ if (list_empty (&vcpu->arch.vgic_cpu.ap_list_head))
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 54/94 KVM: arm64: nv: vgic: Allow userland to set VGIC maintenance IRQ
In reply to Marc ZyngierFrom: Andre Przywara <[email protected]>
The VGIC maintenance IRQ signals various conditions about the LRs, when the GIC's virtualization extension is used.
So far we didn't need it, but nested virtualization needs to know about this interrupt, so add a userland interface to setup the IRQ number. The architecture mandates that it must be a PPI, on top of that this code only exports a per-device option, so the PPI is the same on all VCPUs.
--- .../virt/kvm/devices/arm-vgic-v3.txt | 8 +++++++ arch/arm/include/uapi/asm/kvm.h | 1 + arch/arm64/include/uapi/asm/kvm.h | 1 + include/kvm/arm_vgic.h | 3 +++ virt/kvm/arm/vgic/vgic-kvm-device.c | 22 +++++++++++++++++++ 5 files changed, 35 insertions(+)
Documentation/virt/kvm/devices/arm-vgic-v3.txt +8 -0
@@ -249,3 +249,11 @@ Groups: Errors: -EINVAL: vINTID is not multiple of 32 or info field is not VGIC_LEVEL_INFO_LINE_LEVEL + + KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ + The attr field of kvm_device_attr encodes the following values: + bits: | 31 .... 5 | 4 .... 0 | + values: | RES0 | vINTID | + + The vINTID specifies which interrupt is generated when the vGIC + must generate a maintenance interrupt. This must be a PPI.
arch/arm/include/uapi/asm/kvm.h +1 -0
@@ -246,6 +246,7 @@ struct kvm_vcpu_events {#define KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS 6#define KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO 7#define KVM_DEV_ARM_VGIC_GRP_ITS_REGS 8 +#define KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ 9#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT 10#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK \ (0x3fffffULL << KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT)
arch/arm64/include/uapi/asm/kvm.h +1 -0
@@ -314,6 +314,7 @@ struct kvm_vcpu_events {#define KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS 6#define KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO 7#define KVM_DEV_ARM_VGIC_GRP_ITS_REGS 8 +#define KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ 9#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT 10#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK \ (0x3fffffULL << KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT)
include/kvm/arm_vgic.h +3 -0
@@ -218,6 +218,9 @@ struct vgic_dist {int nr_spis; +/* The GIC maintenance IRQ for nested hypervisors. */ +u32 maint_irq; +/* base addresses in guest physical address space: */ gpa_t vgic_dist_base;/* distributor */ union {
virt/kvm/arm/vgic/vgic-kvm-device.c +22 -0
@@ -241,6 +241,12 @@ static int vgic_get_common_attr(struct kvm_device *dev, VGIC_NR_PRIVATE_IRQS, uaddr);break ; } +case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ : { +u32 __user *uaddr = (u32 __user *)(long )attr->addr; + + r =put_user (dev->kvm->arch.vgic.maint_irq, uaddr); +break ; + } }return r;
@@ -627,6 +633,21 @@ static int vgic_v3_set_attr(struct kvm_device *dev, reg = tmp32;return vgic_v3_attr_regs_access (dev, attr, ®, true); } +case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ : { +u32 __user *uaddr = (u32 __user *)(long )attr->addr; +u32 val; + +if (get_user (val, uaddr)) +return -EFAULT ; + +/* Must be a PPI. */ +if ((val >=VGIC_NR_PRIVATE_IRQS ) || (val <VGIC_NR_SGIS )) +return -EINVAL ; + + dev->kvm->arch.vgic.maint_irq = val; + +return 0 ; + }case KVM_DEV_ARM_VGIC_GRP_CTRL : {int ret;
@@ -712,6 +733,7 @@ static int vgic_v3_has_attr(struct kvm_device *dev,case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS :return vgic_v3_has_attr_regs (dev, attr);case KVM_DEV_ARM_VGIC_GRP_NR_IRQS : +case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ :return 0 ;case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO : { if (((attr->attr & KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK) >>
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 55/94 KVM: arm64: nv: Implement maintenance interrupt forwarding
In reply to Marc ZyngierWhen we take a maintenance interrupt, we need to decide whether it is generated on an action from the guest, or if it is something that needs to be forwarded to the guest hypervisor.
--- virt/kvm/arm/vgic/vgic-init.c | 30 ++++++++++++++++++++++++++++++ virt/kvm/arm/vgic/vgic-v3-nested.c | 25 +++++++++++++++++++++---- 2 files changed, 51 insertions(+), 4 deletions(-)
virt/kvm/arm/vgic/vgic-init.c +30 -0
@@ -6,10 +6,12 @@#include <linux/uaccess.h> #include <linux/interrupt.h> #include <linux/cpu.h> +#include <linux/irq.h> #include <linux/kvm_host.h> #include <kvm/arm_vgic.h> #include <asm/kvm_emulate.h> #include <asm/kvm_mmu.h> +#include <asm/kvm_nested.h> #include "vgic.h" /*
@@ -221,6 +223,16 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)if (!irqchip_in_kernel (vcpu->kvm))return 0 ; +if (nested_virt_in_use (vcpu)) { +/* FIXME: remove this hack */ +if (vcpu->kvm->arch.vgic.maint_irq ==0 ) + vcpu->kvm->arch.vgic.maint_irq = kvm_vgic_global_state.maint_irq; + ret =kvm_vgic_set_owner (vcpu, vcpu->kvm->arch.vgic.maint_irq, + vcpu); +if (ret) +return ret; + } +/* * If we are creating a VCPU with a GICv3 we must also register the * KVM io device for the redistributor that belongs to this VCPU.
@@ -449,12 +461,23 @@ static int vgic_init_cpu_dying(unsigned int cpu)static irqreturn_t vgic_maintenance_handler (int irq,void *data) { +struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)data; +/* * We cannot rely on the vgic maintenance interrupt to be * delivered synchronously. This means we can only use it to * exit the VM, and we perform the handling of EOIed * interrupts on the exit path (see vgic_fold_lr_state). */ + +/* If not nested, deactivate */ +if (!vcpu || !vgic_state_is_nested (vcpu)) { +irq_set_irqchip_state (irq,IRQCHIP_STATE_ACTIVE , false); +return IRQ_HANDLED ; + } + +/* Assume nested from now */ +vgic_v3_handle_nested_maint_irq (vcpu);return IRQ_HANDLED ; }
@@ -525,6 +548,13 @@ int kvm_vgic_hyp_init(void)return ret; } + ret =irq_set_vcpu_affinity (kvm_vgic_global_state.maint_irq, +kvm_get_running_vcpus ()); +if (ret) { +kvm_err ("Error setting vcpu affinity\n" ); + goto out_free_irq; + } + ret =cpuhp_setup_state (CPUHP_AP_KVM_ARM_VGIC_INIT_STARTING ,"kvm/arm/vgic:starting" , vgic_init_cpu_starting, vgic_init_cpu_dying);
virt/kvm/arm/vgic/vgic-v3-nested.c +21 -4
@@ -173,10 +173,20 @@ void vgic_v3_sync_nested(struct kvm_vcpu *vcpu)void vgic_v3_load_nested (struct kvm_vcpu *vcpu) {struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; +struct vgic_irq *irq; +unsigned long flags; vgic_cpu->shadow_vgic_v3 = vgic_cpu->nested_vgic_v3;vgic_v3_create_shadow_lr (vcpu);__vgic_v3_restore_state (vcpu_shadow_if (vcpu)); + + irq =vgic_get_irq (vcpu->kvm, vcpu, vcpu->kvm->arch.vgic.maint_irq); +raw_spin_lock_irqsave (&irq->irq_lock, flags); +if (irq->line_level || irq->active) +irq_set_irqchip_state (kvm_vgic_global_state.maint_irq, +IRQCHIP_STATE_ACTIVE , true); +raw_spin_unlock_irqrestore (&irq->irq_lock, flags); +vgic_put_irq (vcpu->kvm, irq); }void vgic_v3_put_nested (struct kvm_vcpu *vcpu)
@@ -191,11 +201,14 @@ void vgic_v3_put_nested(struct kvm_vcpu *vcpu)*/ vgic_v3_fixup_shadow_lr_state (vcpu); vgic_cpu->nested_vgic_v3 = vgic_cpu->shadow_vgic_v3; +irq_set_irqchip_state (kvm_vgic_global_state.maint_irq, +IRQCHIP_STATE_ACTIVE , false); }void vgic_v3_handle_nested_maint_irq (struct kvm_vcpu *vcpu) {struct vgic_v3_cpu_if *cpu_if =vcpu_nested_if (vcpu); +bool state;/* * If we exit a nested VM with a pending maintenance interrupt from the
@@ -203,8 +216,12 @@ void vgic_v3_handle_nested_maint_irq(struct kvm_vcpu *vcpu)* can re-sync the appropriate LRs and sample level triggered interrupts * again. */ -if (vgic_state_is_nested (vcpu) && - (cpu_if->vgic_hcr &ICH_HCR_EN ) && -vgic_v3_get_misr (vcpu)) -kvm_inject_nested_irq (vcpu); +if (!vgic_state_is_nested (vcpu)) +return ; + + state = cpu_if->vgic_hcr &ICH_HCR_EN ; + state &=vgic_v3_get_misr (vcpu); + +kvm_vgic_inject_irq (vcpu->kvm, vcpu->vcpu_id, + vcpu->kvm->arch.vgic.maint_irq, state, vcpu); }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 56/94 KVM: arm64: nv: Add nested GICv3 tracepoints
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
Adding tracepoints to be able to peek into the shadow LRs used when running a guest guest.
--- virt/kvm/arm/vgic/vgic-nested-trace.h | 137 ++++++++++++++++++++++++++ virt/kvm/arm/vgic/vgic-v3-nested.c | 13 ++- 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 virt/kvm/arm/vgic/vgic-nested-trace.h
virt/kvm/arm/vgic/vgic-nested-trace.h new file +137 -0
@@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#if !defined(_TRACE_VGIC_NESTED_H) || defined(TRACE_HEADER_MULTI_READ ) +#define _TRACE_VGIC_NESTED_H + +#include <linux/tracepoint.h> + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM kvm + +#define SLR_ENTRY_VALS (x) \ + " ", \ + !!(__entry->lrs[x] & ICH_LR_HW), \ + !!(__entry->lrs[x] & ICH_LR_PENDING_BIT), \ + !!(__entry->lrs[x] & ICH_LR_ACTIVE_BIT), \ + __entry->lrs[x] & ICH_LR_VIRTUAL_ID_MASK, \ + (__entry->lrs[x] & ICH_LR_PHYS_ID_MASK) >> ICH_LR_PHYS_ID_SHIFT, \ + (__entry->orig_lrs[x] & ICH_LR_PHYS_ID_MASK) >> ICH_LR_PHYS_ID_SHIFT + +TRACE_EVENT (vgic_create_shadow_lrs, + TP_PROTO(struct kvm_vcpu *vcpu, int nr_lr, u64 *lrs, u64 *orig_lrs), +TP_ARGS (vcpu, nr_lr, lrs, orig_lrs), + +TP_STRUCT__entry ( + __field( int, nr_lr ) + __array( u64, lrs, 16 ) +__array ( u64, orig_lrs,16 ) + ), + +TP_fast_assign ( + __entry->nr_lr = nr_lr; + memcpy(__entry->lrs, lrs, 16 * sizeof(u64)); + memcpy(__entry->orig_lrs, orig_lrs, 16 * sizeof(u64)); + ), + +TP_printk ("nr_lr: %d\n" +"%50sLR[ 0]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[ 1]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[ 2]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[ 3]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[ 4]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[ 5]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[ 6]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[ 7]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[ 8]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[ 9]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[10]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[11]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[12]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[13]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[14]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)\n" +"%50sLR[15]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu (%5llu)" , + __entry->nr_lr, +SLR_ENTRY_VALS (0 ),SLR_ENTRY_VALS (1 ),SLR_ENTRY_VALS (2 ), +SLR_ENTRY_VALS (3 ),SLR_ENTRY_VALS (4 ),SLR_ENTRY_VALS (5 ), +SLR_ENTRY_VALS (6 ),SLR_ENTRY_VALS (7 ),SLR_ENTRY_VALS (8 ), +SLR_ENTRY_VALS (9 ),SLR_ENTRY_VALS (10 ),SLR_ENTRY_VALS (11 ), +SLR_ENTRY_VALS (12 ),SLR_ENTRY_VALS (13 ),SLR_ENTRY_VALS (14 ), +SLR_ENTRY_VALS (15 )) +); + +#define LR_ENTRY_VALS (x) \ + " ", \ + !!(__entry->lrs[x] & ICH_LR_HW), \ + !!(__entry->lrs[x] & ICH_LR_PENDING_BIT), \ + !!(__entry->lrs[x] & ICH_LR_ACTIVE_BIT), \ + __entry->lrs[x] & ICH_LR_VIRTUAL_ID_MASK, \ + (__entry->lrs[x] & ICH_LR_PHYS_ID_MASK) >> ICH_LR_PHYS_ID_SHIFT + +TRACE_EVENT (vgic_put_nested, + TP_PROTO(struct kvm_vcpu *vcpu, int nr_lr, u64 *lrs), +TP_ARGS (vcpu, nr_lr, lrs), + +TP_STRUCT__entry ( + __field( int, nr_lr ) +__array ( u64, lrs,16 ) + ), + +TP_fast_assign ( + __entry->nr_lr = nr_lr; + memcpy(__entry->lrs, lrs, 16 * sizeof(u64)); + ), + +TP_printk ("nr_lr: %d\n" +"%50sLR[ 0]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[ 1]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[ 2]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[ 3]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[ 4]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[ 5]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[ 6]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[ 7]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[ 8]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[ 9]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[10]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[11]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[12]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[13]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[14]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu\n" +"%50sLR[15]: HW: %d P: %d: A: %d vINTID: %5llu pINTID: %5llu" , + __entry->nr_lr, +LR_ENTRY_VALS (0 ),LR_ENTRY_VALS (1 ),LR_ENTRY_VALS (2 ), +LR_ENTRY_VALS (3 ),LR_ENTRY_VALS (4 ),LR_ENTRY_VALS (5 ), +LR_ENTRY_VALS (6 ),LR_ENTRY_VALS (7 ),LR_ENTRY_VALS (8 ), +LR_ENTRY_VALS (9 ),LR_ENTRY_VALS (10 ),LR_ENTRY_VALS (11 ), +LR_ENTRY_VALS (12 ),LR_ENTRY_VALS (13 ),LR_ENTRY_VALS (14 ), +LR_ENTRY_VALS (15 )) +); + +TRACE_EVENT (vgic_nested_hw_emulate, + TP_PROTO(int lr, u64 lr_val, u32 l1_intid), +TP_ARGS (lr, lr_val, l1_intid), + +TP_STRUCT__entry ( + __field( int, lr ) + __field( u64, lr_val ) +__field ( u32, l1_intid ) + ), + +TP_fast_assign ( + __entry->lr = lr; + __entry->lr_val = lr_val; + __entry->l1_intid = l1_intid; + ), + +TP_printk ("lr: %d LR %llx L1 INTID: %u\n" , + __entry->lr, __entry->lr_val, __entry->l1_intid) +); + +#endif /* _TRACE_VGIC_NESTED_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../../virt/kvm/arm/vgic +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE vgic-nested-trace + +/* This part must be outside protection */ +#include <trace/define_trace.h>
virt/kvm/arm/vgic/vgic-v3-nested.c +12 -1
@@ -13,6 +13,9 @@#include "vgic.h" +#define CREATE_TRACE_POINTS +#include "vgic-nested-trace.h" +static inline struct vgic_v3_cpu_if *vcpu_nested_if (struct kvm_vcpu *vcpu) {return &vcpu->arch.vgic_cpu.nested_vgic_v3;
@@ -119,6 +122,9 @@ static void vgic_v3_create_shadow_lr(struct kvm_vcpu *vcpu) used_lrs = i +1 ; } +trace_vgic_create_shadow_lrs (vcpu, kvm_vgic_global_state.nr_lr, + s_cpu_if->vgic_lr, cpu_if->vgic_lr); + s_cpu_if->used_lrs = used_lrs; }
@@ -163,8 +169,10 @@ void vgic_v3_sync_nested(struct kvm_vcpu *vcpu)continue ;/* oh well, the guest hyp is broken */ lr =__gic_v3_get_lr (i); -if (!(lr &ICH_LR_STATE )) +if (!(lr &ICH_LR_STATE )) { +trace_vgic_nested_hw_emulate (i, lr, l1_irq); irq->active = false; + }vgic_put_irq (vcpu->kvm, irq); }
@@ -195,6 +203,9 @@ void vgic_v3_put_nested(struct kvm_vcpu *vcpu)__vgic_v3_save_state (vcpu_shadow_if (vcpu)); +trace_vgic_put_nested (vcpu, kvm_vgic_global_state.nr_lr, +vcpu_shadow_if (vcpu)->vgic_lr); +/* * Translate the shadow state HW fields back to the virtual ones * before copying the shadow struct back to the nested one.
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 57/94 arm64: KVM: nv: Add handling of EL2-specific timer registers
In reply to Marc ZyngierAdd the required handling for EL2 and EL02 registers, as well as EL1 registers used in the E2H context.
--- arch/arm64/kvm/sys_regs.c | 72 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+)
arch/arm64/kvm/sys_regs.c +72 -0
@@ -1384,20 +1384,92 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,switch (reg) {case SYS_CNTP_TVAL_EL0 : +if (vcpu_mode_el2 (vcpu) &&vcpu_el2_e2h_is_set (vcpu)) + tmr =TIMER_HPTIMER ; +else + tmr =TIMER_PTIMER ; + treg =TIMER_REG_TVAL ; +break ; +case SYS_AARCH32_CNTP_TVAL : +case SYS_CNTP_TVAL_EL02 : tmr =TIMER_PTIMER ; treg =TIMER_REG_TVAL ;break ; + +case SYS_CNTV_TVAL_EL02 : + tmr =TIMER_VTIMER ; + treg =TIMER_REG_TVAL ; +break ; + +case SYS_CNTHP_TVAL_EL2 : + tmr =TIMER_HPTIMER ; + treg =TIMER_REG_TVAL ; +break ; + +case SYS_CNTHV_TVAL_EL2 : + tmr =TIMER_HVTIMER ; + treg =TIMER_REG_TVAL ; +break ; +case SYS_CNTP_CTL_EL0 : +if (vcpu_mode_el2 (vcpu) &&vcpu_el2_e2h_is_set (vcpu)) + tmr =TIMER_HPTIMER ; +else + tmr =TIMER_PTIMER ; + treg =TIMER_REG_CTL ; +break ; +case SYS_AARCH32_CNTP_CTL : +case SYS_CNTP_CTL_EL02 : tmr =TIMER_PTIMER ; treg =TIMER_REG_CTL ;break ; + +case SYS_CNTV_CTL_EL02 : + tmr =TIMER_VTIMER ; + treg =TIMER_REG_CTL ; +break ; + +case SYS_CNTHP_CTL_EL2 : + tmr =TIMER_HPTIMER ; + treg =TIMER_REG_CTL ; +break ; + +case SYS_CNTHV_CTL_EL2 : + tmr =TIMER_HVTIMER ; + treg =TIMER_REG_CTL ; +break ; +case SYS_CNTP_CVAL_EL0 : +if (vcpu_mode_el2 (vcpu) &&vcpu_el2_e2h_is_set (vcpu)) + tmr =TIMER_HPTIMER ; +else + tmr =TIMER_PTIMER ; + treg =TIMER_REG_CVAL ; +break ; +case SYS_AARCH32_CNTP_CVAL : +case SYS_CNTP_CVAL_EL02 : tmr =TIMER_PTIMER ; treg =TIMER_REG_CVAL ;break ; + +case SYS_CNTV_CVAL_EL02 : + tmr =TIMER_VTIMER ; + treg =TIMER_REG_CVAL ; +break ; + +case SYS_CNTHP_CVAL_EL2 : + tmr =TIMER_HPTIMER ; + treg =TIMER_REG_CVAL ; +break ; + +case SYS_CNTHV_CVAL_EL2 : + tmr =TIMER_HVTIMER ; + treg =TIMER_REG_CVAL ; +break ; +case SYS_CNTVOFF_EL2 : tmr =TIMER_VTIMER ; treg =TIMER_REG_VOFF ;
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 58/94 arm64: KVM: nv: Honor SCTLR_EL2.SPAN on entering vEL2
In reply to Marc ZyngierOn entering vEL2, we must honor the SCTLR_EL2.SPAN bit so that PSTATE.PAN reflect the expected setting.
--- arch/arm64/kvm/emulate-nested.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
arch/arm64/kvm/emulate-nested.c +12 -1
@@ -131,9 +131,11 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu)static void enter_el2_exception (struct kvm_vcpu *vcpu,u64 esr_el2,enum exception_type type) { +u64 spsr = *vcpu_cpsr (vcpu); +trace_kvm_inject_nested_exception (vcpu, esr_el2, type); -vcpu_write_sys_reg (vcpu, *vcpu_cpsr (vcpu),SPSR_EL2 ); +vcpu_write_sys_reg (vcpu, spsr,SPSR_EL2 );vcpu_write_sys_reg (vcpu, *vcpu_pc (vcpu),ELR_EL2 );vcpu_write_sys_reg (vcpu, esr_el2,ESR_EL2 );
@@ -141,6 +143,15 @@ static void enter_el2_exception(struct kvm_vcpu *vcpu, u64 esr_el2,/* On an exception, PSTATE.SP becomes 1 */ *vcpu_cpsr (vcpu) = PSR_MODE_EL2h; *vcpu_cpsr (vcpu) |=PSR_A_BIT |PSR_F_BIT |PSR_I_BIT |PSR_D_BIT ; + +/* +* If SPAN is clear, set the PAN bit on exception entry +* if SPAN is set, copy the PAN bit across +*/ +if (!(vcpu_read_sys_reg (vcpu,SCTLR_EL2 ) &SCTLR_EL1_SPAN )) + *vcpu_cpsr (vcpu) |=PSR_PAN_BIT ; +else + *vcpu_cpsr (vcpu) |= (spsr &PSR_PAN_BIT ); }/*
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 59/94 arm64: KVM: nv: Handle SCTLR_EL2 RES0/RES1 bits
In reply to Marc ZyngierDepending on the HCR_EL2.{E2H,TGE} values, SCTLR_EL2 has different RES0/RES1 constraints.
Let's handle that.
--- arch/arm64/kvm/sys_regs.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-)
arch/arm64/kvm/sys_regs.c +32 -1
@@ -443,6 +443,37 @@ static bool access_vbar_el1(struct kvm_vcpu *vcpu,return access_rw (vcpu, p, r); } +static bool access_sctlr_el2 (struct kvm_vcpu *vcpu, +struct sys_reg_params *p, +const struct sys_reg_desc *r) +{ +if (el12_reg (p) &&forward_nv_traps (vcpu)) +return false; + +if (p->is_write) { +u64 val = p->regval; + +if (vcpu_el2_e2h_is_set (vcpu) &&vcpu_el2_tge_is_set (vcpu)) { + val &= ~(GENMASK_ULL (63 ,45 ) |GENMASK_ULL (34 ,32 ) | +BIT_ULL (17 )); + val |=SCTLR_EL1_RES1 ; + }else { + val &= ~(GENMASK_ULL (63 ,45 ) |BIT_ULL (42 ) | +GENMASK_ULL (39 ,38 ) |GENMASK_ULL (35 ,32 ) | +BIT_ULL (26 ) |BIT_ULL (24 ) |BIT_ULL (20 ) | +BIT_ULL (17 ) |GENMASK_ULL (15 ,14 ) | +GENMASK (10 ,7 )); + val |=SCTLR_EL2_RES1 ; + } + +vcpu_write_sys_reg (vcpu, val, 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). */
@@ -2254,7 +2285,7 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_VPIDR_EL2), access_rw, reset_vpidr, VPIDR_EL2 }, { SYS_DESC(SYS_VMPIDR_EL2), access_rw, reset_vmpidr, VMPIDR_EL2 }, - { SYS_DESC(SYS_SCTLR_EL2), access_rw, reset_val, SCTLR_EL2, 0 }, + { SYS_DESC(SYS_SCTLR_EL2), access_sctlr_el2, reset_val, SCTLR_EL2, SCTLR_EL2_RES1 }, { SYS_DESC(SYS_ACTLR_EL2), access_rw, reset_val, ACTLR_EL2, 0 }, { SYS_DESC(SYS_HCR_EL2), access_rw, reset_val, HCR_EL2, 0 }, { SYS_DESC(SYS_MDCR_EL2), access_rw, reset_val, MDCR_EL2, 0 },
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 60/94 arm64: KVM: nv: Restrict S2 RD/WR permissions to match the guest's
In reply to Marc ZyngierWhen mapping a page in a shadow stage-2, special care must be taken not to be more permissive than the guest is (writable or readable page when the guest hasn't set that permission).
--- arch/arm/include/asm/kvm_mmu.h | 18 ++++++++++++++++++ arch/arm64/include/asm/kvm_mmu.h | 18 ++++++++++++++++++ arch/arm64/include/asm/kvm_nested.h | 10 ++++++++++ virt/kvm/arm/mmu.c | 21 ++++++++++++++++++++- 4 files changed, 66 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_mmu.h +18 -0
@@ -113,6 +113,12 @@ static inline pud_t kvm_s2pud_mkexec(pud_t pud)return pud; } +static inline pud_t kvm_s2pud_revoke_read (pud_t pud) +{ +WARN_ON (1 ); +return pud; +} +static inline bool kvm_s2pud_exec (pud_t *pud) {WARN_ON (1 );
@@ -155,6 +161,18 @@ static inline pmd_t kvm_s2pmd_mkexec(pmd_t pmd)return pmd; } +static inline pte_t kvm_s2pte_revoke_read (pte_t pte) +{ +pte_val (pte) &= ~L_PTE_S2_RDONLY ; +return pte; +} + +static inline pmd_t kvm_s2pmd_revoke_read (pmd_t pmd) +{ +pmd_val (pmd) &= ~L_PMD_S2_RDONLY ; +return pmd; +} +static inline void kvm_set_s2pte_readonly (pte_t *pte) {pte_val (*pte) = (pte_val (*pte) & ~L_PTE_S2_RDWR ) |L_PTE_S2_RDONLY ;
arch/arm64/include/asm/kvm_mmu.h +18 -0
@@ -228,6 +228,24 @@ static inline pud_t kvm_s2pud_mkexec(pud_t pud)return pud; } +static inline pte_t kvm_s2pte_revoke_read (pte_t pte) +{ +pte_val (pte) &= ~PTE_S2_RDONLY ; +return pte; +} + +static inline pmd_t kvm_s2pmd_revoke_read (pmd_t pmd) +{ +pmd_val (pmd) &= ~PMD_S2_RDONLY ; +return pmd; +} + +static inline pud_t kvm_s2pud_revoke_read (pud_t pud) +{ +pud_val (pud) &= ~PUD_S2_RDONLY ; +return pud; +} +static inline void kvm_set_s2pte_readonly (pte_t *ptep) {pteval_t old_pteval, pteval;
arch/arm64/include/asm/kvm_nested.h +10 -0
@@ -43,6 +43,16 @@ static inline u32 kvm_s2_trans_esr(struct kvm_s2_trans *trans)return trans->esr; } +static inline bool kvm_s2_trans_readable (struct kvm_s2_trans *trans) +{ +return trans->readable; +} + +static inline bool kvm_s2_trans_writable (struct kvm_s2_trans *trans) +{ +return trans->writable; +} +extern int kvm_walk_nested_s2 (struct kvm_vcpu *vcpu,phys_addr_t gipa,struct kvm_s2_trans *result);
virt/kvm/arm/mmu.c +20 -1
@@ -1721,7 +1721,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, unsigned long hva, unsigned long fault_status) { int ret; -bool write_fault, writable; +bool write_fault, writable, readable = true;bool exec_fault, needs_exec;unsigned long mmu_seq;phys_addr_t ipa = fault_ipa;
@@ -1841,6 +1841,16 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,if (exec_fault &&is_iomap (flags))return -ENOEXEC ; +/* +* Potentially reduce shadow S2 permissions to match the guest's own +* S2. For exec faults, we'd only reach this point if the guest +* actually allowed it (see kvm_s2_handle_perm_fault). +*/ +if (kvm_is_shadow_s2_fault (vcpu)) { + writable &=kvm_s2_trans_writable (nested); + readable &=kvm_s2_trans_readable (nested); + } +spin_lock (&kvm->mmu_lock);if (mmu_notifier_retry (kvm, mmu_seq)) goto out_unlock;
@@ -1886,6 +1896,9 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,if (writable) new_pud =kvm_s2pud_mkwrite (new_pud); +if (!readable) + new_pud =kvm_s2pud_revoke_read (new_pud); +if (needs_exec) new_pud =kvm_s2pud_mkexec (new_pud);
@@ -1898,6 +1911,9 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,if (writable) new_pmd =kvm_s2pmd_mkwrite (new_pmd); +if (!readable) + new_pmd =kvm_s2pmd_revoke_read (new_pmd); +if (needs_exec) new_pmd =kvm_s2pmd_mkexec (new_pmd);
@@ -1910,6 +1926,9 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,mark_page_dirty (kvm, gfn); } +if (!readable) + new_pte =kvm_s2pte_revoke_read (new_pte); +if (needs_exec) new_pte =kvm_s2pte_mkexec (new_pte);
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 61/94 arm64: KVM: nv: Allow userspace to request KVM_ARM_VCPU_NESTED_VIRT
In reply to Marc ZyngierSince we're (almost) feature complete, let's allow userspace to request KVM_ARM_VCPU_NESTED_VIRT by bumping the KVM_VCPU_MAX_FEATURES up.
It's going to be great...
--- arch/arm64/include/asm/kvm_host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/include/asm/kvm_host.h +1 -1
@@ -37,7 +37,7 @@#define KVM_MAX_VCPUS VGIC_V3_MAX_CPUS -#define KVM_VCPU_MAX_FEATURES 7 +#define KVM_VCPU_MAX_FEATURES 8#define KVM_REQ_SLEEP \ KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 62/94 arm64: Detect the ARMv8.4 TTL feature
In reply to Marc ZyngierIn order to reduce the cost of TLB invalidation, the ARMv8.4 TTL feature allows TLBs to be issued with a level allowing for quicker invalidation.
Let's detect the feature for now. Further patches will implement its actual usage.
--- arch/arm64/include/asm/cpucaps.h | 3 ++- arch/arm64/include/asm/sysreg.h | 1 + arch/arm64/kernel/cpufeature.c | 11 +++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-)
arch/arm64/include/asm/cpucaps.h +2 -1
@@ -59,7 +59,8 @@#define ARM64_HAS_E0PD 49#define ARM64_HAS_RNG 50#define ARM64_HAS_NESTED_VIRT 51 +#define ARM64_HAS_ARMv8_4_TTL 52 -#define ARM64_NCAPS 52 +#define ARM64_NCAPS 53#endif /* __ASM_CPUCAPS_H */
arch/arm64/include/asm/sysreg.h +1 -0
@@ -804,6 +804,7 @@#define ID_AA64MMFR2_E0PD_SHIFT 60#define ID_AA64MMFR2_EVT_SHIFT 56#define ID_AA64MMFR2_BBM_SHIFT 52 +#define ID_AA64MMFR2_TTL_SHIFT 48#define ID_AA64MMFR2_FWB_SHIFT 40#define ID_AA64MMFR2_AT_SHIFT 32#define ID_AA64MMFR2_ST_SHIFT 28
arch/arm64/kernel/cpufeature.c +11 -0
@@ -249,6 +249,7 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr1[] = {static const struct arm64_ftr_bits ftr_id_aa64mmfr2[] = {ARM64_FTR_BITS (FTR_HIDDEN ,FTR_NONSTRICT ,FTR_LOWER_SAFE ,ID_AA64MMFR2_E0PD_SHIFT ,4 ,0 ), +ARM64_FTR_BITS (FTR_HIDDEN ,FTR_STRICT ,FTR_LOWER_SAFE ,ID_AA64MMFR2_TTL_SHIFT ,4 ,0 ),ARM64_FTR_BITS (FTR_HIDDEN ,FTR_STRICT ,FTR_LOWER_SAFE ,ID_AA64MMFR2_FWB_SHIFT ,4 ,0 ),ARM64_FTR_BITS (FTR_VISIBLE ,FTR_STRICT ,FTR_LOWER_SAFE ,ID_AA64MMFR2_AT_SHIFT ,4 ,0 ),ARM64_FTR_BITS (FTR_HIDDEN ,FTR_STRICT ,FTR_LOWER_SAFE ,ID_AA64MMFR2_NV_SHIFT ,4 ,0 ),
@@ -1557,6 +1558,16 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, .cpu_enable = cpu_has_fwb, }, + { + .desc = "ARMv8.4 Translation Table Level", + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .capability = ARM64_HAS_ARMv8_4_TTL, + .sys_reg = SYS_ID_AA64MMFR2_EL1, + .sign = FTR_UNSIGNED, + .field_pos = ID_AA64MMFR2_TTL_SHIFT, + .min_field_value = 1, + .matches = has_cpuid_feature, + }, #ifdef CONFIG_ARM64_HW_AFDBM { /*
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 63/94 arm64: KVM: nv: Add handling of ARMv8.4-TTL TLB invalidation
In reply to Marc ZyngierSupport guest-provided information information to find out about the range of required invalidation.
--- arch/arm64/include/asm/kvm_nested.h | 1 + arch/arm64/kvm/nested.c | 56 ++++++++++++++++++++ arch/arm64/kvm/sys_regs.c | 79 +++++++++++++++++++---------- 3 files changed, 108 insertions(+), 28 deletions(-)
arch/arm64/include/asm/kvm_nested.h +1 -0
@@ -67,6 +67,7 @@ extern bool __forward_traps(struct kvm_vcpu *vcpu, unsigned int reg, u64 control_bit);extern bool forward_traps (struct kvm_vcpu *vcpu,u64 control_bit);extern bool forward_nv_traps (struct kvm_vcpu *vcpu); +unsigned int ttl_to_size (u8 ttl);struct sys_reg_params ;struct sys_reg_desc ;
arch/arm64/kvm/nested.c +56 -0
62 lines not on this page · raw message
arch/arm64/kvm/sys_regs.c +51 -28
116 lines not on this page · raw message
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 64/94 arm64: KVM: nv: Invalidate TLBs based on shadow S2 TTL-like information
In reply to Marc ZyngierIn order to be able to make S2 TLB invalidations more performant on NV, let's use a scheme derived from the ARMv8.4 TTL extension.
If bits [56:55] in the descriptor are non-zero, they indicate a level which can be used as an invalidation range.
--- arch/arm64/include/asm/kvm_nested.h | 4 ++ arch/arm64/kvm/nested.c | 98 +++++++++++++++++++++++++++++ arch/arm64/kvm/sys_regs.c | 13 ++-- 3 files changed, 110 insertions(+), 5 deletions(-)
arch/arm64/include/asm/kvm_nested.h +4 -0
@@ -67,6 +67,8 @@ extern bool __forward_traps(struct kvm_vcpu *vcpu, unsigned int reg, u64 control_bit);extern bool forward_traps (struct kvm_vcpu *vcpu,u64 control_bit);extern bool forward_nv_traps (struct kvm_vcpu *vcpu); +u8 get_guest_mapping_ttl (struct kvm_vcpu *vcpu,struct kvm_s2_mmu *mmu, +u64 addr);unsigned int ttl_to_size (u8 ttl);struct sys_reg_params ;
@@ -75,4 +77,6 @@ struct sys_reg_desc;void access_nested_id_reg (struct kvm_vcpu *v,struct sys_reg_params *p,const struct sys_reg_desc *r); +#define KVM_NV_GUEST_MAP_SZ GENMASK_ULL(56, 55) +#endif /* __ARM64_KVM_NESTED_H */
arch/arm64/kvm/nested.c +98 -0
116 lines not on this page · raw message
arch/arm64/kvm/sys_regs.c +8 -5
@@ -2589,10 +2589,13 @@ static unsigned long compute_tlb_inval_range(struct kvm_vcpu *vcpu, u64 val) {unsigned long max_size; -u8 ttl =0 ; +u8 ttl; -if (cpus_have_const_cap (ARM64_HAS_ARMv8_4_TTL)) { - ttl =FIELD_GET (GENMASK_ULL (47 ,44 ), val); + ttl =FIELD_GET (GENMASK_ULL (47 ,44 ), val); + +if (!(cpus_have_const_cap (ARM64_HAS_ARMv8_4_TTL) && ttl)) { +u64 addr = (val &GENMASK_ULL (35 ,0 )) <<12 ; + ttl =get_guest_mapping_ttl (vcpu, mmu, addr); } max_size =ttl_to_size (ttl);
@@ -2633,6 +2636,8 @@ static bool handle_ipas2e1is(struct kvm_vcpu *vcpu, struct sys_reg_params *p,u64 base_addr;unsigned long max_size; +spin_lock (&vcpu->kvm->mmu_lock); +/* * We drop a number of things from the supplied value: *
@@ -2644,8 +2649,6 @@ static bool handle_ipas2e1is(struct kvm_vcpu *vcpu, struct sys_reg_params *p,*/ base_addr = (p->regval &GENMASK_ULL (35 ,0 )) <<12 ; -spin_lock (&vcpu->kvm->mmu_lock); - mmu =lookup_s2_mmu (vcpu->kvm, vttbr,HCR_VM );if (mmu) { max_size =compute_tlb_inval_range (vcpu, mmu, p->regval);
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 65/94 arm64: KVM: nv: Tag shadow S2 entries with nested level
In reply to Marc ZyngierPopulate bits [56:55] of the leaf entry with the level provided by the guest's S2 translation.
--- arch/arm/include/asm/kvm_mmu.h | 5 +++++ arch/arm64/include/asm/kvm_nested.h | 6 ++++++ virt/kvm/arm/mmu.c | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+)
arch/arm/include/asm/kvm_mmu.h +5 -0
@@ -484,6 +484,11 @@ static inline bool kvm_s2_trans_writable(struct kvm_s2_trans *trans)BUG (); } +static inline u64 kvm_encode_nested_level (struct kvm_s2_trans *trans) +{ +BUG (); +} +static inline void kvm_nested_s2_flush (struct kvm *kvm) {}static inline void kvm_nested_s2_wp (struct kvm *kvm) {}static inline void kvm_nested_s2_clear (struct kvm *kvm) {}
arch/arm64/include/asm/kvm_nested.h +6 -0
@@ -2,6 +2,7 @@#ifndef __ARM64_KVM_NESTED_H#define __ARM64_KVM_NESTED_H +#include <linux/bitfield.h> #include <linux/kvm_host.h> static inline bool nested_virt_in_use (const struct kvm_vcpu *vcpu)
@@ -79,4 +80,9 @@ void access_nested_id_reg(struct kvm_vcpu *v, struct sys_reg_params *p,#define KVM_NV_GUEST_MAP_SZ GENMASK_ULL(56, 55) +static inline u64 kvm_encode_nested_level (struct kvm_s2_trans *trans) +{ +return FIELD_PREP (KVM_NV_GUEST_MAP_SZ , trans->level); +} +#endif /* __ARM64_KVM_NESTED_H */
virt/kvm/arm/mmu.c +20 -0
@@ -1715,6 +1715,11 @@ static bool fault_supports_stage2_huge_mapping(struct kvm_memory_slot *memslot, (hva & ~(map_size -1 )) + map_size <= uaddr_end; } +#define set_desc_bits (which, desc, val) \ + do { \ + desc = __ ## which(which ## _val(desc) | val); \ + } while(0) + static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, struct kvm_s2_trans *nested, struct kvm_memory_slot *memslot,
@@ -1736,6 +1741,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,unsigned long vma_pagesize, flags =0 ;struct kvm_s2_mmu *mmu = vcpu->arch.hw_mmu;unsigned long max_map_size =PUD_SIZE ; +u64 l1_s2_level; write_fault =kvm_is_write_fault (vcpu); exec_fault =kvm_vcpu_trap_is_iabt (vcpu);
@@ -1845,10 +1851,18 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,* Potentially reduce shadow S2 permissions to match the guest's own * S2. For exec faults, we'd only reach this point if the guest * actually allowed it (see kvm_s2_handle_perm_fault). +* +* Also encode the level of the nested translation in the SW bits of +* the PTE/PMD/PUD. This will be retrived on TLB invalidation from +* the guest. */ if (kvm_is_shadow_s2_fault (vcpu)) { writable &=kvm_s2_trans_writable (nested); readable &=kvm_s2_trans_readable (nested); + + l1_s2_level =kvm_encode_nested_level (nested); + }else { + l1_s2_level =0 ; }spin_lock (&kvm->mmu_lock);
@@ -1902,6 +1916,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,if (needs_exec) new_pud =kvm_s2pud_mkexec (new_pud); +set_desc_bits (pud, new_pud, l1_s2_level); + ret =stage2_set_pud_huge (mmu, memcache, fault_ipa, &new_pud); }else if (vma_pagesize ==PMD_SIZE ) {pmd_t new_pmd =kvm_pfn_pmd (pfn, mem_type);
@@ -1917,6 +1933,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,if (needs_exec) new_pmd =kvm_s2pmd_mkexec (new_pmd); +set_desc_bits (pmd, new_pmd, l1_s2_level); + ret =stage2_set_pmd_huge (mmu, memcache, fault_ipa, &new_pmd); }else {pte_t new_pte =kvm_pfn_pte (pfn, mem_type);
@@ -1932,6 +1950,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,if (needs_exec) new_pte =kvm_s2pte_mkexec (new_pte); +set_desc_bits (pte, new_pte, l1_s2_level); + ret =stage2_set_pte (mmu, memcache, fault_ipa, &new_pte, flags); }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 66/94 arm64: Add SW reserved PTE/PMD bits
In reply to Marc ZyngierAdvertise bits [58:55] as reserved for SW in the S2 descriptors.
--- arch/arm64/include/asm/pgtable-hwdef.h | 2 ++ 1 file changed, 2 insertions(+)
arch/arm64/include/asm/pgtable-hwdef.h +2 -0
@@ -177,10 +177,12 @@#define PTE_S2_RDONLY (_AT(pteval_t, 1) << 6)/* HAP[2:1] */ #define PTE_S2_RDWR (_AT(pteval_t, 3) << 6)/* HAP[2:1] */ #define PTE_S2_XN (_AT(pteval_t, 2) << 53)/* XN[1:0] */ +#define PTE_S2_SW_RESVD (_AT(pteval_t, 15) << 55)/* Reserved for SW */ #define PMD_S2_RDONLY (_AT(pmdval_t, 1) << 6)/* HAP[2:1] */ #define PMD_S2_RDWR (_AT(pmdval_t, 3) << 6)/* HAP[2:1] */ #define PMD_S2_XN (_AT(pmdval_t, 2) << 53)/* XN[1:0] */ +#define PMD_S2_SW_RESVD (_AT(pmdval_t, 15) << 55)/* Reserved for SW */ #define PUD_S2_RDONLY (_AT(pudval_t, 1) << 6)/* HAP[2:1] */ #define PUD_S2_RDWR (_AT(pudval_t, 3) << 6)/* HAP[2:1] */
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 67/94 arm64: Add level-hinted TLB invalidation helper
In reply to Marc ZyngierAdd a level-hinted TLB invalidation helper that only gets used if ARMv8.4-TTL gets detected.
--- arch/arm64/include/asm/tlbflush.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
arch/arm64/include/asm/tlbflush.h +30 -0
@@ -10,6 +10,7 @@#ifndef __ASSEMBLY__ +#include <linux/bitfield.h> #include <linux/mm_types.h> #include <linux/sched.h> #include <asm/cputype.h>
@@ -59,6 +60,35 @@ __ta; \ }) +#define TLBI_TTL_MASK GENMASK_ULL(47, 44) + +#define __tlbi_level (op, addr, level) \ + do { \ + u64 arg = addr; \ + \ + if (cpus_have_const_cap(ARM64_HAS_ARMv8_4_TTL) && \ + level) { \ + u64 ttl = level; \ + \ + switch (PAGE_SIZE) { \ + case SZ_4K: \ + ttl |= 1 << 2; \ + break; \ + case SZ_16K: \ + ttl |= 2 << 2; \ + break; \ + case SZ_64K: \ + ttl |= 3 << 2; \ + break; \ + } \ + \ + arg &= ~TLBI_TTL_MASK; \ + arg |= FIELD_PREP(TLBI_TTL_MASK, ttl); \ + } \ + \ + __tlbi(op, arg); \ + } while(0) +/* * TLB Invalidation * ================
Signature · 1 line
2.20.1
Re: [PATCH v2 67/94] arm64: Add level-hinted TLB invalidation helper
Hi Marc,
Quoted 35 lines · Marc Zyngier
Add a level-hinted TLB invalidation helper that only gets used if ARMv8.4-TTL gets detected.
Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/include/asm/tlbflush.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)arch/arm64/include/asm/tlbflush.h +12
-0@@ -10,6 +10,7 @@#ifndef __ASSEMBLY__ +#include <linux/bitfield.h> #include <linux/mm_types.h> #include <linux/sched.h> #include <asm/cputype.h> @@ -59,6 +60,35 @@ __ta; \ }) +#define TLBI_TTL_MASK GENMASK_ULL(47, 44) + +#define __tlbi_level (op, addr, level) \ + do { \ + u64 arg = addr; \ + \ + if (cpus_have_const_cap(ARM64_HAS_ARMv8_4_TTL) && \ + level) { \ + u64 ttl = level; \ + \ + switch (PAGE_SIZE) { \
arch/arm64/include/asm/tlbflush.h line 83
Quoted 10 lines
arch/arm64/include/asm/tlbflush.h +10
-0+case SZ_4K : \ + ttl |=1 <<2 ; \ +break ; \ +case SZ_16K : \ + ttl |=2 <<2 ; \ +break ; \ +case SZ_64K : \ + ttl |=3 <<2 ; \ +break ; \ + } \
Can we define a macro here to replace the switch? It will be more clearly and efficient. Such as:
#define __TG ((PAGE_SHIFT - 12) / 2 + 1)
ttl |= __TG << 2;
arch/arm64/include/asm/tlbflush.h line 94
Quoted 12 lines
arch/arm64/include/asm/tlbflush.h +8
-0+ \ + arg &= ~TLBI_TTL_MASK ; \ + arg |=FIELD_PREP (TLBI_TTL_MASK , ttl); \ + } \ + \ +__tlbi (op, arg); \ + }while (0 ) +/* * TLB Invalidation * ================
Thanks,
Zhenyu
Marc Zyngier
maintainerRe: [PATCH v2 67/94] arm64: Add level-hinted TLB invalidation helper
Zhenyu,
Quoted 52 lines · Zhenyu Ye
Hi Marc,
Quoted 35 lines · Marc Zyngier
Add a level-hinted TLB invalidation helper that only gets used if ARMv8.4-TTL gets detected.
Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/include/asm/tlbflush.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)arch/arm64/include/asm/tlbflush.h +12
-0@@ -10,6 +10,7 @@#ifndef __ASSEMBLY__ +#include <linux/bitfield.h> #include <linux/mm_types.h> #include <linux/sched.h> #include <asm/cputype.h> @@ -59,6 +60,35 @@ __ta; \ }) +#define TLBI_TTL_MASK GENMASK_ULL(47, 44) + +#define __tlbi_level (op, addr, level) \ + do { \ + u64 arg = addr; \ + \ + if (cpus_have_const_cap(ARM64_HAS_ARMv8_4_TTL) && \ + level) { \ + u64 ttl = level; \ + \ + switch (PAGE_SIZE) { \arch/arm64/include/asm/tlbflush.h line 83
Quoted 10 lines
arch/arm64/include/asm/tlbflush.h +10
-0+case SZ_4K : \ + ttl |=1 <<2 ; \ +break ; \ +case SZ_16K : \ + ttl |=2 <<2 ; \ +break ; \ +case SZ_64K : \ + ttl |=3 <<2 ; \ +break ; \ + } \Can we define a macro here to replace the switch? It will be more clearly and efficient. Such as:
Quoted 2 lines
#define
__TG((PAGE_SHIFT- 12) / 2 + 1)
ttl |=__TG<< 2;
Let me rephrase this: a convoluted formula with magic numbers in it is clearer than the values documented in the specification (Table D5-53)? I have the exact opposite view.
As for efficency, you do realize that the compiler always discards two third of this code on any possible configuration, right?
I think the code above is the clearest way to express table D5-53, and the only missing bit is a reference to that table.
Thanks,
M.
Signature · 1 line
Jazz is not dead. It just smells funny...
Marc Zyngier
maintainerPATCH v2 68/94 arm64: KVM: Add a level hint to __kvm_tlb_flush_vmid_ipa
In reply to Marc ZyngierIntroduce a level hint to __kvm_tlb_flush_vmid_ipa. No functional change.
--- arch/arm/include/asm/kvm_asm.h | 3 ++- arch/arm/kvm/hyp/tlb.c | 7 ++++--- arch/arm64/include/asm/kvm_asm.h | 3 ++- arch/arm64/kvm/hyp/tlb.c | 5 +++-- virt/kvm/arm/mmu.c | 2 +- 5 files changed, 12 insertions(+), 8 deletions(-)
arch/arm/include/asm/kvm_asm.h +2 -1
@@ -55,7 +55,8 @@ extern char __kvm_hyp_init[];extern char __kvm_hyp_init_end[];extern void __kvm_flush_vm_context (void ); -extern void __kvm_tlb_flush_vmid_ipa (struct kvm_s2_mmu *mmu,phys_addr_t ipa); +extern void __kvm_tlb_flush_vmid_ipa (struct kvm_s2_mmu *mmu,phys_addr_t ipa, +int level);extern void __kvm_tlb_flush_vmid (struct kvm_s2_mmu *mmu);extern void __kvm_tlb_flush_local_vmid (struct kvm_vcpu *vcpu);
arch/arm/kvm/hyp/tlb.c +4 -3
@@ -21,8 +21,8 @@* have to use an IPI based mechanism. Until then, we stick to the simple * hardware assisted version. * -* As v7 does not support flushing per IPA, just nuke the whole TLB -* instead, ignoring the ipa value. +* As v7 does not support flushing per IPA (let alone any level hint), +* just nuke the whole TLB instead, ignoring the ipa value. */ void __hyp_text __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu) {
@@ -39,7 +39,8 @@ void __hyp_text __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu)write_sysreg (0 ,VTTBR ); } -void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu, phys_addr_t ipa) +void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu, +phys_addr_t ipa,int level) {__kvm_tlb_flush_vmid (mmu); }
arch/arm64/include/asm/kvm_asm.h +2 -1
@@ -59,7 +59,8 @@ extern char __kvm_hyp_init_end[];extern char __kvm_hyp_vector[];extern void __kvm_flush_vm_context (void ); -extern void __kvm_tlb_flush_vmid_ipa (struct kvm_s2_mmu *mmu,phys_addr_t ipa); +extern void __kvm_tlb_flush_vmid_ipa (struct kvm_s2_mmu *mmu,phys_addr_t ipa, +int level);extern void __kvm_tlb_flush_vmid (struct kvm_s2_mmu *mmu);extern void __kvm_tlb_flush_local_vmid (struct kvm_vcpu *vcpu);extern void __kvm_tlb_vae2is (struct kvm_s2_mmu *mmu,u64 va,u64 sys_encoding);
arch/arm64/kvm/hyp/tlb.c +3 -2
@@ -132,7 +132,8 @@ static void __hyp_text __tlb_switch_to_host(struct kvm_s2_mmu *mmu,__tlb_switch_to_host_nvhe (cxt); } -void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu, phys_addr_t ipa) +void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu, +phys_addr_t ipa,int level) {struct tlb_inv_context cxt;
@@ -148,7 +149,7 @@ void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu, phys_addr_t ipa* whole of Stage-1. Weep... */ ipa >>=12 ; -__tlbi (ipas2e1is, ipa); +__tlbi_level (ipas2e1is, ipa, level);/* * We have to ensure completion of the invalidation at Stage-2,
virt/kvm/arm/mmu.c +1 -1
@@ -76,7 +76,7 @@ void kvm_flush_remote_tlbs(struct kvm *kvm)static void kvm_tlb_flush_vmid_ipa (struct kvm_s2_mmu *mmu,phys_addr_t ipa) { -kvm_call_hyp (__kvm_tlb_flush_vmid_ipa, mmu, ipa); +kvm_call_hyp (__kvm_tlb_flush_vmid_ipa, mmu, ipa,0 ); }/*
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 69/94 arm64: KVM: Use TTL hint in when invalidating stage-2 translations
In reply to Marc ZyngierSince we always have a precide idea of the level we're dealing with when invalidating TLBs, we can provide it to as a hint to our invalidation helper.
--- arch/arm/include/asm/stage2_pgtable.h | 9 +++++++++ arch/arm64/include/asm/stage2_pgtable.h | 9 +++++++++ virt/kvm/arm/mmu.c | 27 +++++++++++++------------ 3 files changed, 32 insertions(+), 13 deletions(-)
arch/arm/include/asm/stage2_pgtable.h +9 -0
@@ -72,4 +72,13 @@ static inline bool kvm_stage2_has_pmd(struct kvm *kvm) return true; } +/* +* The ARMv8.4-TTL extension doesn't apply to AArch32, so the below is +* only to keep things compiling. +*/ +#define S2_NO_LEVEL_HINT 0 +#define S2_PUD_LEVEL S2_NO_LEVEL_HINT +#define S2_PMD_LEVEL S2_NO_LEVEL_HINT +#define S2_PTE_LEVEL S2_NO_LEVEL_HINT +#endif /* __ARM_S2_PGTABLE_H_ */
arch/arm64/include/asm/stage2_pgtable.h +9 -0
@@ -230,4 +230,13 @@ stage2_pgd_addr_end(struct kvm *kvm, phys_addr_t addr, phys_addr_t end) return (boundary - 1 < end - 1) ? boundary : end; } +/* +* Level values for the ARMv8.4-TTL extension, mapping PUD/PMD/PTE and +* the architectural page-table level. +*/ +#define S2_NO_LEVEL_HINT 0 +#define S2_PUD_LEVEL 1 +#define S2_PMD_LEVEL 2 +#define S2_PTE_LEVEL 3 +#endif /* __ARM64_S2_PGTABLE_H_ */
virt/kvm/arm/mmu.c +14 -13
@@ -74,9 +74,10 @@ void kvm_flush_remote_tlbs(struct kvm *kvm) } } -static void kvm_tlb_flush_vmid_ipa (struct kvm_s2_mmu *mmu,phys_addr_t ipa) +static void kvm_tlb_flush_vmid_ipa (struct kvm_s2_mmu *mmu,phys_addr_t ipa, +int level) { -kvm_call_hyp (__kvm_tlb_flush_vmid_ipa, mmu, ipa,0 ); +kvm_call_hyp (__kvm_tlb_flush_vmid_ipa, mmu, ipa, level); }/*
@@ -118,7 +119,7 @@ static void stage2_dissolve_pmd(struct kvm_s2_mmu *mmu, phys_addr_t addr, pmd_treturn ;pmd_clear (pmd); -kvm_tlb_flush_vmid_ipa (mmu, addr); +kvm_tlb_flush_vmid_ipa (mmu, addr,S2_PMD_LEVEL );put_page (virt_to_page (pmd)); }
@@ -138,7 +139,7 @@ static void stage2_dissolve_pud(struct kvm_s2_mmu *mmu, phys_addr_t addr, pud_treturn ;stage2_pud_clear (kvm, pudp); -kvm_tlb_flush_vmid_ipa (mmu, addr); +kvm_tlb_flush_vmid_ipa (mmu, addr,S2_PUD_LEVEL );put_page (virt_to_page (pudp)); }
@@ -180,7 +181,7 @@ static void clear_stage2_pgd_entry(struct kvm_s2_mmu *mmu, pgd_t *pgd, phys_addrpud_t *pud_table__maybe_unused =stage2_pud_offset (kvm, pgd,0UL );stage2_pgd_clear (kvm, pgd); -kvm_tlb_flush_vmid_ipa (mmu, addr); +kvm_tlb_flush_vmid_ipa (mmu, addr,S2_NO_LEVEL_HINT );stage2_pud_free (kvm, pud_table);put_page (virt_to_page (pgd)); }
@@ -192,7 +193,7 @@ static void clear_stage2_pud_entry(struct kvm_s2_mmu *mmu, pud_t *pud, phys_addrpmd_t *pmd_table__maybe_unused =stage2_pmd_offset (kvm, pud,0 );VM_BUG_ON (stage2_pud_huge (kvm, *pud));stage2_pud_clear (kvm, pud); -kvm_tlb_flush_vmid_ipa (mmu, addr); +kvm_tlb_flush_vmid_ipa (mmu, addr,S2_NO_LEVEL_HINT );stage2_pmd_free (kvm, pmd_table);put_page (virt_to_page (pud)); }
@@ -202,7 +203,7 @@ static void clear_stage2_pmd_entry(struct kvm_s2_mmu *mmu, pmd_t *pmd, phys_addrpte_t *pte_table =pte_offset_kernel (pmd,0 );VM_BUG_ON (pmd_thp_or_huge (*pmd));pmd_clear (pmd); -kvm_tlb_flush_vmid_ipa (mmu, addr); +kvm_tlb_flush_vmid_ipa (mmu, addr,S2_NO_LEVEL_HINT );free_page ((unsigned long )pte_table);put_page (virt_to_page (pmd)); }
@@ -272,7 +273,7 @@ static void unmap_stage2_ptes(struct kvm_s2_mmu *mmu, pmd_t *pmd,pte_t old_pte = *pte;kvm_set_pte (pte,__pte (0 )); -kvm_tlb_flush_vmid_ipa (mmu, addr); +kvm_tlb_flush_vmid_ipa (mmu, addr,S2_PTE_LEVEL );/* No need to invalidate the cache for device mappings */ if (!kvm_is_device_pfn (pte_pfn (old_pte)))
@@ -301,7 +302,7 @@ static void unmap_stage2_pmds(struct kvm_s2_mmu *mmu, pud_t *pud,pmd_t old_pmd = *pmd;pmd_clear (pmd); -kvm_tlb_flush_vmid_ipa (mmu, addr); +kvm_tlb_flush_vmid_ipa (mmu, addr,S2_PMD_LEVEL );kvm_flush_dcache_pmd (old_pmd);
@@ -331,7 +332,7 @@ static void unmap_stage2_puds(struct kvm_s2_mmu *mmu, pgd_t *pgd,pud_t old_pud = *pud;stage2_pud_clear (kvm, pud); -kvm_tlb_flush_vmid_ipa (mmu, addr); +kvm_tlb_flush_vmid_ipa (mmu, addr,S2_PUD_LEVEL );kvm_flush_dcache_pud (old_pud);put_page (virt_to_page (pud)); }else {
@@ -1158,7 +1159,7 @@ static int stage2_set_pmd_huge(struct kvm_s2_mmu *mmu,*/ WARN_ON_ONCE (pmd_pfn (old_pmd) !=pmd_pfn (*new_pmd));pmd_clear (pmd); -kvm_tlb_flush_vmid_ipa (mmu, addr); +kvm_tlb_flush_vmid_ipa (mmu, addr,S2_PMD_LEVEL ); }else {get_page (virt_to_page (pmd)); }
@@ -1200,7 +1201,7 @@ static int stage2_set_pud_huge(struct kvm_s2_mmu *mmu,WARN_ON_ONCE (kvm_pud_pfn (old_pud) !=kvm_pud_pfn (*new_pudp));stage2_pud_clear (kvm, pudp); -kvm_tlb_flush_vmid_ipa (mmu, addr); +kvm_tlb_flush_vmid_ipa (mmu, addr,S2_PUD_LEVEL ); }else {get_page (virt_to_page (pudp)); }
@@ -1349,7 +1350,7 @@ static int stage2_set_pte(struct kvm_s2_mmu *mmu,return 0 ;kvm_set_pte (pte,__pte (0 )); -kvm_tlb_flush_vmid_ipa (mmu, addr); +kvm_tlb_flush_vmid_ipa (mmu, addr,S2_PTE_LEVEL ); }else {get_page (virt_to_page (pte)); }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 70/94 arm64: KVM: nv: Add include containing the VNCR_EL2 offsets
In reply to Marc ZyngierVNCR_EL2 points to a page containing a number of system registers accessed by a guest hypervisor when ARMv8.4-NV is enabled.
Let's document the offsets in that page, as we are going to use this layout.
--- arch/arm64/include/asm/vncr_mapping.h | 73 +++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 arch/arm64/include/asm/vncr_mapping.h
arch/arm64/include/asm/vncr_mapping.h new file +73 -0
@@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* System register offsets in the VNCR page +* All offsets are *byte* displacements! +*/ + +#ifndef __ARM64_VNCR_MAPPING_H__ +#define __ARM64_VNCR_MAPPING_H__ + +#define VNCR_VTTBR_EL2 0x020 +#define VNCR_VTCR_EL2 0x040 +#define VNCR_VMPIDR_EL2 0x050 +#define VNCR_CNTVOFF_EL2 0x060 +#define VNCR_HCR_EL2 0x078 +#define VNCR_HSTR_EL2 0x080 +#define VNCR_VPIDR_EL2 0x088 +#define VNCR_TPIDR_EL2 0x090 +#define VNCR_VNCR_EL2 0x0B0 +#define VNCR_CPACR_EL1 0x100 +#define VNCR_CONTEXTIDR_EL1 0x108 +#define VNCR_SCTLR_EL1 0x110 +#define VNCR_ACTLR_EL1 0x118 +#define VNCR_TCR_EL1 0x120 +#define VNCR_AFSR0_EL1 0x128 +#define VNCR_AFSR1_EL1 0x130 +#define VNCR_ESR_EL1 0x138 +#define VNCR_MAIR_EL1 0x140 +#define VNCR_AMAIR_EL1 0x148 +#define VNCR_MDSCR_EL1 0x158 +#define VNCR_SPSR_EL1 0x160 +#define VNCR_CNTV_CVAL_EL0 0x168 +#define VNCR_CNTV_CTL_EL0 0x170 +#define VNCR_CNTP_CVAL_EL0 0x178 +#define VNCR_CNTP_CTL_EL0 0x180 +#define VNCR_SCXTNUM_EL1 0x188 +#define VNCR_ZCR_EL1 0x1E0 +#define VNCR_TTBR0_EL1 0x200 +#define VNCR_TTBR1_EL1 0x210 +#define VNCR_FAR_EL1 0x220 +#define VNCR_ELR_EL1 0x230 +#define VNCR_SP_EL1 0x240 +#define VNCR_VBAR_EL1 0x250 +#define VNCR_ICH_LR0_EL2 0x400 +// VNCR_ICH_LRN_EL2(n) VNCR_ICH_LR0_EL2+8*((n) & 7) +#define VNCR_ICH_AP0R0_EL2 0x480 +// VNCR_ICH_AP0RN_EL2(n) VNCR_ICH_AP0R0_EL2+8*((n) & 3) +#define VNCR_ICH_AP1R0_EL2 0x4A0 +// VNCR_ICH_AP1RN_EL2(n) VNCR_ICH_AP1R0_EL2+8*((n) & 3) +#define VNCR_ICH_HCR_EL2 0x4C0 +#define VNCR_ICH_VMCR_EL2 0x4C8 +#define VNCR_VDISR_EL2 0x500 +#define VNCR_PMBLIMITR_EL1 0x800 +#define VNCR_PMBPTR_EL1 0x810 +#define VNCR_PMBSR_EL1 0x820 +#define VNCR_PMSCR_EL1 0x828 +#define VNCR_PMSEVFR_EL1 0x830 +#define VNCR_PMSICR_EL1 0x838 +#define VNCR_PMSIRR_EL1 0x840 +#define VNCR_PMSLATFR_EL1 0x848 +#define VNCR_TRFCR_EL1 0x880 +#define VNCR_MPAM1_EL1 0x900 +#define VNCR_MPAMHCR_EL2 0x930 +#define VNCR_MPAMVPMV_EL2 0x938 +#define VNCR_MPAMVPM0_EL2 0x940 +#define VNCR_MPAMVPM1_EL2 0x948 +#define VNCR_MPAMVPM2_EL2 0x950 +#define VNCR_MPAMVPM3_EL2 0x958 +#define VNCR_MPAMVPM4_EL2 0x960 +#define VNCR_MPAMVPM5_EL2 0x968 +#define VNCR_MPAMVPM6_EL2 0x970 +#define VNCR_MPAMVPM7_EL2 0x978 + +#endif /* __ARM64_VNCR_MAPPING_H__ */
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 71/94 KVM: arm64: Introduce accessor for ctxt->sys_reg
In reply to Marc ZyngierIn order to allow the disintegration of the per-vcpu sysreg array, let's introduce a new helper (ctxt_sys_reg()) that returns the in-memory copy of a system register, picked from a given context.
__vcpu_sys_reg() is rewritten to use this helper.
--- arch/arm64/include/asm/kvm_host.h | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-)
arch/arm64/include/asm/kvm_host.h +10 -5
@@ -468,12 +468,17 @@ struct kvm_vcpu_arch {#define vcpu_gp_regs (v) (&(v)->arch.ctxt.gp_regs)/* -* Only use __vcpu_sys_reg if you know you want the memory backed version of a -* register, and not the one most recently accessed by a running VCPU. For -* example, for userspace access or for system registers that are never context -* switched, but only emulated. +* Only use __vcpu_sys_reg/ctxt_sys_reg if you know you want the +* memory backed version of a register, and not the one most recently +* accessed by a running VCPU. For example, for userspace access or +* for system registers that are never context switched, but only +* emulated. */ -#define __vcpu_sys_reg (v,r) ((v)->arch.ctxt.sys_regs[(r)]) +#define __ctxt_sys_reg (c,r) (&(c)->sys_regs[(r)]) + +#define ctxt_sys_reg (c,r) (*__ctxt_sys_reg(c,r)) + +#define __vcpu_sys_reg (v,r) (ctxt_sys_reg(&(v)->arch.ctxt, (r)))u64 vcpu_read_sys_reg (const struct kvm_vcpu *vcpu,int reg);void vcpu_write_sys_reg (struct kvm_vcpu *vcpu,u64 val,int reg);
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 72/94 KVM: arm64: sysreg: Use ctxt_sys_reg() instead of raw sys_regs access
In reply to Marc ZyngierNow that we have a wrapper for the sysreg accesses, let's use that consistently.
--- arch/arm64/include/asm/kvm_emulate.h | 6 +- arch/arm64/include/asm/kvm_host.h | 2 +- arch/arm64/kvm/hyp/at.c | 30 ++--- arch/arm64/kvm/hyp/switch.c | 6 +- arch/arm64/kvm/hyp/sysreg-sr.c | 192 +++++++++++++-------------- 5 files changed, 118 insertions(+), 118 deletions(-)
arch/arm64/include/asm/kvm_emulate.h +3 -3
@@ -244,7 +244,7 @@ static inline bool vcpu_mode_el2(const struct kvm_vcpu *vcpu)static inline bool __vcpu_el2_e2h_is_set (const struct kvm_cpu_context *ctxt) { -return ctxt->sys_regs[HCR_EL2 ] &HCR_E2H ; +return ctxt_sys_reg (ctxt,HCR_EL2 ) &HCR_E2H ; }static inline bool vcpu_el2_e2h_is_set (const struct kvm_vcpu *vcpu)
@@ -254,7 +254,7 @@ static inline bool vcpu_el2_e2h_is_set(const struct kvm_vcpu *vcpu)static inline bool __vcpu_el2_tge_is_set (const struct kvm_cpu_context *ctxt) { -return ctxt->sys_regs[HCR_EL2 ] &HCR_TGE ; +return ctxt_sys_reg (ctxt,HCR_EL2 ) &HCR_TGE ; }static inline bool vcpu_el2_tge_is_set (const struct kvm_vcpu *vcpu)
@@ -303,7 +303,7 @@ static inline u64 __fixup_spsr_el2_read(const struct kvm_cpu_context *ctxt, u64* register has still the value we saved on the last write. */ if ((val &0xc ) ==0 ) -return ctxt->sys_regs[SPSR_EL2 ]; +return ctxt_sys_reg (ctxt,SPSR_EL2 );/* * Otherwise there was a "local" exception on the CPU,
arch/arm64/include/asm/kvm_host.h +1 -1
@@ -609,7 +609,7 @@ DECLARE_PER_CPU(kvm_host_data_t, kvm_host_data);static inline void kvm_init_host_cpu_context (struct kvm_cpu_context *cpu_ctxt) {/* The host's MPIDR is immutable, so let's set it up at boot time */ - cpu_ctxt->sys_regs[MPIDR_EL1 ] =read_cpuid_mpidr (); +ctxt_sys_reg (cpu_ctxt,MPIDR_EL1 ) =read_cpuid_mpidr (); }void __kvm_enable_ssbs (void );
arch/arm64/kvm/hyp/at.c +15 -15
@@ -83,10 +83,10 @@ void __kvm_at_s1e01(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)/* We've trapped, so everything is live on the CPU. */ __mmu_config_save (&config); -write_sysreg_el1 (ctxt->sys_regs[TTBR0_EL1 ],SYS_TTBR0 ); -write_sysreg_el1 (ctxt->sys_regs[TTBR1_EL1 ],SYS_TTBR1 ); -write_sysreg_el1 (ctxt->sys_regs[TCR_EL1 ],SYS_TCR ); -write_sysreg_el1 (ctxt->sys_regs[SCTLR_EL1 ],SYS_SCTLR ); +write_sysreg_el1 (ctxt_sys_reg (ctxt,TTBR0_EL1 ),SYS_TTBR0 ); +write_sysreg_el1 (ctxt_sys_reg (ctxt,TTBR1_EL1 ),SYS_TTBR1 ); +write_sysreg_el1 (ctxt_sys_reg (ctxt,TCR_EL1 ),SYS_TCR ); +write_sysreg_el1 (ctxt_sys_reg (ctxt,SCTLR_EL1 ),SYS_SCTLR );write_sysreg (kvm_get_vttbr (mmu), vttbr_el2);/* * REVISIT: do we need anything from the guest's VTCR_EL2? If
@@ -122,7 +122,7 @@ void __kvm_at_s1e01(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)isb (); - ctxt->sys_regs[PAR_EL1 ] =read_sysreg (par_el1); +ctxt_sys_reg (ctxt,PAR_EL1 ) =read_sysreg (par_el1);/* * Failed? let's leave the building now.
@@ -131,7 +131,7 @@ void __kvm_at_s1e01(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)* wasn't populated? We may need to perform a SW PTW, * populating our shadow S2 and retry the instruction. */ -if (ctxt->sys_regs[PAR_EL1 ] &1 ) +if (ctxt_sys_reg (ctxt,PAR_EL1 ) &1 ) goto nopan;/* No PAN? No problem. */
@@ -162,7 +162,7 @@ void __kvm_at_s1e01(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)* should return the real fault level. */ if (!(read_sysreg (par_el1) &1 )) - ctxt->sys_regs[PAR_EL1 ] =0x1f ; +ctxt_sys_reg (ctxt,PAR_EL1 ) =0x1f ; nopan:if (!(vcpu_el2_e2h_is_set (vcpu) &&vcpu_el2_tge_is_set (vcpu)))
@@ -187,17 +187,17 @@ void __kvm_at_s1e2(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)__mmu_config_save (&config);if (vcpu_el2_e2h_is_set (vcpu)) { -write_sysreg_el1 (ctxt->sys_regs[TTBR0_EL2 ],SYS_TTBR0 ); -write_sysreg_el1 (ctxt->sys_regs[TTBR1_EL2 ],SYS_TTBR1 ); -write_sysreg_el1 (ctxt->sys_regs[TCR_EL2 ],SYS_TCR ); -write_sysreg_el1 (ctxt->sys_regs[SCTLR_EL2 ],SYS_SCTLR ); +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,SCTLR_EL2 ),SYS_SCTLR ); val = config.hcr; }else { -write_sysreg_el1 (ctxt->sys_regs[TTBR0_EL2 ],SYS_TTBR0 ); -write_sysreg_el1 (translate_tcr (ctxt->sys_regs[TCR_EL2 ]), +write_sysreg_el1 (ctxt_sys_reg (ctxt,TTBR0_EL2 ),SYS_TTBR0 ); +write_sysreg_el1 (translate_tcr (ctxt_sys_reg (ctxt,TCR_EL2 )),SYS_TCR ); -write_sysreg_el1 (translate_sctlr (ctxt->sys_regs[SCTLR_EL2 ]), +write_sysreg_el1 (translate_sctlr (ctxt_sys_reg (ctxt,SCTLR_EL2 )),SYS_SCTLR ); val = config.hcr |HCR_NV |HCR_NV1 ;
@@ -224,7 +224,7 @@ void __kvm_at_s1e2(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)isb ();/* FIXME: handle failed translation due to shadow S2 */ - ctxt->sys_regs[PAR_EL1 ] =read_sysreg (par_el1); +ctxt_sys_reg (ctxt,PAR_EL1 ) =read_sysreg (par_el1);__mmu_config_restore (&config);spin_unlock (&vcpu->kvm->mmu_lock);
arch/arm64/kvm/hyp/switch.c +3 -3
@@ -138,9 +138,9 @@ static void __hyp_text __activate_traps_nvhe(struct kvm_vcpu *vcpu)* configured and enabled. We can now restore the guest's S1 * configuration: SCTLR, and only then TCR. */ -write_sysreg_el1 (ctxt->sys_regs[SCTLR_EL1 ],SYS_SCTLR ); +write_sysreg_el1 (ctxt_sys_reg (ctxt,SCTLR_EL1 ),SYS_SCTLR );isb (); -write_sysreg_el1 (ctxt->sys_regs[TCR_EL1 ],SYS_TCR ); +write_sysreg_el1 (ctxt_sys_reg (ctxt,TCR_EL1 ),SYS_TCR ); } }
@@ -463,7 +463,7 @@ static bool __hyp_text __hyp_handle_fpsimd(struct kvm_vcpu *vcpu)sve_load_state (vcpu_sve_pffr (vcpu), &vcpu->arch.ctxt.gp_regs.fp_regs.fpsr,sve_vq_from_vl (vcpu->arch.sve_max_vl) -1 ); -write_sysreg_s (vcpu->arch.ctxt.sys_regs[ZCR_EL1 ],SYS_ZCR_EL12 ); +write_sysreg_s (__vcpu_sys_reg (vcpu,ZCR_EL1 ),SYS_ZCR_EL12 ); }else {__fpsimd_restore_state (&vcpu->arch.ctxt.gp_regs.fp_regs); }
arch/arm64/kvm/hyp/sysreg-sr.c +96 -96
316 lines not on this page · raw message
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 73/94 KVM: arm64: sve: Use __vcpu_sys_reg() instead of raw sys_regs access
In reply to Marc ZyngierNow that we have a wrapper for the sysreg accesses, let's use that consistently.
--- arch/arm64/kvm/fpsimd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
arch/arm64/kvm/fpsimd.c +1 -3
@@ -110,12 +110,10 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu)local_irq_save (flags);if (vcpu->arch.flags &KVM_ARM64_FP_ENABLED ) { -u64 *guest_zcr = &vcpu->arch.ctxt.sys_regs[ZCR_EL1 ]; -fpsimd_save_and_flush_cpu_state ();if (guest_has_sve) - *guest_zcr =read_sysreg_s (SYS_ZCR_EL12 ); +__vcpu_sys_reg (vcpu,ZCR_EL1 ) =read_sysreg_s (SYS_ZCR_EL12 ); }else if (host_has_sve) {/* * The FPSIMD/SVE state in the CPU has not been touched, and we
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 74/94 KVM: arm64: pauth: Use ctxt_sys_reg() instead of raw sys_regs access
In reply to Marc ZyngierNow that we have a wrapper for the sysreg accesses, let's use that consistently.
--- arch/arm64/kvm/handle_exit.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
arch/arm64/kvm/handle_exit.c +9 -8
@@ -217,10 +217,11 @@ static int handle_sve(struct kvm_vcpu *vcpu, struct kvm_run *run)return 1 ; } -#define __ptrauth_save_key (regs, key) \ + +#define __ptrauth_save_key (ctxt, key) \ ({ \ - regs[key ## KEYLO_EL1] = read_sysreg_s(SYS_ ## key ## KEYLO_EL1); \ - regs[key ## KEYHI_EL1] = read_sysreg_s(SYS_ ## key ## KEYHI_EL1); \ + ctxt_sys_reg(ctxt, key ## KEYLO_EL1) = read_sysreg_s(SYS_ ## key ## KEYLO_EL1); \ + ctxt_sys_reg(ctxt, key ## KEYHI_EL1) = read_sysreg_s(SYS_ ## key ## KEYHI_EL1); \ })/*
@@ -234,11 +235,11 @@ void kvm_arm_vcpu_ptrauth_trap(struct kvm_vcpu *vcpu)if (vcpu_has_ptrauth (vcpu)) {vcpu_ptrauth_enable (vcpu); ctxt = vcpu->arch.host_cpu_context; -__ptrauth_save_key (ctxt->sys_regs,APIA ); -__ptrauth_save_key (ctxt->sys_regs,APIB ); -__ptrauth_save_key (ctxt->sys_regs,APDA ); -__ptrauth_save_key (ctxt->sys_regs,APDB ); -__ptrauth_save_key (ctxt->sys_regs,APGA ); +__ptrauth_save_key (ctxt,APIA ); +__ptrauth_save_key (ctxt,APIB ); +__ptrauth_save_key (ctxt,APDA ); +__ptrauth_save_key (ctxt,APDB ); +__ptrauth_save_key (ctxt,APGA ); }else {kvm_inject_undefined (vcpu); }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 75/94 KVM: arm64: debug: Use ctxt_sys_reg() instead of raw sys_regs access
In reply to Marc ZyngierNow that we have a wrapper for the sysreg accesses, let's use that consistently.
--- arch/arm64/kvm/hyp/debug-sr.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-)
arch/arm64/kvm/hyp/debug-sr.c +8 -10
@@ -129,8 +129,7 @@ static void __hyp_text __debug_restore_spe_nvhe(u64 pmscr_el1)write_sysreg_s (pmscr_el1,SYS_PMSCR_EL1 ); } -static void __hyp_text __debug_save_state(struct kvm_vcpu *vcpu, -struct kvm_guest_debug_arch *dbg, +static void __hyp_text __debug_save_state(struct kvm_guest_debug_arch *dbg,struct kvm_cpu_context *ctxt) {u64 aa64dfr0;
@@ -145,11 +144,10 @@ static void __hyp_text __debug_save_state(struct kvm_vcpu *vcpu,save_debug (dbg->dbg_wcr, dbgwcr, wrps);save_debug (dbg->dbg_wvr, dbgwvr, wrps); - ctxt->sys_regs[MDCCINT_EL1 ] =read_sysreg (mdccint_el1); +ctxt_sys_reg (ctxt,MDCCINT_EL1 ) =read_sysreg (mdccint_el1); } -static void __hyp_text __debug_restore_state(struct kvm_vcpu *vcpu, -struct kvm_guest_debug_arch *dbg, +static void __hyp_text __debug_restore_state(struct kvm_guest_debug_arch *dbg,struct kvm_cpu_context *ctxt) {u64 aa64dfr0;
@@ -165,7 +163,7 @@ static void __hyp_text __debug_restore_state(struct kvm_vcpu *vcpu,restore_debug (dbg->dbg_wcr, dbgwcr, wrps);restore_debug (dbg->dbg_wvr, dbgwvr, wrps); -write_sysreg (ctxt->sys_regs[MDCCINT_EL1 ], mdccint_el1); +write_sysreg (ctxt_sys_reg (ctxt,MDCCINT_EL1 ), mdccint_el1); } void __hyp_text __debug_switch_to_guest(struct kvm_vcpu *vcpu)
@@ -190,8 +188,8 @@ void __hyp_text __debug_switch_to_guest(struct kvm_vcpu *vcpu) host_dbg = &vcpu->arch.host_debug_state.regs; guest_dbg =kern_hyp_va (vcpu->arch.debug_ptr); -__debug_save_state (vcpu, host_dbg, host_ctxt); -__debug_restore_state (vcpu, guest_dbg, guest_ctxt); +__debug_save_state (host_dbg, host_ctxt); +__debug_restore_state (guest_dbg, guest_ctxt); } void __hyp_text __debug_switch_to_host(struct kvm_vcpu *vcpu)
@@ -212,8 +210,8 @@ void __hyp_text __debug_switch_to_host(struct kvm_vcpu *vcpu) host_dbg = &vcpu->arch.host_debug_state.regs; guest_dbg =kern_hyp_va (vcpu->arch.debug_ptr); -__debug_save_state (vcpu, guest_dbg, guest_ctxt); -__debug_restore_state (vcpu, host_dbg, host_ctxt); +__debug_save_state (guest_dbg, guest_ctxt); +__debug_restore_state (host_dbg, host_ctxt); vcpu->arch.flags &= ~KVM_ARM64_DEBUG_DIRTY ; }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 76/94 KVM: arm64: Add missing reset handlers for PMU emulation
In reply to Marc ZyngierAs we're about to become a bit more harsh when it comes to the lack of reset callbacks, let's add the missing PMU reset handlers. Note that these only cover *CLR registers that were always covered by their *SET counterpart, so there is no semantic change here.
--- arch/arm64/kvm/sys_regs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
arch/arm64/kvm/sys_regs.c +3 -3
@@ -2151,7 +2151,7 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_PAR_EL1), NULL, reset_unknown, PAR_EL1 }, { SYS_DESC(SYS_PMINTENSET_EL1), access_pminten, reset_unknown, PMINTENSET_EL1 }, - { SYS_DESC(SYS_PMINTENCLR_EL1), access_pminten, NULL, PMINTENSET_EL1 }, + { SYS_DESC(SYS_PMINTENCLR_EL1), access_pminten, reset_unknown, PMINTENSET_EL1 }, { SYS_DESC(SYS_MAIR_EL1), access_vm_reg, reset_unknown, MAIR_EL1 }, { SYS_DESC(SYS_AMAIR_EL1), access_vm_reg, reset_amair_el1, AMAIR_EL1 },
@@ -2190,8 +2190,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_PMCR_EL0), access_pmcr, reset_pmcr, PMCR_EL0 }, { SYS_DESC(SYS_PMCNTENSET_EL0), access_pmcnten, reset_unknown, PMCNTENSET_EL0 }, - { SYS_DESC(SYS_PMCNTENCLR_EL0), access_pmcnten, NULL, PMCNTENSET_EL0 }, - { SYS_DESC(SYS_PMOVSCLR_EL0), access_pmovs, NULL, PMOVSSET_EL0 }, + { SYS_DESC(SYS_PMCNTENCLR_EL0), access_pmcnten, reset_unknown, PMCNTENSET_EL0 }, + { SYS_DESC(SYS_PMOVSCLR_EL0), access_pmovs, reset_unknown, PMOVSSET_EL0 }, { SYS_DESC(SYS_PMSWINC_EL0), access_pmswinc, reset_unknown, PMSWINC_EL0 }, { SYS_DESC(SYS_PMSELR_EL0), access_pmselr, reset_unknown, PMSELR_EL0 }, { SYS_DESC(SYS_PMCEID0_EL0), access_pmceid },
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 77/94 KVM: arm64: nv: Move sysreg reset check to boot time
In reply to Marc ZyngierOur sysreg reset check has become a bit silly, as it only checks whether a reset callback actually exists for a given sysreg entry, and apply the method if available. Doing the check at each vcpu reset is pretty dumb, as the tables never change. It is thus perfectly possible to do the same checks at boot time.
This also allows us to introduce a sparse sys_regs[] array, something that will be required with ARMv8.4-NV.
--- arch/arm64/kvm/sys_regs.c | 74 +++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 38 deletions(-)
arch/arm64/kvm/sys_regs.c +36 -38
@@ -3166,12 +3166,37 @@ static const struct sys_reg_desc cp15_64_regs[] = { { SYS_DESC(SYS_AARCH32_CNTP_CVAL), access_arch_timer }, }; +static int check_sysreg_table (const struct sys_reg_desc *table,unsigned int n, +bool is_32) +{ +unsigned int i; + +for (i =0 ; i < n; i++) { +if (!is_32 && table[i].reg && !table[i].reset) { +kvm_err ("sys_reg table %p entry %d has lacks reset\n" , + table, i); +return 1 ; + } + +if (i &&cmp_sys_reg (&table[i-1 ], &table[i]) >=0 ) { +kvm_err ("sys_reg table %p out of order (%d)\n" , table, i -1 ); +return 1 ; + } + } + +return 0 ; +} +/* Target specific emulation tables */ static struct kvm_sys_reg_target_table *target_tables[KVM_ARM_NUM_TARGETS ];void kvm_register_target_sys_reg_table (unsigned int target,struct kvm_sys_reg_target_table *table) { +if (check_sysreg_table (table->table64.table, table->table64.num, false) || +check_sysreg_table (table->table32.table, table->table32.num, true)) +return ; + target_tables[target] = table; }
@@ -3483,19 +3508,13 @@ static int emulate_sys_instr(struct kvm_vcpu *vcpu, struct sys_reg_params *p) }static void reset_sys_reg_descs (struct kvm_vcpu *vcpu, -const struct sys_reg_desc *table,size_t num, -unsigned long *bmap) +const struct sys_reg_desc *table,size_t num) {unsigned long i;for (i =0 ; i < num; i++) -if (table[i].reset) { -int reg = table[i].reg; - +if (table[i].reset) table[i].reset (vcpu, &table[i]); -if (reg >0 && reg <NR_SYS_REGS ) -set_bit (reg, bmap); - } }/**
@@ -3958,33 +3977,19 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)return write_demux_regids (uindices); } -static int check_sysreg_table (const struct sys_reg_desc *table,unsigned int n) -{ -unsigned int i; - -for (i =1 ; i < n; i++) { -if (cmp_sys_reg (&table[i-1 ], &table[i]) >=0 ) { -kvm_err ("sys_reg table %p out of order (%d)\n" , table, i -1 ); -return 1 ; - } - } - -return 0 ; -} -void kvm_sys_reg_table_init (void ) {unsigned int i;struct sys_reg_desc clidr;/* Make sure tables are unique and in order. */ -BUG_ON (check_sysreg_table (sys_reg_descs,ARRAY_SIZE (sys_reg_descs))); -BUG_ON (check_sysreg_table (cp14_regs,ARRAY_SIZE (cp14_regs))); -BUG_ON (check_sysreg_table (cp14_64_regs,ARRAY_SIZE (cp14_64_regs))); -BUG_ON (check_sysreg_table (cp15_regs,ARRAY_SIZE (cp15_regs))); -BUG_ON (check_sysreg_table (cp15_64_regs,ARRAY_SIZE (cp15_64_regs))); -BUG_ON (check_sysreg_table (invariant_sys_regs,ARRAY_SIZE (invariant_sys_regs))); -BUG_ON (check_sysreg_table (sys_insn_descs,ARRAY_SIZE (sys_insn_descs))); +BUG_ON (check_sysreg_table (sys_reg_descs,ARRAY_SIZE (sys_reg_descs), false)); +BUG_ON (check_sysreg_table (cp14_regs,ARRAY_SIZE (cp14_regs), true)); +BUG_ON (check_sysreg_table (cp14_64_regs,ARRAY_SIZE (cp14_64_regs), true)); +BUG_ON (check_sysreg_table (cp15_regs,ARRAY_SIZE (cp15_regs), true)); +BUG_ON (check_sysreg_table (cp15_64_regs,ARRAY_SIZE (cp15_64_regs), true)); +BUG_ON (check_sysreg_table (invariant_sys_regs,ARRAY_SIZE (invariant_sys_regs), false)); +BUG_ON (check_sysreg_table (sys_insn_descs,ARRAY_SIZE (sys_insn_descs), false));/* We abuse the reset function to overwrite the table itself. */ for (i =0 ; i <ARRAY_SIZE (invariant_sys_regs); i++)
@@ -4020,17 +4025,10 @@ void kvm_reset_sys_regs(struct kvm_vcpu *vcpu) {size_t num;const struct sys_reg_desc *table; - DECLARE_BITMAP(bmap, NR_SYS_REGS) = { 0, };/* Generic chip reset first (so target could override). */ -reset_sys_reg_descs (vcpu, sys_reg_descs,ARRAY_SIZE (sys_reg_descs), bmap); +reset_sys_reg_descs (vcpu, sys_reg_descs,ARRAY_SIZE (sys_reg_descs)); table =get_target_table (vcpu->arch.target, true, &num); -reset_sys_reg_descs (vcpu, table, num, bmap); - -for (num =1 ; num <NR_SYS_REGS ; num++) { -if (WARN (!test_bit (num, bmap), -"Didn't reset __vcpu_sys_reg(%zi)\n" , num)) -break ; - } +reset_sys_reg_descs (vcpu, table, num); }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 78/94 KVM: arm64: Map VNCR-capable registers to a separate page
In reply to Marc ZyngierWith ARMv8.4-NV, registers that can be directly accessed in memory by the guest have to live at architected offsets in a special page.
Let's annotate the sysreg enum to reflect the offset at which they are in this page, whith a little twist:
If running on HW that doesn't have the ARMv8.4-NV feature, or even a VM that doesn't use NV, we store all the system registers in the usual sys_regs array. The only difference with the pre-8.4 situation is that VNCR-capable registers are at a "similar" offset as in the VNCR page (we can compute the actual offset at compile time), and that the sys_regs array is both bigger and sparse.
--- arch/arm64/include/asm/kvm_host.h | 78 +++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 24 deletions(-)
arch/arm64/include/asm/kvm_host.h +54 -24
@@ -25,6 +25,7 @@#include <asm/kvm.h> #include <asm/kvm_asm.h> #include <asm/thread_info.h> +#include <asm/vncr_mapping.h> #define __KVM_HAVE_ARCH_INTC_INITIALIZED
@@ -154,6 +155,23 @@ struct kvm_vcpu_fault_info {u64 disr_el1;/* Deferred [SError] Status Register */ }; +/* +* VNCR() just places the VNCR_capable registers in the enum after +* __VNCR_START__, and the value (after correction) to be an 8-byte offset +* from the VNCR base. As we don't require the enum to be otherwise ordered, +* we need the terrible hack below to ensure that we correctly size the +* sys_regs array, no matter what. +* +* The __MAX__ macro has been lifted from Sean Eron Anderson's wonderful +* treasure trove of bit hacks: +* https://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax +*/ +#define __MAX__ (x,y) ((x) ^ (((x) ^ (y)) & -((x) < (y)))) +#define VNCR (r) \ + __before_##r, \ + r = __VNCR_START__ + ((VNCR_ ## r) / 8), \ + __after_##r = __MAX__(__before_##r - 1, r) +/* * 0 is reserved as an invalid value. * Order should be kept in sync with the save/restore code.
@@ -162,27 +180,11 @@ enum vcpu_sysreg { __INVALID_SYSREG__,MPIDR_EL1 ,/* MultiProcessor Affinity Register */ CSSELR_EL1 ,/* Cache Size Selection Register */ -SCTLR_EL1 ,/* System Control Register */ -ACTLR_EL1 ,/* Auxiliary Control Register */ -CPACR_EL1 ,/* Coprocessor Access Control */ -ZCR_EL1 ,/* SVE Control */ -TTBR0_EL1 ,/* Translation Table Base Register 0 */ -TTBR1_EL1 ,/* Translation Table Base Register 1 */ -TCR_EL1 ,/* Translation Control Register */ -ESR_EL1 ,/* Exception Syndrome Register */ -AFSR0_EL1 ,/* Auxiliary Fault Status Register 0 */ -AFSR1_EL1 ,/* Auxiliary Fault Status Register 1 */ -FAR_EL1 ,/* Fault Address Register */ -MAIR_EL1 ,/* Memory Attribute Indirection Register */ -VBAR_EL1 ,/* Vector Base Address Register */ -CONTEXTIDR_EL1 ,/* Context ID Register */ TPIDR_EL0 ,/* Thread ID, User R/W */ TPIDRRO_EL0 ,/* Thread ID, User R/O */ TPIDR_EL1 ,/* Thread ID, Privileged */ -AMAIR_EL1 ,/* Aux Memory Attribute Indirection Register */ CNTKCTL_EL1 ,/* Timer Control Register (EL1) */ PAR_EL1 ,/* Physical Address Register */ -MDSCR_EL1 ,/* Monitor Debug System Control Register */ MDCCINT_EL1 ,/* Monitor Debug Comms Channel Interrupt Enable Reg */ DISR_EL1, /* Deferred Interrupt Status Register */
@@ -220,20 +222,14 @@ enum vcpu_sysreg {DBGVCR32_EL2 ,/* Debug Vector Catch Register */ /* EL2 registers sorted ascending by Op0, Op1, CRn, CRm, Op2 */ -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) */
@@ -247,10 +243,35 @@ enum vcpu_sysreg {RVBAR_EL2 ,/* Reset Vector Base Address Register */ RMR_EL2 ,/* Reset Management 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 */ + __VNCR_START__,/* Any VNCR-capable reg goes after this point */ + +VNCR (SCTLR_EL1 ),/* System Control Register */ +VNCR (ACTLR_EL1 ),/* Auxiliary Control Register */ +VNCR (CPACR_EL1 ),/* Coprocessor Access Control */ +VNCR (ZCR_EL1 ),/* SVE Control */ +VNCR (TTBR0_EL1 ),/* Translation Table Base Register 0 */ +VNCR (TTBR1_EL1 ),/* Translation Table Base Register 1 */ +VNCR (TCR_EL1 ),/* Translation Control Register */ +VNCR (ESR_EL1 ),/* Exception Syndrome Register */ +VNCR (AFSR0_EL1 ),/* Auxiliary Fault Status Register 0 */ +VNCR (AFSR1_EL1 ),/* Auxiliary Fault Status Register 1 */ +VNCR (FAR_EL1 ),/* Fault Address Register */ +VNCR (MAIR_EL1 ),/* Memory Attribute Indirection Register */ +VNCR (VBAR_EL1 ),/* Vector Base Address Register */ +VNCR (CONTEXTIDR_EL1 ),/* Context ID Register */ +VNCR (AMAIR_EL1 ),/* Aux Memory Attribute Indirection Register */ +VNCR (MDSCR_EL1 ),/* Monitor Debug System Control Register */ +VNCR (VPIDR_EL2 ),/* Virtualization Processor ID Register */ +VNCR (VMPIDR_EL2 ),/* Virtualization Multiprocessor ID Register */ +VNCR (HCR_EL2 ),/* Hypervisor Configuration Register */ +VNCR (HSTR_EL2 ),/* Hypervisor System Trap Register */ +VNCR (VTTBR_EL2 ),/* Virtualization Translation Table Base Register */ +VNCR (VTCR_EL2 ),/* Virtualization Translation Control Register */ +VNCR (TPIDR_EL2 ),/* EL2 Software Thread ID Register */ +NR_SYS_REGS /* Nothing after this line! */ };
@@ -303,6 +324,9 @@ struct kvm_cpu_context { };struct kvm_vcpu *__hyp_running_vcpu; + +/* This pointer has to be 4kB aligned. */ +u64 *vncr_array; };struct kvm_pmu_events {
@@ -474,7 +498,13 @@ struct kvm_vcpu_arch {* for system registers that are never context switched, but only * emulated. */ -#define __ctxt_sys_reg (c,r) (&(c)->sys_regs[(r)]) +static inline u64 *__ctxt_sys_reg (const struct kvm_cpu_context *ctxt,int r) +{ +if (unlikely (r >= __VNCR_START__ && ctxt->vncr_array)) +return &ctxt->vncr_array[r - __VNCR_START__]; + +return (u64 *)&ctxt->sys_regs[r]; +}#define ctxt_sys_reg (c,r) (*__ctxt_sys_reg(c,r))
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 79/94 KVM: arm64: nv: Move nested vgic state into the sysreg file
In reply to Marc ZyngierThe vgic nested state needs to be accessible from the VNCR page, and thus needs to be part of the normal sysreg file. Let's move it there.
--- arch/arm64/include/asm/kvm_host.h | 9 +++ arch/arm64/kvm/sys_regs.c | 53 ++++++++++++------ include/kvm/arm_vgic.h | 7 --- virt/kvm/arm/vgic/vgic-v3-nested.c | 88 +++++++++++++++--------------- virt/kvm/arm/vgic/vgic-v3.c | 18 ++++-- virt/kvm/arm/vgic/vgic.h | 10 ++++ 6 files changed, 111 insertions(+), 74 deletions(-)
arch/arm64/include/asm/kvm_host.h +9 -0
@@ -272,6 +272,15 @@ enum vcpu_sysreg {VNCR (VTCR_EL2 ),/* Virtualization Translation Control Register */ VNCR (TPIDR_EL2 ),/* EL2 Software Thread ID Register */ +VNCR (ICH_LR0_EL2 ), +ICH_LR15_EL2 =ICH_LR0_EL2 +15 , +VNCR (ICH_AP0R0_EL2 ), +ICH_AP0R3_EL2 =ICH_AP0R0_EL2 +3 , +VNCR (ICH_AP1R0_EL2 ), +ICH_AP1R3_EL2 =ICH_AP1R0_EL2 +3 , +VNCR (ICH_HCR_EL2 ), +VNCR (ICH_VMCR_EL2 ), +NR_SYS_REGS /* Nothing after this line! */ };
arch/arm64/kvm/sys_regs.c +35 -18
93 lines not on this page · raw message
include/kvm/arm_vgic.h +0 -7
@@ -305,9 +305,6 @@ struct vgic_cpu {struct vgic_irq private_irqs[VGIC_NR_PRIVATE_IRQS ]; -/* CPU vif control registers for the virtual GICH interface */ -struct vgic_v3_cpu_if nested_vgic_v3; -/* * The shadow vif control register loaded to the hardware when * running a nested L2 guest with the virtual IMO/FMO bit set.
@@ -370,10 +367,6 @@ void kvm_vgic_load(struct kvm_vcpu *vcpu);void kvm_vgic_put (struct kvm_vcpu *vcpu);void kvm_vgic_vmcr_sync (struct kvm_vcpu *vcpu); -void vgic_v3_sync_nested (struct kvm_vcpu *vcpu); -void vgic_v3_load_nested (struct kvm_vcpu *vcpu); -void vgic_v3_put_nested (struct kvm_vcpu *vcpu); -void vgic_v3_handle_nested_maint_irq (struct kvm_vcpu *vcpu);u16 vgic_v3_get_eisr (struct kvm_vcpu *vcpu);u16 vgic_v3_get_elrsr (struct kvm_vcpu *vcpu);u64 vgic_v3_get_misr (struct kvm_vcpu *vcpu);
virt/kvm/arm/vgic/vgic-v3-nested.c +45 -43
190 lines not on this page · raw message
virt/kvm/arm/vgic/vgic-v3.c +12 -6
@@ -292,10 +292,11 @@ void vgic_v3_enable(struct kvm_vcpu *vcpu) ICC_SRE_EL1_SRE);/* * If nesting is allowed, force GICv3 onto the nested -* guests as well. +* guests as well by setting the shadow state to the +* same value. */ if (nested_virt_in_use (vcpu)) - vcpu->arch.vgic_cpu.nested_vgic_v3.vgic_sre = vgic_v3->vgic_sre; + vcpu->arch.vgic_cpu.shadow_vgic_v3.vgic_sre = vgic_v3->vgic_sre; vcpu->arch.vgic_cpu.pendbaser =INITIAL_PENDBASER_VALUE ; }else { vgic_v3->vgic_sre =0 ;
@@ -660,11 +661,15 @@ void vgic_v3_load(struct kvm_vcpu *vcpu)struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;/* -* vgic_v3_load_nested only affects the LRs in the shadow -* state, so it is fine to pass the nested state around. +* If the vgic is in nested state, populate the shadow state +* from the guest's nested state. As vgic_v3_load_nested() +* will only load LRs, let's deal with the rest of the state +* here as if it was a non-nested state. Cunning. */ -if (vgic_state_is_nested (vcpu)) - cpu_if = &vcpu->arch.vgic_cpu.nested_vgic_v3; +if (vgic_state_is_nested (vcpu)) { +vgic_v3_create_shadow_state (vcpu); + cpu_if = &vcpu->arch.vgic_cpu.shadow_vgic_v3; + }/* * If dealing with a GICv2 emulation on GICv3, VMCR_EL2.VFIQen
@@ -718,5 +723,6 @@ void vgic_v3_put(struct kvm_vcpu *vcpu)__weak void vgic_v3_sync_nested (struct kvm_vcpu *vcpu) {}__weak void vgic_v3_handle_nested_maint_irq (struct kvm_vcpu *vcpu) {} +__weak void vgic_v3_create_shadow_state (struct kvm_vcpu *vcpu) {}__weak void vgic_v3_load_nested (struct kvm_vcpu *vcpu) {}__weak void vgic_v3_put_nested (struct kvm_vcpu *vcpu) {}
virt/kvm/arm/vgic/vgic.h +10 -0
@@ -317,4 +317,14 @@ bool vgic_supports_direct_msis(struct kvm *kvm);int vgic_v4_init (struct kvm *kvm);void vgic_v4_teardown (struct kvm *kvm); +void vgic_v3_sync_nested (struct kvm_vcpu *vcpu); +void vgic_v3_create_shadow_state (struct kvm_vcpu *vcpu); +void vgic_v3_load_nested (struct kvm_vcpu *vcpu); +void vgic_v3_put_nested (struct kvm_vcpu *vcpu); +void vgic_v3_handle_nested_maint_irq (struct kvm_vcpu *vcpu); + +#define ICH_LRN (n) (ICH_LR0_EL2 + (n)) +#define ICH_AP0RN (n) (ICH_AP0R0_EL2 + (n)) +#define ICH_AP1RN (n) (ICH_AP1R0_EL2 + (n)) +#endif
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 80/94 KVM: arm64: Use accessors for timer ctl/cval/offset
In reply to Marc ZyngierInstead of directly accessing the various fields in the timer data structures, add accessors that provide the same service. By making the weak, we'll be able to override them in a subsequent patch, making it possible to move them in the VNCR page, as they will be directly accessed by the guest.
--- virt/kvm/arm/arch_timer.c | 104 +++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 36 deletions(-)
virt/kvm/arm/arch_timer.c +68 -36
@@ -62,6 +62,36 @@ static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,struct arch_timer_context *timer, enum kvm_arch_timer_regs treg); +__weak u32 timer_get_ctl (struct arch_timer_context *ctxt) +{ +return ctxt->cnt_ctl; +} + +__weak u64 timer_get_cval (struct arch_timer_context *ctxt) +{ +return ctxt->cnt_cval; +} + +__weak u64 timer_get_offset (struct arch_timer_context *ctxt) +{ +return ctxt->cntvoff; +} + +__weak void timer_set_ctl (struct arch_timer_context *ctxt,u32 ctl) +{ + ctxt->cnt_ctl = ctl; +} + +__weak void timer_set_cval (struct arch_timer_context *ctxt,u64 cval) +{ + ctxt->cnt_cval = cval; +} + +__weak void timer_set_offset (struct arch_timer_context *ctxt,u64 offset) +{ + ctxt->cntvoff = offset; +} +u64 kvm_phys_timer_read (void ) {return timecounter->cc->read (timecounter->cc);
@@ -149,8 +179,8 @@ static u64 kvm_timer_compute_delta(struct arch_timer_context *timer_ctx) {u64 cval, now; - cval = timer_ctx->cnt_cval; - now =kvm_phys_timer_read () - timer_ctx->cntvoff; + cval =timer_get_cval (timer_ctx); + now =kvm_phys_timer_read () -timer_get_offset (timer_ctx);if (now < cval) {u64 ns;
@@ -169,8 +199,8 @@ static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx) {WARN_ON (timer_ctx && timer_ctx->loaded);return timer_ctx && - !(timer_ctx->cnt_ctl &ARCH_TIMER_CTRL_IT_MASK ) && - (timer_ctx->cnt_ctl &ARCH_TIMER_CTRL_ENABLE ); + ((timer_get_ctl (timer_ctx) & + (ARCH_TIMER_CTRL_IT_MASK |ARCH_TIMER_CTRL_ENABLE )) ==ARCH_TIMER_CTRL_ENABLE ); }/*
@@ -283,8 +313,8 @@ static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx)if (!kvm_timer_irq_can_fire (timer_ctx))return false; - cval = timer_ctx->cnt_cval; - now =kvm_phys_timer_read () - timer_ctx->cntvoff; + cval =timer_get_cval (timer_ctx); + now =kvm_phys_timer_read () -timer_get_offset (timer_ctx);return cval <= now; }
@@ -379,8 +409,8 @@ static void timer_save_state(struct arch_timer_context *ctx)switch (index) {case TIMER_VTIMER :case TIMER_HVTIMER : - ctx->cnt_ctl =read_sysreg_el0 (SYS_CNTV_CTL ); - ctx->cnt_cval =read_sysreg_el0 (SYS_CNTV_CVAL ); +timer_set_ctl (ctx,read_sysreg_el0 (SYS_CNTV_CTL )); +timer_set_cval (ctx,read_sysreg_el0 (SYS_CNTV_CVAL ));/* Disable the timer */ write_sysreg_el0 (0 ,SYS_CNTV_CTL );
@@ -389,8 +419,8 @@ static void timer_save_state(struct arch_timer_context *ctx)break ;case TIMER_PTIMER :case TIMER_HPTIMER : - ctx->cnt_ctl =read_sysreg_el0 (SYS_CNTP_CTL ); - ctx->cnt_cval =read_sysreg_el0 (SYS_CNTP_CVAL ); +timer_set_ctl (ctx,read_sysreg_el0 (SYS_CNTP_CTL )); +timer_set_cval (ctx,read_sysreg_el0 (SYS_CNTP_CVAL ));/* Disable the timer */ write_sysreg_el0 (0 ,SYS_CNTP_CTL );
@@ -461,15 +491,15 @@ static void timer_restore_state(struct arch_timer_context *ctx)switch (index) {case TIMER_VTIMER :case TIMER_HVTIMER : -write_sysreg_el0 (ctx->cnt_cval,SYS_CNTV_CVAL ); +write_sysreg_el0 (timer_get_cval (ctx),SYS_CNTV_CVAL );isb (); -write_sysreg_el0 (ctx->cnt_ctl,SYS_CNTV_CTL ); +write_sysreg_el0 (timer_get_ctl (ctx),SYS_CNTV_CTL );break ;case TIMER_PTIMER :case TIMER_HPTIMER : -write_sysreg_el0 (ctx->cnt_cval,SYS_CNTP_CVAL ); +write_sysreg_el0 (timer_get_cval (ctx),SYS_CNTP_CVAL );isb (); -write_sysreg_el0 (ctx->cnt_ctl,SYS_CNTP_CTL ); +write_sysreg_el0 (timer_get_ctl (ctx),SYS_CNTP_CTL );break ;case NR_KVM_TIMERS :BUG ();
@@ -608,7 +638,7 @@ void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu)kvm_timer_vcpu_load_nogic (vcpu); } -set_cntvoff (map.direct_vtimer->cntvoff); +set_cntvoff (timer_get_offset (map.direct_vtimer));kvm_timer_unblocking (vcpu);
@@ -722,10 +752,10 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu)* resets the timer to be disabled and unmasked and is compliant with * the ARMv7 architecture. */ -vcpu_vtimer (vcpu)->cnt_ctl =0 ; -vcpu_ptimer (vcpu)->cnt_ctl =0 ; -vcpu_hvtimer (vcpu)->cnt_ctl =0 ; -vcpu_hptimer (vcpu)->cnt_ctl =0 ; +timer_set_ctl (vcpu_vtimer (vcpu),0 ); +timer_set_ctl (vcpu_ptimer (vcpu),0 ); +timer_set_ctl (vcpu_hvtimer (vcpu),0 ); +timer_set_ctl (vcpu_hptimer (vcpu),0 );if (timer->enabled) {kvm_timer_update_irq (vcpu, false,vcpu_vtimer (vcpu));
@@ -757,13 +787,13 @@ static void update_vtimer_cntvoff(struct kvm_vcpu *vcpu, u64 cntvoff)mutex_lock (&kvm->lock);kvm_for_each_vcpu (i, tmp, kvm) -vcpu_vtimer (tmp)->cntvoff = cntvoff; +timer_set_offset (vcpu_vtimer (tmp), cntvoff);/* * When called from the vcpu create path, the CPU being created is not * included in the loop above, so we just set it here as well. */ -vcpu_vtimer (vcpu)->cntvoff = cntvoff; +timer_set_offset (vcpu_vtimer (vcpu), cntvoff);mutex_unlock (&kvm->lock); }
@@ -777,9 +807,9 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)/* Synchronize cntvoff across all vtimers of a VM. */ update_vtimer_cntvoff (vcpu,kvm_phys_timer_read ()); - ptimer->cntvoff =0 ; - hvtimer->cntvoff =0 ; - hptimer->cntvoff =0 ; +timer_set_offset (ptimer,0 ); +timer_set_offset (hvtimer,0 ); +timer_set_offset (hptimer,0 );hrtimer_init (&timer->bg_timer,CLOCK_MONOTONIC ,HRTIMER_MODE_ABS_HARD ); timer->bg_timer.function = kvm_bg_timer_expire;
@@ -862,10 +892,12 @@ static u64 read_timer_ctl(struct arch_timer_context *timer)* UNKNOWN when ENABLE bit is 0, so we chose to set ISTATUS bit * regardless of ENABLE bit for our implementation convenience. */ +u32 ctl =timer_get_ctl (timer); +if (!kvm_timer_compute_delta (timer)) -return timer->cnt_ctl |ARCH_TIMER_CTRL_IT_STAT ; -else -return timer->cnt_ctl; + ctl |=ARCH_TIMER_CTRL_IT_STAT ; + +return ctl; }u64 kvm_arm_timer_get_reg (struct kvm_vcpu *vcpu,u64 regid)
@@ -901,8 +933,8 @@ static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,switch (treg) {case TIMER_REG_TVAL : - val = timer->cnt_cval -kvm_phys_timer_read () + timer->cntvoff; - val &=lower_32_bits (val); + val =timer_get_cval (timer) -kvm_phys_timer_read () +timer_get_offset (timer); + val =lower_32_bits (val);break ;case TIMER_REG_CTL :
@@ -910,15 +942,15 @@ static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,break ;case TIMER_REG_CVAL : - val = timer->cnt_cval; + val =timer_get_cval (timer);break ;case TIMER_REG_CNT : - val =kvm_phys_timer_read () - timer->cntvoff; + val =kvm_phys_timer_read () -timer_get_offset (timer);break ;case TIMER_REG_VOFF : - val = timer->cntvoff; + val =timer_get_offset (timer);break ;default :
@@ -952,19 +984,19 @@ static void kvm_arm_timer_write(struct kvm_vcpu *vcpu, {switch (treg) {case TIMER_REG_TVAL : - timer->cnt_cval =kvm_phys_timer_read () - timer->cntvoff + (s32 )val; +timer_set_cval (timer,kvm_phys_timer_read () -timer_get_offset (timer) + (s32 )val);break ;case TIMER_REG_CTL : - timer->cnt_ctl = val & ~ARCH_TIMER_CTRL_IT_STAT ; +timer_set_ctl (timer, val & ~ARCH_TIMER_CTRL_IT_STAT );break ;case TIMER_REG_CVAL : - timer->cnt_cval = val; +timer_set_cval (timer, val);break ;case TIMER_REG_VOFF : - timer->cntvoff = val; +timer_set_offset (timer, val);break ;default :
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 81/94 KVM: arm64: Add VNCR-capable timer accessors for arm64
In reply to Marc ZyngierAs promissed, we now add an arm64-specific set of timer accessors, which in some cases access the VNCR page instead of the struct timer fields.
This requires moving the initialisation of the timer struct so that some of the helpers (such as arch_timer_ctx_index) can work correctly at an early stage.
--- arch/arm64/include/asm/kvm_host.h | 6 ++ arch/arm64/kvm/Makefile | 1 + virt/kvm/arm/arch_timer.c | 10 ++-- virt/kvm/arm/arch_timer_nested.c | 95 +++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 virt/kvm/arm/arch_timer_nested.c
arch/arm64/include/asm/kvm_host.h +6 -0
@@ -281,6 +281,12 @@ enum vcpu_sysreg {VNCR (ICH_HCR_EL2 ),VNCR (ICH_VMCR_EL2 ), +VNCR (CNTVOFF_EL2 ), +VNCR (CNTV_CVAL_EL0 ), +VNCR (CNTV_CTL_EL0 ), +VNCR (CNTP_CVAL_EL0 ), +VNCR (CNTP_CTL_EL0 ), + NR_SYS_REGS /* Nothing after this line! */ };
arch/arm64/kvm/Makefile +1 -0
@@ -40,4 +40,5 @@ kvm-$(CONFIG_KVM_ARM_PMU) += $(KVM)/arm/pmu.o kvm-$(CONFIG_KVM_ARM_HOST) += nested.o kvm-$(CONFIG_KVM_ARM_HOST) += emulate-nested.o +kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/arch_timer_nested.o kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-v3-nested.o
virt/kvm/arm/arch_timer.c +5 -5
@@ -805,6 +805,11 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)struct arch_timer_context *hvtimer =vcpu_hvtimer (vcpu);struct arch_timer_context *hptimer =vcpu_hptimer (vcpu); + vtimer->vcpu = vcpu; + ptimer->vcpu = vcpu; + hvtimer->vcpu = vcpu; + hptimer->vcpu = vcpu; +/* Synchronize cntvoff across all vtimers of a VM. */ update_vtimer_cntvoff (vcpu,kvm_phys_timer_read ());timer_set_offset (ptimer,0 );
@@ -838,11 +843,6 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) ptimer->host_timer_irq_flags = host_ptimer_irq_flags; hvtimer->host_timer_irq_flags = host_vtimer_irq_flags; hptimer->host_timer_irq_flags = host_ptimer_irq_flags; - - vtimer->vcpu = vcpu; - ptimer->vcpu = vcpu; - hvtimer->vcpu = vcpu; - hptimer->vcpu = vcpu; }static void kvm_timer_init_interrupt (void *info)
virt/kvm/arm/arch_timer_nested.c new file +95 -0
95 lines not on this page · raw message
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 82/94 KVM: arm64: Make struct kvm_regs userspace-only
In reply to Marc Zyngierstruct kvm_regs is used by userspace to indicate which register gets accessed by the {GET,SET}_ONE_REG API. But as we're about to refactor the layout of the in-kernel register structures, we need the kernel to move away from it.
Let's make kvm_regs userspace only, and let the kernel map it to its own internal representation.
--- arch/arm64/include/asm/kvm_emulate.h | 20 ++++---- arch/arm64/include/asm/kvm_host.h | 12 ++++- arch/arm64/kernel/asm-offsets.c | 3 +- arch/arm64/kvm/fpsimd.c | 2 +- arch/arm64/kvm/guest.c | 70 +++++++++++++++++++++++++--- arch/arm64/kvm/hyp/entry.S | 3 +- arch/arm64/kvm/hyp/switch.c | 4 +- arch/arm64/kvm/hyp/sysreg-sr.c | 30 ++++++------ arch/arm64/kvm/regmap.c | 6 +-- arch/arm64/kvm/reset.c | 2 +- arch/arm64/kvm/sys_regs.c | 12 ++--- 11 files changed, 113 insertions(+), 51 deletions(-)
arch/arm64/include/asm/kvm_emulate.h +10 -10
@@ -159,12 +159,12 @@ static inline void vcpu_set_vsesr(struct kvm_vcpu *vcpu, u64 vsesr)static inline unsigned long *vcpu_pc (const struct kvm_vcpu *vcpu) { -return (unsigned long *)&vcpu_gp_regs (vcpu)->regs.pc; +return (unsigned long *)&vcpu_gp_regs (vcpu)->pc; }static inline unsigned long *__vcpu_elr_el1 (const struct kvm_vcpu *vcpu) { -return (unsigned long *)&vcpu_gp_regs (vcpu)->elr_el1; +return (unsigned long *)&vcpu->arch.ctxt.elr_el1; }static inline unsigned long vcpu_read_elr_el1 (const struct kvm_vcpu *vcpu)
@@ -185,7 +185,7 @@ static inline void vcpu_write_elr_el1(const struct kvm_vcpu *vcpu, unsigned longstatic inline unsigned long *vcpu_cpsr (const struct kvm_vcpu *vcpu) { -return (unsigned long *)&vcpu_gp_regs (vcpu)->regs.pstate; +return (unsigned long *)&vcpu_gp_regs (vcpu)->pstate; }static inline bool vcpu_mode_is_32bit (const struct kvm_vcpu *vcpu)
@@ -214,19 +214,19 @@ static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu)static inline unsigned long vcpu_get_reg (const struct kvm_vcpu *vcpu,u8 reg_num) { -return (reg_num ==31 ) ?0 :vcpu_gp_regs (vcpu)->regs.regs[reg_num]; +return (reg_num ==31 ) ?0 :vcpu_gp_regs (vcpu)->regs[reg_num]; }static inline void vcpu_set_reg (struct kvm_vcpu *vcpu,u8 reg_num,unsigned long val) {if (reg_num !=31 ) -vcpu_gp_regs (vcpu)->regs.regs[reg_num] = val; +vcpu_gp_regs (vcpu)->regs[reg_num] = val; }static inline bool vcpu_mode_el2_ctxt (const struct kvm_cpu_context *ctxt) { -unsigned long cpsr = ctxt->gp_regs.regs.pstate; +unsigned long cpsr = ctxt->regs.pstate;switch (cpsr & (PSR_MODE32_BIT |PSR_MODE_MASK )) {case PSR_MODE_EL2h:
@@ -327,7 +327,7 @@ static inline unsigned long vcpu_read_spsr(const struct kvm_vcpu *vcpu)if (vcpu->arch.sysregs_loaded_on_cpu)return read_sysreg_el1 (SYS_SPSR );else -return vcpu_gp_regs (vcpu)->spsr[KVM_SPSR_EL1 ]; +return vcpu->arch.ctxt.spsr[KVM_SPSR_EL1 ]; }static inline void vcpu_write_spsr (struct kvm_vcpu *vcpu,unsigned long v)
@@ -345,7 +345,7 @@ static inline void vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long v)if (vcpu->arch.sysregs_loaded_on_cpu)write_sysreg_el1 (v,SYS_SPSR );else -vcpu_gp_regs (vcpu)->spsr[KVM_SPSR_EL1 ] = v; + vcpu->arch.ctxt.spsr[KVM_SPSR_EL1 ] = v; }/*
@@ -676,11 +676,11 @@ static inline void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr) static inline void __hyp_text __kvm_skip_instr(struct kvm_vcpu *vcpu) { *vcpu_pc (vcpu) =read_sysreg_el2 (SYS_ELR ); - vcpu->arch.ctxt.gp_regs.regs.pstate =read_sysreg_el2 (SYS_SPSR ); +vcpu_gp_regs (vcpu)->pstate =read_sysreg_el2 (SYS_SPSR );kvm_skip_instr (vcpu,kvm_vcpu_trap_il_is32bit (vcpu)); -write_sysreg_el2 (vcpu->arch.ctxt.gp_regs.regs.pstate,SYS_SPSR ); +write_sysreg_el2 (vcpu_gp_regs (vcpu)->pstate,SYS_SPSR );write_sysreg_el2 (*vcpu_pc (vcpu),SYS_ELR ); }
arch/arm64/include/asm/kvm_host.h +10 -2
@@ -332,7 +332,15 @@ enum vcpu_sysreg {#define NR_COPRO_REGS (NR_SYS_REGS * 2)struct kvm_cpu_context { -struct kvm_regs gp_regs; +struct user_pt_regs regs;/* sp = sp_el0 */ + +u64 sp_el1; +u64 elr_el1; + +u64 spsr[KVM_NR_SPSR ]; + +struct user_fpsimd_state fp_regs; +union {u64 sys_regs[NR_SYS_REGS ];u32 copro[NR_COPRO_REGS ];
@@ -504,7 +512,7 @@ struct kvm_vcpu_arch { system_supports_generic_auth()) && \ ((vcpu)->arch.flags & KVM_ARM64_GUEST_HAS_PTRAUTH)) -#define vcpu_gp_regs (v) (&(v)->arch.ctxt.gp_regs) +#define vcpu_gp_regs (v) (&(v)->arch.ctxt.regs)/* * Only use __vcpu_sys_reg/ctxt_sys_reg if you know you want the
arch/arm64/kernel/asm-offsets.c +1 -2
@@ -94,13 +94,12 @@ int main(void) DEFINE(VCPU_FAULT_DISR, offsetof(struct kvm_vcpu, arch.fault.disr_el1)); DEFINE(VCPU_WORKAROUND_FLAGS, offsetof(struct kvm_vcpu, arch.workaround_flags)); DEFINE(VCPU_HCR_EL2, offsetof(struct kvm_vcpu, arch.hcr_el2)); -DEFINE (CPU_GP_REGS , offsetof(struct kvm_cpu_context , gp_regs)); +DEFINE (CPU_USER_PT_REGS , offsetof(struct kvm_cpu_context , regs)); DEFINE(CPU_APIAKEYLO_EL1, offsetof(struct kvm_cpu_context, sys_regs[APIAKEYLO_EL1])); DEFINE(CPU_APIBKEYLO_EL1, offsetof(struct kvm_cpu_context, sys_regs[APIBKEYLO_EL1])); DEFINE(CPU_APDAKEYLO_EL1, offsetof(struct kvm_cpu_context, sys_regs[APDAKEYLO_EL1])); DEFINE(CPU_APDBKEYLO_EL1, offsetof(struct kvm_cpu_context, sys_regs[APDBKEYLO_EL1])); DEFINE(CPU_APGAKEYLO_EL1, offsetof(struct kvm_cpu_context, sys_regs[APGAKEYLO_EL1])); -DEFINE (CPU_USER_PT_REGS , offsetof(struct kvm_regs , regs));DEFINE (HOST_CONTEXT_VCPU , offsetof(struct kvm_cpu_context , __hyp_running_vcpu));DEFINE (HOST_DATA_CONTEXT , offsetof(struct kvm_host_data , host_ctxt));#endif
arch/arm64/kvm/fpsimd.c +1 -1
@@ -86,7 +86,7 @@ void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu)WARN_ON_ONCE (!irqs_disabled ());if (vcpu->arch.flags &KVM_ARM64_FP_ENABLED ) { -fpsimd_bind_state_to_cpu (&vcpu->arch.ctxt.gp_regs.fp_regs, +fpsimd_bind_state_to_cpu (&vcpu->arch.ctxt.fp_regs, vcpu->arch.sve_state, vcpu->arch.sve_max_vl);
arch/arm64/kvm/guest.c +63 -7
107 lines not on this page · raw message
arch/arm64/kvm/hyp/entry.S +1 -2
@@ -16,8 +16,7 @@ #include <asm/kvm_mmu.h> #include <asm/kvm_ptrauth.h> -#define CPU_GP_REG_OFFSET(x) (CPU_GP_REGS + x) -#define CPU_XREG_OFFSET(x) CPU_GP_REG_OFFSET(CPU_USER_PT_REGS + 8*x) +#define CPU_XREG_OFFSET(x) (CPU_USER_PT_REGS + 8*x) .text .pushsection .hyp.text, "ax"
arch/arm64/kvm/hyp/switch.c +2 -2
@@ -461,11 +461,11 @@ static bool __hyp_text __hyp_handle_fpsimd(struct kvm_vcpu *vcpu)if (sve_guest) {sve_load_state (vcpu_sve_pffr (vcpu), - &vcpu->arch.ctxt.gp_regs.fp_regs.fpsr, + &vcpu->arch.ctxt.fp_regs.fpsr,sve_vq_from_vl (vcpu->arch.sve_max_vl) -1 );write_sysreg_s (__vcpu_sys_reg (vcpu,ZCR_EL1 ),SYS_ZCR_EL12 ); }else { -__fpsimd_restore_state (&vcpu->arch.ctxt.gp_regs.fp_regs); +__fpsimd_restore_state (&vcpu->arch.ctxt.fp_regs); }/* Skip restoring fpexc32 for AArch64 guests */
arch/arm64/kvm/hyp/sysreg-sr.c +15 -15
@@ -32,7 +32,7 @@ static void __hyp_text __sysreg_save_common_state(struct kvm_cpu_context *ctxt)* The host arm64 Linux uses sp_el0 to point to 'current' and it must * therefore be saved/restored on every entry/exit to/from the guest. */ - ctxt->gp_regs.regs.sp =read_sysreg (sp_el0); + ctxt->regs.sp =read_sysreg (sp_el0); } static void __hyp_text __sysreg_save_user_state(struct kvm_cpu_context *ctxt)
@@ -58,9 +58,9 @@ static void __hyp_text __sysreg_save_vel1_state(struct kvm_cpu_context *ctxt)ctxt_sys_reg (ctxt,AMAIR_EL1 ) =read_sysreg_el1 (SYS_AMAIR );ctxt_sys_reg (ctxt,CNTKCTL_EL1 ) =read_sysreg_el1 (SYS_CNTKCTL ); - ctxt->gp_regs.sp_el1 =read_sysreg (sp_el1); - ctxt->gp_regs.elr_el1 =read_sysreg_el1 (SYS_ELR ); - ctxt->gp_regs.spsr[KVM_SPSR_EL1 ]=read_sysreg_el1 (SYS_SPSR ); + ctxt->sp_el1 =read_sysreg (sp_el1); + ctxt->elr_el1 =read_sysreg_el1 (SYS_ELR ); + ctxt->spsr[KVM_SPSR_EL1 ] =read_sysreg_el1 (SYS_SPSR ); }static void __sysreg_save_vel2_state (struct kvm_cpu_context *ctxt)
@@ -133,8 +133,8 @@ static u64 __hyp_text from_hw_pstate(const struct kvm_cpu_context *ctxt) static void __hyp_text __sysreg_save_el2_return_state(struct kvm_cpu_context *ctxt) { - ctxt->gp_regs.regs.pc =read_sysreg_el2 (SYS_ELR ); - ctxt->gp_regs.regs.pstate =from_hw_pstate (ctxt); + ctxt->regs.pc =read_sysreg_el2 (SYS_ELR ); + ctxt->regs.pstate =from_hw_pstate (ctxt);if (cpus_have_const_cap (ARM64_HAS_RAS_EXTN ))ctxt_sys_reg (ctxt,DISR_EL1 ) =read_sysreg_s (SYS_VDISR_EL2 );
@@ -169,7 +169,7 @@ static void __hyp_text __sysreg_restore_common_state(struct kvm_cpu_context *ctx* The host arm64 Linux uses sp_el0 to point to 'current' and it must * therefore be saved/restored on every entry/exit to/from the guest. */ -write_sysreg (ctxt->gp_regs.regs.sp, sp_el0); +write_sysreg (ctxt->regs.sp, sp_el0); } static void __hyp_text __sysreg_restore_user_state(struct kvm_cpu_context *ctxt)
@@ -323,9 +323,9 @@ static void __hyp_text __sysreg_restore_vel1_state(struct kvm_cpu_context *ctxt)write_sysreg_el1 (ctxt_sys_reg (ctxt,TCR_EL1 ),SYS_TCR ); } -write_sysreg (ctxt->gp_regs.sp_el1, sp_el1); -write_sysreg_el1 (ctxt->gp_regs.elr_el1,SYS_ELR ); -write_sysreg_el1 (ctxt->gp_regs.spsr[KVM_SPSR_EL1 ],SYS_SPSR ); +write_sysreg (ctxt->sp_el1, sp_el1); +write_sysreg_el1 (ctxt->elr_el1,SYS_ELR ); +write_sysreg_el1 (ctxt->spsr[KVM_SPSR_EL1 ],SYS_SPSR ); } static void __hyp_text __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)
@@ -344,7 +344,7 @@ static void __hyp_text __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)/* Read the VCPU state's PSTATE, but translate (v)EL2 to EL1. */ static u64 __hyp_text to_hw_pstate(const struct kvm_cpu_context *ctxt) { -u64 mode = ctxt->gp_regs.regs.pstate & (PSR_MODE_MASK |PSR_MODE32_BIT ); +u64 mode = ctxt->regs.pstate & (PSR_MODE_MASK |PSR_MODE32_BIT );switch (mode) {case PSR_MODE_EL2t:
@@ -355,7 +355,7 @@ static u64 __hyp_text to_hw_pstate(const struct kvm_cpu_context *ctxt)break ; } -return (ctxt->gp_regs.regs.pstate & ~(PSR_MODE_MASK |PSR_MODE32_BIT )) | mode; +return (ctxt->regs.pstate & ~(PSR_MODE_MASK |PSR_MODE32_BIT )) | mode; }static void __hyp_text
@@ -378,7 +378,7 @@ __sysreg_restore_el2_return_state(struct kvm_cpu_context *ctxt)if (!(mode &PSR_MODE32_BIT ) && mode >= PSR_MODE_EL2t) pstate = PSR_MODE_EL2h |PSR_IL_BIT ; -write_sysreg_el2 (ctxt->gp_regs.regs.pc,SYS_ELR ); +write_sysreg_el2 (ctxt->regs.pc,SYS_ELR );write_sysreg_el2 (pstate,SYS_SPSR );if (cpus_have_const_cap (ARM64_HAS_RAS_EXTN ))
@@ -413,7 +413,7 @@ void __hyp_text __sysreg32_save_state(struct kvm_vcpu *vcpu)if (!vcpu_el1_is_32bit (vcpu))return ; - spsr = vcpu->arch.ctxt.gp_regs.spsr; + spsr = vcpu->arch.ctxt.spsr; sysreg = vcpu->arch.ctxt.sys_regs; spsr[KVM_SPSR_ABT ] =read_sysreg (spsr_abt);
@@ -435,7 +435,7 @@ void __hyp_text __sysreg32_restore_state(struct kvm_vcpu *vcpu)if (!vcpu_el1_is_32bit (vcpu))return ; - spsr = vcpu->arch.ctxt.gp_regs.spsr; + spsr = vcpu->arch.ctxt.spsr; sysreg = vcpu->arch.ctxt.sys_regs;write_sysreg (spsr[KVM_SPSR_ABT ], spsr_abt);
arch/arm64/kvm/regmap.c +3 -3
@@ -100,7 +100,7 @@ static const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][16] = {*/ unsigned long *vcpu_reg32 (const struct kvm_vcpu *vcpu,u8 reg_num) { -unsigned long *reg_array = (unsigned long *)&vcpu->arch.ctxt.gp_regs.regs; +unsigned long *reg_array = (unsigned long *)&vcpu->arch.ctxt.regs;unsigned long mode = *vcpu_cpsr (vcpu) &PSR_AA32_MODE_MASK ;switch (mode) {
@@ -148,7 +148,7 @@ unsigned long vcpu_read_spsr32(const struct kvm_vcpu *vcpu)int spsr_idx =vcpu_spsr32_mode (vcpu);if (!vcpu->arch.sysregs_loaded_on_cpu) -return vcpu_gp_regs (vcpu)->spsr[spsr_idx]; +return vcpu->arch.ctxt.spsr[spsr_idx];switch (spsr_idx) {case KVM_SPSR_SVC :
@@ -171,7 +171,7 @@ void vcpu_write_spsr32(struct kvm_vcpu *vcpu, unsigned long v)int spsr_idx =vcpu_spsr32_mode (vcpu);if (!vcpu->arch.sysregs_loaded_on_cpu) { -vcpu_gp_regs (vcpu)->spsr[spsr_idx] = v; + vcpu->arch.ctxt.spsr[spsr_idx] = v;return ; }
arch/arm64/kvm/reset.c +1 -1
@@ -307,7 +307,7 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)/* Reset core registers */ memset (vcpu_gp_regs (vcpu),0 ,sizeof (*cpu_reset)); -vcpu_gp_regs (vcpu)->regs.pstate = pstate; +vcpu_gp_regs (vcpu)->pstate = pstate;/* Reset system registers */ kvm_reset_sys_regs (vcpu);
arch/arm64/kvm/sys_regs.c +6 -6
@@ -1805,9 +1805,9 @@ static bool access_sp_el1(struct kvm_vcpu *vcpu, {/* SP_EL1 is NOT maintained in sys_regs array */ if (p->is_write) - vcpu->arch.ctxt.gp_regs.sp_el1 = p->regval; + vcpu->arch.ctxt.sp_el1 = p->regval;else - p->regval = vcpu->arch.ctxt.gp_regs.sp_el1; + p->regval = vcpu->arch.ctxt.sp_el1;return true; }
@@ -1833,9 +1833,9 @@ static bool access_elr(struct kvm_vcpu *vcpu,return false;if (p->is_write) - vcpu->arch.ctxt.gp_regs.elr_el1 = p->regval; + vcpu->arch.ctxt.elr_el1 = p->regval;else - p->regval = vcpu->arch.ctxt.gp_regs.elr_el1; + p->regval = vcpu->arch.ctxt.elr_el1;return true; }
@@ -1851,9 +1851,9 @@ static bool access_spsr(struct kvm_vcpu *vcpu,return false;if (p->is_write) - vcpu->arch.ctxt.gp_regs.spsr[KVM_SPSR_EL1 ] = p->regval; + vcpu->arch.ctxt.spsr[KVM_SPSR_EL1 ] = p->regval;else - p->regval = vcpu->arch.ctxt.gp_regs.spsr[KVM_SPSR_EL1 ]; + p->regval = vcpu->arch.ctxt.spsr[KVM_SPSR_EL1 ];return true; }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 83/94 KVM: arm64: VNCR-ize ELR_EL1
In reply to Marc ZyngierELR_EL1 being a VNCR-capable register, let's flag it as such, and repaint all the accesses
--- arch/arm64/include/asm/kvm_emulate.h | 2 +- arch/arm64/include/asm/kvm_host.h | 2 +- arch/arm64/kvm/guest.c | 2 +- arch/arm64/kvm/hyp/sysreg-sr.c | 4 ++-- arch/arm64/kvm/sys_regs.c | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-)
arch/arm64/include/asm/kvm_emulate.h +1 -1
@@ -164,7 +164,7 @@ static inline unsigned long *vcpu_pc(const struct kvm_vcpu *vcpu)static inline unsigned long *__vcpu_elr_el1 (const struct kvm_vcpu *vcpu) { -return (unsigned long *)&vcpu->arch.ctxt.elr_el1; +return (unsigned long *)__ctxt_sys_reg (&vcpu->arch.ctxt,ELR_EL1 ); }static inline unsigned long vcpu_read_elr_el1 (const struct kvm_vcpu *vcpu)
arch/arm64/include/asm/kvm_host.h +1 -1
@@ -264,6 +264,7 @@ enum vcpu_sysreg {VNCR (CONTEXTIDR_EL1 ),/* Context ID Register */ VNCR (AMAIR_EL1 ),/* Aux Memory Attribute Indirection Register */ VNCR (MDSCR_EL1 ),/* Monitor Debug System Control Register */ +VNCR (ELR_EL1 ),VNCR (VPIDR_EL2 ),/* Virtualization Processor ID Register */ VNCR (VMPIDR_EL2 ),/* Virtualization Multiprocessor ID Register */ VNCR(HCR_EL2), /* Hypervisor Configuration Register */
@@ -335,7 +336,6 @@ struct kvm_cpu_context {struct user_pt_regs regs;/* sp = sp_el0 */ u64 sp_el1; -u64 elr_el1;u64 spsr[KVM_NR_SPSR ];
arch/arm64/kvm/guest.c +1 -1
@@ -128,7 +128,7 @@ static void *core_reg_addr(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)return &vcpu->arch.ctxt.sp_el1;case KVM_REG_ARM_CORE_REG (elr_el1): -return &vcpu->arch.ctxt.elr_el1; +return __vcpu_elr_el1 (vcpu); case KVM_REG_ARM_CORE_REG(spsr[0]) ...KVM_REG_ARM_CORE_REG (spsr[KVM_NR_SPSR -1 ]):
arch/arm64/kvm/hyp/sysreg-sr.c +2 -2
@@ -59,7 +59,7 @@ static void __hyp_text __sysreg_save_vel1_state(struct kvm_cpu_context *ctxt)ctxt_sys_reg (ctxt,CNTKCTL_EL1 ) =read_sysreg_el1 (SYS_CNTKCTL ); ctxt->sp_el1 =read_sysreg (sp_el1); - ctxt->elr_el1 =read_sysreg_el1 (SYS_ELR ); +ctxt_sys_reg (ctxt,ELR_EL1 ) =read_sysreg_el1 (SYS_ELR ); ctxt->spsr[KVM_SPSR_EL1 ] =read_sysreg_el1 (SYS_SPSR ); }
@@ -324,7 +324,7 @@ static void __hyp_text __sysreg_restore_vel1_state(struct kvm_cpu_context *ctxt) }write_sysreg (ctxt->sp_el1, sp_el1); -write_sysreg_el1 (ctxt->elr_el1,SYS_ELR ); +write_sysreg_el1 (ctxt_sys_reg (ctxt,ELR_EL1 ),SYS_ELR );write_sysreg_el1 (ctxt->spsr[KVM_SPSR_EL1 ],SYS_SPSR ); }
arch/arm64/kvm/sys_regs.c +2 -2
@@ -1833,9 +1833,9 @@ static bool access_elr(struct kvm_vcpu *vcpu,return false;if (p->is_write) - vcpu->arch.ctxt.elr_el1 = p->regval; + *__vcpu_elr_el1 (vcpu) = p->regval;else - p->regval = vcpu->arch.ctxt.elr_el1; + p->regval = *__vcpu_elr_el1 (vcpu);return true; }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 84/94 KVM: arm64: VNCR-ize SP_EL1
In reply to Marc ZyngierSP_EL1 being a VNCR-capable register, let's flag it as such, and repaint all the accesses
--- arch/arm64/include/asm/kvm_host.h | 3 +-- arch/arm64/kvm/guest.c | 2 +- arch/arm64/kvm/hyp/sysreg-sr.c | 4 ++-- arch/arm64/kvm/sys_regs.c | 5 ++--- 4 files changed, 6 insertions(+), 8 deletions(-)
arch/arm64/include/asm/kvm_host.h +1 -2
@@ -265,6 +265,7 @@ enum vcpu_sysreg {VNCR (AMAIR_EL1 ),/* Aux Memory Attribute Indirection Register */ VNCR (MDSCR_EL1 ),/* Monitor Debug System Control Register */ VNCR (ELR_EL1 ), +VNCR (SP_EL1 ),VNCR (VPIDR_EL2 ),/* Virtualization Processor ID Register */ VNCR (VMPIDR_EL2 ),/* Virtualization Multiprocessor ID Register */ VNCR(HCR_EL2), /* Hypervisor Configuration Register */
@@ -335,8 +336,6 @@ enum vcpu_sysreg {struct kvm_cpu_context {struct user_pt_regs regs;/* sp = sp_el0 */ -u64 sp_el1; -u64 spsr[KVM_NR_SPSR ];struct user_fpsimd_state fp_regs;
arch/arm64/kvm/guest.c +1 -1
@@ -125,7 +125,7 @@ static void *core_reg_addr(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)return &vcpu->arch.ctxt.regs.pstate;case KVM_REG_ARM_CORE_REG (sp_el1): -return &vcpu->arch.ctxt.sp_el1; +return __ctxt_sys_reg (&vcpu->arch.ctxt,SP_EL1 );case KVM_REG_ARM_CORE_REG (elr_el1):return __vcpu_elr_el1 (vcpu);
arch/arm64/kvm/hyp/sysreg-sr.c +2 -2
@@ -58,7 +58,7 @@ static void __hyp_text __sysreg_save_vel1_state(struct kvm_cpu_context *ctxt)ctxt_sys_reg (ctxt,AMAIR_EL1 ) =read_sysreg_el1 (SYS_AMAIR );ctxt_sys_reg (ctxt,CNTKCTL_EL1 ) =read_sysreg_el1 (SYS_CNTKCTL ); - ctxt->sp_el1 =read_sysreg (sp_el1); +ctxt_sys_reg (ctxt,SP_EL1 ) =read_sysreg (sp_el1);ctxt_sys_reg (ctxt,ELR_EL1 ) =read_sysreg_el1 (SYS_ELR ); ctxt->spsr[KVM_SPSR_EL1 ] =read_sysreg_el1 (SYS_SPSR ); }
@@ -323,7 +323,7 @@ static void __hyp_text __sysreg_restore_vel1_state(struct kvm_cpu_context *ctxt)write_sysreg_el1 (ctxt_sys_reg (ctxt,TCR_EL1 ),SYS_TCR ); } -write_sysreg (ctxt->sp_el1, sp_el1); +write_sysreg (ctxt_sys_reg (ctxt,SP_EL1 ), sp_el1);write_sysreg_el1 (ctxt_sys_reg (ctxt,ELR_EL1 ),SYS_ELR );write_sysreg_el1 (ctxt->spsr[KVM_SPSR_EL1 ],SYS_SPSR ); }
arch/arm64/kvm/sys_regs.c +2 -3
@@ -1803,11 +1803,10 @@ static bool access_sp_el1(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) { -/* SP_EL1 is NOT maintained in sys_regs array */ if (p->is_write) - vcpu->arch.ctxt.sp_el1 = p->regval; +__vcpu_sys_reg (vcpu,SP_EL1 ) = p->regval;else - p->regval = vcpu->arch.ctxt.sp_el1; + p->regval =__vcpu_sys_reg (vcpu,SP_EL1 );return true; }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 85/94 KVM: arm64: Disintegrate SPSR array
In reply to Marc ZyngierAs we're about to move SPSR_EL1 into the VNCR page, we need to disassociate it from the rest of the 32bit cruft. Let's break the array into individual fields.
--- arch/arm64/include/asm/kvm_emulate.h | 4 ++-- arch/arm64/include/asm/kvm_host.h | 6 ++++- arch/arm64/kvm/guest.c | 19 +++++++++++---- arch/arm64/kvm/hyp/sysreg-sr.c | 26 ++++++++++----------- arch/arm64/kvm/regmap.c | 35 +++++++++++++++++++++++++--- arch/arm64/kvm/sys_regs.c | 4 ++-- 6 files changed, 67 insertions(+), 27 deletions(-)
arch/arm64/include/asm/kvm_emulate.h +2 -2
@@ -327,7 +327,7 @@ static inline unsigned long vcpu_read_spsr(const struct kvm_vcpu *vcpu)if (vcpu->arch.sysregs_loaded_on_cpu)return read_sysreg_el1 (SYS_SPSR );else -return vcpu->arch.ctxt.spsr[KVM_SPSR_EL1 ]; +return vcpu->arch.ctxt.spsr_el1; }static inline void vcpu_write_spsr (struct kvm_vcpu *vcpu,unsigned long v)
@@ -345,7 +345,7 @@ static inline void vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long v)if (vcpu->arch.sysregs_loaded_on_cpu)write_sysreg_el1 (v,SYS_SPSR );else - vcpu->arch.ctxt.spsr[KVM_SPSR_EL1 ] = v; + vcpu->arch.ctxt.spsr_el1 = v; }/*
arch/arm64/include/asm/kvm_host.h +5 -1
@@ -336,7 +336,11 @@ enum vcpu_sysreg {struct kvm_cpu_context {struct user_pt_regs regs;/* sp = sp_el0 */ -u64 spsr[KVM_NR_SPSR ]; +u64 spsr_el1;/* aka spsr_svc */ +u64 spsr_abt; +u64 spsr_und; +u64 spsr_irq; +u64 spsr_fiq;struct user_fpsimd_state fp_regs;
arch/arm64/kvm/guest.c +14 -5
@@ -130,11 +130,20 @@ static void *core_reg_addr(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)case KVM_REG_ARM_CORE_REG (elr_el1):return __vcpu_elr_el1 (vcpu); - case KVM_REG_ARM_CORE_REG(spsr[0]) ... -KVM_REG_ARM_CORE_REG (spsr[KVM_NR_SPSR -1 ]): - off -=KVM_REG_ARM_CORE_REG (spsr[0 ]); - off /=2 ; -return &vcpu->arch.ctxt.spsr[off]; +case KVM_REG_ARM_CORE_REG (spsr[KVM_SPSR_EL1 ]): +return &vcpu->arch.ctxt.spsr_el1; + +case KVM_REG_ARM_CORE_REG (spsr[KVM_SPSR_ABT ]): +return &vcpu->arch.ctxt.spsr_abt; + +case KVM_REG_ARM_CORE_REG (spsr[KVM_SPSR_UND ]): +return &vcpu->arch.ctxt.spsr_und; + +case KVM_REG_ARM_CORE_REG (spsr[KVM_SPSR_IRQ ]): +return &vcpu->arch.ctxt.spsr_irq; + +case KVM_REG_ARM_CORE_REG (spsr[KVM_SPSR_FIQ ]): +return &vcpu->arch.ctxt.spsr_fiq; case KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]) ...KVM_REG_ARM_CORE_REG (fp_regs.vregs[31 ]):
arch/arm64/kvm/hyp/sysreg-sr.c +12 -14
@@ -60,7 +60,7 @@ static void __hyp_text __sysreg_save_vel1_state(struct kvm_cpu_context *ctxt)ctxt_sys_reg (ctxt,SP_EL1 ) =read_sysreg (sp_el1);ctxt_sys_reg (ctxt,ELR_EL1 ) =read_sysreg_el1 (SYS_ELR ); - ctxt->spsr[KVM_SPSR_EL1 ] =read_sysreg_el1 (SYS_SPSR ); + ctxt->spsr_el1 =read_sysreg_el1 (SYS_SPSR ); }static void __sysreg_save_vel2_state (struct kvm_cpu_context *ctxt)
@@ -325,7 +325,7 @@ static void __hyp_text __sysreg_restore_vel1_state(struct kvm_cpu_context *ctxt)write_sysreg (ctxt_sys_reg (ctxt,SP_EL1 ), sp_el1);write_sysreg_el1 (ctxt_sys_reg (ctxt,ELR_EL1 ),SYS_ELR ); -write_sysreg_el1 (ctxt->spsr[KVM_SPSR_EL1 ],SYS_SPSR ); +write_sysreg_el1 (ctxt->spsr_el1,SYS_SPSR ); } static void __hyp_text __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)
@@ -408,18 +408,17 @@ NOKPROBE_SYMBOL(sysreg_restore_guest_state_vhe); void __hyp_text __sysreg32_save_state(struct kvm_vcpu *vcpu) { -u64 *spsr, *sysreg; +u64 *sysreg;if (!vcpu_el1_is_32bit (vcpu))return ; - spsr = vcpu->arch.ctxt.spsr; sysreg = vcpu->arch.ctxt.sys_regs; - spsr[KVM_SPSR_ABT ] =read_sysreg (spsr_abt); - spsr[KVM_SPSR_UND ] =read_sysreg (spsr_und); - spsr[KVM_SPSR_IRQ ] =read_sysreg (spsr_irq); - spsr[KVM_SPSR_FIQ ] =read_sysreg (spsr_fiq); + vcpu->arch.ctxt.spsr_abt =read_sysreg (spsr_abt); + vcpu->arch.ctxt.spsr_und =read_sysreg (spsr_und); + vcpu->arch.ctxt.spsr_irq =read_sysreg (spsr_irq); + vcpu->arch.ctxt.spsr_fiq =read_sysreg (spsr_fiq); sysreg[DACR32_EL2 ] =read_sysreg (dacr32_el2); sysreg[IFSR32_EL2 ] =read_sysreg (ifsr32_el2);
@@ -430,18 +429,17 @@ void __hyp_text __sysreg32_save_state(struct kvm_vcpu *vcpu) void __hyp_text __sysreg32_restore_state(struct kvm_vcpu *vcpu) { -u64 *spsr, *sysreg; +u64 *sysreg;if (!vcpu_el1_is_32bit (vcpu))return ; - spsr = vcpu->arch.ctxt.spsr; sysreg = vcpu->arch.ctxt.sys_regs; -write_sysreg (spsr[KVM_SPSR_ABT ], spsr_abt); -write_sysreg (spsr[KVM_SPSR_UND ], spsr_und); -write_sysreg (spsr[KVM_SPSR_IRQ ], spsr_irq); -write_sysreg (spsr[KVM_SPSR_FIQ ], spsr_fiq); +write_sysreg (vcpu->arch.ctxt.spsr_abt, spsr_abt); +write_sysreg (vcpu->arch.ctxt.spsr_und, spsr_und); +write_sysreg (vcpu->arch.ctxt.spsr_irq, spsr_irq); +write_sysreg (vcpu->arch.ctxt.spsr_fiq, spsr_fiq);write_sysreg (sysreg[DACR32_EL2 ], dacr32_el2);write_sysreg (sysreg[IFSR32_EL2 ], ifsr32_el2);
arch/arm64/kvm/regmap.c +32 -3
@@ -147,8 +147,20 @@ unsigned long vcpu_read_spsr32(const struct kvm_vcpu *vcpu) {int spsr_idx =vcpu_spsr32_mode (vcpu); -if (!vcpu->arch.sysregs_loaded_on_cpu) -return vcpu->arch.ctxt.spsr[spsr_idx]; +if (!vcpu->arch.sysregs_loaded_on_cpu) { +switch (spsr_idx) { +case KVM_SPSR_SVC : +return vcpu->arch.ctxt.spsr_el1; +case KVM_SPSR_ABT : +return vcpu->arch.ctxt.spsr_abt; +case KVM_SPSR_UND : +return vcpu->arch.ctxt.spsr_und; +case KVM_SPSR_IRQ : +return vcpu->arch.ctxt.spsr_irq; +case KVM_SPSR_FIQ : +return vcpu->arch.ctxt.spsr_fiq; + } + }switch (spsr_idx) {case KVM_SPSR_SVC :
@@ -171,7 +183,24 @@ void vcpu_write_spsr32(struct kvm_vcpu *vcpu, unsigned long v)int spsr_idx =vcpu_spsr32_mode (vcpu);if (!vcpu->arch.sysregs_loaded_on_cpu) { - vcpu->arch.ctxt.spsr[spsr_idx] = v; +switch (spsr_idx) { +case KVM_SPSR_SVC : + vcpu->arch.ctxt.spsr_el1 = v; +break ; +case KVM_SPSR_ABT : + vcpu->arch.ctxt.spsr_abt = v; +break ; +case KVM_SPSR_UND : + vcpu->arch.ctxt.spsr_und = v; +break ; +case KVM_SPSR_IRQ : + vcpu->arch.ctxt.spsr_irq = v; +break ; +case KVM_SPSR_FIQ : + vcpu->arch.ctxt.spsr_fiq = v; +break ; + } +return ; }
arch/arm64/kvm/sys_regs.c +2 -2
@@ -1850,9 +1850,9 @@ static bool access_spsr(struct kvm_vcpu *vcpu,return false;if (p->is_write) - vcpu->arch.ctxt.spsr[KVM_SPSR_EL1 ] = p->regval; + vcpu->arch.ctxt.spsr_el1 = p->regval;else - p->regval = vcpu->arch.ctxt.spsr[KVM_SPSR_EL1 ]; + p->regval = vcpu->arch.ctxt.spsr_el1;return true; }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 86/94 KVM: arm64: aarch32: Use __vcpu_sys_reg() instead of raw sys_regs access
In reply to Marc ZyngierInstead of directly accessing the sysreg array, let's use convenient accessors that will hide the complexity of the register remapping.
--- arch/arm64/kvm/hyp/switch.c | 5 ++--- arch/arm64/kvm/hyp/sysreg-sr.c | 20 ++++++-------------- 2 files changed, 8 insertions(+), 17 deletions(-)
arch/arm64/kvm/hyp/switch.c +2 -3
@@ -49,7 +49,7 @@ static void __hyp_text __fpsimd_save_fpexc32(struct kvm_vcpu *vcpu)if (!vcpu_el1_is_32bit (vcpu))return ; - vcpu->arch.ctxt.sys_regs[FPEXC32_EL2 ] =read_sysreg (fpexc32_el2); +__vcpu_sys_reg (vcpu,FPEXC32_EL2 ) =read_sysreg (fpexc32_el2); } static void __hyp_text __activate_traps_fpsimd32(struct kvm_vcpu *vcpu)
@@ -470,8 +470,7 @@ static bool __hyp_text __hyp_handle_fpsimd(struct kvm_vcpu *vcpu)/* Skip restoring fpexc32 for AArch64 guests */ if (!(read_sysreg (hcr_el2) &HCR_RW )) -write_sysreg (vcpu->arch.ctxt.sys_regs[FPEXC32_EL2 ], - fpexc32_el2); +write_sysreg (__vcpu_sys_reg (vcpu,FPEXC32_EL2 ), fpexc32_el2); vcpu->arch.flags |=KVM_ARM64_FP_ENABLED ;
arch/arm64/kvm/hyp/sysreg-sr.c +6 -14
@@ -408,44 +408,36 @@ NOKPROBE_SYMBOL(sysreg_restore_guest_state_vhe); void __hyp_text __sysreg32_save_state(struct kvm_vcpu *vcpu) { -u64 *sysreg; -if (!vcpu_el1_is_32bit (vcpu))return ; - sysreg = vcpu->arch.ctxt.sys_regs; - vcpu->arch.ctxt.spsr_abt =read_sysreg (spsr_abt); vcpu->arch.ctxt.spsr_und =read_sysreg (spsr_und); vcpu->arch.ctxt.spsr_irq =read_sysreg (spsr_irq); vcpu->arch.ctxt.spsr_fiq =read_sysreg (spsr_fiq); - sysreg[DACR32_EL2 ] =read_sysreg (dacr32_el2); - sysreg[IFSR32_EL2 ] =read_sysreg (ifsr32_el2); +__vcpu_sys_reg (vcpu,DACR32_EL2 ) =read_sysreg (dacr32_el2); +__vcpu_sys_reg (vcpu,IFSR32_EL2 ) =read_sysreg (ifsr32_el2);if (has_vhe () || vcpu->arch.flags &KVM_ARM64_DEBUG_DIRTY ) - sysreg[DBGVCR32_EL2 ] =read_sysreg (dbgvcr32_el2); +__vcpu_sys_reg (vcpu,DBGVCR32_EL2 ) =read_sysreg (dbgvcr32_el2); } void __hyp_text __sysreg32_restore_state(struct kvm_vcpu *vcpu) { -u64 *sysreg; -if (!vcpu_el1_is_32bit (vcpu))return ; - sysreg = vcpu->arch.ctxt.sys_regs; -write_sysreg (vcpu->arch.ctxt.spsr_abt, spsr_abt);write_sysreg (vcpu->arch.ctxt.spsr_und, spsr_und);write_sysreg (vcpu->arch.ctxt.spsr_irq, spsr_irq);write_sysreg (vcpu->arch.ctxt.spsr_fiq, spsr_fiq); -write_sysreg (sysreg[DACR32_EL2 ], dacr32_el2); -write_sysreg (sysreg[IFSR32_EL2 ], ifsr32_el2); +write_sysreg (__vcpu_sys_reg (vcpu,DACR32_EL2 ), dacr32_el2); +write_sysreg (__vcpu_sys_reg (vcpu,IFSR32_EL2 ), ifsr32_el2);if (has_vhe () || vcpu->arch.flags &KVM_ARM64_DEBUG_DIRTY ) -write_sysreg (sysreg[DBGVCR32_EL2 ], dbgvcr32_el2); +write_sysreg (__vcpu_sys_reg (vcpu,DBGVCR32_EL2 ), dbgvcr32_el2); }/**
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 87/94 KVM: arm64: VNCR-ize SPSR_EL1
In reply to Marc ZyngierAs promised, let's move SPSR_EL1 to the VNCR page.
--- arch/arm64/include/asm/kvm_emulate.h | 4 ++-- arch/arm64/include/asm/kvm_host.h | 2 +- arch/arm64/kvm/guest.c | 2 +- arch/arm64/kvm/hyp/sysreg-sr.c | 4 ++-- arch/arm64/kvm/regmap.c | 4 ++-- arch/arm64/kvm/sys_regs.c | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-)
arch/arm64/include/asm/kvm_emulate.h +2 -2
@@ -327,7 +327,7 @@ static inline unsigned long vcpu_read_spsr(const struct kvm_vcpu *vcpu)if (vcpu->arch.sysregs_loaded_on_cpu)return read_sysreg_el1 (SYS_SPSR );else -return vcpu->arch.ctxt.spsr_el1; +return __vcpu_sys_reg (vcpu,SPSR_EL1 ); }static inline void vcpu_write_spsr (struct kvm_vcpu *vcpu,unsigned long v)
@@ -345,7 +345,7 @@ static inline void vcpu_write_spsr(struct kvm_vcpu *vcpu, unsigned long v)if (vcpu->arch.sysregs_loaded_on_cpu)write_sysreg_el1 (v,SYS_SPSR );else - vcpu->arch.ctxt.spsr_el1 = v; +__vcpu_sys_reg (vcpu,SPSR_EL1 ) = v; }/*
arch/arm64/include/asm/kvm_host.h +1 -1
@@ -266,6 +266,7 @@ enum vcpu_sysreg {VNCR (MDSCR_EL1 ),/* Monitor Debug System Control Register */ VNCR (ELR_EL1 ),VNCR (SP_EL1 ), +VNCR (SPSR_EL1 ),VNCR (VPIDR_EL2 ),/* Virtualization Processor ID Register */ VNCR (VMPIDR_EL2 ),/* Virtualization Multiprocessor ID Register */ VNCR(HCR_EL2), /* Hypervisor Configuration Register */
@@ -336,7 +337,6 @@ enum vcpu_sysreg {struct kvm_cpu_context {struct user_pt_regs regs;/* sp = sp_el0 */ -u64 spsr_el1;/* aka spsr_svc */ u64 spsr_abt;u64 spsr_und;u64 spsr_irq;
arch/arm64/kvm/guest.c +1 -1
@@ -131,7 +131,7 @@ static void *core_reg_addr(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)return __vcpu_elr_el1 (vcpu);case KVM_REG_ARM_CORE_REG (spsr[KVM_SPSR_EL1 ]): -return &vcpu->arch.ctxt.spsr_el1; +return __ctxt_sys_reg (&vcpu->arch.ctxt,SPSR_EL1 );case KVM_REG_ARM_CORE_REG (spsr[KVM_SPSR_ABT ]):return &vcpu->arch.ctxt.spsr_abt;
arch/arm64/kvm/hyp/sysreg-sr.c +2 -2
@@ -60,7 +60,7 @@ static void __hyp_text __sysreg_save_vel1_state(struct kvm_cpu_context *ctxt)ctxt_sys_reg (ctxt,SP_EL1 ) =read_sysreg (sp_el1);ctxt_sys_reg (ctxt,ELR_EL1 ) =read_sysreg_el1 (SYS_ELR ); - ctxt->spsr_el1 =read_sysreg_el1 (SYS_SPSR ); +ctxt_sys_reg (ctxt,SPSR_EL1 ) =read_sysreg_el1 (SYS_SPSR ); }static void __sysreg_save_vel2_state (struct kvm_cpu_context *ctxt)
@@ -325,7 +325,7 @@ static void __hyp_text __sysreg_restore_vel1_state(struct kvm_cpu_context *ctxt)write_sysreg (ctxt_sys_reg (ctxt,SP_EL1 ), sp_el1);write_sysreg_el1 (ctxt_sys_reg (ctxt,ELR_EL1 ),SYS_ELR ); -write_sysreg_el1 (ctxt->spsr_el1,SYS_SPSR ); +write_sysreg_el1 (ctxt_sys_reg (ctxt,SPSR_EL1 ),SYS_SPSR ); } static void __hyp_text __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)
arch/arm64/kvm/regmap.c +2 -2
@@ -150,7 +150,7 @@ unsigned long vcpu_read_spsr32(const struct kvm_vcpu *vcpu)if (!vcpu->arch.sysregs_loaded_on_cpu) {switch (spsr_idx) {case KVM_SPSR_SVC : -return vcpu->arch.ctxt.spsr_el1; +return __vcpu_sys_reg (vcpu,SPSR_EL1 );case KVM_SPSR_ABT :return vcpu->arch.ctxt.spsr_abt;case KVM_SPSR_UND :
@@ -185,7 +185,7 @@ void vcpu_write_spsr32(struct kvm_vcpu *vcpu, unsigned long v)if (!vcpu->arch.sysregs_loaded_on_cpu) {switch (spsr_idx) {case KVM_SPSR_SVC : - vcpu->arch.ctxt.spsr_el1 = v; +__vcpu_sys_reg (vcpu,SPSR_EL1 ) = v;break ;case KVM_SPSR_ABT : vcpu->arch.ctxt.spsr_abt = v;
arch/arm64/kvm/sys_regs.c +2 -2
@@ -1850,9 +1850,9 @@ static bool access_spsr(struct kvm_vcpu *vcpu,return false;if (p->is_write) - vcpu->arch.ctxt.spsr_el1 = p->regval; +__vcpu_sys_reg (vcpu,SPSR_EL1 ) = p->regval;else - p->regval = vcpu->arch.ctxt.spsr_el1; + p->regval =__vcpu_sys_reg (vcpu,SPSR_EL1 );return true; }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 88/94 KVM: arm64: Add ARMv8.4 Enhanced Nested Virt cpufeature
In reply to Marc ZyngierAdd the detection code for the ARMv8.4-NV feature.
--- arch/arm/include/asm/kvm_nested.h | 1 + arch/arm64/include/asm/cpucaps.h | 3 ++- arch/arm64/include/asm/kvm_nested.h | 6 ++++++ arch/arm64/kernel/cpufeature.c | 10 ++++++++++ 4 files changed, 19 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_nested.h +1 -0
@@ -5,6 +5,7 @@#include <linux/kvm_host.h> static inline bool nested_virt_in_use (const struct kvm_vcpu *vcpu) {return false; } +static inline bool enhanced_nested_virt_in_use (const struct kvm_vcpu *vcpu) {return false; }static inline void check_nested_vcpu_requests (struct kvm_vcpu *vcpu) {}#endif /* __ARM_KVM_NESTED_H */
arch/arm64/include/asm/cpucaps.h +2 -1
@@ -60,7 +60,8 @@#define ARM64_HAS_RNG 50#define ARM64_HAS_NESTED_VIRT 51#define ARM64_HAS_ARMv8_4_TTL 52 +#define ARM64_HAS_ENHANCED_NESTED_VIRT 53 -#define ARM64_NCAPS 53 +#define ARM64_NCAPS 54#endif /* __ASM_CPUCAPS_H */
arch/arm64/include/asm/kvm_nested.h +6 -0
@@ -11,6 +11,12 @@ static inline bool nested_virt_in_use(const struct kvm_vcpu *vcpu)test_bit (KVM_ARM_VCPU_HAS_EL2 , vcpu->arch.features); } +static inline bool enhanced_nested_virt_in_use (const struct kvm_vcpu *vcpu) +{ +return cpus_have_const_cap (ARM64_HAS_ENHANCED_NESTED_VIRT ) && +nested_virt_in_use (vcpu); +} +extern void kvm_init_nested (struct kvm *kvm);extern int kvm_vcpu_init_nested (struct kvm_vcpu *vcpu);extern void kvm_init_nested_s2_mmu (struct kvm_s2_mmu *mmu);
arch/arm64/kernel/cpufeature.c +10 -0
@@ -1454,6 +1454,16 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .field_pos = ID_AA64MMFR2_NV_SHIFT, .min_field_value = 1, }, + { + .desc = "Enhanced Nested Virtualization Support", + .capability = ARM64_HAS_ENHANCED_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_NV_SHIFT, + .min_field_value = 2, + }, #endif /* CONFIG_ARM64_VHE */ { .desc = "32-bit EL0 Support",
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 89/94 KVM: arm64: nv: Synchronize PSTATE early on exit
In reply to Marc ZyngierThe NV code relies on predicates such as is_hyp_ctxt() being reliable. In turn, is_hyp_ctxt() relies on things like PSTATE and the virtual HCR_EL2 being accurate.
But with ARMv8.4-NV removing trapping for a large part of the EL2 system registers (among which HCR_EL2), we can't use such trapping to synchronize the rest of the state.
Let's look at the following sequence for a VHE guest:
(1) enter guest in host EL0
(2) guest traps to guest vEL2 (no hypervisor intervention)
(3) guest clears virtual HCR_EL2.TGE (no trap either)
(4) host interrupt fires, exit
(5) is_hyp_ctxt() now says "guest" (PSTATE.M==EL1 and TGE==0)
It is obvious that such behaviour would be rather unfortunate, and lead to interesting, difficult to catch bugs specially if preemption kicks in (yes, I wasted a whole week chasing this one).
In order to preserve the invariant that a guest entered in host context must exit in the same context, we must make sure that is_hyp_ctxt() works correctly. Since we can always observe the guest value of HCR_EL2.{E2H,TGE} in the VNCR_EL2 page, we solely need to synchronize PSTATE as early as possible.
This basically amounts to moving from_hw_pstate() as close as possible to the guest exit point, and fixup_guest_exit() seems as good a place as any.
--- arch/arm64/kvm/hyp/switch.c | 51 ++++++++++++++++++++++++++++++++-- arch/arm64/kvm/hyp/sysreg-sr.c | 26 ++--------------- 2 files changed, 51 insertions(+), 26 deletions(-)
arch/arm64/kvm/hyp/switch.c +48 -3
@@ -537,8 +537,45 @@ static bool __hyp_text handle_tx2_tvm(struct kvm_vcpu *vcpu)* the guest, false when we should restore the host state and return to the * main run loop. */ -static bool __hyp_text fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code) +static bool __hyp_text fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code, +bool hyp_ctxt) { +/* +* Sync pstate back as early as possible, so that is_hyp_ctxt() +* reflects the exact context. It is otherwise possible to get +* confused with a VHE guest and ARMv8.4-NV, such as: +* +* (1) enter guest in host EL0 +* (2) guest traps to guest vEL2 (no hypervisor intervention) +* (3) guest clears virtual HCR_EL2.TGE (no trap either) +* (4) host interrupt fires, exit +* (5) is_hyp_ctxt() now says "guest" (pstate.M==EL1 and TGE==0) +* +* If host preemption occurs, vcpu_load/put() will be very confused. +* This of course only matters to VHE. +*/ + +if (has_vhe ()) { +u64 pstate =read_sysreg_el2 (SYS_SPSR ); + +if (unlikely (hyp_ctxt)) { +u64 mode = pstate &PSR_MODE_MASK ; + +switch (mode) { +case PSR_MODE_EL1t: + mode = PSR_MODE_EL2t; +break ; +case PSR_MODE_EL1h: + mode = PSR_MODE_EL2h; +break ; + } + + pstate = (pstate & ~PSR_MODE_MASK ) | mode; + } + + *vcpu_cpsr (vcpu) = pstate; + } +if (ARM_EXCEPTION_CODE (*exit_code) !=ARM_EXCEPTION_IRQ ) vcpu->arch.fault.esr_el2 =read_sysreg_el2 (SYS_ESR );
@@ -683,6 +720,7 @@ int kvm_vcpu_run_vhe(struct kvm_vcpu *vcpu)struct kvm_cpu_context *host_ctxt;struct kvm_cpu_context *guest_ctxt;u64 exit_code; +bool hyp_ctxt; host_ctxt = vcpu->arch.host_cpu_context; host_ctxt->__hyp_running_vcpu = vcpu;
@@ -709,12 +747,19 @@ int kvm_vcpu_run_vhe(struct kvm_vcpu *vcpu)__set_guest_arch_workaround_state (vcpu); +/* +* Being in HYP context or not is an invariant here. If we enter in +* a given context, we exit in the same context. We can thus only +* sample the context once. +*/ + hyp_ctxt =is_hyp_ctxt (vcpu); +do {/* Jump in the fire! */ exit_code =__guest_enter (vcpu, host_ctxt);/* And we're baaack! */ - }while (fixup_guest_exit (vcpu, &exit_code)); + }while (fixup_guest_exit (vcpu, &exit_code, hyp_ctxt));__set_host_arch_workaround_state (vcpu);
@@ -788,7 +833,7 @@ int __hyp_text __kvm_vcpu_run_nvhe(struct kvm_vcpu *vcpu) exit_code =__guest_enter (vcpu, host_ctxt);/* And we're baaack! */ - }while (fixup_guest_exit (vcpu, &exit_code)); + }while (fixup_guest_exit (vcpu, &exit_code, false));__set_host_arch_workaround_state (vcpu);
arch/arm64/kvm/hyp/sysreg-sr.c +3 -23
@@ -109,32 +109,12 @@ static void __hyp_text __sysreg_save_el1_state(struct kvm_cpu_context *ctxt)__sysreg_save_vel1_state (ctxt); } -static u64 __hyp_text from_hw_pstate(const struct kvm_cpu_context *ctxt) -{ -u64 reg =read_sysreg_el2 (SYS_SPSR ); - -if (__is_hyp_ctxt (ctxt)) { -u64 mode = reg & (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 ; - } - -return (reg & ~(PSR_MODE_MASK |PSR_MODE32_BIT )) | mode; - } - -return reg; -} - static void __hyp_text __sysreg_save_el2_return_state(struct kvm_cpu_context *ctxt) { +/* On VHE, PSTATE is saved in fixup_guest_exit() */ +if (!has_vhe ()) + ctxt->regs.pstate =read_sysreg_el2 (SYS_SPSR ); ctxt->regs.pc =read_sysreg_el2 (SYS_ELR ); - ctxt->regs.pstate =from_hw_pstate (ctxt);if (cpus_have_const_cap (ARM64_HAS_RAS_EXTN ))ctxt_sys_reg (ctxt,DISR_EL1 ) =read_sysreg_s (SYS_VDISR_EL2 );
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 90/94 KVM: arm64: nv: Sync nested timer state with ARMv8.4
In reply to Marc ZyngierFrom: Christoffer Dall <[email protected]>
Emulating the ARMv8.4-NV timers is a bit odd, as the timers can be reconfigured behind our back without the hypervisor even noticing. In the VHE case, that's an actual regression in the architecture...
--- include/kvm/arm_arch_timer.h | 3 ++- virt/kvm/arm/arch_timer.c | 41 +++++++++++++++++++++++++++++++++++- virt/kvm/arm/arm.c | 7 ++++-- 3 files changed, 47 insertions(+), 4 deletions(-)
include/kvm/arm_arch_timer.h +2 -1
@@ -75,7 +75,8 @@ int kvm_timer_hyp_init(bool);int kvm_timer_enable (struct kvm_vcpu *vcpu);int kvm_timer_vcpu_reset (struct kvm_vcpu *vcpu);void kvm_timer_vcpu_init (struct kvm_vcpu *vcpu); -void kvm_timer_sync_hwstate (struct kvm_vcpu *vcpu); +void kvm_timer_sync_nested (struct kvm_vcpu *vcpu); +void kvm_timer_sync_user (struct kvm_vcpu *vcpu);bool kvm_timer_should_notify_user (struct kvm_vcpu *vcpu);void kvm_timer_update_run (struct kvm_vcpu *vcpu);void kvm_timer_vcpu_terminate (struct kvm_vcpu *vcpu);
virt/kvm/arm/arch_timer.c +40 -1
53 lines not on this page · raw message
virt/kvm/arm/arm.c +5 -2
@@ -730,7 +730,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)isb ();/* Ensure work in x_flush_hwstate is committed */ kvm_pmu_sync_hwstate (vcpu);if (static_branch_unlikely (&userspace_irqchip_in_use)) -kvm_timer_sync_hwstate (vcpu); +kvm_timer_sync_user (vcpu);kvm_vgic_sync_hwstate (vcpu);local_irq_enable ();preempt_enable ();
@@ -781,7 +781,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)* timer virtual interrupt state. */ if (static_branch_unlikely (&userspace_irqchip_in_use)) -kvm_timer_sync_hwstate (vcpu); +kvm_timer_sync_user (vcpu); + +if (enhanced_nested_virt_in_use (vcpu)) +kvm_timer_sync_nested (vcpu);kvm_arch_vcpu_ctxsync_fp (vcpu);
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 91/94 KVM: arm64: nv: Allocate VNCR page when required
In reply to Marc ZyngierIf running a NV guest on an ARMv8.4-NV capable system, let's allocate an additional page that will be used by the hypervisor to fulfill system register accesses.
--- arch/arm64/include/asm/kvm_host.h | 3 ++- arch/arm64/kvm/nested.c | 8 ++++++++ arch/arm64/kvm/reset.c | 1 + 3 files changed, 11 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kvm_host.h +2 -1
@@ -526,7 +526,8 @@ struct kvm_vcpu_arch {*/ static inline u64 *__ctxt_sys_reg (const struct kvm_cpu_context *ctxt,int r) { -if (unlikely (r >= __VNCR_START__ && ctxt->vncr_array)) +if (unlikely (cpus_have_const_cap (ARM64_HAS_ENHANCED_NESTED_VIRT ) && + r >= __VNCR_START__ && ctxt->vncr_array))return &ctxt->vncr_array[r - __VNCR_START__];return (u64 *)&ctxt->sys_regs[r];
arch/arm64/kvm/nested.c +8 -0
@@ -47,6 +47,12 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)if (!cpus_have_const_cap (ARM64_HAS_NESTED_VIRT ))return -EINVAL ; +if (cpus_have_const_cap (ARM64_HAS_ENHANCED_NESTED_VIRT )) { + vcpu->arch.ctxt.vncr_array = (u64 *)__get_free_page (GFP_KERNEL | __GFP_ZERO); +if (!vcpu->arch.ctxt.vncr_array) +return -ENOMEM ; + } +mutex_lock (&kvm->lock);/*
@@ -64,6 +70,8 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu) kvm_init_stage2_mmu(kvm, &tmp[num_mmus - 2])) { kvm_free_stage2_pgd(&tmp[num_mmus - 1]); kvm_free_stage2_pgd(&tmp[num_mmus - 2]); + free_page((unsigned long)vcpu->arch.ctxt.vncr_array); + vcpu->arch.ctxt.vncr_array = NULL; } else { kvm->arch.nested_mmus_size = num_mmus; ret =0 ;
arch/arm64/kvm/reset.c +1 -0
@@ -207,6 +207,7 @@ bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu)void kvm_arm_vcpu_destroy (struct kvm_vcpu *vcpu) {kfree (vcpu->arch.sve_state); +free_page ((unsigned long )vcpu->arch.ctxt.vncr_array); }static void kvm_vcpu_reset_sve (struct kvm_vcpu *vcpu)
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 92/94 KVM: arm64: nv: Enable ARMv8.4-NV support
In reply to Marc ZyngierAs all the VNCR-capable system registers are nicely separated from the rest of the crowd, let's set HCR_EL2.NV2 on and let the ball rolling.
--- arch/arm64/include/asm/kvm_arm.h | 1 + arch/arm64/include/asm/kvm_emulate.h | 23 +++++++++++++---------- arch/arm64/include/asm/sysreg.h | 1 + arch/arm64/kvm/hyp/switch.c | 18 +++++++++++++++--- 4 files changed, 30 insertions(+), 13 deletions(-)
arch/arm64/include/asm/kvm_arm.h +1 -0
@@ -13,6 +13,7 @@/* Hyp Configuration Register (HCR) bits */ #define HCR_FWB (UL(1) << 46) +#define HCR_NV2 (UL(1) << 45)#define HCR_AT (UL(1) << 44)#define HCR_NV1 (UL(1) << 43)#define HCR_NV (UL(1) << 42)
arch/arm64/include/asm/kvm_emulate.h +13 -10
@@ -281,21 +281,24 @@ static inline bool is_hyp_ctxt(const struct kvm_vcpu *vcpu)static inline u64 __fixup_spsr_el2_write (struct kvm_cpu_context *ctxt,u64 val) { -if (!__vcpu_el2_e2h_is_set (ctxt)) { -/* -* Clear the .M field when writing SPSR to the CPU, so that we -* can detect when the CPU clobbered our SPSR copy during a -* local exception. -*/ - val &= ~0xc ; - } + struct kvm_vcpu *vcpu = container_of(ctxt, struct kvm_vcpu, arch.ctxt); + +if (enhanced_nested_virt_in_use (vcpu) ||__vcpu_el2_e2h_is_set (ctxt)) +return val; -return val; +/* +* Clear the .M field when writing SPSR to the CPU, so that we +* can detect when the CPU clobbered our SPSR copy during a +* local exception. +*/ +return val &= ~0xc ; }static inline u64 __fixup_spsr_el2_read (const struct kvm_cpu_context *ctxt,u64 val) { -if (__vcpu_el2_e2h_is_set (ctxt)) + struct kvm_vcpu *vcpu = container_of(ctxt, struct kvm_vcpu, arch.ctxt); + +if (enhanced_nested_virt_in_use (vcpu) ||__vcpu_el2_e2h_is_set (ctxt))return val;/*
arch/arm64/include/asm/sysreg.h +1 -0
@@ -425,6 +425,7 @@#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_VNCR_EL2 sys_reg(3, 4, 2, 2, 0)#define SYS_DACR32_EL2 sys_reg(3, 4, 3, 0, 0)
arch/arm64/kvm/hyp/switch.c +15 -3
@@ -160,7 +160,13 @@ static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu)* the EL1 virtual memory control register accesses * as well as the AT S1 operations. */ - hcr |=HCR_TVM |HCR_TRVM |HCR_AT |HCR_TTLB |HCR_NV1 ; +if (enhanced_nested_virt_in_use (vcpu)) { + hcr &= ~HCR_TVM ; + }else { + hcr |=HCR_TVM |HCR_TRVM |HCR_TTLB ; + } + + hcr |=HCR_AT |HCR_NV1 ; }else {/* * For a guest hypervisor on v8.1 (VHE), allow to
@@ -186,12 +192,18 @@ static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu)/* * If we're using the EL1 translation regime -* (TGE clear, then ensure that AT S1 ops are -* trapped too. +* (TGE clear), then ensure that AT S1 and +* TLBI E1 ops are trapped too. */ if (!vcpu_el2_tge_is_set (vcpu)) hcr |=HCR_AT |HCR_TTLB ; } + +if (enhanced_nested_virt_in_use (vcpu)) { + hcr |=HCR_AT |HCR_TTLB |HCR_NV2 ; +write_sysreg_s (vcpu->arch.ctxt.vncr_array, +SYS_VNCR_EL2 ); + } }else if (nested_virt_in_use (vcpu)) { hcr |=__vcpu_sys_reg (vcpu,HCR_EL2 ); }
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 93/94 KVM: arm64: nv: Fast-track 'InHost' exception returns
In reply to Marc ZyngierA significant part of the ARMv8.3-NV extension is to trap ERET instructions so that the hypervisor gets a chance to switch from a vEL2 L1 guest to an EL1 L2 guest.
But this also has the unfortunate consequence of trapping ERET in unsuspecting circumstances, such as staying at vEL2 (interrupt handling while being in the guest hypervisor), or returning to host userspace in the case of a VHE guest.
Although we already make some effort to handle these ERET quicker by not doing the put/load dance, it is still way too far down the line for it to be efficient enough.
For these cases, it would ideal to ERET directly, no question asked. Of course, we can't do that. But the next best thing is to do it as early as possible, in fixup_guest_exit(), much as we would handle FPSIMD exceptions.
--- arch/arm64/kvm/emulate-nested.c | 26 ++-------------- arch/arm64/kvm/hyp/switch.c | 54 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 24 deletions(-)
arch/arm64/kvm/emulate-nested.c +2 -24
@@ -78,8 +78,7 @@ static u64 get_el2_except_vector(struct kvm_vcpu *vcpu,void kvm_emulate_nested_eret (struct kvm_vcpu *vcpu) { -u64 spsr, elr, mode; -bool direct_eret; +u64 spsr, elr;/* * Forward this trap to the virtual EL2 if the virtual
@@ -88,31 +87,10 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu)if (forward_nv_traps (vcpu))return ; -/* -* Going through the whole put/load motions is a waste of time -* if this is a VHE guest hypervisor returning to its own -* userspace, or the hypervisor performing a local exception -* return. No need to save/restore registers, no need to -* switch S2 MMU. Just do the canonical ERET. -*/ - spsr =vcpu_read_sys_reg (vcpu,SPSR_EL2 ); - mode = spsr & (PSR_MODE_MASK |PSR_MODE32_BIT ); - - direct_eret = (mode == PSR_MODE_EL0t && -vcpu_el2_e2h_is_set (vcpu) && -vcpu_el2_tge_is_set (vcpu)); - direct_eret |= (mode == PSR_MODE_EL2h || mode == PSR_MODE_EL2t); - -if (direct_eret) { - *vcpu_pc (vcpu) =vcpu_read_sys_reg (vcpu,ELR_EL2 ); - *vcpu_cpsr (vcpu) = spsr; -trace_kvm_nested_eret (vcpu, *vcpu_pc (vcpu), spsr); -return ; - } -preempt_disable ();kvm_arch_vcpu_put (vcpu); + spsr =__vcpu_sys_reg (vcpu,SPSR_EL2 ); elr =__vcpu_sys_reg (vcpu,ELR_EL2 );trace_kvm_nested_eret (vcpu, elr, spsr);
arch/arm64/kvm/hyp/switch.c +54 -0
66 lines not on this page · raw message
Signature · 1 line
2.20.1
Marc Zyngier
maintainerPATCH v2 94/94 KVM: arm64: nv: Fast-track EL1 TLBIs for VHE guests
In reply to Marc ZyngierDue to the way ARMv8.4-NV suppresses traps when accessing EL2 system registers, we can't track when the guest changes its HCR_EL2.TGE setting. This means we alwats trap EL1 TLBIs, even if they don't affect any guest.
This obviously has a huge impact on performance, as we handle TLBI traps as a normal exit, and a normal VHE host issues thousands of TLBIs when booting (and quite a few when running userspace).
A cheap way to reduce the overhead is to handle the limited case of {E2H,TGE}=={1,1} as a guest fixup, as we already have the right mmu configuration in place. Just execute the decoded instruction right away and return to the guest.
--- arch/arm64/kvm/hyp/switch.c | 35 +++++++++++++++++++++++++++++++++++ arch/arm64/kvm/hyp/tlb.c | 6 ++++-- arch/arm64/kvm/sys_regs.c | 28 +++++++++------------------- 3 files changed, 48 insertions(+), 21 deletions(-)
arch/arm64/kvm/hyp/switch.c +35 -0
@@ -539,6 +539,38 @@ static bool __hyp_text __hyp_handle_eret(struct kvm_vcpu *vcpu)return true; } +static bool __hyp_text __hyp_handle_tlbi_el1(struct kvm_vcpu *vcpu) +{ +u32 instr; +u64 val; + +/* +* Ideally, we would never trap on EL1 TLB invalidations when the +* guest's HCR_EL2.{E2H,TGE} == {1,1}. But "thanks" to ARMv8.4, we +* don't trap writes to HCR_EL2, meaning that we can't track +* changes to the virtual TGE bit. So we leave HCR_EL2.TTLB set on +* the host. Oopsie... +* +* In order to speed-up EL1 TLBIs from the vEL2 guest when TGE is +* set, try and handle these invalidation as quickly as possible, +* without fully exiting (unless this needs forwarding). +*/ +if (kvm_vcpu_trap_get_class (vcpu) != ESR_ELx_EC_SYS64 || + !vcpu_mode_el2 (vcpu) || + (__vcpu_sys_reg (vcpu,HCR_EL2 ) & (HCR_E2H |HCR_TGE )) != (HCR_E2H |HCR_TGE )) +return false; + + instr =esr_sys64_to_sysreg (kvm_vcpu_get_hsr (vcpu)); +if (sys_reg_Op0 (instr) != TLBI_Op0 || +sys_reg_Op1 (instr) != TLBI_Op1_EL1) +return false; + + val =vcpu_get_reg (vcpu,kvm_vcpu_sys_get_rt (vcpu)); +__kvm_tlb_el1_instr (NULL , val, instr); +__kvm_skip_instr (vcpu); + +return true; +} static bool __hyp_text handle_tx2_tvm(struct kvm_vcpu *vcpu) {
@@ -669,6 +701,9 @@ static bool __hyp_text fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code,if (__hyp_handle_eret (vcpu))return true; +if (__hyp_handle_tlbi_el1 (vcpu)) +return true; +if (!__populate_fault_info (vcpu))return true;
arch/arm64/kvm/hyp/tlb.c +4 -2
@@ -278,7 +278,8 @@ void __hyp_text __kvm_tlb_el1_instr(struct kvm_s2_mmu *mmu, u64 val, u64 sys_encdsb (ishst);/* Switch to requested VMID */ -__tlb_switch_to_guest (mmu, &cxt); +if (mmu) +__tlb_switch_to_guest (mmu, &cxt);/* * Execute the same instruction as the guest hypervisor did,
@@ -317,5 +318,6 @@ void __hyp_text __kvm_tlb_el1_instr(struct kvm_s2_mmu *mmu, u64 val, u64 sys_encdsb (ish);isb (); -__tlb_switch_to_host (mmu, &cxt); +if (mmu) +__tlb_switch_to_host (mmu, &cxt); }
arch/arm64/kvm/sys_regs.c +9 -19
@@ -2688,6 +2688,8 @@ static bool handle_tlbi_el1(struct kvm_vcpu *vcpu, struct sys_reg_params *p, const struct sys_reg_desc *r) {u32 sys_encoding =sys_insn (p->Op0, p->Op1, p->CRn, p->CRm, p->Op2); +u64 virtual_vttbr =vcpu_read_sys_reg (vcpu,VTTBR_EL2 ); +struct kvm_s2_mmu *mmu;/* * If we're here, this is because we've trapped on a EL1 TLBI
@@ -2706,27 +2708,15 @@ static bool handle_tlbi_el1(struct kvm_vcpu *vcpu, struct sys_reg_params *p,mutex_lock (&vcpu->kvm->lock); -if ((__vcpu_sys_reg (vcpu,HCR_EL2 ) & (HCR_E2H |HCR_TGE )) != (HCR_E2H |HCR_TGE )) { -u64 virtual_vttbr =vcpu_read_sys_reg (vcpu,VTTBR_EL2 ); -struct kvm_s2_mmu *mmu; - - mmu =lookup_s2_mmu (vcpu->kvm, virtual_vttbr,HCR_VM ); -if (mmu) -kvm_call_hyp (__kvm_tlb_el1_instr, - mmu, p->regval, sys_encoding); + mmu =lookup_s2_mmu (vcpu->kvm, virtual_vttbr,HCR_VM ); +if (mmu) +kvm_call_hyp (__kvm_tlb_el1_instr, + mmu, p->regval, sys_encoding); - mmu =lookup_s2_mmu (vcpu->kvm, virtual_vttbr,0 ); -if (mmu) -kvm_call_hyp (__kvm_tlb_el1_instr, - mmu, p->regval, sys_encoding); - }else { -/* -* ARMv8.4-NV allows the guest to change TGE behind -* our back, so we always trap EL1 TLBIs from vEL2... -*/ + mmu =lookup_s2_mmu (vcpu->kvm, virtual_vttbr,0 ); +if (mmu)kvm_call_hyp (__kvm_tlb_el1_instr, - &vcpu->kvm->arch.mmu, p->regval, sys_encoding); - } + mmu, p->regval, sys_encoding);mutex_unlock (&vcpu->kvm->lock);
Signature · 1 line
2.20.1