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 (!strcmp(mtdparts, old_mtdparts) && !strcmp(mtdids, old_mtdids)) 108 return 0; 109 110 /* Update the local copy of mtdparts */ 111 free(old_mtdparts); 112 free(old_mtdids); 113 old_mtdparts = strdup(mtdparts); 114 old_mtdids = strdup(mtdids); 115 116 /* If at least one partition is still in use, do not delete anything */ 117 mtd_for_each_device(mtd) { 118 if (mtd->usecount) { 119 printf("Partition \"%s\" already in use, aborting\n", 120 mtd->name); 121 return -EACCES; 122 } 123 } 124 125 /* 126 * Everything looks clear, remove all partitions. It is not safe to 127 * remove entries from the mtd_for_each_device loop as it uses idr 128 * indexes and the partitions removal is done in bulk (all partitions of 129 * one device at the same time), so break and iterate from start each 130 * time a new partition is found and deleted. 131 */ 132 while (remaining_partitions) { 133 remaining_partitions = false; 134 mtd_for_each_device(mtd) { 135 if (!mtd_is_partition(mtd) && mtd_has_partitions(mtd)) { 136 del_mtd_partitions(mtd); 137 remaining_partitions = true; 138 break; 139 } 140 } 141 } 142 143 /* Start the parsing by ignoring the extra 'mtdparts=' prefix, if any */ 144 if (strstr(mtdparts, "mtdparts=")) 145 mtdparts += 9; 146 147 /* For each MTD device in mtdparts */ 148 while (mtdparts[0] != '\0') { 149 char mtd_name[MTD_NAME_MAX_LEN], *colon; 150 struct mtd_partition *parts; 151 int mtd_name_len, nparts; 152 int ret; 153 154 colon = strchr(mtdparts, ':'); 155 if (!colon) { 156 printf("Wrong mtdparts: %s\n", mtdparts); 157 return -EINVAL; 158 } 159 160 mtd_name_len = colon - mtdparts; 161 strncpy(mtd_name, mtdparts, mtd_name_len); 162 mtd_name[mtd_name_len] = '\0'; 163 /* Move the pointer forward (including the ':') */ 164 mtdparts += mtd_name_len + 1; 165 mtd = get_mtd_device_nm(mtd_name); 166 if (IS_ERR_OR_NULL(mtd)) { 167 char linux_name[MTD_NAME_MAX_LEN]; 168 169 /* 170 * The MTD device named "mtd_name" does not exist. Try 171 * to find a correspondance with an MTD device having 172 * the same type and number as defined in the mtdids. 173 */ 174 debug("No device named %s\n", mtd_name); 175 ret = mtd_search_alternate_name(mtd_name, linux_name, 176 MTD_NAME_MAX_LEN); 177 if (!ret) 178 mtd = get_mtd_device_nm(linux_name); 179 180 /* 181 * If no device could be found, move the mtdparts 182 * pointer forward until the next set of partitions. 183 */ 184 if (ret || IS_ERR_OR_NULL(mtd)) { 185 printf("Could not find a valid device for %s\n", 186 mtd_name); 187 mtdparts = strchr(mtdparts, ';'); 188 if (mtdparts) 189 mtdparts++; 190 191 continue; 192 } 193 } 194 195 /* 196 * Parse the MTD device partitions. It will update the mtdparts 197 * pointer, create an array of parts (that must be freed), and 198 * return the number of partition structures in the array. 199 */ 200 ret = mtd_parse_partitions(mtd, &mtdparts, &parts, &nparts); 201 if (ret) { 202 printf("Could not parse device %s\n", mtd->name); 203 put_mtd_device(mtd); 204 return -EINVAL; 205 } 206 207 if (!nparts) 208 continue; 209 210 /* Create the new MTD partitions */ 211 add_mtd_partitions(mtd, parts, nparts); 212 213 /* Free the structures allocated during the parsing */ 214 mtd_free_parsed_partitions(parts, nparts); 215 216 put_mtd_device(mtd); 217 } 218 219 return 0; 220 } 221 #else 222 int mtd_probe_devices(void) 223 { 224 mtd_probe_uclass_mtd_devs(); 225 226 return 0; 227 } 228 #endif /* defined(CONFIG_MTD_PARTITIONS) */ 229 230 /* Legacy */ 231 232 static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size, 233 loff_t *maxsize, int devtype) 234 { 235 #ifdef CONFIG_CMD_MTDPARTS 236 struct mtd_device *dev; 237 struct part_info *part; 238 u8 pnum; 239 int ret; 240 241 ret = mtdparts_init(); 242 if (ret) 243 return ret; 244 245 ret = find_dev_and_part(partname, &dev, &pnum, &part); 246 if (ret) 247 return ret; 248 249 if (dev->id->type != devtype) { 250 printf("not same typ %d != %d\n", dev->id->type, devtype); 251 return -1; 252 } 253 254 *off = part->offset; 255 *size = part->size; 256 *maxsize = part->size; 257 *idx = dev->id->num; 258 259 return 0; 260 #else 261 puts("mtdparts support missing.\n"); 262 return -1; 263 #endif 264 } 265 266 int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size, 267 loff_t *maxsize, int devtype, uint64_t chipsize) 268 { 269 if (!str2off(arg, off)) 270 return get_part(arg, idx, off, size, maxsize, devtype); 271 272 if (*off >= chipsize) { 273 puts("Offset exceeds device limit\n"); 274 return -1; 275 } 276 277 *maxsize = chipsize - *off; 278 *size = *maxsize; 279 return 0; 280 } 281 282 int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, 283 loff_t *size, loff_t *maxsize, int devtype, 284 uint64_t chipsize) 285 { 286 int ret; 287 288 if (argc == 0) { 289 *off = 0; 290 *size = chipsize; 291 *maxsize = *size; 292 goto print; 293 } 294 295 ret = mtd_arg_off(argv[0], idx, off, size, maxsize, devtype, 296 chipsize); 297 if (ret) 298 return ret; 299 300 if (argc == 1) 301 goto print; 302 303 if (!str2off(argv[1], size)) { 304 printf("'%s' is not a number\n", argv[1]); 305 return -1; 306 } 307 308 if (*size > *maxsize) { 309 puts("Size exceeds partition or device limit\n"); 310 return -1; 311 } 312 313 print: 314 printf("device %d ", *idx); 315 if (*size == chipsize) 316 puts("whole chip\n"); 317 else 318 printf("offset 0x%llx, size 0x%llx\n", 319 (unsigned long long)*off, (unsigned long long)*size); 320 return 0; 321 } 322