Blame view

drivers/mtd/bcm47xxpart.c 8.09 KB
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
1
2
3
4
5
6
7
8
9
10
  /*
   * BCM47XX MTD partitioning
   *
   * Copyright © 2012 Rafał Miłecki <zajec5@gmail.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   */
89a0d9a9f   Rafał Miłecki   mtd: bcm47xxpart:...
11
  #include <linux/bcm47xx_nvram.h>
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
12
13
14
15
16
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/slab.h>
  #include <linux/mtd/mtd.h>
  #include <linux/mtd/partitions.h>
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
17

0b56d2d45   Rafał Miłecki   mtd: bcm47xxpart:...
18
  #include <uapi/linux/magic.h>
59af5c7ac   Rafał Miłecki   mtd: bcm47xxpart:...
19
20
21
22
23
24
  /*
   * NAND flash on Netgear R6250 was verified to contain 15 partitions.
   * This will result in allocating too big array for some old devices, but the
   * memory will be freed soon anyway (see mtd_device_parse_register).
   */
  #define BCM47XXPART_MAX_PARTS		20
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
25

5ca1088f1   Rafał Miłecki   Revert "mtd: bcm4...
26
27
28
29
  /*
   * Amount of bytes we read when analyzing each block of flash memory.
   * Set it big enough to allow detecting partition and reading important data.
   */
4f8aaf722   Rafał Miłecki   mtd: bcm47xxpart:...
30
  #define BCM47XXPART_BYTES_TO_READ	0x4e8
5ca1088f1   Rafał Miłecki   Revert "mtd: bcm4...
31

3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
32
33
  /* Magics */
  #define BOARD_DATA_MAGIC		0x5246504D	/* MPFR */
f0501e81f   Rafał Miłecki   mtd: bcm47xxpart:...
34
  #define BOARD_DATA_MAGIC2		0xBD0D0BBD
4f8aaf722   Rafał Miłecki   mtd: bcm47xxpart:...
35
  #define CFE_MAGIC			0x43464531	/* 1EFC */
33094c736   Rafał Miłecki   mtd: bcm47xxpart:...
36
  #define FACTORY_MAGIC			0x59544346	/* FCTY */
9e3afa5f5   Rafał Miłecki   mtd: bcm47xxpart:...
37
  #define NVRAM_HEADER			0x48534C46	/* FLSH */
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
38
39
40
41
42
  #define POT_MAGIC1			0x54544f50	/* POTT */
  #define POT_MAGIC2			0x504f		/* OP */
  #define ML_MAGIC1			0x39685a42
  #define ML_MAGIC2			0x26594131
  #define TRX_MAGIC			0x30524448
0b56d2d45   Rafał Miłecki   mtd: bcm47xxpart:...
43
  #define SHSQ_MAGIC			0x71736873	/* shsq (weird ZTE H218N endianness) */
99352afe8   Rafał Miłecki   mtd: extract TRX ...
44
45
  
  static const char * const trx_types[] = { "trx", NULL };
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
46
47
48
49
50
51
52
53
54
  
  struct trx_header {
  	uint32_t magic;
  	uint32_t length;
  	uint32_t crc32;
  	uint16_t flags;
  	uint16_t version;
  	uint32_t offset[3];
  } __packed;
bd10c26a4   Rafał Miłecki   mtd: bcm47xxpart:...
55
  static void bcm47xxpart_add_part(struct mtd_partition *part, const char *name,
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
56
57
58
59
60
61
  				 u64 offset, uint32_t mask_flags)
  {
  	part->name = name;
  	part->offset = offset;
  	part->mask_flags = mask_flags;
  }
89a0d9a9f   Rafał Miłecki   mtd: bcm47xxpart:...
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  /**
   * bcm47xxpart_bootpartition - gets index of TRX partition used by bootloader
   *
   * Some devices may have more than one TRX partition. In such case one of them
   * is the main one and another a failsafe one. Bootloader may fallback to the
   * failsafe firmware if it detects corruption of the main image.
   *
   * This function provides info about currently used TRX partition. It's the one
   * containing kernel started by the bootloader.
   */
  static int bcm47xxpart_bootpartition(void)
  {
  	char buf[4];
  	int bootpartition;
  
  	/* Check CFE environment variable */
  	if (bcm47xx_nvram_getenv("bootpartition", buf, sizeof(buf)) > 0) {
  		if (!kstrtoint(buf, 0, &bootpartition))
  			return bootpartition;
  	}
  
  	return 0;
  }
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
85
  static int bcm47xxpart_parse(struct mtd_info *master,
b9adf469f   Brian Norris   mtd: partitions: ...
86
  			     const struct mtd_partition **pparts,
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
87
88
89
90
91
92
93
  			     struct mtd_part_parser_data *data)
  {
  	struct mtd_partition *parts;
  	uint8_t i, curr_part = 0;
  	uint32_t *buf;
  	size_t bytes_read;
  	uint32_t offset;
25bad1d3c   Hauke Mehrtens   mtd: bcm47xxpart:...
94
  	uint32_t blocksize = master->erasesize;
89a0d9a9f   Rafał Miłecki   mtd: bcm47xxpart:...
95
96
  	int trx_parts[2]; /* Array with indexes of TRX partitions */
  	int trx_num = 0; /* Number of found TRX partitions */
91d542f4d   Rafał Miłecki   mtd: bcm47xxpart:...
97
  	int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
36bcc0c9c   Rafał Miłecki   mtd: bcm47xxpart:...
98
  	int err;
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
99

16bd87b3a   Rafał Miłecki   mtd: bcm47xxpart:...
100
101
102
103
104
105
  	/*
  	 * Some really old flashes (like AT45DB*) had smaller erasesize-s, but
  	 * partitions were aligned to at least 0x1000 anyway.
  	 */
  	if (blocksize < 0x1000)
  		blocksize = 0x1000;
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
106
107
108
109
  
  	/* Alloc */
  	parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS,
  			GFP_KERNEL);
99b1d1887   Hauke Mehrtens   mtd: bcm47xxpart:...
110
111
  	if (!parts)
  		return -ENOMEM;
5ca1088f1   Rafał Miłecki   Revert "mtd: bcm4...
112
  	buf = kzalloc(BCM47XXPART_BYTES_TO_READ, GFP_KERNEL);
99b1d1887   Hauke Mehrtens   mtd: bcm47xxpart:...
113
114
115
116
  	if (!buf) {
  		kfree(parts);
  		return -ENOMEM;
  	}
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
117
118
119
120
  
  	/* Parse block by block looking for magics */
  	for (offset = 0; offset <= master->size - blocksize;
  	     offset += blocksize) {
2a36a5c30   Rafał Miłecki   mtd: bcm47xxpart:...
121
  		/* Nothing more in higher memory on BCM47XX (MIPS) */
97f2645f3   Masahiro Yamada   tree-wide: replac...
122
  		if (IS_ENABLED(CONFIG_BCM47XX) && offset >= 0x2000000)
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
123
  			break;
00b79860e   Rafał Miłecki   mtd: bcm47xxpart:...
124
  		if (curr_part >= BCM47XXPART_MAX_PARTS) {
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
125
126
127
128
129
130
  			pr_warn("Reached maximum number of partitions, scanning stopped!
  ");
  			break;
  		}
  
  		/* Read beginning of the block */
36bcc0c9c   Rafał Miłecki   mtd: bcm47xxpart:...
131
132
133
134
135
136
  		err = mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ,
  			       &bytes_read, (uint8_t *)buf);
  		if (err && !mtd_is_bitflip(err)) {
  			pr_err("mtd_read error while parsing (offset: 0x%X): %d
  ",
  			       offset, err);
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
137
138
  			continue;
  		}
4f8aaf722   Rafał Miłecki   mtd: bcm47xxpart:...
139
140
141
  		/* Magic or small NVRAM at 0x400 */
  		if ((buf[0x4e0 / 4] == CFE_MAGIC && buf[0x4e4 / 4] == CFE_MAGIC) ||
  		    (buf[0x400 / 4] == NVRAM_HEADER)) {
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
142
143
144
145
  			bcm47xxpart_add_part(&parts[curr_part++], "boot",
  					     offset, MTD_WRITEABLE);
  			continue;
  		}
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
146
147
148
149
150
151
152
153
154
  		/*
  		 * board_data starts with board_id which differs across boards,
  		 * but we can use 'MPFR' (hopefully) magic at 0x100
  		 */
  		if (buf[0x100 / 4] == BOARD_DATA_MAGIC) {
  			bcm47xxpart_add_part(&parts[curr_part++], "board_data",
  					     offset, MTD_WRITEABLE);
  			continue;
  		}
33094c736   Rafał Miłecki   mtd: bcm47xxpart:...
155
156
157
158
159
160
  		/* Found on Huawei E970 */
  		if (buf[0x000 / 4] == FACTORY_MAGIC) {
  			bcm47xxpart_add_part(&parts[curr_part++], "factory",
  					     offset, MTD_WRITEABLE);
  			continue;
  		}
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  		/* POT(TOP) */
  		if (buf[0x000 / 4] == POT_MAGIC1 &&
  		    (buf[0x004 / 4] & 0xFFFF) == POT_MAGIC2) {
  			bcm47xxpart_add_part(&parts[curr_part++], "POT", offset,
  					     MTD_WRITEABLE);
  			continue;
  		}
  
  		/* ML */
  		if (buf[0x010 / 4] == ML_MAGIC1 &&
  		    buf[0x014 / 4] == ML_MAGIC2) {
  			bcm47xxpart_add_part(&parts[curr_part++], "ML", offset,
  					     MTD_WRITEABLE);
  			continue;
  		}
  
  		/* TRX */
  		if (buf[0x000 / 4] == TRX_MAGIC) {
b522d7b0e   Rafał Miłecki   mtd: bcm47xxpart:...
179
  			struct trx_header *trx;
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
180

89a0d9a9f   Rafał Miłecki   mtd: bcm47xxpart:...
181
182
183
184
185
186
  			if (trx_num >= ARRAY_SIZE(trx_parts))
  				pr_warn("No enough space to store another TRX found at 0x%X
  ",
  					offset);
  			else
  				trx_parts[trx_num++] = curr_part;
396afe553   Rafał Miłecki   mtd: bcm47xxpart:...
187
188
  			bcm47xxpart_add_part(&parts[curr_part++], "firmware",
  					     offset, 0);
bd5d21310   Rafał Miłecki   mtd: bcm47xxpart:...
189
  			/* Jump to the end of TRX */
b522d7b0e   Rafał Miłecki   mtd: bcm47xxpart:...
190
  			trx = (struct trx_header *)buf;
bd5d21310   Rafał Miłecki   mtd: bcm47xxpart:...
191
192
193
  			offset = roundup(offset + trx->length, blocksize);
  			/* Next loop iteration will increase the offset */
  			offset -= blocksize;
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
194
195
  			continue;
  		}
020c6bcfb   Rafał Miłecki   mtd: bcm47xxpart:...
196
197
  
  		/* Squashfs on devices not using TRX */
0b56d2d45   Rafał Miłecki   mtd: bcm47xxpart:...
198
199
  		if (le32_to_cpu(buf[0x000 / 4]) == SQUASHFS_MAGIC ||
  		    buf[0x000 / 4] == SHSQ_MAGIC) {
020c6bcfb   Rafał Miłecki   mtd: bcm47xxpart:...
200
201
202
  			bcm47xxpart_add_part(&parts[curr_part++], "rootfs",
  					     offset, 0);
  			continue;
024629fdc   Rafał Miłecki   mtd: bcm47xxpart:...
203
204
205
206
207
208
209
210
211
212
213
  		}
  
  		/*
  		 * New (ARM?) devices may have NVRAM in some middle block. Last
  		 * block will be checked later, so skip it.
  		 */
  		if (offset != master->size - blocksize &&
  		    buf[0x000 / 4] == NVRAM_HEADER) {
  			bcm47xxpart_add_part(&parts[curr_part++], "nvram",
  					     offset, 0);
  			continue;
020c6bcfb   Rafał Miłecki   mtd: bcm47xxpart:...
214
  		}
f0501e81f   Rafał Miłecki   mtd: bcm47xxpart:...
215
216
  
  		/* Read middle of the block */
36bcc0c9c   Rafał Miłecki   mtd: bcm47xxpart:...
217
218
219
220
221
222
  		err = mtd_read(master, offset + 0x8000, 0x4, &bytes_read,
  			       (uint8_t *)buf);
  		if (err && !mtd_is_bitflip(err)) {
  			pr_err("mtd_read error while parsing (offset: 0x%X): %d
  ",
  			       offset, err);
f0501e81f   Rafał Miłecki   mtd: bcm47xxpart:...
223
224
225
226
227
228
229
230
231
  			continue;
  		}
  
  		/* Some devices (ex. WNDR3700v3) don't have a standard 'MPFR' */
  		if (buf[0x000 / 4] == BOARD_DATA_MAGIC2) {
  			bcm47xxpart_add_part(&parts[curr_part++], "board_data",
  					     offset, MTD_WRITEABLE);
  			continue;
  		}
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
232
  	}
91d542f4d   Rafał Miłecki   mtd: bcm47xxpart:...
233
234
235
  
  	/* Look for NVRAM at the end of the last block. */
  	for (i = 0; i < ARRAY_SIZE(possible_nvram_sizes); i++) {
00b79860e   Rafał Miłecki   mtd: bcm47xxpart:...
236
  		if (curr_part >= BCM47XXPART_MAX_PARTS) {
91d542f4d   Rafał Miłecki   mtd: bcm47xxpart:...
237
238
239
240
241
242
  			pr_warn("Reached maximum number of partitions, scanning stopped!
  ");
  			break;
  		}
  
  		offset = master->size - possible_nvram_sizes[i];
36bcc0c9c   Rafał Miłecki   mtd: bcm47xxpart:...
243
244
245
246
247
248
  		err = mtd_read(master, offset, 0x4, &bytes_read,
  			       (uint8_t *)buf);
  		if (err && !mtd_is_bitflip(err)) {
  			pr_err("mtd_read error while reading (offset 0x%X): %d
  ",
  			       offset, err);
91d542f4d   Rafał Miłecki   mtd: bcm47xxpart:...
249
250
251
252
253
254
255
256
257
258
  			continue;
  		}
  
  		/* Standard NVRAM */
  		if (buf[0] == NVRAM_HEADER) {
  			bcm47xxpart_add_part(&parts[curr_part++], "nvram",
  					     master->size - blocksize, 0);
  			break;
  		}
  	}
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
259
260
261
262
263
264
  	kfree(buf);
  
  	/*
  	 * Assume that partitions end at the beginning of the one they are
  	 * followed by.
  	 */
648bdbee5   Rafał Miłecki   mtd: bcm47xxpart:...
265
266
267
268
269
  	for (i = 0; i < curr_part; i++) {
  		u64 next_part_offset = (i < curr_part - 1) ?
  				       parts[i + 1].offset : master->size;
  
  		parts[i].size = next_part_offset - parts[i].offset;
b522d7b0e   Rafał Miłecki   mtd: bcm47xxpart:...
270
271
272
  	}
  
  	/* If there was TRX parse it now */
89a0d9a9f   Rafał Miłecki   mtd: bcm47xxpart:...
273
274
  	for (i = 0; i < trx_num; i++) {
  		struct mtd_partition *trx = &parts[trx_parts[i]];
99352afe8   Rafał Miłecki   mtd: extract TRX ...
275
276
277
  		if (i == bcm47xxpart_bootpartition())
  			trx->types = trx_types;
  		else
89a0d9a9f   Rafał Miłecki   mtd: bcm47xxpart:...
278
  			trx->name = "failsafe";
648bdbee5   Rafał Miłecki   mtd: bcm47xxpart:...
279
  	}
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
280
281
282
283
284
285
  
  	*pparts = parts;
  	return curr_part;
  };
  
  static struct mtd_part_parser bcm47xxpart_mtd_parser = {
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
286
287
288
  	.parse_fn = bcm47xxpart_parse,
  	.name = "bcm47xxpart",
  };
b8f70badb   Brian Norris   mtd: kill off MTD...
289
  module_mtd_part_parser(bcm47xxpart_mtd_parser);
3cf7f1314   Rafał Miłecki   mtd: bcm47part dr...
290
291
292
  
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("MTD partitioning for BCM47XX flash memories");