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