1 // SPDX-License-Identifier: GPL-2.0
3 * crc32.c - CRC32 and CRC32C using LoongArch crc* instructions
5 * Module based on mips/crypto/crc32-mips.c
7 * Copyright (C) 2014 Linaro Ltd <yazen.ghannam@linaro.org>
8 * Copyright (C) 2018 MIPS Tech, LLC
9 * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
12 #include <linux/module.h>
13 #include <crypto/internal/hash.h>
15 #include <asm/cpu-features.h>
16 #include <linux/unaligned.h>
18 #define _CRC32(crc, value, size, type) \
20 __asm__ __volatile__( \
21 #type ".w." #size ".w" " %0, %1, %0\n\t"\
27 #define CRC32(crc, value, size) _CRC32(crc, value, size, crc)
28 #define CRC32C(crc, value, size) _CRC32(crc, value, size, crcc)
30 static u32
crc32_loongarch_hw(u32 crc_
, const u8
*p
, unsigned int len
)
34 while (len
>= sizeof(u64
)) {
35 u64 value
= get_unaligned_le64(p
);
42 if (len
& sizeof(u32
)) {
43 u32 value
= get_unaligned_le32(p
);
49 if (len
& sizeof(u16
)) {
50 u16 value
= get_unaligned_le16(p
);
56 if (len
& sizeof(u8
)) {
65 static u32
crc32c_loongarch_hw(u32 crc_
, const u8
*p
, unsigned int len
)
69 while (len
>= sizeof(u64
)) {
70 u64 value
= get_unaligned_le64(p
);
72 CRC32C(crc
, value
, d
);
77 if (len
& sizeof(u32
)) {
78 u32 value
= get_unaligned_le32(p
);
80 CRC32C(crc
, value
, w
);
84 if (len
& sizeof(u16
)) {
85 u16 value
= get_unaligned_le16(p
);
87 CRC32C(crc
, value
, h
);
91 if (len
& sizeof(u8
)) {
94 CRC32C(crc
, value
, b
);
100 #define CHKSUM_BLOCK_SIZE 1
101 #define CHKSUM_DIGEST_SIZE 4
107 struct chksum_desc_ctx
{
111 static int chksum_init(struct shash_desc
*desc
)
113 struct chksum_ctx
*mctx
= crypto_shash_ctx(desc
->tfm
);
114 struct chksum_desc_ctx
*ctx
= shash_desc_ctx(desc
);
116 ctx
->crc
= mctx
->key
;
122 * Setting the seed allows arbitrary accumulators and flexible XOR policy
123 * If your algorithm starts with ~0, then XOR with ~0 before you set the seed.
125 static int chksum_setkey(struct crypto_shash
*tfm
, const u8
*key
, unsigned int keylen
)
127 struct chksum_ctx
*mctx
= crypto_shash_ctx(tfm
);
129 if (keylen
!= sizeof(mctx
->key
))
132 mctx
->key
= get_unaligned_le32(key
);
137 static int chksum_update(struct shash_desc
*desc
, const u8
*data
, unsigned int length
)
139 struct chksum_desc_ctx
*ctx
= shash_desc_ctx(desc
);
141 ctx
->crc
= crc32_loongarch_hw(ctx
->crc
, data
, length
);
145 static int chksumc_update(struct shash_desc
*desc
, const u8
*data
, unsigned int length
)
147 struct chksum_desc_ctx
*ctx
= shash_desc_ctx(desc
);
149 ctx
->crc
= crc32c_loongarch_hw(ctx
->crc
, data
, length
);
153 static int chksum_final(struct shash_desc
*desc
, u8
*out
)
155 struct chksum_desc_ctx
*ctx
= shash_desc_ctx(desc
);
157 put_unaligned_le32(ctx
->crc
, out
);
161 static int chksumc_final(struct shash_desc
*desc
, u8
*out
)
163 struct chksum_desc_ctx
*ctx
= shash_desc_ctx(desc
);
165 put_unaligned_le32(~ctx
->crc
, out
);
169 static int __chksum_finup(u32 crc
, const u8
*data
, unsigned int len
, u8
*out
)
171 put_unaligned_le32(crc32_loongarch_hw(crc
, data
, len
), out
);
175 static int __chksumc_finup(u32 crc
, const u8
*data
, unsigned int len
, u8
*out
)
177 put_unaligned_le32(~crc32c_loongarch_hw(crc
, data
, len
), out
);
181 static int chksum_finup(struct shash_desc
*desc
, const u8
*data
, unsigned int len
, u8
*out
)
183 struct chksum_desc_ctx
*ctx
= shash_desc_ctx(desc
);
185 return __chksum_finup(ctx
->crc
, data
, len
, out
);
188 static int chksumc_finup(struct shash_desc
*desc
, const u8
*data
, unsigned int len
, u8
*out
)
190 struct chksum_desc_ctx
*ctx
= shash_desc_ctx(desc
);
192 return __chksumc_finup(ctx
->crc
, data
, len
, out
);
195 static int chksum_digest(struct shash_desc
*desc
, const u8
*data
, unsigned int length
, u8
*out
)
197 struct chksum_ctx
*mctx
= crypto_shash_ctx(desc
->tfm
);
199 return __chksum_finup(mctx
->key
, data
, length
, out
);
202 static int chksumc_digest(struct shash_desc
*desc
, const u8
*data
, unsigned int length
, u8
*out
)
204 struct chksum_ctx
*mctx
= crypto_shash_ctx(desc
->tfm
);
206 return __chksumc_finup(mctx
->key
, data
, length
, out
);
209 static int chksum_cra_init(struct crypto_tfm
*tfm
)
211 struct chksum_ctx
*mctx
= crypto_tfm_ctx(tfm
);
217 static int chksumc_cra_init(struct crypto_tfm
*tfm
)
219 struct chksum_ctx
*mctx
= crypto_tfm_ctx(tfm
);
225 static struct shash_alg crc32_alg
= {
226 .digestsize
= CHKSUM_DIGEST_SIZE
,
227 .setkey
= chksum_setkey
,
229 .update
= chksum_update
,
230 .final
= chksum_final
,
231 .finup
= chksum_finup
,
232 .digest
= chksum_digest
,
233 .descsize
= sizeof(struct chksum_desc_ctx
),
236 .cra_driver_name
= "crc32-loongarch",
238 .cra_flags
= CRYPTO_ALG_OPTIONAL_KEY
,
239 .cra_blocksize
= CHKSUM_BLOCK_SIZE
,
240 .cra_ctxsize
= sizeof(struct chksum_ctx
),
241 .cra_module
= THIS_MODULE
,
242 .cra_init
= chksum_cra_init
,
246 static struct shash_alg crc32c_alg
= {
247 .digestsize
= CHKSUM_DIGEST_SIZE
,
248 .setkey
= chksum_setkey
,
250 .update
= chksumc_update
,
251 .final
= chksumc_final
,
252 .finup
= chksumc_finup
,
253 .digest
= chksumc_digest
,
254 .descsize
= sizeof(struct chksum_desc_ctx
),
256 .cra_name
= "crc32c",
257 .cra_driver_name
= "crc32c-loongarch",
259 .cra_flags
= CRYPTO_ALG_OPTIONAL_KEY
,
260 .cra_blocksize
= CHKSUM_BLOCK_SIZE
,
261 .cra_ctxsize
= sizeof(struct chksum_ctx
),
262 .cra_module
= THIS_MODULE
,
263 .cra_init
= chksumc_cra_init
,
267 static int __init
crc32_mod_init(void)
271 if (!cpu_has(CPU_FEATURE_CRC32
))
274 err
= crypto_register_shash(&crc32_alg
);
278 err
= crypto_register_shash(&crc32c_alg
);
285 static void __exit
crc32_mod_exit(void)
287 if (!cpu_has(CPU_FEATURE_CRC32
))
290 crypto_unregister_shash(&crc32_alg
);
291 crypto_unregister_shash(&crc32c_alg
);
294 module_init(crc32_mod_init
);
295 module_exit(crc32_mod_exit
);
297 MODULE_AUTHOR("Min Zhou <zhoumin@loongson.cn>");
298 MODULE_AUTHOR("Huacai Chen <chenhuacai@loongson.cn>");
299 MODULE_DESCRIPTION("CRC32 and CRC32C using LoongArch crc* instructions");
300 MODULE_LICENSE("GPL v2");