3131 lines
100 KiB
C
3131 lines
100 KiB
C
/*++
|
||
|
||
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 *)(¤tPtr[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 *)(¤tPtr[1]);
|
||
increment += 3; // length of large tag
|
||
}
|
||
|
||
if (tagName == TAG_COMPATIBLE_ID) {
|
||
|
||
*idPtr = '*';
|
||
PnPBiosExpandProductId(¤tPtr[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);
|
||
}
|
||
}
|
||
}
|