Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jeremy Linton and committed by
Will Deacon
9cd2a7f1 ea920b50

+54
+54
arch/arm64/include/asm/gcs.h
··· 116 116 uaccess_ttbr0_disable(); 117 117 } 118 118 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 + 119 160 #else 120 161 121 162 static inline bool task_gcs_el0_enabled(struct task_struct *task) ··· 167 126 static inline void gcs_set_el0_mode(struct task_struct *task) { } 168 127 static inline void gcs_free(struct task_struct *task) { } 169 128 static inline void gcs_preserve_current_state(void) { } 129 + static inline void put_user_gcs(unsigned long val, unsigned long __user *addr, 130 + int *err) { } 131 + static inline void push_user_gcs(unsigned long val, int *err) { } 132 + 170 133 static inline unsigned long gcs_alloc_thread_stack(struct task_struct *tsk, 171 134 const struct kernel_clone_args *args) 172 135 { ··· 178 133 } 179 134 static inline int gcs_check_locked(struct task_struct *task, 180 135 unsigned long new_val) 136 + { 137 + return 0; 138 + } 139 + static inline u64 get_user_gcs(unsigned long __user *addr, int *err) 140 + { 141 + *err = -EFAULT; 142 + return 0; 143 + } 144 + static inline u64 pop_user_gcs(int *err) 181 145 { 182 146 return 0; 183 147 }