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