diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/Kconfig | 19 | ||||
-rw-r--r-- | fs/Makefile | 2 | ||||
-rw-r--r-- | fs/bpkfs.c | 514 | ||||
-rw-r--r-- | fs/uimagefs.c | 554 |
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); |