summaryrefslogtreecommitdiff
path: root/fs/fat
diff options
context:
space:
mode:
authorBenoît Thébaudeau <benoit@wsystem.com>2015-09-28 15:45:28 +0200
committerTom Rini <trini@konsulko.com>2015-10-11 17:12:07 -0400
commit8133f43d1cd4f0df0a6b4c2aa99a47753fda20d6 (patch)
treef997889253a26ce2d463fee41d4e44fb9329ec1c /fs/fat
parent689821fd766fb4855deafd04eaffeef5b2c6579e (diff)
downloadu-boot-8133f43d1cd4f0df0a6b4c2aa99a47753fda20d6.tar.gz
u-boot-8133f43d1cd4f0df0a6b4c2aa99a47753fda20d6.tar.bz2
u-boot-8133f43d1cd4f0df0a6b4c2aa99a47753fda20d6.zip
fs/fat/fat_write: Fix buffer alignments
set_cluster() was using a temporary buffer without enforcing its alignment for DMA and cache. Moreover, it did not check the alignment of the passed buffer, which can come directly from applicative code or from the user. This could cause random data corruption, which has been observed on i.MX25 writing to an SD card. Fix this by only passing ARCH_DMA_MINALIGN-aligned buffers to disk_write(), which requires the introduction of a buffer bouncing mechanism for the misaligned buffers passed to set_cluster(). By the way, improve the handling of the corresponding return values from disk_write(): - print them with debug() in case of error, - consider that there is an error is disk_write() returns a smaller block count than the requested one, not only if its return value is negative. After this change, set_cluster() and get_cluster() are almost symmetrical. Signed-off-by: Benoît Thébaudeau <benoit@wsystem.com>
Diffstat (limited to 'fs/fat')
-rw-r--r--fs/fat/fat_write.c48
1 files changed, 34 insertions, 14 deletions
diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
index adb6940dff..d0d9df7170 100644
--- a/fs/fat/fat_write.c
+++ b/fs/fat/fat_write.c
@@ -555,8 +555,9 @@ static int
set_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 +
@@ -566,26 +567,45 @@ set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer,
debug("clustnum: %d, startsect: %d\n", clustnum, startsect);
- if ((size / mydata->sect_size) > 0) {
- if (disk_write(startsect, size / mydata->sect_size, buffer) < 0) {
- debug("Error writing data\n");
+ 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) {
+ memcpy(tmpbuf, buffer, mydata->sect_size);
+ ret = disk_write(startsect++, 1, tmpbuf);
+ if (ret != 1) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
+
+ buffer += mydata->sect_size;
+ size -= mydata->sect_size;
+ }
+ } else if (size >= mydata->sect_size) {
+ idx = size / mydata->sect_size;
+ ret = disk_write(startsect, idx, buffer);
+ if (ret != idx) {
+ debug("Error writing data (got %d)\n", ret);
return -1;
}
- }
- if (size % mydata->sect_size) {
- __u8 tmpbuf[mydata->sect_size];
+ startsect += idx;
+ idx *= mydata->sect_size;
+ buffer += idx;
+ size -= idx;
+ }
- idx = size / mydata->sect_size;
- buffer += idx * mydata->sect_size;
- memcpy(tmpbuf, buffer, size % mydata->sect_size);
+ if (size) {
+ ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
- if (disk_write(startsect + idx, 1, tmpbuf) < 0) {
- debug("Error writing data\n");
+ memcpy(tmpbuf, buffer, size);
+ ret = disk_write(startsect, 1, tmpbuf);
+ if (ret != 1) {
+ debug("Error writing data (got %d)\n", ret);
return -1;
}
-
- return 0;
}
return 0;