314 lines
8.4 KiB
C
314 lines
8.4 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _ASM_SW64_UACCESS_H
|
|
#define _ASM_SW64_UACCESS_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.
|
|
*
|
|
* Or at least it did once upon a time. Nowadays it is a mask that
|
|
* defines which bits of the address space are off limits. This is a
|
|
* wee bit faster than the above.
|
|
*
|
|
* For historical reasons, these macros are grossly misnamed.
|
|
*/
|
|
|
|
#define KERNEL_DS ((mm_segment_t) { 0UL })
|
|
#define USER_DS ((mm_segment_t) { -0x10000000000000UL })
|
|
|
|
#define get_fs() (current_thread_info()->addr_limit)
|
|
#define get_ds() (KERNEL_DS)
|
|
#define set_fs(x) (current_thread_info()->addr_limit = (x))
|
|
|
|
#define uaccess_kernel() (get_fs().seg == KERNEL_DS.seg)
|
|
|
|
/*
|
|
* Is a address valid? This does a straightforward 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-(size != 0)" doesn't have any high-bits set
|
|
* - OR we are in kernel mode.
|
|
*/
|
|
#define __access_ok(addr, size) ({ \
|
|
unsigned long __ao_a = (addr), __ao_b = (size); \
|
|
unsigned long __ao_end = __ao_a + __ao_b - !!__ao_b; \
|
|
(get_fs().seg & (__ao_a | __ao_b | __ao_end)) == 0; })
|
|
|
|
#define access_ok(addr, size) \
|
|
({ \
|
|
__chk_user_ptr(addr); \
|
|
__access_ok(((unsigned long)(addr)), (size)); \
|
|
})
|
|
|
|
/*
|
|
* These are the main single-value transfer routines. They automatically
|
|
* use the right size if we just have the right pointer type.
|
|
*
|
|
* As the sw64 uses the same address space for kernel and user
|
|
* data, we can just do these as direct assignments. (Of course, the
|
|
* exception handling means that it's no longer "just"...)
|
|
*
|
|
* Careful to not
|
|
* (a) re-use the arguments for side effects (sizeof/typeof is ok)
|
|
* (b) require any knowledge of processes at this stage
|
|
*/
|
|
#define put_user(x, ptr) \
|
|
__put_user_check((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr)))
|
|
#define get_user(x, ptr) \
|
|
__get_user_check((x), (ptr), sizeof(*(ptr)))
|
|
|
|
/*
|
|
* The "__xxx" versions do not do address space checking, useful when
|
|
* doing multiple accesses to the same area (the programmer has to do the
|
|
* checks by hand with "access_ok()")
|
|
*/
|
|
#define __put_user(x, ptr) \
|
|
__put_user_nocheck((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr)))
|
|
#define __get_user(x, ptr) \
|
|
__get_user_nocheck((x), (ptr), sizeof(*(ptr)))
|
|
/*
|
|
* The "ldi %1, 2b-1b(%0)" bits are magic to get the assembler to
|
|
* encode the bits we need for resolving the exception. See the
|
|
* more extensive comments with fixup_inline_exception below for
|
|
* more information.
|
|
*/
|
|
|
|
extern void __get_user_unknown(void);
|
|
|
|
#define __get_user_nocheck(x, ptr, size) \
|
|
({ \
|
|
long __gu_err = 0; \
|
|
unsigned long __gu_val; \
|
|
__chk_user_ptr(ptr); \
|
|
switch (size) { \
|
|
case 1: \
|
|
__get_user_8(ptr); \
|
|
break; \
|
|
case 2: \
|
|
__get_user_16(ptr); \
|
|
break; \
|
|
case 4: \
|
|
__get_user_32(ptr); \
|
|
break; \
|
|
case 8: \
|
|
__get_user_64(ptr); \
|
|
break; \
|
|
default: \
|
|
__get_user_unknown(); \
|
|
break; \
|
|
} \
|
|
(x) = (__force __typeof__(*(ptr))) __gu_val; \
|
|
__gu_err; \
|
|
})
|
|
|
|
#define __get_user_check(x, ptr, size) \
|
|
({ \
|
|
long __gu_err = -EFAULT; \
|
|
unsigned long __gu_val = 0; \
|
|
const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \
|
|
if (__access_ok((unsigned long)__gu_addr, size)) { \
|
|
__gu_err = 0; \
|
|
switch (size) { \
|
|
case 1: \
|
|
__get_user_8(__gu_addr); \
|
|
break; \
|
|
case 2: \
|
|
__get_user_16(__gu_addr); \
|
|
break; \
|
|
case 4: \
|
|
__get_user_32(__gu_addr); \
|
|
break; \
|
|
case 8: \
|
|
__get_user_64(__gu_addr); \
|
|
break; \
|
|
default: \
|
|
__get_user_unknown(); \
|
|
break; \
|
|
} \
|
|
} \
|
|
(x) = (__force __typeof__(*(ptr))) __gu_val; \
|
|
__gu_err; \
|
|
})
|
|
|
|
struct __large_struct { unsigned long buf[100]; };
|
|
#define __m(x) (*(struct __large_struct __user *)(x))
|
|
|
|
#define __get_user_64(addr) \
|
|
__asm__("1: ldl %0,%2\n" \
|
|
"2:\n" \
|
|
".section __ex_table,\"a\"\n" \
|
|
" .long 1b - .\n" \
|
|
" ldi %0, 2b-1b(%1)\n" \
|
|
".previous" \
|
|
: "=r"(__gu_val), "=r"(__gu_err) \
|
|
: "m"(__m(addr)), "1"(__gu_err))
|
|
|
|
#define __get_user_32(addr) \
|
|
__asm__("1: ldw %0,%2\n" \
|
|
"2:\n" \
|
|
".section __ex_table,\"a\"\n" \
|
|
" .long 1b - .\n" \
|
|
" ldi %0, 2b-1b(%1)\n" \
|
|
".previous" \
|
|
: "=r"(__gu_val), "=r"(__gu_err) \
|
|
: "m"(__m(addr)), "1"(__gu_err))
|
|
|
|
#define __get_user_16(addr) \
|
|
__asm__("1: ldhu %0,%2\n" \
|
|
"2:\n" \
|
|
".section __ex_table,\"a\"\n" \
|
|
" .long 1b - .\n" \
|
|
" ldi %0, 2b-1b(%1)\n" \
|
|
".previous" \
|
|
: "=r"(__gu_val), "=r"(__gu_err) \
|
|
: "m"(__m(addr)), "1"(__gu_err))
|
|
|
|
#define __get_user_8(addr) \
|
|
__asm__("1: ldbu %0,%2\n" \
|
|
"2:\n" \
|
|
".section __ex_table,\"a\"\n" \
|
|
" .long 1b - .\n" \
|
|
" ldi %0, 2b-1b(%1)\n" \
|
|
".previous" \
|
|
: "=r"(__gu_val), "=r"(__gu_err) \
|
|
: "m"(__m(addr)), "1"(__gu_err))
|
|
|
|
extern void __put_user_unknown(void);
|
|
|
|
#define __put_user_nocheck(x, ptr, size) \
|
|
({ \
|
|
long __pu_err = 0; \
|
|
__chk_user_ptr(ptr); \
|
|
switch (size) { \
|
|
case 1: \
|
|
__put_user_8(x, ptr); \
|
|
break; \
|
|
case 2: \
|
|
__put_user_16(x, ptr); \
|
|
break; \
|
|
case 4: \
|
|
__put_user_32(x, ptr); \
|
|
break; \
|
|
case 8: \
|
|
__put_user_64(x, ptr); \
|
|
break; \
|
|
default: \
|
|
__put_user_unknown(); \
|
|
break; \
|
|
} \
|
|
__pu_err; \
|
|
})
|
|
|
|
#define __put_user_check(x, ptr, size) \
|
|
({ \
|
|
long __pu_err = -EFAULT; \
|
|
__typeof__(*(ptr)) __user *__pu_addr = (ptr); \
|
|
if (__access_ok((unsigned long)__pu_addr, size)) { \
|
|
__pu_err = 0; \
|
|
switch (size) { \
|
|
case 1: \
|
|
__put_user_8(x, __pu_addr); \
|
|
break; \
|
|
case 2: \
|
|
__put_user_16(x, __pu_addr); \
|
|
break; \
|
|
case 4: \
|
|
__put_user_32(x, __pu_addr); \
|
|
break; \
|
|
case 8: \
|
|
__put_user_64(x, __pu_addr); \
|
|
break; \
|
|
default: \
|
|
__put_user_unknown(); \
|
|
break; \
|
|
} \
|
|
} \
|
|
__pu_err; \
|
|
})
|
|
|
|
/*
|
|
* The "__put_user_xx()" macros tell gcc they read from memory
|
|
* instead of writing: this is because they do not write to
|
|
* any memory gcc knows about, so there are no aliasing issues
|
|
*/
|
|
#define __put_user_64(x, addr) \
|
|
__asm__ __volatile__("1: stl %r2, %1\n" \
|
|
"2:\n" \
|
|
".section __ex_table, \"a\"\n" \
|
|
" .long 1b - .\n" \
|
|
" ldi $31, 2b-1b(%0)\n" \
|
|
".previous" \
|
|
: "=r"(__pu_err) \
|
|
: "m" (__m(addr)), "rJ" (x), "0"(__pu_err))
|
|
|
|
#define __put_user_32(x, addr) \
|
|
__asm__ __volatile__("1: stw %r2, %1\n" \
|
|
"2:\n" \
|
|
".section __ex_table, \"a\"\n" \
|
|
" .long 1b - .\n" \
|
|
" ldi $31, 2b-1b(%0)\n" \
|
|
".previous" \
|
|
: "=r"(__pu_err) \
|
|
: "m"(__m(addr)), "rJ"(x), "0"(__pu_err))
|
|
|
|
#define __put_user_16(x, addr) \
|
|
__asm__ __volatile__("1: sth %r2, %1\n" \
|
|
"2:\n" \
|
|
".section __ex_table, \"a\"\n" \
|
|
" .long 1b - .\n" \
|
|
" ldi $31, 2b-1b(%0)\n" \
|
|
".previous" \
|
|
: "=r"(__pu_err) \
|
|
: "m"(__m(addr)), "rJ"(x), "0"(__pu_err))
|
|
|
|
#define __put_user_8(x, addr) \
|
|
__asm__ __volatile__("1: stb %r2, %1\n" \
|
|
"2:\n" \
|
|
".section __ex_table, \"a\"\n" \
|
|
" .long 1b - .\n" \
|
|
" ldi $31, 2b-1b(%0)\n" \
|
|
".previous" \
|
|
: "=r"(__pu_err) \
|
|
: "m"(__m(addr)), "rJ"(x), "0"(__pu_err))
|
|
|
|
/*
|
|
* Complex access routines
|
|
*/
|
|
|
|
extern long __copy_user(void *to, const void *from, long len);
|
|
|
|
static inline unsigned long
|
|
raw_copy_from_user(void *to, const void __user *from, unsigned long len)
|
|
{
|
|
return __copy_user(to, (__force const void *)from, len);
|
|
}
|
|
|
|
static inline unsigned long
|
|
raw_copy_to_user(void __user *to, const void *from, unsigned long len)
|
|
{
|
|
return __copy_user((__force void *)to, from, len);
|
|
}
|
|
|
|
extern long __clear_user(void __user *to, long len);
|
|
|
|
static inline long
|
|
clear_user(void __user *to, long len)
|
|
{
|
|
if (__access_ok((unsigned long)to, len))
|
|
len = __clear_user(to, len);
|
|
return len;
|
|
}
|
|
|
|
#define user_addr_max() (uaccess_kernel() ? ~0UL : TASK_SIZE)
|
|
|
|
extern long strncpy_from_user(char *dest, const char __user *src, long count);
|
|
extern __must_check long strlen_user(const char __user *str);
|
|
extern __must_check long strnlen_user(const char __user *str, long n);
|
|
|
|
#include <asm/extable.h>
|
|
#endif /* _ASM_SW64_UACCESS_H */
|