xref: /illumos-gate/usr/src/uts/common/io/cxgbe/t4nex/fastlz_api.c (revision 7e6ad4690aa1d3cbf260ef54d5e9632ae40a782e)
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
update_adler32(unsigned long checksum,const void * buf,int len)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
write_magic(struct cudbg_buffer * _out_buff)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
write_to_buf(void * out_buf,u32 out_buf_size,u32 * offset,void * in_buf,u32 in_buf_size)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
read_from_buf(void * in_buf,u32 in_buf_size,u32 * offset,void * out_buf,u32 out_buf_size)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
write_chunk_header(struct cudbg_buffer * _outbuf,int id,int options,unsigned long size,unsigned long checksum,unsigned long extra)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
write_compression_hdr(struct cudbg_buffer * pin_buff,struct cudbg_buffer * pout_buff)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
compress_buff(struct cudbg_buffer * pin_buff,struct cudbg_buffer * pout_buff)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
detect_magic(struct cudbg_buffer * _c_buff)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
readU16(const unsigned char * ptr)326  readU16(const unsigned char *ptr)
327  {
328  	return ptr[0]+(ptr[1]<<8);
329  }
330  
331  static unsigned long
readU32(const unsigned char * ptr)332  readU32(const unsigned char *ptr)
333  {
334  	return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24);
335  }
336  
337  int
read_chunk_header(struct cudbg_buffer * pc_buff,int * pid,int * poptions,unsigned long * psize,unsigned long * pchecksum,unsigned long * pextra)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
validate_buffer(struct cudbg_buffer * compressed_buffer)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
decompress_buffer(struct cudbg_buffer * pc_buff,struct cudbg_buffer * pd_buff)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