fs.c 10.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <config.h>
18
#include <errno.h>
19
#include <common.h>
20
#include <mapmem.h>
21 22 23 24
#include <part.h>
#include <ext4fs.h>
#include <fat.h>
#include <fs.h>
25
#include <sandboxfs.h>
26
#include <asm/io.h>
27 28
#include <div64.h>
#include <linux/math64.h>
29

30 31
DECLARE_GLOBAL_DATA_PTR;

32 33 34 35
static block_dev_desc_t *fs_dev_desc;
static disk_partition_t fs_partition;
static int fs_type = FS_TYPE_ANY;

36 37
static inline int fs_probe_unsupported(block_dev_desc_t *fs_dev_desc,
				      disk_partition_t *fs_partition)
38 39 40 41 42
{
	printf("** Unrecognized filesystem type **\n");
	return -1;
}

43 44 45 46 47
static inline int fs_ls_unsupported(const char *dirname)
{
	return -1;
}

48 49 50 51 52
static inline int fs_exists_unsupported(const char *filename)
{
	return 0;
}

53
static inline int fs_size_unsupported(const char *filename, loff_t *size)
54 55 56 57
{
	return -1;
}

58
static inline int fs_read_unsupported(const char *filename, void *buf,
59 60
				      loff_t offset, loff_t len,
				      loff_t *actread)
61 62 63 64
{
	return -1;
}

65
static inline int fs_write_unsupported(const char *filename, void *buf,
66 67
				      loff_t offset, loff_t len,
				      loff_t *actwrite)
68 69 70 71
{
	return -1;
}

72 73 74 75
static inline void fs_close_unsupported(void)
{
}

76 77 78 79 80
static inline int fs_uuid_unsupported(char *uuid_str)
{
	return -1;
}

81
struct fstype_info {
82
	int fstype;
83
	char *name;
84 85 86 87 88 89 90 91 92
	/*
	 * Is it legal to pass NULL as .probe()'s  fs_dev_desc parameter? This
	 * should be false in most cases. For "virtual" filesystems which
	 * aren't based on a U-Boot block device (e.g. sandbox), this can be
	 * set to true. This should also be true for the dumm entry at the end
	 * of fstypes[], since that is essentially a "virtual" (non-existent)
	 * filesystem.
	 */
	bool null_dev_desc_ok;
93 94
	int (*probe)(block_dev_desc_t *fs_dev_desc,
		     disk_partition_t *fs_partition);
95
	int (*ls)(const char *dirname);
96
	int (*exists)(const char *filename);
97 98 99 100 101
	int (*size)(const char *filename, loff_t *size);
	int (*read)(const char *filename, void *buf, loff_t offset,
		    loff_t len, loff_t *actread);
	int (*write)(const char *filename, void *buf, loff_t offset,
		     loff_t len, loff_t *actwrite);
102
	void (*close)(void);
103
	int (*uuid)(char *uuid_str);
104 105 106 107
};

static struct fstype_info fstypes[] = {
#ifdef CONFIG_FS_FAT
108 109
	{
		.fstype = FS_TYPE_FAT,
110
		.name = "fat",
111
		.null_dev_desc_ok = false,
112 113
		.probe = fat_set_blk_dev,
		.close = fat_close,
114
		.ls = file_fat_ls,
115
		.exists = fat_exists,
116
		.size = fat_size,
117
		.read = fat_read_file,
118 119 120
#ifdef CONFIG_FAT_WRITE
		.write = file_fat_write,
#else
121
		.write = fs_write_unsupported,
122
#endif
123
		.uuid = fs_uuid_unsupported,
124
	},
125 126
#endif
#ifdef CONFIG_FS_EXT4
127 128
	{
		.fstype = FS_TYPE_EXT,
129
		.name = "ext4",
130
		.null_dev_desc_ok = false,
131 132
		.probe = ext4fs_probe,
		.close = ext4fs_close,
133
		.ls = ext4fs_ls,
134
		.exists = ext4fs_exists,
135
		.size = ext4fs_size,
136
		.read = ext4_read_file,
137 138 139
#ifdef CONFIG_CMD_EXT4_WRITE
		.write = ext4_write_file,
#else
140
		.write = fs_write_unsupported,
141
#endif
142
		.uuid = ext4fs_uuid,
143
	},
144 145 146 147
#endif
#ifdef CONFIG_SANDBOX
	{
		.fstype = FS_TYPE_SANDBOX,
148
		.name = "sandbox",
149
		.null_dev_desc_ok = true,
150 151 152
		.probe = sandbox_fs_set_blk_dev,
		.close = sandbox_fs_close,
		.ls = sandbox_fs_ls,
153
		.exists = sandbox_fs_exists,
154
		.size = sandbox_fs_size,
155
		.read = fs_read_sandbox,
156
		.write = fs_write_sandbox,
157
		.uuid = fs_uuid_unsupported,
158
	},
159 160 161
#endif
	{
		.fstype = FS_TYPE_ANY,
162
		.name = "unsupported",
163
		.null_dev_desc_ok = true,
164 165 166
		.probe = fs_probe_unsupported,
		.close = fs_close_unsupported,
		.ls = fs_ls_unsupported,
167
		.exists = fs_exists_unsupported,
168
		.size = fs_size_unsupported,
169
		.read = fs_read_unsupported,
170
		.write = fs_write_unsupported,
171
		.uuid = fs_uuid_unsupported,
172 173 174
	},
};

175 176 177 178 179 180 181 182 183 184 185 186 187 188
static struct fstype_info *fs_get_info(int fstype)
{
	struct fstype_info *info;
	int i;

	for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes) - 1; i++, info++) {
		if (fstype == info->fstype)
			return info;
	}

	/* Return the 'unsupported' sentinel */
	return info;
}

189 190
int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
{
191
	struct fstype_info *info;
192
	int part, i;
193 194 195 196
#ifdef CONFIG_NEEDS_MANUAL_RELOC
	static int relocated;

	if (!relocated) {
197 198
		for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes);
				i++, info++) {
199
			info->name += gd->reloc_off;
200 201 202 203
			info->probe += gd->reloc_off;
			info->close += gd->reloc_off;
			info->ls += gd->reloc_off;
			info->read += gd->reloc_off;
204
			info->write += gd->reloc_off;
205
		}
206 207 208
		relocated = 1;
	}
#endif
209 210 211 212 213 214

	part = get_device_and_partition(ifname, dev_part_str, &fs_dev_desc,
					&fs_partition, 1);
	if (part < 0)
		return -1;

215 216 217
	for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
		if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY &&
				fstype != info->fstype)
218 219
			continue;

220 221 222
		if (!fs_dev_desc && !info->null_dev_desc_ok)
			continue;

223
		if (!info->probe(fs_dev_desc, &fs_partition)) {
224
			fs_type = info->fstype;
225 226 227 228 229 230 231 232 233
			return 0;
		}
	}

	return -1;
}

static void fs_close(void)
{
234
	struct fstype_info *info = fs_get_info(fs_type);
235

236
	info->close();
237

238 239 240
	fs_type = FS_TYPE_ANY;
}

241 242 243 244 245 246 247
int fs_uuid(char *uuid_str)
{
	struct fstype_info *info = fs_get_info(fs_type);

	return info->uuid(uuid_str);
}

248 249 250 251
int fs_ls(const char *dirname)
{
	int ret;

252 253 254
	struct fstype_info *info = fs_get_info(fs_type);

	ret = info->ls(dirname);
255

256
	fs_type = FS_TYPE_ANY;
257 258 259 260 261
	fs_close();

	return ret;
}

262 263 264 265 266 267 268 269 270 271 272 273 274
int fs_exists(const char *filename)
{
	int ret;

	struct fstype_info *info = fs_get_info(fs_type);

	ret = info->exists(filename);

	fs_close();

	return ret;
}

275
int fs_size(const char *filename, loff_t *size)
276 277 278 279 280
{
	int ret;

	struct fstype_info *info = fs_get_info(fs_type);

281
	ret = info->size(filename, size);
282 283 284 285 286 287

	fs_close();

	return ret;
}

288 289
int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
	    loff_t *actread)
290
{
291
	struct fstype_info *info = fs_get_info(fs_type);
292
	void *buf;
293 294
	int ret;

295 296 297 298 299
	/*
	 * We don't actually know how many bytes are being read, since len==0
	 * means read the whole file.
	 */
	buf = map_sysmem(addr, len);
300
	ret = info->read(filename, buf, offset, len, actread);
301
	unmap_sysmem(buf);
302

303
	/* If we requested a specific number of bytes, check we got it */
304 305
	if (ret == 0 && len && *actread != len)
		printf("** %s shorter than offset + len **\n", filename);
306 307 308 309 310
	fs_close();

	return ret;
}

311 312
int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len,
	     loff_t *actwrite)
313 314 315 316 317 318
{
	struct fstype_info *info = fs_get_info(fs_type);
	void *buf;
	int ret;

	buf = map_sysmem(addr, len);
319
	ret = info->write(filename, buf, offset, len, actwrite);
320 321
	unmap_sysmem(buf);

322
	if (ret < 0 && len != *actwrite) {
323 324 325 326 327 328 329 330
		printf("** Unable to write file %s **\n", filename);
		ret = -1;
	}
	fs_close();

	return ret;
}

331 332 333
int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
		int fstype)
{
334
	loff_t size;
335 336 337 338 339 340 341

	if (argc != 4)
		return CMD_RET_USAGE;

	if (fs_set_blk_dev(argv[1], argv[2], fstype))
		return 1;

342
	if (fs_size(argv[3], &size) < 0)
343 344 345 346 347 348 349
		return CMD_RET_FAILURE;

	setenv_hex("filesize", size);

	return 0;
}

350
int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
351
		int fstype)
352 353 354 355
{
	unsigned long addr;
	const char *addr_str;
	const char *filename;
356 357 358 359
	loff_t bytes;
	loff_t pos;
	loff_t len_read;
	int ret;
360
	unsigned long time;
361
	char *ep;
362

363 364 365
	if (argc < 2)
		return CMD_RET_USAGE;
	if (argc > 7)
366 367
		return CMD_RET_USAGE;

368
	if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
369 370 371
		return 1;

	if (argc >= 4) {
372 373 374
		addr = simple_strtoul(argv[3], &ep, 16);
		if (ep == argv[3] || *ep != '\0')
			return CMD_RET_USAGE;
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
	} else {
		addr_str = getenv("loadaddr");
		if (addr_str != NULL)
			addr = simple_strtoul(addr_str, NULL, 16);
		else
			addr = CONFIG_SYS_LOAD_ADDR;
	}
	if (argc >= 5) {
		filename = argv[4];
	} else {
		filename = getenv("bootfile");
		if (!filename) {
			puts("** No boot file defined **\n");
			return 1;
		}
	}
	if (argc >= 6)
392
		bytes = simple_strtoul(argv[5], NULL, 16);
393 394 395
	else
		bytes = 0;
	if (argc >= 7)
396
		pos = simple_strtoul(argv[6], NULL, 16);
397 398 399
	else
		pos = 0;

400
	time = get_timer(0);
401
	ret = fs_read(filename, addr, pos, bytes, &len_read);
402
	time = get_timer(time);
403
	if (ret < 0)
404 405
		return 1;

406
	printf("%llu bytes read in %lu ms", len_read, time);
407 408
	if (time > 0) {
		puts(" (");
409
		print_size(div_u64(len_read, time) * 1000, "/s");
410 411 412
		puts(")");
	}
	puts("\n");
413

414
	setenv_hex("filesize", len_read);
415 416 417 418 419 420 421 422 423

	return 0;
}

int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
	int fstype)
{
	if (argc < 2)
		return CMD_RET_USAGE;
424 425
	if (argc > 4)
		return CMD_RET_USAGE;
426 427 428 429

	if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
		return 1;

430
	if (fs_ls(argc >= 4 ? argv[3] : "/"))
431 432 433 434
		return 1;

	return 0;
}
435

436 437 438 439 440 441 442 443 444
int file_exists(const char *dev_type, const char *dev_part, const char *file,
		int fstype)
{
	if (fs_set_blk_dev(dev_type, dev_part, fstype))
		return 0;

	return fs_exists(file);
}

445
int do_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
446
		int fstype)
447 448 449
{
	unsigned long addr;
	const char *filename;
450 451 452 453
	loff_t bytes;
	loff_t pos;
	loff_t len;
	int ret;
454 455 456 457 458 459 460 461
	unsigned long time;

	if (argc < 6 || argc > 7)
		return CMD_RET_USAGE;

	if (fs_set_blk_dev(argv[1], argv[2], fstype))
		return 1;

462 463
	addr = simple_strtoul(argv[3], NULL, 16);
	filename = argv[4];
464
	bytes = simple_strtoul(argv[5], NULL, 16);
465
	if (argc >= 7)
466
		pos = simple_strtoul(argv[6], NULL, 16);
467 468 469 470
	else
		pos = 0;

	time = get_timer(0);
471
	ret = fs_write(filename, addr, pos, bytes, &len);
472
	time = get_timer(time);
473
	if (ret < 0)
474 475
		return 1;

476
	printf("%llu bytes written in %lu ms", len, time);
477 478
	if (time > 0) {
		puts(" (");
479
		print_size(div_u64(len, time) * 1000, "/s");
480 481 482 483 484 485
		puts(")");
	}
	puts("\n");

	return 0;
}
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510

int do_fs_uuid(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
		int fstype)
{
	int ret;
	char uuid[37];
	memset(uuid, 0, sizeof(uuid));

	if (argc < 3 || argc > 4)
		return CMD_RET_USAGE;

	if (fs_set_blk_dev(argv[1], argv[2], fstype))
		return 1;

	ret = fs_uuid(uuid);
	if (ret)
		return CMD_RET_FAILURE;

	if (argc == 4)
		setenv(argv[3], uuid);
	else
		printf("%s\n", uuid);

	return CMD_RET_SUCCESS;
}
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531

int do_fs_type(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	struct fstype_info *info;

	if (argc < 3 || argc > 4)
		return CMD_RET_USAGE;

	if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY))
		return 1;

	info = fs_get_info(fs_type);

	if (argc == 4)
		setenv(argv[3], info->name);
	else
		printf("%s\n", info->name);

	return CMD_RET_SUCCESS;
}