-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
arm64: ssbd: Add prctl interface for per-thread mitigation
If running on a system that performs dynamic SSBD mitigation, allow userspace to request the mitigation for itself. This is implemented as a prctl call, allowing the mitigation to be enabled or disabled at will for this particular thread. Acked-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
- Loading branch information
Marc Zyngier
authored and
Catalin Marinas
committed
May 31, 2018
1 parent
9dd9614
commit 9cdc010
Showing
2 changed files
with
111 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* Copyright (C) 2018 ARM Ltd, All Rights Reserved. | ||
*/ | ||
|
||
#include <linux/errno.h> | ||
#include <linux/sched.h> | ||
#include <linux/thread_info.h> | ||
|
||
#include <asm/cpufeature.h> | ||
|
||
/* | ||
* prctl interface for SSBD | ||
* FIXME: Drop the below ifdefery once merged in 4.18. | ||
*/ | ||
#ifdef PR_SPEC_STORE_BYPASS | ||
static int ssbd_prctl_set(struct task_struct *task, unsigned long ctrl) | ||
{ | ||
int state = arm64_get_ssbd_state(); | ||
|
||
/* Unsupported */ | ||
if (state == ARM64_SSBD_UNKNOWN) | ||
return -EINVAL; | ||
|
||
/* Treat the unaffected/mitigated state separately */ | ||
if (state == ARM64_SSBD_MITIGATED) { | ||
switch (ctrl) { | ||
case PR_SPEC_ENABLE: | ||
return -EPERM; | ||
case PR_SPEC_DISABLE: | ||
case PR_SPEC_FORCE_DISABLE: | ||
return 0; | ||
} | ||
} | ||
|
||
/* | ||
* Things are a bit backward here: the arm64 internal API | ||
* *enables the mitigation* when the userspace API *disables | ||
* speculation*. So much fun. | ||
*/ | ||
switch (ctrl) { | ||
case PR_SPEC_ENABLE: | ||
/* If speculation is force disabled, enable is not allowed */ | ||
if (state == ARM64_SSBD_FORCE_ENABLE || | ||
task_spec_ssb_force_disable(task)) | ||
return -EPERM; | ||
task_clear_spec_ssb_disable(task); | ||
clear_tsk_thread_flag(task, TIF_SSBD); | ||
break; | ||
case PR_SPEC_DISABLE: | ||
if (state == ARM64_SSBD_FORCE_DISABLE) | ||
return -EPERM; | ||
task_set_spec_ssb_disable(task); | ||
set_tsk_thread_flag(task, TIF_SSBD); | ||
break; | ||
case PR_SPEC_FORCE_DISABLE: | ||
if (state == ARM64_SSBD_FORCE_DISABLE) | ||
return -EPERM; | ||
task_set_spec_ssb_disable(task); | ||
task_set_spec_ssb_force_disable(task); | ||
set_tsk_thread_flag(task, TIF_SSBD); | ||
break; | ||
default: | ||
return -ERANGE; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which, | ||
unsigned long ctrl) | ||
{ | ||
switch (which) { | ||
case PR_SPEC_STORE_BYPASS: | ||
return ssbd_prctl_set(task, ctrl); | ||
default: | ||
return -ENODEV; | ||
} | ||
} | ||
|
||
static int ssbd_prctl_get(struct task_struct *task) | ||
{ | ||
switch (arm64_get_ssbd_state()) { | ||
case ARM64_SSBD_UNKNOWN: | ||
return -EINVAL; | ||
case ARM64_SSBD_FORCE_ENABLE: | ||
return PR_SPEC_DISABLE; | ||
case ARM64_SSBD_KERNEL: | ||
if (task_spec_ssb_force_disable(task)) | ||
return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE; | ||
if (task_spec_ssb_disable(task)) | ||
return PR_SPEC_PRCTL | PR_SPEC_DISABLE; | ||
return PR_SPEC_PRCTL | PR_SPEC_ENABLE; | ||
case ARM64_SSBD_FORCE_DISABLE: | ||
return PR_SPEC_ENABLE; | ||
default: | ||
return PR_SPEC_NOT_AFFECTED; | ||
} | ||
} | ||
|
||
int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which) | ||
{ | ||
switch (which) { | ||
case PR_SPEC_STORE_BYPASS: | ||
return ssbd_prctl_get(task); | ||
default: | ||
return -ENODEV; | ||
} | ||
} | ||
#endif /* PR_SPEC_STORE_BYPASS */ |