kernel: add support for enabling fit firmware partition parser via cmdline
[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/of_fdt.h>
25
26 #include "mtdsplit.h"
27
28 struct fdt_header {
29 uint32_t magic; /* magic word FDT_MAGIC */
30 uint32_t totalsize; /* total size of DT block */
31 uint32_t off_dt_struct; /* offset to structure */
32 uint32_t off_dt_strings; /* offset to strings */
33 uint32_t off_mem_rsvmap; /* offset to memory reserve map */
34 uint32_t version; /* format version */
35 uint32_t last_comp_version; /* last compatible version */
36
37 /* version 2 fields below */
38 uint32_t boot_cpuid_phys; /* Which physical CPU id we're
39 booting on */
40 /* version 3 fields below */
41 uint32_t size_dt_strings; /* size of the strings block */
42
43 /* version 17 fields below */
44 uint32_t size_dt_struct; /* size of the structure block */
45 };
46
47 static int
48 mtdsplit_fit_parse(struct mtd_info *mtd,
49 const struct mtd_partition **pparts,
50 struct mtd_part_parser_data *data)
51 {
52 struct device_node *np = mtd_get_of_node(mtd);
53 const char *cmdline_match = NULL;
54 struct fdt_header hdr;
55 size_t hdr_len, retlen;
56 size_t offset;
57 size_t fit_offset, fit_size;
58 size_t rootfs_offset, rootfs_size;
59 struct mtd_partition *parts;
60 int ret;
61
62 of_property_read_string(np, "openwrt,cmdline-match", &cmdline_match);
63 if (cmdline_match && !strstr(saved_command_line, cmdline_match))
64 return -ENODEV;
65
66 hdr_len = sizeof(struct fdt_header);
67
68 /* Parse the MTD device & search for the FIT image location */
69 for(offset = 0; offset + hdr_len <= mtd->size; offset += mtd->erasesize) {
70 ret = mtd_read(mtd, offset, hdr_len, &retlen, (void*) &hdr);
71 if (ret) {
72 pr_err("read error in \"%s\" at offset 0x%llx\n",
73 mtd->name, (unsigned long long) offset);
74 return ret;
75 }
76
77 if (retlen != hdr_len) {
78 pr_err("short read in \"%s\"\n", mtd->name);
79 return -EIO;
80 }
81
82 /* Check the magic - see if this is a FIT image */
83 if (be32_to_cpu(hdr.magic) != OF_DT_HEADER) {
84 pr_debug("no valid FIT image found in \"%s\" at offset %llx\n",
85 mtd->name, (unsigned long long) offset);
86 continue;
87 }
88
89 /* We found a FIT image. Let's keep going */
90 break;
91 }
92
93 fit_offset = offset;
94 fit_size = be32_to_cpu(hdr.totalsize);
95
96 if (fit_size == 0) {
97 pr_err("FIT image in \"%s\" at offset %llx has null size\n",
98 mtd->name, (unsigned long long) fit_offset);
99 return -ENODEV;
100 }
101
102 /* Search for the rootfs partition after the FIT image */
103 ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size, mtd->size,
104 &rootfs_offset, NULL);
105 if (ret) {
106 pr_info("no rootfs found after FIT image in \"%s\"\n",
107 mtd->name);
108 return ret;
109 }
110
111 rootfs_size = mtd->size - rootfs_offset;
112
113 parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL);
114 if (!parts)
115 return -ENOMEM;
116
117 parts[0].name = KERNEL_PART_NAME;
118 parts[0].offset = fit_offset;
119 parts[0].size = mtd_rounddown_to_eb(fit_size, mtd) + mtd->erasesize;
120
121 parts[1].name = ROOTFS_PART_NAME;
122 parts[1].offset = rootfs_offset;
123 parts[1].size = rootfs_size;
124
125 *pparts = parts;
126 return 2;
127 }
128
129 static const struct of_device_id mtdsplit_fit_of_match_table[] = {
130 { .compatible = "denx,fit" },
131 {},
132 };
133
134 static struct mtd_part_parser uimage_parser = {
135 .owner = THIS_MODULE,
136 .name = "fit-fw",
137 .of_match_table = mtdsplit_fit_of_match_table,
138 .parse_fn = mtdsplit_fit_parse,
139 .type = MTD_PARSER_TYPE_FIRMWARE,
140 };
141
142 /**************************************************
143 * Init
144 **************************************************/
145
146 static int __init mtdsplit_fit_init(void)
147 {
148 register_mtd_parser(&uimage_parser);
149
150 return 0;
151 }
152
153 module_init(mtdsplit_fit_init);