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