windows-nt/Source/XPSP1/NT/base/busdrv/pci/pciverifier.c
2020-09-26 16:20:57 +08:00

363 lines
7.9 KiB
C

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
pciverifier.c
Abstract:
This module implements routines used to catch BIOS, hardware, and driver
bugs.
Author:
Adrian J. Oney (AdriaO) 02/20/2001
Revision History:
--*/
#include "pcip.h"
#include <initguid.h>
#include <wdmguid.h>
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, PciVerifierInit)
#pragma alloc_text(PAGE, PciVerifierUnload)
//#pragma alloc_text(PAGEVRFY, PciVerifierProfileChangeCallback)
//#pragma alloc_text(PAGEVRFY, PciVerifierEnsureTreeConsistancy)
//#pragma alloc_text(PAGEVRFY, PciVerifierRetrieveFailureData)
#endif
BOOLEAN PciVerifierRegistered = FALSE;
#ifdef ALLOC_DATA_PRAGMA
//#pragma data_seg("PAGEVRFD")
#endif
PVOID PciVerifierNotificationHandle = NULL;
//
// This is the table of PCI verifier failures
//
VERIFIER_DATA PciVerifierFailureTable[] = {
{ PCI_VERIFIER_BRIDGE_REPROGRAMMED, VFFAILURE_FAIL_LOGO,
0,
"The BIOS has reprogrammed the bus numbers of an active PCI device "
"(!devstack %DevObj) during a dock or undock!" },
{ PCI_VERIFIER_PMCSR_TIMEOUT, VFFAILURE_FAIL_LOGO,
0,
"A device in the system did not update it's PMCSR register in the spec "
"mandated time (!devstack %DevObj, Power state D%Ulong)" },
{ PCI_VERIFIER_PROTECTED_CONFIGSPACE_ACCESS, VFFAILURE_FAIL_LOGO,
0,
"A driver controlling a PCI device has tried to access OS controlled "
"configuration space registers (!devstack %DevObj, Offset 0x%Ulong1, "
"Length 0x%Ulong2)" },
{ PCI_VERIFIER_INVALID_WHICHSPACE, VFFAILURE_FAIL_UNDER_DEBUGGER,
0,
"A driver controlling a PCI device has tried to read or write from "
"an invalid space using IRP_MN_READ/WRITE_CONFIG or via BUS_INTERFACE_STANDARD. "
"NB: These functions take WhichSpace parameters of the form PCI_WHICHSPACE_* "
"and not a BUS_DATA_TYPE (!devstack %DevObj, WhichSpace 0x%Ulong1)" }
};
VOID
PciVerifierInit(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This routine initializes the hardware verification support, enabling
consistancy hooks and state checks where appropriate.
Arguments:
DriverObject - Pointer to our driver object.
Return Value:
None.
--*/
{
NTSTATUS status;
if (!VfIsVerificationEnabled(VFOBJTYPE_SYSTEM_BIOS, NULL)) {
return;
}
status = IoRegisterPlugPlayNotification(
EventCategoryHardwareProfileChange,
0,
NULL,
DriverObject,
PciVerifierProfileChangeCallback,
(PVOID) NULL,
&PciVerifierNotificationHandle
);
if (NT_SUCCESS(status)) {
PciVerifierRegistered = TRUE;
}
}
VOID
PciVerifierUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This routine uninitializes the hardware verification support.
Arguments:
DriverObject - Pointer to our driver object.
Return Value:
None.
--*/
{
NTSTATUS status;
UNREFERENCED_PARAMETER(DriverObject);
if (!PciVerifierRegistered) {
return;
}
ASSERT(PciVerifierNotificationHandle);
status = IoUnregisterPlugPlayNotification(PciVerifierNotificationHandle);
ASSERT(NT_SUCCESS(status));
PciVerifierRegistered = FALSE;
}
NTSTATUS
PciVerifierProfileChangeCallback(
IN PHWPROFILE_CHANGE_NOTIFICATION NotificationStructure,
IN PVOID NotUsed
)
/*++
Routine Description:
This routine gets called back during hardware profile change events if
hardware verification is enabled.
Arguments:
NotificationStructure - Describes the hardware profile event that occured.
NotUsed - Not used
Return Value:
NTSTATUS.
--*/
{
PAGED_CODE();
UNREFERENCED_PARAMETER(NotUsed);
if (IsEqualGUID((LPGUID) &NotificationStructure->Event,
(LPGUID) &GUID_HWPROFILE_CHANGE_COMPLETE)) {
//
// This is a HW profile change complete message. Do some tests to
// ensure our hardware hasn't been reprogrammed behind our back.
//
PciVerifierEnsureTreeConsistancy();
}
return STATUS_SUCCESS;
}
VOID
PciVerifierEnsureTreeConsistancy(
VOID
)
/*++
Routine Description:
This routine checks the device tree and ensures it's physical state matches
the virtual state described by our structures. A deviation may mean someone
has reprogrammed the hardware behind our back.
Arguments:
None.
Return Value:
None.
--*/
{
PSINGLE_LIST_ENTRY nextEntry;
PPCI_FDO_EXTENSION fdoExtension;
PPCI_PDO_EXTENSION pdoExtension;
PCI_COMMON_CONFIG commonConfig;
PVERIFIER_DATA verifierData;
//
// Walk the list of FDO extensions and verifier the physical hardware
// matches our virtual state. Owning the PciGlobalLock ensures the list
// is locked.
//
ExAcquireFastMutex(&PciGlobalLock);
//
// Grab the bus renumbering lock. Note that this lock can be held when
// child list locks are held.
//
ExAcquireFastMutex(&PciBusLock);
for ( nextEntry = PciFdoExtensionListHead.Next;
nextEntry != NULL;
nextEntry = nextEntry->Next ) {
fdoExtension = CONTAINING_RECORD(nextEntry, PCI_FDO_EXTENSION, List);
if (PCI_IS_ROOT_FDO(fdoExtension)) {
//
// It's a root FDO, ignore it.
//
continue;
}
pdoExtension = PCI_BRIDGE_PDO(fdoExtension);
if (pdoExtension->NotPresent ||
(pdoExtension->PowerState.CurrentDeviceState == PowerDeviceD3)) {
//
// Don't touch.
//
continue;
}
if ((pdoExtension->HeaderType != PCI_BRIDGE_TYPE) &&
(pdoExtension->HeaderType != PCI_CARDBUS_BRIDGE_TYPE)) {
//
// Nothing to verify - in fact, why are here, this is a bridge list!
//
ASSERT(0);
continue;
}
//
// Read in the common config (that should be enough)
//
PciReadDeviceConfig(
pdoExtension,
&commonConfig,
0,
sizeof(PCI_COMMON_CONFIG)
);
//
// Ensure bus numbers haven't changed. Note that P2P and Cardbus
// bridges have their Primary, Secondary & Subordinate fields in the
// same place.
//
if ((commonConfig.u.type1.PrimaryBus !=
pdoExtension->Dependent.type1.PrimaryBus) ||
(commonConfig.u.type1.SecondaryBus !=
pdoExtension->Dependent.type1.SecondaryBus) ||
(commonConfig.u.type1.SubordinateBus !=
pdoExtension->Dependent.type1.SubordinateBus)) {
verifierData = PciVerifierRetrieveFailureData(
PCI_VERIFIER_BRIDGE_REPROGRAMMED
);
ASSERT(verifierData);
VfFailSystemBIOS(
PCI_VERIFIER_DETECTED_VIOLATION,
PCI_VERIFIER_BRIDGE_REPROGRAMMED,
verifierData->FailureClass,
&verifierData->Flags,
verifierData->FailureText,
"%DevObj",
pdoExtension->PhysicalDeviceObject
);
}
}
ExReleaseFastMutex(&PciBusLock);
ExReleaseFastMutex(&PciGlobalLock);
}
PVERIFIER_DATA
PciVerifierRetrieveFailureData(
IN PCI_VFFAILURE VerifierFailure
)
/*++
Routine Description:
This routine retrieves the failure data corresponding to a particular PCI
verifier failure event.
Arguments:
PCI Failure.
Return Value:
Verifier data corresponding to the failure.
--*/
{
PVERIFIER_DATA verifierData;
ULONG i;
for(i=0;
i<(sizeof(PciVerifierFailureTable)/sizeof(PciVerifierFailureTable[0]));
i++) {
verifierData = PciVerifierFailureTable + i;
if (verifierData->VerifierFailure == VerifierFailure) {
return verifierData;
}
}
ASSERT(0);
return NULL;
}