linux/arch/x86/um/ldt.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
   3 * Licensed under the GPL
   4 */
   5
   6#include <linux/mm.h>
   7#include <linux/sched.h>
   8#include <linux/slab.h>
   9#include <linux/uaccess.h>
  10#include <asm/unistd.h>
  11#include <os.h>
  12#include <skas.h>
  13#include <sysdep/tls.h>
  14
  15static inline int modify_ldt (int func, void *ptr, unsigned long bytecount)
  16{
  17        return syscall(__NR_modify_ldt, func, ptr, bytecount);
  18}
  19
  20static long write_ldt_entry(struct mm_id *mm_idp, int func,
  21                     struct user_desc *desc, void **addr, int done)
  22{
  23        long res;
  24        void *stub_addr;
  25        res = syscall_stub_data(mm_idp, (unsigned long *)desc,
  26                                (sizeof(*desc) + sizeof(long) - 1) &
  27                                    ~(sizeof(long) - 1),
  28                                addr, &stub_addr);
  29        if (!res) {
  30                unsigned long args[] = { func,
  31                                         (unsigned long)stub_addr,
  32                                         sizeof(*desc),
  33                                         0, 0, 0 };
  34                res = run_syscall_stub(mm_idp, __NR_modify_ldt, args,
  35                                       0, addr, done);
  36        }
  37
  38        return res;
  39}
  40
  41/*
  42 * In skas mode, we hold our own ldt data in UML.
  43 * Thus, the code implementing sys_modify_ldt_skas
  44 * is very similar to (and mostly stolen from) sys_modify_ldt
  45 * for arch/i386/kernel/ldt.c
  46 * The routines copied and modified in part are:
  47 * - read_ldt
  48 * - read_default_ldt
  49 * - write_ldt
  50 * - sys_modify_ldt_skas
  51 */
  52
  53static int read_ldt(void __user * ptr, unsigned long bytecount)
  54{
  55        int i, err = 0;
  56        unsigned long size;
  57        uml_ldt_t *ldt = &current->mm->context.arch.ldt;
  58
  59        if (!ldt->entry_count)
  60                goto out;
  61        if (bytecount > LDT_ENTRY_SIZE*LDT_ENTRIES)
  62                bytecount = LDT_ENTRY_SIZE*LDT_ENTRIES;
  63        err = bytecount;
  64
  65        mutex_lock(&ldt->lock);
  66        if (ldt->entry_count <= LDT_DIRECT_ENTRIES) {
  67                size = LDT_ENTRY_SIZE*LDT_DIRECT_ENTRIES;
  68                if (size > bytecount)
  69                        size = bytecount;
  70                if (copy_to_user(ptr, ldt->u.entries, size))
  71                        err = -EFAULT;
  72                bytecount -= size;
  73                ptr += size;
  74        }
  75        else {
  76                for (i=0; i<ldt->entry_count/LDT_ENTRIES_PER_PAGE && bytecount;
  77                     i++) {
  78                        size = PAGE_SIZE;
  79                        if (size > bytecount)
  80                                size = bytecount;
  81                        if (copy_to_user(ptr, ldt->u.pages[i], size)) {
  82                                err = -EFAULT;
  83                                break;
  84                        }
  85                        bytecount -= size;
  86                        ptr += size;
  87                }
  88        }
  89        mutex_unlock(&ldt->lock);
  90
  91        if (bytecount == 0 || err == -EFAULT)
  92                goto out;
  93
  94        if (clear_user(ptr, bytecount))
  95                err = -EFAULT;
  96
  97out:
  98        return err;
  99}
 100
 101static int read_default_ldt(void __user * ptr, unsigned long bytecount)
 102{
 103        int err;
 104
 105        if (bytecount > 5*LDT_ENTRY_SIZE)
 106                bytecount = 5*LDT_ENTRY_SIZE;
 107
 108        err = bytecount;
 109        /*
 110         * UML doesn't support lcall7 and lcall27.
 111         * So, we don't really have a default ldt, but emulate
 112         * an empty ldt of common host default ldt size.
 113         */
 114        if (clear_user(ptr, bytecount))
 115                err = -EFAULT;
 116
 117        return err;
 118}
 119
 120static int write_ldt(void __user * ptr, unsigned long bytecount, int func)
 121{
 122        uml_ldt_t *ldt = &current->mm->context.arch.ldt;
 123        struct mm_id * mm_idp = &current->mm->context.id;
 124        int i, err;
 125        struct user_desc ldt_info;
 126        struct ldt_entry entry0, *ldt_p;
 127        void *addr = NULL;
 128
 129        err = -EINVAL;
 130        if (bytecount != sizeof(ldt_info))
 131                goto out;
 132        err = -EFAULT;
 133        if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
 134                goto out;
 135
 136        err = -EINVAL;
 137        if (ldt_info.entry_number >= LDT_ENTRIES)
 138                goto out;
 139        if (ldt_info.contents == 3) {
 140                if (func == 1)
 141                        goto out;
 142                if (ldt_info.seg_not_present == 0)
 143                        goto out;
 144        }
 145
 146        mutex_lock(&ldt->lock);
 147
 148        err = write_ldt_entry(mm_idp, func, &ldt_info, &addr, 1);
 149        if (err)
 150                goto out_unlock;
 151
 152        if (ldt_info.entry_number >= ldt->entry_count &&
 153            ldt_info.entry_number >= LDT_DIRECT_ENTRIES) {
 154                for (i=ldt->entry_count/LDT_ENTRIES_PER_PAGE;
 155                     i*LDT_ENTRIES_PER_PAGE <= ldt_info.entry_number;
 156                     i++) {
 157                        if (i == 0)
 158                                memcpy(&entry0, ldt->u.entries,
 159                                       sizeof(entry0));
 160                        ldt->u.pages[i] = (struct ldt_entry *)
 161                                __get_free_page(GFP_KERNEL|__GFP_ZERO);
 162                        if (!ldt->u.pages[i]) {
 163                                err = -ENOMEM;
 164                                /* Undo the change in host */
 165                                memset(&ldt_info, 0, sizeof(ldt_info));
 166                                write_ldt_entry(mm_idp, 1, &ldt_info, &addr, 1);
 167                                goto out_unlock;
 168                        }
 169                        if (i == 0) {
 170                                memcpy(ldt->u.pages[0], &entry0,
 171                                       sizeof(entry0));
 172                                memcpy(ldt->u.pages[0]+1, ldt->u.entries+1,
 173                                       sizeof(entry0)*(LDT_DIRECT_ENTRIES-1));
 174                        }
 175                        ldt->entry_count = (i + 1) * LDT_ENTRIES_PER_PAGE;
 176                }
 177        }
 178        if (ldt->entry_count <= ldt_info.entry_number)
 179                ldt->entry_count = ldt_info.entry_number + 1;
 180
 181        if (ldt->entry_count <= LDT_DIRECT_ENTRIES)
 182                ldt_p = ldt->u.entries + ldt_info.entry_number;
 183        else
 184                ldt_p = ldt->u.pages[ldt_info.entry_number/LDT_ENTRIES_PER_PAGE] +
 185                        ldt_info.entry_number%LDT_ENTRIES_PER_PAGE;
 186
 187        if (ldt_info.base_addr == 0 && ldt_info.limit == 0 &&
 188           (func == 1 || LDT_empty(&ldt_info))) {
 189                ldt_p->a = 0;
 190                ldt_p->b = 0;
 191        }
 192        else{
 193                if (func == 1)
 194                        ldt_info.useable = 0;
 195                ldt_p->a = LDT_entry_a(&ldt_info);
 196                ldt_p->b = LDT_entry_b(&ldt_info);
 197        }
 198        err = 0;
 199
 200out_unlock:
 201        mutex_unlock(&ldt->lock);
 202out:
 203        return err;
 204}
 205
 206static long do_modify_ldt_skas(int func, void __user *ptr,
 207                               unsigned long bytecount)
 208{
 209        int ret = -ENOSYS;
 210
 211        switch (func) {
 212                case 0:
 213                        ret = read_ldt(ptr, bytecount);
 214                        break;
 215                case 1:
 216                case 0x11:
 217                        ret = write_ldt(ptr, bytecount, func);
 218                        break;
 219                case 2:
 220                        ret = read_default_ldt(ptr, bytecount);
 221                        break;
 222        }
 223        return ret;
 224}
 225
 226static DEFINE_SPINLOCK(host_ldt_lock);
 227static short dummy_list[9] = {0, -1};
 228static short * host_ldt_entries = NULL;
 229
 230static void ldt_get_host_info(void)
 231{
 232        long ret;
 233        struct ldt_entry * ldt;
 234        short *tmp;
 235        int i, size, k, order;
 236
 237        spin_lock(&host_ldt_lock);
 238
 239        if (host_ldt_entries != NULL) {
 240                spin_unlock(&host_ldt_lock);
 241                return;
 242        }
 243        host_ldt_entries = dummy_list+1;
 244
 245        spin_unlock(&host_ldt_lock);
 246
 247        for (i = LDT_PAGES_MAX-1, order=0; i; i>>=1, order++)
 248                ;
 249
 250        ldt = (struct ldt_entry *)
 251              __get_free_pages(GFP_KERNEL|__GFP_ZERO, order);
 252        if (ldt == NULL) {
 253                printk(KERN_ERR "ldt_get_host_info: couldn't allocate buffer "
 254                       "for host ldt\n");
 255                return;
 256        }
 257
 258        ret = modify_ldt(0, ldt, (1<<order)*PAGE_SIZE);
 259        if (ret < 0) {
 260                printk(KERN_ERR "ldt_get_host_info: couldn't read host ldt\n");
 261                goto out_free;
 262        }
 263        if (ret == 0) {
 264                /* default_ldt is active, simply write an empty entry 0 */
 265                host_ldt_entries = dummy_list;
 266                goto out_free;
 267        }
 268
 269        for (i=0, size=0; i<ret/LDT_ENTRY_SIZE; i++) {
 270                if (ldt[i].a != 0 || ldt[i].b != 0)
 271                        size++;
 272        }
 273
 274        if (size < ARRAY_SIZE(dummy_list))
 275                host_ldt_entries = dummy_list;
 276        else {
 277                size = (size + 1) * sizeof(dummy_list[0]);
 278                tmp = kmalloc(size, GFP_KERNEL);
 279                if (tmp == NULL) {
 280                        printk(KERN_ERR "ldt_get_host_info: couldn't allocate "
 281                               "host ldt list\n");
 282                        goto out_free;
 283                }
 284                host_ldt_entries = tmp;
 285        }
 286
 287        for (i=0, k=0; i<ret/LDT_ENTRY_SIZE; i++) {
 288                if (ldt[i].a != 0 || ldt[i].b != 0)
 289                        host_ldt_entries[k++] = i;
 290        }
 291        host_ldt_entries[k] = -1;
 292
 293out_free:
 294        free_pages((unsigned long)ldt, order);
 295}
 296
 297long init_new_ldt(struct mm_context *new_mm, struct mm_context *from_mm)
 298{
 299        struct user_desc desc;
 300        short * num_p;
 301        int i;
 302        long page, err=0;
 303        void *addr = NULL;
 304
 305
 306        mutex_init(&new_mm->arch.ldt.lock);
 307
 308        if (!from_mm) {
 309                memset(&desc, 0, sizeof(desc));
 310                /*
 311                 * Now we try to retrieve info about the ldt, we
 312                 * inherited from the host. All ldt-entries found
 313                 * will be reset in the following loop
 314                 */
 315                ldt_get_host_info();
 316                for (num_p=host_ldt_entries; *num_p != -1; num_p++) {
 317                        desc.entry_number = *num_p;
 318                        err = write_ldt_entry(&new_mm->id, 1, &desc,
 319                                              &addr, *(num_p + 1) == -1);
 320                        if (err)
 321                                break;
 322                }
 323                new_mm->arch.ldt.entry_count = 0;
 324
 325                goto out;
 326        }
 327
 328        /*
 329         * Our local LDT is used to supply the data for
 330         * modify_ldt(READLDT), if PTRACE_LDT isn't available,
 331         * i.e., we have to use the stub for modify_ldt, which
 332         * can't handle the big read buffer of up to 64kB.
 333         */
 334        mutex_lock(&from_mm->arch.ldt.lock);
 335        if (from_mm->arch.ldt.entry_count <= LDT_DIRECT_ENTRIES)
 336                memcpy(new_mm->arch.ldt.u.entries, from_mm->arch.ldt.u.entries,
 337                       sizeof(new_mm->arch.ldt.u.entries));
 338        else {
 339                i = from_mm->arch.ldt.entry_count / LDT_ENTRIES_PER_PAGE;
 340                while (i-->0) {
 341                        page = __get_free_page(GFP_KERNEL|__GFP_ZERO);
 342                        if (!page) {
 343                                err = -ENOMEM;
 344                                break;
 345                        }
 346                        new_mm->arch.ldt.u.pages[i] =
 347                                (struct ldt_entry *) page;
 348                        memcpy(new_mm->arch.ldt.u.pages[i],
 349                               from_mm->arch.ldt.u.pages[i], PAGE_SIZE);
 350                }
 351        }
 352        new_mm->arch.ldt.entry_count = from_mm->arch.ldt.entry_count;
 353        mutex_unlock(&from_mm->arch.ldt.lock);
 354
 355    out:
 356        return err;
 357}
 358
 359
 360void free_ldt(struct mm_context *mm)
 361{
 362        int i;
 363
 364        if (mm->arch.ldt.entry_count > LDT_DIRECT_ENTRIES) {
 365                i = mm->arch.ldt.entry_count / LDT_ENTRIES_PER_PAGE;
 366                while (i-- > 0)
 367                        free_page((long) mm->arch.ldt.u.pages[i]);
 368        }
 369        mm->arch.ldt.entry_count = 0;
 370}
 371
 372int sys_modify_ldt(int func, void __user *ptr, unsigned long bytecount)
 373{
 374        return do_modify_ldt_skas(func, ptr, bytecount);
 375}
 376