blob: 861a11b4f70aa8f2758b5a2ec22e35c007aa01d2 [file] [log] [blame]
/*
* Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* vineetg: Apr 2011 - access_ok( ) simplification
* -Copied from IA-64
* -No need to seperately check for kernel/user segments
* -With addr_limit now having the exact last valid addr, it can simply
* be add < get_fs()
*
* vineetg: June 2010
* -__clear_user( ) called multiple times during elf load was byte loop
* converted to do as much word clear as possible.
*
* vineetg: Dec 2009
* -Hand crafted constant propagation for "constant" copy sizes
* -stock kernel shrunk by 33K at -O3
*
* vineetg: Sept 2009
* -Added option to (UN)inline copy_(to|from)_user to reduce code sz
* -kernel shrunk by 200K even at -O3
* -Enabled when doing -Os
*
* Amit Bhor, Sameer Dhavale: Codito Technologies 2004
*/
#ifndef _ASM_ARC_UACCESS_H
#define _ASM_ARC_UACCESS_H
#include <linux/sched.h>
#include <asm/errno.h>
#include <linux/string.h> /* for generic string functions */
#include <asm/mem.h>
#define VERIFY_READ 0
#define VERIFY_WRITE 1
//#define ARC_HIGH_LATENCY_MEMORY
/*
* The fs value determines whether argument validity checking should be
* performed or not. If get_fs() == USER_DS, checking is performed, with
* get_fs() == KERNEL_DS, checking is bypassed.
*
* For historical reasons, these macros are grossly misnamed.
*/
#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
/* addr_limit is the maximum accessible address for the task. we misuse
* the KERNEL_DS and USER_DS values to both assign and compare the
* addr_limit values through the equally misnamed get/set_fs macros.
* (see above)
*/
#define KERNEL_DS MAKE_MM_SEG(0)
#define USER_DS MAKE_MM_SEG(TASK_SIZE)
#define get_ds() (KERNEL_DS)
/* Sameer : addr_limit is now member of thread_info struct and NOT
of task_struct as was the case in the 2.4 days */
#define get_fs() (current_thread_info()->addr_limit)
#define set_fs(x) (current_thread_info()->addr_limit = (x))
#define segment_eq(a,b) ((a) == (b))
#define __kernel_ok (segment_eq(get_fs(), KERNEL_DS))
#define __user_ok(addr,size) (\
((size) <= TASK_SIZE) && \
(((addr)+(size)) <= get_fs()) \
)
#define __access_ok(addr,size) (unlikely(__kernel_ok) || likely(__user_ok((addr),(size))))
#define access_ok(type,addr,size) __access_ok((unsigned long)(addr),(size))
static inline int verify_area(int type, const void * addr, unsigned long size)
{
return access_ok(type,addr,size) ? 0 : -EFAULT;
}
/*
* The exception table consists of pairs of addresses: the first is the
* address of an instruction that is allowed to fault, and the second is
* the address at which the program should continue. No registers are
* modified, so it is entirely up to the continuation code to figure out
* what to do.
*
* All the routines below use bits of fixup code that are out of line
* with the main instruction path. This means when everything is well,
* we don't even have to jump over them. Further, they do not intrude
* on our cache or tlb entries.
*/
struct exception_table_entry
{
unsigned long insn, nextinsn;
};
/* Returns 0 if exception not found and fixup otherwise. */
extern unsigned long search_exception_table(unsigned long);
/*
* These are the main single-value transfer routines. They automatically
* use the right size if we just have the right pointer type.
*
* This gets kind of ugly. We want to return _two_ values in "get_user()"
* and yet we don't want to do any pointers, because that is too much
* of a performance impact. Thus we have a few rather ugly macros here,
* and hide all the uglyness from the user.
*
* The "__xxx" versions of the user access functions are versions that
* do not verify the address space, that must have been done previously
* with a separate "access_ok()" call (this is used when we do multiple
* accesses to the same area of user memory).
*
* As we use the same address space for kernel and user data on
* ARC, we can just do these as direct assignments. (Of course, the
* exception handling means that it's no longer "just"...)
*/
#define get_user(x,p) __get_user_check((x),(p),sizeof(*(p)))
#define __get_user(x,p) __get_user_nocheck((x),(p),sizeof(*(p)))
#define put_user(x,p) __put_user_check((__typeof(*(p)))(x),(p),sizeof(*(p)))
#define __put_user(x,p) __put_user_nocheck((__typeof(*(p)))(x),(p),sizeof(*(p)))
#define __get_user_check(x,ptr,size) \
({ \
long __gu_err = -EFAULT, __gu_val = 0; \
const __typeof__(*(ptr)) *__gu_addr = (ptr); \
if (access_ok(VERIFY_READ,__gu_addr,size)) { \
__gu_err = 0; \
__get_user_size(__gu_val,__gu_addr,(size),__gu_err); \
} \
(x) = (__typeof__(*(ptr)))__gu_val; \
__gu_err; \
})
#define __get_user_nocheck(x,ptr,size) \
({ \
long __gu_err = 0, __gu_val; \
__get_user_size(__gu_val,(ptr),(size),__gu_err); \
(x) = (__typeof__(*(ptr)))__gu_val; \
__gu_err; \
})
#define __put_user_check(x,ptr,size) \
({ \
long __pu_err = -EFAULT; \
__typeof__(*(ptr)) *__pu_addr = (ptr); \
if (access_ok(VERIFY_WRITE,__pu_addr,size)) { \
__pu_err = 0; \
__put_user_size((x),__pu_addr,(size),__pu_err); \
} \
__pu_err; \
})
#define __put_user_nocheck(x,ptr,size) \
({ \
long __pu_err = 0; \
__typeof__(*(ptr)) *__pu_addr = (ptr); \
__put_user_size((x),__pu_addr,(size),__pu_err); \
__pu_err; \
})
extern long __get_user_bad(void);
#define __get_user_size(x,ptr,size,retval) \
do { \
switch (size) { \
case 1: __get_user_asm(x,ptr,retval,"ldb"); break; \
case 2: __get_user_asm(x,ptr,retval,"ldw"); break; \
case 4: __get_user_asm(x,ptr,retval,"ld"); break; \
case 8: __get_user_asm_64(x,ptr,retval); break; \
default: (x) = __get_user_bad(); \
} \
} while (0)
// FIXME :: check if the "nop" is required
#define __get_user_asm(x,addr,err,op) \
__asm__ __volatile__( \
"1: "op" %1,[%2]\n" \
" mov %0, 0x0\n" \
"2: ;nop\n" \
" .section .fixup, \"ax\"\n" \
" .align 4\n" \
"3: mov %0, %3\n" \
" j 2b\n" \
" .previous\n" \
" .section __ex_table, \"a\"\n" \
" .align 4\n" \
" .word 1b,3b\n" \
" .previous\n" \
: "=r" (err), "=&r" (x) \
: "r" (addr), "i" (-EFAULT), "0" (err))
#define __get_user_asm_64(x,addr,err) \
__asm__ __volatile__( \
"1: ld %1,[%2]\n" \
"2: ld %R1,[%2, 4]\n" \
" mov %0, 0x0\n" \
"3: ;nop\n" \
" .section .fixup, \"ax\"\n" \
" .align 4\n" \
"4: mov %0, %3\n" \
" mov %1, 0x0\n" \
" j 3b\n" \
" .previous\n" \
" .section __ex_table, \"a\"\n" \
" .align 4\n" \
" .word 1b,4b\n" \
" .word 2b,4b\n" \
" .previous\n" \
: "=r" (err), "=&r" (x) \
: "r" (addr), "i" (-EFAULT), "0" (err))
extern long __put_user_bad(void);
#define __put_user_size(x,ptr,size,retval) \
do { \
switch (size) { \
case 1: __put_user_asm(x,ptr,retval,"stb"); break; \
case 2: __put_user_asm(x,ptr,retval,"stw"); break; \
case 4: __put_user_asm(x,ptr,retval,"st"); break; \
case 8: __put_user_asm_64(x,ptr,retval); break; \
default: __put_user_bad(); \
} \
} while (0)
#define __put_user_asm(x,addr,err,op) \
__asm__ __volatile__( \
"1: "op" %1,[%2]\n" \
" mov %0, 0x0\n" \
"2: ;nop\n" \
" .section .fixup, \"ax\"\n" \
" .align 4\n" \
"3: mov %0, %3\n" \
" j 2b\n" \
" .previous\n" \
" .section __ex_table, \"a\"\n" \
" .align 4\n" \
" .word 1b,3b\n" \
" .previous\n" \
: "=r" (err) \
: "r" (x), "r" (addr),"i" (-EFAULT), "0" (err))
#define __put_user_asm_64(x,addr,err) \
__asm__ __volatile__( \
"1: st %1,[%2]\n" \
"2: st %R1,[%2, 4]\n" \
" mov %0, 0x0\n" \
"3: ;nop\n" \
" .section .fixup, \"ax\"\n" \
" .align 4\n" \
"4: mov %0, %3\n" \
" mov %1, 0x0\n" \
" j 3b\n" \
" .previous\n" \
" .section __ex_table, \"a\"\n" \
" .align 4\n" \
" .word 1b,4b\n" \
" .word 2b,4b\n" \
" .previous\n" \
: "=r" (err) \
: "r" (x), "r" (addr),"i" (-EFAULT), "0" (err))
extern unsigned long slowpath_copy_from_user(
void *to, const void *from, unsigned long n);
static inline unsigned long
__copy_from_user_inline(void *to, const void *from, unsigned long n)
{
long res=0;
char val;
unsigned long tmp1,tmp2,tmp3,tmp4;
unsigned long orig_n = n;
if (n == 0)
return 0;
if( ((unsigned long) to & 0x3) || ((unsigned long) from & 0x3))
{
// unaligned
res = slowpath_copy_from_user(to, from, n);
}
else
{
#ifdef ARC_HIGH_LATENCY_MEMORY
__asm__ __volatile__ (
" pf [%1,32] \n"
" sync \n"
" mov %0,%3 \n"
" lsr.f lp_count, %3,5 \n" // number of words
" lpnz 3f \n"
"1: pf [%2,32] \n"
" nop_s \n"
"10: ld.ab %5, [%2, 4] \n" // 0
" nop_s \n"
"11: ld.ab %6, [%2, 4] \n" // 1
" nop_s \n"
"12: ld.ab %7, [%2, 4] \n" // 2
" nop_s \n"
" st.ab %5, [%1, 4] \n" // 0
" nop_s \n"
" st.ab %6, [%1, 4] \n" // 1
" nop_s \n"
" st.ab %7, [%1, 4] \n" // 2
" nop_s \n"
"13: ld.ab %5, [%2, 4] \n" // 3
" nop_s \n"
"14: ld.ab %6, [%2, 4] \n" // 4
" nop_s \n"
"144: pf [%2,0] \n"
" st.ab %5, [%1, 4] \n" // 3
" nop_s \n"
" st.ab %6, [%1, 4] \n" // 4
" nop_s \n"
"15: ld.ab %5, [%2, 4] \n" // 5
" nop_s \n"
"16: ld.ab %6, [%2, 4] \n" // 6
" nop_s \n"
"17: ld.ab %7, [%2, 4] \n" // 7
" nop_s \n"
" st.ab %5, [%1, 4] \n" // 5
" nop_s \n"
" st.ab %6, [%1, 4] \n" // 6
" nop_s \n"
" st.ab %7, [%1, 4] \n" // 7
" sync \n"
" sub %0,%0,32 \n"
"3: and.f %3,%3,0x1f \n" // any left over bytes ?
" bz 34f \n" // no stragglers
" bbit0 %3,4,30f \n"
"18: ld.ab %5, [%2, 4] \n"
"19: ld.ab %6, [%2, 4] \n"
"20: ld.ab %7, [%2, 4] \n"
"21: ld.ab %8, [%2, 4] \n"
" st.ab %5, [%1, 4] \n"
" st.ab %6, [%1, 4] \n"
" st.ab %7, [%1, 4] \n"
" st.ab %8, [%1, 4] \n"
" sub.f %0, %0, 16 \n"
"30: bbit0 %3,3,31f \n" // 8 bytes left
"22: ld.ab %5, [%2,4] \n"
"23: ld.ab %6, [%2,4] \n"
" st.ab %5, [%1,4] \n"
" st.ab %6, [%1,4] \n"
" sub.f %0,%0,8 \n"
"31: bbit0 %3,2,32f \n" // 4 bytes left.
"24: ld.ab %5, [%2,4] \n"
" st.ab %5, [%1,4] \n"
" sub.f %0,%0,4 \n"
"32: bbit0 %3,1,33f \n" // 2 bytes left
"25: ldw.ab %5, [%2,2] \n"
" stw.ab %5, [%1,2] \n"
" sub.f %0,%0,2 \n"
"33: bbit0 %3,0,34f \n"
"26: ldb.ab %5, [%2,1] \n" // just one byte left
" stb.ab %5, [%1,1] \n"
" sub.f %0,%0,1 \n"
"34: nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 34b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 1b,4b \n"
" .word 10b, 4b \n"
" .word 11b,4b \n"
" .word 12b,4b \n"
" .word 13b,4b \n"
" .word 14b,4b \n"
" .word 144b,4b \n"
" .word 15b,4b \n"
" .word 16b,4b \n"
" .word 17b,4b \n"
" .word 18b,4b \n"
" .word 19b,4b \n"
" .word 20b,4b \n"
" .word 21b,4b \n"
" .word 22b,4b \n"
" .word 23b,4b \n"
" .word 24b,4b \n"
" .word 25b,4b \n"
" .word 26b,4b \n"
" .previous \n"
:"=r"(res), "=r"(to), "=r"(from), "=r"(n), "=r"(val) ,"=r"(tmp1),"=r"(tmp2),"=r"(tmp3),"=r"(tmp4)
:"3"(n), "1"(to), "2"(from)
: "lp_count"
);
#else
if (__builtin_constant_p(orig_n))
{
/* Hand-crafted constant propagation to reduce code sz
* of the laddred copy 16x,8,4,2,1
*/
res = orig_n;
if (orig_n / 16)
{
orig_n = orig_n % 16;
__asm__ __volatile__ (
" lsr lp_count, %7,4 \n" // 16byte iters
" lp 3f \n"
"1: ld.ab %3, [%2, 4] \n"
"11: ld.ab %4, [%2, 4] \n"
"12: ld.ab %5, [%2, 4] \n"
"13: ld.ab %6, [%2, 4] \n"
" st.ab %3, [%1, 4] \n"
" st.ab %4, [%1, 4] \n"
" st.ab %5, [%1, 4] \n"
" st.ab %6, [%1, 4] \n"
" sub %0,%0,16 \n"
"3: ;nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 3b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 1b, 4b \n"
" .word 11b,4b \n"
" .word 12b,4b \n"
" .word 13b,4b \n"
" .previous \n"
:"+r"(res), "+r"(to), "+r"(from),
"=r"(tmp1),"=r"(tmp2),"=r"(tmp3),"=r"(tmp4)
:"ir"(n)
:"lp_count");
}
if (orig_n / 8)
{
orig_n = orig_n % 8;
__asm__ __volatile__ (
"14: ld.ab %3, [%2,4] \n"
"15: ld.ab %4, [%2,4] \n"
" st.ab %3, [%1,4] \n"
" st.ab %4, [%1,4] \n"
" sub %0,%0,8 \n"
"31: ;nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 31b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 14b,4b \n"
" .word 15b,4b \n"
" .previous \n"
:"+r"(res), "+r"(to), "+r"(from),
"=r"(tmp1),"=r"(tmp2)
);
}
if (orig_n / 4)
{
orig_n = orig_n % 4;
__asm__ __volatile__ (
"16: ld.ab %3, [%2,4] \n"
" st.ab %3, [%1,4] \n"
" sub %0,%0,4 \n"
"32: ;nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 32b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 16b,4b \n"
" .previous \n"
:"+r"(res), "+r"(to), "+r"(from),
"=r"(tmp1)
);
}
if (orig_n / 2)
{
orig_n = orig_n % 2;
__asm__ __volatile__ (
"17: ldw.ab %3, [%2,2] \n"
" stw.ab %3, [%1,2] \n"
" sub %0,%0,2 \n"
"33: ;nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 33b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 17b,4b \n"
" .previous \n"
:"+r"(res), "+r"(to), "+r"(from),
"=r"(tmp1)
);
}
if (orig_n & 1)
{
__asm__ __volatile__ (
"18: ldb.ab %3, [%2,2] \n"
" stb.ab %3, [%1,2] \n"
" sub %0,%0,1 \n"
"34: ; nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 34b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 18b,4b \n"
" .previous \n"
:"+r"(res), "+r"(to), "+r"(from),
"=r"(tmp1)
);
}
}
else /* n is NOT constant, so laddered copy of 16x,8,4,2,1 */
{
__asm__ __volatile__ (
" mov %0,%3 \n"
" lsr.f lp_count, %3,4 \n" // number of words
" lpnz 3f \n"
"1: ld.ab %5, [%2, 4] \n"
"11: ld.ab %6, [%2, 4] \n"
"12: ld.ab %7, [%2, 4] \n"
"13: ld.ab %8, [%2, 4] \n"
" st.ab %5, [%1, 4] \n"
" st.ab %6, [%1, 4] \n"
" st.ab %7, [%1, 4] \n"
" st.ab %8, [%1, 4] \n"
" sub %0,%0,16 \n"
"3: and.f %3,%3,0xf \n" // any left over bytes ?
" bz 34f \n" // no stragglers
" bbit0 %3,3,31f \n" // 8 bytes left
"14: ld.ab %5, [%2,4] \n"
"15: ld.ab %6, [%2,4] \n"
" st.ab %5, [%1,4] \n"
" st.ab %6, [%1,4] \n"
" sub.f %0,%0,8 \n"
"31: bbit0 %3,2,32f \n" // 4 bytes left.
"16: ld.ab %5, [%2,4] \n"
" st.ab %5, [%1,4] \n"
" sub.f %0,%0,4 \n"
"32: bbit0 %3,1,33f \n" // 2 bytes left
"17: ldw.ab %5, [%2,2] \n"
" stw.ab %5, [%1,2] \n"
" sub.f %0,%0,2 \n"
"33: bbit0 %3,0,34f \n"
"18: ldb.ab %5, [%2,1] \n" // just one byte left
" stb.ab %5, [%1,1] \n"
" sub.f %0,%0,1 \n"
"34: ;nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 34b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 1b, 4b \n"
" .word 11b,4b \n"
" .word 12b,4b \n"
" .word 13b,4b \n"
" .word 14b,4b \n"
" .word 15b,4b \n"
" .word 16b,4b \n"
" .word 17b,4b \n"
" .word 18b,4b \n"
" .previous \n"
:"=r"(res), "=r"(to), "=r"(from), "=r"(n), "=r"(val) ,"=r"(tmp1),"=r"(tmp2),"=r"(tmp3),"=r"(tmp4)
:"3"(n), "1"(to), "2"(from)
:"lp_count"
);
}
#endif
}
return (res);
}
extern unsigned long slowpath_copy_to_user(
void *to, const void *from, unsigned long n);
static inline unsigned long
__copy_to_user_inline(void *to, const void *from, unsigned long n)
{
long res=0;
char val;
unsigned long tmp1,tmp2,tmp3,tmp4;
unsigned long orig_n = n;
if (n == 0)
return 0;
if( ((unsigned long) to & 0x3) || ((unsigned long) from & 0x3))
{
// unaligned
res = slowpath_copy_to_user(to, from, n);
}
else // 32 bit aligned.
{
#ifdef ARC_HIGH_LATENCY_MEMORY
__asm__ __volatile__ (
" sync \n"
" mov %0,%3 \n"
" lsr.f lp_count, %3,5 \n" // number of words
" lpnz 3f \n"
"1: pf [%2,32] \n"
" nop_s \n"
" ld.ab %5, [%2, 4] \n" // 0
" nop_s \n"
" ld.ab %6, [%2, 4] \n" // 1
" nop_s \n"
" ld.ab %7, [%2, 4] \n" // 2
" nop_s \n"
"10: st.ab %5, [%1, 4] \n" // 0
" nop_s \n"
"11: st.ab %6, [%1, 4] \n" // 1
" nop_s \n"
"12: st.ab %7, [%1, 4] \n" // 2
" nop_s \n"
" ld.ab %5, [%2, 4] \n" // 3
" nop_s \n"
" ld.ab %6, [%2, 4] \n" // 4
" nop_s \n"
" pf [%2,0] \n"
"13: st.ab %5, [%1, 4] \n" // 3
" nop_s \n"
"14: st.ab %6, [%1, 4] \n" // 4
" nop_s \n"
" ld.ab %5, [%2, 4] \n" // 5
" nop_s \n"
" ld.ab %6, [%2, 4] \n" // 6
" nop_s \n"
" ld.ab %7, [%2, 4] \n" // 7
" nop_s \n"
"15: st.ab %5, [%1, 4] \n" // 5
" nop_s \n"
"16: st.ab %6, [%1, 4] \n" // 6
" nop_s \n"
"17: st.ab %7, [%1, 4] \n" // 7
" sync \n"
" sub %0,%0,32 \n"
"3: and.f %3,%3,0x1f \n" // any left over bytes ?
" bz 34f \n" // no stragglers
" bbit0 %3,4,30f \n"
" ld.ab %5, [%2, 4] \n"
" ld.ab %6, [%2, 4] \n"
" ld.ab %7, [%2, 4] \n"
" ld.ab %8, [%2, 4] \n"
"18: st.ab %5, [%1, 4] \n"
"19: st.ab %6, [%1, 4] \n"
"20: st.ab %7, [%1, 4] \n"
"21: st.ab %8, [%1, 4] \n"
" sub.f %0, %0, 16 \n"
"30: bbit0 %3,3,31f \n" // 8 bytes left
" ld.ab %5, [%2,4] \n"
" ld.ab %6, [%2,4] \n"
"22: st.ab %5, [%1,4] \n"
"23: st.ab %6, [%1,4] \n"
" sub.f %0,%0,8 \n"
"31: bbit0 %3,2,32f \n" // 4 bytes left.
" ld.ab %5, [%2,4] \n"
"24: st.ab %5, [%1,4] \n"
" sub.f %0,%0,4 \n"
"32: bbit0 %3,1,33f \n" // 2 bytes left
" ldw.ab %5, [%2,2] \n"
"25: stw.ab %5, [%1,2] \n"
" sub.f %0,%0,2 \n"
"33: bbit0 %3,0,34f \n"
" ldb.ab %5, [%2,1] \n" // just one byte left
"26: stb.ab %5, [%1,1] \n"
" sub.f %0,%0,1 \n"
"34: nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 34b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 1b,4b \n"
" .word 10b,4b \n"
" .word 11b,4b \n"
" .word 12b,4b \n"
" .word 13b,4b \n"
" .word 14b,4b \n"
" .word 15b,4b \n"
" .word 16b,4b \n"
" .word 17b,4b \n"
" .word 18b,4b \n"
" .word 19b,4b \n"
" .word 20b,4b \n"
" .word 21b,4b \n"
" .word 22b,4b \n"
" .word 23b,4b \n"
" .word 24b,4b \n"
" .word 25b,4b \n"
" .word 26b,4b \n"
" .previous \n"
:"=r"(res), "=r"(to), "=r"(from), "=r"(n), "=r"(val) ,"=r"(tmp1),"=r"(tmp2),"=r"(tmp3),"=r"(tmp4)
:"3"(n), "1"(to), "2"(from)
: "lp_count"
);
#else
if (__builtin_constant_p(orig_n))
{
/* Hand-crafted constant propagation to reduce code sz
* of the laddred copy 16x,8,4,2,1
*/
res = orig_n;
if (orig_n / 16)
{
orig_n = orig_n % 16;
__asm__ __volatile__ (
" lsr lp_count, %7,4 \n" // 16byte iters
" lp 3f \n"
" ld.ab %3, [%2, 4] \n"
" ld.ab %4, [%2, 4] \n"
" ld.ab %5, [%2, 4] \n"
" ld.ab %6, [%2, 4] \n"
"1: st.ab %3, [%1, 4] \n"
"11: st.ab %4, [%1, 4] \n"
"12: st.ab %5, [%1, 4] \n"
"13: st.ab %6, [%1, 4] \n"
" sub %0, %0, 16 \n"
"3: ;nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 3b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 1b, 4b \n"
" .word 11b,4b \n"
" .word 12b,4b \n"
" .word 13b,4b \n"
" .previous \n"
:"+r"(res), "+r"(to), "+r"(from),
"=r"(tmp1),"=r"(tmp2),"=r"(tmp3),"=r"(tmp4)
:"ir"(n)
:"lp_count");
}
if (orig_n / 8)
{
orig_n = orig_n % 8;
__asm__ __volatile__ (
" ld.ab %3, [%2,4] \n"
" ld.ab %4, [%2,4] \n"
"14: st.ab %3, [%1,4] \n"
"15: st.ab %4, [%1,4] \n"
" sub %0, %0, 8 \n"
"31: ;nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 31b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 14b,4b \n"
" .word 15b,4b \n"
" .previous \n"
:"+r"(res), "+r"(to), "+r"(from),
"=r"(tmp1),"=r"(tmp2)
);
}
if (orig_n / 4)
{
orig_n = orig_n % 4;
__asm__ __volatile__ (
" ld.ab %3, [%2,4] \n"
"16: st.ab %3, [%1,4] \n"
" sub %0, %0, 4 \n"
"32: ;nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 32b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 16b,4b \n"
" .previous \n"
:"+r"(res), "+r"(to), "+r"(from),
"=r"(tmp1)
);
}
if (orig_n / 2)
{
orig_n = orig_n % 2;
__asm__ __volatile__ (
" ldw.ab %3, [%2,2] \n"
"17: stw.ab %3, [%1,2] \n"
" sub %0, %0, 2 \n"
"33: ;nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 33b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 17b,4b \n"
" .previous \n"
:"+r"(res), "+r"(to), "+r"(from),
"=r"(tmp1)
);
}
if (orig_n & 1)
{
__asm__ __volatile__ (
" ldb.ab %3, [%2,1] \n" // just one byte left
"18: stb.ab %3, [%1,1] \n"
" sub %0, %0, 1 \n"
"34: ;nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 34b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 18b,4b \n"
" .previous \n"
:"+r"(res), "+r"(to), "+r"(from),
"=r"(tmp1)
);
}
}
else /* n is NOT constant, so laddered copy of 16x,8,4,2,1 */
{
__asm__ __volatile__ (
" mov %0,%3 \n"
" lsr.f lp_count, %3,4 \n" // number of words
" lpnz 3f \n"
" ld.ab %5, [%2, 4] \n"
" ld.ab %6, [%2, 4] \n"
" ld.ab %7, [%2, 4] \n"
" ld.ab %8, [%2, 4] \n"
"1: st.ab %5, [%1, 4] \n"
"11: st.ab %6, [%1, 4] \n"
"12: st.ab %7, [%1, 4] \n"
"13: st.ab %8, [%1, 4] \n"
" sub %0, %0, 16 \n"
"3: and.f %3,%3,0xf \n" // any left over bytes ?
" bz 34f \n" // no stragglers
" bbit0 %3,3,31f \n" // 8 bytes left
" ld.ab %5, [%2,4] \n"
" ld.ab %6, [%2,4] \n"
"14: st.ab %5, [%1,4] \n"
"15: st.ab %6, [%1,4] \n"
" sub.f %0, %0, 8 \n"
"31: bbit0 %3,2,32f \n" // 4 bytes left.
" ld.ab %5, [%2,4] \n"
"16: st.ab %5, [%1,4] \n"
" sub.f %0, %0, 4 \n"
"32: bbit0 %3,1,33f \n" // 2 bytes left
" ldw.ab %5, [%2,2] \n"
"17: stw.ab %5, [%1,2] \n"
" sub.f %0, %0, 2 \n"
"33: bbit0 %3,0,34f \n"
" ldb.ab %5, [%2,1] \n" // just one byte left
"18: stb.ab %5, [%1,1] \n"
" sub.f %0, %0, 1 \n"
"34: ;nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: j 34b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 1b, 4b \n"
" .word 11b,4b \n"
" .word 12b,4b \n"
" .word 13b,4b \n"
" .word 14b,4b \n"
" .word 15b,4b \n"
" .word 16b,4b \n"
" .word 17b,4b \n"
" .word 18b,4b \n"
" .previous \n"
:"=r"(res), "=r"(to), "=r"(from), "=r"(n), "=r"(val) ,"=r"(tmp1),"=r"(tmp2),"=r"(tmp3),"=r"(tmp4)
:"3"(n), "1"(to), "2"(from)
:"lp_count"
);
}
#endif
}
return (res);
}
/*
* Zero Userspace
*/
static inline unsigned long
__clear_user_inline(void *to, unsigned long n)
{
long res = n;
unsigned char *d_char = to;
__asm__ __volatile__ (
" bbit0 %0, 0, 1f \n"
"75: stb.ab %2, [%0,1]\n"
" sub %1, %1, 1 \n"
"1: \n"
" bbit0 %0, 1, 2f \n"
"76: stw.ab %2, [%0,2]\n"
" sub %1, %1, 2 \n"
"2: \n"
" asr.f lp_count, %1, 2\n"
" lpnz 3f\n"
"77: st.ab %2, [%0,4]\n"
" sub %1, %1, 4 \n"
"3:\n"
" bbit0 %1, 1, 4f \n"
"78: stw.ab %2, [%0,2]\n"
" sub %1, %1, 2 \n"
"4: \n"
" bbit0 %1, 0, 5f \n"
"79: stb.ab %2, [%0,1]\n"
" sub %1, %1, 1 \n"
"5: \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"3: j 5b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 75b, 3b \n"
" .word 76b, 3b \n"
" .word 77b, 3b \n"
" .word 78b, 3b \n"
" .word 79b, 3b \n"
" .previous \n"
:"+r" (d_char), "+r" (res)
:"i" (0)
:"lp_count","lp_start","lp_end");
return (res);
}
/*
* Copy a null terminated string from userspace.
*
* Must return:
* -EFAULT for an exception
* count if we hit the buffer limit
* bytes copied if we hit a null byte
* (without the null byte)
*/
static inline long
__strncpy_from_user_inline(char *dst, const char *src, long count)
{
long res = count;
char val;
unsigned int hw_count;
if (count == 0)
return 0;
__asm__ __volatile__ (
" lp 2f \n"
"1: ldb.ab %3, [%2, 1] \n"
" breq.d %3, 0, 2f \n"
" stb.ab %3, [%1, 1] \n"
"2: sub %0, %6, %4 \n"
"3: ;nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: mov %0, %5 \n"
" j 3b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 1b, 4b \n"
" .previous \n"
:"=r"(res), "+r"(dst), "+r"(src), "=&r"(val),"=l"(hw_count)
:"g" (-EFAULT), "ir"(count),"4"(count) // this "4" seeds lp_count abv
:"memory"
);
return (res);
}
static inline long
__strnlen_user_inline(const char *s, long n)
{
long res, tmp1, cnt;
char val;
__asm__ __volatile__ (
" mov %2, %1 \n"
"1: ldb.ab %3, [%0, 1] \n"
" breq.d %3, 0, 2f \n"
" sub.f %2, %2, 1 \n"
" bnz 1b \n"
" sub %2, %2, 1 \n"
"2: sub %0, %1, %2 \n"
"3: ;nop \n"
" .section .fixup, \"ax\" \n"
" .align 4 \n"
"4: mov %0, 0 \n"
" j 3b \n"
" .previous \n"
" .section __ex_table, \"a\" \n"
" .align 4 \n"
" .word 1b, 4b \n"
" .previous \n"
:"=r" (res), "=r"(tmp1), "=r"(cnt), "=r" (val)
:"0" (s), "1"(n)
);
return (res);
}
#ifndef NONINLINE_USR_CPY
/*
documentation says that copy_from_user should return the number of
bytes that couldn't be copied, we return 0 indicating that all data
was successfully copied.
NOTE: quite a few architectures return the size of the copy (n), this
is wrong because the refernces of copy_from_user consider it an error
when a positive value is returned
*/
static inline unsigned long
copy_from_user(void *to, const void *from, unsigned long n)
{
if(access_ok(VERIFY_READ, from, n)) {
return (__copy_from_user_inline(to, from, n));
}
return n;
}
/*
documentation says that __copy_from_user should return the number of
bytes that couldn't be copied, we return 0 indicating that all data
was successfully copied.
NOTE: quite a few architectures return the size of the copy (n), this
is wrong because the refernces of __copy_from_user consider it an error
when a positive value is returned
*/
static inline unsigned long
__copy_from_user(void *to, const void *from, unsigned long n)
{
return (__copy_from_user_inline(to, from, n));
}
/*
documentation says that copy_to_user should return the number of
bytes that couldn't be copied, we return 0 indicating that all data
was successfully copied.
NOTE: quite a few architectures return the size of the copy (n), this
is wrong because the refernces of copy_to_user consider it an error
when a positive value is returned
*/
static inline unsigned long
copy_to_user(void *to, const void *from, unsigned long n)
{
if(access_ok(VERIFY_READ, to, n))
return (__copy_to_user_inline(to, from, n));
return n;
}
/*
documentation says that __copy_to_user should return the number of
bytes that couldn't be copied, we return 0 indicating that all data
was successfully copied.
NOTE: quite a few architectures return the size of the copy (n), this
is wrong because the refernces of __copy_to_user consider it an error
when a positive value is returned
*/
static inline unsigned long
__copy_to_user(void *to, const void *from, unsigned long n)
{
return(__copy_to_user_inline(to, from, n));
}
static inline unsigned long __clear_user(void *to, unsigned long n)
{
return __clear_user_inline(to,n);
}
static inline unsigned long clear_user(void *to, unsigned long n)
{
if(access_ok(VERIFY_WRITE, to, n))
return __clear_user_inline(to,n);
return n;
}
static inline long
strncpy_from_user(char *dst, const char *src, long count)
{
long res = -EFAULT;
if (access_ok(VERIFY_READ, src, 1))
res = __strncpy_from_user_inline(dst, src, count);
return res;
}
/*
* Return the size of a string (including the ending 0)
*
* Return length of string in userspace including terminating 0
* or 0 for error. Return a value greater than N if too long.
*/
static inline long
strnlen_user(const char *s, long n)
{
if (!access_ok(VERIFY_READ, s, 0))
return 0;
return __strnlen_user_inline(s, n);
}
#else
unsigned long copy_from_user(void *to, const void *from, unsigned long n);
unsigned long __copy_from_user(void *to, const void *from, unsigned long n);
unsigned long copy_to_user(void *to, const void *from, unsigned long n);
unsigned long __copy_to_user(void *to, const void *from, unsigned long n);
unsigned long clear_user(void *to, unsigned long n);
unsigned long __clear_user(void *to, unsigned long n);
long strncpy_from_user(char *dst, const char *src, long count);
long strnlen_user(const char *s, long n);
#endif
#define __copy_to_user_inatomic __copy_to_user
#define __copy_from_user_inatomic __copy_from_user
#define strlen_user(str) strnlen_user((str), 0x7ffffffe)
#endif