xref: /openbmc/qemu/hw/xen/xen_pt.c (revision e692f9c6a681de1372a41999b14a947a553b6a1a)
180b4ecc8SPaolo Bonzini /*
280b4ecc8SPaolo Bonzini  * Copyright (c) 2007, Neocleus Corporation.
380b4ecc8SPaolo Bonzini  * Copyright (c) 2007, Intel Corporation.
480b4ecc8SPaolo Bonzini  *
580b4ecc8SPaolo Bonzini  * This work is licensed under the terms of the GNU GPL, version 2.  See
680b4ecc8SPaolo Bonzini  * the COPYING file in the top-level directory.
780b4ecc8SPaolo Bonzini  *
880b4ecc8SPaolo Bonzini  * Alex Novik <alex@neocleus.com>
980b4ecc8SPaolo Bonzini  * Allen Kay <allen.m.kay@intel.com>
1080b4ecc8SPaolo Bonzini  * Guy Zana <guy@neocleus.com>
1180b4ecc8SPaolo Bonzini  *
1280b4ecc8SPaolo Bonzini  * This file implements direct PCI assignment to a HVM guest
1380b4ecc8SPaolo Bonzini  */
1480b4ecc8SPaolo Bonzini 
1580b4ecc8SPaolo Bonzini /*
1680b4ecc8SPaolo Bonzini  * Interrupt Disable policy:
1780b4ecc8SPaolo Bonzini  *
1880b4ecc8SPaolo Bonzini  * INTx interrupt:
1980b4ecc8SPaolo Bonzini  *   Initialize(register_real_device)
2080b4ecc8SPaolo Bonzini  *     Map INTx(xc_physdev_map_pirq):
2180b4ecc8SPaolo Bonzini  *       <fail>
2280b4ecc8SPaolo Bonzini  *         - Set real Interrupt Disable bit to '1'.
2380b4ecc8SPaolo Bonzini  *         - Set machine_irq and assigned_device->machine_irq to '0'.
2480b4ecc8SPaolo Bonzini  *         * Don't bind INTx.
2580b4ecc8SPaolo Bonzini  *
2680b4ecc8SPaolo Bonzini  *     Bind INTx(xc_domain_bind_pt_pci_irq):
2780b4ecc8SPaolo Bonzini  *       <fail>
2880b4ecc8SPaolo Bonzini  *         - Set real Interrupt Disable bit to '1'.
2980b4ecc8SPaolo Bonzini  *         - Unmap INTx.
3080b4ecc8SPaolo Bonzini  *         - Decrement xen_pt_mapped_machine_irq[machine_irq]
3180b4ecc8SPaolo Bonzini  *         - Set assigned_device->machine_irq to '0'.
3280b4ecc8SPaolo Bonzini  *
3380b4ecc8SPaolo Bonzini  *   Write to Interrupt Disable bit by guest software(xen_pt_cmd_reg_write)
3480b4ecc8SPaolo Bonzini  *     Write '0'
3580b4ecc8SPaolo Bonzini  *       - Set real bit to '0' if assigned_device->machine_irq isn't '0'.
3680b4ecc8SPaolo Bonzini  *
3780b4ecc8SPaolo Bonzini  *     Write '1'
3880b4ecc8SPaolo Bonzini  *       - Set real bit to '1'.
3980b4ecc8SPaolo Bonzini  *
4080b4ecc8SPaolo Bonzini  * MSI interrupt:
4180b4ecc8SPaolo Bonzini  *   Initialize MSI register(xen_pt_msi_setup, xen_pt_msi_update)
4280b4ecc8SPaolo Bonzini  *     Bind MSI(xc_domain_update_msi_irq)
4380b4ecc8SPaolo Bonzini  *       <fail>
4480b4ecc8SPaolo Bonzini  *         - Unmap MSI.
4580b4ecc8SPaolo Bonzini  *         - Set dev->msi->pirq to '-1'.
4680b4ecc8SPaolo Bonzini  *
4780b4ecc8SPaolo Bonzini  * MSI-X interrupt:
4880b4ecc8SPaolo Bonzini  *   Initialize MSI-X register(xen_pt_msix_update_one)
4980b4ecc8SPaolo Bonzini  *     Bind MSI-X(xc_domain_update_msi_irq)
5080b4ecc8SPaolo Bonzini  *       <fail>
5180b4ecc8SPaolo Bonzini  *         - Unmap MSI-X.
5280b4ecc8SPaolo Bonzini  *         - Set entry->pirq to '-1'.
5380b4ecc8SPaolo Bonzini  */
5480b4ecc8SPaolo Bonzini 
5521cbfe5fSPeter Maydell #include "qemu/osdep.h"
56da34e65cSMarkus Armbruster #include "qapi/error.h"
5780b4ecc8SPaolo Bonzini #include <sys/ioctl.h>
5880b4ecc8SPaolo Bonzini 
5980b4ecc8SPaolo Bonzini #include "hw/pci/pci.h"
60a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
61ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
62f28b958cSPhilippe Mathieu-Daudé #include "hw/xen/xen_pt.h"
63f28b958cSPhilippe Mathieu-Daudé #include "hw/xen/xen_igd.h"
6480b4ecc8SPaolo Bonzini #include "hw/xen/xen.h"
652d0ed5e6SPaul Durrant #include "hw/xen/xen-legacy-backend.h"
6680b4ecc8SPaolo Bonzini #include "qemu/range.h"
6780b4ecc8SPaolo Bonzini 
68acd0c941SAnthony PERARD static bool has_igd_gfx_passthru;
69acd0c941SAnthony PERARD 
xen_igd_gfx_pt_enabled(void)70acd0c941SAnthony PERARD bool xen_igd_gfx_pt_enabled(void)
71acd0c941SAnthony PERARD {
72acd0c941SAnthony PERARD     return has_igd_gfx_passthru;
73acd0c941SAnthony PERARD }
74acd0c941SAnthony PERARD 
xen_igd_gfx_pt_set(bool value,Error ** errp)75acd0c941SAnthony PERARD void xen_igd_gfx_pt_set(bool value, Error **errp)
76acd0c941SAnthony PERARD {
77acd0c941SAnthony PERARD     has_igd_gfx_passthru = value;
78acd0c941SAnthony PERARD }
7946472d82SPaolo Bonzini 
8080b4ecc8SPaolo Bonzini #define XEN_PT_NR_IRQS (256)
8180b4ecc8SPaolo Bonzini static uint8_t xen_pt_mapped_machine_irq[XEN_PT_NR_IRQS] = {0};
8280b4ecc8SPaolo Bonzini 
xen_pt_log(const PCIDevice * d,const char * f,...)8380b4ecc8SPaolo Bonzini void xen_pt_log(const PCIDevice *d, const char *f, ...)
8480b4ecc8SPaolo Bonzini {
8580b4ecc8SPaolo Bonzini     va_list ap;
8680b4ecc8SPaolo Bonzini 
8780b4ecc8SPaolo Bonzini     va_start(ap, f);
8880b4ecc8SPaolo Bonzini     if (d) {
89cdc57472SDavid Gibson         fprintf(stderr, "[%02x:%02x.%d] ", pci_dev_bus_num(d),
9080b4ecc8SPaolo Bonzini                 PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
9180b4ecc8SPaolo Bonzini     }
9280b4ecc8SPaolo Bonzini     vfprintf(stderr, f, ap);
9380b4ecc8SPaolo Bonzini     va_end(ap);
9480b4ecc8SPaolo Bonzini }
9580b4ecc8SPaolo Bonzini 
9680b4ecc8SPaolo Bonzini /* Config Space */
9780b4ecc8SPaolo Bonzini 
xen_pt_pci_config_access_check(PCIDevice * d,uint32_t addr,int len)9880b4ecc8SPaolo Bonzini static int xen_pt_pci_config_access_check(PCIDevice *d, uint32_t addr, int len)
9980b4ecc8SPaolo Bonzini {
10080b4ecc8SPaolo Bonzini     /* check offset range */
1014daf6259SAnoob Soman     if (addr > 0xFF) {
10280b4ecc8SPaolo Bonzini         XEN_PT_ERR(d, "Failed to access register with offset exceeding 0xFF. "
10380b4ecc8SPaolo Bonzini                    "(addr: 0x%02x, len: %d)\n", addr, len);
10480b4ecc8SPaolo Bonzini         return -1;
10580b4ecc8SPaolo Bonzini     }
10680b4ecc8SPaolo Bonzini 
10780b4ecc8SPaolo Bonzini     /* check read size */
10880b4ecc8SPaolo Bonzini     if ((len != 1) && (len != 2) && (len != 4)) {
10980b4ecc8SPaolo Bonzini         XEN_PT_ERR(d, "Failed to access register with invalid access length. "
11080b4ecc8SPaolo Bonzini                    "(addr: 0x%02x, len: %d)\n", addr, len);
11180b4ecc8SPaolo Bonzini         return -1;
11280b4ecc8SPaolo Bonzini     }
11380b4ecc8SPaolo Bonzini 
11480b4ecc8SPaolo Bonzini     /* check offset alignment */
11580b4ecc8SPaolo Bonzini     if (addr & (len - 1)) {
11680b4ecc8SPaolo Bonzini         XEN_PT_ERR(d, "Failed to access register with invalid access size "
11780b4ecc8SPaolo Bonzini                    "alignment. (addr: 0x%02x, len: %d)\n", addr, len);
11880b4ecc8SPaolo Bonzini         return -1;
11980b4ecc8SPaolo Bonzini     }
12080b4ecc8SPaolo Bonzini 
12180b4ecc8SPaolo Bonzini     return 0;
12280b4ecc8SPaolo Bonzini }
12380b4ecc8SPaolo Bonzini 
xen_pt_bar_offset_to_index(uint32_t offset)12480b4ecc8SPaolo Bonzini int xen_pt_bar_offset_to_index(uint32_t offset)
12580b4ecc8SPaolo Bonzini {
12680b4ecc8SPaolo Bonzini     int index = 0;
12780b4ecc8SPaolo Bonzini 
12880b4ecc8SPaolo Bonzini     /* check Exp ROM BAR */
12980b4ecc8SPaolo Bonzini     if (offset == PCI_ROM_ADDRESS) {
13080b4ecc8SPaolo Bonzini         return PCI_ROM_SLOT;
13180b4ecc8SPaolo Bonzini     }
13280b4ecc8SPaolo Bonzini 
13380b4ecc8SPaolo Bonzini     /* calculate BAR index */
13480b4ecc8SPaolo Bonzini     index = (offset - PCI_BASE_ADDRESS_0) >> 2;
13580b4ecc8SPaolo Bonzini     if (index >= PCI_NUM_REGIONS) {
13680b4ecc8SPaolo Bonzini         return -1;
13780b4ecc8SPaolo Bonzini     }
13880b4ecc8SPaolo Bonzini 
13980b4ecc8SPaolo Bonzini     return index;
14080b4ecc8SPaolo Bonzini }
14180b4ecc8SPaolo Bonzini 
xen_pt_pci_read_config(PCIDevice * d,uint32_t addr,int len)14280b4ecc8SPaolo Bonzini static uint32_t xen_pt_pci_read_config(PCIDevice *d, uint32_t addr, int len)
14380b4ecc8SPaolo Bonzini {
144f9b9d292SGonglei     XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
14580b4ecc8SPaolo Bonzini     uint32_t val = 0;
14680b4ecc8SPaolo Bonzini     XenPTRegGroup *reg_grp_entry = NULL;
14780b4ecc8SPaolo Bonzini     XenPTReg *reg_entry = NULL;
14880b4ecc8SPaolo Bonzini     int rc = 0;
14980b4ecc8SPaolo Bonzini     int emul_len = 0;
15080b4ecc8SPaolo Bonzini     uint32_t find_addr = addr;
15180b4ecc8SPaolo Bonzini 
15280b4ecc8SPaolo Bonzini     if (xen_pt_pci_config_access_check(d, addr, len)) {
15380b4ecc8SPaolo Bonzini         goto exit;
15480b4ecc8SPaolo Bonzini     }
15580b4ecc8SPaolo Bonzini 
15680b4ecc8SPaolo Bonzini     /* find register group entry */
15780b4ecc8SPaolo Bonzini     reg_grp_entry = xen_pt_find_reg_grp(s, addr);
15880b4ecc8SPaolo Bonzini     if (reg_grp_entry) {
15980b4ecc8SPaolo Bonzini         /* check 0-Hardwired register group */
16080b4ecc8SPaolo Bonzini         if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) {
16180b4ecc8SPaolo Bonzini             /* no need to emulate, just return 0 */
16280b4ecc8SPaolo Bonzini             val = 0;
16380b4ecc8SPaolo Bonzini             goto exit;
16480b4ecc8SPaolo Bonzini         }
16580b4ecc8SPaolo Bonzini     }
16680b4ecc8SPaolo Bonzini 
16780b4ecc8SPaolo Bonzini     /* read I/O device register value */
16880b4ecc8SPaolo Bonzini     rc = xen_host_pci_get_block(&s->real_device, addr, (uint8_t *)&val, len);
16980b4ecc8SPaolo Bonzini     if (rc < 0) {
17080b4ecc8SPaolo Bonzini         XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc);
17180b4ecc8SPaolo Bonzini         memset(&val, 0xff, len);
17280b4ecc8SPaolo Bonzini     }
17380b4ecc8SPaolo Bonzini 
17480b4ecc8SPaolo Bonzini     /* just return the I/O device register value for
17580b4ecc8SPaolo Bonzini      * passthrough type register group */
17680b4ecc8SPaolo Bonzini     if (reg_grp_entry == NULL) {
17780b4ecc8SPaolo Bonzini         goto exit;
17880b4ecc8SPaolo Bonzini     }
17980b4ecc8SPaolo Bonzini 
18080b4ecc8SPaolo Bonzini     /* adjust the read value to appropriate CFC-CFF window */
18180b4ecc8SPaolo Bonzini     val <<= (addr & 3) << 3;
18280b4ecc8SPaolo Bonzini     emul_len = len;
18380b4ecc8SPaolo Bonzini 
18480b4ecc8SPaolo Bonzini     /* loop around the guest requested size */
18580b4ecc8SPaolo Bonzini     while (emul_len > 0) {
18680b4ecc8SPaolo Bonzini         /* find register entry to be emulated */
18780b4ecc8SPaolo Bonzini         reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr);
18880b4ecc8SPaolo Bonzini         if (reg_entry) {
18980b4ecc8SPaolo Bonzini             XenPTRegInfo *reg = reg_entry->reg;
19080b4ecc8SPaolo Bonzini             uint32_t real_offset = reg_grp_entry->base_offset + reg->offset;
19180b4ecc8SPaolo Bonzini             uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3);
19280b4ecc8SPaolo Bonzini             uint8_t *ptr_val = NULL;
19380b4ecc8SPaolo Bonzini 
19480b4ecc8SPaolo Bonzini             valid_mask <<= (find_addr - real_offset) << 3;
19580b4ecc8SPaolo Bonzini             ptr_val = (uint8_t *)&val + (real_offset & 3);
19680b4ecc8SPaolo Bonzini 
19780b4ecc8SPaolo Bonzini             /* do emulation based on register size */
19880b4ecc8SPaolo Bonzini             switch (reg->size) {
19980b4ecc8SPaolo Bonzini             case 1:
20080b4ecc8SPaolo Bonzini                 if (reg->u.b.read) {
20180b4ecc8SPaolo Bonzini                     rc = reg->u.b.read(s, reg_entry, ptr_val, valid_mask);
20280b4ecc8SPaolo Bonzini                 }
20380b4ecc8SPaolo Bonzini                 break;
20480b4ecc8SPaolo Bonzini             case 2:
20580b4ecc8SPaolo Bonzini                 if (reg->u.w.read) {
20680b4ecc8SPaolo Bonzini                     rc = reg->u.w.read(s, reg_entry,
20780b4ecc8SPaolo Bonzini                                        (uint16_t *)ptr_val, valid_mask);
20880b4ecc8SPaolo Bonzini                 }
20980b4ecc8SPaolo Bonzini                 break;
21080b4ecc8SPaolo Bonzini             case 4:
21180b4ecc8SPaolo Bonzini                 if (reg->u.dw.read) {
21280b4ecc8SPaolo Bonzini                     rc = reg->u.dw.read(s, reg_entry,
21380b4ecc8SPaolo Bonzini                                         (uint32_t *)ptr_val, valid_mask);
21480b4ecc8SPaolo Bonzini                 }
21580b4ecc8SPaolo Bonzini                 break;
21680b4ecc8SPaolo Bonzini             }
21780b4ecc8SPaolo Bonzini 
21880b4ecc8SPaolo Bonzini             if (rc < 0) {
21980b4ecc8SPaolo Bonzini                 xen_shutdown_fatal_error("Internal error: Invalid read "
22080b4ecc8SPaolo Bonzini                                          "emulation. (%s, rc: %d)\n",
22180b4ecc8SPaolo Bonzini                                          __func__, rc);
22280b4ecc8SPaolo Bonzini                 return 0;
22380b4ecc8SPaolo Bonzini             }
22480b4ecc8SPaolo Bonzini 
22580b4ecc8SPaolo Bonzini             /* calculate next address to find */
22680b4ecc8SPaolo Bonzini             emul_len -= reg->size;
22780b4ecc8SPaolo Bonzini             if (emul_len > 0) {
22880b4ecc8SPaolo Bonzini                 find_addr = real_offset + reg->size;
22980b4ecc8SPaolo Bonzini             }
23080b4ecc8SPaolo Bonzini         } else {
23180b4ecc8SPaolo Bonzini             /* nothing to do with passthrough type register,
23280b4ecc8SPaolo Bonzini              * continue to find next byte */
23380b4ecc8SPaolo Bonzini             emul_len--;
23480b4ecc8SPaolo Bonzini             find_addr++;
23580b4ecc8SPaolo Bonzini         }
23680b4ecc8SPaolo Bonzini     }
23780b4ecc8SPaolo Bonzini 
23880b4ecc8SPaolo Bonzini     /* need to shift back before returning them to pci bus emulator */
23980b4ecc8SPaolo Bonzini     val >>= ((addr & 3) << 3);
24080b4ecc8SPaolo Bonzini 
24180b4ecc8SPaolo Bonzini exit:
24280b4ecc8SPaolo Bonzini     XEN_PT_LOG_CONFIG(d, addr, val, len);
24380b4ecc8SPaolo Bonzini     return val;
24480b4ecc8SPaolo Bonzini }
24580b4ecc8SPaolo Bonzini 
xen_pt_pci_write_config(PCIDevice * d,uint32_t addr,uint32_t val,int len)24680b4ecc8SPaolo Bonzini static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
24780b4ecc8SPaolo Bonzini                                     uint32_t val, int len)
24880b4ecc8SPaolo Bonzini {
249f9b9d292SGonglei     XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
25080b4ecc8SPaolo Bonzini     int index = 0;
25180b4ecc8SPaolo Bonzini     XenPTRegGroup *reg_grp_entry = NULL;
25280b4ecc8SPaolo Bonzini     int rc = 0;
2535c83b2f5SJan Beulich     uint32_t read_val = 0, wb_mask;
25480b4ecc8SPaolo Bonzini     int emul_len = 0;
25580b4ecc8SPaolo Bonzini     XenPTReg *reg_entry = NULL;
25680b4ecc8SPaolo Bonzini     uint32_t find_addr = addr;
25780b4ecc8SPaolo Bonzini     XenPTRegInfo *reg = NULL;
258c25bbf15SJan Beulich     bool wp_flag = false;
25980b4ecc8SPaolo Bonzini 
26080b4ecc8SPaolo Bonzini     if (xen_pt_pci_config_access_check(d, addr, len)) {
26180b4ecc8SPaolo Bonzini         return;
26280b4ecc8SPaolo Bonzini     }
26380b4ecc8SPaolo Bonzini 
26480b4ecc8SPaolo Bonzini     XEN_PT_LOG_CONFIG(d, addr, val, len);
26580b4ecc8SPaolo Bonzini 
26680b4ecc8SPaolo Bonzini     /* check unused BAR register */
26780b4ecc8SPaolo Bonzini     index = xen_pt_bar_offset_to_index(addr);
26869976894SJan Beulich     if ((index >= 0) && (val != 0)) {
26969976894SJan Beulich         uint32_t chk = val;
27069976894SJan Beulich 
27169976894SJan Beulich         if (index == PCI_ROM_SLOT)
27269976894SJan Beulich             chk |= (uint32_t)~PCI_ROM_ADDRESS_MASK;
27369976894SJan Beulich 
27469976894SJan Beulich         if ((chk != XEN_PT_BAR_ALLF) &&
27580b4ecc8SPaolo Bonzini             (s->bases[index].bar_flag == XEN_PT_BAR_FLAG_UNUSED)) {
27669976894SJan Beulich             XEN_PT_WARN(d, "Guest attempt to set address to unused "
27769976894SJan Beulich                         "Base Address Register. (addr: 0x%02x, len: %d)\n",
27869976894SJan Beulich                         addr, len);
27969976894SJan Beulich         }
28080b4ecc8SPaolo Bonzini     }
28180b4ecc8SPaolo Bonzini 
28280b4ecc8SPaolo Bonzini     /* find register group entry */
28380b4ecc8SPaolo Bonzini     reg_grp_entry = xen_pt_find_reg_grp(s, addr);
28480b4ecc8SPaolo Bonzini     if (reg_grp_entry) {
28580b4ecc8SPaolo Bonzini         /* check 0-Hardwired register group */
28680b4ecc8SPaolo Bonzini         if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) {
28780b4ecc8SPaolo Bonzini             /* ignore silently */
28880b4ecc8SPaolo Bonzini             XEN_PT_WARN(d, "Access to 0-Hardwired register. "
28980b4ecc8SPaolo Bonzini                         "(addr: 0x%02x, len: %d)\n", addr, len);
29080b4ecc8SPaolo Bonzini             return;
29180b4ecc8SPaolo Bonzini         }
29280b4ecc8SPaolo Bonzini     }
29380b4ecc8SPaolo Bonzini 
29480b4ecc8SPaolo Bonzini     rc = xen_host_pci_get_block(&s->real_device, addr,
29580b4ecc8SPaolo Bonzini                                 (uint8_t *)&read_val, len);
29680b4ecc8SPaolo Bonzini     if (rc < 0) {
29780b4ecc8SPaolo Bonzini         XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc);
29880b4ecc8SPaolo Bonzini         memset(&read_val, 0xff, len);
2995c83b2f5SJan Beulich         wb_mask = 0;
3005c83b2f5SJan Beulich     } else {
3015c83b2f5SJan Beulich         wb_mask = 0xFFFFFFFF >> ((4 - len) << 3);
30280b4ecc8SPaolo Bonzini     }
30380b4ecc8SPaolo Bonzini 
30480b4ecc8SPaolo Bonzini     /* pass directly to the real device for passthrough type register group */
30580b4ecc8SPaolo Bonzini     if (reg_grp_entry == NULL) {
306c25bbf15SJan Beulich         if (!s->permissive) {
307c25bbf15SJan Beulich             wb_mask = 0;
308c25bbf15SJan Beulich             wp_flag = true;
309c25bbf15SJan Beulich         }
31080b4ecc8SPaolo Bonzini         goto out;
31180b4ecc8SPaolo Bonzini     }
31280b4ecc8SPaolo Bonzini 
31380b4ecc8SPaolo Bonzini     memory_region_transaction_begin();
31480b4ecc8SPaolo Bonzini     pci_default_write_config(d, addr, val, len);
31580b4ecc8SPaolo Bonzini 
31680b4ecc8SPaolo Bonzini     /* adjust the read and write value to appropriate CFC-CFF window */
31780b4ecc8SPaolo Bonzini     read_val <<= (addr & 3) << 3;
31880b4ecc8SPaolo Bonzini     val <<= (addr & 3) << 3;
31980b4ecc8SPaolo Bonzini     emul_len = len;
32080b4ecc8SPaolo Bonzini 
32180b4ecc8SPaolo Bonzini     /* loop around the guest requested size */
32280b4ecc8SPaolo Bonzini     while (emul_len > 0) {
32380b4ecc8SPaolo Bonzini         /* find register entry to be emulated */
32480b4ecc8SPaolo Bonzini         reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr);
32580b4ecc8SPaolo Bonzini         if (reg_entry) {
32680b4ecc8SPaolo Bonzini             reg = reg_entry->reg;
32780b4ecc8SPaolo Bonzini             uint32_t real_offset = reg_grp_entry->base_offset + reg->offset;
32880b4ecc8SPaolo Bonzini             uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3);
32980b4ecc8SPaolo Bonzini             uint8_t *ptr_val = NULL;
330c25bbf15SJan Beulich             uint32_t wp_mask = reg->emu_mask | reg->ro_mask;
33180b4ecc8SPaolo Bonzini 
33280b4ecc8SPaolo Bonzini             valid_mask <<= (find_addr - real_offset) << 3;
33380b4ecc8SPaolo Bonzini             ptr_val = (uint8_t *)&val + (real_offset & 3);
334c25bbf15SJan Beulich             if (!s->permissive) {
335c25bbf15SJan Beulich                 wp_mask |= reg->res_mask;
336c25bbf15SJan Beulich             }
337c25bbf15SJan Beulich             if (wp_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) {
338c25bbf15SJan Beulich                 wb_mask &= ~((wp_mask >> ((find_addr - real_offset) << 3))
3395c83b2f5SJan Beulich                              << ((len - emul_len) << 3));
3405c83b2f5SJan Beulich             }
34180b4ecc8SPaolo Bonzini 
34280b4ecc8SPaolo Bonzini             /* do emulation based on register size */
34380b4ecc8SPaolo Bonzini             switch (reg->size) {
34480b4ecc8SPaolo Bonzini             case 1:
34580b4ecc8SPaolo Bonzini                 if (reg->u.b.write) {
34680b4ecc8SPaolo Bonzini                     rc = reg->u.b.write(s, reg_entry, ptr_val,
34780b4ecc8SPaolo Bonzini                                         read_val >> ((real_offset & 3) << 3),
34880b4ecc8SPaolo Bonzini                                         valid_mask);
34980b4ecc8SPaolo Bonzini                 }
35080b4ecc8SPaolo Bonzini                 break;
35180b4ecc8SPaolo Bonzini             case 2:
35280b4ecc8SPaolo Bonzini                 if (reg->u.w.write) {
35380b4ecc8SPaolo Bonzini                     rc = reg->u.w.write(s, reg_entry, (uint16_t *)ptr_val,
35480b4ecc8SPaolo Bonzini                                         (read_val >> ((real_offset & 3) << 3)),
35580b4ecc8SPaolo Bonzini                                         valid_mask);
35680b4ecc8SPaolo Bonzini                 }
35780b4ecc8SPaolo Bonzini                 break;
35880b4ecc8SPaolo Bonzini             case 4:
35980b4ecc8SPaolo Bonzini                 if (reg->u.dw.write) {
36080b4ecc8SPaolo Bonzini                     rc = reg->u.dw.write(s, reg_entry, (uint32_t *)ptr_val,
36180b4ecc8SPaolo Bonzini                                          (read_val >> ((real_offset & 3) << 3)),
36280b4ecc8SPaolo Bonzini                                          valid_mask);
36380b4ecc8SPaolo Bonzini                 }
36480b4ecc8SPaolo Bonzini                 break;
36580b4ecc8SPaolo Bonzini             }
36680b4ecc8SPaolo Bonzini 
36780b4ecc8SPaolo Bonzini             if (rc < 0) {
36880b4ecc8SPaolo Bonzini                 xen_shutdown_fatal_error("Internal error: Invalid write"
36980b4ecc8SPaolo Bonzini                                          " emulation. (%s, rc: %d)\n",
37080b4ecc8SPaolo Bonzini                                          __func__, rc);
37180b4ecc8SPaolo Bonzini                 return;
37280b4ecc8SPaolo Bonzini             }
37380b4ecc8SPaolo Bonzini 
37480b4ecc8SPaolo Bonzini             /* calculate next address to find */
37580b4ecc8SPaolo Bonzini             emul_len -= reg->size;
37680b4ecc8SPaolo Bonzini             if (emul_len > 0) {
37780b4ecc8SPaolo Bonzini                 find_addr = real_offset + reg->size;
37880b4ecc8SPaolo Bonzini             }
37980b4ecc8SPaolo Bonzini         } else {
38080b4ecc8SPaolo Bonzini             /* nothing to do with passthrough type register,
38180b4ecc8SPaolo Bonzini              * continue to find next byte */
382c25bbf15SJan Beulich             if (!s->permissive) {
383c25bbf15SJan Beulich                 wb_mask &= ~(0xff << ((len - emul_len) << 3));
384c25bbf15SJan Beulich                 /* Unused BARs will make it here, but we don't want to issue
385c25bbf15SJan Beulich                  * warnings for writes to them (bogus writes get dealt with
386c25bbf15SJan Beulich                  * above).
387c25bbf15SJan Beulich                  */
388c25bbf15SJan Beulich                 if (index < 0) {
389c25bbf15SJan Beulich                     wp_flag = true;
390c25bbf15SJan Beulich                 }
391c25bbf15SJan Beulich             }
39280b4ecc8SPaolo Bonzini             emul_len--;
39380b4ecc8SPaolo Bonzini             find_addr++;
39480b4ecc8SPaolo Bonzini         }
39580b4ecc8SPaolo Bonzini     }
39680b4ecc8SPaolo Bonzini 
397d3b9facbSKonrad Rzeszutek Wilk     /* need to shift back before passing them to xen_host_pci_set_block. */
39880b4ecc8SPaolo Bonzini     val >>= (addr & 3) << 3;
39980b4ecc8SPaolo Bonzini 
40080b4ecc8SPaolo Bonzini     memory_region_transaction_commit();
40180b4ecc8SPaolo Bonzini 
40280b4ecc8SPaolo Bonzini out:
403c25bbf15SJan Beulich     if (wp_flag && !s->permissive_warned) {
404c25bbf15SJan Beulich         s->permissive_warned = true;
405c25bbf15SJan Beulich         xen_pt_log(d, "Write-back to unknown field 0x%02x (partially) inhibited (0x%0*x)\n",
406c25bbf15SJan Beulich                    addr, len * 2, wb_mask);
407c25bbf15SJan Beulich         xen_pt_log(d, "If the device doesn't work, try enabling permissive mode\n");
408c25bbf15SJan Beulich         xen_pt_log(d, "(unsafe) and if it helps report the problem to xen-devel\n");
409c25bbf15SJan Beulich     }
4105c83b2f5SJan Beulich     for (index = 0; wb_mask; index += len) {
41180b4ecc8SPaolo Bonzini         /* unknown regs are passed through */
4125c83b2f5SJan Beulich         while (!(wb_mask & 0xff)) {
4135c83b2f5SJan Beulich             index++;
4145c83b2f5SJan Beulich             wb_mask >>= 8;
4155c83b2f5SJan Beulich         }
4165c83b2f5SJan Beulich         len = 0;
4175c83b2f5SJan Beulich         do {
4185c83b2f5SJan Beulich             len++;
4195c83b2f5SJan Beulich             wb_mask >>= 8;
4205c83b2f5SJan Beulich         } while (wb_mask & 0xff);
4215c83b2f5SJan Beulich         rc = xen_host_pci_set_block(&s->real_device, addr + index,
4225c83b2f5SJan Beulich                                     (uint8_t *)&val + index, len);
42380b4ecc8SPaolo Bonzini 
42480b4ecc8SPaolo Bonzini         if (rc < 0) {
425d3b9facbSKonrad Rzeszutek Wilk             XEN_PT_ERR(d, "xen_host_pci_set_block failed. return value: %d.\n", rc);
42680b4ecc8SPaolo Bonzini         }
42780b4ecc8SPaolo Bonzini     }
42880b4ecc8SPaolo Bonzini }
42980b4ecc8SPaolo Bonzini 
43080b4ecc8SPaolo Bonzini /* register regions */
43180b4ecc8SPaolo Bonzini 
xen_pt_bar_read(void * o,hwaddr addr,unsigned size)43280b4ecc8SPaolo Bonzini static uint64_t xen_pt_bar_read(void *o, hwaddr addr,
43380b4ecc8SPaolo Bonzini                                 unsigned size)
43480b4ecc8SPaolo Bonzini {
43580b4ecc8SPaolo Bonzini     PCIDevice *d = o;
43680b4ecc8SPaolo Bonzini     /* if this function is called, that probably means that there is a
43780b4ecc8SPaolo Bonzini      * misconfiguration of the IOMMU. */
438883f2c59SPhilippe Mathieu-Daudé     XEN_PT_ERR(d, "Should not read BAR through QEMU. @0x"HWADDR_FMT_plx"\n",
43980b4ecc8SPaolo Bonzini                addr);
44080b4ecc8SPaolo Bonzini     return 0;
44180b4ecc8SPaolo Bonzini }
xen_pt_bar_write(void * o,hwaddr addr,uint64_t val,unsigned size)44280b4ecc8SPaolo Bonzini static void xen_pt_bar_write(void *o, hwaddr addr, uint64_t val,
44380b4ecc8SPaolo Bonzini                              unsigned size)
44480b4ecc8SPaolo Bonzini {
44580b4ecc8SPaolo Bonzini     PCIDevice *d = o;
44680b4ecc8SPaolo Bonzini     /* Same comment as xen_pt_bar_read function */
447883f2c59SPhilippe Mathieu-Daudé     XEN_PT_ERR(d, "Should not write BAR through QEMU. @0x"HWADDR_FMT_plx"\n",
44880b4ecc8SPaolo Bonzini                addr);
44980b4ecc8SPaolo Bonzini }
45080b4ecc8SPaolo Bonzini 
45180b4ecc8SPaolo Bonzini static const MemoryRegionOps ops = {
45280b4ecc8SPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
45380b4ecc8SPaolo Bonzini     .read = xen_pt_bar_read,
45480b4ecc8SPaolo Bonzini     .write = xen_pt_bar_write,
45580b4ecc8SPaolo Bonzini };
45680b4ecc8SPaolo Bonzini 
xen_pt_register_regions(XenPCIPassthroughState * s,uint16_t * cmd)45781b23ef8SJan Beulich static int xen_pt_register_regions(XenPCIPassthroughState *s, uint16_t *cmd)
45880b4ecc8SPaolo Bonzini {
45980b4ecc8SPaolo Bonzini     int i = 0;
46080b4ecc8SPaolo Bonzini     XenHostPCIDevice *d = &s->real_device;
46180b4ecc8SPaolo Bonzini 
46280b4ecc8SPaolo Bonzini     /* Register PIO/MMIO BARs */
46380b4ecc8SPaolo Bonzini     for (i = 0; i < PCI_ROM_SLOT; i++) {
46480b4ecc8SPaolo Bonzini         XenHostPCIIORegion *r = &d->io_regions[i];
46580b4ecc8SPaolo Bonzini         uint8_t type;
46680b4ecc8SPaolo Bonzini 
46780b4ecc8SPaolo Bonzini         if (r->base_addr == 0 || r->size == 0) {
46880b4ecc8SPaolo Bonzini             continue;
46980b4ecc8SPaolo Bonzini         }
47080b4ecc8SPaolo Bonzini 
47180b4ecc8SPaolo Bonzini         s->bases[i].access.u = r->base_addr;
47280b4ecc8SPaolo Bonzini 
47380b4ecc8SPaolo Bonzini         if (r->type & XEN_HOST_PCI_REGION_TYPE_IO) {
47480b4ecc8SPaolo Bonzini             type = PCI_BASE_ADDRESS_SPACE_IO;
47581b23ef8SJan Beulich             *cmd |= PCI_COMMAND_IO;
47680b4ecc8SPaolo Bonzini         } else {
47780b4ecc8SPaolo Bonzini             type = PCI_BASE_ADDRESS_SPACE_MEMORY;
47880b4ecc8SPaolo Bonzini             if (r->type & XEN_HOST_PCI_REGION_TYPE_PREFETCH) {
47980b4ecc8SPaolo Bonzini                 type |= PCI_BASE_ADDRESS_MEM_PREFETCH;
48080b4ecc8SPaolo Bonzini             }
48180b4ecc8SPaolo Bonzini             if (r->type & XEN_HOST_PCI_REGION_TYPE_MEM_64) {
48280b4ecc8SPaolo Bonzini                 type |= PCI_BASE_ADDRESS_MEM_TYPE_64;
48380b4ecc8SPaolo Bonzini             }
48481b23ef8SJan Beulich             *cmd |= PCI_COMMAND_MEMORY;
48580b4ecc8SPaolo Bonzini         }
48680b4ecc8SPaolo Bonzini 
48722fc860bSPaolo Bonzini         memory_region_init_io(&s->bar[i], OBJECT(s), &ops, &s->dev,
48880b4ecc8SPaolo Bonzini                               "xen-pci-pt-bar", r->size);
48980b4ecc8SPaolo Bonzini         pci_register_bar(&s->dev, i, type, &s->bar[i]);
49080b4ecc8SPaolo Bonzini 
491fc33b900SAnthony PERARD         XEN_PT_LOG(&s->dev, "IO region %i registered (size=0x%08"PRIx64
49201d152c0SXinhao Zhang                    " base_addr=0x%08"PRIx64" type: 0x%x)\n",
49380b4ecc8SPaolo Bonzini                    i, r->size, r->base_addr, type);
49480b4ecc8SPaolo Bonzini     }
49580b4ecc8SPaolo Bonzini 
49680b4ecc8SPaolo Bonzini     /* Register expansion ROM address */
49780b4ecc8SPaolo Bonzini     if (d->rom.base_addr && d->rom.size) {
49880b4ecc8SPaolo Bonzini         uint32_t bar_data = 0;
49980b4ecc8SPaolo Bonzini 
50080b4ecc8SPaolo Bonzini         /* Re-set BAR reported by OS, otherwise ROM can't be read. */
50180b4ecc8SPaolo Bonzini         if (xen_host_pci_get_long(d, PCI_ROM_ADDRESS, &bar_data)) {
50280b4ecc8SPaolo Bonzini             return 0;
50380b4ecc8SPaolo Bonzini         }
50480b4ecc8SPaolo Bonzini         if ((bar_data & PCI_ROM_ADDRESS_MASK) == 0) {
50580b4ecc8SPaolo Bonzini             bar_data |= d->rom.base_addr & PCI_ROM_ADDRESS_MASK;
50680b4ecc8SPaolo Bonzini             xen_host_pci_set_long(d, PCI_ROM_ADDRESS, bar_data);
50780b4ecc8SPaolo Bonzini         }
50880b4ecc8SPaolo Bonzini 
50980b4ecc8SPaolo Bonzini         s->bases[PCI_ROM_SLOT].access.maddr = d->rom.base_addr;
51080b4ecc8SPaolo Bonzini 
511794798e3SAnthony PERARD         memory_region_init_io(&s->rom, OBJECT(s), &ops, &s->dev,
51280b4ecc8SPaolo Bonzini                               "xen-pci-pt-rom", d->rom.size);
51380b4ecc8SPaolo Bonzini         pci_register_bar(&s->dev, PCI_ROM_SLOT, PCI_BASE_ADDRESS_MEM_PREFETCH,
51480b4ecc8SPaolo Bonzini                          &s->rom);
51580b4ecc8SPaolo Bonzini 
51680b4ecc8SPaolo Bonzini         XEN_PT_LOG(&s->dev, "Expansion ROM registered (size=0x%08"PRIx64
51780b4ecc8SPaolo Bonzini                    " base_addr=0x%08"PRIx64")\n",
51880b4ecc8SPaolo Bonzini                    d->rom.size, d->rom.base_addr);
51980b4ecc8SPaolo Bonzini     }
52080b4ecc8SPaolo Bonzini 
52179814179STiejun Chen     xen_pt_register_vga_regions(d);
52280b4ecc8SPaolo Bonzini     return 0;
52380b4ecc8SPaolo Bonzini }
52480b4ecc8SPaolo Bonzini 
52580b4ecc8SPaolo Bonzini /* region mapping */
52680b4ecc8SPaolo Bonzini 
xen_pt_bar_from_region(XenPCIPassthroughState * s,MemoryRegion * mr)52780b4ecc8SPaolo Bonzini static int xen_pt_bar_from_region(XenPCIPassthroughState *s, MemoryRegion *mr)
52880b4ecc8SPaolo Bonzini {
52980b4ecc8SPaolo Bonzini     int i = 0;
53080b4ecc8SPaolo Bonzini 
53180b4ecc8SPaolo Bonzini     for (i = 0; i < PCI_NUM_REGIONS - 1; i++) {
53280b4ecc8SPaolo Bonzini         if (mr == &s->bar[i]) {
53380b4ecc8SPaolo Bonzini             return i;
53480b4ecc8SPaolo Bonzini         }
53580b4ecc8SPaolo Bonzini     }
53680b4ecc8SPaolo Bonzini     if (mr == &s->rom) {
53780b4ecc8SPaolo Bonzini         return PCI_ROM_SLOT;
53880b4ecc8SPaolo Bonzini     }
53980b4ecc8SPaolo Bonzini     return -1;
54080b4ecc8SPaolo Bonzini }
54180b4ecc8SPaolo Bonzini 
54280b4ecc8SPaolo Bonzini /*
54380b4ecc8SPaolo Bonzini  * This function checks if an io_region overlaps an io_region from another
54480b4ecc8SPaolo Bonzini  * device.  The io_region to check is provided with (addr, size and type)
54580b4ecc8SPaolo Bonzini  * A callback can be provided and will be called for every region that is
54680b4ecc8SPaolo Bonzini  * overlapped.
54780b4ecc8SPaolo Bonzini  * The return value indicates if the region is overlappsed */
54880b4ecc8SPaolo Bonzini struct CheckBarArgs {
54980b4ecc8SPaolo Bonzini     XenPCIPassthroughState *s;
55080b4ecc8SPaolo Bonzini     pcibus_t addr;
55180b4ecc8SPaolo Bonzini     pcibus_t size;
55280b4ecc8SPaolo Bonzini     uint8_t type;
55380b4ecc8SPaolo Bonzini     bool rc;
55480b4ecc8SPaolo Bonzini };
xen_pt_check_bar_overlap(PCIBus * bus,PCIDevice * d,void * opaque)55580b4ecc8SPaolo Bonzini static void xen_pt_check_bar_overlap(PCIBus *bus, PCIDevice *d, void *opaque)
55680b4ecc8SPaolo Bonzini {
55780b4ecc8SPaolo Bonzini     struct CheckBarArgs *arg = opaque;
55880b4ecc8SPaolo Bonzini     XenPCIPassthroughState *s = arg->s;
55980b4ecc8SPaolo Bonzini     uint8_t type = arg->type;
56080b4ecc8SPaolo Bonzini     int i;
56180b4ecc8SPaolo Bonzini 
56280b4ecc8SPaolo Bonzini     if (d->devfn == s->dev.devfn) {
56380b4ecc8SPaolo Bonzini         return;
56480b4ecc8SPaolo Bonzini     }
56580b4ecc8SPaolo Bonzini 
56680b4ecc8SPaolo Bonzini     /* xxx: This ignores bridges. */
56780b4ecc8SPaolo Bonzini     for (i = 0; i < PCI_NUM_REGIONS; i++) {
56880b4ecc8SPaolo Bonzini         const PCIIORegion *r = &d->io_regions[i];
56980b4ecc8SPaolo Bonzini 
57080b4ecc8SPaolo Bonzini         if (!r->size) {
57180b4ecc8SPaolo Bonzini             continue;
57280b4ecc8SPaolo Bonzini         }
57380b4ecc8SPaolo Bonzini         if ((type & PCI_BASE_ADDRESS_SPACE_IO)
57480b4ecc8SPaolo Bonzini             != (r->type & PCI_BASE_ADDRESS_SPACE_IO)) {
57580b4ecc8SPaolo Bonzini             continue;
57680b4ecc8SPaolo Bonzini         }
57780b4ecc8SPaolo Bonzini 
57880b4ecc8SPaolo Bonzini         if (ranges_overlap(arg->addr, arg->size, r->addr, r->size)) {
57980b4ecc8SPaolo Bonzini             XEN_PT_WARN(&s->dev,
58080b4ecc8SPaolo Bonzini                         "Overlapped to device [%02x:%02x.%d] Region: %i"
58101d152c0SXinhao Zhang                         " (addr: 0x%"FMT_PCIBUS", len: 0x%"FMT_PCIBUS")\n",
58280b4ecc8SPaolo Bonzini                         pci_bus_num(bus), PCI_SLOT(d->devfn),
58380b4ecc8SPaolo Bonzini                         PCI_FUNC(d->devfn), i, r->addr, r->size);
58480b4ecc8SPaolo Bonzini             arg->rc = true;
58580b4ecc8SPaolo Bonzini         }
58680b4ecc8SPaolo Bonzini     }
58780b4ecc8SPaolo Bonzini }
58880b4ecc8SPaolo Bonzini 
xen_pt_region_update(XenPCIPassthroughState * s,MemoryRegionSection * sec,bool adding)58980b4ecc8SPaolo Bonzini static void xen_pt_region_update(XenPCIPassthroughState *s,
59080b4ecc8SPaolo Bonzini                                  MemoryRegionSection *sec, bool adding)
59180b4ecc8SPaolo Bonzini {
59280b4ecc8SPaolo Bonzini     PCIDevice *d = &s->dev;
59380b4ecc8SPaolo Bonzini     MemoryRegion *mr = sec->mr;
59480b4ecc8SPaolo Bonzini     int bar = -1;
59580b4ecc8SPaolo Bonzini     int rc;
59680b4ecc8SPaolo Bonzini     int op = adding ? DPCI_ADD_MAPPING : DPCI_REMOVE_MAPPING;
59780b4ecc8SPaolo Bonzini     struct CheckBarArgs args = {
59880b4ecc8SPaolo Bonzini         .s = s,
59980b4ecc8SPaolo Bonzini         .addr = sec->offset_within_address_space,
600052e87b0SPaolo Bonzini         .size = int128_get64(sec->size),
60180b4ecc8SPaolo Bonzini         .rc = false,
60280b4ecc8SPaolo Bonzini     };
60380b4ecc8SPaolo Bonzini 
60480b4ecc8SPaolo Bonzini     bar = xen_pt_bar_from_region(s, mr);
60580b4ecc8SPaolo Bonzini     if (bar == -1 && (!s->msix || &s->msix->mmio != mr)) {
60680b4ecc8SPaolo Bonzini         return;
60780b4ecc8SPaolo Bonzini     }
60880b4ecc8SPaolo Bonzini 
60980b4ecc8SPaolo Bonzini     if (s->msix && &s->msix->mmio == mr) {
61080b4ecc8SPaolo Bonzini         if (adding) {
61180b4ecc8SPaolo Bonzini             s->msix->mmio_base_addr = sec->offset_within_address_space;
61280b4ecc8SPaolo Bonzini             rc = xen_pt_msix_update_remap(s, s->msix->bar_index);
61380b4ecc8SPaolo Bonzini         }
61480b4ecc8SPaolo Bonzini         return;
61580b4ecc8SPaolo Bonzini     }
61680b4ecc8SPaolo Bonzini 
61780b4ecc8SPaolo Bonzini     args.type = d->io_regions[bar].type;
6182914fc61SPeter Xu     pci_for_each_device_under_bus(pci_get_bus(d),
61980b4ecc8SPaolo Bonzini                                   xen_pt_check_bar_overlap, &args);
62080b4ecc8SPaolo Bonzini     if (args.rc) {
62101d152c0SXinhao Zhang         XEN_PT_WARN(d, "Region: %d (addr: 0x%"FMT_PCIBUS
62201d152c0SXinhao Zhang                     ", len: 0x%"FMT_PCIBUS") is overlapped.\n",
623d18e173aSWei Liu                     bar, sec->offset_within_address_space,
624d18e173aSWei Liu                     int128_get64(sec->size));
62580b4ecc8SPaolo Bonzini     }
62680b4ecc8SPaolo Bonzini 
62780b4ecc8SPaolo Bonzini     if (d->io_regions[bar].type & PCI_BASE_ADDRESS_SPACE_IO) {
62880b4ecc8SPaolo Bonzini         uint32_t guest_port = sec->offset_within_address_space;
62980b4ecc8SPaolo Bonzini         uint32_t machine_port = s->bases[bar].access.pio_base;
630052e87b0SPaolo Bonzini         uint32_t size = int128_get64(sec->size);
63180b4ecc8SPaolo Bonzini         rc = xc_domain_ioport_mapping(xen_xc, xen_domid,
63280b4ecc8SPaolo Bonzini                                       guest_port, machine_port, size,
63380b4ecc8SPaolo Bonzini                                       op);
63480b4ecc8SPaolo Bonzini         if (rc) {
6353782f60dSJan Beulich             XEN_PT_ERR(d, "%s ioport mapping failed! (err: %i)\n",
6363782f60dSJan Beulich                        adding ? "create new" : "remove old", errno);
63780b4ecc8SPaolo Bonzini         }
63880b4ecc8SPaolo Bonzini     } else {
63980b4ecc8SPaolo Bonzini         pcibus_t guest_addr = sec->offset_within_address_space;
64080b4ecc8SPaolo Bonzini         pcibus_t machine_addr = s->bases[bar].access.maddr
64180b4ecc8SPaolo Bonzini             + sec->offset_within_region;
642052e87b0SPaolo Bonzini         pcibus_t size = int128_get64(sec->size);
64380b4ecc8SPaolo Bonzini         rc = xc_domain_memory_mapping(xen_xc, xen_domid,
64480b4ecc8SPaolo Bonzini                                       XEN_PFN(guest_addr + XC_PAGE_SIZE - 1),
64580b4ecc8SPaolo Bonzini                                       XEN_PFN(machine_addr + XC_PAGE_SIZE - 1),
64680b4ecc8SPaolo Bonzini                                       XEN_PFN(size + XC_PAGE_SIZE - 1),
64780b4ecc8SPaolo Bonzini                                       op);
64880b4ecc8SPaolo Bonzini         if (rc) {
6493782f60dSJan Beulich             XEN_PT_ERR(d, "%s mem mapping failed! (err: %i)\n",
6503782f60dSJan Beulich                        adding ? "create new" : "remove old", errno);
65180b4ecc8SPaolo Bonzini         }
65280b4ecc8SPaolo Bonzini     }
65380b4ecc8SPaolo Bonzini }
65480b4ecc8SPaolo Bonzini 
xen_pt_region_add(MemoryListener * l,MemoryRegionSection * sec)65580b4ecc8SPaolo Bonzini static void xen_pt_region_add(MemoryListener *l, MemoryRegionSection *sec)
65680b4ecc8SPaolo Bonzini {
65780b4ecc8SPaolo Bonzini     XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState,
65880b4ecc8SPaolo Bonzini                                              memory_listener);
65980b4ecc8SPaolo Bonzini 
660dfde4e6eSPaolo Bonzini     memory_region_ref(sec->mr);
66180b4ecc8SPaolo Bonzini     xen_pt_region_update(s, sec, true);
66280b4ecc8SPaolo Bonzini }
66380b4ecc8SPaolo Bonzini 
xen_pt_region_del(MemoryListener * l,MemoryRegionSection * sec)66480b4ecc8SPaolo Bonzini static void xen_pt_region_del(MemoryListener *l, MemoryRegionSection *sec)
66580b4ecc8SPaolo Bonzini {
66680b4ecc8SPaolo Bonzini     XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState,
66780b4ecc8SPaolo Bonzini                                              memory_listener);
66880b4ecc8SPaolo Bonzini 
66980b4ecc8SPaolo Bonzini     xen_pt_region_update(s, sec, false);
670dfde4e6eSPaolo Bonzini     memory_region_unref(sec->mr);
67180b4ecc8SPaolo Bonzini }
67280b4ecc8SPaolo Bonzini 
xen_pt_io_region_add(MemoryListener * l,MemoryRegionSection * sec)67380b4ecc8SPaolo Bonzini static void xen_pt_io_region_add(MemoryListener *l, MemoryRegionSection *sec)
67480b4ecc8SPaolo Bonzini {
67580b4ecc8SPaolo Bonzini     XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState,
67680b4ecc8SPaolo Bonzini                                              io_listener);
67780b4ecc8SPaolo Bonzini 
678dfde4e6eSPaolo Bonzini     memory_region_ref(sec->mr);
67980b4ecc8SPaolo Bonzini     xen_pt_region_update(s, sec, true);
68080b4ecc8SPaolo Bonzini }
68180b4ecc8SPaolo Bonzini 
xen_pt_io_region_del(MemoryListener * l,MemoryRegionSection * sec)68280b4ecc8SPaolo Bonzini static void xen_pt_io_region_del(MemoryListener *l, MemoryRegionSection *sec)
68380b4ecc8SPaolo Bonzini {
68480b4ecc8SPaolo Bonzini     XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState,
68580b4ecc8SPaolo Bonzini                                              io_listener);
68680b4ecc8SPaolo Bonzini 
68780b4ecc8SPaolo Bonzini     xen_pt_region_update(s, sec, false);
688dfde4e6eSPaolo Bonzini     memory_region_unref(sec->mr);
68980b4ecc8SPaolo Bonzini }
69080b4ecc8SPaolo Bonzini 
69180b4ecc8SPaolo Bonzini static const MemoryListener xen_pt_memory_listener = {
692142518bdSPeter Xu     .name = "xen-pt-mem",
69380b4ecc8SPaolo Bonzini     .region_add = xen_pt_region_add,
69480b4ecc8SPaolo Bonzini     .region_del = xen_pt_region_del,
6955369a36cSIsaku Yamahata     .priority = MEMORY_LISTENER_PRIORITY_ACCEL,
69680b4ecc8SPaolo Bonzini };
69780b4ecc8SPaolo Bonzini 
69880b4ecc8SPaolo Bonzini static const MemoryListener xen_pt_io_listener = {
699142518bdSPeter Xu     .name = "xen-pt-io",
70080b4ecc8SPaolo Bonzini     .region_add = xen_pt_io_region_add,
70180b4ecc8SPaolo Bonzini     .region_del = xen_pt_io_region_del,
7025369a36cSIsaku Yamahata     .priority = MEMORY_LISTENER_PRIORITY_ACCEL,
70380b4ecc8SPaolo Bonzini };
70480b4ecc8SPaolo Bonzini 
705df6aa457SKonrad Rzeszutek Wilk /* destroy. */
xen_pt_destroy(PCIDevice * d)706df6aa457SKonrad Rzeszutek Wilk static void xen_pt_destroy(PCIDevice *d) {
707df6aa457SKonrad Rzeszutek Wilk 
708df6aa457SKonrad Rzeszutek Wilk     XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
709df6aa457SKonrad Rzeszutek Wilk     XenHostPCIDevice *host_dev = &s->real_device;
710df6aa457SKonrad Rzeszutek Wilk     uint8_t machine_irq = s->machine_irq;
711df6aa457SKonrad Rzeszutek Wilk     uint8_t intx;
712df6aa457SKonrad Rzeszutek Wilk     int rc;
713df6aa457SKonrad Rzeszutek Wilk 
714*ee1004bbSPhilippe Mathieu-Daudé     if (machine_irq && !xen_host_pci_device_closed(host_dev)) {
715df6aa457SKonrad Rzeszutek Wilk         intx = xen_pt_pci_intx(s);
716df6aa457SKonrad Rzeszutek Wilk         rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq,
717df6aa457SKonrad Rzeszutek Wilk                                      PT_IRQ_TYPE_PCI,
718cdc57472SDavid Gibson                                      pci_dev_bus_num(d),
719df6aa457SKonrad Rzeszutek Wilk                                      PCI_SLOT(s->dev.devfn),
720df6aa457SKonrad Rzeszutek Wilk                                      intx,
721df6aa457SKonrad Rzeszutek Wilk                                      0 /* isa_irq */);
722df6aa457SKonrad Rzeszutek Wilk         if (rc < 0) {
723df6aa457SKonrad Rzeszutek Wilk             XEN_PT_ERR(d, "unbinding of interrupt INT%c failed."
724df6aa457SKonrad Rzeszutek Wilk                        " (machine irq: %i, err: %d)"
725df6aa457SKonrad Rzeszutek Wilk                        " But bravely continuing on..\n",
726df6aa457SKonrad Rzeszutek Wilk                        'a' + intx, machine_irq, errno);
727df6aa457SKonrad Rzeszutek Wilk         }
728df6aa457SKonrad Rzeszutek Wilk     }
729df6aa457SKonrad Rzeszutek Wilk 
730df6aa457SKonrad Rzeszutek Wilk     /* N.B. xen_pt_config_delete takes care of freeing them. */
731df6aa457SKonrad Rzeszutek Wilk     if (s->msi) {
732df6aa457SKonrad Rzeszutek Wilk         xen_pt_msi_disable(s);
733df6aa457SKonrad Rzeszutek Wilk     }
734df6aa457SKonrad Rzeszutek Wilk     if (s->msix) {
735df6aa457SKonrad Rzeszutek Wilk         xen_pt_msix_disable(s);
736df6aa457SKonrad Rzeszutek Wilk     }
737df6aa457SKonrad Rzeszutek Wilk 
738df6aa457SKonrad Rzeszutek Wilk     if (machine_irq) {
739df6aa457SKonrad Rzeszutek Wilk         xen_pt_mapped_machine_irq[machine_irq]--;
740df6aa457SKonrad Rzeszutek Wilk 
741df6aa457SKonrad Rzeszutek Wilk         if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
742df6aa457SKonrad Rzeszutek Wilk             rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq);
743df6aa457SKonrad Rzeszutek Wilk 
744df6aa457SKonrad Rzeszutek Wilk             if (rc < 0) {
745df6aa457SKonrad Rzeszutek Wilk                 XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)"
746df6aa457SKonrad Rzeszutek Wilk                            " But bravely continuing on..\n",
747df6aa457SKonrad Rzeszutek Wilk                            machine_irq, errno);
748df6aa457SKonrad Rzeszutek Wilk             }
749df6aa457SKonrad Rzeszutek Wilk         }
750df6aa457SKonrad Rzeszutek Wilk         s->machine_irq = 0;
751df6aa457SKonrad Rzeszutek Wilk     }
752df6aa457SKonrad Rzeszutek Wilk 
753df6aa457SKonrad Rzeszutek Wilk     /* delete all emulated config registers */
754df6aa457SKonrad Rzeszutek Wilk     xen_pt_config_delete(s);
755df6aa457SKonrad Rzeszutek Wilk 
756df6aa457SKonrad Rzeszutek Wilk     xen_pt_unregister_vga_regions(host_dev);
757df6aa457SKonrad Rzeszutek Wilk 
758df6aa457SKonrad Rzeszutek Wilk     if (s->listener_set) {
759df6aa457SKonrad Rzeszutek Wilk         memory_listener_unregister(&s->memory_listener);
760df6aa457SKonrad Rzeszutek Wilk         memory_listener_unregister(&s->io_listener);
761df6aa457SKonrad Rzeszutek Wilk         s->listener_set = false;
762df6aa457SKonrad Rzeszutek Wilk     }
763*ee1004bbSPhilippe Mathieu-Daudé     if (!xen_host_pci_device_closed(host_dev)) {
764*ee1004bbSPhilippe Mathieu-Daudé         xen_host_pci_device_put(host_dev);
765df6aa457SKonrad Rzeszutek Wilk     }
766df6aa457SKonrad Rzeszutek Wilk }
76780b4ecc8SPaolo Bonzini /* init */
76880b4ecc8SPaolo Bonzini 
xen_pt_realize(PCIDevice * d,Error ** errp)7695a11d0f7SCao jin static void xen_pt_realize(PCIDevice *d, Error **errp)
77080b4ecc8SPaolo Bonzini {
7711de7096dSVladimir Sementsov-Ogievskiy     ERRP_GUARD();
772f9b9d292SGonglei     XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
7735a11d0f7SCao jin     int i, rc = 0;
7746aa07b14SKonrad Rzeszutek Wilk     uint8_t machine_irq = 0, scratch;
77581b23ef8SJan Beulich     uint16_t cmd = 0;
77680b4ecc8SPaolo Bonzini     int pirq = XEN_PT_UNASSIGNED_PIRQ;
77780b4ecc8SPaolo Bonzini 
77880b4ecc8SPaolo Bonzini     /* register real device */
77980b4ecc8SPaolo Bonzini     XEN_PT_LOG(d, "Assigning real physical device %02x:%02x.%d"
78001d152c0SXinhao Zhang                " to devfn 0x%x\n",
78180b4ecc8SPaolo Bonzini                s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function,
78280b4ecc8SPaolo Bonzini                s->dev.devfn);
78380b4ecc8SPaolo Bonzini 
78480b4ecc8SPaolo Bonzini     s->is_virtfn = s->real_device.is_virtfn;
78580b4ecc8SPaolo Bonzini     if (s->is_virtfn) {
78680b4ecc8SPaolo Bonzini         XEN_PT_LOG(d, "%04x:%02x:%02x.%d is a SR-IOV Virtual Function\n",
78780b4ecc8SPaolo Bonzini                    s->real_device.domain, s->real_device.bus,
78880b4ecc8SPaolo Bonzini                    s->real_device.dev, s->real_device.func);
78980b4ecc8SPaolo Bonzini     }
79080b4ecc8SPaolo Bonzini 
79180b4ecc8SPaolo Bonzini     /* Initialize virtualized PCI configuration (Extended 256 Bytes) */
792cae99f1dSKonrad Rzeszutek Wilk     memset(d->config, 0, PCI_CONFIG_SPACE_SIZE);
79380b4ecc8SPaolo Bonzini 
79480b4ecc8SPaolo Bonzini     s->memory_listener = xen_pt_memory_listener;
79580b4ecc8SPaolo Bonzini     s->io_listener = xen_pt_io_listener;
79680b4ecc8SPaolo Bonzini 
797881213f1STiejun Chen     /* Setup VGA bios for passthrough GFX */
7984f67543bSChuck Zmudzinski     if ((s->real_device.domain == XEN_PCI_IGD_DOMAIN) &&
7994f67543bSChuck Zmudzinski         (s->real_device.bus == XEN_PCI_IGD_BUS) &&
8004f67543bSChuck Zmudzinski         (s->real_device.dev == XEN_PCI_IGD_DEV) &&
8014f67543bSChuck Zmudzinski         (s->real_device.func == XEN_PCI_IGD_FN)) {
802f37d630aSTiejun Chen         if (!is_igd_vga_passthrough(&s->real_device)) {
8035a11d0f7SCao jin             error_setg(errp, "Need to enable igd-passthru if you're trying"
8045a11d0f7SCao jin                     " to passthrough IGD GFX");
805f37d630aSTiejun Chen             xen_host_pci_device_put(&s->real_device);
8065a11d0f7SCao jin             return;
807f37d630aSTiejun Chen         }
808f37d630aSTiejun Chen 
8091de7096dSVladimir Sementsov-Ogievskiy         xen_pt_setup_vga(s, &s->real_device, errp);
8101de7096dSVladimir Sementsov-Ogievskiy         if (*errp) {
8111de7096dSVladimir Sementsov-Ogievskiy             error_append_hint(errp, "Setup VGA BIOS of passthrough"
8125226bb59SCao jin                               " GFX failed");
813881213f1STiejun Chen             xen_host_pci_device_put(&s->real_device);
8145a11d0f7SCao jin             return;
815881213f1STiejun Chen         }
816f37d630aSTiejun Chen 
817f37d630aSTiejun Chen         /* Register ISA bridge for passthrough GFX. */
818f37d630aSTiejun Chen         xen_igd_passthrough_isa_bridge_create(s, &s->real_device);
819881213f1STiejun Chen     }
820881213f1STiejun Chen 
82180b4ecc8SPaolo Bonzini     /* Handle real device's MMIO/PIO BARs */
82281b23ef8SJan Beulich     xen_pt_register_regions(s, &cmd);
82380b4ecc8SPaolo Bonzini 
82480b4ecc8SPaolo Bonzini     /* reinitialize each config register to be emulated */
8251de7096dSVladimir Sementsov-Ogievskiy     xen_pt_config_init(s, errp);
8261de7096dSVladimir Sementsov-Ogievskiy     if (*errp) {
8271de7096dSVladimir Sementsov-Ogievskiy         error_append_hint(errp, "PCI Config space initialisation failed");
828d50a6e58SCao jin         rc = -1;
8293d3697f2SKonrad Rzeszutek Wilk         goto err_out;
83080b4ecc8SPaolo Bonzini     }
83180b4ecc8SPaolo Bonzini 
83280b4ecc8SPaolo Bonzini     /* Bind interrupt */
8336aa07b14SKonrad Rzeszutek Wilk     rc = xen_host_pci_get_byte(&s->real_device, PCI_INTERRUPT_PIN, &scratch);
8346aa07b14SKonrad Rzeszutek Wilk     if (rc) {
8355a11d0f7SCao jin         error_setg_errno(errp, errno, "Failed to read PCI_INTERRUPT_PIN");
8363d3697f2SKonrad Rzeszutek Wilk         goto err_out;
8376aa07b14SKonrad Rzeszutek Wilk     }
8386aa07b14SKonrad Rzeszutek Wilk     if (!scratch) {
8390968c91cSBruce Rogers         XEN_PT_LOG(d, "no pin interrupt\n");
84080b4ecc8SPaolo Bonzini         goto out;
84180b4ecc8SPaolo Bonzini     }
84280b4ecc8SPaolo Bonzini 
84380b4ecc8SPaolo Bonzini     machine_irq = s->real_device.irq;
84492dbfcc6SZhao Yan     if (machine_irq == 0) {
84592dbfcc6SZhao Yan         XEN_PT_LOG(d, "machine irq is 0\n");
84692dbfcc6SZhao Yan         cmd |= PCI_COMMAND_INTX_DISABLE;
84792dbfcc6SZhao Yan         goto out;
84892dbfcc6SZhao Yan     }
84992dbfcc6SZhao Yan 
85080b4ecc8SPaolo Bonzini     rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq);
85180b4ecc8SPaolo Bonzini     if (rc < 0) {
852c61d1d9eSMarkus Armbruster         XEN_PT_ERR(d, "Mapping machine irq %u to pirq %i failed, (err: %d)\n",
853c61d1d9eSMarkus Armbruster                    machine_irq, pirq, errno);
85480b4ecc8SPaolo Bonzini 
85580b4ecc8SPaolo Bonzini         /* Disable PCI intx assertion (turn on bit10 of devctl) */
856950fe0aaSJan Beulich         cmd |= PCI_COMMAND_INTX_DISABLE;
85780b4ecc8SPaolo Bonzini         machine_irq = 0;
85880b4ecc8SPaolo Bonzini         s->machine_irq = 0;
85980b4ecc8SPaolo Bonzini     } else {
86080b4ecc8SPaolo Bonzini         machine_irq = pirq;
86180b4ecc8SPaolo Bonzini         s->machine_irq = pirq;
86280b4ecc8SPaolo Bonzini         xen_pt_mapped_machine_irq[machine_irq]++;
86380b4ecc8SPaolo Bonzini     }
86480b4ecc8SPaolo Bonzini 
86580b4ecc8SPaolo Bonzini     /* bind machine_irq to device */
86680b4ecc8SPaolo Bonzini     if (machine_irq != 0) {
86780b4ecc8SPaolo Bonzini         uint8_t e_intx = xen_pt_pci_intx(s);
86880b4ecc8SPaolo Bonzini 
86980b4ecc8SPaolo Bonzini         rc = xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, machine_irq,
870cdc57472SDavid Gibson                                        pci_dev_bus_num(d),
87180b4ecc8SPaolo Bonzini                                        PCI_SLOT(d->devfn),
87280b4ecc8SPaolo Bonzini                                        e_intx);
87380b4ecc8SPaolo Bonzini         if (rc < 0) {
874c61d1d9eSMarkus Armbruster             XEN_PT_ERR(d, "Binding of interrupt %i failed! (err: %d)\n",
875c61d1d9eSMarkus Armbruster                        e_intx, errno);
87680b4ecc8SPaolo Bonzini 
87780b4ecc8SPaolo Bonzini             /* Disable PCI intx assertion (turn on bit10 of devctl) */
878950fe0aaSJan Beulich             cmd |= PCI_COMMAND_INTX_DISABLE;
87980b4ecc8SPaolo Bonzini             xen_pt_mapped_machine_irq[machine_irq]--;
88080b4ecc8SPaolo Bonzini 
88180b4ecc8SPaolo Bonzini             if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
88280b4ecc8SPaolo Bonzini                 if (xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq)) {
883c61d1d9eSMarkus Armbruster                     XEN_PT_ERR(d, "Unmapping of machine interrupt %i failed!"
884c61d1d9eSMarkus Armbruster                                " (err: %d)\n", machine_irq, errno);
88580b4ecc8SPaolo Bonzini                 }
88680b4ecc8SPaolo Bonzini             }
88780b4ecc8SPaolo Bonzini             s->machine_irq = 0;
88880b4ecc8SPaolo Bonzini         }
88980b4ecc8SPaolo Bonzini     }
89080b4ecc8SPaolo Bonzini 
89180b4ecc8SPaolo Bonzini out:
89281b23ef8SJan Beulich     if (cmd) {
8936aa07b14SKonrad Rzeszutek Wilk         uint16_t val;
8946aa07b14SKonrad Rzeszutek Wilk 
8956aa07b14SKonrad Rzeszutek Wilk         rc = xen_host_pci_get_word(&s->real_device, PCI_COMMAND, &val);
8966aa07b14SKonrad Rzeszutek Wilk         if (rc) {
8975a11d0f7SCao jin             error_setg_errno(errp, errno, "Failed to read PCI_COMMAND");
8983d3697f2SKonrad Rzeszutek Wilk             goto err_out;
8996aa07b14SKonrad Rzeszutek Wilk         } else {
9006aa07b14SKonrad Rzeszutek Wilk             val |= cmd;
9016aa07b14SKonrad Rzeszutek Wilk             rc = xen_host_pci_set_word(&s->real_device, PCI_COMMAND, val);
9026aa07b14SKonrad Rzeszutek Wilk             if (rc) {
9035a11d0f7SCao jin                 error_setg_errno(errp, errno, "Failed to write PCI_COMMAND"
9045a11d0f7SCao jin                                  " val = 0x%x", val);
9053d3697f2SKonrad Rzeszutek Wilk                 goto err_out;
9066aa07b14SKonrad Rzeszutek Wilk             }
9076aa07b14SKonrad Rzeszutek Wilk         }
90881b23ef8SJan Beulich     }
90981b23ef8SJan Beulich 
91084201604SIgor Druzhinin     memory_listener_register(&s->memory_listener, &address_space_memory);
91180b4ecc8SPaolo Bonzini     memory_listener_register(&s->io_listener, &address_space_io);
912bce33948SKonrad Rzeszutek Wilk     s->listener_set = true;
91352f35022SStefan Weil     XEN_PT_LOG(d,
9145a11d0f7SCao jin                "Real physical device %02x:%02x.%d registered successfully\n",
91580b4ecc8SPaolo Bonzini                s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function);
91680b4ecc8SPaolo Bonzini 
9175a11d0f7SCao jin     return;
9183d3697f2SKonrad Rzeszutek Wilk 
9193d3697f2SKonrad Rzeszutek Wilk err_out:
9205a11d0f7SCao jin     for (i = 0; i < PCI_ROM_SLOT; i++) {
9215a11d0f7SCao jin         object_unparent(OBJECT(&s->bar[i]));
9225a11d0f7SCao jin     }
9235a11d0f7SCao jin     object_unparent(OBJECT(&s->rom));
9245a11d0f7SCao jin 
9253d3697f2SKonrad Rzeszutek Wilk     xen_pt_destroy(d);
9263d3697f2SKonrad Rzeszutek Wilk     assert(rc);
92780b4ecc8SPaolo Bonzini }
92880b4ecc8SPaolo Bonzini 
xen_pt_unregister_device(PCIDevice * d)92980b4ecc8SPaolo Bonzini static void xen_pt_unregister_device(PCIDevice *d)
93080b4ecc8SPaolo Bonzini {
931df6aa457SKonrad Rzeszutek Wilk     xen_pt_destroy(d);
932bce33948SKonrad Rzeszutek Wilk }
93380b4ecc8SPaolo Bonzini 
93480b4ecc8SPaolo Bonzini static Property xen_pci_passthrough_properties[] = {
93580b4ecc8SPaolo Bonzini     DEFINE_PROP_PCI_HOST_DEVADDR("hostaddr", XenPCIPassthroughState, hostaddr),
936c25bbf15SJan Beulich     DEFINE_PROP_BOOL("permissive", XenPCIPassthroughState, permissive, false),
93780b4ecc8SPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
93880b4ecc8SPaolo Bonzini };
93980b4ecc8SPaolo Bonzini 
xen_pci_passthrough_instance_init(Object * obj)940d61a363dSYoni Bettan static void xen_pci_passthrough_instance_init(Object *obj)
941d61a363dSYoni Bettan {
942d61a363dSYoni Bettan     /* QEMU_PCI_CAP_EXPRESS initialization does not depend on QEMU command
943d61a363dSYoni Bettan      * line, therefore, no need to wait to realize like other devices */
944d61a363dSYoni Bettan     PCI_DEVICE(obj)->cap_present |= QEMU_PCI_CAP_EXPRESS;
945d61a363dSYoni Bettan }
946d61a363dSYoni Bettan 
xen_igd_reserve_slot(PCIBus * pci_bus)9474f67543bSChuck Zmudzinski void xen_igd_reserve_slot(PCIBus *pci_bus)
9484f67543bSChuck Zmudzinski {
9494f67543bSChuck Zmudzinski     if (!xen_igd_gfx_pt_enabled()) {
9504f67543bSChuck Zmudzinski         return;
9514f67543bSChuck Zmudzinski     }
9524f67543bSChuck Zmudzinski 
9534f67543bSChuck Zmudzinski     XEN_PT_LOG(0, "Reserving PCI slot 2 for IGD\n");
954b93fe7f2SChuck Zmudzinski     pci_bus_set_slot_reserved_mask(pci_bus, XEN_PCI_IGD_SLOT_MASK);
9554f67543bSChuck Zmudzinski }
9564f67543bSChuck Zmudzinski 
xen_igd_clear_slot(DeviceState * qdev,Error ** errp)9574f67543bSChuck Zmudzinski static void xen_igd_clear_slot(DeviceState *qdev, Error **errp)
9584f67543bSChuck Zmudzinski {
9594f67543bSChuck Zmudzinski     ERRP_GUARD();
9604f67543bSChuck Zmudzinski     PCIDevice *pci_dev = (PCIDevice *)qdev;
9614f67543bSChuck Zmudzinski     XenPCIPassthroughState *s = XEN_PT_DEVICE(pci_dev);
9624f67543bSChuck Zmudzinski     XenPTDeviceClass *xpdc = XEN_PT_DEVICE_GET_CLASS(s);
9634f67543bSChuck Zmudzinski     PCIBus *pci_bus = pci_get_bus(pci_dev);
9644f67543bSChuck Zmudzinski 
9654f67543bSChuck Zmudzinski     xen_host_pci_device_get(&s->real_device,
9664f67543bSChuck Zmudzinski                             s->hostaddr.domain, s->hostaddr.bus,
9674f67543bSChuck Zmudzinski                             s->hostaddr.slot, s->hostaddr.function,
9684f67543bSChuck Zmudzinski                             errp);
9694f67543bSChuck Zmudzinski     if (*errp) {
9704f67543bSChuck Zmudzinski         error_append_hint(errp, "Failed to \"open\" the real pci device");
9714f67543bSChuck Zmudzinski         return;
9724f67543bSChuck Zmudzinski     }
9734f67543bSChuck Zmudzinski 
974b93fe7f2SChuck Zmudzinski     if (!(pci_bus_get_slot_reserved_mask(pci_bus) & XEN_PCI_IGD_SLOT_MASK)) {
9754f67543bSChuck Zmudzinski         xpdc->pci_qdev_realize(qdev, errp);
9764f67543bSChuck Zmudzinski         return;
9774f67543bSChuck Zmudzinski     }
9784f67543bSChuck Zmudzinski 
9794f67543bSChuck Zmudzinski     if (is_igd_vga_passthrough(&s->real_device) &&
9804f67543bSChuck Zmudzinski         s->real_device.domain == XEN_PCI_IGD_DOMAIN &&
9814f67543bSChuck Zmudzinski         s->real_device.bus == XEN_PCI_IGD_BUS &&
9824f67543bSChuck Zmudzinski         s->real_device.dev == XEN_PCI_IGD_DEV &&
9834f67543bSChuck Zmudzinski         s->real_device.func == XEN_PCI_IGD_FN &&
9844f67543bSChuck Zmudzinski         s->real_device.vendor_id == PCI_VENDOR_ID_INTEL) {
985b93fe7f2SChuck Zmudzinski         pci_bus_clear_slot_reserved_mask(pci_bus, XEN_PCI_IGD_SLOT_MASK);
9864f67543bSChuck Zmudzinski         XEN_PT_LOG(pci_dev, "Intel IGD found, using slot 2\n");
9874f67543bSChuck Zmudzinski     }
9884f67543bSChuck Zmudzinski     xpdc->pci_qdev_realize(qdev, errp);
9894f67543bSChuck Zmudzinski }
9904f67543bSChuck Zmudzinski 
xen_pci_passthrough_class_init(ObjectClass * klass,void * data)99180b4ecc8SPaolo Bonzini static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data)
99280b4ecc8SPaolo Bonzini {
99380b4ecc8SPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
99480b4ecc8SPaolo Bonzini     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
99580b4ecc8SPaolo Bonzini 
9964f67543bSChuck Zmudzinski     XenPTDeviceClass *xpdc = XEN_PT_DEVICE_CLASS(klass);
9974f67543bSChuck Zmudzinski     xpdc->pci_qdev_realize = dc->realize;
9984f67543bSChuck Zmudzinski     dc->realize = xen_igd_clear_slot;
9995a11d0f7SCao jin     k->realize = xen_pt_realize;
100080b4ecc8SPaolo Bonzini     k->exit = xen_pt_unregister_device;
100180b4ecc8SPaolo Bonzini     k->config_read = xen_pt_pci_read_config;
100280b4ecc8SPaolo Bonzini     k->config_write = xen_pt_pci_write_config;
1003125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
100480b4ecc8SPaolo Bonzini     dc->desc = "Assign an host PCI device with Xen";
10054f67d30bSMarc-André Lureau     device_class_set_props(dc, xen_pci_passthrough_properties);
100680b4ecc8SPaolo Bonzini };
100780b4ecc8SPaolo Bonzini 
xen_pci_passthrough_finalize(Object * obj)10084e494de6SLan Tianyu static void xen_pci_passthrough_finalize(Object *obj)
10094e494de6SLan Tianyu {
10104e494de6SLan Tianyu     XenPCIPassthroughState *s = XEN_PT_DEVICE(obj);
10114e494de6SLan Tianyu 
10124e494de6SLan Tianyu     xen_pt_msix_delete(s);
10134e494de6SLan Tianyu }
10144e494de6SLan Tianyu 
101580b4ecc8SPaolo Bonzini static const TypeInfo xen_pci_passthrough_info = {
1016f9b9d292SGonglei     .name = TYPE_XEN_PT_DEVICE,
101780b4ecc8SPaolo Bonzini     .parent = TYPE_PCI_DEVICE,
101880b4ecc8SPaolo Bonzini     .instance_size = sizeof(XenPCIPassthroughState),
10194e494de6SLan Tianyu     .instance_finalize = xen_pci_passthrough_finalize,
102080b4ecc8SPaolo Bonzini     .class_init = xen_pci_passthrough_class_init,
10214f67543bSChuck Zmudzinski     .class_size = sizeof(XenPTDeviceClass),
1022d61a363dSYoni Bettan     .instance_init = xen_pci_passthrough_instance_init,
1023fd3b02c8SEduardo Habkost     .interfaces = (InterfaceInfo[]) {
1024fd3b02c8SEduardo Habkost         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
10256d702376SEduardo Habkost         { INTERFACE_PCIE_DEVICE },
1026fd3b02c8SEduardo Habkost         { },
1027fd3b02c8SEduardo Habkost     },
102880b4ecc8SPaolo Bonzini };
102980b4ecc8SPaolo Bonzini 
xen_pci_passthrough_register_types(void)103080b4ecc8SPaolo Bonzini static void xen_pci_passthrough_register_types(void)
103180b4ecc8SPaolo Bonzini {
103280b4ecc8SPaolo Bonzini     type_register_static(&xen_pci_passthrough_info);
103380b4ecc8SPaolo Bonzini }
103480b4ecc8SPaolo Bonzini 
103580b4ecc8SPaolo Bonzini type_init(xen_pci_passthrough_register_types)
1036