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