The Pedigree Project 0.1
memory.c
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/compiler.h"
21#include "pedigree/kernel/processor/types.h"
22#include "pedigree/kernel/utilities/assert.h"
23#include "pedigree/kernel/utilities/utility.h"
24
25#undef memcpy
26
27#if HAS_ADDRESS_SANITIZER
28// memcpy etc
29#include <string.h>
30#endif
31
32#define SSE_THRESHOLD 1024
33#define STOSB_THRESHOLD 64
34
35#if HOSTED_X64
36#undef X64
37#define X64 1
38#endif
39
40#if UTILITY_LINUX_COVERAGE
41#undef _STRING_H
42#include <string.h>
43#else
44
45#if UTILITY_LINUX
46#define EXPORT static
47#else
48#define EXPORT EXPORTED_PUBLIC
49#endif
50
51extern void memzero_xmm_aligned(void*, size_t);
52extern void memzero_xmm(void*, size_t);
53
54void* WordSet(void* buf, int c, size_t n);
55void* DoubleWordSet(void* buf, unsigned int c, size_t n);
56void* QuadWordSet(void* buf, unsigned long long c, size_t n);
57
58// asan provides a memcpy/memset/etc that we care about more than our custom
59// ones, in general.
60#if !HAS_ADDRESS_SANITIZER
61EXPORT int memcmp(const void* p1, const void* p2, size_t len) PURE;
62EXPORT void* memset(void* buf, int c, size_t n);
63EXPORT void* memcpy(void* restrict s1, const void* restrict s2, size_t n);
64EXPORT void* memmove(void* s1, const void* s2, size_t n);
65
66EXPORT int memcmp(const void* p1, const void* p2, size_t len) {
67 const char* a = (const char*)p1;
68 const char* b = (const char*)p2;
69 size_t i = 0;
70 int r = 0;
71 for (; i < len; i++) {
72 if ((r = a[i] - b[i]) != 0)
73 break;
74 }
75 return r;
76}
77
78EXPORT void* memset(void* buf, int c, size_t n) {
79#ifdef TARGET_IS_X86
80 if (n >= STOSB_THRESHOLD) {
81#if defined(__x86_64__)
82 // Use qword transfers without requiring ERMS or per-call CPU dispatch.
83 void* destination = buf;
84 size_t head = (-(uintptr_t)buf) & 7;
85 const size_t words = (n - head) / 8;
86 const size_t tail = (n - head) & 7;
87 const uint64_t value = (unsigned char)c * 0x0101010101010101ULL;
88 __asm__ __volatile__(
89 "rep stosb\n\t"
90 "mov %[words], %%rcx\n\t"
91 "rep stosq\n\t"
92 "mov %[tail], %%rcx\n\t"
93 "rep stosb"
94 : "+&D"(destination), "+&c"(head)
95 : "a"(value), [words] "r"(words), [tail] "r"(tail)
96 : "memory");
97#else
98 int a, b;
99 __asm__ __volatile__("rep stosb" : "=&D"(a), "=&c"(b) : "0"(buf), "a"(c), "1"(n) : "memory");
100#endif
101 return buf;
102 }
103#endif
104 unsigned char* tmp = (unsigned char*)buf;
105 while (n--) {
106 *tmp++ = c;
107 }
108 return buf;
109}
110
111EXPORT void* memcpy(void* restrict s1, const void* restrict s2, size_t n) {
112#ifdef TARGET_IS_X86
113 if (n >= STOSB_THRESHOLD) {
114#if defined(__x86_64__)
115 void* destination = s1;
116 const void* source = s2;
117 size_t head = (-(uintptr_t)s1) & 7;
118 const size_t words = (n - head) / 8;
119 const size_t tail = (n - head) & 7;
120 __asm__ __volatile__(
121 "rep movsb\n\t"
122 "mov %[words], %%rcx\n\t"
123 "rep movsq\n\t"
124 "mov %[tail], %%rcx\n\t"
125 "rep movsb"
126 : "+&D"(destination), "+&S"(source), "+&c"(head)
127 : [words] "r"(words), [tail] "r"(tail)
128 : "memory");
129#else
130 int a, b, c;
131 __asm__ __volatile__("rep movsb"
132 : "=&c"(a), "=&D"(b), "=&S"(c)
133 : "1"(s1), "2"(s2), "0"(n)
134 : "memory");
135#endif
136 return s1;
137 }
138#endif
139 const unsigned char* restrict sp = (const unsigned char* restrict)s2;
140 unsigned char* restrict dp = (unsigned char* restrict)s1;
141 while (n--)
142 *dp++ = *sp++;
143 return s1;
144}
145
146#ifdef TARGET_IS_X86
147static inline void* memmove_x86(void* s1, const void* s2, size_t n) {
148 const unsigned char* sp = (const unsigned char*)s2 + (n - 1);
149 unsigned char* dp = (unsigned char*)s1 + (n - 1);
150
151#if defined(__x86_64__)
152 size_t head = ((uintptr_t)s1 + n) & 7;
153 const size_t words = (n - head) / 8;
154 const size_t tail = (n - head) & 7;
155 // Backward MOVSQ addresses the first byte of each qword, not its last.
156 // Keep DF set only inside this block and restore it before returning to C.
157 __asm__ __volatile__(
158 "std\n\t"
159 "rep movsb\n\t"
160 "sub $7, %%rdi\n\t"
161 "sub $7, %%rsi\n\t"
162 "mov %[words], %%rcx\n\t"
163 "rep movsq\n\t"
164 "add $7, %%rdi\n\t"
165 "add $7, %%rsi\n\t"
166 "mov %[tail], %%rcx\n\t"
167 "rep movsb\n\t"
168 "cld"
169 : "+&D"(dp), "+&S"(sp), "+&c"(head)
170 : [words] "r"(words), [tail] "r"(tail)
171 : "memory", "cc");
172#else
173 int a, b, c;
174 __asm__ __volatile__("std; rep movsb; cld"
175 : "=&c"(a), "=&D"(b), "=&S"(c)
176 : "1"(dp), "2"(sp), "0"(n)
177 : "memory");
178#endif
179 return s1;
180}
181#endif
182
183EXPORT void* memmove(void* s1, const void* s2, size_t n) {
184 if (UNLIKELY(!n))
185 return s1;
186
187 const size_t orig_n = n;
188 if (LIKELY((s1 < s2) || !overlaps(s1, s2, n))) {
189 // No overlap, or there's overlap but we can copy forwards.
190 memcpy(s1, s2, n);
191 } else {
192#ifdef TARGET_IS_X86
193 if (n >= STOSB_THRESHOLD) {
194 memmove_x86(s1, s2, n);
195 } else {
196#endif
197 // Writing bytes from s2 into s1 cannot be done forwards, use memmove.
198 const unsigned char* sp = (const unsigned char*)s2 + (n - 1);
199 unsigned char* dp = (unsigned char*)s1 + (n - 1);
200 for (; n != 0; n--)
201 *dp-- = *sp--;
202#ifdef TARGET_IS_X86
203 }
204#endif
205 }
206
207#if EXCESSIVE_ADDITIONAL_CHECKS
208 // We can't memcmp if the regions overlap at all.
209 if (LIKELY(!overlaps(s1, s2, orig_n))) {
210 assert(!memcmp(s1, s2, orig_n));
211 }
212#endif
213
214 return s1;
215}
216
217#endif // HAS_ADDRESS_SANITIZER
218
219#endif // UTILITY_LINUX_COVERAGE
220
221int overlaps(const void* s1, const void* s2, size_t n) {
222 uintptr_t a = (uintptr_t)s1;
223 uintptr_t a_end = (uintptr_t)s1 + n;
224 uintptr_t b = (uintptr_t)s2;
225 uintptr_t b_end = (uintptr_t)s2 + n;
226
227 return (a <= b_end) && (b <= a_end) ? 1 : 0;
228}
229
230void* WordSet(void* buf, int c, size_t n) {
231#ifdef TARGET_IS_X86
232 if (n >= STOSB_THRESHOLD) {
233 int a, b;
234 __asm__ __volatile__("rep stosw" : "=&D"(a), "=&c"(b) : "0"(buf), "a"(c), "1"(n) : "memory");
235 return buf;
236 }
237#endif
238 unsigned short* tmp = (unsigned short*)buf;
239 while (n--) {
240 *tmp++ = c;
241 }
242 return buf;
243}
244
245void* DoubleWordSet(void* buf, unsigned int c, size_t n) {
246#ifdef TARGET_IS_X86
247 if (n >= STOSB_THRESHOLD) {
248 int a, b;
249 __asm__ __volatile__("rep stosl" : "=&D"(a), "=&c"(b) : "0"(buf), "a"(c), "1"(n) : "memory");
250 return buf;
251 }
252#endif
253 unsigned int* tmp = (unsigned int*)buf;
254 while (n--) {
255 *tmp++ = c;
256 }
257 return buf;
258}
259
260void* QuadWordSet(void* buf, unsigned long long c, size_t n) {
261#ifdef TARGET_IS_X86
262 if (n >= STOSB_THRESHOLD) {
263 int a, b;
264 __asm__ __volatile__("rep stosq" : "=&D"(a), "=&c"(b) : "0"(buf), "a"(c), "1"(n) : "memory");
265 return buf;
266 }
267#endif
268 unsigned long long* p = (unsigned long long*)buf;
269 while (n--)
270 *p++ = c;
271 return buf;
272}
273
274// We still need memcpy etc as linked symbols for GCC optimisations, but we
275// don't have to have their prototypes widely available. So, we implement our
276// main functions in terms of the base calls.
277void* ForwardMemoryCopy(void* a, const void* b, size_t c) {
278 return memcpy(a, b, c);
279}
280
281void* MemoryCopy(void* a, const void* b, size_t c) {
282 return memmove(a, b, c);
283}
284
285void* ByteSet(void* a, int b, size_t c) {
286 return memset(a, b, c);
287}
288
289int MemoryCompare(const void* a, const void* b, size_t c) {
290 return memcmp(a, b, c);
291}