public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Leif Lindholm <leif.lindholm@linaro.org>
To: Haojian Zhuang <haojian.zhuang@linaro.org>
Cc: edk2-devel@lists.01.org, Ard Biesheuvel <ard.biesheuvel@linaro.org>
Subject: Re: [PATCH edk-platforms v1 1/4] Platform/Hisilicon: add UsbSerialNumberLib
Date: Thu, 4 Oct 2018 17:48:31 +0100	[thread overview]
Message-ID: <20181004164830.naljr5w5aq2kka5q@bivouac.eciton.net> (raw)
In-Reply-To: <1534761109-27037-2-git-send-email-haojian.zhuang@linaro.org>

Ah, here's UsbSerialNumberLib. Got it.
(Please mention dependencies on other patchsets in the cover-letter.)

Please wrap these 3 sets together into a single one, with patches in
dependency order, for v2.

I will comment on this patch aftes I'm done with DwUsb2.

/
    Leif

On Mon, Aug 20, 2018 at 06:31:46PM +0800, Haojian Zhuang wrote:
> Add UsbSerialNumberLib. The Library could generate USB Serial Number
> that is used in USB device driver. And it could load/save the USB
> Serial Number into storage device.
> 
> Cc: Leif Lindholm <leif.lindholm@linaro.org>
> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
> ---
>  Platform/Hisilicon/Library/UsbSerialNumberLib/UsbSerialNumberLib.dec               |  32 ++
>  Platform/Hisilicon/Library/UsbSerialNumberLib/UsbSerialNumberLib.inf               |  45 +++
>  Platform/Hisilicon/Library/UsbSerialNumberLib/Include/Library/UsbSerialNumberLib.h |  59 ++++
>  Platform/Hisilicon/Library/UsbSerialNumberLib/UsbSerialNumberLib.c                 | 341 ++++++++++++++++++++
>  4 files changed, 477 insertions(+)
> 
> diff --git a/Platform/Hisilicon/Library/UsbSerialNumberLib/UsbSerialNumberLib.dec b/Platform/Hisilicon/Library/UsbSerialNumberLib/UsbSerialNumberLib.dec
> new file mode 100644
> index 000000000000..4b8b2e047ed9
> --- /dev/null
> +++ b/Platform/Hisilicon/Library/UsbSerialNumberLib/UsbSerialNumberLib.dec
> @@ -0,0 +1,32 @@
> +#
> +#  Copyright (c) 2018, Linaro Limited. All rights reserved.
> +#
> +#  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.
> +#
> +
> +[Defines]
> +  DEC_SPECIFICATION              = 0x00010019
> +  PACKAGE_NAME                   = UsbSerialNumberPkg
> +  PACKAGE_GUID                   = dcd67420-4220-4b01-a8ba-1fa97fda1678
> +  PACKAGE_VERSION                = 0.1
> +
> +################################################################################
> +#
> +# Include Section - list of Include Paths that are provided by this package.
> +#                   Comments are used for Keywords and Module Types.
> +#
> +# Supported Module Types:
> +#  BASE SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION
> +#
> +################################################################################
> +[Includes.common]
> +  Include                        # Root include for the package
> +
> +[Guids.common]
> +  gUsbSerialNumberTokenSpaceGuid = { 0x0572f26b, 0x1a88, 0x49c2, { 0xb6, 0x98, 0x4d, 0xe0, 0xd0, 0x2a, 0xfe, 0x09 } }
> diff --git a/Platform/Hisilicon/Library/UsbSerialNumberLib/UsbSerialNumberLib.inf b/Platform/Hisilicon/Library/UsbSerialNumberLib/UsbSerialNumberLib.inf
> new file mode 100644
> index 000000000000..70ea086d324e
> --- /dev/null
> +++ b/Platform/Hisilicon/Library/UsbSerialNumberLib/UsbSerialNumberLib.inf
> @@ -0,0 +1,45 @@
> +#/** @file
> +#
> +#  Copyright (c) 2018, Linaro. All rights reserved.
> +#
> +#  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.
> +#
> +#
> +#**/
> +
> +[Defines]
> +  INF_VERSION                    = 0x00010019
> +  BASE_NAME                      = UsbSerialNumberLib
> +  FILE_GUID                      = 88709c56-2a76-4a13-8bcf-427970b7e32a
> +  MODULE_TYPE                    = BASE
> +  VERSION_STRING                 = 1.0
> +  LIBRARY_CLASS                  = UsbSerialNumberLib
> +
> +#
> +# The following information is for reference only and not required by the build tools.
> +#
> +  VALID_ARCHITECTURES           = ARM AARCH64
> +
> +[Sources]
> +  UsbSerialNumberLib.c
> +
> +[LibraryClasses]
> +  ArmGenericTimerCounterLib
> +  BaseMemoryLib
> +  DebugLib
> +  MemoryAllocationLib
> +  UefiBootServicesTableLib
> +  UefiLib
> +
> +[Protocols]
> +  gEfiBlockIoProtocolGuid
> +
> +[Packages]
> +  ArmPkg/ArmPkg.dec
> +  MdePkg/MdePkg.dec
> +  Platform/Hisilicon/Library/UsbSerialNumberLib/UsbSerialNumberLib.dec
> diff --git a/Platform/Hisilicon/Library/UsbSerialNumberLib/Include/Library/UsbSerialNumberLib.h b/Platform/Hisilicon/Library/UsbSerialNumberLib/Include/Library/UsbSerialNumberLib.h
> new file mode 100644
> index 000000000000..d3307153ff11
> --- /dev/null
> +++ b/Platform/Hisilicon/Library/UsbSerialNumberLib/Include/Library/UsbSerialNumberLib.h
> @@ -0,0 +1,59 @@
> +/** @file
> +
> +  Copyright (c) 2017, Linaro. All rights reserved.
> +
> +  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 __USB_SERIAL_NUMBER_LIB_H__
> +#define __USB_SERIAL_NUMBER_LIB_H__
> +
> +#include <Uefi.h>
> +
> +#define SERIAL_NUMBER_SIZE                      17
> +
> +typedef struct {
> +  UINT64        Magic;
> +  UINT64        Data;
> +  CHAR16        UnicodeSN[SERIAL_NUMBER_SIZE];
> +} RANDOM_SERIAL_NUMBER;
> +
> +EFI_STATUS
> +GenerateUsbSNBySeed (
> +  IN  UINT32                  Seed,
> +  OUT RANDOM_SERIAL_NUMBER   *RandomSN
> +  );
> +
> +EFI_STATUS
> +GenerateUsbSN (
> +  OUT CHAR16                 *UnicodeSN
> +  );
> +
> +EFI_STATUS
> +AssignUsbSN (
> +  IN  CHAR8                  *AsciiCmd,
> +  OUT CHAR16                 *UnicodeSN
> +  );
> +
> +EFI_STATUS
> +LoadSNFromBlock (
> +  IN  EFI_HANDLE              FlashHandle,
> +  IN  EFI_LBA                 Lba,
> +  OUT CHAR16                 *UnicodeSN
> +  );
> +
> +EFI_STATUS
> +StoreSNToBlock (
> +  IN EFI_HANDLE               FlashHandle,
> +  IN EFI_LBA                  Lba,
> +  IN CHAR16                  *UnicodeSN
> +  );
> +
> +#endif /* __USB_SERIAL_NUMBER_LIB_H__ */
> diff --git a/Platform/Hisilicon/Library/UsbSerialNumberLib/UsbSerialNumberLib.c b/Platform/Hisilicon/Library/UsbSerialNumberLib/UsbSerialNumberLib.c
> new file mode 100644
> index 000000000000..98552f5f72fc
> --- /dev/null
> +++ b/Platform/Hisilicon/Library/UsbSerialNumberLib/UsbSerialNumberLib.c
> @@ -0,0 +1,341 @@
> +/** @file
> +
> +  Copyright (c) 2018, Linaro. All rights reserved.
> +
> +  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 <Uefi.h>
> +
> +#include <Library/ArmGenericTimerCounterLib.h>
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/DevicePathLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/PrintLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UsbSerialNumberLib.h>
> +
> +#include <Protocol/BlockIo.h>
> +#include <Protocol/DevicePath.h>
> +
> +#define SERIAL_NUMBER_LEN                16
> +#define SERIAL_NUMBER_SIZE               17
> +
> +#define RANDOM_MAX                       0x7FFFFFFFFFFFFFFF
> +#define RANDOM_MAGIC                     0x9A4DBEAF
> +
> +STATIC
> +EFI_STATUS
> +GenerateRandomData (
> +  IN  UINT32              Seed,
> +  OUT UINT64              *RandomData
> +  )
> +{
> +  INT64                   Quotient, Remainder, Tmp;
> +
> +  if (RandomData == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  Quotient = (INT64) Seed / 127773;
> +  Remainder = (INT64) Seed % 127773;
> +  Tmp = (16807 * Remainder) - (2836 * Quotient);
> +  if (Tmp < 0) {
> +    Tmp += RANDOM_MAX;
> +  }
> +  Tmp = Tmp % ((UINT64)RANDOM_MAX + 1);
> +  *RandomData = (UINT64)Tmp;
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +GenerateUsbSNBySeed (
> +  IN  UINT32                  Seed,
> +  OUT RANDOM_SERIAL_NUMBER    *RandomSN
> +  )
> +{
> +  EFI_STATUS               Status;
> +  UINT64                   Tmp;
> +
> +  if (RandomSN == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  ZeroMem (RandomSN, sizeof (RANDOM_SERIAL_NUMBER));
> +  Status = GenerateRandomData (Seed, &Tmp);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +  RandomSN->Data = (Tmp << 32) | Seed;
> +  UnicodeSPrint (
> +    RandomSN->UnicodeSN,
> +    SERIAL_NUMBER_SIZE * sizeof (CHAR16),
> +    L"%lx",
> +    RandomSN->Data
> +    );
> +  RandomSN->Magic = RANDOM_MAGIC;
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +GenerateUsbSN (
> +  OUT CHAR16                  *UnicodeSN
> +  )
> +{
> +  EFI_STATUS               Status;
> +  UINT64                   Tmp;
> +  UINT32                   Seed;
> +  RANDOM_SERIAL_NUMBER     RandomSN;
> +
> +  if (UnicodeSN == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  ZeroMem (&RandomSN, sizeof (RANDOM_SERIAL_NUMBER));
> +  Seed = ArmGenericTimerGetSystemCount ();
> +  Status = GenerateRandomData (Seed, &Tmp);
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +  RandomSN.Data = (Tmp << 32) | Seed;
> +  UnicodeSPrint (
> +    RandomSN.UnicodeSN,
> +    SERIAL_NUMBER_SIZE * sizeof (CHAR16),
> +    L"%lx",
> +    RandomSN.Data
> +    );
> +  StrCpyS (UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), RandomSN.UnicodeSN);
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +AssignUsbSN (
> +  IN  CHAR8                   *AsciiCmd,
> +  OUT CHAR16                  *UnicodeSN
> +  )
> +{
> +  CHAR8                       Data;
> +  UINTN                       Index;
> +  RANDOM_SERIAL_NUMBER        RandomSN;
> +
> +  if ((AsciiCmd == NULL) || (UnicodeSN == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  for (Index = 0; Index < SERIAL_NUMBER_LEN; Index++) {
> +    Data = *(AsciiCmd + Index);
> +    if (((Data >= '0') && (Data <= '9')) ||
> +        ((Data >= 'A') && (Data <= 'F'))) {
> +      continue;
> +    }
> +    //
> +    // Always use with upper case
> +    //
> +    if ((Data >= 'a') && (Data <= 'f')) {
> +      *(AsciiCmd + Index) = Data - 'a' + 'A';
> +      continue;
> +    }
> +    if (Data == '\0') {
> +      break;
> +    }
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  ZeroMem (&RandomSN, sizeof (RANDOM_SERIAL_NUMBER));
> +  AsciiStrToUnicodeStr (AsciiCmd, RandomSN.UnicodeSN);
> +  StrCpyS (UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), RandomSN.UnicodeSN);
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_STATUS
> +LoadSNFromBlock (
> +  IN  EFI_HANDLE              FlashHandle,
> +  IN  EFI_LBA                 Lba,
> +  OUT CHAR16                  *UnicodeSN
> +  )
> +{
> +  EFI_STATUS                  Status;
> +  EFI_BLOCK_IO_PROTOCOL       *BlockIoProtocol;
> +  VOID                        *DataPtr;
> +  BOOLEAN                     Found = FALSE;
> +  UINT32                      Seed;
> +  RANDOM_SERIAL_NUMBER        *RandomSN;
> +  UINTN                       NumPages;
> +  CHAR16                      UnicodeStr[SERIAL_NUMBER_SIZE];
> +
> +  if (UnicodeSN == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  Status = gBS->OpenProtocol (
> +                  FlashHandle,
> +                  &gEfiBlockIoProtocolGuid,
> +                  (VOID **) &BlockIoProtocol,
> +                  gImageHandle,
> +                  NULL,
> +                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
> +                  );
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((
> +      DEBUG_WARN,
> +      "Warning: Couldn't open block device (status: %r)\n",
> +      Status
> +      ));
> +    return EFI_DEVICE_ERROR;
> +  }
> +
> +  NumPages = EFI_SIZE_TO_PAGES (BlockIoProtocol->Media->BlockSize);
> +  DataPtr = AllocatePages (NumPages);
> +  if (DataPtr == NULL) {
> +    return EFI_BUFFER_TOO_SMALL;
> +  }
> +  Status = BlockIoProtocol->ReadBlocks (
> +                              BlockIoProtocol,
> +                              BlockIoProtocol->Media->MediaId,
> +                              Lba,
> +                              BlockIoProtocol->Media->BlockSize,
> +                              DataPtr
> +                              );
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((DEBUG_WARN, "Warning: Failed on reading blocks\n"));
> +    goto Exit;
> +  }
> +
> +  Seed = ArmGenericTimerGetSystemCount ();
> +  RandomSN = (RANDOM_SERIAL_NUMBER *)DataPtr;
> +  if (RandomSN->Magic == RANDOM_MAGIC) {
> +    Found = TRUE;
> +    //
> +    // Verify the unicode string.
> +    //
> +    ZeroMem (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16));
> +    UnicodeSPrint (
> +      UnicodeStr,
> +      SERIAL_NUMBER_SIZE * sizeof (CHAR16),
> +      L"%lx",
> +      RandomSN->Data
> +      );
> +    if (StrLen (RandomSN->UnicodeSN) != StrLen (UnicodeStr)) {
> +      Found = FALSE;
> +    }
> +    if (StrnCmp (RandomSN->UnicodeSN, UnicodeStr, StrLen (UnicodeStr)) != 0) {
> +      Found = FALSE;
> +    }
> +  }
> +  if (Found == FALSE) {
> +    Status = GenerateUsbSNBySeed (Seed, RandomSN);
> +    if (EFI_ERROR (Status)) {
> +      DEBUG ((DEBUG_WARN, "Warning: Failed to generate serial number\n"));
> +      goto Exit;
> +    }
> +    //
> +    // Update SN to block device
> +    //
> +    Status = BlockIoProtocol->WriteBlocks (
> +                                BlockIoProtocol,
> +                                BlockIoProtocol->Media->MediaId,
> +                                Lba,
> +                                BlockIoProtocol->Media->BlockSize,
> +                                DataPtr
> +                                );
> +    if (EFI_ERROR (Status)) {
> +      DEBUG ((DEBUG_WARN, "Warning: Failed on writing blocks\n"));
> +      goto Exit;
> +    }
> +  }
> +  CopyMem (
> +    UnicodeSN,
> +    RandomSN->UnicodeSN,
> +    SERIAL_NUMBER_SIZE * sizeof (CHAR16)
> +    );
> +Exit:
> +  FreePages (DataPtr, NumPages);
> +  return Status;
> +}
> +
> +EFI_STATUS
> +StoreSNToBlock (
> +  IN EFI_HANDLE               FlashHandle,
> +  IN EFI_LBA                  Lba,
> +  IN CHAR16                   *UnicodeSN
> +  )
> +{
> +  EFI_STATUS                  Status;
> +  EFI_BLOCK_IO_PROTOCOL       *BlockIoProtocol;
> +  VOID                        *DataPtr;
> +  UINTN                       NumPages;
> +  RANDOM_SERIAL_NUMBER        *RandomSN;
> +  CHAR16                      UnicodeStr[SERIAL_NUMBER_SIZE];
> +
> +  if (UnicodeSN == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +  Status = gBS->OpenProtocol (
> +                  FlashHandle,
> +                  &gEfiBlockIoProtocolGuid,
> +                  (VOID **) &BlockIoProtocol,
> +                  gImageHandle,
> +                  NULL,
> +                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
> +                  );
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((
> +      DEBUG_WARN,
> +      "Warning: Couldn't open block device (status: %r)\n",
> +      Status
> +      ));
> +    return EFI_DEVICE_ERROR;
> +  }
> +  NumPages = EFI_SIZE_TO_PAGES (BlockIoProtocol->Media->BlockSize);
> +  DataPtr = AllocatePages (NumPages);
> +  if (DataPtr == NULL) {
> +    return EFI_BUFFER_TOO_SMALL;
> +  }
> +  ZeroMem (DataPtr, BlockIoProtocol->Media->BlockSize);
> +  RandomSN = (RANDOM_SERIAL_NUMBER *)DataPtr;
> +  RandomSN->Magic = RANDOM_MAGIC;
> +  StrnCpyS (
> +    RandomSN->UnicodeSN,
> +    SERIAL_NUMBER_SIZE * sizeof (CHAR16),
> +    UnicodeSN,
> +    StrSize (UnicodeSN)
> +    );
> +  RandomSN->Data = StrHexToUint64 (RandomSN->UnicodeSN);
> +
> +  //
> +  // Verify the unicode string.
> +  //
> +  ZeroMem (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16));
> +  UnicodeSPrint (
> +    UnicodeStr,
> +    SERIAL_NUMBER_SIZE * sizeof (CHAR16),
> +    L"%lx",
> +    RandomSN->Data
> +    );
> +  if (StrLen (RandomSN->UnicodeSN) != StrLen (UnicodeStr)) {
> +    Status = EFI_INVALID_PARAMETER;
> +    goto Exit;
> +  }
> +  if (StrnCmp (RandomSN->UnicodeSN, UnicodeStr, StrLen (UnicodeStr)) != 0) {
> +    Status = EFI_INVALID_PARAMETER;
> +    goto Exit;
> +  }
> +
> +  Status = BlockIoProtocol->WriteBlocks (
> +                              BlockIoProtocol,
> +                              BlockIoProtocol->Media->MediaId,
> +                              Lba,
> +                              BlockIoProtocol->Media->BlockSize,
> +                              DataPtr
> +                              );
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((DEBUG_WARN, "Warning: Failed on writing blocks\n"));
> +    goto Exit;
> +  }
> +Exit:
> +  FreePages (DataPtr, NumPages);
> +  return Status;
> +}
> -- 
> 2.7.4
> 


  reply	other threads:[~2018-10-04 16:48 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-08-20 10:31 [PATCH edk-platforms v1 0/4] enable USB on HiKey960 Haojian Zhuang
2018-08-20 10:31 ` [PATCH edk-platforms v1 1/4] Platform/Hisilicon: add UsbSerialNumberLib Haojian Zhuang
2018-10-04 16:48   ` Leif Lindholm [this message]
2018-08-20 10:31 ` [PATCH edk-platforms v1 2/4] Platform/HiKey960: add platform usb driver Haojian Zhuang
2018-08-20 10:31 ` [PATCH edk-platforms v1 3/4] Platform/HiKey960: add fastboot platform driver Haojian Zhuang
2018-08-20 10:31 ` [PATCH edk-platforms v1 4/4] Platform/HiKey960: enable usb device driver Haojian Zhuang

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=20181004164830.naljr5w5aq2kka5q@bivouac.eciton.net \
    --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