Commit 4c79a2d8e5b7e0a2f987ace9b6af9e7a1655447b
Committed by
Ingo Molnar
1 parent
1bd5718ce5
Exists in
master
and in
7 other branches
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>
Showing 2 changed files with 104 additions and 6 deletions Side-by-side Diff
arch/x86/kernel/tls.c
... | ... | @@ -2,6 +2,7 @@ |
2 | 2 | #include <linux/errno.h> |
3 | 3 | #include <linux/sched.h> |
4 | 4 | #include <linux/user.h> |
5 | +#include <linux/regset.h> | |
5 | 6 | |
6 | 7 | #include <asm/uaccess.h> |
7 | 8 | #include <asm/desc.h> |
... | ... | @@ -10,6 +11,8 @@ |
10 | 11 | #include <asm/processor.h> |
11 | 12 | #include <asm/proto.h> |
12 | 13 | |
14 | +#include "tls.h" | |
15 | + | |
13 | 16 | /* |
14 | 17 | * sys_alloc_thread_area: get a yet unused TLS descriptor index. |
15 | 18 | */ |
... | ... | @@ -25,7 +28,7 @@ |
25 | 28 | } |
26 | 29 | |
27 | 30 | static 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 | 33 | struct thread_struct *t = &p->thread; |
31 | 34 | struct desc_struct *desc = &t->tls_array[idx - GDT_ENTRY_TLS_MIN]; |
... | ... | @@ -36,10 +39,14 @@ |
36 | 39 | */ |
37 | 40 | cpu = get_cpu(); |
38 | 41 | |
39 | - if (LDT_empty(info)) | |
40 | - desc->a = desc->b = 0; | |
41 | - else | |
42 | - fill_ldt(desc, info); | |
42 | + while (n-- > 0) { | |
43 | + if (LDT_empty(info)) | |
44 | + desc->a = desc->b = 0; | |
45 | + else | |
46 | + fill_ldt(desc, info); | |
47 | + ++info; | |
48 | + ++desc; | |
49 | + } | |
43 | 50 | |
44 | 51 | if (t == ¤t->thread) |
45 | 52 | load_TLS(t, cpu); |
... | ... | @@ -77,7 +84,7 @@ |
77 | 84 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) |
78 | 85 | return -EINVAL; |
79 | 86 | |
80 | - set_tls_desc(p, idx, &info); | |
87 | + set_tls_desc(p, idx, &info, 1); | |
81 | 88 | |
82 | 89 | return 0; |
83 | 90 | } |
... | ... | @@ -133,5 +140,75 @@ |
133 | 140 | asmlinkage int sys_get_thread_area(struct user_desc __user *u_info) |
134 | 141 | { |
135 | 142 | return do_get_thread_area(current, -1, u_info); |
143 | +} | |
144 | + | |
145 | +int 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 | + | |
155 | +int 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 | + | |
189 | +int 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; | |
136 | 213 | } |
arch/x86/kernel/tls.h
1 | +/* | |
2 | + * Internal declarations for x86 TLS implementation functions. | |
3 | + * | |
4 | + * Copyright (C) 2007 Red Hat, Inc. All rights reserved. | |
5 | + * | |
6 | + * This copyrighted material is made available to anyone wishing to use, | |
7 | + * modify, copy, or redistribute it subject to the terms and conditions | |
8 | + * of the GNU General Public License v.2. | |
9 | + * | |
10 | + * Red Hat Author: Roland McGrath. | |
11 | + */ | |
12 | + | |
13 | +#ifndef _ARCH_X86_KERNEL_TLS_H | |
14 | + | |
15 | +#include <linux/regset.h> | |
16 | + | |
17 | +extern user_regset_active_fn regset_tls_active; | |
18 | +extern user_regset_get_fn regset_tls_get; | |
19 | +extern user_regset_set_fn regset_tls_set; | |
20 | + | |
21 | +#endif /* _ARCH_X86_KERNEL_TLS_H */ |