aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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> */