summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2018-01-22 09:49:20 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2018-01-22 09:49:20 +0100
commit47f83fa0dad2f0d80b41806709c6c154adf3cf2a (patch)
tree15bed482b0a97f236e13623b3914fe78c63211dc
parent11b9f18c9229dddbeece71e61e5cbf85e82ecc49 (diff)
parent04413763ad3c3c0580c6a35bc44a92d08638e058 (diff)
downloadbarebox-47f83fa0dad2f0d80b41806709c6c154adf3cf2a.tar.gz
barebox-47f83fa0dad2f0d80b41806709c6c154adf3cf2a.tar.xz
Merge branch 'for-next/fastboot-sparse'
-rw-r--r--Documentation/user/usb.rst49
-rw-r--r--common/file-list.c3
-rw-r--r--common/filetype.c5
-rw-r--r--common/ubiformat.c61
-rw-r--r--drivers/usb/gadget/Kconfig1
-rw-r--r--drivers/usb/gadget/f_fastboot.c253
-rw-r--r--fs/fs.c22
-rw-r--r--include/file-list.h1
-rw-r--r--include/filetype.h1
-rw-r--r--include/image-sparse.h67
-rw-r--r--include/ubiformat.h3
-rw-r--r--include/unistd.h1
-rw-r--r--lib/Kconfig3
-rw-r--r--lib/Makefile1
-rw-r--r--lib/image-sparse.c249
15 files changed, 687 insertions, 33 deletions
diff --git a/Documentation/user/usb.rst b/Documentation/user/usb.rst
index ec219cfbe4..8396f3897c 100644
--- a/Documentation/user/usb.rst
+++ b/Documentation/user/usb.rst
@@ -35,6 +35,43 @@ command, they show up as ``/dev/diskx`` and can be used like any other device.
USB device support
------------------
+barebox supports several different USB gadget drivers:
+
+- Device Firmware Upgrade (DFU)
+- Android Fastboot
+- serial gadget
+
+The recommended way to use USB gadget is with the :ref:`command_usbgadget` command.
+While there are individual commands for :ref:`command_dfu` and :ref:`command_usbserial`,
+the :ref:`command_usbgadget` commands supports registering composite gadgets.
+
+Partition description
+^^^^^^^^^^^^^^^^^^^^^
+
+The USB gadget commands for Android Fastboot and DFU take a partition description
+which describes which barebox partitions are exported via USB. The partition
+description is referred to as ``<desc>`` in the command help texts. It has
+the general form ``partition(name)flags,partition(name)flags,...``.
+
+The **partition** field is the partition as accessible in barebox. This can be a
+path in ``/dev/``, but could also be a regular file.
+
+The **name** field is the name under which the partition shall be exported. This
+is the name under which the partition can be found with the host tool.
+
+Several **flags** are supported, each denoted by a single character:
+
+* ``s`` Safe mode. The file is downloaded completely before it is written (DFU specific)
+* ``r`` Readback. The partition is allowed to be read back (DFU specific)
+* ``c`` The file shall be created if it doesn't exist. Needed when a regular file is exported.
+* ``u`` The partition is a MTD device and shall be flashed with a UBI image.
+
+Example:
+
+.. code-block:: sh
+
+ /dev/nand0.barebox.bb(barebox)sr,/kernel(kernel)rc
+
DFU
^^^
@@ -43,16 +80,8 @@ Implementers Forum. It provides a vendor-independent way to update the firmware
devices. The current specification is version 1.1 and can be downloaded here:
http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf
-On the barebox side, the update is handled with the :ref:`command_dfu` command.
-It is passed a list of partitions to provide to the host. The partition list
-has the form ``<file>(<name>)<flags>``. ``file`` is the path to the device or
-regular file which shall be updated. ``name`` is the name under which the partition
-shall be provided to the host. For the possible ``flags`` see
-:ref:`command_dfu`. A typical ``dfu`` command could look like this:
-
-.. code-block:: sh
-
- dfu "/dev/nand0.barebox.bb(barebox)sr,/dev/nand0.kernel.bb(kernel)r,/dev/nand0.root.bb(root)r"
+On the barebox side, the update is handled with the :ref:`command_usbgadget` or the
+:ref:`command_dfu` command.
On the host side, the tool `dfu-util <http://dfu-util.gnumonks.org/>`_ can be used
to update the partitions. It is available for most distributions and typically
diff --git a/common/file-list.c b/common/file-list.c
index 8d61b76cbb..eb469cf9be 100644
--- a/common/file-list.c
+++ b/common/file-list.c
@@ -92,6 +92,9 @@ static int file_list_parse_one(struct file_list *files, const char *partstr, con
case 'c':
flags |= FILE_LIST_FLAG_CREATE;
break;
+ case 'u':
+ flags |= FILE_LIST_FLAG_UBI;
+ break;
default:
pr_err("Unknown flag '%c'\n", *partstr);
return -EINVAL;
diff --git a/common/filetype.c b/common/filetype.c
index 323da026af..f9c034ff2a 100644
--- a/common/filetype.c
+++ b/common/filetype.c
@@ -25,6 +25,7 @@
#include <errno.h>
#include <envfs.h>
#include <disks.h>
+#include <image-sparse.h>
struct filetype_str {
const char *name; /* human readable filetype */
@@ -64,6 +65,7 @@ static const struct filetype_str filetype_str[] = {
[filetype_mxs_bootstream] = { "Freescale MXS bootstream", "mxsbs" },
[filetype_socfpga_xload] = { "SoCFPGA prebootloader image", "socfpga-xload" },
[filetype_kwbimage_v1] = { "MVEBU kwbimage (v1)", "kwb" },
+ [filetype_android_sparse] = { "Android sparse image", "sparse" },
};
const char *file_type_to_string(enum filetype f)
@@ -301,6 +303,9 @@ enum filetype file_detect_type(const void *_buf, size_t bufsize)
(buf8[0x1e] == 0 || buf8[0x1e] == 1))
return filetype_kwbimage_v1;
+ if (is_sparse_image(_buf))
+ return filetype_android_sparse;
+
if (bufsize < 64)
return filetype_unknown;
diff --git a/common/ubiformat.c b/common/ubiformat.c
index aaa1f5d6bc..4c5f1f5794 100644
--- a/common/ubiformat.c
+++ b/common/ubiformat.c
@@ -679,3 +679,64 @@ out_close:
return err;
}
+int ubiformat_write(struct mtd_info *mtd, const void *buf, size_t count,
+ loff_t offset)
+{
+ int writesize = mtd->writesize >> mtd->subpage_sft;
+ size_t retlen;
+ int ret;
+
+ if (offset & (mtd->writesize - 1))
+ return -EINVAL;
+
+ if (count & (mtd->writesize - 1))
+ return -EINVAL;
+
+ while (count) {
+ size_t now;
+
+ now = ALIGN(offset, mtd->erasesize) - offset;
+ if (now > count)
+ now = count;
+
+ if (!now) {
+ const struct ubi_ec_hdr *ec = buf;
+ const struct ubi_vid_hdr *vid;
+
+ if (be32_to_cpu(ec->magic) != UBI_EC_HDR_MAGIC) {
+ pr_err("bad UBI magic %#08x, should be %#08x",
+ be32_to_cpu(ec->magic), UBI_EC_HDR_MAGIC);
+ return -EINVAL;
+ }
+
+ /* skip ec header */
+ offset += writesize;
+ buf += writesize;
+ count -= writesize;
+
+ if (!count)
+ break;
+
+ vid = buf;
+ if (be32_to_cpu(vid->magic) != UBI_VID_HDR_MAGIC) {
+ pr_err("bad UBI magic %#08x, should be %#08x",
+ be32_to_cpu(vid->magic), UBI_VID_HDR_MAGIC);
+ return -EINVAL;
+ }
+
+ continue;
+ }
+
+ ret = mtd_write(mtd, offset, now, &retlen, buf);
+ if (ret < 0)
+ return ret;
+ if (retlen != now)
+ return -EIO;
+
+ buf += now;
+ count -= now;
+ offset += now;
+ }
+
+ return 0;
+}
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 64347f0d18..b612d39a8e 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -57,6 +57,7 @@ config USB_GADGET_FASTBOOT
bool
select BANNER
select FILE_LIST
+ select IMAGE_SPARSE
prompt "Android Fastboot support"
endif
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index 85c64c05c8..2ba5977239 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -32,11 +32,14 @@
#include <ubiformat.h>
#include <stdlib.h>
#include <file-list.h>
+#include <magicvar.h>
+#include <linux/sizes.h>
#include <progress.h>
#include <environment.h>
#include <globalvar.h>
#include <restart.h>
#include <console_countdown.h>
+#include <image-sparse.h>
#include <usb/ch9.h>
#include <usb/gadget.h>
#include <usb/fastboot.h>
@@ -45,6 +48,7 @@
#include <linux/compiler.h>
#include <linux/stat.h>
#include <linux/mtd/mtd-abi.h>
+#include <linux/mtd/mtd.h>
#define FASTBOOT_VERSION "0.4"
@@ -56,6 +60,8 @@
#define EP_BUFFER_SIZE 4096
+static unsigned int fastboot_max_download_size = SZ_8M;
+
struct fb_variable {
char *name;
char *value;
@@ -316,6 +322,8 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
fb_setvar(var, "0.4");
var = fb_addvar(f_fb, "bootloader-version");
fb_setvar(var, release_string);
+ var = fb_addvar(f_fb, "max-download-size");
+ fb_setvar(var, "%u", fastboot_max_download_size);
if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && opts->export_bbu)
bbu_handlers_iterate(fastboot_add_bbu_variables, f_fb);
@@ -526,7 +534,7 @@ static int fastboot_tx_write(struct f_fastboot *f_fb, const char *buffer, unsign
return 0;
}
-static int fastboot_tx_print(struct f_fastboot *f_fb, const char *fmt, ...)
+int fastboot_tx_print(struct f_fastboot *f_fb, const char *fmt, ...)
{
char buf[64];
va_list ap;
@@ -687,6 +695,197 @@ static void __maybe_unused cb_boot(struct usb_ep *ep, struct usb_request *req,
fastboot_tx_print(f_fb, "OKAY");
}
+static struct mtd_info *get_mtd(struct f_fastboot *f_fb, const char *filename)
+{
+ int fd, ret;
+ struct mtd_info_user meminfo;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return ERR_PTR(-errno);
+
+ ret = ioctl(fd, MEMGETINFO, &meminfo);
+
+ close(fd);
+
+ if (ret)
+ return ERR_PTR(ret);
+
+ return meminfo.mtd;
+}
+
+static int do_ubiformat(struct f_fastboot *f_fb, struct mtd_info *mtd,
+ const char *file)
+{
+ struct ubiformat_args args = {
+ .yes = 1,
+ .image = file,
+ };
+
+ if (!file)
+ args.novtbl = 1;
+
+ if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
+ fastboot_tx_print(f_fb, "FAILubiformat is not available");
+ return -ENODEV;
+ }
+
+ return ubiformat(mtd, &args);
+}
+
+
+static int check_ubi(struct f_fastboot *f_fb, struct file_list_entry *fentry,
+ enum filetype filetype)
+{
+ struct mtd_info *mtd;
+
+ mtd = get_mtd(f_fb, fentry->filename);
+
+ /*
+ * Issue a warning when we are about to write a UBI image to a MTD device
+ * and the FILE_LIST_FLAG_UBI is not given as this means we loose all
+ * erase counters.
+ */
+ if (!IS_ERR(mtd) && filetype == filetype_ubi &&
+ !(fentry->flags & FILE_LIST_FLAG_UBI)) {
+ fastboot_tx_print(f_fb, "INFOwriting UBI image to MTD device, "
+ "add the 'u' ");
+ fastboot_tx_print(f_fb, "INFOflag to the partition description");
+ return 0;
+ }
+
+ if (!(fentry->flags & FILE_LIST_FLAG_UBI))
+ return 0;
+
+ if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
+ fastboot_tx_print(f_fb, "FAILformat not available");
+ return -ENOSYS;
+ }
+
+ if (IS_ERR(mtd)) {
+ fastboot_tx_print(f_fb, "FAILUBI flag given on non-MTD device");
+ return -EINVAL;
+ }
+
+ if (filetype == filetype_ubi) {
+ fastboot_tx_print(f_fb, "INFOThis is an UBI image...");
+ return 1;
+ } else {
+ fastboot_tx_print(f_fb, "FAILThis is no UBI image but %s",
+ file_type_to_string(filetype));
+ return -EINVAL;
+ }
+}
+
+static int fastboot_handle_sparse(struct f_fastboot *f_fb,
+ struct file_list_entry *fentry)
+{
+ struct sparse_image_ctx *sparse;
+ void *buf = NULL;
+ int ret, fd;
+ unsigned int flags = O_RDWR;
+ int bufsiz = SZ_128K;
+ struct stat s;
+ struct mtd_info *mtd = NULL;
+
+ ret = stat(fentry->filename, &s);
+ if (ret) {
+ if (fentry->flags & FILE_LIST_FLAG_CREATE)
+ flags |= O_CREAT;
+ else
+ return ret;
+ }
+
+ fd = open(fentry->filename, flags);
+ if (fd < 0)
+ return -errno;
+
+ ret = fstat(fd, &s);
+ if (ret)
+ goto out_close_fd;
+
+ sparse = sparse_image_open(FASTBOOT_TMPFILE);
+ if (IS_ERR(sparse)) {
+ pr_err("Cannot open sparse image\n");
+ ret = PTR_ERR(sparse);
+ goto out_close_fd;
+ }
+
+ if (S_ISREG(s.st_mode)) {
+ ret = ftruncate(fd, sparse_image_size(sparse));
+ if (ret)
+ goto out;
+ }
+
+ buf = malloc(bufsiz);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (fentry->flags & FILE_LIST_FLAG_UBI) {
+ mtd = get_mtd(f_fb, fentry->filename);
+ if (IS_ERR(mtd)) {
+ ret = PTR_ERR(mtd);
+ goto out;
+ }
+ }
+
+ while (1) {
+ int retlen;
+ loff_t pos;
+
+ ret = sparse_image_read(sparse, buf, &pos, bufsiz, &retlen);
+ if (ret)
+ goto out;
+ if (!retlen)
+ break;
+
+ if (pos == 0) {
+ ret = check_ubi(f_fb, fentry, file_detect_type(buf, retlen));
+ if (ret < 0)
+ goto out;
+ }
+
+ if (fentry->flags & FILE_LIST_FLAG_UBI) {
+ if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
+ ret = -ENOSYS;
+ goto out;
+ }
+
+ if (pos == 0) {
+ ret = do_ubiformat(f_fb, mtd, NULL);
+ if (ret)
+ goto out;
+ }
+
+ ret = ubiformat_write(mtd, buf, retlen, pos);
+ if (ret)
+ goto out;
+ } else {
+ pos = lseek(fd, pos, SEEK_SET);
+ if (pos == -1) {
+ ret = -errno;
+ goto out;
+ }
+
+ ret = write_full(fd, buf, retlen);
+ if (ret < 0)
+ goto out;
+ }
+ }
+
+ ret = 0;
+
+out:
+ free(buf);
+ sparse_image_close(sparse);
+out_close_fd:
+ close(fd);
+
+ return ret;
+}
+
static void cb_flash(struct usb_ep *ep, struct usb_request *req, const char *cmd)
{
struct f_fastboot *f_fb = req->context;
@@ -706,33 +905,27 @@ static void cb_flash(struct usb_ep *ep, struct usb_request *req, const char *cmd
filename = fentry->filename;
- if (filetype == filetype_ubi) {
- int fd;
- struct mtd_info_user meminfo;
- struct ubiformat_args args = {
- .yes = 1,
- .image = FASTBOOT_TMPFILE,
- };
-
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- goto copy;
+ if (filetype == filetype_android_sparse) {
+ ret = fastboot_handle_sparse(f_fb, fentry);
+ if (ret) {
+ fastboot_tx_print(f_fb, "FAILwriting sparse image: %s",
+ strerror(-ret));
+ return;
+ }
- ret = ioctl(fd, MEMGETINFO, &meminfo);
- close(fd);
- /* Not a MTD device, ubiformat is not a valid operation */
- if (ret)
- goto copy;
+ goto out;
+ }
- fastboot_tx_print(f_fb, "INFOThis is an UBI image...");
+ ret = check_ubi(f_fb, fentry, filetype);
+ if (ret < 0)
+ return;
- if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
- fastboot_tx_print(f_fb, "FAILubiformat is not available");
- return;
- }
+ if (ret > 0) {
+ struct mtd_info *mtd;
- ret = ubiformat(meminfo.mtd, &args);
+ mtd = get_mtd(f_fb, fentry->filename);
+ ret = do_ubiformat(f_fb, mtd, FASTBOOT_TMPFILE);
if (ret) {
fastboot_tx_print(f_fb, "FAILwrite partition: %s", strerror(-ret));
return;
@@ -973,3 +1166,17 @@ static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
memset(req->buf, 0, EP_BUFFER_SIZE);
usb_ep_queue(ep, req);
}
+
+static int fastboot_globalvars_init(void)
+{
+ globalvar_add_simple_int("usbgadget.fastboot_max_download_size",
+ &fastboot_max_download_size, "%u");
+
+ return 0;
+}
+
+device_initcall(fastboot_globalvars_init);
+
+BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_max_download_size,
+ global.usbgadget.fastboot_max_download_size,
+ "Fastboot maximum download size");
diff --git a/fs/fs.c b/fs/fs.c
index f61ee091b5..051af8d19f 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -742,6 +742,28 @@ int creat(const char *pathname, mode_t mode)
}
EXPORT_SYMBOL(creat);
+int ftruncate(int fd, loff_t length)
+{
+ struct fs_driver_d *fsdrv;
+ FILE *f;
+ int ret;
+
+ if (check_fd(fd))
+ return -errno;
+
+ f = &files[fd];
+
+ fsdrv = f->fsdev->driver;
+
+ ret = fsdrv->truncate(&f->fsdev->dev, f, length);
+ if (ret)
+ return ret;
+
+ f->size = length;
+
+ return 0;
+}
+
int ioctl(int fd, int request, void *buf)
{
struct fs_driver_d *fsdrv;
diff --git a/include/file-list.h b/include/file-list.h
index 1e02539d4d..404d8d64bb 100644
--- a/include/file-list.h
+++ b/include/file-list.h
@@ -4,6 +4,7 @@
#define FILE_LIST_FLAG_SAFE (1 << 0)
#define FILE_LIST_FLAG_READBACK (1 << 1)
#define FILE_LIST_FLAG_CREATE (1 << 2)
+#define FILE_LIST_FLAG_UBI (1 << 3)
struct file_list_entry {
char *name;
diff --git a/include/filetype.h b/include/filetype.h
index c84905d782..b98dcb5014 100644
--- a/include/filetype.h
+++ b/include/filetype.h
@@ -39,6 +39,7 @@ enum filetype {
filetype_mxs_bootstream,
filetype_socfpga_xload,
filetype_kwbimage_v1,
+ filetype_android_sparse,
filetype_max,
};
diff --git a/include/image-sparse.h b/include/image-sparse.h
new file mode 100644
index 0000000000..29242f4fd5
--- /dev/null
+++ b/include/image-sparse.h
@@ -0,0 +1,67 @@
+/*
+ * This is from the Android Project,
+ * Repository: https://android.googlesource.com/platform/system/core
+ * File: libsparse/sparse_format.h
+ * Commit: 28fa5bc347390480fe190294c6c385b6a9f0d68b
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef _IMAGE_SPARSE_H
+#define _IMAGE_SPARSE_H
+
+struct sparse_header {
+ __le32 magic; /* 0xed26ff3a */
+ __le16 major_version; /* (0x1) - reject images with higher major versions */
+ __le16 minor_version; /* (0x0) - allow images with higer minor versions */
+ __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */
+ __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */
+ __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */
+ __le32 total_blks; /* total blocks in the non-sparse output image */
+ __le32 total_chunks; /* total chunks in the sparse input image */
+ __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
+ /* as 0. Standard 802.3 polynomial, use a Public Domain */
+ /* table implementation */
+};
+
+#define SPARSE_HEADER_MAGIC 0xed26ff3a
+
+#define CHUNK_TYPE_RAW 0xCAC1
+#define CHUNK_TYPE_FILL 0xCAC2
+#define CHUNK_TYPE_DONT_CARE 0xCAC3
+#define CHUNK_TYPE_CRC32 0xCAC4
+
+struct chunk_header {
+ __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
+ __le16 reserved1;
+ __le32 chunk_sz; /* in blocks in output image */
+ __le32 total_sz; /* in bytes of chunk input file including chunk header and data */
+};
+
+/* Following a Raw or Fill or CRC32 chunk is data.
+ * For a Raw chunk, it's the data in chunk_sz * blk_sz.
+ * For a Fill chunk, it's 4 bytes of the fill data.
+ * For a CRC32 chunk, it's 4 bytes of CRC32
+ */
+
+static inline int is_sparse_image(const void *buf)
+{
+ const struct sparse_header *s = buf;
+
+ if ((le32_to_cpu(s->magic) == SPARSE_HEADER_MAGIC) &&
+ (le16_to_cpu(s->major_version) == 1))
+ return 1;
+
+ return 0;
+}
+
+struct sparse_image_ctx;
+
+struct sparse_image_ctx *sparse_image_open(const char *path);
+int sparse_image_read(struct sparse_image_ctx *si, void *buf,
+ loff_t *pos, size_t len, int *retlen);
+void sparse_image_close(struct sparse_image_ctx *si);
+loff_t sparse_image_size(struct sparse_image_ctx *si);
+
+#endif /* _IMAGE_SPARSE_H */
diff --git a/include/ubiformat.h b/include/ubiformat.h
index b195fd8392..8305a853c7 100644
--- a/include/ubiformat.h
+++ b/include/ubiformat.h
@@ -20,4 +20,7 @@ struct ubiformat_args {
int ubiformat(struct mtd_info *mtd, struct ubiformat_args *args);
+int ubiformat_write(struct mtd_info *mtd, const void *buf, size_t count,
+ loff_t offset);
+
#endif /* __UBIFORMAT_H */
diff --git a/include/unistd.h b/include/unistd.h
index 31f430a5b6..f392e6dd6c 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -20,5 +20,6 @@ int symlink(const char *pathname, const char *newpath);
int readlink(const char *path, char *buf, size_t bufsiz);
int chdir(const char *pathname);
const char *getcwd(void);
+int ftruncate(int fd, loff_t length);
#endif /* __UNISTD_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 9562b1b8c2..637b3f1003 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -79,6 +79,9 @@ config LIBSCAN
config LIBUBIGEN
bool
+config IMAGE_SPARSE
+ bool
+
config STMP_DEVICE
bool
diff --git a/lib/Makefile b/lib/Makefile
index 1be1742499..0d5ac6586c 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_GENERIC_FIND_NEXT_BIT) += find_next_bit.o
obj-y += glob.o
obj-y += notifier.o
obj-y += random.o
+obj-$(CONFIG_IMAGE_SPARSE) += image-sparse.o
obj-y += lzo/
obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/
obj-y += show_progress.o
diff --git a/lib/image-sparse.c b/lib/image-sparse.c
new file mode 100644
index 0000000000..7137d15fd0
--- /dev/null
+++ b/lib/image-sparse.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
+ * Portions Copyright 2014 Broadcom Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * NOTE:
+ * Although it is very similar, this license text is not identical
+ * to the "BSD-3-Clause", therefore, DO NOT MODIFY THIS LICENSE TEXT!
+ */
+#define pr_fmt(fmt) "image-sparse: " fmt
+
+#include <config.h>
+#include <common.h>
+#include <image-sparse.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <fs.h>
+#include <libfile.h>
+#include <linux/sizes.h>
+
+#include <linux/math64.h>
+
+#ifndef CONFIG_FASTBOOT_FLASH_FILLBUF_SIZE
+#define CONFIG_FASTBOOT_FLASH_FILLBUF_SIZE (1024 * 512)
+#endif
+
+struct sparse_image_ctx {
+ int fd;
+ struct sparse_header sparse;
+ int processed_chunks;
+ struct chunk_header chunk;
+ loff_t pos;
+ size_t remaining;
+ uint32_t fill_val;
+};
+
+int sparse_seek(struct sparse_image_ctx *si)
+{
+ unsigned int chunk_data_sz, payload;
+ loff_t offs;
+ int ret;
+
+again:
+ if (si->processed_chunks == si->sparse.total_chunks)
+ return 0;
+
+ /* Read and skip over chunk header */
+ ret = read_full(si->fd, &si->chunk,
+ sizeof(struct chunk_header));
+ if (ret < 0)
+ return ret;
+ if (ret < sizeof(struct chunk_header))
+ return -EINVAL;
+
+ pr_debug("=== Chunk Header ===\n");
+ pr_debug("chunk_type: 0x%x\n", si->chunk.chunk_type);
+ pr_debug("chunk_data_sz: 0x%x\n", si->chunk.chunk_sz);
+ pr_debug("total_size: 0x%x\n", si->chunk.total_sz);
+
+ if (si->sparse.chunk_hdr_sz > sizeof(struct chunk_header)) {
+ /*
+ * Skip the remaining bytes in a header that is longer
+ * than we expected.
+ */
+ offs = lseek(si->fd, si->sparse.chunk_hdr_sz -
+ sizeof(struct chunk_header), SEEK_CUR);
+ if (offs == -1)
+ return -errno;
+ }
+
+ chunk_data_sz = si->sparse.blk_sz * si->chunk.chunk_sz;
+ payload = si->chunk.total_sz - si->sparse.chunk_hdr_sz;
+
+ si->processed_chunks++;
+
+ switch (si->chunk.chunk_type) {
+ case CHUNK_TYPE_RAW:
+ if (payload != chunk_data_sz)
+ return -EINVAL;
+
+ si->remaining = payload;
+
+ break;
+
+ case CHUNK_TYPE_FILL:
+ if (payload != sizeof(uint32_t))
+ return -EINVAL;
+
+ ret = read_full(si->fd, &si->fill_val, sizeof(uint32_t));
+ if (ret < 0)
+ return ret;
+ if (ret < sizeof(uint32_t))
+ return -EINVAL;
+
+ si->remaining = chunk_data_sz;
+
+ break;
+
+ case CHUNK_TYPE_DONT_CARE:
+ si->pos += chunk_data_sz;
+ goto again;
+
+ case CHUNK_TYPE_CRC32:
+ if (payload != sizeof(uint32_t))
+ return -EINVAL;
+
+ offs = lseek(si->fd, chunk_data_sz, SEEK_CUR);
+ if (offs == -1)
+ return -EINVAL;
+ goto again;
+
+ default:
+ pr_err("Unknown chunk type 0x%04x",
+ si->chunk.chunk_type);
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+loff_t sparse_image_size(struct sparse_image_ctx *si)
+{
+ return (loff_t)si->sparse.blk_sz * si->sparse.total_blks;
+}
+
+struct sparse_image_ctx *sparse_image_open(const char *path)
+{
+ struct sparse_image_ctx *si;
+ loff_t offs;
+ int ret;
+
+ si = xzalloc(sizeof(*si));
+
+ si->fd = open(path, O_RDONLY);
+ if (si->fd < 0) {
+ ret = -errno;
+ goto out;
+ }
+
+ /* Read and skip over sparse image header */
+ read(si->fd, &si->sparse, sizeof(struct sparse_header));
+
+ if (si->sparse.file_hdr_sz > sizeof(struct sparse_header)) {
+ /*
+ * Skip the remaining bytes in a header that is longer than
+ * we expected.
+ */
+ offs = lseek(si->fd, si->sparse.file_hdr_sz, SEEK_SET);
+ if (offs == -1) {
+ ret = -errno;
+ goto out;
+ }
+ }
+
+ ret = sparse_seek(si);
+ if (ret < 0)
+ goto out;
+
+ return si;
+out:
+ free(si);
+
+ return ERR_PTR(ret);
+}
+
+int sparse_image_read(struct sparse_image_ctx *si, void *buf, loff_t *pos,
+ size_t len, int *retlen)
+{
+ size_t now;
+ int ret, i;
+
+ if (si->remaining == 0) {
+ ret = sparse_seek(si);
+ if (ret < 0)
+ return ret;
+ if (ret == 0) {
+ *retlen = 0;
+ return 0;
+ }
+ }
+
+ *pos = si->pos;
+
+ now = min(si->remaining, len);
+
+ switch (si->chunk.chunk_type) {
+ case CHUNK_TYPE_RAW:
+ ret = read_full(si->fd, buf, now);
+ if (ret < 0)
+ return ret;
+ if (ret < now)
+ return -EINVAL;
+
+ break;
+
+ case CHUNK_TYPE_FILL:
+ if (now & 3)
+ return -EINVAL;
+
+ for (i = 0; i < now / sizeof(uint32_t); i++) {
+ uint32_t *buf32 = buf;
+
+ buf32[i] = si->fill_val;
+ }
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ si->pos += now;
+ si->remaining -= now;
+
+ *retlen = now;
+
+ return 0;
+}
+
+void sparse_image_close(struct sparse_image_ctx *si)
+{
+ close(si->fd);
+ free(si);
+}