fs.c 8.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * 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>
#include <common.h>
#include <part.h>
#include <ext4fs.h>
#include <fat.h>
#include <fs.h>
S
Simon Glass 已提交
23
#include <sandboxfs.h>
S
Simon Glass 已提交
24
#include <asm/io.h>
25

26 27
DECLARE_GLOBAL_DATA_PTR;

28 29 30 31
static block_dev_desc_t *fs_dev_desc;
static disk_partition_t fs_partition;
static int fs_type = FS_TYPE_ANY;

32 33
static inline int fs_probe_unsupported(block_dev_desc_t *fs_dev_desc,
				      disk_partition_t *fs_partition)
34 35 36 37 38
{
	printf("** Unrecognized filesystem type **\n");
	return -1;
}

39 40 41 42 43
static inline int fs_ls_unsupported(const char *dirname)
{
	return -1;
}

44 45 46 47 48
static inline int fs_exists_unsupported(const char *filename)
{
	return 0;
}

S
Simon Glass 已提交
49
static inline int fs_read_unsupported(const char *filename, void *buf,
50 51 52 53 54
				      int offset, int len)
{
	return -1;
}

55 56 57 58 59 60
static inline int fs_write_unsupported(const char *filename, void *buf,
				      int offset, int len)
{
	return -1;
}

61 62 63 64 65
static inline void fs_close_unsupported(void)
{
}

struct fstype_info {
66
	int fstype;
67 68 69 70 71 72 73 74 75
	/*
	 * 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;
76 77
	int (*probe)(block_dev_desc_t *fs_dev_desc,
		     disk_partition_t *fs_partition);
78
	int (*ls)(const char *dirname);
79
	int (*exists)(const char *filename);
S
Simon Glass 已提交
80
	int (*read)(const char *filename, void *buf, int offset, int len);
81
	int (*write)(const char *filename, void *buf, int offset, int len);
82 83 84 85 86
	void (*close)(void);
};

static struct fstype_info fstypes[] = {
#ifdef CONFIG_FS_FAT
87 88
	{
		.fstype = FS_TYPE_FAT,
89
		.null_dev_desc_ok = false,
90 91
		.probe = fat_set_blk_dev,
		.close = fat_close,
92
		.ls = file_fat_ls,
93
		.exists = fs_exists_unsupported,
94
		.read = fat_read_file,
95
		.write = fs_write_unsupported,
96
	},
97 98
#endif
#ifdef CONFIG_FS_EXT4
99 100
	{
		.fstype = FS_TYPE_EXT,
101
		.null_dev_desc_ok = false,
102 103
		.probe = ext4fs_probe,
		.close = ext4fs_close,
104
		.ls = ext4fs_ls,
105
		.exists = fs_exists_unsupported,
106
		.read = ext4_read_file,
107
		.write = fs_write_unsupported,
108
	},
S
Simon Glass 已提交
109 110 111 112
#endif
#ifdef CONFIG_SANDBOX
	{
		.fstype = FS_TYPE_SANDBOX,
113
		.null_dev_desc_ok = true,
S
Simon Glass 已提交
114 115 116
		.probe = sandbox_fs_set_blk_dev,
		.close = sandbox_fs_close,
		.ls = sandbox_fs_ls,
117
		.exists = fs_exists_unsupported,
S
Simon Glass 已提交
118
		.read = fs_read_sandbox,
119
		.write = fs_write_sandbox,
S
Simon Glass 已提交
120
	},
121 122 123
#endif
	{
		.fstype = FS_TYPE_ANY,
124
		.null_dev_desc_ok = true,
125 126 127
		.probe = fs_probe_unsupported,
		.close = fs_close_unsupported,
		.ls = fs_ls_unsupported,
128
		.exists = fs_exists_unsupported,
129
		.read = fs_read_unsupported,
130
		.write = fs_write_unsupported,
131 132 133
	},
};

134 135 136 137 138 139 140 141 142 143 144 145 146 147
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;
}

148 149
int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
{
150
	struct fstype_info *info;
151
	int part, i;
152 153 154 155
#ifdef CONFIG_NEEDS_MANUAL_RELOC
	static int relocated;

	if (!relocated) {
156 157 158 159 160 161
		for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes);
				i++, info++) {
			info->probe += gd->reloc_off;
			info->close += gd->reloc_off;
			info->ls += gd->reloc_off;
			info->read += gd->reloc_off;
162
			info->write += gd->reloc_off;
163
		}
164 165 166
		relocated = 1;
	}
#endif
167 168 169 170 171 172

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

173 174 175
	for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
		if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY &&
				fstype != info->fstype)
176 177
			continue;

178 179 180
		if (!fs_dev_desc && !info->null_dev_desc_ok)
			continue;

181
		if (!info->probe(fs_dev_desc, &fs_partition)) {
182
			fs_type = info->fstype;
183 184 185 186 187 188 189 190 191
			return 0;
		}
	}

	return -1;
}

static void fs_close(void)
{
192
	struct fstype_info *info = fs_get_info(fs_type);
193

194
	info->close();
195

196 197 198 199 200 201 202
	fs_type = FS_TYPE_ANY;
}

int fs_ls(const char *dirname)
{
	int ret;

203 204 205
	struct fstype_info *info = fs_get_info(fs_type);

	ret = info->ls(dirname);
206

207
	fs_type = FS_TYPE_ANY;
208 209 210 211 212
	fs_close();

	return ret;
}

213 214 215 216 217 218 219 220 221 222 223 224 225
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;
}

226 227
int fs_read(const char *filename, ulong addr, int offset, int len)
{
228
	struct fstype_info *info = fs_get_info(fs_type);
S
Simon Glass 已提交
229
	void *buf;
230 231
	int ret;

S
Simon Glass 已提交
232 233 234 235 236 237 238
	/*
	 * We don't actually know how many bytes are being read, since len==0
	 * means read the whole file.
	 */
	buf = map_sysmem(addr, len);
	ret = info->read(filename, buf, offset, len);
	unmap_sysmem(buf);
239

240 241 242 243 244
	/* If we requested a specific number of bytes, check we got it */
	if (ret >= 0 && len && ret != len) {
		printf("** Unable to read file %s **\n", filename);
		ret = -1;
	}
245 246 247 248 249
	fs_close();

	return ret;
}

250 251 252 253 254 255 256 257 258 259
int fs_write(const char *filename, ulong addr, int offset, int len)
{
	struct fstype_info *info = fs_get_info(fs_type);
	void *buf;
	int ret;

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

260
	if (ret >= 0 && ret != len) {
261 262 263 264 265 266 267 268
		printf("** Unable to write file %s **\n", filename);
		ret = -1;
	}
	fs_close();

	return ret;
}

S
Stephen Warren 已提交
269
int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
270
		int fstype)
271 272 273 274 275 276 277
{
	unsigned long addr;
	const char *addr_str;
	const char *filename;
	unsigned long bytes;
	unsigned long pos;
	int len_read;
278
	unsigned long time;
279

280 281 282
	if (argc < 2)
		return CMD_RET_USAGE;
	if (argc > 7)
283 284
		return CMD_RET_USAGE;

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

	if (argc >= 4) {
289
		addr = simple_strtoul(argv[3], NULL, 16);
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
	} 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)
307
		bytes = simple_strtoul(argv[5], NULL, 16);
308 309 310
	else
		bytes = 0;
	if (argc >= 7)
311
		pos = simple_strtoul(argv[6], NULL, 16);
312 313 314
	else
		pos = 0;

315
	time = get_timer(0);
316
	len_read = fs_read(filename, addr, pos, bytes);
317
	time = get_timer(time);
318 319 320
	if (len_read <= 0)
		return 1;

321 322 323 324 325 326 327
	printf("%d bytes read in %lu ms", len_read, time);
	if (time > 0) {
		puts(" (");
		print_size(len_read / time * 1000, "/s");
		puts(")");
	}
	puts("\n");
328

329
	setenv_hex("filesize", len_read);
330 331 332 333 334 335 336 337 338

	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;
339 340
	if (argc > 4)
		return CMD_RET_USAGE;
341 342 343 344

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

345
	if (fs_ls(argc >= 4 ? argv[3] : "/"))
346 347 348 349
		return 1;

	return 0;
}
350

351 352 353 354 355 356 357 358 359
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);
}

360
int do_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
361
		int fstype)
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
{
	unsigned long addr;
	const char *filename;
	unsigned long bytes;
	unsigned long pos;
	int len;
	unsigned long time;

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

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

	filename = argv[3];
377 378
	addr = simple_strtoul(argv[4], NULL, 16);
	bytes = simple_strtoul(argv[5], NULL, 16);
379
	if (argc >= 7)
380
		pos = simple_strtoul(argv[6], NULL, 16);
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
	else
		pos = 0;

	time = get_timer(0);
	len = fs_write(filename, addr, pos, bytes);
	time = get_timer(time);
	if (len <= 0)
		return 1;

	printf("%d bytes written in %lu ms", len, time);
	if (time > 0) {
		puts(" (");
		print_size(len / time * 1000, "/s");
		puts(")");
	}
	puts("\n");

	return 0;
}