blob: 4d2a76a208a3cfd69c483e0d7149088b955d6cde [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Stefan Roese68d7d652009-03-19 13:30:36 +01002/*
3 * (C) Copyright 2002
4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5 *
6 * (C) Copyright 2002
7 * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de>
8 *
9 * (C) Copyright 2003
10 * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de>
11 *
12 * (C) Copyright 2005
13 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
14 *
15 * Added support for reading flash partition table from environment.
16 * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4
17 * kernel tree.
18 *
Ben Gardinerca75b202010-08-31 17:48:03 -040019 * (C) Copyright 2008
20 * Harald Welte, OpenMoko, Inc., Harald Welte <laforge@openmoko.org>
21 *
Stefan Roese68d7d652009-03-19 13:30:36 +010022 * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $
23 * Copyright 2002 SYSGO Real-Time Solutions GmbH
Stefan Roese68d7d652009-03-19 13:30:36 +010024 */
25
26/*
27 * Three environment variables are used by the parsing routines:
28 *
29 * 'partition' - keeps current partition identifier
30 *
31 * partition := <part-id>
32 * <part-id> := <dev-id>,part_num
33 *
34 *
35 * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping
36 *
37 * mtdids=<idmap>[,<idmap>,...]
38 *
39 * <idmap> := <dev-id>=<mtd-id>
Miquel Raynal00ac9222018-09-06 09:08:45 +020040 * <dev-id> := 'nand'|'nor'|'onenand'|'spi-nand'<dev-num>
Stefan Roese68d7d652009-03-19 13:30:36 +010041 * <dev-num> := mtd device number, 0...
42 * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)
43 *
44 *
45 * 'mtdparts' - partition list
46 *
Miquel Raynald60aea92018-09-06 09:08:46 +020047 * mtdparts=[mtdparts=]<mtd-def>[;<mtd-def>...]
Stefan Roese68d7d652009-03-19 13:30:36 +010048 *
49 * <mtd-def> := <mtd-id>:<part-def>[,<part-def>...]
50 * <mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)
51 * <part-def> := <size>[@<offset>][<name>][<ro-flag>]
52 * <size> := standard linux memsize OR '-' to denote all remaining space
53 * <offset> := partition start offset within the device
54 * <name> := '(' NAME ')'
55 * <ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)
56 *
57 * Notes:
58 * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping
59 * - if the above variables are not set defaults for a given target are used
60 *
61 * Examples:
62 *
63 * 1 NOR Flash, with 1 single writable partition:
64 * mtdids=nor0=edb7312-nor
Miquel Raynald60aea92018-09-06 09:08:46 +020065 * mtdparts=[mtdparts=]edb7312-nor:-
Stefan Roese68d7d652009-03-19 13:30:36 +010066 *
67 * 1 NOR Flash with 2 partitions, 1 NAND with one
68 * mtdids=nor0=edb7312-nor,nand0=edb7312-nand
Miquel Raynald60aea92018-09-06 09:08:46 +020069 * mtdparts=[mtdparts=]edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
Stefan Roese68d7d652009-03-19 13:30:36 +010070 *
71 */
72
Stefan Roese68d7d652009-03-19 13:30:36 +010073#include <common.h>
74#include <command.h>
Simon Glass3a7d5572019-08-01 09:46:42 -060075#include <env.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060076#include <log.h>
Stefan Roese68d7d652009-03-19 13:30:36 +010077#include <malloc.h>
Simon Glass401d1c42020-10-30 21:38:53 -060078#include <asm/global_data.h>
Ladislav Michlb93b24b2009-03-23 12:06:07 +010079#include <jffs2/load_kernel.h>
Stefan Roese68d7d652009-03-19 13:30:36 +010080#include <linux/list.h>
81#include <linux/ctype.h>
Stefan Roese864aa032009-05-12 14:31:56 +020082#include <linux/err.h>
83#include <linux/mtd/mtd.h>
Stefan Roese68d7d652009-03-19 13:30:36 +010084
85#if defined(CONFIG_CMD_NAND)
Masahiro Yamada6ae39002017-11-30 13:45:24 +090086#include <linux/mtd/rawnand.h>
Stefan Roese68d7d652009-03-19 13:30:36 +010087#include <nand.h>
Stefan Roese68d7d652009-03-19 13:30:36 +010088#endif
89
90#if defined(CONFIG_CMD_ONENAND)
Stefan Roese68d7d652009-03-19 13:30:36 +010091#include <linux/mtd/onenand.h>
92#include <onenand_uboot.h>
93#endif
94
Joe Hershbergera7eb1d62013-04-08 10:32:50 +000095DECLARE_GLOBAL_DATA_PTR;
96
Stefan Roese68d7d652009-03-19 13:30:36 +010097/* special size referring to all the remaining space in a partition */
Paul Burton39ac3442013-09-04 15:16:57 +010098#define SIZE_REMAINING (~0llu)
Stefan Roese68d7d652009-03-19 13:30:36 +010099
100/* special offset value, it is used when not provided by user
101 *
102 * this value is used temporarily during parsing, later such offests
103 * are recalculated */
Paul Burton39ac3442013-09-04 15:16:57 +0100104#define OFFSET_NOT_SPECIFIED (~0llu)
Stefan Roese68d7d652009-03-19 13:30:36 +0100105
106/* minimum partition size */
107#define MIN_PART_SIZE 4096
108
109/* this flag needs to be set in part_info struct mask_flags
110 * field for read-only partitions */
111#define MTD_WRITEABLE_CMD 1
112
Feng Chenb5a1fca2023-04-23 17:01:37 +0800113#ifdef CONFIG_AML_MTDPART
114extern struct list_head aml_device;
115extern int mtdparts_init(void);
116#endif
117
Stefan Roese68d7d652009-03-19 13:30:36 +0100118/* default values for mtdids and mtdparts variables */
Ladislav Michlaf324432016-07-12 20:28:26 +0200119#if !defined(MTDIDS_DEFAULT)
Maxime Ripard0269dfa2017-02-27 18:22:04 +0100120#ifdef CONFIG_MTDIDS_DEFAULT
121#define MTDIDS_DEFAULT CONFIG_MTDIDS_DEFAULT
122#else
Ladislav Michlaf324432016-07-12 20:28:26 +0200123#define MTDIDS_DEFAULT NULL
Stefan Roese68d7d652009-03-19 13:30:36 +0100124#endif
Maxime Ripard0269dfa2017-02-27 18:22:04 +0100125#endif
Ladislav Michlaf324432016-07-12 20:28:26 +0200126#if !defined(MTDPARTS_DEFAULT)
Maxime Ripard0269dfa2017-02-27 18:22:04 +0100127#ifdef CONFIG_MTDPARTS_DEFAULT
128#define MTDPARTS_DEFAULT CONFIG_MTDPARTS_DEFAULT
129#else
Ladislav Michlaf324432016-07-12 20:28:26 +0200130#define MTDPARTS_DEFAULT NULL
Stefan Roese68d7d652009-03-19 13:30:36 +0100131#endif
Maxime Ripard0269dfa2017-02-27 18:22:04 +0100132#endif
Ladislav Michlaf324432016-07-12 20:28:26 +0200133#if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
134extern void board_mtdparts_default(const char **mtdids, const char **mtdparts);
135#endif
Feng Chenb5a1fca2023-04-23 17:01:37 +0800136#ifndef CONFIG_AML_MTDPART
Ladislav Michlaf324432016-07-12 20:28:26 +0200137static const char *mtdids_default = MTDIDS_DEFAULT;
138static const char *mtdparts_default = MTDPARTS_DEFAULT;
Feng Chenb5a1fca2023-04-23 17:01:37 +0800139#endif
Stefan Roese68d7d652009-03-19 13:30:36 +0100140
141/* copies of last seen 'mtdids', 'mtdparts' and 'partition' env variables */
142#define MTDIDS_MAXLEN 128
143#define MTDPARTS_MAXLEN 512
144#define PARTITION_MAXLEN 16
Feng Chenb5a1fca2023-04-23 17:01:37 +0800145
146#ifndef CONFIG_AML_MTDPART
Tom Rini54f17922017-08-14 20:42:27 -0400147static char last_ids[MTDIDS_MAXLEN + 1];
148static char last_parts[MTDPARTS_MAXLEN + 1];
Feng Chenb5a1fca2023-04-23 17:01:37 +0800149#endif
150
Tom Rini54f17922017-08-14 20:42:27 -0400151static char last_partition[PARTITION_MAXLEN + 1];
Stefan Roese68d7d652009-03-19 13:30:36 +0100152
153/* low level jffs2 cache cleaning routine */
154extern void jffs2_free_cache(struct part_info *part);
155
Feng Chenb5a1fca2023-04-23 17:01:37 +0800156#ifndef CONFIG_AML_MTDPART
Stefan Roese68d7d652009-03-19 13:30:36 +0100157/* mtdids mapping list, filled by parse_ids() */
Kim Phillips088f1b12012-10-29 13:34:31 +0000158static struct list_head mtdids;
Feng Chenb5a1fca2023-04-23 17:01:37 +0800159#endif
Stefan Roese68d7d652009-03-19 13:30:36 +0100160
161/* device/partition list, parse_cmdline() parses into here */
Kim Phillips088f1b12012-10-29 13:34:31 +0000162static struct list_head devices;
Stefan Roese68d7d652009-03-19 13:30:36 +0100163
164/* current active device and partition number */
Stefan Roese76b58832009-05-16 12:04:22 +0200165struct mtd_device *current_mtd_dev = NULL;
166u8 current_mtd_partnum = 0;
Stefan Roese68d7d652009-03-19 13:30:36 +0100167
Ladislav Michlf8f744a2016-07-12 20:28:25 +0200168u8 use_defaults;
169
Stefan Roese68d7d652009-03-19 13:30:36 +0100170static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num);
171
Feng Chenb5a1fca2023-04-23 17:01:37 +0800172#ifndef CONFIG_AML_MTDPART
Stefan Roese68d7d652009-03-19 13:30:36 +0100173/* command line only routines */
174static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len);
175static int device_del(struct mtd_device *dev);
Feng Chenb5a1fca2023-04-23 17:01:37 +0800176#endif
Stefan Roese68d7d652009-03-19 13:30:36 +0100177
Feng Chenb5a1fca2023-04-23 17:01:37 +0800178#ifndef CONFIG_AML_MTDPART
Stefan Roese68d7d652009-03-19 13:30:36 +0100179/**
180 * Parses a string into a number. The number stored at ptr is
181 * potentially suffixed with K (for kilobytes, or 1024 bytes),
182 * M (for megabytes, or 1048576 bytes), or G (for gigabytes, or
183 * 1073741824). If the number is suffixed with K, M, or G, then
184 * the return value is the number multiplied by one kilobyte, one
185 * megabyte, or one gigabyte, respectively.
186 *
187 * @param ptr where parse begins
188 * @param retptr output pointer to next char after parse completes (output)
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100189 * Return: resulting unsigned int
Stefan Roese68d7d652009-03-19 13:30:36 +0100190 */
Paul Burton39ac3442013-09-04 15:16:57 +0100191static u64 memsize_parse (const char *const ptr, const char **retptr)
Stefan Roese68d7d652009-03-19 13:30:36 +0100192{
Paul Burton39ac3442013-09-04 15:16:57 +0100193 u64 ret = simple_strtoull(ptr, (char **)retptr, 0);
Stefan Roese68d7d652009-03-19 13:30:36 +0100194
195 switch (**retptr) {
196 case 'G':
197 case 'g':
198 ret <<= 10;
Miquel Raynal2b9ace52018-08-16 17:30:21 +0200199 /* Fallthrough */
Stefan Roese68d7d652009-03-19 13:30:36 +0100200 case 'M':
201 case 'm':
202 ret <<= 10;
Miquel Raynal2b9ace52018-08-16 17:30:21 +0200203 /* Fallthrough */
Stefan Roese68d7d652009-03-19 13:30:36 +0100204 case 'K':
205 case 'k':
206 ret <<= 10;
207 (*retptr)++;
Miquel Raynal2b9ace52018-08-16 17:30:21 +0200208 /* Fallthrough */
Stefan Roese68d7d652009-03-19 13:30:36 +0100209 default:
210 break;
211 }
212
213 return ret;
214}
215
216/**
217 * Format string describing supplied size. This routine does the opposite job
218 * to memsize_parse(). Size in bytes is converted to string and if possible
219 * shortened by using k (kilobytes), m (megabytes) or g (gigabytes) suffix.
220 *
221 * Note, that this routine does not check for buffer overflow, it's the caller
222 * who must assure enough space.
223 *
224 * @param buf output buffer
225 * @param size size to be converted to string
226 */
Paul Burton39ac3442013-09-04 15:16:57 +0100227static void memsize_format(char *buf, u64 size)
Stefan Roese68d7d652009-03-19 13:30:36 +0100228{
229#define SIZE_GB ((u32)1024*1024*1024)
230#define SIZE_MB ((u32)1024*1024)
231#define SIZE_KB ((u32)1024)
232
233 if ((size % SIZE_GB) == 0)
Paul Burton39ac3442013-09-04 15:16:57 +0100234 sprintf(buf, "%llug", size/SIZE_GB);
Stefan Roese68d7d652009-03-19 13:30:36 +0100235 else if ((size % SIZE_MB) == 0)
Paul Burton39ac3442013-09-04 15:16:57 +0100236 sprintf(buf, "%llum", size/SIZE_MB);
Stefan Roese68d7d652009-03-19 13:30:36 +0100237 else if (size % SIZE_KB == 0)
Paul Burton39ac3442013-09-04 15:16:57 +0100238 sprintf(buf, "%lluk", size/SIZE_KB);
Stefan Roese68d7d652009-03-19 13:30:36 +0100239 else
Paul Burton39ac3442013-09-04 15:16:57 +0100240 sprintf(buf, "%llu", size);
Stefan Roese68d7d652009-03-19 13:30:36 +0100241}
Feng Chenb5a1fca2023-04-23 17:01:37 +0800242#endif
Stefan Roese68d7d652009-03-19 13:30:36 +0100243
244/**
245 * This routine does global indexing of all partitions. Resulting index for
246 * current partition is saved in 'mtddevnum'. Current partition name in
247 * 'mtddevname'.
248 */
249static void index_partitions(void)
250{
Stefan Roese68d7d652009-03-19 13:30:36 +0100251 u16 mtddevnum;
252 struct part_info *part;
253 struct list_head *dentry;
254 struct mtd_device *dev;
255
Wolfgang Denke1d29502010-04-28 10:58:10 +0200256 debug("--- index partitions ---\n");
Stefan Roese68d7d652009-03-19 13:30:36 +0100257
Stefan Roese76b58832009-05-16 12:04:22 +0200258 if (current_mtd_dev) {
Stefan Roese68d7d652009-03-19 13:30:36 +0100259 mtddevnum = 0;
260 list_for_each(dentry, &devices) {
261 dev = list_entry(dentry, struct mtd_device, link);
Stefan Roese76b58832009-05-16 12:04:22 +0200262 if (dev == current_mtd_dev) {
263 mtddevnum += current_mtd_partnum;
Simon Glass018f5302017-08-03 12:22:10 -0600264 env_set_ulong("mtddevnum", mtddevnum);
Tom Rini54f17922017-08-14 20:42:27 -0400265 debug("=> mtddevnum %d,\n", mtddevnum);
Stefan Roese68d7d652009-03-19 13:30:36 +0100266 break;
267 }
268 mtddevnum += dev->num_parts;
269 }
270
Stefan Roese76b58832009-05-16 12:04:22 +0200271 part = mtd_part_info(current_mtd_dev, current_mtd_partnum);
Tom Rini54f17922017-08-14 20:42:27 -0400272 if (part) {
273 env_set("mtddevname", part->name);
Stefan Roese68d7d652009-03-19 13:30:36 +0100274
Tom Rini54f17922017-08-14 20:42:27 -0400275 debug("=> mtddevname %s\n", part->name);
276 } else {
277 env_set("mtddevname", NULL);
278
279 debug("=> mtddevname NULL\n");
280 }
Stefan Roese68d7d652009-03-19 13:30:36 +0100281 } else {
Simon Glass382bee52017-08-03 12:22:09 -0600282 env_set("mtddevnum", NULL);
283 env_set("mtddevname", NULL);
Stefan Roese68d7d652009-03-19 13:30:36 +0100284
Wolfgang Denke1d29502010-04-28 10:58:10 +0200285 debug("=> mtddevnum NULL\n=> mtddevname NULL\n");
Stefan Roese68d7d652009-03-19 13:30:36 +0100286 }
287}
288
289/**
290 * Save current device and partition in environment variable 'partition'.
291 */
292static void current_save(void)
293{
294 char buf[16];
295
Wolfgang Denke1d29502010-04-28 10:58:10 +0200296 debug("--- current_save ---\n");
Stefan Roese68d7d652009-03-19 13:30:36 +0100297
Stefan Roese76b58832009-05-16 12:04:22 +0200298 if (current_mtd_dev) {
299 sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_mtd_dev->id->type),
300 current_mtd_dev->id->num, current_mtd_partnum);
Stefan Roese68d7d652009-03-19 13:30:36 +0100301
Simon Glass382bee52017-08-03 12:22:09 -0600302 env_set("partition", buf);
Stefan Roese68d7d652009-03-19 13:30:36 +0100303 strncpy(last_partition, buf, 16);
304
Wolfgang Denke1d29502010-04-28 10:58:10 +0200305 debug("=> partition %s\n", buf);
Stefan Roese68d7d652009-03-19 13:30:36 +0100306 } else {
Simon Glass382bee52017-08-03 12:22:09 -0600307 env_set("partition", NULL);
Stefan Roese68d7d652009-03-19 13:30:36 +0100308 last_partition[0] = '\0';
309
Wolfgang Denke1d29502010-04-28 10:58:10 +0200310 debug("=> partition NULL\n");
Stefan Roese68d7d652009-03-19 13:30:36 +0100311 }
312 index_partitions();
313}
314
Feng Chenb5a1fca2023-04-23 17:01:37 +0800315#ifndef CONFIG_AML_MTDPART
Ben Gardiner0a026d32010-08-31 17:48:00 -0400316/**
317 * Produce a mtd_info given a type and num.
318 *
319 * @param type mtd type
320 * @param num mtd number
321 * @param mtd a pointer to an mtd_info instance (output)
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100322 * Return: 0 if device is valid, 1 otherwise
Ben Gardiner0a026d32010-08-31 17:48:00 -0400323 */
324static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd)
325{
326 char mtd_dev[16];
327
328 sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num);
329 *mtd = get_mtd_device_nm(mtd_dev);
330 if (IS_ERR(*mtd)) {
331 printf("Device %s not found!\n", mtd_dev);
332 return 1;
333 }
Heiko Schocher1668d642014-07-14 09:17:11 +0200334 put_mtd_device(*mtd);
Ben Gardiner0a026d32010-08-31 17:48:00 -0400335
336 return 0;
337}
338
Stefan Roese68d7d652009-03-19 13:30:36 +0100339/**
Stefan Roese864aa032009-05-12 14:31:56 +0200340 * Performs sanity check for supplied flash partition.
341 * Table of existing MTD flash devices is searched and partition device
Stefan Roese68d7d652009-03-19 13:30:36 +0100342 * is located. Alignment with the granularity of nand erasesize is verified.
343 *
344 * @param id of the parent device
345 * @param part partition to validate
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100346 * Return: 0 if partition is valid, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +0100347 */
Stefan Roese864aa032009-05-12 14:31:56 +0200348static int part_validate_eraseblock(struct mtdids *id, struct part_info *part)
Stefan Roese68d7d652009-03-19 13:30:36 +0100349{
Ben Gardiner0a026d32010-08-31 17:48:00 -0400350 struct mtd_info *mtd = NULL;
Stefan Roese864aa032009-05-12 14:31:56 +0200351 int i, j;
352 ulong start;
Paul Burton39ac3442013-09-04 15:16:57 +0100353 u64 offset, size;
Stefan Roese68d7d652009-03-19 13:30:36 +0100354
Ben Gardiner0a026d32010-08-31 17:48:00 -0400355 if (get_mtd_info(id->type, id->num, &mtd))
Stefan Roese864aa032009-05-12 14:31:56 +0200356 return 1;
Stefan Roese68d7d652009-03-19 13:30:36 +0100357
358 part->sector_size = mtd->erasesize;
359
Stefan Roese864aa032009-05-12 14:31:56 +0200360 if (!mtd->numeraseregions) {
361 /*
Miquel Raynal00ac9222018-09-06 09:08:45 +0200362 * Only one eraseregion (NAND, SPI-NAND, OneNAND or uniform NOR),
Stefan Roese864aa032009-05-12 14:31:56 +0200363 * checking for alignment is easy here
364 */
Paul Burton39ac3442013-09-04 15:16:57 +0100365 offset = part->offset;
366 if (do_div(offset, mtd->erasesize)) {
Stefan Roese864aa032009-05-12 14:31:56 +0200367 printf("%s%d: partition (%s) start offset"
368 "alignment incorrect\n",
369 MTD_DEV_TYPE(id->type), id->num, part->name);
370 return 1;
371 }
Stefan Roese68d7d652009-03-19 13:30:36 +0100372
Paul Burton39ac3442013-09-04 15:16:57 +0100373 size = part->size;
374 if (do_div(size, mtd->erasesize)) {
Stefan Roese864aa032009-05-12 14:31:56 +0200375 printf("%s%d: partition (%s) size alignment incorrect\n",
376 MTD_DEV_TYPE(id->type), id->num, part->name);
377 return 1;
378 }
379 } else {
380 /*
381 * Multiple eraseregions (non-uniform NOR),
382 * checking for alignment is more complex here
383 */
384
385 /* Check start alignment */
386 for (i = 0; i < mtd->numeraseregions; i++) {
387 start = mtd->eraseregions[i].offset;
388 for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
389 if (part->offset == start)
390 goto start_ok;
391 start += mtd->eraseregions[i].erasesize;
392 }
393 }
394
395 printf("%s%d: partition (%s) start offset alignment incorrect\n",
396 MTD_DEV_TYPE(id->type), id->num, part->name);
Stefan Roese68d7d652009-03-19 13:30:36 +0100397 return 1;
Stefan Roese864aa032009-05-12 14:31:56 +0200398
399 start_ok:
400
401 /* Check end/size alignment */
402 for (i = 0; i < mtd->numeraseregions; i++) {
403 start = mtd->eraseregions[i].offset;
404 for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
405 if ((part->offset + part->size) == start)
406 goto end_ok;
407 start += mtd->eraseregions[i].erasesize;
408 }
409 }
410 /* Check last sector alignment */
411 if ((part->offset + part->size) == start)
412 goto end_ok;
413
414 printf("%s%d: partition (%s) size alignment incorrect\n",
415 MTD_DEV_TYPE(id->type), id->num, part->name);
416 return 1;
417
418 end_ok:
419 return 0;
Stefan Roese68d7d652009-03-19 13:30:36 +0100420 }
421
422 return 0;
Stefan Roese68d7d652009-03-19 13:30:36 +0100423}
424
425
426/**
Otavio Salvadorf5019912013-10-03 08:04:38 -0300427 * Performs sanity check for supplied partition. Offset and size are
428 * verified to be within valid range. Partition type is checked and
429 * part_validate_eraseblock() is called with the argument of part.
Stefan Roese68d7d652009-03-19 13:30:36 +0100430 *
431 * @param id of the parent device
432 * @param part partition to validate
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100433 * Return: 0 if partition is valid, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +0100434 */
435static int part_validate(struct mtdids *id, struct part_info *part)
436{
437 if (part->size == SIZE_REMAINING)
438 part->size = id->size - part->offset;
439
440 if (part->offset > id->size) {
Paul Burton39ac3442013-09-04 15:16:57 +0100441 printf("%s: offset %08llx beyond flash size %08llx\n",
Stefan Roese68d7d652009-03-19 13:30:36 +0100442 id->mtd_id, part->offset, id->size);
443 return 1;
444 }
445
446 if ((part->offset + part->size) <= part->offset) {
447 printf("%s%d: partition (%s) size too big\n",
448 MTD_DEV_TYPE(id->type), id->num, part->name);
449 return 1;
450 }
451
452 if (part->offset + part->size > id->size) {
453 printf("%s: partitioning exceeds flash size\n", id->mtd_id);
454 return 1;
455 }
456
Stefan Roese864aa032009-05-12 14:31:56 +0200457 /*
458 * Now we need to check if the partition starts and ends on
459 * sector (eraseblock) regions
460 */
461 return part_validate_eraseblock(id, part);
Stefan Roese68d7d652009-03-19 13:30:36 +0100462}
463
464/**
Robert P. J. Day1f8b5462013-08-21 11:39:19 -0400465 * Delete selected partition from the partition list of the specified device.
Stefan Roese68d7d652009-03-19 13:30:36 +0100466 *
467 * @param dev device to delete partition from
468 * @param part partition to delete
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100469 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +0100470 */
471static int part_del(struct mtd_device *dev, struct part_info *part)
472{
473 u8 current_save_needed = 0;
474
475 /* if there is only one partition, remove whole device */
476 if (dev->num_parts == 1)
477 return device_del(dev);
478
479 /* otherwise just delete this partition */
480
Stefan Roese76b58832009-05-16 12:04:22 +0200481 if (dev == current_mtd_dev) {
Stefan Roese68d7d652009-03-19 13:30:36 +0100482 /* we are modyfing partitions for the current device,
483 * update current */
484 struct part_info *curr_pi;
Stefan Roese76b58832009-05-16 12:04:22 +0200485 curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum);
Stefan Roese68d7d652009-03-19 13:30:36 +0100486
487 if (curr_pi) {
488 if (curr_pi == part) {
489 printf("current partition deleted, resetting current to 0\n");
Stefan Roese76b58832009-05-16 12:04:22 +0200490 current_mtd_partnum = 0;
Stefan Roese68d7d652009-03-19 13:30:36 +0100491 } else if (part->offset <= curr_pi->offset) {
Stefan Roese76b58832009-05-16 12:04:22 +0200492 current_mtd_partnum--;
Stefan Roese68d7d652009-03-19 13:30:36 +0100493 }
494 current_save_needed = 1;
495 }
496 }
497
Stefan Roese68d7d652009-03-19 13:30:36 +0100498 list_del(&part->link);
499 free(part);
500 dev->num_parts--;
501
502 if (current_save_needed > 0)
503 current_save();
504 else
505 index_partitions();
506
507 return 0;
508}
509
510/**
511 * Delete all partitions from parts head list, free memory.
512 *
513 * @param head list of partitions to delete
514 */
515static void part_delall(struct list_head *head)
516{
517 struct list_head *entry, *n;
518 struct part_info *part_tmp;
519
520 /* clean tmp_list and free allocated memory */
521 list_for_each_safe(entry, n, head) {
522 part_tmp = list_entry(entry, struct part_info, link);
523
Stefan Roese68d7d652009-03-19 13:30:36 +0100524 list_del(entry);
525 free(part_tmp);
526 }
527}
528
529/**
530 * Add new partition to the supplied partition list. Make sure partitions are
531 * sorted by offset in ascending order.
532 *
533 * @param head list this partition is to be added to
534 * @param new partition to be added
535 */
536static int part_sort_add(struct mtd_device *dev, struct part_info *part)
537{
538 struct list_head *entry;
539 struct part_info *new_pi, *curr_pi;
540
541 /* link partition to parrent dev */
542 part->dev = dev;
543
544 if (list_empty(&dev->parts)) {
Wolfgang Denke1d29502010-04-28 10:58:10 +0200545 debug("part_sort_add: list empty\n");
Stefan Roese68d7d652009-03-19 13:30:36 +0100546 list_add(&part->link, &dev->parts);
547 dev->num_parts++;
548 index_partitions();
549 return 0;
550 }
551
552 new_pi = list_entry(&part->link, struct part_info, link);
553
554 /* get current partition info if we are updating current device */
555 curr_pi = NULL;
Stefan Roese76b58832009-05-16 12:04:22 +0200556 if (dev == current_mtd_dev)
557 curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum);
Stefan Roese68d7d652009-03-19 13:30:36 +0100558
559 list_for_each(entry, &dev->parts) {
560 struct part_info *pi;
561
562 pi = list_entry(entry, struct part_info, link);
563
564 /* be compliant with kernel cmdline, allow only one partition at offset zero */
565 if ((new_pi->offset == pi->offset) && (pi->offset == 0)) {
566 printf("cannot add second partition at offset 0\n");
567 return 1;
568 }
569
570 if (new_pi->offset <= pi->offset) {
571 list_add_tail(&part->link, entry);
572 dev->num_parts++;
573
574 if (curr_pi && (pi->offset <= curr_pi->offset)) {
575 /* we are modyfing partitions for the current
576 * device, update current */
Stefan Roese76b58832009-05-16 12:04:22 +0200577 current_mtd_partnum++;
Stefan Roese68d7d652009-03-19 13:30:36 +0100578 current_save();
579 } else {
580 index_partitions();
581 }
582 return 0;
583 }
584 }
585
586 list_add_tail(&part->link, &dev->parts);
587 dev->num_parts++;
588 index_partitions();
589 return 0;
590}
591
592/**
593 * Add provided partition to the partition list of a given device.
594 *
595 * @param dev device to which partition is added
596 * @param part partition to be added
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100597 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +0100598 */
599static int part_add(struct mtd_device *dev, struct part_info *part)
600{
601 /* verify alignment and size */
602 if (part_validate(dev->id, part) != 0)
603 return 1;
604
605 /* partition is ok, add it to the list */
606 if (part_sort_add(dev, part) != 0)
607 return 1;
608
609 return 0;
610}
611
612/**
613 * Parse one partition definition, allocate memory and return pointer to this
614 * location in retpart.
615 *
616 * @param partdef pointer to the partition definition string i.e. <part-def>
617 * @param ret output pointer to next char after parse completes (output)
618 * @param retpart pointer to the allocated partition (output)
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100619 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +0100620 */
621static int part_parse(const char *const partdef, const char **ret, struct part_info **retpart)
622{
623 struct part_info *part;
Paul Burton39ac3442013-09-04 15:16:57 +0100624 u64 size;
625 u64 offset;
Stefan Roese68d7d652009-03-19 13:30:36 +0100626 const char *name;
627 int name_len;
628 unsigned int mask_flags;
629 const char *p;
630
631 p = partdef;
632 *retpart = NULL;
633 *ret = NULL;
634
635 /* fetch the partition size */
636 if (*p == '-') {
637 /* assign all remaining space to this partition */
Wolfgang Denke1d29502010-04-28 10:58:10 +0200638 debug("'-': remaining size assigned\n");
Stefan Roese68d7d652009-03-19 13:30:36 +0100639 size = SIZE_REMAINING;
640 p++;
641 } else {
642 size = memsize_parse(p, &p);
643 if (size < MIN_PART_SIZE) {
Paul Burton39ac3442013-09-04 15:16:57 +0100644 printf("partition size too small (%llx)\n", size);
Stefan Roese68d7d652009-03-19 13:30:36 +0100645 return 1;
646 }
647 }
648
649 /* check for offset */
650 offset = OFFSET_NOT_SPECIFIED;
651 if (*p == '@') {
652 p++;
653 offset = memsize_parse(p, &p);
654 }
655
656 /* now look for the name */
657 if (*p == '(') {
658 name = ++p;
659 if ((p = strchr(name, ')')) == NULL) {
660 printf("no closing ) found in partition name\n");
661 return 1;
662 }
663 name_len = p - name + 1;
664 if ((name_len - 1) == 0) {
665 printf("empty partition name\n");
666 return 1;
667 }
668 p++;
669 } else {
670 /* 0x00000000@0x00000000 */
671 name_len = 22;
672 name = NULL;
673 }
674
675 /* test for options */
676 mask_flags = 0;
677 if (strncmp(p, "ro", 2) == 0) {
678 mask_flags |= MTD_WRITEABLE_CMD;
679 p += 2;
680 }
681
682 /* check for next partition definition */
683 if (*p == ',') {
684 if (size == SIZE_REMAINING) {
685 *ret = NULL;
686 printf("no partitions allowed after a fill-up partition\n");
687 return 1;
688 }
689 *ret = ++p;
690 } else if ((*p == ';') || (*p == '\0')) {
691 *ret = p;
692 } else {
693 printf("unexpected character '%c' at the end of partition\n", *p);
694 *ret = NULL;
695 return 1;
696 }
697
698 /* allocate memory */
699 part = (struct part_info *)malloc(sizeof(struct part_info) + name_len);
700 if (!part) {
701 printf("out of memory\n");
702 return 1;
703 }
704 memset(part, 0, sizeof(struct part_info) + name_len);
705 part->size = size;
706 part->offset = offset;
707 part->mask_flags = mask_flags;
708 part->name = (char *)(part + 1);
709
710 if (name) {
711 /* copy user provided name */
712 strncpy(part->name, name, name_len - 1);
713 part->auto_name = 0;
714 } else {
715 /* auto generated name in form of size@offset */
Kay Potthoff149c21b2018-07-17 08:19:39 +0200716 snprintf(part->name, name_len, "0x%08llx@0x%08llx", size, offset);
Stefan Roese68d7d652009-03-19 13:30:36 +0100717 part->auto_name = 1;
718 }
719
720 part->name[name_len - 1] = '\0';
721 INIT_LIST_HEAD(&part->link);
722
Paul Burton39ac3442013-09-04 15:16:57 +0100723 debug("+ partition: name %-22s size 0x%08llx offset 0x%08llx mask flags %d\n",
Stefan Roese68d7d652009-03-19 13:30:36 +0100724 part->name, part->size,
725 part->offset, part->mask_flags);
726
727 *retpart = part;
728 return 0;
729}
Feng Chenb5a1fca2023-04-23 17:01:37 +0800730#endif
Stefan Roese68d7d652009-03-19 13:30:36 +0100731
732/**
733 * Check device number to be within valid range for given device type.
734 *
Ben Gardiner0a026d32010-08-31 17:48:00 -0400735 * @param type mtd type
736 * @param num mtd number
737 * @param size a pointer to the size of the mtd device (output)
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100738 * Return: 0 if device is valid, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +0100739 */
Feng Chenb5a1fca2023-04-23 17:01:37 +0800740#ifndef CONFIG_AML_MTDPART
Paul Burton39ac3442013-09-04 15:16:57 +0100741static int mtd_device_validate(u8 type, u8 num, u64 *size)
Stefan Roese68d7d652009-03-19 13:30:36 +0100742{
Ben Gardiner0a026d32010-08-31 17:48:00 -0400743 struct mtd_info *mtd = NULL;
Stefan Roese68d7d652009-03-19 13:30:36 +0100744
Ben Gardiner0a026d32010-08-31 17:48:00 -0400745 if (get_mtd_info(type, num, &mtd))
Stefan Roese864aa032009-05-12 14:31:56 +0200746 return 1;
Stefan Roese68d7d652009-03-19 13:30:36 +0100747
Stefan Roese864aa032009-05-12 14:31:56 +0200748 *size = mtd->size;
Stefan Roese68d7d652009-03-19 13:30:36 +0100749
Stefan Roese864aa032009-05-12 14:31:56 +0200750 return 0;
Stefan Roese68d7d652009-03-19 13:30:36 +0100751}
752
753/**
754 * Delete all mtd devices from a supplied devices list, free memory allocated for
755 * each device and delete all device partitions.
756 *
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100757 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +0100758 */
759static int device_delall(struct list_head *head)
760{
761 struct list_head *entry, *n;
762 struct mtd_device *dev_tmp;
763
764 /* clean devices list */
765 list_for_each_safe(entry, n, head) {
766 dev_tmp = list_entry(entry, struct mtd_device, link);
767 list_del(entry);
768 part_delall(&dev_tmp->parts);
769 free(dev_tmp);
770 }
771 INIT_LIST_HEAD(&devices);
772
773 return 0;
774}
Feng Chenb5a1fca2023-04-23 17:01:37 +0800775#endif
Stefan Roese68d7d652009-03-19 13:30:36 +0100776
777/**
778 * If provided device exists it's partitions are deleted, device is removed
779 * from device list and device memory is freed.
780 *
781 * @param dev device to be deleted
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100782 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +0100783 */
Feng Chenb5a1fca2023-04-23 17:01:37 +0800784#ifndef CONFIG_AML_MTDPART
Stefan Roese68d7d652009-03-19 13:30:36 +0100785static int device_del(struct mtd_device *dev)
786{
787 part_delall(&dev->parts);
788 list_del(&dev->link);
789 free(dev);
790
Stefan Roese76b58832009-05-16 12:04:22 +0200791 if (dev == current_mtd_dev) {
Stefan Roese68d7d652009-03-19 13:30:36 +0100792 /* we just deleted current device */
793 if (list_empty(&devices)) {
Stefan Roese76b58832009-05-16 12:04:22 +0200794 current_mtd_dev = NULL;
Stefan Roese68d7d652009-03-19 13:30:36 +0100795 } else {
796 /* reset first partition from first dev from the
797 * devices list as current */
Stefan Roese76b58832009-05-16 12:04:22 +0200798 current_mtd_dev = list_entry(devices.next, struct mtd_device, link);
799 current_mtd_partnum = 0;
Stefan Roese68d7d652009-03-19 13:30:36 +0100800 }
801 current_save();
802 return 0;
803 }
804
805 index_partitions();
806 return 0;
807}
Feng Chenb5a1fca2023-04-23 17:01:37 +0800808#endif
Stefan Roese68d7d652009-03-19 13:30:36 +0100809/**
810 * Search global device list and return pointer to the device of type and num
811 * specified.
812 *
813 * @param type device type
814 * @param num device number
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100815 * Return: NULL if requested device does not exist
Stefan Roese68d7d652009-03-19 13:30:36 +0100816 */
Anatolij Gustschin3c950e22010-03-16 17:10:05 +0100817struct mtd_device *device_find(u8 type, u8 num)
Stefan Roese68d7d652009-03-19 13:30:36 +0100818{
819 struct list_head *entry;
820 struct mtd_device *dev_tmp;
821
822 list_for_each(entry, &devices) {
823 dev_tmp = list_entry(entry, struct mtd_device, link);
824
825 if ((dev_tmp->id->type == type) && (dev_tmp->id->num == num))
826 return dev_tmp;
827 }
828
829 return NULL;
830}
831
Feng Chenb5a1fca2023-04-23 17:01:37 +0800832#ifndef CONFIG_AML_MTDPART
Stefan Roese68d7d652009-03-19 13:30:36 +0100833/**
834 * Add specified device to the global device list.
835 *
836 * @param dev device to be added
837 */
838static void device_add(struct mtd_device *dev)
839{
840 u8 current_save_needed = 0;
841
842 if (list_empty(&devices)) {
Stefan Roese76b58832009-05-16 12:04:22 +0200843 current_mtd_dev = dev;
844 current_mtd_partnum = 0;
Stefan Roese68d7d652009-03-19 13:30:36 +0100845 current_save_needed = 1;
846 }
847
848 list_add_tail(&dev->link, &devices);
849
850 if (current_save_needed > 0)
851 current_save();
852 else
853 index_partitions();
854}
855
856/**
857 * Parse device type, name and mtd-id. If syntax is ok allocate memory and
858 * return pointer to the device structure.
859 *
860 * @param mtd_dev pointer to the device definition string i.e. <mtd-dev>
861 * @param ret output pointer to next char after parse completes (output)
862 * @param retdev pointer to the allocated device (output)
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100863 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +0100864 */
865static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_device **retdev)
866{
867 struct mtd_device *dev;
868 struct part_info *part;
869 struct mtdids *id;
870 const char *mtd_id;
871 unsigned int mtd_id_len;
Wolfgang Denk419a1fe2011-10-05 22:48:12 +0200872 const char *p;
Wolfgang Denk419a1fe2011-10-05 22:48:12 +0200873 const char *pend;
Stefan Roese68d7d652009-03-19 13:30:36 +0100874 LIST_HEAD(tmp_list);
875 struct list_head *entry, *n;
876 u16 num_parts;
Paul Burton39ac3442013-09-04 15:16:57 +0100877 u64 offset;
Stefan Roese68d7d652009-03-19 13:30:36 +0100878 int err = 1;
879
Wolfgang Denke1d29502010-04-28 10:58:10 +0200880 debug("===device_parse===\n");
Stefan Roese68d7d652009-03-19 13:30:36 +0100881
Wolfgang Denk2697eff2010-04-28 10:53:47 +0200882 assert(retdev);
883 *retdev = NULL;
884
885 if (ret)
886 *ret = NULL;
887
Stefan Roese68d7d652009-03-19 13:30:36 +0100888 /* fetch <mtd-id> */
Wolfgang Denk2697eff2010-04-28 10:53:47 +0200889 mtd_id = p = mtd_dev;
Stefan Roese68d7d652009-03-19 13:30:36 +0100890 if (!(p = strchr(mtd_id, ':'))) {
891 printf("no <mtd-id> identifier\n");
892 return 1;
893 }
894 mtd_id_len = p - mtd_id + 1;
895 p++;
896
897 /* verify if we have a valid device specified */
898 if ((id = id_find_by_mtd_id(mtd_id, mtd_id_len - 1)) == NULL) {
899 printf("invalid mtd device '%.*s'\n", mtd_id_len - 1, mtd_id);
900 return 1;
901 }
902
Wolfgang Denk419a1fe2011-10-05 22:48:12 +0200903 pend = strchr(p, ';');
Wolfgang Denke1d29502010-04-28 10:58:10 +0200904 debug("dev type = %d (%s), dev num = %d, mtd-id = %s\n",
Stefan Roese68d7d652009-03-19 13:30:36 +0100905 id->type, MTD_DEV_TYPE(id->type),
906 id->num, id->mtd_id);
Vasili Galka5d69a5d2014-08-26 13:45:48 +0300907 debug("parsing partitions %.*s\n", (int)(pend ? pend - p : strlen(p)), p);
Stefan Roese68d7d652009-03-19 13:30:36 +0100908
Stefan Roese68d7d652009-03-19 13:30:36 +0100909 /* parse partitions */
910 num_parts = 0;
911
912 offset = 0;
913 if ((dev = device_find(id->type, id->num)) != NULL) {
914 /* if device already exists start at the end of the last partition */
915 part = list_entry(dev->parts.prev, struct part_info, link);
916 offset = part->offset + part->size;
917 }
918
919 while (p && (*p != '\0') && (*p != ';')) {
920 err = 1;
921 if ((part_parse(p, &p, &part) != 0) || (!part))
922 break;
923
924 /* calculate offset when not specified */
925 if (part->offset == OFFSET_NOT_SPECIFIED)
926 part->offset = offset;
927 else
928 offset = part->offset;
929
930 /* verify alignment and size */
931 if (part_validate(id, part) != 0)
932 break;
933
934 offset += part->size;
935
936 /* partition is ok, add it to the list */
937 list_add_tail(&part->link, &tmp_list);
938 num_parts++;
939 err = 0;
940 }
941 if (err == 1) {
942 part_delall(&tmp_list);
943 return 1;
944 }
945
Wolfgang Denke1d29502010-04-28 10:58:10 +0200946 debug("\ntotal partitions: %d\n", num_parts);
Stefan Roese68d7d652009-03-19 13:30:36 +0100947
948 /* check for next device presence */
949 if (p) {
950 if (*p == ';') {
Wolfgang Denk2697eff2010-04-28 10:53:47 +0200951 if (ret)
952 *ret = ++p;
Stefan Roese68d7d652009-03-19 13:30:36 +0100953 } else if (*p == '\0') {
Wolfgang Denk2697eff2010-04-28 10:53:47 +0200954 if (ret)
955 *ret = p;
Stefan Roese68d7d652009-03-19 13:30:36 +0100956 } else {
957 printf("unexpected character '%c' at the end of device\n", *p);
Wolfgang Denk2697eff2010-04-28 10:53:47 +0200958 if (ret)
959 *ret = NULL;
Stefan Roese68d7d652009-03-19 13:30:36 +0100960 return 1;
961 }
962 }
963
964 /* allocate memory for mtd_device structure */
965 if ((dev = (struct mtd_device *)malloc(sizeof(struct mtd_device))) == NULL) {
966 printf("out of memory\n");
967 return 1;
968 }
969 memset(dev, 0, sizeof(struct mtd_device));
970 dev->id = id;
971 dev->num_parts = 0; /* part_sort_add increments num_parts */
972 INIT_LIST_HEAD(&dev->parts);
973 INIT_LIST_HEAD(&dev->link);
974
975 /* move partitions from tmp_list to dev->parts */
976 list_for_each_safe(entry, n, &tmp_list) {
977 part = list_entry(entry, struct part_info, link);
978 list_del(entry);
979 if (part_sort_add(dev, part) != 0) {
980 device_del(dev);
981 return 1;
982 }
983 }
984
985 *retdev = dev;
986
Wolfgang Denke1d29502010-04-28 10:58:10 +0200987 debug("===\n\n");
Stefan Roese68d7d652009-03-19 13:30:36 +0100988 return 0;
989}
990
991/**
992 * Initialize global device list.
993 *
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100994 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +0100995 */
996static int mtd_devices_init(void)
997{
998 last_parts[0] = '\0';
Stefan Roese76b58832009-05-16 12:04:22 +0200999 current_mtd_dev = NULL;
Stefan Roese68d7d652009-03-19 13:30:36 +01001000 current_save();
1001
1002 return device_delall(&devices);
1003}
1004
1005/*
1006 * Search global mtdids list and find id of requested type and number.
1007 *
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001008 * Return: pointer to the id if it exists, NULL otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +01001009 */
1010static struct mtdids* id_find(u8 type, u8 num)
1011{
1012 struct list_head *entry;
1013 struct mtdids *id;
1014
1015 list_for_each(entry, &mtdids) {
1016 id = list_entry(entry, struct mtdids, link);
1017
1018 if ((id->type == type) && (id->num == num))
1019 return id;
1020 }
1021
1022 return NULL;
1023}
1024
1025/**
1026 * Search global mtdids list and find id of a requested mtd_id.
1027 *
1028 * Note: first argument is not null terminated.
1029 *
1030 * @param mtd_id string containing requested mtd_id
1031 * @param mtd_id_len length of supplied mtd_id
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001032 * Return: pointer to the id if it exists, NULL otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +01001033 */
1034static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len)
1035{
1036 struct list_head *entry;
1037 struct mtdids *id;
1038
Wolfgang Denke1d29502010-04-28 10:58:10 +02001039 debug("--- id_find_by_mtd_id: '%.*s' (len = %d)\n",
Stefan Roese68d7d652009-03-19 13:30:36 +01001040 mtd_id_len, mtd_id, mtd_id_len);
1041
1042 list_for_each(entry, &mtdids) {
1043 id = list_entry(entry, struct mtdids, link);
1044
Vasili Galka5d69a5d2014-08-26 13:45:48 +03001045 debug("entry: '%s' (len = %zu)\n",
Stefan Roese68d7d652009-03-19 13:30:36 +01001046 id->mtd_id, strlen(id->mtd_id));
1047
1048 if (mtd_id_len != strlen(id->mtd_id))
1049 continue;
1050 if (strncmp(id->mtd_id, mtd_id, mtd_id_len) == 0)
1051 return id;
1052 }
1053
1054 return NULL;
1055}
Feng Chenb5a1fca2023-04-23 17:01:37 +08001056#endif
Stefan Roese68d7d652009-03-19 13:30:36 +01001057
1058/**
Miquel Raynal00ac9222018-09-06 09:08:45 +02001059 * Parse device id string <dev-id> := 'nand'|'nor'|'onenand'|'spi-nand'<dev-num>,
Stefan Roese68d7d652009-03-19 13:30:36 +01001060 * return device type and number.
1061 *
1062 * @param id string describing device id
1063 * @param ret_id output pointer to next char after parse completes (output)
1064 * @param dev_type parsed device type (output)
1065 * @param dev_num parsed device number (output)
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001066 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +01001067 */
Kim Phillips088f1b12012-10-29 13:34:31 +00001068int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type,
1069 u8 *dev_num)
Stefan Roese68d7d652009-03-19 13:30:36 +01001070{
1071 const char *p = id;
1072
1073 *dev_type = 0;
1074 if (strncmp(p, "nand", 4) == 0) {
1075 *dev_type = MTD_DEV_TYPE_NAND;
1076 p += 4;
1077 } else if (strncmp(p, "nor", 3) == 0) {
1078 *dev_type = MTD_DEV_TYPE_NOR;
1079 p += 3;
1080 } else if (strncmp(p, "onenand", 7) == 0) {
1081 *dev_type = MTD_DEV_TYPE_ONENAND;
1082 p += 7;
Miquel Raynal00ac9222018-09-06 09:08:45 +02001083 } else if (strncmp(p, "spi-nand", 8) == 0) {
1084 *dev_type = MTD_DEV_TYPE_SPINAND;
1085 p += 8;
Stefan Roese68d7d652009-03-19 13:30:36 +01001086 } else {
1087 printf("incorrect device type in %s\n", id);
1088 return 1;
1089 }
1090
1091 if (!isdigit(*p)) {
1092 printf("incorrect device number in %s\n", id);
1093 return 1;
1094 }
1095
1096 *dev_num = simple_strtoul(p, (char **)&p, 0);
1097 if (ret_id)
1098 *ret_id = p;
1099 return 0;
1100}
Feng Chenb5a1fca2023-04-23 17:01:37 +08001101#ifndef CONFIG_AML_MTDPART
Stefan Roese68d7d652009-03-19 13:30:36 +01001102/**
1103 * Process all devices and generate corresponding mtdparts string describing
1104 * all partitions on all devices.
1105 *
1106 * @param buf output buffer holding generated mtdparts string (output)
1107 * @param buflen buffer size
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001108 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +01001109 */
1110static int generate_mtdparts(char *buf, u32 buflen)
1111{
1112 struct list_head *pentry, *dentry;
1113 struct mtd_device *dev;
1114 struct part_info *part, *prev_part;
1115 char *p = buf;
1116 char tmpbuf[32];
Paul Burton39ac3442013-09-04 15:16:57 +01001117 u64 size, offset;
1118 u32 len, part_cnt;
Stefan Roese68d7d652009-03-19 13:30:36 +01001119 u32 maxlen = buflen - 1;
1120
Wolfgang Denke1d29502010-04-28 10:58:10 +02001121 debug("--- generate_mtdparts ---\n");
Stefan Roese68d7d652009-03-19 13:30:36 +01001122
1123 if (list_empty(&devices)) {
1124 buf[0] = '\0';
1125 return 0;
1126 }
1127
Stefan Roese68d7d652009-03-19 13:30:36 +01001128 list_for_each(dentry, &devices) {
1129 dev = list_entry(dentry, struct mtd_device, link);
1130
1131 /* copy mtd_id */
1132 len = strlen(dev->id->mtd_id) + 1;
1133 if (len > maxlen)
1134 goto cleanup;
1135 memcpy(p, dev->id->mtd_id, len - 1);
1136 p += len - 1;
1137 *(p++) = ':';
1138 maxlen -= len;
1139
1140 /* format partitions */
1141 prev_part = NULL;
1142 part_cnt = 0;
1143 list_for_each(pentry, &dev->parts) {
1144 part = list_entry(pentry, struct part_info, link);
1145 size = part->size;
1146 offset = part->offset;
1147 part_cnt++;
1148
1149 /* partition size */
1150 memsize_format(tmpbuf, size);
1151 len = strlen(tmpbuf);
1152 if (len > maxlen)
1153 goto cleanup;
1154 memcpy(p, tmpbuf, len);
1155 p += len;
1156 maxlen -= len;
1157
1158
1159 /* add offset only when there is a gap between
1160 * partitions */
1161 if ((!prev_part && (offset != 0)) ||
1162 (prev_part && ((prev_part->offset + prev_part->size) != part->offset))) {
1163
1164 memsize_format(tmpbuf, offset);
1165 len = strlen(tmpbuf) + 1;
1166 if (len > maxlen)
1167 goto cleanup;
1168 *(p++) = '@';
1169 memcpy(p, tmpbuf, len - 1);
1170 p += len - 1;
1171 maxlen -= len;
1172 }
1173
1174 /* copy name only if user supplied */
1175 if(!part->auto_name) {
1176 len = strlen(part->name) + 2;
1177 if (len > maxlen)
1178 goto cleanup;
1179
1180 *(p++) = '(';
1181 memcpy(p, part->name, len - 2);
1182 p += len - 2;
1183 *(p++) = ')';
1184 maxlen -= len;
1185 }
1186
1187 /* ro mask flag */
1188 if (part->mask_flags && MTD_WRITEABLE_CMD) {
1189 len = 2;
1190 if (len > maxlen)
1191 goto cleanup;
1192 *(p++) = 'r';
1193 *(p++) = 'o';
1194 maxlen -= 2;
1195 }
1196
1197 /* print ',' separator if there are other partitions
1198 * following */
1199 if (dev->num_parts > part_cnt) {
1200 if (1 > maxlen)
1201 goto cleanup;
1202 *(p++) = ',';
1203 maxlen--;
1204 }
1205 prev_part = part;
1206 }
1207 /* print ';' separator if there are other devices following */
1208 if (dentry->next != &devices) {
1209 if (1 > maxlen)
1210 goto cleanup;
1211 *(p++) = ';';
1212 maxlen--;
1213 }
1214 }
1215
1216 /* we still have at least one char left, as we decremented maxlen at
1217 * the begining */
1218 *p = '\0';
1219
1220 return 0;
1221
1222cleanup:
1223 last_parts[0] = '\0';
1224 return 1;
1225}
1226
1227/**
1228 * Call generate_mtdparts to process all devices and generate corresponding
1229 * mtdparts string, save it in mtdparts environment variable.
1230 *
1231 * @param buf output buffer holding generated mtdparts string (output)
1232 * @param buflen buffer size
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001233 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +01001234 */
1235static int generate_mtdparts_save(char *buf, u32 buflen)
1236{
1237 int ret;
1238
1239 ret = generate_mtdparts(buf, buflen);
1240
1241 if ((buf[0] != '\0') && (ret == 0))
Simon Glass382bee52017-08-03 12:22:09 -06001242 env_set("mtdparts", buf);
Stefan Roese68d7d652009-03-19 13:30:36 +01001243 else
Simon Glass382bee52017-08-03 12:22:09 -06001244 env_set("mtdparts", NULL);
Stefan Roese68d7d652009-03-19 13:30:36 +01001245
1246 return ret;
1247}
Feng Chenb5a1fca2023-04-23 17:01:37 +08001248#endif
Ben Gardiner04ac3802010-08-31 17:48:02 -04001249#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
Stefan Roese68d7d652009-03-19 13:30:36 +01001250/**
Ben Gardiner04ac3802010-08-31 17:48:02 -04001251 * Get the net size (w/o bad blocks) of the given partition.
1252 *
1253 * @param mtd the mtd info
1254 * @param part the partition
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001255 * Return: the calculated net size of this partition
Stefan Roese68d7d652009-03-19 13:30:36 +01001256 */
Ben Gardiner04ac3802010-08-31 17:48:02 -04001257static uint64_t net_part_size(struct mtd_info *mtd, struct part_info *part)
1258{
Scott Wood36650ca2010-09-09 15:40:03 -05001259 uint64_t i, net_size = 0;
1260
Alexander Dahl672c5702019-10-30 16:53:55 +01001261 if (!mtd->_block_isbad)
Ben Gardiner04ac3802010-08-31 17:48:02 -04001262 return part->size;
1263
Ben Gardiner04ac3802010-08-31 17:48:02 -04001264 for (i = 0; i < part->size; i += mtd->erasesize) {
Alexander Dahl672c5702019-10-30 16:53:55 +01001265 if (!mtd->_block_isbad(mtd, part->offset + i))
Ben Gardiner04ac3802010-08-31 17:48:02 -04001266 net_size += mtd->erasesize;
1267 }
Scott Wood36650ca2010-09-09 15:40:03 -05001268
Ben Gardiner04ac3802010-08-31 17:48:02 -04001269 return net_size;
1270}
1271#endif
Feng Chenb5a1fca2023-04-23 17:01:37 +08001272#ifndef CONFIG_AML_MTDPART
Ben Gardiner04ac3802010-08-31 17:48:02 -04001273static void print_partition_table(void)
Stefan Roese68d7d652009-03-19 13:30:36 +01001274{
1275 struct list_head *dentry, *pentry;
1276 struct part_info *part;
1277 struct mtd_device *dev;
1278 int part_num;
1279
Stefan Roese68d7d652009-03-19 13:30:36 +01001280 list_for_each(dentry, &devices) {
1281 dev = list_entry(dentry, struct mtd_device, link);
Ben Gardiner04ac3802010-08-31 17:48:02 -04001282 /* list partitions for given device */
1283 part_num = 0;
1284#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
1285 struct mtd_info *mtd;
1286
1287 if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
1288 return;
1289
1290 printf("\ndevice %s%d <%s>, # parts = %d\n",
1291 MTD_DEV_TYPE(dev->id->type), dev->id->num,
1292 dev->id->mtd_id, dev->num_parts);
1293 printf(" #: name\t\tsize\t\tnet size\toffset\t\tmask_flags\n");
1294
1295 list_for_each(pentry, &dev->parts) {
1296 u32 net_size;
1297 char *size_note;
1298
1299 part = list_entry(pentry, struct part_info, link);
1300 net_size = net_part_size(mtd, part);
1301 size_note = part->size == net_size ? " " : " (!)";
Alexander Dahl672c5702019-10-30 16:53:55 +01001302 printf("%2d: %-20s0x%08llx\t0x%08x%s\t0x%08llx\t%d\n",
Ben Gardiner04ac3802010-08-31 17:48:02 -04001303 part_num, part->name, part->size,
1304 net_size, size_note, part->offset,
1305 part->mask_flags);
1306#else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
Stefan Roese68d7d652009-03-19 13:30:36 +01001307 printf("\ndevice %s%d <%s>, # parts = %d\n",
1308 MTD_DEV_TYPE(dev->id->type), dev->id->num,
1309 dev->id->mtd_id, dev->num_parts);
David Brownell08f077d2009-04-16 19:55:48 -07001310 printf(" #: name\t\tsize\t\toffset\t\tmask_flags\n");
Stefan Roese68d7d652009-03-19 13:30:36 +01001311
Stefan Roese68d7d652009-03-19 13:30:36 +01001312 list_for_each(pentry, &dev->parts) {
1313 part = list_entry(pentry, struct part_info, link);
Paul Burton39ac3442013-09-04 15:16:57 +01001314 printf("%2d: %-20s0x%08llx\t0x%08llx\t%d\n",
Stefan Roese68d7d652009-03-19 13:30:36 +01001315 part_num, part->name, part->size,
1316 part->offset, part->mask_flags);
Ben Gardiner04ac3802010-08-31 17:48:02 -04001317#endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
Stefan Roese68d7d652009-03-19 13:30:36 +01001318 part_num++;
1319 }
1320 }
Ben Gardiner04ac3802010-08-31 17:48:02 -04001321
Stefan Roese68d7d652009-03-19 13:30:36 +01001322 if (list_empty(&devices))
1323 printf("no partitions defined\n");
Ben Gardiner04ac3802010-08-31 17:48:02 -04001324}
Feng Chenb5a1fca2023-04-23 17:01:37 +08001325#endif
1326#ifndef CONFIG_AML_MTDPART
Ben Gardiner04ac3802010-08-31 17:48:02 -04001327/**
1328 * Format and print out a partition list for each device from global device
1329 * list.
1330 */
1331static void list_partitions(void)
1332{
1333 struct part_info *part;
1334
1335 debug("\n---list_partitions---\n");
1336 print_partition_table();
Stefan Roese68d7d652009-03-19 13:30:36 +01001337
Stefan Roese76b58832009-05-16 12:04:22 +02001338 /* current_mtd_dev is not NULL only when we have non empty device list */
1339 if (current_mtd_dev) {
1340 part = mtd_part_info(current_mtd_dev, current_mtd_partnum);
Stefan Roese68d7d652009-03-19 13:30:36 +01001341 if (part) {
Paul Burton39ac3442013-09-04 15:16:57 +01001342 printf("\nactive partition: %s%d,%d - (%s) 0x%08llx @ 0x%08llx\n",
Stefan Roese76b58832009-05-16 12:04:22 +02001343 MTD_DEV_TYPE(current_mtd_dev->id->type),
1344 current_mtd_dev->id->num, current_mtd_partnum,
Stefan Roese68d7d652009-03-19 13:30:36 +01001345 part->name, part->size, part->offset);
1346 } else {
1347 printf("could not get current partition info\n\n");
1348 }
1349 }
1350
1351 printf("\ndefaults:\n");
Wolfgang Denk36c91692009-05-17 16:01:54 +02001352 printf("mtdids : %s\n",
1353 mtdids_default ? mtdids_default : "none");
Anatolij Gustschina6934472010-02-24 00:29:44 +01001354 /*
1355 * Using printf() here results in printbuffer overflow
1356 * if default mtdparts string is greater than console
1357 * printbuffer. Use puts() to prevent system crashes.
1358 */
1359 puts("mtdparts: ");
1360 puts(mtdparts_default ? mtdparts_default : "none");
1361 puts("\n");
Stefan Roese68d7d652009-03-19 13:30:36 +01001362}
Feng Chenb5a1fca2023-04-23 17:01:37 +08001363#endif
Stefan Roese68d7d652009-03-19 13:30:36 +01001364
1365/**
1366 * Given partition identifier in form of <dev_type><dev_num>,<part_num> find
1367 * corresponding device and verify partition number.
1368 *
1369 * @param id string describing device and partition or partition name
1370 * @param dev pointer to the requested device (output)
1371 * @param part_num verified partition number (output)
1372 * @param part pointer to requested partition (output)
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001373 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +01001374 */
Feng Chenb5a1fca2023-04-23 17:01:37 +08001375#ifndef CONFIG_AML_MTDPART
Stefan Roese68d7d652009-03-19 13:30:36 +01001376int find_dev_and_part(const char *id, struct mtd_device **dev,
1377 u8 *part_num, struct part_info **part)
1378{
1379 struct list_head *dentry, *pentry;
1380 u8 type, dnum, pnum;
1381 const char *p;
1382
Wolfgang Denke1d29502010-04-28 10:58:10 +02001383 debug("--- find_dev_and_part ---\nid = %s\n", id);
Stefan Roese68d7d652009-03-19 13:30:36 +01001384
1385 list_for_each(dentry, &devices) {
1386 *part_num = 0;
1387 *dev = list_entry(dentry, struct mtd_device, link);
1388 list_for_each(pentry, &(*dev)->parts) {
1389 *part = list_entry(pentry, struct part_info, link);
1390 if (strcmp((*part)->name, id) == 0)
1391 return 0;
1392 (*part_num)++;
1393 }
1394 }
1395
1396 p = id;
1397 *dev = NULL;
1398 *part = NULL;
1399 *part_num = 0;
1400
1401 if (mtd_id_parse(p, &p, &type, &dnum) != 0)
1402 return 1;
1403
1404 if ((*p++ != ',') || (*p == '\0')) {
1405 printf("no partition number specified\n");
1406 return 1;
1407 }
1408 pnum = simple_strtoul(p, (char **)&p, 0);
1409 if (*p != '\0') {
1410 printf("unexpected trailing character '%c'\n", *p);
1411 return 1;
1412 }
1413
1414 if ((*dev = device_find(type, dnum)) == NULL) {
1415 printf("no such device %s%d\n", MTD_DEV_TYPE(type), dnum);
1416 return 1;
1417 }
1418
1419 if ((*part = mtd_part_info(*dev, pnum)) == NULL) {
1420 printf("no such partition\n");
1421 *dev = NULL;
1422 return 1;
1423 }
1424
1425 *part_num = pnum;
1426
1427 return 0;
1428}
1429
Feng Chenb5a1fca2023-04-23 17:01:37 +08001430#else
1431int find_dev_and_part(const char *id, struct mtd_device **dev,
1432u8 *part_num, struct part_info **part)
1433{
1434 struct list_head *dentry, *pentry;
1435
1436 *part_num = 0;
1437 debug("--- find_dev_and_part ---\nid = %s\n", id);
1438 if (list_empty(&aml_device)) {
1439 pr_err("%s() %d: no valid part\n", __func__, __LINE__);
1440 return -1;
1441 }
1442
1443 list_for_each(dentry, &aml_device) {
1444 *part_num = 0;
1445 *dev = list_entry(dentry, struct mtd_device, link);
1446 list_for_each(pentry, &(*dev)->parts) {
1447 *part = list_entry(pentry, struct part_info, link);
1448 if (strcmp((*part)->name, id) == 0)
1449 return 0;
1450 (*part_num)++;
1451 }
1452 }
1453
1454 *dev = NULL;
1455 *part = NULL;
1456 *part_num = 0;
1457 return 1;
1458}
1459#endif
1460#ifndef CONFIG_AML_MTDPART
Stefan Roese68d7d652009-03-19 13:30:36 +01001461/**
1462 * Find and delete partition. For partition id format see find_dev_and_part().
1463 *
1464 * @param id string describing device and partition
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001465 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +01001466 */
1467static int delete_partition(const char *id)
1468{
1469 u8 pnum;
1470 struct mtd_device *dev;
1471 struct part_info *part;
1472
1473 if (find_dev_and_part(id, &dev, &pnum, &part) == 0) {
1474
Paul Burton39ac3442013-09-04 15:16:57 +01001475 debug("delete_partition: device = %s%d, partition %d = (%s) 0x%08llx@0x%08llx\n",
Stefan Roese68d7d652009-03-19 13:30:36 +01001476 MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum,
1477 part->name, part->size, part->offset);
1478
1479 if (part_del(dev, part) != 0)
1480 return 1;
1481
1482 if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
Robert P. J. Daya22bf162013-02-04 13:51:10 +00001483 printf("generated mtdparts too long, resetting to null\n");
Stefan Roese68d7d652009-03-19 13:30:36 +01001484 return 1;
1485 }
1486 return 0;
1487 }
1488
1489 printf("partition %s not found\n", id);
1490 return 1;
1491}
Feng Chenb5a1fca2023-04-23 17:01:37 +08001492#endif
Ben Gardinerca75b202010-08-31 17:48:03 -04001493#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
1494/**
1495 * Increase the size of the given partition so that it's net size is at least
1496 * as large as the size member and such that the next partition would start on a
1497 * good block if it were adjacent to this partition.
1498 *
1499 * @param mtd the mtd device
1500 * @param part the partition
1501 * @param next_offset pointer to the offset of the next partition after this
1502 * partition's size has been modified (output)
1503 */
1504static void spread_partition(struct mtd_info *mtd, struct part_info *part,
1505 uint64_t *next_offset)
1506{
1507 uint64_t net_size, padding_size = 0;
1508 int truncated;
1509
1510 mtd_get_len_incl_bad(mtd, part->offset, part->size, &net_size,
1511 &truncated);
1512
1513 /*
1514 * Absorb bad blocks immediately following this
1515 * partition also into the partition, such that
1516 * the next partition starts with a good block.
1517 */
1518 if (!truncated) {
1519 mtd_get_len_incl_bad(mtd, part->offset + net_size,
1520 mtd->erasesize, &padding_size, &truncated);
1521 if (truncated)
1522 padding_size = 0;
1523 else
1524 padding_size -= mtd->erasesize;
1525 }
1526
1527 if (truncated) {
1528 printf("truncated partition %s to %lld bytes\n", part->name,
1529 (uint64_t) net_size + padding_size);
1530 }
1531
1532 part->size = net_size + padding_size;
1533 *next_offset = part->offset + part->size;
1534}
1535
1536/**
1537 * Adjust all of the partition sizes, such that all partitions are at least
1538 * as big as their mtdparts environment variable sizes and they each start
1539 * on a good block.
1540 *
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001541 * Return: 0 on success, 1 otherwise
Ben Gardinerca75b202010-08-31 17:48:03 -04001542 */
1543static int spread_partitions(void)
1544{
1545 struct list_head *dentry, *pentry;
1546 struct mtd_device *dev;
1547 struct part_info *part;
1548 struct mtd_info *mtd;
1549 int part_num;
1550 uint64_t cur_offs;
1551
1552 list_for_each(dentry, &devices) {
1553 dev = list_entry(dentry, struct mtd_device, link);
1554
1555 if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
1556 return 1;
1557
1558 part_num = 0;
1559 cur_offs = 0;
1560 list_for_each(pentry, &dev->parts) {
1561 part = list_entry(pentry, struct part_info, link);
1562
1563 debug("spread_partitions: device = %s%d, partition %d ="
Steve Rae59441ac2016-06-29 13:50:59 -07001564 " (%s) 0x%08llx@0x%08llx\n",
Ben Gardinerca75b202010-08-31 17:48:03 -04001565 MTD_DEV_TYPE(dev->id->type), dev->id->num,
1566 part_num, part->name, part->size,
1567 part->offset);
1568
1569 if (cur_offs > part->offset)
1570 part->offset = cur_offs;
1571
1572 spread_partition(mtd, part, &cur_offs);
1573
1574 part_num++;
1575 }
1576 }
1577
1578 index_partitions();
1579
1580 if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
Robert P. J. Daya22bf162013-02-04 13:51:10 +00001581 printf("generated mtdparts too long, resetting to null\n");
Ben Gardinerca75b202010-08-31 17:48:03 -04001582 return 1;
1583 }
1584 return 0;
1585}
1586#endif /* CONFIG_CMD_MTDPARTS_SPREAD */
1587
Stefan Roese68d7d652009-03-19 13:30:36 +01001588/**
Ladislav Michl1c2a2622016-07-12 20:28:24 +02001589 * The mtdparts variable tends to be long. If we need to access it
1590 * before the env is relocated, then we need to use our own stack
1591 * buffer. gd->env_buf will be too small.
1592 *
1593 * @param buf temporary buffer pointer MTDPARTS_MAXLEN long
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001594 * Return: mtdparts variable string, NULL if not found
Ladislav Michl1c2a2622016-07-12 20:28:24 +02001595 */
Feng Chenb5a1fca2023-04-23 17:01:37 +08001596#ifndef CONFIG_AML_MTDPART
Simon Glass723806c2017-08-03 12:22:15 -06001597static const char *env_get_mtdparts(char *buf)
Ladislav Michl1c2a2622016-07-12 20:28:24 +02001598{
1599 if (gd->flags & GD_FLG_ENV_READY)
Simon Glass00caae62017-08-03 12:22:12 -06001600 return env_get("mtdparts");
1601 if (env_get_f("mtdparts", buf, MTDPARTS_MAXLEN) != -1)
Ladislav Michl1c2a2622016-07-12 20:28:24 +02001602 return buf;
1603 return NULL;
1604}
1605
1606/**
Stefan Roese68d7d652009-03-19 13:30:36 +01001607 * Accept character string describing mtd partitions and call device_parse()
1608 * for each entry. Add created devices to the global devices list.
1609 *
1610 * @param mtdparts string specifing mtd partitions
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001611 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +01001612 */
1613static int parse_mtdparts(const char *const mtdparts)
1614{
Ladislav Michl06a040a2016-07-12 20:28:23 +02001615 const char *p;
Stefan Roese68d7d652009-03-19 13:30:36 +01001616 struct mtd_device *dev;
1617 int err = 1;
Joe Hershbergera7eb1d62013-04-08 10:32:50 +00001618 char tmp_parts[MTDPARTS_MAXLEN];
Stefan Roese68d7d652009-03-19 13:30:36 +01001619
Lothar Waßmann1aca4d52017-06-08 14:04:03 +02001620 debug("\n---parse_mtdparts---\nmtdparts = %s\n\n", mtdparts);
Stefan Roese68d7d652009-03-19 13:30:36 +01001621
1622 /* delete all devices and partitions */
1623 if (mtd_devices_init() != 0) {
1624 printf("could not initialise device list\n");
1625 return err;
1626 }
1627
1628 /* re-read 'mtdparts' variable, mtd_devices_init may be updating env */
Simon Glass723806c2017-08-03 12:22:15 -06001629 p = env_get_mtdparts(tmp_parts);
Ladislav Michl06a040a2016-07-12 20:28:23 +02001630 if (!p)
1631 p = mtdparts;
1632
Miquel Raynald60aea92018-09-06 09:08:46 +02001633 /* Skip the useless prefix, if any */
1634 if (strncmp(p, "mtdparts=", 9) == 0)
1635 p += 9;
Stefan Roese68d7d652009-03-19 13:30:36 +01001636
Ladislav Michl06a040a2016-07-12 20:28:23 +02001637 while (*p != '\0') {
Stefan Roese68d7d652009-03-19 13:30:36 +01001638 err = 1;
1639 if ((device_parse(p, &p, &dev) != 0) || (!dev))
1640 break;
1641
Wolfgang Denke1d29502010-04-28 10:58:10 +02001642 debug("+ device: %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
Stefan Roese68d7d652009-03-19 13:30:36 +01001643 dev->id->num, dev->id->mtd_id);
1644
1645 /* check if parsed device is already on the list */
1646 if (device_find(dev->id->type, dev->id->num) != NULL) {
1647 printf("device %s%d redefined, please correct mtdparts variable\n",
1648 MTD_DEV_TYPE(dev->id->type), dev->id->num);
1649 break;
1650 }
1651
1652 list_add_tail(&dev->link, &devices);
1653 err = 0;
1654 }
Tom Rini54f17922017-08-14 20:42:27 -04001655 if (err == 1) {
1656 free(dev);
Stefan Roese68d7d652009-03-19 13:30:36 +01001657 device_delall(&devices);
Tom Rini54f17922017-08-14 20:42:27 -04001658 }
Stefan Roese68d7d652009-03-19 13:30:36 +01001659
Ladislav Michl06a040a2016-07-12 20:28:23 +02001660 return err;
Stefan Roese68d7d652009-03-19 13:30:36 +01001661}
Feng Chenb5a1fca2023-04-23 17:01:37 +08001662#endif
Stefan Roese68d7d652009-03-19 13:30:36 +01001663
1664/**
1665 * Parse provided string describing mtdids mapping (see file header for mtdids
1666 * variable format). Allocate memory for each entry and add all found entries
1667 * to the global mtdids list.
1668 *
1669 * @param ids mapping string
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001670 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +01001671 */
Feng Chenb5a1fca2023-04-23 17:01:37 +08001672#ifndef CONFIG_AML_MTDPART
Stefan Roese68d7d652009-03-19 13:30:36 +01001673static int parse_mtdids(const char *const ids)
1674{
1675 const char *p = ids;
1676 const char *mtd_id;
1677 int mtd_id_len;
1678 struct mtdids *id;
1679 struct list_head *entry, *n;
1680 struct mtdids *id_tmp;
1681 u8 type, num;
Paul Burton39ac3442013-09-04 15:16:57 +01001682 u64 size;
Stefan Roese68d7d652009-03-19 13:30:36 +01001683 int ret = 1;
1684
Wolfgang Denke1d29502010-04-28 10:58:10 +02001685 debug("\n---parse_mtdids---\nmtdids = %s\n\n", ids);
Stefan Roese68d7d652009-03-19 13:30:36 +01001686
1687 /* clean global mtdids list */
1688 list_for_each_safe(entry, n, &mtdids) {
1689 id_tmp = list_entry(entry, struct mtdids, link);
Wolfgang Denke1d29502010-04-28 10:58:10 +02001690 debug("mtdids del: %d %d\n", id_tmp->type, id_tmp->num);
Stefan Roese68d7d652009-03-19 13:30:36 +01001691 list_del(entry);
1692 free(id_tmp);
1693 }
1694 last_ids[0] = '\0';
1695 INIT_LIST_HEAD(&mtdids);
1696
1697 while(p && (*p != '\0')) {
1698
1699 ret = 1;
Miquel Raynal00ac9222018-09-06 09:08:45 +02001700 /* parse 'nor'|'nand'|'onenand'|'spi-nand'<dev-num> */
Stefan Roese68d7d652009-03-19 13:30:36 +01001701 if (mtd_id_parse(p, &p, &type, &num) != 0)
1702 break;
1703
1704 if (*p != '=') {
1705 printf("mtdids: incorrect <dev-num>\n");
1706 break;
1707 }
1708 p++;
1709
1710 /* check if requested device exists */
1711 if (mtd_device_validate(type, num, &size) != 0)
1712 return 1;
1713
1714 /* locate <mtd-id> */
1715 mtd_id = p;
1716 if ((p = strchr(mtd_id, ',')) != NULL) {
1717 mtd_id_len = p - mtd_id + 1;
1718 p++;
1719 } else {
1720 mtd_id_len = strlen(mtd_id) + 1;
1721 }
1722 if (mtd_id_len == 0) {
1723 printf("mtdids: no <mtd-id> identifier\n");
1724 break;
1725 }
1726
1727 /* check if this id is already on the list */
1728 int double_entry = 0;
1729 list_for_each(entry, &mtdids) {
1730 id_tmp = list_entry(entry, struct mtdids, link);
1731 if ((id_tmp->type == type) && (id_tmp->num == num)) {
1732 double_entry = 1;
1733 break;
1734 }
1735 }
1736 if (double_entry) {
1737 printf("device id %s%d redefined, please correct mtdids variable\n",
1738 MTD_DEV_TYPE(type), num);
1739 break;
1740 }
1741
1742 /* allocate mtdids structure */
1743 if (!(id = (struct mtdids *)malloc(sizeof(struct mtdids) + mtd_id_len))) {
1744 printf("out of memory\n");
1745 break;
1746 }
1747 memset(id, 0, sizeof(struct mtdids) + mtd_id_len);
1748 id->num = num;
1749 id->type = type;
1750 id->size = size;
1751 id->mtd_id = (char *)(id + 1);
1752 strncpy(id->mtd_id, mtd_id, mtd_id_len - 1);
1753 id->mtd_id[mtd_id_len - 1] = '\0';
1754 INIT_LIST_HEAD(&id->link);
1755
Paul Burton39ac3442013-09-04 15:16:57 +01001756 debug("+ id %s%d\t%16lld bytes\t%s\n",
Stefan Roese68d7d652009-03-19 13:30:36 +01001757 MTD_DEV_TYPE(id->type), id->num,
1758 id->size, id->mtd_id);
1759
1760 list_add_tail(&id->link, &mtdids);
1761 ret = 0;
1762 }
1763 if (ret == 1) {
1764 /* clean mtdids list and free allocated memory */
1765 list_for_each_safe(entry, n, &mtdids) {
1766 id_tmp = list_entry(entry, struct mtdids, link);
1767 list_del(entry);
1768 free(id_tmp);
1769 }
1770 return 1;
1771 }
1772
1773 return 0;
1774}
Feng Chenb5a1fca2023-04-23 17:01:37 +08001775#endif
Ladislav Michl1c2a2622016-07-12 20:28:24 +02001776
Stefan Roese68d7d652009-03-19 13:30:36 +01001777/**
1778 * Parse and initialize global mtdids mapping and create global
1779 * device/partition list.
1780 *
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001781 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +01001782 */
Feng Chenb5a1fca2023-04-23 17:01:37 +08001783#ifndef CONFIG_AML_MTDPART
Stefan Roese68d7d652009-03-19 13:30:36 +01001784int mtdparts_init(void)
1785{
1786 static int initialized = 0;
1787 const char *ids, *parts;
1788 const char *current_partition;
1789 int ids_changed;
Tom Rinibc028342017-08-20 20:05:40 -04001790 char tmp_ep[PARTITION_MAXLEN + 1];
Joe Hershbergera7eb1d62013-04-08 10:32:50 +00001791 char tmp_parts[MTDPARTS_MAXLEN];
Stefan Roese68d7d652009-03-19 13:30:36 +01001792
Wolfgang Denke1d29502010-04-28 10:58:10 +02001793 debug("\n---mtdparts_init---\n");
Stefan Roese68d7d652009-03-19 13:30:36 +01001794 if (!initialized) {
1795 INIT_LIST_HEAD(&mtdids);
1796 INIT_LIST_HEAD(&devices);
Tom Rini54f17922017-08-14 20:42:27 -04001797 memset(last_ids, 0, sizeof(last_ids));
1798 memset(last_parts, 0, sizeof(last_parts));
1799 memset(last_partition, 0, sizeof(last_partition));
Ladislav Michlaf324432016-07-12 20:28:26 +02001800#if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
1801 board_mtdparts_default(&mtdids_default, &mtdparts_default);
1802#endif
Ladislav Michlf8f744a2016-07-12 20:28:25 +02001803 use_defaults = 1;
Stefan Roese68d7d652009-03-19 13:30:36 +01001804 initialized = 1;
1805 }
1806
1807 /* get variables */
Simon Glass00caae62017-08-03 12:22:12 -06001808 ids = env_get("mtdids");
Simon Glass723806c2017-08-03 12:22:15 -06001809 parts = env_get_mtdparts(tmp_parts);
Simon Glass00caae62017-08-03 12:22:12 -06001810 current_partition = env_get("partition");
Stefan Roese68d7d652009-03-19 13:30:36 +01001811
1812 /* save it for later parsing, cannot rely on current partition pointer
1813 * as 'partition' variable may be updated during init */
Tom Rinibc028342017-08-20 20:05:40 -04001814 memset(tmp_parts, 0, sizeof(tmp_parts));
Tom Rini8b3cec72017-08-26 16:59:24 -04001815 memset(tmp_ep, 0, sizeof(tmp_ep));
Stefan Roese68d7d652009-03-19 13:30:36 +01001816 if (current_partition)
1817 strncpy(tmp_ep, current_partition, PARTITION_MAXLEN);
1818
Wolfgang Denke1d29502010-04-28 10:58:10 +02001819 debug("last_ids : %s\n", last_ids);
1820 debug("env_ids : %s\n", ids);
1821 debug("last_parts: %s\n", last_parts);
1822 debug("env_parts : %s\n\n", parts);
Stefan Roese68d7d652009-03-19 13:30:36 +01001823
Wolfgang Denke1d29502010-04-28 10:58:10 +02001824 debug("last_partition : %s\n", last_partition);
1825 debug("env_partition : %s\n", current_partition);
Stefan Roese68d7d652009-03-19 13:30:36 +01001826
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -04001827 /* if mtdids variable is empty try to use defaults */
Stefan Roese68d7d652009-03-19 13:30:36 +01001828 if (!ids) {
1829 if (mtdids_default) {
Wolfgang Denke1d29502010-04-28 10:58:10 +02001830 debug("mtdids variable not defined, using default\n");
Stefan Roese68d7d652009-03-19 13:30:36 +01001831 ids = mtdids_default;
Simon Glass382bee52017-08-03 12:22:09 -06001832 env_set("mtdids", (char *)ids);
Stefan Roese68d7d652009-03-19 13:30:36 +01001833 } else {
1834 printf("mtdids not defined, no default present\n");
1835 return 1;
1836 }
1837 }
1838 if (strlen(ids) > MTDIDS_MAXLEN - 1) {
1839 printf("mtdids too long (> %d)\n", MTDIDS_MAXLEN);
1840 return 1;
1841 }
1842
Ladislav Michlf8f744a2016-07-12 20:28:25 +02001843 /* use defaults when mtdparts variable is not defined
1844 * once mtdparts is saved environment, drop use_defaults flag */
1845 if (!parts) {
1846 if (mtdparts_default && use_defaults) {
1847 parts = mtdparts_default;
Simon Glass382bee52017-08-03 12:22:09 -06001848 if (env_set("mtdparts", (char *)parts) == 0)
Ladislav Michlf8f744a2016-07-12 20:28:25 +02001849 use_defaults = 0;
1850 } else
1851 printf("mtdparts variable not set, see 'help mtdparts'\n");
1852 }
Stefan Roese68d7d652009-03-19 13:30:36 +01001853
1854 if (parts && (strlen(parts) > MTDPARTS_MAXLEN - 1)) {
1855 printf("mtdparts too long (> %d)\n", MTDPARTS_MAXLEN);
1856 return 1;
1857 }
1858
1859 /* check if we have already parsed those mtdids */
1860 if ((last_ids[0] != '\0') && (strcmp(last_ids, ids) == 0)) {
1861 ids_changed = 0;
1862 } else {
1863 ids_changed = 1;
1864
1865 if (parse_mtdids(ids) != 0) {
1866 mtd_devices_init();
1867 return 1;
1868 }
1869
1870 /* ok it's good, save new ids */
1871 strncpy(last_ids, ids, MTDIDS_MAXLEN);
1872 }
1873
1874 /* parse partitions if either mtdparts or mtdids were updated */
1875 if (parts && ((last_parts[0] == '\0') || ((strcmp(last_parts, parts) != 0)) || ids_changed)) {
1876 if (parse_mtdparts(parts) != 0)
1877 return 1;
1878
1879 if (list_empty(&devices)) {
1880 printf("mtdparts_init: no valid partitions\n");
1881 return 1;
1882 }
1883
1884 /* ok it's good, save new parts */
1885 strncpy(last_parts, parts, MTDPARTS_MAXLEN);
1886
1887 /* reset first partition from first dev from the list as current */
Stefan Roese76b58832009-05-16 12:04:22 +02001888 current_mtd_dev = list_entry(devices.next, struct mtd_device, link);
1889 current_mtd_partnum = 0;
Stefan Roese68d7d652009-03-19 13:30:36 +01001890 current_save();
1891
Wolfgang Denke1d29502010-04-28 10:58:10 +02001892 debug("mtdparts_init: current_mtd_dev = %s%d, current_mtd_partnum = %d\n",
Stefan Roese76b58832009-05-16 12:04:22 +02001893 MTD_DEV_TYPE(current_mtd_dev->id->type),
1894 current_mtd_dev->id->num, current_mtd_partnum);
Stefan Roese68d7d652009-03-19 13:30:36 +01001895 }
1896
1897 /* mtdparts variable was reset to NULL, delete all devices/partitions */
1898 if (!parts && (last_parts[0] != '\0'))
1899 return mtd_devices_init();
1900
1901 /* do not process current partition if mtdparts variable is null */
1902 if (!parts)
1903 return 0;
1904
1905 /* is current partition set in environment? if so, use it */
1906 if ((tmp_ep[0] != '\0') && (strcmp(tmp_ep, last_partition) != 0)) {
1907 struct part_info *p;
1908 struct mtd_device *cdev;
1909 u8 pnum;
1910
Wolfgang Denke1d29502010-04-28 10:58:10 +02001911 debug("--- getting current partition: %s\n", tmp_ep);
Stefan Roese68d7d652009-03-19 13:30:36 +01001912
1913 if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) {
Stefan Roese76b58832009-05-16 12:04:22 +02001914 current_mtd_dev = cdev;
1915 current_mtd_partnum = pnum;
Stefan Roese68d7d652009-03-19 13:30:36 +01001916 current_save();
1917 }
Simon Glass00caae62017-08-03 12:22:12 -06001918 } else if (env_get("partition") == NULL) {
Wolfgang Denke1d29502010-04-28 10:58:10 +02001919 debug("no partition variable set, setting...\n");
Stefan Roese68d7d652009-03-19 13:30:36 +01001920 current_save();
1921 }
1922
1923 return 0;
1924}
Feng Chenb5a1fca2023-04-23 17:01:37 +08001925#endif
Stefan Roese68d7d652009-03-19 13:30:36 +01001926/**
1927 * Return pointer to the partition of a requested number from a requested
1928 * device.
1929 *
1930 * @param dev device that is to be searched for a partition
1931 * @param part_num requested partition number
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001932 * Return: pointer to the part_info, NULL otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +01001933 */
1934static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num)
1935{
1936 struct list_head *entry;
1937 struct part_info *part;
1938 int num;
1939
1940 if (!dev)
1941 return NULL;
1942
Wolfgang Denke1d29502010-04-28 10:58:10 +02001943 debug("\n--- mtd_part_info: partition number %d for device %s%d (%s)\n",
Stefan Roese68d7d652009-03-19 13:30:36 +01001944 part_num, MTD_DEV_TYPE(dev->id->type),
1945 dev->id->num, dev->id->mtd_id);
1946
1947 if (part_num >= dev->num_parts) {
1948 printf("invalid partition number %d for device %s%d (%s)\n",
1949 part_num, MTD_DEV_TYPE(dev->id->type),
1950 dev->id->num, dev->id->mtd_id);
1951 return NULL;
1952 }
1953
1954 /* locate partition number, return it */
1955 num = 0;
1956 list_for_each(entry, &dev->parts) {
1957 part = list_entry(entry, struct part_info, link);
1958
1959 if (part_num == num++) {
1960 return part;
1961 }
1962 }
1963
1964 return NULL;
1965}
1966
1967/***************************************************/
Bin Menga1875592016-02-05 19:30:11 -08001968/* U-Boot commands */
Stefan Roese68d7d652009-03-19 13:30:36 +01001969/***************************************************/
1970/* command line only */
1971/**
1972 * Routine implementing u-boot chpart command. Sets new current partition based
1973 * on the user supplied partition id. For partition id format see find_dev_and_part().
1974 *
1975 * @param cmdtp command internal data
1976 * @param flag command flag
1977 * @param argc number of arguments supplied to the command
1978 * @param argv arguments list
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01001979 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +01001980 */
Simon Glass09140112020-05-10 11:40:03 -06001981static int do_chpart(struct cmd_tbl *cmdtp, int flag, int argc,
1982 char *const argv[])
Stefan Roese68d7d652009-03-19 13:30:36 +01001983{
1984/* command line only */
1985 struct mtd_device *dev;
1986 struct part_info *part;
1987 u8 pnum;
1988
1989 if (mtdparts_init() !=0)
1990 return 1;
1991
1992 if (argc < 2) {
1993 printf("no partition id specified\n");
1994 return 1;
1995 }
1996
1997 if (find_dev_and_part(argv[1], &dev, &pnum, &part) != 0)
1998 return 1;
1999
Stefan Roese76b58832009-05-16 12:04:22 +02002000 current_mtd_dev = dev;
2001 current_mtd_partnum = pnum;
Stefan Roese68d7d652009-03-19 13:30:36 +01002002 current_save();
2003
2004 printf("partition changed to %s%d,%d\n",
2005 MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum);
2006
2007 return 0;
2008}
2009
2010/**
2011 * Routine implementing u-boot mtdparts command. Initialize/update default global
2012 * partition list and process user partition request (list, add, del).
2013 *
2014 * @param cmdtp command internal data
2015 * @param flag command flag
2016 * @param argc number of arguments supplied to the command
2017 * @param argv arguments list
Heinrich Schuchardt185f8122022-01-19 18:05:50 +01002018 * Return: 0 on success, 1 otherwise
Stefan Roese68d7d652009-03-19 13:30:36 +01002019 */
Simon Glass09140112020-05-10 11:40:03 -06002020static int do_mtdparts(struct cmd_tbl *cmdtp, int flag, int argc,
2021 char *const argv[])
Stefan Roese68d7d652009-03-19 13:30:36 +01002022{
Feng Chenb5a1fca2023-04-23 17:01:37 +08002023#ifdef CONFIG_AML_MTDPART
2024 if (argc > 1)
2025 printf("we are not support cmd mtdparts operates\n");
2026 return 0;
2027#else
Stefan Roese68d7d652009-03-19 13:30:36 +01002028 if (argc == 2) {
2029 if (strcmp(argv[1], "default") == 0) {
Simon Glass382bee52017-08-03 12:22:09 -06002030 env_set("mtdids", NULL);
2031 env_set("mtdparts", NULL);
2032 env_set("partition", NULL);
Ladislav Michlf8f744a2016-07-12 20:28:25 +02002033 use_defaults = 1;
Stefan Roese68d7d652009-03-19 13:30:36 +01002034
2035 mtdparts_init();
2036 return 0;
2037 } else if (strcmp(argv[1], "delall") == 0) {
2038 /* this may be the first run, initialize lists if needed */
2039 mtdparts_init();
2040
Simon Glass382bee52017-08-03 12:22:09 -06002041 env_set("mtdparts", NULL);
Stefan Roese68d7d652009-03-19 13:30:36 +01002042
2043 /* mtd_devices_init() calls current_save() */
2044 return mtd_devices_init();
2045 }
2046 }
2047
2048 /* make sure we are in sync with env variables */
2049 if (mtdparts_init() != 0)
2050 return 1;
2051
2052 if (argc == 1) {
2053 list_partitions();
2054 return 0;
2055 }
2056
2057 /* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */
Ben Gardiner59a50d22010-08-31 17:48:04 -04002058 if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) {
Stefan Roese68d7d652009-03-19 13:30:36 +01002059#define PART_ADD_DESC_MAXLEN 64
2060 char tmpbuf[PART_ADD_DESC_MAXLEN];
Ben Gardiner59a50d22010-08-31 17:48:04 -04002061#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
2062 struct mtd_info *mtd;
2063 uint64_t next_offset;
2064#endif
Stefan Roese68d7d652009-03-19 13:30:36 +01002065 u8 type, num, len;
2066 struct mtd_device *dev;
2067 struct mtd_device *dev_tmp;
2068 struct mtdids *id;
2069 struct part_info *p;
2070
2071 if (mtd_id_parse(argv[2], NULL, &type, &num) != 0)
2072 return 1;
2073
2074 if ((id = id_find(type, num)) == NULL) {
2075 printf("no such device %s defined in mtdids variable\n", argv[2]);
2076 return 1;
2077 }
2078
2079 len = strlen(id->mtd_id) + 1; /* 'mtd_id:' */
2080 len += strlen(argv[3]); /* size@offset */
2081 len += strlen(argv[4]) + 2; /* '(' name ')' */
2082 if (argv[5] && (strlen(argv[5]) == 2))
2083 len += 2; /* 'ro' */
2084
2085 if (len >= PART_ADD_DESC_MAXLEN) {
2086 printf("too long partition description\n");
2087 return 1;
2088 }
2089 sprintf(tmpbuf, "%s:%s(%s)%s",
2090 id->mtd_id, argv[3], argv[4], argv[5] ? argv[5] : "");
Wolfgang Denke1d29502010-04-28 10:58:10 +02002091 debug("add tmpbuf: %s\n", tmpbuf);
Stefan Roese68d7d652009-03-19 13:30:36 +01002092
2093 if ((device_parse(tmpbuf, NULL, &dev) != 0) || (!dev))
2094 return 1;
2095
Wolfgang Denke1d29502010-04-28 10:58:10 +02002096 debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
Stefan Roese68d7d652009-03-19 13:30:36 +01002097 dev->id->num, dev->id->mtd_id);
2098
Ben Gardiner59a50d22010-08-31 17:48:04 -04002099 p = list_entry(dev->parts.next, struct part_info, link);
2100
2101#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
2102 if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
2103 return 1;
2104
2105 if (!strcmp(&argv[1][3], ".spread")) {
2106 spread_partition(mtd, p, &next_offset);
Steve Rae59441ac2016-06-29 13:50:59 -07002107 debug("increased %s to %llu bytes\n", p->name, p->size);
Ben Gardiner59a50d22010-08-31 17:48:04 -04002108 }
2109#endif
2110
2111 dev_tmp = device_find(dev->id->type, dev->id->num);
2112 if (dev_tmp == NULL) {
Stefan Roese68d7d652009-03-19 13:30:36 +01002113 device_add(dev);
Ben Gardiner59a50d22010-08-31 17:48:04 -04002114 } else if (part_add(dev_tmp, p) != 0) {
Stefan Roese68d7d652009-03-19 13:30:36 +01002115 /* merge new partition with existing ones*/
Ben Gardiner59a50d22010-08-31 17:48:04 -04002116 device_del(dev);
2117 return 1;
Stefan Roese68d7d652009-03-19 13:30:36 +01002118 }
2119
2120 if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
Robert P. J. Daya22bf162013-02-04 13:51:10 +00002121 printf("generated mtdparts too long, resetting to null\n");
Stefan Roese68d7d652009-03-19 13:30:36 +01002122 return 1;
2123 }
2124
2125 return 0;
2126 }
2127
2128 /* mtdparts del part-id */
2129 if ((argc == 3) && (strcmp(argv[1], "del") == 0)) {
Wolfgang Denke1d29502010-04-28 10:58:10 +02002130 debug("del: part-id = %s\n", argv[2]);
Stefan Roese68d7d652009-03-19 13:30:36 +01002131
2132 return delete_partition(argv[2]);
2133 }
2134
Ben Gardinerca75b202010-08-31 17:48:03 -04002135#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
2136 if ((argc == 2) && (strcmp(argv[1], "spread") == 0))
2137 return spread_partitions();
2138#endif /* CONFIG_CMD_MTDPARTS_SPREAD */
Feng Chenb5a1fca2023-04-23 17:01:37 +08002139#endif
Simon Glass4c12eeb2011-12-10 08:44:01 +00002140 return CMD_RET_USAGE;
Stefan Roese68d7d652009-03-19 13:30:36 +01002141}
2142
2143/***************************************************/
2144U_BOOT_CMD(
2145 chpart, 2, 0, do_chpart,
Adarsh Babu Kalepalli1fdafeb2021-05-24 13:35:39 +05302146 "change active partition of a MTD device",
Stefan Roese68d7d652009-03-19 13:30:36 +01002147 "part-id\n"
Adarsh Babu Kalepalli1fdafeb2021-05-24 13:35:39 +05302148 " - change active partition (e.g. part-id = nand0,1) of a MTD device"
Stefan Roese68d7d652009-03-19 13:30:36 +01002149);
2150
Kim Phillips088f1b12012-10-29 13:34:31 +00002151#ifdef CONFIG_SYS_LONGHELP
2152static char mtdparts_help_text[] =
Stefan Roese68d7d652009-03-19 13:30:36 +01002153 "\n"
2154 " - list partition table\n"
2155 "mtdparts delall\n"
2156 " - delete all partitions\n"
2157 "mtdparts del part-id\n"
2158 " - delete partition (e.g. part-id = nand0,1)\n"
2159 "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n"
2160 " - add partition\n"
Ben Gardiner59a50d22010-08-31 17:48:04 -04002161#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
2162 "mtdparts add.spread <mtd-dev> <size>[@<offset>] [<name>] [ro]\n"
2163 " - add partition, padding size by skipping bad blocks\n"
2164#endif
Stefan Roese68d7d652009-03-19 13:30:36 +01002165 "mtdparts default\n"
Ben Gardinerca75b202010-08-31 17:48:03 -04002166 " - reset partition table to defaults\n"
2167#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
2168 "mtdparts spread\n"
2169 " - adjust the sizes of the partitions so they are\n"
2170 " at least as big as the mtdparts variable specifies\n"
2171 " and they each start on a good block\n\n"
2172#else
2173 "\n"
2174#endif /* CONFIG_CMD_MTDPARTS_SPREAD */
Stefan Roese68d7d652009-03-19 13:30:36 +01002175 "-----\n\n"
2176 "this command uses three environment variables:\n\n"
2177 "'partition' - keeps current partition identifier\n\n"
2178 "partition := <part-id>\n"
2179 "<part-id> := <dev-id>,part_num\n\n"
2180 "'mtdids' - linux kernel mtd device id <-> u-boot device id mapping\n\n"
2181 "mtdids=<idmap>[,<idmap>,...]\n\n"
2182 "<idmap> := <dev-id>=<mtd-id>\n"
Miquel Raynal00ac9222018-09-06 09:08:45 +02002183 "<dev-id> := 'nand'|'nor'|'onenand'|'spi-nand'<dev-num>\n"
Stefan Roese68d7d652009-03-19 13:30:36 +01002184 "<dev-num> := mtd device number, 0...\n"
2185 "<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)\n\n"
2186 "'mtdparts' - partition list\n\n"
2187 "mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]\n\n"
2188 "<mtd-def> := <mtd-id>:<part-def>[,<part-def>...]\n"
2189 "<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)\n"
2190 "<part-def> := <size>[@<offset>][<name>][<ro-flag>]\n"
2191 "<size> := standard linux memsize OR '-' to denote all remaining space\n"
2192 "<offset> := partition start offset within the device\n"
2193 "<name> := '(' NAME ')'\n"
Kim Phillips088f1b12012-10-29 13:34:31 +00002194 "<ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)";
2195#endif
2196
2197U_BOOT_CMD(
2198 mtdparts, 6, 0, do_mtdparts,
2199 "define flash/nand partitions", mtdparts_help_text
Stefan Roese68d7d652009-03-19 13:30:36 +01002200);
2201/***************************************************/