((u64)rep_cnt << HV_HYPERCALL_REP_COMP_OFFSET);
 }
 
+static u64 kvm_hv_send_ipi(struct kvm_vcpu *current_vcpu, u64 ingpa, u64 outgpa,
+                          bool ex, bool fast)
+{
+       struct kvm *kvm = current_vcpu->kvm;
+       struct kvm_hv *hv = &kvm->arch.hyperv;
+       struct hv_send_ipi_ex send_ipi_ex;
+       struct hv_send_ipi send_ipi;
+       struct kvm_vcpu *vcpu;
+       unsigned long valid_bank_mask;
+       u64 sparse_banks[64];
+       int sparse_banks_len, bank, i, sbank;
+       struct kvm_lapic_irq irq = {.delivery_mode = APIC_DM_FIXED};
+       bool all_cpus;
+
+       if (!ex) {
+               if (!fast) {
+                       if (unlikely(kvm_read_guest(kvm, ingpa, &send_ipi,
+                                                   sizeof(send_ipi))))
+                               return HV_STATUS_INVALID_HYPERCALL_INPUT;
+                       sparse_banks[0] = send_ipi.cpu_mask;
+                       irq.vector = send_ipi.vector;
+               } else {
+                       /* 'reserved' part of hv_send_ipi should be 0 */
+                       if (unlikely(ingpa >> 32 != 0))
+                               return HV_STATUS_INVALID_HYPERCALL_INPUT;
+                       sparse_banks[0] = outgpa;
+                       irq.vector = (u32)ingpa;
+               }
+               all_cpus = false;
+               valid_bank_mask = BIT_ULL(0);
+
+               trace_kvm_hv_send_ipi(irq.vector, sparse_banks[0]);
+       } else {
+               if (unlikely(kvm_read_guest(kvm, ingpa, &send_ipi_ex,
+                                           sizeof(send_ipi_ex))))
+                       return HV_STATUS_INVALID_HYPERCALL_INPUT;
+
+               trace_kvm_hv_send_ipi_ex(send_ipi_ex.vector,
+                                        send_ipi_ex.vp_set.format,
+                                        send_ipi_ex.vp_set.valid_bank_mask);
+
+               irq.vector = send_ipi_ex.vector;
+               valid_bank_mask = send_ipi_ex.vp_set.valid_bank_mask;
+               sparse_banks_len = bitmap_weight(&valid_bank_mask, 64) *
+                       sizeof(sparse_banks[0]);
+
+               all_cpus = send_ipi_ex.vp_set.format == HV_GENERIC_SET_ALL;
+
+               if (!sparse_banks_len)
+                       goto ret_success;
+
+               if (!all_cpus &&
+                   kvm_read_guest(kvm,
+                                  ingpa + offsetof(struct hv_send_ipi_ex,
+                                                   vp_set.bank_contents),
+                                  sparse_banks,
+                                  sparse_banks_len))
+                       return HV_STATUS_INVALID_HYPERCALL_INPUT;
+       }
+
+       if ((irq.vector < HV_IPI_LOW_VECTOR) ||
+           (irq.vector > HV_IPI_HIGH_VECTOR))
+               return HV_STATUS_INVALID_HYPERCALL_INPUT;
+
+       if (all_cpus || atomic_read(&hv->num_mismatched_vp_indexes)) {
+               kvm_for_each_vcpu(i, vcpu, kvm) {
+                       if (all_cpus || hv_vcpu_in_sparse_set(
+                                   &vcpu->arch.hyperv, sparse_banks,
+                                   valid_bank_mask)) {
+                               /* We fail only when APIC is disabled */
+                               kvm_apic_set_irq(vcpu, &irq, NULL);
+                       }
+               }
+               goto ret_success;
+       }
+
+       /*
+        * num_mismatched_vp_indexes is zero so every vcpu has
+        * vp_index == vcpu_idx.
+        */
+       sbank = 0;
+       for_each_set_bit(bank, (unsigned long *)&valid_bank_mask, 64) {
+               for_each_set_bit(i, (unsigned long *)&sparse_banks[sbank], 64) {
+                       u32 vp_index = bank * 64 + i;
+                       struct kvm_vcpu *vcpu =
+                               get_vcpu_by_vpidx(kvm, vp_index);
+
+                       /* Unknown vCPU specified */
+                       if (!vcpu)
+                               continue;
+
+                       /* We fail only when APIC is disabled */
+                       kvm_apic_set_irq(vcpu, &irq, NULL);
+               }
+               sbank++;
+       }
+
+ret_success:
+       return HV_STATUS_SUCCESS;
+}
+
 bool kvm_hv_hypercall_enabled(struct kvm *kvm)
 {
        return READ_ONCE(kvm->arch.hyperv.hv_hypercall) & HV_X64_MSR_HYPERCALL_ENABLE;
                }
                ret = kvm_hv_flush_tlb(vcpu, ingpa, rep_cnt, true);
                break;
+       case HVCALL_SEND_IPI:
+               if (unlikely(rep)) {
+                       ret = HV_STATUS_INVALID_HYPERCALL_INPUT;
+                       break;
+               }
+               ret = kvm_hv_send_ipi(vcpu, ingpa, outgpa, false, fast);
+               break;
+       case HVCALL_SEND_IPI_EX:
+               if (unlikely(fast || rep)) {
+                       ret = HV_STATUS_INVALID_HYPERCALL_INPUT;
+                       break;
+               }
+               ret = kvm_hv_send_ipi(vcpu, ingpa, outgpa, true, false);
+               break;
        default:
                ret = HV_STATUS_INVALID_HYPERCALL_CODE;
                break;
 
                  __entry->valid_bank_mask, __entry->format,
                  __entry->address_space, __entry->flags)
 );
+
+/*
+ * Tracepoints for kvm_hv_send_ipi.
+ */
+TRACE_EVENT(kvm_hv_send_ipi,
+       TP_PROTO(u32 vector, u64 processor_mask),
+       TP_ARGS(vector, processor_mask),
+
+       TP_STRUCT__entry(
+               __field(u32, vector)
+               __field(u64, processor_mask)
+       ),
+
+       TP_fast_assign(
+               __entry->vector = vector;
+               __entry->processor_mask = processor_mask;
+       ),
+
+       TP_printk("vector %x processor_mask 0x%llx",
+                 __entry->vector, __entry->processor_mask)
+);
+
+TRACE_EVENT(kvm_hv_send_ipi_ex,
+       TP_PROTO(u32 vector, u64 format, u64 valid_bank_mask),
+       TP_ARGS(vector, format, valid_bank_mask),
+
+       TP_STRUCT__entry(
+               __field(u32, vector)
+               __field(u64, format)
+               __field(u64, valid_bank_mask)
+       ),
+
+       TP_fast_assign(
+               __entry->vector = vector;
+               __entry->format = format;
+               __entry->valid_bank_mask = valid_bank_mask;
+       ),
+
+       TP_printk("vector %x format %llx valid_bank_mask 0x%llx",
+                 __entry->vector, __entry->format,
+                 __entry->valid_bank_mask)
+);
 #endif /* _TRACE_KVM_H */
 
 #undef TRACE_INCLUDE_PATH