forked from Minki/linux
- Get rid of a single ksize() usage
- By popular demand, print the previous microcode revision an update was done over - Remove more code related to the now gone MICROCODE_OLD_INTERFACE - Document the problems stemming from microcode late loading -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEzv7L6UO9uDPlPSfHEsHwGGHeVUoFAmM8QL8ACgkQEsHwGGHe VUoDxw/9FA3rOAZD7N0PI/vspMUxEDQVYV60tfuuynao72HZv+tfJbRTXe42p3ZO B+kRPFud4lAOE1ykDHJ2A2OZzvthGfYlUnMyvk1IvK/gOwkkiSH4c6sVSrOYWtl7 uoIN/3J83BMZoWNOKqrg1OOzotzkTyeucPXdWF+sRkfVzBIgbDqtplbFFCP4abPK WxatY2hkTfBCiN92OSOLaMGg0POpmycy+6roR2Qr5rWrC7nfREVNbKdOyEykZsfV U2gPm0A953sZ3Ye6waFib+qjJdyR7zBQRCJVEGOB6g8BlNwqGv/TY7NIUWSVFT9Y qcAnD3hI0g0UTYdToBUvYEpfD8zC9Wg3tZEpZSBRKh3AR2+Xt44VKQFO4L9uIt6g hWFMBLsFiYnBmKW3arNLQcdamE34GRhwUfXm0OjHTvTWb3aFO1I9+NBCaHp19KVy HD13wGSyj5V9SAVD0ztRFut4ZESejDyYBw9joB2IsjkY2IJmAAsRFgV0KXqUvQLX TX13hnhm894UfQ+4KCXnA0UeEDoXhwAbYFxR89yGeOxoGe1oaPXr9C1/r88YLq0n ekjIVZ3G97PIxmayj3cv9YrRIrrJi4PWF1Raey6go3Ma+rNBRnya5UF6Noch1lHh HeF7t84BZ5Ub6GweWYaMHQZCA+wMCZMYYuCMNzN7b54yRtQuvCc= =lWDD -----END PGP SIGNATURE----- Merge tag 'x86_microcode_for_v6.1_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull x75 microcode loader updates from Borislav Petkov: - Get rid of a single ksize() usage - By popular demand, print the previous microcode revision an update was done over - Remove more code related to the now gone MICROCODE_OLD_INTERFACE - Document the problems stemming from microcode late loading * tag 'x86_microcode_for_v6.1_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/microcode/AMD: Track patch allocation size explicitly x86/microcode: Print previous version of microcode after reload x86/microcode: Remove ->request_microcode_user() x86/microcode: Document the whole late loading problem
This commit is contained in:
commit
b5f0b11353
@ -134,6 +134,12 @@ More detailed explanation for tainting
|
|||||||
scsi/snic on something else than x86_64, scsi/ips on non
|
scsi/snic on something else than x86_64, scsi/ips on non
|
||||||
x86/x86_64/itanium, have broken firmware settings for the
|
x86/x86_64/itanium, have broken firmware settings for the
|
||||||
irqchip/irq-gic on arm64 ...).
|
irqchip/irq-gic on arm64 ...).
|
||||||
|
- x86/x86_64: Microcode late loading is dangerous and will result in
|
||||||
|
tainting the kernel. It requires that all CPUs rendezvous to make sure
|
||||||
|
the update happens when the system is as quiescent as possible. However,
|
||||||
|
a higher priority MCE/SMI/NMI can move control flow away from that
|
||||||
|
rendezvous and interrupt the update, which can be detrimental to the
|
||||||
|
machine.
|
||||||
|
|
||||||
3) ``R`` if a module was force unloaded by ``rmmod -f``, ``' '`` if all
|
3) ``R`` if a module was force unloaded by ``rmmod -f``, ``' '`` if all
|
||||||
modules were unloaded normally.
|
modules were unloaded normally.
|
||||||
|
@ -6,6 +6,7 @@ The Linux Microcode Loader
|
|||||||
|
|
||||||
:Authors: - Fenghua Yu <fenghua.yu@intel.com>
|
:Authors: - Fenghua Yu <fenghua.yu@intel.com>
|
||||||
- Borislav Petkov <bp@suse.de>
|
- Borislav Petkov <bp@suse.de>
|
||||||
|
- Ashok Raj <ashok.raj@intel.com>
|
||||||
|
|
||||||
The kernel has a x86 microcode loading facility which is supposed to
|
The kernel has a x86 microcode loading facility which is supposed to
|
||||||
provide microcode loading methods in the OS. Potential use cases are
|
provide microcode loading methods in the OS. Potential use cases are
|
||||||
@ -92,15 +93,8 @@ vendor's site.
|
|||||||
Late loading
|
Late loading
|
||||||
============
|
============
|
||||||
|
|
||||||
There are two legacy user space interfaces to load microcode, either through
|
You simply install the microcode packages your distro supplies and
|
||||||
/dev/cpu/microcode or through /sys/devices/system/cpu/microcode/reload file
|
run::
|
||||||
in sysfs.
|
|
||||||
|
|
||||||
The /dev/cpu/microcode method is deprecated because it needs a special
|
|
||||||
userspace tool for that.
|
|
||||||
|
|
||||||
The easier method is simply installing the microcode packages your distro
|
|
||||||
supplies and running::
|
|
||||||
|
|
||||||
# echo 1 > /sys/devices/system/cpu/microcode/reload
|
# echo 1 > /sys/devices/system/cpu/microcode/reload
|
||||||
|
|
||||||
@ -110,6 +104,110 @@ The loading mechanism looks for microcode blobs in
|
|||||||
/lib/firmware/{intel-ucode,amd-ucode}. The default distro installation
|
/lib/firmware/{intel-ucode,amd-ucode}. The default distro installation
|
||||||
packages already put them there.
|
packages already put them there.
|
||||||
|
|
||||||
|
Since kernel 5.19, late loading is not enabled by default.
|
||||||
|
|
||||||
|
The /dev/cpu/microcode method has been removed in 5.19.
|
||||||
|
|
||||||
|
Why is late loading dangerous?
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Synchronizing all CPUs
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
The microcode engine which receives the microcode update is shared
|
||||||
|
between the two logical threads in a SMT system. Therefore, when
|
||||||
|
the update is executed on one SMT thread of the core, the sibling
|
||||||
|
"automatically" gets the update.
|
||||||
|
|
||||||
|
Since the microcode can "simulate" MSRs too, while the microcode update
|
||||||
|
is in progress, those simulated MSRs transiently cease to exist. This
|
||||||
|
can result in unpredictable results if the SMT sibling thread happens to
|
||||||
|
be in the middle of an access to such an MSR. The usual observation is
|
||||||
|
that such MSR accesses cause #GPs to be raised to signal that former are
|
||||||
|
not present.
|
||||||
|
|
||||||
|
The disappearing MSRs are just one common issue which is being observed.
|
||||||
|
Any other instruction that's being patched and gets concurrently
|
||||||
|
executed by the other SMT sibling, can also result in similar,
|
||||||
|
unpredictable behavior.
|
||||||
|
|
||||||
|
To eliminate this case, a stop_machine()-based CPU synchronization was
|
||||||
|
introduced as a way to guarantee that all logical CPUs will not execute
|
||||||
|
any code but just wait in a spin loop, polling an atomic variable.
|
||||||
|
|
||||||
|
While this took care of device or external interrupts, IPIs including
|
||||||
|
LVT ones, such as CMCI etc, it cannot address other special interrupts
|
||||||
|
that can't be shut off. Those are Machine Check (#MC), System Management
|
||||||
|
(#SMI) and Non-Maskable interrupts (#NMI).
|
||||||
|
|
||||||
|
Machine Checks
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Machine Checks (#MC) are non-maskable. There are two kinds of MCEs.
|
||||||
|
Fatal un-recoverable MCEs and recoverable MCEs. While un-recoverable
|
||||||
|
errors are fatal, recoverable errors can also happen in kernel context
|
||||||
|
are also treated as fatal by the kernel.
|
||||||
|
|
||||||
|
On certain Intel machines, MCEs are also broadcast to all threads in a
|
||||||
|
system. If one thread is in the middle of executing WRMSR, a MCE will be
|
||||||
|
taken at the end of the flow. Either way, they will wait for the thread
|
||||||
|
performing the wrmsr(0x79) to rendezvous in the MCE handler and shutdown
|
||||||
|
eventually if any of the threads in the system fail to check in to the
|
||||||
|
MCE rendezvous.
|
||||||
|
|
||||||
|
To be paranoid and get predictable behavior, the OS can choose to set
|
||||||
|
MCG_STATUS.MCIP. Since MCEs can be at most one in a system, if an
|
||||||
|
MCE was signaled, the above condition will promote to a system reset
|
||||||
|
automatically. OS can turn off MCIP at the end of the update for that
|
||||||
|
core.
|
||||||
|
|
||||||
|
System Management Interrupt
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
SMIs are also broadcast to all CPUs in the platform. Microcode update
|
||||||
|
requests exclusive access to the core before writing to MSR 0x79. So if
|
||||||
|
it does happen such that, one thread is in WRMSR flow, and the 2nd got
|
||||||
|
an SMI, that thread will be stopped in the first instruction in the SMI
|
||||||
|
handler.
|
||||||
|
|
||||||
|
Since the secondary thread is stopped in the first instruction in SMI,
|
||||||
|
there is very little chance that it would be in the middle of executing
|
||||||
|
an instruction being patched. Plus OS has no way to stop SMIs from
|
||||||
|
happening.
|
||||||
|
|
||||||
|
Non-Maskable Interrupts
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
When thread0 of a core is doing the microcode update, if thread1 is
|
||||||
|
pulled into NMI, that can cause unpredictable behavior due to the
|
||||||
|
reasons above.
|
||||||
|
|
||||||
|
OS can choose a variety of methods to avoid running into this situation.
|
||||||
|
|
||||||
|
|
||||||
|
Is the microcode suitable for late loading?
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
Late loading is done when the system is fully operational and running
|
||||||
|
real workloads. Late loading behavior depends on what the base patch on
|
||||||
|
the CPU is before upgrading to the new patch.
|
||||||
|
|
||||||
|
This is true for Intel CPUs.
|
||||||
|
|
||||||
|
Consider, for example, a CPU has patch level 1 and the update is to
|
||||||
|
patch level 3.
|
||||||
|
|
||||||
|
Between patch1 and patch3, patch2 might have deprecated a software-visible
|
||||||
|
feature.
|
||||||
|
|
||||||
|
This is unacceptable if software is even potentially using that feature.
|
||||||
|
For instance, say MSR_X is no longer available after an update,
|
||||||
|
accessing that MSR will cause a #GP fault.
|
||||||
|
|
||||||
|
Basically there is no way to declare a new microcode update suitable
|
||||||
|
for late-loading. This is another one of the problems that caused late
|
||||||
|
loading to be not enabled by default.
|
||||||
|
|
||||||
Builtin microcode
|
Builtin microcode
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
struct ucode_patch {
|
struct ucode_patch {
|
||||||
struct list_head plist;
|
struct list_head plist;
|
||||||
void *data; /* Intel uses only this one */
|
void *data; /* Intel uses only this one */
|
||||||
|
unsigned int size;
|
||||||
u32 patch_id;
|
u32 patch_id;
|
||||||
u16 equiv_cpu;
|
u16 equiv_cpu;
|
||||||
};
|
};
|
||||||
@ -32,9 +33,6 @@ enum ucode_state {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct microcode_ops {
|
struct microcode_ops {
|
||||||
enum ucode_state (*request_microcode_user) (int cpu,
|
|
||||||
const void __user *buf, size_t size);
|
|
||||||
|
|
||||||
enum ucode_state (*request_microcode_fw) (int cpu, struct device *,
|
enum ucode_state (*request_microcode_fw) (int cpu, struct device *,
|
||||||
bool refresh_fw);
|
bool refresh_fw);
|
||||||
|
|
||||||
|
@ -788,6 +788,7 @@ static int verify_and_add_patch(u8 family, u8 *fw, unsigned int leftover,
|
|||||||
kfree(patch);
|
kfree(patch);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
patch->size = *patch_size;
|
||||||
|
|
||||||
mc_hdr = (struct microcode_header_amd *)(fw + SECTION_HDR_SIZE);
|
mc_hdr = (struct microcode_header_amd *)(fw + SECTION_HDR_SIZE);
|
||||||
proc_id = mc_hdr->processor_rev_id;
|
proc_id = mc_hdr->processor_rev_id;
|
||||||
@ -869,7 +870,7 @@ load_microcode_amd(bool save, u8 family, const u8 *data, size_t size)
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
memset(amd_ucode_patch, 0, PATCH_MAX_SIZE);
|
memset(amd_ucode_patch, 0, PATCH_MAX_SIZE);
|
||||||
memcpy(amd_ucode_patch, p->data, min_t(u32, ksize(p->data), PATCH_MAX_SIZE));
|
memcpy(amd_ucode_patch, p->data, min_t(u32, p->size, PATCH_MAX_SIZE));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -924,12 +925,6 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum ucode_state
|
|
||||||
request_microcode_user(int cpu, const void __user *buf, size_t size)
|
|
||||||
{
|
|
||||||
return UCODE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void microcode_fini_cpu_amd(int cpu)
|
static void microcode_fini_cpu_amd(int cpu)
|
||||||
{
|
{
|
||||||
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
|
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
|
||||||
@ -938,7 +933,6 @@ static void microcode_fini_cpu_amd(int cpu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct microcode_ops microcode_amd_ops = {
|
static struct microcode_ops microcode_amd_ops = {
|
||||||
.request_microcode_user = request_microcode_user,
|
|
||||||
.request_microcode_fw = request_microcode_amd,
|
.request_microcode_fw = request_microcode_amd,
|
||||||
.collect_cpu_info = collect_cpu_info_amd,
|
.collect_cpu_info = collect_cpu_info_amd,
|
||||||
.apply_microcode = apply_microcode_amd,
|
.apply_microcode = apply_microcode_amd,
|
||||||
|
@ -491,7 +491,7 @@ wait_for_siblings:
|
|||||||
*/
|
*/
|
||||||
static int microcode_reload_late(void)
|
static int microcode_reload_late(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int old = boot_cpu_data.microcode, ret;
|
||||||
|
|
||||||
pr_err("Attempting late microcode loading - it is dangerous and taints the kernel.\n");
|
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");
|
pr_err("You should switch to early loading, if possible.\n");
|
||||||
@ -503,7 +503,8 @@ static int microcode_reload_late(void)
|
|||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
microcode_check();
|
microcode_check();
|
||||||
|
|
||||||
pr_info("Reload completed, microcode revision: 0x%x\n", boot_cpu_data.microcode);
|
pr_info("Reload completed, microcode revision: 0x%x -> 0x%x\n",
|
||||||
|
old, boot_cpu_data.microcode);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -916,24 +916,7 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum ucode_state
|
|
||||||
request_microcode_user(int cpu, const void __user *buf, size_t size)
|
|
||||||
{
|
|
||||||
struct iov_iter iter;
|
|
||||||
struct iovec iov;
|
|
||||||
|
|
||||||
if (is_blacklisted(cpu))
|
|
||||||
return UCODE_NFOUND;
|
|
||||||
|
|
||||||
iov.iov_base = (void __user *)buf;
|
|
||||||
iov.iov_len = size;
|
|
||||||
iov_iter_init(&iter, WRITE, &iov, 1, size);
|
|
||||||
|
|
||||||
return generic_load_microcode(cpu, &iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct microcode_ops microcode_intel_ops = {
|
static struct microcode_ops microcode_intel_ops = {
|
||||||
.request_microcode_user = request_microcode_user,
|
|
||||||
.request_microcode_fw = request_microcode_fw,
|
.request_microcode_fw = request_microcode_fw,
|
||||||
.collect_cpu_info = collect_cpu_info,
|
.collect_cpu_info = collect_cpu_info,
|
||||||
.apply_microcode = apply_microcode_intel,
|
.apply_microcode = apply_microcode_intel,
|
||||||
|
Loading…
Reference in New Issue
Block a user