diff options
author | Roland McGrath <roland@redhat.com> | 2008-01-30 07:31:45 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-01-30 07:31:45 -0500 |
commit | bae3f7c39dee5951bcbedeaedb6744f882a00173 (patch) | |
tree | b11d7246f1933f80c33c1a5cff6291ab142e87dc /include/linux/regset.h | |
parent | 4206d3aa1978e44f58bfa4e1c9d8d35cbf19c187 (diff) |
x86: user_regset helpers
This adds some inlines to linux/regset.h intended for arch code to use in
its user_regset get and set functions. These make it pretty easy to deal
with the interface's optional kernel-space or user-space pointers and its
generalized access to a part of the register data at a time.
In simple cases where the internal data structure matches the exported
layout (core dump format), a get function can be nothing but a call to
user_regset_copyout, and a set function a call to user_regset_copyin.
In other cases the exported layout is usually made up of a few pieces each
stored contiguously in a different internal data structure. These helpers
make it straightforward to write a get or set function by processing each
contiguous chunk of the data in order. The start_pos and end_pos arguments
are always constants, so these inlines collapse to a small amount of code.
Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'include/linux/regset.h')
-rw-r--r-- | include/linux/regset.h | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/include/linux/regset.h b/include/linux/regset.h index 85d0fb0a014d..761c931af975 100644 --- a/include/linux/regset.h +++ b/include/linux/regset.h | |||
@@ -15,6 +15,7 @@ | |||
15 | 15 | ||
16 | #include <linux/compiler.h> | 16 | #include <linux/compiler.h> |
17 | #include <linux/types.h> | 17 | #include <linux/types.h> |
18 | #include <linux/uaccess.h> | ||
18 | struct task_struct; | 19 | struct task_struct; |
19 | struct user_regset; | 20 | struct user_regset; |
20 | 21 | ||
@@ -203,4 +204,119 @@ struct user_regset_view { | |||
203 | const struct user_regset_view *task_user_regset_view(struct task_struct *tsk); | 204 | const struct user_regset_view *task_user_regset_view(struct task_struct *tsk); |
204 | 205 | ||
205 | 206 | ||
207 | /* | ||
208 | * These are helpers for writing regset get/set functions in arch code. | ||
209 | * Because @start_pos and @end_pos are always compile-time constants, | ||
210 | * these are inlined into very little code though they look large. | ||
211 | * | ||
212 | * Use one or more calls sequentially for each chunk of regset data stored | ||
213 | * contiguously in memory. Call with constants for @start_pos and @end_pos, | ||
214 | * giving the range of byte positions in the regset that data corresponds | ||
215 | * to; @end_pos can be -1 if this chunk is at the end of the regset layout. | ||
216 | * Each call updates the arguments to point past its chunk. | ||
217 | */ | ||
218 | |||
219 | static inline int user_regset_copyout(unsigned int *pos, unsigned int *count, | ||
220 | void **kbuf, | ||
221 | void __user **ubuf, const void *data, | ||
222 | const int start_pos, const int end_pos) | ||
223 | { | ||
224 | if (*count == 0) | ||
225 | return 0; | ||
226 | BUG_ON(*pos < start_pos); | ||
227 | if (end_pos < 0 || *pos < end_pos) { | ||
228 | unsigned int copy = (end_pos < 0 ? *count | ||
229 | : min(*count, end_pos - *pos)); | ||
230 | data += *pos - start_pos; | ||
231 | if (*kbuf) { | ||
232 | memcpy(*kbuf, data, copy); | ||
233 | *kbuf += copy; | ||
234 | } else if (__copy_to_user(*ubuf, data, copy)) | ||
235 | return -EFAULT; | ||
236 | else | ||
237 | *ubuf += copy; | ||
238 | *pos += copy; | ||
239 | *count -= copy; | ||
240 | } | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static inline int user_regset_copyin(unsigned int *pos, unsigned int *count, | ||
245 | const void **kbuf, | ||
246 | const void __user **ubuf, void *data, | ||
247 | const int start_pos, const int end_pos) | ||
248 | { | ||
249 | if (*count == 0) | ||
250 | return 0; | ||
251 | BUG_ON(*pos < start_pos); | ||
252 | if (end_pos < 0 || *pos < end_pos) { | ||
253 | unsigned int copy = (end_pos < 0 ? *count | ||
254 | : min(*count, end_pos - *pos)); | ||
255 | data += *pos - start_pos; | ||
256 | if (*kbuf) { | ||
257 | memcpy(data, *kbuf, copy); | ||
258 | *kbuf += copy; | ||
259 | } else if (__copy_from_user(data, *ubuf, copy)) | ||
260 | return -EFAULT; | ||
261 | else | ||
262 | *ubuf += copy; | ||
263 | *pos += copy; | ||
264 | *count -= copy; | ||
265 | } | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | /* | ||
270 | * These two parallel the two above, but for portions of a regset layout | ||
271 | * that always read as all-zero or for which writes are ignored. | ||
272 | */ | ||
273 | static inline int user_regset_copyout_zero(unsigned int *pos, | ||
274 | unsigned int *count, | ||
275 | void **kbuf, void __user **ubuf, | ||
276 | const int start_pos, | ||
277 | const int end_pos) | ||
278 | { | ||
279 | if (*count == 0) | ||
280 | return 0; | ||
281 | BUG_ON(*pos < start_pos); | ||
282 | if (end_pos < 0 || *pos < end_pos) { | ||
283 | unsigned int copy = (end_pos < 0 ? *count | ||
284 | : min(*count, end_pos - *pos)); | ||
285 | if (*kbuf) { | ||
286 | memset(*kbuf, 0, copy); | ||
287 | *kbuf += copy; | ||
288 | } else if (__clear_user(*ubuf, copy)) | ||
289 | return -EFAULT; | ||
290 | else | ||
291 | *ubuf += copy; | ||
292 | *pos += copy; | ||
293 | *count -= copy; | ||
294 | } | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static inline int user_regset_copyin_ignore(unsigned int *pos, | ||
299 | unsigned int *count, | ||
300 | const void **kbuf, | ||
301 | const void __user **ubuf, | ||
302 | const int start_pos, | ||
303 | const int end_pos) | ||
304 | { | ||
305 | if (*count == 0) | ||
306 | return 0; | ||
307 | BUG_ON(*pos < start_pos); | ||
308 | if (end_pos < 0 || *pos < end_pos) { | ||
309 | unsigned int copy = (end_pos < 0 ? *count | ||
310 | : min(*count, end_pos - *pos)); | ||
311 | if (*kbuf) | ||
312 | *kbuf += copy; | ||
313 | else | ||
314 | *ubuf += copy; | ||
315 | *pos += copy; | ||
316 | *count -= copy; | ||
317 | } | ||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | |||
206 | #endif /* <linux/regset.h> */ | 322 | #endif /* <linux/regset.h> */ |