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