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 controlling which dates appear in FSEG:
* Introducing fw_cfg key 'etc/win-bios-date' to control the date that appears in FSEG(0xFFF5). * If 'etc/win-bios-date' was supplied - we do not allocate the SMBIOS tables, which contain dates, in FSEG but in high-mem instead.
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/boot.c | 9 +++++++++ src/fw/biostables.c | 2 +- src/fw/smbios.c | 2 +- src/misc.c | 2 ++ src/util.h | 4 ++++ 5 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/src/boot.c b/src/boot.c index 9f82f3ca..a3e9eb6d 100644 --- a/src/boot.c +++ b/src/boot.c @@ -285,6 +285,15 @@ boot_init(void) } }
+ int size; + char *date = romfile_loadfile("etc/win-bios-date", &size); + if (date) { + if (size > sizeof(BiosDate) - 1) + size = sizeof(BiosDate) - 1; + memcpy(BiosDate, date, size); + OnlyBiosDateInFSEG = 1; + } + BootRetryTime = romfile_loadint("etc/boot-fail-wait", 60*1000);
loadBootOrder(); diff --git a/src/fw/biostables.c b/src/fw/biostables.c index 269b8582..6e5d4f86 100644 --- a/src/fw/biostables.c +++ b/src/fw/biostables.c @@ -448,7 +448,7 @@ smbios_romfile_setup(void) }
/* allocate final blob and record its address in the entry point */ - if (ep.structure_table_length > BUILD_MAX_SMBIOS_FSEG) + if (OnlyBiosDateInFSEG || ep.structure_table_length > BUILD_MAX_SMBIOS_FSEG) tables = malloc_high(ep.structure_table_length); else tables = malloc_fseg(ep.structure_table_length); diff --git a/src/fw/smbios.c b/src/fw/smbios.c index 6f33a329..ab7dc965 100644 --- a/src/fw/smbios.c +++ b/src/fw/smbios.c @@ -23,7 +23,7 @@ smbios_entry_point_setup(u16 max_structure_size, u16 number_of_structures) { void *finaltable; - if (structure_table_length <= BUILD_MAX_SMBIOS_FSEG) + if (!OnlyBiosDateInFSEG && structure_table_length <= BUILD_MAX_SMBIOS_FSEG) // Table is small enough for f-seg - allocate there. This // works around a bug in JunOS (at least for small SMBIOS tables). finaltable = malloc_fseg(structure_table_length); diff --git a/src/misc.c b/src/misc.c index b5117304..a31800d7 100644 --- a/src/misc.c +++ b/src/misc.c @@ -176,6 +176,8 @@ struct descloc_s rombios32_gdt_48 VARFSEG = { // BIOS build date char BiosDate[] VARFSEGFIXED(0xfff5) = "06/23/99";
+u8 OnlyBiosDateInFSEG = 0; + u8 BiosModelId VARFSEGFIXED(0xfffe) = BUILD_MODEL_ID;
u8 BiosChecksum VARFSEGFIXED(0xffff); diff --git a/src/util.h b/src/util.h index 6dd080f6..e7eab27c 100644 --- a/src/util.h +++ b/src/util.h @@ -243,6 +243,10 @@ void lpt_setup(void); // version.c extern const char VERSION[], BUILDINFO[];
+// misc.c +extern char BiosDate[sizeof("dd/mm/yy")] __aligned(1); +extern u8 OnlyBiosDateInFSEG; + // vgahooks.c void handle_155f(struct bregs *regs); void handle_157f(struct bregs *regs);