diff mbox

[U-Boot,2/2] cmd: fsfitxtract: Extract a part of a FIT multi-image stored on a filesystem

Message ID 20170520065020.28287-2-lexszero@gmail.com
State Superseded
Delegated to: Tom Rini
Headers show

Commit Message

Alexey Ignatov May 20, 2017, 6:50 a.m. UTC
Sometimes we interested only in one single small subimage from big multipart
FIT. This command tries to avoid reading out the whole FIT because of memory
or time considerations.
Since we don't have mmap() in U-Boot, this is done by reading the file in
small chunks and trying to parse FIT until requested image is found or
total read size limit exceeded.

Signed-off-by: Alexey Ignatov <lexszero@gmail.com>
---
 cmd/Kconfig |   7 ++
 cmd/ximg.c  | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 233 insertions(+)

Comments

Tom Rini May 22, 2017, 2:07 p.m. UTC | #1
On Sat, May 20, 2017 at 09:50:20AM +0300, Alexey Ignatov wrote:

> Sometimes we interested only in one single small subimage from big multipart
> FIT. This command tries to avoid reading out the whole FIT because of memory
> or time considerations.
> Since we don't have mmap() in U-Boot, this is done by reading the file in
> small chunks and trying to parse FIT until requested image is found or
> total read size limit exceeded.
> 
> Signed-off-by: Alexey Ignatov <lexszero@gmail.com>
> ---
>  cmd/Kconfig |   7 ++
>  cmd/ximg.c  | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 233 insertions(+)

We need to add an entry for cmd/Makefile as CMD_XIMG and CMD_FSFITXIMG
do not depend on eachother.  I also think you need to guard the CMD_XIMG
specific code in cmd/ximg.c when adding this for when the old command is
disabled and the new one is enabled, thanks!
diff mbox

Patch

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 2e4d2334a6..02b8357224 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -265,6 +265,13 @@  config CMD_XIMG
 	help
 	  Extract a part of a multi-image.
 
+config CMD_FSFITXIMG
+	bool "fsfitxtract"
+	default y
+	depends on FIT
+	help
+	  Extract a part of a FIT multi-image stored on a filesystem
+
 config CMD_POWEROFF
 	bool
 
diff --git a/cmd/ximg.c b/cmd/ximg.c
index 73a571b52b..17d961d5b6 100644
--- a/cmd/ximg.c
+++ b/cmd/ximg.c
@@ -22,6 +22,11 @@ 
 #endif
 #include <asm/byteorder.h>
 #include <asm/io.h>
+#ifdef CONFIG_CMD_FSFITXIMG
+#include <fs.h>
+#include <div64.h>
+#include <libfdt.h>
+#endif
 
 #ifndef CONFIG_SYS_XIMG_LEN
 /* use 8MByte as default max gunzip size */
@@ -283,3 +288,224 @@  U_BOOT_CMD(
 	imxtract, 4, 1, do_imgextract,
 	"extract a part of a multi-image", imgextract_help_text
 );
+
+#if defined(CONFIG_FIT) && defined(CONFIG_CMD_FSFITXIMG)
+
+/* TODO: maybe make this configurable */
+#define CHUNK_SIZE		(1024*1024)
+#define MAX_LOAD_SIZE	(16*1024*1024)
+
+struct fs_file {
+	const char *dev;
+	const char *part;
+	const char *name;
+};
+
+static int fs_load_chunk(struct fs_file *file, ulong addr, loff_t offset, loff_t len)
+{
+	int ret = 0;
+	loff_t len_read;
+
+	debug("Reading %lld bytes from offset 0x%llx to 0x%lx\n", len, offset, addr);
+
+	if (fs_set_blk_dev(file->dev, file->part, FS_TYPE_ANY))
+		return -1;
+
+	ret = fs_read(file->name, addr, offset, len, &len_read);
+
+	if (ret < 0) {
+		printf("Read failed: %d", ret);
+		return ret;
+	}
+
+	return len_read;
+}
+
+static int fdt_err_retry(int err)
+{
+	return
+		(err == -FDT_ERR_TRUNCATED) ||
+		(err == -FDT_ERR_BADSTRUCTURE);
+}
+
+static int
+do_fsfitextract(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
+{
+	ulong		dest;
+	int		ret;
+	int		verify;
+	int		part = 0;
+	struct fs_file file;
+	const char	*uname;
+	void	*fit_hdr;
+	const void	*fit_data;
+	size_t	fit_len;
+	int		noffset = 0,
+			src_off, fit_off = 0;
+	uint8_t		comp;
+	unsigned long time;
+
+	if (argc != 6)
+		return CMD_RET_USAGE;
+
+	verify = getenv_yesno("verify");
+
+	file.dev = argv[1];
+	file.part = argv[2];
+	file.name = argv[3];
+	uname = argv[4];
+	dest = simple_strtoul(argv[5], NULL, 16);
+	
+	time = get_timer(0);
+
+	/* Load FIT from the filesystem incrementally to avoid reading data we
+	 * won't need. If requested subimage contained in the beginning of file,
+	 * this can make a huge speedup.
+	 *
+	 * First, read just the header
+	 */
+	printf("## Loading FIT header to 0x%08lx ...\n", dest);
+	ret = fs_load_chunk(&file, dest, 0, sizeof(struct fdt_header));
+	if (ret < 0)
+		return -1;
+
+	fit_hdr = map_sysmem(dest, sizeof(struct fdt_header));
+	ret = fdt_check_header(fit_hdr);
+	if (ret) {
+		printf("Bad FIT header: %d\n", ret);
+		return 1;
+	}
+	
+	fit_off += sizeof(struct fdt_header);
+
+	/* Read "strings" block to resolve all the node names, put it after the
+	 * header and adjust offset appropriately
+	 */
+	printf("## Loading FIT strings to 0x%08lx ...\n", dest + fit_off);
+	ret = fs_load_chunk(&file, dest + fit_off,
+			fdt_off_dt_strings(fit_hdr),
+			fdt_size_dt_strings(fit_hdr));
+	if (ret < 0)
+		return 1;
+
+	fdt_set_off_dt_strings(fit_hdr, fit_off);
+	fit_off += fdt_size_dt_strings(fit_hdr);
+
+	/* Align to 4 bytes */
+	fit_off &= ~0x3;
+	fit_off += 4;
+
+	/* Save offset of "struct" block in file and set the real in-memory offset */
+	src_off = fdt_off_dt_struct(fit_hdr);
+	fdt_set_off_dt_struct(fit_hdr, fit_off);
+
+	/* Now, start to load struct in CHUNK_SIZE chunks, and parse it at the same
+	 * time. When libfdt notices that the data is truncated or broken, read next
+	 * chunk from the filesystem.
+	 */
+	printf("## Loading FIT struct to 0x%08lx ...\n", dest + fit_off);
+	do {
+		ret = fs_load_chunk(&file, dest + fit_off,
+				src_off, CHUNK_SIZE);
+		if (ret < 0)
+			return 1;
+		fit_off += ret;
+		src_off += ret;
+
+		ret = fit_image_get_node(fit_hdr, uname);
+		if (ret > 0) {
+			noffset = ret;
+			break;
+		}
+
+		debug("fit_image_get_node returned %d\n", ret);
+		if (!fdt_err_retry(ret)) {
+			printf("Error while processing FIT: %d\n", ret);
+			return 1;
+		}
+	} while (src_off < MAX_LOAD_SIZE);
+
+	if (src_off > MAX_LOAD_SIZE) {
+		printf("Requested subimage not found in first 0x%x bytes of FIT\n",
+				MAX_LOAD_SIZE);
+		return 1;
+	}
+
+	debug("found image node at %08x\n", noffset);
+
+	do {
+		/* get subimage fit_data address and length */
+		ret = fit_image_get_data(fit_hdr, noffset,
+				&fit_data, &fit_len);
+		if (!ret)
+			break;
+
+		if (!fdt_err_retry(fit_len)) {
+			puts("Could not find subimage fit_data\n");
+			return 1;
+		}
+
+		ret = fs_load_chunk(&file, dest + fit_off,
+				src_off, CHUNK_SIZE);
+		if (ret < 0)
+			return 1;
+		fit_off += ret;
+		src_off += ret;
+	} while (src_off < MAX_LOAD_SIZE);
+
+	debug("fit_data at %p, fit_len 0x%lx\n", fit_data, fit_len);
+
+	/* Now we know the length of data, so read it out */
+	while (fit_data + fit_len > fit_hdr + fit_off) {
+		ret = fs_load_chunk(&file, dest + fit_off,
+				src_off, CHUNK_SIZE);
+		if (ret < 0)
+			return 1;
+		fit_off += ret;
+		src_off += ret;
+	}
+
+	time = get_timer(time);
+
+	printf("0x%x bytes read in %lu ms", fit_off, time);
+	if (time > 0) {
+		puts(" (");
+		print_size(lldiv(fit_off, time) * 1000, "/s");
+		puts(")");
+	}
+	puts("\n");
+
+	fit_image_print(fit_hdr, noffset, "   ");
+
+	if (fit_image_get_comp(fit_hdr, noffset, &comp)) {
+		puts("Could not find FIT subimage compression type\n");
+		return 1;
+	}
+
+	/* verify integrity */
+	if (verify) {
+		if (!fit_image_verify(fit_hdr, noffset)) {
+			puts("Bad fit_data Hash\n");
+			return 1;
+		}
+	}
+
+	if (!decompress_data(dest, (ulong)fit_data, (ulong)fit_len, comp, part))
+		return 1;
+
+	flush_cache(dest, fit_len);
+
+	setenv_hex("filesize", fit_len);
+
+	return 0;
+}
+
+U_BOOT_CMD(
+	fsfitxtract, 7, 1, do_fsfitextract,
+	"extract a part of a FIT stored on a filesystem",
+	"<interface> [<dev[:part]> <filename> <uname> <addr>\n"
+	"    - Extract subimage <uname> from FIT file <filename> from\n"
+	"       partition <part> on device type <interface> instance <dev>\n"
+	"       to address <addr> in memory"
+);
+#endif