crc32-mips.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * crc32-mips.c - CRC32 and CRC32C using optional MIPSr6 instructions
  4. *
  5. * Module based on arm64/crypto/crc32-arm.c
  6. *
  7. * Copyright (C) 2014 Linaro Ltd <yazen.ghannam@linaro.org>
  8. * Copyright (C) 2018 MIPS Tech, LLC
  9. */
  10. #include <linux/unaligned/access_ok.h>
  11. #include <linux/cpufeature.h>
  12. #include <linux/init.h>
  13. #include <linux/kernel.h>
  14. #include <linux/module.h>
  15. #include <linux/string.h>
  16. #include <asm/mipsregs.h>
  17. #include <crypto/internal/hash.h>
  18. enum crc_op_size {
  19. b, h, w, d,
  20. };
  21. enum crc_type {
  22. crc32,
  23. crc32c,
  24. };
  25. #ifndef TOOLCHAIN_SUPPORTS_CRC
  26. #define _ASM_MACRO_CRC32(OP, SZ, TYPE) \
  27. _ASM_MACRO_3R(OP, rt, rs, rt2, \
  28. ".ifnc \\rt, \\rt2\n\t" \
  29. ".error \"invalid operands \\\"" #OP " \\rt,\\rs,\\rt2\\\"\"\n\t" \
  30. ".endif\n\t" \
  31. _ASM_INSN_IF_MIPS(0x7c00000f | (__rt << 16) | (__rs << 21) | \
  32. ((SZ) << 6) | ((TYPE) << 8)) \
  33. _ASM_INSN32_IF_MM(0x00000030 | (__rs << 16) | (__rt << 21) | \
  34. ((SZ) << 14) | ((TYPE) << 3)))
  35. _ASM_MACRO_CRC32(crc32b, 0, 0);
  36. _ASM_MACRO_CRC32(crc32h, 1, 0);
  37. _ASM_MACRO_CRC32(crc32w, 2, 0);
  38. _ASM_MACRO_CRC32(crc32d, 3, 0);
  39. _ASM_MACRO_CRC32(crc32cb, 0, 1);
  40. _ASM_MACRO_CRC32(crc32ch, 1, 1);
  41. _ASM_MACRO_CRC32(crc32cw, 2, 1);
  42. _ASM_MACRO_CRC32(crc32cd, 3, 1);
  43. #define _ASM_SET_CRC ""
  44. #else /* !TOOLCHAIN_SUPPORTS_CRC */
  45. #define _ASM_SET_CRC ".set\tcrc\n\t"
  46. #endif
  47. #define _CRC32(crc, value, size, type) \
  48. do { \
  49. __asm__ __volatile__( \
  50. ".set push\n\t" \
  51. _ASM_SET_CRC \
  52. #type #size " %0, %1, %0\n\t" \
  53. ".set pop" \
  54. : "+r" (crc) \
  55. : "r" (value)); \
  56. } while (0)
  57. #define CRC32(crc, value, size) \
  58. _CRC32(crc, value, size, crc32)
  59. #define CRC32C(crc, value, size) \
  60. _CRC32(crc, value, size, crc32c)
  61. static u32 crc32_mips_le_hw(u32 crc_, const u8 *p, unsigned int len)
  62. {
  63. u32 crc = crc_;
  64. #ifdef CONFIG_64BIT
  65. while (len >= sizeof(u64)) {
  66. u64 value = get_unaligned_le64(p);
  67. CRC32(crc, value, d);
  68. p += sizeof(u64);
  69. len -= sizeof(u64);
  70. }
  71. if (len & sizeof(u32)) {
  72. #else /* !CONFIG_64BIT */
  73. while (len >= sizeof(u32)) {
  74. #endif
  75. u32 value = get_unaligned_le32(p);
  76. CRC32(crc, value, w);
  77. p += sizeof(u32);
  78. len -= sizeof(u32);
  79. }
  80. if (len & sizeof(u16)) {
  81. u16 value = get_unaligned_le16(p);
  82. CRC32(crc, value, h);
  83. p += sizeof(u16);
  84. }
  85. if (len & sizeof(u8)) {
  86. u8 value = *p++;
  87. CRC32(crc, value, b);
  88. }
  89. return crc;
  90. }
  91. static u32 crc32c_mips_le_hw(u32 crc_, const u8 *p, unsigned int len)
  92. {
  93. u32 crc = crc_;
  94. #ifdef CONFIG_64BIT
  95. while (len >= sizeof(u64)) {
  96. u64 value = get_unaligned_le64(p);
  97. CRC32C(crc, value, d);
  98. p += sizeof(u64);
  99. len -= sizeof(u64);
  100. }
  101. if (len & sizeof(u32)) {
  102. #else /* !CONFIG_64BIT */
  103. while (len >= sizeof(u32)) {
  104. #endif
  105. u32 value = get_unaligned_le32(p);
  106. CRC32C(crc, value, w);
  107. p += sizeof(u32);
  108. len -= sizeof(u32);
  109. }
  110. if (len & sizeof(u16)) {
  111. u16 value = get_unaligned_le16(p);
  112. CRC32C(crc, value, h);
  113. p += sizeof(u16);
  114. }
  115. if (len & sizeof(u8)) {
  116. u8 value = *p++;
  117. CRC32C(crc, value, b);
  118. }
  119. return crc;
  120. }
  121. #define CHKSUM_BLOCK_SIZE 1
  122. #define CHKSUM_DIGEST_SIZE 4
  123. struct chksum_ctx {
  124. u32 key;
  125. };
  126. struct chksum_desc_ctx {
  127. u32 crc;
  128. };
  129. static int chksum_init(struct shash_desc *desc)
  130. {
  131. struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
  132. struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
  133. ctx->crc = mctx->key;
  134. return 0;
  135. }
  136. /*
  137. * Setting the seed allows arbitrary accumulators and flexible XOR policy
  138. * If your algorithm starts with ~0, then XOR with ~0 before you set
  139. * the seed.
  140. */
  141. static int chksum_setkey(struct crypto_shash *tfm, const u8 *key,
  142. unsigned int keylen)
  143. {
  144. struct chksum_ctx *mctx = crypto_shash_ctx(tfm);
  145. if (keylen != sizeof(mctx->key)) {
  146. crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
  147. return -EINVAL;
  148. }
  149. mctx->key = get_unaligned_le32(key);
  150. return 0;
  151. }
  152. static int chksum_update(struct shash_desc *desc, const u8 *data,
  153. unsigned int length)
  154. {
  155. struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
  156. ctx->crc = crc32_mips_le_hw(ctx->crc, data, length);
  157. return 0;
  158. }
  159. static int chksumc_update(struct shash_desc *desc, const u8 *data,
  160. unsigned int length)
  161. {
  162. struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
  163. ctx->crc = crc32c_mips_le_hw(ctx->crc, data, length);
  164. return 0;
  165. }
  166. static int chksum_final(struct shash_desc *desc, u8 *out)
  167. {
  168. struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
  169. put_unaligned_le32(ctx->crc, out);
  170. return 0;
  171. }
  172. static int chksumc_final(struct shash_desc *desc, u8 *out)
  173. {
  174. struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
  175. put_unaligned_le32(~ctx->crc, out);
  176. return 0;
  177. }
  178. static int __chksum_finup(u32 crc, const u8 *data, unsigned int len, u8 *out)
  179. {
  180. put_unaligned_le32(crc32_mips_le_hw(crc, data, len), out);
  181. return 0;
  182. }
  183. static int __chksumc_finup(u32 crc, const u8 *data, unsigned int len, u8 *out)
  184. {
  185. put_unaligned_le32(~crc32c_mips_le_hw(crc, data, len), out);
  186. return 0;
  187. }
  188. static int chksum_finup(struct shash_desc *desc, const u8 *data,
  189. unsigned int len, u8 *out)
  190. {
  191. struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
  192. return __chksum_finup(ctx->crc, data, len, out);
  193. }
  194. static int chksumc_finup(struct shash_desc *desc, const u8 *data,
  195. unsigned int len, u8 *out)
  196. {
  197. struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
  198. return __chksumc_finup(ctx->crc, data, len, out);
  199. }
  200. static int chksum_digest(struct shash_desc *desc, const u8 *data,
  201. unsigned int length, u8 *out)
  202. {
  203. struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
  204. return __chksum_finup(mctx->key, data, length, out);
  205. }
  206. static int chksumc_digest(struct shash_desc *desc, const u8 *data,
  207. unsigned int length, u8 *out)
  208. {
  209. struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
  210. return __chksumc_finup(mctx->key, data, length, out);
  211. }
  212. static int chksum_cra_init(struct crypto_tfm *tfm)
  213. {
  214. struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
  215. mctx->key = ~0;
  216. return 0;
  217. }
  218. static struct shash_alg crc32_alg = {
  219. .digestsize = CHKSUM_DIGEST_SIZE,
  220. .setkey = chksum_setkey,
  221. .init = chksum_init,
  222. .update = chksum_update,
  223. .final = chksum_final,
  224. .finup = chksum_finup,
  225. .digest = chksum_digest,
  226. .descsize = sizeof(struct chksum_desc_ctx),
  227. .base = {
  228. .cra_name = "crc32",
  229. .cra_driver_name = "crc32-mips-hw",
  230. .cra_priority = 300,
  231. .cra_flags = CRYPTO_ALG_OPTIONAL_KEY,
  232. .cra_blocksize = CHKSUM_BLOCK_SIZE,
  233. .cra_alignmask = 0,
  234. .cra_ctxsize = sizeof(struct chksum_ctx),
  235. .cra_module = THIS_MODULE,
  236. .cra_init = chksum_cra_init,
  237. }
  238. };
  239. static struct shash_alg crc32c_alg = {
  240. .digestsize = CHKSUM_DIGEST_SIZE,
  241. .setkey = chksum_setkey,
  242. .init = chksum_init,
  243. .update = chksumc_update,
  244. .final = chksumc_final,
  245. .finup = chksumc_finup,
  246. .digest = chksumc_digest,
  247. .descsize = sizeof(struct chksum_desc_ctx),
  248. .base = {
  249. .cra_name = "crc32c",
  250. .cra_driver_name = "crc32c-mips-hw",
  251. .cra_priority = 300,
  252. .cra_flags = CRYPTO_ALG_OPTIONAL_KEY,
  253. .cra_blocksize = CHKSUM_BLOCK_SIZE,
  254. .cra_alignmask = 0,
  255. .cra_ctxsize = sizeof(struct chksum_ctx),
  256. .cra_module = THIS_MODULE,
  257. .cra_init = chksum_cra_init,
  258. }
  259. };
  260. static int __init crc32_mod_init(void)
  261. {
  262. int err;
  263. err = crypto_register_shash(&crc32_alg);
  264. if (err)
  265. return err;
  266. err = crypto_register_shash(&crc32c_alg);
  267. if (err) {
  268. crypto_unregister_shash(&crc32_alg);
  269. return err;
  270. }
  271. return 0;
  272. }
  273. static void __exit crc32_mod_exit(void)
  274. {
  275. crypto_unregister_shash(&crc32_alg);
  276. crypto_unregister_shash(&crc32c_alg);
  277. }
  278. MODULE_AUTHOR("Marcin Nowakowski <marcin.nowakowski@mips.com");
  279. MODULE_DESCRIPTION("CRC32 and CRC32C using optional MIPS instructions");
  280. MODULE_LICENSE("GPL v2");
  281. module_cpu_feature_match(MIPS_CRC32, crc32_mod_init);
  282. module_exit(crc32_mod_exit);