All of lore.kernel.org
 help / color / mirror / Atom feed
From: Igor Mammedov <imammedo@redhat.com>
To: Gerd Hoffmann <kraxel@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>,
	Richard Henderson <rth@twiddle.net>,
	"Michael S. Tsirkin" <mst@redhat.com>,
	qemu-devel@nongnu.org, Eduardo Habkost <ehabkost@redhat.com>
Subject: Re: [PATCH 1/6] acpi: split hw/i386/acpi-build.c
Date: Fri, 27 Mar 2020 15:17:22 +0100	[thread overview]
Message-ID: <20200327151722.01f6bded@redhat.com> (raw)
In-Reply-To: <20200327121111.1530-2-kraxel@redhat.com>

On Fri, 27 Mar 2020 13:11:06 +0100
Gerd Hoffmann <kraxel@redhat.com> wrote:

> Split hw/i386/acpi-build.c into pieces:
I just suggested slightly different split/generalization on 'microvm: add acpi support' thread

basic idea is to move common arm/pc pieces to hw/acpi/

leave alone acpi_setup() in acpi-build.c as it has too much of legacy/non necessary features
and generalize ARM's acpi_setup() to make it reusable with microvm
(put it somewhere in hw/acpi/)


> 
> acpi-build-internal.h
>   Internal header file for acpi-buid-*.c
> 
> acpi-build-core.c
>   The acpi_setup() function + dependencies.
>   The acpi_build() function simply calls acpi_build_pc() for now,
>   at some point in the future this will dispatch into machine-type
>   specific build functions.
> 
> acpi-build-pc.c
>   The acpi_build_pc() function + dependencies.
>   The bulk of the old acpi-build.c file is here now.
> 
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  hw/i386/acpi-build-internal.h             |  35 ++++
>  hw/i386/acpi-build-core.c                 | 191 ++++++++++++++++++++++
>  hw/i386/{acpi-build.c => acpi-build-pc.c} | 175 +-------------------
>  hw/i386/Makefile.objs                     |   3 +-
>  4 files changed, 230 insertions(+), 174 deletions(-)
>  create mode 100644 hw/i386/acpi-build-internal.h
>  create mode 100644 hw/i386/acpi-build-core.c
>  rename hw/i386/{acpi-build.c => acpi-build-pc.c} (94%)
> 
> diff --git a/hw/i386/acpi-build-internal.h b/hw/i386/acpi-build-internal.h
> new file mode 100644
> index 000000000000..5e783d85dde6
> --- /dev/null
> +++ b/hw/i386/acpi-build-internal.h
> @@ -0,0 +1,35 @@
> +
> +#ifndef HW_I386_ACPI_BUILD_INTERNAL_H
> +#define HW_I386_ACPI_BUILD_INTERNAL_H
> +
> +#include "hw/acpi/aml-build.h"
> +
> +/* #define DEBUG_ACPI_BUILD */
> +#ifdef DEBUG_ACPI_BUILD
> +#define ACPI_BUILD_DPRINTF(fmt, ...)        \
> +    do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define ACPI_BUILD_DPRINTF(fmt, ...)
> +#endif
> +
> +typedef
> +struct AcpiBuildState {
> +    /* Copy of table in RAM (for patching). */
> +    MemoryRegion *table_mr;
> +    /* Is table patched? */
> +    uint8_t patched;
> +    void *rsdp;
> +    MemoryRegion *rsdp_mr;
> +    MemoryRegion *linker_mr;
> +} AcpiBuildState;
> +
> +typedef struct FwCfgTPMConfig {
> +    uint32_t tpmppi_address;
> +    uint8_t tpm_version;
> +    uint8_t tpmppi_version;
> +} QEMU_PACKED FwCfgTPMConfig;

Is it going to be used by microvm?
(at that rate of migrating features we are going to make full PC out of it :) )

> +
> +/* acpi-build-pc.c */
> +void acpi_build_pc(AcpiBuildTables *tables, MachineState *machine);
> +
> +#endif
> diff --git a/hw/i386/acpi-build-core.c b/hw/i386/acpi-build-core.c
> new file mode 100644
> index 000000000000..f0968b4bb594
> --- /dev/null
> +++ b/hw/i386/acpi-build-core.c
> @@ -0,0 +1,191 @@
> +/* Support for generating ACPI tables and passing them to Guests
> + *
> + * Copyright (C) 2008-2010  Kevin O'Connor <kevin@koconnor.net>
> + * Copyright (C) 2006 Fabrice Bellard
> + * Copyright (C) 2013 Red Hat Inc
> + *
> + * Author: Michael S. Tsirkin <mst@redhat.com>
> + *
> + * 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; either version 2 of the License, or
> + * (at your option) any later version.
> +
> + * 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 "qemu/osdep.h"
> +#include "qapi/error.h"
> +
> +#include "exec/memory.h"
> +#include "hw/acpi/acpi.h"
> +#include "hw/acpi/aml-build.h"
> +#include "hw/acpi/bios-linker-loader.h"
> +#include "hw/acpi/tpm.h"
> +#include "hw/acpi/utils.h"
> +#include "hw/acpi/vmgenid.h"
> +#include "hw/boards.h"
> +#include "hw/i386/pc.h"
> +#include "migration/vmstate.h"
> +#include "sysemu/reset.h"
> +#include "sysemu/tpm.h"
> +
> +#include "acpi-build.h"
> +#include "acpi-build-internal.h"
> +
> +static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
> +{
> +    acpi_build_pc(tables, machine);
> +}
> +
> +static void acpi_ram_update(MemoryRegion *mr, GArray *data)
> +{
> +    uint32_t size = acpi_data_len(data);
> +
> +    /* Make sure RAM size is correct - in case it got changed e.g. by migration */
> +    memory_region_ram_resize(mr, size, &error_abort);
> +
> +    memcpy(memory_region_get_ram_ptr(mr), data->data, size);
> +    memory_region_set_dirty(mr, 0, size);
> +}
> +
> +static void acpi_build_update(void *build_opaque)
> +{
> +    AcpiBuildState *build_state = build_opaque;
> +    AcpiBuildTables tables;
> +
> +    /* No state to update or already patched? Nothing to do. */
> +    if (!build_state || build_state->patched) {
> +        return;
> +    }
> +    build_state->patched = 1;
> +
> +    acpi_build_tables_init(&tables);
> +
> +    acpi_build(&tables, MACHINE(qdev_get_machine()));
> +
> +    acpi_ram_update(build_state->table_mr, tables.table_data);
> +
> +    if (build_state->rsdp) {
> +        memcpy(build_state->rsdp, tables.rsdp->data, acpi_data_len(tables.rsdp));
> +    } else {
> +        acpi_ram_update(build_state->rsdp_mr, tables.rsdp);
> +    }
> +
> +    acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob);
> +    acpi_build_tables_cleanup(&tables, true);
> +}
> +
> +static void acpi_build_reset(void *build_opaque)
> +{
> +    AcpiBuildState *build_state = build_opaque;
> +    build_state->patched = 0;
> +}
> +
> +static const VMStateDescription vmstate_acpi_build = {
> +    .name = "acpi_build",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT8(patched, AcpiBuildState),
> +        VMSTATE_END_OF_LIST()
> +    },
> +};
> +
> +void acpi_setup(void)
> +{
> +    PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
> +    PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
> +    X86MachineState *x86ms = X86_MACHINE(pcms);
> +    AcpiBuildTables tables;
> +    AcpiBuildState *build_state;
> +    Object *vmgenid_dev;
> +    TPMIf *tpm;
> +    static FwCfgTPMConfig tpm_config;
> +
> +    if (!x86ms->fw_cfg) {
> +        ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
> +        return;
> +    }
> +
> +    if (!pcms->acpi_build_enabled) {
> +        ACPI_BUILD_DPRINTF("ACPI build disabled. Bailing out.\n");
> +        return;
> +    }
> +
> +    if (!acpi_enabled) {
> +        ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n");
> +        return;
> +    }
> +
> +    build_state = g_malloc0(sizeof *build_state);
> +
> +    acpi_build_tables_init(&tables);
> +    acpi_build(&tables, MACHINE(pcms));
> +
> +    /* Now expose it all to Guest */
> +    build_state->table_mr = acpi_add_rom_blob(acpi_build_update,
> +                                              build_state, tables.table_data,
> +                                              ACPI_BUILD_TABLE_FILE,
> +                                              ACPI_BUILD_TABLE_MAX_SIZE);
> +    assert(build_state->table_mr != NULL);
> +
> +    build_state->linker_mr =
> +        acpi_add_rom_blob(acpi_build_update, build_state,
> +                          tables.linker->cmd_blob, "etc/table-loader", 0);
> +
> +    fw_cfg_add_file(x86ms->fw_cfg, ACPI_BUILD_TPMLOG_FILE,
> +                    tables.tcpalog->data, acpi_data_len(tables.tcpalog));
> +
> +    tpm = tpm_find();
> +    if (tpm && object_property_get_bool(OBJECT(tpm), "ppi", &error_abort)) {
> +        tpm_config = (FwCfgTPMConfig) {
> +            .tpmppi_address = cpu_to_le32(TPM_PPI_ADDR_BASE),
> +            .tpm_version = tpm_get_version(tpm),
> +            .tpmppi_version = TPM_PPI_VERSION_1_30
> +        };
> +        fw_cfg_add_file(x86ms->fw_cfg, "etc/tpm/config",
> +                        &tpm_config, sizeof tpm_config);
> +    }
> +
> +    vmgenid_dev = find_vmgenid_dev();
> +    if (vmgenid_dev) {
> +        vmgenid_add_fw_cfg(VMGENID(vmgenid_dev), x86ms->fw_cfg,
> +                           tables.vmgenid);
> +    }
> +
> +    if (!pcmc->rsdp_in_ram) {
> +        /*
> +         * Keep for compatibility with old machine types.
> +         * Though RSDP is small, its contents isn't immutable, so
> +         * we'll update it along with the rest of tables on guest access.
> +         */
> +        uint32_t rsdp_size = acpi_data_len(tables.rsdp);
> +
> +        build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size);
> +        fw_cfg_add_file_callback(x86ms->fw_cfg, ACPI_BUILD_RSDP_FILE,
> +                                 acpi_build_update, NULL, build_state,
> +                                 build_state->rsdp, rsdp_size, true);
> +        build_state->rsdp_mr = NULL;
> +    } else {
> +        build_state->rsdp = NULL;
> +        build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update,
> +                                                 build_state, tables.rsdp,
> +                                                 ACPI_BUILD_RSDP_FILE, 0);
> +    }
> +
> +    qemu_register_reset(acpi_build_reset, build_state);
> +    acpi_build_reset(build_state);
> +    vmstate_register(NULL, 0, &vmstate_acpi_build, build_state);
> +
> +    /* Cleanup tables but don't free the memory: we track it
> +     * in build_state.
> +     */
> +    acpi_build_tables_cleanup(&tables, false);
> +}
> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build-pc.c
> similarity index 94%
> rename from hw/i386/acpi-build.c
> rename to hw/i386/acpi-build-pc.c
> index 9a19c14e661b..cad44160ff3a 100644
> --- a/hw/i386/acpi-build.c
> +++ b/hw/i386/acpi-build-pc.c
> @@ -24,6 +24,7 @@
>  #include "qapi/error.h"
>  #include "qapi/qmp/qnum.h"
>  #include "acpi-build.h"
> +#include "acpi-build-internal.h"
>  #include "qemu/bitmap.h"
>  #include "qemu/error-report.h"
>  #include "hw/pci/pci.h"
> @@ -81,14 +82,6 @@
>  
>  #define ACPI_BUILD_TABLE_SIZE             0x20000
>  
> -/* #define DEBUG_ACPI_BUILD */
> -#ifdef DEBUG_ACPI_BUILD
> -#define ACPI_BUILD_DPRINTF(fmt, ...)        \
> -    do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0)
> -#else
> -#define ACPI_BUILD_DPRINTF(fmt, ...)
> -#endif
> -
>  /* Default IOAPIC ID */
>  #define ACPI_BUILD_IOAPIC_ID 0x0
>  
> @@ -120,12 +113,6 @@ typedef struct AcpiBuildPciBusHotplugState {
>      bool pcihp_bridge_en;
>  } AcpiBuildPciBusHotplugState;
>  
> -typedef struct FwCfgTPMConfig {
> -    uint32_t tpmppi_address;
> -    uint8_t tpm_version;
> -    uint8_t tpmppi_version;
> -} QEMU_PACKED FwCfgTPMConfig;
> -
>  static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg);
>  
>  static void init_common_fadt_data(MachineState *ms, Object *o,
> @@ -2710,17 +2697,6 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker)
>                   "IVRS", table_data->len - iommu_start, 1, NULL, NULL);
>  }
>  
> -typedef
> -struct AcpiBuildState {
> -    /* Copy of table in RAM (for patching). */
> -    MemoryRegion *table_mr;
> -    /* Is table patched? */
> -    uint8_t patched;
> -    void *rsdp;
> -    MemoryRegion *rsdp_mr;
> -    MemoryRegion *linker_mr;
> -} AcpiBuildState;
> -
>  static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
>  {
>      Object *pci_host;
> @@ -2746,8 +2722,7 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
>      return true;
>  }
>  
> -static
> -void acpi_build(AcpiBuildTables *tables, MachineState *machine)
> +void acpi_build_pc(AcpiBuildTables *tables, MachineState *machine)
>  {
>      PCMachineState *pcms = PC_MACHINE(machine);
>      PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
> @@ -2948,149 +2923,3 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
>      /* Cleanup memory that's no longer used. */
>      g_array_free(table_offsets, true);
>  }
> -
> -static void acpi_ram_update(MemoryRegion *mr, GArray *data)
> -{
> -    uint32_t size = acpi_data_len(data);
> -
> -    /* Make sure RAM size is correct - in case it got changed e.g. by migration */
> -    memory_region_ram_resize(mr, size, &error_abort);
> -
> -    memcpy(memory_region_get_ram_ptr(mr), data->data, size);
> -    memory_region_set_dirty(mr, 0, size);
> -}
> -
> -static void acpi_build_update(void *build_opaque)
> -{
> -    AcpiBuildState *build_state = build_opaque;
> -    AcpiBuildTables tables;
> -
> -    /* No state to update or already patched? Nothing to do. */
> -    if (!build_state || build_state->patched) {
> -        return;
> -    }
> -    build_state->patched = 1;
> -
> -    acpi_build_tables_init(&tables);
> -
> -    acpi_build(&tables, MACHINE(qdev_get_machine()));
> -
> -    acpi_ram_update(build_state->table_mr, tables.table_data);
> -
> -    if (build_state->rsdp) {
> -        memcpy(build_state->rsdp, tables.rsdp->data, acpi_data_len(tables.rsdp));
> -    } else {
> -        acpi_ram_update(build_state->rsdp_mr, tables.rsdp);
> -    }
> -
> -    acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob);
> -    acpi_build_tables_cleanup(&tables, true);
> -}
> -
> -static void acpi_build_reset(void *build_opaque)
> -{
> -    AcpiBuildState *build_state = build_opaque;
> -    build_state->patched = 0;
> -}
> -
> -static const VMStateDescription vmstate_acpi_build = {
> -    .name = "acpi_build",
> -    .version_id = 1,
> -    .minimum_version_id = 1,
> -    .fields = (VMStateField[]) {
> -        VMSTATE_UINT8(patched, AcpiBuildState),
> -        VMSTATE_END_OF_LIST()
> -    },
> -};
> -
> -void acpi_setup(void)
> -{
> -    PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
> -    PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
> -    X86MachineState *x86ms = X86_MACHINE(pcms);
> -    AcpiBuildTables tables;
> -    AcpiBuildState *build_state;
> -    Object *vmgenid_dev;
> -    TPMIf *tpm;
> -    static FwCfgTPMConfig tpm_config;
> -
> -    if (!x86ms->fw_cfg) {
> -        ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
> -        return;
> -    }
> -
> -    if (!pcms->acpi_build_enabled) {
> -        ACPI_BUILD_DPRINTF("ACPI build disabled. Bailing out.\n");
> -        return;
> -    }
> -
> -    if (!acpi_enabled) {
> -        ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n");
> -        return;
> -    }
> -
> -    build_state = g_malloc0(sizeof *build_state);
> -
> -    acpi_build_tables_init(&tables);
> -    acpi_build(&tables, MACHINE(pcms));
> -
> -    /* Now expose it all to Guest */
> -    build_state->table_mr = acpi_add_rom_blob(acpi_build_update,
> -                                              build_state, tables.table_data,
> -                                              ACPI_BUILD_TABLE_FILE,
> -                                              ACPI_BUILD_TABLE_MAX_SIZE);
> -    assert(build_state->table_mr != NULL);
> -
> -    build_state->linker_mr =
> -        acpi_add_rom_blob(acpi_build_update, build_state,
> -                          tables.linker->cmd_blob, "etc/table-loader", 0);
> -
> -    fw_cfg_add_file(x86ms->fw_cfg, ACPI_BUILD_TPMLOG_FILE,
> -                    tables.tcpalog->data, acpi_data_len(tables.tcpalog));
> -
> -    tpm = tpm_find();
> -    if (tpm && object_property_get_bool(OBJECT(tpm), "ppi", &error_abort)) {
> -        tpm_config = (FwCfgTPMConfig) {
> -            .tpmppi_address = cpu_to_le32(TPM_PPI_ADDR_BASE),
> -            .tpm_version = tpm_get_version(tpm),
> -            .tpmppi_version = TPM_PPI_VERSION_1_30
> -        };
> -        fw_cfg_add_file(x86ms->fw_cfg, "etc/tpm/config",
> -                        &tpm_config, sizeof tpm_config);
> -    }
> -
> -    vmgenid_dev = find_vmgenid_dev();
> -    if (vmgenid_dev) {
> -        vmgenid_add_fw_cfg(VMGENID(vmgenid_dev), x86ms->fw_cfg,
> -                           tables.vmgenid);
> -    }
> -
> -    if (!pcmc->rsdp_in_ram) {
> -        /*
> -         * Keep for compatibility with old machine types.
> -         * Though RSDP is small, its contents isn't immutable, so
> -         * we'll update it along with the rest of tables on guest access.
> -         */
> -        uint32_t rsdp_size = acpi_data_len(tables.rsdp);
> -
> -        build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size);
> -        fw_cfg_add_file_callback(x86ms->fw_cfg, ACPI_BUILD_RSDP_FILE,
> -                                 acpi_build_update, NULL, build_state,
> -                                 build_state->rsdp, rsdp_size, true);
> -        build_state->rsdp_mr = NULL;
> -    } else {
> -        build_state->rsdp = NULL;
> -        build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update,
> -                                                 build_state, tables.rsdp,
> -                                                 ACPI_BUILD_RSDP_FILE, 0);
> -    }
> -
> -    qemu_register_reset(acpi_build_reset, build_state);
> -    acpi_build_reset(build_state);
> -    vmstate_register(NULL, 0, &vmstate_acpi_build, build_state);
> -
> -    /* Cleanup tables but don't free the memory: we track it
> -     * in build_state.
> -     */
> -    acpi_build_tables_cleanup(&tables, false);
> -}
> diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs
> index 8ce1b265335b..e777f1ac9fc3 100644
> --- a/hw/i386/Makefile.objs
> +++ b/hw/i386/Makefile.objs
> @@ -16,4 +16,5 @@ obj-$(CONFIG_VMMOUSE) += vmmouse.o
>  obj-$(CONFIG_PC) += port92.o
>  
>  obj-y += kvmvapic.o
> -obj-$(CONFIG_PC) += acpi-build.o
> +obj-$(CONFIG_PC) += acpi-build-core.o
> +obj-$(CONFIG_PC) += acpi-build-pc.o



  reply	other threads:[~2020-03-27 14:18 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-27 12:11 [PATCH 0/6] acpi: i386 tweaks Gerd Hoffmann
2020-03-27 12:11 ` [PATCH 1/6] acpi: split hw/i386/acpi-build.c Gerd Hoffmann
2020-03-27 14:17   ` Igor Mammedov [this message]
2020-03-27 12:11 ` [PATCH 2/6] acpi: make build_madt() more generic Gerd Hoffmann
2020-03-27 12:11 ` [PATCH 3/6] acpi: factor out acpi_dsdt_add_fw_cfg() Gerd Hoffmann
2020-03-27 14:18   ` Igor Mammedov
2020-03-27 12:11 ` [PATCH 4/6] acpi: drop pointless _STA method Gerd Hoffmann
2020-03-27 14:21   ` Igor Mammedov
2020-03-27 12:11 ` [PATCH 5/6] acpi: serial: don't use " Gerd Hoffmann
2020-03-27 14:33   ` Igor Mammedov
2020-03-31 15:23     ` Gerd Hoffmann
2020-03-31 19:53       ` Igor Mammedov
2020-04-01  5:51         ` Gerd Hoffmann
2020-03-27 12:11 ` [PATCH 6/6] acpi: parallel: " Gerd Hoffmann
2020-03-27 14:35   ` Igor Mammedov
2020-03-27 12:47 ` [PATCH 0/6] acpi: i386 tweaks no-reply
2020-03-27 12:49 ` no-reply
2020-03-29 12:46 ` Michael S. Tsirkin

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=20200327151722.01f6bded@redhat.com \
    --to=imammedo@redhat.com \
    --cc=ehabkost@redhat.com \
    --cc=kraxel@redhat.com \
    --cc=mst@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=rth@twiddle.net \
    /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.