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