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