cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rseq-s390.h (17551B)


      1/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
      2
      3/*
      4 * RSEQ_SIG uses the trap4 instruction. As Linux does not make use of the
      5 * access-register mode nor the linkage stack this instruction will always
      6 * cause a special-operation exception (the trap-enabled bit in the DUCT
      7 * is and will stay 0). The instruction pattern is
      8 *	b2 ff 0f ff	trap4	4095(%r0)
      9 */
     10#define RSEQ_SIG	0xB2FF0FFF
     11
     12#define rseq_smp_mb()	__asm__ __volatile__ ("bcr 15,0" ::: "memory")
     13#define rseq_smp_rmb()	rseq_smp_mb()
     14#define rseq_smp_wmb()	rseq_smp_mb()
     15
     16#define rseq_smp_load_acquire(p)					\
     17__extension__ ({							\
     18	__typeof(*p) ____p1 = RSEQ_READ_ONCE(*p);			\
     19	rseq_barrier();							\
     20	____p1;								\
     21})
     22
     23#define rseq_smp_acquire__after_ctrl_dep()	rseq_smp_rmb()
     24
     25#define rseq_smp_store_release(p, v)					\
     26do {									\
     27	rseq_barrier();							\
     28	RSEQ_WRITE_ONCE(*p, v);						\
     29} while (0)
     30
     31#ifdef RSEQ_SKIP_FASTPATH
     32#include "rseq-skip.h"
     33#else /* !RSEQ_SKIP_FASTPATH */
     34
     35#ifdef __s390x__
     36
     37#define LONG_L			"lg"
     38#define LONG_S			"stg"
     39#define LONG_LT_R		"ltgr"
     40#define LONG_CMP		"cg"
     41#define LONG_CMP_R		"cgr"
     42#define LONG_ADDI		"aghi"
     43#define LONG_ADD_R		"agr"
     44
     45#define __RSEQ_ASM_DEFINE_TABLE(label, version, flags,			\
     46				start_ip, post_commit_offset, abort_ip)	\
     47		".pushsection __rseq_cs, \"aw\"\n\t"			\
     48		".balign 32\n\t"					\
     49		__rseq_str(label) ":\n\t"				\
     50		".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
     51		".quad " __rseq_str(start_ip) ", " __rseq_str(post_commit_offset) ", " __rseq_str(abort_ip) "\n\t" \
     52		".popsection\n\t"					\
     53		".pushsection __rseq_cs_ptr_array, \"aw\"\n\t"		\
     54		".quad " __rseq_str(label) "b\n\t"			\
     55		".popsection\n\t"
     56
     57/*
     58 * Exit points of a rseq critical section consist of all instructions outside
     59 * of the critical section where a critical section can either branch to or
     60 * reach through the normal course of its execution. The abort IP and the
     61 * post-commit IP are already part of the __rseq_cs section and should not be
     62 * explicitly defined as additional exit points. Knowing all exit points is
     63 * useful to assist debuggers stepping over the critical section.
     64 */
     65#define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip)			\
     66		".pushsection __rseq_exit_point_array, \"aw\"\n\t"	\
     67		".quad " __rseq_str(start_ip) ", " __rseq_str(exit_ip) "\n\t" \
     68		".popsection\n\t"
     69
     70#elif __s390__
     71
     72#define __RSEQ_ASM_DEFINE_TABLE(label, version, flags,			\
     73				start_ip, post_commit_offset, abort_ip)	\
     74		".pushsection __rseq_cs, \"aw\"\n\t"			\
     75		".balign 32\n\t"					\
     76		__rseq_str(label) ":\n\t"				\
     77		".long " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
     78		".long 0x0, " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) "\n\t" \
     79		".popsection\n\t"					\
     80		".pushsection __rseq_cs_ptr_array, \"aw\"\n\t"		\
     81		".long 0x0, " __rseq_str(label) "b\n\t"			\
     82		".popsection\n\t"
     83
     84/*
     85 * Exit points of a rseq critical section consist of all instructions outside
     86 * of the critical section where a critical section can either branch to or
     87 * reach through the normal course of its execution. The abort IP and the
     88 * post-commit IP are already part of the __rseq_cs section and should not be
     89 * explicitly defined as additional exit points. Knowing all exit points is
     90 * useful to assist debuggers stepping over the critical section.
     91 */
     92#define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip)			\
     93		".pushsection __rseq_exit_point_array, \"aw\"\n\t"	\
     94		".long 0x0, " __rseq_str(start_ip) ", 0x0, " __rseq_str(exit_ip) "\n\t" \
     95		".popsection\n\t"
     96
     97#define LONG_L			"l"
     98#define LONG_S			"st"
     99#define LONG_LT_R		"ltr"
    100#define LONG_CMP		"c"
    101#define LONG_CMP_R		"cr"
    102#define LONG_ADDI		"ahi"
    103#define LONG_ADD_R		"ar"
    104
    105#endif
    106
    107#define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \
    108	__RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip,		\
    109				(post_commit_ip - start_ip), abort_ip)
    110
    111#define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs)		\
    112		RSEQ_INJECT_ASM(1)					\
    113		"larl %%r0, " __rseq_str(cs_label) "\n\t"		\
    114		LONG_S " %%r0, %[" __rseq_str(rseq_cs) "]\n\t"		\
    115		__rseq_str(label) ":\n\t"
    116
    117#define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label)		\
    118		RSEQ_INJECT_ASM(2)					\
    119		"c %[" __rseq_str(cpu_id) "], %[" __rseq_str(current_cpu_id) "]\n\t" \
    120		"jnz " __rseq_str(label) "\n\t"
    121
    122#define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label)		\
    123		".pushsection __rseq_failure, \"ax\"\n\t"		\
    124		".long " __rseq_str(RSEQ_SIG) "\n\t"			\
    125		__rseq_str(label) ":\n\t"				\
    126		teardown						\
    127		"jg %l[" __rseq_str(abort_label) "]\n\t"		\
    128		".popsection\n\t"
    129
    130#define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label)		\
    131		".pushsection __rseq_failure, \"ax\"\n\t"		\
    132		__rseq_str(label) ":\n\t"				\
    133		teardown						\
    134		"jg %l[" __rseq_str(cmpfail_label) "]\n\t"		\
    135		".popsection\n\t"
    136
    137static inline __attribute__((always_inline))
    138int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
    139{
    140	RSEQ_INJECT_C(9)
    141
    142	__asm__ __volatile__ goto (
    143		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
    144		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
    145#ifdef RSEQ_COMPARE_TWICE
    146		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    147		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
    148#endif
    149		/* Start rseq by storing table entry pointer into rseq_cs. */
    150		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
    151		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    152		RSEQ_INJECT_ASM(3)
    153		LONG_CMP " %[expect], %[v]\n\t"
    154		"jnz %l[cmpfail]\n\t"
    155		RSEQ_INJECT_ASM(4)
    156#ifdef RSEQ_COMPARE_TWICE
    157		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
    158		LONG_CMP " %[expect], %[v]\n\t"
    159		"jnz %l[error2]\n\t"
    160#endif
    161		/* final store */
    162		LONG_S " %[newv], %[v]\n\t"
    163		"2:\n\t"
    164		RSEQ_INJECT_ASM(5)
    165		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
    166		: /* gcc asm goto does not allow outputs */
    167		: [cpu_id]		"r" (cpu),
    168		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    169		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    170		  [v]			"m" (*v),
    171		  [expect]		"r" (expect),
    172		  [newv]		"r" (newv)
    173		  RSEQ_INJECT_INPUT
    174		: "memory", "cc", "r0"
    175		  RSEQ_INJECT_CLOBBER
    176		: abort, cmpfail
    177#ifdef RSEQ_COMPARE_TWICE
    178		  , error1, error2
    179#endif
    180	);
    181	rseq_after_asm_goto();
    182	return 0;
    183abort:
    184	rseq_after_asm_goto();
    185	RSEQ_INJECT_FAILED
    186	return -1;
    187cmpfail:
    188	rseq_after_asm_goto();
    189	return 1;
    190#ifdef RSEQ_COMPARE_TWICE
    191error1:
    192	rseq_after_asm_goto();
    193	rseq_bug("cpu_id comparison failed");
    194error2:
    195	rseq_after_asm_goto();
    196	rseq_bug("expected value comparison failed");
    197#endif
    198}
    199
    200/*
    201 * Compare @v against @expectnot. When it does _not_ match, load @v
    202 * into @load, and store the content of *@v + voffp into @v.
    203 */
    204static inline __attribute__((always_inline))
    205int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot,
    206			       long voffp, intptr_t *load, int cpu)
    207{
    208	RSEQ_INJECT_C(9)
    209
    210	__asm__ __volatile__ goto (
    211		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
    212		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
    213#ifdef RSEQ_COMPARE_TWICE
    214		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    215		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
    216#endif
    217		/* Start rseq by storing table entry pointer into rseq_cs. */
    218		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
    219		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    220		RSEQ_INJECT_ASM(3)
    221		LONG_L " %%r1, %[v]\n\t"
    222		LONG_CMP_R " %%r1, %[expectnot]\n\t"
    223		"je %l[cmpfail]\n\t"
    224		RSEQ_INJECT_ASM(4)
    225#ifdef RSEQ_COMPARE_TWICE
    226		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
    227		LONG_L " %%r1, %[v]\n\t"
    228		LONG_CMP_R " %%r1, %[expectnot]\n\t"
    229		"je %l[error2]\n\t"
    230#endif
    231		LONG_S " %%r1, %[load]\n\t"
    232		LONG_ADD_R " %%r1, %[voffp]\n\t"
    233		LONG_L " %%r1, 0(%%r1)\n\t"
    234		/* final store */
    235		LONG_S " %%r1, %[v]\n\t"
    236		"2:\n\t"
    237		RSEQ_INJECT_ASM(5)
    238		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
    239		: /* gcc asm goto does not allow outputs */
    240		: [cpu_id]		"r" (cpu),
    241		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    242		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    243		  /* final store input */
    244		  [v]			"m" (*v),
    245		  [expectnot]		"r" (expectnot),
    246		  [voffp]		"r" (voffp),
    247		  [load]		"m" (*load)
    248		  RSEQ_INJECT_INPUT
    249		: "memory", "cc", "r0", "r1"
    250		  RSEQ_INJECT_CLOBBER
    251		: abort, cmpfail
    252#ifdef RSEQ_COMPARE_TWICE
    253		  , error1, error2
    254#endif
    255	);
    256	rseq_after_asm_goto();
    257	return 0;
    258abort:
    259	rseq_after_asm_goto();
    260	RSEQ_INJECT_FAILED
    261	return -1;
    262cmpfail:
    263	rseq_after_asm_goto();
    264	return 1;
    265#ifdef RSEQ_COMPARE_TWICE
    266error1:
    267	rseq_after_asm_goto();
    268	rseq_bug("cpu_id comparison failed");
    269error2:
    270	rseq_after_asm_goto();
    271	rseq_bug("expected value comparison failed");
    272#endif
    273}
    274
    275static inline __attribute__((always_inline))
    276int rseq_addv(intptr_t *v, intptr_t count, int cpu)
    277{
    278	RSEQ_INJECT_C(9)
    279
    280	__asm__ __volatile__ goto (
    281		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
    282#ifdef RSEQ_COMPARE_TWICE
    283		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    284#endif
    285		/* Start rseq by storing table entry pointer into rseq_cs. */
    286		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
    287		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    288		RSEQ_INJECT_ASM(3)
    289#ifdef RSEQ_COMPARE_TWICE
    290		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
    291#endif
    292		LONG_L " %%r0, %[v]\n\t"
    293		LONG_ADD_R " %%r0, %[count]\n\t"
    294		/* final store */
    295		LONG_S " %%r0, %[v]\n\t"
    296		"2:\n\t"
    297		RSEQ_INJECT_ASM(4)
    298		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
    299		: /* gcc asm goto does not allow outputs */
    300		: [cpu_id]		"r" (cpu),
    301		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    302		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    303		  /* final store input */
    304		  [v]			"m" (*v),
    305		  [count]		"r" (count)
    306		  RSEQ_INJECT_INPUT
    307		: "memory", "cc", "r0"
    308		  RSEQ_INJECT_CLOBBER
    309		: abort
    310#ifdef RSEQ_COMPARE_TWICE
    311		  , error1
    312#endif
    313	);
    314	rseq_after_asm_goto();
    315	return 0;
    316abort:
    317	rseq_after_asm_goto();
    318	RSEQ_INJECT_FAILED
    319	return -1;
    320#ifdef RSEQ_COMPARE_TWICE
    321error1:
    322	rseq_after_asm_goto();
    323	rseq_bug("cpu_id comparison failed");
    324#endif
    325}
    326
    327static inline __attribute__((always_inline))
    328int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect,
    329				 intptr_t *v2, intptr_t newv2,
    330				 intptr_t newv, int cpu)
    331{
    332	RSEQ_INJECT_C(9)
    333
    334	__asm__ __volatile__ goto (
    335		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
    336		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
    337#ifdef RSEQ_COMPARE_TWICE
    338		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    339		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
    340#endif
    341		/* Start rseq by storing table entry pointer into rseq_cs. */
    342		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
    343		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    344		RSEQ_INJECT_ASM(3)
    345		LONG_CMP " %[expect], %[v]\n\t"
    346		"jnz %l[cmpfail]\n\t"
    347		RSEQ_INJECT_ASM(4)
    348#ifdef RSEQ_COMPARE_TWICE
    349		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
    350		LONG_CMP " %[expect], %[v]\n\t"
    351		"jnz %l[error2]\n\t"
    352#endif
    353		/* try store */
    354		LONG_S " %[newv2], %[v2]\n\t"
    355		RSEQ_INJECT_ASM(5)
    356		/* final store */
    357		LONG_S " %[newv], %[v]\n\t"
    358		"2:\n\t"
    359		RSEQ_INJECT_ASM(6)
    360		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
    361		: /* gcc asm goto does not allow outputs */
    362		: [cpu_id]		"r" (cpu),
    363		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    364		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    365		  /* try store input */
    366		  [v2]			"m" (*v2),
    367		  [newv2]		"r" (newv2),
    368		  /* final store input */
    369		  [v]			"m" (*v),
    370		  [expect]		"r" (expect),
    371		  [newv]		"r" (newv)
    372		  RSEQ_INJECT_INPUT
    373		: "memory", "cc", "r0"
    374		  RSEQ_INJECT_CLOBBER
    375		: abort, cmpfail
    376#ifdef RSEQ_COMPARE_TWICE
    377		  , error1, error2
    378#endif
    379	);
    380	rseq_after_asm_goto();
    381	return 0;
    382abort:
    383	rseq_after_asm_goto();
    384	RSEQ_INJECT_FAILED
    385	return -1;
    386cmpfail:
    387	rseq_after_asm_goto();
    388	return 1;
    389#ifdef RSEQ_COMPARE_TWICE
    390error1:
    391	rseq_after_asm_goto();
    392	rseq_bug("cpu_id comparison failed");
    393error2:
    394	rseq_after_asm_goto();
    395	rseq_bug("expected value comparison failed");
    396#endif
    397}
    398
    399/* s390 is TSO. */
    400static inline __attribute__((always_inline))
    401int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect,
    402					 intptr_t *v2, intptr_t newv2,
    403					 intptr_t newv, int cpu)
    404{
    405	return rseq_cmpeqv_trystorev_storev(v, expect, v2, newv2, newv, cpu);
    406}
    407
    408static inline __attribute__((always_inline))
    409int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect,
    410			      intptr_t *v2, intptr_t expect2,
    411			      intptr_t newv, int cpu)
    412{
    413	RSEQ_INJECT_C(9)
    414
    415	__asm__ __volatile__ goto (
    416		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
    417		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
    418#ifdef RSEQ_COMPARE_TWICE
    419		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    420		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
    421		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3])
    422#endif
    423		/* Start rseq by storing table entry pointer into rseq_cs. */
    424		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
    425		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    426		RSEQ_INJECT_ASM(3)
    427		LONG_CMP " %[expect], %[v]\n\t"
    428		"jnz %l[cmpfail]\n\t"
    429		RSEQ_INJECT_ASM(4)
    430		LONG_CMP " %[expect2], %[v2]\n\t"
    431		"jnz %l[cmpfail]\n\t"
    432		RSEQ_INJECT_ASM(5)
    433#ifdef RSEQ_COMPARE_TWICE
    434		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
    435		LONG_CMP " %[expect], %[v]\n\t"
    436		"jnz %l[error2]\n\t"
    437		LONG_CMP " %[expect2], %[v2]\n\t"
    438		"jnz %l[error3]\n\t"
    439#endif
    440		/* final store */
    441		LONG_S " %[newv], %[v]\n\t"
    442		"2:\n\t"
    443		RSEQ_INJECT_ASM(6)
    444		RSEQ_ASM_DEFINE_ABORT(4, "", abort)
    445		: /* gcc asm goto does not allow outputs */
    446		: [cpu_id]		"r" (cpu),
    447		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    448		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    449		  /* cmp2 input */
    450		  [v2]			"m" (*v2),
    451		  [expect2]		"r" (expect2),
    452		  /* final store input */
    453		  [v]			"m" (*v),
    454		  [expect]		"r" (expect),
    455		  [newv]		"r" (newv)
    456		  RSEQ_INJECT_INPUT
    457		: "memory", "cc", "r0"
    458		  RSEQ_INJECT_CLOBBER
    459		: abort, cmpfail
    460#ifdef RSEQ_COMPARE_TWICE
    461		  , error1, error2, error3
    462#endif
    463	);
    464	rseq_after_asm_goto();
    465	return 0;
    466abort:
    467	rseq_after_asm_goto();
    468	RSEQ_INJECT_FAILED
    469	return -1;
    470cmpfail:
    471	rseq_after_asm_goto();
    472	return 1;
    473#ifdef RSEQ_COMPARE_TWICE
    474error1:
    475	rseq_after_asm_goto();
    476	rseq_bug("cpu_id comparison failed");
    477error2:
    478	rseq_after_asm_goto();
    479	rseq_bug("1st expected value comparison failed");
    480error3:
    481	rseq_after_asm_goto();
    482	rseq_bug("2nd expected value comparison failed");
    483#endif
    484}
    485
    486static inline __attribute__((always_inline))
    487int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect,
    488				 void *dst, void *src, size_t len,
    489				 intptr_t newv, int cpu)
    490{
    491	uint64_t rseq_scratch[3];
    492
    493	RSEQ_INJECT_C(9)
    494
    495	__asm__ __volatile__ goto (
    496		RSEQ_ASM_DEFINE_TABLE(3, 1f, 2f, 4f) /* start, commit, abort */
    497		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
    498#ifdef RSEQ_COMPARE_TWICE
    499		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    500		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
    501#endif
    502		LONG_S " %[src], %[rseq_scratch0]\n\t"
    503		LONG_S " %[dst], %[rseq_scratch1]\n\t"
    504		LONG_S " %[len], %[rseq_scratch2]\n\t"
    505		/* Start rseq by storing table entry pointer into rseq_cs. */
    506		RSEQ_ASM_STORE_RSEQ_CS(1, 3b, rseq_cs)
    507		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    508		RSEQ_INJECT_ASM(3)
    509		LONG_CMP " %[expect], %[v]\n\t"
    510		"jnz 5f\n\t"
    511		RSEQ_INJECT_ASM(4)
    512#ifdef RSEQ_COMPARE_TWICE
    513		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
    514		LONG_CMP " %[expect], %[v]\n\t"
    515		"jnz 7f\n\t"
    516#endif
    517		/* try memcpy */
    518		LONG_LT_R " %[len], %[len]\n\t"
    519		"jz 333f\n\t"
    520		"222:\n\t"
    521		"ic %%r0,0(%[src])\n\t"
    522		"stc %%r0,0(%[dst])\n\t"
    523		LONG_ADDI " %[src], 1\n\t"
    524		LONG_ADDI " %[dst], 1\n\t"
    525		LONG_ADDI " %[len], -1\n\t"
    526		"jnz 222b\n\t"
    527		"333:\n\t"
    528		RSEQ_INJECT_ASM(5)
    529		/* final store */
    530		LONG_S " %[newv], %[v]\n\t"
    531		"2:\n\t"
    532		RSEQ_INJECT_ASM(6)
    533		/* teardown */
    534		LONG_L " %[len], %[rseq_scratch2]\n\t"
    535		LONG_L " %[dst], %[rseq_scratch1]\n\t"
    536		LONG_L " %[src], %[rseq_scratch0]\n\t"
    537		RSEQ_ASM_DEFINE_ABORT(4,
    538			LONG_L " %[len], %[rseq_scratch2]\n\t"
    539			LONG_L " %[dst], %[rseq_scratch1]\n\t"
    540			LONG_L " %[src], %[rseq_scratch0]\n\t",
    541			abort)
    542		RSEQ_ASM_DEFINE_CMPFAIL(5,
    543			LONG_L " %[len], %[rseq_scratch2]\n\t"
    544			LONG_L " %[dst], %[rseq_scratch1]\n\t"
    545			LONG_L " %[src], %[rseq_scratch0]\n\t",
    546			cmpfail)
    547#ifdef RSEQ_COMPARE_TWICE
    548		RSEQ_ASM_DEFINE_CMPFAIL(6,
    549			LONG_L " %[len], %[rseq_scratch2]\n\t"
    550			LONG_L " %[dst], %[rseq_scratch1]\n\t"
    551			LONG_L " %[src], %[rseq_scratch0]\n\t",
    552			error1)
    553		RSEQ_ASM_DEFINE_CMPFAIL(7,
    554			LONG_L " %[len], %[rseq_scratch2]\n\t"
    555			LONG_L " %[dst], %[rseq_scratch1]\n\t"
    556			LONG_L " %[src], %[rseq_scratch0]\n\t",
    557			error2)
    558#endif
    559		: /* gcc asm goto does not allow outputs */
    560		: [cpu_id]		"r" (cpu),
    561		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    562		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    563		  /* final store input */
    564		  [v]			"m" (*v),
    565		  [expect]		"r" (expect),
    566		  [newv]		"r" (newv),
    567		  /* try memcpy input */
    568		  [dst]			"r" (dst),
    569		  [src]			"r" (src),
    570		  [len]			"r" (len),
    571		  [rseq_scratch0]	"m" (rseq_scratch[0]),
    572		  [rseq_scratch1]	"m" (rseq_scratch[1]),
    573		  [rseq_scratch2]	"m" (rseq_scratch[2])
    574		  RSEQ_INJECT_INPUT
    575		: "memory", "cc", "r0"
    576		  RSEQ_INJECT_CLOBBER
    577		: abort, cmpfail
    578#ifdef RSEQ_COMPARE_TWICE
    579		  , error1, error2
    580#endif
    581	);
    582	rseq_after_asm_goto();
    583	return 0;
    584abort:
    585	rseq_after_asm_goto();
    586	RSEQ_INJECT_FAILED
    587	return -1;
    588cmpfail:
    589	rseq_after_asm_goto();
    590	return 1;
    591#ifdef RSEQ_COMPARE_TWICE
    592error1:
    593	rseq_after_asm_goto();
    594	rseq_bug("cpu_id comparison failed");
    595error2:
    596	rseq_after_asm_goto();
    597	rseq_bug("expected value comparison failed");
    598#endif
    599}
    600
    601/* s390 is TSO. */
    602static inline __attribute__((always_inline))
    603int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect,
    604					 void *dst, void *src, size_t len,
    605					 intptr_t newv, int cpu)
    606{
    607	return rseq_cmpeqv_trymemcpy_storev(v, expect, dst, src, len,
    608					    newv, cpu);
    609}
    610#endif /* !RSEQ_SKIP_FASTPATH */