ldt.c revision 4dbf7af6442a9a882855bed0d999659ac413e3ac
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 23static void flush_ldt(void *null) 24{ 25 if (current->active_mm) 26 load_LDT(¤t->active_mm->context); 27} 28#endif 29 30static 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 + 511) & (~511); 39 if (mincount * LDT_ENTRY_SIZE > PAGE_SIZE) 40 newldt = vmalloc(mincount * LDT_ENTRY_SIZE); 41 else 42 newldt = (void *)__get_free_page(GFP_KERNEL); 43 44 if (!newldt) 45 return -ENOMEM; 46 47 if (oldsize) 48 memcpy(newldt, pc->ldt, oldsize * LDT_ENTRY_SIZE); 49 oldldt = pc->ldt; 50 memset(newldt + oldsize * LDT_ENTRY_SIZE, 0, 51 (mincount - oldsize) * LDT_ENTRY_SIZE); 52 53#ifdef CONFIG_X86_64 54 /* CHECKME: Do we really need this ? */ 55 wmb(); 56#endif 57 pc->ldt = newldt; 58 wmb(); 59 pc->size = mincount; 60 wmb(); 61 62 if (reload) { 63#ifdef CONFIG_SMP 64 cpumask_t mask; 65 66 preempt_disable(); 67 load_LDT(pc); 68 mask = cpumask_of_cpu(smp_processor_id()); 69 if (!cpus_equal(current->mm->cpu_vm_mask, mask)) 70 smp_call_function(flush_ldt, NULL, 1, 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 85static 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 */ 99int 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 */ 120void 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 136static 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; 166error_return: 167 return err; 168} 169 170static 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 185static 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 ... */ 230install: 231 write_ldt_entry(mm->context.ldt, ldt_info.entry_number, &ldt); 232 error = 0; 233 234out_unlock: 235 mutex_unlock(&mm->context.lock); 236out: 237 return error; 238} 239 240asmlinkage 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