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