0001
0002
0003
0004
0005
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
0019
0020
0021
0022 #define BCM47XXPART_MAX_PARTS 20
0023
0024
0025
0026
0027
0028 #define BCM47XXPART_BYTES_TO_READ 0x4e8
0029
0030
0031 #define BOARD_DATA_MAGIC 0x5246504D
0032 #define BOARD_DATA_MAGIC2 0xBD0D0BBD
0033 #define CFE_MAGIC 0x43464531
0034 #define FACTORY_MAGIC 0x59544346
0035 #define NVRAM_HEADER 0x48534C46
0036 #define POT_MAGIC1 0x54544f50
0037 #define POT_MAGIC2 0x504f
0038 #define ML_MAGIC1 0x39685a42
0039 #define ML_MAGIC2 0x26594131
0040 #define TRX_MAGIC 0x30524448
0041 #define SHSQ_MAGIC 0x71736873
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
0064
0065
0066
0067
0068
0069
0070
0071
0072 static int bcm47xxpart_bootpartition(void)
0073 {
0074 char buf[4];
0075 int bootpartition;
0076
0077
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];
0097 int trx_num = 0;
0098 int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
0099 int err;
0100
0101
0102
0103
0104
0105 if (blocksize < 0x1000)
0106 blocksize = 0x1000;
0107
0108
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
0121 for (offset = 0; offset <= master->size - blocksize;
0122 offset += blocksize) {
0123
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
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
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
0151
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
0160 if (buf[0x000 / 4] == FACTORY_MAGIC) {
0161 bcm47xxpart_add_part(&parts[curr_part++], "factory",
0162 offset, MTD_WRITEABLE);
0163 continue;
0164 }
0165
0166
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
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
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
0198
0199
0200
0201
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
0210
0211
0212 offset += roundup(trx_size, blocksize) - blocksize;
0213 continue;
0214 }
0215
0216
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
0226
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
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
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
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
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
0280
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
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");