diff mbox series

[v3] x86/ucode/AMD: apply the patch early on every logical thread

Message ID 20230130115503.19941-1-sergey.dyasli@citrix.com (mailing list archive)
State New, archived
Headers show
Series [v3] x86/ucode/AMD: apply the patch early on every logical thread | expand

Commit Message

Sergey Dyasli Jan. 30, 2023, 11:55 a.m. UTC
The original issue has been reported on AMD Bulldozer-based CPUs where
ucode loading loses the LWP feature bit in order to gain the IBPB bit.
LWP disabling is per-SMT/CMT core modification and needs to happen on
each sibling thread despite the shared microcode engine. Otherwise,
logical CPUs will end up with different cpuid capabilities.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=216211

Guests running under Xen happen to be not affected because of levelling
logic for the feature masking/override MSRs which causes the LWP bit to
fall out and hides the issue. The latest recommendation from AMD, after
discussing this bug, is to load ucode on every logical CPU.

In Linux kernel this issue has been addressed by e7ad18d1169c
("x86/microcode/AMD: Apply the patch early on every logical thread").
Follow the same approach in Xen.

Introduce SAME_UCODE match result and use it for early AMD ucode
loading. Take this opportunity and move opt_ucode_allow_same out of
compare_revisions() to the relevant callers and also modify the warning
message based on it. Intel's side of things is modified for consistency
but provides no functional change.

Late loading is still performed only on the first of SMT/CMT
siblings and only if a newer ucode revision has been provided (unless
allow_same option is specified).

Signed-off-by: Sergey Dyasli <sergey.dyasli@citrix.com>
---
v2 --> v3:
- Moved opt_ucode_allow_same out of compare_revisions() and updated
  the commit message
- Adjusted the warning message

v1 --> v2:
- Expanded the commit message with the levelling section
- Adjusted comment for OLD_UCODE

CC: Jan Beulich <jbeulich@suse.com>
CC: Andrew Cooper <andrew.cooper3@citrix.com>
CC: "Roger Pau Monné" <roger.pau@citrix.com>
CC: Wei Liu <wl@xen.org>
---
 xen/arch/x86/cpu/microcode/amd.c     | 11 ++++++++---
 xen/arch/x86/cpu/microcode/core.c    | 19 ++++++++++++++-----
 xen/arch/x86/cpu/microcode/intel.c   | 10 +++++++---
 xen/arch/x86/cpu/microcode/private.h |  3 ++-
 4 files changed, 31 insertions(+), 12 deletions(-)

Comments

Jan Beulich Feb. 13, 2023, 10:36 a.m. UTC | #1
On 30.01.2023 12:55, Sergey Dyasli wrote:
> The original issue has been reported on AMD Bulldozer-based CPUs where
> ucode loading loses the LWP feature bit in order to gain the IBPB bit.
> LWP disabling is per-SMT/CMT core modification and needs to happen on
> each sibling thread despite the shared microcode engine. Otherwise,
> logical CPUs will end up with different cpuid capabilities.
> Link: https://bugzilla.kernel.org/show_bug.cgi?id=216211
> 
> Guests running under Xen happen to be not affected because of levelling
> logic for the feature masking/override MSRs which causes the LWP bit to
> fall out and hides the issue. The latest recommendation from AMD, after
> discussing this bug, is to load ucode on every logical CPU.
> 
> In Linux kernel this issue has been addressed by e7ad18d1169c
> ("x86/microcode/AMD: Apply the patch early on every logical thread").
> Follow the same approach in Xen.
> 
> Introduce SAME_UCODE match result and use it for early AMD ucode
> loading. Take this opportunity and move opt_ucode_allow_same out of
> compare_revisions() to the relevant callers and also modify the warning
> message based on it. Intel's side of things is modified for consistency
> but provides no functional change.
> 
> Late loading is still performed only on the first of SMT/CMT
> siblings and only if a newer ucode revision has been provided (unless
> allow_same option is specified).

Another sentence on the "why" would be helpful, or else it ends up
looking as if there was an issue left in place. (I guess latently it's
going to be left in place anyway, until such time where we re-evaluate
features after ucode application.)

> --- a/xen/arch/x86/cpu/microcode/core.c
> +++ b/xen/arch/x86/cpu/microcode/core.c
> @@ -612,13 +612,21 @@ static long cf_check microcode_update_helper(void *data)
>       * that ucode revision.
>       */
>      spin_lock(&microcode_mutex);
> -    if ( microcode_cache &&
> -         alternative_call(ucode_ops.compare_patch,
> -                          patch, microcode_cache) != NEW_UCODE )
> +    if ( microcode_cache )
>      {
> +        enum microcode_match_result result;
> +
> +        result = alternative_call(ucode_ops.compare_patch, patch,
> +                                                           microcode_cache);

Nit: Indentation (3rd arg wants to align with 1st one, not 2nd).

>          spin_unlock(&microcode_mutex);
> -        printk(XENLOG_WARNING "microcode: couldn't find any newer revision "
> -                              "in the provided blob!\n");
> +
> +        if ( result == NEW_UCODE ||
> +             (opt_ucode_allow_same && result == SAME_UCODE) )
> +            goto apply;
> +
> +        printk(XENLOG_WARNING "microcode: couldn't find any newer%s revision "
> +                              "in the provided blob!\n", opt_ucode_allow_same ?
> +                                                         " (or the same)" : "");

Since you touch this entire printk() anyway, may I ask that you re-flow
it to meet our guidelines:

        printk(XENLOG_WARNING
               "microcode: couldn't find any newer%s revision in the provided blob!\n",
               opt_ucode_allow_same ? " (or the same)" : "");

?

> @@ -626,6 +634,7 @@ static long cf_check microcode_update_helper(void *data)
>      }
>      spin_unlock(&microcode_mutex);
>  
> +apply:

Nit: Style (labels indented by at least one blank; see ./CODING_STYLE).
Personally I'd prefer if you got away without another "goto" here, as
our rule of thumb suggests that "goto" generally is to be used only for
error handling or other situations where the alternative would be
significantly worse (excess indentation, code duplication, ...).

Jan
diff mbox series

Patch

diff --git a/xen/arch/x86/cpu/microcode/amd.c b/xen/arch/x86/cpu/microcode/amd.c
index 4b097187a0..a9a5557835 100644
--- a/xen/arch/x86/cpu/microcode/amd.c
+++ b/xen/arch/x86/cpu/microcode/amd.c
@@ -176,8 +176,8 @@  static enum microcode_match_result compare_revisions(
     if ( new_rev > old_rev )
         return NEW_UCODE;
 
-    if ( opt_ucode_allow_same && new_rev == old_rev )
-        return NEW_UCODE;
+    if ( new_rev == old_rev )
+        return SAME_UCODE;
 
     return OLD_UCODE;
 }
@@ -220,8 +220,13 @@  static int cf_check apply_microcode(const struct microcode_patch *patch)
     unsigned int cpu = smp_processor_id();
     struct cpu_signature *sig = &per_cpu(cpu_sig, cpu);
     uint32_t rev, old_rev = sig->rev;
+    enum microcode_match_result result = microcode_fits(patch);
 
-    if ( microcode_fits(patch) != NEW_UCODE )
+    /*
+     * Allow application of the same revision to pick up SMT-specific changes
+     * even if the revision of the other SMT thread is already up-to-date.
+     */
+    if ( result != NEW_UCODE && result != SAME_UCODE )
         return -EINVAL;
 
     if ( check_final_patch_levels(sig) )
diff --git a/xen/arch/x86/cpu/microcode/core.c b/xen/arch/x86/cpu/microcode/core.c
index d14754e222..912ef2c7be 100644
--- a/xen/arch/x86/cpu/microcode/core.c
+++ b/xen/arch/x86/cpu/microcode/core.c
@@ -612,13 +612,21 @@  static long cf_check microcode_update_helper(void *data)
      * that ucode revision.
      */
     spin_lock(&microcode_mutex);
-    if ( microcode_cache &&
-         alternative_call(ucode_ops.compare_patch,
-                          patch, microcode_cache) != NEW_UCODE )
+    if ( microcode_cache )
     {
+        enum microcode_match_result result;
+
+        result = alternative_call(ucode_ops.compare_patch, patch,
+                                                           microcode_cache);
         spin_unlock(&microcode_mutex);
-        printk(XENLOG_WARNING "microcode: couldn't find any newer revision "
-                              "in the provided blob!\n");
+
+        if ( result == NEW_UCODE ||
+             (opt_ucode_allow_same && result == SAME_UCODE) )
+            goto apply;
+
+        printk(XENLOG_WARNING "microcode: couldn't find any newer%s revision "
+                              "in the provided blob!\n", opt_ucode_allow_same ?
+                                                         " (or the same)" : "");
         microcode_free_patch(patch);
         ret = -ENOENT;
 
@@ -626,6 +634,7 @@  static long cf_check microcode_update_helper(void *data)
     }
     spin_unlock(&microcode_mutex);
 
+apply:
     cpumask_clear(&cpu_callin_map);
     atomic_set(&cpu_out, 0);
     atomic_set(&cpu_updated, 0);
diff --git a/xen/arch/x86/cpu/microcode/intel.c b/xen/arch/x86/cpu/microcode/intel.c
index f7fec4b4ed..8d4d6574aa 100644
--- a/xen/arch/x86/cpu/microcode/intel.c
+++ b/xen/arch/x86/cpu/microcode/intel.c
@@ -232,8 +232,8 @@  static enum microcode_match_result compare_revisions(
     if ( new_rev > old_rev )
         return NEW_UCODE;
 
-    if ( opt_ucode_allow_same && new_rev == old_rev )
-        return NEW_UCODE;
+    if ( new_rev == old_rev )
+        return SAME_UCODE;
 
     /*
      * Treat pre-production as always applicable - anyone using pre-production
@@ -290,8 +290,12 @@  static int cf_check apply_microcode(const struct microcode_patch *patch)
     unsigned int cpu = smp_processor_id();
     struct cpu_signature *sig = &this_cpu(cpu_sig);
     uint32_t rev, old_rev = sig->rev;
+    enum microcode_match_result result;
+
+    result = microcode_update_match(patch);
 
-    if ( microcode_update_match(patch) != NEW_UCODE )
+    if ( result != NEW_UCODE &&
+         !(opt_ucode_allow_same && result == SAME_UCODE) )
         return -EINVAL;
 
     wbinvd();
diff --git a/xen/arch/x86/cpu/microcode/private.h b/xen/arch/x86/cpu/microcode/private.h
index 73b095d5bf..626aeb4d08 100644
--- a/xen/arch/x86/cpu/microcode/private.h
+++ b/xen/arch/x86/cpu/microcode/private.h
@@ -6,7 +6,8 @@ 
 extern bool opt_ucode_allow_same;
 
 enum microcode_match_result {
-    OLD_UCODE, /* signature matched, but revision id is older or equal */
+    OLD_UCODE, /* signature matched, but revision id is older */
+    SAME_UCODE, /* signature matched, but revision id is the same */
     NEW_UCODE, /* signature matched, but revision id is newer */
     MIS_UCODE, /* signature mismatched */
 };