xref: /openbmc/linux/arch/x86/lib/iomem.c (revision ca2478a7d974f38d29d27acb42a952c7f168916e)
1170d13caSLinus Torvalds #include <linux/string.h>
2170d13caSLinus Torvalds #include <linux/module.h>
3170d13caSLinus Torvalds #include <linux/io.h>
49245ec01SAlexander Potapenko #include <linux/kmsan-checks.h>
5170d13caSLinus Torvalds 
6c228d294SLinus Torvalds #define movs(type,to,from) \
7c228d294SLinus Torvalds 	asm volatile("movs" type:"=&D" (to), "=&S" (from):"0" (to), "1" (from):"memory")
8c228d294SLinus Torvalds 
9170d13caSLinus Torvalds /* Originally from i386/string.h */
rep_movs(void * to,const void * from,size_t n)10c228d294SLinus Torvalds static __always_inline void rep_movs(void *to, const void *from, size_t n)
11170d13caSLinus Torvalds {
12170d13caSLinus Torvalds 	unsigned long d0, d1, d2;
13170d13caSLinus Torvalds 	asm volatile("rep ; movsl\n\t"
14170d13caSLinus Torvalds 		     "testb $2,%b4\n\t"
15170d13caSLinus Torvalds 		     "je 1f\n\t"
16170d13caSLinus Torvalds 		     "movsw\n"
17170d13caSLinus Torvalds 		     "1:\ttestb $1,%b4\n\t"
18170d13caSLinus Torvalds 		     "je 2f\n\t"
19170d13caSLinus Torvalds 		     "movsb\n"
20170d13caSLinus Torvalds 		     "2:"
21170d13caSLinus Torvalds 		     : "=&c" (d0), "=&D" (d1), "=&S" (d2)
22170d13caSLinus Torvalds 		     : "0" (n / 4), "q" (n), "1" ((long)to), "2" ((long)from)
23170d13caSLinus Torvalds 		     : "memory");
24170d13caSLinus Torvalds }
25170d13caSLinus Torvalds 
string_memcpy_fromio(void * to,const volatile void __iomem * from,size_t n)264009a4acSJoerg Roedel static void string_memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
27170d13caSLinus Torvalds {
28*4ebd15abSBrian Johannesmeyer 	const void *orig_to = to;
29*4ebd15abSBrian Johannesmeyer 	const size_t orig_n = n;
30*4ebd15abSBrian Johannesmeyer 
31c228d294SLinus Torvalds 	if (unlikely(!n))
32c228d294SLinus Torvalds 		return;
33c228d294SLinus Torvalds 
34c228d294SLinus Torvalds 	/* Align any unaligned source IO */
35c228d294SLinus Torvalds 	if (unlikely(1 & (unsigned long)from)) {
36c228d294SLinus Torvalds 		movs("b", to, from);
37c228d294SLinus Torvalds 		n--;
38c228d294SLinus Torvalds 	}
39c228d294SLinus Torvalds 	if (n > 1 && unlikely(2 & (unsigned long)from)) {
40c228d294SLinus Torvalds 		movs("w", to, from);
41c228d294SLinus Torvalds 		n-=2;
42c228d294SLinus Torvalds 	}
43c228d294SLinus Torvalds 	rep_movs(to, (const void *)from, n);
449245ec01SAlexander Potapenko 	/* KMSAN must treat values read from devices as initialized. */
45*4ebd15abSBrian Johannesmeyer 	kmsan_unpoison_memory(orig_to, orig_n);
46170d13caSLinus Torvalds }
47170d13caSLinus Torvalds 
string_memcpy_toio(volatile void __iomem * to,const void * from,size_t n)484009a4acSJoerg Roedel static void string_memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
49170d13caSLinus Torvalds {
50c228d294SLinus Torvalds 	if (unlikely(!n))
51c228d294SLinus Torvalds 		return;
52c228d294SLinus Torvalds 
539245ec01SAlexander Potapenko 	/* Make sure uninitialized memory isn't copied to devices. */
549245ec01SAlexander Potapenko 	kmsan_check_memory(from, n);
55c228d294SLinus Torvalds 	/* Align any unaligned destination IO */
56c228d294SLinus Torvalds 	if (unlikely(1 & (unsigned long)to)) {
57c228d294SLinus Torvalds 		movs("b", to, from);
58c228d294SLinus Torvalds 		n--;
59c228d294SLinus Torvalds 	}
60c228d294SLinus Torvalds 	if (n > 1 && unlikely(2 & (unsigned long)to)) {
61c228d294SLinus Torvalds 		movs("w", to, from);
62c228d294SLinus Torvalds 		n-=2;
63c228d294SLinus Torvalds 	}
64c228d294SLinus Torvalds 	rep_movs((void *)to, (const void *) from, n);
65170d13caSLinus Torvalds }
664009a4acSJoerg Roedel 
unrolled_memcpy_fromio(void * to,const volatile void __iomem * from,size_t n)674009a4acSJoerg Roedel static void unrolled_memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
684009a4acSJoerg Roedel {
694009a4acSJoerg Roedel 	const volatile char __iomem *in = from;
704009a4acSJoerg Roedel 	char *out = to;
714009a4acSJoerg Roedel 	int i;
724009a4acSJoerg Roedel 
734009a4acSJoerg Roedel 	for (i = 0; i < n; ++i)
744009a4acSJoerg Roedel 		out[i] = readb(&in[i]);
754009a4acSJoerg Roedel }
764009a4acSJoerg Roedel 
unrolled_memcpy_toio(volatile void __iomem * to,const void * from,size_t n)774009a4acSJoerg Roedel static void unrolled_memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
784009a4acSJoerg Roedel {
794009a4acSJoerg Roedel 	volatile char __iomem *out = to;
804009a4acSJoerg Roedel 	const char *in = from;
814009a4acSJoerg Roedel 	int i;
824009a4acSJoerg Roedel 
834009a4acSJoerg Roedel 	for (i = 0; i < n; ++i)
844009a4acSJoerg Roedel 		writeb(in[i], &out[i]);
854009a4acSJoerg Roedel }
864009a4acSJoerg Roedel 
unrolled_memset_io(volatile void __iomem * a,int b,size_t c)874009a4acSJoerg Roedel static void unrolled_memset_io(volatile void __iomem *a, int b, size_t c)
884009a4acSJoerg Roedel {
894009a4acSJoerg Roedel 	volatile char __iomem *mem = a;
904009a4acSJoerg Roedel 	int i;
914009a4acSJoerg Roedel 
924009a4acSJoerg Roedel 	for (i = 0; i < c; ++i)
934009a4acSJoerg Roedel 		writeb(b, &mem[i]);
944009a4acSJoerg Roedel }
954009a4acSJoerg Roedel 
memcpy_fromio(void * to,const volatile void __iomem * from,size_t n)964009a4acSJoerg Roedel void memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
974009a4acSJoerg Roedel {
984009a4acSJoerg Roedel 	if (cc_platform_has(CC_ATTR_GUEST_UNROLL_STRING_IO))
994009a4acSJoerg Roedel 		unrolled_memcpy_fromio(to, from, n);
1004009a4acSJoerg Roedel 	else
1014009a4acSJoerg Roedel 		string_memcpy_fromio(to, from, n);
1024009a4acSJoerg Roedel }
1034009a4acSJoerg Roedel EXPORT_SYMBOL(memcpy_fromio);
1044009a4acSJoerg Roedel 
memcpy_toio(volatile void __iomem * to,const void * from,size_t n)1054009a4acSJoerg Roedel void memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
1064009a4acSJoerg Roedel {
1074009a4acSJoerg Roedel 	if (cc_platform_has(CC_ATTR_GUEST_UNROLL_STRING_IO))
1084009a4acSJoerg Roedel 		unrolled_memcpy_toio(to, from, n);
1094009a4acSJoerg Roedel 	else
1104009a4acSJoerg Roedel 		string_memcpy_toio(to, from, n);
1114009a4acSJoerg Roedel }
112170d13caSLinus Torvalds EXPORT_SYMBOL(memcpy_toio);
113170d13caSLinus Torvalds 
memset_io(volatile void __iomem * a,int b,size_t c)114170d13caSLinus Torvalds void memset_io(volatile void __iomem *a, int b, size_t c)
115170d13caSLinus Torvalds {
1164009a4acSJoerg Roedel 	if (cc_platform_has(CC_ATTR_GUEST_UNROLL_STRING_IO)) {
1174009a4acSJoerg Roedel 		unrolled_memset_io(a, b, c);
1184009a4acSJoerg Roedel 	} else {
119170d13caSLinus Torvalds 		/*
120170d13caSLinus Torvalds 		 * TODO: memset can mangle the IO patterns quite a bit.
121170d13caSLinus Torvalds 		 * perhaps it would be better to use a dumb one:
122170d13caSLinus Torvalds 		 */
123170d13caSLinus Torvalds 		memset((void *)a, b, c);
124170d13caSLinus Torvalds 	}
1254009a4acSJoerg Roedel }
126170d13caSLinus Torvalds EXPORT_SYMBOL(memset_io);
127