* [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature
@ 2026-09-12 0:08 Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 1/8] cpu/hotplug: Allow architecture-specific primary CPU bringup Chang S. Bae
` (8 more replies)
0 siblings, 9 replies; 10+ messages in thread
From: Chang S. Bae @ 2026-09-12 0:08 UTC (permalink / raw)
To: linux-kernel; +Cc: x86, tglx, mingo, bp, dave.hansen, kevin.shu, chang.seok.bae
Hi all,
This series enables another Intel microcode loading feature.
While this initial posting is marked with RFC, it is also expected to
provide patches to the people who are interested in testing/using this
feature. Tested-by is anticipated from that end.
So, x86 maintainers, feel free to ignore this round. Having said that,
feedback will be appreciated and always welcomed.
== Introduction ==
Traditionally, a single trigger updates a core-scoped microcode, which
thus requires executing WRMSR0x79 on every core. This also means that
there is a possibility to load different microcode patches between cores.
Despite this fact, the kernel currently enforces loading the same image
across the CPUs.
This uniform microcode conceptually eliminates a chance of running a
different microcode within a scope of CPUs. From the loader perspective,
the uniform feature extends the loading scope to a larger number of cores
than one core.
A few points worth calling out about the feature:
* The CPU enumerates the update scope such as package-wide or system-
wide, depending on the implementation. The scope is advertised
via MSR and isn't programmable.
* The scope reduces the number of triggers, whereas staging primarily
reduces the amount of work under the WRMSR window. Unlike staging,
uniform loading is applicable to both early- and late-loading paths.
* For early loading, only the parallel CPU bringup is relevant. In the
legacy serial bringup, once the first CPU in a scope completes the
update, subsequent CPUs will observe the updated revision so skip
WRMSR0x79.
* Staging introduced a new loading process. But uniform loading extends
the semantics of the existing flow. Software that assumes the legacy
scope remains supported. The next section discusses this
compatibility aspect in more detail.
== Backward Compatibility ==
Older kernels assume a per-core scope, being ignorant of the uniform
loading scope. So, they trigger loading via WRMSR0x79 on every core. And
the spec [1] has the following statement, in Section 2.4 "Uniform
Microcode Update":
NOTE [*]
... It is always allowed to load the update on more logical processors
than necessary, which may result in unnecessary additional latency.
So this means legacy kernels remain functional on uniform systems. To
provide more context, folks involved in the implementation agreed to
share additional implementation details with the community. Their
write-up is attached at the end of this cover letter.
== Latency Trade-off ==
The above spec also states `additional latency` which in fact is a
side-effect from preserving the backward compatibility. Excessive update
triggers may elevate lock contention and waiting time inside the
microcode updater.
For example, consider a system with 100 cores per package where hardware
advertises package scope. Ideally, only one WRMSR is enough instead of
100. However, if all 100 CPUs attempt the update concurrently, the
update mechanism has to secure an atomic operation where only one CPU
participates in the actual update while the others wait.
This effect is measurable when comparing kernels with and without
uniform support. The exact numbers depend on the patch characteristic,
the lock contention, and the underlying synchronization implementation.
On multiple implementations, uniform loading showed loading-time
reductions especially during the late loading.
On the other hand, early loading showed only marginal gains from parallel
bringup. CPU0 already updates the scoped APs before they come online. In
addition, unlike stop_machine()-based late loading, parallel bringup is
not strictly simultaneous but instead iterates through CPUs for bringup.
Thus, the degree of contention appears much lower during early loading.
== Call for Reviews ==
Below are some aspects to collect feedback:
* Early-loading support
Supporting uniform scopes during early loading appears consistent
with late loading support. But there is also a trade-off to consider:
code complexity vs benefit. As mentioned above, the measurable
latency impact looks marginal. And at the same time infrastructure
change (patches 1/2) also appears moderate (except for the new
cpumask -- see below).
If the consensus ends up skipping uniform support for early loading,
then at least the microcode implementation details need to be
documented instead.
* New cpumask: primary core CPUs
Introducing a new topology cpumask was not the preferred option. But,
during early loading CPU0 must determine which APs participate in the
primary bringup phase before AP topology state is fully established.
For both core- and system-scopes, the selection is straightforward.
But package scope cannot reliably depend on package IDs at that stage
because logical package IDs are established later during AP bringup.
Patch3 has more detail.
* Error handling on firmware misconfiguration
If firmware is expected to configure the feature but leaves the
system in an incomplete state, then that could be an indication of
an unreliable situation. This version disables the loader entirely
(patch8).
Alternatively, instead of being paranoid, tainting could be an option
too.
But any review beyond these points are definitely welcome too.
== Patchset and Validation ==
This series can be divided into two parts:
* Part 1, patch 1-3: Preparatory infrastructure changes
* Part 2, patch 4-8: Uniform-loading enablement
Testing was performed on one primary test machine. Since the feature is
architectural, the expected semantics should remain consistent across
implementations.
The patch set is available in this repository:
git://github.com/intel-staging/microcode.git uniform_rfc-v1
Thanks,
Chang
== Reference ==
[1] Intel Runtime Microcode Update Technical Paper
https://cdrdv2.intel.com/v1/dl/getContent/782715
[*] The NOTE paragraph primarily discusses certain unusual configurations
where the uniform scope is not explicitly exported but effectively
active. But the quoted compatibility statement should stand
generally.
== Appendix: Microcode Implementation Note ==
The uniform update protocol is an optimization for boot/runtime microcode
update. It is backward compatible with existing microarchitecture of
core/thread scope update and any OS MCU drivers that rely on legacy
method of update.
With uniform update, if multiple logical processors attempt to load an
update simultaneously, there is a race to an internal semaphore within
the microcode. The winner of the race assumes control of the update
process and sends an internal interrupt to all other threads (if only one
thread initiates the update, it is the winner by default).
All other logical processors receive the internal interrupt at an
architectural instruction boundary and proceed to load the update under
the coordination of the winner. This ensures that the responding threads
load the update in a controlled manner while at a well-defined
architectural instruction boundary. If a higher priority interrupt or a
fault happens, all logical processors will see it either before the
microcode patch has been applied or after. In either case, all logical
processors will see the same microcode revision and nothing intermediate.
Chang S. Bae (8):
cpu/hotplug: Allow architecture-specific primary CPU bringup
x86/hotplug: Implement SMT-primary selection for parallel bringup
x86/cpu/topology: Introduce primary core mask
x86/microcode: Extend struct microcode_ops for uniform loading
x86/microcode: Clarify online enforcement with uniform loading
x86/microcode: Support uniform scope for late loading
x86/microcode/intel: Support uniform scope for early loading
x86/microcode/intel: Enable uniform loading
arch/Kconfig | 4 +
arch/x86/Kconfig | 1 +
arch/x86/include/asm/microcode.h | 2 +
arch/x86/include/asm/msr-index.h | 10 ++
arch/x86/include/asm/topology.h | 3 +
arch/x86/kernel/cpu/microcode/core.c | 76 ++++++++++-
arch/x86/kernel/cpu/microcode/intel.c | 153 +++++++++++++++++++++--
arch/x86/kernel/cpu/microcode/internal.h | 15 ++-
arch/x86/kernel/cpu/topology.c | 23 +++-
arch/x86/kernel/cpu/topology_common.c | 9 ++
include/linux/cpu.h | 9 ++
kernel/cpu.c | 8 +-
12 files changed, 291 insertions(+), 22 deletions(-)
base-commit: df2908090cda368b01ff43709f51890076c56157
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH RFC v1 1/8] cpu/hotplug: Allow architecture-specific primary CPU bringup
2026-09-12 0:08 [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Chang S. Bae
@ 2026-09-12 0:08 ` Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 2/8] x86/hotplug: Implement SMT-primary selection for parallel bringup Chang S. Bae
` (7 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Chang S. Bae @ 2026-09-12 0:08 UTC (permalink / raw)
To: linux-kernel; +Cc: x86, tglx, mingo, bp, dave.hansen, kevin.shu, chang.seok.bae
Parallel CPU bringup currently proceeds in two phases: first for the SMT
primary threads and then second for the remaining secondary threads. This
ordering avoids race conditions, specifically for x86 microcode loading.
Upcoming x86 changes expand the microcode loading scope beyond a single
core to package scope or even system-wide updates. Introduce architecture
hooks that allow customizing which CPUs participate in the first phase:
* arch_cpuhp_primary_aware() - indicates whether the architecture
provides a custom primary CPU selection scheme, or not.
* arch_cpuhp_get_primary_cpus() - returns a CPU mask for the first
bringup iteration.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
arch/Kconfig | 4 ++++
include/linux/cpu.h | 9 +++++++++
kernel/cpu.c | 8 ++++++--
3 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/arch/Kconfig b/arch/Kconfig
index 45c657772362..f38d80d583bf 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -102,6 +102,10 @@ config HOTPLUG_PARALLEL
bool
select HOTPLUG_SPLIT_STARTUP
+config HOTPLUG_PARALLEL_ARCH_PRIMARY
+ bool
+ depends on HOTPLUG_PARALLEL
+
config GENERIC_IRQ_ENTRY
bool
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 9b6b0d87fdb0..11e375d00df7 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -19,6 +19,7 @@
#include <linux/cpuhotplug.h>
#include <linux/cpuhplock.h>
#include <linux/cpu_smt.h>
+#include <linux/cpumask.h>
struct device;
struct device_node;
@@ -233,4 +234,12 @@ int arch_prctl_get_branch_landing_pad_state(struct task_struct *t, unsigned long
int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state);
int arch_prctl_lock_branch_landing_pad_state(struct task_struct *t);
+#ifdef CONFIG_HOTPLUG_PARALLEL_ARCH_PRIMARY
+bool __init arch_cpuhp_primary_aware(void);
+const struct cpumask *__init arch_cpuhp_get_primary_cpus(void);
+#else
+static inline bool arch_cpuhp_primary_aware(void) { return false; }
+static inline const struct cpumask *arch_cpuhp_get_primary_cpus(void) { return cpu_none_mask; }
+#endif
+
#endif /* _LINUX_CPU_H_ */
diff --git a/kernel/cpu.c b/kernel/cpu.c
index b3c8553d7bd6..5225f91c4d8c 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -1837,15 +1837,19 @@ static bool __init cpuhp_bringup_cpus_parallel(unsigned int ncpus)
if (!__cpuhp_parallel_bringup)
return false;
- if (cpuhp_smt_aware()) {
- const struct cpumask *pmask = cpuhp_get_primary_thread_mask();
+ if (arch_cpuhp_primary_aware() || cpuhp_smt_aware()) {
static struct cpumask tmp_mask __initdata;
+ const struct cpumask *pmask;
/*
* X86 requires to prevent that SMT siblings stopped while
* the primary thread does a microcode update for various
* reasons. Bring the primary threads up first.
*/
+ pmask = arch_cpuhp_primary_aware() ?
+ arch_cpuhp_get_primary_cpus() :
+ cpuhp_get_primary_thread_mask();
+
cpumask_and(&tmp_mask, mask, pmask);
cpuhp_bringup_mask(&tmp_mask, ncpus, CPUHP_BP_KICK_AP);
cpuhp_bringup_mask(&tmp_mask, ncpus, CPUHP_ONLINE);
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH RFC v1 2/8] x86/hotplug: Implement SMT-primary selection for parallel bringup
2026-09-12 0:08 [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 1/8] cpu/hotplug: Allow architecture-specific primary CPU bringup Chang S. Bae
@ 2026-09-12 0:08 ` Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 3/8] x86/cpu/topology: Introduce primary core mask Chang S. Bae
` (6 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Chang S. Bae @ 2026-09-12 0:08 UTC (permalink / raw)
To: linux-kernel; +Cc: x86, tglx, mingo, bp, dave.hansen, kevin.shu, chang.seok.bae
Parallel CPU bringup executes in two phases to avoid microcode loading
races between SMT siblings. The core code now supports architecture-
specific customization of the first bringup phase. Implement the x86 logic
to preserve the existing behavior.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
Note:
With this change, the generic SMT-aware two-phase parallel bringup logic
appears redundant since it was originally introduced for the x86
microcode loader path.
But, another architecture, MIPS, also adopted the primary-aware parallel
bringup model in:
76c43eb507bc ("MIPS: SMP: Implement parallel CPU bring up for EyeQ")
So the generic fallback path still appears to have valid users.
---
arch/x86/Kconfig | 1 +
arch/x86/kernel/cpu/microcode/core.c | 18 ++++++++++++++++++
2 files changed, 19 insertions(+)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 15fd9ec5ecac..cd6d4dfe3349 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1319,6 +1319,7 @@ config MICROCODE
def_bool y
depends on CPU_SUP_AMD || CPU_SUP_INTEL
select CRYPTO_LIB_SHA256 if CPU_SUP_AMD
+ select HOTPLUG_PARALLEL_ARCH_PRIMARY if HOTPLUG_PARALLEL
config MICROCODE_INITRD32
def_bool y
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index 0dd0c7241c57..36f08e2c99fb 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -869,6 +869,24 @@ static int mc_cpu_down_prep(unsigned int cpu)
return 0;
}
+#ifdef CONFIG_HOTPLUG_PARALLEL_ARCH_PRIMARY
+
+/*
+ * Queried by the parallel bringup code to determine whether x86 provides a
+ * custom primary CPU mask for early loading.
+ */
+bool __init arch_cpuhp_primary_aware(void)
+{
+ return __max_threads_per_core > 1;
+}
+
+const struct cpumask *__init arch_cpuhp_get_primary_cpus(void)
+{
+ return cpu_primary_thread_mask;
+}
+
+#endif /* CONFIG_HOTPLUG_PARALLEL_ARCH_PRIMARY */
+
static struct attribute *cpu_root_microcode_attrs[] = {
#ifdef CONFIG_MICROCODE_LATE_LOADING
&dev_attr_reload.attr,
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH RFC v1 3/8] x86/cpu/topology: Introduce primary core mask
2026-09-12 0:08 [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 1/8] cpu/hotplug: Allow architecture-specific primary CPU bringup Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 2/8] x86/hotplug: Implement SMT-primary selection for parallel bringup Chang S. Bae
@ 2026-09-12 0:08 ` Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 4/8] x86/microcode: Extend struct microcode_ops for uniform loading Chang S. Bae
` (5 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Chang S. Bae @ 2026-09-12 0:08 UTC (permalink / raw)
To: linux-kernel; +Cc: x86, tglx, mingo, bp, dave.hansen, kevin.shu, chang.seok.bae
The microcode loading distinguishes primary SMT threads from sibling
threads to avoid races during updates. Because early loading relies on
this distinction, parallel CPU bringup splits APs into two groups and
brings them up in phases.
Upcoming Intel microcode loading feature expands the loading scope beyond
per-core updates to package-wide or system-wide updates. While CPU0 is
the obvious primary for the system-wide scope, there is currently no
facility to identify per-package primary CPUs for the parallel bringup.
This matters because the parallel bringup logic must determine primary
CPUs before any AP is ever brought up. But topology_logical_package_id()
is not usable at that stage, as logical package IDs are established
during AP bringup itself, as described in:
7af541cee1e0e ("x86/topology: Don't evaluate logical IDs during early boot")
Unlike cpu_primary_thread_mask, there is also no relevant mask that
tracks one primary CPU per package. Thus, introduce cpu_primary_core_mask
to fill this gap.
The mask intentionally includes all threads of the selected core. This
preserves core-domain granularity and avoids coupling with the lower-
level topology details such as SMT primary selection.
Use the mask for the microcode staging path to simplify staging CPU
selection at the moment. Following changes will address the microcode
loading use.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
Note:
The staging path itself could have continued using topology_logical_package_id()
as the late loading is only relevant. IOW, the cpu_primary_core_mask
introduction was not required for the staging enablement back then.
---
arch/x86/include/asm/topology.h | 3 +++
arch/x86/kernel/cpu/microcode/intel.c | 14 ++++----------
arch/x86/kernel/cpu/topology.c | 23 ++++++++++++++++++++---
arch/x86/kernel/cpu/topology_common.c | 9 +++++++++
4 files changed, 36 insertions(+), 13 deletions(-)
diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h
index 1825691af941..02ad56cfc6f4 100644
--- a/arch/x86/include/asm/topology.h
+++ b/arch/x86/include/asm/topology.h
@@ -234,6 +234,9 @@ static inline unsigned int topology_amd_nodes_per_pkg(void) { return 1; }
extern struct cpumask __cpu_primary_thread_mask;
#define cpu_primary_thread_mask ((const struct cpumask *)&__cpu_primary_thread_mask)
+extern struct cpumask __cpu_primary_core_mask;
+#define cpu_primary_core_mask ((const struct cpumask *)&__cpu_primary_core_mask)
+
/**
* topology_is_primary_thread - Check whether CPU is the primary SMT thread
* @cpu: CPU to check
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index 1142183c950c..4b5feee14712 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -605,7 +605,6 @@ static int do_stage(u64 mmio_pa)
static void stage_microcode(void)
{
- unsigned int pkg_id = UINT_MAX;
int cpu, err;
u64 mmio_pa;
@@ -619,15 +618,10 @@ static void stage_microcode(void)
/*
* The MMIO address is unique per package, and all the SMT
- * primary threads are online here. Find each MMIO space by
- * their package IDs to avoid duplicate staging.
+ * primary threads are online here. Find unique MMIO space to
+ * avoid duplicate staging.
*/
- for_each_cpu(cpu, cpu_primary_thread_mask) {
- if (topology_logical_package_id(cpu) == pkg_id)
- continue;
-
- pkg_id = topology_logical_package_id(cpu);
-
+ for_each_cpu_and(cpu, cpu_primary_core_mask, cpu_primary_thread_mask) {
err = rdmsrq_on_cpu(cpu, MSR_IA32_MCU_STAGING_MBOX_ADDR, &mmio_pa);
if (WARN_ON_ONCE(err))
return;
@@ -635,7 +629,7 @@ static void stage_microcode(void)
err = do_stage(mmio_pa);
if (err) {
pr_err("Error: staging failed (%d) for CPU%d at package %u.\n",
- err, cpu, pkg_id);
+ err, cpu, topology_logical_package_id(cpu));
return;
}
}
diff --git a/arch/x86/kernel/cpu/topology.c b/arch/x86/kernel/cpu/topology.c
index 4913b64ec592..be6f5a3bd54f 100644
--- a/arch/x86/kernel/cpu/topology.c
+++ b/arch/x86/kernel/cpu/topology.c
@@ -75,12 +75,25 @@ bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
return phys_id == (u64)cpuid_to_apicid[cpu];
}
+static unsigned int __max_cores_per_package __ro_after_init = 1;
+
static void cpu_mark_primary_thread(unsigned int cpu, unsigned int apicid)
{
if (!(apicid & (__max_threads_per_core - 1)))
cpumask_set_cpu(cpu, &__cpu_primary_thread_mask);
}
+/*
+ * Select core 0 of every package and all SMT threads of that core so that the
+ * mask stays at core granularity.
+ */
+static void cpu_mark_primary_core(unsigned int cpu, unsigned int apicid)
+{
+ if (!((apicid >> get_count_order(__max_threads_per_core)) &
+ (__max_cores_per_package - 1)))
+ cpumask_set_cpu(cpu, &__cpu_primary_core_mask);
+}
+
/*
* Convert the APIC ID to a domain level ID by masking out the low bits
* below the domain level @dom.
@@ -505,13 +518,16 @@ void __init topology_init_possible_cpus(void)
pr_info("Max. logical dies: %3u\n", cntb);
pr_info("Max. dies per package: %3u\n", __max_dies_per_package);
- cnta = domain_weight(TOPO_CORE_DOMAIN);
- cntb = domain_weight(TOPO_SMT_DOMAIN);
+ cntb = domain_weight(TOPO_CORE_DOMAIN);
+ __max_cores_per_package = 1U << (get_count_order(cntb) - get_count_order(cnta));
+ pr_info("Max. cores per package:%3u\n", __max_cores_per_package);
+
+ cnta = domain_weight(TOPO_SMT_DOMAIN);
/*
* Can't use order delta here as order(cnta) can be equal
* order(cntb) even if cnta != cntb.
*/
- __max_threads_per_core = DIV_ROUND_UP(cntb, cnta);
+ __max_threads_per_core = DIV_ROUND_UP(cnta, cntb);
pr_info("Max. threads per core: %3u\n", __max_threads_per_core);
firstid = find_first_bit(apic_maps[TOPO_SMT_DOMAIN].map, MAX_LOCAL_APIC);
@@ -545,6 +561,7 @@ void __init topology_init_possible_cpus(void)
continue;
cpu_mark_primary_thread(cpu, apicid);
+ cpu_mark_primary_core(cpu, apicid);
set_cpu_present(cpu, test_bit(apicid, phys_cpu_present_map));
}
}
diff --git a/arch/x86/kernel/cpu/topology_common.c b/arch/x86/kernel/cpu/topology_common.c
index 6845e3c63fbb..dc1d96be8ba5 100644
--- a/arch/x86/kernel/cpu/topology_common.c
+++ b/arch/x86/kernel/cpu/topology_common.c
@@ -20,6 +20,15 @@ EXPORT_SYMBOL_GPL(__amd_nodes_per_pkg);
/* CPUs which are the primary SMT threads */
struct cpumask __cpu_primary_thread_mask __read_mostly;
+/*
+ * CPUs belonging to the primary core of each package
+ *
+ * To preserve core-domain granularity and to avoid the next-level primary
+ * details that cpu_primary_thread_mask can present, the mask includes all
+ * threads of each primary core.
+ */
+struct cpumask __cpu_primary_core_mask __read_mostly;
+
void topology_set_dom(struct topo_scan *tscan, enum x86_topology_domains dom,
unsigned int shift, unsigned int ncpus)
{
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH RFC v1 4/8] x86/microcode: Extend struct microcode_ops for uniform loading
2026-09-12 0:08 [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Chang S. Bae
` (2 preceding siblings ...)
2026-09-12 0:08 ` [PATCH RFC v1 3/8] x86/cpu/topology: Introduce primary core mask Chang S. Bae
@ 2026-09-12 0:08 ` Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 5/8] x86/microcode: Clarify online enforcement with " Chang S. Bae
` (4 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Chang S. Bae @ 2026-09-12 0:08 UTC (permalink / raw)
To: linux-kernel; +Cc: x86, tglx, mingo, bp, dave.hansen, kevin.shu, chang.seok.bae
Microcode loading is currently performed on a per-core basis. The loading
process concludes only after every core has been updated. As core counts
continue to grow, reducing the number of update invocations makes the
process increasingly efficient.
The uniform loading feature extends the update scope beyond a single
core, for example to a package or even an entire system. This means a
single update covers multiple cores, which in turn reduces the number of
update triggers.
The common late-loading logic determines the primary CPUs before invoking
vendor-specific callbacks. Extend struct microcode_ops with fields for
describing the feature support and the loading scope.
The actual loading flow changes will follow later.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
arch/x86/kernel/cpu/microcode/internal.h | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kernel/cpu/microcode/internal.h b/arch/x86/kernel/cpu/microcode/internal.h
index a10b547eda1e..0a74794e0245 100644
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -21,6 +21,13 @@ enum ucode_state {
UCODE_OFFLINE,
};
+enum uniform_scope {
+ UNIFORM_DEFAULT = 0,
+ UNIFORM_CORE,
+ UNIFORM_PKG,
+ UNIFORM_SYS,
+};
+
struct microcode_ops {
enum ucode_state (*request_microcode_fw)(int cpu, struct device *dev);
void (*microcode_fini_cpu)(int cpu);
@@ -36,7 +43,9 @@ struct microcode_ops {
void (*finalize_late_load)(int result);
unsigned int nmi_safe : 1,
use_nmi : 1,
- use_staging : 1;
+ use_staging : 1,
+ use_uniform : 1;
+ enum uniform_scope uniform_scope;
};
struct early_load_data {
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH RFC v1 5/8] x86/microcode: Clarify online enforcement with uniform loading
2026-09-12 0:08 [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Chang S. Bae
` (3 preceding siblings ...)
2026-09-12 0:08 ` [PATCH RFC v1 4/8] x86/microcode: Extend struct microcode_ops for uniform loading Chang S. Bae
@ 2026-09-12 0:08 ` Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 6/8] x86/microcode: Support uniform scope for late loading Chang S. Bae
` (3 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Chang S. Bae @ 2026-09-12 0:08 UTC (permalink / raw)
To: linux-kernel; +Cc: x86, tglx, mingo, bp, dave.hansen, kevin.shu, chang.seok.bae
The microcode update process requires at least one primary thread to
oversee the update for each defined scope. Currently, the kernel mandates
that all SMT primary threads remain online, assuming a core scope.
When the loading scope is extended to cover a package or the entire
system, this enforcement may be seen as unnecessary because offlining
more CPUs is affordable as long as at least one CPU remains to
participate in the microcode loading.
The nosmt option can be considered for practical reasons. But it is
impractical to aggressively switch off cores during such system-critical
updates. So, the current online enforcement isn't expected to be too
excessive even with the uniform loading.
Clarify this consideration in code comments.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
arch/x86/kernel/cpu/microcode/core.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index 36f08e2c99fb..8f617745d4d0 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -719,6 +719,12 @@ static bool setup_cpus(void)
*
* Ensure that the primary thread is online so that it is
* guaranteed that all cores are updated.
+ *
+ * When the loading scope is extended beyond each core, it is
+ * not strictly required to have primary threads online.
+ * However, during such system-critical updates, offlining CPUs
+ * besides the nosmt case isn't a sensible measure either. So,
+ * this mandate remains considerably benign.
*/
if (!cpu_online(cpu)) {
if (topology_is_primary_thread(cpu) || !allow_smt_offline) {
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH RFC v1 6/8] x86/microcode: Support uniform scope for late loading
2026-09-12 0:08 [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Chang S. Bae
` (4 preceding siblings ...)
2026-09-12 0:08 ` [PATCH RFC v1 5/8] x86/microcode: Clarify online enforcement with " Chang S. Bae
@ 2026-09-12 0:08 ` Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 7/8] x86/microcode/intel: Support uniform scope for early loading Chang S. Bae
` (2 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Chang S. Bae @ 2026-09-12 0:08 UTC (permalink / raw)
To: linux-kernel; +Cc: x86, tglx, mingo, bp, dave.hansen, kevin.shu, chang.seok.bae
In the late-loading path, per-CPU storage records a primary CPU in the
ctrl_cpu field of struct microcode_ctrl. The primary CPU is then
responsible for coordinating updates for the CPUs within its loading
scope via __load_primary().
To support uniform loading, provide helpers that allow a given CPU to
identify either its corresponding primary CPU or the set of CPUs in its
loading scope.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
arch/x86/kernel/cpu/microcode/core.c | 38 +++++++++++++++++++++++-----
1 file changed, 32 insertions(+), 6 deletions(-)
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index 8f617745d4d0..dfa311a2edbd 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -296,6 +296,36 @@ static void reload_early_microcode(unsigned int cpu)
static struct faux_device *microcode_fdev;
#ifdef CONFIG_MICROCODE_LATE_LOADING
+
+/*
+ * Return the CPU mask corresponding to the update scope that @cpu belongs to.
+ * Unknown scopes fall back to the legacy per-core scope.
+ */
+static const struct cpumask *ucode_scope_cpumask(unsigned int cpu)
+{
+ if (!microcode_ops->use_uniform)
+ return topology_sibling_cpumask(cpu);
+
+ switch (microcode_ops->uniform_scope) {
+ case UNIFORM_PKG:
+ return topology_core_cpumask(cpu);
+ case UNIFORM_SYS:
+ return cpu_online_mask;
+ case UNIFORM_DEFAULT:
+ case UNIFORM_CORE:
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+
+ return topology_sibling_cpumask(cpu);
+}
+
+static inline unsigned int ucode_primary_cpu(unsigned int cpu)
+{
+ return cpumask_first(ucode_scope_cpumask(cpu));
+}
+
/*
* Late loading dance. Why the heavy-handed stomp_machine effort?
*
@@ -437,7 +467,7 @@ static noinstr void load_secondary(unsigned int cpu)
static void __load_primary(unsigned int cpu)
{
- struct cpumask *secondaries = topology_sibling_cpumask(cpu);
+ const struct cpumask *secondaries = ucode_scope_cpumask(cpu);
enum sibling_ctrl ctrl;
enum ucode_state ret;
unsigned int sibling;
@@ -736,11 +766,7 @@ static bool setup_cpus(void)
continue;
}
- /*
- * Initialize the per CPU state. This is core scope for now,
- * but prepared to take package or system scope into account.
- */
- ctrl.ctrl_cpu = cpumask_first(topology_sibling_cpumask(cpu));
+ ctrl.ctrl_cpu = ucode_primary_cpu(cpu);
per_cpu(ucode_ctrl, cpu) = ctrl;
}
return true;
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH RFC v1 7/8] x86/microcode/intel: Support uniform scope for early loading
2026-09-12 0:08 [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Chang S. Bae
` (5 preceding siblings ...)
2026-09-12 0:08 ` [PATCH RFC v1 6/8] x86/microcode: Support uniform scope for late loading Chang S. Bae
@ 2026-09-12 0:08 ` Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 8/8] x86/microcode/intel: Enable uniform loading Chang S. Bae
2026-09-16 0:45 ` [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Borislav Petkov
8 siblings, 0 replies; 10+ messages in thread
From: Chang S. Bae @ 2026-09-12 0:08 UTC (permalink / raw)
To: linux-kernel; +Cc: x86, tglx, mingo, bp, dave.hansen, kevin.shu, chang.seok.bae
The parallel bringup facility queries whether the architecture provides a
custom set of primary CPUs. Extend x86 hooks with the Intel-specific
logic so that the primary CPU selection matches the uniform loading
scope.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
arch/x86/kernel/cpu/microcode/core.c | 9 +++
arch/x86/kernel/cpu/microcode/intel.c | 70 ++++++++++++++++++++++++
arch/x86/kernel/cpu/microcode/internal.h | 4 ++
3 files changed, 83 insertions(+)
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index dfa311a2edbd..ed547f2b4601 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -906,14 +906,23 @@ static int mc_cpu_down_prep(unsigned int cpu)
/*
* Queried by the parallel bringup code to determine whether x86 provides a
* custom primary CPU mask for early loading.
+ *
+ * Uniform loading scope is Intel-specific, so let the vendor code determine the
+ * primary CPU selection.
*/
bool __init arch_cpuhp_primary_aware(void)
{
+ if (x86_cpuid_vendor() == X86_VENDOR_INTEL)
+ return intel_primary_aware();
+
return __max_threads_per_core > 1;
}
const struct cpumask *__init arch_cpuhp_get_primary_cpus(void)
{
+ if (x86_cpuid_vendor() == X86_VENDOR_INTEL)
+ return intel_get_primary_cpus();
+
return cpu_primary_thread_mask;
}
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index 4b5feee14712..6178bddb7f60 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -989,6 +989,76 @@ static __init bool staging_available(void)
return !!(val & MCU_STAGING);
}
+bool __init intel_primary_aware(void)
+{
+ if (!microcode_intel_ops.use_uniform)
+ return __max_threads_per_core > 1;
+
+ switch (microcode_intel_ops.uniform_scope) {
+ case UNIFORM_SYS:
+ /*
+ * The boot CPU performs the update for the entire system. But,
+ * returning false would fall back to the SMT primary mask.
+ * Return true to indicate the primary CPU clearly.
+ */
+ return true;
+ case UNIFORM_PKG:
+ /* One primary core per package participates. */
+ return true;
+ case UNIFORM_DEFAULT:
+ case UNIFORM_CORE:
+ /*
+ * Fallback to the legacy per-core loading, where the primary
+ * CPU distinction matters only when SMT is on.
+ */
+ break;
+ default:
+ /* Unknown scopes fall back to the legacy per-core scope. */
+ WARN_ON_ONCE(1);
+ }
+
+ return __max_threads_per_core > 1;
+}
+
+static struct cpumask primary_cpus __initdata;
+
+static void __init set_primary_cpus(void)
+{
+ if (!microcode_intel_ops.use_uniform) {
+ cpumask_copy(&primary_cpus, cpu_primary_thread_mask);
+ return;
+ }
+
+ switch (microcode_intel_ops.uniform_scope) {
+ case UNIFORM_SYS:
+ /*
+ * CPU0 is guaranteed to be online and acts as the system
+ * primary.
+ */
+ cpumask_set_cpu(0, &primary_cpus);
+ break;
+ case UNIFORM_PKG:
+ /* One CPU per package is sufficient. */
+ cpumask_and(&primary_cpus, cpu_primary_core_mask, cpu_primary_thread_mask);
+ break;
+ case UNIFORM_DEFAULT:
+ case UNIFORM_CORE:
+ default:
+ cpumask_copy(&primary_cpus, cpu_primary_thread_mask);
+ }
+}
+
+/*
+ * The bringup code may query the mask more than once. Compute it on first use.
+ */
+const struct cpumask *__init intel_get_primary_cpus(void)
+{
+ if (cpumask_empty(&primary_cpus))
+ set_primary_cpus();
+
+ return &primary_cpus;
+}
+
struct microcode_ops * __init init_intel_microcode(void)
{
struct cpuinfo_x86 *c = &boot_cpu_data;
diff --git a/arch/x86/kernel/cpu/microcode/internal.h b/arch/x86/kernel/cpu/microcode/internal.h
index 0a74794e0245..0552d66f4012 100644
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -128,11 +128,15 @@ static inline void exit_amd_microcode(void) { }
void load_ucode_intel_bsp(struct early_load_data *ed);
void load_ucode_intel_ap(void);
void reload_ucode_intel(void);
+bool intel_primary_aware(void);
+const struct cpumask *intel_get_primary_cpus(void);
struct microcode_ops *init_intel_microcode(void);
#else /* CONFIG_CPU_SUP_INTEL */
static inline void load_ucode_intel_bsp(struct early_load_data *ed) { }
static inline void load_ucode_intel_ap(void) { }
static inline void reload_ucode_intel(void) { }
+static inline bool intel_primary_aware(void) { return false; }
+static inline const struct cpumask *intel_get_primary_cpus(void) { return cpu_none_mask; }
static inline struct microcode_ops *init_intel_microcode(void) { return NULL; }
#endif /* !CONFIG_CPU_SUP_INTEL */
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH RFC v1 8/8] x86/microcode/intel: Enable uniform loading
2026-09-12 0:08 [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Chang S. Bae
` (6 preceding siblings ...)
2026-09-12 0:08 ` [PATCH RFC v1 7/8] x86/microcode/intel: Support uniform scope for early loading Chang S. Bae
@ 2026-09-12 0:08 ` Chang S. Bae
2026-09-16 0:45 ` [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Borislav Petkov
8 siblings, 0 replies; 10+ messages in thread
From: Chang S. Bae @ 2026-09-12 0:08 UTC (permalink / raw)
To: linux-kernel; +Cc: x86, tglx, mingo, bp, dave.hansen, kevin.shu, chang.seok.bae
Uniform scopes are now available for both early- and late-loading paths.
This feature simplifies the loading process by reducing the number of
update triggers.
Ignoring the CPU's uniform scope results in more triggers than necessary.
While this behavior is acceptable, it can increase update latency,
particularly for the late-loading path. Enable the feature.
Certain invalid states, such as incomplete firmware configuration or an
unknown loading scope, can leave the update mechanism unreliable. Detect
those conditions early and disable the loader before any update occurs.
Module, tile, and die scopes are also possible, but currently reserved
for future implementations. Treat them as equivalent to the default
per-core scope.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
arch/x86/include/asm/microcode.h | 2 +
arch/x86/include/asm/msr-index.h | 10 ++++
arch/x86/kernel/cpu/microcode/core.c | 5 ++
arch/x86/kernel/cpu/microcode/intel.c | 69 +++++++++++++++++++++++++++
4 files changed, 86 insertions(+)
diff --git a/arch/x86/include/asm/microcode.h b/arch/x86/include/asm/microcode.h
index 645e65ac1586..710b79aa03ec 100644
--- a/arch/x86/include/asm/microcode.h
+++ b/arch/x86/include/asm/microcode.h
@@ -21,11 +21,13 @@ void load_ucode_bsp(void);
void load_ucode_ap(void);
void microcode_bsp_resume(void);
bool __init microcode_loader_disabled(void);
+void __init microcode_disable_loader(void);
#else
static inline void load_ucode_bsp(void) { }
static inline void load_ucode_ap(void) { }
static inline void microcode_bsp_resume(void) { }
static inline bool __init microcode_loader_disabled(void) { return false; }
+static inline bool __init microcode_disable_loader(void) { }
#endif
extern unsigned long initrd_start_early;
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 3a8e51a0c9e8..6a412371e02f 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -975,7 +975,17 @@
#define MSR_IA32_UCODE_WRITE 0x00000079
#define MSR_IA32_MCU_ENUMERATION 0x0000007b
+#define MCU_UNIFORM_AVAIL BIT(0)
+#define MCU_UNIFORM_CONFIG_REQD BIT(1)
+#define MCU_UNIFORM_CONFIG_COMPLETE BIT(2)
#define MCU_STAGING BIT(4)
+#define MCU_UNIFORM_SCOPE GENMASK(15, 8)
+#define MCU_UNIFORM_SCOPE_CORE 0x02
+#define MCU_UNIFORM_SCOPE_MODULE 0x03
+#define MCU_UNIFORM_SCOPE_TILE 0x04
+#define MCU_UNIFORM_SCOPE_DIE 0x05
+#define MCU_UNIFORM_SCOPE_PACKAGE 0x80
+#define MCU_UNIFORM_SCOPE_PLATFORM 0xc0
#define MSR_IA32_UCODE_REV 0x0000008b
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index ed547f2b4601..887ffdc814de 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -111,6 +111,11 @@ static bool amd_check_current_patch_level(void)
return false;
}
+void __init microcode_disable_loader(void)
+{
+ dis_ucode_ldr = true;
+}
+
bool __init microcode_loader_disabled(void)
{
if (dis_ucode_ldr)
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index 6178bddb7f60..e6c2b693f30b 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -23,6 +23,7 @@
#include <linux/uio.h>
#include <linux/io.h>
#include <linux/mm.h>
+#include <linux/bitfield.h>
#include <asm/cpu_device_id.h>
#include <asm/cpuid/api.h>
@@ -69,6 +70,8 @@ static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin";
#define MBOX_XACTION_TIMEOUT_MS (10 * MSEC_PER_SEC)
+#define CPUID_EDX_ARCH_CAP BIT(29)
+
/* Current microcode patch used in early patching on the APs. */
static struct microcode_intel *ucode_patch_va __read_mostly;
static struct microcode_intel *ucode_patch_late __read_mostly;
@@ -737,11 +740,21 @@ static int __init save_builtin_microcode(void)
}
early_initcall(save_builtin_microcode);
+static __init void setup_uniform(void);
+
/* Load microcode on BSP from initrd or builtin blobs */
void __init load_ucode_intel_bsp(struct early_load_data *ed)
{
struct ucode_cpu_info uci;
+ /*
+ * The loader could be disabled during the uniform setup if any firmware
+ * misconfiguration is found.
+ */
+ setup_uniform();
+ if (microcode_loader_disabled())
+ return;
+
uci.mc = get_microcode_blob(&uci, false);
ed->old_rev = uci.cpu_sig.rev;
@@ -989,6 +1002,62 @@ static __init bool staging_available(void)
return !!(val & MCU_STAGING);
}
+static __init void setup_uniform(void)
+{
+ u64 val;
+
+ if (native_cpuid_eax(0) < 7)
+ return;
+
+ if (!(native_cpuid_edx(7) & CPUID_EDX_ARCH_CAP))
+ return;
+
+ if (!(native_rdmsrq(MSR_IA32_ARCH_CAPABILITIES) & ARCH_CAP_MCU_ENUM))
+ return;
+
+ val = native_rdmsrq(MSR_IA32_MCU_ENUMERATION);
+ if (!(val & MCU_UNIFORM_AVAIL))
+ return;
+
+ /*
+ * Ensure that the firmware did all the necessary steps. Any improper
+ * configuration makes the update mechanism unusable.
+ */
+ if (val & MCU_UNIFORM_CONFIG_REQD && !(val & MCU_UNIFORM_CONFIG_COMPLETE)) {
+ microcode_disable_loader();
+ pr_err("loading disabled: incomplete firmware configuration.\n");
+ return;
+ }
+
+ /*
+ * Configure the uniform scope accordingly. To make it simple, treat all
+ * scopes narrower than the package scope as per-core scope.
+ */
+ switch (FIELD_GET(MCU_UNIFORM_SCOPE, val)) {
+ case MCU_UNIFORM_SCOPE_MODULE:
+ case MCU_UNIFORM_SCOPE_TILE:
+ case MCU_UNIFORM_SCOPE_DIE:
+ pr_info("Uniform scope is narrower than package, using core scope.\n");
+ fallthrough;
+ case MCU_UNIFORM_SCOPE_CORE:
+ microcode_intel_ops.uniform_scope = UNIFORM_CORE;
+ break;
+ case MCU_UNIFORM_SCOPE_PACKAGE:
+ microcode_intel_ops.uniform_scope = UNIFORM_PKG;
+ break;
+ case MCU_UNIFORM_SCOPE_PLATFORM:
+ microcode_intel_ops.uniform_scope = UNIFORM_SYS;
+ break;
+ default:
+ microcode_disable_loader();
+ pr_err("loading disabled: unknown uniform scope.\n");
+ return;
+ }
+
+ microcode_intel_ops.use_uniform = true;
+ pr_info("Enabled uniform feature.\n");
+}
+
bool __init intel_primary_aware(void)
{
if (!microcode_intel_ops.use_uniform)
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature
2026-09-12 0:08 [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Chang S. Bae
` (7 preceding siblings ...)
2026-09-12 0:08 ` [PATCH RFC v1 8/8] x86/microcode/intel: Enable uniform loading Chang S. Bae
@ 2026-09-16 0:45 ` Borislav Petkov
8 siblings, 0 replies; 10+ messages in thread
From: Borislav Petkov @ 2026-09-16 0:45 UTC (permalink / raw)
To: Chang S. Bae; +Cc: linux-kernel, x86, tglx, mingo, dave.hansen, kevin.shu
On Sat, Sep 12, 2026 at 12:08:06AM +0000, Chang S. Bae wrote:
> Hi all,
>
> This series enables another Intel microcode loading feature.
>
> While this initial posting is marked with RFC, it is also expected to
> provide patches to the people who are interested in testing/using this
> feature. Tested-by is anticipated from that end.
>
> So, x86 maintainers, feel free to ignore this round. Having said that,
> feedback will be appreciated and always welcomed.
>
> == Introduction ==
>
> Traditionally, a single trigger updates a core-scoped microcode, which
> thus requires executing WRMSR0x79 on every core. This also means that
> there is a possibility to load different microcode patches between cores.
Are you talking about heterogeneous cores?
> Despite this fact, the kernel currently enforces loading the same image
> across the CPUs.
>
> This uniform microcode conceptually eliminates a chance of running a
> different microcode within a scope of CPUs. From the loader perspective,
> the uniform feature extends the loading scope to a larger number of cores
> than one core.
What does that even mean?
You want to avoid loading different revisions on different cores, you want to
support heterogeneous cores where you must load different patches, something
else?
> A few points worth calling out about the feature:
>
> * The CPU enumerates the update scope such as package-wide or system-
> wide, depending on the implementation. The scope is advertised
> via MSR and isn't programmable.
>
> * The scope reduces the number of triggers, whereas staging primarily
> reduces the amount of work under the WRMSR window. Unlike staging,
> uniform loading is applicable to both early- and late-loading paths.
>
> * For early loading, only the parallel CPU bringup is relevant. In the
> legacy serial bringup, once the first CPU in a scope completes the
> update, subsequent CPUs will observe the updated revision so skip
> WRMSR0x79.
>
> * Staging introduced a new loading process. But uniform loading extends
> the semantics of the existing flow. Software that assumes the legacy
> scope remains supported. The next section discusses this
> compatibility aspect in more detail.
I am more confused than I was before. I have no idea what uniform loading is.
> == Backward Compatibility ==
>
> Older kernels assume a per-core scope, being ignorant of the uniform
> loading scope. So, they trigger loading via WRMSR0x79 on every core. And
> the spec [1] has the following statement, in Section 2.4 "Uniform
> Microcode Update":
>
> NOTE [*]
> ... It is always allowed to load the update on more logical processors
> than necessary, which may result in unnecessary additional latency.
>
> So this means legacy kernels remain functional on uniform systems. To
> provide more context, folks involved in the implementation agreed to
> share additional implementation details with the community. Their
> write-up is attached at the end of this cover letter.
This sounds like you can load microcode on one logical CPU and that covers the
whole socket? Or L3 slice?
> == Appendix: Microcode Implementation Note ==
>
> The uniform update protocol is an optimization for boot/runtime microcode
> update. It is backward compatible with existing microarchitecture of
> core/thread scope update and any OS MCU drivers that rely on legacy
> method of update.
>
> With uniform update, if multiple logical processors attempt to load an
> update simultaneously, there is a race to an internal semaphore within
> the microcode. The winner of the race assumes control of the update
> process and sends an internal interrupt to all other threads (if only one
> thread initiates the update, it is the winner by default).
>
> All other logical processors receive the internal interrupt at an
> architectural instruction boundary and proceed to load the update under
> the coordination of the winner. This ensures that the responding threads
> load the update in a controlled manner while at a well-defined
> architectural instruction boundary. If a higher priority interrupt or a
> fault happens, all logical processors will see it either before the
> microcode patch has been applied or after. In either case, all logical
> processors will see the same microcode revision and nothing intermediate.
I think you should lead with this, hm, weird requirement.
So let's first, please, give a second try at explaining what this uniform
thing is.
Thx.
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-09-16 0:45 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-12 0:08 [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 1/8] cpu/hotplug: Allow architecture-specific primary CPU bringup Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 2/8] x86/hotplug: Implement SMT-primary selection for parallel bringup Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 3/8] x86/cpu/topology: Introduce primary core mask Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 4/8] x86/microcode: Extend struct microcode_ops for uniform loading Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 5/8] x86/microcode: Clarify online enforcement with " Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 6/8] x86/microcode: Support uniform scope for late loading Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 7/8] x86/microcode/intel: Support uniform scope for early loading Chang S. Bae
2026-09-12 0:08 ` [PATCH RFC v1 8/8] x86/microcode/intel: Enable uniform loading Chang S. Bae
2026-09-16 0:45 ` [PATCH RFC v1 0/8] x86/microcode: Enable uniform feature Borislav Petkov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®