summaryrefslogtreecommitdiffstats
path: root/include/asm-s390x/uaccess.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/asm-s390x/uaccess.h')
-rw-r--r--include/asm-s390x/uaccess.h559
1 files changed, 559 insertions, 0 deletions
diff --git a/include/asm-s390x/uaccess.h b/include/asm-s390x/uaccess.h
new file mode 100644
index 000000000..419d87afc
--- /dev/null
+++ b/include/asm-s390x/uaccess.h
@@ -0,0 +1,559 @@
+/*
+ * include/asm-s390/uaccess.h
+ *
+ * S390 version
+ * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Hartmut Penner (hpenner@de.ibm.com),
+ * Martin Schwidefsky (schwidefsky@de.ibm.com)
+ *
+ * Derived from "include/asm-i386/uaccess.h"
+ */
+#ifndef __S390_UACCESS_H
+#define __S390_UACCESS_H
+
+/*
+ * User space memory access functions
+ */
+#include <linux/sched.h>
+
+#define VERIFY_READ 0
+#define VERIFY_WRITE 1
+
+
+/*
+ * 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) })
+
+
+#define KERNEL_DS MAKE_MM_SEG(0)
+#define USER_DS MAKE_MM_SEG(1)
+
+#define get_ds() (KERNEL_DS)
+#define get_fs() (current->addr_limit)
+#define set_fs(x) ({asm volatile("sar 4,%0"::"a" ((x).ar4));\
+ current->addr_limit = (x);})
+
+#define segment_eq(a,b) ((a).ar4 == (b).ar4)
+
+
+#define __access_ok(addr,size) (1)
+
+#define access_ok(type,addr,size) __access_ok(addr,size)
+
+extern 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, fixup;
+};
+
+/* 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.
+ */
+
+extern inline int __put_user_asm_8(__u64 x, void *ptr)
+{
+ int err;
+
+ __asm__ __volatile__ ( " sr %1,%1\n"
+ " la 4,%0\n"
+ " sacf 512\n"
+ "0: stg %2,0(4)\n"
+ "1: sacf 0\n"
+ ".section .fixup,\"ax\"\n"
+ "2: lhi %1,%h3\n"
+ " jg 1b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 0b,2b\n"
+ ".previous"
+ : "=m" (*((__u64*) ptr)) , "=&d" (err)
+ : "d" (x), "K" (-EFAULT)
+ : "4" );
+ return err;
+}
+extern inline int __put_user_asm_4(__u32 x, void *ptr)
+{
+ int err;
+
+ __asm__ __volatile__ ( " sr %1,%1\n"
+ " la 4,%0\n"
+ " sacf 512\n"
+ "0: st %2,0(4)\n"
+ "1: sacf 0\n"
+ ".section .fixup,\"ax\"\n"
+ "2: lhi %1,%h3\n"
+ " jg 1b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 0b,2b\n"
+ ".previous"
+ : "=m" (*((__u32*) ptr)) , "=&d" (err)
+ : "d" (x), "K" (-EFAULT)
+ : "4" );
+ return err;
+}
+
+extern inline int __put_user_asm_2(__u16 x, void *ptr)
+{
+ int err;
+
+ __asm__ __volatile__ ( " sr %1,%1\n"
+ " la 4,%0\n"
+ " sacf 512\n"
+ "0: sth %2,0(4)\n"
+ "1: sacf 0\n"
+ ".section .fixup,\"ax\"\n"
+ "2: lhi %1,%h3\n"
+ " jg 1b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 0b,2b\n"
+ ".previous"
+ : "=m" (*((__u16*) ptr)) , "=&d" (err)
+ : "d" (x), "K" (-EFAULT)
+ : "4" );
+ return err;
+}
+
+extern inline int __put_user_asm_1(__u8 x, void *ptr)
+{
+ int err;
+
+ __asm__ __volatile__ ( " sr %1,%1\n"
+ " la 4,%0\n"
+ " sacf 512\n"
+ "0: stc %2,0(4)\n"
+ "1: sacf 0\n"
+ ".section .fixup,\"ax\"\n"
+ "2: lhi %1,%h3\n"
+ " jg 1b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 0b,2b\n"
+ ".previous"
+ : "=m" (*((__u8*) ptr)) , "=&d" (err)
+ : "d" (x), "K" (-EFAULT)
+ : "1", "4" );
+ return err;
+}
+
+/*
+ * (u?)(u64) ... autsch, but that the only way we can suppress the
+ * warnings when compiling binfmt_elf.c
+ */
+#define __put_user(x, ptr) \
+({ \
+ int __pu_err; \
+ switch (sizeof (*(ptr))) { \
+ case 1: \
+ __pu_err = __put_user_asm_1((__u8)(__u64)(x),(ptr));\
+ break; \
+ case 2: \
+ __pu_err = __put_user_asm_2((__u16)(__u64)(x),(ptr));\
+ break; \
+ case 4: \
+ __pu_err = __put_user_asm_4((__u32)(__u64)(x),(ptr));\
+ break; \
+ case 8: \
+ __pu_err = __put_user_asm_8((__u64)(x),(ptr));\
+ break; \
+ default: \
+ __pu_err = __put_user_bad(); \
+ break; \
+ } \
+ __pu_err; \
+})
+
+#define put_user(x, ptr) \
+({ \
+ long __pu_err = -EFAULT; \
+ __typeof__(*(ptr)) *__pu_addr = (ptr); \
+ __typeof__(*(ptr)) __x = (x); \
+ if (__access_ok((long)__pu_addr,sizeof(*(ptr)))) { \
+ __pu_err = 0; \
+ __put_user((__x), (__pu_addr)); \
+ } \
+ __pu_err; \
+})
+
+extern int __put_user_bad(void);
+
+
+#define __get_user_asm_8(x, ptr, err) \
+({ \
+ __asm__ __volatile__ ( " sr %1,%1\n" \
+ " la 4,%2\n" \
+ " sacf 512\n" \
+ "0: lg %0,0(4)\n" \
+ "1: sacf 0\n" \
+ ".section .fixup,\"ax\"\n" \
+ "2: lhi %1,%h3\n" \
+ " jg 1b\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 8\n" \
+ " .quad 0b,2b\n" \
+ ".previous" \
+ : "=d" (x) , "=&d" (err) \
+ : "m" (*(const __u64*)(ptr)),"K" (-EFAULT) \
+ : "4" ); \
+})
+#define __get_user_asm_4(x, ptr, err) \
+({ \
+ __asm__ __volatile__ ( " sr %1,%1\n" \
+ " la 4,%2\n" \
+ " sacf 512\n" \
+ "0: l %0,0(4)\n" \
+ "1: sacf 0\n" \
+ ".section .fixup,\"ax\"\n" \
+ "2: lhi %1,%h3\n" \
+ " jg 1b\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 8\n" \
+ " .quad 0b,2b\n" \
+ ".previous" \
+ : "=d" (x) , "=&d" (err) \
+ : "m" (*(const __u32*)(ptr)),"K" (-EFAULT) \
+ : "4" ); \
+})
+
+#define __get_user_asm_2(x, ptr, err) \
+({ \
+ __asm__ __volatile__ ( " sr %1,%1\n" \
+ " la 4,%2\n" \
+ " sacf 512\n" \
+ "0: lh %0,0(4)\n" \
+ "1: sacf 0\n" \
+ ".section .fixup,\"ax\"\n" \
+ "2: lhi %1,%h3\n" \
+ " jg 1b\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 8\n" \
+ " .quad 0b,2b\n" \
+ ".previous" \
+ : "=d" (x) , "=&d" (err) \
+ : "m" (*(const __u16*)(ptr)),"K" (-EFAULT) \
+ : "4" ); \
+})
+
+#define __get_user_asm_1(x, ptr, err) \
+({ \
+ __asm__ __volatile__ ( " sr %1,%1\n" \
+ " la 4,%2\n" \
+ " sr %0,%0\n" \
+ " sacf 512\n" \
+ "0: ic %0,0(4)\n" \
+ "1: sacf 0\n" \
+ ".section .fixup,\"ax\"\n" \
+ "2: lhi %1,%h3\n" \
+ " jg 1b\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 8\n" \
+ " .quad 0b,2b\n" \
+ ".previous" \
+ : "=d" (x) , "=&d" (err) \
+ : "m" (*(const __u8*)(ptr)),"K" (-EFAULT) \
+ : "4" ); \
+})
+
+#define __get_user(x, ptr) \
+({ \
+ int __gu_err; \
+ switch (sizeof(*(ptr))) { \
+ case 1: \
+ __get_user_asm_1(x,ptr,__gu_err); \
+ break; \
+ case 2: \
+ __get_user_asm_2(x,ptr,__gu_err); \
+ break; \
+ case 4: \
+ __get_user_asm_4(x,ptr,__gu_err); \
+ break; \
+ case 8: \
+ __get_user_asm_8(x,ptr,__gu_err); \
+ break; \
+ default: \
+ (x) = 0; \
+ __gu_err = __get_user_bad(); \
+ break; \
+ } \
+ __gu_err; \
+})
+
+#define get_user(x, ptr) \
+({ \
+ long __gu_err = -EFAULT; \
+ __typeof__(ptr) __gu_addr = (ptr); \
+ __typeof__(*(ptr)) __x; \
+ if (__access_ok((long)__gu_addr,sizeof(*(ptr)))) { \
+ __gu_err = 0; \
+ __get_user((__x), (__gu_addr)); \
+ (x) = __x; \
+ } \
+ else \
+ (x) = 0; \
+ __gu_err; \
+})
+
+extern int __get_user_bad(void);
+
+/*
+ * access register are set up, that 4 points to secondary (user) , 2 to primary (kernel)
+ */
+
+asmlinkage void __copy_from_user_fixup(void /* special calling convention */);
+asmlinkage void __copy_to_user_fixup(void /* special calling convention */);
+
+extern inline unsigned long
+__copy_to_user_asm(void* to, const void* from, long n)
+{
+
+ __asm__ __volatile__ ( " lgr 2,%2\n"
+ " lgr 4,%1\n"
+ " lgr 3,%0\n"
+ " lgr 5,3\n"
+ " sacf 512\n"
+ "0: mvcle 4,2,0\n"
+ " jo 0b\n"
+ " sacf 0\n"
+ " lgr %0,3\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 0b,__copy_to_user_fixup\n"
+ ".previous"
+ : "+&d" (n) : "d" (to), "d" (from)
+ : "1", "2", "3", "4", "5" );
+ return n;
+}
+
+#define __copy_to_user(to, from, n) \
+({ \
+ __copy_to_user_asm(to,from,n); \
+})
+
+#define copy_to_user(to, from, n) \
+({ \
+ long err = 0; \
+ __typeof__(n) __n = (n); \
+ if (__access_ok(to,__n)) { \
+ err = __copy_to_user_asm(to,from,__n); \
+ } \
+ else \
+ err = __n; \
+ err; \
+})
+
+extern inline unsigned long
+__copy_from_user_asm(void* to, const void* from, long n)
+{
+ __asm__ __volatile__ ( " lgr 2,%1\n"
+ " lgr 4,%2\n"
+ " lgr 3,%0\n"
+ " lgr 5,3\n"
+ " sacf 512\n"
+ "0: mvcle 2,4,0\n"
+ " jo 0b\n"
+ " sacf 0\n"
+ " lgr %0,5\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 0b,__copy_from_user_fixup\n"
+ ".previous"
+ : "+&d" (n) : "d" (to), "d" (from)
+ : "1", "2", "3", "4", "5" );
+ return n;
+}
+
+
+#define __copy_from_user(to, from, n) \
+({ \
+ __copy_from_user_asm(to,from,n); \
+})
+
+#define copy_from_user(to, from, n) \
+({ \
+ long err = 0; \
+ __typeof__(n) __n = (n); \
+ if (__access_ok(from,__n)) { \
+ err = __copy_from_user_asm(to,from,__n); \
+ } \
+ else \
+ err = __n; \
+ err; \
+})
+
+/*
+ * Copy a null terminated string from userspace.
+ */
+
+static inline long
+__strncpy_from_user(char *dst, const char *src, long count)
+{
+ long len;
+ __asm__ __volatile__ ( " slgr %0,%0\n"
+ " lgr 2,%1\n"
+ " lgr 4,%2\n"
+ " slr 3,3\n"
+ " sacf 512\n"
+ "0: ic 3,0(%0,4)\n"
+ "1: stc 3,0(%0,2)\n"
+ " aghi %0,1\n"
+ " cgr %0,%3\n"
+ " je 2f\n"
+ " ltr 3,3\n"
+ " jne 0b\n"
+ "2: sacf 0\n"
+ "3:\n"
+ ".section .fixup,\"ax\"\n"
+ "4: lghi %0,%h4\n"
+ " jg 3b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 0b,4b\n"
+ " .quad 1b,4b\n"
+ ".previous"
+ : "=&a" (len)
+ : "a" (dst), "d" (src), "d" (count),
+ "K" (-EFAULT)
+ : "2" ,"3", "4" );
+ return len;
+}
+
+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(dst, src, count);
+ return res;
+}
+
+/*
+ * Return the size of a string (including the ending 0)
+ *
+ * Return 0 for error
+ */
+static inline unsigned long
+strnlen_user(const char * src, unsigned long n)
+{
+#if 0
+ __asm__ __volatile__ (" algr %0,%1\n"
+ " slgr 0,0\n"
+ " lgr 4,%1\n"
+ " sacf 512\n"
+ "0: srst %0,4\n"
+ " jo 0b\n"
+ " slgr %0,%1\n"
+ " aghi %0,1\n"
+ "1: sacf 0\n"
+ ".section .fixup,\"ax\"\n"
+ "2: slgr %0,%0\n"
+ " jg 1b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 0b,2b\n"
+ ".previous"
+ : "+&a" (n) : "d" (src)
+ : "cc", "0", "4" );
+#else
+ __asm__ __volatile__ (" lgr 4,%1\n"
+ " sacf 512\n"
+ "0: cli 0(4),0x00\n"
+ " la 4,1(4)\n"
+ " je 1f\n"
+ " brctg %0,0b\n"
+ "1: lgr %0,4\n"
+ " slgr %0,%1\n"
+ "2: sacf 0\n"
+ ".section .fixup,\"ax\"\n"
+ "3: slgr %0,%0\n"
+ " jg 2b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 0b,3b\n"
+ ".previous"
+ : "+&a" (n) : "d" (src)
+ : "cc", "4" );
+#endif
+ return n;
+}
+#define strlen_user(str) strnlen_user(str, ~0UL)
+
+/*
+ * Zero Userspace
+ */
+
+static inline unsigned long
+__clear_user(void *to, unsigned long n)
+{
+ __asm__ __volatile__ ( " sacf 512\n"
+ " lgr 4,%1\n"
+ " lgr 5,%0\n"
+ " sgr 2,2\n"
+ " sgr 3,3\n"
+ "0: mvcle 4,2,0\n"
+ " jo 0b\n"
+ "1: sacf 0\n"
+ " lgr %0,5\n"
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 0b,__copy_to_user_fixup\n"
+ ".previous"
+ : "+&a" (n)
+ : "a" (to)
+ : "cc", "1", "2", "3", "4", "5" );
+ return n;
+}
+
+static inline unsigned long
+clear_user(void *to, unsigned long n)
+{
+ if (access_ok(VERIFY_WRITE, to, n))
+ n = __clear_user(to, n);
+ return n;
+}
+
+#endif /* _S390_UACCESS_H */
+
+
+
+
+