1 /*
2 * Memory Test
3 *
4 * This is intended to test the system-mode code and ensure we properly
5 * behave across normal and unaligned accesses across several pages.
6 * We are not replicating memory tests for stuck bits and other
7 * hardware level failures but looking for issues with different size
8 * accesses when access is:
9 *
10 * - unaligned at various sizes (if -DCHECK_UNALIGNED set)
11 * - spanning a (system) page
12 * - sign extension when loading
13 */
14
15 #include <stdint.h>
16 #include <stdbool.h>
17 #include <minilib.h>
18
19 #ifndef CHECK_UNALIGNED
20 # error "Target does not specify CHECK_UNALIGNED"
21 #endif
22
23 uint32_t test_read_count;
24 uint32_t test_write_count;
25
26 #define MEM_PAGE_SIZE 4096 /* nominal 4k "pages" */
27 #define TEST_SIZE (MEM_PAGE_SIZE * 4) /* 4 pages */
28
29 #define ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0])))
30
31 __attribute__((aligned(TEST_SIZE)))
32 static uint8_t test_data[TEST_SIZE];
33
34 typedef void (*init_ufn) (int offset);
35 typedef bool (*read_ufn) (int offset);
36 typedef bool (*read_sfn) (int offset, bool nf);
37
pdot(int count,bool write)38 static void pdot(int count, bool write)
39 {
40 if (write) {
41 test_write_count++;
42 } else {
43 test_read_count++;
44 }
45 if (count % 128 == 0) {
46 ml_printf(".");
47 }
48 }
49
50 /*
51 * Helper macros for endian handling.
52 */
53 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
54 #define BYTE_SHIFT(b, pos) (b << (pos * 8))
55 #define BYTE_NEXT(b) ((b)++)
56 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
57 #define BYTE_SHIFT(b, pos) (b << ((sizeof(b) - 1 - (pos)) * 8))
58 #define BYTE_NEXT(b) (--(b))
59 #else
60 #error Unsupported __BYTE_ORDER__
61 #endif
62
63 /*
64 * Fill the data with ascending (for little-endian) or descending (for
65 * big-endian) value bytes.
66 */
67
init_test_data_u8(int unused_offset)68 static void init_test_data_u8(int unused_offset)
69 {
70 uint8_t count = 0, *ptr = &test_data[0];
71 int i;
72 (void)(unused_offset);
73
74 ml_printf("Filling test area with u8 (%p):", ptr);
75
76 for (i = 0; i < TEST_SIZE; i++) {
77 *ptr++ = BYTE_NEXT(count);
78 pdot(i, true);
79 }
80
81 ml_printf("done %d @ %p\n", i, ptr);
82 }
83
84 /*
85 * Fill the data with alternating positive and negative bytes. This
86 * should mean for reads larger than a byte all subsequent reads will
87 * stay either negative or positive. We never write 0.
88 */
89
get_byte(int index,bool neg)90 static inline uint8_t get_byte(int index, bool neg)
91 {
92 return neg ? (0xff << (index % 7)) : (0xff >> ((index % 6) + 1));
93 }
94
init_test_data_s8(bool neg_first)95 static void init_test_data_s8(bool neg_first)
96 {
97 uint8_t top, bottom, *ptr = &test_data[0];
98 int i;
99
100 ml_printf("Filling test area with s8 pairs (%s):",
101 neg_first ? "neg first" : "pos first");
102 for (i = 0; i < TEST_SIZE / 2; i++) {
103 *ptr++ = get_byte(i, neg_first);
104 pdot(i, true);
105 *ptr++ = get_byte(i, !neg_first);
106 pdot(i, true);
107 }
108 ml_printf("done %d @ %p\n", i * 2, ptr);
109 }
110
111 /*
112 * Zero the first few bytes of the test data in preparation for
113 * new offset values.
114 */
reset_start_data(int offset)115 static void reset_start_data(int offset)
116 {
117 uint32_t *ptr = (uint32_t *) &test_data[0];
118 int i;
119
120 if (!offset) {
121 return;
122 }
123
124 ml_printf("Flushing %d bytes from %p: ", offset, ptr);
125
126 for (i = 0; i < offset; i++) {
127 *ptr++ = 0;
128 pdot(i, true);
129 }
130
131 ml_printf("done %d @ %p\n", i, ptr);
132 }
133
init_test_data_u16(int offset)134 static void init_test_data_u16(int offset)
135 {
136 uint8_t count = 0;
137 uint16_t word, *ptr = (uint16_t *) &test_data[offset];
138 const int max = (TEST_SIZE - offset) / sizeof(word);
139 int i;
140
141 reset_start_data(offset);
142
143 ml_printf("Filling test area with u16 (offset %d, %p):", offset, ptr);
144
145 for (i = 0; i < max; i++) {
146 uint16_t low = BYTE_NEXT(count), high = BYTE_NEXT(count);
147 word = BYTE_SHIFT(high, 1) | BYTE_SHIFT(low, 0);
148 *ptr++ = word;
149 pdot(i, true);
150 }
151 ml_printf("done %d @ %p\n", i, ptr);
152 }
153
init_test_data_u32(int offset)154 static void init_test_data_u32(int offset)
155 {
156 uint8_t count = 0;
157 uint32_t word, *ptr = (uint32_t *) &test_data[offset];
158 const int max = (TEST_SIZE - offset) / sizeof(word);
159 int i;
160
161 reset_start_data(offset);
162
163 ml_printf("Filling test area with u32 (offset %d, %p):", offset, ptr);
164
165 for (i = 0; i < max; i++) {
166 uint32_t b4 = BYTE_NEXT(count), b3 = BYTE_NEXT(count);
167 uint32_t b2 = BYTE_NEXT(count), b1 = BYTE_NEXT(count);
168 word = BYTE_SHIFT(b1, 3) | BYTE_SHIFT(b2, 2) | BYTE_SHIFT(b3, 1) |
169 BYTE_SHIFT(b4, 0);
170 *ptr++ = word;
171 pdot(i, true);
172 }
173 ml_printf("done %d @ %p\n", i, ptr);
174 }
175
176 #if __SIZEOF_POINTER__ >= 8
init_test_data_u64(int offset)177 static void init_test_data_u64(int offset)
178 {
179 uint8_t count = 0;
180 uint64_t word, *ptr = (uint64_t *) &test_data[offset];
181 const int max = (TEST_SIZE - offset) / sizeof(word);
182 int i;
183
184 reset_start_data(offset);
185
186 ml_printf("Filling test area with u64 (offset %d, %p):", offset, ptr);
187
188 for (i = 0; i < max; i++) {
189 uint64_t b8 = BYTE_NEXT(count), b7 = BYTE_NEXT(count);
190 uint64_t b6 = BYTE_NEXT(count), b5 = BYTE_NEXT(count);
191 uint64_t b4 = BYTE_NEXT(count), b3 = BYTE_NEXT(count);
192 uint64_t b2 = BYTE_NEXT(count), b1 = BYTE_NEXT(count);
193 word = BYTE_SHIFT(b1, 7) | BYTE_SHIFT(b2, 6) | BYTE_SHIFT(b3, 5) |
194 BYTE_SHIFT(b4, 4) | BYTE_SHIFT(b5, 3) | BYTE_SHIFT(b6, 2) |
195 BYTE_SHIFT(b7, 1) | BYTE_SHIFT(b8, 0);
196 *ptr++ = word;
197 pdot(i, true);
198 }
199 ml_printf("done %d @ %p\n", i, ptr);
200 }
201 #endif
202
read_test_data_u16(int offset)203 static bool read_test_data_u16(int offset)
204 {
205 uint16_t word, *ptr = (uint16_t *)&test_data[offset];
206 int i;
207 const int max = (TEST_SIZE - offset) / sizeof(word);
208
209 ml_printf("Reading u16 from %#lx (offset %d):", ptr, offset);
210
211 for (i = 0; i < max; i++) {
212 uint8_t high, low;
213 word = *ptr++;
214 high = (word >> 8) & 0xff;
215 low = word & 0xff;
216 if (high < low && high != 0) {
217 ml_printf("Error %d < %d\n", high, low);
218 return false;
219 } else {
220 pdot(i, false);
221 }
222
223 }
224 ml_printf("done %d @ %p\n", i, ptr);
225 return true;
226 }
227
read_test_data_u32(int offset)228 static bool read_test_data_u32(int offset)
229 {
230 uint32_t word, *ptr = (uint32_t *)&test_data[offset];
231 int i;
232 const int max = (TEST_SIZE - offset) / sizeof(word);
233
234 ml_printf("Reading u32 from %#lx (offset %d):", ptr, offset);
235
236 for (i = 0; i < max; i++) {
237 uint8_t b1, b2, b3, b4;
238 int zeros = 0;
239 word = *ptr++;
240
241 b1 = word >> 24 & 0xff;
242 b2 = word >> 16 & 0xff;
243 b3 = word >> 8 & 0xff;
244 b4 = word & 0xff;
245
246 zeros += (b1 == 0 ? 1 : 0);
247 zeros += (b2 == 0 ? 1 : 0);
248 zeros += (b3 == 0 ? 1 : 0);
249 zeros += (b4 == 0 ? 1 : 0);
250 if (zeros > 1) {
251 ml_printf("Error @ %p, more zeros than expected: %d, %d, %d, %d",
252 ptr - 1, b1, b2, b3, b4);
253 return false;
254 }
255
256 if ((b1 < b2 && b1 != 0) ||
257 (b2 < b3 && b2 != 0) ||
258 (b3 < b4 && b3 != 0)) {
259 ml_printf("Error %d, %d, %d, %d", b1, b2, b3, b4);
260 return false;
261 } else {
262 pdot(i, false);
263 }
264 }
265 ml_printf("done %d @ %p\n", i, ptr);
266 return true;
267 }
268
269 #if __SIZEOF_POINTER__ >= 8
read_test_data_u64(int offset)270 static bool read_test_data_u64(int offset)
271 {
272 uint64_t word, *ptr = (uint64_t *)&test_data[offset];
273 int i;
274 const int max = (TEST_SIZE - offset) / sizeof(word);
275
276 ml_printf("Reading u64 from %#lx (offset %d):", ptr, offset);
277
278 for (i = 0; i < max; i++) {
279 uint8_t b1, b2, b3, b4, b5, b6, b7, b8;
280 int zeros = 0;
281 word = *ptr++;
282
283 b1 = ((uint64_t) (word >> 56)) & 0xff;
284 b2 = ((uint64_t) (word >> 48)) & 0xff;
285 b3 = ((uint64_t) (word >> 40)) & 0xff;
286 b4 = (word >> 32) & 0xff;
287 b5 = (word >> 24) & 0xff;
288 b6 = (word >> 16) & 0xff;
289 b7 = (word >> 8) & 0xff;
290 b8 = (word >> 0) & 0xff;
291
292 zeros += (b1 == 0 ? 1 : 0);
293 zeros += (b2 == 0 ? 1 : 0);
294 zeros += (b3 == 0 ? 1 : 0);
295 zeros += (b4 == 0 ? 1 : 0);
296 zeros += (b5 == 0 ? 1 : 0);
297 zeros += (b6 == 0 ? 1 : 0);
298 zeros += (b7 == 0 ? 1 : 0);
299 zeros += (b8 == 0 ? 1 : 0);
300 if (zeros > 1) {
301 ml_printf("Error @ %p, more zeros than expected: %d, %d, %d, %d, %d, %d, %d, %d",
302 ptr - 1, b1, b2, b3, b4, b5, b6, b7, b8);
303 return false;
304 }
305
306 if ((b1 < b2 && b1 != 0) ||
307 (b2 < b3 && b2 != 0) ||
308 (b3 < b4 && b3 != 0) ||
309 (b4 < b5 && b4 != 0) ||
310 (b5 < b6 && b5 != 0) ||
311 (b6 < b7 && b6 != 0) ||
312 (b7 < b8 && b7 != 0)) {
313 ml_printf("Error %d, %d, %d, %d, %d, %d, %d, %d",
314 b1, b2, b3, b4, b5, b6, b7, b8);
315 return false;
316 } else {
317 pdot(i, false);
318 }
319 }
320 ml_printf("done %d @ %p\n", i, ptr);
321 return true;
322 }
323 #endif
324
325 /* Read the test data and verify at various offsets */
326 read_ufn read_ufns[] = {
327 read_test_data_u16,
328 read_test_data_u32,
329 #if __SIZEOF_POINTER__ >= 8
330 read_test_data_u64
331 #endif
332 };
333
do_unsigned_reads(int start_off)334 bool do_unsigned_reads(int start_off)
335 {
336 int i;
337 bool ok = true;
338
339 for (i = 0; i < ARRAY_SIZE(read_ufns) && ok; i++) {
340 #if CHECK_UNALIGNED
341 int off;
342 for (off = start_off; off < 8 && ok; off++) {
343 ok = read_ufns[i](off);
344 }
345 #else
346 ok = read_ufns[i](start_off);
347 #endif
348 }
349
350 return ok;
351 }
352
do_unsigned_test(init_ufn fn)353 static bool do_unsigned_test(init_ufn fn)
354 {
355 #if CHECK_UNALIGNED
356 bool ok = true;
357 int i;
358 for (i = 0; i < 8 && ok; i++) {
359 fn(i);
360 ok = do_unsigned_reads(i);
361 }
362 return ok;
363 #else
364 fn(0);
365 return do_unsigned_reads(0);
366 #endif
367 }
368
369 /*
370 * We need to ensure signed data is read into a larger data type to
371 * ensure that sign extension is working properly.
372 */
373
read_test_data_s8(int offset,bool neg_first)374 static bool read_test_data_s8(int offset, bool neg_first)
375 {
376 int8_t *ptr = (int8_t *)&test_data[offset];
377 int i;
378 const int max = (TEST_SIZE - offset) / 2;
379
380 ml_printf("Reading s8 pairs from %#lx (offset %d):", ptr, offset);
381
382 for (i = 0; i < max; i++) {
383 int16_t first, second;
384 bool ok;
385 first = *ptr++;
386 second = *ptr++;
387
388 if (neg_first && first < 0 && second > 0) {
389 pdot(i, false);
390 pdot(i, false);
391 } else if (!neg_first && first > 0 && second < 0) {
392 pdot(i, false);
393 pdot(i, false);
394 } else {
395 ml_printf("Error %d %c %d\n", first, neg_first ? '<' : '>', second);
396 return false;
397 }
398 }
399 ml_printf("done %d @ %p\n", i * 2, ptr);
400 return true;
401 }
402
read_test_data_s16(int offset,bool neg_first)403 static bool read_test_data_s16(int offset, bool neg_first)
404 {
405 int16_t *ptr = (int16_t *)&test_data[offset];
406 int i;
407 const int max = (TEST_SIZE - offset) / (sizeof(*ptr));
408
409 ml_printf("Reading s16 from %#lx (offset %d, %s):", ptr,
410 offset, neg_first ? "neg" : "pos");
411
412 /*
413 * If the first byte is negative, then the last byte is positive.
414 * Therefore the logic below must be flipped for big-endian.
415 */
416 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
417 neg_first = !neg_first;
418 #endif
419
420 for (i = 0; i < max; i++) {
421 int32_t data = *ptr++;
422
423 if (neg_first && data < 0) {
424 pdot(i, false);
425 } else if (!neg_first && data > 0) {
426 pdot(i, false);
427 } else {
428 ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>');
429 return false;
430 }
431 }
432 ml_printf("done %d @ %p\n", i, ptr);
433 return true;
434 }
435
read_test_data_s32(int offset,bool neg_first)436 static bool read_test_data_s32(int offset, bool neg_first)
437 {
438 int32_t *ptr = (int32_t *)&test_data[offset];
439 int i;
440 const int max = (TEST_SIZE - offset) / (sizeof(int32_t));
441
442 ml_printf("Reading s32 from %#lx (offset %d, %s):",
443 ptr, offset, neg_first ? "neg" : "pos");
444
445 /*
446 * If the first byte is negative, then the last byte is positive.
447 * Therefore the logic below must be flipped for big-endian.
448 */
449 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
450 neg_first = !neg_first;
451 #endif
452
453 for (i = 0; i < max; i++) {
454 int64_t data = *ptr++;
455
456 if (neg_first && data < 0) {
457 pdot(i, false);
458 } else if (!neg_first && data > 0) {
459 pdot(i, false);
460 } else {
461 ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>');
462 return false;
463 }
464 }
465 ml_printf("done %d @ %p\n", i, ptr);
466 return true;
467 }
468
469 /*
470 * Read the test data and verify at various offsets
471 *
472 * For everything except bytes all our reads should be either positive
473 * or negative depending on what offset we are reading from.
474 */
475 read_sfn read_sfns[] = { read_test_data_s8,
476 read_test_data_s16,
477 read_test_data_s32 };
478
do_signed_reads(bool neg_first)479 bool do_signed_reads(bool neg_first)
480 {
481 int i;
482 bool ok = true;
483
484 for (i = 0; i < ARRAY_SIZE(read_sfns) && ok; i++) {
485 #if CHECK_UNALIGNED
486 int off;
487 for (off = 0; off < 8 && ok; off++) {
488 bool nf = i == 0 ? neg_first ^ (off & 1) : !(neg_first ^ (off & 1));
489 ok = read_sfns[i](off, nf);
490 }
491 #else
492 ok = read_sfns[i](0, i == 0 ? neg_first : !neg_first);
493 #endif
494 }
495
496 return ok;
497 }
498
499 init_ufn init_ufns[] = {
500 init_test_data_u8,
501 init_test_data_u16,
502 init_test_data_u32,
503 #if __SIZEOF_POINTER__ >= 8
504 init_test_data_u64
505 #endif
506 };
507
main(void)508 int main(void)
509 {
510 int i;
511 bool ok = true;
512
513 ml_printf("Test data start: 0x%lx\n", (unsigned long)&test_data[0]);
514 ml_printf("Test data end: 0x%lx\n", (unsigned long)&test_data[TEST_SIZE]);
515
516 /* Run through the unsigned tests first */
517 for (i = 0; i < ARRAY_SIZE(init_ufns) && ok; i++) {
518 ok = do_unsigned_test(init_ufns[i]);
519 }
520
521 if (ok) {
522 init_test_data_s8(false);
523 ok = do_signed_reads(false);
524 }
525
526 if (ok) {
527 init_test_data_s8(true);
528 ok = do_signed_reads(true);
529 }
530
531 ml_printf("Test data read: %lu\n", (unsigned long)test_read_count);
532 ml_printf("Test data write: %lu\n", (unsigned long)test_write_count);
533 ml_printf("Test complete: %s\n", ok ? "PASSED" : "FAILED");
534 return ok ? 0 : -1;
535 }
536