arch/x86/kernel/cpu/microcode/intel.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
Microcode updates can usually jump revisions. However, there is an
erratum on Granite Rapids systems. If they "jump over" revision
0x1000405, they result in #MC. Avoid it.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
Cc: <stable@vger.kernel.org>
---
V2 -> V3:
* Shorten the changelog and rename the function (Boris)
* Reduce the code comment (Dave)
* Allow 0x1000405 loading. Thanks to Andrew, this fix got attention.
* Collect Dave review tag. Thanks, Dave!
Note:
* GNR98 currently describes loading 0x1000405 itself is unsafe, but it
will be updated to say okay with that. I will watch out the GNR98
changes.
* Jumping from < 0x1000380 to 0x1000405 was identified as an issue, but
0x1000380 is the first revision as GNR products. So loading 0x1000405
in production systems should be okay.
---
arch/x86/kernel/cpu/microcode/intel.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index 1142183c950c..30a22388d4b1 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -309,6 +309,26 @@ static void save_microcode_patch(struct microcode_intel *patch)
pr_err("Unable to allocate microcode memory size: %u\n", size);
}
+static bool revision_is_safe(struct cpu_signature *sig, u32 rev)
+{
+ u32 vfm = IFM(x86_family(sig->sig), x86_model(sig->sig));
+
+ /*
+ * Erratum GNR98 can cause #MC's if "jumping over" revision 0x1000405.
+ * Avoid the jumps.
+ */
+ if (vfm == INTEL_GRANITERAPIDS_X &&
+ x86_stepping(sig->sig) == 1 &&
+ sig->pf & 0x95 &&
+ sig->rev < 0x1000405 &&
+ rev > 0x1000405) {
+ pr_err_once("Erratum GNR98: skipping revision 0x%x.\n", rev);
+ return false;
+ }
+
+ return true;
+}
+
/* Scan blob for microcode matching the boot CPUs family, model, stepping */
static __init struct microcode_intel *scan_microcode(void *data, size_t size,
struct ucode_cpu_info *uci,
@@ -330,6 +350,9 @@ static __init struct microcode_intel *scan_microcode(void *data, size_t size,
if (!intel_find_matching_signature(data, &uci->cpu_sig))
continue;
+ if (!revision_is_safe(&uci->cpu_sig, mc_header->rev))
+ continue;
+
/*
* For saving the early microcode, find the matching revision which
* was loaded on the BSP.
@@ -878,6 +901,9 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)
if (!intel_find_matching_signature(mc, &uci->cpu_sig))
continue;
+ if (!revision_is_safe(&uci->cpu_sig, mc_header.rev))
+ continue;
+
is_safe = ucode_validate_minrev(&mc_header);
if (force_minrev && !is_safe)
continue;
--
2.53.0
* Chang S. Bae <chang.seok.bae@intel.com> wrote:
> Microcode updates can usually jump revisions. However, there is an
> erratum on Granite Rapids systems. If they "jump over" revision
> 0x1000405, they result in #MC. Avoid it.
>
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
> Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: <stable@vger.kernel.org>
> ---
> V2 -> V3:
> * Shorten the changelog and rename the function (Boris)
> * Reduce the code comment (Dave)
> * Allow 0x1000405 loading. Thanks to Andrew, this fix got attention.
> * Collect Dave review tag. Thanks, Dave!
>
> Note:
> * GNR98 currently describes loading 0x1000405 itself is unsafe, but it
> will be updated to say okay with that. I will watch out the GNR98
> changes.
> * Jumping from < 0x1000380 to 0x1000405 was identified as an issue, but
> 0x1000380 is the first revision as GNR products. So loading 0x1000405
> in production systems should be okay.
> ---
> arch/x86/kernel/cpu/microcode/intel.c | 26 ++++++++++++++++++++++++++
> 1 file changed, 26 insertions(+)
>
> diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
> index 1142183c950c..30a22388d4b1 100644
> --- a/arch/x86/kernel/cpu/microcode/intel.c
> +++ b/arch/x86/kernel/cpu/microcode/intel.c
> @@ -309,6 +309,26 @@ static void save_microcode_patch(struct microcode_intel *patch)
> pr_err("Unable to allocate microcode memory size: %u\n", size);
> }
>
> +static bool revision_is_safe(struct cpu_signature *sig, u32 rev)
> +{
> + u32 vfm = IFM(x86_family(sig->sig), x86_model(sig->sig));
> +
> + /*
> + * Erratum GNR98 can cause #MC's if "jumping over" revision 0x1000405.
> + * Avoid the jumps.
> + */
> + if (vfm == INTEL_GRANITERAPIDS_X &&
> + x86_stepping(sig->sig) == 1 &&
> + sig->pf & 0x95 &&
> + sig->rev < 0x1000405 &&
> + rev > 0x1000405) {
> + pr_err_once("Erratum GNR98: skipping revision 0x%x.\n", rev);
So this was explained in a really confusing way, I had to read the
changelog and comments trice and then the code to figure out what's
going on:
- There's a microcode bug that makes it unsafe to apply current
post-0x1000405 revisions on Granite Rapid CPUs if the current
microcode version is below 0x1000405. The interim 0x1000405
version *must* be applied first for it to be safe to upgrade
GNR CPUs. Will this be a problem perpetually? Will it be unsafe
to have an older GNR CPU and simply apply fresh microcode to it,
without first loading the interim 0x1000405 version? Will GNR
microcode upgrades on pre-0x1000405 CPUs will always be a
two-step process?
- The new code doesn't declare it, but this patch creates a hidden,
permanent microcode version upgrade barrier if user-space
firmware/microcode tools do not provide the 0x1000405
microcode version reliably and implement the two-step upgrade
workaround.
- The message the kernel prints is rather passive-aggressive as well:
Erratum GNR98: skipping revision 0x%x
It does not explain *why* the fresh microcode upgrade is skipped,
and if firmware tooling does not apply 0x1000405 then the kernel
stays in this state indefinitely.
It should at minimum say something like:
Erratum GNR98: new revision %x is unsafe to apply until 0x1000405 is applied first, skipping it. Please upgrade firmware tools.
Because, presumably, this scenario should not be possible with
new user-space tooling, right?
The comment and changelog should be updated accordingly as well.
So this patch is still problematic IMO.
Thanks,
Ingo
On Fri, Sep 18, 2026 at 08:54:01AM +0200, Ingo Molnar wrote:
> It should at minimum say something like:
>
> Erratum GNR98: new revision %x is unsafe to apply until 0x1000405 is applied first, skipping it. Please upgrade firmware tools.
Even if you put prose in a warning message, it still doesn't explain the issue
fully. For stuff like that you need some properly written longer explanation
in the documentation.
Which I'm hoping we'll get eventually once the dust has settled down.
That's what the rest of this thread is trying to do but I haven't reviewed
yet.
> So this patch is still problematic IMO.
This patch is a minimal stable fix to stop it from #MCing. The real stuff
comes later. It all has been discussed on this very thread.
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
> /* Scan blob for microcode matching the boot CPUs family, model, stepping */ > static __init struct microcode_intel *scan_microcode(void *data, size_t size, > struct ucode_cpu_info *uci, > @@ -330,6 +350,9 @@ static __init struct microcode_intel *scan_microcode(void *data, size_t size, > if (!intel_find_matching_signature(data, &uci->cpu_sig)) > continue; > > + if (!revision_is_safe(&uci->cpu_sig, mc_header->rev)) > + continue; > + > /* > * For saving the early microcode, find the matching revision which > * was loaded on the BSP. > @@ -878,6 +901,9 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter) > if (!intel_find_matching_signature(mc, &uci->cpu_sig)) > continue; > > + if (!revision_is_safe(&uci->cpu_sig, mc_header.rev)) > + continue; > + > is_safe = ucode_validate_minrev(&mc_header); > if (force_minrev && !is_safe) > continue; TL;dr: Should there be a revision_is_safe() check during __apply_microcode()? The patch only adds the revision check during blob selection. Have we evaluated the corner cases hinted by sashiko when this check might be bypassed? It talks about suspend/resume and the CPU hotplug cases. v1: https://sashiko.dev/#/patchset/20260901231634.714144-1-chang.seok.bae%40intel.com?part=1 v3: https://sashiko.dev/#/patchset/20260916225939.1144524-1-chang.seok.bae%40intel.com?part=1 The suspend/resume path probably doesn't matter for GNR servers and most of the CPU hotplug flows also seem to be covered. But, what about ucode update on cores that are not enabled at boot? If there are brought online later, would the ucode update skip the above check? For example, maxcpus=N prevents certain CPUs from showing up in cpus_booted_once_mask. So the checks in setup_cpus() during late-loading would not catch them. IIUC, the BIOS version can be < 0x1000405, early load can bump the booted cpus to 0x1000405, and then late-loading can load newer versions and only cache the latest ucode revision. All this while, the non-booted cores would be stuck at a revision less than 0x1000405. So, when they are brought online later, could they directly jump to a revision greater than 0x1000405? Would it be safer to add the revision_is_safe() check in __apply_microcode() so that all of such cases are covered? Maybe sashiko just made me paranoid. I only started looking at this because it complained! :(
On 9/17/2026 10:52 AM, Sohil Mehta wrote: > > TL;dr: Should there be a revision_is_safe() check during > __apply_microcode()? > > The patch only adds the revision check during blob selection. Have we > evaluated the corner cases hinted by sashiko when this check might be > bypassed? It talks about suspend/resume and the CPU hotplug cases. > > v1: > https://sashiko.dev/#/patchset/20260901231634.714144-1-chang.seok.bae%40intel.com?part=1 > > v3: > https://sashiko.dev/#/patchset/20260916225939.1144524-1-chang.seok.bae%40intel.com?part=1 > > The suspend/resume path probably doesn't matter for GNR servers and most > of the CPU hotplug flows also seem to be covered. But, what about ucode > update on cores that are not enabled at boot? If there are brought > online later, would the ucode update skip the above check? > > For example, maxcpus=N prevents certain CPUs from showing up in > cpus_booted_once_mask. So the checks in setup_cpus() during late-loading > would not catch them. IIUC, the BIOS version can be < 0x1000405, early > load can bump the booted cpus to 0x1000405, and then late-loading can > load newer versions and only cache the latest ucode revision. Currently, what setup_cpus() does as its comment says is first ensure all CPUs that are present and has been booted up have their primary threads online. It just allows its secondary thread offline with nosmt. I don't think the current logic is broken there. Then, those sibling threads assuming late-loading while soft-offlined will see an updated revision on its bringup because the loading scope is per-core by default, meaning the update performed by the primary thread also applies to its sibling. Also, the NMI stop-machine rendezvous includes those soft-offlined CPUs. They are brought into the rendezvous and wait there while the update is being performed. Now, I think it could be misleading if we put the revision check toward the end right before the application. For example, suppose a multi-blob image that is bundled with revision 0x1000405 and later ones and currently running < 0x1000405. With the check during blob selection, the parser can identify 0x1000405 as the loadable revision and reject later revisions. If we move the check to just before application, the parser may instead select a revision newer than 0x1000405. The application would then be rejected, and the same blob would be selected again on the next attempt, resulting in the loading process repeatedly aborting without ever making progress. So the revision check needs to remain part of blob selection, where it can affect which revision is considered loadable, rather than being only an application-time check. Thanks, Chang
> If we move the check to just before application, the parser may instead
> select a revision newer than 0x1000405. The application would then be
> rejected, and the same blob would be selected again on the next attempt,
> resulting in the loading process repeatedly aborting without ever making
> progress.
>
> So the revision check needs to remain part of blob selection, where it
> can affect which revision is considered loadable, rather than being only
> an application-time check.
Agreed that we need the early check.
But we could, on the actual application, at least check if the ucode already got upgraded to EXACTLY the target version, and skip the actual application if it has been upgraded. That is a simple check with basically no policy ("identity") but with huge savings
On 9/17/2026 11:43 AM, Van De Ven, Arjan wrote:
>
> But we could, on the actual application, at least check if the ucode
> already got upgraded to EXACTLY the target version, and skip the
> actual application if it has been upgraded. That is a simple check
> with basically no policy ("identity") but with huge savings
Yes, I think that' what we do now:
__apply_microcode(..., struct microcode_intel *mc, ...)
{
u32 rev;
if (!mc)
return UCODE_NFOUND;
*cur_rev = intel_get_microcode_revision();
^ read MSR0x8b (aka MSR_IA32_UCODE_REV)
if (*cur_rev >= mc->hdr.rev) {
uci->cpu_sig.rev = *cur_rev;
return UCODE_OK;
^ we skip the loading if updated
}
/* write microcode via MSR 0x79 */
native_wrmsrq(MSR_IA32_UCODE_WRITE, (unsigned long)mc->bits);
...
}
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/cpu/microcode/intel.c#n646
Thanks,
Chang
On Wed, 16 Sep 2026 22:59:39 +0000
"Chang S. Bae" <chang.seok.bae@intel.com> wrote:
> Microcode updates can usually jump revisions. However, there is an
> erratum on Granite Rapids systems. If they "jump over" revision
> 0x1000405, they result in #MC. Avoid it.
A probably silly question.
Is it valid to downgrade microcode?
David
>
> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
> Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: <stable@vger.kernel.org>
> ---
> V2 -> V3:
> * Shorten the changelog and rename the function (Boris)
> * Reduce the code comment (Dave)
> * Allow 0x1000405 loading. Thanks to Andrew, this fix got attention.
> * Collect Dave review tag. Thanks, Dave!
>
> Note:
> * GNR98 currently describes loading 0x1000405 itself is unsafe, but it
> will be updated to say okay with that. I will watch out the GNR98
> changes.
> * Jumping from < 0x1000380 to 0x1000405 was identified as an issue, but
> 0x1000380 is the first revision as GNR products. So loading 0x1000405
> in production systems should be okay.
> ---
> arch/x86/kernel/cpu/microcode/intel.c | 26 ++++++++++++++++++++++++++
> 1 file changed, 26 insertions(+)
>
> diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
> index 1142183c950c..30a22388d4b1 100644
> --- a/arch/x86/kernel/cpu/microcode/intel.c
> +++ b/arch/x86/kernel/cpu/microcode/intel.c
> @@ -309,6 +309,26 @@ static void save_microcode_patch(struct microcode_intel *patch)
> pr_err("Unable to allocate microcode memory size: %u\n", size);
> }
>
> +static bool revision_is_safe(struct cpu_signature *sig, u32 rev)
> +{
> + u32 vfm = IFM(x86_family(sig->sig), x86_model(sig->sig));
> +
> + /*
> + * Erratum GNR98 can cause #MC's if "jumping over" revision 0x1000405.
> + * Avoid the jumps.
> + */
> + if (vfm == INTEL_GRANITERAPIDS_X &&
> + x86_stepping(sig->sig) == 1 &&
> + sig->pf & 0x95 &&
> + sig->rev < 0x1000405 &&
> + rev > 0x1000405) {
> + pr_err_once("Erratum GNR98: skipping revision 0x%x.\n", rev);
> + return false;
> + }
> +
> + return true;
> +}
> +
> /* Scan blob for microcode matching the boot CPUs family, model, stepping */
> static __init struct microcode_intel *scan_microcode(void *data, size_t size,
> struct ucode_cpu_info *uci,
> @@ -330,6 +350,9 @@ static __init struct microcode_intel *scan_microcode(void *data, size_t size,
> if (!intel_find_matching_signature(data, &uci->cpu_sig))
> continue;
>
> + if (!revision_is_safe(&uci->cpu_sig, mc_header->rev))
> + continue;
> +
> /*
> * For saving the early microcode, find the matching revision which
> * was loaded on the BSP.
> @@ -878,6 +901,9 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)
> if (!intel_find_matching_signature(mc, &uci->cpu_sig))
> continue;
>
> + if (!revision_is_safe(&uci->cpu_sig, mc_header.rev))
> + continue;
> +
> is_safe = ucode_validate_minrev(&mc_header);
> if (force_minrev && !is_safe)
> continue;
> > Microcode updates can usually jump revisions. However, there is an > > erratum on Granite Rapids systems. If they "jump over" revision > > 0x1000405, they result in #MC. Avoid it. > > A probably silly question. > Is it valid to downgrade microcode? Within SVN in theory it can be done In practice, unless you have very special circumstances (and check with Intel if the exact downgrade you have in mind has been tested for downgrading) I would very strongly recommend against doing so. It may appear to work, but note that A -> B -> A Is NOT identical to just A (staying at A) as part of the microcode load, some microcode runs as "setup" which may change settings in the CPU (versus runtime behavior changes) -- and that setup step is not undone as you go back to A.... which means you run A with a (partial or whole) setup of B.
On 17/09/2026 11:14 am, David Laight wrote: > On Wed, 16 Sep 2026 22:59:39 +0000 > "Chang S. Bae" <chang.seok.bae@intel.com> wrote: > >> Microcode updates can usually jump revisions. However, there is an >> erratum on Granite Rapids systems. If they "jump over" revision >> 0x1000405, they result in #MC. Avoid it. > A probably silly question. > Is it valid to downgrade microcode? Yes, and it does happen in practice. Intel microcode contains a field called the Security Version Number. The SVN bumps as infrequently as possible, but does bump when necessary. There is a hard block on the SVN going backwards, but as long as you're not violating this constraint, you can upgrade or downgrade microcode as desired. ~Andrew
The following commit has been merged into the x86/urgent branch of tip:
Commit-ID: e7d3e2f46dd5a69046e6d95a0f189155a5516b93
Gitweb: https://git.kernel.org/tip/e7d3e2f46dd5a69046e6d95a0f189155a5516b93
Author: Chang S. Bae <chang.seok.bae@intel.com>
AuthorDate: Wed, 16 Sep 2026 22:59:39
Committer: Borislav Petkov (AMD) <bp@alien8.de>
CommitterDate: Thu, 17 Sep 2026 16:54:20 -07:00
x86/microcode/intel: Reject problematic loading on Granite Rapids systems
Microcode updates can usually jump revisions. However, there is an erratum on
Granite Rapids systems. If they "jump over" revision 0x1000405, they result in
an #MC. Avoid it.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260916225939.1144524-1-chang.seok.bae@intel.com
---
arch/x86/kernel/cpu/microcode/intel.c | 26 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+)
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index 1142183..9f09d38 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -309,6 +309,26 @@ static void save_microcode_patch(struct microcode_intel *patch)
pr_err("Unable to allocate microcode memory size: %u\n", size);
}
+static bool revision_is_safe(struct cpu_signature *sig, u32 rev)
+{
+ u32 vfm = IFM(x86_family(sig->sig), x86_model(sig->sig));
+
+ /*
+ * Erratum GNR98 can cause #MCs if "jumping over" revision 0x1000405.
+ * Avoid the jumps.
+ */
+ if (vfm == INTEL_GRANITERAPIDS_X &&
+ x86_stepping(sig->sig) == 1 &&
+ sig->pf & 0x95 &&
+ sig->rev < 0x1000405 &&
+ rev > 0x1000405) {
+ pr_err_once("Erratum GNR98: skipping revision 0x%x.\n", rev);
+ return false;
+ }
+
+ return true;
+}
+
/* Scan blob for microcode matching the boot CPUs family, model, stepping */
static __init struct microcode_intel *scan_microcode(void *data, size_t size,
struct ucode_cpu_info *uci,
@@ -330,6 +350,9 @@ static __init struct microcode_intel *scan_microcode(void *data, size_t size,
if (!intel_find_matching_signature(data, &uci->cpu_sig))
continue;
+ if (!revision_is_safe(&uci->cpu_sig, mc_header->rev))
+ continue;
+
/*
* For saving the early microcode, find the matching revision which
* was loaded on the BSP.
@@ -878,6 +901,9 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)
if (!intel_find_matching_signature(mc, &uci->cpu_sig))
continue;
+ if (!revision_is_safe(&uci->cpu_sig, mc_header.rev))
+ continue;
+
is_safe = ucode_validate_minrev(&mc_header);
if (force_minrev && !is_safe)
continue;
© 2016 - 2026 Red Hat, Inc.