]>
git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blob - arch/x86/kernel/tls.c
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>
6 #include <linux/syscalls.h>
8 #include <asm/uaccess.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 bool tls_desc_okay(const struct user_desc
*info
)
36 * espfix is required for 16-bit data segments, but espfix
37 * only works for LDT segments.
45 static void set_tls_desc(struct task_struct
*p
, int idx
,
46 const struct user_desc
*info
, int n
)
48 struct thread_struct
*t
= &p
->thread
;
49 struct desc_struct
*desc
= &t
->tls_array
[idx
- GDT_ENTRY_TLS_MIN
];
53 * We must not get preempted while modifying the TLS.
59 desc
->a
= desc
->b
= 0;
66 if (t
== ¤t
->thread
)
73 * Set a given TLS descriptor:
75 int do_set_thread_area(struct task_struct
*p
, int idx
,
76 struct user_desc __user
*u_info
,
79 struct user_desc info
;
81 if (copy_from_user(&info
, u_info
, sizeof(info
)))
84 if (!tls_desc_okay(&info
))
88 idx
= info
.entry_number
;
91 * index -1 means the kernel should try to find and
92 * allocate an empty descriptor:
94 if (idx
== -1 && can_allocate
) {
98 if (put_user(idx
, &u_info
->entry_number
))
102 if (idx
< GDT_ENTRY_TLS_MIN
|| idx
> GDT_ENTRY_TLS_MAX
)
105 set_tls_desc(p
, idx
, &info
, 1);
110 SYSCALL_DEFINE1(set_thread_area
, struct user_desc __user
*, u_info
)
112 return do_set_thread_area(current
, -1, u_info
, 1);
117 * Get the current Thread-Local Storage area:
120 static void fill_user_desc(struct user_desc
*info
, int idx
,
121 const struct desc_struct
*desc
)
124 memset(info
, 0, sizeof(*info
));
125 info
->entry_number
= idx
;
126 info
->base_addr
= get_desc_base(desc
);
127 info
->limit
= get_desc_limit(desc
);
128 info
->seg_32bit
= desc
->d
;
129 info
->contents
= desc
->type
>> 2;
130 info
->read_exec_only
= !(desc
->type
& 2);
131 info
->limit_in_pages
= desc
->g
;
132 info
->seg_not_present
= !desc
->p
;
133 info
->useable
= desc
->avl
;
139 int do_get_thread_area(struct task_struct
*p
, int idx
,
140 struct user_desc __user
*u_info
)
142 struct user_desc info
;
144 if (idx
== -1 && get_user(idx
, &u_info
->entry_number
))
147 if (idx
< GDT_ENTRY_TLS_MIN
|| idx
> GDT_ENTRY_TLS_MAX
)
150 fill_user_desc(&info
, idx
,
151 &p
->thread
.tls_array
[idx
- GDT_ENTRY_TLS_MIN
]);
153 if (copy_to_user(u_info
, &info
, sizeof(info
)))
158 SYSCALL_DEFINE1(get_thread_area
, struct user_desc __user
*, u_info
)
160 return do_get_thread_area(current
, -1, u_info
);
163 int regset_tls_active(struct task_struct
*target
,
164 const struct user_regset
*regset
)
166 struct thread_struct
*t
= &target
->thread
;
167 int n
= GDT_ENTRY_TLS_ENTRIES
;
168 while (n
> 0 && desc_empty(&t
->tls_array
[n
- 1]))
173 int regset_tls_get(struct task_struct
*target
, const struct user_regset
*regset
,
174 unsigned int pos
, unsigned int count
,
175 void *kbuf
, void __user
*ubuf
)
177 const struct desc_struct
*tls
;
179 if (pos
>= GDT_ENTRY_TLS_ENTRIES
* sizeof(struct user_desc
) ||
180 (pos
% sizeof(struct user_desc
)) != 0 ||
181 (count
% sizeof(struct user_desc
)) != 0)
184 pos
/= sizeof(struct user_desc
);
185 count
/= sizeof(struct user_desc
);
187 tls
= &target
->thread
.tls_array
[pos
];
190 struct user_desc
*info
= kbuf
;
192 fill_user_desc(info
++, GDT_ENTRY_TLS_MIN
+ pos
++,
195 struct user_desc __user
*u_info
= ubuf
;
196 while (count
-- > 0) {
197 struct user_desc info
;
198 fill_user_desc(&info
, GDT_ENTRY_TLS_MIN
+ pos
++, tls
++);
199 if (__copy_to_user(u_info
++, &info
, sizeof(info
)))
207 int regset_tls_set(struct task_struct
*target
, const struct user_regset
*regset
,
208 unsigned int pos
, unsigned int count
,
209 const void *kbuf
, const void __user
*ubuf
)
211 struct user_desc infobuf
[GDT_ENTRY_TLS_ENTRIES
];
212 const struct user_desc
*info
;
215 if (pos
>= GDT_ENTRY_TLS_ENTRIES
* sizeof(struct user_desc
) ||
216 (pos
% sizeof(struct user_desc
)) != 0 ||
217 (count
% sizeof(struct user_desc
)) != 0)
222 else if (__copy_from_user(infobuf
, ubuf
, count
))
227 for (i
= 0; i
< count
/ sizeof(struct user_desc
); i
++)
228 if (!tls_desc_okay(info
+ i
))
232 GDT_ENTRY_TLS_MIN
+ (pos
/ sizeof(struct user_desc
)),
233 info
, count
/ sizeof(struct user_desc
));