]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
driver: pci: turn jailhouse into a PCI dummy driver
authorHenning Schild <henning.schild@siemens.com>
Tue, 26 May 2015 12:20:08 +0000 (14:20 +0200)
committerJan Kiszka <jan.kiszka@siemens.com>
Sun, 7 Jun 2015 17:05:37 +0000 (19:05 +0200)
Implement claim/release for PCI devices assigned to non-root cells.

Signed-off-by: Henning Schild <henning.schild@siemens.com>
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
driver/cell.c
driver/main.c
driver/pci.c
driver/pci.h

index 4dbaab43c7676e47a114d8dc1d90391b91c640de..e28a96c8fb9212a8c0550d8e118377817aaf92d1 100644 (file)
@@ -139,6 +139,9 @@ void jailhouse_cell_delete_all(void)
        jailhouse_pci_do_all_devices(root_cell, JAILHOUSE_PCI_TYPE_IVSHMEM,
                                     JAILHOUSE_PCI_ACTION_DEL);
 
+       jailhouse_pci_do_all_devices(root_cell, JAILHOUSE_PCI_TYPE_DEVICE,
+                                    JAILHOUSE_PCI_ACTION_RELEASE);
+
        list_for_each_entry_safe(cell, tmp, &cells, entry)
                jailhouse_cell_delete(cell);
 
@@ -212,6 +215,9 @@ int jailhouse_cmd_cell_create(struct jailhouse_cell_create __user *arg)
                cpumask_clear_cpu(cpu, &root_cell->cpus_assigned);
        }
 
+       jailhouse_pci_do_all_devices(cell, JAILHOUSE_PCI_TYPE_DEVICE,
+                                    JAILHOUSE_PCI_ACTION_CLAIM);
+
        id = jailhouse_call_arg1(JAILHOUSE_HC_CELL_CREATE, __pa(config));
        if (id < 0) {
                err = id;
@@ -394,6 +400,9 @@ int jailhouse_cmd_cell_destroy(const char __user *arg)
                cpumask_set_cpu(cpu, &root_cell->cpus_assigned);
        }
 
+       jailhouse_pci_do_all_devices(cell, JAILHOUSE_PCI_TYPE_DEVICE,
+                                    JAILHOUSE_PCI_ACTION_RELEASE);
+
        pr_info("Destroyed Jailhouse cell \"%s\"\n",
                kobject_name(&cell->kobj));
 
index fececf9bffd09aa04a60d594f1eb69d42db1b601..a76deb95856543959e8a8c1cd523434affb53bda 100644 (file)
@@ -480,11 +480,17 @@ static int __init jailhouse_init(void)
        if (err)
                goto exit_sysfs;
 
+       err = jailhouse_pci_register();
+       if (err)
+               goto exit_misc;
+
        register_reboot_notifier(&jailhouse_shutdown_nb);
 
        init_hypercall();
 
        return 0;
+exit_misc:
+       misc_deregister(&jailhouse_misc_dev);
 
 exit_sysfs:
        jailhouse_sysfs_exit(jailhouse_dev);
@@ -499,6 +505,7 @@ static void __exit jailhouse_exit(void)
        unregister_reboot_notifier(&jailhouse_shutdown_nb);
        misc_deregister(&jailhouse_misc_dev);
        jailhouse_sysfs_exit(jailhouse_dev);
+       jailhouse_pci_unregister();
        root_device_unregister(jailhouse_dev);
 }
 
index 8d9e52781c6dee2cb2e120b6ac338e09402e7476..50665b09ab14dbc788cd3b79d4df2a0f9728c4c7 100644 (file)
 
 #include "pci.h"
 
+static int jailhouse_pci_stub_probe(struct pci_dev *dev,
+                                   const struct pci_device_id *id)
+{
+       dev_info(&dev->dev, "claimed for use in non-root cell\n");
+       return 0;
+}
+
+/**
+ * When assigning PCI devices to cells we need to make sure Linux in the
+ * root-cell does not use them anymore. As long as devices are assigned to
+ * other cells the root-cell must not access the devices.
+ * Unfortunately we can not just use PCI hotplugging to remove/re-add
+ * devices at runtime. Linux will reprogram the BARs and locate ressources
+ * where we do not expect/allow them.
+ * So Jailhouse acts as a PCI dummy driver and claims the devices while
+ * other cells use them. When creating a cell devices will be unbound from
+ * their drivers and bound to jailhouse. When a cell is destroyed jailhouse
+ * will release its devices. When jailhouse is disabled it will release all
+ * assigned devices.
+ * @see jailhouse_pci_claim_release
+ *
+ * When releasing devices they will not be bound to any driver anymore and
+ * from Linuxs point of view the jailhouse dummy will still look like a
+ * valid driver. Assignment back to the original driver has to be done
+ * manually.
+ */
+static struct pci_driver jailhouse_pci_stub_driver = {
+       .name           = "jailhouse-pci-stub",
+       .id_table       = NULL,
+       .probe          = jailhouse_pci_stub_probe,
+};
+
 static void jailhouse_pci_add_device(const struct jailhouse_pci_device *dev)
 {
        int num;
@@ -41,6 +73,64 @@ static void jailhouse_pci_remove_device(const struct jailhouse_pci_device *dev)
                pci_stop_and_remove_bus_device_locked(l_dev);
 }
 
+static void jailhouse_pci_claim_release(const struct jailhouse_pci_device *dev,
+                                       unsigned int action)
+{
+       int err;
+       struct pci_bus *bus;
+       struct pci_dev *l_dev;
+       struct device_driver *drv;
+
+       bus = pci_find_bus(dev->domain, PCI_BUS_NUM(dev->bdf));
+       if (!bus)
+               return;
+       l_dev = pci_get_slot(bus, dev->bdf & 0xff);
+       drv = l_dev->dev.driver;
+
+       if (action == JAILHOUSE_PCI_ACTION_CLAIM) {
+               if (drv == &jailhouse_pci_stub_driver.driver)
+                       return;
+               device_release_driver(&l_dev->dev);
+               err = pci_add_dynid(&jailhouse_pci_stub_driver, l_dev->vendor,
+                                   l_dev->device, l_dev->subsystem_vendor,
+                                   l_dev->subsystem_device, l_dev->class,
+                                   0, 0);
+               if (err)
+                       dev_warn(&l_dev->dev, "failed to add dynamic id (%d)\n",
+                                err);
+       } else {
+               /* on "jailhouse disable" we will come here with the
+                * request to release all pci devices, so check the driver */
+               if (drv == &jailhouse_pci_stub_driver.driver)
+                       device_release_driver(&l_dev->dev);
+       }
+}
+
+/**
+ * Register jailhouse as a PCI device driver so it can claim assigned devices.
+ *
+ * @return 0 on success, or error code
+ */
+int jailhouse_pci_register(void)
+{
+       return pci_register_driver(&jailhouse_pci_stub_driver);
+}
+
+/**
+ * Unegister jailhouse as a PCI device driver.
+ */
+void jailhouse_pci_unregister(void)
+{
+       pci_unregister_driver(&jailhouse_pci_stub_driver);
+}
+
+/**
+ * Apply the given action to all of the cells PCI devices matching the given
+ * type.
+ * @param cell         the cell containing the PCI devices
+ * @param type         PCI device type (JAILHOUSE_PCI_TYPE_*)
+ * @param action       action (JAILHOUSE_PCI_ACTION_*)
+ */
 void jailhouse_pci_do_all_devices(struct cell *cell, unsigned int type,
                                  unsigned int action)
 {
@@ -50,10 +140,16 @@ void jailhouse_pci_do_all_devices(struct cell *cell, unsigned int type,
        dev = cell->pci_devices;
        for (n = cell->num_pci_devices; n > 0; n--) {
                if (dev->type == type) {
-                       if (action == JAILHOUSE_PCI_ACTION_ADD)
+                       switch(action) {
+                       case JAILHOUSE_PCI_ACTION_ADD:
                                jailhouse_pci_add_device(dev);
-                       else if (action == JAILHOUSE_PCI_ACTION_DEL)
+                               break;
+                       case JAILHOUSE_PCI_ACTION_DEL:
                                jailhouse_pci_remove_device(dev);
+                               break;
+                       default:
+                               jailhouse_pci_claim_release(dev, action);
+                       }
                }
                dev++;
        }
index c98eddc01b2505549858c5997088965c5f81a0a2..d86a6b6974b12c75d5d21063a83d65c5d0c40929 100644 (file)
 
 #include "cell.h"
 
-enum { JAILHOUSE_PCI_ACTION_ADD, JAILHOUSE_PCI_ACTION_DEL };
+enum {
+       JAILHOUSE_PCI_ACTION_ADD, JAILHOUSE_PCI_ACTION_DEL,
+       JAILHOUSE_PCI_ACTION_CLAIM, JAILHOUSE_PCI_ACTION_RELEASE,
+};
 
 #ifdef CONFIG_PCI
 
@@ -22,6 +25,8 @@ void jailhouse_pci_do_all_devices(struct cell *cell, unsigned int type,
 int jailhouse_pci_cell_setup(struct cell *cell,
                             const struct jailhouse_cell_desc *cell_desc);
 void jailhouse_pci_cell_cleanup(struct cell *cell);
+int jailhouse_pci_register(void);
+void jailhouse_pci_unregister(void);
 
 #else /* !CONFIG_PCI */
 
@@ -42,4 +47,13 @@ static inline void jailhouse_pci_cell_cleanup(struct cell *cell)
 {
 }
 
+static inline int jailhouse_pci_register(void)
+{
+       return 0;
+}
+
+static inline void jailhouse_pci_unregister(void)
+{
+}
+
 #endif /* !CONFIG_PCI */