[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [RFC PATCH 3/4] xen/arm: Sanitize cpuinfo ID registers fields


  • To: Julien Grall <julien@xxxxxxx>
  • From: Bertrand Marquis <Bertrand.Marquis@xxxxxxx>
  • Date: Mon, 12 Jul 2021 11:03:09 +0000
  • Accept-language: en-GB, en-US
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=arm.com; dmarc=pass action=none header.from=arm.com; dkim=pass header.d=arm.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=GGC+gAuQ2833ltizTLXI9PJzrLPwP/h70LyoK5EiFCc=; b=GoyOHT0AAWTiS20yMgEIKBOAgYr1Z1T8XoA7xPEbfOvyoQmuebxvKRwUeGZnJOd8Oy76p5eAN4Qep2ULkZOmpY0pVsZx31ANqNEif8SvWml7N4TnzdY/EL7PVzHDafTw1fXu9H3dOWNlZ+A/fcOlsOeSFReZKzVfMrlpBh9v4LHtbmDxvyh0urSnSb2nKbo0TwG3LrSxPerSXVOZAXjtA2gd0BPnuBRmvWAJPrF+/um/prrvyboSxeB+u5vgU94vVd733AnXdZrFEPamFYrImgzirB6VmFxudNxwlrD/pLQvm6cDqli75QqvTakK/5R4laXSaeD61m/mqabdfjQwiw==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=bfS6IwpB8tZ+9Bx+xqZMr37o9qMbFkEfBL3wfWtwQgAPdlybeMFd1nXxnHjs67qilqwXaoXXVjtLNw1/fE1mEtTTgRBJS63f/5jPuZlxKeaFiGyYQfMI8qT5+OGnq8Ka4ha6D8f9cXs0A2sJbM0PafkTXwmTA+H+GDdFirXC6NTPLZ6GfECS3zCVH0U4jVuRomxxa3eMPBMlLlrlP/5qGCIhjtmi5K0rTfrgPrrsSRh4mc2RGKn5VqOO6kDWoMmGxsFk1tBE/C9n6a4ifWUaJafpIy7w3VkDkRiqwwpTMMv8s4mSiv8KkHRZ/F40Q6W7MCCHOHcGfwsw9ePrAsliQw==
  • Authentication-results-original: xen.org; dkim=none (message not signed) header.d=none;xen.org; dmarc=none action=none header.from=arm.com;
  • Cc: Xen-devel <xen-devel@xxxxxxxxxxxxxxxxxxxx>, Stefano Stabellini <sstabellini@xxxxxxxxxx>, Volodymyr Babchuk <Volodymyr_Babchuk@xxxxxxxx>, Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, George Dunlap <george.dunlap@xxxxxxxxxx>, Ian Jackson <iwj@xxxxxxxxxxxxxx>, Jan Beulich <jbeulich@xxxxxxxx>, Wei Liu <wl@xxxxxxx>
  • Delivery-date: Mon, 12 Jul 2021 11:04:01 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Nodisclaimer: true
  • Original-authentication-results: xen.org; dkim=none (message not signed) header.d=none;xen.org; dmarc=none action=none header.from=arm.com;
  • Thread-index: AQHXbQmqqZ2dUoMblUy//wgHf+AX8qs/M8QAgAANJgA=
  • Thread-topic: [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(&current_cpu_data);
>> +    sanitize_cpu(&current_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




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.