xref: /rk3399_rockchip-uboot/fs/fs.c (revision 117e050727d1f76bfc465f23eb3aa10f9f15cc1b)
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