diff options
-rw-r--r-- | arch/x86/kernel/tls.c | 89 | ||||
-rw-r--r-- | include/asm-x86/desc.h | 11 |
2 files changed, 54 insertions, 46 deletions
diff --git a/arch/x86/kernel/tls.c b/arch/x86/kernel/tls.c index 98f428be8e8c..f11c92a3faac 100644 --- a/arch/x86/kernel/tls.c +++ b/arch/x86/kernel/tls.c | |||
@@ -24,6 +24,29 @@ static int get_free_idx(void) | |||
24 | return -ESRCH; | 24 | return -ESRCH; |
25 | } | 25 | } |
26 | 26 | ||
27 | static void set_tls_desc(struct task_struct *p, int idx, | ||
28 | const struct user_desc *info) | ||
29 | { | ||
30 | struct thread_struct *t = &p->thread; | ||
31 | struct desc_struct *desc = &t->tls_array[idx - GDT_ENTRY_TLS_MIN]; | ||
32 | int cpu; | ||
33 | |||
34 | /* | ||
35 | * We must not get preempted while modifying the TLS. | ||
36 | */ | ||
37 | cpu = get_cpu(); | ||
38 | |||
39 | if (LDT_empty(info)) | ||
40 | desc->a = desc->b = 0; | ||
41 | else | ||
42 | fill_ldt(desc, info); | ||
43 | |||
44 | if (t == ¤t->thread) | ||
45 | load_TLS(t, cpu); | ||
46 | |||
47 | put_cpu(); | ||
48 | } | ||
49 | |||
27 | /* | 50 | /* |
28 | * Set a given TLS descriptor: | 51 | * Set a given TLS descriptor: |
29 | */ | 52 | */ |
@@ -31,10 +54,7 @@ int do_set_thread_area(struct task_struct *p, int idx, | |||
31 | struct user_desc __user *u_info, | 54 | struct user_desc __user *u_info, |
32 | int can_allocate) | 55 | int can_allocate) |
33 | { | 56 | { |
34 | struct thread_struct *t = &p->thread; | ||
35 | struct user_desc info; | 57 | struct user_desc info; |
36 | u32 *desc; | ||
37 | int cpu; | ||
38 | 58 | ||
39 | if (copy_from_user(&info, u_info, sizeof(info))) | 59 | if (copy_from_user(&info, u_info, sizeof(info))) |
40 | return -EFAULT; | 60 | return -EFAULT; |
@@ -57,23 +77,8 @@ int do_set_thread_area(struct task_struct *p, int idx, | |||
57 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | 77 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) |
58 | return -EINVAL; | 78 | return -EINVAL; |
59 | 79 | ||
60 | desc = (u32 *) &t->tls_array[idx - GDT_ENTRY_TLS_MIN]; | 80 | set_tls_desc(p, idx, &info); |
61 | 81 | ||
62 | /* | ||
63 | * We must not get preempted while modifying the TLS. | ||
64 | */ | ||
65 | cpu = get_cpu(); | ||
66 | |||
67 | if (LDT_empty(&info)) { | ||
68 | desc[0] = 0; | ||
69 | desc[1] = 0; | ||
70 | } else | ||
71 | fill_ldt((struct desc_struct *)desc, &info); | ||
72 | |||
73 | if (t == ¤t->thread) | ||
74 | load_TLS(t, cpu); | ||
75 | |||
76 | put_cpu(); | ||
77 | return 0; | 82 | return 0; |
78 | } | 83 | } |
79 | 84 | ||
@@ -87,42 +92,38 @@ asmlinkage int sys_set_thread_area(struct user_desc __user *u_info) | |||
87 | * Get the current Thread-Local Storage area: | 92 | * Get the current Thread-Local Storage area: |
88 | */ | 93 | */ |
89 | 94 | ||
90 | #define GET_LIMIT(desc) (((desc)[0] & 0x0ffff) | ((desc)[1] & 0xf0000)) | 95 | static void fill_user_desc(struct user_desc *info, int idx, |
91 | #define GET_32BIT(desc) (((desc)[1] >> 22) & 1) | 96 | const struct desc_struct *desc) |
92 | #define GET_CONTENTS(desc) (((desc)[1] >> 10) & 3) | 97 | |
93 | #define GET_WRITABLE(desc) (((desc)[1] >> 9) & 1) | 98 | { |
94 | #define GET_LIMIT_PAGES(desc) (((desc)[1] >> 23) & 1) | 99 | memset(info, 0, sizeof(*info)); |
95 | #define GET_PRESENT(desc) (((desc)[1] >> 15) & 1) | 100 | info->entry_number = idx; |
96 | #define GET_USEABLE(desc) (((desc)[1] >> 20) & 1) | 101 | info->base_addr = get_desc_base(desc); |
97 | #define GET_LONGMODE(desc) (((desc)[1] >> 21) & 1) | 102 | info->limit = get_desc_limit(desc); |
103 | info->seg_32bit = desc->d; | ||
104 | info->contents = desc->type >> 2; | ||
105 | info->read_exec_only = !(desc->type & 2); | ||
106 | info->limit_in_pages = desc->g; | ||
107 | info->seg_not_present = !desc->p; | ||
108 | info->useable = desc->avl; | ||
109 | #ifdef CONFIG_X86_64 | ||
110 | info->lm = desc->l; | ||
111 | #endif | ||
112 | } | ||
98 | 113 | ||
99 | int do_get_thread_area(struct task_struct *p, int idx, | 114 | int do_get_thread_area(struct task_struct *p, int idx, |
100 | struct user_desc __user *u_info) | 115 | struct user_desc __user *u_info) |
101 | { | 116 | { |
102 | struct thread_struct *t = &p->thread; | ||
103 | struct user_desc info; | 117 | struct user_desc info; |
104 | u32 *desc; | ||
105 | 118 | ||
106 | if (idx == -1 && get_user(idx, &u_info->entry_number)) | 119 | if (idx == -1 && get_user(idx, &u_info->entry_number)) |
107 | return -EFAULT; | 120 | return -EFAULT; |
121 | |||
108 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | 122 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) |
109 | return -EINVAL; | 123 | return -EINVAL; |
110 | 124 | ||
111 | desc = (u32 *) &t->tls_array[idx - GDT_ENTRY_TLS_MIN]; | 125 | fill_user_desc(&info, idx, |
112 | 126 | &p->thread.tls_array[idx - GDT_ENTRY_TLS_MIN]); | |
113 | memset(&info, 0, sizeof(struct user_desc)); | ||
114 | info.entry_number = idx; | ||
115 | info.base_addr = get_desc_base((struct desc_struct *)desc); | ||
116 | info.limit = GET_LIMIT(desc); | ||
117 | info.seg_32bit = GET_32BIT(desc); | ||
118 | info.contents = GET_CONTENTS(desc); | ||
119 | info.read_exec_only = !GET_WRITABLE(desc); | ||
120 | info.limit_in_pages = GET_LIMIT_PAGES(desc); | ||
121 | info.seg_not_present = !GET_PRESENT(desc); | ||
122 | info.useable = GET_USEABLE(desc); | ||
123 | #ifdef CONFIG_X86_64 | ||
124 | info.lm = GET_LONGMODE(desc); | ||
125 | #endif | ||
126 | 127 | ||
127 | if (copy_to_user(u_info, &info, sizeof(info))) | 128 | if (copy_to_user(u_info, &info, sizeof(info))) |
128 | return -EFAULT; | 129 | return -EFAULT; |
diff --git a/include/asm-x86/desc.h b/include/asm-x86/desc.h index e17581ad8824..5b6a05d3a771 100644 --- a/include/asm-x86/desc.h +++ b/include/asm-x86/desc.h | |||
@@ -7,7 +7,8 @@ | |||
7 | #include <asm/mmu.h> | 7 | #include <asm/mmu.h> |
8 | #include <linux/smp.h> | 8 | #include <linux/smp.h> |
9 | 9 | ||
10 | static inline void fill_ldt(struct desc_struct *desc, struct user_desc *info) | 10 | static inline void fill_ldt(struct desc_struct *desc, |
11 | const struct user_desc *info) | ||
11 | { | 12 | { |
12 | desc->limit0 = info->limit & 0x0ffff; | 13 | desc->limit0 = info->limit & 0x0ffff; |
13 | desc->base0 = info->base_addr & 0x0000ffff; | 14 | desc->base0 = info->base_addr & 0x0000ffff; |
@@ -275,10 +276,16 @@ static inline void load_LDT(mm_context_t *pc) | |||
275 | preempt_enable(); | 276 | preempt_enable(); |
276 | } | 277 | } |
277 | 278 | ||
278 | static inline unsigned long get_desc_base(struct desc_struct *desc) | 279 | static inline unsigned long get_desc_base(const struct desc_struct *desc) |
279 | { | 280 | { |
280 | return desc->base0 | ((desc->base1) << 16) | ((desc->base2) << 24); | 281 | return desc->base0 | ((desc->base1) << 16) | ((desc->base2) << 24); |
281 | } | 282 | } |
283 | |||
284 | static inline unsigned long get_desc_limit(const struct desc_struct *desc) | ||
285 | { | ||
286 | return desc->limit0 | (desc->limit << 16); | ||
287 | } | ||
288 | |||
282 | static inline void _set_gate(int gate, unsigned type, void *addr, | 289 | static inline void _set_gate(int gate, unsigned type, void *addr, |
283 | unsigned dpl, unsigned ist, unsigned seg) | 290 | unsigned dpl, unsigned ist, unsigned seg) |
284 | { | 291 | { |