mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-09 07:51:16 -04:00
Merge branch kvm-arm64/feat_e2h0 into kvmarm/next
* kvm-arm64/feat_e2h0: : Support for FEAT_E2H0, courtesy of Marc Zyngier : : As described in the cover letter: : : Since ARMv8.1, the architecture has grown the VHE feature, which makes : EL2 a superset of EL1. With ARMv9.5 (and retroactively allowed from : ARMv8.1), the architecture allows implementations to have VHE as the : *only* implemented behaviour, meaning that HCR_EL2.E2H can be : implemented as RES1. As a follow-up, HCR_EL2.NV1 can also be : implemented as RES0, making the VHE-ness of the architecture : recursive. : : This series adds support for detecting the architectural feature of E2H : being RES1, leveraging the existing infrastructure for handling : out-of-spec CPUs that are VHE-only. Additionally, the (incomplete) NV : infrastructure in KVM is updated to enforce E2H=1 for guest hypervisors : on implementations that do not support NV1. arm64: cpufeatures: Fix FEAT_NV check when checking for FEAT_NV1 arm64: cpufeatures: Only check for NV1 if NV is present arm64: cpufeatures: Add missing ID_AA64MMFR4_EL1 to __read_sysreg_by_encoding() KVM: arm64: Handle Apple M2 as not having HCR_EL2.NV1 implemented KVM: arm64: Force guest's HCR_EL2.E2H RES1 when NV1 is not implemented KVM: arm64: Expose ID_AA64MMFR4_EL1 to guests arm64: Treat HCR_EL2.E2H as RES1 when ID_AA64MMFR4_EL1.E2H0 is negative arm64: cpufeature: Detect HCR_EL2.NV1 being RES0 arm64: cpufeature: Add ID_AA64MMFR4_EL1 handling arm64: sysreg: Add layout for ID_AA64MMFR4_EL1 arm64: cpufeature: Correctly display signed override values arm64: cpufeatures: Correctly handle signed values arm64: Add macro to compose a sysreg field value Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
This commit is contained in:
@@ -56,6 +56,7 @@ struct cpuinfo_arm64 {
|
||||
u64 reg_id_aa64mmfr1;
|
||||
u64 reg_id_aa64mmfr2;
|
||||
u64 reg_id_aa64mmfr3;
|
||||
u64 reg_id_aa64mmfr4;
|
||||
u64 reg_id_aa64pfr0;
|
||||
u64 reg_id_aa64pfr1;
|
||||
u64 reg_id_aa64zfr0;
|
||||
|
||||
@@ -363,6 +363,7 @@ struct arm64_cpu_capabilities {
|
||||
u8 field_pos;
|
||||
u8 field_width;
|
||||
u8 min_field_value;
|
||||
u8 max_field_value;
|
||||
u8 hwcap_type;
|
||||
bool sign;
|
||||
unsigned long hwcap;
|
||||
|
||||
@@ -209,7 +209,8 @@ static inline bool vcpu_is_el2(const struct kvm_vcpu *vcpu)
|
||||
|
||||
static inline bool __vcpu_el2_e2h_is_set(const struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
return ctxt_sys_reg(ctxt, HCR_EL2) & HCR_E2H;
|
||||
return (!cpus_have_final_cap(ARM64_HAS_HCR_NV1) ||
|
||||
(ctxt_sys_reg(ctxt, HCR_EL2) & HCR_E2H));
|
||||
}
|
||||
|
||||
static inline bool vcpu_el2_e2h_is_set(const struct kvm_vcpu *vcpu)
|
||||
|
||||
@@ -1181,6 +1181,8 @@
|
||||
par; \
|
||||
})
|
||||
|
||||
#define SYS_FIELD_VALUE(reg, field, val) reg##_##field##_##val
|
||||
|
||||
#define SYS_FIELD_GET(reg, field, val) \
|
||||
FIELD_GET(reg##_##field##_MASK, val)
|
||||
|
||||
@@ -1188,7 +1190,8 @@
|
||||
FIELD_PREP(reg##_##field##_MASK, val)
|
||||
|
||||
#define SYS_FIELD_PREP_ENUM(reg, field, val) \
|
||||
FIELD_PREP(reg##_##field##_MASK, reg##_##field##_##val)
|
||||
FIELD_PREP(reg##_##field##_MASK, \
|
||||
SYS_FIELD_VALUE(reg, field, val))
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -140,12 +140,42 @@ void dump_cpu_features(void)
|
||||
pr_emerg("0x%*pb\n", ARM64_NCAPS, &system_cpucaps);
|
||||
}
|
||||
|
||||
#define __ARM64_MAX_POSITIVE(reg, field) \
|
||||
((reg##_##field##_SIGNED ? \
|
||||
BIT(reg##_##field##_WIDTH - 1) : \
|
||||
BIT(reg##_##field##_WIDTH)) - 1)
|
||||
|
||||
#define __ARM64_MIN_NEGATIVE(reg, field) BIT(reg##_##field##_WIDTH - 1)
|
||||
|
||||
#define __ARM64_CPUID_FIELDS(reg, field, min_value, max_value) \
|
||||
.sys_reg = SYS_##reg, \
|
||||
.field_pos = reg##_##field##_SHIFT, \
|
||||
.field_width = reg##_##field##_WIDTH, \
|
||||
.sign = reg##_##field##_SIGNED, \
|
||||
.min_field_value = min_value, \
|
||||
.max_field_value = max_value,
|
||||
|
||||
/*
|
||||
* ARM64_CPUID_FIELDS() encodes a field with a range from min_value to
|
||||
* an implicit maximum that depends on the sign-ess of the field.
|
||||
*
|
||||
* An unsigned field will be capped at all ones, while a signed field
|
||||
* will be limited to the positive half only.
|
||||
*/
|
||||
#define ARM64_CPUID_FIELDS(reg, field, min_value) \
|
||||
.sys_reg = SYS_##reg, \
|
||||
.field_pos = reg##_##field##_SHIFT, \
|
||||
.field_width = reg##_##field##_WIDTH, \
|
||||
.sign = reg##_##field##_SIGNED, \
|
||||
.min_field_value = reg##_##field##_##min_value,
|
||||
__ARM64_CPUID_FIELDS(reg, field, \
|
||||
SYS_FIELD_VALUE(reg, field, min_value), \
|
||||
__ARM64_MAX_POSITIVE(reg, field))
|
||||
|
||||
/*
|
||||
* ARM64_CPUID_FIELDS_NEG() encodes a field with a range from an
|
||||
* implicit minimal value to max_value. This should be used when
|
||||
* matching a non-implemented property.
|
||||
*/
|
||||
#define ARM64_CPUID_FIELDS_NEG(reg, field, max_value) \
|
||||
__ARM64_CPUID_FIELDS(reg, field, \
|
||||
__ARM64_MIN_NEGATIVE(reg, field), \
|
||||
SYS_FIELD_VALUE(reg, field, max_value))
|
||||
|
||||
#define __ARM64_FTR_BITS(SIGNED, VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
|
||||
{ \
|
||||
@@ -407,6 +437,11 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr3[] = {
|
||||
ARM64_FTR_END,
|
||||
};
|
||||
|
||||
static const struct arm64_ftr_bits ftr_id_aa64mmfr4[] = {
|
||||
S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR4_EL1_E2H0_SHIFT, 4, 0),
|
||||
ARM64_FTR_END,
|
||||
};
|
||||
|
||||
static const struct arm64_ftr_bits ftr_ctr[] = {
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, 31, 1, 1), /* RES1 */
|
||||
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, CTR_EL0_DIC_SHIFT, 1, 1),
|
||||
@@ -724,6 +759,7 @@ static const struct __ftr_reg_entry {
|
||||
&id_aa64mmfr1_override),
|
||||
ARM64_FTR_REG(SYS_ID_AA64MMFR2_EL1, ftr_id_aa64mmfr2),
|
||||
ARM64_FTR_REG(SYS_ID_AA64MMFR3_EL1, ftr_id_aa64mmfr3),
|
||||
ARM64_FTR_REG(SYS_ID_AA64MMFR4_EL1, ftr_id_aa64mmfr4),
|
||||
|
||||
/* Op1 = 1, CRn = 0, CRm = 0 */
|
||||
ARM64_FTR_REG(SYS_GMID_EL1, ftr_gmid),
|
||||
@@ -919,7 +955,8 @@ static void init_cpu_ftr_reg(u32 sys_reg, u64 new)
|
||||
pr_warn("%s[%d:%d]: %s to %llx\n",
|
||||
reg->name,
|
||||
ftrp->shift + ftrp->width - 1,
|
||||
ftrp->shift, str, tmp);
|
||||
ftrp->shift, str,
|
||||
tmp & (BIT(ftrp->width) - 1));
|
||||
} else if ((ftr_mask & reg->override->val) == ftr_mask) {
|
||||
reg->override->val &= ~ftr_mask;
|
||||
pr_warn("%s[%d:%d]: impossible override, ignored\n",
|
||||
@@ -1047,6 +1084,7 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info)
|
||||
init_cpu_ftr_reg(SYS_ID_AA64MMFR1_EL1, info->reg_id_aa64mmfr1);
|
||||
init_cpu_ftr_reg(SYS_ID_AA64MMFR2_EL1, info->reg_id_aa64mmfr2);
|
||||
init_cpu_ftr_reg(SYS_ID_AA64MMFR3_EL1, info->reg_id_aa64mmfr3);
|
||||
init_cpu_ftr_reg(SYS_ID_AA64MMFR4_EL1, info->reg_id_aa64mmfr4);
|
||||
init_cpu_ftr_reg(SYS_ID_AA64PFR0_EL1, info->reg_id_aa64pfr0);
|
||||
init_cpu_ftr_reg(SYS_ID_AA64PFR1_EL1, info->reg_id_aa64pfr1);
|
||||
init_cpu_ftr_reg(SYS_ID_AA64ZFR0_EL1, info->reg_id_aa64zfr0);
|
||||
@@ -1418,6 +1456,7 @@ u64 __read_sysreg_by_encoding(u32 sys_id)
|
||||
read_sysreg_case(SYS_ID_AA64MMFR1_EL1);
|
||||
read_sysreg_case(SYS_ID_AA64MMFR2_EL1);
|
||||
read_sysreg_case(SYS_ID_AA64MMFR3_EL1);
|
||||
read_sysreg_case(SYS_ID_AA64MMFR4_EL1);
|
||||
read_sysreg_case(SYS_ID_AA64ISAR0_EL1);
|
||||
read_sysreg_case(SYS_ID_AA64ISAR1_EL1);
|
||||
read_sysreg_case(SYS_ID_AA64ISAR2_EL1);
|
||||
@@ -1451,11 +1490,28 @@ has_always(const struct arm64_cpu_capabilities *entry, int scope)
|
||||
static bool
|
||||
feature_matches(u64 reg, const struct arm64_cpu_capabilities *entry)
|
||||
{
|
||||
int val = cpuid_feature_extract_field_width(reg, entry->field_pos,
|
||||
entry->field_width,
|
||||
entry->sign);
|
||||
int val, min, max;
|
||||
u64 tmp;
|
||||
|
||||
return val >= entry->min_field_value;
|
||||
val = cpuid_feature_extract_field_width(reg, entry->field_pos,
|
||||
entry->field_width,
|
||||
entry->sign);
|
||||
|
||||
tmp = entry->min_field_value;
|
||||
tmp <<= entry->field_pos;
|
||||
|
||||
min = cpuid_feature_extract_field_width(tmp, entry->field_pos,
|
||||
entry->field_width,
|
||||
entry->sign);
|
||||
|
||||
tmp = entry->max_field_value;
|
||||
tmp <<= entry->field_pos;
|
||||
|
||||
max = cpuid_feature_extract_field_width(tmp, entry->field_pos,
|
||||
entry->field_width,
|
||||
entry->sign);
|
||||
|
||||
return val >= min && val <= max;
|
||||
}
|
||||
|
||||
static u64
|
||||
@@ -1739,6 +1795,28 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry,
|
||||
return !meltdown_safe;
|
||||
}
|
||||
|
||||
static bool has_nv1(const struct arm64_cpu_capabilities *entry, int scope)
|
||||
{
|
||||
/*
|
||||
* Although the Apple M2 family appears to support NV1, the
|
||||
* PTW barfs on the nVHE EL2 S1 page table format. Pretend
|
||||
* that it doesn't support NV1 at all.
|
||||
*/
|
||||
static const struct midr_range nv1_ni_list[] = {
|
||||
MIDR_ALL_VERSIONS(MIDR_APPLE_M2_BLIZZARD),
|
||||
MIDR_ALL_VERSIONS(MIDR_APPLE_M2_AVALANCHE),
|
||||
MIDR_ALL_VERSIONS(MIDR_APPLE_M2_BLIZZARD_PRO),
|
||||
MIDR_ALL_VERSIONS(MIDR_APPLE_M2_AVALANCHE_PRO),
|
||||
MIDR_ALL_VERSIONS(MIDR_APPLE_M2_BLIZZARD_MAX),
|
||||
MIDR_ALL_VERSIONS(MIDR_APPLE_M2_AVALANCHE_MAX),
|
||||
{}
|
||||
};
|
||||
|
||||
return (__system_matches_cap(ARM64_HAS_NESTED_VIRT) &&
|
||||
!(has_cpuid_feature(entry, scope) ||
|
||||
is_midr_in_range_list(read_cpuid_id(), nv1_ni_list)));
|
||||
}
|
||||
|
||||
#if defined(ID_AA64MMFR0_EL1_TGRAN_LPA2) && defined(ID_AA64MMFR0_EL1_TGRAN_2_SUPPORTED_LPA2)
|
||||
static bool has_lpa2_at_stage1(u64 mmfr0)
|
||||
{
|
||||
@@ -2739,6 +2817,13 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
|
||||
.type = ARM64_CPUCAP_SYSTEM_FEATURE,
|
||||
.matches = has_lpa2,
|
||||
},
|
||||
{
|
||||
.desc = "NV1",
|
||||
.capability = ARM64_HAS_HCR_NV1,
|
||||
.type = ARM64_CPUCAP_SYSTEM_FEATURE,
|
||||
.matches = has_nv1,
|
||||
ARM64_CPUID_FIELDS_NEG(ID_AA64MMFR4_EL1, E2H0, NI_NV1)
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
|
||||
@@ -447,6 +447,7 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
|
||||
info->reg_id_aa64mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);
|
||||
info->reg_id_aa64mmfr2 = read_cpuid(ID_AA64MMFR2_EL1);
|
||||
info->reg_id_aa64mmfr3 = read_cpuid(ID_AA64MMFR3_EL1);
|
||||
info->reg_id_aa64mmfr4 = read_cpuid(ID_AA64MMFR4_EL1);
|
||||
info->reg_id_aa64pfr0 = read_cpuid(ID_AA64PFR0_EL1);
|
||||
info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1);
|
||||
info->reg_id_aa64zfr0 = read_cpuid(ID_AA64ZFR0_EL1);
|
||||
|
||||
@@ -584,25 +584,32 @@ SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)
|
||||
mov_q x1, INIT_SCTLR_EL1_MMU_OFF
|
||||
|
||||
/*
|
||||
* Fruity CPUs seem to have HCR_EL2.E2H set to RES1,
|
||||
* making it impossible to start in nVHE mode. Is that
|
||||
* compliant with the architecture? Absolutely not!
|
||||
* Compliant CPUs advertise their VHE-onlyness with
|
||||
* ID_AA64MMFR4_EL1.E2H0 < 0. HCR_EL2.E2H can be
|
||||
* RES1 in that case.
|
||||
*
|
||||
* Fruity CPUs seem to have HCR_EL2.E2H set to RES1, but
|
||||
* don't advertise it (they predate this relaxation).
|
||||
*/
|
||||
mrs_s x0, SYS_ID_AA64MMFR4_EL1
|
||||
ubfx x0, x0, #ID_AA64MMFR4_EL1_E2H0_SHIFT, #ID_AA64MMFR4_EL1_E2H0_WIDTH
|
||||
tbnz x0, #(ID_AA64MMFR4_EL1_E2H0_SHIFT + ID_AA64MMFR4_EL1_E2H0_WIDTH - 1), 1f
|
||||
|
||||
mrs x0, hcr_el2
|
||||
and x0, x0, #HCR_E2H
|
||||
cbz x0, 1f
|
||||
|
||||
cbz x0, 2f
|
||||
1:
|
||||
/* Set a sane SCTLR_EL1, the VHE way */
|
||||
pre_disable_mmu_workaround
|
||||
msr_s SYS_SCTLR_EL12, x1
|
||||
mov x2, #BOOT_CPU_FLAG_E2H
|
||||
b 2f
|
||||
b 3f
|
||||
|
||||
1:
|
||||
2:
|
||||
pre_disable_mmu_workaround
|
||||
msr sctlr_el1, x1
|
||||
mov x2, xzr
|
||||
2:
|
||||
3:
|
||||
__init_el2_nvhe_prepare_eret
|
||||
|
||||
mov w0, #BOOT_CPU_MODE_EL2
|
||||
|
||||
@@ -133,6 +133,13 @@ static u64 limit_nv_id_reg(u32 id, u64 val)
|
||||
val |= FIELD_PREP(NV_FTR(MMFR2, TTL), 0b0001);
|
||||
break;
|
||||
|
||||
case SYS_ID_AA64MMFR4_EL1:
|
||||
val = 0;
|
||||
if (!cpus_have_final_cap(ARM64_HAS_HCR_NV1))
|
||||
val |= FIELD_PREP(NV_FTR(MMFR4, E2H0),
|
||||
ID_AA64MMFR4_EL1_E2H0_NI_NV1);
|
||||
break;
|
||||
|
||||
case SYS_ID_AA64DFR0_EL1:
|
||||
/* Only limited support for PMU, Debug, BPs and WPs */
|
||||
val &= (NV_FTR(DFR0, PMUVer) |
|
||||
|
||||
@@ -1685,7 +1685,8 @@ static u64 read_sanitised_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
|
||||
u64 __f_val = FIELD_GET(reg##_##field##_MASK, val); \
|
||||
(val) &= ~reg##_##field##_MASK; \
|
||||
(val) |= FIELD_PREP(reg##_##field##_MASK, \
|
||||
min(__f_val, (u64)reg##_##field##_##limit)); \
|
||||
min(__f_val, \
|
||||
(u64)SYS_FIELD_VALUE(reg, field, limit))); \
|
||||
(val); \
|
||||
})
|
||||
|
||||
@@ -2174,6 +2175,16 @@ static bool access_spsr(struct kvm_vcpu *vcpu,
|
||||
return true;
|
||||
}
|
||||
|
||||
static u64 reset_hcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
||||
{
|
||||
u64 val = r->val;
|
||||
|
||||
if (!cpus_have_final_cap(ARM64_HAS_HCR_NV1))
|
||||
val |= HCR_E2H;
|
||||
|
||||
return __vcpu_sys_reg(vcpu, r->reg) = val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Architected system registers.
|
||||
* Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
|
||||
@@ -2349,7 +2360,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
ID_AA64MMFR2_EL1_NV |
|
||||
ID_AA64MMFR2_EL1_CCIDX)),
|
||||
ID_SANITISED(ID_AA64MMFR3_EL1),
|
||||
ID_UNALLOCATED(7,4),
|
||||
ID_SANITISED(ID_AA64MMFR4_EL1),
|
||||
ID_UNALLOCATED(7,5),
|
||||
ID_UNALLOCATED(7,6),
|
||||
ID_UNALLOCATED(7,7),
|
||||
@@ -2665,7 +2676,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
EL2_REG_VNCR(VMPIDR_EL2, reset_unknown, 0),
|
||||
EL2_REG(SCTLR_EL2, access_rw, reset_val, SCTLR_EL2_RES1),
|
||||
EL2_REG(ACTLR_EL2, access_rw, reset_val, 0),
|
||||
EL2_REG_VNCR(HCR_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(HCR_EL2, reset_hcr, 0),
|
||||
EL2_REG(MDCR_EL2, access_rw, reset_val, 0),
|
||||
EL2_REG(CPTR_EL2, access_rw, reset_val, CPTR_NVHE_EL2_RES1),
|
||||
EL2_REG_VNCR(HSTR_EL2, reset_val, 0),
|
||||
|
||||
@@ -35,6 +35,7 @@ HAS_GENERIC_AUTH_IMP_DEF
|
||||
HAS_GIC_CPUIF_SYSREGS
|
||||
HAS_GIC_PRIO_MASKING
|
||||
HAS_GIC_PRIO_RELAXED_SYNC
|
||||
HAS_HCR_NV1
|
||||
HAS_HCX
|
||||
HAS_LDAPR
|
||||
HAS_LPA2
|
||||
|
||||
@@ -1791,6 +1791,43 @@ UnsignedEnum 3:0 TCRX
|
||||
EndEnum
|
||||
EndSysreg
|
||||
|
||||
Sysreg ID_AA64MMFR4_EL1 3 0 0 7 4
|
||||
Res0 63:40
|
||||
UnsignedEnum 39:36 E3DSE
|
||||
0b0000 NI
|
||||
0b0001 IMP
|
||||
EndEnum
|
||||
Res0 35:28
|
||||
SignedEnum 27:24 E2H0
|
||||
0b0000 IMP
|
||||
0b1110 NI_NV1
|
||||
0b1111 NI
|
||||
EndEnum
|
||||
UnsignedEnum 23:20 NV_frac
|
||||
0b0000 NV_NV2
|
||||
0b0001 NV2_ONLY
|
||||
EndEnum
|
||||
UnsignedEnum 19:16 FGWTE3
|
||||
0b0000 NI
|
||||
0b0001 IMP
|
||||
EndEnum
|
||||
UnsignedEnum 15:12 HACDBS
|
||||
0b0000 NI
|
||||
0b0001 IMP
|
||||
EndEnum
|
||||
UnsignedEnum 11:8 ASID2
|
||||
0b0000 NI
|
||||
0b0001 IMP
|
||||
EndEnum
|
||||
SignedEnum 7:4 EIESB
|
||||
0b0000 NI
|
||||
0b0001 ToEL3
|
||||
0b0010 ToELx
|
||||
0b1111 ANY
|
||||
EndEnum
|
||||
Res0 3:0
|
||||
EndSysreg
|
||||
|
||||
Sysreg SCTLR_EL1 3 0 1 0 0
|
||||
Field 63 TIDCP
|
||||
Field 62 SPINTMASK
|
||||
|
||||
Reference in New Issue
Block a user