diff options
Diffstat (limited to 'arch/powerpc/include/asm/uaccess.h')
| -rw-r--r-- | arch/powerpc/include/asm/uaccess.h | 67 |
1 files changed, 46 insertions, 21 deletions
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h index ba1d878c3f40..17e63244e885 100644 --- a/arch/powerpc/include/asm/uaccess.h +++ b/arch/powerpc/include/asm/uaccess.h @@ -15,6 +15,9 @@ #define TASK_SIZE_MAX TASK_SIZE_USER64 #endif +/* Threshold above which VMX copy path is used */ +#define VMX_COPY_THRESHOLD 3328 + #include <asm-generic/access_ok.h> /* @@ -255,7 +258,7 @@ __gus_failed: \ ".section .fixup,\"ax\"\n" \ "4: li %0,%3\n" \ " li %1,0\n" \ - " li %1+1,0\n" \ + " li %L1,0\n" \ " b 3b\n" \ ".previous\n" \ EX_TABLE(1b, 4b) \ @@ -326,40 +329,62 @@ do { \ extern unsigned long __copy_tofrom_user(void __user *to, const void __user *from, unsigned long size); -#ifdef __powerpc64__ -static inline unsigned long -raw_copy_in_user(void __user *to, const void __user *from, unsigned long n) +unsigned long __copy_tofrom_user_base(void __user *to, + const void __user *from, unsigned long size); + +unsigned long __copy_tofrom_user_power7_vmx(void __user *to, + const void __user *from, unsigned long size); + +static __always_inline bool will_use_vmx(unsigned long n) +{ + return IS_ENABLED(CONFIG_ALTIVEC) && cpu_has_feature(CPU_FTR_VMX_COPY) && + n > VMX_COPY_THRESHOLD; +} + +static __always_inline unsigned long +raw_copy_tofrom_user(void __user *to, const void __user *from, + unsigned long n, unsigned long dir) { unsigned long ret; - barrier_nospec(); - allow_user_access(to, KUAP_READ_WRITE); + if (will_use_vmx(n) && enter_vmx_usercopy()) { + allow_user_access(to, dir); + ret = __copy_tofrom_user_power7_vmx(to, from, n); + prevent_user_access(dir); + exit_vmx_usercopy(); + + if (unlikely(ret)) { + allow_user_access(to, dir); + ret = __copy_tofrom_user_base(to, from, n); + prevent_user_access(dir); + } + return ret; + } + + allow_user_access(to, dir); ret = __copy_tofrom_user(to, from, n); - prevent_user_access(KUAP_READ_WRITE); + prevent_user_access(dir); return ret; } -#endif /* __powerpc64__ */ -static inline unsigned long raw_copy_from_user(void *to, - const void __user *from, unsigned long n) +#ifdef CONFIG_PPC64 +static inline unsigned long +raw_copy_in_user(void __user *to, const void __user *from, unsigned long n) { - unsigned long ret; + barrier_nospec(); + return raw_copy_tofrom_user(to, from, n, KUAP_READ_WRITE); +} +#endif /* CONFIG_PPC64 */ - allow_user_access(NULL, KUAP_READ); - ret = __copy_tofrom_user((__force void __user *)to, from, n); - prevent_user_access(KUAP_READ); - return ret; +static inline unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n) +{ + return raw_copy_tofrom_user((__force void __user *)to, from, n, KUAP_READ); } static inline unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n) { - unsigned long ret; - - allow_user_access(to, KUAP_WRITE); - ret = __copy_tofrom_user(to, (__force const void __user *)from, n); - prevent_user_access(KUAP_WRITE); - return ret; + return raw_copy_tofrom_user(to, (__force const void __user *)from, n, KUAP_WRITE); } unsigned long __arch_clear_user(void __user *addr, unsigned long size); |
