diff --git a/win32ss/drivers/videoprt/resource.c b/win32ss/drivers/videoprt/resource.c index 21d36a21495..537bf294867 100644 --- a/win32ss/drivers/videoprt/resource.c +++ b/win32ss/drivers/videoprt/resource.c @@ -26,6 +26,8 @@ extern BOOLEAN VpBaseVideo; +static UNICODE_STRING VideoClassName = RTL_CONSTANT_STRING(L"VIDEO"); + /* PRIVATE FUNCTIONS **********************************************************/ static BOOLEAN @@ -209,21 +211,41 @@ IntVideoPortReleaseResources( // An empty CM_RESOURCE_LIST UCHAR EmptyResourceList[FIELD_OFFSET(CM_RESOURCE_LIST, List)] = {0}; - Status = IoReportResourceForDetection( - DeviceExtension->DriverObject, - NULL, 0, /* Driver List */ - DeviceExtension->PhysicalDeviceObject, - (PCM_RESOURCE_LIST)EmptyResourceList, - sizeof(EmptyResourceList), - &ConflictDetected); - - if (!NT_SUCCESS(Status)) + if (DeviceExtension->IsLegacyDevice || DeviceExtension->IsLegacyDetect || DeviceExtension->IsVgaDetect) { - ERR_(VIDEOPRT, - "VideoPortReleaseResources IoReportResource failed with 0x%08lx ; ConflictDetected: %s\n", - Status, ConflictDetected ? "TRUE" : "FALSE"); + Status = IoReportResourceForDetection( + DeviceExtension->DriverObject, + NULL, 0, /* Driver List */ + DeviceExtension->PhysicalDeviceObject, + (PCM_RESOURCE_LIST)EmptyResourceList, + sizeof(EmptyResourceList), + &ConflictDetected); + + if (!NT_SUCCESS(Status)) + { + ERR_(VIDEOPRT, + "VideoPortReleaseResources (Detect) failed with 0x%08lx ; ConflictDetected: %s\n", + Status, ConflictDetected ? "TRUE" : "FALSE"); + } + } + else + { + Status = IoReportResourceUsage(&VideoClassName, + DeviceExtension->DriverObject, + NULL, + 0, + DeviceExtension->PhysicalDeviceObject, + (PCM_RESOURCE_LIST)EmptyResourceList, + sizeof(EmptyResourceList), + FALSE, + &ConflictDetected); + if (!NT_SUCCESS(Status)) + { + ERR_(VIDEOPRT, + "VideoPortReleaseResources (Usage) failed with 0x%08lx ; ConflictDetected: %s\n", + Status, ConflictDetected ? "TRUE" : "FALSE"); + } } - /* Ignore the returned status however... */ } NTSTATUS NTAPI @@ -276,6 +298,38 @@ IntVideoPortMapPhysicalMemory( return Status; } +static BOOLEAN +IntAccessRangeIsInAllocatedResources( + _In_ PVIDEO_PORT_DEVICE_EXTENSION DeviceExtension, + _In_ PVIDEO_ACCESS_RANGE Range) +{ + CM_RESOURCE_LIST *Res = DeviceExtension->AllocatedResources; + CM_FULL_RESOURCE_DESCRIPTOR *Full; + CM_PARTIAL_RESOURCE_DESCRIPTOR *Desc; + + if (!Res || Res->Count == 0) + return FALSE; + Full = &Res->List[0]; + for (Desc = Full->PartialResourceList.PartialDescriptors; + Desc < Full->PartialResourceList.PartialDescriptors + Full->PartialResourceList.Count; + ++Desc) + { + if (Range->RangeInIoSpace && Desc->Type == CmResourceTypePort) + { + if (Desc->u.Port.Start.QuadPart == Range->RangeStart.QuadPart && + Desc->u.Port.Length == Range->RangeLength) + return TRUE; + } + else if (!Range->RangeInIoSpace && Desc->Type == CmResourceTypeMemory) + { + if (Desc->u.Memory.Start.QuadPart == Range->RangeStart.QuadPart && + Desc->u.Memory.Length == Range->RangeLength) + return TRUE; + } + } + return FALSE; +} + PVOID NTAPI IntVideoPortMapMemory( @@ -952,12 +1006,82 @@ VideoPortVerifyAccessRanges( { /* Release the resources and do nothing more for now... */ IntVideoPortReleaseResources(DeviceExtension); + /* If releasing VGA device resources, clear tracked ranges */ + if (DeviceExtension->IsVgaDriver) + { + KeWaitForMutexObject(&VgaSyncLock, Executive, KernelMode, FALSE, NULL); + if (VgaRanges) + { + ExFreePoolWithTag(VgaRanges, TAG_VIDEO_PORT); + VgaRanges = NULL; + } + NumOfVgaRanges = 0; + VgaDeviceExtension = NULL; + KeReleaseMutex(&VgaSyncLock, FALSE); + } return NO_ERROR; } - /* Create the resource list */ + /* + * For non-legacy PCI devices, validate that the relevant I/O or memory + * decoding bits are enabled in the PCI command register before claiming + */ + if (!DeviceExtension->IsLegacyDevice && DeviceExtension->AdapterInterfaceType == PCIBus) + { + USHORT PciCommand; + ULONG BytesRead; + + BytesRead = HalGetBusDataByOffset(PCIConfiguration, + DeviceExtension->SystemIoBusNumber, + DeviceExtension->SystemIoSlotNumber, + &PciCommand, + FIELD_OFFSET(PCI_COMMON_CONFIG, Command), + sizeof(PciCommand)); + if (BytesRead == sizeof(PciCommand)) + { + for (i = 0; i < NumAccessRanges; i++) + { + if (AccessRanges[i].RangeInIoSpace) + { + if ((PciCommand & PCI_ENABLE_IO_SPACE) == 0) + { + WARN_(VIDEOPRT, "PCI I/O space disabled; refusing access range claim\n"); + return ERROR_INVALID_PARAMETER; + } + } + else + { + if ((PciCommand & PCI_ENABLE_MEMORY_SPACE) == 0) + { + WARN_(VIDEOPRT, "PCI memory space disabled; refusing access range claim\n"); + return ERROR_INVALID_PARAMETER; + } + } + } + } + } + + /* Determine which ranges need to be claimed (exclude PnP-assigned ones) */ + ULONG Needed = 0; + for (i = 0; i < NumAccessRanges; ++i) + { + if (DeviceExtension->PhysicalDeviceObject && !DeviceExtension->IsLegacyDevice) + { + if (IntAccessRangeIsInAllocatedResources(DeviceExtension, &AccessRanges[i])) + continue; + } + ++Needed; + } + + if (Needed == 0) + { + /* Nothing to report/claim */ + return NO_ERROR; + } + + /* Create the resource list for the non-PnP-assigned ranges */ ResourceListSize = sizeof(CM_RESOURCE_LIST) - + (NumAccessRanges - 1) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR); + + (Needed - 1) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR); ResourceList = ExAllocatePoolWithTag(PagedPool, ResourceListSize, TAG_VIDEO_PORT); if (!ResourceList) { @@ -971,47 +1095,86 @@ VideoPortVerifyAccessRanges( ResourceList->List[0].BusNumber = DeviceExtension->SystemIoBusNumber; ResourceList->List[0].PartialResourceList.Version = 1; ResourceList->List[0].PartialResourceList.Revision = 1; - ResourceList->List[0].PartialResourceList.Count = NumAccessRanges; - for (i = 0; i < NumAccessRanges; i++, AccessRanges++) + ResourceList->List[0].PartialResourceList.Count = Needed; + + ULONG j = 0; + for (i = 0; i < NumAccessRanges; ++i) { - PartialDescriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[i]; - if (AccessRanges->RangeInIoSpace) + if (DeviceExtension->PhysicalDeviceObject && !DeviceExtension->IsLegacyDevice) + { + if (IntAccessRangeIsInAllocatedResources(DeviceExtension, &AccessRanges[i])) + continue; + } + + PartialDescriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[j++]; + if (AccessRanges[i].RangeInIoSpace) { PartialDescriptor->Type = CmResourceTypePort; - PartialDescriptor->u.Port.Start = AccessRanges->RangeStart; - PartialDescriptor->u.Port.Length = AccessRanges->RangeLength; + PartialDescriptor->u.Port.Start = AccessRanges[i].RangeStart; + PartialDescriptor->u.Port.Length = AccessRanges[i].RangeLength; } else { PartialDescriptor->Type = CmResourceTypeMemory; - PartialDescriptor->u.Memory.Start = AccessRanges->RangeStart; - PartialDescriptor->u.Memory.Length = AccessRanges->RangeLength; + PartialDescriptor->u.Memory.Start = AccessRanges[i].RangeStart; + PartialDescriptor->u.Memory.Length = AccessRanges[i].RangeLength; } - if (AccessRanges->RangeShareable) - PartialDescriptor->ShareDisposition = CmResourceShareShared; - else - PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive; + PartialDescriptor->ShareDisposition = AccessRanges[i].RangeShareable ? + CmResourceShareShared : CmResourceShareDeviceExclusive; PartialDescriptor->Flags = 0; - if (AccessRanges->RangePassive & VIDEO_RANGE_PASSIVE_DECODE) + if (AccessRanges[i].RangePassive & VIDEO_RANGE_PASSIVE_DECODE) PartialDescriptor->Flags |= CM_RESOURCE_PORT_PASSIVE_DECODE; - if (AccessRanges->RangePassive & VIDEO_RANGE_10_BIT_DECODE) + if (AccessRanges[i].RangePassive & VIDEO_RANGE_10_BIT_DECODE) PartialDescriptor->Flags |= CM_RESOURCE_PORT_10_BIT_DECODE; } - /* Try to acquire all resource ranges */ - Status = IoReportResourceForDetection( - DeviceExtension->DriverObject, - NULL, 0, /* Driver List */ - DeviceExtension->PhysicalDeviceObject, - ResourceList, ResourceListSize, - &ConflictDetected); + if (DeviceExtension->IsLegacyDevice || DeviceExtension->IsLegacyDetect || DeviceExtension->IsVgaDetect) + { + Status = IoReportResourceForDetection( + DeviceExtension->DriverObject, + NULL, 0, /* Driver List */ + DeviceExtension->PhysicalDeviceObject, + ResourceList, ResourceListSize, + &ConflictDetected); + /* IntIsVgaSaveDriver() will later ignore STATUS_CONFLICTING_ADDRESSES, but we still claim it */ + if (!NT_SUCCESS(Status) && IntIsVgaSaveDriver(DeviceExtension)) + { + NTSTATUS fbStatus; + BOOLEAN fbConflict = FALSE; + fbStatus = IoReportResourceUsage(&VideoClassName, + DeviceExtension->DriverObject, + NULL, + 0, + DeviceExtension->PhysicalDeviceObject, + ResourceList, + ResourceListSize, + FALSE, + &fbConflict); + INFO_(VIDEOPRT, "VGA detect->usage fallback: Status=0x%lx Conflict=%d fbStatus=0x%lx fbConflict=%d\n", + Status, ConflictDetected, fbStatus, fbConflict); + Status = fbStatus; + ConflictDetected = fbConflict; + } + } + else + { + Status = IoReportResourceUsage(&VideoClassName, + DeviceExtension->DriverObject, + NULL, + 0, + DeviceExtension->PhysicalDeviceObject, + ResourceList, + ResourceListSize, + FALSE, + &ConflictDetected); + } ExFreePoolWithTag(ResourceList, TAG_VIDEO_PORT); /* If VgaSave driver is conflicting and we don't explicitely want * to use it, ignore the problem (because win32k will try to use * this driver only if all other ones are failing). */ - if (Status == STATUS_CONFLICTING_ADDRESSES && + if ((Status == STATUS_CONFLICTING_ADDRESSES || ConflictDetected) && IntIsVgaSaveDriver(DeviceExtension) && !VpBaseVideo) { @@ -1021,7 +1184,34 @@ VideoPortVerifyAccessRanges( if (!NT_SUCCESS(Status) || ConflictDetected) return ERROR_INVALID_PARAMETER; else + { + /* Track VGA access ranges on success for fallback handling */ + if (DeviceExtension->IsVgaDriver) + { + KeWaitForMutexObject(&VgaSyncLock, Executive, KernelMode, FALSE, NULL); + if (VgaRanges) + { + ExFreePoolWithTag(VgaRanges, TAG_VIDEO_PORT); + VgaRanges = NULL; + NumOfVgaRanges = 0; + } + if (NumAccessRanges) + { + SIZE_T sz = NumAccessRanges * sizeof(VIDEO_ACCESS_RANGE); + VgaRanges = ExAllocatePoolWithTag(PagedPool, sz, TAG_VIDEO_PORT); + if (VgaRanges) + { + RtlCopyMemory(VgaRanges, AccessRanges, sz); + NumOfVgaRanges = NumAccessRanges; + VgaDeviceExtension = DeviceExtension; + } + } + KeReleaseMutex(&VgaSyncLock, FALSE); + } + /* Leave VGA detect phase after first successful claim */ + DeviceExtension->IsVgaDetect = FALSE; return NO_ERROR; + } } /* diff --git a/win32ss/drivers/videoprt/videoprt.c b/win32ss/drivers/videoprt/videoprt.c index e527e821d53..ddc6012a185 100644 --- a/win32ss/drivers/videoprt/videoprt.c +++ b/win32ss/drivers/videoprt/videoprt.c @@ -42,9 +42,20 @@ BOOLEAN VideoPortUseNewKey = FALSE; KSPIN_LOCK HwResetAdaptersLock; RTL_STATIC_LIST_HEAD(HwResetAdaptersList); +KMUTEX VgaSyncLock; +PVIDEO_PORT_DEVICE_EXTENSION VgaDeviceExtension = NULL; +PVIDEO_ACCESS_RANGE VgaRanges = NULL; +ULONG NumOfVgaRanges = 0; /* PRIVATE FUNCTIONS **********************************************************/ +static BOOLEAN +IntIsVgaSaveDriverName(_In_ PDRIVER_OBJECT DriverObject) +{ + static const UNICODE_STRING VgaSave = RTL_CONSTANT_STRING(L"\\Driver\\VgaSave"); + return RtlEqualUnicodeString(&VgaSave, &DriverObject->DriverName, TRUE); +} + ULONG NTAPI DriverEntry( @@ -423,8 +434,13 @@ IntVideoPortFindAdapter( SYSTEM_BASIC_INFORMATION SystemBasicInfo; UCHAR Again = FALSE; BOOL LegacyDetection = FALSE; + BOOLEAN VgaResourcesReleased = FALSE; DeviceExtension = (PVIDEO_PORT_DEVICE_EXTENSION)DeviceObject->DeviceExtension; + DeviceExtension->IsVgaDriver = IntIsVgaSaveDriverName(DriverObject); + DeviceExtension->IsVgaDetect = DeviceExtension->IsVgaDriver; + DeviceExtension->IsLegacyDetect = FALSE; + DeviceExtension->ReportDevice = FALSE; /* Setup a ConfigInfo structure that we will pass to HwFindAdapter. */ RtlZeroMemory(&ConfigInfo, sizeof(VIDEO_PORT_CONFIG_INFO)); @@ -462,7 +478,25 @@ IntVideoPortFindAdapter( if (DeviceExtension->PhysicalDeviceObject == NULL) { LegacyDetection = TRUE; + DeviceExtension->IsLegacyDevice = TRUE; + DeviceExtension->IsLegacyDetect = TRUE; } + else + { + DeviceExtension->IsLegacyDevice = FALSE; + } + + /* If we already have a VGA miniport and are about to probe for additional adapters, + * release its resources temporarily so conflicts are visible during detection. + * We'll reclaim them later if no new adapter successfully claims them. */ + KeWaitForMutexObject(&VgaSyncLock, Executive, KernelMode, FALSE, NULL); + if (VgaDeviceExtension) + { + INFO_(VIDEOPRT, "Temporarily releasing VGA resources for adapter probing\n"); + IntVideoPortReleaseResources(VgaDeviceExtension); + VgaResourcesReleased = TRUE; + } + KeReleaseMutex(&VgaSyncLock, FALSE); if (LegacyDetection) { @@ -521,7 +555,28 @@ IntVideoPortFindAdapter( if (vpStatus != NO_ERROR) { - ERR_(VIDEOPRT, "HwFindAdapter call failed with error 0x%X\n", vpStatus); + ERR_(VIDEOPRT, "HwFindAdapter failed (vpStatus=0x%X) bus=%u iface=%u legacy=%u vga=%u detect(VGA=%u LEGACY=%u)\n", + vpStatus, + DeviceExtension->SystemIoBusNumber, + DeviceExtension->AdapterInterfaceType, + DeviceExtension->IsLegacyDevice, + DeviceExtension->IsVgaDriver, + DeviceExtension->IsVgaDetect, + DeviceExtension->IsLegacyDetect); + /* If we released VGA resources, reclaim them so VGA fallback still works */ + if (VgaResourcesReleased) + { + if (VgaDeviceExtension && VgaRanges && NumOfVgaRanges) + { + INFO_(VIDEOPRT, "Reclaiming VGA resources after failed probe\n"); + if (VideoPortVerifyAccessRanges(&VgaDeviceExtension->MiniPortDeviceExtension, + NumOfVgaRanges, + VgaRanges) != NO_ERROR) + { + WARN_(VIDEOPRT, "Failed to reclaim VGA resources after probe failure\n"); + } + } + } Status = STATUS_UNSUCCESSFUL; goto Failure; } @@ -559,6 +614,75 @@ IntVideoPortFindAdapter( &HwResetAdaptersLock); } + if (DeviceExtension->IsVgaDriver) + { + KeWaitForMutexObject(&VgaSyncLock, Executive, KernelMode, FALSE, NULL); + if (VgaDeviceExtension == NULL) + { + VgaDeviceExtension = DeviceExtension; + } + KeReleaseMutex(&VgaSyncLock, FALSE); + } + + DeviceExtension->IsVgaDetect = FALSE; + DeviceExtension->IsLegacyDetect = FALSE; + + /* For legacy (non-PnP) adapters we should report a detected device so that + * a PDO exists for higher layers to enumerate consistently (mirrors ScsiPort).*/ + if (DeviceExtension->IsLegacyDevice && !DeviceExtension->ReportDevice) + { + PDEVICE_OBJECT ReportedPdo = NULL; + NTSTATUS repStatus = IoReportDetectedDevice(DriverObject, + DeviceExtension->AdapterInterfaceType, + DeviceExtension->SystemIoBusNumber, + DeviceExtension->SystemIoSlotNumber, + NULL, + NULL, + FALSE, + &ReportedPdo); + if (!NT_SUCCESS(repStatus)) + { + WARN_(VIDEOPRT, "IoReportDetectedDevice failed 0x%08lx (bus=%u slot=%u)\n", + repStatus, + DeviceExtension->SystemIoBusNumber, + DeviceExtension->SystemIoSlotNumber); + } + else + { + INFO_(VIDEOPRT, "Reported legacy adapter PDO %p (bus=%u slot=%u)\n", + ReportedPdo, + DeviceExtension->SystemIoBusNumber, + DeviceExtension->SystemIoSlotNumber); + DeviceExtension->ReportDevice = TRUE; + } + } + + /* Attempt to reclaim VGA resources after probing if a VGA device exists */ + if (VgaDeviceExtension && VgaRanges && NumOfVgaRanges) + { + VP_STATUS vr; + INFO_(VIDEOPRT, "Attempt VGA reclaim after probe (ranges=%lu)\n", NumOfVgaRanges); + vr = VideoPortVerifyAccessRanges(&VgaDeviceExtension->MiniPortDeviceExtension, + NumOfVgaRanges, + VgaRanges); + if (vr != NO_ERROR) + { + /* Another driver has taken VGA resources; drop fallback state */ + WARN_(VIDEOPRT, "VGA reclaim failed (vpStatus=0x%X); releasing fallback state\n", vr); + KeWaitForMutexObject(&VgaSyncLock, Executive, KernelMode, FALSE, NULL); + ExFreePoolWithTag(VgaRanges, TAG_VIDEO_PORT); + VgaRanges = NULL; + NumOfVgaRanges = 0; + VgaDeviceExtension = NULL; + KeReleaseMutex(&VgaSyncLock, FALSE); + + } + else + { + INFO_(VIDEOPRT, "VGA reclaim succeeded\n"); + } + } + INFO_(VIDEOPRT, "STATUS_SUCCESS\n"); return STATUS_SUCCESS; @@ -566,6 +690,21 @@ Failure: RtlFreeUnicodeString(&DeviceExtension->RegistryPath); if (DeviceExtension->NextDeviceObject) IoDetachDevice(DeviceExtension->NextDeviceObject); + + /* Explicitly reclaim VGA resources on complete failure */ + if (VgaResourcesReleased) + { + if (VgaDeviceExtension && VgaRanges && NumOfVgaRanges) + { + INFO_(VIDEOPRT, "Final reclaim attempt of VGA resources during failure cleanup\n"); + if (VideoPortVerifyAccessRanges(&VgaDeviceExtension->MiniPortDeviceExtension, + NumOfVgaRanges, + VgaRanges) != NO_ERROR) + { + WARN_(VIDEOPRT, "VGA reclaim failed during failure cleanup\n"); + } + } + } IoDeleteDevice(DeviceObject); return Status; } @@ -802,6 +941,7 @@ VideoPortInitialize( { FirstInitialization = TRUE; KeInitializeMutex(&VideoPortInt10Mutex, 0); + KeInitializeMutex(&VgaSyncLock, 0); KeInitializeSpinLock(&HwResetAdaptersLock); IntLoadRegistryParameters(); } diff --git a/win32ss/drivers/videoprt/videoprt.h b/win32ss/drivers/videoprt/videoprt.h index 192542b6221..a09a0d1902f 100644 --- a/win32ss/drivers/videoprt/videoprt.h +++ b/win32ss/drivers/videoprt/videoprt.h @@ -110,6 +110,11 @@ typedef struct _VIDEO_PORT_DEVICE_EXTENSTION USHORT AdapterNumber; USHORT DisplayNumber; ULONG NumberOfSecondaryDisplays; + BOOLEAN IsLegacyDevice; + BOOLEAN IsLegacyDetect; + BOOLEAN IsVgaDriver; + BOOLEAN IsVgaDetect; + BOOLEAN ReportDevice; CHAR POINTER_ALIGNMENT MiniPortDeviceExtension[1]; } VIDEO_PORT_DEVICE_EXTENSION, *PVIDEO_PORT_DEVICE_EXTENSION; @@ -257,6 +262,10 @@ extern BOOLEAN VideoPortUseNewKey; extern KSPIN_LOCK HwResetAdaptersLock; extern LIST_ENTRY HwResetAdaptersList; +extern KMUTEX VgaSyncLock; +extern PVIDEO_PORT_DEVICE_EXTENSION VgaDeviceExtension; +extern PVIDEO_ACCESS_RANGE VgaRanges; +extern ULONG NumOfVgaRanges; BOOLEAN FASTCALL