blob: d4f873c6624cde6eb63d659490b8fd606350188d [file] [log] [blame]
TrustworthySystems2f929802014-07-22 15:38:57 +10001/*
Anna Lyons92143412017-06-05 08:29:55 +10002 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
TrustworthySystems2f929802014-07-22 15:38:57 +10005 *
6 * This software may be distributed and modified according to the terms of
7 * the GNU General Public License version 2. Note that NO WARRANTY is provided.
8 * See "LICENSE_GPLv2.txt" for details.
9 *
Anna Lyons92143412017-06-05 08:29:55 +100010 * @TAG(DATA61_GPL)
TrustworthySystems2f929802014-07-22 15:38:57 +100011 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <sel4/sel4.h>
17
18#include <pci/virtual_pci.h>
19#include <pci/helper.h>
20
21#include "vmm/driver/pci_helper.h"
22
23typedef struct pci_bar_emulation {
24 vmm_pci_entry_t passthrough;
25 int num_bars;
26 vmm_pci_bar_t bars[6];
27 uint32_t bar_writes[6];
28} pci_bar_emulation_t;
29
30typedef struct pci_irq_emulation {
31 vmm_pci_entry_t passthrough;
32 int irq;
33} pci_irq_emulation_t;
34
35typedef struct pci_passthrough_device {
36 /* The address on the host system of this device */
37 vmm_pci_address_t addr;
38 /* Ops for accessing config space */
39 vmm_pci_config_t config;
40} pci_passthrough_device_t;
41
42typedef struct pci_cap_emulation {
43 vmm_pci_entry_t passthrough;
44 int num_caps;
45 uint8_t *caps;
46 int num_ignore;
47 uint8_t *ignore_start;
48 uint8_t *ignore_end;
49} pci_cap_emulation_t;
50
51int vmm_pci_mem_device_read(void *cookie, int offset, int size, uint32_t *result) {
52 if (offset < 0) {
Anna Lyons67c869c2016-03-30 11:16:41 +110053 ZF_LOGE("Offset should not be negative");
TrustworthySystems2f929802014-07-22 15:38:57 +100054 return -1;
55 }
56 if (offset + size >= 0x40) {
Anna Lyons67c869c2016-03-30 11:16:41 +110057 ZF_LOGI("Indexing capability space not yet supported, returning 0");
TrustworthySystems2f929802014-07-22 15:38:57 +100058 *result = 0;
59 return 0;
60 }
61 *result = 0;
62 memcpy(result, cookie + offset, size);
63 return 0;
64}
65
66int vmm_pci_entry_ignore_write(void *cookie, int offset, int size, uint32_t value) {
67 return 0;
68}
69
70void define_pci_host_bridge(vmm_pci_device_def_t *bridge) {
71 *bridge = (vmm_pci_device_def_t) {
72 .vendor_id = 0x5E14,
73 .device_id = 0x42,
74 .command = 0,
75 .status = 0,
76 .revision_id = 0x1,
77 .prog_if = 0,
78 .subclass = 0x0,
79 .class_code = 0x06,
80 .cache_line_size = 0,
81 .latency_timer = 0,
82 .header_type = 0x00,
83 .bist = 0,
84 .bar0 = 0,
85 .bar1 = 0,
86 .bar2 = 0,
87 .bar3 = 0,
88 .bar4 = 0,
89 .bar5 = 0,
90 .cardbus = 0,
91 .subsystem_vendor_id = 0,
92 .subsystem_id = 0,
93 .expansion_rom = 0,
94 .caps_pointer = 0,
95 .reserved1 = 0,
96 .reserved2 = 0,
97 .reserved3 = 0,
98 .interrupt_line = 0,
99 .interrupt_pin = 0,
100 .min_grant = 0,
101 .max_latency = 0,
102 .caps_len = 0,
103 .caps = NULL
104 };
105}
106
107static int passthrough_pci_config_ioread(void *cookie, int offset, int size, uint32_t *result) {
108 pci_passthrough_device_t *dev = (pci_passthrough_device_t*)cookie;
109 switch(size) {
110 case 1:
111 *result = dev->config.ioread8(dev->config.cookie, dev->addr, offset);
112 break;
113 case 2:
114 *result = dev->config.ioread16(dev->config.cookie, dev->addr, offset);
115 break;
116 case 4:
117 *result = dev->config.ioread32(dev->config.cookie, dev->addr, offset);
118 break;
119 default:
120 assert(!"Invalid size");
121 }
122 return 0;
123}
124
125static int passthrough_pci_config_iowrite(void *cookie, int offset, int size, uint32_t val) {
126 pci_passthrough_device_t *dev = (pci_passthrough_device_t*)cookie;
127 switch(size) {
128 case 1:
129 dev->config.iowrite8(dev->config.cookie, dev->addr, offset, val);
130 break;
131 case 2:
132 dev->config.iowrite16(dev->config.cookie, dev->addr, offset, val);
133 break;
134 case 4:
135 dev->config.iowrite32(dev->config.cookie, dev->addr, offset, val);
136 break;
137 default:
138 assert(!"Invalid size");
139 }
140 return 0;
141}
142
143static int pci_bar_emul_check_range(unsigned int offset, unsigned int size) {
144 if (offset < PCI_BASE_ADDRESS_0 || offset + size > PCI_BASE_ADDRESS_5 + 4) {
145 return 1;
146 }
147 return 0;
148}
149
150static uint32_t pci_make_bar(pci_bar_emulation_t *emul, int bar) {
151 if (bar >= emul->num_bars) {
152 return 0;
153 }
154 uint32_t raw = 0;
155 raw |= emul->bars[bar].address;
156 if (!emul->bars[bar].ismem) {
157 raw |= 1;
158 } else {
159 if (emul->bars[bar].prefetchable) {
160 raw |= BIT(3);
161 }
162 }
163 raw |= (emul->bar_writes[bar] & ~MASK(emul->bars[bar].size_bits));
164 return raw;
165}
166
167static int pci_irq_emul_read(void *cookie, int offset, int size, uint32_t *result) {
168 pci_irq_emulation_t *emul = (pci_irq_emulation_t*)cookie;
169 if(offset <= PCI_INTERRUPT_LINE && offset + size > PCI_INTERRUPT_LINE) {
170 /* do the regular read, then patch in our value */
171 int ret = emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
172 if (ret) {
173 return ret;
174 }
175 int bit_offset = (PCI_INTERRUPT_LINE - offset) * 8;
176 *result &= ~(MASK(8) << bit_offset);
177 *result |= (emul->irq << bit_offset);
178 return 0;
179 } else {
180 return emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
181 }
182}
183
184static int pci_irq_emul_write(void *cookie, int offset, int size, uint32_t value) {
185 pci_irq_emulation_t *emul = (pci_irq_emulation_t*)cookie;
186 if (offset == PCI_INTERRUPT_LINE && size == 1) {
187 /* ignore */
188 return 0;
189 } else if(offset < PCI_INTERRUPT_LINE && offset + size >= PCI_INTERRUPT_LINE) {
190 assert(!"Guest writing PCI configuration in an unsupported way");
191 return -1;
192 } else {
193 return emul->passthrough.iowrite(emul->passthrough.cookie, offset, size, value);
194 }
195}
196
197static int pci_bar_emul_read(void *cookie, int offset, int size, uint32_t *result) {
198 pci_bar_emulation_t *emul = (pci_bar_emulation_t*)cookie;
199 if (pci_bar_emul_check_range(offset, size)) {
200 return emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
201 }
202 /* Construct the bar value */
203 int bar = (offset - PCI_BASE_ADDRESS_0) / 4;
204 int bar_offset = offset & 3;
205 uint32_t bar_raw = pci_make_bar(emul, bar);
206 char *barp = (char*)&bar_raw;
207 *result = 0;
208 memcpy(result, barp + bar_offset, size);
209 return 0;
210}
211
212static int pci_bar_emul_write(void *cookie, int offset, int size, uint32_t value) {
213 pci_bar_emulation_t *emul = (pci_bar_emulation_t*)cookie;
214 if (pci_bar_emul_check_range(offset, size)) {
215 return emul->passthrough.iowrite(emul->passthrough.cookie, offset, size, value);
216 }
217 /* Construct the bar value */
218 int bar = (offset - PCI_BASE_ADDRESS_0) / 4;
219 int bar_offset = offset & 3;
220 char *barp = (char*)&emul->bar_writes[bar];
221 memcpy(barp + bar_offset, &value, size);
222 return 0;
223}
224
225vmm_pci_entry_t vmm_pci_create_bar_emulation(vmm_pci_entry_t existing, int num_bars, vmm_pci_bar_t *bars) {
226 pci_bar_emulation_t *bar_emul = malloc(sizeof(*bar_emul));
227 assert(bar_emul);
228 memcpy(bar_emul->bars, bars, sizeof(vmm_pci_bar_t) * num_bars);
229 bar_emul->passthrough = existing;
230 bar_emul->num_bars = num_bars;
231 memset(bar_emul->bar_writes, 0, sizeof(bar_emul->bar_writes));
232 return (vmm_pci_entry_t) {.cookie = bar_emul, .ioread = pci_bar_emul_read, .iowrite = pci_bar_emul_write};
233}
234
235vmm_pci_entry_t vmm_pci_create_irq_emulation(vmm_pci_entry_t existing, int irq) {
236 pci_irq_emulation_t *irq_emul = malloc(sizeof(*irq_emul));
237 assert(irq_emul);
238 irq_emul->passthrough = existing;
239 irq_emul->irq = irq;
240 return (vmm_pci_entry_t) {.cookie = irq_emul, .ioread = pci_irq_emul_read, .iowrite = pci_irq_emul_write};
241}
242
243vmm_pci_entry_t vmm_pci_create_passthrough(vmm_pci_address_t addr, vmm_pci_config_t config) {
244 pci_passthrough_device_t *dev = malloc(sizeof(*dev));
245 assert(dev);
246 dev->addr = addr;
247 dev->config = config;
Anna Lyons67c869c2016-03-30 11:16:41 +1100248 ZF_LOGI("Creating passthrough device for %02x:%02x.%d", addr.bus, addr.dev, addr.fun);
TrustworthySystems2f929802014-07-22 15:38:57 +1000249 return (vmm_pci_entry_t){.cookie = dev, .ioread = passthrough_pci_config_ioread, .iowrite = passthrough_pci_config_iowrite};
250}
251
252int vmm_pci_helper_map_bars(vmm_t *vmm, libpci_device_iocfg_t *cfg, vmm_pci_bar_t *bars) {
253 int i;
254 int bar = 0;
255 for (i = 0; i < 6; i++) {
256 if (cfg->base_addr[i] == 0) {
257 continue;
258 }
259 size_t size = cfg->base_addr_size[i];
260 assert(size != 0);
261 int size_bits = 31 - CLZ(size);
262 if (BIT(size_bits) != size) {
Adrian Danisef656e02016-12-09 09:08:00 +1100263 ZF_LOGE("PCI bar is not power of 2 size (%zu)", size);
TrustworthySystems2f929802014-07-22 15:38:57 +1000264 return -1;
265 }
266 bars[bar].size_bits = size_bits;
267 if (cfg->base_addr_space[i] == PCI_BASE_ADDRESS_SPACE_MEMORY) {
268 /* Need to map into the VMM. Make sure it is aligned */
269 uintptr_t addr = vmm_map_guest_device(vmm, cfg->base_addr[i], size, BIT(size_bits));
270 if(addr == 0) {
Adrian Danisef656e02016-12-09 09:08:00 +1100271 ZF_LOGE("Failed to map PCI bar %p size %zu", (void*)(uintptr_t)cfg->base_addr[i], size);
TrustworthySystems2f929802014-07-22 15:38:57 +1000272 return -1;
273 }
274 bars[bar].ismem = 1;
275 bars[bar].address = addr;
276 bars[bar].prefetchable = cfg->base_addr_prefetchable[i];
277 } else {
278 /* Need to add the IO port range */
279 int error = vmm_io_port_add_passthrough(&vmm->io_port, cfg->base_addr[i], cfg->base_addr[i] + size - 1, "PCI Passthrough Device");
280 if (error) {
281 return error;
282 }
283 bars[bar].ismem=0;
284 bars[bar].address = cfg->base_addr[i];
285 bars[bar].prefetchable = 0;
286 }
287 bar++;
288 }
289 return bar;
290}
291
292static int pci_cap_emul_read(void *cookie, int offset, int size, uint32_t *result) {
293 pci_cap_emulation_t *emul = (pci_cap_emulation_t*)cookie;
294 if(offset <= PCI_STATUS && offset + size > PCI_STATUS) {
295 /* do the regular read, then patch in our value */
296 int ret = emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
297 if (ret) {
298 return ret;
299 }
300 int bit_offset = (PCI_STATUS - offset) * 8;
301 *result &= ~(PCI_STATUS_CAP_LIST << bit_offset);
302 if (emul->num_caps > 0) {
303 *result |= (PCI_STATUS_CAP_LIST << bit_offset);
304 }
305 return 0;
306 } else if(offset <= PCI_CAPABILITY_LIST && offset + size > PCI_CAPABILITY_LIST) {
307 /* do the regular read, then patch in our value */
308 int ret = emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
309 if (ret) {
310 return ret;
311 }
312 int bit_offset = (PCI_CAPABILITY_LIST - offset) * 8;
313 *result &= ~(MASK(8) << bit_offset);
314 if (emul->num_caps > 0) {
315 *result |= (emul->caps[0] << bit_offset);
316 }
317 return 0;
318 }
319 /* see if we are reading from any location that we would prefer not to */
320 int i;
321 for (i = 0; i < emul->num_ignore; i++) {
322 if (offset <= emul->ignore_start[i] && offset+size > emul->ignore_end[i]) {
323 /* who cares about the size, just ignore everything */
Anna Lyons67c869c2016-03-30 11:16:41 +1100324 ZF_LOGI("Attempted read at 0x%x of size %d from region 0x%x-0x%x", offset, size, emul->ignore_start[i], emul->ignore_end[i]);
TrustworthySystems2f929802014-07-22 15:38:57 +1000325 *result = 0;
326 return 0;
327 }
328 }
329 /* See if we are reading a capability index */
330 for (i = 0; i < emul->num_caps; i++) {
331 if (offset <= emul->caps[i] + 1 && offset + size > emul->caps[i] + 1) {
332 /* do the regular read, then patch in our value */
333 int ret = emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
334 if (ret) {
335 return ret;
336 }
337 int bit_offset = (emul->caps[i] + 1 - offset) * 8;
338 *result &= ~(MASK(8) << bit_offset);
339 if (i + 1 < emul->num_caps) {
340 *result |= (emul->caps[i + 1] << bit_offset);
341 }
342 return 0;
343 }
344 }
345 /* Pass through whatever is left */
346 return emul->passthrough.ioread(emul->passthrough.cookie, offset, size, result);
347}
348
349static int pci_cap_emul_write(void *cookie, int offset, int size, uint32_t value) {
350 pci_cap_emulation_t *emul = (pci_cap_emulation_t*)cookie;
351 /* Prevents writes to our ignored ranges. but let anything else through */
352 int i;
353 for (i = 0; i < emul->num_ignore; i++) {
354 if (offset <= emul->ignore_start[i] && offset+size > emul->ignore_end[i]) {
355 /* who cares about the size, just ignore everything */
Anna Lyons67c869c2016-03-30 11:16:41 +1100356 ZF_LOGI("Attempted write at 0x%x of size %d from region 0x%x-0x%x", offset, size, emul->ignore_start[i], emul->ignore_end[i]);
TrustworthySystems2f929802014-07-22 15:38:57 +1000357 return 0;
358 }
359 }
360 return emul->passthrough.iowrite(emul->passthrough.cookie, offset, size, value);
361}
362
363vmm_pci_entry_t vmm_pci_create_cap_emulation(vmm_pci_entry_t existing, int num_caps, uint8_t *caps, int num_ranges, uint8_t *range_starts, uint8_t *range_ends) {
364 pci_cap_emulation_t *emul = malloc(sizeof(*emul));
365 emul->passthrough = existing;
366 assert(emul);
367 emul->num_caps = num_caps;
368 emul->caps = malloc(sizeof(uint8_t) * num_caps);
369 assert(emul->caps);
370 memcpy(emul->caps, caps, sizeof(uint8_t) * num_caps);
371 emul->num_ignore = num_ranges;
372 emul->ignore_start = malloc(sizeof(uint8_t) * num_ranges);
373 assert(emul->ignore_start);
374 emul->ignore_end = malloc(sizeof(uint8_t) * num_ranges);
375 assert(emul->ignore_end);
376 memcpy(emul->ignore_start, range_starts, sizeof(uint8_t) * num_ranges);
377 memcpy(emul->ignore_end, range_ends, sizeof(uint8_t) * num_ranges);
378 return (vmm_pci_entry_t) {.cookie = emul, .ioread = pci_cap_emul_read, .iowrite = pci_cap_emul_write};
379}
380
381#define MAX_CAPS 256
382
383vmm_pci_entry_t vmm_pci_no_msi_cap_emulation(vmm_pci_entry_t existing) {
384 uint32_t value;
Adrian Danis56cb8a42017-04-18 17:11:10 +1000385 int UNUSED error;
TrustworthySystems2f929802014-07-22 15:38:57 +1000386 /* Ensure this is a type 0 device */
387 value = 0;
388 error = existing.ioread(existing.cookie, PCI_HEADER_TYPE, 1, &value);
389 assert(!error);
390 assert( (value & (~BIT(7))) == PCI_HEADER_TYPE_NORMAL);
391 /* Check if it has capability space */
392 error = existing.ioread(existing.cookie, PCI_STATUS, 1, &value);
393 assert(!error);
394 if (! (value & PCI_STATUS_CAP_LIST)) {
395 return existing;
396 }
397 /* First we need to scan the capability space, and detect any PCI caps
398 * while we're at it */
399 int num_caps;
400 uint8_t caps[MAX_CAPS];
401 int num_ignore;
402 uint8_t ignore_start[2];
403 uint8_t ignore_end[2];
TrustworthySystems2f929802014-07-22 15:38:57 +1000404 error = existing.ioread(existing.cookie, PCI_CAPABILITY_LIST, 1, &value);
405 assert(!error);
406 /* Mask off the bottom 2 bits, which are reserved */
407 value &= ~MASK(2);
408 num_caps = 0;
409 num_ignore = 0;
410 while (value != 0) {
411 uint32_t cap_type = 0;
412 error = existing.ioread(existing.cookie, value, 1, &cap_type);
413 assert(!error);
414 if (cap_type == PCI_CAP_ID_MSI) {
415 assert(num_ignore < 2);
416 ignore_start[num_ignore] = value;
417 ignore_end[num_ignore] = value + 20;
418 num_ignore++;
419 } else if (cap_type == PCI_CAP_ID_MSIX) {
420 ignore_start[num_ignore] = value;
421 ignore_end[num_ignore] = value + 8;
422 num_ignore++;
423 } else {
424 assert(num_caps < MAX_CAPS);
425 caps[num_caps] = (uint8_t)value;
426 num_caps++;
427 }
428 error = existing.ioread(existing.cookie, value + 1, 1, &value);
429 assert(!error);
430 }
431 if (num_ignore > 0) {
432 return vmm_pci_create_cap_emulation(existing, num_caps, caps, num_ignore, ignore_start, ignore_end);
433 } else {
434 return existing;
435 }
436}