All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexander Graf <agraf@suse.de>
To: qemu-devel Developers <qemu-devel@nongnu.org>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Subject: [Qemu-devel] [PATCH 22/40] xenner: kernel: mmu support for 64-bit
Date: Mon,  1 Nov 2010 16:01:35 +0100	[thread overview]
Message-ID: <1288623713-28062-23-git-send-email-agraf@suse.de> (raw)
In-Reply-To: <1288623713-28062-1-git-send-email-agraf@suse.de>

This patch adds support for memory management on 64 bit systems.

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 pc-bios/xenner/xenner-mm64.c |  369 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 369 insertions(+), 0 deletions(-)
 create mode 100644 pc-bios/xenner/xenner-mm64.c

diff --git a/pc-bios/xenner/xenner-mm64.c b/pc-bios/xenner/xenner-mm64.c
new file mode 100644
index 0000000..89cb076
--- /dev/null
+++ b/pc-bios/xenner/xenner-mm64.c
@@ -0,0 +1,369 @@
+/*
+ *  Copyright (C) Red Hat 2007
+ *  Copyright (C) Novell Inc. 2010
+ *
+ *  Author(s): Gerd Hoffmann <kraxel@redhat.com>
+ *             Alexander Graf <agraf@suse.de>
+ *
+ *  Xenner memory management for 64 bit mode
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <inttypes.h>
+#include <xen/xen.h>
+
+#include "xenner.h"
+#include "xenner-mm.c"
+
+/* --------------------------------------------------------------------- */
+
+uintptr_t emu_pa(uintptr_t va)
+{
+    switch(va & 0xfffffff000000000) {
+    case XEN_RAM_64:
+        return va - (uintptr_t)_vstart;
+    case XEN_M2P_64:
+        return va - XEN_M2P_64 + frame_to_addr(vmconf.mfn_m2p);
+    }
+
+    panic("unknown address", NULL);
+    return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static char *print_pgflags(uint32_t flags)
+{
+    static char buf[80];
+
+    snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s\n",
+             flags & _PAGE_GLOBAL   ? " global"   : "",
+             flags & _PAGE_PSE      ? " pse"      : "",
+             flags & _PAGE_DIRTY    ? " dirty"    : "",
+             flags & _PAGE_ACCESSED ? " accessed" : "",
+             flags & _PAGE_PCD      ? " pcd"      : "",
+             flags & _PAGE_PWT      ? " pwt"      : "",
+             flags & _PAGE_USER     ? " user"     : "",
+             flags & _PAGE_RW       ? " write"    : "",
+             flags & _PAGE_PRESENT  ? " present"  : "");
+    return buf;
+}
+
+void pgtable_walk(int level, uint64_t va, uint64_t root_mfn)
+{
+    void *physmem = (void*)XEN_RAM_64;
+    uint64_t *pgd, *pud, *pmd, *pte;
+    uint64_t mfn;
+    uint32_t slot, flags;
+
+    if (vmconf.debug_level < level)
+        return;
+
+    printk(level, "page table walk for va %" PRIx64 ", root_mfn %" PRIx64 "\n",
+           va, root_mfn);
+
+    pgd   = physmem + frame_to_addr(root_mfn);
+    slot  = PGD_INDEX_64(va);
+    mfn   = get_pgframe_64(pgd[slot]);
+    flags = get_pgflags_64(pgd[slot]);
+    printk(level, "pgd   : %p +%3d  |  mfn %4" PRIx64 "  |  %s",
+           pgd, slot, mfn, print_pgflags(flags));
+    if (!(flags & _PAGE_PRESENT))
+        return;
+
+    pud   = physmem + frame_to_addr(mfn);
+    slot  = PUD_INDEX_64(va);
+    mfn   = get_pgframe_64(pud[slot]);
+    flags = get_pgflags_64(pud[slot]);
+    printk(level, " pud  : %p +%3d  |  mfn %4" PRIx64 "  |  %s",
+           pud, slot, mfn, print_pgflags(flags));
+    if (!(flags & _PAGE_PRESENT))
+        return;
+
+    pmd   = physmem + frame_to_addr(mfn);
+    slot  = PMD_INDEX_64(va);
+    mfn   = get_pgframe_64(pmd[slot]);
+    flags = get_pgflags_64(pmd[slot]);
+    printk(level, "  pmd : %p +%3d  |  mfn %4" PRIx64 "  |  %s",
+           pmd, slot, mfn, print_pgflags(flags));
+    if (!(flags & _PAGE_PRESENT))
+        return;
+    if (flags & _PAGE_PSE)
+        return;
+
+    pte   = physmem + frame_to_addr(mfn);
+    slot  = PTE_INDEX_64(va);
+    mfn   = get_pgframe_64(pte[slot]);
+    flags = get_pgflags_64(pte[slot]);
+    printk(level, "   pte: %p +%3d  |  mfn %4" PRIx64 "  |  %s",
+           pte, slot, mfn, print_pgflags(flags));
+}
+
+static int is_pse(uint64_t va)
+{
+    switch (va & 0xffffff8000000000ULL) {
+    case XEN_RAM_64:
+    case XEN_M2P_64:
+        return 1;
+    default:
+        return 0;
+    }
+}
+
+int pgtable_fixup_flag(struct xen_cpu *cpu, uint64_t va, uint32_t flag)
+{
+    void *physmem = (void*)XEN_RAM_64;
+    uint64_t *pgd, *pud, *pmd, *pte;
+    uint32_t slot;
+    int fixes = 0;
+
+    /* quick test on the leaf page via linear page table when we're sure
+       we're not touching a 2mb page which doesn't have a pte */
+    pte = find_pte_64(va);
+    if (!is_pse(va) && !test_pgflag_64(*pte, flag)) {
+        *pte |= flag;
+        fixes++;
+        goto done;
+    }
+
+    /* do full page table walk */
+    pgd   = physmem + frame_to_addr(read_cr3_mfn(cpu));
+    slot  = PGD_INDEX_64(va);
+    if (!test_pgflag_64(pgd[slot], flag)) {
+        pgd[slot] |= flag;
+        fixes++;
+    }
+
+    pud   = physmem + frame_to_addr(get_pgframe_64(pgd[slot]));
+    slot  = PUD_INDEX_64(va);
+    if (!test_pgflag_64(pud[slot], flag)) {
+        pud[slot] |= flag;
+        fixes++;
+    }
+
+    pmd   = physmem + frame_to_addr(get_pgframe_64(pud[slot]));
+    slot  = PMD_INDEX_64(va);
+    if (!test_pgflag_64(pmd[slot], flag)) {
+        pmd[slot] |= flag;
+        fixes++;
+    }
+
+done:
+    if (fixes)
+        flush_tlb_addr(va);
+    return fixes;
+}
+
+int pgtable_is_present(uint64_t va, uint64_t root_mfn)
+{
+    void *physmem = (void*)XEN_RAM_64;
+    uint64_t *pgd, *pud, *pmd, *pte;
+    uint32_t slot;
+
+    pgd  = physmem + frame_to_addr(root_mfn);
+    slot = PGD_INDEX_64(va);
+    if (!test_pgflag_64(pgd[slot], _PAGE_PRESENT)) {
+        return 0;
+    }
+
+    pud  = physmem + frame_to_addr(get_pgframe_64(pgd[slot]));
+    slot = PUD_INDEX_64(va);
+    if (!test_pgflag_64(pud[slot], _PAGE_PRESENT)) {
+        return 0;
+    }
+
+    pmd  = physmem + frame_to_addr(get_pgframe_64(pud[slot]));
+    slot = PMD_INDEX_64(va);
+    if (!test_pgflag_64(pmd[slot], _PAGE_PRESENT)) {
+        return 0;
+    }
+    if (!test_pgflag_64(pmd[slot], _PAGE_PSE)) {
+        return 1;
+    }
+
+    pte   = physmem + frame_to_addr(get_pgframe_64(pmd[slot]));
+    slot  = PTE_INDEX_64(va);
+    if (!test_pgflag_64(pmd[slot], _PAGE_PRESENT)) {
+        return 0;
+    }
+
+    return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+void *map_page(uint64_t maddr)
+{
+    void *ram = (void*)XEN_RAM_64;
+    return ram + maddr;
+}
+
+uint64_t *find_pte_64(uint64_t va)
+{
+    uint64_t *lpt_base = (void*)XEN_LPT_64;
+    uint64_t offset = (va & 0xffffffffffff) >> PAGE_SHIFT;
+
+    return lpt_base + offset;
+}
+
+void update_emu_mappings(uint64_t cr3_mfn)
+{
+    uint64_t *new_pgd;
+    int idx;
+
+    new_pgd  = map_page(frame_to_addr(cr3_mfn));
+
+    idx = PGD_INDEX_64(XEN_M2P_64);
+    for (; idx < PGD_INDEX_64(XEN_DOM_64); idx++) {
+        if ((test_pgflag_64(new_pgd[idx], _PAGE_PRESENT)) ||
+            (!test_pgflag_64(emu_pgd[idx], _PAGE_PRESENT)) ||
+            (idx == PGD_INDEX_64(XEN_LPT_64))) {
+            continue;
+        }
+
+        new_pgd[idx] = emu_pgd[idx];
+    }
+
+    /* linear pgtable mapping */
+    idx = PGD_INDEX_64(XEN_LPT_64);
+    new_pgd[idx] = get_pgentry_64(cr3_mfn, LPT_PGFLAGS);
+}
+
+static inline uint64_t *find_pgd(uint64_t va, uint64_t mfn, int alloc, int sync)
+{
+    void *physmem = (void*)XEN_RAM_64;
+    uint64_t *pgd, *pud, idx;
+
+    pgd  = physmem + frame_to_addr(mfn);
+    idx = PGD_INDEX_64(va);
+    pgd += idx;
+    if (!test_pgflag_64(*pgd, _PAGE_PRESENT) && alloc) {
+        pud = get_pages(1, "pud");
+        *pgd = get_pgentry_64(EMU_MFN(pud), PGT_PGFLAGS_64) & ~_PAGE_GLOBAL;
+        if (sync && !test_pgflag_64(emu_pgd[idx], _PAGE_PRESENT)) {
+            /* sync emu boot pgd */
+            emu_pgd[idx] = *pgd;
+        }
+    }
+    return pgd;
+}
+
+static inline uint64_t *find_pud(uint64_t va, uint64_t mfn, int alloc)
+{
+    void *physmem = (void*)XEN_RAM_64;
+    uint64_t *pud, *pmd;
+
+    pud  = physmem + frame_to_addr(mfn);
+    pud += PUD_INDEX_64(va);
+    if (!test_pgflag_64(*pud, _PAGE_PRESENT) && alloc) {
+        pmd = get_pages(1, "pmd");
+        *pud = get_pgentry_64(EMU_MFN(pmd), PGT_PGFLAGS_64) & ~_PAGE_GLOBAL;
+    }
+    return pud;
+}
+
+static inline uint64_t *find_pmd(uint64_t va, uint64_t mfn, int alloc)
+{
+    void *physmem = (void*)XEN_RAM_64;
+    uint64_t *pmd, *pte;
+
+    pmd  = physmem + frame_to_addr(mfn);
+    pmd += PMD_INDEX_64(va);
+    if (!test_pgflag_64(*pmd, _PAGE_PRESENT) && alloc) {
+        pte = get_pages(1, "pte");
+        *pmd = get_pgentry_64(EMU_MFN(pte), PGT_PGFLAGS_64);
+    }
+    return pmd;
+}
+
+static inline uint64_t *find_pte(uint64_t va, uint64_t mfn)
+{
+    void *physmem = (void*)XEN_RAM_64;
+    uint64_t *pte;
+
+    pte  = physmem + frame_to_addr(mfn);
+    pte += PTE_INDEX_64(va);
+    return pte;
+}
+
+static int map_region_pse(struct xen_cpu *cpu, uint64_t va_start,
+                          uint32_t flags, uint64_t start, uint64_t count)
+{
+    uint64_t *pgd;
+    uint64_t *pud;
+    uint64_t *pmd;
+    uint64_t va;
+    uint64_t mfn;
+
+    flags |= _PAGE_PSE;
+    for (mfn = start; mfn < (start + count); mfn += PMD_COUNT_64) {
+        va = va_start + frame_to_addr(mfn-start);
+
+        pgd = find_pgd(va, read_cr3_mfn(cpu), 1, 1);
+        pud = find_pud(va, get_pgframe_64(*pgd), 1);
+        pmd = find_pmd(va, get_pgframe_64(*pud), 0);
+        *pmd = get_pgentry_64(mfn, flags);
+    }
+    return 0;
+}
+
+static void map_one_page(struct xen_cpu *cpu, uint64_t va, uint64_t maddr,
+                         int flags, int sync)
+{
+    uint64_t mfn = addr_to_frame(maddr);
+    uint64_t *pgd;
+    uint64_t *pud;
+    uint64_t *pmd;
+    uint64_t *pte;
+
+    pgd = find_pgd(va, read_cr3_mfn(cpu), 1, sync);
+    pud = find_pud(va, get_pgframe_64(*pgd), 1);
+    pmd = find_pmd(va, get_pgframe_64(*pud), 1);
+    if (*pmd & _PAGE_PSE) {
+        *pmd = 0;
+        pmd = find_pmd(va, get_pgframe_64(*pud), 1);
+    }
+    pte = find_pte(va, get_pgframe_64(*pmd));
+    *pte = get_pgentry_64(mfn, flags);
+}
+
+void map_region(struct xen_cpu *cpu, uint64_t va, uint32_t flags,
+                uint64_t start, uint64_t count)
+{
+    uint64_t maddr = frame_to_addr(start);
+    uint64_t maddr_end = maddr + frame_to_addr(count);
+
+    for (; maddr < maddr_end; maddr += PAGE_SIZE, va += PAGE_SIZE) {
+        map_one_page(cpu, va, maddr, flags, 0);
+    }
+}
+
+void *fixmap_page(struct xen_cpu *cpu, uint64_t maddr)
+{
+    static int fixmap_slot = 0;
+    uint32_t off = addr_offset(maddr);
+    uint64_t va;
+
+    va = XEN_MAP_64 + PAGE_SIZE * fixmap_slot++;
+    map_one_page(cpu, va, maddr, EMU_PGFLAGS, 1);
+
+    return (void*)va + off;
+}
+
+void paging_init(struct xen_cpu *cpu)
+{
+    map_region_pse(cpu, XEN_RAM_64, EMU_PGFLAGS,    0,              vmconf.pg_total);
+    map_region_pse(cpu, XEN_M2P_64, M2P_PGFLAGS_64, vmconf.mfn_m2p, vmconf.pg_m2p);
+    m2p = (void*)XEN_M2P_64;
+}
-- 
1.6.0.2

  parent reply	other threads:[~2010-11-01 15:02 UTC|newest]

Thread overview: 96+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-11-01 15:01 [Qemu-devel] [PATCH 00/40] RFC: Xenner Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 01/40] elf: Move translate_fn to helper struct Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 02/40] elf: Add notes implementation Alexander Graf
2010-11-01 18:29   ` Blue Swirl
2010-11-01 18:42     ` Stefan Weil
2010-11-01 19:51       ` Alexander Graf
2010-11-01 20:19         ` Stefan Weil
2010-11-01 21:17           ` Alexander Graf
2010-11-01 21:28             ` [Qemu-devel] " Paolo Bonzini
2010-11-01 21:31             ` [Qemu-devel] " Stefan Weil
2010-11-02 10:17             ` Michael Matz
2010-11-01 18:41   ` [Qemu-devel] " Paolo Bonzini
2010-11-01 18:52     ` Alexander Graf
2010-11-01 19:43       ` Paolo Bonzini
2010-11-01 19:48         ` Alexander Graf
2010-11-01 21:23           ` Paolo Bonzini
2010-11-01 15:01 ` [Qemu-devel] [PATCH 03/40] elf: add header notification Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 04/40] elf: add section analyzer Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 05/40] xen-disk: disable aio Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 06/40] qdev-ify: xen backends Alexander Graf
2010-11-02 10:08   ` Markus Armbruster
2010-11-02 10:43     ` Gerd Hoffmann
2010-11-02 13:26       ` Markus Armbruster
2010-11-01 15:01 ` [Qemu-devel] [PATCH 07/40] xenner: kernel: 32 bit files Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 08/40] xenner: kernel: 64-bit files Alexander Graf
2010-11-01 15:44   ` Anthony Liguori
2010-11-01 15:47     ` Alexander Graf
2010-11-01 15:59       ` Anthony Liguori
2010-11-01 19:00       ` Blue Swirl
2010-11-01 19:02         ` Anthony Liguori
2010-11-01 19:05           ` Alexander Graf
2010-11-01 19:23             ` Blue Swirl
2010-11-01 19:37             ` Anthony Liguori
2010-11-01 15:01 ` [Qemu-devel] [PATCH 09/40] xenner: kernel: Global data Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 10/40] xenner: kernel: Hypercall handler (i386) Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 11/40] xenner: kernel: Hypercall handler (x86_64) Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 12/40] xenner: kernel: Hypercall handler (generic) Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 13/40] xenner: kernel: Headers Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 14/40] xenner: kernel: Instruction emulator Alexander Graf
2010-11-01 15:41   ` malc
2010-11-01 18:46   ` [Qemu-devel] " Paolo Bonzini
2010-11-01 15:01 ` [Qemu-devel] [PATCH 15/40] xenner: kernel: lapic code Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 16/40] xenner: kernel: Main (i386) Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 17/40] xenner: kernel: Main (x86_64) Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 18/40] xenner: kernel: Main Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 19/40] xenner: kernel: Makefile Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 20/40] xenner: kernel: mmu support for 32-bit PAE Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 21/40] xenner: kernel: mmu support for 32-bit normal Alexander Graf
2010-11-01 15:01 ` Alexander Graf [this message]
2010-11-01 15:01 ` [Qemu-devel] [PATCH 23/40] xenner: kernel: generic MM functionality Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 24/40] xenner: kernel: printk Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 25/40] xenner: kernel: KVM PV code Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 26/40] xenner: kernel: xen-names Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 27/40] xenner: add xc_dom.h Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 28/40] xenner: libxc emu: evtchn Alexander Graf
2010-11-01 15:45   ` Anthony Liguori
2010-11-01 15:49     ` Alexander Graf
2010-11-01 16:01       ` Anthony Liguori
2010-11-01 16:07         ` Alexander Graf
2010-11-01 16:14           ` Anthony Liguori
2010-11-01 16:15             ` Alexander Graf
2010-11-01 19:39         ` [Qemu-devel] " Paolo Bonzini
2010-11-01 19:41           ` Anthony Liguori
2010-11-01 19:47             ` Alexander Graf
2010-11-01 20:32               ` Anthony Liguori
2010-11-01 21:47                 ` Paolo Bonzini
2010-11-01 22:00                   ` Anthony Liguori
2010-11-01 22:08                     ` Paolo Bonzini
2010-11-01 22:29                       ` Anthony Liguori
2010-11-02  4:33                 ` Stefano Stabellini
2010-11-02 10:06                   ` Paolo Bonzini
2010-11-02 10:31                     ` Gerd Hoffmann
2010-11-02 10:38                       ` Paolo Bonzini
2010-11-02 13:55                     ` Stefano Stabellini
2010-11-02 15:48                       ` Alexander Graf
2010-11-02 19:20                         ` Stefano Stabellini
2010-11-01 15:01 ` [Qemu-devel] [PATCH 29/40] xenner: libxc emu: grant tables Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 30/40] xenner: libxc emu: memory mapping Alexander Graf
2010-11-01 15:12   ` malc
2010-11-01 15:15     ` Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 31/40] xenner: libxc emu: xenstore Alexander Graf
2010-11-01 18:36   ` Blue Swirl
2010-11-01 15:01 ` [Qemu-devel] [PATCH 32/40] xenner: emudev Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 33/40] xenner: core Alexander Graf
2010-11-01 15:13   ` malc
2010-11-01 15:01 ` [Qemu-devel] [PATCH 34/40] xenner: PV machine Alexander Graf
2010-11-01 15:01 ` [Qemu-devel] [PATCH 35/40] xenner: Domain Builder Alexander Graf
2010-11-02 10:09   ` [Qemu-devel] " Paolo Bonzini
2010-11-02 15:36     ` Alexander Graf
2010-11-02 15:51       ` Paolo Bonzini
2010-11-02 16:28         ` Alexander Graf
2010-11-01 15:21 ` [Qemu-devel] [PATCH 00/40] RFC: Xenner Alexander Graf
2010-11-02 16:26 ` [Qemu-devel] [PATCH 36/40] xen: only create dummy env when necessary Alexander Graf
2010-11-02 16:26 ` [Qemu-devel] [PATCH 38/40] xenner: integrate into build system Alexander Graf
2010-11-02 16:26 ` [Qemu-devel] [PATCH 39/40] xenner: integrate into xen pv machine Alexander Graf
2010-11-02 16:26 ` [Qemu-devel] [PATCH 40/40] xen: add sysrq support Alexander Graf

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=1288623713-28062-23-git-send-email-agraf@suse.de \
    --to=agraf@suse.de \
    --cc=kraxel@redhat.com \
    --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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.