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