linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch 00/30] x86/microcode: Cleanup and late loading enhancements
@ 2023-08-10 18:37 Thomas Gleixner
  2023-08-10 18:37 ` [patch 01/30] x86/mm: Remove unused microcode.h include Thomas Gleixner
                   ` (29 more replies)
  0 siblings, 30 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

Hi!

Late microcode loading is desired by enterprise users. Late loading is
problematic as it requires detailed knowledge about the change and an
analysis whether this change modifies something which is already in use by
the kernel. Large enterprise customers have engineering teams and access to
deep technical vendor support. The regular admin does not have such
resources, so the kernel has always tainted the kernel after late loading.

Intel recently added a new previously reserved field to the microcode
header which contains the minimal microcode revision which must be running
on the CPU to make the load safe. This field is 0 in all older microcode
revisions, which the kernel assumes to be unsafe. Minimal revision checking
can be enforced via Kconfig or kernel command line. It then refuses to load
an unsafe revision. The default loads unsafe revisions like before and
taints the kernel. If a safe revision is loaded the kernel is not tainted.

But that does not solve all other known problems with late loading:

    - Late loading on current Intel CPUs is unsafe vs. NMI when
      hyperthreading is enabled. If a NMI hits the secondary sibling while
      the primary loads the microcode, the machine can crash.

    - Soft offline SMT siblings which are playing dead with MWAIT can cause
      damage too when the microcode update modifies MWAIT. That's a
      realistic scenario in the context of 'nosmt' mitigations. :(

Neither the core code nor the Intel specific code handles any of this at all.

While trying to implement this, I stumbled over disfunctional, horribly
complex and redundant code, which I decided to clean up first so the new
functionality can be added on a clean slate.

So the series has several sections:

   1) Cleanup core code, header files and Kconfig

   2) Cleanup of the Intel specific code

   3) Implementation of proper core control logic to handle the NMI safe
      requirements

   4) Support for minimal revision check in the core and the Intel specific
      parts.

Thanks to Borislav for discussing this with me and helping out with
testing.  Thanks also to Ashok who contributed a few patches and helped
with testing on the Intel side especially with the new minimal revision
mechanism.

The series applies on:

   git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/microcode

and is also available from git:

   git://git.kernel.org/pub/scm/linux/kernel/git/tglx/devel.git ucode-v1

Thanks,

	tglx
---
 arch/x86/include/asm/microcode_amd.h              |   56 -
 arch/x86/include/asm/microcode_intel.h            |   88 --
 b/Documentation/admin-guide/kernel-parameters.txt |    5 
 b/arch/x86/Kconfig                                |   63 -
 b/arch/x86/include/asm/apic.h                     |    5 
 b/arch/x86/include/asm/microcode.h                |  162 +---
 b/arch/x86/kernel/apic/apic_flat_64.c             |    2 
 b/arch/x86/kernel/apic/ipi.c                      |    9 
 b/arch/x86/kernel/apic/x2apic_cluster.c           |    1 
 b/arch/x86/kernel/apic/x2apic_phys.c              |    1 
 b/arch/x86/kernel/cpu/common.c                    |    1 
 b/arch/x86/kernel/cpu/intel.c                     |  176 ----
 b/arch/x86/kernel/cpu/microcode/Makefile          |    4 
 b/arch/x86/kernel/cpu/microcode/amd.c             |   25 
 b/arch/x86/kernel/cpu/microcode/core.c            |  518 +++++++++++---
 b/arch/x86/kernel/cpu/microcode/intel.c           |  807 ++++++++++------------
 b/arch/x86/kernel/cpu/microcode/internal.h        |  190 +++++
 b/arch/x86/kernel/nmi.c                           |    9 
 b/arch/x86/mm/init.c                              |    1 
 b/drivers/platform/x86/intel/ifs/load.c           |    4 
 20 files changed, 1109 insertions(+), 1018 deletions(-)



^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 01/30] x86/mm: Remove unused microcode.h include
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 02/30] x86/microcode: Hide the config knob Thomas Gleixner
                   ` (28 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

No usage for anything in that header.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/mm/init.c | 1 -
 1 file changed, 1 deletion(-)
---
diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
index 8192452d1d2d..f1697a522845 100644
--- a/arch/x86/mm/init.c
+++ b/arch/x86/mm/init.c
@@ -20,7 +20,6 @@
 #include <asm/tlb.h>
 #include <asm/proto.h>
 #include <asm/dma.h>		/* for MAX_DMA_PFN */
-#include <asm/microcode.h>
 #include <asm/kaslr.h>
 #include <asm/hypervisor.h>
 #include <asm/cpufeature.h>


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [patch 02/30] x86/microcode: Hide the config knob
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
  2023-08-10 18:37 ` [patch 01/30] x86/mm: Remove unused microcode.h include Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 20:41   ` Ashok Raj
  2023-08-10 18:37 ` [patch 03/30] x86/microcode/intel: Move microcode functions out of cpu/intel.c Thomas Gleixner
                   ` (27 subsequent siblings)
  29 siblings, 1 reply; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

In reality CONFIG_MICROCODE is enabled in any reasonable configuration when
Intel or AMD support is enabled. Accomodate to reality.

Requested-by: Borislav Petkov <bp@alien8.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 arch/x86/Kconfig                       |   38 ---------------------------------
 arch/x86/include/asm/microcode.h       |    6 ++---
 arch/x86/include/asm/microcode_amd.h   |    2 -
 arch/x86/include/asm/microcode_intel.h |    2 -
 arch/x86/kernel/cpu/microcode/Makefile |    4 +--
 5 files changed, 8 insertions(+), 44 deletions(-)

--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1308,44 +1308,8 @@ config X86_REBOOTFIXUPS
 	  Say N otherwise.
 
 config MICROCODE
-	bool "CPU microcode loading support"
-	default y
+	def_bool y
 	depends on CPU_SUP_AMD || CPU_SUP_INTEL
-	help
-	  If you say Y here, you will be able to update the microcode on
-	  Intel and AMD processors. The Intel support is for the IA32 family,
-	  e.g. Pentium Pro, Pentium II, Pentium III, Pentium 4, Xeon etc. The
-	  AMD support is for families 0x10 and later. You will obviously need
-	  the actual microcode binary data itself which is not shipped with
-	  the Linux kernel.
-
-	  The preferred method to load microcode from a detached initrd is described
-	  in Documentation/arch/x86/microcode.rst. For that you need to enable
-	  CONFIG_BLK_DEV_INITRD in order for the loader to be able to scan the
-	  initrd for microcode blobs.
-
-	  In addition, you can build the microcode into the kernel. For that you
-	  need to add the vendor-supplied microcode to the CONFIG_EXTRA_FIRMWARE
-	  config option.
-
-config MICROCODE_INTEL
-	bool "Intel microcode loading support"
-	depends on CPU_SUP_INTEL && MICROCODE
-	default MICROCODE
-	help
-	  This options enables microcode patch loading support for Intel
-	  processors.
-
-	  For the current Intel microcode data package go to
-	  <https://downloadcenter.intel.com> and search for
-	  'Linux Processor Microcode Data File'.
-
-config MICROCODE_AMD
-	bool "AMD microcode loading support"
-	depends on CPU_SUP_AMD && MICROCODE
-	help
-	  If you select this option, microcode patch loading support for AMD
-	  processors will be enabled.
 
 config MICROCODE_LATE_LOADING
 	bool "Late microcode loading (DANGEROUS)"
--- a/arch/x86/include/asm/microcode.h
+++ b/arch/x86/include/asm/microcode.h
@@ -54,16 +54,16 @@ struct ucode_cpu_info {
 extern struct ucode_cpu_info ucode_cpu_info[];
 struct cpio_data find_microcode_in_initrd(const char *path, bool use_pa);
 
-#ifdef CONFIG_MICROCODE_INTEL
+#ifdef CONFIG_CPU_SUP_INTEL
 extern struct microcode_ops * __init init_intel_microcode(void);
 #else
 static inline struct microcode_ops * __init init_intel_microcode(void)
 {
 	return NULL;
 }
-#endif /* CONFIG_MICROCODE_INTEL */
+#endif /* CONFIG_CPU_SUP_INTEL */
 
-#ifdef CONFIG_MICROCODE_AMD
+#ifdef CONFIG_CPU_SUP_AMD
 extern struct microcode_ops * __init init_amd_microcode(void);
 extern void __exit exit_amd_microcode(void);
 #else
--- a/arch/x86/include/asm/microcode_amd.h
+++ b/arch/x86/include/asm/microcode_amd.h
@@ -43,7 +43,7 @@ struct microcode_amd {
 
 #define PATCH_MAX_SIZE (3 * PAGE_SIZE)
 
-#ifdef CONFIG_MICROCODE_AMD
+#ifdef CONFIG_CPU_SUP_AMD
 extern void load_ucode_amd_early(unsigned int cpuid_1_eax);
 extern int __init save_microcode_in_initrd_amd(unsigned int family);
 void reload_ucode_amd(unsigned int cpu);
--- a/arch/x86/include/asm/microcode_intel.h
+++ b/arch/x86/include/asm/microcode_intel.h
@@ -71,7 +71,7 @@ static inline u32 intel_get_microcode_re
 	return rev;
 }
 
-#ifdef CONFIG_MICROCODE_INTEL
+#ifdef CONFIG_CPU_SUP_INTEL
 extern void __init load_ucode_intel_bsp(void);
 extern void load_ucode_intel_ap(void);
 extern void show_ucode_info_early(void);
--- a/arch/x86/kernel/cpu/microcode/Makefile
+++ b/arch/x86/kernel/cpu/microcode/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
 microcode-y				:= core.o
 obj-$(CONFIG_MICROCODE)			+= microcode.o
-microcode-$(CONFIG_MICROCODE_INTEL)	+= intel.o
-microcode-$(CONFIG_MICROCODE_AMD)	+= amd.o
+microcode-$(CONFIG_CPU_SUP_INTEL)	+= intel.o
+microcode-$(CONFIG_CPU_SUP_AMD)		+= amd.o


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 03/30] x86/microcode/intel: Move microcode functions out of cpu/intel.c
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
  2023-08-10 18:37 ` [patch 01/30] x86/mm: Remove unused microcode.h include Thomas Gleixner
  2023-08-10 18:37 ` [patch 02/30] x86/microcode: Hide the config knob Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 04/30] x86/microcode: Include vendor headers into microcode.h Thomas Gleixner
                   ` (26 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 arch/x86/kernel/cpu/intel.c           |  174 ----------------------------------
 arch/x86/kernel/cpu/microcode/intel.c |  174 ++++++++++++++++++++++++++++++++++
 2 files changed, 174 insertions(+), 174 deletions(-)

--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -184,180 +184,6 @@ static bool bad_spectre_microcode(struct
 	return false;
 }
 
-int intel_cpu_collect_info(struct ucode_cpu_info *uci)
-{
-	unsigned int val[2];
-	unsigned int family, model;
-	struct cpu_signature csig = { 0 };
-	unsigned int eax, ebx, ecx, edx;
-
-	memset(uci, 0, sizeof(*uci));
-
-	eax = 0x00000001;
-	ecx = 0;
-	native_cpuid(&eax, &ebx, &ecx, &edx);
-	csig.sig = eax;
-
-	family = x86_family(eax);
-	model  = x86_model(eax);
-
-	if (model >= 5 || family > 6) {
-		/* get processor flags from MSR 0x17 */
-		native_rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]);
-		csig.pf = 1 << ((val[1] >> 18) & 7);
-	}
-
-	csig.rev = intel_get_microcode_revision();
-
-	uci->cpu_sig = csig;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(intel_cpu_collect_info);
-
-/*
- * Returns 1 if update has been found, 0 otherwise.
- */
-int intel_find_matching_signature(void *mc, unsigned int csig, int cpf)
-{
-	struct microcode_header_intel *mc_hdr = mc;
-	struct extended_sigtable *ext_hdr;
-	struct extended_signature *ext_sig;
-	int i;
-
-	if (intel_cpu_signatures_match(csig, cpf, mc_hdr->sig, mc_hdr->pf))
-		return 1;
-
-	/* Look for ext. headers: */
-	if (get_totalsize(mc_hdr) <= get_datasize(mc_hdr) + MC_HEADER_SIZE)
-		return 0;
-
-	ext_hdr = mc + get_datasize(mc_hdr) + MC_HEADER_SIZE;
-	ext_sig = (void *)ext_hdr + EXT_HEADER_SIZE;
-
-	for (i = 0; i < ext_hdr->count; i++) {
-		if (intel_cpu_signatures_match(csig, cpf, ext_sig->sig, ext_sig->pf))
-			return 1;
-		ext_sig++;
-	}
-	return 0;
-}
-EXPORT_SYMBOL_GPL(intel_find_matching_signature);
-
-/**
- * intel_microcode_sanity_check() - Sanity check microcode file.
- * @mc: Pointer to the microcode file contents.
- * @print_err: Display failure reason if true, silent if false.
- * @hdr_type: Type of file, i.e. normal microcode file or In Field Scan file.
- *            Validate if the microcode header type matches with the type
- *            specified here.
- *
- * Validate certain header fields and verify if computed checksum matches
- * with the one specified in the header.
- *
- * Return: 0 if the file passes all the checks, -EINVAL if any of the checks
- * fail.
- */
-int intel_microcode_sanity_check(void *mc, bool print_err, int hdr_type)
-{
-	unsigned long total_size, data_size, ext_table_size;
-	struct microcode_header_intel *mc_header = mc;
-	struct extended_sigtable *ext_header = NULL;
-	u32 sum, orig_sum, ext_sigcount = 0, i;
-	struct extended_signature *ext_sig;
-
-	total_size = get_totalsize(mc_header);
-	data_size = get_datasize(mc_header);
-
-	if (data_size + MC_HEADER_SIZE > total_size) {
-		if (print_err)
-			pr_err("Error: bad microcode data file size.\n");
-		return -EINVAL;
-	}
-
-	if (mc_header->ldrver != 1 || mc_header->hdrver != hdr_type) {
-		if (print_err)
-			pr_err("Error: invalid/unknown microcode update format. Header type %d\n",
-			       mc_header->hdrver);
-		return -EINVAL;
-	}
-
-	ext_table_size = total_size - (MC_HEADER_SIZE + data_size);
-	if (ext_table_size) {
-		u32 ext_table_sum = 0;
-		u32 *ext_tablep;
-
-		if (ext_table_size < EXT_HEADER_SIZE ||
-		    ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) {
-			if (print_err)
-				pr_err("Error: truncated extended signature table.\n");
-			return -EINVAL;
-		}
-
-		ext_header = mc + MC_HEADER_SIZE + data_size;
-		if (ext_table_size != exttable_size(ext_header)) {
-			if (print_err)
-				pr_err("Error: extended signature table size mismatch.\n");
-			return -EFAULT;
-		}
-
-		ext_sigcount = ext_header->count;
-
-		/*
-		 * Check extended table checksum: the sum of all dwords that
-		 * comprise a valid table must be 0.
-		 */
-		ext_tablep = (u32 *)ext_header;
-
-		i = ext_table_size / sizeof(u32);
-		while (i--)
-			ext_table_sum += ext_tablep[i];
-
-		if (ext_table_sum) {
-			if (print_err)
-				pr_warn("Bad extended signature table checksum, aborting.\n");
-			return -EINVAL;
-		}
-	}
-
-	/*
-	 * Calculate the checksum of update data and header. The checksum of
-	 * valid update data and header including the extended signature table
-	 * must be 0.
-	 */
-	orig_sum = 0;
-	i = (MC_HEADER_SIZE + data_size) / sizeof(u32);
-	while (i--)
-		orig_sum += ((u32 *)mc)[i];
-
-	if (orig_sum) {
-		if (print_err)
-			pr_err("Bad microcode data checksum, aborting.\n");
-		return -EINVAL;
-	}
-
-	if (!ext_table_size)
-		return 0;
-
-	/*
-	 * Check extended signature checksum: 0 => valid.
-	 */
-	for (i = 0; i < ext_sigcount; i++) {
-		ext_sig = (void *)ext_header + EXT_HEADER_SIZE +
-			  EXT_SIGNATURE_SIZE * i;
-
-		sum = (mc_header->sig + mc_header->pf + mc_header->cksum) -
-		      (ext_sig->sig + ext_sig->pf + ext_sig->cksum);
-		if (sum) {
-			if (print_err)
-				pr_err("Bad extended signature checksum, aborting.\n");
-			return -EINVAL;
-		}
-	}
-	return 0;
-}
-EXPORT_SYMBOL_GPL(intel_microcode_sanity_check);
-
 static void early_init_intel(struct cpuinfo_x86 *c)
 {
 	u64 misc_enable;
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -45,6 +45,180 @@ static struct microcode_intel *intel_uco
 /* last level cache size per core */
 static int llc_size_per_core;
 
+int intel_cpu_collect_info(struct ucode_cpu_info *uci)
+{
+	unsigned int val[2];
+	unsigned int family, model;
+	struct cpu_signature csig = { 0 };
+	unsigned int eax, ebx, ecx, edx;
+
+	memset(uci, 0, sizeof(*uci));
+
+	eax = 0x00000001;
+	ecx = 0;
+	native_cpuid(&eax, &ebx, &ecx, &edx);
+	csig.sig = eax;
+
+	family = x86_family(eax);
+	model  = x86_model(eax);
+
+	if (model >= 5 || family > 6) {
+		/* get processor flags from MSR 0x17 */
+		native_rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]);
+		csig.pf = 1 << ((val[1] >> 18) & 7);
+	}
+
+	csig.rev = intel_get_microcode_revision();
+
+	uci->cpu_sig = csig;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(intel_cpu_collect_info);
+
+/*
+ * Returns 1 if update has been found, 0 otherwise.
+ */
+int intel_find_matching_signature(void *mc, unsigned int csig, int cpf)
+{
+	struct microcode_header_intel *mc_hdr = mc;
+	struct extended_sigtable *ext_hdr;
+	struct extended_signature *ext_sig;
+	int i;
+
+	if (intel_cpu_signatures_match(csig, cpf, mc_hdr->sig, mc_hdr->pf))
+		return 1;
+
+	/* Look for ext. headers: */
+	if (get_totalsize(mc_hdr) <= get_datasize(mc_hdr) + MC_HEADER_SIZE)
+		return 0;
+
+	ext_hdr = mc + get_datasize(mc_hdr) + MC_HEADER_SIZE;
+	ext_sig = (void *)ext_hdr + EXT_HEADER_SIZE;
+
+	for (i = 0; i < ext_hdr->count; i++) {
+		if (intel_cpu_signatures_match(csig, cpf, ext_sig->sig, ext_sig->pf))
+			return 1;
+		ext_sig++;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(intel_find_matching_signature);
+
+/**
+ * intel_microcode_sanity_check() - Sanity check microcode file.
+ * @mc: Pointer to the microcode file contents.
+ * @print_err: Display failure reason if true, silent if false.
+ * @hdr_type: Type of file, i.e. normal microcode file or In Field Scan file.
+ *            Validate if the microcode header type matches with the type
+ *            specified here.
+ *
+ * Validate certain header fields and verify if computed checksum matches
+ * with the one specified in the header.
+ *
+ * Return: 0 if the file passes all the checks, -EINVAL if any of the checks
+ * fail.
+ */
+int intel_microcode_sanity_check(void *mc, bool print_err, int hdr_type)
+{
+	unsigned long total_size, data_size, ext_table_size;
+	struct microcode_header_intel *mc_header = mc;
+	struct extended_sigtable *ext_header = NULL;
+	u32 sum, orig_sum, ext_sigcount = 0, i;
+	struct extended_signature *ext_sig;
+
+	total_size = get_totalsize(mc_header);
+	data_size = get_datasize(mc_header);
+
+	if (data_size + MC_HEADER_SIZE > total_size) {
+		if (print_err)
+			pr_err("Error: bad microcode data file size.\n");
+		return -EINVAL;
+	}
+
+	if (mc_header->ldrver != 1 || mc_header->hdrver != hdr_type) {
+		if (print_err)
+			pr_err("Error: invalid/unknown microcode update format. Header type %d\n",
+			       mc_header->hdrver);
+		return -EINVAL;
+	}
+
+	ext_table_size = total_size - (MC_HEADER_SIZE + data_size);
+	if (ext_table_size) {
+		u32 ext_table_sum = 0;
+		u32 *ext_tablep;
+
+		if (ext_table_size < EXT_HEADER_SIZE ||
+		    ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) {
+			if (print_err)
+				pr_err("Error: truncated extended signature table.\n");
+			return -EINVAL;
+		}
+
+		ext_header = mc + MC_HEADER_SIZE + data_size;
+		if (ext_table_size != exttable_size(ext_header)) {
+			if (print_err)
+				pr_err("Error: extended signature table size mismatch.\n");
+			return -EFAULT;
+		}
+
+		ext_sigcount = ext_header->count;
+
+		/*
+		 * Check extended table checksum: the sum of all dwords that
+		 * comprise a valid table must be 0.
+		 */
+		ext_tablep = (u32 *)ext_header;
+
+		i = ext_table_size / sizeof(u32);
+		while (i--)
+			ext_table_sum += ext_tablep[i];
+
+		if (ext_table_sum) {
+			if (print_err)
+				pr_warn("Bad extended signature table checksum, aborting.\n");
+			return -EINVAL;
+		}
+	}
+
+	/*
+	 * Calculate the checksum of update data and header. The checksum of
+	 * valid update data and header including the extended signature table
+	 * must be 0.
+	 */
+	orig_sum = 0;
+	i = (MC_HEADER_SIZE + data_size) / sizeof(u32);
+	while (i--)
+		orig_sum += ((u32 *)mc)[i];
+
+	if (orig_sum) {
+		if (print_err)
+			pr_err("Bad microcode data checksum, aborting.\n");
+		return -EINVAL;
+	}
+
+	if (!ext_table_size)
+		return 0;
+
+	/*
+	 * Check extended signature checksum: 0 => valid.
+	 */
+	for (i = 0; i < ext_sigcount; i++) {
+		ext_sig = (void *)ext_header + EXT_HEADER_SIZE +
+			  EXT_SIGNATURE_SIZE * i;
+
+		sum = (mc_header->sig + mc_header->pf + mc_header->cksum) -
+		      (ext_sig->sig + ext_sig->pf + ext_sig->cksum);
+		if (sum) {
+			if (print_err)
+				pr_err("Bad extended signature checksum, aborting.\n");
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(intel_microcode_sanity_check);
+
 /*
  * Returns 1 if update has been found, 0 otherwise.
  */


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 04/30] x86/microcode: Include vendor headers into microcode.h
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (2 preceding siblings ...)
  2023-08-10 18:37 ` [patch 03/30] x86/microcode/intel: Move microcode functions out of cpu/intel.c Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 05/30] x86/microcode: Make reload_early_microcode() static Thomas Gleixner
                   ` (25 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Ashok Raj <ashok.raj@intel.com>

Currently vendor specific headers are included explicitly when used in common
code. Instead, include the vendor specific headers in microcode.h, and
include that in all usages. No functional change.

Suggested-by: Boris Petkov <bp@alien8.de>
Signed-off-by: Ashok Raj <ashok.raj@intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/include/asm/microcode.h       | 5 ++++-
 arch/x86/include/asm/microcode_amd.h   | 2 --
 arch/x86/include/asm/microcode_intel.h | 2 --
 arch/x86/kernel/cpu/common.c           | 1 -
 arch/x86/kernel/cpu/intel.c            | 2 +-
 arch/x86/kernel/cpu/microcode/amd.c    | 1 -
 arch/x86/kernel/cpu/microcode/core.c   | 2 --
 arch/x86/kernel/cpu/microcode/intel.c  | 2 +-
 drivers/platform/x86/intel/ifs/load.c  | 2 +-
 9 files changed, 7 insertions(+), 12 deletions(-)
---
diff --git a/arch/x86/include/asm/microcode.h b/arch/x86/include/asm/microcode.h
index 320566a0443d..2e1a9d0582e7 100644
--- a/arch/x86/include/asm/microcode.h
+++ b/arch/x86/include/asm/microcode.h
@@ -2,10 +2,13 @@
 #ifndef _ASM_X86_MICROCODE_H
 #define _ASM_X86_MICROCODE_H
 
-#include <asm/cpu.h>
 #include <linux/earlycpio.h>
 #include <linux/initrd.h>
 
+#include <asm/cpu.h>
+#include <asm/microcode_amd.h>
+#include <asm/microcode_intel.h>
+
 struct ucode_patch {
 	struct list_head plist;
 	void *data;		/* Intel uses only this one */
diff --git a/arch/x86/include/asm/microcode_amd.h b/arch/x86/include/asm/microcode_amd.h
index a995b7685223..0531983016c9 100644
--- a/arch/x86/include/asm/microcode_amd.h
+++ b/arch/x86/include/asm/microcode_amd.h
@@ -2,8 +2,6 @@
 #ifndef _ASM_X86_MICROCODE_AMD_H
 #define _ASM_X86_MICROCODE_AMD_H
 
-#include <asm/microcode.h>
-
 #define UCODE_MAGIC			0x00414d44
 #define UCODE_EQUIV_CPU_TABLE_TYPE	0x00000000
 #define UCODE_UCODE_TYPE		0x00000001
diff --git a/arch/x86/include/asm/microcode_intel.h b/arch/x86/include/asm/microcode_intel.h
index f1fa979e05bf..3ca740451a3d 100644
--- a/arch/x86/include/asm/microcode_intel.h
+++ b/arch/x86/include/asm/microcode_intel.h
@@ -2,8 +2,6 @@
 #ifndef _ASM_X86_MICROCODE_INTEL_H
 #define _ASM_X86_MICROCODE_INTEL_H
 
-#include <asm/microcode.h>
-
 struct microcode_header_intel {
 	unsigned int            hdrver;
 	unsigned int            rev;
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 06015f304cb8..1ea5f822a7ca 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -59,7 +59,6 @@
 #include <asm/cacheinfo.h>
 #include <asm/memtype.h>
 #include <asm/microcode.h>
-#include <asm/microcode_intel.h>
 #include <asm/intel-family.h>
 #include <asm/cpu_device_id.h>
 #include <asm/uv/uv.h>
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index 1c4639588ff9..9fd67e68179f 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -20,7 +20,7 @@
 #include <asm/bugs.h>
 #include <asm/cpu.h>
 #include <asm/intel-family.h>
-#include <asm/microcode_intel.h>
+#include <asm/microcode.h>
 #include <asm/hwcap2.h>
 #include <asm/elf.h>
 #include <asm/cpu_device_id.h>
diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c
index a28b103256ff..dc0a3bed5f6e 100644
--- a/arch/x86/kernel/cpu/microcode/amd.c
+++ b/arch/x86/kernel/cpu/microcode/amd.c
@@ -29,7 +29,6 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 
-#include <asm/microcode_amd.h>
 #include <asm/microcode.h>
 #include <asm/processor.h>
 #include <asm/setup.h>
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index c9a53e330df3..73a3c22773c2 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -31,9 +31,7 @@
 #include <linux/fs.h>
 #include <linux/mm.h>
 
-#include <asm/microcode_intel.h>
 #include <asm/cpu_device_id.h>
-#include <asm/microcode_amd.h>
 #include <asm/perf_event.h>
 #include <asm/microcode.h>
 #include <asm/processor.h>
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index 467cf37ea90a..0ae0c3fd66a1 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -30,9 +30,9 @@
 #include <linux/uio.h>
 #include <linux/mm.h>
 
-#include <asm/microcode_intel.h>
 #include <asm/intel-family.h>
 #include <asm/processor.h>
+#include <asm/microcode.h>
 #include <asm/tlbflush.h>
 #include <asm/setup.h>
 #include <asm/msr.h>
diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c
index e6ae8265f3a3..390862ab0357 100644
--- a/drivers/platform/x86/intel/ifs/load.c
+++ b/drivers/platform/x86/intel/ifs/load.c
@@ -3,7 +3,7 @@
 
 #include <linux/firmware.h>
 #include <asm/cpu.h>
-#include <asm/microcode_intel.h>
+#include <asm/microcode.h>
 
 #include "ifs.h"
 


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [patch 05/30] x86/microcode: Make reload_early_microcode() static
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (3 preceding siblings ...)
  2023-08-10 18:37 ` [patch 04/30] x86/microcode: Include vendor headers into microcode.h Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 06/30] x86/microcode/intel: Rename get_datasize() since its used externally Thomas Gleixner
                   ` (24 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

fe055896c040 ("x86/microcode: Merge the early microcode loader") left this
needlessly public. Git archaeology provided by Borislav.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/include/asm/microcode.h     |    2 --
 arch/x86/kernel/cpu/microcode/core.c |    2 +-
 2 files changed, 1 insertion(+), 3 deletions(-)
---
--- a/arch/x86/include/asm/microcode.h
+++ b/arch/x86/include/asm/microcode.h
@@ -128,13 +128,11 @@ static inline unsigned int x86_cpuid_fam
 #ifdef CONFIG_MICROCODE
 extern void __init load_ucode_bsp(void);
 extern void load_ucode_ap(void);
-void reload_early_microcode(unsigned int cpu);
 extern bool initrd_gone;
 void microcode_bsp_resume(void);
 #else
 static inline void __init load_ucode_bsp(void)			{ }
 static inline void load_ucode_ap(void)				{ }
-static inline void reload_early_microcode(unsigned int cpu)	{ }
 static inline void microcode_bsp_resume(void)			{ }
 #endif
 
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -293,7 +293,7 @@ struct cpio_data find_microcode_in_initr
 #endif
 }
 
-void reload_early_microcode(unsigned int cpu)
+static void reload_early_microcode(unsigned int cpu)
 {
 	int vendor, family;
 


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 06/30] x86/microcode/intel: Rename get_datasize() since its used externally
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (4 preceding siblings ...)
  2023-08-10 18:37 ` [patch 05/30] x86/microcode: Make reload_early_microcode() static Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 07/30] x86/microcode: Move core specific defines to local header Thomas Gleixner
                   ` (23 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Ashok Raj <ashok.raj@intel.com>

Rename get_datasize() to intel_microcode_get_datasize() and make it an inline.

Suggested-by: Boris Petkov <bp@alien8.de>
Signed-off-by: Ashok Raj <ashok.raj@intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/include/asm/microcode_intel.h |    9 ++++++---
 arch/x86/kernel/cpu/microcode/intel.c  |    8 ++++----
 drivers/platform/x86/intel/ifs/load.c  |    2 +-
 3 files changed, 11 insertions(+), 8 deletions(-)
---
--- a/arch/x86/include/asm/microcode_intel.h
+++ b/arch/x86/include/asm/microcode_intel.h
@@ -48,9 +48,12 @@ struct extended_sigtable {
 	 ((struct microcode_intel *)mc)->hdr.totalsize : \
 	 DEFAULT_UCODE_TOTALSIZE)
 
-#define get_datasize(mc) \
-	(((struct microcode_intel *)mc)->hdr.datasize ? \
-	 ((struct microcode_intel *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE)
+static inline int intel_microcode_get_datasize(void *data)
+{
+	struct microcode_intel *mc = data;
+
+	return mc->hdr.datasize ? : DEFAULT_UCODE_DATASIZE;
+}
 
 #define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE)
 
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -90,10 +90,10 @@ int intel_find_matching_signature(void *
 		return 1;
 
 	/* Look for ext. headers: */
-	if (get_totalsize(mc_hdr) <= get_datasize(mc_hdr) + MC_HEADER_SIZE)
+	if (get_totalsize(mc_hdr) <= intel_microcode_get_datasize(mc_hdr) + MC_HEADER_SIZE)
 		return 0;
 
-	ext_hdr = mc + get_datasize(mc_hdr) + MC_HEADER_SIZE;
+	ext_hdr = mc + intel_microcode_get_datasize(mc_hdr) + MC_HEADER_SIZE;
 	ext_sig = (void *)ext_hdr + EXT_HEADER_SIZE;
 
 	for (i = 0; i < ext_hdr->count; i++) {
@@ -128,7 +128,7 @@ int intel_microcode_sanity_check(void *m
 	struct extended_signature *ext_sig;
 
 	total_size = get_totalsize(mc_header);
-	data_size = get_datasize(mc_header);
+	data_size = intel_microcode_get_datasize(mc_header);
 
 	if (data_size + MC_HEADER_SIZE > total_size) {
 		if (print_err)
@@ -410,7 +410,7 @@ static void show_saved_mc(void)
 		date	= mc_saved_header->date;
 
 		total_size	= get_totalsize(mc_saved_header);
-		data_size	= get_datasize(mc_saved_header);
+		data_size	= intel_microcode_get_datasize(mc_saved_header);
 
 		pr_debug("mc_saved[%d]: sig=0x%x, pf=0x%x, rev=0x%x, total size=0x%x, date = %04x-%02x-%02x\n",
 			 i++, sig, pf, rev, total_size,
--- a/drivers/platform/x86/intel/ifs/load.c
+++ b/drivers/platform/x86/intel/ifs/load.c
@@ -60,7 +60,7 @@ static struct metadata_header *find_meta
 	unsigned long data_size, total_meta;
 	unsigned long meta_size = 0;
 
-	data_size = get_datasize(ucode);
+	data_size = intel_microcode_get_datasize(ucode);
 	total_meta = ((struct microcode_intel *)ucode)->hdr.metasize;
 	if (!total_meta)
 		return NULL;


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 07/30] x86/microcode: Move core specific defines to local header
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (5 preceding siblings ...)
  2023-08-10 18:37 ` [patch 06/30] x86/microcode/intel: Rename get_datasize() since its used externally Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 08/30] x86/microcode/intel: Rip out mixed stepping support for Intel CPUs Thomas Gleixner
                   ` (22 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

There is no reason to expose all of this globally. Move everything which is
not required outside of the microcode specific code to local header files.

No functional change.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 arch/x86/include/asm/microcode.h         |  157 +++++++-----------------
 arch/x86/include/asm/microcode_amd.h     |   54 --------
 arch/x86/include/asm/microcode_intel.h   |   89 --------------
 arch/x86/kernel/cpu/microcode/amd.c      |    2 
 arch/x86/kernel/cpu/microcode/core.c     |    3 
 arch/x86/kernel/cpu/microcode/intel.c    |    3 
 arch/x86/kernel/cpu/microcode/internal.h |  196 +++++++++++++++++++++++++++++++
 7 files changed, 251 insertions(+), 253 deletions(-)

--- a/arch/x86/include/asm/microcode.h
+++ b/arch/x86/include/asm/microcode.h
@@ -2,138 +2,79 @@
 #ifndef _ASM_X86_MICROCODE_H
 #define _ASM_X86_MICROCODE_H
 
-#include <linux/earlycpio.h>
-#include <linux/initrd.h>
-
-#include <asm/cpu.h>
-#include <asm/microcode_amd.h>
-#include <asm/microcode_intel.h>
-
-struct ucode_patch {
-	struct list_head plist;
-	void *data;		/* Intel uses only this one */
-	unsigned int size;
-	u32 patch_id;
-	u16 equiv_cpu;
-};
-
-extern struct list_head microcode_cache;
-
 struct cpu_signature {
 	unsigned int sig;
 	unsigned int pf;
 	unsigned int rev;
 };
 
-struct device;
-
-enum ucode_state {
-	UCODE_OK	= 0,
-	UCODE_NEW,
-	UCODE_UPDATED,
-	UCODE_NFOUND,
-	UCODE_ERROR,
-};
-
-struct microcode_ops {
-	enum ucode_state (*request_microcode_fw) (int cpu, struct device *);
-
-	void (*microcode_fini_cpu) (int cpu);
-
-	/*
-	 * The generic 'microcode_core' part guarantees that
-	 * the callbacks below run on a target cpu when they
-	 * are being called.
-	 * See also the "Synchronization" section in microcode_core.c.
-	 */
-	enum ucode_state (*apply_microcode) (int cpu);
-	int (*collect_cpu_info) (int cpu, struct cpu_signature *csig);
-};
-
 struct ucode_cpu_info {
 	struct cpu_signature	cpu_sig;
 	void			*mc;
 };
-extern struct ucode_cpu_info ucode_cpu_info[];
-struct cpio_data find_microcode_in_initrd(const char *path, bool use_pa);
-
-#ifdef CONFIG_CPU_SUP_INTEL
-extern struct microcode_ops * __init init_intel_microcode(void);
-#else
-static inline struct microcode_ops * __init init_intel_microcode(void)
-{
-	return NULL;
-}
-#endif /* CONFIG_CPU_SUP_INTEL */
 
-#ifdef CONFIG_CPU_SUP_AMD
-extern struct microcode_ops * __init init_amd_microcode(void);
-extern void __exit exit_amd_microcode(void);
+#ifdef CONFIG_MICROCODE
+void load_ucode_bsp(void);
+void load_ucode_ap(void);
+void microcode_bsp_resume(void);
 #else
-static inline struct microcode_ops * __init init_amd_microcode(void)
-{
-	return NULL;
-}
-static inline void __exit exit_amd_microcode(void) {}
+static inline void load_ucode_bsp(void)	{ }
+static inline void load_ucode_ap(void) { }
+static inline void microcode_bsp_resume(void) { }
 #endif
 
-#define MAX_UCODE_COUNT 128
-
-#define QCHAR(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24))
-#define CPUID_INTEL1 QCHAR('G', 'e', 'n', 'u')
-#define CPUID_INTEL2 QCHAR('i', 'n', 'e', 'I')
-#define CPUID_INTEL3 QCHAR('n', 't', 'e', 'l')
-#define CPUID_AMD1 QCHAR('A', 'u', 't', 'h')
-#define CPUID_AMD2 QCHAR('e', 'n', 't', 'i')
-#define CPUID_AMD3 QCHAR('c', 'A', 'M', 'D')
-
-#define CPUID_IS(a, b, c, ebx, ecx, edx)	\
-		(!((ebx ^ (a))|(edx ^ (b))|(ecx ^ (c))))
-
-/*
- * In early loading microcode phase on BSP, boot_cpu_data is not set up yet.
- * x86_cpuid_vendor() gets vendor id for BSP.
- *
- * In 32 bit AP case, accessing boot_cpu_data needs linear address. To simplify
- * coding, we still use x86_cpuid_vendor() to get vendor id for AP.
- *
- * x86_cpuid_vendor() gets vendor information directly from CPUID.
- */
-static inline int x86_cpuid_vendor(void)
-{
-	u32 eax = 0x00000000;
-	u32 ebx, ecx = 0, edx;
+#ifdef CONFIG_CPU_SUP_INTEL
+/* Intel specific microcode defines. Public for IFS */
+struct microcode_header_intel {
+	unsigned int	hdrver;
+	unsigned int	rev;
+	unsigned int	date;
+	unsigned int	sig;
+	unsigned int	cksum;
+	unsigned int	ldrver;
+	unsigned int	pf;
+	unsigned int	datasize;
+	unsigned int	totalsize;
+	unsigned int	metasize;
+	unsigned int	reserved[2];
+};
 
-	native_cpuid(&eax, &ebx, &ecx, &edx);
+struct microcode_intel {
+	struct microcode_header_intel	hdr;
+	unsigned int			bits[];
+};
 
-	if (CPUID_IS(CPUID_INTEL1, CPUID_INTEL2, CPUID_INTEL3, ebx, ecx, edx))
-		return X86_VENDOR_INTEL;
+#define DEFAULT_UCODE_DATASIZE		(2000)
+#define MC_HEADER_SIZE			(sizeof(struct microcode_header_intel))
+#define MC_HEADER_TYPE_MICROCODE	1
+#define MC_HEADER_TYPE_IFS		2
 
-	if (CPUID_IS(CPUID_AMD1, CPUID_AMD2, CPUID_AMD3, ebx, ecx, edx))
-		return X86_VENDOR_AMD;
+static inline int intel_microcode_get_datasize(void *data)
+{
+	struct microcode_intel *mc = data;
 
-	return X86_VENDOR_UNKNOWN;
+	return mc->hdr.datasize ? : DEFAULT_UCODE_DATASIZE;
 }
 
-static inline unsigned int x86_cpuid_family(void)
+static inline u32 intel_get_microcode_revision(void)
 {
-	u32 eax = 0x00000001;
-	u32 ebx, ecx = 0, edx;
+	u32 rev, dummy;
+
+	native_wrmsrl(MSR_IA32_UCODE_REV, 0);
 
-	native_cpuid(&eax, &ebx, &ecx, &edx);
+	/* As documented in the SDM: Do a CPUID 1 here */
+	native_cpuid_eax(1);
 
-	return x86_family(eax);
+	/* get the current revision from MSR 0x8B */
+	native_rdmsr(MSR_IA32_UCODE_REV, dummy, rev);
+
+	return rev;
 }
 
-#ifdef CONFIG_MICROCODE
-extern void __init load_ucode_bsp(void);
-extern void load_ucode_ap(void);
-extern bool initrd_gone;
-void microcode_bsp_resume(void);
-#else
-static inline void __init load_ucode_bsp(void)			{ }
-static inline void load_ucode_ap(void)				{ }
-static inline void microcode_bsp_resume(void)			{ }
-#endif
+void show_ucode_info_early(void);
+
+#else /* CONFIG_CPU_SUP_INTEL */
+static inline void show_ucode_info_early(void) { }
+#endif /* !CONFIG_CPU_SUP_INTEL */
 
 #endif /* _ASM_X86_MICROCODE_H */
--- a/arch/x86/include/asm/microcode_amd.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _ASM_X86_MICROCODE_AMD_H
-#define _ASM_X86_MICROCODE_AMD_H
-
-#define UCODE_MAGIC			0x00414d44
-#define UCODE_EQUIV_CPU_TABLE_TYPE	0x00000000
-#define UCODE_UCODE_TYPE		0x00000001
-
-#define SECTION_HDR_SIZE		8
-#define CONTAINER_HDR_SZ		12
-
-struct equiv_cpu_entry {
-	u32	installed_cpu;
-	u32	fixed_errata_mask;
-	u32	fixed_errata_compare;
-	u16	equiv_cpu;
-	u16	res;
-} __attribute__((packed));
-
-struct microcode_header_amd {
-	u32	data_code;
-	u32	patch_id;
-	u16	mc_patch_data_id;
-	u8	mc_patch_data_len;
-	u8	init_flag;
-	u32	mc_patch_data_checksum;
-	u32	nb_dev_id;
-	u32	sb_dev_id;
-	u16	processor_rev_id;
-	u8	nb_rev_id;
-	u8	sb_rev_id;
-	u8	bios_api_rev;
-	u8	reserved1[3];
-	u32	match_reg[8];
-} __attribute__((packed));
-
-struct microcode_amd {
-	struct microcode_header_amd	hdr;
-	unsigned int			mpb[];
-};
-
-#define PATCH_MAX_SIZE (3 * PAGE_SIZE)
-
-#ifdef CONFIG_CPU_SUP_AMD
-extern void load_ucode_amd_early(unsigned int cpuid_1_eax);
-extern int __init save_microcode_in_initrd_amd(unsigned int family);
-void reload_ucode_amd(unsigned int cpu);
-#else
-static inline void load_ucode_amd_early(unsigned int cpuid_1_eax) {}
-static inline int __init
-save_microcode_in_initrd_amd(unsigned int family) { return -EINVAL; }
-static inline void reload_ucode_amd(unsigned int cpu) {}
-#endif
-#endif /* _ASM_X86_MICROCODE_AMD_H */
--- a/arch/x86/include/asm/microcode_intel.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _ASM_X86_MICROCODE_INTEL_H
-#define _ASM_X86_MICROCODE_INTEL_H
-
-struct microcode_header_intel {
-	unsigned int            hdrver;
-	unsigned int            rev;
-	unsigned int            date;
-	unsigned int            sig;
-	unsigned int            cksum;
-	unsigned int            ldrver;
-	unsigned int            pf;
-	unsigned int            datasize;
-	unsigned int            totalsize;
-	unsigned int            metasize;
-	unsigned int            reserved[2];
-};
-
-struct microcode_intel {
-	struct microcode_header_intel hdr;
-	unsigned int            bits[];
-};
-
-/* microcode format is extended from prescott processors */
-struct extended_signature {
-	unsigned int            sig;
-	unsigned int            pf;
-	unsigned int            cksum;
-};
-
-struct extended_sigtable {
-	unsigned int            count;
-	unsigned int            cksum;
-	unsigned int            reserved[3];
-	struct extended_signature sigs[];
-};
-
-#define DEFAULT_UCODE_DATASIZE	(2000)
-#define MC_HEADER_SIZE		(sizeof(struct microcode_header_intel))
-#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
-#define EXT_HEADER_SIZE		(sizeof(struct extended_sigtable))
-#define EXT_SIGNATURE_SIZE	(sizeof(struct extended_signature))
-#define MC_HEADER_TYPE_MICROCODE	1
-#define MC_HEADER_TYPE_IFS		2
-
-#define get_totalsize(mc) \
-	(((struct microcode_intel *)mc)->hdr.datasize ? \
-	 ((struct microcode_intel *)mc)->hdr.totalsize : \
-	 DEFAULT_UCODE_TOTALSIZE)
-
-static inline int intel_microcode_get_datasize(void *data)
-{
-	struct microcode_intel *mc = data;
-
-	return mc->hdr.datasize ? : DEFAULT_UCODE_DATASIZE;
-}
-
-#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE)
-
-static inline u32 intel_get_microcode_revision(void)
-{
-	u32 rev, dummy;
-
-	native_wrmsrl(MSR_IA32_UCODE_REV, 0);
-
-	/* As documented in the SDM: Do a CPUID 1 here */
-	native_cpuid_eax(1);
-
-	/* get the current revision from MSR 0x8B */
-	native_rdmsr(MSR_IA32_UCODE_REV, dummy, rev);
-
-	return rev;
-}
-
-#ifdef CONFIG_CPU_SUP_INTEL
-extern void __init load_ucode_intel_bsp(void);
-extern void load_ucode_intel_ap(void);
-extern void show_ucode_info_early(void);
-extern int __init save_microcode_in_initrd_intel(void);
-void reload_ucode_intel(void);
-#else
-static inline __init void load_ucode_intel_bsp(void) {}
-static inline void load_ucode_intel_ap(void) {}
-static inline void show_ucode_info_early(void) {}
-static inline int __init save_microcode_in_initrd_intel(void) { return -EINVAL; }
-static inline void reload_ucode_intel(void) {}
-#endif
-
-#endif /* _ASM_X86_MICROCODE_INTEL_H */
--- a/arch/x86/kernel/cpu/microcode/amd.c
+++ b/arch/x86/kernel/cpu/microcode/amd.c
@@ -35,6 +35,8 @@
 #include <asm/cpu.h>
 #include <asm/msr.h>
 
+#include "internal.h"
+
 static struct equiv_cpu_table {
 	unsigned int num_entries;
 	struct equiv_cpu_entry *entry;
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -33,11 +33,12 @@
 
 #include <asm/cpu_device_id.h>
 #include <asm/perf_event.h>
-#include <asm/microcode.h>
 #include <asm/processor.h>
 #include <asm/cmdline.h>
 #include <asm/setup.h>
 
+#include "internal.h"
+
 #define DRIVER_VERSION	"2.2"
 
 static struct microcode_ops	*microcode_ops;
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -32,11 +32,12 @@
 
 #include <asm/intel-family.h>
 #include <asm/processor.h>
-#include <asm/microcode.h>
 #include <asm/tlbflush.h>
 #include <asm/setup.h>
 #include <asm/msr.h>
 
+#include "internal.h"
+
 static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin";
 
 /* Current microcode patch used in early patching on the APs. */
--- /dev/null
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _X86_MICROCODE_INTERNAL_H
+#define _X86_MICROCODE_INTERNAL_H
+
+#include <linux/earlycpio.h>
+#include <linux/initrd.h>
+
+#include <asm/cpu.h>
+#include <asm/microcode.h>
+
+struct ucode_patch {
+	struct list_head plist;
+	void *data;		/* Intel uses only this one */
+	unsigned int size;
+	u32 patch_id;
+	u16 equiv_cpu;
+};
+
+extern struct list_head microcode_cache;
+
+struct device;
+
+enum ucode_state {
+	UCODE_OK	= 0,
+	UCODE_NEW,
+	UCODE_UPDATED,
+	UCODE_NFOUND,
+	UCODE_ERROR,
+};
+
+struct microcode_ops {
+	enum ucode_state (*request_microcode_fw)(int cpu, struct device *dev);
+
+	void (*microcode_fini_cpu)(int cpu);
+
+	/*
+	 * The generic 'microcode_core' part guarantees that
+	 * the callbacks below run on a target cpu when they
+	 * are being called.
+	 * See also the "Synchronization" section in microcode_core.c.
+	 */
+	enum ucode_state (*apply_microcode)(int cpu);
+	int (*collect_cpu_info)(int cpu, struct cpu_signature *csig);
+};
+
+extern struct ucode_cpu_info ucode_cpu_info[];
+struct cpio_data find_microcode_in_initrd(const char *path, bool use_pa);
+
+#define MAX_UCODE_COUNT 128
+
+#define QCHAR(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24))
+#define CPUID_INTEL1 QCHAR('G', 'e', 'n', 'u')
+#define CPUID_INTEL2 QCHAR('i', 'n', 'e', 'I')
+#define CPUID_INTEL3 QCHAR('n', 't', 'e', 'l')
+#define CPUID_AMD1 QCHAR('A', 'u', 't', 'h')
+#define CPUID_AMD2 QCHAR('e', 'n', 't', 'i')
+#define CPUID_AMD3 QCHAR('c', 'A', 'M', 'D')
+
+#define CPUID_IS(a, b, c, ebx, ecx, edx)	\
+		(!(((ebx) ^ (a)) | ((edx) ^ (b)) | ((ecx) ^ (c))))
+
+/*
+ * In early loading microcode phase on BSP, boot_cpu_data is not set up yet.
+ * x86_cpuid_vendor() gets vendor id for BSP.
+ *
+ * In 32 bit AP case, accessing boot_cpu_data needs linear address. To simplify
+ * coding, we still use x86_cpuid_vendor() to get vendor id for AP.
+ *
+ * x86_cpuid_vendor() gets vendor information directly from CPUID.
+ */
+static inline int x86_cpuid_vendor(void)
+{
+	u32 eax = 0x00000000;
+	u32 ebx, ecx = 0, edx;
+
+	native_cpuid(&eax, &ebx, &ecx, &edx);
+
+	if (CPUID_IS(CPUID_INTEL1, CPUID_INTEL2, CPUID_INTEL3, ebx, ecx, edx))
+		return X86_VENDOR_INTEL;
+
+	if (CPUID_IS(CPUID_AMD1, CPUID_AMD2, CPUID_AMD3, ebx, ecx, edx))
+		return X86_VENDOR_AMD;
+
+	return X86_VENDOR_UNKNOWN;
+}
+
+static inline unsigned int x86_cpuid_family(void)
+{
+	u32 eax = 0x00000001;
+	u32 ebx, ecx = 0, edx;
+
+	native_cpuid(&eax, &ebx, &ecx, &edx);
+
+	return x86_family(eax);
+}
+
+extern bool initrd_gone;
+
+#ifdef CONFIG_CPU_SUP_AMD
+#define UCODE_MAGIC			0x00414d44
+#define UCODE_EQUIV_CPU_TABLE_TYPE	0x00000000
+#define UCODE_UCODE_TYPE		0x00000001
+
+#define SECTION_HDR_SIZE		8
+#define CONTAINER_HDR_SZ		12
+
+struct equiv_cpu_entry {
+	u32	installed_cpu;
+	u32	fixed_errata_mask;
+	u32	fixed_errata_compare;
+	u16	equiv_cpu;
+	u16	res;
+} __packed;
+
+struct microcode_header_amd {
+	u32	data_code;
+	u32	patch_id;
+	u16	mc_patch_data_id;
+	u8	mc_patch_data_len;
+	u8	init_flag;
+	u32	mc_patch_data_checksum;
+	u32	nb_dev_id;
+	u32	sb_dev_id;
+	u16	processor_rev_id;
+	u8	nb_rev_id;
+	u8	sb_rev_id;
+	u8	bios_api_rev;
+	u8	reserved1[3];
+	u32	match_reg[8];
+} __packed;
+
+struct microcode_amd {
+	struct microcode_header_amd	hdr;
+	unsigned int			mpb[];
+};
+
+#define PATCH_MAX_SIZE (3 * PAGE_SIZE)
+
+void load_ucode_amd_bsp(unsigned int family);
+void load_ucode_amd_ap(unsigned int family);
+void load_ucode_amd_early(unsigned int cpuid_1_eax);
+int save_microcode_in_initrd_amd(unsigned int family);
+void reload_ucode_amd(unsigned int cpu);
+struct microcode_ops *init_amd_microcode(void);
+void exit_amd_microcode(void);
+#else /* CONFIG_MICROCODE_AMD */
+static inline void load_ucode_amd_bsp(unsigned int family) { }
+static inline void load_ucode_amd_ap(unsigned int family) { }
+static inline void load_ucode_amd_early(unsigned int family) { }
+static inline int save_microcode_in_initrd_amd(unsigned int family) { return -EINVAL; }
+static inline void reload_ucode_amd(unsigned int cpu) { }
+static inline struct microcode_ops *init_amd_microcode(void) { return NULL; }
+static inline void exit_amd_microcode(void) { }
+#endif /* !CONFIG_MICROCODE_AMD */
+
+#ifdef CONFIG_CPU_SUP_INTEL
+struct extended_signature {
+	unsigned int	sig;
+	unsigned int	pf;
+	unsigned int	cksum;
+};
+
+struct extended_sigtable {
+	unsigned int			count;
+	unsigned int			cksum;
+	unsigned int			reserved[3];
+	struct extended_signature	sigs[];
+};
+
+#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
+#define EXT_HEADER_SIZE		(sizeof(struct extended_sigtable))
+#define EXT_SIGNATURE_SIZE	(sizeof(struct extended_signature))
+
+#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE)
+
+static inline int get_totalsize(void *mc)
+{
+	struct microcode_intel *intel_mc = (struct microcode_intel *)mc;
+
+	return intel_mc->hdr.datasize ? intel_mc->hdr.totalsize : DEFAULT_UCODE_TOTALSIZE;
+}
+
+void load_ucode_intel_bsp(void);
+void load_ucode_intel_ap(void);
+int save_microcode_in_initrd_intel(void);
+void reload_ucode_intel(void);
+struct microcode_ops *init_intel_microcode(void);
+#else /* CONFIG_CPU_SUP_INTEL */
+static inline void load_ucode_intel_bsp(void) { }
+static inline void load_ucode_intel_ap(void) { }
+static inline int save_microcode_in_initrd_intel(void) { return -EINVAL; }
+static inline void reload_ucode_intel(void) { }
+static inline struct microcode_ops *init_intel_microcode(void) { return NULL; }
+#endif  /* !CONFIG_CPU_SUP_INTEL */
+
+#endif /* _X86_MICROCODE_INTERNAL_H */


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 08/30] x86/microcode/intel: Rip out mixed stepping support for Intel CPUs
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (6 preceding siblings ...)
  2023-08-10 18:37 ` [patch 07/30] x86/microcode: Move core specific defines to local header Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-11 22:25   ` Borislav Petkov
  2023-08-10 18:37 ` [patch 09/30] x86/microcode/intel: Remove debug code Thomas Gleixner
                   ` (21 subsequent siblings)
  29 siblings, 1 reply; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Ashok Raj <ashok.raj@intel.com>

Mixed steppings aren't supported on Intel CPUs. Only one patch is required
for the entire system. The caching of micro code blobs which match the
family and model is therefore pointless and in fact it is disfunctional as
CPU hotplug updates use only a single microcode blob, i.e. the one where
*intel_ucode_patch points to.

Remove the microcode cache and make it an AMD local feature.

[ tglx: Save only at the end. Otherwise random microcode ends up in the
  	pointer for early loading ]

Originally-From: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ashok Raj <ashok.raj@intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 arch/x86/kernel/cpu/microcode/amd.c      |   10 +
 arch/x86/kernel/cpu/microcode/core.c     |    2 
 arch/x86/kernel/cpu/microcode/intel.c    |  201 ++++++++-----------------------
 arch/x86/kernel/cpu/microcode/internal.h |   10 -
 4 files changed, 64 insertions(+), 159 deletions(-)
---
--- a/arch/x86/kernel/cpu/microcode/amd.c
+++ b/arch/x86/kernel/cpu/microcode/amd.c
@@ -37,6 +37,16 @@
 
 #include "internal.h"
 
+struct ucode_patch {
+	struct list_head plist;
+	void *data;
+	unsigned int size;
+	u32 patch_id;
+	u16 equiv_cpu;
+};
+
+static LIST_HEAD(microcode_cache);
+
 static struct equiv_cpu_table {
 	unsigned int num_entries;
 	struct equiv_cpu_entry *entry;
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -46,8 +46,6 @@ static bool dis_ucode_ldr = true;
 
 bool initrd_gone;
 
-LIST_HEAD(microcode_cache);
-
 /*
  * Synchronization.
  *
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -41,10 +41,10 @@
 static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin";
 
 /* Current microcode patch used in early patching on the APs. */
-static struct microcode_intel *intel_ucode_patch;
+static struct microcode_intel *intel_ucode_patch __read_mostly;
 
 /* last level cache size per core */
-static int llc_size_per_core;
+static int llc_size_per_core __ro_after_init;
 
 int intel_cpu_collect_info(struct ucode_cpu_info *uci)
 {
@@ -233,81 +233,26 @@ static int has_newer_microcode(void *mc,
 	return intel_find_matching_signature(mc, csig, cpf);
 }
 
-static struct ucode_patch *memdup_patch(void *data, unsigned int size)
+static void save_microcode_patch(void *data, unsigned int size)
 {
-	struct ucode_patch *p;
+	struct microcode_header_intel *p;
 
-	p = kzalloc(sizeof(struct ucode_patch), GFP_KERNEL);
-	if (!p)
-		return NULL;
-
-	p->data = kmemdup(data, size, GFP_KERNEL);
-	if (!p->data) {
-		kfree(p);
-		return NULL;
-	}
-
-	return p;
-}
-
-static void save_microcode_patch(struct ucode_cpu_info *uci, void *data, unsigned int size)
-{
-	struct microcode_header_intel *mc_hdr, *mc_saved_hdr;
-	struct ucode_patch *iter, *tmp, *p = NULL;
-	bool prev_found = false;
-	unsigned int sig, pf;
-
-	mc_hdr = (struct microcode_header_intel *)data;
-
-	list_for_each_entry_safe(iter, tmp, &microcode_cache, plist) {
-		mc_saved_hdr = (struct microcode_header_intel *)iter->data;
-		sig	     = mc_saved_hdr->sig;
-		pf	     = mc_saved_hdr->pf;
-
-		if (intel_find_matching_signature(data, sig, pf)) {
-			prev_found = true;
-
-			if (mc_hdr->rev <= mc_saved_hdr->rev)
-				continue;
-
-			p = memdup_patch(data, size);
-			if (!p)
-				pr_err("Error allocating buffer %p\n", data);
-			else {
-				list_replace(&iter->plist, &p->plist);
-				kfree(iter->data);
-				kfree(iter);
-			}
-		}
-	}
-
-	/*
-	 * There weren't any previous patches found in the list cache; save the
-	 * newly found.
-	 */
-	if (!prev_found) {
-		p = memdup_patch(data, size);
-		if (!p)
-			pr_err("Error allocating buffer for %p\n", data);
-		else
-			list_add_tail(&p->plist, &microcode_cache);
-	}
+	kfree(intel_ucode_patch);
+	intel_ucode_patch = NULL;
 
+	p = kmemdup(data, size, GFP_KERNEL);
 	if (!p)
 		return;
 
-	if (!intel_find_matching_signature(p->data, uci->cpu_sig.sig, uci->cpu_sig.pf))
-		return;
-
 	/*
 	 * Save for early loading. On 32-bit, that needs to be a physical
 	 * address as the APs are running from physical addresses, before
 	 * paging has been enabled.
 	 */
 	if (IS_ENABLED(CONFIG_X86_32))
-		intel_ucode_patch = (struct microcode_intel *)__pa_nodebug(p->data);
+		intel_ucode_patch = (struct microcode_intel *)__pa_nodebug(p);
 	else
-		intel_ucode_patch = p->data;
+		intel_ucode_patch = (struct microcode_intel *)p;
 }
 
 /*
@@ -319,6 +264,7 @@ scan_microcode(void *data, size_t size,
 {
 	struct microcode_header_intel *mc_header;
 	struct microcode_intel *patch = NULL;
+	u32 cur_rev = uci->cpu_sig.rev;
 	unsigned int mc_size;
 
 	while (size) {
@@ -328,8 +274,7 @@ scan_microcode(void *data, size_t size,
 		mc_header = (struct microcode_header_intel *)data;
 
 		mc_size = get_totalsize(mc_header);
-		if (!mc_size ||
-		    mc_size > size ||
+		if (!mc_size || mc_size > size ||
 		    intel_microcode_sanity_check(data, false, MC_HEADER_TYPE_MICROCODE) < 0)
 			break;
 
@@ -341,31 +286,16 @@ scan_microcode(void *data, size_t size,
 			continue;
 		}
 
-		if (save) {
-			save_microcode_patch(uci, data, mc_size);
+		/* BSP scan: Check whether there is newer microcode */
+		if (save && cur_rev >= mc_header->rev)
 			goto next;
-		}
-
 
-		if (!patch) {
-			if (!has_newer_microcode(data,
-						 uci->cpu_sig.sig,
-						 uci->cpu_sig.pf,
-						 uci->cpu_sig.rev))
-				goto next;
-
-		} else {
-			struct microcode_header_intel *phdr = &patch->hdr;
-
-			if (!has_newer_microcode(data,
-						 phdr->sig,
-						 phdr->pf,
-						 phdr->rev))
-				goto next;
-		}
+		/* Save scan: Check whether there is newer or matching microcode */
+		if (save && cur_rev != mc_header->rev)
+			goto next;
 
-		/* We have a newer patch, save it. */
 		patch = data;
+		cur_rev = mc_header->rev;
 
 next:
 		data += mc_size;
@@ -374,18 +304,22 @@ scan_microcode(void *data, size_t size,
 	if (size)
 		return NULL;
 
+	if (save && patch)
+		save_microcode_patch(patch, mc_size);
+
 	return patch;
 }
 
 static void show_saved_mc(void)
 {
 #ifdef DEBUG
-	int i = 0, j;
 	unsigned int sig, pf, rev, total_size, data_size, date;
+	struct extended_sigtable *ext_header;
+	struct extended_signature *ext_sig;
 	struct ucode_cpu_info uci;
-	struct ucode_patch *p;
+	int j, ext_sigcount;
 
-	if (list_empty(&microcode_cache)) {
+	if (!intel_ucode_patch) {
 		pr_debug("no microcode data saved.\n");
 		return;
 	}
@@ -397,45 +331,34 @@ static void show_saved_mc(void)
 	rev	= uci.cpu_sig.rev;
 	pr_debug("CPU: sig=0x%x, pf=0x%x, rev=0x%x\n", sig, pf, rev);
 
-	list_for_each_entry(p, &microcode_cache, plist) {
-		struct microcode_header_intel *mc_saved_header;
-		struct extended_sigtable *ext_header;
-		struct extended_signature *ext_sig;
-		int ext_sigcount;
-
-		mc_saved_header = (struct microcode_header_intel *)p->data;
-
-		sig	= mc_saved_header->sig;
-		pf	= mc_saved_header->pf;
-		rev	= mc_saved_header->rev;
-		date	= mc_saved_header->date;
-
-		total_size	= get_totalsize(mc_saved_header);
-		data_size	= intel_microcode_get_datasize(mc_saved_header);
-
-		pr_debug("mc_saved[%d]: sig=0x%x, pf=0x%x, rev=0x%x, total size=0x%x, date = %04x-%02x-%02x\n",
-			 i++, sig, pf, rev, total_size,
-			 date & 0xffff,
-			 date >> 24,
-			 (date >> 16) & 0xff);
-
-		/* Look for ext. headers: */
-		if (total_size <= data_size + MC_HEADER_SIZE)
-			continue;
+	sig	= intel_ucode_patch->hdr.sig;
+	pf	= intel_ucode_patch->hdr.pf;
+	rev	= intel_ucode_patch->hdr.rev;
+	date	= intel_ucode_patch->hdr.date;
+
+	total_size	= get_totalsize(mc_saved_header);
+	data_size	= intel_microcode_get_datasize(mc_saved_header);
+
+	pr_debug("mc_saved: sig=0x%x, pf=0x%x, rev=0x%x, total size=0x%x, date = %04x-%02x-%02x\n",
+		 sig, pf, rev, total_size, date & 0xffff,
+		 date >> 24, (date >> 16) & 0xff);
 
-		ext_header = (void *)mc_saved_header + data_size + MC_HEADER_SIZE;
-		ext_sigcount = ext_header->count;
-		ext_sig = (void *)ext_header + EXT_HEADER_SIZE;
+	/* Look for ext. headers: */
+	if (total_size <= data_size + MC_HEADER_SIZE)
+		return;
 
-		for (j = 0; j < ext_sigcount; j++) {
-			sig = ext_sig->sig;
-			pf = ext_sig->pf;
+	ext_header = (void *)intel_ucode_patch + data_size + MC_HEADER_SIZE;
+	ext_sigcount = ext_header->count;
+	ext_sig = (void *)ext_header + EXT_HEADER_SIZE;
+
+	for (j = 0; j < ext_sigcount; j++) {
+		sig = ext_sig->sig;
+		pf = ext_sig->pf;
 
-			pr_debug("\tExtended[%d]: sig=0x%x, pf=0x%x\n",
-				 j, sig, pf);
+		pr_debug("\tExtended[%d]: sig=0x%x, pf=0x%x\n",
+			 j, sig, pf);
 
-			ext_sig++;
-		}
+		ext_sig++;
 	}
 #endif
 }
@@ -451,7 +374,7 @@ static void save_mc_for_early(struct uco
 
 	mutex_lock(&x86_cpu_microcode_mutex);
 
-	save_microcode_patch(uci, mc, size);
+	save_microcode_patch(mc, size);
 	show_saved_mc();
 
 	mutex_unlock(&x86_cpu_microcode_mutex);
@@ -675,26 +598,10 @@ void load_ucode_intel_ap(void)
 	apply_microcode_early(&uci, true);
 }
 
-static struct microcode_intel *find_patch(struct ucode_cpu_info *uci)
+/* Accessor for microcode pointer */
+static struct microcode_intel *ucode_get_patch(void)
 {
-	struct microcode_header_intel *phdr;
-	struct ucode_patch *iter, *tmp;
-
-	list_for_each_entry_safe(iter, tmp, &microcode_cache, plist) {
-
-		phdr = (struct microcode_header_intel *)iter->data;
-
-		if (phdr->rev <= uci->cpu_sig.rev)
-			continue;
-
-		if (!intel_find_matching_signature(phdr,
-						   uci->cpu_sig.sig,
-						   uci->cpu_sig.pf))
-			continue;
-
-		return iter->data;
-	}
-	return NULL;
+	return intel_ucode_patch;
 }
 
 void reload_ucode_intel(void)
@@ -704,7 +611,7 @@ void reload_ucode_intel(void)
 
 	intel_cpu_collect_info(&uci);
 
-	p = find_patch(&uci);
+	p = ucode_get_patch();
 	if (!p)
 		return;
 
@@ -748,7 +655,7 @@ static enum ucode_state apply_microcode_
 		return UCODE_ERROR;
 
 	/* Look for a newer patch in our cache: */
-	mc = find_patch(uci);
+	mc = ucode_get_patch();
 	if (!mc) {
 		mc = uci->mc;
 		if (!mc)
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -8,16 +8,6 @@
 #include <asm/cpu.h>
 #include <asm/microcode.h>
 
-struct ucode_patch {
-	struct list_head plist;
-	void *data;		/* Intel uses only this one */
-	unsigned int size;
-	u32 patch_id;
-	u16 equiv_cpu;
-};
-
-extern struct list_head microcode_cache;
-
 struct device;
 
 enum ucode_state {


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 09/30] x86/microcode/intel: Remove debug code
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (7 preceding siblings ...)
  2023-08-10 18:37 ` [patch 08/30] x86/microcode/intel: Rip out mixed stepping support for Intel CPUs Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-11 10:45   ` Nikolay Borisov
  2023-08-10 18:37 ` [patch 10/30] x86/microcode: Remove pointless mutex Thomas Gleixner
                   ` (20 subsequent siblings)
  29 siblings, 1 reply; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

This is really of dubious value.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/kernel/cpu/microcode/intel.c |   65 ----------------------------------
 1 file changed, 65 deletions(-)
---
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -10,15 +10,7 @@
  * Copyright (C) 2012 Fenghua Yu <fenghua.yu@intel.com>
  *		      H Peter Anvin" <hpa@zytor.com>
  */
-
-/*
- * This needs to be before all headers so that pr_debug in printk.h doesn't turn
- * printk calls into no_printk().
- *
- *#define DEBUG
- */
 #define pr_fmt(fmt) "microcode: " fmt
-
 #include <linux/earlycpio.h>
 #include <linux/firmware.h>
 #include <linux/uaccess.h>
@@ -310,59 +302,6 @@ scan_microcode(void *data, size_t size,
 	return patch;
 }
 
-static void show_saved_mc(void)
-{
-#ifdef DEBUG
-	unsigned int sig, pf, rev, total_size, data_size, date;
-	struct extended_sigtable *ext_header;
-	struct extended_signature *ext_sig;
-	struct ucode_cpu_info uci;
-	int j, ext_sigcount;
-
-	if (!intel_ucode_patch) {
-		pr_debug("no microcode data saved.\n");
-		return;
-	}
-
-	intel_cpu_collect_info(&uci);
-
-	sig	= uci.cpu_sig.sig;
-	pf	= uci.cpu_sig.pf;
-	rev	= uci.cpu_sig.rev;
-	pr_debug("CPU: sig=0x%x, pf=0x%x, rev=0x%x\n", sig, pf, rev);
-
-	sig	= intel_ucode_patch->hdr.sig;
-	pf	= intel_ucode_patch->hdr.pf;
-	rev	= intel_ucode_patch->hdr.rev;
-	date	= intel_ucode_patch->hdr.date;
-
-	total_size	= get_totalsize(mc_saved_header);
-	data_size	= intel_microcode_get_datasize(mc_saved_header);
-
-	pr_debug("mc_saved: sig=0x%x, pf=0x%x, rev=0x%x, total size=0x%x, date = %04x-%02x-%02x\n",
-		 sig, pf, rev, total_size, date & 0xffff,
-		 date >> 24, (date >> 16) & 0xff);
-
-	/* Look for ext. headers: */
-	if (total_size <= data_size + MC_HEADER_SIZE)
-		return;
-
-	ext_header = (void *)intel_ucode_patch + data_size + MC_HEADER_SIZE;
-	ext_sigcount = ext_header->count;
-	ext_sig = (void *)ext_header + EXT_HEADER_SIZE;
-
-	for (j = 0; j < ext_sigcount; j++) {
-		sig = ext_sig->sig;
-		pf = ext_sig->pf;
-
-		pr_debug("\tExtended[%d]: sig=0x%x, pf=0x%x\n",
-			 j, sig, pf);
-
-		ext_sig++;
-	}
-#endif
-}
-
 /*
  * Save this microcode patch. It will be loaded early when a CPU is
  * hot-added or resumes.
@@ -375,7 +314,6 @@ static void save_mc_for_early(struct uco
 	mutex_lock(&x86_cpu_microcode_mutex);
 
 	save_microcode_patch(mc, size);
-	show_saved_mc();
 
 	mutex_unlock(&x86_cpu_microcode_mutex);
 }
@@ -526,9 +464,6 @@ int __init save_microcode_in_initrd_inte
 	intel_cpu_collect_info(&uci);
 
 	scan_microcode(cp.data, cp.size, &uci, true);
-
-	show_saved_mc();
-
 	return 0;
 }
 


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 10/30] x86/microcode: Remove pointless mutex
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (8 preceding siblings ...)
  2023-08-10 18:37 ` [patch 09/30] x86/microcode/intel: Remove debug code Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 11/30] x86/microcode/intel: Simplify scan_microcode() Thomas Gleixner
                   ` (19 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

There is no concurreny.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/kernel/cpu/microcode/intel.c |   24 ++----------------------
 1 file changed, 2 insertions(+), 22 deletions(-)
---
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -302,22 +302,6 @@ scan_microcode(void *data, size_t size,
 	return patch;
 }
 
-/*
- * Save this microcode patch. It will be loaded early when a CPU is
- * hot-added or resumes.
- */
-static void save_mc_for_early(struct ucode_cpu_info *uci, u8 *mc, unsigned int size)
-{
-	/* Synchronization during CPU hotplug. */
-	static DEFINE_MUTEX(x86_cpu_microcode_mutex);
-
-	mutex_lock(&x86_cpu_microcode_mutex);
-
-	save_microcode_patch(mc, size);
-
-	mutex_unlock(&x86_cpu_microcode_mutex);
-}
-
 static bool load_builtin_intel_microcode(struct cpio_data *cp)
 {
 	unsigned int eax = 1, ebx, ecx = 0, edx;
@@ -718,12 +702,8 @@ static enum ucode_state generic_load_mic
 	vfree(uci->mc);
 	uci->mc = (struct microcode_intel *)new_mc;
 
-	/*
-	 * If early loading microcode is supported, save this mc into
-	 * permanent memory. So it will be loaded early when a CPU is hot added
-	 * or resumes.
-	 */
-	save_mc_for_early(uci, new_mc, new_mc_size);
+	/* Save for CPU hotplug */
+	save_microcode_patch(new_mc, new_mc_size);
 
 	pr_debug("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n",
 		 cpu, new_rev, uci->cpu_sig.rev);


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 11/30] x86/microcode/intel: Simplify scan_microcode()
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (9 preceding siblings ...)
  2023-08-10 18:37 ` [patch 10/30] x86/microcode: Remove pointless mutex Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 12/30] x86/microcode/intel: Simplify and rename generic_load_microcode() Thomas Gleixner
                   ` (18 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

Make it readable and comprehensible.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 arch/x86/kernel/cpu/microcode/intel.c |   28 +++++++---------------------
 1 file changed, 7 insertions(+), 21 deletions(-)

--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -247,22 +247,16 @@ static void save_microcode_patch(void *d
 		intel_ucode_patch = (struct microcode_intel *)p;
 }
 
-/*
- * Get microcode matching with BSP's model. Only CPUs with the same model as
- * BSP can stay in the platform.
- */
-static struct microcode_intel *
-scan_microcode(void *data, size_t size, struct ucode_cpu_info *uci, bool save)
+/* Scan CPIO for microcode matching the boot CPUs family, model, stepping */
+static struct microcode_intel *scan_microcode(void *data, size_t size,
+					      struct ucode_cpu_info *uci, bool save)
 {
 	struct microcode_header_intel *mc_header;
 	struct microcode_intel *patch = NULL;
 	u32 cur_rev = uci->cpu_sig.rev;
 	unsigned int mc_size;
 
-	while (size) {
-		if (size < sizeof(struct microcode_header_intel))
-			break;
-
+	for (; size >= sizeof(struct microcode_header_intel); size -= mc_size, data += mc_size) {
 		mc_header = (struct microcode_header_intel *)data;
 
 		mc_size = get_totalsize(mc_header);
@@ -270,27 +264,19 @@ scan_microcode(void *data, size_t size,
 		    intel_microcode_sanity_check(data, false, MC_HEADER_TYPE_MICROCODE) < 0)
 			break;
 
-		size -= mc_size;
-
-		if (!intel_find_matching_signature(data, uci->cpu_sig.sig,
-						   uci->cpu_sig.pf)) {
-			data += mc_size;
+		if (!intel_find_matching_signature(data, uci->cpu_sig.sig, uci->cpu_sig.pf))
 			continue;
-		}
 
 		/* BSP scan: Check whether there is newer microcode */
 		if (save && cur_rev >= mc_header->rev)
-			goto next;
+			continue;
 
 		/* Save scan: Check whether there is newer or matching microcode */
 		if (save && cur_rev != mc_header->rev)
-			goto next;
+			continue;
 
 		patch = data;
 		cur_rev = mc_header->rev;
-
-next:
-		data += mc_size;
 	}
 
 	if (size)


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 12/30] x86/microcode/intel: Simplify and rename generic_load_microcode()
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (10 preceding siblings ...)
  2023-08-10 18:37 ` [patch 11/30] x86/microcode/intel: Simplify scan_microcode() Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 13/30] x86/microcode/intel: Cleanup code further Thomas Gleixner
                   ` (17 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

so it becomes less obfuscated and rename it because there is nothing
generic about it.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 arch/x86/kernel/cpu/microcode/intel.c |   47 ++++++++++++----------------------
 1 file changed, 17 insertions(+), 30 deletions(-)

--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -212,19 +212,6 @@ int intel_microcode_sanity_check(void *m
 }
 EXPORT_SYMBOL_GPL(intel_microcode_sanity_check);
 
-/*
- * Returns 1 if update has been found, 0 otherwise.
- */
-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)
-		return 0;
-
-	return intel_find_matching_signature(mc, csig, cpf);
-}
-
 static void save_microcode_patch(void *data, unsigned int size)
 {
 	struct microcode_header_intel *p;
@@ -617,14 +604,12 @@ static enum ucode_state apply_microcode_
 	return ret;
 }
 
-static enum ucode_state generic_load_microcode(int cpu, struct iov_iter *iter)
+static enum ucode_state read_ucode_intel(int cpu, struct iov_iter *iter)
 {
 	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
 	unsigned int curr_mc_size = 0, new_mc_size = 0;
-	enum ucode_state ret = UCODE_OK;
-	int new_rev = uci->cpu_sig.rev;
+	int cur_rev = uci->cpu_sig.rev;
 	u8 *new_mc = NULL, *mc = NULL;
-	unsigned int csig, cpf;
 
 	while (iov_iter_count(iter)) {
 		struct microcode_header_intel mc_header;
@@ -641,6 +626,7 @@ static enum ucode_state generic_load_mic
 			pr_err("error! Bad data in microcode data file (totalsize too small)\n");
 			break;
 		}
+
 		data_size = mc_size - sizeof(mc_header);
 		if (data_size > iov_iter_count(iter)) {
 			pr_err("error! Bad data in microcode data file (truncated file?)\n");
@@ -663,16 +649,17 @@ static enum ucode_state generic_load_mic
 			break;
 		}
 
-		csig = uci->cpu_sig.sig;
-		cpf = uci->cpu_sig.pf;
-		if (has_newer_microcode(mc, csig, cpf, new_rev)) {
-			vfree(new_mc);
-			new_rev = mc_header.rev;
-			new_mc  = mc;
-			new_mc_size = mc_size;
-			mc = NULL;	/* trigger new vmalloc */
-			ret = UCODE_NEW;
-		}
+		if (cur_rev >= mc_header.rev)
+			continue;
+
+		if (!intel_find_matching_signature(mc, uci->cpu_sig.sig, uci->cpu_sig.pf))
+			continue;
+
+		vfree(new_mc);
+		cur_rev = mc_header.rev;
+		new_mc  = mc;
+		new_mc_size = mc_size;
+		mc = NULL;
 	}
 
 	vfree(mc);
@@ -692,9 +679,9 @@ static enum ucode_state generic_load_mic
 	save_microcode_patch(new_mc, new_mc_size);
 
 	pr_debug("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n",
-		 cpu, new_rev, uci->cpu_sig.rev);
+		 cpu, cur_rev, uci->cpu_sig.rev);
 
-	return ret;
+	return UCODE_NEW;
 }
 
 static bool is_blacklisted(unsigned int cpu)
@@ -743,7 +730,7 @@ static enum ucode_state request_microcod
 	kvec.iov_base = (void *)firmware->data;
 	kvec.iov_len = firmware->size;
 	iov_iter_kvec(&iter, ITER_SOURCE, &kvec, 1, firmware->size);
-	ret = generic_load_microcode(cpu, &iter);
+	ret = read_ucode_intel(cpu, &iter);
 
 	release_firmware(firmware);
 


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 13/30] x86/microcode/intel: Cleanup code further
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (11 preceding siblings ...)
  2023-08-10 18:37 ` [patch 12/30] x86/microcode/intel: Simplify and rename generic_load_microcode() Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-11 11:01   ` Nikolay Borisov
  2023-08-10 18:37 ` [patch 14/30] x86/microcode/intel: Simplify early loading Thomas Gleixner
                   ` (16 subsequent siblings)
  29 siblings, 1 reply; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

Sanitize the microcode scan loop, fixup printks and move the initrd loading
function next to the place where it is used and mark it __init. Rename
generic_load_microcode() as that Intel specific function is not generic at
all.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/kernel/cpu/microcode/intel.c |   82 +++++++++++++---------------------
 1 file changed, 33 insertions(+), 49 deletions(-)
---
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -36,7 +36,7 @@ static const char ucode_path[] = "kernel
 static struct microcode_intel *intel_ucode_patch __read_mostly;
 
 /* last level cache size per core */
-static int llc_size_per_core __ro_after_init;
+static unsigned int llc_size_per_core __ro_after_init;
 
 int intel_cpu_collect_info(struct ucode_cpu_info *uci)
 {
@@ -275,37 +275,10 @@ static struct microcode_intel *scan_micr
 	return patch;
 }
 
-static bool load_builtin_intel_microcode(struct cpio_data *cp)
-{
-	unsigned int eax = 1, ebx, ecx = 0, edx;
-	struct firmware fw;
-	char name[30];
-
-	if (IS_ENABLED(CONFIG_X86_32))
-		return false;
-
-	native_cpuid(&eax, &ebx, &ecx, &edx);
-
-	sprintf(name, "intel-ucode/%02x-%02x-%02x",
-		      x86_family(eax), x86_model(eax), x86_stepping(eax));
-
-	if (firmware_request_builtin(&fw, name)) {
-		cp->size = fw.size;
-		cp->data = (void *)fw.data;
-		return true;
-	}
-
-	return false;
-}
-
 static void print_ucode_info(int old_rev, int new_rev, unsigned int date)
 {
 	pr_info_once("updated early: 0x%x -> 0x%x, date = %04x-%02x-%02x\n",
-		     old_rev,
-		     new_rev,
-		     date & 0xffff,
-		     date >> 24,
-		     (date >> 16) & 0xff);
+		     old_rev, new_rev, date & 0xffff, date >> 24, (date >> 16) & 0xff);
 }
 
 #ifdef CONFIG_X86_32
@@ -399,6 +372,28 @@ static int apply_microcode_early(struct
 	return 0;
 }
 
+static bool load_builtin_intel_microcode(struct cpio_data *cp)
+{
+	unsigned int eax = 1, ebx, ecx = 0, edx;
+	struct firmware fw;
+	char name[30];
+
+	if (IS_ENABLED(CONFIG_X86_32))
+		return false;
+
+	native_cpuid(&eax, &ebx, &ecx, &edx);
+
+	sprintf(name, "intel-ucode/%02x-%02x-%02x",
+		x86_family(eax), x86_model(eax), x86_stepping(eax));
+
+	if (firmware_request_builtin(&fw, name)) {
+		cp->size = fw.size;
+		cp->data = (void *)fw.data;
+		return true;
+	}
+	return false;
+}
+
 int __init save_microcode_in_initrd_intel(void)
 {
 	struct ucode_cpu_info uci;
@@ -490,25 +485,16 @@ void load_ucode_intel_ap(void)
 	apply_microcode_early(&uci, true);
 }
 
-/* Accessor for microcode pointer */
-static struct microcode_intel *ucode_get_patch(void)
-{
-	return intel_ucode_patch;
-}
-
 void reload_ucode_intel(void)
 {
-	struct microcode_intel *p;
 	struct ucode_cpu_info uci;
 
 	intel_cpu_collect_info(&uci);
 
-	p = ucode_get_patch();
-	if (!p)
+	uci.mc = intel_ucode_patch;
+	if (!uci.mc)
 		return;
 
-	uci.mc = p;
-
 	apply_microcode_early(&uci, false);
 }
 
@@ -546,8 +532,7 @@ static enum ucode_state apply_microcode_
 	if (WARN_ON(raw_smp_processor_id() != cpu))
 		return UCODE_ERROR;
 
-	/* Look for a newer patch in our cache: */
-	mc = ucode_get_patch();
+	mc = intel_ucode_patch;
 	if (!mc) {
 		mc = uci->mc;
 		if (!mc)
@@ -738,18 +723,17 @@ static enum ucode_state request_microcod
 }
 
 static struct microcode_ops microcode_intel_ops = {
-	.request_microcode_fw             = request_microcode_fw,
-	.collect_cpu_info                 = collect_cpu_info,
-	.apply_microcode                  = apply_microcode_intel,
+	.request_microcode_fw	= request_microcode_fw,
+	.collect_cpu_info	= collect_cpu_info,
+	.apply_microcode	= apply_microcode_intel,
 };
 
-static int __init calc_llc_size_per_core(struct cpuinfo_x86 *c)
+static __init void calc_llc_size_per_core(struct cpuinfo_x86 *c)
 {
 	u64 llc_size = c->x86_cache_size * 1024ULL;
 
 	do_div(llc_size, c->x86_max_cores);
-
-	return (int)llc_size;
+	llc_size_per_core = (unsigned int)llc_size;
 }
 
 struct microcode_ops * __init init_intel_microcode(void)
@@ -762,7 +746,7 @@ struct microcode_ops * __init init_intel
 		return NULL;
 	}
 
-	llc_size_per_core = calc_llc_size_per_core(c);
+	calc_llc_size_per_core(c);
 
 	return &microcode_intel_ops;
 }


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 14/30] x86/microcode/intel: Simplify early loading
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (12 preceding siblings ...)
  2023-08-10 18:37 ` [patch 13/30] x86/microcode/intel: Cleanup code further Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 15/30] x86/microcode/intel: Save the microcode only after a successful late-load Thomas Gleixner
                   ` (15 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

The early loading code is overly complicated:

  - It scans the builtin/initrd for microcode not only on the BSP, but also
    on all APs during early boot and then later in the boot process it
    scans again to duplicate and save the microcode before initrd goes away.

    That's a pointless exercise because this can be simply done before
    bringing up the APs when the memory allocator is up and running.

 - Saving the microcode from within the scan loop is completely
   non-obvious and a left over of the microcode cache.

   This can be done at the call site now which makes it obvious.

Rework the code so that only the BSP scans the builtin/initrd microcode
once during early boot and save it away in an early initcall for later
use.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/kernel/cpu/microcode/core.c     |    4 
 arch/x86/kernel/cpu/microcode/intel.c    |  191 +++++++++++++------------------
 arch/x86/kernel/cpu/microcode/internal.h |    2 
 3 files changed, 86 insertions(+), 111 deletions(-)
---
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -207,10 +207,6 @@ static int __init save_microcode_in_init
 	int ret = -EINVAL;
 
 	switch (c->x86_vendor) {
-	case X86_VENDOR_INTEL:
-		if (c->x86 >= 6)
-			ret = save_microcode_in_initrd_intel();
-		break;
 	case X86_VENDOR_AMD:
 		if (c->x86 >= 0x10)
 			ret = save_microcode_in_initrd_amd(cpuid_eax(1));
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -33,7 +33,7 @@
 static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin";
 
 /* Current microcode patch used in early patching on the APs. */
-static struct microcode_intel *intel_ucode_patch __read_mostly;
+static struct microcode_intel *ucode_patch_va __read_mostly;
 
 /* last level cache size per core */
 static unsigned int llc_size_per_core __ro_after_init;
@@ -212,31 +212,34 @@ int intel_microcode_sanity_check(void *m
 }
 EXPORT_SYMBOL_GPL(intel_microcode_sanity_check);
 
-static void save_microcode_patch(void *data, unsigned int size)
+static void update_ucode_pointer(struct microcode_intel *mc)
 {
-	struct microcode_header_intel *p;
-
-	kfree(intel_ucode_patch);
-	intel_ucode_patch = NULL;
-
-	p = kmemdup(data, size, GFP_KERNEL);
-	if (!p)
-		return;
+	kfree(ucode_patch_va);
 
 	/*
-	 * Save for early loading. On 32-bit, that needs to be a physical
-	 * address as the APs are running from physical addresses, before
-	 * paging has been enabled.
+	 * Save the virtual address for early loading on 64bit
+	 * and for eventual free on late loading.
+	 *
+	 * On 32-bit, that needs to store the physical address too as the
+	 * APs are loading before paging has been enabled.
 	 */
-	if (IS_ENABLED(CONFIG_X86_32))
-		intel_ucode_patch = (struct microcode_intel *)__pa_nodebug(p);
+	ucode_patch_va = mc;
+}
+
+static void save_microcode_patch(struct microcode_intel *patch)
+{
+	struct microcode_intel *mc;
+
+	mc = kmemdup(patch, get_totalsize(&patch->hdr), GFP_KERNEL);
+	if (mc)
+		update_ucode_pointer(mc);
 	else
-		intel_ucode_patch = (struct microcode_intel *)p;
+		pr_err("Unable to allocate microcode memory\n");
 }
 
 /* Scan CPIO for microcode matching the boot CPUs family, model, stepping */
-static struct microcode_intel *scan_microcode(void *data, size_t size,
-					      struct ucode_cpu_info *uci, bool save)
+static __init struct microcode_intel *scan_microcode(void *data, size_t size,
+						     struct ucode_cpu_info *uci)
 {
 	struct microcode_header_intel *mc_header;
 	struct microcode_intel *patch = NULL;
@@ -254,25 +257,15 @@ static struct microcode_intel *scan_micr
 		if (!intel_find_matching_signature(data, uci->cpu_sig.sig, uci->cpu_sig.pf))
 			continue;
 
-		/* BSP scan: Check whether there is newer microcode */
-		if (save && cur_rev >= mc_header->rev)
-			continue;
-
-		/* Save scan: Check whether there is newer or matching microcode */
-		if (save && cur_rev != mc_header->rev)
+		/* Check whether there is newer microcode */
+		if (cur_rev >= mc_header->rev)
 			continue;
 
 		patch = data;
 		cur_rev = mc_header->rev;
 	}
 
-	if (size)
-		return NULL;
-
-	if (save && patch)
-		save_microcode_patch(patch, mc_size);
-
-	return patch;
+	return size ? NULL : patch;
 }
 
 static void print_ucode_info(int old_rev, int new_rev, unsigned int date)
@@ -327,14 +320,14 @@ static inline void print_ucode(int old_r
 }
 #endif
 
-static int apply_microcode_early(struct ucode_cpu_info *uci, bool early)
+static enum ucode_state apply_microcode_early(struct ucode_cpu_info *uci, bool early)
 {
 	struct microcode_intel *mc;
 	u32 rev, old_rev;
 
 	mc = uci->mc;
 	if (!mc)
-		return 0;
+		return UCODE_NFOUND;
 
 	/*
 	 * Save us the MSR write below - which is a particular expensive
@@ -360,7 +353,7 @@ static int apply_microcode_early(struct
 
 	rev = intel_get_microcode_revision();
 	if (rev != mc->hdr.rev)
-		return -1;
+		return UCODE_ERROR;
 
 	uci->cpu_sig.rev = rev;
 
@@ -369,10 +362,10 @@ static int apply_microcode_early(struct
 	else
 		print_ucode_info(old_rev, uci->cpu_sig.rev, mc->hdr.date);
 
-	return 0;
+	return UCODE_UPDATED;
 }
 
-static bool load_builtin_intel_microcode(struct cpio_data *cp)
+static __init bool load_builtin_intel_microcode(struct cpio_data *cp)
 {
 	unsigned int eax = 1, ebx, ecx = 0, edx;
 	struct firmware fw;
@@ -394,108 +387,96 @@ static bool load_builtin_intel_microcode
 	return false;
 }
 
-int __init save_microcode_in_initrd_intel(void)
+static __init struct microcode_intel *get_ucode_from_cpio(struct ucode_cpu_info *uci)
 {
-	struct ucode_cpu_info uci;
+	bool use_pa = IS_ENABLED(CONFIG_X86_32);
+	const char *path = ucode_path;
 	struct cpio_data cp;
 
-	/*
-	 * initrd is going away, clear patch ptr. We will scan the microcode one
-	 * last time before jettisoning and save a patch, if found. Then we will
-	 * update that pointer too, with a stable patch address to use when
-	 * resuming the cores.
-	 */
-	intel_ucode_patch = NULL;
+	/* Paging is not yet enabled on 32bit! */
+	if (IS_ENABLED(CONFIG_X86_32))
+		path = (const char *)__pa_nodebug(ucode_path);
 
+	/* Try built-in microcode first */
 	if (!load_builtin_intel_microcode(&cp))
-		cp = find_microcode_in_initrd(ucode_path, false);
+		cp = find_microcode_in_initrd(path, use_pa);
 
 	if (!(cp.data && cp.size))
-		return 0;
+		return NULL;
 
-	intel_cpu_collect_info(&uci);
+	intel_cpu_collect_info(uci);
 
-	scan_microcode(cp.data, cp.size, &uci, true);
-	return 0;
+	return scan_microcode(cp.data, cp.size, uci);
 }
 
+static struct microcode_intel *ucode_early_pa __initdata;
+
 /*
- * @res_patch, output: a pointer to the patch we found.
+ * Invoked from an early init call to save the microcode blob which was
+ * selected during early boot when mm was not usable. The microcode must be
+ * saved because initrd is going away. It's an early init call so the APs
+ * just can use the pointer and do not have to scan initrd/builtin firmware
+ * again.
  */
-static struct microcode_intel *__load_ucode_intel(struct ucode_cpu_info *uci)
+static int __init save_microcode_from_cpio(void)
 {
-	static const char *path;
-	struct cpio_data cp;
-	bool use_pa;
-
-	if (IS_ENABLED(CONFIG_X86_32)) {
-		path	  = (const char *)__pa_nodebug(ucode_path);
-		use_pa	  = true;
-	} else {
-		path	  = ucode_path;
-		use_pa	  = false;
-	}
-
-	/* try built-in microcode first */
-	if (!load_builtin_intel_microcode(&cp))
-		cp = find_microcode_in_initrd(path, use_pa);
-
-	if (!(cp.data && cp.size))
-		return NULL;
+	struct microcode_intel *mc;
 
-	intel_cpu_collect_info(uci);
+	if (!ucode_early_pa)
+		return 0;
 
-	return scan_microcode(cp.data, cp.size, uci, false);
+	mc = __va((void *)ucode_early_pa);
+	save_microcode_patch(mc);
+	return 0;
 }
+early_initcall(save_microcode_from_cpio);
 
+/* Load microcode on BSP from CPIO */
 void __init load_ucode_intel_bsp(void)
 {
-	struct microcode_intel *patch;
 	struct ucode_cpu_info uci;
 
-	patch = __load_ucode_intel(&uci);
-	if (!patch)
+	uci.mc = get_ucode_from_cpio(&uci);
+	if (!uci.mc)
 		return;
 
-	uci.mc = patch;
+	if (apply_microcode_early(&uci, true) != UCODE_UPDATED)
+		return;
 
-	apply_microcode_early(&uci, true);
+	if (IS_ENABLED(CONFIG_X86_64)) {
+		/* Store the physical address as KASLR happens after this. */
+		ucode_early_pa = (struct microcode_intel *)__pa_nodebug(uci.mc);
+	} else {
+		struct microcode_intel **uce;
+
+		/* Physical address pointer required for 32-bit */
+		uce = (struct microcode_intel **)__pa_nodebug(&ucode_early_pa);
+		/* uci.mc is the physical address of the microcode blob */
+		*uce = uci.mc;
+	}
 }
 
+/* Load microcode on AP bringup */
 void load_ucode_intel_ap(void)
 {
-	struct microcode_intel *patch, **iup;
 	struct ucode_cpu_info uci;
 
+	/* Must use physical address on 32bit as paging is not yet enabled */
+	uci.mc = ucode_patch_va;
 	if (IS_ENABLED(CONFIG_X86_32))
-		iup = (struct microcode_intel **) __pa_nodebug(&intel_ucode_patch);
-	else
-		iup = &intel_ucode_patch;
-
-	if (!*iup) {
-		patch = __load_ucode_intel(&uci);
-		if (!patch)
-			return;
-
-		*iup = patch;
-	}
-
-	uci.mc = *iup;
+		uci.mc = (struct microcode_intel *)__pa_nodebug(uci.mc);
 
-	apply_microcode_early(&uci, true);
+	if (uci.mc)
+		apply_microcode_early(&uci, true);
 }
 
+/* Reload microcode on resume */
 void reload_ucode_intel(void)
 {
-	struct ucode_cpu_info uci;
-
-	intel_cpu_collect_info(&uci);
+	struct ucode_cpu_info uci = { .mc = ucode_patch_va, };
 
-	uci.mc = intel_ucode_patch;
-	if (!uci.mc)
-		return;
-
-	apply_microcode_early(&uci, false);
+	if (uci.mc)
+		apply_microcode_early(&uci, false);
 }
 
 static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
@@ -532,7 +513,7 @@ static enum ucode_state apply_microcode_
 	if (WARN_ON(raw_smp_processor_id() != cpu))
 		return UCODE_ERROR;
 
-	mc = intel_ucode_patch;
+	mc = ucode_patch_va;
 	if (!mc) {
 		mc = uci->mc;
 		if (!mc)
@@ -657,11 +638,11 @@ static enum ucode_state read_ucode_intel
 	if (!new_mc)
 		return UCODE_NFOUND;
 
-	vfree(uci->mc);
-	uci->mc = (struct microcode_intel *)new_mc;
-
 	/* Save for CPU hotplug */
-	save_microcode_patch(new_mc, new_mc_size);
+	save_microcode_patch((struct microcode_intel *)new_mc);
+	uci->mc = ucode_patch_va;
+
+	vfree(new_mc);
 
 	pr_debug("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n",
 		 cpu, cur_rev, uci->cpu_sig.rev);
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -172,13 +172,11 @@ static inline int get_totalsize(void *mc
 
 void load_ucode_intel_bsp(void);
 void load_ucode_intel_ap(void);
-int save_microcode_in_initrd_intel(void);
 void reload_ucode_intel(void);
 struct microcode_ops *init_intel_microcode(void);
 #else /* CONFIG_CPU_SUP_INTEL */
 static inline void load_ucode_intel_bsp(void) { }
 static inline void load_ucode_intel_ap(void) { }
-static inline int save_microcode_in_initrd_intel(void) { return -EINVAL; }
 static inline void reload_ucode_intel(void) { }
 static inline struct microcode_ops *init_intel_microcode(void) { return NULL; }
 #endif  /* !CONFIG_CPU_SUP_INTEL */


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 15/30] x86/microcode/intel: Save the microcode only after a successful late-load
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (13 preceding siblings ...)
  2023-08-10 18:37 ` [patch 14/30] x86/microcode/intel: Simplify early loading Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 16/30] x86/microcode/intel: Switch to kvmalloc() Thomas Gleixner
                   ` (14 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

There are situations where the late microcode is loaded into memory, but is
not applied:

  1) The rendevouz fails
  2) The microcode is rejected by the CPUs

If any of this happens then the pointer which was updated at firmware load
time is stale and subsequent CPU hotplug operations either fail to update
or create inconsistent microcode state.

Save the loaded microcode in a separate pointer from with the late load is
attempted and when successful, update the hotplug pointer accordingly via a
new micrcode_ops callback.

Remove the pointless fallback in the loader to a microcode pointer which is
never populated.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/kernel/cpu/microcode/core.c     |    4 ++++
 arch/x86/kernel/cpu/microcode/intel.c    |   30 +++++++++++++++---------------
 arch/x86/kernel/cpu/microcode/internal.h |    1 +
 3 files changed, 20 insertions(+), 15 deletions(-)
---
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -443,6 +443,10 @@ static int microcode_reload_late(void)
 	store_cpu_caps(&prev_info);
 
 	ret = stop_machine_cpuslocked(__reload_late, NULL, cpu_online_mask);
+
+	if (microcode_ops->finalize_late_load)
+		microcode_ops->finalize_late_load(ret);
+
 	if (!ret) {
 		pr_info("Reload succeeded, microcode revision: 0x%x -> 0x%x\n",
 			old, boot_cpu_data.microcode);
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -34,6 +34,7 @@ static const char ucode_path[] = "kernel
 
 /* Current microcode patch used in early patching on the APs. */
 static struct microcode_intel *ucode_patch_va __read_mostly;
+static struct microcode_intel *ucode_patch_late __read_mostly;
 
 /* last level cache size per core */
 static unsigned int llc_size_per_core __ro_after_init;
@@ -517,12 +518,9 @@ static enum ucode_state apply_microcode_
 	if (WARN_ON(raw_smp_processor_id() != cpu))
 		return UCODE_ERROR;
 
-	mc = ucode_patch_va;
-	if (!mc) {
-		mc = uci->mc;
-		if (!mc)
-			return UCODE_NFOUND;
-	}
+	mc = ucode_patch_late;
+	if (!mc)
+		return UCODE_NFOUND;
 
 	/*
 	 * Save us the MSR write below - which is a particular expensive
@@ -642,15 +640,7 @@ static enum ucode_state read_ucode_intel
 	if (!new_mc)
 		return UCODE_NFOUND;
 
-	/* Save for CPU hotplug */
-	save_microcode_patch((struct microcode_intel *)new_mc);
-	uci->mc = ucode_patch_va;
-
-	vfree(new_mc);
-
-	pr_debug("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n",
-		 cpu, cur_rev, uci->cpu_sig.rev);
-
+	ucode_patch_late = (struct microcode_intel *)new_mc;
 	return UCODE_NEW;
 }
 
@@ -707,10 +697,20 @@ static enum ucode_state request_microcod
 	return ret;
 }
 
+static void finalize_late_load(int result)
+{
+	if (!result)
+		save_microcode_patch(ucode_patch_late);
+
+	vfree(ucode_patch_late);
+	ucode_patch_late = NULL;
+}
+
 static struct microcode_ops microcode_intel_ops = {
 	.request_microcode_fw	= request_microcode_fw,
 	.collect_cpu_info	= collect_cpu_info,
 	.apply_microcode	= apply_microcode_intel,
+	.finalize_late_load	= finalize_late_load,
 };
 
 static __init void calc_llc_size_per_core(struct cpuinfo_x86 *c)
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -31,6 +31,7 @@ struct microcode_ops {
 	 */
 	enum ucode_state (*apply_microcode)(int cpu);
 	int (*collect_cpu_info)(int cpu, struct cpu_signature *csig);
+	void (*finalize_late_load)(int result);
 };
 
 extern struct ucode_cpu_info ucode_cpu_info[];


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 16/30] x86/microcode/intel: Switch to kvmalloc()
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (14 preceding siblings ...)
  2023-08-10 18:37 ` [patch 15/30] x86/microcode/intel: Save the microcode only after a successful late-load Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 17/30] x86/microcode/intel: Unify microcode apply() functions Thomas Gleixner
                   ` (13 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

Microcode blobs are getting larger and might soon reach the kmalloc()
limit. Switch over kvmalloc().

32-bit has to stay with kmalloc() as it needs physically contiguous memory
because the early loading runs before paging is enabled, so there is a
sanity check added to ensure that.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/kernel/cpu/microcode/intel.c |   55 +++++++++++++++++++---------------
 1 file changed, 32 insertions(+), 23 deletions(-)
---
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -14,7 +14,6 @@
 #include <linux/earlycpio.h>
 #include <linux/firmware.h>
 #include <linux/uaccess.h>
-#include <linux/vmalloc.h>
 #include <linux/initrd.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
@@ -215,7 +214,7 @@ EXPORT_SYMBOL_GPL(intel_microcode_sanity
 
 static void update_ucode_pointer(struct microcode_intel *mc)
 {
-	kfree(ucode_patch_va);
+	kvfree(ucode_patch_va);
 
 	/*
 	 * Save the virtual address for early loading on 64bit
@@ -229,13 +228,18 @@ static void update_ucode_pointer(struct
 
 static void save_microcode_patch(struct microcode_intel *patch)
 {
-	struct microcode_intel *mc;
+	unsigned int size = get_totalsize(&patch->hdr);
+	struct microcode_intel *mc = NULL;
+
+	if (IS_ENABLED(CONFIG_X86_64))
+		mc = kvmemdup(patch, size, GFP_KERNEL);
+	else
+		mc = kmemdup(patch, size, GFP_KERNEL);
 
-	mc = kmemdup(patch, get_totalsize(&patch->hdr), GFP_KERNEL);
 	if (mc)
 		update_ucode_pointer(mc);
 	else
-		pr_err("Unable to allocate microcode memory\n");
+		pr_err("Unable to allocate microcode memory size: %u\n", size);
 }
 
 /* Scan CPIO for microcode matching the boot CPUs family, model, stepping */
@@ -586,36 +590,34 @@ static enum ucode_state read_ucode_intel
 
 		if (!copy_from_iter_full(&mc_header, sizeof(mc_header), iter)) {
 			pr_err("error! Truncated or inaccessible header in microcode data file\n");
-			break;
+			goto fail;
 		}
 
 		mc_size = get_totalsize(&mc_header);
 		if (mc_size < sizeof(mc_header)) {
 			pr_err("error! Bad data in microcode data file (totalsize too small)\n");
-			break;
+			goto fail;
 		}
-
 		data_size = mc_size - sizeof(mc_header);
 		if (data_size > iov_iter_count(iter)) {
 			pr_err("error! Bad data in microcode data file (truncated file?)\n");
-			break;
+			goto fail;
 		}
 
 		/* For performance reasons, reuse mc area when possible */
 		if (!mc || mc_size > curr_mc_size) {
-			vfree(mc);
-			mc = vmalloc(mc_size);
+			kvfree(mc);
+			mc = kvmalloc(mc_size, GFP_KERNEL);
 			if (!mc)
-				break;
+				goto fail;
 			curr_mc_size = mc_size;
 		}
 
 		memcpy(mc, &mc_header, sizeof(mc_header));
 		data = mc + sizeof(mc_header);
 		if (!copy_from_iter_full(data, data_size, iter) ||
-		    intel_microcode_sanity_check(mc, true, MC_HEADER_TYPE_MICROCODE) < 0) {
-			break;
-		}
+		    intel_microcode_sanity_check(mc, true, MC_HEADER_TYPE_MICROCODE) < 0)
+			goto fail;
 
 		if (cur_rev >= mc_header.rev)
 			continue;
@@ -623,25 +625,32 @@ static enum ucode_state read_ucode_intel
 		if (!intel_find_matching_signature(mc, uci->cpu_sig.sig, uci->cpu_sig.pf))
 			continue;
 
-		vfree(new_mc);
+		kvfree(new_mc);
 		cur_rev = mc_header.rev;
 		new_mc  = mc;
 		new_mc_size = mc_size;
 		mc = NULL;
 	}
 
-	vfree(mc);
+	if (iov_iter_count(iter))
+		goto fail;
 
-	if (iov_iter_count(iter)) {
-		vfree(new_mc);
-		return UCODE_ERROR;
+	if (IS_ENABLED(CONFIG_X86_32) && new_mc && is_vmalloc_addr(new_mc)) {
+		pr_err("Microcode too large for 32-bit mode\n");
+		goto fail;
 	}
 
+	kvfree(mc);
 	if (!new_mc)
 		return UCODE_NFOUND;
 
 	ucode_patch_late = (struct microcode_intel *)new_mc;
 	return UCODE_NEW;
+
+fail:
+	kvfree(mc);
+	kvfree(new_mc);
+	return UCODE_ERROR;
 }
 
 static bool is_blacklisted(unsigned int cpu)
@@ -700,9 +709,9 @@ static enum ucode_state request_microcod
 static void finalize_late_load(int result)
 {
 	if (!result)
-		save_microcode_patch(ucode_patch_late);
-
-	vfree(ucode_patch_late);
+		update_ucode_pointer(ucode_patch_late);
+	else
+		kvfree(ucode_patch_late);
 	ucode_patch_late = NULL;
 }
 


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 17/30] x86/microcode/intel: Unify microcode apply() functions
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (15 preceding siblings ...)
  2023-08-10 18:37 ` [patch 16/30] x86/microcode/intel: Switch to kvmalloc() Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 18/30] x86/microcode: Handle "nosmt" correctly Thomas Gleixner
                   ` (12 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

Deduplicate the early and late apply() functions.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 arch/x86/kernel/cpu/microcode/intel.c |  106 +++++++++++-----------------------
 1 file changed, 36 insertions(+), 70 deletions(-)

--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -325,12 +325,11 @@ static inline void print_ucode(int old_r
 }
 #endif
 
-static enum ucode_state apply_microcode_early(struct ucode_cpu_info *uci, bool early)
+static enum ucode_state apply_microcode(struct ucode_cpu_info *uci, struct microcode_intel *mc,
+					u32 *cur_rev)
 {
-	struct microcode_intel *mc;
-	u32 rev, old_rev;
+	u32 rev;
 
-	mc = uci->mc;
 	if (!mc)
 		return UCODE_NFOUND;
 
@@ -339,14 +338,12 @@ static enum ucode_state apply_microcode_
 	 * operation - when the other hyperthread has updated the microcode
 	 * already.
 	 */
-	rev = intel_get_microcode_revision();
-	if (rev >= mc->hdr.rev) {
-		uci->cpu_sig.rev = rev;
+	*cur_rev = intel_get_microcode_revision();
+	if (*cur_rev >= mc->hdr.rev) {
+		uci->cpu_sig.rev = *cur_rev;
 		return UCODE_OK;
 	}
 
-	old_rev = rev;
-
 	/*
 	 * Writeback and invalidate caches before updating microcode to avoid
 	 * internal issues depending on what the microcode is updating.
@@ -361,13 +358,23 @@ static enum ucode_state apply_microcode_
 		return UCODE_ERROR;
 
 	uci->cpu_sig.rev = rev;
+	return UCODE_UPDATED;
+}
 
-	if (early)
-		print_ucode(old_rev, uci->cpu_sig.rev, mc->hdr.date);
-	else
-		print_ucode_info(old_rev, uci->cpu_sig.rev, mc->hdr.date);
+static enum ucode_state apply_microcode_early(struct ucode_cpu_info *uci, bool early)
+{
+	struct microcode_intel *mc = uci->mc;
+	enum ucode_state ret;
+	u32 cur_rev;
 
-	return UCODE_UPDATED;
+	ret = apply_microcode(uci, mc, &cur_rev);
+	if (ret == UCODE_UPDATED) {
+		if (early)
+			print_ucode(cur_rev, uci->cpu_sig.rev, mc->hdr.date);
+		else
+			print_ucode_info(cur_rev, uci->cpu_sig.rev, mc->hdr.date);
+	}
+	return ret;
 }
 
 static __init bool load_builtin_intel_microcode(struct cpio_data *cp)
@@ -508,70 +515,29 @@ static int collect_cpu_info(int cpu_num,
 	return 0;
 }
 
-static enum ucode_state apply_microcode_intel(int cpu)
+static enum ucode_state apply_microcode_late(int cpu)
 {
 	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
-	struct cpuinfo_x86 *c = &cpu_data(cpu);
-	bool bsp = c->cpu_index == boot_cpu_data.cpu_index;
-	struct microcode_intel *mc;
+	struct microcode_intel *mc = ucode_patch_late;
 	enum ucode_state ret;
-	static int prev_rev;
-	u32 rev;
-
-	/* We should bind the task to the CPU */
-	if (WARN_ON(raw_smp_processor_id() != cpu))
-		return UCODE_ERROR;
-
-	mc = ucode_patch_late;
-	if (!mc)
-		return UCODE_NFOUND;
+	u32 cur_rev;
 
-	/*
-	 * Save us the MSR write below - which is a particular expensive
-	 * operation - when the other hyperthread has updated the microcode
-	 * already.
-	 */
-	rev = intel_get_microcode_revision();
-	if (rev >= mc->hdr.rev) {
-		ret = UCODE_OK;
-		goto out;
-	}
-
-	/*
-	 * Writeback and invalidate caches before updating microcode to avoid
-	 * internal issues depending on what the microcode is updating.
-	 */
-	native_wbinvd();
-
-	/* write microcode via MSR 0x79 */
-	wrmsrl(MSR_IA32_UCODE_WRITE, (unsigned long)mc->bits);
-
-	rev = intel_get_microcode_revision();
-
-	if (rev != mc->hdr.rev) {
-		pr_err("CPU%d update to revision 0x%x failed\n",
-		       cpu, mc->hdr.rev);
+	if (WARN_ON_ONCE(smp_processor_id() != cpu))
 		return UCODE_ERROR;
-	}
 
-	if (bsp && rev != prev_rev) {
-		pr_info("updated to revision 0x%x, date = %04x-%02x-%02x\n",
-			rev,
-			mc->hdr.date & 0xffff,
-			mc->hdr.date >> 24,
+	ret = apply_microcode(uci, mc, &cur_rev);
+	if (ret != UCODE_UPDATED && ret != UCODE_OK)
+		return ret;
+
+	if (!cpu && uci->cpu_sig.rev != cur_rev) {
+		pr_info("Updated to revision 0x%x, date = %04x-%02x-%02x\n",
+			uci->cpu_sig.rev, mc->hdr.date & 0xffff, mc->hdr.date >> 24,
 			(mc->hdr.date >> 16) & 0xff);
-		prev_rev = rev;
 	}
 
-	ret = UCODE_UPDATED;
-
-out:
-	uci->cpu_sig.rev = rev;
-	c->microcode	 = rev;
-
-	/* Update boot_cpu_data's revision too, if we're on the BSP: */
-	if (bsp)
-		boot_cpu_data.microcode = rev;
+	cpu_data(cpu).microcode	 = uci->cpu_sig.rev;
+	if (!cpu)
+		boot_cpu_data.microcode = uci->cpu_sig.rev;
 
 	return ret;
 }
@@ -718,7 +684,7 @@ static void finalize_late_load(int resul
 static struct microcode_ops microcode_intel_ops = {
 	.request_microcode_fw	= request_microcode_fw,
 	.collect_cpu_info	= collect_cpu_info,
-	.apply_microcode	= apply_microcode_intel,
+	.apply_microcode	= apply_microcode_late,
 	.finalize_late_load	= finalize_late_load,
 };
 


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 18/30] x86/microcode: Handle "nosmt" correctly
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (16 preceding siblings ...)
  2023-08-10 18:37 ` [patch 17/30] x86/microcode/intel: Unify microcode apply() functions Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 19/30] x86/microcode: Clarify the late load logic Thomas Gleixner
                   ` (11 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

On CPUs where microcode loading is not NMI safe the SMT sibling which is
parked in one of the play_dead() variants, these parked CPUs still react
on NMIs. So if a NMI hits while the primary thread updates the microcode
the resulting behaviour is undefined. The default play_dead()
implementation on modern CPUs is using MWAIT, which is not guaranteed to
be safe against an microcode update which affects MWAIT.

Take the cpus_booted_once_mask into account to detect this case and refuse
to load late if the vendor specific driver does not advertise that late
loading is NMI safe.

AMD stated that this is safe, so mark the AMD driver accordingly.

This requirement will be partially lifted in later changes.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/Kconfig                         |    2 -
 arch/x86/kernel/cpu/microcode/amd.c      |    9 +++--
 arch/x86/kernel/cpu/microcode/core.c     |   51 +++++++++++++++++++------------
 arch/x86/kernel/cpu/microcode/internal.h |   13 +++----
 4 files changed, 44 insertions(+), 31 deletions(-)
---
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1314,7 +1314,7 @@ config MICROCODE
 config MICROCODE_LATE_LOADING
 	bool "Late microcode loading (DANGEROUS)"
 	default n
-	depends on MICROCODE
+	depends on MICROCODE && SMP
 	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
--- a/arch/x86/kernel/cpu/microcode/amd.c
+++ b/arch/x86/kernel/cpu/microcode/amd.c
@@ -909,10 +909,11 @@ static void microcode_fini_cpu_amd(int c
 }
 
 static struct microcode_ops microcode_amd_ops = {
-	.request_microcode_fw             = request_microcode_amd,
-	.collect_cpu_info                 = collect_cpu_info_amd,
-	.apply_microcode                  = apply_microcode_amd,
-	.microcode_fini_cpu               = microcode_fini_cpu_amd,
+	.request_microcode_fw	= request_microcode_amd,
+	.collect_cpu_info	= collect_cpu_info_amd,
+	.apply_microcode	= apply_microcode_amd,
+	.microcode_fini_cpu	= microcode_fini_cpu_amd,
+	.nmi_safe		= true,
 };
 
 struct microcode_ops * __init init_amd_microcode(void)
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -326,23 +326,6 @@ static struct platform_device	*microcode
  */
 #define SPINUNIT 100 /* 100 nsec */
 
-static int check_online_cpus(void)
-{
-	unsigned int cpu;
-
-	/*
-	 * Make sure all CPUs are online.  It's fine for SMT to be disabled if
-	 * all the primary threads are still online.
-	 */
-	for_each_present_cpu(cpu) {
-		if (topology_is_primary_thread(cpu) && !cpu_online(cpu)) {
-			pr_err("Not all CPUs online, aborting microcode update.\n");
-			return -EINVAL;
-		}
-	}
-
-	return 0;
-}
 
 static atomic_t late_cpus_in;
 static atomic_t late_cpus_out;
@@ -459,6 +442,35 @@ static int microcode_reload_late(void)
 	return ret;
 }
 
+/*
+ *  Ensure that all required CPUs which are present and have been booted
+ *  once are online.
+ *
+ *    To pass this check, all primary threads must be online.
+ *
+ *    If the microcode load is not safe against NMI then all SMT threads
+ *    must be online as well because they still react on NMI when they are
+ *    soft-offlined and parked in one of the play_dead() variants. So if a
+ *    NMI hits while the primary thread updates the microcode the resulting
+ *    behaviour is undefined. The default play_dead() implementation on
+ *    modern CPUs is using MWAIT, which is also not guaranteed to be safe
+ *    against a microcode update which affects MWAIT.
+ */
+static bool ensure_cpus_are_online(void)
+{
+	unsigned int cpu;
+
+	for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
+		if (!cpu_online(cpu)) {
+			if (topology_is_primary_thread(cpu) || !microcode_ops->nmi_safe) {
+				pr_err("CPU %u not online\n", cpu);
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
 static ssize_t reload_store(struct device *dev,
 			    struct device_attribute *attr,
 			    const char *buf, size_t size)
@@ -474,9 +486,10 @@ static ssize_t reload_store(struct devic
 
 	cpus_read_lock();
 
-	ret = check_online_cpus();
-	if (ret)
+	if (!ensure_cpus_are_online()) {
+		ret = -EBUSY;
 		goto put;
+	}
 
 	tmp_ret = microcode_ops->request_microcode_fw(bsp, &microcode_pdev->dev);
 	if (tmp_ret != UCODE_NEW)
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -20,18 +20,17 @@ enum ucode_state {
 
 struct microcode_ops {
 	enum ucode_state (*request_microcode_fw)(int cpu, struct device *dev);
-
 	void (*microcode_fini_cpu)(int cpu);
 
 	/*
-	 * The generic 'microcode_core' part guarantees that
-	 * the callbacks below run on a target cpu when they
-	 * are being called.
+	 * The generic 'microcode_core' part guarantees that the callbacks
+	 * below run on a target cpu when they are being called.
 	 * See also the "Synchronization" section in microcode_core.c.
 	 */
-	enum ucode_state (*apply_microcode)(int cpu);
-	int (*collect_cpu_info)(int cpu, struct cpu_signature *csig);
-	void (*finalize_late_load)(int result);
+	enum ucode_state	(*apply_microcode)(int cpu);
+	int			(*collect_cpu_info)(int cpu, struct cpu_signature *csig);
+	void			(*finalize_late_load)(int result);
+	unsigned int		nmi_safe	: 1;
 };
 
 extern struct ucode_cpu_info ucode_cpu_info[];


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 19/30] x86/microcode: Clarify the late load logic
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (17 preceding siblings ...)
  2023-08-10 18:37 ` [patch 18/30] x86/microcode: Handle "nosmt" correctly Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 20/30] x86/microcode: Sanitize __wait_for_cpus() Thomas Gleixner
                   ` (10 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

reload_store() is way too complicated. Split the inner workings out and
make the following enhancements:

 - Taint the kernel only when the microcode was actually updated. If. e.g.
   the rendevouz fails, then nothing happened and there is no reason for
   tainting.

 - Return useful error codes

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/kernel/cpu/microcode/core.c |   39 +++++++++++++++--------------------
 1 file changed, 17 insertions(+), 22 deletions(-)
---
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -434,11 +434,11 @@ static int microcode_reload_late(void)
 		pr_info("Reload succeeded, microcode revision: 0x%x -> 0x%x\n",
 			old, boot_cpu_data.microcode);
 		microcode_check(&prev_info);
+		add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
 	} else {
 		pr_info("Reload failed, current microcode revision: 0x%x\n",
 			boot_cpu_data.microcode);
 	}
-
 	return ret;
 }
 
@@ -471,40 +471,35 @@ static bool ensure_cpus_are_online(void)
 	return true;
 }
 
+static int ucode_load_late_locked(void)
+{
+	int ret;
+
+	if (!ensure_cpus_are_online())
+		return -EBUSY;
+
+	ret = microcode_ops->request_microcode_fw(0, &microcode_pdev->dev);
+	if (ret != UCODE_NEW)
+		return ret == UCODE_NFOUND ? -ENOENT : -EBADFD;
+	return microcode_reload_late();
+}
+
 static ssize_t reload_store(struct device *dev,
 			    struct device_attribute *attr,
 			    const char *buf, size_t size)
 {
-	enum ucode_state tmp_ret = UCODE_OK;
-	int bsp = boot_cpu_data.cpu_index;
 	unsigned long val;
-	ssize_t ret = 0;
+	ssize_t ret;
 
 	ret = kstrtoul(buf, 0, &val);
 	if (ret || val != 1)
 		return -EINVAL;
 
 	cpus_read_lock();
-
-	if (!ensure_cpus_are_online()) {
-		ret = -EBUSY;
-		goto put;
-	}
-
-	tmp_ret = microcode_ops->request_microcode_fw(bsp, &microcode_pdev->dev);
-	if (tmp_ret != UCODE_NEW)
-		goto put;
-
-	ret = microcode_reload_late();
-put:
+	ret = ucode_load_late_locked();
 	cpus_read_unlock();
 
-	if (ret == 0)
-		ret = size;
-
-	add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
-
-	return ret;
+	return ret ? : size;
 }
 
 static DEVICE_ATTR_WO(reload);


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 20/30] x86/microcode: Sanitize __wait_for_cpus()
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (18 preceding siblings ...)
  2023-08-10 18:37 ` [patch 19/30] x86/microcode: Clarify the late load logic Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 21/30] x86/microcode: Add per CPU result state Thomas Gleixner
                   ` (9 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

The code is too complicated for no reason:

 - The return value is pointless as this is a strict boolean.

 - It's way simpler to count down from num_online_cpus() and check for
   zero.

  - The timeout argument is pointless as this is always one second.

  - Touching the NMI watchdog every 100ns does not make any sense, neither
    does checking every 100ns. This is really not a hotpath operation.

Preload the atomic counter with the number of online CPUs and simplify the
whole timeout logic. Delay for one microsecond and touch the NMI watchdog
once per millisecond.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/kernel/cpu/microcode/core.c |   41 ++++++++++++++---------------------
 1 file changed, 17 insertions(+), 24 deletions(-)
---
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -324,31 +324,24 @@ static struct platform_device	*microcode
  *   requirement can be relaxed in the future. Right now, this is conservative
  *   and good.
  */
-#define SPINUNIT 100 /* 100 nsec */
+static atomic_t late_cpus_in, late_cpus_out;
 
-
-static atomic_t late_cpus_in;
-static atomic_t late_cpus_out;
-
-static int __wait_for_cpus(atomic_t *t, long long timeout)
+static bool wait_for_cpus(atomic_t *cnt)
 {
-	int all_cpus = num_online_cpus();
-
-	atomic_inc(t);
-
-	while (atomic_read(t) < all_cpus) {
-		if (timeout < SPINUNIT) {
-			pr_err("Timeout while waiting for CPUs rendezvous, remaining: %d\n",
-				all_cpus - atomic_read(t));
-			return 1;
-		}
+	unsigned int timeout;
 
-		ndelay(SPINUNIT);
-		timeout -= SPINUNIT;
+	WARN_ON_ONCE(atomic_dec_return(cnt) < 0);
 
-		touch_nmi_watchdog();
+	for (timeout = 0; timeout < USEC_PER_SEC; timeout++) {
+		if (!atomic_read(cnt))
+			return true;
+		udelay(1);
+		if (!(timeout % 1000))
+			touch_nmi_watchdog();
 	}
-	return 0;
+	/* Prevent the late comers to make progress and let them time out */
+	atomic_inc(cnt);
+	return false;
 }
 
 /*
@@ -366,7 +359,7 @@ static int __reload_late(void *info)
 	 * Wait for all CPUs to arrive. A load will not be attempted unless all
 	 * CPUs show up.
 	 * */
-	if (__wait_for_cpus(&late_cpus_in, NSEC_PER_SEC))
+	if (!wait_for_cpus(&late_cpus_in))
 		return -1;
 
 	/*
@@ -389,7 +382,7 @@ static int __reload_late(void *info)
 	}
 
 wait_for_siblings:
-	if (__wait_for_cpus(&late_cpus_out, NSEC_PER_SEC))
+	if (!wait_for_cpus(&late_cpus_out))
 		panic("Timeout during microcode update!\n");
 
 	/*
@@ -416,8 +409,8 @@ static int microcode_reload_late(void)
 	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");
 
-	atomic_set(&late_cpus_in,  0);
-	atomic_set(&late_cpus_out, 0);
+	atomic_set(&late_cpus_in, num_online_cpus());
+	atomic_set(&late_cpus_out, num_online_cpus());
 
 	/*
 	 * Take a snapshot before the microcode update in order to compare and


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 21/30] x86/microcode: Add per CPU result state
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (19 preceding siblings ...)
  2023-08-10 18:37 ` [patch 20/30] x86/microcode: Sanitize __wait_for_cpus() Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:37 ` [patch 22/30] x86/microcode: Add per CPU control field Thomas Gleixner
                   ` (8 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

The microcode rendevouz is purely acting on global state, which does not
allow to analyze fails in a coherent way.

Introduce per CPU state where the results are written into, which allows to
analyze the return codes of the individual CPUs.

Initialize the state when walking the cpu_present_mask in the online check
to avoid another for_each_cpu() loop.

Enhance the result print out with that.

The structure is intentionally named ucode_ctrl as it will gain control
fields in subsequent changes.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/kernel/cpu/microcode/core.c     |  108 ++++++++++++++++++-------------
 arch/x86/kernel/cpu/microcode/internal.h |    1 
 2 files changed, 65 insertions(+), 44 deletions(-)
---
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -324,6 +324,11 @@ static struct platform_device	*microcode
  *   requirement can be relaxed in the future. Right now, this is conservative
  *   and good.
  */
+struct ucode_ctrl {
+	enum ucode_state	result;
+};
+
+static DEFINE_PER_CPU(struct ucode_ctrl, ucode_ctrl);
 static atomic_t late_cpus_in, late_cpus_out;
 
 static bool wait_for_cpus(atomic_t *cnt)
@@ -344,23 +349,19 @@ static bool wait_for_cpus(atomic_t *cnt)
 	return false;
 }
 
-/*
- * Returns:
- * < 0 - on error
- *   0 - success (no update done or microcode was updated)
- */
-static int __reload_late(void *info)
+static int ucode_load_cpus_stopped(void *unused)
 {
 	int cpu = smp_processor_id();
-	enum ucode_state err;
-	int ret = 0;
+	enum ucode_state ret;
 
 	/*
 	 * Wait for all CPUs to arrive. A load will not be attempted unless all
 	 * CPUs show up.
 	 * */
-	if (!wait_for_cpus(&late_cpus_in))
-		return -1;
+	if (!wait_for_cpus(&late_cpus_in)) {
+		this_cpu_write(ucode_ctrl.result, UCODE_TIMEOUT);
+		return 0;
+	}
 
 	/*
 	 * On an SMT system, it suffices to load the microcode on one sibling of
@@ -369,17 +370,11 @@ static int __reload_late(void *info)
 	 * loading attempts happen on multiple threads of an SMT core. See
 	 * below.
 	 */
-	if (cpumask_first(topology_sibling_cpumask(cpu)) == cpu)
-		err = microcode_ops->apply_microcode(cpu);
-	else
+	if (cpumask_first(topology_sibling_cpumask(cpu)) != cpu)
 		goto wait_for_siblings;
 
-	if (err >= UCODE_NFOUND) {
-		if (err == UCODE_ERROR) {
-			pr_warn("Error reloading microcode on CPU %d\n", cpu);
-			ret = -1;
-		}
-	}
+	ret = microcode_ops->apply_microcode(cpu);
+	this_cpu_write(ucode_ctrl.result, ret);
 
 wait_for_siblings:
 	if (!wait_for_cpus(&late_cpus_out))
@@ -391,19 +386,18 @@ static int __reload_late(void *info)
 	 * per-cpu cpuinfo can be updated with right microcode
 	 * revision.
 	 */
-	if (cpumask_first(topology_sibling_cpumask(cpu)) != cpu)
-		err = microcode_ops->apply_microcode(cpu);
+	if (cpumask_first(topology_sibling_cpumask(cpu)) == cpu)
+		return 0;
 
-	return ret;
+	ret = microcode_ops->apply_microcode(cpu);
+	this_cpu_write(ucode_ctrl.result, ret);
+	return 0;
 }
 
-/*
- * Reload microcode late on all CPUs. Wait for a sec until they
- * all gather together.
- */
-static int microcode_reload_late(void)
+static int ucode_load_late_stop_cpus(void)
 {
-	int old = boot_cpu_data.microcode, ret;
+	unsigned int cpu, updated = 0, failed = 0, timedout = 0, siblings = 0;
+	int old_rev = boot_cpu_data.microcode;
 	struct cpuinfo_x86 prev_info;
 
 	pr_err("Attempting late microcode loading - it is dangerous and taints the kernel.\n");
@@ -418,26 +412,47 @@ static int microcode_reload_late(void)
 	 */
 	store_cpu_caps(&prev_info);
 
-	ret = stop_machine_cpuslocked(__reload_late, NULL, cpu_online_mask);
+	stop_machine_cpuslocked(ucode_load_cpus_stopped, NULL, cpu_online_mask);
+
+	/* Analyze the results */
+	for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
+		switch (per_cpu(ucode_ctrl.result, cpu)) {
+		case UCODE_UPDATED:	updated++; break;
+		case UCODE_TIMEOUT:	timedout++; break;
+		case UCODE_OK:		siblings++; break;
+		default:		failed++; break;
+		}
+	}
 
 	if (microcode_ops->finalize_late_load)
-		microcode_ops->finalize_late_load(ret);
+		microcode_ops->finalize_late_load(!updated);
 
-	if (!ret) {
-		pr_info("Reload succeeded, microcode revision: 0x%x -> 0x%x\n",
-			old, boot_cpu_data.microcode);
-		microcode_check(&prev_info);
-		add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
-	} else {
-		pr_info("Reload failed, current microcode revision: 0x%x\n",
-			boot_cpu_data.microcode);
+	if (!updated) {
+		/* Nothing changed. */
+		if (!failed && !timedout)
+			return 0;
+		pr_err("Microcode update failed: %u CPUs failed %u CPUs timed out\n",
+		       failed, timedout);
+		return -EIO;
 	}
-	return ret;
+
+	add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
+	pr_info("Microcode load: updated on %u primary CPUs with %u siblings\n", updated, siblings);
+	if (failed || timedout) {
+		pr_err("Microcode load incomplete. %u CPUs timed out or failed\n",
+		       num_online_cpus() - (updated + siblings));
+	}
+	pr_info("Microcode revision: 0x%x -> 0x%x\n", old_rev, boot_cpu_data.microcode);
+	microcode_check(&prev_info);
+
+	return updated + siblings == num_online_cpus() ? 0 : -EIO;
 }
 
 /*
- *  Ensure that all required CPUs which are present and have been booted
- *  once are online.
+ * This function does two things:
+ *
+ * 1) Ensure that all required CPUs which are present and have been booted
+ *    once are online.
  *
  *    To pass this check, all primary threads must be online.
  *
@@ -448,9 +463,12 @@ static int microcode_reload_late(void)
  *    behaviour is undefined. The default play_dead() implementation on
  *    modern CPUs is using MWAIT, which is also not guaranteed to be safe
  *    against a microcode update which affects MWAIT.
+ *
+ * 2) Initialize the per CPU control structure
  */
-static bool ensure_cpus_are_online(void)
+static bool ucode_setup_cpus(void)
 {
+	struct ucode_ctrl ctrl = { .result = -1, };
 	unsigned int cpu;
 
 	for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
@@ -460,6 +478,8 @@ static bool ensure_cpus_are_online(void)
 				return false;
 			}
 		}
+		/* Initialize the per CPU state */
+		per_cpu(ucode_ctrl, cpu) = ctrl;
 	}
 	return true;
 }
@@ -468,13 +488,13 @@ static int ucode_load_late_locked(void)
 {
 	int ret;
 
-	if (!ensure_cpus_are_online())
+	if (!ucode_setup_cpus())
 		return -EBUSY;
 
 	ret = microcode_ops->request_microcode_fw(0, &microcode_pdev->dev);
 	if (ret != UCODE_NEW)
 		return ret == UCODE_NFOUND ? -ENOENT : -EBADFD;
-	return microcode_reload_late();
+	return ucode_load_late_stop_cpus();
 }
 
 static ssize_t reload_store(struct device *dev,
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -16,6 +16,7 @@ enum ucode_state {
 	UCODE_UPDATED,
 	UCODE_NFOUND,
 	UCODE_ERROR,
+	UCODE_TIMEOUT,
 };
 
 struct microcode_ops {


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 22/30] x86/microcode: Add per CPU control field
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (20 preceding siblings ...)
  2023-08-10 18:37 ` [patch 21/30] x86/microcode: Add per CPU result state Thomas Gleixner
@ 2023-08-10 18:37 ` Thomas Gleixner
  2023-08-10 18:38 ` [patch 23/30] x86/microcode: Provide new control functions Thomas Gleixner
                   ` (7 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:37 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

Add a per CPU control field to ucode_ctrl and define constants for it:

SCTRL_WAIT    indicates that the CPU needs to spinwait with timeout
SCTRL_APPLY   indicates that the CPU needs to invoke the microcode_apply()
	      callback
SCTRL_DONE    indicates that the CPU can proceed without invoking the
	      microcode_apply() callback.

In theory this could be a global control field, but a global control does
not cover the following case:

 15 primary CPUs load microcode successfully
  1 primary CPU fails and returns with an error code

With global control the sibling of the failed CPU would either try again or
the whole operation would be aborted with the consequence that the 15
siblings do not invoke the apply path and end up with inconsistent software
state. The result in dmesg would be inconsistent too.

There are two additional fields added and initialized:

ctrl_cpu and secondaries. ctrl_cpu is the CPU number of the primary thread
for now, but with the upcoming uniform loading at package or system scope
this will be one CPU per package or just one CPU. Secondaries hands the
control CPU a CPU mask which will be required to release the secondary CPUs
out of the wait loop.

Preparatory change for implementing a properly split control flow for
primary and secondary CPUs.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>


---
 arch/x86/kernel/cpu/microcode/core.c |   20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)
---
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -324,8 +324,16 @@ static struct platform_device	*microcode
  *   requirement can be relaxed in the future. Right now, this is conservative
  *   and good.
  */
+enum sibling_ctrl {
+	SCTRL_WAIT,
+	SCTRL_APPLY,
+	SCTRL_DONE,
+};
+
 struct ucode_ctrl {
+	enum sibling_ctrl	ctrl;
 	enum ucode_state	result;
+	unsigned int		ctrl_cpu;
 };
 
 static DEFINE_PER_CPU(struct ucode_ctrl, ucode_ctrl);
@@ -468,7 +476,7 @@ static int ucode_load_late_stop_cpus(voi
  */
 static bool ucode_setup_cpus(void)
 {
-	struct ucode_ctrl ctrl = { .result = -1, };
+	struct ucode_ctrl ctrl = { .ctrl = SCTRL_WAIT, .result = -1, };
 	unsigned int cpu;
 
 	for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
@@ -478,7 +486,15 @@ static bool ucode_setup_cpus(void)
 				return false;
 			}
 		}
-		/* Initialize the per CPU state */
+
+		/*
+		 * Initialize the per CPU state. This is core scope for now,
+		 * but prepared to take package or system scope into account.
+		 */
+		if (topology_is_primary_thread(cpu))
+			ctrl.ctrl_cpu = cpu;
+		else
+			ctrl.ctrl_cpu = cpumask_first(topology_sibling_cpumask(cpu));
 		per_cpu(ucode_ctrl, cpu) = ctrl;
 	}
 	return true;


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 23/30] x86/microcode: Provide new control functions
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (21 preceding siblings ...)
  2023-08-10 18:37 ` [patch 22/30] x86/microcode: Add per CPU control field Thomas Gleixner
@ 2023-08-10 18:38 ` Thomas Gleixner
  2023-08-10 20:25   ` Peter Zijlstra
  2023-08-10 18:38 ` [patch 24/30] x86/microcode: Replace the all in one rendevouz handler Thomas Gleixner
                   ` (6 subsequent siblings)
  29 siblings, 1 reply; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:38 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

The current all in one code is unreadable and really not suited for adding
future features like uniform loading with package or system scope.

Provide a set of new control functions which split the handling of the
primary and secondary CPUs. These will replace the current rendevouz all in
one function in the next step. This is intentionally a separate change
because diff makes an complete unreadable mess otherwise.

So the flow separates the primary and the secondary CPUs into their own
functions, which use the control field in the per CPU ucode_ctrl struct.

   primary()			secondary()
    wait_for_all()		 wait_for_all()
    apply_ucode()		 wait_for_release()
    release()			 apply_ucode()

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/kernel/cpu/microcode/core.c |   86 +++++++++++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)
---
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -357,6 +357,92 @@ static bool wait_for_cpus(atomic_t *cnt)
 	return false;
 }
 
+static bool wait_for_ctrl(void)
+{
+	unsigned int timeout;
+
+	for (timeout = 0; timeout < USEC_PER_SEC; timeout++) {
+		if (this_cpu_read(ucode_ctrl.ctrl) != SCTRL_WAIT)
+			return true;
+		udelay(1);
+		if (!(timeout % 1000))
+			touch_nmi_watchdog();
+	}
+	return false;
+}
+
+static __maybe_unused void ucode_load_secondary(unsigned int cpu)
+{
+	unsigned int ctrl_cpu = this_cpu_read(ucode_ctrl.ctrl_cpu);
+	enum ucode_state ret;
+
+	/* Initial rendevouz to ensure that all CPUs have arrived */
+	if (!wait_for_cpus(&late_cpus_in)) {
+		pr_err_once("Microcode load: %d CPUs timed out\n",
+			    atomic_read(&late_cpus_in) - 1);
+		this_cpu_write(ucode_ctrl.result, UCODE_TIMEOUT);
+		return;
+	}
+
+	/*
+	 * Wait for primary threads to complete. If one of them hangs due
+	 * to the update, there is no way out. This is non-recoverable
+	 * because the CPU might hold locks or resources and confuse the
+	 * scheduler, watchdogs etc. There is no way to safely evacuate the
+	 * machine.
+	 */
+	if (!wait_for_ctrl())
+		panic("Microcode load: Primary CPU %d timed out\n", ctrl_cpu);
+
+	/*
+	 * If the primary succeeded then invoke the apply() callback,
+	 * otherwise copy the state from the primary thread.
+	 */
+	if (this_cpu_read(ucode_ctrl.ctrl) == SCTRL_APPLY)
+		ret = microcode_ops->apply_microcode(cpu);
+	else
+		ret = per_cpu(ucode_ctrl.result, ctrl_cpu);
+
+	this_cpu_write(ucode_ctrl.result, ret);
+	this_cpu_write(ucode_ctrl.ctrl, SCTRL_DONE);
+}
+
+static __maybe_unused void ucode_load_primary(unsigned int cpu)
+{
+	struct cpumask *secondaries = topology_sibling_cpumask(cpu);
+	enum sibling_ctrl ctrl;
+	enum ucode_state ret;
+	unsigned int sibling;
+
+	/* Initial rendevouz to ensure that all CPUs have arrived */
+	if (!wait_for_cpus(&late_cpus_in)) {
+		this_cpu_write(ucode_ctrl.result, UCODE_TIMEOUT);
+		pr_err_once("Microcode load: %d CPUs timed out\n",
+			    atomic_read(&late_cpus_in) - 1);
+		return;
+	}
+
+	ret = microcode_ops->apply_microcode(cpu);
+	this_cpu_write(ucode_ctrl.result, ret);
+	this_cpu_write(ucode_ctrl.ctrl, SCTRL_DONE);
+
+	/*
+	 * If the update was successful, let the siblings run the apply()
+	 * callback. If not, tell them it's done. This also covers the
+	 * case where the CPU has uniform loading at package or system
+	 * scope implemented but does not advertise it.
+	 */
+	if (ret == UCODE_UPDATED || ret == UCODE_OK)
+		ctrl = SCTRL_APPLY;
+	else
+		ctrl = SCTRL_DONE;
+
+	for_each_cpu(sibling, secondaries) {
+		if (sibling != cpu)
+			per_cpu(ucode_ctrl.ctrl, sibling) = ctrl;
+	}
+}
+
 static int ucode_load_cpus_stopped(void *unused)
 {
 	int cpu = smp_processor_id();


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 24/30] x86/microcode: Replace the all in one rendevouz handler
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (22 preceding siblings ...)
  2023-08-10 18:38 ` [patch 23/30] x86/microcode: Provide new control functions Thomas Gleixner
@ 2023-08-10 18:38 ` Thomas Gleixner
  2023-08-10 18:38 ` [patch 25/30] x86/microcode: Rendezvous and load in NMI Thomas Gleixner
                   ` (5 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:38 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

with a new handler which just separates the control flow of primary and
secondary CPUs.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/kernel/cpu/microcode/core.c |   51 ++++++-----------------------------
 1 file changed, 9 insertions(+), 42 deletions(-)
---
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -337,7 +337,7 @@ struct ucode_ctrl {
 };
 
 static DEFINE_PER_CPU(struct ucode_ctrl, ucode_ctrl);
-static atomic_t late_cpus_in, late_cpus_out;
+static atomic_t late_cpus_in;
 
 static bool wait_for_cpus(atomic_t *cnt)
 {
@@ -371,7 +371,7 @@ static bool wait_for_ctrl(void)
 	return false;
 }
 
-static __maybe_unused void ucode_load_secondary(unsigned int cpu)
+static void ucode_load_secondary(unsigned int cpu)
 {
 	unsigned int ctrl_cpu = this_cpu_read(ucode_ctrl.ctrl_cpu);
 	enum ucode_state ret;
@@ -407,7 +407,7 @@ static __maybe_unused void ucode_load_se
 	this_cpu_write(ucode_ctrl.ctrl, SCTRL_DONE);
 }
 
-static __maybe_unused void ucode_load_primary(unsigned int cpu)
+static void ucode_load_primary(unsigned int cpu)
 {
 	struct cpumask *secondaries = topology_sibling_cpumask(cpu);
 	enum sibling_ctrl ctrl;
@@ -445,46 +445,14 @@ static __maybe_unused void ucode_load_pr
 
 static int ucode_load_cpus_stopped(void *unused)
 {
-	int cpu = smp_processor_id();
-	enum ucode_state ret;
-
-	/*
-	 * Wait for all CPUs to arrive. A load will not be attempted unless all
-	 * CPUs show up.
-	 * */
-	if (!wait_for_cpus(&late_cpus_in)) {
-		this_cpu_write(ucode_ctrl.result, UCODE_TIMEOUT);
-		return 0;
-	}
-
-	/*
-	 * On an SMT system, it suffices to load the microcode on one sibling of
-	 * the core because the microcode engine is shared between the threads.
-	 * Synchronization still needs to take place so that no concurrent
-	 * loading attempts happen on multiple threads of an SMT core. See
-	 * below.
-	 */
-	if (cpumask_first(topology_sibling_cpumask(cpu)) != cpu)
-		goto wait_for_siblings;
+	unsigned int cpu = smp_processor_id();
 
-	ret = microcode_ops->apply_microcode(cpu);
-	this_cpu_write(ucode_ctrl.result, ret);
-
-wait_for_siblings:
-	if (!wait_for_cpus(&late_cpus_out))
-		panic("Timeout during microcode update!\n");
-
-	/*
-	 * At least one thread has completed update on each core.
-	 * For others, simply call the update to make sure the
-	 * per-cpu cpuinfo can be updated with right microcode
-	 * revision.
-	 */
-	if (cpumask_first(topology_sibling_cpumask(cpu)) == cpu)
-		return 0;
+	if (this_cpu_read(ucode_ctrl.ctrl_cpu) == cpu)
+		ucode_load_primary(cpu);
+	else
+		ucode_load_secondary(cpu);
 
-	ret = microcode_ops->apply_microcode(cpu);
-	this_cpu_write(ucode_ctrl.result, ret);
+	/* No point to wait here. The CPUs will all wait in stop_machine(). */
 	return 0;
 }
 
@@ -498,7 +466,6 @@ static int ucode_load_late_stop_cpus(voi
 	pr_err("You should switch to early loading, if possible.\n");
 
 	atomic_set(&late_cpus_in, num_online_cpus());
-	atomic_set(&late_cpus_out, num_online_cpus());
 
 	/*
 	 * Take a snapshot before the microcode update in order to compare and


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 25/30] x86/microcode: Rendezvous and load in NMI
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (23 preceding siblings ...)
  2023-08-10 18:38 ` [patch 24/30] x86/microcode: Replace the all in one rendevouz handler Thomas Gleixner
@ 2023-08-10 18:38 ` Thomas Gleixner
  2023-08-10 18:38 ` [patch 26/30] x86/microcode: Protect against instrumentation Thomas Gleixner
                   ` (4 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:38 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

stop_machine() does not prevent the spin-waiting sibling from handling an
NMI, which is obviously violating the whole concept of rendezvous.

Implement a static branch right in the beginning of the NMI handler which
is NOOPed except when enabled by the late loading mechanism.

The later loader enables the static branch before stop_machine() is
invoked. Each CPU has an nmi_enable in its control structure which
indicates whether the CPU should go into the update routine.

This is required to bridge the gap between enabling the branch and actually
being at the point where it makes sense.

Each CPU which arrives in the stopper thread function sets that flag and
issues a self NMI right after that. If the NMI function sees the flag
clear, it returns. If it's set it clears the flag and enters the rendezvous.

This is safe against a real NMI which hits in between setting the flag and
sending the NMI to itself. The real NMI will be swallowed by the microcode
update and the self NMI will then let stuff continue. Otherwise this would
end up with a spurious NMI.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/include/asm/microcode.h         |   12 ++++++++
 arch/x86/kernel/cpu/microcode/core.c     |   42 ++++++++++++++++++++++++++++---
 arch/x86/kernel/cpu/microcode/intel.c    |    1 
 arch/x86/kernel/cpu/microcode/internal.h |    3 +-
 arch/x86/kernel/nmi.c                    |    4 ++
 5 files changed, 57 insertions(+), 5 deletions(-)
---
--- a/arch/x86/include/asm/microcode.h
+++ b/arch/x86/include/asm/microcode.h
@@ -78,4 +78,16 @@ void show_ucode_info_early(void);
 static inline void show_ucode_info_early(void) { }
 #endif /* !CONFIG_CPU_SUP_INTEL */
 
+bool microcode_nmi_handler(void);
+
+#ifdef CONFIG_MICROCODE_LATE_LOADING
+DECLARE_STATIC_KEY_FALSE(microcode_nmi_handler_enable);
+static __always_inline bool microcode_nmi_handler_enabled(void)
+{
+	return static_branch_unlikely(&microcode_nmi_handler_enable);
+}
+#else
+static __always_inline bool microcode_nmi_handler_enabled(void) { return false; }
+#endif
+
 #endif /* _ASM_X86_MICROCODE_H */
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -23,6 +23,7 @@
 #include <linux/miscdevice.h>
 #include <linux/capability.h>
 #include <linux/firmware.h>
+#include <linux/cpumask.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
@@ -31,6 +32,7 @@
 #include <linux/fs.h>
 #include <linux/mm.h>
 
+#include <asm/apic.h>
 #include <asm/cpu_device_id.h>
 #include <asm/perf_event.h>
 #include <asm/processor.h>
@@ -334,8 +336,10 @@ struct ucode_ctrl {
 	enum sibling_ctrl	ctrl;
 	enum ucode_state	result;
 	unsigned int		ctrl_cpu;
+	bool			nmi_enabled;
 };
 
+DEFINE_STATIC_KEY_FALSE(microcode_nmi_handler_enable);
 static DEFINE_PER_CPU(struct ucode_ctrl, ucode_ctrl);
 static atomic_t late_cpus_in;
 
@@ -349,7 +353,8 @@ static bool wait_for_cpus(atomic_t *cnt)
 		if (!atomic_read(cnt))
 			return true;
 		udelay(1);
-		if (!(timeout % 1000))
+		/* If invoked directly, tickle the NMI watchdog */
+		if (!microcode_ops->use_nmi && !(timeout % 1000))
 			touch_nmi_watchdog();
 	}
 	/* Prevent the late comers to make progress and let them time out */
@@ -365,7 +370,8 @@ static bool wait_for_ctrl(void)
 		if (this_cpu_read(ucode_ctrl.ctrl) != SCTRL_WAIT)
 			return true;
 		udelay(1);
-		if (!(timeout % 1000))
+		/* If invoked directly, tickle the NMI watchdog */
+		if (!microcode_ops->use_nmi && !(timeout % 1000))
 			touch_nmi_watchdog();
 	}
 	return false;
@@ -443,7 +449,7 @@ static void ucode_load_primary(unsigned
 	}
 }
 
-static int ucode_load_cpus_stopped(void *unused)
+static bool microcode_update_handler(void)
 {
 	unsigned int cpu = smp_processor_id();
 
@@ -452,7 +458,29 @@ static int ucode_load_cpus_stopped(void
 	else
 		ucode_load_secondary(cpu);
 
-	/* No point to wait here. The CPUs will all wait in stop_machine(). */
+	touch_nmi_watchdog();
+	return true;
+}
+
+bool microcode_nmi_handler(void)
+{
+	if (!this_cpu_read(ucode_ctrl.nmi_enabled))
+		return false;
+
+	this_cpu_write(ucode_ctrl.nmi_enabled, false);
+	return microcode_update_handler();
+}
+
+static int ucode_load_cpus_stopped(void *unused)
+{
+	if (microcode_ops->use_nmi) {
+		/* Enable the NMI handler and raise NMI */
+		this_cpu_write(ucode_ctrl.nmi_enabled, true);
+		apic->send_IPI(smp_processor_id(), NMI_VECTOR);
+	} else {
+		/* Just invoke the handler directly */
+		microcode_update_handler();
+	}
 	return 0;
 }
 
@@ -473,8 +501,14 @@ static int ucode_load_late_stop_cpus(voi
 	 */
 	store_cpu_caps(&prev_info);
 
+	if (microcode_ops->use_nmi)
+		static_branch_enable_cpuslocked(&microcode_nmi_handler_enable);
+
 	stop_machine_cpuslocked(ucode_load_cpus_stopped, NULL, cpu_online_mask);
 
+	if (microcode_ops->use_nmi)
+		static_branch_disable_cpuslocked(&microcode_nmi_handler_enable);
+
 	/* Analyze the results */
 	for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
 		switch (per_cpu(ucode_ctrl.result, cpu)) {
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -698,6 +698,7 @@ static struct microcode_ops microcode_in
 	.collect_cpu_info	= collect_cpu_info,
 	.apply_microcode	= apply_microcode_late,
 	.finalize_late_load	= finalize_late_load,
+	.use_nmi		= IS_ENABLED(CONFIG_X86_64),
 };
 
 static __init void calc_llc_size_per_core(struct cpuinfo_x86 *c)
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -31,7 +31,8 @@ struct microcode_ops {
 	enum ucode_state	(*apply_microcode)(int cpu);
 	int			(*collect_cpu_info)(int cpu, struct cpu_signature *csig);
 	void			(*finalize_late_load)(int result);
-	unsigned int		nmi_safe	: 1;
+	unsigned int		nmi_safe	: 1,
+				use_nmi		: 1;
 };
 
 extern struct ucode_cpu_info ucode_cpu_info[];
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -33,6 +33,7 @@
 #include <asm/reboot.h>
 #include <asm/cache.h>
 #include <asm/nospec-branch.h>
+#include <asm/microcode.h>
 #include <asm/sev.h>
 
 #define CREATE_TRACE_POINTS
@@ -343,6 +344,9 @@ static noinstr void default_do_nmi(struc
 
 	instrumentation_begin();
 
+	if (microcode_nmi_handler_enabled() && microcode_nmi_handler())
+		goto out;
+
 	handled = nmi_handle(NMI_LOCAL, regs);
 	__this_cpu_add(nmi_stats.normal, handled);
 	if (handled) {


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 26/30] x86/microcode: Protect against instrumentation
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (24 preceding siblings ...)
  2023-08-10 18:38 ` [patch 25/30] x86/microcode: Rendezvous and load in NMI Thomas Gleixner
@ 2023-08-10 18:38 ` Thomas Gleixner
  2023-08-10 20:36   ` Peter Zijlstra
  2023-08-10 18:38 ` [patch 27/30] x86/apic: Provide apic_force_nmi_on_cpu() Thomas Gleixner
                   ` (3 subsequent siblings)
  29 siblings, 1 reply; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:38 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

The wait for control loop in which the siblings are waiting for the
microcode update on the primary thread must be protected against
instrumentation as instrumentation can end up in #INT3, #DB or #PF, which
then returns with IRET. That IRET reenables NMI which is the opposite of
what the NMI rendezvouz is trying to achieve.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/kernel/cpu/microcode/core.c |  112 ++++++++++++++++++++++++++---------
 1 file changed, 84 insertions(+), 28 deletions(-)
---
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -341,53 +341,65 @@ struct ucode_ctrl {
 
 DEFINE_STATIC_KEY_FALSE(microcode_nmi_handler_enable);
 static DEFINE_PER_CPU(struct ucode_ctrl, ucode_ctrl);
+static unsigned int loops_per_usec;
 static atomic_t late_cpus_in;
 
-static bool wait_for_cpus(atomic_t *cnt)
+static noinstr bool wait_for_cpus(atomic_t *cnt)
 {
-	unsigned int timeout;
+	unsigned int timeout, loops;
 
-	WARN_ON_ONCE(atomic_dec_return(cnt) < 0);
+	WARN_ON_ONCE(raw_atomic_dec_return(cnt) < 0);
 
 	for (timeout = 0; timeout < USEC_PER_SEC; timeout++) {
-		if (!atomic_read(cnt))
+		if (!raw_atomic_read(cnt))
 			return true;
-		udelay(1);
+
+		for (loops = 0; loops < loops_per_usec; loops++)
+			cpu_relax();
+
 		/* If invoked directly, tickle the NMI watchdog */
-		if (!microcode_ops->use_nmi && !(timeout % 1000))
+		if (!microcode_ops->use_nmi && !(timeout % 1000)) {
+			instrumentation_begin();
 			touch_nmi_watchdog();
+			instrumentation_end();
+		}
 	}
 	/* Prevent the late comers to make progress and let them time out */
-	atomic_inc(cnt);
+	raw_atomic_inc(cnt);
 	return false;
 }
 
-static bool wait_for_ctrl(void)
+static noinstr bool wait_for_ctrl(void)
 {
-	unsigned int timeout;
+	unsigned int timeout, loops;
 
 	for (timeout = 0; timeout < USEC_PER_SEC; timeout++) {
-		if (this_cpu_read(ucode_ctrl.ctrl) != SCTRL_WAIT)
+		if (raw_cpu_read(ucode_ctrl.ctrl) != SCTRL_WAIT)
 			return true;
-		udelay(1);
+
+		for (loops = 0; loops < loops_per_usec; loops++)
+			cpu_relax();
+
 		/* If invoked directly, tickle the NMI watchdog */
-		if (!microcode_ops->use_nmi && !(timeout % 1000))
+		if (!microcode_ops->use_nmi && !(timeout % 1000)) {
+			instrumentation_begin();
 			touch_nmi_watchdog();
+			instrumentation_end();
+		}
 	}
 	return false;
 }
 
-static void ucode_load_secondary(unsigned int cpu)
+/*
+ * Protected against instrumentation up to the point where the primary
+ * thread completed the update. See microcode_nmi_handler() for details.
+ */
+static noinstr bool ucode_load_secondary_wait(unsigned int ctrl_cpu)
 {
-	unsigned int ctrl_cpu = this_cpu_read(ucode_ctrl.ctrl_cpu);
-	enum ucode_state ret;
-
 	/* Initial rendevouz to ensure that all CPUs have arrived */
 	if (!wait_for_cpus(&late_cpus_in)) {
-		pr_err_once("Microcode load: %d CPUs timed out\n",
-			    atomic_read(&late_cpus_in) - 1);
 		this_cpu_write(ucode_ctrl.result, UCODE_TIMEOUT);
-		return;
+		return false;
 	}
 
 	/*
@@ -397,9 +409,33 @@ static void ucode_load_secondary(unsigne
 	 * scheduler, watchdogs etc. There is no way to safely evacuate the
 	 * machine.
 	 */
-	if (!wait_for_ctrl())
-		panic("Microcode load: Primary CPU %d timed out\n", ctrl_cpu);
+	if (wait_for_ctrl())
+		return true;
+
+	instrumentation_begin();
+	panic("Microcode load: Primary CPU %d timed out\n", ctrl_cpu);
+	instrumentation_end();
+}
 
+/*
+ * Protected against instrumentation up to the point where the primary
+ * thread completed the update. See microcode_nmi_handler() for details.
+ */
+static noinstr void ucode_load_secondary(unsigned int cpu)
+{
+	unsigned int ctrl_cpu = raw_cpu_read(ucode_ctrl.ctrl_cpu);
+	enum ucode_state ret;
+
+	if (!ucode_load_secondary_wait(ctrl_cpu)) {
+		instrumentation_begin();
+		pr_err_once("Microcode load: %d CPUs timed out\n",
+			    atomic_read(&late_cpus_in) - 1);
+		instrumentation_end();
+		return;
+	}
+
+	/* Primary thread completed. Allow to invoke instrumentable code */
+	instrumentation_begin();
 	/*
 	 * If the primary succeeded then invoke the apply() callback,
 	 * otherwise copy the state from the primary thread.
@@ -411,6 +447,7 @@ static void ucode_load_secondary(unsigne
 
 	this_cpu_write(ucode_ctrl.result, ret);
 	this_cpu_write(ucode_ctrl.ctrl, SCTRL_DONE);
+	instrumentation_end();
 }
 
 static void ucode_load_primary(unsigned int cpu)
@@ -449,25 +486,43 @@ static void ucode_load_primary(unsigned
 	}
 }
 
-static bool microcode_update_handler(void)
+static noinstr bool microcode_update_handler(void)
 {
-	unsigned int cpu = smp_processor_id();
+	unsigned int cpu = raw_smp_processor_id();
 
-	if (this_cpu_read(ucode_ctrl.ctrl_cpu) == cpu)
+	if (raw_cpu_read(ucode_ctrl.ctrl_cpu) == cpu) {
+		instrumentation_begin();
 		ucode_load_primary(cpu);
-	else
+		instrumentation_end();
+	} else {
 		ucode_load_secondary(cpu);
+	}
 
+	instrumentation_begin();
 	touch_nmi_watchdog();
+	instrumentation_end();
+
 	return true;
 }
 
-bool microcode_nmi_handler(void)
+/*
+ * Protection against instrumentation is required for CPUs which are not
+ * safe against an NMI which is delivered to the secondary SMT sibling
+ * while the primary thread updates the microcode. Instrumentation can end
+ * up in #INT3, #DB and #PF. The IRET from those exceptions reenables NMI
+ * which is the opposite of what the NMI rendevouz is trying to achieve.
+ *
+ * The primary thread is safe versus instrumentation as the actual
+ * microcode update handles this correctly. It's only the sibling code
+ * path which must be NMI safe until the primary thread completed the
+ * update.
+ */
+bool noinstr microcode_nmi_handler(void)
 {
-	if (!this_cpu_read(ucode_ctrl.nmi_enabled))
+	if (!raw_cpu_read(ucode_ctrl.nmi_enabled))
 		return false;
 
-	this_cpu_write(ucode_ctrl.nmi_enabled, false);
+	raw_cpu_write(ucode_ctrl.nmi_enabled, false);
 	return microcode_update_handler();
 }
 
@@ -494,6 +549,7 @@ static int ucode_load_late_stop_cpus(voi
 	pr_err("You should switch to early loading, if possible.\n");
 
 	atomic_set(&late_cpus_in, num_online_cpus());
+	loops_per_usec = loops_per_jiffy / (TICK_NSEC / 1000);
 
 	/*
 	 * Take a snapshot before the microcode update in order to compare and


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 27/30] x86/apic: Provide apic_force_nmi_on_cpu()
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (25 preceding siblings ...)
  2023-08-10 18:38 ` [patch 26/30] x86/microcode: Protect against instrumentation Thomas Gleixner
@ 2023-08-10 18:38 ` Thomas Gleixner
  2023-08-10 18:38 ` [patch 28/30] x86/microcode: Handle "offline" CPUs correctly Thomas Gleixner
                   ` (2 subsequent siblings)
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:38 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

When SMT siblings are soft-offlined and parked in one of the play_dead()
variants they still react on NMI, which is problematic on affected Intel
CPUs. The default play_dead() variant uses MWAIT on modern CPUs, which is
not guaranteed to be safe when updated concurrently.

Right now late loading is prevented when not all SMT siblings are online,
but as they still react on NMI, it is possible to bring them out of their
park position into a trivial rendevouz handler.

Provide a function which allows to do that. I does sanity checks whether
the target is in the cpus_booted_once_mask and whether the APIC driver
supports it.

Mark X2APIC and XAPIC as capable, but exclude 32bit and the UV and NUMACHIP
variants as that needs feedback from the relevant experts.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/include/asm/apic.h           |  5 +++++
 arch/x86/kernel/apic/apic_flat_64.c   |  2 ++
 arch/x86/kernel/apic/ipi.c            |  9 ++++++++-
 arch/x86/kernel/apic/x2apic_cluster.c |  1 +
 arch/x86/kernel/apic/x2apic_phys.c    |  1 +
 5 files changed, 17 insertions(+), 1 deletion(-)
---
diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index 98c32aa5963a..e219e6c62138 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -301,6 +301,9 @@ struct apic {
 	enum apic_delivery_modes delivery_mode;
 	bool	dest_mode_logical;
 
+	/* Allows to send an NMI to an "offline" CPU which hangs in *play_dead() */
+	bool	nmi_to_offline_cpu;
+
 	u32	(*calc_dest_apicid)(unsigned int cpu);
 
 	/* ICR related functions */
@@ -505,6 +508,8 @@ extern void default_ioapic_phys_id_map(physid_mask_t *phys_map, physid_mask_t *r
 extern int default_cpu_present_to_apicid(int mps_cpu);
 extern int default_check_phys_apicid_present(int phys_apicid);
 
+void apic_send_nmi_to_offline_cpu(unsigned int cpu);
+
 #endif /* CONFIG_X86_LOCAL_APIC */
 
 #ifdef CONFIG_SMP
diff --git a/arch/x86/kernel/apic/apic_flat_64.c b/arch/x86/kernel/apic/apic_flat_64.c
index 8f72b4351c9f..4340f471e6a6 100644
--- a/arch/x86/kernel/apic/apic_flat_64.c
+++ b/arch/x86/kernel/apic/apic_flat_64.c
@@ -138,6 +138,7 @@ static struct apic apic_flat __ro_after_init = {
 	.send_IPI_allbutself		= default_send_IPI_allbutself,
 	.send_IPI_all			= default_send_IPI_all,
 	.send_IPI_self			= default_send_IPI_self,
+	.nmi_to_offline_cpu		= true,
 
 	.inquire_remote_apic		= default_inquire_remote_apic,
 
@@ -229,6 +230,7 @@ static struct apic apic_physflat __ro_after_init = {
 	.send_IPI_allbutself		= default_send_IPI_allbutself,
 	.send_IPI_all			= default_send_IPI_all,
 	.send_IPI_self			= default_send_IPI_self,
+	.nmi_to_offline_cpu		= true,
 
 	.inquire_remote_apic		= default_inquire_remote_apic,
 
diff --git a/arch/x86/kernel/apic/ipi.c b/arch/x86/kernel/apic/ipi.c
index 2a6509e8c840..6ee6cce4423a 100644
--- a/arch/x86/kernel/apic/ipi.c
+++ b/arch/x86/kernel/apic/ipi.c
@@ -95,8 +95,15 @@ void native_send_call_func_ipi(const struct cpumask *mask)
 	apic->send_IPI_mask(mask, CALL_FUNCTION_VECTOR);
 }
 
+void apic_send_nmi_to_offline_cpu(unsigned int cpu)
+{
+	if (WARN_ON_ONCE(!apic->nmi_to_offline_cpu))
+		return;
+	if (WARN_ON_ONCE(!cpumask_test_cpu(cpu, &cpus_booted_once_mask)))
+		return;
+	apic->send_IPI(cpu, NMI_VECTOR);
+}
 #endif /* CONFIG_SMP */
-
 static inline int __prepare_ICR2(unsigned int mask)
 {
 	return SET_XAPIC_DEST_FIELD(mask);
diff --git a/arch/x86/kernel/apic/x2apic_cluster.c b/arch/x86/kernel/apic/x2apic_cluster.c
index b2b2b7f3e03f..685437a98463 100644
--- a/arch/x86/kernel/apic/x2apic_cluster.c
+++ b/arch/x86/kernel/apic/x2apic_cluster.c
@@ -264,6 +264,7 @@ static struct apic apic_x2apic_cluster __ro_after_init = {
 	.send_IPI_allbutself		= x2apic_send_IPI_allbutself,
 	.send_IPI_all			= x2apic_send_IPI_all,
 	.send_IPI_self			= x2apic_send_IPI_self,
+	.nmi_to_offline_cpu		= true,
 
 	.inquire_remote_apic		= NULL,
 
diff --git a/arch/x86/kernel/apic/x2apic_phys.c b/arch/x86/kernel/apic/x2apic_phys.c
index 896bc41cb2ba..d5e44cb7e15f 100644
--- a/arch/x86/kernel/apic/x2apic_phys.c
+++ b/arch/x86/kernel/apic/x2apic_phys.c
@@ -188,6 +188,7 @@ static struct apic apic_x2apic_phys __ro_after_init = {
 	.send_IPI_allbutself		= x2apic_send_IPI_allbutself,
 	.send_IPI_all			= x2apic_send_IPI_all,
 	.send_IPI_self			= x2apic_send_IPI_self,
+	.nmi_to_offline_cpu		= true,
 
 	.inquire_remote_apic		= NULL,
 


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [patch 28/30] x86/microcode: Handle "offline" CPUs correctly
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (26 preceding siblings ...)
  2023-08-10 18:38 ` [patch 27/30] x86/apic: Provide apic_force_nmi_on_cpu() Thomas Gleixner
@ 2023-08-10 18:38 ` Thomas Gleixner
  2023-08-10 20:46   ` Peter Zijlstra
  2023-08-10 18:38 ` [patch 29/30] x86/microcode: Prepare for minimal revision check Thomas Gleixner
  2023-08-10 18:38 ` [patch 30/30] x86/microcode/intel: Add a minimum required revision for late-loads Thomas Gleixner
  29 siblings, 1 reply; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:38 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

Offline CPUs need to be parked in a safe loop when microcode update is in
progress on the primary CPU. Currently offline CPUs are parked in
'mwait_play_dead()', and for Intel CPUs, its not a safe instruction, because
'mwait' instruction can be patched in the new microcode update that can
cause instability.

- Adds a new microcode state 'UCODE_OFFLINE' to report status on per-cpu
  basis.
- Force NMI on the offline CPUs.

Wakeup offline CPUs while the update is in progress and then return them
back to 'mwait_play_dead()' after microcode update is complete.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 arch/x86/include/asm/microcode.h         |    1 
 arch/x86/kernel/cpu/microcode/core.c     |  112 +++++++++++++++++++++++++++++--
 arch/x86/kernel/cpu/microcode/internal.h |    1 
 arch/x86/kernel/nmi.c                    |    5 +
 4 files changed, 113 insertions(+), 6 deletions(-)
---
--- a/arch/x86/include/asm/microcode.h
+++ b/arch/x86/include/asm/microcode.h
@@ -79,6 +79,7 @@ static inline void show_ucode_info_early
 #endif /* !CONFIG_CPU_SUP_INTEL */
 
 bool microcode_nmi_handler(void);
+void microcode_offline_nmi_handler(void);
 
 #ifdef CONFIG_MICROCODE_LATE_LOADING
 DECLARE_STATIC_KEY_FALSE(microcode_nmi_handler_enable);
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -341,8 +341,9 @@ struct ucode_ctrl {
 
 DEFINE_STATIC_KEY_FALSE(microcode_nmi_handler_enable);
 static DEFINE_PER_CPU(struct ucode_ctrl, ucode_ctrl);
+static atomic_t late_cpus_in, offline_in_nmi;
 static unsigned int loops_per_usec;
-static atomic_t late_cpus_in;
+static cpumask_t cpu_offline_mask;
 
 static noinstr bool wait_for_cpus(atomic_t *cnt)
 {
@@ -450,7 +451,7 @@ static noinstr void ucode_load_secondary
 	instrumentation_end();
 }
 
-static void ucode_load_primary(unsigned int cpu)
+static void __ucode_load_primary(unsigned int cpu)
 {
 	struct cpumask *secondaries = topology_sibling_cpumask(cpu);
 	enum sibling_ctrl ctrl;
@@ -486,6 +487,67 @@ static void ucode_load_primary(unsigned
 	}
 }
 
+static bool ucode_kick_offline_cpus(unsigned int nr_offl)
+{
+	unsigned int cpu, timeout;
+
+	for_each_cpu(cpu, &cpu_offline_mask) {
+		/* Enable the rendevouz handler and send NMI */
+		per_cpu(ucode_ctrl.nmi_enabled, cpu) = true;
+		apic_send_nmi_to_offline_cpu(cpu);
+	}
+
+	/* Wait for them to arrive */
+	for (timeout = 0; timeout < (USEC_PER_SEC / 2); timeout++) {
+		if (atomic_read(&offline_in_nmi) == nr_offl)
+			return true;
+		udelay(1);
+	}
+	/* Let the others time out */
+	return false;
+}
+
+static void ucode_release_offline_cpus(void)
+{
+	unsigned int cpu;
+
+	for_each_cpu(cpu, &cpu_offline_mask)
+		per_cpu(ucode_ctrl.ctrl, cpu) = SCTRL_DONE;
+}
+
+static void ucode_load_primary(unsigned int cpu)
+{
+	unsigned int nr_offl = cpumask_weight(&cpu_offline_mask);
+	bool proceed = true;
+
+	/* Kick soft-offlined SMT siblings if required */
+	if (!cpu && nr_offl)
+		proceed = ucode_kick_offline_cpus(nr_offl);
+
+	/* If the soft-offlined CPUs did not respond, abort */
+	if (proceed)
+		__ucode_load_primary(cpu);
+
+	/* Unconditionally release soft-offlined SMT siblings if required */
+	if (!cpu && nr_offl)
+		ucode_release_offline_cpus();
+}
+
+/*
+ * Minimal stub rendevouz handler for soft-offlined CPUs which participate
+ * in the NMI rendevouz to protect against a concurrent NMI on affected
+ * CPUs.
+ */
+void noinstr microcode_offline_nmi_handler(void)
+{
+	if (!raw_cpu_read(ucode_ctrl.nmi_enabled))
+		return;
+	raw_cpu_write(ucode_ctrl.nmi_enabled, false);
+	raw_cpu_write(ucode_ctrl.result, UCODE_OFFLINE);
+	raw_atomic_inc(&offline_in_nmi);
+	wait_for_ctrl();
+}
+
 static noinstr bool microcode_update_handler(void)
 {
 	unsigned int cpu = raw_smp_processor_id();
@@ -542,6 +604,7 @@ static int ucode_load_cpus_stopped(void
 static int ucode_load_late_stop_cpus(void)
 {
 	unsigned int cpu, updated = 0, failed = 0, timedout = 0, siblings = 0;
+	unsigned int nr_offl, offline = 0;
 	int old_rev = boot_cpu_data.microcode;
 	struct cpuinfo_x86 prev_info;
 
@@ -549,6 +612,7 @@ static int ucode_load_late_stop_cpus(voi
 	pr_err("You should switch to early loading, if possible.\n");
 
 	atomic_set(&late_cpus_in, num_online_cpus());
+	atomic_set(&offline_in_nmi, 0);
 	loops_per_usec = loops_per_jiffy / (TICK_NSEC / 1000);
 
 	/*
@@ -571,6 +635,7 @@ static int ucode_load_late_stop_cpus(voi
 		case UCODE_UPDATED:	updated++; break;
 		case UCODE_TIMEOUT:	timedout++; break;
 		case UCODE_OK:		siblings++; break;
+		case UCODE_OFFLINE:	offline++; break;
 		default:		failed++; break;
 		}
 	}
@@ -582,6 +647,13 @@ static int ucode_load_late_stop_cpus(voi
 		/* Nothing changed. */
 		if (!failed && !timedout)
 			return 0;
+
+		nr_offl = cpumask_weight(&cpu_offline_mask);
+		if (offline < nr_offl) {
+			pr_warn("%u offline siblings did not respond.\n",
+				nr_offl - atomic_read(&offline_in_nmi));
+			return -EIO;
+		}
 		pr_err("Microcode update failed: %u CPUs failed %u CPUs timed out\n",
 		       failed, timedout);
 		return -EIO;
@@ -615,19 +687,49 @@ static int ucode_load_late_stop_cpus(voi
  *    modern CPUs is using MWAIT, which is also not guaranteed to be safe
  *    against a microcode update which affects MWAIT.
  *
- * 2) Initialize the per CPU control structure
+ *    As soft-offlined CPUs still react on NMIs, the SMT sibling
+ *    restriction can be lifted when the vendor driver signals to use NMI
+ *    for rendevouz and the APIC provides a mechanism to send an NMI to a
+ *    soft-offlined CPU. The soft-offlined CPUs are then able to
+ *    participate in the rendezvouz in a trivial stub handler.
+ *
+ * 2) Initialize the per CPU control structure and create a cpumask
+ *    which contains "offline"; secondary threads, so they can be handled
+ *    correctly by a control CPU.
  */
 static bool ucode_setup_cpus(void)
 {
 	struct ucode_ctrl ctrl = { .ctrl = SCTRL_WAIT, .result = -1, };
+	bool allow_smt_offline;
 	unsigned int cpu;
 
+	allow_smt_offline = microcode_ops->nmi_safe ||
+		(microcode_ops->use_nmi && apic->nmi_to_offline_cpu);
+
+	cpumask_clear(&cpu_offline_mask);
+
 	for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
+		/*
+		 * Offline CPUs sit in one of the play_dead() functions
+		 * with interrupts disabled, but they still react on NMIs
+		 * and execute arbitrary code. Also MWAIT being updated
+		 * while the offline CPU sits there is not necessarily safe
+		 * on all CPU variants.
+		 *
+		 * Mark them in the offline_cpus mask which will be handled
+		 * by CPU0 later in the update process.
+		 *
+		 * Ensure that the primary thread is online so that it is
+		 * guaranteed that all cores are updated.
+		 */
 		if (!cpu_online(cpu)) {
-			if (topology_is_primary_thread(cpu) || !microcode_ops->nmi_safe) {
-				pr_err("CPU %u not online\n", cpu);
+			if (topology_is_primary_thread(cpu) || !allow_smt_offline) {
+				pr_err("CPU %u not online, loading aborted\n", cpu);
 				return false;
 			}
+			cpumask_set_cpu(cpu, &cpu_offline_mask);
+			per_cpu(ucode_ctrl, cpu) = ctrl;
+			continue;
 		}
 
 		/*
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -17,6 +17,7 @@ enum ucode_state {
 	UCODE_NFOUND,
 	UCODE_ERROR,
 	UCODE_TIMEOUT,
+	UCODE_OFFLINE,
 };
 
 struct microcode_ops {
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -502,8 +502,11 @@ DEFINE_IDTENTRY_RAW(exc_nmi)
 	if (IS_ENABLED(CONFIG_NMI_CHECK_CPU))
 		raw_atomic_long_inc(&nsp->idt_calls);
 
-	if (IS_ENABLED(CONFIG_SMP) && arch_cpu_is_offline(smp_processor_id()))
+	if (IS_ENABLED(CONFIG_SMP) && arch_cpu_is_offline(smp_processor_id())) {
+		if (microcode_nmi_handler_enabled())
+			microcode_offline_nmi_handler();
 		return;
+	}
 
 	if (this_cpu_read(nmi_state) != NMI_NOT_RUNNING) {
 		this_cpu_write(nmi_state, NMI_LATCHED);


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 29/30] x86/microcode: Prepare for minimal revision check
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (27 preceding siblings ...)
  2023-08-10 18:38 ` [patch 28/30] x86/microcode: Handle "offline" CPUs correctly Thomas Gleixner
@ 2023-08-10 18:38 ` Thomas Gleixner
  2023-08-10 20:54   ` Peter Zijlstra
  2023-08-10 18:38 ` [patch 30/30] x86/microcode/intel: Add a minimum required revision for late-loads Thomas Gleixner
  29 siblings, 1 reply; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:38 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Thomas Gleixner <tglx@linutronix.de>

Applying microcode late can be fatal for the running kernel when the update
changes functionality which is in use already in a non-compatible way,
e.g. by removing a CPUID bit.

There is no way for admins which do not have access to the vendors deep
technical support to decide whether late loading of such a microcode is
safe or not.

Intel has added a new field to the microcode header which tells the minimal
microcode revision which is required to be active in the CPU in order to be
safe.

Provide infrastructure for handling this in the core code and a command
line switch which allows to enforce it.

If the update is considered safe the kernel is not tainted and the annoying
warning message not emitted. If it's enforced and the currently loaded
microcode revision is not safe for late loading then the load is aborted.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 Documentation/admin-guide/kernel-parameters.txt |    5 ++++
 arch/x86/Kconfig                                |   23 ++++++++++++++++++-
 arch/x86/kernel/cpu/microcode/amd.c             |    3 ++
 arch/x86/kernel/cpu/microcode/core.c            |   29 ++++++++++++++++++------
 arch/x86/kernel/cpu/microcode/intel.c           |    3 ++
 arch/x86/kernel/cpu/microcode/internal.h        |    3 ++
 6 files changed, 58 insertions(+), 8 deletions(-)
---
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3239,6 +3239,11 @@
 
 	mga=		[HW,DRM]
 
+	microcode.force_minrev=	[X86]
+			Format: <bool>
+			Enable or disable the microcode minimal revision
+			enforcement for the runtime microcode loader.
+
 	min_addr=nn[KMG]	[KNL,BOOT,IA-64] All physical memory below this
 			physical address is ignored.
 
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1320,7 +1320,28 @@ config MICROCODE_LATE_LOADING
 	  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 unless the
+	  microcode header indicates that it is safe for late loading via the
+	  minimal revision check. This minimal revision check can be enforced on
+	  the kernel command line with "microcode.minrev=Y".
+
+config MICROCODE_LATE_FORCE_MINREV
+	bool "Enforce late microcode loading minimal revision check"
+	default n
+	depends on MICROCODE_LATE_LOADING
+	help
+	  To prevent that users load microcode late which modifies already
+	  in use features, newer microcodes have a minimum revision field
+	  in the microcode header, which tells the kernel which minimum
+	  revision must be active in the CPU to safely load that new microcode
+	  late into the running system. If disabled the check will not
+	  be enforced but the kernel will be tainted when the minimal
+	  revision check fails.
+
+	  This minimal revision check can also be controlled via the
+	  "microcode.minrev" parameter on the kernel command line.
+
+	  If unsure say Y.
 
 config X86_MSR
 	tristate "/dev/cpu/*/msr - Model-specific register support"
--- a/arch/x86/kernel/cpu/microcode/amd.c
+++ b/arch/x86/kernel/cpu/microcode/amd.c
@@ -880,6 +880,9 @@ static enum ucode_state request_microcod
 	enum ucode_state ret = UCODE_NFOUND;
 	const struct firmware *fw;
 
+	if (force_minrev)
+		return UCODE_NFOUND;
+
 	if (c->x86 >= 0x15)
 		snprintf(fw_name, sizeof(fw_name), "amd-ucode/microcode_amd_fam%.2xh.bin", c->x86);
 
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -46,6 +46,9 @@
 static struct microcode_ops	*microcode_ops;
 static bool dis_ucode_ldr = true;
 
+bool force_minrev = IS_ENABLED(CONFIG_MICROCODE_LATE_FORCE_MINREV);
+module_param(force_minrev, bool, S_IRUSR | S_IWUSR);
+
 bool initrd_gone;
 
 /*
@@ -601,15 +604,17 @@ static int ucode_load_cpus_stopped(void
 	return 0;
 }
 
-static int ucode_load_late_stop_cpus(void)
+static int ucode_load_late_stop_cpus(bool is_safe)
 {
 	unsigned int cpu, updated = 0, failed = 0, timedout = 0, siblings = 0;
 	unsigned int nr_offl, offline = 0;
 	int old_rev = boot_cpu_data.microcode;
 	struct cpuinfo_x86 prev_info;
 
-	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 (!is_safe) {
+		pr_err("Late microcode loading without minimal revision check.\n");
+		pr_err("You should switch to early loading, if possible.\n");
+	}
 
 	atomic_set(&late_cpus_in, num_online_cpus());
 	atomic_set(&offline_in_nmi, 0);
@@ -659,7 +664,9 @@ static int ucode_load_late_stop_cpus(voi
 		return -EIO;
 	}
 
-	add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
+	if (!is_safe || failed || timedout)
+		add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
+
 	pr_info("Microcode load: updated on %u primary CPUs with %u siblings\n", updated, siblings);
 	if (failed || timedout) {
 		pr_err("Microcode load incomplete. %u CPUs timed out or failed\n",
@@ -753,9 +760,17 @@ static int ucode_load_late_locked(void)
 		return -EBUSY;
 
 	ret = microcode_ops->request_microcode_fw(0, &microcode_pdev->dev);
-	if (ret != UCODE_NEW)
-		return ret == UCODE_NFOUND ? -ENOENT : -EBADFD;
-	return ucode_load_late_stop_cpus();
+
+	switch (ret) {
+	case UCODE_NEW:
+	case UCODE_NEW_SAFE:
+		break;
+	case UCODE_NFOUND:
+		return -ENOENT;
+	default:
+		return -EBADFD;
+	}
+	return ucode_load_late_stop_cpus(ret == UCODE_NEW_SAFE);
 }
 
 static ssize_t reload_store(struct device *dev,
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -549,6 +549,9 @@ static enum ucode_state read_ucode_intel
 	int cur_rev = uci->cpu_sig.rev;
 	u8 *new_mc = NULL, *mc = NULL;
 
+	if (force_minrev)
+		return UCODE_NFOUND;
+
 	while (iov_iter_count(iter)) {
 		struct microcode_header_intel mc_header;
 		unsigned int mc_size, data_size;
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -13,6 +13,7 @@ struct device;
 enum ucode_state {
 	UCODE_OK	= 0,
 	UCODE_NEW,
+	UCODE_NEW_SAFE,
 	UCODE_UPDATED,
 	UCODE_NFOUND,
 	UCODE_ERROR,
@@ -36,6 +37,8 @@ struct microcode_ops {
 				use_nmi		: 1;
 };
 
+extern bool force_minrev;
+
 extern struct ucode_cpu_info ucode_cpu_info[];
 struct cpio_data find_microcode_in_initrd(const char *path, bool use_pa);
 


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [patch 30/30] x86/microcode/intel: Add a minimum required revision for late-loads
  2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
                   ` (28 preceding siblings ...)
  2023-08-10 18:38 ` [patch 29/30] x86/microcode: Prepare for minimal revision check Thomas Gleixner
@ 2023-08-10 18:38 ` Thomas Gleixner
  29 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-10 18:38 UTC (permalink / raw)
  To: LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

From: Ashok Raj <ashok.raj@intel.com>

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 as
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 therefor 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 load microcode with a minium 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 itself, then the kernel is not tainted.

[ tglx: Massaged changelog and fixed up the implementation ]

Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ashok Raj <ashok.raj@intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>


---
 arch/x86/include/asm/microcode.h      |    3 +-
 arch/x86/kernel/cpu/microcode/intel.c |   37 ++++++++++++++++++++++++++++++----
 2 files changed, 35 insertions(+), 5 deletions(-)
---
--- a/arch/x86/include/asm/microcode.h
+++ b/arch/x86/include/asm/microcode.h
@@ -36,7 +36,8 @@ struct microcode_header_intel {
 	unsigned int	datasize;
 	unsigned int	totalsize;
 	unsigned int	metasize;
-	unsigned int	reserved[2];
+	unsigned int	min_req_ver;
+	unsigned int	reserved;
 };
 
 struct microcode_intel {
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -542,16 +542,40 @@ static enum ucode_state apply_microcode_
 	return ret;
 }
 
+static bool ucode_validate_minrev(struct microcode_header_intel *mc_header)
+{
+	int cur_rev = boot_cpu_data.microcode;
+
+	/*
+	 * When late-loading, ensure the header declares a minimum revision
+	 * required to perform a late-load. The previously reserved field
+	 * is 0 in older microcode blobs.
+	 */
+	if (!mc_header->min_req_ver) {
+		pr_info("Unsafe microcode update: Microcode header does not specify a required min version\n");
+		return false;
+	}
+
+	/*
+	 * Check whether the minimum revision specified in the header is either
+	 * greater or equal to the current revision.
+	 */
+	if (cur_rev < mc_header->min_req_ver) {
+		pr_info("Unsafe microcode update: Current revision 0x%x too old\n", cur_rev);
+		pr_info("Current should be at 0x%x or higher. Use early loading instead\n", mc_header->min_req_ver);
+		return false;
+	}
+	return true;
+}
+
 static enum ucode_state read_ucode_intel(int cpu, struct iov_iter *iter)
 {
 	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
 	unsigned int curr_mc_size = 0, new_mc_size = 0;
+	bool is_safe, new_is_safe = false;
 	int cur_rev = uci->cpu_sig.rev;
 	u8 *new_mc = NULL, *mc = NULL;
 
-	if (force_minrev)
-		return UCODE_NFOUND;
-
 	while (iov_iter_count(iter)) {
 		struct microcode_header_intel mc_header;
 		unsigned int mc_size, data_size;
@@ -594,10 +618,15 @@ static enum ucode_state read_ucode_intel
 		if (!intel_find_matching_signature(mc, uci->cpu_sig.sig, uci->cpu_sig.pf))
 			continue;
 
+		is_safe = ucode_validate_minrev(&mc_header);
+		if (force_minrev && !is_safe)
+			continue;
+
 		kvfree(new_mc);
 		cur_rev = mc_header.rev;
 		new_mc  = mc;
 		new_mc_size = mc_size;
+		new_is_safe = is_safe;
 		mc = NULL;
 	}
 
@@ -614,7 +643,7 @@ static enum ucode_state read_ucode_intel
 		return UCODE_NFOUND;
 
 	ucode_patch_late = (struct microcode_intel *)new_mc;
-	return UCODE_NEW;
+	return new_is_safe ? UCODE_NEW_SAFE : UCODE_NEW;
 
 fail:
 	kvfree(mc);


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 23/30] x86/microcode: Provide new control functions
  2023-08-10 18:38 ` [patch 23/30] x86/microcode: Provide new control functions Thomas Gleixner
@ 2023-08-10 20:25   ` Peter Zijlstra
  0 siblings, 0 replies; 53+ messages in thread
From: Peter Zijlstra @ 2023-08-10 20:25 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: LKML, x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

On Thu, Aug 10, 2023 at 08:38:00PM +0200, Thomas Gleixner wrote:
> From: Thomas Gleixner <tglx@linutronix.de>
> 
> The current all in one code is unreadable and really not suited for adding
> future features like uniform loading with package or system scope.
> 
> Provide a set of new control functions which split the handling of the
> primary and secondary CPUs. These will replace the current rendevouz all in
> one function in the next step. This is intentionally a separate change
> because diff makes an complete unreadable mess otherwise.
> 
> So the flow separates the primary and the secondary CPUs into their own
> functions, which use the control field in the per CPU ucode_ctrl struct.
> 
>    primary()			secondary()
>     wait_for_all()		 wait_for_all()
>     apply_ucode()		 wait_for_release()
>     release()			 apply_ucode()

This hard assumes SMT2, right? If someone were ever to do an x86 smt4
part then smt siblings 1,2,3 would all apply concurrently in secondary,
is that intended?

> +	/*
> +	 * Wait for primary threads to complete. If one of them hangs due
> +	 * to the update, there is no way out. This is non-recoverable
> +	 * because the CPU might hold locks or resources and confuse the
> +	 * scheduler, watchdogs etc. There is no way to safely evacuate the
> +	 * machine.
> +	 */
> +	if (!wait_for_ctrl())
> +		panic("Microcode load: Primary CPU %d timed out\n", ctrl_cpu);

One way around this is to first hot-unplug the CPUs, then NMI prod them
into the rendevous, and only on-line them again if ucode update is
successful. On failure stick them in a (new) error state so that manual
online also fails and scream murder, like above.

But yeah, rather unlikely, and for another day etc..

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 26/30] x86/microcode: Protect against instrumentation
  2023-08-10 18:38 ` [patch 26/30] x86/microcode: Protect against instrumentation Thomas Gleixner
@ 2023-08-10 20:36   ` Peter Zijlstra
  2023-08-11  9:19     ` Thomas Gleixner
  0 siblings, 1 reply; 53+ messages in thread
From: Peter Zijlstra @ 2023-08-10 20:36 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: LKML, x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

On Thu, Aug 10, 2023 at 08:38:04PM +0200, Thomas Gleixner wrote:
> From: Thomas Gleixner <tglx@linutronix.de>
> 
> The wait for control loop in which the siblings are waiting for the
> microcode update on the primary thread must be protected against
> instrumentation as instrumentation can end up in #INT3, #DB or #PF, which
> then returns with IRET. That IRET reenables NMI which is the opposite of
> what the NMI rendezvouz is trying to achieve.

asm_exc_nmi will re-enable NMIs when DEBUG_ENTRY before we reach C.
Late loading had better depend on !DEBUG_ENTRY.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 02/30] x86/microcode: Hide the config knob
  2023-08-10 18:37 ` [patch 02/30] x86/microcode: Hide the config knob Thomas Gleixner
@ 2023-08-10 20:41   ` Ashok Raj
  2023-08-11 11:22     ` Borislav Petkov
  0 siblings, 1 reply; 53+ messages in thread
From: Ashok Raj @ 2023-08-10 20:41 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: LKML, x86, Borislav Petkov, Arjan van de Ven, Ashok Raj

On Thu, Aug 10, 2023 at 08:37:29PM +0200, Thomas Gleixner wrote:
> In reality CONFIG_MICROCODE is enabled in any reasonable configuration when
> Intel or AMD support is enabled. Accomodate to reality.
> 
> Requested-by: Borislav Petkov <bp@alien8.de>
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> ---
>  arch/x86/Kconfig                       |   38 ---------------------------------
>  arch/x86/include/asm/microcode.h       |    6 ++---
>  arch/x86/include/asm/microcode_amd.h   |    2 -
>  arch/x86/include/asm/microcode_intel.h |    2 -
>  arch/x86/kernel/cpu/microcode/Makefile |    4 +--
>  5 files changed, 8 insertions(+), 44 deletions(-)
> 
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -1308,44 +1308,8 @@ config X86_REBOOTFIXUPS
>  	  Say N otherwise.
>  
>  config MICROCODE
> -	bool "CPU microcode loading support"
> -	default y
> +	def_bool y
>  	depends on CPU_SUP_AMD || CPU_SUP_INTEL

Seems like there is a dracut dependency that checks for either of these to
be present.

dracut: Generating /boot/initrd.img-6.5.0-rc3-ucode-minrev+
dracut: Disabling early microcode, because kernel does not support it. CONFIG_MICROCODE_[AMD|INTEL]!=y

Just a tool fix. 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 28/30] x86/microcode: Handle "offline" CPUs correctly
  2023-08-10 18:38 ` [patch 28/30] x86/microcode: Handle "offline" CPUs correctly Thomas Gleixner
@ 2023-08-10 20:46   ` Peter Zijlstra
  2023-08-10 20:50     ` Ashok Raj
  2023-08-11  9:36     ` Thomas Gleixner
  0 siblings, 2 replies; 53+ messages in thread
From: Peter Zijlstra @ 2023-08-10 20:46 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: LKML, x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

On Thu, Aug 10, 2023 at 08:38:07PM +0200, Thomas Gleixner wrote:

>  	for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
> +		/*
> +		 * Offline CPUs sit in one of the play_dead() functions
> +		 * with interrupts disabled, but they still react on NMIs
> +		 * and execute arbitrary code. Also MWAIT being updated
> +		 * while the offline CPU sits there is not necessarily safe
> +		 * on all CPU variants.
> +		 *
> +		 * Mark them in the offline_cpus mask which will be handled
> +		 * by CPU0 later in the update process.
> +		 *
> +		 * Ensure that the primary thread is online so that it is
> +		 * guaranteed that all cores are updated.
> +		 */
>  		if (!cpu_online(cpu)) {
> +			if (topology_is_primary_thread(cpu) || !allow_smt_offline) {
> +				pr_err("CPU %u not online, loading aborted\n", cpu);

We could make the NMI handler do the ucode load, no? Also, you just need
any thread online, don't particularly care about primary thread or not
afaict.

>  				return false;
>  			}
> +			cpumask_set_cpu(cpu, &cpu_offline_mask);
> +			per_cpu(ucode_ctrl, cpu) = ctrl;
> +			continue;
>  		}

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 28/30] x86/microcode: Handle "offline" CPUs correctly
  2023-08-10 20:46   ` Peter Zijlstra
@ 2023-08-10 20:50     ` Ashok Raj
  2023-08-10 21:05       ` Peter Zijlstra
  2023-08-11  9:36     ` Thomas Gleixner
  1 sibling, 1 reply; 53+ messages in thread
From: Ashok Raj @ 2023-08-10 20:50 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Thomas Gleixner, LKML, x86, Borislav Petkov, Arjan van de Ven, Ashok Raj

On Thu, Aug 10, 2023 at 10:46:05PM +0200, Peter Zijlstra wrote:
> On Thu, Aug 10, 2023 at 08:38:07PM +0200, Thomas Gleixner wrote:
> 
> >  	for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
> > +		/*
> > +		 * Offline CPUs sit in one of the play_dead() functions
> > +		 * with interrupts disabled, but they still react on NMIs
> > +		 * and execute arbitrary code. Also MWAIT being updated
> > +		 * while the offline CPU sits there is not necessarily safe
> > +		 * on all CPU variants.
> > +		 *
> > +		 * Mark them in the offline_cpus mask which will be handled
> > +		 * by CPU0 later in the update process.
> > +		 *
> > +		 * Ensure that the primary thread is online so that it is
> > +		 * guaranteed that all cores are updated.
> > +		 */
> >  		if (!cpu_online(cpu)) {
> > +			if (topology_is_primary_thread(cpu) || !allow_smt_offline) {
> > +				pr_err("CPU %u not online, loading aborted\n", cpu);
> 
> We could make the NMI handler do the ucode load, no? Also, you just need
> any thread online, don't particularly care about primary thread or not
> afaict.

Patch 25 does that load in NMI. You are right, we just need "a" CPU in each
core online. 
> 
> >  				return false;
> >  			}
> > +			cpumask_set_cpu(cpu, &cpu_offline_mask);
> > +			per_cpu(ucode_ctrl, cpu) = ctrl;
> > +			continue;
> >  		}

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 29/30] x86/microcode: Prepare for minimal revision check
  2023-08-10 18:38 ` [patch 29/30] x86/microcode: Prepare for minimal revision check Thomas Gleixner
@ 2023-08-10 20:54   ` Peter Zijlstra
  2023-08-11  9:38     ` Thomas Gleixner
  2023-08-11 14:20     ` Arjan van de Ven
  0 siblings, 2 replies; 53+ messages in thread
From: Peter Zijlstra @ 2023-08-10 20:54 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: LKML, x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

On Thu, Aug 10, 2023 at 08:38:09PM +0200, Thomas Gleixner wrote:
> From: Thomas Gleixner <tglx@linutronix.de>
> 
> Applying microcode late can be fatal for the running kernel when the update
> changes functionality which is in use already in a non-compatible way,
> e.g. by removing a CPUID bit.

This includes all compatibility constraints? Because IIRC we've also had
trouble because a CPUID bit got set. Kernel didn't know about, didn't
manage it, but userspace saw the bit and happily tried to use it.

Ofc I can't remember the exact case :/ but anything that changes the
xsave size/state would obviously cause trouble.


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 28/30] x86/microcode: Handle "offline" CPUs correctly
  2023-08-10 20:50     ` Ashok Raj
@ 2023-08-10 21:05       ` Peter Zijlstra
  2023-08-10 21:49         ` Ashok Raj
  0 siblings, 1 reply; 53+ messages in thread
From: Peter Zijlstra @ 2023-08-10 21:05 UTC (permalink / raw)
  To: Ashok Raj; +Cc: Thomas Gleixner, LKML, x86, Borislav Petkov, Arjan van de Ven

On Thu, Aug 10, 2023 at 01:50:17PM -0700, Ashok Raj wrote:
> On Thu, Aug 10, 2023 at 10:46:05PM +0200, Peter Zijlstra wrote:
> > On Thu, Aug 10, 2023 at 08:38:07PM +0200, Thomas Gleixner wrote:
> > 
> > >  	for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
> > > +		/*
> > > +		 * Offline CPUs sit in one of the play_dead() functions
> > > +		 * with interrupts disabled, but they still react on NMIs
> > > +		 * and execute arbitrary code. Also MWAIT being updated
> > > +		 * while the offline CPU sits there is not necessarily safe
> > > +		 * on all CPU variants.
> > > +		 *
> > > +		 * Mark them in the offline_cpus mask which will be handled
> > > +		 * by CPU0 later in the update process.
> > > +		 *
> > > +		 * Ensure that the primary thread is online so that it is
> > > +		 * guaranteed that all cores are updated.
> > > +		 */
> > >  		if (!cpu_online(cpu)) {
> > > +			if (topology_is_primary_thread(cpu) || !allow_smt_offline) {
> > > +				pr_err("CPU %u not online, loading aborted\n", cpu);
> > 
> > We could make the NMI handler do the ucode load, no? Also, you just need
> > any thread online, don't particularly care about primary thread or not
> > afaict.
> 
> Patch 25 does that load in NMI. You are right, we just need "a" CPU in each
> core online. 

Patch 25 does it for online CPUs, offline CPUs are having a separate
code path:

  microcode_nmi_handler()

vs

  microcode_offline_nmi_handler()

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 28/30] x86/microcode: Handle "offline" CPUs correctly
  2023-08-10 21:05       ` Peter Zijlstra
@ 2023-08-10 21:49         ` Ashok Raj
  2023-08-10 22:29           ` Peter Zijlstra
  0 siblings, 1 reply; 53+ messages in thread
From: Ashok Raj @ 2023-08-10 21:49 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Thomas Gleixner, LKML, x86, Borislav Petkov, Arjan van de Ven, Ashok Raj

On Thu, Aug 10, 2023 at 11:05:11PM +0200, Peter Zijlstra wrote:
> On Thu, Aug 10, 2023 at 01:50:17PM -0700, Ashok Raj wrote:
> > On Thu, Aug 10, 2023 at 10:46:05PM +0200, Peter Zijlstra wrote:
> > > On Thu, Aug 10, 2023 at 08:38:07PM +0200, Thomas Gleixner wrote:
> > > 
> > > >  	for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
> > > > +		/*
> > > > +		 * Offline CPUs sit in one of the play_dead() functions
> > > > +		 * with interrupts disabled, but they still react on NMIs
> > > > +		 * and execute arbitrary code. Also MWAIT being updated
> > > > +		 * while the offline CPU sits there is not necessarily safe
> > > > +		 * on all CPU variants.
> > > > +		 *
> > > > +		 * Mark them in the offline_cpus mask which will be handled
> > > > +		 * by CPU0 later in the update process.
> > > > +		 *
> > > > +		 * Ensure that the primary thread is online so that it is
> > > > +		 * guaranteed that all cores are updated.
> > > > +		 */
> > > >  		if (!cpu_online(cpu)) {
> > > > +			if (topology_is_primary_thread(cpu) || !allow_smt_offline) {
> > > > +				pr_err("CPU %u not online, loading aborted\n", cpu);
> > > 
> > > We could make the NMI handler do the ucode load, no? Also, you just need
> > > any thread online, don't particularly care about primary thread or not
> > > afaict.
> > 
> > Patch 25 does that load in NMI. You are right, we just need "a" CPU in each
> > core online. 
> 
> Patch 25 does it for online CPUs, offline CPUs are having a separate
> code path:
> 
>   microcode_nmi_handler()
> 
> vs
> 
>   microcode_offline_nmi_handler()

Since the code enforces all primary CPUs to be ONLINE, the secondaries are the
other thread of the same core. So they automatically get the update when
primary does it. 

The secondaries are parked in NMI just to avoid the risk of executing code
that might be patched by primary.

Or maybe you had something else in mind. 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 28/30] x86/microcode: Handle "offline" CPUs correctly
  2023-08-10 21:49         ` Ashok Raj
@ 2023-08-10 22:29           ` Peter Zijlstra
  2023-08-10 23:02             ` Ashok Raj
  0 siblings, 1 reply; 53+ messages in thread
From: Peter Zijlstra @ 2023-08-10 22:29 UTC (permalink / raw)
  To: Ashok Raj; +Cc: Thomas Gleixner, LKML, x86, Borislav Petkov, Arjan van de Ven

On Thu, Aug 10, 2023 at 02:49:39PM -0700, Ashok Raj wrote:
> On Thu, Aug 10, 2023 at 11:05:11PM +0200, Peter Zijlstra wrote:
> > On Thu, Aug 10, 2023 at 01:50:17PM -0700, Ashok Raj wrote:
> > > On Thu, Aug 10, 2023 at 10:46:05PM +0200, Peter Zijlstra wrote:
> > > > On Thu, Aug 10, 2023 at 08:38:07PM +0200, Thomas Gleixner wrote:
> > > > 
> > > > >  	for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
> > > > > +		/*
> > > > > +		 * Offline CPUs sit in one of the play_dead() functions
> > > > > +		 * with interrupts disabled, but they still react on NMIs
> > > > > +		 * and execute arbitrary code. Also MWAIT being updated
> > > > > +		 * while the offline CPU sits there is not necessarily safe
> > > > > +		 * on all CPU variants.
> > > > > +		 *
> > > > > +		 * Mark them in the offline_cpus mask which will be handled
> > > > > +		 * by CPU0 later in the update process.
> > > > > +		 *
> > > > > +		 * Ensure that the primary thread is online so that it is
> > > > > +		 * guaranteed that all cores are updated.
> > > > > +		 */
> > > > >  		if (!cpu_online(cpu)) {
> > > > > +			if (topology_is_primary_thread(cpu) || !allow_smt_offline) {
> > > > > +				pr_err("CPU %u not online, loading aborted\n", cpu);
> > > > 
> > > > We could make the NMI handler do the ucode load, no? Also, you just need
> > > > any thread online, don't particularly care about primary thread or not
> > > > afaict.
> > > 
> > > Patch 25 does that load in NMI. You are right, we just need "a" CPU in each
> > > core online. 
> > 
> > Patch 25 does it for online CPUs, offline CPUs are having a separate
> > code path:
> > 
> >   microcode_nmi_handler()
> > 
> > vs
> > 
> >   microcode_offline_nmi_handler()
> 
> Since the code enforces all primary CPUs to be ONLINE, the secondaries are the
> other thread of the same core. So they automatically get the update when
> primary does it. 
> 
> The secondaries are parked in NMI just to avoid the risk of executing code
> that might be patched by primary.
> 
> Or maybe you had something else in mind. 

Yeah, not placing constraints on who is online at all. Also, if both
siblings are offline, then onlining will re-load ucode anyway, no?

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 28/30] x86/microcode: Handle "offline" CPUs correctly
  2023-08-10 22:29           ` Peter Zijlstra
@ 2023-08-10 23:02             ` Ashok Raj
  2023-08-11  1:19               ` Thomas Gleixner
  0 siblings, 1 reply; 53+ messages in thread
From: Ashok Raj @ 2023-08-10 23:02 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Thomas Gleixner, LKML, x86, Borislav Petkov, Arjan van de Ven, Ashok Raj

On Fri, Aug 11, 2023 at 12:29:57AM +0200, Peter Zijlstra wrote:
> On Thu, Aug 10, 2023 at 02:49:39PM -0700, Ashok Raj wrote:
> > On Thu, Aug 10, 2023 at 11:05:11PM +0200, Peter Zijlstra wrote:
> > > On Thu, Aug 10, 2023 at 01:50:17PM -0700, Ashok Raj wrote:
> > > > On Thu, Aug 10, 2023 at 10:46:05PM +0200, Peter Zijlstra wrote:
> > > > > On Thu, Aug 10, 2023 at 08:38:07PM +0200, Thomas Gleixner wrote:
> > > > > 
> > > > > >  	for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
> > > > > > +		/*
> > > > > > +		 * Offline CPUs sit in one of the play_dead() functions
> > > > > > +		 * with interrupts disabled, but they still react on NMIs
> > > > > > +		 * and execute arbitrary code. Also MWAIT being updated
> > > > > > +		 * while the offline CPU sits there is not necessarily safe
> > > > > > +		 * on all CPU variants.
> > > > > > +		 *
> > > > > > +		 * Mark them in the offline_cpus mask which will be handled
> > > > > > +		 * by CPU0 later in the update process.
> > > > > > +		 *
> > > > > > +		 * Ensure that the primary thread is online so that it is
> > > > > > +		 * guaranteed that all cores are updated.
> > > > > > +		 */
> > > > > >  		if (!cpu_online(cpu)) {
> > > > > > +			if (topology_is_primary_thread(cpu) || !allow_smt_offline) {
> > > > > > +				pr_err("CPU %u not online, loading aborted\n", cpu);
> > > > > 
> > > > > We could make the NMI handler do the ucode load, no? Also, you just need
> > > > > any thread online, don't particularly care about primary thread or not
> > > > > afaict.
> > > > 
> > > > Patch 25 does that load in NMI. You are right, we just need "a" CPU in each
> > > > core online. 
> > > 
> > > Patch 25 does it for online CPUs, offline CPUs are having a separate
> > > code path:
> > > 
> > >   microcode_nmi_handler()
> > > 
> > > vs
> > > 
> > >   microcode_offline_nmi_handler()
> > 
> > Since the code enforces all primary CPUs to be ONLINE, the secondaries are the
> > other thread of the same core. So they automatically get the update when
> > primary does it. 
> > 
> > The secondaries are parked in NMI just to avoid the risk of executing code
> > that might be patched by primary.
> > 
> > Or maybe you had something else in mind. 
> 
> Yeah, not placing constraints on who is online at all. Also, if both
> siblings are offline, then onlining will re-load ucode anyway, no?

We need one thread in a core online,  because a MCE can happen and we don't
want those running something stale.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 28/30] x86/microcode: Handle "offline" CPUs correctly
  2023-08-10 23:02             ` Ashok Raj
@ 2023-08-11  1:19               ` Thomas Gleixner
  2023-08-11  1:31                 ` Thomas Gleixner
  0 siblings, 1 reply; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-11  1:19 UTC (permalink / raw)
  To: Ashok Raj, Peter Zijlstra
  Cc: LKML, x86, Borislav Petkov, Arjan van de Ven, Ashok Raj

On Thu, Aug 10 2023 at 16:02, Ashok Raj wrote:
> On Fri, Aug 11, 2023 at 12:29:57AM +0200, Peter Zijlstra wrote:
>> 
>> Yeah, not placing constraints on who is online at all. Also, if both
>> siblings are offline, then onlining will re-load ucode anyway, no?
>
> We need one thread in a core online,  because a MCE can happen and we don't
> want those running something stale.

Nonsense. This is a constraint at boot time. But afterwards it does not
matter at all. That's what Peter is talking about.



^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 28/30] x86/microcode: Handle "offline" CPUs correctly
  2023-08-11  1:19               ` Thomas Gleixner
@ 2023-08-11  1:31                 ` Thomas Gleixner
  0 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-11  1:31 UTC (permalink / raw)
  To: Ashok Raj, Peter Zijlstra
  Cc: LKML, x86, Borislav Petkov, Arjan van de Ven, Ashok Raj

On Fri, Aug 11 2023 at 03:19, Thomas Gleixner wrote:

> On Thu, Aug 10 2023 at 16:02, Ashok Raj wrote:
>> On Fri, Aug 11, 2023 at 12:29:57AM +0200, Peter Zijlstra wrote:
>>> 
>>> Yeah, not placing constraints on who is online at all. Also, if both
>>> siblings are offline, then onlining will re-load ucode anyway, no?
>>
>> We need one thread in a core online,  because a MCE can happen and we don't
>> want those running something stale.
>
> Nonsense. This is a constraint at boot time. But afterwards it does not
> matter at all. That's what Peter is talking about.

And worse. It does not matter whether one thread of a core is online or
not in the case of a broadcast MCE during a microcode update simply
because that's game over.

Thanks,

        tglx

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 26/30] x86/microcode: Protect against instrumentation
  2023-08-10 20:36   ` Peter Zijlstra
@ 2023-08-11  9:19     ` Thomas Gleixner
  0 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-11  9:19 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: LKML, x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

On Thu, Aug 10 2023 at 22:36, Peter Zijlstra wrote:
> On Thu, Aug 10, 2023 at 08:38:04PM +0200, Thomas Gleixner wrote:
>> From: Thomas Gleixner <tglx@linutronix.de>
>> 
>> The wait for control loop in which the siblings are waiting for the
>> microcode update on the primary thread must be protected against
>> instrumentation as instrumentation can end up in #INT3, #DB or #PF, which
>> then returns with IRET. That IRET reenables NMI which is the opposite of
>> what the NMI rendezvouz is trying to achieve.
>
> asm_exc_nmi will re-enable NMIs when DEBUG_ENTRY before we reach C.
> Late loading had better depend on !DEBUG_ENTRY.

Good point

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 28/30] x86/microcode: Handle "offline" CPUs correctly
  2023-08-10 20:46   ` Peter Zijlstra
  2023-08-10 20:50     ` Ashok Raj
@ 2023-08-11  9:36     ` Thomas Gleixner
  2023-08-12 16:47       ` Thomas Gleixner
  1 sibling, 1 reply; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-11  9:36 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: LKML, x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

On Thu, Aug 10 2023 at 22:46, Peter Zijlstra wrote:

> On Thu, Aug 10, 2023 at 08:38:07PM +0200, Thomas Gleixner wrote:
>
>>  	for_each_cpu_and(cpu, cpu_present_mask, &cpus_booted_once_mask) {
>> +		/*
>> +		 * Offline CPUs sit in one of the play_dead() functions
>> +		 * with interrupts disabled, but they still react on NMIs
>> +		 * and execute arbitrary code. Also MWAIT being updated
>> +		 * while the offline CPU sits there is not necessarily safe
>> +		 * on all CPU variants.
>> +		 *
>> +		 * Mark them in the offline_cpus mask which will be handled
>> +		 * by CPU0 later in the update process.
>> +		 *
>> +		 * Ensure that the primary thread is online so that it is
>> +		 * guaranteed that all cores are updated.
>> +		 */
>>  		if (!cpu_online(cpu)) {
>> +			if (topology_is_primary_thread(cpu) || !allow_smt_offline) {
>> +				pr_err("CPU %u not online, loading aborted\n", cpu);
>
> We could make the NMI handler do the ucode load, no? Also, you just need
> any thread online, don't particularly care about primary thread or not
> afaict.

Yes, we could. But I did not go there because it's a fricking nightmare
vs. the offline state and noinstr.

OTOH, it's not really required. Right now we mandate that _all_ present
cores have at least one sibling online. For simplicity (and practical
reasons - think "nosmt") we require the "primary" thread to be online.

Microcode is strict per core, no matter how many threads are there. We
would not need any of this mess if Intel would have synchronized the
threads on microcode update like AMD does. This is coming with future
CPUs which advertise "uniform" update with a scope ranging from core,
package to systemwide.

Even today, the only exercise what online SMT siblings do after the
primary thread updated the microcode is verification that update
happened which creates consistent software state. But in principle the
secondaries could just do nothing and everything would work (+/-
hardware,firmware bugs).

Sure we could lift that requirement, but why making this horrorshow even
more complex than it is already ?

There is zero point to support esoteric usecases just because we
can. The realistic use case is a server with all threads online or SMT
disabled via command line or sysfs. Anything else is just a pointless
exercise.

Thanks,

        tglx

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 29/30] x86/microcode: Prepare for minimal revision check
  2023-08-10 20:54   ` Peter Zijlstra
@ 2023-08-11  9:38     ` Thomas Gleixner
  2023-08-11 14:20     ` Arjan van de Ven
  1 sibling, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-11  9:38 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: LKML, x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

On Thu, Aug 10 2023 at 22:54, Peter Zijlstra wrote:
> On Thu, Aug 10, 2023 at 08:38:09PM +0200, Thomas Gleixner wrote:
>> From: Thomas Gleixner <tglx@linutronix.de>
>> 
>> Applying microcode late can be fatal for the running kernel when the update
>> changes functionality which is in use already in a non-compatible way,
>> e.g. by removing a CPUID bit.
>
> This includes all compatibility constraints? Because IIRC we've also had
> trouble because a CPUID bit got set. Kernel didn't know about, didn't
> manage it, but userspace saw the bit and happily tried to use it.

We don't know. If the microcoders screw that minrev constraint up, then
we are up the creek w/o a paddle as before.

> Ofc I can't remember the exact case :/ but anything that changes the
> xsave size/state would obviously cause trouble.

Details. :)

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 09/30] x86/microcode/intel: Remove debug code
  2023-08-10 18:37 ` [patch 09/30] x86/microcode/intel: Remove debug code Thomas Gleixner
@ 2023-08-11 10:45   ` Nikolay Borisov
  0 siblings, 0 replies; 53+ messages in thread
From: Nikolay Borisov @ 2023-08-11 10:45 UTC (permalink / raw)
  To: Thomas Gleixner, LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven



On 10.08.23 г. 21:37 ч., Thomas Gleixner wrote:
> From: Thomas Gleixner <tglx@linutronix.de>
> 
> This is really of dubious value.
> 
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

If this patch is re-ordered with the previous one then the diff there 
will be smaller as there won't be any changes in show_saved_mc

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 13/30] x86/microcode/intel: Cleanup code further
  2023-08-10 18:37 ` [patch 13/30] x86/microcode/intel: Cleanup code further Thomas Gleixner
@ 2023-08-11 11:01   ` Nikolay Borisov
  0 siblings, 0 replies; 53+ messages in thread
From: Nikolay Borisov @ 2023-08-11 11:01 UTC (permalink / raw)
  To: Thomas Gleixner, LKML; +Cc: x86, Borislav Petkov, Ashok Raj, Arjan van de Ven



On 10.08.23 г. 21:37 ч., Thomas Gleixner wrote:
> From: Thomas Gleixner <tglx@linutronix.de>
> 
> Sanitize the microcode scan loop, fixup printks and move the initrd loading
> function next to the place where it is used and mark it __init. Rename
> generic_load_microcode() as that Intel specific function is not generic at
> all.

The rename happened in the previous patch, so drop the last sentence.

<snip>

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 02/30] x86/microcode: Hide the config knob
  2023-08-10 20:41   ` Ashok Raj
@ 2023-08-11 11:22     ` Borislav Petkov
  0 siblings, 0 replies; 53+ messages in thread
From: Borislav Petkov @ 2023-08-11 11:22 UTC (permalink / raw)
  To: Ashok Raj, initramfs; +Cc: Thomas Gleixner, LKML, x86, Arjan van de Ven

+ dracut ML (I hope this is the right one).

Dracut folks, you can drop this check soon - microcode loader will be
unconditionally built in.

On Thu, Aug 10, 2023 at 01:41:43PM -0700, Ashok Raj wrote:
> On Thu, Aug 10, 2023 at 08:37:29PM +0200, Thomas Gleixner wrote:
> > In reality CONFIG_MICROCODE is enabled in any reasonable configuration when
> > Intel or AMD support is enabled. Accomodate to reality.
> > 
> > Requested-by: Borislav Petkov <bp@alien8.de>
> > Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> > ---
> >  arch/x86/Kconfig                       |   38 ---------------------------------
> >  arch/x86/include/asm/microcode.h       |    6 ++---
> >  arch/x86/include/asm/microcode_amd.h   |    2 -
> >  arch/x86/include/asm/microcode_intel.h |    2 -
> >  arch/x86/kernel/cpu/microcode/Makefile |    4 +--
> >  5 files changed, 8 insertions(+), 44 deletions(-)
> > 
> > --- a/arch/x86/Kconfig
> > +++ b/arch/x86/Kconfig
> > @@ -1308,44 +1308,8 @@ config X86_REBOOTFIXUPS
> >  	  Say N otherwise.
> >  
> >  config MICROCODE
> > -	bool "CPU microcode loading support"
> > -	default y
> > +	def_bool y
> >  	depends on CPU_SUP_AMD || CPU_SUP_INTEL
> 
> Seems like there is a dracut dependency that checks for either of these to
> be present.
> 
> dracut: Generating /boot/initrd.img-6.5.0-rc3-ucode-minrev+
> dracut: Disabling early microcode, because kernel does not support it. CONFIG_MICROCODE_[AMD|INTEL]!=y
> 
> Just a tool fix. 

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 29/30] x86/microcode: Prepare for minimal revision check
  2023-08-10 20:54   ` Peter Zijlstra
  2023-08-11  9:38     ` Thomas Gleixner
@ 2023-08-11 14:20     ` Arjan van de Ven
  1 sibling, 0 replies; 53+ messages in thread
From: Arjan van de Ven @ 2023-08-11 14:20 UTC (permalink / raw)
  To: Peter Zijlstra, Thomas Gleixner; +Cc: LKML, x86, Borislav Petkov, Ashok Raj

On 8/10/2023 1:54 PM, Peter Zijlstra wrote:
> On Thu, Aug 10, 2023 at 08:38:09PM +0200, Thomas Gleixner wrote:
>> From: Thomas Gleixner <tglx@linutronix.de>
>>
>> Applying microcode late can be fatal for the running kernel when the update
>> changes functionality which is in use already in a non-compatible way,
>> e.g. by removing a CPUID bit.
> 
> This includes all compatibility constraints? Because IIRC we've also had
> trouble because a CPUID bit got set. Kernel didn't know about, didn't

do you have the details on that -- I don't know of any of those outside
of enumerating the sidechannel status cpuid bits.

> manage it, but userspace saw the bit and happily tried to use it.

yes it contains all the compatibility constraints the OS folks (e.g. the intel kernel folks)
could convey to the microcode team. If you think the constraints are
not complete please help us improve them

> 
> Ofc I can't remember the exact case :/ but anything that changes the
> xsave size/state would obviously cause trouble.

of course that cant' happen at runtime at all correctly.
> 


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 08/30] x86/microcode/intel: Rip out mixed stepping support for Intel CPUs
  2023-08-10 18:37 ` [patch 08/30] x86/microcode/intel: Rip out mixed stepping support for Intel CPUs Thomas Gleixner
@ 2023-08-11 22:25   ` Borislav Petkov
  2023-08-12  7:16     ` Thomas Gleixner
  0 siblings, 1 reply; 53+ messages in thread
From: Borislav Petkov @ 2023-08-11 22:25 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: LKML, x86, Ashok Raj, Arjan van de Ven

On Thu, Aug 10, 2023 at 08:37:38PM +0200, Thomas Gleixner wrote:
> @@ -319,6 +264,7 @@ scan_microcode(void *data, size_t size,
>  {
>  	struct microcode_header_intel *mc_header;
>  	struct microcode_intel *patch = NULL;
> +	u32 cur_rev = uci->cpu_sig.rev;
>  	unsigned int mc_size;
>  
>  	while (size) {
> @@ -328,8 +274,7 @@ scan_microcode(void *data, size_t size,
>  		mc_header = (struct microcode_header_intel *)data;
>  
>  		mc_size = get_totalsize(mc_header);
> -		if (!mc_size ||
> -		    mc_size > size ||
> +		if (!mc_size || mc_size > size ||
>  		    intel_microcode_sanity_check(data, false, MC_HEADER_TYPE_MICROCODE) < 0)
>  			break;
>  
> @@ -341,31 +286,16 @@ scan_microcode(void *data, size_t size,
>  			continue;
>  		}
>  
> -		if (save) {
> -			save_microcode_patch(uci, data, mc_size);
> +		/* BSP scan: Check whether there is newer microcode */
> +		if (save && cur_rev >= mc_header->rev)
>  			goto next;
> -		}
> -
>  
> -		if (!patch) {
> -			if (!has_newer_microcode(data,
> -						 uci->cpu_sig.sig,
> -						 uci->cpu_sig.pf,
> -						 uci->cpu_sig.rev))
> -				goto next;
> -
> -		} else {
> -			struct microcode_header_intel *phdr = &patch->hdr;
> -
> -			if (!has_newer_microcode(data,
> -						 phdr->sig,
> -						 phdr->pf,
> -						 phdr->rev))
> -				goto next;
> -		}
> +		/* Save scan: Check whether there is newer or matching microcode */
> +		if (save && cur_rev != mc_header->rev)
> +			goto next;

I'm confused: when you look at those statements when this patch is
applied, they look like this:

                /* BSP scan: Check whether there is newer microcode */
                if (save && cur_rev >= mc_header->rev)
                        goto next;

                /* Save scan: Check whether there is newer or matching microcode */
                if (save && cur_rev != mc_header->rev)
                        goto next;

You'd only hit the second one if

		cur_rev < mc_header->rev

but then that implies

		cur_rev != mc_header->rev

too. I *think* you wanna have the first test be only ">" as you're
looking for newer microcode.

Besides, __load_ucode_intel() is calling this function with safe ==
false so those statements would never check anything. I guess that's
still ok because the above intel_find_matching_signature() would match.

Hmmm?

Uff, this function is ugly and can be simplified. Perhaps that happens
later.


>  
> -		/* We have a newer patch, save it. */
>  		patch = data;
> +		cur_rev = mc_header->rev;
>  
>  next:
>  		data += mc_size;
> @@ -374,18 +304,22 @@ scan_microcode(void *data, size_t size,
>  	if (size)
>  		return NULL;
>  
> +	if (save && patch)
> +		save_microcode_patch(patch, mc_size);
> +
>  	return patch;
>  }
>  
>  static void show_saved_mc(void)
>  {
>  #ifdef DEBUG

Yeah, what Nikolay said - move the next one before this one and then the
show_saved_mc() hunks are gone.

> -	int i = 0, j;
>  	unsigned int sig, pf, rev, total_size, data_size, date;
> +	struct extended_sigtable *ext_header;
> +	struct extended_signature *ext_sig;
>  	struct ucode_cpu_info uci;
> -	struct ucode_patch *p;
> +	int j, ext_sigcount;
>  
> -	if (list_empty(&microcode_cache)) {
> +	if (!intel_ucode_patch) {
>  		pr_debug("no microcode data saved.\n");
>  		return;
>  	}

...

> @@ -451,7 +374,7 @@ static void save_mc_for_early(struct uco
>  
>  	mutex_lock(&x86_cpu_microcode_mutex);
>  
> -	save_microcode_patch(uci, mc, size);
> +	save_microcode_patch(mc, size);
>  	show_saved_mc();
>  
>  	mutex_unlock(&x86_cpu_microcode_mutex);
> @@ -675,26 +598,10 @@ void load_ucode_intel_ap(void)
>  	apply_microcode_early(&uci, true);
>  }
>  
> -static struct microcode_intel *find_patch(struct ucode_cpu_info *uci)
> +/* Accessor for microcode pointer */
> +static struct microcode_intel *ucode_get_patch(void)

static function - "get_patch" only is fine.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 08/30] x86/microcode/intel: Rip out mixed stepping support for Intel CPUs
  2023-08-11 22:25   ` Borislav Petkov
@ 2023-08-12  7:16     ` Thomas Gleixner
  0 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-12  7:16 UTC (permalink / raw)
  To: Borislav Petkov; +Cc: LKML, x86, Ashok Raj, Arjan van de Ven

On Sat, Aug 12 2023 at 00:25, Borislav Petkov wrote:
> On Thu, Aug 10, 2023 at 08:37:38PM +0200, Thomas Gleixner wrote:
> I'm confused: when you look at those statements when this patch is
> applied, they look like this:
>
>                 /* BSP scan: Check whether there is newer microcode */
>                 if (save && cur_rev >= mc_header->rev)
>                         goto next;

Bah. this clearly must be !save here.

> Uff, this function is ugly and can be simplified. Perhaps that happens
> later.

Yes, it is cleaned up later. I tried it the other way round, but that
didn't really work out.

Thanks,

        tglx



^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [patch 28/30] x86/microcode: Handle "offline" CPUs correctly
  2023-08-11  9:36     ` Thomas Gleixner
@ 2023-08-12 16:47       ` Thomas Gleixner
  0 siblings, 0 replies; 53+ messages in thread
From: Thomas Gleixner @ 2023-08-12 16:47 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: LKML, x86, Borislav Petkov, Ashok Raj, Arjan van de Ven

On Fri, Aug 11 2023 at 11:36, Thomas Gleixner wrote:
> On Thu, Aug 10 2023 at 22:46, Peter Zijlstra wrote:
> OTOH, it's not really required. Right now we mandate that _all_ present
> cores have at least one sibling online. For simplicity (and practical
> reasons - think "nosmt") we require the "primary" thread to be online.
>
> Microcode is strict per core, no matter how many threads are there. We
> would not need any of this mess if Intel would have synchronized the
> threads on microcode update like AMD does. This is coming with future
> CPUs which advertise "uniform" update with a scope ranging from core,
> package to systemwide.

Which still requires the "offline" CPU treatment as the siblings are not
allowed to sit in MWAIT or HLT. So this whole NMI exercise is bound to
stay.

Thanks,

        tglx


^ permalink raw reply	[flat|nested] 53+ messages in thread

end of thread, other threads:[~2023-08-12 16:47 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-10 18:37 [patch 00/30] x86/microcode: Cleanup and late loading enhancements Thomas Gleixner
2023-08-10 18:37 ` [patch 01/30] x86/mm: Remove unused microcode.h include Thomas Gleixner
2023-08-10 18:37 ` [patch 02/30] x86/microcode: Hide the config knob Thomas Gleixner
2023-08-10 20:41   ` Ashok Raj
2023-08-11 11:22     ` Borislav Petkov
2023-08-10 18:37 ` [patch 03/30] x86/microcode/intel: Move microcode functions out of cpu/intel.c Thomas Gleixner
2023-08-10 18:37 ` [patch 04/30] x86/microcode: Include vendor headers into microcode.h Thomas Gleixner
2023-08-10 18:37 ` [patch 05/30] x86/microcode: Make reload_early_microcode() static Thomas Gleixner
2023-08-10 18:37 ` [patch 06/30] x86/microcode/intel: Rename get_datasize() since its used externally Thomas Gleixner
2023-08-10 18:37 ` [patch 07/30] x86/microcode: Move core specific defines to local header Thomas Gleixner
2023-08-10 18:37 ` [patch 08/30] x86/microcode/intel: Rip out mixed stepping support for Intel CPUs Thomas Gleixner
2023-08-11 22:25   ` Borislav Petkov
2023-08-12  7:16     ` Thomas Gleixner
2023-08-10 18:37 ` [patch 09/30] x86/microcode/intel: Remove debug code Thomas Gleixner
2023-08-11 10:45   ` Nikolay Borisov
2023-08-10 18:37 ` [patch 10/30] x86/microcode: Remove pointless mutex Thomas Gleixner
2023-08-10 18:37 ` [patch 11/30] x86/microcode/intel: Simplify scan_microcode() Thomas Gleixner
2023-08-10 18:37 ` [patch 12/30] x86/microcode/intel: Simplify and rename generic_load_microcode() Thomas Gleixner
2023-08-10 18:37 ` [patch 13/30] x86/microcode/intel: Cleanup code further Thomas Gleixner
2023-08-11 11:01   ` Nikolay Borisov
2023-08-10 18:37 ` [patch 14/30] x86/microcode/intel: Simplify early loading Thomas Gleixner
2023-08-10 18:37 ` [patch 15/30] x86/microcode/intel: Save the microcode only after a successful late-load Thomas Gleixner
2023-08-10 18:37 ` [patch 16/30] x86/microcode/intel: Switch to kvmalloc() Thomas Gleixner
2023-08-10 18:37 ` [patch 17/30] x86/microcode/intel: Unify microcode apply() functions Thomas Gleixner
2023-08-10 18:37 ` [patch 18/30] x86/microcode: Handle "nosmt" correctly Thomas Gleixner
2023-08-10 18:37 ` [patch 19/30] x86/microcode: Clarify the late load logic Thomas Gleixner
2023-08-10 18:37 ` [patch 20/30] x86/microcode: Sanitize __wait_for_cpus() Thomas Gleixner
2023-08-10 18:37 ` [patch 21/30] x86/microcode: Add per CPU result state Thomas Gleixner
2023-08-10 18:37 ` [patch 22/30] x86/microcode: Add per CPU control field Thomas Gleixner
2023-08-10 18:38 ` [patch 23/30] x86/microcode: Provide new control functions Thomas Gleixner
2023-08-10 20:25   ` Peter Zijlstra
2023-08-10 18:38 ` [patch 24/30] x86/microcode: Replace the all in one rendevouz handler Thomas Gleixner
2023-08-10 18:38 ` [patch 25/30] x86/microcode: Rendezvous and load in NMI Thomas Gleixner
2023-08-10 18:38 ` [patch 26/30] x86/microcode: Protect against instrumentation Thomas Gleixner
2023-08-10 20:36   ` Peter Zijlstra
2023-08-11  9:19     ` Thomas Gleixner
2023-08-10 18:38 ` [patch 27/30] x86/apic: Provide apic_force_nmi_on_cpu() Thomas Gleixner
2023-08-10 18:38 ` [patch 28/30] x86/microcode: Handle "offline" CPUs correctly Thomas Gleixner
2023-08-10 20:46   ` Peter Zijlstra
2023-08-10 20:50     ` Ashok Raj
2023-08-10 21:05       ` Peter Zijlstra
2023-08-10 21:49         ` Ashok Raj
2023-08-10 22:29           ` Peter Zijlstra
2023-08-10 23:02             ` Ashok Raj
2023-08-11  1:19               ` Thomas Gleixner
2023-08-11  1:31                 ` Thomas Gleixner
2023-08-11  9:36     ` Thomas Gleixner
2023-08-12 16:47       ` Thomas Gleixner
2023-08-10 18:38 ` [patch 29/30] x86/microcode: Prepare for minimal revision check Thomas Gleixner
2023-08-10 20:54   ` Peter Zijlstra
2023-08-11  9:38     ` Thomas Gleixner
2023-08-11 14:20     ` Arjan van de Ven
2023-08-10 18:38 ` [patch 30/30] x86/microcode/intel: Add a minimum required revision for late-loads Thomas Gleixner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).