Skip to content

Commit cf5ab01

Browse files
ashok-rajbp3tk0v
authored andcommitted
x86/microcode/intel: Add a minimum required revision for late loading
In general users, don't have the necessary information to determine whether late loading of a new microcode version is safe and does not modify anything which the currently running kernel uses already, e.g. removal of CPUID bits or behavioural changes of MSRs. To address this issue, Intel has added a "minimum required version" field to a previously reserved field in the microcode header. Microcode updates should only be applied if the current microcode version is equal to, or greater than this minimum required version. Thomas made some suggestions on how meta-data in the microcode file could provide Linux with information to decide if the new microcode is suitable candidate for late loading. But even the "simpler" option requires a lot of metadata and corresponding kernel code to parse it, so the final suggestion was to add the 'minimum required version' field in the header. When microcode changes visible features, microcode will set the minimum required version to its own revision which prevents late loading. Old microcode blobs have the minimum revision field always set to 0, which indicates that there is no information and the kernel considers it unsafe. This is a pure OS software mechanism. The hardware/firmware ignores this header field. For early loading there is no restriction because OS visible features are enumerated after the early load and therefore a change has no effect. The check is always enabled, but by default not enforced. It can be enforced via Kconfig or kernel command line. If enforced, the kernel refuses to late load microcode with a minimum required version field which is zero or when the currently loaded microcode revision is smaller than the minimum required revision. If not enforced the load happens independent of the revision check to stay compatible with the existing behaviour, but it influences the decision whether the kernel is tainted or not. If the check signals that the late load is safe, then the kernel is not tainted. Early loading is not affected by this. [ tglx: Massaged changelog and fixed up the implementation ] Suggested-by: Thomas Gleixner <[email protected]> Signed-off-by: Ashok Raj <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 9407bda commit cf5ab01

File tree

2 files changed

+35
-5
lines changed

2 files changed

+35
-5
lines changed

arch/x86/include/asm/microcode.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ struct microcode_header_intel {
3838
unsigned int datasize;
3939
unsigned int totalsize;
4040
unsigned int metasize;
41-
unsigned int reserved[2];
41+
unsigned int min_req_ver;
42+
unsigned int reserved;
4243
};
4344

4445
struct microcode_intel {

arch/x86/kernel/cpu/microcode/intel.c

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -473,16 +473,40 @@ static enum ucode_state apply_microcode_late(int cpu)
473473
return ret;
474474
}
475475

476+
static bool ucode_validate_minrev(struct microcode_header_intel *mc_header)
477+
{
478+
int cur_rev = boot_cpu_data.microcode;
479+
480+
/*
481+
* When late-loading, ensure the header declares a minimum revision
482+
* required to perform a late-load. The previously reserved field
483+
* is 0 in older microcode blobs.
484+
*/
485+
if (!mc_header->min_req_ver) {
486+
pr_info("Unsafe microcode update: Microcode header does not specify a required min version\n");
487+
return false;
488+
}
489+
490+
/*
491+
* Check whether the current revision is either greater or equal to
492+
* to the minimum revision specified in the header.
493+
*/
494+
if (cur_rev < mc_header->min_req_ver) {
495+
pr_info("Unsafe microcode update: Current revision 0x%x too old\n", cur_rev);
496+
pr_info("Current should be at 0x%x or higher. Use early loading instead\n", mc_header->min_req_ver);
497+
return false;
498+
}
499+
return true;
500+
}
501+
476502
static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)
477503
{
478504
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
505+
bool is_safe, new_is_safe = false;
479506
int cur_rev = uci->cpu_sig.rev;
480507
unsigned int curr_mc_size = 0;
481508
u8 *new_mc = NULL, *mc = NULL;
482509

483-
if (force_minrev)
484-
return UCODE_NFOUND;
485-
486510
while (iov_iter_count(iter)) {
487511
struct microcode_header_intel mc_header;
488512
unsigned int mc_size, data_size;
@@ -525,9 +549,14 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)
525549
if (!intel_find_matching_signature(mc, &uci->cpu_sig))
526550
continue;
527551

552+
is_safe = ucode_validate_minrev(&mc_header);
553+
if (force_minrev && !is_safe)
554+
continue;
555+
528556
kvfree(new_mc);
529557
cur_rev = mc_header.rev;
530558
new_mc = mc;
559+
new_is_safe = is_safe;
531560
mc = NULL;
532561
}
533562

@@ -539,7 +568,7 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)
539568
return UCODE_NFOUND;
540569

541570
ucode_patch_late = (struct microcode_intel *)new_mc;
542-
return UCODE_NEW;
571+
return new_is_safe ? UCODE_NEW_SAFE : UCODE_NEW;
543572

544573
fail:
545574
kvfree(mc);

0 commit comments

Comments
 (0)