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 */ 16static 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 */ 31int 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 78asmlinkage 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 105int 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 135asmlinkage 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 141int 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} 164