xref: /illumos-gate/usr/src/uts/common/io/cxgbe/t4nex/fastlz_api.c (revision 3665ce8aeee26b1a84fb98951ef011e0779e1ae2)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source. A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13    FastLZ - lightning-fast lossless compression library
14 
15    Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
16    Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
17    Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
18 
19    Permission is hereby granted, free of charge, to any person obtaining a copy
20    of this software and associated documentation files (the "Software"), to deal
21    in the Software without restriction, including without limitation the rights
22    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23    copies of the Software, and to permit persons to whom the Software is
24    furnished to do so, subject to the following conditions:
25 
26    The above copyright notice and this permission notice shall be included in
27    all copies or substantial portions of the Software.
28 
29    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35    THE SOFTWARE.
36    */
37 
38 #include "osdep.h"
39 #include "cudbg.h"
40 #include "cudbg_lib_common.h"
41 #include "fastlz.h"
42 
43 #ifndef _KERNEL
44 #include <string.h>
45 #endif
46 
47 static unsigned char sixpack_magic[8] = {137, '6', 'P', 'K', 13, 10, 26, 10};
48 
49 #define CUDBG_BLOCK_SIZE      (63*1024)
50 #define CUDBG_CHUNK_BUF_LEN   16
51 #define CUDBG_MIN_COMPR_LEN   32	/*min data length for applying compression*/
52 
53 /* for Adler-32 checksum algorithm, see RFC 1950 Section 8.2 */
54 
55 #define ADLER32_BASE 65521
56 
57 static unsigned long
58 update_adler32(unsigned long checksum,
59 	       const void *buf, int len)
60 {
61 	const unsigned char *ptr = (const unsigned char *)buf;
62 	unsigned long s1 = checksum & 0xffff;
63 	unsigned long s2 = (checksum >> 16) & 0xffff;
64 
65 	while (len > 0) {
66 		unsigned k = len < 5552 ? len : 5552;
67 		len -= k;
68 
69 		while (k >= 8) {
70 			s1 += *ptr++; s2 += s1;
71 			s1 += *ptr++; s2 += s1;
72 			s1 += *ptr++; s2 += s1;
73 			s1 += *ptr++; s2 += s1;
74 			s1 += *ptr++; s2 += s1;
75 			s1 += *ptr++; s2 += s1;
76 			s1 += *ptr++; s2 += s1;
77 			s1 += *ptr++; s2 += s1;
78 			k -= 8;
79 		}
80 
81 		while (k-- > 0) {
82 			s1 += *ptr++; s2 += s1;
83 		}
84 		s1 = s1 % ADLER32_BASE;
85 		s2 = s2 % ADLER32_BASE;
86 	}
87 	return (s2 << 16) + s1;
88 }
89 
90 int
91 write_magic(struct cudbg_buffer *_out_buff)
92 {
93 	int rc;
94 
95 	rc = write_to_buf(_out_buff->data, _out_buff->size, &_out_buff->offset,
96 			  sixpack_magic, 8);
97 
98 	return rc;
99 }
100 
101 int
102 write_to_buf(void *out_buf, u32 out_buf_size, u32 *offset, void *in_buf,
103 	     u32 in_buf_size)
104 {
105 	int rc = 0;
106 
107 	if (*offset >= out_buf_size) {
108 		rc = CUDBG_STATUS_OUTBUFF_OVERFLOW;
109 		goto err;
110 	}
111 
112 	memcpy((char *)out_buf + *offset, in_buf, in_buf_size);
113 	*offset = *offset + in_buf_size;
114 
115 err:
116 	return rc;
117 }
118 
119 int
120 read_from_buf(void *in_buf, u32 in_buf_size, u32 *offset, void *out_buf,
121 	      u32 out_buf_size)
122 {
123 	if (in_buf_size - *offset < out_buf_size)
124 		return 0;
125 
126 	memcpy((char *)out_buf, (char *)in_buf + *offset, out_buf_size);
127 	*offset =  *offset + out_buf_size;
128 	return out_buf_size;
129 }
130 
131 int
132 write_chunk_header(struct cudbg_buffer *_outbuf, int id, int options,
133 		   unsigned long size, unsigned long checksum,
134 		   unsigned long extra)
135 {
136 	unsigned char buffer[CUDBG_CHUNK_BUF_LEN];
137 	int rc = 0;
138 
139 	buffer[0] = id & 255;
140 	buffer[1] = (unsigned char)(id >> 8);
141 	buffer[2] = options & 255;
142 	buffer[3] = (unsigned char)(options >> 8);
143 	buffer[4] = size & 255;
144 	buffer[5] = (size >> 8) & 255;
145 	buffer[6] = (size >> 16) & 255;
146 	buffer[7] = (size >> 24) & 255;
147 	buffer[8] = checksum & 255;
148 	buffer[9] = (checksum >> 8) & 255;
149 	buffer[10] = (checksum >> 16) & 255;
150 	buffer[11] = (checksum >> 24) & 255;
151 	buffer[12] = extra & 255;
152 	buffer[13] = (extra >> 8) & 255;
153 	buffer[14] = (extra >> 16) & 255;
154 	buffer[15] = (extra >> 24) & 255;
155 
156 	rc = write_to_buf(_outbuf->data, _outbuf->size, &_outbuf->offset,
157 			  buffer, 16);
158 
159 	return rc;
160 }
161 
162 int
163 write_compression_hdr(struct cudbg_buffer *pin_buff,
164 		      struct cudbg_buffer *pout_buff)
165 {
166 	struct cudbg_buffer tmp_buffer;
167 	unsigned long fsize = pin_buff->size;
168 	unsigned char *buffer;
169 	unsigned long checksum;
170 	int rc;
171 	char *shown_name = "abc";
172 
173 	/* Always release inner scratch buffer, before releasing outer. */
174 	rc = get_scratch_buff(pout_buff, 10, &tmp_buffer);
175 
176 	if (rc)
177 		goto err;
178 
179 	buffer = (unsigned char *)tmp_buffer.data;
180 
181 	rc = write_magic(pout_buff);
182 
183 	if (rc)
184 		goto err1;
185 
186 	/* chunk for File Entry */
187 	buffer[0] = fsize & 255;
188 	buffer[1] = (fsize >> 8) & 255;
189 	buffer[2] = (fsize >> 16) & 255;
190 	buffer[3] = (fsize >> 24) & 255;
191 	buffer[4] = 0;
192 	buffer[5] = 0;
193 	buffer[6] = 0;
194 	buffer[7] = 0;
195 	buffer[8] = (strlen(shown_name)+1) & 255;
196 	buffer[9] = (unsigned char)((strlen(shown_name)+1) >> 8);
197 	checksum = 1L;
198 	checksum = update_adler32(checksum, buffer, 10);
199 	checksum = update_adler32(checksum, shown_name,
200 				  (int)strlen(shown_name)+1);
201 
202 	rc = write_chunk_header(pout_buff, 1, 0,
203 				10+(unsigned long)strlen(shown_name)+1,
204 				checksum, 0);
205 
206 	if (rc)
207 		goto err1;
208 
209 	rc = write_to_buf(pout_buff->data, pout_buff->size,
210 			  &(pout_buff->offset), buffer, 10);
211 
212 	if (rc)
213 		goto err1;
214 
215 	rc = write_to_buf(pout_buff->data, pout_buff->size,
216 			   &(pout_buff->offset), shown_name,
217 			   (u32)strlen(shown_name)+1);
218 
219 	if (rc)
220 		goto err1;
221 
222 err1:
223 	release_scratch_buff(&tmp_buffer, pout_buff);
224 err:
225 	return rc;
226 }
227 
228 int
229 compress_buff(struct cudbg_buffer *pin_buff, struct cudbg_buffer *pout_buff)
230 {
231 	struct cudbg_buffer tmp_buffer;
232 	struct cudbg_hdr *cudbg_hdr;
233 	unsigned long checksum;
234 	unsigned char *result;
235 	unsigned int bytes_read;
236 	int chunk_size, level = 2, rc = 0;
237 	int compress_method = 1;
238 
239 	bytes_read = pin_buff->size;
240 	rc = get_scratch_buff(pout_buff, CUDBG_BLOCK_SIZE, &tmp_buffer);
241 
242 	if (rc)
243 		goto err;
244 
245 	result = (unsigned char *)tmp_buffer.data;
246 
247 	if (bytes_read < 32)
248 		compress_method = 0;
249 
250 	cudbg_hdr = (struct cudbg_hdr *)  pout_buff->data;
251 
252 	switch (compress_method) {
253 	case 1:
254 		chunk_size = fastlz_compress_level(level, pin_buff->data,
255 						   bytes_read, result);
256 
257 		checksum = update_adler32(1L, result, chunk_size);
258 
259 		if ((chunk_size > 62000) && (cudbg_hdr->reserved[7] < (u32)
260 		    chunk_size))   /* 64512 */
261 			cudbg_hdr->reserved[7] = (u32) chunk_size;
262 
263 		rc = write_chunk_header(pout_buff, 17, 1, chunk_size, checksum,
264 					bytes_read);
265 
266 		if (rc)
267 			goto err_put_buff;
268 
269 		rc = write_to_buf(pout_buff->data, pout_buff->size,
270 				  &pout_buff->offset, result, chunk_size);
271 
272 		if (rc)
273 			goto err_put_buff;
274 
275 		break;
276 
277 		/* uncompressed, also fallback method */
278 	case 0:
279 	default:
280 		checksum = update_adler32(1L, pin_buff->data, bytes_read);
281 
282 		rc = write_chunk_header(pout_buff, 17, 0, bytes_read, checksum,
283 					bytes_read);
284 
285 		if (rc)
286 			goto err_put_buff;
287 
288 		rc = write_to_buf(pout_buff->data, pout_buff->size,
289 				  &pout_buff->offset, pin_buff->data,
290 				  bytes_read);
291 		if (rc)
292 			goto err_put_buff;
293 
294 		break;
295 	}
296 
297 err_put_buff:
298 	release_scratch_buff(&tmp_buffer, pout_buff);
299 err:
300 	return rc;
301 }
302 
303 /* return non-zero if magic sequence is detected */
304 /* warning: reset the read pointer to the beginning of the file */
305 int
306 detect_magic(struct cudbg_buffer *_c_buff)
307 {
308 	unsigned char buffer[8];
309 	size_t bytes_read;
310 	int c;
311 
312 	bytes_read = read_from_buf(_c_buff->data, _c_buff->size,
313 				   &_c_buff->offset, buffer, 8);
314 
315 	if (bytes_read < 8)
316 		return 0;
317 
318 	for (c = 0; c < 8; c++)
319 		if (buffer[c] != sixpack_magic[c])
320 			return 0;
321 
322 	return -1;
323 }
324 
325 static unsigned long
326 readU16(const unsigned char *ptr)
327 {
328 	return ptr[0]+(ptr[1]<<8);
329 }
330 
331 static unsigned long
332 readU32(const unsigned char *ptr)
333 {
334 	return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24);
335 }
336 
337 int
338 read_chunk_header(struct cudbg_buffer *pc_buff, int *pid, int *poptions,
339 		  unsigned long *psize, unsigned long *pchecksum,
340 		  unsigned long *pextra)
341 {
342 	unsigned char buffer[CUDBG_CHUNK_BUF_LEN];
343 	int byte_r = read_from_buf(pc_buff->data, pc_buff->size,
344 				   &pc_buff->offset, buffer, 16);
345 	if (byte_r == 0)
346 		return 0;
347 
348 	*pid = readU16(buffer) & 0xffff;
349 	*poptions = readU16(buffer+2) & 0xffff;
350 	*psize = readU32(buffer+4) & 0xffffffff;
351 	*pchecksum = readU32(buffer+8) & 0xffffffff;
352 	*pextra = readU32(buffer+12) & 0xffffffff;
353 	return 0;
354 }
355 
356 int
357 validate_buffer(struct cudbg_buffer *compressed_buffer)
358 {
359 	if (!detect_magic(compressed_buffer))
360 		return CUDBG_STATUS_INVALID_BUFF;
361 
362 	return 0;
363 }
364 
365 int
366 decompress_buffer(struct cudbg_buffer *pc_buff,
367 		  struct cudbg_buffer *pd_buff)
368 {
369 	struct cudbg_buffer tmp_compressed_buffer;
370 	struct cudbg_buffer tmp_decompressed_buffer;
371 	unsigned char *compressed_buffer;
372 	unsigned char *decompressed_buffer;
373 	unsigned char buffer[CUDBG_MIN_COMPR_LEN];
374 	unsigned long chunk_size;
375 	unsigned long chunk_checksum;
376 	unsigned long chunk_extra;
377 	unsigned long checksum;
378 	unsigned long total_extracted = 0;
379 	unsigned long r;
380 	unsigned long remaining;
381 	unsigned long bytes_read;
382 	u32 decompressed_size = 0;
383 	int chunk_id, chunk_options, rc;
384 
385 	if (pd_buff->size < 2 * CUDBG_BLOCK_SIZE)
386 		return CUDBG_STATUS_SMALL_BUFF;
387 
388 	rc = get_scratch_buff(pd_buff, CUDBG_BLOCK_SIZE,
389 			      &tmp_compressed_buffer);
390 
391 	if (rc)
392 		goto err_cbuff;
393 
394 	rc = get_scratch_buff(pd_buff, CUDBG_BLOCK_SIZE,
395 			      &tmp_decompressed_buffer);
396 	if (rc)
397 		goto err_dcbuff;
398 
399 	compressed_buffer = (unsigned char *)tmp_compressed_buffer.data;
400 	decompressed_buffer = (unsigned char *)tmp_decompressed_buffer.data;
401 
402 	/* main loop */
403 
404 	for (;;) {
405 		if (pc_buff->offset > pc_buff->size)
406 			break;
407 
408 		rc =  read_chunk_header(pc_buff, &chunk_id, &chunk_options,
409 					&chunk_size, &chunk_checksum,
410 					&chunk_extra);
411 		if (rc != 0)
412 			break;
413 
414 		/* skip 8+16 */
415 		if ((chunk_id == 1) && (chunk_size > 10) &&
416 		    (chunk_size < CUDBG_BLOCK_SIZE)) {
417 
418 			bytes_read = read_from_buf(pc_buff->data, pc_buff->size,
419 						   &pc_buff->offset, buffer,
420 						   chunk_size);
421 
422 			if (bytes_read == 0)
423 				return 0;
424 
425 			checksum = update_adler32(1L, buffer, chunk_size);
426 			if (checksum != chunk_checksum)
427 				return CUDBG_STATUS_CHKSUM_MISSMATCH;
428 
429 			decompressed_size = (u32)readU32(buffer);
430 
431 			if (pd_buff->size < decompressed_size) {
432 
433 				pd_buff->size = 2 * CUDBG_BLOCK_SIZE +
434 						decompressed_size;
435 				pc_buff->offset -= chunk_size + 16;
436 				return CUDBG_STATUS_SMALL_BUFF;
437 			}
438 			total_extracted = 0;
439 
440 		}
441 
442 		if (chunk_size > CUDBG_BLOCK_SIZE) {
443 			/* Release old allocated memory */
444 			release_scratch_buff(&tmp_decompressed_buffer, pd_buff);
445 			release_scratch_buff(&tmp_compressed_buffer, pd_buff);
446 
447 			/* allocate new memory with chunk_size size */
448 			rc = get_scratch_buff(pd_buff, chunk_size,
449 					      &tmp_compressed_buffer);
450 			if (rc)
451 				goto err_cbuff;
452 
453 			rc = get_scratch_buff(pd_buff, chunk_size,
454 					      &tmp_decompressed_buffer);
455 			if (rc)
456 				goto err_dcbuff;
457 
458 			compressed_buffer = (unsigned char *)tmp_compressed_buffer.data;
459 			decompressed_buffer = (unsigned char *)tmp_decompressed_buffer.data;
460 		}
461 
462 		if ((chunk_id == 17) && decompressed_size) {
463 			/* uncompressed */
464 			switch (chunk_options) {
465 				/* stored, simply copy to output */
466 			case 0:
467 				total_extracted += chunk_size;
468 				remaining = chunk_size;
469 				checksum = 1L;
470 				for (;;) {
471 					/* Write a funtion for this */
472 					r = (CUDBG_BLOCK_SIZE < remaining) ?
473 					    CUDBG_BLOCK_SIZE : remaining;
474 					bytes_read =
475 					read_from_buf(pc_buff->data,
476 						      pc_buff->size,
477 						      &pc_buff->offset, buffer,
478 						      r);
479 
480 					if (bytes_read == 0)
481 						return 0;
482 
483 					write_to_buf(pd_buff->data,
484 						     pd_buff->size,
485 						     &pd_buff->offset, buffer,
486 						     bytes_read);
487 					checksum = update_adler32(checksum,
488 								  buffer,
489 								  bytes_read);
490 					remaining -= bytes_read;
491 
492 					/* verify everything is written
493 					 * correctly */
494 					if (checksum != chunk_checksum)
495 						return
496 						CUDBG_STATUS_CHKSUM_MISSMATCH;
497 				}
498 
499 				break;
500 
501 				/* compressed using FastLZ */
502 			case 1:
503 				bytes_read = read_from_buf(pc_buff->data,
504 							   pc_buff->size,
505 							   &pc_buff->offset,
506 							   compressed_buffer,
507 							   chunk_size);
508 
509 				if (bytes_read == 0)
510 					return 0;
511 
512 				checksum = update_adler32(1L, compressed_buffer,
513 							  chunk_size);
514 				total_extracted += chunk_extra;
515 
516 				/* verify that the chunk data is correct */
517 				if (checksum != chunk_checksum) {
518 					return CUDBG_STATUS_CHKSUM_MISSMATCH;
519 				} else {
520 					/* decompress and verify */
521 					remaining =
522 					fastlz_decompress(compressed_buffer,
523 							  chunk_size,
524 							  decompressed_buffer,
525 							  chunk_extra);
526 
527 					if (remaining != chunk_extra) {
528 						rc =
529 						CUDBG_STATUS_DECOMPRESS_FAIL;
530 						goto err;
531 					} else {
532 						write_to_buf(pd_buff->data,
533 							     pd_buff->size,
534 							     &pd_buff->offset,
535 							     decompressed_buffer,
536 							     chunk_extra);
537 					}
538 				}
539 				break;
540 
541 			default:
542 				break;
543 			}
544 
545 		}
546 
547 	}
548 
549 err:
550 	release_scratch_buff(&tmp_decompressed_buffer, pd_buff);
551 err_dcbuff:
552 	release_scratch_buff(&tmp_compressed_buffer, pd_buff);
553 
554 err_cbuff:
555 	return rc;
556 }
557