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