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 <bg...@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 --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(¤t->thread)); + + return 0; +} -- 2.41.0