stable.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 4/7] KVM: x86: SVM: fix avic_kick_target_vcpus_fast
       [not found] <20220606180829.102503-1-mlevitsk@redhat.com>
@ 2022-06-06 18:08 ` Maxim Levitsky
  2022-06-08 13:21   ` Paolo Bonzini
  0 siblings, 1 reply; 3+ messages in thread
From: Maxim Levitsky @ 2022-06-06 18:08 UTC (permalink / raw)
  To: kvm
  Cc: Wanpeng Li, Vitaly Kuznetsov, Sean Christopherson, Jim Mattson,
	H. Peter Anvin, Joerg Roedel, Dave Hansen, Ingo Molnar,
	Suravee Suthikulpanit, linux-kernel, Maxim Levitsky,
	Thomas Gleixner, x86, Borislav Petkov, Paolo Bonzini, stable

There are two issues in avic_kick_target_vcpus_fast

1. It is legal to issue an IPI request with APIC_DEST_NOSHORT
   and a physical destination of 0xFF (or 0xFFFFFFFF in case of x2apic),
   which must be treated as a broadcast destination.

   Fix this by explicitly checking for it.
   Also don’t use ‘index’ in this case as it gives no new information.

2. It is legal to issue a logical IPI request to more than one target.
   Index field only provides index in physical id table of first
   such target and therefore can't be used before we are sure
   that only a single target was addressed.

   Instead, parse the ICRL/ICRH, double check that a unicast interrupt
   was requested, and use that info to figure out the physical id
   of the target vCPU.
   At that point there is no need to use the index field as well.


In addition to fixing the above	issues,	also skip the call to
kvm_apic_match_dest.

It is possible to do this now, because now as long as AVIC is not
inhibited, it is guaranteed that none of the vCPUs changed their
apic id from its default value.


This fixes boot of windows guest with AVIC enabled because it uses
IPI with 0xFF destination and no destination shorthand.

Fixes: 7223fd2d5338 ("KVM: SVM: Use target APIC ID to complete AVIC IRQs when possible")
Cc: stable@vger.kernel.org

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 arch/x86/kvm/svm/avic.c | 105 ++++++++++++++++++++++++++--------------
 1 file changed, 69 insertions(+), 36 deletions(-)

diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index 072e2c8cc66aa..5d98ac575dedc 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,
-- 
2.26.3


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH 4/7] KVM: x86: SVM: fix avic_kick_target_vcpus_fast
  2022-06-06 18:08 ` [PATCH 4/7] KVM: x86: SVM: fix avic_kick_target_vcpus_fast Maxim Levitsky
@ 2022-06-08 13:21   ` Paolo Bonzini
  2022-06-09  8:13     ` Maxim Levitsky
  0 siblings, 1 reply; 3+ messages in thread
From: Paolo Bonzini @ 2022-06-08 13:21 UTC (permalink / raw)
  To: Maxim Levitsky, kvm
  Cc: Wanpeng Li, Vitaly Kuznetsov, Sean Christopherson, Jim Mattson,
	H. Peter Anvin, Joerg Roedel, Dave Hansen, Ingo Molnar,
	Suravee Suthikulpanit, linux-kernel, Thomas Gleixner, x86,
	Borislav Petkov, stable

On 6/6/22 20:08, Maxim Levitsky wrote:
> There are two issues in avic_kick_target_vcpus_fast
> 
> 1. It is legal to issue an IPI request with APIC_DEST_NOSHORT
>     and a physical destination of 0xFF (or 0xFFFFFFFF in case of x2apic),
>     which must be treated as a broadcast destination.
> 
>     Fix this by explicitly checking for it.
>     Also don’t use ‘index’ in this case as it gives no new information.
> 
> 2. It is legal to issue a logical IPI request to more than one target.
>     Index field only provides index in physical id table of first
>     such target and therefore can't be used before we are sure
>     that only a single target was addressed.
> 
>     Instead, parse the ICRL/ICRH, double check that a unicast interrupt
>     was requested, and use that info to figure out the physical id
>     of the target vCPU.
>     At that point there is no need to use the index field as well.
> 
> 
> In addition to fixing the above	issues,	also skip the call to
> kvm_apic_match_dest.
> 
> It is possible to do this now, because now as long as AVIC is not
> inhibited, it is guaranteed that none of the vCPUs changed their
> apic id from its default value.
> 
> 
> This fixes boot of windows guest with AVIC enabled because it uses
> IPI with 0xFF destination and no destination shorthand.
> 
> Fixes: 7223fd2d5338 ("KVM: SVM: Use target APIC ID to complete AVIC IRQs when possible")
> Cc: stable@vger.kernel.org
> 
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>

Is it possible to use kvm_intr_is_single_vcpu_fast, or am I missing 
something?

Series queued, thanks.

Paolo


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH 4/7] KVM: x86: SVM: fix avic_kick_target_vcpus_fast
  2022-06-08 13:21   ` Paolo Bonzini
@ 2022-06-09  8:13     ` Maxim Levitsky
  0 siblings, 0 replies; 3+ messages in thread
From: Maxim Levitsky @ 2022-06-09  8:13 UTC (permalink / raw)
  To: Paolo Bonzini, kvm
  Cc: Wanpeng Li, Vitaly Kuznetsov, Sean Christopherson, Jim Mattson,
	H. Peter Anvin, Joerg Roedel, Dave Hansen, Ingo Molnar,
	Suravee Suthikulpanit, linux-kernel, Thomas Gleixner, x86,
	Borislav Petkov, stable

On Wed, 2022-06-08 at 15:21 +0200, Paolo Bonzini wrote:
> On 6/6/22 20:08, Maxim Levitsky wrote:
> > There are two issues in avic_kick_target_vcpus_fast
> > 
> > 1. It is legal to issue an IPI request with APIC_DEST_NOSHORT
> >     and a physical destination of 0xFF (or 0xFFFFFFFF in case of x2apic),
> >     which must be treated as a broadcast destination.
> > 
> >     Fix this by explicitly checking for it.
> >     Also don’t use ‘index’ in this case as it gives no new information.
> > 
> > 2. It is legal to issue a logical IPI request to more than one target.
> >     Index field only provides index in physical id table of first
> >     such target and therefore can't be used before we are sure
> >     that only a single target was addressed.
> > 
> >     Instead, parse the ICRL/ICRH, double check that a unicast interrupt
> >     was requested, and use that info to figure out the physical id
> >     of the target vCPU.
> >     At that point there is no need to use the index field as well.
> > 
> > 
> > In addition to fixing the above	issues,	also skip the call to
> > kvm_apic_match_dest.
> > 
> > It is possible to do this now, because now as long as AVIC is not
> > inhibited, it is guaranteed that none of the vCPUs changed their
> > apic id from its default value.
> > 
> > 
> > This fixes boot of windows guest with AVIC enabled because it uses
> > IPI with 0xFF destination and no destination shorthand.
> > 
> > Fixes: 7223fd2d5338 ("KVM: SVM: Use target APIC ID to complete AVIC IRQs when possible")
> > Cc: stable@vger.kernel.org
> > 
> > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> 
> Is it possible to use kvm_intr_is_single_vcpu_fast, or am I missing 
> something?

Yes, except that it needs 'struct kvm_lapic_irq' which we won't have when
we emulate guest<->guest interrupts, and also it goes over apic map and such,
which can be be skipped.

It also does more unneeded things like dealing with low priority mode for example,
which thankfully AVIC doenst' support and if attempted will still VM exit
with 'incomplete IPI' but with AVIC_IPI_FAILURE_INVALID_INT_TYPE subreason,
which goes through full APIC register emulation.

I do think about the fact that ICRL/H parsing in the case of logical ID,
(which depends on cluser mode and x2apic mode) can be moved to some common
code, but I wasn't able yet to find a clean way to do it.

BTW: there is another case where AVIC must be inhibited: in xapic mode,
logical ids, don't have to have a single bit set in the mask area of the logical id, 
(low 4 bits in cluster mode and all 8 bits in flat mode)
and neither there is a guarnantee that multilple CPUs don't share these bits.

AVIC however has a logical ID table which maps each (bit x cluster value) to a physical id,
and therefore a single vCPU, so tha later is not possible to support with AVIC.

I haven't studied the code that is responsible for this, I will do this soon.


Thankfully IPIv only supports physical IPI mode (this is what I heard, don't know for sure).

I also will write a unit test for this very soon, to test various logical id
IPIs, messing with logical id registers, etc, etc.

Best regards,
	Maxim Levitsky


> 
> Series queued, thanks.
> 
> Paolo
> 



^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2022-06-09  8:13 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20220606180829.102503-1-mlevitsk@redhat.com>
2022-06-06 18:08 ` [PATCH 4/7] KVM: x86: SVM: fix avic_kick_target_vcpus_fast Maxim Levitsky
2022-06-08 13:21   ` Paolo Bonzini
2022-06-09  8:13     ` Maxim Levitsky

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).