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