From: Marcin Wojtas <mw@semihalf.com>
To: Leif Lindholm <leif.lindholm@linaro.org>
Cc: edk2-devel-01 <edk2-devel@lists.01.org>,
"Tian, Feng" <feng.tian@intel.com>,
"Kinney, Michael D" <michael.d.kinney@intel.com>,
"Gao, Liming" <liming.gao@intel.com>,
Ard Biesheuvel <ard.biesheuvel@linaro.org>,
nadavh@marvell.com, Neta Zur Hershkovits <neta@marvell.com>,
Kostya Porotchkin <kostap@marvell.com>,
Hua Jing <jinghua@marvell.com>,
semihalf-dabros-jan <jsd@semihalf.com>
Subject: Re: [PATCH 1/1] EmbeddedPkg: Implement NorFlashLib
Date: Tue, 31 Oct 2017 09:16:17 +0100 [thread overview]
Message-ID: <CAPv3WKcdm482+cvfQA9pPU5MWnBuq2WBSBH3tw4oeKp9WtTiKg@mail.gmail.com> (raw)
In-Reply-To: <20171031075818.l7rdpy6d6nue7l4r@bivouac.eciton.net>
Hi Leif,
2017-10-31 8:58 GMT+01:00 Leif Lindholm <leif.lindholm@linaro.org>:
> On Mon, Oct 30, 2017 at 09:30:25PM +0100, Marcin Wojtas wrote:
>> The SPI NOR flash drivers which base on ArmPlatformPkg's
>> NorFlashDxe usually make use of static declarations of the
>> flash instances with their type and parameters. As a result
>> it implies hardcoding the exact way flash handling, not to
>> mention the code does not look very nice. Much better solution
>> would be obtaining the flash ID and hence its description
>> in runtime.
>>
>> Because JEDEC compliant SPI NOR devices allow to obtain their ID
>> with READ_ID command (0x9f), implement a NorFlashLib that gives
>> access to the NOR flash data, such as name, page size, sector
>> (block) size and others, of more than 50 different models.
>> The new library user should pass an output array issuing
>> READ_ID command to the GetNorFlashInfo () routine - if the
>> match is found, an allocated (optionally for RT) pool with
>> the flash description will be returned.
>>
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Signed-off-by: Marcin Wojtas <mw@semihalf.com>
>> ---
>> EmbeddedPkg/EmbeddedPkg.dec | 1 +
>> EmbeddedPkg/Include/Library/NorFlashInfoLib.h | 84 ++++++++
>> EmbeddedPkg/Library/NorFlashInfoLib/NorFlashInfoLib.c | 225 ++++++++++++++++++++
>> EmbeddedPkg/Library/NorFlashInfoLib/NorFlashInfoLib.inf | 34 +++
>> 4 files changed, 344 insertions(+)
>>
>> diff --git a/EmbeddedPkg/EmbeddedPkg.dec b/EmbeddedPkg/EmbeddedPkg.dec
>> index 52482af..aa551ab 100644
>> --- a/EmbeddedPkg/EmbeddedPkg.dec
>> +++ b/EmbeddedPkg/EmbeddedPkg.dec
>> @@ -45,6 +45,7 @@
>> EblNetworkLib|Include/Library/EblNetworkLib.h
>> GdbSerialLib|Include/Library/GdbSerialLib.h
>> DebugAgentTimerLib|Include/Library/DebugAgentTimerLib.h
>> + NorFlashInfoLib|Include/Library/NorFlashInfoLib.h
>>
>> DtPlatformDtbLoaderLib|Include/Library/DtPlatformDtbLoaderLib.h
>>
>> diff --git a/EmbeddedPkg/Include/Library/NorFlashInfoLib.h b/EmbeddedPkg/Include/Library/NorFlashInfoLib.h
>> new file mode 100644
>> index 0000000..ae0e45f
>> --- /dev/null
>> +++ b/EmbeddedPkg/Include/Library/NorFlashInfoLib.h
>> @@ -0,0 +1,84 @@
>> +/** @file
>> +*
>> +* Copyright (c) 2017 Marvell International Ltd.
>> +*
>> +* This program and the accompanying materials
>> +* are licensed and made available under the terms and conditions of the BSD License
>> +* which accompanies this distribution. The full text of the license may be found at
>> +* http://opensource.org/licenses/bsd-license.php
>> +*
>> +* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
>> +* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
>> +*
>> +**/
>> +
>> +#ifndef __NOR_FLASH_ID_LIB_H__
>> +#define __NOR_FLASH_ID_LIB_H__
>> +
>> +#include <Uefi/UefiBaseType.h>
>> +
>> +#define NOR_FLASH_MAX_ID_LEN 6
>> +
>> +typedef struct {
>> + /* Device name */
>> + UINT16 *Name;
>> +
>> + /*
>> + * JEDEC ID
>> + */
>> + UINT8 Id[NOR_FLASH_MAX_ID_LEN];
>> + UINT8 IdLen;
>> +
>> + UINT16 PageSize;
>> +
>> + /*
>> + * Below parameters can be referred as BlockSize
>> + * and BlockCount, when treating the NorFlash as
>> + * block device.
>> + */
>> + UINT32 SectorSize;
>> + UINT32 SectorCount;
>> +
>> + UINT16 Flags;
>> +#define NF_ERASE_4K 1 << 0 /* Use 4096B erase blocks and CMD_ERASE_4K */
>> +#define NF_WRITE_FSR 1 << 1 /* Use flag status register for write */
>> +#define NF_4B_ADDR 1 << 2 /* Use 4B addressing */
>
> I think these should keep a NOR_FLASH_ prefix, like the rest of the file.
>
>> +} NOR_FLASH_INFO;
>> +
>> +/**
>> + Return a pool allocated copy of the NOR flash .
>> +
>> + @param[in] Id Pointer to an array with JEDEC ID obtained
>> + from the NOR flash with READ_ID command
>> + (0x9f)
>> + @param[in out] FlashInfo Pointer to NOR flash information structure
>> + @param[in] AllocateForRuntime A flag specifying a type of a copy pool
>> + allocation (TRUE for runtime, FALSE for
>> + normal)
>> +
>> + @retval EFI_SUCCESS Operation completed successfully
>> + @retval EFI_NOT_FOUND No matching entry in NOR ID table found
>> + @retval EFI_OUT_OF_RESOURCES No pool memory available
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +GetNorFlashInfo (
>
> NorFlashGetInfo?
>
>> + IN UINT8 *Id,
>> + IN OUT NOR_FLASH_INFO **FlashInfo,
>> + IN BOOLEAN AllocateForRuntime
>> + );
>> +
>> +/**
>> + Print NOR flash information basing on data stored in
>> + the NOR_FLASH_INFO structure.
>> +
>> + @param[in] FlashInfo Pointer to NOR flash information structure
>> +
>> +**/
>> +VOID
>> +EFIAPI
>> +PrintNorFlashInfo (
>
> NorFlashPrintInfo?
>
>> + IN NOR_FLASH_INFO *Info
>> + );
>> +#endif
>> diff --git a/EmbeddedPkg/Library/NorFlashInfoLib/NorFlashInfoLib.c b/EmbeddedPkg/Library/NorFlashInfoLib/NorFlashInfoLib.c
>> new file mode 100644
>> index 0000000..2185163
>> --- /dev/null
>> +++ b/EmbeddedPkg/Library/NorFlashInfoLib/NorFlashInfoLib.c
>> @@ -0,0 +1,225 @@
>> +/** @file
>> +*
>> +* Copyright (c) 2017 Marvell International Ltd.
>> +*
>> +* This program and the accompanying materials
>> +* are licensed and made available under the terms and conditions of the BSD License
>> +* which accompanies this distribution. The full text of the license may be found at
>> +* http://opensource.org/licenses/bsd-license.php
>> +*
>> +* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
>> +* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
>> +*
>> +**/
>> +
>> +#include <PiDxe.h>
>> +
>> +#include <Library/BaseLib.h>
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/DxeServicesLib.h>
>> +#include <Library/MemoryAllocationLib.h>
>> +#include <Library/NorFlashInfoLib.h>
>> +
>> +static CONST NOR_FLASH_INFO NorFlashIds[] = {
>> + /* ATMEL */
>> + {L"at45db011d", {0x1f, 0x22, 0x00}, 3, 256, 64 * 1024, 4, NF_ERASE_4K},
>> + {L"at45db021d", {0x1f, 0x23, 0x00}, 3, 256, 64 * 1024, 8, NF_ERASE_4K},
>> + {L"at45db041d", {0x1f, 0x24, 0x00}, 3, 256, 64 * 1024, 8, NF_ERASE_4K},
>> + {L"at45db081d", {0x1f, 0x25, 0x00}, 3, 256, 64 * 1024, 16, NF_ERASE_4K},
>> + {L"at45db161d", {0x1f, 0x26, 0x00}, 3, 256, 64 * 1024, 32, NF_ERASE_4K},
>> + {L"at45db321d", {0x1f, 0x27, 0x00}, 3, 256, 64 * 1024, 64, NF_ERASE_4K},
>> + {L"at45db641d", {0x1f, 0x28, 0x00}, 3, 256, 64 * 1024, 128, NF_ERASE_4K},
>> + {L"at25df321a", {0x1f, 0x47, 0x01}, 3, 256, 64 * 1024, 64, NF_ERASE_4K},
>> + {L"at25df321", {0x1f, 0x47, 0x00}, 3, 256, 64 * 1024, 64, NF_ERASE_4K},
>> + {L"at26df081a", {0x1f, 0x45, 0x01}, 3, 256, 64 * 1024, 16, NF_ERASE_4K},
>> + /* EON */
>> + {L"en25q32b", {0x1c, 0x30, 0x16}, 3, 256, 64 * 1024, 64, 0},
>> + {L"en25q64", {0x1c, 0x30, 0x17}, 3, 256, 64 * 1024, 128, NF_ERASE_4K},
>> + {L"en25q128b", {0x1c, 0x30, 0x18}, 3, 256, 64 * 1024, 256, 0},
>> + {L"en25s64", {0x1c, 0x38, 0x17}, 3, 256, 64 * 1024, 128, 0},
>> + /* GIGADEVICE */
>> + {L"gd25q64b", {0xc8, 0x40, 0x17}, 3, 256, 64 * 1024, 128, NF_ERASE_4K},
>> + {L"gd25lq32", {0xc8, 0x60, 0x16}, 3, 256, 64 * 1024, 64, NF_ERASE_4K},
>> + /* ISSI */
>> + {L"is25lp032", {0x9d, 0x60, 0x16}, 3, 256, 64 * 1024, 64, 0},
>> + {L"is25lp064", {0x9d, 0x60, 0x17}, 3, 256, 64 * 1024, 128, 0},
>> + {L"is25lp128", {0x9d, 0x60, 0x18}, 3, 256, 64 * 1024, 256, 0},
>> + /* MACRONIX */
>> + {L"mx25l2006e", {0xc2, 0x20, 0x12}, 3, 256, 64 * 1024, 4, 0},
>> + {L"mx25l4005", {0xc2, 0x20, 0x13}, 3, 256, 64 * 1024, 8, 0},
>> + {L"mx25l8005", {0xc2, 0x20, 0x14}, 3, 256, 64 * 1024, 16, 0},
>> + {L"mx25l1605d", {0xc2, 0x20, 0x15}, 3, 256, 64 * 1024, 32, 0},
>> + {L"mx25l3205d", {0xc2, 0x20, 0x16}, 3, 256, 64 * 1024, 64, 0},
>> + {L"mx25l6405d", {0xc2, 0x20, 0x17}, 3, 256, 64 * 1024, 128, 0},
>> + {L"mx25l12805", {0xc2, 0x20, 0x18}, 3, 256, 64 * 1024, 256, 0},
>> + {L"mx25l25635f", {0xc2, 0x20, 0x19}, 3, 256, 64 * 1024, 512, 0},
>> + {L"mx25l51235f", {0xc2, 0x20, 0x1a}, 3, 256, 64 * 1024, 1024, 0},
>> + {L"mx25l12855e", {0xc2, 0x26, 0x18}, 3, 256, 64 * 1024, 256, 0},
>> + {L"mx66u51235f", {0xc2, 0x25, 0x3a}, 3, 256, 64 * 1024, 1024, 0},
>> + {L"mx66l1g45g", {0xc2, 0x20, 0x1b}, 3, 256, 64 * 1024, 2048, 0},
>> + /* SPANSION */
>> + {L"s25fl008a", {0x01, 0x02, 0x13}, 3, 256, 64 * 1024, 16, 0},
>> + {L"s25fl016a", {0x01, 0x02, 0x14}, 3, 256, 64 * 1024, 32, 0},
>> + {L"s25fl032a", {0x01, 0x02, 0x15}, 3, 256, 64 * 1024, 64, 0},
>> + {L"s25fl064a", {0x01, 0x02, 0x16}, 3, 256, 64 * 1024, 128, 0},
>> + {L"s25fl116k", {0x01, 0x40, 0x15}, 3, 256, 64 * 1024, 128, 0},
>> + {L"s25fl164k", {0x01, 0x40, 0x17, 0x01, 0x40}, 5, 256, 64 * 1024, 128, 0},
>> + {L"s25fl128p_256k", {0x01, 0x20, 0x18, 0x03, 0x00}, 5, 256, 256 * 1024, 64, 0},
>> + {L"s25fl128p_64k", {0x01, 0x20, 0x18, 0x03, 0x01}, 5, 256, 64 * 1024, 256, 0},
>> + {L"s25fl032p", {0x01, 0x02, 0x15, 0x4d, 0x00}, 5, 256, 64 * 1024, 64, 0},
>> + {L"s25fl064p", {0x01, 0x02, 0x16, 0x4d, 0x00}, 5, 256, 64 * 1024, 128, 0},
>> + {L"s25fl128s_256k", {0x01, 0x20, 0x18, 0x4d, 0x00}, 5, 256, 256 * 1024, 64, 0},
>> + {L"s25fl128s_64k", {0x01, 0x20, 0x18, 0x4d, 0x01}, 5, 256, 64 * 1024, 256, 0},
>> + {L"s25fl256s_256k", {0x01, 0x02, 0x19, 0x4d, 0x00}, 5, 256, 256 * 1024, 128, 0},
>> + {L"s25fl256s_64k", {0x01, 0x02, 0x19, 0x4d, 0x01}, 5, 256, 64 * 1024, 512, 0},
>> + {L"s25fl512s_256k", {0x01, 0x02, 0x20, 0x4d, 0x00}, 5, 256, 256 * 1024, 256, 0},
>> + {L"s25fl512s_64k", {0x01, 0x02, 0x20, 0x4d, 0x01}, 5, 256, 64 * 1024, 1024, 0},
>> + {L"s25fl512s_512k", {0x01, 0x02, 0x20, 0x4f, 0x00}, 5, 256, 256 * 1024, 256, 0},
>> + /* STMICRO */
>> + {L"m25p10", {0x20, 0x20, 0x11}, 3, 256, 32 * 1024, 4, 0},
>> + {L"m25p20", {0x20, 0x20, 0x12}, 3, 256, 64 * 1024, 4, 0},
>> + {L"m25p40", {0x20, 0x20, 0x13}, 3, 256, 64 * 1024, 8, 0},
>> + {L"m25p80", {0x20, 0x20, 0x14}, 3, 256, 64 * 1024, 16, 0},
>> + {L"m25p16", {0x20, 0x20, 0x15}, 3, 256, 64 * 1024, 32, 0},
>> + {L"m25pE16", {0x20, 0x80, 0x15, 0x10, 0x00}, 5, 256, 64 * 1024, 32, 0},
>> + {L"m25pX16", {0x20, 0x71, 0x15, 0x10, 0x00}, 5, 256, 64 * 1024, 32, 0},
>> + {L"m25p32", {0x20, 0x20, 0x16}, 3, 256, 64 * 1024, 64, 0},
>> + {L"m25p64", {0x20, 0x20, 0x17}, 3, 256, 64 * 1024, 128, 0},
>> + {L"m25p128", {0x20, 0x20, 0x18}, 3, 256, 256 * 1024, 64, 0},
>> + {L"m25pX64", {0x20, 0x71, 0x17}, 3, 256, 64 * 1024, 128, NF_ERASE_4K},
>> + {L"n25q016a", {0x20, 0xbb, 0x15}, 3, 256, 64 * 1024, 32, NF_ERASE_4K},
>> + {L"n25q32", {0x20, 0xba, 0x16}, 3, 256, 64 * 1024, 64, NF_ERASE_4K},
>> + {L"n25q32a", {0x20, 0xbb, 0x16}, 3, 256, 64 * 1024, 64, NF_ERASE_4K},
>> + {L"n25q64", {0x20, 0xba, 0x17}, 3, 256, 64 * 1024, 128, NF_ERASE_4K},
>> + {L"n25q64a", {0x20, 0xbb, 0x17}, 3, 256, 64 * 1024, 128, NF_ERASE_4K},
>> + {L"n25q128", {0x20, 0xba, 0x18}, 3, 256, 64 * 1024, 256, 0},
>> + {L"n25q128a", {0x20, 0xbb, 0x18}, 3, 256, 64 * 1024, 256, 0},
>> + {L"n25q256", {0x20, 0xba, 0x19}, 3, 256, 64 * 1024, 512, NF_ERASE_4K},
>> + {L"n25q256a", {0x20, 0xbb, 0x19}, 3, 256, 64 * 1024, 512, NF_ERASE_4K},
>> + {L"n25q512", {0x20, 0xba, 0x20}, 3, 256, 64 * 1024, 1024, NF_WRITE_FSR | NF_ERASE_4K},
>> + {L"n25q512a", {0x20, 0xbb, 0x20}, 3, 256, 64 * 1024, 1024, NF_WRITE_FSR | NF_ERASE_4K},
>> + {L"n25q1024", {0x20, 0xba, 0x21}, 3, 256, 64 * 1024, 2048, NF_WRITE_FSR | NF_ERASE_4K},
>> + {L"n25q1024a", {0x20, 0xbb, 0x21}, 3, 256, 64 * 1024, 2048, NF_WRITE_FSR | NF_ERASE_4K},
>> + {L"mt25qu02g", {0x20, 0xbb, 0x22}, 3, 256, 64 * 1024, 4096, NF_WRITE_FSR | NF_ERASE_4K},
>> + {L"mt25ql02g", {0x20, 0xba, 0x22}, 3, 256, 64 * 1024, 4096, NF_WRITE_FSR | NF_ERASE_4K},
>> + /* SST */
>> + {L"sst25vf040b", {0xbf, 0x25, 0x8d}, 3, 256, 64 * 1024, 8, NF_ERASE_4K},
>> + {L"sst25vf080b", {0xbf, 0x25, 0x8e}, 3, 256, 64 * 1024, 16, NF_ERASE_4K},
>> + {L"sst25vf016b", {0xbf, 0x25, 0x41}, 3, 256, 64 * 1024, 32, NF_ERASE_4K},
>> + {L"sst25vf032b", {0xbf, 0x25, 0x4a}, 3, 256, 64 * 1024, 64, NF_ERASE_4K},
>> + {L"sst25vf064c", {0xbf, 0x25, 0x4b}, 3, 256, 64 * 1024, 128, NF_ERASE_4K},
>> + {L"sst25wf512", {0xbf, 0x25, 0x01}, 3, 256, 64 * 1024, 1, NF_ERASE_4K},
>> + {L"sst25wf010", {0xbf, 0x25, 0x02}, 3, 256, 64 * 1024, 2, NF_ERASE_4K},
>> + {L"sst25wf020", {0xbf, 0x25, 0x03}, 3, 256, 64 * 1024, 4, NF_ERASE_4K},
>> + {L"sst25wf040", {0xbf, 0x25, 0x04}, 3, 256, 64 * 1024, 8, NF_ERASE_4K},
>> + {L"sst25wf040b", {0x62, 0x16, 0x13}, 3, 256, 64 * 1024, 8, NF_ERASE_4K},
>> + {L"sst25wf080", {0xbf, 0x25, 0x05}, 3, 256, 64 * 1024, 16, NF_ERASE_4K},
>> + /* WINBOND */
>> + {L"w25p80", {0xef, 0x20, 0x14}, 3, 256, 64 * 1024, 16, 0},
>> + {L"w25p16", {0xef, 0x20, 0x15}, 3, 256, 64 * 1024, 32, 0},
>> + {L"w25p32", {0xef, 0x20, 0x16}, 3, 256, 64 * 1024, 64, 0},
>> + {L"w25x40", {0xef, 0x30, 0x13}, 3, 256, 64 * 1024, 8, NF_ERASE_4K},
>> + {L"w25x16", {0xef, 0x30, 0x15}, 3, 256, 64 * 1024, 32, NF_ERASE_4K},
>> + {L"w25x32", {0xef, 0x30, 0x16}, 3, 256, 64 * 1024, 64, NF_ERASE_4K},
>> + {L"w25x64", {0xef, 0x30, 0x17}, 3, 256, 64 * 1024, 128, NF_ERASE_4K},
>> + {L"w25q80bl", {0xef, 0x40, 0x14}, 3, 256, 64 * 1024, 16, NF_ERASE_4K},
>> + {L"w25q16cl", {0xef, 0x40, 0x15}, 3, 256, 64 * 1024, 32, NF_ERASE_4K},
>> + {L"w25q32bv", {0xef, 0x40, 0x16}, 3, 256, 64 * 1024, 64, NF_ERASE_4K},
>> + {L"w25q64cv", {0xef, 0x40, 0x17}, 3, 256, 64 * 1024, 128, NF_ERASE_4K},
>> + {L"w25q128bv", {0xef, 0x40, 0x18}, 3, 256, 64 * 1024, 256, NF_ERASE_4K},
>> + {L"w25q256", {0xef, 0x40, 0x19}, 3, 256, 64 * 1024, 512, NF_ERASE_4K},
>> + {L"w25q80bw", {0xef, 0x50, 0x14}, 3, 256, 64 * 1024, 16, NF_ERASE_4K},
>> + {L"w25q16dw", {0xef, 0x60, 0x15}, 3, 256, 64 * 1024, 32, NF_ERASE_4K},
>> + {L"w25q32dw", {0xef, 0x60, 0x16}, 3, 256, 64 * 1024, 64, NF_ERASE_4K},
>> + {L"w25q64dw", {0xef, 0x60, 0x17}, 3, 256, 64 * 1024, 128, NF_ERASE_4K},
>> + {L"w25q128fw", {0xef, 0x60, 0x18}, 3, 256, 64 * 1024, 256, NF_ERASE_4K},
>> + {}, /* Empty entry to terminate the list */
>> +};
>> +
>> +/**
>> + Return a pool allocated copy of the NOR flash .
>
> Well, a copy of the NOR flash information structure, not the flash contents.
> Extra space before period.
>
> /
Thanks for the review. Will submit v2 with all corrections (also with
typo fix in the commit log).
Best regards,
Marcin
next prev parent reply other threads:[~2017-10-31 8:12 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-10-30 20:30 [PATCH 1/1] EmbeddedPkg: Implement NorFlashLib Marcin Wojtas
2017-10-31 7:58 ` Leif Lindholm
2017-10-31 8:16 ` Marcin Wojtas [this message]
2017-11-01 5:57 ` Pankaj Bansal
2017-11-01 8:45 ` Marcin Wojtas
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=CAPv3WKcdm482+cvfQA9pPU5MWnBuq2WBSBH3tw4oeKp9WtTiKg@mail.gmail.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