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 <inttypes.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 PAGE_SIZE 4096 /* nominal 4k "pages" */ 24 #define TEST_SIZE (PAGE_SIZE * 4) /* 4 pages */ 25 26 #define ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0]))) 27 28 __attribute__((aligned(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 word = *ptr++; 212 213 b1 = word >> 24 & 0xff; 214 b2 = word >> 16 & 0xff; 215 b3 = word >> 8 & 0xff; 216 b4 = word & 0xff; 217 218 if ((b1 < b2 && b1 != 0) || 219 (b2 < b3 && b2 != 0) || 220 (b3 < b4 && b3 != 0)) { 221 ml_printf("Error %d, %d, %d, %d", b1, b2, b3, b4); 222 return false; 223 } else { 224 pdot(i); 225 } 226 } 227 ml_printf("done @ %p\n", ptr); 228 return true; 229 } 230 231 static bool read_test_data_u64(int offset) 232 { 233 uint64_t word, *ptr = (uint64_t *)&test_data[offset]; 234 int i; 235 const int max = (TEST_SIZE - offset) / sizeof(word); 236 237 ml_printf("Reading u64 from %#lx (offset %d):", ptr, offset); 238 239 for (i = 0; i < max; i++) { 240 uint8_t b1, b2, b3, b4, b5, b6, b7, b8; 241 word = *ptr++; 242 243 b1 = ((uint64_t) (word >> 56)) & 0xff; 244 b2 = ((uint64_t) (word >> 48)) & 0xff; 245 b3 = ((uint64_t) (word >> 40)) & 0xff; 246 b4 = (word >> 32) & 0xff; 247 b5 = (word >> 24) & 0xff; 248 b6 = (word >> 16) & 0xff; 249 b7 = (word >> 8) & 0xff; 250 b8 = (word >> 0) & 0xff; 251 252 if ((b1 < b2 && b1 != 0) || 253 (b2 < b3 && b2 != 0) || 254 (b3 < b4 && b3 != 0) || 255 (b4 < b5 && b4 != 0) || 256 (b5 < b6 && b5 != 0) || 257 (b6 < b7 && b6 != 0) || 258 (b7 < b8 && b7 != 0)) { 259 ml_printf("Error %d, %d, %d, %d, %d, %d, %d, %d", 260 b1, b2, b3, b4, b5, b6, b7, b8); 261 return false; 262 } else { 263 pdot(i); 264 } 265 } 266 ml_printf("done @ %p\n", ptr); 267 return true; 268 } 269 270 /* Read the test data and verify at various offsets */ 271 read_ufn read_ufns[] = { read_test_data_u16, 272 read_test_data_u32, 273 read_test_data_u64 }; 274 275 bool do_unsigned_reads(void) 276 { 277 int i; 278 bool ok = true; 279 280 for (i = 0; i < ARRAY_SIZE(read_ufns) && ok; i++) { 281 #if CHECK_UNALIGNED 282 int off; 283 for (off = 0; off < 8 && ok; off++) { 284 ok = read_ufns[i](off); 285 } 286 #else 287 ok = read_ufns[i](0); 288 #endif 289 } 290 291 return ok; 292 } 293 294 static bool do_unsigned_test(init_ufn fn) 295 { 296 #if CHECK_UNALIGNED 297 bool ok = true; 298 int i; 299 for (i = 0; i < 8 && ok; i++) { 300 fn(i); 301 ok = do_unsigned_reads(); 302 } 303 #else 304 fn(0); 305 return do_unsigned_reads(); 306 #endif 307 } 308 309 /* 310 * We need to ensure signed data is read into a larger data type to 311 * ensure that sign extension is working properly. 312 */ 313 314 static bool read_test_data_s8(int offset, bool neg_first) 315 { 316 int8_t *ptr = (int8_t *)&test_data[offset]; 317 int i; 318 const int max = (TEST_SIZE - offset) / 2; 319 320 ml_printf("Reading s8 pairs from %#lx (offset %d):", ptr, offset); 321 322 for (i = 0; i < max; i++) { 323 int16_t first, second; 324 bool ok; 325 first = *ptr++; 326 second = *ptr++; 327 328 if (neg_first && first < 0 && second > 0) { 329 pdot(i); 330 } else if (!neg_first && first > 0 && second < 0) { 331 pdot(i); 332 } else { 333 ml_printf("Error %d %c %d\n", first, neg_first ? '<' : '>', second); 334 return false; 335 } 336 } 337 ml_printf("done @ %p\n", ptr); 338 return true; 339 } 340 341 static bool read_test_data_s16(int offset, bool neg_first) 342 { 343 int16_t *ptr = (int16_t *)&test_data[offset]; 344 int i; 345 const int max = (TEST_SIZE - offset) / (sizeof(*ptr)); 346 347 ml_printf("Reading s16 from %#lx (offset %d, %s):", ptr, 348 offset, neg_first ? "neg" : "pos"); 349 350 for (i = 0; i < max; i++) { 351 int32_t data = *ptr++; 352 353 if (neg_first && data < 0) { 354 pdot(i); 355 } else if (data > 0) { 356 pdot(i); 357 } else { 358 ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>'); 359 return false; 360 } 361 } 362 ml_printf("done @ %p\n", ptr); 363 return true; 364 } 365 366 static bool read_test_data_s32(int offset, bool neg_first) 367 { 368 int32_t *ptr = (int32_t *)&test_data[offset]; 369 int i; 370 const int max = (TEST_SIZE - offset) / (sizeof(int32_t)); 371 372 ml_printf("Reading s32 from %#lx (offset %d, %s):", 373 ptr, offset, neg_first ? "neg" : "pos"); 374 375 for (i = 0; i < max; i++) { 376 int64_t data = *ptr++; 377 378 if (neg_first && data < 0) { 379 pdot(i); 380 } else if (data > 0) { 381 pdot(i); 382 } else { 383 ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>'); 384 return false; 385 } 386 } 387 ml_printf("done @ %p\n", ptr); 388 return true; 389 } 390 391 /* 392 * Read the test data and verify at various offsets 393 * 394 * For everything except bytes all our reads should be either positive 395 * or negative depending on what offset we are reading from. Currently 396 * we only handle LE systems. 397 */ 398 read_sfn read_sfns[] = { read_test_data_s8, 399 read_test_data_s16, 400 read_test_data_s32 }; 401 402 bool do_signed_reads(bool neg_first) 403 { 404 int i; 405 bool ok = true; 406 407 for (i = 0; i < ARRAY_SIZE(read_sfns) && ok; i++) { 408 #if CHECK_UNALIGNED 409 int off; 410 for (off = 0; off < 8 && ok; off++) { 411 bool nf = i == 0 ? neg_first ^ (off & 1) : !(neg_first ^ (off & 1)); 412 ok = read_sfns[i](off, nf); 413 } 414 #else 415 ok = read_sfns[i](0, i == 0 ? neg_first : !neg_first); 416 #endif 417 } 418 419 return ok; 420 } 421 422 init_ufn init_ufns[] = { init_test_data_u8, 423 init_test_data_u16, 424 init_test_data_u32, 425 init_test_data_u64 }; 426 427 int main(void) 428 { 429 int i; 430 bool ok = true; 431 432 /* Run through the unsigned tests first */ 433 for (i = 0; i < ARRAY_SIZE(init_ufns) && ok; i++) { 434 ok = do_unsigned_test(init_ufns[i]); 435 } 436 437 if (ok) { 438 init_test_data_s8(false); 439 ok = do_signed_reads(false); 440 } 441 442 if (ok) { 443 init_test_data_s8(true); 444 ok = do_signed_reads(true); 445 } 446 447 ml_printf("Test complete: %s\n", ok ? "PASSED" : "FAILED"); 448 return ok ? 0 : -1; 449 } 450