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). If found, we no longer build smbios tables locally; instead, we replace only the type 0 table with a default of our own, and (re)calculate only the minimum set of necessary fields from the provided entry point.
Signed-off-by: Gabriel L. Somlo somlo@cmu.edu ---
Reworked to fit on top of 028f3487cfaa136815dd8d1896310c763402e969. Thanks, Gabriel
src/fw/smbios.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+)
diff --git a/src/fw/smbios.c b/src/fw/smbios.c index 0ac9ff5..b38d199 100644 --- a/src/fw/smbios.c +++ b/src/fw/smbios.c @@ -160,6 +160,60 @@ get_external(int type, char **p, unsigned *nr_structs, } \ } while (0)
+#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 *rel_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, rel_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; +} + /* Type 0 -- BIOS Information */ #define RELEASE_DATE_STR "01/01/2011" static void * @@ -501,6 +555,82 @@ smbios_init_type_127(void *start) return start + 2; }
+static int +smbios_try_fw_cfg_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; + u8 *tables, *t; + u16 t_len; + + 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; + + tables = malloc_tmphigh(f_tables->size); + if (!tables) { + warn_noalloc(); + return 0; + } + f_tables->copy(f_tables, tables, f_tables->size); + + /* rip out any type 0 tables we may have received */ + t = tables; + t_len = ep.structure_table_length; + while (t < tables + t_len) { + struct smbios_structure_header *h = (struct smbios_structure_header *)t; + u8 *next; + + /* find start of next structure (past the double-'\0' terminator) */ + for (next = t + h->length; *next || *(next+1); next++); + next += 2; + + if (h->type == 0) { + /* move remaining tables down over t */ + memmove(t, next, t_len - (next - tables)); + /* shorten total blob length */ + t_len -= (next - t); + } else { + t = next; + } + } + + /* final blob length adds our own type 0 with 3 strings and 4 '\0's */ + ep.structure_table_length = t_len + sizeof(struct smbios_type_0) + + strlen("SeaBIOS") + strlen(VERSION) + + strlen(RELEASE_DATE_STR) + 4; + + /* allocate final blob */ + if (ep.structure_table_length > BUILD_MAX_SMBIOS_FSEG) + t = malloc_high(ep.structure_table_length); + else + t = malloc_fseg(ep.structure_table_length); + if (!t) { + warn_noalloc(); + free(tables); + return 0; + } + + /* finalize entry point */ + ep.structure_table_address = (u32)t; + ep.checksum = -checksum(&ep, 0x10); + ep.intermediate_checksum = -checksum((void *)&ep + 0x10, ep.length - 0x10); + + /* populate final blob */ + t = smbios_new_type_0(t, "SeaBIOS", VERSION, RELEASE_DATE_STR); + memcpy(t, tables, t_len); + free(tables); + + /* success */ + copy_smbios(&ep); + return 1; +} + #define TEMPSMBIOSSIZE (32 * 1024)
void @@ -511,6 +641,9 @@ smbios_setup(void)
dprintf(3, "init SMBIOS tables\n");
+ if (smbios_try_fw_cfg_setup()) + return; /* got full tables via fw_cfg, we're done */ + char *start = malloc_tmphigh(TEMPSMBIOSSIZE); if (! start) { warn_noalloc();