1 /*
2 * Copyright (c) 2014 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <lk/err.h>
12 #include <arch.h>
13 #include <arch/ops.h>
14 #include <lk/console_cmd.h>
15 #include <platform.h>
16 #include <lk/debug.h>
17
18 #if WITH_KERNEL_VM
19 #include <kernel/vm.h>
20 #endif
21
mem_test_fail(void * ptr,uint32_t should,uint32_t is)22 static void mem_test_fail(void *ptr, uint32_t should, uint32_t is) {
23 printf("ERROR at %p: should be 0x%x, is 0x%x\n", ptr, should, is);
24
25 ptr = (void *)ROUNDDOWN((uintptr_t)ptr, 64);
26 hexdump(ptr, 128);
27 }
28
do_pattern_test(void * ptr,size_t len,uint32_t pat)29 static status_t do_pattern_test(void *ptr, size_t len, uint32_t pat) {
30 volatile uint32_t *vbuf32 = ptr;
31 size_t i;
32
33 printf("\tpattern 0x%08x\n", pat);
34 for (i = 0; i < len / 4; i++) {
35 vbuf32[i] = pat;
36 }
37
38 for (i = 0; i < len / 4; i++) {
39 if (vbuf32[i] != pat) {
40 mem_test_fail((void *)&vbuf32[i], pat, vbuf32[i]);
41 return ERR_GENERIC;
42 }
43 }
44
45 return NO_ERROR;
46 }
47
do_moving_inversion_test(void * ptr,size_t len,uint32_t pat)48 static status_t do_moving_inversion_test(void *ptr, size_t len, uint32_t pat) {
49 volatile uint32_t *vbuf32 = ptr;
50 size_t i;
51
52 printf("\tpattern 0x%08x\n", pat);
53
54 /* fill memory */
55 for (i = 0; i < len / 4; i++) {
56 vbuf32[i] = pat;
57 }
58
59 /* from the bottom, walk through each cell, inverting the value */
60 //printf("\t\tbottom up invert\n");
61 for (i = 0; i < len / 4; i++) {
62 if (vbuf32[i] != pat) {
63 mem_test_fail((void *)&vbuf32[i], pat, vbuf32[i]);
64 return ERR_GENERIC;
65 }
66
67 vbuf32[i] = ~pat;
68 }
69
70 /* repeat, walking from top down */
71 //printf("\t\ttop down invert\n");
72 for (i = len / 4; i > 0; i--) {
73 if (vbuf32[i-1] != ~pat) {
74 mem_test_fail((void *)&vbuf32[i-1], ~pat, vbuf32[i-1]);
75 return ERR_GENERIC;
76 }
77
78 vbuf32[i-1] = pat;
79 }
80
81 /* verify that we have the original pattern */
82 //printf("\t\tfinal test\n");
83 for (i = 0; i < len / 4; i++) {
84 if (vbuf32[i] != pat) {
85 mem_test_fail((void *)&vbuf32[i], pat, vbuf32[i]);
86 return ERR_GENERIC;
87 }
88 }
89
90 return NO_ERROR;
91 }
92
do_mem_tests(void * ptr,size_t len)93 static void do_mem_tests(void *ptr, size_t len) {
94 size_t i;
95
96 printf("running memory tests on address range [%p..%p]\n",
97 ptr, (uint8_t *)ptr + len - 1);
98
99 /* test 1: simple write address to memory, read back */
100 printf("test 1: simple address write, read back\n");
101 volatile uint32_t *vbuf32 = ptr;
102 for (i = 0; i < len / 4; i++) {
103 vbuf32[i] = i;
104 }
105
106 for (i = 0; i < len / 4; i++) {
107 if (vbuf32[i] != i) {
108 mem_test_fail((void *)&vbuf32[i], i, vbuf32[i]);
109 goto out;
110 }
111 }
112
113 /* test 2: write various patterns, read back */
114 printf("test 2: write patterns, read back\n");
115
116 static const uint32_t pat[] = {
117 0x0, 0xffffffff,
118 0xaaaaaaaa, 0x55555555,
119 };
120
121 for (size_t p = 0; p < countof(pat); p++) {
122 if (do_pattern_test(ptr, len, pat[p]) < 0)
123 goto out;
124 }
125 // shift bits through 32bit word
126 for (uint32_t p = 1; p != 0; p <<= 1) {
127 if (do_pattern_test(ptr, len, p) < 0)
128 goto out;
129 }
130 // shift bits through 16bit word, invert top of 32bit
131 for (uint16_t p = 1; p != 0; p <<= 1) {
132 if (do_pattern_test(ptr, len, ((~p) << 16) | p) < 0)
133 goto out;
134 }
135
136 /* test 3: moving inversion, patterns */
137 printf("test 3: moving inversions with patterns\n");
138 for (size_t p = 0; p < countof(pat); p++) {
139 if (do_moving_inversion_test(ptr, len, pat[p]) < 0)
140 goto out;
141
142 }
143 // shift bits through 32bit word
144 for (uint32_t p = 1; p != 0; p <<= 1) {
145 if (do_moving_inversion_test(ptr, len, p) < 0)
146 goto out;
147 }
148 // shift bits through 16bit word, invert top of 32bit
149 for (uint16_t p = 1; p != 0; p <<= 1) {
150 if (do_moving_inversion_test(ptr, len, ((~p) << 16) | p) < 0)
151 goto out;
152 }
153
154 out:
155 printf("done with tests\n");
156 }
157
mem_test(int argc,const console_cmd_args * argv)158 static int mem_test(int argc, const console_cmd_args *argv) {
159 if (argc < 2) {
160 printf("not enough arguments\n");
161 usage:
162 printf("usage: %s <length>\n", argv[0].str);
163 printf("usage: %s <base> <length>\n", argv[0].str);
164 return -1;
165 }
166
167 if (argc == 2) {
168 void *ptr;
169 size_t len = argv[1].u;
170
171 #if WITH_KERNEL_VM
172 /* rounding up len to the next page */
173 len = PAGE_ALIGN(len);
174 if (len == 0) {
175 printf("invalid length\n");
176 return -1;
177 }
178
179 /* allocate a region to test in */
180 status_t err = vmm_alloc_contiguous(vmm_get_kernel_aspace(), "memtest", len, &ptr, 0, 0, ARCH_MMU_FLAG_UNCACHED);
181 if (err < 0) {
182 printf("error %d allocating test region\n", err);
183 return -1;
184 }
185
186 paddr_t pa;
187 pa = vaddr_to_paddr(ptr);
188 printf("physical address 0x%lx\n", pa);
189 #else
190 /* allocate from the heap */
191 ptr = malloc(len);
192 if (!ptr ) {
193 printf("error allocating test area from heap\n");
194 return -1;
195 }
196
197 #endif
198
199 printf("got buffer at %p of length %#zx\n", ptr, len);
200
201 /* run the tests */
202 do_mem_tests(ptr, len);
203
204 #if WITH_KERNEL_VM
205 // XXX free memory region here
206 printf("NOTE: leaked memory\n");
207 #else
208 free(ptr);
209 #endif
210 } else if (argc == 3) {
211 void *ptr = argv[1].p;
212 size_t len = argv[2].u;
213
214 /* run the tests */
215 do_mem_tests(ptr, len);
216 } else {
217 goto usage;
218 }
219
220 return 0;
221 }
222
223 STATIC_COMMAND_START
224 STATIC_COMMAND("mem_test", "test memory", &mem_test)
225 STATIC_COMMAND_END(mem_tests);
226