1// Copyright 2017 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#include <arch/x86/feature.h>
8#include <assert.h>
9#include <lib/unittest/unittest.h>
10#include <stddef.h>
11
12extern "C" {
13
14extern void* memcpy(void*, const void*, size_t);
15extern void* memcpy_erms(void*, const void*, size_t);
16extern void* memcpy_quad(void*, const void*, size_t);
17
18extern void* memset(void*, int, size_t);
19extern void* memset_erms(void*, int, size_t);
20extern void* memset_quad(void*, int, size_t);
21
22}
23
24typedef void* (*memcpy_func_t)(void*, const void*, size_t);
25typedef void* (*memset_func_t)(void*, int, size_t);
26
27// Initializes buf with |fill_len| bytes of |fill|, and pads the remaining
28// |len - fill_len| bytes with 0xff.
29static void initialize_buffer(char* buf, size_t len, char fill, size_t fill_len) {
30    for (size_t i = 0; i < fill_len; ++i) {
31        buf[i] = fill;
32    }
33    for (size_t i = fill_len; i < len; ++i) {
34        buf[i] = static_cast<char>(0xff);
35    }
36}
37
38static bool memcpy_func_test(memcpy_func_t cpy) {
39    BEGIN_TEST;
40
41    // Test buffers for sizes from 0 to 64
42    constexpr size_t kBufLen = 64;
43    for (size_t len = 0; len < kBufLen; ++len) {
44        // Give the buffers an extra byte so we can check we're not copying
45        // excess.
46        char src[kBufLen + 1];
47        char dst[kBufLen + 1] = { 0 };
48
49        initialize_buffer(src, sizeof(src), static_cast<char>(len + 1), len);
50        cpy(dst, src, len);
51        ASSERT_TRUE(!memcmp(src, dst, len), "buffer mismatch");
52        for (size_t i = len; i < sizeof(dst); ++i) {
53            ASSERT_EQ(0, dst[i], "coppied padding");
54        }
55    }
56
57    // Test alignment offsets relative to 8 bytes.
58    for (size_t dst_offset = 0; dst_offset < 8; ++dst_offset) {
59        for (size_t src_offset = 0; src_offset < 8; ++src_offset) {
60            // Give the buffers an extra byte so we can check we're not copying
61            // excess.
62            char src[kBufLen + 1];
63            // Give the destination an extra 8 bytes so we don't need to worry
64            // about the case where src_offset = 0 and dst_offset = 7.
65            char dst[kBufLen + 1 + 8] = { 0 };
66
67            for (size_t i = 0; i < src_offset; ++i) {
68                src[i] = static_cast<char>(0xff);
69            }
70            for (size_t i = src_offset; i < kBufLen; ++i) {
71                src[i] = static_cast<char>(i - src_offset + 1);
72            }
73            src[kBufLen] = static_cast<char>(0xff);
74
75            const size_t cpy_len = kBufLen - src_offset;
76            cpy(dst + dst_offset, src + src_offset, cpy_len);
77            for (size_t i = 0; i < dst_offset; ++i) {
78                ASSERT_EQ(0, dst[i], "overwrote before buffer");
79            }
80            for (size_t i = dst_offset; i < dst_offset + cpy_len; ++i) {
81                ASSERT_EQ(static_cast<char>(i - dst_offset + 1), dst[i], "buffer mismatch");
82            }
83            for (size_t i = dst_offset + cpy_len; i < sizeof(dst); ++i) {
84                ASSERT_EQ(0, dst[i], "overwrote after buffer");
85            }
86        }
87    }
88
89    END_TEST;
90}
91
92static bool memset_func_test(memset_func_t set) {
93    BEGIN_TEST;
94
95    // Test buffers for sizes from 0 to 64
96    constexpr size_t kBufLen = 64;
97    for (size_t len = 0; len < kBufLen; ++len) {
98        // Give the buffer an extra byte so we can check we're not copying
99        // excess.
100        char dst[kBufLen + 1] = { 0 };
101
102        set(dst, static_cast<int>(len + 1), len);
103        for (size_t i = 0; i < len; ++i) {
104            ASSERT_EQ(static_cast<char>(len + 1), dst[i], "buffer mismatch");
105        }
106        for (size_t i = len; i < sizeof(dst); ++i) {
107            ASSERT_EQ(0, dst[i], "overwrote padding");
108        }
109    }
110
111    // Test all fill values
112    for (int fill = 0; fill < 0x100; ++fill) {
113        char dst[kBufLen] = { static_cast<char>(fill + 1) };
114        set(dst, fill, sizeof(dst));
115        for (size_t i = 0; i < kBufLen; ++i) {
116            ASSERT_EQ(static_cast<char>(fill), dst[i], "buffer mismatch");
117        }
118    }
119
120    // Test all alignment offsets relative to 8 bytes.
121    for (size_t offset = 0; offset < 8; ++offset) {
122        // Give the buffer an extra byte so we can check we're not copying
123        // excess.
124        char dst[kBufLen + 1] = { 0 };
125
126        set(dst + offset, static_cast<int>(kBufLen - offset), kBufLen - offset);
127        for (size_t i = 0; i < offset; ++i) {
128            ASSERT_EQ(0, dst[i], "overwrote before buffer");
129        }
130        for (size_t i = offset; i < kBufLen; ++i) {
131            ASSERT_EQ(static_cast<char>(kBufLen - offset), dst[i], "buffer mismatch");
132        }
133        for (size_t i = kBufLen; i < sizeof(dst); ++i) {
134            ASSERT_EQ(0, dst[i], "overwrote after buffer");
135        }
136    }
137
138    END_TEST;
139}
140
141static bool memcpy_test() {
142    return memcpy_func_test(memcpy);
143}
144
145static bool memcpy_quad_test() {
146    return memcpy_func_test(memcpy_quad);
147}
148
149static bool memcpy_erms_test() {
150    if (!x86_feature_test(X86_FEATURE_ERMS)) {
151        return true;
152    }
153
154    return memcpy_func_test(memcpy_erms);
155}
156
157static bool memset_test() {
158    return memset_func_test(memset);
159}
160
161static bool memset_quad_test() {
162    return memset_func_test(memset_quad);
163}
164
165static bool memset_erms_test() {
166    if (!x86_feature_test(X86_FEATURE_ERMS)) {
167        return true;
168    }
169
170    return memset_func_test(memset_erms);
171}
172
173UNITTEST_START_TESTCASE(memops_tests)
174UNITTEST("memcpy tests", memcpy_test)
175UNITTEST("memcpy_quad tests", memcpy_quad_test)
176UNITTEST("memcpy_erms tests", memcpy_erms_test)
177UNITTEST("memset tests", memset_test)
178UNITTEST("memset_quad tests", memset_quad_test)
179UNITTEST("memset_erms tests", memset_erms_test)
180UNITTEST_END_TESTCASE(memops_tests, "memops_tests", "memcpy/memset tests");
181