chacha-scalar-core.S 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. /*
  3. * Copyright (C) 2018 Google, Inc.
  4. */
  5. #include <linux/linkage.h>
  6. #include <asm/assembler.h>
  7. /*
  8. * Design notes:
  9. *
  10. * 16 registers would be needed to hold the state matrix, but only 14 are
  11. * available because 'sp' and 'pc' cannot be used. So we spill the elements
  12. * (x8, x9) to the stack and swap them out with (x10, x11). This adds one
  13. * 'ldrd' and one 'strd' instruction per round.
  14. *
  15. * All rotates are performed using the implicit rotate operand accepted by the
  16. * 'add' and 'eor' instructions. This is faster than using explicit rotate
  17. * instructions. To make this work, we allow the values in the second and last
  18. * rows of the ChaCha state matrix (rows 'b' and 'd') to temporarily have the
  19. * wrong rotation amount. The rotation amount is then fixed up just in time
  20. * when the values are used. 'brot' is the number of bits the values in row 'b'
  21. * need to be rotated right to arrive at the correct values, and 'drot'
  22. * similarly for row 'd'. (brot, drot) start out as (0, 0) but we make it such
  23. * that they end up as (25, 24) after every round.
  24. */
  25. // ChaCha state registers
  26. X0 .req r0
  27. X1 .req r1
  28. X2 .req r2
  29. X3 .req r3
  30. X4 .req r4
  31. X5 .req r5
  32. X6 .req r6
  33. X7 .req r7
  34. X8_X10 .req r8 // shared by x8 and x10
  35. X9_X11 .req r9 // shared by x9 and x11
  36. X12 .req r10
  37. X13 .req r11
  38. X14 .req r12
  39. X15 .req r14
  40. .macro _le32_bswap_4x a, b, c, d, tmp
  41. #ifdef __ARMEB__
  42. rev_l \a, \tmp
  43. rev_l \b, \tmp
  44. rev_l \c, \tmp
  45. rev_l \d, \tmp
  46. #endif
  47. .endm
  48. .macro __ldrd a, b, src, offset
  49. #if __LINUX_ARM_ARCH__ >= 6
  50. ldrd \a, \b, [\src, #\offset]
  51. #else
  52. ldr \a, [\src, #\offset]
  53. ldr \b, [\src, #\offset + 4]
  54. #endif
  55. .endm
  56. .macro __strd a, b, dst, offset
  57. #if __LINUX_ARM_ARCH__ >= 6
  58. strd \a, \b, [\dst, #\offset]
  59. #else
  60. str \a, [\dst, #\offset]
  61. str \b, [\dst, #\offset + 4]
  62. #endif
  63. .endm
  64. .macro _halfround a1, b1, c1, d1, a2, b2, c2, d2
  65. // a += b; d ^= a; d = rol(d, 16);
  66. add \a1, \a1, \b1, ror #brot
  67. add \a2, \a2, \b2, ror #brot
  68. eor \d1, \a1, \d1, ror #drot
  69. eor \d2, \a2, \d2, ror #drot
  70. // drot == 32 - 16 == 16
  71. // c += d; b ^= c; b = rol(b, 12);
  72. add \c1, \c1, \d1, ror #16
  73. add \c2, \c2, \d2, ror #16
  74. eor \b1, \c1, \b1, ror #brot
  75. eor \b2, \c2, \b2, ror #brot
  76. // brot == 32 - 12 == 20
  77. // a += b; d ^= a; d = rol(d, 8);
  78. add \a1, \a1, \b1, ror #20
  79. add \a2, \a2, \b2, ror #20
  80. eor \d1, \a1, \d1, ror #16
  81. eor \d2, \a2, \d2, ror #16
  82. // drot == 32 - 8 == 24
  83. // c += d; b ^= c; b = rol(b, 7);
  84. add \c1, \c1, \d1, ror #24
  85. add \c2, \c2, \d2, ror #24
  86. eor \b1, \c1, \b1, ror #20
  87. eor \b2, \c2, \b2, ror #20
  88. // brot == 32 - 7 == 25
  89. .endm
  90. .macro _doubleround
  91. // column round
  92. // quarterrounds: (x0, x4, x8, x12) and (x1, x5, x9, x13)
  93. _halfround X0, X4, X8_X10, X12, X1, X5, X9_X11, X13
  94. // save (x8, x9); restore (x10, x11)
  95. __strd X8_X10, X9_X11, sp, 0
  96. __ldrd X8_X10, X9_X11, sp, 8
  97. // quarterrounds: (x2, x6, x10, x14) and (x3, x7, x11, x15)
  98. _halfround X2, X6, X8_X10, X14, X3, X7, X9_X11, X15
  99. .set brot, 25
  100. .set drot, 24
  101. // diagonal round
  102. // quarterrounds: (x0, x5, x10, x15) and (x1, x6, x11, x12)
  103. _halfround X0, X5, X8_X10, X15, X1, X6, X9_X11, X12
  104. // save (x10, x11); restore (x8, x9)
  105. __strd X8_X10, X9_X11, sp, 8
  106. __ldrd X8_X10, X9_X11, sp, 0
  107. // quarterrounds: (x2, x7, x8, x13) and (x3, x4, x9, x14)
  108. _halfround X2, X7, X8_X10, X13, X3, X4, X9_X11, X14
  109. .endm
  110. .macro _chacha_permute nrounds
  111. .set brot, 0
  112. .set drot, 0
  113. .rept \nrounds / 2
  114. _doubleround
  115. .endr
  116. .endm
  117. .macro _chacha nrounds
  118. .Lnext_block\@:
  119. // Stack: unused0-unused1 x10-x11 x0-x15 OUT IN LEN
  120. // Registers contain x0-x9,x12-x15.
  121. // Do the core ChaCha permutation to update x0-x15.
  122. _chacha_permute \nrounds
  123. add sp, #8
  124. // Stack: x10-x11 orig_x0-orig_x15 OUT IN LEN
  125. // Registers contain x0-x9,x12-x15.
  126. // x4-x7 are rotated by 'brot'; x12-x15 are rotated by 'drot'.
  127. // Free up some registers (r8-r12,r14) by pushing (x8-x9,x12-x15).
  128. push {X8_X10, X9_X11, X12, X13, X14, X15}
  129. // Load (OUT, IN, LEN).
  130. ldr r14, [sp, #96]
  131. ldr r12, [sp, #100]
  132. ldr r11, [sp, #104]
  133. orr r10, r14, r12
  134. // Use slow path if fewer than 64 bytes remain.
  135. cmp r11, #64
  136. blt .Lxor_slowpath\@
  137. // Use slow path if IN and/or OUT isn't 4-byte aligned. Needed even on
  138. // ARMv6+, since ldmia and stmia (used below) still require alignment.
  139. tst r10, #3
  140. bne .Lxor_slowpath\@
  141. // Fast path: XOR 64 bytes of aligned data.
  142. // Stack: x8-x9 x12-x15 x10-x11 orig_x0-orig_x15 OUT IN LEN
  143. // Registers: r0-r7 are x0-x7; r8-r11 are free; r12 is IN; r14 is OUT.
  144. // x4-x7 are rotated by 'brot'; x12-x15 are rotated by 'drot'.
  145. // x0-x3
  146. __ldrd r8, r9, sp, 32
  147. __ldrd r10, r11, sp, 40
  148. add X0, X0, r8
  149. add X1, X1, r9
  150. add X2, X2, r10
  151. add X3, X3, r11
  152. _le32_bswap_4x X0, X1, X2, X3, r8
  153. ldmia r12!, {r8-r11}
  154. eor X0, X0, r8
  155. eor X1, X1, r9
  156. eor X2, X2, r10
  157. eor X3, X3, r11
  158. stmia r14!, {X0-X3}
  159. // x4-x7
  160. __ldrd r8, r9, sp, 48
  161. __ldrd r10, r11, sp, 56
  162. add X4, r8, X4, ror #brot
  163. add X5, r9, X5, ror #brot
  164. ldmia r12!, {X0-X3}
  165. add X6, r10, X6, ror #brot
  166. add X7, r11, X7, ror #brot
  167. _le32_bswap_4x X4, X5, X6, X7, r8
  168. eor X4, X4, X0
  169. eor X5, X5, X1
  170. eor X6, X6, X2
  171. eor X7, X7, X3
  172. stmia r14!, {X4-X7}
  173. // x8-x15
  174. pop {r0-r7} // (x8-x9,x12-x15,x10-x11)
  175. __ldrd r8, r9, sp, 32
  176. __ldrd r10, r11, sp, 40
  177. add r0, r0, r8 // x8
  178. add r1, r1, r9 // x9
  179. add r6, r6, r10 // x10
  180. add r7, r7, r11 // x11
  181. _le32_bswap_4x r0, r1, r6, r7, r8
  182. ldmia r12!, {r8-r11}
  183. eor r0, r0, r8 // x8
  184. eor r1, r1, r9 // x9
  185. eor r6, r6, r10 // x10
  186. eor r7, r7, r11 // x11
  187. stmia r14!, {r0,r1,r6,r7}
  188. ldmia r12!, {r0,r1,r6,r7}
  189. __ldrd r8, r9, sp, 48
  190. __ldrd r10, r11, sp, 56
  191. add r2, r8, r2, ror #drot // x12
  192. add r3, r9, r3, ror #drot // x13
  193. add r4, r10, r4, ror #drot // x14
  194. add r5, r11, r5, ror #drot // x15
  195. _le32_bswap_4x r2, r3, r4, r5, r9
  196. ldr r9, [sp, #72] // load LEN
  197. eor r2, r2, r0 // x12
  198. eor r3, r3, r1 // x13
  199. eor r4, r4, r6 // x14
  200. eor r5, r5, r7 // x15
  201. subs r9, #64 // decrement and check LEN
  202. stmia r14!, {r2-r5}
  203. beq .Ldone\@
  204. .Lprepare_for_next_block\@:
  205. // Stack: x0-x15 OUT IN LEN
  206. // Increment block counter (x12)
  207. add r8, #1
  208. // Store updated (OUT, IN, LEN)
  209. str r14, [sp, #64]
  210. str r12, [sp, #68]
  211. str r9, [sp, #72]
  212. mov r14, sp
  213. // Store updated block counter (x12)
  214. str r8, [sp, #48]
  215. sub sp, #16
  216. // Reload state and do next block
  217. ldmia r14!, {r0-r11} // load x0-x11
  218. __strd r10, r11, sp, 8 // store x10-x11 before state
  219. ldmia r14, {r10-r12,r14} // load x12-x15
  220. b .Lnext_block\@
  221. .Lxor_slowpath\@:
  222. // Slow path: < 64 bytes remaining, or unaligned input or output buffer.
  223. // We handle it by storing the 64 bytes of keystream to the stack, then
  224. // XOR-ing the needed portion with the data.
  225. // Allocate keystream buffer
  226. sub sp, #64
  227. mov r14, sp
  228. // Stack: ks0-ks15 x8-x9 x12-x15 x10-x11 orig_x0-orig_x15 OUT IN LEN
  229. // Registers: r0-r7 are x0-x7; r8-r11 are free; r12 is IN; r14 is &ks0.
  230. // x4-x7 are rotated by 'brot'; x12-x15 are rotated by 'drot'.
  231. // Save keystream for x0-x3
  232. __ldrd r8, r9, sp, 96
  233. __ldrd r10, r11, sp, 104
  234. add X0, X0, r8
  235. add X1, X1, r9
  236. add X2, X2, r10
  237. add X3, X3, r11
  238. _le32_bswap_4x X0, X1, X2, X3, r8
  239. stmia r14!, {X0-X3}
  240. // Save keystream for x4-x7
  241. __ldrd r8, r9, sp, 112
  242. __ldrd r10, r11, sp, 120
  243. add X4, r8, X4, ror #brot
  244. add X5, r9, X5, ror #brot
  245. add X6, r10, X6, ror #brot
  246. add X7, r11, X7, ror #brot
  247. _le32_bswap_4x X4, X5, X6, X7, r8
  248. add r8, sp, #64
  249. stmia r14!, {X4-X7}
  250. // Save keystream for x8-x15
  251. ldm r8, {r0-r7} // (x8-x9,x12-x15,x10-x11)
  252. __ldrd r8, r9, sp, 128
  253. __ldrd r10, r11, sp, 136
  254. add r0, r0, r8 // x8
  255. add r1, r1, r9 // x9
  256. add r6, r6, r10 // x10
  257. add r7, r7, r11 // x11
  258. _le32_bswap_4x r0, r1, r6, r7, r8
  259. stmia r14!, {r0,r1,r6,r7}
  260. __ldrd r8, r9, sp, 144
  261. __ldrd r10, r11, sp, 152
  262. add r2, r8, r2, ror #drot // x12
  263. add r3, r9, r3, ror #drot // x13
  264. add r4, r10, r4, ror #drot // x14
  265. add r5, r11, r5, ror #drot // x15
  266. _le32_bswap_4x r2, r3, r4, r5, r9
  267. stmia r14, {r2-r5}
  268. // Stack: ks0-ks15 unused0-unused7 x0-x15 OUT IN LEN
  269. // Registers: r8 is block counter, r12 is IN.
  270. ldr r9, [sp, #168] // LEN
  271. ldr r14, [sp, #160] // OUT
  272. cmp r9, #64
  273. mov r0, sp
  274. movle r1, r9
  275. movgt r1, #64
  276. // r1 is number of bytes to XOR, in range [1, 64]
  277. .if __LINUX_ARM_ARCH__ < 6
  278. orr r2, r12, r14
  279. tst r2, #3 // IN or OUT misaligned?
  280. bne .Lxor_next_byte\@
  281. .endif
  282. // XOR a word at a time
  283. .rept 16
  284. subs r1, #4
  285. blt .Lxor_words_done\@
  286. ldr r2, [r12], #4
  287. ldr r3, [r0], #4
  288. eor r2, r2, r3
  289. str r2, [r14], #4
  290. .endr
  291. b .Lxor_slowpath_done\@
  292. .Lxor_words_done\@:
  293. ands r1, r1, #3
  294. beq .Lxor_slowpath_done\@
  295. // XOR a byte at a time
  296. .Lxor_next_byte\@:
  297. ldrb r2, [r12], #1
  298. ldrb r3, [r0], #1
  299. eor r2, r2, r3
  300. strb r2, [r14], #1
  301. subs r1, #1
  302. bne .Lxor_next_byte\@
  303. .Lxor_slowpath_done\@:
  304. subs r9, #64
  305. add sp, #96
  306. bgt .Lprepare_for_next_block\@
  307. .Ldone\@:
  308. .endm // _chacha
  309. /*
  310. * void chacha_doarm(u8 *dst, const u8 *src, unsigned int bytes,
  311. * const struct chacha_state *state, int nrounds);
  312. */
  313. ENTRY(chacha_doarm)
  314. cmp r2, #0 // len == 0?
  315. reteq lr
  316. ldr ip, [sp]
  317. cmp ip, #12
  318. push {r0-r2,r4-r11,lr}
  319. // Push state x0-x15 onto stack.
  320. // Also store an extra copy of x10-x11 just before the state.
  321. add X12, r3, #48
  322. ldm X12, {X12,X13,X14,X15}
  323. push {X12,X13,X14,X15}
  324. sub sp, sp, #64
  325. __ldrd X8_X10, X9_X11, r3, 40
  326. __strd X8_X10, X9_X11, sp, 8
  327. __strd X8_X10, X9_X11, sp, 56
  328. ldm r3, {X0-X9_X11}
  329. __strd X0, X1, sp, 16
  330. __strd X2, X3, sp, 24
  331. __strd X4, X5, sp, 32
  332. __strd X6, X7, sp, 40
  333. __strd X8_X10, X9_X11, sp, 48
  334. beq 1f
  335. _chacha 20
  336. 0: add sp, #76
  337. pop {r4-r11, pc}
  338. 1: _chacha 12
  339. b 0b
  340. ENDPROC(chacha_doarm)
  341. /*
  342. * void hchacha_block_arm(const struct chacha_state *state,
  343. * u32 out[HCHACHA_OUT_WORDS], int nrounds);
  344. */
  345. ENTRY(hchacha_block_arm)
  346. push {r1,r4-r11,lr}
  347. cmp r2, #12 // ChaCha12 ?
  348. mov r14, r0
  349. ldmia r14!, {r0-r11} // load x0-x11
  350. push {r10-r11} // store x10-x11 to stack
  351. ldm r14, {r10-r12,r14} // load x12-x15
  352. sub sp, #8
  353. beq 1f
  354. _chacha_permute 20
  355. // Skip over (unused0-unused1, x10-x11)
  356. 0: add sp, #16
  357. // Fix up rotations of x12-x15
  358. ror X12, X12, #drot
  359. ror X13, X13, #drot
  360. pop {r4} // load 'out'
  361. ror X14, X14, #drot
  362. ror X15, X15, #drot
  363. // Store (x0-x3,x12-x15) to 'out'
  364. stm r4, {X0,X1,X2,X3,X12,X13,X14,X15}
  365. pop {r4-r11,pc}
  366. 1: _chacha_permute 12
  367. b 0b
  368. ENDPROC(hchacha_block_arm)