diff options
Diffstat (limited to 'arch/powerpc/kernel/dexcr.c')
| -rw-r--r-- | arch/powerpc/kernel/dexcr.c | 124 | 
1 files changed, 124 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/dexcr.c b/arch/powerpc/kernel/dexcr.c new file mode 100644 index 000000000000..3a0358e91c60 --- /dev/null +++ b/arch/powerpc/kernel/dexcr.c @@ -0,0 +1,124 @@ +// 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; +}  |