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("** %s shorter than offset + len **\n", filename); 306 fs_close(); 307 308 return ret; 309 } 310 311 int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len, 312 loff_t *actwrite) 313 { 314 struct fstype_info *info = fs_get_info(fs_type); 315 void *buf; 316 int ret; 317 318 buf = map_sysmem(addr, len); 319 ret = info->write(filename, buf, offset, len, actwrite); 320 unmap_sysmem(buf); 321 322 if (ret < 0 && len != *actwrite) { 323 printf("** Unable to write file %s **\n", filename); 324 ret = -1; 325 } 326 fs_close(); 327 328 return ret; 329 } 330 331 int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 332 int fstype) 333 { 334 loff_t size; 335 336 if (argc != 4) 337 return CMD_RET_USAGE; 338 339 if (fs_set_blk_dev(argv[1], argv[2], fstype)) 340 return 1; 341 342 if (fs_size(argv[3], &size) < 0) 343 return CMD_RET_FAILURE; 344 345 setenv_hex("filesize", size); 346 347 return 0; 348 } 349 350 int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 351 int fstype) 352 { 353 unsigned long addr; 354 const char *addr_str; 355 const char *filename; 356 loff_t bytes; 357 loff_t pos; 358 loff_t len_read; 359 int ret; 360 unsigned long time; 361 char *ep; 362 363 if (argc < 2) 364 return CMD_RET_USAGE; 365 if (argc > 7) 366 return CMD_RET_USAGE; 367 368 if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) 369 return 1; 370 371 if (argc >= 4) { 372 addr = simple_strtoul(argv[3], &ep, 16); 373 if (ep == argv[3] || *ep != '\0') 374 return CMD_RET_USAGE; 375 } else { 376 addr_str = getenv("loadaddr"); 377 if (addr_str != NULL) 378 addr = simple_strtoul(addr_str, NULL, 16); 379 else 380 addr = CONFIG_SYS_LOAD_ADDR; 381 } 382 if (argc >= 5) { 383 filename = argv[4]; 384 } else { 385 filename = getenv("bootfile"); 386 if (!filename) { 387 puts("** No boot file defined **\n"); 388 return 1; 389 } 390 } 391 if (argc >= 6) 392 bytes = simple_strtoul(argv[5], NULL, 16); 393 else 394 bytes = 0; 395 if (argc >= 7) 396 pos = simple_strtoul(argv[6], NULL, 16); 397 else 398 pos = 0; 399 400 time = get_timer(0); 401 ret = fs_read(filename, addr, pos, bytes, &len_read); 402 time = get_timer(time); 403 if (ret < 0) 404 return 1; 405 406 printf("%llu bytes read in %lu ms", len_read, time); 407 if (time > 0) { 408 puts(" ("); 409 print_size(div_u64(len_read, time) * 1000, "/s"); 410 puts(")"); 411 } 412 puts("\n"); 413 414 setenv_hex("filesize", len_read); 415 416 return 0; 417 } 418 419 int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 420 int fstype) 421 { 422 if (argc < 2) 423 return CMD_RET_USAGE; 424 if (argc > 4) 425 return CMD_RET_USAGE; 426 427 if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) 428 return 1; 429 430 if (fs_ls(argc >= 4 ? argv[3] : "/")) 431 return 1; 432 433 return 0; 434 } 435 436 int file_exists(const char *dev_type, const char *dev_part, const char *file, 437 int fstype) 438 { 439 if (fs_set_blk_dev(dev_type, dev_part, fstype)) 440 return 0; 441 442 return fs_exists(file); 443 } 444 445 int do_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 446 int fstype) 447 { 448 unsigned long addr; 449 const char *filename; 450 loff_t bytes; 451 loff_t pos; 452 loff_t len; 453 int ret; 454 unsigned long time; 455 456 if (argc < 6 || argc > 7) 457 return CMD_RET_USAGE; 458 459 if (fs_set_blk_dev(argv[1], argv[2], fstype)) 460 return 1; 461 462 addr = simple_strtoul(argv[3], NULL, 16); 463 filename = argv[4]; 464 bytes = simple_strtoul(argv[5], NULL, 16); 465 if (argc >= 7) 466 pos = simple_strtoul(argv[6], NULL, 16); 467 else 468 pos = 0; 469 470 time = get_timer(0); 471 ret = fs_write(filename, addr, pos, bytes, &len); 472 time = get_timer(time); 473 if (ret < 0) 474 return 1; 475 476 printf("%llu bytes written in %lu ms", len, time); 477 if (time > 0) { 478 puts(" ("); 479 print_size(div_u64(len, time) * 1000, "/s"); 480 puts(")"); 481 } 482 puts("\n"); 483 484 return 0; 485 } 486 487 int do_fs_uuid(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 488 int fstype) 489 { 490 int ret; 491 char uuid[37]; 492 memset(uuid, 0, sizeof(uuid)); 493 494 if (argc < 3 || argc > 4) 495 return CMD_RET_USAGE; 496 497 if (fs_set_blk_dev(argv[1], argv[2], fstype)) 498 return 1; 499 500 ret = fs_uuid(uuid); 501 if (ret) 502 return CMD_RET_FAILURE; 503 504 if (argc == 4) 505 setenv(argv[3], uuid); 506 else 507 printf("%s\n", uuid); 508 509 return CMD_RET_SUCCESS; 510 } 511 512 int do_fs_type(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 513 { 514 struct fstype_info *info; 515 516 if (argc < 3 || argc > 4) 517 return CMD_RET_USAGE; 518 519 if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY)) 520 return 1; 521 522 info = fs_get_info(fs_type); 523 524 if (argc == 4) 525 setenv(argv[3], info->name); 526 else 527 printf("%s\n", info->name); 528 529 return CMD_RET_SUCCESS; 530 } 531 532