1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2014
4  * Heiko Schocher, DENX Software Engineering, hs@denx.de.
5  */
6 #include <env.h>
7 #include <log.h>
8 #include <malloc.h>
9 #include <dm/device.h>
10 #include <dm/uclass-internal.h>
11 #include <dm/uclass.h>
12 #include <linux/err.h>
13 #include <linux/mtd/mtd.h>
14 #include <linux/mtd/partitions.h>
15 #include <asm/global_data.h>
16 #include <mtd.h>
17 
18 #define MTD_NAME_MAX_LEN 20
19 
20 void board_mtdparts_default(const char **mtdids, const char **mtdparts);
21 
get_mtdids(void)22 static const char *get_mtdids(void)
23 {
24 	__maybe_unused const char *mtdparts = NULL;
25 	const char *mtdids = env_get("mtdids");
26 
27 	if (mtdids)
28 		return mtdids;
29 
30 #if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
31 	board_mtdparts_default(&mtdids, &mtdparts);
32 #elif defined(CONFIG_MTDIDS_DEFAULT)
33 	mtdids = CONFIG_MTDIDS_DEFAULT;
34 #endif
35 
36 	if (mtdids)
37 		env_set("mtdids", mtdids);
38 
39 	return mtdids;
40 }
41 
42 /**
43  * mtd_search_alternate_name - Search an alternate name for @mtdname thanks to
44  *                             the mtdids legacy environment variable.
45  *
46  * The mtdids string is a list of comma-separated 'dev_id=mtd_id' tupples.
47  * Check if one of the mtd_id matches mtdname, in this case save dev_id in
48  * altname.
49  *
50  * @mtdname: Current MTD device name
51  * @altname: Alternate name to return
52  * @max_len: Length of the alternate name buffer
53  *
54  * Return: 0 on success, an error otherwise.
55  */
mtd_search_alternate_name(const char * mtdname,char * altname,unsigned int max_len)56 int mtd_search_alternate_name(const char *mtdname, char *altname,
57 			      unsigned int max_len)
58 {
59 	const char *mtdids, *equal, *comma, *dev_id, *mtd_id;
60 	int dev_id_len, mtd_id_len;
61 
62 	mtdids = get_mtdids();
63 	if (!mtdids)
64 		return -EINVAL;
65 
66 	do {
67 		/* Find the '=' sign */
68 		dev_id = mtdids;
69 		equal = strchr(dev_id, '=');
70 		if (!equal)
71 			break;
72 		dev_id_len = equal - mtdids;
73 		mtd_id = equal + 1;
74 
75 		/* Find the end of the tupple */
76 		comma = strchr(mtdids, ',');
77 		if (comma)
78 			mtd_id_len = comma - mtd_id;
79 		else
80 			mtd_id_len = &mtdids[strlen(mtdids)] - mtd_id + 1;
81 
82 		if (!dev_id_len || !mtd_id_len)
83 			return -EINVAL;
84 
85 		if (dev_id_len + 1 > max_len)
86 			continue;
87 
88 		/* Compare the name we search with the current mtd_id */
89 		if (!strncmp(mtdname, mtd_id, mtd_id_len)) {
90 			strncpy(altname, dev_id, dev_id_len);
91 			altname[dev_id_len] = 0;
92 
93 			return 0;
94 		}
95 
96 		/* Go to the next tupple */
97 		mtdids = comma + 1;
98 	} while (comma);
99 
100 	return -EINVAL;
101 }
102 
103 #if IS_ENABLED(CONFIG_DM_MTD)
mtd_probe_uclass_mtd_devs(void)104 static void mtd_probe_uclass_mtd_devs(void)
105 {
106 	struct udevice *dev;
107 
108 	uclass_foreach_dev_probe(UCLASS_MTD, dev)
109 		;
110 }
111 #else
mtd_probe_uclass_mtd_devs(void)112 static void mtd_probe_uclass_mtd_devs(void) { }
113 #endif
114 
115 #if IS_ENABLED(CONFIG_DM_SPI_FLASH) && IS_ENABLED(CONFIG_SPI_FLASH_MTD)
mtd_probe_uclass_spi_nor_devs(void)116 static void mtd_probe_uclass_spi_nor_devs(void)
117 {
118 	struct udevice *dev;
119 
120 	uclass_foreach_dev_probe(UCLASS_SPI_FLASH, dev)
121 		;
122 }
123 #else
mtd_probe_uclass_spi_nor_devs(void)124 static void mtd_probe_uclass_spi_nor_devs(void) { }
125 #endif
126 
127 #if defined(CONFIG_MTD_PARTITIONS)
128 
129 #define MTDPARTS_MAXLEN         512
130 
get_mtdparts(void)131 static const char *get_mtdparts(void)
132 {
133 	__maybe_unused const char *mtdids = NULL;
134 	static char tmp_parts[MTDPARTS_MAXLEN];
135 	const char *mtdparts = NULL;
136 
137 	if (gd->flags & GD_FLG_ENV_READY)
138 		mtdparts = env_get("mtdparts");
139 	else if (env_get_f("mtdparts", tmp_parts, sizeof(tmp_parts)) != -1)
140 		mtdparts = tmp_parts;
141 
142 	if (mtdparts)
143 		return mtdparts;
144 
145 #if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
146 	board_mtdparts_default(&mtdids, &mtdparts);
147 #elif defined(CONFIG_MTDPARTS_DEFAULT)
148 	mtdparts = CONFIG_MTDPARTS_DEFAULT;
149 #endif
150 
151 	if (mtdparts)
152 		env_set("mtdparts", mtdparts);
153 
154 	return mtdparts;
155 }
156 
mtd_del_parts(struct mtd_info * mtd,bool quiet)157 static int mtd_del_parts(struct mtd_info *mtd, bool quiet)
158 {
159 	int ret;
160 
161 	if (!mtd_has_partitions(mtd))
162 		return 0;
163 
164 	/* do not delete partitions if they are in use. */
165 	if (mtd_partitions_used(mtd)) {
166 		if (!quiet)
167 			printf("\"%s\" partitions still in use, can't delete them\n",
168 			       mtd->name);
169 		return -EACCES;
170 	}
171 
172 	ret = del_mtd_partitions(mtd);
173 	if (ret)
174 		return ret;
175 
176 	return 1;
177 }
178 
179 static bool mtd_del_all_parts_failed;
180 
mtd_del_all_parts(void)181 static void mtd_del_all_parts(void)
182 {
183 	struct mtd_info *mtd;
184 	int ret = 0;
185 
186 	mtd_del_all_parts_failed = false;
187 
188 	/*
189 	 * It is not safe to remove entries from the mtd_for_each_device loop
190 	 * as it uses idr indexes and the partitions removal is done in bulk
191 	 * (all partitions of one device at the same time), so break and
192 	 * iterate from start each time a new partition is found and deleted.
193 	 */
194 	do {
195 		mtd_for_each_device(mtd) {
196 			ret = mtd_del_parts(mtd, false);
197 			if (ret > 0)
198 				break;
199 			else if (ret < 0)
200 				mtd_del_all_parts_failed = true;
201 		}
202 	} while (ret > 0);
203 }
204 
parse_mtdparts(const char * mtdparts,const char * mtdids)205 static int parse_mtdparts(const char *mtdparts, const char *mtdids)
206 {
207 	const char *mtdparts_next;
208 	struct mtd_info *mtd;
209 
210 	/* Start the parsing by ignoring the extra 'mtdparts=' prefix, if any */
211 	if (!strncmp(mtdparts, "mtdparts=", sizeof("mtdparts=") - 1))
212 		mtdparts += 9;
213 
214 	/* For each MTD device in mtdparts */
215 	for (; mtdparts[0] != '\0'; mtdparts = mtdparts_next) {
216 		char mtd_name[MTD_NAME_MAX_LEN], *colon;
217 		struct mtd_partition *parts;
218 		unsigned int mtd_name_len;
219 		int nparts, ret;
220 
221 		mtdparts_next = strchr(mtdparts, ';');
222 		if (!mtdparts_next)
223 			mtdparts_next = mtdparts + strlen(mtdparts);
224 		else
225 			mtdparts_next++;
226 
227 		colon = strchr(mtdparts, ':');
228 		if (colon > mtdparts_next)
229 			colon = NULL;
230 
231 		if (!colon) {
232 			printf("Wrong mtdparts: %s\n", mtdparts);
233 			return -EINVAL;
234 		}
235 
236 		mtd_name_len = (unsigned int)(colon - mtdparts);
237 		if (mtd_name_len + 1 > sizeof(mtd_name)) {
238 			printf("MTD name too long: %s\n", mtdparts);
239 			return -EINVAL;
240 		}
241 
242 		strncpy(mtd_name, mtdparts, mtd_name_len);
243 		mtd_name[mtd_name_len] = '\0';
244 		/* Move the pointer forward (including the ':') */
245 		mtdparts += mtd_name_len + 1;
246 		mtd = get_mtd_device_nm(mtd_name);
247 		if (IS_ERR_OR_NULL(mtd)) {
248 			char linux_name[MTD_NAME_MAX_LEN];
249 
250 			/*
251 			 * The MTD device named "mtd_name" does not exist. Try
252 			 * to find a correspondance with an MTD device having
253 			 * the same type and number as defined in the mtdids.
254 			 */
255 			debug("No device named %s\n", mtd_name);
256 			ret = mtd_search_alternate_name(mtd_name, linux_name,
257 							MTD_NAME_MAX_LEN);
258 			if (!ret)
259 				mtd = get_mtd_device_nm(linux_name);
260 
261 			/*
262 			 * If no device could be found, move the mtdparts
263 			 * pointer forward until the next set of partitions.
264 			 */
265 			if (ret || IS_ERR_OR_NULL(mtd)) {
266 				printf("Could not find a valid device for %s\n",
267 				       mtd_name);
268 				mtdparts = mtdparts_next;
269 				continue;
270 			}
271 		}
272 
273 		/*
274 		 * Call mtd_del_parts() again, even if it's already been called
275 		 * in mtd_del_all_parts(). We need to know if old partitions are
276 		 * still around (because they are still being used by someone),
277 		 * and if they are, we shouldn't create new partitions, so just
278 		 * skip this MTD device and try the next one.
279 		 */
280 		ret = mtd_del_parts(mtd, true);
281 		if (ret < 0)
282 			continue;
283 
284 		/*
285 		 * Parse the MTD device partitions. It will update the mtdparts
286 		 * pointer, create an array of parts (that must be freed), and
287 		 * return the number of partition structures in the array.
288 		 */
289 		ret = mtd_parse_partitions(mtd, &mtdparts, &parts, &nparts);
290 		if (ret) {
291 			printf("Could not parse device %s\n", mtd->name);
292 			put_mtd_device(mtd);
293 			return -EINVAL;
294 		}
295 
296 		if (!nparts)
297 			continue;
298 
299 		/* Create the new MTD partitions */
300 		add_mtd_partitions(mtd, parts, nparts);
301 
302 		/* Free the structures allocated during the parsing */
303 		mtd_free_parsed_partitions(parts, nparts);
304 
305 		put_mtd_device(mtd);
306 	}
307 
308 	return 0;
309 }
310 
mtd_probe_devices(void)311 int mtd_probe_devices(void)
312 {
313 	static char *old_mtdparts;
314 	static char *old_mtdids;
315 	const char *mtdparts = get_mtdparts();
316 	const char *mtdids = get_mtdids();
317 	struct mtd_info *mtd;
318 
319 	mtd_probe_uclass_mtd_devs();
320 	mtd_probe_uclass_spi_nor_devs();
321 
322 	/*
323 	 * Check if mtdparts/mtdids changed, if the MTD dev list was updated
324 	 * or if our previous attempt to delete existing partititions failed.
325 	 * In any of these cases we want to update the partitions, otherwise,
326 	 * everything is up-to-date and we can return 0 directly.
327 	 */
328 	if ((!mtdparts && !old_mtdparts && !mtdids && !old_mtdids) ||
329 	    (mtdparts && old_mtdparts && mtdids && old_mtdids &&
330 	     !mtd_dev_list_updated() && !mtd_del_all_parts_failed &&
331 	     !strcmp(mtdparts, old_mtdparts) &&
332 	     !strcmp(mtdids, old_mtdids)))
333 		return 0;
334 
335 	/* Update the local copy of mtdparts */
336 	free(old_mtdparts);
337 	free(old_mtdids);
338 	old_mtdparts = strdup(mtdparts);
339 	old_mtdids = strdup(mtdids);
340 
341 	/*
342 	 * Remove all old parts. Note that partition removal can fail in case
343 	 * one of the partition is still being used by an MTD user, so this
344 	 * does not guarantee that all old partitions are gone.
345 	 */
346 	mtd_del_all_parts();
347 
348 	/*
349 	 * Call mtd_dev_list_updated() to clear updates generated by our own
350 	 * parts removal loop.
351 	 */
352 	mtd_dev_list_updated();
353 
354 	/* If both mtdparts and mtdids are non-empty, parse */
355 	if (mtdparts && mtdids) {
356 		if (parse_mtdparts(mtdparts, mtdids) < 0)
357 			printf("Failed parsing MTD partitions from mtdparts!\n");
358 	}
359 
360 	/* Fallback to OF partitions */
361 	mtd_for_each_device(mtd) {
362 		if (list_empty(&mtd->partitions)) {
363 			if (add_mtd_partitions_of(mtd) < 0)
364 				printf("Failed parsing MTD %s OF partitions!\n",
365 					mtd->name);
366 		}
367 	}
368 
369 	/*
370 	 * Call mtd_dev_list_updated() to clear updates generated by our own
371 	 * parts registration loop.
372 	 */
373 	mtd_dev_list_updated();
374 
375 	return 0;
376 }
377 #else
mtd_probe_devices(void)378 int mtd_probe_devices(void)
379 {
380 	mtd_probe_uclass_mtd_devs();
381 	mtd_probe_uclass_spi_nor_devs();
382 
383 	return 0;
384 }
385 #endif /* defined(CONFIG_MTD_PARTITIONS) */
386