/*++

Copyright (c) 1994-2000 Microsoft Corporation

Module Name:

    pnpmap.c

Abstract:

    This module contains the code that translates the device info returned from
    the PnP BIOS into root enumerated devices.

Author:

    Robert B. Nelson (RobertN) 22-Sep-1997

Environment:

    Kernel mode

Revision History :

--*/

#include "pnpmgrp.h"
#pragma hdrstop
#include "pnpcvrt.h"
#include "pbios.h"

#ifdef POOL_TAGGING
#undef ExAllocatePool
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'PpaM')
#endif

#if UMODETEST
#define MULTIFUNCTION_KEY_NAME L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\TestSystem\\MultifunctionAdapter"
#define ENUMROOT_KEY_NAME L"\\Registry\\Machine\\System\\TestControlSet\\Enum\\Root"
#else
#define MULTIFUNCTION_KEY_NAME L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\MultifunctionAdapter"
#define ENUMROOT_KEY_NAME L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\Root"
#endif

#define BIOSINFO_KEY_NAME L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Biosinfo\\PNPBios"
#define DISABLENODES_VALUE_NAME L"DisableNodes"
#define DECODEINFO_VALUE_NAME L"FullDecodeChipsetOverride"

#define INSTANCE_ID_PREFIX      L"PnPBIOS_"

#define DEFAULT_STRING_SIZE     80
#define DEFAULT_VALUE_SIZE      80

#define DEFAULT_DEVICE_DESCRIPTION  L"Unknown device class"


#define EXCLUSION_ENTRY(a)  { a, sizeof(a) - sizeof(UNICODE_NULL) }

typedef struct  _EXCLUDED_PNPNODE  {
    PWCHAR  Id;
    ULONG   IdLength;
} EXCLUDED_PNPNODE, *PEXCLUDED_PNPNODE;

#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("INITCONST")
#endif
const EXCLUDED_PNPNODE ExcludedDevices[] =  {
    EXCLUSION_ENTRY(L"*PNP03"),     // Keyboards
    EXCLUSION_ENTRY(L"*PNP0A"),     // PCI Busses
    EXCLUSION_ENTRY(L"*PNP0E"),     // PCMCIA Busses
    EXCLUSION_ENTRY(L"*PNP0F"),     // Mice
    EXCLUSION_ENTRY(L"*IBM3780"),   // IBM Trackpoint Mouse
    EXCLUSION_ENTRY(L"*IBM3781")    // IBM Trackpoint Mouse
};

#define EXCLUDED_DEVICES_COUNT  (sizeof(ExcludedDevices) / sizeof(ExcludedDevices[0]))

const EXCLUDED_PNPNODE ExcludeIfDisabled[] = {
    EXCLUSION_ENTRY(L"*PNP0C01"),   // Motherboard resources
    EXCLUSION_ENTRY(L"*PNP0C02")    // Motherboard resources
};

#define EXCLUDE_DISABLED_COUNT  (sizeof(ExcludeIfDisabled) / sizeof(ExcludeIfDisabled[0]))

typedef struct _CLASSDATA {
    ULONG   Value;
    PWCHAR  Description;
} CLASSDATA;

const CLASSDATA Class1Descriptions[] = {
    { 0x0000, L"SCSI Controller" },
    { 0x0100, L"IDE Controller" },
    { 0x0200, L"Floppy Controller" },
    { 0x0300, L"IPI Controller" },
    { 0x0400, L"RAID Controller" },
    { 0x8000, L"Other Mass Storage" }
};

const CLASSDATA Class2Descriptions[] = {
    { 0x0000, L"Ethernet" },
    { 0x0100, L"Token ring" },
    { 0x0200, L"FDDI" },
    { 0x0300, L"ATM" },
    { 0x8000, L"Other network" }
};

const CLASSDATA Class3Descriptions[] = {
    { 0x0000, L"VGA" },
    { 0x0001, L"SVGA" },
    { 0x0100, L"XGA" },
    { 0x8000, L"Other display" }
};

const CLASSDATA Class4Descriptions[] = {
    { 0x0000, L"Video device" },
    { 0x0100, L"Audio device" },
    { 0x8000, L"Other multimedia" }
};

const CLASSDATA Class5Descriptions[] = {
    { 0x0000, L"RAM memory" },
    { 0x0100, L"Flash memory" },
    { 0x8000, L"Other memory" }
};

const CLASSDATA Class6Descriptions[] = {
    { 0x0000, L"HOST / PCI" },
    { 0x0100, L"PCI / ISA" },
    { 0x0200, L"PCI / EISA" },
    { 0x0300, L"PCI / MCA" },
    { 0x0400, L"PCI / PCI" },
    { 0x0500, L"PCI / PCMCIA" },
    { 0x0600, L"NuBus" },
    { 0x0700, L"Cardbus" },
    { 0x8000, L"Other bridge" }
};

const CLASSDATA Class7Descriptions[] = {
    { 0x0000, L"XT Serial" },
    { 0x0001, L"16450" },
    { 0x0002, L"16550" },
    { 0x0100, L"Parallel output only" },
    { 0x0101, L"BiDi Parallel" },
    { 0x0102, L"ECP 1.x parallel" },
    { 0x8000, L"Other comm" }
};

const CLASSDATA Class8Descriptions[] = {
    { 0x0000, L"Generic 8259" },
    { 0x0001, L"ISA PIC" },
    { 0x0002, L"EISA PIC" },
    { 0x0100, L"Generic 8237" },
    { 0x0101, L"ISA DMA" },
    { 0x0102, L"EISA DMA" },
    { 0x0200, L"Generic 8254" },
    { 0x0201, L"ISA timer" },
    { 0x0202, L"EISA timer" },
    { 0x0300, L"Generic RTC" },
    { 0x0301, L"ISA RTC" },
    { 0x8000, L"Other system device" }
};

const CLASSDATA Class9Descriptions[] = {
    { 0x0000, L"Keyboard" },
    { 0x0100, L"Digitizer" },
    { 0x0200, L"Mouse" },
    { 0x8000, L"Other input" }
};

const CLASSDATA Class10Descriptions[] = {
    { 0x0000, L"Generic dock" },
    { 0x8000, L"Other dock" },
};

const CLASSDATA Class11Descriptions[] = {
    { 0x0000, L"386" },
    { 0x0100, L"486" },
    { 0x0200, L"Pentium" },
    { 0x1000, L"Alpha" },
    { 0x4000, L"Co-processor" }
};

const CLASSDATA Class12Descriptions[] = {
    { 0x0000, L"Firewire" },
    { 0x0100, L"Access bus" },
    { 0x0200, L"SSA" },
    { 0x8000, L"Other serial bus" }
};

#define CLASSLIST_ENTRY(a)   { a, sizeof(a) / sizeof(a[0]) }

struct _CLASS_DESCRIPTIONS_LIST  {

    CLASSDATA const*Descriptions;
    ULONG      Count;

}   const ClassDescriptionsList[] =  {
    { NULL, 0 },
    CLASSLIST_ENTRY( Class1Descriptions ),
    CLASSLIST_ENTRY( Class2Descriptions ),
    CLASSLIST_ENTRY( Class3Descriptions ),
    CLASSLIST_ENTRY( Class4Descriptions ),
    CLASSLIST_ENTRY( Class5Descriptions ),
    CLASSLIST_ENTRY( Class6Descriptions ),
    CLASSLIST_ENTRY( Class7Descriptions ),
    CLASSLIST_ENTRY( Class8Descriptions ),
    CLASSLIST_ENTRY( Class9Descriptions ),
    CLASSLIST_ENTRY( Class10Descriptions ),
    CLASSLIST_ENTRY( Class11Descriptions ),
    CLASSLIST_ENTRY( Class12Descriptions )

};

#define CLASSLIST_COUNT  ( sizeof(ClassDescriptionsList) / sizeof(ClassDescriptionsList[0]) )

typedef struct _BIOS_DEVNODE_INFO  {
    WCHAR   ProductId[10];  // '*' + 7 char ID + NUL + NUL for REG_MULTI_SZ
    UCHAR   Handle;         // BIOS Node # / Handle
    UCHAR   TypeCode[3];
    USHORT  Attributes;
    PWSTR   Replaces;       // Instance ID of Root enumerated device being replaced

    PCM_RESOURCE_LIST               BootConfig;
    ULONG                           BootConfigLength;
    PIO_RESOURCE_REQUIREMENTS_LIST  BasicConfig;
    ULONG                           BasicConfigLength;
    PWSTR                           CompatibleIDs;  // REG_MULTI_SZ list of compatible IDs (including ProductId)
    ULONG                           CompatibleIDsLength;
    BOOLEAN                         FirmwareDisabled; // determined that it's disabled by firmware

}   BIOS_DEVNODE_INFO, *PBIOS_DEVNODE_INFO;

NTSTATUS
PbBiosResourcesToNtResources (
    IN ULONG BusNumber,
    IN ULONG SlotNumber,
    IN OUT PUCHAR *BiosData,
    OUT PIO_RESOURCE_REQUIREMENTS_LIST *ReturnedList,
    OUT PULONG ReturnedLength
    );

VOID
PnPBiosExpandProductId(
    PUCHAR CompressedId,
    PWCHAR ProductIDStr
    );

NTSTATUS
PnPBiosIoResourceListToCmResourceList(
    IN PIO_RESOURCE_REQUIREMENTS_LIST IoResourceList,
    OUT PCM_RESOURCE_LIST *CmResourceList,
    OUT ULONG *CmResourceListSize
    );

NTSTATUS
PnPBiosExtractCompatibleIDs(
    IN  PUCHAR *DevNodeData,
    IN  ULONG DevNodeDataLength,
    OUT PWSTR *CompatibleIDs,
    OUT ULONG *CompatibleIDsLength
    );

NTSTATUS
PnPBiosTranslateInfo(
    IN VOID *BiosInfo,
    IN ULONG BiosInfoLength,
    OUT PBIOS_DEVNODE_INFO *DevNodeInfoList,
    OUT ULONG *NumberNodes
    );

LONG
PnPBiosFindMatchingDevNode(
    IN PWCHAR MapperName,
    IN PCM_RESOURCE_LIST ResourceList,
    IN PBIOS_DEVNODE_INFO DevNodeInfoList,
    IN ULONG NumberNodes
    );

NTSTATUS
PnPBiosEliminateDupes(
    IN PBIOS_DEVNODE_INFO DevNodeInfoList,
    IN ULONG NumberNodes
    );

PWCHAR
PnPBiosGetDescription(
    IN PBIOS_DEVNODE_INFO DevNodeInfoEntry
    );

NTSTATUS
PnPBiosWriteInfo(
    IN PBIOS_DEVNODE_INFO DevNodeInfoList,
    IN ULONG NumberNodes
    );

VOID
PnPBiosCopyIoDecode(
    IN HANDLE EnumRootKey,
    IN PBIOS_DEVNODE_INFO DevNodeInfo
    );

NTSTATUS
PnPBiosFreeDevNodeInfo(
    IN PBIOS_DEVNODE_INFO DevNodeInfoList,
    IN ULONG NumberNodes
    );

NTSTATUS
PnPBiosCheckForHardwareDisabled(
    IN PIO_RESOURCE_REQUIREMENTS_LIST IoResourceList,
    IN OUT PBOOLEAN Disabled
    );

BOOLEAN
PnPBiosCheckForExclusion(
    IN EXCLUDED_PNPNODE const* ExclusionArray,
    IN ULONG ExclusionCount,
    IN PWCHAR PnpDeviceName,
    IN PWCHAR PnpCompatIds
    );

NTSTATUS
PnPBiosMapper(
    VOID
    );

VOID
PpFilterNtResource (
    IN PWCHAR PnpDeviceName,
    PIO_RESOURCE_REQUIREMENTS_LIST ResReqList
    );

NTSTATUS
ComPortDBAdd(
    IN  HANDLE  DeviceParamKey,
    IN  PWSTR   PortName
    );

#ifdef ALLOC_PRAGMA
BOOLEAN
PnPBiosIgnoreNode (
    PWCHAR PnpID,
    PWCHAR excludeNodes
    );
PKEY_VALUE_FULL_INFORMATION
PnPGetBiosInfoValue(
    PWCHAR ValueName
    );
NTSTATUS
PnPBiosCopyDeviceParamKey(
    IN HANDLE EnumRootKey,
    IN PWCHAR SourcePath,
    IN PWCHAR DestinationPath
    );
#pragma alloc_text(INIT, PnPBiosExpandProductId)
#pragma alloc_text(INIT, PnPBiosIgnoreNode)
#pragma alloc_text(INIT, PnPGetBiosInfoValue)
#pragma alloc_text(INIT, PnPBiosIoResourceListToCmResourceList)
#pragma alloc_text(INIT, PnPBiosExtractCompatibleIDs)
#pragma alloc_text(INIT, PnPBiosTranslateInfo)
#pragma alloc_text(INIT, PnPBiosFindMatchingDevNode)
#pragma alloc_text(INIT, PnPBiosEliminateDupes)
#pragma alloc_text(INIT, PnPBiosGetDescription)
#pragma alloc_text(INIT, PnPBiosCopyDeviceParamKey)
#pragma alloc_text(INIT, PnPBiosWriteInfo)
#pragma alloc_text(INIT, PnPBiosCopyIoDecode)
#pragma alloc_text(INIT, PnPBiosFreeDevNodeInfo)
#pragma alloc_text(INIT, PnPBiosCheckForHardwareDisabled)
#pragma alloc_text(INIT, PnPBiosCheckForExclusion)
#pragma alloc_text(INIT, PnPBiosMapper)
#pragma alloc_text(INIT, PpFilterNtResource)
#pragma alloc_text(PAGE, PnPBiosGetBiosInfo)
#endif

NTSTATUS
PnPBiosGetBiosInfo(
    OUT PVOID *BiosInfo,
    OUT ULONG *BiosInfoLength
    )
/*++

Routine Description:

    This function retrieves the PnP BIOS info accumulated by NTDETECT.COM and
    placed in the registry.

Arguments:

    BiosInfo - Set to a dynamically allocated block of information retrieved
        from the PnP BIOS by NTDETECT.  This block should be freed using
        ExFreePool.  The contents of the block are the PnP BIOS
        Installation Check Structure followed by the DevNode Structures reported
        by the BIOS.  The detailed format is documented in the PnP BIOS spec.

    BiosInfoLength - Length of the block whose address is stored in BiosInfo.


Return Value:

    STATUS_SUCCESS if no errors, otherwise the appropriate error.

--*/
{
    UNICODE_STRING                  multifunctionKeyName, biosKeyName, valueName;
    HANDLE                          multifunctionKey = NULL, biosKey = NULL;
    PKEY_BASIC_INFORMATION          keyBasicInfo = NULL;
    ULONG                           keyBasicInfoLength;
    PKEY_VALUE_PARTIAL_INFORMATION  valueInfo = NULL;
    ULONG                           valueInfoLength;
    ULONG                           returnedLength;

    PCM_FULL_RESOURCE_DESCRIPTOR    biosValue;

    ULONG                           index;
    NTSTATUS                        status = STATUS_UNSUCCESSFUL;

    //
    // The PnP BIOS info is written to one of the subkeys under
    // MULTIFUNCTION_KEY_NAME.  The appropriate key is determined by
    // enumerating the subkeys and using the first one which has a value named
    // "Identifier" that is "PNP BIOS".
    //

    PiWstrToUnicodeString(&multifunctionKeyName, MULTIFUNCTION_KEY_NAME);

    status = IopOpenRegistryKeyEx( &multifunctionKey,
                                   NULL,
                                   &multifunctionKeyName,
                                   KEY_READ
                                   );

    if (!NT_SUCCESS(status)) {

        IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                    "Could not open %S, status = %8.8X\n",
                    MULTIFUNCTION_KEY_NAME,
                    status) );

        return STATUS_UNSUCCESSFUL;
    }

    //
    // Allocate memory for key names returned from ZwEnumerateKey and values
    // returned from ZwQueryValueKey.
    //
    keyBasicInfoLength = sizeof(KEY_BASIC_INFORMATION) + DEFAULT_STRING_SIZE;
    keyBasicInfo = ExAllocatePool(PagedPool, keyBasicInfoLength + sizeof(UNICODE_NULL));

    if (keyBasicInfo == NULL)  {

        ZwClose( multifunctionKey );

        return STATUS_NO_MEMORY;
    }

    valueInfoLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + DEFAULT_STRING_SIZE;
    valueInfo = ExAllocatePool(PagedPool, valueInfoLength);

    if (valueInfo == NULL)  {

        ExFreePool( keyBasicInfo );

        ZwClose( multifunctionKey );

        return STATUS_NO_MEMORY;
    }

    //
    // Enumerate each key under HKLM\HARDWARE\\DESCRIPTION\\System\\MultifunctionAdapter
    // to locate the one representing the PnP BIOS information.
    //
    for (index = 0; ; index++) {

        status = ZwEnumerateKey( multifunctionKey,   // handle of key to enumerate
                                 index,              // index of subkey to enumerate
                                 KeyBasicInformation,
                                 keyBasicInfo,
                                 keyBasicInfoLength,
                                 &returnedLength);

        if (!NT_SUCCESS(status)) {

            if (status != STATUS_NO_MORE_ENTRIES)  {

                IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                            "Could not enumerate under key %S, status = %8.8X\n",
                            MULTIFUNCTION_KEY_NAME,
                            status) );
            }

            break;
        }

        //
        // We found a subkey, NUL terminate the name and open the subkey.
        //
        keyBasicInfo->Name[ keyBasicInfo->NameLength / 2 ] = L'\0';

        RtlInitUnicodeString(&biosKeyName, keyBasicInfo->Name);

        status = IopOpenRegistryKeyEx( &biosKey,
                                       multifunctionKey,
                                       &biosKeyName,
                                       KEY_READ
                                       );

        if (!NT_SUCCESS(status)) {

            IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                        "Could not open registry key %S\\%S, status = %8.8X\n",
                        MULTIFUNCTION_KEY_NAME,
                        keyBasicInfo->Name,
                        status) );
            break;
        }

        //
        // Now we need to check the Identifier value in the subkey to see if
        // it is PNP BIOS.
        //
        PiWstrToUnicodeString(&valueName, L"Identifier");

        status = ZwQueryValueKey( biosKey,
                                  &valueName,
                                  KeyValuePartialInformation,
                                  valueInfo,
                                  valueInfoLength,
                                  &returnedLength);


        // lets see if its the PNP BIOS identifier
        if (NT_SUCCESS(status)) {

            if (wcscmp((PWSTR)valueInfo->Data, L"PNP BIOS") == 0) {

                //
                // We found the PnP BIOS subkey, retrieve the BIOS info which
                // is stored in the "Configuration Data" value.
                //
                // We'll start off with our default value buffer and increase
                // its size if necessary.
                //

                PiWstrToUnicodeString(&valueName, L"Configuration Data");

                status = ZwQueryValueKey( biosKey,
                                          &valueName,
                                          KeyValuePartialInformation,
                                          valueInfo,
                                          valueInfoLength,
                                          &returnedLength);

                if (!NT_SUCCESS(status)) {

                    if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW) {

                        //
                        // The default buffer was too small, free it and reallocate
                        // it to the required size.
                        //
                        ExFreePool( valueInfo );

                        valueInfoLength = returnedLength;
                        valueInfo = ExAllocatePool( PagedPool, valueInfoLength );

                        if (valueInfo != NULL)  {

                            status = ZwQueryValueKey( biosKey,
                                                      &valueName,
                                                      KeyValuePartialInformation,
                                                      valueInfo,
                                                      valueInfoLength,
                                                      &returnedLength );
                        } else {

                            status = STATUS_NO_MEMORY;
                        }
                    }
                }

                if (NT_SUCCESS(status)) {

                    //
                    // We now have the PnP BIOS data but it is buried inside
                    // the resource structures.  Do some consistency checks and
                    // then extract it into its own buffer.
                    //

                    ASSERT(valueInfo->Type == REG_FULL_RESOURCE_DESCRIPTOR);

                    biosValue = (PCM_FULL_RESOURCE_DESCRIPTOR)valueInfo->Data;

                    //
                    // The WMI folks added another list so we should search for
                    // the PnPBIOS one, but for now the BIOS one is always
                    // first.
                    //

                    *BiosInfoLength = biosValue->PartialResourceList.PartialDescriptors[0].u.DeviceSpecificData.DataSize;
                    *BiosInfo = ExAllocatePool(PagedPool, *BiosInfoLength);

                    if (*BiosInfo != NULL) {

                        RtlCopyMemory( *BiosInfo,
                                       &biosValue->PartialResourceList.PartialDescriptors[1],
                                       *BiosInfoLength );

                        status = STATUS_SUCCESS;

                    } else {

                        *BiosInfoLength = 0;

                        status = STATUS_NO_MEMORY;
                    }

                } else {

                    IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                                "Error retrieving %S\\%S\\Configuration Data, status = %8.8X\n",
                                MULTIFUNCTION_KEY_NAME,
                                keyBasicInfo->Name,
                                status) );
                }

                //
                // We found the PnP BIOS entry, so close the key handle and
                // return.
                //

                ZwClose(biosKey);

                break;
            }
        }

        //
        // That wasn't it so close this handle and try the next subkey.
        //
        ZwClose(biosKey);
    }

    //
    // Cleanup the dynamically allocated temporary buffers.
    //

    if (valueInfo != NULL) {

        ExFreePool(valueInfo);
    }

    if (keyBasicInfo != NULL) {

        ExFreePool(keyBasicInfo);
    }

    ZwClose(multifunctionKey);

    return status;
}

VOID
PnPBiosExpandProductId(
    PUCHAR CompressedId,
    PWCHAR ProductIDStr
    )
/*++

Routine Description:

    This function expands a PnP Device ID from the 4 byte compressed form into
    an 7 character unicode string.  The string is then NUL terminated.

Arguments:

    CompressedId - Pointer to the 4 byte compressed Device ID as defined in the
        PnP Specification.

    ProductIDStr - Pointer to the 16 byte buffer in which the unicode string
        version of the ID is placed.


Return Value:

    NONE.

--*/
{
    static const CHAR HexDigits[16] = "0123456789ABCDEF";

    ProductIDStr[0] = (CompressedId[0] >> 2) + 0x40;
    ProductIDStr[1] = (((CompressedId[0] & 0x03) << 3) | (CompressedId[1] >> 5)) + 0x40;
    ProductIDStr[2] = (CompressedId[1] & 0x1f) + 0x40;
    ProductIDStr[3] = HexDigits[CompressedId[2] >> 4];
    ProductIDStr[4] = HexDigits[CompressedId[2] & 0x0F];
    ProductIDStr[5] = HexDigits[CompressedId[3] >> 4];
    ProductIDStr[6] = HexDigits[CompressedId[3] & 0x0F];
    ProductIDStr[7] = 0x00;
}

BOOLEAN
PnPBiosIgnoreNode (
    PWCHAR PnpID,
    PWCHAR excludeNodes
    )
{
    BOOLEAN bRet=FALSE;
    ULONG   keyLen;
    PWCHAR  pTmp;

    ASSERT(excludeNodes);

    //
    //excludeNodes is multi-sz, so walk through each one and check it.
    //
    pTmp=excludeNodes;

    while (*pTmp != '\0') {

        keyLen = (ULONG)wcslen(pTmp);

        if (RtlCompareMemory(PnpID,pTmp,keyLen*sizeof (WCHAR)) == keyLen*sizeof (WCHAR)) {
            bRet=TRUE;
            break;
        }
        pTmp = pTmp + keyLen + 1;

    }


    return bRet;
}

PKEY_VALUE_FULL_INFORMATION
PnPGetBiosInfoValue(
    PWCHAR ValueName
    )
{
    UNICODE_STRING biosKeyName;
    HANDLE  biosKey;
    NTSTATUS status;
    PKEY_VALUE_FULL_INFORMATION info;

    info = NULL;
    PiWstrToUnicodeString(&biosKeyName, BIOSINFO_KEY_NAME);
    status = IopOpenRegistryKeyEx( &biosKey,
                                   NULL,
                                   &biosKeyName,
                                   KEY_READ
                                   );

    if (NT_SUCCESS(status)) {

        IopGetRegistryValue (biosKey, ValueName, &info);
        ZwClose (biosKey);
    }

    return info;
}

BOOLEAN
PnPBiosCheckForExclusion(
    IN EXCLUDED_PNPNODE const*Exclusions,
    IN ULONG  ExclusionCount,
    IN PWCHAR PnpDeviceName,
    IN PWCHAR PnpCompatIds
    )
{
    PWCHAR idPtr;
    ULONG exclusionIndex;

    for (exclusionIndex = 0; exclusionIndex < ExclusionCount; exclusionIndex++) {

        idPtr = PnpDeviceName;

        if (RtlCompareMemory( idPtr,
                              Exclusions[ exclusionIndex ].Id,
                              Exclusions[ exclusionIndex ].IdLength) != Exclusions[ exclusionIndex ].IdLength )  {

            idPtr = PnpCompatIds;

            if (idPtr != NULL)  {

                while (*idPtr != '\0') {

                    if (RtlCompareMemory( idPtr,
                                          Exclusions[ exclusionIndex ].Id,
                                          Exclusions[ exclusionIndex ].IdLength) == Exclusions[ exclusionIndex ].IdLength )  {

                        break;
                    }

                    idPtr += 9;
                }

                if (*idPtr == '\0') {

                    idPtr = NULL;
                }
            }
        }

        if (idPtr != NULL)  {

            break;
        }
    }

    if (exclusionIndex < ExclusionCount) {
        return TRUE;
    }

    return FALSE;
}

NTSTATUS
PnPBiosIoResourceListToCmResourceList(
    IN PIO_RESOURCE_REQUIREMENTS_LIST IoResourceList,
    OUT PCM_RESOURCE_LIST *CmResourceList,
    OUT ULONG *CmResourceListSize
    )
/*++

Routine Description:

    Converts an IO_RESOURCE_REQUIREMENTS_LIST into a CM_RESOURCE_LIST.  This
    routine is used to convert the list of resources currently being used by a
    device into a form suitable for writing to the BootConfig registry value.

Arguments:

    IoResourceList - Pointer to the input list.

    CmResourceList - Pointer to a PCM_RESOURCE_LIST which is set to the
        dynamically allocated and filled in using the data from IoResourceList.

    CmResourceListSize - Pointer to a variable which is set to the size in bytes
        of the dynamically allocated *CmResourceList.

Return Value:

    STATUS_SUCCESS if no errors, otherwise the appropriate error.

--*/
{
    PCM_PARTIAL_RESOURCE_LIST       partialList;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor;
    PIO_RESOURCE_DESCRIPTOR         ioDescriptor;
    ULONG                           descIndex;

    //
    // Since this routine is only used to translate the allocated resources
    // returned by the PnP BIOS, we can assume that there is only 1 alternative
    // list
    //

    ASSERT(IoResourceList->AlternativeLists == 1);

    //
    // Calculate the size of the translated list and allocate memory for it.
    //
    *CmResourceListSize = sizeof(CM_RESOURCE_LIST) +
                          (IoResourceList->AlternativeLists - 1) * sizeof(CM_FULL_RESOURCE_DESCRIPTOR) +
                          (IoResourceList->List[0].Count - 1) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);

    *CmResourceList = ExAllocatePool( PagedPool, *CmResourceListSize );

    if (*CmResourceList == NULL) {

        *CmResourceListSize = 0;

        return STATUS_NO_MEMORY;
    }

    //
    // Copy the header info from the requirements list to the resource list.
    //
    (*CmResourceList)->Count = 1;

    (*CmResourceList)->List[ 0 ].InterfaceType = IoResourceList->InterfaceType;
    (*CmResourceList)->List[ 0 ].BusNumber = IoResourceList->BusNumber;

    partialList = &(*CmResourceList)->List[ 0 ].PartialResourceList;

    partialList->Version = IoResourceList->List[ 0 ].Version;
    partialList->Revision = IoResourceList->List[ 0 ].Revision;
    partialList->Count = 0;

    //
    // Translate each resource descriptor, currently we only handle ports,
    // memory, interrupts, and dma.  The current implementation of the routine
    // which converts from ISA PnP Resource data to IO_RESOURCE_REQUIREMENTS
    // won't generate any other descriptor types given the data returned from
    // the BIOS.
    //

    partialDescriptor = &partialList->PartialDescriptors[ 0 ];
    for (descIndex = 0; descIndex < IoResourceList->List[ 0 ].Count; descIndex++) {

        ioDescriptor = &IoResourceList->List[ 0 ].Descriptors[ descIndex ];

        switch (ioDescriptor->Type) {

        case CmResourceTypePort:
            partialDescriptor->u.Port.Start = ioDescriptor->u.Port.MinimumAddress;
            partialDescriptor->u.Port.Length = ioDescriptor->u.Port.Length;
            break;

        case CmResourceTypeInterrupt:
            if (ioDescriptor->u.Interrupt.MinimumVector == (ULONG)2 ) {
                *CmResourceListSize -= sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
                continue;
            }
            partialDescriptor->u.Interrupt.Level = ioDescriptor->u.Interrupt.MinimumVector;
            partialDescriptor->u.Interrupt.Vector = ioDescriptor->u.Interrupt.MinimumVector;
            partialDescriptor->u.Interrupt.Affinity = ~0ul;
            break;

        case CmResourceTypeMemory:
            partialDescriptor->u.Memory.Start = ioDescriptor->u.Memory.MinimumAddress;
            partialDescriptor->u.Memory.Length = ioDescriptor->u.Memory.Length;
            break;

        case CmResourceTypeDma:
            partialDescriptor->u.Dma.Channel = ioDescriptor->u.Dma.MinimumChannel;
            partialDescriptor->u.Dma.Port = 0;
            partialDescriptor->u.Dma.Reserved1 = 0;
            break;

        default:
            IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                        "Unexpected ResourceType (%d) in I/O Descriptor\n",
                        ioDescriptor->Type) );

#if DBG
            // DbgBreakPoint();
#endif
            break;
        }

        partialDescriptor->Type = ioDescriptor->Type;
        partialDescriptor->ShareDisposition = ioDescriptor->ShareDisposition;
        partialDescriptor->Flags = ioDescriptor->Flags;
        partialDescriptor++;

        partialList->Count++;
    }

    return STATUS_SUCCESS;
}

NTSTATUS
PnPBiosExtractCompatibleIDs(
    IN  PUCHAR *DevNodeData,
    IN  ULONG DevNodeDataLength,
    OUT PWSTR *CompatibleIDs,
    OUT ULONG *CompatibleIDsLength
    )
{
    PWCHAR  idPtr;
    PUCHAR  currentPtr, endPtr;
    UCHAR   tagName;
    ULONG   increment;
    ULONG   compatibleCount;

    endPtr = &(*DevNodeData)[DevNodeDataLength];

    compatibleCount = 0;

    for (currentPtr = *DevNodeData; currentPtr < endPtr; currentPtr += increment) {

        tagName = *currentPtr;

        if (tagName == TAG_COMPLETE_END)  {

            break;
        }

        //
        // Determine the size of the BIOS resource descriptor
        //

        if (!(tagName & LARGE_RESOURCE_TAG)) {
            increment = (USHORT)(tagName & SMALL_TAG_SIZE_MASK);
            increment++;     // length of small tag
            tagName &= SMALL_TAG_MASK;
        } else {
            increment = *(USHORT UNALIGNED *)(&currentPtr[1]);
            increment += 3;     // length of large tag
        }

        if (tagName == TAG_COMPATIBLE_ID) {

            compatibleCount++;
        }
    }

    if (compatibleCount == 0) {
        *CompatibleIDs = NULL;
        *CompatibleIDsLength = 0;

        return STATUS_SUCCESS;
    }

    *CompatibleIDsLength = (compatibleCount * 9 + 1) * sizeof(WCHAR);
    *CompatibleIDs = ExAllocatePool(PagedPool, *CompatibleIDsLength);

    if (*CompatibleIDs == NULL)  {

        *CompatibleIDsLength = 0;
        return STATUS_NO_MEMORY;
    }

    idPtr = *CompatibleIDs;

    for (currentPtr = *DevNodeData; currentPtr < endPtr; currentPtr += increment) {

        tagName = *currentPtr;

        if (tagName == TAG_COMPLETE_END)  {

            break;
        }

        //
        // Determine the size of the BIOS resource descriptor
        //

        if (!(tagName & LARGE_RESOURCE_TAG)) {
            increment = (USHORT)(tagName & SMALL_TAG_SIZE_MASK);
            increment++;     // length of small tag
            tagName &= SMALL_TAG_MASK;
        } else {
            increment = *(USHORT UNALIGNED *)(&currentPtr[1]);
            increment += 3;     // length of large tag
        }

        if (tagName == TAG_COMPATIBLE_ID) {

            *idPtr = '*';
            PnPBiosExpandProductId(&currentPtr[1], &idPtr[1]);
            idPtr += 9;
        }
    }

    *idPtr++ = '\0';  // Extra NUL for REG_MULTI_SZ
    *CompatibleIDsLength = (ULONG)(idPtr - *CompatibleIDs) * sizeof(WCHAR);

    return STATUS_SUCCESS;
}

NTSTATUS
PnPBiosTranslateInfo(
    IN VOID *BiosInfo,
    IN ULONG BiosInfoLength,
    OUT PBIOS_DEVNODE_INFO *DevNodeInfoList,
    OUT ULONG *NumberNodes
    )
/*++

Routine Description:

    Translates the devnode info retrieved from the BIOS.

Arguments:

    BiosInfo - The PnP BIOS Installation Check Structure followed by the
        DevNode Structures reported by the BIOS.  The detailed format is
        documented in the PnP BIOS spec.

    BiosInfoLength - Length in bytes of the block whose address is stored in
        BiosInfo.

    DevNodeInfoList - Dynamically allocated array of BIOS_DEVNODE_INFO
        structures, one for each device reported by the BIOS.  The information
        supplied by the BIOS: device ID, type, current resources, and supported
        configurations is converted into a more useful format.  For example the
        current resource allocation is converted from ISA PnP descriptors into
        an IO_RESOURCE_REQUIREMENTS_LIST and then into a CM_RESOURCE_LIST for
        storing into the BootConfig registry value.

    NumberNodes - Number of BIOS_DEVNODE_INFO elements pointed to by
        DevNodeInfoList.

Return Value:

    STATUS_SUCCESS if no errors, otherwise the appropriate error.

--*/
{
    PCM_PNP_BIOS_INSTALLATION_CHECK biosInstallCheck;
    PCM_PNP_BIOS_DEVICE_NODE        devNodeHeader;
    PBIOS_DEVNODE_INFO              devNodeInfo;

    PIO_RESOURCE_REQUIREMENTS_LIST  tempResReqList;

    PUCHAR                          currentPtr;
    LONG                            lengthRemaining;

    LONG                            remainingNodeLength;

    ULONG                           numNodes;
    ULONG                           nodeIndex;
    PUCHAR                          configPtr;
    ULONG                           configListLength;
    NTSTATUS                        status;
    ULONG                           convertFlags = 0;
    PKEY_VALUE_FULL_INFORMATION     fullValueInfo;

    //
    // Make sure the data is at least large enough to hold the BIOS Installation
    // Check structure and check that the PnP signature is correct.
    //
    if (BiosInfoLength < sizeof(CM_PNP_BIOS_INSTALLATION_CHECK)) {

        IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                    "BiosInfoLength (%d) is smaller than sizeof(PNPBIOS_INSTALLATION_CHECK) (%d)\n",
                    BiosInfoLength,
                    sizeof(CM_PNP_BIOS_INSTALLATION_CHECK)) );

        return STATUS_UNSUCCESSFUL;
    }

    biosInstallCheck = (PCM_PNP_BIOS_INSTALLATION_CHECK)BiosInfo;

    if (biosInstallCheck->Signature[0] != '$' ||
        biosInstallCheck->Signature[1] != 'P' ||
        biosInstallCheck->Signature[2] != 'n' ||
        biosInstallCheck->Signature[3] != 'P') {

        return STATUS_UNSUCCESSFUL;
    }
    //
    // First scan the data and count the devnodes to determine the size of our
    // allocated data structures.
    //
    currentPtr = (PUCHAR)BiosInfo + biosInstallCheck->Length;
    lengthRemaining = BiosInfoLength - biosInstallCheck->Length;

    for (numNodes = 0; lengthRemaining > sizeof(CM_PNP_BIOS_DEVICE_NODE); numNodes++) {

        devNodeHeader = (PCM_PNP_BIOS_DEVICE_NODE)currentPtr;

        if (devNodeHeader->Size > lengthRemaining) {

            IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                        "Node # %d, invalid size (%d), length remaining (%d)\n",
                        devNodeHeader->Node,
                        devNodeHeader->Size,
                        lengthRemaining) );

            return STATUS_UNSUCCESSFUL;
        }

        currentPtr += devNodeHeader->Size;
        lengthRemaining -= devNodeHeader->Size;
    }

    //
    // Allocate the list of translated devnodes.
    //
    devNodeInfo = ExAllocatePool( PagedPool, numNodes * sizeof(BIOS_DEVNODE_INFO) );

    if (devNodeInfo == NULL) {

        return STATUS_NO_MEMORY;
    }

    //
    // Should we force all fixed IO decodes to 16bit?
    //
    fullValueInfo = PnPGetBiosInfoValue(DECODEINFO_VALUE_NAME);
    if (fullValueInfo) {

        if (fullValueInfo->Type == REG_DWORD && 
            fullValueInfo->DataLength == sizeof(ULONG) && 
            *(PULONG)((PUCHAR)fullValueInfo + fullValueInfo->DataOffset)) {

            convertFlags |= PPCONVERTFLAG_FORCE_FIXED_IO_16BIT_DECODE;
        }
        ExFreePool(fullValueInfo);
    }
    //
    // Now scan the data translating the info for each devnode into an entry in
    // our devNodeInfo array.
    //

    currentPtr = (PUCHAR)BiosInfo + biosInstallCheck->Length;
    lengthRemaining = BiosInfoLength - biosInstallCheck->Length;

    for (nodeIndex = 0; nodeIndex < numNodes; nodeIndex++) {

        devNodeHeader = (PCM_PNP_BIOS_DEVICE_NODE)currentPtr;

        if (devNodeHeader->Size > lengthRemaining) {

            IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                        "Node # %d, invalid size (%d), length remaining (%d)\n",
                        devNodeHeader->Node,
                        devNodeHeader->Size,
                        lengthRemaining) );

            break;
        }

        //
        // We use the Product ID field as the DeviceID key name.  So we insert
        // an initial asterisk so we don't have to copy and mangle it later.
        //
        devNodeInfo[nodeIndex].ProductId[0] = '*';

        PnPBiosExpandProductId((PUCHAR)&devNodeHeader->ProductId, &devNodeInfo[nodeIndex].ProductId[1]);

        devNodeInfo[nodeIndex].ProductId[9] = '\0';  // Extra NUL for REG_MULTI_SZ

        //
        // The handle is used as part of the Instance ID
        devNodeInfo[nodeIndex].Handle = devNodeHeader->Node;

        //
        // The type code and attributes aren't currently used but are copied
        // for completeness.
        //
        RtlCopyMemory( &devNodeInfo[nodeIndex].TypeCode,
                       devNodeHeader->DeviceType,
                       sizeof(devNodeInfo[nodeIndex].TypeCode) );

        devNodeInfo[nodeIndex].Attributes = devNodeHeader->DeviceAttributes;

        //
        // Replaces will eventually be set to the path of the Firmware
        // Enumerated devnode which duplicates this one (if a duplicate exists).
        //
        devNodeInfo[nodeIndex].Replaces = NULL;

        //
        // CompatibleIDs will be set to the list of compatible IDs.
        //
        devNodeInfo[nodeIndex].CompatibleIDs = NULL;

        //
        // Convert the allocated resources from ISA PnP resource descriptor
        // format to an IO_RESOURCE_REQUIREMENTS_LIST.
        //
        configPtr = currentPtr + sizeof(*devNodeHeader);
        remainingNodeLength = devNodeHeader->Size - sizeof(*devNodeHeader);

        devNodeInfo[nodeIndex].BootConfig = NULL;
        devNodeInfo[nodeIndex].FirmwareDisabled = FALSE;

        status = PpBiosResourcesToNtResources( 0,            /* BusNumber */
                                               0,            /* SlotNumber */
                                               &configPtr,   /* BiosData */
                                               convertFlags, /* ConvertFlags */
                                               &tempResReqList, /* ReturnedList */
                                               &configListLength);    /* ReturnedLength */

        remainingNodeLength = devNodeHeader->Size - (LONG)(configPtr - (PUCHAR)devNodeHeader);

        if (NT_SUCCESS( status )) {

            if (tempResReqList != NULL) {

                PpFilterNtResource (
                    devNodeInfo[nodeIndex].ProductId,
                    tempResReqList
                );

                //
                // Now we need to convert from a IO_RESOURCE_REQUIREMENTS_LIST to a
                // CM_RESOURCE_LIST.
                //
                status = PnPBiosIoResourceListToCmResourceList( tempResReqList,
                                                                &devNodeInfo[nodeIndex].BootConfig,
                                                                &devNodeInfo[nodeIndex].BootConfigLength);

                status = PnPBiosCheckForHardwareDisabled(tempResReqList,&devNodeInfo[nodeIndex].FirmwareDisabled);
                ExFreePool( tempResReqList );
            }

        } else {

            IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                        "Error converting allocated resources for devnode # %d, status = %8.8X\n",
                        devNodeInfo[nodeIndex].Handle,
                        status) );
        }

        //
        // Convert the supported resource configurations from ISA PnP resource
        // descriptor format to an IO_RESOURCE_REQUIREMENTS_LIST.
        //
        status = PpBiosResourcesToNtResources( 0,            /* BusNumber */
                                               0,            /* SlotNumber */
                                               &configPtr,   /* BiosData */
                                               convertFlags | PPCONVERTFLAG_SET_RESTART_LCPRI, /* ConvertFlags */
                                               &devNodeInfo[nodeIndex].BasicConfig, /* ReturnedList */
                                               &devNodeInfo[nodeIndex].BasicConfigLength );  /* ReturnedLength */

        remainingNodeLength = devNodeHeader->Size - (LONG)(configPtr - (PUCHAR)devNodeHeader);

        if (!NT_SUCCESS( status )) {

            devNodeInfo[nodeIndex].BasicConfig = NULL;

            IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                        "Error converting allowed resources for devnode # %d, status = %8.8X\n",
                        devNodeInfo[nodeIndex].Handle,
                        status) );
        } else {

            PpFilterNtResource (
                devNodeInfo[nodeIndex].ProductId,
                devNodeInfo[nodeIndex].BasicConfig
            );
        }

        //
        // Convert the list of compatible IDs if present
        //

        ASSERT(remainingNodeLength >= 0);

        status = PnPBiosExtractCompatibleIDs( &configPtr,       // BiosData
                                              (ULONG)remainingNodeLength,
                                              &devNodeInfo[nodeIndex].CompatibleIDs,
                                              &devNodeInfo[nodeIndex].CompatibleIDsLength );

        currentPtr += devNodeHeader->Size;
        lengthRemaining -= devNodeHeader->Size;

    }

    *DevNodeInfoList = devNodeInfo;
    *NumberNodes = numNodes;
    return STATUS_SUCCESS;
}

LONG
PnPBiosFindMatchingDevNode(
    IN PWCHAR MapperName,
    IN PCM_RESOURCE_LIST ResourceList,
    IN PBIOS_DEVNODE_INFO DevNodeInfoList,
    IN ULONG NumberNodes
    )
/*++

Routine Description:

    Given a list of resources this routine finds an entry in the
    DevNodeInfoList whose BootConfig resources match.  A match is defined as
    having at least overlapping I/O Ports or Memory Ranges.  If ResourceList doesn't
    include any I/O Ports or Memory Ranges then a match is defined as exactly
    the same interrupts and/or DMA channels.

    This routine is used to find PnP BIOS reported devices which match devices
    created by the Firmware Mapper.

Arguments:

    ResourceList - Pointer to CM_RESOURCE_LIST describing the resources
        currently used by the device for which a match is being searched.

    DevNodeInfoList - Array of BIOS_DEVNODE_INFO structures, one for each device
        reported by the BIOS.

    NumberNodes - Number of BIOS_DEVNODE_INFO elements pointed to by
        DevNodeInfoList.


Return Value:

    Index of the entry in DevNodeInfoList whose BootConfig matches the resources
    listed in ResourceList.  If no matching entry is found then -1 is returned.

--*/
{
    PCM_PARTIAL_RESOURCE_LIST       sourceList;
    PCM_PARTIAL_RESOURCE_LIST       targetList;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR sourceDescriptor;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR targetDescriptor;
    ULONG                           nodeIndex, sourceIndex, targetIndex;
    LONG                            firstMatch = -1;
    LONG                            bestMatch = -1;
    ULONG                           numResourcesMatch;
    ULONG                           score, possibleScore, bestScore = 0;
    PWCHAR                          idPtr;
    BOOLEAN                         idsMatch;
    BOOLEAN                         bestIdsMatch = FALSE;

#if DEBUG_DUP_MATCH
    CHAR                            sourceMapping[256];
    CHAR                            targetMapping[256];
#endif

    //
    // In order to simplify the problem we assume there is only one list.  This
    // assumption holds true in the BootConfig structures generated by the
    // current firmware mapper.
    //
    ASSERT( ResourceList->Count == 1 );

    sourceList = &ResourceList->List[0].PartialResourceList;

#if DEBUG_DUP_MATCH
    //
    // For debugging purposes we keep track of which resource entries map to
    // each other.  These relationships are stored in a fixed CHAR array, thus
    // the restriction on the number of descriptors.
    //
    ASSERT( sourceList->Count < 255 );
#endif

    //
    // Loop through each devnode and try and match it to the source resource
    // list.
    //
    for (nodeIndex = 0; nodeIndex < NumberNodes; nodeIndex++) {

        if (DevNodeInfoList[ nodeIndex ].BootConfig == NULL) {

            continue;
        }

        //
        // We found at least one potential match.  Let's double check if
        // the PNP ids also match.  We use a lack of ID match to disqualify
        // entries which don't match at least I/O ports or memory.
        //

        idPtr = DevNodeInfoList[ nodeIndex ].ProductId;

        if (RtlCompareMemory( idPtr, MapperName, 12 ) != 12) {

            idPtr = DevNodeInfoList[ nodeIndex ].CompatibleIDs;

            if (idPtr != NULL) {

                while (*idPtr != '\0') {

                    if (RtlCompareMemory( idPtr, MapperName, 12 ) == 12) {

                        break;
                    }

                    idPtr += 9;
                }

                if (*idPtr == '\0') {

                    idPtr = NULL;
                }
            }
        }

        idsMatch = (BOOLEAN)(idPtr != NULL);

        ASSERT( DevNodeInfoList[ nodeIndex ].BootConfig->Count == 1 );

        targetList = &DevNodeInfoList[ nodeIndex ].BootConfig->List[0].PartialResourceList;

#if DEBUG_DUP_MATCH
        RtlFillMemory( sourceMapping, sizeof(sourceMapping), -1 );
        RtlFillMemory( targetMapping, sizeof(targetMapping), -1 );
#endif

        numResourcesMatch = 0;
        possibleScore = 0;
        score = 0;

        //
        // Loop through each source descriptor (resource) and try and match it
        // to one of this devnode's descriptors.
        //

        for (sourceIndex = 0; sourceIndex < sourceList->Count; sourceIndex++) {

            sourceDescriptor = &sourceList->PartialDescriptors[sourceIndex];

            //
            // We are recalculating the possible score unnecessarily each time
            // we process a devnode.  We might save a small amount of time by
            // looping through the source descriptors once at the beginning but
            // its not clear it would make all that much difference given the
            // few devices reported by the BIOS.
            //

            switch (sourceDescriptor->Type) {

            case CmResourceTypePort:
                possibleScore += 0x1100;
                break;

            case CmResourceTypeInterrupt:
                possibleScore += 0x0001;
                break;

            case CmResourceTypeMemory:
                possibleScore += 0x1100;
                break;

            case CmResourceTypeDma:
                possibleScore += 0x0010;
                break;

            default:
                continue;
            }

            //
            // Try to find a resource in the target devnode which matches the
            // current source resource.
            //
            for (targetIndex = 0; targetIndex < targetList->Count; targetIndex++) {

                targetDescriptor = &targetList->PartialDescriptors[targetIndex];

                if (sourceDescriptor->Type == targetDescriptor->Type) {
                    switch (sourceDescriptor->Type) {
                    case CmResourceTypePort:
                        if ((sourceDescriptor->u.Port.Start.LowPart + sourceDescriptor->u.Port.Length) <=
                             targetDescriptor->u.Port.Start.LowPart ||
                            (targetDescriptor->u.Port.Start.LowPart + targetDescriptor->u.Port.Length) <=
                             sourceDescriptor->u.Port.Start.LowPart) {
                            continue;
                        }
                        if (sourceDescriptor->u.Port.Start.LowPart ==
                                targetDescriptor->u.Port.Start.LowPart &&
                            sourceDescriptor->u.Port.Length ==
                                targetDescriptor->u.Port.Length) {

                            score += 0x1100;

                        } else {

                            IopDbgPrint( (IOP_MAPPER_INFO_LEVEL,
                                        "Overlapping port resources, source = %4.4X-%4.4X, target = %4.4X-%4.4X\n",
                                        sourceDescriptor->u.Port.Start.LowPart,
                                        sourceDescriptor->u.Port.Start.LowPart + sourceDescriptor->u.Port.Length - 1,
                                        targetDescriptor->u.Port.Start.LowPart,
                                        targetDescriptor->u.Port.Start.LowPart + targetDescriptor->u.Port.Length - 1) );

                            score += 0x1000;

                        }
                        break;

                    case CmResourceTypeInterrupt:
                        if (sourceDescriptor->u.Interrupt.Level !=
                            targetDescriptor->u.Interrupt.Level) {
                            continue;
                        }
                        score += 0x0001;
                        break;

                    case CmResourceTypeMemory:
                        if ((sourceDescriptor->u.Memory.Start.LowPart + sourceDescriptor->u.Memory.Length) <=
                             targetDescriptor->u.Memory.Start.LowPart ||
                            (targetDescriptor->u.Memory.Start.LowPart + targetDescriptor->u.Memory.Length) <=
                             sourceDescriptor->u.Memory.Start.LowPart) {

                            continue;
                        }
                        if (sourceDescriptor->u.Memory.Start.LowPart ==
                                targetDescriptor->u.Memory.Start.LowPart &&
                            sourceDescriptor->u.Memory.Length ==
                                targetDescriptor->u.Memory.Length) {

                            score += 0x1100;

                        } else {

                            score += 0x1000;

                        }
                        break;

                    case CmResourceTypeDma:
                        if (sourceDescriptor->u.Dma.Channel !=
                            targetDescriptor->u.Dma.Channel) {

                            continue;
                        }
                        score += 0x0010;
                        break;

                    }
                    break;
                }
            }

            if (targetIndex < targetList->Count) {
#if DEBUG_DUP_MATCH
                sourceMapping[sourceIndex] = (CHAR)targetIndex;
                targetMapping[targetIndex] = (CHAR)sourceIndex;
#endif
                numResourcesMatch++;
            }
        }

        if (numResourcesMatch != 0) {
            if (firstMatch == -1) {
                firstMatch = nodeIndex;
            }

            if ((score > bestScore) || (score == bestScore && !bestIdsMatch && idsMatch))  {
                bestScore = score;
                bestMatch = nodeIndex;
                bestIdsMatch = idsMatch;
            }
        }
    }

    if (bestMatch != -1) {

        if (bestScore == possibleScore) {

            IopDbgPrint( (IOP_MAPPER_INFO_LEVEL,
                        "Perfect match, score = %4.4X, possible = %4.4X, index = %d\n",
                        bestScore,
                        possibleScore,
                        bestMatch) );

            if (possibleScore < 0x1000 && !bestIdsMatch) {

                bestMatch = -1;

            }

        } else if (possibleScore > 0x1000 && bestScore >= 0x1000) {

            IopDbgPrint( (IOP_MAPPER_INFO_LEVEL,
                        "Best match is close enough, score = %4.4X, possible = %4.4X, index = %d\n",
                        bestScore,
                        possibleScore,
                        bestMatch) );

        } else  {

            IopDbgPrint( (IOP_MAPPER_INFO_LEVEL,
                        "Best match is less than threshold, score = %4.4X, possible = %4.4X, index = %d\n",
                        bestScore,
                        possibleScore,
                        bestMatch) );

            bestMatch = -1;

        }
    }

    return bestMatch;
}

NTSTATUS
PnPBiosEliminateDupes(
    IN PBIOS_DEVNODE_INFO DevNodeInfoList,
    IN ULONG NumberNodes
    )
/*++

Routine Description:

    This routine enumerates the Firmware Mapper generated devices under
    Enum\Root.  Those that match entries in DevNodeInfoList have their registry
    key name stored in the DevNodeInfoList entry so that the Firmare Mapper
    instance may be removed later.

Arguments:

    DevNodeInfoList - Array of BIOS_DEVNODE_INFO structures, one for each device
        reported by the BIOS.

    NumberNodes - Number of BIOS_DEVNODE_INFO elements pointed to by
        DevNodeInfoList.

Return Value:

    STATUS_SUCCESS if no errors, otherwise the appropriate error.

--*/
{
    UNICODE_STRING                  enumRootKeyName, valueName;
    HANDLE                          enumRootKey;
    PKEY_BASIC_INFORMATION          deviceBasicInfo = NULL;
    ULONG                           deviceBasicInfoLength;
    UNICODE_STRING                  deviceKeyName;
    HANDLE                          deviceKey = NULL;
    PKEY_BASIC_INFORMATION          instanceBasicInfo = NULL;
    ULONG                           instanceBasicInfoLength;
    WCHAR                           logConfStr[DEFAULT_STRING_SIZE];
    UNICODE_STRING                  logConfKeyName;
    HANDLE                          logConfKey = NULL;

    PKEY_VALUE_PARTIAL_INFORMATION  valueInfo = NULL;
    ULONG                           valueInfoLength;
    ULONG                           returnedLength;

    ULONG                           deviceIndex, instanceIndex;
    NTSTATUS                        status = STATUS_UNSUCCESSFUL;

    PiWstrToUnicodeString(&enumRootKeyName, ENUMROOT_KEY_NAME);

    status = IopOpenRegistryKeyEx( &enumRootKey,
                                   NULL,
                                   &enumRootKeyName,
                                   KEY_READ
                                   );

    if (!NT_SUCCESS(status)) {

        IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                    "Could not open registry key %S, status = %8.8X\n",
                    ENUMROOT_KEY_NAME,
                    status) );

        return STATUS_UNSUCCESSFUL;
    }

    deviceBasicInfoLength = sizeof(KEY_BASIC_INFORMATION) + DEFAULT_STRING_SIZE;
    deviceBasicInfo = ExAllocatePool(PagedPool, deviceBasicInfoLength);

    instanceBasicInfoLength = sizeof(KEY_BASIC_INFORMATION) + DEFAULT_STRING_SIZE;
    instanceBasicInfo = ExAllocatePool(PagedPool, instanceBasicInfoLength);

    valueInfoLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + DEFAULT_STRING_SIZE;
    valueInfo = ExAllocatePool(PagedPool, valueInfoLength);

    if (deviceBasicInfo != NULL && instanceBasicInfo != NULL && valueInfo != NULL) {

        for (deviceIndex = 0; ; deviceIndex++) {

            status = ZwEnumerateKey( enumRootKey,
                                     deviceIndex,
                                     KeyBasicInformation,
                                     deviceBasicInfo,
                                     deviceBasicInfoLength,
                                     &returnedLength);

            if (!NT_SUCCESS(status)) {

                if (status == STATUS_BUFFER_TOO_SMALL ||
                    status == STATUS_BUFFER_OVERFLOW) {

                    continue;

                } else if (status != STATUS_NO_MORE_ENTRIES)  {

                    IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                                "Could not enumerate under key %S, status = %8.8X\n",
                                ENUMROOT_KEY_NAME,
                                status) );
                } else {
                    status = STATUS_SUCCESS;
                }
                break;
            }

            if (deviceBasicInfo->Name[0] != '*') {
                continue;
            }

            deviceBasicInfo->Name[ deviceBasicInfo->NameLength / 2 ] = L'\0';
            RtlInitUnicodeString(&deviceKeyName, deviceBasicInfo->Name);

            status = IopOpenRegistryKeyEx( &deviceKey,
                                           enumRootKey,
                                           &deviceKeyName,
                                           KEY_READ
                                           );

            if (!NT_SUCCESS(status)) {

                IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                            "Could not open registry key %S\\%S, status = %8.8X\n",
                            ENUMROOT_KEY_NAME,
                            deviceBasicInfo->Name,
                            status) );
                break;
            }

            for (instanceIndex = 0; ; instanceIndex++) {

                status = ZwEnumerateKey( deviceKey,
                                         instanceIndex,
                                         KeyBasicInformation,
                                         instanceBasicInfo,
                                         instanceBasicInfoLength,
                                         &returnedLength);

                if (!NT_SUCCESS(status)) {

                    if (status == STATUS_BUFFER_TOO_SMALL ||
                        status == STATUS_BUFFER_OVERFLOW) {

                        continue;

                    } else if (status != STATUS_NO_MORE_ENTRIES)  {
                        IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                                    "Could not enumerate under key %S\\%S, status = %8.8X\n",
                                    ENUMROOT_KEY_NAME,
                                    deviceBasicInfo->Name,
                                    status) );
                    } else {
                        status = STATUS_SUCCESS;
                    }
                    break;
                }

                if (RtlCompareMemory( instanceBasicInfo->Name,
                                      INSTANCE_ID_PREFIX,
                                      sizeof(INSTANCE_ID_PREFIX) - sizeof(UNICODE_NULL)
                                      ) == (sizeof(INSTANCE_ID_PREFIX) - sizeof(UNICODE_NULL))) {

                    continue;
                }

                instanceBasicInfo->Name[ instanceBasicInfo->NameLength / 2 ] = L'\0';

                RtlCopyMemory( logConfStr,
                               instanceBasicInfo->Name,
                               instanceBasicInfo->NameLength );

                logConfStr[ instanceBasicInfo->NameLength / 2 ] = L'\\';

                RtlCopyMemory( &logConfStr[ instanceBasicInfo->NameLength / 2 + 1 ],
                               REGSTR_KEY_LOGCONF,
                               sizeof(REGSTR_KEY_LOGCONF) );

                RtlInitUnicodeString( &logConfKeyName, logConfStr );

                status = IopOpenRegistryKeyEx( &logConfKey,
                                               deviceKey,
                                               &logConfKeyName,
                                               KEY_READ
                                               );

                if (!NT_SUCCESS(status)) {

                    IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                                "Could not open registry key %S\\%S\\%S, status = %8.8X\n",
                                ENUMROOT_KEY_NAME,
                                deviceBasicInfo->Name,
                                logConfStr,
                                status) );
                    continue;
                }

                PiWstrToUnicodeString( &valueName, REGSTR_VAL_BOOTCONFIG );

                status = ZwQueryValueKey( logConfKey,
                                          &valueName,
                                          KeyValuePartialInformation,
                                          valueInfo,
                                          valueInfoLength,
                                          &returnedLength );

                if (!NT_SUCCESS(status)) {

                    if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW) {

                        ExFreePool( valueInfo );

                        valueInfoLength = returnedLength;
                        valueInfo = ExAllocatePool( PagedPool, valueInfoLength );

                        if (valueInfo != NULL) {

                            status = ZwQueryValueKey( logConfKey,
                                                      &valueName,
                                                      KeyValuePartialInformation,
                                                      valueInfo,
                                                      valueInfoLength,
                                                      &returnedLength );
                        } else {
                            IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                                        "Error allocating memory for %S\\%S\\LogConf\\BootConfig value\n",
                                        ENUMROOT_KEY_NAME,
                                        deviceBasicInfo->Name) );
                            valueInfoLength = 0;
                            status = STATUS_NO_MEMORY;

                            break;
                        }

                    } else {
                        IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                                    "Error retrieving %S\\%S\\LogConf\\BootConfig size, status = %8.8X\n",
                                    ENUMROOT_KEY_NAME,
                                    deviceBasicInfo->Name,
                                    status) );

                        status = STATUS_UNSUCCESSFUL;
                    }
                }

                if (NT_SUCCESS( status )) {
                    PCM_RESOURCE_LIST   resourceList;
                    LONG                matchingIndex;

                    resourceList = (PCM_RESOURCE_LIST)valueInfo->Data;

                    matchingIndex = PnPBiosFindMatchingDevNode( deviceBasicInfo->Name,
                                                                resourceList,
                                                                DevNodeInfoList,
                                                                NumberNodes );

                    if (matchingIndex != -1) {

                        DevNodeInfoList[ matchingIndex ].Replaces = ExAllocatePool( PagedPool,
                                                                                    deviceBasicInfo->NameLength + instanceBasicInfo->NameLength + 2 * sizeof(UNICODE_NULL));

                        if (DevNodeInfoList[ matchingIndex ].Replaces != NULL) {

                            RtlCopyMemory( DevNodeInfoList[ matchingIndex ].Replaces,
                                           deviceBasicInfo->Name,
                                           deviceBasicInfo->NameLength );

                            DevNodeInfoList[ matchingIndex ].Replaces[ deviceBasicInfo->NameLength / 2 ] = '\\';

                            RtlCopyMemory( &DevNodeInfoList[ matchingIndex ].Replaces[ deviceBasicInfo->NameLength / 2 + 1 ],
                                           instanceBasicInfo->Name,
                                           instanceBasicInfo->NameLength );

                            DevNodeInfoList[ matchingIndex ].Replaces[ (deviceBasicInfo->NameLength + instanceBasicInfo->NameLength) / 2 + 1 ] = '\0';

                            IopDbgPrint( (IOP_MAPPER_INFO_LEVEL,
                                        "Match found: %S\\%S%d replaces %S\n",
                                        DevNodeInfoList[ matchingIndex ].ProductId,
                                        INSTANCE_ID_PREFIX,
                                        DevNodeInfoList[ matchingIndex ].Handle,
                                        DevNodeInfoList[ matchingIndex ].Replaces) );
                        } else {
                            IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                                        "Error allocating memory for %S\\%S%d\\Replaces\n",
                                        DevNodeInfoList[ matchingIndex ].ProductId,
                                        INSTANCE_ID_PREFIX,
                                        DevNodeInfoList[ matchingIndex ].Handle) );
                        }
                    } else {
                        IopDbgPrint( (IOP_MAPPER_INFO_LEVEL,
                                    "No matching PnP Bios DevNode found for FW Enumerated device %S\\%S\n",
                                    deviceBasicInfo->Name,
                                    instanceBasicInfo->Name) );
                    }
                } else {
                    IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                                "Error retrieving %S\\%S\\%S\\BootConfig, status = %8.8X\n",
                                ENUMROOT_KEY_NAME,
                                deviceBasicInfo->Name,
                                logConfStr,
                                status) );
                }

                ZwClose(logConfKey);

                logConfKey = NULL;
            }

            ZwClose(deviceKey);

            deviceKey = NULL;
        }
    } else {
        status = STATUS_NO_MEMORY;
    }

    if (valueInfo != NULL) {
        ExFreePool(valueInfo);
    }

    if (instanceBasicInfo != NULL) {
        ExFreePool(instanceBasicInfo);
    }

    if (deviceBasicInfo != NULL) {
        ExFreePool(deviceBasicInfo);
    }

    if (logConfKey != NULL) {
        ZwClose(logConfKey);
    }

    if (deviceKey != NULL) {
        ZwClose(deviceKey);
    }

    ZwClose(enumRootKey);

    return status;
}

PWCHAR
PnPBiosGetDescription(
    IN PBIOS_DEVNODE_INFO DevNodeInfoEntry
    )
{
    ULONG       class, subClass;
    LONG        index;
    CLASSDATA   const*classDescriptions;
    LONG        descriptionCount;

    class = DevNodeInfoEntry->TypeCode[0];
    subClass = (DevNodeInfoEntry->TypeCode[1] << 8) | DevNodeInfoEntry->TypeCode[2];

    if (class > 0 && class < CLASSLIST_COUNT) {

        classDescriptions = ClassDescriptionsList[ class ].Descriptions;
        descriptionCount = ClassDescriptionsList[ class ].Count;

        //
        // The last description entry is the default so there is no use
        // comparing it, if we get that far just use it.
        //
        for (index = 0; index < (descriptionCount - 1); index++) {

            if (subClass == classDescriptions[ index ].Value)  {

                break;
            }
        }

        return classDescriptions[ index ].Description;
    }

    return DEFAULT_DEVICE_DESCRIPTION;
}

NTSTATUS
PnPBiosCopyDeviceParamKey(
    IN HANDLE EnumRootKey,
    IN PWCHAR SourcePath,
    IN PWCHAR DestinationPath
    )
/*++

Routine Description:

    Copy the Device Parameters key from the firmware mapper node in
    DevNodeInfo->Replaces to the BIOS mapper node represented by DevNodeInfo.

Arguments:

    EnumRootKey - Handle to Enum\Root.

    SourcePath - Instance path of FW Mapper node relative to Enum\Root.

    DestinationKey - Handle to destination instance key.

Return Value:

    STATUS_SUCCESS if no errors, otherwise the appropriate error.

--*/
{
    NTSTATUS                    status;
    UNICODE_STRING              sourceInstanceKeyName;
    HANDLE                      sourceInstanceKey = NULL;

    UNICODE_STRING              deviceParamKeyName;
    HANDLE                      sourceDeviceParamKey = NULL;
    HANDLE                      destinationDeviceParamKey = NULL;
    UNICODE_STRING              destinationInstanceKeyName;

    PKEY_VALUE_FULL_INFORMATION valueFullInfo = NULL;
    ULONG                       valueFullInfoLength;
    ULONG                       resultLength;

    UNICODE_STRING              valueName;

    ULONG                       index;

    RtlInitUnicodeString( &sourceInstanceKeyName, SourcePath );

    status = IopOpenRegistryKeyEx( &sourceInstanceKey,
                                   EnumRootKey,
                                   &sourceInstanceKeyName,
                                   KEY_ALL_ACCESS
                                   );

    if (!NT_SUCCESS(status)) {

        IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                    "PnPBiosCopyDeviceParamKey() - Could not open source instance key %S, status = %8.8X\n",
                    SourcePath,
                    status) );

        return status;
    }

    PiWstrToUnicodeString(&deviceParamKeyName, REGSTR_KEY_DEVICEPARAMETERS);

    status = IopOpenRegistryKeyEx( &sourceDeviceParamKey,
                                   sourceInstanceKey,
                                   &deviceParamKeyName,
                                   KEY_ALL_ACCESS
                                   );

    if (!NT_SUCCESS(status)) {

        if (status != STATUS_OBJECT_NAME_NOT_FOUND) {

            IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                        "PnPBiosCopyDeviceParamKey() - Could not open source device parameter key %S\\%S, status = %8.8X\n",
                        SourcePath,
                        deviceParamKeyName.Buffer,
                        status) );
        }

        goto Cleanup;
    }

    RtlInitUnicodeString(&destinationInstanceKeyName, DestinationPath);

    status = IopOpenDeviceParametersSubkey( &destinationDeviceParamKey,
                                            EnumRootKey,
                                            &destinationInstanceKeyName,
                                            KEY_ALL_ACCESS );

    if (!NT_SUCCESS(status)) {

        IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                    "PnPBiosCopyDeviceParamKey() - Could not open destination device parameter key %S\\%S, status = %8.8X\n",
                    DestinationPath,
                    REGSTR_KEY_DEVICEPARAMETERS,
                    status) );

        goto Cleanup;
    }

    valueFullInfoLength = sizeof(KEY_VALUE_FULL_INFORMATION) + DEFAULT_STRING_SIZE + DEFAULT_VALUE_SIZE;
    valueFullInfo = ExAllocatePool(PagedPool, valueFullInfoLength);

    if (valueFullInfo == NULL) {

        goto Cleanup;
    }

    for (index = 0; ; index++) {
        status = ZwEnumerateValueKey( sourceDeviceParamKey,
                                      index,
                                      KeyValueFullInformation,
                                      valueFullInfo,
                                      valueFullInfoLength,
                                      &resultLength );

        if (NT_SUCCESS(status)) {
            UNICODE_STRING  sourcePathString;
            UNICODE_STRING  serialPrefixString;
            UNICODE_STRING  portNameString;

            valueName.Length = (USHORT)valueFullInfo->NameLength;
            valueName.MaximumLength = valueName.Length;
            valueName.Buffer = valueFullInfo->Name;

            RtlInitUnicodeString(&sourcePathString, SourcePath);
            PiWstrToUnicodeString(&serialPrefixString, L"*PNP0501");

            if (sourcePathString.Length > serialPrefixString.Length) {
                sourcePathString.Length = serialPrefixString.Length;
            }

            if (RtlCompareUnicodeString(&sourcePathString, &serialPrefixString, TRUE) == 0) {

                PiWstrToUnicodeString(&portNameString, L"DosDeviceName");

                if (valueName.Length == 16 &&
                    RtlCompareUnicodeString(&valueName, &portNameString, TRUE) == 0)  {

                    // ComPortDBRemove(SourcePath, &unicodeValue);
                    ComPortDBAdd(destinationDeviceParamKey, (PWSTR)((PUCHAR)valueFullInfo + valueFullInfo->DataOffset));
                    continue;
                }
            }

            status = ZwSetValueKey( destinationDeviceParamKey,
                                    &valueName,
                                    valueFullInfo->TitleIndex,
                                    valueFullInfo->Type,
                                    (PUCHAR)valueFullInfo + valueFullInfo->DataOffset,
                                    valueFullInfo->DataLength );
        } else {
            if (status == STATUS_BUFFER_OVERFLOW) {
                ExFreePool( valueFullInfo );

                valueFullInfoLength = resultLength;
                valueFullInfo = ExAllocatePool(PagedPool, valueFullInfoLength);

                if (valueFullInfo == NULL) {
                    status = STATUS_NO_MEMORY;
                } else {
                    index--;
                    continue;
                }
            } else if (status != STATUS_NO_MORE_ENTRIES)  {
                IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                            "Could not enumerate under key %S\\%S, status = %8.8X\n",
                            SourcePath,
                            deviceParamKeyName.Buffer,
                            status) );
            } else {
                status = STATUS_SUCCESS;
            }

            break;
        }
    }

Cleanup:
    if (sourceInstanceKey != NULL) {
        ZwClose( sourceInstanceKey );
    }

    if (sourceDeviceParamKey != NULL) {
        ZwClose( sourceDeviceParamKey );
    }

    if (destinationDeviceParamKey != NULL) {
        ZwClose( destinationDeviceParamKey );
    }

    if (valueFullInfo != NULL) {
        ExFreePool( valueFullInfo );
    }

    return status;
}

NTSTATUS
PnPBiosWriteInfo(
    IN PBIOS_DEVNODE_INFO DevNodeInfoList,
    IN ULONG NumberNodes
    )
/*++

Routine Description:

    Creates an entry under Enum\Root for each DevNodeInfoList element.  Also
    removes any duplicate entries which were created by the Firmware Mapper.

    Note: Currently entries for the Keyboard, Mouse, and PCI bus are ignored.

Arguments:

    DevNodeInfoList - Array of BIOS_DEVNODE_INFO structures, one for each device
        reported by the BIOS.

    NumberNodes - Number of BIOS_DEVNODE_INFO elements pointed to by
        DevNodeInfoList.

Return Value:

    STATUS_SUCCESS if no errors, otherwise the appropriate error.

--*/
{
    PKEY_VALUE_FULL_INFORMATION     excludeList=NULL;

    UNICODE_STRING                  enumRootKeyName;
    HANDLE                          enumRootKey;
    WCHAR                           instanceNameStr[DEFAULT_STRING_SIZE];
    UNICODE_STRING                  instanceKeyName;
    HANDLE                          instanceKey;
    UNICODE_STRING                  controlKeyName;
    HANDLE                          controlKey;
    UNICODE_STRING                  logConfKeyName;
    HANDLE                          logConfKey;

    UNICODE_STRING                  valueName;
    ULONG                           dwordValue;
    ULONG                           disposition;

    PWCHAR                          descriptionStr;
    ULONG                           descriptionStrLength;

    ULONG                           nodeIndex;
    NTSTATUS                        status;

    BOOLEAN                         isNewDevice;
    PCHAR                           ids;

    PiWstrToUnicodeString(&enumRootKeyName, ENUMROOT_KEY_NAME);

    status = IopOpenRegistryKeyEx( &enumRootKey,
                                   NULL,
                                   &enumRootKeyName,
                                   KEY_ALL_ACCESS
                                   );

    if (!NT_SUCCESS(status)) {

        IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                    "Could not open registry key %S, status = %8.8X\n",
                    ENUMROOT_KEY_NAME,
                    status) );

        return STATUS_UNSUCCESSFUL;

    }

    //
    // Reasons why a node might be excluded (i.e not enumerated)
    // * included in ExcludedDevices array (non-conditional)
    // * included in CCS\Control\BiosInfo\PnpBios\DisableNodes via biosinfo.inf
    // * resources are disabled and device is included in the
    //   ExcludeIfDisabled array

    excludeList = PnPGetBiosInfoValue(DISABLENODES_VALUE_NAME);

    for (nodeIndex = 0; nodeIndex < NumberNodes; nodeIndex++) {

        //
        // Check if this node is in the 'ignore on this machine' list.
        //

        if ( excludeList &&
             PnPBiosIgnoreNode( &DevNodeInfoList[ nodeIndex ].ProductId[1],
                                (PWCHAR)((PUCHAR)excludeList+excludeList->DataOffset))) {
            continue;
        }

        // Checking for nodes we always exclude
        if ( PnPBiosCheckForExclusion( ExcludedDevices,
                                       EXCLUDED_DEVICES_COUNT,
                                       DevNodeInfoList[ nodeIndex ].ProductId,
                                       DevNodeInfoList[ nodeIndex ].CompatibleIDs)) {
            //
            // If we are skipping the device, we need to first copy the decode
            // info that the BIOS supplied to the ntdetected device's Boot
            // Config which was generated by the FW mapper.
            //
            PnPBiosCopyIoDecode( enumRootKey, &DevNodeInfoList[ nodeIndex ] );

            //
            // Skip excluded devices, ie busses, mice and keyboards for now.
            //

            continue;
        }

        // Checking for nodes we exclude if disabled
        if ( DevNodeInfoList[ nodeIndex ].FirmwareDisabled &&
             PnPBiosCheckForExclusion( ExcludeIfDisabled,
                                       EXCLUDE_DISABLED_COUNT,
                                       DevNodeInfoList[ nodeIndex ].ProductId,
                                       NULL)) {
            continue;
        }

        swprintf( instanceNameStr,
                  L"%s\\%s%d",
                  DevNodeInfoList[ nodeIndex ].ProductId,
                  INSTANCE_ID_PREFIX,
                  DevNodeInfoList[ nodeIndex ].Handle );

        RtlInitUnicodeString( &instanceKeyName, instanceNameStr );

        status = IopCreateRegistryKeyEx( &instanceKey,
                                         enumRootKey,
                                         &instanceKeyName,
                                         KEY_ALL_ACCESS,
                                         REG_OPTION_NON_VOLATILE,
                                         &disposition
                                         );

        if (NT_SUCCESS(status))  {

            //
            // If the key already exists because it was explicitly migrated
            // during textmode setup, we should still consider it a "new key".
            //
            if (disposition != REG_CREATED_NEW_KEY) {
                PKEY_VALUE_FULL_INFORMATION keyValueInformation;
                UNICODE_STRING unicodeString;

                status = IopGetRegistryValue(instanceKey,
                                             REGSTR_VALUE_MIGRATED,
                                             &keyValueInformation);
                if (NT_SUCCESS(status)) {

                    if ((keyValueInformation->Type == REG_DWORD) &&
                        (keyValueInformation->DataLength == sizeof(ULONG)) &&
                        ((*(PULONG)KEY_VALUE_DATA(keyValueInformation)) != 0)) {
                        disposition = REG_CREATED_NEW_KEY;
                    }

                    ExFreePool(keyValueInformation);

                    PiWstrToUnicodeString(&unicodeString, REGSTR_VALUE_MIGRATED);
                    ZwDeleteValueKey(instanceKey, &unicodeString);
                }
            }

            isNewDevice = (BOOLEAN)(disposition == REG_CREATED_NEW_KEY);

            if (isNewDevice) {

                PiWstrToUnicodeString( &valueName, L"DeviceDesc" );

                descriptionStr = PnPBiosGetDescription( &DevNodeInfoList[ nodeIndex ] );
                descriptionStrLength = (ULONG)(wcslen(descriptionStr) * 2 + sizeof(UNICODE_NULL));

                status = ZwSetValueKey( instanceKey,
                                        &valueName,
                                        0,
                                        REG_SZ,
                                        descriptionStr,
                                        descriptionStrLength );
            }

            PiWstrToUnicodeString( &valueName, REGSTR_VAL_FIRMWAREIDENTIFIED );
            dwordValue = 1;

            status = ZwSetValueKey( instanceKey,
                                    &valueName,
                                    0,
                                    REG_DWORD,
                                    &dwordValue,
                                    sizeof(dwordValue) );

            if (isNewDevice)  {

                PiWstrToUnicodeString( &valueName, REGSTR_VALUE_HARDWAREID);
                status = ZwSetValueKey( instanceKey,
                                        &valueName,
                                        0,
                                        REG_MULTI_SZ,
                                        DevNodeInfoList[ nodeIndex ].ProductId,
                                        sizeof(DevNodeInfoList[nodeIndex].ProductId));

                if (DevNodeInfoList[ nodeIndex ].CompatibleIDs != NULL) {

                    PiWstrToUnicodeString( &valueName, REGSTR_VALUE_COMPATIBLEIDS);
                    status = ZwSetValueKey( instanceKey,
                                            &valueName,
                                            0,
                                            REG_MULTI_SZ,
                                            DevNodeInfoList[ nodeIndex ].CompatibleIDs,
                                            DevNodeInfoList[ nodeIndex ].CompatibleIDsLength);
                }
            }

            PiWstrToUnicodeString( &valueName, L"Replaces" );

            if (DevNodeInfoList[ nodeIndex ].Replaces != NULL) {

                status = ZwSetValueKey( instanceKey,
                                        &valueName,
                                        0,
                                        REG_SZ,
                                        DevNodeInfoList[ nodeIndex ].Replaces,
                                        (ULONG)(wcslen(DevNodeInfoList[ nodeIndex ].Replaces) * 2 + sizeof(UNICODE_NULL)) );

            } else if (!isNewDevice) {

                status = ZwDeleteValueKey( instanceKey,
                                           &valueName );
            }

            PiWstrToUnicodeString( &controlKeyName, REGSTR_KEY_DEVICECONTROL );

            status = IopCreateRegistryKeyEx( &controlKey,
                                             instanceKey,
                                             &controlKeyName,
                                             KEY_ALL_ACCESS,
                                             REG_OPTION_VOLATILE,
                                             NULL
                                             );

            if (NT_SUCCESS(status))  {

                PiWstrToUnicodeString( &valueName, REGSTR_VAL_FIRMWAREMEMBER );
                dwordValue = 1;

                status = ZwSetValueKey( controlKey,
                                        &valueName,
                                        0,
                                        REG_DWORD,
                                        &dwordValue,
                                        sizeof(dwordValue) );

                PiWstrToUnicodeString( &valueName, L"PnpBiosDeviceHandle" );
                dwordValue = DevNodeInfoList[ nodeIndex ].Handle;

                status = ZwSetValueKey( controlKey,
                                        &valueName,
                                        0,
                                        REG_DWORD,
                                        &dwordValue,
                                        sizeof(dwordValue) );

                PiWstrToUnicodeString( &valueName, REGSTR_VAL_FIRMWAREDISABLED );
                dwordValue = DevNodeInfoList[ nodeIndex ].FirmwareDisabled;

                status = ZwSetValueKey( controlKey,
                                        &valueName,
                                        0,
                                        REG_DWORD,
                                        &dwordValue,
                                        sizeof(dwordValue) );

                PiWstrToUnicodeString( &valueName, L"PnpBiosDeviceHandle" );
                dwordValue = DevNodeInfoList[ nodeIndex ].Handle;

                ZwClose( controlKey );

            } else {

                IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                            "Could not open registry key %S\\%S\\%S\\Control, status = %8.8X\n",
                            ENUMROOT_KEY_NAME,
                            DevNodeInfoList[ nodeIndex ].ProductId,
                            instanceNameStr,
                            status) );

                ZwClose( instanceKey );
                status = STATUS_UNSUCCESSFUL;

                goto Cleanup;
            }

            PiWstrToUnicodeString( &logConfKeyName, REGSTR_KEY_LOGCONF );

            status = IopCreateRegistryKeyEx( &logConfKey,
                                           instanceKey,
                                           &logConfKeyName,
                                           KEY_ALL_ACCESS,
                                           REG_OPTION_NON_VOLATILE,
                                           NULL
                                           );

            if (NT_SUCCESS(status))  {

                if (DevNodeInfoList[ nodeIndex ].BootConfig != NULL) {

                    PiWstrToUnicodeString(&valueName, REGSTR_VAL_BOOTCONFIG);
                    status = ZwSetValueKey( logConfKey,
                                            &valueName,
                                            0,
                                            REG_RESOURCE_LIST,
                                            DevNodeInfoList[ nodeIndex ].BootConfig,
                                            DevNodeInfoList[ nodeIndex ].BootConfigLength );
                }

                if (DevNodeInfoList[ nodeIndex ].BasicConfig != NULL) {

                    PiWstrToUnicodeString( &valueName, REGSTR_VAL_BASICCONFIGVECTOR );

                    status = ZwSetValueKey( logConfKey,
                                            &valueName,
                                            0,
                                            REG_RESOURCE_REQUIREMENTS_LIST,
                                            DevNodeInfoList[ nodeIndex ].BasicConfig,
                                            DevNodeInfoList[ nodeIndex ].BasicConfigLength );

                }

                ZwClose( logConfKey );

            } else {

                IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                            "Could not open registry key %S\\%S\\%S\\LogConf, status = %8.8X\n",
                            ENUMROOT_KEY_NAME,
                            DevNodeInfoList[ nodeIndex ].ProductId,
                            instanceNameStr,
                            status) );

                ZwClose( instanceKey );
                status = STATUS_UNSUCCESSFUL;

                goto Cleanup;
            }

            //
            // If we are replacing a FW Mapper devnode we need to copy the
            // Device Parameters subkey.
            //
            if (isNewDevice && DevNodeInfoList[ nodeIndex ].Replaces != NULL) {

                status = PnPBiosCopyDeviceParamKey( enumRootKey,
                                                    DevNodeInfoList[ nodeIndex ].Replaces,
                                                    instanceNameStr );
            }

            ZwClose( instanceKey );

        } else {

            IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                        "Could not open registry key %S\\%S\\%S, status = %8.8X\n",
                        ENUMROOT_KEY_NAME,
                        DevNodeInfoList[ nodeIndex ].ProductId,
                        instanceNameStr,
                        status) );

            ZwClose( instanceKey );
            status = STATUS_UNSUCCESSFUL;

            goto Cleanup;
        }

        //
        // Now check if the entry just written duplicates one written by the
        // Firmware Mapper.  If it does then remove the Firmware Mapper entry.
        //

        if (DevNodeInfoList[ nodeIndex ].Replaces != NULL) {

            IopDeleteKeyRecursive( enumRootKey, DevNodeInfoList[ nodeIndex ].Replaces );

        }
    }

    status = STATUS_SUCCESS;

 Cleanup:
    ZwClose( enumRootKey );

    if (excludeList) {
        ExFreePool (excludeList);
    }

    return status;
}
VOID
PnPBiosCopyIoDecode(
    IN HANDLE EnumRootKey,
    IN PBIOS_DEVNODE_INFO DevNodeInfo
    )
{
    WCHAR                           logConfKeyNameStr[DEFAULT_STRING_SIZE];
    UNICODE_STRING                  logConfKeyName;
    HANDLE                          logConfKey;
    UNICODE_STRING                  valueName;
    PKEY_VALUE_PARTIAL_INFORMATION  valueInfo = NULL;
    ULONG                           valueInfoLength;
    ULONG                           returnedLength;
    NTSTATUS                        status;
    PCM_PARTIAL_RESOURCE_LIST       partialResourceList;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor;
    ULONG                           index;
    USHORT                          flags;

    if (DevNodeInfo->Replaces == NULL || DevNodeInfo->BootConfig == NULL) {

        //
        // If we didn't find a FW Mapper created devnode then there is nothing
        // to do.
        //
        return;
    }

    //
    // Search through the Boot Config and see if the device's I/O ports are
    // 16 bit decode.
    //

    ASSERT(DevNodeInfo->BootConfig->Count == 1);

    partialResourceList = &DevNodeInfo->BootConfig->List[0].PartialResourceList;

    partialDescriptor = &partialResourceList->PartialDescriptors[0];

    flags = (USHORT)~0;

#define DECODE_FLAGS ( CM_RESOURCE_PORT_10_BIT_DECODE | \
                       CM_RESOURCE_PORT_12_BIT_DECODE | \
                       CM_RESOURCE_PORT_16_BIT_DECODE | \
                       CM_RESOURCE_PORT_POSITIVE_DECODE )

    for ( index = 0; index < partialResourceList->Count; index++ ) {
        if (partialDescriptor->Type == CmResourceTypePort) {
            if (flags == (USHORT)~0) {
                flags = partialDescriptor->Flags & DECODE_FLAGS;
            } else {
                ASSERT(flags == (partialDescriptor->Flags & DECODE_FLAGS));
            }
        }
        partialDescriptor++;
    }

    if (!(flags & (CM_RESOURCE_PORT_16_BIT_DECODE | CM_RESOURCE_PORT_POSITIVE_DECODE)))  {
        return;
    }

    swprintf( logConfKeyNameStr,
              L"%s\\%s",
              DevNodeInfo->Replaces,
              REGSTR_KEY_LOGCONF
              );

    RtlInitUnicodeString( &logConfKeyName, logConfKeyNameStr );

    status = IopCreateRegistryKeyEx( &logConfKey,
                                     EnumRootKey,
                                     &logConfKeyName,
                                     KEY_ALL_ACCESS,
                                     REG_OPTION_NON_VOLATILE,
                                     NULL
                                     );

    if (!NT_SUCCESS(status)) {

        IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                    "Could not open registry key %S\\%S\\%S, status = %8.8X\n",
                    ENUMROOT_KEY_NAME,
                    DevNodeInfo->Replaces,
                    REGSTR_KEY_LOGCONF,
                    status) );

        return;
    }

    valueInfoLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + DEFAULT_STRING_SIZE;
    valueInfo = ExAllocatePool(PagedPool, valueInfoLength);

    if (valueInfo == NULL)  {

        ZwClose( logConfKey );

        return;
    }

    PiWstrToUnicodeString( &valueName, REGSTR_VAL_BOOTCONFIG );

    status = ZwQueryValueKey( logConfKey,
                              &valueName,
                              KeyValuePartialInformation,
                              valueInfo,
                              valueInfoLength,
                              &returnedLength);

    if (!NT_SUCCESS(status)) {

        if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW) {

            //
            // The default buffer was too small, free it and reallocate
            // it to the required size.
            //
            ExFreePool( valueInfo );

            valueInfoLength = returnedLength;
            valueInfo = ExAllocatePool( PagedPool, valueInfoLength );

            if (valueInfo != NULL)  {

                status = ZwQueryValueKey( logConfKey,
                                          &valueName,
                                          KeyValuePartialInformation,
                                          valueInfo,
                                          valueInfoLength,
                                          &returnedLength );

                if (!NT_SUCCESS(status)) {
                    IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                                "Could not query registry value %S\\%S\\LogConf\\BootConfig, status = %8.8X\n",
                                ENUMROOT_KEY_NAME,
                                DevNodeInfo->Replaces,
                                status) );

                    ExFreePool( valueInfo );

                    ZwClose( logConfKey );

                    return;
                }
            } else {

                IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                            "Could not allocate memory for BootConfig value\n"
                            ) );

                ZwClose( logConfKey );

                return;
            }
        }
    }

    partialResourceList = &((PCM_RESOURCE_LIST)valueInfo->Data)->List[0].PartialResourceList;

    partialDescriptor = &partialResourceList->PartialDescriptors[0];

    for ( index = 0; index < partialResourceList->Count; index++ ) {
        if (partialDescriptor->Type == CmResourceTypePort) {
            partialDescriptor->Flags &= ~DECODE_FLAGS;
            partialDescriptor->Flags |= flags;
        }
        partialDescriptor++;
    }

    status = ZwSetValueKey( logConfKey,
                            &valueName,
                            0,
                            REG_RESOURCE_LIST,
                            valueInfo->Data,
                            valueInfo->DataLength );

    if (!NT_SUCCESS(status)) {
        IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                    "Could not set registry value %S\\%S\\LogConf\\BootConfig, status = %8.8X\n",
                    ENUMROOT_KEY_NAME,
                    DevNodeInfo->Replaces,
                    status) );
    }

    ExFreePool(valueInfo);

    ZwClose(logConfKey);
}

NTSTATUS
PnPBiosCheckForHardwareDisabled(
    IN PIO_RESOURCE_REQUIREMENTS_LIST IoResourceList,
    IN OUT PBOOLEAN Disabled
    )
/*++

Routine Description:

    If this device has been assigned one or more resources, and each resource has a length of zero, then it is
    hardware disabled.

Arguments:

    IoResourceList - Resource obtained from BIOS that we're about to map to a CmResourceList

    Disabled - Set to TRUE if the device is deemed to be disabled

Return Value:

    STATUS_SUCCESS if no errors, otherwise the appropriate error.

--*/
{
    BOOLEAN ParsedResource = FALSE;
    PIO_RESOURCE_DESCRIPTOR ioDescriptor;
    ULONG descIndex;
    //
    // Since this routine is only used to translate the allocated resources
    // returned by the PnP BIOS, we can assume that there is only 1 alternative
    // list
    //

    ASSERT(IoResourceList->AlternativeLists == 1);
    ASSERT(Disabled != NULL);

    *Disabled = FALSE;

    //
    // Translate each resource descriptor, currently we only handle ports,
    // memory, interrupts, and dma.  The current implementation of the routine
    // which converts from ISA PnP Resource data to IO_RESOURCE_REQUIREMENTS
    // won't generate any other descriptor types given the data returned from
    // the BIOS.
    //

    for (descIndex = 0; descIndex < IoResourceList->List[ 0 ].Count; descIndex++) {

        ioDescriptor = &IoResourceList->List[ 0 ].Descriptors[ descIndex ];

        switch (ioDescriptor->Type) {

        case CmResourceTypePort:
            if (ioDescriptor->u.Port.Length) {
                return STATUS_SUCCESS;
            }
            ParsedResource = TRUE;
            break;

        case CmResourceTypeInterrupt:
            if (ioDescriptor->u.Interrupt.MinimumVector != (ULONG)(-1)) {
                return STATUS_SUCCESS;
            }
            ParsedResource = TRUE;
            break;

        case CmResourceTypeMemory:
            if (ioDescriptor->u.Memory.Length) {
                return STATUS_SUCCESS;
            }
            ParsedResource = TRUE;
            break;

        case CmResourceTypeDma:
            if (ioDescriptor->u.Dma.MinimumChannel != (ULONG)(-1)) {
                return STATUS_SUCCESS;
            }
            ParsedResource = TRUE;
            break;

        default:
            IopDbgPrint( (IOP_MAPPER_ERROR_LEVEL,
                        "Unexpected ResourceType (%d) in I/O Descriptor\n",
                        ioDescriptor->Type) );

#if DBG
            // DbgBreakPoint();
#endif
            break;
        }
    }

    if (ParsedResource) {
        //
        // at least one empty resource, no non-empty resources
        //
        *Disabled = TRUE;
    }

    return STATUS_SUCCESS;

}


NTSTATUS
PnPBiosFreeDevNodeInfo(
    IN PBIOS_DEVNODE_INFO DevNodeInfoList,
    IN ULONG NumberNodes
    )
/*++

Routine Description:

    Free the dynamically allocated DevNodeInfoList as well as any dynamically
    allocated dependent structures.

Arguments:

    DevNodeInfoList - Array of BIOS_DEVNODE_INFO structures, one for each device
        reported by the BIOS.

    NumberNodes - Number of BIOS_DEVNODE_INFO elements pointed to by
        DevNodeInfoList.

Return Value:

    STATUS_SUCCESS if no errors, otherwise the appropriate error.

--*/
{
    ULONG   nodeIndex;

    for (nodeIndex = 0; nodeIndex < NumberNodes; nodeIndex++) {

        if (DevNodeInfoList[nodeIndex].Replaces != NULL) {
            ExFreePool( DevNodeInfoList[nodeIndex].Replaces );
        }

        if (DevNodeInfoList[nodeIndex].CompatibleIDs != NULL) {
            ExFreePool( DevNodeInfoList[nodeIndex].CompatibleIDs );
        }

        if (DevNodeInfoList[nodeIndex].BootConfig != NULL) {
            ExFreePool( DevNodeInfoList[nodeIndex].BootConfig );
        }

        if (DevNodeInfoList[nodeIndex].BasicConfig != NULL) {
            ExFreePool( DevNodeInfoList[nodeIndex].BasicConfig );
        }
    }

    ExFreePool( DevNodeInfoList );

    return STATUS_SUCCESS;
}

NTSTATUS
PnPBiosMapper()
/*++

Routine Description:

    Map the information provided from the PnP BIOS and stored in the registry by
    NTDETECT into root enumerated devices.

Arguments:

    NONE

Return Value:

    STATUS_SUCCESS if no errors, otherwise the appropriate error.

--*/
{
    PCM_RESOURCE_LIST   biosInfo;
    ULONG               length;
    NTSTATUS            status;
    PBIOS_DEVNODE_INFO  devNodeInfoList;
    ULONG               numberNodes;

    status = PnPBiosGetBiosInfo( &biosInfo, &length );

    if (!NT_SUCCESS( status )) {

        return status;
    }

    status = PnPBiosTranslateInfo( biosInfo,
                                   length,
                                   &devNodeInfoList,
                                   &numberNodes );

    ExFreePool( biosInfo );

    if (!NT_SUCCESS( status )) {

        return status;
    }

    status = PnPBiosEliminateDupes( devNodeInfoList, numberNodes );

    if (NT_SUCCESS( status )) {

        status = PnPBiosWriteInfo( devNodeInfoList, numberNodes );

    }

    PnPBiosFreeDevNodeInfo( devNodeInfoList, numberNodes );

    return status;
}

VOID
PpFilterNtResource (
    IN PWCHAR PnpDeviceName,
    PIO_RESOURCE_REQUIREMENTS_LIST ResReqList
)
{
    PIO_RESOURCE_LIST ioResourceList;
    PIO_RESOURCE_DESCRIPTOR ioResourceDescriptors;

    if (ResReqList == NULL) {
        return;
    }

#if 0 //_X86_
    if (KeI386MachineType == MACHINE_TYPE_EISA) {

        PCM_FULL_RESOURCE_DESCRIPTOR    fullDescriptor;
        PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor;
        PUCHAR                          nextDescriptor;
        ULONG                           j;
        ULONG                           lastResourceIndex;

        fullDescriptor = &ResourceList->List[0];

        for (i = 0; i < ResourceList->Count; i++) {

            partialResourceList = &fullDescriptor->PartialResourceList;

            for (j = 0; j < partialResourceList->Count; j++) {
                partialDescriptor = &partialResourceList->PartialDescriptors[j];

                if (partialDescriptor->Type == CmResourceTypePort) {
                    if (partialDescriptor->u.Port.Start.HighPart == 0 &&
                        (partialDescriptor->u.Port.Start.LowPart & 0x00000300) == 0) {
                        partialDescriptor->Flags |= CM_RESOURCE_PORT_16_BIT_DECODE;
                    }
                }
            }

            nextDescriptor = (PUCHAR)fullDescriptor + sizeof(CM_FULL_RESOURCE_DESCRIPTOR);

            //
            // account for any resource descriptors in addition to the single
            // imbedded one I've already accounted for (if there aren't any,
            // then I'll end up subtracting off the extra imbedded descriptor
            // from the previous step)
            //
            //
            // finally, account for any extra device specific data at the end of
            // the last partial resource descriptor (if any)
            //
            if (partialResourceList->Count > 0) {

                nextDescriptor += (partialResourceList->Count - 1) *
                     sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);

                lastResourceIndex = partialResourceList->Count - 1;

                if (partialResourceList->PartialDescriptors[lastResourceIndex].Type ==
                          CmResourceTypeDeviceSpecific) {

                    nextDescriptor += partialResourceList->PartialDescriptors[lastResourceIndex].
                               u.DeviceSpecificData.DataSize;
                }
            }

            fullDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)nextDescriptor;
        }
    }
#endif

    if (RtlCompareMemory(PnpDeviceName,
                         L"*PNP06",
                         sizeof(L"*PNP06") - sizeof(WCHAR)) ==
                         sizeof(L"*PNP06") - sizeof(WCHAR)) {

        ULONG i, j;

        ioResourceList = ResReqList->List;

        for (j = 0; j < ResReqList->AlternativeLists; j++) {

            ioResourceDescriptors = ioResourceList->Descriptors;

            for (i = 0; i < ioResourceList->Count; i++) {

                if (ioResourceDescriptors[i].Type == CmResourceTypePort) {

                    //
                    // some bios asks for 1 too many io port for ide channel
                    //
                    if ((ioResourceDescriptors[i].u.Port.Length == 2) &&
                            (ioResourceDescriptors[i].u.Port.MaximumAddress.QuadPart ==
                            (ioResourceDescriptors[i].u.Port.MinimumAddress.QuadPart + 1))) {

                            ioResourceDescriptors[i].u.Port.Length = 1;
                        ioResourceDescriptors[i].u.Port.MaximumAddress =
                            ioResourceDescriptors[i].u.Port.MinimumAddress;
                    }
                }
            }

            ioResourceList = (PIO_RESOURCE_LIST) (ioResourceDescriptors + ioResourceList->Count);
        }
    }
}
