kernel: mtdsplit: modify rootfs helpers to provide partition type
[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, struct mtd_partition **pparts,
49                    struct mtd_part_parser_data *data)
50 {
51         struct fdt_header hdr;
52         size_t hdr_len, retlen;
53         size_t offset;
54         size_t fit_offset, fit_size;
55         size_t rootfs_offset, rootfs_size;
56         struct mtd_partition *parts;
57         int ret;
58
59         hdr_len = sizeof(struct fdt_header);
60
61         /* Parse the MTD device & search for the FIT image location */
62         for(offset = 0; offset < mtd->size; offset += mtd->erasesize) {
63                 ret = mtd_read(mtd, 0, hdr_len, &retlen, (void*) &hdr);
64                 if (ret) {
65                         pr_err("read error in \"%s\" at offset 0x%llx\n",
66                                mtd->name, (unsigned long long) offset);
67                         return ret;
68                 }
69
70                 if (retlen != hdr_len) {
71                         pr_err("short read in \"%s\"\n", mtd->name);
72                         return -EIO;
73                 }
74
75                 /* Check the magic - see if this is a FIT image */
76                 if (be32_to_cpu(hdr.magic) != OF_DT_HEADER) {
77                         pr_debug("no valid FIT image found in \"%s\" at offset %llx\n",
78                                  mtd->name, (unsigned long long) offset);
79                         continue;
80                 }
81
82                 /* We found a FIT image. Let's keep going */
83                 break;
84         }
85
86         fit_offset = offset;
87         fit_size = be32_to_cpu(hdr.totalsize);
88
89         if (fit_size == 0) {
90                 pr_err("FIT image in \"%s\" at offset %llx has null size\n",
91                        mtd->name, (unsigned long long) fit_offset);
92                 return -ENODEV;
93         }
94
95         /* Search for the rootfs partition after the FIT image */
96         ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size, mtd->size,
97                                    &rootfs_offset, NULL);
98         if (ret) {
99                 pr_info("no rootfs found after FIT image in \"%s\"\n",
100                         mtd->name);
101                 return ret;
102         }
103
104         rootfs_size = mtd->size - rootfs_offset;
105
106         parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL);
107         if (!parts)
108                 return -ENOMEM;
109
110         parts[0].name = KERNEL_PART_NAME;
111         parts[0].offset = fit_offset;
112         parts[0].size = mtd_rounddown_to_eb(fit_size, mtd) + mtd->erasesize;
113
114         parts[1].name = ROOTFS_PART_NAME;
115         parts[1].offset = rootfs_offset;
116         parts[1].size = rootfs_size;
117
118         *pparts = parts;
119         return 2;
120 }
121
122 static struct mtd_part_parser uimage_parser = {
123         .owner = THIS_MODULE,
124         .name = "fit-fw",
125         .parse_fn = mtdsplit_fit_parse,
126         .type = MTD_PARSER_TYPE_FIRMWARE,
127 };
128
129 /**************************************************
130  * Init
131  **************************************************/
132
133 static int __init mtdsplit_fit_init(void)
134 {
135         register_mtd_parser(&uimage_parser);
136
137         return 0;
138 }
139
140 module_init(mtdsplit_fit_init);