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