qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [ARM SMBIOS V1 PATCH 5/6] smbios: add smbios 3.0 suppor


From: Laszlo Ersek
Subject: Re: [Qemu-devel] [ARM SMBIOS V1 PATCH 5/6] smbios: add smbios 3.0 support
Date: Fri, 31 Jul 2015 19:12:17 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.1.0

Hello Wei,

there's a bug in one of the SMBIOS 3.0 entry point struct fields. It
relates to the use of "smbios_table_max":

On 07/28/15 08:00, Wei Huang wrote:
> This patch adds support for SMBIOS 3.0 entry point. When caller invokes
> smbios_set_defaults(), it can specify entry point as 2.1 or 3.0. Then
> smbios_get_tables() will return the entry point table in right format.
> 
> Signed-off-by: Wei Huang <address@hidden>
> ---
>  hw/i386/pc_piix.c          |  3 +-
>  hw/i386/pc_q35.c           |  3 +-
>  hw/smbios/smbios.c         | 72 
> +++++++++++++++++++++++++++++++---------------
>  include/hw/smbios/smbios.h | 30 +++++++++++++++++--
>  4 files changed, 81 insertions(+), 27 deletions(-)
> 
> diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
> index 653c710..04636b1 100644
> --- a/hw/i386/pc_piix.c
> +++ b/hw/i386/pc_piix.c
> @@ -173,7 +173,8 @@ static void pc_init1(MachineState *machine)
>          MachineClass *mc = MACHINE_GET_CLASS(machine);
>          /* These values are guest ABI, do not change */
>          smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)",
> -                            mc->name, smbios_legacy_mode, 
> smbios_uuid_encoded);
> +                            mc->name, smbios_legacy_mode, 
> smbios_uuid_encoded,
> +                            SMBIOS_ENTRY_POINT_21);
>      }
>  
>      /* allocate ram and load rom/bios */
> diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
> index d83df14..061507d 100644
> --- a/hw/i386/pc_q35.c
> +++ b/hw/i386/pc_q35.c
> @@ -165,7 +165,8 @@ static void pc_q35_init(MachineState *machine)
>      if (smbios_defaults) {
>          /* These values are guest ABI, do not change */
>          smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)",
> -                            mc->name, smbios_legacy_mode, 
> smbios_uuid_encoded);
> +                            mc->name, smbios_legacy_mode, 
> smbios_uuid_encoded,
> +                            SMBIOS_ENTRY_POINT_21);
>      }
>  
>      /* allocate ram and load rom/bios */
> diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
> index 08ba62a..70ecd87 100644
> --- a/hw/smbios/smbios.c
> +++ b/hw/smbios/smbios.c
> @@ -48,6 +48,7 @@ static uint8_t *smbios_entries;
>  static size_t smbios_entries_len;
>  static bool smbios_legacy = true;
>  static bool smbios_uuid_encoded = true;
> +static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_21;
>  /* end: legacy structures & constants for <= 2.0 machines */
>  
>  
> @@ -55,7 +56,8 @@ static uint8_t *smbios_tables;
>  static size_t smbios_tables_len;
>  static unsigned smbios_table_max;
>  static unsigned smbios_table_cnt;
> -static struct smbios_entry_point ep;
> +static smbios_entry_point ep;
> +static size_t ep_length;
>  
>  static int smbios_type4_count = 0;
>  static bool smbios_immutable;
> @@ -771,11 +773,12 @@ void smbios_set_cpuid(uint32_t version, uint32_t 
> features)
>  
>  void smbios_set_defaults(const char *manufacturer, const char *product,
>                           const char *version, bool legacy_mode,
> -                         bool uuid_encoded)
> +                         bool uuid_encoded, SmbiosEntryPointType ep_type)
>  {
>      smbios_have_defaults = true;
>      smbios_legacy = legacy_mode;
>      smbios_uuid_encoded = uuid_encoded;
> +    smbios_ep_type = ep_type;
>  
>      /* drop unwanted version of command-line file blob(s) */
>      if (smbios_legacy) {
> @@ -808,26 +811,49 @@ void smbios_set_defaults(const char *manufacturer, 
> const char *product,
>  
>  static void smbios_entry_point_setup(void)
>  {
> -    memcpy(ep.anchor_string, "_SM_", 4);
> -    memcpy(ep.intermediate_anchor_string, "_DMI_", 5);
> -    ep.length = sizeof(struct smbios_entry_point);
> -    ep.entry_point_revision = 0; /* formatted_area reserved, per spec v2.1+ 
> */
> -    memset(ep.formatted_area, 0, 5);
> -
> -    /* compliant with smbios spec v2.8 */
> -    ep.smbios_major_version = 2;
> -    ep.smbios_minor_version = 8;
> -    ep.smbios_bcd_revision = 0x28;
> -
> -    /* set during table construction, but BIOS may override: */
> -    ep.structure_table_length = cpu_to_le16(smbios_tables_len);
> -    ep.max_structure_size = cpu_to_le16(smbios_table_max);
> -    ep.number_of_structures = cpu_to_le16(smbios_table_cnt);
> -
> -    /* BIOS must recalculate: */
> -    ep.checksum = 0;
> -    ep.intermediate_checksum = 0;
> -    ep.structure_table_address = cpu_to_le32(0);
> +    if (smbios_ep_type == SMBIOS_ENTRY_POINT_21) {
> +        memcpy(ep.ep21.anchor_string, "_SM_", 4);
> +        memcpy(ep.ep21.intermediate_anchor_string, "_DMI_", 5);
> +        ep.ep21.length = sizeof(struct smbios_21_entry_point);
> +        ep.ep21.entry_point_revision = 0; /* formatted_area reserved */
> +        memset(ep.ep21.formatted_area, 0, 5);
> +
> +        /* compliant with smbios spec v2.8 */
> +        ep.ep21.smbios_major_version = 2;
> +        ep.ep21.smbios_minor_version = 8;
> +        ep.ep21.smbios_bcd_revision = 0x28;
> +
> +        /* set during table construction, but BIOS may override: */
> +        ep.ep21.structure_table_length = cpu_to_le16(smbios_tables_len);
> +        ep.ep21.max_structure_size = cpu_to_le16(smbios_table_max);

This is correct. In SMBIOS 2.x, the "max_structure_size" field reports:

  Size of the largest SMBIOS structure, in bytes, and encompasses the
  structure’s formatted area and text strings

And the "smbios_table_max" variable actually tracks that, see the
SMBIOS_BUILD_TABLE_POST macro.

        /* update smbios max. element size */

However,

> +        ep.ep21.number_of_structures = cpu_to_le16(smbios_table_cnt);
> +
> +        /* BIOS must recalculate: */
> +        ep.ep21.checksum = 0;
> +        ep.ep21.intermediate_checksum = 0;
> +        ep.ep21.structure_table_address = cpu_to_le32(0);
> +
> +        /* setup the anchor point length */
> +        ep_length = sizeof (struct smbios_21_entry_point);
> +    } else if (smbios_ep_type == SMBIOS_ENTRY_POINT_30) {
> +        memcpy(ep.ep30.anchor_string, "_SM3_", 5);
> +        ep.ep30.length = sizeof(struct smbios_30_entry_point);
> +        ep.ep30.entry_point_revision = 1;
> +
> +        /* compliant with smbios spec 3.0 */
> +        ep.ep30.smbios_major_version = 3;
> +        ep.ep30.smbios_minor_version = 0;
> +        ep.ep30.smbios_doc_rev = 0;
> +
> +        /* set during table construct, but BIOS might override */
> +        ep.ep30.structure_table_max_size = cpu_to_le32(smbios_table_max);

This is incorrect. In SMBIOS 3.0, the "structure_table_max_size" field
does not track the largest individual table (there is no such field any
longer). Instead, this field reports

  Maximum size of SMBIOS Structure Table, pointed to by the Structure
  Table Address, in bytes. The actual size is guaranteed to be less or
  equal to the maximum size.

In other words, you should fill this field in from "smbios_tables_len"
(similarly to "ep.ep21.structure_table_length").

Now you might ask what sense it makes for the 3.0 spec to require an
"upper limit" on the full blob length, and not an *exact* length (as it
was with 2.x). I don't know. I asked Jon Masters on IRC (CC'd), and if I
understood his reply correctly, this is simply an error in the 3.0 spec,
to be fixed in 3.1.

So, please just squash in the attached patch, which should fix this bug
and satisfy the requirements of both the 3.0 and the (expected) 3.1 specs.

With that patch applied, the firmware code I'm about to post (round 2)
seems to accept and install the stuff in the guest. I haven't verified
the tables in a Linux guest (with the dmidecode utility) yet, because I
don't have ready access to a Mustang right now, and with TCG I won't
boot Linux :)

But, I ran the SMBIOSVIEW command in the UEFI shell of AAVMF, and things
look okay there. I'm attaching an excerpt of the output. (Note that the
Type=0 table comes from the firmware itself, as a fallback, same as on
x86 / OVMF, if QEMU doesn't provide one.)

Thanks!
Laszlo

> +
> +        /* BIOS must recalculate */
> +        ep.ep30.checksum = 0;
> +        ep.ep30.structure_table_address = cpu_to_le64(0);
> +
> +        ep_length = sizeof (struct smbios_30_entry_point);
> +    }
>  }
>  
>  void smbios_get_tables(const struct smbios_phys_mem_area *mem_array,
> @@ -886,7 +912,7 @@ void smbios_get_tables(const struct smbios_phys_mem_area 
> *mem_array,
>      *tables = smbios_tables;
>      *tables_len = smbios_tables_len;
>      *anchor = (uint8_t *)&ep;
> -    *anchor_len = sizeof(struct smbios_entry_point);
> +    *anchor_len = ep_length;
>  }
>  
>  static void save_opt(const char **dest, QemuOpts *opts, const char *name)
> diff --git a/include/hw/smbios/smbios.h b/include/hw/smbios/smbios.h
> index e727233..3dd8a41 100644
> --- a/include/hw/smbios/smbios.h
> +++ b/include/hw/smbios/smbios.h
> @@ -18,6 +18,12 @@
>  
>  #define SMBIOS_MAX_TYPE 127
>  
> +typedef enum SmbiosEntryPointType {
> +    SMBIOS_ENTRY_POINT_21,
> +    SMBIOS_ENTRY_POINT_30,
> +} SmbiosEntryPointType;
> +
> +
>  /* memory area description, used by type 19 table */
>  struct smbios_phys_mem_area {
>      uint64_t address;
> @@ -28,7 +34,7 @@ void smbios_entry_add(QemuOpts *opts);
>  void smbios_set_cpuid(uint32_t version, uint32_t features);
>  void smbios_set_defaults(const char *manufacturer, const char *product,
>                           const char *version, bool legacy_mode,
> -                         bool uuid_encoded);
> +                         bool uuid_encoded, SmbiosEntryPointType ep_type);
>  uint8_t *smbios_get_table_legacy(size_t *length);
>  void smbios_get_tables(const struct smbios_phys_mem_area *mem_array,
>                         const unsigned int mem_array_size,
> @@ -43,7 +49,8 @@ void smbios_get_tables(const struct smbios_phys_mem_area 
> *mem_array,
>  /* SMBIOS entry point (anchor).
>   * BIOS must place this at a 16-bit-aligned address between 0xf0000 and 
> 0xfffff.
>   */
> -struct smbios_entry_point {
> +/* SMBIOS 2.1 entry point */
> +struct smbios_21_entry_point {
>      uint8_t anchor_string[4];
>      uint8_t checksum;
>      uint8_t length;
> @@ -60,6 +67,25 @@ struct smbios_entry_point {
>      uint8_t smbios_bcd_revision;
>  } QEMU_PACKED;
>  
> +/* SMBIOS 3.0 entry point */
> +struct smbios_30_entry_point {
> +    uint8_t anchor_string[5];
> +    uint8_t checksum;
> +    uint8_t length;
> +    uint8_t smbios_major_version;
> +    uint8_t smbios_minor_version;
> +    uint8_t smbios_doc_rev;
> +    uint8_t entry_point_revision;
> +    uint8_t _reserved;
> +    uint32_t structure_table_max_size;
> +    uint64_t structure_table_address;
> +} QEMU_PACKED;
> +
> +typedef union QEMU_PACKED {
> +    struct smbios_21_entry_point ep21;
> +    struct smbios_30_entry_point ep30;
> +} smbios_entry_point;
> +
>  /* This goes at the beginning of every SMBIOS structure. */
>  struct smbios_structure_header {
>      uint8_t type;
> 

Attachment: 0001-hw-smbios-fix-Structure-table-maximum-size-field-in-.patch
Description: Text Data

Attachment: smbiosview.txt
Description: Text document


reply via email to

[Prev in Thread] Current Thread [Next in Thread]