diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
index 355c654693..c7d70e618b 100644
--- a/docs/man/xl.cfg.5.pod.in
+++ b/docs/man/xl.cfg.5.pod.in
@@ -2175,6 +2175,12 @@ ticks and hence enabling this group will ensure that ticks will be
 consistent with use of an enlightened time source (B<time_ref_count> or
 B<reference_tsc>).
 
+=item B<hcall_ipi>
+
+This set incorporates use of a hypercall for interprocessor interrupts.
+This enlightenment may improve performance of Windows guests with multiple
+virtual CPUs.
+
 =item B<defaults>
 
 This is a special value that enables the default set of groups, which
diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index c8f219b0d3..482499a6c0 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -330,6 +330,12 @@
  */
 #define LIBXL_HAVE_VIRIDIAN_STIMER 1
 
+/*
+ * LIBXL_HAVE_VIRIDIAN_HCALL_IPI indicates that the 'hcall_ipi' value
+ * is present in the viridian enlightenment enumeration.
+ */
+#define LIBXL_HAVE_VIRIDIAN_HCALL_IPI 1
+
 /*
  * LIBXL_HAVE_BUILDINFO_HVM_ACPI_LAPTOP_SLATE indicates that
  * libxl_domain_build_info has the u.hvm.acpi_laptop_slate field.
diff --git a/tools/libxl/libxl_dom.c b/tools/libxl/libxl_dom.c
index 2ee0f82ee7..879c806139 100644
--- a/tools/libxl/libxl_dom.c
+++ b/tools/libxl/libxl_dom.c
@@ -324,6 +324,9 @@ static int hvm_set_viridian_features(libxl__gc *gc, uint32_t domid,
     if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_STIMER))
         mask |= HVMPV_time_ref_count | HVMPV_synic | HVMPV_stimer;
 
+    if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_HCALL_IPI))
+        mask |= HVMPV_hcall_ipi;
+
     if (mask != 0 &&
         xc_hvm_param_set(CTX->xch,
                          domid,
diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl
index 1cce249de4..cb4702fd7a 100644
--- a/tools/libxl/libxl_types.idl
+++ b/tools/libxl/libxl_types.idl
@@ -237,6 +237,7 @@ libxl_viridian_enlightenment = Enumeration("viridian_enlightenment", [
     (6, "crash_ctl"),
     (7, "synic"),
     (8, "stimer"),
+    (9, "hcall_ipi"),
     ])
 
 libxl_hdtype = Enumeration("hdtype", [
diff --git a/xen/arch/x86/hvm/viridian/viridian.c b/xen/arch/x86/hvm/viridian/viridian.c
index dce648bb4e..4b06b78a27 100644
--- a/xen/arch/x86/hvm/viridian/viridian.c
+++ b/xen/arch/x86/hvm/viridian/viridian.c
@@ -28,6 +28,7 @@
 #define HvFlushVirtualAddressSpace 0x0002
 #define HvFlushVirtualAddressList  0x0003
 #define HvNotifyLongSpinWait       0x0008
+#define HvSendSyntheticClusterIpi  0x000b
 #define HvGetPartitionId           0x0046
 #define HvExtCallQueryCapabilities 0x8001
 
@@ -95,6 +96,7 @@ typedef union _HV_CRASH_CTL_REG_CONTENTS
 #define CPUID4A_HCALL_REMOTE_TLB_FLUSH (1 << 2)
 #define CPUID4A_MSR_BASED_APIC         (1 << 3)
 #define CPUID4A_RELAX_TIMER_INT        (1 << 5)
+#define CPUID4A_SYNTHETIC_CLUSTER_IPI  (1 << 10)
 
 /* Viridian CPUID leaf 6: Implementation HW features detected and in use */
 #define CPUID6A_APIC_OVERLAY    (1 << 0)
@@ -206,6 +208,8 @@ void cpuid_viridian_leaves(const struct vcpu *v, uint32_t leaf,
             res->a |= CPUID4A_HCALL_REMOTE_TLB_FLUSH;
         if ( !cpu_has_vmx_apic_reg_virt )
             res->a |= CPUID4A_MSR_BASED_APIC;
+        if ( viridian_feature_mask(d) & HVMPV_hcall_ipi )
+            res->a |= CPUID4A_SYNTHETIC_CLUSTER_IPI;
 
         /*
          * This value is the recommended number of attempts to try to
@@ -628,6 +632,65 @@ int viridian_hypercall(struct cpu_user_regs *regs)
         break;
     }
 
+    case HvSendSyntheticClusterIpi:
+    {
+        struct vcpu *v;
+        uint32_t vector;
+        uint64_t vcpu_mask;
+
+        status = HV_STATUS_INVALID_PARAMETER;
+
+        /* Get input parameters. */
+        if ( input.fast )
+        {
+            if ( input_params_gpa >> 32 )
+                break;
+
+            vector = input_params_gpa;
+            vcpu_mask = output_params_gpa;
+        }
+        else
+        {
+            struct {
+                uint32_t vector;
+                uint8_t target_vtl;
+                uint8_t reserved_zero[3];
+                uint64_t vcpu_mask;
+            } input_params;
+
+            if ( hvm_copy_from_guest_phys(&input_params, input_params_gpa,
+                                          sizeof(input_params)) !=
+                 HVMTRANS_okay )
+                break;
+
+            if ( input_params.target_vtl ||
+                 input_params.reserved_zero[0] ||
+                 input_params.reserved_zero[1] ||
+                 input_params.reserved_zero[2] )
+                break;
+
+            vector = input_params.vector;
+            vcpu_mask = input_params.vcpu_mask;
+        }
+
+        if ( vector < 0x10 || vector > 0xff )
+            break;
+
+        for_each_vcpu ( currd, v )
+        {
+            if ( v->vcpu_id >= (sizeof(vcpu_mask) * 8) )
+                break;
+
+            if ( !(vcpu_mask & (1ul << v->vcpu_id)) )
+                continue;
+
+            vlapic_set_irq(vcpu_vlapic(v), vector, 0);
+        }
+
+        status = HV_STATUS_SUCCESS;
+        break;
+    }
+
     default:
         gprintk(XENLOG_WARNING, "unimplemented hypercall %04x\n",
                 input.call_code);
diff --git a/xen/include/public/hvm/params.h b/xen/include/public/hvm/params.h
index e06b0942d0..36832e4b94 100644
--- a/xen/include/public/hvm/params.h
+++ b/xen/include/public/hvm/params.h
@@ -154,6 +154,10 @@
 #define _HVMPV_stimer 8
 #define HVMPV_stimer (1 << _HVMPV_stimer)
 
+/* Use Synthetic Cluster IPI Hypercall */
+#define _HVMPV_hcall_ipi 9
+#define HVMPV_hcall_ipi (1 << _HVMPV_hcall_ipi)
+
 #define HVMPV_feature_mask \
         (HVMPV_base_freq | \
          HVMPV_no_freq | \
@@ -163,7 +167,8 @@
          HVMPV_apic_assist | \
          HVMPV_crash_ctl | \
          HVMPV_synic | \
-         HVMPV_stimer)
+         HVMPV_stimer | \
+         HVMPV_hcall_ipi)
 
 #endif
 
