1 /* 2 * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu> 3 * Copyright (C) 2008-2009 PetaLogix 4 * Copyright (C) 2007 John Williams 5 * 6 * Reasonably optimised generic C-code for memcpy on Microblaze 7 * This is generic C code to do efficient, alignment-aware memmove. 8 * 9 * It is based on demo code originally Copyright 2001 by Intel Corp, taken from 10 * http://www.embedded.com/showArticle.jhtml?articleID=19205567 11 * 12 * Attempts were made, unsuccessfully, to contact the original 13 * author of this code (Michael Morrow, Intel). Below is the original 14 * copyright notice. 15 * 16 * This software has been developed by Intel Corporation. 17 * Intel specifically disclaims all warranties, express or 18 * implied, and all liability, including consequential and 19 * other indirect damages, for the use of this program, including 20 * liability for infringement of any proprietary rights, 21 * and including the warranties of merchantability and fitness 22 * for a particular purpose. Intel does not assume any 23 * responsibility for and errors which may appear in this program 24 * not any responsibility to update it. 25 */ 26 27 #include <linux/export.h> 28 #include <linux/types.h> 29 #include <linux/stddef.h> 30 #include <linux/compiler.h> 31 #include <linux/string.h> 32 33 #ifdef __HAVE_ARCH_MEMMOVE 34 #ifndef CONFIG_OPT_LIB_FUNCTION 35 void *memmove(void *v_dst, const void *v_src, __kernel_size_t c) 36 { 37 const char *src = v_src; 38 char *dst = v_dst; 39 40 if (!c) 41 return v_dst; 42 43 /* Use memcpy when source is higher than dest */ 44 if (v_dst <= v_src) 45 return memcpy(v_dst, v_src, c); 46 47 /* copy backwards, from end to beginning */ 48 src += c; 49 dst += c; 50 51 /* Simple, byte oriented memmove. */ 52 while (c--) 53 *--dst = *--src; 54 55 return v_dst; 56 } 57 #else /* CONFIG_OPT_LIB_FUNCTION */ 58 void *memmove(void *v_dst, const void *v_src, __kernel_size_t c) 59 { 60 const char *src = v_src; 61 char *dst = v_dst; 62 const uint32_t *i_src; 63 uint32_t *i_dst; 64 65 if (!c) 66 return v_dst; 67 68 /* Use memcpy when source is higher than dest */ 69 if (v_dst <= v_src) 70 return memcpy(v_dst, v_src, c); 71 72 /* The following code tries to optimize the copy by using unsigned 73 * alignment. This will work fine if both source and destination are 74 * aligned on the same boundary. However, if they are aligned on 75 * different boundaries shifts will be necessary. This might result in 76 * bad performance on MicroBlaze systems without a barrel shifter. 77 */ 78 /* FIXME this part needs more test */ 79 /* Do a descending copy - this is a bit trickier! */ 80 dst += c; 81 src += c; 82 83 if (c >= 4) { 84 unsigned value, buf_hold; 85 86 /* Align the destination to a word boundary. */ 87 /* This is done in an endian independent manner. */ 88 89 switch ((unsigned long)dst & 3) { 90 case 3: 91 *--dst = *--src; 92 --c; 93 fallthrough; 94 case 2: 95 *--dst = *--src; 96 --c; 97 fallthrough; 98 case 1: 99 *--dst = *--src; 100 --c; 101 } 102 103 i_dst = (void *)dst; 104 /* Choose a copy scheme based on the source */ 105 /* alignment relative to dstination. */ 106 switch ((unsigned long)src & 3) { 107 case 0x0: /* Both byte offsets are aligned */ 108 109 i_src = (const void *)src; 110 111 for (; c >= 4; c -= 4) 112 *--i_dst = *--i_src; 113 114 src = (const void *)i_src; 115 break; 116 case 0x1: /* Unaligned - Off by 1 */ 117 /* Word align the source */ 118 i_src = (const void *) (((unsigned)src + 4) & ~3); 119 #ifndef __MICROBLAZEEL__ 120 /* Load the holding buffer */ 121 buf_hold = *--i_src >> 24; 122 123 for (; c >= 4; c -= 4) { 124 value = *--i_src; 125 *--i_dst = buf_hold << 8 | value; 126 buf_hold = value >> 24; 127 } 128 #else 129 /* Load the holding buffer */ 130 buf_hold = (*--i_src & 0xFF) << 24; 131 132 for (; c >= 4; c -= 4) { 133 value = *--i_src; 134 *--i_dst = buf_hold | 135 ((value & 0xFFFFFF00) >> 8); 136 buf_hold = (value & 0xFF) << 24; 137 } 138 #endif 139 /* Realign the source */ 140 src = (const void *)i_src; 141 src += 1; 142 break; 143 case 0x2: /* Unaligned - Off by 2 */ 144 /* Word align the source */ 145 i_src = (const void *) (((unsigned)src + 4) & ~3); 146 #ifndef __MICROBLAZEEL__ 147 /* Load the holding buffer */ 148 buf_hold = *--i_src >> 16; 149 150 for (; c >= 4; c -= 4) { 151 value = *--i_src; 152 *--i_dst = buf_hold << 16 | value; 153 buf_hold = value >> 16; 154 } 155 #else 156 /* Load the holding buffer */ 157 buf_hold = (*--i_src & 0xFFFF) << 16; 158 159 for (; c >= 4; c -= 4) { 160 value = *--i_src; 161 *--i_dst = buf_hold | 162 ((value & 0xFFFF0000) >> 16); 163 buf_hold = (value & 0xFFFF) << 16; 164 } 165 #endif 166 /* Realign the source */ 167 src = (const void *)i_src; 168 src += 2; 169 break; 170 case 0x3: /* Unaligned - Off by 3 */ 171 /* Word align the source */ 172 i_src = (const void *) (((unsigned)src + 4) & ~3); 173 #ifndef __MICROBLAZEEL__ 174 /* Load the holding buffer */ 175 buf_hold = *--i_src >> 8; 176 177 for (; c >= 4; c -= 4) { 178 value = *--i_src; 179 *--i_dst = buf_hold << 24 | value; 180 buf_hold = value >> 8; 181 } 182 #else 183 /* Load the holding buffer */ 184 buf_hold = (*--i_src & 0xFFFFFF) << 8; 185 186 for (; c >= 4; c -= 4) { 187 value = *--i_src; 188 *--i_dst = buf_hold | 189 ((value & 0xFF000000) >> 24); 190 buf_hold = (value & 0xFFFFFF) << 8; 191 } 192 #endif 193 /* Realign the source */ 194 src = (const void *)i_src; 195 src += 3; 196 break; 197 } 198 dst = (void *)i_dst; 199 } 200 201 /* simple fast copy, ... unless a cache boundary is crossed */ 202 /* Finish off any remaining bytes */ 203 switch (c) { 204 case 4: 205 *--dst = *--src; 206 fallthrough; 207 case 3: 208 *--dst = *--src; 209 fallthrough; 210 case 2: 211 *--dst = *--src; 212 fallthrough; 213 case 1: 214 *--dst = *--src; 215 } 216 return v_dst; 217 } 218 #endif /* CONFIG_OPT_LIB_FUNCTION */ 219 EXPORT_SYMBOL(memmove); 220 #endif /* __HAVE_ARCH_MEMMOVE */ 221