diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-14 14:24:45 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-14 14:24:45 -0700 |
| commit | 9f2bb6c7b364f186aa37c524f6df33bd488d4efa (patch) | |
| tree | bb3334b84c1bfaaed5ac763282d1f8a38d3db946 /arch | |
| parent | 49b30f3e9cde3403b8719dfcddc923bce572b69c (diff) | |
| parent | 124ad3034ec0029b65178f3ab8a6cdca5a0b0519 (diff) | |
Merge tag 'x86_cpu_for_7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 cpu updates from Dave Hansen:
- Complete LASS enabling: deal with vsyscall and EFI
The existing Linear Address Space Separation (LASS) support punted
on support for common EFI and vsyscall configs. Complete the
implementation by supporting EFI and vsyscall=xonly.
- Clean up CPUID usage in newer Intel "avs" audio driver and update the
x86-cpuid-db file
* tag 'x86_cpu_for_7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
tools/x86/kcpuid: Update bitfields to x86-cpuid-db v3.0
ASoC: Intel: avs: Include CPUID header at file scope
ASoC: Intel: avs: Check maximum valid CPUID leaf
x86/cpu: Remove LASS restriction on vsyscall emulation
x86/vsyscall: Disable LASS if vsyscall mode is set to EMULATE
x86/vsyscall: Restore vsyscall=xonly mode under LASS
x86/traps: Consolidate user fixups in the #GP handler
x86/vsyscall: Reorganize the page fault emulation code
x86/cpu: Remove LASS restriction on EFI
x86/efi: Disable LASS while executing runtime services
x86/cpu: Defer LASS enabling until userspace comes up
Diffstat (limited to 'arch')
| -rw-r--r-- | arch/x86/entry/vsyscall/vsyscall_64.c | 91 | ||||
| -rw-r--r-- | arch/x86/include/asm/vsyscall.h | 13 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/common.c | 35 | ||||
| -rw-r--r-- | arch/x86/kernel/traps.c | 12 | ||||
| -rw-r--r-- | arch/x86/kernel/umip.c | 3 | ||||
| -rw-r--r-- | arch/x86/mm/fault.c | 2 | ||||
| -rw-r--r-- | arch/x86/platform/efi/efi_64.c | 35 |
7 files changed, 129 insertions, 62 deletions
diff --git a/arch/x86/entry/vsyscall/vsyscall_64.c b/arch/x86/entry/vsyscall/vsyscall_64.c index 4bd1e271bb22..ea36de9fa864 100644 --- a/arch/x86/entry/vsyscall/vsyscall_64.c +++ b/arch/x86/entry/vsyscall/vsyscall_64.c @@ -23,7 +23,7 @@ * soon be no new userspace code that will ever use a vsyscall. * * The code in this file emulates vsyscalls when notified of a page - * fault to a vsyscall address. + * fault or a general protection fault to a vsyscall address. */ #include <linux/kernel.h> @@ -62,6 +62,11 @@ static int __init vsyscall_setup(char *str) else return -EINVAL; + if (cpu_feature_enabled(X86_FEATURE_LASS) && vsyscall_mode == EMULATE) { + setup_clear_cpu_cap(X86_FEATURE_LASS); + pr_warn_once("x86/cpu: Disabling LASS due to vsyscall=emulate\n"); + } + return 0; } @@ -111,48 +116,17 @@ static bool write_ok_or_segv(unsigned long ptr, size_t size) } } -bool emulate_vsyscall(unsigned long error_code, - struct pt_regs *regs, unsigned long address) +static bool __emulate_vsyscall(struct pt_regs *regs, unsigned long address) { unsigned long caller; int vsyscall_nr, syscall_nr, tmp; long ret; unsigned long orig_dx; - /* Write faults or kernel-privilege faults never get fixed up. */ - if ((error_code & (X86_PF_WRITE | X86_PF_USER)) != X86_PF_USER) + /* Confirm that the fault happened in 64-bit user mode */ + if (!user_64bit_mode(regs)) return false; - /* - * Assume that faults at regs->ip are because of an - * instruction fetch. Return early and avoid - * emulation for faults during data accesses: - */ - if (address != regs->ip) { - /* Failed vsyscall read */ - if (vsyscall_mode == EMULATE) - return false; - - /* - * User code tried and failed to read the vsyscall page. - */ - warn_bad_vsyscall(KERN_INFO, regs, "vsyscall read attempt denied -- look up the vsyscall kernel parameter if you need a workaround"); - return false; - } - - /* - * X86_PF_INSTR is only set when NX is supported. When - * available, use it to double-check that the emulation code - * is only being used for instruction fetches: - */ - if (cpu_feature_enabled(X86_FEATURE_NX)) - WARN_ON_ONCE(!(error_code & X86_PF_INSTR)); - - /* - * No point in checking CS -- the only way to get here is a user mode - * trap to a high address, which means that we're in 64-bit user code. - */ - if (vsyscall_mode == NONE) { warn_bad_vsyscall(KERN_INFO, regs, "vsyscall attempted with vsyscall=none"); @@ -280,6 +254,53 @@ sigsegv: return true; } +bool emulate_vsyscall_pf(unsigned long error_code, struct pt_regs *regs, + unsigned long address) +{ + /* Write faults or kernel-privilege faults never get fixed up. */ + if ((error_code & (X86_PF_WRITE | X86_PF_USER)) != X86_PF_USER) + return false; + + /* + * Assume that faults at regs->ip are because of an instruction + * fetch. Return early and avoid emulation for faults during + * data accesses: + */ + if (address != regs->ip) { + /* Failed vsyscall read */ + if (vsyscall_mode == EMULATE) + return false; + + /* User code tried and failed to read the vsyscall page. */ + warn_bad_vsyscall(KERN_INFO, regs, + "vsyscall read attempt denied -- look up the vsyscall kernel parameter if you need a workaround"); + return false; + } + + /* + * X86_PF_INSTR is only set when NX is supported. When + * available, use it to double-check that the emulation code + * is only being used for instruction fetches: + */ + if (cpu_feature_enabled(X86_FEATURE_NX)) + WARN_ON_ONCE(!(error_code & X86_PF_INSTR)); + + return __emulate_vsyscall(regs, address); +} + +bool emulate_vsyscall_gp(struct pt_regs *regs) +{ + /* Without LASS, vsyscall accesses are expected to generate a #PF */ + if (!cpu_feature_enabled(X86_FEATURE_LASS)) + return false; + + /* Emulate only if the RIP points to the vsyscall address */ + if (!is_vsyscall_vaddr(regs->ip)) + return false; + + return __emulate_vsyscall(regs, regs->ip); +} + /* * A pseudo VMA to allow ptrace access for the vsyscall page. This only * covers the 64bit vsyscall page now. 32bit has a real VMA now and does diff --git a/arch/x86/include/asm/vsyscall.h b/arch/x86/include/asm/vsyscall.h index 472f0263dbc6..538053b1656a 100644 --- a/arch/x86/include/asm/vsyscall.h +++ b/arch/x86/include/asm/vsyscall.h @@ -14,12 +14,17 @@ extern void set_vsyscall_pgtable_user_bits(pgd_t *root); * Called on instruction fetch fault in vsyscall page. * Returns true if handled. */ -extern bool emulate_vsyscall(unsigned long error_code, - struct pt_regs *regs, unsigned long address); +bool emulate_vsyscall_pf(unsigned long error_code, struct pt_regs *regs, unsigned long address); +bool emulate_vsyscall_gp(struct pt_regs *regs); #else static inline void map_vsyscall(void) {} -static inline bool emulate_vsyscall(unsigned long error_code, - struct pt_regs *regs, unsigned long address) +static inline bool emulate_vsyscall_pf(unsigned long error_code, + struct pt_regs *regs, unsigned long address) +{ + return false; +} + +static inline bool emulate_vsyscall_gp(struct pt_regs *regs) { return false; } diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index ec0670114efa..8060cccd24d8 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -409,27 +409,29 @@ out: cr4_clear_bits(X86_CR4_UMIP); } -static __always_inline void setup_lass(struct cpuinfo_x86 *c) +static int enable_lass(unsigned int cpu) +{ + cr4_set_bits(X86_CR4_LASS); + + return 0; +} + +/* + * Finalize features that need to be enabled just before entering + * userspace. Note that this only runs on a single CPU. Use appropriate + * callbacks if all the CPUs need to reflect the same change. + */ +static int cpu_finalize_pre_userspace(void) { if (!cpu_feature_enabled(X86_FEATURE_LASS)) - return; + return 0; - /* - * Legacy vsyscall page access causes a #GP when LASS is active. - * Disable LASS because the #GP handler doesn't support vsyscall - * emulation. - * - * Also disable LASS when running under EFI, as some runtime and - * boot services rely on 1:1 mappings in the lower half. - */ - if (IS_ENABLED(CONFIG_X86_VSYSCALL_EMULATION) || - IS_ENABLED(CONFIG_EFI)) { - setup_clear_cpu_cap(X86_FEATURE_LASS); - return; - } + /* Runs on all online CPUs and future CPUs that come online. */ + cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/lass:enable", enable_lass, NULL); - cr4_set_bits(X86_CR4_LASS); + return 0; } +late_initcall(cpu_finalize_pre_userspace); /* These bits should not change their value after CPU init is finished. */ static const unsigned long cr4_pinned_mask = X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_UMIP | @@ -2061,7 +2063,6 @@ static void identify_cpu(struct cpuinfo_x86 *c) setup_smep(c); setup_smap(c); setup_umip(c); - setup_lass(c); /* * The vendor-specific functions might have changed features. diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 4dbff8ef9b1c..0ca3912ecb7f 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -70,6 +70,7 @@ #include <asm/tdx.h> #include <asm/cfi.h> #include <asm/msr.h> +#include <asm/vsyscall.h> #ifdef CONFIG_X86_64 #include <asm/x86_init.h> @@ -921,11 +922,6 @@ DEFINE_IDTENTRY_ERRORCODE(exc_general_protection) cond_local_irq_enable(regs); - if (static_cpu_has(X86_FEATURE_UMIP)) { - if (user_mode(regs) && fixup_umip_exception(regs)) - goto exit; - } - if (v8086_mode(regs)) { local_irq_enable(); handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code); @@ -940,6 +936,12 @@ DEFINE_IDTENTRY_ERRORCODE(exc_general_protection) if (fixup_vdso_exception(regs, X86_TRAP_GP, error_code, 0)) goto exit; + if (fixup_umip_exception(regs)) + goto exit; + + if (emulate_vsyscall_gp(regs)) + goto exit; + gp_user_force_sig_segv(regs, X86_TRAP_GP, error_code, desc); goto exit; } diff --git a/arch/x86/kernel/umip.c b/arch/x86/kernel/umip.c index d432f3824f0c..3ce99cbcf187 100644 --- a/arch/x86/kernel/umip.c +++ b/arch/x86/kernel/umip.c @@ -354,6 +354,9 @@ bool fixup_umip_exception(struct pt_regs *regs) void __user *uaddr; struct insn insn; + if (!cpu_feature_enabled(X86_FEATURE_UMIP)) + return false; + if (!regs) return false; diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index b83a06739b51..f0e77e084482 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -1314,7 +1314,7 @@ void do_user_addr_fault(struct pt_regs *regs, * to consider the PF_PK bit. */ if (is_vsyscall_vaddr(address)) { - if (emulate_vsyscall(error_code, regs, address)) + if (emulate_vsyscall_pf(error_code, regs, address)) return; } #endif diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c index b4409df2105a..5861008eab22 100644 --- a/arch/x86/platform/efi/efi_64.c +++ b/arch/x86/platform/efi/efi_64.c @@ -55,6 +55,7 @@ */ static u64 efi_va = EFI_VA_START; static struct mm_struct *efi_prev_mm; +static unsigned long efi_cr4_lass; /* * We need our own copy of the higher levels of the page tables @@ -443,16 +444,50 @@ static void efi_leave_mm(void) unuse_temporary_mm(efi_prev_mm); } +/* + * Toggle LASS to allow EFI to access any 1:1 mapped region in the lower + * half. + * + * Disable LASS only after switching to EFI-mm, as userspace is not + * mapped in it. Similar to EFI-mm, these rely on preemption being + * disabled and the calls being serialized. + */ + +static void efi_disable_lass(void) +{ + if (!cpu_feature_enabled(X86_FEATURE_LASS)) + return; + + lockdep_assert_preemption_disabled(); + + /* Save current CR4.LASS state */ + efi_cr4_lass = cr4_read_shadow() & X86_CR4_LASS; + cr4_clear_bits(efi_cr4_lass); +} + +static void efi_enable_lass(void) +{ + if (!cpu_feature_enabled(X86_FEATURE_LASS)) + return; + + lockdep_assert_preemption_disabled(); + + /* Reprogram CR4.LASS only if it was set earlier */ + cr4_set_bits(efi_cr4_lass); +} + void arch_efi_call_virt_setup(void) { efi_sync_low_kernel_mappings(); efi_fpu_begin(); firmware_restrict_branch_speculation_start(); efi_enter_mm(); + efi_disable_lass(); } void arch_efi_call_virt_teardown(void) { + efi_enable_lass(); efi_leave_mm(); firmware_restrict_branch_speculation_end(); efi_fpu_end(); |
