linux/tools/testing/selftests/rseq/rseq-mips.h
<<
>>
Prefs
   1/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
   2/*
   3 * Author: Paul Burton <paul.burton@mips.com>
   4 * (C) Copyright 2018 MIPS Tech LLC
   5 *
   6 * Based on rseq-arm.h:
   7 * (C) Copyright 2016-2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
   8 */
   9
  10/*
  11 * RSEQ_SIG uses the break instruction. The instruction pattern is:
  12 *
  13 * On MIPS:
  14 *      0350000d        break     0x350
  15 *
  16 * On nanoMIPS:
  17 *      00100350        break     0x350
  18 *
  19 * On microMIPS:
  20 *      0000d407        break     0x350
  21 *
  22 * For nanoMIPS32 and microMIPS, the instruction stream is encoded as 16-bit
  23 * halfwords, so the signature halfwords need to be swapped accordingly for
  24 * little-endian.
  25 */
  26#if defined(__nanomips__)
  27# ifdef __MIPSEL__
  28#  define RSEQ_SIG      0x03500010
  29# else
  30#  define RSEQ_SIG      0x00100350
  31# endif
  32#elif defined(__mips_micromips)
  33# ifdef __MIPSEL__
  34#  define RSEQ_SIG      0xd4070000
  35# else
  36#  define RSEQ_SIG      0x0000d407
  37# endif
  38#elif defined(__mips__)
  39# define RSEQ_SIG       0x0350000d
  40#else
  41/* Unknown MIPS architecture. */
  42#endif
  43
  44#define rseq_smp_mb()   __asm__ __volatile__ ("sync" ::: "memory")
  45#define rseq_smp_rmb()  rseq_smp_mb()
  46#define rseq_smp_wmb()  rseq_smp_mb()
  47
  48#define rseq_smp_load_acquire(p)                                        \
  49__extension__ ({                                                        \
  50        __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p);                       \
  51        rseq_smp_mb();                                                  \
  52        ____p1;                                                         \
  53})
  54
  55#define rseq_smp_acquire__after_ctrl_dep()      rseq_smp_rmb()
  56
  57#define rseq_smp_store_release(p, v)                                    \
  58do {                                                                    \
  59        rseq_smp_mb();                                                  \
  60        RSEQ_WRITE_ONCE(*p, v);                                         \
  61} while (0)
  62
  63#ifdef RSEQ_SKIP_FASTPATH
  64#include "rseq-skip.h"
  65#else /* !RSEQ_SKIP_FASTPATH */
  66
  67#if _MIPS_SZLONG == 64
  68# define LONG                   ".dword"
  69# define LONG_LA                "dla"
  70# define LONG_L                 "ld"
  71# define LONG_S                 "sd"
  72# define LONG_ADDI              "daddiu"
  73# define U32_U64_PAD(x)         x
  74#elif _MIPS_SZLONG == 32
  75# define LONG                   ".word"
  76# define LONG_LA                "la"
  77# define LONG_L                 "lw"
  78# define LONG_S                 "sw"
  79# define LONG_ADDI              "addiu"
  80# ifdef __BIG_ENDIAN
  81#  define U32_U64_PAD(x)        "0x0, " x
  82# else
  83#  define U32_U64_PAD(x)        x ", 0x0"
  84# endif
  85#else
  86# error unsupported _MIPS_SZLONG
  87#endif
  88
  89#define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, start_ip, \
  90                                post_commit_offset, abort_ip) \
  91                ".pushsection __rseq_cs, \"aw\"\n\t" \
  92                ".balign 32\n\t" \
  93                __rseq_str(label) ":\n\t"                                       \
  94                ".word " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
  95                LONG " " U32_U64_PAD(__rseq_str(start_ip)) "\n\t" \
  96                LONG " " U32_U64_PAD(__rseq_str(post_commit_offset)) "\n\t" \
  97                LONG " " U32_U64_PAD(__rseq_str(abort_ip)) "\n\t" \
  98                ".popsection\n\t" \
  99                ".pushsection __rseq_cs_ptr_array, \"aw\"\n\t" \
 100                LONG " " U32_U64_PAD(__rseq_str(label) "b") "\n\t" \
 101                ".popsection\n\t"
 102
 103#define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \
 104        __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \
 105                                (post_commit_ip - start_ip), abort_ip)
 106
 107/*
 108 * Exit points of a rseq critical section consist of all instructions outside
 109 * of the critical section where a critical section can either branch to or
 110 * reach through the normal course of its execution. The abort IP and the
 111 * post-commit IP are already part of the __rseq_cs section and should not be
 112 * explicitly defined as additional exit points. Knowing all exit points is
 113 * useful to assist debuggers stepping over the critical section.
 114 */
 115#define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \
 116                ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \
 117                LONG " " U32_U64_PAD(__rseq_str(start_ip)) "\n\t" \
 118                LONG " " U32_U64_PAD(__rseq_str(exit_ip)) "\n\t" \
 119                ".popsection\n\t"
 120
 121#define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
 122                RSEQ_INJECT_ASM(1) \
 123                LONG_LA " $4, " __rseq_str(cs_label) "\n\t" \
 124                LONG_S  " $4, %[" __rseq_str(rseq_cs) "]\n\t" \
 125                __rseq_str(label) ":\n\t"
 126
 127#define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \
 128                RSEQ_INJECT_ASM(2) \
 129                "lw  $4, %[" __rseq_str(current_cpu_id) "]\n\t" \
 130                "bne $4, %[" __rseq_str(cpu_id) "], " __rseq_str(label) "\n\t"
 131
 132#define __RSEQ_ASM_DEFINE_ABORT(table_label, label, teardown, \
 133                                abort_label, version, flags, \
 134                                start_ip, post_commit_offset, abort_ip) \
 135                ".balign 32\n\t" \
 136                __rseq_str(table_label) ":\n\t" \
 137                ".word " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
 138                LONG " " U32_U64_PAD(__rseq_str(start_ip)) "\n\t" \
 139                LONG " " U32_U64_PAD(__rseq_str(post_commit_offset)) "\n\t" \
 140                LONG " " U32_U64_PAD(__rseq_str(abort_ip)) "\n\t" \
 141                ".word " __rseq_str(RSEQ_SIG) "\n\t" \
 142                __rseq_str(label) ":\n\t" \
 143                teardown \
 144                "b %l[" __rseq_str(abort_label) "]\n\t"
 145
 146#define RSEQ_ASM_DEFINE_ABORT(table_label, label, teardown, abort_label, \
 147                              start_ip, post_commit_ip, abort_ip) \
 148        __RSEQ_ASM_DEFINE_ABORT(table_label, label, teardown, \
 149                                abort_label, 0x0, 0x0, start_ip, \
 150                                (post_commit_ip - start_ip), abort_ip)
 151
 152#define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \
 153                __rseq_str(label) ":\n\t" \
 154                teardown \
 155                "b %l[" __rseq_str(cmpfail_label) "]\n\t"
 156
 157#define rseq_workaround_gcc_asm_size_guess()    __asm__ __volatile__("")
 158
 159static inline __attribute__((always_inline))
 160int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
 161{
 162        RSEQ_INJECT_C(9)
 163
 164        rseq_workaround_gcc_asm_size_guess();
 165        __asm__ __volatile__ goto (
 166                RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
 167                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
 168#ifdef RSEQ_COMPARE_TWICE
 169                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
 170                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
 171#endif
 172                /* Start rseq by storing table entry pointer into rseq_cs. */
 173                RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
 174                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
 175                RSEQ_INJECT_ASM(3)
 176                LONG_L " $4, %[v]\n\t"
 177                "bne $4, %[expect], %l[cmpfail]\n\t"
 178                RSEQ_INJECT_ASM(4)
 179#ifdef RSEQ_COMPARE_TWICE
 180                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
 181                LONG_L " $4, %[v]\n\t"
 182                "bne $4, %[expect], %l[error2]\n\t"
 183#endif
 184                /* final store */
 185                LONG_S " %[newv], %[v]\n\t"
 186                "2:\n\t"
 187                RSEQ_INJECT_ASM(5)
 188                "b 5f\n\t"
 189                RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
 190                "5:\n\t"
 191                : /* gcc asm goto does not allow outputs */
 192                : [cpu_id]              "r" (cpu),
 193                  [current_cpu_id]      "m" (__rseq_abi.cpu_id),
 194                  [rseq_cs]             "m" (__rseq_abi.rseq_cs),
 195                  [v]                   "m" (*v),
 196                  [expect]              "r" (expect),
 197                  [newv]                "r" (newv)
 198                  RSEQ_INJECT_INPUT
 199                : "$4", "memory"
 200                  RSEQ_INJECT_CLOBBER
 201                : abort, cmpfail
 202#ifdef RSEQ_COMPARE_TWICE
 203                  , error1, error2
 204#endif
 205        );
 206        rseq_workaround_gcc_asm_size_guess();
 207        return 0;
 208abort:
 209        rseq_workaround_gcc_asm_size_guess();
 210        RSEQ_INJECT_FAILED
 211        return -1;
 212cmpfail:
 213        rseq_workaround_gcc_asm_size_guess();
 214        return 1;
 215#ifdef RSEQ_COMPARE_TWICE
 216error1:
 217        rseq_bug("cpu_id comparison failed");
 218error2:
 219        rseq_bug("expected value comparison failed");
 220#endif
 221}
 222
 223static inline __attribute__((always_inline))
 224int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot,
 225                               off_t voffp, intptr_t *load, int cpu)
 226{
 227        RSEQ_INJECT_C(9)
 228
 229        rseq_workaround_gcc_asm_size_guess();
 230        __asm__ __volatile__ goto (
 231                RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
 232                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
 233#ifdef RSEQ_COMPARE_TWICE
 234                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
 235                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
 236#endif
 237                /* Start rseq by storing table entry pointer into rseq_cs. */
 238                RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
 239                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
 240                RSEQ_INJECT_ASM(3)
 241                LONG_L " $4, %[v]\n\t"
 242                "beq $4, %[expectnot], %l[cmpfail]\n\t"
 243                RSEQ_INJECT_ASM(4)
 244#ifdef RSEQ_COMPARE_TWICE
 245                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
 246                LONG_L " $4, %[v]\n\t"
 247                "beq $4, %[expectnot], %l[error2]\n\t"
 248#endif
 249                LONG_S " $4, %[load]\n\t"
 250                LONG_ADDI " $4, %[voffp]\n\t"
 251                LONG_L " $4, 0($4)\n\t"
 252                /* final store */
 253                LONG_S " $4, %[v]\n\t"
 254                "2:\n\t"
 255                RSEQ_INJECT_ASM(5)
 256                "b 5f\n\t"
 257                RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
 258                "5:\n\t"
 259                : /* gcc asm goto does not allow outputs */
 260                : [cpu_id]              "r" (cpu),
 261                  [current_cpu_id]      "m" (__rseq_abi.cpu_id),
 262                  [rseq_cs]             "m" (__rseq_abi.rseq_cs),
 263                  /* final store input */
 264                  [v]                   "m" (*v),
 265                  [expectnot]           "r" (expectnot),
 266                  [voffp]               "Ir" (voffp),
 267                  [load]                "m" (*load)
 268                  RSEQ_INJECT_INPUT
 269                : "$4", "memory"
 270                  RSEQ_INJECT_CLOBBER
 271                : abort, cmpfail
 272#ifdef RSEQ_COMPARE_TWICE
 273                  , error1, error2
 274#endif
 275        );
 276        rseq_workaround_gcc_asm_size_guess();
 277        return 0;
 278abort:
 279        rseq_workaround_gcc_asm_size_guess();
 280        RSEQ_INJECT_FAILED
 281        return -1;
 282cmpfail:
 283        rseq_workaround_gcc_asm_size_guess();
 284        return 1;
 285#ifdef RSEQ_COMPARE_TWICE
 286error1:
 287        rseq_bug("cpu_id comparison failed");
 288error2:
 289        rseq_bug("expected value comparison failed");
 290#endif
 291}
 292
 293static inline __attribute__((always_inline))
 294int rseq_addv(intptr_t *v, intptr_t count, int cpu)
 295{
 296        RSEQ_INJECT_C(9)
 297
 298        rseq_workaround_gcc_asm_size_guess();
 299        __asm__ __volatile__ goto (
 300                RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
 301#ifdef RSEQ_COMPARE_TWICE
 302                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
 303#endif
 304                /* Start rseq by storing table entry pointer into rseq_cs. */
 305                RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
 306                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
 307                RSEQ_INJECT_ASM(3)
 308#ifdef RSEQ_COMPARE_TWICE
 309                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
 310#endif
 311                LONG_L " $4, %[v]\n\t"
 312                LONG_ADDI " $4, %[count]\n\t"
 313                /* final store */
 314                LONG_S " $4, %[v]\n\t"
 315                "2:\n\t"
 316                RSEQ_INJECT_ASM(4)
 317                "b 5f\n\t"
 318                RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
 319                "5:\n\t"
 320                : /* gcc asm goto does not allow outputs */
 321                : [cpu_id]              "r" (cpu),
 322                  [current_cpu_id]      "m" (__rseq_abi.cpu_id),
 323                  [rseq_cs]             "m" (__rseq_abi.rseq_cs),
 324                  [v]                   "m" (*v),
 325                  [count]               "Ir" (count)
 326                  RSEQ_INJECT_INPUT
 327                : "$4", "memory"
 328                  RSEQ_INJECT_CLOBBER
 329                : abort
 330#ifdef RSEQ_COMPARE_TWICE
 331                  , error1
 332#endif
 333        );
 334        rseq_workaround_gcc_asm_size_guess();
 335        return 0;
 336abort:
 337        rseq_workaround_gcc_asm_size_guess();
 338        RSEQ_INJECT_FAILED
 339        return -1;
 340#ifdef RSEQ_COMPARE_TWICE
 341error1:
 342        rseq_bug("cpu_id comparison failed");
 343#endif
 344}
 345
 346static inline __attribute__((always_inline))
 347int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect,
 348                                 intptr_t *v2, intptr_t newv2,
 349                                 intptr_t newv, int cpu)
 350{
 351        RSEQ_INJECT_C(9)
 352
 353        rseq_workaround_gcc_asm_size_guess();
 354        __asm__ __volatile__ goto (
 355                RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
 356                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
 357#ifdef RSEQ_COMPARE_TWICE
 358                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
 359                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
 360#endif
 361                /* Start rseq by storing table entry pointer into rseq_cs. */
 362                RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
 363                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
 364                RSEQ_INJECT_ASM(3)
 365                LONG_L " $4, %[v]\n\t"
 366                "bne $4, %[expect], %l[cmpfail]\n\t"
 367                RSEQ_INJECT_ASM(4)
 368#ifdef RSEQ_COMPARE_TWICE
 369                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
 370                LONG_L " $4, %[v]\n\t"
 371                "bne $4, %[expect], %l[error2]\n\t"
 372#endif
 373                /* try store */
 374                LONG_S " %[newv2], %[v2]\n\t"
 375                RSEQ_INJECT_ASM(5)
 376                /* final store */
 377                LONG_S " %[newv], %[v]\n\t"
 378                "2:\n\t"
 379                RSEQ_INJECT_ASM(6)
 380                "b 5f\n\t"
 381                RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
 382                "5:\n\t"
 383                : /* gcc asm goto does not allow outputs */
 384                : [cpu_id]              "r" (cpu),
 385                  [current_cpu_id]      "m" (__rseq_abi.cpu_id),
 386                  [rseq_cs]             "m" (__rseq_abi.rseq_cs),
 387                  /* try store input */
 388                  [v2]                  "m" (*v2),
 389                  [newv2]               "r" (newv2),
 390                  /* final store input */
 391                  [v]                   "m" (*v),
 392                  [expect]              "r" (expect),
 393                  [newv]                "r" (newv)
 394                  RSEQ_INJECT_INPUT
 395                : "$4", "memory"
 396                  RSEQ_INJECT_CLOBBER
 397                : abort, cmpfail
 398#ifdef RSEQ_COMPARE_TWICE
 399                  , error1, error2
 400#endif
 401        );
 402        rseq_workaround_gcc_asm_size_guess();
 403        return 0;
 404abort:
 405        rseq_workaround_gcc_asm_size_guess();
 406        RSEQ_INJECT_FAILED
 407        return -1;
 408cmpfail:
 409        rseq_workaround_gcc_asm_size_guess();
 410        return 1;
 411#ifdef RSEQ_COMPARE_TWICE
 412error1:
 413        rseq_bug("cpu_id comparison failed");
 414error2:
 415        rseq_bug("expected value comparison failed");
 416#endif
 417}
 418
 419static inline __attribute__((always_inline))
 420int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect,
 421                                         intptr_t *v2, intptr_t newv2,
 422                                         intptr_t newv, int cpu)
 423{
 424        RSEQ_INJECT_C(9)
 425
 426        rseq_workaround_gcc_asm_size_guess();
 427        __asm__ __volatile__ goto (
 428                RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
 429                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
 430#ifdef RSEQ_COMPARE_TWICE
 431                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
 432                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
 433#endif
 434                /* Start rseq by storing table entry pointer into rseq_cs. */
 435                RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
 436                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
 437                RSEQ_INJECT_ASM(3)
 438                LONG_L " $4, %[v]\n\t"
 439                "bne $4, %[expect], %l[cmpfail]\n\t"
 440                RSEQ_INJECT_ASM(4)
 441#ifdef RSEQ_COMPARE_TWICE
 442                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
 443                LONG_L " $4, %[v]\n\t"
 444                "bne $4, %[expect], %l[error2]\n\t"
 445#endif
 446                /* try store */
 447                LONG_S " %[newv2], %[v2]\n\t"
 448                RSEQ_INJECT_ASM(5)
 449                "sync\n\t"      /* full sync provides store-release */
 450                /* final store */
 451                LONG_S " %[newv], %[v]\n\t"
 452                "2:\n\t"
 453                RSEQ_INJECT_ASM(6)
 454                "b 5f\n\t"
 455                RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
 456                "5:\n\t"
 457                : /* gcc asm goto does not allow outputs */
 458                : [cpu_id]              "r" (cpu),
 459                  [current_cpu_id]      "m" (__rseq_abi.cpu_id),
 460                  [rseq_cs]             "m" (__rseq_abi.rseq_cs),
 461                  /* try store input */
 462                  [v2]                  "m" (*v2),
 463                  [newv2]               "r" (newv2),
 464                  /* final store input */
 465                  [v]                   "m" (*v),
 466                  [expect]              "r" (expect),
 467                  [newv]                "r" (newv)
 468                  RSEQ_INJECT_INPUT
 469                : "$4", "memory"
 470                  RSEQ_INJECT_CLOBBER
 471                : abort, cmpfail
 472#ifdef RSEQ_COMPARE_TWICE
 473                  , error1, error2
 474#endif
 475        );
 476        rseq_workaround_gcc_asm_size_guess();
 477        return 0;
 478abort:
 479        rseq_workaround_gcc_asm_size_guess();
 480        RSEQ_INJECT_FAILED
 481        return -1;
 482cmpfail:
 483        rseq_workaround_gcc_asm_size_guess();
 484        return 1;
 485#ifdef RSEQ_COMPARE_TWICE
 486error1:
 487        rseq_bug("cpu_id comparison failed");
 488error2:
 489        rseq_bug("expected value comparison failed");
 490#endif
 491}
 492
 493static inline __attribute__((always_inline))
 494int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect,
 495                              intptr_t *v2, intptr_t expect2,
 496                              intptr_t newv, int cpu)
 497{
 498        RSEQ_INJECT_C(9)
 499
 500        rseq_workaround_gcc_asm_size_guess();
 501        __asm__ __volatile__ goto (
 502                RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
 503                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
 504#ifdef RSEQ_COMPARE_TWICE
 505                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
 506                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
 507                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3])
 508#endif
 509                /* Start rseq by storing table entry pointer into rseq_cs. */
 510                RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
 511                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
 512                RSEQ_INJECT_ASM(3)
 513                LONG_L " $4, %[v]\n\t"
 514                "bne $4, %[expect], %l[cmpfail]\n\t"
 515                RSEQ_INJECT_ASM(4)
 516                LONG_L " $4, %[v2]\n\t"
 517                "bne $4, %[expect2], %l[cmpfail]\n\t"
 518                RSEQ_INJECT_ASM(5)
 519#ifdef RSEQ_COMPARE_TWICE
 520                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
 521                LONG_L " $4, %[v]\n\t"
 522                "bne $4, %[expect], %l[error2]\n\t"
 523                LONG_L " $4, %[v2]\n\t"
 524                "bne $4, %[expect2], %l[error3]\n\t"
 525#endif
 526                /* final store */
 527                LONG_S " %[newv], %[v]\n\t"
 528                "2:\n\t"
 529                RSEQ_INJECT_ASM(6)
 530                "b 5f\n\t"
 531                RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
 532                "5:\n\t"
 533                : /* gcc asm goto does not allow outputs */
 534                : [cpu_id]              "r" (cpu),
 535                  [current_cpu_id]      "m" (__rseq_abi.cpu_id),
 536                  [rseq_cs]             "m" (__rseq_abi.rseq_cs),
 537                  /* cmp2 input */
 538                  [v2]                  "m" (*v2),
 539                  [expect2]             "r" (expect2),
 540                  /* final store input */
 541                  [v]                   "m" (*v),
 542                  [expect]              "r" (expect),
 543                  [newv]                "r" (newv)
 544                  RSEQ_INJECT_INPUT
 545                : "$4", "memory"
 546                  RSEQ_INJECT_CLOBBER
 547                : abort, cmpfail
 548#ifdef RSEQ_COMPARE_TWICE
 549                  , error1, error2, error3
 550#endif
 551        );
 552        rseq_workaround_gcc_asm_size_guess();
 553        return 0;
 554abort:
 555        rseq_workaround_gcc_asm_size_guess();
 556        RSEQ_INJECT_FAILED
 557        return -1;
 558cmpfail:
 559        rseq_workaround_gcc_asm_size_guess();
 560        return 1;
 561#ifdef RSEQ_COMPARE_TWICE
 562error1:
 563        rseq_bug("cpu_id comparison failed");
 564error2:
 565        rseq_bug("1st expected value comparison failed");
 566error3:
 567        rseq_bug("2nd expected value comparison failed");
 568#endif
 569}
 570
 571static inline __attribute__((always_inline))
 572int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect,
 573                                 void *dst, void *src, size_t len,
 574                                 intptr_t newv, int cpu)
 575{
 576        uintptr_t rseq_scratch[3];
 577
 578        RSEQ_INJECT_C(9)
 579
 580        rseq_workaround_gcc_asm_size_guess();
 581        __asm__ __volatile__ goto (
 582                RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
 583                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
 584#ifdef RSEQ_COMPARE_TWICE
 585                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
 586                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
 587#endif
 588                LONG_S " %[src], %[rseq_scratch0]\n\t"
 589                LONG_S "  %[dst], %[rseq_scratch1]\n\t"
 590                LONG_S " %[len], %[rseq_scratch2]\n\t"
 591                /* Start rseq by storing table entry pointer into rseq_cs. */
 592                RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
 593                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
 594                RSEQ_INJECT_ASM(3)
 595                LONG_L " $4, %[v]\n\t"
 596                "bne $4, %[expect], 5f\n\t"
 597                RSEQ_INJECT_ASM(4)
 598#ifdef RSEQ_COMPARE_TWICE
 599                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
 600                LONG_L " $4, %[v]\n\t"
 601                "bne $4, %[expect], 7f\n\t"
 602#endif
 603                /* try memcpy */
 604                "beqz %[len], 333f\n\t" \
 605                "222:\n\t" \
 606                "lb   $4, 0(%[src])\n\t" \
 607                "sb   $4, 0(%[dst])\n\t" \
 608                LONG_ADDI " %[src], 1\n\t" \
 609                LONG_ADDI " %[dst], 1\n\t" \
 610                LONG_ADDI " %[len], -1\n\t" \
 611                "bnez %[len], 222b\n\t" \
 612                "333:\n\t" \
 613                RSEQ_INJECT_ASM(5)
 614                /* final store */
 615                LONG_S " %[newv], %[v]\n\t"
 616                "2:\n\t"
 617                RSEQ_INJECT_ASM(6)
 618                /* teardown */
 619                LONG_L " %[len], %[rseq_scratch2]\n\t"
 620                LONG_L " %[dst], %[rseq_scratch1]\n\t"
 621                LONG_L " %[src], %[rseq_scratch0]\n\t"
 622                "b 8f\n\t"
 623                RSEQ_ASM_DEFINE_ABORT(3, 4,
 624                                      /* teardown */
 625                                      LONG_L " %[len], %[rseq_scratch2]\n\t"
 626                                      LONG_L " %[dst], %[rseq_scratch1]\n\t"
 627                                      LONG_L " %[src], %[rseq_scratch0]\n\t",
 628                                      abort, 1b, 2b, 4f)
 629                RSEQ_ASM_DEFINE_CMPFAIL(5,
 630                                        /* teardown */
 631                                        LONG_L " %[len], %[rseq_scratch2]\n\t"
 632                                        LONG_L " %[dst], %[rseq_scratch1]\n\t"
 633                                        LONG_L " %[src], %[rseq_scratch0]\n\t",
 634                                        cmpfail)
 635#ifdef RSEQ_COMPARE_TWICE
 636                RSEQ_ASM_DEFINE_CMPFAIL(6,
 637                                        /* teardown */
 638                                        LONG_L " %[len], %[rseq_scratch2]\n\t"
 639                                        LONG_L " %[dst], %[rseq_scratch1]\n\t"
 640                                        LONG_L " %[src], %[rseq_scratch0]\n\t",
 641                                        error1)
 642                RSEQ_ASM_DEFINE_CMPFAIL(7,
 643                                        /* teardown */
 644                                        LONG_L " %[len], %[rseq_scratch2]\n\t"
 645                                        LONG_L " %[dst], %[rseq_scratch1]\n\t"
 646                                        LONG_L " %[src], %[rseq_scratch0]\n\t",
 647                                        error2)
 648#endif
 649                "8:\n\t"
 650                : /* gcc asm goto does not allow outputs */
 651                : [cpu_id]              "r" (cpu),
 652                  [current_cpu_id]      "m" (__rseq_abi.cpu_id),
 653                  [rseq_cs]             "m" (__rseq_abi.rseq_cs),
 654                  /* final store input */
 655                  [v]                   "m" (*v),
 656                  [expect]              "r" (expect),
 657                  [newv]                "r" (newv),
 658                  /* try memcpy input */
 659                  [dst]                 "r" (dst),
 660                  [src]                 "r" (src),
 661                  [len]                 "r" (len),
 662                  [rseq_scratch0]       "m" (rseq_scratch[0]),
 663                  [rseq_scratch1]       "m" (rseq_scratch[1]),
 664                  [rseq_scratch2]       "m" (rseq_scratch[2])
 665                  RSEQ_INJECT_INPUT
 666                : "$4", "memory"
 667                  RSEQ_INJECT_CLOBBER
 668                : abort, cmpfail
 669#ifdef RSEQ_COMPARE_TWICE
 670                  , error1, error2
 671#endif
 672        );
 673        rseq_workaround_gcc_asm_size_guess();
 674        return 0;
 675abort:
 676        rseq_workaround_gcc_asm_size_guess();
 677        RSEQ_INJECT_FAILED
 678        return -1;
 679cmpfail:
 680        rseq_workaround_gcc_asm_size_guess();
 681        return 1;
 682#ifdef RSEQ_COMPARE_TWICE
 683error1:
 684        rseq_workaround_gcc_asm_size_guess();
 685        rseq_bug("cpu_id comparison failed");
 686error2:
 687        rseq_workaround_gcc_asm_size_guess();
 688        rseq_bug("expected value comparison failed");
 689#endif
 690}
 691
 692static inline __attribute__((always_inline))
 693int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect,
 694                                         void *dst, void *src, size_t len,
 695                                         intptr_t newv, int cpu)
 696{
 697        uintptr_t rseq_scratch[3];
 698
 699        RSEQ_INJECT_C(9)
 700
 701        rseq_workaround_gcc_asm_size_guess();
 702        __asm__ __volatile__ goto (
 703                RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
 704                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
 705#ifdef RSEQ_COMPARE_TWICE
 706                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
 707                RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
 708#endif
 709                LONG_S " %[src], %[rseq_scratch0]\n\t"
 710                LONG_S " %[dst], %[rseq_scratch1]\n\t"
 711                LONG_S " %[len], %[rseq_scratch2]\n\t"
 712                /* Start rseq by storing table entry pointer into rseq_cs. */
 713                RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
 714                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
 715                RSEQ_INJECT_ASM(3)
 716                LONG_L " $4, %[v]\n\t"
 717                "bne $4, %[expect], 5f\n\t"
 718                RSEQ_INJECT_ASM(4)
 719#ifdef RSEQ_COMPARE_TWICE
 720                RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
 721                LONG_L " $4, %[v]\n\t"
 722                "bne $4, %[expect], 7f\n\t"
 723#endif
 724                /* try memcpy */
 725                "beqz %[len], 333f\n\t" \
 726                "222:\n\t" \
 727                "lb   $4, 0(%[src])\n\t" \
 728                "sb   $4, 0(%[dst])\n\t" \
 729                LONG_ADDI " %[src], 1\n\t" \
 730                LONG_ADDI " %[dst], 1\n\t" \
 731                LONG_ADDI " %[len], -1\n\t" \
 732                "bnez %[len], 222b\n\t" \
 733                "333:\n\t" \
 734                RSEQ_INJECT_ASM(5)
 735                "sync\n\t"      /* full sync provides store-release */
 736                /* final store */
 737                LONG_S " %[newv], %[v]\n\t"
 738                "2:\n\t"
 739                RSEQ_INJECT_ASM(6)
 740                /* teardown */
 741                LONG_L " %[len], %[rseq_scratch2]\n\t"
 742                LONG_L " %[dst], %[rseq_scratch1]\n\t"
 743                LONG_L " %[src], %[rseq_scratch0]\n\t"
 744                "b 8f\n\t"
 745                RSEQ_ASM_DEFINE_ABORT(3, 4,
 746                                      /* teardown */
 747                                      LONG_L " %[len], %[rseq_scratch2]\n\t"
 748                                      LONG_L " %[dst], %[rseq_scratch1]\n\t"
 749                                      LONG_L " %[src], %[rseq_scratch0]\n\t",
 750                                      abort, 1b, 2b, 4f)
 751                RSEQ_ASM_DEFINE_CMPFAIL(5,
 752                                        /* teardown */
 753                                        LONG_L " %[len], %[rseq_scratch2]\n\t"
 754                                        LONG_L " %[dst], %[rseq_scratch1]\n\t"
 755                                        LONG_L " %[src], %[rseq_scratch0]\n\t",
 756                                        cmpfail)
 757#ifdef RSEQ_COMPARE_TWICE
 758                RSEQ_ASM_DEFINE_CMPFAIL(6,
 759                                        /* teardown */
 760                                        LONG_L " %[len], %[rseq_scratch2]\n\t"
 761                                        LONG_L " %[dst], %[rseq_scratch1]\n\t"
 762                                        LONG_L " %[src], %[rseq_scratch0]\n\t",
 763                                        error1)
 764                RSEQ_ASM_DEFINE_CMPFAIL(7,
 765                                        /* teardown */
 766                                        LONG_L " %[len], %[rseq_scratch2]\n\t"
 767                                        LONG_L " %[dst], %[rseq_scratch1]\n\t"
 768                                        LONG_L " %[src], %[rseq_scratch0]\n\t",
 769                                        error2)
 770#endif
 771                "8:\n\t"
 772                : /* gcc asm goto does not allow outputs */
 773                : [cpu_id]              "r" (cpu),
 774                  [current_cpu_id]      "m" (__rseq_abi.cpu_id),
 775                  [rseq_cs]             "m" (__rseq_abi.rseq_cs),
 776                  /* final store input */
 777                  [v]                   "m" (*v),
 778                  [expect]              "r" (expect),
 779                  [newv]                "r" (newv),
 780                  /* try memcpy input */
 781                  [dst]                 "r" (dst),
 782                  [src]                 "r" (src),
 783                  [len]                 "r" (len),
 784                  [rseq_scratch0]       "m" (rseq_scratch[0]),
 785                  [rseq_scratch1]       "m" (rseq_scratch[1]),
 786                  [rseq_scratch2]       "m" (rseq_scratch[2])
 787                  RSEQ_INJECT_INPUT
 788                : "$4", "memory"
 789                  RSEQ_INJECT_CLOBBER
 790                : abort, cmpfail
 791#ifdef RSEQ_COMPARE_TWICE
 792                  , error1, error2
 793#endif
 794        );
 795        rseq_workaround_gcc_asm_size_guess();
 796        return 0;
 797abort:
 798        rseq_workaround_gcc_asm_size_guess();
 799        RSEQ_INJECT_FAILED
 800        return -1;
 801cmpfail:
 802        rseq_workaround_gcc_asm_size_guess();
 803        return 1;
 804#ifdef RSEQ_COMPARE_TWICE
 805error1:
 806        rseq_workaround_gcc_asm_size_guess();
 807        rseq_bug("cpu_id comparison failed");
 808error2:
 809        rseq_workaround_gcc_asm_size_guess();
 810        rseq_bug("expected value comparison failed");
 811#endif
 812}
 813
 814#endif /* !RSEQ_SKIP_FASTPATH */
 815