/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) Equator Technologies
 */

#ifndef _ASM_BSP_UACCESS_H_
#define _ASM_BSP_UACCESS_H_

/*
 * User space memory access functions
 */
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/types.h>

#include <asm/page.h>
#include <asm/debug.h>

/*
 * 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 KERNEL_DS	((mm_segment_t) { 0UL })
#define USER_DS		((mm_segment_t) { 1UL })

#define VERIFY_READ 0
#define VERIFY_WRITE 1

#define get_fs()        (current->tss.current_ds)
#define get_ds()	(KERNEL_DS)
#define set_fs(x)       (current->tss.current_ds=(x))

#define segment_eq(a,b)	((a).seg == (b).seg)

/*
 * Is a address valid? This does a straighforward calculation rather
 * than tests.
 *
 * Address valid if:
 *  - "addr" doesn't have any high-bits set
 *  - AND "size" doesn't have any high-bits set
 *  - AND "addr+size" doesn't have any high-bits set
 *  - OR we are in kernel mode.
 */
#define __access_ok(addr,size,mask) \
        (((__signed__ long)((mask)&(addr | size | (addr+size-1)))) >= 0)
#define __access_mask (-(long)(get_fs().seg))

#define access_ok(type,addr,size) \
	__access_ok(((unsigned long)(addr)),(size),__access_mask)

/* user-space access functions */
#define __get_user(x, ptr)	( (x) = *(__typeof__(ptr + 0)) (ptr), 0 )
#define __put_user(x, ptr)	( *(__typeof__(ptr + 0)) (ptr) = (x), 0 )

#define __strlen_user(s)	__strnlen_user(s, (size_t)-1)

extern unsigned long __copy_to_user(void *to, void *from, size_t n);
extern unsigned long __copy_from_user(void *to, void *from, size_t n);
extern unsigned long __clear_user(void *addr, unsigned long n);

extern long __strncpy_from_user(char *__to, const char *__from, long __len);
extern long __strnlen_user(const char *__s, size_t __n);


#define get_user(x, ptr)                                        \
        ({__typeof__(ptr) __tmp = ptr;                          \
         access_ok(VERIFY_READ, __tmp, sizeof(*__tmp)) ?        \
                 __get_user(x, __tmp) : -EFAULT; })

#define put_user(x, ptr)                                        \
        ({__typeof__(ptr) __tmp = ptr;                          \
          access_ok(VERIFY_WRITE, __tmp, sizeof(*__tmp)) ?      \
                  __put_user(x, __tmp) : -EFAULT; })


extern inline int verify_area(int type, const void * addr, unsigned long size)
{
	return access_ok(type, addr, size) ? 0 : -EFAULT;
}

extern inline unsigned long copy_from_user(void *to, void *from, size_t n)
{
	if ( access_ok(VERIFY_READ, from, n) )
		n = __copy_from_user(to, from, n);
	return n;
}

extern inline unsigned long copy_to_user(void *to, void *from, size_t n)
{
   	if ( access_ok(VERIFY_WRITE, to, n) )
		n = __copy_to_user(to, from, n);
	return n;
}


extern inline unsigned long clear_user(void *addr, unsigned long n) 
{
   	if ( access_ok(VERIFY_WRITE, addr, n) )
		n = __clear_user(addr, n);
	return n;
}


/*
 * Returns: -EFAULT if exception before terminator, N if the entire
 * buffer filled, else strlen.
 */
extern inline long
strncpy_from_user(char *to, const char *from, long n)
{
	long ret = -EFAULT;
	if (access_ok(VERIFY_READ, (long)from, 0)) {
		ret = __strncpy_from_user(to, from, n);
                if (ret == 0) ret = -EFAULT;
                else if (ret == 1)  /* empty string, only NULL */
                    ret = 0;
        }
	return ret;
}


/*
 * Returns: 0 if bad, string length+1 (memory size) of string if ok
 */
extern inline long strlen_user(const char *s)
{
	return access_ok(VERIFY_READ,s,0) ? __strnlen_user(s, (unsigned long)-1) : 0;
}


/*
 * Returns: 0 if exception before NUL or reaching the supplied limit (N),
 * a value greater than N if the limit would be exceeded, else strlen.
 */
extern inline long strnlen_user(const char *s, size_t n)
{
	return access_ok(VERIFY_READ,s,0) ? __strnlen_user(s, n) : 0;
}

/*
 * 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, fixup;
};

/* Returns 0 if exception not found and fixup otherwise.  */
/*  extern unsigned long search_exception_table(unsigned long); */

#endif /*  _ASM_BSP_UACCESS_H_ */
