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