summaryrefslogtreecommitdiff
path: root/lib/zlib_dfltcc/dfltcc_util.h
blob: 10509270d8223a342010147326cb3234988ed738 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// SPDX-License-Identifier: Zlib
#ifndef DFLTCC_UTIL_H
#define DFLTCC_UTIL_H

#include "dfltcc.h"
#include <linux/kmsan-checks.h>
#include <linux/zutil.h>

/*
 * C wrapper for the DEFLATE CONVERSION CALL instruction.
 */
typedef enum {
    DFLTCC_CC_OK = 0,
    DFLTCC_CC_OP1_TOO_SHORT = 1,
    DFLTCC_CC_OP2_TOO_SHORT = 2,
    DFLTCC_CC_OP2_CORRUPT = 2,
    DFLTCC_CC_AGAIN = 3,
} dfltcc_cc;

#define DFLTCC_QAF 0
#define DFLTCC_GDHT 1
#define DFLTCC_CMPR 2
#define DFLTCC_XPND 4
#define HBT_CIRCULAR (1 << 7)
#define DFLTCC_FN_MASK ((1 << 7) - 1)
#define HB_BITS 15
#define HB_SIZE (1 << HB_BITS)

static inline dfltcc_cc dfltcc(
    int fn,
    void *param,
    Byte **op1,
    size_t *len1,
    const Byte **op2,
    size_t *len2,
    void *hist
)
{
    Byte *t2 = op1 ? *op1 : NULL;
    unsigned char *orig_t2 = t2;
    size_t t3 = len1 ? *len1 : 0;
    const Byte *t4 = op2 ? *op2 : NULL;
    size_t t5 = len2 ? *len2 : 0;
    register int r0 __asm__("r0") = fn;
    register void *r1 __asm__("r1") = param;
    register Byte *r2 __asm__("r2") = t2;
    register size_t r3 __asm__("r3") = t3;
    register const Byte *r4 __asm__("r4") = t4;
    register size_t r5 __asm__("r5") = t5;
    int cc;

    __asm__ volatile(
                     ".insn rrf,0xb9390000,%[r2],%[r4],%[hist],0\n"
                     "ipm %[cc]\n"
                     : [r2] "+r" (r2)
                     , [r3] "+r" (r3)
                     , [r4] "+r" (r4)
                     , [r5] "+r" (r5)
                     , [cc] "=r" (cc)
                     : [r0] "r" (r0)
                     , [r1] "r" (r1)
                     , [hist] "r" (hist)
                     : "cc", "memory");
    t2 = r2; t3 = r3; t4 = r4; t5 = r5;

    /*
     * Unpoison the parameter block and the output buffer.
     * This is a no-op in non-KMSAN builds.
     */
    switch (fn & DFLTCC_FN_MASK) {
    case DFLTCC_QAF:
        kmsan_unpoison_memory(param, sizeof(struct dfltcc_qaf_param));
        break;
    case DFLTCC_GDHT:
        kmsan_unpoison_memory(param, offsetof(struct dfltcc_param_v0, csb));
        break;
    case DFLTCC_CMPR:
        kmsan_unpoison_memory(param, sizeof(struct dfltcc_param_v0));
        kmsan_unpoison_memory(
                orig_t2,
                t2 - orig_t2 +
                    (((struct dfltcc_param_v0 *)param)->sbb == 0 ? 0 : 1));
        break;
    case DFLTCC_XPND:
        kmsan_unpoison_memory(param, sizeof(struct dfltcc_param_v0));
        kmsan_unpoison_memory(orig_t2, t2 - orig_t2);
        break;
    }

    if (op1)
        *op1 = t2;
    if (len1)
        *len1 = t3;
    if (op2)
        *op2 = t4;
    if (len2)
        *len2 = t5;
    return (cc >> 28) & 3;
}

static inline int is_bit_set(
    const char *bits,
    int n
)
{
    return bits[n / 8] & (1 << (7 - (n % 8)));
}

static inline void turn_bit_off(
    char *bits,
    int n
)
{
    bits[n / 8] &= ~(1 << (7 - (n % 8)));
}

static inline int dfltcc_are_params_ok(
    int level,
    uInt window_bits,
    int strategy,
    uLong level_mask
)
{
    return (level_mask & (1 << level)) != 0 &&
        (window_bits == HB_BITS) &&
        (strategy == Z_DEFAULT_STRATEGY);
}

char *oesc_msg(char *buf, int oesc);

#endif /* DFLTCC_UTIL_H */