Commit | Line | Data |
---|---|---|
83ca897d FW |
1 | Chinese translated version of Documentation/arm/kernel_user_helpers.txt |
2 | ||
3 | If you have any comment or update to the content, please contact the | |
4 | original document maintainer directly. However, if you have a problem | |
5 | communicating in English you can also ask the Chinese maintainer for | |
6 | help. Contact the Chinese maintainer if this translation is outdated | |
7 | or if there is a problem with the translation. | |
8 | ||
9 | Maintainer: Nicolas Pitre <nicolas.pitre@linaro.org> | |
10 | Dave Martin <dave.martin@linaro.org> | |
11 | Chinese maintainer: Fu Wei <tekkamanninja@gmail.com> | |
12 | --------------------------------------------------------------------- | |
13 | Documentation/arm/kernel_user_helpers.txt 的中文翻译 | |
14 | ||
15 | 如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 | |
16 | 交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 | |
17 | 译存在问题,请联系中文版维护者。 | |
18 | 英文版维护者: Nicolas Pitre <nicolas.pitre@linaro.org> | |
19 | Dave Martin <dave.martin@linaro.org> | |
20 | 中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com> | |
21 | 中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com> | |
22 | 中文版校译者: 宋冬生 Dongsheng Song <dongshneg.song@gmail.com> | |
23 | 傅炜 Fu Wei <tekkamanninja@gmail.com> | |
24 | ||
25 | ||
26 | 以下为正文 | |
27 | --------------------------------------------------------------------- | |
28 | 内核提供的用户空间辅助代码 | |
29 | ========================= | |
30 | ||
31 | 在内核内存空间的固定地址处,有一个由内核提供并可从用户空间访问的代码 | |
32 | 段。它用于向用户空间提供因在许多 ARM CPU 中未实现的特性和/或指令而需 | |
33 | 内核提供帮助的某些操作。这些代码直接在用户模式下执行的想法是为了获得 | |
34 | 最佳效率,但那些与内核计数器联系过于紧密的部分,则被留给了用户库实现。 | |
35 | 事实上,此代码甚至可能因不同的 CPU 而异,这取决于其可用的指令集或它 | |
36 | 是否为 SMP 系统。换句话说,内核保留在不作出警告的情况下根据需要更改 | |
37 | 这些代码的权利。只有本文档描述的入口及其结果是保证稳定的。 | |
38 | ||
39 | 这与完全成熟的 VDSO 实现不同(但两者并不冲突),尽管如此,VDSO 可阻止 | |
40 | 某些通过常量高效跳转到那些代码段的汇编技巧。且由于那些代码段在返回用户 | |
41 | 代码前仅使用少量的代码周期,则一个 VDSO 间接远程调用将会在这些简单的 | |
42 | 操作上增加一个可测量的开销。 | |
43 | ||
44 | 在对那些拥有原生支持的新型处理器进行代码优化时,仅在已为其他操作使用 | |
45 | 了类似的新增指令,而导致二进制结果已与早期 ARM 处理器不兼容的情况下, | |
46 | 用户空间才应绕过这些辅助代码,并在内联函数中实现这些操作(无论是通过 | |
47 | 编译器在代码中直接放置,还是作为库函数调用实现的一部分)。也就是说, | |
48 | 如果你编译的代码不会为了其他目的使用新指令,则不要仅为了避免使用这些 | |
49 | 内核辅助代码,导致二进制程序无法在早期处理器上运行。 | |
50 | ||
51 | 新的辅助代码可能随着时间的推移而增加,所以新内核中的某些辅助代码在旧 | |
52 | 内核中可能不存在。因此,程序必须在对任何辅助代码调用假设是安全之前, | |
53 | 检测 __kuser_helper_version 的值(见下文)。理想情况下,这种检测应该 | |
54 | 只在进程启动时执行一次;如果内核版本不支持所需辅助代码,则该进程可尽早 | |
55 | 中止执行。 | |
56 | ||
57 | kuser_helper_version | |
58 | -------------------- | |
59 | ||
60 | 位置: 0xffff0ffc | |
61 | ||
62 | 参考声明: | |
63 | ||
64 | extern int32_t __kuser_helper_version; | |
65 | ||
66 | 定义: | |
67 | ||
68 | 这个区域包含了当前运行内核实现的辅助代码版本号。用户空间可以通过读 | |
69 | 取此版本号以确定特定的辅助代码是否存在。 | |
70 | ||
71 | 使用范例: | |
72 | ||
73 | #define __kuser_helper_version (*(int32_t *)0xffff0ffc) | |
74 | ||
75 | void check_kuser_version(void) | |
76 | { | |
77 | if (__kuser_helper_version < 2) { | |
78 | fprintf(stderr, "can't do atomic operations, kernel too old\n"); | |
79 | abort(); | |
80 | } | |
81 | } | |
82 | ||
83 | 注意: | |
84 | ||
85 | 用户空间可以假设这个域的值不会在任何单个进程的生存期内改变。也就 | |
86 | 是说,这个域可以仅在库的初始化阶段或进程启动阶段读取一次。 | |
87 | ||
88 | kuser_get_tls | |
89 | ------------- | |
90 | ||
91 | 位置: 0xffff0fe0 | |
92 | ||
93 | 参考原型: | |
94 | ||
95 | void * __kuser_get_tls(void); | |
96 | ||
97 | 输入: | |
98 | ||
99 | lr = 返回地址 | |
100 | ||
101 | 输出: | |
102 | ||
103 | r0 = TLS 值 | |
104 | ||
105 | 被篡改的寄存器: | |
106 | ||
107 | 无 | |
108 | ||
109 | 定义: | |
110 | ||
111 | 获取之前通过 __ARM_NR_set_tls 系统调用设置的 TLS 值。 | |
112 | ||
113 | 使用范例: | |
114 | ||
115 | typedef void * (__kuser_get_tls_t)(void); | |
116 | #define __kuser_get_tls (*(__kuser_get_tls_t *)0xffff0fe0) | |
117 | ||
118 | void foo() | |
119 | { | |
120 | void *tls = __kuser_get_tls(); | |
121 | printf("TLS = %p\n", tls); | |
122 | } | |
123 | ||
124 | 注意: | |
125 | ||
126 | - 仅在 __kuser_helper_version >= 1 时,此辅助代码存在 | |
127 | (从内核版本 2.6.12 开始)。 | |
128 | ||
129 | kuser_cmpxchg | |
130 | ------------- | |
131 | ||
132 | 位置: 0xffff0fc0 | |
133 | ||
134 | 参考原型: | |
135 | ||
136 | int __kuser_cmpxchg(int32_t oldval, int32_t newval, volatile int32_t *ptr); | |
137 | ||
138 | 输入: | |
139 | ||
140 | r0 = oldval | |
141 | r1 = newval | |
142 | r2 = ptr | |
143 | lr = 返回地址 | |
144 | ||
145 | 输出: | |
146 | ||
147 | r0 = 成功代码 (零或非零) | |
148 | C flag = 如果 r0 == 0 则置 1,如果 r0 != 0 则清零。 | |
149 | ||
150 | 被篡改的寄存器: | |
151 | ||
152 | r3, ip, flags | |
153 | ||
154 | 定义: | |
155 | ||
156 | 仅在 *ptr 为 oldval 时原子保存 newval 于 *ptr 中。 | |
157 | 如果 *ptr 被改变,则返回值为零,否则为非零值。 | |
158 | 如果 *ptr 被改变,则 C flag 也会被置 1,以实现调用代码中的汇编 | |
159 | 优化。 | |
160 | ||
161 | 使用范例: | |
162 | ||
163 | typedef int (__kuser_cmpxchg_t)(int oldval, int newval, volatile int *ptr); | |
164 | #define __kuser_cmpxchg (*(__kuser_cmpxchg_t *)0xffff0fc0) | |
165 | ||
166 | int atomic_add(volatile int *ptr, int val) | |
167 | { | |
168 | int old, new; | |
169 | ||
170 | do { | |
171 | old = *ptr; | |
172 | new = old + val; | |
173 | } while(__kuser_cmpxchg(old, new, ptr)); | |
174 | ||
175 | return new; | |
176 | } | |
177 | ||
178 | 注意: | |
179 | ||
180 | - 这个例程已根据需要包含了内存屏障。 | |
181 | ||
182 | - 仅在 __kuser_helper_version >= 2 时,此辅助代码存在 | |
183 | (从内核版本 2.6.12 开始)。 | |
184 | ||
185 | kuser_memory_barrier | |
186 | -------------------- | |
187 | ||
188 | 位置: 0xffff0fa0 | |
189 | ||
190 | 参考原型: | |
191 | ||
192 | void __kuser_memory_barrier(void); | |
193 | ||
194 | 输入: | |
195 | ||
196 | lr = 返回地址 | |
197 | ||
198 | 输出: | |
199 | ||
200 | 无 | |
201 | ||
202 | 被篡改的寄存器: | |
203 | ||
204 | 无 | |
205 | ||
206 | 定义: | |
207 | ||
208 | 应用于任何需要内存屏障以防止手动数据修改带来的一致性问题,以及 | |
209 | __kuser_cmpxchg 中。 | |
210 | ||
211 | 使用范例: | |
212 | ||
213 | typedef void (__kuser_dmb_t)(void); | |
214 | #define __kuser_dmb (*(__kuser_dmb_t *)0xffff0fa0) | |
215 | ||
216 | 注意: | |
217 | ||
218 | - 仅在 __kuser_helper_version >= 3 时,此辅助代码存在 | |
219 | (从内核版本 2.6.15 开始)。 | |
220 | ||
221 | kuser_cmpxchg64 | |
222 | --------------- | |
223 | ||
224 | 位置: 0xffff0f60 | |
225 | ||
226 | 参考原型: | |
227 | ||
228 | int __kuser_cmpxchg64(const int64_t *oldval, | |
229 | const int64_t *newval, | |
230 | volatile int64_t *ptr); | |
231 | ||
232 | 输入: | |
233 | ||
234 | r0 = 指向 oldval | |
235 | r1 = 指向 newval | |
236 | r2 = 指向目标值 | |
237 | lr = 返回地址 | |
238 | ||
239 | 输出: | |
240 | ||
241 | r0 = 成功代码 (零或非零) | |
242 | C flag = 如果 r0 == 0 则置 1,如果 r0 != 0 则清零。 | |
243 | ||
244 | 被篡改的寄存器: | |
245 | ||
246 | r3, lr, flags | |
247 | ||
248 | 定义: | |
249 | ||
250 | 仅在 *ptr 等于 *oldval 指向的 64 位值时,原子保存 *newval | |
251 | 指向的 64 位值于 *ptr 中。如果 *ptr 被改变,则返回值为零, | |
252 | 否则为非零值。 | |
253 | ||
254 | 如果 *ptr 被改变,则 C flag 也会被置 1,以实现调用代码中的汇编 | |
255 | 优化。 | |
256 | ||
257 | 使用范例: | |
258 | ||
259 | typedef int (__kuser_cmpxchg64_t)(const int64_t *oldval, | |
260 | const int64_t *newval, | |
261 | volatile int64_t *ptr); | |
262 | #define __kuser_cmpxchg64 (*(__kuser_cmpxchg64_t *)0xffff0f60) | |
263 | ||
264 | int64_t atomic_add64(volatile int64_t *ptr, int64_t val) | |
265 | { | |
266 | int64_t old, new; | |
267 | ||
268 | do { | |
269 | old = *ptr; | |
270 | new = old + val; | |
271 | } while(__kuser_cmpxchg64(&old, &new, ptr)); | |
272 | ||
273 | return new; | |
274 | } | |
275 | ||
276 | 注意: | |
277 | ||
278 | - 这个例程已根据需要包含了内存屏障。 | |
279 | ||
280 | - 由于这个过程的代码长度(此辅助代码跨越 2 个常规的 kuser “槽”), | |
281 | 因此 0xffff0f80 不被作为有效的入口点。 | |
282 | ||
283 | - 仅在 __kuser_helper_version >= 5 时,此辅助代码存在 | |
284 | (从内核版本 3.1 开始)。 |