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