• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  // SPDX-License-Identifier: GPL-2.0+
2  /*
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   *
19   *   $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $
20   *   Copyright 2002 SYSGO Real-Time Solutions GmbH
21   */
22  
23  /*
24   * Three environment variables are used by the parsing routines:
25   *
26   * 'partition' - keeps current partition identifier
27   *
28   * partition  := <part-id>
29   * <part-id>  := <dev-id>,part_num
30   *
31   *
32   * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping
33   *
34   * mtdids=<idmap>[,<idmap>,...]
35   *
36   * <idmap>    := <dev-id>=<mtd-id>
37   * <dev-id>   := 'nand'|'nor'|'onenand'<dev-num>
38   * <dev-num>  := mtd device number, 0...
39   * <mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)
40   *
41   *
42   * 'mtdparts' - partition list
43   *
44   * mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]
45   *
46   * <mtd-def>  := <mtd-id>:<part-def>[,<part-def>...]
47   * <mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)
48   * <part-def> := <size>[@<offset>][<name>][<ro-flag>]
49   * <size>     := standard linux memsize OR '-' to denote all remaining space
50   * <offset>   := partition start offset within the device
51   * <name>     := '(' NAME ')'
52   * <ro-flag>  := when set to 'ro' makes partition read-only (not used, passed to kernel)
53   *
54   * Notes:
55   * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping
56   * - if the above variables are not set defaults for a given target are used
57   *
58   * Examples:
59   *
60   * 1 NOR Flash, with 1 single writable partition:
61   * mtdids=nor0=edb7312-nor
62   * mtdparts=mtdparts=edb7312-nor:-
63   *
64   * 1 NOR Flash with 2 partitions, 1 NAND with one
65   * mtdids=nor0=edb7312-nor,nand0=edb7312-nand
66   * mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
67   *
68   */
69  
70  /*
71   * JFFS2/CRAMFS support
72   */
73  #include <common.h>
74  #include <command.h>
75  #include <malloc.h>
76  #include <jffs2/jffs2.h>
77  #include <linux/list.h>
78  #include <linux/ctype.h>
79  #include <cramfs/cramfs_fs.h>
80  
81  #if defined(CONFIG_CMD_NAND)
82  #include <linux/mtd/rawnand.h>
83  #include <nand.h>
84  #endif
85  
86  #if defined(CONFIG_CMD_ONENAND)
87  #include <linux/mtd/mtd.h>
88  #include <linux/mtd/onenand.h>
89  #include <onenand_uboot.h>
90  #endif
91  
92  /* enable/disable debugging messages */
93  #define	DEBUG_JFFS
94  #undef	DEBUG_JFFS
95  
96  #ifdef  DEBUG_JFFS
97  # define DEBUGF(fmt, args...)	printf(fmt ,##args)
98  #else
99  # define DEBUGF(fmt, args...)
100  #endif
101  
102  /* special size referring to all the remaining space in a partition */
103  #define SIZE_REMAINING		0xFFFFFFFF
104  
105  /* special offset value, it is used when not provided by user
106   *
107   * this value is used temporarily during parsing, later such offests
108   * are recalculated */
109  #define OFFSET_NOT_SPECIFIED	0xFFFFFFFF
110  
111  /* minimum partition size */
112  #define MIN_PART_SIZE		4096
113  
114  /* this flag needs to be set in part_info struct mask_flags
115   * field for read-only partitions */
116  #define MTD_WRITEABLE_CMD		1
117  
118  /* current active device and partition number */
119  #ifdef CONFIG_CMD_MTDPARTS
120  /* Use the ones declared in cmd_mtdparts.c */
121  extern struct mtd_device *current_mtd_dev;
122  extern u8 current_mtd_partnum;
123  #else
124  /* Use local ones */
125  struct mtd_device *current_mtd_dev = NULL;
126  u8 current_mtd_partnum = 0;
127  #endif
128  
129  #if defined(CONFIG_CMD_CRAMFS)
130  extern int cramfs_check (struct part_info *info);
131  extern int cramfs_load (char *loadoffset, struct part_info *info, char *filename);
132  extern int cramfs_ls (struct part_info *info, char *filename);
133  extern int cramfs_info (struct part_info *info);
134  #else
135  /* defining empty macros for function names is ugly but avoids ifdef clutter
136   * all over the code */
137  #define cramfs_check(x)		(0)
138  #define cramfs_load(x,y,z)	(-1)
139  #define cramfs_ls(x,y)		(0)
140  #define cramfs_info(x)		(0)
141  #endif
142  
143  #ifndef CONFIG_CMD_MTDPARTS
144  /**
145   * Check device number to be within valid range for given device type.
146   *
147   * @param dev device to validate
148   * @return 0 if device is valid, 1 otherwise
149   */
mtd_device_validate(u8 type,u8 num,u32 * size)150  static int mtd_device_validate(u8 type, u8 num, u32 *size)
151  {
152  	if (type == MTD_DEV_TYPE_NOR) {
153  #if defined(CONFIG_CMD_FLASH)
154  		if (num < CONFIG_SYS_MAX_FLASH_BANKS) {
155  			extern flash_info_t flash_info[];
156  			*size = flash_info[num].size;
157  
158  			return 0;
159  		}
160  
161  		printf("no such FLASH device: %s%d (valid range 0 ... %d\n",
162  				MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_FLASH_BANKS - 1);
163  #else
164  		printf("support for FLASH devices not present\n");
165  #endif
166  	} else if (type == MTD_DEV_TYPE_NAND) {
167  #if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
168  		struct mtd_info *mtd = get_nand_dev_by_index(num);
169  		if (mtd) {
170  			*size = mtd->size;
171  			return 0;
172  		}
173  
174  		printf("no such NAND device: %s%d (valid range 0 ... %d)\n",
175  				MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_NAND_DEVICE - 1);
176  #else
177  		printf("support for NAND devices not present\n");
178  #endif
179  	} else if (type == MTD_DEV_TYPE_ONENAND) {
180  #if defined(CONFIG_CMD_ONENAND)
181  		*size = onenand_mtd.size;
182  		return 0;
183  #else
184  		printf("support for OneNAND devices not present\n");
185  #endif
186  	} else
187  		printf("Unknown defice type %d\n", type);
188  
189  	return 1;
190  }
191  
192  /**
193   * Parse device id string <dev-id> := 'nand'|'nor'|'onenand'<dev-num>,
194   * return device type and number.
195   *
196   * @param id string describing device id
197   * @param ret_id output pointer to next char after parse completes (output)
198   * @param dev_type parsed device type (output)
199   * @param dev_num parsed device number (output)
200   * @return 0 on success, 1 otherwise
201   */
mtd_id_parse(const char * id,const char ** ret_id,u8 * dev_type,u8 * dev_num)202  static int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num)
203  {
204  	const char *p = id;
205  
206  	*dev_type = 0;
207  	if (strncmp(p, "nand", 4) == 0) {
208  		*dev_type = MTD_DEV_TYPE_NAND;
209  		p += 4;
210  	} else if (strncmp(p, "nor", 3) == 0) {
211  		*dev_type = MTD_DEV_TYPE_NOR;
212  		p += 3;
213  	} else if (strncmp(p, "onenand", 7) == 0) {
214  		*dev_type = MTD_DEV_TYPE_ONENAND;
215  		p += 7;
216  	} else {
217  		printf("incorrect device type in %s\n", id);
218  		return 1;
219  	}
220  
221  	if (!isdigit(*p)) {
222  		printf("incorrect device number in %s\n", id);
223  		return 1;
224  	}
225  
226  	*dev_num = simple_strtoul(p, (char **)&p, 0);
227  	if (ret_id)
228  		*ret_id = p;
229  	return 0;
230  }
231  
232  /*
233   * 'Static' version of command line mtdparts_init() routine. Single partition on
234   * a single device configuration.
235   */
236  
237  /**
238   * Calculate sector size.
239   *
240   * @return sector size
241   */
get_part_sector_size_nand(struct mtdids * id)242  static inline u32 get_part_sector_size_nand(struct mtdids *id)
243  {
244  #if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
245  	struct mtd_info *mtd;
246  
247  	mtd = get_nand_dev_by_index(id->num);
248  
249  	return mtd->erasesize;
250  #else
251  	BUG();
252  	return 0;
253  #endif
254  }
255  
get_part_sector_size_nor(struct mtdids * id,struct part_info * part)256  static inline u32 get_part_sector_size_nor(struct mtdids *id, struct part_info *part)
257  {
258  #if defined(CONFIG_CMD_FLASH)
259  	extern flash_info_t flash_info[];
260  
261  	u32 end_phys, start_phys, sector_size = 0, size = 0;
262  	int i;
263  	flash_info_t *flash;
264  
265  	flash = &flash_info[id->num];
266  
267  	start_phys = flash->start[0] + part->offset;
268  	end_phys = start_phys + part->size - 1;
269  
270  	for (i = 0; i < flash->sector_count; i++) {
271  		if (flash->start[i] >= end_phys)
272  			break;
273  
274  		if (flash->start[i] >= start_phys) {
275  			if (i == flash->sector_count - 1) {
276  				size = flash->start[0] + flash->size - flash->start[i];
277  			} else {
278  				size = flash->start[i+1] - flash->start[i];
279  			}
280  
281  			if (sector_size < size)
282  				sector_size = size;
283  		}
284  	}
285  
286  	return sector_size;
287  #else
288  	BUG();
289  	return 0;
290  #endif
291  }
292  
get_part_sector_size_onenand(void)293  static inline u32 get_part_sector_size_onenand(void)
294  {
295  #if defined(CONFIG_CMD_ONENAND)
296  	struct mtd_info *mtd;
297  
298  	mtd = &onenand_mtd;
299  
300  	return mtd->erasesize;
301  #else
302  	BUG();
303  	return 0;
304  #endif
305  }
306  
get_part_sector_size(struct mtdids * id,struct part_info * part)307  static inline u32 get_part_sector_size(struct mtdids *id, struct part_info *part)
308  {
309  	if (id->type == MTD_DEV_TYPE_NAND)
310  		return get_part_sector_size_nand(id);
311  	else if (id->type == MTD_DEV_TYPE_NOR)
312  		return get_part_sector_size_nor(id, part);
313  	else if (id->type == MTD_DEV_TYPE_ONENAND)
314  		return get_part_sector_size_onenand();
315  	else
316  		DEBUGF("Error: Unknown device type.\n");
317  
318  	return 0;
319  }
320  
321  /**
322   * Parse and initialize global mtdids mapping and create global
323   * device/partition list.
324   *
325   * 'Static' version of command line mtdparts_init() routine. Single partition on
326   * a single device configuration.
327   *
328   * @return 0 on success, 1 otherwise
329   */
mtdparts_init(void)330  int mtdparts_init(void)
331  {
332  	static int initialized = 0;
333  	u32 size;
334  	char *dev_name;
335  
336  	DEBUGF("\n---mtdparts_init---\n");
337  	if (!initialized) {
338  		struct mtdids *id;
339  		struct part_info *part;
340  
341  		initialized = 1;
342  		current_mtd_dev = (struct mtd_device *)
343  			malloc(sizeof(struct mtd_device) +
344  					sizeof(struct part_info) +
345  					sizeof(struct mtdids));
346  		if (!current_mtd_dev) {
347  			printf("out of memory\n");
348  			return 1;
349  		}
350  		memset(current_mtd_dev, 0, sizeof(struct mtd_device) +
351  		       sizeof(struct part_info) + sizeof(struct mtdids));
352  
353  		id = (struct mtdids *)(current_mtd_dev + 1);
354  		part = (struct part_info *)(id + 1);
355  
356  		/* id */
357  		id->mtd_id = "single part";
358  
359  #if defined(CONFIG_JFFS2_DEV)
360  		dev_name = CONFIG_JFFS2_DEV;
361  #else
362  		dev_name = "nor0";
363  #endif
364  
365  		if ((mtd_id_parse(dev_name, NULL, &id->type, &id->num) != 0) ||
366  				(mtd_device_validate(id->type, id->num, &size) != 0)) {
367  			printf("incorrect device: %s%d\n", MTD_DEV_TYPE(id->type), id->num);
368  			free(current_mtd_dev);
369  			return 1;
370  		}
371  		id->size = size;
372  		INIT_LIST_HEAD(&id->link);
373  
374  		DEBUGF("dev id: type = %d, num = %d, size = 0x%08lx, mtd_id = %s\n",
375  				id->type, id->num, id->size, id->mtd_id);
376  
377  		/* partition */
378  		part->name = "static";
379  		part->auto_name = 0;
380  
381  #if defined(CONFIG_JFFS2_PART_SIZE)
382  		part->size = CONFIG_JFFS2_PART_SIZE;
383  #else
384  		part->size = SIZE_REMAINING;
385  #endif
386  
387  #if defined(CONFIG_JFFS2_PART_OFFSET)
388  		part->offset = CONFIG_JFFS2_PART_OFFSET;
389  #else
390  		part->offset = 0x00000000;
391  #endif
392  
393  		part->dev = current_mtd_dev;
394  		INIT_LIST_HEAD(&part->link);
395  
396  		/* recalculate size if needed */
397  		if (part->size == SIZE_REMAINING)
398  			part->size = id->size - part->offset;
399  
400  		part->sector_size = get_part_sector_size(id, part);
401  
402  		DEBUGF("part  : name = %s, size = 0x%08lx, offset = 0x%08lx\n",
403  				part->name, part->size, part->offset);
404  
405  		/* device */
406  		current_mtd_dev->id = id;
407  		INIT_LIST_HEAD(&current_mtd_dev->link);
408  		current_mtd_dev->num_parts = 1;
409  		INIT_LIST_HEAD(&current_mtd_dev->parts);
410  		list_add(&part->link, &current_mtd_dev->parts);
411  	}
412  
413  	return 0;
414  }
415  #endif /* #ifndef CONFIG_CMD_MTDPARTS */
416  
417  /**
418   * Return pointer to the partition of a requested number from a requested
419   * device.
420   *
421   * @param dev device that is to be searched for a partition
422   * @param part_num requested partition number
423   * @return pointer to the part_info, NULL otherwise
424   */
jffs2_part_info(struct mtd_device * dev,unsigned int part_num)425  static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num)
426  {
427  	struct list_head *entry;
428  	struct part_info *part;
429  	int num;
430  
431  	if (!dev)
432  		return NULL;
433  
434  	DEBUGF("\n--- jffs2_part_info: partition number %d for device %s%d (%s)\n",
435  			part_num, MTD_DEV_TYPE(dev->id->type),
436  			dev->id->num, dev->id->mtd_id);
437  
438  	if (part_num >= dev->num_parts) {
439  		printf("invalid partition number %d for device %s%d (%s)\n",
440  				part_num, MTD_DEV_TYPE(dev->id->type),
441  				dev->id->num, dev->id->mtd_id);
442  		return NULL;
443  	}
444  
445  	/* locate partition number, return it */
446  	num = 0;
447  	list_for_each(entry, &dev->parts) {
448  		part = list_entry(entry, struct part_info, link);
449  
450  		if (part_num == num++) {
451  			return part;
452  		}
453  	}
454  
455  	return NULL;
456  }
457  
458  /***************************************************/
459  /* U-Boot commands				   */
460  /***************************************************/
461  
462  /**
463   * Routine implementing fsload u-boot command. This routine tries to load
464   * a requested file from jffs2/cramfs filesystem on a current partition.
465   *
466   * @param cmdtp command internal data
467   * @param flag command flag
468   * @param argc number of arguments supplied to the command
469   * @param argv arguments list
470   * @return 0 on success, 1 otherwise
471   */
do_jffs2_fsload(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])472  int do_jffs2_fsload(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
473  {
474  	char *fsname;
475  	char *filename;
476  	int size;
477  	struct part_info *part;
478  	ulong offset = load_addr;
479  
480  	/* pre-set Boot file name */
481  	filename = env_get("bootfile");
482  	if (!filename)
483  		filename = "uImage";
484  
485  	if (argc == 2) {
486  		filename = argv[1];
487  	}
488  	if (argc == 3) {
489  		offset = simple_strtoul(argv[1], NULL, 16);
490  		load_addr = offset;
491  		filename = argv[2];
492  	}
493  
494  	/* make sure we are in sync with env variables */
495  	if (mtdparts_init() !=0)
496  		return 1;
497  
498  	if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){
499  
500  		/* check partition type for cramfs */
501  		fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2");
502  		printf("### %s loading '%s' to 0x%lx\n", fsname, filename, offset);
503  
504  		if (cramfs_check(part)) {
505  			size = cramfs_load ((char *) offset, part, filename);
506  		} else {
507  			/* if this is not cramfs assume jffs2 */
508  			size = jffs2_1pass_load((char *)offset, part, filename);
509  		}
510  
511  		if (size > 0) {
512  			printf("### %s load complete: %d bytes loaded to 0x%lx\n",
513  				fsname, size, offset);
514  			env_set_hex("filesize", size);
515  		} else {
516  			printf("### %s LOAD ERROR<%x> for %s!\n", fsname, size, filename);
517  		}
518  
519  		return !(size > 0);
520  	}
521  	return 1;
522  }
523  
524  /**
525   * Routine implementing u-boot ls command which lists content of a given
526   * directory on a current partition.
527   *
528   * @param cmdtp command internal data
529   * @param flag command flag
530   * @param argc number of arguments supplied to the command
531   * @param argv arguments list
532   * @return 0 on success, 1 otherwise
533   */
do_jffs2_ls(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])534  int do_jffs2_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
535  {
536  	char *filename = "/";
537  	int ret;
538  	struct part_info *part;
539  
540  	if (argc == 2)
541  		filename = argv[1];
542  
543  	/* make sure we are in sync with env variables */
544  	if (mtdparts_init() !=0)
545  		return 1;
546  
547  	if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){
548  
549  		/* check partition type for cramfs */
550  		if (cramfs_check(part)) {
551  			ret = cramfs_ls (part, filename);
552  		} else {
553  			/* if this is not cramfs assume jffs2 */
554  			ret = jffs2_1pass_ls(part, filename);
555  		}
556  
557  		return ret ? 0 : 1;
558  	}
559  	return 1;
560  }
561  
562  /**
563   * Routine implementing u-boot fsinfo command. This routine prints out
564   * miscellaneous filesystem informations/statistics.
565   *
566   * @param cmdtp command internal data
567   * @param flag command flag
568   * @param argc number of arguments supplied to the command
569   * @param argv arguments list
570   * @return 0 on success, 1 otherwise
571   */
do_jffs2_fsinfo(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])572  int do_jffs2_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
573  {
574  	struct part_info *part;
575  	char *fsname;
576  	int ret;
577  
578  	/* make sure we are in sync with env variables */
579  	if (mtdparts_init() !=0)
580  		return 1;
581  
582  	if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){
583  
584  		/* check partition type for cramfs */
585  		fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2");
586  		printf("### filesystem type is %s\n", fsname);
587  
588  		if (cramfs_check(part)) {
589  			ret = cramfs_info (part);
590  		} else {
591  			/* if this is not cramfs assume jffs2 */
592  			ret = jffs2_1pass_info(part);
593  		}
594  
595  		return ret ? 0 : 1;
596  	}
597  	return 1;
598  }
599  
600  /***************************************************/
601  U_BOOT_CMD(
602  	fsload,	3,	0,	do_jffs2_fsload,
603  	"load binary file from a filesystem image",
604  	"[ off ] [ filename ]\n"
605  	"    - load binary file from flash bank\n"
606  	"      with offset 'off'"
607  );
608  U_BOOT_CMD(
609  	fsls,	2,	1,	do_jffs2_ls,
610  	"list files in a directory (default /)",
611  	"[ directory ]"
612  );
613  
614  U_BOOT_CMD(
615  	fsinfo,	1,	1,	do_jffs2_fsinfo,
616  	"print information about filesystems",
617  	""
618  );
619  /***************************************************/
620