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 <dm/device.h> 8 #include <dm/uclass-internal.h> 9 #include <jffs2/jffs2.h> /* LEGACY */ 10 #include <linux/mtd/mtd.h> 11 #include <linux/mtd/partitions.h> 12 #include <mtd.h> 13 14 #define MTD_NAME_MAX_LEN 20 15 16 17 /** 18 * mtd_search_alternate_name - Search an alternate name for @mtdname thanks to 19 * the mtdids legacy environment variable. 20 * 21 * The mtdids string is a list of comma-separated 'dev_id=mtd_id' tupples. 22 * Check if one of the mtd_id matches mtdname, in this case save dev_id in 23 * altname. 24 * 25 * @mtdname: Current MTD device name 26 * @altname: Alternate name to return 27 * @max_len: Length of the alternate name buffer 28 * 29 * @return 0 on success, an error otherwise. 30 */ 31 int mtd_search_alternate_name(const char *mtdname, char *altname, 32 unsigned int max_len) 33 { 34 const char *mtdids, *equal, *comma, *dev_id, *mtd_id; 35 int dev_id_len, mtd_id_len; 36 37 mtdids = env_get("mtdids"); 38 if (!mtdids) 39 return -EINVAL; 40 41 do { 42 /* Find the '=' sign */ 43 dev_id = mtdids; 44 equal = strchr(dev_id, '='); 45 if (!equal) 46 break; 47 dev_id_len = equal - mtdids; 48 mtd_id = equal + 1; 49 50 /* Find the end of the tupple */ 51 comma = strchr(mtdids, ','); 52 if (comma) 53 mtd_id_len = comma - mtd_id; 54 else 55 mtd_id_len = &mtdids[strlen(mtdids)] - mtd_id + 1; 56 57 if (!dev_id_len || !mtd_id_len) 58 return -EINVAL; 59 60 if (dev_id_len + 1 > max_len) 61 continue; 62 63 /* Compare the name we search with the current mtd_id */ 64 if (!strncmp(mtdname, mtd_id, mtd_id_len)) { 65 strncpy(altname, dev_id, dev_id_len); 66 altname[dev_id_len] = 0; 67 68 return 0; 69 } 70 71 /* Go to the next tupple */ 72 mtdids = comma + 1; 73 } while (comma); 74 75 return -EINVAL; 76 } 77 78 #if IS_ENABLED(CONFIG_MTD) 79 static void mtd_probe_uclass_mtd_devs(void) 80 { 81 struct udevice *dev; 82 int idx = 0; 83 84 /* Probe devices with DM compliant drivers */ 85 while (!uclass_find_device(UCLASS_MTD, idx, &dev) && dev) { 86 mtd_probe(dev); 87 idx++; 88 } 89 } 90 #else 91 static void mtd_probe_uclass_mtd_devs(void) { } 92 #endif 93 94 #if defined(CONFIG_MTD_PARTITIONS) 95 int mtd_probe_devices(void) 96 { 97 static char *old_mtdparts; 98 static char *old_mtdids; 99 const char *mtdparts = env_get("mtdparts"); 100 const char *mtdids = env_get("mtdids"); 101 bool remaining_partitions = true; 102 struct mtd_info *mtd; 103 104 mtd_probe_uclass_mtd_devs(); 105 106 /* Check if mtdparts/mtdids changed since last call, otherwise: exit */ 107 if ((!mtdparts && !old_mtdparts && !mtdids && !old_mtdids) || 108 (mtdparts && old_mtdparts && mtdids && old_mtdids && 109 !strcmp(mtdparts, old_mtdparts) && 110 !strcmp(mtdids, old_mtdids))) 111 return 0; 112 113 /* Update the local copy of mtdparts */ 114 free(old_mtdparts); 115 free(old_mtdids); 116 old_mtdparts = strdup(mtdparts); 117 old_mtdids = strdup(mtdids); 118 119 /* If at least one partition is still in use, do not delete anything */ 120 mtd_for_each_device(mtd) { 121 if (mtd->usecount) { 122 printf("Partition \"%s\" already in use, aborting\n", 123 mtd->name); 124 return -EACCES; 125 } 126 } 127 128 /* 129 * Everything looks clear, remove all partitions. It is not safe to 130 * remove entries from the mtd_for_each_device loop as it uses idr 131 * indexes and the partitions removal is done in bulk (all partitions of 132 * one device at the same time), so break and iterate from start each 133 * time a new partition is found and deleted. 134 */ 135 while (remaining_partitions) { 136 remaining_partitions = false; 137 mtd_for_each_device(mtd) { 138 if (!mtd_is_partition(mtd) && mtd_has_partitions(mtd)) { 139 del_mtd_partitions(mtd); 140 remaining_partitions = true; 141 break; 142 } 143 } 144 } 145 146 /* If either mtdparts or mtdids is empty, then exit */ 147 if (!mtdparts || !mtdids) 148 return 0; 149 150 /* Start the parsing by ignoring the extra 'mtdparts=' prefix, if any */ 151 if (strstr(mtdparts, "mtdparts=")) 152 mtdparts += 9; 153 154 /* For each MTD device in mtdparts */ 155 while (mtdparts[0] != '\0') { 156 char mtd_name[MTD_NAME_MAX_LEN], *colon; 157 struct mtd_partition *parts; 158 int mtd_name_len, nparts; 159 int ret; 160 161 colon = strchr(mtdparts, ':'); 162 if (!colon) { 163 printf("Wrong mtdparts: %s\n", mtdparts); 164 return -EINVAL; 165 } 166 167 mtd_name_len = colon - mtdparts; 168 strncpy(mtd_name, mtdparts, mtd_name_len); 169 mtd_name[mtd_name_len] = '\0'; 170 /* Move the pointer forward (including the ':') */ 171 mtdparts += mtd_name_len + 1; 172 mtd = get_mtd_device_nm(mtd_name); 173 if (IS_ERR_OR_NULL(mtd)) { 174 char linux_name[MTD_NAME_MAX_LEN]; 175 176 /* 177 * The MTD device named "mtd_name" does not exist. Try 178 * to find a correspondance with an MTD device having 179 * the same type and number as defined in the mtdids. 180 */ 181 debug("No device named %s\n", mtd_name); 182 ret = mtd_search_alternate_name(mtd_name, linux_name, 183 MTD_NAME_MAX_LEN); 184 if (!ret) 185 mtd = get_mtd_device_nm(linux_name); 186 187 /* 188 * If no device could be found, move the mtdparts 189 * pointer forward until the next set of partitions. 190 */ 191 if (ret || IS_ERR_OR_NULL(mtd)) { 192 printf("Could not find a valid device for %s\n", 193 mtd_name); 194 mtdparts = strchr(mtdparts, ';'); 195 if (mtdparts) 196 mtdparts++; 197 198 continue; 199 } 200 } 201 202 /* 203 * Parse the MTD device partitions. It will update the mtdparts 204 * pointer, create an array of parts (that must be freed), and 205 * return the number of partition structures in the array. 206 */ 207 ret = mtd_parse_partitions(mtd, &mtdparts, &parts, &nparts); 208 if (ret) { 209 printf("Could not parse device %s\n", mtd->name); 210 put_mtd_device(mtd); 211 return -EINVAL; 212 } 213 214 if (!nparts) 215 continue; 216 217 /* Create the new MTD partitions */ 218 add_mtd_partitions(mtd, parts, nparts); 219 220 /* Free the structures allocated during the parsing */ 221 mtd_free_parsed_partitions(parts, nparts); 222 223 put_mtd_device(mtd); 224 } 225 226 return 0; 227 } 228 #else 229 int mtd_probe_devices(void) 230 { 231 mtd_probe_uclass_mtd_devs(); 232 233 return 0; 234 } 235 #endif /* defined(CONFIG_MTD_PARTITIONS) */ 236 237 /* Legacy */ 238 239 static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size, 240 loff_t *maxsize, int devtype) 241 { 242 #ifdef CONFIG_CMD_MTDPARTS 243 struct mtd_device *dev; 244 struct part_info *part; 245 u8 pnum; 246 int ret; 247 248 ret = mtdparts_init(); 249 if (ret) 250 return ret; 251 252 ret = find_dev_and_part(partname, &dev, &pnum, &part); 253 if (ret) 254 return ret; 255 256 if (dev->id->type != devtype) { 257 printf("not same typ %d != %d\n", dev->id->type, devtype); 258 return -1; 259 } 260 261 *off = part->offset; 262 *size = part->size; 263 *maxsize = part->size; 264 *idx = dev->id->num; 265 266 return 0; 267 #else 268 puts("mtdparts support missing.\n"); 269 return -1; 270 #endif 271 } 272 273 int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size, 274 loff_t *maxsize, int devtype, uint64_t chipsize) 275 { 276 if (!str2off(arg, off)) 277 return get_part(arg, idx, off, size, maxsize, devtype); 278 279 if (*off >= chipsize) { 280 puts("Offset exceeds device limit\n"); 281 return -1; 282 } 283 284 *maxsize = chipsize - *off; 285 *size = *maxsize; 286 return 0; 287 } 288 289 int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, 290 loff_t *size, loff_t *maxsize, int devtype, 291 uint64_t chipsize) 292 { 293 int ret; 294 295 if (argc == 0) { 296 *off = 0; 297 *size = chipsize; 298 *maxsize = *size; 299 goto print; 300 } 301 302 ret = mtd_arg_off(argv[0], idx, off, size, maxsize, devtype, 303 chipsize); 304 if (ret) 305 return ret; 306 307 if (argc == 1) 308 goto print; 309 310 if (!str2off(argv[1], size)) { 311 printf("'%s' is not a number\n", argv[1]); 312 return -1; 313 } 314 315 if (*size > *maxsize) { 316 puts("Size exceeds partition or device limit\n"); 317 return -1; 318 } 319 320 print: 321 printf("device %d ", *idx); 322 if (*size == chipsize) 323 puts("whole chip\n"); 324 else 325 printf("offset 0x%llx, size 0x%llx\n", 326 (unsigned long long)*off, (unsigned long long)*size); 327 return 0; 328 } 329