| // SPDX-License-Identifier: GPL-2.0-or-later |
| |
| #include <linux/capability.h> |
| #include <linux/cpu.h> |
| #include <linux/init.h> |
| #include <linux/prctl.h> |
| #include <linux/sched.h> |
| |
| #include <asm/cpu_has_feature.h> |
| #include <asm/cputable.h> |
| #include <asm/processor.h> |
| #include <asm/reg.h> |
| |
| static int __init init_task_dexcr(void) |
| { |
| if (!early_cpu_has_feature(CPU_FTR_ARCH_31)) |
| return 0; |
| |
| current->thread.dexcr_onexec = mfspr(SPRN_DEXCR); |
| |
| return 0; |
| } |
| early_initcall(init_task_dexcr) |
| |
| /* Allow thread local configuration of these by default */ |
| #define DEXCR_PRCTL_EDITABLE ( \ |
| DEXCR_PR_IBRTPD | \ |
| DEXCR_PR_SRAPD | \ |
| DEXCR_PR_NPHIE) |
| |
| static int prctl_to_aspect(unsigned long which, unsigned int *aspect) |
| { |
| switch (which) { |
| case PR_PPC_DEXCR_SBHE: |
| *aspect = DEXCR_PR_SBHE; |
| break; |
| case PR_PPC_DEXCR_IBRTPD: |
| *aspect = DEXCR_PR_IBRTPD; |
| break; |
| case PR_PPC_DEXCR_SRAPD: |
| *aspect = DEXCR_PR_SRAPD; |
| break; |
| case PR_PPC_DEXCR_NPHIE: |
| *aspect = DEXCR_PR_NPHIE; |
| break; |
| default: |
| return -ENODEV; |
| } |
| |
| return 0; |
| } |
| |
| int get_dexcr_prctl(struct task_struct *task, unsigned long which) |
| { |
| unsigned int aspect; |
| int ret; |
| |
| ret = prctl_to_aspect(which, &aspect); |
| if (ret) |
| return ret; |
| |
| if (aspect & DEXCR_PRCTL_EDITABLE) |
| ret |= PR_PPC_DEXCR_CTRL_EDITABLE; |
| |
| if (aspect & mfspr(SPRN_DEXCR)) |
| ret |= PR_PPC_DEXCR_CTRL_SET; |
| else |
| ret |= PR_PPC_DEXCR_CTRL_CLEAR; |
| |
| if (aspect & task->thread.dexcr_onexec) |
| ret |= PR_PPC_DEXCR_CTRL_SET_ONEXEC; |
| else |
| ret |= PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC; |
| |
| return ret; |
| } |
| |
| int set_dexcr_prctl(struct task_struct *task, unsigned long which, unsigned long ctrl) |
| { |
| unsigned long dexcr; |
| unsigned int aspect; |
| int err = 0; |
| |
| err = prctl_to_aspect(which, &aspect); |
| if (err) |
| return err; |
| |
| if (!(aspect & DEXCR_PRCTL_EDITABLE)) |
| return -EPERM; |
| |
| if (ctrl & ~PR_PPC_DEXCR_CTRL_MASK) |
| return -EINVAL; |
| |
| if (ctrl & PR_PPC_DEXCR_CTRL_SET && ctrl & PR_PPC_DEXCR_CTRL_CLEAR) |
| return -EINVAL; |
| |
| if (ctrl & PR_PPC_DEXCR_CTRL_SET_ONEXEC && ctrl & PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC) |
| return -EINVAL; |
| |
| /* |
| * We do not want an unprivileged process being able to disable |
| * a setuid process's hash check instructions |
| */ |
| if (aspect == DEXCR_PR_NPHIE && |
| ctrl & PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC && |
| !capable(CAP_SYS_ADMIN)) |
| return -EPERM; |
| |
| dexcr = mfspr(SPRN_DEXCR); |
| |
| if (ctrl & PR_PPC_DEXCR_CTRL_SET) |
| dexcr |= aspect; |
| else if (ctrl & PR_PPC_DEXCR_CTRL_CLEAR) |
| dexcr &= ~aspect; |
| |
| if (ctrl & PR_PPC_DEXCR_CTRL_SET_ONEXEC) |
| task->thread.dexcr_onexec |= aspect; |
| else if (ctrl & PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC) |
| task->thread.dexcr_onexec &= ~aspect; |
| |
| mtspr(SPRN_DEXCR, dexcr); |
| |
| return 0; |
| } |