Introduce memcpy_fromio() and memcpy_toio() helpers to copy between regular memory and MMIO space on Arm. The generic prototypes live in io.h so other architectures can provide their own implementations.
These helpers handle alignment safely by using ordered byte accesses for any leading/trailing unaligned bytes and ordered 32-bit accesses for the aligned bulk transfer. Using the ordered `readb/readl` and `writeb/writel` accessors avoids unintended endianness conversion while respecting device ordering requirements on ARM32/ARM64 hardware that may not support 64-bit MMIO atomically. The interface lives in the generic header so other architectures can provide their own implementations (as macros or functions). The ARM implementation is placed under `arch/arm/lib/` (mirroring the x86 reference layout) and is split into separate compilation units added via the architecture-specific lib Makefile. Signed-off-by: Oleksii Moisieiev <[email protected]> --- Changes in v9: - reword commit description to refer to memcpy_fromio and memcpy_toio - ordering obj-y in Makefile - rename ALL_LIBS to ARCH_LIBS - drop io.h and move definitions to the common header, fix comments to be arch neutral - update comments for memcpy_{from/to}io implementation Changes in v8: - switched to ordered accessors to address the ordering and barrier concerns. - updated the documentation to match the implementation and explicitly state the supported access sizes and granularity. - rename memcpy_* implementation files to memcpu-* to follow naming convension - fix indentation to match Xen style - fix intendation to match Xen style - move memcpy-{from/to}io to more convenient library place Changes in v7: - x86 guidance: removed the speculative note; header now just says each arch supplies its own implementation or macro. - name spacing: dropped the double-underscore; the helpers are now memcpy_fromio / memcpy_toio. The header also explicitly allows an arch to define these as macros before including it. - updated io.c to keep 32-bit transfers safe on arm32 - moved to __raw_read*/__raw_write* accessors to avoid endianness conversion. - split the helpers into separate compilation units Changes in v6: - sorted objs in Makefile alhabetically - added newline at the end of Makefile - used uint{N}_t intead of u{N} - add comment about why 32 bit IO operations were used - updated cast opertaions to avoid dropping constness which is wrong - move function definitions to generic place so the could be reused by other arch - add SPDX tag to io.c Changes in v5: - move memcpy_toio/fromio to the generic place xen/arch/arm/Makefile | 1 + xen/arch/arm/arch.mk | 1 + xen/arch/arm/lib/Makefile | 2 ++ xen/arch/arm/lib/memcpy-fromio.c | 56 ++++++++++++++++++++++++++++++++ xen/arch/arm/lib/memcpy-toio.c | 56 ++++++++++++++++++++++++++++++++ xen/include/xen/io.h | 10 ++++++ 6 files changed, 126 insertions(+) create mode 100644 xen/arch/arm/lib/Makefile create mode 100644 xen/arch/arm/lib/memcpy-fromio.c create mode 100644 xen/arch/arm/lib/memcpy-toio.c diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 7494a0f926..5b8e170e01 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -8,6 +8,7 @@ ifneq ($(CONFIG_NO_PLAT),y) obj-y += platforms/ endif obj-y += firmware/ +obj-y += lib/ obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_HAS_VPCI) += vpci.o diff --git a/xen/arch/arm/arch.mk b/xen/arch/arm/arch.mk index dea8dbd18a..009bb22c45 100644 --- a/xen/arch/arm/arch.mk +++ b/xen/arch/arm/arch.mk @@ -2,6 +2,7 @@ # arm-specific definitions ARCH_LIBS-y += arch/arm/$(ARCH)/lib/lib.a +ARCH_LIBS-y += arch/arm/lib/lib.a $(call cc-options-add,CFLAGS,CC,$(EMBEDDED_EXTRA_CFLAGS)) $(call cc-option-add,CFLAGS,CC,-Wnested-externs) diff --git a/xen/arch/arm/lib/Makefile b/xen/arch/arm/lib/Makefile new file mode 100644 index 0000000000..07a0d9186c --- /dev/null +++ b/xen/arch/arm/lib/Makefile @@ -0,0 +1,2 @@ +lib-y += memcpy-fromio.o +lib-y += memcpy-toio.o diff --git a/xen/arch/arm/lib/memcpy-fromio.c b/xen/arch/arm/lib/memcpy-fromio.c new file mode 100644 index 0000000000..1b13cf5ff8 --- /dev/null +++ b/xen/arch/arm/lib/memcpy-fromio.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include <xen/io.h> + +#include <asm/io.h> + +/* + * Arm implementation notes / limitations: + * - Uses ordered 8-bit for leading/trailing unaligned bytes and ordered + * 32-bit accesses for the aligned bulk; no wider accesses are issued. + * - Only suitable for devices that tolerate 8-bit and 32-bit accesses; + * do not use with devices requiring strictly 16-bit or 64-bit accesses. + * - MMIO must be mapped with appropriate device attributes to preserve + * ordering; no extra barriers beyond the ordered accessors are added. + * - If source or destination is misaligned, leading bytes are copied + * byte-by-byte until both sides are 32-bit aligned, then bulk copy uses + * 32-bit accesses. + */ + +void memcpy_fromio(void *to, const volatile void __iomem *from, + size_t count) +{ + while ( count && (!IS_ALIGNED((unsigned long)from, 4) || + !IS_ALIGNED((unsigned long)to, 4)) ) + { + *(uint8_t *)to = readb(from); + from++; + to++; + count--; + } + + while ( count >= 4 ) + { + *(uint32_t *)to = readl(from); + from += 4; + to += 4; + count -= 4; + } + + while ( count ) + { + *(uint8_t *)to = readb(from); + from++; + to++; + count--; + } +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/xen/arch/arm/lib/memcpy-toio.c b/xen/arch/arm/lib/memcpy-toio.c new file mode 100644 index 0000000000..2554ac3efc --- /dev/null +++ b/xen/arch/arm/lib/memcpy-toio.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include <xen/io.h> + +#include <asm/io.h> + +/* + * Arm implementation notes / limitations: + * - Uses ordered 8-bit for leading/trailing unaligned bytes and ordered + * 32-bit accesses for the aligned bulk; no wider accesses are issued. + * - Only suitable for devices that tolerate 8-bit and 32-bit accesses; + * do not use with devices requiring strictly 16-bit or 64-bit accesses. + * - MMIO must be mapped with appropriate device attributes to preserve + * ordering; no extra barriers beyond the ordered accessors are added. + * - If source or destination is misaligned, leading bytes are copied + * byte-by-byte until both sides are 32-bit aligned, then bulk copy uses + * 32-bit accesses. + */ + +void memcpy_toio(volatile void __iomem *to, const void *from, + size_t count) +{ + while ( count && (!IS_ALIGNED((unsigned long)to, 4) || + !IS_ALIGNED((unsigned long)from, 4)) ) + { + writeb(*(const uint8_t *)from, to); + from++; + to++; + count--; + } + + while ( count >= 4 ) + { + writel(*(const uint32_t *)from, to); + from += 4; + to += 4; + count -= 4; + } + + while ( count ) + { + writeb(*(const uint8_t *)from, to); + from++; + to++; + count--; + } +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/xen/include/xen/io.h b/xen/include/xen/io.h index 164a20c5d7..1bb164b6ef 100644 --- a/xen/include/xen/io.h +++ b/xen/include/xen/io.h @@ -67,4 +67,14 @@ static inline bool write_mmio(volatile void __iomem *mem, unsigned long data, return true; } +/* + * Copy between regular memory and MMIO space. Implementations are + * architecture-specific and must use appropriate MMIO accessors for + * their memory and I/O models. + */ +void memcpy_fromio(void *to, const volatile void __iomem *from, + size_t count); +void memcpy_toio(volatile void __iomem *to, const void *from, + size_t count); + #endif /* XEN_IO_H */ -- 2.34.1
