ebtables: fix compilation with GCC14
[openwrt/openwrt.git] / target / linux / generic / files / drivers / mtd / mtdsplit / mtdsplit_fit.c
1 /*
2 * Copyright (c) 2015 The Linux Foundation
3 * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
4 *
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.
8 *
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.
16 */
17
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>
26
27 #include "mtdsplit.h"
28
29 // string macros from git://git.denx.de/u-boot.git/include/image.h
30
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"
36
37 // functions from git://git.denx.de/u-boot.git/common/image-fit.c
38
39 /**
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
45 *
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
48 * the caller.
49 *
50 * returns:
51 * 0, on success
52 * -1, on failure
53 */
54 static int fit_image_get_data(const void *fit, int noffset,
55 const void **data, size_t *size)
56 {
57 int len;
58
59 *data = fdt_getprop(fit, noffset, FIT_DATA_PROP, &len);
60 if (*data == NULL) {
61 *size = 0;
62 return -1;
63 }
64
65 *size = len;
66 return 0;
67 }
68
69
70 /**
71 * Get 'data-offset' property from a given image node.
72 *
73 * @fit: pointer to the FIT image header
74 * @noffset: component image node offset
75 * @data_offset: holds the data-offset property
76 *
77 * returns:
78 * 0, on success
79 * -ENOENT if the property could not be found
80 */
81 static int fit_image_get_data_offset(const void *fit, int noffset, int *data_offset)
82 {
83 const fdt32_t *val;
84
85 val = fdt_getprop(fit, noffset, FIT_DATA_OFFSET_PROP, NULL);
86 if (!val)
87 return -ENOENT;
88
89 *data_offset = fdt32_to_cpu(*val);
90
91 return 0;
92 }
93
94 /**
95 * Get 'data-position' property from a given image node.
96 *
97 * @fit: pointer to the FIT image header
98 * @noffset: component image node offset
99 * @data_position: holds the data-position property
100 *
101 * returns:
102 * 0, on success
103 * -ENOENT if the property could not be found
104 */
105 static int fit_image_get_data_position(const void *fit, int noffset,
106 int *data_position)
107 {
108 const fdt32_t *val;
109
110 val = fdt_getprop(fit, noffset, FIT_DATA_POSITION_PROP, NULL);
111 if (!val)
112 return -ENOENT;
113
114 *data_position = fdt32_to_cpu(*val);
115
116 return 0;
117 }
118
119 /**
120 * Get 'data-size' property from a given image node.
121 *
122 * @fit: pointer to the FIT image header
123 * @noffset: component image node offset
124 * @data_size: holds the data-size property
125 *
126 * returns:
127 * 0, on success
128 * -ENOENT if the property could not be found
129 */
130 static int fit_image_get_data_size(const void *fit, int noffset, int *data_size)
131 {
132 const fdt32_t *val;
133
134 val = fdt_getprop(fit, noffset, FIT_DATA_SIZE_PROP, NULL);
135 if (!val)
136 return -ENOENT;
137
138 *data_size = fdt32_to_cpu(*val);
139
140 return 0;
141 }
142
143 /**
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
150 *
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.
154 *
155 * returns:
156 * 0, on success
157 * otherwise, on failure
158 */
159 static int fit_image_get_data_and_size(const void *fit, int noffset,
160 const void **data, size_t *size)
161 {
162 bool external_data = false;
163 int offset;
164 int len;
165 int ret;
166
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;
171 /*
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.
175 */
176 offset += ((fdt_totalsize(fit) + 3) & ~3);
177 }
178
179 if (external_data) {
180 ret = fit_image_get_data_size(fit, noffset, &len);
181 if (!ret) {
182 *data = fit + offset;
183 *size = len;
184 }
185 } else {
186 ret = fit_image_get_data(fit, noffset, data, size);
187 }
188
189 return ret;
190 }
191
192 static int
193 mtdsplit_fit_parse(struct mtd_info *mtd,
194 const struct mtd_partition **pparts,
195 struct mtd_part_parser_data *data)
196 {
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;
201 size_t offset;
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;
209 void *fit;
210
211 of_property_read_string(np, "openwrt,cmdline-match", &cmdline_match);
212 if (cmdline_match && !strstr(saved_command_line, cmdline_match))
213 return -ENODEV;
214
215 of_property_read_u32(np, "openwrt,fit-offset", &offset_start);
216
217 hdr_len = sizeof(struct fdt_header);
218
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);
222 if (ret) {
223 pr_err("read error in \"%s\" at offset 0x%llx\n",
224 mtd->name, (unsigned long long) offset);
225 return ret;
226 }
227
228 if (retlen != hdr_len) {
229 pr_err("short read in \"%s\"\n", mtd->name);
230 return -EIO;
231 }
232
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);
237 continue;
238 }
239
240 /* We found a FIT image. Let's keep going */
241 break;
242 }
243
244 fit_offset = offset;
245 fit_size = be32_to_cpu(hdr.totalsize);
246
247 if (fit_size == 0) {
248 pr_err("FIT image in \"%s\" at offset %llx has null size\n",
249 mtd->name, (unsigned long long) fit_offset);
250 return -ENODEV;
251 }
252
253 /*
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.
260 */
261 if (fit_size > 0x1000) {
262 enum mtdsplit_part_type type;
263
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);
267 if (ret) {
268 pr_info("no rootfs found after FIT image in \"%s\"\n",
269 mtd->name);
270 return ret;
271 }
272
273 rootfs_size = mtd->size - rootfs_offset;
274
275 parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL);
276 if (!parts)
277 return -ENOMEM;
278
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;
282
283 if (type == MTDSPLIT_PART_TYPE_UBI)
284 parts[1].name = UBI_PART_NAME;
285 else
286 parts[1].name = ROOTFS_PART_NAME;
287 parts[1].offset = rootfs_offset;
288 parts[1].size = rootfs_size;
289
290 *pparts = parts;
291
292 return 2;
293 } else {
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);
297 if (ret) {
298 pr_err("read error in \"%s\" at offset 0x%llx\n",
299 mtd->name, (unsigned long long) offset);
300 return ret;
301 }
302
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));
307 return -ENODEV;
308 }
309
310 for (ndepth = 0,
311 noffset = fdt_next_node(fit, images_noffset, &ndepth);
312 (noffset >= 0) && (ndepth > 0);
313 noffset = fdt_next_node(fit, noffset, &ndepth)) {
314 if (ndepth == 1) {
315 ret = fit_image_get_data_and_size(fit, noffset, &img_data, &data_size);
316 if (ret)
317 return 0;
318
319 img_total = data_size + (img_data - fit);
320
321 max_size = (max_size > img_total) ? max_size : img_total;
322 }
323 }
324
325 parts = kzalloc(sizeof(*parts), GFP_KERNEL);
326 if (!parts)
327 return -ENOMEM;
328
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;
332
333 *pparts = parts;
334
335 kfree(fit);
336
337 return 1;
338 }
339 }
340
341 static const struct of_device_id mtdsplit_fit_of_match_table[] = {
342 { .compatible = "denx,fit" },
343 {},
344 };
345
346 static struct mtd_part_parser uimage_parser = {
347 .owner = THIS_MODULE,
348 .name = "fit-fw",
349 .of_match_table = mtdsplit_fit_of_match_table,
350 .parse_fn = mtdsplit_fit_parse,
351 .type = MTD_PARSER_TYPE_FIRMWARE,
352 };
353
354 /**************************************************
355 * Init
356 **************************************************/
357
358 static int __init mtdsplit_fit_init(void)
359 {
360 register_mtd_parser(&uimage_parser);
361
362 return 0;
363 }
364
365 module_init(mtdsplit_fit_init);