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