0001
0002
0003
0004
0005
0006
0007
0008
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
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
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
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
0178
0179
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);