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