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