178acc472SPeter Tyser /* 278acc472SPeter Tyser * LZO1X Decompressor from MiniLZO 378acc472SPeter Tyser * 478acc472SPeter Tyser * Copyright (C) 1996-2005 Markus F.X.J. Oberhumer <markus@oberhumer.com> 578acc472SPeter Tyser * 678acc472SPeter Tyser * The full LZO package can be found at: 778acc472SPeter Tyser * http://www.oberhumer.com/opensource/lzo/ 878acc472SPeter Tyser * 978acc472SPeter Tyser * Changed for kernel use by: 1078acc472SPeter Tyser * Nitin Gupta <nitingupta910@gmail.com> 1178acc472SPeter Tyser * Richard Purdie <rpurdie@openedhand.com> 1278acc472SPeter Tyser */ 1378acc472SPeter Tyser 1478acc472SPeter Tyser #include <common.h> 1578acc472SPeter Tyser #include <linux/lzo.h> 1678acc472SPeter Tyser #include <asm/byteorder.h> 1778acc472SPeter Tyser #include <asm/unaligned.h> 1878acc472SPeter Tyser #include "lzodefs.h" 1978acc472SPeter Tyser 2078acc472SPeter Tyser #define HAVE_IP(x, ip_end, ip) ((size_t)(ip_end - ip) < (x)) 2178acc472SPeter Tyser #define HAVE_OP(x, op_end, op) ((size_t)(op_end - op) < (x)) 2278acc472SPeter Tyser #define HAVE_LB(m_pos, out, op) (m_pos < out || m_pos >= op) 2378acc472SPeter Tyser 2478acc472SPeter Tyser #define COPY4(dst, src) \ 2578acc472SPeter Tyser put_unaligned(get_unaligned((const u32 *)(src)), (u32 *)(dst)) 2678acc472SPeter Tyser 2778acc472SPeter Tyser static const unsigned char lzop_magic[] = { 2878acc472SPeter Tyser 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a 2978acc472SPeter Tyser }; 3078acc472SPeter Tyser 3178acc472SPeter Tyser #define HEADER_HAS_FILTER 0x00000800L 3278acc472SPeter Tyser 3378acc472SPeter Tyser static inline const unsigned char *parse_header(const unsigned char *src) 3478acc472SPeter Tyser { 3578acc472SPeter Tyser u16 version; 3678acc472SPeter Tyser int i; 3778acc472SPeter Tyser 3878acc472SPeter Tyser /* read magic: 9 first bytes */ 3978acc472SPeter Tyser for (i = 0; i < ARRAY_SIZE(lzop_magic); i++) { 4078acc472SPeter Tyser if (*src++ != lzop_magic[i]) 4178acc472SPeter Tyser return NULL; 4278acc472SPeter Tyser } 4378acc472SPeter Tyser /* get version (2bytes), skip library version (2), 4478acc472SPeter Tyser * 'need to be extracted' version (2) and 4578acc472SPeter Tyser * method (1) */ 4678acc472SPeter Tyser version = get_unaligned_be16(src); 4778acc472SPeter Tyser src += 7; 4878acc472SPeter Tyser if (version >= 0x0940) 497b8ffea2SMarek Vasut src++; 5078acc472SPeter Tyser if (get_unaligned_be32(src) & HEADER_HAS_FILTER) 5178acc472SPeter Tyser src += 4; /* filter info */ 5278acc472SPeter Tyser 5378acc472SPeter Tyser /* skip flags, mode and mtime_low */ 5478acc472SPeter Tyser src += 12; 5578acc472SPeter Tyser if (version >= 0x0940) 5678acc472SPeter Tyser src += 4; /* skip mtime_high */ 5778acc472SPeter Tyser 5878acc472SPeter Tyser i = *src++; 5978acc472SPeter Tyser /* don't care about the file name, and skip checksum */ 6078acc472SPeter Tyser src += i + 4; 6178acc472SPeter Tyser 6278acc472SPeter Tyser return src; 6378acc472SPeter Tyser } 6478acc472SPeter Tyser 6578acc472SPeter Tyser int lzop_decompress(const unsigned char *src, size_t src_len, 6678acc472SPeter Tyser unsigned char *dst, size_t *dst_len) 6778acc472SPeter Tyser { 6878acc472SPeter Tyser unsigned char *start = dst; 6978acc472SPeter Tyser const unsigned char *send = src + src_len; 7078acc472SPeter Tyser u32 slen, dlen; 71ff9d2efdSKees Cook size_t tmp, remaining; 7278acc472SPeter Tyser int r; 7378acc472SPeter Tyser 7478acc472SPeter Tyser src = parse_header(src); 7578acc472SPeter Tyser if (!src) 7678acc472SPeter Tyser return LZO_E_ERROR; 7778acc472SPeter Tyser 78ff9d2efdSKees Cook remaining = *dst_len; 7978acc472SPeter Tyser while (src < send) { 8078acc472SPeter Tyser /* read uncompressed block size */ 8178acc472SPeter Tyser dlen = get_unaligned_be32(src); 8278acc472SPeter Tyser src += 4; 8378acc472SPeter Tyser 8478acc472SPeter Tyser /* exit if last block */ 8578acc472SPeter Tyser if (dlen == 0) { 8678acc472SPeter Tyser *dst_len = dst - start; 8778acc472SPeter Tyser return LZO_E_OK; 8878acc472SPeter Tyser } 8978acc472SPeter Tyser 9078acc472SPeter Tyser /* read compressed block size, and skip block checksum info */ 9178acc472SPeter Tyser slen = get_unaligned_be32(src); 9278acc472SPeter Tyser src += 8; 9378acc472SPeter Tyser 9478acc472SPeter Tyser if (slen <= 0 || slen > dlen) 9578acc472SPeter Tyser return LZO_E_ERROR; 9678acc472SPeter Tyser 97ff9d2efdSKees Cook /* abort if buffer ran out of room */ 98ff9d2efdSKees Cook if (dlen > remaining) 99ff9d2efdSKees Cook return LZO_E_OUTPUT_OVERRUN; 100ff9d2efdSKees Cook 101*a2cfc8d5SJoris Lijssens /* When the input data is not compressed at all, 102*a2cfc8d5SJoris Lijssens * lzo1x_decompress_safe will fail, so call memcpy() 103*a2cfc8d5SJoris Lijssens * instead */ 104*a2cfc8d5SJoris Lijssens if (dlen == slen) { 105*a2cfc8d5SJoris Lijssens memcpy(dst, src, slen); 106*a2cfc8d5SJoris Lijssens } else { 10778acc472SPeter Tyser /* decompress */ 10878acc472SPeter Tyser tmp = dlen; 10978acc472SPeter Tyser r = lzo1x_decompress_safe((u8 *)src, slen, dst, &tmp); 11078acc472SPeter Tyser 111670c0179SSimon Glass if (r != LZO_E_OK) { 112670c0179SSimon Glass *dst_len = dst - start; 11378acc472SPeter Tyser return r; 114670c0179SSimon Glass } 11578acc472SPeter Tyser 11678acc472SPeter Tyser if (dlen != tmp) 11778acc472SPeter Tyser return LZO_E_ERROR; 118*a2cfc8d5SJoris Lijssens } 11978acc472SPeter Tyser 12078acc472SPeter Tyser src += slen; 12178acc472SPeter Tyser dst += dlen; 122ff9d2efdSKees Cook remaining -= dlen; 12378acc472SPeter Tyser } 12478acc472SPeter Tyser 12578acc472SPeter Tyser return LZO_E_INPUT_OVERRUN; 12678acc472SPeter Tyser } 12778acc472SPeter Tyser 12878acc472SPeter Tyser int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 12978acc472SPeter Tyser unsigned char *out, size_t *out_len) 13078acc472SPeter Tyser { 13178acc472SPeter Tyser const unsigned char * const ip_end = in + in_len; 13278acc472SPeter Tyser unsigned char * const op_end = out + *out_len; 13378acc472SPeter Tyser const unsigned char *ip = in, *m_pos; 13478acc472SPeter Tyser unsigned char *op = out; 13578acc472SPeter Tyser size_t t; 13678acc472SPeter Tyser 13778acc472SPeter Tyser *out_len = 0; 13878acc472SPeter Tyser 13978acc472SPeter Tyser if (*ip > 17) { 14078acc472SPeter Tyser t = *ip++ - 17; 14178acc472SPeter Tyser if (t < 4) 14278acc472SPeter Tyser goto match_next; 14378acc472SPeter Tyser if (HAVE_OP(t, op_end, op)) 14478acc472SPeter Tyser goto output_overrun; 14578acc472SPeter Tyser if (HAVE_IP(t + 1, ip_end, ip)) 14678acc472SPeter Tyser goto input_overrun; 14778acc472SPeter Tyser do { 14878acc472SPeter Tyser *op++ = *ip++; 14978acc472SPeter Tyser } while (--t > 0); 15078acc472SPeter Tyser goto first_literal_run; 15178acc472SPeter Tyser } 15278acc472SPeter Tyser 15378acc472SPeter Tyser while ((ip < ip_end)) { 15478acc472SPeter Tyser t = *ip++; 15578acc472SPeter Tyser if (t >= 16) 15678acc472SPeter Tyser goto match; 15778acc472SPeter Tyser if (t == 0) { 15878acc472SPeter Tyser if (HAVE_IP(1, ip_end, ip)) 15978acc472SPeter Tyser goto input_overrun; 16078acc472SPeter Tyser while (*ip == 0) { 16178acc472SPeter Tyser t += 255; 16278acc472SPeter Tyser ip++; 16378acc472SPeter Tyser if (HAVE_IP(1, ip_end, ip)) 16478acc472SPeter Tyser goto input_overrun; 16578acc472SPeter Tyser } 16678acc472SPeter Tyser t += 15 + *ip++; 16778acc472SPeter Tyser } 16878acc472SPeter Tyser if (HAVE_OP(t + 3, op_end, op)) 16978acc472SPeter Tyser goto output_overrun; 17078acc472SPeter Tyser if (HAVE_IP(t + 4, ip_end, ip)) 17178acc472SPeter Tyser goto input_overrun; 17278acc472SPeter Tyser 17378acc472SPeter Tyser COPY4(op, ip); 17478acc472SPeter Tyser op += 4; 17578acc472SPeter Tyser ip += 4; 17678acc472SPeter Tyser if (--t > 0) { 17778acc472SPeter Tyser if (t >= 4) { 17878acc472SPeter Tyser do { 17978acc472SPeter Tyser COPY4(op, ip); 18078acc472SPeter Tyser op += 4; 18178acc472SPeter Tyser ip += 4; 18278acc472SPeter Tyser t -= 4; 18378acc472SPeter Tyser } while (t >= 4); 18478acc472SPeter Tyser if (t > 0) { 18578acc472SPeter Tyser do { 18678acc472SPeter Tyser *op++ = *ip++; 18778acc472SPeter Tyser } while (--t > 0); 18878acc472SPeter Tyser } 18978acc472SPeter Tyser } else { 19078acc472SPeter Tyser do { 19178acc472SPeter Tyser *op++ = *ip++; 19278acc472SPeter Tyser } while (--t > 0); 19378acc472SPeter Tyser } 19478acc472SPeter Tyser } 19578acc472SPeter Tyser 19678acc472SPeter Tyser first_literal_run: 19778acc472SPeter Tyser t = *ip++; 19878acc472SPeter Tyser if (t >= 16) 19978acc472SPeter Tyser goto match; 20078acc472SPeter Tyser m_pos = op - (1 + M2_MAX_OFFSET); 20178acc472SPeter Tyser m_pos -= t >> 2; 20278acc472SPeter Tyser m_pos -= *ip++ << 2; 20378acc472SPeter Tyser 20478acc472SPeter Tyser if (HAVE_LB(m_pos, out, op)) 20578acc472SPeter Tyser goto lookbehind_overrun; 20678acc472SPeter Tyser 20778acc472SPeter Tyser if (HAVE_OP(3, op_end, op)) 20878acc472SPeter Tyser goto output_overrun; 20978acc472SPeter Tyser *op++ = *m_pos++; 21078acc472SPeter Tyser *op++ = *m_pos++; 21178acc472SPeter Tyser *op++ = *m_pos; 21278acc472SPeter Tyser 21378acc472SPeter Tyser goto match_done; 21478acc472SPeter Tyser 21578acc472SPeter Tyser do { 21678acc472SPeter Tyser match: 21778acc472SPeter Tyser if (t >= 64) { 21878acc472SPeter Tyser m_pos = op - 1; 21978acc472SPeter Tyser m_pos -= (t >> 2) & 7; 22078acc472SPeter Tyser m_pos -= *ip++ << 3; 22178acc472SPeter Tyser t = (t >> 5) - 1; 22278acc472SPeter Tyser if (HAVE_LB(m_pos, out, op)) 22378acc472SPeter Tyser goto lookbehind_overrun; 22478acc472SPeter Tyser if (HAVE_OP(t + 3 - 1, op_end, op)) 22578acc472SPeter Tyser goto output_overrun; 22678acc472SPeter Tyser goto copy_match; 22778acc472SPeter Tyser } else if (t >= 32) { 22878acc472SPeter Tyser t &= 31; 22978acc472SPeter Tyser if (t == 0) { 23078acc472SPeter Tyser if (HAVE_IP(1, ip_end, ip)) 23178acc472SPeter Tyser goto input_overrun; 23278acc472SPeter Tyser while (*ip == 0) { 23378acc472SPeter Tyser t += 255; 23478acc472SPeter Tyser ip++; 23578acc472SPeter Tyser if (HAVE_IP(1, ip_end, ip)) 23678acc472SPeter Tyser goto input_overrun; 23778acc472SPeter Tyser } 23878acc472SPeter Tyser t += 31 + *ip++; 23978acc472SPeter Tyser } 24078acc472SPeter Tyser m_pos = op - 1; 24178acc472SPeter Tyser m_pos -= get_unaligned_le16(ip) >> 2; 24278acc472SPeter Tyser ip += 2; 24378acc472SPeter Tyser } else if (t >= 16) { 24478acc472SPeter Tyser m_pos = op; 24578acc472SPeter Tyser m_pos -= (t & 8) << 11; 24678acc472SPeter Tyser 24778acc472SPeter Tyser t &= 7; 24878acc472SPeter Tyser if (t == 0) { 24978acc472SPeter Tyser if (HAVE_IP(1, ip_end, ip)) 25078acc472SPeter Tyser goto input_overrun; 25178acc472SPeter Tyser while (*ip == 0) { 25278acc472SPeter Tyser t += 255; 25378acc472SPeter Tyser ip++; 25478acc472SPeter Tyser if (HAVE_IP(1, ip_end, ip)) 25578acc472SPeter Tyser goto input_overrun; 25678acc472SPeter Tyser } 25778acc472SPeter Tyser t += 7 + *ip++; 25878acc472SPeter Tyser } 25978acc472SPeter Tyser m_pos -= get_unaligned_le16(ip) >> 2; 26078acc472SPeter Tyser ip += 2; 26178acc472SPeter Tyser if (m_pos == op) 26278acc472SPeter Tyser goto eof_found; 26378acc472SPeter Tyser m_pos -= 0x4000; 26478acc472SPeter Tyser } else { 26578acc472SPeter Tyser m_pos = op - 1; 26678acc472SPeter Tyser m_pos -= t >> 2; 26778acc472SPeter Tyser m_pos -= *ip++ << 2; 26878acc472SPeter Tyser 26978acc472SPeter Tyser if (HAVE_LB(m_pos, out, op)) 27078acc472SPeter Tyser goto lookbehind_overrun; 27178acc472SPeter Tyser if (HAVE_OP(2, op_end, op)) 27278acc472SPeter Tyser goto output_overrun; 27378acc472SPeter Tyser 27478acc472SPeter Tyser *op++ = *m_pos++; 27578acc472SPeter Tyser *op++ = *m_pos; 27678acc472SPeter Tyser goto match_done; 27778acc472SPeter Tyser } 27878acc472SPeter Tyser 27978acc472SPeter Tyser if (HAVE_LB(m_pos, out, op)) 28078acc472SPeter Tyser goto lookbehind_overrun; 28178acc472SPeter Tyser if (HAVE_OP(t + 3 - 1, op_end, op)) 28278acc472SPeter Tyser goto output_overrun; 28378acc472SPeter Tyser 28478acc472SPeter Tyser if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) { 28578acc472SPeter Tyser COPY4(op, m_pos); 28678acc472SPeter Tyser op += 4; 28778acc472SPeter Tyser m_pos += 4; 28878acc472SPeter Tyser t -= 4 - (3 - 1); 28978acc472SPeter Tyser do { 29078acc472SPeter Tyser COPY4(op, m_pos); 29178acc472SPeter Tyser op += 4; 29278acc472SPeter Tyser m_pos += 4; 29378acc472SPeter Tyser t -= 4; 29478acc472SPeter Tyser } while (t >= 4); 29578acc472SPeter Tyser if (t > 0) 29678acc472SPeter Tyser do { 29778acc472SPeter Tyser *op++ = *m_pos++; 29878acc472SPeter Tyser } while (--t > 0); 29978acc472SPeter Tyser } else { 30078acc472SPeter Tyser copy_match: 30178acc472SPeter Tyser *op++ = *m_pos++; 30278acc472SPeter Tyser *op++ = *m_pos++; 30378acc472SPeter Tyser do { 30478acc472SPeter Tyser *op++ = *m_pos++; 30578acc472SPeter Tyser } while (--t > 0); 30678acc472SPeter Tyser } 30778acc472SPeter Tyser match_done: 30878acc472SPeter Tyser t = ip[-2] & 3; 30978acc472SPeter Tyser if (t == 0) 31078acc472SPeter Tyser break; 31178acc472SPeter Tyser match_next: 31278acc472SPeter Tyser if (HAVE_OP(t, op_end, op)) 31378acc472SPeter Tyser goto output_overrun; 31478acc472SPeter Tyser if (HAVE_IP(t + 1, ip_end, ip)) 31578acc472SPeter Tyser goto input_overrun; 31678acc472SPeter Tyser 31778acc472SPeter Tyser *op++ = *ip++; 31878acc472SPeter Tyser if (t > 1) { 31978acc472SPeter Tyser *op++ = *ip++; 32078acc472SPeter Tyser if (t > 2) 32178acc472SPeter Tyser *op++ = *ip++; 32278acc472SPeter Tyser } 32378acc472SPeter Tyser 32478acc472SPeter Tyser t = *ip++; 32578acc472SPeter Tyser } while (ip < ip_end); 32678acc472SPeter Tyser } 32778acc472SPeter Tyser 32878acc472SPeter Tyser *out_len = op - out; 32978acc472SPeter Tyser return LZO_E_EOF_NOT_FOUND; 33078acc472SPeter Tyser 33178acc472SPeter Tyser eof_found: 33278acc472SPeter Tyser *out_len = op - out; 33378acc472SPeter Tyser return (ip == ip_end ? LZO_E_OK : 33478acc472SPeter Tyser (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); 33578acc472SPeter Tyser input_overrun: 33678acc472SPeter Tyser *out_len = op - out; 33778acc472SPeter Tyser return LZO_E_INPUT_OVERRUN; 33878acc472SPeter Tyser 33978acc472SPeter Tyser output_overrun: 34078acc472SPeter Tyser *out_len = op - out; 34178acc472SPeter Tyser return LZO_E_OUTPUT_OVERRUN; 34278acc472SPeter Tyser 34378acc472SPeter Tyser lookbehind_overrun: 34478acc472SPeter Tyser *out_len = op - out; 34578acc472SPeter Tyser return LZO_E_LOOKBEHIND_OVERRUN; 34678acc472SPeter Tyser } 347