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).
BIOS_DATE and RELEASE_DATE_STR appear in FSEG together with BiosDate. This makes Windows report BIOS_DATE, which is the most recent one, as the date in HKLM/HARDWARE/DESCRIPTION/System SystemBiosDate.
In some cases we would like to control the value that Windows will calculate and store in SystemBiosDate (This value is popular among activation an licensing software). To do that we must clean FSEG from date strings which may interfere with our intended calculation. In this commit we remove all static dates from FSEG which are not BiosDate. In the next commit we will deal with dynamic strings that may sometimes appear in FSEG (from SMBIOS tables).
Removing BIOS_DATE and RELEASE_DATE_STR static strings from FSEG will only affect machines using legacy SMBIOS:
* SystemBiosDate will change from 04/01/2014 (BIOS_DATE) to 01/01/2011 (RELEASE_DATE_STR).
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 | 2 +- src/fw/smbios.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/fw/biostables.c b/src/fw/biostables.c index fe8626ef..269b8582 100644 --- a/src/fw/biostables.c +++ b/src/fw/biostables.c @@ -401,7 +401,7 @@ smbios_new_type_0(void *start, }
#define BIOS_NAME "SeaBIOS" -#define BIOS_DATE "04/01/2014" +static const char BIOS_DATE[] = "04/01/2014";
static int smbios_romfile_setup(void) diff --git a/src/fw/smbios.c b/src/fw/smbios.c index f3b5ad9d..6f33a329 100644 --- a/src/fw/smbios.c +++ b/src/fw/smbios.c @@ -134,7 +134,7 @@ get_external(int type, char **p, unsigned *nr_structs, end += size; \ p->field = ++str_index; \ } else { \ - memcpy(end, def, sizeof(def)); \ + memcpy(end, (void*)def, sizeof(def)); \ end += sizeof(def); \ p->field = ++str_index; \ } \ @@ -161,7 +161,7 @@ get_external(int type, char **p, unsigned *nr_structs, } while (0)
/* Type 0 -- BIOS Information */ -#define RELEASE_DATE_STR "01/01/2011" +static volatile const char RELEASE_DATE_STR[] = "01/01/2011"; static void * smbios_init_type_0(void *start) {
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);
Hi,
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).
I don't think we need a separate fw_cfg file for this.
If qemu provides a type0 table we can just take the date from it.
If qemu does not provide a type0 table we should not have a inconsistency in the first place.
cheers, Gerd
So what do you suggest?
If qemu supplies a date in legacy/non-legacy we override the date in FSEG(0xfff5) with it (or make sure it appears in FSEG and is greater than 06/23/99)?
Do we wish to support cases where:
wmic bios get releasedate HKLM/HARDWARE/DESCRIPTION/System SystemBiosDate
return different dates? (this is what happens at the moment)
Sam
On 29 May 2019, at 8:36, Gerd Hoffmann kraxel@redhat.com wrote:
Hi,
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).
I don't think we need a separate fw_cfg file for this.
If qemu provides a type0 table we can just take the date from it.
If qemu does not provide a type0 table we should not have a inconsistency in the first place.
cheers, Gerd
On Wed, May 29, 2019 at 09:39:05AM +0300, Sam Eiderman wrote:
So what do you suggest?
If qemu supplies a date in legacy/non-legacy we override the date in FSEG(0xfff5) with it
I think that makes sense, yes.
cheers, Gerd
The implementation for this is very hacky.
For non-legacy smbios we need to deconstruct a packed smbios table that qemu supplies. Preferably around this code in smbios_romfile_setup(void):
/* 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; }
We need to do some smbios tables math to find the correct date string.
For legacy smbios we can read the fw_cfg that supplies the date, just like get_field() does, ignoring the unused case for get_external() which may also require deconstructing a packed smbios table table.
This will not look good.
I think using a simple fw_cfg value for this task has the following advantages: Makes less changes in the current flow Only enabled by “power users” that know what they are doing, otherwise nothing changes Detaches smbios date from the date that is reported via SystemBiosDate (So we can now support cases that require different values to be reported that way) Makes the SystemBiosDate behavior more explicit
WDYT?
Sam
On 29 May 2019, at 9:49, Gerd Hoffmann kraxel@redhat.com wrote:
On Wed, May 29, 2019 at 09:39:05AM +0300, Sam Eiderman wrote:
So what do you suggest?
If qemu supplies a date in legacy/non-legacy we override the date in FSEG(0xfff5) with it
I think that makes sense, yes.
cheers, Gerd
On Wed, May 29, 2019 at 10:27:57PM +0300, Sam Eiderman wrote:
The implementation for this is very hacky.
For non-legacy smbios we need to deconstruct a packed smbios table that qemu supplies. Preferably around this code in smbios_romfile_setup(void):
/* 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; }
We need to do some smbios tables math to find the correct date string.
For legacy smbios we can read the fw_cfg that supplies the date, just like get_field() does, ignoring the unused case for get_external() which may also require deconstructing a packed smbios table table.
This will not look good.
I think using a simple fw_cfg value for this task has the following advantages: Makes less changes in the current flow Only enabled by “power users” that know what they are doing, otherwise nothing changes Detaches smbios date from the date that is reported via SystemBiosDate (So we can now support cases that require different values to be reported that way) Makes the SystemBiosDate behavior more explicit
WDYT?
I'm not sure adding another interface between QEMU and SeaBIOS is less complex. Also, ideally this would work regardless of where the smbios table came from (seabios, qemu, coreboot, csm, etc.). So, I think it would be preferable if the code followed SMBiosAddr to the date string - take a look at display_uuid() for similar code.
That said, if there's a simpler approach that would be even better.
-Kevin