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