Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
memmove.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008-2009 Michal Simek <[email protected]>
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 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  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 boundary 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 */
212 #endif /* __HAVE_ARCH_MEMMOVE */