qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Peter Maydell <peter.maydell@linaro.org>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PULL 10/13] target/arm: A32, T32: Create Instruction Syndromes for Data Aborts
Date: Tue,  7 Feb 2017 18:37:22 +0000	[thread overview]
Message-ID: <1486492645-27803-11-git-send-email-peter.maydell@linaro.org> (raw)
In-Reply-To: <1486492645-27803-1-git-send-email-peter.maydell@linaro.org>

Add support for generating the ISS (Instruction Specific Syndrome)
for Data Abort exceptions taken from AArch32. These syndromes are
used by hypervisors for example to trap and emulate memory accesses.

This is the equivalent for AArch32 guests of the work done for AArch64
guests in commit aaa1f954d4cab243.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
---
 target/arm/translate.h     |  14 ++++
 target/arm/translate-a64.c |  14 ----
 target/arm/translate.c     | 184 +++++++++++++++++++++++++++++++++------------
 3 files changed, 149 insertions(+), 63 deletions(-)

diff --git a/target/arm/translate.h b/target/arm/translate.h
index 285e96f..abb0760 100644
--- a/target/arm/translate.h
+++ b/target/arm/translate.h
@@ -104,6 +104,20 @@ static inline int default_exception_el(DisasContext *s)
             ? 3 : MAX(1, s->current_el);
 }
 
+static void disas_set_insn_syndrome(DisasContext *s, uint32_t syn)
+{
+    /* We don't need to save all of the syndrome so we mask and shift
+     * out unneeded bits to help the sleb128 encoder do a better job.
+     */
+    syn &= ARM_INSN_START_WORD2_MASK;
+    syn >>= ARM_INSN_START_WORD2_SHIFT;
+
+    /* We check and clear insn_start_idx to catch multiple updates.  */
+    assert(s->insn_start_idx != 0);
+    tcg_set_insn_param(s->insn_start_idx, 2, syn);
+    s->insn_start_idx = 0;
+}
+
 /* target-specific extra values for is_jmp */
 /* These instructions trap after executing, so the A32/T32 decoder must
  * defer them until after the conditional execution state has been updated.
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index d0352e2..e61bbd6 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -379,20 +379,6 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest)
     }
 }
 
-static void disas_set_insn_syndrome(DisasContext *s, uint32_t syn)
-{
-    /* We don't need to save all of the syndrome so we mask and shift
-     * out uneeded bits to help the sleb128 encoder do a better job.
-     */
-    syn &= ARM_INSN_START_WORD2_MASK;
-    syn >>= ARM_INSN_START_WORD2_SHIFT;
-
-    /* We check and clear insn_start_idx to catch multiple updates.  */
-    assert(s->insn_start_idx != 0);
-    tcg_set_insn_param(s->insn_start_idx, 2, syn);
-    s->insn_start_idx = 0;
-}
-
 static void unallocated_encoding(DisasContext *s)
 {
     /* Unallocated and reserved encodings are uncategorized */
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 175b4c1..a14f74c 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -102,6 +102,49 @@ void arm_translate_init(void)
     a64_translate_init();
 }
 
+/* Flags for the disas_set_da_iss info argument:
+ * lower bits hold the Rt register number, higher bits are flags.
+ */
+typedef enum ISSInfo {
+    ISSNone = 0,
+    ISSRegMask = 0x1f,
+    ISSInvalid = (1 << 5),
+    ISSIsAcqRel = (1 << 6),
+    ISSIsWrite = (1 << 7),
+    ISSIs16Bit = (1 << 8),
+} ISSInfo;
+
+/* Save the syndrome information for a Data Abort */
+static void disas_set_da_iss(DisasContext *s, TCGMemOp memop, ISSInfo issinfo)
+{
+    uint32_t syn;
+    int sas = memop & MO_SIZE;
+    bool sse = memop & MO_SIGN;
+    bool is_acqrel = issinfo & ISSIsAcqRel;
+    bool is_write = issinfo & ISSIsWrite;
+    bool is_16bit = issinfo & ISSIs16Bit;
+    int srt = issinfo & ISSRegMask;
+
+    if (issinfo & ISSInvalid) {
+        /* Some callsites want to conditionally provide ISS info,
+         * eg "only if this was not a writeback"
+         */
+        return;
+    }
+
+    if (srt == 15) {
+        /* For AArch32, insns where the src/dest is R15 never generate
+         * ISS information. Catching that here saves checking at all
+         * the call sites.
+         */
+        return;
+    }
+
+    syn = syn_data_abort_with_iss(0, sas, sse, srt, 0, is_acqrel,
+                                  0, 0, 0, is_write, 0, is_16bit);
+    disas_set_insn_syndrome(s, syn);
+}
+
 static inline ARMMMUIdx get_a32_user_mem_index(DisasContext *s)
 {
     /* Return the mmu_idx to use for A32/T32 "unprivileged load/store"
@@ -933,6 +976,14 @@ static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val,      \
                                      TCGv_i32 a32, int index)            \
 {                                                                        \
     gen_aa32_ld_i32(s, val, a32, index, OPC | s->be_data);               \
+}                                                                        \
+static inline void gen_aa32_ld##SUFF##_iss(DisasContext *s,              \
+                                           TCGv_i32 val,                 \
+                                           TCGv_i32 a32, int index,      \
+                                           ISSInfo issinfo)              \
+{                                                                        \
+    gen_aa32_ld_i32(s, val, a32, index, OPC | s->be_data);               \
+    disas_set_da_iss(s, OPC, issinfo);                                   \
 }
 
 #define DO_GEN_ST(SUFF, OPC)                                             \
@@ -940,6 +991,14 @@ static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val,      \
                                      TCGv_i32 a32, int index)            \
 {                                                                        \
     gen_aa32_st_i32(s, val, a32, index, OPC | s->be_data);               \
+}                                                                        \
+static inline void gen_aa32_st##SUFF##_iss(DisasContext *s,              \
+                                           TCGv_i32 val,                 \
+                                           TCGv_i32 a32, int index,      \
+                                           ISSInfo issinfo)              \
+{                                                                        \
+    gen_aa32_st_i32(s, val, a32, index, OPC | s->be_data);               \
+    disas_set_da_iss(s, OPC, issinfo | ISSIsWrite);                      \
 }
 
 static inline void gen_aa32_frob64(DisasContext *s, TCGv_i64 val)
@@ -8682,16 +8741,19 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
                                 tmp = tcg_temp_new_i32();
                                 switch (op1) {
                                 case 0: /* lda */
-                                    gen_aa32_ld32u(s, tmp, addr,
-                                                   get_mem_index(s));
+                                    gen_aa32_ld32u_iss(s, tmp, addr,
+                                                       get_mem_index(s),
+                                                       rd | ISSIsAcqRel);
                                     break;
                                 case 2: /* ldab */
-                                    gen_aa32_ld8u(s, tmp, addr,
-                                                  get_mem_index(s));
+                                    gen_aa32_ld8u_iss(s, tmp, addr,
+                                                      get_mem_index(s),
+                                                      rd | ISSIsAcqRel);
                                     break;
                                 case 3: /* ldah */
-                                    gen_aa32_ld16u(s, tmp, addr,
-                                                   get_mem_index(s));
+                                    gen_aa32_ld16u_iss(s, tmp, addr,
+                                                       get_mem_index(s),
+                                                       rd | ISSIsAcqRel);
                                     break;
                                 default:
                                     abort();
@@ -8702,16 +8764,19 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
                                 tmp = load_reg(s, rm);
                                 switch (op1) {
                                 case 0: /* stl */
-                                    gen_aa32_st32(s, tmp, addr,
-                                                  get_mem_index(s));
+                                    gen_aa32_st32_iss(s, tmp, addr,
+                                                      get_mem_index(s),
+                                                      rm | ISSIsAcqRel);
                                     break;
                                 case 2: /* stlb */
-                                    gen_aa32_st8(s, tmp, addr,
-                                                 get_mem_index(s));
+                                    gen_aa32_st8_iss(s, tmp, addr,
+                                                     get_mem_index(s),
+                                                     rm | ISSIsAcqRel);
                                     break;
                                 case 3: /* stlh */
-                                    gen_aa32_st16(s, tmp, addr,
-                                                  get_mem_index(s));
+                                    gen_aa32_st16_iss(s, tmp, addr,
+                                                      get_mem_index(s),
+                                                      rm | ISSIsAcqRel);
                                     break;
                                 default:
                                     abort();
@@ -8785,10 +8850,15 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
                 bool wbit = insn & (1 << 21);
                 bool pbit = insn & (1 << 24);
                 bool doubleword = false;
+                ISSInfo issinfo;
+
                 /* Misc load/store */
                 rn = (insn >> 16) & 0xf;
                 rd = (insn >> 12) & 0xf;
 
+                /* ISS not valid if writeback */
+                issinfo = (pbit & !wbit) ? rd : ISSInvalid;
+
                 if (!load && (sh & 2)) {
                     /* doubleword */
                     ARCH(5TE);
@@ -8832,20 +8902,23 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
                     tmp = tcg_temp_new_i32();
                     switch (sh) {
                     case 1:
-                        gen_aa32_ld16u(s, tmp, addr, get_mem_index(s));
+                        gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s),
+                                           issinfo);
                         break;
                     case 2:
-                        gen_aa32_ld8s(s, tmp, addr, get_mem_index(s));
+                        gen_aa32_ld8s_iss(s, tmp, addr, get_mem_index(s),
+                                          issinfo);
                         break;
                     default:
                     case 3:
-                        gen_aa32_ld16s(s, tmp, addr, get_mem_index(s));
+                        gen_aa32_ld16s_iss(s, tmp, addr, get_mem_index(s),
+                                           issinfo);
                         break;
                     }
                 } else {
                     /* store */
                     tmp = load_reg(s, rd);
-                    gen_aa32_st16(s, tmp, addr, get_mem_index(s));
+                    gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s), issinfo);
                     tcg_temp_free_i32(tmp);
                 }
                 /* Perform base writeback before the loaded value to
@@ -9198,17 +9271,17 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
                 /* load */
                 tmp = tcg_temp_new_i32();
                 if (insn & (1 << 22)) {
-                    gen_aa32_ld8u(s, tmp, tmp2, i);
+                    gen_aa32_ld8u_iss(s, tmp, tmp2, i, rd);
                 } else {
-                    gen_aa32_ld32u(s, tmp, tmp2, i);
+                    gen_aa32_ld32u_iss(s, tmp, tmp2, i, rd);
                 }
             } else {
                 /* store */
                 tmp = load_reg(s, rd);
                 if (insn & (1 << 22)) {
-                    gen_aa32_st8(s, tmp, tmp2, i);
+                    gen_aa32_st8_iss(s, tmp, tmp2, i, rd);
                 } else {
-                    gen_aa32_st32(s, tmp, tmp2, i);
+                    gen_aa32_st32_iss(s, tmp, tmp2, i, rd);
                 }
                 tcg_temp_free_i32(tmp);
             }
@@ -9669,13 +9742,16 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
                         tmp = tcg_temp_new_i32();
                         switch (op) {
                         case 0: /* ldab */
-                            gen_aa32_ld8u(s, tmp, addr, get_mem_index(s));
+                            gen_aa32_ld8u_iss(s, tmp, addr, get_mem_index(s),
+                                              rs | ISSIsAcqRel);
                             break;
                         case 1: /* ldah */
-                            gen_aa32_ld16u(s, tmp, addr, get_mem_index(s));
+                            gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s),
+                                               rs | ISSIsAcqRel);
                             break;
                         case 2: /* lda */
-                            gen_aa32_ld32u(s, tmp, addr, get_mem_index(s));
+                            gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s),
+                                               rs | ISSIsAcqRel);
                             break;
                         default:
                             abort();
@@ -9685,13 +9761,16 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
                         tmp = load_reg(s, rs);
                         switch (op) {
                         case 0: /* stlb */
-                            gen_aa32_st8(s, tmp, addr, get_mem_index(s));
+                            gen_aa32_st8_iss(s, tmp, addr, get_mem_index(s),
+                                             rs | ISSIsAcqRel);
                             break;
                         case 1: /* stlh */
-                            gen_aa32_st16(s, tmp, addr, get_mem_index(s));
+                            gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s),
+                                              rs | ISSIsAcqRel);
                             break;
                         case 2: /* stl */
-                            gen_aa32_st32(s, tmp, addr, get_mem_index(s));
+                            gen_aa32_st32_iss(s, tmp, addr, get_mem_index(s),
+                                              rs | ISSIsAcqRel);
                             break;
                         default:
                             abort();
@@ -10637,6 +10716,8 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
         int postinc = 0;
         int writeback = 0;
         int memidx;
+        ISSInfo issinfo;
+
         if ((insn & 0x01100000) == 0x01000000) {
             if (disas_neon_ls_insn(s, insn)) {
                 goto illegal_op;
@@ -10740,24 +10821,27 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
                 }
             }
         }
+
+        issinfo = writeback ? ISSInvalid : rs;
+
         if (insn & (1 << 20)) {
             /* Load.  */
             tmp = tcg_temp_new_i32();
             switch (op) {
             case 0:
-                gen_aa32_ld8u(s, tmp, addr, memidx);
+                gen_aa32_ld8u_iss(s, tmp, addr, memidx, issinfo);
                 break;
             case 4:
-                gen_aa32_ld8s(s, tmp, addr, memidx);
+                gen_aa32_ld8s_iss(s, tmp, addr, memidx, issinfo);
                 break;
             case 1:
-                gen_aa32_ld16u(s, tmp, addr, memidx);
+                gen_aa32_ld16u_iss(s, tmp, addr, memidx, issinfo);
                 break;
             case 5:
-                gen_aa32_ld16s(s, tmp, addr, memidx);
+                gen_aa32_ld16s_iss(s, tmp, addr, memidx, issinfo);
                 break;
             case 2:
-                gen_aa32_ld32u(s, tmp, addr, memidx);
+                gen_aa32_ld32u_iss(s, tmp, addr, memidx, issinfo);
                 break;
             default:
                 tcg_temp_free_i32(tmp);
@@ -10774,13 +10858,13 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
             tmp = load_reg(s, rs);
             switch (op) {
             case 0:
-                gen_aa32_st8(s, tmp, addr, memidx);
+                gen_aa32_st8_iss(s, tmp, addr, memidx, issinfo);
                 break;
             case 1:
-                gen_aa32_st16(s, tmp, addr, memidx);
+                gen_aa32_st16_iss(s, tmp, addr, memidx, issinfo);
                 break;
             case 2:
-                gen_aa32_st32(s, tmp, addr, memidx);
+                gen_aa32_st32_iss(s, tmp, addr, memidx, issinfo);
                 break;
             default:
                 tcg_temp_free_i32(tmp);
@@ -10917,7 +11001,8 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
             addr = tcg_temp_new_i32();
             tcg_gen_movi_i32(addr, val);
             tmp = tcg_temp_new_i32();
-            gen_aa32_ld32u(s, tmp, addr, get_mem_index(s));
+            gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s),
+                               rd | ISSIs16Bit);
             tcg_temp_free_i32(addr);
             store_reg(s, rd, tmp);
             break;
@@ -11120,28 +11205,28 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
 
         switch (op) {
         case 0: /* str */
-            gen_aa32_st32(s, tmp, addr, get_mem_index(s));
+            gen_aa32_st32_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             break;
         case 1: /* strh */
-            gen_aa32_st16(s, tmp, addr, get_mem_index(s));
+            gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             break;
         case 2: /* strb */
-            gen_aa32_st8(s, tmp, addr, get_mem_index(s));
+            gen_aa32_st8_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             break;
         case 3: /* ldrsb */
-            gen_aa32_ld8s(s, tmp, addr, get_mem_index(s));
+            gen_aa32_ld8s_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             break;
         case 4: /* ldr */
-            gen_aa32_ld32u(s, tmp, addr, get_mem_index(s));
+            gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             break;
         case 5: /* ldrh */
-            gen_aa32_ld16u(s, tmp, addr, get_mem_index(s));
+            gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             break;
         case 6: /* ldrb */
-            gen_aa32_ld8u(s, tmp, addr, get_mem_index(s));
+            gen_aa32_ld8u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             break;
         case 7: /* ldrsh */
-            gen_aa32_ld16s(s, tmp, addr, get_mem_index(s));
+            gen_aa32_ld16s_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             break;
         }
         if (op >= 3) { /* load */
@@ -11185,12 +11270,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
         if (insn & (1 << 11)) {
             /* load */
             tmp = tcg_temp_new_i32();
-            gen_aa32_ld8u(s, tmp, addr, get_mem_index(s));
+            gen_aa32_ld8u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             store_reg(s, rd, tmp);
         } else {
             /* store */
             tmp = load_reg(s, rd);
-            gen_aa32_st8(s, tmp, addr, get_mem_index(s));
+            gen_aa32_st8_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             tcg_temp_free_i32(tmp);
         }
         tcg_temp_free_i32(addr);
@@ -11207,12 +11292,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
         if (insn & (1 << 11)) {
             /* load */
             tmp = tcg_temp_new_i32();
-            gen_aa32_ld16u(s, tmp, addr, get_mem_index(s));
+            gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             store_reg(s, rd, tmp);
         } else {
             /* store */
             tmp = load_reg(s, rd);
-            gen_aa32_st16(s, tmp, addr, get_mem_index(s));
+            gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             tcg_temp_free_i32(tmp);
         }
         tcg_temp_free_i32(addr);
@@ -11228,12 +11313,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
         if (insn & (1 << 11)) {
             /* load */
             tmp = tcg_temp_new_i32();
-            gen_aa32_ld32u(s, tmp, addr, get_mem_index(s));
+            gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             store_reg(s, rd, tmp);
         } else {
             /* store */
             tmp = load_reg(s, rd);
-            gen_aa32_st32(s, tmp, addr, get_mem_index(s));
+            gen_aa32_st32_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit);
             tcg_temp_free_i32(tmp);
         }
         tcg_temp_free_i32(addr);
@@ -11715,6 +11800,7 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb)
         store_cpu_field(tmp, condexec_bits);
       }
     do {
+        dc->insn_start_idx = tcg_op_buf_count();
         tcg_gen_insn_start(dc->pc,
                            (dc->condexec_cond << 4) | (dc->condexec_mask >> 1),
                            0);
-- 
2.7.4

  parent reply	other threads:[~2017-02-07 18:37 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-02-07 18:37 [Qemu-devel] [PULL 00/13] target-arm queue Peter Maydell
2017-02-07 18:37 ` [Qemu-devel] [PULL 01/13] integratorcp: adding vmstate for save/restore Peter Maydell
2017-02-07 18:37 ` [Qemu-devel] [PULL 02/13] wdt: Add Aspeed watchdog device model Peter Maydell
2017-02-07 18:37 ` [Qemu-devel] [PULL 03/13] aspeed: add a watchdog controller Peter Maydell
2017-02-07 18:37 ` [Qemu-devel] [PULL 04/13] sd: sdhci: check data length during dma_memory_read Peter Maydell
2017-02-07 18:37 ` [Qemu-devel] [PULL 05/13] hw/arm/integratorcp: Support specifying features via -cpu Peter Maydell
2017-02-07 18:37 ` [Qemu-devel] [PULL 06/13] target/arm: Add cfgend parameter for ARM CPU selection Peter Maydell
2017-02-07 18:37 ` [Qemu-devel] [PULL 07/13] Fix Thumb-1 BE32 execution and disassembly Peter Maydell
2017-02-07 18:37 ` [Qemu-devel] [PULL 08/13] arm: Correctly handle watchpoints for BE32 CPUs Peter Maydell
2017-02-07 18:37 ` [Qemu-devel] [PULL 09/13] target/arm: Abstract out pbit/wbit tests in ARM ldr/str decode Peter Maydell
2017-02-07 18:37 ` Peter Maydell [this message]
2017-02-07 18:37 ` [Qemu-devel] [PULL 11/13] stellaris: Document memory map and which SoC devices are unimplemented Peter Maydell
2017-02-07 18:37 ` [Qemu-devel] [PULL 12/13] hw/misc: New "unimplemented" sysbus device Peter Maydell
2017-02-07 18:37 ` [Qemu-devel] [PULL 13/13] stellaris: Use the 'unimplemented' device for parts we don't implement Peter Maydell
2017-02-07 18:55 ` [Qemu-devel] [PULL 00/13] target-arm queue Peter Maydell
2017-02-07 19:01 ` no-reply

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-all 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=1486492645-27803-11-git-send-email-peter.maydell@linaro.org \
    --to=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    /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;
as well as URLs for NNTP newsgroup(s).