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