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