[PATCH RFC 4/7] x86/microcode/intel: Prepare for microcode staging

From: Chang S. Bae
Date: Tue Oct 01 2024 - 12:20:14 EST


When microcode staging is initiated, operations are carried out through
an MMIO interface. Each interface is independently discoverable per CPU
via the IA32_MCU_STAGING_MBOX_ADDR MSR, which points to a set of
dword-sized registers.

Software must first ensure the microcode image is dword-aligned, then
proceed to stage the update for each exposed MMIO space as specified.

Follow these two steps to arrange staging process. Identify each unique
MMIO interface by iterating over the CPUs and reading the MSR for each
one. While this process can be tedious, it remains simple enough and
acceptable in the slow path.

Suggested-by: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>
Signed-off-by: Chang S. Bae <chang.seok.bae@xxxxxxxxx>
---
Note:

1. Initially, a per-package and parallel staging invocation approach was
considered, but it seemed overly complex. Dave helped to identify a
simpler way.

2. Ideally, the staging_work() function would be as simple as a single
WRMSR execution. If this were the case, the staging flow could be
completed with this patch. From this perspective, the software handling
for interacting with the staging firmware has been separated from this
vendor code and moved into a new file dedicated to staging logic.
---
arch/x86/kernel/cpu/microcode/intel.c | 50 ++++++++++++++++++++++++
arch/x86/kernel/cpu/microcode/internal.h | 5 +++
2 files changed, 55 insertions(+)

diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index f3d534807d91..03c4b0e7e97c 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -299,6 +299,55 @@ static __init struct microcode_intel *scan_microcode(void *data, size_t size,
return size ? NULL : patch;
}

+static inline u64 staging_addr(u32 cpu)
+{
+ u32 lo, hi;
+
+ rdmsr_on_cpu(cpu, MSR_IA32_MCU_STAGING_MBOX_ADDR, &lo, &hi);
+ return lo | ((u64)hi << 32);
+}
+
+static bool need_staging(u64 *mmio_addrs, u64 pa)
+{
+ unsigned int i;
+
+ for (i = 0; mmio_addrs[i] != 0; i++) {
+ if (mmio_addrs[i] == pa)
+ return false;
+ }
+ mmio_addrs[i] = pa;
+ return true;
+}
+
+static void staging_microcode(void)
+{
+ u64 *mmio_addrs, mmio_pa;
+ unsigned int totalsize;
+ int cpu;
+
+ totalsize = get_totalsize(&ucode_patch_late->hdr);
+ if (!IS_ALIGNED(totalsize, sizeof(u32)))
+ return;
+
+ mmio_addrs = kcalloc(nr_cpu_ids, sizeof(*mmio_addrs), GFP_KERNEL);
+ if (WARN_ON_ONCE(!mmio_addrs))
+ return;
+
+ for_each_cpu(cpu, cpu_online_mask) {
+ mmio_pa = staging_addr(cpu);
+
+ if (need_staging(mmio_addrs, mmio_pa) &&
+ !staging_work(mmio_pa, ucode_patch_late, totalsize)) {
+ pr_err("Error: staging failed.\n");
+ goto out;
+ }
+ }
+
+ pr_info("Staging succeeded.\n");
+out:
+ kfree(mmio_addrs);
+}
+
static enum ucode_state __apply_microcode(struct ucode_cpu_info *uci,
struct microcode_intel *mc,
u32 *cur_rev)
@@ -627,6 +676,7 @@ static struct microcode_ops microcode_intel_ops = {
.collect_cpu_info = collect_cpu_info,
.apply_microcode = apply_microcode_late,
.finalize_late_load = finalize_late_load,
+ .staging_microcode = staging_microcode,
.use_nmi = IS_ENABLED(CONFIG_X86_64),
};

diff --git a/arch/x86/kernel/cpu/microcode/internal.h b/arch/x86/kernel/cpu/microcode/internal.h
index cb58e83e4934..06c3c8db4ceb 100644
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -120,6 +120,11 @@ void load_ucode_intel_bsp(struct early_load_data *ed);
void load_ucode_intel_ap(void);
void reload_ucode_intel(void);
struct microcode_ops *init_intel_microcode(void);
+static inline bool staging_work(u64 mmio_pa, void *ucode_ptr, unsigned int totalsize)
+{
+ pr_debug_once("Need to implement the Staging code.\n");
+ return false;
+}
#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) { }
--
2.43.0