Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 1 | ============================ |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 2 | Kernel-provided User Helpers |
| 3 | ============================ |
| 4 | |
| 5 | These are segment of kernel provided user code reachable from user space |
| 6 | at a fixed address in kernel memory. This is used to provide user space |
| 7 | with some operations which require kernel help because of unimplemented |
| 8 | native feature and/or instructions in many ARM CPUs. The idea is for this |
| 9 | code to be executed directly in user mode for best efficiency but which is |
| 10 | too intimate with the kernel counter part to be left to user libraries. |
| 11 | In fact this code might even differ from one CPU to another depending on |
| 12 | the available instruction set, or whether it is a SMP systems. In other |
| 13 | words, the kernel reserves the right to change this code as needed without |
| 14 | warning. Only the entry points and their results as documented here are |
| 15 | guaranteed to be stable. |
| 16 | |
| 17 | This is different from (but doesn't preclude) a full blown VDSO |
| 18 | implementation, however a VDSO would prevent some assembly tricks with |
| 19 | constants that allows for efficient branching to those code segments. And |
| 20 | since those code segments only use a few cycles before returning to user |
| 21 | code, the overhead of a VDSO indirect far call would add a measurable |
| 22 | overhead to such minimalistic operations. |
| 23 | |
| 24 | User space is expected to bypass those helpers and implement those things |
| 25 | inline (either in the code emitted directly by the compiler, or part of |
| 26 | the implementation of a library call) when optimizing for a recent enough |
| 27 | processor that has the necessary native support, but only if resulting |
| 28 | binaries are already to be incompatible with earlier ARM processors due to |
Masanari Iida | 40e4712 | 2012-03-04 23:16:11 +0900 | [diff] [blame] | 29 | usage of similar native instructions for other things. In other words |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 30 | don't make binaries unable to run on earlier processors just for the sake |
| 31 | of not using these kernel helpers if your compiled code is not going to |
| 32 | use new instructions for other purpose. |
| 33 | |
| 34 | New helpers may be added over time, so an older kernel may be missing some |
| 35 | helpers present in a newer kernel. For this reason, programs must check |
| 36 | the value of __kuser_helper_version (see below) before assuming that it is |
| 37 | safe to call any particular helper. This check should ideally be |
| 38 | performed only once at process startup time, and execution aborted early |
| 39 | if the required helpers are not provided by the kernel version that |
| 40 | process is running on. |
| 41 | |
| 42 | kuser_helper_version |
| 43 | -------------------- |
| 44 | |
| 45 | Location: 0xffff0ffc |
| 46 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 47 | Reference declaration:: |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 48 | |
| 49 | extern int32_t __kuser_helper_version; |
| 50 | |
| 51 | Definition: |
| 52 | |
| 53 | This field contains the number of helpers being implemented by the |
| 54 | running kernel. User space may read this to determine the availability |
| 55 | of a particular helper. |
| 56 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 57 | Usage example:: |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 58 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 59 | #define __kuser_helper_version (*(int32_t *)0xffff0ffc) |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 60 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 61 | void check_kuser_version(void) |
| 62 | { |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 63 | if (__kuser_helper_version < 2) { |
| 64 | fprintf(stderr, "can't do atomic operations, kernel too old\n"); |
| 65 | abort(); |
| 66 | } |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 67 | } |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 68 | |
| 69 | Notes: |
| 70 | |
| 71 | User space may assume that the value of this field never changes |
| 72 | during the lifetime of any single process. This means that this |
| 73 | field can be read once during the initialisation of a library or |
| 74 | startup phase of a program. |
| 75 | |
| 76 | kuser_get_tls |
| 77 | ------------- |
| 78 | |
| 79 | Location: 0xffff0fe0 |
| 80 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 81 | Reference prototype:: |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 82 | |
| 83 | void * __kuser_get_tls(void); |
| 84 | |
| 85 | Input: |
| 86 | |
| 87 | lr = return address |
| 88 | |
| 89 | Output: |
| 90 | |
| 91 | r0 = TLS value |
| 92 | |
| 93 | Clobbered registers: |
| 94 | |
| 95 | none |
| 96 | |
| 97 | Definition: |
| 98 | |
| 99 | Get the TLS value as previously set via the __ARM_NR_set_tls syscall. |
| 100 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 101 | Usage example:: |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 102 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 103 | typedef void * (__kuser_get_tls_t)(void); |
| 104 | #define __kuser_get_tls (*(__kuser_get_tls_t *)0xffff0fe0) |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 105 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 106 | void foo() |
| 107 | { |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 108 | void *tls = __kuser_get_tls(); |
| 109 | printf("TLS = %p\n", tls); |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 110 | } |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 111 | |
| 112 | Notes: |
| 113 | |
| 114 | - Valid only if __kuser_helper_version >= 1 (from kernel version 2.6.12). |
| 115 | |
| 116 | kuser_cmpxchg |
| 117 | ------------- |
| 118 | |
| 119 | Location: 0xffff0fc0 |
| 120 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 121 | Reference prototype:: |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 122 | |
| 123 | int __kuser_cmpxchg(int32_t oldval, int32_t newval, volatile int32_t *ptr); |
| 124 | |
| 125 | Input: |
| 126 | |
| 127 | r0 = oldval |
| 128 | r1 = newval |
| 129 | r2 = ptr |
| 130 | lr = return address |
| 131 | |
| 132 | Output: |
| 133 | |
| 134 | r0 = success code (zero or non-zero) |
| 135 | C flag = set if r0 == 0, clear if r0 != 0 |
| 136 | |
| 137 | Clobbered registers: |
| 138 | |
| 139 | r3, ip, flags |
| 140 | |
| 141 | Definition: |
| 142 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 143 | Atomically store newval in `*ptr` only if `*ptr` is equal to oldval. |
| 144 | Return zero if `*ptr` was changed or non-zero if no exchange happened. |
| 145 | The C flag is also set if `*ptr` was changed to allow for assembly |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 146 | optimization in the calling code. |
| 147 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 148 | Usage example:: |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 149 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 150 | typedef int (__kuser_cmpxchg_t)(int oldval, int newval, volatile int *ptr); |
| 151 | #define __kuser_cmpxchg (*(__kuser_cmpxchg_t *)0xffff0fc0) |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 152 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 153 | int atomic_add(volatile int *ptr, int val) |
| 154 | { |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 155 | int old, new; |
| 156 | |
| 157 | do { |
| 158 | old = *ptr; |
| 159 | new = old + val; |
| 160 | } while(__kuser_cmpxchg(old, new, ptr)); |
| 161 | |
| 162 | return new; |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 163 | } |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 164 | |
| 165 | Notes: |
| 166 | |
| 167 | - This routine already includes memory barriers as needed. |
| 168 | |
| 169 | - Valid only if __kuser_helper_version >= 2 (from kernel version 2.6.12). |
| 170 | |
| 171 | kuser_memory_barrier |
| 172 | -------------------- |
| 173 | |
| 174 | Location: 0xffff0fa0 |
| 175 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 176 | Reference prototype:: |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 177 | |
| 178 | void __kuser_memory_barrier(void); |
| 179 | |
| 180 | Input: |
| 181 | |
| 182 | lr = return address |
| 183 | |
| 184 | Output: |
| 185 | |
| 186 | none |
| 187 | |
| 188 | Clobbered registers: |
| 189 | |
| 190 | none |
| 191 | |
| 192 | Definition: |
| 193 | |
| 194 | Apply any needed memory barrier to preserve consistency with data modified |
| 195 | manually and __kuser_cmpxchg usage. |
| 196 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 197 | Usage example:: |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 198 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 199 | typedef void (__kuser_dmb_t)(void); |
| 200 | #define __kuser_dmb (*(__kuser_dmb_t *)0xffff0fa0) |
Nicolas Pitre | 37b8304 | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 201 | |
| 202 | Notes: |
| 203 | |
| 204 | - Valid only if __kuser_helper_version >= 3 (from kernel version 2.6.15). |
Nicolas Pitre | 40fb79c | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 205 | |
| 206 | kuser_cmpxchg64 |
| 207 | --------------- |
| 208 | |
| 209 | Location: 0xffff0f60 |
| 210 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 211 | Reference prototype:: |
Nicolas Pitre | 40fb79c | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 212 | |
| 213 | int __kuser_cmpxchg64(const int64_t *oldval, |
| 214 | const int64_t *newval, |
| 215 | volatile int64_t *ptr); |
| 216 | |
| 217 | Input: |
| 218 | |
| 219 | r0 = pointer to oldval |
| 220 | r1 = pointer to newval |
| 221 | r2 = pointer to target value |
| 222 | lr = return address |
| 223 | |
| 224 | Output: |
| 225 | |
| 226 | r0 = success code (zero or non-zero) |
| 227 | C flag = set if r0 == 0, clear if r0 != 0 |
| 228 | |
| 229 | Clobbered registers: |
| 230 | |
| 231 | r3, lr, flags |
| 232 | |
| 233 | Definition: |
| 234 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 235 | Atomically store the 64-bit value pointed by `*newval` in `*ptr` only if `*ptr` |
| 236 | is equal to the 64-bit value pointed by `*oldval`. Return zero if `*ptr` was |
Nicolas Pitre | 40fb79c | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 237 | changed or non-zero if no exchange happened. |
| 238 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 239 | The C flag is also set if `*ptr` was changed to allow for assembly |
Nicolas Pitre | 40fb79c | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 240 | optimization in the calling code. |
| 241 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 242 | Usage example:: |
Nicolas Pitre | 40fb79c | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 243 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 244 | typedef int (__kuser_cmpxchg64_t)(const int64_t *oldval, |
| 245 | const int64_t *newval, |
| 246 | volatile int64_t *ptr); |
| 247 | #define __kuser_cmpxchg64 (*(__kuser_cmpxchg64_t *)0xffff0f60) |
Nicolas Pitre | 40fb79c | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 248 | |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 249 | int64_t atomic_add64(volatile int64_t *ptr, int64_t val) |
| 250 | { |
Nicolas Pitre | 40fb79c | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 251 | int64_t old, new; |
| 252 | |
| 253 | do { |
| 254 | old = *ptr; |
| 255 | new = old + val; |
| 256 | } while(__kuser_cmpxchg64(&old, &new, ptr)); |
| 257 | |
| 258 | return new; |
Mauro Carvalho Chehab | dc7a12b | 2019-04-14 15:51:10 -0300 | [diff] [blame] | 259 | } |
Nicolas Pitre | 40fb79c | 2011-06-19 23:36:03 -0400 | [diff] [blame] | 260 | |
| 261 | Notes: |
| 262 | |
| 263 | - This routine already includes memory barriers as needed. |
| 264 | |
| 265 | - Due to the length of this sequence, this spans 2 conventional kuser |
| 266 | "slots", therefore 0xffff0f80 is not used as a valid entry point. |
| 267 | |
| 268 | - Valid only if __kuser_helper_version >= 5 (from kernel version 3.1). |