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 24 DECLARE_GLOBAL_DATA_PTR; 25 26 static block_dev_desc_t *fs_dev_desc; 27 static disk_partition_t fs_partition; 28 static int fs_type = FS_TYPE_ANY; 29 30 static inline int fs_probe_unsupported(void) 31 { 32 printf("** Unrecognized filesystem type **\n"); 33 return -1; 34 } 35 36 static inline int fs_ls_unsupported(const char *dirname) 37 { 38 printf("** Unrecognized filesystem type **\n"); 39 return -1; 40 } 41 42 static inline int fs_read_unsupported(const char *filename, ulong addr, 43 int offset, int len) 44 { 45 printf("** Unrecognized filesystem type **\n"); 46 return -1; 47 } 48 49 static inline void fs_close_unsupported(void) 50 { 51 } 52 53 #ifdef CONFIG_FS_FAT 54 static int fs_probe_fat(void) 55 { 56 return fat_set_blk_dev(fs_dev_desc, &fs_partition); 57 } 58 59 static void fs_close_fat(void) 60 { 61 } 62 63 #define fs_ls_fat file_fat_ls 64 65 static int fs_read_fat(const char *filename, ulong addr, int offset, int len) 66 { 67 int len_read; 68 69 len_read = file_fat_read_at(filename, offset, 70 (unsigned char *)addr, len); 71 if (len_read == -1) { 72 printf("** Unable to read file %s **\n", filename); 73 return -1; 74 } 75 76 return len_read; 77 } 78 #else 79 static inline int fs_probe_fat(void) 80 { 81 return -1; 82 } 83 84 static inline void fs_close_fat(void) 85 { 86 } 87 88 #define fs_ls_fat fs_ls_unsupported 89 #define fs_read_fat fs_read_unsupported 90 #endif 91 92 #ifdef CONFIG_FS_EXT4 93 static int fs_probe_ext(void) 94 { 95 ext4fs_set_blk_dev(fs_dev_desc, &fs_partition); 96 97 if (!ext4fs_mount(fs_partition.size)) { 98 ext4fs_close(); 99 return -1; 100 } 101 102 return 0; 103 } 104 105 static void fs_close_ext(void) 106 { 107 ext4fs_close(); 108 } 109 110 #define fs_ls_ext ext4fs_ls 111 112 static int fs_read_ext(const char *filename, ulong addr, int offset, int len) 113 { 114 int file_len; 115 int len_read; 116 117 if (offset != 0) { 118 printf("** Cannot support non-zero offset **\n"); 119 return -1; 120 } 121 122 file_len = ext4fs_open(filename); 123 if (file_len < 0) { 124 printf("** File not found %s **\n", filename); 125 ext4fs_close(); 126 return -1; 127 } 128 129 if (len == 0) 130 len = file_len; 131 132 len_read = ext4fs_read((char *)addr, len); 133 ext4fs_close(); 134 135 if (len_read != len) { 136 printf("** Unable to read file %s **\n", filename); 137 return -1; 138 } 139 140 return len_read; 141 } 142 #else 143 static inline int fs_probe_ext(void) 144 { 145 return -1; 146 } 147 148 static inline void fs_close_ext(void) 149 { 150 } 151 152 #define fs_ls_ext fs_ls_unsupported 153 #define fs_read_ext fs_read_unsupported 154 #endif 155 156 struct fstype_info { 157 int fstype; 158 int (*probe)(void); 159 int (*ls)(const char *dirname); 160 int (*read)(const char *filename, ulong addr, int offset, int len); 161 void (*close)(void); 162 }; 163 164 static struct fstype_info fstypes[] = { 165 #ifdef CONFIG_FS_FAT 166 { 167 .fstype = FS_TYPE_FAT, 168 .probe = fs_probe_fat, 169 .close = fs_close_fat, 170 .ls = file_fat_ls, 171 .read = fs_read_fat, 172 }, 173 #endif 174 #ifdef CONFIG_FS_EXT4 175 { 176 .fstype = FS_TYPE_EXT, 177 .probe = fs_probe_ext, 178 .close = fs_close_ext, 179 .ls = ext4fs_ls, 180 .read = fs_read_ext, 181 }, 182 #endif 183 { 184 .fstype = FS_TYPE_ANY, 185 .probe = fs_probe_unsupported, 186 .close = fs_close_unsupported, 187 .ls = fs_ls_unsupported, 188 .read = fs_read_unsupported, 189 }, 190 }; 191 192 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype) 193 { 194 struct fstype_info *info; 195 int part, i; 196 #ifdef CONFIG_NEEDS_MANUAL_RELOC 197 static int relocated; 198 199 if (!relocated) { 200 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); 201 i++, info++) { 202 info->probe += gd->reloc_off; 203 info->close += gd->reloc_off; 204 info->ls += gd->reloc_off; 205 info->read += gd->reloc_off; 206 } 207 relocated = 1; 208 } 209 #endif 210 211 part = get_device_and_partition(ifname, dev_part_str, &fs_dev_desc, 212 &fs_partition, 1); 213 if (part < 0) 214 return -1; 215 216 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) { 217 if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY && 218 fstype != info->fstype) 219 continue; 220 221 if (!info->probe()) { 222 fs_type = info->fstype; 223 return 0; 224 } 225 } 226 227 return -1; 228 } 229 230 static void fs_close(void) 231 { 232 switch (fs_type) { 233 case FS_TYPE_FAT: 234 fs_close_fat(); 235 break; 236 case FS_TYPE_EXT: 237 fs_close_ext(); 238 break; 239 default: 240 break; 241 } 242 243 fs_type = FS_TYPE_ANY; 244 } 245 246 int fs_ls(const char *dirname) 247 { 248 int ret; 249 250 switch (fs_type) { 251 case FS_TYPE_FAT: 252 ret = fs_ls_fat(dirname); 253 break; 254 case FS_TYPE_EXT: 255 ret = fs_ls_ext(dirname); 256 break; 257 default: 258 ret = fs_ls_unsupported(dirname); 259 break; 260 } 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 int ret; 270 271 switch (fs_type) { 272 case FS_TYPE_FAT: 273 ret = fs_read_fat(filename, addr, offset, len); 274 break; 275 case FS_TYPE_EXT: 276 ret = fs_read_ext(filename, addr, offset, len); 277 break; 278 default: 279 ret = fs_read_unsupported(filename, addr, offset, len); 280 break; 281 } 282 283 fs_close(); 284 285 return ret; 286 } 287 288 int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 289 int fstype, int cmdline_base) 290 { 291 unsigned long addr; 292 const char *addr_str; 293 const char *filename; 294 unsigned long bytes; 295 unsigned long pos; 296 int len_read; 297 unsigned long time; 298 299 if (argc < 2) 300 return CMD_RET_USAGE; 301 if (argc > 7) 302 return CMD_RET_USAGE; 303 304 if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) 305 return 1; 306 307 if (argc >= 4) { 308 addr = simple_strtoul(argv[3], NULL, cmdline_base); 309 } else { 310 addr_str = getenv("loadaddr"); 311 if (addr_str != NULL) 312 addr = simple_strtoul(addr_str, NULL, 16); 313 else 314 addr = CONFIG_SYS_LOAD_ADDR; 315 } 316 if (argc >= 5) { 317 filename = argv[4]; 318 } else { 319 filename = getenv("bootfile"); 320 if (!filename) { 321 puts("** No boot file defined **\n"); 322 return 1; 323 } 324 } 325 if (argc >= 6) 326 bytes = simple_strtoul(argv[5], NULL, cmdline_base); 327 else 328 bytes = 0; 329 if (argc >= 7) 330 pos = simple_strtoul(argv[6], NULL, cmdline_base); 331 else 332 pos = 0; 333 334 time = get_timer(0); 335 len_read = fs_read(filename, addr, pos, bytes); 336 time = get_timer(time); 337 if (len_read <= 0) 338 return 1; 339 340 printf("%d bytes read in %lu ms", len_read, time); 341 if (time > 0) { 342 puts(" ("); 343 print_size(len_read / time * 1000, "/s"); 344 puts(")"); 345 } 346 puts("\n"); 347 348 setenv_hex("filesize", len_read); 349 350 return 0; 351 } 352 353 int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 354 int fstype) 355 { 356 if (argc < 2) 357 return CMD_RET_USAGE; 358 if (argc > 4) 359 return CMD_RET_USAGE; 360 361 if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) 362 return 1; 363 364 if (fs_ls(argc >= 4 ? argv[3] : "/")) 365 return 1; 366 367 return 0; 368 } 369