Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * crc32-mips.c - CRC32 and CRC32C using optional MIPSr6 instructions
0004  *
0005  * Module based on arm64/crypto/crc32-arm.c
0006  *
0007  * Copyright (C) 2014 Linaro Ltd <yazen.ghannam@linaro.org>
0008  * Copyright (C) 2018 MIPS Tech, LLC
0009  */
0010 
0011 #include <linux/cpufeature.h>
0012 #include <linux/init.h>
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/string.h>
0016 #include <asm/mipsregs.h>
0017 #include <asm/unaligned.h>
0018 
0019 #include <crypto/internal/hash.h>
0020 
0021 enum crc_op_size {
0022     b, h, w, d,
0023 };
0024 
0025 enum crc_type {
0026     crc32,
0027     crc32c,
0028 };
0029 
0030 #ifndef TOOLCHAIN_SUPPORTS_CRC
0031 #define _ASM_SET_CRC(OP, SZ, TYPE)                    \
0032 _ASM_MACRO_3R(OP, rt, rs, rt2,                        \
0033     ".ifnc  \\rt, \\rt2\n\t"                      \
0034     ".error \"invalid operands \\\"" #OP " \\rt,\\rs,\\rt2\\\"\"\n\t" \
0035     ".endif\n\t"                              \
0036     _ASM_INSN_IF_MIPS(0x7c00000f | (__rt << 16) | (__rs << 21) |      \
0037               ((SZ) <<  6) | ((TYPE) << 8))           \
0038     _ASM_INSN32_IF_MM(0x00000030 | (__rs << 16) | (__rt << 21) |      \
0039               ((SZ) << 14) | ((TYPE) << 3)))
0040 #define _ASM_UNSET_CRC(op, SZ, TYPE) ".purgem " #op "\n\t"
0041 #else /* !TOOLCHAIN_SUPPORTS_CRC */
0042 #define _ASM_SET_CRC(op, SZ, TYPE) ".set\tcrc\n\t"
0043 #define _ASM_UNSET_CRC(op, SZ, TYPE)
0044 #endif
0045 
0046 #define __CRC32(crc, value, op, SZ, TYPE)       \
0047 do {                            \
0048     __asm__ __volatile__(               \
0049         ".set   push\n\t"           \
0050         _ASM_SET_CRC(op, SZ, TYPE)      \
0051         #op "   %0, %1, %0\n\t"         \
0052         _ASM_UNSET_CRC(op, SZ, TYPE)        \
0053         ".set   pop"                \
0054         : "+r" (crc)                \
0055         : "r" (value));             \
0056 } while (0)
0057 
0058 #define _CRC32_crc32b(crc, value)   __CRC32(crc, value, crc32b, 0, 0)
0059 #define _CRC32_crc32h(crc, value)   __CRC32(crc, value, crc32h, 1, 0)
0060 #define _CRC32_crc32w(crc, value)   __CRC32(crc, value, crc32w, 2, 0)
0061 #define _CRC32_crc32d(crc, value)   __CRC32(crc, value, crc32d, 3, 0)
0062 #define _CRC32_crc32cb(crc, value)  __CRC32(crc, value, crc32cb, 0, 1)
0063 #define _CRC32_crc32ch(crc, value)  __CRC32(crc, value, crc32ch, 1, 1)
0064 #define _CRC32_crc32cw(crc, value)  __CRC32(crc, value, crc32cw, 2, 1)
0065 #define _CRC32_crc32cd(crc, value)  __CRC32(crc, value, crc32cd, 3, 1)
0066 
0067 #define _CRC32(crc, value, size, op) \
0068     _CRC32_##op##size(crc, value)
0069 
0070 #define CRC32(crc, value, size) \
0071     _CRC32(crc, value, size, crc32)
0072 
0073 #define CRC32C(crc, value, size) \
0074     _CRC32(crc, value, size, crc32c)
0075 
0076 static u32 crc32_mips_le_hw(u32 crc_, const u8 *p, unsigned int len)
0077 {
0078     u32 crc = crc_;
0079 
0080 #ifdef CONFIG_64BIT
0081     while (len >= sizeof(u64)) {
0082         u64 value = get_unaligned_le64(p);
0083 
0084         CRC32(crc, value, d);
0085         p += sizeof(u64);
0086         len -= sizeof(u64);
0087     }
0088 
0089     if (len & sizeof(u32)) {
0090 #else /* !CONFIG_64BIT */
0091     while (len >= sizeof(u32)) {
0092 #endif
0093         u32 value = get_unaligned_le32(p);
0094 
0095         CRC32(crc, value, w);
0096         p += sizeof(u32);
0097         len -= sizeof(u32);
0098     }
0099 
0100     if (len & sizeof(u16)) {
0101         u16 value = get_unaligned_le16(p);
0102 
0103         CRC32(crc, value, h);
0104         p += sizeof(u16);
0105     }
0106 
0107     if (len & sizeof(u8)) {
0108         u8 value = *p++;
0109 
0110         CRC32(crc, value, b);
0111     }
0112 
0113     return crc;
0114 }
0115 
0116 static u32 crc32c_mips_le_hw(u32 crc_, const u8 *p, unsigned int len)
0117 {
0118     u32 crc = crc_;
0119 
0120 #ifdef CONFIG_64BIT
0121     while (len >= sizeof(u64)) {
0122         u64 value = get_unaligned_le64(p);
0123 
0124         CRC32C(crc, value, d);
0125         p += sizeof(u64);
0126         len -= sizeof(u64);
0127     }
0128 
0129     if (len & sizeof(u32)) {
0130 #else /* !CONFIG_64BIT */
0131     while (len >= sizeof(u32)) {
0132 #endif
0133         u32 value = get_unaligned_le32(p);
0134 
0135         CRC32C(crc, value, w);
0136         p += sizeof(u32);
0137         len -= sizeof(u32);
0138     }
0139 
0140     if (len & sizeof(u16)) {
0141         u16 value = get_unaligned_le16(p);
0142 
0143         CRC32C(crc, value, h);
0144         p += sizeof(u16);
0145     }
0146 
0147     if (len & sizeof(u8)) {
0148         u8 value = *p++;
0149 
0150         CRC32C(crc, value, b);
0151     }
0152     return crc;
0153 }
0154 
0155 #define CHKSUM_BLOCK_SIZE   1
0156 #define CHKSUM_DIGEST_SIZE  4
0157 
0158 struct chksum_ctx {
0159     u32 key;
0160 };
0161 
0162 struct chksum_desc_ctx {
0163     u32 crc;
0164 };
0165 
0166 static int chksum_init(struct shash_desc *desc)
0167 {
0168     struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
0169     struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
0170 
0171     ctx->crc = mctx->key;
0172 
0173     return 0;
0174 }
0175 
0176 /*
0177  * Setting the seed allows arbitrary accumulators and flexible XOR policy
0178  * If your algorithm starts with ~0, then XOR with ~0 before you set
0179  * the seed.
0180  */
0181 static int chksum_setkey(struct crypto_shash *tfm, const u8 *key,
0182              unsigned int keylen)
0183 {
0184     struct chksum_ctx *mctx = crypto_shash_ctx(tfm);
0185 
0186     if (keylen != sizeof(mctx->key))
0187         return -EINVAL;
0188     mctx->key = get_unaligned_le32(key);
0189     return 0;
0190 }
0191 
0192 static int chksum_update(struct shash_desc *desc, const u8 *data,
0193              unsigned int length)
0194 {
0195     struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
0196 
0197     ctx->crc = crc32_mips_le_hw(ctx->crc, data, length);
0198     return 0;
0199 }
0200 
0201 static int chksumc_update(struct shash_desc *desc, const u8 *data,
0202              unsigned int length)
0203 {
0204     struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
0205 
0206     ctx->crc = crc32c_mips_le_hw(ctx->crc, data, length);
0207     return 0;
0208 }
0209 
0210 static int chksum_final(struct shash_desc *desc, u8 *out)
0211 {
0212     struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
0213 
0214     put_unaligned_le32(ctx->crc, out);
0215     return 0;
0216 }
0217 
0218 static int chksumc_final(struct shash_desc *desc, u8 *out)
0219 {
0220     struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
0221 
0222     put_unaligned_le32(~ctx->crc, out);
0223     return 0;
0224 }
0225 
0226 static int __chksum_finup(u32 crc, const u8 *data, unsigned int len, u8 *out)
0227 {
0228     put_unaligned_le32(crc32_mips_le_hw(crc, data, len), out);
0229     return 0;
0230 }
0231 
0232 static int __chksumc_finup(u32 crc, const u8 *data, unsigned int len, u8 *out)
0233 {
0234     put_unaligned_le32(~crc32c_mips_le_hw(crc, data, len), out);
0235     return 0;
0236 }
0237 
0238 static int chksum_finup(struct shash_desc *desc, const u8 *data,
0239             unsigned int len, u8 *out)
0240 {
0241     struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
0242 
0243     return __chksum_finup(ctx->crc, data, len, out);
0244 }
0245 
0246 static int chksumc_finup(struct shash_desc *desc, const u8 *data,
0247             unsigned int len, u8 *out)
0248 {
0249     struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
0250 
0251     return __chksumc_finup(ctx->crc, data, len, out);
0252 }
0253 
0254 static int chksum_digest(struct shash_desc *desc, const u8 *data,
0255              unsigned int length, u8 *out)
0256 {
0257     struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
0258 
0259     return __chksum_finup(mctx->key, data, length, out);
0260 }
0261 
0262 static int chksumc_digest(struct shash_desc *desc, const u8 *data,
0263              unsigned int length, u8 *out)
0264 {
0265     struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
0266 
0267     return __chksumc_finup(mctx->key, data, length, out);
0268 }
0269 
0270 static int chksum_cra_init(struct crypto_tfm *tfm)
0271 {
0272     struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
0273 
0274     mctx->key = ~0;
0275     return 0;
0276 }
0277 
0278 static struct shash_alg crc32_alg = {
0279     .digestsize     =   CHKSUM_DIGEST_SIZE,
0280     .setkey         =   chksum_setkey,
0281     .init           =   chksum_init,
0282     .update         =   chksum_update,
0283     .final          =   chksum_final,
0284     .finup          =   chksum_finup,
0285     .digest         =   chksum_digest,
0286     .descsize       =   sizeof(struct chksum_desc_ctx),
0287     .base           =   {
0288         .cra_name       =   "crc32",
0289         .cra_driver_name    =   "crc32-mips-hw",
0290         .cra_priority       =   300,
0291         .cra_flags      =   CRYPTO_ALG_OPTIONAL_KEY,
0292         .cra_blocksize      =   CHKSUM_BLOCK_SIZE,
0293         .cra_alignmask      =   0,
0294         .cra_ctxsize        =   sizeof(struct chksum_ctx),
0295         .cra_module     =   THIS_MODULE,
0296         .cra_init       =   chksum_cra_init,
0297     }
0298 };
0299 
0300 static struct shash_alg crc32c_alg = {
0301     .digestsize     =   CHKSUM_DIGEST_SIZE,
0302     .setkey         =   chksum_setkey,
0303     .init           =   chksum_init,
0304     .update         =   chksumc_update,
0305     .final          =   chksumc_final,
0306     .finup          =   chksumc_finup,
0307     .digest         =   chksumc_digest,
0308     .descsize       =   sizeof(struct chksum_desc_ctx),
0309     .base           =   {
0310         .cra_name       =   "crc32c",
0311         .cra_driver_name    =   "crc32c-mips-hw",
0312         .cra_priority       =   300,
0313         .cra_flags      =   CRYPTO_ALG_OPTIONAL_KEY,
0314         .cra_blocksize      =   CHKSUM_BLOCK_SIZE,
0315         .cra_alignmask      =   0,
0316         .cra_ctxsize        =   sizeof(struct chksum_ctx),
0317         .cra_module     =   THIS_MODULE,
0318         .cra_init       =   chksum_cra_init,
0319     }
0320 };
0321 
0322 static int __init crc32_mod_init(void)
0323 {
0324     int err;
0325 
0326     err = crypto_register_shash(&crc32_alg);
0327 
0328     if (err)
0329         return err;
0330 
0331     err = crypto_register_shash(&crc32c_alg);
0332 
0333     if (err) {
0334         crypto_unregister_shash(&crc32_alg);
0335         return err;
0336     }
0337 
0338     return 0;
0339 }
0340 
0341 static void __exit crc32_mod_exit(void)
0342 {
0343     crypto_unregister_shash(&crc32_alg);
0344     crypto_unregister_shash(&crc32c_alg);
0345 }
0346 
0347 MODULE_AUTHOR("Marcin Nowakowski <marcin.nowakowski@mips.com");
0348 MODULE_DESCRIPTION("CRC32 and CRC32C using optional MIPS instructions");
0349 MODULE_LICENSE("GPL v2");
0350 
0351 module_cpu_feature_match(MIPS_CRC32, crc32_mod_init);
0352 module_exit(crc32_mod_exit);