From 70237c636be15aefed7f3c4980528fc4561e07e1 Mon Sep 17 00:00:00 2001
From: Alex Williamson <alex.williamson@redhat.com>
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 <alex.williamson@redhat.com>
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 <ddutile@redhat.com>
RH-Acked-by: Michael S. Tsirkin <mst@redhat.com>
RH-Acked-by: Marcelo Tosatti <mtosatti@redhat.com>
RH-Acked-by: Juan Quintela <quintela@redhat.com>

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 <alex.williamson@redhat.com>
---

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 <ehabkost@redhat.com>
---
 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

