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>
This commit is contained in:
Haojian Zhuang 2016-07-28 10:15:32 +08:00
parent 3d99b17f60
commit 9d063aa2e8
1 changed files with 50 additions and 14 deletions

View File

@ -198,6 +198,7 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
io_block_ops_t *ops; io_block_ops_t *ops;
size_t aligned_length, skip, count, left, padding, block_size; size_t aligned_length, skip, count, left, padding, block_size;
int lba; int lba;
int buffer_not_aligned;
assert(entity->info != (uintptr_t)NULL); assert(entity->info != (uintptr_t)NULL);
cur = (block_dev_state_t *)entity->info; 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) && (length > 0) &&
(ops->read != 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; skip = cur->file_pos % block_size;
aligned_length = ((skip + length) + (block_size - 1)) & aligned_length = ((skip + length) + (block_size - 1)) &
~(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 { do {
lba = (cur->file_pos + cur->base) / block_size; lba = (cur->file_pos + cur->base) / block_size;
if (left >= buf->length) { 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 * The beginning address (file_pos) isn't
* aligned with block size, we need to use * 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); assert(count == buf->length);
cur->file_pos += count - skip; cur->file_pos += count - skip;
if (skip) { if (skip || buffer_not_aligned) {
/* /*
* Since it's not aligned with block size, * Since there's not aligned block size caused
* block buffer is used to store data. * by skip or not aligned buffer, block buffer
* is used to store data.
*/ */
memcpy((void *)buffer, memcpy((void *)buffer,
(void *)(buf->offset + skip), (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); left = left - (count - skip);
} else { } else {
if (skip || padding) { if (skip || padding || buffer_not_aligned) {
/* /*
* The beginning address (file_pos) isn't * The beginning address (file_pos) isn't
* aligned with block size, we have to read * aligned with block size, we have to read
* full block by block buffer instead. * full block by block buffer instead.
* The size isn't aligned with block size. * The size isn't aligned with block size.
* Use block buffer to avoid overflow. * 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); count = ops->read(lba, buf->offset, left);
} else } else
@ -256,10 +277,10 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
assert(count == left); assert(count == left);
left = left - (skip + padding); left = left - (skip + padding);
cur->file_pos += left; cur->file_pos += left;
if (skip || padding) { if (skip || padding || buffer_not_aligned) {
/* /*
* Since it's not aligned with block size, * Since there's not aligned block size or
* block buffer is used to store data. * buffer, block buffer is used to store data.
*/ */
memcpy((void *)buffer, memcpy((void *)buffer,
(void *)(buf->offset + skip), (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; io_block_ops_t *ops;
size_t aligned_length, skip, count, left, padding, block_size; size_t aligned_length, skip, count, left, padding, block_size;
int lba; int lba;
int buffer_not_aligned;
assert(entity->info != (uintptr_t)NULL); assert(entity->info != (uintptr_t)NULL);
cur = (block_dev_state_t *)entity->info; 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->read != 0) &&
(ops->write != 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; skip = cur->file_pos % block_size;
aligned_length = ((skip + length) + (block_size - 1)) & aligned_length = ((skip + length) + (block_size - 1)) &
~(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; lba = (cur->file_pos + cur->base) / block_size;
if (left >= buf->length) { if (left >= buf->length) {
/* Since left is larger, it's impossible to padding. */ /* Since left is larger, it's impossible to padding. */
if (skip) { if (skip || buffer_not_aligned) {
/* /*
* The beginning address (file_pos) isn't * The beginning address (file_pos) isn't
* aligned with block size, we need to use * aligned with block size or buffer isn't
* block buffer to write block. Since block * aligned, we need to use block buffer to
* device is always relied on DMA operation. * write block.
*/ */
count = ops->read(lba, buf->offset, count = ops->read(lba, buf->offset,
buf->length); buf->length);
@ -324,7 +357,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer,
cur->file_pos += count - skip; cur->file_pos += count - skip;
left = left - (count - skip); left = left - (count - skip);
} else { } else {
if (skip || padding) { if (skip || padding || buffer_not_aligned) {
/* /*
* The beginning address (file_pos) isn't * The beginning address (file_pos) isn't
* aligned with block size, we need to avoid * 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. * skipping the beginning is the only way.
* The size isn't aligned with block size. * The size isn't aligned with block size.
* Use block buffer to avoid overflow. * 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); count = ops->read(lba, buf->offset, left);
assert(count == left); assert(count == left);