summaryrefslogtreecommitdiff
path: root/drivers/io
diff options
context:
space:
mode:
authorHaojian Zhuang <haojian.zhuang@linaro.org>2016-07-28 10:15:32 +0800
committerHaojian Zhuang <haojian.zhuang@linaro.org>2016-08-04 09:53:29 +0800
commit9d063aa2e86bc3dc8abf866614607b973358cebb (patch)
tree208e2e8902c7c250d0612ad0e58ba439c317a058 /drivers/io
parent3d99b17f60142ef96d39759132d4448e138b6c4e (diff)
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>
Diffstat (limited to 'drivers/io')
-rw-r--r--drivers/io/io_block.c64
1 files changed, 50 insertions, 14 deletions
diff --git a/drivers/io/io_block.c b/drivers/io/io_block.c
index 198b723b..4ec59bc7 100644
--- a/drivers/io/io_block.c
+++ b/drivers/io/io_block.c
@@ -198,6 +198,7 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
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 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
(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 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
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 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
}
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 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
}
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 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
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 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer,
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 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer,
(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 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer,
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 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer,
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 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer,
* 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);