/*++ 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 #include #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; }