18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * arch/arm/include/asm/fncpy.h - helper macros for function body copying
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Linaro Limited
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * These macros are intended for use when there is a need to copy a low-level
108c2ecf20Sopenharmony_ci * function body into special memory.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * For example, when reconfiguring the SDRAM controller, the code doing the
138c2ecf20Sopenharmony_ci * reconfiguration may need to run from SRAM.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * NOTE: that the copied function body must be entirely self-contained and
168c2ecf20Sopenharmony_ci * position-independent in order for this to work properly.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * NOTE: in order for embedded literals and data to get referenced correctly,
198c2ecf20Sopenharmony_ci * the alignment of functions must be preserved when copying.  To ensure this,
208c2ecf20Sopenharmony_ci * the source and destination addresses for fncpy() must be aligned to a
218c2ecf20Sopenharmony_ci * multiple of 8 bytes: you will be get a BUG() if this condition is not met.
228c2ecf20Sopenharmony_ci * You will typically need a ".align 3" directive in the assembler where the
238c2ecf20Sopenharmony_ci * function to be copied is defined, and ensure that your allocator for the
248c2ecf20Sopenharmony_ci * destination buffer returns 8-byte-aligned pointers.
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * Typical usage example:
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * extern int f(args);
298c2ecf20Sopenharmony_ci * extern uint32_t size_of_f;
308c2ecf20Sopenharmony_ci * int (*copied_f)(args);
318c2ecf20Sopenharmony_ci * void *sram_buffer;
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * copied_f = fncpy(sram_buffer, &f, size_of_f);
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * ... later, call the function: ...
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci * copied_f(args);
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * The size of the function to be copied can't be determined from C:
408c2ecf20Sopenharmony_ci * this must be determined by other means, such as adding assmbler directives
418c2ecf20Sopenharmony_ci * in the file where f is defined.
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#ifndef __ASM_FNCPY_H
458c2ecf20Sopenharmony_ci#define __ASM_FNCPY_H
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#include <linux/types.h>
488c2ecf20Sopenharmony_ci#include <linux/string.h>
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#include <asm/bug.h>
518c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/*
548c2ecf20Sopenharmony_ci * Minimum alignment requirement for the source and destination addresses
558c2ecf20Sopenharmony_ci * for function copying.
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_ci#define FNCPY_ALIGN 8
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define fncpy(dest_buf, funcp, size) ({					\
608c2ecf20Sopenharmony_ci	uintptr_t __funcp_address;					\
618c2ecf20Sopenharmony_ci	typeof(funcp) __result;						\
628c2ecf20Sopenharmony_ci									\
638c2ecf20Sopenharmony_ci	asm("" : "=r" (__funcp_address) : "0" (funcp));			\
648c2ecf20Sopenharmony_ci									\
658c2ecf20Sopenharmony_ci	/*								\
668c2ecf20Sopenharmony_ci	 * Ensure alignment of source and destination addresses,	\
678c2ecf20Sopenharmony_ci	 * disregarding the function's Thumb bit:			\
688c2ecf20Sopenharmony_ci	 */								\
698c2ecf20Sopenharmony_ci	BUG_ON((uintptr_t)(dest_buf) & (FNCPY_ALIGN - 1) ||		\
708c2ecf20Sopenharmony_ci		(__funcp_address & ~(uintptr_t)1 & (FNCPY_ALIGN - 1)));	\
718c2ecf20Sopenharmony_ci									\
728c2ecf20Sopenharmony_ci	memcpy(dest_buf, (void const *)(__funcp_address & ~1), size);	\
738c2ecf20Sopenharmony_ci	flush_icache_range((unsigned long)(dest_buf),			\
748c2ecf20Sopenharmony_ci		(unsigned long)(dest_buf) + (size));			\
758c2ecf20Sopenharmony_ci									\
768c2ecf20Sopenharmony_ci	asm("" : "=r" (__result)					\
778c2ecf20Sopenharmony_ci		: "0" ((uintptr_t)(dest_buf) | (__funcp_address & 1)));	\
788c2ecf20Sopenharmony_ci									\
798c2ecf20Sopenharmony_ci	__result;							\
808c2ecf20Sopenharmony_ci})
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#endif /* !__ASM_FNCPY_H */
83