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