dfltcc_detail.h 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. #include "zbuild.h"
  2. #include <stdio.h>
  3. #ifdef HAVE_SYS_SDT_H
  4. #include <sys/sdt.h>
  5. #endif
  6. /*
  7. Tuning parameters.
  8. */
  9. #ifndef DFLTCC_LEVEL_MASK
  10. #define DFLTCC_LEVEL_MASK 0x2
  11. #endif
  12. #ifndef DFLTCC_BLOCK_SIZE
  13. #define DFLTCC_BLOCK_SIZE 1048576
  14. #endif
  15. #ifndef DFLTCC_FIRST_FHT_BLOCK_SIZE
  16. #define DFLTCC_FIRST_FHT_BLOCK_SIZE 4096
  17. #endif
  18. #ifndef DFLTCC_DHT_MIN_SAMPLE_SIZE
  19. #define DFLTCC_DHT_MIN_SAMPLE_SIZE 4096
  20. #endif
  21. #ifndef DFLTCC_RIBM
  22. #define DFLTCC_RIBM 0
  23. #endif
  24. #define static_assert(c, msg) __attribute__((unused)) static char static_assert_failed_ ## msg[c ? 1 : -1]
  25. #define DFLTCC_SIZEOF_QAF 32
  26. static_assert(sizeof(struct dfltcc_qaf_param) == DFLTCC_SIZEOF_QAF, qaf);
  27. static inline int is_bit_set(const char *bits, int n) {
  28. return bits[n / 8] & (1 << (7 - (n % 8)));
  29. }
  30. static inline void clear_bit(char *bits, int n) {
  31. bits[n / 8] &= ~(1 << (7 - (n % 8)));
  32. }
  33. #define DFLTCC_FACILITY 151
  34. static inline int is_dfltcc_enabled(void) {
  35. uint64_t facilities[(DFLTCC_FACILITY / 64) + 1];
  36. Z_REGISTER uint8_t r0 __asm__("r0");
  37. memset(facilities, 0, sizeof(facilities));
  38. r0 = sizeof(facilities) / sizeof(facilities[0]) - 1;
  39. /* STFLE is supported since z9-109 and only in z/Architecture mode. When
  40. * compiling with -m31, gcc defaults to ESA mode, however, since the kernel
  41. * is 64-bit, it's always z/Architecture mode at runtime.
  42. */
  43. __asm__ volatile(
  44. #ifndef __clang__
  45. ".machinemode push\n"
  46. ".machinemode zarch\n"
  47. #endif
  48. "stfle %[facilities]\n"
  49. #ifndef __clang__
  50. ".machinemode pop\n"
  51. #endif
  52. : [facilities] "=Q" (facilities), [r0] "+r" (r0) :: "cc");
  53. return is_bit_set((const char *)facilities, DFLTCC_FACILITY);
  54. }
  55. #define DFLTCC_FMT0 0
  56. #define CVT_CRC32 0
  57. #define CVT_ADLER32 1
  58. #define HTT_FIXED 0
  59. #define HTT_DYNAMIC 1
  60. #define DFLTCC_SIZEOF_GDHT_V0 384
  61. #define DFLTCC_SIZEOF_CMPR_XPND_V0 1536
  62. static_assert(offsetof(struct dfltcc_param_v0, csb) == DFLTCC_SIZEOF_GDHT_V0, gdht_v0);
  63. static_assert(sizeof(struct dfltcc_param_v0) == DFLTCC_SIZEOF_CMPR_XPND_V0, cmpr_xpnd_v0);
  64. static inline z_const char *oesc_msg(char *buf, int oesc) {
  65. if (oesc == 0x00)
  66. return NULL; /* Successful completion */
  67. else {
  68. sprintf(buf, "Operation-Ending-Supplemental Code is 0x%.2X", oesc);
  69. return buf;
  70. }
  71. }
  72. /*
  73. C wrapper for the DEFLATE CONVERSION CALL instruction.
  74. */
  75. typedef enum {
  76. DFLTCC_CC_OK = 0,
  77. DFLTCC_CC_OP1_TOO_SHORT = 1,
  78. DFLTCC_CC_OP2_TOO_SHORT = 2,
  79. DFLTCC_CC_OP2_CORRUPT = 2,
  80. DFLTCC_CC_AGAIN = 3,
  81. } dfltcc_cc;
  82. #define DFLTCC_QAF 0
  83. #define DFLTCC_GDHT 1
  84. #define DFLTCC_CMPR 2
  85. #define DFLTCC_XPND 4
  86. #define HBT_CIRCULAR (1 << 7)
  87. #define DFLTCC_FN_MASK ((1 << 7) - 1)
  88. /* Return lengths of high (starting at param->ho) and low (starting at 0) fragments of the circular history buffer. */
  89. static inline void get_history_lengths(struct dfltcc_param_v0 *param, size_t *hl_high, size_t *hl_low) {
  90. *hl_high = MIN(param->hl, HB_SIZE - param->ho);
  91. *hl_low = param->hl - *hl_high;
  92. }
  93. /* Notify instrumentation about an upcoming read/write access to the circular history buffer. */
  94. static inline void instrument_read_write_hist(struct dfltcc_param_v0 *param, void *hist) {
  95. size_t hl_high, hl_low;
  96. get_history_lengths(param, &hl_high, &hl_low);
  97. instrument_read_write(hist + param->ho, hl_high);
  98. instrument_read_write(hist, hl_low);
  99. }
  100. /* Notify MSan about a completed write to the circular history buffer. */
  101. static inline void msan_unpoison_hist(struct dfltcc_param_v0 *param, void *hist) {
  102. size_t hl_high, hl_low;
  103. get_history_lengths(param, &hl_high, &hl_low);
  104. __msan_unpoison(hist + param->ho, hl_high);
  105. __msan_unpoison(hist, hl_low);
  106. }
  107. static inline dfltcc_cc dfltcc(int fn, void *param,
  108. unsigned char **op1, size_t *len1,
  109. z_const unsigned char **op2, size_t *len2, void *hist) {
  110. unsigned char *t2 = op1 ? *op1 : NULL;
  111. unsigned char *orig_t2 = t2;
  112. size_t t3 = len1 ? *len1 : 0;
  113. z_const unsigned char *t4 = op2 ? *op2 : NULL;
  114. size_t t5 = len2 ? *len2 : 0;
  115. Z_REGISTER int r0 __asm__("r0");
  116. Z_REGISTER void *r1 __asm__("r1");
  117. Z_REGISTER unsigned char *r2 __asm__("r2");
  118. Z_REGISTER size_t r3 __asm__("r3");
  119. Z_REGISTER z_const unsigned char *r4 __asm__("r4");
  120. Z_REGISTER size_t r5 __asm__("r5");
  121. int cc;
  122. /* Insert pre-instrumentation for DFLTCC. */
  123. switch (fn & DFLTCC_FN_MASK) {
  124. case DFLTCC_QAF:
  125. instrument_write(param, DFLTCC_SIZEOF_QAF);
  126. break;
  127. case DFLTCC_GDHT:
  128. instrument_read_write(param, DFLTCC_SIZEOF_GDHT_V0);
  129. instrument_read(t4, t5);
  130. break;
  131. case DFLTCC_CMPR:
  132. case DFLTCC_XPND:
  133. instrument_read_write(param, DFLTCC_SIZEOF_CMPR_XPND_V0);
  134. instrument_read(t4, t5);
  135. instrument_write(t2, t3);
  136. instrument_read_write_hist(param, hist);
  137. break;
  138. }
  139. r0 = fn; r1 = param; r2 = t2; r3 = t3; r4 = t4; r5 = t5;
  140. __asm__ volatile(
  141. #ifdef HAVE_SYS_SDT_H
  142. STAP_PROBE_ASM(zlib, dfltcc_entry, STAP_PROBE_ASM_TEMPLATE(5))
  143. #endif
  144. ".insn rrf,0xb9390000,%[r2],%[r4],%[hist],0\n"
  145. #ifdef HAVE_SYS_SDT_H
  146. STAP_PROBE_ASM(zlib, dfltcc_exit, STAP_PROBE_ASM_TEMPLATE(5))
  147. #endif
  148. "ipm %[cc]\n"
  149. : [r2] "+r" (r2)
  150. , [r3] "+r" (r3)
  151. , [r4] "+r" (r4)
  152. , [r5] "+r" (r5)
  153. , [cc] "=r" (cc)
  154. : [r0] "r" (r0)
  155. , [r1] "r" (r1)
  156. , [hist] "r" (hist)
  157. #ifdef HAVE_SYS_SDT_H
  158. , STAP_PROBE_ASM_OPERANDS(5, r2, r3, r4, r5, hist)
  159. #endif
  160. : "cc", "memory");
  161. t2 = r2; t3 = r3; t4 = r4; t5 = r5;
  162. /* Insert post-instrumentation for DFLTCC. */
  163. switch (fn & DFLTCC_FN_MASK) {
  164. case DFLTCC_QAF:
  165. __msan_unpoison(param, DFLTCC_SIZEOF_QAF);
  166. break;
  167. case DFLTCC_GDHT:
  168. __msan_unpoison(param, DFLTCC_SIZEOF_GDHT_V0);
  169. break;
  170. case DFLTCC_CMPR:
  171. __msan_unpoison(param, DFLTCC_SIZEOF_CMPR_XPND_V0);
  172. __msan_unpoison(orig_t2, t2 - orig_t2 + (((struct dfltcc_param_v0 *)param)->sbb == 0 ? 0 : 1));
  173. msan_unpoison_hist(param, hist);
  174. break;
  175. case DFLTCC_XPND:
  176. __msan_unpoison(param, DFLTCC_SIZEOF_CMPR_XPND_V0);
  177. __msan_unpoison(orig_t2, t2 - orig_t2);
  178. msan_unpoison_hist(param, hist);
  179. break;
  180. }
  181. if (op1)
  182. *op1 = t2;
  183. if (len1)
  184. *len1 = t3;
  185. if (op2)
  186. *op2 = t4;
  187. if (len2)
  188. *len2 = t5;
  189. return (cc >> 28) & 3;
  190. }
  191. #define ALIGN_UP(p, size) (__typeof__(p))(((uintptr_t)(p) + ((size) - 1)) & ~((size) - 1))
  192. static inline void dfltcc_reset_state(struct dfltcc_state *dfltcc_state) {
  193. /* Initialize available functions */
  194. if (is_dfltcc_enabled()) {
  195. dfltcc(DFLTCC_QAF, &dfltcc_state->param, NULL, NULL, NULL, NULL, NULL);
  196. memmove(&dfltcc_state->af, &dfltcc_state->param, sizeof(dfltcc_state->af));
  197. } else
  198. memset(&dfltcc_state->af, 0, sizeof(dfltcc_state->af));
  199. /* Initialize parameter block */
  200. memset(&dfltcc_state->param, 0, sizeof(dfltcc_state->param));
  201. dfltcc_state->param.nt = 1;
  202. dfltcc_state->param.ribm = DFLTCC_RIBM;
  203. }
  204. static inline void dfltcc_copy_state(void *dst, const void *src, uInt size, uInt extension_size) {
  205. memcpy(dst, src, ALIGN_UP(size, 8) + extension_size);
  206. }
  207. static inline void append_history(struct dfltcc_param_v0 *param, unsigned char *history,
  208. const unsigned char *buf, uInt count) {
  209. size_t offset;
  210. size_t n;
  211. /* Do not use more than 32K */
  212. if (count > HB_SIZE) {
  213. buf += count - HB_SIZE;
  214. count = HB_SIZE;
  215. }
  216. offset = (param->ho + param->hl) % HB_SIZE;
  217. if (offset + count <= HB_SIZE)
  218. /* Circular history buffer does not wrap - copy one chunk */
  219. memcpy(history + offset, buf, count);
  220. else {
  221. /* Circular history buffer wraps - copy two chunks */
  222. n = HB_SIZE - offset;
  223. memcpy(history + offset, buf, n);
  224. memcpy(history, buf + n, count - n);
  225. }
  226. n = param->hl + count;
  227. if (n <= HB_SIZE)
  228. /* All history fits into buffer - no need to discard anything */
  229. param->hl = n;
  230. else {
  231. /* History does not fit into buffer - discard extra bytes */
  232. param->ho = (param->ho + (n - HB_SIZE)) % HB_SIZE;
  233. param->hl = HB_SIZE;
  234. }
  235. }
  236. static inline void get_history(struct dfltcc_param_v0 *param, const unsigned char *history,
  237. unsigned char *buf) {
  238. size_t hl_high, hl_low;
  239. get_history_lengths(param, &hl_high, &hl_low);
  240. memcpy(buf, history + param->ho, hl_high);
  241. memcpy(buf + hl_high, history, hl_low);
  242. }