diff options
Diffstat (limited to 'arch/x86/kvm/svm/avic.c')
| -rw-r--r-- | arch/x86/kvm/svm/avic.c | 171 | 
1 files changed, 76 insertions, 95 deletions
diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index 54fe03714f8a..d1bc5820ea46 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -291,58 +291,91 @@ void avic_ring_doorbell(struct kvm_vcpu *vcpu)  static int avic_kick_target_vcpus_fast(struct kvm *kvm, struct kvm_lapic *source,  				       u32 icrl, u32 icrh, u32 index)  { -	u32 dest, apic_id; -	struct kvm_vcpu *vcpu; +	u32 l1_physical_id, dest; +	struct kvm_vcpu *target_vcpu;  	int dest_mode = icrl & APIC_DEST_MASK;  	int shorthand = icrl & APIC_SHORT_MASK;  	struct kvm_svm *kvm_svm = to_kvm_svm(kvm); -	u32 *avic_logical_id_table = page_address(kvm_svm->avic_logical_id_table_page);  	if (shorthand != APIC_DEST_NOSHORT)  		return -EINVAL; -	/* -	 * The AVIC incomplete IPI #vmexit info provides index into -	 * the physical APIC ID table, which can be used to derive -	 * guest physical APIC ID. -	 */ +	if (apic_x2apic_mode(source)) +		dest = icrh; +	else +		dest = GET_APIC_DEST_FIELD(icrh); +  	if (dest_mode == APIC_DEST_PHYSICAL) { -		apic_id = index; +		/* broadcast destination, use slow path */ +		if (apic_x2apic_mode(source) && dest == X2APIC_BROADCAST) +			return -EINVAL; +		if (!apic_x2apic_mode(source) && dest == APIC_BROADCAST) +			return -EINVAL; + +		l1_physical_id = dest; + +		if (WARN_ON_ONCE(l1_physical_id != index)) +			return -EINVAL; +  	} else { -		if (!apic_x2apic_mode(source)) { -			/* For xAPIC logical mode, the index is for logical APIC table. */ -			apic_id = avic_logical_id_table[index] & 0x1ff; +		u32 bitmap, cluster; +		int logid_index; + +		if (apic_x2apic_mode(source)) { +			/* 16 bit dest mask, 16 bit cluster id */ +			bitmap = dest & 0xFFFF0000; +			cluster = (dest >> 16) << 4; +		} else if (kvm_lapic_get_reg(source, APIC_DFR) == APIC_DFR_FLAT) { +			/* 8 bit dest mask*/ +			bitmap = dest; +			cluster = 0;  		} else { -			return -EINVAL; +			/* 4 bit desk mask, 4 bit cluster id */ +			bitmap = dest & 0xF; +			cluster = (dest >> 4) << 2;  		} -	} -	/* -	 * Assuming vcpu ID is the same as physical apic ID, -	 * and use it to retrieve the target vCPU. -	 */ -	vcpu = kvm_get_vcpu_by_id(kvm, apic_id); -	if (!vcpu) -		return -EINVAL; +		if (unlikely(!bitmap)) +			/* guest bug: nobody to send the logical interrupt to */ +			return 0; -	if (apic_x2apic_mode(vcpu->arch.apic)) -		dest = icrh; -	else -		dest = GET_APIC_DEST_FIELD(icrh); +		if (!is_power_of_2(bitmap)) +			/* multiple logical destinations, use slow path */ +			return -EINVAL; -	/* -	 * Try matching the destination APIC ID with the vCPU. -	 */ -	if (kvm_apic_match_dest(vcpu, source, shorthand, dest, dest_mode)) { -		vcpu->arch.apic->irr_pending = true; -		svm_complete_interrupt_delivery(vcpu, -						icrl & APIC_MODE_MASK, -						icrl & APIC_INT_LEVELTRIG, -						icrl & APIC_VECTOR_MASK); -		return 0; +		logid_index = cluster + __ffs(bitmap); + +		if (apic_x2apic_mode(source)) { +			l1_physical_id = logid_index; +		} else { +			u32 *avic_logical_id_table = +				page_address(kvm_svm->avic_logical_id_table_page); + +			u32 logid_entry = avic_logical_id_table[logid_index]; + +			if (WARN_ON_ONCE(index != logid_index)) +				return -EINVAL; + +			/* guest bug: non existing/reserved logical destination */ +			if (unlikely(!(logid_entry & AVIC_LOGICAL_ID_ENTRY_VALID_MASK))) +				return 0; + +			l1_physical_id = logid_entry & +					 AVIC_LOGICAL_ID_ENTRY_GUEST_PHYSICAL_ID_MASK; +		}  	} -	return -EINVAL; +	target_vcpu = kvm_get_vcpu_by_id(kvm, l1_physical_id); +	if (unlikely(!target_vcpu)) +		/* guest bug: non existing vCPU is a target of this IPI*/ +		return 0; + +	target_vcpu->arch.apic->irr_pending = true; +	svm_complete_interrupt_delivery(target_vcpu, +					icrl & APIC_MODE_MASK, +					icrl & APIC_INT_LEVELTRIG, +					icrl & APIC_VECTOR_MASK); +	return 0;  }  static void avic_kick_target_vcpus(struct kvm *kvm, struct kvm_lapic *source, @@ -508,35 +541,6 @@ static int avic_handle_ldr_update(struct kvm_vcpu *vcpu)  	return ret;  } -static int avic_handle_apic_id_update(struct kvm_vcpu *vcpu) -{ -	u64 *old, *new; -	struct vcpu_svm *svm = to_svm(vcpu); -	u32 id = kvm_xapic_id(vcpu->arch.apic); - -	if (vcpu->vcpu_id == id) -		return 0; - -	old = avic_get_physical_id_entry(vcpu, vcpu->vcpu_id); -	new = avic_get_physical_id_entry(vcpu, id); -	if (!new || !old) -		return 1; - -	/* We need to move physical_id_entry to new offset */ -	*new = *old; -	*old = 0ULL; -	to_svm(vcpu)->avic_physical_id_cache = new; - -	/* -	 * Also update the guest physical APIC ID in the logical -	 * APIC ID table entry if already setup the LDR. -	 */ -	if (svm->ldr_reg) -		avic_handle_ldr_update(vcpu); - -	return 0; -} -  static void avic_handle_dfr_update(struct kvm_vcpu *vcpu)  {  	struct vcpu_svm *svm = to_svm(vcpu); @@ -555,10 +559,6 @@ static int avic_unaccel_trap_write(struct kvm_vcpu *vcpu)  				AVIC_UNACCEL_ACCESS_OFFSET_MASK;  	switch (offset) { -	case APIC_ID: -		if (avic_handle_apic_id_update(vcpu)) -			return 0; -		break;  	case APIC_LDR:  		if (avic_handle_ldr_update(vcpu))  			return 0; @@ -650,8 +650,6 @@ int avic_init_vcpu(struct vcpu_svm *svm)  void avic_apicv_post_state_restore(struct kvm_vcpu *vcpu)  { -	if (avic_handle_apic_id_update(vcpu) != 0) -		return;  	avic_handle_dfr_update(vcpu);  	avic_handle_ldr_update(vcpu);  } @@ -910,7 +908,9 @@ bool avic_check_apicv_inhibit_reasons(enum kvm_apicv_inhibit reason)  			  BIT(APICV_INHIBIT_REASON_PIT_REINJ) |  			  BIT(APICV_INHIBIT_REASON_X2APIC) |  			  BIT(APICV_INHIBIT_REASON_BLOCKIRQ) | -			  BIT(APICV_INHIBIT_REASON_SEV); +			  BIT(APICV_INHIBIT_REASON_SEV)      | +			  BIT(APICV_INHIBIT_REASON_APIC_ID_MODIFIED) | +			  BIT(APICV_INHIBIT_REASON_APIC_BASE_MODIFIED);  	return supported & BIT(reason);  } @@ -946,7 +946,7 @@ out:  	return ret;  } -void __avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu) +void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu)  {  	u64 entry;  	int h_physical_id = kvm_cpu_get_apicid(cpu); @@ -978,7 +978,7 @@ void __avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu)  	avic_update_iommu_vcpu_affinity(vcpu, h_physical_id, true);  } -void __avic_vcpu_put(struct kvm_vcpu *vcpu) +void avic_vcpu_put(struct kvm_vcpu *vcpu)  {  	u64 entry;  	struct vcpu_svm *svm = to_svm(vcpu); @@ -997,25 +997,6 @@ void __avic_vcpu_put(struct kvm_vcpu *vcpu)  	WRITE_ONCE(*(svm->avic_physical_id_cache), entry);  } -static void avic_vcpu_load(struct kvm_vcpu *vcpu) -{ -	int cpu = get_cpu(); - -	WARN_ON(cpu != vcpu->cpu); - -	__avic_vcpu_load(vcpu, cpu); - -	put_cpu(); -} - -static void avic_vcpu_put(struct kvm_vcpu *vcpu) -{ -	preempt_disable(); - -	__avic_vcpu_put(vcpu); - -	preempt_enable(); -}  void avic_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu)  { @@ -1042,7 +1023,7 @@ void avic_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu)  	vmcb_mark_dirty(vmcb, VMCB_AVIC);  	if (activated) -		avic_vcpu_load(vcpu); +		avic_vcpu_load(vcpu, vcpu->cpu);  	else  		avic_vcpu_put(vcpu); @@ -1075,5 +1056,5 @@ void avic_vcpu_unblocking(struct kvm_vcpu *vcpu)  	if (!kvm_vcpu_apicv_active(vcpu))  		return; -	avic_vcpu_load(vcpu); +	avic_vcpu_load(vcpu, vcpu->cpu);  }  |