xref: /openbmc/qemu/tests/tcg/multiarch/system/memory.c (revision 2e1cacfb)
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 
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 
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 
91 static inline uint8_t get_byte(int index, bool neg)
92 {
93     return neg ? (0xff << (index % 7)) : (0xff >> ((index % 6) + 1));
94 }
95 
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  */
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 
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 
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
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 
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 
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
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 
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 
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 
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 
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 
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 
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 
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