diff mbox

[SeaBIOS,v3] SMBIOS: Check for full tables & entry point in fw_cfg

Message ID 20140414193012.GE1690@ERROL.INI.CMU.EDU
State New
Headers show

Commit Message

Gabriel L. Somlo April 14, 2014, 7:30 p.m. UTC
Check fw_cfg for the presence of a complete set of smbios
tables (etc/smbios/smbios-tables) and an entry point structure
(etc/smbios/smbios-anchor), and, if found, use them instead of
generating our own copies locally.

We ensure the presence of a type 0 (bios information) structure
by generating one locally if necessary, which is expected to be
the common case.

Signed-off-by: Gabriel L. Somlo <somlo@cmu.edu>
---

This now goes on top of Kevin's patch factoring out smbios table walking
into smbios_next().

On Sat, Apr 12, 2014 at 11:56:08AM -0400, Kevin O'Connor wrote:
> I'd prefer to add this code to src/fw/paravirt.c and
> src/fw/biostables.c instead of src/fw/smbios.c.  That way, the
> smbios.c file is limited to the legacy smbios generation and we can
> more clearly document that the whole file is deprecated.

OK, so most of the new code is now in biostables.c, hope that's OK.

> QEMU currently has command-line options that can modify the fields of
> the type0 tables (-smbios type=0,vendor='foo').  To continue to
> support that, I think QEMU should be able to build the type0 table as
> it feels fit to, and SeaBIOS should be able to pass it through.  Of
> course, if there's no specific request from the end user, then I think
> QEMU can tell SeaBIOS that it may replace the type0 content with its
> own data (eg, via "etc/update-smbios-type0").

Something like that, except (hopefully) even simpler :)

If QEMU adds its (still optional) type 0 structure, SeaBIOS just uses
everything as-is. If no type 0 structure is found, we generate and
prepend our own, and update the entry point fields accordingly.

I expect the latter to be the common case (having QEMU add its own
type 0 sub-table is rare, and discouraged in practice, according to
the QEMU guys).

Finally, it feels like BIOS_NAME and BIOS_DATE should maybe come from
some other, more central place in the SeaBIOS tree, but didn't find
anything applicable...

Thanks,
   Gabriel

 src/fw/biostables.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/fw/smbios.c     |   2 +-
 src/util.h          |   3 +-
 3 files changed, 138 insertions(+), 2 deletions(-)

Comments

Kevin O'Connor April 14, 2014, 9:52 p.m. UTC | #1
On Mon, Apr 14, 2014 at 03:30:14PM -0400, Gabriel L. Somlo wrote:
> Check fw_cfg for the presence of a complete set of smbios
> tables (etc/smbios/smbios-tables) and an entry point structure
> (etc/smbios/smbios-anchor), and, if found, use them instead of
> generating our own copies locally.
> 
> We ensure the presence of a type 0 (bios information) structure
> by generating one locally if necessary, which is expected to be
> the common case.

Thanks.  Looks good to me.

-Kevin
Markus Armbruster April 15, 2014, 8:36 a.m. UTC | #2
"Gabriel L. Somlo" <gsomlo@gmail.com> writes:

> Check fw_cfg for the presence of a complete set of smbios
> tables (etc/smbios/smbios-tables) and an entry point structure
> (etc/smbios/smbios-anchor), and, if found, use them instead of
> generating our own copies locally.
>
> We ensure the presence of a type 0 (bios information) structure
> by generating one locally if necessary, which is expected to be
> the common case.
>
> Signed-off-by: Gabriel L. Somlo <somlo@cmu.edu>
> ---
>
> This now goes on top of Kevin's patch factoring out smbios table walking
> into smbios_next().
>
> On Sat, Apr 12, 2014 at 11:56:08AM -0400, Kevin O'Connor wrote:
>> I'd prefer to add this code to src/fw/paravirt.c and
>> src/fw/biostables.c instead of src/fw/smbios.c.  That way, the
>> smbios.c file is limited to the legacy smbios generation and we can
>> more clearly document that the whole file is deprecated.
>
> OK, so most of the new code is now in biostables.c, hope that's OK.
>
>> QEMU currently has command-line options that can modify the fields of
>> the type0 tables (-smbios type=0,vendor='foo').  To continue to
>> support that, I think QEMU should be able to build the type0 table as
>> it feels fit to, and SeaBIOS should be able to pass it through.  Of
>> course, if there's no specific request from the end user, then I think
>> QEMU can tell SeaBIOS that it may replace the type0 content with its
>> own data (eg, via "etc/update-smbios-type0").
>
> Something like that, except (hopefully) even simpler :)
>
> If QEMU adds its (still optional) type 0 structure, SeaBIOS just uses
> everything as-is. If no type 0 structure is found, we generate and
> prepend our own, and update the entry point fields accordingly.
>
> I expect the latter to be the common case (having QEMU add its own
> type 0 sub-table is rare, and discouraged in practice, according to
> the QEMU guys).

Messing with SMBIOS entries is a rather obscure feature, and messing
with type 0 is perhaps more obscure than messing with type 1.

QEMU provides two ways to mess with SMBIOS entries:

* -smbios type=T,KEY=VAL...

  T can be either 0 or 1.

* -smbios file=F

  F contains an SMBIOS struct.  Its type is parsed from file F.

If -smbios file=F is given, it completely defines the type T entry.
Else, the type T entry is to be constructed from SeaBIOS defaults and
any -smbios type=T,... given.

If I read your discussion correctly, you're proposing to change "from
SeaBIOS defaults" to "from QEMU defaults".  Correct?
Gerd Hoffmann April 15, 2014, 8:43 a.m. UTC | #3
> If I read your discussion correctly, you're proposing to change "from
> SeaBIOS defaults" to "from QEMU defaults".  Correct?

That is correct, where "qemu defaults" will be "seabios defaults of the
current stable release aka 1.7.4".

cheers,
  Gerd
Gerd Hoffmann April 15, 2014, 8:46 a.m. UTC | #4
On Mo, 2014-04-14 at 15:30 -0400, Gabriel L. Somlo wrote:
> If QEMU adds its (still optional) type 0 structure, SeaBIOS just uses
> everything as-is. If no type 0 structure is found, we generate and
> prepend our own, and update the entry point fields accordingly.
> 
> I expect the latter to be the common case (having QEMU add its own
> type 0 sub-table is rare, and discouraged in practice, according to
> the QEMU guys).

[ it's probably a good idea to update the qemu docs explicitly say so ]

> Finally, it feels like BIOS_NAME and BIOS_DATE should maybe come from
> some other, more central place in the SeaBIOS tree, but didn't find
> anything applicable...

Looks good to me too.  The remaining issues which are to be sorted are
on the qemu side only.

cheers,
  Gerd
diff mbox

Patch

diff --git a/src/fw/biostables.c b/src/fw/biostables.c
index 818a8fe..838168b 100644
--- a/src/fw/biostables.c
+++ b/src/fw/biostables.c
@@ -13,6 +13,7 @@ 
 #include "std/mptable.h" // MPTABLE_SIGNATURE
 #include "std/pirtable.h" // struct pir_header
 #include "std/smbios.h" // struct smbios_entry_point
+#include "romfile.h"
 #include "string.h" // memcpy
 #include "util.h" // copy_table
 #include "x86.h" // outb
@@ -314,6 +315,140 @@  display_uuid(void)
         }
 }
 
+#define set_str_field_or_skip(type, field, value)                       \
+    do {                                                                \
+        int size = (value != NULL) ? strlen(value) + 1 : 0;             \
+        if (size > 1) {                                                 \
+            memcpy(end, value, size);                                   \
+            end += size;                                                \
+            p->field = ++str_index;                                     \
+        } else {                                                        \
+            p->field = 0;                                               \
+        }                                                               \
+    } while (0)
+
+static void *
+smbios_new_type_0(void *start,
+                  const char *vendor, const char *version, const char *date)
+{
+    struct smbios_type_0 *p = (struct smbios_type_0 *)start;
+    char *end = (char *)start + sizeof(struct smbios_type_0);
+    int str_index = 0;
+
+    p->header.type = 0;
+    p->header.length = sizeof(struct smbios_type_0);
+    p->header.handle = 0;
+
+    set_str_field_or_skip(0, vendor_str, vendor);
+    set_str_field_or_skip(0, bios_version_str, version);
+    p->bios_starting_address_segment = 0xe800;
+    set_str_field_or_skip(0, bios_release_date_str, date);
+
+    p->bios_rom_size = 0; /* FIXME */
+
+    /* BIOS characteristics not supported */
+    memset(p->bios_characteristics, 0, 8);
+    p->bios_characteristics[0] = 0x08;
+
+    /* Enable targeted content distribution (needed for SVVP, per SeaBIOS) */
+    p->bios_characteristics_extension_bytes[0] = 0;
+    p->bios_characteristics_extension_bytes[1] = 4;
+
+    p->system_bios_major_release = 0;
+    p->system_bios_minor_release = 0;
+    p->embedded_controller_major_release = 0xFF;
+    p->embedded_controller_minor_release = 0xFF;
+
+    *end = 0;
+    end++;
+    if (!str_index) {
+        *end = 0;
+        end++;
+    }
+
+    return end;
+}
+
+#define BIOS_NAME "SeaBIOS"
+#define BIOS_DATE "04/01/2014"
+
+static int
+smbios_romfile_setup(void)
+{
+    struct romfile_s *f_anchor = romfile_find("etc/smbios/smbios-anchor");
+    struct romfile_s *f_tables = romfile_find("etc/smbios/smbios-tables");
+    struct smbios_entry_point ep;
+    struct smbios_type_0 *t0;
+    u16 qtables_len, need_t0 = 1;
+    u8 *qtables, *tables;
+
+    if (!f_anchor || !f_tables || f_anchor->size != sizeof(ep))
+        return 0;
+
+    f_anchor->copy(f_anchor, &ep, f_anchor->size);
+
+    if (f_tables->size != ep.structure_table_length)
+        return 0;
+
+    qtables = malloc_tmphigh(f_tables->size);
+    if (!qtables) {
+        warn_noalloc();
+        return 0;
+    }
+    f_tables->copy(f_tables, qtables, f_tables->size);
+    ep.structure_table_address = (u32)qtables; /* for smbios_next(), below */
+
+    /* did we get a type 0 structure ? */
+    for (t0 = smbios_next(&ep, NULL); t0; t0 = smbios_next(&ep, t0))
+        if (t0->header.type == 0) {
+            need_t0 = 0;
+            break;
+        }
+
+    qtables_len = ep.structure_table_length;
+    if (need_t0) {
+        /* common case: add our own type 0, with 3 strings and 4 '\0's */
+        u16 t0_len = sizeof(struct smbios_type_0) + strlen(BIOS_NAME) +
+                     strlen(VERSION) + strlen(BIOS_DATE) + 4;
+        ep.structure_table_length += t0_len;
+        if (t0_len > ep.max_structure_size)
+            ep.max_structure_size = t0_len;
+        ep.number_of_structures++;
+    }
+
+    /* allocate final blob and record its address in the entry point */
+    if (ep.structure_table_length > BUILD_MAX_SMBIOS_FSEG)
+        tables = malloc_high(ep.structure_table_length);
+    else
+        tables = malloc_fseg(ep.structure_table_length);
+    if (!tables) {
+        warn_noalloc();
+        free(qtables);
+        return 0;
+    }
+    ep.structure_table_address = (u32)tables;
+
+    /* populate final blob */
+    if (need_t0)
+        tables = smbios_new_type_0(tables, BIOS_NAME, VERSION, BIOS_DATE);
+    memcpy(tables, qtables, qtables_len);
+    free(qtables);
+
+    /* finalize entry point */
+    ep.checksum -= checksum(&ep, 0x10);
+    ep.intermediate_checksum -= checksum((void *)&ep + 0x10, ep.length - 0x10);
+
+    copy_smbios(&ep);
+    return 1;
+}
+
+void
+smbios_setup(void)
+{
+    if (smbios_romfile_setup())
+      return;
+    smbios_legacy_setup();
+}
 
 void
 copy_table(void *pos)
diff --git a/src/fw/smbios.c b/src/fw/smbios.c
index 0ac9ff5..dba0541 100644
--- a/src/fw/smbios.c
+++ b/src/fw/smbios.c
@@ -504,7 +504,7 @@  smbios_init_type_127(void *start)
 #define TEMPSMBIOSSIZE (32 * 1024)
 
 void
-smbios_setup(void)
+smbios_legacy_setup(void)
 {
     if (! CONFIG_SMBIOS)
         return;
diff --git a/src/util.h b/src/util.h
index 4f242bf..9557581 100644
--- a/src/util.h
+++ b/src/util.h
@@ -78,6 +78,7 @@  extern struct smbios_entry_point *SMBiosAddr;
 void copy_smbios(void *pos);
 void display_uuid(void);
 void copy_table(void *pos);
+void smbios_setup(void);
 
 // fw/coreboot.c
 extern const char *CBvendor, *CBpart;
@@ -117,7 +118,7 @@  void make_bios_readonly(void);
 void qemu_prep_reset(void);
 
 // fw/smbios.c
-void smbios_setup(void);
+void smbios_legacy_setup(void);
 
 // fw/smm.c
 void smm_device_setup(void);