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