1 // SPDX-License-Identifier: Zlib
2 
3 #include "../zlib_inflate/inflate.h"
4 #include "dfltcc_util.h"
5 #include "dfltcc.h"
6 #include <asm/setup.h>
7 #include <linux/export.h>
8 #include <linux/zutil.h>
9 
10 /*
11  * Expand.
12  */
13 int dfltcc_can_inflate(
14     z_streamp strm
15 )
16 {
17     struct inflate_state *state = (struct inflate_state *)strm->state;
18     struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
19 
20     /* Check for kernel dfltcc command line parameter */
21     if (zlib_dfltcc_support == ZLIB_DFLTCC_DISABLED ||
22             zlib_dfltcc_support == ZLIB_DFLTCC_DEFLATE_ONLY)
23         return 0;
24 
25     /* Unsupported compression settings */
26     if (state->wbits != HB_BITS)
27         return 0;
28 
29     /* Unsupported hardware */
30     return is_bit_set(dfltcc_state->af.fns, DFLTCC_XPND) &&
31                is_bit_set(dfltcc_state->af.fmts, DFLTCC_FMT0);
32 }
33 EXPORT_SYMBOL(dfltcc_can_inflate);
34 
35 static int dfltcc_was_inflate_used(
36     z_streamp strm
37 )
38 {
39     struct inflate_state *state = (struct inflate_state *)strm->state;
40     struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param;
41 
42     return !param->nt;
43 }
44 
45 static int dfltcc_inflate_disable(
46     z_streamp strm
47 )
48 {
49     struct inflate_state *state = (struct inflate_state *)strm->state;
50     struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
51 
52     if (!dfltcc_can_inflate(strm))
53         return 0;
54     if (dfltcc_was_inflate_used(strm))
55         /* DFLTCC has already decompressed some data. Since there is not
56          * enough information to resume decompression in software, the call
57          * must fail.
58          */
59         return 1;
60     /* DFLTCC was not used yet - decompress in software */
61     memset(&dfltcc_state->af, 0, sizeof(dfltcc_state->af));
62     return 0;
63 }
64 
65 static dfltcc_cc dfltcc_xpnd(
66     z_streamp strm
67 )
68 {
69     struct inflate_state *state = (struct inflate_state *)strm->state;
70     struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param;
71     size_t avail_in = strm->avail_in;
72     size_t avail_out = strm->avail_out;
73     dfltcc_cc cc;
74 
75     cc = dfltcc(DFLTCC_XPND | HBT_CIRCULAR,
76                 param, &strm->next_out, &avail_out,
77                 &strm->next_in, &avail_in, state->window);
78     strm->avail_in = avail_in;
79     strm->avail_out = avail_out;
80     return cc;
81 }
82 
83 dfltcc_inflate_action dfltcc_inflate(
84     z_streamp strm,
85     int flush,
86     int *ret
87 )
88 {
89     struct inflate_state *state = (struct inflate_state *)strm->state;
90     struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
91     struct dfltcc_param_v0 *param = &dfltcc_state->param;
92     dfltcc_cc cc;
93 
94     if (flush == Z_BLOCK) {
95         /* DFLTCC does not support stopping on block boundaries */
96         if (dfltcc_inflate_disable(strm)) {
97             *ret = Z_STREAM_ERROR;
98             return DFLTCC_INFLATE_BREAK;
99         } else
100             return DFLTCC_INFLATE_SOFTWARE;
101     }
102 
103     if (state->last) {
104         if (state->bits != 0) {
105             strm->next_in++;
106             strm->avail_in--;
107             state->bits = 0;
108         }
109         state->mode = CHECK;
110         return DFLTCC_INFLATE_CONTINUE;
111     }
112 
113     if (strm->avail_in == 0 && !param->cf)
114         return DFLTCC_INFLATE_BREAK;
115 
116     if (!state->window || state->wsize == 0) {
117         state->mode = MEM;
118         return DFLTCC_INFLATE_CONTINUE;
119     }
120 
121     /* Translate stream to parameter block */
122     param->cvt = CVT_ADLER32;
123     param->sbb = state->bits;
124     param->hl = state->whave; /* Software and hardware history formats match */
125     param->ho = (state->write - state->whave) & ((1 << HB_BITS) - 1);
126     if (param->hl)
127         param->nt = 0; /* Honor history for the first block */
128     param->cv = state->flags ? REVERSE(state->check) : state->check;
129 
130     /* Inflate */
131     do {
132         cc = dfltcc_xpnd(strm);
133     } while (cc == DFLTCC_CC_AGAIN);
134 
135     /* Translate parameter block to stream */
136     strm->msg = oesc_msg(dfltcc_state->msg, param->oesc);
137     state->last = cc == DFLTCC_CC_OK;
138     state->bits = param->sbb;
139     state->whave = param->hl;
140     state->write = (param->ho + param->hl) & ((1 << HB_BITS) - 1);
141     state->check = state->flags ? REVERSE(param->cv) : param->cv;
142     if (cc == DFLTCC_CC_OP2_CORRUPT && param->oesc != 0) {
143         /* Report an error if stream is corrupted */
144         state->mode = BAD;
145         return DFLTCC_INFLATE_CONTINUE;
146     }
147     state->mode = TYPEDO;
148     /* Break if operands are exhausted, otherwise continue looping */
149     return (cc == DFLTCC_CC_OP1_TOO_SHORT || cc == DFLTCC_CC_OP2_TOO_SHORT) ?
150         DFLTCC_INFLATE_BREAK : DFLTCC_INFLATE_CONTINUE;
151 }
152 EXPORT_SYMBOL(dfltcc_inflate);
153