mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:44:45 +01:00
arm64: uaccess: Add additional userspace GCS accessors
Uprobes need more advanced read, push, and pop userspace GCS functionality. Implement those features using the existing gcsstr() and copy_from_user(). Its important to note that GCS pages can be read by normal instructions, but the hardware validates that pages used by GCS specific operations, have a GCS privilege set. We aren't validating this in load_user_gcs because it requires stabilizing the VMA over the read which may fault. Signed-off-by: Jeremy Linton <jeremy.linton@arm.com> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Reviewed-by: Mark Brown <broonie@kernel.org> [will: Add '__force' to gcspr cast in pop_user_gcs()] Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
parent
ea920b50ac
commit
9cd2a7f118
1 changed files with 54 additions and 0 deletions
|
|
@ -116,6 +116,47 @@ static inline void put_user_gcs(unsigned long val, unsigned long __user *addr,
|
|||
uaccess_ttbr0_disable();
|
||||
}
|
||||
|
||||
static inline void push_user_gcs(unsigned long val, int *err)
|
||||
{
|
||||
u64 gcspr = read_sysreg_s(SYS_GCSPR_EL0);
|
||||
|
||||
gcspr -= sizeof(u64);
|
||||
put_user_gcs(val, (unsigned long __user *)gcspr, err);
|
||||
if (!*err)
|
||||
write_sysreg_s(gcspr, SYS_GCSPR_EL0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlike put/push_user_gcs() above, get/pop_user_gsc() doesn't
|
||||
* validate the GCS permission is set on the page being read. This
|
||||
* differs from how the hardware works when it consumes data stored at
|
||||
* GCSPR. Callers should ensure this is acceptable.
|
||||
*/
|
||||
static inline u64 get_user_gcs(unsigned long __user *addr, int *err)
|
||||
{
|
||||
unsigned long ret;
|
||||
u64 load = 0;
|
||||
|
||||
/* Ensure previous GCS operation are visible before we read the page */
|
||||
gcsb_dsync();
|
||||
ret = copy_from_user(&load, addr, sizeof(load));
|
||||
if (ret != 0)
|
||||
*err = ret;
|
||||
return load;
|
||||
}
|
||||
|
||||
static inline u64 pop_user_gcs(int *err)
|
||||
{
|
||||
u64 gcspr = read_sysreg_s(SYS_GCSPR_EL0);
|
||||
u64 read_val;
|
||||
|
||||
read_val = get_user_gcs((__force unsigned long __user *)gcspr, err);
|
||||
if (!*err)
|
||||
write_sysreg_s(gcspr + sizeof(u64), SYS_GCSPR_EL0);
|
||||
|
||||
return read_val;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline bool task_gcs_el0_enabled(struct task_struct *task)
|
||||
|
|
@ -126,6 +167,10 @@ static inline bool task_gcs_el0_enabled(struct task_struct *task)
|
|||
static inline void gcs_set_el0_mode(struct task_struct *task) { }
|
||||
static inline void gcs_free(struct task_struct *task) { }
|
||||
static inline void gcs_preserve_current_state(void) { }
|
||||
static inline void put_user_gcs(unsigned long val, unsigned long __user *addr,
|
||||
int *err) { }
|
||||
static inline void push_user_gcs(unsigned long val, int *err) { }
|
||||
|
||||
static inline unsigned long gcs_alloc_thread_stack(struct task_struct *tsk,
|
||||
const struct kernel_clone_args *args)
|
||||
{
|
||||
|
|
@ -136,6 +181,15 @@ static inline int gcs_check_locked(struct task_struct *task,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
static inline u64 get_user_gcs(unsigned long __user *addr, int *err)
|
||||
{
|
||||
*err = -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
static inline u64 pop_user_gcs(int *err)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue