aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2008-01-30 07:31:45 -0500
committerIngo Molnar <mingo@elte.hu>2008-01-30 07:31:45 -0500
commitbae3f7c39dee5951bcbedeaedb6744f882a00173 (patch)
treeb11d7246f1933f80c33c1a5cff6291ab142e87dc
parent4206d3aa1978e44f58bfa4e1c9d8d35cbf19c187 (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>
-rw-r--r--include/linux/regset.h116
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>
18struct task_struct; 19struct task_struct;
19struct user_regset; 20struct user_regset;
20 21
@@ -203,4 +204,119 @@ struct user_regset_view {
203const struct user_regset_view *task_user_regset_view(struct task_struct *tsk); 204const 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
219static 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
244static 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 */
273static 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
298static 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> */