~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/x86_64/kernel/ldt.c

Version: ~ [ linux-5.1-rc1 ] ~ [ linux-5.0.3 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.30 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.107 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.164 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.176 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.136 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.63 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp