Skip to content

Commit

Permalink
ARM: 9112/1: uaccess: add __{get,put}_kernel_nofault
Browse files Browse the repository at this point in the history
These mimic the behavior of get_user and put_user, except
for domain switching, address limit checking and handling
of mismatched sizes, none of which are relevant here.

To work with pre-Armv6 kernels, this has to avoid TUSER()
inside of the new macros, the new approach passes the "t"
string along with the opcode, which is a bit uglier but
avoids duplicating more code.

As there is no __get_user_asm_dword(), I work around it
by copying 32 bit at a time, which is possible because
the output size is known.

Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
  • Loading branch information
Arnd Bergmann authored and Russell King (Oracle) committed Aug 20, 2021
1 parent 7e2d8c2 commit 2df4c9a
Showing 1 changed file with 83 additions and 40 deletions.
123 changes: 83 additions & 40 deletions arch/arm/include/asm/uaccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,11 @@ static inline void set_fs(mm_segment_t fs)
#define __get_user(x, ptr) \
({ \
long __gu_err = 0; \
__get_user_err((x), (ptr), __gu_err); \
__get_user_err((x), (ptr), __gu_err, TUSER()); \
__gu_err; \
})

#define __get_user_err(x, ptr, err) \
#define __get_user_err(x, ptr, err, __t) \
do { \
unsigned long __gu_addr = (unsigned long)(ptr); \
unsigned long __gu_val; \
Expand All @@ -321,18 +321,19 @@ do { \
might_fault(); \
__ua_flags = uaccess_save_and_enable(); \
switch (sizeof(*(ptr))) { \
case 1: __get_user_asm_byte(__gu_val, __gu_addr, err); break; \
case 2: __get_user_asm_half(__gu_val, __gu_addr, err); break; \
case 4: __get_user_asm_word(__gu_val, __gu_addr, err); break; \
case 1: __get_user_asm_byte(__gu_val, __gu_addr, err, __t); break; \
case 2: __get_user_asm_half(__gu_val, __gu_addr, err, __t); break; \
case 4: __get_user_asm_word(__gu_val, __gu_addr, err, __t); break; \
default: (__gu_val) = __get_user_bad(); \
} \
uaccess_restore(__ua_flags); \
(x) = (__typeof__(*(ptr)))__gu_val; \
} while (0)
#endif

#define __get_user_asm(x, addr, err, instr) \
__asm__ __volatile__( \
"1: " TUSER(instr) " %1, [%2], #0\n" \
"1: " instr " %1, [%2], #0\n" \
"2:\n" \
" .pushsection .text.fixup,\"ax\"\n" \
" .align 2\n" \
Expand All @@ -348,40 +349,38 @@ do { \
: "r" (addr), "i" (-EFAULT) \
: "cc")

#define __get_user_asm_byte(x, addr, err) \
__get_user_asm(x, addr, err, ldrb)
#define __get_user_asm_byte(x, addr, err, __t) \
__get_user_asm(x, addr, err, "ldrb" __t)

#if __LINUX_ARM_ARCH__ >= 6

#define __get_user_asm_half(x, addr, err) \
__get_user_asm(x, addr, err, ldrh)
#define __get_user_asm_half(x, addr, err, __t) \
__get_user_asm(x, addr, err, "ldrh" __t)

#else

#ifndef __ARMEB__
#define __get_user_asm_half(x, __gu_addr, err) \
#define __get_user_asm_half(x, __gu_addr, err, __t) \
({ \
unsigned long __b1, __b2; \
__get_user_asm_byte(__b1, __gu_addr, err); \
__get_user_asm_byte(__b2, __gu_addr + 1, err); \
__get_user_asm_byte(__b1, __gu_addr, err, __t); \
__get_user_asm_byte(__b2, __gu_addr + 1, err, __t); \
(x) = __b1 | (__b2 << 8); \
})
#else
#define __get_user_asm_half(x, __gu_addr, err) \
#define __get_user_asm_half(x, __gu_addr, err, __t) \
({ \
unsigned long __b1, __b2; \
__get_user_asm_byte(__b1, __gu_addr, err); \
__get_user_asm_byte(__b2, __gu_addr + 1, err); \
__get_user_asm_byte(__b1, __gu_addr, err, __t); \
__get_user_asm_byte(__b2, __gu_addr + 1, err, __t); \
(x) = (__b1 << 8) | __b2; \
})
#endif

#endif /* __LINUX_ARM_ARCH__ >= 6 */

#define __get_user_asm_word(x, addr, err) \
__get_user_asm(x, addr, err, ldr)
#endif

#define __get_user_asm_word(x, addr, err, __t) \
__get_user_asm(x, addr, err, "ldr" __t)

#define __put_user_switch(x, ptr, __err, __fn) \
do { \
Expand Down Expand Up @@ -425,17 +424,19 @@ do { \
#define __put_user_nocheck(x, __pu_ptr, __err, __size) \
do { \
unsigned long __pu_addr = (unsigned long)__pu_ptr; \
__put_user_nocheck_##__size(x, __pu_addr, __err); \
__put_user_nocheck_##__size(x, __pu_addr, __err, TUSER());\
} while (0)

#define __put_user_nocheck_1 __put_user_asm_byte
#define __put_user_nocheck_2 __put_user_asm_half
#define __put_user_nocheck_4 __put_user_asm_word
#define __put_user_nocheck_8 __put_user_asm_dword

#endif /* !CONFIG_CPU_SPECTRE */

#define __put_user_asm(x, __pu_addr, err, instr) \
__asm__ __volatile__( \
"1: " TUSER(instr) " %1, [%2], #0\n" \
"1: " instr " %1, [%2], #0\n" \
"2:\n" \
" .pushsection .text.fixup,\"ax\"\n" \
" .align 2\n" \
Expand All @@ -450,36 +451,36 @@ do { \
: "r" (x), "r" (__pu_addr), "i" (-EFAULT) \
: "cc")

#define __put_user_asm_byte(x, __pu_addr, err) \
__put_user_asm(x, __pu_addr, err, strb)
#define __put_user_asm_byte(x, __pu_addr, err, __t) \
__put_user_asm(x, __pu_addr, err, "strb" __t)

#if __LINUX_ARM_ARCH__ >= 6

#define __put_user_asm_half(x, __pu_addr, err) \
__put_user_asm(x, __pu_addr, err, strh)
#define __put_user_asm_half(x, __pu_addr, err, __t) \
__put_user_asm(x, __pu_addr, err, "strh" __t)

#else

#ifndef __ARMEB__
#define __put_user_asm_half(x, __pu_addr, err) \
#define __put_user_asm_half(x, __pu_addr, err, __t) \
({ \
unsigned long __temp = (__force unsigned long)(x); \
__put_user_asm_byte(__temp, __pu_addr, err); \
__put_user_asm_byte(__temp >> 8, __pu_addr + 1, err); \
__put_user_asm_byte(__temp, __pu_addr, err, __t); \
__put_user_asm_byte(__temp >> 8, __pu_addr + 1, err, __t);\
})
#else
#define __put_user_asm_half(x, __pu_addr, err) \
#define __put_user_asm_half(x, __pu_addr, err, __t) \
({ \
unsigned long __temp = (__force unsigned long)(x); \
__put_user_asm_byte(__temp >> 8, __pu_addr, err); \
__put_user_asm_byte(__temp, __pu_addr + 1, err); \
__put_user_asm_byte(__temp >> 8, __pu_addr, err, __t); \
__put_user_asm_byte(__temp, __pu_addr + 1, err, __t); \
})
#endif

#endif /* __LINUX_ARM_ARCH__ >= 6 */

#define __put_user_asm_word(x, __pu_addr, err) \
__put_user_asm(x, __pu_addr, err, str)
#define __put_user_asm_word(x, __pu_addr, err, __t) \
__put_user_asm(x, __pu_addr, err, "str" __t)

#ifndef __ARMEB__
#define __reg_oper0 "%R2"
Expand All @@ -489,12 +490,12 @@ do { \
#define __reg_oper1 "%R2"
#endif

#define __put_user_asm_dword(x, __pu_addr, err) \
#define __put_user_asm_dword(x, __pu_addr, err, __t) \
__asm__ __volatile__( \
ARM( "1: " TUSER(str) " " __reg_oper1 ", [%1], #4\n" ) \
ARM( "2: " TUSER(str) " " __reg_oper0 ", [%1]\n" ) \
THUMB( "1: " TUSER(str) " " __reg_oper1 ", [%1]\n" ) \
THUMB( "2: " TUSER(str) " " __reg_oper0 ", [%1, #4]\n" ) \
ARM( "1: str" __t " " __reg_oper1 ", [%1], #4\n" ) \
ARM( "2: str" __t " " __reg_oper0 ", [%1]\n" ) \
THUMB( "1: str" __t " " __reg_oper1 ", [%1]\n" ) \
THUMB( "2: str" __t " " __reg_oper0 ", [%1, #4]\n" ) \
"3:\n" \
" .pushsection .text.fixup,\"ax\"\n" \
" .align 2\n" \
Expand All @@ -510,7 +511,49 @@ do { \
: "r" (x), "i" (-EFAULT) \
: "cc")

#endif /* !CONFIG_CPU_SPECTRE */
#define HAVE_GET_KERNEL_NOFAULT

#define __get_kernel_nofault(dst, src, type, err_label) \
do { \
const type *__pk_ptr = (src); \
unsigned long __src = (unsigned long)(__pk_ptr); \
type __val; \
int __err = 0; \
switch (sizeof(type)) { \
case 1: __get_user_asm_byte(__val, __src, __err, ""); break; \
case 2: __get_user_asm_half(__val, __src, __err, ""); break; \
case 4: __get_user_asm_word(__val, __src, __err, ""); break; \
case 8: { \
u32 *__v32 = (u32*)&__val; \
__get_user_asm_word(__v32[0], __src, __err, ""); \
if (__err) \
break; \
__get_user_asm_word(__v32[1], __src+4, __err, ""); \
break; \
} \
default: __err = __get_user_bad(); break; \
} \
*(type *)(dst) = __val; \
if (__err) \
goto err_label; \
} while (0)

#define __put_kernel_nofault(dst, src, type, err_label) \
do { \
const type *__pk_ptr = (dst); \
unsigned long __dst = (unsigned long)__pk_ptr; \
int __err = 0; \
type __val = *(type *)src; \
switch (sizeof(type)) { \
case 1: __put_user_asm_byte(__val, __dst, __err, ""); break; \
case 2: __put_user_asm_half(__val, __dst, __err, ""); break; \
case 4: __put_user_asm_word(__val, __dst, __err, ""); break; \
case 8: __put_user_asm_dword(__val, __dst, __err, ""); break; \
default: __err = __put_user_bad(); break; \
} \
if (__err) \
goto err_label; \
} while (0)

#ifdef CONFIG_MMU
extern unsigned long __must_check
Expand Down

0 comments on commit 2df4c9a

Please sign in to comment.