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