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 /* test 1: simple write address to memory, read back */
97 printf("test 1: simple address write, read back\n");
98 volatile uint32_t *vbuf32 = ptr;
99 for (i = 0; i < len / 4; i++) {
100 vbuf32[i] = i;
101 }
102
103 for (i = 0; i < len / 4; i++) {
104 if (vbuf32[i] != i) {
105 mem_test_fail((void *)&vbuf32[i], i, vbuf32[i]);
106 goto out;
107 }
108 }
109
110 /* test 2: write various patterns, read back */
111 printf("test 2: write patterns, read back\n");
112
113 static const uint32_t pat[] = {
114 0x0, 0xffffffff,
115 0xaaaaaaaa, 0x55555555,
116 };
117
118 for (size_t p = 0; p < countof(pat); p++) {
119 if (do_pattern_test(ptr, len, pat[p]) < 0)
120 goto out;
121 }
122 // shift bits through 32bit word
123 for (uint32_t p = 1; p != 0; p <<= 1) {
124 if (do_pattern_test(ptr, len, p) < 0)
125 goto out;
126 }
127 // shift bits through 16bit word, invert top of 32bit
128 for (uint16_t p = 1; p != 0; p <<= 1) {
129 if (do_pattern_test(ptr, len, ((~p) << 16) | p) < 0)
130 goto out;
131 }
132
133 /* test 3: moving inversion, patterns */
134 printf("test 3: moving inversions with patterns\n");
135 for (size_t p = 0; p < countof(pat); p++) {
136 if (do_moving_inversion_test(ptr, len, pat[p]) < 0)
137 goto out;
138
139 }
140 // shift bits through 32bit word
141 for (uint32_t p = 1; p != 0; p <<= 1) {
142 if (do_moving_inversion_test(ptr, len, p) < 0)
143 goto out;
144 }
145 // shift bits through 16bit word, invert top of 32bit
146 for (uint16_t p = 1; p != 0; p <<= 1) {
147 if (do_moving_inversion_test(ptr, len, ((~p) << 16) | p) < 0)
148 goto out;
149 }
150
151 out:
152 printf("done with tests\n");
153 }
154
mem_test(int argc,const console_cmd_args * argv)155 static int mem_test(int argc, const console_cmd_args *argv) {
156 if (argc < 2) {
157 printf("not enough arguments\n");
158 usage:
159 printf("usage: %s <length>\n", argv[0].str);
160 printf("usage: %s <base> <length>\n", argv[0].str);
161 return -1;
162 }
163
164 if (argc == 2) {
165 void *ptr;
166 size_t len = argv[1].u;
167
168 #if WITH_KERNEL_VM
169 /* rounding up len to the next page */
170 len = PAGE_ALIGN(len);
171 if (len == 0) {
172 printf("invalid length\n");
173 return -1;
174 }
175
176 /* allocate a region to test in */
177 status_t err = vmm_alloc_contiguous(vmm_get_kernel_aspace(), "memtest", len, &ptr, 0, 0, ARCH_MMU_FLAG_UNCACHED);
178 if (err < 0) {
179 printf("error %d allocating test region\n", err);
180 return -1;
181 }
182
183 paddr_t pa;
184 pa = vaddr_to_paddr(ptr);
185 printf("physical address 0x%lx\n", pa);
186 #else
187 /* allocate from the heap */
188 ptr = malloc(len);
189 if (!ptr ) {
190 printf("error allocating test area from heap\n");
191 return -1;
192 }
193
194 #endif
195
196 printf("got buffer at %p of length %#zx\n", ptr, len);
197
198 /* run the tests */
199 do_mem_tests(ptr, len);
200
201 #if WITH_KERNEL_VM
202 // XXX free memory region here
203 printf("NOTE: leaked memory\n");
204 #else
205 free(ptr);
206 #endif
207 } else if (argc == 3) {
208 void *ptr = argv[1].p;
209 size_t len = argv[2].u;
210
211 /* run the tests */
212 do_mem_tests(ptr, len);
213 } else {
214 goto usage;
215 }
216
217 return 0;
218 }
219
220 STATIC_COMMAND_START
221 STATIC_COMMAND("mem_test", "test memory", &mem_test)
222 STATIC_COMMAND_END(mem_tests);
223