/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    smbios.c.c

Abstract:

    SMBIOS interface for WMI

--*/

#if defined(_AMD64_) || defined(i386)

#include "wmikmp.h"
#include "arc.h"
#include "smbios.h"

void WmipGetSMBiosFromLoaderBlock(
    PVOID LoaderBlockPtr
    );

NTSTATUS WmipSMBiosDataRegQueryRoutine(
    IN PWSTR ValueName,
    IN ULONG ValueType,
    IN PVOID ValueData,
    IN ULONG ValueLength,
    IN PVOID Context,
    IN PVOID EntryContext
    );

BOOLEAN WmipIsSMBiosKey(
    HANDLE ParentKeyHandle,
    PWCHAR KeyName,
    PUCHAR *SMBiosTableVirtualAddress,
    PULONG SMBiosTableLength
    );

NTSTATUS WmipSMBiosIdentifierRegQueryRoutine(
    IN PWSTR ValueName,
    IN ULONG ValueType,
    IN PVOID ValueData,
    IN ULONG ValueLength,
    IN PVOID Context,
    IN PVOID EntryContext
    );

BOOLEAN WmipFindSMBiosEPSHeader(
    PUCHAR SMBiosVirtualAddress,
    ULONG BiosSize,
    PSMBIOS_EPS_HEADER EPSHeader
    );

NTSTATUS WmipFindSMBiosStructure(
    IN UCHAR Type,
    OUT PVOID *StructPtr,
    OUT PVOID *MapPtr,
    OUT PULONG MapSize
    );

NTSTATUS WmipFindSysIdTable(
    PPHYSICAL_ADDRESS SysidTablePhysicalAddress,
    PUCHAR SysIdBiosRevision,
    PULONG NumberEntries
    );

NTSTATUS WmipParseSysIdTable(
    PHYSICAL_ADDRESS PhysicalAddress,
    ULONG NumberEntries,
    PSYSID_UUID SysIdUuid,
    ULONG *SysIdUuidCount,
    PSYSID_1394 SysId1394,
    ULONG *SysId1394Count
    );

NTSTATUS WmipGetSysIds(
    PSYSID_UUID *SysIdUuid,
    ULONG *SysIdUuidCount,
    PSYSID_1394 *SysId1394,
    ULONG *SysId1394Count
    );

#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("PAGEDATA")
#endif
//
// These hold pointers to the SMBIOS data. If SMBIOS data is in the table
// format then WmipSMBiosTablePhysicalAddress holds the physical address of
// the table. If the SMBIOS was gathered at boot time by NTDETECT then
// WmipSMBiosTableVirtualAddress holds a pointer to a paged pool buffer that
// contains the SMBIOS data. In both cases WmipSMBiosTableLength holds the
// actual length of the SMBIOS table. If both the physical and virtual
// addresses are 0 then SMBIOS data is not available.
PHYSICAL_ADDRESS WmipSMBiosTablePhysicalAddress = {0};
PUCHAR WmipSMBiosTableVirtualAddress = NULL;
ULONG WmipSMBiosTableLength = 0;
SMBIOSVERSIONINFO WmipSMBiosVersionInfo = {0};
BOOLEAN WmipSMBiosChecked = FALSE;

//
// Have we tried to get SYSID yet and if so what was the ultimate status
BOOLEAN WmipSysIdRead;
NTSTATUS WmipSysIdStatus;

//
// Count and arrays of UUIDs and 1394 ids
PSYSID_UUID WmipSysIdUuid;
ULONG WmipSysIdUuidCount;

PSYSID_1394 WmipSysId1394;
ULONG WmipSysId1394Count;

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,WmipGetSMBiosFromLoaderBlock)

#pragma alloc_text(PAGE,WmipFindSMBiosEPSHeader)
#pragma alloc_text(PAGE,WmipFindSMBiosTable)
#pragma alloc_text(PAGE,WmipFindSMBiosStructure)
#pragma alloc_text(PAGE,WmipFindSysIdTable)
#pragma alloc_text(PAGE,WmipParseSysIdTable)
#pragma alloc_text(PAGE,WmipGetSysIds)
#pragma alloc_text(PAGE,WmipGetSMBiosTableData)
#pragma alloc_text(PAGE,WmipGetSMBiosEventlog)
#pragma alloc_text(PAGE,WmipDockUndockEventCallback)

#pragma alloc_text(PAGE,WmipSMBiosDataRegQueryRoutine)
#pragma alloc_text(PAGE,WmipSMBiosIdentifierRegQueryRoutine)
#pragma alloc_text(PAGE,WmipIsSMBiosKey)


#endif


BOOLEAN WmipFindSMBiosEPSHeader(
    PUCHAR SMBiosVirtualAddress,
    ULONG BiosSize,
    PSMBIOS_EPS_HEADER EPSHeader
    )
/*++

Routine Description:

    Search for the SMBIOS 2.1 EPS structure and copy it.

Arguments:

    SMBiosVirtualAddress is the beginning virtual address to start searching
        for the SMBIOS 2.1 EPS anchor string.

    BiosSize is the number of bytes to search for the anchor string

    EPSHeader is the memory into which the EPS header is copied
Return Value:

    Pointer to SMBIOS 2.1 EPS or NULL if EPS not found

--*/
{
    PUCHAR SearchEnd;
    UCHAR CheckSum;
    PSMBIOS_EPS_HEADER SMBiosEPSHeader;
    PDMIBIOS_EPS_HEADER DMIBiosEPSHeader;
    ULONG i;
    ULONG CheckLength;

    PAGED_CODE();

    UNREFERENCED_PARAMETER (BiosSize);

    RtlZeroMemory(EPSHeader, sizeof(SMBIOS_EPS_HEADER));
    
    //
    // Scan the bios for the two anchor strings that that signal the SMBIOS
    // table.
    SearchEnd = SMBiosVirtualAddress + SMBIOS_EPS_SEARCH_SIZE -
                                             2 * SMBIOS_EPS_SEARCH_INCREMENT;

    while (SMBiosVirtualAddress < SearchEnd)
    {
       SMBiosEPSHeader = (PSMBIOS_EPS_HEADER)SMBiosVirtualAddress;
       DMIBiosEPSHeader = (PDMIBIOS_EPS_HEADER)SMBiosVirtualAddress;

       //
       // First check for _DMI_ anchor string
       if ((*((PULONG)DMIBiosEPSHeader->Signature2) == DMI_EPS_SIGNATURE) &&
           (DMIBiosEPSHeader->Signature2[4] == '_'))
       {
           WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Found possible DMIBIOS EPS Header at %x\n", SMBiosEPSHeader));
           CheckLength = sizeof(DMIBIOS_EPS_HEADER);
       }

       //
       // Then check for full _SM_ anchor string
       else if ((*((PULONG)SMBiosEPSHeader->Signature) == SMBIOS_EPS_SIGNATURE) &&
                (SMBiosEPSHeader->Length >= sizeof(SMBIOS_EPS_HEADER)) &&
                (*((PULONG)SMBiosEPSHeader->Signature2) == DMI_EPS_SIGNATURE) &&
                (SMBiosEPSHeader->Signature2[4] == '_' ))
       {
           WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Found possible SMBIOS EPS Header at %p\n", SMBiosEPSHeader));
           CheckLength = SMBiosEPSHeader->Length;
       } else {
           //
           // Did not find anchor string, go search next paragraph
           SMBiosVirtualAddress += SMBIOS_EPS_SEARCH_INCREMENT;
           continue;
       }

       //
       // Verify anchor string with checksum
       CheckSum = 0;
       for (i = 0; i < CheckLength ; i++)
       {
           CheckSum = (UCHAR)(CheckSum + SMBiosVirtualAddress[i]);
       }

       if (CheckSum == 0)
       {
           WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Found SMBIOS EPS Header at %p\n", SMBiosEPSHeader));
           if (CheckLength == sizeof(DMIBIOS_EPS_HEADER))
           {
               //
               // We only had got a DMI header so copy that
               //
               RtlCopyMemory(&EPSHeader->Signature2[0],
                             DMIBiosEPSHeader,
                             sizeof(DMIBIOS_EPS_HEADER));
           } else {
               //
               // We got the full SMBIOS header so copy that
               //
               RtlCopyMemory(EPSHeader,
                             SMBiosEPSHeader,
                             sizeof(SMBIOS_EPS_HEADER));
           }
           return(TRUE);
       }
       SMBiosVirtualAddress += SMBIOS_EPS_SEARCH_INCREMENT;

    }

    WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: SMBIOS EPS Header not found\n"));
    return(FALSE);
}

//
// On X86 we look at the hardware device description keys to find the
// one that contains the SMBIOS data. The key is created by NTDETECT in
// the case that the machine only supports the 2.0 calling mechanism
//

//
// For x86 and ia64 the key is Someplace like
// HKLM\Hardware\System\MultiFunctionAdapter\<some number>
//
NTSTATUS WmipSMBiosIdentifierRegQueryRoutine(
    IN PWSTR ValueName,
    IN ULONG ValueType,
    IN PVOID ValueData,
    IN ULONG ValueLength,
    IN PVOID Context,
    IN PVOID EntryContext
    )
/*++

Routine Description:

    Registry query values callback routine for reading SMBIOS data from
    registry.

Arguments:

    ValueName - the name of the value

    ValueType - the type of the value

    ValueData - the data in the value (unicode string data)

    ValueLength - the number of bytes in the value data

    Context - Not used

    EntryContext - Pointer to PUCHAR to store a pointer to
        store the SMBIOS data read from the registry value. If this is NULL
        then the caller is not interested in the SMBIOS data

Return Value:

    NT Status code -
        STATUS_SUCCESS - Identifier is valid for SMBIOS key
        STATUS_UNSUCCESSFUL - Identifier is not valid for SMBIOS key

--*/
{
    NTSTATUS Status;

    PAGED_CODE();

    UNREFERENCED_PARAMETER (ValueName);
    UNREFERENCED_PARAMETER (ValueLength);
    UNREFERENCED_PARAMETER (Context);
    UNREFERENCED_PARAMETER (EntryContext);

    Status =  ((ValueType == REG_SZ) &&
               (ValueData != NULL) &&
               (wcscmp(ValueData, SMBIOSIDENTIFIERVALUEDATA) == 0)) ?
                       STATUS_SUCCESS :
                       STATUS_UNSUCCESSFUL;

    return(Status);
}

NTSTATUS WmipSMBiosDataRegQueryRoutine(
    IN PWSTR ValueName,
    IN ULONG ValueType,
    IN PVOID ValueData,
    IN ULONG ValueLength,
    IN PVOID Context,
    IN PVOID EntryContext
    )
/*++

Routine Description:

    Registry query values callback routine for reading SMBIOS data from
    registry.

Arguments:

    ValueName - the name of the value

    ValueType - the type of the value

    ValueData - the data in the value (unicode string data)

    ValueLength - the number of bytes in the value data

    Context - Not used

    EntryContext - Pointer to PUCHAR to store a pointer to
        store the SMBIOS data read from the registry value. If this is NULL
        then the caller is not interested in the SMBIOS data

Return Value:

    NT Status code -
        STATUS_SUCCESS - SMBIOS data is present in the value
        STATUS_INSUFFICIENT_RESOURCES - Not enough memory to keep SMBIOS data
        STATUS_UNSUCCESSFUL - SMBios data is not present in the value

--*/
{
    NTSTATUS Status;
    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
    PUCHAR Buffer;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
    ULONG BufferSize;
    PREGQUERYBUFFERXFER RegQueryBufferXfer;

    PAGED_CODE();

    UNREFERENCED_PARAMETER (ValueName);
    UNREFERENCED_PARAMETER (ValueLength);
    UNREFERENCED_PARAMETER (Context);

    WmipAssert(EntryContext != NULL);

    if ((ValueType == REG_FULL_RESOURCE_DESCRIPTOR) &&
        (ValueData != NULL))
    {
        //
        // On x86 get the actual SMBIOS data out of the registry and
        // place it into a buffer
        //
        RegQueryBufferXfer = (PREGQUERYBUFFERXFER)EntryContext;

        PartialResourceList = &(((PCM_FULL_RESOURCE_DESCRIPTOR)ValueData)->PartialResourceList);
        if (PartialResourceList->Count > 1)
        {
            //
            // Second partial resource descriptor contains SMBIOS data. There
            // should ALWAYS be a second partial resource descriptor and it
            // may have 0 bytes in the case that SMBIOS data was not collected
            // by NTDETECT.

            PartialDescriptor = &PartialResourceList->PartialDescriptors[0];
            Buffer = (PUCHAR)PartialDescriptor +
                             sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) +
                             PartialDescriptor->u.DeviceSpecificData.DataSize;
            PartialDescriptor = (PCM_PARTIAL_RESOURCE_DESCRIPTOR)Buffer;
            BufferSize = PartialDescriptor->u.DeviceSpecificData.DataSize;
            RegQueryBufferXfer->BufferSize = BufferSize;
            Status = STATUS_SUCCESS;
            if (BufferSize > 0)
            {
                RegQueryBufferXfer->Buffer = (PUCHAR)ExAllocatePoolWithTag(
                                                                  PagedPool,
                                                                  BufferSize,
                                                                  WMIPOOLTAG);
                if (RegQueryBufferXfer->Buffer != NULL)
                {
                    Buffer += sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
                    RtlCopyMemory(RegQueryBufferXfer->Buffer,
                                  Buffer,
                                  BufferSize);
                } else {
                    Status = STATUS_INSUFFICIENT_RESOURCES;
                }
            }
        } else {
            WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Old NTDETECT.COM - No SMBIOS partial resource descriptor\n"));
            Status = STATUS_SUCCESS;
            RegQueryBufferXfer->BufferSize = 0;
        }
    } else {
        Status = STATUS_UNSUCCESSFUL;
    }
    return(Status);
}

BOOLEAN WmipIsSMBiosKey(
    HANDLE ParentKeyHandle,
    PWCHAR KeyName,
    PUCHAR *SMBiosTableVirtualAddress,
    PULONG SMBiosTableLength
    )
{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING BaseKeyName;
    HANDLE KeyHandle;
    RTL_QUERY_REGISTRY_TABLE QueryTable[3];
    REGQUERYBUFFERXFER RegQueryBufferXfer = {0, NULL};

    PAGED_CODE();

    RtlInitUnicodeString(&BaseKeyName,
                         KeyName);

    InitializeObjectAttributes(&ObjectAttributes,
                               &BaseKeyName,
                               OBJ_CASE_INSENSITIVE,
                               ParentKeyHandle,
                               NULL);

    Status = ZwOpenKey(&KeyHandle,
                       KEY_READ,
                       &ObjectAttributes);
    if (NT_SUCCESS(Status))
    {
        RtlZeroMemory(QueryTable, sizeof(QueryTable));
        QueryTable[0].Name = SMBIOSIDENTIFIERVALUENAME;
        QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
        QueryTable[0].DefaultType = REG_SZ;
        QueryTable[0].QueryRoutine = WmipSMBiosIdentifierRegQueryRoutine;

        QueryTable[1].Name = SMBIOSDATAVALUENAME;
        QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED;
        QueryTable[1].EntryContext = &RegQueryBufferXfer;
        QueryTable[1].DefaultType = REG_FULL_RESOURCE_DESCRIPTOR;
        QueryTable[1].QueryRoutine = WmipSMBiosDataRegQueryRoutine;

        Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_ABSOLUTE,
                                        (PWCHAR)KeyHandle,
                                        QueryTable,
                                        NULL,
                                        NULL);
        if (NT_SUCCESS(Status))
        {
            *SMBiosTableVirtualAddress = RegQueryBufferXfer.Buffer;
            *SMBiosTableLength = RegQueryBufferXfer.BufferSize;
        }

        ZwClose(KeyHandle);
    } else {
        WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: H/D/S/MultifunctionAdapter subkey open error %x\n",
                 Status));
    }

    return(NT_SUCCESS(Status) ? TRUE : FALSE);
}

BOOLEAN WmipFindSMBiosTable(
    PPHYSICAL_ADDRESS SMBiosTablePhysicalAddress,
    PUCHAR *SMBiosTableVirtualAddress,
    PULONG SMBiosTableLength,
    PSMBIOSVERSIONINFO SMBiosVersionInfo
    )
/*++

Routine Description:

    Determines if the SMBIOS data is available

Arguments:

    SMBiosTablePhysicalAddress points to a variable to return the physical
        address of the SMBIOS 2.1 table. If table is not available then
        it returns with 0.

    SMBiosTableVirtualAddress points to a variable to return the virtual
        address of the SMBIOS 2.0 table as collected by NTDETECT. If the
        SMBIOS 2.0 data was not collected by NTDETECT it returns with 0.

    SMBiosTableLength points to a variable to return the length of the
        SMBIOS table data.

    SMBiosVersionInfo returns with the version information for SMBIOS

Return Value:

    TRUE if SMBIOS data is available, else FALSE

--*/
{
    PHYSICAL_ADDRESS BiosPhysicalAddress;
    PUCHAR BiosVirtualAddress;
    PDMIBIOS_EPS_HEADER DMIBiosEPSHeader;

    NTSTATUS Status;
    UNICODE_STRING BaseKeyName;
    HANDLE KeyHandle;
    OBJECT_ATTRIBUTES ObjectAttributes;
    ULONG KeyIndex;
    ULONG KeyInformationLength;
    
    union {
        KEY_BASIC_INFORMATION Info;
        UCHAR Buffer[sizeof(KEY_BASIC_INFORMATION) + (MAXSMBIOSKEYNAMESIZE * sizeof(WCHAR))];
    } KeyBasic;

    SMBIOS_EPS_HEADER SMBiosEPSHeader;
    BOOLEAN HaveEPSHeader = FALSE;
    BOOLEAN SearchForHeader = TRUE;

    PAGED_CODE();

    SMBiosTablePhysicalAddress->QuadPart = 0;
    *SMBiosTableVirtualAddress = NULL;
    *SMBiosTableLength = 0;

    //
    // First check registry to see if we captured SMBIOS 2.0 data in
    // NTDETECT. Search the keys under
    // MultiFunctionAdapter for the one
    // with the "PnP Bios" (x86)
    //
    RtlInitUnicodeString(&BaseKeyName,
                         SMBIOSPARENTKEYNAME);

    InitializeObjectAttributes(&ObjectAttributes,
                               &BaseKeyName,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               NULL);

    Status = ZwOpenKey(&KeyHandle,
                       KEY_READ,
                       &ObjectAttributes);

    if (NT_SUCCESS(Status))
    {

        KeyIndex = 0;
        
        while (NT_SUCCESS(Status))
        {

            Status = ZwEnumerateKey(KeyHandle,
                                    KeyIndex++,
                                    KeyBasicInformation,
                                    &KeyBasic.Info,
                                    sizeof(KeyBasic.Buffer) - sizeof(WCHAR),
                                    &KeyInformationLength);
            if (NT_SUCCESS(Status))
            {
                KeyBasic.Info.Name[KeyBasic.Info.NameLength / sizeof(WCHAR)] = UNICODE_NULL;
                if (WmipIsSMBiosKey(KeyHandle,
                                    KeyBasic.Info.Name,
                                    SMBiosTableVirtualAddress,
                                    SMBiosTableLength))
                {
                    if (*SMBiosTableLength != 0)
                    {
                        SMBiosVersionInfo->Used20CallingMethod = TRUE;
                        SearchForHeader = FALSE;
                    }
                    break;
                }
            } else {
                WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Status %x enum H\\D\\S\\MultiFunctionAdapter key, index %d\n",
                 Status, KeyIndex-1));
            }
        }
        ZwClose(KeyHandle);
    } else {
        WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Status %x opening H\\D\\S\\MultiFunctionAdapter key\n",
                 Status));
    }
    
    if (SearchForHeader)
    {
        //
        // If not in registry then check for EPS in the BIOS
        BiosPhysicalAddress.QuadPart = SMBIOS_EPS_SEARCH_START;
        BiosVirtualAddress = MmMapIoSpace(BiosPhysicalAddress,
                                          SMBIOS_EPS_SEARCH_SIZE,
                                          MmNonCached);

        if (BiosVirtualAddress != NULL)
        {
            HaveEPSHeader = WmipFindSMBiosEPSHeader(BiosVirtualAddress,
                                                    SMBIOS_EPS_SEARCH_SIZE,
                                                    &SMBiosEPSHeader);
            MmUnmapIoSpace(BiosVirtualAddress, SMBIOS_EPS_SEARCH_SIZE);
        }
    } else {
         WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: SMBIOS data recovered from loader\n"));
    }
        
    if (HaveEPSHeader)
    {
        //
        // We found the EPS so just extract the physical
        // address of the table
        //
        DMIBiosEPSHeader = (PDMIBIOS_EPS_HEADER)&SMBiosEPSHeader.Signature2[0];

        //
        // Ignore tables with invalid length sizes
        //
        if (DMIBiosEPSHeader->StructureTableLength)
        {
            SMBiosVersionInfo->Used20CallingMethod = FALSE;

            SMBiosTablePhysicalAddress->HighPart = 0;
            SMBiosTablePhysicalAddress->LowPart = DMIBiosEPSHeader->StructureTableAddress;

            *SMBiosTableLength = DMIBiosEPSHeader->StructureTableLength;

            SMBiosVersionInfo->SMBiosMajorVersion = SMBiosEPSHeader.MajorVersion;
            SMBiosVersionInfo->SMBiosMinorVersion = SMBiosEPSHeader.MinorVersion;

            SMBiosVersionInfo->DMIBiosRevision = DMIBiosEPSHeader->Revision;
            WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: SMBIOS 2.1 data at (%x%x) size %x \n",
                          SMBiosTablePhysicalAddress->HighPart,
                          SMBiosTablePhysicalAddress->LowPart,
                          *SMBiosTableLength));
        }
    }

    return(*SMBiosTableLength > 0 ? TRUE : FALSE);
}

NTSTATUS WmipGetSMBiosTableData(
    OUT PUCHAR Buffer,
    IN OUT PULONG BufferSize,
    OUT PSMBIOSVERSIONINFO SMBiosVersionInfo
    )
/*++

Routine Description:

    Registry query values callback routine for reading SMBIOS data from
    registry.

Arguments:

    Buffer is a pointer to a buffer in which to write the SMBIOS data

    *BufferSize has the maximum number of bytes available to write into
        Buffer. On return it has the actual size of the SMBIOS data.

Return Value:

    NT Status code -
        STATUS_SUCCESS - Buffer filled with SMBIOS data
        STATUS_BUFFER_TOO_SMALL - Buffer not filled with SMBIOS data,
                                  *BufferSize returns with buffer size neeeded

--*/
{
    NTSTATUS status;
    PUCHAR SMBiosDataVirtualAddress;

    PAGED_CODE();

    WmipEnterSMCritSection();
    if (! WmipSMBiosChecked)
    {
        //
        // See if there is any SMBIOS information and if so register
        WmipFindSMBiosTable(&WmipSMBiosTablePhysicalAddress,
                            &WmipSMBiosTableVirtualAddress,
                            &WmipSMBiosTableLength,
                            &WmipSMBiosVersionInfo);
        WmipSMBiosChecked = TRUE;
    }
    WmipLeaveSMCritSection();

    if (SMBiosVersionInfo != NULL)
    {
        *SMBiosVersionInfo = WmipSMBiosVersionInfo;
    }
    if (*BufferSize >= WmipSMBiosTableLength)
    {
        if (WmipSMBiosTableLength == 0)
        {
            status = STATUS_INVALID_DEVICE_REQUEST;
        } 
        else if (WmipSMBiosTablePhysicalAddress.QuadPart != 0)
        {
            //
            // 2.1 table format - map in table and copy
            SMBiosDataVirtualAddress = MmMapIoSpace(WmipSMBiosTablePhysicalAddress,
                                                    WmipSMBiosTableLength,
                                                    MmNonCached);
            if (SMBiosDataVirtualAddress != NULL)
            {
                RtlCopyMemory(Buffer,
                          SMBiosDataVirtualAddress,
                          WmipSMBiosTableLength);

                MmUnmapIoSpace(SMBiosDataVirtualAddress,
                               WmipSMBiosTableLength);
                status = STATUS_SUCCESS;
            } else {
                status = STATUS_INSUFFICIENT_RESOURCES;
            }
        } else if (WmipSMBiosTableVirtualAddress != NULL) {
            RtlCopyMemory(Buffer,
                          WmipSMBiosTableVirtualAddress,
                          WmipSMBiosTableLength);
            status = STATUS_SUCCESS;
        } else {
            status = STATUS_INVALID_DEVICE_REQUEST;
        }
    } else {
        status = STATUS_BUFFER_TOO_SMALL;
    }

    *BufferSize = WmipSMBiosTableLength;

    return(status);
}


#define WmipUnmapSMBiosStructure(Address, Size) \
    if ((Address) != NULL) MmUnmapIoSpace((Address), (Size));

NTSTATUS WmipFindSMBiosStructure(
    IN UCHAR Type,
    OUT PVOID *StructPtr,
    OUT PVOID *MapPtr,
    OUT PULONG MapSize
    )
/*++

Routine Description:

    Find a specific SMBIOS structure in the SMBIOS information.
    WmipUnmapSNVuisStructure should be called if this function returns
    successfully.

Arguments:

    Type is structure type to find

    *StructPtr returns with pointer to beginning of structure

    *MapPtr returns with pointer to address SMBIOS data was mapped.

    *MapSize returns with size mapped
Return Value:

    STATUS

--*/
{
    NTSTATUS Status;
    BOOLEAN Found;
    PUCHAR Ptr;
    PUCHAR PtrEnd;
    PSMBIOS_STRUCT_HEADER StructHeader;

    PAGED_CODE();

    //
    // Make sure SMBIOS table has been obtained. Note we already hold
    // the critical section
    if (! WmipSMBiosChecked)
    {
        //
        // See if there is any SMBIOS information and if so register
        Found = WmipFindSMBiosTable(&WmipSMBiosTablePhysicalAddress,
                            &WmipSMBiosTableVirtualAddress,
                            &WmipSMBiosTableLength,
                            &WmipSMBiosVersionInfo);
        WmipSMBiosChecked = TRUE;
    } else {
        Found = (WmipSMBiosTableLength > 0  ? TRUE : FALSE);
    }

    if (Found)
    {
        Status = STATUS_SUCCESS;
        if (WmipSMBiosTablePhysicalAddress.QuadPart != 0)
        {
            //
            // SMBIOS is available in physical memory
            *MapPtr = MmMapIoSpace(WmipSMBiosTablePhysicalAddress,
                                   WmipSMBiosTableLength,
                                   MmCached);
            if (*MapPtr != NULL)
            {
                *MapSize = WmipSMBiosTableLength;
                Ptr = *MapPtr;
            } else {
                //
                // Lets hope this is a temporary problem
                Status = STATUS_INSUFFICIENT_RESOURCES;
                Ptr = NULL;
            }
        } else if (WmipSMBiosTableVirtualAddress != NULL) {
            *MapPtr = NULL;
            Ptr = WmipSMBiosTableVirtualAddress;
        } else {
            WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: SMBIOS table was found, but is not in physical or virtual memory\n"));
            WmipAssert(FALSE);
            Status = STATUS_UNSUCCESSFUL;
            Ptr = NULL;
        }

        if (NT_SUCCESS(Status))
        {
            //
            // Now scan the SMBIOS table to find our structure
            *StructPtr = NULL;
            PtrEnd = (PVOID)((PUCHAR)Ptr + WmipSMBiosTableLength);
            Status = STATUS_UNSUCCESSFUL;
            StructHeader = NULL;
            try
            {
                while (Ptr < PtrEnd)
                {
                    StructHeader = (PSMBIOS_STRUCT_HEADER)Ptr;

                    if (StructHeader->Type == Type)
                    {
                        WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: SMBIOS struct for type %d found at %p\n",
                             Type, Ptr));
                        *StructPtr = Ptr;
                        Status = STATUS_SUCCESS;
                        break;
                    }

                    Ptr+= StructHeader->Length;
                    while ( (*((USHORT UNALIGNED *)Ptr) != 0)  &&
                            (Ptr < PtrEnd) )
                    {
                        Ptr++;
                    }
                    Ptr += 2;
                }
            } except(EXCEPTION_EXECUTE_HANDLER) {
                WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid SMBIOS data table %p at %p\n",
                         *MapPtr, StructHeader));
                WmipAssert(FALSE);
            }

            if (! NT_SUCCESS(Status) )
            {
                WmipUnmapSMBiosStructure(*MapPtr, *MapSize);
            }
        }
    } else {
        Status = STATUS_UNSUCCESSFUL;
    }
    return(Status);
}

NTSTATUS WmipFindSysIdTable(
    PPHYSICAL_ADDRESS SysidTablePhysicalAddress,
    PUCHAR SysIdBiosRevision,
    PULONG NumberEntries
    )
/*++

Routine Description:

    Scan the system bios to search for the SYSID table

Arguments:

    *SysidTablePhysicalAddress returns with the physical address of the
        sysid table

    *SysIdBiosRevision returns with the bios revision of the sysid table

    *NumberEntries returns the number of SYSID entries in the table

Return Value:

    STATUS

--*/
{
    UCHAR Checksum;
    PUCHAR p;
    PSYSID_EPS_HEADER SysIdEps, SearchEnd;
    PHYSICAL_ADDRESS BiosPhysicalAddress;
    PUCHAR BiosVirtualAddress;
    ULONG i;
    NTSTATUS Status;

    PAGED_CODE();

    BiosPhysicalAddress.QuadPart = SYSID_EPS_SEARCH_START;
    BiosVirtualAddress = MmMapIoSpace(BiosPhysicalAddress,
                                      SYSID_EPS_SEARCH_SIZE,
                                      MmCached);

    SearchEnd = (PSYSID_EPS_HEADER)(BiosVirtualAddress + SYSID_EPS_SEARCH_SIZE);
    SysIdEps = (PSYSID_EPS_HEADER)BiosVirtualAddress;

    if (BiosVirtualAddress != NULL)
    {
        try
        {
            while (SysIdEps < SearchEnd)
            {
                if (((*(PULONG)SysIdEps->Signature) == SYSID_EPS_SIGNATURE) &&
                     (*(PUSHORT)(&SysIdEps->Signature[4]) == SYSID_EPS_SIGNATURE2) &&
                     (SysIdEps->Signature[6] == '_') )
                {
                    //
                    // This may be the SYSID table, check the checksum
                    Checksum = 0;
                    p = (PUCHAR)SysIdEps;
                    for (i = 0; i < sizeof(SYSID_EPS_HEADER); i++)
                    {
                        Checksum = (UCHAR)(Checksum + p[i]);
                    }

                    if (Checksum == 0)
                    {
                        WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: SYSID EPS found at %p\n",
                                     SysIdEps));
                        break;
                    } else {
                        WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalis SYSID EPS checksum at %p\n",
                                  SysIdEps));
                    }
                }

                SysIdEps = (PSYSID_EPS_HEADER)( ((PUCHAR)SysIdEps) +
                                     SYSID_EPS_SEARCH_INCREMENT);
            }

            if (SysIdEps != SearchEnd)
            {
                SysidTablePhysicalAddress->HighPart = 0;
                SysidTablePhysicalAddress->LowPart = SysIdEps->SysIdTableAddress;
                *SysIdBiosRevision = SysIdEps->BiosRev;
                *NumberEntries = SysIdEps->SysIdCount;
                Status = STATUS_SUCCESS;
            } else {
                //
                // Not finding the SYSID EPS is a terminal error
                Status = STATUS_UNSUCCESSFUL;
            }
        } except(EXCEPTION_EXECUTE_HANDLER) {
            WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid SYSID EPS Table at %p\n", SysIdEps));
            Status = STATUS_UNSUCCESSFUL;
        }

        MmUnmapIoSpace(BiosVirtualAddress, SYSID_EPS_SEARCH_SIZE);
    } else {
        //
        // Lets hope that failure to map memory is a temporary problem
        Status = STATUS_INSUFFICIENT_RESOURCES;
    }
    return(Status);
}

typedef enum
{
	SYSID_UNKNOWN_TYPE,
	SYSID_UUID_TYPE,
	SYSID_1394_TYPE
} SYSID_ENTRY_TYPE, *PSYSID_ENTRY_TYPE;

NTSTATUS WmipParseSysIdTable(
    PHYSICAL_ADDRESS PhysicalAddress,
    ULONG NumberEntries,
    PSYSID_UUID SysIdUuid,
    ULONG *SysIdUuidCount,
    PSYSID_1394 SysId1394,
    ULONG *SysId1394Count
    )
/*++

Routine Description:

    Determine the set of UUIDs and 1394 Ids that are in the sysid table

Arguments:

    PhysicalAddress is the physical address of the SysId table

    NumberEntries is the number of entries in the SysId table

    SysIdUuid returns filled with an array of UUIDs. If NULL then no
        UUIDs are returned.

    *SysIdUuidCount returns with the number of UUIDs in the table

    SysId1394 returns filled with an array of 1394 ids. If NULL then no
        1394 ids are returned.

    *SysId1394Count returns with the number of 1394 ids in the table


Return Value:

    STATUS

--*/
{
    NTSTATUS Status;
    ULONG TableSize = NumberEntries * LARGEST_SYSID_TABLE_ENTRY;
    ULONG i;
    ULONG  j;
    PUCHAR VirtualAddress;
    PSYSID_TABLE_ENTRY SysId;
    PUCHAR p;
    UCHAR Checksum;
    ULONG Length;
    ULONG x1394Count, UuidCount;
    ULONG BytesLeft;
	SYSID_ENTRY_TYPE SysidType;

    PAGED_CODE();

    VirtualAddress = MmMapIoSpace(PhysicalAddress,
                                  TableSize,
                                  MmCached);

    if (VirtualAddress != NULL)
    {
        UuidCount = 0;
        x1394Count = 0;
        SysId = (PSYSID_TABLE_ENTRY)VirtualAddress;
        BytesLeft = TableSize;
        Status = STATUS_SUCCESS;

        for (i = 0; i < NumberEntries; i++)
        {
            //
            // Make sure we have not moved beyond the end of the mapped
            // memory.
            if (BytesLeft >= sizeof(SYSID_TABLE_ENTRY))
            {

                Length = SysId->Length;
				
				//
				// Determine what kind of sysid we have
				//
				if ((RtlCompareMemory(&SysId->Type,
									  SYSID_TYPE_UUID, 6) == 6) &&
					(Length == sizeof(SYSID_UUID_ENTRY)))
				{
					SysidType = SYSID_UUID_TYPE;
				} else if ((RtlCompareMemory(&SysId->Type,
											SYSID_TYPE_1394, 6) == 6) &&
						   (Length == sizeof(SYSID_1394_ENTRY))) {

					SysidType = SYSID_1394_TYPE;
				} else {
					//
					// unknown type SYSID
					//
					WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Unknown SYSID type %c%c%c%c%c%c found at %p\n",
								 SysId->Type[0],
								 SysId->Type[1],
								 SysId->Type[2],
								 SysId->Type[3],
								 SysId->Type[4],
								 SysId->Type[5],
								 SysId
							 ));
					Status = STATUS_UNSUCCESSFUL;
					break;
				}
				
                //
                // Validate checksum for this table entry

                if (BytesLeft >= Length)
                {
                    Checksum = 0;
                    p = (PUCHAR)SysId;
                    for (j = 0; j < Length; j++)
                    {
                        Checksum = (UCHAR)(Checksum + p[j]);
                    }

                    if (Checksum != 0)
                    {
                        WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: SYSID Table checksum is not valid at %p\n",
                                 SysId));
                        Status = STATUS_UNSUCCESSFUL;
                        break;
                    }

                    //
                    // Determine what kind of SYSID we have
                    if (SysidType == SYSID_UUID_TYPE)
                    {
                        if (BytesLeft >= sizeof(SYSID_UUID_ENTRY))
                        {
                            //
                            // _UUID_ type SYSID
                            UuidCount++;
                            if (SysIdUuid != NULL)
                            {
                                RtlCopyMemory(SysIdUuid,
                                              SysId->Data,
                                              sizeof(SYSID_UUID));
                                SysIdUuid++;
                            }
                         } else {
                            Status = STATUS_UNSUCCESSFUL;
                            break;
                         }
                    } else if (SysidType == SYSID_1394_TYPE) {
                        if (BytesLeft >= sizeof(SYSID_1394_ENTRY))
                        {
                            //
                            // _1394_ type SYSID
                            x1394Count++;
                            if (SysId1394 != NULL)
                            {
                                RtlCopyMemory(SysId1394,
                                              SysId->Data,
                                              sizeof(SYSID_1394));
                                SysId1394++;
                            }
                        } else {
                            Status = STATUS_UNSUCCESSFUL;
                            break;
                        }
                    } else {
						WmipAssert(FALSE);
						Status = STATUS_UNSUCCESSFUL;
						break;
					}
                    
                    //
                    // Advance to next sysid in table
                    SysId = (PSYSID_TABLE_ENTRY)(((PUCHAR)SysId) + Length);
                    BytesLeft -= Length;
                } else {
                    WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: SYSID Table at %p is larger at %p than expected",
                             VirtualAddress, SysId));
                    Status = STATUS_UNSUCCESSFUL;
                    break;
                }
            } else {
                WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: SYSID Table at %p is larger at %p than expected",
                         VirtualAddress, SysId));
                Status = STATUS_UNSUCCESSFUL;
                break;
            }
        }

        *SysIdUuidCount = UuidCount;
        *SysId1394Count = x1394Count;

        MmUnmapIoSpace(VirtualAddress, TableSize);
    } else {
        //
        // Lets hope that the failure to map is a temporary condition
        Status = STATUS_INSUFFICIENT_RESOURCES;
    }

    return(Status);
}

NTSTATUS WmipGetSysIds(
    PSYSID_UUID *SysIdUuid,
    ULONG *SysIdUuidCount,
    PSYSID_1394 *SysId1394,
    ULONG *SysId1394Count
    )
/*++

Routine Description:

    This routine will obtain the 1394 and UUID sysids from the bios. First
    we look for a specific memory signature that contains a list of 1394 and
    UUID sysids. If we do not find that we then look at the SMBIOS information
    structure SYSTEM INFORMATION (type 1) which may have it embedded within
    it. If not then we give up.

Arguments:

    *SysIdUuid returns pointing to an array of UUID Sysids

    *SysIdUuidCount returns with the number of UUID Sysids in *SysIdUuid

    *SysId1394 returns pointing to an array of 1394 Sysids

    *SysId1394Count returns with the number of 1394 Sysids in *SysIdUuid


Return Value:

    NT Status code

--*/
{
    NTSTATUS Status;
    PHYSICAL_ADDRESS PhysicalAddress;
    UCHAR BiosRevision;
    ULONG NumberEntries;
    ULONG UuidCount, x1394Count;
    PSYSID_UUID Uuid;
    PSYSID_1394 x1394;
    ULONG TotalSize, x1394Size, UuidSize;

    PAGED_CODE();

    WmipEnterSMCritSection();

    //
    // First See if we have already obtained the SYSIDS
    if (! WmipSysIdRead)
    {
        //
        // First see if the sysids are maintained in a separate SYSID table
        Status = WmipFindSysIdTable(&PhysicalAddress,
                                    &BiosRevision,
                                    &NumberEntries);

        if (NT_SUCCESS(Status))
        {
            //
            // Get the count of entries in each table
            Status = WmipParseSysIdTable(PhysicalAddress,
                                         NumberEntries,
                                         NULL,
                                         &UuidCount,
                                         NULL,
                                         &x1394Count);

            if (NT_SUCCESS(Status))
            {
                 //
                // Get the entire SYSID table

                UuidSize = UuidCount * sizeof(SYSID_UUID);
                x1394Size = x1394Count * sizeof(SYSID_1394);
                TotalSize = UuidSize+x1394Size;

                if (TotalSize > 0)
                {
                    Uuid = ExAllocatePoolWithTag(PagedPool,
                                                 TotalSize,
                                                 WMISYSIDPOOLTAG);

                    if (Uuid == NULL)
                    {
                        WmipLeaveSMCritSection();
                        return(STATUS_INSUFFICIENT_RESOURCES);
                    }

                    x1394 = (PSYSID_1394)( ((PUCHAR)Uuid) + UuidSize );

                    //
                    // Now get the SYSIDs
                    Status = WmipParseSysIdTable(PhysicalAddress,
                                         NumberEntries,
                                         Uuid,
                                         &UuidCount,
                                         x1394,
                                         &x1394Count);

                    if (NT_SUCCESS(Status))
                    {
                        WmipSysIdUuid = Uuid;
                        WmipSysIdUuidCount = UuidCount;
                        WmipSysId1394 = x1394;
                        WmipSysId1394Count = x1394Count;
                    } else {
                        ExFreePool(Uuid);
                    }

                }

            }
        } else {
            //
            // Get SYSID information from SMBIOS
            PVOID MapAddress;
            PSMBIOS_SYSTEM_INFORMATION_STRUCT Info;
            ULONG MapSize;

            Status = WmipFindSMBiosStructure(SMBIOS_SYSTEM_INFORMATION,
                                             (PVOID *)&Info,
                                             &MapAddress,
                                             &MapSize);

            if (NT_SUCCESS(Status))
            {
                Uuid = NULL;
                WmipSysId1394 = NULL;
                WmipSysId1394Count = 0;
                try
                {
                    if (Info->Length > SMBIOS_SYSTEM_INFORMATION_LENGTH_20)
                    {
                        Uuid = ExAllocatePoolWithTag(PagedPool,
                                                           sizeof(SYSID_UUID),
                                                           WMISYSIDPOOLTAG);
                        if (Uuid != NULL)
                        {
                            RtlCopyMemory(Uuid,
                                          Info->Uuid,
                                          sizeof(SYSID_UUID));
                            WmipSysIdUuidCount = 1;
                            WmipSysIdUuid = Uuid;
                            Status = STATUS_SUCCESS;
                        } else {
                            ExFreePool(Uuid);
                            Status = STATUS_UNSUCCESSFUL;
                        }
                    } else {
                        WmipSysIdUuid = NULL;
                        WmipSysIdUuidCount = 0;
                    }
                } except(EXCEPTION_EXECUTE_HANDLER) {
                    WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid SMBIOS SYSTEM INFO structure %p\n",
                              Info));
                    WmipAssert(FALSE);
                    Status = STATUS_UNSUCCESSFUL;
                }

                WmipUnmapSMBiosStructure(MapAddress, MapSize);
            }
        }

        //
        // Mark that we were not able to obtain SysId Information
        WmipSysIdRead = (Status != STATUS_INSUFFICIENT_RESOURCES) ? TRUE : FALSE;
        WmipSysIdStatus = Status;
    }

    WmipLeaveSMCritSection();

    if (NT_SUCCESS(WmipSysIdStatus))
    {
        *SysIdUuid = WmipSysIdUuid;
        *SysIdUuidCount = WmipSysIdUuidCount;
        *SysId1394 = WmipSysId1394;
        *SysId1394Count = WmipSysId1394Count;
    }

    return(WmipSysIdStatus);
}

NTSTATUS WmipGetSMBiosEventlog(
    PUCHAR Buffer,
    PULONG BufferSize
    )
/*++

Routine Description:

    Return the contents of the SMBios eventlog

Arguments:

    Buffer is a pointer to a buffer that receives the eventlog

    *BufferSize on entry has the size of the buffer that can receive
        the eventlog data, on return it has the number of bytes used
        by the smbios eventlog data or the number of bytes needed for
        the smbios eventlog data.

Return Value:

    NT Status code -
        STATUS_SUCCESS - Buffer filled with SMBIOS eventlog data
        STATUS_BUFFER_TOO_SMALL - Buffer not filled with SMBIOS eventlog data,
                                  *BufferSize returns with buffer size neeeded

--*/
{
    PVOID MapAddress;
    PSMBIOS_SYSTEM_EVENTLOG_STRUCT SystemEventlog;
    ULONG MapSize;
    USHORT LogAreaLength;
    UCHAR AccessMethod;
    ACCESS_METHOD_ADDRESS AccessMethodAddress;
    PSMBIOS_EVENTLOG_INFO EventlogInfo;
    UCHAR LogHeaderDescExists;
    PUCHAR EventlogArea;
    NTSTATUS Status;
    USHORT LogTypeDescLength;
    ULONG SizeNeeded;

    PAGED_CODE();
    Status = WmipFindSMBiosStructure(SMBIOS_SYSTEM_EVENTLOG,
                                     (PVOID *)&SystemEventlog,
                                     &MapAddress,
                                     &MapSize);

    if (NT_SUCCESS(Status))
    {
        //
        // Copy data out of SMBIOS eventlog header so we can unmap quickly
        //
        LogAreaLength = SystemEventlog->LogAreaLength;
        AccessMethod = SystemEventlog->AccessMethod;
        AccessMethodAddress = SystemEventlog->AccessMethodAddress;

        if (SystemEventlog->Length >= SMBIOS_SYSTEM_EVENTLOG_LENGTH)
        {
            LogTypeDescLength = SystemEventlog->NumLogTypeDescriptors *
                                SystemEventlog->LenLogTypeDescriptors;
            LogHeaderDescExists = 1;
            if (SystemEventlog->Length != (LogTypeDescLength +
                                  FIELD_OFFSET(SMBIOS_SYSTEM_EVENTLOG_STRUCT,
                                               LogTypeDescriptor)))
            {
                //
                // The SMBIOS spec says that the Length of the structure
                // is the length of the base part of the structures plus
                // the length of the type descriptors. Since this is not
                // the case we may have run into a buggy bios
                //
                WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: SMBIOS System Eventlog structure %p size is %x, but expecting %x\n",
                           SystemEventlog,
                           SystemEventlog->Length,
                           (LogTypeDescLength +
                            FIELD_OFFSET(SMBIOS_SYSTEM_EVENTLOG_STRUCT,
                                         LogTypeDescriptor)) ));
                WmipAssert(FALSE);
                WmipUnmapSMBiosStructure(MapAddress, MapSize);
                Status = STATUS_UNSUCCESSFUL;
                return(Status);
            }
        } else {
            LogTypeDescLength = 0;
            LogHeaderDescExists = 0;
        }

        SizeNeeded = FIELD_OFFSET(SMBIOS_EVENTLOG_INFO, VariableData) +
                                         LogTypeDescLength +
                                         LogAreaLength;
        if (*BufferSize >= SizeNeeded)
        {
            EventlogInfo = (PSMBIOS_EVENTLOG_INFO)Buffer;
            EventlogInfo->LogTypeDescLength = LogTypeDescLength;
            EventlogInfo->LogHeaderDescExists = LogHeaderDescExists;
            EventlogInfo->Reserved = 0;

            EventlogArea = &EventlogInfo->VariableData[LogTypeDescLength];

            if (LogHeaderDescExists == 1)
            {
                //
                // if log header descriptors exist (smbios 2.1+) then copy
                // rest of smbios header plus log type descriptors
                //
                RtlCopyMemory(&EventlogInfo->LogAreaLength,
                              &SystemEventlog->LogAreaLength,
                              (SystemEventlog->Length -
                                  FIELD_OFFSET(SMBIOS_SYSTEM_EVENTLOG_STRUCT,
                                               LogAreaLength)));
            } else {
                //
                // if no log header descriptors then just copy smbios 2.0
                // defined fields and zero out rest of structure
                //
                RtlCopyMemory(&EventlogInfo->LogAreaLength,
                              &SystemEventlog->LogAreaLength,
                        FIELD_OFFSET(SMBIOS_EVENTLOG_INFO, LogHeaderFormat) -
                        FIELD_OFFSET(SMBIOS_EVENTLOG_INFO, LogAreaLength));

                *((PUSHORT)&EventlogInfo->LogHeaderFormat) = 0;
                EventlogInfo->LengthEachLogTypeDesc = 0;
            }

            WmipUnmapSMBiosStructure(MapAddress, MapSize);

            switch(AccessMethod)
            {
                case ACCESS_METHOD_MEMMAP:
                {
                    //
                    // Eventlog is maintained in physical memory
                    //
                    PHYSICAL_ADDRESS PhysicalAddress;
                    PUCHAR EventlogVirtualAddress;

                    PhysicalAddress.HighPart = 0;
                    PhysicalAddress.LowPart = AccessMethodAddress.AccessMethodAddress.PhysicalAddress32;
                    EventlogVirtualAddress = MmMapIoSpace(PhysicalAddress,
                                                LogAreaLength,
                                                MmCached);

                    if ((EventlogArea != NULL) &&
                        (EventlogVirtualAddress != NULL))
                    {
                        RtlCopyMemory(EventlogArea,
                                      EventlogVirtualAddress,
                                      LogAreaLength);
                        MmUnmapIoSpace(EventlogVirtualAddress,
                                       LogAreaLength);
                        Status = STATUS_SUCCESS;
                    } else {
                        Status = STATUS_UNSUCCESSFUL;
                    }
                    break;
                };

                case ACCESS_METHOD_INDEXIO_1:
                case ACCESS_METHOD_INDEXIO_2:
                case ACCESS_METHOD_INDEXIO_3:
                // falls through to ACCESS_METHOD_GPNV

                case ACCESS_METHOD_GPNV:
                {
                    WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: SMBIOS Eventlog access method GPNV %x\n",
                                     AccessMethod));
                    Status = STATUS_UNSUCCESSFUL;
                    break;
                };

                default:
                {
                    WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: SMBIOS Eventlog access method %x\n",
                                     AccessMethod));
                    WmipAssert(FALSE);
                    Status = STATUS_UNSUCCESSFUL;
                }
            };

        } else {
            WmipUnmapSMBiosStructure(MapAddress, MapSize);
            Status = STATUS_BUFFER_TOO_SMALL;
        }
        *BufferSize = SizeNeeded;
    }
    return(Status);
}

NTSTATUS
WmipDockUndockEventCallback(
    IN PVOID NotificationStructure,
    IN PVOID Context
    )
{
    PAGED_CODE();

    UNREFERENCED_PARAMETER (NotificationStructure);
    UNREFERENCED_PARAMETER (Context);

    //
    // if SMBIOS data is obtained via the table in the bios, then reset
    // the flag to indicate that we need to rescan for the table. It is
    // possible that a dock or undock could have changed the data. If we
    // obtained the data from ntdetect then there is nothing we can do
    // since we cannot call the bios.
    if (WmipSMBiosTablePhysicalAddress.QuadPart != 0)
    {
        WmipEnterSMCritSection();
        WmipSMBiosChecked = FALSE;
        WmipLeaveSMCritSection();
    }

    return(STATUS_SUCCESS);
}

#endif

#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg()
#endif

