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(¤t_mtd_dev->link); 408 current_mtd_dev->num_parts = 1; 409 INIT_LIST_HEAD(¤t_mtd_dev->parts); 410 list_add(&part->link, ¤t_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