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-mips.h (21703B)


      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
    157static inline __attribute__((always_inline))
    158int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
    159{
    160	RSEQ_INJECT_C(9)
    161
    162	__asm__ __volatile__ goto (
    163		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
    164		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
    165#ifdef RSEQ_COMPARE_TWICE
    166		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    167		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
    168#endif
    169		/* Start rseq by storing table entry pointer into rseq_cs. */
    170		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
    171		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    172		RSEQ_INJECT_ASM(3)
    173		LONG_L " $4, %[v]\n\t"
    174		"bne $4, %[expect], %l[cmpfail]\n\t"
    175		RSEQ_INJECT_ASM(4)
    176#ifdef RSEQ_COMPARE_TWICE
    177		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
    178		LONG_L " $4, %[v]\n\t"
    179		"bne $4, %[expect], %l[error2]\n\t"
    180#endif
    181		/* final store */
    182		LONG_S " %[newv], %[v]\n\t"
    183		"2:\n\t"
    184		RSEQ_INJECT_ASM(5)
    185		"b 5f\n\t"
    186		RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
    187		"5:\n\t"
    188		: /* gcc asm goto does not allow outputs */
    189		: [cpu_id]		"r" (cpu),
    190		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    191		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    192		  [v]			"m" (*v),
    193		  [expect]		"r" (expect),
    194		  [newv]		"r" (newv)
    195		  RSEQ_INJECT_INPUT
    196		: "$4", "memory"
    197		  RSEQ_INJECT_CLOBBER
    198		: abort, cmpfail
    199#ifdef RSEQ_COMPARE_TWICE
    200		  , error1, error2
    201#endif
    202	);
    203	return 0;
    204abort:
    205	RSEQ_INJECT_FAILED
    206	return -1;
    207cmpfail:
    208	return 1;
    209#ifdef RSEQ_COMPARE_TWICE
    210error1:
    211	rseq_bug("cpu_id comparison failed");
    212error2:
    213	rseq_bug("expected value comparison failed");
    214#endif
    215}
    216
    217static inline __attribute__((always_inline))
    218int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot,
    219			       long voffp, intptr_t *load, int cpu)
    220{
    221	RSEQ_INJECT_C(9)
    222
    223	__asm__ __volatile__ goto (
    224		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
    225		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
    226#ifdef RSEQ_COMPARE_TWICE
    227		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    228		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
    229#endif
    230		/* Start rseq by storing table entry pointer into rseq_cs. */
    231		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
    232		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    233		RSEQ_INJECT_ASM(3)
    234		LONG_L " $4, %[v]\n\t"
    235		"beq $4, %[expectnot], %l[cmpfail]\n\t"
    236		RSEQ_INJECT_ASM(4)
    237#ifdef RSEQ_COMPARE_TWICE
    238		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
    239		LONG_L " $4, %[v]\n\t"
    240		"beq $4, %[expectnot], %l[error2]\n\t"
    241#endif
    242		LONG_S " $4, %[load]\n\t"
    243		LONG_ADDI " $4, %[voffp]\n\t"
    244		LONG_L " $4, 0($4)\n\t"
    245		/* final store */
    246		LONG_S " $4, %[v]\n\t"
    247		"2:\n\t"
    248		RSEQ_INJECT_ASM(5)
    249		"b 5f\n\t"
    250		RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
    251		"5:\n\t"
    252		: /* gcc asm goto does not allow outputs */
    253		: [cpu_id]		"r" (cpu),
    254		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    255		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    256		  /* final store input */
    257		  [v]			"m" (*v),
    258		  [expectnot]		"r" (expectnot),
    259		  [voffp]		"Ir" (voffp),
    260		  [load]		"m" (*load)
    261		  RSEQ_INJECT_INPUT
    262		: "$4", "memory"
    263		  RSEQ_INJECT_CLOBBER
    264		: abort, cmpfail
    265#ifdef RSEQ_COMPARE_TWICE
    266		  , error1, error2
    267#endif
    268	);
    269	return 0;
    270abort:
    271	RSEQ_INJECT_FAILED
    272	return -1;
    273cmpfail:
    274	return 1;
    275#ifdef RSEQ_COMPARE_TWICE
    276error1:
    277	rseq_bug("cpu_id comparison failed");
    278error2:
    279	rseq_bug("expected value comparison failed");
    280#endif
    281}
    282
    283static inline __attribute__((always_inline))
    284int rseq_addv(intptr_t *v, intptr_t count, int cpu)
    285{
    286	RSEQ_INJECT_C(9)
    287
    288	__asm__ __volatile__ goto (
    289		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
    290#ifdef RSEQ_COMPARE_TWICE
    291		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    292#endif
    293		/* Start rseq by storing table entry pointer into rseq_cs. */
    294		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
    295		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    296		RSEQ_INJECT_ASM(3)
    297#ifdef RSEQ_COMPARE_TWICE
    298		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
    299#endif
    300		LONG_L " $4, %[v]\n\t"
    301		LONG_ADDI " $4, %[count]\n\t"
    302		/* final store */
    303		LONG_S " $4, %[v]\n\t"
    304		"2:\n\t"
    305		RSEQ_INJECT_ASM(4)
    306		"b 5f\n\t"
    307		RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
    308		"5:\n\t"
    309		: /* gcc asm goto does not allow outputs */
    310		: [cpu_id]		"r" (cpu),
    311		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    312		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    313		  [v]			"m" (*v),
    314		  [count]		"Ir" (count)
    315		  RSEQ_INJECT_INPUT
    316		: "$4", "memory"
    317		  RSEQ_INJECT_CLOBBER
    318		: abort
    319#ifdef RSEQ_COMPARE_TWICE
    320		  , error1
    321#endif
    322	);
    323	return 0;
    324abort:
    325	RSEQ_INJECT_FAILED
    326	return -1;
    327#ifdef RSEQ_COMPARE_TWICE
    328error1:
    329	rseq_bug("cpu_id comparison failed");
    330#endif
    331}
    332
    333static inline __attribute__((always_inline))
    334int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect,
    335				 intptr_t *v2, intptr_t newv2,
    336				 intptr_t newv, int cpu)
    337{
    338	RSEQ_INJECT_C(9)
    339
    340	__asm__ __volatile__ goto (
    341		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
    342		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
    343#ifdef RSEQ_COMPARE_TWICE
    344		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    345		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
    346#endif
    347		/* Start rseq by storing table entry pointer into rseq_cs. */
    348		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
    349		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    350		RSEQ_INJECT_ASM(3)
    351		LONG_L " $4, %[v]\n\t"
    352		"bne $4, %[expect], %l[cmpfail]\n\t"
    353		RSEQ_INJECT_ASM(4)
    354#ifdef RSEQ_COMPARE_TWICE
    355		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
    356		LONG_L " $4, %[v]\n\t"
    357		"bne $4, %[expect], %l[error2]\n\t"
    358#endif
    359		/* try store */
    360		LONG_S " %[newv2], %[v2]\n\t"
    361		RSEQ_INJECT_ASM(5)
    362		/* final store */
    363		LONG_S " %[newv], %[v]\n\t"
    364		"2:\n\t"
    365		RSEQ_INJECT_ASM(6)
    366		"b 5f\n\t"
    367		RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
    368		"5:\n\t"
    369		: /* gcc asm goto does not allow outputs */
    370		: [cpu_id]		"r" (cpu),
    371		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    372		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    373		  /* try store input */
    374		  [v2]			"m" (*v2),
    375		  [newv2]		"r" (newv2),
    376		  /* final store input */
    377		  [v]			"m" (*v),
    378		  [expect]		"r" (expect),
    379		  [newv]		"r" (newv)
    380		  RSEQ_INJECT_INPUT
    381		: "$4", "memory"
    382		  RSEQ_INJECT_CLOBBER
    383		: abort, cmpfail
    384#ifdef RSEQ_COMPARE_TWICE
    385		  , error1, error2
    386#endif
    387	);
    388	return 0;
    389abort:
    390	RSEQ_INJECT_FAILED
    391	return -1;
    392cmpfail:
    393	return 1;
    394#ifdef RSEQ_COMPARE_TWICE
    395error1:
    396	rseq_bug("cpu_id comparison failed");
    397error2:
    398	rseq_bug("expected value comparison failed");
    399#endif
    400}
    401
    402static inline __attribute__((always_inline))
    403int rseq_cmpeqv_trystorev_storev_release(intptr_t *v, intptr_t expect,
    404					 intptr_t *v2, intptr_t newv2,
    405					 intptr_t newv, int cpu)
    406{
    407	RSEQ_INJECT_C(9)
    408
    409	__asm__ __volatile__ goto (
    410		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
    411		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
    412#ifdef RSEQ_COMPARE_TWICE
    413		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    414		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
    415#endif
    416		/* Start rseq by storing table entry pointer into rseq_cs. */
    417		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
    418		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    419		RSEQ_INJECT_ASM(3)
    420		LONG_L " $4, %[v]\n\t"
    421		"bne $4, %[expect], %l[cmpfail]\n\t"
    422		RSEQ_INJECT_ASM(4)
    423#ifdef RSEQ_COMPARE_TWICE
    424		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
    425		LONG_L " $4, %[v]\n\t"
    426		"bne $4, %[expect], %l[error2]\n\t"
    427#endif
    428		/* try store */
    429		LONG_S " %[newv2], %[v2]\n\t"
    430		RSEQ_INJECT_ASM(5)
    431		"sync\n\t"	/* full sync provides store-release */
    432		/* final store */
    433		LONG_S " %[newv], %[v]\n\t"
    434		"2:\n\t"
    435		RSEQ_INJECT_ASM(6)
    436		"b 5f\n\t"
    437		RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
    438		"5:\n\t"
    439		: /* gcc asm goto does not allow outputs */
    440		: [cpu_id]		"r" (cpu),
    441		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    442		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    443		  /* try store input */
    444		  [v2]			"m" (*v2),
    445		  [newv2]		"r" (newv2),
    446		  /* final store input */
    447		  [v]			"m" (*v),
    448		  [expect]		"r" (expect),
    449		  [newv]		"r" (newv)
    450		  RSEQ_INJECT_INPUT
    451		: "$4", "memory"
    452		  RSEQ_INJECT_CLOBBER
    453		: abort, cmpfail
    454#ifdef RSEQ_COMPARE_TWICE
    455		  , error1, error2
    456#endif
    457	);
    458	return 0;
    459abort:
    460	RSEQ_INJECT_FAILED
    461	return -1;
    462cmpfail:
    463	return 1;
    464#ifdef RSEQ_COMPARE_TWICE
    465error1:
    466	rseq_bug("cpu_id comparison failed");
    467error2:
    468	rseq_bug("expected value comparison failed");
    469#endif
    470}
    471
    472static inline __attribute__((always_inline))
    473int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect,
    474			      intptr_t *v2, intptr_t expect2,
    475			      intptr_t newv, int cpu)
    476{
    477	RSEQ_INJECT_C(9)
    478
    479	__asm__ __volatile__ goto (
    480		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
    481		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
    482#ifdef RSEQ_COMPARE_TWICE
    483		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    484		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
    485		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error3])
    486#endif
    487		/* Start rseq by storing table entry pointer into rseq_cs. */
    488		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
    489		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    490		RSEQ_INJECT_ASM(3)
    491		LONG_L " $4, %[v]\n\t"
    492		"bne $4, %[expect], %l[cmpfail]\n\t"
    493		RSEQ_INJECT_ASM(4)
    494		LONG_L " $4, %[v2]\n\t"
    495		"bne $4, %[expect2], %l[cmpfail]\n\t"
    496		RSEQ_INJECT_ASM(5)
    497#ifdef RSEQ_COMPARE_TWICE
    498		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
    499		LONG_L " $4, %[v]\n\t"
    500		"bne $4, %[expect], %l[error2]\n\t"
    501		LONG_L " $4, %[v2]\n\t"
    502		"bne $4, %[expect2], %l[error3]\n\t"
    503#endif
    504		/* final store */
    505		LONG_S " %[newv], %[v]\n\t"
    506		"2:\n\t"
    507		RSEQ_INJECT_ASM(6)
    508		"b 5f\n\t"
    509		RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
    510		"5:\n\t"
    511		: /* gcc asm goto does not allow outputs */
    512		: [cpu_id]		"r" (cpu),
    513		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    514		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    515		  /* cmp2 input */
    516		  [v2]			"m" (*v2),
    517		  [expect2]		"r" (expect2),
    518		  /* final store input */
    519		  [v]			"m" (*v),
    520		  [expect]		"r" (expect),
    521		  [newv]		"r" (newv)
    522		  RSEQ_INJECT_INPUT
    523		: "$4", "memory"
    524		  RSEQ_INJECT_CLOBBER
    525		: abort, cmpfail
    526#ifdef RSEQ_COMPARE_TWICE
    527		  , error1, error2, error3
    528#endif
    529	);
    530	return 0;
    531abort:
    532	RSEQ_INJECT_FAILED
    533	return -1;
    534cmpfail:
    535	return 1;
    536#ifdef RSEQ_COMPARE_TWICE
    537error1:
    538	rseq_bug("cpu_id comparison failed");
    539error2:
    540	rseq_bug("1st expected value comparison failed");
    541error3:
    542	rseq_bug("2nd expected value comparison failed");
    543#endif
    544}
    545
    546static inline __attribute__((always_inline))
    547int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect,
    548				 void *dst, void *src, size_t len,
    549				 intptr_t newv, int cpu)
    550{
    551	uintptr_t rseq_scratch[3];
    552
    553	RSEQ_INJECT_C(9)
    554
    555	__asm__ __volatile__ goto (
    556		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
    557		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
    558#ifdef RSEQ_COMPARE_TWICE
    559		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    560		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
    561#endif
    562		LONG_S " %[src], %[rseq_scratch0]\n\t"
    563		LONG_S "  %[dst], %[rseq_scratch1]\n\t"
    564		LONG_S " %[len], %[rseq_scratch2]\n\t"
    565		/* Start rseq by storing table entry pointer into rseq_cs. */
    566		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
    567		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    568		RSEQ_INJECT_ASM(3)
    569		LONG_L " $4, %[v]\n\t"
    570		"bne $4, %[expect], 5f\n\t"
    571		RSEQ_INJECT_ASM(4)
    572#ifdef RSEQ_COMPARE_TWICE
    573		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
    574		LONG_L " $4, %[v]\n\t"
    575		"bne $4, %[expect], 7f\n\t"
    576#endif
    577		/* try memcpy */
    578		"beqz %[len], 333f\n\t" \
    579		"222:\n\t" \
    580		"lb   $4, 0(%[src])\n\t" \
    581		"sb   $4, 0(%[dst])\n\t" \
    582		LONG_ADDI " %[src], 1\n\t" \
    583		LONG_ADDI " %[dst], 1\n\t" \
    584		LONG_ADDI " %[len], -1\n\t" \
    585		"bnez %[len], 222b\n\t" \
    586		"333:\n\t" \
    587		RSEQ_INJECT_ASM(5)
    588		/* final store */
    589		LONG_S " %[newv], %[v]\n\t"
    590		"2:\n\t"
    591		RSEQ_INJECT_ASM(6)
    592		/* teardown */
    593		LONG_L " %[len], %[rseq_scratch2]\n\t"
    594		LONG_L " %[dst], %[rseq_scratch1]\n\t"
    595		LONG_L " %[src], %[rseq_scratch0]\n\t"
    596		"b 8f\n\t"
    597		RSEQ_ASM_DEFINE_ABORT(3, 4,
    598				      /* teardown */
    599				      LONG_L " %[len], %[rseq_scratch2]\n\t"
    600				      LONG_L " %[dst], %[rseq_scratch1]\n\t"
    601				      LONG_L " %[src], %[rseq_scratch0]\n\t",
    602				      abort, 1b, 2b, 4f)
    603		RSEQ_ASM_DEFINE_CMPFAIL(5,
    604					/* teardown */
    605					LONG_L " %[len], %[rseq_scratch2]\n\t"
    606					LONG_L " %[dst], %[rseq_scratch1]\n\t"
    607					LONG_L " %[src], %[rseq_scratch0]\n\t",
    608					cmpfail)
    609#ifdef RSEQ_COMPARE_TWICE
    610		RSEQ_ASM_DEFINE_CMPFAIL(6,
    611					/* teardown */
    612					LONG_L " %[len], %[rseq_scratch2]\n\t"
    613					LONG_L " %[dst], %[rseq_scratch1]\n\t"
    614					LONG_L " %[src], %[rseq_scratch0]\n\t",
    615					error1)
    616		RSEQ_ASM_DEFINE_CMPFAIL(7,
    617					/* teardown */
    618					LONG_L " %[len], %[rseq_scratch2]\n\t"
    619					LONG_L " %[dst], %[rseq_scratch1]\n\t"
    620					LONG_L " %[src], %[rseq_scratch0]\n\t",
    621					error2)
    622#endif
    623		"8:\n\t"
    624		: /* gcc asm goto does not allow outputs */
    625		: [cpu_id]		"r" (cpu),
    626		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    627		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    628		  /* final store input */
    629		  [v]			"m" (*v),
    630		  [expect]		"r" (expect),
    631		  [newv]		"r" (newv),
    632		  /* try memcpy input */
    633		  [dst]			"r" (dst),
    634		  [src]			"r" (src),
    635		  [len]			"r" (len),
    636		  [rseq_scratch0]	"m" (rseq_scratch[0]),
    637		  [rseq_scratch1]	"m" (rseq_scratch[1]),
    638		  [rseq_scratch2]	"m" (rseq_scratch[2])
    639		  RSEQ_INJECT_INPUT
    640		: "$4", "memory"
    641		  RSEQ_INJECT_CLOBBER
    642		: abort, cmpfail
    643#ifdef RSEQ_COMPARE_TWICE
    644		  , error1, error2
    645#endif
    646	);
    647	return 0;
    648abort:
    649	RSEQ_INJECT_FAILED
    650	return -1;
    651cmpfail:
    652	return 1;
    653#ifdef RSEQ_COMPARE_TWICE
    654error1:
    655	rseq_bug("cpu_id comparison failed");
    656error2:
    657	rseq_bug("expected value comparison failed");
    658#endif
    659}
    660
    661static inline __attribute__((always_inline))
    662int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect,
    663					 void *dst, void *src, size_t len,
    664					 intptr_t newv, int cpu)
    665{
    666	uintptr_t rseq_scratch[3];
    667
    668	RSEQ_INJECT_C(9)
    669
    670	__asm__ __volatile__ goto (
    671		RSEQ_ASM_DEFINE_TABLE(9, 1f, 2f, 4f) /* start, commit, abort */
    672		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[cmpfail])
    673#ifdef RSEQ_COMPARE_TWICE
    674		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error1])
    675		RSEQ_ASM_DEFINE_EXIT_POINT(1f, %l[error2])
    676#endif
    677		LONG_S " %[src], %[rseq_scratch0]\n\t"
    678		LONG_S " %[dst], %[rseq_scratch1]\n\t"
    679		LONG_S " %[len], %[rseq_scratch2]\n\t"
    680		/* Start rseq by storing table entry pointer into rseq_cs. */
    681		RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
    682		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
    683		RSEQ_INJECT_ASM(3)
    684		LONG_L " $4, %[v]\n\t"
    685		"bne $4, %[expect], 5f\n\t"
    686		RSEQ_INJECT_ASM(4)
    687#ifdef RSEQ_COMPARE_TWICE
    688		RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
    689		LONG_L " $4, %[v]\n\t"
    690		"bne $4, %[expect], 7f\n\t"
    691#endif
    692		/* try memcpy */
    693		"beqz %[len], 333f\n\t" \
    694		"222:\n\t" \
    695		"lb   $4, 0(%[src])\n\t" \
    696		"sb   $4, 0(%[dst])\n\t" \
    697		LONG_ADDI " %[src], 1\n\t" \
    698		LONG_ADDI " %[dst], 1\n\t" \
    699		LONG_ADDI " %[len], -1\n\t" \
    700		"bnez %[len], 222b\n\t" \
    701		"333:\n\t" \
    702		RSEQ_INJECT_ASM(5)
    703		"sync\n\t"	/* full sync provides store-release */
    704		/* final store */
    705		LONG_S " %[newv], %[v]\n\t"
    706		"2:\n\t"
    707		RSEQ_INJECT_ASM(6)
    708		/* teardown */
    709		LONG_L " %[len], %[rseq_scratch2]\n\t"
    710		LONG_L " %[dst], %[rseq_scratch1]\n\t"
    711		LONG_L " %[src], %[rseq_scratch0]\n\t"
    712		"b 8f\n\t"
    713		RSEQ_ASM_DEFINE_ABORT(3, 4,
    714				      /* teardown */
    715				      LONG_L " %[len], %[rseq_scratch2]\n\t"
    716				      LONG_L " %[dst], %[rseq_scratch1]\n\t"
    717				      LONG_L " %[src], %[rseq_scratch0]\n\t",
    718				      abort, 1b, 2b, 4f)
    719		RSEQ_ASM_DEFINE_CMPFAIL(5,
    720					/* teardown */
    721					LONG_L " %[len], %[rseq_scratch2]\n\t"
    722					LONG_L " %[dst], %[rseq_scratch1]\n\t"
    723					LONG_L " %[src], %[rseq_scratch0]\n\t",
    724					cmpfail)
    725#ifdef RSEQ_COMPARE_TWICE
    726		RSEQ_ASM_DEFINE_CMPFAIL(6,
    727					/* teardown */
    728					LONG_L " %[len], %[rseq_scratch2]\n\t"
    729					LONG_L " %[dst], %[rseq_scratch1]\n\t"
    730					LONG_L " %[src], %[rseq_scratch0]\n\t",
    731					error1)
    732		RSEQ_ASM_DEFINE_CMPFAIL(7,
    733					/* teardown */
    734					LONG_L " %[len], %[rseq_scratch2]\n\t"
    735					LONG_L " %[dst], %[rseq_scratch1]\n\t"
    736					LONG_L " %[src], %[rseq_scratch0]\n\t",
    737					error2)
    738#endif
    739		"8:\n\t"
    740		: /* gcc asm goto does not allow outputs */
    741		: [cpu_id]		"r" (cpu),
    742		  [current_cpu_id]	"m" (rseq_get_abi()->cpu_id),
    743		  [rseq_cs]		"m" (rseq_get_abi()->rseq_cs.arch.ptr),
    744		  /* final store input */
    745		  [v]			"m" (*v),
    746		  [expect]		"r" (expect),
    747		  [newv]		"r" (newv),
    748		  /* try memcpy input */
    749		  [dst]			"r" (dst),
    750		  [src]			"r" (src),
    751		  [len]			"r" (len),
    752		  [rseq_scratch0]	"m" (rseq_scratch[0]),
    753		  [rseq_scratch1]	"m" (rseq_scratch[1]),
    754		  [rseq_scratch2]	"m" (rseq_scratch[2])
    755		  RSEQ_INJECT_INPUT
    756		: "$4", "memory"
    757		  RSEQ_INJECT_CLOBBER
    758		: abort, cmpfail
    759#ifdef RSEQ_COMPARE_TWICE
    760		  , error1, error2
    761#endif
    762	);
    763	return 0;
    764abort:
    765	RSEQ_INJECT_FAILED
    766	return -1;
    767cmpfail:
    768	return 1;
    769#ifdef RSEQ_COMPARE_TWICE
    770error1:
    771	rseq_bug("cpu_id comparison failed");
    772error2:
    773	rseq_bug("expected value comparison failed");
    774#endif
    775}
    776
    777#endif /* !RSEQ_SKIP_FASTPATH */