169 строки
3.5 KiB
C
169 строки
3.5 KiB
C
/* MN10300 Userspace accessor functions
|
|
*
|
|
* Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
|
|
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
|
|
* Written by David Howells (dhowells@redhat.com)
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public Licence
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the Licence, or (at your option) any later version.
|
|
*/
|
|
#include <linux/uaccess.h>
|
|
|
|
unsigned long
|
|
__generic_copy_to_user(void *to, const void *from, unsigned long n)
|
|
{
|
|
if (access_ok(VERIFY_WRITE, to, n))
|
|
__copy_user(to, from, n);
|
|
return n;
|
|
}
|
|
|
|
unsigned long
|
|
__generic_copy_from_user(void *to, const void *from, unsigned long n)
|
|
{
|
|
if (access_ok(VERIFY_READ, from, n))
|
|
__copy_user_zeroing(to, from, n);
|
|
else
|
|
memset(to, 0, n);
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* Copy a null terminated string from userspace.
|
|
*/
|
|
#define __do_strncpy_from_user(dst, src, count, res) \
|
|
do { \
|
|
int w; \
|
|
asm volatile( \
|
|
" mov %1,%0\n" \
|
|
" cmp 0,%1\n" \
|
|
" beq 2f\n" \
|
|
"0:\n" \
|
|
" movbu (%5),%2\n" \
|
|
"1:\n" \
|
|
" movbu %2,(%6)\n" \
|
|
" inc %5\n" \
|
|
" inc %6\n" \
|
|
" cmp 0,%2\n" \
|
|
" beq 2f\n" \
|
|
" add -1,%1\n" \
|
|
" bne 0b\n" \
|
|
"2:\n" \
|
|
" sub %1,%0\n" \
|
|
"3:\n" \
|
|
" .section .fixup,\"ax\"\n" \
|
|
"4:\n" \
|
|
" mov %3,%0\n" \
|
|
" jmp 3b\n" \
|
|
" .previous\n" \
|
|
" .section __ex_table,\"a\"\n" \
|
|
" .balign 4\n" \
|
|
" .long 0b,4b\n" \
|
|
" .long 1b,4b\n" \
|
|
" .previous" \
|
|
:"=&r"(res), "=r"(count), "=&r"(w) \
|
|
:"i"(-EFAULT), "1"(count), "a"(src), "a"(dst) \
|
|
: "memory", "cc"); \
|
|
} while (0)
|
|
|
|
long
|
|
__strncpy_from_user(char *dst, const char *src, long count)
|
|
{
|
|
long res;
|
|
__do_strncpy_from_user(dst, src, count, res);
|
|
return res;
|
|
}
|
|
|
|
long
|
|
strncpy_from_user(char *dst, const char *src, long count)
|
|
{
|
|
long res = -EFAULT;
|
|
if (access_ok(VERIFY_READ, src, 1))
|
|
__do_strncpy_from_user(dst, src, count, res);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* Clear a userspace memory
|
|
*/
|
|
#define __do_clear_user(addr, size) \
|
|
do { \
|
|
int w; \
|
|
asm volatile( \
|
|
" cmp 0,%0\n" \
|
|
" beq 1f\n" \
|
|
" clr %1\n" \
|
|
"0: movbu %1,(%3,%2)\n" \
|
|
" inc %3\n" \
|
|
" cmp %0,%3\n" \
|
|
" bne 0b\n" \
|
|
"1:\n" \
|
|
" sub %3,%0\n" \
|
|
"2:\n" \
|
|
".section .fixup,\"ax\"\n" \
|
|
"3: jmp 2b\n" \
|
|
".previous\n" \
|
|
".section __ex_table,\"a\"\n" \
|
|
" .balign 4\n" \
|
|
" .long 0b,3b\n" \
|
|
".previous\n" \
|
|
: "+r"(size), "=&r"(w) \
|
|
: "a"(addr), "d"(0) \
|
|
: "memory", "cc"); \
|
|
} while (0)
|
|
|
|
unsigned long
|
|
__clear_user(void *to, unsigned long n)
|
|
{
|
|
__do_clear_user(to, n);
|
|
return n;
|
|
}
|
|
|
|
unsigned long
|
|
clear_user(void *to, unsigned long n)
|
|
{
|
|
if (access_ok(VERIFY_WRITE, to, n))
|
|
__do_clear_user(to, n);
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* Return the size of a string (including the ending 0)
|
|
*
|
|
* Return 0 on exception, a value greater than N if too long
|
|
*/
|
|
long strnlen_user(const char *s, long n)
|
|
{
|
|
unsigned long res, w;
|
|
|
|
if (!__addr_ok(s))
|
|
return 0;
|
|
|
|
if (n < 0 || n + (u_long) s > current_thread_info()->addr_limit.seg)
|
|
n = current_thread_info()->addr_limit.seg - (u_long)s;
|
|
|
|
asm volatile(
|
|
"0: cmp %4,%0\n"
|
|
" beq 2f\n"
|
|
"1: movbu (%0,%3),%1\n"
|
|
" inc %0\n"
|
|
" cmp 0,%1\n"
|
|
" beq 3f\n"
|
|
" bra 0b\n"
|
|
"2: clr %0\n"
|
|
"3:\n"
|
|
".section .fixup,\"ax\"\n"
|
|
"4: jmp 2b\n"
|
|
".previous\n"
|
|
".section __ex_table,\"a\"\n"
|
|
" .balign 4\n"
|
|
" .long 1b,4b\n"
|
|
".previous\n"
|
|
:"=d"(res), "=&r"(w)
|
|
:"0"(0), "a"(s), "r"(n)
|
|
: "memory", "cc");
|
|
return res;
|
|
}
|