]> rtime.felk.cvut.cz Git - jailhouse.git/blob - hypervisor/pci.c
core: Don't match MMIO accesses if there is no MMCONFIG space
[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  *
9  * This work is licensed under the terms of the GNU GPL, version 2.  See
10  * the COPYING file in the top-level directory.
11  */
12
13 #include <jailhouse/acpi.h>
14 #include <jailhouse/mmio.h>
15 #include <jailhouse/pci.h>
16 #include <jailhouse/printk.h>
17 #include <jailhouse/utils.h>
18
19 struct acpi_mcfg_alloc {
20         u64 base_addr;
21         u16 segment_num;
22         u8 start_bus;
23         u8 end_bus;
24         u32 reserved;
25 } __attribute__((packed));
26
27 struct acpi_mcfg_table {
28         struct acpi_table_header header;
29         u8 reserved[8];
30         struct acpi_mcfg_alloc alloc_structs[];
31 } __attribute__((packed));
32
33 /* entry for PCI config space whitelist (granting access) */
34 struct pci_cfg_access {
35         u32 reg_num; /** Register number (4-byte aligned) */
36         u32 mask; /** Bit set: access allowed */
37 };
38
39 /* --- Whilelist for writing to PCI config space registers --- */
40 /* Type 1: Endpoints */
41 static const struct pci_cfg_access endpoint_write_access[] = {
42         { 0x04, 0xffffffff }, /* Command, Status */
43         { 0x0c, 0xff000000 }, /* BIST */
44         { 0x3c, 0x000000ff }, /* Int Line */
45 };
46 /* Type 2: Bridges */
47 static const struct pci_cfg_access bridge_write_access[] = {
48         { 0x04, 0xffffffff }, /* Command, Status */
49         { 0x0c, 0xff000000 }, /* BIST */
50         { 0x3c, 0xffff00ff }, /* Int Line, Bridge Control */
51 };
52
53 static void *pci_space;
54 static u64 pci_mmcfg_addr;
55 static u32 pci_mmcfg_size;
56
57 /**
58  * pci_get_assigned_device() - Look up device owned by a cell
59  * @cell:       Owning cell
60  * @bdf:        16-bit bus/device/function ID
61  *
62  * Return: Valid pointer - owns, NULL - doesn't own.
63  */
64 const struct jailhouse_pci_device *
65 pci_get_assigned_device(const struct cell *cell, u16 bdf)
66 {
67         const struct jailhouse_pci_device *device =
68                 jailhouse_cell_pci_devices(cell->config);
69         u32 n;
70
71         for (n = 0; n < cell->config->num_pci_devices; n++)
72                 if (((device[n].bus << 8) | device[n].devfn) == bdf)
73                         return &device[n];
74
75         return NULL;
76 }
77
78 /**
79  * pci_cfg_read_moderate() - Moderate config space read access
80  * @cell:       Request issuing cell
81  * @device:     The device to be accessed; if NULL, access will be emulated,
82  *              returning a value of -1
83  * @reg_num:    Register number (4-byte aligned)
84  * @bias:       Bias from register base address in bytes
85  * @size:       Access size (1, 2 or 4 bytes)
86  * @value:      Pointer to buffer to receive the emulated value if
87  *              PCI_ACCESS_EMULATE is returned
88  *
89  * Return: PCI_ACCESS_PERFORM or PCI_ACCESS_EMULATE.
90  */
91 enum pci_access
92 pci_cfg_read_moderate(const struct cell *cell,
93                       const struct jailhouse_pci_device *device, u8 reg_num,
94                       unsigned int reg_bias, unsigned int size, u32 *value)
95 {
96         if (!device) {
97                 *value = -1;
98                 return PCI_ACCESS_EMULATE;
99         }
100
101         if (reg_num < PCI_CONFIG_HEADER_SIZE)
102                 return PCI_ACCESS_PERFORM;
103
104         // HACK: permit capability access until we properly filter it
105         return PCI_ACCESS_PERFORM;
106 }
107
108 /**
109  * pci_cfg_write_moderate() - Moderate config space write access
110  * @cell:       Request issuing cell
111  * @device:     The device to be accessed; if NULL, access will be rejected
112  * @reg_num:    Register number (4-byte aligned)
113  * @bias:       Bias from register base address in bytes
114  * @size:       Access size (1, 2 or 4 bytes)
115  * @value:      Pointer to value to be written, initialized with cell value,
116  *              set to the to-be-written hardware value if PCI_ACCESS_EMULATE
117  *              is returned
118  *
119  * Return: PCI_ACCESS_REJECT, PCI_ACCESS_PERFORM or PCI_ACCESS_EMULATE.
120  */
121 enum pci_access
122 pci_cfg_write_moderate(const struct cell *cell,
123                        const struct jailhouse_pci_device *device, u8 reg_num,
124                        unsigned int reg_bias, unsigned int size, u32 *value)
125 {
126         /* initialize list to work around wrong compiler warning */
127         const struct pci_cfg_access *list = NULL;
128         unsigned int n, len = 0;
129
130         if (!device)
131                 return PCI_ACCESS_REJECT;
132
133         if (reg_num < PCI_CONFIG_HEADER_SIZE) {
134                 if (device->type == JAILHOUSE_PCI_TYPE_DEVICE) {
135                         list = endpoint_write_access;
136                         len = ARRAY_SIZE(endpoint_write_access);
137                 } else if (device->type == JAILHOUSE_PCI_TYPE_BRIDGE) {
138                         list = bridge_write_access;
139                         len = ARRAY_SIZE(bridge_write_access);
140                 }
141
142                 for (n = 0; n < len; n++) {
143                         if (list[n].reg_num == reg_num &&
144                             ((list[n].mask >> (reg_bias * 8)) &
145                              BYTE_MASK(size)) == BYTE_MASK(size))
146                                 return PCI_ACCESS_PERFORM;
147                 }
148                 return PCI_ACCESS_REJECT;
149         }
150
151         // HACK: permit capability access until we properly filter it
152         return PCI_ACCESS_PERFORM;
153 }
154
155 /**
156  * pci_init() - Initialization of PCI module
157  *
158  * Return: 0 - success, error code - if error.
159  */
160 int pci_init(void)
161 {
162         struct acpi_mcfg_table *mcfg;
163
164         mcfg = (struct acpi_mcfg_table *)acpi_find_table("MCFG", NULL);
165         if (!mcfg)
166                 return 0;
167
168         if (mcfg->header.length !=
169             sizeof(struct acpi_mcfg_table) + sizeof(struct acpi_mcfg_alloc))
170                 return -EIO;
171
172         pci_mmcfg_addr = mcfg->alloc_structs[0].base_addr;
173         pci_mmcfg_size = (mcfg->alloc_structs[0].end_bus -
174                           mcfg->alloc_structs[0].start_bus) * 256 * 4096;
175         pci_space = page_alloc(&remap_pool, pci_mmcfg_size / PAGE_SIZE);
176         if (!pci_space)
177                 return -ENOMEM;
178
179         return page_map_create(&hv_paging_structs,
180                                mcfg->alloc_structs[0].base_addr,
181                                pci_mmcfg_size, (unsigned long)pci_space,
182                                PAGE_DEFAULT_FLAGS | PAGE_FLAG_UNCACHED,
183                                PAGE_MAP_NON_COHERENT);
184 }
185
186 /**
187  * pci_mmio_access_handler() - Handler for MMIO-accesses to PCI config space
188  * @cell:       Request issuing cell
189  * @is_write:   True if write access
190  * @addr:       Address accessed
191  * @value:      Pointer to value for reading/writing
192  *
193  * Return: 1 if handled successfully, 0 if unhandled, -1 on access error
194  */
195 int pci_mmio_access_handler(const struct cell *cell, bool is_write,
196                             u64 addr, u32 *value)
197 {
198         u32 mmcfg_offset, val, reg_bias, reg_num;
199         const struct jailhouse_pci_device *device;
200         enum pci_access access;
201
202         if (!pci_space || addr < pci_mmcfg_addr ||
203             addr >= (pci_mmcfg_addr + pci_mmcfg_size - 4))
204                 return 0;
205
206         mmcfg_offset = addr - pci_mmcfg_addr;
207         reg_bias = mmcfg_offset % 4;
208         reg_num = mmcfg_offset & 0xfff;
209         device = pci_get_assigned_device(cell, mmcfg_offset >> 12);
210
211         if (is_write) {
212                 val = *value;
213                 access = pci_cfg_write_moderate(cell, device,
214                                                 reg_num - reg_bias, reg_bias,
215                                                 4, &val);
216                 if (access == PCI_ACCESS_REJECT)
217                         goto invalid_access;
218                 mmio_write32(pci_space + mmcfg_offset, val);
219         } else {
220                 access = pci_cfg_read_moderate(cell, device,
221                                                reg_num - reg_bias, reg_bias,
222                                                4, value);
223                 if (access == PCI_ACCESS_PERFORM)
224                         *value = mmio_read32(pci_space + mmcfg_offset);
225         }
226
227         return 1;
228
229 invalid_access:
230         panic_printk("FATAL: Invalid PCI MMCONFIG write, device %02x:%02x.%x, "
231                      "reg: %\n", mmcfg_offset >> 20, (mmcfg_offset >> 15) & 31,
232                      (mmcfg_offset >> 12) & 7, reg_num);
233         return -1;
234
235 }