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