1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2174d66d4SDan Streetman /*
3174d66d4SDan Streetman * Cryptographic API for the NX-842 hardware compression.
4174d66d4SDan Streetman *
5174d66d4SDan Streetman * Copyright (C) IBM Corporation, 2011-2015
6174d66d4SDan Streetman *
7174d66d4SDan Streetman * Designer of the Power data compression engine:
8174d66d4SDan Streetman * Bulent Abali <abali@us.ibm.com>
9174d66d4SDan Streetman *
10174d66d4SDan Streetman * Original Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
11174d66d4SDan Streetman * Seth Jennings <sjenning@linux.vnet.ibm.com>
12174d66d4SDan Streetman *
13174d66d4SDan Streetman * Rewrite: Dan Streetman <ddstreet@ieee.org>
14174d66d4SDan Streetman *
15174d66d4SDan Streetman * This is an interface to the NX-842 compression hardware in PowerPC
16174d66d4SDan Streetman * processors. Most of the complexity of this drvier is due to the fact that
17174d66d4SDan Streetman * the NX-842 compression hardware requires the input and output data buffers
18174d66d4SDan Streetman * to be specifically aligned, to be a specific multiple in length, and within
19174d66d4SDan Streetman * specific minimum and maximum lengths. Those restrictions, provided by the
20174d66d4SDan Streetman * nx-842 driver via nx842_constraints, mean this driver must use bounce
21174d66d4SDan Streetman * buffers and headers to correct misaligned in or out buffers, and to split
22174d66d4SDan Streetman * input buffers that are too large.
23174d66d4SDan Streetman *
24174d66d4SDan Streetman * This driver will fall back to software decompression if the hardware
25174d66d4SDan Streetman * decompression fails, so this driver's decompression should never fail as
26174d66d4SDan Streetman * long as the provided compressed buffer is valid. Any compressed buffer
27174d66d4SDan Streetman * created by this driver will have a header (except ones where the input
28174d66d4SDan Streetman * perfectly matches the constraints); so users of this driver cannot simply
29174d66d4SDan Streetman * pass a compressed buffer created by this driver over to the 842 software
30174d66d4SDan Streetman * decompression library. Instead, users must use this driver to decompress;
31174d66d4SDan Streetman * if the hardware fails or is unavailable, the compressed buffer will be
32174d66d4SDan Streetman * parsed and the header removed, and the raw 842 buffer(s) passed to the 842
33174d66d4SDan Streetman * software decompression library.
34174d66d4SDan Streetman *
35174d66d4SDan Streetman * This does not fall back to software compression, however, since the caller
36174d66d4SDan Streetman * of this function is specifically requesting hardware compression; if the
37174d66d4SDan Streetman * hardware compression fails, the caller can fall back to software
38174d66d4SDan Streetman * compression, and the raw 842 compressed buffer that the software compressor
39174d66d4SDan Streetman * creates can be passed to this driver for hardware decompression; any
40174d66d4SDan Streetman * buffer without our specific header magic is assumed to be a raw 842 buffer
41174d66d4SDan Streetman * and passed directly to the hardware. Note that the software compression
42174d66d4SDan Streetman * library will produce a compressed buffer that is incompatible with the
43174d66d4SDan Streetman * hardware decompressor if the original input buffer length is not a multiple
44174d66d4SDan Streetman * of 8; if such a compressed buffer is passed to this driver for
45174d66d4SDan Streetman * decompression, the hardware will reject it and this driver will then pass
46174d66d4SDan Streetman * it over to the software library for decompression.
47174d66d4SDan Streetman */
48174d66d4SDan Streetman
49174d66d4SDan Streetman #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
50174d66d4SDan Streetman
51174d66d4SDan Streetman #include <linux/vmalloc.h>
52174d66d4SDan Streetman #include <linux/sw842.h>
53174d66d4SDan Streetman #include <linux/spinlock.h>
54174d66d4SDan Streetman
55174d66d4SDan Streetman #include "nx-842.h"
56174d66d4SDan Streetman
57174d66d4SDan Streetman /* The first 5 bits of this magic are 0x1f, which is an invalid 842 5-bit
58174d66d4SDan Streetman * template (see lib/842/842.h), so this magic number will never appear at
59174d66d4SDan Streetman * the start of a raw 842 compressed buffer. That is important, as any buffer
60174d66d4SDan Streetman * passed to us without this magic is assumed to be a raw 842 compressed
61174d66d4SDan Streetman * buffer, and passed directly to the hardware to decompress.
62174d66d4SDan Streetman */
63174d66d4SDan Streetman #define NX842_CRYPTO_MAGIC (0xf842)
64174d66d4SDan Streetman #define NX842_CRYPTO_HEADER_SIZE(g) \
65174d66d4SDan Streetman (sizeof(struct nx842_crypto_header) + \
66174d66d4SDan Streetman sizeof(struct nx842_crypto_header_group) * (g))
67174d66d4SDan Streetman #define NX842_CRYPTO_HEADER_MAX_SIZE \
68174d66d4SDan Streetman NX842_CRYPTO_HEADER_SIZE(NX842_CRYPTO_GROUP_MAX)
69174d66d4SDan Streetman
70174d66d4SDan Streetman /* bounce buffer size */
71174d66d4SDan Streetman #define BOUNCE_BUFFER_ORDER (2)
72174d66d4SDan Streetman #define BOUNCE_BUFFER_SIZE \
73174d66d4SDan Streetman ((unsigned int)(PAGE_SIZE << BOUNCE_BUFFER_ORDER))
74174d66d4SDan Streetman
75174d66d4SDan Streetman /* try longer on comp because we can fallback to sw decomp if hw is busy */
76174d66d4SDan Streetman #define COMP_BUSY_TIMEOUT (250) /* ms */
77174d66d4SDan Streetman #define DECOMP_BUSY_TIMEOUT (50) /* ms */
78174d66d4SDan Streetman
79174d66d4SDan Streetman struct nx842_crypto_param {
80174d66d4SDan Streetman u8 *in;
81174d66d4SDan Streetman unsigned int iremain;
82174d66d4SDan Streetman u8 *out;
83174d66d4SDan Streetman unsigned int oremain;
84174d66d4SDan Streetman unsigned int ototal;
85174d66d4SDan Streetman };
86174d66d4SDan Streetman
update_param(struct nx842_crypto_param * p,unsigned int slen,unsigned int dlen)87174d66d4SDan Streetman static int update_param(struct nx842_crypto_param *p,
88174d66d4SDan Streetman unsigned int slen, unsigned int dlen)
89174d66d4SDan Streetman {
90174d66d4SDan Streetman if (p->iremain < slen)
91174d66d4SDan Streetman return -EOVERFLOW;
92174d66d4SDan Streetman if (p->oremain < dlen)
93174d66d4SDan Streetman return -ENOSPC;
94174d66d4SDan Streetman
95174d66d4SDan Streetman p->in += slen;
96174d66d4SDan Streetman p->iremain -= slen;
97174d66d4SDan Streetman p->out += dlen;
98174d66d4SDan Streetman p->oremain -= dlen;
99174d66d4SDan Streetman p->ototal += dlen;
100174d66d4SDan Streetman
101174d66d4SDan Streetman return 0;
102174d66d4SDan Streetman }
103174d66d4SDan Streetman
nx842_crypto_init(struct crypto_tfm * tfm,struct nx842_driver * driver)10403952d98SDan Streetman int nx842_crypto_init(struct crypto_tfm *tfm, struct nx842_driver *driver)
105174d66d4SDan Streetman {
106174d66d4SDan Streetman struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
107174d66d4SDan Streetman
108174d66d4SDan Streetman spin_lock_init(&ctx->lock);
10903952d98SDan Streetman ctx->driver = driver;
1100f46a79aSHaren Myneni ctx->wmem = kmalloc(driver->workmem_size, GFP_KERNEL);
111174d66d4SDan Streetman ctx->sbounce = (u8 *)__get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER);
112174d66d4SDan Streetman ctx->dbounce = (u8 *)__get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER);
113174d66d4SDan Streetman if (!ctx->wmem || !ctx->sbounce || !ctx->dbounce) {
114174d66d4SDan Streetman kfree(ctx->wmem);
115174d66d4SDan Streetman free_page((unsigned long)ctx->sbounce);
116174d66d4SDan Streetman free_page((unsigned long)ctx->dbounce);
117174d66d4SDan Streetman return -ENOMEM;
118174d66d4SDan Streetman }
119174d66d4SDan Streetman
120174d66d4SDan Streetman return 0;
121174d66d4SDan Streetman }
12203952d98SDan Streetman EXPORT_SYMBOL_GPL(nx842_crypto_init);
123174d66d4SDan Streetman
nx842_crypto_exit(struct crypto_tfm * tfm)12403952d98SDan Streetman void nx842_crypto_exit(struct crypto_tfm *tfm)
125174d66d4SDan Streetman {
126174d66d4SDan Streetman struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
127174d66d4SDan Streetman
128174d66d4SDan Streetman kfree(ctx->wmem);
129174d66d4SDan Streetman free_page((unsigned long)ctx->sbounce);
130174d66d4SDan Streetman free_page((unsigned long)ctx->dbounce);
131174d66d4SDan Streetman }
13203952d98SDan Streetman EXPORT_SYMBOL_GPL(nx842_crypto_exit);
133174d66d4SDan Streetman
check_constraints(struct nx842_constraints * c)134174d66d4SDan Streetman static void check_constraints(struct nx842_constraints *c)
135174d66d4SDan Streetman {
136174d66d4SDan Streetman /* limit maximum, to always have enough bounce buffer to decompress */
137174d66d4SDan Streetman if (c->maximum > BOUNCE_BUFFER_SIZE)
138174d66d4SDan Streetman c->maximum = BOUNCE_BUFFER_SIZE;
139174d66d4SDan Streetman }
140174d66d4SDan Streetman
nx842_crypto_add_header(struct nx842_crypto_header * hdr,u8 * buf)141174d66d4SDan Streetman static int nx842_crypto_add_header(struct nx842_crypto_header *hdr, u8 *buf)
142174d66d4SDan Streetman {
143174d66d4SDan Streetman int s = NX842_CRYPTO_HEADER_SIZE(hdr->groups);
144174d66d4SDan Streetman
145174d66d4SDan Streetman /* compress should have added space for header */
146174d66d4SDan Streetman if (s > be16_to_cpu(hdr->group[0].padding)) {
147174d66d4SDan Streetman pr_err("Internal error: no space for header\n");
148174d66d4SDan Streetman return -EINVAL;
149174d66d4SDan Streetman }
150174d66d4SDan Streetman
151174d66d4SDan Streetman memcpy(buf, hdr, s);
152174d66d4SDan Streetman
153174d66d4SDan Streetman print_hex_dump_debug("header ", DUMP_PREFIX_OFFSET, 16, 1, buf, s, 0);
154174d66d4SDan Streetman
155174d66d4SDan Streetman return 0;
156174d66d4SDan Streetman }
157174d66d4SDan Streetman
compress(struct nx842_crypto_ctx * ctx,struct nx842_crypto_param * p,struct nx842_crypto_header_group * g,struct nx842_constraints * c,u16 * ignore,unsigned int hdrsize)158174d66d4SDan Streetman static int compress(struct nx842_crypto_ctx *ctx,
159174d66d4SDan Streetman struct nx842_crypto_param *p,
160174d66d4SDan Streetman struct nx842_crypto_header_group *g,
161174d66d4SDan Streetman struct nx842_constraints *c,
162174d66d4SDan Streetman u16 *ignore,
163174d66d4SDan Streetman unsigned int hdrsize)
164174d66d4SDan Streetman {
165174d66d4SDan Streetman unsigned int slen = p->iremain, dlen = p->oremain, tmplen;
166174d66d4SDan Streetman unsigned int adj_slen = slen;
167174d66d4SDan Streetman u8 *src = p->in, *dst = p->out;
168174d66d4SDan Streetman int ret, dskip = 0;
169174d66d4SDan Streetman ktime_t timeout;
170174d66d4SDan Streetman
171174d66d4SDan Streetman if (p->iremain == 0)
172174d66d4SDan Streetman return -EOVERFLOW;
173174d66d4SDan Streetman
174174d66d4SDan Streetman if (p->oremain == 0 || hdrsize + c->minimum > dlen)
175174d66d4SDan Streetman return -ENOSPC;
176174d66d4SDan Streetman
177174d66d4SDan Streetman if (slen % c->multiple)
178174d66d4SDan Streetman adj_slen = round_up(slen, c->multiple);
179174d66d4SDan Streetman if (slen < c->minimum)
180174d66d4SDan Streetman adj_slen = c->minimum;
181174d66d4SDan Streetman if (slen > c->maximum)
182174d66d4SDan Streetman adj_slen = slen = c->maximum;
183174d66d4SDan Streetman if (adj_slen > slen || (u64)src % c->alignment) {
184174d66d4SDan Streetman adj_slen = min(adj_slen, BOUNCE_BUFFER_SIZE);
185174d66d4SDan Streetman slen = min(slen, BOUNCE_BUFFER_SIZE);
186174d66d4SDan Streetman if (adj_slen > slen)
187174d66d4SDan Streetman memset(ctx->sbounce + slen, 0, adj_slen - slen);
188174d66d4SDan Streetman memcpy(ctx->sbounce, src, slen);
189174d66d4SDan Streetman src = ctx->sbounce;
190174d66d4SDan Streetman slen = adj_slen;
191174d66d4SDan Streetman pr_debug("using comp sbounce buffer, len %x\n", slen);
192174d66d4SDan Streetman }
193174d66d4SDan Streetman
194174d66d4SDan Streetman dst += hdrsize;
195174d66d4SDan Streetman dlen -= hdrsize;
196174d66d4SDan Streetman
197174d66d4SDan Streetman if ((u64)dst % c->alignment) {
198174d66d4SDan Streetman dskip = (int)(PTR_ALIGN(dst, c->alignment) - dst);
199174d66d4SDan Streetman dst += dskip;
200174d66d4SDan Streetman dlen -= dskip;
201174d66d4SDan Streetman }
202174d66d4SDan Streetman if (dlen % c->multiple)
203174d66d4SDan Streetman dlen = round_down(dlen, c->multiple);
204174d66d4SDan Streetman if (dlen < c->minimum) {
205174d66d4SDan Streetman nospc:
206174d66d4SDan Streetman dst = ctx->dbounce;
207174d66d4SDan Streetman dlen = min(p->oremain, BOUNCE_BUFFER_SIZE);
208174d66d4SDan Streetman dlen = round_down(dlen, c->multiple);
209174d66d4SDan Streetman dskip = 0;
210174d66d4SDan Streetman pr_debug("using comp dbounce buffer, len %x\n", dlen);
211174d66d4SDan Streetman }
212174d66d4SDan Streetman if (dlen > c->maximum)
213174d66d4SDan Streetman dlen = c->maximum;
214174d66d4SDan Streetman
215174d66d4SDan Streetman tmplen = dlen;
216174d66d4SDan Streetman timeout = ktime_add_ms(ktime_get(), COMP_BUSY_TIMEOUT);
217174d66d4SDan Streetman do {
218174d66d4SDan Streetman dlen = tmplen; /* reset dlen, if we're retrying */
21903952d98SDan Streetman ret = ctx->driver->compress(src, slen, dst, &dlen, ctx->wmem);
220174d66d4SDan Streetman /* possibly we should reduce the slen here, instead of
221174d66d4SDan Streetman * retrying with the dbounce buffer?
222174d66d4SDan Streetman */
223174d66d4SDan Streetman if (ret == -ENOSPC && dst != ctx->dbounce)
224174d66d4SDan Streetman goto nospc;
225174d66d4SDan Streetman } while (ret == -EBUSY && ktime_before(ktime_get(), timeout));
226174d66d4SDan Streetman if (ret)
227174d66d4SDan Streetman return ret;
228174d66d4SDan Streetman
229174d66d4SDan Streetman dskip += hdrsize;
230174d66d4SDan Streetman
231174d66d4SDan Streetman if (dst == ctx->dbounce)
232174d66d4SDan Streetman memcpy(p->out + dskip, dst, dlen);
233174d66d4SDan Streetman
234174d66d4SDan Streetman g->padding = cpu_to_be16(dskip);
235174d66d4SDan Streetman g->compressed_length = cpu_to_be32(dlen);
236174d66d4SDan Streetman g->uncompressed_length = cpu_to_be32(slen);
237174d66d4SDan Streetman
238174d66d4SDan Streetman if (p->iremain < slen) {
239174d66d4SDan Streetman *ignore = slen - p->iremain;
240174d66d4SDan Streetman slen = p->iremain;
241174d66d4SDan Streetman }
242174d66d4SDan Streetman
243174d66d4SDan Streetman pr_debug("compress slen %x ignore %x dlen %x padding %x\n",
244174d66d4SDan Streetman slen, *ignore, dlen, dskip);
245174d66d4SDan Streetman
246174d66d4SDan Streetman return update_param(p, slen, dskip + dlen);
247174d66d4SDan Streetman }
248174d66d4SDan Streetman
nx842_crypto_compress(struct crypto_tfm * tfm,const u8 * src,unsigned int slen,u8 * dst,unsigned int * dlen)24903952d98SDan Streetman int nx842_crypto_compress(struct crypto_tfm *tfm,
250174d66d4SDan Streetman const u8 *src, unsigned int slen,
251174d66d4SDan Streetman u8 *dst, unsigned int *dlen)
252174d66d4SDan Streetman {
253174d66d4SDan Streetman struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
254*1e6b251cSGustavo A. R. Silva struct nx842_crypto_header *hdr =
255*1e6b251cSGustavo A. R. Silva container_of(&ctx->header,
256*1e6b251cSGustavo A. R. Silva struct nx842_crypto_header, hdr);
257174d66d4SDan Streetman struct nx842_crypto_param p;
25803952d98SDan Streetman struct nx842_constraints c = *ctx->driver->constraints;
259174d66d4SDan Streetman unsigned int groups, hdrsize, h;
260174d66d4SDan Streetman int ret, n;
261174d66d4SDan Streetman bool add_header;
262174d66d4SDan Streetman u16 ignore = 0;
263174d66d4SDan Streetman
264174d66d4SDan Streetman check_constraints(&c);
265174d66d4SDan Streetman
266174d66d4SDan Streetman p.in = (u8 *)src;
267174d66d4SDan Streetman p.iremain = slen;
268174d66d4SDan Streetman p.out = dst;
269174d66d4SDan Streetman p.oremain = *dlen;
270174d66d4SDan Streetman p.ototal = 0;
271174d66d4SDan Streetman
272174d66d4SDan Streetman *dlen = 0;
273174d66d4SDan Streetman
274174d66d4SDan Streetman groups = min_t(unsigned int, NX842_CRYPTO_GROUP_MAX,
275174d66d4SDan Streetman DIV_ROUND_UP(p.iremain, c.maximum));
276174d66d4SDan Streetman hdrsize = NX842_CRYPTO_HEADER_SIZE(groups);
277174d66d4SDan Streetman
278174d66d4SDan Streetman spin_lock_bh(&ctx->lock);
279174d66d4SDan Streetman
280174d66d4SDan Streetman /* skip adding header if the buffers meet all constraints */
281174d66d4SDan Streetman add_header = (p.iremain % c.multiple ||
282174d66d4SDan Streetman p.iremain < c.minimum ||
283174d66d4SDan Streetman p.iremain > c.maximum ||
284174d66d4SDan Streetman (u64)p.in % c.alignment ||
285174d66d4SDan Streetman p.oremain % c.multiple ||
286174d66d4SDan Streetman p.oremain < c.minimum ||
287174d66d4SDan Streetman p.oremain > c.maximum ||
288174d66d4SDan Streetman (u64)p.out % c.alignment);
289174d66d4SDan Streetman
290174d66d4SDan Streetman hdr->magic = cpu_to_be16(NX842_CRYPTO_MAGIC);
291174d66d4SDan Streetman hdr->groups = 0;
292174d66d4SDan Streetman hdr->ignore = 0;
293174d66d4SDan Streetman
294174d66d4SDan Streetman while (p.iremain > 0) {
295174d66d4SDan Streetman n = hdr->groups++;
296174d66d4SDan Streetman ret = -ENOSPC;
297174d66d4SDan Streetman if (hdr->groups > NX842_CRYPTO_GROUP_MAX)
298174d66d4SDan Streetman goto unlock;
299174d66d4SDan Streetman
300174d66d4SDan Streetman /* header goes before first group */
301174d66d4SDan Streetman h = !n && add_header ? hdrsize : 0;
302174d66d4SDan Streetman
303174d66d4SDan Streetman if (ignore)
304fc4fa6e1SMasanari Iida pr_warn("internal error, ignore is set %x\n", ignore);
305174d66d4SDan Streetman
306174d66d4SDan Streetman ret = compress(ctx, &p, &hdr->group[n], &c, &ignore, h);
307174d66d4SDan Streetman if (ret)
308174d66d4SDan Streetman goto unlock;
309174d66d4SDan Streetman }
310174d66d4SDan Streetman
311174d66d4SDan Streetman if (!add_header && hdr->groups > 1) {
312174d66d4SDan Streetman pr_err("Internal error: No header but multiple groups\n");
313174d66d4SDan Streetman ret = -EINVAL;
314174d66d4SDan Streetman goto unlock;
315174d66d4SDan Streetman }
316174d66d4SDan Streetman
317174d66d4SDan Streetman /* ignore indicates the input stream needed to be padded */
318174d66d4SDan Streetman hdr->ignore = cpu_to_be16(ignore);
319174d66d4SDan Streetman if (ignore)
320174d66d4SDan Streetman pr_debug("marked %d bytes as ignore\n", ignore);
321174d66d4SDan Streetman
322174d66d4SDan Streetman if (add_header)
323174d66d4SDan Streetman ret = nx842_crypto_add_header(hdr, dst);
324174d66d4SDan Streetman if (ret)
325174d66d4SDan Streetman goto unlock;
326174d66d4SDan Streetman
327174d66d4SDan Streetman *dlen = p.ototal;
328174d66d4SDan Streetman
329174d66d4SDan Streetman pr_debug("compress total slen %x dlen %x\n", slen, *dlen);
330174d66d4SDan Streetman
331174d66d4SDan Streetman unlock:
332174d66d4SDan Streetman spin_unlock_bh(&ctx->lock);
333174d66d4SDan Streetman return ret;
334174d66d4SDan Streetman }
33503952d98SDan Streetman EXPORT_SYMBOL_GPL(nx842_crypto_compress);
336174d66d4SDan Streetman
decompress(struct nx842_crypto_ctx * ctx,struct nx842_crypto_param * p,struct nx842_crypto_header_group * g,struct nx842_constraints * c,u16 ignore)337174d66d4SDan Streetman static int decompress(struct nx842_crypto_ctx *ctx,
338174d66d4SDan Streetman struct nx842_crypto_param *p,
339174d66d4SDan Streetman struct nx842_crypto_header_group *g,
340174d66d4SDan Streetman struct nx842_constraints *c,
341174d66d4SDan Streetman u16 ignore)
342174d66d4SDan Streetman {
343174d66d4SDan Streetman unsigned int slen = be32_to_cpu(g->compressed_length);
344174d66d4SDan Streetman unsigned int required_len = be32_to_cpu(g->uncompressed_length);
345174d66d4SDan Streetman unsigned int dlen = p->oremain, tmplen;
346174d66d4SDan Streetman unsigned int adj_slen = slen;
347174d66d4SDan Streetman u8 *src = p->in, *dst = p->out;
348174d66d4SDan Streetman u16 padding = be16_to_cpu(g->padding);
349f947d7fdSYueHaibing int ret, spadding = 0;
350174d66d4SDan Streetman ktime_t timeout;
351174d66d4SDan Streetman
352174d66d4SDan Streetman if (!slen || !required_len)
353174d66d4SDan Streetman return -EINVAL;
354174d66d4SDan Streetman
355174d66d4SDan Streetman if (p->iremain <= 0 || padding + slen > p->iremain)
356174d66d4SDan Streetman return -EOVERFLOW;
357174d66d4SDan Streetman
358174d66d4SDan Streetman if (p->oremain <= 0 || required_len - ignore > p->oremain)
359174d66d4SDan Streetman return -ENOSPC;
360174d66d4SDan Streetman
361174d66d4SDan Streetman src += padding;
362174d66d4SDan Streetman
363174d66d4SDan Streetman if (slen % c->multiple)
364174d66d4SDan Streetman adj_slen = round_up(slen, c->multiple);
365174d66d4SDan Streetman if (slen < c->minimum)
366174d66d4SDan Streetman adj_slen = c->minimum;
367174d66d4SDan Streetman if (slen > c->maximum)
368174d66d4SDan Streetman goto usesw;
369174d66d4SDan Streetman if (slen < adj_slen || (u64)src % c->alignment) {
370174d66d4SDan Streetman /* we can append padding bytes because the 842 format defines
371174d66d4SDan Streetman * an "end" template (see lib/842/842_decompress.c) and will
372174d66d4SDan Streetman * ignore any bytes following it.
373174d66d4SDan Streetman */
374174d66d4SDan Streetman if (slen < adj_slen)
375174d66d4SDan Streetman memset(ctx->sbounce + slen, 0, adj_slen - slen);
376174d66d4SDan Streetman memcpy(ctx->sbounce, src, slen);
377174d66d4SDan Streetman src = ctx->sbounce;
378174d66d4SDan Streetman spadding = adj_slen - slen;
379174d66d4SDan Streetman slen = adj_slen;
380174d66d4SDan Streetman pr_debug("using decomp sbounce buffer, len %x\n", slen);
381174d66d4SDan Streetman }
382174d66d4SDan Streetman
383174d66d4SDan Streetman if (dlen % c->multiple)
384174d66d4SDan Streetman dlen = round_down(dlen, c->multiple);
385174d66d4SDan Streetman if (dlen < required_len || (u64)dst % c->alignment) {
386174d66d4SDan Streetman dst = ctx->dbounce;
387174d66d4SDan Streetman dlen = min(required_len, BOUNCE_BUFFER_SIZE);
388174d66d4SDan Streetman pr_debug("using decomp dbounce buffer, len %x\n", dlen);
389174d66d4SDan Streetman }
390174d66d4SDan Streetman if (dlen < c->minimum)
391174d66d4SDan Streetman goto usesw;
392174d66d4SDan Streetman if (dlen > c->maximum)
393174d66d4SDan Streetman dlen = c->maximum;
394174d66d4SDan Streetman
395174d66d4SDan Streetman tmplen = dlen;
396174d66d4SDan Streetman timeout = ktime_add_ms(ktime_get(), DECOMP_BUSY_TIMEOUT);
397174d66d4SDan Streetman do {
398174d66d4SDan Streetman dlen = tmplen; /* reset dlen, if we're retrying */
39903952d98SDan Streetman ret = ctx->driver->decompress(src, slen, dst, &dlen, ctx->wmem);
400174d66d4SDan Streetman } while (ret == -EBUSY && ktime_before(ktime_get(), timeout));
401174d66d4SDan Streetman if (ret) {
402174d66d4SDan Streetman usesw:
403174d66d4SDan Streetman /* reset everything, sw doesn't have constraints */
404174d66d4SDan Streetman src = p->in + padding;
405174d66d4SDan Streetman slen = be32_to_cpu(g->compressed_length);
406174d66d4SDan Streetman spadding = 0;
407174d66d4SDan Streetman dst = p->out;
408174d66d4SDan Streetman dlen = p->oremain;
409174d66d4SDan Streetman if (dlen < required_len) { /* have ignore bytes */
410174d66d4SDan Streetman dst = ctx->dbounce;
411174d66d4SDan Streetman dlen = BOUNCE_BUFFER_SIZE;
412174d66d4SDan Streetman }
413174d66d4SDan Streetman pr_info_ratelimited("using software 842 decompression\n");
414174d66d4SDan Streetman ret = sw842_decompress(src, slen, dst, &dlen);
415174d66d4SDan Streetman }
416174d66d4SDan Streetman if (ret)
417174d66d4SDan Streetman return ret;
418174d66d4SDan Streetman
419174d66d4SDan Streetman slen -= spadding;
420174d66d4SDan Streetman
421174d66d4SDan Streetman dlen -= ignore;
422174d66d4SDan Streetman if (ignore)
423174d66d4SDan Streetman pr_debug("ignoring last %x bytes\n", ignore);
424174d66d4SDan Streetman
425174d66d4SDan Streetman if (dst == ctx->dbounce)
426174d66d4SDan Streetman memcpy(p->out, dst, dlen);
427174d66d4SDan Streetman
428174d66d4SDan Streetman pr_debug("decompress slen %x padding %x dlen %x ignore %x\n",
429174d66d4SDan Streetman slen, padding, dlen, ignore);
430174d66d4SDan Streetman
431174d66d4SDan Streetman return update_param(p, slen + padding, dlen);
432174d66d4SDan Streetman }
433174d66d4SDan Streetman
nx842_crypto_decompress(struct crypto_tfm * tfm,const u8 * src,unsigned int slen,u8 * dst,unsigned int * dlen)43403952d98SDan Streetman int nx842_crypto_decompress(struct crypto_tfm *tfm,
435174d66d4SDan Streetman const u8 *src, unsigned int slen,
436174d66d4SDan Streetman u8 *dst, unsigned int *dlen)
437174d66d4SDan Streetman {
438174d66d4SDan Streetman struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
439174d66d4SDan Streetman struct nx842_crypto_header *hdr;
440174d66d4SDan Streetman struct nx842_crypto_param p;
44103952d98SDan Streetman struct nx842_constraints c = *ctx->driver->constraints;
442174d66d4SDan Streetman int n, ret, hdr_len;
443174d66d4SDan Streetman u16 ignore = 0;
444174d66d4SDan Streetman
445174d66d4SDan Streetman check_constraints(&c);
446174d66d4SDan Streetman
447174d66d4SDan Streetman p.in = (u8 *)src;
448174d66d4SDan Streetman p.iremain = slen;
449174d66d4SDan Streetman p.out = dst;
450174d66d4SDan Streetman p.oremain = *dlen;
451174d66d4SDan Streetman p.ototal = 0;
452174d66d4SDan Streetman
453174d66d4SDan Streetman *dlen = 0;
454174d66d4SDan Streetman
455174d66d4SDan Streetman hdr = (struct nx842_crypto_header *)src;
456174d66d4SDan Streetman
457174d66d4SDan Streetman spin_lock_bh(&ctx->lock);
458174d66d4SDan Streetman
459174d66d4SDan Streetman /* If it doesn't start with our header magic number, assume it's a raw
460174d66d4SDan Streetman * 842 compressed buffer and pass it directly to the hardware driver
461174d66d4SDan Streetman */
462174d66d4SDan Streetman if (be16_to_cpu(hdr->magic) != NX842_CRYPTO_MAGIC) {
463174d66d4SDan Streetman struct nx842_crypto_header_group g = {
464174d66d4SDan Streetman .padding = 0,
465174d66d4SDan Streetman .compressed_length = cpu_to_be32(p.iremain),
466174d66d4SDan Streetman .uncompressed_length = cpu_to_be32(p.oremain),
467174d66d4SDan Streetman };
468174d66d4SDan Streetman
469174d66d4SDan Streetman ret = decompress(ctx, &p, &g, &c, 0);
470174d66d4SDan Streetman if (ret)
471174d66d4SDan Streetman goto unlock;
472174d66d4SDan Streetman
473174d66d4SDan Streetman goto success;
474174d66d4SDan Streetman }
475174d66d4SDan Streetman
476174d66d4SDan Streetman if (!hdr->groups) {
477174d66d4SDan Streetman pr_err("header has no groups\n");
478174d66d4SDan Streetman ret = -EINVAL;
479174d66d4SDan Streetman goto unlock;
480174d66d4SDan Streetman }
481174d66d4SDan Streetman if (hdr->groups > NX842_CRYPTO_GROUP_MAX) {
482174d66d4SDan Streetman pr_err("header has too many groups %x, max %x\n",
483174d66d4SDan Streetman hdr->groups, NX842_CRYPTO_GROUP_MAX);
484174d66d4SDan Streetman ret = -EINVAL;
485174d66d4SDan Streetman goto unlock;
486174d66d4SDan Streetman }
487174d66d4SDan Streetman
488174d66d4SDan Streetman hdr_len = NX842_CRYPTO_HEADER_SIZE(hdr->groups);
489174d66d4SDan Streetman if (hdr_len > slen) {
490174d66d4SDan Streetman ret = -EOVERFLOW;
491174d66d4SDan Streetman goto unlock;
492174d66d4SDan Streetman }
493174d66d4SDan Streetman
494174d66d4SDan Streetman memcpy(&ctx->header, src, hdr_len);
495*1e6b251cSGustavo A. R. Silva hdr = container_of(&ctx->header, struct nx842_crypto_header, hdr);
496174d66d4SDan Streetman
497174d66d4SDan Streetman for (n = 0; n < hdr->groups; n++) {
498174d66d4SDan Streetman /* ignore applies to last group */
499174d66d4SDan Streetman if (n + 1 == hdr->groups)
500174d66d4SDan Streetman ignore = be16_to_cpu(hdr->ignore);
501174d66d4SDan Streetman
502174d66d4SDan Streetman ret = decompress(ctx, &p, &hdr->group[n], &c, ignore);
503174d66d4SDan Streetman if (ret)
504174d66d4SDan Streetman goto unlock;
505174d66d4SDan Streetman }
506174d66d4SDan Streetman
507174d66d4SDan Streetman success:
508174d66d4SDan Streetman *dlen = p.ototal;
509174d66d4SDan Streetman
510174d66d4SDan Streetman pr_debug("decompress total slen %x dlen %x\n", slen, *dlen);
511174d66d4SDan Streetman
512174d66d4SDan Streetman ret = 0;
513174d66d4SDan Streetman
514174d66d4SDan Streetman unlock:
515174d66d4SDan Streetman spin_unlock_bh(&ctx->lock);
516174d66d4SDan Streetman
517174d66d4SDan Streetman return ret;
518174d66d4SDan Streetman }
51903952d98SDan Streetman EXPORT_SYMBOL_GPL(nx842_crypto_decompress);
520174d66d4SDan Streetman
521174d66d4SDan Streetman MODULE_LICENSE("GPL");
522174d66d4SDan Streetman MODULE_DESCRIPTION("IBM PowerPC Nest (NX) 842 Hardware Compression Driver");
523174d66d4SDan Streetman MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
524