Skip to content

Commit 9cd2a7f

Browse files
jlintonarmwilldeacon
authored andcommitted
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>
1 parent ea920b5 commit 9cd2a7f

1 file changed

Lines changed: 54 additions & 0 deletions

File tree

  • arch/arm64/include/asm

arch/arm64/include/asm/gcs.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,47 @@ static inline void put_user_gcs(unsigned long val, unsigned long __user *addr,
116116
uaccess_ttbr0_disable();
117117
}
118118

119+
static inline void push_user_gcs(unsigned long val, int *err)
120+
{
121+
u64 gcspr = read_sysreg_s(SYS_GCSPR_EL0);
122+
123+
gcspr -= sizeof(u64);
124+
put_user_gcs(val, (unsigned long __user *)gcspr, err);
125+
if (!*err)
126+
write_sysreg_s(gcspr, SYS_GCSPR_EL0);
127+
}
128+
129+
/*
130+
* Unlike put/push_user_gcs() above, get/pop_user_gsc() doesn't
131+
* validate the GCS permission is set on the page being read. This
132+
* differs from how the hardware works when it consumes data stored at
133+
* GCSPR. Callers should ensure this is acceptable.
134+
*/
135+
static inline u64 get_user_gcs(unsigned long __user *addr, int *err)
136+
{
137+
unsigned long ret;
138+
u64 load = 0;
139+
140+
/* Ensure previous GCS operation are visible before we read the page */
141+
gcsb_dsync();
142+
ret = copy_from_user(&load, addr, sizeof(load));
143+
if (ret != 0)
144+
*err = ret;
145+
return load;
146+
}
147+
148+
static inline u64 pop_user_gcs(int *err)
149+
{
150+
u64 gcspr = read_sysreg_s(SYS_GCSPR_EL0);
151+
u64 read_val;
152+
153+
read_val = get_user_gcs((__force unsigned long __user *)gcspr, err);
154+
if (!*err)
155+
write_sysreg_s(gcspr + sizeof(u64), SYS_GCSPR_EL0);
156+
157+
return read_val;
158+
}
159+
119160
#else
120161

121162
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)
126167
static inline void gcs_set_el0_mode(struct task_struct *task) { }
127168
static inline void gcs_free(struct task_struct *task) { }
128169
static inline void gcs_preserve_current_state(void) { }
170+
static inline void put_user_gcs(unsigned long val, unsigned long __user *addr,
171+
int *err) { }
172+
static inline void push_user_gcs(unsigned long val, int *err) { }
173+
129174
static inline unsigned long gcs_alloc_thread_stack(struct task_struct *tsk,
130175
const struct kernel_clone_args *args)
131176
{
@@ -136,6 +181,15 @@ static inline int gcs_check_locked(struct task_struct *task,
136181
{
137182
return 0;
138183
}
184+
static inline u64 get_user_gcs(unsigned long __user *addr, int *err)
185+
{
186+
*err = -EFAULT;
187+
return 0;
188+
}
189+
static inline u64 pop_user_gcs(int *err)
190+
{
191+
return 0;
192+
}
139193

140194
#endif
141195

0 commit comments

Comments
 (0)