diff mbox series

[RFC,06/13] powerpc/dexcr: Add prctl implementation

Message ID 20221128024458.46121-7-bgray@linux.ibm.com (mailing list archive)
State Handled Elsewhere
Headers show
Series Add DEXCR support | expand

Commit Message

Benjamin Gray Nov. 28, 2022, 2:44 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 |  13 +++
 arch/powerpc/kernel/dexcr.c          | 133 ++++++++++++++++++++++++++-
 arch/powerpc/kernel/process.c        |   6 ++
 3 files changed, 151 insertions(+), 1 deletion(-)

Comments

Nicholas Piggin March 7, 2023, 5:12 a.m. UTC | #1
On Mon Nov 28, 2022 at 12:44 PM AEST, Benjamin Gray wrote:
> 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.

You said (offline) that you were looking at the PR_SPEC_* speculation
control APIs but that this was different enough that you needed a
different one.

It would be good to know what some of those issues were in the
changelog, would be nice to have some docs (could we add something
to spec_ctrl.rst maybe?). I assume at least one difference is that
some of our bits are not speculative but architectural (e.g., the
stack hash check).

I also wonder if we could implement some of the PR_SPEC controls
APIs by mapping relevant DEXCR aspects to them instead of (or as well
as) the DEXCR controls? Or would the PR_SPEC users be amenable to
extensions that make our usage fit a bit better?

I'm just thinking if we can reduce reliance on arch specific APIs a
bit would be nice.

>
> Signed-off-by: Benjamin Gray <bgray@linux.ibm.com>
> ---
>  arch/powerpc/include/asm/processor.h |  13 +++
>  arch/powerpc/kernel/dexcr.c          | 133 ++++++++++++++++++++++++++-
>  arch/powerpc/kernel/process.c        |   6 ++
>  3 files changed, 151 insertions(+), 1 deletion(-)
>
> diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
> index 2381217c95dc..4c995258f668 100644
> --- a/arch/powerpc/include/asm/processor.h
> +++ b/arch/powerpc/include/asm/processor.h
> @@ -265,6 +265,9 @@ struct thread_struct {
>  	unsigned long   sier2;
>  	unsigned long   sier3;
>  	unsigned long	hashkeyr;
> +	unsigned int	dexcr_override;
> +	unsigned int	dexcr_mask;

Hmm, what's the mask doing here? It only gets bits set and never
cleared AFAIKS. What is different between an initial state and a
SET then CLEAR state?

Thanks,
Nick
diff mbox series

Patch

diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index 2381217c95dc..4c995258f668 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -265,6 +265,9 @@  struct thread_struct {
 	unsigned long   sier2;
 	unsigned long   sier3;
 	unsigned long	hashkeyr;
+	unsigned int	dexcr_override;
+	unsigned int	dexcr_mask;
+	unsigned int	dexcr_forced;
 
 #endif
 };
@@ -338,6 +341,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) dexcr_prctl_get((tsk), (asp))
+#define PPC_SET_DEXCR_ASPECT(tsk, asp, val) dexcr_prctl_set((tsk), (asp), (val))
+
+int dexcr_prctl_get(struct task_struct *tsk, unsigned long asp);
+int dexcr_prctl_set(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/dexcr.c b/arch/powerpc/kernel/dexcr.c
index 11515e67afac..9290beed722a 100644
--- a/arch/powerpc/kernel/dexcr.c
+++ b/arch/powerpc/kernel/dexcr.c
@@ -1,5 +1,8 @@ 
 #include <linux/cache.h>
+#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>
@@ -11,6 +14,10 @@ 
 
 #define DEFAULT_DEXCR	0
 
+/* Allow process configuration of these by default */
+#define DEXCR_PRCTL_EDITABLE (DEXCR_PRO_SBHE | DEXCR_PRO_IBRTPD | \
+			      DEXCR_PRO_SRAPD | DEXCR_PRO_NPHIE)
+
 static int __init dexcr_init(void)
 {
 	if (!early_cpu_has_feature(CPU_FTR_ARCH_31))
@@ -43,5 +50,129 @@  bool is_hashchk_trap(struct pt_regs const *regs)
 
 unsigned long get_thread_dexcr(struct thread_struct const *t)
 {
-	return DEFAULT_DEXCR;
+	unsigned long dexcr = DEFAULT_DEXCR;
+
+	/* Apply prctl overrides */
+	dexcr = (dexcr & ~t->dexcr_mask) | t->dexcr_override;
+
+	return dexcr;
+}
+
+static void update_dexcr_on_cpu(void *info)
+{
+	mtspr(SPRN_DEXCR, get_thread_dexcr(&current->thread));
+}
+
+static int dexcr_aspect_get(struct task_struct *task, unsigned int aspect)
+{
+	int ret = 0;
+
+	if (aspect & DEXCR_PRCTL_EDITABLE)
+		ret |= PR_PPC_DEXCR_PRCTL;
+
+	if (aspect & task->thread.dexcr_mask) {
+		if (aspect & task->thread.dexcr_override) {
+			if (aspect & task->thread.dexcr_forced)
+				ret |= PR_PPC_DEXCR_FORCE_SET_ASPECT;
+			else
+				ret |= PR_PPC_DEXCR_SET_ASPECT;
+		} else {
+			ret |= PR_PPC_DEXCR_CLEAR_ASPECT;
+		}
+	}
+
+	return ret;
+}
+
+int dexcr_prctl_get(struct task_struct *task, unsigned long which)
+{
+	switch (which) {
+	case PR_PPC_DEXCR_SBHE:
+		if (!cpu_has_feature(CPU_FTR_DEXCR_SBHE))
+			return -ENODEV;
+		return dexcr_aspect_get(task, DEXCR_PRO_SBHE);
+	case PR_PPC_DEXCR_IBRTPD:
+		if (!cpu_has_feature(CPU_FTR_DEXCR_IBRTPD))
+			return -ENODEV;
+		return dexcr_aspect_get(task, DEXCR_PRO_IBRTPD);
+	case PR_PPC_DEXCR_SRAPD:
+		if (!cpu_has_feature(CPU_FTR_DEXCR_SRAPD))
+			return -ENODEV;
+		return dexcr_aspect_get(task, DEXCR_PRO_SRAPD);
+	case PR_PPC_DEXCR_NPHIE:
+		if (!cpu_has_feature(CPU_FTR_DEXCR_NPHIE))
+			return -ENODEV;
+		return dexcr_aspect_get(task, DEXCR_PRO_NPHIE);
+	default:
+		return -ENODEV;
+	}
+}
+
+static int dexcr_aspect_set(struct task_struct *task, unsigned int aspect, unsigned long ctrl)
+{
+	if (!(aspect & DEXCR_PRCTL_EDITABLE))
+		return -ENXIO;  /* Aspect is not allowed to be changed by prctl */
+
+	if (aspect & task->thread.dexcr_forced)
+		return -EPERM;  /* Aspect has been forced to current state */
+
+	switch (ctrl) {
+	case PR_PPC_DEXCR_SET_ASPECT:
+		task->thread.dexcr_mask |= aspect;
+		task->thread.dexcr_override |= aspect;
+		break;
+	case PR_PPC_DEXCR_FORCE_SET_ASPECT:
+		task->thread.dexcr_mask |= aspect;
+		task->thread.dexcr_override |= aspect;
+		task->thread.dexcr_forced |= aspect;
+		break;
+	case PR_PPC_DEXCR_CLEAR_ASPECT:
+		task->thread.dexcr_mask |= aspect;
+		task->thread.dexcr_override &= ~aspect;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	return 0;
+}
+
+int dexcr_prctl_set(struct task_struct *task, unsigned long which, unsigned long ctrl)
+{
+	int err = 0;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	switch (which) {
+	case PR_PPC_DEXCR_SBHE:
+		if (!cpu_has_feature(CPU_FTR_DEXCR_SBHE))
+			return -ENODEV;
+		err = dexcr_aspect_set(task, DEXCR_PRO_SBHE, ctrl);
+		break;
+	case PR_PPC_DEXCR_IBRTPD:
+		if (!cpu_has_feature(CPU_FTR_DEXCR_IBRTPD))
+			return -ENODEV;
+		err = dexcr_aspect_set(task, DEXCR_PRO_IBRTPD, ctrl);
+		break;
+	case PR_PPC_DEXCR_SRAPD:
+		if (!cpu_has_feature(CPU_FTR_DEXCR_SRAPD))
+			return -ENODEV;
+		err = dexcr_aspect_set(task, DEXCR_PRO_SRAPD, ctrl);
+		break;
+	case PR_PPC_DEXCR_NPHIE:
+		if (!cpu_has_feature(CPU_FTR_DEXCR_NPHIE))
+			return -ENODEV;
+		err = dexcr_aspect_set(task, DEXCR_PRO_NPHIE, ctrl);
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	if (err)
+		return err;
+
+	update_dexcr_on_cpu(NULL);
+
+	return 0;
 }
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 4d7b0c7641d0..a280842750f9 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -1825,6 +1825,12 @@  int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
 #ifdef CONFIG_PPC_BOOK3S_64
 	if (cpu_has_feature(CPU_FTR_DEXCR_NPHIE))
 		p->thread.hashkeyr = current->thread.hashkeyr;
+
+	if (cpu_has_feature(CPU_FTR_ARCH_31)) {
+		p->thread.dexcr_override = current->thread.dexcr_override;
+		p->thread.dexcr_mask = current->thread.dexcr_mask;
+		p->thread.dexcr_forced = current->thread.dexcr_forced;
+	}
 #endif
 	/*
 	 * Run with the current AMR value of the kernel