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