diff --git a/xen/arch/x86/msi.c b/xen/arch/x86/msi.c
index d630600..ecea91a 100644
--- a/xen/arch/x86/msi.c
+++ b/xen/arch/x86/msi.c
@@ -1443,6 +1443,51 @@ int pci_restore_msi_state(struct pci_dev *pdev)
     return 0;
 }
 
+int msi_control(struct pci_dev *pdev, bool msix, bool enable)
+{
+    unsigned int cap = msix ? PCI_CAP_ID_MSIX : PCI_CAP_ID_MSI;
+    unsigned int other_cap = msix ? PCI_CAP_ID_MSI : PCI_CAP_ID_MSIX;
+    uint16_t cmd;
+
+    if ( !use_msi )
+        return -EOPNOTSUPP;
+
+    if ( !pci_find_cap_offset(pdev->seg,
+                              pdev->bus,
+                              PCI_SLOT(pdev->devfn),
+                              PCI_FUNC(pdev->devfn),
+                              cap) )
+        return -ENODEV;
+
+    cmd = pci_conf_read16(pdev->sbdf, PCI_COMMAND);
+
+    /* don't allow enabling MSI(-X) and INTx at the same time */
+    if ( enable && ! (cmd & PCI_COMMAND_INTX_DISABLE) )
+        return -EBUSY;
+
+    /* don't allow enabling both MSI and MSI-X at the same time */
+    if ( enable && find_msi_entry(pdev, -1, other_cap) )
+        return -EBUSY;
+
+    if ( msix )
+        msix_set_enable(pdev, enable);
+    else
+        msi_set_enable(pdev, enable);
+
+    return 0;
+}
+
+int intx_control(struct pci_dev *pdev, bool enable)
+{
+    /* don't allow enabling INTx if MSI(-X) is already enabled */
+    if ( enable && find_msi_entry(pdev, -1, PCI_CAP_ID_MSI) )
+        return -EBUSY;
+    if ( enable && find_msi_entry(pdev, -1, PCI_CAP_ID_MSIX) )
+        return -EBUSY;
+    pci_intx(pdev, enable);
+    return 0;
+}
+
 void __init early_msi_init(void)
 {
     if ( use_msi < 0 )
diff --git a/xen/arch/x86/physdev.c b/xen/arch/x86/physdev.c
index 3a3c158..7b71039 100644
--- a/xen/arch/x86/physdev.c
+++ b/xen/arch/x86/physdev.c
@@ -662,6 +662,59 @@ ret_t do_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
         break;
     }
 
+    case PHYSDEVOP_interrupt_control: {
+        struct physdev_interrupt_control op;
+        struct pci_dev *pdev;
+        int intr_type;
+        bool enable;
+
+        ret = -EFAULT;
+        if ( copy_from_guest(&op, arg, 1) )
+            break;
+
+        ret = -EINVAL;
+        if ( op.flags & ~(PHYSDEVOP_INTERRUPT_CONTROL_TYPE_MASK |
+                          PHYSDEVOP_INTERRUPT_CONTROL_ENABLE) )
+            break;
+
+        intr_type = op.flags & PHYSDEVOP_INTERRUPT_CONTROL_TYPE_MASK;
+        enable = op.flags & PHYSDEVOP_INTERRUPT_CONTROL_ENABLE;
+
+        pcidevs_lock();
+        pdev = pci_get_pdev(op.seg, op.bus, op.devfn);
+        ret = -ENODEV;
+        /* explicitly exclude hidden devices */
+        if ( !pdev || pdev->domain == dom_xen )
+            goto pci_unlock;
+
+        ret = xsm_interrupt_control(XSM_DM_PRIV,
+                                    pdev->domain,
+                                    pdev->sbdf.sbdf,
+                                    intr_type,
+                                    enable);
+        if ( ret )
+            goto pci_unlock;
+
+        switch ( intr_type )
+        {
+            case PHYSDEVOP_INTERRUPT_CONTROL_INTX:
+                ret = intx_control(pdev, enable);
+                break;
+            case PHYSDEVOP_INTERRUPT_CONTROL_MSI:
+                ret = msi_control(pdev, false, enable);
+                break;
+            case PHYSDEVOP_INTERRUPT_CONTROL_MSIX:
+                ret = msi_control(pdev, true, enable);
+                break;
+            default:
+                ret = -EINVAL;
+                break;
+        }
+pci_unlock:
+        pcidevs_unlock();
+        break;
+    }
+
     default:
         ret = -ENOSYS;
         break;
diff --git a/xen/arch/x86/x86_64/physdev.c b/xen/arch/x86/x86_64/physdev.c
index c5a00ea..6e0e488 100644
--- a/xen/arch/x86/x86_64/physdev.c
+++ b/xen/arch/x86/x86_64/physdev.c
@@ -76,6 +76,10 @@ CHECK_physdev_pci_device_add
 CHECK_physdev_pci_device
 #undef xen_physdev_pci_device
 
+#define xen_physdev_interrupt_control physdev_interrupt_control
+CHECK_physdev_interrupt_control
+#undef xen_physdev_interrupt_control
+
 #define COMPAT
 #undef guest_handle_okay
 #define guest_handle_okay          compat_handle_okay
diff --git a/xen/include/asm-x86/msi.h b/xen/include/asm-x86/msi.h
index 10387dc..4c13e6b 100644
--- a/xen/include/asm-x86/msi.h
+++ b/xen/include/asm-x86/msi.h
@@ -252,5 +252,7 @@ void guest_mask_msi_irq(struct irq_desc *, bool mask);
 void ack_nonmaskable_msi_irq(struct irq_desc *);
 void end_nonmaskable_msi_irq(struct irq_desc *, u8 vector);
 void set_msi_affinity(struct irq_desc *, const cpumask_t *);
+int msi_control(struct pci_dev *pdev, bool msix, bool enable);
+int intx_control(struct pci_dev *pdev, bool enable);
 
 #endif /* __ASM_MSI_H */
diff --git a/xen/include/public/physdev.h b/xen/include/public/physdev.h
index b6faf83..689c11e 100644
--- a/xen/include/public/physdev.h
+++ b/xen/include/public/physdev.h
@@ -345,6 +345,29 @@ typedef struct physdev_dbgp_op physdev_dbgp_op_t;
 DEFINE_XEN_GUEST_HANDLE(physdev_dbgp_op_t);
 
 /*
+ * Choose which interrupt type to control. If neither MSI nor MSI-X is chosen,
+ * will apply to INTx - for convenience define PHYSDEVOP_INTERRUPT_CONTROL_INTX
+ * and PHYSDEVOP_INTERRUPT_CONTROL_TYPE_MASK
+ */
+#define PHYSDEVOP_INTERRUPT_CONTROL_TYPE_MASK 3
+#define PHYSDEVOP_INTERRUPT_CONTROL_INTX      0
+#define PHYSDEVOP_INTERRUPT_CONTROL_MSI       1
+#define PHYSDEVOP_INTERRUPT_CONTROL_MSIX      2
+/* when PHYSDEVOP_INTERRUPT_CONTROL_ENABLE not set, disable */
+#define PHYSDEVOP_INTERRUPT_CONTROL_ENABLE    4
+
+#define PHYSDEVOP_interrupt_control   32
+struct physdev_interrupt_control {
+    /* IN */
+    uint16_t seg;
+    uint8_t bus;
+    uint8_t devfn;
+    uint16_t flags;
+};
+typedef struct physdev_interrupt_control physdev_interrupt_control_t;
+DEFINE_XEN_GUEST_HANDLE(physdev_interrupt_control_t);
+
+/*
  * Notify that some PIRQ-bound event channels have been unmasked.
  * ** This command is obsolete since interface version 0x00030202 and is **
  * ** unsupported by newer versions of Xen.                              **
diff --git a/xen/include/xlat.lst b/xen/include/xlat.lst
index 95f5e55..18af663 100644
--- a/xen/include/xlat.lst
+++ b/xen/include/xlat.lst
@@ -104,6 +104,7 @@
 !	vnuma_topology_info		memory.h
 ?	physdev_eoi			physdev.h
 ?	physdev_get_free_pirq		physdev.h
+?	physdev_interrupt_control	physdev.h
 ?	physdev_irq			physdev.h
 ?	physdev_irq_status_query	physdev.h
 ?	physdev_manage_pci		physdev.h
diff --git a/xen/include/xsm/dummy.h b/xen/include/xsm/dummy.h
index ef52bb1..5a758c5 100644
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -514,6 +514,13 @@ static XSM_INLINE int xsm_pci_config_permission(XSM_DEFAULT_ARG struct domain *d
     return xsm_default_action(action, current->domain, d);
 }
 
+static XSM_INLINE int xsm_interrupt_control(XSM_DEFAULT_ARG struct domain *d, uint32_t machine_bdf,
+                                            uint8_t intr_type, uint8_t enable)
+{
+    XSM_ASSERT_ACTION(XSM_DM_PRIV);
+    return xsm_default_action(action, current->domain, d);
+}
+
 static XSM_INLINE int xsm_add_to_physmap(XSM_DEFAULT_ARG struct domain *d1, struct domain *d2)
 {
     XSM_ASSERT_ACTION(XSM_TARGET);
diff --git a/xen/include/xsm/xsm.h b/xen/include/xsm/xsm.h
index e22d616..f080189 100644
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -106,6 +106,7 @@ struct xsm_operations {
     int (*iomem_permission) (struct domain *d, uint64_t s, uint64_t e, uint8_t allow);
     int (*iomem_mapping) (struct domain *d, uint64_t s, uint64_t e, uint8_t allow);
     int (*pci_config_permission) (struct domain *d, uint32_t machine_bdf, uint16_t start, uint16_t end, uint8_t access);
+    int (*interrupt_control) (struct domain *d, uint32_t machine_bdf, uint8_t intr_type, uint8_t enable);
 
 #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI)
     int (*get_device_group) (uint32_t machine_bdf);
@@ -464,6 +465,11 @@ static inline int xsm_pci_config_permission (xsm_default_t def, struct domain *d
     return xsm_ops->pci_config_permission(d, machine_bdf, start, end, access);
 }
 
+static inline int xsm_interrupt_control (xsm_default_t def, struct domain *d, uint32_t machine_bdf, uint8_t msix, uint8_t enable)
+{
+    return xsm_ops->interrupt_control(d, machine_bdf, msix, enable);
+}
+
 #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI)
 static inline int xsm_get_device_group(xsm_default_t def, uint32_t machine_bdf)
 {
diff --git a/xen/xsm/dummy.c b/xen/xsm/dummy.c
index 5705e52..3080ae7 100644
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -81,6 +81,7 @@ void __init xsm_fixup_ops (struct xsm_operations *ops)
     set_to_dummy_if_null(ops, iomem_permission);
     set_to_dummy_if_null(ops, iomem_mapping);
     set_to_dummy_if_null(ops, pci_config_permission);
+    set_to_dummy_if_null(ops, interrupt_control);
     set_to_dummy_if_null(ops, get_vnumainfo);
 
 #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI)
diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c
index 791c1f6..ee2fc52 100644
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -1083,6 +1083,29 @@ static int flask_pci_config_permission(struct domain *d, uint32_t machine_bdf, u
 
 }
 
+static int flask_interrupt_control(struct domain *d, uint32_t machine_bdf, uint8_t type, uint8_t enable)
+{
+    uint32_t dsid, rsid;
+    int rc = -EPERM;
+    struct avc_audit_data ad;
+    uint32_t perm;
+
+    AVC_AUDIT_DATA_INIT(&ad, DEV);
+    ad.device = machine_bdf;
+
+    rc = security_device_sid(machine_bdf, &rsid);
+    if ( rc )
+        return rc;
+
+    rc = avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__SETUP, &ad);
+    if ( rc )
+        return rc;
+
+    perm = flask_iommu_resource_use_perm();
+    dsid = domain_sid(d);
+    return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, perm, &ad);
+}
+
 static int flask_resource_plug_core(void)
 {
     return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__PLUG, NULL);
@@ -1800,6 +1823,7 @@ static struct xsm_operations flask_ops = {
     .iomem_permission = flask_iomem_permission,
     .iomem_mapping = flask_iomem_mapping,
     .pci_config_permission = flask_pci_config_permission,
+    .interrupt_control = flask_interrupt_control,
 
     .resource_plug_core = flask_resource_plug_core,
     .resource_unplug_core = flask_resource_unplug_core,
diff --git a/xen/xsm/flask/policy/access_vectors b/xen/xsm/flask/policy/access_vectors
index 194d743..82eaeac 100644
--- a/xen/xsm/flask/policy/access_vectors
+++ b/xen/xsm/flask/policy/access_vectors
@@ -466,6 +466,7 @@ class resource
 # checked for PHYSDEVOP_restore_msi* (target PCI device)
 # checked for PHYSDEVOP_setup_gsi (target IRQ)
 # checked for PHYSDEVOP_pci_mmcfg_reserved (target xen_t)
+# checked for PHYSDEVOP_interrupt_control (target PCI device)
     setup
 }
 
