]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/mtd/bcm47xxpart.c
net: dsa: mv88e6xxx: Enable CMODE config support for 6390X
[karo-tx-linux.git] / drivers / mtd / bcm47xxpart.c
1 /*
2  * BCM47XX MTD partitioning
3  *
4  * Copyright © 2012 Rafał Miłecki <zajec5@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  */
11
12 #include <linux/bcm47xx_nvram.h>
13 #include <linux/module.h>
14 #include <linux/kernel.h>
15 #include <linux/slab.h>
16 #include <linux/mtd/mtd.h>
17 #include <linux/mtd/partitions.h>
18
19 #include <uapi/linux/magic.h>
20
21 /*
22  * NAND flash on Netgear R6250 was verified to contain 15 partitions.
23  * This will result in allocating too big array for some old devices, but the
24  * memory will be freed soon anyway (see mtd_device_parse_register).
25  */
26 #define BCM47XXPART_MAX_PARTS           20
27
28 /*
29  * Amount of bytes we read when analyzing each block of flash memory.
30  * Set it big enough to allow detecting partition and reading important data.
31  */
32 #define BCM47XXPART_BYTES_TO_READ       0x4e8
33
34 /* Magics */
35 #define BOARD_DATA_MAGIC                0x5246504D      /* MPFR */
36 #define BOARD_DATA_MAGIC2               0xBD0D0BBD
37 #define CFE_MAGIC                       0x43464531      /* 1EFC */
38 #define FACTORY_MAGIC                   0x59544346      /* FCTY */
39 #define NVRAM_HEADER                    0x48534C46      /* FLSH */
40 #define POT_MAGIC1                      0x54544f50      /* POTT */
41 #define POT_MAGIC2                      0x504f          /* OP */
42 #define ML_MAGIC1                       0x39685a42
43 #define ML_MAGIC2                       0x26594131
44 #define TRX_MAGIC                       0x30524448
45 #define SHSQ_MAGIC                      0x71736873      /* shsq (weird ZTE H218N endianness) */
46 #define UBI_EC_MAGIC                    0x23494255      /* UBI# */
47
48 struct trx_header {
49         uint32_t magic;
50         uint32_t length;
51         uint32_t crc32;
52         uint16_t flags;
53         uint16_t version;
54         uint32_t offset[3];
55 } __packed;
56
57 static void bcm47xxpart_add_part(struct mtd_partition *part, const char *name,
58                                  u64 offset, uint32_t mask_flags)
59 {
60         part->name = name;
61         part->offset = offset;
62         part->mask_flags = mask_flags;
63 }
64
65 static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master,
66                                                   size_t offset)
67 {
68         uint32_t buf;
69         size_t bytes_read;
70         int err;
71
72         err  = mtd_read(master, offset, sizeof(buf), &bytes_read,
73                         (uint8_t *)&buf);
74         if (err && !mtd_is_bitflip(err)) {
75                 pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
76                         offset, err);
77                 goto out_default;
78         }
79
80         if (buf == UBI_EC_MAGIC)
81                 return "ubi";
82
83 out_default:
84         return "rootfs";
85 }
86
87 static int bcm47xxpart_parse_trx(struct mtd_info *master,
88                                  struct mtd_partition *trx,
89                                  struct mtd_partition *parts,
90                                  size_t parts_len)
91 {
92         struct trx_header header;
93         size_t bytes_read;
94         int curr_part = 0;
95         int i, err;
96
97         if (parts_len < 3) {
98                 pr_warn("No enough space to add TRX partitions!\n");
99                 return -ENOMEM;
100         }
101
102         err = mtd_read(master, trx->offset, sizeof(header), &bytes_read,
103                        (uint8_t *)&header);
104         if (err && !mtd_is_bitflip(err)) {
105                 pr_err("mtd_read error while reading TRX header: %d\n", err);
106                 return err;
107         }
108
109         i = 0;
110
111         /* We have LZMA loader if offset[2] points to sth */
112         if (header.offset[2]) {
113                 bcm47xxpart_add_part(&parts[curr_part++], "loader",
114                                      trx->offset + header.offset[i], 0);
115                 i++;
116         }
117
118         if (header.offset[i]) {
119                 bcm47xxpart_add_part(&parts[curr_part++], "linux",
120                                      trx->offset + header.offset[i], 0);
121                 i++;
122         }
123
124         if (header.offset[i]) {
125                 size_t offset = trx->offset + header.offset[i];
126                 const char *name = bcm47xxpart_trx_data_part_name(master,
127                                                                   offset);
128
129                 bcm47xxpart_add_part(&parts[curr_part++], name, offset, 0);
130                 i++;
131         }
132
133         /*
134          * Assume that every partition ends at the beginning of the one it is
135          * followed by.
136          */
137         for (i = 0; i < curr_part; i++) {
138                 u64 next_part_offset = (i < curr_part - 1) ?
139                                         parts[i + 1].offset :
140                                         trx->offset + trx->size;
141
142                 parts[i].size = next_part_offset - parts[i].offset;
143         }
144
145         return curr_part;
146 }
147
148 /**
149  * bcm47xxpart_bootpartition - gets index of TRX partition used by bootloader
150  *
151  * Some devices may have more than one TRX partition. In such case one of them
152  * is the main one and another a failsafe one. Bootloader may fallback to the
153  * failsafe firmware if it detects corruption of the main image.
154  *
155  * This function provides info about currently used TRX partition. It's the one
156  * containing kernel started by the bootloader.
157  */
158 static int bcm47xxpart_bootpartition(void)
159 {
160         char buf[4];
161         int bootpartition;
162
163         /* Check CFE environment variable */
164         if (bcm47xx_nvram_getenv("bootpartition", buf, sizeof(buf)) > 0) {
165                 if (!kstrtoint(buf, 0, &bootpartition))
166                         return bootpartition;
167         }
168
169         return 0;
170 }
171
172 static int bcm47xxpart_parse(struct mtd_info *master,
173                              const struct mtd_partition **pparts,
174                              struct mtd_part_parser_data *data)
175 {
176         struct mtd_partition *parts;
177         uint8_t i, curr_part = 0;
178         uint32_t *buf;
179         size_t bytes_read;
180         uint32_t offset;
181         uint32_t blocksize = master->erasesize;
182         int trx_parts[2]; /* Array with indexes of TRX partitions */
183         int trx_num = 0; /* Number of found TRX partitions */
184         int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
185         int err;
186
187         /*
188          * Some really old flashes (like AT45DB*) had smaller erasesize-s, but
189          * partitions were aligned to at least 0x1000 anyway.
190          */
191         if (blocksize < 0x1000)
192                 blocksize = 0x1000;
193
194         /* Alloc */
195         parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS,
196                         GFP_KERNEL);
197         if (!parts)
198                 return -ENOMEM;
199
200         buf = kzalloc(BCM47XXPART_BYTES_TO_READ, GFP_KERNEL);
201         if (!buf) {
202                 kfree(parts);
203                 return -ENOMEM;
204         }
205
206         /* Parse block by block looking for magics */
207         for (offset = 0; offset <= master->size - blocksize;
208              offset += blocksize) {
209                 /* Nothing more in higher memory on BCM47XX (MIPS) */
210                 if (IS_ENABLED(CONFIG_BCM47XX) && offset >= 0x2000000)
211                         break;
212
213                 if (curr_part >= BCM47XXPART_MAX_PARTS) {
214                         pr_warn("Reached maximum number of partitions, scanning stopped!\n");
215                         break;
216                 }
217
218                 /* Read beginning of the block */
219                 err = mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ,
220                                &bytes_read, (uint8_t *)buf);
221                 if (err && !mtd_is_bitflip(err)) {
222                         pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
223                                offset, err);
224                         continue;
225                 }
226
227                 /* Magic or small NVRAM at 0x400 */
228                 if ((buf[0x4e0 / 4] == CFE_MAGIC && buf[0x4e4 / 4] == CFE_MAGIC) ||
229                     (buf[0x400 / 4] == NVRAM_HEADER)) {
230                         bcm47xxpart_add_part(&parts[curr_part++], "boot",
231                                              offset, MTD_WRITEABLE);
232                         continue;
233                 }
234
235                 /*
236                  * board_data starts with board_id which differs across boards,
237                  * but we can use 'MPFR' (hopefully) magic at 0x100
238                  */
239                 if (buf[0x100 / 4] == BOARD_DATA_MAGIC) {
240                         bcm47xxpart_add_part(&parts[curr_part++], "board_data",
241                                              offset, MTD_WRITEABLE);
242                         continue;
243                 }
244
245                 /* Found on Huawei E970 */
246                 if (buf[0x000 / 4] == FACTORY_MAGIC) {
247                         bcm47xxpart_add_part(&parts[curr_part++], "factory",
248                                              offset, MTD_WRITEABLE);
249                         continue;
250                 }
251
252                 /* POT(TOP) */
253                 if (buf[0x000 / 4] == POT_MAGIC1 &&
254                     (buf[0x004 / 4] & 0xFFFF) == POT_MAGIC2) {
255                         bcm47xxpart_add_part(&parts[curr_part++], "POT", offset,
256                                              MTD_WRITEABLE);
257                         continue;
258                 }
259
260                 /* ML */
261                 if (buf[0x010 / 4] == ML_MAGIC1 &&
262                     buf[0x014 / 4] == ML_MAGIC2) {
263                         bcm47xxpart_add_part(&parts[curr_part++], "ML", offset,
264                                              MTD_WRITEABLE);
265                         continue;
266                 }
267
268                 /* TRX */
269                 if (buf[0x000 / 4] == TRX_MAGIC) {
270                         struct trx_header *trx;
271
272                         if (trx_num >= ARRAY_SIZE(trx_parts))
273                                 pr_warn("No enough space to store another TRX found at 0x%X\n",
274                                         offset);
275                         else
276                                 trx_parts[trx_num++] = curr_part;
277                         bcm47xxpart_add_part(&parts[curr_part++], "firmware",
278                                              offset, 0);
279
280                         /* Jump to the end of TRX */
281                         trx = (struct trx_header *)buf;
282                         offset = roundup(offset + trx->length, blocksize);
283                         /* Next loop iteration will increase the offset */
284                         offset -= blocksize;
285                         continue;
286                 }
287
288                 /* Squashfs on devices not using TRX */
289                 if (le32_to_cpu(buf[0x000 / 4]) == SQUASHFS_MAGIC ||
290                     buf[0x000 / 4] == SHSQ_MAGIC) {
291                         bcm47xxpart_add_part(&parts[curr_part++], "rootfs",
292                                              offset, 0);
293                         continue;
294                 }
295
296                 /*
297                  * New (ARM?) devices may have NVRAM in some middle block. Last
298                  * block will be checked later, so skip it.
299                  */
300                 if (offset != master->size - blocksize &&
301                     buf[0x000 / 4] == NVRAM_HEADER) {
302                         bcm47xxpart_add_part(&parts[curr_part++], "nvram",
303                                              offset, 0);
304                         continue;
305                 }
306
307                 /* Read middle of the block */
308                 err = mtd_read(master, offset + 0x8000, 0x4, &bytes_read,
309                                (uint8_t *)buf);
310                 if (err && !mtd_is_bitflip(err)) {
311                         pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
312                                offset, err);
313                         continue;
314                 }
315
316                 /* Some devices (ex. WNDR3700v3) don't have a standard 'MPFR' */
317                 if (buf[0x000 / 4] == BOARD_DATA_MAGIC2) {
318                         bcm47xxpart_add_part(&parts[curr_part++], "board_data",
319                                              offset, MTD_WRITEABLE);
320                         continue;
321                 }
322         }
323
324         /* Look for NVRAM at the end of the last block. */
325         for (i = 0; i < ARRAY_SIZE(possible_nvram_sizes); i++) {
326                 if (curr_part >= BCM47XXPART_MAX_PARTS) {
327                         pr_warn("Reached maximum number of partitions, scanning stopped!\n");
328                         break;
329                 }
330
331                 offset = master->size - possible_nvram_sizes[i];
332                 err = mtd_read(master, offset, 0x4, &bytes_read,
333                                (uint8_t *)buf);
334                 if (err && !mtd_is_bitflip(err)) {
335                         pr_err("mtd_read error while reading (offset 0x%X): %d\n",
336                                offset, err);
337                         continue;
338                 }
339
340                 /* Standard NVRAM */
341                 if (buf[0] == NVRAM_HEADER) {
342                         bcm47xxpart_add_part(&parts[curr_part++], "nvram",
343                                              master->size - blocksize, 0);
344                         break;
345                 }
346         }
347
348         kfree(buf);
349
350         /*
351          * Assume that partitions end at the beginning of the one they are
352          * followed by.
353          */
354         for (i = 0; i < curr_part; i++) {
355                 u64 next_part_offset = (i < curr_part - 1) ?
356                                        parts[i + 1].offset : master->size;
357
358                 parts[i].size = next_part_offset - parts[i].offset;
359         }
360
361         /* If there was TRX parse it now */
362         for (i = 0; i < trx_num; i++) {
363                 struct mtd_partition *trx = &parts[trx_parts[i]];
364
365                 if (i == bcm47xxpart_bootpartition()) {
366                         int num_parts;
367
368                         num_parts = bcm47xxpart_parse_trx(master, trx,
369                                                           parts + curr_part,
370                                                           BCM47XXPART_MAX_PARTS - curr_part);
371                         if (num_parts > 0)
372                                 curr_part += num_parts;
373                 } else {
374                         trx->name = "failsafe";
375                 }
376         }
377
378         *pparts = parts;
379         return curr_part;
380 };
381
382 static struct mtd_part_parser bcm47xxpart_mtd_parser = {
383         .parse_fn = bcm47xxpart_parse,
384         .name = "bcm47xxpart",
385 };
386 module_mtd_part_parser(bcm47xxpart_mtd_parser);
387
388 MODULE_LICENSE("GPL");
389 MODULE_DESCRIPTION("MTD partitioning for BCM47XX flash memories");