aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/arch/x86/core/x86_string.c50
-rw-r--r--src/arch/x86/include/bits/string.h48
2 files changed, 74 insertions, 24 deletions
diff --git a/src/arch/x86/core/x86_string.c b/src/arch/x86/core/x86_string.c
index 698dbe2e..59e0eaae 100644
--- a/src/arch/x86/core/x86_string.c
+++ b/src/arch/x86/core/x86_string.c
@@ -35,7 +35,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
* @v len Length
* @ret dest Destination address
*/
-void * __memcpy ( void *dest, const void *src, size_t len ) {
+void * __attribute__ (( noinline )) __memcpy ( void *dest, const void *src,
+ size_t len ) {
void *edi = dest;
const void *esi = src;
int discard_ecx;
@@ -56,3 +57,50 @@ void * __memcpy ( void *dest, const void *src, size_t len ) {
: "memory" );
return dest;
}
+
+/**
+ * Copy memory area backwards
+ *
+ * @v dest Destination address
+ * @v src Source address
+ * @v len Length
+ * @ret dest Destination address
+ */
+void * __attribute__ (( noinline )) __memcpy_reverse ( void *dest,
+ const void *src,
+ size_t len ) {
+ void *edi = ( dest + len - 1 );
+ const void *esi = ( src + len - 1 );
+ int discard_ecx;
+
+ /* Assume memmove() is not performance-critical, and perform a
+ * bytewise copy for simplicity.
+ */
+ __asm__ __volatile__ ( "std\n\t"
+ "rep movsb\n\t"
+ "cld\n\t"
+ : "=&D" ( edi ), "=&S" ( esi ),
+ "=&c" ( discard_ecx )
+ : "0" ( edi ), "1" ( esi ),
+ "2" ( len )
+ : "memory" );
+ return dest;
+}
+
+
+/**
+ * Copy (possibly overlapping) memory area
+ *
+ * @v dest Destination address
+ * @v src Source address
+ * @v len Length
+ * @ret dest Destination address
+ */
+void * __memmove ( void *dest, const void *src, size_t len ) {
+
+ if ( dest <= src ) {
+ return __memcpy ( dest, src, len );
+ } else {
+ return __memcpy_reverse ( dest, src, len );
+ }
+}
diff --git a/src/arch/x86/include/bits/string.h b/src/arch/x86/include/bits/string.h
index d9ebe306..e5850ed9 100644
--- a/src/arch/x86/include/bits/string.h
+++ b/src/arch/x86/include/bits/string.h
@@ -26,6 +26,7 @@ FILE_LICENCE ( PUBLIC_DOMAIN );
#define __HAVE_ARCH_MEMCPY
extern void * __memcpy ( void *dest, const void *src, size_t len );
+extern void * __memcpy_reverse ( void *dest, const void *src, size_t len );
static inline __attribute__ (( always_inline )) void *
__constant_memcpy ( void *dest, const void *src, size_t len ) {
@@ -144,29 +145,30 @@ __constant_memcpy ( void *dest, const void *src, size_t len ) {
__memcpy ( (dest), (src), (len) ) )
#define __HAVE_ARCH_MEMMOVE
-static inline void * memmove(void * dest,const void * src, size_t n)
-{
-int d0, d1, d2;
-if (dest<src)
-__asm__ __volatile__(
- "cld\n\t"
- "rep\n\t"
- "movsb"
- : "=&c" (d0), "=&S" (d1), "=&D" (d2)
- :"0" (n),"1" (src),"2" (dest)
- : "memory");
-else
-__asm__ __volatile__(
- "std\n\t"
- "rep\n\t"
- "movsb\n\t"
- "cld"
- : "=&c" (d0), "=&S" (d1), "=&D" (d2)
- :"0" (n),
- "1" (n-1+(const char *)src),
- "2" (n-1+(char *)dest)
- :"memory");
-return dest;
+
+extern void * __memmove ( void *dest, const void *src, size_t len );
+
+/**
+ * Copy (possibly overlapping) memory area
+ *
+ * @v dest Destination address
+ * @v src Source address
+ * @v len Length
+ * @ret dest Destination address
+ */
+static inline __attribute__ (( always_inline )) void *
+memmove ( void *dest, const void *src, size_t len ) {
+ ssize_t offset = ( dest - src );
+
+ if ( __builtin_constant_p ( offset ) ) {
+ if ( offset <= 0 ) {
+ return memcpy ( dest, src, len );
+ } else {
+ return __memcpy_reverse ( dest, src, len );
+ }
+ } else {
+ return __memmove ( dest, src, len );
+ }
}
#define __HAVE_ARCH_MEMSET