[coreboot-gerrit] New patch to review for coreboot: selfboot: add sequential lz4 payload decompression
Antonello Dettori (dev@dettori.io)
gerrit at coreboot.org
Sat Jul 23 16:31:51 CEST 2016
Antonello Dettori (dev at dettori.io) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/15816
-gerrit
commit 1575de6964da7eeed6186f4ad52cb304cb83cfe9
Author: Antonello Dettori <dev at dettori.io>
Date: Sat Jul 23 16:03:34 2016 +0200
selfboot: add sequential lz4 payload decompression
Split the loading of lz4 compressed segments into multiple
parts in order to reduce the overall amount of resource required
to load them.
Change-Id: I889daca0d6232e6e88296150148ba847059c9de8
Signed-off-by: Antonello Dettori <dev at dettori.io>
---
src/commonlib/include/commonlib/compression.h | 6 ++
src/commonlib/lz4_wrapper.c | 91 +++++++++++++++++++++++++++
src/lib/selfboot.c | 6 +-
3 files changed, 98 insertions(+), 5 deletions(-)
diff --git a/src/commonlib/include/commonlib/compression.h b/src/commonlib/include/commonlib/compression.h
index 428ee42..a64cdf2 100644
--- a/src/commonlib/include/commonlib/compression.h
+++ b/src/commonlib/include/commonlib/compression.h
@@ -17,6 +17,7 @@
#define _COMMONLIB_COMPRESSION_H_
#include <stddef.h>
+#include <commonlib/region.h>
/* Decompresses an LZ4F image (multiple LZ4 blocks with frame header) from src
* to dst, ensuring that it doesn't read more than srcn bytes and doesn't write
@@ -28,6 +29,11 @@
*/
size_t ulz4fn(const void *src, size_t srcn, void *dst, size_t dstn);
+/* Same as ulz4fn() but it reads the content of the region_device
+ * procedurally. */
+size_t rdev_ulz4fn(const struct region_device *src, size_t srcn, void *dst,
+ size_t dstn);
+
/* Same as ulz4fn() but does not perform any bounds checks. */
size_t ulz4f(const void *src, void *dst);
diff --git a/src/commonlib/lz4_wrapper.c b/src/commonlib/lz4_wrapper.c
index 0342868..1b1b693 100644
--- a/src/commonlib/lz4_wrapper.c
+++ b/src/commonlib/lz4_wrapper.c
@@ -32,8 +32,11 @@
#include <commonlib/compression.h>
#include <commonlib/endian.h>
#include <commonlib/helpers.h>
+#include <commonlib/region.h>
+#include <stdlib.h>
#include <stdint.h>
#include <string.h>
+#include <console/console.h>
/* LZ4 comes with its own supposedly portable memory access functions, but they
* seem to be very inefficient in practice (at least on ARM64). Since coreboot
@@ -200,3 +203,91 @@ size_t ulz4f(const void *src, void *dst)
/* LZ4 uses signed size parameters, so can't just use ((u32)-1) here. */
return ulz4fn(src, 1*GiB, dst, 1*GiB);
}
+
+size_t rdev_ulz4fn(const struct region_device *src, size_t srcn, void *dst,
+ size_t dstn)
+{
+ void *out = dst;
+ size_t out_size = 0;
+ int has_block_checksum;
+ size_t rdev_offset = 0;
+
+ { /* With in-place decompression the header may become invalid later. */
+ struct lz4_frame_header h;
+
+ rdev_readat(src, &h, rdev_offset, sizeof(h));
+
+ if (srcn < sizeof(h) + sizeof(uint64_t) + sizeof(uint8_t))
+ return 0; /* input overrun */
+
+ /* We assume there's always only a single, standard frame. */
+ if (read_le32(&h.magic) != LZ4F_MAGICNUMBER || h.version != 1)
+ return 0; /* unknown format */
+ if (h.reserved0 || h.reserved1 || h.reserved2)
+ return 0; /* reserved must be zero */
+ if (!h.independent_blocks)
+ return 0; /* we don't support block dependency */
+ has_block_checksum = h.has_block_checksum;
+
+ rdev_offset += sizeof(h);
+ if (h.has_content_size)
+ rdev_offset += sizeof(uint64_t);
+ rdev_offset += sizeof(uint8_t);
+ }
+
+ void *in;
+
+ while (1) {
+ struct lz4_block_header b;
+ uint32_t tmp_le;
+
+ rdev_readat(src, &tmp_le, rdev_offset, sizeof(uint32_t));
+ b.raw = read_le32(&tmp_le);
+ rdev_offset += sizeof(struct lz4_block_header);
+
+ if (rdev_offset + b.size > srcn)
+ break; /* input overrun */
+
+ if (!b.size) {
+ out_size = out - dst;
+ break; /* decompression successful */
+ }
+
+
+ if (b.not_compressed) {
+ size_t size = MIN((uintptr_t)b.size,
+ (uintptr_t)dst + dstn - (uintptr_t)out);
+
+ rdev_readat(src, out, rdev_offset, size);
+
+ if (size < b.size)
+ break; /* output overrun */
+
+ out += size;
+
+ } else {
+ /* constant folding essential, do not touch params! */
+ printk(BIOS_EMERG, "b.size: %u\n", b.size);
+ printk(BIOS_EMERG, "rdev_offset: %lu\n", rdev_offset);
+
+ in = rdev_mmap(src, rdev_offset, b.size);
+
+ int ret = LZ4_decompress_generic(in, out, b.size,
+ dst + dstn - out, endOnInputSize,
+ full, 0, noDict, out, NULL, 0);
+ rdev_munmap(src, in);
+
+ if (ret < 0)
+ break; /* decompression error */
+
+ out += ret;
+
+ }
+
+ rdev_offset += b.size;
+ if (has_block_checksum)
+ rdev_offset += sizeof(uint32_t);
+ }
+
+ return out_size;
+}
diff --git a/src/lib/selfboot.c b/src/lib/selfboot.c
index a380165..f2c0e4f 100644
--- a/src/lib/selfboot.c
+++ b/src/lib/selfboot.c
@@ -440,11 +440,7 @@ static int load_self_segments(struct segment *head)
case CBFS_COMPRESS_LZ4: {
printk(BIOS_DEBUG, "using LZ4\n");
timestamp_add_now(TS_START_ULZ4F);
- src = rdev_mmap_full(&ptr->src);
- if (src == NULL)
- return 0;
-
- len = ulz4fn(src, len, dest, memsz);
+ len = rdev_ulz4fn(&ptr->src, len, dest, memsz);
timestamp_add_now(TS_END_ULZ4F);
if (!len) /* Decompression Error. */
return 0;
More information about the coreboot-gerrit
mailing list