From nobody Fri May 3 10:54:04 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org ARC-Seal: i=1; a=rsa-sha256; t=1613829917; cv=none; d=zohomail.com; s=zohoarc; b=k6wXA9p7NTirhLHRJtrxcq4vwL7spuRducpUtnIQrsVdKAV38ZqtJPjWHboh7Z4Mo5IOPG0A6IsamrQSzL8fMWSF5fB2f9RPuc9CQwjs6kSHLrq4GrN4w30vKTL6kEAEK1cdskt7mXvBtw5QjKqcXh5L0rCBQDhOD4Iw8tepwLI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1613829917; h=Cc:Date:From:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:Message-ID:Sender:Subject:To; bh=DHsWNJWyNVapDyEP7btR9oRnZl4HrHvqZ66xkvNY/So=; b=Q0meR+wbYXwsoq+7EsHpgd/xcKc/6gdc5A0ZbdFsu5Q2V9e80KZTiZnFj2eNWWwH6ZrfoZW0mZlLfdhq0Z9gsrQlY3ppNSYGgx7hhxomW6246ZMLfd3/zCsDXxhiwXT8pEZkEdEdEqPAVTNm05C1ShjFf+dbziB5yB6eVRetrZg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1613829916787353.5615684528773; Sat, 20 Feb 2021 06:05:16 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.87232.164350 (Exim 4.92) (envelope-from ) id 1lDSrr-0001be-It; Sat, 20 Feb 2021 14:04:19 +0000 Received: by outflank-mailman (output) from mailman id 87232.164350; Sat, 20 Feb 2021 14:04:19 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1lDSrr-0001bX-Fp; Sat, 20 Feb 2021 14:04:19 +0000 Received: by outflank-mailman (input) for mailman id 87232; Sat, 20 Feb 2021 14:04:17 +0000 Received: from mail.xenproject.org ([104.130.215.37]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1lDSrp-0001bS-KQ for xen-devel@lists.xenproject.org; Sat, 20 Feb 2021 14:04:17 +0000 Received: from xenbits.xenproject.org ([104.239.192.120]) by mail.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1lDSrn-00041Q-Uw; Sat, 20 Feb 2021 14:04:15 +0000 Received: from 54-240-197-235.amazon.com ([54.240.197.235] helo=ufe34d9ed68d054.ant.amazon.com) by xenbits.xenproject.org with esmtpsa (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lDSrn-0007Yh-LC; Sat, 20 Feb 2021 14:04:15 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=xen.org; s=20200302mail; h=Message-Id:Date:Subject:Cc:To:From; bh=DHsWNJWyNVapDyEP7btR9oRnZl4HrHvqZ66xkvNY/So=; b=VP2ziViRgYOdBZv4XsM2EVyrth T+DJJNVItILG+CPonIja75oSyqltE4waI00KAbS1rbR4i9tio8+48pT+A51vBafJ62bdCyqRe3brW qpvuIsbv1GqHlEQGsw1DFxD1acF4ePqg7i5HdMdRJwkK+NXD2/eTPb5kerLRdA/vAfko=; From: Julien Grall To: xen-devel@lists.xenproject.org Cc: bertrand.marquis@arm.com, iwj@xenproject.org, Julien Grall , Stefano Stabellini , Julien Grall , Volodymyr Babchuk Subject: [PATCH for-4.15] xen/vgic: Implement write to ISPENDR in vGICv{2, 3} Date: Sat, 20 Feb 2021 14:04:12 +0000 Message-Id: <20210220140412.31610-1-julien@xen.org> X-Mailer: git-send-email 2.17.1 X-ZohoMail-DKIM: pass (identity @xen.org) Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Julien Grall Currently, Xen will send a data abort to a guest trying to write to the ISPENDR. Unfortunately, recent version of Linux (at least 5.9+) will start writing to the register if the interrupt needs to be re-triggered (see the callback irq_retrigger). This can happen when a driver (such as the xgbe network driver on AMD Seattle) re-enable an interrupt: (XEN) d0v0: vGICD: unhandled word write 0x00000004000000 to ISPENDR44 [...] [ 25.635837] Unhandled fault at 0xffff80001000522c [...] [ 25.818716] gic_retrigger+0x2c/0x38 [ 25.822361] irq_startup+0x78/0x138 [ 25.825920] __enable_irq+0x70/0x80 [ 25.829478] enable_irq+0x50/0xa0 [ 25.832864] xgbe_one_poll+0xc8/0xd8 [ 25.836509] net_rx_action+0x110/0x3a8 [ 25.840328] __do_softirq+0x124/0x288 [ 25.844061] irq_exit+0xe0/0xf0 [ 25.847272] __handle_domain_irq+0x68/0xc0 [ 25.851442] gic_handle_irq+0xa8/0xe0 [ 25.855171] el1_irq+0xb0/0x180 [ 25.858383] arch_cpu_idle+0x18/0x28 [ 25.862028] default_idle_call+0x24/0x5c [ 25.866021] do_idle+0x204/0x278 [ 25.869319] cpu_startup_entry+0x24/0x68 [ 25.873313] rest_init+0xd4/0xe4 [ 25.876611] arch_call_rest_init+0x10/0x1c [ 25.880777] start_kernel+0x5b8/0x5ec As a consequence, the OS may become unusable. Implementing the write part of ISPENDR is somewhat easy. For virtual interrupt, we only need to inject the interrupt again. For physical interrupt, we need to be more careful as the de-activation of the virtual interrupt will be propagated to the physical distributor. For simplicity, the physical interrupt will be set pending so the workflow will not differ from a "real" interrupt. Longer term, we could possible directly activate the physical interrupt and avoid taking an exception to inject the interrupt to the domain. (This is the approach taken by the new vGIC based on KVM). Signed-off-by: Julien Grall Reviewed-by: Bertrand Marquis Reviewed-by: Stefano Stabellini --- Note that this doesn't touch the read part for I{S,C}PENDR nor the write part of ICPENDR because they are more complex to implement. For physical interrupt, I didn't implement the same solution as KVM because I couldn't convince myself this could be done race free for physical interrupt. This was tested using the IRQ debugfs (CONFIG_GENERIC_IRQ_DEBUGFS=3Dy) which allows to retrigger an interrupt: 42sh> echo trigger > /sys/kernel/debug/irq/irqs/ This patch is candidate for 4.15 and also backporting to older trees. Without this patch, recent Linux version may not boot on Xen on some platforms (such as AMD Seattle used in OssTest). The patch is self-contained to implementing a single set of registers. So this would not introduce any risk on platforms where OSes don't use those registers. For the other setup (e.g. AMD Seattle + Linux 5.9+), it cannot be worse than today. So therefore, I would consider the risk limited. --- xen/arch/arm/vgic-v2.c | 10 ++++---- xen/arch/arm/vgic-v3.c | 18 ++++++--------- xen/arch/arm/vgic.c | 47 ++++++++++++++++++++++++++++++++++++++ xen/include/asm-arm/vgic.h | 2 ++ 4 files changed, 62 insertions(+), 15 deletions(-) diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c index 64b141fea586..b2da886adc18 100644 --- a/xen/arch/arm/vgic-v2.c +++ b/xen/arch/arm/vgic-v2.c @@ -472,10 +472,12 @@ static int vgic_v2_distr_mmio_write(struct vcpu *v, m= mio_info_t *info, =20 case VRANGE32(GICD_ISPENDR, GICD_ISPENDRN): if ( dabt.size !=3D DABT_WORD ) goto bad_width; - printk(XENLOG_G_ERR - "%pv: vGICD: unhandled word write %#"PRIregister" to ISPEND= R%d\n", - v, r, gicd_reg - GICD_ISPENDR); - return 0; + rank =3D vgic_rank_offset(v, 1, gicd_reg - GICD_ISPENDR, DABT_WORD= ); + if ( rank =3D=3D NULL ) goto write_ignore; + + vgic_set_irqs_pending(v, r, rank->index); + + return 1; =20 case VRANGE32(GICD_ICPENDR, GICD_ICPENDRN): if ( dabt.size !=3D DABT_WORD ) goto bad_width; diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c index fd8cfc156d0c..613f37abab5e 100644 --- a/xen/arch/arm/vgic-v3.c +++ b/xen/arch/arm/vgic-v3.c @@ -808,10 +808,12 @@ static int __vgic_v3_distr_common_mmio_write(const ch= ar *name, struct vcpu *v, =20 case VRANGE32(GICD_ISPENDR, GICD_ISPENDRN): if ( dabt.size !=3D DABT_WORD ) goto bad_width; - printk(XENLOG_G_ERR - "%pv: %s: unhandled word write %#"PRIregister" to ISPENDR%d= \n", - v, name, r, reg - GICD_ISPENDR); - return 0; + rank =3D vgic_rank_offset(v, 1, reg - GICD_ISPENDR, DABT_WORD); + if ( rank =3D=3D NULL ) goto write_ignore; + + vgic_set_irqs_pending(v, r, rank->index); + + return 1; =20 case VRANGE32(GICD_ICPENDR, GICD_ICPENDRN): if ( dabt.size !=3D DABT_WORD ) goto bad_width; @@ -975,6 +977,7 @@ static int vgic_v3_rdistr_sgi_mmio_write(struct vcpu *v= , mmio_info_t *info, case VREG32(GICR_ICACTIVER0): case VREG32(GICR_ICFGR1): case VRANGE32(GICR_IPRIORITYR0, GICR_IPRIORITYR7): + case VREG32(GICR_ISPENDR0): /* * Above registers offset are common with GICD. * So handle common with GICD handling @@ -982,13 +985,6 @@ static int vgic_v3_rdistr_sgi_mmio_write(struct vcpu *= v, mmio_info_t *info, return __vgic_v3_distr_common_mmio_write("vGICR: SGI", v, info, gicr_reg, r); =20 - case VREG32(GICR_ISPENDR0): - if ( dabt.size !=3D DABT_WORD ) goto bad_width; - printk(XENLOG_G_ERR - "%pv: vGICR: SGI: unhandled word write %#"PRIregister" to I= SPENDR0\n", - v, r); - return 0; - case VREG32(GICR_ICPENDR0): if ( dabt.size !=3D DABT_WORD ) goto bad_width; printk(XENLOG_G_ERR diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c index 82f524a35c9e..8f9400a51960 100644 --- a/xen/arch/arm/vgic.c +++ b/xen/arch/arm/vgic.c @@ -423,6 +423,53 @@ void vgic_enable_irqs(struct vcpu *v, uint32_t r, int = n) } } =20 +void vgic_set_irqs_pending(struct vcpu *v, uint32_t r, unsigned int rank) +{ + const unsigned long mask =3D r; + unsigned int i; + /* The first rank is always per-vCPU */ + bool private =3D rank =3D=3D 0; + + /* LPIs will never be set pending via this function */ + ASSERT(!is_lpi(32 * rank + 31)); + + for_each_set_bit( i, &mask, 32 ) + { + unsigned int irq =3D i + 32 * rank; + + if ( !private ) + { + struct pending_irq *p =3D spi_to_pending(v->domain, irq); + + /* + * When the domain sets the pending state for a HW interrupt on + * the virtual distributor, we set the pending state on the + * physical distributor. + * + * XXX: Investigate whether we would be able to set the + * physical interrupt active and save an interruption. (This + * is what the new vGIC does). + */ + if ( p->desc !=3D NULL ) + { + unsigned long flags; + + spin_lock_irqsave(&p->desc->lock, flags); + gic_set_pending_state(p->desc, true); + spin_unlock_irqrestore(&p->desc->lock, flags); + continue; + } + } + + /* + * If the interrupt is per-vCPU, then we want to inject the vIRQ + * to v, otherwise we should let the function figuring out the + * correct vCPU. + */ + vgic_inject_irq(v->domain, private ? v : NULL, irq, true); + } +} + bool vgic_to_sgi(struct vcpu *v, register_t sgir, enum gic_sgi_mode irqmod= e, int virq, const struct sgi_target *target) { diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h index ce1e3c4bbdac..62c2ae538db2 100644 --- a/xen/include/asm-arm/vgic.h +++ b/xen/include/asm-arm/vgic.h @@ -288,6 +288,8 @@ extern struct vgic_irq_rank *vgic_rank_offset(struct vc= pu *v, int b, int n, int extern struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned int ir= q); extern void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n); extern void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n); +extern void vgic_set_irqs_pending(struct vcpu *v, uint32_t r, + unsigned int rank); extern void register_vgic_ops(struct domain *d, const struct vgic_ops *ops= ); int vgic_v2_init(struct domain *d, int *mmio_count); int vgic_v3_init(struct domain *d, int *mmio_count); --=20 2.17.1