Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | #include <linux/kernel.h> |
2 | #include <linux/errno.h> | |
3 | #include <linux/sched.h> | |
4 | #include <linux/user.h> | |
5 | ||
6 | #include <asm/uaccess.h> | |
7 | #include <asm/desc.h> | |
8 | #include <asm/system.h> | |
9 | #include <asm/ldt.h> | |
10 | #include <asm/processor.h> | |
11 | #include <asm/proto.h> | |
12 | ||
13 | /* | |
14 | * sys_alloc_thread_area: get a yet unused TLS descriptor index. | |
15 | */ | |
16 | static int get_free_idx(void) | |
17 | { | |
18 | struct thread_struct *t = ¤t->thread; | |
19 | int idx; | |
20 | ||
21 | for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++) | |
22 | if (desc_empty((struct n_desc_struct *)(t->tls_array) + idx)) | |
23 | return idx + GDT_ENTRY_TLS_MIN; | |
24 | return -ESRCH; | |
25 | } | |
26 | ||
27 | /* | |
28 | * Set a given TLS descriptor: | |
29 | * When you want addresses > 32bit use arch_prctl() | |
30 | */ | |
31 | int do_set_thread_area(struct thread_struct *t, struct user_desc __user *u_info) | |
32 | { | |
33 | struct user_desc info; | |
34 | struct n_desc_struct *desc; | |
35 | int cpu, idx; | |
36 | ||
37 | if (copy_from_user(&info, u_info, sizeof(info))) | |
38 | return -EFAULT; | |
39 | ||
40 | idx = info.entry_number; | |
41 | ||
42 | /* | |
43 | * index -1 means the kernel should try to find and | |
44 | * allocate an empty descriptor: | |
45 | */ | |
46 | if (idx == -1) { | |
47 | idx = get_free_idx(); | |
48 | if (idx < 0) | |
49 | return idx; | |
50 | if (put_user(idx, &u_info->entry_number)) | |
51 | return -EFAULT; | |
52 | } | |
53 | ||
54 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | |
55 | return -EINVAL; | |
56 | ||
57 | desc = ((struct n_desc_struct *)t->tls_array) + idx - GDT_ENTRY_TLS_MIN; | |
58 | ||
59 | /* | |
60 | * We must not get preempted while modifying the TLS. | |
61 | */ | |
62 | cpu = get_cpu(); | |
63 | ||
64 | if (LDT_empty(&info)) { | |
65 | desc->a = 0; | |
66 | desc->b = 0; | |
67 | } else { | |
68 | desc->a = LDT_entry_a(&info); | |
69 | desc->b = LDT_entry_b(&info); | |
70 | } | |
71 | if (t == ¤t->thread) | |
72 | load_TLS(t, cpu); | |
73 | ||
74 | put_cpu(); | |
75 | return 0; | |
76 | } | |
77 | ||
78 | asmlinkage long sys32_set_thread_area(struct user_desc __user *u_info) | |
79 | { | |
80 | return do_set_thread_area(¤t->thread, u_info); | |
81 | } | |
82 | ||
83 | ||
84 | /* | |
85 | * Get the current Thread-Local Storage area: | |
86 | */ | |
87 | ||
88 | #define GET_BASE(desc) ( \ | |
89 | (((desc)->a >> 16) & 0x0000ffff) | \ | |
90 | (((desc)->b << 16) & 0x00ff0000) | \ | |
91 | ( (desc)->b & 0xff000000) ) | |
92 | ||
93 | #define GET_LIMIT(desc) ( \ | |
94 | ((desc)->a & 0x0ffff) | \ | |
95 | ((desc)->b & 0xf0000) ) | |
96 | ||
97 | #define GET_32BIT(desc) (((desc)->b >> 22) & 1) | |
98 | #define GET_CONTENTS(desc) (((desc)->b >> 10) & 3) | |
99 | #define GET_WRITABLE(desc) (((desc)->b >> 9) & 1) | |
100 | #define GET_LIMIT_PAGES(desc) (((desc)->b >> 23) & 1) | |
101 | #define GET_PRESENT(desc) (((desc)->b >> 15) & 1) | |
102 | #define GET_USEABLE(desc) (((desc)->b >> 20) & 1) | |
103 | #define GET_LONGMODE(desc) (((desc)->b >> 21) & 1) | |
104 | ||
105 | int do_get_thread_area(struct thread_struct *t, struct user_desc __user *u_info) | |
106 | { | |
107 | struct user_desc info; | |
108 | struct n_desc_struct *desc; | |
109 | int idx; | |
110 | ||
111 | if (get_user(idx, &u_info->entry_number)) | |
112 | return -EFAULT; | |
113 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | |
114 | return -EINVAL; | |
115 | ||
116 | desc = ((struct n_desc_struct *)t->tls_array) + idx - GDT_ENTRY_TLS_MIN; | |
117 | ||
118 | memset(&info, 0, sizeof(struct user_desc)); | |
119 | info.entry_number = idx; | |
120 | info.base_addr = GET_BASE(desc); | |
121 | info.limit = GET_LIMIT(desc); | |
122 | info.seg_32bit = GET_32BIT(desc); | |
123 | info.contents = GET_CONTENTS(desc); | |
124 | info.read_exec_only = !GET_WRITABLE(desc); | |
125 | info.limit_in_pages = GET_LIMIT_PAGES(desc); | |
126 | info.seg_not_present = !GET_PRESENT(desc); | |
127 | info.useable = GET_USEABLE(desc); | |
128 | info.lm = GET_LONGMODE(desc); | |
129 | ||
130 | if (copy_to_user(u_info, &info, sizeof(info))) | |
131 | return -EFAULT; | |
132 | return 0; | |
133 | } | |
134 | ||
135 | asmlinkage long sys32_get_thread_area(struct user_desc __user *u_info) | |
136 | { | |
137 | return do_get_thread_area(¤t->thread, u_info); | |
138 | } | |
139 | ||
140 | ||
141 | int ia32_child_tls(struct task_struct *p, struct pt_regs *childregs) | |
142 | { | |
143 | struct n_desc_struct *desc; | |
144 | struct user_desc info; | |
145 | struct user_desc __user *cp; | |
146 | int idx; | |
147 | ||
148 | cp = (void __user *)childregs->rsi; | |
149 | if (copy_from_user(&info, cp, sizeof(info))) | |
150 | return -EFAULT; | |
151 | if (LDT_empty(&info)) | |
152 | return -EINVAL; | |
153 | ||
154 | idx = info.entry_number; | |
155 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | |
156 | return -EINVAL; | |
157 | ||
158 | desc = (struct n_desc_struct *)(p->thread.tls_array) + idx - GDT_ENTRY_TLS_MIN; | |
159 | desc->a = LDT_entry_a(&info); | |
160 | desc->b = LDT_entry_b(&info); | |
161 | ||
162 | return 0; | |
163 | } |