]> rtime.felk.cvut.cz Git - jailhouse.git/blob - hypervisor/pci.c
core: Fix PCI device runtime ownership tracking
[jailhouse.git] / hypervisor / pci.c
1 /*
2  * Jailhouse, a Linux-based partitioning hypervisor
3  *
4  * Copyright (c) Siemens AG, 2014
5  *
6  * Authors:
7  *  Ivan Kolchin <ivan.kolchin@siemens.com>
8  *  Jan Kiszka <jan.kiszka@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 <jailhouse/acpi.h>
15 #include <jailhouse/control.h>
16 #include <jailhouse/mmio.h>
17 #include <jailhouse/pci.h>
18 #include <jailhouse/printk.h>
19 #include <jailhouse/utils.h>
20
21 #define PCI_CONFIG_HEADER_SIZE          0x40
22
23 struct acpi_mcfg_alloc {
24         u64 base_addr;
25         u16 segment_num;
26         u8 start_bus;
27         u8 end_bus;
28         u32 reserved;
29 } __attribute__((packed));
30
31 struct acpi_mcfg_table {
32         struct acpi_table_header header;
33         u8 reserved[8];
34         struct acpi_mcfg_alloc alloc_structs[];
35 } __attribute__((packed));
36
37 /* entry for PCI config space whitelist (granting access) */
38 struct pci_cfg_access {
39         u32 reg_num; /** Register number (4-byte aligned) */
40         u32 mask; /** Bit set: access allowed */
41 };
42
43 /* --- Whilelist for writing to PCI config space registers --- */
44 /* Type 1: Endpoints */
45 static const struct pci_cfg_access endpoint_write_access[] = {
46         { 0x04, 0xffffffff }, /* Command, Status */
47         { 0x0c, 0xff00ffff }, /* BIST, Latency Timer, Cacheline */
48         { 0x3c, 0x000000ff }, /* Int Line */
49 };
50 /* Type 2: Bridges */
51 static const struct pci_cfg_access bridge_write_access[] = {
52         { 0x04, 0xffffffff }, /* Command, Status */
53         { 0x0c, 0xff00ffff }, /* BIST, Latency Timer, Cacheline */
54         { 0x3c, 0xffff00ff }, /* Int Line, Bridge Control */
55 };
56
57 static void *pci_space;
58 static u64 pci_mmcfg_addr;
59 static u32 pci_mmcfg_size;
60 static u8 end_bus;
61
62 static void *pci_get_device_mmcfg_base(u16 bdf)
63 {
64         return pci_space + ((unsigned long)bdf << 12);
65 }
66
67 /**
68  * pci_read_config() - Read from PCI config space
69  * @bdf:        16-bit bus/device/function ID of target
70  * @address:    Config space access address
71  * @size:       Access size (1, 2 or 4 bytes)
72  *
73  * Return: read value
74  */
75 u32 pci_read_config(u16 bdf, u16 address, unsigned int size)
76 {
77         void *mmcfg_addr = pci_get_device_mmcfg_base(bdf) + address;
78
79         if (!pci_space || PCI_BUS(bdf) > end_bus)
80                 return arch_pci_read_config(bdf, address, size);
81
82         if (size == 1)
83                 return mmio_read8(mmcfg_addr);
84         else if (size == 2)
85                 return mmio_read16(mmcfg_addr);
86         else
87                 return mmio_read32(mmcfg_addr);
88 }
89
90 /**
91  * pci_write_config() - Write to PCI config space
92  * @bdf:        16-bit bus/device/function ID of target
93  * @address:    Config space access address
94  * @value:      Value to be written
95  * @size:       Access size (1, 2 or 4 bytes)
96  */
97 void pci_write_config(u16 bdf, u16 address, u32 value, unsigned int size)
98 {
99         void *mmcfg_addr = pci_get_device_mmcfg_base(bdf) + address;
100
101         if (!pci_space || PCI_BUS(bdf) > end_bus)
102                 return arch_pci_write_config(bdf, address, value, size);
103
104         if (size == 1)
105                 mmio_write8(mmcfg_addr, value);
106         else if (size == 2)
107                 mmio_write16(mmcfg_addr, value);
108         else
109                 mmio_write32(mmcfg_addr, value);
110 }
111
112 /**
113  * pci_get_assigned_device() - Look up device owned by a cell
114  * @cell:       Owning cell
115  * @bdf:        16-bit bus/device/function ID
116  *
117  * Return: Pointer to owned PCI device or NULL.
118  */
119 struct pci_device *pci_get_assigned_device(const struct cell *cell, u16 bdf)
120 {
121         const struct jailhouse_pci_device *dev_info =
122                 jailhouse_cell_pci_devices(cell->config);
123         u32 n;
124
125         /* We iterate over the static device information to increase cache
126          * locality. */
127         for (n = 0; n < cell->config->num_pci_devices; n++)
128                 if (dev_info[n].bdf == bdf)
129                         return cell->pci_devices[n].cell ?
130                                 &cell->pci_devices[n] : NULL;
131
132         return NULL;
133 }
134
135 /**
136  * pci_find_capability() - Look up capability at given config space address
137  * @device:     The device to be accessed
138  * @address:    Config space access address
139  *
140  * Return: Corresponding capability structure or NULL if none found.
141  */
142 static const struct jailhouse_pci_capability *
143 pci_find_capability(struct pci_device *device, u16 address)
144 {
145         const struct jailhouse_pci_capability *cap =
146                 jailhouse_cell_pci_caps(device->cell->config) +
147                 device->info->caps_start;
148         u32 n;
149
150         for (n = 0; n < device->info->num_caps; n++, cap++)
151                 if (cap->start <= address && cap->start + cap->len > address)
152                         return cap;
153
154         return NULL;
155 }
156
157 /**
158  * pci_cfg_read_moderate() - Moderate config space read access
159  * @device:     The device to be accessed; if NULL, access will be emulated,
160  *              returning a value of -1
161  * @address:    Config space address
162  * @size:       Access size (1, 2 or 4 bytes)
163  * @value:      Pointer to buffer to receive the emulated value if
164  *              PCI_ACCESS_DONE is returned
165  *
166  * Return: PCI_ACCESS_PERFORM or PCI_ACCESS_DONE.
167  */
168 enum pci_access pci_cfg_read_moderate(struct pci_device *device, u16 address,
169                                       unsigned int size, u32 *value)
170 {
171         const struct jailhouse_pci_capability *cap;
172
173         if (!device) {
174                 *value = -1;
175                 return PCI_ACCESS_DONE;
176         }
177
178         if (address < PCI_CONFIG_HEADER_SIZE)
179                 return PCI_ACCESS_PERFORM;
180
181         cap = pci_find_capability(device, address);
182         if (!cap)
183                 return PCI_ACCESS_PERFORM;
184
185         // TODO: Emulate MSI/MSI-X etc.
186
187         return PCI_ACCESS_PERFORM;
188 }
189
190 /**
191  * pci_cfg_write_moderate() - Moderate config space write access
192  * @device:     The device to be accessed; if NULL, access will be rejected
193  * @address:    Config space address
194  * @size:       Access size (1, 2 or 4 bytes)
195  * @value:      Value to be written
196  *
197  * Return: PCI_ACCESS_REJECT, PCI_ACCESS_PERFORM or PCI_ACCESS_DONE.
198  */
199 enum pci_access pci_cfg_write_moderate(struct pci_device *device, u16 address,
200                                        unsigned int size, u32 value)
201 {
202         const struct jailhouse_pci_capability *cap;
203         /* initialize list to work around wrong compiler warning */
204         const struct pci_cfg_access *list = NULL;
205         unsigned int n, bias_shift, len = 0;
206         u32 mask;
207
208         if (!device)
209                 return PCI_ACCESS_REJECT;
210
211         if (address < PCI_CONFIG_HEADER_SIZE) {
212                 if (device->info->type == JAILHOUSE_PCI_TYPE_DEVICE) {
213                         list = endpoint_write_access;
214                         len = ARRAY_SIZE(endpoint_write_access);
215                 } else if (device->info->type == JAILHOUSE_PCI_TYPE_BRIDGE) {
216                         list = bridge_write_access;
217                         len = ARRAY_SIZE(bridge_write_access);
218                 }
219
220                 bias_shift = (address & 0x003) * 8;
221                 mask = BYTE_MASK(size);
222
223                 for (n = 0; n < len; n++) {
224                         if (list[n].reg_num == (address & 0xffc) &&
225                             ((list[n].mask >> bias_shift) & mask) == mask)
226                                 return PCI_ACCESS_PERFORM;
227                 }
228
229                 return PCI_ACCESS_REJECT;
230         }
231
232         cap = pci_find_capability(device, address);
233         if (!cap || !(cap->flags & JAILHOUSE_PCICAPS_WRITE))
234                 return PCI_ACCESS_REJECT;
235
236         return PCI_ACCESS_PERFORM;
237 }
238
239 /**
240  * pci_init() - Initialization of PCI module
241  *
242  * Return: 0 - success, error code - if error.
243  */
244 int pci_init(void)
245 {
246         struct acpi_mcfg_table *mcfg;
247         int err;
248
249         err = pci_cell_init(&root_cell);
250         if (err)
251                 return err;
252
253         mcfg = (struct acpi_mcfg_table *)acpi_find_table("MCFG", NULL);
254         if (!mcfg)
255                 return 0;
256
257         if (mcfg->header.length !=
258             sizeof(struct acpi_mcfg_table) + sizeof(struct acpi_mcfg_alloc))
259                 return -EIO;
260
261         pci_mmcfg_addr = mcfg->alloc_structs[0].base_addr;
262         pci_mmcfg_size = (mcfg->alloc_structs[0].end_bus + 1) * 256 * 4096;
263         pci_space = page_alloc(&remap_pool, pci_mmcfg_size / PAGE_SIZE);
264         if (!pci_space)
265                 return -ENOMEM;
266
267         end_bus = mcfg->alloc_structs[0].end_bus;
268
269         return page_map_create(&hv_paging_structs,
270                                mcfg->alloc_structs[0].base_addr,
271                                pci_mmcfg_size, (unsigned long)pci_space,
272                                PAGE_DEFAULT_FLAGS | PAGE_FLAG_UNCACHED,
273                                PAGE_MAP_NON_COHERENT);
274 }
275
276 /**
277  * pci_mmio_access_handler() - Handler for MMIO-accesses to PCI config space
278  * @cell:       Request issuing cell
279  * @is_write:   True if write access
280  * @addr:       Address accessed
281  * @value:      Pointer to value for reading/writing
282  *
283  * Return: 1 if handled successfully, 0 if unhandled, -1 on access error
284  */
285 int pci_mmio_access_handler(const struct cell *cell, bool is_write,
286                             u64 addr, u32 *value)
287 {
288         u32 mmcfg_offset, reg_addr;
289         struct pci_device *device;
290         enum pci_access access;
291
292         if (!pci_space || addr < pci_mmcfg_addr ||
293             addr >= (pci_mmcfg_addr + pci_mmcfg_size - 4))
294                 return 0;
295
296         mmcfg_offset = addr - pci_mmcfg_addr;
297         reg_addr = mmcfg_offset & 0xfff;
298         device = pci_get_assigned_device(cell, mmcfg_offset >> 12);
299
300         if (is_write) {
301                 access = pci_cfg_write_moderate(device, reg_addr, 4, *value);
302                 if (access == PCI_ACCESS_REJECT)
303                         goto invalid_access;
304                 if (access == PCI_ACCESS_PERFORM)
305                         mmio_write32(pci_space + mmcfg_offset, *value);
306         } else {
307                 access = pci_cfg_read_moderate(device, reg_addr, 4, value);
308                 if (access == PCI_ACCESS_PERFORM)
309                         *value = mmio_read32(pci_space + mmcfg_offset);
310         }
311
312         return 1;
313
314 invalid_access:
315         panic_printk("FATAL: Invalid PCI MMCONFIG write, device %02x:%02x.%x, "
316                      "reg: %\n", PCI_BDF_PARAMS(mmcfg_offset >> 12), reg_addr);
317         return -1;
318
319 }
320
321 static int pci_add_device(struct cell *cell, struct pci_device *device)
322 {
323         printk("Adding PCI device %02x:%02x.%x to cell \"%s\"\n",
324                PCI_BDF_PARAMS(device->info->bdf), cell->config->name);
325         return arch_pci_add_device(cell, device);
326 }
327
328 static void pci_remove_device(struct pci_device *device)
329 {
330         printk("Removing PCI device %02x:%02x.%x from cell \"%s\"\n",
331                PCI_BDF_PARAMS(device->info->bdf), device->cell->config->name);
332         arch_pci_remove_device(device);
333 }
334
335 int pci_cell_init(struct cell *cell)
336 {
337         unsigned long array_size = PAGE_ALIGN(cell->config->num_pci_devices *
338                                               sizeof(struct pci_device));
339         const struct jailhouse_pci_device *dev_infos =
340                 jailhouse_cell_pci_devices(cell->config);
341         struct pci_device *device, *root_device;
342         unsigned int ndev;
343         int err;
344
345         cell->pci_devices = page_alloc(&mem_pool, array_size / PAGE_SIZE);
346         if (!cell->pci_devices)
347                 return -ENOMEM;
348
349         /*
350          * We order device states in the same way as the static information
351          * so that we can use the index of the latter to find the former. For
352          * the other way around and for obtaining the owner cell, we use more
353          * handy pointers. The cell pointer also encodes active ownership.
354          */
355         for (ndev = 0; ndev < cell->config->num_pci_devices; ndev++) {
356                 device = &cell->pci_devices[ndev];
357                 device->info = &dev_infos[ndev];
358
359                 root_device = pci_get_assigned_device(&root_cell,
360                                                       dev_infos[ndev].bdf);
361                 if (root_device) {
362                         pci_remove_device(root_device);
363                         root_device->cell = NULL;
364                 }
365
366                 err = pci_add_device(cell, device);
367                 if (err) {
368                         pci_cell_exit(cell);
369                         return err;
370                 }
371
372                 device->cell = cell;
373         }
374
375         return 0;
376 }
377
378 static void pci_return_device_to_root_cell(struct pci_device *device)
379 {
380         struct pci_device *root_device = root_cell.pci_devices;
381         unsigned int n;
382
383         for (n = 0; n < root_cell.config->num_pci_devices; n++, root_device++)
384                 if (root_device->info->domain == device->info->domain &&
385                     root_device->info->bdf == device->info->bdf) {
386                         if (pci_add_device(&root_cell, root_device) < 0)
387                                 printk("WARNING: Failed to re-assign PCI "
388                                        "device to root cell\n");
389                         else
390                                 root_device->cell = &root_cell;
391                         break;
392                 }
393 }
394
395 void pci_cell_exit(struct cell *cell)
396 {
397         unsigned long array_size = PAGE_ALIGN(cell->config->num_pci_devices *
398                                               sizeof(struct pci_device));
399         unsigned int n;
400
401         /*
402          * Do not destroy the root cell. We will shut down the complete
403          * hypervisor instead.
404          */
405         if (cell == &root_cell)
406                 return;
407
408         for (n = 0; n < cell->config->num_pci_devices; n++) {
409                 if (!cell->pci_devices[n].cell)
410                         continue;
411                 pci_remove_device(&cell->pci_devices[n]);
412                 pci_return_device_to_root_cell(&cell->pci_devices[n]);
413         }
414
415         page_free(&mem_pool, cell->pci_devices, array_size / PAGE_SIZE);
416 }