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: | |
13abd0e5 | 29 | * When you want addresses > 32bit use arch_prctl() |
1da177e4 LT |
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) | |
13abd0e5 RM |
79 | { |
80 | return do_set_thread_area(¤t->thread, u_info); | |
81 | } | |
1da177e4 LT |
82 | |
83 | ||
84 | /* | |
85 | * Get the current Thread-Local Storage area: | |
86 | */ | |
87 | ||
1da177e4 LT |
88 | #define GET_LIMIT(desc) ( \ |
89 | ((desc)->a & 0x0ffff) | \ | |
90 | ((desc)->b & 0xf0000) ) | |
13abd0e5 | 91 | |
1da177e4 LT |
92 | #define GET_32BIT(desc) (((desc)->b >> 22) & 1) |
93 | #define GET_CONTENTS(desc) (((desc)->b >> 10) & 3) | |
94 | #define GET_WRITABLE(desc) (((desc)->b >> 9) & 1) | |
95 | #define GET_LIMIT_PAGES(desc) (((desc)->b >> 23) & 1) | |
96 | #define GET_PRESENT(desc) (((desc)->b >> 15) & 1) | |
97 | #define GET_USEABLE(desc) (((desc)->b >> 20) & 1) | |
98 | #define GET_LONGMODE(desc) (((desc)->b >> 21) & 1) | |
99 | ||
100 | int do_get_thread_area(struct thread_struct *t, struct user_desc __user *u_info) | |
101 | { | |
102 | struct user_desc info; | |
103 | struct n_desc_struct *desc; | |
104 | int idx; | |
105 | ||
106 | if (get_user(idx, &u_info->entry_number)) | |
107 | return -EFAULT; | |
108 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | |
109 | return -EINVAL; | |
110 | ||
111 | desc = ((struct n_desc_struct *)t->tls_array) + idx - GDT_ENTRY_TLS_MIN; | |
112 | ||
113 | memset(&info, 0, sizeof(struct user_desc)); | |
114 | info.entry_number = idx; | |
91394eb0 | 115 | info.base_addr = get_desc_base(desc); |
1da177e4 LT |
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 | info.lm = GET_LONGMODE(desc); | |
124 | ||
125 | if (copy_to_user(u_info, &info, sizeof(info))) | |
126 | return -EFAULT; | |
127 | return 0; | |
128 | } | |
129 | ||
130 | asmlinkage long sys32_get_thread_area(struct user_desc __user *u_info) | |
131 | { | |
132 | return do_get_thread_area(¤t->thread, u_info); | |
13abd0e5 | 133 | } |
1da177e4 LT |
134 | |
135 | ||
136 | int ia32_child_tls(struct task_struct *p, struct pt_regs *childregs) | |
137 | { | |
138 | struct n_desc_struct *desc; | |
139 | struct user_desc info; | |
140 | struct user_desc __user *cp; | |
141 | int idx; | |
13abd0e5 | 142 | |
1da177e4 LT |
143 | cp = (void __user *)childregs->rsi; |
144 | if (copy_from_user(&info, cp, sizeof(info))) | |
145 | return -EFAULT; | |
146 | if (LDT_empty(&info)) | |
147 | return -EINVAL; | |
13abd0e5 | 148 | |
1da177e4 LT |
149 | idx = info.entry_number; |
150 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | |
151 | return -EINVAL; | |
13abd0e5 | 152 | |
1da177e4 LT |
153 | desc = (struct n_desc_struct *)(p->thread.tls_array) + idx - GDT_ENTRY_TLS_MIN; |
154 | desc->a = LDT_entry_a(&info); | |
155 | desc->b = LDT_entry_b(&info); | |
156 | ||
157 | return 0; | |
158 | } |