1// SPDX-License-Identifier: GPL-2.0-or-later
2
3#include <linux/capability.h>
4#include <linux/cpu.h>
5#include <linux/init.h>
6#include <linux/prctl.h>
7#include <linux/sched.h>
8
9#include <asm/cpu_has_feature.h>
10#include <asm/cputable.h>
11#include <asm/processor.h>
12#include <asm/reg.h>
13
14static int __init init_task_dexcr(void)
15{
16	if (!early_cpu_has_feature(CPU_FTR_ARCH_31))
17		return 0;
18
19	current->thread.dexcr_onexec = mfspr(SPRN_DEXCR);
20
21	return 0;
22}
23early_initcall(init_task_dexcr)
24
25/* Allow thread local configuration of these by default */
26#define DEXCR_PRCTL_EDITABLE ( \
27	DEXCR_PR_IBRTPD | \
28	DEXCR_PR_SRAPD | \
29	DEXCR_PR_NPHIE)
30
31static int prctl_to_aspect(unsigned long which, unsigned int *aspect)
32{
33	switch (which) {
34	case PR_PPC_DEXCR_SBHE:
35		*aspect = DEXCR_PR_SBHE;
36		break;
37	case PR_PPC_DEXCR_IBRTPD:
38		*aspect = DEXCR_PR_IBRTPD;
39		break;
40	case PR_PPC_DEXCR_SRAPD:
41		*aspect = DEXCR_PR_SRAPD;
42		break;
43	case PR_PPC_DEXCR_NPHIE:
44		*aspect = DEXCR_PR_NPHIE;
45		break;
46	default:
47		return -ENODEV;
48	}
49
50	return 0;
51}
52
53int get_dexcr_prctl(struct task_struct *task, unsigned long which)
54{
55	unsigned int aspect;
56	int ret;
57
58	ret = prctl_to_aspect(which, &aspect);
59	if (ret)
60		return ret;
61
62	if (aspect & DEXCR_PRCTL_EDITABLE)
63		ret |= PR_PPC_DEXCR_CTRL_EDITABLE;
64
65	if (aspect & mfspr(SPRN_DEXCR))
66		ret |= PR_PPC_DEXCR_CTRL_SET;
67	else
68		ret |= PR_PPC_DEXCR_CTRL_CLEAR;
69
70	if (aspect & task->thread.dexcr_onexec)
71		ret |= PR_PPC_DEXCR_CTRL_SET_ONEXEC;
72	else
73		ret |= PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC;
74
75	return ret;
76}
77
78int set_dexcr_prctl(struct task_struct *task, unsigned long which, unsigned long ctrl)
79{
80	unsigned long dexcr;
81	unsigned int aspect;
82	int err = 0;
83
84	err = prctl_to_aspect(which, &aspect);
85	if (err)
86		return err;
87
88	if (!(aspect & DEXCR_PRCTL_EDITABLE))
89		return -EPERM;
90
91	if (ctrl & ~PR_PPC_DEXCR_CTRL_MASK)
92		return -EINVAL;
93
94	if (ctrl & PR_PPC_DEXCR_CTRL_SET && ctrl & PR_PPC_DEXCR_CTRL_CLEAR)
95		return -EINVAL;
96
97	if (ctrl & PR_PPC_DEXCR_CTRL_SET_ONEXEC && ctrl & PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC)
98		return -EINVAL;
99
100	/*
101	 * We do not want an unprivileged process being able to disable
102	 * a setuid process's hash check instructions
103	 */
104	if (aspect == DEXCR_PR_NPHIE &&
105	    ctrl & PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC &&
106	    !capable(CAP_SYS_ADMIN))
107		return -EPERM;
108
109	dexcr = mfspr(SPRN_DEXCR);
110
111	if (ctrl & PR_PPC_DEXCR_CTRL_SET)
112		dexcr |= aspect;
113	else if (ctrl & PR_PPC_DEXCR_CTRL_CLEAR)
114		dexcr &= ~aspect;
115
116	if (ctrl & PR_PPC_DEXCR_CTRL_SET_ONEXEC)
117		task->thread.dexcr_onexec |= aspect;
118	else if (ctrl & PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC)
119		task->thread.dexcr_onexec &= ~aspect;
120
121	mtspr(SPRN_DEXCR, dexcr);
122
123	return 0;
124}
125