1 #include <linux/kernel.h>
2 #include <linux/errno.h>
3 #include <linux/sched.h>
4 #include <linux/user.h>
5 #include <linux/regset.h>
7 #include <asm/uaccess.h>
9 #include <asm/system.h>
11 #include <asm/processor.h>
12 #include <asm/proto.h>
17 * sys_alloc_thread_area: get a yet unused TLS descriptor index.
19 static int get_free_idx(void)
21 struct thread_struct
*t
= ¤t
->thread
;
24 for (idx
= 0; idx
< GDT_ENTRY_TLS_ENTRIES
; idx
++)
25 if (desc_empty(&t
->tls_array
[idx
]))
26 return idx
+ GDT_ENTRY_TLS_MIN
;
30 static void set_tls_desc(struct task_struct
*p
, int idx
,
31 const struct user_desc
*info
, int n
)
33 struct thread_struct
*t
= &p
->thread
;
34 struct desc_struct
*desc
= &t
->tls_array
[idx
- GDT_ENTRY_TLS_MIN
];
38 * We must not get preempted while modifying the TLS.
44 desc
->a
= desc
->b
= 0;
51 if (t
== ¤t
->thread
)
58 * Set a given TLS descriptor:
60 int do_set_thread_area(struct task_struct
*p
, int idx
,
61 struct user_desc __user
*u_info
,
64 struct user_desc info
;
66 if (copy_from_user(&info
, u_info
, sizeof(info
)))
70 idx
= info
.entry_number
;
73 * index -1 means the kernel should try to find and
74 * allocate an empty descriptor:
76 if (idx
== -1 && can_allocate
) {
80 if (put_user(idx
, &u_info
->entry_number
))
84 if (idx
< GDT_ENTRY_TLS_MIN
|| idx
> GDT_ENTRY_TLS_MAX
)
87 set_tls_desc(p
, idx
, &info
, 1);
92 asmlinkage
int sys_set_thread_area(struct user_desc __user
*u_info
)
94 int ret
= do_set_thread_area(current
, -1, u_info
, 1);
95 prevent_tail_call(ret
);
101 * Get the current Thread-Local Storage area:
104 static void fill_user_desc(struct user_desc
*info
, int idx
,
105 const struct desc_struct
*desc
)
108 memset(info
, 0, sizeof(*info
));
109 info
->entry_number
= idx
;
110 info
->base_addr
= get_desc_base(desc
);
111 info
->limit
= get_desc_limit(desc
);
112 info
->seg_32bit
= desc
->d
;
113 info
->contents
= desc
->type
>> 2;
114 info
->read_exec_only
= !(desc
->type
& 2);
115 info
->limit_in_pages
= desc
->g
;
116 info
->seg_not_present
= !desc
->p
;
117 info
->useable
= desc
->avl
;
123 int do_get_thread_area(struct task_struct
*p
, int idx
,
124 struct user_desc __user
*u_info
)
126 struct user_desc info
;
128 if (idx
== -1 && get_user(idx
, &u_info
->entry_number
))
131 if (idx
< GDT_ENTRY_TLS_MIN
|| idx
> GDT_ENTRY_TLS_MAX
)
134 fill_user_desc(&info
, idx
,
135 &p
->thread
.tls_array
[idx
- GDT_ENTRY_TLS_MIN
]);
137 if (copy_to_user(u_info
, &info
, sizeof(info
)))
142 asmlinkage
int sys_get_thread_area(struct user_desc __user
*u_info
)
144 int ret
= do_get_thread_area(current
, -1, u_info
);
145 prevent_tail_call(ret
);
149 int regset_tls_active(struct task_struct
*target
,
150 const struct user_regset
*regset
)
152 struct thread_struct
*t
= &target
->thread
;
153 int n
= GDT_ENTRY_TLS_ENTRIES
;
154 while (n
> 0 && desc_empty(&t
->tls_array
[n
- 1]))
159 int regset_tls_get(struct task_struct
*target
, const struct user_regset
*regset
,
160 unsigned int pos
, unsigned int count
,
161 void *kbuf
, void __user
*ubuf
)
163 const struct desc_struct
*tls
;
165 if (pos
> GDT_ENTRY_TLS_ENTRIES
* sizeof(struct user_desc
) ||
166 (pos
% sizeof(struct user_desc
)) != 0 ||
167 (count
% sizeof(struct user_desc
)) != 0)
170 pos
/= sizeof(struct user_desc
);
171 count
/= sizeof(struct user_desc
);
173 tls
= &target
->thread
.tls_array
[pos
];
176 struct user_desc
*info
= kbuf
;
178 fill_user_desc(info
++, GDT_ENTRY_TLS_MIN
+ pos
++,
181 struct user_desc __user
*u_info
= ubuf
;
182 while (count
-- > 0) {
183 struct user_desc info
;
184 fill_user_desc(&info
, GDT_ENTRY_TLS_MIN
+ pos
++, tls
++);
185 if (__copy_to_user(u_info
++, &info
, sizeof(info
)))
193 int regset_tls_set(struct task_struct
*target
, const struct user_regset
*regset
,
194 unsigned int pos
, unsigned int count
,
195 const void *kbuf
, const void __user
*ubuf
)
197 struct user_desc infobuf
[GDT_ENTRY_TLS_ENTRIES
];
198 const struct user_desc
*info
;
200 if (pos
> GDT_ENTRY_TLS_ENTRIES
* sizeof(struct user_desc
) ||
201 (pos
% sizeof(struct user_desc
)) != 0 ||
202 (count
% sizeof(struct user_desc
)) != 0)
207 else if (__copy_from_user(infobuf
, ubuf
, count
))
213 GDT_ENTRY_TLS_MIN
+ (pos
/ sizeof(struct user_desc
)),
214 info
, count
/ sizeof(struct user_desc
));