#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;
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)
{
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++;
}