root / hw / xen_pt.c @ 1de7afc9
History | View | Annotate | Download (26.2 kB)
1 |
/*
|
---|---|
2 |
* Copyright (c) 2007, Neocleus Corporation.
|
3 |
* Copyright (c) 2007, Intel Corporation.
|
4 |
*
|
5 |
* This work is licensed under the terms of the GNU GPL, version 2. See
|
6 |
* the COPYING file in the top-level directory.
|
7 |
*
|
8 |
* Alex Novik <alex@neocleus.com>
|
9 |
* Allen Kay <allen.m.kay@intel.com>
|
10 |
* Guy Zana <guy@neocleus.com>
|
11 |
*
|
12 |
* This file implements direct PCI assignment to a HVM guest
|
13 |
*/
|
14 |
|
15 |
/*
|
16 |
* Interrupt Disable policy:
|
17 |
*
|
18 |
* INTx interrupt:
|
19 |
* Initialize(register_real_device)
|
20 |
* Map INTx(xc_physdev_map_pirq):
|
21 |
* <fail>
|
22 |
* - Set real Interrupt Disable bit to '1'.
|
23 |
* - Set machine_irq and assigned_device->machine_irq to '0'.
|
24 |
* * Don't bind INTx.
|
25 |
*
|
26 |
* Bind INTx(xc_domain_bind_pt_pci_irq):
|
27 |
* <fail>
|
28 |
* - Set real Interrupt Disable bit to '1'.
|
29 |
* - Unmap INTx.
|
30 |
* - Decrement xen_pt_mapped_machine_irq[machine_irq]
|
31 |
* - Set assigned_device->machine_irq to '0'.
|
32 |
*
|
33 |
* Write to Interrupt Disable bit by guest software(xen_pt_cmd_reg_write)
|
34 |
* Write '0'
|
35 |
* - Set real bit to '0' if assigned_device->machine_irq isn't '0'.
|
36 |
*
|
37 |
* Write '1'
|
38 |
* - Set real bit to '1'.
|
39 |
*
|
40 |
* MSI interrupt:
|
41 |
* Initialize MSI register(xen_pt_msi_setup, xen_pt_msi_update)
|
42 |
* Bind MSI(xc_domain_update_msi_irq)
|
43 |
* <fail>
|
44 |
* - Unmap MSI.
|
45 |
* - Set dev->msi->pirq to '-1'.
|
46 |
*
|
47 |
* MSI-X interrupt:
|
48 |
* Initialize MSI-X register(xen_pt_msix_update_one)
|
49 |
* Bind MSI-X(xc_domain_update_msi_irq)
|
50 |
* <fail>
|
51 |
* - Unmap MSI-X.
|
52 |
* - Set entry->pirq to '-1'.
|
53 |
*/
|
54 |
|
55 |
#include <sys/ioctl.h> |
56 |
|
57 |
#include "pci/pci.h" |
58 |
#include "xen.h" |
59 |
#include "xen_backend.h" |
60 |
#include "xen_pt.h" |
61 |
#include "qemu/range.h" |
62 |
#include "exec/address-spaces.h" |
63 |
|
64 |
#define XEN_PT_NR_IRQS (256) |
65 |
static uint8_t xen_pt_mapped_machine_irq[XEN_PT_NR_IRQS] = {0}; |
66 |
|
67 |
void xen_pt_log(const PCIDevice *d, const char *f, ...) |
68 |
{ |
69 |
va_list ap; |
70 |
|
71 |
va_start(ap, f); |
72 |
if (d) {
|
73 |
fprintf(stderr, "[%02x:%02x.%d] ", pci_bus_num(d->bus),
|
74 |
PCI_SLOT(d->devfn), PCI_FUNC(d->devfn)); |
75 |
} |
76 |
vfprintf(stderr, f, ap); |
77 |
va_end(ap); |
78 |
} |
79 |
|
80 |
/* Config Space */
|
81 |
|
82 |
static int xen_pt_pci_config_access_check(PCIDevice *d, uint32_t addr, int len) |
83 |
{ |
84 |
/* check offset range */
|
85 |
if (addr >= 0xFF) { |
86 |
XEN_PT_ERR(d, "Failed to access register with offset exceeding 0xFF. "
|
87 |
"(addr: 0x%02x, len: %d)\n", addr, len);
|
88 |
return -1; |
89 |
} |
90 |
|
91 |
/* check read size */
|
92 |
if ((len != 1) && (len != 2) && (len != 4)) { |
93 |
XEN_PT_ERR(d, "Failed to access register with invalid access length. "
|
94 |
"(addr: 0x%02x, len: %d)\n", addr, len);
|
95 |
return -1; |
96 |
} |
97 |
|
98 |
/* check offset alignment */
|
99 |
if (addr & (len - 1)) { |
100 |
XEN_PT_ERR(d, "Failed to access register with invalid access size "
|
101 |
"alignment. (addr: 0x%02x, len: %d)\n", addr, len);
|
102 |
return -1; |
103 |
} |
104 |
|
105 |
return 0; |
106 |
} |
107 |
|
108 |
int xen_pt_bar_offset_to_index(uint32_t offset)
|
109 |
{ |
110 |
int index = 0; |
111 |
|
112 |
/* check Exp ROM BAR */
|
113 |
if (offset == PCI_ROM_ADDRESS) {
|
114 |
return PCI_ROM_SLOT;
|
115 |
} |
116 |
|
117 |
/* calculate BAR index */
|
118 |
index = (offset - PCI_BASE_ADDRESS_0) >> 2;
|
119 |
if (index >= PCI_NUM_REGIONS) {
|
120 |
return -1; |
121 |
} |
122 |
|
123 |
return index;
|
124 |
} |
125 |
|
126 |
static uint32_t xen_pt_pci_read_config(PCIDevice *d, uint32_t addr, int len) |
127 |
{ |
128 |
XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); |
129 |
uint32_t val = 0;
|
130 |
XenPTRegGroup *reg_grp_entry = NULL;
|
131 |
XenPTReg *reg_entry = NULL;
|
132 |
int rc = 0; |
133 |
int emul_len = 0; |
134 |
uint32_t find_addr = addr; |
135 |
|
136 |
if (xen_pt_pci_config_access_check(d, addr, len)) {
|
137 |
goto exit;
|
138 |
} |
139 |
|
140 |
/* find register group entry */
|
141 |
reg_grp_entry = xen_pt_find_reg_grp(s, addr); |
142 |
if (reg_grp_entry) {
|
143 |
/* check 0-Hardwired register group */
|
144 |
if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) {
|
145 |
/* no need to emulate, just return 0 */
|
146 |
val = 0;
|
147 |
goto exit;
|
148 |
} |
149 |
} |
150 |
|
151 |
/* read I/O device register value */
|
152 |
rc = xen_host_pci_get_block(&s->real_device, addr, (uint8_t *)&val, len); |
153 |
if (rc < 0) { |
154 |
XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc);
|
155 |
memset(&val, 0xff, len);
|
156 |
} |
157 |
|
158 |
/* just return the I/O device register value for
|
159 |
* passthrough type register group */
|
160 |
if (reg_grp_entry == NULL) { |
161 |
goto exit;
|
162 |
} |
163 |
|
164 |
/* adjust the read value to appropriate CFC-CFF window */
|
165 |
val <<= (addr & 3) << 3; |
166 |
emul_len = len; |
167 |
|
168 |
/* loop around the guest requested size */
|
169 |
while (emul_len > 0) { |
170 |
/* find register entry to be emulated */
|
171 |
reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr); |
172 |
if (reg_entry) {
|
173 |
XenPTRegInfo *reg = reg_entry->reg; |
174 |
uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; |
175 |
uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); |
176 |
uint8_t *ptr_val = NULL;
|
177 |
|
178 |
valid_mask <<= (find_addr - real_offset) << 3;
|
179 |
ptr_val = (uint8_t *)&val + (real_offset & 3);
|
180 |
|
181 |
/* do emulation based on register size */
|
182 |
switch (reg->size) {
|
183 |
case 1: |
184 |
if (reg->u.b.read) {
|
185 |
rc = reg->u.b.read(s, reg_entry, ptr_val, valid_mask); |
186 |
} |
187 |
break;
|
188 |
case 2: |
189 |
if (reg->u.w.read) {
|
190 |
rc = reg->u.w.read(s, reg_entry, |
191 |
(uint16_t *)ptr_val, valid_mask); |
192 |
} |
193 |
break;
|
194 |
case 4: |
195 |
if (reg->u.dw.read) {
|
196 |
rc = reg->u.dw.read(s, reg_entry, |
197 |
(uint32_t *)ptr_val, valid_mask); |
198 |
} |
199 |
break;
|
200 |
} |
201 |
|
202 |
if (rc < 0) { |
203 |
xen_shutdown_fatal_error("Internal error: Invalid read "
|
204 |
"emulation. (%s, rc: %d)\n",
|
205 |
__func__, rc); |
206 |
return 0; |
207 |
} |
208 |
|
209 |
/* calculate next address to find */
|
210 |
emul_len -= reg->size; |
211 |
if (emul_len > 0) { |
212 |
find_addr = real_offset + reg->size; |
213 |
} |
214 |
} else {
|
215 |
/* nothing to do with passthrough type register,
|
216 |
* continue to find next byte */
|
217 |
emul_len--; |
218 |
find_addr++; |
219 |
} |
220 |
} |
221 |
|
222 |
/* need to shift back before returning them to pci bus emulator */
|
223 |
val >>= ((addr & 3) << 3); |
224 |
|
225 |
exit:
|
226 |
XEN_PT_LOG_CONFIG(d, addr, val, len); |
227 |
return val;
|
228 |
} |
229 |
|
230 |
static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, |
231 |
uint32_t val, int len)
|
232 |
{ |
233 |
XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); |
234 |
int index = 0; |
235 |
XenPTRegGroup *reg_grp_entry = NULL;
|
236 |
int rc = 0; |
237 |
uint32_t read_val = 0;
|
238 |
int emul_len = 0; |
239 |
XenPTReg *reg_entry = NULL;
|
240 |
uint32_t find_addr = addr; |
241 |
XenPTRegInfo *reg = NULL;
|
242 |
|
243 |
if (xen_pt_pci_config_access_check(d, addr, len)) {
|
244 |
return;
|
245 |
} |
246 |
|
247 |
XEN_PT_LOG_CONFIG(d, addr, val, len); |
248 |
|
249 |
/* check unused BAR register */
|
250 |
index = xen_pt_bar_offset_to_index(addr); |
251 |
if ((index >= 0) && (val > 0 && val < XEN_PT_BAR_ALLF) && |
252 |
(s->bases[index].bar_flag == XEN_PT_BAR_FLAG_UNUSED)) { |
253 |
XEN_PT_WARN(d, "Guest attempt to set address to unused Base Address "
|
254 |
"Register. (addr: 0x%02x, len: %d)\n", addr, len);
|
255 |
} |
256 |
|
257 |
/* find register group entry */
|
258 |
reg_grp_entry = xen_pt_find_reg_grp(s, addr); |
259 |
if (reg_grp_entry) {
|
260 |
/* check 0-Hardwired register group */
|
261 |
if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) {
|
262 |
/* ignore silently */
|
263 |
XEN_PT_WARN(d, "Access to 0-Hardwired register. "
|
264 |
"(addr: 0x%02x, len: %d)\n", addr, len);
|
265 |
return;
|
266 |
} |
267 |
} |
268 |
|
269 |
rc = xen_host_pci_get_block(&s->real_device, addr, |
270 |
(uint8_t *)&read_val, len); |
271 |
if (rc < 0) { |
272 |
XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc);
|
273 |
memset(&read_val, 0xff, len);
|
274 |
} |
275 |
|
276 |
/* pass directly to the real device for passthrough type register group */
|
277 |
if (reg_grp_entry == NULL) { |
278 |
goto out;
|
279 |
} |
280 |
|
281 |
memory_region_transaction_begin(); |
282 |
pci_default_write_config(d, addr, val, len); |
283 |
|
284 |
/* adjust the read and write value to appropriate CFC-CFF window */
|
285 |
read_val <<= (addr & 3) << 3; |
286 |
val <<= (addr & 3) << 3; |
287 |
emul_len = len; |
288 |
|
289 |
/* loop around the guest requested size */
|
290 |
while (emul_len > 0) { |
291 |
/* find register entry to be emulated */
|
292 |
reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr); |
293 |
if (reg_entry) {
|
294 |
reg = reg_entry->reg; |
295 |
uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; |
296 |
uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); |
297 |
uint8_t *ptr_val = NULL;
|
298 |
|
299 |
valid_mask <<= (find_addr - real_offset) << 3;
|
300 |
ptr_val = (uint8_t *)&val + (real_offset & 3);
|
301 |
|
302 |
/* do emulation based on register size */
|
303 |
switch (reg->size) {
|
304 |
case 1: |
305 |
if (reg->u.b.write) {
|
306 |
rc = reg->u.b.write(s, reg_entry, ptr_val, |
307 |
read_val >> ((real_offset & 3) << 3), |
308 |
valid_mask); |
309 |
} |
310 |
break;
|
311 |
case 2: |
312 |
if (reg->u.w.write) {
|
313 |
rc = reg->u.w.write(s, reg_entry, (uint16_t *)ptr_val, |
314 |
(read_val >> ((real_offset & 3) << 3)), |
315 |
valid_mask); |
316 |
} |
317 |
break;
|
318 |
case 4: |
319 |
if (reg->u.dw.write) {
|
320 |
rc = reg->u.dw.write(s, reg_entry, (uint32_t *)ptr_val, |
321 |
(read_val >> ((real_offset & 3) << 3)), |
322 |
valid_mask); |
323 |
} |
324 |
break;
|
325 |
} |
326 |
|
327 |
if (rc < 0) { |
328 |
xen_shutdown_fatal_error("Internal error: Invalid write"
|
329 |
" emulation. (%s, rc: %d)\n",
|
330 |
__func__, rc); |
331 |
return;
|
332 |
} |
333 |
|
334 |
/* calculate next address to find */
|
335 |
emul_len -= reg->size; |
336 |
if (emul_len > 0) { |
337 |
find_addr = real_offset + reg->size; |
338 |
} |
339 |
} else {
|
340 |
/* nothing to do with passthrough type register,
|
341 |
* continue to find next byte */
|
342 |
emul_len--; |
343 |
find_addr++; |
344 |
} |
345 |
} |
346 |
|
347 |
/* need to shift back before passing them to xen_host_pci_device */
|
348 |
val >>= (addr & 3) << 3; |
349 |
|
350 |
memory_region_transaction_commit(); |
351 |
|
352 |
out:
|
353 |
if (!(reg && reg->no_wb)) {
|
354 |
/* unknown regs are passed through */
|
355 |
rc = xen_host_pci_set_block(&s->real_device, addr, |
356 |
(uint8_t *)&val, len); |
357 |
|
358 |
if (rc < 0) { |
359 |
XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc);
|
360 |
} |
361 |
} |
362 |
} |
363 |
|
364 |
/* register regions */
|
365 |
|
366 |
static uint64_t xen_pt_bar_read(void *o, hwaddr addr, |
367 |
unsigned size)
|
368 |
{ |
369 |
PCIDevice *d = o; |
370 |
/* if this function is called, that probably means that there is a
|
371 |
* misconfiguration of the IOMMU. */
|
372 |
XEN_PT_ERR(d, "Should not read BAR through QEMU. @0x"TARGET_FMT_plx"\n", |
373 |
addr); |
374 |
return 0; |
375 |
} |
376 |
static void xen_pt_bar_write(void *o, hwaddr addr, uint64_t val, |
377 |
unsigned size)
|
378 |
{ |
379 |
PCIDevice *d = o; |
380 |
/* Same comment as xen_pt_bar_read function */
|
381 |
XEN_PT_ERR(d, "Should not write BAR through QEMU. @0x"TARGET_FMT_plx"\n", |
382 |
addr); |
383 |
} |
384 |
|
385 |
static const MemoryRegionOps ops = { |
386 |
.endianness = DEVICE_NATIVE_ENDIAN, |
387 |
.read = xen_pt_bar_read, |
388 |
.write = xen_pt_bar_write, |
389 |
}; |
390 |
|
391 |
static int xen_pt_register_regions(XenPCIPassthroughState *s) |
392 |
{ |
393 |
int i = 0; |
394 |
XenHostPCIDevice *d = &s->real_device; |
395 |
|
396 |
/* Register PIO/MMIO BARs */
|
397 |
for (i = 0; i < PCI_ROM_SLOT; i++) { |
398 |
XenHostPCIIORegion *r = &d->io_regions[i]; |
399 |
uint8_t type; |
400 |
|
401 |
if (r->base_addr == 0 || r->size == 0) { |
402 |
continue;
|
403 |
} |
404 |
|
405 |
s->bases[i].access.u = r->base_addr; |
406 |
|
407 |
if (r->type & XEN_HOST_PCI_REGION_TYPE_IO) {
|
408 |
type = PCI_BASE_ADDRESS_SPACE_IO; |
409 |
} else {
|
410 |
type = PCI_BASE_ADDRESS_SPACE_MEMORY; |
411 |
if (r->type & XEN_HOST_PCI_REGION_TYPE_PREFETCH) {
|
412 |
type |= PCI_BASE_ADDRESS_MEM_PREFETCH; |
413 |
} |
414 |
if (r->type & XEN_HOST_PCI_REGION_TYPE_MEM_64) {
|
415 |
type |= PCI_BASE_ADDRESS_MEM_TYPE_64; |
416 |
} |
417 |
} |
418 |
|
419 |
memory_region_init_io(&s->bar[i], &ops, &s->dev, |
420 |
"xen-pci-pt-bar", r->size);
|
421 |
pci_register_bar(&s->dev, i, type, &s->bar[i]); |
422 |
|
423 |
XEN_PT_LOG(&s->dev, "IO region %i registered (size=0x%lx"PRIx64
|
424 |
" base_addr=0x%lx"PRIx64" type: %#x)\n", |
425 |
i, r->size, r->base_addr, type); |
426 |
} |
427 |
|
428 |
/* Register expansion ROM address */
|
429 |
if (d->rom.base_addr && d->rom.size) {
|
430 |
uint32_t bar_data = 0;
|
431 |
|
432 |
/* Re-set BAR reported by OS, otherwise ROM can't be read. */
|
433 |
if (xen_host_pci_get_long(d, PCI_ROM_ADDRESS, &bar_data)) {
|
434 |
return 0; |
435 |
} |
436 |
if ((bar_data & PCI_ROM_ADDRESS_MASK) == 0) { |
437 |
bar_data |= d->rom.base_addr & PCI_ROM_ADDRESS_MASK; |
438 |
xen_host_pci_set_long(d, PCI_ROM_ADDRESS, bar_data); |
439 |
} |
440 |
|
441 |
s->bases[PCI_ROM_SLOT].access.maddr = d->rom.base_addr; |
442 |
|
443 |
memory_region_init_rom_device(&s->rom, NULL, NULL, |
444 |
"xen-pci-pt-rom", d->rom.size);
|
445 |
pci_register_bar(&s->dev, PCI_ROM_SLOT, PCI_BASE_ADDRESS_MEM_PREFETCH, |
446 |
&s->rom); |
447 |
|
448 |
XEN_PT_LOG(&s->dev, "Expansion ROM registered (size=0x%08"PRIx64
|
449 |
" base_addr=0x%08"PRIx64")\n", |
450 |
d->rom.size, d->rom.base_addr); |
451 |
} |
452 |
|
453 |
return 0; |
454 |
} |
455 |
|
456 |
static void xen_pt_unregister_regions(XenPCIPassthroughState *s) |
457 |
{ |
458 |
XenHostPCIDevice *d = &s->real_device; |
459 |
int i;
|
460 |
|
461 |
for (i = 0; i < PCI_NUM_REGIONS - 1; i++) { |
462 |
XenHostPCIIORegion *r = &d->io_regions[i]; |
463 |
|
464 |
if (r->base_addr == 0 || r->size == 0) { |
465 |
continue;
|
466 |
} |
467 |
|
468 |
memory_region_destroy(&s->bar[i]); |
469 |
} |
470 |
if (d->rom.base_addr && d->rom.size) {
|
471 |
memory_region_destroy(&s->rom); |
472 |
} |
473 |
} |
474 |
|
475 |
/* region mapping */
|
476 |
|
477 |
static int xen_pt_bar_from_region(XenPCIPassthroughState *s, MemoryRegion *mr) |
478 |
{ |
479 |
int i = 0; |
480 |
|
481 |
for (i = 0; i < PCI_NUM_REGIONS - 1; i++) { |
482 |
if (mr == &s->bar[i]) {
|
483 |
return i;
|
484 |
} |
485 |
} |
486 |
if (mr == &s->rom) {
|
487 |
return PCI_ROM_SLOT;
|
488 |
} |
489 |
return -1; |
490 |
} |
491 |
|
492 |
/*
|
493 |
* This function checks if an io_region overlaps an io_region from another
|
494 |
* device. The io_region to check is provided with (addr, size and type)
|
495 |
* A callback can be provided and will be called for every region that is
|
496 |
* overlapped.
|
497 |
* The return value indicates if the region is overlappsed */
|
498 |
struct CheckBarArgs {
|
499 |
XenPCIPassthroughState *s; |
500 |
pcibus_t addr; |
501 |
pcibus_t size; |
502 |
uint8_t type; |
503 |
bool rc;
|
504 |
}; |
505 |
static void xen_pt_check_bar_overlap(PCIBus *bus, PCIDevice *d, void *opaque) |
506 |
{ |
507 |
struct CheckBarArgs *arg = opaque;
|
508 |
XenPCIPassthroughState *s = arg->s; |
509 |
uint8_t type = arg->type; |
510 |
int i;
|
511 |
|
512 |
if (d->devfn == s->dev.devfn) {
|
513 |
return;
|
514 |
} |
515 |
|
516 |
/* xxx: This ignores bridges. */
|
517 |
for (i = 0; i < PCI_NUM_REGIONS; i++) { |
518 |
const PCIIORegion *r = &d->io_regions[i];
|
519 |
|
520 |
if (!r->size) {
|
521 |
continue;
|
522 |
} |
523 |
if ((type & PCI_BASE_ADDRESS_SPACE_IO)
|
524 |
!= (r->type & PCI_BASE_ADDRESS_SPACE_IO)) { |
525 |
continue;
|
526 |
} |
527 |
|
528 |
if (ranges_overlap(arg->addr, arg->size, r->addr, r->size)) {
|
529 |
XEN_PT_WARN(&s->dev, |
530 |
"Overlapped to device [%02x:%02x.%d] Region: %i"
|
531 |
" (addr: %#"FMT_PCIBUS", len: %#"FMT_PCIBUS")\n", |
532 |
pci_bus_num(bus), PCI_SLOT(d->devfn), |
533 |
PCI_FUNC(d->devfn), i, r->addr, r->size); |
534 |
arg->rc = true;
|
535 |
} |
536 |
} |
537 |
} |
538 |
|
539 |
static void xen_pt_region_update(XenPCIPassthroughState *s, |
540 |
MemoryRegionSection *sec, bool adding)
|
541 |
{ |
542 |
PCIDevice *d = &s->dev; |
543 |
MemoryRegion *mr = sec->mr; |
544 |
int bar = -1; |
545 |
int rc;
|
546 |
int op = adding ? DPCI_ADD_MAPPING : DPCI_REMOVE_MAPPING;
|
547 |
struct CheckBarArgs args = {
|
548 |
.s = s, |
549 |
.addr = sec->offset_within_address_space, |
550 |
.size = sec->size, |
551 |
.rc = false,
|
552 |
}; |
553 |
|
554 |
bar = xen_pt_bar_from_region(s, mr); |
555 |
if (bar == -1 && (!s->msix || &s->msix->mmio != mr)) { |
556 |
return;
|
557 |
} |
558 |
|
559 |
if (s->msix && &s->msix->mmio == mr) {
|
560 |
if (adding) {
|
561 |
s->msix->mmio_base_addr = sec->offset_within_address_space; |
562 |
rc = xen_pt_msix_update_remap(s, s->msix->bar_index); |
563 |
} |
564 |
return;
|
565 |
} |
566 |
|
567 |
args.type = d->io_regions[bar].type; |
568 |
pci_for_each_device(d->bus, pci_bus_num(d->bus), |
569 |
xen_pt_check_bar_overlap, &args); |
570 |
if (args.rc) {
|
571 |
XEN_PT_WARN(d, "Region: %d (addr: %#"FMT_PCIBUS
|
572 |
", len: %#"FMT_PCIBUS") is overlapped.\n", |
573 |
bar, sec->offset_within_address_space, sec->size); |
574 |
} |
575 |
|
576 |
if (d->io_regions[bar].type & PCI_BASE_ADDRESS_SPACE_IO) {
|
577 |
uint32_t guest_port = sec->offset_within_address_space; |
578 |
uint32_t machine_port = s->bases[bar].access.pio_base; |
579 |
uint32_t size = sec->size; |
580 |
rc = xc_domain_ioport_mapping(xen_xc, xen_domid, |
581 |
guest_port, machine_port, size, |
582 |
op); |
583 |
if (rc) {
|
584 |
XEN_PT_ERR(d, "%s ioport mapping failed! (rc: %i)\n",
|
585 |
adding ? "create new" : "remove old", rc); |
586 |
} |
587 |
} else {
|
588 |
pcibus_t guest_addr = sec->offset_within_address_space; |
589 |
pcibus_t machine_addr = s->bases[bar].access.maddr |
590 |
+ sec->offset_within_region; |
591 |
pcibus_t size = sec->size; |
592 |
rc = xc_domain_memory_mapping(xen_xc, xen_domid, |
593 |
XEN_PFN(guest_addr + XC_PAGE_SIZE - 1),
|
594 |
XEN_PFN(machine_addr + XC_PAGE_SIZE - 1),
|
595 |
XEN_PFN(size + XC_PAGE_SIZE - 1),
|
596 |
op); |
597 |
if (rc) {
|
598 |
XEN_PT_ERR(d, "%s mem mapping failed! (rc: %i)\n",
|
599 |
adding ? "create new" : "remove old", rc); |
600 |
} |
601 |
} |
602 |
} |
603 |
|
604 |
static void xen_pt_region_add(MemoryListener *l, MemoryRegionSection *sec) |
605 |
{ |
606 |
XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState, |
607 |
memory_listener); |
608 |
|
609 |
xen_pt_region_update(s, sec, true);
|
610 |
} |
611 |
|
612 |
static void xen_pt_region_del(MemoryListener *l, MemoryRegionSection *sec) |
613 |
{ |
614 |
XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState, |
615 |
memory_listener); |
616 |
|
617 |
xen_pt_region_update(s, sec, false);
|
618 |
} |
619 |
|
620 |
static void xen_pt_io_region_add(MemoryListener *l, MemoryRegionSection *sec) |
621 |
{ |
622 |
XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState, |
623 |
io_listener); |
624 |
|
625 |
xen_pt_region_update(s, sec, true);
|
626 |
} |
627 |
|
628 |
static void xen_pt_io_region_del(MemoryListener *l, MemoryRegionSection *sec) |
629 |
{ |
630 |
XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState, |
631 |
io_listener); |
632 |
|
633 |
xen_pt_region_update(s, sec, false);
|
634 |
} |
635 |
|
636 |
static const MemoryListener xen_pt_memory_listener = { |
637 |
.region_add = xen_pt_region_add, |
638 |
.region_del = xen_pt_region_del, |
639 |
.priority = 10,
|
640 |
}; |
641 |
|
642 |
static const MemoryListener xen_pt_io_listener = { |
643 |
.region_add = xen_pt_io_region_add, |
644 |
.region_del = xen_pt_io_region_del, |
645 |
.priority = 10,
|
646 |
}; |
647 |
|
648 |
/* init */
|
649 |
|
650 |
static int xen_pt_initfn(PCIDevice *d) |
651 |
{ |
652 |
XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); |
653 |
int rc = 0; |
654 |
uint8_t machine_irq = 0;
|
655 |
int pirq = XEN_PT_UNASSIGNED_PIRQ;
|
656 |
|
657 |
/* register real device */
|
658 |
XEN_PT_LOG(d, "Assigning real physical device %02x:%02x.%d"
|
659 |
" to devfn %#x\n",
|
660 |
s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function, |
661 |
s->dev.devfn); |
662 |
|
663 |
rc = xen_host_pci_device_get(&s->real_device, |
664 |
s->hostaddr.domain, s->hostaddr.bus, |
665 |
s->hostaddr.slot, s->hostaddr.function); |
666 |
if (rc) {
|
667 |
XEN_PT_ERR(d, "Failed to \"open\" the real pci device. rc: %i\n", rc);
|
668 |
return -1; |
669 |
} |
670 |
|
671 |
s->is_virtfn = s->real_device.is_virtfn; |
672 |
if (s->is_virtfn) {
|
673 |
XEN_PT_LOG(d, "%04x:%02x:%02x.%d is a SR-IOV Virtual Function\n",
|
674 |
s->real_device.domain, bus, slot, func); |
675 |
} |
676 |
|
677 |
/* Initialize virtualized PCI configuration (Extended 256 Bytes) */
|
678 |
if (xen_host_pci_get_block(&s->real_device, 0, d->config, |
679 |
PCI_CONFIG_SPACE_SIZE) == -1) {
|
680 |
xen_host_pci_device_put(&s->real_device); |
681 |
return -1; |
682 |
} |
683 |
|
684 |
s->memory_listener = xen_pt_memory_listener; |
685 |
s->io_listener = xen_pt_io_listener; |
686 |
|
687 |
/* Handle real device's MMIO/PIO BARs */
|
688 |
xen_pt_register_regions(s); |
689 |
|
690 |
/* reinitialize each config register to be emulated */
|
691 |
if (xen_pt_config_init(s)) {
|
692 |
XEN_PT_ERR(d, "PCI Config space initialisation failed.\n");
|
693 |
xen_host_pci_device_put(&s->real_device); |
694 |
return -1; |
695 |
} |
696 |
|
697 |
/* Bind interrupt */
|
698 |
if (!s->dev.config[PCI_INTERRUPT_PIN]) {
|
699 |
XEN_PT_LOG(d, "no pin interrupt\n");
|
700 |
goto out;
|
701 |
} |
702 |
|
703 |
machine_irq = s->real_device.irq; |
704 |
rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq); |
705 |
|
706 |
if (rc < 0) { |
707 |
XEN_PT_ERR(d, "Mapping machine irq %u to pirq %i failed, (rc: %d)\n",
|
708 |
machine_irq, pirq, rc); |
709 |
|
710 |
/* Disable PCI intx assertion (turn on bit10 of devctl) */
|
711 |
xen_host_pci_set_word(&s->real_device, |
712 |
PCI_COMMAND, |
713 |
pci_get_word(s->dev.config + PCI_COMMAND) |
714 |
| PCI_COMMAND_INTX_DISABLE); |
715 |
machine_irq = 0;
|
716 |
s->machine_irq = 0;
|
717 |
} else {
|
718 |
machine_irq = pirq; |
719 |
s->machine_irq = pirq; |
720 |
xen_pt_mapped_machine_irq[machine_irq]++; |
721 |
} |
722 |
|
723 |
/* bind machine_irq to device */
|
724 |
if (machine_irq != 0) { |
725 |
uint8_t e_intx = xen_pt_pci_intx(s); |
726 |
|
727 |
rc = xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, machine_irq, |
728 |
pci_bus_num(d->bus), |
729 |
PCI_SLOT(d->devfn), |
730 |
e_intx); |
731 |
if (rc < 0) { |
732 |
XEN_PT_ERR(d, "Binding of interrupt %i failed! (rc: %d)\n",
|
733 |
e_intx, rc); |
734 |
|
735 |
/* Disable PCI intx assertion (turn on bit10 of devctl) */
|
736 |
xen_host_pci_set_word(&s->real_device, PCI_COMMAND, |
737 |
*(uint16_t *)(&s->dev.config[PCI_COMMAND]) |
738 |
| PCI_COMMAND_INTX_DISABLE); |
739 |
xen_pt_mapped_machine_irq[machine_irq]--; |
740 |
|
741 |
if (xen_pt_mapped_machine_irq[machine_irq] == 0) { |
742 |
if (xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq)) {
|
743 |
XEN_PT_ERR(d, "Unmapping of machine interrupt %i failed!"
|
744 |
" (rc: %d)\n", machine_irq, rc);
|
745 |
} |
746 |
} |
747 |
s->machine_irq = 0;
|
748 |
} |
749 |
} |
750 |
|
751 |
out:
|
752 |
memory_listener_register(&s->memory_listener, &address_space_memory); |
753 |
memory_listener_register(&s->io_listener, &address_space_io); |
754 |
XEN_PT_LOG(d, "Real physical device %02x:%02x.%d registered successfuly!\n",
|
755 |
bus, slot, func); |
756 |
|
757 |
return 0; |
758 |
} |
759 |
|
760 |
static void xen_pt_unregister_device(PCIDevice *d) |
761 |
{ |
762 |
XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); |
763 |
uint8_t machine_irq = s->machine_irq; |
764 |
uint8_t intx = xen_pt_pci_intx(s); |
765 |
int rc;
|
766 |
|
767 |
if (machine_irq) {
|
768 |
rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq, |
769 |
PT_IRQ_TYPE_PCI, |
770 |
pci_bus_num(d->bus), |
771 |
PCI_SLOT(s->dev.devfn), |
772 |
intx, |
773 |
0 /* isa_irq */); |
774 |
if (rc < 0) { |
775 |
XEN_PT_ERR(d, "unbinding of interrupt INT%c failed."
|
776 |
" (machine irq: %i, rc: %d)"
|
777 |
" But bravely continuing on..\n",
|
778 |
'a' + intx, machine_irq, rc);
|
779 |
} |
780 |
} |
781 |
|
782 |
if (s->msi) {
|
783 |
xen_pt_msi_disable(s); |
784 |
} |
785 |
if (s->msix) {
|
786 |
xen_pt_msix_disable(s); |
787 |
} |
788 |
|
789 |
if (machine_irq) {
|
790 |
xen_pt_mapped_machine_irq[machine_irq]--; |
791 |
|
792 |
if (xen_pt_mapped_machine_irq[machine_irq] == 0) { |
793 |
rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq); |
794 |
|
795 |
if (rc < 0) { |
796 |
XEN_PT_ERR(d, "unmapping of interrupt %i failed. (rc: %d)"
|
797 |
" But bravely continuing on..\n",
|
798 |
machine_irq, rc); |
799 |
} |
800 |
} |
801 |
} |
802 |
|
803 |
/* delete all emulated config registers */
|
804 |
xen_pt_config_delete(s); |
805 |
|
806 |
xen_pt_unregister_regions(s); |
807 |
memory_listener_unregister(&s->memory_listener); |
808 |
memory_listener_unregister(&s->io_listener); |
809 |
|
810 |
xen_host_pci_device_put(&s->real_device); |
811 |
} |
812 |
|
813 |
static Property xen_pci_passthrough_properties[] = {
|
814 |
DEFINE_PROP_PCI_HOST_DEVADDR("hostaddr", XenPCIPassthroughState, hostaddr),
|
815 |
DEFINE_PROP_END_OF_LIST(), |
816 |
}; |
817 |
|
818 |
static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data) |
819 |
{ |
820 |
DeviceClass *dc = DEVICE_CLASS(klass); |
821 |
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
822 |
|
823 |
k->init = xen_pt_initfn; |
824 |
k->exit = xen_pt_unregister_device; |
825 |
k->config_read = xen_pt_pci_read_config; |
826 |
k->config_write = xen_pt_pci_write_config; |
827 |
dc->desc = "Assign an host PCI device with Xen";
|
828 |
dc->props = xen_pci_passthrough_properties; |
829 |
}; |
830 |
|
831 |
static TypeInfo xen_pci_passthrough_info = {
|
832 |
.name = "xen-pci-passthrough",
|
833 |
.parent = TYPE_PCI_DEVICE, |
834 |
.instance_size = sizeof(XenPCIPassthroughState),
|
835 |
.class_init = xen_pci_passthrough_class_init, |
836 |
}; |
837 |
|
838 |
static void xen_pci_passthrough_register_types(void) |
839 |
{ |
840 |
type_register_static(&xen_pci_passthrough_info); |
841 |
} |
842 |
|
843 |
type_init(xen_pci_passthrough_register_types) |