~ubuntu-branches/ubuntu/utopic/xen/utopic

« back to all changes in this revision

Viewing changes to xenolinux-2.4.25-sparse/arch/xeno/kernel/ldt.c

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2010-05-06 15:47:38 UTC
  • mto: (1.3.1) (15.1.1 sid) (4.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20100506154738-agoz0rlafrh1fnq7
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * linux/kernel/ldt.c
3
 
 *
4
 
 * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds
5
 
 * Copyright (C) 1999 Ingo Molnar <mingo@redhat.com>
6
 
 */
7
 
 
8
 
#include <linux/errno.h>
9
 
#include <linux/sched.h>
10
 
#include <linux/string.h>
11
 
#include <linux/mm.h>
12
 
#include <linux/smp.h>
13
 
#include <linux/smp_lock.h>
14
 
#include <linux/vmalloc.h>
15
 
#include <linux/slab.h>
16
 
 
17
 
#include <asm/uaccess.h>
18
 
#include <asm/system.h>
19
 
#include <asm/ldt.h>
20
 
#include <asm/desc.h>
21
 
 
22
 
#ifdef CONFIG_SMP /* avoids "defined but not used" warnig */
23
 
static void flush_ldt(void *mm)
24
 
{
25
 
        if (current->active_mm)
26
 
                load_LDT(&current->active_mm->context);
27
 
}
28
 
#endif
29
 
 
30
 
static int alloc_ldt(mm_context_t *pc, int mincount, int reload)
31
 
{
32
 
        void *oldldt;
33
 
        void *newldt;
34
 
        int oldsize;
35
 
 
36
 
        if (mincount <= pc->size)
37
 
                return 0;
38
 
        oldsize = pc->size;
39
 
        mincount = (mincount+511)&(~511);
40
 
        if (mincount*LDT_ENTRY_SIZE > PAGE_SIZE)
41
 
                newldt = vmalloc(mincount*LDT_ENTRY_SIZE);
42
 
        else
43
 
                newldt = kmalloc(mincount*LDT_ENTRY_SIZE, GFP_KERNEL);
44
 
 
45
 
        if (!newldt)
46
 
                return -ENOMEM;
47
 
 
48
 
        if (oldsize)
49
 
                memcpy(newldt, pc->ldt, oldsize*LDT_ENTRY_SIZE);
50
 
 
51
 
        oldldt = pc->ldt;
52
 
        memset(newldt+oldsize*LDT_ENTRY_SIZE, 0, (mincount-oldsize)*LDT_ENTRY_SIZE);
53
 
        wmb();
54
 
        pc->ldt = newldt;
55
 
        pc->size = mincount;
56
 
        if (reload) {
57
 
                make_pages_readonly(
58
 
                        pc->ldt,
59
 
                        (pc->size*LDT_ENTRY_SIZE)/PAGE_SIZE);
60
 
                load_LDT(pc);
61
 
                flush_page_update_queue();
62
 
#ifdef CONFIG_SMP
63
 
                if (current->mm->cpu_vm_mask != (1<<smp_processor_id()))
64
 
                        smp_call_function(flush_ldt, 0, 1, 1);
65
 
#endif
66
 
        }
67
 
        wmb();
68
 
        if (oldsize) {
69
 
                if (oldsize*LDT_ENTRY_SIZE > PAGE_SIZE)
70
 
                        vfree(oldldt);
71
 
                else
72
 
                        kfree(oldldt);
73
 
        }
74
 
        return 0;
75
 
}
76
 
 
77
 
static inline int copy_ldt(mm_context_t *new, mm_context_t *old)
78
 
{
79
 
        int err = alloc_ldt(new, old->size, 0);
80
 
        if (err < 0) {
81
 
                printk(KERN_WARNING "ldt allocation failed\n");
82
 
                new->size = 0;
83
 
                return err;
84
 
        }
85
 
        memcpy(new->ldt, old->ldt, old->size*LDT_ENTRY_SIZE);
86
 
        make_pages_readonly(new->ldt, (new->size*LDT_ENTRY_SIZE)/PAGE_SIZE);
87
 
        return 0;
88
 
}
89
 
 
90
 
/*
91
 
 * we do not have to muck with descriptors here, that is
92
 
 * done in switch_mm() as needed.
93
 
 */
94
 
int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
95
 
{
96
 
        struct mm_struct * old_mm;
97
 
        int retval = 0;
98
 
 
99
 
        init_MUTEX(&mm->context.sem);
100
 
        mm->context.size = 0;
101
 
        old_mm = current->mm;
102
 
        if (old_mm && old_mm->context.size > 0) {
103
 
                down(&old_mm->context.sem);
104
 
                retval = copy_ldt(&mm->context, &old_mm->context);
105
 
                up(&old_mm->context.sem);
106
 
        }
107
 
        return retval;
108
 
}
109
 
 
110
 
/*
111
 
 * No need to lock the MM as we are the last user
112
 
 * Do not touch the ldt register, we are already
113
 
 * in the next thread.
114
 
 */
115
 
void destroy_context(struct mm_struct *mm)
116
 
{
117
 
        if (mm->context.size) {
118
 
                make_pages_writeable(
119
 
                        mm->context.ldt, 
120
 
                        (mm->context.size*LDT_ENTRY_SIZE)/PAGE_SIZE);
121
 
                flush_page_update_queue();
122
 
                if (mm->context.size*LDT_ENTRY_SIZE > PAGE_SIZE)
123
 
                        vfree(mm->context.ldt);
124
 
                else
125
 
                        kfree(mm->context.ldt);
126
 
                mm->context.size = 0;
127
 
        }
128
 
}
129
 
 
130
 
static int read_ldt(void * ptr, unsigned long bytecount)
131
 
{
132
 
        int err;
133
 
        unsigned long size;
134
 
        struct mm_struct * mm = current->mm;
135
 
 
136
 
        if (!mm->context.size)
137
 
                return 0;
138
 
        if (bytecount > LDT_ENTRY_SIZE*LDT_ENTRIES)
139
 
                bytecount = LDT_ENTRY_SIZE*LDT_ENTRIES;
140
 
 
141
 
        down(&mm->context.sem);
142
 
        size = mm->context.size*LDT_ENTRY_SIZE;
143
 
        if (size > bytecount)
144
 
                size = bytecount;
145
 
 
146
 
        err = 0;
147
 
        if (copy_to_user(ptr, mm->context.ldt, size))
148
 
                err = -EFAULT;
149
 
        up(&mm->context.sem);
150
 
        if (err < 0)
151
 
                return err;
152
 
        if (size != bytecount) {
153
 
                /* zero-fill the rest */
154
 
                clear_user(ptr+size, bytecount-size);
155
 
        }
156
 
        return bytecount;
157
 
}
158
 
 
159
 
 
160
 
static int read_default_ldt(void * ptr, unsigned long bytecount)
161
 
{
162
 
    int err;
163
 
    unsigned long size;
164
 
    void *address;
165
 
 
166
 
    err = 0;
167
 
    address = &default_ldt[0];
168
 
    size = 5*sizeof(struct desc_struct);
169
 
    if (size > bytecount)
170
 
        size = bytecount;
171
 
 
172
 
    err = size;
173
 
    if (copy_to_user(ptr, address, size))
174
 
        err = -EFAULT;
175
 
 
176
 
    return err;
177
 
}
178
 
 
179
 
static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
180
 
{
181
 
    struct mm_struct * mm = current->mm;
182
 
    __u32 entry_1, entry_2, *lp;
183
 
    unsigned long phys_lp, max_limit;
184
 
    int error;
185
 
    struct modify_ldt_ldt_s ldt_info;
186
 
 
187
 
    error = -EINVAL;
188
 
    if (bytecount != sizeof(ldt_info))
189
 
        goto out;
190
 
    error = -EFAULT;    
191
 
    if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
192
 
        goto out;
193
 
 
194
 
    error = -EINVAL;
195
 
    if (ldt_info.entry_number >= LDT_ENTRIES)
196
 
        goto out;
197
 
    if (ldt_info.contents == 3) {
198
 
        if (oldmode)
199
 
            goto out;
200
 
        if (ldt_info.seg_not_present == 0)
201
 
            goto out;
202
 
    }
203
 
 
204
 
    /*
205
 
     * This makes our tests for overlap with Xen space easier. There's no good
206
 
     * reason to have a user segment starting this high anyway.
207
 
     */
208
 
    if (ldt_info.base_addr >= PAGE_OFFSET)
209
 
        goto out;
210
 
 
211
 
    down(&mm->context.sem);
212
 
    if (ldt_info.entry_number >= mm->context.size) {
213
 
      error = alloc_ldt(&current->mm->context, ldt_info.entry_number+1, 1);
214
 
      if (error < 0)
215
 
        goto out_unlock;
216
 
    }
217
 
 
218
 
 
219
 
    lp = (__u32 *)((ldt_info.entry_number<<3) + (char *)mm->context.ldt);
220
 
    phys_lp = arbitrary_virt_to_phys(lp);
221
 
 
222
 
    /* Allow LDTs to be cleared by the user. */
223
 
    if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
224
 
        if (oldmode ||
225
 
            (ldt_info.contents == 0             &&
226
 
             ldt_info.read_exec_only == 1       &&
227
 
             ldt_info.seg_32bit == 0            &&
228
 
             ldt_info.limit_in_pages == 0       &&
229
 
             ldt_info.seg_not_present == 1      &&
230
 
             ldt_info.useable == 0 )) {
231
 
            entry_1 = 0;
232
 
            entry_2 = 0;
233
 
            goto install;
234
 
        }
235
 
    }
236
 
 
237
 
    max_limit = HYPERVISOR_VIRT_START - ldt_info.base_addr;
238
 
    if ( ldt_info.limit_in_pages )
239
 
        max_limit >>= PAGE_SHIFT;
240
 
    max_limit--;
241
 
    if ( (ldt_info.limit & 0xfffff) > (max_limit & 0xfffff) )
242
 
        ldt_info.limit = max_limit;
243
 
 
244
 
    entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
245
 
        (ldt_info.limit & 0x0ffff);
246
 
    entry_2 = (ldt_info.base_addr & 0xff000000) |
247
 
        ((ldt_info.base_addr & 0x00ff0000) >> 16) |
248
 
        (ldt_info.limit & 0xf0000) |
249
 
        ((ldt_info.read_exec_only ^ 1) << 9) |
250
 
        (ldt_info.contents << 10) |
251
 
        ((ldt_info.seg_not_present ^ 1) << 15) |
252
 
        (ldt_info.seg_32bit << 22) |
253
 
        (ldt_info.limit_in_pages << 23) |
254
 
        0x7000;
255
 
    if (!oldmode)
256
 
        entry_2 |= (ldt_info.useable << 20);
257
 
 
258
 
    /* Install the new entry ...  */
259
 
 install:
260
 
    error = HYPERVISOR_update_descriptor(phys_lp, entry_1, entry_2);
261
 
 
262
 
 out_unlock:
263
 
    up(&mm->context.sem);
264
 
 out:
265
 
    return error;
266
 
}
267
 
 
268
 
asmlinkage int sys_modify_ldt(int func, void *ptr, unsigned long bytecount)
269
 
{
270
 
    int ret = -ENOSYS;
271
 
 
272
 
    switch (func) {
273
 
    case 0:
274
 
        ret = read_ldt(ptr, bytecount);
275
 
        break;
276
 
    case 1:
277
 
        ret = write_ldt(ptr, bytecount, 1);
278
 
        break;
279
 
    case 2:
280
 
        ret = read_default_ldt(ptr, bytecount);
281
 
        break;
282
 
    case 0x11:
283
 
        ret = write_ldt(ptr, bytecount, 0);
284
 
        break;
285
 
    }
286
 
    return ret;
287
 
}