xref: /openbmc/u-boot/cmd/jffs2.c (revision c68c03f52badc90951dbf8a054c0e500e04bf365)
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  		struct mtd_info *mtd = get_nand_dev_by_index(num);
170  		if (mtd) {
171  			*size = mtd->size;
172  			return 0;
173  		}
174  
175  		printf("no such NAND device: %s%d (valid range 0 ... %d)\n",
176  				MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_NAND_DEVICE - 1);
177  #else
178  		printf("support for NAND devices not present\n");
179  #endif
180  	} else if (type == MTD_DEV_TYPE_ONENAND) {
181  #if defined(CONFIG_CMD_ONENAND)
182  		*size = onenand_mtd.size;
183  		return 0;
184  #else
185  		printf("support for OneNAND devices not present\n");
186  #endif
187  	} else
188  		printf("Unknown defice type %d\n", type);
189  
190  	return 1;
191  }
192  
193  /**
194   * Parse device id string <dev-id> := 'nand'|'nor'|'onenand'<dev-num>,
195   * return device type and number.
196   *
197   * @param id string describing device id
198   * @param ret_id output pointer to next char after parse completes (output)
199   * @param dev_type parsed device type (output)
200   * @param dev_num parsed device number (output)
201   * @return 0 on success, 1 otherwise
202   */
203  static int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num)
204  {
205  	const char *p = id;
206  
207  	*dev_type = 0;
208  	if (strncmp(p, "nand", 4) == 0) {
209  		*dev_type = MTD_DEV_TYPE_NAND;
210  		p += 4;
211  	} else if (strncmp(p, "nor", 3) == 0) {
212  		*dev_type = MTD_DEV_TYPE_NOR;
213  		p += 3;
214  	} else if (strncmp(p, "onenand", 7) == 0) {
215  		*dev_type = MTD_DEV_TYPE_ONENAND;
216  		p += 7;
217  	} else {
218  		printf("incorrect device type in %s\n", id);
219  		return 1;
220  	}
221  
222  	if (!isdigit(*p)) {
223  		printf("incorrect device number in %s\n", id);
224  		return 1;
225  	}
226  
227  	*dev_num = simple_strtoul(p, (char **)&p, 0);
228  	if (ret_id)
229  		*ret_id = p;
230  	return 0;
231  }
232  
233  /*
234   * 'Static' version of command line mtdparts_init() routine. Single partition on
235   * a single device configuration.
236   */
237  
238  /**
239   * Calculate sector size.
240   *
241   * @return sector size
242   */
243  static inline u32 get_part_sector_size_nand(struct mtdids *id)
244  {
245  #if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
246  	struct mtd_info *mtd;
247  
248  	mtd = get_nand_dev_by_index(id->num);
249  
250  	return mtd->erasesize;
251  #else
252  	BUG();
253  	return 0;
254  #endif
255  }
256  
257  static inline u32 get_part_sector_size_nor(struct mtdids *id, struct part_info *part)
258  {
259  #if defined(CONFIG_CMD_FLASH)
260  	extern flash_info_t flash_info[];
261  
262  	u32 end_phys, start_phys, sector_size = 0, size = 0;
263  	int i;
264  	flash_info_t *flash;
265  
266  	flash = &flash_info[id->num];
267  
268  	start_phys = flash->start[0] + part->offset;
269  	end_phys = start_phys + part->size - 1;
270  
271  	for (i = 0; i < flash->sector_count; i++) {
272  		if (flash->start[i] >= end_phys)
273  			break;
274  
275  		if (flash->start[i] >= start_phys) {
276  			if (i == flash->sector_count - 1) {
277  				size = flash->start[0] + flash->size - flash->start[i];
278  			} else {
279  				size = flash->start[i+1] - flash->start[i];
280  			}
281  
282  			if (sector_size < size)
283  				sector_size = size;
284  		}
285  	}
286  
287  	return sector_size;
288  #else
289  	BUG();
290  	return 0;
291  #endif
292  }
293  
294  static inline u32 get_part_sector_size_onenand(void)
295  {
296  #if defined(CONFIG_CMD_ONENAND)
297  	struct mtd_info *mtd;
298  
299  	mtd = &onenand_mtd;
300  
301  	return mtd->erasesize;
302  #else
303  	BUG();
304  	return 0;
305  #endif
306  }
307  
308  static inline u32 get_part_sector_size(struct mtdids *id, struct part_info *part)
309  {
310  	if (id->type == MTD_DEV_TYPE_NAND)
311  		return get_part_sector_size_nand(id);
312  	else if (id->type == MTD_DEV_TYPE_NOR)
313  		return get_part_sector_size_nor(id, part);
314  	else if (id->type == MTD_DEV_TYPE_ONENAND)
315  		return get_part_sector_size_onenand();
316  	else
317  		DEBUGF("Error: Unknown device type.\n");
318  
319  	return 0;
320  }
321  
322  /**
323   * Parse and initialize global mtdids mapping and create global
324   * device/partition list.
325   *
326   * 'Static' version of command line mtdparts_init() routine. Single partition on
327   * a single device configuration.
328   *
329   * @return 0 on success, 1 otherwise
330   */
331  int mtdparts_init(void)
332  {
333  	static int initialized = 0;
334  	u32 size;
335  	char *dev_name;
336  
337  	DEBUGF("\n---mtdparts_init---\n");
338  	if (!initialized) {
339  		struct mtdids *id;
340  		struct part_info *part;
341  
342  		initialized = 1;
343  		current_mtd_dev = (struct mtd_device *)
344  			malloc(sizeof(struct mtd_device) +
345  					sizeof(struct part_info) +
346  					sizeof(struct mtdids));
347  		if (!current_mtd_dev) {
348  			printf("out of memory\n");
349  			return 1;
350  		}
351  		memset(current_mtd_dev, 0, sizeof(struct mtd_device) +
352  		       sizeof(struct part_info) + sizeof(struct mtdids));
353  
354  		id = (struct mtdids *)(current_mtd_dev + 1);
355  		part = (struct part_info *)(id + 1);
356  
357  		/* id */
358  		id->mtd_id = "single part";
359  
360  #if defined(CONFIG_JFFS2_DEV)
361  		dev_name = CONFIG_JFFS2_DEV;
362  #else
363  		dev_name = "nor0";
364  #endif
365  
366  		if ((mtd_id_parse(dev_name, NULL, &id->type, &id->num) != 0) ||
367  				(mtd_device_validate(id->type, id->num, &size) != 0)) {
368  			printf("incorrect device: %s%d\n", MTD_DEV_TYPE(id->type), id->num);
369  			free(current_mtd_dev);
370  			return 1;
371  		}
372  		id->size = size;
373  		INIT_LIST_HEAD(&id->link);
374  
375  		DEBUGF("dev id: type = %d, num = %d, size = 0x%08lx, mtd_id = %s\n",
376  				id->type, id->num, id->size, id->mtd_id);
377  
378  		/* partition */
379  		part->name = "static";
380  		part->auto_name = 0;
381  
382  #if defined(CONFIG_JFFS2_PART_SIZE)
383  		part->size = CONFIG_JFFS2_PART_SIZE;
384  #else
385  		part->size = SIZE_REMAINING;
386  #endif
387  
388  #if defined(CONFIG_JFFS2_PART_OFFSET)
389  		part->offset = CONFIG_JFFS2_PART_OFFSET;
390  #else
391  		part->offset = 0x00000000;
392  #endif
393  
394  		part->dev = current_mtd_dev;
395  		INIT_LIST_HEAD(&part->link);
396  
397  		/* recalculate size if needed */
398  		if (part->size == SIZE_REMAINING)
399  			part->size = id->size - part->offset;
400  
401  		part->sector_size = get_part_sector_size(id, part);
402  
403  		DEBUGF("part  : name = %s, size = 0x%08lx, offset = 0x%08lx\n",
404  				part->name, part->size, part->offset);
405  
406  		/* device */
407  		current_mtd_dev->id = id;
408  		INIT_LIST_HEAD(&current_mtd_dev->link);
409  		current_mtd_dev->num_parts = 1;
410  		INIT_LIST_HEAD(&current_mtd_dev->parts);
411  		list_add(&part->link, &current_mtd_dev->parts);
412  	}
413  
414  	return 0;
415  }
416  #endif /* #ifndef CONFIG_CMD_MTDPARTS */
417  
418  /**
419   * Return pointer to the partition of a requested number from a requested
420   * device.
421   *
422   * @param dev device that is to be searched for a partition
423   * @param part_num requested partition number
424   * @return pointer to the part_info, NULL otherwise
425   */
426  static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num)
427  {
428  	struct list_head *entry;
429  	struct part_info *part;
430  	int num;
431  
432  	if (!dev)
433  		return NULL;
434  
435  	DEBUGF("\n--- jffs2_part_info: partition number %d for device %s%d (%s)\n",
436  			part_num, MTD_DEV_TYPE(dev->id->type),
437  			dev->id->num, dev->id->mtd_id);
438  
439  	if (part_num >= dev->num_parts) {
440  		printf("invalid partition number %d for device %s%d (%s)\n",
441  				part_num, MTD_DEV_TYPE(dev->id->type),
442  				dev->id->num, dev->id->mtd_id);
443  		return NULL;
444  	}
445  
446  	/* locate partition number, return it */
447  	num = 0;
448  	list_for_each(entry, &dev->parts) {
449  		part = list_entry(entry, struct part_info, link);
450  
451  		if (part_num == num++) {
452  			return part;
453  		}
454  	}
455  
456  	return NULL;
457  }
458  
459  /***************************************************/
460  /* U-Boot commands				   */
461  /***************************************************/
462  
463  /**
464   * Routine implementing fsload u-boot command. This routine tries to load
465   * a requested file from jffs2/cramfs filesystem on a current partition.
466   *
467   * @param cmdtp command internal data
468   * @param flag command flag
469   * @param argc number of arguments supplied to the command
470   * @param argv arguments list
471   * @return 0 on success, 1 otherwise
472   */
473  int do_jffs2_fsload(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
474  {
475  	char *fsname;
476  	char *filename;
477  	int size;
478  	struct part_info *part;
479  	ulong offset = load_addr;
480  
481  	/* pre-set Boot file name */
482  	filename = env_get("bootfile");
483  	if (!filename)
484  		filename = "uImage";
485  
486  	if (argc == 2) {
487  		filename = argv[1];
488  	}
489  	if (argc == 3) {
490  		offset = simple_strtoul(argv[1], NULL, 16);
491  		load_addr = offset;
492  		filename = argv[2];
493  	}
494  
495  	/* make sure we are in sync with env variables */
496  	if (mtdparts_init() !=0)
497  		return 1;
498  
499  	if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){
500  
501  		/* check partition type for cramfs */
502  		fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2");
503  		printf("### %s loading '%s' to 0x%lx\n", fsname, filename, offset);
504  
505  		if (cramfs_check(part)) {
506  			size = cramfs_load ((char *) offset, part, filename);
507  		} else {
508  			/* if this is not cramfs assume jffs2 */
509  			size = jffs2_1pass_load((char *)offset, part, filename);
510  		}
511  
512  		if (size > 0) {
513  			printf("### %s load complete: %d bytes loaded to 0x%lx\n",
514  				fsname, size, offset);
515  			env_set_hex("filesize", size);
516  		} else {
517  			printf("### %s LOAD ERROR<%x> for %s!\n", fsname, size, filename);
518  		}
519  
520  		return !(size > 0);
521  	}
522  	return 1;
523  }
524  
525  /**
526   * Routine implementing u-boot ls command which lists content of a given
527   * directory on a current partition.
528   *
529   * @param cmdtp command internal data
530   * @param flag command flag
531   * @param argc number of arguments supplied to the command
532   * @param argv arguments list
533   * @return 0 on success, 1 otherwise
534   */
535  int do_jffs2_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
536  {
537  	char *filename = "/";
538  	int ret;
539  	struct part_info *part;
540  
541  	if (argc == 2)
542  		filename = argv[1];
543  
544  	/* make sure we are in sync with env variables */
545  	if (mtdparts_init() !=0)
546  		return 1;
547  
548  	if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){
549  
550  		/* check partition type for cramfs */
551  		if (cramfs_check(part)) {
552  			ret = cramfs_ls (part, filename);
553  		} else {
554  			/* if this is not cramfs assume jffs2 */
555  			ret = jffs2_1pass_ls(part, filename);
556  		}
557  
558  		return ret ? 0 : 1;
559  	}
560  	return 1;
561  }
562  
563  /**
564   * Routine implementing u-boot fsinfo command. This routine prints out
565   * miscellaneous filesystem informations/statistics.
566   *
567   * @param cmdtp command internal data
568   * @param flag command flag
569   * @param argc number of arguments supplied to the command
570   * @param argv arguments list
571   * @return 0 on success, 1 otherwise
572   */
573  int do_jffs2_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
574  {
575  	struct part_info *part;
576  	char *fsname;
577  	int ret;
578  
579  	/* make sure we are in sync with env variables */
580  	if (mtdparts_init() !=0)
581  		return 1;
582  
583  	if ((part = jffs2_part_info(current_mtd_dev, current_mtd_partnum))){
584  
585  		/* check partition type for cramfs */
586  		fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2");
587  		printf("### filesystem type is %s\n", fsname);
588  
589  		if (cramfs_check(part)) {
590  			ret = cramfs_info (part);
591  		} else {
592  			/* if this is not cramfs assume jffs2 */
593  			ret = jffs2_1pass_info(part);
594  		}
595  
596  		return ret ? 0 : 1;
597  	}
598  	return 1;
599  }
600  
601  /***************************************************/
602  U_BOOT_CMD(
603  	fsload,	3,	0,	do_jffs2_fsload,
604  	"load binary file from a filesystem image",
605  	"[ off ] [ filename ]\n"
606  	"    - load binary file from flash bank\n"
607  	"      with offset 'off'"
608  );
609  U_BOOT_CMD(
610  	fsls,	2,	1,	do_jffs2_ls,
611  	"list files in a directory (default /)",
612  	"[ directory ]"
613  );
614  
615  U_BOOT_CMD(
616  	fsinfo,	1,	1,	do_jffs2_fsinfo,
617  	"print information about filesystems",
618  	""
619  );
620  /***************************************************/
621