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