0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0050
0051 #include <linux/vmalloc.h>
0052 #include <linux/sw842.h>
0053 #include <linux/spinlock.h>
0054
0055 #include "nx-842.h"
0056
0057
0058
0059
0060
0061
0062
0063 #define NX842_CRYPTO_MAGIC (0xf842)
0064 #define NX842_CRYPTO_HEADER_SIZE(g) \
0065 (sizeof(struct nx842_crypto_header) + \
0066 sizeof(struct nx842_crypto_header_group) * (g))
0067 #define NX842_CRYPTO_HEADER_MAX_SIZE \
0068 NX842_CRYPTO_HEADER_SIZE(NX842_CRYPTO_GROUP_MAX)
0069
0070
0071 #define BOUNCE_BUFFER_ORDER (2)
0072 #define BOUNCE_BUFFER_SIZE \
0073 ((unsigned int)(PAGE_SIZE << BOUNCE_BUFFER_ORDER))
0074
0075
0076 #define COMP_BUSY_TIMEOUT (250)
0077 #define DECOMP_BUSY_TIMEOUT (50)
0078
0079 struct nx842_crypto_param {
0080 u8 *in;
0081 unsigned int iremain;
0082 u8 *out;
0083 unsigned int oremain;
0084 unsigned int ototal;
0085 };
0086
0087 static int update_param(struct nx842_crypto_param *p,
0088 unsigned int slen, unsigned int dlen)
0089 {
0090 if (p->iremain < slen)
0091 return -EOVERFLOW;
0092 if (p->oremain < dlen)
0093 return -ENOSPC;
0094
0095 p->in += slen;
0096 p->iremain -= slen;
0097 p->out += dlen;
0098 p->oremain -= dlen;
0099 p->ototal += dlen;
0100
0101 return 0;
0102 }
0103
0104 int nx842_crypto_init(struct crypto_tfm *tfm, struct nx842_driver *driver)
0105 {
0106 struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
0107
0108 spin_lock_init(&ctx->lock);
0109 ctx->driver = driver;
0110 ctx->wmem = kmalloc(driver->workmem_size, GFP_KERNEL);
0111 ctx->sbounce = (u8 *)__get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER);
0112 ctx->dbounce = (u8 *)__get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER);
0113 if (!ctx->wmem || !ctx->sbounce || !ctx->dbounce) {
0114 kfree(ctx->wmem);
0115 free_page((unsigned long)ctx->sbounce);
0116 free_page((unsigned long)ctx->dbounce);
0117 return -ENOMEM;
0118 }
0119
0120 return 0;
0121 }
0122 EXPORT_SYMBOL_GPL(nx842_crypto_init);
0123
0124 void nx842_crypto_exit(struct crypto_tfm *tfm)
0125 {
0126 struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
0127
0128 kfree(ctx->wmem);
0129 free_page((unsigned long)ctx->sbounce);
0130 free_page((unsigned long)ctx->dbounce);
0131 }
0132 EXPORT_SYMBOL_GPL(nx842_crypto_exit);
0133
0134 static void check_constraints(struct nx842_constraints *c)
0135 {
0136
0137 if (c->maximum > BOUNCE_BUFFER_SIZE)
0138 c->maximum = BOUNCE_BUFFER_SIZE;
0139 }
0140
0141 static int nx842_crypto_add_header(struct nx842_crypto_header *hdr, u8 *buf)
0142 {
0143 int s = NX842_CRYPTO_HEADER_SIZE(hdr->groups);
0144
0145
0146 if (s > be16_to_cpu(hdr->group[0].padding)) {
0147 pr_err("Internal error: no space for header\n");
0148 return -EINVAL;
0149 }
0150
0151 memcpy(buf, hdr, s);
0152
0153 print_hex_dump_debug("header ", DUMP_PREFIX_OFFSET, 16, 1, buf, s, 0);
0154
0155 return 0;
0156 }
0157
0158 static int compress(struct nx842_crypto_ctx *ctx,
0159 struct nx842_crypto_param *p,
0160 struct nx842_crypto_header_group *g,
0161 struct nx842_constraints *c,
0162 u16 *ignore,
0163 unsigned int hdrsize)
0164 {
0165 unsigned int slen = p->iremain, dlen = p->oremain, tmplen;
0166 unsigned int adj_slen = slen;
0167 u8 *src = p->in, *dst = p->out;
0168 int ret, dskip = 0;
0169 ktime_t timeout;
0170
0171 if (p->iremain == 0)
0172 return -EOVERFLOW;
0173
0174 if (p->oremain == 0 || hdrsize + c->minimum > dlen)
0175 return -ENOSPC;
0176
0177 if (slen % c->multiple)
0178 adj_slen = round_up(slen, c->multiple);
0179 if (slen < c->minimum)
0180 adj_slen = c->minimum;
0181 if (slen > c->maximum)
0182 adj_slen = slen = c->maximum;
0183 if (adj_slen > slen || (u64)src % c->alignment) {
0184 adj_slen = min(adj_slen, BOUNCE_BUFFER_SIZE);
0185 slen = min(slen, BOUNCE_BUFFER_SIZE);
0186 if (adj_slen > slen)
0187 memset(ctx->sbounce + slen, 0, adj_slen - slen);
0188 memcpy(ctx->sbounce, src, slen);
0189 src = ctx->sbounce;
0190 slen = adj_slen;
0191 pr_debug("using comp sbounce buffer, len %x\n", slen);
0192 }
0193
0194 dst += hdrsize;
0195 dlen -= hdrsize;
0196
0197 if ((u64)dst % c->alignment) {
0198 dskip = (int)(PTR_ALIGN(dst, c->alignment) - dst);
0199 dst += dskip;
0200 dlen -= dskip;
0201 }
0202 if (dlen % c->multiple)
0203 dlen = round_down(dlen, c->multiple);
0204 if (dlen < c->minimum) {
0205 nospc:
0206 dst = ctx->dbounce;
0207 dlen = min(p->oremain, BOUNCE_BUFFER_SIZE);
0208 dlen = round_down(dlen, c->multiple);
0209 dskip = 0;
0210 pr_debug("using comp dbounce buffer, len %x\n", dlen);
0211 }
0212 if (dlen > c->maximum)
0213 dlen = c->maximum;
0214
0215 tmplen = dlen;
0216 timeout = ktime_add_ms(ktime_get(), COMP_BUSY_TIMEOUT);
0217 do {
0218 dlen = tmplen;
0219 ret = ctx->driver->compress(src, slen, dst, &dlen, ctx->wmem);
0220
0221
0222
0223 if (ret == -ENOSPC && dst != ctx->dbounce)
0224 goto nospc;
0225 } while (ret == -EBUSY && ktime_before(ktime_get(), timeout));
0226 if (ret)
0227 return ret;
0228
0229 dskip += hdrsize;
0230
0231 if (dst == ctx->dbounce)
0232 memcpy(p->out + dskip, dst, dlen);
0233
0234 g->padding = cpu_to_be16(dskip);
0235 g->compressed_length = cpu_to_be32(dlen);
0236 g->uncompressed_length = cpu_to_be32(slen);
0237
0238 if (p->iremain < slen) {
0239 *ignore = slen - p->iremain;
0240 slen = p->iremain;
0241 }
0242
0243 pr_debug("compress slen %x ignore %x dlen %x padding %x\n",
0244 slen, *ignore, dlen, dskip);
0245
0246 return update_param(p, slen, dskip + dlen);
0247 }
0248
0249 int nx842_crypto_compress(struct crypto_tfm *tfm,
0250 const u8 *src, unsigned int slen,
0251 u8 *dst, unsigned int *dlen)
0252 {
0253 struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
0254 struct nx842_crypto_header *hdr = &ctx->header;
0255 struct nx842_crypto_param p;
0256 struct nx842_constraints c = *ctx->driver->constraints;
0257 unsigned int groups, hdrsize, h;
0258 int ret, n;
0259 bool add_header;
0260 u16 ignore = 0;
0261
0262 check_constraints(&c);
0263
0264 p.in = (u8 *)src;
0265 p.iremain = slen;
0266 p.out = dst;
0267 p.oremain = *dlen;
0268 p.ototal = 0;
0269
0270 *dlen = 0;
0271
0272 groups = min_t(unsigned int, NX842_CRYPTO_GROUP_MAX,
0273 DIV_ROUND_UP(p.iremain, c.maximum));
0274 hdrsize = NX842_CRYPTO_HEADER_SIZE(groups);
0275
0276 spin_lock_bh(&ctx->lock);
0277
0278
0279 add_header = (p.iremain % c.multiple ||
0280 p.iremain < c.minimum ||
0281 p.iremain > c.maximum ||
0282 (u64)p.in % c.alignment ||
0283 p.oremain % c.multiple ||
0284 p.oremain < c.minimum ||
0285 p.oremain > c.maximum ||
0286 (u64)p.out % c.alignment);
0287
0288 hdr->magic = cpu_to_be16(NX842_CRYPTO_MAGIC);
0289 hdr->groups = 0;
0290 hdr->ignore = 0;
0291
0292 while (p.iremain > 0) {
0293 n = hdr->groups++;
0294 ret = -ENOSPC;
0295 if (hdr->groups > NX842_CRYPTO_GROUP_MAX)
0296 goto unlock;
0297
0298
0299 h = !n && add_header ? hdrsize : 0;
0300
0301 if (ignore)
0302 pr_warn("internal error, ignore is set %x\n", ignore);
0303
0304 ret = compress(ctx, &p, &hdr->group[n], &c, &ignore, h);
0305 if (ret)
0306 goto unlock;
0307 }
0308
0309 if (!add_header && hdr->groups > 1) {
0310 pr_err("Internal error: No header but multiple groups\n");
0311 ret = -EINVAL;
0312 goto unlock;
0313 }
0314
0315
0316 hdr->ignore = cpu_to_be16(ignore);
0317 if (ignore)
0318 pr_debug("marked %d bytes as ignore\n", ignore);
0319
0320 if (add_header)
0321 ret = nx842_crypto_add_header(hdr, dst);
0322 if (ret)
0323 goto unlock;
0324
0325 *dlen = p.ototal;
0326
0327 pr_debug("compress total slen %x dlen %x\n", slen, *dlen);
0328
0329 unlock:
0330 spin_unlock_bh(&ctx->lock);
0331 return ret;
0332 }
0333 EXPORT_SYMBOL_GPL(nx842_crypto_compress);
0334
0335 static int decompress(struct nx842_crypto_ctx *ctx,
0336 struct nx842_crypto_param *p,
0337 struct nx842_crypto_header_group *g,
0338 struct nx842_constraints *c,
0339 u16 ignore)
0340 {
0341 unsigned int slen = be32_to_cpu(g->compressed_length);
0342 unsigned int required_len = be32_to_cpu(g->uncompressed_length);
0343 unsigned int dlen = p->oremain, tmplen;
0344 unsigned int adj_slen = slen;
0345 u8 *src = p->in, *dst = p->out;
0346 u16 padding = be16_to_cpu(g->padding);
0347 int ret, spadding = 0;
0348 ktime_t timeout;
0349
0350 if (!slen || !required_len)
0351 return -EINVAL;
0352
0353 if (p->iremain <= 0 || padding + slen > p->iremain)
0354 return -EOVERFLOW;
0355
0356 if (p->oremain <= 0 || required_len - ignore > p->oremain)
0357 return -ENOSPC;
0358
0359 src += padding;
0360
0361 if (slen % c->multiple)
0362 adj_slen = round_up(slen, c->multiple);
0363 if (slen < c->minimum)
0364 adj_slen = c->minimum;
0365 if (slen > c->maximum)
0366 goto usesw;
0367 if (slen < adj_slen || (u64)src % c->alignment) {
0368
0369
0370
0371
0372 if (slen < adj_slen)
0373 memset(ctx->sbounce + slen, 0, adj_slen - slen);
0374 memcpy(ctx->sbounce, src, slen);
0375 src = ctx->sbounce;
0376 spadding = adj_slen - slen;
0377 slen = adj_slen;
0378 pr_debug("using decomp sbounce buffer, len %x\n", slen);
0379 }
0380
0381 if (dlen % c->multiple)
0382 dlen = round_down(dlen, c->multiple);
0383 if (dlen < required_len || (u64)dst % c->alignment) {
0384 dst = ctx->dbounce;
0385 dlen = min(required_len, BOUNCE_BUFFER_SIZE);
0386 pr_debug("using decomp dbounce buffer, len %x\n", dlen);
0387 }
0388 if (dlen < c->minimum)
0389 goto usesw;
0390 if (dlen > c->maximum)
0391 dlen = c->maximum;
0392
0393 tmplen = dlen;
0394 timeout = ktime_add_ms(ktime_get(), DECOMP_BUSY_TIMEOUT);
0395 do {
0396 dlen = tmplen;
0397 ret = ctx->driver->decompress(src, slen, dst, &dlen, ctx->wmem);
0398 } while (ret == -EBUSY && ktime_before(ktime_get(), timeout));
0399 if (ret) {
0400 usesw:
0401
0402 src = p->in + padding;
0403 slen = be32_to_cpu(g->compressed_length);
0404 spadding = 0;
0405 dst = p->out;
0406 dlen = p->oremain;
0407 if (dlen < required_len) {
0408 dst = ctx->dbounce;
0409 dlen = BOUNCE_BUFFER_SIZE;
0410 }
0411 pr_info_ratelimited("using software 842 decompression\n");
0412 ret = sw842_decompress(src, slen, dst, &dlen);
0413 }
0414 if (ret)
0415 return ret;
0416
0417 slen -= spadding;
0418
0419 dlen -= ignore;
0420 if (ignore)
0421 pr_debug("ignoring last %x bytes\n", ignore);
0422
0423 if (dst == ctx->dbounce)
0424 memcpy(p->out, dst, dlen);
0425
0426 pr_debug("decompress slen %x padding %x dlen %x ignore %x\n",
0427 slen, padding, dlen, ignore);
0428
0429 return update_param(p, slen + padding, dlen);
0430 }
0431
0432 int nx842_crypto_decompress(struct crypto_tfm *tfm,
0433 const u8 *src, unsigned int slen,
0434 u8 *dst, unsigned int *dlen)
0435 {
0436 struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
0437 struct nx842_crypto_header *hdr;
0438 struct nx842_crypto_param p;
0439 struct nx842_constraints c = *ctx->driver->constraints;
0440 int n, ret, hdr_len;
0441 u16 ignore = 0;
0442
0443 check_constraints(&c);
0444
0445 p.in = (u8 *)src;
0446 p.iremain = slen;
0447 p.out = dst;
0448 p.oremain = *dlen;
0449 p.ototal = 0;
0450
0451 *dlen = 0;
0452
0453 hdr = (struct nx842_crypto_header *)src;
0454
0455 spin_lock_bh(&ctx->lock);
0456
0457
0458
0459
0460 if (be16_to_cpu(hdr->magic) != NX842_CRYPTO_MAGIC) {
0461 struct nx842_crypto_header_group g = {
0462 .padding = 0,
0463 .compressed_length = cpu_to_be32(p.iremain),
0464 .uncompressed_length = cpu_to_be32(p.oremain),
0465 };
0466
0467 ret = decompress(ctx, &p, &g, &c, 0);
0468 if (ret)
0469 goto unlock;
0470
0471 goto success;
0472 }
0473
0474 if (!hdr->groups) {
0475 pr_err("header has no groups\n");
0476 ret = -EINVAL;
0477 goto unlock;
0478 }
0479 if (hdr->groups > NX842_CRYPTO_GROUP_MAX) {
0480 pr_err("header has too many groups %x, max %x\n",
0481 hdr->groups, NX842_CRYPTO_GROUP_MAX);
0482 ret = -EINVAL;
0483 goto unlock;
0484 }
0485
0486 hdr_len = NX842_CRYPTO_HEADER_SIZE(hdr->groups);
0487 if (hdr_len > slen) {
0488 ret = -EOVERFLOW;
0489 goto unlock;
0490 }
0491
0492 memcpy(&ctx->header, src, hdr_len);
0493 hdr = &ctx->header;
0494
0495 for (n = 0; n < hdr->groups; n++) {
0496
0497 if (n + 1 == hdr->groups)
0498 ignore = be16_to_cpu(hdr->ignore);
0499
0500 ret = decompress(ctx, &p, &hdr->group[n], &c, ignore);
0501 if (ret)
0502 goto unlock;
0503 }
0504
0505 success:
0506 *dlen = p.ototal;
0507
0508 pr_debug("decompress total slen %x dlen %x\n", slen, *dlen);
0509
0510 ret = 0;
0511
0512 unlock:
0513 spin_unlock_bh(&ctx->lock);
0514
0515 return ret;
0516 }
0517 EXPORT_SYMBOL_GPL(nx842_crypto_decompress);
0518
0519 MODULE_LICENSE("GPL");
0520 MODULE_DESCRIPTION("IBM PowerPC Nest (NX) 842 Hardware Compression Driver");
0521 MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");