1 /* 2 * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds 3 * Copyright (C) 1999 Ingo Molnar <mingo@redhat.com> 4 * Copyright (C) 2002 Andi Kleen 5 * 6 * This handles calls from both 32bit and 64bit mode. 7 */ 8 9 #include <linux/errno.h> 10 #include <linux/sched.h> 11 #include <linux/string.h> 12 #include <linux/mm.h> 13 #include <linux/smp.h> 14 #include <linux/vmalloc.h> 15 16 #include <asm/uaccess.h> 17 #include <asm/system.h> 18 #include <asm/ldt.h> 19 #include <asm/desc.h> 20 #include <asm/mmu_context.h> 21 22 #ifdef CONFIG_SMP 23 static void flush_ldt(void *current_mm) 24 { 25 if (current->active_mm == current_mm) 26 load_LDT(¤t->active_mm->context); 27 } 28 #endif 29 30 static int alloc_ldt(mm_context_t *pc, int mincount, int reload) 31 { 32 void *oldldt, *newldt; 33 int oldsize; 34 35 if (mincount <= pc->size) 36 return 0; 37 oldsize = pc->size; 38 mincount = (mincount + (PAGE_SIZE / LDT_ENTRY_SIZE - 1)) & 39 (~(PAGE_SIZE / LDT_ENTRY_SIZE - 1)); 40 if (mincount * LDT_ENTRY_SIZE > PAGE_SIZE) 41 newldt = vmalloc(mincount * LDT_ENTRY_SIZE); 42 else 43 newldt = (void *)__get_free_page(GFP_KERNEL); 44 45 if (!newldt) 46 return -ENOMEM; 47 48 if (oldsize) 49 memcpy(newldt, pc->ldt, oldsize * LDT_ENTRY_SIZE); 50 oldldt = pc->ldt; 51 memset(newldt + oldsize * LDT_ENTRY_SIZE, 0, 52 (mincount - oldsize) * LDT_ENTRY_SIZE); 53 54 #ifdef CONFIG_X86_64 55 /* CHECKME: Do we really need this ? */ 56 wmb(); 57 #endif 58 pc->ldt = newldt; 59 wmb(); 60 pc->size = mincount; 61 wmb(); 62 63 if (reload) { 64 #ifdef CONFIG_SMP 65 cpumask_of_cpu_ptr_declare(mask); 66 67 preempt_disable(); 68 load_LDT(pc); 69 cpumask_of_cpu_ptr_next(mask, smp_processor_id()); 70 if (!cpus_equal(current->mm->cpu_vm_mask, *mask)) 71 smp_call_function(flush_ldt, current->mm, 1); 72 preempt_enable(); 73 #else 74 load_LDT(pc); 75 #endif 76 } 77 if (oldsize) { 78 if (oldsize * LDT_ENTRY_SIZE > PAGE_SIZE) 79 vfree(oldldt); 80 else 81 put_page(virt_to_page(oldldt)); 82 } 83 return 0; 84 } 85 86 static inline int copy_ldt(mm_context_t *new, mm_context_t *old) 87 { 88 int err = alloc_ldt(new, old->size, 0); 89 90 if (err < 0) 91 return err; 92 memcpy(new->ldt, old->ldt, old->size * LDT_ENTRY_SIZE); 93 return 0; 94 } 95 96 /* 97 * we do not have to muck with descriptors here, that is 98 * done in switch_mm() as needed. 99 */ 100 int init_new_context(struct task_struct *tsk, struct mm_struct *mm) 101 { 102 struct mm_struct *old_mm; 103 int retval = 0; 104 105 mutex_init(&mm->context.lock); 106 mm->context.size = 0; 107 old_mm = current->mm; 108 if (old_mm && old_mm->context.size > 0) { 109 mutex_lock(&old_mm->context.lock); 110 retval = copy_ldt(&mm->context, &old_mm->context); 111 mutex_unlock(&old_mm->context.lock); 112 } 113 return retval; 114 } 115 116 /* 117 * No need to lock the MM as we are the last user 118 * 119 * 64bit: Don't touch the LDT register - we're already in the next thread. 120 */ 121 void destroy_context(struct mm_struct *mm) 122 { 123 if (mm->context.size) { 124 #ifdef CONFIG_X86_32 125 /* CHECKME: Can this ever happen ? */ 126 if (mm == current->active_mm) 127 clear_LDT(); 128 #endif 129 if (mm->context.size * LDT_ENTRY_SIZE > PAGE_SIZE) 130 vfree(mm->context.ldt); 131 else 132 put_page(virt_to_page(mm->context.ldt)); 133 mm->context.size = 0; 134 } 135 } 136 137 static int read_ldt(void __user *ptr, unsigned long bytecount) 138 { 139 int err; 140 unsigned long size; 141 struct mm_struct *mm = current->mm; 142 143 if (!mm->context.size) 144 return 0; 145 if (bytecount > LDT_ENTRY_SIZE * LDT_ENTRIES) 146 bytecount = LDT_ENTRY_SIZE * LDT_ENTRIES; 147 148 mutex_lock(&mm->context.lock); 149 size = mm->context.size * LDT_ENTRY_SIZE; 150 if (size > bytecount) 151 size = bytecount; 152 153 err = 0; 154 if (copy_to_user(ptr, mm->context.ldt, size)) 155 err = -EFAULT; 156 mutex_unlock(&mm->context.lock); 157 if (err < 0) 158 goto error_return; 159 if (size != bytecount) { 160 /* zero-fill the rest */ 161 if (clear_user(ptr + size, bytecount - size) != 0) { 162 err = -EFAULT; 163 goto error_return; 164 } 165 } 166 return bytecount; 167 error_return: 168 return err; 169 } 170 171 static int read_default_ldt(void __user *ptr, unsigned long bytecount) 172 { 173 /* CHECKME: Can we use _one_ random number ? */ 174 #ifdef CONFIG_X86_32 175 unsigned long size = 5 * sizeof(struct desc_struct); 176 #else 177 unsigned long size = 128; 178 #endif 179 if (bytecount > size) 180 bytecount = size; 181 if (clear_user(ptr, bytecount)) 182 return -EFAULT; 183 return bytecount; 184 } 185 186 static int write_ldt(void __user *ptr, unsigned long bytecount, int oldmode) 187 { 188 struct mm_struct *mm = current->mm; 189 struct desc_struct ldt; 190 int error; 191 struct user_desc ldt_info; 192 193 error = -EINVAL; 194 if (bytecount != sizeof(ldt_info)) 195 goto out; 196 error = -EFAULT; 197 if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info))) 198 goto out; 199 200 error = -EINVAL; 201 if (ldt_info.entry_number >= LDT_ENTRIES) 202 goto out; 203 if (ldt_info.contents == 3) { 204 if (oldmode) 205 goto out; 206 if (ldt_info.seg_not_present == 0) 207 goto out; 208 } 209 210 mutex_lock(&mm->context.lock); 211 if (ldt_info.entry_number >= mm->context.size) { 212 error = alloc_ldt(¤t->mm->context, 213 ldt_info.entry_number + 1, 1); 214 if (error < 0) 215 goto out_unlock; 216 } 217 218 /* Allow LDTs to be cleared by the user. */ 219 if (ldt_info.base_addr == 0 && ldt_info.limit == 0) { 220 if (oldmode || LDT_empty(&ldt_info)) { 221 memset(&ldt, 0, sizeof(ldt)); 222 goto install; 223 } 224 } 225 226 fill_ldt(&ldt, &ldt_info); 227 if (oldmode) 228 ldt.avl = 0; 229 230 /* Install the new entry ... */ 231 install: 232 write_ldt_entry(mm->context.ldt, ldt_info.entry_number, &ldt); 233 error = 0; 234 235 out_unlock: 236 mutex_unlock(&mm->context.lock); 237 out: 238 return error; 239 } 240 241 asmlinkage int sys_modify_ldt(int func, void __user *ptr, 242 unsigned long bytecount) 243 { 244 int ret = -ENOSYS; 245 246 switch (func) { 247 case 0: 248 ret = read_ldt(ptr, bytecount); 249 break; 250 case 1: 251 ret = write_ldt(ptr, bytecount, 1); 252 break; 253 case 2: 254 ret = read_default_ldt(ptr, bytecount); 255 break; 256 case 0x11: 257 ret = write_ldt(ptr, bytecount, 0); 258 break; 259 } 260 return ret; 261 } 262