From: "Laszlo Ersek" <lersek@redhat.com>
To: devel@edk2.groups.io, thomas.lendacky@amd.com
Cc: Jordan Justen <jordan.l.justen@intel.com>,
Ard Biesheuvel <ard.biesheuvel@linaro.org>,
Michael D Kinney <michael.d.kinney@intel.com>,
Liming Gao <liming.gao@intel.com>,
Eric Dong <eric.dong@intel.com>, Ray Ni <ray.ni@intel.com>,
Brijesh Singh <brijesh.singh@amd.com>
Subject: Re: [edk2-devel] [PATCH v4 31/40] OvmfPkg/ResetVector: Add support for a 32-bit SEV check
Date: Thu, 6 Feb 2020 09:51:15 +0100 [thread overview]
Message-ID: <9c0cbb02-d9f1-4686-0fe8-37d8bbc10b77@redhat.com> (raw)
In-Reply-To: <68967471a5e9c2652c1a43ff2d5557e5983c0625.1580857303.git.thomas.lendacky@amd.com>
On 02/05/20 00:01, Lendacky, Thomas wrote:
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198
>
> During BSP startup, the reset vector code will issue a CPUID instruction
> while in 32-bit mode. When running as an SEV-ES guest, this will trigger
> a #VC exception.
>
> Add exception handling support to the early reset vector code to catch
> these exceptions. Also, since the guest is in 32-bit mode at this point,
> writes to the GHCB will be encrypted and thus not able to be read by the
> hypervisor, so use the GHCB CPUID request/response protocol to obtain the
> requested CPUID function values and provide these to the guest.
>
> The exception handling support is active during the SEV check and uses the
> OVMF temporary RAM space for a stack. After the SEV check is complete, the
> exception handling support is removed and the stack pointer cleared.
>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
> ---
> OvmfPkg/ResetVector/ResetVector.inf | 3 +
> OvmfPkg/ResetVector/Ia32/PageTables64.asm | 275 +++++++++++++++++++++-
> OvmfPkg/ResetVector/ResetVector.nasmb | 2 +
> 3 files changed, 277 insertions(+), 3 deletions(-)
The new SEV_ES_WORK_AREA actions look OK to me.
Thanks
Laszlo
>
> diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
> index 9aedbe9b3640..f9e9578d22b2 100644
> --- a/OvmfPkg/ResetVector/ResetVector.inf
> +++ b/OvmfPkg/ResetVector/ResetVector.inf
> @@ -36,7 +36,10 @@ [BuildOptions]
> [Pcd]
> gUefiCpuPkgTokenSpaceGuid.PcdSecGhcbBase
> gUefiCpuPkgTokenSpaceGuid.PcdSecGhcbSize
> + gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
> gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
> + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
> + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
> diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
> index 9f86ddf6f08f..7c72128a84d6 100644
> --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
> +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
> @@ -36,13 +36,58 @@ BITS 32
> PAGE_READ_WRITE + \
> PAGE_PRESENT)
>
> +;
> +; SEV-ES #VC exception handler support
> +;
> +; #VC handler local variable locations
> +;
> +%define VC_CPUID_RESULT_EAX 0
> +%define VC_CPUID_RESULT_EBX 4
> +%define VC_CPUID_RESULT_ECX 8
> +%define VC_CPUID_RESULT_EDX 12
> +%define VC_GHCB_MSR_EDX 16
> +%define VC_GHCB_MSR_EAX 20
> +%define VC_CPUID_REQUEST_REGISTER 24
> +%define VC_CPUID_FUNCTION 28
> +
> +; #VC handler total local variable size
> +;
> +%define VC_VARIABLE_SIZE 32
> +
> +; #VC handler GHCB CPUID request/response protocol values
> +;
> +%define GHCB_CPUID_REQUEST 4
> +%define GHCB_CPUID_RESPONSE 5
> +%define GHCB_CPUID_REGISTER_SHIFT 30
> +%define CPUID_INSN_LEN 2
> +
> +
> ; Check if Secure Encrypted Virtualization (SEV) feature is enabled
> ;
> -; If SEV is enabled then EAX will be at least 32
> +; Modified: EAX, EBX, ECX, EDX, ESP
> +;
> +; If SEV is enabled then EAX will be at least 32.
> ; If SEV is disabled then EAX will be zero.
> ;
> CheckSevFeature:
> + ; Set the first byte of the workarea to zero to communicate to the SEC
> + ; phase that SEV-ES is not enabled. If SEV-ES is enabled, the CPUID
> + ; instruction will trigger a #VC exception where the first byte of the
> + ; workarea will be set to one.
> + mov byte[SEV_ES_WORK_AREA], 0
> +
> + ;
> + ; Set up exception handlers to check for SEV-ES
> + ; Load temporary RAM stack based on PCDs (see SevEsIdtVmmComm for
> + ; stack usage)
> + ; Establish exception handlers
> + ;
> + mov esp, SEV_ES_VC_TOP_OF_STACK
> + mov eax, ADDR_OF(Idtr)
> + lidt [cs:eax]
> +
> ; Check if we have a valid (0x8000_001F) CPUID leaf
> + ; CPUID raises a #VC exception if running as an SEV-ES guest
> mov eax, 0x80000000
> cpuid
>
> @@ -53,8 +98,8 @@ CheckSevFeature:
> jl NoSev
>
> ; Check for memory encryption feature:
> - ; CPUID Fn8000_001F[EAX] - Bit 1
> - ;
> + ; CPUID Fn8000_001F[EAX] - Bit 1
> + ; CPUID raises a #VC exception if running as an SEV-ES guest
> mov eax, 0x8000001f
> cpuid
> bt eax, 1
> @@ -78,6 +123,15 @@ NoSev:
> xor eax, eax
>
> SevExit:
> + ;
> + ; Clear exception handlers and stack
> + ;
> + push eax
> + mov eax, ADDR_OF(IdtrClear)
> + lidt [cs:eax]
> + pop eax
> + mov esp, 0
> +
> OneTimeCallRet CheckSevFeature
>
> ; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature
> @@ -222,3 +276,218 @@ SetCr3:
> mov cr3, eax
>
> OneTimeCallRet SetCr3ForPageTables64
> +
> +;
> +; Start of #VC exception handling routines
> +;
> +
> +SevEsIdtNotCpuid:
> + ;
> + ; Use VMGEXIT to request termination.
> + ; 1 - #VC was not for CPUID
> + ;
> + mov eax, 1
> + jmp SevEsIdtTerminate
> +
> +SevEsIdtNoCpuidResponse:
> + ;
> + ; Use VMGEXIT to request termination.
> + ; 2 - GHCB_CPUID_RESPONSE not received
> + ;
> + mov eax, 2
> +
> +SevEsIdtTerminate:
> + ;
> + ; Use VMGEXIT to request termination. At this point the reason code is
> + ; located in EAX, so shift it left 16 bits to the proper location.
> + ;
> + ; EAX[11:0] => 0x100 - request termination
> + ; EAX[15:12] => 0x1 - OVMF
> + ; EAX[23:16] => 0xXX - REASON CODE
> + ;
> + shl eax, 16
> + or eax, 0x1100
> + xor edx, edx
> + mov ecx, 0xc0010130
> + wrmsr
> + ;
> + ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
> + ; mode, so work around this by temporarily switching to 64-bit mode.
> + ;
> +BITS 64
> + rep vmmcall
> +BITS 32
> +
> + ;
> + ; We shouldn't come back from the VMGEXIT, but if we do, just loop.
> + ;
> +SevEsIdtHlt:
> + hlt
> + jmp SevEsIdtHlt
> + iret
> +
> + ;
> + ; Total stack usage for the #VC handler is 44 bytes:
> + ; - 12 bytes for the exception IRET (after popping error code)
> + ; - 32 bytes for the local variables.
> + ;
> +SevEsIdtVmmComm:
> + ;
> + ; If we're here, then we are an SEV-ES guest and this
> + ; was triggered by a CPUID instruction
> + ;
> + ; Set the first byte of the workarea to one to communicate to the SEC
> + ; phase that SEV-ES is enabled.
> + mov byte[SEV_ES_WORK_AREA], 1
> +
> + pop ecx ; Error code
> + cmp ecx, 0x72 ; Be sure it was CPUID
> + jne SevEsIdtNotCpuid
> +
> + ; Set up local variable room on the stack
> + ; CPUID function : + 28
> + ; CPUID request register : + 24
> + ; GHCB MSR (EAX) : + 20
> + ; GHCB MSR (EDX) : + 16
> + ; CPUID result (EDX) : + 12
> + ; CPUID result (ECX) : + 8
> + ; CPUID result (EBX) : + 4
> + ; CPUID result (EAX) : + 0
> + sub esp, VC_VARIABLE_SIZE
> +
> + ; Save the CPUID function being requested
> + mov [esp + VC_CPUID_FUNCTION], eax
> +
> + ; The GHCB CPUID protocol uses the following mapping to request
> + ; a specific register:
> + ; 0 => EAX, 1 => EBX, 2 => ECX, 3 => EDX
> + ;
> + ; Set EAX as the first register to request. This will also be used as a
> + ; loop variable to request all register values (EAX to EDX).
> + xor eax, eax
> + mov [esp + VC_CPUID_REQUEST_REGISTER], eax
> +
> + ; Save current GHCB MSR value
> + mov ecx, 0xc0010130
> + rdmsr
> + mov [esp + VC_GHCB_MSR_EAX], eax
> + mov [esp + VC_GHCB_MSR_EDX], edx
> +
> +NextReg:
> + ;
> + ; Setup GHCB MSR
> + ; GHCB_MSR[63:32] = CPUID function
> + ; GHCB_MSR[31:30] = CPUID register
> + ; GHCB_MSR[11:0] = CPUID request protocol
> + ;
> + mov eax, [esp + VC_CPUID_REQUEST_REGISTER]
> + cmp eax, 4
> + jge VmmDone
> +
> + shl eax, GHCB_CPUID_REGISTER_SHIFT
> + or eax, GHCB_CPUID_REQUEST
> + mov edx, [esp + VC_CPUID_FUNCTION]
> + mov ecx, 0xc0010130
> + wrmsr
> +
> + ;
> + ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
> + ; mode, so work around this by temporarily switching to 64-bit mode.
> + ;
> +BITS 64
> + rep vmmcall
> +BITS 32
> +
> + ;
> + ; Read GHCB MSR
> + ; GHCB_MSR[63:32] = CPUID register value
> + ; GHCB_MSR[31:30] = CPUID register
> + ; GHCB_MSR[11:0] = CPUID response protocol
> + ;
> + mov ecx, 0xc0010130
> + rdmsr
> + mov ecx, eax
> + and ecx, 0xfff
> + cmp ecx, GHCB_CPUID_RESPONSE
> + jne SevEsIdtNoCpuidResponse
> +
> + ; Save returned value
> + shr eax, GHCB_CPUID_REGISTER_SHIFT
> + mov [esp + eax * 4], edx
> +
> + ; Next register
> + inc word [esp + VC_CPUID_REQUEST_REGISTER]
> +
> + jmp NextReg
> +
> +VmmDone:
> + ;
> + ; At this point we have all CPUID register values. Restore the GHCB MSR,
> + ; set the return register values and return.
> + ;
> + mov eax, [esp + VC_GHCB_MSR_EAX]
> + mov edx, [esp + VC_GHCB_MSR_EDX]
> + mov ecx, 0xc0010130
> + wrmsr
> +
> + mov eax, [esp + VC_CPUID_RESULT_EAX]
> + mov ebx, [esp + VC_CPUID_RESULT_EBX]
> + mov ecx, [esp + VC_CPUID_RESULT_ECX]
> + mov edx, [esp + VC_CPUID_RESULT_EDX]
> +
> + add esp, VC_VARIABLE_SIZE
> +
> + ; Update the EIP value to skip over the now handled CPUID instruction
> + ; (the CPUID instruction has a length of 2)
> + add word [esp], CPUID_INSN_LEN
> + iret
> +
> +ALIGN 2
> +
> +Idtr:
> + dw IDT_END - IDT_BASE - 1 ; Limit
> + dd ADDR_OF(IDT_BASE) ; Base
> +
> +IdtrClear:
> + dw 0 ; Limit
> + dd 0 ; Base
> +
> +ALIGN 16
> +
> +;
> +; The Interrupt Descriptor Table (IDT)
> +; This will be used to determine if SEV-ES is enabled. Upon execution
> +; of the CPUID instruction, a VMM Communication Exception will occur.
> +; This will tell us if SEV-ES is enabled. We can use the current value
> +; of the GHCB MSR to determine the SEV attributes.
> +;
> +IDT_BASE:
> +;
> +; Vectors 0 - 28 (No handlers)
> +;
> +%rep 29
> + dw 0 ; Offset low bits 15..0
> + dw 0x10 ; Selector
> + db 0 ; Reserved
> + db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
> + dw 0 ; Offset high bits 31..16
> +%endrep
> +;
> +; Vector 29 (VMM Communication Exception)
> +;
> + dw (ADDR_OF(SevEsIdtVmmComm) & 0xffff) ; Offset low bits 15..0
> + dw 0x10 ; Selector
> + db 0 ; Reserved
> + db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
> + dw (ADDR_OF(SevEsIdtVmmComm) >> 16) ; Offset high bits 31..16
> +;
> +; Vectors 30 - 31 (No handlers)
> +;
> +%rep 2
> + dw 0 ; Offset low bits 15..0
> + dw 0x10 ; Selector
> + db 0 ; Reserved
> + db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
> + dw 0 ; Offset high bits 31..16
> +%endrep
> +IDT_END:
> diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
> index c25932513b80..97e36ef591ab 100644
> --- a/OvmfPkg/ResetVector/ResetVector.nasmb
> +++ b/OvmfPkg/ResetVector/ResetVector.nasmb
> @@ -72,6 +72,8 @@
> %define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
> %define GHCB_BASE (FixedPcdGet32 (PcdSecGhcbBase))
> %define GHCB_SIZE (FixedPcdGet32 (PcdSecGhcbSize))
> + %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
> + %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
> %include "Ia32/PageTables64.asm"
> %endif
>
>
next prev parent reply other threads:[~2020-02-06 8:51 UTC|newest]
Thread overview: 56+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-02-04 23:01 [PATCH v4 00/40] SEV-ES guest support Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 01/40] MdePkg: Create PCDs to be used in support of SEV-ES Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 02/40] MdePkg: Add the MSR definition for the GHCB register Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 03/40] MdePkg: Add a structure definition for the GHCB Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 04/40] MdeModulePkg/DxeIplPeim: Support GHCB pages when creating page tables Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 05/40] MdePkg/BaseLib: Add support for the XGETBV instruction Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 06/40] MdePkg/BaseLib: Add support for the VMGEXIT instruction Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 07/40] UefiCpuPkg: Implement library support for VMGEXIT Lendacky, Thomas
2020-02-14 0:42 ` [edk2-devel] " Dong, Eric
2020-02-14 17:56 ` Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 08/40] UefiCpuPkg/CpuExceptionHandler: Add base support for the #VC exception Lendacky, Thomas
2020-02-06 8:03 ` [edk2-devel] " Laszlo Ersek
2020-02-07 15:07 ` Lendacky, Thomas
2020-02-10 14:09 ` Laszlo Ersek
2020-02-10 18:13 ` Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 09/40] UefiCpuPkg/CpuExceptionHandler: Add support for IOIO_PROT NAE events Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 10/40] UefiCpuPkg/CpuExceptionHandler: Support string IO " Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 11/40] UefiCpuPkg/CpuExceptionHandler: Add support for CPUID " Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 12/40] UefiCpuPkg/CpuExceptionHandler: Add support for MSR_PROT " Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 13/40] UefiCpuPkg/CpuExceptionHandler: Add support for NPF NAE events (MMIO) Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 14/40] UefiCpuPkg/CpuExceptionHandler: Add support for WBINVD NAE events Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 15/40] UefiCpuPkg/CpuExceptionHandler: Add support for RDTSC " Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 16/40] UefiCpuPkg/CpuExceptionHandler: Add support for RDPMC " Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 17/40] UefiCpuPkg/CpuExceptionHandler: Add support for INVD " Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 18/40] UefiCpuPkg/CpuExceptionHandler: Add support for VMMCALL " Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 19/40] UefiCpuPkg/CpuExceptionHandler: Add support for RDTSCP " Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 20/40] UefiCpuPkg/CpuExceptionHandler: Add support for MONITOR/MONITORX " Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 21/40] UefiCpuPkg/CpuExceptionHandler: Add support for MWAIT/MWAITX " Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 22/40] UefiCpuPkg/CpuExceptionHandler: Add support for DR7 Read/Write " Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 23/40] OvmfPkg/MemEncryptSevLib: Add an SEV-ES guest indicator function Lendacky, Thomas
2020-02-06 8:21 ` [edk2-devel] " Laszlo Ersek
2020-02-07 15:29 ` Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 24/40] OvmfPkg: Add support to perform SEV-ES initialization Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 25/40] OvmfPkg: Create a GHCB page for use during Sec phase Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 26/40] OvmfPkg/PlatformPei: Reserve GHCB-related areas if S3 is supported Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 27/40] OvmfPkg: Create GHCB pages for use during Pei and Dxe phase Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 28/40] OvmfPkg/PlatformPei: Move early GDT into ram when SEV-ES is enabled Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 29/40] UefiCpuPkg: Create an SEV-ES workarea PCD Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 30/40] OvmfPkg: Reserve a page in memory for the SEV-ES usage Lendacky, Thomas
2020-02-06 8:43 ` [edk2-devel] " Laszlo Ersek
2020-02-04 23:01 ` [PATCH v4 31/40] OvmfPkg/ResetVector: Add support for a 32-bit SEV check Lendacky, Thomas
2020-02-06 8:51 ` Laszlo Ersek [this message]
2020-02-04 23:01 ` [PATCH v4 32/40] OvmfPkg/Sec: Add #VC exception handling for Sec phase Lendacky, Thomas
2020-02-06 9:03 ` [edk2-devel] " Laszlo Ersek
2020-02-04 23:01 ` [PATCH v4 33/40] OvmfPkg/Sec: Enable cache early to speed up booting Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 34/40] OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Bypass flash detection with SEV-ES is enabled Lendacky, Thomas
2020-02-06 9:16 ` [edk2-devel] " Laszlo Ersek
2020-02-04 23:01 ` [PATCH v4 35/40] UefiCpuPkg: Add a 16-bit protected mode code segment descriptor Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 36/40] UefiCpuPkg/MpInitLib: Add a CPU MP data flag to indicate if SEV-ES is enabled Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 37/40] UefiCpuPkg: Allow AP booting under SEV-ES Lendacky, Thomas
2020-02-06 9:35 ` [edk2-devel] " Laszlo Ersek
2020-02-07 15:51 ` Lendacky, Thomas
2020-02-04 23:01 ` [PATCH v4 38/40] OvmfPkg: Use the SEV-ES work area for the SEV-ES AP reset vector Lendacky, Thomas
2020-02-06 9:29 ` [edk2-devel] " Laszlo Ersek
2020-02-04 23:01 ` [PATCH v4 39/40] OvmfPkg: Move the GHCB allocations into reserved memory Lendacky, Thomas
2020-02-05 13:56 ` [PATCH v4 40/40] UefiCpuPkg/MpInitLib: Prepare SEV-ES guest APs for OS use Lendacky, Thomas
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-list 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=9c0cbb02-d9f1-4686-0fe8-37d8bbc10b77@redhat.com \
--to=devel@edk2.groups.io \
/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