This commit introduces two helper functions, `memcpy-fromio` and `memcpy-toio`, to provide a robust mechanism for copying data between standard memory and memory-mapped I/O (MMIO) space for the ARM architecture.
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 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 | 48 +++++++++++++++++++++ xen/arch/arm/lib/memcpy-toio.c | 48 +++++++++++++++++++++ xen/include/xen/lib/io.h | 71 ++++++++++++++++++++++++++++++++ 6 files changed, 171 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 create mode 100644 xen/include/xen/lib/io.h diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index 7494a0f926..bd8638c8a7 100644 --- a/xen/arch/arm/Makefile +++ b/xen/arch/arm/Makefile @@ -10,6 +10,7 @@ endif obj-y += firmware/ obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_HAS_VPCI) += vpci.o +obj-y += lib/ obj-$(CONFIG_HAS_ALTERNATIVE) += alternative.o obj-y += cpuerrata.o diff --git a/xen/arch/arm/arch.mk b/xen/arch/arm/arch.mk index dea8dbd18a..0c28dbeb87 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 +ALL_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..85377137c3 --- /dev/null +++ b/xen/arch/arm/lib/memcpy-fromio.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include <asm/io.h> +#include <xen/lib/io.h> + +/* + * Use ordered 8-bit and 32-bit IO accessors for portability across + * ARM32/ARM64 where 64-bit accesses may not be atomic and some devices + * only support 32-bit aligned 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..588497ed0f --- /dev/null +++ b/xen/arch/arm/lib/memcpy-toio.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include <asm/io.h> +#include <xen/lib/io.h> + +/* + * Use ordered 8-bit and 32-bit IO accessors for portability across + * ARM32/ARM64 where 64-bit accesses may not be atomic and some devices + * only support 32-bit aligned 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/lib/io.h b/xen/include/xen/lib/io.h new file mode 100644 index 0000000000..1c0865401e --- /dev/null +++ b/xen/include/xen/lib/io.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Generic I/O memory copy function prototypes. + * + * These functions provide low-level implementation for copying data between + * regular memory and I/O memory regions. Each architecture must provide its + * own implementation based on the specific requirements of the architecture's + * memory model and I/O access patterns. An architecture may supply these as + * functions or as macros in its own headers before including this file. + * + * Architecture-specific implementations: + * ===================================== + * Each architecture should implement these functions in xen/lib/<arch>/io.c + * (or define them as macros) based on their hardware requirements. See + * xen/lib/arm/io.c for an example using explicit I/O accessors. + */ + +#ifndef _XEN_LIB_IO_H +#define _XEN_LIB_IO_H + +#include <xen/types.h> + +/* + * memcpy_fromio - Copy data from I/O memory space to regular memory + * @to: Destination buffer in regular memory + * @from: Source address in I/O memory space (must be marked __iomem) + * @count: Number of bytes to copy + * + * This function handles copying from memory-mapped I/O regions using + * architecture-appropriate I/O accessor functions (e.g. readb/readl on Arm) + * that already impose the required ordering for device accesses. Typical + * implementations may: + * - Handle leading/trailing unaligned bytes with 8-bit accesses + * - Use the widest safe aligned access size supported by the target (often + * 32-bit on Arm where 64-bit MMIO may not be atomic) + * - Rely on MMIO accessors to provide the needed barriers + * + * Limitations: + * - Only suitable for devices that tolerate 8-bit and 32-bit accesses; it is + * not valid for devices that require strictly 16-bit or 64-bit access sizes. + * - Callers must ensure the target MMIO region is mapped with appropriate + * device attributes. + */ +extern void memcpy_fromio(void *to, const volatile void __iomem *from, + size_t count); + +/* + * memcpy_toio - Copy data from regular memory to I/O memory space + * @to: Destination address in I/O memory space (must be marked __iomem) + * @from: Source buffer in regular memory + * @count: Number of bytes to copy + * + * This function handles copying to memory-mapped I/O regions using + * architecture-appropriate I/O accessor functions (e.g. writeb/writel on Arm) + * that already impose the required ordering for device accesses. Typical + * implementations may: + * - Handle leading/trailing unaligned bytes with 8-bit accesses + * - Use the widest safe aligned access size supported by the target (often + * 32-bit on Arm where 64-bit MMIO may not be atomic) + * - Rely on MMIO accessors to provide the needed barriers + * + * Limitations: + * - Only suitable for devices that tolerate 8-bit and 32-bit accesses; it is + * not valid for devices that require strictly 16-bit or 64-bit access sizes. + * - Callers must ensure the target MMIO region is mapped with appropriate + * device attributes. + */ +extern void memcpy_toio(volatile void __iomem *to, const void *from, + size_t count); + +#endif /* _XEN_LIB_IO_H */ -- 2.34.1
