]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
core: Fix PCI device runtime ownership tracking
authorJan Kiszka <jan.kiszka@siemens.com>
Mon, 4 Aug 2014 06:29:26 +0000 (08:29 +0200)
committerJan Kiszka <jan.kiszka@siemens.com>
Mon, 4 Aug 2014 15:52:57 +0000 (17:52 +0200)
Trigger PCI device addition and removal from the PCI core and update the
cell field in the device state in order to track active ownership. The
vtd module now only provides callbacks to update its tables when adding
or removing a device.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
hypervisor/arch/x86/include/asm/vtd.h
hypervisor/arch/x86/pci.c
hypervisor/arch/x86/vtd.c
hypervisor/include/jailhouse/pci.h
hypervisor/pci.c

index 9a7f28c6d1c37562a84a9aa139cecb3123d204fb..2f78a99aa1f978a1a3757ea0fcd1c123e087c121 100644 (file)
@@ -11,6 +11,7 @@
  */
 
 #include <jailhouse/acpi.h>
+#include <jailhouse/pci.h>
 #include <jailhouse/utils.h>
 #include <asm/cell.h>
 
@@ -131,6 +132,8 @@ int vtd_map_memory_region(struct cell *cell,
                          const struct jailhouse_memory *mem);
 int vtd_unmap_memory_region(struct cell *cell,
                            const struct jailhouse_memory *mem);
+int vtd_add_pci_device(struct cell *cell, struct pci_device *device);
+void vtd_remove_pci_device(struct pci_device *device);
 void vtd_cell_exit(struct cell *cell);
 
 void vtd_config_commit(struct cell *cell_added_removed);
index 00904c211e7fc9e5579f3157204e087add9dabf5..733878b766e4efdca11432032a3c162ddeb51e88 100644 (file)
  */
 
 #include <jailhouse/pci.h>
+#include <jailhouse/printk.h>
 #include <jailhouse/utils.h>
 #include <asm/io.h>
 #include <asm/pci.h>
+#include <asm/vtd.h>
 
 /* protects the root bridge's PIO interface to the PCI config space */
 static DEFINE_SPINLOCK(pci_lock);
@@ -211,3 +213,13 @@ int x86_pci_config_handler(struct registers *guest_regs, struct cell *cell,
 
        return result;
 }
+
+int arch_pci_add_device(struct cell *cell, struct pci_device *device)
+{
+       return vtd_add_pci_device(cell, device);
+}
+
+void arch_pci_remove_device(struct pci_device *device)
+{
+       vtd_remove_pci_device(device);
+}
index 697cd2f1d75ad939b3ece992f0a6a0a581462a97..2927c57af69620b802f19c33970f910491657f1d 100644 (file)
@@ -286,31 +286,32 @@ int vtd_init(void)
        return vtd_cell_init(&root_cell);
 }
 
-static bool vtd_add_device_to_cell(struct cell *cell,
-                                  const struct jailhouse_pci_device *device)
+int vtd_add_pci_device(struct cell *cell, struct pci_device *device)
 {
-       u64 *root_entry_lo = &root_entry_table[PCI_BUS(device->bdf)].lo_word;
+       u16 bdf = device->info->bdf;
+       u64 *root_entry_lo = &root_entry_table[PCI_BUS(bdf)].lo_word;
        struct vtd_entry *context_entry_table, *context_entry;
 
+       // HACK for QEMU
+       if (dmar_units == 0)
+               return 0;
+
        if (*root_entry_lo & VTD_ROOT_PRESENT) {
                context_entry_table =
                        page_map_phys2hvirt(*root_entry_lo & PAGE_MASK);
        } else {
                context_entry_table = page_alloc(&mem_pool, 1);
                if (!context_entry_table)
-                       return false;
+                       return -ENOMEM;
                *root_entry_lo = VTD_ROOT_PRESENT |
                        page_map_hvirt2phys(context_entry_table);
                flush_cache(root_entry_lo, sizeof(u64));
        }
 
-       context_entry = &context_entry_table[PCI_DEVFN(device->bdf)];
+       context_entry = &context_entry_table[PCI_DEVFN(bdf)];
        if (context_entry->lo_word & VTD_CTX_PRESENT)
                return true;
 
-       printk("Adding PCI device %02x:%02x.%x to cell \"%s\"\n",
-              PCI_BDF_PARAMS(device->bdf), cell->config->name);
-
        context_entry->lo_word = VTD_CTX_PRESENT | VTD_CTX_TTYPE_MLP_UNTRANS |
                page_map_hvirt2phys(cell->vtd.pg_structs.root_table);
        context_entry->hi_word =
@@ -318,30 +319,30 @@ static bool vtd_add_device_to_cell(struct cell *cell,
                (cell->id << VTD_CTX_DID_SHIFT);
        flush_cache(context_entry, sizeof(*context_entry));
 
-       return true;
+       return 0;
 }
 
-static void
-vtd_remove_device_from_cell(struct cell *cell,
-                           const struct jailhouse_pci_device *device)
+void vtd_remove_pci_device(struct pci_device *device)
 {
-       u64 *root_entry_lo = &root_entry_table[PCI_BUS(device->bdf)].lo_word;
+       u16 bdf = device->info->bdf;
+       u64 *root_entry_lo = &root_entry_table[PCI_BUS(bdf)].lo_word;
        struct vtd_entry *context_entry_table;
        struct vtd_entry *context_entry;
        unsigned int n;
 
+       // HACK for QEMU
+       if (dmar_units == 0)
+               return;
+
        if (!(*root_entry_lo & VTD_ROOT_PRESENT))
                return;
 
        context_entry_table = page_map_phys2hvirt(*root_entry_lo & PAGE_MASK);
-       context_entry = &context_entry_table[PCI_DEVFN(device->bdf)];
+       context_entry = &context_entry_table[PCI_DEVFN(bdf)];
 
        if (!(context_entry->lo_word & VTD_CTX_PRESENT))
                return;
 
-       printk("Removing PCI device %02x:%02x.%x from cell \"%s\"\n",
-              PCI_BDF_PARAMS(device->bdf), cell->config->name);
-
        context_entry->lo_word &= ~VTD_CTX_PRESENT;
        flush_cache(&context_entry->lo_word, sizeof(u64));
 
@@ -356,10 +357,6 @@ vtd_remove_device_from_cell(struct cell *cell,
 
 int vtd_cell_init(struct cell *cell)
 {
-       const struct jailhouse_pci_device *dev =
-               jailhouse_cell_pci_devices(cell->config);
-       int n;
-
        // HACK for QEMU
        if (dmar_units == 0)
                return 0;
@@ -372,14 +369,6 @@ int vtd_cell_init(struct cell *cell)
        if (!cell->vtd.pg_structs.root_table)
                return -ENOMEM;
 
-       for (n = 0; n < cell->config->num_pci_devices; n++) {
-               vtd_remove_device_from_cell(&root_cell, &dev[n]);
-               if (!vtd_add_device_to_cell(cell, &dev[n])) {
-                       vtd_cell_exit(cell);
-                       return -ENOMEM;
-               }
-       }
-
        vtd_init_fault_nmi();
 
        return 0;
@@ -421,38 +410,12 @@ int vtd_unmap_memory_region(struct cell *cell,
                                mem->size, PAGE_MAP_COHERENT);
 }
 
-static bool
-vtd_return_device_to_root_cell(const struct jailhouse_pci_device *dev)
-{
-       const struct jailhouse_pci_device *root_cell_dev =
-               jailhouse_cell_pci_devices(root_cell.config);
-       unsigned int n;
-
-       for (n = 0; n < root_cell.config->num_pci_devices; n++)
-               if (root_cell_dev[n].domain == dev->domain &&
-                   root_cell_dev[n].bdf == dev->bdf)
-                       return vtd_add_device_to_cell(&root_cell,
-                                                     &root_cell_dev[n]);
-       return true;
-}
-
 void vtd_cell_exit(struct cell *cell)
 {
-       const struct jailhouse_pci_device *dev =
-               jailhouse_cell_pci_devices(cell->config);
-       unsigned int n;
-
        // HACK for QEMU
        if (dmar_units == 0)
                return;
 
-       for (n = 0; n < cell->config->num_pci_devices; n++) {
-               vtd_remove_device_from_cell(cell, &dev[n]);
-               if (!vtd_return_device_to_root_cell(&dev[n]))
-                       printk("WARNING: Failed to re-assign PCI device to "
-                              "root cell\n");
-       }
-
        page_free(&mem_pool, cell->vtd.pg_structs.root_table, 1);
 }
 
index ada89789e729f0268c0c4e510ebba62d16f6b9a1..f67cf35527fe292ac9940b03cd7aefc5fddd2810 100644 (file)
@@ -48,4 +48,7 @@ void pci_cell_exit(struct cell *cell);
 u32 arch_pci_read_config(u16 bdf, u16 address, unsigned int size);
 void arch_pci_write_config(u16 bdf, u16 address, u32 value, unsigned int size);
 
+int arch_pci_add_device(struct cell *cell, struct pci_device *device);
+void arch_pci_remove_device(struct pci_device *device);
+
 #endif /* !_JAILHOUSE_PCI_H */
index 5d6072b39dd96f50545b2f319bdef11562c41872..ee46adf7e8bd7ef5d8486085788bc945db6cbf60 100644 (file)
@@ -12,6 +12,7 @@
  */
 
 #include <jailhouse/acpi.h>
+#include <jailhouse/control.h>
 #include <jailhouse/mmio.h>
 #include <jailhouse/pci.h>
 #include <jailhouse/printk.h>
@@ -125,7 +126,8 @@ struct pci_device *pci_get_assigned_device(const struct cell *cell, u16 bdf)
         * locality. */
        for (n = 0; n < cell->config->num_pci_devices; n++)
                if (dev_info[n].bdf == bdf)
-                       return &cell->pci_devices[n];
+                       return cell->pci_devices[n].cell ?
+                               &cell->pci_devices[n] : NULL;
 
        return NULL;
 }
@@ -316,14 +318,29 @@ invalid_access:
 
 }
 
+static int pci_add_device(struct cell *cell, struct pci_device *device)
+{
+       printk("Adding PCI device %02x:%02x.%x to cell \"%s\"\n",
+              PCI_BDF_PARAMS(device->info->bdf), cell->config->name);
+       return arch_pci_add_device(cell, device);
+}
+
+static void pci_remove_device(struct pci_device *device)
+{
+       printk("Removing PCI device %02x:%02x.%x from cell \"%s\"\n",
+              PCI_BDF_PARAMS(device->info->bdf), device->cell->config->name);
+       arch_pci_remove_device(device);
+}
+
 int pci_cell_init(struct cell *cell)
 {
        unsigned long array_size = PAGE_ALIGN(cell->config->num_pci_devices *
                                              sizeof(struct pci_device));
        const struct jailhouse_pci_device *dev_infos =
                jailhouse_cell_pci_devices(cell->config);
-       struct pci_device *device;
+       struct pci_device *device, *root_device;
        unsigned int ndev;
+       int err;
 
        cell->pci_devices = page_alloc(&mem_pool, array_size / PAGE_SIZE);
        if (!cell->pci_devices)
@@ -333,21 +350,67 @@ int pci_cell_init(struct cell *cell)
         * We order device states in the same way as the static information
         * so that we can use the index of the latter to find the former. For
         * the other way around and for obtaining the owner cell, we use more
-        * handy pointers.
+        * handy pointers. The cell pointer also encodes active ownership.
         */
        for (ndev = 0; ndev < cell->config->num_pci_devices; ndev++) {
                device = &cell->pci_devices[ndev];
                device->info = &dev_infos[ndev];
+
+               root_device = pci_get_assigned_device(&root_cell,
+                                                     dev_infos[ndev].bdf);
+               if (root_device) {
+                       pci_remove_device(root_device);
+                       root_device->cell = NULL;
+               }
+
+               err = pci_add_device(cell, device);
+               if (err) {
+                       pci_cell_exit(cell);
+                       return err;
+               }
+
                device->cell = cell;
        }
 
        return 0;
 }
 
+static void pci_return_device_to_root_cell(struct pci_device *device)
+{
+       struct pci_device *root_device = root_cell.pci_devices;
+       unsigned int n;
+
+       for (n = 0; n < root_cell.config->num_pci_devices; n++, root_device++)
+               if (root_device->info->domain == device->info->domain &&
+                   root_device->info->bdf == device->info->bdf) {
+                       if (pci_add_device(&root_cell, root_device) < 0)
+                               printk("WARNING: Failed to re-assign PCI "
+                                      "device to root cell\n");
+                       else
+                               root_device->cell = &root_cell;
+                       break;
+               }
+}
+
 void pci_cell_exit(struct cell *cell)
 {
        unsigned long array_size = PAGE_ALIGN(cell->config->num_pci_devices *
                                              sizeof(struct pci_device));
+       unsigned int n;
+
+       /*
+        * Do not destroy the root cell. We will shut down the complete
+        * hypervisor instead.
+        */
+       if (cell == &root_cell)
+               return;
+
+       for (n = 0; n < cell->config->num_pci_devices; n++) {
+               if (!cell->pci_devices[n].cell)
+                       continue;
+               pci_remove_device(&cell->pci_devices[n]);
+               pci_return_device_to_root_cell(&cell->pci_devices[n]);
+       }
 
        page_free(&mem_pool, cell->pci_devices, array_size / PAGE_SIZE);
 }