public inbox for kvm@vger.kernel.org
 help / color / mirror / Atom feed
From: Marc Zyngier <maz@kernel.org>
To: Sascha Bischoff <Sascha.Bischoff@arm.com>
Cc: "linux-arm-kernel@lists.infradead.org"
	<linux-arm-kernel@lists.infradead.org>,
	"kvmarm@lists.linux.dev" <kvmarm@lists.linux.dev>,
	"kvm@vger.kernel.org" <kvm@vger.kernel.org>, nd <nd@arm.com>,
	"oliver.upton@linux.dev" <oliver.upton@linux.dev>,
	Joey Gouly <Joey.Gouly@arm.com>,
	Suzuki Poulose <Suzuki.Poulose@arm.com>,
	"yuzenghui@huawei.com" <yuzenghui@huawei.com>,
	"peter.maydell@linaro.org" <peter.maydell@linaro.org>,
	"lpieralisi@kernel.org" <lpieralisi@kernel.org>,
	Timothy Hayes <Timothy.Hayes@arm.com>,
	"jonathan.cameron@huawei.com" <jonathan.cameron@huawei.com>
Subject: Re: [PATCH v5 10/36] KVM: arm64: gic-v5: Sanitize ID_AA64PFR2_EL1.GCIE
Date: Tue, 03 Mar 2026 15:54:33 +0000	[thread overview]
Message-ID: <86a4wo9adi.wl-maz@kernel.org> (raw)
In-Reply-To: <20260226155515.1164292-11-sascha.bischoff@arm.com>

On Thu, 26 Feb 2026 15:58:00 +0000,
Sascha Bischoff <Sascha.Bischoff@arm.com> wrote:
> 
> Add in a sanitization function for ID_AA64PFR2_EL1, preserving the
> already-present behaviour for the FPMR, MTEFAR, and MTESTOREONLY
> fields. Add sanitisation for the GCIE field, which is set to IMP if
> the host supports a GICv5 guest and NI, otherwise.
> 
> Extend the sanitisation that takes place in kvm_vgic_create() to zero
> the ID_AA64PFR2.GCIE field when a non-GICv5 GIC is created. More
> importantly, move this sanitisation to a separate function,
> kvm_vgic_finalize_sysregs(), and call it from kvm_finalize_sys_regs().
> 
> We are required to finalize the GIC and GCIE fields a second time in
> kvm_finalize_sys_regs() due to how QEMU blindly reads out then
> verbatim restores the system register state. This avoids the issue
> where both the GCIE and GIC features are marked as present (an
> architecturally invalid combination), and hence guests fall over. See
> the comment in kvm_finalize_sys_regs() for more details.
> 
> Overall, the following happens:
> 
> * Before an irqchip is created, FEAT_GCIE is presented if the host
>   supports GICv5-based guests.
> * Once an irqchip is created, all other supported irqchips are hidden
>   from the guest; system register state reflects the guest's irqchip.
> * Userspace is allowed to set invalid irqchip feature combinations in
>   the system registers, but...
> * ...invalid combinations are removed a second time prior to the first
>   run of the guest, and things hopefully just work.
> 
> All of this extra work is required to make sure that "legacy" GICv3
> guests based on QEMU transparently work on compatible GICv5 hosts
> without modification.
> 
> Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
> ---
>  arch/arm64/kvm/sys_regs.c       | 70 +++++++++++++++++++++++++++++----
>  arch/arm64/kvm/vgic/vgic-init.c | 43 +++++++++++++-------
>  include/kvm/arm_vgic.h          |  1 +
>  3 files changed, 92 insertions(+), 22 deletions(-)
> 
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 11e75f2522f95..1039150716d43 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -1758,6 +1758,7 @@ static u8 pmuver_to_perfmon(u8 pmuver)
>  
>  static u64 sanitise_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu, u64 val);
>  static u64 sanitise_id_aa64pfr1_el1(const struct kvm_vcpu *vcpu, u64 val);
> +static u64 sanitise_id_aa64pfr2_el1(const struct kvm_vcpu *vcpu, u64 val);
>  static u64 sanitise_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val);
>  
>  /* Read a sanitised cpufeature ID register by sys_reg_desc */
> @@ -1783,10 +1784,7 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu,
>  		val = sanitise_id_aa64pfr1_el1(vcpu, val);
>  		break;
>  	case SYS_ID_AA64PFR2_EL1:
> -		val &= ID_AA64PFR2_EL1_FPMR |
> -			(kvm_has_mte(vcpu->kvm) ?
> -			 ID_AA64PFR2_EL1_MTEFAR | ID_AA64PFR2_EL1_MTESTOREONLY :
> -			 0);
> +		val = sanitise_id_aa64pfr2_el1(vcpu, val);
>  		break;
>  	case SYS_ID_AA64ISAR1_EL1:
>  		if (!vcpu_has_ptrauth(vcpu))
> @@ -2024,6 +2022,23 @@ static u64 sanitise_id_aa64pfr1_el1(const struct kvm_vcpu *vcpu, u64 val)
>  	return val;
>  }
>  
> +static u64 sanitise_id_aa64pfr2_el1(const struct kvm_vcpu *vcpu, u64 val)
> +{
> +	val &= ID_AA64PFR2_EL1_FPMR |
> +	       ID_AA64PFR2_EL1_MTEFAR |
> +	       ID_AA64PFR2_EL1_MTESTOREONLY;
> +
> +	if (!kvm_has_mte(vcpu->kvm)) {
> +		val &= ~ID_AA64PFR2_EL1_MTEFAR;
> +		val &= ~ID_AA64PFR2_EL1_MTESTOREONLY;
> +	}
> +
> +	if (vgic_host_has_gicv5())
> +		val |= SYS_FIELD_PREP_ENUM(ID_AA64PFR2_EL1, GCIE, IMP);
> +
> +	return val;
> +}
> +
>  static u64 sanitise_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, u64 val)
>  {
>  	val = ID_REG_LIMIT_FIELD_ENUM(val, ID_AA64DFR0_EL1, DebugVer, V8P8);
> @@ -2213,6 +2228,12 @@ static int set_id_aa64pfr1_el1(struct kvm_vcpu *vcpu,
>  	return set_id_reg(vcpu, rd, user_val);
>  }
>  
> +static int set_id_aa64pfr2_el1(struct kvm_vcpu *vcpu,
> +			       const struct sys_reg_desc *rd, u64 user_val)
> +{
> +	return set_id_reg(vcpu, rd, user_val);
> +}
> +
>  /*
>   * Allow userspace to de-feature a stage-2 translation granule but prevent it
>   * from claiming the impossible.
> @@ -3194,10 +3215,11 @@ static const struct sys_reg_desc sys_reg_descs[] = {
>  				       ID_AA64PFR1_EL1_RES0 |
>  				       ID_AA64PFR1_EL1_MPAM_frac |
>  				       ID_AA64PFR1_EL1_MTE)),
> -	ID_WRITABLE(ID_AA64PFR2_EL1,
> -		    ID_AA64PFR2_EL1_FPMR |
> -		    ID_AA64PFR2_EL1_MTEFAR |
> -		    ID_AA64PFR2_EL1_MTESTOREONLY),
> +	ID_FILTERED(ID_AA64PFR2_EL1, id_aa64pfr2_el1,
> +		    ~(ID_AA64PFR2_EL1_FPMR |
> +		      ID_AA64PFR2_EL1_MTEFAR |
> +		      ID_AA64PFR2_EL1_MTESTOREONLY |
> +		      ID_AA64PFR2_EL1_GCIE)),
>  	ID_UNALLOCATED(4,3),
>  	ID_WRITABLE(ID_AA64ZFR0_EL1, ~ID_AA64ZFR0_EL1_RES0),
>  	ID_HIDDEN(ID_AA64SMFR0_EL1),
> @@ -5668,8 +5690,40 @@ int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu)
>  
>  		val = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1) & ~ID_AA64PFR0_EL1_GIC;
>  		kvm_set_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1, val);
> +		val = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR2_EL1) & ~ID_AA64PFR2_EL1_GCIE;
> +		kvm_set_vm_id_reg(kvm, SYS_ID_AA64PFR2_EL1, val);
>  		val = kvm_read_vm_id_reg(kvm, SYS_ID_PFR1_EL1) & ~ID_PFR1_EL1_GIC;
>  		kvm_set_vm_id_reg(kvm, SYS_ID_PFR1_EL1, val);
> +	} else {
> +		/*
> +		 * Certain userspace software - QEMU - samples the system
> +		 * register state without creating an irqchip, then blindly
> +		 * restores the state prior to running the final guest. This
> +		 * means that it restores the virtualization & emulation
> +		 * capabilities of the host system, rather than something that
> +		 * reflects the final guest state. Moreover, it checks that the
> +		 * state was "correctly" restored (i.e., verbatim), bailing if
> +		 * it isn't, so masking off invalid state isn't an option.
> +		 *
> +		 * On GICv5 hardware that supports FEAT_GCIE_LEGACY we can run
> +		 * both GICv3- and GICv5-based guests. Therefore, we initially
> +		 * present both ID_AA64PFR0.GIC and ID_AA64PFR2.GCIE as IMP to
> +		 * reflect that userspace can create EITHER a vGICv3 or a
> +		 * vGICv5. This is an architecturally invalid combination, of
> +		 * course. Once an in-kernel GIC is created, the sysreg state is
> +		 * updated to reflect the actual, valid configuration.
> +		 *
> +		 * Setting both the GIC and GCIE features to IMP unsurprisingly
> +		 * results in guests falling over, and hence we need to fix up
> +		 * this mess in KVM. Before running for the first time we yet
> +		 * again ensure that the GIC and GCIE fields accurately reflect
> +		 * the actual hardware the guest should see.
> +		 *
> +		 * This hack allows legacy QEMU-based GICv3 guests to run
> +		 * unmodified on compatible GICv5 hosts, and avoids the inverse
> +		 * problem for GICv5-based guests in the future.
> +		 */
> +		kvm_vgic_finalize_sysregs(kvm);

An alternative to this sorry hack would be to have a separate view of
the idregs for luserspace to get whatever expected. But you then need
to invalidate that copy at some point so that you can migrate the
guest safely, and you'd probably end-up doing a similar thing.

I appreciate that you are doing this for the sake of preserving SW
compatibility, but do you foresee a way out of this mess that does not
involve asking the QEMU folks to fix their stuff? I don't think we can
paper over their over-simplistic design forever.

>  	}
>  
>  	if (vcpu_has_nv(vcpu)) {
> diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
> index 9b3091ad868cf..d1db384698238 100644
> --- a/arch/arm64/kvm/vgic/vgic-init.c
> +++ b/arch/arm64/kvm/vgic/vgic-init.c
> @@ -71,7 +71,6 @@ static int vgic_allocate_private_irqs_locked(struct kvm_vcpu *vcpu, u32 type);
>  int kvm_vgic_create(struct kvm *kvm, u32 type)
>  {
>  	struct kvm_vcpu *vcpu;
> -	u64 aa64pfr0, pfr1;
>  	unsigned long i;
>  	int ret;
>  
> @@ -162,19 +161,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
>  
>  	kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
>  
> -	aa64pfr0 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1) & ~ID_AA64PFR0_EL1_GIC;
> -	pfr1 = kvm_read_vm_id_reg(kvm, SYS_ID_PFR1_EL1) & ~ID_PFR1_EL1_GIC;
> -
> -	if (type == KVM_DEV_TYPE_ARM_VGIC_V2) {
> -		kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
> -	} else {
> -		INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
> -		aa64pfr0 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP);
> -		pfr1 |= SYS_FIELD_PREP_ENUM(ID_PFR1_EL1, GIC, GICv3);
> -	}
> -
> -	kvm_set_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1, aa64pfr0);
> -	kvm_set_vm_id_reg(kvm, SYS_ID_PFR1_EL1, pfr1);
> +	/*
> +	 * We've now created the GIC. Update the system register state
> +	 * to accurately reflect what we've created.
> +	 */
> +	kvm_vgic_finalize_sysregs(kvm);

As pointed out f2f, this will conflict with the patch posted at
https://patch.msgid.link/20260228164559.936268-1-maz@kernel.org

>  
>  	if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
>  		kvm->arch.vgic.nassgicap = system_supports_direct_sgis();
> @@ -617,6 +608,30 @@ int kvm_vgic_map_resources(struct kvm *kvm)
>  	return ret;
>  }
>  
> +void kvm_vgic_finalize_sysregs(struct kvm *kvm)

nit: could you rename this to kvm_vgic_finalize_idregs()?

> +{
> +	u32 type = kvm->arch.vgic.vgic_model;
> +	u64 aa64pfr0, aa64pfr2, pfr1;
> +
> +	aa64pfr0 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1) & ~ID_AA64PFR0_EL1_GIC;
> +	aa64pfr2 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR2_EL1) & ~ID_AA64PFR2_EL1_GCIE;
> +	pfr1 = kvm_read_vm_id_reg(kvm, SYS_ID_PFR1_EL1) & ~ID_PFR1_EL1_GIC;
> +
> +	if (type == KVM_DEV_TYPE_ARM_VGIC_V2) {
> +		kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
> +	} else if (type == KVM_DEV_TYPE_ARM_VGIC_V3) {
> +		INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
> +		aa64pfr0 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP);
> +		pfr1 |= SYS_FIELD_PREP_ENUM(ID_PFR1_EL1, GIC, GICv3);
> +	} else {
> +		aa64pfr2 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR2_EL1, GCIE, IMP);
> +	}

I'd rather see this written as:

	switch (kvm->arch.vgic.vgic_model) {
	case KVM_DEV_TYPE_ARM_VGIC_V2:
		kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
		break;
	case KVM_DEV_TYPE_ARM_VGIC_V3:
		INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
		aa64pfr0 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP);
		pfr1 |= SYS_FIELD_PREP_ENUM(ID_PFR1_EL1, GIC, GICv3);
		break;
	case KVM_DEV_TYPE_ARM_VGIC_V5:
		aa64pfr2 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR2_EL1, GCIE, IMP);
		break;
	default:
		WARN_ONCE(1, "WTF???\n");
	}

which I find more readable than the if/else cascade.

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.

  reply	other threads:[~2026-03-03 15:54 UTC|newest]

Thread overview: 56+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-26 15:55 [PATCH v5 00/36] KVM: arm64: Introduce vGIC-v5 with PPI support Sascha Bischoff
2026-02-26 15:55 ` [PATCH v5 01/36] KVM: arm64: vgic-v3: Drop userspace write sanitization for ID_AA64PFR0.GIC on GICv5 Sascha Bischoff
2026-02-26 15:55 ` [PATCH v5 02/36] KVM: arm64: vgic: Rework vgic_is_v3() and add vgic_host_has_gicvX() Sascha Bischoff
2026-02-26 15:56 ` [PATCH v5 03/36] KVM: arm64: Return early from kvm_finalize_sys_regs() if guest has run Sascha Bischoff
2026-02-26 15:56 ` [PATCH v5 04/36] arm64/sysreg: Add remaining GICv5 ICC_ & ICH_ sysregs for KVM support Sascha Bischoff
2026-02-26 15:56 ` [PATCH v5 05/36] arm64/sysreg: Add GICR CDNMIA encoding Sascha Bischoff
2026-02-26 15:56 ` [PATCH v5 06/36] KVM: arm64: gic-v5: Add ARM_VGIC_V5 device to KVM headers Sascha Bischoff
2026-02-26 15:57 ` [PATCH v5 07/36] KVM: arm64: gic: Introduce interrupt type helpers Sascha Bischoff
2026-03-03 15:04   ` Marc Zyngier
2026-03-03 17:21     ` Sascha Bischoff
2026-02-26 15:57 ` [PATCH v5 08/36] KVM: arm64: gic-v5: Add Arm copyright header Sascha Bischoff
2026-02-26 15:57 ` [PATCH v5 09/36] KVM: arm64: gic-v5: Detect implemented PPIs on boot Sascha Bischoff
2026-03-03 15:10   ` Marc Zyngier
2026-03-03 17:22     ` Sascha Bischoff
2026-02-26 15:58 ` [PATCH v5 10/36] KVM: arm64: gic-v5: Sanitize ID_AA64PFR2_EL1.GCIE Sascha Bischoff
2026-03-03 15:54   ` Marc Zyngier [this message]
2026-03-03 17:49     ` Sascha Bischoff
2026-02-26 15:58 ` [PATCH v5 11/36] KVM: arm64: gic-v5: Support GICv5 FGTs & FGUs Sascha Bischoff
2026-02-26 15:58 ` [PATCH v5 12/36] KVM: arm64: gic-v5: Add emulation for ICC_IAFFIDR_EL1 accesses Sascha Bischoff
2026-03-03 16:02   ` Marc Zyngier
2026-03-03 17:54     ` Sascha Bischoff
2026-02-26 15:58 ` [PATCH v5 13/36] KVM: arm64: gic-v5: Trap and emulate ICC_IDR0_EL1 accesses Sascha Bischoff
2026-02-26 15:59 ` [PATCH v5 14/36] KVM: arm64: gic-v5: Add vgic-v5 save/restore hyp interface Sascha Bischoff
2026-03-03 17:10   ` Marc Zyngier
2026-03-04 11:32     ` Sascha Bischoff
2026-02-26 15:59 ` [PATCH v5 15/36] KVM: arm64: gic-v5: Implement GICv5 load/put and save/restore Sascha Bischoff
2026-03-04  9:26   ` Marc Zyngier
2026-03-04 14:21     ` Sascha Bischoff
2026-02-26 15:59 ` [PATCH v5 16/36] KVM: arm64: gic-v5: Implement direct injection of PPIs Sascha Bischoff
2026-03-04  9:35   ` Marc Zyngier
2026-03-05 11:22     ` Sascha Bischoff
2026-02-26 15:59 ` [PATCH v5 17/36] KVM: arm64: gic-v5: Finalize GICv5 PPIs and generate mask Sascha Bischoff
2026-03-04 10:50   ` Marc Zyngier
2026-03-04 17:38     ` Sascha Bischoff
2026-02-26 16:00 ` [PATCH v5 18/36] KVM: arm64: gic: Introduce queue_irq_unlock to irq_ops Sascha Bischoff
2026-02-26 16:00 ` [PATCH v5 19/36] KVM: arm64: gic-v5: Implement PPI interrupt injection Sascha Bischoff
2026-03-04 13:08   ` Marc Zyngier
2026-02-26 16:00 ` [PATCH v5 20/36] KVM: arm64: gic-v5: Init Private IRQs (PPIs) for GICv5 Sascha Bischoff
2026-03-04 14:21   ` Marc Zyngier
2026-03-05 13:35     ` Sascha Bischoff
2026-02-26 16:00 ` [PATCH v5 21/36] KVM: arm64: gic-v5: Check for pending PPIs Sascha Bischoff
2026-02-26 16:01 ` [PATCH v5 22/36] KVM: arm64: gic-v5: Trap and mask guest ICC_PPI_ENABLERx_EL1 writes Sascha Bischoff
2026-02-26 16:01 ` [PATCH v5 23/36] KVM: arm64: gic-v5: Support GICv5 interrupts with KVM_IRQ_LINE Sascha Bischoff
2026-02-26 16:01 ` [PATCH v5 24/36] KVM: arm64: gic-v5: Create and initialise vgic_v5 Sascha Bischoff
2026-02-26 16:01 ` [PATCH v5 25/36] KVM: arm64: gic-v5: Initialise ID and priority bits when resetting vcpu Sascha Bischoff
2026-02-26 16:02 ` [PATCH v5 26/36] KVM: arm64: gic-v5: Enlighten arch timer for GICv5 Sascha Bischoff
2026-02-26 16:02 ` [PATCH v5 27/36] KVM: arm64: gic-v5: Mandate architected PPI for PMU emulation on GICv5 Sascha Bischoff
2026-02-26 16:02 ` [PATCH v5 28/36] KVM: arm64: gic: Hide GICv5 for protected guests Sascha Bischoff
2026-02-26 16:02 ` [PATCH v5 29/36] KVM: arm64: gic-v5: Hide FEAT_GCIE from NV GICv5 guests Sascha Bischoff
2026-02-26 16:03 ` [PATCH v5 30/36] KVM: arm64: gic-v5: Introduce kvm_arm_vgic_v5_ops and register them Sascha Bischoff
2026-02-26 16:03 ` [PATCH v5 31/36] KVM: arm64: gic-v5: Set ICH_VCTLR_EL2.En on boot Sascha Bischoff
2026-02-26 16:03 ` [PATCH v5 32/36] KVM: arm64: gic-v5: Probe for GICv5 device Sascha Bischoff
2026-02-26 16:04 ` [PATCH v5 33/36] Documentation: KVM: Introduce documentation for VGICv5 Sascha Bischoff
2026-02-26 16:04 ` [PATCH v5 34/36] KVM: arm64: selftests: Introduce a minimal GICv5 PPI selftest Sascha Bischoff
2026-02-26 16:04 ` [PATCH v5 35/36] KVM: arm64: gic-v5: Communicate userspace-driveable PPIs via a UAPI Sascha Bischoff
2026-02-26 16:04 ` [PATCH v5 36/36] KVM: arm64: selftests: Add no-vgic-v5 selftest Sascha Bischoff

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=86a4wo9adi.wl-maz@kernel.org \
    --to=maz@kernel.org \
    --cc=Joey.Gouly@arm.com \
    --cc=Sascha.Bischoff@arm.com \
    --cc=Suzuki.Poulose@arm.com \
    --cc=Timothy.Hayes@arm.com \
    --cc=jonathan.cameron@huawei.com \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.linux.dev \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=lpieralisi@kernel.org \
    --cc=nd@arm.com \
    --cc=oliver.upton@linux.dev \
    --cc=peter.maydell@linaro.org \
    --cc=yuzenghui@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox