dm: enforce data size when accessing PCI BARs

Always enforce data size when using BAR access functions.

Currently, the size serves as a hint to the BAR access functions and
these functions are expected to behave accordingly. Some of the access
functions, e.g. virtio ones, don't always truncate the data but expect
the caller to take care of the data size. This causes problems with
OVMF's virtio drivers during I/O instruction emulation because RAX can
contain junk bits that shouldn't be written to the device.

v1 -> v2:
- improve readability

Tracked-On: #1935
Signed-off-by: Peter Fang <peter.fang@intel.com>
Acked-by: Anthony Xu <anthony.xu@intel.com>
This commit is contained in:
Peter Fang 2018-11-28 22:34:46 -08:00 committed by wenlingz
parent f5a66e8e20
commit e835f5f5d2
1 changed files with 29 additions and 10 deletions

View File

@ -355,6 +355,17 @@ pci_msix_pba_bar(struct pci_vdev *dev)
return -1;
}
static inline uint64_t
bar_value(int size, uint64_t val)
{
uint64_t mask;
assert(size == 1 || size == 2 || size == 4 || size == 8);
mask = (size < 8 ? 1UL << (size * 8) : 0UL) - 1;
return val & mask;
}
static int
pci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
@ -369,12 +380,13 @@ pci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
port >= pdi->bar[i].addr &&
port + bytes <= pdi->bar[i].addr + pdi->bar[i].size) {
offset = port - pdi->bar[i].addr;
if (in)
if (in) {
*eax = (*ops->vdev_barread)(ctx, vcpu, pdi, i,
offset, bytes);
else
offset, bytes);
*eax = bar_value(bytes, *eax);
} else
(*ops->vdev_barwrite)(ctx, vcpu, pdi, i, offset,
bytes, *eax);
bytes, bar_value(bytes, *eax));
return 0;
}
}
@ -406,17 +418,24 @@ pci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr,
4, *val >> 32);
} else {
(*ops->vdev_barwrite)(ctx, vcpu, pdi, bidx, offset,
size, *val);
size, bar_value(size, *val));
}
} else {
if (size == 8) {
*val = (*ops->vdev_barread)(ctx, vcpu, pdi, bidx,
offset, 4);
*val |= (*ops->vdev_barread)(ctx, vcpu, pdi, bidx,
offset + 4, 4) << 32;
uint64_t val_lo, val_hi;
val_lo = (*ops->vdev_barread)(ctx, vcpu, pdi, bidx,
offset, 4);
val_lo = bar_value(4, val_lo);
val_hi = (*ops->vdev_barread)(ctx, vcpu, pdi, bidx,
offset + 4, 4);
*val = val_lo | (val_hi << 32);
} else {
*val = (*ops->vdev_barread)(ctx, vcpu, pdi, bidx,
offset, size);
offset, size);
*val = bar_value(size, *val);
}
}