1 /* 2 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms and conditions of the GNU General Public License, 6 * version 2, as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 #include <config.h> 18 #include <errno.h> 19 #include <common.h> 20 #include <mapmem.h> 21 #include <part.h> 22 #include <ext4fs.h> 23 #include <fat.h> 24 #include <fs.h> 25 #include <sandboxfs.h> 26 #include <asm/io.h> 27 #include <div64.h> 28 #include <linux/math64.h> 29 30 DECLARE_GLOBAL_DATA_PTR; 31 32 static block_dev_desc_t *fs_dev_desc; 33 static disk_partition_t fs_partition; 34 static int fs_type = FS_TYPE_ANY; 35 36 static inline int fs_probe_unsupported(block_dev_desc_t *fs_dev_desc, 37 disk_partition_t *fs_partition) 38 { 39 printf("** Unrecognized filesystem type **\n"); 40 return -1; 41 } 42 43 static inline int fs_ls_unsupported(const char *dirname) 44 { 45 return -1; 46 } 47 48 static inline int fs_exists_unsupported(const char *filename) 49 { 50 return 0; 51 } 52 53 static inline int fs_size_unsupported(const char *filename, loff_t *size) 54 { 55 return -1; 56 } 57 58 static inline int fs_read_unsupported(const char *filename, void *buf, 59 loff_t offset, loff_t len, 60 loff_t *actread) 61 { 62 return -1; 63 } 64 65 static inline int fs_write_unsupported(const char *filename, void *buf, 66 loff_t offset, loff_t len, 67 loff_t *actwrite) 68 { 69 return -1; 70 } 71 72 static inline void fs_close_unsupported(void) 73 { 74 } 75 76 static inline int fs_uuid_unsupported(char *uuid_str) 77 { 78 return -1; 79 } 80 81 struct fstype_info { 82 int fstype; 83 char *name; 84 /* 85 * Is it legal to pass NULL as .probe()'s fs_dev_desc parameter? This 86 * should be false in most cases. For "virtual" filesystems which 87 * aren't based on a U-Boot block device (e.g. sandbox), this can be 88 * set to true. This should also be true for the dumm entry at the end 89 * of fstypes[], since that is essentially a "virtual" (non-existent) 90 * filesystem. 91 */ 92 bool null_dev_desc_ok; 93 int (*probe)(block_dev_desc_t *fs_dev_desc, 94 disk_partition_t *fs_partition); 95 int (*ls)(const char *dirname); 96 int (*exists)(const char *filename); 97 int (*size)(const char *filename, loff_t *size); 98 int (*read)(const char *filename, void *buf, loff_t offset, 99 loff_t len, loff_t *actread); 100 int (*write)(const char *filename, void *buf, loff_t offset, 101 loff_t len, loff_t *actwrite); 102 void (*close)(void); 103 int (*uuid)(char *uuid_str); 104 }; 105 106 static struct fstype_info fstypes[] = { 107 #ifdef CONFIG_FS_FAT 108 { 109 .fstype = FS_TYPE_FAT, 110 .name = "fat", 111 .null_dev_desc_ok = false, 112 .probe = fat_set_blk_dev, 113 .close = fat_close, 114 .ls = file_fat_ls, 115 .exists = fat_exists, 116 .size = fat_size, 117 .read = fat_read_file, 118 #ifdef CONFIG_FAT_WRITE 119 .write = file_fat_write, 120 #else 121 .write = fs_write_unsupported, 122 #endif 123 .uuid = fs_uuid_unsupported, 124 }, 125 #endif 126 #ifdef CONFIG_FS_EXT4 127 { 128 .fstype = FS_TYPE_EXT, 129 .name = "ext4", 130 .null_dev_desc_ok = false, 131 .probe = ext4fs_probe, 132 .close = ext4fs_close, 133 .ls = ext4fs_ls, 134 .exists = ext4fs_exists, 135 .size = ext4fs_size, 136 .read = ext4_read_file, 137 #ifdef CONFIG_CMD_EXT4_WRITE 138 .write = ext4_write_file, 139 #else 140 .write = fs_write_unsupported, 141 #endif 142 .uuid = ext4fs_uuid, 143 }, 144 #endif 145 #ifdef CONFIG_SANDBOX 146 { 147 .fstype = FS_TYPE_SANDBOX, 148 .name = "sandbox", 149 .null_dev_desc_ok = true, 150 .probe = sandbox_fs_set_blk_dev, 151 .close = sandbox_fs_close, 152 .ls = sandbox_fs_ls, 153 .exists = sandbox_fs_exists, 154 .size = sandbox_fs_size, 155 .read = fs_read_sandbox, 156 .write = fs_write_sandbox, 157 .uuid = fs_uuid_unsupported, 158 }, 159 #endif 160 { 161 .fstype = FS_TYPE_ANY, 162 .name = "unsupported", 163 .null_dev_desc_ok = true, 164 .probe = fs_probe_unsupported, 165 .close = fs_close_unsupported, 166 .ls = fs_ls_unsupported, 167 .exists = fs_exists_unsupported, 168 .size = fs_size_unsupported, 169 .read = fs_read_unsupported, 170 .write = fs_write_unsupported, 171 .uuid = fs_uuid_unsupported, 172 }, 173 }; 174 175 static struct fstype_info *fs_get_info(int fstype) 176 { 177 struct fstype_info *info; 178 int i; 179 180 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes) - 1; i++, info++) { 181 if (fstype == info->fstype) 182 return info; 183 } 184 185 /* Return the 'unsupported' sentinel */ 186 return info; 187 } 188 189 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype) 190 { 191 struct fstype_info *info; 192 int part, i; 193 #ifdef CONFIG_NEEDS_MANUAL_RELOC 194 static int relocated; 195 196 if (!relocated) { 197 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); 198 i++, info++) { 199 info->name += gd->reloc_off; 200 info->probe += gd->reloc_off; 201 info->close += gd->reloc_off; 202 info->ls += gd->reloc_off; 203 info->read += gd->reloc_off; 204 info->write += gd->reloc_off; 205 } 206 relocated = 1; 207 } 208 #endif 209 210 part = get_device_and_partition(ifname, dev_part_str, &fs_dev_desc, 211 &fs_partition, 1); 212 if (part < 0) 213 return -1; 214 215 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) { 216 if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY && 217 fstype != info->fstype) 218 continue; 219 220 if (!fs_dev_desc && !info->null_dev_desc_ok) 221 continue; 222 223 if (!info->probe(fs_dev_desc, &fs_partition)) { 224 fs_type = info->fstype; 225 return 0; 226 } 227 } 228 229 return -1; 230 } 231 232 static void fs_close(void) 233 { 234 struct fstype_info *info = fs_get_info(fs_type); 235 236 info->close(); 237 238 fs_type = FS_TYPE_ANY; 239 } 240 241 int fs_uuid(char *uuid_str) 242 { 243 struct fstype_info *info = fs_get_info(fs_type); 244 245 return info->uuid(uuid_str); 246 } 247 248 int fs_ls(const char *dirname) 249 { 250 int ret; 251 252 struct fstype_info *info = fs_get_info(fs_type); 253 254 ret = info->ls(dirname); 255 256 fs_type = FS_TYPE_ANY; 257 fs_close(); 258 259 return ret; 260 } 261 262 int fs_exists(const char *filename) 263 { 264 int ret; 265 266 struct fstype_info *info = fs_get_info(fs_type); 267 268 ret = info->exists(filename); 269 270 fs_close(); 271 272 return ret; 273 } 274 275 int fs_size(const char *filename, loff_t *size) 276 { 277 int ret; 278 279 struct fstype_info *info = fs_get_info(fs_type); 280 281 ret = info->size(filename, size); 282 283 fs_close(); 284 285 return ret; 286 } 287 288 int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len, 289 loff_t *actread) 290 { 291 struct fstype_info *info = fs_get_info(fs_type); 292 void *buf; 293 int ret; 294 295 /* 296 * We don't actually know how many bytes are being read, since len==0 297 * means read the whole file. 298 */ 299 buf = map_sysmem(addr, len); 300 ret = info->read(filename, buf, offset, len, actread); 301 unmap_sysmem(buf); 302 303 /* If we requested a specific number of bytes, check we got it */ 304 if (ret == 0 && len && *actread != len) { 305 printf("** Unable to read file %s **\n", filename); 306 ret = -1; 307 } 308 fs_close(); 309 310 return ret; 311 } 312 313 int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len, 314 loff_t *actwrite) 315 { 316 struct fstype_info *info = fs_get_info(fs_type); 317 void *buf; 318 int ret; 319 320 buf = map_sysmem(addr, len); 321 ret = info->write(filename, buf, offset, len, actwrite); 322 unmap_sysmem(buf); 323 324 if (ret < 0 && len != *actwrite) { 325 printf("** Unable to write file %s **\n", filename); 326 ret = -1; 327 } 328 fs_close(); 329 330 return ret; 331 } 332 333 int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 334 int fstype) 335 { 336 loff_t size; 337 338 if (argc != 4) 339 return CMD_RET_USAGE; 340 341 if (fs_set_blk_dev(argv[1], argv[2], fstype)) 342 return 1; 343 344 if (fs_size(argv[3], &size) < 0) 345 return CMD_RET_FAILURE; 346 347 setenv_hex("filesize", size); 348 349 return 0; 350 } 351 352 int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 353 int fstype) 354 { 355 unsigned long addr; 356 const char *addr_str; 357 const char *filename; 358 loff_t bytes; 359 loff_t pos; 360 loff_t len_read; 361 int ret; 362 unsigned long time; 363 char *ep; 364 365 if (argc < 2) 366 return CMD_RET_USAGE; 367 if (argc > 7) 368 return CMD_RET_USAGE; 369 370 if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) 371 return 1; 372 373 if (argc >= 4) { 374 addr = simple_strtoul(argv[3], &ep, 16); 375 if (ep == argv[3] || *ep != '\0') 376 return CMD_RET_USAGE; 377 } else { 378 addr_str = getenv("loadaddr"); 379 if (addr_str != NULL) 380 addr = simple_strtoul(addr_str, NULL, 16); 381 else 382 addr = CONFIG_SYS_LOAD_ADDR; 383 } 384 if (argc >= 5) { 385 filename = argv[4]; 386 } else { 387 filename = getenv("bootfile"); 388 if (!filename) { 389 puts("** No boot file defined **\n"); 390 return 1; 391 } 392 } 393 if (argc >= 6) 394 bytes = simple_strtoul(argv[5], NULL, 16); 395 else 396 bytes = 0; 397 if (argc >= 7) 398 pos = simple_strtoul(argv[6], NULL, 16); 399 else 400 pos = 0; 401 402 time = get_timer(0); 403 ret = fs_read(filename, addr, pos, bytes, &len_read); 404 time = get_timer(time); 405 if (ret < 0) 406 return 1; 407 408 printf("%llu bytes read in %lu ms", len_read, time); 409 if (time > 0) { 410 puts(" ("); 411 print_size(div_u64(len_read, time) * 1000, "/s"); 412 puts(")"); 413 } 414 puts("\n"); 415 416 setenv_hex("filesize", len_read); 417 418 return 0; 419 } 420 421 int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 422 int fstype) 423 { 424 if (argc < 2) 425 return CMD_RET_USAGE; 426 if (argc > 4) 427 return CMD_RET_USAGE; 428 429 if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) 430 return 1; 431 432 if (fs_ls(argc >= 4 ? argv[3] : "/")) 433 return 1; 434 435 return 0; 436 } 437 438 int file_exists(const char *dev_type, const char *dev_part, const char *file, 439 int fstype) 440 { 441 if (fs_set_blk_dev(dev_type, dev_part, fstype)) 442 return 0; 443 444 return fs_exists(file); 445 } 446 447 int do_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 448 int fstype) 449 { 450 unsigned long addr; 451 const char *filename; 452 loff_t bytes; 453 loff_t pos; 454 loff_t len; 455 int ret; 456 unsigned long time; 457 458 if (argc < 6 || argc > 7) 459 return CMD_RET_USAGE; 460 461 if (fs_set_blk_dev(argv[1], argv[2], fstype)) 462 return 1; 463 464 addr = simple_strtoul(argv[3], NULL, 16); 465 filename = argv[4]; 466 bytes = simple_strtoul(argv[5], NULL, 16); 467 if (argc >= 7) 468 pos = simple_strtoul(argv[6], NULL, 16); 469 else 470 pos = 0; 471 472 time = get_timer(0); 473 ret = fs_write(filename, addr, pos, bytes, &len); 474 time = get_timer(time); 475 if (ret < 0) 476 return 1; 477 478 printf("%llu bytes written in %lu ms", len, time); 479 if (time > 0) { 480 puts(" ("); 481 print_size(div_u64(len, time) * 1000, "/s"); 482 puts(")"); 483 } 484 puts("\n"); 485 486 return 0; 487 } 488 489 int do_fs_uuid(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 490 int fstype) 491 { 492 int ret; 493 char uuid[37]; 494 memset(uuid, 0, sizeof(uuid)); 495 496 if (argc < 3 || argc > 4) 497 return CMD_RET_USAGE; 498 499 if (fs_set_blk_dev(argv[1], argv[2], fstype)) 500 return 1; 501 502 ret = fs_uuid(uuid); 503 if (ret) 504 return CMD_RET_FAILURE; 505 506 if (argc == 4) 507 setenv(argv[3], uuid); 508 else 509 printf("%s\n", uuid); 510 511 return CMD_RET_SUCCESS; 512 } 513 514 int do_fs_type(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 515 { 516 struct fstype_info *info; 517 518 if (argc < 3 || argc > 4) 519 return CMD_RET_USAGE; 520 521 if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY)) 522 return 1; 523 524 info = fs_get_info(fs_type); 525 526 if (argc == 4) 527 setenv(argv[3], info->name); 528 else 529 printf("%s\n", info->name); 530 531 return CMD_RET_SUCCESS; 532 } 533 534