xref: /openbmc/u-boot/cmd/jffs2.c (revision 70b26cd057f42c7126088b49d4285955c8a00eae)
1  /*
2   * (C) Copyright 2002
3   * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4   *
5   * (C) Copyright 2002
6   * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de>
7   *
8   * (C) Copyright 2003
9   * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de>
10   *
11   * (C) Copyright 2005
12   * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
13   *
14   *   Added support for reading flash partition table from environment.
15   *   Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4
16   *   kernel tree.
17   *
18   *   $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $
19   *   Copyright 2002 SYSGO Real-Time Solutions GmbH
20   *
21   * SPDX-License-Identifier:	GPL-2.0+
22   */
23  
24  /*
25   * Three environment variables are used by the parsing routines:
26   *
27   * 'partition' - keeps current partition identifier
28   *
29   * partition  := <part-id>
30   * <part-id>  := <dev-id>,part_num
31   *
32   *
33   * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping
34   *
35   * mtdids=<idmap>[,<idmap>,...]
36   *
37   * <idmap>    := <dev-id>=<mtd-id>
38   * <dev-id>   := 'nand'|'nor'|'onenand'<dev-num>
39   * <dev-num>  := mtd device number, 0...
40   * <mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)
41   *
42   *
43   * 'mtdparts' - partition list
44   *
45   * mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]
46   *
47   * <mtd-def>  := <mtd-id>:<part-def>[,<part-def>...]
48   * <mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)
49   * <part-def> := <size>[@<offset>][<name>][<ro-flag>]
50   * <size>     := standard linux memsize OR '-' to denote all remaining space
51   * <offset>   := partition start offset within the device
52   * <name>     := '(' NAME ')'
53   * <ro-flag>  := when set to 'ro' makes partition read-only (not used, passed to kernel)
54   *
55   * Notes:
56   * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping
57   * - if the above variables are not set defaults for a given target are used
58   *
59   * Examples:
60   *
61   * 1 NOR Flash, with 1 single writable partition:
62   * mtdids=nor0=edb7312-nor
63   * mtdparts=mtdparts=edb7312-nor:-
64   *
65   * 1 NOR Flash with 2 partitions, 1 NAND with one
66   * mtdids=nor0=edb7312-nor,nand0=edb7312-nand
67   * mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
68   *
69   */
70  
71  /*
72   * JFFS2/CRAMFS support
73   */
74  #include <common.h>
75  #include <command.h>
76  #include <malloc.h>
77  #include <jffs2/jffs2.h>
78  #include <linux/list.h>
79  #include <linux/ctype.h>
80  #include <cramfs/cramfs_fs.h>
81  
82  #if defined(CONFIG_CMD_NAND)
83  #include <linux/mtd/nand.h>
84  #include <nand.h>
85  #endif
86  
87  #if defined(CONFIG_CMD_ONENAND)
88  #include <linux/mtd/mtd.h>
89  #include <linux/mtd/onenand.h>
90  #include <onenand_uboot.h>
91  #endif
92  
93  /* enable/disable debugging messages */
94  #define	DEBUG_JFFS
95  #undef	DEBUG_JFFS
96  
97  #ifdef  DEBUG_JFFS
98  # define DEBUGF(fmt, args...)	printf(fmt ,##args)
99  #else
100  # define DEBUGF(fmt, args...)
101  #endif
102  
103  /* special size referring to all the remaining space in a partition */
104  #define SIZE_REMAINING		0xFFFFFFFF
105  
106  /* special offset value, it is used when not provided by user
107   *
108   * this value is used temporarily during parsing, later such offests
109   * are recalculated */
110  #define OFFSET_NOT_SPECIFIED	0xFFFFFFFF
111  
112  /* minimum partition size */
113  #define MIN_PART_SIZE		4096
114  
115  /* this flag needs to be set in part_info struct mask_flags
116   * field for read-only partitions */
117  #define MTD_WRITEABLE_CMD		1
118  
119  /* current active device and partition number */
120  #ifdef CONFIG_CMD_MTDPARTS
121  /* Use the ones declared in cmd_mtdparts.c */
122  extern struct mtd_device *current_mtd_dev;
123  extern u8 current_mtd_partnum;
124  #else
125  /* Use local ones */
126  struct mtd_device *current_mtd_dev = NULL;
127  u8 current_mtd_partnum = 0;
128  #endif
129  
130  #if defined(CONFIG_CMD_CRAMFS)
131  extern int cramfs_check (struct part_info *info);
132  extern int cramfs_load (char *loadoffset, struct part_info *info, char *filename);
133  extern int cramfs_ls (struct part_info *info, char *filename);
134  extern int cramfs_info (struct part_info *info);
135  #else
136  /* defining empty macros for function names is ugly but avoids ifdef clutter
137   * all over the code */
138  #define cramfs_check(x)		(0)
139  #define cramfs_load(x,y,z)	(-1)
140  #define cramfs_ls(x,y)		(0)
141  #define cramfs_info(x)		(0)
142  #endif
143  
144  #ifndef CONFIG_CMD_MTDPARTS
145  /**
146   * Check device number to be within valid range for given device type.
147   *
148   * @param dev device to validate
149   * @return 0 if device is valid, 1 otherwise
150   */
151  static int mtd_device_validate(u8 type, u8 num, u32 *size)
152  {
153  	if (type == MTD_DEV_TYPE_NOR) {
154  #if defined(CONFIG_CMD_FLASH)
155  		if (num < CONFIG_SYS_MAX_FLASH_BANKS) {
156  			extern flash_info_t flash_info[];
157  			*size = flash_info[num].size;
158  
159  			return 0;
160  		}
161  
162  		printf("no such FLASH device: %s%d (valid range 0 ... %d\n",
163  				MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_FLASH_BANKS - 1);
164  #else
165  		printf("support for FLASH devices not present\n");
166  #endif
167  	} else if (type == MTD_DEV_TYPE_NAND) {
168  #if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
169  		if (num < CONFIG_SYS_MAX_NAND_DEVICE) {
170  			*size = nand_info[num]->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   */
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   */
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 = nand_info[id->num];
248  
249  	return mtd->erasesize;
250  #else
251  	BUG();
252  	return 0;
253  #endif
254  }
255  
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  
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  
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   */
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   */
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   */
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  	if ((filename = getenv("bootfile")) == NULL) {
482  		filename = "uImage";
483  	}
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  			setenv_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   */
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   */
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  	ls,	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