kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: David Rientjes <rientjes@google.com>
To: Joerg Roedel <joro@8bytes.org>
Cc: x86@kernel.org, hpa@zytor.com, Andy Lutomirski <luto@kernel.org>,
	Dave Hansen <dave.hansen@linux.intel.com>,
	Peter Zijlstra <peterz@infradead.org>,
	Thomas Hellstrom <thellstrom@vmware.com>,
	Jiri Slaby <jslaby@suse.cz>,
	Dan Williams <dan.j.williams@intel.com>,
	Tom Lendacky <thomas.lendacky@amd.com>,
	Juergen Gross <jgross@suse.com>,
	Kees Cook <keescook@chromium.org>,
	linux-kernel@vger.kernel.org, kvm@vger.kernel.org,
	virtualization@lists.linux-foundation.org,
	Joerg Roedel <jroedel@suse.de>
Subject: Re: [PATCH 21/70] x86/boot/compressed/64: Add function to map a page unencrypted
Date: Fri, 20 Mar 2020 13:53:08 -0700 (PDT)	[thread overview]
Message-ID: <alpine.DEB.2.21.2003201350300.205664@chino.kir.corp.google.com> (raw)
In-Reply-To: <20200319091407.1481-22-joro@8bytes.org>

On Thu, 19 Mar 2020, Joerg Roedel wrote:

> From: Joerg Roedel <jroedel@suse.de>
> 
> This function is needed to map the GHCB for SEV-ES guests. The GHCB is
> used for communication with the hypervisor, so its content must not be
> encrypted.
> 
> Signed-off-by: Joerg Roedel <jroedel@suse.de>
> ---
>  arch/x86/boot/compressed/ident_map_64.c | 125 ++++++++++++++++++++++++
>  arch/x86/boot/compressed/misc.h         |   1 +
>  2 files changed, 126 insertions(+)
> 
> diff --git a/arch/x86/boot/compressed/ident_map_64.c b/arch/x86/boot/compressed/ident_map_64.c
> index feb180cced28..04a5ff4bda66 100644
> --- a/arch/x86/boot/compressed/ident_map_64.c
> +++ b/arch/x86/boot/compressed/ident_map_64.c
> @@ -26,6 +26,7 @@
>  #include <asm/init.h>
>  #include <asm/pgtable.h>
>  #include <asm/trap_defs.h>
> +#include <asm/cmpxchg.h>
>  /* Use the static base for this part of the boot process */
>  #undef __PAGE_OFFSET
>  #define __PAGE_OFFSET __PAGE_OFFSET_BASE
> @@ -157,6 +158,130 @@ void initialize_identity_maps(void)
>  	write_cr3(top_level_pgt);
>  }
>  
> +static pte_t *split_large_pmd(struct x86_mapping_info *info,
> +			      pmd_t *pmdp, unsigned long __address)
> +{
> +	unsigned long page_flags;
> +	unsigned long address;
> +	pte_t *pte;
> +	pmd_t pmd;
> +	int i;
> +
> +	pte = (pte_t *)info->alloc_pgt_page(info->context);
> +	if (!pte)
> +		return NULL;
> +
> +	address     = __address & PMD_MASK;
> +	/* No large page - clear PSE flag */
> +	page_flags  = info->page_flag & ~_PAGE_PSE;
> +
> +	/* Populate the PTEs */
> +	for (i = 0; i < PTRS_PER_PMD; i++) {
> +		set_pte(&pte[i], __pte(address | page_flags));
> +		address += PAGE_SIZE;
> +	}
> +
> +	/*
> +	 * Ideally we need to clear the large PMD first and do a TLB
> +	 * flush before we write the new PMD. But the 2M range of the
> +	 * PMD might contain the code we execute and/or the stack
> +	 * we are on, so we can't do that. But that should be safe here
> +	 * because we are going from large to small mappings and we are
> +	 * also the only user of the page-table, so there is no chance
> +	 * of a TLB multihit.
> +	 */
> +	pmd = __pmd((unsigned long)pte | info->kernpg_flag);
> +	set_pmd(pmdp, pmd);
> +	/* Flush TLB to establish the new PMD */
> +	write_cr3(top_level_pgt);
> +
> +	return pte + pte_index(__address);
> +}
> +
> +static void clflush_page(unsigned long address)
> +{
> +	unsigned int flush_size;
> +	char *cl, *start, *end;
> +
> +	/*
> +	 * Hardcode cl-size to 64 - CPUID can't be used here because that might
> +	 * cause another #VC exception and the GHCB is not ready to use yet.
> +	 */
> +	flush_size = 64;
> +	start      = (char *)(address & PAGE_MASK);
> +	end        = start + PAGE_SIZE;
> +
> +	/*
> +	 * First make sure there are no pending writes on the cache-lines to
> +	 * flush.
> +	 */
> +	asm volatile("mfence" : : : "memory");
> +
> +	for (cl = start; cl != end; cl += flush_size)
> +		clflush(cl);
> +}
> +
> +static int __set_page_decrypted(struct x86_mapping_info *info,
> +				unsigned long address)
> +{
> +	unsigned long scratch, *target;
> +	pgd_t *pgdp = (pgd_t *)top_level_pgt;
> +	p4d_t *p4dp;
> +	pud_t *pudp;
> +	pmd_t *pmdp;
> +	pte_t *ptep, pte;
> +
> +	/*
> +	 * First make sure there is a PMD mapping for 'address'.
> +	 * It should already exist, but keep things generic.
> +	 *
> +	 * To map the page just read from it and fault it in if there is no
> +	 * mapping yet. add_identity_map() can't be called here because that
> +	 * would unconditionally map the address on PMD level, destroying any
> +	 * PTE-level mappings that might already exist.  Also do something
> +	 * useless with 'scratch' so the access won't be optimized away.
> +	 */
> +	target = (unsigned long *)address;
> +	scratch = *target;
> +	arch_cmpxchg(target, scratch, scratch);
> +
> +	/*
> +	 * The page is mapped at least with PMD size - so skip checks and walk
> +	 * directly to the PMD.
> +	 */
> +	p4dp = p4d_offset(pgdp, address);
> +	pudp = pud_offset(p4dp, address);
> +	pmdp = pmd_offset(pudp, address);
> +
> +	if (pmd_large(*pmdp))
> +		ptep = split_large_pmd(info, pmdp, address);
> +	else
> +		ptep = pte_offset_kernel(pmdp, address);
> +
> +	if (!ptep)
> +		return -ENOMEM;
> +
> +	/* Clear encryption flag and write new pte */
> +	pte = pte_clear_flags(*ptep, _PAGE_ENC);
> +	set_pte(ptep, pte);
> +
> +	/* Flush TLB to map the page unencrypted */
> +	write_cr3(top_level_pgt);
> +

Is there a guarantee that this flushes the tlb if cr3 == top_level_pgt 
alrady without an invlpg?

> +	/*
> +	 * Changing encryption attributes of a page requires to flush it from
> +	 * the caches.
> +	 */
> +	clflush_page(address);
> +
> +	return 0;
> +}
> +
> +int set_page_decrypted(unsigned long address)
> +{
> +	return __set_page_decrypted(&mapping_info, address);
> +}
> +
>  static void pf_error(unsigned long error_code, unsigned long address,
>  		     struct pt_regs *regs)
>  {
> diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
> index 0e3508c5c15c..42f68a858a35 100644
> --- a/arch/x86/boot/compressed/misc.h
> +++ b/arch/x86/boot/compressed/misc.h
> @@ -98,6 +98,7 @@ static inline void choose_random_location(unsigned long input,
>  #endif
>  
>  #ifdef CONFIG_X86_64
> +extern int set_page_decrypted(unsigned long address);
>  extern unsigned char _pgtable[];
>  #endif
>  

  reply	other threads:[~2020-03-20 20:53 UTC|newest]

Thread overview: 180+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-19  9:12 [RFC PATCH 00/70 v2] x86: SEV-ES Guest Support Joerg Roedel
2020-03-19  9:12 ` [PATCH 01/70] KVM: SVM: Add GHCB definitions Joerg Roedel
2020-03-23 13:23   ` [PATCH] KVM: SVM: Use __packed shorthard Borislav Petkov
2020-03-24 12:43     ` Joerg Roedel
2020-03-19  9:12 ` [PATCH 02/70] KVM: SVM: Add GHCB Accessor functions Joerg Roedel
2020-03-19  9:13 ` [PATCH 03/70] x86/cpufeatures: Add SEV-ES CPU feature Joerg Roedel
2020-03-19  9:13 ` [PATCH 04/70] x86/traps: Move some definitions to <asm/trap_defs.h> Joerg Roedel
2020-03-19  9:13 ` [PATCH 05/70] x86/insn: Make inat-tables.c suitable for pre-decompression code Joerg Roedel
2020-03-25 15:39   ` Borislav Petkov
2020-03-27  3:02     ` Masami Hiramatsu
2020-04-16 15:24       ` Joerg Roedel
2020-04-17 12:50         ` Masami Hiramatsu
2020-04-17 13:39           ` Joerg Roedel
2020-03-19  9:13 ` [PATCH 06/70] x86/umip: Factor out instruction fetch Joerg Roedel
2020-03-26 17:21   ` Borislav Petkov
2020-03-19  9:13 ` [PATCH 07/70] x86/umip: Factor out instruction decoding Joerg Roedel
2020-03-26 17:24   ` Borislav Petkov
2020-03-19  9:13 ` [PATCH 08/70] x86/insn: Add insn_get_modrm_reg_off() Joerg Roedel
2020-03-27  3:57   ` Masami Hiramatsu
2020-03-19  9:13 ` [PATCH 09/70] x86/insn: Add insn_rep_prefix() helper Joerg Roedel
2020-03-27  3:56   ` Masami Hiramatsu
2020-03-19  9:13 ` [PATCH 10/70] x86/boot/compressed: Fix debug_puthex() parameter type Joerg Roedel
2020-03-19  9:13 ` [PATCH 11/70] x86/boot/compressed/64: Disable red-zone usage Joerg Roedel
2020-03-31 13:16   ` Borislav Petkov
2020-03-19  9:13 ` [PATCH 12/70] x86/boot/compressed/64: Add IDT Infrastructure Joerg Roedel
2020-04-07  2:21   ` Arvind Sankar
2020-04-16 13:30     ` Joerg Roedel
2020-03-19  9:13 ` [PATCH 13/70] x86/boot/compressed/64: Rename kaslr_64.c to ident_map_64.c Joerg Roedel
2020-03-19  9:13 ` [PATCH 14/70] x86/boot/compressed/64: Add page-fault handler Joerg Roedel
2020-04-02 11:49   ` Borislav Petkov
2020-03-19  9:13 ` [PATCH 15/70] x86/boot/compressed/64: Always switch to own page-table Joerg Roedel
2020-04-06 11:56   ` Borislav Petkov
2020-03-19  9:13 ` [PATCH 16/70] x86/boot/compressed/64: Don't pre-map memory in KASLR code Joerg Roedel
2020-03-19  9:13 ` [PATCH 17/70] x86/boot/compressed/64: Change add_identity_map() to take start and end Joerg Roedel
2020-03-19  9:13 ` [PATCH 18/70] x86/boot/compressed/64: Add stage1 #VC handler Joerg Roedel
2020-03-20 21:16   ` David Rientjes
2020-03-20 22:19     ` Joerg Roedel
2020-04-06 12:41   ` Borislav Petkov
2020-03-19  9:13 ` [PATCH 19/70] x86/boot/compressed/64: Call set_sev_encryption_mask earlier Joerg Roedel
2020-03-19  9:13 ` [PATCH 20/70] x86/boot/compressed/64: Check return value of kernel_ident_mapping_init() Joerg Roedel
2020-03-19  9:13 ` [PATCH 21/70] x86/boot/compressed/64: Add function to map a page unencrypted Joerg Roedel
2020-03-20 20:53   ` David Rientjes [this message]
2020-03-20 21:02     ` Dave Hansen
2020-03-20 22:12       ` Joerg Roedel
2020-03-20 22:26         ` Dave Hansen
2020-03-21 15:40           ` Joerg Roedel
2020-03-19  9:13 ` [PATCH 22/70] x86/boot/compressed/64: Setup GHCB Based VC Exception handler Joerg Roedel
2020-03-19  9:13 ` [PATCH 23/70] x86/sev-es: Add support for handling IOIO exceptions Joerg Roedel
2020-03-20 21:03   ` David Rientjes
2020-03-20 22:24     ` Joerg Roedel
2020-03-19  9:13 ` [PATCH 24/70] x86/fpu: Move xgetbv()/xsetbv() into separate header Joerg Roedel
2020-03-19  9:13 ` [PATCH 25/70] x86/sev-es: Add CPUID handling to #VC handler Joerg Roedel
2020-03-19  9:13 ` [PATCH 26/70] x86/idt: Move IDT to data segment Joerg Roedel
2020-03-19  9:13 ` [PATCH 27/70] x86/idt: Split idt_data setup out of set_intr_gate() Joerg Roedel
2020-03-19  9:13 ` [PATCH 28/70] x86/idt: Move two function from k/idt.c to i/a/desc.h Joerg Roedel
2020-03-19  9:13 ` [PATCH 29/70] x86/head/64: Install boot GDT Joerg Roedel
2020-03-19  9:13 ` [PATCH 30/70] x86/head/64: Reload GDT after switch to virtual addresses Joerg Roedel
2020-03-19  9:13 ` [PATCH 31/70] x86/head/64: Load segment registers earlier Joerg Roedel
2020-03-19  9:13 ` [PATCH 32/70] x86/head/64: Switch to initial stack earlier Joerg Roedel
2020-03-19  9:13 ` [PATCH 33/70] x86/head/64: Build k/head64.c with -fno-stack-protector Joerg Roedel
2020-03-19  9:13 ` [PATCH 34/70] x86/head/64: Load IDT earlier Joerg Roedel
2020-03-19  9:13 ` [PATCH 35/70] x86/head/64: Move early exception dispatch to C code Joerg Roedel
2020-03-19  9:13 ` [PATCH 36/70] x86/sev-es: Add SEV-ES Feature Detection Joerg Roedel
2020-03-19  9:13 ` [PATCH 37/70] x86/sev-es: Compile early handler code into kernel image Joerg Roedel
2020-03-19  9:13 ` [PATCH 38/70] x86/sev-es: Setup early #VC handler Joerg Roedel
2020-03-19  9:13 ` [PATCH 39/70] x86/sev-es: Setup GHCB based boot " Joerg Roedel
2020-03-19  9:13 ` [PATCH 40/70] x86/sev-es: Setup per-cpu GHCBs for the runtime handler Joerg Roedel
2020-04-14 19:03   ` Mike Stunes
2020-04-14 20:04     ` Tom Lendacky
2020-04-14 20:12       ` Dave Hansen
2020-04-14 20:16         ` Tom Lendacky
2020-04-14 20:18           ` Tom Lendacky
2020-04-15 15:54       ` Joerg Roedel
2020-04-15 15:53     ` Joerg Roedel
2020-04-23  1:33       ` Bo Gan
2020-04-23 11:30         ` Joerg Roedel
2020-03-19  9:13 ` [PATCH 41/70] x86/sev-es: Add Runtime #VC Exception Handler Joerg Roedel
2020-03-19 15:44   ` Andy Lutomirski
2020-03-19 16:24     ` Joerg Roedel
2020-03-19 18:43       ` Andy Lutomirski
2020-03-19 19:38         ` Joerg Roedel
2020-03-19  9:13 ` [PATCH 42/70] x86/sev-es: Support nested #VC exceptions Joerg Roedel
2020-03-19 15:46   ` Andy Lutomirski
2020-03-19 16:12     ` Joerg Roedel
2020-03-19  9:13 ` [PATCH 43/70] x86/sev-es: Wire up existing #VC exit-code handlers Joerg Roedel
2020-03-19  9:13 ` [PATCH 44/70] x86/sev-es: Handle instruction fetches from user-space Joerg Roedel
2020-03-19  9:13 ` [PATCH 45/70] x86/sev-es: Harden runtime #VC handler for exceptions " Joerg Roedel
2020-03-19  9:13 ` [PATCH 46/70] x86/sev-es: Filter exceptions not supported " Joerg Roedel
2020-03-19  9:13 ` [PATCH 47/70] x86/sev-es: Handle MMIO events Joerg Roedel
2020-03-19  9:13 ` [PATCH 48/70] x86/sev-es: Handle MMIO String Instructions Joerg Roedel
2020-03-19  9:13 ` [PATCH 49/70] x86/sev-es: Handle MSR events Joerg Roedel
2020-03-19  9:13 ` [PATCH 50/70] x86/sev-es: Handle DR7 read/write events Joerg Roedel
2020-03-19  9:13 ` [PATCH 51/70] x86/sev-es: Handle WBINVD Events Joerg Roedel
2020-03-19  9:13 ` [PATCH 52/70] x86/sev-es: Handle RDTSC Events Joerg Roedel
2020-03-19  9:13 ` [PATCH 53/70] x86/sev-es: Handle RDPMC Events Joerg Roedel
2020-03-19  9:13 ` [PATCH 54/70] x86/sev-es: Handle INVD Events Joerg Roedel
2020-03-19  9:13 ` [PATCH 55/70] x86/sev-es: Handle RDTSCP Events Joerg Roedel
2020-04-24 21:03   ` [PATCH] Allow RDTSC and RDTSCP from userspace Mike Stunes
2020-04-24 21:24     ` Dave Hansen
2020-04-24 21:27       ` Tom Lendacky
2020-04-24 22:53         ` Dave Hansen
2020-04-25 12:49           ` Joerg Roedel
2020-04-25 18:15             ` Andy Lutomirski
2020-04-25 19:10               ` Joerg Roedel
2020-04-25 19:47                 ` Andy Lutomirski
2020-04-25 20:23                   ` Joerg Roedel
2020-04-25 22:10                     ` Andy Lutomirski
2020-04-27 17:37                       ` Should SEV-ES #VC use IST? (Re: [PATCH] Allow RDTSC and RDTSCP from userspace) Andy Lutomirski
2020-04-27 18:15                         ` Andrew Cooper
2020-04-27 18:43                         ` Tom Lendacky
2020-04-28  7:55                         ` Joerg Roedel
2020-04-28 16:34                           ` Andrew Cooper
2020-06-23 11:07                           ` Peter Zijlstra
2020-06-23 11:30                             ` Joerg Roedel
2020-06-23 11:48                               ` Peter Zijlstra
2020-06-23 12:04                                 ` Joerg Roedel
2020-06-23 12:52                                   ` Peter Zijlstra
2020-06-23 13:40                                     ` Joerg Roedel
2020-06-23 13:59                                       ` Peter Zijlstra
2020-06-23 14:53                                         ` Peter Zijlstra
2020-06-23 14:59                                           ` Joerg Roedel
2020-06-23 15:23                                             ` Peter Zijlstra
2020-06-23 15:38                                               ` Peter Zijlstra
2020-06-23 15:38                                               ` Joerg Roedel
2020-06-23 16:02                                                 ` Peter Zijlstra
2020-06-23 15:39                                               ` Andrew Cooper
2020-06-23 15:52                                                 ` Peter Zijlstra
2020-06-23 16:03                                                   ` Dave Hansen
2020-06-23 16:13                                                     ` Peter Zijlstra
2020-06-23 16:13                                                 ` Borislav Petkov
2020-06-23 11:51                               ` Andrew Cooper
2020-06-23 12:47                                 ` Peter Zijlstra
2020-06-23 13:57                                   ` Andrew Cooper
2020-06-23 15:51                                 ` Borislav Petkov
2020-06-23  9:45                         ` Joerg Roedel
2020-06-23 10:45                           ` Peter Zijlstra
2020-06-23 11:11                             ` Joerg Roedel
2020-06-23 11:14                               ` Peter Zijlstra
2020-06-23 11:43                                 ` Joerg Roedel
2020-06-23 11:50                                   ` Peter Zijlstra
2020-06-23 12:12                                     ` Joerg Roedel
2020-06-23 13:03                                       ` Peter Zijlstra
2020-06-23 14:49                                         ` Joerg Roedel
2020-06-23 15:16                                           ` Peter Zijlstra
2020-06-23 15:32                                             ` Andrew Cooper
2020-06-23 16:10                                               ` Borislav Petkov
2020-06-23 15:22                                         ` Andrew Cooper
2020-06-23 18:26                                           ` Andy Lutomirski
2020-06-23 18:56                                             ` Andrew Cooper
2020-04-27 18:47             ` [PATCH] Allow RDTSC and RDTSCP from userspace Dave Hansen
2020-04-25 12:28     ` Joerg Roedel
2020-03-19  9:13 ` [PATCH 56/70] x86/sev-es: Handle MONITOR/MONITORX Events Joerg Roedel
2020-03-19  9:13 ` [PATCH 57/70] x86/sev-es: Handle MWAIT/MWAITX Events Joerg Roedel
2020-03-19  9:13 ` [PATCH 58/70] x86/sev-es: Handle VMMCALL Events Joerg Roedel
2020-03-19  9:13 ` [PATCH 59/70] x86/sev-es: Handle #AC Events Joerg Roedel
2020-03-19  9:13 ` [PATCH 60/70] x86/sev-es: Handle #DB Events Joerg Roedel
2020-03-19  9:13 ` [PATCH 61/70] x86/paravirt: Allow hypervisor specific VMMCALL handling under SEV-ES Joerg Roedel
2020-03-19  9:13 ` [PATCH 62/70] x86/kvm: Add KVM " Joerg Roedel
2020-03-20 21:23   ` David Rientjes
2020-03-20 22:21     ` Joerg Roedel
2020-03-19  9:14 ` [PATCH 63/70] x86/vmware: Add VMware specific handling for VMMCALL " Joerg Roedel
2020-03-19 10:18   ` Thomas Hellstrom
2020-03-19  9:14 ` [PATCH 64/70] x86/realmode: Add SEV-ES specific trampoline entry point Joerg Roedel
2020-03-19  9:14 ` [PATCH 65/70] x86/realmode: Setup AP jump table Joerg Roedel
2020-03-19  9:14 ` [PATCH 66/70] x86/head/64: Don't call verify_cpu() on starting APs Joerg Roedel
2020-03-19  9:14 ` [PATCH 67/70] x86/head/64: Rename start_cpu0 Joerg Roedel
2020-03-19  9:14 ` [PATCH 68/70] x86/sev-es: Support CPU offline/online Joerg Roedel
2020-03-19  9:14 ` [PATCH 69/70] x86/cpufeature: Add SEV_ES_GUEST CPU Feature Joerg Roedel
2020-03-19  9:14 ` [PATCH 70/70] x86/sev-es: Add NMI state tracking Joerg Roedel
2020-03-19 15:35   ` Andy Lutomirski
2020-03-19 16:07     ` Joerg Roedel
2020-03-19 18:40       ` Andy Lutomirski
2020-03-19 19:26         ` Joerg Roedel
2020-03-19 21:27           ` Andy Lutomirski
2020-03-20 19:48             ` Joerg Roedel
2020-03-20 13:17     ` [RFC PATCH v2.1] x86/sev-es: Handle NMI State Joerg Roedel
2020-03-20 14:42       ` Dave Hansen
2020-03-20 19:42         ` Joerg Roedel
2020-03-19 16:53   ` [PATCH 70/70] x86/sev-es: Add NMI state tracking Mika Penttilä
2020-03-19 19:41     ` Joerg Roedel

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=alpine.DEB.2.21.2003201350300.205664@chino.kir.corp.google.com \
    --to=rientjes@google.com \
    --cc=dan.j.williams@intel.com \
    --cc=dave.hansen@linux.intel.com \
    --cc=hpa@zytor.com \
    --cc=jgross@suse.com \
    --cc=joro@8bytes.org \
    --cc=jroedel@suse.de \
    --cc=jslaby@suse.cz \
    --cc=keescook@chromium.org \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luto@kernel.org \
    --cc=peterz@infradead.org \
    --cc=thellstrom@vmware.com \
    --cc=thomas.lendacky@amd.com \
    --cc=virtualization@lists.linux-foundation.org \
    --cc=x86@kernel.org \
    /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;
as well as URLs for NNTP newsgroup(s).