]> rtime.felk.cvut.cz Git - jailhouse.git/blob - driver/pci.c
jailhouse: inmates: bench: Add -R option -- repeats count.
[jailhouse.git] / driver / pci.c
1 /*
2  * Jailhouse, a Linux-based partitioning hypervisor
3  *
4  * Copyright (c) Siemens AG, 2014-2015
5  *
6  * Authors:
7  *  Jan Kiszka <jan.kiszka@siemens.com>
8  *  Henning Schild <henning.schild@siemens.com>
9  *
10  * This work is licensed under the terms of the GNU GPL, version 2.  See
11  * the COPYING file in the top-level directory.
12  */
13
14 #include <linux/pci.h>
15 #include <linux/vmalloc.h>
16
17 #include "pci.h"
18
19 static int jailhouse_pci_stub_probe(struct pci_dev *dev,
20                                     const struct pci_device_id *id)
21 {
22         dev_info(&dev->dev, "claimed for use in non-root cell\n");
23         return 0;
24 }
25
26 /**
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
37  * assigned devices.
38  * @see jailhouse_pci_claim_release
39  *
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
43  * manually.
44  */
45 static struct pci_driver jailhouse_pci_stub_driver = {
46         .name           = "jailhouse-pci-stub",
47         .id_table       = NULL,
48         .probe          = jailhouse_pci_stub_probe,
49 };
50
51 static void jailhouse_pci_add_device(const struct jailhouse_pci_device *dev)
52 {
53         int num;
54         struct pci_bus *bus;
55
56         bus = pci_find_bus(dev->domain, PCI_BUS_NUM(dev->bdf));
57         if (bus) {
58                 num = pci_scan_slot(bus, dev->bdf & 0xff);
59                 if (num) {
60                         pci_lock_rescan_remove();
61                         pci_bus_assign_resources(bus);
62                         pci_bus_add_devices(bus);
63                         pci_unlock_rescan_remove();
64                 }
65         }
66 }
67
68 static void jailhouse_pci_remove_device(const struct jailhouse_pci_device *dev)
69 {
70         struct pci_dev *l_dev;
71
72         l_dev = pci_get_bus_and_slot(PCI_BUS_NUM(dev->bdf), dev->bdf & 0xff);
73         if (l_dev)
74                 pci_stop_and_remove_bus_device_locked(l_dev);
75 }
76
77 static void jailhouse_pci_claim_release(const struct jailhouse_pci_device *dev,
78                                         unsigned int action)
79 {
80         int err;
81         struct pci_bus *bus;
82         struct pci_dev *l_dev;
83         struct device_driver *drv;
84
85         bus = pci_find_bus(dev->domain, PCI_BUS_NUM(dev->bdf));
86         if (!bus)
87                 return;
88         l_dev = pci_get_slot(bus, dev->bdf & 0xff);
89         if (!l_dev)
90                 return;
91         drv = l_dev->dev.driver;
92
93         if (action == JAILHOUSE_PCI_ACTION_CLAIM) {
94                 if (drv == &jailhouse_pci_stub_driver.driver)
95                         return;
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,
100                                     0, 0);
101                 if (err)
102                         dev_warn(&l_dev->dev, "failed to add dynamic id (%d)\n",
103                                  err);
104         } else {
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);
109         }
110 }
111
112 /**
113  * Register jailhouse as a PCI device driver so it can claim assigned devices.
114  *
115  * @return 0 on success, or error code
116  */
117 int jailhouse_pci_register(void)
118 {
119         return pci_register_driver(&jailhouse_pci_stub_driver);
120 }
121
122 /**
123  * Unegister jailhouse as a PCI device driver.
124  */
125 void jailhouse_pci_unregister(void)
126 {
127         pci_unregister_driver(&jailhouse_pci_stub_driver);
128 }
129
130 /**
131  * Apply the given action to all of the cells PCI devices matching the given
132  * type.
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_*)
136  */
137 void jailhouse_pci_do_all_devices(struct cell *cell, unsigned int type,
138                                   unsigned int action)
139 {
140         unsigned int n;
141         const struct jailhouse_pci_device *dev;
142
143         dev = cell->pci_devices;
144         for (n = cell->num_pci_devices; n > 0; n--) {
145                 if (dev->type == type) {
146                         switch(action) {
147                         case JAILHOUSE_PCI_ACTION_ADD:
148                                 jailhouse_pci_add_device(dev);
149                                 break;
150                         case JAILHOUSE_PCI_ACTION_DEL:
151                                 jailhouse_pci_remove_device(dev);
152                                 break;
153                         default:
154                                 jailhouse_pci_claim_release(dev, action);
155                         }
156                 }
157                 dev++;
158         }
159 }
160
161 int jailhouse_pci_cell_setup(struct cell *cell,
162                              const struct jailhouse_cell_desc *cell_desc)
163 {
164         if (cell_desc->num_pci_devices == 0)
165                 /* cell is zero-initialized, no need to set pci fields */
166                 return 0;
167
168         if (cell_desc->num_pci_devices >=
169             ULONG_MAX / sizeof(struct jailhouse_pci_device))
170                 return -EINVAL;
171
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)
176                 return -ENOMEM;
177
178         memcpy(cell->pci_devices,
179                jailhouse_cell_pci_devices(cell_desc),
180                sizeof(struct jailhouse_pci_device) * cell->num_pci_devices);
181
182         return 0;
183 }
184
185 void jailhouse_pci_cell_cleanup(struct cell *cell)
186 {
187         vfree(cell->pci_devices);
188 }