rseq-mips.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  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. #define RSEQ_SIG 0x53053053
  10. #define rseq_smp_mb() __asm__ __volatile__ ("sync" ::: "memory")
  11. #define rseq_smp_rmb() rseq_smp_mb()
  12. #define rseq_smp_wmb() rseq_smp_mb()
  13. #define rseq_smp_load_acquire(p) \
  14. __extension__ ({ \
  15. __typeof(*p) ____p1 = RSEQ_READ_ONCE(*p); \
  16. rseq_smp_mb(); \
  17. ____p1; \
  18. })
  19. #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb()
  20. #define rseq_smp_store_release(p, v) \
  21. do { \
  22. rseq_smp_mb(); \
  23. RSEQ_WRITE_ONCE(*p, v); \
  24. } while (0)
  25. #ifdef RSEQ_SKIP_FASTPATH
  26. #include "rseq-skip.h"
  27. #else /* !RSEQ_SKIP_FASTPATH */
  28. #if _MIPS_SZLONG == 64
  29. # define LONG ".dword"
  30. # define LONG_LA "dla"
  31. # define LONG_L "ld"
  32. # define LONG_S "sd"
  33. # define LONG_ADDI "daddiu"
  34. # define U32_U64_PAD(x) x
  35. #elif _MIPS_SZLONG == 32
  36. # define LONG ".word"
  37. # define LONG_LA "la"
  38. # define LONG_L "lw"
  39. # define LONG_S "sw"
  40. # define LONG_ADDI "addiu"
  41. # ifdef __BIG_ENDIAN
  42. # define U32_U64_PAD(x) "0x0, " x
  43. # else
  44. # define U32_U64_PAD(x) x ", 0x0"
  45. # endif
  46. #else
  47. # error unsupported _MIPS_SZLONG
  48. #endif
  49. #define __RSEQ_ASM_DEFINE_TABLE(version, flags, start_ip, \
  50. post_commit_offset, abort_ip) \
  51. ".pushsection __rseq_table, \"aw\"\n\t" \
  52. ".balign 32\n\t" \
  53. ".word " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
  54. LONG " " U32_U64_PAD(__rseq_str(start_ip)) "\n\t" \
  55. LONG " " U32_U64_PAD(__rseq_str(post_commit_offset)) "\n\t" \
  56. LONG " " U32_U64_PAD(__rseq_str(abort_ip)) "\n\t" \
  57. ".popsection\n\t"
  58. #define RSEQ_ASM_DEFINE_TABLE(start_ip, post_commit_ip, abort_ip) \
  59. __RSEQ_ASM_DEFINE_TABLE(0x0, 0x0, start_ip, \
  60. (post_commit_ip - start_ip), abort_ip)
  61. #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \
  62. RSEQ_INJECT_ASM(1) \
  63. LONG_LA " $4, " __rseq_str(cs_label) "\n\t" \
  64. LONG_S " $4, %[" __rseq_str(rseq_cs) "]\n\t" \
  65. __rseq_str(label) ":\n\t"
  66. #define RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, label) \
  67. RSEQ_INJECT_ASM(2) \
  68. "lw $4, %[" __rseq_str(current_cpu_id) "]\n\t" \
  69. "bne $4, %[" __rseq_str(cpu_id) "], " __rseq_str(label) "\n\t"
  70. #define __RSEQ_ASM_DEFINE_ABORT(table_label, label, teardown, \
  71. abort_label, version, flags, \
  72. start_ip, post_commit_offset, abort_ip) \
  73. ".balign 32\n\t" \
  74. __rseq_str(table_label) ":\n\t" \
  75. ".word " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
  76. LONG " " U32_U64_PAD(__rseq_str(start_ip)) "\n\t" \
  77. LONG " " U32_U64_PAD(__rseq_str(post_commit_offset)) "\n\t" \
  78. LONG " " U32_U64_PAD(__rseq_str(abort_ip)) "\n\t" \
  79. ".word " __rseq_str(RSEQ_SIG) "\n\t" \
  80. __rseq_str(label) ":\n\t" \
  81. teardown \
  82. "b %l[" __rseq_str(abort_label) "]\n\t"
  83. #define RSEQ_ASM_DEFINE_ABORT(table_label, label, teardown, abort_label, \
  84. start_ip, post_commit_ip, abort_ip) \
  85. __RSEQ_ASM_DEFINE_ABORT(table_label, label, teardown, \
  86. abort_label, 0x0, 0x0, start_ip, \
  87. (post_commit_ip - start_ip), abort_ip)
  88. #define RSEQ_ASM_DEFINE_CMPFAIL(label, teardown, cmpfail_label) \
  89. __rseq_str(label) ":\n\t" \
  90. teardown \
  91. "b %l[" __rseq_str(cmpfail_label) "]\n\t"
  92. #define rseq_workaround_gcc_asm_size_guess() __asm__ __volatile__("")
  93. static inline __attribute__((always_inline))
  94. int rseq_cmpeqv_storev(intptr_t *v, intptr_t expect, intptr_t newv, int cpu)
  95. {
  96. RSEQ_INJECT_C(9)
  97. rseq_workaround_gcc_asm_size_guess();
  98. __asm__ __volatile__ goto (
  99. RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
  100. /* Start rseq by storing table entry pointer into rseq_cs. */
  101. RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
  102. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
  103. RSEQ_INJECT_ASM(3)
  104. LONG_L " $4, %[v]\n\t"
  105. "bne $4, %[expect], %l[cmpfail]\n\t"
  106. RSEQ_INJECT_ASM(4)
  107. #ifdef RSEQ_COMPARE_TWICE
  108. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
  109. LONG_L " $4, %[v]\n\t"
  110. "bne $4, %[expect], %l[error2]\n\t"
  111. #endif
  112. /* final store */
  113. LONG_S " %[newv], %[v]\n\t"
  114. "2:\n\t"
  115. RSEQ_INJECT_ASM(5)
  116. "b 5f\n\t"
  117. RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
  118. "5:\n\t"
  119. : /* gcc asm goto does not allow outputs */
  120. : [cpu_id] "r" (cpu),
  121. [current_cpu_id] "m" (__rseq_abi.cpu_id),
  122. [rseq_cs] "m" (__rseq_abi.rseq_cs),
  123. [v] "m" (*v),
  124. [expect] "r" (expect),
  125. [newv] "r" (newv)
  126. RSEQ_INJECT_INPUT
  127. : "$4", "memory"
  128. RSEQ_INJECT_CLOBBER
  129. : abort, cmpfail
  130. #ifdef RSEQ_COMPARE_TWICE
  131. , error1, error2
  132. #endif
  133. );
  134. rseq_workaround_gcc_asm_size_guess();
  135. return 0;
  136. abort:
  137. rseq_workaround_gcc_asm_size_guess();
  138. RSEQ_INJECT_FAILED
  139. return -1;
  140. cmpfail:
  141. rseq_workaround_gcc_asm_size_guess();
  142. return 1;
  143. #ifdef RSEQ_COMPARE_TWICE
  144. error1:
  145. rseq_bug("cpu_id comparison failed");
  146. error2:
  147. rseq_bug("expected value comparison failed");
  148. #endif
  149. }
  150. static inline __attribute__((always_inline))
  151. int rseq_cmpnev_storeoffp_load(intptr_t *v, intptr_t expectnot,
  152. off_t voffp, intptr_t *load, int cpu)
  153. {
  154. RSEQ_INJECT_C(9)
  155. rseq_workaround_gcc_asm_size_guess();
  156. __asm__ __volatile__ goto (
  157. RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
  158. /* Start rseq by storing table entry pointer into rseq_cs. */
  159. RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
  160. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
  161. RSEQ_INJECT_ASM(3)
  162. LONG_L " $4, %[v]\n\t"
  163. "beq $4, %[expectnot], %l[cmpfail]\n\t"
  164. RSEQ_INJECT_ASM(4)
  165. #ifdef RSEQ_COMPARE_TWICE
  166. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
  167. LONG_L " $4, %[v]\n\t"
  168. "beq $4, %[expectnot], %l[error2]\n\t"
  169. #endif
  170. LONG_S " $4, %[load]\n\t"
  171. LONG_ADDI " $4, %[voffp]\n\t"
  172. LONG_L " $4, 0($4)\n\t"
  173. /* final store */
  174. LONG_S " $4, %[v]\n\t"
  175. "2:\n\t"
  176. RSEQ_INJECT_ASM(5)
  177. "b 5f\n\t"
  178. RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
  179. "5:\n\t"
  180. : /* gcc asm goto does not allow outputs */
  181. : [cpu_id] "r" (cpu),
  182. [current_cpu_id] "m" (__rseq_abi.cpu_id),
  183. [rseq_cs] "m" (__rseq_abi.rseq_cs),
  184. /* final store input */
  185. [v] "m" (*v),
  186. [expectnot] "r" (expectnot),
  187. [voffp] "Ir" (voffp),
  188. [load] "m" (*load)
  189. RSEQ_INJECT_INPUT
  190. : "$4", "memory"
  191. RSEQ_INJECT_CLOBBER
  192. : abort, cmpfail
  193. #ifdef RSEQ_COMPARE_TWICE
  194. , error1, error2
  195. #endif
  196. );
  197. rseq_workaround_gcc_asm_size_guess();
  198. return 0;
  199. abort:
  200. rseq_workaround_gcc_asm_size_guess();
  201. RSEQ_INJECT_FAILED
  202. return -1;
  203. cmpfail:
  204. rseq_workaround_gcc_asm_size_guess();
  205. return 1;
  206. #ifdef RSEQ_COMPARE_TWICE
  207. error1:
  208. rseq_bug("cpu_id comparison failed");
  209. error2:
  210. rseq_bug("expected value comparison failed");
  211. #endif
  212. }
  213. static inline __attribute__((always_inline))
  214. int rseq_addv(intptr_t *v, intptr_t count, int cpu)
  215. {
  216. RSEQ_INJECT_C(9)
  217. rseq_workaround_gcc_asm_size_guess();
  218. __asm__ __volatile__ goto (
  219. RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
  220. /* Start rseq by storing table entry pointer into rseq_cs. */
  221. RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
  222. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
  223. RSEQ_INJECT_ASM(3)
  224. #ifdef RSEQ_COMPARE_TWICE
  225. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
  226. #endif
  227. LONG_L " $4, %[v]\n\t"
  228. LONG_ADDI " $4, %[count]\n\t"
  229. /* final store */
  230. LONG_S " $4, %[v]\n\t"
  231. "2:\n\t"
  232. RSEQ_INJECT_ASM(4)
  233. "b 5f\n\t"
  234. RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
  235. "5:\n\t"
  236. : /* gcc asm goto does not allow outputs */
  237. : [cpu_id] "r" (cpu),
  238. [current_cpu_id] "m" (__rseq_abi.cpu_id),
  239. [rseq_cs] "m" (__rseq_abi.rseq_cs),
  240. [v] "m" (*v),
  241. [count] "Ir" (count)
  242. RSEQ_INJECT_INPUT
  243. : "$4", "memory"
  244. RSEQ_INJECT_CLOBBER
  245. : abort
  246. #ifdef RSEQ_COMPARE_TWICE
  247. , error1
  248. #endif
  249. );
  250. rseq_workaround_gcc_asm_size_guess();
  251. return 0;
  252. abort:
  253. rseq_workaround_gcc_asm_size_guess();
  254. RSEQ_INJECT_FAILED
  255. return -1;
  256. #ifdef RSEQ_COMPARE_TWICE
  257. error1:
  258. rseq_bug("cpu_id comparison failed");
  259. #endif
  260. }
  261. static inline __attribute__((always_inline))
  262. int rseq_cmpeqv_trystorev_storev(intptr_t *v, intptr_t expect,
  263. intptr_t *v2, intptr_t newv2,
  264. intptr_t newv, int cpu)
  265. {
  266. RSEQ_INJECT_C(9)
  267. rseq_workaround_gcc_asm_size_guess();
  268. __asm__ __volatile__ goto (
  269. RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
  270. /* Start rseq by storing table entry pointer into rseq_cs. */
  271. RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
  272. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
  273. RSEQ_INJECT_ASM(3)
  274. LONG_L " $4, %[v]\n\t"
  275. "bne $4, %[expect], %l[cmpfail]\n\t"
  276. RSEQ_INJECT_ASM(4)
  277. #ifdef RSEQ_COMPARE_TWICE
  278. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
  279. LONG_L " $4, %[v]\n\t"
  280. "bne $4, %[expect], %l[error2]\n\t"
  281. #endif
  282. /* try store */
  283. LONG_S " %[newv2], %[v2]\n\t"
  284. RSEQ_INJECT_ASM(5)
  285. /* final store */
  286. LONG_S " %[newv], %[v]\n\t"
  287. "2:\n\t"
  288. RSEQ_INJECT_ASM(6)
  289. "b 5f\n\t"
  290. RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
  291. "5:\n\t"
  292. : /* gcc asm goto does not allow outputs */
  293. : [cpu_id] "r" (cpu),
  294. [current_cpu_id] "m" (__rseq_abi.cpu_id),
  295. [rseq_cs] "m" (__rseq_abi.rseq_cs),
  296. /* try store input */
  297. [v2] "m" (*v2),
  298. [newv2] "r" (newv2),
  299. /* final store input */
  300. [v] "m" (*v),
  301. [expect] "r" (expect),
  302. [newv] "r" (newv)
  303. RSEQ_INJECT_INPUT
  304. : "$4", "memory"
  305. RSEQ_INJECT_CLOBBER
  306. : abort, cmpfail
  307. #ifdef RSEQ_COMPARE_TWICE
  308. , error1, error2
  309. #endif
  310. );
  311. rseq_workaround_gcc_asm_size_guess();
  312. return 0;
  313. abort:
  314. rseq_workaround_gcc_asm_size_guess();
  315. RSEQ_INJECT_FAILED
  316. return -1;
  317. cmpfail:
  318. rseq_workaround_gcc_asm_size_guess();
  319. return 1;
  320. #ifdef RSEQ_COMPARE_TWICE
  321. error1:
  322. rseq_bug("cpu_id comparison failed");
  323. error2:
  324. rseq_bug("expected value comparison failed");
  325. #endif
  326. }
  327. static inline __attribute__((always_inline))
  328. int rseq_cmpeqv_trystorev_storev_release(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. rseq_workaround_gcc_asm_size_guess();
  334. __asm__ __volatile__ goto (
  335. RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
  336. /* Start rseq by storing table entry pointer into rseq_cs. */
  337. RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
  338. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
  339. RSEQ_INJECT_ASM(3)
  340. LONG_L " $4, %[v]\n\t"
  341. "bne $4, %[expect], %l[cmpfail]\n\t"
  342. RSEQ_INJECT_ASM(4)
  343. #ifdef RSEQ_COMPARE_TWICE
  344. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
  345. LONG_L " $4, %[v]\n\t"
  346. "bne $4, %[expect], %l[error2]\n\t"
  347. #endif
  348. /* try store */
  349. LONG_S " %[newv2], %[v2]\n\t"
  350. RSEQ_INJECT_ASM(5)
  351. "sync\n\t" /* full sync provides store-release */
  352. /* final store */
  353. LONG_S " %[newv], %[v]\n\t"
  354. "2:\n\t"
  355. RSEQ_INJECT_ASM(6)
  356. "b 5f\n\t"
  357. RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
  358. "5:\n\t"
  359. : /* gcc asm goto does not allow outputs */
  360. : [cpu_id] "r" (cpu),
  361. [current_cpu_id] "m" (__rseq_abi.cpu_id),
  362. [rseq_cs] "m" (__rseq_abi.rseq_cs),
  363. /* try store input */
  364. [v2] "m" (*v2),
  365. [newv2] "r" (newv2),
  366. /* final store input */
  367. [v] "m" (*v),
  368. [expect] "r" (expect),
  369. [newv] "r" (newv)
  370. RSEQ_INJECT_INPUT
  371. : "$4", "memory"
  372. RSEQ_INJECT_CLOBBER
  373. : abort, cmpfail
  374. #ifdef RSEQ_COMPARE_TWICE
  375. , error1, error2
  376. #endif
  377. );
  378. rseq_workaround_gcc_asm_size_guess();
  379. return 0;
  380. abort:
  381. rseq_workaround_gcc_asm_size_guess();
  382. RSEQ_INJECT_FAILED
  383. return -1;
  384. cmpfail:
  385. rseq_workaround_gcc_asm_size_guess();
  386. return 1;
  387. #ifdef RSEQ_COMPARE_TWICE
  388. error1:
  389. rseq_bug("cpu_id comparison failed");
  390. error2:
  391. rseq_bug("expected value comparison failed");
  392. #endif
  393. }
  394. static inline __attribute__((always_inline))
  395. int rseq_cmpeqv_cmpeqv_storev(intptr_t *v, intptr_t expect,
  396. intptr_t *v2, intptr_t expect2,
  397. intptr_t newv, int cpu)
  398. {
  399. RSEQ_INJECT_C(9)
  400. rseq_workaround_gcc_asm_size_guess();
  401. __asm__ __volatile__ goto (
  402. RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
  403. /* Start rseq by storing table entry pointer into rseq_cs. */
  404. RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
  405. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
  406. RSEQ_INJECT_ASM(3)
  407. LONG_L " $4, %[v]\n\t"
  408. "bne $4, %[expect], %l[cmpfail]\n\t"
  409. RSEQ_INJECT_ASM(4)
  410. LONG_L " $4, %[v2]\n\t"
  411. "bne $4, %[expect2], %l[cmpfail]\n\t"
  412. RSEQ_INJECT_ASM(5)
  413. #ifdef RSEQ_COMPARE_TWICE
  414. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, %l[error1])
  415. LONG_L " $4, %[v]\n\t"
  416. "bne $4, %[expect], %l[error2]\n\t"
  417. LONG_L " $4, %[v2]\n\t"
  418. "bne $4, %[expect2], %l[error3]\n\t"
  419. #endif
  420. /* final store */
  421. LONG_S " %[newv], %[v]\n\t"
  422. "2:\n\t"
  423. RSEQ_INJECT_ASM(6)
  424. "b 5f\n\t"
  425. RSEQ_ASM_DEFINE_ABORT(3, 4, "", abort, 1b, 2b, 4f)
  426. "5:\n\t"
  427. : /* gcc asm goto does not allow outputs */
  428. : [cpu_id] "r" (cpu),
  429. [current_cpu_id] "m" (__rseq_abi.cpu_id),
  430. [rseq_cs] "m" (__rseq_abi.rseq_cs),
  431. /* cmp2 input */
  432. [v2] "m" (*v2),
  433. [expect2] "r" (expect2),
  434. /* final store input */
  435. [v] "m" (*v),
  436. [expect] "r" (expect),
  437. [newv] "r" (newv)
  438. RSEQ_INJECT_INPUT
  439. : "$4", "memory"
  440. RSEQ_INJECT_CLOBBER
  441. : abort, cmpfail
  442. #ifdef RSEQ_COMPARE_TWICE
  443. , error1, error2, error3
  444. #endif
  445. );
  446. rseq_workaround_gcc_asm_size_guess();
  447. return 0;
  448. abort:
  449. rseq_workaround_gcc_asm_size_guess();
  450. RSEQ_INJECT_FAILED
  451. return -1;
  452. cmpfail:
  453. rseq_workaround_gcc_asm_size_guess();
  454. return 1;
  455. #ifdef RSEQ_COMPARE_TWICE
  456. error1:
  457. rseq_bug("cpu_id comparison failed");
  458. error2:
  459. rseq_bug("1st expected value comparison failed");
  460. error3:
  461. rseq_bug("2nd expected value comparison failed");
  462. #endif
  463. }
  464. static inline __attribute__((always_inline))
  465. int rseq_cmpeqv_trymemcpy_storev(intptr_t *v, intptr_t expect,
  466. void *dst, void *src, size_t len,
  467. intptr_t newv, int cpu)
  468. {
  469. uintptr_t rseq_scratch[3];
  470. RSEQ_INJECT_C(9)
  471. rseq_workaround_gcc_asm_size_guess();
  472. __asm__ __volatile__ goto (
  473. RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
  474. LONG_S " %[src], %[rseq_scratch0]\n\t"
  475. LONG_S " %[dst], %[rseq_scratch1]\n\t"
  476. LONG_S " %[len], %[rseq_scratch2]\n\t"
  477. /* Start rseq by storing table entry pointer into rseq_cs. */
  478. RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
  479. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
  480. RSEQ_INJECT_ASM(3)
  481. LONG_L " $4, %[v]\n\t"
  482. "bne $4, %[expect], 5f\n\t"
  483. RSEQ_INJECT_ASM(4)
  484. #ifdef RSEQ_COMPARE_TWICE
  485. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
  486. LONG_L " $4, %[v]\n\t"
  487. "bne $4, %[expect], 7f\n\t"
  488. #endif
  489. /* try memcpy */
  490. "beqz %[len], 333f\n\t" \
  491. "222:\n\t" \
  492. "lb $4, 0(%[src])\n\t" \
  493. "sb $4, 0(%[dst])\n\t" \
  494. LONG_ADDI " %[src], 1\n\t" \
  495. LONG_ADDI " %[dst], 1\n\t" \
  496. LONG_ADDI " %[len], -1\n\t" \
  497. "bnez %[len], 222b\n\t" \
  498. "333:\n\t" \
  499. RSEQ_INJECT_ASM(5)
  500. /* final store */
  501. LONG_S " %[newv], %[v]\n\t"
  502. "2:\n\t"
  503. RSEQ_INJECT_ASM(6)
  504. /* teardown */
  505. LONG_L " %[len], %[rseq_scratch2]\n\t"
  506. LONG_L " %[dst], %[rseq_scratch1]\n\t"
  507. LONG_L " %[src], %[rseq_scratch0]\n\t"
  508. "b 8f\n\t"
  509. RSEQ_ASM_DEFINE_ABORT(3, 4,
  510. /* teardown */
  511. LONG_L " %[len], %[rseq_scratch2]\n\t"
  512. LONG_L " %[dst], %[rseq_scratch1]\n\t"
  513. LONG_L " %[src], %[rseq_scratch0]\n\t",
  514. abort, 1b, 2b, 4f)
  515. RSEQ_ASM_DEFINE_CMPFAIL(5,
  516. /* teardown */
  517. LONG_L " %[len], %[rseq_scratch2]\n\t"
  518. LONG_L " %[dst], %[rseq_scratch1]\n\t"
  519. LONG_L " %[src], %[rseq_scratch0]\n\t",
  520. cmpfail)
  521. #ifdef RSEQ_COMPARE_TWICE
  522. RSEQ_ASM_DEFINE_CMPFAIL(6,
  523. /* teardown */
  524. LONG_L " %[len], %[rseq_scratch2]\n\t"
  525. LONG_L " %[dst], %[rseq_scratch1]\n\t"
  526. LONG_L " %[src], %[rseq_scratch0]\n\t",
  527. error1)
  528. RSEQ_ASM_DEFINE_CMPFAIL(7,
  529. /* teardown */
  530. LONG_L " %[len], %[rseq_scratch2]\n\t"
  531. LONG_L " %[dst], %[rseq_scratch1]\n\t"
  532. LONG_L " %[src], %[rseq_scratch0]\n\t",
  533. error2)
  534. #endif
  535. "8:\n\t"
  536. : /* gcc asm goto does not allow outputs */
  537. : [cpu_id] "r" (cpu),
  538. [current_cpu_id] "m" (__rseq_abi.cpu_id),
  539. [rseq_cs] "m" (__rseq_abi.rseq_cs),
  540. /* final store input */
  541. [v] "m" (*v),
  542. [expect] "r" (expect),
  543. [newv] "r" (newv),
  544. /* try memcpy input */
  545. [dst] "r" (dst),
  546. [src] "r" (src),
  547. [len] "r" (len),
  548. [rseq_scratch0] "m" (rseq_scratch[0]),
  549. [rseq_scratch1] "m" (rseq_scratch[1]),
  550. [rseq_scratch2] "m" (rseq_scratch[2])
  551. RSEQ_INJECT_INPUT
  552. : "$4", "memory"
  553. RSEQ_INJECT_CLOBBER
  554. : abort, cmpfail
  555. #ifdef RSEQ_COMPARE_TWICE
  556. , error1, error2
  557. #endif
  558. );
  559. rseq_workaround_gcc_asm_size_guess();
  560. return 0;
  561. abort:
  562. rseq_workaround_gcc_asm_size_guess();
  563. RSEQ_INJECT_FAILED
  564. return -1;
  565. cmpfail:
  566. rseq_workaround_gcc_asm_size_guess();
  567. return 1;
  568. #ifdef RSEQ_COMPARE_TWICE
  569. error1:
  570. rseq_workaround_gcc_asm_size_guess();
  571. rseq_bug("cpu_id comparison failed");
  572. error2:
  573. rseq_workaround_gcc_asm_size_guess();
  574. rseq_bug("expected value comparison failed");
  575. #endif
  576. }
  577. static inline __attribute__((always_inline))
  578. int rseq_cmpeqv_trymemcpy_storev_release(intptr_t *v, intptr_t expect,
  579. void *dst, void *src, size_t len,
  580. intptr_t newv, int cpu)
  581. {
  582. uintptr_t rseq_scratch[3];
  583. RSEQ_INJECT_C(9)
  584. rseq_workaround_gcc_asm_size_guess();
  585. __asm__ __volatile__ goto (
  586. RSEQ_ASM_DEFINE_TABLE(1f, 2f, 4f) /* start, commit, abort */
  587. LONG_S " %[src], %[rseq_scratch0]\n\t"
  588. LONG_S " %[dst], %[rseq_scratch1]\n\t"
  589. LONG_S " %[len], %[rseq_scratch2]\n\t"
  590. /* Start rseq by storing table entry pointer into rseq_cs. */
  591. RSEQ_ASM_STORE_RSEQ_CS(1, 3f, rseq_cs)
  592. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 4f)
  593. RSEQ_INJECT_ASM(3)
  594. LONG_L " $4, %[v]\n\t"
  595. "bne $4, %[expect], 5f\n\t"
  596. RSEQ_INJECT_ASM(4)
  597. #ifdef RSEQ_COMPARE_TWICE
  598. RSEQ_ASM_CMP_CPU_ID(cpu_id, current_cpu_id, 6f)
  599. LONG_L " $4, %[v]\n\t"
  600. "bne $4, %[expect], 7f\n\t"
  601. #endif
  602. /* try memcpy */
  603. "beqz %[len], 333f\n\t" \
  604. "222:\n\t" \
  605. "lb $4, 0(%[src])\n\t" \
  606. "sb $4, 0(%[dst])\n\t" \
  607. LONG_ADDI " %[src], 1\n\t" \
  608. LONG_ADDI " %[dst], 1\n\t" \
  609. LONG_ADDI " %[len], -1\n\t" \
  610. "bnez %[len], 222b\n\t" \
  611. "333:\n\t" \
  612. RSEQ_INJECT_ASM(5)
  613. "sync\n\t" /* full sync provides store-release */
  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;
  675. abort:
  676. rseq_workaround_gcc_asm_size_guess();
  677. RSEQ_INJECT_FAILED
  678. return -1;
  679. cmpfail:
  680. rseq_workaround_gcc_asm_size_guess();
  681. return 1;
  682. #ifdef RSEQ_COMPARE_TWICE
  683. error1:
  684. rseq_workaround_gcc_asm_size_guess();
  685. rseq_bug("cpu_id comparison failed");
  686. error2:
  687. rseq_workaround_gcc_asm_size_guess();
  688. rseq_bug("expected value comparison failed");
  689. #endif
  690. }
  691. #endif /* !RSEQ_SKIP_FASTPATH */