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