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/types.h> 28 #include <linux/stddef.h> 29 #include <linux/compiler.h> 30 #include <linux/module.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 boundry. */ 87 /* This is done in an endian independant manner. */ 88 89 switch ((unsigned long)dst & 3) { 90 case 3: 91 *--dst = *--src; 92 --c; 93 case 2: 94 *--dst = *--src; 95 --c; 96 case 1: 97 *--dst = *--src; 98 --c; 99 } 100 101 i_dst = (void *)dst; 102 /* Choose a copy scheme based on the source */ 103 /* alignment relative to dstination. */ 104 switch ((unsigned long)src & 3) { 105 case 0x0: /* Both byte offsets are aligned */ 106 107 i_src = (const void *)src; 108 109 for (; c >= 4; c -= 4) 110 *--i_dst = *--i_src; 111 112 src = (const void *)i_src; 113 break; 114 case 0x1: /* Unaligned - Off by 1 */ 115 /* Word align the source */ 116 i_src = (const void *) (((unsigned)src + 4) & ~3); 117 #ifndef __MICROBLAZEEL__ 118 /* Load the holding buffer */ 119 buf_hold = *--i_src >> 24; 120 121 for (; c >= 4; c -= 4) { 122 value = *--i_src; 123 *--i_dst = buf_hold << 8 | value; 124 buf_hold = value >> 24; 125 } 126 #else 127 /* Load the holding buffer */ 128 buf_hold = (*--i_src & 0xFF) << 24; 129 130 for (; c >= 4; c -= 4) { 131 value = *--i_src; 132 *--i_dst = buf_hold | ((value & 0xFFFFFF00)>>8); 133 buf_hold = (value & 0xFF) << 24; 134 } 135 #endif 136 /* Realign the source */ 137 src = (const void *)i_src; 138 src += 1; 139 break; 140 case 0x2: /* Unaligned - Off by 2 */ 141 /* Word align the source */ 142 i_src = (const void *) (((unsigned)src + 4) & ~3); 143 #ifndef __MICROBLAZEEL__ 144 /* Load the holding buffer */ 145 buf_hold = *--i_src >> 16; 146 147 for (; c >= 4; c -= 4) { 148 value = *--i_src; 149 *--i_dst = buf_hold << 16 | value; 150 buf_hold = value >> 16; 151 } 152 #else 153 /* Load the holding buffer */ 154 buf_hold = (*--i_src & 0xFFFF) << 16; 155 156 for (; c >= 4; c -= 4) { 157 value = *--i_src; 158 *--i_dst = buf_hold | ((value & 0xFFFF0000)>>16); 159 buf_hold = (value & 0xFFFF) << 16; 160 } 161 #endif 162 /* Realign the source */ 163 src = (const void *)i_src; 164 src += 2; 165 break; 166 case 0x3: /* Unaligned - Off by 3 */ 167 /* Word align the source */ 168 i_src = (const void *) (((unsigned)src + 4) & ~3); 169 #ifndef __MICROBLAZEEL__ 170 /* Load the holding buffer */ 171 buf_hold = *--i_src >> 8; 172 173 for (; c >= 4; c -= 4) { 174 value = *--i_src; 175 *--i_dst = buf_hold << 24 | value; 176 buf_hold = value >> 8; 177 } 178 #else 179 /* Load the holding buffer */ 180 buf_hold = (*--i_src & 0xFFFFFF) << 8; 181 182 for (; c >= 4; c -= 4) { 183 value = *--i_src; 184 *--i_dst = buf_hold | ((value & 0xFF000000)>> 24); 185 buf_hold = (value & 0xFFFFFF) << 8; 186 } 187 #endif 188 /* Realign the source */ 189 src = (const void *)i_src; 190 src += 3; 191 break; 192 } 193 dst = (void *)i_dst; 194 } 195 196 /* simple fast copy, ... unless a cache boundry is crossed */ 197 /* Finish off any remaining bytes */ 198 switch (c) { 199 case 4: 200 *--dst = *--src; 201 case 3: 202 *--dst = *--src; 203 case 2: 204 *--dst = *--src; 205 case 1: 206 *--dst = *--src; 207 } 208 return v_dst; 209 } 210 #endif /* CONFIG_OPT_LIB_FUNCTION */ 211 EXPORT_SYMBOL(memmove); 212 #endif /* __HAVE_ARCH_MEMMOVE */ 213