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;
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);