1*c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
22da572c9SDan Streetman /*
32da572c9SDan Streetman * 842 Software Decompression
42da572c9SDan Streetman *
52da572c9SDan Streetman * Copyright (C) 2015 Dan Streetman, IBM Corp
62da572c9SDan Streetman *
72da572c9SDan Streetman * See 842.h for details of the 842 compressed format.
82da572c9SDan Streetman */
92da572c9SDan Streetman
102da572c9SDan Streetman #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
112da572c9SDan Streetman #define MODULE_NAME "842_decompress"
122da572c9SDan Streetman
132da572c9SDan Streetman #include "842.h"
142da572c9SDan Streetman #include "842_debugfs.h"
152da572c9SDan Streetman
162da572c9SDan Streetman /* rolling fifo sizes */
172da572c9SDan Streetman #define I2_FIFO_SIZE (2 * (1 << I2_BITS))
182da572c9SDan Streetman #define I4_FIFO_SIZE (4 * (1 << I4_BITS))
192da572c9SDan Streetman #define I8_FIFO_SIZE (8 * (1 << I8_BITS))
202da572c9SDan Streetman
212da572c9SDan Streetman static u8 decomp_ops[OPS_MAX][4] = {
222da572c9SDan Streetman { D8, N0, N0, N0 },
232da572c9SDan Streetman { D4, D2, I2, N0 },
242da572c9SDan Streetman { D4, I2, D2, N0 },
252da572c9SDan Streetman { D4, I2, I2, N0 },
262da572c9SDan Streetman { D4, I4, N0, N0 },
272da572c9SDan Streetman { D2, I2, D4, N0 },
282da572c9SDan Streetman { D2, I2, D2, I2 },
292da572c9SDan Streetman { D2, I2, I2, D2 },
302da572c9SDan Streetman { D2, I2, I2, I2 },
312da572c9SDan Streetman { D2, I2, I4, N0 },
322da572c9SDan Streetman { I2, D2, D4, N0 },
332da572c9SDan Streetman { I2, D4, I2, N0 },
342da572c9SDan Streetman { I2, D2, I2, D2 },
352da572c9SDan Streetman { I2, D2, I2, I2 },
362da572c9SDan Streetman { I2, D2, I4, N0 },
372da572c9SDan Streetman { I2, I2, D4, N0 },
382da572c9SDan Streetman { I2, I2, D2, I2 },
392da572c9SDan Streetman { I2, I2, I2, D2 },
402da572c9SDan Streetman { I2, I2, I2, I2 },
412da572c9SDan Streetman { I2, I2, I4, N0 },
422da572c9SDan Streetman { I4, D4, N0, N0 },
432da572c9SDan Streetman { I4, D2, I2, N0 },
442da572c9SDan Streetman { I4, I2, D2, N0 },
452da572c9SDan Streetman { I4, I2, I2, N0 },
462da572c9SDan Streetman { I4, I4, N0, N0 },
472da572c9SDan Streetman { I8, N0, N0, N0 }
482da572c9SDan Streetman };
492da572c9SDan Streetman
502da572c9SDan Streetman struct sw842_param {
512da572c9SDan Streetman u8 *in;
522da572c9SDan Streetman u8 bit;
532da572c9SDan Streetman u64 ilen;
542da572c9SDan Streetman u8 *out;
552da572c9SDan Streetman u8 *ostart;
562da572c9SDan Streetman u64 olen;
572da572c9SDan Streetman };
582da572c9SDan Streetman
592da572c9SDan Streetman #define beN_to_cpu(d, s) \
602da572c9SDan Streetman ((s) == 2 ? be16_to_cpu(get_unaligned((__be16 *)d)) : \
612da572c9SDan Streetman (s) == 4 ? be32_to_cpu(get_unaligned((__be32 *)d)) : \
622da572c9SDan Streetman (s) == 8 ? be64_to_cpu(get_unaligned((__be64 *)d)) : \
635ca636b9SDan Streetman 0)
642da572c9SDan Streetman
652da572c9SDan Streetman static int next_bits(struct sw842_param *p, u64 *d, u8 n);
662da572c9SDan Streetman
__split_next_bits(struct sw842_param * p,u64 * d,u8 n,u8 s)672da572c9SDan Streetman static int __split_next_bits(struct sw842_param *p, u64 *d, u8 n, u8 s)
682da572c9SDan Streetman {
692da572c9SDan Streetman u64 tmp = 0;
702da572c9SDan Streetman int ret;
712da572c9SDan Streetman
722da572c9SDan Streetman if (n <= s) {
732da572c9SDan Streetman pr_debug("split_next_bits invalid n %u s %u\n", n, s);
742da572c9SDan Streetman return -EINVAL;
752da572c9SDan Streetman }
762da572c9SDan Streetman
772da572c9SDan Streetman ret = next_bits(p, &tmp, n - s);
782da572c9SDan Streetman if (ret)
792da572c9SDan Streetman return ret;
802da572c9SDan Streetman ret = next_bits(p, d, s);
812da572c9SDan Streetman if (ret)
822da572c9SDan Streetman return ret;
832da572c9SDan Streetman *d |= tmp << s;
842da572c9SDan Streetman return 0;
852da572c9SDan Streetman }
862da572c9SDan Streetman
next_bits(struct sw842_param * p,u64 * d,u8 n)872da572c9SDan Streetman static int next_bits(struct sw842_param *p, u64 *d, u8 n)
882da572c9SDan Streetman {
892da572c9SDan Streetman u8 *in = p->in, b = p->bit, bits = b + n;
902da572c9SDan Streetman
912da572c9SDan Streetman if (n > 64) {
922da572c9SDan Streetman pr_debug("next_bits invalid n %u\n", n);
932da572c9SDan Streetman return -EINVAL;
942da572c9SDan Streetman }
952da572c9SDan Streetman
962da572c9SDan Streetman /* split this up if reading > 8 bytes, or if we're at the end of
972da572c9SDan Streetman * the input buffer and would read past the end
982da572c9SDan Streetman */
992da572c9SDan Streetman if (bits > 64)
1002da572c9SDan Streetman return __split_next_bits(p, d, n, 32);
1012da572c9SDan Streetman else if (p->ilen < 8 && bits > 32 && bits <= 56)
1022da572c9SDan Streetman return __split_next_bits(p, d, n, 16);
1032da572c9SDan Streetman else if (p->ilen < 4 && bits > 16 && bits <= 24)
1042da572c9SDan Streetman return __split_next_bits(p, d, n, 8);
1052da572c9SDan Streetman
1062da572c9SDan Streetman if (DIV_ROUND_UP(bits, 8) > p->ilen)
1072da572c9SDan Streetman return -EOVERFLOW;
1082da572c9SDan Streetman
1092da572c9SDan Streetman if (bits <= 8)
1102da572c9SDan Streetman *d = *in >> (8 - bits);
1112da572c9SDan Streetman else if (bits <= 16)
1122da572c9SDan Streetman *d = be16_to_cpu(get_unaligned((__be16 *)in)) >> (16 - bits);
1132da572c9SDan Streetman else if (bits <= 32)
1142da572c9SDan Streetman *d = be32_to_cpu(get_unaligned((__be32 *)in)) >> (32 - bits);
1152da572c9SDan Streetman else
1162da572c9SDan Streetman *d = be64_to_cpu(get_unaligned((__be64 *)in)) >> (64 - bits);
1172da572c9SDan Streetman
1182da572c9SDan Streetman *d &= GENMASK_ULL(n - 1, 0);
1192da572c9SDan Streetman
1202da572c9SDan Streetman p->bit += n;
1212da572c9SDan Streetman
1222da572c9SDan Streetman if (p->bit > 7) {
1232da572c9SDan Streetman p->in += p->bit / 8;
1242da572c9SDan Streetman p->ilen -= p->bit / 8;
1252da572c9SDan Streetman p->bit %= 8;
1262da572c9SDan Streetman }
1272da572c9SDan Streetman
1282da572c9SDan Streetman return 0;
1292da572c9SDan Streetman }
1302da572c9SDan Streetman
do_data(struct sw842_param * p,u8 n)1312da572c9SDan Streetman static int do_data(struct sw842_param *p, u8 n)
1322da572c9SDan Streetman {
1332da572c9SDan Streetman u64 v;
1342da572c9SDan Streetman int ret;
1352da572c9SDan Streetman
1362da572c9SDan Streetman if (n > p->olen)
1372da572c9SDan Streetman return -ENOSPC;
1382da572c9SDan Streetman
1392da572c9SDan Streetman ret = next_bits(p, &v, n * 8);
1402da572c9SDan Streetman if (ret)
1412da572c9SDan Streetman return ret;
1422da572c9SDan Streetman
1432da572c9SDan Streetman switch (n) {
1442da572c9SDan Streetman case 2:
1452da572c9SDan Streetman put_unaligned(cpu_to_be16((u16)v), (__be16 *)p->out);
1462da572c9SDan Streetman break;
1472da572c9SDan Streetman case 4:
1482da572c9SDan Streetman put_unaligned(cpu_to_be32((u32)v), (__be32 *)p->out);
1492da572c9SDan Streetman break;
1502da572c9SDan Streetman case 8:
1512da572c9SDan Streetman put_unaligned(cpu_to_be64((u64)v), (__be64 *)p->out);
1522da572c9SDan Streetman break;
1532da572c9SDan Streetman default:
1542da572c9SDan Streetman return -EINVAL;
1552da572c9SDan Streetman }
1562da572c9SDan Streetman
1572da572c9SDan Streetman p->out += n;
1582da572c9SDan Streetman p->olen -= n;
1592da572c9SDan Streetman
1602da572c9SDan Streetman return 0;
1612da572c9SDan Streetman }
1622da572c9SDan Streetman
__do_index(struct sw842_param * p,u8 size,u8 bits,u64 fsize)1632da572c9SDan Streetman static int __do_index(struct sw842_param *p, u8 size, u8 bits, u64 fsize)
1642da572c9SDan Streetman {
1652da572c9SDan Streetman u64 index, offset, total = round_down(p->out - p->ostart, 8);
1662da572c9SDan Streetman int ret;
1672da572c9SDan Streetman
1682da572c9SDan Streetman ret = next_bits(p, &index, bits);
1692da572c9SDan Streetman if (ret)
1702da572c9SDan Streetman return ret;
1712da572c9SDan Streetman
1722da572c9SDan Streetman offset = index * size;
1732da572c9SDan Streetman
1742da572c9SDan Streetman /* a ring buffer of fsize is used; correct the offset */
1752da572c9SDan Streetman if (total > fsize) {
1762da572c9SDan Streetman /* this is where the current fifo is */
1772da572c9SDan Streetman u64 section = round_down(total, fsize);
1782da572c9SDan Streetman /* the current pos in the fifo */
179ca7fc7e9SDan Streetman u64 pos = total - section;
1802da572c9SDan Streetman
1812da572c9SDan Streetman /* if the offset is past/at the pos, we need to
1822da572c9SDan Streetman * go back to the last fifo section
1832da572c9SDan Streetman */
1842da572c9SDan Streetman if (offset >= pos)
1852da572c9SDan Streetman section -= fsize;
1862da572c9SDan Streetman
1872da572c9SDan Streetman offset += section;
1882da572c9SDan Streetman }
1892da572c9SDan Streetman
1902da572c9SDan Streetman if (offset + size > total) {
1912da572c9SDan Streetman pr_debug("index%x %lx points past end %lx\n", size,
1922da572c9SDan Streetman (unsigned long)offset, (unsigned long)total);
1932da572c9SDan Streetman return -EINVAL;
1942da572c9SDan Streetman }
1952da572c9SDan Streetman
1965ca636b9SDan Streetman if (size != 2 && size != 4 && size != 8)
1975ca636b9SDan Streetman WARN(1, "__do_index invalid size %x\n", size);
1985ca636b9SDan Streetman else
1992da572c9SDan Streetman pr_debug("index%x to %lx off %lx adjoff %lx tot %lx data %lx\n",
2005ca636b9SDan Streetman size, (unsigned long)index,
2015ca636b9SDan Streetman (unsigned long)(index * size), (unsigned long)offset,
2025ca636b9SDan Streetman (unsigned long)total,
2032da572c9SDan Streetman (unsigned long)beN_to_cpu(&p->ostart[offset], size));
2042da572c9SDan Streetman
2052da572c9SDan Streetman memcpy(p->out, &p->ostart[offset], size);
2062da572c9SDan Streetman p->out += size;
2072da572c9SDan Streetman p->olen -= size;
2082da572c9SDan Streetman
2092da572c9SDan Streetman return 0;
2102da572c9SDan Streetman }
2112da572c9SDan Streetman
do_index(struct sw842_param * p,u8 n)212f7ead7b4SDan Streetman static int do_index(struct sw842_param *p, u8 n)
2132da572c9SDan Streetman {
2142da572c9SDan Streetman switch (n) {
2152da572c9SDan Streetman case 2:
2162da572c9SDan Streetman return __do_index(p, 2, I2_BITS, I2_FIFO_SIZE);
2172da572c9SDan Streetman case 4:
2182da572c9SDan Streetman return __do_index(p, 4, I4_BITS, I4_FIFO_SIZE);
2192da572c9SDan Streetman case 8:
2202da572c9SDan Streetman return __do_index(p, 8, I8_BITS, I8_FIFO_SIZE);
2212da572c9SDan Streetman default:
2222da572c9SDan Streetman return -EINVAL;
2232da572c9SDan Streetman }
2242da572c9SDan Streetman }
2252da572c9SDan Streetman
do_op(struct sw842_param * p,u8 o)226f7ead7b4SDan Streetman static int do_op(struct sw842_param *p, u8 o)
2272da572c9SDan Streetman {
2282da572c9SDan Streetman int i, ret = 0;
2292da572c9SDan Streetman
2302da572c9SDan Streetman if (o >= OPS_MAX)
2312da572c9SDan Streetman return -EINVAL;
2322da572c9SDan Streetman
2332da572c9SDan Streetman for (i = 0; i < 4; i++) {
2342da572c9SDan Streetman u8 op = decomp_ops[o][i];
2352da572c9SDan Streetman
2362da572c9SDan Streetman pr_debug("op is %x\n", op);
2372da572c9SDan Streetman
2382da572c9SDan Streetman switch (op & OP_ACTION) {
2392da572c9SDan Streetman case OP_ACTION_DATA:
2402da572c9SDan Streetman ret = do_data(p, op & OP_AMOUNT);
2412da572c9SDan Streetman break;
2422da572c9SDan Streetman case OP_ACTION_INDEX:
2432da572c9SDan Streetman ret = do_index(p, op & OP_AMOUNT);
2442da572c9SDan Streetman break;
2452da572c9SDan Streetman case OP_ACTION_NOOP:
2462da572c9SDan Streetman break;
2472da572c9SDan Streetman default:
248fc4fa6e1SMasanari Iida pr_err("Internal error, invalid op %x\n", op);
2492da572c9SDan Streetman return -EINVAL;
2502da572c9SDan Streetman }
2512da572c9SDan Streetman
2522da572c9SDan Streetman if (ret)
2532da572c9SDan Streetman return ret;
2542da572c9SDan Streetman }
2552da572c9SDan Streetman
2562da572c9SDan Streetman if (sw842_template_counts)
2572da572c9SDan Streetman atomic_inc(&template_count[o]);
2582da572c9SDan Streetman
2592da572c9SDan Streetman return 0;
2602da572c9SDan Streetman }
2612da572c9SDan Streetman
2622da572c9SDan Streetman /**
2632da572c9SDan Streetman * sw842_decompress
2642da572c9SDan Streetman *
2652da572c9SDan Streetman * Decompress the 842-compressed buffer of length @ilen at @in
2662da572c9SDan Streetman * to the output buffer @out, using no more than @olen bytes.
2672da572c9SDan Streetman *
2682da572c9SDan Streetman * The compressed buffer must be only a single 842-compressed buffer,
2692da572c9SDan Streetman * with the standard format described in the comments in 842.h
2702da572c9SDan Streetman * Processing will stop when the 842 "END" template is detected,
2712da572c9SDan Streetman * not the end of the buffer.
2722da572c9SDan Streetman *
2732da572c9SDan Streetman * Returns: 0 on success, error on failure. The @olen parameter
2742da572c9SDan Streetman * will contain the number of output bytes written on success, or
2752da572c9SDan Streetman * 0 on error.
2762da572c9SDan Streetman */
sw842_decompress(const u8 * in,unsigned int ilen,u8 * out,unsigned int * olen)2772da572c9SDan Streetman int sw842_decompress(const u8 *in, unsigned int ilen,
2782da572c9SDan Streetman u8 *out, unsigned int *olen)
2792da572c9SDan Streetman {
2802da572c9SDan Streetman struct sw842_param p;
2812da572c9SDan Streetman int ret;
2822da572c9SDan Streetman u64 op, rep, tmp, bytes, total;
283ea0b3984SHaren Myneni u64 crc;
2842da572c9SDan Streetman
2852da572c9SDan Streetman p.in = (u8 *)in;
2862da572c9SDan Streetman p.bit = 0;
2872da572c9SDan Streetman p.ilen = ilen;
2882da572c9SDan Streetman p.out = out;
2892da572c9SDan Streetman p.ostart = out;
2902da572c9SDan Streetman p.olen = *olen;
2912da572c9SDan Streetman
2922da572c9SDan Streetman total = p.olen;
2932da572c9SDan Streetman
2942da572c9SDan Streetman *olen = 0;
2952da572c9SDan Streetman
2962da572c9SDan Streetman do {
2972da572c9SDan Streetman ret = next_bits(&p, &op, OP_BITS);
2982da572c9SDan Streetman if (ret)
2992da572c9SDan Streetman return ret;
3002da572c9SDan Streetman
3012da572c9SDan Streetman pr_debug("template is %lx\n", (unsigned long)op);
3022da572c9SDan Streetman
3032da572c9SDan Streetman switch (op) {
3042da572c9SDan Streetman case OP_REPEAT:
3052da572c9SDan Streetman ret = next_bits(&p, &rep, REPEAT_BITS);
3062da572c9SDan Streetman if (ret)
3072da572c9SDan Streetman return ret;
3082da572c9SDan Streetman
3092da572c9SDan Streetman if (p.out == out) /* no previous bytes */
3102da572c9SDan Streetman return -EINVAL;
3112da572c9SDan Streetman
3122da572c9SDan Streetman /* copy rep + 1 */
3132da572c9SDan Streetman rep++;
3142da572c9SDan Streetman
3152da572c9SDan Streetman if (rep * 8 > p.olen)
3162da572c9SDan Streetman return -ENOSPC;
3172da572c9SDan Streetman
3182da572c9SDan Streetman while (rep-- > 0) {
3192da572c9SDan Streetman memcpy(p.out, p.out - 8, 8);
3202da572c9SDan Streetman p.out += 8;
3212da572c9SDan Streetman p.olen -= 8;
3222da572c9SDan Streetman }
3232da572c9SDan Streetman
3242da572c9SDan Streetman if (sw842_template_counts)
3252da572c9SDan Streetman atomic_inc(&template_repeat_count);
3262da572c9SDan Streetman
3272da572c9SDan Streetman break;
3282da572c9SDan Streetman case OP_ZEROS:
3292da572c9SDan Streetman if (8 > p.olen)
3302da572c9SDan Streetman return -ENOSPC;
3312da572c9SDan Streetman
3322da572c9SDan Streetman memset(p.out, 0, 8);
3332da572c9SDan Streetman p.out += 8;
3342da572c9SDan Streetman p.olen -= 8;
3352da572c9SDan Streetman
3362da572c9SDan Streetman if (sw842_template_counts)
3372da572c9SDan Streetman atomic_inc(&template_zeros_count);
3382da572c9SDan Streetman
3392da572c9SDan Streetman break;
3402da572c9SDan Streetman case OP_SHORT_DATA:
3412da572c9SDan Streetman ret = next_bits(&p, &bytes, SHORT_DATA_BITS);
3422da572c9SDan Streetman if (ret)
3432da572c9SDan Streetman return ret;
3442da572c9SDan Streetman
3452da572c9SDan Streetman if (!bytes || bytes > SHORT_DATA_BITS_MAX)
3462da572c9SDan Streetman return -EINVAL;
3472da572c9SDan Streetman
3482da572c9SDan Streetman while (bytes-- > 0) {
3492da572c9SDan Streetman ret = next_bits(&p, &tmp, 8);
3502da572c9SDan Streetman if (ret)
3512da572c9SDan Streetman return ret;
3522da572c9SDan Streetman *p.out = (u8)tmp;
3532da572c9SDan Streetman p.out++;
3542da572c9SDan Streetman p.olen--;
3552da572c9SDan Streetman }
3562da572c9SDan Streetman
3572da572c9SDan Streetman if (sw842_template_counts)
3582da572c9SDan Streetman atomic_inc(&template_short_data_count);
3592da572c9SDan Streetman
3602da572c9SDan Streetman break;
3612da572c9SDan Streetman case OP_END:
3622da572c9SDan Streetman if (sw842_template_counts)
3632da572c9SDan Streetman atomic_inc(&template_end_count);
3642da572c9SDan Streetman
3652da572c9SDan Streetman break;
3662da572c9SDan Streetman default: /* use template */
3672da572c9SDan Streetman ret = do_op(&p, op);
3682da572c9SDan Streetman if (ret)
3692da572c9SDan Streetman return ret;
3702da572c9SDan Streetman break;
3712da572c9SDan Streetman }
3722da572c9SDan Streetman } while (op != OP_END);
3732da572c9SDan Streetman
374ea0b3984SHaren Myneni /*
375ea0b3984SHaren Myneni * crc(0:31) is saved in compressed data starting with the
376ea0b3984SHaren Myneni * next bit after End of stream template.
377ea0b3984SHaren Myneni */
378ea0b3984SHaren Myneni ret = next_bits(&p, &crc, CRC_BITS);
379ea0b3984SHaren Myneni if (ret)
380ea0b3984SHaren Myneni return ret;
381ea0b3984SHaren Myneni
382ea0b3984SHaren Myneni /*
383ea0b3984SHaren Myneni * Validate CRC saved in compressed data.
384ea0b3984SHaren Myneni */
385ea0b3984SHaren Myneni if (crc != (u64)crc32_be(0, out, total - p.olen)) {
386ea0b3984SHaren Myneni pr_debug("CRC mismatch for decompression\n");
387ea0b3984SHaren Myneni return -EINVAL;
388ea0b3984SHaren Myneni }
389ea0b3984SHaren Myneni
3902da572c9SDan Streetman if (unlikely((total - p.olen) > UINT_MAX))
3912da572c9SDan Streetman return -ENOSPC;
3922da572c9SDan Streetman
3932da572c9SDan Streetman *olen = total - p.olen;
3942da572c9SDan Streetman
3952da572c9SDan Streetman return 0;
3962da572c9SDan Streetman }
3972da572c9SDan Streetman EXPORT_SYMBOL_GPL(sw842_decompress);
3982da572c9SDan Streetman
sw842_init(void)3992da572c9SDan Streetman static int __init sw842_init(void)
4002da572c9SDan Streetman {
4012da572c9SDan Streetman if (sw842_template_counts)
4022da572c9SDan Streetman sw842_debugfs_create();
4032da572c9SDan Streetman
4042da572c9SDan Streetman return 0;
4052da572c9SDan Streetman }
4062da572c9SDan Streetman module_init(sw842_init);
4072da572c9SDan Streetman
sw842_exit(void)4082da572c9SDan Streetman static void __exit sw842_exit(void)
4092da572c9SDan Streetman {
4102da572c9SDan Streetman if (sw842_template_counts)
4112da572c9SDan Streetman sw842_debugfs_remove();
4122da572c9SDan Streetman }
4132da572c9SDan Streetman module_exit(sw842_exit);
4142da572c9SDan Streetman
4152da572c9SDan Streetman MODULE_LICENSE("GPL");
4162da572c9SDan Streetman MODULE_DESCRIPTION("Software 842 Decompressor");
4172da572c9SDan Streetman MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
418