From 70237c636be15aefed7f3c4980528fc4561e07e1 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 29 Jun 2010 14:40:50 -0300 Subject: [PATCH 1/2] device-assignment: Avoid munmapping the real MSIX area RH-Author: Alex Williamson Message-id: <20100629143853.30906.2553.stgit@localhost.localdomain> Patchwork-id: 10336 O-Subject: [RHEL6.0 qemu-kvm PATCH 1/2 v2] device-assignment: Avoid munmapping the real MSIX area Bugzilla: 572043 RH-Acked-by: Don Dutile RH-Acked-by: Michael S. Tsirkin RH-Acked-by: Marcelo Tosatti RH-Acked-by: Juan Quintela Bugzilla: 572043 Upstream status: N/A: kvm-88-4599-g128a4da changed the code in a way that makes this unnecessary on upstream. When setting up mappings for a BAR, we take special care to munmap the area covered by the MSIX table to prevent the guest from poking real hardware. Unfortunately, the 4k page we unmap then goes back into the pool and may get allocated elsewhere. This is exactly the problem we hit when you have two devices assigned to a guest (ex. 82576 VFs), do some hotplugs, then reboot the guest with the devices assigned. The munmap frees the 4k page, which we then mmap as anonymous memory for the MSIX table, which promptly gets munmapped again on the next pass through assigned_dev_iomem_map(). Signed-off-by: Alex Williamson --- v2: mst noticed the second mapping call meant to place the remainder of the bar above the MSIX page was using the base of the mmap for the bar. This should include the offset to get past the MSIX page. hw/device-assignment.c | 81 ++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 64 insertions(+), 17 deletions(-) Signed-off-by: Eduardo Habkost --- hw/device-assignment.c | 81 ++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 64 insertions(+), 17 deletions(-) diff --git a/hw/device-assignment.c b/hw/device-assignment.c index eba7d8b..12ddac3 100644 --- a/hw/device-assignment.c +++ b/hw/device-assignment.c @@ -166,32 +166,60 @@ static void assigned_dev_iomem_map(PCIDevice *pci_dev, int region_num, region->e_physbase = e_phys; region->e_size = e_size; - if (!first_map) - kvm_destroy_phys_mem(kvm_context, old_ephys, - TARGET_PAGE_ALIGN(old_esize)); - if (e_size > 0) { /* deal with MSI-X MMIO page */ if (real_region->base_addr <= r_dev->msix_table_addr && real_region->base_addr + real_region->size >= r_dev->msix_table_addr) { + int offset = r_dev->msix_table_addr - real_region->base_addr; - ret = munmap(region->u.r_virtbase + offset, TARGET_PAGE_SIZE); - if (ret == 0) - DEBUG("munmap done, virt_base 0x%p\n", - region->u.r_virtbase + offset); - else { - fprintf(stderr, "%s: fail munmap msix table!\n", __func__); - exit(1); - } + cpu_register_physical_memory(e_phys + offset, - TARGET_PAGE_SIZE, r_dev->mmio_index); + TARGET_PAGE_SIZE, r_dev->mmio_index); + + if (offset > 0) { + if (!first_map) + kvm_destroy_phys_mem(kvm_context, old_ephys, + TARGET_PAGE_ALIGN(offset)); + + ret = kvm_register_phys_mem(kvm_context, e_phys, + region->u.r_virtbase, + TARGET_PAGE_ALIGN(offset), 0); + if (ret != 0) + goto out; + } + + if (e_size - offset - TARGET_PAGE_SIZE > 0) { + if (!first_map) + kvm_destroy_phys_mem(kvm_context, + old_ephys + offset + TARGET_PAGE_SIZE, + TARGET_PAGE_ALIGN(e_size - offset - + TARGET_PAGE_SIZE)); + + ret = kvm_register_phys_mem(kvm_context, + e_phys + offset + TARGET_PAGE_SIZE, + region->u.r_virtbase + offset + + TARGET_PAGE_SIZE, + TARGET_PAGE_ALIGN(e_size - offset - + TARGET_PAGE_SIZE), + 0); + if (ret != 0) + goto out; + } + + } else { + + if (!first_map) + kvm_destroy_phys_mem(kvm_context, old_ephys, + TARGET_PAGE_ALIGN(old_esize)); + + ret = kvm_register_phys_mem(kvm_context, e_phys, + region->u.r_virtbase, + TARGET_PAGE_ALIGN(e_size), 0); } - ret = kvm_register_phys_mem(kvm_context, e_phys, - region->u.r_virtbase, - TARGET_PAGE_ALIGN(e_size), 0); } +out: if (ret != 0) { fprintf(stderr, "%s: Error: create new mapping failed\n", __func__); exit(1); @@ -648,9 +676,28 @@ static void free_assigned_device(AssignedDevice *dev) kvm_remove_ioperm_data(region->u.r_baseport, region->r_size); continue; } else if (pci_region->type & IORESOURCE_MEM) { - if (region->e_size > 0) + if (region->e_size == 0) + continue; + + if (pci_region->base_addr <= dev->msix_table_addr && + pci_region->base_addr + pci_region->size >= + dev->msix_table_addr) { + + int offset = dev->msix_table_addr - pci_region->base_addr; + + if (offset > 0) + kvm_destroy_phys_mem(kvm_context, region->e_physbase, + TARGET_PAGE_ALIGN(offset)); + if (region->e_size - offset - TARGET_PAGE_SIZE > 0) + kvm_destroy_phys_mem(kvm_context, + region->e_physbase + offset + TARGET_PAGE_SIZE, + TARGET_PAGE_ALIGN(region->e_size - offset - + TARGET_PAGE_SIZE)); + + } else { kvm_destroy_phys_mem(kvm_context, region->e_physbase, TARGET_PAGE_ALIGN(region->e_size)); + } if (region->u.r_virtbase) { int ret = munmap(region->u.r_virtbase, -- 1.7.0.3