diff --git a/xen/arch/arm/arm64/Makefile b/xen/arch/arm/arm64/Makefile index 40642ff574..701d66883d 100644 --- a/xen/arch/arm/arm64/Makefile +++ b/xen/arch/arm/arm64/Makefile @@ -1,6 +1,7 @@ obj-y += lib/ obj-y += cache.o +obj-y += cpufeature.o obj-$(CONFIG_HARDEN_BRANCH_PREDICTOR) += bpi.o obj-$(CONFIG_EARLY_PRINTK) += debug.o obj-y += domctl.o diff --git a/xen/arch/arm/arm64/cpufeature.c b/xen/arch/arm/arm64/cpufeature.c index 152086dc9d..58596495a8 100644 --- a/xen/arch/arm/arm64/cpufeature.c +++ b/xen/arch/arm/arm64/cpufeature.c @@ -275,6 +275,9 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr2[] = { ARM64_FTR_END, }; +#if 0 +/* TODO: use this to sanitize the cache line size among cores */ + 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_DIC_SHIFT, 1, 1), @@ -291,6 +294,7 @@ static const struct arm64_ftr_bits ftr_ctr[] = { ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, CTR_IMINLINE_SHIFT, 4, 0), ARM64_FTR_END, }; +#endif static const struct arm64_ftr_bits ftr_id_mmfr0[] = { S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_MMFR0_INNERSHR_SHIFT, 4, 0xf), @@ -325,11 +329,14 @@ static const struct arm64_ftr_bits ftr_mvfr2[] = { ARM64_FTR_END, }; +#if 0 +/* TODO: handle this when sanitizing cache related registers */ static const struct arm64_ftr_bits ftr_dczid[] = { ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, DCZID_DZP_SHIFT, 1, 1), ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, DCZID_BS_SHIFT, 4, 0), ARM64_FTR_END, }; +#endif static const struct arm64_ftr_bits ftr_id_isar0[] = { ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR0_DIVIDE_SHIFT, 4, 0), @@ -444,11 +451,15 @@ static const struct arm64_ftr_bits ftr_id_dfr1[] = { ARM64_FTR_END, }; +#if 0 +/* TODO: use this to sanitize SVE once we support it */ + static const struct arm64_ftr_bits ftr_zcr[] = { ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ZCR_ELx_LEN_SHIFT, ZCR_ELx_LEN_SIZE, 0), /* LEN */ ARM64_FTR_END, }; +#endif /* * Common ftr bits for a 32bit register with all hidden, strict @@ -512,3 +523,109 @@ static s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new, * End of imported linux structures and code */ +static void sanitize_reg(u64 *cur_reg, u64 new_reg, const char *reg_name, + const struct arm64_ftr_bits *ftrp) +{ + int taint = 0; + u64 old_reg = *cur_reg; + + for (;ftrp->width != 0;ftrp++) + { + s64 cur_field = arm64_ftr_value(ftrp, *cur_reg); + s64 new_field = arm64_ftr_value(ftrp, new_reg); + + if (cur_field == new_field) + continue; + + if (ftrp->strict) + taint = 1; + + *cur_reg = arm64_ftr_set_value(ftrp, *cur_reg, + arm64_ftr_safe_value(ftrp, new_field, cur_field)); + } + + if (old_reg != new_reg) + printk(XENLOG_DEBUG "SANITY DIF: %s 0x%"PRIx64" -> 0x%"PRIx64"\n", + reg_name, old_reg, new_reg); + if (old_reg != *cur_reg) + printk(XENLOG_DEBUG "SANITY FIX: %s 0x%"PRIx64" -> 0x%"PRIx64"\n", + reg_name, old_reg, *cur_reg); + + if (taint) + { + printk(XENLOG_WARNING "SANITY CHECK: Unexpected variation in %s.\n", + reg_name); + add_taint(TAINT_CPU_OUT_OF_SPEC); + } +} + + +/* + * This function should be called on secondary cores to sanitize the boot cpu + * cpuinfo. + */ +void update_system_features(const struct cpuinfo_arm *new) +{ + +#define SANITIZE_REG(field, num, reg) \ + sanitize_reg(&system_cpuinfo.field.bits[num], new->field.bits[num], \ + #reg, ftr_##reg) + +#define SANITIZE_ID_REG(field, num, reg) \ + sanitize_reg(&system_cpuinfo.field.bits[num], new->field.bits[num], \ + #reg, ftr_id_##reg) + +#define SANITIZE_RAZ_REG(field, num, reg) \ + sanitize_reg(&system_cpuinfo.field.bits[num], new->field.bits[num], \ + #reg, ftr_raz) + +#define SANITIZE_GENERIC_REG(field, num, reg) \ + sanitize_reg(&system_cpuinfo.field.bits[num], new->field.bits[num], \ + #reg, ftr_generic_32bits) + + SANITIZE_ID_REG(pfr64, 0, aa64pfr0); + SANITIZE_ID_REG(pfr64, 1, aa64pfr1); + + SANITIZE_ID_REG(dbg64, 0, aa64dfr0); + SANITIZE_RAZ_REG(dbg64, 1, aa64dfr1); + + SANITIZE_ID_REG(mm64, 0, aa64mmfr0); + SANITIZE_ID_REG(mm64, 1, aa64mmfr1); + SANITIZE_ID_REG(mm64, 2, aa64mmfr2); + + SANITIZE_ID_REG(isa64, 0, aa64isar0); + SANITIZE_ID_REG(isa64, 1, aa64isar1); + + SANITIZE_ID_REG(zfr64, 0, aa64zfr0); + + if ( cpu_feature64_has_el0_32(&system_cpuinfo) ) + { + SANITIZE_ID_REG(pfr32, 0, pfr0); + SANITIZE_ID_REG(pfr32, 1, pfr1); + SANITIZE_ID_REG(pfr32, 2, pfr2); + + SANITIZE_ID_REG(dbg32, 0, dfr0); + SANITIZE_ID_REG(dbg32, 1, dfr1); + + SANITIZE_ID_REG(mm32, 0, mmfr0); + SANITIZE_GENERIC_REG(mm32, 1, mmfr1); + SANITIZE_GENERIC_REG(mm32, 2, mmfr2); + SANITIZE_GENERIC_REG(mm32, 3, mmfr3); + SANITIZE_ID_REG(mm32, 4, mmfr4); + SANITIZE_ID_REG(mm32, 5, mmfr5); + + SANITIZE_ID_REG(isa32, 0, isar0); + SANITIZE_GENERIC_REG(isa32, 1, isar1); + SANITIZE_GENERIC_REG(isa32, 2, isar2); + SANITIZE_GENERIC_REG(isa32, 3, isar3); + SANITIZE_ID_REG(isa32, 4, isar4); + SANITIZE_ID_REG(isa32, 5, isar5); + SANITIZE_ID_REG(isa32, 6, isar6); + + SANITIZE_GENERIC_REG(mvfr, 0, mvfr0); + SANITIZE_GENERIC_REG(mvfr, 1, mvfr1); +#ifndef MVFR2_MAYBE_UNDEFINED + SANITIZE_REG(mvfr, 2, mvfr2); +#endif + } +} diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c index c9f2827d56..60c0e82fc5 100644 --- a/xen/arch/arm/smpboot.c +++ b/xen/arch/arm/smpboot.c @@ -318,15 +318,26 @@ void start_secondary(void) * is manually specified for all domains). Better to park them for * now. */ - if ( !opt_hmp_unsafe && - current_cpu_data.midr.bits != system_cpuinfo.midr.bits ) + if ( current_cpu_data.midr.bits != system_cpuinfo.midr.bits ) { - printk(XENLOG_ERR - "CPU%u MIDR (0x%"PRIregister") does not match boot CPU MIDR (0x%"PRIregister"),\n" - XENLOG_ERR "disable cpu (see big.LITTLE.txt under docs/).\n", - smp_processor_id(), current_cpu_data.midr.bits, - system_cpuinfo.midr.bits); - stop_cpu(); + if ( !opt_hmp_unsafe ) + { + printk(XENLOG_ERR + "CPU%u MIDR (0x%"PRIregister") does not match boot CPU MIDR (0x%"PRIregister"),\n" + XENLOG_ERR "disable cpu (see big.LITTLE.txt under docs/).\n", + smp_processor_id(), current_cpu_data.midr.bits, + system_cpuinfo.midr.bits); + stop_cpu(); + } + else + { + printk(XENLOG_ERR + "CPU%u MIDR (0x%"PRIregister") does not match boot CPU MIDR (0x%"PRIregister"),\n" + XENLOG_ERR "hmp-unsafe turned on so tainting Xen and keep core on!!\n", + smp_processor_id(), current_cpu_data.midr.bits, + system_cpuinfo.midr.bits); + add_taint(TAINT_CPU_OUT_OF_SPEC); + } } if ( dcache_line_bytes != read_dcache_line_bytes() ) @@ -337,6 +348,13 @@ void start_secondary(void) stop_cpu(); } + /* + * system features must be updated only if we do not stop the core or + * we might disable features due to a non used core (for example when + * booting on big cores on a big.LITTLE system with hmp_unsafe) + */ + update_system_features(¤t_cpu_data); + mmu_init_secondary_cpu(); gic_init_secondary_cpu(); diff --git a/xen/common/kernel.c b/xen/common/kernel.c index d77756a81e..e119e5401f 100644 --- a/xen/common/kernel.c +++ b/xen/common/kernel.c @@ -327,6 +327,7 @@ unsigned int tainted; * 'H' - HVM forced emulation prefix is permitted. * 'M' - Machine had a machine check experience. * 'U' - Platform is unsecure (usually due to an errata on the platform). + * 'S' - Out of spec CPU (One core has a feature incompatible with others). * * The string is overwritten by the next call to print_taint(). */ @@ -334,12 +335,13 @@ char *print_tainted(char *str) { if ( tainted ) { - snprintf(str, TAINT_STRING_MAX_LEN, "Tainted: %c%c%c%c%c", + snprintf(str, TAINT_STRING_MAX_LEN, "Tainted: %c%c%c%c%c%c", tainted & TAINT_MACHINE_UNSECURE ? 'U' : ' ', tainted & TAINT_MACHINE_CHECK ? 'M' : ' ', tainted & TAINT_SYNC_CONSOLE ? 'C' : ' ', tainted & TAINT_ERROR_INJECT ? 'E' : ' ', - tainted & TAINT_HVM_FEP ? 'H' : ' '); + tainted & TAINT_HVM_FEP ? 'H' : ' ', + tainted & TAINT_CPU_OUT_OF_SPEC ? 'S' : ' '); } else { diff --git a/xen/include/asm-arm/cpufeature.h b/xen/include/asm-arm/cpufeature.h index 8f2b8e7830..52cb3133e0 100644 --- a/xen/include/asm-arm/cpufeature.h +++ b/xen/include/asm-arm/cpufeature.h @@ -330,6 +330,15 @@ extern struct cpuinfo_arm system_cpuinfo; extern void identify_cpu(struct cpuinfo_arm *); +#ifdef CONFIG_ARM_64 +extern void update_system_features(const struct cpuinfo_arm *); +#else +static inline void update_system_features(const struct cpuinfo_arm *cpuinfo) +{ + /* Not supported on arm32 */ +} +#endif + extern struct cpuinfo_arm cpu_data[]; #define current_cpu_data cpu_data[smp_processor_id()] diff --git a/xen/include/xen/lib.h b/xen/include/xen/lib.h index 1198c7c0b2..c6987973bf 100644 --- a/xen/include/xen/lib.h +++ b/xen/include/xen/lib.h @@ -192,6 +192,7 @@ uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c); #define TAINT_ERROR_INJECT (1u << 2) #define TAINT_HVM_FEP (1u << 3) #define TAINT_MACHINE_UNSECURE (1u << 4) +#define TAINT_CPU_OUT_OF_SPEC (1u << 5) extern unsigned int tainted; #define TAINT_STRING_MAX_LEN 20 extern char *print_tainted(char *str);