2205 lines
91 KiB
C
2205 lines
91 KiB
C
/*++
|
|
Copyright (c) 1994 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
|
|
--*/
|
|
|
|
#include "iop.h"
|
|
#pragma hdrstop
|
|
#include "pnpcvrt.h"
|
|
#include "pbios.h"
|
|
|
|
#if UMODETEST
|
|
#undef IsNEC_98
|
|
#define IsNEC_98 0
|
|
#endif
|
|
|
|
#ifdef POOL_TAGGING
|
|
#undef ExAllocatePool
|
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'PpaM')
|
|
#endif
|
|
|
|
// Debugging stuff
|
|
|
|
#if DBG
|
|
|
|
#define MAPPER_ERROR 0x00000001
|
|
#define MAPPER_INFORMATION 0x00000002
|
|
#define MAPPER_PNP_ID 0x00000004
|
|
#define MAPPER_RESOURCES 0x00000008
|
|
#define MAPPER_REGISTRY 0x00000010
|
|
#define MAPPER_VERBOSE 0x00008000
|
|
|
|
ULONG PnPBiosMapperDebugMask = MAPPER_ERROR |
|
|
// MAPPER_INFORMATION |
|
|
// MAPPER_REGISTRY |
|
|
// MAPPER_PNP_ID |
|
|
// MAPPER_RESOURCES |
|
|
// MAPPER_VERBOSE |
|
|
0;
|
|
|
|
#define DebugPrint(X) PnPBiosDebugPrint X
|
|
|
|
VOID PnPBiosDebugPrint(ULONG DebugMask, PCCHAR DebugMessage, ...);
|
|
|
|
#else
|
|
#define DebugPrint(X)
|
|
#endif // DBG
|
|
|
|
#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 BIOSINFO_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;
|
|
|
|
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"*nEC13"), // Keyboard for NEC98
|
|
EXCLUSION_ENTRY(L"*nEC1E"), // PCMCIA Busses for NEC98
|
|
EXCLUSION_ENTRY(L"*nEC1F"), // Mouse for NEC98
|
|
EXCLUSION_ENTRY(L"*IBM3780"), // IBM Trackpoint Mouse
|
|
EXCLUSION_ENTRY(L"*IBM3781") // IBM Trackpoint Mouse
|
|
};
|
|
|
|
#define EXCLUDED_DEVICES_COUNT (sizeof(ExcludedDevices) / sizeof(ExcludedDevices[0]))
|
|
|
|
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;
|
|
|
|
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" }
|
|
};
|
|
|
|
CLASSDATA Class2Descriptions[] = {
|
|
{ 0x0000, L"Ethernet" },
|
|
{ 0x0100, L"Token ring" },
|
|
{ 0x0200, L"FDDI" },
|
|
{ 0x0300, L"ATM" },
|
|
{ 0x8000, L"Other network" }
|
|
};
|
|
|
|
CLASSDATA Class3Descriptions[] = {
|
|
{ 0x0000, L"VGA" },
|
|
{ 0x0001, L"SVGA" },
|
|
{ 0x0100, L"XGA" },
|
|
{ 0x8000, L"Other display" }
|
|
};
|
|
|
|
CLASSDATA Class4Descriptions[] = {
|
|
{ 0x0000, L"Video device" },
|
|
{ 0x0100, L"Audio device" },
|
|
{ 0x8000, L"Other multimedia" }
|
|
};
|
|
|
|
CLASSDATA Class5Descriptions[] = {
|
|
{ 0x0000, L"RAM memory" },
|
|
{ 0x0100, L"Flash memory" },
|
|
{ 0x8000, L"Other memory" }
|
|
};
|
|
|
|
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" }
|
|
};
|
|
|
|
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" }
|
|
};
|
|
|
|
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" }
|
|
};
|
|
|
|
CLASSDATA Class9Descriptions[] = {
|
|
{ 0x0000, L"Keyboard" },
|
|
{ 0x0100, L"Digitizer" },
|
|
{ 0x0200, L"Mouse" },
|
|
{ 0x8000, L"Other input" }
|
|
};
|
|
|
|
CLASSDATA Class10Descriptions[] = {
|
|
{ 0x0000, L"Generic dock" },
|
|
{ 0x8000, L"Other dock" },
|
|
};
|
|
|
|
CLASSDATA Class11Descriptions[] = {
|
|
{ 0x0000, L"386" },
|
|
{ 0x0100, L"486" },
|
|
{ 0x0200, L"Pentium" },
|
|
{ 0x1000, L"Alpha" },
|
|
{ 0x4000, L"Co-processor" }
|
|
};
|
|
|
|
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* Descriptions;
|
|
ULONG Count;
|
|
} 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 PEXCLUDED_PNPNODE 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
|
|
#pragma alloc_text(INIT, PnPBiosExpandProductId)
|
|
#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, 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".
|
|
RtlInitUnicodeString(&multifunctionKeyName, MULTIFUNCTION_KEY_NAME);
|
|
status = IopOpenRegistryKeyEx(&multifunctionKey, NULL, &multifunctionKeyName, KEY_READ);
|
|
if (!NT_SUCCESS(status)) {
|
|
DebugPrint((MAPPER_ERROR, "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) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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)) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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.
|
|
RtlInitUnicodeString(&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.
|
|
RtlInitUnicodeString(&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;
|
|
|
|
// BUGBUG - The WMI folks added another list so we should search for the PnPBIOS one, but for now the BIOS one is always first.
|
|
|
|
//ASSERT(biosValue->PartialResourceList.Count == 1);
|
|
*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 {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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 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 = wcslen(pTmp);
|
|
if (RtlCompareMemory(PnpID, pTmp, keyLen * sizeof(WCHAR)) == keyLen * sizeof(WCHAR)) {
|
|
bRet = TRUE;
|
|
break;
|
|
}
|
|
|
|
pTmp = pTmp + keyLen + 1;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
VOID PnPGetDevnodeExcludeList(OUT PKEY_VALUE_FULL_INFORMATION* ExcludeList)
|
|
{
|
|
UNICODE_STRING biosKeyName;
|
|
HANDLE biosKey;
|
|
NTSTATUS status;
|
|
|
|
RtlInitUnicodeString(&biosKeyName, BIOSINFO_KEY_NAME);
|
|
status = IopOpenRegistryKeyEx(&biosKey, NULL, &biosKeyName, KEY_READ);
|
|
if (!NT_SUCCESS(status)) {
|
|
// Don't really need to complain, likely not there.
|
|
return;
|
|
}
|
|
|
|
(VOID)IopGetRegistryValue(biosKey, BIOSINFO_VALUE_NAME, ExcludeList);
|
|
|
|
ZwClose(biosKey);
|
|
}
|
|
|
|
|
|
BOOLEAN PnPCheckFixedIoOverrideDecodes(VOID)
|
|
{
|
|
PKEY_VALUE_FULL_INFORMATION decodeFlagInfo = NULL;
|
|
UNICODE_STRING biosKeyName;
|
|
HANDLE biosKey;
|
|
NTSTATUS status;
|
|
BOOLEAN overrideDecodes = FALSE;
|
|
|
|
RtlInitUnicodeString(&biosKeyName, BIOSINFO_KEY_NAME);
|
|
status = IopOpenRegistryKeyEx(&biosKey, NULL, &biosKeyName, KEY_READ);
|
|
if (!NT_SUCCESS(status)) {
|
|
// Don't really need to complain, likely not there.
|
|
return FALSE;
|
|
}
|
|
|
|
status = IopGetRegistryValue(biosKey, DECODEINFO_VALUE_NAME, &decodeFlagInfo);
|
|
if (NT_SUCCESS(status)) {
|
|
ASSERT(decodeFlagInfo->Type == REG_DWORD);
|
|
if (decodeFlagInfo->DataLength == sizeof(ULONG)) {
|
|
overrideDecodes = (BOOLEAN) * ((PULONG)decodeFlagInfo->Name);
|
|
}
|
|
}
|
|
|
|
if (decodeFlagInfo) {
|
|
ExFreePool(decodeFlagInfo);
|
|
}
|
|
|
|
ZwClose(biosKey);
|
|
return overrideDecodes;
|
|
}
|
|
|
|
|
|
BOOLEAN PnPBiosCheckForExclusion(IN EXCLUDED_PNPNODE* 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)(IsNEC_98 ? 7 : 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 = ~0;
|
|
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:
|
|
DebugPrint((MAPPER_ERROR, "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;
|
|
PKEY_VALUE_FULL_INFORMATION excludeList = NULL;
|
|
PIO_RESOURCE_REQUIREMENTS_LIST tempResReqList;
|
|
PUCHAR currentPtr;
|
|
LONG lengthRemaining;
|
|
LONG remainingNodeLength;
|
|
ULONG numNodes;
|
|
ULONG nodeIndex;
|
|
PUCHAR configPtr;
|
|
ULONG configListLength;
|
|
NTSTATUS status;
|
|
ULONG convertFlags = 0;
|
|
|
|
// 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)) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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?
|
|
if (PnPCheckFixedIoOverrideDecodes()) {
|
|
convertFlags |= PPCONVERTFLAG_FORCE_FIXED_IO_16BIT_DECODE;
|
|
}
|
|
|
|
// 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) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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 {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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;
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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 = 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 {
|
|
DebugPrint((MAPPER_INFORMATION, "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) {
|
|
DebugPrint((MAPPER_INFORMATION,
|
|
"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) {
|
|
DebugPrint((MAPPER_INFORMATION,
|
|
"Best match is close enough, score = %4.4X, possible = %4.4X, index = %d\n",
|
|
bestScore,
|
|
possibleScore,
|
|
bestMatch));
|
|
} else {
|
|
DebugPrint((MAPPER_INFORMATION,
|
|
"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;
|
|
|
|
RtlInitUnicodeString(&enumRootKeyName, ENUMROOT_KEY_NAME);
|
|
status = IopOpenRegistryKeyEx(&enumRootKey, NULL, &enumRootKeyName, KEY_READ);
|
|
if (!NT_SUCCESS(status)) {
|
|
DebugPrint((MAPPER_ERROR, "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_NO_MORE_ENTRIES) {
|
|
DebugPrint((MAPPER_ERROR, "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)) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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_NO_MORE_ENTRIES) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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)) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"Could not open registry key %S\\%S\\%S, status = %8.8X\n",
|
|
ENUMROOT_KEY_NAME,
|
|
deviceBasicInfo->Name,
|
|
logConfStr,
|
|
status));
|
|
continue;
|
|
}
|
|
RtlInitUnicodeString(&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 {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"Error allocating memory for %S\\%S\\LogConf\\BootConfig value\n",
|
|
ENUMROOT_KEY_NAME,
|
|
deviceBasicInfo->Name));
|
|
valueInfoLength = 0;
|
|
status = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
} else {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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';
|
|
DebugPrint((MAPPER_INFORMATION,
|
|
"Match found: %S\\%S%d replaces %S\n",
|
|
DevNodeInfoList[matchingIndex].ProductId,
|
|
INSTANCE_ID_PREFIX,
|
|
DevNodeInfoList[matchingIndex].Handle,
|
|
DevNodeInfoList[matchingIndex].Replaces));
|
|
} else {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"Error allocating memory for %S\\%S%d\\Replaces\n",
|
|
DevNodeInfoList[matchingIndex].ProductId,
|
|
INSTANCE_ID_PREFIX,
|
|
DevNodeInfoList[matchingIndex].Handle));
|
|
}
|
|
} else {
|
|
DebugPrint((MAPPER_INFORMATION,
|
|
"No matching PnP Bios DevNode found for FW Enumerated device %S\\%S\n",
|
|
deviceBasicInfo->Name,
|
|
instanceBasicInfo->Name));
|
|
}
|
|
} else {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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* 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)) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"PnPBiosCopyDeviceParamKey() - Could not open source instance key %S, status = %8.8X\n",
|
|
SourcePath,
|
|
status));
|
|
return status;
|
|
}
|
|
RtlInitUnicodeString(&deviceParamKeyName, REGSTR_KEY_DEVICEPARAMETERS);
|
|
status = IopOpenRegistryKeyEx(&sourceDeviceParamKey, sourceInstanceKey, &deviceParamKeyName, KEY_ALL_ACCESS);
|
|
if (!NT_SUCCESS(status)) {
|
|
if (status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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)) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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);
|
|
RtlInitUnicodeString(&serialPrefixString, L"*PNP0501");
|
|
|
|
if (sourcePathString.Length > serialPrefixString.Length) {
|
|
sourcePathString.Length = serialPrefixString.Length;
|
|
}
|
|
|
|
if (RtlCompareUnicodeString(&sourcePathString, &serialPrefixString, TRUE) == 0) {
|
|
RtlInitUnicodeString(&portNameString, L"PortName");
|
|
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) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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;
|
|
|
|
RtlInitUnicodeString(&enumRootKeyName, ENUMROOT_KEY_NAME);
|
|
status = IopOpenRegistryKeyEx(&enumRootKey, NULL, &enumRootKeyName, KEY_ALL_ACCESS);
|
|
if (!NT_SUCCESS(status)) {
|
|
DebugPrint((MAPPER_ERROR, "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
|
|
PnPGetDevnodeExcludeList(&excludeList);
|
|
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)) {
|
|
isNewDevice = disposition == REG_CREATED_NEW_KEY;
|
|
if (isNewDevice) {
|
|
RtlInitUnicodeString(&valueName, L"DeviceDesc");
|
|
descriptionStr = PnPBiosGetDescription(&DevNodeInfoList[nodeIndex]);
|
|
descriptionStrLength = wcslen(descriptionStr) * 2 + sizeof(UNICODE_NULL);
|
|
status = ZwSetValueKey(instanceKey, &valueName, 0, REG_SZ, descriptionStr, descriptionStrLength);
|
|
}
|
|
|
|
RtlInitUnicodeString(&valueName, REGSTR_VAL_FIRMWAREIDENTIFIED);
|
|
dwordValue = 1;
|
|
status = ZwSetValueKey(instanceKey, &valueName, 0, REG_DWORD, &dwordValue, sizeof(dwordValue));
|
|
if (isNewDevice) {
|
|
RtlInitUnicodeString(&valueName, L"HardwareID");
|
|
status = ZwSetValueKey(instanceKey,
|
|
&valueName,
|
|
0,
|
|
REG_MULTI_SZ,
|
|
DevNodeInfoList[nodeIndex].ProductId,
|
|
sizeof(DevNodeInfoList[nodeIndex].ProductId));
|
|
if (DevNodeInfoList[nodeIndex].CompatibleIDs != NULL) {
|
|
RtlInitUnicodeString(&valueName, L"CompatibleIDs");
|
|
status = ZwSetValueKey(instanceKey,
|
|
&valueName,
|
|
0,
|
|
REG_MULTI_SZ,
|
|
DevNodeInfoList[nodeIndex].CompatibleIDs,
|
|
DevNodeInfoList[nodeIndex].CompatibleIDsLength);
|
|
}
|
|
}
|
|
|
|
RtlInitUnicodeString(&valueName, L"Replaces");
|
|
if (DevNodeInfoList[nodeIndex].Replaces != NULL) {
|
|
status = ZwSetValueKey(instanceKey,
|
|
&valueName,
|
|
0,
|
|
REG_SZ,
|
|
DevNodeInfoList[nodeIndex].Replaces,
|
|
wcslen(DevNodeInfoList[nodeIndex].Replaces) * 2 + sizeof(UNICODE_NULL));
|
|
} else if (!isNewDevice) {
|
|
status = ZwDeleteValueKey(instanceKey, &valueName);
|
|
}
|
|
|
|
RtlInitUnicodeString(&controlKeyName, REGSTR_KEY_DEVICECONTROL);
|
|
status = IopCreateRegistryKeyEx(&controlKey,
|
|
instanceKey,
|
|
&controlKeyName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_VOLATILE,
|
|
NULL);
|
|
if (NT_SUCCESS(status)) {
|
|
RtlInitUnicodeString(&valueName, REGSTR_VAL_FIRMWAREMEMBER);
|
|
dwordValue = 1;
|
|
status = ZwSetValueKey(controlKey, &valueName, 0, REG_DWORD, &dwordValue, sizeof(dwordValue));
|
|
RtlInitUnicodeString(&valueName, L"PnpBiosDeviceHandle");
|
|
dwordValue = DevNodeInfoList[nodeIndex].Handle;
|
|
status = ZwSetValueKey(controlKey, &valueName, 0, REG_DWORD, &dwordValue, sizeof(dwordValue));
|
|
RtlInitUnicodeString(&valueName, REGSTR_VAL_FIRMWAREDISABLED);
|
|
dwordValue = DevNodeInfoList[nodeIndex].FirmwareDisabled;
|
|
status = ZwSetValueKey(controlKey, &valueName, 0, REG_DWORD, &dwordValue, sizeof(dwordValue));
|
|
RtlInitUnicodeString(&valueName, L"PnpBiosDeviceHandle");
|
|
dwordValue = DevNodeInfoList[nodeIndex].Handle;
|
|
ZwClose(controlKey);
|
|
} else {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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;
|
|
}
|
|
|
|
RtlInitUnicodeString(&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) {
|
|
RtlInitUnicodeString(&valueName, REGSTR_VAL_BOOTCONFIG);
|
|
status = ZwSetValueKey(logConfKey,
|
|
&valueName,
|
|
0,
|
|
REG_RESOURCE_LIST,
|
|
DevNodeInfoList[nodeIndex].BootConfig,
|
|
DevNodeInfoList[nodeIndex].BootConfigLength);
|
|
}
|
|
|
|
if (DevNodeInfoList[nodeIndex].BasicConfig != NULL) {
|
|
RtlInitUnicodeString(&valueName, REGSTR_VAL_BASICCONFIGVECTOR);
|
|
status = ZwSetValueKey(logConfKey,
|
|
&valueName,
|
|
0,
|
|
REG_RESOURCE_REQUIREMENTS_LIST,
|
|
DevNodeInfoList[nodeIndex].BasicConfig,
|
|
DevNodeInfoList[nodeIndex].BasicConfigLength);
|
|
}
|
|
|
|
ZwClose(logConfKey);
|
|
} else {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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 {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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)
|
|
{
|
|
HANDLE deviceKey;
|
|
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)) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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;
|
|
}
|
|
|
|
RtlInitUnicodeString(&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)) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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 {
|
|
DebugPrint((MAPPER_ERROR, "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)) {
|
|
DebugPrint((MAPPER_ERROR,
|
|
"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:
|
|
DebugPrint((MAPPER_ERROR, "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 (IsNEC_98) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#if DBG
|
|
VOID PnPBiosDebugPrint(ULONG DebugMask, PCCHAR DebugMessage, ...)
|
|
/*++
|
|
Routine Description:
|
|
Debug print for the firmware mapper
|
|
Arguments:
|
|
Check the mask value to see if the debug message is requested.
|
|
--*/
|
|
{
|
|
va_list ap;
|
|
CHAR buffer[256];
|
|
|
|
va_start(ap, DebugMessage);
|
|
|
|
#define DBG_MSG_PREFIX "BiosMapper: "
|
|
|
|
strcpy(buffer, DBG_MSG_PREFIX);
|
|
|
|
if (DebugMask & PnPBiosMapperDebugMask) {
|
|
_vsnprintf(&buffer[sizeof(DBG_MSG_PREFIX) - 1], sizeof(buffer) - sizeof(DBG_MSG_PREFIX), DebugMessage, ap);
|
|
DbgPrint(buffer);
|
|
}
|
|
|
|
va_end(ap);
|
|
}
|
|
#endif |