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