diff --git a/reactos/drivers/bus/pcix/enum.c b/reactos/drivers/bus/pcix/enum.c index db2313250f1..3551a3b3506 100644 --- a/reactos/drivers/bus/pcix/enum.c +++ b/reactos/drivers/bus/pcix/enum.c @@ -16,6 +16,40 @@ /* FUNCTIONS ******************************************************************/ +BOOLEAN +NTAPI +PcipIsSameDevice(IN PPCI_PDO_EXTENSION DeviceExtension, + IN PPCI_COMMON_HEADER PciData) +{ + BOOLEAN IdMatch, RevMatch, SubsysMatch; + ULONGLONG HackFlags = DeviceExtension->HackFlags; + + /* Check if the IDs match */ + IdMatch = (PciData->VendorID == DeviceExtension->VendorId) && + (PciData->DeviceID == DeviceExtension->DeviceId); + if (!IdMatch) return FALSE; + + /* If the device has a valid revision, check if it matches */ + RevMatch = (HackFlags & PCI_HACK_NO_REVISION_AFTER_D3) || + (PciData->RevisionID == DeviceExtension->RevisionId); + if (!RevMatch) return FALSE; + + /* For multifunction devices, this is enough to assume they're the same */ + if (PCI_MULTIFUNCTION_DEVICE(PciData)) return TRUE; + + /* For bridge devices, there's also nothing else that can be checked */ + if (DeviceExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) return TRUE; + + /* Devices, on the other hand, have subsystem data that can be compared */ + SubsysMatch = (HackFlags & (PCI_HACK_NO_SUBSYSTEM | + PCI_HACK_NO_SUBSYSTEM_AFTER_D3)) || + ((DeviceExtension->SubsystemVendorId == + PciData->u.type0.SubVendorID) && + (DeviceExtension->SubsystemId == + PciData->u.type0.SubSystemID)); + return SubsysMatch; +} + BOOLEAN NTAPI PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData, @@ -100,7 +134,9 @@ PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension) LONGLONG HackFlags; PDEVICE_OBJECT DeviceObject; UCHAR Buffer[PCI_COMMON_HDR_LENGTH]; + UCHAR BiosBuffer[PCI_COMMON_HDR_LENGTH]; PPCI_COMMON_HEADER PciData = (PVOID)Buffer; + PPCI_COMMON_HEADER BiosData = (PVOID)BiosBuffer; PCI_SLOT_NUMBER PciSlot; NTSTATUS Status; PPCI_PDO_EXTENSION PdoExtension, NewExtension; @@ -160,7 +196,8 @@ PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension) /* Find description for this device for the debugger's sake */ DescriptionText = PciGetDeviceDescriptionMessage(PciData->BaseClass, PciData->SubClass); - DPRINT1("Device Description \"%S\".\n", DescriptionText ? DescriptionText : L"(NULL)"); + DPRINT1("Device Description \"%S\".\n", + DescriptionText ? DescriptionText : L"(NULL)"); if (DescriptionText) ExFreePoolWithTag(DescriptionText, 0); /* Check if there is an ACPI Watchdog Table */ @@ -258,7 +295,7 @@ PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension) NewExtension->BaseClass = PciData->BaseClass; NewExtension->HeaderType = PCI_CONFIGURATION_TYPE(PciData); - /* Check for PCI or Cardbus bridges, which are supported by this driver */ + /* Check for modern bridge types, which are managed by the driver */ if ((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && ((NewExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) || (NewExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS))) @@ -281,10 +318,84 @@ PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension) ASSERT(NewExtension->NextBridge == NULL); /* Release this device's lock */ - KeSetEvent(&DeviceExtension->ChildListLock, IO_NO_INCREMENT, FALSE); + KeSetEvent(&DeviceExtension->ChildListLock, + IO_NO_INCREMENT, + FALSE); KeLeaveCriticalRegion(); } - + + /* Get the PCI BIOS configuration saved in the registry */ + Status = PciGetBiosConfig(NewExtension, BiosData); + if (NT_SUCCESS(Status)) + { + /* This path has not yet been fully tested by eVb */ + DPRINT1("Have BIOS configuration!\n"); + UNIMPLEMENTED; + + /* Check if the PCI BIOS configuration has changed */ + if (!PcipIsSameDevice(NewExtension, BiosData)) + { + /* This is considered failure, and new data will be saved */ + Status = STATUS_UNSUCCESSFUL; + } + else + { + /* Data is still correct, check for interrupt line change */ + if (BiosData->u.type0.InterruptLine != + PciData->u.type0.InterruptLine) + { + /* Update the current BIOS with the saved interrupt line */ + PciWriteDeviceConfig(NewExtension, + &BiosData->u.type0.InterruptLine, + FIELD_OFFSET(PCI_COMMON_HEADER, + u.type0.InterruptLine), + sizeof(UCHAR)); + } + + /* Save the BIOS interrupt line and the initial command */ + NewExtension->RawInterruptLine = BiosData->u.type0.InterruptLine; + NewExtension->InitialCommand = BiosData->Command; + } + } + + /* Check if no saved data was present or if it was a mismatch */ + if (!NT_SUCCESS(Status)) + { + /* Save the new data */ + Status = PciSaveBiosConfig(NewExtension, PciData); + ASSERT(NT_SUCCESS(Status)); + + /* Save the interrupt line and command from the device */ + NewExtension->RawInterruptLine = PciData->u.type0.InterruptLine; + NewExtension->InitialCommand = PciData->Command; + } + + /* Save original command from the device and hack flags */ + NewExtension->CommandEnables = PciData->Command; + NewExtension->HackFlags = HackFlags; + + /* Save interrupt pin */ + NewExtension->InterruptPin = PciData->u.type0.InterruptPin; + + /* + * Use either this device's actual IRQ line or, if it's connected on + * a master bus whose IRQ line is actually connected to the host, use + * the HAL to query the bus' IRQ line and store that as the adjusted + * interrupt line instead + */ + NewExtension->AdjustedInterruptLine = PciGetAdjustedInterruptLine(NewExtension); + + /* Check if this device is used for PCI debugger cards */ + NewExtension->OnDebugPath = PciIsDeviceOnDebugPath(NewExtension); + + /* Check for devices with invalid/bogus subsystem data */ + if (HackFlags & PCI_HACK_NO_SUBSYSTEM) + { + /* Set the subsystem information to zero instead */ + NewExtension->SubsystemVendorId = 0; + NewExtension->SubsystemId = 0; + } + /* Check for IDE controllers */ if ((NewExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) && (NewExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR)) @@ -292,26 +403,27 @@ PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension) /* Do not allow them to power down completely */ NewExtension->DisablePowerDown = TRUE; } - + /* * Check if this is a legacy bridge. Note that the i82375 PCI/EISA - * bridge that is present on certain NT Alpha machines appears as + * bridge that is present on certain NT Alpha machines appears as * non-classified so detect it manually by scanning for its VID/PID. */ if (((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && ((NewExtension->SubClass == PCI_SUBCLASS_BR_ISA) || (NewExtension->SubClass == PCI_SUBCLASS_BR_EISA) || (NewExtension->SubClass == PCI_SUBCLASS_BR_MCA))) || - ((NewExtension->VendorId == 0x8086) && (NewExtension->DeviceId == 0x482))) + ((NewExtension->VendorId == 0x8086) && + (NewExtension->DeviceId == 0x482))) { /* Do not allow these legacy bridges to be powered down */ NewExtension->DisablePowerDown = TRUE; } - + /* Save latency and cache size information */ NewExtension->SavedLatencyTimer = PciData->LatencyTimer; NewExtension->SavedCacheLineSize = PciData->CacheLineSize; - + /* The PDO is now ready to go */ DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; } diff --git a/reactos/drivers/bus/pcix/pci.h b/reactos/drivers/bus/pcix/pci.h index d9549b05fb7..82eed3275b3 100644 --- a/reactos/drivers/bus/pcix/pci.h +++ b/reactos/drivers/bus/pcix/pci.h @@ -57,11 +57,16 @@ #define PCI_INTERFACE_ROOT 0x04 // -// PCI Skip Function FLags +// PCI Skip Function Flags // #define PCI_SKIP_DEVICE_ENUMERATION 0x01 #define PCI_SKIP_RESOURCE_ENUMERATION 0x02 +// +// PCI Debugging Device Support +// +#define MAX_DEBUGGING_DEVICES_SUPPORTED 0x04 + // // Device Extension, Interface, Translator and Arbiter Signatures // @@ -900,6 +905,26 @@ PciIsCriticalDeviceClass( IN UCHAR SubClass ); +BOOLEAN +NTAPI +PciIsDeviceOnDebugPath( + IN PPCI_PDO_EXTENSION DeviceExtension +); + +NTSTATUS +NTAPI +PciGetBiosConfig( + IN PPCI_PDO_EXTENSION DeviceExtension, + OUT PPCI_COMMON_HEADER PciData +); + +NTSTATUS +NTAPI +PciSaveBiosConfig( + IN PPCI_PDO_EXTENSION DeviceExtension, + OUT PPCI_COMMON_HEADER PciData +); + // // Configuration Routines // @@ -919,6 +944,21 @@ PciReadSlotConfig( IN ULONG Length ); +VOID +NTAPI +PciWriteDeviceConfig( + IN PPCI_PDO_EXTENSION DeviceExtension, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length +); + +UCHAR +NTAPI +PciGetAdjustedInterruptLine( + IN PPCI_PDO_EXTENSION PdoExtension +); + // // State Machine Logic Transition Routines // diff --git a/reactos/drivers/bus/pcix/pci/config.c b/reactos/drivers/bus/pcix/pci/config.c index e86d73b4ea2..e5382bf949f 100644 --- a/reactos/drivers/bus/pcix/pci/config.c +++ b/reactos/drivers/bus/pcix/pci/config.c @@ -18,6 +18,31 @@ BOOLEAN PciAssignBusNumbers; /* FUNCTIONS ******************************************************************/ +UCHAR +NTAPI +PciGetAdjustedInterruptLine(IN PPCI_PDO_EXTENSION PdoExtension) +{ + UCHAR InterruptLine = 0, PciInterruptLine; + ULONG Length; + + /* Does the device have an interrupt pin? */ + if (PdoExtension->InterruptPin) + { + /* Find the associated line on the parent bus */ + Length = HalGetBusDataByOffset(PCIConfiguration, + PdoExtension->ParentFdoExtension->BaseBus, + PdoExtension->Slot.u.AsULONG, + &PciInterruptLine, + FIELD_OFFSET(PCI_COMMON_HEADER, + u.type0.InterruptLine), + sizeof(UCHAR)); + if (Length) InterruptLine = PciInterruptLine; + } + + /* Either keep the original interrupt line, or the one on the master bus */ + return InterruptLine ? PdoExtension->RawInterruptLine : InterruptLine; +} + VOID NTAPI PciReadWriteConfigSpace(IN PPCI_FDO_EXTENSION DeviceExtension, @@ -61,6 +86,22 @@ PciReadWriteConfigSpace(IN PPCI_FDO_EXTENSION DeviceExtension, } } +VOID +NTAPI +PciWriteDeviceConfig(IN PPCI_PDO_EXTENSION DeviceExtension, + IN PVOID Buffer, + IN ULONG Offset, + IN ULONG Length) +{ + /* Call the generic worker function */ + PciReadWriteConfigSpace(DeviceExtension->ParentFdoExtension, + DeviceExtension->Slot, + Buffer, + Offset, + Length, + FALSE); +} + VOID NTAPI PciReadSlotConfig(IN PPCI_FDO_EXTENSION DeviceExtension, diff --git a/reactos/drivers/bus/pcix/utils.c b/reactos/drivers/bus/pcix/utils.c index ed60cb55973..43ce2a884de 100644 --- a/reactos/drivers/bus/pcix/utils.c +++ b/reactos/drivers/bus/pcix/utils.c @@ -14,6 +14,8 @@ /* GLOBALS ********************************************************************/ +ULONG PciDebugPortsCount; + RTL_RANGE_LIST PciIsaBitExclusionList; RTL_RANGE_LIST PciVgaAndIsaBitExclusionList; @@ -741,4 +743,137 @@ PciFindPdoByFunction(IN PPCI_FDO_EXTENSION DeviceExtension, return PdoExtension; } +BOOLEAN +NTAPI +PciIsDeviceOnDebugPath(IN PPCI_PDO_EXTENSION DeviceExtension) +{ + PAGED_CODE(); + + /* Check for too many, or no, debug ports */ + ASSERT(PciDebugPortsCount <= MAX_DEBUGGING_DEVICES_SUPPORTED); + if (!PciDebugPortsCount) return FALSE; + + /* eVb has not been able to test such devices yet */ + UNIMPLEMENTED; + while (TRUE); +} + +NTSTATUS +NTAPI +PciGetBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension, + OUT PPCI_COMMON_HEADER PciData) +{ + HANDLE KeyHandle, SubKeyHandle; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING KeyName, KeyValue; + WCHAR Buffer[32]; + WCHAR DataBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + PCI_COMMON_HDR_LENGTH]; + PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)DataBuffer; + NTSTATUS Status; + ULONG ResultLength; + PAGED_CODE(); + + /* Open the PCI key */ + Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension-> + PhysicalDeviceObject, + TRUE, + KEY_ALL_ACCESS, + &KeyHandle); + if (!NT_SUCCESS(Status)) return Status; + + /* Create a volatile BIOS configuration key */ + RtlInitUnicodeString(&KeyName, L"BiosConfig"); + InitializeObjectAttributes(&ObjectAttributes, + &KeyName, + OBJ_KERNEL_HANDLE, + KeyHandle, + NULL); + Status = ZwCreateKey(&SubKeyHandle, + KEY_READ, + &ObjectAttributes, + 0, + NULL, + REG_OPTION_VOLATILE, + NULL); + ZwClose(KeyHandle); + if (!NT_SUCCESS(Status)) return Status; + + /* Create the key value based on the device and function number */ + swprintf(Buffer, + L"DEV_%02x&FUN_%02x", + DeviceExtension->Slot.u.bits.DeviceNumber, + DeviceExtension->Slot.u.bits.FunctionNumber); + RtlInitUnicodeString(&KeyValue, Buffer); + + /* Query the value information (PCI BIOS configuration header) */ + Status = ZwQueryValueKey(SubKeyHandle, + &KeyValue, + KeyValuePartialInformation, + PartialInfo, + sizeof(DataBuffer), + &ResultLength); + ZwClose(SubKeyHandle); + if (!NT_SUCCESS(Status)) return Status; + + /* If any information was returned, go ahead and copy its data */ + ASSERT(PartialInfo->DataLength == PCI_COMMON_HDR_LENGTH); + RtlCopyMemory(PciData, PartialInfo->Data, PCI_COMMON_HDR_LENGTH); + return Status; +} + +NTSTATUS +NTAPI +PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension, + IN PPCI_COMMON_HEADER PciData) +{ + HANDLE KeyHandle, SubKeyHandle; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING KeyName, KeyValue; + WCHAR Buffer[32]; + NTSTATUS Status; + PAGED_CODE(); + + /* Open the PCI key */ + Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension-> + PhysicalDeviceObject, + TRUE, + KEY_READ | KEY_WRITE, + &KeyHandle); + if (!NT_SUCCESS(Status)) return Status; + + /* Create a volatile BIOS configuration key */ + RtlInitUnicodeString(&KeyName, L"BiosConfig"); + InitializeObjectAttributes(&ObjectAttributes, + &KeyName, + OBJ_KERNEL_HANDLE, + KeyHandle, + NULL); + Status = ZwCreateKey(&SubKeyHandle, + KEY_READ | KEY_WRITE, + &ObjectAttributes, + 0, + NULL, + REG_OPTION_VOLATILE, + NULL); + ZwClose(KeyHandle); + if (!NT_SUCCESS(Status)) return Status; + + /* Create the key value based on the device and function number */ + swprintf(Buffer, + L"DEV_%02x&FUN_%02x", + DeviceExtension->Slot.u.bits.DeviceNumber, + DeviceExtension->Slot.u.bits.FunctionNumber); + RtlInitUnicodeString(&KeyValue, Buffer); + + /* Set the value data (the PCI BIOS configuration header) */ + Status = ZwSetValueKey(SubKeyHandle, + &KeyValue, + 0, + REG_BINARY, + PciData, + PCI_COMMON_HDR_LENGTH); + ZwClose(SubKeyHandle); + return Status; +} + /* EOF */