提交 ed78352a 编写于 作者: A Alexander Popov 提交者: John Snow

ide: Fix incorrect handling of some PRDTs in ide_dma_cb()

The commit a718978e from July 2015 introduced the assertion which
implies that the size of successful DMA transfers handled in ide_dma_cb()
should be multiple of 512 (the size of a sector). But guest systems can
initiate DMA transfers that don't fit this requirement.

For fixing that let's check the number of bytes prepared for the transfer
by the prepare_buf() handler. The code in ide_dma_cb() must behave
according to the Programming Interface for Bus Master IDE Controller
(Revision 1.0 5/16/94):
1. If PRDs specified a smaller size than the IDE transfer
   size, then the Interrupt and Active bits in the Controller
   status register are not set (Error Condition).
2. If the size of the physical memory regions was equal to
   the IDE device transfer size, the Interrupt bit in the
   Controller status register is set to 1, Active bit is set to 0.
3. If PRDs specified a larger size than the IDE transfer size,
   the Interrupt and Active bits in the Controller status register
   are both set to 1.
Signed-off-by: NAlexander Popov <alex.popov@linux.com>
Reviewed-by: NKevin Wolf <kwolf@redhat.com>
Message-id: 20191223175117.508990-2-alex.popov@linux.com
Signed-off-by: NJohn Snow <jsnow@redhat.com>
上级 105b07f1
......@@ -849,6 +849,7 @@ static void ide_dma_cb(void *opaque, int ret)
int64_t sector_num;
uint64_t offset;
bool stay_active = false;
int32_t prep_size = 0;
if (ret == -EINVAL) {
ide_dma_error(s);
......@@ -863,13 +864,15 @@ static void ide_dma_cb(void *opaque, int ret)
}
}
n = s->io_buffer_size >> 9;
if (n > s->nsector) {
/* The PRDs were longer than needed for this request. Shorten them so
* we don't get a negative remainder. The Active bit must remain set
* after the request completes. */
if (s->io_buffer_size > s->nsector * 512) {
/*
* The PRDs were longer than needed for this request.
* The Active bit must remain set after the request completes.
*/
n = s->nsector;
stay_active = true;
} else {
n = s->io_buffer_size >> 9;
}
sector_num = ide_get_sector(s);
......@@ -892,9 +895,20 @@ static void ide_dma_cb(void *opaque, int ret)
n = s->nsector;
s->io_buffer_index = 0;
s->io_buffer_size = n * 512;
if (s->bus->dma->ops->prepare_buf(s->bus->dma, s->io_buffer_size) < 512) {
/* The PRDs were too short. Reset the Active bit, but don't raise an
* interrupt. */
prep_size = s->bus->dma->ops->prepare_buf(s->bus->dma, s->io_buffer_size);
/* prepare_buf() must succeed and respect the limit */
assert(prep_size >= 0 && prep_size <= n * 512);
/*
* Now prep_size stores the number of bytes in the sglist, and
* s->io_buffer_size stores the number of bytes described by the PRDs.
*/
if (prep_size < n * 512) {
/*
* The PRDs are too short for this request. Error condition!
* Reset the Active bit and don't raise the interrupt.
*/
s->status = READY_STAT | SEEK_STAT;
dma_buf_commit(s, 0);
goto eot;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册