From: "Kuo, Ted" <ted.kuo@intel.com>
To: devel@edk2.groups.io
Cc: Chasel Chiu <chasel.chiu@intel.com>,
Nate DeSimone <nathaniel.l.desimone@intel.com>,
Star Zeng <star.zeng@intel.com>,
Ashraf Ali S <ashraf.ali.s@intel.com>
Subject: [edk2-devel][PATCH v2 8/8] IntelFsp2WrapperPkg: SecFspWrapperPlatformSecLibSample support for X64
Date: Mon, 4 Apr 2022 14:23:11 +0800 [thread overview]
Message-ID: <db418e0c452aa36bb6b51d72f1773166cbbe9459.1649053236.git.ted.kuo@intel.com> (raw)
In-Reply-To: <cover.1649053236.git.ted.kuo@intel.com>
In-Reply-To: <cover.1649053236.git.ted.kuo@intel.com>
REF:https://bugzilla.tianocore.org/show_bug.cgi?id=3893
1.Added SecFspWrapperPlatformSecLibSample support for X64.
2.Adopted FSPT_ARCH2_UPD in SecFspWrapperPlatformSecLibSample.
3.Moved Fsp.h up one level to be shared across IA32 and X64.
Cc: Chasel Chiu <chasel.chiu@intel.com>
Cc: Nate DeSimone <nathaniel.l.desimone@intel.com>
Cc: Star Zeng <star.zeng@intel.com>
Cc: Ashraf Ali S <ashraf.ali.s@intel.com>
Signed-off-by: Ted Kuo <ted.kuo@intel.com>
---
.../{Ia32 => }/Fsp.h | 0
.../Ia32/Stack.nasm | 6 +-
.../SecFspWrapperPlatformSecLibSample.inf | 7 +-
.../SecRamInitData.c | 32 ++--
.../X64/PeiCoreEntry.nasm | 149 ++++++++++++++++++
.../X64/SecEntry.nasm | 171 +++++++++++++++++++++
.../X64/Stack.nasm | 73 +++++++++
7 files changed, 415 insertions(+), 23 deletions(-)
rename IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/{Ia32 => }/Fsp.h (100%)
create mode 100644 IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/X64/PeiCoreEntry.nasm
create mode 100644 IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/X64/SecEntry.nasm
create mode 100644 IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/X64/Stack.nasm
diff --git a/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/Ia32/Fsp.h b/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/Fsp.h
similarity index 100%
rename from IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/Ia32/Fsp.h
rename to IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/Fsp.h
diff --git a/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/Ia32/Stack.nasm b/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/Ia32/Stack.nasm
index d7394cf286..65e9c2e895 100644
--- a/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/Ia32/Stack.nasm
+++ b/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/Ia32/Stack.nasm
@@ -22,7 +22,7 @@
global ASM_PFX(SecSwitchStack)
ASM_PFX(SecSwitchStack):
;
- ; Save three register: eax, ebx, ecx
+ ; Save four register: eax, ebx, ecx, edx
;
push eax
push ebx
@@ -55,7 +55,7 @@ ASM_PFX(SecSwitchStack):
mov dword [eax + 12], edx
mov edx, dword [esp + 16] ; Update this function's return address into permanent memory
mov dword [eax + 16], edx
- mov esp, eax ; From now, esp is pointed to permanent memory
+ mov esp, eax ; From now, esp is pointed to permanent memory
;
; Fixup the ebp point to permanent memory
@@ -63,7 +63,7 @@ ASM_PFX(SecSwitchStack):
mov eax, ebp
sub eax, ebx
add eax, ecx
- mov ebp, eax ; From now, ebp is pointed to permanent memory
+ mov ebp, eax ; From now, ebp is pointed to permanent memory
pop edx
pop ecx
diff --git a/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/SecFspWrapperPlatformSecLibSample.inf b/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/SecFspWrapperPlatformSecLibSample.inf
index 027b127724..7aa4297bcc 100644
--- a/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/SecFspWrapperPlatformSecLibSample.inf
+++ b/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/SecFspWrapperPlatformSecLibSample.inf
@@ -39,13 +39,18 @@
SecGetPerformance.c
SecTempRamDone.c
PlatformInit.c
+ Fsp.h
[Sources.IA32]
- Ia32/Fsp.h
Ia32/SecEntry.nasm
Ia32/PeiCoreEntry.nasm
Ia32/Stack.nasm
+[Sources.X64]
+ X64/SecEntry.nasm
+ X64/PeiCoreEntry.nasm
+ X64/Stack.nasm
+
################################################################################
#
# Package Dependency Section - list of Package files that are required for
diff --git a/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/SecRamInitData.c b/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/SecRamInitData.c
index 03616cb418..4464abaca0 100644
--- a/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/SecRamInitData.c
+++ b/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/SecRamInitData.c
@@ -9,20 +9,14 @@
#include <Library/PcdLib.h>
#include <FspEas.h>
-typedef struct {
- UINT32 MicrocodeRegionBase;
- UINT32 MicrocodeRegionSize;
- UINT32 CodeRegionBase;
- UINT32 CodeRegionSize;
-} FSPT_CORE_UPD;
-
typedef struct {
FSP_UPD_HEADER FspUpdHeader;
//
- // If platform does not support FSP spec 2.2 remove FSPT_ARCH_UPD structure.
+ // If FSP spec version < 2.2, remove FSPT_ARCH_UPD structure.
+ // Else If FSP spec version >= 2.2 and FSP spec version < 2.4, use FSPT_ARCH_UPD structure.
+ // Else, use FSPT_ARCH2_UPD structure.
//
- FSPT_ARCH_UPD FsptArchUpd;
- FSPT_CORE_UPD FsptCoreUpd;
+ FSPT_ARCH2_UPD FsptArchUpd;
} FSPT_UPD_CORE_DATA;
GLOBAL_REMOVE_IF_UNREFERENCED CONST FSPT_UPD_CORE_DATA FsptUpdDataPtr = {
@@ -36,24 +30,24 @@ GLOBAL_REMOVE_IF_UNREFERENCED CONST FSPT_UPD_CORE_DATA FsptUpdDataPtr = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
},
//
- // If platform does not support FSP spec 2.2 remove FSPT_ARCH_UPD structure.
+ // If FSP spec version < 2.2, remove FSPT_ARCH_UPD structure.
+ // Else If FSP spec version >= 2.2 and FSP spec version < 2.4, use FSPT_ARCH_UPD structure.
+ // Else, use FSPT_ARCH2_UPD structure.
//
{
- 0x01,
+ 0x02,
{
0x00, 0x00, 0x00
},
- 0x00000020,
+ 0x00000040,
0x00000000,
- {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- }
- },
- {
FixedPcdGet32 (PcdCpuMicrocodePatchAddress),
FixedPcdGet32 (PcdCpuMicrocodePatchRegionSize),
FixedPcdGet32 (PcdFlashCodeCacheAddress),
FixedPcdGet32 (PcdFlashCodeCacheSize),
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ }
}
};
diff --git a/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/X64/PeiCoreEntry.nasm b/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/X64/PeiCoreEntry.nasm
new file mode 100644
index 0000000000..0c0766acb8
--- /dev/null
+++ b/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/X64/PeiCoreEntry.nasm
@@ -0,0 +1,149 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+; Module Name:
+;
+; PeiCoreEntry.nasm
+;
+; Abstract:
+;
+; Find and call SecStartup
+;
+;------------------------------------------------------------------------------
+
+SECTION .text
+
+%include "PushPopRegsNasm.inc"
+
+extern ASM_PFX(SecStartup)
+extern ASM_PFX(PlatformInit)
+
+;
+; args 1:XMM, 2:REG, 3:IDX
+;
+%macro LXMMN 3
+ pextrq %2, %1, (%3 & 3)
+ %endmacro
+
+;
+; args 1:YMM, 2:XMM, 3:IDX (0 - lower 128bits, 1 - upper 128bits)
+;
+%macro LYMMN 3
+ vextractf128 %2, %1, %3
+ %endmacro
+
+%macro LOAD_TS 1
+ LYMMN ymm6, xmm5, 1
+ LXMMN xmm5, %1, 1
+ %endmacro
+
+global ASM_PFX(CallPeiCoreEntryPoint)
+ASM_PFX(CallPeiCoreEntryPoint):
+ ;
+ ; Per X64 calling convention, make sure RSP is 16-byte aligned.
+ ;
+ mov rax, rsp
+ and rax, 0fh
+ sub rsp, rax
+
+ ;
+ ; Platform init
+ ;
+ PUSHA_64
+ sub rsp, 20h
+ call ASM_PFX(PlatformInit)
+ add rsp, 20h
+ POPA_64
+
+ ;
+ ; Set stack top pointer
+ ;
+ mov rsp, r8
+
+ ;
+ ; Push the hob list pointer
+ ;
+ push rcx
+
+ ;
+ ; RBP holds start of BFV passed from Vtf0. Save it to r10.
+ ;
+ mov r10, rbp
+
+ ;
+ ; Save the value
+ ; RDX: start of range
+ ; r8: end of range
+ ;
+ mov rbp, rsp
+ push rdx
+ push r8
+ mov r14, rdx
+ mov r15, r8
+
+ ;
+ ; Push processor count to stack first, then BIST status (AP then BSP)
+ ;
+ mov eax, 1
+ cpuid
+ shr ebx, 16
+ and ebx, 0000000FFh
+ cmp bl, 1
+ jae PushProcessorCount
+
+ ;
+ ; Some processors report 0 logical processors. Effectively 0 = 1.
+ ; So we fix up the processor count
+ ;
+ inc ebx
+
+PushProcessorCount:
+ sub rsp, 4
+ mov rdi, rsp
+ mov DWORD [rdi], ebx
+
+ ;
+ ; We need to implement a long-term solution for BIST capture. For now, we just copy BSP BIST
+ ; for all processor threads
+ ;
+ xor ecx, ecx
+ mov cl, bl
+PushBist:
+ sub rsp, 4
+ mov rdi, rsp
+ movd eax, mm0
+ mov DWORD [rdi], eax
+ loop PushBist
+
+ ; Save Time-Stamp Counter
+ LOAD_TS rax
+ push rax
+
+ ;
+ ; Pass entry point of the PEI core
+ ;
+ mov rdi, 0FFFFFFE0h
+ mov edi, DWORD [rdi]
+ mov r9, rdi
+
+ ;
+ ; Pass BFV into the PEI Core
+ ;
+ mov r8, r10
+
+ ;
+ ; Pass stack size into the PEI Core
+ ;
+ mov rcx, r15 ; Start of TempRam
+ mov rdx, r14 ; End of TempRam
+
+ sub rcx, rdx ; Size of TempRam
+
+ ;
+ ; Pass Control into the PEI Core
+ ;
+ sub rsp, 20h
+ call ASM_PFX(SecStartup)
+
diff --git a/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/X64/SecEntry.nasm b/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/X64/SecEntry.nasm
new file mode 100644
index 0000000000..dbbf63336e
--- /dev/null
+++ b/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/X64/SecEntry.nasm
@@ -0,0 +1,171 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+; Module Name:
+;
+; SecEntry.asm
+;
+; Abstract:
+;
+; This is the code that calls TempRamInit API from FSP binary and passes
+; control into PEI core.
+;
+;------------------------------------------------------------------------------
+
+#include "Fsp.h"
+
+IA32_CR4_OSFXSR equ 200h
+IA32_CR4_OSXMMEXCPT equ 400h
+IA32_CR0_MP equ 2h
+
+IA32_CPUID_SSE2 equ 02000000h
+IA32_CPUID_SSE2_B equ 26
+
+SECTION .text
+
+extern ASM_PFX(CallPeiCoreEntryPoint)
+extern ASM_PFX(FsptUpdDataPtr)
+
+; Pcds
+extern ASM_PFX(PcdGet32 (PcdFsptBaseAddress))
+
+;----------------------------------------------------------------------------
+;
+; Procedure: _ModuleEntryPoint
+;
+; Input: None
+;
+; Output: None
+;
+; Destroys: Assume all registers
+;
+; Description:
+;
+; Call TempRamInit API from FSP binary. After TempRamInit done, pass
+; control into PEI core.
+;
+; Return: None
+;
+; MMX Usage:
+; MM0 = BIST State
+;
+;----------------------------------------------------------------------------
+
+BITS 64
+align 16
+global ASM_PFX(ModuleEntryPoint)
+ASM_PFX(ModuleEntryPoint):
+ fninit ; clear any pending Floating point exceptions
+ ;
+ ; Store the BIST value in mm0
+ ;
+ movd mm0, eax
+
+ ; Find the fsp info header
+ mov rax, ASM_PFX(PcdGet32 (PcdFsptBaseAddress))
+ mov edi, [eax]
+
+ mov eax, dword [edi + FVH_SIGINATURE_OFFSET]
+ cmp eax, FVH_SIGINATURE_VALID_VALUE
+ jnz FspHeaderNotFound
+
+ xor eax, eax
+ mov ax, word [edi + FVH_EXTHEADER_OFFSET_OFFSET]
+ cmp ax, 0
+ jnz FspFvExtHeaderExist
+
+ xor eax, eax
+ mov ax, word [edi + FVH_HEADER_LENGTH_OFFSET] ; Bypass Fv Header
+ add edi, eax
+ jmp FspCheckFfsHeader
+
+FspFvExtHeaderExist:
+ add edi, eax
+ mov eax, dword [edi + FVH_EXTHEADER_SIZE_OFFSET] ; Bypass Ext Fv Header
+ add edi, eax
+
+ ; Round up to 8 byte alignment
+ mov eax, edi
+ and al, 07h
+ jz FspCheckFfsHeader
+
+ and edi, 0FFFFFFF8h
+ add edi, 08h
+
+FspCheckFfsHeader:
+ ; Check the ffs guid
+ mov eax, dword [edi]
+ cmp eax, FSP_HEADER_GUID_DWORD1
+ jnz FspHeaderNotFound
+
+ mov eax, dword [edi + 4]
+ cmp eax, FSP_HEADER_GUID_DWORD2
+ jnz FspHeaderNotFound
+
+ mov eax, dword [edi + 8]
+ cmp eax, FSP_HEADER_GUID_DWORD3
+ jnz FspHeaderNotFound
+
+ mov eax, dword [edi + 0Ch]
+ cmp eax, FSP_HEADER_GUID_DWORD4
+ jnz FspHeaderNotFound
+
+ add edi, FFS_HEADER_SIZE_VALUE ; Bypass the ffs header
+
+ ; Check the section type as raw section
+ mov al, byte [edi + SECTION_HEADER_TYPE_OFFSET]
+ cmp al, 019h
+ jnz FspHeaderNotFound
+
+ add edi, RAW_SECTION_HEADER_SIZE_VALUE ; Bypass the section header
+ jmp FspHeaderFound
+
+FspHeaderNotFound:
+ jmp $
+
+FspHeaderFound:
+ ; Get the fsp TempRamInit Api address
+ mov eax, dword [edi + FSP_HEADER_IMAGEBASE_OFFSET]
+ add eax, dword [edi + FSP_HEADER_TEMPRAMINIT_OFFSET]
+
+ ; Setup the hardcode stack
+ mov rsp, TempRamInitStack
+
+ ; Call the fsp TempRamInit Api
+ jmp rax
+
+TempRamInitDone:
+ cmp rax, 0800000000000000Eh ; Check if EFI_NOT_FOUND returned. Error code for Microcode Update not found.
+ je CallSecFspInit ; If microcode not found, don't hang, but continue.
+
+ cmp rax, 0 ; Check if EFI_SUCCESS returned.
+ jnz FspApiFailed
+
+ ; RDX: start of range
+ ; R8: end of range
+CallSecFspInit:
+
+ mov r8, rdx
+ mov rdx, rcx
+ xor ecx, ecx ; zero - no Hob List Yet
+ mov rsp, r8
+
+ ;
+ ; Per X64 calling convention, make sure RSP is 16-byte aligned.
+ ;
+ mov rax, rsp
+ and rax, 0fh
+ sub rsp, rax
+
+ call ASM_PFX(CallPeiCoreEntryPoint)
+
+FspApiFailed:
+ jmp $
+
+align 10h
+TempRamInitStack:
+ DQ TempRamInitDone
+ DQ ASM_PFX(FsptUpdDataPtr) ; TempRamInitParams
+
diff --git a/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/X64/Stack.nasm b/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/X64/Stack.nasm
new file mode 100644
index 0000000000..64e46ce953
--- /dev/null
+++ b/IntelFsp2WrapperPkg/Library/SecFspWrapperPlatformSecLibSample/X64/Stack.nasm
@@ -0,0 +1,73 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+; Abstract:
+;
+; Switch the stack from temporary memory to permanent memory.
+;
+;------------------------------------------------------------------------------
+
+ SECTION .text
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; SecSwitchStack (
+; UINT32 TemporaryMemoryBase,
+; UINT32 PermanentMemoryBase
+; );
+;------------------------------------------------------------------------------
+global ASM_PFX(SecSwitchStack)
+ASM_PFX(SecSwitchStack):
+ ;
+ ; Save four register: rax, rbx, rcx, rdx
+ ;
+ push rax
+ push rbx
+ push rcx
+ push rdx
+
+ ;
+ ; !!CAUTION!! this function address's is pushed into stack after
+ ; migration of whole temporary memory, so need save it to permanent
+ ; memory at first!
+ ;
+
+ mov rbx, rcx ; Save the first parameter
+ mov rcx, rdx ; Save the second parameter
+
+ ;
+ ; Save this function's return address into permanent memory at first.
+ ; Then, Fixup the esp point to permanent memory
+ ;
+ mov rax, rsp
+ sub rax, rbx
+ add rax, rcx
+ mov rdx, qword [rsp] ; copy pushed register's value to permanent memory
+ mov qword [rax], rdx
+ mov rdx, qword [rsp + 8]
+ mov qword [rax + 8], rdx
+ mov rdx, qword [rsp + 16]
+ mov qword [rax + 16], rdx
+ mov rdx, qword [rsp + 24]
+ mov qword [rax + 24], rdx
+ mov rdx, qword [rsp + 32] ; Update this function's return address into permanent memory
+ mov qword [rax + 32], rdx
+ mov rsp, rax ; From now, rsp is pointed to permanent memory
+
+ ;
+ ; Fixup the rbp point to permanent memory
+ ;
+ mov rax, rbp
+ sub rax, rbx
+ add rax, rcx
+ mov rbp, rax ; From now, rbp is pointed to permanent memory
+
+ pop rdx
+ pop rcx
+ pop rbx
+ pop rax
+ ret
+
--
2.16.2.windows.1
next prev parent reply other threads:[~2022-04-04 6:23 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-04-04 6:23 [edk2-devel][PATCH v2 0/8] Support PEI 64bit in IntelFsp2Pkg and IntelFsp2WrapperPkg Kuo, Ted
2022-04-04 6:23 ` [edk2-devel][PATCH v2 1/8] IntelFsp2Pkg: X64 compatible changes to support PEI in 64bit Kuo, Ted
2022-04-06 14:54 ` Chiu, Chasel
2022-04-04 6:23 ` [edk2-devel][PATCH v2 2/8] IntelFsp2Pkg: Add FSPx_ARCH2_UPD support for X64 Kuo, Ted
2022-04-06 14:39 ` Chiu, Chasel
2022-04-06 15:09 ` Chiu, Chasel
2022-04-04 6:23 ` [edk2-devel][PATCH v2 3/8] IntelFsp2Pkg: Update FSP_GLOBAL_DATA and FSP_PLAT_DATA " Kuo, Ted
2022-04-04 6:23 ` [edk2-devel][PATCH v2 4/8] IntelFsp2Pkg: FspSecCore support " Kuo, Ted
2022-04-04 6:23 ` [edk2-devel][PATCH v2 5/8] IntelFsp2Pkg: SecFspSecPlatformLibNull " Kuo, Ted
2022-04-04 6:23 ` [edk2-devel][PATCH v2 6/8] IntelFsp2WrapperPkg: Adopt FSPM_UPD_COMMON_FSP24 " Kuo, Ted
2022-04-04 6:23 ` [edk2-devel][PATCH v2 7/8] IntelFsp2WrapperPkg: BaseFspWrapperApiLib support " Kuo, Ted
2022-04-04 6:23 ` Kuo, Ted [this message]
2022-04-06 15:15 ` [edk2-devel][PATCH v2 8/8] IntelFsp2WrapperPkg: SecFspWrapperPlatformSecLibSample " Chiu, Chasel
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=db418e0c452aa36bb6b51d72f1773166cbbe9459.1649053236.git.ted.kuo@intel.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