devices: vfio: Fix a msix table and msix pba table overlapping issue

An error occured when passthrough some nvme to a guest based on vfio:

[ERROR:devices/src/pci/vfio_pci.rs:773] add_bar_mmap_msix failed:
		Out-of-space detected in MSIX Allocator

Althrough the issue may only happen to some devices whose msix table
overlaps with its msix pba table, as it was mentioned before[1], this
situation must be covered by some validation test. Otherwise, it will
block crosvm booting.

[1] https://patchwork.kernel.org/project/qemu-devel/patch/099db937-3fa3-465e-9a23-a900df9adb7c@default/

BUG=b:1971693450
TEST=passthrough a nvme with a msix table overlapping the msix pba table,
then boot Linux kernel and verify MSIX-capable passed-through devices
work properly.

Change-Id: I602dda95d4671682dc03478415f6a96d7c40ec6e
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3152434
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
Tina Zhang 2021-07-24 22:11:22 +08:00 committed by Commit Bot
parent e32b55670b
commit 7458a3a19b

View file

@ -260,14 +260,21 @@ struct VfioMsixCap {
impl VfioMsixCap {
fn new(config: &VfioPciConfig, msix_cap_start: u32, vm_socket_irq: Tube) -> Self {
let msix_ctl = config.read_config_word(msix_cap_start + PCI_MSIX_FLAGS);
let table_size = (msix_ctl & PCI_MSIX_FLAGS_QSIZE) as u64 + 1;
let table = config.read_config_dword(msix_cap_start + PCI_MSIX_TABLE);
let table_pci_bar = table & PCI_MSIX_TABLE_BIR;
let table_offset = (table & PCI_MSIX_TABLE_OFFSET) as u64;
let table_size_bytes = table_size * MSIX_TABLE_ENTRIES_MODULO;
let pba = config.read_config_dword(msix_cap_start + PCI_MSIX_PBA);
let pba_pci_bar = pba & PCI_MSIX_PBA_BIR;
let pba_offset = (pba & PCI_MSIX_PBA_OFFSET) as u64;
let mut table_size = (msix_ctl & PCI_MSIX_FLAGS_QSIZE) as u64 + 1;
if table_pci_bar == pba_pci_bar
&& (table_offset + table_size * MSIX_TABLE_ENTRIES_MODULO) > pba_offset
{
table_size = (pba_offset - table_offset) / MSIX_TABLE_ENTRIES_MODULO;
}
let table_size_bytes = table_size * MSIX_TABLE_ENTRIES_MODULO;
let pba_size_bytes = ((table_size + BITS_PER_PBA_ENTRY as u64 - 1)
/ BITS_PER_PBA_ENTRY as u64)
* MSIX_PBA_ENTRIES_MODULO;