363 lines
7.9 KiB
C
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;
|
||
|
}
|
||
|
|