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