| 
    
 [Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [RFC PATCH 3/4] xen/arm: Sanitize cpuinfo ID registers fields
 Hi Julien,
> On 12 Jul 2021, at 11:16, Julien Grall <julien@xxxxxxx> wrote:
> 
> Hi Bertrand,
> 
> On 29/06/2021 18:08, Bertrand Marquis wrote:
>> Define a sanitize_cpu function to be called on secondary cores to
>> sanitize the cpuinfo structure from the boot CPU.
>> The safest value is taken when possible and the system is marked tainted
>> if we encounter values which are incompatible with each other.
>> Call the sanitize_cpu function on all secondary cores and remove the
>> code disabling secondary cores if they are not the same as the boot core
>> as we are now able to handle this use case.
>> This is only supported on arm64 so cpu_sanitize is an empty static
>> inline on arm32.
>> This patch also removes the code imported from Linux that will not be
>> used by Xen.
>> Signed-off-by: Bertrand Marquis <bertrand.marquis@xxxxxxx>
>> ---
>>  xen/arch/arm/arm64/cpusanitize.c | 236 ++++++++++++++++++++++++-------
>>  xen/arch/arm/smpboot.c           |   5 +-
>>  xen/include/asm-arm/cpufeature.h |   9 ++
>>  xen/include/xen/lib.h            |   1 +
>>  4 files changed, 199 insertions(+), 52 deletions(-)
>> diff --git a/xen/arch/arm/arm64/cpusanitize.c 
>> b/xen/arch/arm/arm64/cpusanitize.c
>> index 4cc8378c14..744006ca7c 100644
>> --- a/xen/arch/arm/arm64/cpusanitize.c
>> +++ b/xen/arch/arm/arm64/cpusanitize.c
>> @@ -97,10 +97,6 @@ struct arm64_ftr_bits {
>>              .width = 0,                             \
>>      }
>>  -static void cpu_enable_cnp(struct arm64_cpu_capabilities const *cap);
>> -
>> -static bool __system_matches_cap(unsigned int n);
>> -
>>  /*
>>   * NOTE: Any changes to the visibility of features should be kept in
>>   * sync with the documentation of the CPU feature register ABI.
>> @@ -277,31 +273,6 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr2[] = 
>> {
>>      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_DIC_SHIFT, 
>> 1, 1),
>> -    ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, CTR_IDC_SHIFT, 
>> 1, 1),
>> -    ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_HIGHER_OR_ZERO_SAFE, 
>> CTR_CWG_SHIFT, 4, 0),
>> -    ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_HIGHER_OR_ZERO_SAFE, 
>> CTR_ERG_SHIFT, 4, 0),
>> -    ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, 
>> CTR_DMINLINE_SHIFT, 4, 1),
>> -    /*
>> -     * Linux can handle differing I-cache policies. Userspace JITs will
>> -     * make use of *minLine.
>> -     * If we have differing I-cache policies, report it as the weakest - 
>> VIPT.
>> -     */
>> -    ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_EXACT, CTR_L1IP_SHIFT, 
>> 2, ICACHE_POLICY_VIPT),   /* L1Ip */
>> -    ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, 
>> CTR_IMINLINE_SHIFT, 4, 0),
>> -    ARM64_FTR_END,
>> -};
> 
> I don't think this is should be dropped. Xen will need to know the safest 
> cacheline size that can be used for cache maintenance instructions.
I will surround those entries by #if 0 instead
> 
>> -
>> -static struct arm64_ftr_override __ro_after_init no_override = { };
>> -
>> -struct arm64_ftr_reg arm64_ftr_reg_ctrel0 = {
>> -    .name           = "SYS_CTR_EL0",
>> -    .ftr_bits       = ftr_ctr,
>> -    .override       = &no_override,
>> -};
>> -
>>  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),
>>      ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 
>> ID_MMFR0_FCSE_SHIFT, 4, 0),
>> @@ -335,12 +306,6 @@ static const struct arm64_ftr_bits ftr_mvfr2[] = {
>>      ARM64_FTR_END,
>>  };
>>  -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,
>> -};
> 
> Why is this dropped?
Same I will keep that with #if 0
> 
>> -
>>  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),
>>      ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 
>> ID_ISAR0_DEBUG_SHIFT, 4, 0),
>> @@ -454,12 +419,6 @@ static const struct arm64_ftr_bits ftr_id_dfr1[] = {
>>      ARM64_FTR_END,
>>  };
>>  -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,
>> -};
> 
> At some point we will support SVE in Xen. So any reason we would not want to 
> keep the code?
Same I will keep that with #if 0
> 
>> -
>>  /*
>>   * Common ftr bits for a 32bit register with all hidden, strict
>>   * attributes, with 4bit feature fields and a default safe value of
>> @@ -478,17 +437,192 @@ static const struct arm64_ftr_bits 
>> ftr_generic_32bits[] = {
>>      ARM64_FTR_END,
>>  };
>>  -/* Table for a single 32bit feature value */
>> -static const struct arm64_ftr_bits ftr_single32[] = {
>> -    ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 0, 32, 0),
>> -    ARM64_FTR_END,
>> -};
>> -
>> -static const struct arm64_ftr_bits ftr_raz[] = {
>> -    ARM64_FTR_END,
>> -};
>> -
>>  /*
>>   * End of imported linux structures
>>   */
>>  +static inline int __attribute_const__
>> +cpuid_feature_extract_signed_field_width(u64 features, int field, int width)
>> +{
>> +    return (s64)(features << (64 - width - field)) >> (64 - width);
>> +}
> 
> Please avoid to mix Xen and Linux coding style in the same file.
> 
> Also, this function and some others below seems to have been taken as-is from 
> Linux. So this should at least be mentionned in the commit message.
> 
> I would also consider to import them in a separate patch (or maybe in 
> patch#2) so it is clear this is not "new" code.
I will move those in patch 2 and keep Linux code.
> 
>> +
>> +static inline int __attribute_const__
>> +cpuid_feature_extract_signed_field(u64 features, int field)
>> +{
>> +    return cpuid_feature_extract_signed_field_width(features, field, 4);
>> +}
>> +
>> +static inline unsigned int __attribute_const__
>> +cpuid_feature_extract_unsigned_field_width(u64 features, int field, int 
>> width)
>> +{
>> +    return (u64)(features << (64 - width - field)) >> (64 - width);
>> +}
>> +
>> +static inline unsigned int __attribute_const__
>> +cpuid_feature_extract_unsigned_field(u64 features, int field)
>> +{
>> +    return cpuid_feature_extract_unsigned_field_width(features, field, 4);
>> +}
>> +
>> +static inline u64 arm64_ftr_mask(const struct arm64_ftr_bits *ftrp)
>> +{
>> +    return (u64)GENMASK(ftrp->shift + ftrp->width - 1, ftrp->shift);
>> +}
>> +
>> +static inline int __attribute_const__
>> +cpuid_feature_extract_field_width(u64 features, int field, int width,
>> +                                  bool sign)
>> +{
>> +    return (sign) ?
>> +        cpuid_feature_extract_signed_field_width(features, field, width) :
>> +        cpuid_feature_extract_unsigned_field_width(features, field, width);
>> +}
>> +
>> +static inline int __attribute_const__
>> +cpuid_feature_extract_field(u64 features, int field, bool sign)
>> +{
>> +    return cpuid_feature_extract_field_width(features, field, 4, sign);
>> +}
>> +
>> +static inline s64 arm64_ftr_value(const struct arm64_ftr_bits *ftrp, u64 
>> val)
>> +{
>> +    return (s64)cpuid_feature_extract_field_width(val, ftrp->shift,
>> +                                                  ftrp->width, ftrp->sign);
>> +}
>> +
>> +static s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new,
>> +                                s64 cur)
>> +{
>> +    s64 ret = 0;
>> +
>> +    switch ( ftrp->type ) {
>> +    case FTR_EXACT:
>> +        ret = ftrp->safe_val;
>> +        break;
>> +    case FTR_LOWER_SAFE:
>> +        ret = new < cur ? new : cur;
>> +        break;
>> +    case FTR_HIGHER_OR_ZERO_SAFE:
>> +        if (!cur || !new)
>> +            break;
>> +        fallthrough;
>> +    case FTR_HIGHER_SAFE:
>> +        ret = new > cur ? new : cur;
> 
> We have a macro max() defined in kernel.h.
Right I will use that instead.
> 
>> +        break;
>> +    default:
>> +        BUG();
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +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++ )
>> +    {
>> +        u64 mask;
>> +        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;
>> +
>> +        mask = arm64_ftr_mask(ftrp);
>> +
>> +        *cur_reg &= ~mask;
>> +        *cur_reg |= (arm64_ftr_safe_value(ftrp, new_field, cur_field)
>> +                    << ftrp->shift) & mask;
>> +    }
>> +
>> +    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.
> 
> Can we renamed boot_cpu_data to system_cpuinfo (or something similar)? This 
> would make clear this is not only the features for the boot CPU?
Ok I will do that.
> 
>> + */
>> +void sanitize_cpu(const struct cpuinfo_arm *new)
> 
> How about naming it to "update_system_features()"?
Ok
> 
>> +{
>> +
>> +#define SANITIZE_REG(field, num, reg)  \
>> +    sanitize_reg(&boot_cpu_data.field.bits[num], new->field.bits[num], \
>> +                 #reg, ftr_##reg)
>> +
>> +#define SANITIZE_ID_REG(field, num, reg)  \
>> +    sanitize_reg(&boot_cpu_data.field.bits[num], new->field.bits[num], \
>> +                 #reg, ftr_id_##reg)
>> +
>> +#define SANITIZE_GENERIC_REG(field, num, reg)  \
>> +    sanitize_reg(&boot_cpu_data.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);
>> +
>> +    /* aux64 x2 */
>> +
>> +    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(&boot_cpu_data) )
>> +    {
>> +        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);
>> +
>> +        /* aux32 x1 */
> 
> What does this comment mean?
Leftover during dev that I should turn in proper comment.
It was there as I am not sanitizing one aux32 register.
Same goes for the comment before for aux64.
I will remove them.
> 
>> +
>> +        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 a1ee3146ef..8fdf5e70d3 100644
>> --- a/xen/arch/arm/smpboot.c
>> +++ b/xen/arch/arm/smpboot.c
>> @@ -307,12 +307,14 @@ void start_secondary(void)
>>      set_processor_id(cpuid);
>>        identify_cpu(¤t_cpu_data);
>> +    sanitize_cpu(¤t_cpu_data);
>>      processor_setup();
>>        init_traps();
>>  +#ifndef CONFIG_ARM_64
>>      /*
>> -     * Currently Xen assumes the platform has only one kind of CPUs.
>> +     * Currently Xen assumes the platform has only one kind of CPUs on 
>> ARM32.
>>       * This assumption does not hold on big.LITTLE platform and may
>>       * result to instability and insecure platform (unless cpu affinity
>>       * is manually specified for all domains). Better to park them for
>> @@ -328,6 +330,7 @@ void start_secondary(void)
>>                 boot_cpu_data.midr.bits);
>>          stop_cpu();
>>      }
>> +#endif
>>        if ( dcache_line_bytes != read_dcache_line_bytes() )
>>      {
> 
> Any plan to drop this check?
Yes this should be done as a next patch in the serie once we valeted
The way to go for the base.
> 
>> diff --git a/xen/include/asm-arm/cpufeature.h 
>> b/xen/include/asm-arm/cpufeature.h
>> index ba48db3eac..ad34e55cc8 100644
>> --- a/xen/include/asm-arm/cpufeature.h
>> +++ b/xen/include/asm-arm/cpufeature.h
>> @@ -330,6 +330,15 @@ extern struct cpuinfo_arm boot_cpu_data;
>>    extern void identify_cpu(struct cpuinfo_arm *);
>>  +#ifdef CONFIG_ARM_64
>> +extern void sanitize_cpu(const struct cpuinfo_arm *);
>> +#else
>> +static inline void sanitize_cpu(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)
> 
> You want to also update at least print_tainted().
Oh yes I will fix that in the next version.
Cheers
Bertrand
> 
>>  extern unsigned int tainted;
>>  #define TAINT_STRING_MAX_LEN            20
>>  extern char *print_tainted(char *str);
> 
> -- 
> Julien Grall
 
 
  | 
  
![]()  | 
            
         Lists.xenproject.org is hosted with RackSpace, monitoring our  |