From patchwork Sun Feb 15 23:16:06 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Nelson X-Patchwork-Id: 439940 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id EE664140172 for ; Mon, 16 Feb 2015 10:16:23 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id D98EA4B8FD; Mon, 16 Feb 2015 00:16:21 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id l3Qe9hNavB_v; Mon, 16 Feb 2015 00:16:21 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 265484B8F8; Mon, 16 Feb 2015 00:16:21 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id BDF8D4B8F8 for ; Mon, 16 Feb 2015 00:16:17 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id BY6MELdH2trC for ; Mon, 16 Feb 2015 00:16:17 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-pa0-f49.google.com (mail-pa0-f49.google.com [209.85.220.49]) by theia.denx.de (Postfix) with ESMTPS id 41C844B8DF for ; Mon, 16 Feb 2015 00:16:14 +0100 (CET) Received: by mail-pa0-f49.google.com with SMTP id fb1so31087468pad.8 for ; Sun, 15 Feb 2015 15:16:13 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=vqOVtp24AQpspaD5GvJJ/B/f27VtP2nVKwcJZ009Gu0=; b=X3VCRj559UN5pLLLeSr2ZFqPvTm1L7jioXjXjJJeMrgcz0gKPEp8iEHJ67dvbw4AY2 maowoJCTOk+g6A5Bwb+PuVgPMcd0SKWi7MkzwCDsCytc5Dqun/7i0mbr+Op1WK7pTLxj KMjzRtyWZ/7JTIkg7SXEaKjPRJYprmi31llQegp9OZjs/15RzGgvHgM6bWehbhiQVZXs qWUrQkn5IDbeVL+l3Ch14GKeLDCkhdgoWiw0hAfHZnXB7NAd8fNUpE6PQtbf5SLOVc+n G8186Z6I9C1FGSgGTDpbzzkoXrpeGPkvbzXnSxJzYouNSMjV6g4hCx1RwnSi+jM6TAqG N2EQ== X-Gm-Message-State: ALoCoQnliT6SlsKhIZEP5gvEYQOiFWyc4Gzx5238hKhoycL9581lo+76X1u9JicIF+ZFWzO6Puj4 X-Received: by 10.68.221.34 with SMTP id qb2mr34600761pbc.47.1424042173004; Sun, 15 Feb 2015 15:16:13 -0800 (PST) Received: from ericsam.boundarydevices.com (wsip-70-184-93-199.ph.ph.cox.net. [70.184.93.199]) by mx.google.com with ESMTPSA id dx6sm12870453pab.14.2015.02.15.15.16.10 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 15 Feb 2015 15:16:11 -0800 (PST) From: Eric Nelson To: u-boot@lists.denx.de Date: Sun, 15 Feb 2015 16:16:06 -0700 Message-Id: <1424042167-27008-1-git-send-email-eric.nelson@boundarydevices.com> X-Mailer: git-send-email 1.9.1 Cc: marex@denx.de, p.marczak@samsung.com, yorksun@freescale.com, soren.brinkmann@xilinx.com Subject: [U-Boot] [PATCH 1/2] gunzip: add gzwrite routine for extracting compresed images to block device X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Initial filesystem images are generally highly compressible. Add a routine gzwrite that allows gzip-compressed images to be written to block devices. Signed-off-by: Eric Nelson --- include/common.h | 39 +++++++++++ lib/gunzip.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 232 insertions(+), 1 deletion(-) diff --git a/include/common.h b/include/common.h index 9129454..f96baea 100644 --- a/include/common.h +++ b/include/common.h @@ -713,6 +713,45 @@ int gunzip(void *, int, unsigned char *, unsigned long *); int zunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp, int stoponerr, int offset); +/** + * gzwrite progress indicators: defined weak to allow board-specific + * overrides: + * + * gzwrite_progress_init called on startup + * gzwrite_progress called during decompress/write loop + * gzwrite_progress_finish called at end of loop to + * indicate success (retcode=0) or failure + */ +void gzwrite_progress_init(u64 expected_size); + +void gzwrite_progress(int iteration, + u64 bytes_written, + u64 total_bytes); + +void gzwrite_progress_finish(int retcode, + u64 totalwritten, + u64 totalsize, + u32 expected_crc, + u32 calculated_crc); + +/** + * decompress and write gzipped image from memory to block device + * + * @param src compressed image address + * @param len compressed image length in bytes + * @param dev block device descriptor + * @param szwritebuf bytes per write (pad to erase size) + * @param startoffs offset in bytes of first write + * @param szexpected expected uncompressed length + * may be zero to use gzip trailer + * for files under 4GiB + */ +int gzwrite(unsigned char *src, int len, + struct block_dev_desc *dev, + unsigned long szwritebuf, + u64 startoffs, + u64 szexpected); + /* lib/qsort.c */ void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)); diff --git a/lib/gunzip.c b/lib/gunzip.c index f469fcb..d28fda8 100644 --- a/lib/gunzip.c +++ b/lib/gunzip.c @@ -12,6 +12,8 @@ #include #include +#define HEADER0 '\x1f' +#define HEADER1 '\x8b' #define ZALLOC_ALIGNMENT 16 #define HEAD_CRC 2 #define EXTRA_FIELD 4 @@ -66,6 +68,196 @@ int gunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp) return zunzip(dst, dstlen, src, lenp, 1, i); } +__weak +void gzwrite_progress_init(u64 expectedsize) +{ + putc('\n'); +} + +__weak +void gzwrite_progress(int iteration, + u64 bytes_written, + u64 total_bytes) +{ + if (0 == (iteration & 3)) + printf("%llu/%llu\r", bytes_written, total_bytes); +} + +__weak +void gzwrite_progress_finish(int returnval, + u64 bytes_written, + u64 total_bytes, + u32 expected_crc, + u32 calculated_crc) +{ + if (0 == returnval) { + printf("\n\t%llu bytes, crc 0x%08x\n", + total_bytes, calculated_crc); + } else { + printf("\n\tuncompressed %llu of %llu\n" + "\tcrcs == 0x%08x/0x%08x\n", + bytes_written, total_bytes, + expected_crc, calculated_crc); + } +} + +int gzwrite(unsigned char *src, int len, + struct block_dev_desc *dev, + unsigned long szwritebuf, + u64 startoffs, + u64 szexpected) +{ + int i, flags; + z_stream s; + int r = 0; + unsigned char *writebuf; + unsigned crc = 0; + u64 totalfilled = 0; + lbaint_t blksperbuf, outblock; + u32 expected_crc; + u32 payload_size; + int iteration = 0; + + if (!szwritebuf || + (szwritebuf % dev->blksz) || + (szwritebuf < dev->blksz)) { + printf("%s: size %lu not a multiple of %lu\n", + __func__, szwritebuf, dev->blksz); + return -1; + } + + if (startoffs % dev->blksz) { + printf("%s: start offset %llu not a multiple of %lu\n", + __func__, startoffs, dev->blksz); + return -1; + } + + blksperbuf = szwritebuf / dev->blksz; + outblock = startoffs / dev->blksz; + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != DEFLATED || (flags & RESERVED) != 0) { + puts("Error: Bad gzipped data\n"); + return -1; + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + + if (i >= len-8) { + puts("Error: gunzip out of data in header"); + return -1; + } + + payload_size = len - i - 8; + + memcpy(&expected_crc, src + len - 8, sizeof(expected_crc)); + expected_crc = le32_to_cpu(expected_crc); + u32 szuncompressed; + memcpy(&szuncompressed, src + len - 4, sizeof(szuncompressed)); + if (szexpected == 0) { + szexpected = le32_to_cpu(szuncompressed); + } else if (szuncompressed != (u32)szexpected) { + printf("size of %llx doesn't match trailer low bits %x\n", + szexpected, szuncompressed); + return -1; + } + if (szexpected / dev->blksz > (dev->lba - outblock)) { + printf("%s: uncompressed size %llu exceeds device size\n", + __func__, szexpected); + return -1; + } + + gzwrite_progress_init(szexpected); + + s.zalloc = gzalloc; + s.zfree = gzfree; + + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + printf("Error: inflateInit2() returned %d\n", r); + return -1; + } + + s.next_in = src + i; + s.avail_in = payload_size+8; + writebuf = (unsigned char *)malloc(szwritebuf); + + /* decompress until deflate stream ends or end of file */ + do { + if (s.avail_in == 0) { + printf("%s: weird termination with result %d\n", + __func__, r); + break; + } + + /* run inflate() on input until output buffer not full */ + do { + unsigned long blocks_written; + int numfilled; + lbaint_t writeblocks; + + s.avail_out = szwritebuf; + s.next_out = writebuf; + r = inflate(&s, Z_SYNC_FLUSH); + if ((r != Z_OK) && + (r != Z_STREAM_END)) { + printf("Error: inflate() returned %d\n", r); + goto out; + } + numfilled = szwritebuf - s.avail_out; + crc = crc32(crc, writebuf, numfilled); + totalfilled += numfilled; + if (numfilled < szwritebuf) { + writeblocks = (numfilled+dev->blksz-1) + / dev->blksz; + memset(writebuf+numfilled, 0, + dev->blksz-(numfilled%dev->blksz)); + } else { + writeblocks = blksperbuf; + } + + gzwrite_progress(iteration++, + totalfilled, + szexpected); + blocks_written = dev->block_write(dev->dev, + outblock, + writeblocks, + writebuf); + outblock += blocks_written; + if (ctrlc()) { + puts("abort\n"); + goto out; + } + WATCHDOG_RESET(); + } while (s.avail_out == 0); + /* done when inflate() says it's done */ + } while (r != Z_STREAM_END); + + if ((szexpected != totalfilled) || + (crc != expected_crc)) + r = -1; + else + r = 0; + +out: + gzwrite_progress_finish(r, totalfilled, szexpected, + expected_crc, crc); + free(writebuf); + inflateEnd(&s); + + return r; +} + /* * Uncompress blocks compressed with zlib without headers */ @@ -81,7 +273,7 @@ int zunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp, r = inflateInit2(&s, -MAX_WBITS); if (r != Z_OK) { - printf ("Error: inflateInit2() returned %d\n", r); + printf("Error: inflateInit2() returned %d\n", r); return -1; } s.next_in = src + offset;