2 * Copyright (c) 2015 The Linux Foundation
3 * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <linux/module.h>
19 #include <linux/mtd/mtd.h>
20 #include <linux/mtd/partitions.h>
21 #include <linux/types.h>
22 #include <linux/byteorder/generic.h>
23 #include <linux/slab.h>
24 #include <linux/libfdt.h>
25 #include <linux/of_fdt.h>
29 // string macros from git://git.denx.de/u-boot.git/include/image.h
31 #define FIT_IMAGES_PATH "/images"
32 #define FIT_DATA_PROP "data"
33 #define FIT_DATA_POSITION_PROP "data-position"
34 #define FIT_DATA_OFFSET_PROP "data-offset"
35 #define FIT_DATA_SIZE_PROP "data-size"
37 // functions from git://git.denx.de/u-boot.git/common/image-fit.c
40 * fit_image_get_data - get data property and its size for a given component image node
41 * @fit: pointer to the FIT format image header
42 * @noffset: component image node offset
43 * @data: double pointer to void, will hold data property's data address
44 * @size: pointer to size_t, will hold data property's data size
46 * fit_image_get_data() finds data property in a given component image node.
47 * If the property is found its data start address and size are returned to
54 static int fit_image_get_data(const void *fit
, int noffset
,
55 const void **data
, size_t *size
)
59 *data
= fdt_getprop(fit
, noffset
, FIT_DATA_PROP
, &len
);
71 * Get 'data-offset' property from a given image node.
73 * @fit: pointer to the FIT image header
74 * @noffset: component image node offset
75 * @data_offset: holds the data-offset property
79 * -ENOENT if the property could not be found
81 static int fit_image_get_data_offset(const void *fit
, int noffset
, int *data_offset
)
85 val
= fdt_getprop(fit
, noffset
, FIT_DATA_OFFSET_PROP
, NULL
);
89 *data_offset
= fdt32_to_cpu(*val
);
95 * Get 'data-position' property from a given image node.
97 * @fit: pointer to the FIT image header
98 * @noffset: component image node offset
99 * @data_position: holds the data-position property
103 * -ENOENT if the property could not be found
105 static int fit_image_get_data_position(const void *fit
, int noffset
,
110 val
= fdt_getprop(fit
, noffset
, FIT_DATA_POSITION_PROP
, NULL
);
114 *data_position
= fdt32_to_cpu(*val
);
120 * Get 'data-size' property from a given image node.
122 * @fit: pointer to the FIT image header
123 * @noffset: component image node offset
124 * @data_size: holds the data-size property
128 * -ENOENT if the property could not be found
130 static int fit_image_get_data_size(const void *fit
, int noffset
, int *data_size
)
134 val
= fdt_getprop(fit
, noffset
, FIT_DATA_SIZE_PROP
, NULL
);
138 *data_size
= fdt32_to_cpu(*val
);
144 * fit_image_get_data_and_size - get data and its size including
145 * both embedded and external data
146 * @fit: pointer to the FIT format image header
147 * @noffset: component image node offset
148 * @data: double pointer to void, will hold data property's data address
149 * @size: pointer to size_t, will hold data property's data size
151 * fit_image_get_data_and_size() finds data and its size including
152 * both embedded and external data. If the property is found
153 * its data start address and size are returned to the caller.
157 * otherwise, on failure
159 static int fit_image_get_data_and_size(const void *fit
, int noffset
,
160 const void **data
, size_t *size
)
162 bool external_data
= false;
167 if (!fit_image_get_data_position(fit
, noffset
, &offset
)) {
168 external_data
= true;
169 } else if (!fit_image_get_data_offset(fit
, noffset
, &offset
)) {
170 external_data
= true;
172 * For FIT with external data, figure out where
173 * the external images start. This is the base
174 * for the data-offset properties in each image.
176 offset
+= ((fdt_totalsize(fit
) + 3) & ~3);
180 ret
= fit_image_get_data_size(fit
, noffset
, &len
);
182 *data
= fit
+ offset
;
186 ret
= fit_image_get_data(fit
, noffset
, data
, size
);
193 mtdsplit_fit_parse(struct mtd_info
*mtd
,
194 const struct mtd_partition
**pparts
,
195 struct mtd_part_parser_data
*data
)
197 struct device_node
*np
= mtd_get_of_node(mtd
);
198 const char *cmdline_match
= NULL
;
199 struct fdt_header hdr
;
200 size_t hdr_len
, retlen
;
202 u32 offset_start
= 0;
203 size_t fit_offset
, fit_size
;
204 size_t rootfs_offset
, rootfs_size
;
205 size_t data_size
, img_total
, max_size
= 0;
206 struct mtd_partition
*parts
;
207 int ret
, ndepth
, noffset
, images_noffset
;
208 const void *img_data
;
211 of_property_read_string(np
, "openwrt,cmdline-match", &cmdline_match
);
212 if (cmdline_match
&& !strstr(saved_command_line
, cmdline_match
))
215 of_property_read_u32(np
, "openwrt,fit-offset", &offset_start
);
217 hdr_len
= sizeof(struct fdt_header
);
219 /* Parse the MTD device & search for the FIT image location */
220 for(offset
= 0; offset
+ hdr_len
<= mtd
->size
; offset
+= mtd
->erasesize
) {
221 ret
= mtd_read(mtd
, offset
+ offset_start
, hdr_len
, &retlen
, (void*) &hdr
);
223 pr_err("read error in \"%s\" at offset 0x%llx\n",
224 mtd
->name
, (unsigned long long) offset
);
228 if (retlen
!= hdr_len
) {
229 pr_err("short read in \"%s\"\n", mtd
->name
);
233 /* Check the magic - see if this is a FIT image */
234 if (be32_to_cpu(hdr
.magic
) != OF_DT_HEADER
) {
235 pr_debug("no valid FIT image found in \"%s\" at offset %llx\n",
236 mtd
->name
, (unsigned long long) offset
);
240 /* We found a FIT image. Let's keep going */
245 fit_size
= be32_to_cpu(hdr
.totalsize
);
248 pr_err("FIT image in \"%s\" at offset %llx has null size\n",
249 mtd
->name
, (unsigned long long) fit_offset
);
254 * Classic uImage.FIT has all data embedded into the FDT
255 * data structure. Hence the total size of the image equals
256 * the total size of the FDT structure.
257 * Modern uImage.FIT may have only references to data in FDT,
258 * hence we need to parse FDT structure to find the end of the
259 * last external data refernced.
261 if (fit_size
> 0x1000) {
262 enum mtdsplit_part_type type
;
264 /* Search for the rootfs partition after the FIT image */
265 ret
= mtd_find_rootfs_from(mtd
, fit_offset
+ fit_size
+ offset_start
, mtd
->size
,
266 &rootfs_offset
, &type
);
268 pr_info("no rootfs found after FIT image in \"%s\"\n",
273 rootfs_size
= mtd
->size
- rootfs_offset
;
275 parts
= kzalloc(2 * sizeof(*parts
), GFP_KERNEL
);
279 parts
[0].name
= KERNEL_PART_NAME
;
280 parts
[0].offset
= fit_offset
;
281 parts
[0].size
= mtd_rounddown_to_eb(fit_size
+ offset_start
, mtd
) + mtd
->erasesize
;
283 if (type
== MTDSPLIT_PART_TYPE_UBI
)
284 parts
[1].name
= UBI_PART_NAME
;
286 parts
[1].name
= ROOTFS_PART_NAME
;
287 parts
[1].offset
= rootfs_offset
;
288 parts
[1].size
= rootfs_size
;
294 /* Search for rootfs_data after FIT external data */
295 fit
= kzalloc(fit_size
, GFP_KERNEL
);
296 ret
= mtd_read(mtd
, offset
, fit_size
+ offset_start
, &retlen
, fit
);
298 pr_err("read error in \"%s\" at offset 0x%llx\n",
299 mtd
->name
, (unsigned long long) offset
);
303 images_noffset
= fdt_path_offset(fit
, FIT_IMAGES_PATH
);
304 if (images_noffset
< 0) {
305 pr_err("Can't find images parent node '%s' (%s)\n",
306 FIT_IMAGES_PATH
, fdt_strerror(images_noffset
));
311 noffset
= fdt_next_node(fit
, images_noffset
, &ndepth
);
312 (noffset
>= 0) && (ndepth
> 0);
313 noffset
= fdt_next_node(fit
, noffset
, &ndepth
)) {
315 ret
= fit_image_get_data_and_size(fit
, noffset
, &img_data
, &data_size
);
319 img_total
= data_size
+ (img_data
- fit
);
321 max_size
= (max_size
> img_total
) ? max_size
: img_total
;
325 parts
= kzalloc(sizeof(*parts
), GFP_KERNEL
);
329 parts
[0].name
= ROOTFS_SPLIT_NAME
;
330 parts
[0].offset
= fit_offset
+ mtd_rounddown_to_eb(max_size
, mtd
) + mtd
->erasesize
;
331 parts
[0].size
= mtd
->size
- parts
[0].offset
;
341 static const struct of_device_id mtdsplit_fit_of_match_table
[] = {
342 { .compatible
= "denx,fit" },
346 static struct mtd_part_parser uimage_parser
= {
347 .owner
= THIS_MODULE
,
349 .of_match_table
= mtdsplit_fit_of_match_table
,
350 .parse_fn
= mtdsplit_fit_parse
,
351 .type
= MTD_PARSER_TYPE_FIRMWARE
,
354 /**************************************************
356 **************************************************/
358 static int __init
mtdsplit_fit_init(void)
360 register_mtd_parser(&uimage_parser
);
365 module_init(mtdsplit_fit_init
);