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