[PATCH v8 5/9] ntdll: Return BIOS info from NtQuerySystemInformation on Linux

Huw Davies huw at codeweavers.com
Wed Jul 11 03:43:43 CDT 2018


On Wed, Jun 20, 2018 at 11:18:48PM -0600, Alex Henrie wrote:
> Signed-off-by: Alex Henrie <alexhenrie24 at gmail.com>
> ---
>  dlls/ntdll/nt.c         | 171 ++++++++++++++++++++++++++++++++++++++++
>  dlls/ntdll/tests/info.c |  11 ++-
>  2 files changed, 180 insertions(+), 2 deletions(-)
> 
> diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c
> index dc0ce04f42..c46c3cd01d 100644
> --- a/dlls/ntdll/nt.c
> +++ b/dlls/ntdll/nt.c
> @@ -66,6 +66,35 @@
>  
>  WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
>  
> +#include "pshpack1.h"
> +
> +struct smbios_prologue {
> +    BYTE calling_method;
> +    BYTE major_version;
> +    BYTE minor_version;
> +    BYTE revision;
> +    DWORD length;
> +};
> +
> +struct smbios_bios {
> +    BYTE type;
> +    BYTE length;
> +    WORD handle;
> +    BYTE vendor;
> +    BYTE version;
> +    WORD start;
> +    BYTE date;
> +    BYTE size;
> +    UINT64 characteristics;
> +};
> +
> +#include "poppack.h"
> +
> +/* Firmware table providers */
> +#define ACPI 0x41435049
> +#define FIRM 0x4649524D
> +#define RSMB 0x52534D42
> +
>  /*
>   *	Token
>   */
> @@ -1850,6 +1879,126 @@ static NTSTATUS create_logical_proc_info(SYSTEM_LOGICAL_PROCESSOR_INFORMATION **
>  }
>  #endif
>  
> +static inline void copy_smbios_string(char **buffer, char *s, size_t len)

This needs to go inside the #ifdef linux

> +{
> +    if (!len) return;
> +    strcpy(*buffer, s);

Since we're passing the length, this would be better as a memcpy( , , len + 1).

> +    *buffer += len + 1;
> +}
> +
> +#ifdef linux
> +
> +static size_t get_smbios_string(const char *path, char *str, size_t size)
> +{
> +    FILE *file;
> +    size_t len;
> +
> +    if (!(file = fopen(path, "r")))
> +        return 0;
> +
> +    len = fread(str, 1, size - 1, file);
> +    fclose(file);
> +
> +    if (len >= 1 && str[len - 1] == '\n')
> +    {
> +        str[len - 1] = 0;
> +        len--;
> +    }
> +    else
> +    {
> +        str[len] = 0;
> +    }

This would be cleaner as:

    if (len >= 1 && str[len - 1] == '\n')
        len--;

    str[len] = 0;

(basically avoid the code duplication of str[len] = 0).

> +
> +    return len;
> +}
> +
> +static inline NTSTATUS get_firmware_info(SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti,
> +                                         ULONG available_len, ULONG *required_len)

Don't use inline on this big function.  These days compilers are pretty good
at this sort of thing anyway.

> +{
> +    switch (sfti->ProviderSignature)
> +    {
> +    case RSMB:
> +        {
> +            char bios_vendor[128], bios_version[128], bios_date[128];
> +            size_t bios_vendor_len, bios_version_len, bios_date_len;
> +            char *buffer = (char*)sfti->TableBuffer;
> +            BYTE string_count;
> +            struct smbios_prologue *prologue;
> +            struct smbios_bios *bios;
> +
> +#define S(s) s, sizeof(s)
> +            bios_vendor_len = get_smbios_string("/sys/class/dmi/id/bios_vendor", S(bios_vendor));
> +            bios_version_len = get_smbios_string("/sys/class/dmi/id/bios_version", S(bios_version));
> +            bios_date_len = get_smbios_string("/sys/class/dmi/id/bios_date", S(bios_date));
> +#undef S
> +
> +            *required_len = sizeof(struct smbios_prologue);
> +
> +            *required_len += sizeof(struct smbios_bios);
> +            *required_len += max(bios_vendor_len + bios_version_len + bios_date_len + 4, 2);
> +
> +            sfti->TableBufferLength = *required_len;
> +
> +            *required_len += FIELD_OFFSET(SYSTEM_FIRMWARE_TABLE_INFORMATION, TableBuffer);
> +
> +            if (available_len < *required_len)
> +                return STATUS_BUFFER_TOO_SMALL;
> +
> +            prologue = (struct smbios_prologue*)buffer;
> +            prologue->calling_method = 0;
> +            prologue->major_version = 2;
> +            prologue->minor_version = 0;
> +            prologue->revision = 0;
> +            prologue->length = sfti->TableBufferLength - sizeof(struct smbios_prologue);
> +            buffer += sizeof(struct smbios_prologue);
> +
> +            string_count = 0;
> +            bios = (struct smbios_bios*)buffer;
> +            bios->type = 0;
> +            bios->length = sizeof(struct smbios_bios);
> +            bios->handle = 0;
> +            bios->vendor = bios_vendor_len ? ++string_count : 0;
> +            bios->version = bios_version_len ? ++string_count : 0;
> +            bios->start = 0;
> +            bios->date = bios_date_len ? ++string_count : 0;
> +            bios->size = 0;
> +            bios->characteristics = 0x4; /* not supported */
> +            buffer += sizeof(struct smbios_bios);
> +
> +            if (string_count)
> +            {
> +                copy_smbios_string(&buffer, bios_vendor, bios_vendor_len);
> +                copy_smbios_string(&buffer, bios_version, bios_version_len);
> +                copy_smbios_string(&buffer, bios_date, bios_date_len);
> +                memset(buffer, 0, 1);
> +            }
> +            else
> +            {
> +                memset(buffer, 0, 2);
> +            }

I dislike this if block, partly because of the use of memsets and partly
because of the code duplication in setting the final '\0'.
This looks cleaner to me:

           copy_smbios_string(&buffer, bios_vendor, bios_vendor_len);
           copy_smbios_string(&buffer, bios_version, bios_version_len);
           copy_smbios_string(&buffer, bios_date, bios_date_len);

           if (!string_count) *buffer++ = 0;
           *buffer++ = 0;

(Yes, it calls copy_smbios_string() in the string_count == 0 case, but I can
live with that for the sake of simpler code).

Obviously this applies to the other patches too.

> +
> +            return STATUS_SUCCESS;
> +        }
> +    default:
> +        {
> +            FIXME("info_class SYSTEM_FIRMWARE_TABLE_INFORMATION\n");

Printing the provider signature in the fixme would be a good idea.

> +            return STATUS_NOT_IMPLEMENTED;
> +        }
> +    }
> +}
> +
> +#else
> +
> +static inline NTSTATUS get_firmware_info(SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti,
> +                                         ULONG available_len, ULONG *required_len)

Again, drop the inline.

> +{
> +    FIXME("info_class SYSTEM_FIRMWARE_TABLE_INFORMATION\n");
> +    sfti->TableBufferLength = 0;
> +    return STATUS_NOT_IMPLEMENTED;
> +}
> +
> +#endif
> +
>  /******************************************************************************
>   * NtQuerySystemInformation [NTDLL.@]
>   * ZwQuerySystemInformation [NTDLL.@]
> @@ -2359,6 +2508,28 @@ NTSTATUS WINAPI NtQuerySystemInformation(
>              else ret = STATUS_INFO_LENGTH_MISMATCH;
>          }
>          break;
> +    case SystemFirmwareTableInformation:
> +        {
> +            SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti = (SYSTEM_FIRMWARE_TABLE_INFORMATION*)SystemInformation;
> +            len = FIELD_OFFSET(SYSTEM_FIRMWARE_TABLE_INFORMATION, TableBuffer);
> +            if (Length < len)
> +            {
> +                ret = STATUS_INFO_LENGTH_MISMATCH;
> +                break;
> +            }
> +
> +            switch (sfti->Action)
> +            {
> +            case SystemFirmwareTable_Get:
> +                ret = get_firmware_info(sfti, Length, &len);
> +                break;
> +            default:
> +                len = 0;
> +                ret = STATUS_NOT_IMPLEMENTED;
> +                FIXME("info_class SYSTEM_FIRMWARE_TABLE_INFORMATION\n");

Let's print the action here.

> +            }
> +        }
> +        break;
>      default:
>  	FIXME("(0x%08x,%p,0x%08x,%p) stub\n",
>  	      SystemInformationClass,SystemInformation,Length,ResultLength);
> diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c
> index 9e12a013cf..29af0e3d90 100644
> --- a/dlls/ntdll/tests/info.c
> +++ b/dlls/ntdll/tests/info.c
> @@ -59,6 +59,12 @@ static DWORD one_before_last_pid = 0;
>  #define FIRM 0x4649524D
>  #define RSMB 0x52534D42
>  
> +#ifdef linux
> +static const int firmware_todo = 0;
> +#else
> +static const int firmware_todo = 1;
> +#endif
> +
>  static BOOL InitFunctionPtrs(void)
>  {
>      /* All needed functions are NT based, so using GetModuleHandle is a good check */
> @@ -842,12 +848,11 @@ static void test_query_firmware(void)
>      ok(!!sfti, "Failed to allocate memory\n");
>  
>      status = pNtQuerySystemInformation(SystemFirmwareTableInformation, sfti, 15, &len1);
> -todo_wine
>      ok(status == STATUS_INFO_LENGTH_MISMATCH || broken(status == STATUS_INVALID_INFO_CLASS) /* xp */,
>         "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status);
>      if (len1 == 0) /* xp, 2003 */
>      {
> -        skip("SystemFirmwareTableInformation is not available\n");
> +        win_skip("SystemFirmwareTableInformation is not available\n");
>          HeapFree(GetProcessHeap(), 0, sfti);
>          return;
>      }
> @@ -862,6 +867,7 @@ todo_wine
>      sfti->TableID = 0;
>  
>      status = pNtQuerySystemInformation(SystemFirmwareTableInformation, sfti, 16, &len1);
> +todo_wine_if(firmware_todo)
>      ok(status == STATUS_BUFFER_TOO_SMALL, "Expected STATUS_BUFFER_TOO_SMALL, got %08x\n", status);
>      ok(len1 >= 16, "Expected length >= 16, got %u\n", len1);
>      ok(sfti->TableBufferLength == len1 - 16, "Expected length %u, got %u\n", len1 - 16, sfti->TableBufferLength);
> @@ -873,6 +879,7 @@ todo_wine
>      {
>          sfti->TableID = i;
>          status = pNtQuerySystemInformation(SystemFirmwareTableInformation, sfti, len1, &len2);
> +todo_wine_if(firmware_todo)
>          ok(status == STATUS_SUCCESS, "Table %u: Expected STATUS_SUCCESS, got %08x\n", i, status);
>          ok(len2 == len1, "Table %u: Expected length %u, got %u\n", i, len1, len2);
>          ok(sfti->TableBufferLength == len1 - 16,
> -- 
> 2.17.1
> 
> 
> 



More information about the wine-devel mailing list