162306a36Sopenharmony_ci#include <linux/string.h>
262306a36Sopenharmony_ci#include <linux/module.h>
362306a36Sopenharmony_ci#include <linux/io.h>
462306a36Sopenharmony_ci#include <linux/kmsan-checks.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define movs(type,to,from) \
762306a36Sopenharmony_ci	asm volatile("movs" type:"=&D" (to), "=&S" (from):"0" (to), "1" (from):"memory")
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/* Originally from i386/string.h */
1062306a36Sopenharmony_cistatic __always_inline void rep_movs(void *to, const void *from, size_t n)
1162306a36Sopenharmony_ci{
1262306a36Sopenharmony_ci	unsigned long d0, d1, d2;
1362306a36Sopenharmony_ci	asm volatile("rep ; movsl\n\t"
1462306a36Sopenharmony_ci		     "testb $2,%b4\n\t"
1562306a36Sopenharmony_ci		     "je 1f\n\t"
1662306a36Sopenharmony_ci		     "movsw\n"
1762306a36Sopenharmony_ci		     "1:\ttestb $1,%b4\n\t"
1862306a36Sopenharmony_ci		     "je 2f\n\t"
1962306a36Sopenharmony_ci		     "movsb\n"
2062306a36Sopenharmony_ci		     "2:"
2162306a36Sopenharmony_ci		     : "=&c" (d0), "=&D" (d1), "=&S" (d2)
2262306a36Sopenharmony_ci		     : "0" (n / 4), "q" (n), "1" ((long)to), "2" ((long)from)
2362306a36Sopenharmony_ci		     : "memory");
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic void string_memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	if (unlikely(!n))
2962306a36Sopenharmony_ci		return;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	/* Align any unaligned source IO */
3262306a36Sopenharmony_ci	if (unlikely(1 & (unsigned long)from)) {
3362306a36Sopenharmony_ci		movs("b", to, from);
3462306a36Sopenharmony_ci		n--;
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci	if (n > 1 && unlikely(2 & (unsigned long)from)) {
3762306a36Sopenharmony_ci		movs("w", to, from);
3862306a36Sopenharmony_ci		n-=2;
3962306a36Sopenharmony_ci	}
4062306a36Sopenharmony_ci	rep_movs(to, (const void *)from, n);
4162306a36Sopenharmony_ci	/* KMSAN must treat values read from devices as initialized. */
4262306a36Sopenharmony_ci	kmsan_unpoison_memory(to, n);
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void string_memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	if (unlikely(!n))
4862306a36Sopenharmony_ci		return;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	/* Make sure uninitialized memory isn't copied to devices. */
5162306a36Sopenharmony_ci	kmsan_check_memory(from, n);
5262306a36Sopenharmony_ci	/* Align any unaligned destination IO */
5362306a36Sopenharmony_ci	if (unlikely(1 & (unsigned long)to)) {
5462306a36Sopenharmony_ci		movs("b", to, from);
5562306a36Sopenharmony_ci		n--;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci	if (n > 1 && unlikely(2 & (unsigned long)to)) {
5862306a36Sopenharmony_ci		movs("w", to, from);
5962306a36Sopenharmony_ci		n-=2;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci	rep_movs((void *)to, (const void *) from, n);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void unrolled_memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	const volatile char __iomem *in = from;
6762306a36Sopenharmony_ci	char *out = to;
6862306a36Sopenharmony_ci	int i;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	for (i = 0; i < n; ++i)
7162306a36Sopenharmony_ci		out[i] = readb(&in[i]);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void unrolled_memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	volatile char __iomem *out = to;
7762306a36Sopenharmony_ci	const char *in = from;
7862306a36Sopenharmony_ci	int i;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	for (i = 0; i < n; ++i)
8162306a36Sopenharmony_ci		writeb(in[i], &out[i]);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic void unrolled_memset_io(volatile void __iomem *a, int b, size_t c)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	volatile char __iomem *mem = a;
8762306a36Sopenharmony_ci	int i;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	for (i = 0; i < c; ++i)
9062306a36Sopenharmony_ci		writeb(b, &mem[i]);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_civoid memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	if (cc_platform_has(CC_ATTR_GUEST_UNROLL_STRING_IO))
9662306a36Sopenharmony_ci		unrolled_memcpy_fromio(to, from, n);
9762306a36Sopenharmony_ci	else
9862306a36Sopenharmony_ci		string_memcpy_fromio(to, from, n);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ciEXPORT_SYMBOL(memcpy_fromio);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_civoid memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	if (cc_platform_has(CC_ATTR_GUEST_UNROLL_STRING_IO))
10562306a36Sopenharmony_ci		unrolled_memcpy_toio(to, from, n);
10662306a36Sopenharmony_ci	else
10762306a36Sopenharmony_ci		string_memcpy_toio(to, from, n);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ciEXPORT_SYMBOL(memcpy_toio);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_civoid memset_io(volatile void __iomem *a, int b, size_t c)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	if (cc_platform_has(CC_ATTR_GUEST_UNROLL_STRING_IO)) {
11462306a36Sopenharmony_ci		unrolled_memset_io(a, b, c);
11562306a36Sopenharmony_ci	} else {
11662306a36Sopenharmony_ci		/*
11762306a36Sopenharmony_ci		 * TODO: memset can mangle the IO patterns quite a bit.
11862306a36Sopenharmony_ci		 * perhaps it would be better to use a dumb one:
11962306a36Sopenharmony_ci		 */
12062306a36Sopenharmony_ci		memset((void *)a, b, c);
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ciEXPORT_SYMBOL(memset_io);
124