You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5146 lines
136 KiB
5146 lines
136 KiB
/*++
|
|
|
|
Copyright (c) 1996-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
utils.c
|
|
|
|
Abstract:
|
|
|
|
This module contains assorted utility functions for PCI.SYS.
|
|
|
|
Author:
|
|
|
|
Peter Johnston (peterj) 20-Nov-1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pcip.h"
|
|
|
|
typedef struct _LIST_CONTEXT {
|
|
PCM_PARTIAL_RESOURCE_LIST List;
|
|
CM_RESOURCE_TYPE DesiredType;
|
|
ULONG Remaining;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR Next;
|
|
CM_PARTIAL_RESOURCE_DESCRIPTOR Alias;
|
|
} LIST_CONTEXT, *PLIST_CONTEXT;
|
|
|
|
extern PPCI_IRQ_ROUTING_TABLE PciIrqRoutingTable;
|
|
|
|
VOID
|
|
PcipInitializePartialListContext(
|
|
IN PLIST_CONTEXT ListContext,
|
|
IN PCM_PARTIAL_RESOURCE_LIST PartialList,
|
|
IN CM_RESOURCE_TYPE DesiredType
|
|
);
|
|
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR
|
|
PcipGetNextRangeFromList(
|
|
PLIST_CONTEXT ListContext
|
|
);
|
|
|
|
NTSTATUS
|
|
PciGetDeviceCapabilities(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDEVICE_CAPABILITIES DeviceCapabilities
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, PcipDestroySecondaryExtension)
|
|
#pragma alloc_text(PAGE, PciFindDescriptorInCmResourceList)
|
|
#pragma alloc_text(PAGE, PciFindParentPciFdoExtension)
|
|
#pragma alloc_text(PAGE, PciGetDeviceCapabilities)
|
|
#pragma alloc_text(PAGE, PciGetDeviceProperty)
|
|
#pragma alloc_text(PAGE, PcipGetNextRangeFromList)
|
|
#pragma alloc_text(PAGE, PciGetRegistryValue)
|
|
#pragma alloc_text(PAGE, PcipInitializePartialListContext)
|
|
#pragma alloc_text(PAGE, PciInsertEntryAtHead)
|
|
#pragma alloc_text(PAGE, PciInsertEntryAtTail)
|
|
#pragma alloc_text(PAGE, PcipLinkSecondaryExtension)
|
|
#pragma alloc_text(PAGE, PciOpenKey)
|
|
#pragma alloc_text(PAGE, PciQueryBusInformation)
|
|
#pragma alloc_text(PAGE, PciQueryLegacyBusInformation)
|
|
#pragma alloc_text(PAGE, PciQueryCapabilities)
|
|
#pragma alloc_text(PAGE, PciRangeListFromResourceList)
|
|
#pragma alloc_text(PAGE, PciSaveBiosConfig)
|
|
#pragma alloc_text(PAGE, PciGetBiosConfig)
|
|
#pragma alloc_text(PAGE, PciStringToUSHORT)
|
|
#pragma alloc_text(PAGE, PciSendIoctl)
|
|
#pragma alloc_text(INIT, PciBuildDefaultExclusionLists)
|
|
#pragma alloc_text(PAGE, PciIsDeviceOnDebugPath)
|
|
#endif
|
|
|
|
|
|
//
|
|
// Range lists indicating the ranges excluded from decode when the ISA and/or
|
|
// VGA bits are set on a bridge. Initialized by PciBuildDefaultExclusionLists
|
|
// from DriverEntry.
|
|
//
|
|
RTL_RANGE_LIST PciIsaBitExclusionList;
|
|
RTL_RANGE_LIST PciVgaAndIsaBitExclusionList;
|
|
|
|
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR
|
|
PciFindDescriptorInCmResourceList(
|
|
IN CM_RESOURCE_TYPE DescriptorType,
|
|
IN PCM_RESOURCE_LIST ResourceList,
|
|
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR PreviousHit
|
|
)
|
|
{
|
|
ULONG numlists;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR full;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor;
|
|
|
|
if (ResourceList == NULL) {
|
|
return NULL;
|
|
}
|
|
numlists = ResourceList->Count;
|
|
full = ResourceList->List;
|
|
while (numlists--) {
|
|
PCM_PARTIAL_RESOURCE_LIST partial = &full->PartialResourceList;
|
|
ULONG count = partial->Count;
|
|
|
|
descriptor = partial->PartialDescriptors;
|
|
while (count--) {
|
|
|
|
if (descriptor->Type == DescriptorType) {
|
|
|
|
//
|
|
// We have a hit on the type. If we we are doing a
|
|
// find next, check to see if we're back where we got
|
|
// to last time yet.
|
|
//
|
|
|
|
if (PreviousHit != NULL) {
|
|
if (PreviousHit == descriptor) {
|
|
|
|
//
|
|
// We found it again, now we can search for real.
|
|
//
|
|
|
|
PreviousHit = NULL;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// It's the one.
|
|
//
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
}
|
|
descriptor = PciNextPartialDescriptor(descriptor);
|
|
}
|
|
|
|
full = (PCM_FULL_RESOURCE_DESCRIPTOR)descriptor;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
PVOID
|
|
PciFindNextSecondaryExtension(
|
|
IN PSINGLE_LIST_ENTRY ListEntry,
|
|
IN PCI_SIGNATURE DesiredType
|
|
)
|
|
{
|
|
PPCI_SECONDARY_EXTENSION extension;
|
|
|
|
while (ListEntry != NULL) {
|
|
|
|
extension = CONTAINING_RECORD(ListEntry,
|
|
PCI_SECONDARY_EXTENSION,
|
|
List);
|
|
if (extension->ExtensionType == DesiredType) {
|
|
|
|
//
|
|
// This extension is the right type, get out.
|
|
//
|
|
|
|
return extension;
|
|
}
|
|
ListEntry = extension->List.Next;
|
|
}
|
|
|
|
//
|
|
// Didn't find it, fail.
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
PcipLinkSecondaryExtension(
|
|
IN PSINGLE_LIST_ENTRY ListHead,
|
|
IN PFAST_MUTEX Mutex,
|
|
IN PVOID NewExtension,
|
|
IN PCI_SIGNATURE Type,
|
|
IN PSECONDARYEXTENSIONDESTRUCTOR Destructor
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add a secondary extension to the secondary extension list for
|
|
a PDO/FDO and fill in the header fields.
|
|
|
|
NOTE: Use the macro PciLinkSecondaryExtension which takes a
|
|
PDO extension or FDO extension instead of the list header and
|
|
mutex fields.
|
|
|
|
Arguments:
|
|
|
|
ListHead &SecondaryExtension.Next from the FDO/PDO extension.
|
|
Mutex FDO/PDO Mutex.
|
|
NewExtension Extension being added to the list.
|
|
Type Member of the enum PCI_SIGNATURE.
|
|
Destructor Routine to call when this entry is being torn down.
|
|
(Optional).
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPCI_SECONDARY_EXTENSION Header;
|
|
|
|
PAGED_CODE();
|
|
|
|
Header = (PPCI_SECONDARY_EXTENSION)NewExtension;
|
|
|
|
Header->ExtensionType = Type;
|
|
Header->Destructor = Destructor;
|
|
|
|
PciInsertEntryAtHead(ListHead, &Header->List, Mutex);
|
|
}
|
|
|
|
VOID
|
|
PcipDestroySecondaryExtension(
|
|
IN PSINGLE_LIST_ENTRY ListHead,
|
|
IN PFAST_MUTEX Mutex,
|
|
IN PVOID Extension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove this secondary extension from the list of secondary
|
|
extensions, call its destructor routine and free the memory
|
|
allocated to it. The destructor is responsible for deleting
|
|
any associated allocations.
|
|
|
|
Failure is not an option.
|
|
|
|
Note: Use the macro PciDestroySecondaryExtension instead of
|
|
calling this routine directly.
|
|
|
|
Arguments:
|
|
|
|
ListHead Pointer to the list this extension is on.
|
|
Mutex Mutex for synchronization of list manipulation.
|
|
Extension The Secondary extension being destroyed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPCI_SECONDARY_EXTENSION Header;
|
|
|
|
PAGED_CODE();
|
|
|
|
Header = (PPCI_SECONDARY_EXTENSION)Extension;
|
|
|
|
PciRemoveEntryFromList(ListHead, &Header->List, Mutex);
|
|
|
|
//
|
|
// Call the extension's destructor if one was specified.
|
|
//
|
|
|
|
if (Header->Destructor != NULL) {
|
|
Header->Destructor(Extension);
|
|
}
|
|
|
|
//
|
|
// Free the memory allocated for this extension.
|
|
//
|
|
|
|
ExFreePool(Extension);
|
|
}
|
|
|
|
VOID
|
|
PciInsertEntryAtTail(
|
|
IN PSINGLE_LIST_ENTRY ListHead,
|
|
IN PSINGLE_LIST_ENTRY NewEntry,
|
|
IN PFAST_MUTEX Mutex
|
|
)
|
|
{
|
|
PSINGLE_LIST_ENTRY Previous;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (Mutex) {
|
|
ExAcquireFastMutex(Mutex);
|
|
}
|
|
|
|
//
|
|
// Find the end of the list.
|
|
//
|
|
|
|
Previous = ListHead;
|
|
|
|
while (Previous->Next) {
|
|
Previous = Previous->Next;
|
|
}
|
|
|
|
//
|
|
// Append the entry.
|
|
//
|
|
|
|
Previous->Next = NewEntry;
|
|
|
|
if (Mutex) {
|
|
ExReleaseFastMutex(Mutex);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
PciInsertEntryAtHead(
|
|
IN PSINGLE_LIST_ENTRY ListHead,
|
|
IN PSINGLE_LIST_ENTRY NewEntry,
|
|
IN PFAST_MUTEX Mutex
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (Mutex) {
|
|
ExAcquireFastMutex(Mutex);
|
|
}
|
|
|
|
NewEntry->Next = ListHead->Next;
|
|
ListHead->Next = NewEntry;
|
|
|
|
if (Mutex) {
|
|
ExReleaseFastMutex(Mutex);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
PciRemoveEntryFromList(
|
|
IN PSINGLE_LIST_ENTRY ListHead,
|
|
IN PSINGLE_LIST_ENTRY OldEntry,
|
|
IN PFAST_MUTEX Mutex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove an entry from a singly linked list.
|
|
|
|
It is the caller's responsibility to have locked the list if
|
|
there is danger of multiple updates.
|
|
|
|
Arguments:
|
|
|
|
ListHead - Address of the first entry in the list.
|
|
OldEntry - Address of the entry to be removed from the
|
|
list.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSINGLE_LIST_ENTRY Previous;
|
|
|
|
//
|
|
// Sanity check, the list head can't be removed.
|
|
//
|
|
|
|
PCI_ASSERT(ListHead != OldEntry);
|
|
|
|
if (Mutex) {
|
|
ExAcquireFastMutex(Mutex);
|
|
}
|
|
|
|
//
|
|
// Locate the entry that points to this entry.
|
|
//
|
|
|
|
for (Previous = ListHead; Previous; Previous = Previous->Next) {
|
|
if (Previous->Next == OldEntry) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The entry is not in the list - this is bad but fail gracefully...
|
|
//
|
|
|
|
if (!Previous) {
|
|
PCI_ASSERT(Previous);
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Pull it off the list.
|
|
//
|
|
|
|
Previous->Next = OldEntry->Next;
|
|
OldEntry->Next = NULL;
|
|
|
|
exit:
|
|
|
|
if (Mutex) {
|
|
ExReleaseFastMutex(Mutex);
|
|
}
|
|
|
|
}
|
|
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR
|
|
PciNextPartialDescriptor(
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a pointer to a CmPartialResourceDescriptor, return a pointer
|
|
to the next descriptor in the same list.
|
|
|
|
This is only done in a routine (rather than a simple descriptor++)
|
|
because if the variable length resource CmResourceTypeDeviceSpecific.
|
|
|
|
Arguments:
|
|
|
|
Descriptor - Pointer to the descriptor being advanced over.
|
|
|
|
Return Value:
|
|
|
|
Pointer to the next descriptor in the same list (or byte beyond
|
|
end of list).
|
|
|
|
--*/
|
|
|
|
{
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR nextDescriptor;
|
|
|
|
nextDescriptor = Descriptor + 1;
|
|
|
|
if (Descriptor->Type == CmResourceTypeDeviceSpecific) {
|
|
|
|
//
|
|
// This (old) descriptor is followed by DataSize bytes
|
|
// of device specific data, ie, not immediatelly by the
|
|
// next descriptor. Adjust nextDescriptor by this amount.
|
|
//
|
|
|
|
nextDescriptor = (PCM_PARTIAL_RESOURCE_DESCRIPTOR)
|
|
((ULONG_PTR)nextDescriptor + Descriptor->u.DeviceSpecificData.DataSize);
|
|
}
|
|
return nextDescriptor;
|
|
}
|
|
|
|
VOID
|
|
PcipInitializePartialListContext(
|
|
IN PLIST_CONTEXT ListContext,
|
|
IN PCM_PARTIAL_RESOURCE_LIST PartialList,
|
|
IN CM_RESOURCE_TYPE DesiredType
|
|
)
|
|
{
|
|
PCI_ASSERT(DesiredType != CmResourceTypeNull);
|
|
|
|
ListContext->List = PartialList;
|
|
ListContext->DesiredType = DesiredType;
|
|
ListContext->Remaining = PartialList->Count;
|
|
ListContext->Next = PartialList->PartialDescriptors;
|
|
ListContext->Alias.Type = CmResourceTypeNull;
|
|
}
|
|
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR
|
|
PcipGetNextRangeFromList(
|
|
PLIST_CONTEXT ListContext
|
|
)
|
|
{
|
|
ULONG Addend;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR current;
|
|
|
|
//
|
|
// See if we should be generating an alias to the current
|
|
// descriptor.
|
|
//
|
|
|
|
if (ListContext->Alias.Type == ListContext->DesiredType) {
|
|
|
|
//
|
|
// Yes, advance to alias by adding offset to next 10 bit or
|
|
// 12 bit alias (only allowable values).
|
|
//
|
|
|
|
if (ListContext->Alias.Flags & CM_RESOURCE_PORT_10_BIT_DECODE) {
|
|
Addend = 1 << 10;
|
|
} else {
|
|
Addend = 1 << 12;
|
|
}
|
|
Addend += ListContext->Alias.u.Generic.Start.LowPart;
|
|
|
|
if (Addend < (1 << 16)) {
|
|
|
|
//
|
|
// This is a valid alias, return it.
|
|
//
|
|
|
|
ListContext->Alias.u.Generic.Start.LowPart = Addend;
|
|
return &ListContext->Alias;
|
|
}
|
|
|
|
//
|
|
// Out of aliases to this resource.
|
|
//
|
|
|
|
ListContext->Alias.Type = CmResourceTypeNull;
|
|
}
|
|
|
|
//
|
|
// We get here if there are no aliases or it is time to advance
|
|
// to the next descriptor of the desired type.
|
|
//
|
|
|
|
while (ListContext->Remaining != 0) {
|
|
|
|
current = ListContext->Next;
|
|
|
|
//
|
|
// Advance context to next before examining and possibly
|
|
// returning current.
|
|
//
|
|
|
|
ListContext->Next = PciNextPartialDescriptor(current);
|
|
ListContext->Remaining--;
|
|
|
|
//
|
|
// Is this current descriptor a candidate?
|
|
//
|
|
|
|
if (current->Type == ListContext->DesiredType) {
|
|
|
|
//
|
|
// Return this one to caller. If this descriptor has
|
|
// aliases, setup so the next call will return an alias.
|
|
//
|
|
|
|
if (current->Flags & (CM_RESOURCE_PORT_10_BIT_DECODE |
|
|
CM_RESOURCE_PORT_12_BIT_DECODE)) {
|
|
ListContext->Alias = *current;
|
|
}
|
|
return current;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No aliases and no new descriptors of the desired type.
|
|
//
|
|
|
|
return NULL;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciQueryPowerCapabilities(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN PDEVICE_CAPABILITIES Capabilities
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
determine a device's power capabilites by using its parent capabilities
|
|
|
|
It should be noted that there two ways that the code calculates the system
|
|
and device wake levels. The first method, which is preferred, biases toward
|
|
the deepest possible system state, and the second, which gets used if the
|
|
first fails to find something legal, is biased towards finding the deepest
|
|
possible device wake state
|
|
|
|
Arguments:
|
|
|
|
PdoExtension - The PDO whose capabilities we will provide
|
|
Capablities - Where we will store the device capabilities
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
DEVICE_CAPABILITIES parentCapabilities;
|
|
DEVICE_POWER_STATE deviceState;
|
|
DEVICE_POWER_STATE validDeviceWakeState = PowerDeviceUnspecified;
|
|
SYSTEM_POWER_STATE index;
|
|
SYSTEM_POWER_STATE highestSupportedSleepState = PowerSystemUnspecified;
|
|
SYSTEM_POWER_STATE validSystemWakeState = PowerSystemUnspecified;
|
|
|
|
//
|
|
// Get the device capabilities of the parent
|
|
//
|
|
status = PciGetDeviceCapabilities(
|
|
PdoExtension->ParentFdoExtension->PhysicalDeviceObject,
|
|
&parentCapabilities
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that we have sane device capabilities to start with...
|
|
//
|
|
if (parentCapabilities.DeviceState[PowerSystemWorking] == PowerDeviceUnspecified) {
|
|
|
|
parentCapabilities.DeviceState[PowerSystemWorking] = PowerDeviceD0;
|
|
|
|
}
|
|
if (parentCapabilities.DeviceState[PowerSystemShutdown] == PowerDeviceUnspecified) {
|
|
|
|
parentCapabilities.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
|
|
|
|
}
|
|
|
|
//
|
|
// Does the device have any PCI power capabilities?
|
|
//
|
|
if ( (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)) {
|
|
|
|
//
|
|
// Use the parent's mapping as our own
|
|
//
|
|
RtlCopyMemory(
|
|
Capabilities->DeviceState,
|
|
parentCapabilities.DeviceState,
|
|
(PowerSystemShutdown + 1) * sizeof(DEVICE_POWER_STATE)
|
|
);
|
|
|
|
//
|
|
// As D1 and D2 are not supported here, round down to D3.
|
|
//
|
|
// This code is not enabled so that a hack becomes available for
|
|
// older PCI video cards. Basically, older video cards can do D3 hot
|
|
// but not D3 cold (in which case they need reposting). ACPI supplies
|
|
// a hack by which all PCI-to-PCI bridges are said to map S1->D1. The
|
|
// code below lets the parent's D1 "appear" as a state the child
|
|
// supports, regardless of it's real capabilities. Video drivers for
|
|
// such cards fail D3 (which may be D3-cold), but succeed D1 (which is
|
|
// really D3-hot).
|
|
//
|
|
// Also note that this is not targetted at video cards but rather is
|
|
// targetted at any non-PCI power managed device. That means drivers
|
|
// for older devices need to either map D1&D2 to D3 themselves, or
|
|
// treat unexpected D1&D2 IRPs as if D3. Folklore says that there is
|
|
// also a net card or two that also takes advantage of this hack.
|
|
//
|
|
#if 0
|
|
for (index = PowerSystemWorking; index < PowerSystemMaximum; index++) {
|
|
|
|
//
|
|
// This is the device state that the parent supports
|
|
//
|
|
deviceState = parentCapabilities.DeviceState[index];
|
|
|
|
//
|
|
// Round down if D1 or D2
|
|
//
|
|
if ((deviceState == PowerDeviceD1) || (deviceState == PowerDeviceD2)) {
|
|
|
|
Capabilities->DeviceState[index] = PowerDeviceD3;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// The device has no wake capabilities
|
|
//
|
|
Capabilities->DeviceWake = PowerDeviceUnspecified;
|
|
Capabilities->SystemWake = PowerSystemUnspecified;
|
|
|
|
//
|
|
// Set these bits explicitly
|
|
//
|
|
Capabilities->DeviceD1 = FALSE;
|
|
Capabilities->DeviceD2 = FALSE;
|
|
Capabilities->WakeFromD0 = FALSE;
|
|
Capabilities->WakeFromD1 = FALSE;
|
|
Capabilities->WakeFromD2 = FALSE;
|
|
Capabilities->WakeFromD3 = FALSE;
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Set all the capabilities bits
|
|
//
|
|
Capabilities->DeviceD1 = PdoExtension->PowerCapabilities.Support.D1;
|
|
Capabilities->DeviceD2 = PdoExtension->PowerCapabilities.Support.D2;
|
|
Capabilities->WakeFromD0 = PdoExtension->PowerCapabilities.Support.PMED0;
|
|
Capabilities->WakeFromD1 = PdoExtension->PowerCapabilities.Support.PMED1;
|
|
Capabilities->WakeFromD2 = PdoExtension->PowerCapabilities.Support.PMED2;
|
|
if (parentCapabilities.DeviceWake == PowerDeviceD3) {
|
|
|
|
//
|
|
// If our parent can wake from the D3 state, than we must support
|
|
// PM3 From D3 Cold. The (obvious) exception to this is if the
|
|
// parent is a root bus...
|
|
//
|
|
if (PCI_PDO_ON_ROOT(PdoExtension)) {
|
|
|
|
Capabilities->WakeFromD3 =
|
|
PdoExtension->PowerCapabilities.Support.PMED3Hot;
|
|
|
|
} else {
|
|
|
|
Capabilities->WakeFromD3 =
|
|
PdoExtension->PowerCapabilities.Support.PMED3Cold;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If our parent cannot wake from the D3 state, then we support
|
|
// the D3 state if we support PME3Hot
|
|
//
|
|
Capabilities->WakeFromD3 =
|
|
PdoExtension->PowerCapabilities.Support.PMED3Hot;
|
|
|
|
}
|
|
|
|
//
|
|
// First step is to make sure that all the S-states that we got from
|
|
// out parent map to valid D-states for this device
|
|
//
|
|
// ADRIAO N.B. 08/18/1999 -
|
|
// This algorithm works but it's overly aggressive. It is in fact legal
|
|
// for a bridge to be in D2 with a card behind it in D1.
|
|
//
|
|
for (index = PowerSystemWorking; index < PowerSystemMaximum; index++) {
|
|
|
|
//
|
|
// This is the device state that the parent supports
|
|
//
|
|
deviceState = parentCapabilities.DeviceState[index];
|
|
|
|
//
|
|
// If the device state is D1 and we don't support D1, then
|
|
// consider D2 instead
|
|
//
|
|
if (deviceState == PowerDeviceD1 &&
|
|
PdoExtension->PowerCapabilities.Support.D1 == FALSE) {
|
|
|
|
deviceState++;
|
|
|
|
}
|
|
|
|
//
|
|
// If the device state is D2 and we don't support D2, then
|
|
// consider D3 instead
|
|
//
|
|
if (deviceState == PowerDeviceD2 &&
|
|
PdoExtension->PowerCapabilities.Support.D2 == FALSE) {
|
|
|
|
deviceState++;
|
|
|
|
}
|
|
|
|
//
|
|
// We should be able to support this deviceState
|
|
//
|
|
Capabilities->DeviceState[index] = deviceState;
|
|
|
|
//
|
|
// If this S-state is less than PowerSystemHibernate, and the
|
|
// S-State doesn't map to PowerDeviceUnspecified, then consider
|
|
// this to be the highest supported SleepState
|
|
//
|
|
if (index < PowerSystemHibernate &&
|
|
Capabilities->DeviceState[index] != PowerDeviceUnspecified) {
|
|
|
|
highestSupportedSleepState = index;
|
|
|
|
}
|
|
|
|
//
|
|
// Can we support this as a wake state?
|
|
//
|
|
if (index < parentCapabilities.SystemWake &&
|
|
deviceState >= parentCapabilities.DeviceState[index] &&
|
|
parentCapabilities.DeviceState[index] != PowerDeviceUnspecified) {
|
|
|
|
//
|
|
// Consider using this as a valid wake state
|
|
//
|
|
if ( (deviceState == PowerDeviceD0 && Capabilities->WakeFromD0) ||
|
|
(deviceState == PowerDeviceD1 && Capabilities->WakeFromD1) ||
|
|
(deviceState == PowerDeviceD2 && Capabilities->WakeFromD2) ) {
|
|
|
|
validSystemWakeState = index;
|
|
validDeviceWakeState = deviceState;
|
|
|
|
} else if (deviceState == PowerDeviceD3 &&
|
|
PdoExtension->PowerCapabilities.Support.PMED3Hot) {
|
|
|
|
//
|
|
// This is a special case logic (which is why it is seperate from
|
|
// the above logic
|
|
//
|
|
if (parentCapabilities.DeviceState[index] < PowerDeviceD3 ||
|
|
PdoExtension->PowerCapabilities.Support.PMED3Cold) {
|
|
|
|
validSystemWakeState = index;
|
|
validDeviceWakeState = deviceState;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Does the parent device have power management capabilities?
|
|
// Does the device have power management capabilities?
|
|
// Can we wake up from the same D-states that our parent can? or better?
|
|
//
|
|
if (parentCapabilities.SystemWake == PowerSystemUnspecified ||
|
|
parentCapabilities.DeviceWake == PowerDeviceUnspecified ||
|
|
PdoExtension->PowerState.DeviceWakeLevel == PowerDeviceUnspecified ||
|
|
PdoExtension->PowerState.DeviceWakeLevel < parentCapabilities.DeviceWake) {
|
|
|
|
//
|
|
// The device doesn't support any kind of wakeup (that we know about)
|
|
// or the device doesn't support wakeup from supported D-states, so
|
|
// set the latency and return
|
|
//
|
|
Capabilities->D1Latency = 0;
|
|
Capabilities->D2Latency = 0;
|
|
Capabilities->D3Latency = 0;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// We should be able to wake the device from the same state
|
|
// that our parent can wake from
|
|
//
|
|
Capabilities->SystemWake = parentCapabilities.SystemWake;
|
|
Capabilities->DeviceWake = PdoExtension->PowerState.DeviceWakeLevel;
|
|
|
|
//
|
|
// Change our device wake level to include a state that we support
|
|
//
|
|
if (Capabilities->DeviceWake == PowerDeviceD0 && !Capabilities->WakeFromD0) {
|
|
|
|
Capabilities->DeviceWake++;
|
|
|
|
}
|
|
if (Capabilities->DeviceWake == PowerDeviceD1 && !Capabilities->WakeFromD1) {
|
|
|
|
Capabilities->DeviceWake++;
|
|
|
|
}
|
|
if (Capabilities->DeviceWake == PowerDeviceD2 && !Capabilities->WakeFromD2) {
|
|
|
|
Capabilities->DeviceWake++;
|
|
|
|
}
|
|
if (Capabilities->DeviceWake == PowerDeviceD3 && !Capabilities->WakeFromD3) {
|
|
|
|
Capabilities->DeviceWake = PowerDeviceUnspecified;
|
|
Capabilities->SystemWake = PowerSystemUnspecified;
|
|
|
|
}
|
|
|
|
//
|
|
// This is our fallback position. If we got here and there is no wake
|
|
// capability using the above method of calcuation, then we should
|
|
// check to see if we noticed a valid wake combination while scanning
|
|
// the S to D mapping information
|
|
//
|
|
if ( (Capabilities->DeviceWake == PowerDeviceUnspecified ||
|
|
Capabilities->SystemWake == PowerSystemUnspecified) &&
|
|
(validSystemWakeState != PowerSystemUnspecified &&
|
|
validDeviceWakeState != PowerSystemUnspecified) ) {
|
|
|
|
Capabilities->DeviceWake = validDeviceWakeState;
|
|
Capabilities->SystemWake = validSystemWakeState;
|
|
|
|
//
|
|
// Note that in this case, we might have set DeviceWake to D3, without
|
|
// having set the bit, so "correct" that situation.
|
|
//
|
|
if (validDeviceWakeState == PowerDeviceD3) {
|
|
|
|
Capabilities->WakeFromD3 = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
//
|
|
// We shouldn't allow Wake From S4, S5, unless the supports the D3 state
|
|
// Even then, we really shouldn't allow S4, S5 unless the device supports
|
|
// the D3Cold PME state
|
|
//
|
|
if (Capabilities->SystemWake > PowerSystemSleeping3) {
|
|
|
|
//
|
|
// Does the device support wake from D3?
|
|
//
|
|
if (Capabilities->DeviceWake != PowerDeviceD3) {
|
|
|
|
//
|
|
// Reduce the systemwake level to something more realistic
|
|
//
|
|
Capabilities->SystemWake = highestSupportedSleepState;
|
|
|
|
}
|
|
|
|
//
|
|
// This is in a seperate if statement so that the code can be easily
|
|
// commented out
|
|
//
|
|
if (!PdoExtension->PowerCapabilities.Support.PMED3Cold) {
|
|
|
|
//
|
|
// Reduce the systemwake level to something more realistic
|
|
//
|
|
Capabilities->SystemWake = highestSupportedSleepState;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// From the PCI Power Management spec V1.0, table 18
|
|
// "PCI Function State Transition Delays".
|
|
//
|
|
// D1 -> D0 0
|
|
// D2 -> D0 200 us
|
|
// D3 -> D0 10 ms
|
|
//
|
|
// The latency entries are in units of 100 us.
|
|
//
|
|
Capabilities->D1Latency = 0;
|
|
Capabilities->D2Latency = 2;
|
|
Capabilities->D3Latency = 100;
|
|
|
|
//
|
|
// Make sure that S0 maps to D0
|
|
//
|
|
PCI_ASSERT( Capabilities->DeviceState[PowerSystemWorking] == PowerDeviceD0);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciDetermineSlotNumber(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN OUT PULONG SlotNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Determine the slot number associated with a PCI device (if any)
|
|
through use of the PCI IRQ routing table information we may have
|
|
stored earlier.
|
|
|
|
If the previous mechanism fails to retrieve a slot number, see if
|
|
we can inherit our parent's slot number.
|
|
|
|
This result may be filtered further by ACPI and other bus filters..
|
|
|
|
Arguments:
|
|
|
|
PdoExtension - PDO extension of device in question.
|
|
SlotNumber - Pointer to slot number to update
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if slot # found
|
|
|
|
--*/
|
|
|
|
{
|
|
PSLOT_INFO slotInfo, lastSlot;
|
|
ULONG length;
|
|
|
|
//
|
|
// If we have a legacy PCI routing table and our Pdo isn't orphaned search
|
|
// the table for our physical slot number. If this is an ACPI machine then ACPI
|
|
// will overwrite this with the value from the _SUN if it exists.
|
|
//
|
|
|
|
if (PciIrqRoutingTable && PCI_PARENT_FDOX(PdoExtension)) {
|
|
|
|
slotInfo = (PSLOT_INFO)((PUCHAR) PciIrqRoutingTable +
|
|
sizeof(PCI_IRQ_ROUTING_TABLE));
|
|
lastSlot = (PSLOT_INFO)((PUCHAR) PciIrqRoutingTable +
|
|
PciIrqRoutingTable->TableSize);
|
|
|
|
// Search for a entry in the routing table that matches this device
|
|
|
|
while (slotInfo < lastSlot) {
|
|
if ((PCI_PARENT_FDOX(PdoExtension)->BaseBus == slotInfo->BusNumber) &&
|
|
((UCHAR)PdoExtension->Slot.u.bits.DeviceNumber == (slotInfo->DeviceNumber >> 3)) &&
|
|
(slotInfo->SlotNumber != 0)) {
|
|
*SlotNumber = slotInfo->SlotNumber;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
slotInfo++;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Maybe our parent has a UI Number that we could 'inherit'.
|
|
// but only if we're not a PDO off a root bus otherwise we pick up
|
|
// the UI number from the PNPA03 node (likely 0)
|
|
//
|
|
|
|
if (PCI_PDO_ON_ROOT(PdoExtension)) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return IoGetDeviceProperty(PCI_PARENT_PDO(PdoExtension),
|
|
DevicePropertyUINumber,
|
|
sizeof(*SlotNumber),
|
|
SlotNumber,
|
|
&length);
|
|
}
|
|
|
|
NTSTATUS
|
|
PciQueryCapabilities(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN PDEVICE_CAPABILITIES Capabilities
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
return a subset of our parent's capabilities.
|
|
|
|
Arguments:
|
|
|
|
Capabilities - pointer to a DEVICE_CAPABILITIES structured supplied
|
|
by the caller.
|
|
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
#ifndef HANDLE_BOGUS_CAPS
|
|
if (Capabilities->Version < 1) {
|
|
|
|
//
|
|
// do not touch irp!
|
|
//
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// For PCI devices, the Capabilities Address field contains
|
|
// the Device Number in the upper 16 bits and the function
|
|
// number in the lower.
|
|
//
|
|
Capabilities->Address =
|
|
PdoExtension->Slot.u.bits.DeviceNumber << 16 |
|
|
PdoExtension->Slot.u.bits.FunctionNumber;
|
|
|
|
//
|
|
// The PCI bus driver does not generate Unique IDs for its children.
|
|
//
|
|
Capabilities->UniqueID = FALSE;
|
|
|
|
//
|
|
// If this PDO is for a HOST BRIDGE, claim that it supports
|
|
// being handled Raw. This is so the device controller will
|
|
// allow installation of the NULL device on this puppy.
|
|
//
|
|
|
|
if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
|
|
(PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST)) {
|
|
|
|
Capabilities->RawDeviceOK = TRUE;
|
|
|
|
} else {
|
|
|
|
Capabilities->RawDeviceOK = FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// The following values should be fixed by filters or function
|
|
// drivers that actually know the answer.
|
|
//
|
|
Capabilities->LockSupported = FALSE;
|
|
Capabilities->EjectSupported = FALSE;
|
|
Capabilities->Removable = FALSE;
|
|
Capabilities->DockDevice = FALSE;
|
|
|
|
PciDetermineSlotNumber(PdoExtension, &Capabilities->UINumber);
|
|
|
|
//
|
|
// Get the device power capabilities
|
|
//
|
|
status = PciQueryPowerCapabilities( PdoExtension, Capabilities );
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
#if DBG
|
|
if (PciDebug & PciDbgQueryCap) {
|
|
|
|
PciDebugDumpQueryCapabilities(Capabilities);
|
|
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciQueryBusInformation(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN PPNP_BUS_INFORMATION *BusInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Tell PnP that it's talking to a PCI bus.
|
|
|
|
Arguments:
|
|
|
|
BusInformation - Pointer to a PPNP_BUS_INFORMATION. We create
|
|
a PNP_BUS_INFORMATION and pass its address
|
|
back thru here.
|
|
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPNP_BUS_INFORMATION information;
|
|
|
|
information = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, sizeof(PNP_BUS_INFORMATION));
|
|
|
|
if (information == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(&information->BusTypeGuid, &GUID_BUS_TYPE_PCI, sizeof(GUID));
|
|
information->LegacyBusType = PCIBus;
|
|
information->BusNumber = PCI_PARENT_FDOX(PdoExtension)->BaseBus;
|
|
|
|
*BusInformation = information;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciQueryLegacyBusInformation(
|
|
IN PPCI_FDO_EXTENSION FdoExtension,
|
|
IN PLEGACY_BUS_INFORMATION *BusInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Tell PnP that it's talking to a PCI bus.
|
|
|
|
Arguments:
|
|
|
|
BusInformation - Pointer to a PLEGACY_BUS_INFORMATION. We create
|
|
a LEGACY_BUS_INFORMATION and pass its address
|
|
back thru here.
|
|
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLEGACY_BUS_INFORMATION information;
|
|
|
|
information = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, sizeof(LEGACY_BUS_INFORMATION));
|
|
|
|
if (information == NULL) {
|
|
PCI_ASSERT(information != NULL);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(&information->BusTypeGuid, &GUID_BUS_TYPE_PCI, sizeof(GUID));
|
|
information->LegacyBusType = PCIBus;
|
|
information->BusNumber = FdoExtension->BaseBus;
|
|
|
|
*BusInformation = information;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciGetInterruptAssignment(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
OUT ULONG *Minimum,
|
|
OUT ULONG *Maximum
|
|
)
|
|
{
|
|
UCHAR pin = PdoExtension->InterruptPin;
|
|
|
|
//
|
|
// Using HAL for interrupts.
|
|
//
|
|
|
|
PIO_RESOURCE_REQUIREMENTS_LIST reqList;
|
|
PIO_RESOURCE_DESCRIPTOR resource;
|
|
NTSTATUS status = STATUS_RESOURCE_TYPE_NOT_FOUND;
|
|
|
|
if (pin != 0) {
|
|
|
|
//
|
|
// This hardware uses an interrupt.
|
|
//
|
|
// Depend on the HAL to understand how IRQ routing is
|
|
// really done.
|
|
//
|
|
|
|
reqList = PciAllocateIoRequirementsList(
|
|
1, // number of resources
|
|
PCI_PARENT_FDOX(PdoExtension)->BaseBus,
|
|
PdoExtension->Slot.u.AsULONG
|
|
);
|
|
|
|
if (reqList == NULL) {
|
|
|
|
//
|
|
// Out of system resources? Bad things are happening.
|
|
//
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
resource = reqList->List[0].Descriptors;
|
|
resource->Type = CmResourceTypeInterrupt;
|
|
resource->ShareDisposition = CmResourceShareShared;
|
|
resource->Option = 0;
|
|
resource->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
|
resource->u.Interrupt.MinimumVector = 0x00;
|
|
|
|
//
|
|
// Historically the maximum allowable interrupt vector for a PCI
|
|
// device was FF, since that is the largest value that can be written
|
|
// into the interrupt line register. However, the interrupt line
|
|
// register is largely irrelevant now, and large machines may contain
|
|
// enough interrupt controllers to kick the number of interrupt vectors
|
|
// in the machine above FF. To support devices connected to these vectors
|
|
// the maximum vector in the requirement must be as large as possible.
|
|
// For now this change is only made on the Datacenter SKU as drivers may
|
|
// rely on the interrupt line register, which is now bogus. When more
|
|
// complete testing is available, this change will be made global.
|
|
//
|
|
if (PciRunningDatacenter) {
|
|
resource->u.Interrupt.MaximumVector = MAXULONG;
|
|
} else {
|
|
resource->u.Interrupt.MaximumVector = 0xff;
|
|
}
|
|
|
|
|
|
#if defined(NO_LEGACY_DRIVERS)
|
|
*Minimum = 0;
|
|
if (PciRunningDatacenter) {
|
|
*Maximum = MAXULONG;
|
|
} else {
|
|
*Maximum = 0xFF;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
#else
|
|
status = HalAdjustResourceList(&reqList);
|
|
|
|
//
|
|
// If the HAL succeeded it will have reallocated the list.
|
|
//
|
|
resource = reqList->List[0].Descriptors;
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
PciDebugPrint(
|
|
PciDbgInformative,
|
|
" PIN %02x, HAL FAILED Interrupt Assignment, status %08x\n",
|
|
pin,
|
|
status
|
|
);
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
} else if (resource->u.Interrupt.MinimumVector >
|
|
resource->u.Interrupt.MaximumVector) {
|
|
|
|
UCHAR line;
|
|
|
|
//
|
|
// The HAL succeeded but returned an invalid range. This
|
|
// is the HALs way of telling us that, sorry, it doesn't
|
|
// know either.
|
|
//
|
|
|
|
//
|
|
// We have a bug in that we restore the interrupt line to
|
|
// config space before we power up the device and thus if
|
|
// the device is in D>0 and the interrupt line register
|
|
// isn't sticky it doesn't stick. It doesn't matter unless
|
|
// we are on a machine that doesn't support interrupt
|
|
// routing in which case we are toast. The correct fix is
|
|
// to move the restore code after we power managed the device
|
|
// but that changes things too much for Whistler Beta2 and this
|
|
// is totally rewritten for Blackcomb so, now that you know
|
|
// the right way to fix this, the hack is if the HAL fails
|
|
// the call use what we would have restored into the interrupt
|
|
// line.
|
|
//
|
|
|
|
//
|
|
// Get the current int line (this is in the same place for all header types)
|
|
//
|
|
|
|
PciReadDeviceConfig(PdoExtension,
|
|
&line,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.InterruptLine),
|
|
sizeof(line)
|
|
);
|
|
|
|
//
|
|
// If this is 0 and it was something when we first saw the device then use
|
|
// what we first saw
|
|
//
|
|
|
|
if (line == 0 && PdoExtension->RawInterruptLine != 0) {
|
|
*Minimum = *Maximum = (ULONG)PdoExtension->RawInterruptLine;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
PciDebugPrint(
|
|
PciDbgInformative,
|
|
" PIN %02x, HAL could not assign interrupt.\n",
|
|
pin
|
|
);
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
} else {
|
|
|
|
*Minimum = resource->u.Interrupt.MinimumVector;
|
|
*Maximum = resource->u.Interrupt.MaximumVector;
|
|
|
|
PciDebugPrint(
|
|
PciDbgObnoxious,
|
|
" Interrupt assigned = 0x%x through 0x%x\n",
|
|
*Minimum,
|
|
*Maximum
|
|
);
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
ExFreePool(reqList);
|
|
|
|
#endif // NO_LEGACY_DRIVERS
|
|
|
|
} else {
|
|
|
|
#if MSI_SUPPORTED
|
|
|
|
if (PdoExtension->CapableMSI) {
|
|
|
|
//
|
|
// MSI Only device - we need to return a success here so that
|
|
// this device gets resource requests passed to a (hopefully)
|
|
// MSI-aware arbiter. If the arbiter is not MSI aware, we will
|
|
// simply get extraneous/unusable resources allocated for this
|
|
// device - not to mention the fact that the device will not work.
|
|
//
|
|
// The below could be anything, they are only limited by message
|
|
// size and the available APIC ranges which only the arbiter
|
|
// knows about.
|
|
//
|
|
|
|
*Minimum = 0x00;
|
|
*Maximum = 0xFF;
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
#endif // MSI_SUPPORTED
|
|
|
|
}
|
|
return status;
|
|
}
|
|
|
|
PPCI_PDO_EXTENSION
|
|
PciFindPdoByFunction(
|
|
IN PPCI_FDO_EXTENSION FdoExtension,
|
|
IN PCI_SLOT_NUMBER Slot,
|
|
IN PPCI_COMMON_CONFIG Config
|
|
)
|
|
{
|
|
PPCI_PDO_EXTENSION pdoExtension;
|
|
KIRQL currentIrql;
|
|
|
|
//
|
|
// This can be called at >= DISPATCH_LEVEL when we scan the bus on returning
|
|
// from hibernate. Don't try to acquire the locks because (1) it'll crash
|
|
// and (2) it is guaranteed to be single threaded
|
|
//
|
|
|
|
currentIrql = KeGetCurrentIrql();
|
|
|
|
if (currentIrql < DISPATCH_LEVEL) {
|
|
ExAcquireFastMutex(&FdoExtension->ChildListMutex);
|
|
};
|
|
|
|
//
|
|
// Seach each PDO hanging off of the given FDO until we find a matching
|
|
// PCI function or fall off the end of the list.
|
|
//
|
|
|
|
for (pdoExtension = FdoExtension->ChildPdoList;
|
|
pdoExtension;
|
|
pdoExtension = pdoExtension->Next) {
|
|
|
|
if ((!pdoExtension->ReportedMissing) &&
|
|
(pdoExtension->Slot.u.bits.DeviceNumber == Slot.u.bits.DeviceNumber) &&
|
|
(pdoExtension->Slot.u.bits.FunctionNumber == Slot.u.bits.FunctionNumber)) {
|
|
|
|
//
|
|
// Check that the device in this slot hasn't changed. (as best
|
|
// we can).
|
|
//
|
|
|
|
if ( (pdoExtension->VendorId == Config->VendorID)
|
|
&& (pdoExtension->DeviceId == Config->DeviceID)
|
|
&& (pdoExtension->RevisionId == Config->RevisionID)
|
|
#if 0
|
|
//
|
|
// NTRAID #62668 - 4/25/2000
|
|
//
|
|
// These do not contribute towards the device ID itself, and
|
|
// as they are unfortunately volatile on some cards (SubClass
|
|
// changes on the ATIRage, Programming interface on IDE cards).
|
|
// Therefore a change in these fields does not mean a change in
|
|
// the presence of the card.
|
|
//
|
|
// What about the SSVID?
|
|
//
|
|
&& (pdoExtension->ProgIf == Config->ProgIf)
|
|
&& (pdoExtension->SubClass == Config->SubClass)
|
|
&& (pdoExtension->BaseClass == Config->BaseClass)
|
|
#endif
|
|
) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (currentIrql < DISPATCH_LEVEL) {
|
|
ExReleaseFastMutex(&FdoExtension->ChildListMutex);
|
|
}
|
|
|
|
return pdoExtension;
|
|
}
|
|
|
|
PPCI_FDO_EXTENSION
|
|
PciFindParentPciFdoExtension(
|
|
PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN PFAST_MUTEX Mutex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For each Parent PCI FDO, search the child Pdo lists for the supplied
|
|
PhysicalDeviceObject.
|
|
|
|
Arguments:
|
|
|
|
PhysicalDeviceObject Pdo to find.
|
|
Mutex Mutex list is protected by.
|
|
|
|
Return Value:
|
|
|
|
If Pdo is found as a child, returns a pointer to the root Fdo's
|
|
device extension, otherwise returns NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPCI_FDO_EXTENSION fdoExtension;
|
|
PPCI_PDO_EXTENSION pdoExtension;
|
|
PPCI_PDO_EXTENSION target;
|
|
PSINGLE_LIST_ENTRY nextEntry;
|
|
|
|
if (Mutex) {
|
|
ExAcquireFastMutex(Mutex);
|
|
}
|
|
|
|
target = (PPCI_PDO_EXTENSION)PhysicalDeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// For each root
|
|
//
|
|
|
|
for ( nextEntry = PciFdoExtensionListHead.Next;
|
|
nextEntry != NULL;
|
|
nextEntry = nextEntry->Next ) {
|
|
|
|
fdoExtension = CONTAINING_RECORD(nextEntry,
|
|
PCI_FDO_EXTENSION,
|
|
List);
|
|
|
|
//
|
|
// Search the child Pdo list.
|
|
//
|
|
|
|
ExAcquireFastMutex(&fdoExtension->ChildListMutex);
|
|
for ( pdoExtension = fdoExtension->ChildPdoList;
|
|
pdoExtension;
|
|
pdoExtension = pdoExtension->Next ) {
|
|
|
|
//
|
|
// Is this the one we're looking for?
|
|
//
|
|
|
|
if ( pdoExtension == target ) {
|
|
|
|
ExReleaseFastMutex(&fdoExtension->ChildListMutex);
|
|
|
|
//
|
|
// Yes, return it.
|
|
//
|
|
|
|
if (Mutex) {
|
|
ExReleaseFastMutex(Mutex);
|
|
}
|
|
|
|
return fdoExtension;
|
|
}
|
|
}
|
|
ExReleaseFastMutex(&fdoExtension->ChildListMutex);
|
|
}
|
|
|
|
//
|
|
// Did not find match.
|
|
//
|
|
if (Mutex) {
|
|
ExReleaseFastMutex(Mutex);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PCI_OBJECT_TYPE
|
|
PciClassifyDeviceType(
|
|
PPCI_PDO_EXTENSION PdoExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Examine the Configuration Header BaseClass and SubClass fields
|
|
and classify the device into a simple enumerated type.
|
|
|
|
Arguments:
|
|
|
|
PdoExtension Pointer to the Physical Device Object extension
|
|
into which the above fields have been previously
|
|
been copied from PCI config space.
|
|
|
|
Return Value:
|
|
|
|
Returns a device type from the PCI_OBJECT_TYPE enumeration.
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT_PCI_PDO_EXTENSION(PdoExtension);
|
|
|
|
switch (PdoExtension->BaseClass) {
|
|
|
|
case PCI_CLASS_BRIDGE_DEV:
|
|
|
|
//
|
|
// It's a bridge, subdivide it into the kind of bridge.
|
|
//
|
|
|
|
switch (PdoExtension->SubClass) {
|
|
|
|
case PCI_SUBCLASS_BR_HOST:
|
|
|
|
return PciTypeHostBridge;
|
|
|
|
case PCI_SUBCLASS_BR_PCI_TO_PCI:
|
|
|
|
return PciTypePciBridge;
|
|
|
|
case PCI_SUBCLASS_BR_CARDBUS:
|
|
|
|
return PciTypeCardbusBridge;
|
|
|
|
default:
|
|
|
|
//
|
|
// Anything else is just a device.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
|
|
//
|
|
// Anything else is just another device.
|
|
//
|
|
|
|
break;
|
|
}
|
|
return PciTypeDevice;
|
|
}
|
|
|
|
ULONG
|
|
PciGetLengthFromBar(
|
|
ULONG BaseAddressRegister
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given the contents of a PCI Base Address Register, after it
|
|
has been written with all ones, this routine calculates the
|
|
length (and alignment) requirement for this BAR.
|
|
|
|
This method for determining requirements is described in
|
|
section 6.2.5.1 of the PCI Specification (Rev 2.1).
|
|
|
|
NTRAID #62631 - 4/25/2000 - andrewth
|
|
The length is a power of two, given only a ULONG to
|
|
contain it, we are restricted to a maximum resource size of
|
|
2GB.
|
|
|
|
Arguments:
|
|
|
|
BaseAddressRegister contains something.
|
|
|
|
Return Value:
|
|
|
|
Returns the length of the resource requirement. This will be a number
|
|
in the range 0 thru 0x80000000.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Length;
|
|
|
|
//
|
|
// A number of least significant bits should be ignored in the
|
|
// determination of the length. These are flag bits, the number
|
|
// of bits is dependent on the type of the resource.
|
|
//
|
|
|
|
if (BaseAddressRegister & PCI_ADDRESS_IO_SPACE) {
|
|
|
|
//
|
|
// PCI IO space.
|
|
//
|
|
|
|
BaseAddressRegister &= PCI_ADDRESS_IO_ADDRESS_MASK;
|
|
|
|
} else {
|
|
|
|
//
|
|
// PCI Memory space.
|
|
//
|
|
|
|
BaseAddressRegister &= PCI_ADDRESS_MEMORY_ADDRESS_MASK;
|
|
}
|
|
|
|
//
|
|
// BaseAddressRegister now contains the maximum base address
|
|
// this device can reside at and still exist below the top of
|
|
// memory.
|
|
//
|
|
// The value 0xffffffff was written to the BAR. The device will
|
|
// have adjusted this value to the maximum it can really use.
|
|
//
|
|
// Length MUST be a power of 2.
|
|
//
|
|
// For most devices, h/w will simply have cleared bits from the
|
|
// least significant bit positions so that the address 0xffffffff
|
|
// is adjusted to accomodate the length. eg: if the new value is
|
|
// 0xffffff00, the device requires 256 bytes.
|
|
//
|
|
// The difference between the original and new values is the length (-1).
|
|
//
|
|
// For example, if the value fead back from the BAR is 0xffff0000,
|
|
// the length of this resource is
|
|
//
|
|
// 0xffffffff - 0xffff0000 + 1
|
|
// = 0x0000ffff + 1
|
|
// = 0x00010000
|
|
//
|
|
// ie 16KB.
|
|
//
|
|
// Some devices cannot reside at the top of PCI address space. These
|
|
// devices will have adjusted the value such that length bytes are
|
|
// accomodated below the highest address. For example, if a device
|
|
// must reside below 1MB, and occupies 256 bytes, the value will now
|
|
// be 0x000fff00.
|
|
//
|
|
// In the first case, length can be calculated as-
|
|
//
|
|
|
|
Length = (0xffffffff - BaseAddressRegister) + 1;
|
|
|
|
if (((Length - 1) & Length) != 0) {
|
|
|
|
//
|
|
// We didn't end up with a power of two, must be the latter
|
|
// case, we will have to scan for it.
|
|
//
|
|
|
|
Length = 4; // start with minimum possible
|
|
|
|
while ((Length | BaseAddressRegister) != BaseAddressRegister) {
|
|
|
|
//
|
|
// Length *= 2, note we will eventually drop out of this
|
|
// loop for one of two reasons (a) because we found the
|
|
// length, or (b) because Length left shifted off the end
|
|
// and became 0.
|
|
//
|
|
|
|
Length <<= 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check that we got something - if this is a 64bit bar then nothing is ok as
|
|
// we might be asking for a range >= 4GB (not that that's going to work any time soon)
|
|
//
|
|
|
|
if (!((BaseAddressRegister & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT)) {
|
|
PCI_ASSERT(Length);
|
|
}
|
|
|
|
return Length;
|
|
}
|
|
|
|
BOOLEAN
|
|
PciCreateIoDescriptorFromBarLimit(
|
|
IN PIO_RESOURCE_DESCRIPTOR Descriptor,
|
|
IN PULONG BaseAddress,
|
|
IN BOOLEAN Rom
|
|
)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Generate an IO resource descriptor to describe the settings
|
|
a Base Address Register can take.
|
|
|
|
Arguments:
|
|
|
|
Descriptor -
|
|
BaseAddress - Pointer to the value read from a base address register
|
|
immediately after writing all ones to it.
|
|
Rom - If true, this is a base address register for ROM.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if this address register was a 64 bit address register,
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG bar = *BaseAddress;
|
|
ULONG length;
|
|
ULONG addressMask;
|
|
BOOLEAN returnValue = FALSE;
|
|
|
|
//
|
|
// If the Base Address Register contains zero after being written
|
|
// with all ones, it is not implemented. Set the resource type to
|
|
// NULL, no further processing is required.
|
|
//
|
|
// Note: We ignore the I/O bit in the BAR due to HARDWARE BUGS
|
|
// in some people's hardware.
|
|
//
|
|
|
|
if ((bar & ~1) == 0) {
|
|
Descriptor->Type = CmResourceTypeNull;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Default to ordinary (32 bit) memory.
|
|
//
|
|
|
|
Descriptor->Flags = CM_RESOURCE_MEMORY_READ_WRITE;
|
|
Descriptor->u.Memory.MaximumAddress.HighPart = 0;
|
|
Descriptor->u.Memory.MinimumAddress.QuadPart = 0;
|
|
|
|
if (Rom == TRUE) {
|
|
|
|
//
|
|
// Mask out unused bits and indicate in the descriptor that
|
|
// this entry describes ROM.
|
|
//
|
|
|
|
bar &= PCI_ADDRESS_ROM_ADDRESS_MASK;
|
|
Descriptor->Flags = CM_RESOURCE_MEMORY_READ_ONLY;
|
|
}
|
|
|
|
//
|
|
// Ranges described by PCI Base Address Registers must be a
|
|
// power of 2 in length and naturally aligned. Get the length
|
|
// and set the length and alignment in the descriptor.
|
|
//
|
|
|
|
length = PciGetLengthFromBar(bar);
|
|
Descriptor->u.Generic.Length = length;
|
|
Descriptor->u.Generic.Alignment = length;
|
|
|
|
if ((bar & PCI_ADDRESS_IO_SPACE) != 0) {
|
|
|
|
//
|
|
// This BAR describes I/O space.
|
|
//
|
|
|
|
addressMask = PCI_ADDRESS_IO_ADDRESS_MASK;
|
|
Descriptor->Type = CmResourceTypePort;
|
|
Descriptor->Flags = CM_RESOURCE_PORT_IO;
|
|
|
|
} else {
|
|
|
|
//
|
|
// This BAR describes PCI memory space.
|
|
//
|
|
|
|
addressMask = PCI_ADDRESS_MEMORY_ADDRESS_MASK;
|
|
Descriptor->Type = CmResourceTypeMemory;
|
|
|
|
if ((bar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT) {
|
|
|
|
//
|
|
// This is a 64 bit PCI device. Get the high 32 bits
|
|
// from the next BAR.
|
|
//
|
|
|
|
Descriptor->u.Memory.MaximumAddress.HighPart = *(BaseAddress+1);
|
|
returnValue = TRUE;
|
|
|
|
} else if ((bar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_20BIT) {
|
|
|
|
//
|
|
// This device must locate below 1MB, the BAR shouldn't
|
|
// have any top bits set but this isn't clear from the
|
|
// spec. Enforce it by clearing the top bits.
|
|
//
|
|
|
|
addressMask &= 0x000fffff;
|
|
}
|
|
|
|
if (bar & PCI_ADDRESS_MEMORY_PREFETCHABLE) {
|
|
Descriptor->Flags |= CM_RESOURCE_MEMORY_PREFETCHABLE;
|
|
}
|
|
}
|
|
Descriptor->u.Generic.MaximumAddress.LowPart = bar & addressMask;
|
|
Descriptor->u.Generic.MaximumAddress.QuadPart += (length - 1);
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
BOOLEAN
|
|
PciOpenKey(
|
|
IN PWSTR KeyName,
|
|
IN HANDLE ParentHandle,
|
|
IN ACCESS_MASK Access,
|
|
OUT PHANDLE Handle,
|
|
OUT PNTSTATUS Status
|
|
)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Open a registry key.
|
|
|
|
Arguments:
|
|
|
|
KeyName Name of the key to be opened.
|
|
ParentHandle Pointer to the parent handle (OPTIONAL)
|
|
Handle Pointer to a handle to recieve the opened key.
|
|
|
|
Return Value:
|
|
|
|
TRUE is key successfully opened, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING nameString;
|
|
OBJECT_ATTRIBUTES nameAttributes;
|
|
NTSTATUS localStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlInitUnicodeString(&nameString, KeyName);
|
|
|
|
InitializeObjectAttributes(&nameAttributes,
|
|
&nameString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
ParentHandle,
|
|
(PSECURITY_DESCRIPTOR)NULL
|
|
);
|
|
localStatus = ZwOpenKey(Handle,
|
|
Access,
|
|
&nameAttributes
|
|
);
|
|
|
|
if (Status != NULL) {
|
|
|
|
//
|
|
// Caller wants underlying status.
|
|
//
|
|
|
|
*Status = localStatus;
|
|
}
|
|
|
|
//
|
|
// Return status converted to a boolean, TRUE if
|
|
// successful.
|
|
//
|
|
|
|
return NT_SUCCESS(localStatus);
|
|
}
|
|
|
|
NTSTATUS
|
|
PciGetRegistryValue(
|
|
IN PWSTR ValueName,
|
|
IN PWSTR KeyName,
|
|
IN HANDLE ParentHandle,
|
|
IN ULONG Type,
|
|
OUT PVOID *Buffer,
|
|
OUT PULONG Length
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE keyHandle = NULL;
|
|
ULONG neededLength;
|
|
ULONG actualLength;
|
|
UNICODE_STRING unicodeValueName;
|
|
PKEY_VALUE_PARTIAL_INFORMATION info = NULL;
|
|
|
|
if (!PciOpenKey(KeyName, ParentHandle, KEY_READ, &keyHandle, &status)) {
|
|
goto exit;
|
|
}
|
|
|
|
RtlInitUnicodeString(&unicodeValueName, ValueName);
|
|
|
|
//
|
|
// Find out how much memory we need for this.
|
|
//
|
|
|
|
status = ZwQueryValueKey(
|
|
keyHandle,
|
|
&unicodeValueName,
|
|
KeyValuePartialInformation,
|
|
NULL,
|
|
0,
|
|
&neededLength
|
|
);
|
|
|
|
if (status != STATUS_BUFFER_TOO_SMALL) {
|
|
|
|
//
|
|
// Either the value doesn't exist or something else went wrong but this
|
|
// should never succeed
|
|
//
|
|
|
|
ASSERT(!(NT_SUCCESS(status)));
|
|
|
|
goto exit;
|
|
}
|
|
|
|
ASSERT(neededLength != 0);
|
|
|
|
//
|
|
// Get memory to return the data in. Note this includes
|
|
// a header that we really don't want.
|
|
//
|
|
|
|
info = ExAllocatePool(
|
|
PagedPool | POOL_COLD_ALLOCATION,
|
|
neededLength);
|
|
|
|
if (info == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Get the data.
|
|
//
|
|
|
|
status = ZwQueryValueKey(
|
|
keyHandle,
|
|
&unicodeValueName,
|
|
KeyValuePartialInformation,
|
|
info,
|
|
neededLength,
|
|
&actualLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Make sure the data is the correcty type
|
|
//
|
|
|
|
if (info->Type != Type) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
ASSERT(neededLength == actualLength);
|
|
|
|
//
|
|
// Subtract out the header size and get memory for just
|
|
// the data we want.
|
|
//
|
|
|
|
neededLength -= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
|
|
|
|
*Buffer = ExAllocatePool(
|
|
PagedPool | POOL_COLD_ALLOCATION,
|
|
neededLength
|
|
);
|
|
|
|
if (*Buffer == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Copy data sans header.
|
|
//
|
|
|
|
RtlCopyMemory(*Buffer, info->Data, neededLength);
|
|
|
|
|
|
if (Length) {
|
|
*Length = neededLength;
|
|
}
|
|
|
|
exit:
|
|
|
|
if (keyHandle) {
|
|
ZwClose(keyHandle);
|
|
}
|
|
|
|
if (info) {
|
|
ExFreePool(info);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciGetDeviceCapabilities(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDEVICE_CAPABILITIES DeviceCapabilities
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends the get capabilities irp to the given stack
|
|
|
|
Arguments:
|
|
|
|
DeviceObject A device object in the stack whose capabilities we want
|
|
DeviceCapabilites Where to store the answer
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
IO_STATUS_BLOCK ioStatus;
|
|
KEVENT pnpEvent;
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT targetObject;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PIRP pnpIrp;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize the capabilities that we will send down
|
|
//
|
|
RtlZeroMemory( DeviceCapabilities, sizeof(DEVICE_CAPABILITIES) );
|
|
DeviceCapabilities->Size = sizeof(DEVICE_CAPABILITIES);
|
|
DeviceCapabilities->Version = 1;
|
|
DeviceCapabilities->Address = MAXULONG;
|
|
DeviceCapabilities->UINumber = MAXULONG;
|
|
|
|
//
|
|
// Initialize the event
|
|
//
|
|
KeInitializeEvent( &pnpEvent, SynchronizationEvent, FALSE );
|
|
|
|
//
|
|
// Get the irp that we will send the request to
|
|
//
|
|
targetObject = IoGetAttachedDeviceReference( DeviceObject );
|
|
|
|
//
|
|
// Build an Irp
|
|
//
|
|
pnpIrp = IoBuildSynchronousFsdRequest(
|
|
IRP_MJ_PNP,
|
|
targetObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&pnpEvent,
|
|
&ioStatus
|
|
);
|
|
if (pnpIrp == NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto PciGetDeviceCapabilitiesExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Pnp Irps all begin life as STATUS_NOT_SUPPORTED;
|
|
//
|
|
pnpIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
pnpIrp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Get the top of stack
|
|
//
|
|
irpStack = IoGetNextIrpStackLocation( pnpIrp );
|
|
if (irpStack == NULL) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto PciGetDeviceCapabilitiesExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Set the top of stack
|
|
//
|
|
RtlZeroMemory( irpStack, sizeof(IO_STACK_LOCATION ) );
|
|
irpStack->MajorFunction = IRP_MJ_PNP;
|
|
irpStack->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
|
|
irpStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;
|
|
|
|
//
|
|
// Make sure that there are no completion routines set
|
|
//
|
|
IoSetCompletionRoutine(
|
|
pnpIrp,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Call the driver
|
|
//
|
|
status = IoCallDriver( targetObject, pnpIrp );
|
|
if (status == STATUS_PENDING) {
|
|
|
|
//
|
|
// Block until the irp comes back
|
|
//
|
|
KeWaitForSingleObject(
|
|
&pnpEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
status = ioStatus.Status;
|
|
|
|
}
|
|
|
|
PciGetDeviceCapabilitiesExit:
|
|
//
|
|
// Done with reference
|
|
//
|
|
ObDereferenceObject( targetObject );
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
|
|
}
|
|
|
|
ULONGLONG
|
|
PciGetHackFlags(
|
|
IN USHORT VendorID,
|
|
IN USHORT DeviceID,
|
|
IN USHORT SubVendorID,
|
|
IN USHORT SubSystemID,
|
|
IN UCHAR RevisionID
|
|
)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Look in the registry for any flags for this VendorId/DeviceId.
|
|
|
|
Arguments:
|
|
|
|
VendorId PCI Vendor ID (16 bits) of the manufacturer of the
|
|
device.
|
|
|
|
DeviceId PCI Device ID (16 bits) of the device.
|
|
|
|
SubVendorID PCI SubVendorID representing the manufacturer of the
|
|
subsystem
|
|
|
|
SubSystemID PCI SubSystemID representing subsystem
|
|
|
|
RevisionID PCI Revision denoting the revision of the device
|
|
|
|
Return Value:
|
|
|
|
64 bit flags value or 0 if not found.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPCI_HACK_TABLE_ENTRY current;
|
|
ULONGLONG hackFlags = 0;
|
|
ULONG match, bestMatch = 0;
|
|
PCI_ASSERT(PciHackTable);
|
|
|
|
//
|
|
// We want to do a best-case match:
|
|
// VVVVDDDDSSSSssssRR
|
|
// VVVVDDDDSSSSssss
|
|
// VVVVDDDDRR
|
|
// VVVVDDDD
|
|
//
|
|
// List is currently unsorted, so keep updating current best match.
|
|
//
|
|
|
|
for (current = PciHackTable; current->VendorID != 0xFFFF; current++) {
|
|
match = 0;
|
|
|
|
//
|
|
// Must at least match vendor/dev
|
|
//
|
|
|
|
if ((current->DeviceID != DeviceID) ||
|
|
(current->VendorID != VendorID)) {
|
|
continue;
|
|
}
|
|
|
|
match = 1;
|
|
|
|
//
|
|
// If this entry specifies a revision, check that it is consistent.
|
|
//
|
|
|
|
if (current->Flags & PCI_HACK_FLAG_REVISION) {
|
|
if (current->RevisionID == RevisionID) {
|
|
match += 2;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this entry specifies subsystems, check that they are consistent
|
|
//
|
|
|
|
if (current->Flags & PCI_HACK_FLAG_SUBSYSTEM) {
|
|
if (current->SubVendorID == SubVendorID &&
|
|
current->SubSystemID == SubSystemID) {
|
|
match += 4;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (match > bestMatch) {
|
|
bestMatch = match;
|
|
hackFlags = current->HackFlags;
|
|
}
|
|
}
|
|
|
|
return hackFlags;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciGetDeviceProperty(
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN DEVICE_REGISTRY_PROPERTY DeviceProperty,
|
|
OUT PVOID *PropertyBuffer
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
NTSTATUS expected;
|
|
ULONG length;
|
|
ULONG length2;
|
|
PVOID buffer;
|
|
|
|
//
|
|
// Two passes, first pass, find out what size buffer
|
|
// is needed.
|
|
//
|
|
|
|
status = IoGetDeviceProperty(
|
|
PhysicalDeviceObject,
|
|
DeviceProperty,
|
|
0,
|
|
NULL,
|
|
&length
|
|
);
|
|
|
|
expected = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
if (status == expected) {
|
|
|
|
//
|
|
// Good, now get a buffer.
|
|
//
|
|
|
|
buffer = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, length);
|
|
|
|
if (buffer == NULL) {
|
|
|
|
PciDebugPrint(
|
|
PciDbgAlways,
|
|
"PCI - Failed to allocate DeviceProperty buffer (%d bytes).\n",
|
|
length
|
|
);
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
} else {
|
|
|
|
//
|
|
// This time, do it for real.
|
|
//
|
|
|
|
status = IoGetDeviceProperty(
|
|
PhysicalDeviceObject,
|
|
DeviceProperty,
|
|
length,
|
|
buffer,
|
|
&length2
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
PCI_ASSERT(length == length2);
|
|
|
|
//
|
|
// Return the buffer containing the requested device
|
|
// property to the caller.
|
|
//
|
|
|
|
*PropertyBuffer = buffer;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
expected = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
PciDebugPrint(
|
|
PciDbgAlways,
|
|
"PCI - Unexpected status from GetDeviceProperty, saw %08X, expected %08X.\n",
|
|
status,
|
|
expected
|
|
);
|
|
|
|
//
|
|
// Clear the caller's buffer pointer, and, if the unexpected status
|
|
// is success (from the first call to IoGetDeviceProperty) change it
|
|
// to STATUS_UNSUCCESSFUL (N.B. This is if course impossible).
|
|
//
|
|
|
|
*PropertyBuffer = NULL;
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
PCI_ASSERTMSG("PCI Successfully did the impossible!", 0);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciRangeListFromResourceList(
|
|
IN PPCI_FDO_EXTENSION FdoExtension,
|
|
IN PCM_RESOURCE_LIST ResourceList,
|
|
IN CM_RESOURCE_TYPE DesiredType,
|
|
IN BOOLEAN Complement,
|
|
IN PRTL_RANGE_LIST ResultRange
|
|
)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Generates a range list for the resources of a given type
|
|
from a resource list.
|
|
|
|
Note: This routine supports only Memory or Io resources.
|
|
|
|
Overlapping ranges in the incoming list will be combined.
|
|
|
|
Arguments:
|
|
|
|
FdoExtension Bus particulars. NOTE: This is only needed for the
|
|
gross X86 hack for A0000 due to buggy MPS BIOS
|
|
implementations. Otherwise this routine is more
|
|
generalized.
|
|
ResourceList Incoming CM Resource List.
|
|
DesiredType Type of resource to be included in the range list.
|
|
Complement Specifies wether or not the range list should be
|
|
the "complement" of the incoming data.
|
|
ResultRange Output range list.
|
|
|
|
Return Value:
|
|
|
|
TRUE is key successfully opened, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#define EXIT_IF_ERROR(status) \
|
|
if (!NT_SUCCESS(status)) { \
|
|
PCI_ASSERT(NT_SUCCESS(status)); \
|
|
goto exitPoint; \
|
|
}
|
|
|
|
#if DBG
|
|
|
|
#define ADD_RANGE(range, start, end, status) \
|
|
PciDebugPrint( \
|
|
PciDbgObnoxious, \
|
|
" Adding to RtlRange %I64x thru %I64x\n", \
|
|
(ULONGLONG)start, \
|
|
(ULONGLONG)end \
|
|
); \
|
|
status = RtlAddRange(range, start, end, 0, 0, NULL, NULL); \
|
|
if (!NT_SUCCESS(status)) { \
|
|
PCI_ASSERT(NT_SUCCESS(status)); \
|
|
goto exitPoint; \
|
|
}
|
|
|
|
#else
|
|
|
|
#define ADD_RANGE(range, start, end, status) \
|
|
status = RtlAddRange(range, start, end, 0, 0, NULL, NULL); \
|
|
if (!NT_SUCCESS(status)) { \
|
|
PCI_ASSERT(NT_SUCCESS(status)); \
|
|
goto exitPoint; \
|
|
}
|
|
|
|
#endif
|
|
|
|
NTSTATUS status;
|
|
ULONG elementCount;
|
|
ULONG count;
|
|
ULONG numlists;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR full = NULL;
|
|
PCM_PARTIAL_RESOURCE_LIST partial = NULL;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor = NULL;
|
|
|
|
typedef struct {
|
|
LIST_ENTRY list;
|
|
ULONGLONG start;
|
|
ULONGLONG end;
|
|
BOOLEAN valid;
|
|
} PCI_RANGE_LIST_ELEMENT, *PPCI_RANGE_LIST_ELEMENT;
|
|
|
|
PPCI_RANGE_LIST_ELEMENT elementBuffer;
|
|
PPCI_RANGE_LIST_ELEMENT upper;
|
|
PPCI_RANGE_LIST_ELEMENT lower = NULL;
|
|
PPCI_RANGE_LIST_ELEMENT current;
|
|
ULONG allocatedElement;
|
|
ULONGLONG start;
|
|
ULONGLONG end;
|
|
|
|
#if defined(_X86_) && defined(PCI_NT50_BETA1_HACKS)
|
|
|
|
//
|
|
// BETA1_HACKS - Remove this when the problem is fixed.
|
|
//
|
|
// HACK HACK some MPS BIOS implementations don't report the
|
|
// memory range 0xA0000 thru 0xBFFFF. They should. HACK
|
|
// them into the memory list. Gross.
|
|
// Even grosser, assume this applies only to bus 0.
|
|
//
|
|
// The 400 hack is because some cards (Matrox MGA) want access
|
|
// to the SYSTEM BIOS DATA area which is in memory at address
|
|
// 0x400 thru 0x4ff. It's not on the BUS so why are we making
|
|
// it appear here?
|
|
//
|
|
// Note, there is TWO hacks here but we do both under the
|
|
// exact same condition so we have only one boolean. If the
|
|
// two are seperated (or one removed) this needs to be split.
|
|
//
|
|
|
|
BOOLEAN doA0000Hack = (DesiredType == CmResourceTypeMemory) &&
|
|
(FdoExtension && (FdoExtension->BaseBus == 0));
|
|
|
|
#else
|
|
|
|
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
PCI_ASSERT((DesiredType == CmResourceTypeMemory) ||
|
|
(DesiredType == CmResourceTypePort));
|
|
|
|
//
|
|
// First, get a count of the number of resources of the desired
|
|
// type in the list. This gives us the maximum number of entries
|
|
// in the resulting list.
|
|
//
|
|
// Plus 1 in case we're complementing it. 2 actually, we start
|
|
// with a beginning and end entry.
|
|
//
|
|
|
|
elementCount = 2;
|
|
|
|
numlists = 0;
|
|
if (ResourceList != NULL) {
|
|
numlists = ResourceList->Count;
|
|
full = ResourceList->List;
|
|
}
|
|
|
|
while (numlists--) {
|
|
partial = &full->PartialResourceList;
|
|
count = partial->Count;
|
|
descriptor = partial->PartialDescriptors;
|
|
while (count--) {
|
|
if (descriptor->Type == DesiredType) {
|
|
if (DesiredType == CmResourceTypePort) {
|
|
if (descriptor->Flags & CM_RESOURCE_PORT_10_BIT_DECODE) {
|
|
elementCount += ((1 << 16) / (1 << 10)) - 1;
|
|
} else if (descriptor->Flags & CM_RESOURCE_PORT_12_BIT_DECODE) {
|
|
elementCount += ((1 << 16) / (1 << 12)) - 1;
|
|
}
|
|
}
|
|
elementCount++;
|
|
}
|
|
descriptor = PciNextPartialDescriptor(descriptor);
|
|
}
|
|
full = (PCM_FULL_RESOURCE_DESCRIPTOR)descriptor;
|
|
}
|
|
|
|
PciDebugPrint(
|
|
PciDbgObnoxious,
|
|
"PCI - PciRangeListFromResourceList processing %d elements.\n",
|
|
elementCount - 2
|
|
);
|
|
|
|
#if defined(_X86_) && defined(PCI_NT50_BETA1_HACKS)
|
|
|
|
if (doA0000Hack) {
|
|
elementCount += 3; // one for A0000 hack, one for 400 hack. + 1 for 70
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Allocate list entries and initialize the list.
|
|
//
|
|
|
|
elementBuffer = ExAllocatePool(
|
|
PagedPool | POOL_COLD_ALLOCATION,
|
|
elementCount * sizeof(PCI_RANGE_LIST_ELEMENT)
|
|
);
|
|
|
|
if (elementBuffer == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Take the first two entries and set them to the absolute minimum
|
|
// and absolute maximum possible values. Everything else will
|
|
// either end up between these or be combined with them.
|
|
//
|
|
// Setting the terminators this way should avoid us having to check
|
|
// for end conditions.
|
|
//
|
|
|
|
allocatedElement = 2;
|
|
current = &elementBuffer[1];
|
|
|
|
// first element (list min terminator)
|
|
|
|
elementBuffer[1].start = elementBuffer[1].end = 0;
|
|
elementBuffer[1].list.Flink = &elementBuffer[0].list;
|
|
elementBuffer[1].list.Blink = &elementBuffer[0].list;
|
|
elementBuffer[1].valid = FALSE;
|
|
|
|
// last element (list max terminator)
|
|
|
|
elementBuffer[0].start = elementBuffer[0].end = MAXULONGLONG;
|
|
elementBuffer[0].list.Flink = &elementBuffer[1].list;
|
|
elementBuffer[0].list.Blink = &elementBuffer[1].list;
|
|
elementBuffer[0].valid = FALSE;
|
|
|
|
#if defined(_X86_) && defined(PCI_NT50_BETA1_HACKS)
|
|
|
|
if (doA0000Hack) {
|
|
|
|
//
|
|
// Hack in A0000 thru FFFFF by just adding an entry for it
|
|
// to the otherwise empty list.
|
|
//
|
|
// Hack in 400 thru 4ff too.
|
|
//
|
|
|
|
PLIST_ENTRY minEntry = &elementBuffer[1].list;
|
|
PLIST_ENTRY maxEntry = &elementBuffer[0].list;
|
|
|
|
allocatedElement = 5;
|
|
|
|
elementBuffer[2].start = 0x70; // HACK Trident
|
|
elementBuffer[2].end = 0x70;
|
|
elementBuffer[2].valid = TRUE;
|
|
|
|
elementBuffer[3].start = 0x400; // HACK Matrox MGA
|
|
elementBuffer[3].end = 0x4FF;
|
|
elementBuffer[3].valid = TRUE;
|
|
|
|
elementBuffer[4].start = 0xA0000; // HACK broken MPS BIOS
|
|
elementBuffer[4].end = 0xBFFFF;
|
|
elementBuffer[4].valid = TRUE;
|
|
|
|
// set the flinks
|
|
|
|
minEntry->Flink = &elementBuffer[2].list;
|
|
elementBuffer[2].list.Flink = &elementBuffer[3].list;
|
|
elementBuffer[3].list.Flink = &elementBuffer[4].list;
|
|
elementBuffer[4].list.Flink = maxEntry;
|
|
|
|
// set the blinks
|
|
|
|
elementBuffer[2].list.Blink = minEntry;
|
|
elementBuffer[3].list.Blink = &elementBuffer[2].list;
|
|
elementBuffer[4].list.Blink = &elementBuffer[3].list;
|
|
maxEntry->Blink = &elementBuffer[4].list;
|
|
|
|
#if DBG
|
|
{
|
|
PPCI_RANGE_LIST_ELEMENT tempElement;
|
|
|
|
tempElement = CONTAINING_RECORD(
|
|
minEntry,
|
|
PCI_RANGE_LIST_ELEMENT,
|
|
list
|
|
);
|
|
|
|
PciDebugPrint(
|
|
PciDbgObnoxious,
|
|
" === PCI added default initial ranges ===\n"
|
|
);
|
|
|
|
do {
|
|
|
|
//
|
|
// Print this entry if it is valid.
|
|
//
|
|
|
|
if (tempElement->valid == TRUE) {
|
|
PciDebugPrint(
|
|
PciDbgObnoxious,
|
|
" %I64x .. %I64x\n",
|
|
tempElement->start,
|
|
tempElement->end
|
|
);
|
|
}
|
|
|
|
//
|
|
// Next entry.
|
|
//
|
|
|
|
if (tempElement->list.Flink == minEntry) {
|
|
break;
|
|
}
|
|
tempElement = CONTAINING_RECORD(
|
|
tempElement->list.Flink,
|
|
PCI_RANGE_LIST_ELEMENT,
|
|
list
|
|
);
|
|
} while (TRUE);
|
|
|
|
PciDebugPrint(
|
|
PciDbgObnoxious,
|
|
" === end added default initial ranges ===\n"
|
|
);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Starting again at the beginning of the resource list, extract
|
|
// the desired resources and insert them in our new list.
|
|
//
|
|
|
|
numlists = 0;
|
|
if (ResourceList != NULL) {
|
|
full = ResourceList->List;
|
|
numlists = ResourceList->Count;
|
|
}
|
|
|
|
while (numlists--) {
|
|
|
|
LIST_CONTEXT listContext;
|
|
|
|
PcipInitializePartialListContext(
|
|
&listContext,
|
|
&full->PartialResourceList,
|
|
DesiredType
|
|
);
|
|
|
|
while ((descriptor = PcipGetNextRangeFromList(&listContext)) != NULL) {
|
|
|
|
|
|
PCI_ASSERT(descriptor->Type == DesiredType);
|
|
|
|
//
|
|
// insert this element into the list.
|
|
//
|
|
|
|
start = (ULONGLONG)descriptor->u.Generic.Start.QuadPart;
|
|
end = start - 1 + descriptor->u.Generic.Length;
|
|
|
|
//
|
|
// First find the element to the left of this one
|
|
// (below it).
|
|
//
|
|
|
|
lower = current;
|
|
|
|
//
|
|
// Just in case we actually need to go right,...
|
|
//
|
|
|
|
while (start > lower->end) {
|
|
|
|
lower = CONTAINING_RECORD(
|
|
lower->list.Flink,
|
|
PCI_RANGE_LIST_ELEMENT,
|
|
list
|
|
);
|
|
}
|
|
|
|
//
|
|
// Search left.
|
|
//
|
|
|
|
while (start <= lower->end) {
|
|
if (start >= lower->start) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Go left.
|
|
//
|
|
|
|
lower = CONTAINING_RECORD(
|
|
lower->list.Blink,
|
|
PCI_RANGE_LIST_ELEMENT,
|
|
list
|
|
);
|
|
}
|
|
|
|
//
|
|
// Early out if the lower entry completely
|
|
// covers the new entry.
|
|
//
|
|
|
|
if ((start >= lower->start) && (end <= lower->end)) {
|
|
|
|
//
|
|
// It does, just skip it.
|
|
//
|
|
|
|
PciDebugPrint(
|
|
PciDbgObnoxious,
|
|
" -- (%I64x .. %I64x) swallows (%I64x .. %I64x)\n",
|
|
lower->start,
|
|
lower->end,
|
|
start,
|
|
end
|
|
);
|
|
|
|
current = lower;
|
|
current->valid = TRUE;
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// Then, the one above it.
|
|
//
|
|
|
|
upper = lower;
|
|
|
|
while (end > upper->start) {
|
|
if (end <= upper->end) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Go right.
|
|
//
|
|
|
|
upper = CONTAINING_RECORD(
|
|
upper->list.Flink,
|
|
PCI_RANGE_LIST_ELEMENT,
|
|
list
|
|
);
|
|
}
|
|
current = &elementBuffer[allocatedElement++];
|
|
current->start = start;
|
|
current->end = end;
|
|
current->valid = TRUE;
|
|
|
|
PciDebugPrint(
|
|
PciDbgObnoxious,
|
|
" (%I64x .. %I64x) <= (%I64x .. %I64x) <= (%I64x .. %I64x)\n",
|
|
lower->start,
|
|
lower->end,
|
|
start,
|
|
end,
|
|
upper->start,
|
|
upper->end
|
|
);
|
|
|
|
//
|
|
// We now have, the element below this one, possibly
|
|
// overlapping, the element above this one, possibly
|
|
// overlapping, and a new one.
|
|
//
|
|
// The easiest way to deal with this is to create
|
|
// the new entry, link it in, then unlink the overlaps
|
|
// if they exist.
|
|
//
|
|
//
|
|
// Note: The new entry may overlap several entries,
|
|
// these are orphaned.
|
|
//
|
|
// Link it in.
|
|
//
|
|
|
|
current->list.Flink = &upper->list;
|
|
current->list.Blink = &lower->list;
|
|
upper->list.Blink = ¤t->list;
|
|
lower->list.Flink = ¤t->list;
|
|
|
|
//
|
|
// Check for lower overlap.
|
|
//
|
|
|
|
if ((lower->valid == TRUE) && (start > 0)) {
|
|
start--;
|
|
}
|
|
|
|
if (lower->end >= start) {
|
|
|
|
//
|
|
// Overlaps from below,...
|
|
//
|
|
// Merge lower into current.
|
|
//
|
|
|
|
current->start = lower->start;
|
|
current->list.Blink = lower->list.Blink;
|
|
|
|
//
|
|
//
|
|
// lower is being orphaned, reuse it to get to
|
|
// our new lower neighbor.
|
|
//
|
|
|
|
lower = CONTAINING_RECORD(
|
|
lower->list.Blink,
|
|
PCI_RANGE_LIST_ELEMENT,
|
|
list
|
|
);
|
|
lower->list.Flink = ¤t->list;
|
|
|
|
PciDebugPrint(
|
|
PciDbgObnoxious,
|
|
" -- Overlaps lower, merged to (%I64x .. %I64x)\n",
|
|
current->start,
|
|
current->end
|
|
);
|
|
}
|
|
|
|
//
|
|
// Check for upper overlap.
|
|
//
|
|
|
|
if ((upper->valid == TRUE) && (end < MAXULONGLONG)) {
|
|
end++;
|
|
}
|
|
if ((end >= upper->start) && (current != upper)) {
|
|
|
|
//
|
|
// Overlaps above,... merge upper into current.
|
|
//
|
|
|
|
current->end = upper->end;
|
|
current->list.Flink = upper->list.Flink;
|
|
|
|
//
|
|
// upper is being orphaned, reuse it to get to
|
|
// our new upper neighbor.
|
|
//
|
|
|
|
upper = CONTAINING_RECORD(
|
|
upper->list.Flink,
|
|
PCI_RANGE_LIST_ELEMENT,
|
|
list
|
|
);
|
|
upper->list.Blink = ¤t->list;
|
|
|
|
PciDebugPrint(
|
|
PciDbgObnoxious,
|
|
" -- Overlaps upper, merged to (%I64x .. %I64x)\n",
|
|
current->start,
|
|
current->end
|
|
);
|
|
}
|
|
}
|
|
full = (PCM_FULL_RESOURCE_DESCRIPTOR)listContext.Next;
|
|
}
|
|
|
|
//
|
|
// Find the lowest value.
|
|
//
|
|
|
|
while (current->valid == TRUE) {
|
|
|
|
lower = CONTAINING_RECORD(
|
|
current->list.Blink,
|
|
PCI_RANGE_LIST_ELEMENT,
|
|
list
|
|
);
|
|
if ((lower->valid == FALSE) ||
|
|
(lower->start > current->start)) {
|
|
break;
|
|
}
|
|
current = lower;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
lower = current;
|
|
|
|
if (current->valid == FALSE) {
|
|
PciDebugPrint(
|
|
PciDbgObnoxious,
|
|
" ==== No ranges in results list. ====\n"
|
|
);
|
|
} else {
|
|
|
|
PciDebugPrint(
|
|
PciDbgObnoxious,
|
|
" === ranges ===\n"
|
|
);
|
|
|
|
do {
|
|
if (current->valid == TRUE) {
|
|
PciDebugPrint(
|
|
PciDbgObnoxious,
|
|
" %I64x .. %I64x\n",
|
|
current->start,
|
|
current->end
|
|
);
|
|
}
|
|
|
|
//
|
|
// Next entry.
|
|
//
|
|
current = CONTAINING_RECORD(
|
|
current->list.Flink,
|
|
PCI_RANGE_LIST_ELEMENT,
|
|
list
|
|
);
|
|
|
|
} while (current != lower);
|
|
}
|
|
|
|
#endif
|
|
|
|
if (Complement == TRUE) {
|
|
|
|
//
|
|
// Invert the list.
|
|
//
|
|
// The generation of the list always results in the orphaning
|
|
// of elementBuffer[1] (which was the original start point),
|
|
// we can use that one for the first element of the new
|
|
// inverted list.
|
|
//
|
|
|
|
if (current->valid == FALSE) {
|
|
|
|
//
|
|
// Empty list, complement it you get everything.
|
|
//
|
|
|
|
ADD_RANGE(ResultRange, 0, MAXULONGLONG, status);
|
|
} else {
|
|
|
|
//
|
|
// If the original range doesn't start at zero we must
|
|
// generate an entry from 0 to the start of that range.
|
|
//
|
|
|
|
if (current->start != 0) {
|
|
ADD_RANGE(ResultRange, 0, current->start - 1, status);
|
|
}
|
|
|
|
//
|
|
// Run the list greating range list entries for the
|
|
// gaps between entries in this list.
|
|
//
|
|
|
|
do {
|
|
PPCI_RANGE_LIST_ELEMENT next = CONTAINING_RECORD(
|
|
current->list.Flink,
|
|
PCI_RANGE_LIST_ELEMENT,
|
|
list
|
|
);
|
|
if (current->valid == TRUE) {
|
|
start = current->end + 1;
|
|
end = next->start - 1;
|
|
|
|
if ((end < start) || (next == elementBuffer)) {
|
|
end = MAXULONGLONG;
|
|
}
|
|
ADD_RANGE(ResultRange, start, end, status);
|
|
}
|
|
|
|
//
|
|
// Next entry.
|
|
//
|
|
current = next;
|
|
|
|
} while (current != lower);
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Not complementing,... add a range for each member of the
|
|
// list.
|
|
//
|
|
|
|
if (current->valid == TRUE) {
|
|
do {
|
|
ADD_RANGE(ResultRange, current->start, current->end, status);
|
|
|
|
//
|
|
// Next entry.
|
|
//
|
|
current = CONTAINING_RECORD(
|
|
current->list.Flink,
|
|
PCI_RANGE_LIST_ELEMENT,
|
|
list
|
|
);
|
|
|
|
} while (current != lower);
|
|
}
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
|
|
exitPoint:
|
|
|
|
ExFreePool(elementBuffer);
|
|
return status;
|
|
|
|
#undef EXIT_IF_ERROR
|
|
}
|
|
|
|
|
|
UCHAR
|
|
PciReadDeviceCapability(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN UCHAR Offset,
|
|
IN UCHAR Id,
|
|
IN OUT PVOID Buffer,
|
|
IN ULONG Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Searches configuration space for the PCI Capabilities structure
|
|
identified by Id. Begins at offset Offset in PCI config space.
|
|
|
|
Arguments:
|
|
|
|
PdoExtension Pointer to the PDO Extension for this device.
|
|
Offset Offset into PCI config space to begin traversing
|
|
the capabilities list.
|
|
Id Capabilities ID. (0 if want to match any).
|
|
Buffer Pointer to the buffer where the capabilities
|
|
structure is to be returned (includes capabilities
|
|
header).
|
|
Length Number of bytes wanted (must be at least large
|
|
enough to contain the header).
|
|
|
|
Return Value:
|
|
|
|
Returns the Offset in PCI config space at which the capability
|
|
was found or 0 if not found.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPCI_CAPABILITIES_HEADER capHeader;
|
|
UCHAR loopCount = 0;
|
|
|
|
capHeader = (PPCI_CAPABILITIES_HEADER)Buffer;
|
|
|
|
//
|
|
// In case the caller is running the list, check if we got
|
|
// handed the list end.
|
|
//
|
|
|
|
if (Offset == 0) {
|
|
return 0;
|
|
}
|
|
|
|
ASSERT_PCI_PDO_EXTENSION(PdoExtension);
|
|
|
|
PCI_ASSERT(PdoExtension->CapabilitiesPtr != 0);
|
|
|
|
PCI_ASSERT(Buffer);
|
|
|
|
PCI_ASSERT(Length >= sizeof(PCI_CAPABILITIES_HEADER));
|
|
|
|
do {
|
|
|
|
//
|
|
// Catch case where the device has been powered off. (Reads
|
|
// from a powered off device return FF,... allowing also for
|
|
// the case where the device is just broken).
|
|
//
|
|
|
|
if ((Offset < PCI_COMMON_HDR_LENGTH) ||
|
|
((Offset & 0x3) != 0)) {
|
|
PCI_ASSERT((Offset >= PCI_COMMON_HDR_LENGTH) && ((Offset & 0x3) == 0));
|
|
|
|
return 0;
|
|
}
|
|
|
|
PciReadDeviceConfig(
|
|
PdoExtension,
|
|
Buffer,
|
|
Offset,
|
|
sizeof(PCI_CAPABILITIES_HEADER)
|
|
);
|
|
|
|
//
|
|
// Check if this capability is the one we want (or if we want
|
|
// ALL capability structures).
|
|
//
|
|
// NOTE: Intel 21554 non-transparent P2P bridge has a VPD
|
|
// capability that has the Chassis capability id. Needs to be
|
|
// handled here in the future. Maybe fixed in later revisions.
|
|
//
|
|
|
|
if ((capHeader->CapabilityID == Id) || (Id == 0)) {
|
|
break;
|
|
}
|
|
|
|
Offset = capHeader->Next;
|
|
|
|
//
|
|
// One more check for broken h/w. Make sure we're not
|
|
// traversing a circular list. A Capabilities header
|
|
// cannot be in the common header and must be DWORD aligned
|
|
// in config space so there can only be (256-64)/4 of them.
|
|
//
|
|
|
|
if (++loopCount > ((256-64)/4)) {
|
|
|
|
PciDebugPrint(
|
|
PciDbgAlways,
|
|
"PCI device %p capabilities list is broken.\n",
|
|
PdoExtension
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
} while (Offset != 0);
|
|
|
|
//
|
|
// If we found a match and we haven't read all the data, get the
|
|
// remainder.
|
|
//
|
|
|
|
if ((Offset != 0) && (Length > sizeof(PCI_CAPABILITIES_HEADER))) {
|
|
|
|
if (Length > (sizeof(PCI_COMMON_CONFIG) - Offset)) {
|
|
|
|
//
|
|
// If we are too close to the end of config space to
|
|
// return the amount of data the caller requested,
|
|
// truncate.
|
|
//
|
|
// Worst case truncation will be to 4 bytes so no need
|
|
// to check we have data to read (again).
|
|
//
|
|
|
|
PCI_ASSERT(Length <= (sizeof(PCI_COMMON_CONFIG) - Offset));
|
|
|
|
Length = sizeof(PCI_COMMON_CONFIG) - Offset;
|
|
}
|
|
|
|
//
|
|
// Read remainder.
|
|
//
|
|
|
|
Length -= sizeof(PCI_CAPABILITIES_HEADER);
|
|
|
|
PciReadDeviceConfig(
|
|
PdoExtension,
|
|
capHeader + 1,
|
|
Offset + sizeof(PCI_CAPABILITIES_HEADER),
|
|
Length
|
|
);
|
|
}
|
|
return Offset;
|
|
}
|
|
|
|
BOOLEAN
|
|
PciIsCriticalDeviceClass(
|
|
IN UCHAR BaseClass,
|
|
IN UCHAR SubClass
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks to see if a given class/subclass pair identifies a
|
|
"critical" device class, that is, a class of devices that
|
|
cannot be turned off at any time during boot (not to probe
|
|
the BARs, etc) without risking a crash.
|
|
|
|
Arguments:
|
|
|
|
BaseClass - the PCI class code to check.
|
|
|
|
SubClass - the subclass within BaseClass to check.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the class/subclass pair is critical
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Interrupt controllers are critical system devices and
|
|
// must be treated very specially. They cannot be turned
|
|
// off without stopping all traffic in the system, but
|
|
// they can't be left alone either.
|
|
//
|
|
|
|
if ((BaseClass == PCI_CLASS_BASE_SYSTEM_DEV) &&
|
|
(SubClass == PCI_SUBCLASS_SYS_INTERRUPT_CTLR)) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Video cards are critical because vga writes to the
|
|
// video card during boot in parallel with PnP enumeration
|
|
// of the video card. It doesn't know if/when PnP tries
|
|
// to turn off the card for enumeration.
|
|
//
|
|
|
|
if (BaseClass == PCI_CLASS_DISPLAY_CTLR) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
PciCanDisableDecodes(
|
|
IN PPCI_PDO_EXTENSION PdoExtension OPTIONAL,
|
|
IN PPCI_COMMON_CONFIG Config OPTIONAL,
|
|
IN ULONGLONG HackFlags,
|
|
IN ULONG Flags
|
|
)
|
|
// N.B. - not paged so we can power down at dispatch level
|
|
{
|
|
UCHAR baseClass;
|
|
UCHAR subClass;
|
|
BOOLEAN canDisableVideoDecodes;
|
|
|
|
canDisableVideoDecodes = (Flags & PCI_CAN_DISABLE_VIDEO_DECODES) == PCI_CAN_DISABLE_VIDEO_DECODES;
|
|
|
|
if (ARGUMENT_PRESENT(PdoExtension)) {
|
|
PCI_ASSERT(HackFlags == 0);
|
|
HackFlags = PdoExtension->HackFlags;
|
|
baseClass = PdoExtension->BaseClass;
|
|
subClass = PdoExtension->SubClass;
|
|
} else {
|
|
PCI_ASSERT(ARGUMENT_PRESENT(Config));
|
|
baseClass = Config->BaseClass;
|
|
subClass = Config->SubClass;
|
|
}
|
|
|
|
if (HackFlags & PCI_HACK_PRESERVE_COMMAND) {
|
|
|
|
//
|
|
// Bad things happen if we touch this device's command
|
|
// register, leave it alone.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (HackFlags & PCI_HACK_CB_SHARE_CMD_BITS) {
|
|
|
|
//
|
|
// This is a multifunction cardbus controller with a shared
|
|
// command register. Never turn of any of the functions because it has
|
|
// the unfortunate side effect of turning of all of them!
|
|
//
|
|
// NTRAID #62672 - 4/25/2000 - andrewth
|
|
// We should probably ensure that the windows for all functions
|
|
// are closed on all functions before enabling any of them...
|
|
//
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (HackFlags & PCI_HACK_NO_DISABLE_DECODES) {
|
|
|
|
//
|
|
// If we disable the decodes on this device bad things happen
|
|
//
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// If this is a video device then don't allow the decodes to be disabled unless
|
|
// we are allowed to...
|
|
//
|
|
|
|
if ((baseClass == PCI_CLASS_DISPLAY_CTLR && subClass == PCI_SUBCLASS_VID_VGA_CTLR)
|
|
|| (baseClass == PCI_CLASS_PRE_20 && subClass == PCI_SUBCLASS_PRE_20_VGA)) {
|
|
|
|
return canDisableVideoDecodes;
|
|
|
|
}
|
|
|
|
//
|
|
// There are various things in the world we shouldn't turn off.
|
|
// The system is quite possibly unable to recover if we do, so
|
|
// don't (just pretend).
|
|
//
|
|
switch (baseClass) {
|
|
case PCI_CLASS_BRIDGE_DEV:
|
|
|
|
//
|
|
// Bad things happen if we turn off the HOST bridge (the
|
|
// system doesn't understand that this device, which is
|
|
// on a PCI bus, is actually the parent of that PCI bus),
|
|
// or ISA/EISA/MCA bridges under which are devices we still
|
|
// need to have working but are legacy detected so not in
|
|
// the heirachy in any way we understand.
|
|
//
|
|
|
|
if ((subClass == PCI_SUBCLASS_BR_ISA ) ||
|
|
(subClass == PCI_SUBCLASS_BR_EISA) ||
|
|
(subClass == PCI_SUBCLASS_BR_MCA) ||
|
|
(subClass == PCI_SUBCLASS_BR_HOST) ||
|
|
(subClass == PCI_SUBCLASS_BR_OTHER)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// We don't want to turn off bridges that might have the VGA card behind
|
|
// then otherwise video stops working. Seeing as we can't actually tell
|
|
// where the VGA card is use the hint that if the bridge is passing VGA
|
|
// ranges the video card is probably somewhere down there.
|
|
//
|
|
|
|
if (subClass == PCI_SUBCLASS_BR_PCI_TO_PCI
|
|
|| subClass == PCI_SUBCLASS_BR_CARDBUS) {
|
|
|
|
BOOLEAN vgaBitSet;
|
|
|
|
if (ARGUMENT_PRESENT(PdoExtension)) {
|
|
vgaBitSet = PdoExtension->Dependent.type1.VgaBitSet;
|
|
} else {
|
|
vgaBitSet = (Config->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA) != 0;
|
|
}
|
|
|
|
if (vgaBitSet) {
|
|
//
|
|
// We can disable the video path if we are powering down the machine
|
|
//
|
|
return canDisableVideoDecodes;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case PCI_CLASS_DISPLAY_CTLR:
|
|
|
|
//
|
|
// If a video driver fails to start, the device reverts back to being
|
|
// VGA if it is the VGA device. Don't disable the decodes on VGA
|
|
// devices.
|
|
//
|
|
|
|
if (subClass == PCI_SUBCLASS_VID_VGA_CTLR) {
|
|
//
|
|
// We can disable the video path if we are powering down the machine
|
|
//
|
|
return canDisableVideoDecodes;
|
|
}
|
|
break;
|
|
|
|
case PCI_CLASS_PRE_20:
|
|
|
|
//
|
|
// Same as above.
|
|
//
|
|
|
|
if (subClass == PCI_SUBCLASS_PRE_20_VGA) {
|
|
//
|
|
// We can disable the video path if we are powering down the machine
|
|
//
|
|
return canDisableVideoDecodes;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// NB - The check to see if this device is a critical device is done
|
|
// AFTER the check to see if this device is the video device. This
|
|
// is done so that video devices, which are normally critical, can
|
|
// be turned off when the caller specifies the PCI_CAN_DISABLE_VIDEO_DECODES
|
|
// flag (eg when going into a sleep state)
|
|
//
|
|
if (HackFlags & PCI_HACK_CRITICAL_DEVICE) {
|
|
|
|
//
|
|
// This device performs a critical system function,
|
|
// like processing interrupts. Turning it off is
|
|
// likely to cause the machine to crash.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
PciDecodeEnable(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN BOOLEAN Enable,
|
|
IN PUSHORT ExistingCommand OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Either sets the decodes to match the extension (misnomered Enable) or zeros
|
|
the decodes entirely.
|
|
|
|
N.B. - not paged so we can power down at dispatch level
|
|
|
|
Arguments:
|
|
|
|
PdoExtension Pointer to the PDO Extension for this device.
|
|
Enable If TRUE, decodes are set to match the extension (on or off).
|
|
If FALSE, decodes are disabled.
|
|
ExistingCommand Optional saved command to prevent a reread of the config
|
|
space command field.
|
|
|
|
Return Value:
|
|
|
|
Nothing.
|
|
|
|
--*/
|
|
{
|
|
USHORT cmd;
|
|
|
|
//
|
|
// Can we disable it if so ordered?
|
|
//
|
|
if (!Enable && !PciCanDisableDecodes(PdoExtension, NULL, 0, 0)) {
|
|
return;
|
|
}
|
|
|
|
if (PdoExtension->HackFlags & PCI_HACK_PRESERVE_COMMAND) {
|
|
|
|
//
|
|
// Bad things happen if we touch this device's command
|
|
// register, leave it alone.
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(ExistingCommand)) {
|
|
|
|
//
|
|
// The caller has supplied the current contents of the
|
|
// device's config space.
|
|
//
|
|
|
|
cmd = *ExistingCommand;
|
|
} else {
|
|
|
|
//
|
|
// Get the current command register from the device.
|
|
//
|
|
|
|
PciGetCommandRegister(PdoExtension, &cmd);
|
|
}
|
|
|
|
cmd &= ~(PCI_ENABLE_IO_SPACE |
|
|
PCI_ENABLE_MEMORY_SPACE |
|
|
PCI_ENABLE_BUS_MASTER);
|
|
|
|
if (Enable) {
|
|
|
|
//
|
|
// Set enables
|
|
//
|
|
|
|
cmd |= PdoExtension->CommandEnables & (PCI_ENABLE_IO_SPACE
|
|
| PCI_ENABLE_MEMORY_SPACE
|
|
| PCI_ENABLE_BUS_MASTER);
|
|
}
|
|
|
|
//
|
|
// Set the new command register into the device.
|
|
//
|
|
|
|
PciSetCommandRegister(PdoExtension, cmd);
|
|
}
|
|
|
|
NTSTATUS
|
|
PciExcludeRangesFromWindow(
|
|
IN ULONGLONG Start,
|
|
IN ULONGLONG End,
|
|
IN PRTL_RANGE_LIST ArbiterRanges,
|
|
IN PRTL_RANGE_LIST ExclusionRanges
|
|
)
|
|
{
|
|
|
|
NTSTATUS status;
|
|
RTL_RANGE_LIST_ITERATOR iterator;
|
|
PRTL_RANGE current;
|
|
|
|
FOR_ALL_RANGES(ExclusionRanges, &iterator, current) {
|
|
|
|
if (current->Owner == NULL
|
|
&& INTERSECT(current->Start, current->End, Start, End)) {
|
|
|
|
status = RtlAddRange(ArbiterRanges,
|
|
current->Start,
|
|
current->End,
|
|
0,
|
|
RTL_RANGE_LIST_ADD_IF_CONFLICT,
|
|
NULL,
|
|
NULL // this range is not on the bus
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
PCI_ASSERT(NT_SUCCESS(status));
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciBuildDefaultExclusionLists(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG windowBase;
|
|
|
|
PCI_ASSERT(PciIsaBitExclusionList.Count == 0);
|
|
PCI_ASSERT(PciVgaAndIsaBitExclusionList.Count == 0);
|
|
|
|
RtlInitializeRangeList(&PciIsaBitExclusionList);
|
|
RtlInitializeRangeList(&PciVgaAndIsaBitExclusionList);
|
|
|
|
|
|
for (windowBase = 0; windowBase <= 0xFFFF; windowBase += 0x400) {
|
|
|
|
//
|
|
// Add the x100-x3ff range to the ISA list
|
|
//
|
|
|
|
status = RtlAddRange(&PciIsaBitExclusionList,
|
|
windowBase + 0x100,
|
|
windowBase + 0x3FF,
|
|
0,
|
|
RTL_RANGE_LIST_ADD_IF_CONFLICT,
|
|
NULL,
|
|
NULL // this range is not on the bus
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Add the x100-x3af, x3bc-x3bf and x3e0-x3ff ranges to the VGA/ISA list
|
|
//
|
|
|
|
status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
|
|
windowBase + 0x100,
|
|
windowBase + 0x3AF,
|
|
0,
|
|
RTL_RANGE_LIST_ADD_IF_CONFLICT,
|
|
NULL,
|
|
NULL // this range is not on the bus
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
|
|
windowBase + 0x3BC,
|
|
windowBase + 0x3BF,
|
|
0,
|
|
RTL_RANGE_LIST_ADD_IF_CONFLICT,
|
|
NULL,
|
|
NULL // this range is not on the bus
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
|
|
windowBase + 0x3E0,
|
|
windowBase + 0x3FF,
|
|
0,
|
|
RTL_RANGE_LIST_ADD_IF_CONFLICT,
|
|
NULL,
|
|
NULL // this range is not on the bus
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
RtlFreeRangeList(&PciIsaBitExclusionList);
|
|
RtlFreeRangeList(&PciVgaAndIsaBitExclusionList);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciSaveBiosConfig(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN PPCI_COMMON_CONFIG Config
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This saves the original configuration of a device in the registry
|
|
|
|
Arguments:
|
|
|
|
PdoExtension Pointer to the PDO Extension for this device.
|
|
|
|
Config The config space as the BIOS initialized it
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES attributes;
|
|
UNICODE_STRING unicodeString;
|
|
HANDLE deviceHandle, configHandle;
|
|
WCHAR buffer[sizeof("DEV_xx&FUN_xx")];
|
|
|
|
PAGED_CODE();
|
|
|
|
status = IoOpenDeviceRegistryKey(PCI_PARENT_PDO(PdoExtension),
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
KEY_WRITE,
|
|
&deviceHandle
|
|
);
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
PciConstStringToUnicodeString(&unicodeString, BIOS_CONFIG_KEY_NAME);
|
|
|
|
InitializeObjectAttributes(&attributes,
|
|
&unicodeString,
|
|
OBJ_KERNEL_HANDLE,
|
|
deviceHandle,
|
|
NULL
|
|
);
|
|
|
|
status = ZwCreateKey(&configHandle,
|
|
KEY_WRITE,
|
|
&attributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_VOLATILE,
|
|
NULL
|
|
);
|
|
|
|
ZwClose(deviceHandle);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (FAILED(StringCbPrintfW(buffer,
|
|
sizeof(buffer),
|
|
L"DEV_%02x&FUN_%02x",
|
|
PdoExtension->Slot.u.bits.DeviceNumber,
|
|
PdoExtension->Slot.u.bits.FunctionNumber
|
|
))) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
RtlInitUnicodeString(&unicodeString, buffer);
|
|
|
|
status = ZwSetValueKey(configHandle,
|
|
&unicodeString,
|
|
0,
|
|
REG_BINARY,
|
|
Config,
|
|
PCI_COMMON_HDR_LENGTH
|
|
);
|
|
|
|
ZwClose(configHandle);
|
|
|
|
return status;
|
|
|
|
cleanup:
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciGetBiosConfig(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN PPCI_COMMON_CONFIG Config
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This retrieves the original configuration of a device from the registry
|
|
|
|
Arguments:
|
|
|
|
PdoExtension Pointer to the PDO Extension for this device.
|
|
|
|
Config The config space as the BIOS initialized it
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES attributes;
|
|
UNICODE_STRING unicodeString;
|
|
HANDLE deviceHandle, configHandle;
|
|
WCHAR buffer[sizeof("DEV_xx&FUN_xx")];
|
|
CHAR returnBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + PCI_COMMON_HDR_LENGTH - 1];
|
|
PKEY_VALUE_PARTIAL_INFORMATION info;
|
|
ULONG resultLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = IoOpenDeviceRegistryKey(PCI_PARENT_PDO(PdoExtension),
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
KEY_READ | KEY_WRITE,
|
|
&deviceHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
PciConstStringToUnicodeString(&unicodeString, BIOS_CONFIG_KEY_NAME);
|
|
|
|
InitializeObjectAttributes(&attributes,
|
|
&unicodeString,
|
|
OBJ_KERNEL_HANDLE,
|
|
deviceHandle,
|
|
NULL
|
|
);
|
|
|
|
status = ZwOpenKey(&configHandle,
|
|
KEY_READ,
|
|
&attributes
|
|
);
|
|
|
|
ZwClose(deviceHandle);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (FAILED(StringCbPrintfW(buffer,
|
|
sizeof(buffer),
|
|
L"DEV_%02x&FUN_%02x",
|
|
PdoExtension->Slot.u.bits.DeviceNumber,
|
|
PdoExtension->Slot.u.bits.FunctionNumber
|
|
))) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlInitUnicodeString(&unicodeString, buffer);
|
|
|
|
status = ZwQueryValueKey(configHandle,
|
|
&unicodeString,
|
|
KeyValuePartialInformation,
|
|
returnBuffer,
|
|
sizeof(returnBuffer),
|
|
&resultLength
|
|
);
|
|
|
|
ZwClose(configHandle);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
info = (PKEY_VALUE_PARTIAL_INFORMATION) returnBuffer;
|
|
|
|
PCI_ASSERT(info->DataLength == PCI_COMMON_HDR_LENGTH);
|
|
|
|
RtlCopyMemory(Config, info->Data, PCI_COMMON_HDR_LENGTH);
|
|
}
|
|
|
|
return status;
|
|
|
|
cleanup:
|
|
|
|
return status;
|
|
}
|
|
|
|
#if 0
|
|
BOOLEAN
|
|
PciPresenceCheck(
|
|
IN PPCI_PDO_EXTENSION PdoExtension
|
|
)
|
|
{
|
|
UCHAR configSpaceBuffer[PCI_COMMON_HDR_LENGTH];
|
|
PPCI_COMMON_CONFIG cardConfig = (PPCI_COMMON_CONFIG) configSpaceBuffer;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If the card is already missing, don't bother reexamining it.
|
|
//
|
|
if (PdoExtension->NotPresent) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (PciIsSameDevice(PdoExtension)) {
|
|
|
|
//
|
|
// Still here.
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Mark it not present, then tell the OS it's gone.
|
|
//
|
|
PdoExtension->NotPresent = 1;
|
|
|
|
IoInvalidateDeviceState(PdoExtension->PhysicalDeviceObject);
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
BOOLEAN
|
|
PciStringToUSHORT(
|
|
IN PWCHAR String,
|
|
OUT PUSHORT Result
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Takes a 4 character hexidecimal sting and converts it into a USHORT.
|
|
|
|
Arguments:
|
|
|
|
String - the string
|
|
|
|
Result - the USHORT
|
|
|
|
Return Value:
|
|
|
|
TRUE is success, FASLE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG count;
|
|
USHORT number = 0;
|
|
PWCHAR current;
|
|
|
|
current = String;
|
|
|
|
for (count = 0; count < 4; count++) {
|
|
|
|
number <<= 4;
|
|
|
|
if (*current >= L'0' && *current <= L'9') {
|
|
number |= *current - L'0';
|
|
} else if (*current >= L'A' && *current <= L'F') {
|
|
number |= *current + 10 - L'A';
|
|
} else if (*current >= L'a' && *current <= L'f') {
|
|
number |= *current + 10 - L'a';
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
current++;
|
|
}
|
|
|
|
*Result = number;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PciSendIoctl(
|
|
IN PDEVICE_OBJECT Device,
|
|
IN ULONG IoctlCode,
|
|
IN PVOID InputBuffer OPTIONAL,
|
|
IN ULONG InputBufferLength,
|
|
IN PVOID OutputBuffer OPTIONAL,
|
|
IN ULONG OutputBufferLength
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Builds and send an IOCTL to a device and return the results
|
|
|
|
Arguments:
|
|
|
|
Device - a device on the device stack to receive the IOCTL - the
|
|
irp is always sent to the top of the stack
|
|
|
|
IoctlCode - the IOCTL to run
|
|
|
|
InputBuffer - arguments to the IOCTL
|
|
|
|
InputBufferLength - length in bytes of the InputBuffer
|
|
|
|
OutputBuffer - data returned by the IOCTL
|
|
|
|
OnputBufferLength - the size in bytes of the OutputBuffer
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
PDEVICE_OBJECT targetDevice = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
|
|
|
|
//
|
|
// Get the top of the stack to send the IRP to
|
|
//
|
|
|
|
targetDevice = IoGetAttachedDeviceReference(Device);
|
|
|
|
if (!targetDevice) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Get Io to build the IRP for us
|
|
//
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IoctlCode,
|
|
targetDevice,
|
|
InputBuffer,
|
|
InputBufferLength,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
FALSE, // InternalDeviceIoControl
|
|
&event,
|
|
&ioStatus
|
|
);
|
|
|
|
|
|
if (!irp) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Send the IRP and wait for it to complete
|
|
//
|
|
|
|
status = IoCallDriver(targetDevice, irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
exit:
|
|
|
|
if (targetDevice) {
|
|
ObDereferenceObject(targetDevice);
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
PciIsOnVGAPath(
|
|
IN PPCI_PDO_EXTENSION Pdo
|
|
)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Guesses if we are on the VGA path or not!
|
|
|
|
Arguments:
|
|
|
|
Pdo - The PDO for the device in question
|
|
|
|
Return Value:
|
|
|
|
TRUE if we are on the VGA path, TRUE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
switch (Pdo->BaseClass) {
|
|
|
|
case PCI_CLASS_BRIDGE_DEV:
|
|
//
|
|
// We don't want to turn off bridges that might have the VGA card behind
|
|
// then otherwise video stops working. Seeing as we can't actually tell
|
|
// where the VGA card is use the hint that if the bridge is passing VGA
|
|
// ranges the video card is probably somewhere down there.
|
|
//
|
|
|
|
if (Pdo->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI
|
|
|| Pdo->SubClass == PCI_SUBCLASS_BR_CARDBUS) {
|
|
|
|
if (Pdo->Dependent.type1.VgaBitSet) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case PCI_CLASS_DISPLAY_CTLR:
|
|
|
|
if (Pdo->SubClass == PCI_SUBCLASS_VID_VGA_CTLR) {
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case PCI_CLASS_PRE_20:
|
|
|
|
if (Pdo->SubClass == PCI_SUBCLASS_PRE_20_VGA) {
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
PciIsSlotPresentInParentMethod(
|
|
IN PPCI_PDO_EXTENSION Pdo,
|
|
IN ULONG Method
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This function checks if the slot this device is in is present in a
|
|
Method named package on the parent of this device.
|
|
|
|
Arguments:
|
|
|
|
Pdo - The PDO extension for the device
|
|
|
|
Method - The Parents method to examine
|
|
|
|
Return Value:
|
|
|
|
TRUE if present, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
ACPI_EVAL_INPUT_BUFFER input;
|
|
PACPI_EVAL_OUTPUT_BUFFER output = NULL;
|
|
ULONG count, adr;
|
|
PACPI_METHOD_ARGUMENT argument;
|
|
BOOLEAN result = FALSE;
|
|
//
|
|
// Allocate a buffer big enough for all possible slots
|
|
//
|
|
ULONG outputSize = sizeof(ACPI_EVAL_OUTPUT_BUFFER) + sizeof(ACPI_METHOD_ARGUMENT) * (PCI_MAX_DEVICES * PCI_MAX_FUNCTION);
|
|
|
|
PAGED_CODE();
|
|
|
|
output = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, outputSize);
|
|
|
|
if (!output) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
RtlZeroMemory(&input, sizeof(ACPI_EVAL_INPUT_BUFFER));
|
|
RtlZeroMemory(output, outputSize);
|
|
|
|
//
|
|
// Send a IOCTL to ACPI to request it to run the method on this device's
|
|
// parent if the method it is present
|
|
//
|
|
|
|
input.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
|
|
input.MethodNameAsUlong = Method;
|
|
|
|
status = PciSendIoctl(PCI_PARENT_FDOX(Pdo)->PhysicalDeviceObject,
|
|
IOCTL_ACPI_EVAL_METHOD,
|
|
&input,
|
|
sizeof(ACPI_EVAL_INPUT_BUFFER),
|
|
output,
|
|
outputSize
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Format my slot number as an _ADR style integer
|
|
//
|
|
|
|
adr = (Pdo->Slot.u.bits.DeviceNumber << 16) | Pdo->Slot.u.bits.FunctionNumber;
|
|
|
|
for (count = 0; count < output->Count; count++) {
|
|
|
|
//
|
|
// Walking the arguments works like this because we are a package of
|
|
// integers
|
|
//
|
|
|
|
argument = &output->Argument[count];
|
|
|
|
if (argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (argument->Argument == adr) {
|
|
//
|
|
// Jackpot!
|
|
//
|
|
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
|
|
if (output) {
|
|
ExFreePool(output);
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
PciIsDeviceOnDebugPath(
|
|
IN PPCI_PDO_EXTENSION Pdo
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This function checks if device is on the path to the debugging device
|
|
|
|
NOTE: PDO is only partially initialized at this point. Take care to insure
|
|
that fields touched here are valid.
|
|
|
|
Arguments:
|
|
|
|
Pdo - The PDO extension for the device
|
|
|
|
Return Value:
|
|
|
|
TRUE if on the debug path, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PPCI_DEBUG_PORT current;
|
|
PCI_COMMON_HEADER header;
|
|
PPCI_COMMON_CONFIG config = (PPCI_COMMON_CONFIG) &header;
|
|
|
|
PAGED_CODE();
|
|
|
|
PCI_ASSERT(PciDebugPortsCount <= MAX_DEBUGGING_DEVICES_SUPPORTED);
|
|
|
|
//
|
|
// We can't be on the debug path if we aren't using a PCI debug port!
|
|
//
|
|
if (PciDebugPortsCount == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(&header, sizeof(header));
|
|
|
|
//
|
|
// If its a bridge check if one of its subordinate buses has the debugger
|
|
// port on it
|
|
//
|
|
|
|
if (Pdo->HeaderType == PCI_BRIDGE_TYPE
|
|
|| Pdo->HeaderType == PCI_CARDBUS_BRIDGE_TYPE) {
|
|
|
|
//
|
|
// Use the configuration that the firmware left the device in
|
|
//
|
|
|
|
status = PciGetBiosConfig(Pdo, config);
|
|
|
|
PCI_ASSERT(NT_SUCCESS(status));
|
|
|
|
FOR_ALL_IN_ARRAY(PciDebugPorts, PciDebugPortsCount, current) {
|
|
|
|
if (current->Bus >= config->u.type1.SecondaryBus
|
|
&& current->Bus <= config->u.type1.SubordinateBus
|
|
&& config->u.type1.SecondaryBus != 0
|
|
&& config->u.type1.SubordinateBus != 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
UCHAR parentBus;
|
|
|
|
if (PCI_PDO_ON_ROOT(Pdo)) {
|
|
|
|
parentBus = PCI_PARENT_FDOX(Pdo)->BaseBus;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Get the BIOS config of the parent so we can get its initial bus
|
|
// number
|
|
//
|
|
|
|
status = PciGetBiosConfig(PCI_BRIDGE_PDO(PCI_PARENT_FDOX(Pdo)),
|
|
config
|
|
);
|
|
|
|
PCI_ASSERT(NT_SUCCESS(status));
|
|
|
|
if (config->u.type1.SecondaryBus == 0
|
|
|| config->u.type1.SubordinateBus == 0) {
|
|
//
|
|
// This is a bridge that wasn't configured by the firmware so this
|
|
// child can't be on the debug path.
|
|
//
|
|
return FALSE;
|
|
|
|
} else {
|
|
|
|
parentBus = config->u.type1.SecondaryBus;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Check if we are the device on the correct bus in the correct slot
|
|
//
|
|
|
|
FOR_ALL_IN_ARRAY(PciDebugPorts, PciDebugPortsCount, current) {
|
|
|
|
|
|
if (current->Bus == parentBus
|
|
&& current->Slot.u.AsULONG == Pdo->Slot.u.AsULONG) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciUpdateLegacyHardwareDescription(
|
|
IN PPCI_FDO_EXTENSION Fdo
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE multifunctionHandle = NULL, indexHandle = NULL;
|
|
WCHAR indexStringBuffer[10];
|
|
UNICODE_STRING indexString, tempString, pciString;
|
|
OBJECT_ATTRIBUTES attributes;
|
|
UCHAR infoBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 50];
|
|
PKEY_VALUE_PARTIAL_INFORMATION info = (PKEY_VALUE_PARTIAL_INFORMATION) infoBuffer;
|
|
ULONG infoLength;
|
|
ULONG disposition;
|
|
CM_FULL_RESOURCE_DESCRIPTOR descriptor;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR full;
|
|
CONFIGURATION_COMPONENT component;
|
|
ULONG index;
|
|
BOOLEAN createdNewKey = FALSE;
|
|
|
|
if (!PciOpenKey(L"\\REGISTRY\\MACHINE\\HARDWARE\\DESCRIPTION\\System\\MultifunctionAdapter",
|
|
NULL,
|
|
KEY_READ | KEY_WRITE,
|
|
&multifunctionHandle,
|
|
&status)) {
|
|
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// HKML\Hardware\Description\System\MultifunctionAdapter is structured as
|
|
// a set of 0 base consecutive numbered keys.
|
|
// Run through all the subkeys and check that we haven't already reported
|
|
// this bus
|
|
//
|
|
|
|
RtlInitEmptyUnicodeString(&indexString,
|
|
indexStringBuffer,
|
|
sizeof(indexStringBuffer)
|
|
);
|
|
|
|
for (index = 0;;index++) {
|
|
|
|
status = RtlIntegerToUnicodeString(index, 10, &indexString);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
InitializeObjectAttributes(&attributes,
|
|
&indexString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
multifunctionHandle,
|
|
NULL
|
|
);
|
|
|
|
status = ZwCreateKey(&indexHandle,
|
|
KEY_READ | KEY_WRITE,
|
|
&attributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_VOLATILE,
|
|
&disposition
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// As the keys are all consecutive then if we created this key we have
|
|
// enumerated then all and we can get on with registering out data
|
|
//
|
|
|
|
if (disposition == REG_CREATED_NEW_KEY) {
|
|
createdNewKey = TRUE;
|
|
break;
|
|
}
|
|
|
|
PciConstStringToUnicodeString(&tempString, L"Identifier");
|
|
|
|
status = ZwQueryValueKey(indexHandle,
|
|
&tempString,
|
|
KeyValuePartialInformation,
|
|
info,
|
|
sizeof(infoBuffer),
|
|
&infoLength
|
|
);
|
|
|
|
if (NT_SUCCESS(status) && info->Type == REG_SZ) {
|
|
|
|
//
|
|
// Build counted strings of the REG_SZ which we assume has been
|
|
// NULL terminated and then compare knowing we can't overrun
|
|
// as everything is counted. If the string is longer than MAXUSHORT
|
|
// we truncate it.
|
|
//
|
|
|
|
PciConstStringToUnicodeString(&pciString, L"PCI");
|
|
|
|
tempString.Buffer = (PWSTR)&info->Data;
|
|
tempString.MaximumLength = (USHORT)info->DataLength;
|
|
tempString.Length = tempString.MaximumLength - sizeof(UNICODE_NULL);
|
|
|
|
if (RtlEqualUnicodeString(&pciString, &tempString, FALSE)) {
|
|
//
|
|
// This is a PCI bus, now check if its our bus number
|
|
//
|
|
|
|
PciConstStringToUnicodeString(&tempString, L"Configuration Data");
|
|
|
|
status = ZwQueryValueKey(indexHandle,
|
|
&tempString,
|
|
KeyValuePartialInformation,
|
|
info,
|
|
sizeof(infoBuffer),
|
|
&infoLength
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
if (info->Type == REG_FULL_RESOURCE_DESCRIPTOR) {
|
|
|
|
full = (PCM_FULL_RESOURCE_DESCRIPTOR) &info->Data;
|
|
|
|
PCI_ASSERT(full->InterfaceType == PCIBus);
|
|
|
|
if (full->BusNumber == Fdo->BaseBus) {
|
|
|
|
//
|
|
// We're already reported this so we don't need to
|
|
// do anything.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// indexHandle will be closed by the exit path.
|
|
//
|
|
goto exit;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ZwClose(indexHandle);
|
|
indexHandle = NULL;
|
|
}
|
|
|
|
//
|
|
// if we created a new key then indexHandle is it
|
|
//
|
|
|
|
if (createdNewKey) {
|
|
|
|
//
|
|
// Fill in the Identifier entry. This is a PCI bus.
|
|
//
|
|
|
|
PciConstStringToUnicodeString(&tempString, L"Identifier");
|
|
|
|
status = ZwSetValueKey(indexHandle,
|
|
&tempString,
|
|
0,
|
|
REG_SZ,
|
|
L"PCI",
|
|
sizeof(L"PCI")
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Fill in the Configuration Data entry.
|
|
//
|
|
// Note that the complete descriptor is not written to the registry just
|
|
// enough data to indicate that this is an empty list (the first 16 bytes).
|
|
// This is a bit gross but it is what happens on x86 machines today and
|
|
// after all we're only doing this for backward compatibility.
|
|
//
|
|
|
|
RtlZeroMemory(&descriptor, sizeof(CM_FULL_RESOURCE_DESCRIPTOR));
|
|
|
|
descriptor.InterfaceType = PCIBus;
|
|
descriptor.BusNumber = Fdo->BaseBus;
|
|
|
|
PciConstStringToUnicodeString(&tempString, L"Configuration Data");
|
|
|
|
status = ZwSetValueKey(indexHandle,
|
|
&tempString,
|
|
0,
|
|
REG_FULL_RESOURCE_DESCRIPTOR,
|
|
&descriptor,
|
|
16
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
|
|
//
|
|
// Fill in the Component Information entry. This is the Flags, Revision, Version,
|
|
// Key and AffinityMask members from the CONFIGURATION_COMPONENT structure.
|
|
//
|
|
// For PCI buses the affinity is set to all processors (0xFFFFFFFF) and
|
|
// everything else is 0.
|
|
//
|
|
|
|
RtlZeroMemory(&component, sizeof(CONFIGURATION_COMPONENT));
|
|
|
|
component.AffinityMask = 0xFFFFFFFF;
|
|
|
|
PciConstStringToUnicodeString(&tempString, L"Component Information");
|
|
|
|
status = ZwSetValueKey(indexHandle,
|
|
&tempString,
|
|
0,
|
|
REG_BINARY,
|
|
&component.Flags,
|
|
FIELD_OFFSET(CONFIGURATION_COMPONENT, ConfigurationDataLength) -
|
|
FIELD_OFFSET(CONFIGURATION_COMPONENT, Flags)
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
exit:
|
|
|
|
if (indexHandle) {
|
|
|
|
//
|
|
// If we are failing attempt to cleanup by deleting the key we tried
|
|
// to create.
|
|
//
|
|
if (!NT_SUCCESS(status) && createdNewKey) {
|
|
ZwDeleteKey(indexHandle);
|
|
}
|
|
|
|
ZwClose(indexHandle);
|
|
}
|
|
|
|
|
|
if (multifunctionHandle) {
|
|
ZwClose(multifunctionHandle);
|
|
}
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
PciReadDeviceSpace(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN ULONG WhichSpace,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length,
|
|
OUT PULONG LengthRead
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles reading from PCI device spaces and is called for both
|
|
the IRP_MN_READ_CONFIG and the BUS_INTERFACE_STANDARD.GetBusData cases.
|
|
|
|
Arguments:
|
|
|
|
PdoExtension - the PDO for the device we want to read from
|
|
|
|
WhichSpace - what type of space we want to read - of the form PCI_WHICHSPACE_*
|
|
|
|
Buffer - Supplies a pointer to where the data is to be returned
|
|
|
|
Offset - Indicates the offset into the space where the reading should begin.
|
|
|
|
Length - Indicates the count of bytes which should be read.
|
|
|
|
LengthRead - Indicates the count of bytes which was actually read.
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
|
|
{
|
|
// NOT PAGED
|
|
|
|
NTSTATUS status;
|
|
PVERIFIER_DATA verifierData;
|
|
|
|
*LengthRead = 0;
|
|
|
|
switch (WhichSpace) {
|
|
|
|
default:
|
|
|
|
//
|
|
// Many people hand in the wrong WhichSpace parameters slap them around if we are verifing...
|
|
//
|
|
|
|
verifierData = PciVerifierRetrieveFailureData(PCI_VERIFIER_INVALID_WHICHSPACE);
|
|
|
|
PCI_ASSERT(verifierData);
|
|
|
|
VfFailDeviceNode(
|
|
PdoExtension->PhysicalDeviceObject,
|
|
PCI_VERIFIER_DETECTED_VIOLATION,
|
|
PCI_VERIFIER_INVALID_WHICHSPACE,
|
|
verifierData->FailureClass,
|
|
&verifierData->Flags,
|
|
verifierData->FailureText,
|
|
"%DevObj%Ulong",
|
|
PdoExtension->PhysicalDeviceObject,
|
|
WhichSpace
|
|
);
|
|
|
|
// fall through
|
|
|
|
case PCI_WHICHSPACE_CONFIG:
|
|
|
|
status = PciExternalReadDeviceConfig(
|
|
PdoExtension,
|
|
Buffer,
|
|
Offset,
|
|
Length
|
|
);
|
|
|
|
if(NT_SUCCESS(status)){
|
|
*LengthRead = Length;
|
|
}
|
|
|
|
break;
|
|
|
|
case PCI_WHICHSPACE_ROM:
|
|
|
|
//
|
|
// Read ROM.
|
|
//
|
|
|
|
*LengthRead = Length;
|
|
|
|
status = PciReadRomImage(
|
|
PdoExtension,
|
|
WhichSpace,
|
|
Buffer,
|
|
Offset,
|
|
LengthRead
|
|
);
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PciWriteDeviceSpace(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN ULONG WhichSpace,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length,
|
|
OUT PULONG LengthWritten
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles reading from PCI device spaces and is called for both
|
|
the IRP_MN_WRITE_CONFIG and the BUS_INTERFACE_STANDARD.SetBusData cases.
|
|
|
|
Arguments:
|
|
|
|
PdoExtension - the PDO for the device we want to write to
|
|
|
|
WhichSpace - what type of space we want to write - of the form PCI_WHICHSPACE_*
|
|
|
|
Buffer - Supplies a pointer to where the data is to be written resides
|
|
|
|
Offset - Indicates the offset into the space where the writing should begin.
|
|
|
|
Length - Indicates the count of bytes which should be written.
|
|
|
|
LengthWritten - Indicates the count of bytes which was actually written.
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PVERIFIER_DATA verifierData;
|
|
|
|
*LengthWritten = 0;
|
|
|
|
switch (WhichSpace) {
|
|
|
|
default:
|
|
|
|
//
|
|
// Many people hand in the wrong WhichSpace parameters slap them around if we are verifing...
|
|
//
|
|
|
|
verifierData = PciVerifierRetrieveFailureData(PCI_VERIFIER_INVALID_WHICHSPACE);
|
|
|
|
PCI_ASSERT(verifierData);
|
|
|
|
VfFailDeviceNode(
|
|
PdoExtension->PhysicalDeviceObject,
|
|
PCI_VERIFIER_DETECTED_VIOLATION,
|
|
PCI_VERIFIER_INVALID_WHICHSPACE,
|
|
verifierData->FailureClass,
|
|
&verifierData->Flags,
|
|
verifierData->FailureText,
|
|
"%DevObj%Ulong",
|
|
PdoExtension->PhysicalDeviceObject,
|
|
WhichSpace
|
|
);
|
|
|
|
// fall through
|
|
|
|
case PCI_WHICHSPACE_CONFIG:
|
|
|
|
status = PciExternalWriteDeviceConfig(
|
|
PdoExtension,
|
|
Buffer,
|
|
Offset,
|
|
Length
|
|
);
|
|
|
|
if( NT_SUCCESS(status)){
|
|
*LengthWritten = Length;
|
|
}
|
|
|
|
break;
|
|
|
|
case PCI_WHICHSPACE_ROM:
|
|
|
|
//
|
|
// You can't write ROM
|
|
//
|
|
|
|
PciDebugPrint(
|
|
PciDbgAlways,
|
|
"PCI (%08x) WRITE_CONFIG IRP for ROM, failing.\n",
|
|
PdoExtension
|
|
);
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
*LengthWritten = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
PciIsSuiteVersion(
|
|
IN USHORT Version
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if the system is currently running the given
|
|
product suite.
|
|
|
|
Arguments:
|
|
|
|
Version - a USHORT representing the suite to check for.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the currently running system matches the suite.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
OSVERSIONINFOEXW versionInfo;
|
|
ULONGLONG conditionMask = 0;
|
|
|
|
RtlZeroMemory(&versionInfo,sizeof(OSVERSIONINFOEX));
|
|
versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
versionInfo.wSuiteMask = Version;
|
|
|
|
VER_SET_CONDITION(conditionMask, VER_SUITENAME, VER_AND);
|
|
|
|
return NT_SUCCESS(RtlVerifyVersionInfo(&versionInfo, VER_SUITENAME, conditionMask));
|
|
}
|
|
|
|
BOOLEAN
|
|
PciIsDatacenter(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if the system is currently running the datacenter SKU.
|
|
PciIsSuiteVersion is the supported system API to do this, but it does not work
|
|
in text mode setup, when only a value under the setupdd registry key contains
|
|
the information. The setupdd key only exists in text mode setup, so if it doesn't
|
|
exist, the normal API is used.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the system is currently running datacenter.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
PVOID valueBuffer = NULL;
|
|
ULONG valueLength = 0;
|
|
ULONG suiteVersion;
|
|
BOOLEAN returnValue = FALSE;
|
|
|
|
//
|
|
// Look for an unnamed value under CCS\Services\setupdd
|
|
// If it exists, we are in text mode setup and need to get the information out of this
|
|
// unnamed value.
|
|
//
|
|
if (NT_SUCCESS(PciGetRegistryValue(L"",
|
|
L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\setupdd",
|
|
NULL,
|
|
REG_BINARY,
|
|
&valueBuffer,
|
|
&valueLength
|
|
))) {
|
|
if (valueLength == 16) {
|
|
|
|
//
|
|
// The unnamed value is 4 DWORDs long, and the fourth is the Suite version.
|
|
// 130 is the encoding for Datacenter.
|
|
//
|
|
suiteVersion = *((PULONG)valueBuffer + 3);
|
|
if (suiteVersion == 130) {
|
|
returnValue = TRUE;
|
|
}
|
|
}
|
|
|
|
ExFreePool(valueBuffer);
|
|
return returnValue;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Not in text mode setup. The normal APIs will work.
|
|
//
|
|
return PciIsSuiteVersion(VER_SUITE_DATACENTER);
|
|
}
|
|
}
|
|
|
|
ULONG_PTR
|
|
PciExecuteCriticalSystemRoutine(
|
|
IN ULONG_PTR Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called in the context of KeIpiGenericCall, which
|
|
executes it on all processors. It is used to execute
|
|
a critical routine which needs all processors synchronized, such
|
|
as probing the BARs of a device that could not otherwise be turned off.
|
|
Only one context parameter is allowed in this routine, so it must
|
|
contain both the routine to execute and any context that routine
|
|
requires.
|
|
|
|
When this routine is entered, it is guaranteed that all processors will
|
|
already have been targeted with an IPI, and will be running at IPI_LEVEL.
|
|
All processors will either be running this routine, or will be about to
|
|
enter the routine. No arbitrary threads can possibly be running. No
|
|
devices can interrupt the execution of this routine, since IPI_LEVEL is
|
|
above all device IRQLs.
|
|
|
|
Because this routine runs at IPI_LEVEL, no debug prints, asserts or other
|
|
debugging can occur in this function without hanging MP machines.
|
|
|
|
Arguments:
|
|
|
|
Context - the context passed into the call to KeIpiGenericCall.
|
|
It contains the critical routine to execute, any context required
|
|
in that routine and a gate and a barrier to ensure that the critical routine
|
|
is executed on only one processor, even though this function is
|
|
executed on all processors.
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
PPCI_CRITICAL_ROUTINE_CONTEXT routineContext = (PPCI_CRITICAL_ROUTINE_CONTEXT)Context;
|
|
|
|
//
|
|
// The Gate parameter in the routineContext is preinitialized
|
|
// to 1, meaning that the first processor to reach this point
|
|
// in the routine will decrement it to 0, and succeed the if
|
|
// statement.
|
|
//
|
|
if (InterlockedDecrement(&routineContext->Gate) == 0) {
|
|
|
|
//
|
|
// This is only executed on one processor.
|
|
//
|
|
routineContext->Routine(routineContext->Extension,
|
|
routineContext->Context
|
|
);
|
|
|
|
//
|
|
// Free other processors.
|
|
//
|
|
routineContext->Barrier = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Wait for gated function to complete.
|
|
//
|
|
do {
|
|
} while (routineContext->Barrier != 0);
|
|
}
|
|
|
|
return (ULONG_PTR)0;
|
|
}
|
|
|
|
BOOLEAN
|
|
PciUnicodeStringStrStr(
|
|
IN PUNICODE_STRING SearchString,
|
|
IN PUNICODE_STRING SubString,
|
|
IN BOOLEAN CaseInsensitive
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a counted string version of strstr and searchs for any
|
|
instance of SubString within SearchString.
|
|
|
|
Arguments:
|
|
|
|
SearchString - String to search within
|
|
|
|
SubString - String to search for
|
|
|
|
CaseInsensitive - If TRUE indicates that a the comparison should be case
|
|
insensitive
|
|
|
|
Return Value:
|
|
|
|
TRUE if SubString is contained within SearchString, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
USHORT searchIndex, searchCount, subCount;
|
|
UNICODE_STRING currentSearchString;
|
|
|
|
searchCount = SearchString->Length / sizeof(WCHAR);
|
|
subCount = SubString->Length / sizeof(WCHAR);
|
|
|
|
currentSearchString.Buffer = SearchString->Buffer;
|
|
currentSearchString.MaximumLength = SearchString->MaximumLength;
|
|
|
|
//
|
|
// Set the length of the potential match string to the same length as
|
|
// SubString so we can use RtlEqualUnicodeString to compare them.
|
|
//
|
|
|
|
currentSearchString.Length = SubString->Length;
|
|
|
|
//
|
|
// Iterate through the search string until we are less than searchCount
|
|
// characters from the end since the SubString can't possibly fit.
|
|
//
|
|
|
|
for (searchIndex = 0;
|
|
searchIndex <= searchCount - subCount;
|
|
searchIndex++) {
|
|
|
|
//
|
|
// Now see if our substring is located at this position.
|
|
//
|
|
|
|
if(RtlEqualUnicodeString(SubString, ¤tSearchString, CaseInsensitive)) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Advance one character in the currentSearchString and decrement maximum
|
|
// length accordingly
|
|
//
|
|
|
|
currentSearchString.Buffer++;
|
|
currentSearchString.MaximumLength -= sizeof(WCHAR);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|