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