|
@@ -1405,6 +1405,107 @@ ret_success:
|
|
|
((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;
|
|
@@ -1574,6 +1675,20 @@ int kvm_hv_hypercall(struct kvm_vcpu *vcpu)
|
|
|
}
|
|
|
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;
|