1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 4 */ 5 6 #include <config.h> 7 #include <errno.h> 8 #include <common.h> 9 #include <mapmem.h> 10 #include <part.h> 11 #include <ext4fs.h> 12 #include <fat.h> 13 #include <fs.h> 14 #include <sandboxfs.h> 15 #include <ubifs_uboot.h> 16 #include <btrfs.h> 17 #include <asm/io.h> 18 #include <div64.h> 19 #include <linux/math64.h> 20 21 DECLARE_GLOBAL_DATA_PTR; 22 23 static struct blk_desc *fs_dev_desc; 24 static int fs_dev_part; 25 static disk_partition_t fs_partition; 26 static int fs_type = FS_TYPE_ANY; 27 28 static inline int fs_probe_unsupported(struct blk_desc *fs_dev_desc, 29 disk_partition_t *fs_partition) 30 { 31 printf("** Unrecognized filesystem type **\n"); 32 return -1; 33 } 34 35 static inline int fs_ls_unsupported(const char *dirname) 36 { 37 return -1; 38 } 39 40 /* generic implementation of ls in terms of opendir/readdir/closedir */ 41 __maybe_unused 42 static int fs_ls_generic(const char *dirname) 43 { 44 struct fs_dir_stream *dirs; 45 struct fs_dirent *dent; 46 int nfiles = 0, ndirs = 0; 47 48 dirs = fs_opendir(dirname); 49 if (!dirs) 50 return -errno; 51 52 while ((dent = fs_readdir(dirs))) { 53 if (dent->type == FS_DT_DIR) { 54 printf(" %s/\n", dent->name); 55 ndirs++; 56 } else { 57 printf(" %8lld %s\n", dent->size, dent->name); 58 nfiles++; 59 } 60 } 61 62 fs_closedir(dirs); 63 64 printf("\n%d file(s), %d dir(s)\n\n", nfiles, ndirs); 65 66 return 0; 67 } 68 69 static inline int fs_exists_unsupported(const char *filename) 70 { 71 return 0; 72 } 73 74 static inline int fs_size_unsupported(const char *filename, loff_t *size) 75 { 76 return -1; 77 } 78 79 static inline int fs_read_unsupported(const char *filename, void *buf, 80 loff_t offset, loff_t len, 81 loff_t *actread) 82 { 83 return -1; 84 } 85 86 static inline int fs_write_unsupported(const char *filename, void *buf, 87 loff_t offset, loff_t len, 88 loff_t *actwrite) 89 { 90 return -1; 91 } 92 93 static inline void fs_close_unsupported(void) 94 { 95 } 96 97 static inline int fs_uuid_unsupported(char *uuid_str) 98 { 99 return -1; 100 } 101 102 static inline int fs_opendir_unsupported(const char *filename, 103 struct fs_dir_stream **dirs) 104 { 105 return -EACCES; 106 } 107 108 struct fstype_info { 109 int fstype; 110 char *name; 111 /* 112 * Is it legal to pass NULL as .probe()'s fs_dev_desc parameter? This 113 * should be false in most cases. For "virtual" filesystems which 114 * aren't based on a U-Boot block device (e.g. sandbox), this can be 115 * set to true. This should also be true for the dummy entry at the end 116 * of fstypes[], since that is essentially a "virtual" (non-existent) 117 * filesystem. 118 */ 119 bool null_dev_desc_ok; 120 int (*probe)(struct blk_desc *fs_dev_desc, 121 disk_partition_t *fs_partition); 122 int (*ls)(const char *dirname); 123 int (*exists)(const char *filename); 124 int (*size)(const char *filename, loff_t *size); 125 int (*read)(const char *filename, void *buf, loff_t offset, 126 loff_t len, loff_t *actread); 127 int (*write)(const char *filename, void *buf, loff_t offset, 128 loff_t len, loff_t *actwrite); 129 void (*close)(void); 130 int (*uuid)(char *uuid_str); 131 /* 132 * Open a directory stream. On success return 0 and directory 133 * stream pointer via 'dirsp'. On error, return -errno. See 134 * fs_opendir(). 135 */ 136 int (*opendir)(const char *filename, struct fs_dir_stream **dirsp); 137 /* 138 * Read next entry from directory stream. On success return 0 139 * and directory entry pointer via 'dentp'. On error return 140 * -errno. See fs_readdir(). 141 */ 142 int (*readdir)(struct fs_dir_stream *dirs, struct fs_dirent **dentp); 143 /* see fs_closedir() */ 144 void (*closedir)(struct fs_dir_stream *dirs); 145 }; 146 147 static struct fstype_info fstypes[] = { 148 #ifdef CONFIG_FS_FAT 149 { 150 .fstype = FS_TYPE_FAT, 151 .name = "fat", 152 .null_dev_desc_ok = false, 153 .probe = fat_set_blk_dev, 154 .close = fat_close, 155 .ls = fs_ls_generic, 156 .exists = fat_exists, 157 .size = fat_size, 158 .read = fat_read_file, 159 #ifdef CONFIG_FAT_WRITE 160 .write = file_fat_write, 161 #else 162 .write = fs_write_unsupported, 163 #endif 164 .uuid = fs_uuid_unsupported, 165 .opendir = fat_opendir, 166 .readdir = fat_readdir, 167 .closedir = fat_closedir, 168 }, 169 #endif 170 #ifdef CONFIG_FS_EXT4 171 { 172 .fstype = FS_TYPE_EXT, 173 .name = "ext4", 174 .null_dev_desc_ok = false, 175 .probe = ext4fs_probe, 176 .close = ext4fs_close, 177 .ls = ext4fs_ls, 178 .exists = ext4fs_exists, 179 .size = ext4fs_size, 180 .read = ext4_read_file, 181 #ifdef CONFIG_CMD_EXT4_WRITE 182 .write = ext4_write_file, 183 #else 184 .write = fs_write_unsupported, 185 #endif 186 .uuid = ext4fs_uuid, 187 .opendir = fs_opendir_unsupported, 188 }, 189 #endif 190 #ifdef CONFIG_SANDBOX 191 { 192 .fstype = FS_TYPE_SANDBOX, 193 .name = "sandbox", 194 .null_dev_desc_ok = true, 195 .probe = sandbox_fs_set_blk_dev, 196 .close = sandbox_fs_close, 197 .ls = sandbox_fs_ls, 198 .exists = sandbox_fs_exists, 199 .size = sandbox_fs_size, 200 .read = fs_read_sandbox, 201 .write = fs_write_sandbox, 202 .uuid = fs_uuid_unsupported, 203 .opendir = fs_opendir_unsupported, 204 }, 205 #endif 206 #ifdef CONFIG_CMD_UBIFS 207 { 208 .fstype = FS_TYPE_UBIFS, 209 .name = "ubifs", 210 .null_dev_desc_ok = true, 211 .probe = ubifs_set_blk_dev, 212 .close = ubifs_close, 213 .ls = ubifs_ls, 214 .exists = ubifs_exists, 215 .size = ubifs_size, 216 .read = ubifs_read, 217 .write = fs_write_unsupported, 218 .uuid = fs_uuid_unsupported, 219 .opendir = fs_opendir_unsupported, 220 }, 221 #endif 222 #ifdef CONFIG_FS_BTRFS 223 { 224 .fstype = FS_TYPE_BTRFS, 225 .name = "btrfs", 226 .null_dev_desc_ok = false, 227 .probe = btrfs_probe, 228 .close = btrfs_close, 229 .ls = btrfs_ls, 230 .exists = btrfs_exists, 231 .size = btrfs_size, 232 .read = btrfs_read, 233 .write = fs_write_unsupported, 234 .uuid = btrfs_uuid, 235 .opendir = fs_opendir_unsupported, 236 }, 237 #endif 238 { 239 .fstype = FS_TYPE_ANY, 240 .name = "unsupported", 241 .null_dev_desc_ok = true, 242 .probe = fs_probe_unsupported, 243 .close = fs_close_unsupported, 244 .ls = fs_ls_unsupported, 245 .exists = fs_exists_unsupported, 246 .size = fs_size_unsupported, 247 .read = fs_read_unsupported, 248 .write = fs_write_unsupported, 249 .uuid = fs_uuid_unsupported, 250 .opendir = fs_opendir_unsupported, 251 }, 252 }; 253 254 static struct fstype_info *fs_get_info(int fstype) 255 { 256 struct fstype_info *info; 257 int i; 258 259 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes) - 1; i++, info++) { 260 if (fstype == info->fstype) 261 return info; 262 } 263 264 /* Return the 'unsupported' sentinel */ 265 return info; 266 } 267 268 /** 269 * fs_get_type_name() - Get type of current filesystem 270 * 271 * Return: Pointer to filesystem name 272 * 273 * Returns a string describing the current filesystem, or the sentinel 274 * "unsupported" for any unrecognised filesystem. 275 */ 276 const char *fs_get_type_name(void) 277 { 278 return fs_get_info(fs_type)->name; 279 } 280 281 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype) 282 { 283 struct fstype_info *info; 284 int part, i; 285 #ifdef CONFIG_NEEDS_MANUAL_RELOC 286 static int relocated; 287 288 if (!relocated) { 289 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); 290 i++, info++) { 291 info->name += gd->reloc_off; 292 info->probe += gd->reloc_off; 293 info->close += gd->reloc_off; 294 info->ls += gd->reloc_off; 295 info->read += gd->reloc_off; 296 info->write += gd->reloc_off; 297 } 298 relocated = 1; 299 } 300 #endif 301 302 part = blk_get_device_part_str(ifname, dev_part_str, &fs_dev_desc, 303 &fs_partition, 1); 304 if (part < 0) 305 return -1; 306 307 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) { 308 if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY && 309 fstype != info->fstype) 310 continue; 311 312 if (!fs_dev_desc && !info->null_dev_desc_ok) 313 continue; 314 315 if (!info->probe(fs_dev_desc, &fs_partition)) { 316 fs_type = info->fstype; 317 fs_dev_part = part; 318 return 0; 319 } 320 } 321 322 return -1; 323 } 324 325 /* set current blk device w/ blk_desc + partition # */ 326 int fs_set_blk_dev_with_part(struct blk_desc *desc, int part) 327 { 328 struct fstype_info *info; 329 int ret, i; 330 331 if (part >= 1) 332 ret = part_get_info(desc, part, &fs_partition); 333 else 334 ret = part_get_info_whole_disk(desc, &fs_partition); 335 if (ret) 336 return ret; 337 fs_dev_desc = desc; 338 339 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) { 340 if (!info->probe(fs_dev_desc, &fs_partition)) { 341 fs_type = info->fstype; 342 return 0; 343 } 344 } 345 346 return -1; 347 } 348 349 static void fs_close(void) 350 { 351 struct fstype_info *info = fs_get_info(fs_type); 352 353 info->close(); 354 355 fs_type = FS_TYPE_ANY; 356 } 357 358 int fs_uuid(char *uuid_str) 359 { 360 struct fstype_info *info = fs_get_info(fs_type); 361 362 return info->uuid(uuid_str); 363 } 364 365 int fs_ls(const char *dirname) 366 { 367 int ret; 368 369 struct fstype_info *info = fs_get_info(fs_type); 370 371 ret = info->ls(dirname); 372 373 fs_type = FS_TYPE_ANY; 374 fs_close(); 375 376 return ret; 377 } 378 379 int fs_exists(const char *filename) 380 { 381 int ret; 382 383 struct fstype_info *info = fs_get_info(fs_type); 384 385 ret = info->exists(filename); 386 387 fs_close(); 388 389 return ret; 390 } 391 392 int fs_size(const char *filename, loff_t *size) 393 { 394 int ret; 395 396 struct fstype_info *info = fs_get_info(fs_type); 397 398 ret = info->size(filename, size); 399 400 fs_close(); 401 402 return ret; 403 } 404 405 int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len, 406 loff_t *actread) 407 { 408 struct fstype_info *info = fs_get_info(fs_type); 409 void *buf; 410 int ret; 411 412 /* 413 * We don't actually know how many bytes are being read, since len==0 414 * means read the whole file. 415 */ 416 buf = map_sysmem(addr, len); 417 ret = info->read(filename, buf, offset, len, actread); 418 unmap_sysmem(buf); 419 420 /* If we requested a specific number of bytes, check we got it */ 421 if (ret == 0 && len && *actread != len) 422 debug("** %s shorter than offset + len **\n", filename); 423 fs_close(); 424 425 return ret; 426 } 427 428 int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len, 429 loff_t *actwrite) 430 { 431 struct fstype_info *info = fs_get_info(fs_type); 432 void *buf; 433 int ret; 434 435 buf = map_sysmem(addr, len); 436 ret = info->write(filename, buf, offset, len, actwrite); 437 unmap_sysmem(buf); 438 439 if (ret < 0 && len != *actwrite) { 440 printf("** Unable to write file %s **\n", filename); 441 ret = -1; 442 } 443 fs_close(); 444 445 return ret; 446 } 447 448 struct fs_dir_stream *fs_opendir(const char *filename) 449 { 450 struct fstype_info *info = fs_get_info(fs_type); 451 struct fs_dir_stream *dirs = NULL; 452 int ret; 453 454 ret = info->opendir(filename, &dirs); 455 fs_close(); 456 if (ret) { 457 errno = -ret; 458 return NULL; 459 } 460 461 dirs->desc = fs_dev_desc; 462 dirs->part = fs_dev_part; 463 464 return dirs; 465 } 466 467 struct fs_dirent *fs_readdir(struct fs_dir_stream *dirs) 468 { 469 struct fstype_info *info; 470 struct fs_dirent *dirent; 471 int ret; 472 473 fs_set_blk_dev_with_part(dirs->desc, dirs->part); 474 info = fs_get_info(fs_type); 475 476 ret = info->readdir(dirs, &dirent); 477 fs_close(); 478 if (ret) { 479 errno = -ret; 480 return NULL; 481 } 482 483 return dirent; 484 } 485 486 void fs_closedir(struct fs_dir_stream *dirs) 487 { 488 struct fstype_info *info; 489 490 if (!dirs) 491 return; 492 493 fs_set_blk_dev_with_part(dirs->desc, dirs->part); 494 info = fs_get_info(fs_type); 495 496 info->closedir(dirs); 497 fs_close(); 498 } 499 500 501 int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 502 int fstype) 503 { 504 loff_t size; 505 506 if (argc != 4) 507 return CMD_RET_USAGE; 508 509 if (fs_set_blk_dev(argv[1], argv[2], fstype)) 510 return 1; 511 512 if (fs_size(argv[3], &size) < 0) 513 return CMD_RET_FAILURE; 514 515 env_set_hex("filesize", size); 516 517 return 0; 518 } 519 520 int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 521 int fstype) 522 { 523 unsigned long addr; 524 const char *addr_str; 525 const char *filename; 526 loff_t bytes; 527 loff_t pos; 528 loff_t len_read; 529 int ret; 530 unsigned long time; 531 char *ep; 532 533 if (argc < 2) 534 return CMD_RET_USAGE; 535 if (argc > 7) 536 return CMD_RET_USAGE; 537 538 if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) 539 return 1; 540 541 if (argc >= 4) { 542 addr = simple_strtoul(argv[3], &ep, 16); 543 if (ep == argv[3] || *ep != '\0') 544 return CMD_RET_USAGE; 545 } else { 546 addr_str = env_get("loadaddr"); 547 if (addr_str != NULL) 548 addr = simple_strtoul(addr_str, NULL, 16); 549 else 550 addr = CONFIG_SYS_LOAD_ADDR; 551 } 552 if (argc >= 5) { 553 filename = argv[4]; 554 } else { 555 filename = env_get("bootfile"); 556 if (!filename) { 557 puts("** No boot file defined **\n"); 558 return 1; 559 } 560 } 561 if (argc >= 6) 562 bytes = simple_strtoul(argv[5], NULL, 16); 563 else 564 bytes = 0; 565 if (argc >= 7) 566 pos = simple_strtoul(argv[6], NULL, 16); 567 else 568 pos = 0; 569 570 time = get_timer(0); 571 ret = fs_read(filename, addr, pos, bytes, &len_read); 572 time = get_timer(time); 573 if (ret < 0) 574 return 1; 575 576 printf("%llu bytes read in %lu ms", len_read, time); 577 if (time > 0) { 578 puts(" ("); 579 print_size(div_u64(len_read, time) * 1000, "/s"); 580 puts(")"); 581 } 582 puts("\n"); 583 584 env_set_hex("fileaddr", addr); 585 env_set_hex("filesize", len_read); 586 587 return 0; 588 } 589 590 int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 591 int fstype) 592 { 593 if (argc < 2) 594 return CMD_RET_USAGE; 595 if (argc > 4) 596 return CMD_RET_USAGE; 597 598 if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) 599 return 1; 600 601 if (fs_ls(argc >= 4 ? argv[3] : "/")) 602 return 1; 603 604 return 0; 605 } 606 607 int file_exists(const char *dev_type, const char *dev_part, const char *file, 608 int fstype) 609 { 610 if (fs_set_blk_dev(dev_type, dev_part, fstype)) 611 return 0; 612 613 return fs_exists(file); 614 } 615 616 int do_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 617 int fstype) 618 { 619 unsigned long addr; 620 const char *filename; 621 loff_t bytes; 622 loff_t pos; 623 loff_t len; 624 int ret; 625 unsigned long time; 626 627 if (argc < 6 || argc > 7) 628 return CMD_RET_USAGE; 629 630 if (fs_set_blk_dev(argv[1], argv[2], fstype)) 631 return 1; 632 633 addr = simple_strtoul(argv[3], NULL, 16); 634 filename = argv[4]; 635 bytes = simple_strtoul(argv[5], NULL, 16); 636 if (argc >= 7) 637 pos = simple_strtoul(argv[6], NULL, 16); 638 else 639 pos = 0; 640 641 time = get_timer(0); 642 ret = fs_write(filename, addr, pos, bytes, &len); 643 time = get_timer(time); 644 if (ret < 0) 645 return 1; 646 647 printf("%llu bytes written in %lu ms", len, time); 648 if (time > 0) { 649 puts(" ("); 650 print_size(div_u64(len, time) * 1000, "/s"); 651 puts(")"); 652 } 653 puts("\n"); 654 655 return 0; 656 } 657 658 int do_fs_uuid(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 659 int fstype) 660 { 661 int ret; 662 char uuid[37]; 663 memset(uuid, 0, sizeof(uuid)); 664 665 if (argc < 3 || argc > 4) 666 return CMD_RET_USAGE; 667 668 if (fs_set_blk_dev(argv[1], argv[2], fstype)) 669 return 1; 670 671 ret = fs_uuid(uuid); 672 if (ret) 673 return CMD_RET_FAILURE; 674 675 if (argc == 4) 676 env_set(argv[3], uuid); 677 else 678 printf("%s\n", uuid); 679 680 return CMD_RET_SUCCESS; 681 } 682 683 int do_fs_type(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 684 { 685 struct fstype_info *info; 686 687 if (argc < 3 || argc > 4) 688 return CMD_RET_USAGE; 689 690 if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY)) 691 return 1; 692 693 info = fs_get_info(fs_type); 694 695 if (argc == 4) 696 env_set(argv[3], info->name); 697 else 698 printf("%s\n", info->name); 699 700 return CMD_RET_SUCCESS; 701 } 702 703