xref: /linux/drivers/crypto/nx/nx-842.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
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