summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/Kconfig19
-rw-r--r--fs/Makefile2
-rw-r--r--fs/bpkfs.c514
-rw-r--r--fs/uimagefs.c554
4 files changed, 1089 insertions, 0 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index be4797f0e6..50a15c6bee 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -44,6 +44,25 @@ config FS_NFS
source fs/fat/Kconfig
source fs/ubifs/Kconfig
+config FS_BPKFS
+ bool
+ prompt "BPKFS support"
+ help
+ Simple update file format developed for Somfy, tools and library are
+ available under LGPLv2 (https://www.gitorious.org/libbpk).
+ This format in the v1.0 allow you to store 6 types a binary stream for
+ a unique hardware id:
+ - bootloader
+ - bootloader_version
+ - description.gz
+ - kernel
+ - rootfs
+ - firmware_version
+
+config FS_UIMAGEFS
+ bool
+ prompt "uImage FS support"
+
config PARTITION_NEED_MTD
bool
diff --git a/fs/Makefile b/fs/Makefile
index bd02d94267..e8347bd670 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -9,3 +9,5 @@ obj-$(CONFIG_FS_UBIFS) += ubifs/
obj-$(CONFIG_FS_TFTP) += tftp.o
obj-$(CONFIG_FS_OMAP4_USBBOOT) += omap4_usbbootfs.o
obj-$(CONFIG_FS_NFS) += nfs.o
+obj-$(CONFIG_FS_BPKFS) += bpkfs.o
+obj-$(CONFIG_FS_UIMAGEFS) += uimagefs.o
diff --git a/fs/bpkfs.c b/fs/bpkfs.c
new file mode 100644
index 0000000000..b3b45be76d
--- /dev/null
+++ b/fs/bpkfs.c
@@ -0,0 +1,514 @@
+/*
+ * Copyright (c) 2013 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ *
+ * Simple update file format developed for Somfy, tools and library are
+ * available under LGPLv2 (https://www.gitorious.org/libbpk).
+ *
+ * under GPLv2 ONLY
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <fs.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fs.h>
+#include <malloc.h>
+#include <init.h>
+#include <linux/stat.h>
+#include <linux/err.h>
+#include <bpkfs.h>
+#include <libgen.h>
+
+static bool bpkfs_is_crc_file(struct bpkfs_handle_data *d)
+{
+ return d->type & (1 << 31);
+}
+
+const char* bpkfs_type_to_str(uint32_t type)
+{
+ switch (type) {
+ case BPKFS_TYPE_BL:
+ return "bootloader";
+ case BPKFS_TYPE_BLV:
+ return "bootloader_version";
+ case BPKFS_TYPE_DSC:
+ return "description.gz";
+ case BPKFS_TYPE_KER:
+ return "kernel";
+ case BPKFS_TYPE_RFS:
+ return "rootfs";
+ case BPKFS_TYPE_FMV:
+ return "firmware_version";
+ }
+
+ return NULL;
+}
+
+static struct bpkfs_handle_hw *bpkfs_get_by_hw_id(
+ struct bpkfs_handle *handle, uint32_t hw_id)
+{
+ struct bpkfs_handle_hw *h;
+
+ list_for_each_entry(h, &handle->list, list_hw_id) {
+ if (h->hw_id == hw_id)
+ return h;
+ }
+
+ return NULL;
+}
+
+static struct bpkfs_handle_hw *bpkfs_hw_id_get_by_name(
+ struct bpkfs_handle *handle, const char *name)
+{
+ struct bpkfs_handle_hw *h;
+
+ if (!name)
+ return NULL;
+
+ list_for_each_entry(h, &handle->list, list_hw_id) {
+ if (strcmp(h->name, name) == 0)
+ return h;
+ }
+
+ return NULL;
+}
+
+static struct bpkfs_handle_data *bpkfs_data_get_by_name(
+ struct bpkfs_handle_hw *h, const char *name)
+{
+ struct bpkfs_handle_data *d;
+
+ if (!name)
+ return NULL;
+
+ list_for_each_entry(d, &h->list_data, list) {
+ if (strcmp(d->name, name) == 0)
+ return d;
+ }
+
+ return NULL;
+}
+
+static struct bpkfs_handle_hw *bpkfs_get_or_add_hw_id(
+ struct bpkfs_handle *handle, uint32_t hw_id)
+{
+ struct bpkfs_handle_hw *h;
+
+ h = bpkfs_get_by_hw_id(handle, hw_id);
+ if (h)
+ return h;
+
+ h = xzalloc(sizeof(*h));
+
+ INIT_LIST_HEAD(&h->list_data);
+ h->hw_id = hw_id;
+ h->name = asprintf("hw_id_%x", hw_id);
+ list_add_tail(&h->list_hw_id, &handle->list);
+
+ return h;
+}
+
+static struct bpkfs_handle_data *bpkfs_get_by_type(
+ struct bpkfs_handle *handle, uint32_t hw_id, uint32_t type)
+{
+ struct bpkfs_handle_data *d;
+ struct bpkfs_handle_hw *h;
+
+ h = bpkfs_get_by_hw_id(handle, hw_id);
+ if (!h)
+ return NULL;
+
+ list_for_each_entry(d, &h->list_data, list) {
+ if (d->type == type)
+ return d;
+ }
+
+ return NULL;
+}
+
+static int bpkfs_open(struct device_d *dev, FILE *f, const char *filename)
+{
+ struct bpkfs_handle *priv = dev->priv;
+ struct bpkfs_handle_data *d;
+ struct bpkfs_handle_hw *h;
+ char *dir, *file;
+ int ret = -EINVAL;
+ char *tmp = xstrdup(filename);
+ char *tmp2 = xstrdup(filename);
+
+ dir = dirname(tmp);
+
+ if (dir[0] == '/')
+ dir++;
+
+ h = bpkfs_hw_id_get_by_name(priv, dir);
+ if (!h)
+ goto out;
+
+ file = basename(tmp2);
+ d = bpkfs_data_get_by_name(h, file);
+ if (!d)
+ goto out;
+
+ if (!bpkfs_is_crc_file(d)) {
+ d->fd = open(priv->filename, O_RDONLY);
+ if (d->fd < 0) {
+ ret = d->fd;
+ goto out;
+ }
+
+ lseek(d->fd, d->offset, SEEK_SET);
+ }
+
+ f->size = d->size;
+ f->inode = d;
+ ret = 0;
+
+out:
+ free(tmp);
+ free(tmp2);
+ return ret;
+}
+
+static int bpkfs_close(struct device_d *dev, FILE *file)
+{
+ struct bpkfs_handle_data *d = file->inode;
+
+ close(d->fd);
+
+ return 0;
+}
+
+static int bpkfs_read(struct device_d *dev, FILE *file, void *buf, size_t insize)
+{
+ struct bpkfs_handle_data *d = file->inode;
+
+ if (bpkfs_is_crc_file(d)) {
+ memcpy(buf, &d->data[d->pos], insize);
+ return insize;
+ } else {
+ return read(d->fd, buf, insize);
+ }
+}
+
+static loff_t bpkfs_lseek(struct device_d *dev, FILE *file, loff_t pos)
+{
+ struct bpkfs_handle_data *d = file->inode;
+
+ if (!bpkfs_is_crc_file(d))
+ lseek(d->fd, d->offset + pos, SEEK_SET);
+
+ d->pos = pos;
+
+ return pos;
+}
+
+struct somfy_readdir {
+ struct bpkfs_handle_hw *h;
+ struct bpkfs_handle_data *d;
+
+ DIR dir;
+};
+
+static DIR *bpkfs_opendir(struct device_d *dev, const char *pathname)
+{
+ struct bpkfs_handle *priv = dev->priv;
+ struct somfy_readdir *sdir;
+ DIR *dir;
+
+ sdir = xzalloc(sizeof(*sdir));
+ dir = &sdir->dir;
+ dir->priv = sdir;
+
+ if (pathname[0] == '/')
+ pathname++;
+
+ if (!strlen(pathname)) {
+ if (list_empty(&priv->list))
+ return dir;
+
+ sdir->h = list_first_entry(&priv->list,
+ struct bpkfs_handle_hw, list_hw_id);
+ } else {
+ sdir->h = bpkfs_hw_id_get_by_name(priv, pathname);
+ if (!sdir->h || list_empty(&sdir->h->list_data))
+ return dir;
+
+ sdir->d = list_first_entry(&sdir->h->list_data,
+ struct bpkfs_handle_data, list);
+ }
+
+ return dir;
+}
+
+static struct dirent *bpkfs_readdir(struct device_d *dev, DIR *dir)
+{
+ struct bpkfs_handle *priv = dev->priv;
+ struct somfy_readdir *sdir = dir->priv;
+ struct bpkfs_handle_hw *h = sdir->h;
+ struct bpkfs_handle_data *d = sdir->d;
+
+ if (!h)
+ return NULL;
+
+ if (!d) {
+ if (&h->list_hw_id == &priv->list)
+ return NULL;
+
+ strcpy(dir->d.d_name, h->name);
+ sdir->h = list_entry(h->list_hw_id.next, struct bpkfs_handle_hw, list_hw_id);
+ } else {
+ if (&d->list == &h->list_data)
+ return NULL;
+
+ strcpy(dir->d.d_name, d->name);
+ sdir->d = list_entry(d->list.next, struct bpkfs_handle_data, list);
+ }
+
+ return &dir->d;
+}
+
+static int bpkfs_closedir(struct device_d *dev, DIR *dir)
+{
+ struct somfy_readdir *sdir = dir->priv;
+
+ free(sdir);
+ return 0;
+}
+
+static int bpkfs_stat(struct device_d *dev, const char *filename, struct stat *s)
+{
+ struct bpkfs_handle *priv = dev->priv;
+ struct bpkfs_handle_data *d;
+ struct bpkfs_handle_hw *h;
+ char *dir, *file;
+ int ret = -EINVAL;
+ char *tmp = xstrdup(filename);
+ char *tmp2 = xstrdup(filename);
+
+ dir = dirname(tmp);
+
+ if (filename[0] == '/')
+ filename++;
+
+ if (dir[0] == '/')
+ dir++;
+
+ if (!strlen(dir)) {
+ h = bpkfs_hw_id_get_by_name(priv, filename);
+ if (!h)
+ goto out;
+
+ s->st_size = strlen(filename);
+ s->st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
+ ret = 0;
+ goto out;
+ }
+ h = bpkfs_hw_id_get_by_name(priv, dir);
+ if (!h)
+ goto out;
+
+ file = basename(tmp2);
+ d = bpkfs_data_get_by_name(h, file);
+ if (!d)
+ goto out;
+
+ s->st_size = d->size;
+ s->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
+
+ ret = 0;
+
+out:
+ free(tmp);
+ free(tmp2);
+ return ret;
+}
+
+static void bpkfs_remove_data(struct bpkfs_handle_hw *h)
+{
+ struct bpkfs_handle_data *d, *tmp;
+
+ list_for_each_entry_safe(d, tmp, &h->list_data, list) {
+ free(d->name);
+ free(d);
+ }
+}
+
+static void bpkfs_remove(struct device_d *dev)
+{
+ struct bpkfs_handle *priv = dev->priv;
+ struct bpkfs_handle_hw *h, *tmp;
+
+ list_for_each_entry_safe(h, tmp, &priv->list, list_hw_id) {
+ bpkfs_remove_data(h);
+ free(h->name);
+ free(h);
+ }
+
+ free(priv);
+}
+
+static int bpkfs_probe(struct device_d *dev)
+{
+ struct fs_device_d *fsdev = dev_to_fs_device(dev);
+ struct bpkfs_handle *priv;
+ struct bpkfs_header *header;
+ struct bpkfs_data_header data_header;
+ int ret = 0;
+ uint32_t checksum, crc;
+ uint64_t size;
+ int i;
+ size_t offset = 0;
+ char *buf;
+ int fd;
+
+ priv = xzalloc(sizeof(struct bpkfs_handle));
+ INIT_LIST_HEAD(&priv->list);
+ buf = xmalloc(2048);
+ dev->priv = priv;
+
+ priv->filename = fsdev->backingstore;
+ dev_dbg(dev, "mount: %s\n", fsdev->backingstore);
+
+ fd = open(fsdev->backingstore, O_RDONLY);
+ if (fd < 0) {
+ ret = fd;
+ goto err;
+ }
+
+ header = &priv->header;
+
+ ret = read(fd, header, sizeof(*header));
+ if (ret < 0) {
+ dev_err(dev, "could not read: %s (ret = %d)\n", errno_str(), ret);
+ goto err;
+ }
+
+ dev_dbg(dev, "header.magic = 0x%x\n", be32_to_cpu(header->magic));
+ dev_dbg(dev, "header.version = 0x%x\n", be32_to_cpu(header->version));
+ dev_dbg(dev, "header.crc = 0x%x\n", be32_to_cpu(header->crc));
+ dev_dbg(dev, "header.size = %llu\n", be64_to_cpu(header->size));
+ dev_dbg(dev, "header.spare = %llu\n", be64_to_cpu(header->spare));
+
+ size = be64_to_cpu(header->size);
+ offset += sizeof(*header);
+ size -= sizeof(*header);
+
+ checksum = be32_to_cpu(header->crc);
+ header->crc = 0;
+
+ crc = crc32(0, header, sizeof(*header));
+
+ for (i = 0; size; i++) {
+ struct bpkfs_handle_data *d;
+ struct bpkfs_handle_hw *h;
+ const char *type;
+
+ ret = read(fd, &data_header, sizeof(data_header));
+ if (ret < 0) {
+ dev_err(dev, "could not read: %s\n", errno_str());
+ goto err;
+ } else if (ret == 0) {
+ dev_err(dev, "EOF: to_read %llu\n", size);
+ goto err;
+ }
+
+ d = xzalloc(sizeof(*d));
+
+ crc = crc32(crc, &data_header, sizeof(data_header));
+ offset += sizeof(data_header);
+ size -= sizeof(data_header);
+
+ d->type = be32_to_cpu(data_header.type);
+ d->hw_id = be32_to_cpu(data_header.hw_id);
+ d->size = be64_to_cpu(data_header.size);
+ d->offset = offset;
+ d->crc = be32_to_cpu(data_header.crc);
+ type = bpkfs_type_to_str(d->type);
+
+ h = bpkfs_get_or_add_hw_id(priv, d->hw_id);
+
+ if (!type) {
+ type = "unknown";
+ d->name = asprintf("%s_%08x", type, d->type);
+ } else {
+ d->name = xstrdup(type);
+ }
+
+ dev_dbg(dev, "%d: type = 0x%x => %s\n", i, d->type, d->name);
+ dev_dbg(dev, "%d: size = %llu\n", i, d->size);
+ dev_dbg(dev, "%d: offset = %d\n", i, d->offset);
+
+ dev_dbg(dev, "%d: hw_id = 0x%x => %s\n", i, h->hw_id, h->name);
+
+ offset += d->size;
+ size -= d->size;
+
+ if (bpkfs_get_by_type(priv, d->hw_id, d->type)) {
+ dev_info(dev, "ignore data %d type %s already present, ignored\n",
+ i, type);
+ free(d);
+ continue;
+ }
+
+ list_add_tail(&d->list, &h->list_data);
+ priv->nb_data_entries++;
+
+ ret = lseek(fd, d->size, SEEK_CUR);
+ if (ret < 0) {
+ dev_err(dev, "could not seek: %s\n", errno_str());
+ goto err;
+ }
+
+ type = d->name;
+ d = xzalloc(sizeof(*d));
+ d->type = be32_to_cpu(data_header.type);
+ d->name = asprintf("%s.crc", type);
+ d->type |= (1 << 31);
+ d->size = 8;
+ sprintf(d->data, "%08x", be32_to_cpu(data_header.crc));
+ list_add_tail(&d->list, &h->list_data);
+ }
+
+ if (crc != checksum) {
+ dev_err(dev, "invalid crc (0x%x != 0x%x)\n", checksum, crc);
+ goto err;
+ }
+
+ close(fd);
+ free(buf);
+
+ return 0;
+
+err:
+ close(fd);
+ free(buf);
+ bpkfs_remove(dev);
+
+ return ret;
+}
+
+static struct fs_driver_d bpkfs_driver = {
+ .open = bpkfs_open,
+ .close = bpkfs_close,
+ .read = bpkfs_read,
+ .lseek = bpkfs_lseek,
+ .opendir = bpkfs_opendir,
+ .readdir = bpkfs_readdir,
+ .closedir = bpkfs_closedir,
+ .stat = bpkfs_stat,
+ .flags = 0,
+ .type = filetype_bpk,
+ .drv = {
+ .probe = bpkfs_probe,
+ .remove = bpkfs_remove,
+ .name = "bpkfs",
+ }
+};
+
+static int bpkfs_init(void)
+{
+ return register_fs_driver(&bpkfs_driver);
+}
+coredevice_initcall(bpkfs_init);
diff --git a/fs/uimagefs.c b/fs/uimagefs.c
new file mode 100644
index 0000000000..6547b7cb77
--- /dev/null
+++ b/fs/uimagefs.c
@@ -0,0 +1,554 @@
+/*
+ * Copyright (c) 2013 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ *
+ * under GPLv2 ONLY
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <fs.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fs.h>
+#include <malloc.h>
+#include <init.h>
+#include <linux/stat.h>
+#include <linux/err.h>
+#include <uimagefs.h>
+#include <libbb.h>
+#include <rtc.h>
+
+static bool uimagefs_is_data_file(struct uimagefs_handle_data *d)
+{
+ return d->type == UIMAGEFS_DATA;
+}
+
+const char* uimagefs_type_to_str(enum uimagefs_type type)
+{
+ switch (type) {
+ case UIMAGEFS_DATA:
+ return "data";
+ case UIMAGEFS_DATA_CRC:
+ return "data.crc";
+ case UIMAGEFS_NAME:
+ return "name";
+ case UIMAGEFS_TIME:
+ return "time";
+ case UIMAGEFS_LOAD:
+ return "load_addr";
+ case UIMAGEFS_EP:
+ return "entry_point";
+ case UIMAGEFS_OS:
+ return "os";
+ case UIMAGEFS_ARCH:
+ return "arch";
+ case UIMAGEFS_TYPE:
+ return "type";
+ case UIMAGEFS_COMP:
+ return "compression";
+ }
+
+ return "unknown";
+}
+
+static struct uimagefs_handle_data *uimagefs_get_by_name(
+ struct uimagefs_handle *handle, const char *name)
+{
+ struct uimagefs_handle_data *d;
+
+ if (!name)
+ return NULL;
+
+ list_for_each_entry(d, &handle->list, list) {
+ if (strcmp(d->name, name) == 0)
+ return d;
+ }
+
+ return NULL;
+}
+
+static int uimagefs_open(struct device_d *dev, FILE *file, const char *filename)
+{
+ struct uimagefs_handle *priv = dev->priv;
+ struct uimagefs_handle_data *d;
+
+ if (filename[0] == '/')
+ filename++;
+
+ d = uimagefs_get_by_name(priv, filename);
+ if (!d)
+ return -EINVAL;
+
+ if (uimagefs_is_data_file(d)) {
+ d->fd = open(priv->filename, O_RDONLY);
+ if (d->fd < 0)
+ return d->fd;
+
+ lseek(d->fd, d->offset, SEEK_SET);
+ }
+
+ file->size = d->size;
+ file->inode = d;
+
+ return 0;
+}
+
+static int uimagefs_close(struct device_d *dev, FILE *file)
+{
+ struct uimagefs_handle_data *d = file->inode;
+
+ close(d->fd);
+
+ return 0;
+}
+
+static int uimagefs_read(struct device_d *dev, FILE *file, void *buf, size_t insize)
+{
+ struct uimagefs_handle_data *d = file->inode;
+
+ if (!uimagefs_is_data_file(d)) {
+ memcpy(buf, &d->data[d->pos], insize);
+ return insize;
+ } else {
+ return read(d->fd, buf, insize);
+ }
+}
+
+static loff_t uimagefs_lseek(struct device_d *dev, FILE *file, loff_t pos)
+{
+ struct uimagefs_handle_data *d = file->inode;
+
+ if (uimagefs_is_data_file(d))
+ lseek(d->fd, d->offset + pos, SEEK_SET);
+
+ d->pos = pos;
+
+ return pos;
+}
+
+static DIR *uimagefs_opendir(struct device_d *dev, const char *pathname)
+{
+ struct uimagefs_handle *priv = dev->priv;
+ DIR *dir;
+
+ dir = xzalloc(sizeof(DIR));
+
+ if (list_empty(&priv->list))
+ return dir;
+
+ dir->priv = list_first_entry(&priv->list,
+ struct uimagefs_handle_data, list);
+ return dir;
+}
+
+static struct dirent *uimagefs_readdir(struct device_d *dev, DIR *dir)
+{
+ struct uimagefs_handle *priv = dev->priv;
+ struct uimagefs_handle_data *d = dir->priv;
+
+ if (!d || &d->list == &priv->list)
+ return NULL;
+
+ strcpy(dir->d.d_name, d->name);
+ dir->priv = list_entry(d->list.next, struct uimagefs_handle_data, list);
+ return &dir->d;
+}
+
+static int uimagefs_closedir(struct device_d *dev, DIR *dir)
+{
+ free(dir);
+ return 0;
+}
+
+static int uimagefs_stat(struct device_d *dev, const char *filename, struct stat *s)
+{
+ struct uimagefs_handle *priv = dev->priv;
+ struct uimagefs_handle_data *d;
+
+ if (filename[0] == '/')
+ filename++;
+
+ d = uimagefs_get_by_name(priv, filename);
+ if (!d)
+ return -EINVAL;
+
+ s->st_size = d->size;
+ s->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
+
+ return 0;
+}
+
+static int uimagefs_ioctl(struct device_d *dev, FILE *f, int request, void *buf)
+{
+ struct uimagefs_handle *priv = dev->priv;
+
+ if (request != UIMAGEFS_METADATA)
+ return -EINVAL;
+
+ memcpy(buf, &priv->header, sizeof(struct image_header));
+
+ return 0;
+}
+
+static void uimagefs_remove(struct device_d *dev)
+{
+ struct uimagefs_handle *priv = dev->priv;
+ struct uimagefs_handle_data *d, *tmp;
+ struct stat s;
+
+ list_for_each_entry_safe(d, tmp, &priv->list, list) {
+ free(d->name);
+ free(d->data);
+ free(d);
+ }
+
+ if (IS_BUILTIN(CONFIG_FS_TFTP) && !stat(priv->tmp, &s))
+ unlink(priv->tmp);
+
+ free(priv->tmp);
+ free(priv);
+}
+
+static inline int uimage_is_multi_image(struct uimagefs_handle *priv)
+{
+ return (priv->header.ih_type == IH_TYPE_MULTI) ? 1 : 0;
+}
+
+static int uimagefs_add_str(struct uimagefs_handle *priv, enum uimagefs_type type,
+ char *s)
+{
+ struct uimagefs_handle_data *d;
+
+ d = xzalloc(sizeof(*d));
+ d->type = type;
+ d->name = xstrdup(uimagefs_type_to_str(type));
+ d->data = s;
+ d->size = strlen(s);
+
+ list_add_tail(&d->list, &priv->list);
+
+ return 0;
+}
+
+static int uimagefs_add_name(struct uimagefs_handle *priv)
+{
+ char *name;
+ struct image_header *header = &priv->header;
+
+ if (header->ih_name[0]) {
+ name = xzalloc(IH_NMLEN + 1);
+ strncpy(name, header->ih_name, IH_NMLEN);
+ } else {
+ name = xstrdup(priv->filename);
+ }
+
+ return uimagefs_add_str(priv, UIMAGEFS_NAME, name);
+}
+
+static int uimagefs_add_hex(struct uimagefs_handle *priv, enum uimagefs_type type,
+ uint32_t data)
+{
+ char *val = asprintf("0x%x", data);
+
+ return uimagefs_add_str(priv, type, val);
+}
+
+static int __uimagefs_add_data(struct uimagefs_handle *priv, size_t offset,
+ uint64_t size, int i)
+{
+ struct uimagefs_handle_data *d;
+ const char *name = uimagefs_type_to_str(UIMAGEFS_DATA);
+
+ d = xzalloc(sizeof(*d));
+ d->type = UIMAGEFS_DATA;
+ if (i < 0)
+ d->name = xstrdup(name);
+ else
+ d->name = asprintf("%s%d", name, i);
+
+ d->offset = offset;
+ d->size = size;
+
+ list_add_tail(&d->list, &priv->list);
+
+ return 0;
+}
+
+static int uimagefs_add_data(struct uimagefs_handle *priv, size_t offset,
+ uint64_t size)
+{
+ return __uimagefs_add_data(priv, offset, size, -1);
+}
+
+static int uimagefs_add_data_entry(struct uimagefs_handle *priv, size_t offset,
+ uint64_t size)
+{
+ int ret;
+
+ ret = __uimagefs_add_data(priv, offset, size, priv->nb_data_entries);
+ if (ret)
+ return ret;
+
+ priv->nb_data_entries++;
+
+ return 0;
+}
+
+#if defined(CONFIG_TIMESTAMP)
+static int uimagefs_add_time(struct uimagefs_handle *priv)
+{
+ struct image_header *header = &priv->header;
+ struct rtc_time tm;
+ char *val;
+
+ to_tm(header->ih_time, &tm);
+ val = asprintf("%4d-%02d-%02d %2d:%02d:%02d UTC",
+ tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+ return uimagefs_add_str(priv, UIMAGEFS_TIME, val);
+}
+#else
+static int uimagefs_add_time(struct uimagefs_handle *priv)
+{
+ struct image_header *header = &priv->header;
+
+ return uimagefs_add_hex(priv, UIMAGEFS_TIME, header->ih_time);
+}
+#endif
+
+static int uimagefs_add_os(struct uimagefs_handle *priv)
+{
+ struct image_header *header = &priv->header;
+ char *val = xstrdup(image_get_os_name(header->ih_os));
+
+ return uimagefs_add_str(priv, UIMAGEFS_OS, val);
+}
+
+static int uimagefs_add_arch(struct uimagefs_handle *priv)
+{
+ struct image_header *header = &priv->header;
+ char *val = xstrdup(image_get_arch_name(header->ih_arch));
+
+ return uimagefs_add_str(priv, UIMAGEFS_ARCH, val);
+}
+
+static int uimagefs_add_type(struct uimagefs_handle *priv)
+{
+ struct image_header *header = &priv->header;
+ char *val = xstrdup(image_get_type_name(header->ih_type));
+
+ return uimagefs_add_str(priv, UIMAGEFS_TYPE, val);
+}
+
+static int uimagefs_add_comp(struct uimagefs_handle *priv)
+{
+ struct image_header *header = &priv->header;
+ char *val = xstrdup(image_get_comp_name(header->ih_comp));
+
+ return uimagefs_add_str(priv, UIMAGEFS_COMP, val);
+}
+
+/*
+ * open a uimage. This will check the header contents and
+ * return a handle to the uImage
+ */
+static int __uimage_open(struct uimagefs_handle *priv)
+{
+ int fd;
+ uint32_t checksum;
+ struct image_header *header;
+ int ret;
+ size_t offset = 0;
+ size_t data_offset = 0;
+
+again:
+ fd = open(priv->filename, O_RDONLY);
+ if (fd < 0) {
+ printf("could not open: %s\n", errno_str());
+ return fd;
+ }
+
+ /*
+ * Hack around tftp fs. We need lseek for uImage support, but
+ * this cannot be implemented in tftp fs, so we detect this
+ * by doing a test lseek and copy the file to ram if it fails
+ */
+ if (IS_BUILTIN(CONFIG_FS_TFTP) && lseek(fd, 0, SEEK_SET)) {
+ close(fd);
+ ret = copy_file(priv->filename, priv->tmp, 0);
+ if (ret)
+ return ret;
+ priv->filename = priv->tmp;
+ goto again;
+ }
+
+ header = &priv->header;
+
+ ret = read(fd, header, sizeof(*header));
+ if (ret < 0) {
+ printf("could not read: %s\n", errno_str());
+ goto err_out;
+ }
+ offset += sizeof(*header);
+
+ if (uimage_to_cpu(header->ih_magic) != IH_MAGIC) {
+ printf("Bad Magic Number\n");
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ checksum = uimage_to_cpu(header->ih_hcrc);
+ header->ih_hcrc = 0;
+
+ if (crc32(0, header, sizeof(*header)) != checksum) {
+ printf("Bad Header Checksum\n");
+ ret = -EIO;
+ goto err_out;
+ }
+
+ /* convert header to cpu native endianess */
+ header->ih_magic = uimage_to_cpu(header->ih_magic);
+ header->ih_hcrc = checksum;
+ header->ih_time = uimage_to_cpu(header->ih_time);
+ header->ih_size = uimage_to_cpu(header->ih_size);
+ header->ih_load = uimage_to_cpu(header->ih_load);
+ header->ih_ep = uimage_to_cpu(header->ih_ep);
+ header->ih_dcrc = uimage_to_cpu(header->ih_dcrc);
+
+ ret = uimagefs_add_arch(priv);
+ if (ret)
+ goto err_out;
+
+ ret = uimagefs_add_comp(priv);
+ if (ret)
+ goto err_out;
+
+ ret = uimagefs_add_name(priv);
+ if (ret)
+ goto err_out;
+
+ ret = uimagefs_add_os(priv);
+ if (ret)
+ goto err_out;
+
+ ret = uimagefs_add_time(priv);
+ if (ret)
+ goto err_out;
+
+ ret = uimagefs_add_type(priv);
+ if (ret)
+ goto err_out;
+
+ ret = uimagefs_add_hex(priv, UIMAGEFS_LOAD, header->ih_load);
+ if (ret)
+ goto err_out;
+
+ ret = uimagefs_add_hex(priv, UIMAGEFS_EP, header->ih_ep);
+ if (ret)
+ goto err_out;
+
+ data_offset = offset;
+
+ if (uimage_is_multi_image(priv)) {
+ struct uimagefs_handle_data *d;
+
+ do {
+ u32 size;
+
+ ret = read(fd, &size, sizeof(size));
+ if (ret < 0)
+ goto err_out;
+
+ offset += sizeof(size);
+
+ if (!size)
+ break;
+
+ ret = uimagefs_add_data_entry(priv, 0, uimage_to_cpu(size));
+ if (ret)
+ goto err_out;
+ } while(1);
+
+ /* offset of the first image in a multifile image */
+ list_for_each_entry(d, &priv->list, list) {
+ if (!uimagefs_is_data_file(d))
+ continue;
+ d->offset = offset;
+ offset += (d->size + 3) & ~3;
+ }
+ } else {
+ ret = uimagefs_add_data_entry(priv, offset, header->ih_size);
+ if (ret)
+ goto err_out;
+ }
+
+ ret = uimagefs_add_data(priv, data_offset, header->ih_size);
+ if (ret)
+ goto err_out;
+
+ ret = uimagefs_add_hex(priv, UIMAGEFS_DATA_CRC, header->ih_dcrc);
+ if (ret)
+ goto err_out;
+
+ ret = 0;
+err_out:
+
+ close(fd);
+
+ return ret;
+}
+
+static int uimagefs_probe(struct device_d *dev)
+{
+ struct fs_device_d *fsdev = dev_to_fs_device(dev);
+ struct uimagefs_handle *priv;
+ int ret = 0;
+
+ priv = xzalloc(sizeof(struct uimagefs_handle));
+ INIT_LIST_HEAD(&priv->list);
+ dev->priv = priv;
+
+ priv->filename = fsdev->backingstore;
+ dev_dbg(dev, "mount: %s\n", fsdev->backingstore);
+
+ if (IS_BUILTIN(CONFIG_FS_TFTP))
+ priv->tmp = asprintf("/.uImage_tmp_%08x",
+ crc32(0, fsdev->path, strlen(fsdev->path)));
+
+ ret = __uimage_open(priv);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ uimagefs_remove(dev);
+
+ return ret;
+}
+
+static struct fs_driver_d uimagefs_driver = {
+ .open = uimagefs_open,
+ .close = uimagefs_close,
+ .read = uimagefs_read,
+ .lseek = uimagefs_lseek,
+ .opendir = uimagefs_opendir,
+ .readdir = uimagefs_readdir,
+ .closedir = uimagefs_closedir,
+ .stat = uimagefs_stat,
+ .ioctl = uimagefs_ioctl,
+ .flags = 0,
+ .type = filetype_uimage,
+ .drv = {
+ .probe = uimagefs_probe,
+ .remove = uimagefs_remove,
+ .name = "uimagefs",
+ }
+};
+
+static int uimagefs_init(void)
+{
+ return register_fs_driver(&uimagefs_driver);
+}
+coredevice_initcall(uimagefs_init);