/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_X86_UACCESS_64_H
#define _ASM_X86_UACCESS_64_H


#include <linux/compiler.h>
#include <linux/lockdep.h>
#include <linux/kasan-checks.h>
#include <asm/alternative.h>
#include <asm/cpufeatures.h>
#include <asm/page.h>
#include <asm/percpu.h>

#ifdef MODULE
  #define runtime_const_ptr(sym) (sym)
#else
  #include <asm/runtime-const.h>
#endif
extern unsigned long USER_PTR_MAX;

#ifdef CONFIG_ADDRESS_MASKING

static inline unsigned long __untagged_addr(unsigned long addr)
{
	asm_inline (ALTERNATIVE("", "and " __percpu_arg([mask]) ", %[addr]",
				X86_FEATURE_LAM)
	     : [addr] "+r" (addr)
	     : [mask] "m" (__my_cpu_var(tlbstate_untag_mask)));

	return addr;
}

#define untagged_addr(addr)	({					\
	unsigned long __addr = (__force unsigned long)(addr);		\
	(__force __typeof__(addr))__untagged_addr(__addr);		\
})

static inline unsigned long __untagged_addr_remote(struct mm_struct *mm,
						   unsigned long addr)
{
	mmap_assert_locked(mm);
	return addr & (mm)->context.untag_mask;
}

#define untagged_addr_remote(mm, addr)	({				\
	unsigned long __addr = (__force unsigned long)(addr);		\
	(__force __typeof__(addr))__untagged_addr_remote(mm, __addr);	\
})

#endif

#define valid_user_address(x) \
	likely((__force unsigned long)(x) <= runtime_const_ptr(USER_PTR_MAX))


static inline void __user *mask_user_address(const void __user *ptr)
{
	void __user *ret;
	asm("cmp %1,%0\n\t"
	    "cmova %1,%0"
		:"=r" (ret)
		:"r" (runtime_const_ptr(USER_PTR_MAX)),
		 "0" (ptr));
	return ret;
}
#define masked_user_access_begin(x) ({				\
	auto __masked_ptr = (x);				\
	__masked_ptr = mask_user_address(__masked_ptr);		\
	__uaccess_begin(); __masked_ptr; })


static inline bool __access_ok(const void __user *ptr, unsigned long size)
{
	if (__builtin_constant_p(size <= PAGE_SIZE) && size <= PAGE_SIZE) {
		return valid_user_address(ptr);
	} else {
		unsigned long sum = size + (__force unsigned long)ptr;

		return valid_user_address(sum) && sum >= (__force unsigned long)ptr;
	}
}
#define __access_ok __access_ok




__must_check unsigned long
rep_movs_alternative(void *to, const void *from, unsigned len);

static __always_inline __must_check unsigned long
copy_user_generic(void *to, const void *from, unsigned long len)
{
	stac();
	
	asm volatile(
		"1:\n\t"
		ALTERNATIVE("rep movsb",
			    "call rep_movs_alternative", ALT_NOT(X86_FEATURE_FSRM))
		"2:\n"
		_ASM_EXTABLE_UA(1b, 2b)
		:"+c" (len), "+D" (to), "+S" (from), ASM_CALL_CONSTRAINT
		: : "memory", "rax");
	clac();
	return len;
}

static __always_inline __must_check unsigned long
raw_copy_from_user(void *dst, const void __user *src, unsigned long size)
{
	return copy_user_generic(dst, (__force void *)src, size);
}

static __always_inline __must_check unsigned long
raw_copy_to_user(void __user *dst, const void *src, unsigned long size)
{
	return copy_user_generic((__force void *)dst, src, size);
}

#define copy_to_nontemporal copy_to_nontemporal
extern size_t copy_to_nontemporal(void *dst, const void *src, size_t size);
extern size_t copy_user_flushcache(void *dst, const void __user *src, size_t size);

static inline int
copy_from_user_inatomic_nontemporal(void *dst, const void __user *src,
				  unsigned size)
{
	long ret;
	kasan_check_write(dst, size);
	src = mask_user_address(src);
	stac();
	ret = copy_to_nontemporal(dst, (__force const void *)src, size);
	clac();
	return ret;
}

static inline size_t
copy_from_user_flushcache(void *dst, const void __user *src, size_t size)
{
	kasan_check_write(dst, size);
	return copy_user_flushcache(dst, src, size);
}



__must_check unsigned long
rep_stos_alternative(void __user *addr, unsigned long len);

static __always_inline __must_check unsigned long __clear_user(void __user *addr, unsigned long size)
{
	might_fault();
	stac();

	
	asm volatile(
		"1:\n\t"
		ALTERNATIVE("rep stosb",
			    "call rep_stos_alternative", ALT_NOT(X86_FEATURE_FSRS))
		"2:\n"
	       _ASM_EXTABLE_UA(1b, 2b)
	       : "+c" (size), "+D" (addr), ASM_CALL_CONSTRAINT
	       : "a" (0));

	clac();

	return size;
}

static __always_inline unsigned long clear_user(void __user *to, unsigned long n)
{
	if (__access_ok(to, n))
		return __clear_user(to, n);
	return n;
}
#endif 
