// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 ARM Ltd, All Rights Reserved. */ #include #include #include #include /* * 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 -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; } }