From 3c1b49578d7721b345a92fe59c08a1408ac8768d Mon Sep 17 00:00:00 2001 From: Ashok Raj Date: Thu, 19 Aug 2021 14:49:47 -0700 Subject: [PATCH] x86/microcode: Add an option to reload microcode even if revision is unchanged ANBZ: #1252 commit f55303451a2829c6d1187e409774f2e9cfa1888b intel-github This is POC to support rollback. This is a simple version, admin uses echo 2 instead of echo 1 to reload. We don't do the version checks. # echo 1 > /sys/devices/system/cpu/microcode/reload The following usage, writing 2 to reload file is helpful to reload the microcode again even if the revision is less than what is loaded. # echo 2 > /sys/devices/system/cpu/microcode/reload Signed-off-by: Ashok Raj [guanjun: add documentation for ucode] Signed-off-by: Guanjun Reviewed-by: Artie Ding --- .../admin-guide/kernel-parameters.txt | 5 +++ arch/x86/kernel/cpu/microcode/core.c | 40 ++++++++++++++++++- arch/x86/kernel/cpu/microcode/intel.c | 14 ++++--- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index bbfb3a983ee2..ff10b37877d8 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -5594,6 +5594,11 @@ ,,,,,,, See also Documentation/input/devices/joystick-parport.rst + ucode= [X86,Intel] Control microcode rollback in Intel CPUs + Format: + For example, to enable microcode rollback: + ucode=rollback + udbg-immortal [PPC] When debugging early kernel crashes that happen after console_init() and before a proper console driver takes over, this boot options might diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c index 8b7c89a7f324..57114722820d 100644 --- a/arch/x86/kernel/cpu/microcode/core.c +++ b/arch/x86/kernel/cpu/microcode/core.c @@ -43,6 +43,8 @@ static struct microcode_ops *microcode_ops; static bool dis_ucode_ldr = true; +bool ucode_rollback = false; +int enable_rollback = 0; bool initrd_gone; @@ -79,6 +81,26 @@ static u32 final_levels[] = { 0, /* T-101 terminator */ }; +static int __init ucode_setup(char *str) +{ + if (!str) + return -EINVAL; + + while (*str) { + if (!strncmp(str, "rollback", 8)) { + enable_rollback = 1; + pr_info("Microcode Rollback Enabled\n"); + } + str += strcspn(str, ","); + while (*str == ',') + str++; + } + return 0; +} + +__setup("ucode=", ucode_setup); + + /* * Check the current patch level on this CPU. * @@ -599,6 +621,7 @@ static ssize_t reload_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { + struct cpuinfo_x86 *c = &boot_cpu_data; enum ucode_state tmp_ret = UCODE_OK; int bsp = boot_cpu_data.cpu_index; unsigned long val; @@ -608,7 +631,7 @@ static ssize_t reload_store(struct device *dev, if (ret) return ret; - if (val != 1) + if (!val || val > 2) return size; get_online_cpus(); @@ -616,6 +639,20 @@ static ssize_t reload_store(struct device *dev, ret = check_online_cpus(); if (ret) goto put; + /* + * Check if the vendor is Intel to permit reloading + * microcode even if the revision is unchanged. + * This is typically used during development of microcode + * and changing rev is a pain. + */ + if ((val == 2) && ((c->x86_vendor != X86_VENDOR_INTEL) || + !enable_rollback)) + return size; + else if (val == 2) { + mutex_lock(µcode_mutex); + ucode_rollback = true; + mutex_unlock(µcode_mutex); + } tmp_ret = microcode_ops->request_microcode_fw(bsp, µcode_pdev->dev, true); if (tmp_ret != UCODE_NEW) @@ -626,6 +663,7 @@ static ssize_t reload_store(struct device *dev, mutex_unlock(µcode_mutex); put: + ucode_rollback = false; put_online_cpus(); if (ret == 0) diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c index 4b316a87bf0a..238b640f3a88 100644 --- a/arch/x86/kernel/cpu/microcode/intel.c +++ b/arch/x86/kernel/cpu/microcode/intel.c @@ -45,6 +45,7 @@ static struct microcode_intel *intel_ucode_patch; /* last level cache size per core */ static int llc_size_per_core; +extern bool ucode_rollback; static inline bool cpu_signatures_match(unsigned int s1, unsigned int p1, unsigned int s2, unsigned int p2) @@ -95,7 +96,7 @@ static int has_newer_microcode(void *mc, unsigned int csig, int cpf, int new_rev { struct microcode_header_intel *mc_hdr = mc; - if (mc_hdr->rev <= new_rev) + if (!ucode_rollback && mc_hdr->rev <= new_rev) return 0; return find_matching_signature(mc, csig, cpf); @@ -135,7 +136,7 @@ static void save_microcode_patch(struct ucode_cpu_info *uci, void *data, unsigne if (find_matching_signature(data, sig, pf)) { prev_found = true; - if (mc_hdr->rev <= mc_saved_hdr->rev) + if (!ucode_rollback && mc_hdr->rev <= mc_saved_hdr->rev) continue; p = memdup_patch(data, size); @@ -688,7 +689,7 @@ static struct microcode_intel *find_patch(struct ucode_cpu_info *uci) phdr = (struct microcode_header_intel *)iter->data; - if (phdr->rev <= uci->cpu_sig.rev) + if (!ucode_rollback && phdr->rev <= uci->cpu_sig.rev) continue; if (!find_matching_signature(phdr, @@ -773,10 +774,11 @@ static enum ucode_state apply_microcode_intel(int cpu) * already. */ rev = intel_get_microcode_revision(); - if (rev >= mc->hdr.rev) { + if (!ucode_rollback && rev >= mc->hdr.rev) { ret = UCODE_OK; goto out; - } + } else if (ucode_rollback) + ret = UCODE_OK; /* * Writeback and invalidate caches before updating microcode to avoid @@ -795,7 +797,7 @@ static enum ucode_state apply_microcode_intel(int cpu) return UCODE_ERROR; } - if (bsp && rev != prev_rev) { + if (bsp && ((rev != prev_rev) || ucode_rollback)) { pr_info("updated to revision 0x%x, date = %04x-%02x-%02x\n", rev, mc->hdr.date & 0xffff, -- Gitee