1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Test the powerpc alignment handler on POWER8/POWER9 4 * 5 * Copyright (C) 2017 IBM Corporation (Michael Neuling, Andrew Donnellan) 6 */ 7 8 /* 9 * This selftest exercises the powerpc alignment fault handler. 10 * 11 * We create two sets of source and destination buffers, one in regular memory, 12 * the other cache-inhibited (by default we use /dev/fb0 for this, but an 13 * alterative path for cache-inhibited memory may be provided). 14 * 15 * One way to get cache-inhibited memory is to use the "mem" kernel parameter 16 * to limit the kernel to less memory than actually exists. Addresses above 17 * the limit may still be accessed but will be treated as cache-inhibited. For 18 * example, if there is actually 4GB of memory and the parameter "mem=3GB" is 19 * used, memory from address 0xC0000000 onwards is treated as cache-inhibited. 20 * To access this region /dev/mem is used. The kernel should be configured 21 * without CONFIG_STRICT_DEVMEM. In this case use: 22 * ./alignment_handler /dev/mem 0xc0000000 23 * 24 * We initialise the source buffers, then use whichever set of load/store 25 * instructions is under test to copy bytes from the source buffers to the 26 * destination buffers. For the regular buffers, these instructions will 27 * execute normally. For the cache-inhibited buffers, these instructions 28 * will trap and cause an alignment fault, and the alignment fault handler 29 * will emulate the particular instruction under test. We then compare the 30 * destination buffers to ensure that the native and emulated cases give the 31 * same result. 32 * 33 * TODO: 34 * - Any FIXMEs below 35 * - Test VSX regs < 32 and > 32 36 * - Test all loads and stores 37 * - Check update forms do update register 38 * - Test alignment faults over page boundary 39 * 40 * Some old binutils may not support all the instructions. 41 */ 42 43 44 #include <sys/mman.h> 45 #include <sys/types.h> 46 #include <sys/stat.h> 47 #include <fcntl.h> 48 #include <unistd.h> 49 #include <stdbool.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <assert.h> 54 #include <getopt.h> 55 #include <setjmp.h> 56 #include <signal.h> 57 58 #include <asm/cputable.h> 59 60 #include "utils.h" 61 62 int bufsize; 63 int debug; 64 int testing; 65 volatile int gotsig; 66 char *cipath = "/dev/fb0"; 67 long cioffset; 68 69 void sighandler(int sig, siginfo_t *info, void *ctx) 70 { 71 ucontext_t *ucp = ctx; 72 73 if (!testing) { 74 signal(sig, SIG_DFL); 75 kill(0, sig); 76 } 77 gotsig = sig; 78 #ifdef __powerpc64__ 79 ucp->uc_mcontext.gp_regs[PT_NIP] += 4; 80 #else 81 ucp->uc_mcontext.uc_regs->gregs[PT_NIP] += 4; 82 #endif 83 } 84 85 #define XFORM(reg, n) " " #reg " ,%"#n",%2 ;" 86 #define DFORM(reg, n) " " #reg " ,0(%"#n") ;" 87 88 #define TEST(name, ld_op, st_op, form, ld_reg, st_reg) \ 89 void test_##name(char *s, char *d) \ 90 { \ 91 asm volatile( \ 92 #ld_op form(ld_reg, 0) \ 93 #st_op form(st_reg, 1) \ 94 :: "r"(s), "r"(d), "r"(0) \ 95 : "memory", "vs0", "vs32", "r31"); \ 96 } \ 97 rc |= do_test(#name, test_##name) 98 99 #define LOAD_VSX_XFORM_TEST(op) TEST(op, op, stxvd2x, XFORM, 32, 32) 100 #define STORE_VSX_XFORM_TEST(op) TEST(op, lxvd2x, op, XFORM, 32, 32) 101 #define LOAD_VSX_DFORM_TEST(op) TEST(op, op, stxv, DFORM, 32, 32) 102 #define STORE_VSX_DFORM_TEST(op) TEST(op, lxv, op, DFORM, 32, 32) 103 #define LOAD_VMX_XFORM_TEST(op) TEST(op, op, stxvd2x, XFORM, 0, 32) 104 #define STORE_VMX_XFORM_TEST(op) TEST(op, lxvd2x, op, XFORM, 32, 0) 105 #define LOAD_VMX_DFORM_TEST(op) TEST(op, op, stxv, DFORM, 0, 32) 106 #define STORE_VMX_DFORM_TEST(op) TEST(op, lxv, op, DFORM, 32, 0) 107 108 #define LOAD_XFORM_TEST(op) TEST(op, op, stdx, XFORM, 31, 31) 109 #define STORE_XFORM_TEST(op) TEST(op, ldx, op, XFORM, 31, 31) 110 #define LOAD_DFORM_TEST(op) TEST(op, op, std, DFORM, 31, 31) 111 #define STORE_DFORM_TEST(op) TEST(op, ld, op, DFORM, 31, 31) 112 113 #define LOAD_FLOAT_DFORM_TEST(op) TEST(op, op, stfd, DFORM, 0, 0) 114 #define STORE_FLOAT_DFORM_TEST(op) TEST(op, lfd, op, DFORM, 0, 0) 115 #define LOAD_FLOAT_XFORM_TEST(op) TEST(op, op, stfdx, XFORM, 0, 0) 116 #define STORE_FLOAT_XFORM_TEST(op) TEST(op, lfdx, op, XFORM, 0, 0) 117 118 119 /* FIXME: Unimplemented tests: */ 120 // STORE_DFORM_TEST(stq) /* FIXME: need two registers for quad */ 121 // STORE_DFORM_TEST(stswi) /* FIXME: string instruction */ 122 123 // STORE_XFORM_TEST(stwat) /* AMO can't emulate or run on CI */ 124 // STORE_XFORM_TEST(stdat) /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ 125 126 127 /* preload byte by byte */ 128 void preload_data(void *dst, int offset, int width) 129 { 130 char *c = dst; 131 int i; 132 133 c += offset; 134 135 for (i = 0 ; i < width ; i++) 136 c[i] = i; 137 } 138 139 int test_memcpy(void *dst, void *src, int size, int offset, 140 void (*test_func)(char *, char *)) 141 { 142 char *s, *d; 143 144 s = src; 145 s += offset; 146 d = dst; 147 d += offset; 148 149 assert(size == 16); 150 gotsig = 0; 151 testing = 1; 152 153 test_func(s, d); /* run the actual test */ 154 155 testing = 0; 156 if (gotsig) { 157 if (debug) 158 printf(" Got signal %i\n", gotsig); 159 return 1; 160 } 161 return 0; 162 } 163 164 void dumpdata(char *s1, char *s2, int n, char *test_name) 165 { 166 int i; 167 168 printf(" %s: unexpected result:\n", test_name); 169 printf(" mem:"); 170 for (i = 0; i < n; i++) 171 printf(" %02x", s1[i]); 172 printf("\n"); 173 printf(" ci: "); 174 for (i = 0; i < n; i++) 175 printf(" %02x", s2[i]); 176 printf("\n"); 177 } 178 179 int test_memcmp(void *s1, void *s2, int n, int offset, char *test_name) 180 { 181 char *s1c, *s2c; 182 183 s1c = s1; 184 s1c += offset; 185 s2c = s2; 186 s2c += offset; 187 188 if (memcmp(s1c, s2c, n)) { 189 if (debug) { 190 printf("\n Compare failed. Offset:%i length:%i\n", 191 offset, n); 192 dumpdata(s1c, s2c, n, test_name); 193 } 194 return 1; 195 } 196 return 0; 197 } 198 199 /* 200 * Do two memcpy tests using the same instructions. One cachable 201 * memory and the other doesn't. 202 */ 203 int do_test(char *test_name, void (*test_func)(char *, char *)) 204 { 205 int offset, width, fd, rc, r; 206 void *mem0, *mem1, *ci0, *ci1; 207 208 printf("\tDoing %s:\t", test_name); 209 210 fd = open(cipath, O_RDWR); 211 if (fd < 0) { 212 printf("\n"); 213 perror("Can't open ci file now?"); 214 return 1; 215 } 216 217 ci0 = mmap(NULL, bufsize, PROT_WRITE | PROT_READ, MAP_SHARED, 218 fd, cioffset); 219 ci1 = mmap(NULL, bufsize, PROT_WRITE | PROT_READ, MAP_SHARED, 220 fd, cioffset + bufsize); 221 222 if ((ci0 == MAP_FAILED) || (ci1 == MAP_FAILED)) { 223 printf("\n"); 224 perror("mmap failed"); 225 SKIP_IF(1); 226 } 227 228 rc = posix_memalign(&mem0, bufsize, bufsize); 229 if (rc) { 230 printf("\n"); 231 return rc; 232 } 233 234 rc = posix_memalign(&mem1, bufsize, bufsize); 235 if (rc) { 236 printf("\n"); 237 free(mem0); 238 return rc; 239 } 240 241 rc = 0; 242 /* offset = 0 no alignment fault, so skip */ 243 for (offset = 1; offset < 16; offset++) { 244 width = 16; /* vsx == 16 bytes */ 245 r = 0; 246 247 /* load pattern into memory byte by byte */ 248 preload_data(ci0, offset, width); 249 preload_data(mem0, offset, width); // FIXME: remove?? 250 memcpy(ci0, mem0, bufsize); 251 memcpy(ci1, mem1, bufsize); /* initialise output to the same */ 252 253 /* sanity check */ 254 test_memcmp(mem0, ci0, width, offset, test_name); 255 256 r |= test_memcpy(ci1, ci0, width, offset, test_func); 257 r |= test_memcpy(mem1, mem0, width, offset, test_func); 258 if (r && !debug) { 259 printf("FAILED: Got signal"); 260 rc = 1; 261 break; 262 } 263 264 r |= test_memcmp(mem1, ci1, width, offset, test_name); 265 if (r && !debug) { 266 printf("FAILED: Wrong Data"); 267 rc = 1; 268 break; 269 } 270 } 271 272 if (rc == 0) 273 printf("PASSED"); 274 275 printf("\n"); 276 277 munmap(ci0, bufsize); 278 munmap(ci1, bufsize); 279 free(mem0); 280 free(mem1); 281 close(fd); 282 283 return rc; 284 } 285 286 static bool can_open_cifile(void) 287 { 288 int fd; 289 290 fd = open(cipath, O_RDWR); 291 if (fd < 0) 292 return false; 293 294 close(fd); 295 return true; 296 } 297 298 int test_alignment_handler_vsx_206(void) 299 { 300 int rc = 0; 301 302 SKIP_IF(!can_open_cifile()); 303 SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06)); 304 305 printf("VSX: 2.06B\n"); 306 LOAD_VSX_XFORM_TEST(lxvd2x); 307 LOAD_VSX_XFORM_TEST(lxvw4x); 308 LOAD_VSX_XFORM_TEST(lxsdx); 309 LOAD_VSX_XFORM_TEST(lxvdsx); 310 STORE_VSX_XFORM_TEST(stxvd2x); 311 STORE_VSX_XFORM_TEST(stxvw4x); 312 STORE_VSX_XFORM_TEST(stxsdx); 313 return rc; 314 } 315 316 int test_alignment_handler_vsx_207(void) 317 { 318 int rc = 0; 319 320 SKIP_IF(!can_open_cifile()); 321 SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07)); 322 323 printf("VSX: 2.07B\n"); 324 LOAD_VSX_XFORM_TEST(lxsspx); 325 LOAD_VSX_XFORM_TEST(lxsiwax); 326 LOAD_VSX_XFORM_TEST(lxsiwzx); 327 STORE_VSX_XFORM_TEST(stxsspx); 328 STORE_VSX_XFORM_TEST(stxsiwx); 329 return rc; 330 } 331 332 int test_alignment_handler_vsx_300(void) 333 { 334 int rc = 0; 335 336 SKIP_IF(!can_open_cifile()); 337 338 SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00)); 339 printf("VSX: 3.00B\n"); 340 LOAD_VMX_DFORM_TEST(lxsd); 341 LOAD_VSX_XFORM_TEST(lxsibzx); 342 LOAD_VSX_XFORM_TEST(lxsihzx); 343 LOAD_VMX_DFORM_TEST(lxssp); 344 LOAD_VSX_DFORM_TEST(lxv); 345 LOAD_VSX_XFORM_TEST(lxvb16x); 346 LOAD_VSX_XFORM_TEST(lxvh8x); 347 LOAD_VSX_XFORM_TEST(lxvx); 348 LOAD_VSX_XFORM_TEST(lxvwsx); 349 LOAD_VSX_XFORM_TEST(lxvl); 350 LOAD_VSX_XFORM_TEST(lxvll); 351 STORE_VMX_DFORM_TEST(stxsd); 352 STORE_VSX_XFORM_TEST(stxsibx); 353 STORE_VSX_XFORM_TEST(stxsihx); 354 STORE_VMX_DFORM_TEST(stxssp); 355 STORE_VSX_DFORM_TEST(stxv); 356 STORE_VSX_XFORM_TEST(stxvb16x); 357 STORE_VSX_XFORM_TEST(stxvh8x); 358 STORE_VSX_XFORM_TEST(stxvx); 359 STORE_VSX_XFORM_TEST(stxvl); 360 STORE_VSX_XFORM_TEST(stxvll); 361 return rc; 362 } 363 364 int test_alignment_handler_integer(void) 365 { 366 int rc = 0; 367 368 SKIP_IF(!can_open_cifile()); 369 370 printf("Integer\n"); 371 LOAD_DFORM_TEST(lbz); 372 LOAD_DFORM_TEST(lbzu); 373 LOAD_XFORM_TEST(lbzx); 374 LOAD_XFORM_TEST(lbzux); 375 LOAD_DFORM_TEST(lhz); 376 LOAD_DFORM_TEST(lhzu); 377 LOAD_XFORM_TEST(lhzx); 378 LOAD_XFORM_TEST(lhzux); 379 LOAD_DFORM_TEST(lha); 380 LOAD_DFORM_TEST(lhau); 381 LOAD_XFORM_TEST(lhax); 382 LOAD_XFORM_TEST(lhaux); 383 LOAD_XFORM_TEST(lhbrx); 384 LOAD_DFORM_TEST(lwz); 385 LOAD_DFORM_TEST(lwzu); 386 LOAD_XFORM_TEST(lwzx); 387 LOAD_XFORM_TEST(lwzux); 388 LOAD_DFORM_TEST(lwa); 389 LOAD_XFORM_TEST(lwax); 390 LOAD_XFORM_TEST(lwaux); 391 LOAD_XFORM_TEST(lwbrx); 392 LOAD_DFORM_TEST(ld); 393 LOAD_DFORM_TEST(ldu); 394 LOAD_XFORM_TEST(ldx); 395 LOAD_XFORM_TEST(ldux); 396 LOAD_DFORM_TEST(lmw); 397 STORE_DFORM_TEST(stb); 398 STORE_XFORM_TEST(stbx); 399 STORE_DFORM_TEST(stbu); 400 STORE_XFORM_TEST(stbux); 401 STORE_DFORM_TEST(sth); 402 STORE_XFORM_TEST(sthx); 403 STORE_DFORM_TEST(sthu); 404 STORE_XFORM_TEST(sthux); 405 STORE_XFORM_TEST(sthbrx); 406 STORE_DFORM_TEST(stw); 407 STORE_XFORM_TEST(stwx); 408 STORE_DFORM_TEST(stwu); 409 STORE_XFORM_TEST(stwux); 410 STORE_XFORM_TEST(stwbrx); 411 STORE_DFORM_TEST(std); 412 STORE_XFORM_TEST(stdx); 413 STORE_DFORM_TEST(stdu); 414 STORE_XFORM_TEST(stdux); 415 STORE_DFORM_TEST(stmw); 416 417 return rc; 418 } 419 420 int test_alignment_handler_integer_206(void) 421 { 422 int rc = 0; 423 424 SKIP_IF(!can_open_cifile()); 425 SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06)); 426 427 printf("Integer: 2.06\n"); 428 429 LOAD_XFORM_TEST(ldbrx); 430 STORE_XFORM_TEST(stdbrx); 431 432 return rc; 433 } 434 435 int test_alignment_handler_vmx(void) 436 { 437 int rc = 0; 438 439 SKIP_IF(!can_open_cifile()); 440 SKIP_IF(!have_hwcap(PPC_FEATURE_HAS_ALTIVEC)); 441 442 printf("VMX\n"); 443 LOAD_VMX_XFORM_TEST(lvx); 444 445 /* 446 * FIXME: These loads only load part of the register, so our 447 * testing method doesn't work. Also they don't take alignment 448 * faults, so it's kinda pointless anyway 449 * 450 LOAD_VMX_XFORM_TEST(lvebx) 451 LOAD_VMX_XFORM_TEST(lvehx) 452 LOAD_VMX_XFORM_TEST(lvewx) 453 LOAD_VMX_XFORM_TEST(lvxl) 454 */ 455 STORE_VMX_XFORM_TEST(stvx); 456 STORE_VMX_XFORM_TEST(stvebx); 457 STORE_VMX_XFORM_TEST(stvehx); 458 STORE_VMX_XFORM_TEST(stvewx); 459 STORE_VMX_XFORM_TEST(stvxl); 460 return rc; 461 } 462 463 int test_alignment_handler_fp(void) 464 { 465 int rc = 0; 466 467 SKIP_IF(!can_open_cifile()); 468 469 printf("Floating point\n"); 470 LOAD_FLOAT_DFORM_TEST(lfd); 471 LOAD_FLOAT_XFORM_TEST(lfdx); 472 LOAD_FLOAT_DFORM_TEST(lfdu); 473 LOAD_FLOAT_XFORM_TEST(lfdux); 474 LOAD_FLOAT_DFORM_TEST(lfs); 475 LOAD_FLOAT_XFORM_TEST(lfsx); 476 LOAD_FLOAT_DFORM_TEST(lfsu); 477 LOAD_FLOAT_XFORM_TEST(lfsux); 478 STORE_FLOAT_DFORM_TEST(stfd); 479 STORE_FLOAT_XFORM_TEST(stfdx); 480 STORE_FLOAT_DFORM_TEST(stfdu); 481 STORE_FLOAT_XFORM_TEST(stfdux); 482 STORE_FLOAT_DFORM_TEST(stfs); 483 STORE_FLOAT_XFORM_TEST(stfsx); 484 STORE_FLOAT_DFORM_TEST(stfsu); 485 STORE_FLOAT_XFORM_TEST(stfsux); 486 STORE_FLOAT_XFORM_TEST(stfiwx); 487 488 return rc; 489 } 490 491 int test_alignment_handler_fp_205(void) 492 { 493 int rc = 0; 494 495 SKIP_IF(!can_open_cifile()); 496 SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_05)); 497 498 printf("Floating point: 2.05\n"); 499 500 LOAD_FLOAT_DFORM_TEST(lfdp); 501 LOAD_FLOAT_XFORM_TEST(lfdpx); 502 LOAD_FLOAT_XFORM_TEST(lfiwax); 503 STORE_FLOAT_DFORM_TEST(stfdp); 504 STORE_FLOAT_XFORM_TEST(stfdpx); 505 506 return rc; 507 } 508 509 int test_alignment_handler_fp_206(void) 510 { 511 int rc = 0; 512 513 SKIP_IF(!can_open_cifile()); 514 SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06)); 515 516 printf("Floating point: 2.06\n"); 517 518 LOAD_FLOAT_XFORM_TEST(lfiwzx); 519 520 return rc; 521 } 522 523 void usage(char *prog) 524 { 525 printf("Usage: %s [options] [path [offset]]\n", prog); 526 printf(" -d Enable debug error output\n"); 527 printf("\n"); 528 printf("This test requires a POWER8 or POWER9 CPU and either a "); 529 printf("usable framebuffer at /dev/fb0 or the path to usable "); 530 printf("cache-inhibited memory and optional offset to be provided\n"); 531 } 532 533 int main(int argc, char *argv[]) 534 { 535 536 struct sigaction sa; 537 int rc = 0; 538 int option = 0; 539 540 while ((option = getopt(argc, argv, "d")) != -1) { 541 switch (option) { 542 case 'd': 543 debug++; 544 break; 545 default: 546 usage(argv[0]); 547 exit(1); 548 } 549 } 550 argc -= optind; 551 argv += optind; 552 553 if (argc > 0) 554 cipath = argv[0]; 555 if (argc > 1) 556 cioffset = strtol(argv[1], 0, 0x10); 557 558 bufsize = getpagesize(); 559 560 sa.sa_sigaction = sighandler; 561 sigemptyset(&sa.sa_mask); 562 sa.sa_flags = SA_SIGINFO; 563 if (sigaction(SIGSEGV, &sa, NULL) == -1 564 || sigaction(SIGBUS, &sa, NULL) == -1 565 || sigaction(SIGILL, &sa, NULL) == -1) { 566 perror("sigaction"); 567 exit(1); 568 } 569 570 rc |= test_harness(test_alignment_handler_vsx_206, 571 "test_alignment_handler_vsx_206"); 572 rc |= test_harness(test_alignment_handler_vsx_207, 573 "test_alignment_handler_vsx_207"); 574 rc |= test_harness(test_alignment_handler_vsx_300, 575 "test_alignment_handler_vsx_300"); 576 rc |= test_harness(test_alignment_handler_integer, 577 "test_alignment_handler_integer"); 578 rc |= test_harness(test_alignment_handler_integer_206, 579 "test_alignment_handler_integer_206"); 580 rc |= test_harness(test_alignment_handler_vmx, 581 "test_alignment_handler_vmx"); 582 rc |= test_harness(test_alignment_handler_fp, 583 "test_alignment_handler_fp"); 584 rc |= test_harness(test_alignment_handler_fp_205, 585 "test_alignment_handler_fp_205"); 586 rc |= test_harness(test_alignment_handler_fp_206, 587 "test_alignment_handler_fp_206"); 588 return rc; 589 } 590