2 * Jailhouse, a Linux-based partitioning hypervisor
4 * Copyright (c) Siemens AG, 2014-2015
7 * Jan Kiszka <jan.kiszka@siemens.com>
8 * Henning Schild <henning.schild@siemens.com>
10 * This work is licensed under the terms of the GNU GPL, version 2. See
11 * the COPYING file in the top-level directory.
14 #include <linux/pci.h>
15 #include <linux/vmalloc.h>
19 static int jailhouse_pci_stub_probe(struct pci_dev *dev,
20 const struct pci_device_id *id)
22 dev_info(&dev->dev, "claimed for use in non-root cell\n");
27 * When assigning PCI devices to cells we need to make sure Linux in the
28 * root-cell does not use them anymore. As long as devices are assigned to
29 * other cells the root-cell must not access the devices.
30 * Unfortunately we can not just use PCI hotplugging to remove/re-add
31 * devices at runtime. Linux will reprogram the BARs and locate ressources
32 * where we do not expect/allow them.
33 * So Jailhouse acts as a PCI dummy driver and claims the devices while
34 * other cells use them. When creating a cell devices will be unbound from
35 * their drivers and bound to jailhouse. When a cell is destroyed jailhouse
36 * will release its devices. When jailhouse is disabled it will release all
38 * @see jailhouse_pci_claim_release
40 * When releasing devices they will not be bound to any driver anymore and
41 * from Linuxs point of view the jailhouse dummy will still look like a
42 * valid driver. Assignment back to the original driver has to be done
45 static struct pci_driver jailhouse_pci_stub_driver = {
46 .name = "jailhouse-pci-stub",
48 .probe = jailhouse_pci_stub_probe,
51 static void jailhouse_pci_add_device(const struct jailhouse_pci_device *dev)
56 bus = pci_find_bus(dev->domain, PCI_BUS_NUM(dev->bdf));
58 num = pci_scan_slot(bus, dev->bdf & 0xff);
60 pci_lock_rescan_remove();
61 pci_bus_assign_resources(bus);
62 pci_bus_add_devices(bus);
63 pci_unlock_rescan_remove();
68 static void jailhouse_pci_remove_device(const struct jailhouse_pci_device *dev)
70 struct pci_dev *l_dev;
72 l_dev = pci_get_bus_and_slot(PCI_BUS_NUM(dev->bdf), dev->bdf & 0xff);
74 pci_stop_and_remove_bus_device_locked(l_dev);
77 static void jailhouse_pci_claim_release(const struct jailhouse_pci_device *dev,
82 struct pci_dev *l_dev;
83 struct device_driver *drv;
85 bus = pci_find_bus(dev->domain, PCI_BUS_NUM(dev->bdf));
88 l_dev = pci_get_slot(bus, dev->bdf & 0xff);
91 drv = l_dev->dev.driver;
93 if (action == JAILHOUSE_PCI_ACTION_CLAIM) {
94 if (drv == &jailhouse_pci_stub_driver.driver)
96 device_release_driver(&l_dev->dev);
97 err = pci_add_dynid(&jailhouse_pci_stub_driver, l_dev->vendor,
98 l_dev->device, l_dev->subsystem_vendor,
99 l_dev->subsystem_device, l_dev->class,
102 dev_warn(&l_dev->dev, "failed to add dynamic id (%d)\n",
105 /* on "jailhouse disable" we will come here with the
106 * request to release all pci devices, so check the driver */
107 if (drv == &jailhouse_pci_stub_driver.driver)
108 device_release_driver(&l_dev->dev);
113 * Register jailhouse as a PCI device driver so it can claim assigned devices.
115 * @return 0 on success, or error code
117 int jailhouse_pci_register(void)
119 return pci_register_driver(&jailhouse_pci_stub_driver);
123 * Unegister jailhouse as a PCI device driver.
125 void jailhouse_pci_unregister(void)
127 pci_unregister_driver(&jailhouse_pci_stub_driver);
131 * Apply the given action to all of the cells PCI devices matching the given
133 * @param cell the cell containing the PCI devices
134 * @param type PCI device type (JAILHOUSE_PCI_TYPE_*)
135 * @param action action (JAILHOUSE_PCI_ACTION_*)
137 void jailhouse_pci_do_all_devices(struct cell *cell, unsigned int type,
141 const struct jailhouse_pci_device *dev;
143 dev = cell->pci_devices;
144 for (n = cell->num_pci_devices; n > 0; n--) {
145 if (dev->type == type) {
147 case JAILHOUSE_PCI_ACTION_ADD:
148 jailhouse_pci_add_device(dev);
150 case JAILHOUSE_PCI_ACTION_DEL:
151 jailhouse_pci_remove_device(dev);
154 jailhouse_pci_claim_release(dev, action);
161 int jailhouse_pci_cell_setup(struct cell *cell,
162 const struct jailhouse_cell_desc *cell_desc)
164 if (cell_desc->num_pci_devices == 0)
165 /* cell is zero-initialized, no need to set pci fields */
168 if (cell_desc->num_pci_devices >=
169 ULONG_MAX / sizeof(struct jailhouse_pci_device))
172 cell->num_pci_devices = cell_desc->num_pci_devices;
173 cell->pci_devices = vmalloc(sizeof(struct jailhouse_pci_device) *
174 cell->num_pci_devices);
175 if (!cell->pci_devices)
178 memcpy(cell->pci_devices,
179 jailhouse_cell_pci_devices(cell_desc),
180 sizeof(struct jailhouse_pci_device) * cell->num_pci_devices);
185 void jailhouse_pci_cell_cleanup(struct cell *cell)
187 vfree(cell->pci_devices);