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