tls.c 2.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/user.h>

#include <asm/uaccess.h>
#include <asm/desc.h>
#include <asm/system.h>
#include <asm/ldt.h>
#include <asm/processor.h>
#include <asm/proto.h>

/*
 * sys_alloc_thread_area: get a yet unused TLS descriptor index.
 */
static int get_free_idx(void)
{
	struct thread_struct *t = &current->thread;
	int idx;

	for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++)
R
Roland McGrath 已提交
22
		if (desc_empty(&t->tls_array[idx]))
L
Linus Torvalds 已提交
23 24 25 26
			return idx + GDT_ENTRY_TLS_MIN;
	return -ESRCH;
}

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
static void set_tls_desc(struct task_struct *p, int idx,
			 const struct user_desc *info)
{
	struct thread_struct *t = &p->thread;
	struct desc_struct *desc = &t->tls_array[idx - GDT_ENTRY_TLS_MIN];
	int cpu;

	/*
	 * We must not get preempted while modifying the TLS.
	 */
	cpu = get_cpu();

	if (LDT_empty(info))
		desc->a = desc->b = 0;
	else
		fill_ldt(desc, info);

	if (t == &current->thread)
		load_TLS(t, cpu);

	put_cpu();
}

L
Linus Torvalds 已提交
50 51 52
/*
 * Set a given TLS descriptor:
 */
R
Roland McGrath 已提交
53 54 55
int do_set_thread_area(struct task_struct *p, int idx,
		       struct user_desc __user *u_info,
		       int can_allocate)
L
Linus Torvalds 已提交
56 57 58 59 60 61
{
	struct user_desc info;

	if (copy_from_user(&info, u_info, sizeof(info)))
		return -EFAULT;

R
Roland McGrath 已提交
62 63
	if (idx == -1)
		idx = info.entry_number;
L
Linus Torvalds 已提交
64 65 66 67 68

	/*
	 * index -1 means the kernel should try to find and
	 * allocate an empty descriptor:
	 */
R
Roland McGrath 已提交
69
	if (idx == -1 && can_allocate) {
L
Linus Torvalds 已提交
70 71 72 73 74 75 76 77 78 79
		idx = get_free_idx();
		if (idx < 0)
			return idx;
		if (put_user(idx, &u_info->entry_number))
			return -EFAULT;
	}

	if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
		return -EINVAL;

80
	set_tls_desc(p, idx, &info);
L
Linus Torvalds 已提交
81 82 83 84

	return 0;
}

R
Roland McGrath 已提交
85
asmlinkage int sys_set_thread_area(struct user_desc __user *u_info)
R
Roland McGrath 已提交
86
{
R
Roland McGrath 已提交
87
	return do_set_thread_area(current, -1, u_info, 1);
R
Roland McGrath 已提交
88
}
L
Linus Torvalds 已提交
89 90 91 92 93 94


/*
 * Get the current Thread-Local Storage area:
 */

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
static void fill_user_desc(struct user_desc *info, int idx,
			   const struct desc_struct *desc)

{
	memset(info, 0, sizeof(*info));
	info->entry_number = idx;
	info->base_addr = get_desc_base(desc);
	info->limit = get_desc_limit(desc);
	info->seg_32bit = desc->d;
	info->contents = desc->type >> 2;
	info->read_exec_only = !(desc->type & 2);
	info->limit_in_pages = desc->g;
	info->seg_not_present = !desc->p;
	info->useable = desc->avl;
#ifdef CONFIG_X86_64
	info->lm = desc->l;
#endif
}
R
Roland McGrath 已提交
113 114 115

int do_get_thread_area(struct task_struct *p, int idx,
		       struct user_desc __user *u_info)
L
Linus Torvalds 已提交
116 117 118
{
	struct user_desc info;

R
Roland McGrath 已提交
119
	if (idx == -1 && get_user(idx, &u_info->entry_number))
L
Linus Torvalds 已提交
120
		return -EFAULT;
121

L
Linus Torvalds 已提交
122 123 124
	if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
		return -EINVAL;

125 126
	fill_user_desc(&info, idx,
		       &p->thread.tls_array[idx - GDT_ENTRY_TLS_MIN]);
L
Linus Torvalds 已提交
127 128 129 130 131 132

	if (copy_to_user(u_info, &info, sizeof(info)))
		return -EFAULT;
	return 0;
}

R
Roland McGrath 已提交
133
asmlinkage int sys_get_thread_area(struct user_desc __user *u_info)
L
Linus Torvalds 已提交
134
{
R
Roland McGrath 已提交
135
	return do_get_thread_area(current, -1, u_info);
L
Linus Torvalds 已提交
136
}