Skip to content

Commit b710fe0

Browse files
committed
Merge branch kvm-arm64/hvhe into kvmarm/next
* kvm-arm64/hvhe: : Support for running split-hypervisor w/VHE, courtesy of Marc Zyngier : : From the cover letter: : : KVM (on ARMv8.0) and pKVM (on all revisions of the architecture) use : the split hypervisor model that makes the EL2 code more or less : standalone. In the later case, we totally ignore the VHE mode and : stick with the good old v8.0 EL2 setup. : : We introduce a new "mode" for KVM called hVHE, in reference to the : nVHE mode, and indicating that only the hypervisor is using VHE. KVM: arm64: Fix hVHE init on CPUs where HCR_EL2.E2H is not RES1 arm64: Allow arm64_sw.hvhe on command line KVM: arm64: Force HCR_E2H in guest context when ARM64_KVM_HVHE is set KVM: arm64: Program the timer traps with VHE layout in hVHE mode KVM: arm64: Rework CPTR_EL2 programming for HVHE configuration KVM: arm64: Adjust EL2 stage-1 leaf AP bits when ARM64_KVM_HVHE is set KVM: arm64: Disable TTBR1_EL2 when using ARM64_KVM_HVHE KVM: arm64: Force HCR_EL2.E2H when ARM64_KVM_HVHE is set KVM: arm64: Key use of VHE instructions in nVHE code off ARM64_KVM_HVHE KVM: arm64: Remove alternatives from sysreg accessors in VHE hypervisor context arm64: Use CPACR_EL1 format to set CPTR_EL2 when E2H is set arm64: Allow EL1 physical timer access when running VHE arm64: Don't enable VHE for the kernel if OVERRIDE_HVHE is set arm64: Add KVM_HVHE capability and has_hvhe() predicate arm64: Turn kaslr_feature_override into a generic SW feature override arm64: Prevent the use of is_kernel_in_hyp_mode() in hypervisor code KVM: arm64: Drop is_kernel_in_hyp_mode() from __invalidate_icache_guest_page() Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
2 parents 1a08f49 + 1700f89 commit b710fe0

24 files changed

Lines changed: 245 additions & 66 deletions

arch/arm64/include/asm/cpufeature.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
#define MAX_CPU_FEATURES 128
1616
#define cpu_feature(x) KERNEL_HWCAP_ ## x
1717

18+
#define ARM64_SW_FEATURE_OVERRIDE_NOKASLR 0
19+
#define ARM64_SW_FEATURE_OVERRIDE_HVHE 4
20+
1821
#ifndef __ASSEMBLY__
1922

2023
#include <linux/bug.h>
@@ -925,6 +928,8 @@ extern struct arm64_ftr_override id_aa64smfr0_override;
925928
extern struct arm64_ftr_override id_aa64isar1_override;
926929
extern struct arm64_ftr_override id_aa64isar2_override;
927930

931+
extern struct arm64_ftr_override arm64_sw_feature_override;
932+
928933
u32 get_kvm_ipa_limit(void);
929934
void dump_cpu_features(void);
930935

arch/arm64/include/asm/el2_setup.h

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@
3434
*/
3535
.macro __init_el2_timers
3636
mov x0, #3 // Enable EL1 physical timers
37+
mrs x1, hcr_el2
38+
and x1, x1, #HCR_E2H
39+
cbz x1, .LnVHE_\@
40+
lsl x0, x0, #10
41+
.LnVHE_\@:
3742
msr cnthctl_el2, x0
3843
msr cntvoff_el2, xzr // Clear virtual offset
3944
.endm
@@ -124,8 +129,15 @@
124129
.endm
125130

126131
/* Coprocessor traps */
127-
.macro __init_el2_nvhe_cptr
132+
.macro __init_el2_cptr
133+
mrs x1, hcr_el2
134+
and x1, x1, #HCR_E2H
135+
cbz x1, .LnVHE_\@
136+
mov x0, #(CPACR_EL1_FPEN_EL1EN | CPACR_EL1_FPEN_EL0EN)
137+
b .Lset_cptr_\@
138+
.LnVHE_\@:
128139
mov x0, #0x33ff
140+
.Lset_cptr_\@:
129141
msr cptr_el2, x0 // Disable copro. traps to EL2
130142
.endm
131143

@@ -191,9 +203,8 @@
191203
__init_el2_gicv3
192204
__init_el2_hstr
193205
__init_el2_nvhe_idregs
194-
__init_el2_nvhe_cptr
206+
__init_el2_cptr
195207
__init_el2_fgt
196-
__init_el2_nvhe_prepare_eret
197208
.endm
198209

199210
#ifndef __KVM_NVHE_HYPERVISOR__
@@ -239,7 +250,17 @@
239250

240251
.Linit_sve_\@: /* SVE register access */
241252
mrs x0, cptr_el2 // Disable SVE traps
253+
mrs x1, hcr_el2
254+
and x1, x1, #HCR_E2H
255+
cbz x1, .Lcptr_nvhe_\@
256+
257+
// VHE case
258+
orr x0, x0, #(CPACR_EL1_ZEN_EL1EN | CPACR_EL1_ZEN_EL0EN)
259+
b .Lset_cptr_\@
260+
261+
.Lcptr_nvhe_\@: // nVHE case
242262
bic x0, x0, #CPTR_EL2_TZ
263+
.Lset_cptr_\@:
243264
msr cptr_el2, x0
244265
isb
245266
mov x1, #ZCR_ELx_LEN_MASK // SVE: Enable full vector

arch/arm64/include/asm/kvm_arm.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,6 @@
285285
#define CPTR_EL2_TFP (1 << CPTR_EL2_TFP_SHIFT)
286286
#define CPTR_EL2_TZ (1 << 8)
287287
#define CPTR_NVHE_EL2_RES1 0x000032ff /* known RES1 bits in CPTR_EL2 (nVHE) */
288-
#define CPTR_EL2_DEFAULT CPTR_NVHE_EL2_RES1
289288
#define CPTR_NVHE_EL2_RES0 (GENMASK(63, 32) | \
290289
GENMASK(29, 21) | \
291290
GENMASK(19, 14) | \
@@ -347,8 +346,7 @@
347346
ECN(SOFTSTP_CUR), ECN(WATCHPT_LOW), ECN(WATCHPT_CUR), \
348347
ECN(BKPT32), ECN(VECTOR32), ECN(BRK64), ECN(ERET)
349348

350-
#define CPACR_EL1_DEFAULT (CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN |\
351-
CPACR_EL1_ZEN_EL1EN)
349+
#define CPACR_EL1_TTA (1 << 28)
352350

353351
#define kvm_mode_names \
354352
{ PSR_MODE_EL0t, "EL0t" }, \

arch/arm64/include/asm/kvm_emulate.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu)
7474
static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu)
7575
{
7676
vcpu->arch.hcr_el2 = HCR_GUEST_FLAGS;
77-
if (is_kernel_in_hyp_mode())
77+
if (has_vhe() || has_hvhe())
7878
vcpu->arch.hcr_el2 |= HCR_E2H;
7979
if (cpus_have_const_cap(ARM64_HAS_RAS_EXTN)) {
8080
/* route synchronous external abort exceptions to EL2 */
@@ -570,4 +570,35 @@ static inline bool vcpu_has_feature(struct kvm_vcpu *vcpu, int feature)
570570
return test_bit(feature, vcpu->arch.features);
571571
}
572572

573+
static __always_inline u64 kvm_get_reset_cptr_el2(struct kvm_vcpu *vcpu)
574+
{
575+
u64 val;
576+
577+
if (has_vhe()) {
578+
val = (CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN |
579+
CPACR_EL1_ZEN_EL1EN);
580+
} else if (has_hvhe()) {
581+
val = (CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN);
582+
} else {
583+
val = CPTR_NVHE_EL2_RES1;
584+
585+
if (vcpu_has_sve(vcpu) &&
586+
(vcpu->arch.fp_state == FP_STATE_GUEST_OWNED))
587+
val |= CPTR_EL2_TZ;
588+
if (cpus_have_final_cap(ARM64_SME))
589+
val &= ~CPTR_EL2_TSM;
590+
}
591+
592+
return val;
593+
}
594+
595+
static __always_inline void kvm_reset_cptr_el2(struct kvm_vcpu *vcpu)
596+
{
597+
u64 val = kvm_get_reset_cptr_el2(vcpu);
598+
599+
if (has_vhe() || has_hvhe())
600+
write_sysreg(val, cpacr_el1);
601+
else
602+
write_sysreg(val, cptr_el2);
603+
}
573604
#endif /* __ARM64_KVM_EMULATE_H__ */

arch/arm64/include/asm/kvm_hyp.h

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,35 @@ DECLARE_PER_CPU(struct kvm_cpu_context, kvm_hyp_ctxt);
1616
DECLARE_PER_CPU(unsigned long, kvm_hyp_vector);
1717
DECLARE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
1818

19+
/*
20+
* Unified accessors for registers that have a different encoding
21+
* between VHE and non-VHE. They must be specified without their "ELx"
22+
* encoding, but with the SYS_ prefix, as defined in asm/sysreg.h.
23+
*/
24+
25+
#if defined(__KVM_VHE_HYPERVISOR__)
26+
27+
#define read_sysreg_el0(r) read_sysreg_s(r##_EL02)
28+
#define write_sysreg_el0(v,r) write_sysreg_s(v, r##_EL02)
29+
#define read_sysreg_el1(r) read_sysreg_s(r##_EL12)
30+
#define write_sysreg_el1(v,r) write_sysreg_s(v, r##_EL12)
31+
#define read_sysreg_el2(r) read_sysreg_s(r##_EL1)
32+
#define write_sysreg_el2(v,r) write_sysreg_s(v, r##_EL1)
33+
34+
#else // !__KVM_VHE_HYPERVISOR__
35+
36+
#if defined(__KVM_NVHE_HYPERVISOR__)
37+
#define VHE_ALT_KEY ARM64_KVM_HVHE
38+
#else
39+
#define VHE_ALT_KEY ARM64_HAS_VIRT_HOST_EXTN
40+
#endif
41+
1942
#define read_sysreg_elx(r,nvh,vh) \
2043
({ \
2144
u64 reg; \
22-
asm volatile(ALTERNATIVE(__mrs_s("%0", r##nvh), \
45+
asm volatile(ALTERNATIVE(__mrs_s("%0", r##nvh), \
2346
__mrs_s("%0", r##vh), \
24-
ARM64_HAS_VIRT_HOST_EXTN) \
47+
VHE_ALT_KEY) \
2548
: "=r" (reg)); \
2649
reg; \
2750
})
@@ -31,23 +54,19 @@ DECLARE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
3154
u64 __val = (u64)(v); \
3255
asm volatile(ALTERNATIVE(__msr_s(r##nvh, "%x0"), \
3356
__msr_s(r##vh, "%x0"), \
34-
ARM64_HAS_VIRT_HOST_EXTN) \
57+
VHE_ALT_KEY) \
3558
: : "rZ" (__val)); \
3659
} while (0)
3760

38-
/*
39-
* Unified accessors for registers that have a different encoding
40-
* between VHE and non-VHE. They must be specified without their "ELx"
41-
* encoding, but with the SYS_ prefix, as defined in asm/sysreg.h.
42-
*/
43-
4461
#define read_sysreg_el0(r) read_sysreg_elx(r, _EL0, _EL02)
4562
#define write_sysreg_el0(v,r) write_sysreg_elx(v, r, _EL0, _EL02)
4663
#define read_sysreg_el1(r) read_sysreg_elx(r, _EL1, _EL12)
4764
#define write_sysreg_el1(v,r) write_sysreg_elx(v, r, _EL1, _EL12)
4865
#define read_sysreg_el2(r) read_sysreg_elx(r, _EL2, _EL1)
4966
#define write_sysreg_el2(v,r) write_sysreg_elx(v, r, _EL2, _EL1)
5067

68+
#endif // __KVM_VHE_HYPERVISOR__
69+
5170
/*
5271
* Without an __arch_swab32(), we fall back to ___constant_swab32(), but the
5372
* static inline can allow the compiler to out-of-line this. KVM always wants

arch/arm64/include/asm/kvm_mmu.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ static inline void __invalidate_icache_guest_page(void *va, size_t size)
228228
if (icache_is_aliasing()) {
229229
/* any kind of VIPT cache */
230230
icache_inval_all_pou();
231-
} else if (is_kernel_in_hyp_mode() || !icache_is_vpipt()) {
231+
} else if (read_sysreg(CurrentEL) != CurrentEL_EL1 ||
232+
!icache_is_vpipt()) {
232233
/* PIPT or VPIPT at EL2 (see comment in __kvm_tlb_flush_vmid_ipa) */
233234
icache_inval_pou((unsigned long)va, (unsigned long)va + size);
234235
}

arch/arm64/include/asm/virt.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,10 @@ static inline bool is_hyp_mode_mismatched(void)
110110
return __boot_cpu_mode[0] != __boot_cpu_mode[1];
111111
}
112112

113-
static inline bool is_kernel_in_hyp_mode(void)
113+
static __always_inline bool is_kernel_in_hyp_mode(void)
114114
{
115+
BUILD_BUG_ON(__is_defined(__KVM_NVHE_HYPERVISOR__) ||
116+
__is_defined(__KVM_VHE_HYPERVISOR__));
115117
return read_sysreg(CurrentEL) == CurrentEL_EL2;
116118
}
117119

@@ -140,6 +142,14 @@ static __always_inline bool is_protected_kvm_enabled(void)
140142
return cpus_have_final_cap(ARM64_KVM_PROTECTED_MODE);
141143
}
142144

145+
static __always_inline bool has_hvhe(void)
146+
{
147+
if (is_vhe_hyp_code())
148+
return false;
149+
150+
return cpus_have_final_cap(ARM64_KVM_HVHE);
151+
}
152+
143153
static inline bool is_hyp_nvhe(void)
144154
{
145155
return is_hyp_mode_available() && !is_kernel_in_hyp_mode();

arch/arm64/kernel/cpufeature.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,8 @@ struct arm64_ftr_override __ro_after_init id_aa64smfr0_override;
664664
struct arm64_ftr_override __ro_after_init id_aa64isar1_override;
665665
struct arm64_ftr_override __ro_after_init id_aa64isar2_override;
666666

667+
struct arm64_ftr_override arm64_sw_feature_override;
668+
667669
static const struct __ftr_reg_entry {
668670
u32 sys_id;
669671
struct arm64_ftr_reg *reg;
@@ -1996,6 +1998,19 @@ static bool has_nested_virt_support(const struct arm64_cpu_capabilities *cap,
19961998
return true;
19971999
}
19982000

2001+
static bool hvhe_possible(const struct arm64_cpu_capabilities *entry,
2002+
int __unused)
2003+
{
2004+
u64 val;
2005+
2006+
val = read_sysreg(id_aa64mmfr1_el1);
2007+
if (!cpuid_feature_extract_unsigned_field(val, ID_AA64MMFR1_EL1_VH_SHIFT))
2008+
return false;
2009+
2010+
val = arm64_sw_feature_override.val & arm64_sw_feature_override.mask;
2011+
return cpuid_feature_extract_unsigned_field(val, ARM64_SW_FEATURE_OVERRIDE_HVHE);
2012+
}
2013+
19992014
#ifdef CONFIG_ARM64_PAN
20002015
static void cpu_enable_pan(const struct arm64_cpu_capabilities *__unused)
20012016
{
@@ -2641,6 +2656,12 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
26412656
.cpu_enable = cpu_enable_dit,
26422657
ARM64_CPUID_FIELDS(ID_AA64PFR0_EL1, DIT, IMP)
26432658
},
2659+
{
2660+
.desc = "VHE for hypervisor only",
2661+
.capability = ARM64_KVM_HVHE,
2662+
.type = ARM64_CPUCAP_SYSTEM_FEATURE,
2663+
.matches = hvhe_possible,
2664+
},
26442665
{},
26452666
};
26462667

arch/arm64/kernel/head.S

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,8 @@ SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)
603603
msr sctlr_el1, x1
604604
mov x2, xzr
605605
2:
606+
__init_el2_nvhe_prepare_eret
607+
606608
mov w0, #BOOT_CPU_MODE_EL2
607609
orr x0, x0, x2
608610
eret

arch/arm64/kernel/hyp-stub.S

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,15 @@ SYM_CODE_START_LOCAL(__finalise_el2)
8282
tbnz x1, #0, 1f
8383

8484
// Needs to be VHE capable, obviously
85-
check_override id_aa64mmfr1 ID_AA64MMFR1_EL1_VH_SHIFT 2f 1f x1 x2
85+
check_override id_aa64mmfr1 ID_AA64MMFR1_EL1_VH_SHIFT 0f 1f x1 x2
86+
87+
0: // Check whether we only want the hypervisor to run VHE, not the kernel
88+
adr_l x1, arm64_sw_feature_override
89+
ldr x2, [x1, FTR_OVR_VAL_OFFSET]
90+
ldr x1, [x1, FTR_OVR_MASK_OFFSET]
91+
and x2, x2, x1
92+
ubfx x2, x2, #ARM64_SW_FEATURE_OVERRIDE_HVHE, #4
93+
cbz x2, 2f
8694

8795
1: mov_q x0, HVC_STUB_ERR
8896
eret

0 commit comments

Comments
 (0)