[PATCH v4 1/5] x86/ucode: Add Kconfig option to remove microcode loading

Alejandro Vallejo posted 4 patches 5 days, 22 hours ago
[PATCH v4 1/5] x86/ucode: Add Kconfig option to remove microcode loading
Posted by Alejandro Vallejo 5 days, 22 hours ago
Amend docs to reflect ucode= command/stanza depend on MICROCODE_LOADING
being set.

The new MICROCODE_OP() is a conditional setter for the microcode_ops
struct. By using IS_ENABLED() there rather than ifdef we allow DCE to
remove all statics no longer used when microcode loading is disabled
without tagging them with __maybe_unused.

Signed-off-by: Alejandro Vallejo <alejandro.garciavallejo@amd.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
v4:
  * Minor spell fix in commit message ("rather")
  * efi.pandoc: terminate sentence with "enabled".
  * xen-command-line: State applicability as first line.
  * reorder cpu hooks.
  * MICROCODE_OP() comment
  * MICROCODE_OP() moved next to microcode_ops
  * Re-arranged platform_ops to turn them into smaller hunks.
---
 docs/admin-guide/microcode-loading.rst |  2 ++
 docs/misc/efi.pandoc                   |  2 ++
 docs/misc/xen-command-line.pandoc      |  7 ++++---
 xen/arch/x86/Kconfig                   | 14 ++++++++++++++
 xen/arch/x86/cpu/microcode/amd.c       | 16 +++++++++-------
 xen/arch/x86/cpu/microcode/core.c      | 15 ++++++++++++---
 xen/arch/x86/cpu/microcode/intel.c     | 11 +++++++----
 xen/arch/x86/cpu/microcode/private.h   |  3 +++
 xen/arch/x86/efi/efi-boot.h            |  3 ++-
 xen/arch/x86/platform_hypercall.c      | 10 ++++++++--
 10 files changed, 63 insertions(+), 20 deletions(-)

diff --git a/docs/admin-guide/microcode-loading.rst b/docs/admin-guide/microcode-loading.rst
index a07e25802f..148bc8559b 100644
--- a/docs/admin-guide/microcode-loading.rst
+++ b/docs/admin-guide/microcode-loading.rst
@@ -23,6 +23,8 @@ TSX errata which necessitated disabling the feature entirely), or the addition
 of brand new features (e.g. the Spectre v2 controls to work around speculative
 execution vulnerabilities).
 
+Microcode loading support in Xen is controlled by the
+``CONFIG_MICROCODE_LOADING`` Kconfig option.
 
 Boot time microcode loading
 ---------------------------
diff --git a/docs/misc/efi.pandoc b/docs/misc/efi.pandoc
index 11c1ac3346..8198a7f063 100644
--- a/docs/misc/efi.pandoc
+++ b/docs/misc/efi.pandoc
@@ -104,6 +104,8 @@ Specifies an XSM module to load.
 
 Specifies a CPU microcode blob to load. (x86 only)
 
+Only available when Xen is compiled with CONFIG_MICROCODE_LOADING enabled.
+
 ###`dtb=<filename>`
 
 Specifies a device tree file to load.  The platform firmware may provide a
diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc
index 15f7a315a4..0eca489b7d 100644
--- a/docs/misc/xen-command-line.pandoc
+++ b/docs/misc/xen-command-line.pandoc
@@ -2751,9 +2751,10 @@ performance.
     Applicability: x86
     Default: `scan` is selectable via Kconfig, `nmi,digest-check`
 
-Controls for CPU microcode loading. For early loading, this parameter can
-specify how and where to find the microcode update blob. For late loading,
-this parameter specifies if the update happens within a NMI handler.
+Controls for CPU microcode loading, available when `CONFIG_MICROCODE_LOADING` is
+enabled. For early loading, this parameter can specify how and where to find the
+microcode update blob. For late loading, this parameter specifies if the update
+happens within a NMI handler.
 
 'integer' specifies the CPU microcode update blob module index. When positive,
 this specifies the n-th module (in the GrUB entry, zero based) to be used
diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig
index d5705e4bff..61f58aa829 100644
--- a/xen/arch/x86/Kconfig
+++ b/xen/arch/x86/Kconfig
@@ -331,8 +331,22 @@ config REQUIRE_NX
 	  was unavailable. However, if enabled, Xen will no longer boot on
 	  any CPU which is lacking NX support.
 
+config MICROCODE_LOADING
+	bool "Microcode loading"
+	default y
+	help
+	  Microcode updates for CPUs fix errata and provide new functionality for
+	  software to work around bugs, such as the speculative execution
+	  vulnerabilities. It is common for OSes to carry updated microcode as
+	  software tends to get updated more frequently than firmware.
+
+	  For embedded environments where a full firmware/software stack is being
+	  provided, it might not be necessary for Xen to need to load microcode
+	  itself.
+
 config MICROCODE_SCAN_DEFAULT
 	bool "Scan for microcode by default"
+	depends on MICROCODE_LOADING
 	help
 	  During boot, Xen can scan the multiboot images for a CPIO archive
 	  containing CPU microcode to be loaded, which is Linux's mechanism for
diff --git a/xen/arch/x86/cpu/microcode/amd.c b/xen/arch/x86/cpu/microcode/amd.c
index 71b041c84b..c1ab6deb4d 100644
--- a/xen/arch/x86/cpu/microcode/amd.c
+++ b/xen/arch/x86/cpu/microcode/amd.c
@@ -561,11 +561,11 @@ static const char __initconst amd_cpio_path[] =
     "kernel/x86/microcode/AuthenticAMD.bin";
 
 static const struct microcode_ops __initconst_cf_clobber amd_ucode_ops = {
-    .cpu_request_microcode            = cpu_request_microcode,
     .collect_cpu_info                 = collect_cpu_info,
-    .apply_microcode                  = apply_microcode,
-    .compare                          = amd_compare,
-    .cpio_path                        = amd_cpio_path,
+    .cpu_request_microcode            = MICROCODE_OP(cpu_request_microcode),
+    .apply_microcode                  = MICROCODE_OP(apply_microcode),
+    .compare                          = MICROCODE_OP(amd_compare),
+    .cpio_path                        = MICROCODE_OP(amd_cpio_path),
 };
 
 void __init ucode_probe_amd(struct microcode_ops *ops)
@@ -574,7 +574,8 @@ void __init ucode_probe_amd(struct microcode_ops *ops)
      * The Entrysign vulnerability (SB-7033, CVE-2024-36347) affects Zen1-5
      * CPUs.  Taint Xen if digest checking is turned off.
      */
-    if ( boot_cpu_data.family >= 0x17 && boot_cpu_data.family <= 0x1a &&
+    if ( IS_ENABLED(CONFIG_MICROCODE_LOADING) &&
+         boot_cpu_data.family >= 0x17 && boot_cpu_data.family <= 0x1a &&
          !opt_digest_check )
     {
         printk(XENLOG_WARNING
@@ -614,8 +615,9 @@ void __init amd_check_entrysign(void)
     unsigned int curr_rev;
     uint8_t fixed_rev;
 
-    if ( boot_cpu_data.vendor != X86_VENDOR_AMD ||
-         boot_cpu_data.family < 0x17 ||
+    if ( !IS_ENABLED(CONFIG_MICROCODE_LOADING)  ||
+         boot_cpu_data.vendor != X86_VENDOR_AMD ||
+         boot_cpu_data.family < 0x17            ||
          boot_cpu_data.family > 0x1a )
         return;
 
diff --git a/xen/arch/x86/cpu/microcode/core.c b/xen/arch/x86/cpu/microcode/core.c
index dabdb95b4c..efaf808f1a 100644
--- a/xen/arch/x86/cpu/microcode/core.c
+++ b/xen/arch/x86/cpu/microcode/core.c
@@ -58,7 +58,7 @@
  */
 #define MICROCODE_UPDATE_TIMEOUT_US 1000000
 
-static bool __initdata ucode_mod_forced;
+static bool __initdata __maybe_unused ucode_mod_forced;
 static unsigned int nr_cores;
 
 /*
@@ -104,6 +104,7 @@ static int __initdata opt_mod_idx;
 static bool __initdata opt_scan = IS_ENABLED(CONFIG_MICROCODE_SCAN_DEFAULT);
 bool __ro_after_init opt_digest_check = true;
 
+#ifdef CONFIG_MICROCODE_LOADING
 /*
  * Used by the EFI path only, when xen.cfg identifies an explicit microcode
  * file.  Overrides ucode=<int>|scan on the regular command line.
@@ -162,6 +163,7 @@ static int __init cf_check parse_ucode(const char *s)
     return rc;
 }
 custom_param("ucode", parse_ucode);
+#endif /* CONFIG_MICROCODE_LOADING */
 
 static struct microcode_ops __ro_after_init ucode_ops;
 
@@ -469,7 +471,7 @@ struct ucode_buf {
     char buffer[];
 };
 
-static long cf_check ucode_update_hcall_cont(void *data)
+static long cf_check __maybe_unused ucode_update_hcall_cont(void *data)
 {
     struct microcode_patch *patch = NULL;
     int ret, result;
@@ -613,6 +615,7 @@ static long cf_check ucode_update_hcall_cont(void *data)
     return ret;
 }
 
+#ifdef CONFIG_MICROCODE_LOADING
 int ucode_update_hcall(XEN_GUEST_HANDLE(const_void) buf,
                        unsigned long len, unsigned int flags)
 {
@@ -645,6 +648,7 @@ int ucode_update_hcall(XEN_GUEST_HANDLE(const_void) buf,
      */
     return continue_hypercall_on_cpu(0, ucode_update_hcall_cont, buffer);
 }
+#endif /* CONFIG_MICROCODE_LOADING */
 
 /* Load a cached update to current cpu */
 int microcode_update_one(void)
@@ -658,7 +662,7 @@ int microcode_update_one(void)
     if ( ucode_ops.collect_cpu_info )
         alternative_vcall(ucode_ops.collect_cpu_info);
 
-    if ( !ucode_ops.apply_microcode )
+    if ( !IS_ENABLED(CONFIG_MICROCODE_LOADING) || !ucode_ops.apply_microcode )
         return -EOPNOTSUPP;
 
     spin_lock(&microcode_mutex);
@@ -678,6 +682,7 @@ int microcode_update_one(void)
  */
 static int __initdata early_mod_idx = -1;
 
+#ifdef CONFIG_MICROCODE_LOADING
 static int __init cf_check microcode_init_cache(void)
 {
     struct boot_info *bi = &xen_boot_info;
@@ -734,6 +739,7 @@ static int __init cf_check microcode_init_cache(void)
     return rc;
 }
 presmp_initcall(microcode_init_cache);
+#endif /* CONFIG_MICROCODE_LOADING */
 
 /*
  * There are several tasks:
@@ -898,6 +904,9 @@ int __init early_microcode_init(struct boot_info *bi)
 
     printk(XENLOG_INFO "BSP microcode revision: 0x%08x\n", this_cpu(cpu_sig).rev);
 
+    if ( !IS_ENABLED(CONFIG_MICROCODE_LOADING) )
+        return -ENODEV;
+
     /*
      * Some hypervisors deliberately report a microcode revision of -1 to
      * mean that they will not accept microcode updates.
diff --git a/xen/arch/x86/cpu/microcode/intel.c b/xen/arch/x86/cpu/microcode/intel.c
index 281993e725..dac464961a 100644
--- a/xen/arch/x86/cpu/microcode/intel.c
+++ b/xen/arch/x86/cpu/microcode/intel.c
@@ -408,17 +408,20 @@ static const char __initconst intel_cpio_path[] =
     "kernel/x86/microcode/GenuineIntel.bin";
 
 static const struct microcode_ops __initconst_cf_clobber intel_ucode_ops = {
-    .cpu_request_microcode            = cpu_request_microcode,
     .collect_cpu_info                 = collect_cpu_info,
-    .apply_microcode                  = apply_microcode,
-    .compare                          = intel_compare,
-    .cpio_path                        = intel_cpio_path,
+    .cpu_request_microcode            = MICROCODE_OP(cpu_request_microcode),
+    .apply_microcode                  = MICROCODE_OP(apply_microcode),
+    .compare                          = MICROCODE_OP(intel_compare),
+    .cpio_path                        = MICROCODE_OP(intel_cpio_path),
 };
 
 void __init ucode_probe_intel(struct microcode_ops *ops)
 {
     *ops = intel_ucode_ops;
 
+    if ( !IS_ENABLED(CONFIG_MICROCODE_LOADING) )
+        return;
+
     if ( !can_load_microcode() )
         ops->apply_microcode = NULL;
 }
diff --git a/xen/arch/x86/cpu/microcode/private.h b/xen/arch/x86/cpu/microcode/private.h
index e6c965dc99..77ce816c1a 100644
--- a/xen/arch/x86/cpu/microcode/private.h
+++ b/xen/arch/x86/cpu/microcode/private.h
@@ -8,6 +8,9 @@
 /* Opaque.  Internals are vendor-specific. */
 struct microcode_patch;
 
+/* Aids dead-code elimination of the static hooks */
+#define MICROCODE_OP(x) (IS_ENABLED(CONFIG_MICROCODE_LOADING) ? (x) : NULL)
+
 struct microcode_ops {
     /*
      * Parse a microcode container.  Format is vendor-specific.
diff --git a/xen/arch/x86/efi/efi-boot.h b/xen/arch/x86/efi/efi-boot.h
index 0194720003..42a2c46b5e 100644
--- a/xen/arch/x86/efi/efi-boot.h
+++ b/xen/arch/x86/efi/efi-boot.h
@@ -295,7 +295,8 @@ static void __init efi_arch_cfg_file_late(const EFI_LOADED_IMAGE *image,
 {
     union string name;
 
-    if ( read_section(image, L"ucode", &ucode, NULL) )
+    if ( !IS_ENABLED(CONFIG_MICROCODE_LOADING) ||
+         read_section(image, L"ucode", &ucode, NULL) )
         return;
 
     name.s = get_value(&cfg, section, "ucode");
diff --git a/xen/arch/x86/platform_hypercall.c b/xen/arch/x86/platform_hypercall.c
index f8eca48170..2ac9fc2d96 100644
--- a/xen/arch/x86/platform_hypercall.c
+++ b/xen/arch/x86/platform_hypercall.c
@@ -317,8 +317,11 @@ ret_t do_platform_op(
     {
         XEN_GUEST_HANDLE(const_void) data;
 
-        guest_from_compat_handle(data, op->u.microcode.data);
+        ret = -EOPNOTSUPP;
+        if ( !IS_ENABLED(CONFIG_MICROCODE_LOADING) )
+            break;
 
+        guest_from_compat_handle(data, op->u.microcode.data);
         ret = ucode_update_hcall(data, op->u.microcode.length, 0);
         break;
     }
@@ -327,8 +330,11 @@ ret_t do_platform_op(
     {
         XEN_GUEST_HANDLE(const_void) data;
 
-        guest_from_compat_handle(data, op->u.microcode2.data);
+        ret = -EOPNOTSUPP;
+        if ( !IS_ENABLED(CONFIG_MICROCODE_LOADING) )
+            break;
 
+        guest_from_compat_handle(data, op->u.microcode2.data);
         ret = ucode_update_hcall(data, op->u.microcode2.length,
                                  op->u.microcode2.flags);
         break;
-- 
2.43.0


Re: [PATCH v4 1/5] x86/ucode: Add Kconfig option to remove microcode loading
Posted by Andrew Cooper 5 days, 17 hours ago
On 20/01/2026 9:38 am, Alejandro Vallejo wrote:
> diff --git a/xen/arch/x86/platform_hypercall.c b/xen/arch/x86/platform_hypercall.c
> index f8eca48170..2ac9fc2d96 100644
> --- a/xen/arch/x86/platform_hypercall.c
> +++ b/xen/arch/x86/platform_hypercall.c
> @@ -317,8 +317,11 @@ ret_t do_platform_op(
>      {
>          XEN_GUEST_HANDLE(const_void) data;
>  
> -        guest_from_compat_handle(data, op->u.microcode.data);
> +        ret = -EOPNOTSUPP;
> +        if ( !IS_ENABLED(CONFIG_MICROCODE_LOADING) )
> +            break;
>  
> +        guest_from_compat_handle(data, op->u.microcode.data);
>          ret = ucode_update_hcall(data, op->u.microcode.length, 0);
>          break;
>      }
> @@ -327,8 +330,11 @@ ret_t do_platform_op(
>      {
>          XEN_GUEST_HANDLE(const_void) data;
>  
> -        guest_from_compat_handle(data, op->u.microcode2.data);
> +        ret = -EOPNOTSUPP;
> +        if ( !IS_ENABLED(CONFIG_MICROCODE_LOADING) )
> +            break;
>  
> +        guest_from_compat_handle(data, op->u.microcode2.data);
>          ret = ucode_update_hcall(data, op->u.microcode2.length,
>                                   op->u.microcode2.flags);
>          break;

Very minor.  This diff looks like this because you've dropped the blank
line between guest_from_compat_handle() and ucode_update_hcall().  That
can also be fixed up on commit.

~Andrew

Re: [PATCH v4 1/5] x86/ucode: Add Kconfig option to remove microcode loading
Posted by Alejandro Vallejo 5 days, 16 hours ago
On Tue Jan 20, 2026 at 3:09 PM CET, Andrew Cooper wrote:
> On 20/01/2026 9:38 am, Alejandro Vallejo wrote:
>> diff --git a/xen/arch/x86/platform_hypercall.c b/xen/arch/x86/platform_hypercall.c
>> index f8eca48170..2ac9fc2d96 100644
>> --- a/xen/arch/x86/platform_hypercall.c
>> +++ b/xen/arch/x86/platform_hypercall.c
>> @@ -317,8 +317,11 @@ ret_t do_platform_op(
>>      {
>>          XEN_GUEST_HANDLE(const_void) data;
>>  
>> -        guest_from_compat_handle(data, op->u.microcode.data);
>> +        ret = -EOPNOTSUPP;
>> +        if ( !IS_ENABLED(CONFIG_MICROCODE_LOADING) )
>> +            break;
>>  
>> +        guest_from_compat_handle(data, op->u.microcode.data);
>>          ret = ucode_update_hcall(data, op->u.microcode.length, 0);
>>          break;
>>      }
>> @@ -327,8 +330,11 @@ ret_t do_platform_op(
>>      {
>>          XEN_GUEST_HANDLE(const_void) data;
>>  
>> -        guest_from_compat_handle(data, op->u.microcode2.data);
>> +        ret = -EOPNOTSUPP;
>> +        if ( !IS_ENABLED(CONFIG_MICROCODE_LOADING) )
>> +            break;
>>  
>> +        guest_from_compat_handle(data, op->u.microcode2.data);
>>          ret = ucode_update_hcall(data, op->u.microcode2.length,
>>                                   op->u.microcode2.flags);
>>          break;
>
> Very minor.  This diff looks like this because you've dropped the blank
> line between guest_from_compat_handle() and ucode_update_hcall().  That
> can also be fixed up on commit.

sure

>
> ~Andrew