import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / arch / x86_64 / kernel / ldt.c
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  * Manage the local descriptor table for user processes.
9  * This handles calls from both 32bit and 64bit mode.
10  */
11
12 #include <linux/errno.h>
13 #include <linux/sched.h>
14 #include <linux/string.h>
15 #include <linux/mm.h>
16 #include <linux/smp.h>
17 #include <linux/smp_lock.h>
18 #include <linux/vmalloc.h>
19
20 #include <asm/uaccess.h>
21 #include <asm/system.h>
22 #include <asm/ldt.h>
23 #include <asm/desc.h>
24
25 /*
26  * read_ldt() is not really atomic - this is not a problem since
27  * synchronization of reads and writes done to the LDT has to be
28  * assured by user-space anyway. Writes are atomic, to protect
29  * the security checks done on new descriptors.
30  */
31 static int read_ldt(void * ptr, unsigned long bytecount)
32 {
33         int err;
34         unsigned long size;
35         struct mm_struct * mm = current->mm;
36
37         err = 0;
38         if (!mm->context.segments)
39                 goto out;
40
41         size = LDT_ENTRIES*LDT_ENTRY_SIZE;
42         if (size > bytecount)
43                 size = bytecount;
44
45         err = size;
46         if (copy_to_user(ptr, mm->context.segments, size))
47                 err = -EFAULT;
48 out:
49         return err;
50 }
51
52 static int read_default_ldt(void * ptr, unsigned long bytecount)
53 {
54         /* Arbitary number */ 
55         if (bytecount > 128) 
56                 bytecount = 128;        
57         if (clear_user(ptr, bytecount))
58                 return -EFAULT;
59         return bytecount; 
60 }
61
62 static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
63 {
64         struct task_struct *me = current;
65         struct mm_struct * mm = me->mm;
66         __u32 entry_1, entry_2, *lp;
67         int error;
68         struct modify_ldt_ldt_s ldt_info;
69
70         error = -EINVAL;
71
72         if (bytecount != sizeof(ldt_info))
73                 goto out;
74         error = -EFAULT;        
75         if (copy_from_user(&ldt_info, ptr, bytecount))
76                 goto out;
77
78         error = -EINVAL;
79         if (ldt_info.entry_number >= LDT_ENTRIES)
80                 goto out;
81         if (ldt_info.contents == 3) {
82                 if (oldmode)
83                         goto out;
84                 if (ldt_info.seg_not_present == 0)
85                         goto out;
86         }
87
88         /*
89          * the GDT index of the LDT is allocated dynamically, and is
90          * limited by MAX_LDT_DESCRIPTORS.
91          */
92         down_write(&mm->mmap_sem);
93         if (!mm->context.segments) {
94                 void * segments = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
95                 error = -ENOMEM;
96                 if (!segments)
97                         goto out_unlock;
98                 memset(segments, 0, LDT_ENTRIES*LDT_ENTRY_SIZE);
99                 wmb();
100                 mm->context.segments = segments;
101                 mm->context.cpuvalid = 1UL << smp_processor_id();
102                 load_LDT(mm);
103         }
104
105         lp = (__u32 *) ((ldt_info.entry_number << 3) + (char *) mm->context.segments);
106
107         /* Allow LDTs to be cleared by the user. */
108         if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
109                 if (oldmode ||
110                     (ldt_info.contents == 0             &&
111                      ldt_info.read_exec_only == 1       &&
112                      ldt_info.seg_32bit == 0            &&
113                      ldt_info.limit_in_pages == 0       &&
114                      ldt_info.seg_not_present == 1      &&
115                      ldt_info.useable == 0 && 
116                      ldt_info.lm == 0)) {
117                         entry_1 = 0;
118                         entry_2 = 0;
119                         goto install;
120                 }
121         }
122
123         entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
124                   (ldt_info.limit & 0x0ffff);
125         entry_2 = (ldt_info.base_addr & 0xff000000) |
126                   ((ldt_info.base_addr & 0x00ff0000) >> 16) |
127                   (ldt_info.limit & 0xf0000) |
128                   ((ldt_info.read_exec_only ^ 1) << 9) |
129                   (ldt_info.contents << 10) |
130                   ((ldt_info.seg_not_present ^ 1) << 15) |
131                   (ldt_info.seg_32bit << 22) |
132                   (ldt_info.limit_in_pages << 23) |
133                   (ldt_info.lm << 21) |
134                   0x7000;
135         if (!oldmode)
136                 entry_2 |= (ldt_info.useable << 20);
137
138         /* Install the new entry ...  */
139 install:
140         *lp     = entry_1;
141         *(lp+1) = entry_2;
142         error = 0;
143
144 out_unlock:
145         up_write(&mm->mmap_sem);
146 out:
147         return error;
148 }
149
150 asmlinkage long sys_modify_ldt(int func, void *ptr, unsigned long bytecount)
151 {
152         int ret = -ENOSYS;
153
154         switch (func) {
155         case 0:
156                 ret = read_ldt(ptr, bytecount);
157                 break;
158         case 1:
159                 ret = write_ldt(ptr, bytecount, 1);
160                 break;
161         case 2:
162                 ret = read_default_ldt(ptr, bytecount);
163                 break;
164         case 0x11:
165                 ret = write_ldt(ptr, bytecount, 0);
166                 break;
167         }
168         return ret;
169 }