diff mbox series

[SRU,Xenial,v2,2/4] UBUNTU: SAUCE: x86/speculation: Cleanup IBRS runtime control handling

Message ID 20181213132102.23677-3-juergh@canonical.com
State New
Headers show
Series None | expand

Commit Message

Juerg Haefliger Dec. 13, 2018, 1:21 p.m. UTC
In Ubuntu, we have runtime control for enabling/disabling IBRS via the
commandline ("noibrs") and through the proc interface
/proc/sys/kernel/ibrs_enabled. This commit simplifies the current
(probably broken) implementation by merging it with all the IBRS-related
upstream changes from previous commits.

What we have now is the upstream implementation for detecting the presence
of IBRS support. This commit adds a global state variable 'ibrs_enabled'
which is set to 1 if the CPU supports IBRS but can be overridden via the
commandline "noibrs" switch or by writting 0, 1 or 2 to
/proc/sys/kernel/ibrs_enabled at runtime.

Note that the runtime controls are disabled if the CPU runs in Enhanced
IBRS mode.

CVE-2017-5715

Signed-off-by: Juerg Haefliger <juergh@canonical.com>
---
 arch/x86/include/asm/mwait.h         |   6 +-
 arch/x86/include/asm/nospec-branch.h |  20 +++++
 arch/x86/include/asm/spec_ctrl.h     |  10 ++-
 arch/x86/kernel/cpu/bugs.c           |  45 ++++-------
 arch/x86/kernel/cpu/microcode/core.c |  11 ---
 arch/x86/kernel/process.c            |  10 +--
 arch/x86/kernel/smpboot.c            |   6 +-
 arch/x86/lib/delay.c                 |   8 +-
 include/linux/smp.h                  |  46 -----------
 kernel/smp.c                         |  28 -------
 kernel/sysctl.c                      | 115 ++++++++++++++++-----------
 11 files changed, 123 insertions(+), 182 deletions(-)

Comments

Stefan Bader Jan. 9, 2019, 1:32 p.m. UTC | #1
On 13.12.18 14:21, Juerg Haefliger wrote:
> In Ubuntu, we have runtime control for enabling/disabling IBRS via the
> commandline ("noibrs") and through the proc interface
> /proc/sys/kernel/ibrs_enabled. This commit simplifies the current
> (probably broken) implementation by merging it with all the IBRS-related
> upstream changes from previous commits.
> 
> What we have now is the upstream implementation for detecting the presence
> of IBRS support. This commit adds a global state variable 'ibrs_enabled'
> which is set to 1 if the CPU supports IBRS but can be overridden via the
> commandline "noibrs" switch or by writting 0, 1 or 2 to
> /proc/sys/kernel/ibrs_enabled at runtime.
> 
> Note that the runtime controls are disabled if the CPU runs in Enhanced
> IBRS mode.
> 
> CVE-2017-5715
> 
> Signed-off-by: Juerg Haefliger <juergh@canonical.com>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
> ---

Ok, right, so that mutex I complained about was declared in smp.c and exported
in smp.h and then moved into sysctl.c directly. Looks ok then.

>  arch/x86/include/asm/mwait.h         |   6 +-
>  arch/x86/include/asm/nospec-branch.h |  20 +++++
>  arch/x86/include/asm/spec_ctrl.h     |  10 ++-
>  arch/x86/kernel/cpu/bugs.c           |  45 ++++-------
>  arch/x86/kernel/cpu/microcode/core.c |  11 ---
>  arch/x86/kernel/process.c            |  10 +--
>  arch/x86/kernel/smpboot.c            |   6 +-
>  arch/x86/lib/delay.c                 |   8 +-
>  include/linux/smp.h                  |  46 -----------
>  kernel/smp.c                         |  28 -------
>  kernel/sysctl.c                      | 115 ++++++++++++++++-----------
>  11 files changed, 123 insertions(+), 182 deletions(-)
> 
> diff --git a/arch/x86/include/asm/mwait.h b/arch/x86/include/asm/mwait.h
> index 9821763b02cf..9bd760758640 100644
> --- a/arch/x86/include/asm/mwait.h
> +++ b/arch/x86/include/asm/mwait.h
> @@ -106,15 +106,13 @@ static inline void mwait_idle_with_hints(unsigned long eax, unsigned long ecx)
>  			mb();
>  		}
>  
> -		if (ibrs_inuse)
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +		ubuntu_restrict_branch_speculation_end();
>  
>  		__monitor((void *)&current_thread_info()->flags, 0, 0);
>  		if (!need_resched())
>  			__mwait(eax, ecx);
>  
> -		if (ibrs_inuse)
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +		ubuntu_restrict_branch_speculation_start();
>  	}
>  	current_clr_polling();
>  }
> diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
> index dcc7b0348fbc..a6120d43caa7 100644
> --- a/arch/x86/include/asm/nospec-branch.h
> +++ b/arch/x86/include/asm/nospec-branch.h
> @@ -188,6 +188,10 @@
>  extern unsigned int ibpb_enabled;
>  int set_ibpb_enabled(unsigned int);
>  
> +/* The IBRS runtime control knob */
> +extern unsigned int ibrs_enabled;
> +int set_ibrs_enabled(unsigned int);
> +
>  /* The Spectre V2 mitigation variants */
>  enum spectre_v2_mitigation {
>  	SPECTRE_V2_NONE,
> @@ -275,6 +279,22 @@ do {									\
>  	preempt_enable();						\
>  } while (0)
>  
> +#define ubuntu_restrict_branch_speculation_start()			\
> +do {									\
> +	u64 val = x86_spec_ctrl_base | SPEC_CTRL_IBRS;			\
> +									\
> +	if (ibrs_enabled)						\
> +		native_wrmsrl(MSR_IA32_SPEC_CTRL, val);			\
> +} while (0)
> +
> +#define ubuntu_restrict_branch_speculation_end()			\
> +do {									\
> +	u64 val = x86_spec_ctrl_base;					\
> +									\
> +	if (ibrs_enabled)						\
> +		native_wrmsrl(MSR_IA32_SPEC_CTRL, val);			\
> + } while (0)
> +
>  #endif /* __ASSEMBLY__ */
>  
>  /*
> diff --git a/arch/x86/include/asm/spec_ctrl.h b/arch/x86/include/asm/spec_ctrl.h
> index 49c3b0a83e9f..a5d93d23390e 100644
> --- a/arch/x86/include/asm/spec_ctrl.h
> +++ b/arch/x86/include/asm/spec_ctrl.h
> @@ -8,7 +8,7 @@
>  
>  #ifdef __ASSEMBLY__
>  
> -.extern use_ibrs
> +.extern ibrs_enabled
>  
>  #define __ASM_ENABLE_IBRS			\
>  	pushq %rax;				\
> @@ -21,11 +21,13 @@
>  	popq %rdx;				\
>  	popq %rcx;				\
>  	popq %rax
> +
>  #define __ASM_ENABLE_IBRS_CLOBBER		\
>  	movl $MSR_IA32_SPEC_CTRL, %ecx;		\
>  	movl $0, %edx;				\
>  	movl $SPEC_CTRL_IBRS, %eax;		\
>  	wrmsr;
> +
>  #define __ASM_DISABLE_IBRS			\
>  	pushq %rax;				\
>  	pushq %rcx;				\
> @@ -39,7 +41,7 @@
>  	popq %rax
>  
>  .macro ENABLE_IBRS
> -	testl	$1, use_ibrs
> +	testl	$1, ibrs_enabled
>  	jz	10f
>  	__ASM_ENABLE_IBRS
>  	jmp 20f
> @@ -49,7 +51,7 @@
>  .endm
>  
>  .macro ENABLE_IBRS_CLOBBER
> -	testl	$1, use_ibrs
> +	testl	$1, ibrs_enabled
>  	jz	11f
>  	__ASM_ENABLE_IBRS_CLOBBER
>  	jmp 21f
> @@ -59,7 +61,7 @@
>  .endm
>  
>  .macro DISABLE_IBRS
> -	testl	$1, use_ibrs
> +	testl	$1, ibrs_enabled
>  	jz	9f
>  	__ASM_DISABLE_IBRS
>  9:
> diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
> index eeb89d781a9c..7a9940015af5 100644
> --- a/arch/x86/kernel/cpu/bugs.c
> +++ b/arch/x86/kernel/cpu/bugs.c
> @@ -40,6 +40,16 @@ static int __init noibpb_handler(char *str)
>  
>  early_param("noibpb", noibpb_handler);
>  
> +unsigned int noibrs = 0;
> +
> +static int __init noibrs_handler(char *str)
> +{
> +	noibrs = 1;
> +	return 0;
> +}
> +
> +early_param("noibrs", noibrs_handler);
> +
>  static void __init spectre_v2_select_mitigation(void);
>  static void __init ssb_select_mitigation(void);
>  static void __init l1tf_select_mitigation(void);
> @@ -410,18 +420,6 @@ specv2_set_mode:
>  			set_ibpb_enabled(1);   /* Enable IBPB */
>  	}
>  
> -	/* Initialize Indirect Branch Restricted Speculation if supported */
> -	if (boot_cpu_has(X86_FEATURE_IBRS)) {
> -		pr_info("Spectre v2 mitigation: Enabling Indirect Branch Restricted Speculation\n");
> -
> -		set_ibrs_supported();
> -		if (ibrs_inuse)
> -			sysctl_ibrs_enabled = 1;
> -        }
> -
> -	pr_info("Spectre v2 mitigation: Speculation control IBRS %s",
> -	        ibrs_supported ? "supported" : "not-supported");
> -
>  	/*
>  	 * If spectre v2 protection has been enabled, unconditionally fill
>  	 * RSB during a context switch; this protects against two independent
> @@ -433,21 +431,6 @@ specv2_set_mode:
>  	setup_force_cpu_cap(X86_FEATURE_RSB_CTXSW);
>  	pr_info("Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch\n");
>  
> -	/*
> -	 * If we have a full retpoline mode and then disable IBPB in kernel mode
> -	 * we do not require both.
> -	 */
> -	if (mode == SPECTRE_V2_RETPOLINE_AMD ||
> -	    mode == SPECTRE_V2_RETPOLINE_GENERIC)
> -	{
> -		if (ibrs_supported) {
> -			pr_info("Retpoline compiled kernel.  Defaulting IBRS to disabled");
> -			set_ibrs_disabled();
> -			if (!ibrs_inuse)
> -				sysctl_ibrs_enabled = 0;
> -		}
> -	}
> -
>  	/*
>  	 * Retpoline means the kernel is safe because it has no indirect
>  	 * branches. Enhanced IBRS protects firmware too, so, enable restricted
> @@ -462,6 +445,11 @@ specv2_set_mode:
>  	if (boot_cpu_has(X86_FEATURE_IBRS) && mode != SPECTRE_V2_IBRS_ENHANCED) {
>  		setup_force_cpu_cap(X86_FEATURE_USE_IBRS_FW);
>  		pr_info("Enabling Restricted Speculation for firmware calls\n");
> +
> +		if (!noibrs &&
> +		    mode != SPECTRE_V2_RETPOLINE_GENERIC &&
> +		    mode != SPECTRE_V2_RETPOLINE_AMD)
> +			set_ibrs_enabled(1);   /* Enable IBRS */
>  	}
>  }
>  
> @@ -871,8 +859,9 @@ static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr
>  		break;
>  
>  	case X86_BUG_SPECTRE_V2:
> -		return sprintf(buf, "%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
> +		return sprintf(buf, "%s%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
>  			       ibpb_enabled ? ", IBPB" : "",
> +			       ibrs_enabled == 2 ? ", IBRS (user space)" : ibrs_enabled ? ", IBRS" : "",
>  			       boot_cpu_has(X86_FEATURE_USE_IBRS_FW) ? ", IBRS_FW" : "",
>  			       spectre_v2_module_string());
>  
> diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
> index 84bd97f8eeab..4fe475c2f745 100644
> --- a/arch/x86/kernel/cpu/microcode/core.c
> +++ b/arch/x86/kernel/cpu/microcode/core.c
> @@ -439,17 +439,6 @@ static ssize_t reload_store(struct device *dev,
>  	if (!ret)
>  		perf_check_microcode();
>  
> -	/* Initialize Indirect Branch Restricted Speculation if supported */
> -	if (boot_cpu_has(X86_FEATURE_IBRS)) {
> -		pr_info("Enabling Indirect Branch Restricted Speculation\n");
> -
> -		mutex_lock(&spec_ctrl_mutex);
> -		set_ibrs_supported();
> -		if (ibrs_inuse)
> -			sysctl_ibrs_enabled = 1;
> -		mutex_unlock(&spec_ctrl_mutex);
> -	}
> -
>  	mutex_unlock(&microcode_mutex);
>  	put_online_cpus();
>  
> diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
> index a5b3404266eb..5587098a5823 100644
> --- a/arch/x86/kernel/process.c
> +++ b/arch/x86/kernel/process.c
> @@ -566,17 +566,15 @@ static void mwait_idle(void)
>  			smp_mb(); /* quirk */
>  		}
>  
> -		if (ibrs_inuse)
> -                        native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +		ubuntu_restrict_branch_speculation_end();
>  
>  		__monitor((void *)&current_thread_info()->flags, 0, 0);
> +
>  		if (!need_resched()) {
>  			__sti_mwait(0, 0);
> -			if (ibrs_inuse)
> -				native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +			ubuntu_restrict_branch_speculation_start();
>  		} else {
> -			if (ibrs_inuse)
> -				native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +			ubuntu_restrict_branch_speculation_start();
>  			local_irq_enable();
>  		}
>  		trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id());
> diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
> index 21c3bcbd69a2..6c9bb4db2ed7 100644
> --- a/arch/x86/kernel/smpboot.c
> +++ b/arch/x86/kernel/smpboot.c
> @@ -1697,15 +1697,13 @@ void native_play_dead(void)
>  	play_dead_common();
>  	tboot_shutdown(TB_SHUTDOWN_WFS);
>  
> -	if (ibrs_inuse)
> -		native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +	ubuntu_restrict_branch_speculation_end();
>  
>  	mwait_play_dead();	/* Only returns on failure */
>  	if (cpuidle_play_dead())
>  		hlt_play_dead();
>  
> -	if (ibrs_inuse)
> -		native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +	ubuntu_restrict_branch_speculation_start();
>  }
>  
>  #else /* ... !CONFIG_HOTPLUG_CPU */
> diff --git a/arch/x86/lib/delay.c b/arch/x86/lib/delay.c
> index 5b66d4417f8d..20e9a5ac91dc 100644
> --- a/arch/x86/lib/delay.c
> +++ b/arch/x86/lib/delay.c
> @@ -107,8 +107,8 @@ static void delay_mwaitx(unsigned long __loops)
>  	for (;;) {
>  		delay = min_t(u64, MWAITX_MAX_LOOPS, loops);
>  
> -		if (ibrs_inuse && (delay > IBRS_DISABLE_THRESHOLD))
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +		if (delay > IBRS_DISABLE_THRESHOLD)
> +			ubuntu_restrict_branch_speculation_end();
>  
>  		/*
>  		 * Use cpu_tss as a cacheline-aligned, seldomly
> @@ -123,8 +123,8 @@ static void delay_mwaitx(unsigned long __loops)
>  		 */
>  		__mwaitx(MWAITX_DISABLE_CSTATES, delay, MWAITX_ECX_TIMER_ENABLE);
>  
> -		if (ibrs_inuse && (delay > IBRS_DISABLE_THRESHOLD))
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +		if (delay > IBRS_DISABLE_THRESHOLD)
> +			ubuntu_restrict_branch_speculation_start();
>  
>  		end = rdtsc_ordered();
>  
> diff --git a/include/linux/smp.h b/include/linux/smp.h
> index 98ac8ff2afab..c4414074bd88 100644
> --- a/include/linux/smp.h
> +++ b/include/linux/smp.h
> @@ -50,52 +50,6 @@ void on_each_cpu_cond(bool (*cond_func)(int cpu, void *info),
>  
>  int smp_call_function_single_async(int cpu, struct call_single_data *csd);
>  
> -#ifdef CONFIG_X86
> -
> -#define IBxx_INUSE 	(1 << 0)
> -#define IBxx_SUPPORTED 	(1 << 1)
> -#define IBxx_DISABLED 	(1 << 2)
> -
> -/* indicate usage of IBRS to control execution speculation */
> -extern int use_ibrs;
> -extern u32 sysctl_ibrs_enabled;
> -extern struct mutex spec_ctrl_mutex;
> -
> -static inline int __check_ibrs_inuse(void)
> -{
> -	if (use_ibrs & IBxx_INUSE)
> -		return 1;
> -	else
> -		/* rmb to prevent wrong speculation for security */
> -		rmb();
> -	return 0;
> -}
> -
> -#define ibrs_inuse 	(__check_ibrs_inuse())
> -#define ibrs_supported	(use_ibrs & IBxx_SUPPORTED)
> -#define ibrs_disabled	(use_ibrs & IBxx_DISABLED)
> -
> -static inline void set_ibrs_supported(void)
> -{
> -	use_ibrs |= IBxx_SUPPORTED;
> -	if (!ibrs_disabled)
> -		use_ibrs |= IBxx_INUSE;
> -}
> -static inline void set_ibrs_disabled(void)
> -{
> -	use_ibrs |= IBxx_DISABLED;
> -	if (ibrs_inuse)
> -		use_ibrs &= ~IBxx_INUSE;
> -}
> -static inline void clear_ibrs_disabled(void)
> -{
> -	use_ibrs &= ~IBxx_DISABLED;
> -	if (ibrs_supported)
> -		use_ibrs |= IBxx_INUSE;
> -}
> -
> -#endif /* CONFIG_X86 */
> -
>  #ifdef CONFIG_SMP
>  
>  #include <linux/preempt.h>
> diff --git a/kernel/smp.c b/kernel/smp.c
> index 139681ddf916..48eb4fafc356 100644
> --- a/kernel/smp.c
> +++ b/kernel/smp.c
> @@ -499,22 +499,6 @@ EXPORT_SYMBOL(smp_call_function);
>  unsigned int setup_max_cpus = NR_CPUS;
>  EXPORT_SYMBOL(setup_max_cpus);
>  
> -#ifdef CONFIG_X86
> -/*
> - * use IBRS
> - * bit 0 = indicate if ibrs is currently in use
> - * bit 1 = indicate if system supports ibrs
> - * bit 2 = indicate if admin disables ibrs
> -*/
> -
> -int use_ibrs;
> -EXPORT_SYMBOL(use_ibrs);
> -#endif
> -
> -/* mutex to serialize IBRS & IBPB control changes */
> -DEFINE_MUTEX(spec_ctrl_mutex);
> -EXPORT_SYMBOL(spec_ctrl_mutex);
> -
>  /*
>   * Setup routine for controlling SMP activation
>   *
> @@ -538,18 +522,6 @@ static int __init nosmp(char *str)
>  
>  early_param("nosmp", nosmp);
>  
> -#ifdef CONFIG_X86
> -static int __init noibrs(char *str)
> -{
> -	set_ibrs_disabled();
> -
> -	return 0;
> -}
> -
> -early_param("noibrs", noibrs);
> -#endif
> -
> -
>  /* this is hard limit */
>  static int __init nrcpus(char *str)
>  {
> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> index d00f95726ac4..02df9c031a45 100644
> --- a/kernel/sysctl.c
> +++ b/kernel/sysctl.c
> @@ -202,8 +202,8 @@ static int proc_dostring_coredump(struct ctl_table *table, int write,
>  #endif
>  
>  #ifdef CONFIG_X86
> -int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write,
> -                 void __user *buffer, size_t *lenp, loff_t *ppos);
> +/* Mutex to serialize IBPB and IBRS runtime control changes */
> +DEFINE_MUTEX(spec_ctrl_mutex);
>  
>  unsigned int ibpb_enabled = 0;
>  EXPORT_SYMBOL(ibpb_enabled);   /* Required in some modules */
> @@ -252,6 +252,70 @@ static int ibpb_enabled_handler(struct ctl_table *table, int write,
>  
>  	return set_ibpb_enabled(__ibpb_enabled);
>  }
> +
> +unsigned int ibrs_enabled = 0;
> +EXPORT_SYMBOL(ibrs_enabled);   /* Required in some modules */
> +
> +static unsigned int __ibrs_enabled = 0;   /* procfs shadow variable */
> +
> +int set_ibrs_enabled(unsigned int val)
> +{
> +	int error = 0;
> +	unsigned int cpu;
> +	unsigned int prev = ibrs_enabled;
> +
> +	mutex_lock(&spec_ctrl_mutex);
> +
> +	/* Only enable/disable IBRS if the CPU supports it */
> +	if (boot_cpu_has(X86_FEATURE_USE_IBRS_FW)) {
> +		ibrs_enabled = val;
> +		if (ibrs_enabled != prev)
> +			pr_info("Spectre V2 : Spectre v2 mitigation: %s "
> +				"Indirect Branch Restricted Speculation%s\n",
> +				ibrs_enabled ? "Enabling" : "Disabling",
> +				ibrs_enabled == 2 ? " (user space)" : "");
> +
> +		if (ibrs_enabled == 0) {
> +			/* Always disable IBRS */
> +			u64 val = x86_spec_ctrl_base;
> +
> +			for_each_online_cpu(cpu)
> +				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, val);
> +		} else if (ibrs_enabled == 2) {
> +			/* Always enable IBRS, even in user space */
> +			u64 val = x86_spec_ctrl_base | SPEC_CTRL_IBRS;
> +
> +			for_each_online_cpu(cpu)
> +				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, val);
> +		}
> +	} else {
> +		ibrs_enabled = 0;
> +		if (val) {
> +			/* IBRS is not supported but we try to turn it on */
> +			error = -EINVAL;
> +		}
> +	}
> +
> +	/* Update the shadow variable */
> +	__ibrs_enabled = ibrs_enabled;
> +
> +	mutex_unlock(&spec_ctrl_mutex);
> +
> +	return error;
> +}
> +
> +static int ibrs_enabled_handler(struct ctl_table *table, int write,
> +                               void __user *buffer, size_t *lenp,
> +                               loff_t *ppos)
> +{
> +	int error;
> +
> +	error = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
> +	if (error)
> +		return error;
> +
> +	return set_ibrs_enabled(__ibrs_enabled);
> +}
>  #endif
>  
>  #ifdef CONFIG_MAGIC_SYSRQ
> @@ -290,9 +354,6 @@ extern struct ctl_table epoll_table[];
>  int sysctl_legacy_va_layout;
>  #endif
>  
> -u32 sysctl_ibrs_enabled = 0;
> -EXPORT_SYMBOL(sysctl_ibrs_enabled);
> -
>  /* The default sysctl tables: */
>  
>  static struct ctl_table sysctl_base_table[] = {
> @@ -1281,10 +1342,10 @@ static struct ctl_table kern_table[] = {
>  #ifdef CONFIG_X86
>  	{
>  		.procname       = "ibrs_enabled",
> -		.data           = &sysctl_ibrs_enabled,
> +		.data           = &__ibrs_enabled,
>  		.maxlen         = sizeof(unsigned int),
>  		.mode           = 0644,
> -		.proc_handler   = proc_dointvec_ibrs_ctrl,
> +		.proc_handler   = ibrs_enabled_handler,
>  		.extra1         = &zero,
>  		.extra2         = &two,
>  	},
> @@ -2455,46 +2516,6 @@ int proc_dointvec_minmax(struct ctl_table *table, int write,
>  				do_proc_dointvec_minmax_conv, &param);
>  }
>  
> -#ifdef CONFIG_X86
> -int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write,
> -	void __user *buffer, size_t *lenp, loff_t *ppos)
> -{
> -	int ret;
> -	unsigned int cpu;
> -
> -	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
> -	pr_debug("sysctl_ibrs_enabled = %u\n", sysctl_ibrs_enabled);
> -	pr_debug("before:use_ibrs = %d\n", use_ibrs);
> -	mutex_lock(&spec_ctrl_mutex);
> -	if (sysctl_ibrs_enabled == 0) {
> -		/* always set IBRS off */
> -		set_ibrs_disabled();
> -		if (ibrs_supported) {
> -			for_each_online_cpu(cpu)
> -				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> -		}
> -	} else if (sysctl_ibrs_enabled == 2) {
> -		/* always set IBRS on, even in user space */
> -		clear_ibrs_disabled();
> -		if (ibrs_supported) {
> -			for_each_online_cpu(cpu)
> -				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> -		} else {
> -			sysctl_ibrs_enabled = 0;
> -		}
> -	} else if (sysctl_ibrs_enabled == 1) {
> -		/* use IBRS in kernel */
> -		clear_ibrs_disabled();
> -		if (!ibrs_inuse)
> -			/* platform don't support ibrs */
> -			sysctl_ibrs_enabled = 0;
> -	}
> -	mutex_unlock(&spec_ctrl_mutex);
> -	pr_debug("after:use_ibrs = %d\n", use_ibrs);
> -	return ret;
> -}
> -#endif
> -
>  static void validate_coredump_safety(void)
>  {
>  #ifdef CONFIG_COREDUMP
>
Kleber Sacilotto de Souza Jan. 10, 2019, 9:48 a.m. UTC | #2
On 12/13/18 2:21 PM, Juerg Haefliger wrote:
> In Ubuntu, we have runtime control for enabling/disabling IBRS via the
> commandline ("noibrs") and through the proc interface
> /proc/sys/kernel/ibrs_enabled. This commit simplifies the current
> (probably broken) implementation by merging it with all the IBRS-related
> upstream changes from previous commits.
>
> What we have now is the upstream implementation for detecting the presence
> of IBRS support. This commit adds a global state variable 'ibrs_enabled'
> which is set to 1 if the CPU supports IBRS but can be overridden via the
> commandline "noibrs" switch or by writting 0, 1 or 2 to
> /proc/sys/kernel/ibrs_enabled at runtime.
>
> Note that the runtime controls are disabled if the CPU runs in Enhanced
> IBRS mode.
>
> CVE-2017-5715
>
> Signed-off-by: Juerg Haefliger <juergh@canonical.com>
> ---
>  arch/x86/include/asm/mwait.h         |   6 +-
>  arch/x86/include/asm/nospec-branch.h |  20 +++++
>  arch/x86/include/asm/spec_ctrl.h     |  10 ++-
>  arch/x86/kernel/cpu/bugs.c           |  45 ++++-------
>  arch/x86/kernel/cpu/microcode/core.c |  11 ---
>  arch/x86/kernel/process.c            |  10 +--
>  arch/x86/kernel/smpboot.c            |   6 +-
>  arch/x86/lib/delay.c                 |   8 +-
>  include/linux/smp.h                  |  46 -----------
>  kernel/smp.c                         |  28 -------
>  kernel/sysctl.c                      | 115 ++++++++++++++++-----------
>  11 files changed, 123 insertions(+), 182 deletions(-)
>
> diff --git a/arch/x86/include/asm/mwait.h b/arch/x86/include/asm/mwait.h
> index 9821763b02cf..9bd760758640 100644
> --- a/arch/x86/include/asm/mwait.h
> +++ b/arch/x86/include/asm/mwait.h
> @@ -106,15 +106,13 @@ static inline void mwait_idle_with_hints(unsigned long eax, unsigned long ecx)
>  			mb();
>  		}
>  
> -		if (ibrs_inuse)
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +		ubuntu_restrict_branch_speculation_end();
>  
>  		__monitor((void *)&current_thread_info()->flags, 0, 0);
>  		if (!need_resched())
>  			__mwait(eax, ecx);
>  
> -		if (ibrs_inuse)
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +		ubuntu_restrict_branch_speculation_start();
>  	}
>  	current_clr_polling();
>  }
> diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
> index dcc7b0348fbc..a6120d43caa7 100644
> --- a/arch/x86/include/asm/nospec-branch.h
> +++ b/arch/x86/include/asm/nospec-branch.h
> @@ -188,6 +188,10 @@
>  extern unsigned int ibpb_enabled;
>  int set_ibpb_enabled(unsigned int);
>  
> +/* The IBRS runtime control knob */
> +extern unsigned int ibrs_enabled;
> +int set_ibrs_enabled(unsigned int);
> +
>  /* The Spectre V2 mitigation variants */
>  enum spectre_v2_mitigation {
>  	SPECTRE_V2_NONE,
> @@ -275,6 +279,22 @@ do {									\
>  	preempt_enable();						\
>  } while (0)
>  
> +#define ubuntu_restrict_branch_speculation_start()			\
> +do {									\
> +	u64 val = x86_spec_ctrl_base | SPEC_CTRL_IBRS;			\
> +									\
> +	if (ibrs_enabled)						\
> +		native_wrmsrl(MSR_IA32_SPEC_CTRL, val);			\
> +} while (0)
> +
> +#define ubuntu_restrict_branch_speculation_end()			\
> +do {									\
> +	u64 val = x86_spec_ctrl_base;					\
> +									\
> +	if (ibrs_enabled)						\
> +		native_wrmsrl(MSR_IA32_SPEC_CTRL, val);			\
> + } while (0)
> +
>  #endif /* __ASSEMBLY__ */
>  
>  /*
> diff --git a/arch/x86/include/asm/spec_ctrl.h b/arch/x86/include/asm/spec_ctrl.h
> index 49c3b0a83e9f..a5d93d23390e 100644
> --- a/arch/x86/include/asm/spec_ctrl.h
> +++ b/arch/x86/include/asm/spec_ctrl.h
> @@ -8,7 +8,7 @@
>  
>  #ifdef __ASSEMBLY__
>  
> -.extern use_ibrs
> +.extern ibrs_enabled
>  
>  #define __ASM_ENABLE_IBRS			\
>  	pushq %rax;				\
> @@ -21,11 +21,13 @@
>  	popq %rdx;				\
>  	popq %rcx;				\
>  	popq %rax
> +
>  #define __ASM_ENABLE_IBRS_CLOBBER		\
>  	movl $MSR_IA32_SPEC_CTRL, %ecx;		\
>  	movl $0, %edx;				\
>  	movl $SPEC_CTRL_IBRS, %eax;		\
>  	wrmsr;
> +
>  #define __ASM_DISABLE_IBRS			\
>  	pushq %rax;				\
>  	pushq %rcx;				\
> @@ -39,7 +41,7 @@
>  	popq %rax
>  
>  .macro ENABLE_IBRS
> -	testl	$1, use_ibrs
> +	testl	$1, ibrs_enabled
>  	jz	10f
>  	__ASM_ENABLE_IBRS
>  	jmp 20f
> @@ -49,7 +51,7 @@
>  .endm
>  
>  .macro ENABLE_IBRS_CLOBBER
> -	testl	$1, use_ibrs
> +	testl	$1, ibrs_enabled
>  	jz	11f
>  	__ASM_ENABLE_IBRS_CLOBBER
>  	jmp 21f
> @@ -59,7 +61,7 @@
>  .endm
>  
>  .macro DISABLE_IBRS
> -	testl	$1, use_ibrs
> +	testl	$1, ibrs_enabled
>  	jz	9f
>  	__ASM_DISABLE_IBRS
>  9:
> diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
> index eeb89d781a9c..7a9940015af5 100644
> --- a/arch/x86/kernel/cpu/bugs.c
> +++ b/arch/x86/kernel/cpu/bugs.c
> @@ -40,6 +40,16 @@ static int __init noibpb_handler(char *str)
>  
>  early_param("noibpb", noibpb_handler);
>  
> +unsigned int noibrs = 0;
> +
> +static int __init noibrs_handler(char *str)
> +{
> +	noibrs = 1;
> +	return 0;
> +}
> +
> +early_param("noibrs", noibrs_handler);
> +
>  static void __init spectre_v2_select_mitigation(void);
>  static void __init ssb_select_mitigation(void);
>  static void __init l1tf_select_mitigation(void);
> @@ -410,18 +420,6 @@ specv2_set_mode:
>  			set_ibpb_enabled(1);   /* Enable IBPB */
>  	}
>  
> -	/* Initialize Indirect Branch Restricted Speculation if supported */
> -	if (boot_cpu_has(X86_FEATURE_IBRS)) {
> -		pr_info("Spectre v2 mitigation: Enabling Indirect Branch Restricted Speculation\n");
> -
> -		set_ibrs_supported();
> -		if (ibrs_inuse)
> -			sysctl_ibrs_enabled = 1;
> -        }
> -
> -	pr_info("Spectre v2 mitigation: Speculation control IBRS %s",
> -	        ibrs_supported ? "supported" : "not-supported");
> -
>  	/*
>  	 * If spectre v2 protection has been enabled, unconditionally fill
>  	 * RSB during a context switch; this protects against two independent
> @@ -433,21 +431,6 @@ specv2_set_mode:
>  	setup_force_cpu_cap(X86_FEATURE_RSB_CTXSW);
>  	pr_info("Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch\n");
>  
> -	/*
> -	 * If we have a full retpoline mode and then disable IBPB in kernel mode
> -	 * we do not require both.
> -	 */
> -	if (mode == SPECTRE_V2_RETPOLINE_AMD ||
> -	    mode == SPECTRE_V2_RETPOLINE_GENERIC)
> -	{
> -		if (ibrs_supported) {
> -			pr_info("Retpoline compiled kernel.  Defaulting IBRS to disabled");
> -			set_ibrs_disabled();
> -			if (!ibrs_inuse)
> -				sysctl_ibrs_enabled = 0;
> -		}
> -	}
> -
>  	/*
>  	 * Retpoline means the kernel is safe because it has no indirect
>  	 * branches. Enhanced IBRS protects firmware too, so, enable restricted
> @@ -462,6 +445,11 @@ specv2_set_mode:
>  	if (boot_cpu_has(X86_FEATURE_IBRS) && mode != SPECTRE_V2_IBRS_ENHANCED) {
>  		setup_force_cpu_cap(X86_FEATURE_USE_IBRS_FW);
>  		pr_info("Enabling Restricted Speculation for firmware calls\n");
> +
> +		if (!noibrs &&
> +		    mode != SPECTRE_V2_RETPOLINE_GENERIC &&
> +		    mode != SPECTRE_V2_RETPOLINE_AMD)
> +			set_ibrs_enabled(1);   /* Enable IBRS */
>  	}
>  }
>  
> @@ -871,8 +859,9 @@ static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr
>  		break;
>  
>  	case X86_BUG_SPECTRE_V2:
> -		return sprintf(buf, "%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
> +		return sprintf(buf, "%s%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
>  			       ibpb_enabled ? ", IBPB" : "",
> +			       ibrs_enabled == 2 ? ", IBRS (user space)" : ibrs_enabled ? ", IBRS" : "",
>  			       boot_cpu_has(X86_FEATURE_USE_IBRS_FW) ? ", IBRS_FW" : "",
>  			       spectre_v2_module_string());
>  
> diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
> index 84bd97f8eeab..4fe475c2f745 100644
> --- a/arch/x86/kernel/cpu/microcode/core.c
> +++ b/arch/x86/kernel/cpu/microcode/core.c
> @@ -439,17 +439,6 @@ static ssize_t reload_store(struct device *dev,
>  	if (!ret)
>  		perf_check_microcode();
>  
> -	/* Initialize Indirect Branch Restricted Speculation if supported */
> -	if (boot_cpu_has(X86_FEATURE_IBRS)) {
> -		pr_info("Enabling Indirect Branch Restricted Speculation\n");
> -
> -		mutex_lock(&spec_ctrl_mutex);
> -		set_ibrs_supported();
> -		if (ibrs_inuse)
> -			sysctl_ibrs_enabled = 1;
> -		mutex_unlock(&spec_ctrl_mutex);
> -	}
> -
>  	mutex_unlock(&microcode_mutex);
>  	put_online_cpus();
>  
> diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
> index a5b3404266eb..5587098a5823 100644
> --- a/arch/x86/kernel/process.c
> +++ b/arch/x86/kernel/process.c
> @@ -566,17 +566,15 @@ static void mwait_idle(void)
>  			smp_mb(); /* quirk */
>  		}
>  
> -		if (ibrs_inuse)
> -                        native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +		ubuntu_restrict_branch_speculation_end();
>  
>  		__monitor((void *)&current_thread_info()->flags, 0, 0);
> +
>  		if (!need_resched()) {
>  			__sti_mwait(0, 0);
> -			if (ibrs_inuse)
> -				native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +			ubuntu_restrict_branch_speculation_start();
>  		} else {
> -			if (ibrs_inuse)
> -				native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +			ubuntu_restrict_branch_speculation_start();
>  			local_irq_enable();
>  		}
>  		trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id());
> diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
> index 21c3bcbd69a2..6c9bb4db2ed7 100644
> --- a/arch/x86/kernel/smpboot.c
> +++ b/arch/x86/kernel/smpboot.c
> @@ -1697,15 +1697,13 @@ void native_play_dead(void)
>  	play_dead_common();
>  	tboot_shutdown(TB_SHUTDOWN_WFS);
>  
> -	if (ibrs_inuse)
> -		native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +	ubuntu_restrict_branch_speculation_end();
>  
>  	mwait_play_dead();	/* Only returns on failure */
>  	if (cpuidle_play_dead())
>  		hlt_play_dead();
>  
> -	if (ibrs_inuse)
> -		native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +	ubuntu_restrict_branch_speculation_start();
>  }
>  
>  #else /* ... !CONFIG_HOTPLUG_CPU */
> diff --git a/arch/x86/lib/delay.c b/arch/x86/lib/delay.c
> index 5b66d4417f8d..20e9a5ac91dc 100644
> --- a/arch/x86/lib/delay.c
> +++ b/arch/x86/lib/delay.c
> @@ -107,8 +107,8 @@ static void delay_mwaitx(unsigned long __loops)
>  	for (;;) {
>  		delay = min_t(u64, MWAITX_MAX_LOOPS, loops);
>  
> -		if (ibrs_inuse && (delay > IBRS_DISABLE_THRESHOLD))
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +		if (delay > IBRS_DISABLE_THRESHOLD)
> +			ubuntu_restrict_branch_speculation_end();
>  
>  		/*
>  		 * Use cpu_tss as a cacheline-aligned, seldomly
> @@ -123,8 +123,8 @@ static void delay_mwaitx(unsigned long __loops)
>  		 */
>  		__mwaitx(MWAITX_DISABLE_CSTATES, delay, MWAITX_ECX_TIMER_ENABLE);
>  
> -		if (ibrs_inuse && (delay > IBRS_DISABLE_THRESHOLD))
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +		if (delay > IBRS_DISABLE_THRESHOLD)
> +			ubuntu_restrict_branch_speculation_start();
>  
>  		end = rdtsc_ordered();
>  
> diff --git a/include/linux/smp.h b/include/linux/smp.h
> index 98ac8ff2afab..c4414074bd88 100644
> --- a/include/linux/smp.h
> +++ b/include/linux/smp.h
> @@ -50,52 +50,6 @@ void on_each_cpu_cond(bool (*cond_func)(int cpu, void *info),
>  
>  int smp_call_function_single_async(int cpu, struct call_single_data *csd);
>  
> -#ifdef CONFIG_X86
> -
> -#define IBxx_INUSE 	(1 << 0)
> -#define IBxx_SUPPORTED 	(1 << 1)
> -#define IBxx_DISABLED 	(1 << 2)
> -
> -/* indicate usage of IBRS to control execution speculation */
> -extern int use_ibrs;
> -extern u32 sysctl_ibrs_enabled;
> -extern struct mutex spec_ctrl_mutex;
> -
> -static inline int __check_ibrs_inuse(void)
> -{
> -	if (use_ibrs & IBxx_INUSE)
> -		return 1;
> -	else
> -		/* rmb to prevent wrong speculation for security */
> -		rmb();
> -	return 0;
> -}
> -
> -#define ibrs_inuse 	(__check_ibrs_inuse())
> -#define ibrs_supported	(use_ibrs & IBxx_SUPPORTED)
> -#define ibrs_disabled	(use_ibrs & IBxx_DISABLED)
> -
> -static inline void set_ibrs_supported(void)
> -{
> -	use_ibrs |= IBxx_SUPPORTED;
> -	if (!ibrs_disabled)
> -		use_ibrs |= IBxx_INUSE;
> -}
> -static inline void set_ibrs_disabled(void)
> -{
> -	use_ibrs |= IBxx_DISABLED;
> -	if (ibrs_inuse)
> -		use_ibrs &= ~IBxx_INUSE;
> -}
> -static inline void clear_ibrs_disabled(void)
> -{
> -	use_ibrs &= ~IBxx_DISABLED;
> -	if (ibrs_supported)
> -		use_ibrs |= IBxx_INUSE;
> -}
> -
> -#endif /* CONFIG_X86 */
> -
>  #ifdef CONFIG_SMP
>  
>  #include <linux/preempt.h>
> diff --git a/kernel/smp.c b/kernel/smp.c
> index 139681ddf916..48eb4fafc356 100644
> --- a/kernel/smp.c
> +++ b/kernel/smp.c
> @@ -499,22 +499,6 @@ EXPORT_SYMBOL(smp_call_function);
>  unsigned int setup_max_cpus = NR_CPUS;
>  EXPORT_SYMBOL(setup_max_cpus);
>  
> -#ifdef CONFIG_X86
> -/*
> - * use IBRS
> - * bit 0 = indicate if ibrs is currently in use
> - * bit 1 = indicate if system supports ibrs
> - * bit 2 = indicate if admin disables ibrs
> -*/
> -
> -int use_ibrs;
> -EXPORT_SYMBOL(use_ibrs);
> -#endif
> -
> -/* mutex to serialize IBRS & IBPB control changes */
> -DEFINE_MUTEX(spec_ctrl_mutex);
> -EXPORT_SYMBOL(spec_ctrl_mutex);
> -
>  /*
>   * Setup routine for controlling SMP activation
>   *
> @@ -538,18 +522,6 @@ static int __init nosmp(char *str)
>  
>  early_param("nosmp", nosmp);
>  
> -#ifdef CONFIG_X86
> -static int __init noibrs(char *str)
> -{
> -	set_ibrs_disabled();
> -
> -	return 0;
> -}
> -
> -early_param("noibrs", noibrs);
> -#endif
> -
> -
>  /* this is hard limit */
>  static int __init nrcpus(char *str)
>  {
> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> index d00f95726ac4..02df9c031a45 100644
> --- a/kernel/sysctl.c
> +++ b/kernel/sysctl.c
> @@ -202,8 +202,8 @@ static int proc_dostring_coredump(struct ctl_table *table, int write,
>  #endif
>  
>  #ifdef CONFIG_X86
> -int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write,
> -                 void __user *buffer, size_t *lenp, loff_t *ppos);
> +/* Mutex to serialize IBPB and IBRS runtime control changes */
> +DEFINE_MUTEX(spec_ctrl_mutex);
>  
>  unsigned int ibpb_enabled = 0;
>  EXPORT_SYMBOL(ibpb_enabled);   /* Required in some modules */
> @@ -252,6 +252,70 @@ static int ibpb_enabled_handler(struct ctl_table *table, int write,
>  
>  	return set_ibpb_enabled(__ibpb_enabled);
>  }
> +
> +unsigned int ibrs_enabled = 0;
> +EXPORT_SYMBOL(ibrs_enabled);   /* Required in some modules */
> +
> +static unsigned int __ibrs_enabled = 0;   /* procfs shadow variable */
> +
> +int set_ibrs_enabled(unsigned int val)
> +{
> +	int error = 0;
> +	unsigned int cpu;
> +	unsigned int prev = ibrs_enabled;
> +
> +	mutex_lock(&spec_ctrl_mutex);
> +
> +	/* Only enable/disable IBRS if the CPU supports it */
> +	if (boot_cpu_has(X86_FEATURE_USE_IBRS_FW)) {
> +		ibrs_enabled = val;
> +		if (ibrs_enabled != prev)
> +			pr_info("Spectre V2 : Spectre v2 mitigation: %s "
> +				"Indirect Branch Restricted Speculation%s\n",
> +				ibrs_enabled ? "Enabling" : "Disabling",
> +				ibrs_enabled == 2 ? " (user space)" : "");
> +
> +		if (ibrs_enabled == 0) {
> +			/* Always disable IBRS */
> +			u64 val = x86_spec_ctrl_base;
> +
> +			for_each_online_cpu(cpu)
> +				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, val);

We don't need to handle here the case ibrs_enabled == 1?


> +		} else if (ibrs_enabled == 2) {
> +			/* Always enable IBRS, even in user space */
> +			u64 val = x86_spec_ctrl_base | SPEC_CTRL_IBRS;
> +
> +			for_each_online_cpu(cpu)
> +				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, val);
> +		}
> +	} else {
> +		ibrs_enabled = 0;
> +		if (val) {
> +			/* IBRS is not supported but we try to turn it on */
> +			error = -EINVAL;
> +		}
> +	}
> +
> +	/* Update the shadow variable */
> +	__ibrs_enabled = ibrs_enabled;
> +
> +	mutex_unlock(&spec_ctrl_mutex);
> +
> +	return error;
> +}
> +
> +static int ibrs_enabled_handler(struct ctl_table *table, int write,
> +                               void __user *buffer, size_t *lenp,
> +                               loff_t *ppos)
> +{
> +	int error;
> +
> +	error = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
> +	if (error)
> +		return error;
> +
> +	return set_ibrs_enabled(__ibrs_enabled);
> +}
>  #endif
>  
>  #ifdef CONFIG_MAGIC_SYSRQ
> @@ -290,9 +354,6 @@ extern struct ctl_table epoll_table[];
>  int sysctl_legacy_va_layout;
>  #endif
>  
> -u32 sysctl_ibrs_enabled = 0;
> -EXPORT_SYMBOL(sysctl_ibrs_enabled);
> -
>  /* The default sysctl tables: */
>  
>  static struct ctl_table sysctl_base_table[] = {
> @@ -1281,10 +1342,10 @@ static struct ctl_table kern_table[] = {
>  #ifdef CONFIG_X86
>  	{
>  		.procname       = "ibrs_enabled",
> -		.data           = &sysctl_ibrs_enabled,
> +		.data           = &__ibrs_enabled,
>  		.maxlen         = sizeof(unsigned int),
>  		.mode           = 0644,
> -		.proc_handler   = proc_dointvec_ibrs_ctrl,
> +		.proc_handler   = ibrs_enabled_handler,
>  		.extra1         = &zero,
>  		.extra2         = &two,
>  	},
> @@ -2455,46 +2516,6 @@ int proc_dointvec_minmax(struct ctl_table *table, int write,
>  				do_proc_dointvec_minmax_conv, &param);
>  }
>  
> -#ifdef CONFIG_X86
> -int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write,
> -	void __user *buffer, size_t *lenp, loff_t *ppos)
> -{
> -	int ret;
> -	unsigned int cpu;
> -
> -	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
> -	pr_debug("sysctl_ibrs_enabled = %u\n", sysctl_ibrs_enabled);
> -	pr_debug("before:use_ibrs = %d\n", use_ibrs);
> -	mutex_lock(&spec_ctrl_mutex);
> -	if (sysctl_ibrs_enabled == 0) {
> -		/* always set IBRS off */
> -		set_ibrs_disabled();
> -		if (ibrs_supported) {
> -			for_each_online_cpu(cpu)
> -				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> -		}
> -	} else if (sysctl_ibrs_enabled == 2) {
> -		/* always set IBRS on, even in user space */
> -		clear_ibrs_disabled();
> -		if (ibrs_supported) {
> -			for_each_online_cpu(cpu)
> -				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> -		} else {
> -			sysctl_ibrs_enabled = 0;
> -		}
> -	} else if (sysctl_ibrs_enabled == 1) {
> -		/* use IBRS in kernel */
> -		clear_ibrs_disabled();
> -		if (!ibrs_inuse)
> -			/* platform don't support ibrs */
> -			sysctl_ibrs_enabled = 0;
> -	}
> -	mutex_unlock(&spec_ctrl_mutex);
> -	pr_debug("after:use_ibrs = %d\n", use_ibrs);
> -	return ret;
> -}
> -#endif
> -
>  static void validate_coredump_safety(void)
>  {
>  #ifdef CONFIG_COREDUMP
Juerg Haefliger Jan. 10, 2019, 1:12 p.m. UTC | #3
> > +int set_ibrs_enabled(unsigned int val)
> > +{
> > +	int error = 0;
> > +	unsigned int cpu;
> > +	unsigned int prev = ibrs_enabled;
> > +
> > +	mutex_lock(&spec_ctrl_mutex);
> > +
> > +	/* Only enable/disable IBRS if the CPU supports it */
> > +	if (boot_cpu_has(X86_FEATURE_USE_IBRS_FW)) {
> > +		ibrs_enabled = val;
> > +		if (ibrs_enabled != prev)
> > +			pr_info("Spectre V2 : Spectre v2 mitigation: %s "
> > +				"Indirect Branch Restricted Speculation%s\n",
> > +				ibrs_enabled ? "Enabling" : "Disabling",
> > +				ibrs_enabled == 2 ? " (user space)" : "");
> > +
> > +		if (ibrs_enabled == 0) {
> > +			/* Always disable IBRS */
> > +			u64 val = x86_spec_ctrl_base;
> > +
> > +			for_each_online_cpu(cpu)
> > +				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, val);  
> 
> We don't need to handle here the case ibrs_enabled == 1?

No. == 1 means IBRS is temporarily turned on when we enter the kernel and turned
back off again when we leave it, so it's not sticky. Whereas == 0 means always
off (sticky) and == 2 is always on (also sticky).

...Juerg
Kleber Sacilotto de Souza Jan. 10, 2019, 1:15 p.m. UTC | #4
On 12/13/18 2:21 PM, Juerg Haefliger wrote:
> In Ubuntu, we have runtime control for enabling/disabling IBRS via the
> commandline ("noibrs") and through the proc interface
> /proc/sys/kernel/ibrs_enabled. This commit simplifies the current
> (probably broken) implementation by merging it with all the IBRS-related
> upstream changes from previous commits.
>
> What we have now is the upstream implementation for detecting the presence
> of IBRS support. This commit adds a global state variable 'ibrs_enabled'
> which is set to 1 if the CPU supports IBRS but can be overridden via the
> commandline "noibrs" switch or by writting 0, 1 or 2 to
> /proc/sys/kernel/ibrs_enabled at runtime.
>
> Note that the runtime controls are disabled if the CPU runs in Enhanced
> IBRS mode.
>
> CVE-2017-5715
>
> Signed-off-by: Juerg Haefliger <juergh@canonical.com>


Acked-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>


> ---
>  arch/x86/include/asm/mwait.h         |   6 +-
>  arch/x86/include/asm/nospec-branch.h |  20 +++++
>  arch/x86/include/asm/spec_ctrl.h     |  10 ++-
>  arch/x86/kernel/cpu/bugs.c           |  45 ++++-------
>  arch/x86/kernel/cpu/microcode/core.c |  11 ---
>  arch/x86/kernel/process.c            |  10 +--
>  arch/x86/kernel/smpboot.c            |   6 +-
>  arch/x86/lib/delay.c                 |   8 +-
>  include/linux/smp.h                  |  46 -----------
>  kernel/smp.c                         |  28 -------
>  kernel/sysctl.c                      | 115 ++++++++++++++++-----------
>  11 files changed, 123 insertions(+), 182 deletions(-)
>
> diff --git a/arch/x86/include/asm/mwait.h b/arch/x86/include/asm/mwait.h
> index 9821763b02cf..9bd760758640 100644
> --- a/arch/x86/include/asm/mwait.h
> +++ b/arch/x86/include/asm/mwait.h
> @@ -106,15 +106,13 @@ static inline void mwait_idle_with_hints(unsigned long eax, unsigned long ecx)
>  			mb();
>  		}
>  
> -		if (ibrs_inuse)
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +		ubuntu_restrict_branch_speculation_end();
>  
>  		__monitor((void *)&current_thread_info()->flags, 0, 0);
>  		if (!need_resched())
>  			__mwait(eax, ecx);
>  
> -		if (ibrs_inuse)
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +		ubuntu_restrict_branch_speculation_start();
>  	}
>  	current_clr_polling();
>  }
> diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
> index dcc7b0348fbc..a6120d43caa7 100644
> --- a/arch/x86/include/asm/nospec-branch.h
> +++ b/arch/x86/include/asm/nospec-branch.h
> @@ -188,6 +188,10 @@
>  extern unsigned int ibpb_enabled;
>  int set_ibpb_enabled(unsigned int);
>  
> +/* The IBRS runtime control knob */
> +extern unsigned int ibrs_enabled;
> +int set_ibrs_enabled(unsigned int);
> +
>  /* The Spectre V2 mitigation variants */
>  enum spectre_v2_mitigation {
>  	SPECTRE_V2_NONE,
> @@ -275,6 +279,22 @@ do {									\
>  	preempt_enable();						\
>  } while (0)
>  
> +#define ubuntu_restrict_branch_speculation_start()			\
> +do {									\
> +	u64 val = x86_spec_ctrl_base | SPEC_CTRL_IBRS;			\
> +									\
> +	if (ibrs_enabled)						\
> +		native_wrmsrl(MSR_IA32_SPEC_CTRL, val);			\
> +} while (0)
> +
> +#define ubuntu_restrict_branch_speculation_end()			\
> +do {									\
> +	u64 val = x86_spec_ctrl_base;					\
> +									\
> +	if (ibrs_enabled)						\
> +		native_wrmsrl(MSR_IA32_SPEC_CTRL, val);			\
> + } while (0)
> +
>  #endif /* __ASSEMBLY__ */
>  
>  /*
> diff --git a/arch/x86/include/asm/spec_ctrl.h b/arch/x86/include/asm/spec_ctrl.h
> index 49c3b0a83e9f..a5d93d23390e 100644
> --- a/arch/x86/include/asm/spec_ctrl.h
> +++ b/arch/x86/include/asm/spec_ctrl.h
> @@ -8,7 +8,7 @@
>  
>  #ifdef __ASSEMBLY__
>  
> -.extern use_ibrs
> +.extern ibrs_enabled
>  
>  #define __ASM_ENABLE_IBRS			\
>  	pushq %rax;				\
> @@ -21,11 +21,13 @@
>  	popq %rdx;				\
>  	popq %rcx;				\
>  	popq %rax
> +
>  #define __ASM_ENABLE_IBRS_CLOBBER		\
>  	movl $MSR_IA32_SPEC_CTRL, %ecx;		\
>  	movl $0, %edx;				\
>  	movl $SPEC_CTRL_IBRS, %eax;		\
>  	wrmsr;
> +
>  #define __ASM_DISABLE_IBRS			\
>  	pushq %rax;				\
>  	pushq %rcx;				\
> @@ -39,7 +41,7 @@
>  	popq %rax
>  
>  .macro ENABLE_IBRS
> -	testl	$1, use_ibrs
> +	testl	$1, ibrs_enabled
>  	jz	10f
>  	__ASM_ENABLE_IBRS
>  	jmp 20f
> @@ -49,7 +51,7 @@
>  .endm
>  
>  .macro ENABLE_IBRS_CLOBBER
> -	testl	$1, use_ibrs
> +	testl	$1, ibrs_enabled
>  	jz	11f
>  	__ASM_ENABLE_IBRS_CLOBBER
>  	jmp 21f
> @@ -59,7 +61,7 @@
>  .endm
>  
>  .macro DISABLE_IBRS
> -	testl	$1, use_ibrs
> +	testl	$1, ibrs_enabled
>  	jz	9f
>  	__ASM_DISABLE_IBRS
>  9:
> diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
> index eeb89d781a9c..7a9940015af5 100644
> --- a/arch/x86/kernel/cpu/bugs.c
> +++ b/arch/x86/kernel/cpu/bugs.c
> @@ -40,6 +40,16 @@ static int __init noibpb_handler(char *str)
>  
>  early_param("noibpb", noibpb_handler);
>  
> +unsigned int noibrs = 0;
> +
> +static int __init noibrs_handler(char *str)
> +{
> +	noibrs = 1;
> +	return 0;
> +}
> +
> +early_param("noibrs", noibrs_handler);
> +
>  static void __init spectre_v2_select_mitigation(void);
>  static void __init ssb_select_mitigation(void);
>  static void __init l1tf_select_mitigation(void);
> @@ -410,18 +420,6 @@ specv2_set_mode:
>  			set_ibpb_enabled(1);   /* Enable IBPB */
>  	}
>  
> -	/* Initialize Indirect Branch Restricted Speculation if supported */
> -	if (boot_cpu_has(X86_FEATURE_IBRS)) {
> -		pr_info("Spectre v2 mitigation: Enabling Indirect Branch Restricted Speculation\n");
> -
> -		set_ibrs_supported();
> -		if (ibrs_inuse)
> -			sysctl_ibrs_enabled = 1;
> -        }
> -
> -	pr_info("Spectre v2 mitigation: Speculation control IBRS %s",
> -	        ibrs_supported ? "supported" : "not-supported");
> -
>  	/*
>  	 * If spectre v2 protection has been enabled, unconditionally fill
>  	 * RSB during a context switch; this protects against two independent
> @@ -433,21 +431,6 @@ specv2_set_mode:
>  	setup_force_cpu_cap(X86_FEATURE_RSB_CTXSW);
>  	pr_info("Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch\n");
>  
> -	/*
> -	 * If we have a full retpoline mode and then disable IBPB in kernel mode
> -	 * we do not require both.
> -	 */
> -	if (mode == SPECTRE_V2_RETPOLINE_AMD ||
> -	    mode == SPECTRE_V2_RETPOLINE_GENERIC)
> -	{
> -		if (ibrs_supported) {
> -			pr_info("Retpoline compiled kernel.  Defaulting IBRS to disabled");
> -			set_ibrs_disabled();
> -			if (!ibrs_inuse)
> -				sysctl_ibrs_enabled = 0;
> -		}
> -	}
> -
>  	/*
>  	 * Retpoline means the kernel is safe because it has no indirect
>  	 * branches. Enhanced IBRS protects firmware too, so, enable restricted
> @@ -462,6 +445,11 @@ specv2_set_mode:
>  	if (boot_cpu_has(X86_FEATURE_IBRS) && mode != SPECTRE_V2_IBRS_ENHANCED) {
>  		setup_force_cpu_cap(X86_FEATURE_USE_IBRS_FW);
>  		pr_info("Enabling Restricted Speculation for firmware calls\n");
> +
> +		if (!noibrs &&
> +		    mode != SPECTRE_V2_RETPOLINE_GENERIC &&
> +		    mode != SPECTRE_V2_RETPOLINE_AMD)
> +			set_ibrs_enabled(1);   /* Enable IBRS */
>  	}
>  }
>  
> @@ -871,8 +859,9 @@ static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr
>  		break;
>  
>  	case X86_BUG_SPECTRE_V2:
> -		return sprintf(buf, "%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
> +		return sprintf(buf, "%s%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
>  			       ibpb_enabled ? ", IBPB" : "",
> +			       ibrs_enabled == 2 ? ", IBRS (user space)" : ibrs_enabled ? ", IBRS" : "",
>  			       boot_cpu_has(X86_FEATURE_USE_IBRS_FW) ? ", IBRS_FW" : "",
>  			       spectre_v2_module_string());
>  
> diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
> index 84bd97f8eeab..4fe475c2f745 100644
> --- a/arch/x86/kernel/cpu/microcode/core.c
> +++ b/arch/x86/kernel/cpu/microcode/core.c
> @@ -439,17 +439,6 @@ static ssize_t reload_store(struct device *dev,
>  	if (!ret)
>  		perf_check_microcode();
>  
> -	/* Initialize Indirect Branch Restricted Speculation if supported */
> -	if (boot_cpu_has(X86_FEATURE_IBRS)) {
> -		pr_info("Enabling Indirect Branch Restricted Speculation\n");
> -
> -		mutex_lock(&spec_ctrl_mutex);
> -		set_ibrs_supported();
> -		if (ibrs_inuse)
> -			sysctl_ibrs_enabled = 1;
> -		mutex_unlock(&spec_ctrl_mutex);
> -	}
> -
>  	mutex_unlock(&microcode_mutex);
>  	put_online_cpus();
>  
> diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
> index a5b3404266eb..5587098a5823 100644
> --- a/arch/x86/kernel/process.c
> +++ b/arch/x86/kernel/process.c
> @@ -566,17 +566,15 @@ static void mwait_idle(void)
>  			smp_mb(); /* quirk */
>  		}
>  
> -		if (ibrs_inuse)
> -                        native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +		ubuntu_restrict_branch_speculation_end();
>  
>  		__monitor((void *)&current_thread_info()->flags, 0, 0);
> +
>  		if (!need_resched()) {
>  			__sti_mwait(0, 0);
> -			if (ibrs_inuse)
> -				native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +			ubuntu_restrict_branch_speculation_start();
>  		} else {
> -			if (ibrs_inuse)
> -				native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +			ubuntu_restrict_branch_speculation_start();
>  			local_irq_enable();
>  		}
>  		trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id());
> diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
> index 21c3bcbd69a2..6c9bb4db2ed7 100644
> --- a/arch/x86/kernel/smpboot.c
> +++ b/arch/x86/kernel/smpboot.c
> @@ -1697,15 +1697,13 @@ void native_play_dead(void)
>  	play_dead_common();
>  	tboot_shutdown(TB_SHUTDOWN_WFS);
>  
> -	if (ibrs_inuse)
> -		native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +	ubuntu_restrict_branch_speculation_end();
>  
>  	mwait_play_dead();	/* Only returns on failure */
>  	if (cpuidle_play_dead())
>  		hlt_play_dead();
>  
> -	if (ibrs_inuse)
> -		native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +	ubuntu_restrict_branch_speculation_start();
>  }
>  
>  #else /* ... !CONFIG_HOTPLUG_CPU */
> diff --git a/arch/x86/lib/delay.c b/arch/x86/lib/delay.c
> index 5b66d4417f8d..20e9a5ac91dc 100644
> --- a/arch/x86/lib/delay.c
> +++ b/arch/x86/lib/delay.c
> @@ -107,8 +107,8 @@ static void delay_mwaitx(unsigned long __loops)
>  	for (;;) {
>  		delay = min_t(u64, MWAITX_MAX_LOOPS, loops);
>  
> -		if (ibrs_inuse && (delay > IBRS_DISABLE_THRESHOLD))
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +		if (delay > IBRS_DISABLE_THRESHOLD)
> +			ubuntu_restrict_branch_speculation_end();
>  
>  		/*
>  		 * Use cpu_tss as a cacheline-aligned, seldomly
> @@ -123,8 +123,8 @@ static void delay_mwaitx(unsigned long __loops)
>  		 */
>  		__mwaitx(MWAITX_DISABLE_CSTATES, delay, MWAITX_ECX_TIMER_ENABLE);
>  
> -		if (ibrs_inuse && (delay > IBRS_DISABLE_THRESHOLD))
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +		if (delay > IBRS_DISABLE_THRESHOLD)
> +			ubuntu_restrict_branch_speculation_start();
>  
>  		end = rdtsc_ordered();
>  
> diff --git a/include/linux/smp.h b/include/linux/smp.h
> index 98ac8ff2afab..c4414074bd88 100644
> --- a/include/linux/smp.h
> +++ b/include/linux/smp.h
> @@ -50,52 +50,6 @@ void on_each_cpu_cond(bool (*cond_func)(int cpu, void *info),
>  
>  int smp_call_function_single_async(int cpu, struct call_single_data *csd);
>  
> -#ifdef CONFIG_X86
> -
> -#define IBxx_INUSE 	(1 << 0)
> -#define IBxx_SUPPORTED 	(1 << 1)
> -#define IBxx_DISABLED 	(1 << 2)
> -
> -/* indicate usage of IBRS to control execution speculation */
> -extern int use_ibrs;
> -extern u32 sysctl_ibrs_enabled;
> -extern struct mutex spec_ctrl_mutex;
> -
> -static inline int __check_ibrs_inuse(void)
> -{
> -	if (use_ibrs & IBxx_INUSE)
> -		return 1;
> -	else
> -		/* rmb to prevent wrong speculation for security */
> -		rmb();
> -	return 0;
> -}
> -
> -#define ibrs_inuse 	(__check_ibrs_inuse())
> -#define ibrs_supported	(use_ibrs & IBxx_SUPPORTED)
> -#define ibrs_disabled	(use_ibrs & IBxx_DISABLED)
> -
> -static inline void set_ibrs_supported(void)
> -{
> -	use_ibrs |= IBxx_SUPPORTED;
> -	if (!ibrs_disabled)
> -		use_ibrs |= IBxx_INUSE;
> -}
> -static inline void set_ibrs_disabled(void)
> -{
> -	use_ibrs |= IBxx_DISABLED;
> -	if (ibrs_inuse)
> -		use_ibrs &= ~IBxx_INUSE;
> -}
> -static inline void clear_ibrs_disabled(void)
> -{
> -	use_ibrs &= ~IBxx_DISABLED;
> -	if (ibrs_supported)
> -		use_ibrs |= IBxx_INUSE;
> -}
> -
> -#endif /* CONFIG_X86 */
> -
>  #ifdef CONFIG_SMP
>  
>  #include <linux/preempt.h>
> diff --git a/kernel/smp.c b/kernel/smp.c
> index 139681ddf916..48eb4fafc356 100644
> --- a/kernel/smp.c
> +++ b/kernel/smp.c
> @@ -499,22 +499,6 @@ EXPORT_SYMBOL(smp_call_function);
>  unsigned int setup_max_cpus = NR_CPUS;
>  EXPORT_SYMBOL(setup_max_cpus);
>  
> -#ifdef CONFIG_X86
> -/*
> - * use IBRS
> - * bit 0 = indicate if ibrs is currently in use
> - * bit 1 = indicate if system supports ibrs
> - * bit 2 = indicate if admin disables ibrs
> -*/
> -
> -int use_ibrs;
> -EXPORT_SYMBOL(use_ibrs);
> -#endif
> -
> -/* mutex to serialize IBRS & IBPB control changes */
> -DEFINE_MUTEX(spec_ctrl_mutex);
> -EXPORT_SYMBOL(spec_ctrl_mutex);
> -
>  /*
>   * Setup routine for controlling SMP activation
>   *
> @@ -538,18 +522,6 @@ static int __init nosmp(char *str)
>  
>  early_param("nosmp", nosmp);
>  
> -#ifdef CONFIG_X86
> -static int __init noibrs(char *str)
> -{
> -	set_ibrs_disabled();
> -
> -	return 0;
> -}
> -
> -early_param("noibrs", noibrs);
> -#endif
> -
> -
>  /* this is hard limit */
>  static int __init nrcpus(char *str)
>  {
> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> index d00f95726ac4..02df9c031a45 100644
> --- a/kernel/sysctl.c
> +++ b/kernel/sysctl.c
> @@ -202,8 +202,8 @@ static int proc_dostring_coredump(struct ctl_table *table, int write,
>  #endif
>  
>  #ifdef CONFIG_X86
> -int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write,
> -                 void __user *buffer, size_t *lenp, loff_t *ppos);
> +/* Mutex to serialize IBPB and IBRS runtime control changes */
> +DEFINE_MUTEX(spec_ctrl_mutex);
>  
>  unsigned int ibpb_enabled = 0;
>  EXPORT_SYMBOL(ibpb_enabled);   /* Required in some modules */
> @@ -252,6 +252,70 @@ static int ibpb_enabled_handler(struct ctl_table *table, int write,
>  
>  	return set_ibpb_enabled(__ibpb_enabled);
>  }
> +
> +unsigned int ibrs_enabled = 0;
> +EXPORT_SYMBOL(ibrs_enabled);   /* Required in some modules */
> +
> +static unsigned int __ibrs_enabled = 0;   /* procfs shadow variable */
> +
> +int set_ibrs_enabled(unsigned int val)
> +{
> +	int error = 0;
> +	unsigned int cpu;
> +	unsigned int prev = ibrs_enabled;
> +
> +	mutex_lock(&spec_ctrl_mutex);
> +
> +	/* Only enable/disable IBRS if the CPU supports it */
> +	if (boot_cpu_has(X86_FEATURE_USE_IBRS_FW)) {
> +		ibrs_enabled = val;
> +		if (ibrs_enabled != prev)
> +			pr_info("Spectre V2 : Spectre v2 mitigation: %s "
> +				"Indirect Branch Restricted Speculation%s\n",
> +				ibrs_enabled ? "Enabling" : "Disabling",
> +				ibrs_enabled == 2 ? " (user space)" : "");
> +
> +		if (ibrs_enabled == 0) {
> +			/* Always disable IBRS */
> +			u64 val = x86_spec_ctrl_base;
> +
> +			for_each_online_cpu(cpu)
> +				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, val);
> +		} else if (ibrs_enabled == 2) {
> +			/* Always enable IBRS, even in user space */
> +			u64 val = x86_spec_ctrl_base | SPEC_CTRL_IBRS;
> +
> +			for_each_online_cpu(cpu)
> +				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, val);
> +		}
> +	} else {
> +		ibrs_enabled = 0;
> +		if (val) {
> +			/* IBRS is not supported but we try to turn it on */
> +			error = -EINVAL;
> +		}
> +	}
> +
> +	/* Update the shadow variable */
> +	__ibrs_enabled = ibrs_enabled;
> +
> +	mutex_unlock(&spec_ctrl_mutex);
> +
> +	return error;
> +}
> +
> +static int ibrs_enabled_handler(struct ctl_table *table, int write,
> +                               void __user *buffer, size_t *lenp,
> +                               loff_t *ppos)
> +{
> +	int error;
> +
> +	error = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
> +	if (error)
> +		return error;
> +
> +	return set_ibrs_enabled(__ibrs_enabled);
> +}
>  #endif
>  
>  #ifdef CONFIG_MAGIC_SYSRQ
> @@ -290,9 +354,6 @@ extern struct ctl_table epoll_table[];
>  int sysctl_legacy_va_layout;
>  #endif
>  
> -u32 sysctl_ibrs_enabled = 0;
> -EXPORT_SYMBOL(sysctl_ibrs_enabled);
> -
>  /* The default sysctl tables: */
>  
>  static struct ctl_table sysctl_base_table[] = {
> @@ -1281,10 +1342,10 @@ static struct ctl_table kern_table[] = {
>  #ifdef CONFIG_X86
>  	{
>  		.procname       = "ibrs_enabled",
> -		.data           = &sysctl_ibrs_enabled,
> +		.data           = &__ibrs_enabled,
>  		.maxlen         = sizeof(unsigned int),
>  		.mode           = 0644,
> -		.proc_handler   = proc_dointvec_ibrs_ctrl,
> +		.proc_handler   = ibrs_enabled_handler,
>  		.extra1         = &zero,
>  		.extra2         = &two,
>  	},
> @@ -2455,46 +2516,6 @@ int proc_dointvec_minmax(struct ctl_table *table, int write,
>  				do_proc_dointvec_minmax_conv, &param);
>  }
>  
> -#ifdef CONFIG_X86
> -int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write,
> -	void __user *buffer, size_t *lenp, loff_t *ppos)
> -{
> -	int ret;
> -	unsigned int cpu;
> -
> -	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
> -	pr_debug("sysctl_ibrs_enabled = %u\n", sysctl_ibrs_enabled);
> -	pr_debug("before:use_ibrs = %d\n", use_ibrs);
> -	mutex_lock(&spec_ctrl_mutex);
> -	if (sysctl_ibrs_enabled == 0) {
> -		/* always set IBRS off */
> -		set_ibrs_disabled();
> -		if (ibrs_supported) {
> -			for_each_online_cpu(cpu)
> -				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> -		}
> -	} else if (sysctl_ibrs_enabled == 2) {
> -		/* always set IBRS on, even in user space */
> -		clear_ibrs_disabled();
> -		if (ibrs_supported) {
> -			for_each_online_cpu(cpu)
> -				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> -		} else {
> -			sysctl_ibrs_enabled = 0;
> -		}
> -	} else if (sysctl_ibrs_enabled == 1) {
> -		/* use IBRS in kernel */
> -		clear_ibrs_disabled();
> -		if (!ibrs_inuse)
> -			/* platform don't support ibrs */
> -			sysctl_ibrs_enabled = 0;
> -	}
> -	mutex_unlock(&spec_ctrl_mutex);
> -	pr_debug("after:use_ibrs = %d\n", use_ibrs);
> -	return ret;
> -}
> -#endif
> -
>  static void validate_coredump_safety(void)
>  {
>  #ifdef CONFIG_COREDUMP
Kleber Sacilotto de Souza Jan. 10, 2019, 1:47 p.m. UTC | #5
On 12/13/18 2:21 PM, Juerg Haefliger wrote:
> In Ubuntu, we have runtime control for enabling/disabling IBRS via the
> commandline ("noibrs") and through the proc interface
> /proc/sys/kernel/ibrs_enabled. This commit simplifies the current
> (probably broken) implementation by merging it with all the IBRS-related
> upstream changes from previous commits.
>
> What we have now is the upstream implementation for detecting the presence
> of IBRS support. This commit adds a global state variable 'ibrs_enabled'
> which is set to 1 if the CPU supports IBRS but can be overridden via the
> commandline "noibrs" switch or by writting 0, 1 or 2 to
> /proc/sys/kernel/ibrs_enabled at runtime.
>
> Note that the runtime controls are disabled if the CPU runs in Enhanced
> IBRS mode.
>
> CVE-2017-5715
>
> Signed-off-by: Juerg Haefliger <juergh@canonical.com>
> ---
>  arch/x86/include/asm/mwait.h         |   6 +-
>  arch/x86/include/asm/nospec-branch.h |  20 +++++
>  arch/x86/include/asm/spec_ctrl.h     |  10 ++-
>  arch/x86/kernel/cpu/bugs.c           |  45 ++++-------
>  arch/x86/kernel/cpu/microcode/core.c |  11 ---
>  arch/x86/kernel/process.c            |  10 +--
>  arch/x86/kernel/smpboot.c            |   6 +-
>  arch/x86/lib/delay.c                 |   8 +-
>  include/linux/smp.h                  |  46 -----------
>  kernel/smp.c                         |  28 -------
>  kernel/sysctl.c                      | 115 ++++++++++++++++-----------
>  11 files changed, 123 insertions(+), 182 deletions(-)
>
> diff --git a/arch/x86/include/asm/mwait.h b/arch/x86/include/asm/mwait.h
> index 9821763b02cf..9bd760758640 100644
> --- a/arch/x86/include/asm/mwait.h
> +++ b/arch/x86/include/asm/mwait.h
> @@ -106,15 +106,13 @@ static inline void mwait_idle_with_hints(unsigned long eax, unsigned long ecx)
>  			mb();
>  		}
>  
> -		if (ibrs_inuse)
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +		ubuntu_restrict_branch_speculation_end();
>  
>  		__monitor((void *)&current_thread_info()->flags, 0, 0);
>  		if (!need_resched())
>  			__mwait(eax, ecx);
>  
> -		if (ibrs_inuse)
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +		ubuntu_restrict_branch_speculation_start();
>  	}
>  	current_clr_polling();
>  }
> diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
> index dcc7b0348fbc..a6120d43caa7 100644
> --- a/arch/x86/include/asm/nospec-branch.h
> +++ b/arch/x86/include/asm/nospec-branch.h
> @@ -188,6 +188,10 @@
>  extern unsigned int ibpb_enabled;
>  int set_ibpb_enabled(unsigned int);
>  
> +/* The IBRS runtime control knob */
> +extern unsigned int ibrs_enabled;
> +int set_ibrs_enabled(unsigned int);
> +
>  /* The Spectre V2 mitigation variants */
>  enum spectre_v2_mitigation {
>  	SPECTRE_V2_NONE,
> @@ -275,6 +279,22 @@ do {									\
>  	preempt_enable();						\
>  } while (0)
>  
> +#define ubuntu_restrict_branch_speculation_start()			\
> +do {									\
> +	u64 val = x86_spec_ctrl_base | SPEC_CTRL_IBRS;			\
> +									\
> +	if (ibrs_enabled)						\
> +		native_wrmsrl(MSR_IA32_SPEC_CTRL, val);			\
> +} while (0)
> +
> +#define ubuntu_restrict_branch_speculation_end()			\
> +do {									\
> +	u64 val = x86_spec_ctrl_base;					\
> +									\
> +	if (ibrs_enabled)						\
> +		native_wrmsrl(MSR_IA32_SPEC_CTRL, val);			\
> + } while (0)
> +
>  #endif /* __ASSEMBLY__ */
>  
>  /*
> diff --git a/arch/x86/include/asm/spec_ctrl.h b/arch/x86/include/asm/spec_ctrl.h
> index 49c3b0a83e9f..a5d93d23390e 100644
> --- a/arch/x86/include/asm/spec_ctrl.h
> +++ b/arch/x86/include/asm/spec_ctrl.h
> @@ -8,7 +8,7 @@
>  
>  #ifdef __ASSEMBLY__
>  
> -.extern use_ibrs
> +.extern ibrs_enabled
>  
>  #define __ASM_ENABLE_IBRS			\
>  	pushq %rax;				\
> @@ -21,11 +21,13 @@
>  	popq %rdx;				\
>  	popq %rcx;				\
>  	popq %rax
> +
>  #define __ASM_ENABLE_IBRS_CLOBBER		\
>  	movl $MSR_IA32_SPEC_CTRL, %ecx;		\
>  	movl $0, %edx;				\
>  	movl $SPEC_CTRL_IBRS, %eax;		\
>  	wrmsr;
> +
>  #define __ASM_DISABLE_IBRS			\
>  	pushq %rax;				\
>  	pushq %rcx;				\
> @@ -39,7 +41,7 @@
>  	popq %rax
>  
>  .macro ENABLE_IBRS
> -	testl	$1, use_ibrs
> +	testl	$1, ibrs_enabled
>  	jz	10f
>  	__ASM_ENABLE_IBRS
>  	jmp 20f
> @@ -49,7 +51,7 @@
>  .endm
>  
>  .macro ENABLE_IBRS_CLOBBER
> -	testl	$1, use_ibrs
> +	testl	$1, ibrs_enabled
>  	jz	11f
>  	__ASM_ENABLE_IBRS_CLOBBER
>  	jmp 21f
> @@ -59,7 +61,7 @@
>  .endm
>  
>  .macro DISABLE_IBRS
> -	testl	$1, use_ibrs
> +	testl	$1, ibrs_enabled
>  	jz	9f
>  	__ASM_DISABLE_IBRS
>  9:
> diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
> index eeb89d781a9c..7a9940015af5 100644
> --- a/arch/x86/kernel/cpu/bugs.c
> +++ b/arch/x86/kernel/cpu/bugs.c
> @@ -40,6 +40,16 @@ static int __init noibpb_handler(char *str)
>  
>  early_param("noibpb", noibpb_handler);
>  
> +unsigned int noibrs = 0;
> +
> +static int __init noibrs_handler(char *str)
> +{
> +	noibrs = 1;
> +	return 0;
> +}
> +
> +early_param("noibrs", noibrs_handler);
> +
>  static void __init spectre_v2_select_mitigation(void);
>  static void __init ssb_select_mitigation(void);
>  static void __init l1tf_select_mitigation(void);
> @@ -410,18 +420,6 @@ specv2_set_mode:
>  			set_ibpb_enabled(1);   /* Enable IBPB */
>  	}
>  
> -	/* Initialize Indirect Branch Restricted Speculation if supported */
> -	if (boot_cpu_has(X86_FEATURE_IBRS)) {
> -		pr_info("Spectre v2 mitigation: Enabling Indirect Branch Restricted Speculation\n");
> -
> -		set_ibrs_supported();
> -		if (ibrs_inuse)
> -			sysctl_ibrs_enabled = 1;
> -        }
> -
> -	pr_info("Spectre v2 mitigation: Speculation control IBRS %s",
> -	        ibrs_supported ? "supported" : "not-supported");
> -
>  	/*
>  	 * If spectre v2 protection has been enabled, unconditionally fill
>  	 * RSB during a context switch; this protects against two independent
> @@ -433,21 +431,6 @@ specv2_set_mode:
>  	setup_force_cpu_cap(X86_FEATURE_RSB_CTXSW);
>  	pr_info("Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch\n");
>  
> -	/*
> -	 * If we have a full retpoline mode and then disable IBPB in kernel mode
> -	 * we do not require both.
> -	 */
> -	if (mode == SPECTRE_V2_RETPOLINE_AMD ||
> -	    mode == SPECTRE_V2_RETPOLINE_GENERIC)
> -	{
> -		if (ibrs_supported) {
> -			pr_info("Retpoline compiled kernel.  Defaulting IBRS to disabled");
> -			set_ibrs_disabled();
> -			if (!ibrs_inuse)
> -				sysctl_ibrs_enabled = 0;
> -		}
> -	}
> -
>  	/*
>  	 * Retpoline means the kernel is safe because it has no indirect
>  	 * branches. Enhanced IBRS protects firmware too, so, enable restricted
> @@ -462,6 +445,11 @@ specv2_set_mode:
>  	if (boot_cpu_has(X86_FEATURE_IBRS) && mode != SPECTRE_V2_IBRS_ENHANCED) {
>  		setup_force_cpu_cap(X86_FEATURE_USE_IBRS_FW);
>  		pr_info("Enabling Restricted Speculation for firmware calls\n");
> +
> +		if (!noibrs &&
> +		    mode != SPECTRE_V2_RETPOLINE_GENERIC &&
> +		    mode != SPECTRE_V2_RETPOLINE_AMD)
> +			set_ibrs_enabled(1);   /* Enable IBRS */
>  	}
>  }
>  
> @@ -871,8 +859,9 @@ static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr
>  		break;
>  
>  	case X86_BUG_SPECTRE_V2:
> -		return sprintf(buf, "%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
> +		return sprintf(buf, "%s%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
>  			       ibpb_enabled ? ", IBPB" : "",
> +			       ibrs_enabled == 2 ? ", IBRS (user space)" : ibrs_enabled ? ", IBRS" : "",
>  			       boot_cpu_has(X86_FEATURE_USE_IBRS_FW) ? ", IBRS_FW" : "",
>  			       spectre_v2_module_string());
>  
> diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
> index 84bd97f8eeab..4fe475c2f745 100644
> --- a/arch/x86/kernel/cpu/microcode/core.c
> +++ b/arch/x86/kernel/cpu/microcode/core.c
> @@ -439,17 +439,6 @@ static ssize_t reload_store(struct device *dev,
>  	if (!ret)
>  		perf_check_microcode();
>  
> -	/* Initialize Indirect Branch Restricted Speculation if supported */
> -	if (boot_cpu_has(X86_FEATURE_IBRS)) {
> -		pr_info("Enabling Indirect Branch Restricted Speculation\n");
> -
> -		mutex_lock(&spec_ctrl_mutex);
> -		set_ibrs_supported();
> -		if (ibrs_inuse)
> -			sysctl_ibrs_enabled = 1;
> -		mutex_unlock(&spec_ctrl_mutex);
> -	}
> -
>  	mutex_unlock(&microcode_mutex);
>  	put_online_cpus();
>  
> diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
> index a5b3404266eb..5587098a5823 100644
> --- a/arch/x86/kernel/process.c
> +++ b/arch/x86/kernel/process.c
> @@ -566,17 +566,15 @@ static void mwait_idle(void)
>  			smp_mb(); /* quirk */
>  		}
>  
> -		if (ibrs_inuse)
> -                        native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +		ubuntu_restrict_branch_speculation_end();
>  
>  		__monitor((void *)&current_thread_info()->flags, 0, 0);
> +
>  		if (!need_resched()) {
>  			__sti_mwait(0, 0);
> -			if (ibrs_inuse)
> -				native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +			ubuntu_restrict_branch_speculation_start();
>  		} else {
> -			if (ibrs_inuse)
> -				native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +			ubuntu_restrict_branch_speculation_start();
>  			local_irq_enable();
>  		}
>  		trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id());
> diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
> index 21c3bcbd69a2..6c9bb4db2ed7 100644
> --- a/arch/x86/kernel/smpboot.c
> +++ b/arch/x86/kernel/smpboot.c
> @@ -1697,15 +1697,13 @@ void native_play_dead(void)
>  	play_dead_common();
>  	tboot_shutdown(TB_SHUTDOWN_WFS);
>  
> -	if (ibrs_inuse)
> -		native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +	ubuntu_restrict_branch_speculation_end();
>  
>  	mwait_play_dead();	/* Only returns on failure */
>  	if (cpuidle_play_dead())
>  		hlt_play_dead();
>  
> -	if (ibrs_inuse)
> -		native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +	ubuntu_restrict_branch_speculation_start();
>  }
>  
>  #else /* ... !CONFIG_HOTPLUG_CPU */
> diff --git a/arch/x86/lib/delay.c b/arch/x86/lib/delay.c
> index 5b66d4417f8d..20e9a5ac91dc 100644
> --- a/arch/x86/lib/delay.c
> +++ b/arch/x86/lib/delay.c
> @@ -107,8 +107,8 @@ static void delay_mwaitx(unsigned long __loops)
>  	for (;;) {
>  		delay = min_t(u64, MWAITX_MAX_LOOPS, loops);
>  
> -		if (ibrs_inuse && (delay > IBRS_DISABLE_THRESHOLD))
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> +		if (delay > IBRS_DISABLE_THRESHOLD)
> +			ubuntu_restrict_branch_speculation_end();
>  
>  		/*
>  		 * Use cpu_tss as a cacheline-aligned, seldomly
> @@ -123,8 +123,8 @@ static void delay_mwaitx(unsigned long __loops)
>  		 */
>  		__mwaitx(MWAITX_DISABLE_CSTATES, delay, MWAITX_ECX_TIMER_ENABLE);
>  
> -		if (ibrs_inuse && (delay > IBRS_DISABLE_THRESHOLD))
> -			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> +		if (delay > IBRS_DISABLE_THRESHOLD)
> +			ubuntu_restrict_branch_speculation_start();
>  
>  		end = rdtsc_ordered();
>  
> diff --git a/include/linux/smp.h b/include/linux/smp.h
> index 98ac8ff2afab..c4414074bd88 100644
> --- a/include/linux/smp.h
> +++ b/include/linux/smp.h
> @@ -50,52 +50,6 @@ void on_each_cpu_cond(bool (*cond_func)(int cpu, void *info),
>  
>  int smp_call_function_single_async(int cpu, struct call_single_data *csd);
>  
> -#ifdef CONFIG_X86
> -
> -#define IBxx_INUSE 	(1 << 0)
> -#define IBxx_SUPPORTED 	(1 << 1)
> -#define IBxx_DISABLED 	(1 << 2)
> -
> -/* indicate usage of IBRS to control execution speculation */
> -extern int use_ibrs;
> -extern u32 sysctl_ibrs_enabled;
> -extern struct mutex spec_ctrl_mutex;
> -
> -static inline int __check_ibrs_inuse(void)
> -{
> -	if (use_ibrs & IBxx_INUSE)
> -		return 1;
> -	else
> -		/* rmb to prevent wrong speculation for security */
> -		rmb();
> -	return 0;
> -}
> -
> -#define ibrs_inuse 	(__check_ibrs_inuse())
> -#define ibrs_supported	(use_ibrs & IBxx_SUPPORTED)
> -#define ibrs_disabled	(use_ibrs & IBxx_DISABLED)
> -
> -static inline void set_ibrs_supported(void)
> -{
> -	use_ibrs |= IBxx_SUPPORTED;
> -	if (!ibrs_disabled)
> -		use_ibrs |= IBxx_INUSE;
> -}
> -static inline void set_ibrs_disabled(void)
> -{
> -	use_ibrs |= IBxx_DISABLED;
> -	if (ibrs_inuse)
> -		use_ibrs &= ~IBxx_INUSE;
> -}
> -static inline void clear_ibrs_disabled(void)
> -{
> -	use_ibrs &= ~IBxx_DISABLED;
> -	if (ibrs_supported)
> -		use_ibrs |= IBxx_INUSE;
> -}
> -
> -#endif /* CONFIG_X86 */
> -
>  #ifdef CONFIG_SMP
>  
>  #include <linux/preempt.h>
> diff --git a/kernel/smp.c b/kernel/smp.c
> index 139681ddf916..48eb4fafc356 100644
> --- a/kernel/smp.c
> +++ b/kernel/smp.c
> @@ -499,22 +499,6 @@ EXPORT_SYMBOL(smp_call_function);
>  unsigned int setup_max_cpus = NR_CPUS;
>  EXPORT_SYMBOL(setup_max_cpus);
>  
> -#ifdef CONFIG_X86
> -/*
> - * use IBRS
> - * bit 0 = indicate if ibrs is currently in use
> - * bit 1 = indicate if system supports ibrs
> - * bit 2 = indicate if admin disables ibrs
> -*/
> -
> -int use_ibrs;
> -EXPORT_SYMBOL(use_ibrs);
> -#endif
> -
> -/* mutex to serialize IBRS & IBPB control changes */
> -DEFINE_MUTEX(spec_ctrl_mutex);
> -EXPORT_SYMBOL(spec_ctrl_mutex);
> -
>  /*
>   * Setup routine for controlling SMP activation
>   *
> @@ -538,18 +522,6 @@ static int __init nosmp(char *str)
>  
>  early_param("nosmp", nosmp);
>  
> -#ifdef CONFIG_X86
> -static int __init noibrs(char *str)
> -{
> -	set_ibrs_disabled();
> -
> -	return 0;
> -}
> -
> -early_param("noibrs", noibrs);
> -#endif
> -
> -
>  /* this is hard limit */
>  static int __init nrcpus(char *str)
>  {
> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> index d00f95726ac4..02df9c031a45 100644
> --- a/kernel/sysctl.c
> +++ b/kernel/sysctl.c
> @@ -202,8 +202,8 @@ static int proc_dostring_coredump(struct ctl_table *table, int write,
>  #endif
>  
>  #ifdef CONFIG_X86
> -int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write,
> -                 void __user *buffer, size_t *lenp, loff_t *ppos);
> +/* Mutex to serialize IBPB and IBRS runtime control changes */
> +DEFINE_MUTEX(spec_ctrl_mutex);
>  
>  unsigned int ibpb_enabled = 0;
>  EXPORT_SYMBOL(ibpb_enabled);   /* Required in some modules */
> @@ -252,6 +252,70 @@ static int ibpb_enabled_handler(struct ctl_table *table, int write,
>  
>  	return set_ibpb_enabled(__ibpb_enabled);
>  }
> +
> +unsigned int ibrs_enabled = 0;
> +EXPORT_SYMBOL(ibrs_enabled);   /* Required in some modules */
> +
> +static unsigned int __ibrs_enabled = 0;   /* procfs shadow variable */
> +
> +int set_ibrs_enabled(unsigned int val)
> +{
> +	int error = 0;
> +	unsigned int cpu;
> +	unsigned int prev = ibrs_enabled;
> +
> +	mutex_lock(&spec_ctrl_mutex);
> +
> +	/* Only enable/disable IBRS if the CPU supports it */
> +	if (boot_cpu_has(X86_FEATURE_USE_IBRS_FW)) {
> +		ibrs_enabled = val;
> +		if (ibrs_enabled != prev)
> +			pr_info("Spectre V2 : Spectre v2 mitigation: %s "
> +				"Indirect Branch Restricted Speculation%s\n",
> +				ibrs_enabled ? "Enabling" : "Disabling",
> +				ibrs_enabled == 2 ? " (user space)" : "");
> +
> +		if (ibrs_enabled == 0) {
> +			/* Always disable IBRS */
> +			u64 val = x86_spec_ctrl_base;
> +
> +			for_each_online_cpu(cpu)
> +				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, val);
> +		} else if (ibrs_enabled == 2) {
> +			/* Always enable IBRS, even in user space */
> +			u64 val = x86_spec_ctrl_base | SPEC_CTRL_IBRS;
> +
> +			for_each_online_cpu(cpu)
> +				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, val);
> +		}
> +	} else {
> +		ibrs_enabled = 0;
> +		if (val) {
> +			/* IBRS is not supported but we try to turn it on */
> +			error = -EINVAL;
> +		}
> +	}
> +
> +	/* Update the shadow variable */
> +	__ibrs_enabled = ibrs_enabled;
> +
> +	mutex_unlock(&spec_ctrl_mutex);
> +
> +	return error;
> +}
> +
> +static int ibrs_enabled_handler(struct ctl_table *table, int write,
> +                               void __user *buffer, size_t *lenp,
> +                               loff_t *ppos)
> +{
> +	int error;
> +
> +	error = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
> +	if (error)
> +		return error;
> +
> +	return set_ibrs_enabled(__ibrs_enabled);
> +}
>  #endif
>  
>  #ifdef CONFIG_MAGIC_SYSRQ
> @@ -290,9 +354,6 @@ extern struct ctl_table epoll_table[];
>  int sysctl_legacy_va_layout;
>  #endif
>  
> -u32 sysctl_ibrs_enabled = 0;
> -EXPORT_SYMBOL(sysctl_ibrs_enabled);
> -
>  /* The default sysctl tables: */
>  
>  static struct ctl_table sysctl_base_table[] = {
> @@ -1281,10 +1342,10 @@ static struct ctl_table kern_table[] = {
>  #ifdef CONFIG_X86
>  	{
>  		.procname       = "ibrs_enabled",
> -		.data           = &sysctl_ibrs_enabled,
> +		.data           = &__ibrs_enabled,
>  		.maxlen         = sizeof(unsigned int),
>  		.mode           = 0644,
> -		.proc_handler   = proc_dointvec_ibrs_ctrl,
> +		.proc_handler   = ibrs_enabled_handler,
>  		.extra1         = &zero,
>  		.extra2         = &two,
>  	},
> @@ -2455,46 +2516,6 @@ int proc_dointvec_minmax(struct ctl_table *table, int write,
>  				do_proc_dointvec_minmax_conv, &param);
>  }
>  
> -#ifdef CONFIG_X86
> -int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write,
> -	void __user *buffer, size_t *lenp, loff_t *ppos)
> -{
> -	int ret;
> -	unsigned int cpu;
> -
> -	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
> -	pr_debug("sysctl_ibrs_enabled = %u\n", sysctl_ibrs_enabled);
> -	pr_debug("before:use_ibrs = %d\n", use_ibrs);
> -	mutex_lock(&spec_ctrl_mutex);
> -	if (sysctl_ibrs_enabled == 0) {
> -		/* always set IBRS off */
> -		set_ibrs_disabled();
> -		if (ibrs_supported) {
> -			for_each_online_cpu(cpu)
> -				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
> -		}
> -	} else if (sysctl_ibrs_enabled == 2) {
> -		/* always set IBRS on, even in user space */
> -		clear_ibrs_disabled();
> -		if (ibrs_supported) {
> -			for_each_online_cpu(cpu)
> -				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
> -		} else {
> -			sysctl_ibrs_enabled = 0;
> -		}
> -	} else if (sysctl_ibrs_enabled == 1) {
> -		/* use IBRS in kernel */
> -		clear_ibrs_disabled();
> -		if (!ibrs_inuse)
> -			/* platform don't support ibrs */
> -			sysctl_ibrs_enabled = 0;
> -	}
> -	mutex_unlock(&spec_ctrl_mutex);
> -	pr_debug("after:use_ibrs = %d\n", use_ibrs);
> -	return ret;
> -}
> -#endif
> -
>  static void validate_coredump_safety(void)
>  {
>  #ifdef CONFIG_COREDUMP

Applied to xenial/master-next branch.

Thanks,
Kleber
diff mbox series

Patch

diff --git a/arch/x86/include/asm/mwait.h b/arch/x86/include/asm/mwait.h
index 9821763b02cf..9bd760758640 100644
--- a/arch/x86/include/asm/mwait.h
+++ b/arch/x86/include/asm/mwait.h
@@ -106,15 +106,13 @@  static inline void mwait_idle_with_hints(unsigned long eax, unsigned long ecx)
 			mb();
 		}
 
-		if (ibrs_inuse)
-			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
+		ubuntu_restrict_branch_speculation_end();
 
 		__monitor((void *)&current_thread_info()->flags, 0, 0);
 		if (!need_resched())
 			__mwait(eax, ecx);
 
-		if (ibrs_inuse)
-			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
+		ubuntu_restrict_branch_speculation_start();
 	}
 	current_clr_polling();
 }
diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
index dcc7b0348fbc..a6120d43caa7 100644
--- a/arch/x86/include/asm/nospec-branch.h
+++ b/arch/x86/include/asm/nospec-branch.h
@@ -188,6 +188,10 @@ 
 extern unsigned int ibpb_enabled;
 int set_ibpb_enabled(unsigned int);
 
+/* The IBRS runtime control knob */
+extern unsigned int ibrs_enabled;
+int set_ibrs_enabled(unsigned int);
+
 /* The Spectre V2 mitigation variants */
 enum spectre_v2_mitigation {
 	SPECTRE_V2_NONE,
@@ -275,6 +279,22 @@  do {									\
 	preempt_enable();						\
 } while (0)
 
+#define ubuntu_restrict_branch_speculation_start()			\
+do {									\
+	u64 val = x86_spec_ctrl_base | SPEC_CTRL_IBRS;			\
+									\
+	if (ibrs_enabled)						\
+		native_wrmsrl(MSR_IA32_SPEC_CTRL, val);			\
+} while (0)
+
+#define ubuntu_restrict_branch_speculation_end()			\
+do {									\
+	u64 val = x86_spec_ctrl_base;					\
+									\
+	if (ibrs_enabled)						\
+		native_wrmsrl(MSR_IA32_SPEC_CTRL, val);			\
+ } while (0)
+
 #endif /* __ASSEMBLY__ */
 
 /*
diff --git a/arch/x86/include/asm/spec_ctrl.h b/arch/x86/include/asm/spec_ctrl.h
index 49c3b0a83e9f..a5d93d23390e 100644
--- a/arch/x86/include/asm/spec_ctrl.h
+++ b/arch/x86/include/asm/spec_ctrl.h
@@ -8,7 +8,7 @@ 
 
 #ifdef __ASSEMBLY__
 
-.extern use_ibrs
+.extern ibrs_enabled
 
 #define __ASM_ENABLE_IBRS			\
 	pushq %rax;				\
@@ -21,11 +21,13 @@ 
 	popq %rdx;				\
 	popq %rcx;				\
 	popq %rax
+
 #define __ASM_ENABLE_IBRS_CLOBBER		\
 	movl $MSR_IA32_SPEC_CTRL, %ecx;		\
 	movl $0, %edx;				\
 	movl $SPEC_CTRL_IBRS, %eax;		\
 	wrmsr;
+
 #define __ASM_DISABLE_IBRS			\
 	pushq %rax;				\
 	pushq %rcx;				\
@@ -39,7 +41,7 @@ 
 	popq %rax
 
 .macro ENABLE_IBRS
-	testl	$1, use_ibrs
+	testl	$1, ibrs_enabled
 	jz	10f
 	__ASM_ENABLE_IBRS
 	jmp 20f
@@ -49,7 +51,7 @@ 
 .endm
 
 .macro ENABLE_IBRS_CLOBBER
-	testl	$1, use_ibrs
+	testl	$1, ibrs_enabled
 	jz	11f
 	__ASM_ENABLE_IBRS_CLOBBER
 	jmp 21f
@@ -59,7 +61,7 @@ 
 .endm
 
 .macro DISABLE_IBRS
-	testl	$1, use_ibrs
+	testl	$1, ibrs_enabled
 	jz	9f
 	__ASM_DISABLE_IBRS
 9:
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index eeb89d781a9c..7a9940015af5 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -40,6 +40,16 @@  static int __init noibpb_handler(char *str)
 
 early_param("noibpb", noibpb_handler);
 
+unsigned int noibrs = 0;
+
+static int __init noibrs_handler(char *str)
+{
+	noibrs = 1;
+	return 0;
+}
+
+early_param("noibrs", noibrs_handler);
+
 static void __init spectre_v2_select_mitigation(void);
 static void __init ssb_select_mitigation(void);
 static void __init l1tf_select_mitigation(void);
@@ -410,18 +420,6 @@  specv2_set_mode:
 			set_ibpb_enabled(1);   /* Enable IBPB */
 	}
 
-	/* Initialize Indirect Branch Restricted Speculation if supported */
-	if (boot_cpu_has(X86_FEATURE_IBRS)) {
-		pr_info("Spectre v2 mitigation: Enabling Indirect Branch Restricted Speculation\n");
-
-		set_ibrs_supported();
-		if (ibrs_inuse)
-			sysctl_ibrs_enabled = 1;
-        }
-
-	pr_info("Spectre v2 mitigation: Speculation control IBRS %s",
-	        ibrs_supported ? "supported" : "not-supported");
-
 	/*
 	 * If spectre v2 protection has been enabled, unconditionally fill
 	 * RSB during a context switch; this protects against two independent
@@ -433,21 +431,6 @@  specv2_set_mode:
 	setup_force_cpu_cap(X86_FEATURE_RSB_CTXSW);
 	pr_info("Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch\n");
 
-	/*
-	 * If we have a full retpoline mode and then disable IBPB in kernel mode
-	 * we do not require both.
-	 */
-	if (mode == SPECTRE_V2_RETPOLINE_AMD ||
-	    mode == SPECTRE_V2_RETPOLINE_GENERIC)
-	{
-		if (ibrs_supported) {
-			pr_info("Retpoline compiled kernel.  Defaulting IBRS to disabled");
-			set_ibrs_disabled();
-			if (!ibrs_inuse)
-				sysctl_ibrs_enabled = 0;
-		}
-	}
-
 	/*
 	 * Retpoline means the kernel is safe because it has no indirect
 	 * branches. Enhanced IBRS protects firmware too, so, enable restricted
@@ -462,6 +445,11 @@  specv2_set_mode:
 	if (boot_cpu_has(X86_FEATURE_IBRS) && mode != SPECTRE_V2_IBRS_ENHANCED) {
 		setup_force_cpu_cap(X86_FEATURE_USE_IBRS_FW);
 		pr_info("Enabling Restricted Speculation for firmware calls\n");
+
+		if (!noibrs &&
+		    mode != SPECTRE_V2_RETPOLINE_GENERIC &&
+		    mode != SPECTRE_V2_RETPOLINE_AMD)
+			set_ibrs_enabled(1);   /* Enable IBRS */
 	}
 }
 
@@ -871,8 +859,9 @@  static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr
 		break;
 
 	case X86_BUG_SPECTRE_V2:
-		return sprintf(buf, "%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
+		return sprintf(buf, "%s%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
 			       ibpb_enabled ? ", IBPB" : "",
+			       ibrs_enabled == 2 ? ", IBRS (user space)" : ibrs_enabled ? ", IBRS" : "",
 			       boot_cpu_has(X86_FEATURE_USE_IBRS_FW) ? ", IBRS_FW" : "",
 			       spectre_v2_module_string());
 
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index 84bd97f8eeab..4fe475c2f745 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -439,17 +439,6 @@  static ssize_t reload_store(struct device *dev,
 	if (!ret)
 		perf_check_microcode();
 
-	/* Initialize Indirect Branch Restricted Speculation if supported */
-	if (boot_cpu_has(X86_FEATURE_IBRS)) {
-		pr_info("Enabling Indirect Branch Restricted Speculation\n");
-
-		mutex_lock(&spec_ctrl_mutex);
-		set_ibrs_supported();
-		if (ibrs_inuse)
-			sysctl_ibrs_enabled = 1;
-		mutex_unlock(&spec_ctrl_mutex);
-	}
-
 	mutex_unlock(&microcode_mutex);
 	put_online_cpus();
 
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index a5b3404266eb..5587098a5823 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -566,17 +566,15 @@  static void mwait_idle(void)
 			smp_mb(); /* quirk */
 		}
 
-		if (ibrs_inuse)
-                        native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
+		ubuntu_restrict_branch_speculation_end();
 
 		__monitor((void *)&current_thread_info()->flags, 0, 0);
+
 		if (!need_resched()) {
 			__sti_mwait(0, 0);
-			if (ibrs_inuse)
-				native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
+			ubuntu_restrict_branch_speculation_start();
 		} else {
-			if (ibrs_inuse)
-				native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
+			ubuntu_restrict_branch_speculation_start();
 			local_irq_enable();
 		}
 		trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id());
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 21c3bcbd69a2..6c9bb4db2ed7 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -1697,15 +1697,13 @@  void native_play_dead(void)
 	play_dead_common();
 	tboot_shutdown(TB_SHUTDOWN_WFS);
 
-	if (ibrs_inuse)
-		native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
+	ubuntu_restrict_branch_speculation_end();
 
 	mwait_play_dead();	/* Only returns on failure */
 	if (cpuidle_play_dead())
 		hlt_play_dead();
 
-	if (ibrs_inuse)
-		native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
+	ubuntu_restrict_branch_speculation_start();
 }
 
 #else /* ... !CONFIG_HOTPLUG_CPU */
diff --git a/arch/x86/lib/delay.c b/arch/x86/lib/delay.c
index 5b66d4417f8d..20e9a5ac91dc 100644
--- a/arch/x86/lib/delay.c
+++ b/arch/x86/lib/delay.c
@@ -107,8 +107,8 @@  static void delay_mwaitx(unsigned long __loops)
 	for (;;) {
 		delay = min_t(u64, MWAITX_MAX_LOOPS, loops);
 
-		if (ibrs_inuse && (delay > IBRS_DISABLE_THRESHOLD))
-			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
+		if (delay > IBRS_DISABLE_THRESHOLD)
+			ubuntu_restrict_branch_speculation_end();
 
 		/*
 		 * Use cpu_tss as a cacheline-aligned, seldomly
@@ -123,8 +123,8 @@  static void delay_mwaitx(unsigned long __loops)
 		 */
 		__mwaitx(MWAITX_DISABLE_CSTATES, delay, MWAITX_ECX_TIMER_ENABLE);
 
-		if (ibrs_inuse && (delay > IBRS_DISABLE_THRESHOLD))
-			native_wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
+		if (delay > IBRS_DISABLE_THRESHOLD)
+			ubuntu_restrict_branch_speculation_start();
 
 		end = rdtsc_ordered();
 
diff --git a/include/linux/smp.h b/include/linux/smp.h
index 98ac8ff2afab..c4414074bd88 100644
--- a/include/linux/smp.h
+++ b/include/linux/smp.h
@@ -50,52 +50,6 @@  void on_each_cpu_cond(bool (*cond_func)(int cpu, void *info),
 
 int smp_call_function_single_async(int cpu, struct call_single_data *csd);
 
-#ifdef CONFIG_X86
-
-#define IBxx_INUSE 	(1 << 0)
-#define IBxx_SUPPORTED 	(1 << 1)
-#define IBxx_DISABLED 	(1 << 2)
-
-/* indicate usage of IBRS to control execution speculation */
-extern int use_ibrs;
-extern u32 sysctl_ibrs_enabled;
-extern struct mutex spec_ctrl_mutex;
-
-static inline int __check_ibrs_inuse(void)
-{
-	if (use_ibrs & IBxx_INUSE)
-		return 1;
-	else
-		/* rmb to prevent wrong speculation for security */
-		rmb();
-	return 0;
-}
-
-#define ibrs_inuse 	(__check_ibrs_inuse())
-#define ibrs_supported	(use_ibrs & IBxx_SUPPORTED)
-#define ibrs_disabled	(use_ibrs & IBxx_DISABLED)
-
-static inline void set_ibrs_supported(void)
-{
-	use_ibrs |= IBxx_SUPPORTED;
-	if (!ibrs_disabled)
-		use_ibrs |= IBxx_INUSE;
-}
-static inline void set_ibrs_disabled(void)
-{
-	use_ibrs |= IBxx_DISABLED;
-	if (ibrs_inuse)
-		use_ibrs &= ~IBxx_INUSE;
-}
-static inline void clear_ibrs_disabled(void)
-{
-	use_ibrs &= ~IBxx_DISABLED;
-	if (ibrs_supported)
-		use_ibrs |= IBxx_INUSE;
-}
-
-#endif /* CONFIG_X86 */
-
 #ifdef CONFIG_SMP
 
 #include <linux/preempt.h>
diff --git a/kernel/smp.c b/kernel/smp.c
index 139681ddf916..48eb4fafc356 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -499,22 +499,6 @@  EXPORT_SYMBOL(smp_call_function);
 unsigned int setup_max_cpus = NR_CPUS;
 EXPORT_SYMBOL(setup_max_cpus);
 
-#ifdef CONFIG_X86
-/*
- * use IBRS
- * bit 0 = indicate if ibrs is currently in use
- * bit 1 = indicate if system supports ibrs
- * bit 2 = indicate if admin disables ibrs
-*/
-
-int use_ibrs;
-EXPORT_SYMBOL(use_ibrs);
-#endif
-
-/* mutex to serialize IBRS & IBPB control changes */
-DEFINE_MUTEX(spec_ctrl_mutex);
-EXPORT_SYMBOL(spec_ctrl_mutex);
-
 /*
  * Setup routine for controlling SMP activation
  *
@@ -538,18 +522,6 @@  static int __init nosmp(char *str)
 
 early_param("nosmp", nosmp);
 
-#ifdef CONFIG_X86
-static int __init noibrs(char *str)
-{
-	set_ibrs_disabled();
-
-	return 0;
-}
-
-early_param("noibrs", noibrs);
-#endif
-
-
 /* this is hard limit */
 static int __init nrcpus(char *str)
 {
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index d00f95726ac4..02df9c031a45 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -202,8 +202,8 @@  static int proc_dostring_coredump(struct ctl_table *table, int write,
 #endif
 
 #ifdef CONFIG_X86
-int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write,
-                 void __user *buffer, size_t *lenp, loff_t *ppos);
+/* Mutex to serialize IBPB and IBRS runtime control changes */
+DEFINE_MUTEX(spec_ctrl_mutex);
 
 unsigned int ibpb_enabled = 0;
 EXPORT_SYMBOL(ibpb_enabled);   /* Required in some modules */
@@ -252,6 +252,70 @@  static int ibpb_enabled_handler(struct ctl_table *table, int write,
 
 	return set_ibpb_enabled(__ibpb_enabled);
 }
+
+unsigned int ibrs_enabled = 0;
+EXPORT_SYMBOL(ibrs_enabled);   /* Required in some modules */
+
+static unsigned int __ibrs_enabled = 0;   /* procfs shadow variable */
+
+int set_ibrs_enabled(unsigned int val)
+{
+	int error = 0;
+	unsigned int cpu;
+	unsigned int prev = ibrs_enabled;
+
+	mutex_lock(&spec_ctrl_mutex);
+
+	/* Only enable/disable IBRS if the CPU supports it */
+	if (boot_cpu_has(X86_FEATURE_USE_IBRS_FW)) {
+		ibrs_enabled = val;
+		if (ibrs_enabled != prev)
+			pr_info("Spectre V2 : Spectre v2 mitigation: %s "
+				"Indirect Branch Restricted Speculation%s\n",
+				ibrs_enabled ? "Enabling" : "Disabling",
+				ibrs_enabled == 2 ? " (user space)" : "");
+
+		if (ibrs_enabled == 0) {
+			/* Always disable IBRS */
+			u64 val = x86_spec_ctrl_base;
+
+			for_each_online_cpu(cpu)
+				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, val);
+		} else if (ibrs_enabled == 2) {
+			/* Always enable IBRS, even in user space */
+			u64 val = x86_spec_ctrl_base | SPEC_CTRL_IBRS;
+
+			for_each_online_cpu(cpu)
+				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, val);
+		}
+	} else {
+		ibrs_enabled = 0;
+		if (val) {
+			/* IBRS is not supported but we try to turn it on */
+			error = -EINVAL;
+		}
+	}
+
+	/* Update the shadow variable */
+	__ibrs_enabled = ibrs_enabled;
+
+	mutex_unlock(&spec_ctrl_mutex);
+
+	return error;
+}
+
+static int ibrs_enabled_handler(struct ctl_table *table, int write,
+                               void __user *buffer, size_t *lenp,
+                               loff_t *ppos)
+{
+	int error;
+
+	error = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+	if (error)
+		return error;
+
+	return set_ibrs_enabled(__ibrs_enabled);
+}
 #endif
 
 #ifdef CONFIG_MAGIC_SYSRQ
@@ -290,9 +354,6 @@  extern struct ctl_table epoll_table[];
 int sysctl_legacy_va_layout;
 #endif
 
-u32 sysctl_ibrs_enabled = 0;
-EXPORT_SYMBOL(sysctl_ibrs_enabled);
-
 /* The default sysctl tables: */
 
 static struct ctl_table sysctl_base_table[] = {
@@ -1281,10 +1342,10 @@  static struct ctl_table kern_table[] = {
 #ifdef CONFIG_X86
 	{
 		.procname       = "ibrs_enabled",
-		.data           = &sysctl_ibrs_enabled,
+		.data           = &__ibrs_enabled,
 		.maxlen         = sizeof(unsigned int),
 		.mode           = 0644,
-		.proc_handler   = proc_dointvec_ibrs_ctrl,
+		.proc_handler   = ibrs_enabled_handler,
 		.extra1         = &zero,
 		.extra2         = &two,
 	},
@@ -2455,46 +2516,6 @@  int proc_dointvec_minmax(struct ctl_table *table, int write,
 				do_proc_dointvec_minmax_conv, &param);
 }
 
-#ifdef CONFIG_X86
-int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write,
-	void __user *buffer, size_t *lenp, loff_t *ppos)
-{
-	int ret;
-	unsigned int cpu;
-
-	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
-	pr_debug("sysctl_ibrs_enabled = %u\n", sysctl_ibrs_enabled);
-	pr_debug("before:use_ibrs = %d\n", use_ibrs);
-	mutex_lock(&spec_ctrl_mutex);
-	if (sysctl_ibrs_enabled == 0) {
-		/* always set IBRS off */
-		set_ibrs_disabled();
-		if (ibrs_supported) {
-			for_each_online_cpu(cpu)
-				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
-		}
-	} else if (sysctl_ibrs_enabled == 2) {
-		/* always set IBRS on, even in user space */
-		clear_ibrs_disabled();
-		if (ibrs_supported) {
-			for_each_online_cpu(cpu)
-				wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base | SPEC_CTRL_IBRS);
-		} else {
-			sysctl_ibrs_enabled = 0;
-		}
-	} else if (sysctl_ibrs_enabled == 1) {
-		/* use IBRS in kernel */
-		clear_ibrs_disabled();
-		if (!ibrs_inuse)
-			/* platform don't support ibrs */
-			sysctl_ibrs_enabled = 0;
-	}
-	mutex_unlock(&spec_ctrl_mutex);
-	pr_debug("after:use_ibrs = %d\n", use_ibrs);
-	return ret;
-}
-#endif
-
 static void validate_coredump_safety(void)
 {
 #ifdef CONFIG_COREDUMP