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