Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Roland McGrath and committed by
Ingo Molnar
bae3f7c3 4206d3aa

+116
+116
include/linux/regset.h
··· 15 15 16 16 #include <linux/compiler.h> 17 17 #include <linux/types.h> 18 + #include <linux/uaccess.h> 18 19 struct task_struct; 19 20 struct user_regset; 20 21 ··· 202 201 * Throughout the life of the process, this only changes at exec. 203 202 */ 204 203 const struct user_regset_view *task_user_regset_view(struct task_struct *tsk); 204 + 205 + 206 + /* 207 + * These are helpers for writing regset get/set functions in arch code. 208 + * Because @start_pos and @end_pos are always compile-time constants, 209 + * these are inlined into very little code though they look large. 210 + * 211 + * Use one or more calls sequentially for each chunk of regset data stored 212 + * contiguously in memory. Call with constants for @start_pos and @end_pos, 213 + * giving the range of byte positions in the regset that data corresponds 214 + * to; @end_pos can be -1 if this chunk is at the end of the regset layout. 215 + * Each call updates the arguments to point past its chunk. 216 + */ 217 + 218 + static inline int user_regset_copyout(unsigned int *pos, unsigned int *count, 219 + void **kbuf, 220 + void __user **ubuf, const void *data, 221 + const int start_pos, const int end_pos) 222 + { 223 + if (*count == 0) 224 + return 0; 225 + BUG_ON(*pos < start_pos); 226 + if (end_pos < 0 || *pos < end_pos) { 227 + unsigned int copy = (end_pos < 0 ? *count 228 + : min(*count, end_pos - *pos)); 229 + data += *pos - start_pos; 230 + if (*kbuf) { 231 + memcpy(*kbuf, data, copy); 232 + *kbuf += copy; 233 + } else if (__copy_to_user(*ubuf, data, copy)) 234 + return -EFAULT; 235 + else 236 + *ubuf += copy; 237 + *pos += copy; 238 + *count -= copy; 239 + } 240 + return 0; 241 + } 242 + 243 + static inline int user_regset_copyin(unsigned int *pos, unsigned int *count, 244 + const void **kbuf, 245 + const void __user **ubuf, void *data, 246 + const int start_pos, const int end_pos) 247 + { 248 + if (*count == 0) 249 + return 0; 250 + BUG_ON(*pos < start_pos); 251 + if (end_pos < 0 || *pos < end_pos) { 252 + unsigned int copy = (end_pos < 0 ? *count 253 + : min(*count, end_pos - *pos)); 254 + data += *pos - start_pos; 255 + if (*kbuf) { 256 + memcpy(data, *kbuf, copy); 257 + *kbuf += copy; 258 + } else if (__copy_from_user(data, *ubuf, copy)) 259 + return -EFAULT; 260 + else 261 + *ubuf += copy; 262 + *pos += copy; 263 + *count -= copy; 264 + } 265 + return 0; 266 + } 267 + 268 + /* 269 + * These two parallel the two above, but for portions of a regset layout 270 + * that always read as all-zero or for which writes are ignored. 271 + */ 272 + static inline int user_regset_copyout_zero(unsigned int *pos, 273 + unsigned int *count, 274 + void **kbuf, void __user **ubuf, 275 + const int start_pos, 276 + const int end_pos) 277 + { 278 + if (*count == 0) 279 + return 0; 280 + BUG_ON(*pos < start_pos); 281 + if (end_pos < 0 || *pos < end_pos) { 282 + unsigned int copy = (end_pos < 0 ? *count 283 + : min(*count, end_pos - *pos)); 284 + if (*kbuf) { 285 + memset(*kbuf, 0, copy); 286 + *kbuf += copy; 287 + } else if (__clear_user(*ubuf, copy)) 288 + return -EFAULT; 289 + else 290 + *ubuf += copy; 291 + *pos += copy; 292 + *count -= copy; 293 + } 294 + return 0; 295 + } 296 + 297 + static inline int user_regset_copyin_ignore(unsigned int *pos, 298 + unsigned int *count, 299 + const void **kbuf, 300 + const void __user **ubuf, 301 + const int start_pos, 302 + const int end_pos) 303 + { 304 + if (*count == 0) 305 + return 0; 306 + BUG_ON(*pos < start_pos); 307 + if (end_pos < 0 || *pos < end_pos) { 308 + unsigned int copy = (end_pos < 0 ? *count 309 + : min(*count, end_pos - *pos)); 310 + if (*kbuf) 311 + *kbuf += copy; 312 + else 313 + *ubuf += copy; 314 + *pos += copy; 315 + *count -= copy; 316 + } 317 + return 0; 318 + } 205 319 206 320 207 321 #endif /* <linux/regset.h> */