diff mbox series

[RFC,4/6] powerpc/dexcr: Add prctl implementation

Message ID 20231009055406.142940-5-bgray@linux.ibm.com (mailing list archive)
State RFC
Headers show
Series Add dynamic DEXCR support | expand

Commit Message

Benjamin Gray Oct. 9, 2023, 5:54 a.m. UTC
Adds an initial prctl interface implementation. Unprivileged processes
can query the current prctl setting, including whether an aspect is
implemented by the hardware or is permitted to be modified by a setter
prctl. Editable aspects can be changed by a CAP_SYS_ADMIN privileged
process.

The prctl setting represents what the process itself has requested, and
does not account for any overrides. Either the kernel or a hypervisor
may enforce a different setting for an aspect.

Userspace can access a readonly view of the current DEXCR via SPR 812,
and a readonly view of the aspects enforced by the hypervisor via
SPR 455. A bitwise OR of these two SPRs will give the effective
DEXCR aspect state of the process.

Signed-off-by: Benjamin Gray <bgray@linux.ibm.com>
---
 arch/powerpc/include/asm/processor.h |  10 +++
 arch/powerpc/kernel/Makefile         |   1 +
 arch/powerpc/kernel/dexcr.c          | 128 +++++++++++++++++++++++++++
 3 files changed, 139 insertions(+)
 create mode 100644 arch/powerpc/kernel/dexcr.c
diff mbox series

Patch

diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index 28a72023f9bd..a9d83621dfad 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -336,6 +336,16 @@  extern int set_endian(struct task_struct *tsk, unsigned int val);
 extern int get_unalign_ctl(struct task_struct *tsk, unsigned long adr);
 extern int set_unalign_ctl(struct task_struct *tsk, unsigned int val);
 
+#ifdef CONFIG_PPC_BOOK3S_64
+
+#define PPC_GET_DEXCR_ASPECT(tsk, asp) get_dexcr_prctl((tsk), (asp))
+#define PPC_SET_DEXCR_ASPECT(tsk, asp, val) set_dexcr_prctl((tsk), (asp), (val))
+
+int get_dexcr_prctl(struct task_struct *tsk, unsigned long asp);
+int set_dexcr_prctl(struct task_struct *tsk, unsigned long asp, unsigned long val);
+
+#endif
+
 extern void load_fp_state(struct thread_fp_state *fp);
 extern void store_fp_state(struct thread_fp_state *fp);
 extern void load_vr_state(struct thread_vr_state *vr);
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 2919433be355..24f82b09246c 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -88,6 +88,7 @@  obj-$(CONFIG_HAVE_HW_BREAKPOINT)	+= hw_breakpoint.o
 obj-$(CONFIG_PPC_DAWR)		+= dawr.o
 obj-$(CONFIG_PPC_BOOK3S_64)	+= cpu_setup_ppc970.o cpu_setup_pa6t.o
 obj-$(CONFIG_PPC_BOOK3S_64)	+= cpu_setup_power.o
+obj-$(CONFIG_PPC_BOOK3S_64)	+= dexcr.o
 obj-$(CONFIG_PPC_BOOK3S_64)	+= mce.o mce_power.o
 obj-$(CONFIG_PPC_BOOK3E_64)	+= exceptions-64e.o idle_64e.o
 obj-$(CONFIG_PPC_BARRIER_NOSPEC) += security.o
diff --git a/arch/powerpc/kernel/dexcr.c b/arch/powerpc/kernel/dexcr.c
new file mode 100644
index 000000000000..db663ce7b3ce
--- /dev/null
+++ b/arch/powerpc/kernel/dexcr.c
@@ -0,0 +1,128 @@ 
+#include <linux/capability.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>
+
+/* Allow thread local configuration of these by default */
+#define DEXCR_PRCTL_EDITABLE ( \
+	DEXCR_PR_IBRTPD | \
+	DEXCR_PR_SRAPD | \
+	DEXCR_PR_NPHIE)
+
+static unsigned long dexcr_supported __ro_after_init = 0;
+
+static int __init dexcr_init(void)
+{
+	if (!early_cpu_has_feature(CPU_FTR_ARCH_31))
+		return 0;
+
+	if (early_cpu_has_feature(CPU_FTR_DEXCR_SBHE))
+		dexcr_supported |= DEXCR_PR_SBHE;
+
+	if (early_cpu_has_feature(CPU_FTR_DEXCR_IBRTPD))
+		dexcr_supported |= DEXCR_PR_IBRTPD;
+
+	if (early_cpu_has_feature(CPU_FTR_DEXCR_SRAPD))
+		dexcr_supported |= DEXCR_PR_SRAPD;
+
+	if (early_cpu_has_feature(CPU_FTR_DEXCR_NPHIE))
+		dexcr_supported |= DEXCR_PR_NPHIE;
+
+	return 0;
+}
+early_initcall(dexcr_init);
+
+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_supported))
+		return -ENODEV;
+
+	if (aspect & task->thread.dexcr_enabled)
+		ret |= PR_PPC_DEXCR_CTRL_ON;
+	else
+		ret |= PR_PPC_DEXCR_CTRL_OFF;
+
+	if (aspect & task->thread.dexcr_inherit)
+		ret |= PR_PPC_DEXCR_CTRL_INHERIT;
+
+	return ret;
+}
+
+int set_dexcr_prctl(struct task_struct *task, unsigned long which, unsigned long ctrl)
+{
+	unsigned int aspect;
+	unsigned long enable;
+	unsigned long disable;
+	unsigned long inherit;
+	int err = 0;
+
+	/* We do not want an unprivileged process being able to set a value that a setuid process may inherit (particularly for NPHIE) */
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	err = prctl_to_aspect(which, &aspect);
+	if (err)
+		return err;
+
+	if (!(aspect & dexcr_supported))
+		return -ENODEV;
+
+	enable = ctrl & PR_PPC_DEXCR_CTRL_ON;
+	disable = ctrl & PR_PPC_DEXCR_CTRL_OFF;
+	inherit = ctrl & PR_PPC_DEXCR_CTRL_INHERIT;
+	ctrl &= ~(PR_PPC_DEXCR_CTRL_ON | PR_PPC_DEXCR_CTRL_OFF | PR_PPC_DEXCR_CTRL_INHERIT);
+
+	if (ctrl)
+		return -EINVAL;
+
+	if ((enable && disable) || !(enable || disable))
+		return -EINVAL;
+
+	if (enable)
+		task->thread.dexcr_enabled |= aspect;
+	else
+		task->thread.dexcr_enabled &= ~aspect;
+
+	if (inherit)
+		task->thread.dexcr_inherit |= aspect;
+	else
+		task->thread.dexcr_inherit &= ~aspect;
+
+	mtspr(SPRN_DEXCR, get_thread_dexcr(&current->thread));
+
+	return 0;
+}