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 4
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
*master
, uint8_t *buf
,
52 const char *name
, size_t name_len
,
55 struct jffs2_raw_dirent
*node
;
61 for (; *offs
< master
->size
; *offs
+= master
->erasesize
) {
62 unsigned int block_offs
= 0;
64 /* Skip CFE erased blocks */
65 rc
= mtd_read(master
, *offs
, sizeof(magic
), &retlen
,
67 if (rc
|| retlen
!= sizeof(magic
)) {
71 /* Skip blocks not starting with JFFS2 magic */
72 if (magic
!= JFFS2_MAGIC_BITMASK
)
76 rc
= mtd_read(master
, *offs
, master
->erasesize
, &retlen
,
80 if (retlen
!= master
->erasesize
)
83 while (block_offs
< master
->erasesize
) {
84 node
= (struct jffs2_raw_dirent
*) &buf
[block_offs
];
86 if (!jffs2_dirent_valid(node
)) {
91 if (!memcmp(node
->name
, OPENWRT_NAME
,
94 else if (!memcmp(node
->name
, name
, name_len
))
95 return valid
? 0 : -EINVAL
;
97 block_offs
+= je32_to_cpu(node
->totlen
);
98 block_offs
= (block_offs
+ 0x3) & ~0x3;
105 static int ubifs_find(struct mtd_info
*master
, loff_t
*offs
)
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
))
117 if (be32_to_cpu(magic
) == UBI_MAGIC
)
124 static int mtdsplit_parse_bcm_wfi(struct mtd_info
*master
,
125 const struct mtd_partition
**pparts
,
126 struct mtd_part_parser_data
*data
)
128 struct mtd_partition
*parts
;
129 loff_t cfe_off
, kernel_off
, rootfs_off
;
133 buf
= kzalloc(master
->erasesize
, GFP_KERNEL
);
138 ret
= jffs2_find_file(master
, buf
, CFERAM_NAME
, CFERAM_NAME_LEN
,
145 kernel_off
= cfe_off
+ master
->erasesize
;
146 ret
= jffs2_find_file(master
, buf
, KERNEL_NAME
, KERNEL_NAME_LEN
,
152 rootfs_off
= kernel_off
+ master
->erasesize
;
153 ret
= ubifs_find(master
, &rootfs_off
);
157 parts
= kzalloc(BCM_WFI_PARTS
* sizeof(*parts
), GFP_KERNEL
);
161 parts
[0].name
= "cferam";
162 parts
[0].mask_flags
= MTD_WRITEABLE
;
164 parts
[0].size
= kernel_off
;
166 parts
[1].name
= "firmware";
167 parts
[1].offset
= kernel_off
;
168 parts
[1].size
= master
->size
- kernel_off
;
170 parts
[2].name
= KERNEL_PART_NAME
;
171 parts
[2].offset
= kernel_off
;
172 parts
[2].size
= rootfs_off
- kernel_off
;
174 parts
[3].name
= UBI_PART_NAME
;
175 parts
[3].offset
= rootfs_off
;
176 parts
[3].size
= master
->size
- rootfs_off
;
180 return BCM_WFI_PARTS
;
183 static const struct of_device_id mtdsplit_fit_of_match_table
[] = {
184 { .compatible
= "brcm,wfi" },
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
,
196 static int __init
mtdsplit_bcm_wfi_init(void)
198 register_mtd_parser(&mtdsplit_bcm_wfi_parser
);
203 module_init(mtdsplit_bcm_wfi_init
);