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 struct fstype_info { 54 int fstype; 55 int (*probe)(block_dev_desc_t *fs_dev_desc, 56 disk_partition_t *fs_partition); 57 int (*ls)(const char *dirname); 58 int (*read)(const char *filename, void *buf, int offset, int len); 59 void (*close)(void); 60 }; 61 62 static struct fstype_info fstypes[] = { 63 #ifdef CONFIG_FS_FAT 64 { 65 .fstype = FS_TYPE_FAT, 66 .probe = fat_set_blk_dev, 67 .close = fat_close, 68 .ls = file_fat_ls, 69 .read = fat_read_file, 70 }, 71 #endif 72 #ifdef CONFIG_FS_EXT4 73 { 74 .fstype = FS_TYPE_EXT, 75 .probe = ext4fs_probe, 76 .close = ext4fs_close, 77 .ls = ext4fs_ls, 78 .read = ext4_read_file, 79 }, 80 #endif 81 { 82 .fstype = FS_TYPE_ANY, 83 .probe = fs_probe_unsupported, 84 .close = fs_close_unsupported, 85 .ls = fs_ls_unsupported, 86 .read = fs_read_unsupported, 87 }, 88 }; 89 90 static struct fstype_info *fs_get_info(int fstype) 91 { 92 struct fstype_info *info; 93 int i; 94 95 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes) - 1; i++, info++) { 96 if (fstype == info->fstype) 97 return info; 98 } 99 100 /* Return the 'unsupported' sentinel */ 101 return info; 102 } 103 104 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype) 105 { 106 struct fstype_info *info; 107 int part, i; 108 #ifdef CONFIG_NEEDS_MANUAL_RELOC 109 static int relocated; 110 111 if (!relocated) { 112 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); 113 i++, info++) { 114 info->probe += gd->reloc_off; 115 info->close += gd->reloc_off; 116 info->ls += gd->reloc_off; 117 info->read += gd->reloc_off; 118 } 119 relocated = 1; 120 } 121 #endif 122 123 part = get_device_and_partition(ifname, dev_part_str, &fs_dev_desc, 124 &fs_partition, 1); 125 if (part < 0) 126 return -1; 127 128 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) { 129 if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY && 130 fstype != info->fstype) 131 continue; 132 133 if (!info->probe(fs_dev_desc, &fs_partition)) { 134 fs_type = info->fstype; 135 return 0; 136 } 137 } 138 139 return -1; 140 } 141 142 static void fs_close(void) 143 { 144 struct fstype_info *info = fs_get_info(fs_type); 145 146 info->close(); 147 148 fs_type = FS_TYPE_ANY; 149 } 150 151 int fs_ls(const char *dirname) 152 { 153 int ret; 154 155 struct fstype_info *info = fs_get_info(fs_type); 156 157 ret = info->ls(dirname); 158 159 fs_type = FS_TYPE_ANY; 160 fs_close(); 161 162 return ret; 163 } 164 165 int fs_read(const char *filename, ulong addr, int offset, int len) 166 { 167 struct fstype_info *info = fs_get_info(fs_type); 168 void *buf; 169 int ret; 170 171 /* 172 * We don't actually know how many bytes are being read, since len==0 173 * means read the whole file. 174 */ 175 buf = map_sysmem(addr, len); 176 ret = info->read(filename, buf, offset, len); 177 unmap_sysmem(buf); 178 179 /* If we requested a specific number of bytes, check we got it */ 180 if (ret >= 0 && len && ret != len) { 181 printf("** Unable to read file %s **\n", filename); 182 ret = -1; 183 } 184 fs_close(); 185 186 return ret; 187 } 188 189 int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 190 int fstype, int cmdline_base) 191 { 192 unsigned long addr; 193 const char *addr_str; 194 const char *filename; 195 unsigned long bytes; 196 unsigned long pos; 197 int len_read; 198 unsigned long time; 199 200 if (argc < 2) 201 return CMD_RET_USAGE; 202 if (argc > 7) 203 return CMD_RET_USAGE; 204 205 if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) 206 return 1; 207 208 if (argc >= 4) { 209 addr = simple_strtoul(argv[3], NULL, cmdline_base); 210 } else { 211 addr_str = getenv("loadaddr"); 212 if (addr_str != NULL) 213 addr = simple_strtoul(addr_str, NULL, 16); 214 else 215 addr = CONFIG_SYS_LOAD_ADDR; 216 } 217 if (argc >= 5) { 218 filename = argv[4]; 219 } else { 220 filename = getenv("bootfile"); 221 if (!filename) { 222 puts("** No boot file defined **\n"); 223 return 1; 224 } 225 } 226 if (argc >= 6) 227 bytes = simple_strtoul(argv[5], NULL, cmdline_base); 228 else 229 bytes = 0; 230 if (argc >= 7) 231 pos = simple_strtoul(argv[6], NULL, cmdline_base); 232 else 233 pos = 0; 234 235 time = get_timer(0); 236 len_read = fs_read(filename, addr, pos, bytes); 237 time = get_timer(time); 238 if (len_read <= 0) 239 return 1; 240 241 printf("%d bytes read in %lu ms", len_read, time); 242 if (time > 0) { 243 puts(" ("); 244 print_size(len_read / time * 1000, "/s"); 245 puts(")"); 246 } 247 puts("\n"); 248 249 setenv_hex("filesize", len_read); 250 251 return 0; 252 } 253 254 int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 255 int fstype) 256 { 257 if (argc < 2) 258 return CMD_RET_USAGE; 259 if (argc > 4) 260 return CMD_RET_USAGE; 261 262 if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) 263 return 1; 264 265 if (fs_ls(argc >= 4 ? argv[3] : "/")) 266 return 1; 267 268 return 0; 269 } 270