]> rtime.felk.cvut.cz Git - jailhouse.git/blob - hypervisor/pci.c
core: Pass combined register address to pci_cfg_read/write_moderate
[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 #define PCI_CONFIG_HEADER_SIZE          0x40
20
21 struct acpi_mcfg_alloc {
22         u64 base_addr;
23         u16 segment_num;
24         u8 start_bus;
25         u8 end_bus;
26         u32 reserved;
27 } __attribute__((packed));
28
29 struct acpi_mcfg_table {
30         struct acpi_table_header header;
31         u8 reserved[8];
32         struct acpi_mcfg_alloc alloc_structs[];
33 } __attribute__((packed));
34
35 /* entry for PCI config space whitelist (granting access) */
36 struct pci_cfg_access {
37         u32 reg_num; /** Register number (4-byte aligned) */
38         u32 mask; /** Bit set: access allowed */
39 };
40
41 /* --- Whilelist for writing to PCI config space registers --- */
42 /* Type 1: Endpoints */
43 static const struct pci_cfg_access endpoint_write_access[] = {
44         { 0x04, 0xffffffff }, /* Command, Status */
45         { 0x0c, 0xff00ffff }, /* BIST, Latency Timer, Cacheline */
46         { 0x3c, 0x000000ff }, /* Int Line */
47 };
48 /* Type 2: Bridges */
49 static const struct pci_cfg_access bridge_write_access[] = {
50         { 0x04, 0xffffffff }, /* Command, Status */
51         { 0x0c, 0xff00ffff }, /* BIST, Latency Timer, Cacheline */
52         { 0x3c, 0xffff00ff }, /* Int Line, Bridge Control */
53 };
54
55 static void *pci_space;
56 static u64 pci_mmcfg_addr;
57 static u32 pci_mmcfg_size;
58
59 /**
60  * pci_get_assigned_device() - Look up device owned by a cell
61  * @cell:       Owning cell
62  * @bdf:        16-bit bus/device/function ID
63  *
64  * Return: Valid pointer - owns, NULL - doesn't own.
65  */
66 const struct jailhouse_pci_device *
67 pci_get_assigned_device(const struct cell *cell, u16 bdf)
68 {
69         const struct jailhouse_pci_device *device =
70                 jailhouse_cell_pci_devices(cell->config);
71         u32 n;
72
73         for (n = 0; n < cell->config->num_pci_devices; n++)
74                 if (device[n].bdf == bdf)
75                         return &device[n];
76
77         return NULL;
78 }
79
80 /**
81  * pci_find_capability() - Look up capability at given config space address
82  * @cell:       Device owning cell
83  * @device:     The device to be accessed
84  * @address:    Config space access address
85  *
86  * Return: Corresponding capability structure or NULL if none found.
87  */
88 static const struct jailhouse_pci_capability *
89 pci_find_capability(const struct cell *cell,
90                     const struct jailhouse_pci_device *device, u16 address)
91 {
92         const struct jailhouse_pci_capability *cap =
93                 jailhouse_cell_pci_caps(cell->config) + device->caps_start;
94         u32 n;
95
96         for (n = 0; n < device->num_caps; n++, cap++)
97                 if (cap->start <= address && cap->start + cap->len > address)
98                         return cap;
99
100         return NULL;
101 }
102
103 /**
104  * pci_cfg_read_moderate() - Moderate config space read access
105  * @cell:       Request issuing cell
106  * @device:     The device to be accessed; if NULL, access will be emulated,
107  *              returning a value of -1
108  * @address:    Config space address
109  * @size:       Access size (1, 2 or 4 bytes)
110  * @value:      Pointer to buffer to receive the emulated value if
111  *              PCI_ACCESS_EMULATE is returned
112  *
113  * Return: PCI_ACCESS_PERFORM or PCI_ACCESS_EMULATE.
114  */
115 enum pci_access
116 pci_cfg_read_moderate(const struct cell *cell,
117                       const struct jailhouse_pci_device *device, u16 address,
118                       unsigned int size, u32 *value)
119 {
120         const struct jailhouse_pci_capability *cap;
121
122         if (!device) {
123                 *value = -1;
124                 return PCI_ACCESS_EMULATE;
125         }
126
127         if (address < PCI_CONFIG_HEADER_SIZE)
128                 return PCI_ACCESS_PERFORM;
129
130         cap = pci_find_capability(cell, device, address);
131         if (!cap)
132                 return PCI_ACCESS_PERFORM;
133
134         // TODO: Emulate MSI/MSI-X etc.
135
136         return PCI_ACCESS_PERFORM;
137 }
138
139 /**
140  * pci_cfg_write_moderate() - Moderate config space write access
141  * @cell:       Request issuing cell
142  * @device:     The device to be accessed; if NULL, access will be rejected
143  * @address:    Config space address
144  * @size:       Access size (1, 2 or 4 bytes)
145  * @value:      Pointer to value to be written, initialized with cell value,
146  *              set to the to-be-written hardware value if PCI_ACCESS_EMULATE
147  *              is returned
148  *
149  * Return: PCI_ACCESS_REJECT, PCI_ACCESS_PERFORM or PCI_ACCESS_EMULATE.
150  */
151 enum pci_access
152 pci_cfg_write_moderate(const struct cell *cell,
153                        const struct jailhouse_pci_device *device, u16 address,
154                        unsigned int size, u32 *value)
155 {
156         const struct jailhouse_pci_capability *cap;
157         /* initialize list to work around wrong compiler warning */
158         const struct pci_cfg_access *list = NULL;
159         unsigned int n, bias_shift, len = 0;
160         u32 mask;
161
162         if (!device)
163                 return PCI_ACCESS_REJECT;
164
165         if (address < PCI_CONFIG_HEADER_SIZE) {
166                 if (device->type == JAILHOUSE_PCI_TYPE_DEVICE) {
167                         list = endpoint_write_access;
168                         len = ARRAY_SIZE(endpoint_write_access);
169                 } else if (device->type == JAILHOUSE_PCI_TYPE_BRIDGE) {
170                         list = bridge_write_access;
171                         len = ARRAY_SIZE(bridge_write_access);
172                 }
173
174                 bias_shift = (address & 0x003) * 8;
175                 mask = BYTE_MASK(size);
176
177                 for (n = 0; n < len; n++) {
178                         if (list[n].reg_num == (address & 0xffc) &&
179                             ((list[n].mask >> bias_shift) & mask) == mask)
180                                 return PCI_ACCESS_PERFORM;
181                 }
182
183                 return PCI_ACCESS_REJECT;
184         }
185
186         cap = pci_find_capability(cell, device, address);
187         if (!cap || !(cap->flags & JAILHOUSE_PCICAPS_WRITE))
188                 return PCI_ACCESS_REJECT;
189
190         return PCI_ACCESS_PERFORM;
191 }
192
193 /**
194  * pci_init() - Initialization of PCI module
195  *
196  * Return: 0 - success, error code - if error.
197  */
198 int pci_init(void)
199 {
200         struct acpi_mcfg_table *mcfg;
201
202         mcfg = (struct acpi_mcfg_table *)acpi_find_table("MCFG", NULL);
203         if (!mcfg)
204                 return 0;
205
206         if (mcfg->header.length !=
207             sizeof(struct acpi_mcfg_table) + sizeof(struct acpi_mcfg_alloc))
208                 return -EIO;
209
210         pci_mmcfg_addr = mcfg->alloc_structs[0].base_addr;
211         pci_mmcfg_size = (mcfg->alloc_structs[0].end_bus + 1) * 256 * 4096;
212         pci_space = page_alloc(&remap_pool, pci_mmcfg_size / PAGE_SIZE);
213         if (!pci_space)
214                 return -ENOMEM;
215
216         return page_map_create(&hv_paging_structs,
217                                mcfg->alloc_structs[0].base_addr,
218                                pci_mmcfg_size, (unsigned long)pci_space,
219                                PAGE_DEFAULT_FLAGS | PAGE_FLAG_UNCACHED,
220                                PAGE_MAP_NON_COHERENT);
221 }
222
223 /**
224  * pci_mmio_access_handler() - Handler for MMIO-accesses to PCI config space
225  * @cell:       Request issuing cell
226  * @is_write:   True if write access
227  * @addr:       Address accessed
228  * @value:      Pointer to value for reading/writing
229  *
230  * Return: 1 if handled successfully, 0 if unhandled, -1 on access error
231  */
232 int pci_mmio_access_handler(const struct cell *cell, bool is_write,
233                             u64 addr, u32 *value)
234 {
235         u32 mmcfg_offset, val, reg_addr;
236         const struct jailhouse_pci_device *device;
237         enum pci_access access;
238
239         if (!pci_space || addr < pci_mmcfg_addr ||
240             addr >= (pci_mmcfg_addr + pci_mmcfg_size - 4))
241                 return 0;
242
243         mmcfg_offset = addr - pci_mmcfg_addr;
244         reg_addr = mmcfg_offset & 0xfff;
245         device = pci_get_assigned_device(cell, mmcfg_offset >> 12);
246
247         if (is_write) {
248                 val = *value;
249                 access = pci_cfg_write_moderate(cell, device, reg_addr, 4,
250                                                 &val);
251                 if (access == PCI_ACCESS_REJECT)
252                         goto invalid_access;
253                 mmio_write32(pci_space + mmcfg_offset, val);
254         } else {
255                 access = pci_cfg_read_moderate(cell, device, reg_addr, 4,
256                                                value);
257                 if (access == PCI_ACCESS_PERFORM)
258                         *value = mmio_read32(pci_space + mmcfg_offset);
259         }
260
261         return 1;
262
263 invalid_access:
264         panic_printk("FATAL: Invalid PCI MMCONFIG write, device %02x:%02x.%x, "
265                      "reg: %\n", PCI_BDF_PARAMS(mmcfg_offset >> 12), reg_addr);
266         return -1;
267
268 }