Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * BCM47XX MTD partitioning
0004  *
0005  * Copyright © 2012 Rafał Miłecki <zajec5@gmail.com>
0006  */
0007 
0008 #include <linux/bcm47xx_nvram.h>
0009 #include <linux/module.h>
0010 #include <linux/kernel.h>
0011 #include <linux/slab.h>
0012 #include <linux/mtd/mtd.h>
0013 #include <linux/mtd/partitions.h>
0014 
0015 #include <uapi/linux/magic.h>
0016 
0017 /*
0018  * NAND flash on Netgear R6250 was verified to contain 15 partitions.
0019  * This will result in allocating too big array for some old devices, but the
0020  * memory will be freed soon anyway (see mtd_device_parse_register).
0021  */
0022 #define BCM47XXPART_MAX_PARTS       20
0023 
0024 /*
0025  * Amount of bytes we read when analyzing each block of flash memory.
0026  * Set it big enough to allow detecting partition and reading important data.
0027  */
0028 #define BCM47XXPART_BYTES_TO_READ   0x4e8
0029 
0030 /* Magics */
0031 #define BOARD_DATA_MAGIC        0x5246504D  /* MPFR */
0032 #define BOARD_DATA_MAGIC2       0xBD0D0BBD
0033 #define CFE_MAGIC           0x43464531  /* 1EFC */
0034 #define FACTORY_MAGIC           0x59544346  /* FCTY */
0035 #define NVRAM_HEADER            0x48534C46  /* FLSH */
0036 #define POT_MAGIC1          0x54544f50  /* POTT */
0037 #define POT_MAGIC2          0x504f      /* OP */
0038 #define ML_MAGIC1           0x39685a42
0039 #define ML_MAGIC2           0x26594131
0040 #define TRX_MAGIC           0x30524448
0041 #define SHSQ_MAGIC          0x71736873  /* shsq (weird ZTE H218N endianness) */
0042 
0043 static const char * const trx_types[] = { "trx", NULL };
0044 
0045 struct trx_header {
0046     uint32_t magic;
0047     uint32_t length;
0048     uint32_t crc32;
0049     uint16_t flags;
0050     uint16_t version;
0051     uint32_t offset[3];
0052 } __packed;
0053 
0054 static void bcm47xxpart_add_part(struct mtd_partition *part, const char *name,
0055                  u64 offset, uint32_t mask_flags)
0056 {
0057     part->name = name;
0058     part->offset = offset;
0059     part->mask_flags = mask_flags;
0060 }
0061 
0062 /**
0063  * bcm47xxpart_bootpartition - gets index of TRX partition used by bootloader
0064  *
0065  * Some devices may have more than one TRX partition. In such case one of them
0066  * is the main one and another a failsafe one. Bootloader may fallback to the
0067  * failsafe firmware if it detects corruption of the main image.
0068  *
0069  * This function provides info about currently used TRX partition. It's the one
0070  * containing kernel started by the bootloader.
0071  */
0072 static int bcm47xxpart_bootpartition(void)
0073 {
0074     char buf[4];
0075     int bootpartition;
0076 
0077     /* Check CFE environment variable */
0078     if (bcm47xx_nvram_getenv("bootpartition", buf, sizeof(buf)) > 0) {
0079         if (!kstrtoint(buf, 0, &bootpartition))
0080             return bootpartition;
0081     }
0082 
0083     return 0;
0084 }
0085 
0086 static int bcm47xxpart_parse(struct mtd_info *master,
0087                  const struct mtd_partition **pparts,
0088                  struct mtd_part_parser_data *data)
0089 {
0090     struct mtd_partition *parts;
0091     uint8_t i, curr_part = 0;
0092     uint32_t *buf;
0093     size_t bytes_read;
0094     uint32_t offset;
0095     uint32_t blocksize = master->erasesize;
0096     int trx_parts[2]; /* Array with indexes of TRX partitions */
0097     int trx_num = 0; /* Number of found TRX partitions */
0098     int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
0099     int err;
0100 
0101     /*
0102      * Some really old flashes (like AT45DB*) had smaller erasesize-s, but
0103      * partitions were aligned to at least 0x1000 anyway.
0104      */
0105     if (blocksize < 0x1000)
0106         blocksize = 0x1000;
0107 
0108     /* Alloc */
0109     parts = kcalloc(BCM47XXPART_MAX_PARTS, sizeof(struct mtd_partition),
0110             GFP_KERNEL);
0111     if (!parts)
0112         return -ENOMEM;
0113 
0114     buf = kzalloc(BCM47XXPART_BYTES_TO_READ, GFP_KERNEL);
0115     if (!buf) {
0116         kfree(parts);
0117         return -ENOMEM;
0118     }
0119 
0120     /* Parse block by block looking for magics */
0121     for (offset = 0; offset <= master->size - blocksize;
0122          offset += blocksize) {
0123         /* Nothing more in higher memory on BCM47XX (MIPS) */
0124         if (IS_ENABLED(CONFIG_BCM47XX) && offset >= 0x2000000)
0125             break;
0126 
0127         if (curr_part >= BCM47XXPART_MAX_PARTS) {
0128             pr_warn("Reached maximum number of partitions, scanning stopped!\n");
0129             break;
0130         }
0131 
0132         /* Read beginning of the block */
0133         err = mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ,
0134                    &bytes_read, (uint8_t *)buf);
0135         if (err && !mtd_is_bitflip(err)) {
0136             pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
0137                    offset, err);
0138             continue;
0139         }
0140 
0141         /* Magic or small NVRAM at 0x400 */
0142         if ((buf[0x4e0 / 4] == CFE_MAGIC && buf[0x4e4 / 4] == CFE_MAGIC) ||
0143             (buf[0x400 / 4] == NVRAM_HEADER)) {
0144             bcm47xxpart_add_part(&parts[curr_part++], "boot",
0145                          offset, MTD_WRITEABLE);
0146             continue;
0147         }
0148 
0149         /*
0150          * board_data starts with board_id which differs across boards,
0151          * but we can use 'MPFR' (hopefully) magic at 0x100
0152          */
0153         if (buf[0x100 / 4] == BOARD_DATA_MAGIC) {
0154             bcm47xxpart_add_part(&parts[curr_part++], "board_data",
0155                          offset, MTD_WRITEABLE);
0156             continue;
0157         }
0158 
0159         /* Found on Huawei E970 */
0160         if (buf[0x000 / 4] == FACTORY_MAGIC) {
0161             bcm47xxpart_add_part(&parts[curr_part++], "factory",
0162                          offset, MTD_WRITEABLE);
0163             continue;
0164         }
0165 
0166         /* POT(TOP) */
0167         if (buf[0x000 / 4] == POT_MAGIC1 &&
0168             (buf[0x004 / 4] & 0xFFFF) == POT_MAGIC2) {
0169             bcm47xxpart_add_part(&parts[curr_part++], "POT", offset,
0170                          MTD_WRITEABLE);
0171             continue;
0172         }
0173 
0174         /* ML */
0175         if (buf[0x010 / 4] == ML_MAGIC1 &&
0176             buf[0x014 / 4] == ML_MAGIC2) {
0177             bcm47xxpart_add_part(&parts[curr_part++], "ML", offset,
0178                          MTD_WRITEABLE);
0179             continue;
0180         }
0181 
0182         /* TRX */
0183         if (buf[0x000 / 4] == TRX_MAGIC) {
0184             struct trx_header *trx;
0185             uint32_t last_subpart;
0186             uint32_t trx_size;
0187 
0188             if (trx_num >= ARRAY_SIZE(trx_parts))
0189                 pr_warn("No enough space to store another TRX found at 0x%X\n",
0190                     offset);
0191             else
0192                 trx_parts[trx_num++] = curr_part;
0193             bcm47xxpart_add_part(&parts[curr_part++], "firmware",
0194                          offset, 0);
0195 
0196             /*
0197              * Try to find TRX size. The "length" field isn't fully
0198              * reliable as it could be decreased to make CRC32 cover
0199              * only part of TRX data. It's commonly used as checksum
0200              * can't cover e.g. ever-changing rootfs partition.
0201              * Use offsets as helpers for assuming min TRX size.
0202              */
0203             trx = (struct trx_header *)buf;
0204             last_subpart = max3(trx->offset[0], trx->offset[1],
0205                         trx->offset[2]);
0206             trx_size = max(trx->length, last_subpart + blocksize);
0207 
0208             /*
0209              * Skip the TRX data. Decrease offset by block size as
0210              * the next loop iteration will increase it.
0211              */
0212             offset += roundup(trx_size, blocksize) - blocksize;
0213             continue;
0214         }
0215 
0216         /* Squashfs on devices not using TRX */
0217         if (le32_to_cpu(buf[0x000 / 4]) == SQUASHFS_MAGIC ||
0218             buf[0x000 / 4] == SHSQ_MAGIC) {
0219             bcm47xxpart_add_part(&parts[curr_part++], "rootfs",
0220                          offset, 0);
0221             continue;
0222         }
0223 
0224         /*
0225          * New (ARM?) devices may have NVRAM in some middle block. Last
0226          * block will be checked later, so skip it.
0227          */
0228         if (offset != master->size - blocksize &&
0229             buf[0x000 / 4] == NVRAM_HEADER) {
0230             bcm47xxpart_add_part(&parts[curr_part++], "nvram",
0231                          offset, 0);
0232             continue;
0233         }
0234 
0235         /* Read middle of the block */
0236         err = mtd_read(master, offset + 0x8000, 0x4, &bytes_read,
0237                    (uint8_t *)buf);
0238         if (err && !mtd_is_bitflip(err)) {
0239             pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
0240                    offset + 0x8000, err);
0241             continue;
0242         }
0243 
0244         /* Some devices (ex. WNDR3700v3) don't have a standard 'MPFR' */
0245         if (buf[0x000 / 4] == BOARD_DATA_MAGIC2) {
0246             bcm47xxpart_add_part(&parts[curr_part++], "board_data",
0247                          offset, MTD_WRITEABLE);
0248             continue;
0249         }
0250     }
0251 
0252     /* Look for NVRAM at the end of the last block. */
0253     for (i = 0; i < ARRAY_SIZE(possible_nvram_sizes); i++) {
0254         if (curr_part >= BCM47XXPART_MAX_PARTS) {
0255             pr_warn("Reached maximum number of partitions, scanning stopped!\n");
0256             break;
0257         }
0258 
0259         offset = master->size - possible_nvram_sizes[i];
0260         err = mtd_read(master, offset, 0x4, &bytes_read,
0261                    (uint8_t *)buf);
0262         if (err && !mtd_is_bitflip(err)) {
0263             pr_err("mtd_read error while reading (offset 0x%X): %d\n",
0264                    offset, err);
0265             continue;
0266         }
0267 
0268         /* Standard NVRAM */
0269         if (buf[0] == NVRAM_HEADER) {
0270             bcm47xxpart_add_part(&parts[curr_part++], "nvram",
0271                          master->size - blocksize, 0);
0272             break;
0273         }
0274     }
0275 
0276     kfree(buf);
0277 
0278     /*
0279      * Assume that partitions end at the beginning of the one they are
0280      * followed by.
0281      */
0282     for (i = 0; i < curr_part; i++) {
0283         u64 next_part_offset = (i < curr_part - 1) ?
0284                        parts[i + 1].offset : master->size;
0285 
0286         parts[i].size = next_part_offset - parts[i].offset;
0287     }
0288 
0289     /* If there was TRX parse it now */
0290     for (i = 0; i < trx_num; i++) {
0291         struct mtd_partition *trx = &parts[trx_parts[i]];
0292 
0293         if (i == bcm47xxpart_bootpartition())
0294             trx->types = trx_types;
0295         else
0296             trx->name = "failsafe";
0297     }
0298 
0299     *pparts = parts;
0300     return curr_part;
0301 };
0302 
0303 static const struct of_device_id bcm47xxpart_of_match_table[] = {
0304     { .compatible = "brcm,bcm947xx-cfe-partitions" },
0305     {},
0306 };
0307 MODULE_DEVICE_TABLE(of, bcm47xxpart_of_match_table);
0308 
0309 static struct mtd_part_parser bcm47xxpart_mtd_parser = {
0310     .parse_fn = bcm47xxpart_parse,
0311     .name = "bcm47xxpart",
0312     .of_match_table = bcm47xxpart_of_match_table,
0313 };
0314 module_mtd_part_parser(bcm47xxpart_mtd_parser);
0315 
0316 MODULE_LICENSE("GPL");
0317 MODULE_DESCRIPTION("MTD partitioning for BCM47XX flash memories");