aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/tls.c
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2008-01-30 07:31:52 -0500
committerIngo Molnar <mingo@elte.hu>2008-01-30 07:31:52 -0500
commit4c79a2d8e5b7e0a2f987ace9b6af9e7a1655447b (patch)
tree6f29726cabe888dcd1b6865c4e995eb5718f84b7 /arch/x86/kernel/tls.c
parent1bd5718ce58fb49ac158653380fa200f4759daad (diff)
x86: x86 user_regset TLS
This adds accessor functions in the user_regset style for the TLS data. 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 'arch/x86/kernel/tls.c')
-rw-r--r--arch/x86/kernel/tls.c89
1 files changed, 83 insertions, 6 deletions
diff --git a/arch/x86/kernel/tls.c b/arch/x86/kernel/tls.c
index f11c92a3faac..6dfd4e76661a 100644
--- a/arch/x86/kernel/tls.c
+++ b/arch/x86/kernel/tls.c
@@ -2,6 +2,7 @@
2#include <linux/errno.h> 2#include <linux/errno.h>
3#include <linux/sched.h> 3#include <linux/sched.h>
4#include <linux/user.h> 4#include <linux/user.h>
5#include <linux/regset.h>
5 6
6#include <asm/uaccess.h> 7#include <asm/uaccess.h>
7#include <asm/desc.h> 8#include <asm/desc.h>
@@ -10,6 +11,8 @@
10#include <asm/processor.h> 11#include <asm/processor.h>
11#include <asm/proto.h> 12#include <asm/proto.h>
12 13
14#include "tls.h"
15
13/* 16/*
14 * sys_alloc_thread_area: get a yet unused TLS descriptor index. 17 * sys_alloc_thread_area: get a yet unused TLS descriptor index.
15 */ 18 */
@@ -25,7 +28,7 @@ static int get_free_idx(void)
25} 28}
26 29
27static void set_tls_desc(struct task_struct *p, int idx, 30static void set_tls_desc(struct task_struct *p, int idx,
28 const struct user_desc *info) 31 const struct user_desc *info, int n)
29{ 32{
30 struct thread_struct *t = &p->thread; 33 struct thread_struct *t = &p->thread;
31 struct desc_struct *desc = &t->tls_array[idx - GDT_ENTRY_TLS_MIN]; 34 struct desc_struct *desc = &t->tls_array[idx - GDT_ENTRY_TLS_MIN];
@@ -36,10 +39,14 @@ static void set_tls_desc(struct task_struct *p, int idx,
36 */ 39 */
37 cpu = get_cpu(); 40 cpu = get_cpu();
38 41
39 if (LDT_empty(info)) 42 while (n-- > 0) {
40 desc->a = desc->b = 0; 43 if (LDT_empty(info))
41 else 44 desc->a = desc->b = 0;
42 fill_ldt(desc, info); 45 else
46 fill_ldt(desc, info);
47 ++info;
48 ++desc;
49 }
43 50
44 if (t == &current->thread) 51 if (t == &current->thread)
45 load_TLS(t, cpu); 52 load_TLS(t, cpu);
@@ -77,7 +84,7 @@ int do_set_thread_area(struct task_struct *p, int idx,
77 if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) 84 if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
78 return -EINVAL; 85 return -EINVAL;
79 86
80 set_tls_desc(p, idx, &info); 87 set_tls_desc(p, idx, &info, 1);
81 88
82 return 0; 89 return 0;
83} 90}
@@ -134,3 +141,73 @@ asmlinkage int sys_get_thread_area(struct user_desc __user *u_info)
134{ 141{
135 return do_get_thread_area(current, -1, u_info); 142 return do_get_thread_area(current, -1, u_info);
136} 143}
144
145int regset_tls_active(struct task_struct *target,
146 const struct user_regset *regset)
147{
148 struct thread_struct *t = &target->thread;
149 int n = GDT_ENTRY_TLS_ENTRIES;
150 while (n > 0 && desc_empty(&t->tls_array[n - 1]))
151 --n;
152 return n;
153}
154
155int regset_tls_get(struct task_struct *target, const struct user_regset *regset,
156 unsigned int pos, unsigned int count,
157 void *kbuf, void __user *ubuf)
158{
159 const struct desc_struct *tls;
160
161 if (pos > GDT_ENTRY_TLS_ENTRIES * sizeof(struct user_desc) ||
162 (pos % sizeof(struct user_desc)) != 0 ||
163 (count % sizeof(struct user_desc)) != 0)
164 return -EINVAL;
165
166 pos /= sizeof(struct user_desc);
167 count /= sizeof(struct user_desc);
168
169 tls = &target->thread.tls_array[pos];
170
171 if (kbuf) {
172 struct user_desc *info = kbuf;
173 while (count-- > 0)
174 fill_user_desc(info++, GDT_ENTRY_TLS_MIN + pos++,
175 tls++);
176 } else {
177 struct user_desc __user *u_info = ubuf;
178 while (count-- > 0) {
179 struct user_desc info;
180 fill_user_desc(&info, GDT_ENTRY_TLS_MIN + pos++, tls++);
181 if (__copy_to_user(u_info++, &info, sizeof(info)))
182 return -EFAULT;
183 }
184 }
185
186 return 0;
187}
188
189int regset_tls_set(struct task_struct *target, const struct user_regset *regset,
190 unsigned int pos, unsigned int count,
191 const void *kbuf, const void __user *ubuf)
192{
193 struct user_desc infobuf[GDT_ENTRY_TLS_ENTRIES];
194 const struct user_desc *info;
195
196 if (pos > GDT_ENTRY_TLS_ENTRIES * sizeof(struct user_desc) ||
197 (pos % sizeof(struct user_desc)) != 0 ||
198 (count % sizeof(struct user_desc)) != 0)
199 return -EINVAL;
200
201 if (kbuf)
202 info = kbuf;
203 else if (__copy_from_user(infobuf, ubuf, count))
204 return -EFAULT;
205 else
206 info = infobuf;
207
208 set_tls_desc(target,
209 GDT_ENTRY_TLS_MIN + (pos / sizeof(struct user_desc)),
210 info, count / sizeof(struct user_desc));
211
212 return 0;
213}