Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 ARM Ltd, All Rights Reserved. */ #include <linux/compat.h> #include <linux/errno.h> #include <linux/prctl.h> #include <linux/sched.h> #include <linux/sched/task_stack.h> #include <linux/thread_info.h> #include <asm/cpufeature.h> static void ssbd_ssbs_enable(struct task_struct *task) { u64 val = is_compat_thread(task_thread_info(task)) ? PSR_AA32_SSBS_BIT : PSR_SSBS_BIT; task_pt_regs(task)->pstate |= val; } static void ssbd_ssbs_disable(struct task_struct *task) { u64 val = is_compat_thread(task_thread_info(task)) ? PSR_AA32_SSBS_BIT : PSR_SSBS_BIT; task_pt_regs(task)->pstate &= ~val; } /* * prctl interface for SSBD */ 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 -ENODEV; /* 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); ssbd_ssbs_enable(task); 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); ssbd_ssbs_disable(task); 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); ssbd_ssbs_disable(task); 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 -ENODEV; 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; } } |