All of lore.kernel.org
 help / color / mirror / Atom feed
From: Luc Michel <luc@lmichel.fr>
To: Peter Maydell <peter.maydell@linaro.org>
Cc: Damien Hedde <damien.hedde@greensocs.com>,
	Alistair Francis <alistair@alistair23.me>,
	qemu-devel@nongnu.org, Subbaraya Sundeep <sundeep.lkml@gmail.com>,
	qemu-arm@nongnu.org, Joel Stanley <joel@jms.id.au>,
	Alexandre Iooss <erdnaxe@crans.org>
Subject: Re: [PATCH for-6.2 02/25] arm: Move systick device creation from NVIC to ARMv7M object
Date: Tue, 17 Aug 2021 11:24:18 +0200	[thread overview]
Message-ID: <20210817092418.b5l2qbj3cthg7dhi@sekoia-pc.home.lmichel.fr> (raw)
In-Reply-To: <20210812093356.1946-3-peter.maydell@linaro.org>

On 10:33 Thu 12 Aug     , Peter Maydell wrote:
> There's no particular reason why the NVIC should be owning the
> SysTick device objects; move them into the ARMv7M container object
> instead, as part of consolidating the "create the devices which are
> built into an M-profile CPU and map them into their architected
> locations in the address space" work into one place.
> 
> This involves temporarily creating a duplicate copy of the
> nvic_sysreg_ns_ops struct and its read/write functions (renamed as
> v7m_sysreg_ns_*), but we will delete the NVIC's copy of this code in
> a subsequent patch.
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Reviewed-by: Luc Michel <luc@lmichel.fr>

> ---
>  include/hw/arm/armv7m.h       |  12 ++++
>  include/hw/intc/armv7m_nvic.h |   4 --
>  hw/arm/armv7m.c               | 125 ++++++++++++++++++++++++++++++++++
>  hw/intc/armv7m_nvic.c         |  73 --------------------
>  4 files changed, 137 insertions(+), 77 deletions(-)
> 
> diff --git a/include/hw/arm/armv7m.h b/include/hw/arm/armv7m.h
> index 4cae0d7eeaa..360c35c5fb2 100644
> --- a/include/hw/arm/armv7m.h
> +++ b/include/hw/arm/armv7m.h
> @@ -60,11 +60,23 @@ struct ARMv7MState {
>      BitBandState bitband[ARMV7M_NUM_BITBANDS];
>      ARMCPU *cpu;
>      ARMv7MRAS ras;
> +    SysTickState systick[M_REG_NUM_BANKS];
>  
>      /* MemoryRegion we pass to the CPU, with our devices layered on
>       * top of the ones the board provides in board_memory.
>       */
>      MemoryRegion container;
> +    /*
> +     * MemoryRegion which passes the transaction to either the S or the
> +     * NS systick device depending on the transaction attributes
> +     */
> +    MemoryRegion systickmem;
> +    /*
> +     * MemoryRegion which enforces the S/NS handling of the systick
> +     * device NS alias region and passes the transaction to the
> +     * NS systick device if appropriate.
> +     */
> +    MemoryRegion systick_ns_mem;
>  
>      /* Properties */
>      char *cpu_type;
> diff --git a/include/hw/intc/armv7m_nvic.h b/include/hw/intc/armv7m_nvic.h
> index 33b6d8810c7..6a6a99090c7 100644
> --- a/include/hw/intc/armv7m_nvic.h
> +++ b/include/hw/intc/armv7m_nvic.h
> @@ -81,16 +81,12 @@ struct NVICState {
>  
>      MemoryRegion sysregmem;
>      MemoryRegion sysreg_ns_mem;
> -    MemoryRegion systickmem;
> -    MemoryRegion systick_ns_mem;
>      MemoryRegion container;
>      MemoryRegion defaultmem;
>  
>      uint32_t num_irq;
>      qemu_irq excpout;
>      qemu_irq sysresetreq;
> -
> -    SysTickState systick[M_REG_NUM_BANKS];
>  };
>  
>  #endif
> diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c
> index 8964730d153..364ac069702 100644
> --- a/hw/arm/armv7m.c
> +++ b/hw/arm/armv7m.c
> @@ -124,6 +124,85 @@ static const hwaddr bitband_output_addr[ARMV7M_NUM_BITBANDS] = {
>      0x22000000, 0x42000000
>  };
>  
> +static MemTxResult v7m_sysreg_ns_write(void *opaque, hwaddr addr,
> +                                       uint64_t value, unsigned size,
> +                                       MemTxAttrs attrs)
> +{
> +    MemoryRegion *mr = opaque;
> +
> +    if (attrs.secure) {
> +        /* S accesses to the alias act like NS accesses to the real region */
> +        attrs.secure = 0;
> +        return memory_region_dispatch_write(mr, addr, value,
> +                                            size_memop(size) | MO_TE, attrs);
> +    } else {
> +        /* NS attrs are RAZ/WI for privileged, and BusFault for user */
> +        if (attrs.user) {
> +            return MEMTX_ERROR;
> +        }
> +        return MEMTX_OK;
> +    }
> +}
> +
> +static MemTxResult v7m_sysreg_ns_read(void *opaque, hwaddr addr,
> +                                      uint64_t *data, unsigned size,
> +                                      MemTxAttrs attrs)
> +{
> +    MemoryRegion *mr = opaque;
> +
> +    if (attrs.secure) {
> +        /* S accesses to the alias act like NS accesses to the real region */
> +        attrs.secure = 0;
> +        return memory_region_dispatch_read(mr, addr, data,
> +                                           size_memop(size) | MO_TE, attrs);
> +    } else {
> +        /* NS attrs are RAZ/WI for privileged, and BusFault for user */
> +        if (attrs.user) {
> +            return MEMTX_ERROR;
> +        }
> +        *data = 0;
> +        return MEMTX_OK;
> +    }
> +}
> +
> +static const MemoryRegionOps v7m_sysreg_ns_ops = {
> +    .read_with_attrs = v7m_sysreg_ns_read,
> +    .write_with_attrs = v7m_sysreg_ns_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static MemTxResult v7m_systick_write(void *opaque, hwaddr addr,
> +                                     uint64_t value, unsigned size,
> +                                     MemTxAttrs attrs)
> +{
> +    ARMv7MState *s = opaque;
> +    MemoryRegion *mr;
> +
> +    /* Direct the access to the correct systick */
> +    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0);
> +    return memory_region_dispatch_write(mr, addr, value,
> +                                        size_memop(size) | MO_TE, attrs);
> +}
> +
> +static MemTxResult v7m_systick_read(void *opaque, hwaddr addr,
> +                                    uint64_t *data, unsigned size,
> +                                    MemTxAttrs attrs)
> +{
> +    ARMv7MState *s = opaque;
> +    MemoryRegion *mr;
> +
> +    /* Direct the access to the correct systick */
> +    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0);
> +    return memory_region_dispatch_read(mr, addr, data, size_memop(size) | MO_TE,
> +                                       attrs);
> +}
> +
> +static const MemoryRegionOps v7m_systick_ops = {
> +    .read_with_attrs = v7m_systick_read,
> +    .write_with_attrs = v7m_systick_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
>  static void armv7m_instance_init(Object *obj)
>  {
>      ARMv7MState *s = ARMV7M(obj);
> @@ -137,6 +216,13 @@ static void armv7m_instance_init(Object *obj)
>      object_property_add_alias(obj, "num-irq",
>                                OBJECT(&s->nvic), "num-irq");
>  
> +    object_initialize_child(obj, "systick-reg-ns", &s->systick[M_REG_NS],
> +                            TYPE_SYSTICK);
> +    /*
> +     * We can't initialize the secure systick here, as we don't know
> +     * yet if we need it.
> +     */
> +
>      for (i = 0; i < ARRAY_SIZE(s->bitband); i++) {
>          object_initialize_child(obj, "bitband[*]", &s->bitband[i],
>                                  TYPE_BITBAND);
> @@ -231,6 +317,45 @@ static void armv7m_realize(DeviceState *dev, Error **errp)
>      memory_region_add_subregion(&s->container, 0xe0000000,
>                                  sysbus_mmio_get_region(sbd, 0));
>  
> +    /* Create and map the systick devices */
> +    if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), errp)) {
> +        return;
> +    }
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), 0,
> +                       qdev_get_gpio_in_named(DEVICE(&s->nvic),
> +                                              "systick-trigger", M_REG_NS));
> +
> +    if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY)) {
> +        /*
> +         * We couldn't init the secure systick device in instance_init
> +         * as we didn't know then if the CPU had the security extensions;
> +         * so we have to do it here.
> +         */
> +        object_initialize_child(OBJECT(dev), "systick-reg-s",
> +                                &s->systick[M_REG_S], TYPE_SYSTICK);
> +
> +        if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_S]), errp)) {
> +            return;
> +        }
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_S]), 0,
> +                           qdev_get_gpio_in_named(DEVICE(&s->nvic),
> +                                                  "systick-trigger", M_REG_S));
> +    }
> +
> +    memory_region_init_io(&s->systickmem, OBJECT(s),
> +                          &v7m_systick_ops, s,
> +                          "v7m_systick", 0xe0);
> +
> +    memory_region_add_subregion_overlap(&s->container, 0xe000e010,
> +                                        &s->systickmem, 1);
> +    if (arm_feature(&s->cpu->env, ARM_FEATURE_V8)) {
> +        memory_region_init_io(&s->systick_ns_mem, OBJECT(s),
> +                              &v7m_sysreg_ns_ops, &s->systickmem,
> +                              "v7m_systick_ns", 0xe0);
> +        memory_region_add_subregion_overlap(&s->container, 0xe002e010,
> +                                            &s->systick_ns_mem, 1);
> +    }
> +
>      /* If the CPU has RAS support, create the RAS register block */
>      if (cpu_isar_feature(aa32_ras, s->cpu)) {
>          object_initialize_child(OBJECT(dev), "armv7m-ras",
> diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
> index a5975592dfa..2b3e79a3da9 100644
> --- a/hw/intc/armv7m_nvic.c
> +++ b/hw/intc/armv7m_nvic.c
> @@ -2517,38 +2517,6 @@ static const MemoryRegionOps nvic_sysreg_ns_ops = {
>      .endianness = DEVICE_NATIVE_ENDIAN,
>  };
>  
> -static MemTxResult nvic_systick_write(void *opaque, hwaddr addr,
> -                                      uint64_t value, unsigned size,
> -                                      MemTxAttrs attrs)
> -{
> -    NVICState *s = opaque;
> -    MemoryRegion *mr;
> -
> -    /* Direct the access to the correct systick */
> -    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0);
> -    return memory_region_dispatch_write(mr, addr, value,
> -                                        size_memop(size) | MO_TE, attrs);
> -}
> -
> -static MemTxResult nvic_systick_read(void *opaque, hwaddr addr,
> -                                     uint64_t *data, unsigned size,
> -                                     MemTxAttrs attrs)
> -{
> -    NVICState *s = opaque;
> -    MemoryRegion *mr;
> -
> -    /* Direct the access to the correct systick */
> -    mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0);
> -    return memory_region_dispatch_read(mr, addr, data, size_memop(size) | MO_TE,
> -                                       attrs);
> -}
> -
> -static const MemoryRegionOps nvic_systick_ops = {
> -    .read_with_attrs = nvic_systick_read,
> -    .write_with_attrs = nvic_systick_write,
> -    .endianness = DEVICE_NATIVE_ENDIAN,
> -};
> -
>  /*
>   * Unassigned portions of the PPB space are RAZ/WI for privileged
>   * accesses, and fault for non-privileged accesses.
> @@ -2801,29 +2769,6 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
>  
>      s->num_prio_bits = arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 8 : 2;
>  
> -    if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), errp)) {
> -        return;
> -    }
> -    sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), 0,
> -                       qdev_get_gpio_in_named(dev, "systick-trigger",
> -                                              M_REG_NS));
> -
> -    if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY)) {
> -        /* We couldn't init the secure systick device in instance_init
> -         * as we didn't know then if the CPU had the security extensions;
> -         * so we have to do it here.
> -         */
> -        object_initialize_child(OBJECT(dev), "systick-reg-s",
> -                                &s->systick[M_REG_S], TYPE_SYSTICK);
> -
> -        if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_S]), errp)) {
> -            return;
> -        }
> -        sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_S]), 0,
> -                           qdev_get_gpio_in_named(dev, "systick-trigger",
> -                                                  M_REG_S));
> -    }
> -
>      /*
>       * This device provides a single sysbus memory region which
>       * represents the whole of the "System PPB" space. This is the
> @@ -2877,23 +2822,11 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
>                            "nvic_sysregs", 0x1000);
>      memory_region_add_subregion(&s->container, 0xe000, &s->sysregmem);
>  
> -    memory_region_init_io(&s->systickmem, OBJECT(s),
> -                          &nvic_systick_ops, s,
> -                          "nvic_systick", 0xe0);
> -
> -    memory_region_add_subregion_overlap(&s->container, 0xe010,
> -                                        &s->systickmem, 1);
> -
>      if (arm_feature(&s->cpu->env, ARM_FEATURE_V8)) {
>          memory_region_init_io(&s->sysreg_ns_mem, OBJECT(s),
>                                &nvic_sysreg_ns_ops, &s->sysregmem,
>                                "nvic_sysregs_ns", 0x1000);
>          memory_region_add_subregion(&s->container, 0x2e000, &s->sysreg_ns_mem);
> -        memory_region_init_io(&s->systick_ns_mem, OBJECT(s),
> -                              &nvic_sysreg_ns_ops, &s->systickmem,
> -                              "nvic_systick_ns", 0xe0);
> -        memory_region_add_subregion_overlap(&s->container, 0x2e010,
> -                                            &s->systick_ns_mem, 1);
>      }
>  
>      sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->container);
> @@ -2905,12 +2838,6 @@ static void armv7m_nvic_instance_init(Object *obj)
>      NVICState *nvic = NVIC(obj);
>      SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
>  
> -    object_initialize_child(obj, "systick-reg-ns", &nvic->systick[M_REG_NS],
> -                            TYPE_SYSTICK);
> -    /* We can't initialize the secure systick here, as we don't know
> -     * yet if we need it.
> -     */
> -
>      sysbus_init_irq(sbd, &nvic->excpout);
>      qdev_init_gpio_out_named(dev, &nvic->sysresetreq, "SYSRESETREQ", 1);
>      qdev_init_gpio_in_named(dev, nvic_systick_trigger, "systick-trigger",
> -- 
> 2.20.1
> 

-- 


  parent reply	other threads:[~2021-08-17  9:22 UTC|newest]

Thread overview: 91+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-12  9:33 [PATCH for-6.2 00/25] arm: Get rid of system_clock_scale global Peter Maydell
2021-08-12  9:33 ` [PATCH for-6.2 01/25] arm: Move M-profile RAS register block into its own device Peter Maydell
2021-08-12 11:08   ` Alexandre IOOSS
2021-08-12 11:09     ` Peter Maydell
2021-08-13  0:59   ` Alistair Francis
2021-08-15 17:30   ` Philippe Mathieu-Daudé
2021-08-16  7:28     ` David Hildenbrand
2021-08-16  9:16     ` Peter Maydell
2021-08-17  8:25   ` Luc Michel
2021-08-17 10:10     ` Damien Hedde
2021-08-12  9:33 ` [PATCH for-6.2 02/25] arm: Move systick device creation from NVIC to ARMv7M object Peter Maydell
2021-08-13  1:23   ` Alistair Francis
2021-08-17  9:24   ` Luc Michel [this message]
2021-08-12  9:33 ` [PATCH for-6.2 03/25] arm: Move system PPB container handling to armv7m Peter Maydell
2021-08-12 11:56   ` Alexandre IOOSS
2021-08-17  9:25   ` Luc Michel
2021-08-12  9:33 ` [PATCH for-6.2 04/25] hw/timer/armv7m_systick: Add usual QEMU interface comment Peter Maydell
2021-08-13  1:26   ` Alistair Francis
2021-08-17  9:29   ` Luc Michel
2021-08-12  9:33 ` [PATCH for-6.2 05/25] hw/timer/armv7m_systick: Add input clocks Peter Maydell
2021-08-13  1:27   ` Alistair Francis
2021-08-17  9:32   ` Luc Michel
2021-08-12  9:33 ` [PATCH for-6.2 06/25] hw/arm/armv7m: Create " Peter Maydell
2021-08-13  1:28   ` Alistair Francis
2021-08-17  9:34   ` Luc Michel
2021-08-12  9:33 ` [PATCH for-6.2 07/25] armsse: Wire up systick cpuclk clock Peter Maydell
2021-08-13  1:29   ` Alistair Francis
2021-08-17  9:36   ` Luc Michel
2021-08-12  9:33 ` [PATCH for-6.2 08/25] hw/arm/mps2.c: Connect up armv7m clocks Peter Maydell
2021-08-17  9:39   ` Luc Michel
2021-08-12  9:33 ` [PATCH for-6.2 09/25] clock: Provide builtin multiplier/divider Peter Maydell
2021-08-12 12:08   ` Alexandre IOOSS
2021-08-12 12:22     ` Peter Maydell
2021-08-13  1:33   ` Alistair Francis
2021-08-15 16:32   ` Philippe Mathieu-Daudé
2021-08-16  9:05     ` Peter Maydell
2021-08-16  9:32       ` Philippe Mathieu-Daudé
2021-08-16  9:36         ` Peter Maydell
2021-08-16  9:58           ` Philippe Mathieu-Daudé
2021-08-15 21:27   ` Luc Michel
2021-08-17  9:59   ` Damien Hedde
2021-08-17 10:46     ` Peter Maydell
2021-08-17 14:58       ` Damien Hedde
2021-08-12  9:33 ` [PATCH for-6.2 10/25] hw/arm: Don't allocate separate MemoryRegions in stm32 SoC realize Peter Maydell
2021-08-12 12:13   ` Alexandre IOOSS
2021-08-12 12:27     ` Peter Maydell
2021-08-15 16:37       ` Philippe Mathieu-Daudé
2021-08-13  1:34   ` Alistair Francis
2021-08-17  9:41   ` Luc Michel
2021-08-12  9:33 ` [PATCH for-6.2 11/25] hw/arm/stm32f100: Wire up sysclk and refclk Peter Maydell
2021-08-13  1:36   ` Alistair Francis
2021-08-14  9:01   ` Alexandre IOOSS
2021-08-17  9:45   ` Luc Michel
2021-08-12  9:33 ` [PATCH for-6.2 12/25] hw/arm/stm32f205: " Peter Maydell
2021-08-13  1:38   ` Alistair Francis
2021-08-14  9:02   ` Alexandre IOOSS
2021-08-17  9:47   ` Luc Michel
2021-08-12  9:33 ` [PATCH for-6.2 13/25] hw/arm/stm32f405: " Peter Maydell
2021-08-13  1:37   ` Alistair Francis
2021-08-14  9:03   ` Alexandre IOOSS
2021-08-17  9:47   ` Luc Michel
2021-08-12  9:33 ` [PATCH for-6.2 14/25] hw/arm/stm32vldiscovery: Delete trailing blank line Peter Maydell
2021-08-12 12:27   ` Alexandre IOOSS
2021-08-13  1:39   ` Alistair Francis
2021-08-17  9:48   ` Luc Michel
2021-08-12  9:33 ` [PATCH for-6.2 15/25] hw/arm/nrf51: Wire up sysclk Peter Maydell
2021-08-14  9:06   ` Alexandre IOOSS
2021-08-12  9:33 ` [PATCH for-6.2 16/25] hw/arm/stellaris: split stellaris_sys_init() Peter Maydell
2021-08-14  9:10   ` Alexandre IOOSS
2021-08-12  9:33 ` [PATCH for-6.2 17/25] hw/arm/stellaris: Wire sysclk up to armv7m Peter Maydell
2021-08-14  9:12   ` Alexandre IOOSS
2021-08-12  9:33 ` [PATCH for-6.2 18/25] hw/arm/msf2_soc: Don't allocate separate MemoryRegions Peter Maydell
2021-08-14  9:13   ` Alexandre IOOSS
2021-08-12  9:33 ` [PATCH for-6.2 19/25] hw/arm/msf2: Use Clock input to MSF2_SOC instead of m3clk property Peter Maydell
2021-08-14  9:20   ` Alexandre IOOSS
2021-08-14 10:11     ` Peter Maydell
2021-08-14 10:47       ` Alexandre IOOSS
2021-08-12  9:33 ` [PATCH for-6.2 20/25] hw/arm/msf2-soc: Wire up refclk Peter Maydell
2021-08-17 15:05   ` Damien Hedde
2021-08-12  9:33 ` [PATCH for-6.2 21/25] hw/timer/armv7m_systick: Use clock inputs instead of system_clock_scale Peter Maydell
2021-08-17 15:55   ` Damien Hedde
2021-08-17 15:59     ` Peter Maydell
2021-08-17 16:14       ` Damien Hedde
2021-08-12  9:33 ` [PATCH for-6.2 22/25] hw/arm/stellaris: Fix code style issues in GPTM code Peter Maydell
2021-08-14  9:26   ` Alexandre IOOSS
2021-08-12  9:33 ` [PATCH for-6.2 23/25] hw/arm/stellaris: Split stellaris-gptm into its own file Peter Maydell
2021-08-17 15:59   ` Damien Hedde
2021-08-12  9:33 ` [PATCH for-6.2 24/25] hw/timer/stellaris-gptm: Use Clock input instead of system_clock_scale Peter Maydell
2021-08-18 12:03   ` Damien Hedde
2021-08-12  9:33 ` [PATCH for-6.2 25/25] arm: Remove system_clock_scale global Peter Maydell
2021-08-13  5:08   ` Alistair Francis

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=20210817092418.b5l2qbj3cthg7dhi@sekoia-pc.home.lmichel.fr \
    --to=luc@lmichel.fr \
    --cc=alistair@alistair23.me \
    --cc=damien.hedde@greensocs.com \
    --cc=erdnaxe@crans.org \
    --cc=joel@jms.id.au \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=sundeep.lkml@gmail.com \
    /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.