diff options
Diffstat (limited to 'fs/fs.c')
-rw-r--r-- | fs/fs.c | 323 |
1 files changed, 323 insertions, 0 deletions
@@ -0,0 +1,323 @@ +/* + * 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> + +DECLARE_GLOBAL_DATA_PTR; + +static block_dev_desc_t *fs_dev_desc; +static disk_partition_t fs_partition; +static int fs_type = FS_TYPE_ANY; + +static inline int fs_ls_unsupported(const char *dirname) +{ + printf("** Unrecognized filesystem type **\n"); + return -1; +} + +static inline int fs_read_unsupported(const char *filename, ulong addr, + int offset, int len) +{ + printf("** Unrecognized filesystem type **\n"); + return -1; +} + +#ifdef CONFIG_FS_FAT +static int fs_probe_fat(void) +{ + return fat_set_blk_dev(fs_dev_desc, &fs_partition); +} + +static void fs_close_fat(void) +{ +} + +#define fs_ls_fat file_fat_ls + +static int fs_read_fat(const char *filename, ulong addr, int offset, int len) +{ + int len_read; + + len_read = file_fat_read_at(filename, offset, + (unsigned char *)addr, len); + if (len_read == -1) { + printf("** Unable to read file %s **\n", filename); + return -1; + } + + return len_read; +} +#else +static inline int fs_probe_fat(void) +{ + return -1; +} + +static inline void fs_close_fat(void) +{ +} + +#define fs_ls_fat fs_ls_unsupported +#define fs_read_fat fs_read_unsupported +#endif + +#ifdef CONFIG_FS_EXT4 +static int fs_probe_ext(void) +{ + ext4fs_set_blk_dev(fs_dev_desc, &fs_partition); + + if (!ext4fs_mount(fs_partition.size)) { + ext4fs_close(); + return -1; + } + + return 0; +} + +static void fs_close_ext(void) +{ + ext4fs_close(); +} + +#define fs_ls_ext ext4fs_ls + +static int fs_read_ext(const char *filename, ulong addr, int offset, int len) +{ + int file_len; + int len_read; + + if (offset != 0) { + printf("** Cannot support non-zero offset **\n"); + return -1; + } + + file_len = ext4fs_open(filename); + if (file_len < 0) { + printf("** File not found %s **\n", filename); + ext4fs_close(); + return -1; + } + + if (len == 0) + len = file_len; + + len_read = ext4fs_read((char *)addr, len); + ext4fs_close(); + + if (len_read != len) { + printf("** Unable to read file %s **\n", filename); + return -1; + } + + return len_read; +} +#else +static inline int fs_probe_ext(void) +{ + return -1; +} + +static inline void fs_close_ext(void) +{ +} + +#define fs_ls_ext fs_ls_unsupported +#define fs_read_ext fs_read_unsupported +#endif + +static struct { + int fstype; + int (*probe)(void); +} fstypes[] = { + { + .fstype = FS_TYPE_FAT, + .probe = fs_probe_fat, + }, + { + .fstype = FS_TYPE_EXT, + .probe = fs_probe_ext, + }, +}; + +int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype) +{ + int part, i; +#ifdef CONFIG_NEEDS_MANUAL_RELOC + static int relocated; + + if (!relocated) { + for (i = 0; i < ARRAY_SIZE(fstypes); i++) + fstypes[i].probe += gd->reloc_off; + relocated = 1; + } +#endif + + part = get_device_and_partition(ifname, dev_part_str, &fs_dev_desc, + &fs_partition, 1); + if (part < 0) + return -1; + + for (i = 0; i < ARRAY_SIZE(fstypes); i++) { + if ((fstype != FS_TYPE_ANY) && (fstype != fstypes[i].fstype)) + continue; + + if (!fstypes[i].probe()) { + fs_type = fstypes[i].fstype; + return 0; + } + } + + printf("** Unrecognized filesystem type **\n"); + return -1; +} + +static void fs_close(void) +{ + switch (fs_type) { + case FS_TYPE_FAT: + fs_close_fat(); + break; + case FS_TYPE_EXT: + fs_close_ext(); + break; + default: + break; + } + + fs_type = FS_TYPE_ANY; +} + +int fs_ls(const char *dirname) +{ + int ret; + + switch (fs_type) { + case FS_TYPE_FAT: + ret = fs_ls_fat(dirname); + break; + case FS_TYPE_EXT: + ret = fs_ls_ext(dirname); + break; + default: + ret = fs_ls_unsupported(dirname); + break; + } + + fs_close(); + + return ret; +} + +int fs_read(const char *filename, ulong addr, int offset, int len) +{ + int ret; + + switch (fs_type) { + case FS_TYPE_FAT: + ret = fs_read_fat(filename, addr, offset, len); + break; + case FS_TYPE_EXT: + ret = fs_read_ext(filename, addr, offset, len); + break; + default: + ret = fs_read_unsupported(filename, addr, offset, len); + break; + } + + fs_close(); + + return ret; +} + +int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + int fstype, int cmdline_base) +{ + unsigned long addr; + const char *addr_str; + const char *filename; + unsigned long bytes; + unsigned long pos; + int len_read; + char buf[12]; + + if (argc < 2) + return CMD_RET_USAGE; + if (argc > 7) + return CMD_RET_USAGE; + + if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) + return 1; + + if (argc >= 4) { + addr = simple_strtoul(argv[3], NULL, cmdline_base); + } 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) + bytes = simple_strtoul(argv[5], NULL, cmdline_base); + else + bytes = 0; + if (argc >= 7) + pos = simple_strtoul(argv[6], NULL, cmdline_base); + else + pos = 0; + + len_read = fs_read(filename, addr, pos, bytes); + if (len_read <= 0) + return 1; + + printf("%d bytes read\n", len_read); + + sprintf(buf, "0x%x", len_read); + setenv("filesize", buf); + + 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; + if (argc > 4) + return CMD_RET_USAGE; + + if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) + return 1; + + if (fs_ls(argc >= 4 ? argv[3] : "/")) + return 1; + + return 0; +} |