summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/arch/x86/core/x86_string.c
blob: d48347c962a9bece82c1368e0a4909592edb0329 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

/** @file
 *
 * Optimised string operations
 *
 */

FILE_LICENCE ( GPL2_OR_LATER );

#include <string.h>

/**
 * Copy memory area
 *
 * @v dest		Destination address
 * @v src		Source address
 * @v len		Length
 * @ret dest		Destination address
 */
void * __attribute__ (( noinline )) __memcpy ( void *dest, const void *src,
					       size_t len ) {
	void *edi = dest;
	const void *esi = src;
	int discard_ecx;

	/* We often do large dword-aligned and dword-length block
	 * moves.  Using movsl rather than movsb speeds these up by
	 * around 32%.
	 */
	__asm__ __volatile__ ( "rep movsl"
			       : "=&D" ( edi ), "=&S" ( esi ),
				 "=&c" ( discard_ecx )
			       : "0" ( edi ), "1" ( esi ), "2" ( len >> 2 )
			       : "memory" );
	__asm__ __volatile__ ( "rep movsb"
			       : "=&D" ( edi ), "=&S" ( esi ),
				 "=&c" ( discard_ecx )
			       : "0" ( edi ), "1" ( esi ), "2" ( len & 3 )
			       : "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 );
	}
}

/**
 * Swap memory areas
 *
 * @v dest		Destination address
 * @v src		Source address
 * @v len		Length
 * @ret dest		Destination address
 */
void * memswap ( void *dest, void *src, size_t len ) {
	size_t discard_c;
	int discard;

	__asm__ __volatile__ ( "\n1:\n\t"
			       "dec %2\n\t"
			       "js 2f\n\t"
			       "movb (%0,%2), %b3\n\t"
			       "xchgb (%1,%2), %b3\n\t"
			       "movb %b3, (%0,%2)\n\t"
			       "jmp 1b\n\t"
			       "2:\n\t"
			       : "=r" ( src ), "=r" ( dest ),
				 "=&c" ( discard_c ), "=&q" ( discard )
			       : "0" ( src ), "1" ( dest ), "2" ( len )
			       : "memory" );

	return dest;
}

/**
 * Calculate length of string
 *
 * @v string		String
 * @ret len		Length (excluding NUL)
 */
size_t strlen ( const char *string ) {
	const char *discard_D;
	size_t len_plus_one;

	__asm__ __volatile__ ( "repne scasb\n\t"
			       "not %1\n\t"
			       : "=&D" ( discard_D ), "=&c" ( len_plus_one )
			       : "0" ( string ), "1" ( -1UL ), "a" ( 0 ) );

	return ( len_plus_one - 1 );
}

/**
 * Compare strings (up to a specified length)
 *
 * @v str1		First string
 * @v str2		Second string
 * @v len		Maximum length
 * @ret diff		Difference
 */
int strncmp ( const char *str1, const char *str2, size_t len ) {
	const void *discard_S;
	const void *discard_D;
	size_t discard_c;
	int diff;

	__asm__ __volatile__ ( "\n1:\n\t"
			       "dec %2\n\t"
			       "js 2f\n\t"
			       "lodsb\n\t"
			       "scasb\n\t"
			       "jne 3f\n\t"
			       "testb %b3, %b3\n\t"
			       "jnz 1b\n\t"
			       /* Equal */
			       "\n2:\n\t"
			       "xor %3, %3\n\t"
			       "jmp 4f\n\t"
			       /* Not equal; CF indicates difference */
			       "\n3:\n\t"
			       "sbb %3, %3\n\t"
			       "orb $1, %b3\n\t"
			       "\n4:\n\t"
			       : "=&S" ( discard_S ), "=&D" ( discard_D ),
				 "=&c" ( discard_c ), "=&a" ( diff )
			       : "0" ( str1 ), "1" ( str2 ), "2" ( len ) );

	return diff;
}