diff options
-rw-r--r-- | target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c | 282 |
1 files changed, 243 insertions, 39 deletions
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c index 5cc1658dbd..3230d859b0 100644 --- a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c @@ -21,28 +21,173 @@ #include <linux/types.h> #include <linux/byteorder/generic.h> #include <linux/slab.h> +#include <linux/libfdt.h> #include <linux/of_fdt.h> #include "mtdsplit.h" -struct fdt_header { - uint32_t magic; /* magic word FDT_MAGIC */ - uint32_t totalsize; /* total size of DT block */ - uint32_t off_dt_struct; /* offset to structure */ - uint32_t off_dt_strings; /* offset to strings */ - uint32_t off_mem_rsvmap; /* offset to memory reserve map */ - uint32_t version; /* format version */ - uint32_t last_comp_version; /* last compatible version */ - - /* version 2 fields below */ - uint32_t boot_cpuid_phys; /* Which physical CPU id we're - booting on */ - /* version 3 fields below */ - uint32_t size_dt_strings; /* size of the strings block */ - - /* version 17 fields below */ - uint32_t size_dt_struct; /* size of the structure block */ -}; +// string macros from git://git.denx.de/u-boot.git/include/image.h + +#define FIT_IMAGES_PATH "/images" +#define FIT_DATA_PROP "data" +#define FIT_DATA_POSITION_PROP "data-position" +#define FIT_DATA_OFFSET_PROP "data-offset" +#define FIT_DATA_SIZE_PROP "data-size" + +// functions from git://git.denx.de/u-boot.git/common/image-fit.c + +/** + * fit_image_get_data - get data property and its size for a given component image node + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @data: double pointer to void, will hold data property's data address + * @size: pointer to size_t, will hold data property's data size + * + * fit_image_get_data() finds data property in a given component image node. + * If the property is found its data start address and size are returned to + * the caller. + * + * returns: + * 0, on success + * -1, on failure + */ +static int fit_image_get_data(const void *fit, int noffset, + const void **data, size_t *size) +{ + int len; + + *data = fdt_getprop(fit, noffset, FIT_DATA_PROP, &len); + if (*data == NULL) { + *size = 0; + return -1; + } + + *size = len; + return 0; +} + + +/** + * Get 'data-offset' property from a given image node. + * + * @fit: pointer to the FIT image header + * @noffset: component image node offset + * @data_offset: holds the data-offset property + * + * returns: + * 0, on success + * -ENOENT if the property could not be found + */ +static int fit_image_get_data_offset(const void *fit, int noffset, int *data_offset) +{ + const fdt32_t *val; + + val = fdt_getprop(fit, noffset, FIT_DATA_OFFSET_PROP, NULL); + if (!val) + return -ENOENT; + + *data_offset = fdt32_to_cpu(*val); + + return 0; +} + +/** + * Get 'data-position' property from a given image node. + * + * @fit: pointer to the FIT image header + * @noffset: component image node offset + * @data_position: holds the data-position property + * + * returns: + * 0, on success + * -ENOENT if the property could not be found + */ +static int fit_image_get_data_position(const void *fit, int noffset, + int *data_position) +{ + const fdt32_t *val; + + val = fdt_getprop(fit, noffset, FIT_DATA_POSITION_PROP, NULL); + if (!val) + return -ENOENT; + + *data_position = fdt32_to_cpu(*val); + + return 0; +} + +/** + * Get 'data-size' property from a given image node. + * + * @fit: pointer to the FIT image header + * @noffset: component image node offset + * @data_size: holds the data-size property + * + * returns: + * 0, on success + * -ENOENT if the property could not be found + */ +static int fit_image_get_data_size(const void *fit, int noffset, int *data_size) +{ + const fdt32_t *val; + + val = fdt_getprop(fit, noffset, FIT_DATA_SIZE_PROP, NULL); + if (!val) + return -ENOENT; + + *data_size = fdt32_to_cpu(*val); + + return 0; +} + +/** + * fit_image_get_data_and_size - get data and its size including + * both embedded and external data + * @fit: pointer to the FIT format image header + * @noffset: component image node offset + * @data: double pointer to void, will hold data property's data address + * @size: pointer to size_t, will hold data property's data size + * + * fit_image_get_data_and_size() finds data and its size including + * both embedded and external data. If the property is found + * its data start address and size are returned to the caller. + * + * returns: + * 0, on success + * otherwise, on failure + */ +static int fit_image_get_data_and_size(const void *fit, int noffset, + const void **data, size_t *size) +{ + bool external_data = false; + int offset; + int len; + int ret; + + if (!fit_image_get_data_position(fit, noffset, &offset)) { + external_data = true; + } else if (!fit_image_get_data_offset(fit, noffset, &offset)) { + external_data = true; + /* + * For FIT with external data, figure out where + * the external images start. This is the base + * for the data-offset properties in each image. + */ + offset += ((fdt_totalsize(fit) + 3) & ~3); + } + + if (external_data) { + ret = fit_image_get_data_size(fit, noffset, &len); + if (!ret) { + *data = fit + offset; + *size = len; + } + } else { + ret = fit_image_get_data(fit, noffset, data, size); + } + + return ret; +} static int mtdsplit_fit_parse(struct mtd_info *mtd, @@ -56,8 +201,11 @@ mtdsplit_fit_parse(struct mtd_info *mtd, size_t offset; size_t fit_offset, fit_size; size_t rootfs_offset, rootfs_size; + size_t data_size, img_total, max_size = 0; struct mtd_partition *parts; - int ret; + int ret, ndepth, noffset, images_noffset; + const void *img_data; + void *fit; of_property_read_string(np, "openwrt,cmdline-match", &cmdline_match); if (cmdline_match && !strstr(saved_command_line, cmdline_match)) @@ -99,31 +247,87 @@ mtdsplit_fit_parse(struct mtd_info *mtd, return -ENODEV; } - /* Search for the rootfs partition after the FIT image */ - ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size, mtd->size, - &rootfs_offset, NULL); - if (ret) { - pr_info("no rootfs found after FIT image in \"%s\"\n", - mtd->name); - return ret; - } + /* + * Classic uImage.FIT has all data embedded into the FDT + * data structure. Hence the total size of the image equals + * the total size of the FDT structure. + * Modern uImage.FIT may have only references to data in FDT, + * hence we need to parse FDT structure to find the end of the + * last external data refernced. + */ + if (fit_size > 0x1000) { + /* Search for the rootfs partition after the FIT image */ + ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size, mtd->size, + &rootfs_offset, NULL); + if (ret) { + pr_info("no rootfs found after FIT image in \"%s\"\n", + mtd->name); + return ret; + } + + rootfs_size = mtd->size - rootfs_offset; + + parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL); + if (!parts) + return -ENOMEM; + + parts[0].name = KERNEL_PART_NAME; + parts[0].offset = fit_offset; + parts[0].size = mtd_rounddown_to_eb(fit_size, mtd) + mtd->erasesize; + + parts[1].name = ROOTFS_PART_NAME; + parts[1].offset = rootfs_offset; + parts[1].size = rootfs_size; + + *pparts = parts; + + return 2; + } else { + /* Search for rootfs_data after FIT external data */ + fit = kzalloc(fit_size, GFP_KERNEL); + ret = mtd_read(mtd, offset, fit_size, &retlen, fit); + if (ret) { + pr_err("read error in \"%s\" at offset 0x%llx\n", + mtd->name, (unsigned long long) offset); + return ret; + } + + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_noffset < 0) { + pr_err("Can't find images parent node '%s' (%s)\n", + FIT_IMAGES_PATH, fdt_strerror(images_noffset)); + return -ENODEV; + } - rootfs_size = mtd->size - rootfs_offset; + for (ndepth = 0, + noffset = fdt_next_node(fit, images_noffset, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(fit, noffset, &ndepth)) { + if (ndepth == 1) { + ret = fit_image_get_data_and_size(fit, noffset, &img_data, &data_size); + if (ret) + return 0; - parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL); - if (!parts) - return -ENOMEM; + img_total = data_size + (img_data - fit); - parts[0].name = KERNEL_PART_NAME; - parts[0].offset = fit_offset; - parts[0].size = mtd_rounddown_to_eb(fit_size, mtd) + mtd->erasesize; + max_size = (max_size > img_total) ? max_size : img_total; + } + } + + parts = kzalloc(sizeof(*parts), GFP_KERNEL); + if (!parts) + return -ENOMEM; + + parts[0].name = ROOTFS_SPLIT_NAME; + parts[0].offset = fit_offset + mtd_rounddown_to_eb(max_size, mtd) + mtd->erasesize; + parts[0].size = mtd->size - parts[0].offset; - parts[1].name = ROOTFS_PART_NAME; - parts[1].offset = rootfs_offset; - parts[1].size = rootfs_size; + *pparts = parts; - *pparts = parts; - return 2; + kfree(fit); + + return 1; + } } static const struct of_device_id mtdsplit_fit_of_match_table[] = { |