diff options
Diffstat (limited to 'include/asm-s390x/uaccess.h')
-rw-r--r-- | include/asm-s390x/uaccess.h | 559 |
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 */ + + + + + |