|
|
@@ -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[] = {
|