summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/regset.h51
-rw-r--r--kernel/regset.c12
2 files changed, 62 insertions, 1 deletions
diff --git a/include/linux/regset.h b/include/linux/regset.h
index af57c1db1924..f6125a7d949d 100644
--- a/include/linux/regset.h
+++ b/include/linux/regset.h
@@ -17,6 +17,52 @@
struct task_struct;
struct user_regset;
+struct membuf {
+ void *p;
+ size_t left;
+};
+
+static inline int membuf_zero(struct membuf *s, size_t size)
+{
+ if (s->left) {
+ if (size > s->left)
+ size = s->left;
+ memset(s->p, 0, size);
+ s->p += size;
+ s->left -= size;
+ }
+ return s->left;
+}
+
+static inline int membuf_write(struct membuf *s, const void *v, size_t size)
+{
+ if (s->left) {
+ if (size > s->left)
+ size = s->left;
+ memcpy(s->p, v, size);
+ s->p += size;
+ s->left -= size;
+ }
+ return s->left;
+}
+
+/* current s->p must be aligned for v; v must be a scalar */
+#define membuf_store(s, v) \
+({ \
+ struct membuf *__s = (s); \
+ if (__s->left) { \
+ typeof(v) __v = (v); \
+ size_t __size = sizeof(__v); \
+ if (unlikely(__size > __s->left)) { \
+ __size = __s->left; \
+ memcpy(__s->p, &__v, __size); \
+ } else { \
+ *(typeof(__v + 0) *)__s->p = __v; \
+ } \
+ __s->p += __size; \
+ __s->left -= __size; \
+ } \
+ __s->left;})
/**
* user_regset_active_fn - type of @active function in &struct user_regset
@@ -57,6 +103,10 @@ typedef int user_regset_get_fn(struct task_struct *target,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf);
+typedef int user_regset_get2_fn(struct task_struct *target,
+ const struct user_regset *regset,
+ struct membuf to);
+
/**
* user_regset_set_fn - type of @set function in &struct user_regset
* @target: thread being examined
@@ -186,6 +236,7 @@ typedef unsigned int user_regset_get_size_fn(struct task_struct *target,
*/
struct user_regset {
user_regset_get_fn *get;
+ user_regset_get2_fn *regset_get;
user_regset_set_fn *set;
user_regset_active_fn *active;
user_regset_writeback_fn *writeback;
diff --git a/kernel/regset.c b/kernel/regset.c
index 0a610983ce43..eaeaefbbd39e 100644
--- a/kernel/regset.c
+++ b/kernel/regset.c
@@ -11,7 +11,7 @@ static int __regset_get(struct task_struct *target,
void *p = *data, *to_free = NULL;
int res;
- if (!regset->get)
+ if (!regset->get && !regset->regset_get)
return -EOPNOTSUPP;
if (size > regset->n * regset->size)
size = regset->n * regset->size;
@@ -20,6 +20,16 @@ static int __regset_get(struct task_struct *target,
if (!p)
return -ENOMEM;
}
+ if (regset->regset_get) {
+ res = regset->regset_get(target, regset,
+ (struct membuf){.p = p, .left = size});
+ if (res < 0) {
+ kfree(to_free);
+ return res;
+ }
+ *data = p;
+ return size - res;
+ }
res = regset->get(target, regset, 0, size, p, NULL);
if (unlikely(res < 0)) {
kfree(to_free);