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
30 #define BCM_WFI_SPLIT_PARTS 2
32 #define CFERAM_NAME "cferam"
33 #define CFERAM_NAME_LEN (sizeof(CFERAM_NAME) - 1)
34 #define KERNEL_NAME "vmlinux.lz"
35 #define KERNEL_NAME_LEN (sizeof(KERNEL_NAME) - 1)
36 #define OPENWRT_NAME "1-openwrt"
37 #define OPENWRT_NAME_LEN (sizeof(OPENWRT_NAME) - 1)
39 #define UBI_MAGIC 0x55424923
41 #define CFE_MAGIC_PFX "cferam."
42 #define CFE_MAGIC_PFX_LEN (sizeof(CFE_MAGIC_PFX) - 1)
43 #define CFE_MAGIC "cferam.000"
44 #define CFE_MAGIC_LEN (sizeof(CFE_MAGIC) - 1)
45 #define SERCOMM_MAGIC_PFX "eRcOmM."
46 #define SERCOMM_MAGIC_PFX_LEN (sizeof(SERCOMM_MAGIC_PFX) - 1)
47 #define SERCOMM_MAGIC "eRcOmM.000"
48 #define SERCOMM_MAGIC_LEN (sizeof(SERCOMM_MAGIC) - 1)
50 #define PART_CFERAM "cferam"
51 #define PART_FIRMWARE "firmware"
52 #define PART_IMAGE_1 "img1"
53 #define PART_IMAGE_2 "img2"
55 static u32
jffs2_dirent_crc(struct jffs2_raw_dirent
*node
)
57 return crc32(0, node
, sizeof(struct jffs2_raw_dirent
) - 8);
60 static bool jffs2_dirent_valid(struct jffs2_raw_dirent
*node
)
62 return ((je16_to_cpu(node
->magic
) == JFFS2_MAGIC_BITMASK
) &&
63 (je16_to_cpu(node
->nodetype
) == JFFS2_NODETYPE_DIRENT
) &&
64 je32_to_cpu(node
->ino
) &&
65 je32_to_cpu(node
->node_crc
) == jffs2_dirent_crc(node
));
68 static int jffs2_find_file(struct mtd_info
*mtd
, uint8_t *buf
,
69 const char *name
, size_t name_len
,
70 loff_t
*offs
, loff_t size
,
71 char **out_name
, size_t *out_name_len
)
73 const loff_t end
= *offs
+ size
;
74 struct jffs2_raw_dirent
*node
;
80 for (; *offs
< end
; *offs
+= mtd
->erasesize
) {
81 unsigned int block_offs
= 0;
83 /* Skip CFE erased blocks */
84 rc
= mtd_read(mtd
, *offs
, sizeof(magic
), &retlen
,
86 if (rc
|| retlen
!= sizeof(magic
)) {
90 /* Skip blocks not starting with JFFS2 magic */
91 if (magic
!= JFFS2_MAGIC_BITMASK
)
95 rc
= mtd_read(mtd
, *offs
, mtd
->erasesize
, &retlen
,
99 if (retlen
!= mtd
->erasesize
)
102 while (block_offs
< mtd
->erasesize
) {
103 node
= (struct jffs2_raw_dirent
*) &buf
[block_offs
];
105 if (!jffs2_dirent_valid(node
)) {
110 if (!memcmp(node
->name
, OPENWRT_NAME
,
113 } else if (!memcmp(node
->name
, name
, name_len
)) {
118 *out_name
= kstrndup(node
->name
,
123 *out_name_len
= node
->nsize
;
128 block_offs
+= je32_to_cpu(node
->totlen
);
129 block_offs
= (block_offs
+ 0x3) & ~0x3;
136 static int ubifs_find(struct mtd_info
*mtd
, loff_t
*offs
, loff_t size
)
138 const loff_t end
= *offs
+ size
;
143 for (; *offs
< end
; *offs
+= mtd
->erasesize
) {
144 rc
= mtd_read(mtd
, *offs
, sizeof(magic
), &retlen
,
145 (unsigned char *) &magic
);
146 if (rc
|| retlen
!= sizeof(magic
))
149 if (be32_to_cpu(magic
) == UBI_MAGIC
)
156 static int parse_bcm_wfi(struct mtd_info
*master
,
157 const struct mtd_partition
**pparts
,
158 uint8_t *buf
, loff_t off
, loff_t size
, bool cfe_part
)
160 struct mtd_partition
*parts
;
161 loff_t cfe_off
, kernel_off
, rootfs_off
;
162 unsigned int num_parts
= BCM_WFI_PARTS
, cur_part
= 0;
169 ret
= jffs2_find_file(master
, buf
, CFERAM_NAME
,
170 CFERAM_NAME_LEN
, &cfe_off
,
171 size
- (cfe_off
- off
), NULL
, NULL
);
175 kernel_off
= cfe_off
+ master
->erasesize
;
180 ret
= jffs2_find_file(master
, buf
, KERNEL_NAME
, KERNEL_NAME_LEN
,
181 &kernel_off
, size
- (kernel_off
- off
),
186 rootfs_off
= kernel_off
+ master
->erasesize
;
187 ret
= ubifs_find(master
, &rootfs_off
, size
- (rootfs_off
- off
));
191 parts
= kzalloc(num_parts
* sizeof(*parts
), GFP_KERNEL
);
196 parts
[cur_part
].name
= PART_CFERAM
;
197 parts
[cur_part
].mask_flags
= MTD_WRITEABLE
;
198 parts
[cur_part
].offset
= cfe_off
;
199 parts
[cur_part
].size
= kernel_off
- cfe_off
;
203 parts
[cur_part
].name
= PART_FIRMWARE
;
204 parts
[cur_part
].offset
= kernel_off
;
205 parts
[cur_part
].size
= size
- (kernel_off
- off
);
208 parts
[cur_part
].name
= KERNEL_PART_NAME
;
209 parts
[cur_part
].offset
= kernel_off
;
210 parts
[cur_part
].size
= rootfs_off
- kernel_off
;
213 parts
[cur_part
].name
= UBI_PART_NAME
;
214 parts
[cur_part
].offset
= rootfs_off
;
215 parts
[cur_part
].size
= size
- (rootfs_off
- off
);
223 static int mtdsplit_parse_bcm_wfi(struct mtd_info
*master
,
224 const struct mtd_partition
**pparts
,
225 struct mtd_part_parser_data
*data
)
227 struct device_node
*mtd_node
;
228 bool cfe_part
= true;
232 mtd_node
= mtd_get_of_node(master
);
236 buf
= kzalloc(master
->erasesize
, GFP_KERNEL
);
240 if (of_property_read_bool(mtd_node
, "brcm,no-cferam"))
243 ret
= parse_bcm_wfi(master
, pparts
, buf
, 0, master
->size
, cfe_part
);
250 static const struct of_device_id mtdsplit_bcm_wfi_of_match
[] = {
251 { .compatible
= "brcm,wfi" },
255 static struct mtd_part_parser mtdsplit_bcm_wfi_parser
= {
256 .owner
= THIS_MODULE
,
257 .name
= "bcm-wfi-fw",
258 .of_match_table
= mtdsplit_bcm_wfi_of_match
,
259 .parse_fn
= mtdsplit_parse_bcm_wfi
,
260 .type
= MTD_PARSER_TYPE_FIRMWARE
,
263 static int cferam_bootflag_value(const char *name
, size_t name_len
)
268 (name_len
>= CFE_MAGIC_LEN
) &&
269 !memcmp(name
, CFE_MAGIC_PFX
, CFE_MAGIC_PFX_LEN
)) {
270 rc
= char_to_num(name
[CFE_MAGIC_PFX_LEN
+ 0]) * 100;
271 rc
+= char_to_num(name
[CFE_MAGIC_PFX_LEN
+ 1]) * 10;
272 rc
+= char_to_num(name
[CFE_MAGIC_PFX_LEN
+ 2]) * 1;
278 static int mtdsplit_parse_bcm_wfi_split(struct mtd_info
*master
,
279 const struct mtd_partition
**pparts
,
280 struct mtd_part_parser_data
*data
)
282 struct mtd_partition
*parts
;
285 loff_t img2_off
= master
->size
/ 2;
286 loff_t img1_size
= (img2_off
- img1_off
);
287 loff_t img2_size
= (master
->size
- img2_off
);
288 loff_t active_off
, inactive_off
;
289 loff_t active_size
, inactive_size
;
290 const char *inactive_name
;
292 char *cfe1_name
= NULL
, *cfe2_name
= NULL
;
293 size_t cfe1_size
= 0, cfe2_size
= 0;
297 buf
= kzalloc(master
->erasesize
, GFP_KERNEL
);
302 ret
= jffs2_find_file(master
, buf
, CFERAM_NAME
, CFERAM_NAME_LEN
,
303 &cfe_off
, img1_size
, &cfe1_name
, &cfe1_size
);
306 ret
= jffs2_find_file(master
, buf
, CFERAM_NAME
, CFERAM_NAME_LEN
,
307 &cfe_off
, img2_size
, &cfe2_name
, &cfe2_size
);
309 bf1
= cferam_bootflag_value(cfe1_name
, cfe1_size
);
311 printk("cferam: bootflag1=%d\n", bf1
);
313 bf2
= cferam_bootflag_value(cfe2_name
, cfe2_size
);
315 printk("cferam: bootflag2=%d\n", bf2
);
321 active_off
= img1_off
;
322 active_size
= img1_size
;
323 inactive_off
= img2_off
;
324 inactive_size
= img2_size
;
325 inactive_name
= PART_IMAGE_2
;
327 active_off
= img2_off
;
328 active_size
= img2_size
;
329 inactive_off
= img1_off
;
330 inactive_size
= img1_size
;
331 inactive_name
= PART_IMAGE_1
;
334 ret
= parse_bcm_wfi(master
, pparts
, buf
, active_off
, active_size
, true);
339 parts
= kzalloc((ret
+ 1) * sizeof(*parts
), GFP_KERNEL
);
343 memcpy(parts
, *pparts
, ret
* sizeof(*parts
));
346 parts
[ret
].name
= inactive_name
;
347 parts
[ret
].offset
= inactive_off
;
348 parts
[ret
].size
= inactive_size
;
353 parts
= kzalloc(BCM_WFI_SPLIT_PARTS
* sizeof(*parts
), GFP_KERNEL
);
355 parts
[0].name
= PART_IMAGE_1
;
356 parts
[0].offset
= img1_off
;
357 parts
[0].size
= img1_size
;
359 parts
[1].name
= PART_IMAGE_2
;
360 parts
[1].offset
= img2_off
;
361 parts
[1].size
= img2_size
;
369 static const struct of_device_id mtdsplit_bcm_wfi_split_of_match
[] = {
370 { .compatible
= "brcm,wfi-split" },
374 static struct mtd_part_parser mtdsplit_bcm_wfi_split_parser
= {
375 .owner
= THIS_MODULE
,
376 .name
= "bcm-wfi-split-fw",
377 .of_match_table
= mtdsplit_bcm_wfi_split_of_match
,
378 .parse_fn
= mtdsplit_parse_bcm_wfi_split
,
379 .type
= MTD_PARSER_TYPE_FIRMWARE
,
382 static int sercomm_bootflag_value(struct mtd_info
*mtd
, uint8_t *buf
)
388 for (offs
= 0; offs
< mtd
->size
; offs
+= mtd
->erasesize
) {
389 rc
= mtd_read(mtd
, offs
, SERCOMM_MAGIC_LEN
, &retlen
, buf
);
390 if (rc
|| retlen
!= SERCOMM_MAGIC_LEN
)
393 if (memcmp(buf
, SERCOMM_MAGIC_PFX
, SERCOMM_MAGIC_PFX_LEN
))
396 rc
= char_to_num(buf
[SERCOMM_MAGIC_PFX_LEN
+ 0]) * 100;
397 rc
+= char_to_num(buf
[SERCOMM_MAGIC_PFX_LEN
+ 1]) * 10;
398 rc
+= char_to_num(buf
[SERCOMM_MAGIC_PFX_LEN
+ 2]) * 1;
406 static int mtdsplit_parse_ser_wfi(struct mtd_info
*master
,
407 const struct mtd_partition
**pparts
,
408 struct mtd_part_parser_data
*data
)
410 struct mtd_partition
*parts
;
411 struct mtd_info
*mtd_bf1
, *mtd_bf2
;
413 loff_t img2_off
= master
->size
/ 2;
414 loff_t img1_size
= (img2_off
- img1_off
);
415 loff_t img2_size
= (master
->size
- img2_off
);
416 loff_t active_off
, inactive_off
;
417 loff_t active_size
, inactive_size
;
418 const char *inactive_name
;
423 mtd_bf1
= get_mtd_device_nm("bootflag1");
427 mtd_bf2
= get_mtd_device_nm("bootflag2");
431 buf
= kzalloc(master
->erasesize
, GFP_KERNEL
);
435 bf1
= sercomm_bootflag_value(mtd_bf1
, buf
);
437 printk("sercomm: bootflag1=%d\n", bf1
);
439 bf2
= sercomm_bootflag_value(mtd_bf2
, buf
);
441 printk("sercomm: bootflag2=%d\n", bf2
);
443 if (bf1
== bf2
&& bf2
>= 0) {
444 struct erase_info bf_erase
;
448 bf_erase
.len
= mtd_bf2
->size
;
449 mtd_erase(mtd_bf2
, &bf_erase
);
453 active_off
= img1_off
;
454 active_size
= img1_size
;
455 inactive_off
= img2_off
;
456 inactive_size
= img2_size
;
457 inactive_name
= PART_IMAGE_2
;
459 active_off
= img2_off
;
460 active_size
= img2_size
;
461 inactive_off
= img1_off
;
462 inactive_size
= img1_size
;
463 inactive_name
= PART_IMAGE_1
;
466 ret
= parse_bcm_wfi(master
, pparts
, buf
, active_off
, active_size
, false);
471 parts
= kzalloc((ret
+ 1) * sizeof(*parts
), GFP_KERNEL
);
475 memcpy(parts
, *pparts
, ret
* sizeof(*parts
));
478 parts
[ret
].name
= inactive_name
;
479 parts
[ret
].offset
= inactive_off
;
480 parts
[ret
].size
= inactive_size
;
485 parts
= kzalloc(BCM_WFI_SPLIT_PARTS
* sizeof(*parts
), GFP_KERNEL
);
487 parts
[0].name
= PART_IMAGE_1
;
488 parts
[0].offset
= img1_off
;
489 parts
[0].size
= img1_size
;
491 parts
[1].name
= PART_IMAGE_2
;
492 parts
[1].offset
= img2_off
;
493 parts
[1].size
= img2_size
;
501 static const struct of_device_id mtdsplit_ser_wfi_of_match
[] = {
502 { .compatible
= "sercomm,wfi" },
506 static struct mtd_part_parser mtdsplit_ser_wfi_parser
= {
507 .owner
= THIS_MODULE
,
508 .name
= "ser-wfi-fw",
509 .of_match_table
= mtdsplit_ser_wfi_of_match
,
510 .parse_fn
= mtdsplit_parse_ser_wfi
,
511 .type
= MTD_PARSER_TYPE_FIRMWARE
,
514 static int __init
mtdsplit_bcm_wfi_init(void)
516 register_mtd_parser(&mtdsplit_bcm_wfi_parser
);
517 register_mtd_parser(&mtdsplit_bcm_wfi_split_parser
);
518 register_mtd_parser(&mtdsplit_ser_wfi_parser
);
523 module_init(mtdsplit_bcm_wfi_init
);