linux/lib/842/842_decompress.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * 842 Software Decompression
   4 *
   5 * Copyright (C) 2015 Dan Streetman, IBM Corp
   6 *
   7 * See 842.h for details of the 842 compressed format.
   8 */
   9
  10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  11#define MODULE_NAME "842_decompress"
  12
  13#include "842.h"
  14#include "842_debugfs.h"
  15
  16/* rolling fifo sizes */
  17#define I2_FIFO_SIZE    (2 * (1 << I2_BITS))
  18#define I4_FIFO_SIZE    (4 * (1 << I4_BITS))
  19#define I8_FIFO_SIZE    (8 * (1 << I8_BITS))
  20
  21static u8 decomp_ops[OPS_MAX][4] = {
  22        { D8, N0, N0, N0 },
  23        { D4, D2, I2, N0 },
  24        { D4, I2, D2, N0 },
  25        { D4, I2, I2, N0 },
  26        { D4, I4, N0, N0 },
  27        { D2, I2, D4, N0 },
  28        { D2, I2, D2, I2 },
  29        { D2, I2, I2, D2 },
  30        { D2, I2, I2, I2 },
  31        { D2, I2, I4, N0 },
  32        { I2, D2, D4, N0 },
  33        { I2, D4, I2, N0 },
  34        { I2, D2, I2, D2 },
  35        { I2, D2, I2, I2 },
  36        { I2, D2, I4, N0 },
  37        { I2, I2, D4, N0 },
  38        { I2, I2, D2, I2 },
  39        { I2, I2, I2, D2 },
  40        { I2, I2, I2, I2 },
  41        { I2, I2, I4, N0 },
  42        { I4, D4, N0, N0 },
  43        { I4, D2, I2, N0 },
  44        { I4, I2, D2, N0 },
  45        { I4, I2, I2, N0 },
  46        { I4, I4, N0, N0 },
  47        { I8, N0, N0, N0 }
  48};
  49
  50struct sw842_param {
  51        u8 *in;
  52        u8 bit;
  53        u64 ilen;
  54        u8 *out;
  55        u8 *ostart;
  56        u64 olen;
  57};
  58
  59#define beN_to_cpu(d, s)                                        \
  60        ((s) == 2 ? be16_to_cpu(get_unaligned((__be16 *)d)) :   \
  61         (s) == 4 ? be32_to_cpu(get_unaligned((__be32 *)d)) :   \
  62         (s) == 8 ? be64_to_cpu(get_unaligned((__be64 *)d)) :   \
  63         0)
  64
  65static int next_bits(struct sw842_param *p, u64 *d, u8 n);
  66
  67static int __split_next_bits(struct sw842_param *p, u64 *d, u8 n, u8 s)
  68{
  69        u64 tmp = 0;
  70        int ret;
  71
  72        if (n <= s) {
  73                pr_debug("split_next_bits invalid n %u s %u\n", n, s);
  74                return -EINVAL;
  75        }
  76
  77        ret = next_bits(p, &tmp, n - s);
  78        if (ret)
  79                return ret;
  80        ret = next_bits(p, d, s);
  81        if (ret)
  82                return ret;
  83        *d |= tmp << s;
  84        return 0;
  85}
  86
  87static int next_bits(struct sw842_param *p, u64 *d, u8 n)
  88{
  89        u8 *in = p->in, b = p->bit, bits = b + n;
  90
  91        if (n > 64) {
  92                pr_debug("next_bits invalid n %u\n", n);
  93                return -EINVAL;
  94        }
  95
  96        /* split this up if reading > 8 bytes, or if we're at the end of
  97         * the input buffer and would read past the end
  98         */
  99        if (bits > 64)
 100                return __split_next_bits(p, d, n, 32);
 101        else if (p->ilen < 8 && bits > 32 && bits <= 56)
 102                return __split_next_bits(p, d, n, 16);
 103        else if (p->ilen < 4 && bits > 16 && bits <= 24)
 104                return __split_next_bits(p, d, n, 8);
 105
 106        if (DIV_ROUND_UP(bits, 8) > p->ilen)
 107                return -EOVERFLOW;
 108
 109        if (bits <= 8)
 110                *d = *in >> (8 - bits);
 111        else if (bits <= 16)
 112                *d = be16_to_cpu(get_unaligned((__be16 *)in)) >> (16 - bits);
 113        else if (bits <= 32)
 114                *d = be32_to_cpu(get_unaligned((__be32 *)in)) >> (32 - bits);
 115        else
 116                *d = be64_to_cpu(get_unaligned((__be64 *)in)) >> (64 - bits);
 117
 118        *d &= GENMASK_ULL(n - 1, 0);
 119
 120        p->bit += n;
 121
 122        if (p->bit > 7) {
 123                p->in += p->bit / 8;
 124                p->ilen -= p->bit / 8;
 125                p->bit %= 8;
 126        }
 127
 128        return 0;
 129}
 130
 131static int do_data(struct sw842_param *p, u8 n)
 132{
 133        u64 v;
 134        int ret;
 135
 136        if (n > p->olen)
 137                return -ENOSPC;
 138
 139        ret = next_bits(p, &v, n * 8);
 140        if (ret)
 141                return ret;
 142
 143        switch (n) {
 144        case 2:
 145                put_unaligned(cpu_to_be16((u16)v), (__be16 *)p->out);
 146                break;
 147        case 4:
 148                put_unaligned(cpu_to_be32((u32)v), (__be32 *)p->out);
 149                break;
 150        case 8:
 151                put_unaligned(cpu_to_be64((u64)v), (__be64 *)p->out);
 152                break;
 153        default:
 154                return -EINVAL;
 155        }
 156
 157        p->out += n;
 158        p->olen -= n;
 159
 160        return 0;
 161}
 162
 163static int __do_index(struct sw842_param *p, u8 size, u8 bits, u64 fsize)
 164{
 165        u64 index, offset, total = round_down(p->out - p->ostart, 8);
 166        int ret;
 167
 168        ret = next_bits(p, &index, bits);
 169        if (ret)
 170                return ret;
 171
 172        offset = index * size;
 173
 174        /* a ring buffer of fsize is used; correct the offset */
 175        if (total > fsize) {
 176                /* this is where the current fifo is */
 177                u64 section = round_down(total, fsize);
 178                /* the current pos in the fifo */
 179                u64 pos = total - section;
 180
 181                /* if the offset is past/at the pos, we need to
 182                 * go back to the last fifo section
 183                 */
 184                if (offset >= pos)
 185                        section -= fsize;
 186
 187                offset += section;
 188        }
 189
 190        if (offset + size > total) {
 191                pr_debug("index%x %lx points past end %lx\n", size,
 192                         (unsigned long)offset, (unsigned long)total);
 193                return -EINVAL;
 194        }
 195
 196        if (size != 2 && size != 4 && size != 8)
 197                WARN(1, "__do_index invalid size %x\n", size);
 198        else
 199                pr_debug("index%x to %lx off %lx adjoff %lx tot %lx data %lx\n",
 200                         size, (unsigned long)index,
 201                         (unsigned long)(index * size), (unsigned long)offset,
 202                         (unsigned long)total,
 203                         (unsigned long)beN_to_cpu(&p->ostart[offset], size));
 204
 205        memcpy(p->out, &p->ostart[offset], size);
 206        p->out += size;
 207        p->olen -= size;
 208
 209        return 0;
 210}
 211
 212static int do_index(struct sw842_param *p, u8 n)
 213{
 214        switch (n) {
 215        case 2:
 216                return __do_index(p, 2, I2_BITS, I2_FIFO_SIZE);
 217        case 4:
 218                return __do_index(p, 4, I4_BITS, I4_FIFO_SIZE);
 219        case 8:
 220                return __do_index(p, 8, I8_BITS, I8_FIFO_SIZE);
 221        default:
 222                return -EINVAL;
 223        }
 224}
 225
 226static int do_op(struct sw842_param *p, u8 o)
 227{
 228        int i, ret = 0;
 229
 230        if (o >= OPS_MAX)
 231                return -EINVAL;
 232
 233        for (i = 0; i < 4; i++) {
 234                u8 op = decomp_ops[o][i];
 235
 236                pr_debug("op is %x\n", op);
 237
 238                switch (op & OP_ACTION) {
 239                case OP_ACTION_DATA:
 240                        ret = do_data(p, op & OP_AMOUNT);
 241                        break;
 242                case OP_ACTION_INDEX:
 243                        ret = do_index(p, op & OP_AMOUNT);
 244                        break;
 245                case OP_ACTION_NOOP:
 246                        break;
 247                default:
 248                        pr_err("Internal error, invalid op %x\n", op);
 249                        return -EINVAL;
 250                }
 251
 252                if (ret)
 253                        return ret;
 254        }
 255
 256        if (sw842_template_counts)
 257                atomic_inc(&template_count[o]);
 258
 259        return 0;
 260}
 261
 262/**
 263 * sw842_decompress
 264 *
 265 * Decompress the 842-compressed buffer of length @ilen at @in
 266 * to the output buffer @out, using no more than @olen bytes.
 267 *
 268 * The compressed buffer must be only a single 842-compressed buffer,
 269 * with the standard format described in the comments in 842.h
 270 * Processing will stop when the 842 "END" template is detected,
 271 * not the end of the buffer.
 272 *
 273 * Returns: 0 on success, error on failure.  The @olen parameter
 274 * will contain the number of output bytes written on success, or
 275 * 0 on error.
 276 */
 277int sw842_decompress(const u8 *in, unsigned int ilen,
 278                     u8 *out, unsigned int *olen)
 279{
 280        struct sw842_param p;
 281        int ret;
 282        u64 op, rep, tmp, bytes, total;
 283        u64 crc;
 284
 285        p.in = (u8 *)in;
 286        p.bit = 0;
 287        p.ilen = ilen;
 288        p.out = out;
 289        p.ostart = out;
 290        p.olen = *olen;
 291
 292        total = p.olen;
 293
 294        *olen = 0;
 295
 296        do {
 297                ret = next_bits(&p, &op, OP_BITS);
 298                if (ret)
 299                        return ret;
 300
 301                pr_debug("template is %lx\n", (unsigned long)op);
 302
 303                switch (op) {
 304                case OP_REPEAT:
 305                        ret = next_bits(&p, &rep, REPEAT_BITS);
 306                        if (ret)
 307                                return ret;
 308
 309                        if (p.out == out) /* no previous bytes */
 310                                return -EINVAL;
 311
 312                        /* copy rep + 1 */
 313                        rep++;
 314
 315                        if (rep * 8 > p.olen)
 316                                return -ENOSPC;
 317
 318                        while (rep-- > 0) {
 319                                memcpy(p.out, p.out - 8, 8);
 320                                p.out += 8;
 321                                p.olen -= 8;
 322                        }
 323
 324                        if (sw842_template_counts)
 325                                atomic_inc(&template_repeat_count);
 326
 327                        break;
 328                case OP_ZEROS:
 329                        if (8 > p.olen)
 330                                return -ENOSPC;
 331
 332                        memset(p.out, 0, 8);
 333                        p.out += 8;
 334                        p.olen -= 8;
 335
 336                        if (sw842_template_counts)
 337                                atomic_inc(&template_zeros_count);
 338
 339                        break;
 340                case OP_SHORT_DATA:
 341                        ret = next_bits(&p, &bytes, SHORT_DATA_BITS);
 342                        if (ret)
 343                                return ret;
 344
 345                        if (!bytes || bytes > SHORT_DATA_BITS_MAX)
 346                                return -EINVAL;
 347
 348                        while (bytes-- > 0) {
 349                                ret = next_bits(&p, &tmp, 8);
 350                                if (ret)
 351                                        return ret;
 352                                *p.out = (u8)tmp;
 353                                p.out++;
 354                                p.olen--;
 355                        }
 356
 357                        if (sw842_template_counts)
 358                                atomic_inc(&template_short_data_count);
 359
 360                        break;
 361                case OP_END:
 362                        if (sw842_template_counts)
 363                                atomic_inc(&template_end_count);
 364
 365                        break;
 366                default: /* use template */
 367                        ret = do_op(&p, op);
 368                        if (ret)
 369                                return ret;
 370                        break;
 371                }
 372        } while (op != OP_END);
 373
 374        /*
 375         * crc(0:31) is saved in compressed data starting with the
 376         * next bit after End of stream template.
 377         */
 378        ret = next_bits(&p, &crc, CRC_BITS);
 379        if (ret)
 380                return ret;
 381
 382        /*
 383         * Validate CRC saved in compressed data.
 384         */
 385        if (crc != (u64)crc32_be(0, out, total - p.olen)) {
 386                pr_debug("CRC mismatch for decompression\n");
 387                return -EINVAL;
 388        }
 389
 390        if (unlikely((total - p.olen) > UINT_MAX))
 391                return -ENOSPC;
 392
 393        *olen = total - p.olen;
 394
 395        return 0;
 396}
 397EXPORT_SYMBOL_GPL(sw842_decompress);
 398
 399static int __init sw842_init(void)
 400{
 401        if (sw842_template_counts)
 402                sw842_debugfs_create();
 403
 404        return 0;
 405}
 406module_init(sw842_init);
 407
 408static void __exit sw842_exit(void)
 409{
 410        if (sw842_template_counts)
 411                sw842_debugfs_remove();
 412}
 413module_exit(sw842_exit);
 414
 415MODULE_LICENSE("GPL");
 416MODULE_DESCRIPTION("Software 842 Decompressor");
 417MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
 418