diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index bbfb3a983ee252d91a77a305058150701792f579..ff10b37877d8aece254d9ff04bd127e13e21e2a8 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 8b7c89a7f324501400afd5b478eee6dd0d2afbcc..57114722820d9d3f98a8bcb9daa48be55475ccc2 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 4b316a87bf0a551363fb6ea9ad62cc3d2578c0c8..238b640f3a889e60229309642a1cc636186ce892 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,