1 /*
2 * Copyright (c) 2014 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <err.h>
27 #include <arch.h>
28 #include <arch/ops.h>
29 #include <lib/console.h>
30 #include <platform.h>
31 #include <debug.h>
32
33 #if WITH_KERNEL_VM
34 #include <kernel/vm.h>
35 #endif
36
mem_test_fail(void * ptr,uint32_t should,uint32_t is)37 static void mem_test_fail(void *ptr, uint32_t should, uint32_t is)
38 {
39 printf("ERROR at %p: should be 0x%x, is 0x%x\n", ptr, should, is);
40
41 ptr = (void *)round_down((uintptr_t)ptr, 64);
42 hexdump(ptr, 128);
43 }
44
do_pattern_test(void * ptr,size_t len,uint32_t pat)45 static status_t do_pattern_test(void *ptr, size_t len, uint32_t pat)
46 {
47 volatile uint32_t *vbuf32 = ptr;
48 size_t i;
49
50 printf("\tpattern 0x%08x\n", pat);
51 for (i = 0; i < len / 4; i++) {
52 vbuf32[i] = pat;
53 }
54
55 for (i = 0; i < len / 4; i++) {
56 if (vbuf32[i] != pat) {
57 mem_test_fail((void *)&vbuf32[i], pat, vbuf32[i]);
58 return ERR_GENERIC;
59 }
60 }
61
62 return NO_ERROR;
63 }
64
do_moving_inversion_test(void * ptr,size_t len,uint32_t pat)65 static status_t do_moving_inversion_test(void *ptr, size_t len, uint32_t pat)
66 {
67 volatile uint32_t *vbuf32 = ptr;
68 size_t i;
69
70 printf("\tpattern 0x%08x\n", pat);
71
72 /* fill memory */
73 for (i = 0; i < len / 4; i++) {
74 vbuf32[i] = pat;
75 }
76
77 /* from the bottom, walk through each cell, inverting the value */
78 //printf("\t\tbottom up invert\n");
79 for (i = 0; i < len / 4; i++) {
80 if (vbuf32[i] != pat) {
81 mem_test_fail((void *)&vbuf32[i], pat, vbuf32[i]);
82 return ERR_GENERIC;
83 }
84
85 vbuf32[i] = ~pat;
86 }
87
88 /* repeat, walking from top down */
89 //printf("\t\ttop down invert\n");
90 for (i = len / 4; i > 0; i--) {
91 if (vbuf32[i-1] != ~pat) {
92 mem_test_fail((void *)&vbuf32[i-1], ~pat, vbuf32[i-1]);
93 return ERR_GENERIC;
94 }
95
96 vbuf32[i-1] = pat;
97 }
98
99 /* verify that we have the original pattern */
100 //printf("\t\tfinal test\n");
101 for (i = 0; i < len / 4; i++) {
102 if (vbuf32[i] != pat) {
103 mem_test_fail((void *)&vbuf32[i], pat, vbuf32[i]);
104 return ERR_GENERIC;
105 }
106 }
107
108 return NO_ERROR;
109 }
110
do_mem_tests(void * ptr,size_t len)111 static void do_mem_tests(void *ptr, size_t len)
112 {
113 size_t i;
114
115 /* test 1: simple write address to memory, read back */
116 printf("test 1: simple address write, read back\n");
117 volatile uint32_t *vbuf32 = ptr;
118 for (i = 0; i < len / 4; i++) {
119 vbuf32[i] = i;
120 }
121
122 for (i = 0; i < len / 4; i++) {
123 if (vbuf32[i] != i) {
124 mem_test_fail((void *)&vbuf32[i], i, vbuf32[i]);
125 goto out;
126 }
127 }
128
129 /* test 2: write various patterns, read back */
130 printf("test 2: write patterns, read back\n");
131
132 static const uint32_t pat[] = {
133 0x0, 0xffffffff,
134 0xaaaaaaaa, 0x55555555,
135 };
136
137 for (size_t p = 0; p < countof(pat); p++) {
138 if (do_pattern_test(ptr, len, pat[p]) < 0)
139 goto out;
140 }
141 // shift bits through 32bit word
142 for (uint32_t p = 1; p != 0; p <<= 1) {
143 if (do_pattern_test(ptr, len, p) < 0)
144 goto out;
145 }
146 // shift bits through 16bit word, invert top of 32bit
147 for (uint16_t p = 1; p != 0; p <<= 1) {
148 if (do_pattern_test(ptr, len, ((~p) << 16) | p) < 0)
149 goto out;
150 }
151
152 /* test 3: moving inversion, patterns */
153 printf("test 3: moving inversions with patterns\n");
154 for (size_t p = 0; p < countof(pat); p++) {
155 if (do_moving_inversion_test(ptr, len, pat[p]) < 0)
156 goto out;
157
158 }
159 // shift bits through 32bit word
160 for (uint32_t p = 1; p != 0; p <<= 1) {
161 if (do_moving_inversion_test(ptr, len, p) < 0)
162 goto out;
163 }
164 // shift bits through 16bit word, invert top of 32bit
165 for (uint16_t p = 1; p != 0; p <<= 1) {
166 if (do_moving_inversion_test(ptr, len, ((~p) << 16) | p) < 0)
167 goto out;
168 }
169
170 out:
171 printf("done with tests\n");
172 }
173
mem_test(int argc,const cmd_args * argv)174 static int mem_test(int argc, const cmd_args *argv)
175 {
176 if (argc < 2) {
177 printf("not enough arguments\n");
178 usage:
179 printf("usage: %s <length>\n", argv[0].str);
180 printf("usage: %s <base> <length>\n", argv[0].str);
181 return -1;
182 }
183
184 if (argc == 2) {
185 void *ptr;
186 size_t len = argv[1].u;
187
188 #if WITH_KERNEL_VM
189 /* rounding up len to the next page */
190 len = page_align(len);
191 if (len == 0) {
192 printf("invalid length\n");
193 return -1;
194 }
195
196 /* allocate a region to test in */
197 status_t err = vmm_alloc_contiguous(vmm_get_kernel_aspace(), "memtest", len, &ptr, 0, 0, ARCH_MMU_FLAG_UNCACHED);
198 if (err < 0) {
199 printf("error %d allocating test region\n", err);
200 return -1;
201 }
202
203 paddr_t pa;
204 pa = vaddr_to_paddr(ptr);
205 printf("physical address 0x%lx\n", pa);
206 #else
207 /* allocate from the heap */
208 ptr = malloc(len);
209 if (!ptr ) {
210 printf("error allocating test area from heap\n");
211 return -1;
212 }
213
214 #endif
215
216 printf("got buffer at %p of length 0x%lx\n", ptr, len);
217
218 /* run the tests */
219 do_mem_tests(ptr, len);
220
221 #if WITH_KERNEL_VM
222 // XXX free memory region here
223 printf("NOTE: leaked memory\n");
224 #else
225 free(ptr);
226 #endif
227 } else if (argc == 3) {
228 void *ptr = argv[1].p;
229 size_t len = argv[2].u;
230
231 /* run the tests */
232 do_mem_tests(ptr, len);
233 } else {
234 goto usage;
235 }
236
237 return 0;
238 }
239
240 STATIC_COMMAND_START
241 STATIC_COMMAND("mem_test", "test memory", &mem_test)
242 STATIC_COMMAND_END(mem_tests);
243