[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [qemu-xen-4.2-testing] xen/pt: unknown PCI config space fields should be read-only



commit 1259e092ee27f444f683f0d76a13a8a72d3f26cb
Author:     Jan Beulich <jbeulich@xxxxxxxx>
AuthorDate: Wed Jun 10 14:17:55 2015 +0100
Commit:     Ian Jackson <Ian.Jackson@xxxxxxxxxxxxx>
CommitDate: Wed Jun 10 14:17:55 2015 +0100

    xen/pt: unknown PCI config space fields should be read-only
    
    ... by default. Add a per-device "permissive" mode similar to pciback's
    to allow restoring previous behavior (and hence break security again,
    i.e. should be used only for trusted guests).
    
    This is part of XSA-131.
    
    Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
    Acked-by: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx>
    Reviewed-by: Anthony PERARD <anthony.perard@xxxxxxxxxx>)
---
 hw/pass-through.c |   47 ++++++++++++++++++++++++++++++++++++++++-------
 hw/pass-through.h |    2 ++
 2 files changed, 42 insertions(+), 7 deletions(-)

diff --git a/hw/pass-through.c b/hw/pass-through.c
index 152f8a1..9725ecd 100644
--- a/hw/pass-through.c
+++ b/hw/pass-through.c
@@ -1613,10 +1613,10 @@ static void pt_pci_write_config(PCIDevice *d, uint32_t 
address, uint32_t val,
     uint32_t find_addr = address;
     uint32_t real_offset = 0;
     uint32_t valid_mask = 0xFFFFFFFF;
-    uint32_t read_val = 0, wb_mask;
+    uint32_t read_val = 0, wb_mask, wp_mask;
     uint8_t *ptr_val = NULL;
     int emul_len = 0;
-    int index = 0;
+    int index = 0, wp_flag = 0;
     int ret = 0;
 
 #ifdef PT_DEBUG_PCI_CONFIG_ACCESS
@@ -1693,7 +1693,14 @@ static void pt_pci_write_config(PCIDevice *d, uint32_t 
address, uint32_t val,
 
     /* pass directly to libpci for passthrough type register group */
     if (reg_grp_entry == NULL)
+    {
+        if (!assigned_device->permissive)
+        {
+            wb_mask = 0;
+            wp_flag = 1;
+        }
         goto out;
+    }
 
     /* adjust the read and write value to appropriate CFC-CFF window */
     read_val <<= ((address & 3) << 3);
@@ -1712,11 +1719,12 @@ static void pt_pci_write_config(PCIDevice *d, uint32_t 
address, uint32_t val,
             valid_mask = (0xFFFFFFFF >> ((4 - emul_len) << 3));
             valid_mask <<= ((find_addr - real_offset) << 3);
             ptr_val = ((uint8_t *)&val + (real_offset & 3));
-            if (reg->emu_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) {
-                wb_mask &= ~((reg->emu_mask
-                              >> ((find_addr - real_offset) << 3))
+            wp_mask = reg->emu_mask | reg->ro_mask;
+            if (!assigned_device->permissive)
+                wp_mask |= reg->res_mask;
+            if (wp_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3)))
+                wb_mask &= ~((wp_mask >> ((find_addr - real_offset) << 3))
                              << ((len - emul_len) << 3));
-            }
 
             /* do emulation depend on register size */
             switch (reg->size) {
@@ -1765,6 +1773,16 @@ static void pt_pci_write_config(PCIDevice *d, uint32_t 
address, uint32_t val,
             /* nothing to do with passthrough type register,
              * continue to find next byte
              */
+            if (!assigned_device->permissive)
+            {
+                wb_mask &= ~(0xff << ((len - emul_len) << 3));
+                /* Unused BARs will make it here, but we don't want to issue
+                 * warnings for writes to them (bogus writes get dealt with
+                 * above).
+                 */
+                if (index < 0)
+                    wp_flag = 1;
+            }
             emul_len--;
             find_addr++;
         }
@@ -1774,6 +1792,15 @@ static void pt_pci_write_config(PCIDevice *d, uint32_t 
address, uint32_t val,
     val >>= ((address & 3) << 3);
 
 out:
+    if (wp_flag && !assigned_device->permissive_warned)
+    {
+        assigned_device->permissive_warned = 1;
+        PT_LOG("Write-back to unknown field 0x%02x (partially) inhibited 
(0x%0*x)\n",
+               addr, len * 2, wb_mask);
+        PT_LOG("If device %02x:%02x.%o doesn't work, try enabling 
permissive\n",
+               pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
+        PT_LOG("mode (unsafe) and if it helps report the problem to 
xen-devel\n");
+    }
     for (index = 0; wb_mask; index += len) {
         /* unknown regs are passed through */
         while (!(wb_mask & 0xff)) {
@@ -3482,6 +3509,9 @@ static uint32_t get_throughable_mask(const struct pt_dev 
*ptdev,
 {
     uint32_t throughable_mask = ~(reg->emu_mask | reg->ro_mask);
 
+    if (!ptdev->permissive)
+        throughable_mask &= ~reg->res_mask;
+
     return throughable_mask & valid_mask;
 }
 
@@ -4314,7 +4344,7 @@ static struct pt_dev * register_real_device(PCIBus *e_bus,
     uint8_t e_device, e_intx;
     uint16_t cmd = 0;
     char *key, *val;
-    int msi_translate, power_mgmt;
+    int msi_translate, power_mgmt, permissive = 0;
 
     PT_LOG("Assigning real physical device %02x:%02x.%x ...\n",
         r_bus, r_dev, r_func);
@@ -4358,6 +4388,8 @@ static struct pt_dev * register_real_device(PCIBus *e_bus,
             else
                 PT_LOG("Error: unrecognized value for msitranslate=\n");
         }
+        else if (strcmp(key, "permissive") == 0)
+            permissive = 1;
         else if (strcmp(key, "power_mgmt") == 0)
         {
             if (strcmp(val, "0") == 0)
@@ -4395,6 +4427,7 @@ static struct pt_dev * register_real_device(PCIBus *e_bus,
     assigned_device->msi_trans_cap = msi_translate;
     assigned_device->power_mgmt = power_mgmt;
     assigned_device->is_virtfn = pt_dev_is_virtfn(pci_dev);
+    assigned_device->permissive = permissive;
     pt_iomul_init(assigned_device, r_bus, r_dev, r_func);
 
     /* Initialize virtualized PCI configuration (Extended 256 Bytes) */
diff --git a/hw/pass-through.h b/hw/pass-through.h
index 95d033b..bb6ddce 100644
--- a/hw/pass-through.h
+++ b/hw/pass-through.h
@@ -248,6 +248,8 @@ struct pt_dev {
     unsigned power_mgmt:1;
     struct pt_pm_info *pm_state;                /* PM virtualization */
     unsigned is_virtfn:1;
+    unsigned permissive:1;
+    unsigned permissive_warned:1;
 
     /* io port multiplexing */
 #define PCI_IOMUL_INVALID_FD    (-1)
--
generated by git-patchbot for /home/xen/git/qemu-xen-4.2-testing.git

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.