15756e9ddSDave Martin /* 25756e9ddSDave Martin * arch/arm/include/asm/fncpy.h - helper macros for function body copying 35756e9ddSDave Martin * 45756e9ddSDave Martin * Copyright (C) 2011 Linaro Limited 55756e9ddSDave Martin * 65756e9ddSDave Martin * This program is free software; you can redistribute it and/or modify 75756e9ddSDave Martin * it under the terms of the GNU General Public License version 2 as 85756e9ddSDave Martin * published by the Free Software Foundation. 95756e9ddSDave Martin * 105756e9ddSDave Martin * This program is distributed in the hope that it will be useful, 115756e9ddSDave Martin * but WITHOUT ANY WARRANTY; without even the implied warranty of 125756e9ddSDave Martin * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 135756e9ddSDave Martin * GNU General Public License for more details. 145756e9ddSDave Martin * 155756e9ddSDave Martin * You should have received a copy of the GNU General Public License 165756e9ddSDave Martin * along with this program; if not, write to the Free Software 175756e9ddSDave Martin * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 185756e9ddSDave Martin */ 195756e9ddSDave Martin 205756e9ddSDave Martin /* 215756e9ddSDave Martin * These macros are intended for use when there is a need to copy a low-level 225756e9ddSDave Martin * function body into special memory. 235756e9ddSDave Martin * 245756e9ddSDave Martin * For example, when reconfiguring the SDRAM controller, the code doing the 255756e9ddSDave Martin * reconfiguration may need to run from SRAM. 265756e9ddSDave Martin * 275756e9ddSDave Martin * NOTE: that the copied function body must be entirely self-contained and 285756e9ddSDave Martin * position-independent in order for this to work properly. 295756e9ddSDave Martin * 305756e9ddSDave Martin * NOTE: in order for embedded literals and data to get referenced correctly, 315756e9ddSDave Martin * the alignment of functions must be preserved when copying. To ensure this, 325756e9ddSDave Martin * the source and destination addresses for fncpy() must be aligned to a 335756e9ddSDave Martin * multiple of 8 bytes: you will be get a BUG() if this condition is not met. 345756e9ddSDave Martin * You will typically need a ".align 3" directive in the assembler where the 355756e9ddSDave Martin * function to be copied is defined, and ensure that your allocator for the 365756e9ddSDave Martin * destination buffer returns 8-byte-aligned pointers. 375756e9ddSDave Martin * 385756e9ddSDave Martin * Typical usage example: 395756e9ddSDave Martin * 405756e9ddSDave Martin * extern int f(args); 415756e9ddSDave Martin * extern uint32_t size_of_f; 425756e9ddSDave Martin * int (*copied_f)(args); 435756e9ddSDave Martin * void *sram_buffer; 445756e9ddSDave Martin * 455756e9ddSDave Martin * copied_f = fncpy(sram_buffer, &f, size_of_f); 465756e9ddSDave Martin * 475756e9ddSDave Martin * ... later, call the function: ... 485756e9ddSDave Martin * 495756e9ddSDave Martin * copied_f(args); 505756e9ddSDave Martin * 515756e9ddSDave Martin * The size of the function to be copied can't be determined from C: 525756e9ddSDave Martin * this must be determined by other means, such as adding assmbler directives 535756e9ddSDave Martin * in the file where f is defined. 545756e9ddSDave Martin */ 555756e9ddSDave Martin 565756e9ddSDave Martin #ifndef __ASM_FNCPY_H 575756e9ddSDave Martin #define __ASM_FNCPY_H 585756e9ddSDave Martin 595756e9ddSDave Martin #include <linux/types.h> 605756e9ddSDave Martin #include <linux/string.h> 615756e9ddSDave Martin 625756e9ddSDave Martin #include <asm/bug.h> 635756e9ddSDave Martin #include <asm/cacheflush.h> 645756e9ddSDave Martin 655756e9ddSDave Martin /* 665756e9ddSDave Martin * Minimum alignment requirement for the source and destination addresses 675756e9ddSDave Martin * for function copying. 685756e9ddSDave Martin */ 695756e9ddSDave Martin #define FNCPY_ALIGN 8 705756e9ddSDave Martin 715756e9ddSDave Martin #define fncpy(dest_buf, funcp, size) ({ \ 725756e9ddSDave Martin uintptr_t __funcp_address; \ 735756e9ddSDave Martin typeof(funcp) __result; \ 745756e9ddSDave Martin \ 755756e9ddSDave Martin asm("" : "=r" (__funcp_address) : "0" (funcp)); \ 765756e9ddSDave Martin \ 775756e9ddSDave Martin /* \ 785756e9ddSDave Martin * Ensure alignment of source and destination addresses, \ 795756e9ddSDave Martin * disregarding the function's Thumb bit: \ 805756e9ddSDave Martin */ \ 815756e9ddSDave Martin BUG_ON((uintptr_t)(dest_buf) & (FNCPY_ALIGN - 1) || \ 825756e9ddSDave Martin (__funcp_address & ~(uintptr_t)1 & (FNCPY_ALIGN - 1))); \ 835756e9ddSDave Martin \ 845756e9ddSDave Martin memcpy(dest_buf, (void const *)(__funcp_address & ~1), size); \ 855756e9ddSDave Martin flush_icache_range((unsigned long)(dest_buf), \ 865756e9ddSDave Martin (unsigned long)(dest_buf) + (size)); \ 875756e9ddSDave Martin \ 885756e9ddSDave Martin asm("" : "=r" (__result) \ 895756e9ddSDave Martin : "0" ((uintptr_t)(dest_buf) | (__funcp_address & 1))); \ 905756e9ddSDave Martin \ 915756e9ddSDave Martin __result; \ 925756e9ddSDave Martin }) 935756e9ddSDave Martin 945756e9ddSDave Martin #endif /* !__ASM_FNCPY_H */ 95