rseq-arm.h 19 KB

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