Merge pull request #676 from hzhuang1/fix_io_unaligned

io: block: fix unaligned buffer
diff --git a/drivers/io/io_block.c b/drivers/io/io_block.c
index 198b723..4ec59bc 100644
--- a/drivers/io/io_block.c
+++ b/drivers/io/io_block.c
@@ -198,6 +198,7 @@
 	io_block_ops_t *ops;
 	size_t aligned_length, skip, count, left, padding, block_size;
 	int lba;
+	int buffer_not_aligned;
 
 	assert(entity->info != (uintptr_t)NULL);
 	cur = (block_dev_state_t *)entity->info;
@@ -208,6 +209,17 @@
 	       (length > 0) &&
 	       (ops->read != 0));
 
+	if ((buffer & (block_size - 1)) != 0) {
+		/*
+		 * buffer isn't aligned with block size.
+		 * Block device always relies on DMA operation.
+		 * It's better to make the buffer as block size aligned.
+		 */
+		buffer_not_aligned = 1;
+	} else {
+		buffer_not_aligned = 0;
+	}
+
 	skip = cur->file_pos % block_size;
 	aligned_length = ((skip + length) + (block_size - 1)) &
 			 ~(block_size - 1);
@@ -216,8 +228,13 @@
 	do {
 		lba = (cur->file_pos + cur->base) / block_size;
 		if (left >= buf->length) {
-			/* Since left is larger, it's impossible to padding. */
-			if (skip) {
+			/*
+			 * Since left is larger, it's impossible to padding.
+			 *
+			 * If buffer isn't aligned, we need to use aligned
+			 * buffer instead.
+			 */
+			if (skip || buffer_not_aligned) {
 				/*
 				 * The beginning address (file_pos) isn't
 				 * aligned with block size, we need to use
@@ -231,10 +248,11 @@
 			}
 			assert(count == buf->length);
 			cur->file_pos += count - skip;
-			if (skip) {
+			if (skip || buffer_not_aligned) {
 				/*
-				 * Since it's not aligned with block size,
-				 * block buffer is used to store data.
+				 * Since there's not aligned block size caused
+				 * by skip or not aligned buffer, block buffer
+				 * is used to store data.
 				 */
 				memcpy((void *)buffer,
 				       (void *)(buf->offset + skip),
@@ -242,13 +260,16 @@
 			}
 			left = left - (count - skip);
 		} else {
-			if (skip || padding) {
+			if (skip || padding || buffer_not_aligned) {
 				/*
 				 * The beginning address (file_pos) isn't
 				 * aligned with block size, we have to read
 				 * full block by block buffer instead.
 				 * The size isn't aligned with block size.
 				 * Use block buffer to avoid overflow.
+				 *
+				 * If buffer isn't aligned, use block buffer
+				 * to avoid DMA error.
 				 */
 				count = ops->read(lba, buf->offset, left);
 			} else
@@ -256,10 +277,10 @@
 			assert(count == left);
 			left = left - (skip + padding);
 			cur->file_pos += left;
-			if (skip || padding) {
+			if (skip || padding || buffer_not_aligned) {
 				/*
-				 * Since it's not aligned with block size,
-				 * block buffer is used to store data.
+				 * Since there's not aligned block size or
+				 * buffer, block buffer is used to store data.
 				 */
 				memcpy((void *)buffer,
 				       (void *)(buf->offset + skip),
@@ -283,6 +304,7 @@
 	io_block_ops_t *ops;
 	size_t aligned_length, skip, count, left, padding, block_size;
 	int lba;
+	int buffer_not_aligned;
 
 	assert(entity->info != (uintptr_t)NULL);
 	cur = (block_dev_state_t *)entity->info;
@@ -294,6 +316,17 @@
 	       (ops->read != 0) &&
 	       (ops->write != 0));
 
+	if ((buffer & (block_size - 1)) != 0) {
+		/*
+		 * buffer isn't aligned with block size.
+		 * Block device always relies on DMA operation.
+		 * It's better to make the buffer as block size aligned.
+		 */
+		buffer_not_aligned = 1;
+	} else {
+		buffer_not_aligned = 0;
+	}
+
 	skip = cur->file_pos % block_size;
 	aligned_length = ((skip + length) + (block_size - 1)) &
 			 ~(block_size - 1);
@@ -303,12 +336,12 @@
 		lba = (cur->file_pos + cur->base) / block_size;
 		if (left >= buf->length) {
 			/* Since left is larger, it's impossible to padding. */
-			if (skip) {
+			if (skip || buffer_not_aligned) {
 				/*
 				 * The beginning address (file_pos) isn't
-				 * aligned with block size, we need to use
-				 * block buffer to write block. Since block
-				 * device is always relied on DMA operation.
+				 * aligned with block size or buffer isn't
+				 * aligned, we need to use block buffer to
+				 * write block.
 				 */
 				count = ops->read(lba, buf->offset,
 						  buf->length);
@@ -324,7 +357,7 @@
 			cur->file_pos += count - skip;
 			left = left - (count - skip);
 		} else {
-			if (skip || padding) {
+			if (skip || padding || buffer_not_aligned) {
 				/*
 				 * The beginning address (file_pos) isn't
 				 * aligned with block size, we need to avoid
@@ -332,6 +365,9 @@
 				 * skipping the beginning is the only way.
 				 * The size isn't aligned with block size.
 				 * Use block buffer to avoid overflow.
+				 *
+				 * If buffer isn't aligned, use block buffer
+				 * to avoid DMA error.
 				 */
 				count = ops->read(lba, buf->offset, left);
 				assert(count == left);