kernel: mtdsplit: support Broadcom WFI bootflags
authorÁlvaro Fernández Rojas <noltari@gmail.com>
Tue, 2 Jun 2020 15:52:12 +0000 (17:52 +0200)
committerÁlvaro Fernández Rojas <noltari@gmail.com>
Tue, 2 Jun 2020 16:23:40 +0000 (18:23 +0200)
When firmware is flashed, cferam.000 extension is renamed to the next number.
When booting, CFE scans the NAND and picks the partition with the highest
cferam extension and ignores the other one.

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_bcm_wfi.c

index 7487567ed595df13aeb26a54246e05e9491e4296..56bcff31c2db2d138d1f4014aaaf3f3691c23ec3 100644 (file)
 
 #define UBI_MAGIC              0x55424923
 
+#define CFE_MAGIC_PFX          "cferam."
+#define CFE_MAGIC_PFX_LEN      (sizeof(CFE_MAGIC_PFX) - 1)
+#define CFE_MAGIC              "cferam.000"
+#define CFE_MAGIC_LEN          (sizeof(CFE_MAGIC) - 1)
 #define SERCOMM_MAGIC_PFX      "eRcOmM."
 #define SERCOMM_MAGIC_PFX_LEN  (sizeof(SERCOMM_MAGIC_PFX) - 1)
 #define SERCOMM_MAGIC          "eRcOmM.000"
@@ -250,6 +254,111 @@ static struct mtd_part_parser mtdsplit_bcm_wfi_parser = {
        .type = MTD_PARSER_TYPE_FIRMWARE,
 };
 
+static int cferam_bootflag_value(const char *name, size_t name_len)
+{
+       int rc = -ENOENT;
+
+       if (name &&
+           (name_len >= CFE_MAGIC_LEN) &&
+           !memcmp(name, CFE_MAGIC_PFX, CFE_MAGIC_PFX_LEN)) {
+               rc = char_to_num(name[CFE_MAGIC_PFX_LEN + 0]) * 100;
+               rc += char_to_num(name[CFE_MAGIC_PFX_LEN + 1]) * 10;
+               rc += char_to_num(name[CFE_MAGIC_PFX_LEN + 2]) * 1;
+       }
+
+       return rc;
+}
+
+static int mtdsplit_parse_bcm_wfi_split(struct mtd_info *master,
+                                       const struct mtd_partition **pparts,
+                                       struct mtd_part_parser_data *data)
+{
+       loff_t cfe_off;
+       loff_t img1_off = 0;
+       loff_t img2_off = master->size / 2;
+       loff_t img1_size = (img2_off - img1_off);
+       loff_t img2_size = (master->size - img2_off);
+       loff_t active_off, inactive_off;
+       loff_t active_size, inactive_size;
+       uint8_t *buf;
+       char *cfe1_name = NULL, *cfe2_name = NULL;
+       size_t cfe1_size = 0, cfe2_size = 0;
+       int ret;
+       int bf1, bf2;
+
+       buf = kzalloc(master->erasesize, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       cfe_off = img1_off;
+       ret = jffs2_find_file(master, buf, CFERAM_NAME, CFERAM_NAME_LEN,
+                             &cfe_off, img1_size, &cfe1_name, &cfe1_size);
+
+       cfe_off = img2_off;
+       ret = jffs2_find_file(master, buf, CFERAM_NAME, CFERAM_NAME_LEN,
+                             &cfe_off, img2_size, &cfe2_name, &cfe2_size);
+
+       bf1 = cferam_bootflag_value(cfe1_name, cfe1_size);
+       if (bf1 >= 0)
+               printk("cferam: bootflag1=%d\n", bf1);
+
+       bf2 = cferam_bootflag_value(cfe2_name, cfe2_size);
+       if (bf2 >= 0)
+               printk("cferam: bootflag2=%d\n", bf2);
+
+       kfree(cfe1_name);
+       kfree(cfe2_name);
+
+       if (bf1 >= bf2) {
+               active_off = img1_off;
+               active_size = img1_size;
+               inactive_off = img2_off;
+               inactive_size = img2_size;
+       } else {
+               active_off = img2_off;
+               active_size = img2_size;
+               inactive_off = img1_off;
+               inactive_size = img1_size;
+       }
+
+       ret = parse_bcm_wfi(master, pparts, buf, active_off, active_size, true);
+
+       kfree(buf);
+
+       if (ret > 0) {
+               struct mtd_partition *parts;
+
+               parts = kzalloc((ret + 1) * sizeof(*parts), GFP_KERNEL);
+               if (!parts)
+                       return -ENOMEM;
+
+               memcpy(parts, *pparts, ret * sizeof(*parts));
+               kfree(*pparts);
+
+               parts[ret].name = "img2";
+               parts[ret].offset = inactive_off;
+               parts[ret].size = inactive_size;
+               ret++;
+
+               *pparts = parts;
+       }
+
+       return ret;
+}
+
+static const struct of_device_id mtdsplit_bcm_wfi_split_of_match[] = {
+       { .compatible = "brcm,wfi-split" },
+       { },
+};
+
+static struct mtd_part_parser mtdsplit_bcm_wfi_split_parser = {
+       .owner = THIS_MODULE,
+       .name = "bcm-wfi-split-fw",
+       .of_match_table = mtdsplit_bcm_wfi_split_of_match,
+       .parse_fn = mtdsplit_parse_bcm_wfi_split,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
 static int sercomm_bootflag_value(struct mtd_info *mtd, uint8_t *buf)
 {
        size_t retlen;
@@ -370,6 +479,7 @@ static struct mtd_part_parser mtdsplit_ser_wfi_parser = {
 static int __init mtdsplit_bcm_wfi_init(void)
 {
        register_mtd_parser(&mtdsplit_bcm_wfi_parser);
+       register_mtd_parser(&mtdsplit_bcm_wfi_split_parser);
        register_mtd_parser(&mtdsplit_ser_wfi_parser);
 
        return 0;