From: Brian Cain <brian.cain@oss.qualcomm.com>
To: qemu-devel@nongnu.org
Subject: Re: [PATCH 28/38] target/hexagon: Initialize htid, modectl regs
Date: Mon, 1 Sep 2025 19:56:14 -0500 [thread overview]
Message-ID: <04f31a1c-e1a1-40fd-a892-2a149ac4998e@oss.qualcomm.com> (raw)
In-Reply-To: <IA0PR02MB9486FD682111B54F09E6296FBED82@IA0PR02MB9486.namprd02.prod.outlook.com>
On 3/20/2025 12:38 PM, Sid Manning wrote:
>
>> -----Original Message-----
>> From: Richard Henderson <richard.henderson@linaro.org>
>> Sent: Thursday, March 20, 2025 10:34 AM
>> To: Sid Manning <sidneym@quicinc.com>; ltaylorsimpson@gmail.com;
>> 'Philippe Mathieu-Daudé' <philmd@linaro.org>; 'Brian Cain'
>> <brian.cain@oss.qualcomm.com>; qemu-devel@nongnu.org
>> Cc: Matheus Bernardino (QUIC) <quic_mathbern@quicinc.com>;
>> ale@rev.ng; anjo@rev.ng; Marco Liebel (QUIC)
>> <quic_mliebel@quicinc.com>; alex.bennee@linaro.org; Mark Burton (QUIC)
>> <quic_mburton@quicinc.com>; Brian Cain <bcain@quicinc.com>
>> Subject: Re: [PATCH 28/38] target/hexagon: Initialize htid, modectl regs
>>
>> WARNING: This email originated from outside of Qualcomm. Please be wary
>> of any links or attachments, and do not enable macros.
>>
>> On 3/19/25 09:08, Sid Manning wrote:
>>>
>>>> -----Original Message-----
>>>> From: Richard Henderson <richard.henderson@linaro.org>
>>>> Sent: Thursday, March 13, 2025 2:07 PM
>>>> To: ltaylorsimpson@gmail.com; 'Philippe Mathieu-Daudé'
>>>> <philmd@linaro.org>; 'Brian Cain' <brian.cain@oss.qualcomm.com>;
>>>> qemu- devel@nongnu.org
>>>> Cc: Matheus Bernardino (QUIC) <quic_mathbern@quicinc.com>;
>>>> ale@rev.ng; anjo@rev.ng; Marco Liebel (QUIC)
>>>> <quic_mliebel@quicinc.com>; alex.bennee@linaro.org; Mark Burton
>>>> (QUIC) <quic_mburton@quicinc.com>; Sid Manning
>> <sidneym@quicinc.com>;
>>>> Brian Cain <bcain@quicinc.com>
>>>> Subject: Re: [PATCH 28/38] target/hexagon: Initialize htid, modectl
>>>> regs
>>>>
>>>> WARNING: This email originated from outside of Qualcomm. Please be
>>>> wary of any links or attachments, and do not enable macros.
>>>>
>>>> On 3/13/25 11:47, ltaylorsimpson@gmail.com wrote:
>>>>> What we are trying to model is an instance of a Hexagon that has a
>>>>> number
>>>> of threads and some resources that are shared. The shared resources
>>>> include the TLB and global S registers. The initial thought was to
>>>> tie the shared resources to the thread with cpu_index == 0. If we
>>>> were to model a Qualcomm SoC, there would be multiple ARM cores and
>>>> multiple Hexagon instances. Each Hexagon instance would have
>>>> distinct shared resources. So, you are correct that using cpu_index is not
>> going to scale.
>>>>> What is the recommended way to model this? I see a "nr_threads"
>>>>> field in
>>>> CPUCore but no clear way to find the threads. PPC has some cores
>>>> that add a "threads" field. Should we follow this approach?
>>>>
>>>> I recommend that the shared resources be modeled as a separate
>>>> Object, which is linked via object_property_add_link to all of the cpus that
>> use it.
>>> [Sid Manning]
>>> Hi Richard,
>>> An example of shared resources would be the system registers. They are
>> broken down into 2 regions. Each thread has its own copy of system
>> registers 0-15 while registers 16-63 are global. Right now CPUHexagonState
>> contains a pointer, g_sreg which points back to cpu[0]'s state thus keeping
>> one copy of the global registers, accesses are done with BQL held to avoid
>> race conditions.
>>> Your suggestion is to create a new object to represent the set of global
>> system registers, I tried this:
>>> #define TYPE_HEXAGON_G_SREG "hexagon.global_sreg"
>>> OBJECT_DECLARE_SIMPLE_TYPE(HexagonGlobalSREGState,
>> HEXAGON_G_SREG)
>>> struct HexagonGlobalSREGState {
>>> SysBusDevice parent_obj;
>> SysBusDevice is more than you need -- Object is sufficient here.
> [Sid Manning]
> Thanks! Will change that to Object.
>
>>> uint32_t regs[64];
>>> };
>>>
>>> In our virtual machine init:
>>> vms->g_sreg =
>> HEXAGON_G_SREG(qdev_new(TYPE_HEXAGON_G_SREG));
>>> and
>>> object_property_set_link(OBJECT(cpu), "global-sreg",
>>> OBJECT(vms->g_sreg), &error_abort);
>>>
>>> to attach the global regs to the cpu, but the above doesn't update cpu
>> elements the same way calls to qdev_prop_set_uint32 will do,
>> object_property_set_link doesn’t error out and returns true.
>>
>> Did you add the DEFINE_PROP_LINK to match? I'd expect something like
>>
>> DEFINE_PROP_LINK("global-sreg", HexagonCPU, g_sreg,
>> TYPE_HEXAGON_G_SREG, HexagonGlobalSREGState *),
>>
>> Beyond that, I guess I'd have to see an actual patch to work out what's
>> wrong.
> [Sid Manning]
> Yes, PROP_LINK above is almost exactly what I added.
>
> Below is a patch representing what I tried. I hoped that cpu->global_sreg would be updated after the call to object_property_set_link but it was not, in the patch below I manually set it.
>
> diff --git a/hw/hexagon/sysreg.h b/hw/hexagon/sysreg.h
> new file mode 100644
> index 0000000000..d7204896cf
> --- /dev/null
> +++ b/hw/hexagon/sysreg.h
> @@ -0,0 +1,47 @@
> +/*
> + * Hexagon system reg
> + * FIXME
> + */
> +
> +#ifndef HW_HEXAGON_HART_H
> +#define HW_HEXAGON_HART_H
> +#if !defined(CONFIG_USER_ONLY)
> +#include "hw/sysbus.h"
> +#include "qom/object.h"
> +
> +#define NUM_SREGS 64
> +struct HexagonGlobalSREGState {
> + struct Object parent_obj;
> + uint32_t regs[NUM_SREGS];
> +};
> +
> +#define TYPE_HEXAGON_G_SREG "hexagon.global_sreg"
> +OBJECT_DECLARE_SIMPLE_TYPE(HexagonGlobalSREGState, HEXAGON_G_SREG)
> +
> +static void hexagon_global_sreg_init(Object *obj)
> +{
> + HexagonGlobalSREGState *s = HEXAGON_G_SREG(obj);
> + /*
> + * The first 16 registers are thread local and should not come from
> + * this structure
> + */
> + for (int i = 0; i < 16; i++) {
> + s->regs[i] = 0xffffffff;
> + }
> +}
> +
> +static const TypeInfo hexagon_sreg_info = {
> + .name = TYPE_HEXAGON_G_SREG,
> + .parent = TYPE_DEVICE,
> + .instance_size = sizeof(struct HexagonGlobalSREGState),
> + .instance_init = hexagon_global_sreg_init,
> +};
> +
> +__attribute__ ((unused))
> +static void hexagon_sreg_register_types(void)
> +{
> + type_register_static(&hexagon_sreg_info);
> +}
> +#endif
> +#endif
> +
> diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c
> index 1e7ac4e5b7..d2d599ac1d 100644
> --- a/hw/hexagon/virt.c
> +++ b/hw/hexagon/virt.c
> @@ -10,12 +10,14 @@
> #include "hw/char/pl011.h"
> #include "hw/core/sysbus-fdt.h"
> #include "hw/hexagon/hexagon.h"
> +#include "hw/hexagon/sysreg.h"
> #include "hw/hexagon/virt.h"
> #include "hw/loader.h"
> #include "hw/qdev-properties.h"
> #include "hw/register.h"
> #include "hw/timer/qct-qtimer.h"
> #include "qemu/error-report.h"
> +#include "qapi/error.h"
> #include "qemu/guest-random.h"
> #include "qemu/units.h"
> #include "elf.h"
> @@ -335,6 +337,7 @@ static void virt_init(MachineState *ms)
> cpu_model = HEXAGON_CPU_TYPE_NAME("v73");
> }
>
> + vms->g_sreg = HEXAGON_G_SREG(qdev_new(TYPE_HEXAGON_G_SREG));
> HexagonCPU *cpu_0 = NULL;
> for (int i = 0; i < ms->smp.cpus; i++) {
> HexagonCPU *cpu = HEXAGON_CPU(object_new(ms->cpu_type));
> @@ -356,6 +359,14 @@ static void virt_init(MachineState *ms)
> qdev_prop_set_uint32(DEVICE(cpu), "qtimer-base-addr", m_cfg->qtmr_region);
> qdev_prop_set_uint32(DEVICE(cpu), "jtlb-entries",
> m_cfg->cfgtable.jtlb_size_entries);
> + bool rc = object_property_set_link(OBJECT(cpu), "global-sreg",
> + OBJECT(vms->g_sreg), &error_abort);
> + g_assert(rc == true);
> +
> + /* This is doing what I think object_property_set_link should do.*/
> + cpu->global_sreg = vms->g_sreg;
> +
> +
>
> if (!qdev_realize_and_unref(DEVICE(cpu), NULL, errp)) {
> return;
> @@ -413,3 +424,5 @@ static const TypeInfo virt_machine_types[] = { {
> } };
>
> DEFINE_TYPES(virt_machine_types)
> +
> +type_init(hexagon_sreg_register_types)
> diff --git a/include/hw/hexagon/virt.h b/include/hw/hexagon/virt.h
> index 0c165a786d..dcd09d50b1 100644
> --- a/include/hw/hexagon/virt.h
> +++ b/include/hw/hexagon/virt.h
> @@ -9,6 +9,7 @@
> #define HW_HEXAGONVIRT_H
>
> #include "hw/boards.h"
> +#include "hw/hexagon/sysreg.h"
> #include "target/hexagon/cpu.h"
>
> struct HexagonVirtMachineState {
> @@ -22,6 +23,7 @@ struct HexagonVirtMachineState {
> MemoryRegion tcm;
> MemoryRegion vtcm;
> DeviceState *l2vic;
> + HexagonGlobalSREGState *g_sreg;
> };
>
> void hexagon_load_fdt(const struct HexagonVirtMachineState *vms);
> diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
> index c649aef99e..9773ee0be8 100644
> --- a/target/hexagon/cpu.c
> +++ b/target/hexagon/cpu.c
> @@ -80,6 +80,8 @@ static const Property hexagon_cpu_properties[] = {
> DEFINE_PROP_UINT32("exec-start-addr", HexagonCPU, boot_addr, 0xffffffffULL),
> DEFINE_PROP_UINT64("config-table-addr", HexagonCPU, config_table_addr,
> 0xffffffffULL),
> + DEFINE_PROP_LINK("global-sreg", HexagonCPU, global_sreg,
> + TYPE_HEXAGON_G_SREG, HexagonGlobalSREGState *),
> #endif
> DEFINE_PROP_UINT32("dsp-rev", HexagonCPU, rev_reg, 0),
> DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false),
> @@ -378,6 +380,11 @@ static void hexagon_cpu_reset_hold(Object *obj, ResetType type)
> CPUState *cs = CPU(obj);
> HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(obj);
> CPUHexagonState *env = cpu_env(cs);
> +#ifndef CONFIG_USER_ONLY
> + HexagonCPU *cpu = HEXAGON_CPU(cs);
> + env->g_sreg = cpu->global_sreg->regs;
> +#endif
> +
>
> if (mcc->parent_phases.hold) {
> mcc->parent_phases.hold(obj, type);
> @@ -389,11 +396,6 @@ static void hexagon_cpu_reset_hold(Object *obj, ResetType type)
> set_float_default_nan_pattern(0b11111111, &env->fp_status);
>
> #ifndef CONFIG_USER_ONLY
> - HexagonCPU *cpu = HEXAGON_CPU(cs);
> -
> - if (cs->cpu_index == 0) {
> - memset(env->g_sreg, 0, sizeof(target_ulong) * NUM_SREGS);
> - }
> memset(env->t_sreg, 0, sizeof(target_ulong) * NUM_SREGS);
> memset(env->greg, 0, sizeof(target_ulong) * NUM_GREGS);
>
> @@ -468,13 +470,6 @@ static void hexagon_cpu_realize(DeviceState *dev, Error **errp)
> CPUHexagonState *env = cpu_env(cs);
> #ifndef CONFIG_USER_ONLY
> hex_mmu_realize(env);
> - if (cs->cpu_index == 0) {
> - env->g_sreg = g_new0(target_ulong, NUM_SREGS);
> - } else {
> - CPUState *cpu0 = qemu_get_cpu(0);
> - CPUHexagonState *env0 = cpu_env(cpu0);
> - env->g_sreg = env0->g_sreg;
> - }
> #endif
> if (cs->cpu_index == 0) {
> env->g_pcycle_base = g_malloc0(sizeof(*env->g_pcycle_base));
> diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
> index 8b334068e2..716dd8253b 100644
> --- a/target/hexagon/cpu.h
> +++ b/target/hexagon/cpu.h
> @@ -19,10 +19,10 @@
> #define HEXAGON_CPU_H
>
> #include "fpu/softfloat-types.h"
> +#include "hw/hexagon/sysreg.h"
>
> #define NUM_GREGS 32
> #define GREG_WRITES_MAX 32
> -#define NUM_SREGS 64
> #define SREG_WRITES_MAX 64
>
> #include "cpu-qom.h"
> @@ -199,6 +199,7 @@ struct ArchCPU {
> uint32_t hvx_contexts;
> uint32_t boot_addr;
> uint64_t config_table_addr;
> + HexagonGlobalSREGState *global_sreg;
> #endif
> };
>
>
I think the re-design of the global registers present in v2 should be a
satisfactory resolution to this thread but please let me know if that's
not the case.
>
next prev parent reply other threads:[~2025-09-02 3:57 UTC|newest]
Thread overview: 120+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-01 5:25 [PATCH 00/38] hexagon system emu, part 1/3 Brian Cain
2025-03-01 5:25 ` [PATCH 01/38] docs: Add hexagon sysemu docs Brian Cain
2025-03-05 19:29 ` ltaylorsimpson
2025-03-01 5:25 ` [PATCH 02/38] docs/system: Add hexagon CPU emulation Brian Cain
2025-03-05 19:36 ` ltaylorsimpson
2025-03-05 20:12 ` Brian Cain
2025-03-05 21:21 ` ltaylorsimpson
2025-03-05 21:28 ` Brian Cain
2025-03-01 5:25 ` [PATCH 03/38] target/hexagon: Add System/Guest register definitions Brian Cain
2025-03-06 20:54 ` ltaylorsimpson
2025-04-16 17:54 ` ltaylorsimpson
2025-04-16 19:43 ` Brian Cain
2025-04-16 22:02 ` ltaylorsimpson
2025-09-02 0:17 ` Brian Cain
2025-03-01 5:25 ` [PATCH 04/38] target/hexagon: Make gen_exception_end_tb non-static Brian Cain
2025-03-06 20:55 ` ltaylorsimpson
2025-03-01 5:25 ` [PATCH 05/38] target/hexagon: Switch to tag_ignore(), generate via get_{user, sys}_tags() Brian Cain via
2025-03-06 21:07 ` ltaylorsimpson
2025-03-01 5:25 ` [PATCH 06/38] target/hexagon: Add privilege check, use tag_ignore() Brian Cain
2025-03-06 21:11 ` ltaylorsimpson
2025-03-06 22:01 ` Richard Henderson
2025-09-02 0:24 ` Brian Cain
2025-03-01 5:25 ` [PATCH 07/38] target/hexagon: Add a placeholder fp exception Brian Cain
2025-03-06 21:22 ` ltaylorsimpson
2025-03-01 5:25 ` [PATCH 08/38] target/hexagon: Add guest, system reg number defs Brian Cain
2025-03-06 21:30 ` ltaylorsimpson
2025-03-08 0:35 ` Sid Manning
2025-09-02 0:25 ` Brian Cain
2025-03-01 5:25 ` [PATCH 09/38] target/hexagon: Add guest, system reg number state Brian Cain
2025-03-06 21:32 ` ltaylorsimpson
2025-03-12 19:15 ` Philippe Mathieu-Daudé
2025-09-02 0:27 ` Brian Cain
2025-03-01 5:26 ` [PATCH 10/38] target/hexagon: Add TCG values for sreg, greg Brian Cain
2025-03-06 21:38 ` ltaylorsimpson
2025-09-02 0:28 ` Brian Cain
2025-03-01 5:26 ` [PATCH 11/38] target/hexagon: Add guest/sys reg writes to DisasContext Brian Cain
2025-03-06 21:40 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 12/38] target/hexagon: Add imported macro, attr defs for sysemu Brian Cain
2025-03-07 19:01 ` ltaylorsimpson
2025-09-02 0:36 ` Brian Cain
2025-03-01 5:26 ` [PATCH 13/38] target/hexagon: Define DCache states Brian Cain
2025-03-07 19:03 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 14/38] target/hexagon: Add new macro definitions for sysemu Brian Cain
2025-03-07 19:35 ` ltaylorsimpson
2025-09-02 0:38 ` Brian Cain
2025-03-01 5:26 ` [PATCH 15/38] target/hexagon: Add handlers for guest/sysreg r/w Brian Cain
2025-03-07 19:46 ` ltaylorsimpson
2025-09-02 0:40 ` Brian Cain
2025-03-01 5:26 ` [PATCH 16/38] target/hexagon: Add placeholder greg/sreg r/w helpers Brian Cain
2025-03-07 20:45 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 17/38] target/hexagon: Add vmstate representation Brian Cain
2025-03-07 21:19 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 18/38] target/hexagon: Make A_PRIV, "J2_trap*" insts need_env() Brian Cain
2025-03-07 21:20 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 19/38] target/hexagon: Define register fields for system regs Brian Cain
2025-03-07 21:21 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 20/38] target/hexagon: Implement do_raise_exception() Brian Cain
2025-03-07 21:28 ` ltaylorsimpson
2025-09-02 0:41 ` Brian Cain
2025-03-01 5:26 ` [PATCH 21/38] target/hexagon: Add system reg insns Brian Cain
2025-03-08 1:32 ` ltaylorsimpson
2025-09-02 0:44 ` Brian Cain
2025-03-01 5:26 ` [PATCH 22/38] target/hexagon: Add sysemu TCG overrides Brian Cain
2025-03-08 1:43 ` ltaylorsimpson
2025-09-02 0:46 ` Brian Cain
2025-03-01 5:26 ` [PATCH 23/38] target/hexagon: Add implicit attributes to sysemu macros Brian Cain
2025-03-11 22:30 ` ltaylorsimpson
2025-09-02 0:47 ` Brian Cain
2025-03-01 5:26 ` [PATCH 24/38] target/hexagon: Add TCG overrides for int handler insts Brian Cain
2025-03-08 1:46 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 25/38] target/hexagon: Add TCG overrides for thread ctl Brian Cain
2025-03-08 1:47 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 26/38] target/hexagon: Add TCG overrides for rte, nmi Brian Cain
2025-03-11 22:33 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 27/38] target/hexagon: Add sreg_{read,write} helpers Brian Cain
2025-03-11 23:22 ` ltaylorsimpson
2025-09-02 0:53 ` Brian Cain
2025-03-01 5:26 ` [PATCH 28/38] target/hexagon: Initialize htid, modectl regs Brian Cain
2025-03-11 23:26 ` ltaylorsimpson
2025-03-12 14:02 ` Sid Manning
2025-03-12 19:19 ` Philippe Mathieu-Daudé
2025-03-12 23:10 ` Brian Cain
2025-03-12 23:40 ` Philippe Mathieu-Daudé
2025-03-13 18:47 ` ltaylorsimpson
2025-03-13 19:06 ` Richard Henderson
2025-03-19 16:08 ` Sid Manning
2025-03-20 15:34 ` Richard Henderson
2025-03-20 17:38 ` Sid Manning
2025-09-02 0:56 ` Brian Cain [this message]
2025-03-01 5:26 ` [PATCH 29/38] target/hexagon: Add locks, id, next_PC to state Brian Cain
2025-03-11 23:33 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 30/38] target/hexagon: Add a TLB count property Brian Cain
2025-03-11 23:41 ` ltaylorsimpson
2025-03-12 14:01 ` Sid Manning
2025-03-01 5:26 ` [PATCH 31/38] target/hexagon: Add {TLB, k0}lock, cause code, wait_next_pc Brian Cain via
2025-03-11 23:44 ` ltaylorsimpson
2025-03-12 16:58 ` [PATCH 31/38] target/hexagon: Add {TLB,k0}lock, " Sid Manning
2025-03-01 5:26 ` [PATCH 32/38] target/hexagon: Add stubs for modify_ssr/get_exe_mode Brian Cain
2025-03-11 23:43 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 33/38] target/hexagon: Add gdb support for sys regs Brian Cain
2025-03-12 16:27 ` ltaylorsimpson
2025-03-12 19:10 ` Sid Manning
2025-03-12 19:27 ` Sid Manning
2025-03-12 19:46 ` Matheus Tavares Bernardino
2025-09-02 1:15 ` Brian Cain
2025-03-01 5:26 ` [PATCH 34/38] target/hexagon: Add initial MMU model Brian Cain
2025-03-12 17:04 ` ltaylorsimpson
2025-09-02 1:20 ` Brian Cain
2025-03-12 19:20 ` Philippe Mathieu-Daudé
2025-03-12 21:15 ` Sid Manning
2025-03-12 23:32 ` Philippe Mathieu-Daudé
2025-03-01 5:26 ` [PATCH 35/38] target/hexagon: Add IRQ events Brian Cain
2025-03-12 17:06 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 36/38] target/hexagon: Add clear_wait_mode() definition Brian Cain
2025-03-12 17:08 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 37/38] target/hexagon: Define f{S,G}ET_FIELD macros Brian Cain
2025-03-12 17:11 ` ltaylorsimpson
2025-03-01 5:26 ` [PATCH 38/38] target/hexagon: Add hex_interrupts support Brian Cain
2025-03-12 17:32 ` ltaylorsimpson
2025-09-02 1:22 ` Brian Cain
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=04f31a1c-e1a1-40fd-a892-2a149ac4998e@oss.qualcomm.com \
--to=brian.cain@oss.qualcomm.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 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).