xref: /openbmc/u-boot/fs/fs.c (revision 6645fd2c)
1 /*
2  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * SPDX-License-Identifier:	GPL-2.0
5  */
6 
7 #include <config.h>
8 #include <errno.h>
9 #include <common.h>
10 #include <mapmem.h>
11 #include <part.h>
12 #include <ext4fs.h>
13 #include <fat.h>
14 #include <fs.h>
15 #include <sandboxfs.h>
16 #include <ubifs_uboot.h>
17 #include <asm/io.h>
18 #include <div64.h>
19 #include <linux/math64.h>
20 
21 DECLARE_GLOBAL_DATA_PTR;
22 
23 static struct blk_desc *fs_dev_desc;
24 static disk_partition_t fs_partition;
25 static int fs_type = FS_TYPE_ANY;
26 
27 static inline int fs_probe_unsupported(struct blk_desc *fs_dev_desc,
28 				      disk_partition_t *fs_partition)
29 {
30 	printf("** Unrecognized filesystem type **\n");
31 	return -1;
32 }
33 
34 static inline int fs_ls_unsupported(const char *dirname)
35 {
36 	return -1;
37 }
38 
39 static inline int fs_exists_unsupported(const char *filename)
40 {
41 	return 0;
42 }
43 
44 static inline int fs_size_unsupported(const char *filename, loff_t *size)
45 {
46 	return -1;
47 }
48 
49 static inline int fs_read_unsupported(const char *filename, void *buf,
50 				      loff_t offset, loff_t len,
51 				      loff_t *actread)
52 {
53 	return -1;
54 }
55 
56 static inline int fs_write_unsupported(const char *filename, void *buf,
57 				      loff_t offset, loff_t len,
58 				      loff_t *actwrite)
59 {
60 	return -1;
61 }
62 
63 static inline void fs_close_unsupported(void)
64 {
65 }
66 
67 static inline int fs_uuid_unsupported(char *uuid_str)
68 {
69 	return -1;
70 }
71 
72 struct fstype_info {
73 	int fstype;
74 	char *name;
75 	/*
76 	 * Is it legal to pass NULL as .probe()'s  fs_dev_desc parameter? This
77 	 * should be false in most cases. For "virtual" filesystems which
78 	 * aren't based on a U-Boot block device (e.g. sandbox), this can be
79 	 * set to true. This should also be true for the dumm entry at the end
80 	 * of fstypes[], since that is essentially a "virtual" (non-existent)
81 	 * filesystem.
82 	 */
83 	bool null_dev_desc_ok;
84 	int (*probe)(struct blk_desc *fs_dev_desc,
85 		     disk_partition_t *fs_partition);
86 	int (*ls)(const char *dirname);
87 	int (*exists)(const char *filename);
88 	int (*size)(const char *filename, loff_t *size);
89 	int (*read)(const char *filename, void *buf, loff_t offset,
90 		    loff_t len, loff_t *actread);
91 	int (*write)(const char *filename, void *buf, loff_t offset,
92 		     loff_t len, loff_t *actwrite);
93 	void (*close)(void);
94 	int (*uuid)(char *uuid_str);
95 };
96 
97 static struct fstype_info fstypes[] = {
98 #ifdef CONFIG_FS_FAT
99 	{
100 		.fstype = FS_TYPE_FAT,
101 		.name = "fat",
102 		.null_dev_desc_ok = false,
103 		.probe = fat_set_blk_dev,
104 		.close = fat_close,
105 		.ls = file_fat_ls,
106 		.exists = fat_exists,
107 		.size = fat_size,
108 		.read = fat_read_file,
109 #ifdef CONFIG_FAT_WRITE
110 		.write = file_fat_write,
111 #else
112 		.write = fs_write_unsupported,
113 #endif
114 		.uuid = fs_uuid_unsupported,
115 	},
116 #endif
117 #ifdef CONFIG_FS_EXT4
118 	{
119 		.fstype = FS_TYPE_EXT,
120 		.name = "ext4",
121 		.null_dev_desc_ok = false,
122 		.probe = ext4fs_probe,
123 		.close = ext4fs_close,
124 		.ls = ext4fs_ls,
125 		.exists = ext4fs_exists,
126 		.size = ext4fs_size,
127 		.read = ext4_read_file,
128 #ifdef CONFIG_CMD_EXT4_WRITE
129 		.write = ext4_write_file,
130 #else
131 		.write = fs_write_unsupported,
132 #endif
133 		.uuid = ext4fs_uuid,
134 	},
135 #endif
136 #ifdef CONFIG_SANDBOX
137 	{
138 		.fstype = FS_TYPE_SANDBOX,
139 		.name = "sandbox",
140 		.null_dev_desc_ok = true,
141 		.probe = sandbox_fs_set_blk_dev,
142 		.close = sandbox_fs_close,
143 		.ls = sandbox_fs_ls,
144 		.exists = sandbox_fs_exists,
145 		.size = sandbox_fs_size,
146 		.read = fs_read_sandbox,
147 		.write = fs_write_sandbox,
148 		.uuid = fs_uuid_unsupported,
149 	},
150 #endif
151 #ifdef CONFIG_CMD_UBIFS
152 	{
153 		.fstype = FS_TYPE_UBIFS,
154 		.name = "ubifs",
155 		.null_dev_desc_ok = true,
156 		.probe = ubifs_set_blk_dev,
157 		.close = ubifs_close,
158 		.ls = ubifs_ls,
159 		.exists = ubifs_exists,
160 		.size = ubifs_size,
161 		.read = ubifs_read,
162 		.write = fs_write_unsupported,
163 		.uuid = fs_uuid_unsupported,
164 	},
165 #endif
166 	{
167 		.fstype = FS_TYPE_ANY,
168 		.name = "unsupported",
169 		.null_dev_desc_ok = true,
170 		.probe = fs_probe_unsupported,
171 		.close = fs_close_unsupported,
172 		.ls = fs_ls_unsupported,
173 		.exists = fs_exists_unsupported,
174 		.size = fs_size_unsupported,
175 		.read = fs_read_unsupported,
176 		.write = fs_write_unsupported,
177 		.uuid = fs_uuid_unsupported,
178 	},
179 };
180 
181 static struct fstype_info *fs_get_info(int fstype)
182 {
183 	struct fstype_info *info;
184 	int i;
185 
186 	for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes) - 1; i++, info++) {
187 		if (fstype == info->fstype)
188 			return info;
189 	}
190 
191 	/* Return the 'unsupported' sentinel */
192 	return info;
193 }
194 
195 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
196 {
197 	struct fstype_info *info;
198 	int part, i;
199 #ifdef CONFIG_NEEDS_MANUAL_RELOC
200 	static int relocated;
201 
202 	if (!relocated) {
203 		for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes);
204 				i++, info++) {
205 			info->name += gd->reloc_off;
206 			info->probe += gd->reloc_off;
207 			info->close += gd->reloc_off;
208 			info->ls += gd->reloc_off;
209 			info->read += gd->reloc_off;
210 			info->write += gd->reloc_off;
211 		}
212 		relocated = 1;
213 	}
214 #endif
215 
216 	part = blk_get_device_part_str(ifname, dev_part_str, &fs_dev_desc,
217 					&fs_partition, 1);
218 	if (part < 0)
219 		return -1;
220 
221 	for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
222 		if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY &&
223 				fstype != info->fstype)
224 			continue;
225 
226 		if (!fs_dev_desc && !info->null_dev_desc_ok)
227 			continue;
228 
229 		if (!info->probe(fs_dev_desc, &fs_partition)) {
230 			fs_type = info->fstype;
231 			return 0;
232 		}
233 	}
234 
235 	return -1;
236 }
237 
238 static void fs_close(void)
239 {
240 	struct fstype_info *info = fs_get_info(fs_type);
241 
242 	info->close();
243 
244 	fs_type = FS_TYPE_ANY;
245 }
246 
247 int fs_uuid(char *uuid_str)
248 {
249 	struct fstype_info *info = fs_get_info(fs_type);
250 
251 	return info->uuid(uuid_str);
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_type = FS_TYPE_ANY;
263 	fs_close();
264 
265 	return ret;
266 }
267 
268 int fs_exists(const char *filename)
269 {
270 	int ret;
271 
272 	struct fstype_info *info = fs_get_info(fs_type);
273 
274 	ret = info->exists(filename);
275 
276 	fs_close();
277 
278 	return ret;
279 }
280 
281 int fs_size(const char *filename, loff_t *size)
282 {
283 	int ret;
284 
285 	struct fstype_info *info = fs_get_info(fs_type);
286 
287 	ret = info->size(filename, size);
288 
289 	fs_close();
290 
291 	return ret;
292 }
293 
294 int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
295 	    loff_t *actread)
296 {
297 	struct fstype_info *info = fs_get_info(fs_type);
298 	void *buf;
299 	int ret;
300 
301 	/*
302 	 * We don't actually know how many bytes are being read, since len==0
303 	 * means read the whole file.
304 	 */
305 	buf = map_sysmem(addr, len);
306 	ret = info->read(filename, buf, offset, len, actread);
307 	unmap_sysmem(buf);
308 
309 	/* If we requested a specific number of bytes, check we got it */
310 	if (ret == 0 && len && *actread != len)
311 		printf("** %s shorter than offset + len **\n", filename);
312 	fs_close();
313 
314 	return ret;
315 }
316 
317 int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len,
318 	     loff_t *actwrite)
319 {
320 	struct fstype_info *info = fs_get_info(fs_type);
321 	void *buf;
322 	int ret;
323 
324 	buf = map_sysmem(addr, len);
325 	ret = info->write(filename, buf, offset, len, actwrite);
326 	unmap_sysmem(buf);
327 
328 	if (ret < 0 && len != *actwrite) {
329 		printf("** Unable to write file %s **\n", filename);
330 		ret = -1;
331 	}
332 	fs_close();
333 
334 	return ret;
335 }
336 
337 int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
338 		int fstype)
339 {
340 	loff_t size;
341 
342 	if (argc != 4)
343 		return CMD_RET_USAGE;
344 
345 	if (fs_set_blk_dev(argv[1], argv[2], fstype))
346 		return 1;
347 
348 	if (fs_size(argv[3], &size) < 0)
349 		return CMD_RET_FAILURE;
350 
351 	setenv_hex("filesize", size);
352 
353 	return 0;
354 }
355 
356 int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
357 		int fstype)
358 {
359 	unsigned long addr;
360 	const char *addr_str;
361 	const char *filename;
362 	loff_t bytes;
363 	loff_t pos;
364 	loff_t len_read;
365 	int ret;
366 	unsigned long time;
367 	char *ep;
368 
369 	if (argc < 2)
370 		return CMD_RET_USAGE;
371 	if (argc > 7)
372 		return CMD_RET_USAGE;
373 
374 	if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
375 		return 1;
376 
377 	if (argc >= 4) {
378 		addr = simple_strtoul(argv[3], &ep, 16);
379 		if (ep == argv[3] || *ep != '\0')
380 			return CMD_RET_USAGE;
381 	} else {
382 		addr_str = getenv("loadaddr");
383 		if (addr_str != NULL)
384 			addr = simple_strtoul(addr_str, NULL, 16);
385 		else
386 			addr = CONFIG_SYS_LOAD_ADDR;
387 	}
388 	if (argc >= 5) {
389 		filename = argv[4];
390 	} else {
391 		filename = getenv("bootfile");
392 		if (!filename) {
393 			puts("** No boot file defined **\n");
394 			return 1;
395 		}
396 	}
397 	if (argc >= 6)
398 		bytes = simple_strtoul(argv[5], NULL, 16);
399 	else
400 		bytes = 0;
401 	if (argc >= 7)
402 		pos = simple_strtoul(argv[6], NULL, 16);
403 	else
404 		pos = 0;
405 
406 	time = get_timer(0);
407 	ret = fs_read(filename, addr, pos, bytes, &len_read);
408 	time = get_timer(time);
409 	if (ret < 0)
410 		return 1;
411 
412 	printf("%llu bytes read in %lu ms", len_read, time);
413 	if (time > 0) {
414 		puts(" (");
415 		print_size(div_u64(len_read, time) * 1000, "/s");
416 		puts(")");
417 	}
418 	puts("\n");
419 
420 	setenv_hex("fileaddr", addr);
421 	setenv_hex("filesize", len_read);
422 
423 	return 0;
424 }
425 
426 int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
427 	int fstype)
428 {
429 	if (argc < 2)
430 		return CMD_RET_USAGE;
431 	if (argc > 4)
432 		return CMD_RET_USAGE;
433 
434 	if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
435 		return 1;
436 
437 	if (fs_ls(argc >= 4 ? argv[3] : "/"))
438 		return 1;
439 
440 	return 0;
441 }
442 
443 int file_exists(const char *dev_type, const char *dev_part, const char *file,
444 		int fstype)
445 {
446 	if (fs_set_blk_dev(dev_type, dev_part, fstype))
447 		return 0;
448 
449 	return fs_exists(file);
450 }
451 
452 int do_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
453 		int fstype)
454 {
455 	unsigned long addr;
456 	const char *filename;
457 	loff_t bytes;
458 	loff_t pos;
459 	loff_t len;
460 	int ret;
461 	unsigned long time;
462 
463 	if (argc < 6 || argc > 7)
464 		return CMD_RET_USAGE;
465 
466 	if (fs_set_blk_dev(argv[1], argv[2], fstype))
467 		return 1;
468 
469 	addr = simple_strtoul(argv[3], NULL, 16);
470 	filename = argv[4];
471 	bytes = simple_strtoul(argv[5], NULL, 16);
472 	if (argc >= 7)
473 		pos = simple_strtoul(argv[6], NULL, 16);
474 	else
475 		pos = 0;
476 
477 	time = get_timer(0);
478 	ret = fs_write(filename, addr, pos, bytes, &len);
479 	time = get_timer(time);
480 	if (ret < 0)
481 		return 1;
482 
483 	printf("%llu bytes written in %lu ms", len, time);
484 	if (time > 0) {
485 		puts(" (");
486 		print_size(div_u64(len, time) * 1000, "/s");
487 		puts(")");
488 	}
489 	puts("\n");
490 
491 	return 0;
492 }
493 
494 int do_fs_uuid(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
495 		int fstype)
496 {
497 	int ret;
498 	char uuid[37];
499 	memset(uuid, 0, sizeof(uuid));
500 
501 	if (argc < 3 || argc > 4)
502 		return CMD_RET_USAGE;
503 
504 	if (fs_set_blk_dev(argv[1], argv[2], fstype))
505 		return 1;
506 
507 	ret = fs_uuid(uuid);
508 	if (ret)
509 		return CMD_RET_FAILURE;
510 
511 	if (argc == 4)
512 		setenv(argv[3], uuid);
513 	else
514 		printf("%s\n", uuid);
515 
516 	return CMD_RET_SUCCESS;
517 }
518 
519 int do_fs_type(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
520 {
521 	struct fstype_info *info;
522 
523 	if (argc < 3 || argc > 4)
524 		return CMD_RET_USAGE;
525 
526 	if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY))
527 		return 1;
528 
529 	info = fs_get_info(fs_type);
530 
531 	if (argc == 4)
532 		setenv(argv[3], info->name);
533 	else
534 		printf("%s\n", info->name);
535 
536 	return CMD_RET_SUCCESS;
537 }
538 
539