riscv: Add prctl controls for userspace vector management
This patch add two riscv-specific prctls, to allow usespace control the use of vector unit: * PR_RISCV_V_SET_CONTROL: control the permission to use Vector at next, or all following execve for a thread. Turning off a thread's Vector live is not possible since libraries may have registered ifunc that may execute Vector instructions. * PR_RISCV_V_GET_CONTROL: get the same permission setting for the current thread, and the setting for following execve(s). Signed-off-by: Andy Chiu <andy.chiu@sifive.com> Reviewed-by: Greentime Hu <greentime.hu@sifive.com> Reviewed-by: Vincent Chen <vincent.chen@sifive.com> Link: https://lore.kernel.org/r/20230605110724.21391-22-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
This commit is contained in:
parent
50724efcb3
commit
1fd96a3e9d
@ -40,6 +40,7 @@ struct thread_struct {
|
|||||||
unsigned long s[12]; /* s[0]: frame pointer */
|
unsigned long s[12]; /* s[0]: frame pointer */
|
||||||
struct __riscv_d_ext_state fstate;
|
struct __riscv_d_ext_state fstate;
|
||||||
unsigned long bad_cause;
|
unsigned long bad_cause;
|
||||||
|
unsigned long vstate_ctrl;
|
||||||
struct __riscv_v_ext_state vstate;
|
struct __riscv_v_ext_state vstate;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,6 +84,15 @@ extern void riscv_fill_hwcap(void);
|
|||||||
extern int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src);
|
extern int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src);
|
||||||
|
|
||||||
extern unsigned long signal_minsigstksz __ro_after_init;
|
extern unsigned long signal_minsigstksz __ro_after_init;
|
||||||
|
|
||||||
|
#ifdef CONFIG_RISCV_ISA_V
|
||||||
|
/* Userspace interface for PR_RISCV_V_{SET,GET}_VS prctl()s: */
|
||||||
|
#define RISCV_V_SET_CONTROL(arg) riscv_v_vstate_ctrl_set_current(arg)
|
||||||
|
#define RISCV_V_GET_CONTROL() riscv_v_vstate_ctrl_get_current()
|
||||||
|
extern long riscv_v_vstate_ctrl_set_current(unsigned long arg);
|
||||||
|
extern long riscv_v_vstate_ctrl_get_current(void);
|
||||||
|
#endif /* CONFIG_RISCV_ISA_V */
|
||||||
|
|
||||||
#endif /* __ASSEMBLY__ */
|
#endif /* __ASSEMBLY__ */
|
||||||
|
|
||||||
#endif /* _ASM_RISCV_PROCESSOR_H */
|
#endif /* _ASM_RISCV_PROCESSOR_H */
|
||||||
|
@ -160,6 +160,9 @@ static inline void __switch_to_vector(struct task_struct *prev,
|
|||||||
riscv_v_vstate_restore(next, task_pt_regs(next));
|
riscv_v_vstate_restore(next, task_pt_regs(next));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void riscv_v_vstate_ctrl_init(struct task_struct *tsk);
|
||||||
|
bool riscv_v_vstate_ctrl_user_allowed(void);
|
||||||
|
|
||||||
#else /* ! CONFIG_RISCV_ISA_V */
|
#else /* ! CONFIG_RISCV_ISA_V */
|
||||||
|
|
||||||
struct pt_regs;
|
struct pt_regs;
|
||||||
@ -168,6 +171,7 @@ static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; }
|
|||||||
static __always_inline bool has_vector(void) { return false; }
|
static __always_inline bool has_vector(void) { return false; }
|
||||||
static inline bool riscv_v_first_use_handler(struct pt_regs *regs) { return false; }
|
static inline bool riscv_v_first_use_handler(struct pt_regs *regs) { return false; }
|
||||||
static inline bool riscv_v_vstate_query(struct pt_regs *regs) { return false; }
|
static inline bool riscv_v_vstate_query(struct pt_regs *regs) { return false; }
|
||||||
|
static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; }
|
||||||
#define riscv_v_vsize (0)
|
#define riscv_v_vsize (0)
|
||||||
#define riscv_v_vstate_save(task, regs) do {} while (0)
|
#define riscv_v_vstate_save(task, regs) do {} while (0)
|
||||||
#define riscv_v_vstate_restore(task, regs) do {} while (0)
|
#define riscv_v_vstate_restore(task, regs) do {} while (0)
|
||||||
|
@ -295,7 +295,14 @@ void __init riscv_fill_hwcap(void)
|
|||||||
|
|
||||||
unsigned long riscv_get_elf_hwcap(void)
|
unsigned long riscv_get_elf_hwcap(void)
|
||||||
{
|
{
|
||||||
return (elf_hwcap & ((1UL << RISCV_ISA_EXT_BASE) - 1));
|
unsigned long hwcap;
|
||||||
|
|
||||||
|
hwcap = (elf_hwcap & ((1UL << RISCV_ISA_EXT_BASE) - 1));
|
||||||
|
|
||||||
|
if (!riscv_v_vstate_ctrl_user_allowed())
|
||||||
|
hwcap &= ~COMPAT_HWCAP_ISA_V;
|
||||||
|
|
||||||
|
return hwcap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_RISCV_ALTERNATIVE
|
#ifdef CONFIG_RISCV_ALTERNATIVE
|
||||||
|
@ -149,6 +149,7 @@ void flush_thread(void)
|
|||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_RISCV_ISA_V
|
#ifdef CONFIG_RISCV_ISA_V
|
||||||
/* Reset vector state */
|
/* Reset vector state */
|
||||||
|
riscv_v_vstate_ctrl_init(current);
|
||||||
riscv_v_vstate_off(task_pt_regs(current));
|
riscv_v_vstate_off(task_pt_regs(current));
|
||||||
kfree(current->thread.vstate.datap);
|
kfree(current->thread.vstate.datap);
|
||||||
memset(¤t->thread.vstate, 0, sizeof(struct __riscv_v_ext_state));
|
memset(¤t->thread.vstate, 0, sizeof(struct __riscv_v_ext_state));
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/prctl.h>
|
||||||
|
|
||||||
#include <asm/thread_info.h>
|
#include <asm/thread_info.h>
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
@ -19,6 +20,8 @@
|
|||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
#include <asm/bug.h>
|
#include <asm/bug.h>
|
||||||
|
|
||||||
|
static bool riscv_v_implicit_uacc = IS_ENABLED(CONFIG_RISCV_ISA_V_DEFAULT_ENABLE);
|
||||||
|
|
||||||
unsigned long riscv_v_vsize __read_mostly;
|
unsigned long riscv_v_vsize __read_mostly;
|
||||||
EXPORT_SYMBOL_GPL(riscv_v_vsize);
|
EXPORT_SYMBOL_GPL(riscv_v_vsize);
|
||||||
|
|
||||||
@ -91,6 +94,43 @@ static int riscv_v_thread_zalloc(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define VSTATE_CTRL_GET_CUR(x) ((x) & PR_RISCV_V_VSTATE_CTRL_CUR_MASK)
|
||||||
|
#define VSTATE_CTRL_GET_NEXT(x) (((x) & PR_RISCV_V_VSTATE_CTRL_NEXT_MASK) >> 2)
|
||||||
|
#define VSTATE_CTRL_MAKE_NEXT(x) (((x) << 2) & PR_RISCV_V_VSTATE_CTRL_NEXT_MASK)
|
||||||
|
#define VSTATE_CTRL_GET_INHERIT(x) (!!((x) & PR_RISCV_V_VSTATE_CTRL_INHERIT))
|
||||||
|
static inline int riscv_v_ctrl_get_cur(struct task_struct *tsk)
|
||||||
|
{
|
||||||
|
return VSTATE_CTRL_GET_CUR(tsk->thread.vstate_ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int riscv_v_ctrl_get_next(struct task_struct *tsk)
|
||||||
|
{
|
||||||
|
return VSTATE_CTRL_GET_NEXT(tsk->thread.vstate_ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool riscv_v_ctrl_test_inherit(struct task_struct *tsk)
|
||||||
|
{
|
||||||
|
return VSTATE_CTRL_GET_INHERIT(tsk->thread.vstate_ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void riscv_v_ctrl_set(struct task_struct *tsk, int cur, int nxt,
|
||||||
|
bool inherit)
|
||||||
|
{
|
||||||
|
unsigned long ctrl;
|
||||||
|
|
||||||
|
ctrl = cur & PR_RISCV_V_VSTATE_CTRL_CUR_MASK;
|
||||||
|
ctrl |= VSTATE_CTRL_MAKE_NEXT(nxt);
|
||||||
|
if (inherit)
|
||||||
|
ctrl |= PR_RISCV_V_VSTATE_CTRL_INHERIT;
|
||||||
|
tsk->thread.vstate_ctrl = ctrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool riscv_v_vstate_ctrl_user_allowed(void)
|
||||||
|
{
|
||||||
|
return riscv_v_ctrl_get_cur(current) == PR_RISCV_V_VSTATE_CTRL_ON;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(riscv_v_vstate_ctrl_user_allowed);
|
||||||
|
|
||||||
bool riscv_v_first_use_handler(struct pt_regs *regs)
|
bool riscv_v_first_use_handler(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
u32 __user *epc = (u32 __user *)regs->epc;
|
u32 __user *epc = (u32 __user *)regs->epc;
|
||||||
@ -129,3 +169,77 @@ bool riscv_v_first_use_handler(struct pt_regs *regs)
|
|||||||
riscv_v_vstate_on(regs);
|
riscv_v_vstate_on(regs);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void riscv_v_vstate_ctrl_init(struct task_struct *tsk)
|
||||||
|
{
|
||||||
|
bool inherit;
|
||||||
|
int cur, next;
|
||||||
|
|
||||||
|
if (!has_vector())
|
||||||
|
return;
|
||||||
|
|
||||||
|
next = riscv_v_ctrl_get_next(tsk);
|
||||||
|
if (!next) {
|
||||||
|
if (riscv_v_implicit_uacc)
|
||||||
|
cur = PR_RISCV_V_VSTATE_CTRL_ON;
|
||||||
|
else
|
||||||
|
cur = PR_RISCV_V_VSTATE_CTRL_OFF;
|
||||||
|
} else {
|
||||||
|
cur = next;
|
||||||
|
}
|
||||||
|
/* Clear next mask if inherit-bit is not set */
|
||||||
|
inherit = riscv_v_ctrl_test_inherit(tsk);
|
||||||
|
if (!inherit)
|
||||||
|
next = PR_RISCV_V_VSTATE_CTRL_DEFAULT;
|
||||||
|
|
||||||
|
riscv_v_ctrl_set(tsk, cur, next, inherit);
|
||||||
|
}
|
||||||
|
|
||||||
|
long riscv_v_vstate_ctrl_get_current(void)
|
||||||
|
{
|
||||||
|
if (!has_vector())
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return current->thread.vstate_ctrl & PR_RISCV_V_VSTATE_CTRL_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
long riscv_v_vstate_ctrl_set_current(unsigned long arg)
|
||||||
|
{
|
||||||
|
bool inherit;
|
||||||
|
int cur, next;
|
||||||
|
|
||||||
|
if (!has_vector())
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (arg & ~PR_RISCV_V_VSTATE_CTRL_MASK)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
cur = VSTATE_CTRL_GET_CUR(arg);
|
||||||
|
switch (cur) {
|
||||||
|
case PR_RISCV_V_VSTATE_CTRL_OFF:
|
||||||
|
/* Do not allow user to turn off V if current is not off */
|
||||||
|
if (riscv_v_ctrl_get_cur(current) != PR_RISCV_V_VSTATE_CTRL_OFF)
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case PR_RISCV_V_VSTATE_CTRL_ON:
|
||||||
|
break;
|
||||||
|
case PR_RISCV_V_VSTATE_CTRL_DEFAULT:
|
||||||
|
cur = riscv_v_ctrl_get_cur(current);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
next = VSTATE_CTRL_GET_NEXT(arg);
|
||||||
|
inherit = VSTATE_CTRL_GET_INHERIT(arg);
|
||||||
|
switch (next) {
|
||||||
|
case PR_RISCV_V_VSTATE_CTRL_DEFAULT:
|
||||||
|
case PR_RISCV_V_VSTATE_CTRL_OFF:
|
||||||
|
case PR_RISCV_V_VSTATE_CTRL_ON:
|
||||||
|
riscv_v_ctrl_set(current, cur, next, inherit);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
@ -88,6 +88,8 @@ static bool kvm_riscv_vcpu_isa_enable_allowed(unsigned long ext)
|
|||||||
switch (ext) {
|
switch (ext) {
|
||||||
case KVM_RISCV_ISA_EXT_H:
|
case KVM_RISCV_ISA_EXT_H:
|
||||||
return false;
|
return false;
|
||||||
|
case KVM_RISCV_ISA_EXT_V:
|
||||||
|
return riscv_v_vstate_ctrl_user_allowed();
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -294,4 +294,15 @@ struct prctl_mm_map {
|
|||||||
|
|
||||||
#define PR_SET_MEMORY_MERGE 67
|
#define PR_SET_MEMORY_MERGE 67
|
||||||
#define PR_GET_MEMORY_MERGE 68
|
#define PR_GET_MEMORY_MERGE 68
|
||||||
|
|
||||||
|
#define PR_RISCV_V_SET_CONTROL 69
|
||||||
|
#define PR_RISCV_V_GET_CONTROL 70
|
||||||
|
# define PR_RISCV_V_VSTATE_CTRL_DEFAULT 0
|
||||||
|
# define PR_RISCV_V_VSTATE_CTRL_OFF 1
|
||||||
|
# define PR_RISCV_V_VSTATE_CTRL_ON 2
|
||||||
|
# define PR_RISCV_V_VSTATE_CTRL_INHERIT (1 << 4)
|
||||||
|
# define PR_RISCV_V_VSTATE_CTRL_CUR_MASK 0x3
|
||||||
|
# define PR_RISCV_V_VSTATE_CTRL_NEXT_MASK 0xc
|
||||||
|
# define PR_RISCV_V_VSTATE_CTRL_MASK 0x1f
|
||||||
|
|
||||||
#endif /* _LINUX_PRCTL_H */
|
#endif /* _LINUX_PRCTL_H */
|
||||||
|
12
kernel/sys.c
12
kernel/sys.c
@ -140,6 +140,12 @@
|
|||||||
#ifndef GET_TAGGED_ADDR_CTRL
|
#ifndef GET_TAGGED_ADDR_CTRL
|
||||||
# define GET_TAGGED_ADDR_CTRL() (-EINVAL)
|
# define GET_TAGGED_ADDR_CTRL() (-EINVAL)
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef RISCV_V_SET_CONTROL
|
||||||
|
# define RISCV_V_SET_CONTROL(a) (-EINVAL)
|
||||||
|
#endif
|
||||||
|
#ifndef RISCV_V_GET_CONTROL
|
||||||
|
# define RISCV_V_GET_CONTROL() (-EINVAL)
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* this is where the system-wide overflow UID and GID are defined, for
|
* this is where the system-wide overflow UID and GID are defined, for
|
||||||
@ -2708,6 +2714,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
|||||||
error = !!test_bit(MMF_VM_MERGE_ANY, &me->mm->flags);
|
error = !!test_bit(MMF_VM_MERGE_ANY, &me->mm->flags);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case PR_RISCV_V_SET_CONTROL:
|
||||||
|
error = RISCV_V_SET_CONTROL(arg2);
|
||||||
|
break;
|
||||||
|
case PR_RISCV_V_GET_CONTROL:
|
||||||
|
error = RISCV_V_GET_CONTROL();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
error = -EINVAL;
|
error = -EINVAL;
|
||||||
break;
|
break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user