public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Min Xu" <min.m.xu@intel.com>
To: devel@edk2.groups.io
Cc: Min Xu <min.m.xu@intel.com>,
	Ard Biesheuvel <ardb+tianocore@kernel.org>,
	Jordan Justen <jordan.l.justen@intel.com>,
	Brijesh Singh <brijesh.singh@amd.com>,
	Erdem Aktas <erdemaktas@google.com>,
	James Bottomley <jejb@linux.ibm.com>,
	Jiewen Yao <jiewen.yao@intel.com>,
	Tom Lendacky <thomas.lendacky@amd.com>,
	Gerd Hoffmann <kraxel@redhat.com>
Subject: [PATCH V8 15/47] OvmfPkg: Add TdxMailboxLib
Date: Sat, 12 Mar 2022 09:53:40 +0800	[thread overview]
Message-ID: <25479838ba8fd7849090f9a137bfddf64606fd59.1647047482.git.min.m.xu@intel.com> (raw)
In-Reply-To: <cover.1647047481.git.min.m.xu@intel.com>

RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429

In Tdx BSP may issues commands to APs for some task, for example, to
accept pages paralelly. BSP also need to wait until all the APs have
done the task. TdxMailboxLib wraps these common funtions for BSP.

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
 OvmfPkg/Include/Library/TdxMailboxLib.h       |  76 ++++++++++
 OvmfPkg/Library/TdxMailboxLib/TdxMailbox.c    | 141 ++++++++++++++++++
 .../Library/TdxMailboxLib/TdxMailboxLib.inf   |  52 +++++++
 .../Library/TdxMailboxLib/TdxMailboxNull.c    |  85 +++++++++++
 OvmfPkg/OvmfPkg.dec                           |   4 +
 5 files changed, 358 insertions(+)
 create mode 100644 OvmfPkg/Include/Library/TdxMailboxLib.h
 create mode 100644 OvmfPkg/Library/TdxMailboxLib/TdxMailbox.c
 create mode 100644 OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf
 create mode 100644 OvmfPkg/Library/TdxMailboxLib/TdxMailboxNull.c

diff --git a/OvmfPkg/Include/Library/TdxMailboxLib.h b/OvmfPkg/Include/Library/TdxMailboxLib.h
new file mode 100644
index 000000000000..166cab43bc02
--- /dev/null
+++ b/OvmfPkg/Include/Library/TdxMailboxLib.h
@@ -0,0 +1,76 @@
+/** @file
+
+  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef TDX_MAILBOX_LIB_H_
+#define TDX_MAILBOX_LIB_H_
+
+#include <Library/BaseLib.h>
+#include <Uefi/UefiBaseType.h>
+#include <Uefi/UefiSpec.h>
+#include <Pi/PiPeiCis.h>
+#include <Library/DebugLib.h>
+#include <Protocol/DebugSupport.h>
+
+/**
+  This function will be called by BSP to get the CPU number.
+
+  @retval   CPU number
+**/
+UINT32
+EFIAPI
+GetCpusNum (
+  VOID
+  );
+
+/**
+  Get the address of Td mailbox.
+**/
+volatile VOID *
+EFIAPI
+GetTdxMailBox (
+  VOID
+  );
+
+/**
+  This function will be called by BSP to wakeup APs the are spinning on mailbox
+  in protected mode
+
+  @param[in] Command          Command to send APs
+  @param[in] WakeupVector     If used, address for APs to start executing
+  @param[in] WakeArgsX        Args to pass to APs for excuting commands
+**/
+VOID
+EFIAPI
+MpSendWakeupCommand (
+  IN UINT16  Command,
+  IN UINT64  WakeupVector,
+  IN UINT64  WakeupArgs1,
+  IN UINT64  WakeupArgs2,
+  IN UINT64  WakeupArgs3,
+  IN UINT64  WakeupArgs4
+  );
+
+/**
+  BSP wait until all the APs arriving. It means the task triggered by BSP is started.
+**/
+VOID
+EFIAPI
+MpSerializeStart (
+  VOID
+  );
+
+/**
+  BSP wait until all the APs arriving. It means the task triggered by BSP is ended.
+**/
+VOID
+EFIAPI
+MpSerializeEnd (
+  VOID
+  );
+
+#endif
diff --git a/OvmfPkg/Library/TdxMailboxLib/TdxMailbox.c b/OvmfPkg/Library/TdxMailboxLib/TdxMailbox.c
new file mode 100644
index 000000000000..74cb55611fe3
--- /dev/null
+++ b/OvmfPkg/Library/TdxMailboxLib/TdxMailbox.c
@@ -0,0 +1,141 @@
+/** @file
+
+  Copyright (c) 2008, Intel Corporation. All rights reserved.<BR>
+  (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiCpuLib.h>
+#include <Library/SynchronizationLib.h>
+#include <Uefi/UefiBaseType.h>
+#include <Library/TdxLib.h>
+#include <IndustryStandard/IntelTdx.h>
+#include <IndustryStandard/Tdx.h>
+#include <Library/TdxMailboxLib.h>
+
+volatile VOID  *mMailBox  = NULL;
+UINT32         mNumOfCpus = 0;
+
+/**
+  This function will be called by BSP to get the CPU number.
+
+  @retval   CPU number
+**/
+UINT32
+EFIAPI
+GetCpusNum (
+  VOID
+  )
+{
+  if (mNumOfCpus == 0) {
+    mNumOfCpus = TdVCpuNum ();
+  }
+
+  return mNumOfCpus;
+}
+
+/**
+  Get the address of Td mailbox.
+**/
+volatile VOID *
+EFIAPI
+GetTdxMailBox (
+  VOID
+  )
+{
+  if (mMailBox == NULL) {
+    mMailBox = (VOID *)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupBase);
+  }
+
+  return mMailBox;
+}
+
+/**
+  This function will be called by BSP to wakeup APs the are spinning on mailbox
+  in protected mode
+
+  @param[in] Command          Command to send APs
+  @param[in] WakeupVector     If used, address for APs to start executing
+  @param[in] WakeArgsX        Args to pass to APs for excuting commands
+**/
+VOID
+EFIAPI
+MpSendWakeupCommand (
+  IN UINT16  Command,
+  IN UINT64  WakeupVector,
+  IN UINT64  WakeupArgs1,
+  IN UINT64  WakeupArgs2,
+  IN UINT64  WakeupArgs3,
+  IN UINT64  WakeupArgs4
+  )
+{
+  volatile MP_WAKEUP_MAILBOX  *MailBox;
+
+  MailBox               = (volatile MP_WAKEUP_MAILBOX *)GetTdxMailBox ();
+  MailBox->ApicId       = MP_CPU_PROTECTED_MODE_MAILBOX_APICID_INVALID;
+  MailBox->WakeUpVector = 0;
+  MailBox->Command      = MpProtectedModeWakeupCommandNoop;
+  MailBox->ApicId       = MP_CPU_PROTECTED_MODE_MAILBOX_APICID_BROADCAST;
+  MailBox->WakeUpVector = WakeupVector;
+  MailBox->WakeUpArgs1  = WakeupArgs1;
+  MailBox->WakeUpArgs2  = WakeupArgs2;
+  MailBox->WakeUpArgs3  = WakeupArgs3;
+  MailBox->WakeUpArgs4  = WakeupArgs4;
+  AsmCpuid (0x01, NULL, NULL, NULL, NULL);
+  MailBox->Command = Command;
+  AsmCpuid (0x01, NULL, NULL, NULL, NULL);
+  return;
+}
+
+/**
+  BSP wait until all the APs arriving. It means the task triggered by BSP is started.
+**/
+VOID
+EFIAPI
+MpSerializeStart (
+  VOID
+  )
+{
+  volatile MP_WAKEUP_MAILBOX  *MailBox;
+  UINT32                      NumOfCpus;
+
+  NumOfCpus = GetCpusNum ();
+  MailBox   = (volatile MP_WAKEUP_MAILBOX *)GetTdxMailBox ();
+
+  DEBUG ((DEBUG_VERBOSE, "Waiting for APs to arriving. NumOfCpus=%d, MailBox=%p\n", NumOfCpus, MailBox));
+  while (MailBox->NumCpusArriving != (NumOfCpus -1)) {
+    CpuPause ();
+  }
+
+  DEBUG ((DEBUG_VERBOSE, "Releasing APs\n"));
+  MailBox->NumCpusExiting = NumOfCpus;
+  InterlockedIncrement ((UINT32 *)&MailBox->NumCpusArriving);
+}
+
+/**
+  BSP wait until all the APs arriving. It means the task triggered by BSP is ended.
+**/
+VOID
+EFIAPI
+MpSerializeEnd (
+  VOID
+  )
+{
+  volatile MP_WAKEUP_MAILBOX  *MailBox;
+
+  MailBox = (volatile MP_WAKEUP_MAILBOX *)GetTdxMailBox ();
+  DEBUG ((DEBUG_VERBOSE, "Waiting for APs to finish\n"));
+  while (MailBox->NumCpusExiting != 1 ) {
+    CpuPause ();
+  }
+
+  DEBUG ((DEBUG_VERBOSE, "Restarting APs\n"));
+  MailBox->Command         = MpProtectedModeWakeupCommandNoop;
+  MailBox->NumCpusArriving = 0;
+  InterlockedDecrement ((UINT32 *)&MailBox->NumCpusExiting);
+}
diff --git a/OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf b/OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf
new file mode 100644
index 000000000000..3cf3690a16c7
--- /dev/null
+++ b/OvmfPkg/Library/TdxMailboxLib/TdxMailboxLib.inf
@@ -0,0 +1,52 @@
+#/** @file
+#
+#  TBD
+#
+#  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+#  Copyright (c) 2008, Apple Inc. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = TdxMailboxLib
+  FILE_GUID                      = 2F81A9BA-748E-4519-BB11-A63A039D561E
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = TdxMailboxLib
+
+#
+#  VALID_ARCHITECTURES           = X64 IA32
+#
+
+[Sources.IA32]
+  TdxMailboxNull.c
+
+[Sources.X64]
+  TdxMailbox.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  BaseMemoryLib
+  PcdLib
+  UefiCpuLib
+  DebugAgentLib
+  IoLib
+  SynchronizationLib
+  MemoryAllocationLib
+
+[Guids]
+
+[Pcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
diff --git a/OvmfPkg/Library/TdxMailboxLib/TdxMailboxNull.c b/OvmfPkg/Library/TdxMailboxLib/TdxMailboxNull.c
new file mode 100644
index 000000000000..35b070361eb1
--- /dev/null
+++ b/OvmfPkg/Library/TdxMailboxLib/TdxMailboxNull.c
@@ -0,0 +1,85 @@
+/** @file
+
+  Copyright (c) 2008, Intel Corporation. All rights reserved.<BR>
+  (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/TdxMailboxLib.h>
+
+/**
+  This function will be called by BSP to get the CPU number.
+
+  @retval   CPU number
+**/
+UINT32
+EFIAPI
+GetCpusNum (
+  VOID
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Get the address of Td mailbox.
+**/
+volatile VOID *
+EFIAPI
+GetTdxMailBox (
+  VOID
+  )
+{
+  ASSERT (FALSE);
+  return (volatile VOID *)NULL;
+}
+
+/**
+  This function will be called by BSP to wakeup APs the are spinning on mailbox
+  in protected mode
+
+  @param[in] Command          Command to send APs
+  @param[in] WakeupVector     If used, address for APs to start executing
+  @param[in] WakeArgsX        Args to pass to APs for excuting commands
+**/
+VOID
+EFIAPI
+MpSendWakeupCommand (
+  IN UINT16  Command,
+  IN UINT64  WakeupVector,
+  IN UINT64  WakeupArgs1,
+  IN UINT64  WakeupArgs2,
+  IN UINT64  WakeupArgs3,
+  IN UINT64  WakeupArgs4
+  )
+{
+  ASSERT (FALSE);
+}
+
+/**
+  BSP wait until all the APs arriving. It means the task triggered by BSP is started.
+**/
+VOID
+EFIAPI
+MpSerializeStart (
+  VOID
+  )
+{
+  ASSERT (FALSE);
+}
+
+/**
+  BSP wait until all the APs arriving. It means the task triggered by BSP is ended.
+**/
+VOID
+EFIAPI
+MpSerializeEnd (
+  VOID
+  )
+{
+  ASSERT (FALSE);
+}
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 7aa94ca02863..d373b5d6042e 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -109,6 +109,10 @@
   #
   XenPlatformLib|Include/Library/XenPlatformLib.h
 
+  ##  @libraryclass  TdxMailboxLib
+  #
+  TdxMailboxLib|Include/Library/TdxMailboxLib.h
+
 [Guids]
   gUefiOvmfPkgTokenSpaceGuid            = {0x93bb96af, 0xb9f2, 0x4eb8, {0x94, 0x62, 0xe0, 0xba, 0x74, 0x56, 0x42, 0x36}}
   gEfiXenInfoGuid                       = {0xd3b46f3b, 0xd441, 0x1244, {0x9a, 0x12, 0x0, 0x12, 0x27, 0x3f, 0xc1, 0x4d}}
-- 
2.29.2.windows.2


  parent reply	other threads:[~2022-03-12  1:55 UTC|newest]

Thread overview: 75+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-12  1:53 [PATCH V8 00/47] Enable Intel TDX in OvmfPkg (Config-A) Min Xu
2022-03-12  1:53 ` [PATCH V8 01/47] MdePkg: Add Tdx.h Min Xu
2022-03-12  1:53 ` [PATCH V8 02/47] MdePkg: Introduce basic Tdx functions in BaseLib Min Xu
2022-03-15  7:41   ` [edk2-devel] " Ni, Ray
2022-03-16  1:18     ` Min Xu
2022-03-16  1:26       ` Ni, Ray
2022-03-15  7:44   ` Ni, Ray
2022-03-16  2:15     ` Min Xu
2022-03-12  1:53 ` [PATCH V8 03/47] MdePkg: Add TdxLib to wrap Tdx operations Min Xu
2022-03-12  1:53 ` [PATCH V8 04/47] UefiCpuPkg: Extend VmgExitLibNull to handle #VE exception Min Xu
2022-03-12  1:53 ` [PATCH V8 05/47] OvmfPkg: Extend VmgExitLib " Min Xu
2022-03-12  1:53 ` [PATCH V8 06/47] UefiCpuPkg/CpuExceptionHandler: Add base support for the " Min Xu
2022-03-12  1:53 ` [PATCH V8 07/47] MdePkg: Add helper functions for Tdx guest in BaseIoLibIntrinsic Min Xu
2022-03-12  1:53 ` [PATCH V8 08/47] MdePkg: Support mmio " Min Xu
2022-03-12  1:53 ` [PATCH V8 09/47] MdePkg: Support IoFifo " Min Xu
2022-03-12  1:53 ` [PATCH V8 10/47] MdePkg: Support IoRead/IoWrite " Min Xu
2022-03-12  1:53 ` [PATCH V8 11/47] UefiCpuPkg: Support TDX in BaseXApicX2ApicLib Min Xu
2022-03-12  1:53 ` [PATCH V8 12/47] MdePkg: Add macro to check SEV / TDX guest Min Xu
2022-03-12  1:53 ` [PATCH V8 13/47] UefiCpuPkg: Enable Tdx support in MpInitLib Min Xu
2022-03-15  7:57   ` Ni, Ray
2022-03-16  8:32     ` Min Xu
2022-03-16  8:36       ` Ni, Ray
2022-03-16 11:53         ` Min Xu
2022-03-12  1:53 ` [PATCH V8 14/47] OvmfPkg: Add IntelTdx.h in OvmfPkg/Include/IndustryStandard Min Xu
2022-03-12  1:53 ` Min Xu [this message]
2022-03-12  1:53 ` [PATCH V8 16/47] OvmfPkg: Create initial version of PlatformInitLib Min Xu
2022-03-15  7:41   ` Gerd Hoffmann
2022-03-12  1:53 ` [PATCH V8 17/47] OvmfPkg/PlatformInitLib: Add hob functions Min Xu
2022-03-15  7:42   ` Gerd Hoffmann
2022-03-12  1:53 ` [PATCH V8 18/47] OvmfPkg/PlatformPei: Move global variables to PlatformInfoHob Min Xu
2022-03-15  7:57   ` Gerd Hoffmann
2022-03-16  5:26     ` [edk2-devel] " Min Xu
2022-03-12  1:53 ` [PATCH V8 19/47] OvmfPkg/PlatformPei: Refactor MiscInitialization Min Xu
2022-03-15  7:59   ` Gerd Hoffmann
2022-03-12  1:53 ` [PATCH V8 20/47] OvmfPkg/PlatformPei: Refactor MiscInitialization for CloudHV Min Xu
2022-03-15  7:59   ` Gerd Hoffmann
2022-03-12  1:53 ` [PATCH V8 21/47] OvmfPkg/PlatformPei: Refactor AddressWidthInitialization Min Xu
2022-03-15  8:04   ` Gerd Hoffmann
2022-03-16  5:56     ` [edk2-devel] " Min Xu
2022-03-12  1:53 ` [PATCH V8 22/47] OvmfPkg/PlatformPei: Refactor MaxCpuCountInitialization Min Xu
2022-03-15  8:04   ` Gerd Hoffmann
2022-03-16  6:25     ` Min Xu
2022-03-12  1:53 ` [PATCH V8 23/47] OvmfPkg/PlatformPei: Refactor QemuUc32BaseInitialization Min Xu
2022-03-15  8:05   ` Gerd Hoffmann
2022-03-12  1:53 ` [PATCH V8 25/47] OvmfPkg/PlatformPei: Refactor MemMapInitialization Min Xu
2022-03-15  8:06   ` Gerd Hoffmann
2022-03-12  1:53 ` [PATCH V8 26/47] OvmfPkg/PlatformPei: Refactor NoexecDxeInitialization Min Xu
2022-03-15  8:13   ` Gerd Hoffmann
2022-03-12  1:53 ` [PATCH V8 27/47] OvmfPkg/PlatformPei: Refactor MiscInitialization Min Xu
2022-03-15  8:14   ` Gerd Hoffmann
2022-03-12  1:53 ` [PATCH V8 28/47] OvmfPkg/PlatformInitLib: Create MemDetect.c Min Xu
2022-03-15  8:14   ` Gerd Hoffmann
2022-03-12  1:53 ` [PATCH V8 29/47] OvmfPkg/PlatformInitLib: Move functions to Platform.c Min Xu
2022-03-15  8:15   ` Gerd Hoffmann
2022-03-12  1:53 ` [PATCH V8 30/47] MdePkg: Add EFI_RESOURCE_MEMORY_UNACCEPTED defition Min Xu
2022-03-15  8:15   ` Gerd Hoffmann
2022-03-12  1:53 ` [PATCH V8 31/47] OvmfPkg: Update PlatformInitLib to process Tdx hoblist Min Xu
2022-03-12  1:53 ` [PATCH V8 32/47] OvmfPkg/Sec: Declare local variable as volatile in SecCoreStartupWithStack Min Xu
2022-03-12  1:53 ` [PATCH V8 33/47] OvmfPkg: Update Sec to support Tdx Min Xu
2022-03-12  1:53 ` [PATCH V8 34/47] OvmfPkg: Check Tdx in QemuFwCfgPei to avoid DMA operation Min Xu
2022-03-12  1:54 ` [PATCH V8 35/47] MdeModulePkg: Skip setting IA32_ERER.NXE if it has already been set Min Xu
2022-03-15  5:16   ` Wang, Jian J
2022-03-12  1:54 ` [PATCH V8 36/47] MdeModulePkg: Add PcdTdxSharedBitMask Min Xu
2022-03-15  5:16   ` Wang, Jian J
2022-03-12  1:54 ` [PATCH V8 37/47] UefiCpuPkg: Update AddressEncMask in CpuPageTable Min Xu
2022-03-12  1:54 ` [PATCH V8 38/47] OvmfPkg: Update PlatformInitLib for Tdx guest Min Xu
2022-03-12  1:54 ` [PATCH V8 39/47] OvmfPkg: Update PlatformPei to support " Min Xu
2022-03-12  1:54 ` [PATCH V8 40/47] OvmfPkg: Update AcpiPlatformDxe to alter MADT table Min Xu
2022-03-15  3:55 ` 回复: [edk2-devel] [PATCH V8 00/47] Enable Intel TDX in OvmfPkg (Config-A) gaoliming
2022-03-15  7:11   ` Min Xu
2022-03-17  1:00     ` 回复: " gaoliming
2022-03-17  1:16       ` Min Xu
     [not found] ` <5cf66f701073359f385e7ff28b5173555ab483c3.1647047482.git.min.m.xu@intel.com>
2022-03-15  8:05   ` [PATCH V8 24/47] OvmfPkg/PlatformPei: Refactor InitializeRamRegions Gerd Hoffmann
     [not found] ` <16DB7F586DEAD807.23528@groups.io>
2022-03-17  5:31   ` [edk2-devel] [PATCH V8 12/47] MdePkg: Add macro to check SEV / TDX guest Min Xu
2022-03-18  1:13     ` 回复: " gaoliming

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=25479838ba8fd7849090f9a137bfddf64606fd59.1647047482.git.min.m.xu@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