On Thu Jan 15, 2026 at 4:17 PM CET, Julian Vetter wrote:
> Currently the CONFIG_REQUIRE_NX prevents booting XEN, if NX is disabled
> in the BIOS. AMD doesn't have a software-accessible MSR to re-enable it,
> so there is nothing we can do. The system is going to die anyway. But on
> Intel NX might just be hidden via IA32_MISC_ENABLE.XD_DISABLE. But the
> function to re-enable it is called after the check + panic in
> efi_arch_cpu. So, this patch removes the early check and moves the
> entire NX handling into a dedicated place.

Let me prefix this with I haven't looked at the discussion at large. However,
I'm guessing the BIOS is merely hiding NX and not precluding its use (I don't
think there's a way to do that). I also imagine it's not an AMD BIOS and rather
an OEM BIOS?

If so, commit message and comments need to reflect that.

Also, this might be a good time to reverse a mistake here on the polarity of
this option. It should've been CONFIG_OPT_NX, where having the option set allows
machines without NX to run and having it disabled mandates NX to be set.

That makes the option strictly additive and compatible with allyesconfig.

My .02, anyway

Cheers,
Alejandro

>
> Signed-off-by: Julian Vetter <[email protected]>
> ---
>  xen/arch/x86/boot/head.S         | 56 --------------------------------
>  xen/arch/x86/boot/trampoline.S   |  5 ++-
>  xen/arch/x86/cpu/intel.c         |  4 ---
>  xen/arch/x86/efi/efi-boot.h      | 12 -------
>  xen/arch/x86/include/asm/setup.h |  2 ++
>  xen/arch/x86/setup.c             | 46 ++++++++++++++++++++++++++
>  6 files changed, 50 insertions(+), 75 deletions(-)
>
> diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
> index 77bb7a9e21..7fb7fb1351 100644
> --- a/xen/arch/x86/boot/head.S
> +++ b/xen/arch/x86/boot/head.S
> @@ -133,7 +133,6 @@ multiboot2_header:
>  .Lbad_ldr_nbs: .asciz "ERR: Bootloader shutdown EFI x64 boot services!"
>  .Lbad_efi_msg: .asciz "ERR: EFI IA-32 platforms are not supported!"
>  .Lbag_alg_msg: .asciz "ERR: Xen must be loaded at a 2Mb boundary!"
> -.Lno_nx_msg:   .asciz "ERR: Not an NX-capable CPU!"
>  
>          .section .init.data, "aw", @progbits
>          .subsection 1 /* Put data here after the page tables (in x86_64.S). 
> */
> @@ -165,11 +164,6 @@ early_error: /* Here to improve the disassembly. */
>  .Lnot_aligned:
>          mov     $sym_offs(.Lbag_alg_msg), %ecx
>          jmp     .Lget_vtb
> -#ifdef CONFIG_REQUIRE_NX
> -.Lno_nx:
> -        mov     $sym_offs(.Lno_nx_msg), %ecx
> -        jmp     .Lget_vtb
> -#endif
>  .Lmb2_no_bs:
>          /*
>           * Ditto. Additionally, here there is a chance that Xen was started
> @@ -547,56 +541,6 @@ trampoline_setup:
>          bt      $cpufeat_bit(X86_FEATURE_LM),%edx
>          jnc     .Lbad_cpu
>  
> -        /*
> -         * Check for NX
> -         *   - If Xen was compiled requiring it simply assert it's
> -         *     supported. The trampoline already has the right constant.
> -         *   - Otherwise, update the trampoline EFER mask accordingly.
> -         */
> -        bt      $cpufeat_bit(X86_FEATURE_NX), %edx
> -        jc     .Lgot_nx
> -
> -        /*
> -         * NX appears to be unsupported, but it might be hidden.
> -         *
> -         * The feature is part of the AMD64 spec, but the very first Intel
> -         * 64bit CPUs lacked the feature, and thereafter there was a
> -         * firmware knob to disable the feature. Undo the disable if
> -         * possible.
> -         *
> -         * All 64bit Intel CPUs support this MSR. If virtualised, expect
> -         * the hypervisor to either emulate the MSR or give us NX.
> -         */
> -        xor     %eax, %eax
> -        cpuid
> -        cmp     $X86_VENDOR_INTEL_EBX, %ebx
> -        jnz     .Lno_nx
> -        cmp     $X86_VENDOR_INTEL_EDX, %edx
> -        jnz     .Lno_nx
> -        cmp     $X86_VENDOR_INTEL_ECX, %ecx
> -        jnz     .Lno_nx
> -
> -        /* Clear the XD_DISABLE bit */
> -        mov     $MSR_IA32_MISC_ENABLE, %ecx
> -        rdmsr
> -        btr     $2, %edx
> -        jnc     .Lno_nx
> -        wrmsr
> -        orb     $MSR_IA32_MISC_ENABLE_XD_DISABLE >> 32, 4 + 
> sym_esi(trampoline_misc_enable_off)
> -
> -        /* Check again for NX */
> -        mov     $0x80000001, %eax
> -        cpuid
> -        bt      $cpufeat_bit(X86_FEATURE_NX), %edx
> -        jnc     .Lno_nx
> -
> -.Lgot_nx:
> -#ifndef CONFIG_REQUIRE_NX
> -        /* Adjust EFER given that NX is present */
> -        orb     $EFER_NXE >> 8, 1 + sym_esi(trampoline_efer)
> -.Lno_nx:
> -#endif
> -
>          /* Stash TSC to calculate a good approximation of time-since-boot */
>          rdtsc
>          mov     %eax,     sym_esi(boot_tsc_stamp)
> diff --git a/xen/arch/x86/boot/trampoline.S b/xen/arch/x86/boot/trampoline.S
> index a92e399fbe..8e8d50cbdf 100644
> --- a/xen/arch/x86/boot/trampoline.S
> +++ b/xen/arch/x86/boot/trampoline.S
> @@ -144,10 +144,9 @@ gdt_48:
>  GLOBAL(trampoline_misc_enable_off)
>          .quad   0
>  
> -/* EFER OR-mask for boot paths.  SCE conditional on PV support, NX added 
> when available. */
> +/* EFER OR-mask for boot paths.  SCE conditional on PV support. */
>  GLOBAL(trampoline_efer)
> -        .long   EFER_LME | (EFER_SCE * IS_ENABLED(CONFIG_PV)) | \
> -                (EFER_NXE * IS_ENABLED(CONFIG_REQUIRE_NX))
> +        .long   EFER_LME | (EFER_SCE * IS_ENABLED(CONFIG_PV))
>  
>  GLOBAL(trampoline_xen_phys_start)
>          .long   0
> diff --git a/xen/arch/x86/cpu/intel.c b/xen/arch/x86/cpu/intel.c
> index b76797cb9a..e8cf51e853 100644
> --- a/xen/arch/x86/cpu/intel.c
> +++ b/xen/arch/x86/cpu/intel.c
> @@ -351,10 +351,6 @@ static void cf_check early_init_intel(struct cpuinfo_x86 
> *c)
>       if (c->x86 == 15 && c->x86_cache_alignment == 64)
>               c->x86_cache_alignment = 128;
>  
> -     if (c == &boot_cpu_data &&
> -         bootsym(trampoline_misc_enable_off) & 
> MSR_IA32_MISC_ENABLE_XD_DISABLE)
> -             printk(KERN_INFO "re-enabled NX (Execute Disable) 
> protection\n");
> -
>       intel_unlock_cpuid_leaves(c);
>  
>       /* CPUID workaround for Intel 0F33/0F34 CPU */
> diff --git a/xen/arch/x86/efi/efi-boot.h b/xen/arch/x86/efi/efi-boot.h
> index 0194720003..8dfd549f12 100644
> --- a/xen/arch/x86/efi/efi-boot.h
> +++ b/xen/arch/x86/efi/efi-boot.h
> @@ -748,18 +748,6 @@ static void __init efi_arch_cpu(void)
>      if ( (eax >> 16) == 0x8000 && eax > 0x80000000U )
>      {
>          caps[FEATURESET_e1d] = cpuid_edx(0x80000001U);
> -
> -        /*
> -         * This check purposefully doesn't use cpu_has_nx because
> -         * cpu_has_nx bypasses the boot_cpu_data read if Xen was compiled
> -         * with CONFIG_REQUIRE_NX
> -         */
> -        if ( IS_ENABLED(CONFIG_REQUIRE_NX) &&
> -             !boot_cpu_has(X86_FEATURE_NX) )
> -            blexit(L"This build of Xen requires NX support");
> -
> -        if ( cpu_has_nx )
> -            trampoline_efer |= EFER_NXE;
>      }
>  }
>  
> diff --git a/xen/arch/x86/include/asm/setup.h 
> b/xen/arch/x86/include/asm/setup.h
> index b01e83a8ed..16f53725ca 100644
> --- a/xen/arch/x86/include/asm/setup.h
> +++ b/xen/arch/x86/include/asm/setup.h
> @@ -70,4 +70,6 @@ extern bool opt_dom0_msr_relaxed;
>  
>  #define max_init_domid (0)
>  
> +void nx_init(void);
> +
>  #endif
> diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c
> index 27c63d1d97..608720b717 100644
> --- a/xen/arch/x86/setup.c
> +++ b/xen/arch/x86/setup.c
> @@ -1119,6 +1119,50 @@ static struct domain *__init create_dom0(struct 
> boot_info *bi)
>      return d;
>  }
>  
> +void __init nx_init(void)
> +{
> +    uint64_t misc_enable;
> +    uint32_t eax, ebx, ecx, edx;
> +
> +    if ( !boot_cpu_has(X86_FEATURE_NX) )
> +    {
> +        /* Intel: try to unhide NX by clearing XD_DISABLE */
> +        cpuid(0, &eax, &ebx, &ecx, &edx);
> +        if ( ebx == X86_VENDOR_INTEL_EBX &&
> +             ecx == X86_VENDOR_INTEL_ECX &&
> +             edx == X86_VENDOR_INTEL_EDX )
> +        {
> +            rdmsrl(MSR_IA32_MISC_ENABLE, misc_enable);
> +            if ( misc_enable & MSR_IA32_MISC_ENABLE_XD_DISABLE )
> +            {
> +                misc_enable &= ~MSR_IA32_MISC_ENABLE_XD_DISABLE;
> +                wrmsrl(MSR_IA32_MISC_ENABLE, misc_enable);
> +
> +                /* Re-read CPUID after having cleared XD_DISABLE */
> +                boot_cpu_data.x86_capability[FEATURESET_e1d] = 
> cpuid_edx(0x80000001U);
> +
> +                /* Adjust misc_enable_off for secondary startup and wakeup 
> code */
> +                bootsym(trampoline_misc_enable_off) |= 
> MSR_IA32_MISC_ENABLE_XD_DISABLE;
> +                printk(KERN_INFO "re-enabled NX (Execute Disable) 
> protection\n");
> +            }
> +        }
> +        /* AMD: nothing we can do - NX must be enabled in BIOS */
> +    }
> +
> +    /* Enable EFER.NXE only if NX is available */
> +    if ( boot_cpu_has(X86_FEATURE_NX) )
> +    {
> +        if ( !(read_efer() & EFER_NXE) )
> +            write_efer(read_efer() | EFER_NXE);
> +
> +        /* Adjust trampoline_efer for secondary startup and wakeup code */
> +        bootsym(trampoline_efer) |= EFER_NXE;
> +    }
> +
> +    if ( IS_ENABLED(CONFIG_REQUIRE_NX) && !boot_cpu_has(X86_FEATURE_NX) )
> +        panic("This build of Xen requires NX support\n");
> +}
> +
>  /* How much of the directmap is prebuilt at compile time. */
>  #define PREBUILT_MAP_LIMIT (1 << L2_PAGETABLE_SHIFT)
>  
> @@ -1159,6 +1203,8 @@ void asmlinkage __init noreturn __start_xen(void)
>      rdmsrl(MSR_EFER, this_cpu(efer));
>      asm volatile ( "mov %%cr4,%0" : "=r" (info->cr4) );
>  
> +    nx_init();
> +
>      /* Enable NMIs.  Our loader (e.g. Tboot) may have left them disabled. */
>      enable_nmis();
>  


Reply via email to