1 /* dfltcc.c -- compress data using IBM Z DEFLATE COMPRESSION CALL
3 Copyright (C) 2019-2024 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
31 # error "DYN_ALLOC is not supported by DFLTCC"
34 /* ===========================================================================
35 * C wrappers for the DEFLATE CONVERSION CALL instruction.
41 DFLTCC_CC_OP1_TOO_SHORT
= 1,
42 DFLTCC_CC_OP2_TOO_SHORT
= 2,
43 DFLTCC_CC_OP2_CORRUPT
= 2,
51 #define HBT_CIRCULAR (1 << 7)
52 #define DFLTCC_FACILITY 151
58 #ifndef DFLTCC_BLOCK_SIZE
59 # define DFLTCC_BLOCK_SIZE 1048576
61 #ifndef DFLTCC_FIRST_FHT_BLOCK_SIZE
62 # define DFLTCC_FIRST_FHT_BLOCK_SIZE 4096
64 #ifndef DFLTCC_LEVEL_MASK
65 # define DFLTCC_LEVEL_MASK 0x2
68 # define DFLTCC_RIBM 0
71 #define MAX(a, b) ((a) > (b) ? (a) : (b))
73 #define STREQ(a, b) (strcmp (a, b) == 0)
75 struct dfltcc_qaf_param
83 union aligned_dfltcc_qaf_param
85 struct dfltcc_qaf_param af
;
86 char alignas (8) aligned
;
89 struct dfltcc_param_v0
91 unsigned short pbvn
; /* Parameter-Block-Version Number */
92 unsigned char mvn
; /* Model-Version Number */
93 unsigned char ribm
; /* Reserved for IBM use */
94 unsigned reserved32
: 31;
95 unsigned cf
: 1; /* Continuation Flag */
96 unsigned char reserved64
[8];
97 unsigned nt
: 1; /* New Task */
98 unsigned reserved129
: 1;
99 unsigned cvt
: 1; /* Check Value Type */
100 unsigned reserved131
: 1;
101 unsigned htt
: 1; /* Huffman-Table Type */
102 unsigned bcf
: 1; /* Block-Continuation Flag */
103 unsigned bcc
: 1; /* Block Closing Control */
104 unsigned bhf
: 1; /* Block Header Final */
105 unsigned reserved136
: 1;
106 unsigned reserved137
: 1;
107 unsigned dhtgc
: 1; /* DHT Generation Control */
108 unsigned reserved139
: 5;
109 unsigned reserved144
: 5;
110 unsigned sbb
: 3; /* Sub-Byte Boundary */
111 unsigned char oesc
; /* Operation-Ending-Supplemental Code */
112 unsigned reserved160
: 12;
113 unsigned ifs
: 4; /* Incomplete-Function Status */
114 unsigned short ifl
; /* Incomplete-Function Length */
115 unsigned char reserved192
[8];
116 unsigned char reserved256
[8];
117 unsigned char reserved320
[4];
118 unsigned short hl
; /* History Length */
119 unsigned reserved368
: 1;
120 unsigned short ho
: 15; /* History Offset */
121 unsigned int cv
; /* Check Value */
122 unsigned eobs
: 15; /* End-of-block Symbol */
123 unsigned reserved431
: 1;
124 unsigned char eobl
: 4; /* End-of-block Length */
125 unsigned reserved436
: 12;
126 unsigned reserved448
: 4;
127 unsigned short cdhtl
: 12; /* Compressed-Dynamic-Huffman Table
129 unsigned char reserved464
[6];
130 unsigned char cdht
[288];
131 unsigned char reserved
[32];
132 unsigned char csb
[1152];
135 union aligned_dfltcc_param_v0
137 struct dfltcc_param_v0 param
;
138 char alignas (8) aligned
;
142 is_bit_set (const char *bits
, int n
)
144 return bits
[n
/ 8] & (1 << (7 - (n
% 8)));
148 is_dfltcc_enabled (void)
150 char facilities
[(DFLTCC_FACILITY
/ 64 + 1) * 8];
152 char const *env
= getenv ("DFLTCC");
153 if (env
&& STREQ (env
, "0"))
156 memset (facilities
, 0, sizeof facilities
);
157 register char r0
__asm__ ("r0") = sizeof facilities
/ 8 - 1;
158 /* STFLE is supported since z9-109 and only in z/Architecture mode. When
159 * compiling with -m31, gcc defaults to ESA mode, however, since the kernel
160 * is 64-bit, it's always z/Architecture mode at runtime. */
163 ".machinemode push\n"
164 ".machinemode zarch\n"
166 "stfle %[facilities]\n"
170 : [facilities
] "=Q"(facilities
), [r0
] "+r"(r0
) :: "cc");
171 return is_bit_set (facilities
, DFLTCC_FACILITY
);
175 dfltcc (int fn
, void *param
,
176 uch
**op1
, size_t *len1
,
177 uch
const **op2
, size_t *len2
,
180 uch
*t2
= op1
? *op1
: NULL
;
181 size_t t3
= len1
? *len1
: 0;
182 const uch
*t4
= op2
? *op2
: NULL
;
183 size_t t5
= len2
? *len2
: 0;
184 register int r0
__asm__ ("r0") = fn
;
185 register void *r1
__asm__ ("r1") = param
;
186 register uch
*r2
__asm__ ("r2") = t2
;
187 register size_t r3
__asm__ ("r3") = t3
;
188 register const uch
*r4
__asm__ ("r4") = t4
;
189 register size_t r5
__asm__ ("r5") = t5
;
193 #ifdef HAVE_SYS_SDT_H
194 STAP_PROBE_ASM (zlib
, dfltcc_entry
,
195 STAP_PROBE_ASM_TEMPLATE (5))
197 ".insn rrf,0xb9390000,%[r2],%[r4],%[hist],0\n"
198 #ifdef HAVE_SYS_SDT_H
199 STAP_PROBE_ASM (zlib
, dfltcc_exit
,
200 STAP_PROBE_ASM_TEMPLATE (5))
211 #ifdef HAVE_SYS_SDT_H
212 , STAP_PROBE_ASM_OPERANDS (5, r2
, r3
, r4
, r5
, hist
)
215 t2
= r2
; t3
= r3
; t4
= r4
; t5
= r5
;
225 return (cc
>> 28) & 3;
229 dfltcc_qaf (struct dfltcc_qaf_param
*param
)
231 dfltcc (DFLTCC_QAF
, param
, NULL
, NULL
, NULL
, NULL
, NULL
);
235 dfltcc_gdht (struct dfltcc_param_v0
*param
)
237 const uch
*next_in
= inbuf
+ inptr
;
238 size_t avail_in
= insize
- inptr
;
240 dfltcc (DFLTCC_GDHT
, param
, NULL
, NULL
, &next_in
, &avail_in
, NULL
);
244 dfltcc_cmpr_xpnd (struct dfltcc_param_v0
*param
, int fn
, off_t
*total_in
)
246 uch
*next_out
= outbuf
+ outcnt
;
247 size_t avail_out
= OUTBUFSIZ
- outcnt
;
248 const uch
*next_in
= inbuf
+ inptr
;
249 size_t avail_in
= insize
- inptr
;
250 dfltcc_cc cc
= dfltcc (fn
| HBT_CIRCULAR
, param
,
251 &next_out
, &avail_out
,
254 off_t consumed_in
= next_in
- (inbuf
+ inptr
);
255 inptr
+= consumed_in
;
256 *total_in
+= consumed_in
;
257 outcnt
+= ((OUTBUFSIZ
- outcnt
) - avail_out
);
261 static struct dfltcc_param_v0
*
262 init_param (union aligned_dfltcc_param_v0
*ctx
)
264 char const *s
= getenv ("DFLTCC_RIBM");
265 struct dfltcc_param_v0
*param
= &ctx
->param
;
266 memset (param
, 0, sizeof *param
);
267 param
->ribm
= s
&& *s
? strtoul (s
, NULL
, 0) : DFLTCC_RIBM
;
269 param
->cvt
= CVT_CRC32
;
270 param
->cv
= __builtin_bswap32 (getcrc ());
275 bi_load (struct dfltcc_param_v0
*param
)
277 bi_valid
= param
->sbb
;
278 bi_buf
= bi_valid
== 0 ? 0 : outbuf
[outcnt
] & ((1 << bi_valid
) - 1);
282 bi_close_block (struct dfltcc_param_v0
*param
)
285 send_bits (bi_reverse (param
->eobs
>> (15 - param
->eobl
), param
->eobl
),
291 close_block (struct dfltcc_param_v0
*param
)
293 bi_close_block (param
);
295 /* bi_windup has written out a possibly partial byte, fix up the position */
296 param
->sbb
= (param
->sbb
+ param
->eobl
) % 8;
299 Assert (outcnt
> 0, "outbuf must have enough space for EOBS");
305 close_stream (struct dfltcc_param_v0
*param
)
308 bi_close_block (param
);
311 send_bits (1, 3); /* BFINAL=1, BTYPE=00 */
317 /* Compress ifd into ofd in hardware or fall back to software. */
320 dfltcc_deflate (int pack_level
)
322 /* Check whether we can use hardware compression. */
323 if (!is_dfltcc_enabled () || getenv ("SOURCE_DATE_EPOCH"))
324 return gzip_deflate (pack_level
);
325 char const *s
= getenv ("DFLTCC_LEVEL_MASK");
326 unsigned long level_mask
327 = s
&& *s
? strtoul (s
, NULL
, 0) : DFLTCC_LEVEL_MASK
;
328 if ((level_mask
& (1 << pack_level
)) == 0)
329 return gzip_deflate (pack_level
);
330 union aligned_dfltcc_qaf_param ctx
;
331 dfltcc_qaf (&ctx
.af
);
332 if (!is_bit_set (ctx
.af
.fns
, DFLTCC_CMPR
)
333 || !is_bit_set (ctx
.af
.fns
, DFLTCC_GDHT
)
334 || !is_bit_set (ctx
.af
.fmts
, DFLTCC_FMT0
))
335 return gzip_deflate (pack_level
);
337 /* Initialize tuning parameters. */
338 s
= getenv ("DFLTCC_BLOCK_SIZE");
339 unsigned long block_size
340 = s
&& *s
? strtoul (s
, NULL
, 0) : DFLTCC_BLOCK_SIZE
;
342 s
= getenv ("DFLTCC_FIRST_FHT_BLOCK_SIZE");
343 off_t block_threshold
344 = s
&& *s
? strtoul (s
, NULL
, 0) : DFLTCC_FIRST_FHT_BLOCK_SIZE
;
346 union aligned_dfltcc_param_v0 ctx_v0
;
347 struct dfltcc_param_v0
*param
= init_param (&ctx_v0
);
350 /* Compress ifd into ofd in a loop. */
353 /* Flush the output data. */
354 if (outcnt
> OUTBUFSIZ
- 8)
360 uch partial
= outbuf
[outcnt
];
362 outbuf
[outcnt
] = partial
;
366 /* Close the block. */
367 if (param
->bcf
&& total_in
== block_threshold
&& !param
->cf
)
370 block_threshold
+= block_size
;
373 /* Read the input data. */
376 if (fill_inbuf (1) == EOF
&& !param
->cf
)
381 /* Temporarily mask some input data. */
382 int extra
= MAX (0, total_in
+ (insize
- inptr
) - block_threshold
);
385 /* Start a new block. */
388 if (total_in
== 0 && block_threshold
> 0)
389 param
->htt
= HTT_FIXED
;
392 param
->htt
= HTT_DYNAMIC
;
397 /* Compress inbuf into outbuf. */
398 while (dfltcc_cmpr_xpnd (param
, DFLTCC_CMPR
, &total_in
)
402 /* Unmask the input data. */
405 /* Continue the block */
409 close_stream (param
);
410 setcrc (__builtin_bswap32 (param
->cv
));
414 /* Decompress ifd into ofd in hardware or fall back to software. */
418 /* Check whether we can use hardware decompression. */
419 if (!is_dfltcc_enabled ())
420 return gzip_inflate ();
421 union aligned_dfltcc_qaf_param ctx
;
422 dfltcc_qaf (&ctx
.af
);
423 if (!is_bit_set (ctx
.af
.fns
, DFLTCC_XPND
))
424 return gzip_inflate ();
426 union aligned_dfltcc_param_v0 ctx_v0
;
427 struct dfltcc_param_v0
*param
= init_param (&ctx_v0
);
430 /* Decompress ifd into ofd in a loop. */
434 if (outcnt
== OUTBUFSIZ
)
438 if (fill_inbuf (1) == EOF
)
446 /* Decompress inbuf into outbuf. */
448 while ((cc
= dfltcc_cmpr_xpnd (param
, DFLTCC_XPND
, &total_in
))
451 if (cc
== DFLTCC_CC_OK
)
453 /* The entire deflate stream has been successfully decompressed. */
456 if (cc
== DFLTCC_CC_OP2_CORRUPT
&& param
->oesc
!= 0)
458 /* The deflate stream is corrupted. */
459 fprintf (stderr
, "Operation-Ending-Supplemental Code 0x%x\n",
464 /* There must be more data to decompress. */
469 /* The deflate stream has ended in the middle of a byte. Go to
470 the next byte boundary, so that unzip can read CRC and length. */
474 /* Set CRC value and update bytes_out for unzip. */
475 setcrc (__builtin_bswap32 (param
->cv
));