| // 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 */ |