From: Liran Alon liran.alon@oracle.com
Windows kernel extracts various BIOS information at boot-time. The method it uses to extract SystemBiosDate is very hueristic. It is done by nt!CmpGetBiosDate().
nt!CmpGetBiosDate() works by scanning all BIOS memory from 0xF0000 to 0xFFFF5 (FSEG) in search for a string which is formatted like a date. It then chooses the string which represents the most recent date, and writes it to:
HKLM/HARDWARE/DESCRIPTION/System SystemBiosDate
This date should usually be BiosDate located at FSEG(0xFFF5).
In some cases when the SMBIOS tables are small enough (both in legacy and non-legacy mode) - These tables are allocated in FSEG instead of high-mem, specifically Type0->release_date string which might cause SystemBiosDate to change - depending on its value. This leads to an inconsistent behaviour that depends on the SMBIOS table sizes.
We fix this inconsistency by changing BiosDate to the same value provided by SMBIOS, regardless whether SMBIOS tables reside in FSEG or high-mem.
For reference implementation of nt!CmpGetBiosDate(), see ReactOS: https://doxygen.reactos.org/d5/dd2/i386_2cmhardwr_8c.html
Reviewed-by: Konrad Rzeszutek Wilk konrad.wilk@oracle.com Reviewed-by: Arbel Moshe arbel.moshe@oracle.com Signed-off-by: Sam Eiderman shmuel.eiderman@oracle.com Signed-off-by: Liran Alon liran.alon@oracle.com --- src/fw/biostables.c | 36 ++++++++++++++++++++++++++++++++++++ src/fw/smbios.c | 20 +++++++++++++++++++- src/util.h | 1 + 3 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/src/fw/biostables.c b/src/fw/biostables.c index 546e83c6..90bb3b92 100644 --- a/src/fw/biostables.c +++ b/src/fw/biostables.c @@ -268,6 +268,38 @@ smbios_next(struct smbios_entry_point *smbios, void *prev) return prev; }
+// Get string from the smbios table. +void * +smbios_get_str(struct smbios_entry_point *smbios, void *offset, u8 n) +{ + if (!smbios || !offset || n == 0) + return NULL; + void *start = (void*)smbios->structure_table_address; + void *end = start + smbios->structure_table_length; + void *prev = NULL; + struct smbios_structure_header *hdr = offset; + + if (offset + sizeof(*hdr) > end) + return NULL; + + offset += hdr->length; + + while (n > 0) { + prev = offset; + while (*(u8*)offset) { + if (offset + 3 > end) + return NULL; /* not enough space for "\0\0" */ + offset++; + } + if (prev == offset) + return NULL; /* reached end of table */ + n--; + offset++; + } + + return prev; +} + struct smbios_entry_point *SMBiosAddr;
void @@ -409,6 +441,7 @@ smbios_romfile_setup(void) struct romfile_s *f_tables = romfile_find("etc/smbios/smbios-tables"); struct smbios_entry_point ep; struct smbios_type_0 *t0; + char *release_date; u16 qtables_len, need_t0 = 1; u8 *qtables, *tables;
@@ -432,6 +465,9 @@ smbios_romfile_setup(void) for (t0 = smbios_next(&ep, NULL); t0; t0 = smbios_next(&ep, t0)) if (t0->header.type == 0) { need_t0 = 0; + /* Sync BIOS hardcoded date with the SMBIOS provided one */ + release_date = smbios_get_str(&ep, t0, t0->bios_release_date_str); + smbios_update_bios_date(release_date); break; }
diff --git a/src/fw/smbios.c b/src/fw/smbios.c index 96104714..7cb02dba 100644 --- a/src/fw/smbios.c +++ b/src/fw/smbios.c @@ -160,12 +160,26 @@ get_external(int type, char **p, unsigned *nr_structs, } \ } while (0)
+void +smbios_update_bios_date(const char *release_date) +{ + if (!release_date) + return; + if (strlen(release_date) == sizeof("mm/dd/yyyy") - 1) { + memcpy(BiosDate, release_date, sizeof("mm/dd/") - 1); + memcpy(BiosDate + sizeof("mm/dd/") - 1, + release_date + sizeof("mm/dd/yy") - 1, + sizeof("yy") - 1); + } +} + /* Type 0 -- BIOS Information */ static void * smbios_init_type_0(void *start) { struct smbios_type_0 *p = (struct smbios_type_0 *)start; char *end = (char *)start + sizeof(struct smbios_type_0); + char *release_date; size_t size; int str_index = 0;
@@ -178,7 +192,11 @@ smbios_init_type_0(void *start)
p->bios_starting_address_segment = 0xe800;
- load_str_field_with_default(0, bios_release_date_str, BIOS_DATE); + /* Sync BIOS hardcoded date with the SMBIOS provided one */ + release_date = end; + load_str_field_with_default(0, bios_release_date_str, BiosDate); + if (p->bios_release_date_str) + smbios_update_bios_date(release_date);
p->bios_rom_size = 0; /* FIXME */
diff --git a/src/util.h b/src/util.h index 68ba848d..81cddbbd 100644 --- a/src/util.h +++ b/src/util.h @@ -84,6 +84,7 @@ void copy_smbios(void *pos); void display_uuid(void); void copy_table(void *pos); void smbios_setup(void); +void smbios_update_bios_date(const char *release_date);
// fw/coreboot.c extern const char *CBvendor, *CBpart;