]>
Commit | Line | Data |
---|---|---|
9cdc0108 MZ |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2018 ARM Ltd, All Rights Reserved. | |
4 | */ | |
5 | ||
8f04e8e6 | 6 | #include <linux/compat.h> |
9cdc0108 | 7 | #include <linux/errno.h> |
adeaa21a | 8 | #include <linux/prctl.h> |
9cdc0108 | 9 | #include <linux/sched.h> |
8f04e8e6 | 10 | #include <linux/sched/task_stack.h> |
9cdc0108 MZ |
11 | #include <linux/thread_info.h> |
12 | ||
13 | #include <asm/cpufeature.h> | |
14 | ||
8f04e8e6 WD |
15 | static void ssbd_ssbs_enable(struct task_struct *task) |
16 | { | |
17 | u64 val = is_compat_thread(task_thread_info(task)) ? | |
18 | PSR_AA32_SSBS_BIT : PSR_SSBS_BIT; | |
19 | ||
20 | task_pt_regs(task)->pstate |= val; | |
21 | } | |
22 | ||
23 | static void ssbd_ssbs_disable(struct task_struct *task) | |
24 | { | |
25 | u64 val = is_compat_thread(task_thread_info(task)) ? | |
26 | PSR_AA32_SSBS_BIT : PSR_SSBS_BIT; | |
27 | ||
28 | task_pt_regs(task)->pstate &= ~val; | |
29 | } | |
30 | ||
9cdc0108 MZ |
31 | /* |
32 | * prctl interface for SSBD | |
9cdc0108 | 33 | */ |
9cdc0108 MZ |
34 | static int ssbd_prctl_set(struct task_struct *task, unsigned long ctrl) |
35 | { | |
36 | int state = arm64_get_ssbd_state(); | |
37 | ||
38 | /* Unsupported */ | |
39 | if (state == ARM64_SSBD_UNKNOWN) | |
40 | return -EINVAL; | |
41 | ||
42 | /* Treat the unaffected/mitigated state separately */ | |
43 | if (state == ARM64_SSBD_MITIGATED) { | |
44 | switch (ctrl) { | |
45 | case PR_SPEC_ENABLE: | |
46 | return -EPERM; | |
47 | case PR_SPEC_DISABLE: | |
48 | case PR_SPEC_FORCE_DISABLE: | |
49 | return 0; | |
50 | } | |
51 | } | |
52 | ||
53 | /* | |
54 | * Things are a bit backward here: the arm64 internal API | |
55 | * *enables the mitigation* when the userspace API *disables | |
56 | * speculation*. So much fun. | |
57 | */ | |
58 | switch (ctrl) { | |
59 | case PR_SPEC_ENABLE: | |
60 | /* If speculation is force disabled, enable is not allowed */ | |
61 | if (state == ARM64_SSBD_FORCE_ENABLE || | |
62 | task_spec_ssb_force_disable(task)) | |
63 | return -EPERM; | |
64 | task_clear_spec_ssb_disable(task); | |
65 | clear_tsk_thread_flag(task, TIF_SSBD); | |
8f04e8e6 | 66 | ssbd_ssbs_enable(task); |
9cdc0108 MZ |
67 | break; |
68 | case PR_SPEC_DISABLE: | |
69 | if (state == ARM64_SSBD_FORCE_DISABLE) | |
70 | return -EPERM; | |
71 | task_set_spec_ssb_disable(task); | |
72 | set_tsk_thread_flag(task, TIF_SSBD); | |
8f04e8e6 | 73 | ssbd_ssbs_disable(task); |
9cdc0108 MZ |
74 | break; |
75 | case PR_SPEC_FORCE_DISABLE: | |
76 | if (state == ARM64_SSBD_FORCE_DISABLE) | |
77 | return -EPERM; | |
78 | task_set_spec_ssb_disable(task); | |
79 | task_set_spec_ssb_force_disable(task); | |
80 | set_tsk_thread_flag(task, TIF_SSBD); | |
8f04e8e6 | 81 | ssbd_ssbs_disable(task); |
9cdc0108 MZ |
82 | break; |
83 | default: | |
84 | return -ERANGE; | |
85 | } | |
86 | ||
87 | return 0; | |
88 | } | |
89 | ||
90 | int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which, | |
91 | unsigned long ctrl) | |
92 | { | |
93 | switch (which) { | |
94 | case PR_SPEC_STORE_BYPASS: | |
95 | return ssbd_prctl_set(task, ctrl); | |
96 | default: | |
97 | return -ENODEV; | |
98 | } | |
99 | } | |
100 | ||
101 | static int ssbd_prctl_get(struct task_struct *task) | |
102 | { | |
103 | switch (arm64_get_ssbd_state()) { | |
104 | case ARM64_SSBD_UNKNOWN: | |
105 | return -EINVAL; | |
106 | case ARM64_SSBD_FORCE_ENABLE: | |
107 | return PR_SPEC_DISABLE; | |
108 | case ARM64_SSBD_KERNEL: | |
109 | if (task_spec_ssb_force_disable(task)) | |
110 | return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE; | |
111 | if (task_spec_ssb_disable(task)) | |
112 | return PR_SPEC_PRCTL | PR_SPEC_DISABLE; | |
113 | return PR_SPEC_PRCTL | PR_SPEC_ENABLE; | |
114 | case ARM64_SSBD_FORCE_DISABLE: | |
115 | return PR_SPEC_ENABLE; | |
116 | default: | |
117 | return PR_SPEC_NOT_AFFECTED; | |
118 | } | |
119 | } | |
120 | ||
121 | int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which) | |
122 | { | |
123 | switch (which) { | |
124 | case PR_SPEC_STORE_BYPASS: | |
125 | return ssbd_prctl_get(task); | |
126 | default: | |
127 | return -ENODEV; | |
128 | } | |
129 | } |