18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Cryptographic API for the NX-842 hardware compression.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) IBM Corporation, 2011-2015
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Designer of the Power data compression engine:
88c2ecf20Sopenharmony_ci *   Bulent Abali <abali@us.ibm.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Original Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
118c2ecf20Sopenharmony_ci *                   Seth Jennings <sjenning@linux.vnet.ibm.com>
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Rewrite: Dan Streetman <ddstreet@ieee.org>
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * This is an interface to the NX-842 compression hardware in PowerPC
168c2ecf20Sopenharmony_ci * processors.  Most of the complexity of this drvier is due to the fact that
178c2ecf20Sopenharmony_ci * the NX-842 compression hardware requires the input and output data buffers
188c2ecf20Sopenharmony_ci * to be specifically aligned, to be a specific multiple in length, and within
198c2ecf20Sopenharmony_ci * specific minimum and maximum lengths.  Those restrictions, provided by the
208c2ecf20Sopenharmony_ci * nx-842 driver via nx842_constraints, mean this driver must use bounce
218c2ecf20Sopenharmony_ci * buffers and headers to correct misaligned in or out buffers, and to split
228c2ecf20Sopenharmony_ci * input buffers that are too large.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * This driver will fall back to software decompression if the hardware
258c2ecf20Sopenharmony_ci * decompression fails, so this driver's decompression should never fail as
268c2ecf20Sopenharmony_ci * long as the provided compressed buffer is valid.  Any compressed buffer
278c2ecf20Sopenharmony_ci * created by this driver will have a header (except ones where the input
288c2ecf20Sopenharmony_ci * perfectly matches the constraints); so users of this driver cannot simply
298c2ecf20Sopenharmony_ci * pass a compressed buffer created by this driver over to the 842 software
308c2ecf20Sopenharmony_ci * decompression library.  Instead, users must use this driver to decompress;
318c2ecf20Sopenharmony_ci * if the hardware fails or is unavailable, the compressed buffer will be
328c2ecf20Sopenharmony_ci * parsed and the header removed, and the raw 842 buffer(s) passed to the 842
338c2ecf20Sopenharmony_ci * software decompression library.
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * This does not fall back to software compression, however, since the caller
368c2ecf20Sopenharmony_ci * of this function is specifically requesting hardware compression; if the
378c2ecf20Sopenharmony_ci * hardware compression fails, the caller can fall back to software
388c2ecf20Sopenharmony_ci * compression, and the raw 842 compressed buffer that the software compressor
398c2ecf20Sopenharmony_ci * creates can be passed to this driver for hardware decompression; any
408c2ecf20Sopenharmony_ci * buffer without our specific header magic is assumed to be a raw 842 buffer
418c2ecf20Sopenharmony_ci * and passed directly to the hardware.  Note that the software compression
428c2ecf20Sopenharmony_ci * library will produce a compressed buffer that is incompatible with the
438c2ecf20Sopenharmony_ci * hardware decompressor if the original input buffer length is not a multiple
448c2ecf20Sopenharmony_ci * of 8; if such a compressed buffer is passed to this driver for
458c2ecf20Sopenharmony_ci * decompression, the hardware will reject it and this driver will then pass
468c2ecf20Sopenharmony_ci * it over to the software library for decompression.
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
528c2ecf20Sopenharmony_ci#include <linux/sw842.h>
538c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#include "nx-842.h"
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* The first 5 bits of this magic are 0x1f, which is an invalid 842 5-bit
588c2ecf20Sopenharmony_ci * template (see lib/842/842.h), so this magic number will never appear at
598c2ecf20Sopenharmony_ci * the start of a raw 842 compressed buffer.  That is important, as any buffer
608c2ecf20Sopenharmony_ci * passed to us without this magic is assumed to be a raw 842 compressed
618c2ecf20Sopenharmony_ci * buffer, and passed directly to the hardware to decompress.
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_ci#define NX842_CRYPTO_MAGIC	(0xf842)
648c2ecf20Sopenharmony_ci#define NX842_CRYPTO_HEADER_SIZE(g)				\
658c2ecf20Sopenharmony_ci	(sizeof(struct nx842_crypto_header) +			\
668c2ecf20Sopenharmony_ci	 sizeof(struct nx842_crypto_header_group) * (g))
678c2ecf20Sopenharmony_ci#define NX842_CRYPTO_HEADER_MAX_SIZE				\
688c2ecf20Sopenharmony_ci	NX842_CRYPTO_HEADER_SIZE(NX842_CRYPTO_GROUP_MAX)
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* bounce buffer size */
718c2ecf20Sopenharmony_ci#define BOUNCE_BUFFER_ORDER	(2)
728c2ecf20Sopenharmony_ci#define BOUNCE_BUFFER_SIZE					\
738c2ecf20Sopenharmony_ci	((unsigned int)(PAGE_SIZE << BOUNCE_BUFFER_ORDER))
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/* try longer on comp because we can fallback to sw decomp if hw is busy */
768c2ecf20Sopenharmony_ci#define COMP_BUSY_TIMEOUT	(250) /* ms */
778c2ecf20Sopenharmony_ci#define DECOMP_BUSY_TIMEOUT	(50) /* ms */
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistruct nx842_crypto_param {
808c2ecf20Sopenharmony_ci	u8 *in;
818c2ecf20Sopenharmony_ci	unsigned int iremain;
828c2ecf20Sopenharmony_ci	u8 *out;
838c2ecf20Sopenharmony_ci	unsigned int oremain;
848c2ecf20Sopenharmony_ci	unsigned int ototal;
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int update_param(struct nx842_crypto_param *p,
888c2ecf20Sopenharmony_ci			unsigned int slen, unsigned int dlen)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	if (p->iremain < slen)
918c2ecf20Sopenharmony_ci		return -EOVERFLOW;
928c2ecf20Sopenharmony_ci	if (p->oremain < dlen)
938c2ecf20Sopenharmony_ci		return -ENOSPC;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	p->in += slen;
968c2ecf20Sopenharmony_ci	p->iremain -= slen;
978c2ecf20Sopenharmony_ci	p->out += dlen;
988c2ecf20Sopenharmony_ci	p->oremain -= dlen;
998c2ecf20Sopenharmony_ci	p->ototal += dlen;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return 0;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ciint nx842_crypto_init(struct crypto_tfm *tfm, struct nx842_driver *driver)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	spin_lock_init(&ctx->lock);
1098c2ecf20Sopenharmony_ci	ctx->driver = driver;
1108c2ecf20Sopenharmony_ci	ctx->wmem = kmalloc(driver->workmem_size, GFP_KERNEL);
1118c2ecf20Sopenharmony_ci	ctx->sbounce = (u8 *)__get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER);
1128c2ecf20Sopenharmony_ci	ctx->dbounce = (u8 *)__get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER);
1138c2ecf20Sopenharmony_ci	if (!ctx->wmem || !ctx->sbounce || !ctx->dbounce) {
1148c2ecf20Sopenharmony_ci		kfree(ctx->wmem);
1158c2ecf20Sopenharmony_ci		free_page((unsigned long)ctx->sbounce);
1168c2ecf20Sopenharmony_ci		free_page((unsigned long)ctx->dbounce);
1178c2ecf20Sopenharmony_ci		return -ENOMEM;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return 0;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nx842_crypto_init);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_civoid nx842_crypto_exit(struct crypto_tfm *tfm)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	kfree(ctx->wmem);
1298c2ecf20Sopenharmony_ci	free_page((unsigned long)ctx->sbounce);
1308c2ecf20Sopenharmony_ci	free_page((unsigned long)ctx->dbounce);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nx842_crypto_exit);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic void check_constraints(struct nx842_constraints *c)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	/* limit maximum, to always have enough bounce buffer to decompress */
1378c2ecf20Sopenharmony_ci	if (c->maximum > BOUNCE_BUFFER_SIZE)
1388c2ecf20Sopenharmony_ci		c->maximum = BOUNCE_BUFFER_SIZE;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic int nx842_crypto_add_header(struct nx842_crypto_header *hdr, u8 *buf)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	int s = NX842_CRYPTO_HEADER_SIZE(hdr->groups);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* compress should have added space for header */
1468c2ecf20Sopenharmony_ci	if (s > be16_to_cpu(hdr->group[0].padding)) {
1478c2ecf20Sopenharmony_ci		pr_err("Internal error: no space for header\n");
1488c2ecf20Sopenharmony_ci		return -EINVAL;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	memcpy(buf, hdr, s);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	print_hex_dump_debug("header ", DUMP_PREFIX_OFFSET, 16, 1, buf, s, 0);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	return 0;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int compress(struct nx842_crypto_ctx *ctx,
1598c2ecf20Sopenharmony_ci		    struct nx842_crypto_param *p,
1608c2ecf20Sopenharmony_ci		    struct nx842_crypto_header_group *g,
1618c2ecf20Sopenharmony_ci		    struct nx842_constraints *c,
1628c2ecf20Sopenharmony_ci		    u16 *ignore,
1638c2ecf20Sopenharmony_ci		    unsigned int hdrsize)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	unsigned int slen = p->iremain, dlen = p->oremain, tmplen;
1668c2ecf20Sopenharmony_ci	unsigned int adj_slen = slen;
1678c2ecf20Sopenharmony_ci	u8 *src = p->in, *dst = p->out;
1688c2ecf20Sopenharmony_ci	int ret, dskip = 0;
1698c2ecf20Sopenharmony_ci	ktime_t timeout;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (p->iremain == 0)
1728c2ecf20Sopenharmony_ci		return -EOVERFLOW;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (p->oremain == 0 || hdrsize + c->minimum > dlen)
1758c2ecf20Sopenharmony_ci		return -ENOSPC;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (slen % c->multiple)
1788c2ecf20Sopenharmony_ci		adj_slen = round_up(slen, c->multiple);
1798c2ecf20Sopenharmony_ci	if (slen < c->minimum)
1808c2ecf20Sopenharmony_ci		adj_slen = c->minimum;
1818c2ecf20Sopenharmony_ci	if (slen > c->maximum)
1828c2ecf20Sopenharmony_ci		adj_slen = slen = c->maximum;
1838c2ecf20Sopenharmony_ci	if (adj_slen > slen || (u64)src % c->alignment) {
1848c2ecf20Sopenharmony_ci		adj_slen = min(adj_slen, BOUNCE_BUFFER_SIZE);
1858c2ecf20Sopenharmony_ci		slen = min(slen, BOUNCE_BUFFER_SIZE);
1868c2ecf20Sopenharmony_ci		if (adj_slen > slen)
1878c2ecf20Sopenharmony_ci			memset(ctx->sbounce + slen, 0, adj_slen - slen);
1888c2ecf20Sopenharmony_ci		memcpy(ctx->sbounce, src, slen);
1898c2ecf20Sopenharmony_ci		src = ctx->sbounce;
1908c2ecf20Sopenharmony_ci		slen = adj_slen;
1918c2ecf20Sopenharmony_ci		pr_debug("using comp sbounce buffer, len %x\n", slen);
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	dst += hdrsize;
1958c2ecf20Sopenharmony_ci	dlen -= hdrsize;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if ((u64)dst % c->alignment) {
1988c2ecf20Sopenharmony_ci		dskip = (int)(PTR_ALIGN(dst, c->alignment) - dst);
1998c2ecf20Sopenharmony_ci		dst += dskip;
2008c2ecf20Sopenharmony_ci		dlen -= dskip;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	if (dlen % c->multiple)
2038c2ecf20Sopenharmony_ci		dlen = round_down(dlen, c->multiple);
2048c2ecf20Sopenharmony_ci	if (dlen < c->minimum) {
2058c2ecf20Sopenharmony_cinospc:
2068c2ecf20Sopenharmony_ci		dst = ctx->dbounce;
2078c2ecf20Sopenharmony_ci		dlen = min(p->oremain, BOUNCE_BUFFER_SIZE);
2088c2ecf20Sopenharmony_ci		dlen = round_down(dlen, c->multiple);
2098c2ecf20Sopenharmony_ci		dskip = 0;
2108c2ecf20Sopenharmony_ci		pr_debug("using comp dbounce buffer, len %x\n", dlen);
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci	if (dlen > c->maximum)
2138c2ecf20Sopenharmony_ci		dlen = c->maximum;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	tmplen = dlen;
2168c2ecf20Sopenharmony_ci	timeout = ktime_add_ms(ktime_get(), COMP_BUSY_TIMEOUT);
2178c2ecf20Sopenharmony_ci	do {
2188c2ecf20Sopenharmony_ci		dlen = tmplen; /* reset dlen, if we're retrying */
2198c2ecf20Sopenharmony_ci		ret = ctx->driver->compress(src, slen, dst, &dlen, ctx->wmem);
2208c2ecf20Sopenharmony_ci		/* possibly we should reduce the slen here, instead of
2218c2ecf20Sopenharmony_ci		 * retrying with the dbounce buffer?
2228c2ecf20Sopenharmony_ci		 */
2238c2ecf20Sopenharmony_ci		if (ret == -ENOSPC && dst != ctx->dbounce)
2248c2ecf20Sopenharmony_ci			goto nospc;
2258c2ecf20Sopenharmony_ci	} while (ret == -EBUSY && ktime_before(ktime_get(), timeout));
2268c2ecf20Sopenharmony_ci	if (ret)
2278c2ecf20Sopenharmony_ci		return ret;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	dskip += hdrsize;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (dst == ctx->dbounce)
2328c2ecf20Sopenharmony_ci		memcpy(p->out + dskip, dst, dlen);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	g->padding = cpu_to_be16(dskip);
2358c2ecf20Sopenharmony_ci	g->compressed_length = cpu_to_be32(dlen);
2368c2ecf20Sopenharmony_ci	g->uncompressed_length = cpu_to_be32(slen);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (p->iremain < slen) {
2398c2ecf20Sopenharmony_ci		*ignore = slen - p->iremain;
2408c2ecf20Sopenharmony_ci		slen = p->iremain;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	pr_debug("compress slen %x ignore %x dlen %x padding %x\n",
2448c2ecf20Sopenharmony_ci		 slen, *ignore, dlen, dskip);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return update_param(p, slen, dskip + dlen);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ciint nx842_crypto_compress(struct crypto_tfm *tfm,
2508c2ecf20Sopenharmony_ci			  const u8 *src, unsigned int slen,
2518c2ecf20Sopenharmony_ci			  u8 *dst, unsigned int *dlen)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
2548c2ecf20Sopenharmony_ci	struct nx842_crypto_header *hdr = &ctx->header;
2558c2ecf20Sopenharmony_ci	struct nx842_crypto_param p;
2568c2ecf20Sopenharmony_ci	struct nx842_constraints c = *ctx->driver->constraints;
2578c2ecf20Sopenharmony_ci	unsigned int groups, hdrsize, h;
2588c2ecf20Sopenharmony_ci	int ret, n;
2598c2ecf20Sopenharmony_ci	bool add_header;
2608c2ecf20Sopenharmony_ci	u16 ignore = 0;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	check_constraints(&c);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	p.in = (u8 *)src;
2658c2ecf20Sopenharmony_ci	p.iremain = slen;
2668c2ecf20Sopenharmony_ci	p.out = dst;
2678c2ecf20Sopenharmony_ci	p.oremain = *dlen;
2688c2ecf20Sopenharmony_ci	p.ototal = 0;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	*dlen = 0;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	groups = min_t(unsigned int, NX842_CRYPTO_GROUP_MAX,
2738c2ecf20Sopenharmony_ci		       DIV_ROUND_UP(p.iremain, c.maximum));
2748c2ecf20Sopenharmony_ci	hdrsize = NX842_CRYPTO_HEADER_SIZE(groups);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	spin_lock_bh(&ctx->lock);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/* skip adding header if the buffers meet all constraints */
2798c2ecf20Sopenharmony_ci	add_header = (p.iremain % c.multiple	||
2808c2ecf20Sopenharmony_ci		      p.iremain < c.minimum	||
2818c2ecf20Sopenharmony_ci		      p.iremain > c.maximum	||
2828c2ecf20Sopenharmony_ci		      (u64)p.in % c.alignment	||
2838c2ecf20Sopenharmony_ci		      p.oremain % c.multiple	||
2848c2ecf20Sopenharmony_ci		      p.oremain < c.minimum	||
2858c2ecf20Sopenharmony_ci		      p.oremain > c.maximum	||
2868c2ecf20Sopenharmony_ci		      (u64)p.out % c.alignment);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	hdr->magic = cpu_to_be16(NX842_CRYPTO_MAGIC);
2898c2ecf20Sopenharmony_ci	hdr->groups = 0;
2908c2ecf20Sopenharmony_ci	hdr->ignore = 0;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	while (p.iremain > 0) {
2938c2ecf20Sopenharmony_ci		n = hdr->groups++;
2948c2ecf20Sopenharmony_ci		ret = -ENOSPC;
2958c2ecf20Sopenharmony_ci		if (hdr->groups > NX842_CRYPTO_GROUP_MAX)
2968c2ecf20Sopenharmony_ci			goto unlock;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		/* header goes before first group */
2998c2ecf20Sopenharmony_ci		h = !n && add_header ? hdrsize : 0;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci		if (ignore)
3028c2ecf20Sopenharmony_ci			pr_warn("internal error, ignore is set %x\n", ignore);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		ret = compress(ctx, &p, &hdr->group[n], &c, &ignore, h);
3058c2ecf20Sopenharmony_ci		if (ret)
3068c2ecf20Sopenharmony_ci			goto unlock;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (!add_header && hdr->groups > 1) {
3108c2ecf20Sopenharmony_ci		pr_err("Internal error: No header but multiple groups\n");
3118c2ecf20Sopenharmony_ci		ret = -EINVAL;
3128c2ecf20Sopenharmony_ci		goto unlock;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	/* ignore indicates the input stream needed to be padded */
3168c2ecf20Sopenharmony_ci	hdr->ignore = cpu_to_be16(ignore);
3178c2ecf20Sopenharmony_ci	if (ignore)
3188c2ecf20Sopenharmony_ci		pr_debug("marked %d bytes as ignore\n", ignore);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (add_header)
3218c2ecf20Sopenharmony_ci		ret = nx842_crypto_add_header(hdr, dst);
3228c2ecf20Sopenharmony_ci	if (ret)
3238c2ecf20Sopenharmony_ci		goto unlock;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	*dlen = p.ototal;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	pr_debug("compress total slen %x dlen %x\n", slen, *dlen);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ciunlock:
3308c2ecf20Sopenharmony_ci	spin_unlock_bh(&ctx->lock);
3318c2ecf20Sopenharmony_ci	return ret;
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nx842_crypto_compress);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic int decompress(struct nx842_crypto_ctx *ctx,
3368c2ecf20Sopenharmony_ci		      struct nx842_crypto_param *p,
3378c2ecf20Sopenharmony_ci		      struct nx842_crypto_header_group *g,
3388c2ecf20Sopenharmony_ci		      struct nx842_constraints *c,
3398c2ecf20Sopenharmony_ci		      u16 ignore)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	unsigned int slen = be32_to_cpu(g->compressed_length);
3428c2ecf20Sopenharmony_ci	unsigned int required_len = be32_to_cpu(g->uncompressed_length);
3438c2ecf20Sopenharmony_ci	unsigned int dlen = p->oremain, tmplen;
3448c2ecf20Sopenharmony_ci	unsigned int adj_slen = slen;
3458c2ecf20Sopenharmony_ci	u8 *src = p->in, *dst = p->out;
3468c2ecf20Sopenharmony_ci	u16 padding = be16_to_cpu(g->padding);
3478c2ecf20Sopenharmony_ci	int ret, spadding = 0;
3488c2ecf20Sopenharmony_ci	ktime_t timeout;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (!slen || !required_len)
3518c2ecf20Sopenharmony_ci		return -EINVAL;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if (p->iremain <= 0 || padding + slen > p->iremain)
3548c2ecf20Sopenharmony_ci		return -EOVERFLOW;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (p->oremain <= 0 || required_len - ignore > p->oremain)
3578c2ecf20Sopenharmony_ci		return -ENOSPC;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	src += padding;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (slen % c->multiple)
3628c2ecf20Sopenharmony_ci		adj_slen = round_up(slen, c->multiple);
3638c2ecf20Sopenharmony_ci	if (slen < c->minimum)
3648c2ecf20Sopenharmony_ci		adj_slen = c->minimum;
3658c2ecf20Sopenharmony_ci	if (slen > c->maximum)
3668c2ecf20Sopenharmony_ci		goto usesw;
3678c2ecf20Sopenharmony_ci	if (slen < adj_slen || (u64)src % c->alignment) {
3688c2ecf20Sopenharmony_ci		/* we can append padding bytes because the 842 format defines
3698c2ecf20Sopenharmony_ci		 * an "end" template (see lib/842/842_decompress.c) and will
3708c2ecf20Sopenharmony_ci		 * ignore any bytes following it.
3718c2ecf20Sopenharmony_ci		 */
3728c2ecf20Sopenharmony_ci		if (slen < adj_slen)
3738c2ecf20Sopenharmony_ci			memset(ctx->sbounce + slen, 0, adj_slen - slen);
3748c2ecf20Sopenharmony_ci		memcpy(ctx->sbounce, src, slen);
3758c2ecf20Sopenharmony_ci		src = ctx->sbounce;
3768c2ecf20Sopenharmony_ci		spadding = adj_slen - slen;
3778c2ecf20Sopenharmony_ci		slen = adj_slen;
3788c2ecf20Sopenharmony_ci		pr_debug("using decomp sbounce buffer, len %x\n", slen);
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (dlen % c->multiple)
3828c2ecf20Sopenharmony_ci		dlen = round_down(dlen, c->multiple);
3838c2ecf20Sopenharmony_ci	if (dlen < required_len || (u64)dst % c->alignment) {
3848c2ecf20Sopenharmony_ci		dst = ctx->dbounce;
3858c2ecf20Sopenharmony_ci		dlen = min(required_len, BOUNCE_BUFFER_SIZE);
3868c2ecf20Sopenharmony_ci		pr_debug("using decomp dbounce buffer, len %x\n", dlen);
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci	if (dlen < c->minimum)
3898c2ecf20Sopenharmony_ci		goto usesw;
3908c2ecf20Sopenharmony_ci	if (dlen > c->maximum)
3918c2ecf20Sopenharmony_ci		dlen = c->maximum;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	tmplen = dlen;
3948c2ecf20Sopenharmony_ci	timeout = ktime_add_ms(ktime_get(), DECOMP_BUSY_TIMEOUT);
3958c2ecf20Sopenharmony_ci	do {
3968c2ecf20Sopenharmony_ci		dlen = tmplen; /* reset dlen, if we're retrying */
3978c2ecf20Sopenharmony_ci		ret = ctx->driver->decompress(src, slen, dst, &dlen, ctx->wmem);
3988c2ecf20Sopenharmony_ci	} while (ret == -EBUSY && ktime_before(ktime_get(), timeout));
3998c2ecf20Sopenharmony_ci	if (ret) {
4008c2ecf20Sopenharmony_ciusesw:
4018c2ecf20Sopenharmony_ci		/* reset everything, sw doesn't have constraints */
4028c2ecf20Sopenharmony_ci		src = p->in + padding;
4038c2ecf20Sopenharmony_ci		slen = be32_to_cpu(g->compressed_length);
4048c2ecf20Sopenharmony_ci		spadding = 0;
4058c2ecf20Sopenharmony_ci		dst = p->out;
4068c2ecf20Sopenharmony_ci		dlen = p->oremain;
4078c2ecf20Sopenharmony_ci		if (dlen < required_len) { /* have ignore bytes */
4088c2ecf20Sopenharmony_ci			dst = ctx->dbounce;
4098c2ecf20Sopenharmony_ci			dlen = BOUNCE_BUFFER_SIZE;
4108c2ecf20Sopenharmony_ci		}
4118c2ecf20Sopenharmony_ci		pr_info_ratelimited("using software 842 decompression\n");
4128c2ecf20Sopenharmony_ci		ret = sw842_decompress(src, slen, dst, &dlen);
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci	if (ret)
4158c2ecf20Sopenharmony_ci		return ret;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	slen -= spadding;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	dlen -= ignore;
4208c2ecf20Sopenharmony_ci	if (ignore)
4218c2ecf20Sopenharmony_ci		pr_debug("ignoring last %x bytes\n", ignore);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (dst == ctx->dbounce)
4248c2ecf20Sopenharmony_ci		memcpy(p->out, dst, dlen);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	pr_debug("decompress slen %x padding %x dlen %x ignore %x\n",
4278c2ecf20Sopenharmony_ci		 slen, padding, dlen, ignore);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return update_param(p, slen + padding, dlen);
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ciint nx842_crypto_decompress(struct crypto_tfm *tfm,
4338c2ecf20Sopenharmony_ci			    const u8 *src, unsigned int slen,
4348c2ecf20Sopenharmony_ci			    u8 *dst, unsigned int *dlen)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
4378c2ecf20Sopenharmony_ci	struct nx842_crypto_header *hdr;
4388c2ecf20Sopenharmony_ci	struct nx842_crypto_param p;
4398c2ecf20Sopenharmony_ci	struct nx842_constraints c = *ctx->driver->constraints;
4408c2ecf20Sopenharmony_ci	int n, ret, hdr_len;
4418c2ecf20Sopenharmony_ci	u16 ignore = 0;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	check_constraints(&c);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	p.in = (u8 *)src;
4468c2ecf20Sopenharmony_ci	p.iremain = slen;
4478c2ecf20Sopenharmony_ci	p.out = dst;
4488c2ecf20Sopenharmony_ci	p.oremain = *dlen;
4498c2ecf20Sopenharmony_ci	p.ototal = 0;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	*dlen = 0;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	hdr = (struct nx842_crypto_header *)src;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	spin_lock_bh(&ctx->lock);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* If it doesn't start with our header magic number, assume it's a raw
4588c2ecf20Sopenharmony_ci	 * 842 compressed buffer and pass it directly to the hardware driver
4598c2ecf20Sopenharmony_ci	 */
4608c2ecf20Sopenharmony_ci	if (be16_to_cpu(hdr->magic) != NX842_CRYPTO_MAGIC) {
4618c2ecf20Sopenharmony_ci		struct nx842_crypto_header_group g = {
4628c2ecf20Sopenharmony_ci			.padding =		0,
4638c2ecf20Sopenharmony_ci			.compressed_length =	cpu_to_be32(p.iremain),
4648c2ecf20Sopenharmony_ci			.uncompressed_length =	cpu_to_be32(p.oremain),
4658c2ecf20Sopenharmony_ci		};
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci		ret = decompress(ctx, &p, &g, &c, 0);
4688c2ecf20Sopenharmony_ci		if (ret)
4698c2ecf20Sopenharmony_ci			goto unlock;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		goto success;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (!hdr->groups) {
4758c2ecf20Sopenharmony_ci		pr_err("header has no groups\n");
4768c2ecf20Sopenharmony_ci		ret = -EINVAL;
4778c2ecf20Sopenharmony_ci		goto unlock;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci	if (hdr->groups > NX842_CRYPTO_GROUP_MAX) {
4808c2ecf20Sopenharmony_ci		pr_err("header has too many groups %x, max %x\n",
4818c2ecf20Sopenharmony_ci		       hdr->groups, NX842_CRYPTO_GROUP_MAX);
4828c2ecf20Sopenharmony_ci		ret = -EINVAL;
4838c2ecf20Sopenharmony_ci		goto unlock;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	hdr_len = NX842_CRYPTO_HEADER_SIZE(hdr->groups);
4878c2ecf20Sopenharmony_ci	if (hdr_len > slen) {
4888c2ecf20Sopenharmony_ci		ret = -EOVERFLOW;
4898c2ecf20Sopenharmony_ci		goto unlock;
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	memcpy(&ctx->header, src, hdr_len);
4938c2ecf20Sopenharmony_ci	hdr = &ctx->header;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	for (n = 0; n < hdr->groups; n++) {
4968c2ecf20Sopenharmony_ci		/* ignore applies to last group */
4978c2ecf20Sopenharmony_ci		if (n + 1 == hdr->groups)
4988c2ecf20Sopenharmony_ci			ignore = be16_to_cpu(hdr->ignore);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci		ret = decompress(ctx, &p, &hdr->group[n], &c, ignore);
5018c2ecf20Sopenharmony_ci		if (ret)
5028c2ecf20Sopenharmony_ci			goto unlock;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cisuccess:
5068c2ecf20Sopenharmony_ci	*dlen = p.ototal;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	pr_debug("decompress total slen %x dlen %x\n", slen, *dlen);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	ret = 0;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ciunlock:
5138c2ecf20Sopenharmony_ci	spin_unlock_bh(&ctx->lock);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	return ret;
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nx842_crypto_decompress);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5208c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IBM PowerPC Nest (NX) 842 Hardware Compression Driver");
5218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
522