2 * MTD split for Broadcom Whole Flash Image
4 * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
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.
12 #define je16_to_cpu(x) ((x).v16)
13 #define je32_to_cpu(x) ((x).v32)
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>
27 #define char_to_num(c) ((c >= '0' && c <= '9') ? (c - '0') : (0))
29 #define BCM_WFI_PARTS 3
31 #define CFERAM_NAME "cferam"
32 #define CFERAM_NAME_LEN (sizeof(CFERAM_NAME) - 1)
33 #define KERNEL_NAME "vmlinux.lz"
34 #define KERNEL_NAME_LEN (sizeof(KERNEL_NAME) - 1)
35 #define OPENWRT_NAME "1-openwrt"
36 #define OPENWRT_NAME_LEN (sizeof(OPENWRT_NAME) - 1)
38 #define UBI_MAGIC 0x55424923
40 #define SERCOMM_MAGIC_PFX "eRcOmM."
41 #define SERCOMM_MAGIC_PFX_LEN (sizeof(SERCOMM_MAGIC_PFX) - 1)
42 #define SERCOMM_MAGIC "eRcOmM.000"
43 #define SERCOMM_MAGIC_LEN (sizeof(SERCOMM_MAGIC) - 1)
45 static u32
jffs2_dirent_crc(struct jffs2_raw_dirent
*node
)
47 return crc32(0, node
, sizeof(struct jffs2_raw_dirent
) - 8);
50 static bool jffs2_dirent_valid(struct jffs2_raw_dirent
*node
)
52 return ((je16_to_cpu(node
->magic
) == JFFS2_MAGIC_BITMASK
) &&
53 (je16_to_cpu(node
->nodetype
) == JFFS2_NODETYPE_DIRENT
) &&
54 je32_to_cpu(node
->ino
) &&
55 je32_to_cpu(node
->node_crc
) == jffs2_dirent_crc(node
));
58 static int jffs2_find_file(struct mtd_info
*mtd
, uint8_t *buf
,
59 const char *name
, size_t name_len
,
60 loff_t
*offs
, loff_t size
)
62 const loff_t end
= *offs
+ size
;
63 struct jffs2_raw_dirent
*node
;
69 for (; *offs
< end
; *offs
+= mtd
->erasesize
) {
70 unsigned int block_offs
= 0;
72 /* Skip CFE erased blocks */
73 rc
= mtd_read(mtd
, *offs
, sizeof(magic
), &retlen
,
75 if (rc
|| retlen
!= sizeof(magic
)) {
79 /* Skip blocks not starting with JFFS2 magic */
80 if (magic
!= JFFS2_MAGIC_BITMASK
)
84 rc
= mtd_read(mtd
, *offs
, mtd
->erasesize
, &retlen
,
88 if (retlen
!= mtd
->erasesize
)
91 while (block_offs
< mtd
->erasesize
) {
92 node
= (struct jffs2_raw_dirent
*) &buf
[block_offs
];
94 if (!jffs2_dirent_valid(node
)) {
99 if (!memcmp(node
->name
, OPENWRT_NAME
,
102 else if (!memcmp(node
->name
, name
, name_len
))
103 return valid
? 0 : -EINVAL
;
105 block_offs
+= je32_to_cpu(node
->totlen
);
106 block_offs
= (block_offs
+ 0x3) & ~0x3;
113 static int ubifs_find(struct mtd_info
*mtd
, loff_t
*offs
, loff_t size
)
115 const loff_t end
= *offs
+ size
;
120 for (; *offs
< end
; *offs
+= mtd
->erasesize
) {
121 rc
= mtd_read(mtd
, *offs
, sizeof(magic
), &retlen
,
122 (unsigned char *) &magic
);
123 if (rc
|| retlen
!= sizeof(magic
))
126 if (be32_to_cpu(magic
) == UBI_MAGIC
)
133 static int mtdsplit_parse_bcm_wfi(struct mtd_info
*master
,
134 const struct mtd_partition
**pparts
,
135 struct mtd_part_parser_data
*data
)
137 struct mtd_partition
*parts
;
138 struct device_node
*mtd_node
;
139 loff_t cfe_off
, kernel_off
, rootfs_off
;
140 bool cfe_part
= true;
141 unsigned int num_parts
= BCM_WFI_PARTS
, cur_part
= 0;
145 buf
= kzalloc(master
->erasesize
, GFP_KERNEL
);
149 mtd_node
= mtd_get_of_node(master
);
153 if (of_device_is_compatible(mtd_node
, "brcm,wfi-sercomm"))
160 ret
= jffs2_find_file(master
, buf
, CFERAM_NAME
,
161 CFERAM_NAME_LEN
, &cfe_off
,
168 kernel_off
= cfe_off
+ master
->erasesize
;
173 ret
= jffs2_find_file(master
, buf
, KERNEL_NAME
, KERNEL_NAME_LEN
,
174 &kernel_off
, master
->size
);
179 rootfs_off
= kernel_off
+ master
->erasesize
;
180 ret
= ubifs_find(master
, &rootfs_off
, master
->size
);
184 parts
= kzalloc(num_parts
* sizeof(*parts
), GFP_KERNEL
);
189 parts
[cur_part
].name
= "cferam";
190 parts
[cur_part
].mask_flags
= MTD_WRITEABLE
;
191 parts
[cur_part
].offset
= 0;
192 parts
[cur_part
].size
= kernel_off
;
196 parts
[cur_part
].name
= "firmware";
197 parts
[cur_part
].offset
= kernel_off
;
198 parts
[cur_part
].size
= master
->size
- kernel_off
;
201 parts
[cur_part
].name
= KERNEL_PART_NAME
;
202 parts
[cur_part
].offset
= kernel_off
;
203 parts
[cur_part
].size
= rootfs_off
- kernel_off
;
206 parts
[cur_part
].name
= UBI_PART_NAME
;
207 parts
[cur_part
].offset
= rootfs_off
;
208 parts
[cur_part
].size
= master
->size
- rootfs_off
;
216 static const struct of_device_id mtdsplit_bcm_wfi_of_match
[] = {
217 { .compatible
= "brcm,wfi" },
218 { .compatible
= "brcm,wfi-sercomm" },
222 static struct mtd_part_parser mtdsplit_bcm_wfi_parser
= {
223 .owner
= THIS_MODULE
,
224 .name
= "bcm-wfi-fw",
225 .of_match_table
= mtdsplit_bcm_wfi_of_match
,
226 .parse_fn
= mtdsplit_parse_bcm_wfi
,
227 .type
= MTD_PARSER_TYPE_FIRMWARE
,
230 static int sercomm_bootflag_value(struct mtd_info
*mtd
, uint8_t *buf
)
236 for (offs
= 0; offs
< mtd
->size
; offs
+= mtd
->erasesize
) {
237 rc
= mtd_read(mtd
, offs
, SERCOMM_MAGIC_LEN
, &retlen
, buf
);
238 if (rc
|| retlen
!= SERCOMM_MAGIC_LEN
)
241 if (memcmp(buf
, SERCOMM_MAGIC_PFX
, SERCOMM_MAGIC_PFX_LEN
))
244 rc
= char_to_num(buf
[SERCOMM_MAGIC_PFX_LEN
+ 0]) * 100;
245 rc
+= char_to_num(buf
[SERCOMM_MAGIC_PFX_LEN
+ 1]) * 10;
246 rc
+= char_to_num(buf
[SERCOMM_MAGIC_PFX_LEN
+ 2]) * 1;
254 static int mtdsplit_parse_ser_wfi(struct mtd_info
*master
,
255 const struct mtd_partition
**pparts
,
256 struct mtd_part_parser_data
*data
)
258 struct mtd_info
*mtd_bf1
, *mtd_bf2
;
259 struct erase_info bf_erase
;
260 struct mtd_partition
*parts
;
261 loff_t kernel_off
, rootfs_off
, img_size
;
262 loff_t img2_off
, img2_size
= 0;
263 unsigned int num_parts
= BCM_WFI_PARTS
, cur_part
= 0;
268 mtd_bf1
= get_mtd_device_nm("bootflag1");
272 mtd_bf2
= get_mtd_device_nm("bootflag2");
276 buf
= kzalloc(master
->erasesize
, GFP_KERNEL
);
280 bf1
= sercomm_bootflag_value(mtd_bf1
, buf
);
282 printk("sercomm: bootflag1=%d\n", bf1
);
284 bf2
= sercomm_bootflag_value(mtd_bf2
, buf
);
286 printk("sercomm: bootflag2=%d\n", bf2
);
288 if (bf1
== bf2
&& bf2
>= 0) {
291 bf_erase
.len
= mtd_bf2
->size
;
292 mtd_erase(mtd_bf2
, &bf_erase
);
298 img_size
= master
->size
/ 2;
301 img2_size
= master
->size
- img2_off
;
304 img_size
= master
->size
;
307 kernel_off
= master
->size
/ 2;
308 img_size
= master
->size
;
311 img2_size
= kernel_off
;
315 ret
= jffs2_find_file(master
, buf
, KERNEL_NAME
, KERNEL_NAME_LEN
,
316 &kernel_off
, img_size
);
321 rootfs_off
= kernel_off
+ master
->erasesize
;
322 ret
= ubifs_find(master
, &rootfs_off
, img_size
);
326 parts
= kzalloc(num_parts
* sizeof(*parts
), GFP_KERNEL
);
330 parts
[cur_part
].name
= "firmware";
331 parts
[cur_part
].offset
= kernel_off
;
332 parts
[cur_part
].size
= img_size
- kernel_off
;
335 parts
[cur_part
].name
= KERNEL_PART_NAME
;
336 parts
[cur_part
].offset
= kernel_off
;
337 parts
[cur_part
].size
= rootfs_off
- kernel_off
;
340 parts
[cur_part
].name
= UBI_PART_NAME
;
341 parts
[cur_part
].offset
= rootfs_off
;
342 parts
[cur_part
].size
= img_size
- rootfs_off
;
346 parts
[cur_part
].name
= "img2";
347 parts
[cur_part
].offset
= img2_off
;
348 parts
[cur_part
].size
= img2_size
;
357 static const struct of_device_id mtdsplit_ser_wfi_of_match
[] = {
358 { .compatible
= "sercomm,wfi" },
362 static struct mtd_part_parser mtdsplit_ser_wfi_parser
= {
363 .owner
= THIS_MODULE
,
364 .name
= "ser-wfi-fw",
365 .of_match_table
= mtdsplit_ser_wfi_of_match
,
366 .parse_fn
= mtdsplit_parse_ser_wfi
,
367 .type
= MTD_PARSER_TYPE_FIRMWARE
,
370 static int __init
mtdsplit_bcm_wfi_init(void)
372 register_mtd_parser(&mtdsplit_bcm_wfi_parser
);
373 register_mtd_parser(&mtdsplit_ser_wfi_parser
);
378 module_init(mtdsplit_bcm_wfi_init
);