[PATCH RFC 7/8] x86/microcode: Introduce iterative late loading
From: Chang S. Bae
Date: Tue Sep 01 2026 - 19:46:25 EST
The minimum revision requirement was introduced to handle dependencies
between microcode revisions. On the other hand, Intel has traditionally
distributed only the latest microcode blob in the repository [1].
Instead, to better accommodate those dependent blobs at hand, it is
useful to concatenate blobs together into a single image. This allows
loading the prerequisite revisions in sequence.
The loader currently handles a multi-blob image by selecting the highest
revision without minimum revision enforcement. This loading approach may
not achieve the safety requirement.
If enforced, it skips revisions whose prerequisites have not been loaded,
possibly leaving the loading process at an intermediate revision. Then,
admins have to manually trigger another load to apply the remaining
higher revisions.
To avoid this manual step, introduce iterative late loading. After each
successful update, let the vendor-specific parser select the next blob.
If the parser selects the lowest loadable revision on each pass, the
loader can apply the blobs incrementally until no more blobs are
available.
For now, vendor code rejects this loading because the blob parser is not
ready yet to support incremental blob selection.
To retain the existing behavior by default, add a Kconfig option and a
kernel command-line option to select iterative loading.
[1] https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files.git
Signed-off-by: Chang S. Bae <chang.seok.bae@xxxxxxxxx>
---
.../admin-guide/kernel-parameters.txt | 4 ++
arch/x86/Kconfig | 12 +++++
arch/x86/kernel/cpu/microcode/amd.c | 3 +-
arch/x86/kernel/cpu/microcode/core.c | 53 +++++++++++++++++--
arch/x86/kernel/cpu/microcode/intel.c | 3 ++
arch/x86/kernel/cpu/microcode/internal.h | 1 +
6 files changed, 72 insertions(+), 4 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index b5f7a8142f58..67f5b233315f 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -4076,6 +4076,10 @@ Kernel parameters
for the microcode loader according to <bool>. If <bool>
is not given, enable the enforcement.
+ iterative_loading=<bool>:
+ Enable or disable iterative microcode patch application
+ for the runtime microcode loader according to <bool>.
+
mini2440= [ARM,HW,KNL]
Format:[0..2][b][c][t]
Default: "0tb"
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 6c503004775e..021c77a6ad4b 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1355,6 +1355,18 @@ config MICROCODE_FORCE_MINREV
If unsure say Y.
+config MICROCODE_LATE_ITERATIVE_LOADING
+ bool "Iterative microcode late loading"
+ default n
+ depends on MICROCODE_LATE_LOADING
+ help
+ Apply the microcode blobs in a concatenated image one at a time, in
+ ascending revision order, so that revisions with prerequisites can be
+ applied in a single process.
+
+ This loading option can also be controlled via the
+ "microcode=iterative_loading=" parameter on the kernel command line.
+
config MICROCODE_DBG
bool "Enable microcode loader debugging"
default n
diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c
index 0625d5e8eb7e..97b30da9e5e6 100644
--- a/arch/x86/kernel/cpu/microcode/amd.c
+++ b/arch/x86/kernel/cpu/microcode/amd.c
@@ -1251,7 +1251,8 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device)
enum ucode_state ret = UCODE_NFOUND;
const struct firmware *fw;
- if (force_minrev)
+ /* The blob parser does not support these features yet. */
+ if (force_minrev || iterative_loading)
return UCODE_NFOUND;
if (c->x86 >= 0x15)
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index 7d6caf191795..e7e159479975 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -47,6 +47,7 @@ static struct microcode_ops *microcode_ops;
static bool dis_ucode_ldr;
bool force_minrev = IS_ENABLED(CONFIG_MICROCODE_FORCE_MINREV);
+bool iterative_loading = IS_ENABLED(CONFIG_MICROCODE_LATE_ITERATIVE_LOADING);
/*
* Those below should be behind CONFIG_MICROCODE_DBG ifdeffery but in
@@ -166,6 +167,12 @@ static void __init early_parse_cmdline(void)
continue;
}
+ if (str_has_prefix(s, "iterative_loading=")) {
+ advance_option_argument(&s);
+ if (kstrtobool(s, &iterative_loading)) { ; }
+ continue;
+ }
+
if (!strcmp(s, "dis_ucode_ldr"))
dis_ucode_ldr = true;
}
@@ -750,16 +757,34 @@ static bool setup_cpus(void)
return true;
}
+static void reset_ucode_ctrl(void)
+{
+ struct microcode_ctrl ctrl = { .ctrl = SCTRL_WAIT, .result = -1, };
+ unsigned int cpu;
+
+ for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
+ ctrl.ctrl_cpu = per_cpu(ucode_ctrl.ctrl_cpu, cpu);
+ per_cpu(ucode_ctrl, cpu) = ctrl;
+ }
+}
+
static int load_late_locked(void)
{
+ enum ucode_state state;
+ int err;
+
if (!setup_cpus())
return -EBUSY;
- switch (microcode_ops->request_microcode_fw(0, µcode_fdev->dev)) {
+ state = microcode_ops->request_microcode_fw(0, µcode_fdev->dev);
+next:
+ switch (state) {
case UCODE_NEW:
- return load_late_stop_cpus(false);
+ err = load_late_stop_cpus(false);
+ break;
case UCODE_NEW_SAFE:
- return load_late_stop_cpus(true);
+ err = load_late_stop_cpus(true);
+ break;
case UCODE_NFOUND:
return -ENOENT;
case UCODE_OK:
@@ -767,6 +792,28 @@ static int load_late_locked(void)
default:
return -EBADFD;
}
+
+ if (err)
+ return err;
+
+ /*
+ * A multi-blob image is traditionally handled by selecting the highest
+ * revision to load it in one shot. With iterative loading, the
+ * vendor-specific parser instead selects the lowest loadable revision.
+ *
+ * After each successful update, find the next loadable blob to continue
+ * the iteration. Stop if no more blobs are found.
+ */
+ if (iterative_loading) {
+ state = microcode_ops->request_microcode_fw(0, µcode_fdev->dev);
+ if (state == UCODE_NFOUND)
+ return 0;
+
+ reset_ucode_ctrl();
+ goto next;
+ }
+
+ return 0;
}
static ssize_t reload_store(struct device *dev,
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index ddd5b72b27a1..48e7d023ea41 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -978,6 +978,9 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device)
if (is_late_loading_denied(cpu))
return UCODE_NFOUND;
+ if (iterative_loading)
+ return UCODE_NFOUND;
+
sprintf(name, "intel-ucode/%02x-%02x-%02x",
c->x86, c->x86_model, c->x86_stepping);
diff --git a/arch/x86/kernel/cpu/microcode/internal.h b/arch/x86/kernel/cpu/microcode/internal.h
index 1b35f9099580..311e885819a2 100644
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -101,6 +101,7 @@ static inline unsigned int x86_cpuid_family(void)
}
extern bool force_minrev;
+extern bool iterative_loading;
#ifdef CONFIG_CPU_SUP_AMD
void load_ucode_amd_bsp(struct early_load_data *ed, unsigned int family);
--
2.53.0