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 BCM_WFI_PARTS 3
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)
36 #define UBI_MAGIC 0x55424923
38 static u32
jffs2_dirent_crc(struct jffs2_raw_dirent
*node
)
40 return crc32(0, node
, sizeof(struct jffs2_raw_dirent
) - 8);
43 static bool jffs2_dirent_valid(struct jffs2_raw_dirent
*node
)
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
));
51 static int jffs2_find_file(struct mtd_info
*mtd
, uint8_t *buf
,
52 const char *name
, size_t name_len
,
53 loff_t
*offs
, loff_t size
)
55 const loff_t end
= *offs
+ size
;
56 struct jffs2_raw_dirent
*node
;
62 for (; *offs
< end
; *offs
+= mtd
->erasesize
) {
63 unsigned int block_offs
= 0;
65 /* Skip CFE erased blocks */
66 rc
= mtd_read(mtd
, *offs
, sizeof(magic
), &retlen
,
68 if (rc
|| retlen
!= sizeof(magic
)) {
72 /* Skip blocks not starting with JFFS2 magic */
73 if (magic
!= JFFS2_MAGIC_BITMASK
)
77 rc
= mtd_read(mtd
, *offs
, mtd
->erasesize
, &retlen
,
81 if (retlen
!= mtd
->erasesize
)
84 while (block_offs
< mtd
->erasesize
) {
85 node
= (struct jffs2_raw_dirent
*) &buf
[block_offs
];
87 if (!jffs2_dirent_valid(node
)) {
92 if (!memcmp(node
->name
, OPENWRT_NAME
,
95 else if (!memcmp(node
->name
, name
, name_len
))
96 return valid
? 0 : -EINVAL
;
98 block_offs
+= je32_to_cpu(node
->totlen
);
99 block_offs
= (block_offs
+ 0x3) & ~0x3;
106 static int ubifs_find(struct mtd_info
*mtd
, loff_t
*offs
, loff_t size
)
108 const loff_t end
= *offs
+ size
;
113 for (; *offs
< end
; *offs
+= mtd
->erasesize
) {
114 rc
= mtd_read(mtd
, *offs
, sizeof(magic
), &retlen
,
115 (unsigned char *) &magic
);
116 if (rc
|| retlen
!= sizeof(magic
))
119 if (be32_to_cpu(magic
) == UBI_MAGIC
)
126 static int mtdsplit_parse_bcm_wfi(struct mtd_info
*master
,
127 const struct mtd_partition
**pparts
,
128 struct mtd_part_parser_data
*data
)
130 struct mtd_partition
*parts
;
131 struct device_node
*mtd_node
;
132 loff_t cfe_off
, kernel_off
, rootfs_off
;
133 bool cfe_part
= true;
134 unsigned int num_parts
= BCM_WFI_PARTS
, cur_part
= 0;
138 buf
= kzalloc(master
->erasesize
, GFP_KERNEL
);
142 mtd_node
= mtd_get_of_node(master
);
146 if (of_device_is_compatible(mtd_node
, "brcm,wfi-sercomm"))
153 ret
= jffs2_find_file(master
, buf
, CFERAM_NAME
,
154 CFERAM_NAME_LEN
, &cfe_off
,
161 kernel_off
= cfe_off
+ master
->erasesize
;
166 ret
= jffs2_find_file(master
, buf
, KERNEL_NAME
, KERNEL_NAME_LEN
,
167 &kernel_off
, master
->size
);
172 rootfs_off
= kernel_off
+ master
->erasesize
;
173 ret
= ubifs_find(master
, &rootfs_off
, master
->size
);
177 parts
= kzalloc(num_parts
* sizeof(*parts
), GFP_KERNEL
);
182 parts
[cur_part
].name
= "cferam";
183 parts
[cur_part
].mask_flags
= MTD_WRITEABLE
;
184 parts
[cur_part
].offset
= 0;
185 parts
[cur_part
].size
= kernel_off
;
189 parts
[cur_part
].name
= "firmware";
190 parts
[cur_part
].offset
= kernel_off
;
191 parts
[cur_part
].size
= master
->size
- kernel_off
;
194 parts
[cur_part
].name
= KERNEL_PART_NAME
;
195 parts
[cur_part
].offset
= kernel_off
;
196 parts
[cur_part
].size
= rootfs_off
- kernel_off
;
199 parts
[cur_part
].name
= UBI_PART_NAME
;
200 parts
[cur_part
].offset
= rootfs_off
;
201 parts
[cur_part
].size
= master
->size
- rootfs_off
;
209 static const struct of_device_id mtdsplit_bcm_wfi_of_match
[] = {
210 { .compatible
= "brcm,wfi" },
211 { .compatible
= "brcm,wfi-sercomm" },
215 static struct mtd_part_parser mtdsplit_bcm_wfi_parser
= {
216 .owner
= THIS_MODULE
,
217 .name
= "bcm-wfi-fw",
218 .of_match_table
= mtdsplit_bcm_wfi_of_match
,
219 .parse_fn
= mtdsplit_parse_bcm_wfi
,
220 .type
= MTD_PARSER_TYPE_FIRMWARE
,
223 static int __init
mtdsplit_bcm_wfi_init(void)
225 register_mtd_parser(&mtdsplit_bcm_wfi_parser
);
230 module_init(mtdsplit_bcm_wfi_init
);