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