public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Wu, Hao A" <hao.a.wu@intel.com>
To: "Kubacki, Michael A" <michael.a.kubacki@intel.com>,
	"devel@edk2.groups.io" <devel@edk2.groups.io>
Cc: "Bi, Dandan" <dandan.bi@intel.com>,
	Ard Biesheuvel <ard.biesheuvel@linaro.org>,
	"Dong, Eric" <eric.dong@intel.com>,
	Laszlo Ersek <lersek@redhat.com>,
	"Gao, Liming" <liming.gao@intel.com>,
	"Kinney, Michael D" <michael.d.kinney@intel.com>,
	"Ni, Ray" <ray.ni@intel.com>,
	"Wang, Jian J" <jian.j.wang@intel.com>,
	"Yao, Jiewen" <jiewen.yao@intel.com>
Subject: Re: [PATCH V4 05/10] MdeModulePkg/Variable: Add a file for NV variable functions
Date: Wed, 16 Oct 2019 07:55:54 +0000	[thread overview]
Message-ID: <B80AF82E9BFB8E4FBD8C89DA810C6A093C94802B@SHSMSX104.ccr.corp.intel.com> (raw)
In-Reply-To: <20191014233001.33024-6-michael.a.kubacki@intel.com>

> -----Original Message-----
> From: Kubacki, Michael A
> Sent: Tuesday, October 15, 2019 7:30 AM
> To: devel@edk2.groups.io
> Cc: Bi, Dandan; Ard Biesheuvel; Dong, Eric; Laszlo Ersek; Gao, Liming; Kinney,
> Michael D; Ni, Ray; Wang, Jian J; Wu, Hao A; Yao, Jiewen
> Subject: [PATCH V4 05/10] MdeModulePkg/Variable: Add a file for NV
> variable functions
> 
> This change adds a dedicated file for variable operations specific
> to non-volatile variables. This decreases the overall length of the
> relatively large Variable.c file.


Since all my previous comments have been addressed, the patch looks good to me:

Reviewed-by: Hao A Wu <hao.a.wu@intel.com>

Best Regards,
Hao Wu


> 
> Cc: Dandan Bi <dandan.bi@intel.com>
> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Liming Gao <liming.gao@intel.com>
> Cc: Michael D Kinney <michael.d.kinney@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Jian J Wang <jian.j.wang@intel.com>
> Cc: Hao A Wu <hao.a.wu@intel.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Signed-off-by: Michael Kubacki <michael.a.kubacki@intel.com>
> ---
>  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
> |   2 +
>  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf          |   2
> +
> 
> MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf
> |   2 +
>  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.h    |
> 67 ++++
>  MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c               | 317 +--
> ----------------
>  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c    |
> 334 ++++++++++++++++++++
>  6 files changed, 408 insertions(+), 316 deletions(-)
> 
> diff --git
> a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
> b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
> index c35e5fe787..08a5490787 100644
> ---
> a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
> +++
> b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
> @@ -36,6 +36,8 @@
>    Variable.c
>    VariableDxe.c
>    Variable.h
> +  VariableNonVolatile.c
> +  VariableNonVolatile.h
>    VariableParsing.c
>    VariableParsing.h
>    PrivilegePolymorphic.h
> diff --git
> a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf
> b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf
> index 626738b9c7..6dc2721b81 100644
> --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf
> +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf
> @@ -45,6 +45,8 @@
>    Variable.c
>    VariableTraditionalMm.c
>    VariableSmm.c
> +  VariableNonVolatile.c
> +  VariableNonVolatile.h
>    VariableParsing.c
>    VariableParsing.h
>    VarCheck.c
> diff --git
> a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.i
> nf
> b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.
> inf
> index ff714b193a..f8a3742959 100644
> ---
> a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.i
> nf
> +++
> b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.
> inf
> @@ -45,6 +45,8 @@
>    Variable.c
>    VariableSmm.c
>    VariableStandaloneMm.c
> +  VariableNonVolatile.c
> +  VariableNonVolatile.h
>    VariableParsing.c
>    VariableParsing.h
>    VarCheck.c
> diff --git
> a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.h
> b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.h
> new file mode 100644
> index 0000000000..43653f27e6
> --- /dev/null
> +++
> b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.h
> @@ -0,0 +1,67 @@
> +/** @file
> +  Common variable non-volatile store routines.
> +
> +Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef _VARIABLE_NON_VOLATILE_H_
> +#define _VARIABLE_NON_VOLATILE_H_
> +
> +#include "Variable.h"
> +
> +/**
> +  Get non-volatile maximum variable size.
> +
> +  @return Non-volatile maximum variable size.
> +
> +**/
> +UINTN
> +GetNonVolatileMaxVariableSize (
> +  VOID
> +  );
> +
> +/**
> +  Init emulated non-volatile variable store.
> +
> +  @param[out] VariableStoreBase Output pointer to emulated non-volatile
> variable store base.
> +
> +  @retval EFI_SUCCESS           Function successfully executed.
> +  @retval EFI_OUT_OF_RESOURCES  Fail to allocate enough memory
> resource.
> +
> +**/
> +EFI_STATUS
> +InitEmuNonVolatileVariableStore (
> +  EFI_PHYSICAL_ADDRESS  *VariableStoreBase
> +  );
> +
> +/**
> +  Init real non-volatile variable store.
> +
> +  @param[out] VariableStoreBase Output pointer to real non-volatile
> variable store base.
> +
> +  @retval EFI_SUCCESS           Function successfully executed.
> +  @retval EFI_OUT_OF_RESOURCES  Fail to allocate enough memory
> resource.
> +  @retval EFI_VOLUME_CORRUPTED  Variable Store or Firmware Volume for
> Variable Store is corrupted.
> +
> +**/
> +EFI_STATUS
> +InitRealNonVolatileVariableStore (
> +  OUT EFI_PHYSICAL_ADDRESS              *VariableStoreBase
> +  );
> +
> +/**
> +  Init non-volatile variable store.
> +
> +  @retval EFI_SUCCESS           Function successfully executed.
> +  @retval EFI_OUT_OF_RESOURCES  Fail to allocate enough memory
> resource.
> +  @retval EFI_VOLUME_CORRUPTED  Variable Store or Firmware Volume for
> Variable Store is corrupted.
> +
> +**/
> +EFI_STATUS
> +InitNonVolatileVariableStore (
> +  VOID
> +  );
> +
> +#endif
> diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c
> b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c
> index 2e32905dfe..0bd2f22e1a 100644
> --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c
> +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c
> @@ -23,6 +23,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
>  **/
> 
>  #include "Variable.h"
> +#include "VariableNonVolatile.h"
>  #include "VariableParsing.h"
> 
>  VARIABLE_MODULE_GLOBAL  *mVariableModuleGlobal;
> @@ -3079,25 +3080,6 @@ ReclaimForOS(
>    }
>  }
> 
> -/**
> -  Get non-volatile maximum variable size.
> -
> -  @return Non-volatile maximum variable size.
> -
> -**/
> -UINTN
> -GetNonVolatileMaxVariableSize (
> -  VOID
> -  )
> -{
> -  if (PcdGet32 (PcdHwErrStorageSize) != 0) {
> -    return MAX (MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32
> (PcdMaxAuthVariableSize)),
> -                PcdGet32 (PcdMaxHardwareErrorVariableSize));
> -  } else {
> -    return MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32
> (PcdMaxAuthVariableSize));
> -  }
> -}
> -
>  /**
>    Get maximum variable size, covering both non-volatile and volatile variables.
> 
> @@ -3122,303 +3104,6 @@ GetMaxVariableSize (
>    return MaxVariableSize;
>  }
> 
> -/**
> -  Init real non-volatile variable store.
> -
> -  @param[out] VariableStoreBase Output pointer to real non-volatile
> variable store base.
> -
> -  @retval EFI_SUCCESS           Function successfully executed.
> -  @retval EFI_OUT_OF_RESOURCES  Fail to allocate enough memory
> resource.
> -  @retval EFI_VOLUME_CORRUPTED  Variable Store or Firmware Volume for
> Variable Store is corrupted.
> -
> -**/
> -EFI_STATUS
> -InitRealNonVolatileVariableStore (
> -  OUT EFI_PHYSICAL_ADDRESS              *VariableStoreBase
> -  )
> -{
> -  EFI_FIRMWARE_VOLUME_HEADER            *FvHeader;
> -  VARIABLE_STORE_HEADER                 *VariableStore;
> -  UINT32                                VariableStoreLength;
> -  EFI_HOB_GUID_TYPE                     *GuidHob;
> -  EFI_PHYSICAL_ADDRESS                  NvStorageBase;
> -  UINT8                                 *NvStorageData;
> -  UINT32                                NvStorageSize;
> -  FAULT_TOLERANT_WRITE_LAST_WRITE_DATA  *FtwLastWriteData;
> -  UINT32                                BackUpOffset;
> -  UINT32                                BackUpSize;
> -  UINT32                                HwErrStorageSize;
> -  UINT32                                MaxUserNvVariableSpaceSize;
> -  UINT32                                BoottimeReservedNvVariableSpaceSize;
> -  EFI_STATUS                            Status;
> -  VOID                                  *FtwProtocol;
> -
> -  mVariableModuleGlobal->FvbInstance = NULL;
> -
> -  //
> -  // Allocate runtime memory used for a memory copy of the FLASH region.
> -  // Keep the memory and the FLASH in sync as updates occur.
> -  //
> -  NvStorageSize = PcdGet32 (PcdFlashNvStorageVariableSize);
> -  NvStorageData = AllocateRuntimeZeroPool (NvStorageSize);
> -  if (NvStorageData == NULL) {
> -    return EFI_OUT_OF_RESOURCES;
> -  }
> -
> -  NvStorageBase = NV_STORAGE_VARIABLE_BASE;
> -  ASSERT (NvStorageBase != 0);
> -
> -  //
> -  // Copy NV storage data to the memory buffer.
> -  //
> -  CopyMem (NvStorageData, (UINT8 *) (UINTN) NvStorageBase,
> NvStorageSize);
> -
> -  Status = GetFtwProtocol ((VOID **)&FtwProtocol);
> -  //
> -  // If FTW protocol has been installed, no need to check FTW last write data
> hob.
> -  //
> -  if (EFI_ERROR (Status)) {
> -    //
> -    // Check the FTW last write data hob.
> -    //
> -    GuidHob = GetFirstGuidHob (&gEdkiiFaultTolerantWriteGuid);
> -    if (GuidHob != NULL) {
> -      FtwLastWriteData = (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA *)
> GET_GUID_HOB_DATA (GuidHob);
> -      if (FtwLastWriteData->TargetAddress == NvStorageBase) {
> -        DEBUG ((EFI_D_INFO, "Variable: NV storage is backed up in spare block:
> 0x%x\n", (UINTN) FtwLastWriteData->SpareAddress));
> -        //
> -        // Copy the backed up NV storage data to the memory buffer from
> spare block.
> -        //
> -        CopyMem (NvStorageData, (UINT8 *) (UINTN) (FtwLastWriteData-
> >SpareAddress), NvStorageSize);
> -      } else if ((FtwLastWriteData->TargetAddress > NvStorageBase) &&
> -                 (FtwLastWriteData->TargetAddress < (NvStorageBase +
> NvStorageSize))) {
> -        //
> -        // Flash NV storage from the Offset is backed up in spare block.
> -        //
> -        BackUpOffset = (UINT32) (FtwLastWriteData->TargetAddress -
> NvStorageBase);
> -        BackUpSize = NvStorageSize - BackUpOffset;
> -        DEBUG ((EFI_D_INFO, "Variable: High partial NV storage from offset: %x
> is backed up in spare block: 0x%x\n", BackUpOffset, (UINTN)
> FtwLastWriteData->SpareAddress));
> -        //
> -        // Copy the partial backed up NV storage data to the memory buffer
> from spare block.
> -        //
> -        CopyMem (NvStorageData + BackUpOffset, (UINT8 *) (UINTN)
> FtwLastWriteData->SpareAddress, BackUpSize);
> -      }
> -    }
> -  }
> -
> -  FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) NvStorageData;
> -
> -  //
> -  // Check if the Firmware Volume is not corrupted
> -  //
> -  if ((FvHeader->Signature != EFI_FVH_SIGNATURE) || (!CompareGuid
> (&gEfiSystemNvDataFvGuid, &FvHeader->FileSystemGuid))) {
> -    FreePool (NvStorageData);
> -    DEBUG ((EFI_D_ERROR, "Firmware Volume for Variable Store is
> corrupted\n"));
> -    return EFI_VOLUME_CORRUPTED;
> -  }
> -
> -  VariableStore = (VARIABLE_STORE_HEADER *) ((UINTN) FvHeader +
> FvHeader->HeaderLength);
> -  VariableStoreLength = NvStorageSize - FvHeader->HeaderLength;
> -  ASSERT (sizeof (VARIABLE_STORE_HEADER) <= VariableStoreLength);
> -  ASSERT (VariableStore->Size == VariableStoreLength);
> -
> -  //
> -  // Check if the Variable Store header is not corrupted
> -  //
> -  if (GetVariableStoreStatus (VariableStore) != EfiValid) {
> -    FreePool (NvStorageData);
> -    DEBUG((EFI_D_ERROR, "Variable Store header is corrupted\n"));
> -    return EFI_VOLUME_CORRUPTED;
> -  }
> -
> -  mNvFvHeaderCache = FvHeader;
> -
> -  *VariableStoreBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStore;
> -
> -  HwErrStorageSize = PcdGet32 (PcdHwErrStorageSize);
> -  MaxUserNvVariableSpaceSize = PcdGet32
> (PcdMaxUserNvVariableSpaceSize);
> -  BoottimeReservedNvVariableSpaceSize = PcdGet32
> (PcdBoottimeReservedNvVariableSpaceSize);
> -
> -  //
> -  // Note that in EdkII variable driver implementation, Hardware Error Record
> type variable
> -  // is stored with common variable in the same NV region. So the platform
> integrator should
> -  // ensure that the value of PcdHwErrStorageSize is less than the value of
> -  // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).
> -  //
> -  ASSERT (HwErrStorageSize < (VariableStoreLength - sizeof
> (VARIABLE_STORE_HEADER)));
> -  //
> -  // Ensure that the value of PcdMaxUserNvVariableSpaceSize is less than
> the value of
> -  // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32
> (PcdHwErrStorageSize).
> -  //
> -  ASSERT (MaxUserNvVariableSpaceSize < (VariableStoreLength - sizeof
> (VARIABLE_STORE_HEADER) - HwErrStorageSize));
> -  //
> -  // Ensure that the value of PcdBoottimeReservedNvVariableSpaceSize is
> less than the value of
> -  // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32
> (PcdHwErrStorageSize).
> -  //
> -  ASSERT (BoottimeReservedNvVariableSpaceSize < (VariableStoreLength -
> sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize));
> -
> -  mVariableModuleGlobal->CommonVariableSpace = ((UINTN)
> VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) -
> HwErrStorageSize);
> -  mVariableModuleGlobal->CommonMaxUserVariableSpace =
> ((MaxUserNvVariableSpaceSize != 0) ? MaxUserNvVariableSpaceSize :
> mVariableModuleGlobal->CommonVariableSpace);
> -  mVariableModuleGlobal->CommonRuntimeVariableSpace =
> mVariableModuleGlobal->CommonVariableSpace -
> BoottimeReservedNvVariableSpaceSize;
> -
> -  DEBUG ((EFI_D_INFO, "Variable driver common space: 0x%x 0x%x 0x%x\n",
> mVariableModuleGlobal->CommonVariableSpace, mVariableModuleGlobal-
> >CommonMaxUserVariableSpace, mVariableModuleGlobal-
> >CommonRuntimeVariableSpace));
> -
> -  //
> -  // The max NV variable size should be < (VariableStoreLength - sizeof
> (VARIABLE_STORE_HEADER)).
> -  //
> -  ASSERT (GetNonVolatileMaxVariableSize () < (VariableStoreLength - sizeof
> (VARIABLE_STORE_HEADER)));
> -
> -  return EFI_SUCCESS;
> -}
> -
> -/**
> -  Init emulated non-volatile variable store.
> -
> -  @param[out] VariableStoreBase Output pointer to emulated non-volatile
> variable store base.
> -
> -  @retval EFI_SUCCESS           Function successfully executed.
> -  @retval EFI_OUT_OF_RESOURCES  Fail to allocate enough memory
> resource.
> -
> -**/
> -EFI_STATUS
> -InitEmuNonVolatileVariableStore (
> -  EFI_PHYSICAL_ADDRESS  *VariableStoreBase
> -  )
> -{
> -  VARIABLE_STORE_HEADER *VariableStore;
> -  UINT32                VariableStoreLength;
> -  BOOLEAN               FullyInitializeStore;
> -  UINT32                HwErrStorageSize;
> -
> -  FullyInitializeStore = TRUE;
> -
> -  VariableStoreLength = PcdGet32 (PcdVariableStoreSize);
> -  ASSERT (sizeof (VARIABLE_STORE_HEADER) <= VariableStoreLength);
> -
> -  //
> -  // Allocate memory for variable store.
> -  //
> -  if (PcdGet64 (PcdEmuVariableNvStoreReserved) == 0) {
> -    VariableStore = (VARIABLE_STORE_HEADER *) AllocateRuntimePool
> (VariableStoreLength);
> -    if (VariableStore == NULL) {
> -      return EFI_OUT_OF_RESOURCES;
> -    }
> -  } else {
> -    //
> -    // A memory location has been reserved for the NV variable store.
> Certain
> -    // platforms may be able to preserve a memory range across system
> resets,
> -    // thereby providing better NV variable emulation.
> -    //
> -    VariableStore =
> -      (VARIABLE_STORE_HEADER *)(VOID*)(UINTN)
> -        PcdGet64 (PcdEmuVariableNvStoreReserved);
> -    if ((VariableStore->Size == VariableStoreLength) &&
> -        (CompareGuid (&VariableStore->Signature,
> &gEfiAuthenticatedVariableGuid) ||
> -         CompareGuid (&VariableStore->Signature, &gEfiVariableGuid)) &&
> -        (VariableStore->Format == VARIABLE_STORE_FORMATTED) &&
> -        (VariableStore->State == VARIABLE_STORE_HEALTHY)) {
> -      DEBUG((
> -        DEBUG_INFO,
> -        "Variable Store reserved at %p appears to be valid\n",
> -        VariableStore
> -        ));
> -      FullyInitializeStore = FALSE;
> -    }
> -  }
> -
> -  if (FullyInitializeStore) {
> -    SetMem (VariableStore, VariableStoreLength, 0xff);
> -    //
> -    // Use gEfiAuthenticatedVariableGuid for potential auth variable support.
> -    //
> -    CopyGuid (&VariableStore->Signature, &gEfiAuthenticatedVariableGuid);
> -    VariableStore->Size       = VariableStoreLength;
> -    VariableStore->Format     = VARIABLE_STORE_FORMATTED;
> -    VariableStore->State      = VARIABLE_STORE_HEALTHY;
> -    VariableStore->Reserved   = 0;
> -    VariableStore->Reserved1  = 0;
> -  }
> -
> -  *VariableStoreBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStore;
> -
> -  HwErrStorageSize = PcdGet32 (PcdHwErrStorageSize);
> -
> -  //
> -  // Note that in EdkII variable driver implementation, Hardware Error Record
> type variable
> -  // is stored with common variable in the same NV region. So the platform
> integrator should
> -  // ensure that the value of PcdHwErrStorageSize is less than the value of
> -  // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).
> -  //
> -  ASSERT (HwErrStorageSize < (VariableStoreLength - sizeof
> (VARIABLE_STORE_HEADER)));
> -
> -  mVariableModuleGlobal->CommonVariableSpace = ((UINTN)
> VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) -
> HwErrStorageSize);
> -  mVariableModuleGlobal->CommonMaxUserVariableSpace =
> mVariableModuleGlobal->CommonVariableSpace;
> -  mVariableModuleGlobal->CommonRuntimeVariableSpace =
> mVariableModuleGlobal->CommonVariableSpace;
> -
> -  return EFI_SUCCESS;
> -}
> -
> -/**
> -  Init non-volatile variable store.
> -
> -  @retval EFI_SUCCESS           Function successfully executed.
> -  @retval EFI_OUT_OF_RESOURCES  Fail to allocate enough memory
> resource.
> -  @retval EFI_VOLUME_CORRUPTED  Variable Store or Firmware Volume for
> Variable Store is corrupted.
> -
> -**/
> -EFI_STATUS
> -InitNonVolatileVariableStore (
> -  VOID
> -  )
> -{
> -  VARIABLE_HEADER                       *Variable;
> -  VARIABLE_HEADER                       *NextVariable;
> -  EFI_PHYSICAL_ADDRESS                  VariableStoreBase;
> -  UINTN                                 VariableSize;
> -  EFI_STATUS                            Status;
> -
> -  if (PcdGetBool (PcdEmuVariableNvModeEnable)) {
> -    Status = InitEmuNonVolatileVariableStore (&VariableStoreBase);
> -    if (EFI_ERROR (Status)) {
> -      return Status;
> -    }
> -    mVariableModuleGlobal->VariableGlobal.EmuNvMode = TRUE;
> -    DEBUG ((DEBUG_INFO, "Variable driver will work at emulated non-volatile
> variable mode!\n"));
> -  } else {
> -    Status = InitRealNonVolatileVariableStore (&VariableStoreBase);
> -    if (EFI_ERROR (Status)) {
> -      return Status;
> -    }
> -    mVariableModuleGlobal->VariableGlobal.EmuNvMode = FALSE;
> -  }
> -
> -  mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase =
> VariableStoreBase;
> -  mNvVariableCache = (VARIABLE_STORE_HEADER *) (UINTN)
> VariableStoreBase;
> -  mVariableModuleGlobal->VariableGlobal.AuthFormat =
> (BOOLEAN)(CompareGuid (&mNvVariableCache->Signature,
> &gEfiAuthenticatedVariableGuid));
> -
> -  mVariableModuleGlobal->MaxVariableSize = PcdGet32
> (PcdMaxVariableSize);
> -  mVariableModuleGlobal->MaxAuthVariableSize = ((PcdGet32
> (PcdMaxAuthVariableSize) != 0) ? PcdGet32 (PcdMaxAuthVariableSize) :
> mVariableModuleGlobal->MaxVariableSize);
> -
> -  //
> -  // Parse non-volatile variable data and get last variable offset.
> -  //
> -  Variable  = GetStartPointer (mNvVariableCache);
> -  while (IsValidVariableHeader (Variable, GetEndPointer
> (mNvVariableCache))) {
> -    NextVariable = GetNextVariablePtr (Variable, mVariableModuleGlobal-
> >VariableGlobal.AuthFormat);
> -    VariableSize = (UINTN) NextVariable - (UINTN) Variable;
> -    if ((Variable->Attributes & (EFI_VARIABLE_NON_VOLATILE |
> EFI_VARIABLE_HARDWARE_ERROR_RECORD)) ==
> (EFI_VARIABLE_NON_VOLATILE |
> EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
> -      mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize;
> -    } else {
> -      mVariableModuleGlobal->CommonVariableTotalSize += VariableSize;
> -    }
> -
> -    Variable = NextVariable;
> -  }
> -  mVariableModuleGlobal->NonVolatileLastVariableOffset = (UINTN)
> Variable - (UINTN) mNvVariableCache;
> -
> -  return EFI_SUCCESS;
> -}
> -
>  /**
>    Flush the HOB variable to flash.
> 
> diff --git
> a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
> b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
> new file mode 100644
> index 0000000000..0637a828b3
> --- /dev/null
> +++
> b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
> @@ -0,0 +1,334 @@
> +/** @file
> +  Common variable non-volatile store routines.
> +
> +Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "VariableNonVolatile.h"
> +#include "VariableParsing.h"
> +
> +extern VARIABLE_MODULE_GLOBAL  *mVariableModuleGlobal;
> +
> +/**
> +  Get non-volatile maximum variable size.
> +
> +  @return Non-volatile maximum variable size.
> +
> +**/
> +UINTN
> +GetNonVolatileMaxVariableSize (
> +  VOID
> +  )
> +{
> +  if (PcdGet32 (PcdHwErrStorageSize) != 0) {
> +    return MAX (MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32
> (PcdMaxAuthVariableSize)),
> +                PcdGet32 (PcdMaxHardwareErrorVariableSize));
> +  } else {
> +    return MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32
> (PcdMaxAuthVariableSize));
> +  }
> +}
> +
> +/**
> +  Init emulated non-volatile variable store.
> +
> +  @param[out] VariableStoreBase Output pointer to emulated non-volatile
> variable store base.
> +
> +  @retval EFI_SUCCESS           Function successfully executed.
> +  @retval EFI_OUT_OF_RESOURCES  Fail to allocate enough memory
> resource.
> +
> +**/
> +EFI_STATUS
> +InitEmuNonVolatileVariableStore (
> +  OUT EFI_PHYSICAL_ADDRESS  *VariableStoreBase
> +  )
> +{
> +  VARIABLE_STORE_HEADER *VariableStore;
> +  UINT32                VariableStoreLength;
> +  BOOLEAN               FullyInitializeStore;
> +  UINT32                HwErrStorageSize;
> +
> +  FullyInitializeStore = TRUE;
> +
> +  VariableStoreLength = PcdGet32 (PcdVariableStoreSize);
> +  ASSERT (sizeof (VARIABLE_STORE_HEADER) <= VariableStoreLength);
> +
> +  //
> +  // Allocate memory for variable store.
> +  //
> +  if (PcdGet64 (PcdEmuVariableNvStoreReserved) == 0) {
> +    VariableStore = (VARIABLE_STORE_HEADER *) AllocateRuntimePool
> (VariableStoreLength);
> +    if (VariableStore == NULL) {
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +  } else {
> +    //
> +    // A memory location has been reserved for the NV variable store.
> Certain
> +    // platforms may be able to preserve a memory range across system
> resets,
> +    // thereby providing better NV variable emulation.
> +    //
> +    VariableStore =
> +      (VARIABLE_STORE_HEADER *)(VOID*)(UINTN)
> +        PcdGet64 (PcdEmuVariableNvStoreReserved);
> +    if ((VariableStore->Size == VariableStoreLength) &&
> +        (CompareGuid (&VariableStore->Signature,
> &gEfiAuthenticatedVariableGuid) ||
> +         CompareGuid (&VariableStore->Signature, &gEfiVariableGuid)) &&
> +        (VariableStore->Format == VARIABLE_STORE_FORMATTED) &&
> +        (VariableStore->State == VARIABLE_STORE_HEALTHY)) {
> +      DEBUG((
> +        DEBUG_INFO,
> +        "Variable Store reserved at %p appears to be valid\n",
> +        VariableStore
> +        ));
> +      FullyInitializeStore = FALSE;
> +    }
> +  }
> +
> +  if (FullyInitializeStore) {
> +    SetMem (VariableStore, VariableStoreLength, 0xff);
> +    //
> +    // Use gEfiAuthenticatedVariableGuid for potential auth variable support.
> +    //
> +    CopyGuid (&VariableStore->Signature, &gEfiAuthenticatedVariableGuid);
> +    VariableStore->Size       = VariableStoreLength;
> +    VariableStore->Format     = VARIABLE_STORE_FORMATTED;
> +    VariableStore->State      = VARIABLE_STORE_HEALTHY;
> +    VariableStore->Reserved   = 0;
> +    VariableStore->Reserved1  = 0;
> +  }
> +
> +  *VariableStoreBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStore;
> +
> +  HwErrStorageSize = PcdGet32 (PcdHwErrStorageSize);
> +
> +  //
> +  // Note that in EdkII variable driver implementation, Hardware Error
> Record type variable
> +  // is stored with common variable in the same NV region. So the platform
> integrator should
> +  // ensure that the value of PcdHwErrStorageSize is less than the value of
> +  // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).
> +  //
> +  ASSERT (HwErrStorageSize < (VariableStoreLength - sizeof
> (VARIABLE_STORE_HEADER)));
> +
> +  mVariableModuleGlobal->CommonVariableSpace = ((UINTN)
> VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) -
> HwErrStorageSize);
> +  mVariableModuleGlobal->CommonMaxUserVariableSpace =
> mVariableModuleGlobal->CommonVariableSpace;
> +  mVariableModuleGlobal->CommonRuntimeVariableSpace =
> mVariableModuleGlobal->CommonVariableSpace;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Init real non-volatile variable store.
> +
> +  @param[out] VariableStoreBase Output pointer to real non-volatile
> variable store base.
> +
> +  @retval EFI_SUCCESS           Function successfully executed.
> +  @retval EFI_OUT_OF_RESOURCES  Fail to allocate enough memory
> resource.
> +  @retval EFI_VOLUME_CORRUPTED  Variable Store or Firmware Volume for
> Variable Store is corrupted.
> +
> +**/
> +EFI_STATUS
> +InitRealNonVolatileVariableStore (
> +  OUT EFI_PHYSICAL_ADDRESS              *VariableStoreBase
> +  )
> +{
> +  EFI_FIRMWARE_VOLUME_HEADER            *FvHeader;
> +  VARIABLE_STORE_HEADER                 *VariableStore;
> +  UINT32                                VariableStoreLength;
> +  EFI_HOB_GUID_TYPE                     *GuidHob;
> +  EFI_PHYSICAL_ADDRESS                  NvStorageBase;
> +  UINT8                                 *NvStorageData;
> +  UINT32                                NvStorageSize;
> +  FAULT_TOLERANT_WRITE_LAST_WRITE_DATA  *FtwLastWriteData;
> +  UINT32                                BackUpOffset;
> +  UINT32                                BackUpSize;
> +  UINT32                                HwErrStorageSize;
> +  UINT32                                MaxUserNvVariableSpaceSize;
> +  UINT32                                BoottimeReservedNvVariableSpaceSize;
> +  EFI_STATUS                            Status;
> +  VOID                                  *FtwProtocol;
> +
> +  mVariableModuleGlobal->FvbInstance = NULL;
> +
> +  //
> +  // Allocate runtime memory used for a memory copy of the FLASH region.
> +  // Keep the memory and the FLASH in sync as updates occur.
> +  //
> +  NvStorageSize = PcdGet32 (PcdFlashNvStorageVariableSize);
> +  NvStorageData = AllocateRuntimeZeroPool (NvStorageSize);
> +  if (NvStorageData == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  NvStorageBase = NV_STORAGE_VARIABLE_BASE;
> +  ASSERT (NvStorageBase != 0);
> +
> +  //
> +  // Copy NV storage data to the memory buffer.
> +  //
> +  CopyMem (NvStorageData, (UINT8 *) (UINTN) NvStorageBase,
> NvStorageSize);
> +
> +  Status = GetFtwProtocol ((VOID **)&FtwProtocol);
> +  //
> +  // If FTW protocol has been installed, no need to check FTW last write data
> hob.
> +  //
> +  if (EFI_ERROR (Status)) {
> +    //
> +    // Check the FTW last write data hob.
> +    //
> +    GuidHob = GetFirstGuidHob (&gEdkiiFaultTolerantWriteGuid);
> +    if (GuidHob != NULL) {
> +      FtwLastWriteData = (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA *)
> GET_GUID_HOB_DATA (GuidHob);
> +      if (FtwLastWriteData->TargetAddress == NvStorageBase) {
> +        DEBUG ((DEBUG_INFO, "Variable: NV storage is backed up in spare
> block: 0x%x\n", (UINTN) FtwLastWriteData->SpareAddress));
> +        //
> +        // Copy the backed up NV storage data to the memory buffer from
> spare block.
> +        //
> +        CopyMem (NvStorageData, (UINT8 *) (UINTN) (FtwLastWriteData-
> >SpareAddress), NvStorageSize);
> +      } else if ((FtwLastWriteData->TargetAddress > NvStorageBase) &&
> +                 (FtwLastWriteData->TargetAddress < (NvStorageBase +
> NvStorageSize))) {
> +        //
> +        // Flash NV storage from the Offset is backed up in spare block.
> +        //
> +        BackUpOffset = (UINT32) (FtwLastWriteData->TargetAddress -
> NvStorageBase);
> +        BackUpSize = NvStorageSize - BackUpOffset;
> +        DEBUG ((DEBUG_INFO, "Variable: High partial NV storage from
> offset: %x is backed up in spare block: 0x%x\n", BackUpOffset, (UINTN)
> FtwLastWriteData->SpareAddress));
> +        //
> +        // Copy the partial backed up NV storage data to the memory buffer
> from spare block.
> +        //
> +        CopyMem (NvStorageData + BackUpOffset, (UINT8 *) (UINTN)
> FtwLastWriteData->SpareAddress, BackUpSize);
> +      }
> +    }
> +  }
> +
> +  FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) NvStorageData;
> +
> +  //
> +  // Check if the Firmware Volume is not corrupted
> +  //
> +  if ((FvHeader->Signature != EFI_FVH_SIGNATURE) || (!CompareGuid
> (&gEfiSystemNvDataFvGuid, &FvHeader->FileSystemGuid))) {
> +    FreePool (NvStorageData);
> +    DEBUG ((DEBUG_ERROR, "Firmware Volume for Variable Store is
> corrupted\n"));
> +    return EFI_VOLUME_CORRUPTED;
> +  }
> +
> +  VariableStore = (VARIABLE_STORE_HEADER *) ((UINTN) FvHeader +
> FvHeader->HeaderLength);
> +  VariableStoreLength = NvStorageSize - FvHeader->HeaderLength;
> +  ASSERT (sizeof (VARIABLE_STORE_HEADER) <= VariableStoreLength);
> +  ASSERT (VariableStore->Size == VariableStoreLength);
> +
> +  //
> +  // Check if the Variable Store header is not corrupted
> +  //
> +  if (GetVariableStoreStatus (VariableStore) != EfiValid) {
> +    FreePool (NvStorageData);
> +    DEBUG((DEBUG_ERROR, "Variable Store header is corrupted\n"));
> +    return EFI_VOLUME_CORRUPTED;
> +  }
> +
> +  mNvFvHeaderCache = FvHeader;
> +
> +  *VariableStoreBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStore;
> +
> +  HwErrStorageSize = PcdGet32 (PcdHwErrStorageSize);
> +  MaxUserNvVariableSpaceSize = PcdGet32
> (PcdMaxUserNvVariableSpaceSize);
> +  BoottimeReservedNvVariableSpaceSize = PcdGet32
> (PcdBoottimeReservedNvVariableSpaceSize);
> +
> +  //
> +  // Note that in EdkII variable driver implementation, Hardware Error
> Record type variable
> +  // is stored with common variable in the same NV region. So the platform
> integrator should
> +  // ensure that the value of PcdHwErrStorageSize is less than the value of
> +  // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).
> +  //
> +  ASSERT (HwErrStorageSize < (VariableStoreLength - sizeof
> (VARIABLE_STORE_HEADER)));
> +  //
> +  // Ensure that the value of PcdMaxUserNvVariableSpaceSize is less than
> the value of
> +  // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32
> (PcdHwErrStorageSize).
> +  //
> +  ASSERT (MaxUserNvVariableSpaceSize < (VariableStoreLength - sizeof
> (VARIABLE_STORE_HEADER) - HwErrStorageSize));
> +  //
> +  // Ensure that the value of PcdBoottimeReservedNvVariableSpaceSize is
> less than the value of
> +  // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32
> (PcdHwErrStorageSize).
> +  //
> +  ASSERT (BoottimeReservedNvVariableSpaceSize < (VariableStoreLength -
> sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize));
> +
> +  mVariableModuleGlobal->CommonVariableSpace = ((UINTN)
> VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) -
> HwErrStorageSize);
> +  mVariableModuleGlobal->CommonMaxUserVariableSpace =
> ((MaxUserNvVariableSpaceSize != 0) ? MaxUserNvVariableSpaceSize :
> mVariableModuleGlobal->CommonVariableSpace);
> +  mVariableModuleGlobal->CommonRuntimeVariableSpace =
> mVariableModuleGlobal->CommonVariableSpace -
> BoottimeReservedNvVariableSpaceSize;
> +
> +  DEBUG ((
> +    DEBUG_INFO,
> +    "Variable driver common space: 0x%x 0x%x 0x%x\n",
> +    mVariableModuleGlobal->CommonVariableSpace,
> +    mVariableModuleGlobal->CommonMaxUserVariableSpace,
> +    mVariableModuleGlobal->CommonRuntimeVariableSpace
> +    ));
> +
> +  //
> +  // The max NV variable size should be < (VariableStoreLength - sizeof
> (VARIABLE_STORE_HEADER)).
> +  //
> +  ASSERT (GetNonVolatileMaxVariableSize () < (VariableStoreLength - sizeof
> (VARIABLE_STORE_HEADER)));
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Init non-volatile variable store.
> +
> +  @retval EFI_SUCCESS           Function successfully executed.
> +  @retval EFI_OUT_OF_RESOURCES  Fail to allocate enough memory
> resource.
> +  @retval EFI_VOLUME_CORRUPTED  Variable Store or Firmware Volume for
> Variable Store is corrupted.
> +
> +**/
> +EFI_STATUS
> +InitNonVolatileVariableStore (
> +  VOID
> +  )
> +{
> +  VARIABLE_HEADER                       *Variable;
> +  VARIABLE_HEADER                       *NextVariable;
> +  EFI_PHYSICAL_ADDRESS                  VariableStoreBase;
> +  UINTN                                 VariableSize;
> +  EFI_STATUS                            Status;
> +
> +  if (PcdGetBool (PcdEmuVariableNvModeEnable)) {
> +    Status = InitEmuNonVolatileVariableStore (&VariableStoreBase);
> +    if (EFI_ERROR (Status)) {
> +      return Status;
> +    }
> +    mVariableModuleGlobal->VariableGlobal.EmuNvMode = TRUE;
> +    DEBUG ((DEBUG_INFO, "Variable driver will work at emulated non-
> volatile variable mode!\n"));
> +  } else {
> +    Status = InitRealNonVolatileVariableStore (&VariableStoreBase);
> +    if (EFI_ERROR (Status)) {
> +      return Status;
> +    }
> +    mVariableModuleGlobal->VariableGlobal.EmuNvMode = FALSE;
> +  }
> +
> +  mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase =
> VariableStoreBase;
> +  mNvVariableCache = (VARIABLE_STORE_HEADER *) (UINTN)
> VariableStoreBase;
> +  mVariableModuleGlobal->VariableGlobal.AuthFormat =
> (BOOLEAN)(CompareGuid (&mNvVariableCache->Signature,
> &gEfiAuthenticatedVariableGuid));
> +
> +  mVariableModuleGlobal->MaxVariableSize = PcdGet32
> (PcdMaxVariableSize);
> +  mVariableModuleGlobal->MaxAuthVariableSize = ((PcdGet32
> (PcdMaxAuthVariableSize) != 0) ? PcdGet32 (PcdMaxAuthVariableSize) :
> mVariableModuleGlobal->MaxVariableSize);
> +
> +  //
> +  // Parse non-volatile variable data and get last variable offset.
> +  //
> +  Variable  = GetStartPointer (mNvVariableCache);
> +  while (IsValidVariableHeader (Variable, GetEndPointer
> (mNvVariableCache))) {
> +    NextVariable = GetNextVariablePtr (Variable, mVariableModuleGlobal-
> >VariableGlobal.AuthFormat);
> +    VariableSize = (UINTN) NextVariable - (UINTN) Variable;
> +    if ((Variable->Attributes & (EFI_VARIABLE_NON_VOLATILE |
> EFI_VARIABLE_HARDWARE_ERROR_RECORD)) ==
> (EFI_VARIABLE_NON_VOLATILE |
> EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
> +      mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize;
> +    } else {
> +      mVariableModuleGlobal->CommonVariableTotalSize += VariableSize;
> +    }
> +
> +    Variable = NextVariable;
> +  }
> +  mVariableModuleGlobal->NonVolatileLastVariableOffset = (UINTN)
> Variable - (UINTN) mNvVariableCache;
> +
> +  return EFI_SUCCESS;
> +}
> --
> 2.16.2.windows.1


  reply	other threads:[~2019-10-16  7:56 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-10-14 23:29 [PATCH V4 00/10] UEFI Variable SMI Reduction Kubacki, Michael A
2019-10-14 23:29 ` [PATCH V4 01/10] MdeModulePkg/Variable: Consolidate common parsing functions Kubacki, Michael A
2019-10-16  7:52   ` Wu, Hao A
2019-10-14 23:29 ` [PATCH V4 02/10] MdeModulePkg/Variable: Parameterize GetNextVariableInternal () stores Kubacki, Michael A
2019-10-16  7:53   ` Wu, Hao A
2019-10-14 23:29 ` [PATCH V4 03/10] MdeModulePkg/Variable: Parameterize VARIABLE_INFO_ENTRY buffer Kubacki, Michael A
2019-10-16  7:54   ` Wu, Hao A
2019-10-14 23:29 ` [PATCH V4 04/10] MdeModulePkg/Variable: Parameterize auth status in VariableParsing Kubacki, Michael A
2019-10-17  1:01   ` Wu, Hao A
2019-10-17  1:41     ` Kubacki, Michael A
2019-10-17  1:49       ` Wu, Hao A
2019-10-14 23:29 ` [PATCH V4 05/10] MdeModulePkg/Variable: Add a file for NV variable functions Kubacki, Michael A
2019-10-16  7:55   ` Wu, Hao A [this message]
2019-10-14 23:29 ` [PATCH V4 06/10] MdeModulePkg VariableInfo: Always consider RT DXE and SMM stats Kubacki, Michael A
2019-10-16  7:56   ` Wu, Hao A
2019-10-14 23:29 ` [PATCH V4 07/10] MdeModulePkg/Variable: Add RT GetVariable() cache support Kubacki, Michael A
2019-10-16  6:46   ` Wang, Jian J
     [not found]   ` <15CE0DB2DE3EB613.1607@groups.io>
2019-10-16  6:54     ` [edk2-devel] " Wang, Jian J
2019-10-17  1:24       ` Kubacki, Michael A
2019-10-17  1:47         ` Wang, Jian J
2019-10-16  7:56   ` Wu, Hao A
2019-10-16 16:44     ` Kubacki, Michael A
2019-10-17 14:23     ` Wang, Jian J
2019-10-17 17:44       ` Kubacki, Michael A
2019-10-14 23:29 ` [PATCH V4 08/10] MdeModulePkg/Variable: Add RT GetNextVariableName() " Kubacki, Michael A
2019-10-16  7:56   ` Wu, Hao A
2019-10-14 23:30 ` [PATCH V4 09/10] OvmfPkg: Disable variable runtime cache Kubacki, Michael A
2019-10-15  7:32   ` Laszlo Ersek
2019-10-14 23:30 ` [PATCH V4 10/10] MdeModulePkg: Enable variable runtime cache by default Kubacki, Michael A
2019-10-15  7:33   ` Laszlo Ersek
2019-10-16  7:57   ` Wu, Hao A
2019-10-15  0:49 ` [PATCH V4 00/10] UEFI Variable SMI Reduction Liming Gao
2019-10-15 16:15   ` Kubacki, Michael A

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=B80AF82E9BFB8E4FBD8C89DA810C6A093C94802B@SHSMSX104.ccr.corp.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