linux/arch/um/kernel/skas/uaccess.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
   3 * Licensed under the GPL
   4 */
   5
   6#include <linux/err.h>
   7#include <linux/highmem.h>
   8#include <linux/mm.h>
   9#include <linux/module.h>
  10#include <linux/sched.h>
  11#include <asm/current.h>
  12#include <asm/page.h>
  13#include <asm/pgtable.h>
  14#include <kern_util.h>
  15#include <os.h>
  16
  17pte_t *virt_to_pte(struct mm_struct *mm, unsigned long addr)
  18{
  19        pgd_t *pgd;
  20        pud_t *pud;
  21        pmd_t *pmd;
  22
  23        if (mm == NULL)
  24                return NULL;
  25
  26        pgd = pgd_offset(mm, addr);
  27        if (!pgd_present(*pgd))
  28                return NULL;
  29
  30        pud = pud_offset(pgd, addr);
  31        if (!pud_present(*pud))
  32                return NULL;
  33
  34        pmd = pmd_offset(pud, addr);
  35        if (!pmd_present(*pmd))
  36                return NULL;
  37
  38        return pte_offset_kernel(pmd, addr);
  39}
  40
  41static pte_t *maybe_map(unsigned long virt, int is_write)
  42{
  43        pte_t *pte = virt_to_pte(current->mm, virt);
  44        int err, dummy_code;
  45
  46        if ((pte == NULL) || !pte_present(*pte) ||
  47            (is_write && !pte_write(*pte))) {
  48                err = handle_page_fault(virt, 0, is_write, 1, &dummy_code);
  49                if (err)
  50                        return NULL;
  51                pte = virt_to_pte(current->mm, virt);
  52        }
  53        if (!pte_present(*pte))
  54                pte = NULL;
  55
  56        return pte;
  57}
  58
  59static int do_op_one_page(unsigned long addr, int len, int is_write,
  60                 int (*op)(unsigned long addr, int len, void *arg), void *arg)
  61{
  62        jmp_buf buf;
  63        struct page *page;
  64        pte_t *pte;
  65        int n, faulted;
  66
  67        pte = maybe_map(addr, is_write);
  68        if (pte == NULL)
  69                return -1;
  70
  71        page = pte_page(*pte);
  72        addr = (unsigned long) kmap_atomic(page) +
  73                (addr & ~PAGE_MASK);
  74
  75        current->thread.fault_catcher = &buf;
  76
  77        faulted = UML_SETJMP(&buf);
  78        if (faulted == 0)
  79                n = (*op)(addr, len, arg);
  80        else
  81                n = -1;
  82
  83        current->thread.fault_catcher = NULL;
  84
  85        kunmap_atomic((void *)addr);
  86
  87        return n;
  88}
  89
  90static int buffer_op(unsigned long addr, int len, int is_write,
  91                     int (*op)(unsigned long, int, void *), void *arg)
  92{
  93        int size, remain, n;
  94
  95        size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
  96        remain = len;
  97
  98        n = do_op_one_page(addr, size, is_write, op, arg);
  99        if (n != 0) {
 100                remain = (n < 0 ? remain : 0);
 101                goto out;
 102        }
 103
 104        addr += size;
 105        remain -= size;
 106        if (remain == 0)
 107                goto out;
 108
 109        while (addr < ((addr + remain) & PAGE_MASK)) {
 110                n = do_op_one_page(addr, PAGE_SIZE, is_write, op, arg);
 111                if (n != 0) {
 112                        remain = (n < 0 ? remain : 0);
 113                        goto out;
 114                }
 115
 116                addr += PAGE_SIZE;
 117                remain -= PAGE_SIZE;
 118        }
 119        if (remain == 0)
 120                goto out;
 121
 122        n = do_op_one_page(addr, remain, is_write, op, arg);
 123        if (n != 0) {
 124                remain = (n < 0 ? remain : 0);
 125                goto out;
 126        }
 127
 128        return 0;
 129 out:
 130        return remain;
 131}
 132
 133static int copy_chunk_from_user(unsigned long from, int len, void *arg)
 134{
 135        unsigned long *to_ptr = arg, to = *to_ptr;
 136
 137        memcpy((void *) to, (void *) from, len);
 138        *to_ptr += len;
 139        return 0;
 140}
 141
 142int copy_from_user(void *to, const void __user *from, int n)
 143{
 144        if (segment_eq(get_fs(), KERNEL_DS)) {
 145                memcpy(to, (__force void*)from, n);
 146                return 0;
 147        }
 148
 149        return access_ok(VERIFY_READ, from, n) ?
 150               buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to):
 151               n;
 152}
 153EXPORT_SYMBOL(copy_from_user);
 154
 155static int copy_chunk_to_user(unsigned long to, int len, void *arg)
 156{
 157        unsigned long *from_ptr = arg, from = *from_ptr;
 158
 159        memcpy((void *) to, (void *) from, len);
 160        *from_ptr += len;
 161        return 0;
 162}
 163
 164int copy_to_user(void __user *to, const void *from, int n)
 165{
 166        if (segment_eq(get_fs(), KERNEL_DS)) {
 167                memcpy((__force void *) to, from, n);
 168                return 0;
 169        }
 170
 171        return access_ok(VERIFY_WRITE, to, n) ?
 172               buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) :
 173               n;
 174}
 175EXPORT_SYMBOL(copy_to_user);
 176
 177static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
 178{
 179        char **to_ptr = arg, *to = *to_ptr;
 180        int n;
 181
 182        strncpy(to, (void *) from, len);
 183        n = strnlen(to, len);
 184        *to_ptr += n;
 185
 186        if (n < len)
 187                return 1;
 188        return 0;
 189}
 190
 191int strncpy_from_user(char *dst, const char __user *src, int count)
 192{
 193        int n;
 194        char *ptr = dst;
 195
 196        if (segment_eq(get_fs(), KERNEL_DS)) {
 197                strncpy(dst, (__force void *) src, count);
 198                return strnlen(dst, count);
 199        }
 200
 201        if (!access_ok(VERIFY_READ, src, 1))
 202                return -EFAULT;
 203
 204        n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user,
 205                      &ptr);
 206        if (n != 0)
 207                return -EFAULT;
 208        return strnlen(dst, count);
 209}
 210EXPORT_SYMBOL(strncpy_from_user);
 211
 212static int clear_chunk(unsigned long addr, int len, void *unused)
 213{
 214        memset((void *) addr, 0, len);
 215        return 0;
 216}
 217
 218int __clear_user(void __user *mem, int len)
 219{
 220        return buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL);
 221}
 222
 223int clear_user(void __user *mem, int len)
 224{
 225        if (segment_eq(get_fs(), KERNEL_DS)) {
 226                memset((__force void*)mem, 0, len);
 227                return 0;
 228        }
 229
 230        return access_ok(VERIFY_WRITE, mem, len) ?
 231               buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len;
 232}
 233EXPORT_SYMBOL(clear_user);
 234
 235static int strnlen_chunk(unsigned long str, int len, void *arg)
 236{
 237        int *len_ptr = arg, n;
 238
 239        n = strnlen((void *) str, len);
 240        *len_ptr += n;
 241
 242        if (n < len)
 243                return 1;
 244        return 0;
 245}
 246
 247int strnlen_user(const void __user *str, int len)
 248{
 249        int count = 0, n;
 250
 251        if (segment_eq(get_fs(), KERNEL_DS))
 252                return strnlen((__force char*)str, len) + 1;
 253
 254        n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count);
 255        if (n == 0)
 256                return count + 1;
 257        return 0;
 258}
 259EXPORT_SYMBOL(strnlen_user);
 260