Commit | Line | Data |
---|---|---|
dc7a12bd | 1 | ============================ |
37b83046 NP |
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 | |
40e47125 | 29 | usage of similar native instructions for other things. In other words |
37b83046 NP |
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 | ||
dc7a12bd | 47 | Reference declaration:: |
37b83046 NP |
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 | ||
dc7a12bd | 57 | Usage example:: |
37b83046 | 58 | |
dc7a12bd | 59 | #define __kuser_helper_version (*(int32_t *)0xffff0ffc) |
37b83046 | 60 | |
dc7a12bd MCC |
61 | void check_kuser_version(void) |
62 | { | |
37b83046 NP |
63 | if (__kuser_helper_version < 2) { |
64 | fprintf(stderr, "can't do atomic operations, kernel too old\n"); | |
65 | abort(); | |
66 | } | |
dc7a12bd | 67 | } |
37b83046 NP |
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 | ||
dc7a12bd | 81 | Reference prototype:: |
37b83046 NP |
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 | ||
dc7a12bd | 101 | Usage example:: |
37b83046 | 102 | |
dc7a12bd MCC |
103 | typedef void * (__kuser_get_tls_t)(void); |
104 | #define __kuser_get_tls (*(__kuser_get_tls_t *)0xffff0fe0) | |
37b83046 | 105 | |
dc7a12bd MCC |
106 | void foo() |
107 | { | |
37b83046 NP |
108 | void *tls = __kuser_get_tls(); |
109 | printf("TLS = %p\n", tls); | |
dc7a12bd | 110 | } |
37b83046 NP |
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 | ||
dc7a12bd | 121 | Reference prototype:: |
37b83046 NP |
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 | ||
dc7a12bd MCC |
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 | |
37b83046 NP |
146 | optimization in the calling code. |
147 | ||
dc7a12bd | 148 | Usage example:: |
37b83046 | 149 | |
dc7a12bd MCC |
150 | typedef int (__kuser_cmpxchg_t)(int oldval, int newval, volatile int *ptr); |
151 | #define __kuser_cmpxchg (*(__kuser_cmpxchg_t *)0xffff0fc0) | |
37b83046 | 152 | |
dc7a12bd MCC |
153 | int atomic_add(volatile int *ptr, int val) |
154 | { | |
37b83046 NP |
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; | |
dc7a12bd | 163 | } |
37b83046 NP |
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 | ||
dc7a12bd | 176 | Reference prototype:: |
37b83046 NP |
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 | ||
dc7a12bd | 197 | Usage example:: |
37b83046 | 198 | |
dc7a12bd MCC |
199 | typedef void (__kuser_dmb_t)(void); |
200 | #define __kuser_dmb (*(__kuser_dmb_t *)0xffff0fa0) | |
37b83046 NP |
201 | |
202 | Notes: | |
203 | ||
204 | - Valid only if __kuser_helper_version >= 3 (from kernel version 2.6.15). | |
40fb79c8 NP |
205 | |
206 | kuser_cmpxchg64 | |
207 | --------------- | |
208 | ||
209 | Location: 0xffff0f60 | |
210 | ||
dc7a12bd | 211 | Reference prototype:: |
40fb79c8 NP |
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 | ||
dc7a12bd MCC |
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 | |
40fb79c8 NP |
237 | changed or non-zero if no exchange happened. |
238 | ||
dc7a12bd | 239 | The C flag is also set if `*ptr` was changed to allow for assembly |
40fb79c8 NP |
240 | optimization in the calling code. |
241 | ||
dc7a12bd | 242 | Usage example:: |
40fb79c8 | 243 | |
dc7a12bd MCC |
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) | |
40fb79c8 | 248 | |
dc7a12bd MCC |
249 | int64_t atomic_add64(volatile int64_t *ptr, int64_t val) |
250 | { | |
40fb79c8 NP |
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; | |
dc7a12bd | 259 | } |
40fb79c8 NP |
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). |