xref: /openbmc/linux/arch/x86/lib/iomem.c (revision 0e6774ec)
1 #include <linux/string.h>
2 #include <linux/module.h>
3 #include <linux/io.h>
4 #include <linux/kmsan-checks.h>
5 
6 #define movs(type,to,from) \
7 	asm volatile("movs" type:"=&D" (to), "=&S" (from):"0" (to), "1" (from):"memory")
8 
9 /* Originally from i386/string.h */
10 static __always_inline void rep_movs(void *to, const void *from, size_t n)
11 {
12 	unsigned long d0, d1, d2;
13 	asm volatile("rep ; movsl\n\t"
14 		     "testb $2,%b4\n\t"
15 		     "je 1f\n\t"
16 		     "movsw\n"
17 		     "1:\ttestb $1,%b4\n\t"
18 		     "je 2f\n\t"
19 		     "movsb\n"
20 		     "2:"
21 		     : "=&c" (d0), "=&D" (d1), "=&S" (d2)
22 		     : "0" (n / 4), "q" (n), "1" ((long)to), "2" ((long)from)
23 		     : "memory");
24 }
25 
26 static void string_memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
27 {
28 	const void *orig_to = to;
29 	const size_t orig_n = n;
30 
31 	if (unlikely(!n))
32 		return;
33 
34 	/* Align any unaligned source IO */
35 	if (unlikely(1 & (unsigned long)from)) {
36 		movs("b", to, from);
37 		n--;
38 	}
39 	if (n > 1 && unlikely(2 & (unsigned long)from)) {
40 		movs("w", to, from);
41 		n-=2;
42 	}
43 	rep_movs(to, (const void *)from, n);
44 	/* KMSAN must treat values read from devices as initialized. */
45 	kmsan_unpoison_memory(orig_to, orig_n);
46 }
47 
48 static void string_memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
49 {
50 	if (unlikely(!n))
51 		return;
52 
53 	/* Make sure uninitialized memory isn't copied to devices. */
54 	kmsan_check_memory(from, n);
55 	/* Align any unaligned destination IO */
56 	if (unlikely(1 & (unsigned long)to)) {
57 		movs("b", to, from);
58 		n--;
59 	}
60 	if (n > 1 && unlikely(2 & (unsigned long)to)) {
61 		movs("w", to, from);
62 		n-=2;
63 	}
64 	rep_movs((void *)to, (const void *) from, n);
65 }
66 
67 static void unrolled_memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
68 {
69 	const volatile char __iomem *in = from;
70 	char *out = to;
71 	int i;
72 
73 	for (i = 0; i < n; ++i)
74 		out[i] = readb(&in[i]);
75 }
76 
77 static void unrolled_memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
78 {
79 	volatile char __iomem *out = to;
80 	const char *in = from;
81 	int i;
82 
83 	for (i = 0; i < n; ++i)
84 		writeb(in[i], &out[i]);
85 }
86 
87 static void unrolled_memset_io(volatile void __iomem *a, int b, size_t c)
88 {
89 	volatile char __iomem *mem = a;
90 	int i;
91 
92 	for (i = 0; i < c; ++i)
93 		writeb(b, &mem[i]);
94 }
95 
96 void memcpy_fromio(void *to, const volatile void __iomem *from, size_t n)
97 {
98 	if (cc_platform_has(CC_ATTR_GUEST_UNROLL_STRING_IO))
99 		unrolled_memcpy_fromio(to, from, n);
100 	else
101 		string_memcpy_fromio(to, from, n);
102 }
103 EXPORT_SYMBOL(memcpy_fromio);
104 
105 void memcpy_toio(volatile void __iomem *to, const void *from, size_t n)
106 {
107 	if (cc_platform_has(CC_ATTR_GUEST_UNROLL_STRING_IO))
108 		unrolled_memcpy_toio(to, from, n);
109 	else
110 		string_memcpy_toio(to, from, n);
111 }
112 EXPORT_SYMBOL(memcpy_toio);
113 
114 void memset_io(volatile void __iomem *a, int b, size_t c)
115 {
116 	if (cc_platform_has(CC_ATTR_GUEST_UNROLL_STRING_IO)) {
117 		unrolled_memset_io(a, b, c);
118 	} else {
119 		/*
120 		 * TODO: memset can mangle the IO patterns quite a bit.
121 		 * perhaps it would be better to use a dumb one:
122 		 */
123 		memset((void *)a, b, c);
124 	}
125 }
126 EXPORT_SYMBOL(memset_io);
127