io: block: fix unaligned buffer

If buffer address parameter isn't aligned, it may cause
DMA issue in block device driver, as eMMC. Now check the buffer
address. If it's not aligned, use temporary buffer in io block
driver instead.

Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
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);