[09/13] x86/microcode: Add a generic mechanism to declare support for minrev
Commit Message
Intel microcode adds some meta-data to report a minimum required revision
before this new microcode can be late-loaded. There are no generic mechanism
to declare support for all vendors.
Add generic support to microcode to declare support, so the tainting and
late-loading can be permitted in those architectures that support reporting
a minrev in some form.
Late loading has added support for
- New images declaring a required minimum base version before a late-load
is performed.
- Improved NMI handling during update to avoid sibling threads taking NMI's
while primary is still not complete with the microcode update.
With these changes, late-loading can be re-enabled. Tainting only happens
on architectures that don't support minimum required version reporting.
Signed-off-by: Ashok Raj <ashok.raj@intel.com>
---
arch/x86/include/asm/microcode.h | 1 +
arch/x86/kernel/cpu/microcode/core.c | 14 +++++++++++---
arch/x86/kernel/cpu/microcode/intel.c | 6 ++++++
arch/x86/Kconfig | 7 ++++---
4 files changed, 22 insertions(+), 6 deletions(-)
Comments
On Fri, 2022-10-14 at 13:09 -0700, Ashok Raj wrote:
> @@ -606,6 +606,7 @@ static ssize_t reload_store(struct device *dev,
> enum ucode_state tmp_ret = UCODE_OK;
> int bsp = boot_cpu_data.cpu_index;
> unsigned long val;
> + int minrev;
> ssize_t ret = 0;
>
> ret = kstrtoul(buf, 0, &val);
> @@ -621,8 +622,14 @@ static ssize_t reload_store(struct device *dev,
> if (ret)
> goto put;
>
> - pr_err("Attempting late microcode loading - it is dangerous and taints the kernel.\n");
> - pr_err("You should switch to early loading, if possible.\n");
> + if (microcode_ops->check_minrev())
> + minrev = microcode_ops->check_minrev();
> +
> + if (!minrev) {
> + pr_err("Attempting late microcode loading - it is dangerous and taints the kernel.\n");
> + pr_err("You should switch to early loading, if possible.\n");
> + }
> +
Hi Ashok,
IIUC a variable in stack isn't initialized to 0 automatically. So looks if
check_minrev() callback is NULL, you will get an uninitialized 'minrev' in the
above if() statement check.
--
Thanks,
-Kai
On Tue, Oct 18, 2022 at 09:03:03PM -0700, Huang, Kai wrote:
> On Fri, 2022-10-14 at 13:09 -0700, Ashok Raj wrote:
> > @@ -606,6 +606,7 @@ static ssize_t reload_store(struct device *dev,
> > enum ucode_state tmp_ret = UCODE_OK;
> > int bsp = boot_cpu_data.cpu_index;
> > unsigned long val;
> > + int minrev;
> > ssize_t ret = 0;
> >
> > ret = kstrtoul(buf, 0, &val);
> > @@ -621,8 +622,14 @@ static ssize_t reload_store(struct device *dev,
> > if (ret)
> > goto put;
> >
> > - pr_err("Attempting late microcode loading - it is dangerous and taints the kernel.\n");
> > - pr_err("You should switch to early loading, if possible.\n");
> > + if (microcode_ops->check_minrev())
> > + minrev = microcode_ops->check_minrev();
> > +
> > + if (!minrev) {
> > + pr_err("Attempting late microcode loading - it is dangerous and taints the kernel.\n");
> > + pr_err("You should switch to early loading, if possible.\n");
> > + }
> > +
>
> Hi Ashok,
>
> IIUC a variable in stack isn't initialized to 0 automatically. So looks if
> check_minrev() callback is NULL, you will get an uninitialized 'minrev' in the
> above if() statement check.
>
Thanks for the review Kai.
Correct, that's a miss. I originally had a weak function bound for AMD.
But later i removed and forgot to initialize the local variable.
I've already queued this for next post. Just waiting for a few more review
comments to come on.
Cheers,
Ashok
@@ -59,6 +59,7 @@ enum ucode_state {
};
struct microcode_ops {
+ int (*check_minrev) (void);
enum ucode_state (*request_microcode_fw) (int cpu, struct device *,
bool late_loading);
@@ -606,6 +606,7 @@ static ssize_t reload_store(struct device *dev,
enum ucode_state tmp_ret = UCODE_OK;
int bsp = boot_cpu_data.cpu_index;
unsigned long val;
+ int minrev;
ssize_t ret = 0;
ret = kstrtoul(buf, 0, &val);
@@ -621,8 +622,14 @@ static ssize_t reload_store(struct device *dev,
if (ret)
goto put;
- pr_err("Attempting late microcode loading - it is dangerous and taints the kernel.\n");
- pr_err("You should switch to early loading, if possible.\n");
+ if (microcode_ops->check_minrev())
+ minrev = microcode_ops->check_minrev();
+
+ if (!minrev) {
+ pr_err("Attempting late microcode loading - it is dangerous and taints the kernel.\n");
+ pr_err("You should switch to early loading, if possible.\n");
+ }
+
tmp_ret = microcode_ops->request_microcode_fw(bsp, µcode_pdev->dev, true);
if (tmp_ret != UCODE_NEW)
goto put;
@@ -637,7 +644,8 @@ static ssize_t reload_store(struct device *dev,
if (ret == 0)
ret = size;
- add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
+ if (!minrev)
+ add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
return ret;
}
@@ -950,7 +950,13 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device,
return ret;
}
+static int intel_check_minrev(void)
+{
+ return 1;
+}
+
static struct microcode_ops microcode_intel_ops = {
+ .check_minrev = intel_check_minrev,
.request_microcode_fw = request_microcode_fw,
.collect_cpu_info = collect_cpu_info,
.apply_microcode = apply_microcode_intel,
@@ -1336,15 +1336,16 @@ config MICROCODE_AMD
processors will be enabled.
config MICROCODE_LATE_LOADING
- bool "Late microcode loading (DANGEROUS)"
- default n
+ bool "Late microcode loading"
+ default y
depends on MICROCODE
help
Loading microcode late, when the system is up and executing instructions
is a tricky business and should be avoided if possible. Just the sequence
of synchronizing all cores and SMT threads is one fragile dance which does
not guarantee that cores might not softlock after the loading. Therefore,
- use this at your own risk. Late loading taints the kernel too.
+ use this at your own risk. Late loading taints the kernel, if it
+ doesn't support a minimum required base version before an update.
config X86_MSR
tristate "/dev/cpu/*/msr - Model-specific register support"