9fc3c8ac507e91f8c2bed1063a747e30d5f094ae
[openwrt/staging/luka.git] / target / linux / generic / files / drivers / mtd / mtdsplit / mtdsplit_bcm_wfi.c
1 /*
2 * MTD split for Broadcom Whole Flash Image
3 *
4 * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published
8 * by the Free Software Foundation.
9 *
10 */
11
12 #define je16_to_cpu(x) ((x).v16)
13 #define je32_to_cpu(x) ((x).v32)
14
15 #include <linux/crc32.h>
16 #include <linux/init.h>
17 #include <linux/jffs2.h>
18 #include <linux/kernel.h>
19 #include <linux/module.h>
20 #include <linux/slab.h>
21 #include <linux/byteorder/generic.h>
22 #include <linux/mtd/mtd.h>
23 #include <linux/mtd/partitions.h>
24
25 #include "mtdsplit.h"
26
27 #define BCM_WFI_PARTS 4
28
29 #define CFERAM_NAME "cferam"
30 #define CFERAM_NAME_LEN (sizeof(CFERAM_NAME) - 1)
31 #define KERNEL_NAME "vmlinux.lz"
32 #define KERNEL_NAME_LEN (sizeof(KERNEL_NAME) - 1)
33 #define OPENWRT_NAME "1-openwrt"
34 #define OPENWRT_NAME_LEN (sizeof(OPENWRT_NAME) - 1)
35
36 #define UBI_MAGIC 0x55424923
37
38 static u32 jffs2_dirent_crc(struct jffs2_raw_dirent *node)
39 {
40 return crc32(0, node, sizeof(struct jffs2_raw_dirent) - 8);
41 }
42
43 static bool jffs2_dirent_valid(struct jffs2_raw_dirent *node)
44 {
45 return ((je16_to_cpu(node->magic) == JFFS2_MAGIC_BITMASK) &&
46 (je16_to_cpu(node->nodetype) == JFFS2_NODETYPE_DIRENT) &&
47 je32_to_cpu(node->ino) &&
48 je32_to_cpu(node->node_crc) == jffs2_dirent_crc(node));
49 }
50
51 static int jffs2_find_file(struct mtd_info *master, uint8_t *buf,
52 const char *name, size_t name_len,
53 loff_t *offs)
54 {
55 struct jffs2_raw_dirent *node;
56 bool valid = false;
57 size_t retlen;
58 uint16_t magic;
59 int rc;
60
61 for (; *offs < master->size; *offs += master->erasesize) {
62 unsigned int block_offs = 0;
63
64 /* Skip CFE erased blocks */
65 rc = mtd_read(master, *offs, sizeof(magic), &retlen,
66 (void *) &magic);
67 if (rc || retlen != sizeof(magic)) {
68 continue;
69 }
70
71 /* Skip blocks not starting with JFFS2 magic */
72 if (magic != JFFS2_MAGIC_BITMASK)
73 continue;
74
75 /* Read full block */
76 rc = mtd_read(master, *offs, master->erasesize, &retlen,
77 (void *) buf);
78 if (rc)
79 return rc;
80 if (retlen != master->erasesize)
81 return -EINVAL;
82
83 while (block_offs < master->erasesize) {
84 node = (struct jffs2_raw_dirent *) &buf[block_offs];
85
86 if (!jffs2_dirent_valid(node)) {
87 block_offs += 4;
88 continue;
89 }
90
91 if (!memcmp(node->name, OPENWRT_NAME,
92 OPENWRT_NAME_LEN))
93 valid = true;
94 else if (!memcmp(node->name, name, name_len))
95 return valid ? 0 : -EINVAL;
96
97 block_offs += je32_to_cpu(node->totlen);
98 block_offs = (block_offs + 0x3) & ~0x3;
99 }
100 }
101
102 return -ENOENT;
103 }
104
105 static int ubifs_find(struct mtd_info *master, loff_t *offs)
106 {
107 uint32_t magic;
108 size_t retlen;
109 int rc;
110
111 for (; *offs < master->size; *offs += master->erasesize) {
112 rc = mtd_read(master, *offs, sizeof(magic), &retlen,
113 (unsigned char *) &magic);
114 if (rc || retlen != sizeof(magic))
115 continue;
116
117 if (be32_to_cpu(magic) == UBI_MAGIC)
118 return 0;
119 }
120
121 return -ENOENT;
122 }
123
124 static int mtdsplit_parse_bcm_wfi(struct mtd_info *master,
125 const struct mtd_partition **pparts,
126 struct mtd_part_parser_data *data)
127 {
128 struct mtd_partition *parts;
129 loff_t cfe_off, kernel_off, rootfs_off;
130 uint8_t *buf;
131 int ret;
132
133 buf = kzalloc(master->erasesize, GFP_KERNEL);
134 if (!buf)
135 return -ENOMEM;
136
137 cfe_off = 0;
138 ret = jffs2_find_file(master, buf, CFERAM_NAME, CFERAM_NAME_LEN,
139 &cfe_off);
140 if (ret) {
141 kfree(buf);
142 return ret;
143 }
144
145 kernel_off = cfe_off + master->erasesize;
146 ret = jffs2_find_file(master, buf, KERNEL_NAME, KERNEL_NAME_LEN,
147 &kernel_off);
148 kfree(buf);
149 if (ret)
150 return ret;
151
152 rootfs_off = kernel_off + master->erasesize;
153 ret = ubifs_find(master, &rootfs_off);
154 if (ret)
155 return ret;
156
157 parts = kzalloc(BCM_WFI_PARTS * sizeof(*parts), GFP_KERNEL);
158 if (!parts)
159 return -ENOMEM;
160
161 parts[0].name = "cferam";
162 parts[0].mask_flags = MTD_WRITEABLE;
163 parts[0].offset = 0;
164 parts[0].size = kernel_off;
165
166 parts[1].name = "firmware";
167 parts[1].offset = kernel_off;
168 parts[1].size = master->size - kernel_off;
169
170 parts[2].name = KERNEL_PART_NAME;
171 parts[2].offset = kernel_off;
172 parts[2].size = rootfs_off - kernel_off;
173
174 parts[3].name = UBI_PART_NAME;
175 parts[3].offset = rootfs_off;
176 parts[3].size = master->size - rootfs_off;
177
178 *pparts = parts;
179
180 return BCM_WFI_PARTS;
181 }
182
183 static const struct of_device_id mtdsplit_fit_of_match_table[] = {
184 { .compatible = "brcm,wfi" },
185 { },
186 };
187
188 static struct mtd_part_parser mtdsplit_bcm_wfi_parser = {
189 .owner = THIS_MODULE,
190 .name = "bcm-wfi-fw",
191 .of_match_table = mtdsplit_fit_of_match_table,
192 .parse_fn = mtdsplit_parse_bcm_wfi,
193 .type = MTD_PARSER_TYPE_FIRMWARE,
194 };
195
196 static int __init mtdsplit_bcm_wfi_init(void)
197 {
198 register_mtd_parser(&mtdsplit_bcm_wfi_parser);
199
200 return 0;
201 }
202
203 module_init(mtdsplit_bcm_wfi_init);