diff options
author | H. Nikolaus Schaller <hns@goldelico.com> | 2013-01-10 16:42:11 +0100 |
---|---|---|
committer | H. Nikolaus Schaller <hns@goldelico.com> | 2013-01-10 16:42:11 +0100 |
commit | 1d18c95643416cc9f1577755922e6593b508e8da (patch) | |
tree | 3d30910058cbfbddc388357d29307aaa0a615e90 | |
parent | 2f7f1bd2415b74d98cfb8e0a0cd5e6436261de4a (diff) | |
download | bootable_bootloader_goldelico_gta04-1d18c95643416cc9f1577755922e6593b508e8da.zip bootable_bootloader_goldelico_gta04-1d18c95643416cc9f1577755922e6593b508e8da.tar.gz bootable_bootloader_goldelico_gta04-1d18c95643416cc9f1577755922e6593b508e8da.tar.bz2 |
made ext4 and new general ls/load commands work
-rw-r--r-- | u-boot/Makefile | 3 | ||||
-rw-r--r-- | u-boot/common/Makefile | 2 | ||||
-rwxr-xr-x[-rw-r--r--] | u-boot/common/cmd_fat.c | 193 | ||||
-rwxr-xr-x | u-boot/common/cmd_fs.c | 53 | ||||
-rw-r--r-- | u-boot/common/cmd_jffs2.c | 12 | ||||
-rwxr-xr-x[-rw-r--r--] | u-boot/disk/part.c | 365 | ||||
-rwxr-xr-x[-rw-r--r--] | u-boot/disk/part_dos.c | 80 | ||||
-rwxr-xr-x[-rw-r--r--] | u-boot/disk/part_dos.h | 9 | ||||
-rw-r--r-- | u-boot/fs/Makefile | 27 | ||||
-rwxr-xr-x[-rw-r--r--] | u-boot/fs/fat/fat.c | 531 | ||||
-rwxr-xr-x | u-boot/fs/fat/fat_write.c | 1098 | ||||
-rwxr-xr-x[-rw-r--r--] | u-boot/fs/fat/file.c | 0 | ||||
-rwxr-xr-x | u-boot/fs/fs.c | 334 | ||||
-rw-r--r-- | u-boot/include/common.h | 87 | ||||
-rw-r--r-- | u-boot/include/config_defaults.h | 1 | ||||
-rw-r--r-- | u-boot/include/configs/omap3_beagle.h | 2 | ||||
-rw-r--r-- | u-boot/include/configs/omap3_gta04.h | 14 | ||||
-rwxr-xr-x[-rw-r--r--] | u-boot/include/fat.h | 32 | ||||
-rwxr-xr-x | u-boot/include/fs.h | 65 | ||||
-rwxr-xr-x[-rw-r--r--] | u-boot/include/part.h | 44 |
20 files changed, 2459 insertions, 493 deletions
diff --git a/u-boot/Makefile b/u-boot/Makefile index dc2e3d8..ca4b915 100644 --- a/u-boot/Makefile +++ b/u-boot/Makefile @@ -193,8 +193,9 @@ ifeq ($(CPU),ixp) LIBS += arch/arm/cpu/ixp/npe/libnpe.o endif LIBS += arch/$(ARCH)/lib/lib$(ARCH).o +LIBS += fs/fs.o LIBS += fs/cramfs/libcramfs.o fs/fat/libfat.o fs/fdos/libfdos.o fs/jffs2/libjffs2.o \ - fs/reiserfs/libreiserfs.o fs/ext2/libext2fs.o fs/yaffs2/libyaffs2.o \ + fs/reiserfs/libreiserfs.o fs/ext2/libext2fs.o fs/ext4/libext4fs.o fs/yaffs2/libyaffs2.o \ fs/ubifs/libubifs.o LIBS += net/libnet.o LIBS += disk/libdisk.o diff --git a/u-boot/common/Makefile b/u-boot/common/Makefile index 048df0c..121a47f 100644 --- a/u-boot/common/Makefile +++ b/u-boot/common/Makefile @@ -89,7 +89,9 @@ COBJS-$(CONFIG_ENV_IS_IN_EEPROM) += cmd_eeprom.o COBJS-$(CONFIG_CMD_EEPROM) += cmd_eeprom.o COBJS-$(CONFIG_CMD_ELF) += cmd_elf.o COBJS-$(CONFIG_SYS_HUSH_PARSER) += cmd_exit.o +COBJS-$(CONFIG_CMD_FS) += cmd_fs.o COBJS-$(CONFIG_CMD_EXT2) += cmd_ext2.o +COBJS-$(CONFIG_CMD_EXT4) += cmd_ext4.o COBJS-$(CONFIG_CMD_FAT) += cmd_fat.o COBJS-$(CONFIG_CMD_FDC)$(CONFIG_CMD_FDOS) += cmd_fdc.o COBJS-$(CONFIG_OF_LIBFDT) += cmd_fdt.o fdt_support.o diff --git a/u-boot/common/cmd_fat.c b/u-boot/common/cmd_fat.c index 0220494..86be044 100644..100755 --- a/u-boot/common/cmd_fat.c +++ b/u-boot/common/cmd_fat.c @@ -31,146 +31,57 @@ #include <ata.h> #include <part.h> #include <fat.h> - +#include <fs.h> int do_fat_fsload (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - long size; - unsigned long offset; - unsigned long count; - char buf [12]; - block_dev_desc_t *dev_desc=NULL; - int dev=0; - int part=1; - char *ep; - - if (argc < 5) { - printf( "usage: fatload <interface> <dev[:part]> " - "<addr> <filename> [bytes]\n"); - return 1; - } - - dev = (int)simple_strtoul(argv[2], &ep, 16); - dev_desc = get_dev(argv[1],dev); - if (dev_desc == NULL) { - puts("\n** Invalid boot device **\n"); - return 1; - } - if (*ep) { - if (*ep != ':') { - puts("\n** Invalid boot device, use `dev[:part]' **\n"); - return 1; - } - part = (int)simple_strtoul(++ep, NULL, 16); - } - if (fat_register_device(dev_desc,part)!=0) { - printf("\n** Unable to use %s %d:%d for fatload **\n", - argv[1], dev, part); - return 1; - } - offset = simple_strtoul(argv[3], NULL, 16); - if (argc == 6) - count = simple_strtoul(argv[5], NULL, 16); - else - count = 0; - size = file_fat_read(argv[4], (unsigned char *)offset, count); - - if(size==-1) { - printf("\n** Unable to read \"%s\" from %s %d:%d **\n", - argv[4], argv[1], dev, part); - return 1; - } - - printf("\n%ld bytes read\n", size); - - sprintf(buf, "%lX", size); - setenv("filesize", buf); - - return 0; + return do_load(cmdtp, flag, argc, argv, FS_TYPE_FAT, 16); } U_BOOT_CMD( - fatload, 6, 0, do_fat_fsload, + fatload, 7, 0, do_fat_fsload, "load binary file from a dos filesystem", - "<interface> <dev[:part]> <addr> <filename> [bytes]\n" - " - load binary file 'filename' from 'dev' on 'interface'\n" - " to address 'addr' from dos filesystem" + "<interface> [<dev[:part]>] <addr> <filename> [bytes [pos]]\n" + " - Load binary file 'filename' from 'dev' on 'interface'\n" + " to address 'addr' from dos filesystem.\n" + " 'pos' gives the file position to start loading from.\n" + " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n" + " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n" + " the load stops on end of file.\n" + " All numeric parameters are assumed to be hex." ); -int do_fat_ls (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +static int do_fat_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *filename = "/"; - int ret; - int dev=0; - int part=1; - char *ep; - block_dev_desc_t *dev_desc=NULL; - - if (argc < 3) { - printf("usage: fatls <interface> <dev[:part]> [directory]\n"); - return 0; - } - dev = (int)simple_strtoul(argv[2], &ep, 16); - dev_desc = get_dev(argv[1],dev); - if (dev_desc == NULL) { - puts("\n** Invalid boot device **\n"); - return 1; - } - if (*ep) { - if (*ep != ':') { - puts("\n** Invalid boot device, use `dev[:part]' **\n"); - return 1; - } - part = (int)simple_strtoul(++ep, NULL, 16); - } - if (fat_register_device(dev_desc,part)!=0) { - printf("\n** Unable to use %s %d:%d for fatls **\n", - argv[1], dev, part); - return 1; - } - if (argc == 4) - ret = file_fat_ls(argv[3]); - else - ret = file_fat_ls(filename); - - if(ret!=0) - printf("No Fat FS detected\n"); - return ret; + return do_ls(cmdtp, flag, argc, argv, FS_TYPE_FAT); } U_BOOT_CMD( fatls, 4, 1, do_fat_ls, "list files in a directory (default /)", - "<interface> <dev[:part]> [directory]\n" + "<interface> [<dev[:part]>] [directory]\n" " - list files from 'dev' on 'interface' in a 'directory'" ); -int do_fat_fsinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +static int do_fat_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) { - int dev=0; - int part=1; - char *ep; - block_dev_desc_t *dev_desc=NULL; + int dev, part; + block_dev_desc_t *dev_desc; + disk_partition_t info; if (argc < 2) { - printf("usage: fatinfo <interface> <dev[:part]>\n"); + printf("usage: fatinfo <interface> [<dev[:part]>]\n"); return 0; } - dev = (int)simple_strtoul(argv[2], &ep, 16); - dev_desc = get_dev(argv[1],dev); - if (dev_desc == NULL) { - puts("\n** Invalid boot device **\n"); + + part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) return 1; - } - if (*ep) { - if (*ep != ':') { - puts("\n** Invalid boot device, use `dev[:part]' **\n"); - return 1; - } - part = (int)simple_strtoul(++ep, NULL, 16); - } - if (fat_register_device(dev_desc,part)!=0) { + + dev = dev_desc->dev; + if (fat_set_blk_dev(dev_desc, &info) != 0) { printf("\n** Unable to use %s %d:%d for fatinfo **\n", argv[1], dev, part); return 1; @@ -181,6 +92,56 @@ int do_fat_fsinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) U_BOOT_CMD( fatinfo, 3, 1, do_fat_fsinfo, "print information about filesystem", - "<interface> <dev[:part]>\n" + "<interface> [<dev[:part]>]\n" " - print information about filesystem from 'dev' on 'interface'" ); + +#ifdef CONFIG_FAT_WRITE +static int do_fat_fswrite(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + long size; + unsigned long addr; + unsigned long count; + block_dev_desc_t *dev_desc = NULL; + disk_partition_t info; + int dev = 0; + int part = 1; + + if (argc < 5) + return cmd_usage(cmdtp); + + part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1); + if (part < 0) + return 1; + + dev = dev_desc->dev; + + if (fat_set_blk_dev(dev_desc, &info) != 0) { + printf("\n** Unable to use %s %d:%d for fatwrite **\n", + argv[1], dev, part); + return 1; + } + addr = simple_strtoul(argv[3], NULL, 16); + count = simple_strtoul(argv[5], NULL, 16); + + size = file_fat_write(argv[4], (void *)addr, count); + if (size == -1) { + printf("\n** Unable to write \"%s\" from %s %d:%d **\n", + argv[4], argv[1], dev, part); + return 1; + } + + printf("%ld bytes written\n", size); + + return 0; +} + +U_BOOT_CMD( + fatwrite, 6, 0, do_fat_fswrite, + "write file into a dos filesystem", + "<interface> <dev[:part]> <addr> <filename> <bytes>\n" + " - write file 'filename' from the address 'addr' in RAM\n" + " to 'dev' on 'interface'" +); +#endif diff --git a/u-boot/common/cmd_fs.c b/u-boot/common/cmd_fs.c new file mode 100755 index 0000000..a681d03 --- /dev/null +++ b/u-boot/common/cmd_fs.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * Inspired by cmd_ext_common.c, cmd_fat.c. + * + * 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 <common.h> +#include <command.h> +#include <fs.h> + +int do_load_wrapper(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_load(cmdtp, flag, argc, argv, FS_TYPE_ANY, 0); +} + +U_BOOT_CMD( + load, 7, 0, do_load_wrapper, + "load binary file from a filesystem", + "<interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]\n" + " - Load binary file 'filename' from partition 'part' on device\n" + " type 'interface' instance 'dev' to address 'addr' in memory.\n" + " 'bytes' gives the size to load in bytes.\n" + " If 'bytes' is 0 or omitted, the file is read until the end.\n" + " 'pos' gives the file byte position to start reading from.\n" + " If 'pos' is 0 or omitted, the file is read from the start.\n" + " All numeric parameters are assumed to be decimal,\n" + " unless specified otherwise using a leading \"0x\"." +); + +int do_ls_wrapper(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_ls(cmdtp, flag, argc, argv, FS_TYPE_ANY); +} + +U_BOOT_CMD( + ls, 4, 1, do_ls_wrapper, + "list files in a directory (default /)", + "<interface> [<dev[:part]> [directory]]\n" + " - List files in directory 'directory' of partition 'part' on\n" + " device type 'interface' instance 'dev'." +); diff --git a/u-boot/common/cmd_jffs2.c b/u-boot/common/cmd_jffs2.c index 27296dd..5843de8 100644 --- a/u-boot/common/cmd_jffs2.c +++ b/u-boot/common/cmd_jffs2.c @@ -617,21 +617,21 @@ int do_jffs2_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) /***************************************************/ U_BOOT_CMD( - fsload, 3, 0, do_jffs2_fsload, - "load binary file from a filesystem image", + jcload, 3, 0, do_jffs2_fsload, + "load binary file from a JFFS2/CRAMFS filesystem image", "[ off ] [ filename ]\n" " - load binary file from flash bank\n" " with offset 'off'" ); U_BOOT_CMD( - ls, 2, 1, do_jffs2_ls, - "list files in a directory (default /)", + jcls, 2, 1, do_jffs2_ls, + "list files in a JFFS2/CRAMFS directory (default /)", "[ directory ]" ); U_BOOT_CMD( - fsinfo, 1, 1, do_jffs2_fsinfo, - "print information about filesystems", + jcinfo, 1, 1, do_jffs2_fsinfo, + "print information about JFFS2/CRAMFS filesystems", "" ); /***************************************************/ diff --git a/u-boot/disk/part.c b/u-boot/disk/part.c index 13723f2..4646f68 100644..100755 --- a/u-boot/disk/part.c +++ b/u-boot/disk/part.c @@ -24,6 +24,7 @@ #include <common.h> #include <command.h> #include <ide.h> +#include <malloc.h> #include <part.h> #undef PART_DEBUG @@ -34,13 +35,15 @@ #define PRINTF(fmt,args...) #endif +/* Rather than repeat this expression each time, add a define for it */ #if (defined(CONFIG_CMD_IDE) || \ - defined(CONFIG_CMD_MG_DISK) || \ defined(CONFIG_CMD_SATA) || \ defined(CONFIG_CMD_SCSI) || \ defined(CONFIG_CMD_USB) || \ defined(CONFIG_MMC) || \ defined(CONFIG_SYSTEMACE) ) +#define HAVE_BLOCK_DEVICE +#endif struct block_drvr { char *name; @@ -66,25 +69,26 @@ static const struct block_drvr block_drvr[] = { #if defined(CONFIG_SYSTEMACE) { .name = "ace", .get_dev = systemace_get_dev, }, #endif -#if defined(CONFIG_CMD_MG_DISK) - { .name = "mgd", .get_dev = mg_disk_get_dev, }, -#endif { }, }; DECLARE_GLOBAL_DATA_PTR; -block_dev_desc_t *get_dev(char* ifname, int dev) +#ifdef HAVE_BLOCK_DEVICE +block_dev_desc_t *get_dev(const char *ifname, int dev) { const struct block_drvr *drvr = block_drvr; block_dev_desc_t* (*reloc_get_dev)(int dev); char *name; + if (!ifname) + return NULL; + name = drvr->name; #ifdef CONFIG_NEEDS_MANUAL_RELOC name += gd->reloc_off; #endif - while (name) { + while (drvr->name) { name = drvr->name; reloc_get_dev = drvr->get_dev; #ifdef CONFIG_NEEDS_MANUAL_RELOC @@ -98,19 +102,13 @@ block_dev_desc_t *get_dev(char* ifname, int dev) return NULL; } #else -block_dev_desc_t *get_dev(char* ifname, int dev) +block_dev_desc_t *get_dev(const char *ifname, int dev) { return NULL; } #endif -#if (defined(CONFIG_CMD_IDE) || \ - defined(CONFIG_CMD_MG_DISK) || \ - defined(CONFIG_CMD_SATA) || \ - defined(CONFIG_CMD_SCSI) || \ - defined(CONFIG_CMD_USB) || \ - defined(CONFIG_MMC) || \ - defined(CONFIG_SYSTEMACE) ) +#ifdef HAVE_BLOCK_DEVICE /* ------------------------------------------------------------------------- */ /* @@ -240,19 +238,7 @@ void dev_print (block_dev_desc_t *dev_desc) } #endif -#if (defined(CONFIG_CMD_IDE) || \ - defined(CONFIG_CMD_MG_DISK) || \ - defined(CONFIG_CMD_SATA) || \ - defined(CONFIG_CMD_SCSI) || \ - defined(CONFIG_CMD_USB) || \ - defined(CONFIG_MMC) || \ - defined(CONFIG_SYSTEMACE) ) - -#if defined(CONFIG_MAC_PARTITION) || \ - defined(CONFIG_DOS_PARTITION) || \ - defined(CONFIG_ISO_PARTITION) || \ - defined(CONFIG_AMIGA_PARTITION) || \ - defined(CONFIG_EFI_PARTITION) +#ifdef HAVE_BLOCK_DEVICE void init_part (block_dev_desc_t * dev_desc) { @@ -291,63 +277,15 @@ void init_part (block_dev_desc_t * dev_desc) return; } #endif + dev_desc->part_type = PART_TYPE_UNKNOWN; } -int get_partition_info (block_dev_desc_t *dev_desc, int part - , disk_partition_t *info) -{ - switch (dev_desc->part_type) { -#ifdef CONFIG_MAC_PARTITION - case PART_TYPE_MAC: - if (get_partition_info_mac(dev_desc,part,info) == 0) { - PRINTF ("## Valid MAC partition found ##\n"); - return (0); - } - break; -#endif - -#ifdef CONFIG_DOS_PARTITION - case PART_TYPE_DOS: - if (get_partition_info_dos(dev_desc,part,info) == 0) { - PRINTF ("## Valid DOS partition found ##\n"); - return (0); - } - break; -#endif - -#ifdef CONFIG_ISO_PARTITION - case PART_TYPE_ISO: - if (get_partition_info_iso(dev_desc,part,info) == 0) { - PRINTF ("## Valid ISO boot partition found ##\n"); - return (0); - } - break; -#endif - -#ifdef CONFIG_AMIGA_PARTITION - case PART_TYPE_AMIGA: - if (get_partition_info_amiga(dev_desc, part, info) == 0) - { - PRINTF ("## Valid Amiga partition found ##\n"); - return (0); - } - break; -#endif - -#ifdef CONFIG_EFI_PARTITION - case PART_TYPE_EFI: - if (get_partition_info_efi(dev_desc,part,info) == 0) { - PRINTF ("## Valid EFI partition found ##\n"); - return (0); - } - break; -#endif - default: - break; - } - return (-1); -} +#if defined(CONFIG_MAC_PARTITION) || \ + defined(CONFIG_DOS_PARTITION) || \ + defined(CONFIG_ISO_PARTITION) || \ + defined(CONFIG_AMIGA_PARTITION) || \ + defined(CONFIG_EFI_PARTITION) static void print_part_header (const char *type, block_dev_desc_t * dev_desc) { @@ -382,6 +320,8 @@ static void print_part_header (const char *type, block_dev_desc_t * dev_desc) dev_desc->dev, type); } +#endif /* any CONFIG_..._PARTITION */ + void print_part (block_dev_desc_t * dev_desc) { @@ -428,11 +368,266 @@ void print_part (block_dev_desc_t * dev_desc) puts ("## Unknown partition table\n"); } +#endif /* HAVE_BLOCK_DEVICE */ -#else /* neither MAC nor DOS nor ISO nor AMIGA nor EFI partition configured */ -# error neither CONFIG_MAC_PARTITION nor CONFIG_DOS_PARTITION -# error nor CONFIG_ISO_PARTITION nor CONFIG_AMIGA_PARTITION -# error nor CONFIG_EFI_PARTITION configured! +int get_partition_info(block_dev_desc_t *dev_desc, int part + , disk_partition_t *info) +{ +#ifdef HAVE_BLOCK_DEVICE + +#ifdef CONFIG_PARTITION_UUIDS + /* The common case is no UUID support */ + info->uuid[0] = 0; #endif + switch (dev_desc->part_type) { +#ifdef CONFIG_MAC_PARTITION + case PART_TYPE_MAC: + if (get_partition_info_mac(dev_desc, part, info) == 0) { + PRINTF("## Valid MAC partition found ##\n"); + return 0; + } + break; #endif + +#ifdef CONFIG_DOS_PARTITION + case PART_TYPE_DOS: + if (get_partition_info_dos(dev_desc, part, info) == 0) { + PRINTF("## Valid DOS partition found ##\n"); + return 0; + } + break; +#endif + +#ifdef CONFIG_ISO_PARTITION + case PART_TYPE_ISO: + if (get_partition_info_iso(dev_desc, part, info) == 0) { + PRINTF("## Valid ISO boot partition found ##\n"); + return 0; + } + break; +#endif + +#ifdef CONFIG_AMIGA_PARTITION + case PART_TYPE_AMIGA: + if (get_partition_info_amiga(dev_desc, part, info) == 0) { + PRINTF("## Valid Amiga partition found ##\n"); + return 0; + } + break; +#endif + +#ifdef CONFIG_EFI_PARTITION + case PART_TYPE_EFI: + if (get_partition_info_efi(dev_desc, part, info) == 0) { + PRINTF("## Valid EFI partition found ##\n"); + return 0; + } + break; +#endif + default: + break; + } +#endif /* HAVE_BLOCK_DEVICE */ + + return -1; +} + +int get_device(const char *ifname, const char *dev_str, + block_dev_desc_t **dev_desc) +{ + char *ep; + int dev; + + dev = simple_strtoul(dev_str, &ep, 16); + if (*ep) { + printf("** Bad device specification %s %s **\n", + ifname, dev_str); + return -1; + } + + *dev_desc = get_dev(ifname, dev); + if (!(*dev_desc) || ((*dev_desc)->type == DEV_TYPE_UNKNOWN)) { + printf("** Bad device %s %s **\n", ifname, dev_str); + return -1; + } + + return dev; +} + +#define PART_UNSPECIFIED -2 +#define PART_AUTO -1 +#define MAX_SEARCH_PARTITIONS 16 +int get_device_and_partition(const char *ifname, const char *dev_part_str, + block_dev_desc_t **dev_desc, + disk_partition_t *info, int allow_whole_dev) +{ + int ret = -1; + const char *part_str; + char *dup_str = NULL; + const char *dev_str; + int dev; + char *ep; + int p; + int part; + disk_partition_t tmpinfo; + + /* If no dev_part_str, use bootdevice environment variable */ + if (!dev_part_str || !strlen(dev_part_str) || + !strcmp(dev_part_str, "-")) + dev_part_str = getenv("bootdevice"); + + /* If still no dev_part_str, it's an error */ + if (!dev_part_str) { + printf("** No device specified **\n"); + goto cleanup; + } + + /* Separate device and partition ID specification */ + part_str = strchr(dev_part_str, ':'); + if (part_str) { + dup_str = strdup(dev_part_str); + dup_str[part_str - dev_part_str] = 0; + dev_str = dup_str; + part_str++; + } else { + dev_str = dev_part_str; + } + + /* Look up the device */ + dev = get_device(ifname, dev_str, dev_desc); + if (dev < 0) + goto cleanup; + + /* Convert partition ID string to number */ + if (!part_str || !*part_str) { + part = PART_UNSPECIFIED; + } else if (!strcmp(part_str, "auto")) { + part = PART_AUTO; + } else { + /* Something specified -> use exactly that */ + part = (int)simple_strtoul(part_str, &ep, 16); + /* + * Less than whole string converted, + * or request for whole device, but caller requires partition. + */ + if (*ep || (part == 0 && !allow_whole_dev)) { + printf("** Bad partition specification %s %s **\n", + ifname, dev_part_str); + goto cleanup; + } + } + + /* + * No partition table on device, + * or user requested partition 0 (entire device). + */ + if (((*dev_desc)->part_type == PART_TYPE_UNKNOWN) || + (part == 0)) { + if (!(*dev_desc)->lba) { + printf("** Bad device size - %s %s **\n", ifname, + dev_str); + goto cleanup; + } + + /* + * If user specified a partition ID other than 0, + * or the calling command only accepts partitions, + * it's an error. + */ + if ((part > 0) || (!allow_whole_dev)) { + printf("** No partition table - %s %s **\n", ifname, + dev_str); + goto cleanup; + } + + info->start = 0; + info->size = (*dev_desc)->lba; + info->blksz = (*dev_desc)->blksz; + info->bootable = 0; + strcpy((char *)info->type, BOOT_PART_TYPE); + strcpy((char *)info->name, "Whole Disk"); +#ifdef CONFIG_PARTITION_UUIDS + info->uuid[0] = 0; +#endif + + ret = 0; + goto cleanup; + } + + /* + * Now there's known to be a partition table, + * not specifying a partition means to pick partition 1. + */ + if (part == PART_UNSPECIFIED) + part = 1; + + /* + * If user didn't specify a partition number, or did specify something + * other than "auto", use that partition number directly. + */ + if (part != PART_AUTO) { + ret = get_partition_info(*dev_desc, part, info); + if (ret) { + printf("** Invalid partition %d **\n", part); + goto cleanup; + } + } else { + /* + * Find the first bootable partition. + * If none are bootable, fall back to the first valid partition. + */ + part = 0; + for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { + ret = get_partition_info(*dev_desc, p, info); + if (ret) + continue; + + /* + * First valid partition, or new better partition? + * If so, save partition ID. + */ + if (!part || info->bootable) + part = p; + + /* Best possible partition? Stop searching. */ + if (info->bootable) + break; + + /* + * We now need to search further for best possible. + * If we what we just queried was the best so far, + * save the info since we over-write it next loop. + */ + if (part == p) + tmpinfo = *info; + } + /* If we found any acceptable partition */ + if (part) { + /* + * If we searched all possible partition IDs, + * return the first valid partition we found. + */ + if (p == MAX_SEARCH_PARTITIONS + 1) + *info = tmpinfo; + } else { + printf("** No valid partitions found **\n"); + ret = -1; + goto cleanup; + } + } + if (strncmp((char *)info->type, BOOT_PART_TYPE, sizeof(info->type)) != 0) { + printf("** Invalid partition type \"%.32s\"" + " (expect \"" BOOT_PART_TYPE "\")\n", + info->type); + ret = -1; + goto cleanup; + } + + ret = part; + goto cleanup; + +cleanup: + free(dup_str); + return ret; +} diff --git a/u-boot/disk/part_dos.c b/u-boot/disk/part_dos.c index 2de1bb8..3fe901b 100644..100755 --- a/u-boot/disk/part_dos.c +++ b/u-boot/disk/part_dos.c @@ -36,7 +36,6 @@ #include "part_dos.h" #if defined(CONFIG_CMD_IDE) || \ - defined(CONFIG_CMD_MG_DISK) || \ defined(CONFIG_CMD_SATA) || \ defined(CONFIG_CMD_SCSI) || \ defined(CONFIG_CMD_USB) || \ @@ -61,14 +60,21 @@ static inline int is_extended(int part_type) part_type == 0x85); } -static void print_one_part (dos_partition_t *p, int ext_part_sector, int part_num) +static inline int is_bootable(dos_partition_t *p) +{ + return p->boot_ind == 0x80; +} + +static void print_one_part(dos_partition_t *p, int ext_part_sector, + int part_num, unsigned int disksig) { int lba_start = ext_part_sector + le32_to_int (p->start4); int lba_size = le32_to_int (p->size4); - printf ("%5d\t\t%10d\t%10d\t%2x%s\n", - part_num, lba_start, lba_size, p->sys_ind, - (is_extended (p->sys_ind) ? " Extd" : "")); + printf("%3d\t%-10d\t%-10d\t%08x-%02x\t%02x%s%s\n", + part_num, lba_start, lba_size, disksig, part_num, p->sys_ind, + (is_extended(p->sys_ind) ? " Extd" : ""), + (is_bootable(p) ? " Boot" : "")); } static int test_block_type(unsigned char *buffer) @@ -87,22 +93,24 @@ static int test_block_type(unsigned char *buffer) int test_part_dos (block_dev_desc_t *dev_desc) { - unsigned char buffer[DEFAULT_SECTOR_SIZE]; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz); - if ((dev_desc->block_read(dev_desc->dev, 0, 1, (ulong *) buffer) != 1) || - (buffer[DOS_PART_MAGIC_OFFSET + 0] != 0x55) || - (buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) ) { - return (-1); - } - return (0); + if (dev_desc->block_read(dev_desc->dev, 0, 1, (ulong *) buffer) != 1) + return -1; + + if (test_block_type(buffer) != DOS_MBR) + return -1; + + return 0; } /* Print a partition that is relative to its Extended partition table */ -static void print_partition_extended (block_dev_desc_t *dev_desc, int ext_part_sector, int relative, - int part_num) +static void print_partition_extended(block_dev_desc_t *dev_desc, + int ext_part_sector, int relative, + int part_num, unsigned int disksig) { - unsigned char buffer[DEFAULT_SECTOR_SIZE]; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz); dos_partition_t *pt; int i; @@ -112,17 +120,16 @@ static void print_partition_extended (block_dev_desc_t *dev_desc, int ext_part_s return; } i=test_block_type(buffer); - if(i==-1) { + if (i != DOS_MBR) { printf ("bad MBR sector signature 0x%02x%02x\n", buffer[DOS_PART_MAGIC_OFFSET], buffer[DOS_PART_MAGIC_OFFSET + 1]); return; } - if(i==DOS_PBR) { - printf (" 1\t\t 0\t%10ld\t%2x\n", - dev_desc->lba, buffer[DOS_PBR_MEDIA_TYPE_OFFSET]); - return; - } + + if (!ext_part_sector) + disksig = le32_to_int(&buffer[DOS_PART_DISKSIG_OFFSET]); + /* Print all primary/logical partitions */ pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET); for (i = 0; i < 4; i++, pt++) { @@ -133,7 +140,7 @@ static void print_partition_extended (block_dev_desc_t *dev_desc, int ext_part_s if ((pt->sys_ind != 0) && (ext_part_sector == 0 || !is_extended (pt->sys_ind)) ) { - print_one_part (pt, ext_part_sector, part_num); + print_one_part(pt, ext_part_sector, part_num, disksig); } /* Reverse engr the fdisk part# assignment rule! */ @@ -149,10 +156,9 @@ static void print_partition_extended (block_dev_desc_t *dev_desc, int ext_part_s if (is_extended (pt->sys_ind)) { int lba_start = le32_to_int (pt->start4) + relative; - print_partition_extended (dev_desc, lba_start, - ext_part_sector == 0 ? lba_start - : relative, - part_num); + print_partition_extended(dev_desc, lba_start, + ext_part_sector == 0 ? lba_start : relative, + part_num, disksig); } } @@ -164,9 +170,10 @@ static void print_partition_extended (block_dev_desc_t *dev_desc, int ext_part_s */ static int get_partition_info_extended (block_dev_desc_t *dev_desc, int ext_part_sector, int relative, int part_num, - int which_part, disk_partition_t *info) + int which_part, disk_partition_t *info, + unsigned int disksig) { - unsigned char buffer[DEFAULT_SECTOR_SIZE]; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz); dos_partition_t *pt; int i; @@ -183,6 +190,11 @@ static int get_partition_info_extended (block_dev_desc_t *dev_desc, int ext_part return -1; } +#ifdef CONFIG_PARTITION_UUIDS + if (!ext_part_sector) + disksig = le32_to_int(&buffer[DOS_PART_DISKSIG_OFFSET]); +#endif + /* Print all primary/logical partitions */ pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET); for (i = 0; i < 4; i++, pt++) { @@ -223,6 +235,10 @@ static int get_partition_info_extended (block_dev_desc_t *dev_desc, int ext_part } /* sprintf(info->type, "%d, pt->sys_ind); */ sprintf ((char *)info->type, "U-Boot"); + info->bootable = is_bootable(pt); +#ifdef CONFIG_PARTITION_UUIDS + sprintf(info->uuid, "%08x-%02x", disksig, part_num); +#endif return 0; } @@ -241,7 +257,7 @@ static int get_partition_info_extended (block_dev_desc_t *dev_desc, int ext_part return get_partition_info_extended (dev_desc, lba_start, ext_part_sector == 0 ? lba_start : relative, - part_num, which_part, info); + part_num, which_part, info, disksig); } } return -1; @@ -249,13 +265,13 @@ static int get_partition_info_extended (block_dev_desc_t *dev_desc, int ext_part void print_part_dos (block_dev_desc_t *dev_desc) { - printf ("Partition Start Sector Num Sectors Type\n"); - print_partition_extended (dev_desc, 0, 0, 1); + printf("Part\tStart Sector\tNum Sectors\tUUID\t\tType\n"); + print_partition_extended(dev_desc, 0, 0, 1, 0); } int get_partition_info_dos (block_dev_desc_t *dev_desc, int part, disk_partition_t * info) { - return get_partition_info_extended (dev_desc, 0, 0, 1, part, info); + return get_partition_info_extended(dev_desc, 0, 0, 1, part, info, 0); } diff --git a/u-boot/disk/part_dos.h b/u-boot/disk/part_dos.h index 195a32c..7b77c1d 100644..100755 --- a/u-boot/disk/part_dos.h +++ b/u-boot/disk/part_dos.h @@ -24,14 +24,7 @@ #ifndef _DISK_PART_DOS_H #define _DISK_PART_DOS_H - -#ifdef CONFIG_ISO_PARTITION -/* Make the buffers bigger if ISO partition support is enabled -- CD-ROMS - have 2048 byte blocks */ -#define DEFAULT_SECTOR_SIZE 2048 -#else -#define DEFAULT_SECTOR_SIZE 512 -#endif +#define DOS_PART_DISKSIG_OFFSET 0x1b8 #define DOS_PART_TBL_OFFSET 0x1be #define DOS_PART_MAGIC_OFFSET 0x1fe #define DOS_PBR_FSTYPE_OFFSET 0x36 diff --git a/u-boot/fs/Makefile b/u-boot/fs/Makefile index 22aad12..bd72111 100644 --- a/u-boot/fs/Makefile +++ b/u-boot/fs/Makefile @@ -22,8 +22,21 @@ # # +include $(TOPDIR)/config.mk + +LIB = $(obj)libfs.o + +COBJS-y += fs.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + subdirs-$(CONFIG_CMD_CRAMFS) := cramfs subdirs-$(CONFIG_CMD_EXT2) += ext2 +subdirs-$(CONFIG_CMD_EXT4) += ext4 subdirs-$(CONFIG_CMD_FAT) += fat subdirs-$(CONFIG_CMD_FDOS) += fdos subdirs-$(CONFIG_CMD_JFFS2) += jffs2 @@ -31,8 +44,16 @@ subdirs-$(CONFIG_CMD_REISER) += reiserfs subdirs-$(CONFIG_YAFFS2) += yaffs2 subdirs-$(CONFIG_CMD_UBIFS) += ubifs +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + SUBDIRS := $(subdirs-y) -$(obj).depend all: - @for dir in $(SUBDIRS) ; do \ - $(MAKE) -C $$dir $@ ; done +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/u-boot/fs/fat/fat.c b/u-boot/fs/fat/fat.c index a75e4f2..393c378 100644..100755 --- a/u-boot/fs/fat/fat.c +++ b/u-boot/fs/fat/fat.c @@ -27,14 +27,17 @@ #include <common.h> #include <config.h> +#include <exports.h> #include <fat.h> #include <asm/byteorder.h> #include <part.h> +#include <malloc.h> +#include <linux/compiler.h> /* * Convert a string to lowercase. */ -static void downcase (char *str) +static void downcase(char *str) { while (*str != '\0') { TOLOWER(*str); @@ -42,99 +45,85 @@ static void downcase (char *str) } } -static block_dev_desc_t *cur_dev = NULL; +static block_dev_desc_t *cur_dev; +static disk_partition_t cur_part_info; -static unsigned long part_offset = 0; - -static int cur_part = 1; - -#define DOS_PART_TBL_OFFSET 0x1be -#define DOS_PART_MAGIC_OFFSET 0x1fe +#define DOS_BOOT_MAGIC_OFFSET 0x1fe #define DOS_FS_TYPE_OFFSET 0x36 #define DOS_FS32_TYPE_OFFSET 0x52 -static int disk_read (__u32 startblock, __u32 getsize, __u8 * bufptr) +static int disk_read(__u32 block, __u32 nr_blocks, void *buf) { - if (cur_dev == NULL) + if (!cur_dev || !cur_dev->block_read) return -1; - startblock += part_offset; - - if (cur_dev->block_read) { - return cur_dev->block_read(cur_dev->dev, startblock, getsize, - (unsigned long *) bufptr); - } - return -1; + return cur_dev->block_read(cur_dev->dev, + cur_part_info.start + block, nr_blocks, buf); } -int fat_register_device (block_dev_desc_t * dev_desc, int part_no) +int fat_set_blk_dev(block_dev_desc_t *dev_desc, disk_partition_t *info) { - unsigned char buffer[SECTOR_SIZE]; - - disk_partition_t info; - - if (!dev_desc->block_read) - return -1; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz); cur_dev = dev_desc; - /* check if we have a MBR (on floppies we have only a PBR) */ - if (dev_desc->block_read(dev_desc->dev, 0, 1, (ulong *)buffer) != 1) { - printf("** Can't read from device %d **\n", - dev_desc->dev); - return -1; - } - if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 || - buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) { - /* no signature found */ + cur_part_info = *info; + + /* Make sure it has a valid FAT header */ + if (disk_read(0, 1, buffer) != 1) { + cur_dev = NULL; return -1; } -#if (defined(CONFIG_CMD_IDE) || \ - defined(CONFIG_CMD_MG_DISK) || \ - defined(CONFIG_CMD_SATA) || \ - defined(CONFIG_CMD_SCSI) || \ - defined(CONFIG_CMD_USB) || \ - defined(CONFIG_MMC) || \ - defined(CONFIG_SYSTEMACE) ) - /* First we assume there is a MBR */ - if (!get_partition_info(dev_desc, part_no, &info)) { - part_offset = info.start; - cur_part = part_no; - } else if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0) || - (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) { - /* ok, we assume we are on a PBR only */ - cur_part = 1; - part_offset = 0; - } else { - printf("** Partition %d not valid on device %d **\n", - part_no, dev_desc->dev); + + /* Check if it's actually a DOS volume */ + if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) { + cur_dev = NULL; return -1; } -#else - if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0) || - (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) { - /* ok, we assume we are on a PBR only */ - cur_part = 1; - part_offset = 0; - info.start = part_offset; - } else { - /* FIXME we need to determine the start block of the - * partition where the DOS FS resides. This can be done - * by using the get_partition_info routine. For this - * purpose the libpart must be included. - */ - part_offset = 32; - cur_part = 1; - } + /* Check for FAT12/FAT16/FAT32 filesystem */ + if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3)) + return 0; + if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5)) + return 0; + + cur_dev = NULL; + return -1; +} + +int fat_register_device(block_dev_desc_t *dev_desc, int part_no) +{ + disk_partition_t info; + + /* First close any currently found FAT filesystem */ + cur_dev = NULL; + + /* Read the partition table, if present */ + if (get_partition_info(dev_desc, part_no, &info)) { + if (part_no != 0) { + printf("** Partition %d not valid on device %d **\n", + part_no, dev_desc->dev); + return -1; + } + + info.start = 0; + info.size = dev_desc->lba; + info.blksz = dev_desc->blksz; + info.name[0] = 0; + info.type[0] = 0; + info.bootable = 0; +#ifdef CONFIG_PARTITION_UUIDS + info.uuid[0] = 0; #endif - return 0; + } + + return fat_set_blk_dev(dev_desc, &info); } /* * Get the first occurence of a directory delimiter ('/' or '\') in a string. * Return index into string if found, -1 otherwise. */ -static int dirdelim (char *str) +static int dirdelim(char *str) { char *start = str; @@ -149,7 +138,7 @@ static int dirdelim (char *str) /* * Extract zero terminated short name from a directory entry. */ -static void get_name (dir_entry *dirent, char *s_name) +static void get_name(dir_entry *dirent, char *s_name) { char *ptr; @@ -178,7 +167,7 @@ static void get_name (dir_entry *dirent, char *s_name) * Get the entry at index 'entry' in a FAT (12/16/32) table. * On failure 0x00 is returned. */ -static __u32 get_fatent (fsdata *mydata, __u32 entry) +static __u32 get_fatent(fsdata *mydata, __u32 entry) { __u32 bufnum; __u32 off16, offset; @@ -209,16 +198,16 @@ static __u32 get_fatent (fsdata *mydata, __u32 entry) /* Read a new block of FAT entries into the cache. */ if (bufnum != mydata->fatbufnum) { - int getsize = FATBUFSIZE / FS_BLOCK_SIZE; + __u32 getsize = FATBUFBLOCKS; __u8 *bufptr = mydata->fatbuf; __u32 fatlength = mydata->fatlength; __u32 startblock = bufnum * FATBUFBLOCKS; - fatlength *= SECTOR_SIZE; /* We want it in bytes now */ + if (startblock + getsize > fatlength) + getsize = fatlength - startblock; + startblock += mydata->fat_sect; /* Offset from start of disk */ - if (getsize > fatlength) - getsize = fatlength; if (disk_read(startblock, getsize, bufptr) < 0) { debug("Error reading FAT blocks\n"); return ret; @@ -276,11 +265,11 @@ static __u32 get_fatent (fsdata *mydata, __u32 entry) * Return 0 on success, -1 otherwise. */ static int -get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer, - unsigned long size) +get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size) { - int idx = 0; + __u32 idx = 0; __u32 startsect; + int ret; if (clustnum > 0) { startsect = mydata->data_begin + @@ -291,50 +280,122 @@ get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer, debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect); - if (disk_read(startsect, size / FS_BLOCK_SIZE, buffer) < 0) { - debug("Error reading data\n"); - return -1; + if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) { + ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); + + printf("FAT: Misaligned buffer address (%p)\n", buffer); + + while (size >= mydata->sect_size) { + ret = disk_read(startsect++, 1, tmpbuf); + if (ret != 1) { + debug("Error reading data (got %d)\n", ret); + return -1; + } + + memcpy(buffer, tmpbuf, mydata->sect_size); + buffer += mydata->sect_size; + size -= mydata->sect_size; + } + } else { + idx = size / mydata->sect_size; + ret = disk_read(startsect, idx, buffer); + if (ret != idx) { + debug("Error reading data (got %d)\n", ret); + return -1; + } + startsect += idx; + idx *= mydata->sect_size; + buffer += idx; + size -= idx; } - if (size % FS_BLOCK_SIZE) { - __u8 tmpbuf[FS_BLOCK_SIZE]; + if (size) { + ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); - idx = size / FS_BLOCK_SIZE; - if (disk_read(startsect + idx, 1, tmpbuf) < 0) { - debug("Error reading data\n"); + ret = disk_read(startsect, 1, tmpbuf); + if (ret != 1) { + debug("Error reading data (got %d)\n", ret); return -1; } - buffer += idx * FS_BLOCK_SIZE; - memcpy(buffer, tmpbuf, size % FS_BLOCK_SIZE); - return 0; + memcpy(buffer, tmpbuf, size); } return 0; } /* - * Read at most 'maxsize' bytes from the file associated with 'dentptr' + * Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr' * into 'buffer'. * Return the number of bytes read or -1 on fatal errors. */ +__u8 get_contents_vfatname_block[MAX_CLUSTSIZE] + __aligned(ARCH_DMA_MINALIGN); + static long -get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer, - unsigned long maxsize) +get_contents(fsdata *mydata, dir_entry *dentptr, unsigned long pos, + __u8 *buffer, unsigned long maxsize) { unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0; - unsigned int bytesperclust = mydata->clust_size * SECTOR_SIZE; + unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; __u32 curclust = START(dentptr); __u32 endclust, newclust; unsigned long actsize; debug("Filesize: %ld bytes\n", filesize); - if (maxsize > 0 && filesize > maxsize) - filesize = maxsize; + if (pos >= filesize) { + debug("Read position past EOF: %lu\n", pos); + return gotsize; + } + + if (maxsize > 0 && filesize > pos + maxsize) + filesize = pos + maxsize; debug("%ld bytes\n", filesize); actsize = bytesperclust; + + /* go to cluster at pos */ + while (actsize <= pos) { + curclust = get_fatent(mydata, curclust); + if (CHECK_CLUST(curclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", curclust); + debug("Invalid FAT entry\n"); + return gotsize; + } + actsize += bytesperclust; + } + + /* actsize > pos */ + actsize -= bytesperclust; + filesize -= actsize; + pos -= actsize; + + /* align to beginning of next cluster if any */ + if (pos) { + actsize = min(filesize, bytesperclust); + if (get_cluster(mydata, curclust, get_contents_vfatname_block, + (int)actsize) != 0) { + printf("Error reading cluster\n"); + return -1; + } + filesize -= actsize; + actsize -= pos; + memcpy(buffer, get_contents_vfatname_block + pos, actsize); + gotsize += actsize; + if (!filesize) + return gotsize; + buffer += actsize; + + curclust = get_fatent(mydata, curclust); + if (CHECK_CLUST(curclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", curclust); + debug("Invalid FAT entry\n"); + return gotsize; + } + } + + actsize = bytesperclust; endclust = curclust; do { @@ -352,21 +413,9 @@ get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer, actsize += bytesperclust; } - /* actsize >= file size */ - actsize -= bytesperclust; - - /* get remaining clusters */ - if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { - printf("Error reading cluster\n"); - return -1; - } - /* get remaining bytes */ - gotsize += (int)actsize; - filesize -= actsize; - buffer += actsize; actsize = filesize; - if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) { + if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { printf("Error reading cluster\n"); return -1; } @@ -398,7 +447,7 @@ getit: * starting at l_name[*idx]. * Return 1 if terminator (zero byte) is found, 0 otherwise. */ -static int slot2str (dir_slot *slotptr, char *l_name, int *idx) +static int slot2str(dir_slot *slotptr, char *l_name, int *idx) { int j; @@ -430,19 +479,15 @@ static int slot2str (dir_slot *slotptr, char *l_name, int *idx) * into 'retdent' * Return 0 on success, -1 otherwise. */ -__attribute__ ((__aligned__ (__alignof__ (dir_entry)))) -__u8 get_vfatname_block[MAX_CLUSTSIZE]; - static int -get_vfatname (fsdata *mydata, int curclust, __u8 *cluster, - dir_entry *retdent, char *l_name) +get_vfatname(fsdata *mydata, int curclust, __u8 *cluster, + dir_entry *retdent, char *l_name) { dir_entry *realdent; dir_slot *slotptr = (dir_slot *)retdent; - __u8 *buflimit = cluster + ((curclust == 0) ? - LINEAR_PREFETCH_SIZE : - (mydata->clust_size * SECTOR_SIZE) - ); + __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ? + PREFETCH_BLOCKS : + mydata->clust_size); __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff; int idx = 0; @@ -472,13 +517,13 @@ get_vfatname (fsdata *mydata, int curclust, __u8 *cluster, return -1; } - if (get_cluster(mydata, curclust, get_vfatname_block, - mydata->clust_size * SECTOR_SIZE) != 0) { + if (get_cluster(mydata, curclust, get_contents_vfatname_block, + mydata->clust_size * mydata->sect_size) != 0) { debug("Error: reading directory block\n"); return -1; } - slotptr2 = (dir_slot *)get_vfatname_block; + slotptr2 = (dir_slot *)get_contents_vfatname_block; while (counter > 0) { if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter) @@ -489,7 +534,7 @@ get_vfatname (fsdata *mydata, int curclust, __u8 *cluster, /* Save the real directory entry */ realdent = (dir_entry *)slotptr2; - while ((__u8 *)slotptr2 > get_vfatname_block) { + while ((__u8 *)slotptr2 > get_contents_vfatname_block) { slotptr2--; slot2str(slotptr2, l_name, &idx); } @@ -518,15 +563,16 @@ get_vfatname (fsdata *mydata, int curclust, __u8 *cluster, } /* Calculate short name checksum */ -static __u8 mkcksum (const char *str) +static __u8 mkcksum(const char name[8], const char ext[3]) { int i; __u8 ret = 0; - for (i = 0; i < 11; i++) { - ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + str[i]; - } + for (i = 0; i < sizeof(name); i++) + ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + name[i]; + for (i = 0; i < sizeof(ext); i++) + ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + ext[i]; return ret; } @@ -536,12 +582,12 @@ static __u8 mkcksum (const char *str) * Get the directory entry associated with 'filename' from the directory * starting at 'startsect' */ -__attribute__ ((__aligned__ (__alignof__ (dir_entry)))) -__u8 get_dentfromdir_block[MAX_CLUSTSIZE]; +__u8 get_dentfromdir_block[MAX_CLUSTSIZE] + __aligned(ARCH_DMA_MINALIGN); -static dir_entry *get_dentfromdir (fsdata *mydata, int startsect, - char *filename, dir_entry *retdent, - int dols) +static dir_entry *get_dentfromdir(fsdata *mydata, int startsect, + char *filename, dir_entry *retdent, + int dols) { __u16 prevcksum = 0xffff; __u32 curclust = START(retdent); @@ -555,7 +601,7 @@ static dir_entry *get_dentfromdir (fsdata *mydata, int startsect, int i; if (get_cluster(mydata, curclust, get_dentfromdir_block, - mydata->clust_size * SECTOR_SIZE) != 0) { + mydata->clust_size * mydata->sect_size) != 0) { debug("Error: reading directory block\n"); return NULL; } @@ -572,8 +618,8 @@ static dir_entry *get_dentfromdir (fsdata *mydata, int startsect, } if ((dentptr->attr & ATTR_VOLUME)) { #ifdef CONFIG_SUPPORT_VFAT - if ((dentptr->attr & ATTR_VFAT) && - (dentptr-> name[0] & LAST_LONG_ENTRY_MASK)) { + if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT && + (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { prevcksum = ((dir_slot *)dentptr)->alias_checksum; get_vfatname(mydata, curclust, get_dentfromdir_block, @@ -629,7 +675,9 @@ static dir_entry *get_dentfromdir (fsdata *mydata, int startsect, return NULL; } #ifdef CONFIG_SUPPORT_VFAT - if (dols && mkcksum(dentptr->name) == prevcksum) { + __u8 csum = mkcksum(dentptr->name, dentptr->ext); + if (dols && csum == prevcksum) { + prevcksum = 0xffff; dentptr++; continue; } @@ -700,17 +748,28 @@ static dir_entry *get_dentfromdir (fsdata *mydata, int startsect, * Read boot sector and volume info from a FAT filesystem */ static int -read_bootsectandvi (boot_sector *bs, volume_info *volinfo, int *fatsize) +read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize) { - __u8 block[FS_BLOCK_SIZE]; - + __u8 *block; volume_info *vistart; + int ret = 0; - if (disk_read (0, 1, block) < 0) { - debug("Error: reading block\n"); + if (cur_dev == NULL) { + debug("Error: no device selected\n"); + return -1; + } + + block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz); + if (block == NULL) { + debug("Error: allocating block\n"); return -1; } + if (disk_read(0, 1, block) < 0) { + debug("Error: reading block\n"); + goto fail; + } + memcpy(bs, block, sizeof(boot_sector)); bs->reserved = FAT2CPU16(bs->reserved); bs->fat_length = FAT2CPU16(bs->fat_length); @@ -736,43 +795,48 @@ read_bootsectandvi (boot_sector *bs, volume_info *volinfo, int *fatsize) if (*fatsize == 32) { if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0) - return 0; + goto exit; } else { if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) { *fatsize = 12; - return 0; + goto exit; } if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) { *fatsize = 16; - return 0; + goto exit; } } debug("Error: broken fs_type sign\n"); - return -1; +fail: + ret = -1; +exit: + free(block); + return ret; } -__attribute__ ((__aligned__ (__alignof__ (dir_entry)))) -__u8 do_fat_read_block[MAX_CLUSTSIZE]; +__u8 do_fat_read_at_block[MAX_CLUSTSIZE] + __aligned(ARCH_DMA_MINALIGN); long -do_fat_read (const char *filename, void *buffer, unsigned long maxsize, - int dols) +do_fat_read_at(const char *filename, unsigned long pos, void *buffer, + unsigned long maxsize, int dols) { char fnamecopy[2048]; boot_sector bs; volume_info volinfo; fsdata datablock; fsdata *mydata = &datablock; - dir_entry *dentptr; + dir_entry *dentptr = NULL; __u16 prevcksum = 0xffff; char *subname = ""; - int cursect; + __u32 cursect; int idx, isdir = 0; int files = 0, dirs = 0; - long ret = 0; + long ret = -1; int firsttime; - int root_cluster; + __u32 root_cluster = 0; + int rootdir_size = 0; int j; if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) { @@ -780,36 +844,45 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize, return -1; } - root_cluster = bs.root_cluster; - - if (mydata->fatsize == 32) + if (mydata->fatsize == 32) { + root_cluster = bs.root_cluster; mydata->fatlength = bs.fat32_length; - else + } else { mydata->fatlength = bs.fat_length; + } mydata->fat_sect = bs.reserved; cursect = mydata->rootdir_sect = mydata->fat_sect + mydata->fatlength * bs.fats; + mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0]; mydata->clust_size = bs.cluster_size; + if (mydata->sect_size != cur_part_info.blksz) { + printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n", + mydata->sect_size, cur_part_info.blksz); + return -1; + } if (mydata->fatsize == 32) { mydata->data_begin = mydata->rootdir_sect - (mydata->clust_size * 2); } else { - int rootdir_size; - rootdir_size = ((bs.dir_entries[1] * (int)256 + bs.dir_entries[0]) * sizeof(dir_entry)) / - SECTOR_SIZE; + mydata->sect_size; mydata->data_begin = mydata->rootdir_sect + rootdir_size - (mydata->clust_size * 2); } mydata->fatbufnum = -1; + mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE); + if (mydata->fatbuf == NULL) { + debug("Error: allocating memory\n"); + return -1; + } #ifdef CONFIG_SUPPORT_VFAT debug("VFAT Support enabled\n"); @@ -820,8 +893,9 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize, "Data begins at: %d\n", root_cluster, mydata->rootdir_sect, - mydata->rootdir_sect * SECTOR_SIZE, mydata->data_begin); - debug("Cluster size: %d\n", mydata->clust_size); + mydata->rootdir_sect * mydata->sect_size, mydata->data_begin); + debug("Sector size: %d, cluster size: %d\n", mydata->sect_size, + mydata->clust_size); /* "cwd" is always the root... */ while (ISDIRDELIM(*filename)) @@ -833,7 +907,7 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize, if (*fnamecopy == '\0') { if (!dols) - return -1; + goto exit; dols = LS_ROOT; } else if ((idx = dirdelim(fnamecopy)) >= 0) { @@ -852,40 +926,43 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize, while (1) { int i; - debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%d\n", - cursect, mydata->clust_size, DIRENTSPERBLOCK); + if (j == 0) { + debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%zd\n", + cursect, mydata->clust_size, DIRENTSPERBLOCK); + + if (disk_read(cursect, + (mydata->fatsize == 32) ? + (mydata->clust_size) : + PREFETCH_BLOCKS, + do_fat_read_at_block) < 0) { + debug("Error: reading rootdir block\n"); + goto exit; + } - if (disk_read(cursect, - (mydata->fatsize == 32) ? - (mydata->clust_size) : - LINEAR_PREFETCH_SIZE / SECTOR_SIZE, - do_fat_read_block) < 0) { - debug("Error: reading rootdir block\n"); - return -1; + dentptr = (dir_entry *) do_fat_read_at_block; } - dentptr = (dir_entry *) do_fat_read_block; - for (i = 0; i < DIRENTSPERBLOCK; i++) { char s_name[14], l_name[VFAT_MAXLEN_BYTES]; + __u8 csum; l_name[0] = '\0'; if (dentptr->name[0] == DELETED_FLAG) { dentptr++; continue; } - if ((dentptr->attr & ATTR_VOLUME)) { + + csum = mkcksum(dentptr->name, dentptr->ext); + if (dentptr->attr & ATTR_VOLUME) { #ifdef CONFIG_SUPPORT_VFAT - if ((dentptr->attr & ATTR_VFAT) && + if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT && (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { prevcksum = ((dir_slot *)dentptr)->alias_checksum; get_vfatname(mydata, - (mydata->fatsize == 32) ? - root_cluster : - 0, - do_fat_read_block, + root_cluster, + do_fat_read_at_block, dentptr, l_name); if (dols == LS_ROOT) { @@ -934,13 +1011,13 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize, if (dols == LS_ROOT) { printf("\n%d file(s), %d dir(s)\n\n", files, dirs); - return 0; + ret = 0; } - return -1; + goto exit; } #ifdef CONFIG_SUPPORT_VFAT - else if (dols == LS_ROOT && - mkcksum(dentptr->name) == prevcksum) { + else if (dols == LS_ROOT && csum == prevcksum) { + prevcksum = 0xffff; dentptr++; continue; } @@ -988,7 +1065,7 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize, } if (isdir && !(dentptr->attr & ATTR_DIR)) - return -1; + goto exit; debug("RootName: %s", s_name); debug(", start: 0x%x", START(dentptr)); @@ -1006,26 +1083,40 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize, * root directory clusters when a cluster has been * completely processed. */ - if ((mydata->fatsize == 32) && (++j == mydata->clust_size)) { - int nxtsect; - int nxt_clust; - - nxt_clust = get_fatent(mydata, root_cluster); + ++j; + int rootdir_end = 0; + if (mydata->fatsize == 32) { + if (j == mydata->clust_size) { + int nxtsect = 0; + int nxt_clust = 0; - nxtsect = mydata->data_begin + - (nxt_clust * mydata->clust_size); + nxt_clust = get_fatent(mydata, root_cluster); + rootdir_end = CHECK_CLUST(nxt_clust, 32); - debug("END LOOP: sect=%d, root_clust=%d, " - "n_sect=%d, n_clust=%d\n", - cursect, root_cluster, - nxtsect, nxt_clust); + nxtsect = mydata->data_begin + + (nxt_clust * mydata->clust_size); - root_cluster = nxt_clust; + root_cluster = nxt_clust; - cursect = nxtsect; - j = 0; + cursect = nxtsect; + j = 0; + } } else { - cursect++; + if (j == PREFETCH_BLOCKS) + j = 0; + + rootdir_end = (++cursect - mydata->rootdir_sect >= + rootdir_size); + } + + /* If end of rootdir reached */ + if (rootdir_end) { + if (dols == LS_ROOT) { + printf("\n%d file(s), %d dir(s)\n\n", + files, dirs); + ret = 0; + } + goto exit; } } rootdir_done: @@ -1062,24 +1153,32 @@ rootdir_done: if (get_dentfromdir(mydata, startsect, subname, dentptr, isdir ? 0 : dols) == NULL) { if (dols && !isdir) - return 0; - return -1; + ret = 0; + goto exit; } - if (idx >= 0) { - if (!(dentptr->attr & ATTR_DIR)) - return -1; + if (isdir && !(dentptr->attr & ATTR_DIR)) + goto exit; + + if (idx >= 0) subname = nextname; - } } - ret = get_contents(mydata, dentptr, buffer, maxsize); + ret = get_contents(mydata, dentptr, pos, buffer, maxsize); debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret); +exit: + free(mydata->fatbuf); return ret; } -int file_fat_detectfs (void) +long +do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols) +{ + return do_fat_read_at(filename, 0, buffer, maxsize, dols); +} + +int file_fat_detectfs(void) { boot_sector bs; volume_info volinfo; @@ -1092,7 +1191,6 @@ int file_fat_detectfs (void) } #if defined(CONFIG_CMD_IDE) || \ - defined(CONFIG_CMD_MG_DISK) || \ defined(CONFIG_CMD_SATA) || \ defined(CONFIG_CMD_SCSI) || \ defined(CONFIG_CMD_USB) || \ @@ -1137,19 +1235,24 @@ int file_fat_detectfs (void) vol_label[11] = '\0'; volinfo.fs_type[5] = '\0'; - printf("Partition %d: Filesystem: %s \"%s\"\n", cur_part, - volinfo.fs_type, vol_label); + printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label); return 0; } -int file_fat_ls (const char *dir) +int file_fat_ls(const char *dir) { return do_fat_read(dir, NULL, 0, LS_YES); } -long file_fat_read (const char *filename, void *buffer, unsigned long maxsize) +long file_fat_read_at(const char *filename, unsigned long pos, void *buffer, + unsigned long maxsize) { printf("reading %s\n", filename); - return do_fat_read(filename, buffer, maxsize, LS_NO); + return do_fat_read_at(filename, pos, buffer, maxsize, LS_NO); +} + +long file_fat_read(const char *filename, void *buffer, unsigned long maxsize) +{ + return file_fat_read_at(filename, 0, buffer, maxsize); } diff --git a/u-boot/fs/fat/fat_write.c b/u-boot/fs/fat/fat_write.c new file mode 100755 index 0000000..4a1bda0 --- /dev/null +++ b/u-boot/fs/fat/fat_write.c @@ -0,0 +1,1098 @@ +/* + * fat_write.c + * + * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <command.h> +#include <config.h> +#include <fat.h> +#include <asm/byteorder.h> +#include <part.h> +#include "fat.c" + +static void uppercase(char *str, int len) +{ + int i; + + for (i = 0; i < len; i++) { + TOUPPER(*str); + str++; + } +} + +static int total_sector; +static int disk_write(__u32 block, __u32 nr_blocks, void *buf) +{ + if (!cur_dev || !cur_dev->block_write) + return -1; + + if (cur_part_info.start + block + nr_blocks > + cur_part_info.start + total_sector) { + printf("error: overflow occurs\n"); + return -1; + } + + return cur_dev->block_write(cur_dev->dev, + cur_part_info.start + block, nr_blocks, buf); +} + +/* + * Set short name in directory entry + */ +static void set_name(dir_entry *dirent, const char *filename) +{ + char s_name[VFAT_MAXLEN_BYTES]; + char *period; + int period_location, len, i, ext_num; + + if (filename == NULL) + return; + + len = strlen(filename); + if (len == 0) + return; + + memcpy(s_name, filename, len); + uppercase(s_name, len); + + period = strchr(s_name, '.'); + if (period == NULL) { + period_location = len; + ext_num = 0; + } else { + period_location = period - s_name; + ext_num = len - period_location - 1; + } + + /* Pad spaces when the length of file name is shorter than eight */ + if (period_location < 8) { + memcpy(dirent->name, s_name, period_location); + for (i = period_location; i < 8; i++) + dirent->name[i] = ' '; + } else if (period_location == 8) { + memcpy(dirent->name, s_name, period_location); + } else { + memcpy(dirent->name, s_name, 6); + dirent->name[6] = '~'; + dirent->name[7] = '1'; + } + + if (ext_num < 3) { + memcpy(dirent->ext, s_name + period_location + 1, ext_num); + for (i = ext_num; i < 3; i++) + dirent->ext[i] = ' '; + } else + memcpy(dirent->ext, s_name + period_location + 1, 3); + + debug("name : %s\n", dirent->name); + debug("ext : %s\n", dirent->ext); +} + +static __u8 num_of_fats; +/* + * Write fat buffer into block device + */ +static int flush_fat_buffer(fsdata *mydata) +{ + int getsize = FATBUFBLOCKS; + __u32 fatlength = mydata->fatlength; + __u8 *bufptr = mydata->fatbuf; + __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS; + + fatlength *= mydata->sect_size; + startblock += mydata->fat_sect; + + if (getsize > fatlength) + getsize = fatlength; + + /* Write FAT buf */ + if (disk_write(startblock, getsize, bufptr) < 0) { + debug("error: writing FAT blocks\n"); + return -1; + } + + if (num_of_fats == 2) { + /* Update corresponding second FAT blocks */ + startblock += mydata->fatlength; + if (disk_write(startblock, getsize, bufptr) < 0) { + debug("error: writing second FAT blocks\n"); + return -1; + } + } + + return 0; +} + +/* + * Get the entry at index 'entry' in a FAT (12/16/32) table. + * On failure 0x00 is returned. + * When bufnum is changed, write back the previous fatbuf to the disk. + */ +static __u32 get_fatent_value(fsdata *mydata, __u32 entry) +{ + __u32 bufnum; + __u32 off16, offset; + __u32 ret = 0x00; + __u16 val1, val2; + + switch (mydata->fatsize) { + case 32: + bufnum = entry / FAT32BUFSIZE; + offset = entry - bufnum * FAT32BUFSIZE; + break; + case 16: + bufnum = entry / FAT16BUFSIZE; + offset = entry - bufnum * FAT16BUFSIZE; + break; + case 12: + bufnum = entry / FAT12BUFSIZE; + offset = entry - bufnum * FAT12BUFSIZE; + break; + + default: + /* Unsupported FAT size */ + return ret; + } + + debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n", + mydata->fatsize, entry, entry, offset, offset); + + /* Read a new block of FAT entries into the cache. */ + if (bufnum != mydata->fatbufnum) { + int getsize = FATBUFBLOCKS; + __u8 *bufptr = mydata->fatbuf; + __u32 fatlength = mydata->fatlength; + __u32 startblock = bufnum * FATBUFBLOCKS; + + if (getsize > fatlength) + getsize = fatlength; + + fatlength *= mydata->sect_size; /* We want it in bytes now */ + startblock += mydata->fat_sect; /* Offset from start of disk */ + + /* Write back the fatbuf to the disk */ + if (mydata->fatbufnum != -1) { + if (flush_fat_buffer(mydata) < 0) + return -1; + } + + if (disk_read(startblock, getsize, bufptr) < 0) { + debug("Error reading FAT blocks\n"); + return ret; + } + mydata->fatbufnum = bufnum; + } + + /* Get the actual entry from the table */ + switch (mydata->fatsize) { + case 32: + ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]); + break; + case 16: + ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]); + break; + case 12: + off16 = (offset * 3) / 4; + + switch (offset & 0x3) { + case 0: + ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]); + ret &= 0xfff; + break; + case 1: + val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); + val1 &= 0xf000; + val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); + val2 &= 0x00ff; + ret = (val2 << 4) | (val1 >> 12); + break; + case 2: + val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); + val1 &= 0xff00; + val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); + val2 &= 0x000f; + ret = (val2 << 8) | (val1 >> 8); + break; + case 3: + ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); + ret = (ret & 0xfff0) >> 4; + break; + default: + break; + } + break; + } + debug("FAT%d: ret: %08x, entry: %08x, offset: %04x\n", + mydata->fatsize, ret, entry, offset); + + return ret; +} + +#ifdef CONFIG_SUPPORT_VFAT +/* + * Set the file name information from 'name' into 'slotptr', + */ +static int str2slot(dir_slot *slotptr, const char *name, int *idx) +{ + int j, end_idx = 0; + + for (j = 0; j <= 8; j += 2) { + if (name[*idx] == 0x00) { + slotptr->name0_4[j] = 0; + slotptr->name0_4[j + 1] = 0; + end_idx++; + goto name0_4; + } + slotptr->name0_4[j] = name[*idx]; + (*idx)++; + end_idx++; + } + for (j = 0; j <= 10; j += 2) { + if (name[*idx] == 0x00) { + slotptr->name5_10[j] = 0; + slotptr->name5_10[j + 1] = 0; + end_idx++; + goto name5_10; + } + slotptr->name5_10[j] = name[*idx]; + (*idx)++; + end_idx++; + } + for (j = 0; j <= 2; j += 2) { + if (name[*idx] == 0x00) { + slotptr->name11_12[j] = 0; + slotptr->name11_12[j + 1] = 0; + end_idx++; + goto name11_12; + } + slotptr->name11_12[j] = name[*idx]; + (*idx)++; + end_idx++; + } + + if (name[*idx] == 0x00) + return 1; + + return 0; +/* Not used characters are filled with 0xff 0xff */ +name0_4: + for (; end_idx < 5; end_idx++) { + slotptr->name0_4[end_idx * 2] = 0xff; + slotptr->name0_4[end_idx * 2 + 1] = 0xff; + } + end_idx = 5; +name5_10: + end_idx -= 5; + for (; end_idx < 6; end_idx++) { + slotptr->name5_10[end_idx * 2] = 0xff; + slotptr->name5_10[end_idx * 2 + 1] = 0xff; + } + end_idx = 11; +name11_12: + end_idx -= 11; + for (; end_idx < 2; end_idx++) { + slotptr->name11_12[end_idx * 2] = 0xff; + slotptr->name11_12[end_idx * 2 + 1] = 0xff; + } + + return 1; +} + +static int is_next_clust(fsdata *mydata, dir_entry *dentptr); +static void flush_dir_table(fsdata *mydata, dir_entry **dentptr); + +/* + * Fill dir_slot entries with appropriate name, id, and attr + * The real directory entry is returned by 'dentptr' + */ +static void +fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name) +{ + dir_slot *slotptr = (dir_slot *)get_contents_vfatname_block; + __u8 counter = 0, checksum; + int idx = 0, ret; + char s_name[16]; + + /* Get short file name and checksum value */ + strncpy(s_name, (*dentptr)->name, 16); + checksum = mkcksum((*dentptr)->name, (*dentptr)->ext); + + do { + memset(slotptr, 0x00, sizeof(dir_slot)); + ret = str2slot(slotptr, l_name, &idx); + slotptr->id = ++counter; + slotptr->attr = ATTR_VFAT; + slotptr->alias_checksum = checksum; + slotptr++; + } while (ret == 0); + + slotptr--; + slotptr->id |= LAST_LONG_ENTRY_MASK; + + while (counter >= 1) { + if (is_next_clust(mydata, *dentptr)) { + /* A new cluster is allocated for directory table */ + flush_dir_table(mydata, dentptr); + } + memcpy(*dentptr, slotptr, sizeof(dir_slot)); + (*dentptr)++; + slotptr--; + counter--; + } + + if (is_next_clust(mydata, *dentptr)) { + /* A new cluster is allocated for directory table */ + flush_dir_table(mydata, dentptr); + } +} + +static __u32 dir_curclust; + +/* + * Extract the full long filename starting at 'retdent' (which is really + * a slot) into 'l_name'. If successful also copy the real directory entry + * into 'retdent' + * If additional adjacent cluster for directory entries is read into memory, + * then 'get_contents_vfatname_block' is copied into 'get_dentfromdir_block' and + * the location of the real directory entry is returned by 'retdent' + * Return 0 on success, -1 otherwise. + */ +static int +get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster, + dir_entry **retdent, char *l_name) +{ + dir_entry *realdent; + dir_slot *slotptr = (dir_slot *)(*retdent); + dir_slot *slotptr2 = NULL; + __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ? + PREFETCH_BLOCKS : + mydata->clust_size); + __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff; + int idx = 0, cur_position = 0; + + if (counter > VFAT_MAXSEQ) { + debug("Error: VFAT name is too long\n"); + return -1; + } + + while ((__u8 *)slotptr < buflimit) { + if (counter == 0) + break; + if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter) + return -1; + slotptr++; + counter--; + } + + if ((__u8 *)slotptr >= buflimit) { + if (curclust == 0) + return -1; + curclust = get_fatent_value(mydata, dir_curclust); + if (CHECK_CLUST(curclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", curclust); + printf("Invalid FAT entry\n"); + return -1; + } + + dir_curclust = curclust; + + if (get_cluster(mydata, curclust, get_contents_vfatname_block, + mydata->clust_size * mydata->sect_size) != 0) { + debug("Error: reading directory block\n"); + return -1; + } + + slotptr2 = (dir_slot *)get_contents_vfatname_block; + while (counter > 0) { + if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK) + & 0xff) != counter) + return -1; + slotptr2++; + counter--; + } + + /* Save the real directory entry */ + realdent = (dir_entry *)slotptr2; + while ((__u8 *)slotptr2 > get_contents_vfatname_block) { + slotptr2--; + slot2str(slotptr2, l_name, &idx); + } + } else { + /* Save the real directory entry */ + realdent = (dir_entry *)slotptr; + } + + do { + slotptr--; + if (slot2str(slotptr, l_name, &idx)) + break; + } while (!(slotptr->id & LAST_LONG_ENTRY_MASK)); + + l_name[idx] = '\0'; + if (*l_name == DELETED_FLAG) + *l_name = '\0'; + else if (*l_name == aRING) + *l_name = DELETED_FLAG; + downcase(l_name); + + /* Return the real directory entry */ + *retdent = realdent; + + if (slotptr2) { + memcpy(get_dentfromdir_block, get_contents_vfatname_block, + mydata->clust_size * mydata->sect_size); + cur_position = (__u8 *)realdent - get_contents_vfatname_block; + *retdent = (dir_entry *) &get_dentfromdir_block[cur_position]; + } + + return 0; +} + +#endif + +/* + * Set the entry at index 'entry' in a FAT (16/32) table. + */ +static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value) +{ + __u32 bufnum, offset; + + switch (mydata->fatsize) { + case 32: + bufnum = entry / FAT32BUFSIZE; + offset = entry - bufnum * FAT32BUFSIZE; + break; + case 16: + bufnum = entry / FAT16BUFSIZE; + offset = entry - bufnum * FAT16BUFSIZE; + break; + default: + /* Unsupported FAT size */ + return -1; + } + + /* Read a new block of FAT entries into the cache. */ + if (bufnum != mydata->fatbufnum) { + int getsize = FATBUFBLOCKS; + __u8 *bufptr = mydata->fatbuf; + __u32 fatlength = mydata->fatlength; + __u32 startblock = bufnum * FATBUFBLOCKS; + + fatlength *= mydata->sect_size; + startblock += mydata->fat_sect; + + if (getsize > fatlength) + getsize = fatlength; + + if (mydata->fatbufnum != -1) { + if (flush_fat_buffer(mydata) < 0) + return -1; + } + + if (disk_read(startblock, getsize, bufptr) < 0) { + debug("Error reading FAT blocks\n"); + return -1; + } + mydata->fatbufnum = bufnum; + } + + /* Set the actual entry */ + switch (mydata->fatsize) { + case 32: + ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value); + break; + case 16: + ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value); + break; + default: + return -1; + } + + return 0; +} + +/* + * Determine the entry value at index 'entry' in a FAT (16/32) table + */ +static __u32 determine_fatent(fsdata *mydata, __u32 entry) +{ + __u32 next_fat, next_entry = entry + 1; + + while (1) { + next_fat = get_fatent_value(mydata, next_entry); + if (next_fat == 0) { + set_fatent_value(mydata, entry, next_entry); + break; + } + next_entry++; + } + debug("FAT%d: entry: %08x, entry_value: %04x\n", + mydata->fatsize, entry, next_entry); + + return next_entry; +} + +/* + * Write at most 'size' bytes from 'buffer' into the specified cluster. + * Return 0 on success, -1 otherwise. + */ +static int +set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, + unsigned long size) +{ + int idx = 0; + __u32 startsect; + + if (clustnum > 0) + startsect = mydata->data_begin + + clustnum * mydata->clust_size; + else + startsect = mydata->rootdir_sect; + + debug("clustnum: %d, startsect: %d\n", clustnum, startsect); + + if (disk_write(startsect, size / mydata->sect_size, buffer) < 0) { + debug("Error writing data\n"); + return -1; + } + + if (size % mydata->sect_size) { + __u8 tmpbuf[mydata->sect_size]; + + idx = size / mydata->sect_size; + buffer += idx * mydata->sect_size; + memcpy(tmpbuf, buffer, size % mydata->sect_size); + + if (disk_write(startsect + idx, 1, tmpbuf) < 0) { + debug("Error writing data\n"); + return -1; + } + + return 0; + } + + return 0; +} + +/* + * Find the first empty cluster + */ +static int find_empty_cluster(fsdata *mydata) +{ + __u32 fat_val, entry = 3; + + while (1) { + fat_val = get_fatent_value(mydata, entry); + if (fat_val == 0) + break; + entry++; + } + + return entry; +} + +/* + * Write directory entries in 'get_dentfromdir_block' to block device + */ +static void flush_dir_table(fsdata *mydata, dir_entry **dentptr) +{ + int dir_newclust = 0; + + if (set_cluster(mydata, dir_curclust, + get_dentfromdir_block, + mydata->clust_size * mydata->sect_size) != 0) { + printf("error: wrinting directory entry\n"); + return; + } + dir_newclust = find_empty_cluster(mydata); + set_fatent_value(mydata, dir_curclust, dir_newclust); + if (mydata->fatsize == 32) + set_fatent_value(mydata, dir_newclust, 0xffffff8); + else if (mydata->fatsize == 16) + set_fatent_value(mydata, dir_newclust, 0xfff8); + + dir_curclust = dir_newclust; + + if (flush_fat_buffer(mydata) < 0) + return; + + memset(get_dentfromdir_block, 0x00, + mydata->clust_size * mydata->sect_size); + + *dentptr = (dir_entry *) get_dentfromdir_block; +} + +/* + * Set empty cluster from 'entry' to the end of a file + */ +static int clear_fatent(fsdata *mydata, __u32 entry) +{ + __u32 fat_val; + + while (1) { + fat_val = get_fatent_value(mydata, entry); + if (fat_val != 0) + set_fatent_value(mydata, entry, 0); + else + break; + + if (fat_val == 0xfffffff || fat_val == 0xffff) + break; + + entry = fat_val; + } + + /* Flush fat buffer */ + if (flush_fat_buffer(mydata) < 0) + return -1; + + return 0; +} + +/* + * Write at most 'maxsize' bytes from 'buffer' into + * the file associated with 'dentptr' + * Return the number of bytes read or -1 on fatal errors. + */ +static int +set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, + unsigned long maxsize) +{ + unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0; + unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; + __u32 curclust = START(dentptr); + __u32 endclust = 0, newclust = 0; + unsigned long actsize; + + debug("Filesize: %ld bytes\n", filesize); + + if (maxsize > 0 && filesize > maxsize) + filesize = maxsize; + + debug("%ld bytes\n", filesize); + + actsize = bytesperclust; + endclust = curclust; + do { + /* search for consecutive clusters */ + while (actsize < filesize) { + newclust = determine_fatent(mydata, endclust); + + if ((newclust - 1) != endclust) + goto getit; + + if (CHECK_CLUST(newclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", newclust); + debug("Invalid FAT entry\n"); + return gotsize; + } + endclust = newclust; + actsize += bytesperclust; + } + /* actsize >= file size */ + actsize -= bytesperclust; + /* set remaining clusters */ + if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { + debug("error: writing cluster\n"); + return -1; + } + + /* set remaining bytes */ + gotsize += (int)actsize; + filesize -= actsize; + buffer += actsize; + actsize = filesize; + + if (set_cluster(mydata, endclust, buffer, (int)actsize) != 0) { + debug("error: writing cluster\n"); + return -1; + } + gotsize += actsize; + + /* Mark end of file in FAT */ + if (mydata->fatsize == 16) + newclust = 0xffff; + else if (mydata->fatsize == 32) + newclust = 0xfffffff; + set_fatent_value(mydata, endclust, newclust); + + return gotsize; +getit: + if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) { + debug("error: writing cluster\n"); + return -1; + } + gotsize += (int)actsize; + filesize -= actsize; + buffer += actsize; + + if (CHECK_CLUST(curclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", curclust); + debug("Invalid FAT entry\n"); + return gotsize; + } + actsize = bytesperclust; + curclust = endclust = newclust; + } while (1); +} + +/* + * Fill dir_entry + */ +static void fill_dentry(fsdata *mydata, dir_entry *dentptr, + const char *filename, __u32 start_cluster, __u32 size, __u8 attr) +{ + if (mydata->fatsize == 32) + dentptr->starthi = + cpu_to_le16((start_cluster & 0xffff0000) >> 16); + dentptr->start = cpu_to_le16(start_cluster & 0xffff); + dentptr->size = cpu_to_le32(size); + + dentptr->attr = attr; + + set_name(dentptr, filename); +} + +/* + * Check whether adding a file makes the file system to + * exceed the size of the block device + * Return -1 when overflow occurs, otherwise return 0 + */ +static int check_overflow(fsdata *mydata, __u32 clustnum, unsigned long size) +{ + __u32 startsect, sect_num; + + if (clustnum > 0) { + startsect = mydata->data_begin + + clustnum * mydata->clust_size; + } else { + startsect = mydata->rootdir_sect; + } + + sect_num = size / mydata->sect_size; + if (size % mydata->sect_size) + sect_num++; + + if (startsect + sect_num > cur_part_info.start + total_sector) + return -1; + + return 0; +} + +/* + * Check if adding several entries exceed one cluster boundary + */ +static int is_next_clust(fsdata *mydata, dir_entry *dentptr) +{ + int cur_position; + + cur_position = (__u8 *)dentptr - get_dentfromdir_block; + + if (cur_position >= mydata->clust_size * mydata->sect_size) + return 1; + else + return 0; +} + +static dir_entry *empty_dentptr; +/* + * Find a directory entry based on filename or start cluster number + * If the directory entry is not found, + * the new position for writing a directory entry will be returned + */ +static dir_entry *find_directory_entry(fsdata *mydata, int startsect, + char *filename, dir_entry *retdent, __u32 start) +{ + __u32 curclust = (startsect - mydata->data_begin) / mydata->clust_size; + + debug("get_dentfromdir: %s\n", filename); + + while (1) { + dir_entry *dentptr; + + int i; + + if (get_cluster(mydata, curclust, get_dentfromdir_block, + mydata->clust_size * mydata->sect_size) != 0) { + printf("Error: reading directory block\n"); + return NULL; + } + + dentptr = (dir_entry *)get_dentfromdir_block; + + dir_curclust = curclust; + + for (i = 0; i < DIRENTSPERCLUST; i++) { + char s_name[14], l_name[VFAT_MAXLEN_BYTES]; + + l_name[0] = '\0'; + if (dentptr->name[0] == DELETED_FLAG) { + dentptr++; + if (is_next_clust(mydata, dentptr)) + break; + continue; + } + if ((dentptr->attr & ATTR_VOLUME)) { +#ifdef CONFIG_SUPPORT_VFAT + if ((dentptr->attr & ATTR_VFAT) && + (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { + get_long_file_name(mydata, curclust, + get_dentfromdir_block, + &dentptr, l_name); + debug("vfatname: |%s|\n", l_name); + } else +#endif + { + /* Volume label or VFAT entry */ + dentptr++; + if (is_next_clust(mydata, dentptr)) + break; + continue; + } + } + if (dentptr->name[0] == 0) { + debug("Dentname == NULL - %d\n", i); + empty_dentptr = dentptr; + return NULL; + } + + get_name(dentptr, s_name); + + if (strcmp(filename, s_name) + && strcmp(filename, l_name)) { + debug("Mismatch: |%s|%s|\n", + s_name, l_name); + dentptr++; + if (is_next_clust(mydata, dentptr)) + break; + continue; + } + + memcpy(retdent, dentptr, sizeof(dir_entry)); + + debug("DentName: %s", s_name); + debug(", start: 0x%x", START(dentptr)); + debug(", size: 0x%x %s\n", + FAT2CPU32(dentptr->size), + (dentptr->attr & ATTR_DIR) ? + "(DIR)" : ""); + + return dentptr; + } + + curclust = get_fatent_value(mydata, dir_curclust); + if ((curclust >= 0xffffff8) || (curclust >= 0xfff8)) { + empty_dentptr = dentptr; + return NULL; + } + if (CHECK_CLUST(curclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", curclust); + debug("Invalid FAT entry\n"); + return NULL; + } + } + + return NULL; +} + +static int do_fat_write(const char *filename, void *buffer, + unsigned long size) +{ + dir_entry *dentptr, *retdent; + __u32 startsect; + __u32 start_cluster; + boot_sector bs; + volume_info volinfo; + fsdata datablock; + fsdata *mydata = &datablock; + int cursect; + int ret = -1, name_len; + char l_filename[VFAT_MAXLEN_BYTES]; + int write_size = size; + + dir_curclust = 0; + + if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) { + debug("error: reading boot sector\n"); + return -1; + } + + total_sector = bs.total_sect; + if (total_sector == 0) + total_sector = cur_part_info.size; + + if (mydata->fatsize == 32) + mydata->fatlength = bs.fat32_length; + else + mydata->fatlength = bs.fat_length; + + mydata->fat_sect = bs.reserved; + + cursect = mydata->rootdir_sect + = mydata->fat_sect + mydata->fatlength * bs.fats; + num_of_fats = bs.fats; + + mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0]; + mydata->clust_size = bs.cluster_size; + + if (mydata->fatsize == 32) { + mydata->data_begin = mydata->rootdir_sect - + (mydata->clust_size * 2); + } else { + int rootdir_size; + + rootdir_size = ((bs.dir_entries[1] * (int)256 + + bs.dir_entries[0]) * + sizeof(dir_entry)) / + mydata->sect_size; + mydata->data_begin = mydata->rootdir_sect + + rootdir_size - + (mydata->clust_size * 2); + } + + mydata->fatbufnum = -1; + mydata->fatbuf = malloc(FATBUFSIZE); + if (mydata->fatbuf == NULL) { + debug("Error: allocating memory\n"); + return -1; + } + + if (disk_read(cursect, + (mydata->fatsize == 32) ? + (mydata->clust_size) : + PREFETCH_BLOCKS, do_fat_read_at_block) < 0) { + debug("Error: reading rootdir block\n"); + goto exit; + } + dentptr = (dir_entry *) do_fat_read_at_block; + + name_len = strlen(filename); + if (name_len >= VFAT_MAXLEN_BYTES) + name_len = VFAT_MAXLEN_BYTES - 1; + + memcpy(l_filename, filename, name_len); + l_filename[name_len] = 0; /* terminate the string */ + downcase(l_filename); + + startsect = mydata->rootdir_sect; + retdent = find_directory_entry(mydata, startsect, + l_filename, dentptr, 0); + if (retdent) { + /* Update file size and start_cluster in a directory entry */ + retdent->size = cpu_to_le32(size); + start_cluster = FAT2CPU16(retdent->start); + if (mydata->fatsize == 32) + start_cluster |= + (FAT2CPU16(retdent->starthi) << 16); + + ret = check_overflow(mydata, start_cluster, size); + if (ret) { + printf("Error: %ld overflow\n", size); + goto exit; + } + + ret = clear_fatent(mydata, start_cluster); + if (ret) { + printf("Error: clearing FAT entries\n"); + goto exit; + } + + ret = set_contents(mydata, retdent, buffer, size); + if (ret < 0) { + printf("Error: writing contents\n"); + goto exit; + } + write_size = ret; + debug("attempt to write 0x%x bytes\n", write_size); + + /* Flush fat buffer */ + ret = flush_fat_buffer(mydata); + if (ret) { + printf("Error: flush fat buffer\n"); + goto exit; + } + + /* Write directory table to device */ + ret = set_cluster(mydata, dir_curclust, + get_dentfromdir_block, + mydata->clust_size * mydata->sect_size); + if (ret) { + printf("Error: writing directory entry\n"); + goto exit; + } + } else { + /* Set short name to set alias checksum field in dir_slot */ + set_name(empty_dentptr, filename); + fill_dir_slot(mydata, &empty_dentptr, filename); + + ret = start_cluster = find_empty_cluster(mydata); + if (ret < 0) { + printf("Error: finding empty cluster\n"); + goto exit; + } + + ret = check_overflow(mydata, start_cluster, size); + if (ret) { + printf("Error: %ld overflow\n", size); + goto exit; + } + + /* Set attribute as archieve for regular file */ + fill_dentry(mydata, empty_dentptr, filename, + start_cluster, size, 0x20); + + ret = set_contents(mydata, empty_dentptr, buffer, size); + if (ret < 0) { + printf("Error: writing contents\n"); + goto exit; + } + write_size = ret; + debug("attempt to write 0x%x bytes\n", write_size); + + /* Flush fat buffer */ + ret = flush_fat_buffer(mydata); + if (ret) { + printf("Error: flush fat buffer\n"); + goto exit; + } + + /* Write directory table to device */ + ret = set_cluster(mydata, dir_curclust, + get_dentfromdir_block, + mydata->clust_size * mydata->sect_size); + if (ret) { + printf("Error: writing directory entry\n"); + goto exit; + } + } + +exit: + free(mydata->fatbuf); + return ret < 0 ? ret : write_size; +} + +int file_fat_write(const char *filename, void *buffer, unsigned long maxsize) +{ + printf("writing %s\n", filename); + return do_fat_write(filename, buffer, maxsize); +} diff --git a/u-boot/fs/fat/file.c b/u-boot/fs/fat/file.c index 59c5d37..59c5d37 100644..100755 --- a/u-boot/fs/fat/file.c +++ b/u-boot/fs/fat/file.c diff --git a/u-boot/fs/fs.c b/u-boot/fs/fs.c new file mode 100755 index 0000000..ce62faa --- /dev/null +++ b/u-boot/fs/fs.c @@ -0,0 +1,334 @@ +/* + * 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; +} + +#define CMD_RET_USAGE cmd_usage(cmdtp) + +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]; + unsigned long time; + + 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; + + time = get_timer(0); + len_read = fs_read(filename, addr, pos, bytes); + time = get_timer(time); + if (len_read <= 0) + return 1; + + printf("%d bytes read in %lu ms", len_read, time); + if (time > 0) { + puts(" ("); + print_size(len_read / time * 1000, "/s"); + puts(")"); + } + puts("\n"); + + 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; +} diff --git a/u-boot/include/common.h b/u-boot/include/common.h index d8c912d..d5ec64d 100644 --- a/u-boot/include/common.h +++ b/u-boot/include/common.h @@ -750,4 +750,91 @@ int cpu_release(int nr, int argc, char * const argv[]); #define ALIGN(x,a) __ALIGN_MASK((x),(typeof(x))(a)-1) #define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) +/* + * ARCH_DMA_MINALIGN is defined in asm/cache.h for each architecture. It + * is used to align DMA buffers. + */ +#ifndef __ASSEMBLY__ +// #include <asm/cache.h> +#define ARCH_DMA_MINALIGN 64 // for ARM +#endif + +/* + * The ALLOC_CACHE_ALIGN_BUFFER macro is used to allocate a buffer on the + * stack that meets the minimum architecture alignment requirements for DMA. + * Such a buffer is useful for DMA operations where flushing and invalidating + * the cache before and after a read and/or write operation is required for + * correct operations. + * + * When called the macro creates an array on the stack that is sized such + * that: + * + * 1) The beginning of the array can be advanced enough to be aligned. + * + * 2) The size of the aligned portion of the array is a multiple of the minimum + * architecture alignment required for DMA. + * + * 3) The aligned portion contains enough space for the original number of + * elements requested. + * + * The macro then creates a pointer to the aligned portion of this array and + * assigns to the pointer the address of the first element in the aligned + * portion of the array. + * + * Calling the macro as: + * + * ALLOC_CACHE_ALIGN_BUFFER(uint32_t, buffer, 1024); + * + * Will result in something similar to saying: + * + * uint32_t buffer[1024]; + * + * The following differences exist: + * + * 1) The resulting buffer is guaranteed to be aligned to the value of + * ARCH_DMA_MINALIGN. + * + * 2) The buffer variable created by the macro is a pointer to the specified + * type, and NOT an array of the specified type. This can be very important + * if you want the address of the buffer, which you probably do, to pass it + * to the DMA hardware. The value of &buffer is different in the two cases. + * In the macro case it will be the address of the pointer, not the address + * of the space reserved for the buffer. However, in the second case it + * would be the address of the buffer. So if you are replacing hard coded + * stack buffers with this macro you need to make sure you remove the & from + * the locations where you are taking the address of the buffer. + * + * Note that the size parameter is the number of array elements to allocate, + * not the number of bytes. + * + * This macro can not be used outside of function scope, or for the creation + * of a function scoped static buffer. It can not be used to create a cache + * line aligned global buffer. + */ +#define ALLOC_ALIGN_BUFFER(type, name, size, align) \ +char __##name[ROUND(size * sizeof(type), align) + (align - 1)]; \ +\ +type *name = (type *) ALIGN((uintptr_t)__##name, align) +#define ALLOC_CACHE_ALIGN_BUFFER(type, name, size) \ +ALLOC_ALIGN_BUFFER(type, name, size, ARCH_DMA_MINALIGN) + +/* + * DEFINE_CACHE_ALIGN_BUFFER() is similar to ALLOC_CACHE_ALIGN_BUFFER, but it's + * purpose is to allow allocating aligned buffers outside of function scope. + * Usage of this macro shall be avoided or used with extreme care! + */ +#define DEFINE_ALIGN_BUFFER(type, name, size, align) \ +static char __##name[roundup(size * sizeof(type), align)] \ +__attribute__((aligned(align))); \ +\ +static type *name = (type *)__##name +#define DEFINE_CACHE_ALIGN_BUFFER(type, name, size) \ +DEFINE_ALIGN_BUFFER(type, name, size, ARCH_DMA_MINALIGN) + +/* Pull in stuff for the build system */ +#ifdef DO_DEPS_ONLY +# include <environment.h> +#endif + + #endif /* __COMMON_H_ */ diff --git a/u-boot/include/config_defaults.h b/u-boot/include/config_defaults.h index abdf3be..c822bbd 100644 --- a/u-boot/include/config_defaults.h +++ b/u-boot/include/config_defaults.h @@ -17,5 +17,6 @@ #define CONFIG_GZIP 1 #define CONFIG_ZLIB 1 +#define CONFIG_PARTITIONS 1 #endif diff --git a/u-boot/include/configs/omap3_beagle.h b/u-boot/include/configs/omap3_beagle.h index 519da79..9866d49 100644 --- a/u-boot/include/configs/omap3_beagle.h +++ b/u-boot/include/configs/omap3_beagle.h @@ -322,7 +322,7 @@ #define CONFIG_ENV_OFFSET boot_flash_off #define CONFIG_ENV_ADDR SMNAND_ENV_OFFSET -#define CONFIG_START_WITH_DEFAULT_ENVIRONMENT 1 +//#define CONFIG_START_WITH_DEFAULT_ENVIRONMENT 1 #ifndef __ASSEMBLY__ extern unsigned int boot_flash_base; diff --git a/u-boot/include/configs/omap3_gta04.h b/u-boot/include/configs/omap3_gta04.h index c6269af..69eb055 100644 --- a/u-boot/include/configs/omap3_gta04.h +++ b/u-boot/include/configs/omap3_gta04.h @@ -31,15 +31,13 @@ #define CONFIG_CMD_UNZIP 1 /* for reducing size of splash image */ -// #undef CONFIG_CMD_JFFS2 -// #define CONFIG_CMD_JFFS2 1 /* to access the rootfs in NAND flash */ -// FIXME: needs to add configs for the partitions so that JFFS2 runs in the correct NAND partition - -#define CONFIG_CMD_UBIFS 1 - -#define CONFIG_CMD_EXT2 1 -#define CONFIG_CMD_EXT4 1 +// #define CONFIG_CMD_UBIFS 1 // broken +#define CONFIG_FS_FAT 1 +#define CONFIG_FS_EXT2 1 +#define CONFIG_FS_EXT4 1 +#define CONFIG_CMD_FS 1 +#define CONFIG_CMD_EXT4 1 #undef CONFIG_SYS_PROMPT #define CONFIG_SYS_PROMPT "GTA04 # " diff --git a/u-boot/include/fat.h b/u-boot/include/fat.h index afb2116..706cd7a 100644..100755 --- a/u-boot/include/fat.h +++ b/u-boot/include/fat.h @@ -33,22 +33,15 @@ /* Maximum Long File Name length supported here is 128 UTF-16 code units */ #define VFAT_MAXLEN_BYTES 256 /* Maximum LFN buffer in bytes */ #define VFAT_MAXSEQ 9 /* Up to 9 of 13 2-byte UTF-16 entries */ -#define LINEAR_PREFETCH_SIZE (SECTOR_SIZE*2) /* Prefetch buffer size */ - -#define SECTOR_SIZE FS_BLOCK_SIZE - -#define FS_BLOCK_SIZE 512 - -#if FS_BLOCK_SIZE != SECTOR_SIZE -#error FS_BLOCK_SIZE != SECTOR_SIZE - This code needs to be fixed! -#endif +#define PREFETCH_BLOCKS 2 #define MAX_CLUSTSIZE 65536 -#define DIRENTSPERBLOCK (FS_BLOCK_SIZE/sizeof(dir_entry)) -#define DIRENTSPERCLUST ((mydata->clust_size*SECTOR_SIZE)/sizeof(dir_entry)) +#define DIRENTSPERBLOCK (mydata->sect_size / sizeof(dir_entry)) +#define DIRENTSPERCLUST ((mydata->clust_size * mydata->sect_size) / \ + sizeof(dir_entry)) #define FATBUFBLOCKS 6 -#define FATBUFSIZE (FS_BLOCK_SIZE*FATBUFBLOCKS) +#define FATBUFSIZE (mydata->sect_size * FATBUFBLOCKS) #define FAT12BUFSIZE ((FATBUFSIZE*2)/3) #define FAT16BUFSIZE (FATBUFSIZE/2) #define FAT32BUFSIZE (FATBUFSIZE/4) @@ -106,6 +99,8 @@ #endif #define TOLOWER(c) if((c) >= 'A' && (c) <= 'Z'){(c)+=('a' - 'A');} +#define TOUPPER(c) if ((c) >= 'a' && (c) <= 'z') \ + (c) -= ('a' - 'A'); #define START(dent) (FAT2CPU16((dent)->start) \ + (mydata->fatsize != 32 ? 0 : \ (FAT2CPU16((dent)->starthi) << 16))) @@ -181,13 +176,14 @@ typedef struct dir_slot { * (see FAT32 accesses) */ typedef struct { - __u8 fatbuf[FATBUFSIZE]; /* Current FAT buffer */ + __u8 *fatbuf; /* Current FAT buffer */ int fatsize; /* Size of FAT in bits */ - __u16 fatlength; /* Length of FAT in sectors */ + __u32 fatlength; /* Length of FAT in sectors */ __u16 fat_sect; /* Starting sector of the FAT */ - __u16 rootdir_sect; /* Start sector of root directory */ + __u32 rootdir_sect; /* Start sector of root directory */ + __u16 sect_size; /* Size of sectors in bytes */ __u16 clust_size; /* Size of clusters in sectors */ - short data_begin; /* The sector of the first cluster, can be negative */ + int data_begin; /* The sector of the first cluster, can be negative */ int fatbufnum; /* Used by get_fatent, init to -1 */ } fsdata; @@ -212,8 +208,12 @@ file_read_func file_fat_read; int file_cd(const char *path); int file_fat_detectfs(void); int file_fat_ls(const char *dir); +long file_fat_read_at(const char *filename, unsigned long pos, void *buffer, + unsigned long maxsize); long file_fat_read(const char *filename, void *buffer, unsigned long maxsize); const char *file_getfsname(int idx); +int fat_set_blk_dev(block_dev_desc_t *rbdd, disk_partition_t *info); int fat_register_device(block_dev_desc_t *dev_desc, int part_no); +int file_fat_write(const char *filename, void *buffer, unsigned long maxsize); #endif /* _FAT_H_ */ diff --git a/u-boot/include/fs.h b/u-boot/include/fs.h new file mode 100755 index 0000000..4f30a38 --- /dev/null +++ b/u-boot/include/fs.h @@ -0,0 +1,65 @@ +/* + * 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/>. + */ +#ifndef _FS_H +#define _FS_H + +#include <common.h> + +#define FS_TYPE_ANY 0 +#define FS_TYPE_FAT 1 +#define FS_TYPE_EXT 2 + +/* + * Tell the fs layer which block device an partition to use for future + * commands. This also internally identifies the filesystem that is present + * within the partition. The identification process may be limited to a + * specific filesystem type by passing FS_* in the fstype parameter. + * + * Returns 0 on success. + * Returns non-zero if there is an error accessing the disk or partition, or + * no known filesystem type could be recognized on it. + */ +int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype); + +/* + * Print the list of files on the partition previously set by fs_set_blk_dev(), + * in directory "dirname". + * + * Returns 0 on success. Returns non-zero on error. + */ +int fs_ls(const char *dirname); + +/* + * Read file "filename" from the partition previously set by fs_set_blk_dev(), + * to address "addr", starting at byte offset "offset", and reading "len" + * bytes. "offset" may be 0 to read from the start of the file. "len" may be + * 0 to read the entire file. Note that not all filesystem types support + * either/both offset!=0 or len!=0. + * + * Returns number of bytes read on success. Returns <= 0 on error. + */ +int fs_read(const char *filename, ulong addr, int offset, int len); + +/* + * Common implementation for various filesystem commands, optionally limited + * to a specific filesystem type via the fstype parameter. + */ +int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + int fstype, int cmdline_base); +int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + int fstype); + +#endif /* _FS_H */ diff --git a/u-boot/include/part.h b/u-boot/include/part.h index 3cdae02..27ea283 100644..100755 --- a/u-boot/include/part.h +++ b/u-boot/include/part.h @@ -36,7 +36,7 @@ typedef struct block_dev_desc { #ifdef CONFIG_LBA48 unsigned char lba48; /* device can use 48bit addr (ATA/ATAPI v7) */ #endif - lbaint_t lba; /* number of blocks */ + lbaint_t lba; /* number of blocks */ unsigned long blksz; /* block size */ char vendor [40+1]; /* IDE model, SCSI Vendor */ char product[20+1]; /* IDE Serial no, SCSI product */ @@ -49,6 +49,9 @@ typedef struct block_dev_desc { unsigned long start, lbaint_t blkcnt, const void *buffer); + unsigned long (*block_erase)(int dev, + unsigned long start, + lbaint_t blkcnt); void *priv; /* driver private struct pointer */ }block_dev_desc_t; @@ -90,10 +93,15 @@ typedef struct disk_partition { ulong blksz; /* block size in bytes */ uchar name[32]; /* partition name */ uchar type[32]; /* string type description */ + int bootable; /* Active/Bootable flag is set */ +#ifdef CONFIG_PARTITION_UUIDS + char uuid[37]; /* filesystem UUID as string, if exists */ +#endif } disk_partition_t; /* Misc _get_dev functions */ -block_dev_desc_t* get_dev(char* ifname, int dev); +#ifdef CONFIG_PARTITIONS +block_dev_desc_t *get_dev(const char *ifname, int dev); block_dev_desc_t* ide_get_dev(int dev); block_dev_desc_t* sata_get_dev(int dev); block_dev_desc_t* scsi_get_dev(int dev); @@ -107,7 +115,37 @@ int get_partition_info (block_dev_desc_t * dev_desc, int part, disk_partition_t void print_part (block_dev_desc_t *dev_desc); void init_part (block_dev_desc_t *dev_desc); void dev_print(block_dev_desc_t *dev_desc); - +int get_device(const char *ifname, const char *dev_str, + block_dev_desc_t **dev_desc); +int get_device_and_partition(const char *ifname, const char *dev_part_str, + block_dev_desc_t **dev_desc, + disk_partition_t *info, int allow_whole_dev); +#else +static inline block_dev_desc_t *get_dev(const char *ifname, int dev) +{ return NULL; } +static inline block_dev_desc_t* ide_get_dev(int dev) { return NULL; } +static inline block_dev_desc_t* sata_get_dev(int dev) { return NULL; } +static inline block_dev_desc_t* scsi_get_dev(int dev) { return NULL; } +static inline block_dev_desc_t* usb_stor_get_dev(int dev) { return NULL; } +static inline block_dev_desc_t* mmc_get_dev(int dev) { return NULL; } +static inline block_dev_desc_t* systemace_get_dev(int dev) { return NULL; } +static inline block_dev_desc_t* mg_disk_get_dev(int dev) { return NULL; } + +static inline int get_partition_info (block_dev_desc_t * dev_desc, int part, + disk_partition_t *info) { return -1; } +static inline void print_part (block_dev_desc_t *dev_desc) {} +static inline void init_part (block_dev_desc_t *dev_desc) {} +static inline void dev_print(block_dev_desc_t *dev_desc) {} +static inline int get_device(const char *ifname, const char *dev_str, + block_dev_desc_t **dev_desc) +{ return -1; } +static inline int get_device_and_partition(const char *ifname, + const char *dev_part_str, + block_dev_desc_t **dev_desc, + disk_partition_t *info, + int allow_whole_dev) +{ *dev_desc = NULL; return -1; } +#endif #ifdef CONFIG_MAC_PARTITION /* disk/part_mac.c */ |