arm64: Add support for FEAT_E2H0, or lack thereof
Marc Zyngier
maintainerv3 cover arm64: Add support for FEAT_E2H0, or lack thereof
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 has a number of consequences, both at boot time and for KVM, though the changes at that level are pretty minor.
The real meat of this series is on the cpufeature front, as FEAT_E2H0 is a *negative* feature, where 0b1111 (-1) represents E2H being RES1 and 0b1110 (-2) additionally indicates that NV1 is RES0. Fun, isn't it?
As a bonus, a popular crop of non-compliant HW gets promoted as the first batch of non-zero E2H0 users, making things easy for KVM.
This series is a prefix for the NV support. It also conflicts badly with Ard's pre-LVA2 rework. FWIW, I've pushed out a resolution of the conflict at [3].
* From v2 [2]:
- Moved some SYS_FIELD_VALUE() usage to the correct patch
- Fixed a couple of spelling mistakes
- Picked RBs from Suzuki (thanks!)
* From v1 [1]:
- Added a SYS_FIELD_VALUE() helper to handle the concatenation of various fields
- Only test for the top bit of ID_AA64MMFR4_EL1.E2H0 to decide whether HCR_EL2.E2H is RES1.
- Picked RBs from Oliver (thanks!)
- Rebased on 6.7-rc2
[1] https://lore.kernel.org/r/[email protected]
[2] https://lore.kernel.org/r/[email protected]
[3] https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms.git/log/?h=arm64/pre-lva2
Marc Zyngier (13):
arm64: Add macro to compose a sysreg field value
arm64: cpufeatures: Correctly handle signed values
arm64: cpufeature: Correctly display signed override values
arm64: sysreg: Add layout for ID_AA64MMFR4_EL1
arm64: cpufeature: Add ID_AA64MMFR4_EL1 handling
arm64: cpufeature: Detect E2H0 not being implemented
arm64: cpufeature: Detect HCR_EL2.NV1 being RES0
arm64: Treat HCR_EL2.E2H as RES1 when ID_AA64MMFR4_EL1.E2H0 is
negative
arm64: Add override for ID_AA64MMFR4_EL1.E2H0
arm64: Add MIDR-based override infrastructure
arm64: Add MIDR-based overrides for ID_AA64MMFR4_EL1.E2H0
KVM: arm64: Expose ID_AA64MMFR4_EL1 to guests
KVM: arm64: Force guest's HCR_EL2.E2H RES1 when NV1 is not implemented
arch/arm64/include/asm/cpu.h | 1 + arch/arm64/include/asm/cpufeature.h | 2 + arch/arm64/include/asm/kvm_emulate.h | 3 +- arch/arm64/include/asm/sysreg.h | 5 +- arch/arm64/kernel/cpufeature.c | 91 +++++++++++++++++++++++++--- arch/arm64/kernel/cpuinfo.c | 1 + arch/arm64/kernel/head.S | 23 ++++--- arch/arm64/kernel/idreg-override.c | 65 ++++++++++++++++++++ arch/arm64/kvm/nested.c | 4 ++ arch/arm64/kvm/sys_regs.c | 17 +++++- arch/arm64/tools/cpucaps | 2 + arch/arm64/tools/sysreg | 37 +++++++++++ 12 files changed, 228 insertions(+), 23 deletions(-)
Signature · 1 line
2.39.2
Marc Zyngier
maintainerPATCH v3 1/13 arm64: Add macro to compose a sysreg field value
A common idiom is to compose a tupple (reg, field, val) into a symbol matching an autogenerated definition.
Add a help performing the concatenation and replace it when open-coded implementations exist.
--- arch/arm64/include/asm/sysreg.h | 5 ++++- arch/arm64/kvm/sys_regs.c | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-)
arch/arm64/include/asm/sysreg.h +4 -1
@@ -1156,6 +1156,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)
@@ -1163,7 +1165,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
arch/arm64/kvm/sys_regs.c +2 -1
@@ -1561,7 +1561,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); \ })
Signature · 1 line
2.39.2
Marc Zyngier
maintainerPATCH v3 2/13 arm64: cpufeatures: Correctly handle signed values
In reply to Marc ZyngierAlthough we've had signed values for some features such as PMUv3 and FP, the code that handles the comparaison with some limit has a couple of annoying issues:
- the min_field_value is always unsigned, meaning that we cannot easily compare it with a negative value
- it is not possible to have a range of values, let alone a range of negative values
Fix this by:
- adding an upper limit to the comparison, defaulting to all bits being set to the maximum positive value
- ensuring that the signess of the min and max values are taken into account
A ARM64_CPUID_FIELDS_NEG() macro is provided for signed features, but nothing is using it yet.
--- arch/arm64/include/asm/cpufeature.h | 1 + arch/arm64/kernel/cpufeature.c | 65 +++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 9 deletions(-)
arch/arm64/include/asm/cpufeature.h +1 -0
@@ -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;
arch/arm64/kernel/cpufeature.c +56 -9
79 lines not on this page · raw message
Signature · 1 line
2.39.2
Will Deacon
maintainerRe: [PATCH v3 02/13] arm64: cpufeatures: Correctly handle signed values
Quoted 44 lines · Marc Zyngier
Although we've had signed values for some features such as PMUv3 and FP, the code that handles the comparaison with some limit has a couple of annoying issues:
- the
min_field_valueis always unsigned, meaning that we cannot easily compare it with a negative value- it is not possible to have a range of values, let alone a range of negative values
Fix this by:
- adding an upper limit to the comparison, defaulting to all bits being set to the maximum positive value
- ensuring that the signess of the min and max values are taken into account
A
ARM64_CPUID_FIELDS_NEG()macro is provided for signed features, but nothing is using it yet.Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/include/asm/cpufeature.h | 1 + arch/arm64/kernel/cpufeature.c | 65 +++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 9 deletions(-)arch/arm64/include/asm/cpufeature.h +1
-0@@ -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;arch/arm64/kernel/cpufeature.c +0
-0
arch/arm64/kernel/cpufeature.c void dump_cpu_features(void) · line 148
Quoted 9 lines
arch/arm64/kernel/cpufeature.c +6
-0@@ -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)
I'm struggling to grok these two macros. For example, let's say I have a 4-bit signed field. In that case, the maximum positive value is 7 (0b0111) and the minimum negative value is -8 (0b1000), but the macros above appear to give 0b1000 for both.
Will
Marc Zyngier
maintainerRe: [PATCH v3 02/13] arm64: cpufeatures: Correctly handle signed values
Quoted 49 lines · Will Deacon
Quoted 47 lines · Marc Zyngier
Although we've had signed values for some features such as PMUv3 and FP, the code that handles the comparaison with some limit has a couple of annoying issues:
- the
min_field_valueis always unsigned, meaning that we cannot easily compare it with a negative value- it is not possible to have a range of values, let alone a range of negative values
Fix this by:
- adding an upper limit to the comparison, defaulting to all bits being set to the maximum positive value
- ensuring that the signess of the min and max values are taken into account
A
ARM64_CPUID_FIELDS_NEG()macro is provided for signed features, but nothing is using it yet.Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/include/asm/cpufeature.h | 1 + arch/arm64/kernel/cpufeature.c | 65 +++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 9 deletions(-)arch/arm64/include/asm/cpufeature.h +1
-0@@ -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;arch/arm64/kernel/cpufeature.c +0
-0@@ -140,12 +140,42 @@ void dump_cpu_features(void)pr_emerg ("0x%*pb\n" ,ARM64_NCAPS , &system_cpucaps); }
Quoted 11 lines
arch/arm64/kernel/cpufeature.c void dump_cpu_features(void) · line 148
Quoted 6 lines
arch/arm64/kernel/cpufeature.c +6
-0+#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)I'm struggling to grok these two macros. For example, let's say I have a 4-bit signed field. In that case, the maximum positive value is 7 (0b0111) and the minimum negative value is -8 (0b1000), but the macros above appear to give 0b1000 for both.
Crap. Well spotted. The signed maximum needs to be further adjusted like this:
#define __ARM64_MAX_POSITIVE(reg, field) \ ((reg##_##field##_SIGNED ? \ BIT(reg##_##field##_WIDTH - 1) - 1: \ BIT(reg##_##field##_WIDTH)) - 1)
Thanks,
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Marc Zyngier
maintainerRe: [PATCH v3 02/13] arm64: cpufeatures: Correctly handle signed values
Quoted 3 lines · Marc Zyngier
On Mon, 11 Dec 2023 12:24:16 +0000,
Will Deacon <[email protected]> wrote:
[...]
Quoted 20 lines
Quoted 8 lines
Quoted 8 lines
arch/arm64/kernel/cpufeature.c +0
-0@@ -140,12 +140,42 @@ void dump_cpu_features(void)pr_emerg ("0x%*pb\n" ,ARM64_NCAPS , &system_cpucaps); }Quoted 11 lines
arch/arm64/kernel/cpufeature.c void dump_cpu_features(void) · line 148
Quoted 6 lines
arch/arm64/kernel/cpufeature.c +6
-0+#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)I'm struggling to grok these two macros. For example, let's say I have a 4-bit signed field. In that case, the maximum positive value is 7 (0b0111) and the minimum negative value is -8 (0b1000), but the macros above appear to give 0b1000 for both.
Quoted 2 lines
Crap. Well spotted. The signed maximum needs to be further adjusted like this:
Actually, scratch that. The original code is correct and does return 0b0111 for the max of a signed 4 bit value, and 0b1000 for the min. The clue is in the bracketing (the '- 1' is applied to the result of the conditional expression in the max case).
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Will Deacon
maintainerRe: [PATCH v3 02/13] arm64: cpufeatures: Correctly handle signed values
Quoted 28 lines · Marc Zyngier
Quoted 3 lines · Marc Zyngier
On Mon, 11 Dec 2023 12:24:16 +0000,
Will Deacon <[email protected]> wrote:[...]
Quoted 20 lines
Quoted 19 lines
Quoted 5 lines
arch/arm64/kernel/cpufeature.c +0
-0arch/arm64/kernel/cpufeature.c void dump_cpu_features(void) · line 148
Quoted 9 lines
arch/arm64/kernel/cpufeature.c +6
-0@@ -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)I'm struggling to grok these two macros. For example, let's say I have a 4-bit signed field. In that case, the maximum positive value is 7 (0b0111) and the minimum negative value is -8 (0b1000), but the macros above appear to give 0b1000 for both.
Quoted 7 lines
Quoted 2 lines
Crap. Well spotted. The signed maximum needs to be further adjusted like this:
Actually, scratch that. The original code is correct and does return 0b0111 for the max of a signed 4 bit value, and 0b1000 for the min. The clue is in the bracketing (the '- 1' is applied to the result of the conditional expression in the max case).
Gah, sorry, that trailing '- 1' really caught me out with the formatting!
Will
Marc Zyngier
maintainerPATCH v3 3/13 arm64: cpufeature: Correctly display signed override values
In reply to Marc ZyngierWhen a field gets overriden, the kernel indicates the result of the override in dmesg. This works well with unsigned fields, but results in a pretty ugly output when the field is signed.
Truncate the field to its width before displaying it.
--- arch/arm64/kernel/cpufeature.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
arch/arm64/kernel/cpufeature.c +2 -1
@@ -949,7 +949,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",
Signature · 1 line
2.39.2
Marc Zyngier
maintainerPATCH v3 4/13 arm64: sysreg: Add layout for ID_AA64MMFR4_EL1
In reply to Marc ZyngierARMv9.5 has infroduced ID_AA64MMFR4_EL1 with a bunch of new features. Add the corresponding layout.
This is extracted from the public ARM SysReg_xml_A_profile-2023-09 delivery, timestamped d55f5af8e09052abe92a02adf820deea2eaed717.
--- arch/arm64/tools/sysreg | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
arch/arm64/tools/sysreg +37 -0
@@ -1675,6 +1675,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
Signature · 1 line
2.39.2
Marc Zyngier
maintainerPATCH v3 5/13 arm64: cpufeature: Add ID_AA64MMFR4_EL1 handling
In reply to Marc ZyngierAdd ID_AA64MMFR4_EL1 to the list of idregs the kernel knows about, and describe the E2H0 field.
--- arch/arm64/include/asm/cpu.h | 1 + arch/arm64/kernel/cpufeature.c | 7 +++++++ arch/arm64/kernel/cpuinfo.c | 1 + 3 files changed, 9 insertions(+)
arch/arm64/include/asm/cpu.h +1 -0
@@ -57,6 +57,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;
arch/arm64/kernel/cpufeature.c +7 -0
@@ -437,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 ),
@@ -754,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),
@@ -1078,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);
arch/arm64/kernel/cpuinfo.c +1 -0
@@ -452,6 +452,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 );
Signature · 1 line
2.39.2
Marc Zyngier
maintainerPATCH v3 6/13 arm64: cpufeature: Detect E2H0 not being implemented
In reply to Marc ZyngierFEAT_E2H0 is a new feature that indicates whether or not HCR_EL2.E2H can be set to 0. Amusingly, this feature is set to 0 when implemented, and otherwise negative.
Add a new capability and detection infrastructure to detect the absence of FEAT_E2H0.
--- arch/arm64/kernel/cpufeature.c | 7 +++++++ arch/arm64/tools/cpucaps | 1 + 2 files changed, 8 insertions(+)
arch/arm64/kernel/cpufeature.c +7 -0
@@ -2786,6 +2786,13 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, ARM64_CPUID_FIELDS(ID_AA64MMFR2_EL1, EVT, IMP) }, + { + .desc = "FEAT_E2H0 not implemented", + .capability = ARM64_HCR_E2H_RES1, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_cpuid_feature, +ARM64_CPUID_FIELDS_NEG (ID_AA64MMFR4_EL1 ,E2H0 ,NI ) + }, {}, };
arch/arm64/tools/cpucaps +1 -0
@@ -52,6 +52,7 @@ HAS_TIDCP1 HAS_TLB_RANGE HAS_VIRT_HOST_EXTN HAS_WFXT +HCR_E2H_RES1 HW_DBM KVM_HVHE KVM_PROTECTED_MODE
Signature · 1 line
2.39.2
Will Deacon
maintainerRe: [PATCH v3 06/13] arm64: cpufeature: Detect E2H0 not being implemented
Quoted 18 lines · Marc Zyngier
FEAT_E2H0is a new feature that indicates whether or not HCR_EL2.E2H can be set to 0. Amusingly, this feature is set to 0 when implemented, and otherwise negative.Add a new capability and detection infrastructure to detect the absence of
FEAT_E2H0.Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/kernel/cpufeature.c | 7 +++++++ arch/arm64/tools/cpucaps | 1 + 2 files changed, 8 insertions(+)arch/arm64/kernel/cpufeature.c +0
-0
arch/arm64/kernel/cpufeature.c static const struct arm64_cpu_capabilities arm64_features[] = { · line 2792
Quoted 7 lines
arch/arm64/kernel/cpufeature.c +4
-0@@ -2786,6 +2786,13 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature,ARM64_CPUID_FIELDS (ID_AA64MMFR2_EL1 ,EVT ,IMP ) }, + { + .desc ="FEAT_E2H0 not implemented" , + .capability = ARM64_HCR_E2H_RES1, + .type = ARM64_CPUCAP_SYSTEM_FEATURE,
Is ARM64_CPUCAP_SYSTEM_FEATURE the right thing to use here? For example, if the boot CPUs have this feature but a late CPU doesn't, then I think we should be ok as long as we're using VHE, but I don't think the above takes that into account.
Maybe we shouldn't be adding new capabilities for this at all, and instead just look at the sanitised register value when we need to?
Will
Marc Zyngier
maintainerRe: [PATCH v3 06/13] arm64: cpufeature: Detect E2H0 not being implemented
Quoted 33 lines · Will Deacon
Quoted 18 lines · Marc Zyngier
FEAT_E2H0is a new feature that indicates whether or not HCR_EL2.E2H can be set to 0. Amusingly, this feature is set to 0 when implemented, and otherwise negative.Add a new capability and detection infrastructure to detect the absence of
FEAT_E2H0.Signed-off-by Marc Zyngier <[email protected]>--- arch/arm64/kernel/cpufeature.c | 7 +++++++ arch/arm64/tools/cpucaps | 1 + 2 files changed, 8 insertions(+)arch/arm64/kernel/cpufeature.c +0
-0arch/arm64/kernel/cpufeature.c static const struct arm64_cpu_capabilities arm64_features[] = { · line 2792
Quoted 7 lines
arch/arm64/kernel/cpufeature.c +4
-0@@ -2786,6 +2786,13 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature,ARM64_CPUID_FIELDS (ID_AA64MMFR2_EL1 ,EVT ,IMP ) }, + { + .desc ="FEAT_E2H0 not implemented" , + .capability = ARM64_HCR_E2H_RES1, + .type = ARM64_CPUCAP_SYSTEM_FEATURE,Is
ARM64_CPUCAP_SYSTEM_FEATUREthe right thing to use here? For example, if the boot CPUs have this feature but a late CPU doesn't, then I think we should be ok as long as we're using VHE, but I don't think the above takes that into account.
Quoted 2 lines
Maybe we shouldn't be adding new capabilities for this at all, and instead just look at the sanitised register value when we need to?
That's probably good enough for this particular use of E2H0, as this is not used on any fast path.
However, when E2H0 is 0b1110 (NV1 being RES0, and thus the guest's own HCR_EL2.E2H being RES1), we need to sanitise the guest's HCR_EL2 no matter what the guest writes there.
With that in mind, __vcpu_el2_e2h_is_set() using the sanitised value would result in a binary search on a lot of very hot paths, something that I'm keen to avoid.
So while I'm happy to drop this particular cap, I'll keep the one introduced in patch #6.
Thanks,
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Marc Zyngier
maintainerPATCH v3 7/13 arm64: cpufeature: Detect HCR_EL2.NV1 being RES0
In reply to Marc ZyngierA variant of FEAT_E2H0 not being implemented exists in the form of HCR_EL2.E2H being RES1 *and* HCR_EL2.NV1 being RES0 (indicating that only VHE is supported on the host and nested guests).
Add the necessary infrastructure for this new CPU capability.
--- arch/arm64/kernel/cpufeature.c | 7 +++++++ arch/arm64/tools/cpucaps | 1 + 2 files changed, 8 insertions(+)
arch/arm64/kernel/cpufeature.c +7 -0
@@ -2793,6 +2793,13 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, ARM64_CPUID_FIELDS_NEG(ID_AA64MMFR4_EL1, E2H0, NI) }, + { + .desc = "FEAT_E2H0 not implemented (NV1 RES0)", + .capability = ARM64_HCR_NV1_RES0, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_cpuid_feature, +ARM64_CPUID_FIELDS_NEG (ID_AA64MMFR4_EL1 ,E2H0 ,NI_NV1 ) + }, {}, };
arch/arm64/tools/cpucaps +1 -0
@@ -53,6 +53,7 @@ HAS_TLB_RANGE HAS_VIRT_HOST_EXTN HAS_WFXT HCR_E2H_RES1 +HCR_NV1_RES0 HW_DBM KVM_HVHE KVM_PROTECTED_MODE
Signature · 1 line
2.39.2
Marc Zyngier
maintainerPATCH v3 8/13 arm64: Treat HCR_EL2.E2H as RES1 when ID_AA64MMFR4_EL1.E2H0 is negative
In reply to Marc ZyngierFor CPUs that have ID_AA64MMFR4_EL1.E2H0 as negative, it is important to avoid the boot path that sets HCR_EL2.E2H=0. Fortunately, we already have this path to cope with fruity CPUs.
Tweak init_el2 to look at ID_AA64MMFR4_EL1.E2H0 first.
--- arch/arm64/kernel/head.S | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-)
arch/arm64/kernel/head.S +15 -8
@@ -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
Signature · 1 line
2.39.2
Marc Zyngier
maintainerPATCH v3 9/13 arm64: Add override for ID_AA64MMFR4_EL1.E2H0
In reply to Marc ZyngierAllow ID_AA64MMFR4_EL1.E2H0 to be overriden from the command-line. This will subsequently be of use for non-compliant CPUs.
--- arch/arm64/include/asm/cpufeature.h | 1 + arch/arm64/kernel/cpufeature.c | 4 +++- arch/arm64/kernel/idreg-override.c | 10 ++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-)
arch/arm64/include/asm/cpufeature.h +1 -0
@@ -901,6 +901,7 @@ s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new, s64 cur);struct arm64_ftr_reg *get_arm64_ftr_reg (u32 sys_id);extern struct arm64_ftr_override id_aa64mmfr1_override; +extern struct arm64_ftr_override id_aa64mmfr4_override;extern struct arm64_ftr_override id_aa64pfr0_override;extern struct arm64_ftr_override id_aa64pfr1_override;extern struct arm64_ftr_override id_aa64zfr0_override;
arch/arm64/kernel/cpufeature.c +3 -1
@@ -691,6 +691,7 @@ static const struct arm64_ftr_bits ftr_raz[] = { __ARM64_FTR_REG_OVERRIDE(#id, id, table, &no_override)struct arm64_ftr_override __ro_after_init id_aa64mmfr1_override; +struct arm64_ftr_override __ro_after_init id_aa64mmfr4_override;struct arm64_ftr_override __ro_after_init id_aa64pfr0_override;struct arm64_ftr_override __ro_after_init id_aa64pfr1_override;struct arm64_ftr_override __ro_after_init id_aa64zfr0_override;
@@ -759,7 +760,8 @@ 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), +ARM64_FTR_REG_OVERRIDE (SYS_ID_AA64MMFR4_EL1 , ftr_id_aa64mmfr4, + &id_aa64mmfr4_override),/* Op1 = 1, CRn = 0, CRm = 0 */ ARM64_FTR_REG(SYS_GMID_EL1, ftr_gmid),
arch/arm64/kernel/idreg-override.c +10 -0
@@ -55,6 +55,15 @@ static const struct ftr_set_desc mmfr1 __initconst = { }, }; +static const struct ftr_set_desc mmfr4__initconst = { + .name ="id_aa64mmfr4" , + .override = &id_aa64mmfr4_override, + .fields = { +FIELD ("e2h0" ,ID_AA64MMFR4_EL1_E2H0_SHIFT ,NULL ), + {} + }, +}; +static bool __init pfr0_sve_filter (u64 val) {/*
@@ -161,6 +170,7 @@ static const struct ftr_set_desc sw_features __initconst = {static const struct ftr_set_desc *const regs[]__initconst = { &mmfr1, + &mmfr4, &pfr0, &pfr1, &isar1,
Signature · 1 line
2.39.2
Marc Zyngier
maintainerPATCH v3 10/13 arm64: Add MIDR-based override infrastructure
In reply to Marc ZyngierIn order to be able to use the override infrastructure to "fix" CPUs that have deviated from the architecture spec, add a new pile of hacks on top of the existing one, allowing a feature to be overriden based on matching a MIDR list.
--- arch/arm64/kernel/idreg-override.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
arch/arm64/kernel/idreg-override.c +19 -0
@@ -321,6 +321,23 @@ static __init void parse_cmdline(void)__parse_cmdline (prop, true); } +struct midr_override_data { +const char feature[FTR_ALIAS_OPTION_LEN ]; +const struct midr_range ranges[]; +}; + +static const struct midr_override_data *const midr_ovr_data[]__initconst = { +}; + +static void __init apply_midr_overrides (void ) +{ +const u64 midr =read_cpuid_id (); + +for (int i =0 ; i <ARRAY_SIZE (midr_ovr_data); i++) +if (is_midr_in_range_list (midr, midr_ovr_data[i]->ranges)) +__parse_cmdline (midr_ovr_data[i]->feature, false); +} +/* Keep checkers quiet */ void init_feature_override (u64 boot_status);
@@ -337,6 +354,8 @@ asmlinkage void __init init_feature_override(u64 boot_status) __boot_status = boot_status; +apply_midr_overrides (); +parse_cmdline ();for (i =0 ; i <ARRAY_SIZE (regs); i++) {
Signature · 1 line
2.39.2
Marc Zyngier
maintainerPATCH v3 11/13 arm64: Add MIDR-based overrides for ID_AA64MMFR4_EL1.E2H0
In reply to Marc ZyngierNone the Apple M1/M2 CPUs effectively implement E2H=0, and M2 doesn't correctly implement NV1=1 (the EL2 S1 PTW seems to barf on the nVHE format).
Override ID_AA64MMFR4_EL1.E2H0 for these CPUs to reflect what they actually support.
--- arch/arm64/kernel/idreg-override.c | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+)
arch/arm64/kernel/idreg-override.c +36 -0
@@ -326,7 +326,43 @@ struct midr_override_data {const struct midr_range ranges[]; }; +static const struct midr_override_data e2h0_ni__initconst = { +/* +* These CPUs predate FEAT_E2H0, but have HCR_EL2.E2H RES1 +* anyway. +*/ + .feature ="id_aa64mmfr4.e2h0=0xf" , + .ranges = { +MIDR_ALL_VERSIONS (MIDR_APPLE_M1_ICESTORM ), +MIDR_ALL_VERSIONS (MIDR_APPLE_M1_FIRESTORM ), +MIDR_ALL_VERSIONS (MIDR_APPLE_M1_ICESTORM_PRO ), +MIDR_ALL_VERSIONS (MIDR_APPLE_M1_FIRESTORM_PRO ), +MIDR_ALL_VERSIONS (MIDR_APPLE_M1_ICESTORM_MAX ), +MIDR_ALL_VERSIONS (MIDR_APPLE_M1_FIRESTORM_MAX ), + {} + }, +}; + +static const struct midr_override_data e2h0_nv1_ni__initconst = { +/* +* These CPUs predate FEAT_E2H0, but have both HCR_EL2.E2H +* RES1 and a non-functional HCR_EL2.NV1. +*/ + .feature ="id_aa64mmfr4.e2h0=0xe" , + .ranges = { +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 ), + {} + }, +}; +static const struct midr_override_data *const midr_ovr_data[]__initconst = { + &e2h0_ni, + &e2h0_nv1_ni, };static void __init apply_midr_overrides (void )
Signature · 1 line
2.39.2
Marc Zyngier
maintainerPATCH v3 12/13 KVM: arm64: Expose ID_AA64MMFR4_EL1 to guests
In reply to Marc ZyngierWe can now expose ID_AA64MMFR4_EL1 to guests, and let NV guests understand that they cannot really switch HCR_EL2.E2H to 0 on some platforms.
--- arch/arm64/kvm/nested.c | 4 ++++ arch/arm64/kvm/sys_regs.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-)
arch/arm64/kvm/nested.c +4 -0
@@ -137,6 +137,10 @@ void access_nested_id_reg(struct kvm_vcpu *v, struct sys_reg_params *p, val |=FIELD_PREP (NV_FTR (MMFR2 ,TTL ),0b0001 );break ; +case SYS_ID_AA64MMFR4_EL1 : + val &=NV_FTR (MMFR4 ,E2H0 ); +break ; +case SYS_ID_AA64DFR0_EL1 :/* Only limited support for PMU, Debug, BPs and WPs */ val &= (NV_FTR(DFR0, PMUVer) |
arch/arm64/kvm/sys_regs.c +1 -1
@@ -2197,7 +2197,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),
Signature · 1 line
2.39.2
Marc Zyngier
maintainerPATCH v3 13/13 KVM: arm64: Force guest's HCR_EL2.E2H RES1 when NV1 is not implemented
In reply to Marc ZyngierIf NV1 isn't supported on a system, make sure we always evaluate the guest's HCR_EL2.E2H as RES1, irrespective of what the guest may have written there.
--- arch/arm64/include/asm/kvm_emulate.h | 3 ++- arch/arm64/kvm/sys_regs.c | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-)
arch/arm64/include/asm/kvm_emulate.h +2 -1
@@ -213,7 +213,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_HCR_NV1_RES0 ) || + (ctxt_sys_reg (ctxt,HCR_EL2 ) &HCR_E2H )); }static inline bool vcpu_el2_e2h_is_set (const struct kvm_vcpu *vcpu)
arch/arm64/kvm/sys_regs.c +11 -1
@@ -2022,6 +2022,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_HCR_NV1_RES0 )) + 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
@@ -2513,7 +2523,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {EL2_REG (VMPIDR_EL2 , access_rw, reset_unknown,0 ),EL2_REG (SCTLR_EL2 , access_rw, reset_val,SCTLR_EL2_RES1 ),EL2_REG (ACTLR_EL2 , access_rw, reset_val,0 ), -EL2_REG (HCR_EL2 , access_rw, reset_val,0 ), +EL2_REG (HCR_EL2 , access_rw, 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(HSTR_EL2, access_rw, reset_val, 0),
Signature · 1 line
2.39.2