diff --git a/drivers/storage/ide/legide/CMakeLists.txt b/drivers/storage/ide/legide/CMakeLists.txt new file mode 100644 index 00000000000..1f8848c209d --- /dev/null +++ b/drivers/storage/ide/legide/CMakeLists.txt @@ -0,0 +1,17 @@ + +add_library(legide MODULE legide.c legide.rc) +set_module_type(legide kernelmodedriver) + +if(MSVC) + # Required for NT5.x pciidex.sys + target_link_options(legide PRIVATE /SECTION:.rsrc,!D) +else() + add_custom_command( + TARGET legide POST_BUILD + COMMAND native-pefixup --section:.rsrc,!D $ + VERBATIM) +endif() + +add_importlibs(legide pciidex ntoskrnl hal) +add_cd_file(TARGET legide DESTINATION reactos/system32/drivers NO_CAB FOR all) +add_registry_inf(legide_reg.inf) diff --git a/drivers/storage/ide/legide/legide.c b/drivers/storage/ide/legide/legide.c new file mode 100644 index 00000000000..3e9a71a2581 --- /dev/null +++ b/drivers/storage/ide/legide/legide.c @@ -0,0 +1,850 @@ +/* + * PROJECT: Legacy PATA Bus Enumerator Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Main file + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* + * This driver detects non-PnP PATA controllers in the system. + * The legacy PATA device detection should be done after PCI bus enumeration, + * therefore this logic is implemented in a separate kernel-mode driver. + */ + +/* INCLUDES *******************************************************************/ + +#include +#include +#include +#include +#include + +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +#define ATAPORT_TAG 'PedI' + +#define IDE_DRIVE_SELECT 0xA0 + +#define ATA_READ(Port) READ_PORT_UCHAR((Port)) +#define ATA_WRITE(Port, Value) WRITE_PORT_UCHAR((Port), (Value)) +#define ATA_IO_WAIT() KeStallExecutionProcessor(1) + +#define ATA_TIME_BUSY_SELECT 3000 // 30 ms +#define ATA_TIME_BUSY_IDENTIFY 500000 // 5 s +#define ATA_TIME_DRQ_CLEAR 1000 // 10 ms +#define ATA_TIME_RESET_SELECT 200000 // 2 s +#define ATA_TIME_BUSY_RESET 1000000 // 10 s + +typedef struct _ATA_LEGACY_CHANNEL +{ + ULONG IoBase; + ULONG Irq; +} ATA_LEGACY_CHANNEL, *PATA_LEGACY_CHANNEL; + +CODE_SEG("INIT") +DRIVER_INITIALIZE DriverEntry; + +/* FUNCTIONS ******************************************************************/ + +static +CODE_SEG("INIT") +UCHAR +AtaWait( + _In_ PIDE_REGISTERS Registers, + _In_range_(>, 0) ULONG Timeout, + _In_ UCHAR Mask, + _In_ UCHAR Value) +{ + UCHAR IdeStatus; + ULONG i; + + for (i = 0; i < Timeout; ++i) + { + IdeStatus = ATA_READ(Registers->Status); + if ((IdeStatus & Mask) == Value) + break; + + if (IdeStatus == 0xFF) + break; + + KeStallExecutionProcessor(10); + } + + return IdeStatus; +} + +static +CODE_SEG("INIT") +VOID +AtaLegacyFetchIdentifyData( + _In_ PIDE_REGISTERS Registers) +{ + USHORT Buffer[256]; + + READ_PORT_BUFFER_USHORT((PUSHORT)Registers->Data, Buffer, RTL_NUMBER_OF(Buffer)); +} + +static +CODE_SEG("INIT") +BOOLEAN +AtaLegacyIdentifyDevice( + _In_ PIDE_REGISTERS Registers, + _In_ ULONG DeviceNumber, + _In_ UCHAR Command) +{ + UCHAR IdeStatus; + + /* Select the device */ + ATA_WRITE(Registers->Device, ((DeviceNumber << 4) | IDE_DRIVE_SELECT)); + ATA_IO_WAIT(); + IdeStatus = AtaWait(Registers, ATA_TIME_BUSY_SELECT, IDE_STATUS_BUSY, 0); + if (IdeStatus & IDE_STATUS_BUSY) + { + DPRINT("Timeout, status 0x%02x\n", IdeStatus); + return FALSE; + } + + ATA_WRITE(Registers->Features, 0); + ATA_WRITE(Registers->SectorCount, 0); + ATA_WRITE(Registers->LbaLow, 0); + ATA_WRITE(Registers->LbaMid, 0); + ATA_WRITE(Registers->LbaHigh, 0); + ATA_WRITE(Registers->Command, Command); + + /* Need to wait for a valid status */ + ATA_IO_WAIT(); + + /* Now wait for busy to clear */ + IdeStatus = AtaWait(Registers, ATA_TIME_BUSY_IDENTIFY, IDE_STATUS_BUSY, 0); + if (IdeStatus & IDE_STATUS_BUSY) + { + DPRINT("Timeout, status 0x%02x\n", IdeStatus); + return FALSE; + } + if (IdeStatus & ((IDE_STATUS_ERROR | IDE_STATUS_DEVICE_FAULT))) + { + DPRINT("Command 0x%02x aborted, status 0x%02x, error 0x%02x\n", + Command, + IdeStatus, + ATA_READ(Registers->Error)); + return FALSE; + } + if (!(IdeStatus & IDE_STATUS_DRQ)) + { + DPRINT1("DRQ not set, status 0x%02x\n", IdeStatus); + return FALSE; + } + + /* Complete the data transfer */ + AtaLegacyFetchIdentifyData(Registers); + + /* All data has been transferred, wait for DRQ to clear */ + IdeStatus = AtaWait(Registers, + ATA_TIME_DRQ_CLEAR, + IDE_STATUS_BUSY | IDE_STATUS_DRQ, + 0); + if (IdeStatus & (IDE_STATUS_BUSY | IDE_STATUS_DRQ)) + { + DPRINT1("DRQ not cleared, status 0x%02x\n", IdeStatus); + return FALSE; + } + + return TRUE; +} + +static +CODE_SEG("INIT") +BOOLEAN +AtaLegacyPerformSoftwareReset( + _In_ PIDE_REGISTERS Registers, + _In_ ULONG DeviceNumber) +{ + UCHAR IdeStatus; + ULONG i; + + /* Perform a software reset */ + ATA_WRITE(Registers->Control, IDE_DC_RESET_CONTROLLER); + KeStallExecutionProcessor(20); + ATA_WRITE(Registers->Control, IDE_DC_DISABLE_INTERRUPTS); + KeStallExecutionProcessor(20); + + /* The reset will cause the master device to be selected */ + if ((DeviceNumber & 1) != 0) + { + for (i = ATA_TIME_RESET_SELECT; i > 0; i--) + { + /* Select the device again */ + ATA_WRITE(Registers->Device, ((DeviceNumber << 4) | IDE_DRIVE_SELECT)); + ATA_IO_WAIT(); + + /* Check whether the selection was successful */ + ATA_WRITE(Registers->ByteCountLow, 0xAA); + ATA_WRITE(Registers->ByteCountLow, 0x55); + ATA_WRITE(Registers->ByteCountLow, 0xAA); + if (ATA_READ(Registers->ByteCountLow) == 0xAA) + break; + + KeStallExecutionProcessor(10); + } + if (i == 0) + { + DPRINT("Selection timeout\n"); + return FALSE; + } + } + + /* Now wait for busy to clear */ + IdeStatus = AtaWait(Registers, ATA_TIME_BUSY_RESET, IDE_STATUS_BUSY, 0); + if (IdeStatus & IDE_STATUS_BUSY) + { + DPRINT1("Timeout, status 0x%02x\n", IdeStatus); + return FALSE; + } + + return FALSE; +} + +static +CODE_SEG("INIT") +BOOLEAN +AtaLegacyFindAtaDevice( + _In_ PIDE_REGISTERS Registers, + _In_ ULONG DeviceNumber) +{ + UCHAR IdeStatus, SignatureLow, SignatureHigh; + + /* Select the device */ + ATA_WRITE(Registers->Device, ((DeviceNumber << 4) | IDE_DRIVE_SELECT)); + ATA_IO_WAIT(); + + /* Do a quick check first */ + IdeStatus = ATA_READ(Registers->Status); + if (IdeStatus == 0xFF || IdeStatus == 0x7F) + return FALSE; + + /* Look at controller */ + ATA_WRITE(Registers->ByteCountLow, 0x55); + ATA_WRITE(Registers->ByteCountLow, 0xAA); + ATA_WRITE(Registers->ByteCountLow, 0x55); + if (ATA_READ(Registers->ByteCountLow) != 0x55) + return FALSE; + ATA_WRITE(Registers->ByteCountHigh, 0xAA); + ATA_WRITE(Registers->ByteCountHigh, 0x55); + ATA_WRITE(Registers->ByteCountHigh, 0xAA); + if (ATA_READ(Registers->ByteCountHigh) != 0xAA) + return FALSE; + + /* Wait for busy to clear */ + IdeStatus = AtaWait(Registers, ATA_TIME_BUSY_SELECT, IDE_STATUS_BUSY, 0); + if (IdeStatus & IDE_STATUS_BUSY) + { + DPRINT1("Device is busy, attempting to recover %02x\n", IdeStatus); + + if (!AtaLegacyPerformSoftwareReset(Registers, DeviceNumber)) + { + DPRINT1("Failed to reset device %02x\n", ATA_READ(Registers->Status)); + return FALSE; + } + } + + /* Check for ATA */ + if (AtaLegacyIdentifyDevice(Registers, DeviceNumber, IDE_COMMAND_IDENTIFY)) + return TRUE; + + SignatureLow = ATA_READ(Registers->SignatureLow); + SignatureHigh = ATA_READ(Registers->SignatureHigh); + + DPRINT("SL = 0x%02x, SH = 0x%02x\n", SignatureLow, SignatureHigh); + + /* Check for ATAPI */ + if (SignatureLow == 0x14 && SignatureHigh == 0xEB) + return TRUE; + + /* + * ATAPI devices abort the IDENTIFY command and return an ATAPI signature + * in the task file registers. However the NEC CDR-260 drive doesn't return + * the correct signature, but instead shows up with zeroes. + * This drive also reports an ATA signature after device reset. + * To overcome this behavior, we try the ATAPI IDENTIFY command. + * It should be successfully completed or failed (aborted or time-out). + */ + return AtaLegacyIdentifyDevice(Registers, DeviceNumber, IDE_COMMAND_ATAPI_IDENTIFY); +} + +static +CODE_SEG("INIT") +BOOLEAN +AtaLegacyChannelPresent( + _In_ PIDE_REGISTERS Registers) +{ + ULONG i; + + /* Check for the PC-98 on-board IDE interface */ + if (IsNEC_98) + return (ATA_READ((PUCHAR)0x432) != 0xFF); + + /* + * The only reliable way to detect the legacy IDE channel + * is to check for devices attached to the bus. + */ + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + if (AtaLegacyFindAtaDevice(Registers, i)) + { + DPRINT("Found IDE device\n"); + return TRUE; + } + } + + return FALSE; +} + +static +CODE_SEG("INIT") +VOID +AtaLegacyMakePortResource( + _Out_ PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor, + _In_ ULONG IoBase, + _In_ ULONG Length) +{ + Descriptor->Type = CmResourceTypePort; + Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; + Descriptor->Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE; + Descriptor->u.Port.Start.u.LowPart = IoBase; + Descriptor->u.Port.Length = Length; +} + +static +CODE_SEG("INIT") +VOID +AtaLegacyMakeInterruptResource( + _Out_ PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor, + _In_ ULONG Level) +{ + Descriptor->Type = CmResourceTypeInterrupt; + Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; + Descriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED; + Descriptor->u.Interrupt.Level = Level; + Descriptor->u.Interrupt.Vector = Level; + Descriptor->u.Interrupt.Affinity = (KAFFINITY)-1; +} + +static +CODE_SEG("INIT") +BOOLEAN +AtaLegacyChannelBuildResources( + _In_ PATA_LEGACY_CHANNEL LegacyChannel, + _Inout_ PCM_RESOURCE_LIST ResourceList) +{ + PCONFIGURATION_INFORMATION ConfigInfo = IoGetConfigurationInformation(); + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; + ULONG i; + + Descriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[0]; + + if (IsNEC_98) + { + if (ConfigInfo->AtDiskPrimaryAddressClaimed || ConfigInfo->AtDiskSecondaryAddressClaimed) + return FALSE; + +#define CHANNEL_PC98_RESOURCE_COUNT 12 + /* + * Make a resource list for the internal IDE interface: + * + * [ShareDisposition 1, Flags 11] IO: Start 0:640, Len 1 + * [ShareDisposition 1, Flags 11] IO: Start 0:74C, Len 1 + * [ShareDisposition 1, Flags 1] INT: Lev 9 Vec 9 Aff FFFFFFFF + * [ShareDisposition 1, Flags 11] IO: Start 0:642, Len 1 + * [ShareDisposition 1, Flags 11] IO: Start 0:644, Len 1 + * [ShareDisposition 1, Flags 11] IO: Start 0:646, Len 1 + * [ShareDisposition 1, Flags 11] IO: Start 0:648, Len 1 + * [ShareDisposition 1, Flags 11] IO: Start 0:64A, Len 1 + * [ShareDisposition 1, Flags 11] IO: Start 0:64C, Len 1 + * [ShareDisposition 1, Flags 11] IO: Start 0:64E, Len 1 + * [ShareDisposition 1, Flags 11] IO: Start 0:432, Len 2 + * [ShareDisposition 1, Flags 11] IO: Start 0:435, Len 1 + */ + AtaLegacyMakePortResource(Descriptor++, 0x640, 1); + AtaLegacyMakePortResource(Descriptor++, 0x74C, 1); + AtaLegacyMakeInterruptResource(Descriptor++, 9); + for (i = 0; i < 7; ++i) + { + AtaLegacyMakePortResource(Descriptor++, 0x642 + i * 2, 1); + } + AtaLegacyMakePortResource(Descriptor++, 0x432, 2); + AtaLegacyMakePortResource(Descriptor++, 0x435, 1); + } + else + { + if (LegacyChannel->IoBase == 0x1F0 && ConfigInfo->AtDiskPrimaryAddressClaimed) + return FALSE; + + if (LegacyChannel->IoBase == 0x170 && ConfigInfo->AtDiskSecondaryAddressClaimed) + return FALSE; + +#define CHANNEL_PCAT_RESOURCE_COUNT 3 + /* + * For example, the following resource list is created for the primary IDE channel: + * + * [ShareDisposition 1, Flags 11] IO: Start 0:1F0, Len 8 + * [ShareDisposition 1, Flags 11] IO: Start 0:3F6, Len 1 + * [ShareDisposition 1, Flags 1] INT: Lev A Vec A Aff FFFFFFFF + */ + AtaLegacyMakePortResource(Descriptor++, LegacyChannel->IoBase, 8); + AtaLegacyMakePortResource(Descriptor++, LegacyChannel->IoBase + 0x206, 1); + AtaLegacyMakeInterruptResource(Descriptor++, LegacyChannel->Irq); + } + + return TRUE; +} + +static +CODE_SEG("INIT") +VOID +AtaLegacyChannelTranslateResources( + _Inout_ PCM_RESOURCE_LIST ResourceList, + _In_ PUCHAR CommandPortBase, + _In_ PUCHAR ControlPortBase) +{ + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; + KIRQL Irql; + + Descriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[0]; + + /* Move on to the interrupt descriptor */ + if (IsNEC_98) + { + Descriptor += 2; + } + else + { + Descriptor->u.Port.Start.QuadPart = (ULONG_PTR)CommandPortBase; + ++Descriptor; + + Descriptor->u.Port.Start.QuadPart = (ULONG_PTR)ControlPortBase; + ++Descriptor; + } + ASSERT(Descriptor->Type == CmResourceTypeInterrupt); + + Descriptor->u.Interrupt.Vector = HalGetInterruptVector(Isa, + 0, + Descriptor->u.Interrupt.Level, + Descriptor->u.Interrupt.Vector, + &Irql, + &Descriptor->u.Interrupt.Affinity); + Descriptor->u.Interrupt.Level = Irql; +} + +static +CODE_SEG("INIT") +PVOID +AtaLegacyTranslateBusAddress( + _In_ ULONG Address, + _In_ ULONG NumberOfBytes, + _Out_ PBOOLEAN IsAddressMmio) +{ + ULONG AddressSpace; + BOOLEAN Success; + PHYSICAL_ADDRESS BusAddress, TranslatedAddress; + + BusAddress.QuadPart = Address; + AddressSpace = 1; // I/O space + Success = HalTranslateBusAddress(Isa, 0, BusAddress, &AddressSpace, &TranslatedAddress); + if (!Success) + return NULL; + + /* I/O space */ + if (AddressSpace != 0) + { + *IsAddressMmio = FALSE; + return (PVOID)(ULONG_PTR)TranslatedAddress.QuadPart; + } + else + { + *IsAddressMmio = TRUE; + return MmMapIoSpace(TranslatedAddress, NumberOfBytes, MmNonCached); + } +} + +static +CODE_SEG("INIT") +BOOLEAN +AtaLegacyClaimHardwareResources( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PCM_RESOURCE_LIST ResourceList, + _In_ ULONG ResourceListSize) +{ + NTSTATUS Status; + BOOLEAN ConflictDetected; + + Status = IoReportResourceForDetection(DriverObject, + ResourceList, + ResourceListSize, + NULL, + NULL, + 0, + &ConflictDetected); + /* HACK: We really need to fix a number of resource bugs in the kernel */ + if (IsNEC_98) + return TRUE; + + if (!NT_SUCCESS(Status) || ConflictDetected) + return FALSE; + + return TRUE; +} + +static +CODE_SEG("INIT") +VOID +AtaLegacyReleaseHardwareResources( + _In_ PDRIVER_OBJECT DriverObject) +{ + BOOLEAN Dummy; + + IoReportResourceForDetection(DriverObject, NULL, 0, NULL, NULL, 0, &Dummy); +} + +static +CODE_SEG("INIT") +BOOLEAN +AtaLegacyDetectChannel( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PPCIIDEX_LEGACY_CONTROLLER_INTERFACE ControllerInferface, + _In_ PATA_LEGACY_CHANNEL LegacyChannel, + _In_ PCM_RESOURCE_LIST ResourceList, + _In_ ULONG ResourceListSize) +{ + BOOLEAN IsAddressMmio[2]; + PDEVICE_OBJECT PhysicalDeviceObject; + PVOID ControllerContext; + IDE_REGISTERS Registers; + NTSTATUS Status; + ULONG Spare; + + DPRINT("IDE Channel IO %lx, Irq %lu\n", LegacyChannel->IoBase, LegacyChannel->Irq); + + if (!AtaLegacyChannelBuildResources(LegacyChannel, ResourceList)) + { + DPRINT("Failed to build the resource list\n"); + return FALSE; + } + + if (!AtaLegacyClaimHardwareResources(DriverObject, ResourceList, ResourceListSize)) + { + DPRINT("Failed to claim resources\n"); + return FALSE; + } + + Status = STATUS_SUCCESS; + IsAddressMmio[0] = FALSE; + IsAddressMmio[1] = FALSE; + + if (IsNEC_98) + { + /* No translation required for the C-Bus I/O space */ + Registers.Data = (PVOID)0x640; + Registers.Control = (PVOID)0x74C; + + Spare = 2; + } + else + { + Registers.Data = AtaLegacyTranslateBusAddress(LegacyChannel->IoBase, + 8, + &IsAddressMmio[0]); + if (!Registers.Data) + { + DPRINT("Failed to map command port\n"); + + Status = STATUS_UNSUCCESSFUL; + goto ReleaseResources; + } + + Registers.Control = AtaLegacyTranslateBusAddress(LegacyChannel->IoBase + 0x206, + 1, + &IsAddressMmio[1]); + if (!Registers.Control) + { + DPRINT("Failed to map control port\n"); + + Status = STATUS_UNSUCCESSFUL; + goto ReleaseResources; + } + + Spare = 1; + } + Registers.Error = (PVOID)((ULONG_PTR)Registers.Data + 1 * Spare); + Registers.SectorCount = (PVOID)((ULONG_PTR)Registers.Data + 2 * Spare); + Registers.LbaLow = (PVOID)((ULONG_PTR)Registers.Data + 3 * Spare); + Registers.LbaMid = (PVOID)((ULONG_PTR)Registers.Data + 4 * Spare); + Registers.LbaHigh = (PVOID)((ULONG_PTR)Registers.Data + 5 * Spare); + Registers.Device = (PVOID)((ULONG_PTR)Registers.Data + 6 * Spare); + Registers.Status = (PVOID)((ULONG_PTR)Registers.Data + 7 * Spare); + + if (!AtaLegacyChannelPresent(&Registers)) + { + DPRINT("No IDE devices found\n"); + Status = STATUS_UNSUCCESSFUL; + } + +ReleaseResources: + AtaLegacyReleaseHardwareResources(DriverObject); + + if (!NT_SUCCESS(Status)) + goto Cleanup; + + PhysicalDeviceObject = NULL; + Status = IoReportDetectedDevice(DriverObject, + InterfaceTypeUndefined, + (ULONG)-1, + (ULONG)-1, + ResourceList, + NULL, + 0, + &PhysicalDeviceObject); + if (!NT_SUCCESS(Status)) + { + DPRINT("IoReportDetectedDevice() failed with status 0x%lx\n", Status); + goto Cleanup; + } + + if (ControllerInferface->Version != PCIIDEX_INTERFACE_VERSION) + { + /* ReactOS-specific: Retrieve the interface for legacy device detection */ + Status = PciIdeXInitialize(DriverObject, + NULL, + (PVOID)ControllerInferface, + PCIIDEX_GET_CONTROLLER_INTERFACE_SIGNATURE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to register the legacy channel 0x%lx, status 0x%lx\n", + LegacyChannel->IoBase, Status); + goto Cleanup; + } + + if (ControllerInferface->Version != PCIIDEX_INTERFACE_VERSION) + { + DPRINT1("Unknown interface version 0x%lx\n", ControllerInferface->Version); + Status = STATUS_REVISION_MISMATCH; + goto Cleanup; + } + } + + Status = ControllerInferface->AddDevice(DriverObject, PhysicalDeviceObject, &ControllerContext); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to add the legacy channel 0x%lx, status 0x%lx\n", + LegacyChannel->IoBase, Status); + goto Cleanup; + } + + AtaLegacyChannelTranslateResources(ResourceList, Registers.Data, Registers.Control); + + Status = ControllerInferface->StartDevice(ControllerContext, ResourceList); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to start the legacy channel 0x%lx, status 0x%lx\n", + LegacyChannel->IoBase, Status); + + ControllerInferface->RemoveDevice(ControllerContext); + } + +Cleanup: + if (IsAddressMmio[0] && Registers.Data) + { + MmUnmapIoSpace(Registers.Data, 8); + } + + if (IsAddressMmio[1] && Registers.Control) + { + MmUnmapIoSpace(Registers.Control, 1); + } + + return NT_SUCCESS(Status); +} + +static +CODE_SEG("INIT") +NTSTATUS +AtaOpenRegistryKey( + _Out_ PHANDLE KeyHandle, + _In_ HANDLE RootKey, + _In_ PUNICODE_STRING KeyName) +{ + OBJECT_ATTRIBUTES ObjectAttributes; + + PAGED_CODE(); + + InitializeObjectAttributes(&ObjectAttributes, + KeyName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + RootKey, + NULL); + return ZwOpenKey(KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes); +} + +static +CODE_SEG("INIT") +BOOLEAN +AtaLegacyShouldDetectChannels( + _In_ PUNICODE_STRING RegistryPath) +{ + NTSTATUS Status; + ULONG KeyValue; + BOOLEAN PerformDetection; + HANDLE SoftKeyHandle, ParamsKeyHandle; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + UNICODE_STRING ParametersKeyName = RTL_CONSTANT_STRING(L"Parameters"); + static const WCHAR MapperKeyPath[] = + L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Pnp"; + + PAGED_CODE(); + + /* + * Read the firmware mapper key. If the firmware mapper is disabled, + * it is the responsibility of PnP drivers (ACPI, PCI, and others) + * to detect and enumerate IDE channels. + */ + KeyValue = 0; + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + QueryTable[0].Name = L"DisableFirmwareMapper"; + QueryTable[0].EntryContext = &KeyValue; + Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MapperKeyPath, QueryTable, NULL, NULL); + if (NT_SUCCESS(Status) && (KeyValue != 0)) + { + DPRINT("Skipping legacy detection on a PnP system\n"); + return FALSE; + } + + /* Open the driver's software key */ + Status = AtaOpenRegistryKey(&SoftKeyHandle, NULL, RegistryPath); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to open the '%wZ' key, status 0x%lx\n", RegistryPath, Status); + return FALSE; + } + + /* Open the 'Parameters' key */ + Status = AtaOpenRegistryKey(&ParamsKeyHandle, SoftKeyHandle, &ParametersKeyName); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to open the 'Parameters' key, status 0x%lx\n", Status); + + ZwClose(SoftKeyHandle); + return FALSE; + } + + /* Check whether it is the first time we detect IDE channels */ + KeyValue = 0; + RtlZeroMemory(QueryTable, sizeof(QueryTable)); + QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[0].Name = L"LegacyDetection"; + QueryTable[0].EntryContext = &KeyValue; + RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + (PWSTR)ParamsKeyHandle, + QueryTable, + NULL, + NULL); + PerformDetection = (KeyValue != 0); + + /* Do not detect devices again on subsequent boots after driver installation */ + if (PerformDetection) + { + KeyValue = 0; + RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + (PWSTR)ParamsKeyHandle, + L"LegacyDetection", + REG_DWORD, + &KeyValue, + sizeof(KeyValue)); + } + + ZwClose(ParamsKeyHandle); + ZwClose(SoftKeyHandle); + + return PerformDetection; +} + +CODE_SEG("INIT") +NTSTATUS +NTAPI +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath) +{ + PCIIDEX_LEGACY_CONTROLLER_INTERFACE ControllerInferface = { 0 }; + ATA_LEGACY_CHANNEL LegacyChannel[4 + 1] = { 0 }; + PCM_RESOURCE_LIST ResourceList; + ULONG i, ListSize, ResourceCount; + NTSTATUS Status; + + if (!AtaLegacyShouldDetectChannels(RegistryPath)) + return STATUS_CANCELLED; + + if (IsNEC_98) + { + /* Internal IDE interface */ + LegacyChannel[0].IoBase = 0x640; + LegacyChannel[0].Irq = 9; + + ResourceCount = CHANNEL_PC98_RESOURCE_COUNT; + } + else + { + /* Primary IDE channel */ + LegacyChannel[0].IoBase = 0x1F0; + LegacyChannel[0].Irq = 14; + + /* Secondary IDE channel */ + LegacyChannel[1].IoBase = 0x170; + LegacyChannel[1].Irq = 15; + + /* Tertiary IDE channel */ + LegacyChannel[2].IoBase = 0x1E8; + LegacyChannel[2].Irq = 11; + + /* Quaternary IDE channel */ + LegacyChannel[3].IoBase = 0x168; + LegacyChannel[3].Irq = 10; + + ResourceCount = CHANNEL_PCAT_RESOURCE_COUNT; + } + + ListSize = FIELD_OFFSET(CM_RESOURCE_LIST, List[0].PartialResourceList.PartialDescriptors) + + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * ResourceCount; + + ResourceList = ExAllocatePoolZero(PagedPool, ListSize, ATAPORT_TAG); + if (!ResourceList) + { + DPRINT1("Failed to allocate the resource list\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + ResourceList->Count = 1; + ResourceList->List[0].InterfaceType = Isa; + ResourceList->List[0].BusNumber = 0; + ResourceList->List[0].PartialResourceList.Version = 1; + ResourceList->List[0].PartialResourceList.Revision = 1; + ResourceList->List[0].PartialResourceList.Count = ResourceCount; + + Status = STATUS_NOT_FOUND; + + for (i = 0; LegacyChannel[i].IoBase != 0; ++i) + { + if (AtaLegacyDetectChannel(DriverObject, + &ControllerInferface, + &LegacyChannel[i], + ResourceList, + ListSize)) + { + Status = STATUS_SUCCESS; + } + } + + ExFreePoolWithTag(ResourceList, ATAPORT_TAG); + return Status; +} diff --git a/drivers/storage/ide/legide/legide.rc b/drivers/storage/ide/legide/legide.rc new file mode 100644 index 00000000000..78229861142 --- /dev/null +++ b/drivers/storage/ide/legide/legide.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "Legacy PATA Bus Enumerator Driver" +#define REACTOS_STR_INTERNAL_NAME "legide" +#define REACTOS_STR_ORIGINAL_FILENAME "legide.sys" +#include diff --git a/drivers/storage/ide/legide/legide_reg.inf b/drivers/storage/ide/legide/legide_reg.inf new file mode 100644 index 00000000000..593c43d9e68 --- /dev/null +++ b/drivers/storage/ide/legide/legide_reg.inf @@ -0,0 +1,9 @@ +; Legacy PATA Bus Enumerator Driver +[AddReg] +HKLM,"SYSTEM\CurrentControlSet\Services\legide","ErrorControl",0x00010001,0x00000003 +HKLM,"SYSTEM\CurrentControlSet\Services\legide","Group",0x00000000,"SCSI Miniport" +HKLM,"SYSTEM\CurrentControlSet\Services\legide","ImagePath",0x00020000,"system32\drivers\legide.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\legide","Start",0x00010001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\legide","Type",0x00010001,0x00000001 +HKLM,"SYSTEM\CurrentControlSet\Services\legide","Tag",0x00010001,0x00000021 +HKLM,"SYSTEM\CurrentControlSet\Services\legide\Parameters","LegacyDetection",0x00010001,0x00000001 diff --git a/drivers/storage/ide/pciide/pciide.c b/drivers/storage/ide/pciide/pciide.c index fc1bb902978..2529fa04f0b 100644 --- a/drivers/storage/ide/pciide/pciide.c +++ b/drivers/storage/ide/pciide/pciide.c @@ -3,7 +3,7 @@ * PROJECT: PCI IDE bus driver * FILE: drivers/storage/pciide/pciide.c * PURPOSE: Main file - * PROGRAMMERS: Hervé Poussineau (hpoussin@reactos.org) + * PROGRAMMERS: HervĂ© Poussineau (hpoussin@reactos.org) */ #include "pciide.h" @@ -16,34 +16,8 @@ PciIdeChannelEnabled( IN PVOID DeviceExtension, IN ULONG Channel) { - PCI_COMMON_CONFIG PciConfig; - NTSTATUS Status; - DPRINT("PciIdeChannelEnabled(%p, %lu)\n", DeviceExtension, Channel); - Status = PciIdeXGetBusData( - DeviceExtension, - &PciConfig, - 0, - PCI_COMMON_HDR_LENGTH); - if (!NT_SUCCESS(Status)) - { - DPRINT("PciIdeXGetBusData() failed with status 0x%08lx\n", Status); - return ChannelStateUnknown; - } - - if (PCI_CONFIGURATION_TYPE(&PciConfig) != PCI_DEVICE_TYPE) - { - DPRINT("Wrong PCI card type. Disabling IDE channel #%lu\n", Channel); - return ChannelDisabled; - } - - if (PciConfig.BaseClass != PCI_CLASS_MASS_STORAGE_CTLR || PciConfig.SubClass != PCI_SUBCLASS_MSC_IDE_CTLR) - { - DPRINT("Wrong PCI card base class/sub class. Disabling IDE channel #%lu\n", Channel); - return ChannelDisabled; - } - return ChannelStateUnknown; } @@ -51,24 +25,9 @@ BOOLEAN NTAPI PciIdeSyncAccessRequired( IN PVOID DeviceExtension) { - DPRINT1("PciIdeSyncAccessRequired %p\n", DeviceExtension); + DPRINT("PciIdeSyncAccessRequired %p\n", DeviceExtension); - return FALSE; /* FIXME */ -} - -NTSTATUS NTAPI -PciIdeTransferModeSelect( - IN PVOID DeviceExtension, - IN PPCIIDE_TRANSFER_MODE_SELECT XferMode) -{ - ULONG i; - - DPRINT1("PciIdeTransferModeSelect(%p %p)\n", DeviceExtension, XferMode); - - for (i = 0; i < MAX_IDE_DEVICE; i++) - XferMode->DevicePresent[i] = FALSE; /* FIXME */ - - return STATUS_SUCCESS; + return FALSE; } ULONG NTAPI @@ -88,27 +47,40 @@ PciIdeGetControllerProperties( IN PVOID DeviceExtension, OUT PIDE_CONTROLLER_PROPERTIES ControllerProperties) { + ULONG SupportedMode; + USHORT PciCommand; + if (ControllerProperties->Size != sizeof(IDE_CONTROLLER_PROPERTIES)) return STATUS_REVISION_MISMATCH; ControllerProperties->PciIdeChannelEnabled = PciIdeChannelEnabled; ControllerProperties->PciIdeSyncAccessRequired = PciIdeSyncAccessRequired; - ControllerProperties->PciIdeTransferModeSelect = PciIdeTransferModeSelect; ControllerProperties->IgnoreActiveBitForAtaDevice = FALSE; ControllerProperties->AlwaysClearBusMasterInterrupt = TRUE; ControllerProperties->PciIdeUseDma = PciIdeUseDma; ControllerProperties->AlignmentRequirement = 1; - ControllerProperties->DefaultPIO = 0; /* FIXME */ - ControllerProperties->PciIdeUdmaModesSupported = NULL; /* optional */ + + PciIdeXGetBusData(DeviceExtension, + &PciCommand, + FIELD_OFFSET(PCI_COMMON_HEADER, Command), + sizeof(PciCommand)); + if (!(PciCommand & PCI_ENABLE_BUS_MASTER)) + { + SupportedMode = PIO_MODE0 | PIO_MODE1 | PIO_MODE2 | PIO_MODE3 | PIO_MODE4; + } + else + { + SupportedMode = PIO_MODE0 | PIO_MODE1 | PIO_MODE2 | PIO_MODE3 | PIO_MODE4 | + SWDMA_MODE0 | SWDMA_MODE1 | SWDMA_MODE2 | + MWDMA_MODE0 | MWDMA_MODE1 | MWDMA_MODE2 | + UDMA_MODE0 | UDMA_MODE1 | UDMA_MODE2 | + UDMA_MODE3 | UDMA_MODE4 | UDMA_MODE5 | UDMA_MODE6; + } ControllerProperties->SupportedTransferMode[0][0] = ControllerProperties->SupportedTransferMode[0][1] = ControllerProperties->SupportedTransferMode[1][0] = - ControllerProperties->SupportedTransferMode[1][1] = - PIO_MODE0 | PIO_MODE1 | PIO_MODE2 | PIO_MODE3 | PIO_MODE4 | - SWDMA_MODE0 | SWDMA_MODE1 | SWDMA_MODE2 | - MWDMA_MODE0 | MWDMA_MODE1 | MWDMA_MODE2 | - UDMA_MODE0 | UDMA_MODE1 | UDMA_MODE2 | UDMA_MODE3 | UDMA_MODE4; + ControllerProperties->SupportedTransferMode[1][1] = SupportedMode; return STATUS_SUCCESS; } diff --git a/drivers/storage/ide/pciidex/CMakeLists.txt b/drivers/storage/ide/pciidex/CMakeLists.txt index 492ea66bd11..c7a85aecd58 100644 --- a/drivers/storage/ide/pciidex/CMakeLists.txt +++ b/drivers/storage/ide/pciidex/CMakeLists.txt @@ -2,12 +2,36 @@ spec2def(pciidex.sys pciidex.spec ADD_IMPORTLIB) list(APPEND SOURCE + chipset/ahci_generic.c + chipset/ahci_hw.c + chipset/ahci_io.c + chipset/amd.c + chipset/ati.c + chipset/cmd.c + chipset/intel.c + chipset/pata_generic.c + chipset/pata_hw.c + chipset/pata_io.c + chipset/pctech.c + chipset/sil680.c + chipset/svw_pata.c + chipset/svw_sata.c + chipset/toshiba.c + chipset/via.c + acpi.c + ahci.h + debug.h fdo.c miniport.c pciidex.c + pciidex.h pdo.c - power.c - pciidex.h) + power.c) + +if(ARCH STREQUAL "i386") + add_definitions(-DATA_DETECT_LEGACY_DEVICES) + list(APPEND SOURCE chipset/pata_legacy.c) +endif() add_library(pciidex MODULE ${SOURCE} diff --git a/drivers/storage/ide/pciidex/acpi.c b/drivers/storage/ide/pciidex/acpi.c new file mode 100644 index 00000000000..80cfe55f27a --- /dev/null +++ b/drivers/storage/ide/pciidex/acpi.c @@ -0,0 +1,337 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: ACPI interface with SATA ports, IDE controllers and drives + * COPYRIGHT: Copyright 2026 + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +#include + +/* FUNCTIONS ******************************************************************/ + +_IRQL_requires_max_(DISPATCH_LEVEL) +static +NTSTATUS +AtaAcpiEvaluateObject( + _In_ PDEVICE_OBJECT DeviceObject, + _In_reads_bytes_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PACPI_EVAL_OUTPUT_BUFFER OutputBuffer, + _In_ ULONG OutputBufferLength) +{ + PIRP Irp; + PIO_STACK_LOCATION IoStack; + KEVENT Event; + NTSTATUS Status; + PDEVICE_OBJECT TopDeviceObject; + + /* Get the ACPI bus filter device for this DO */ + TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject); + + /* + * We could be called at DISPATCH_LEVEL, + * so use IoAllocateIrp() rather going through IoBuildDeviceIoControlRequest(). + */ + Irp = IoAllocateIrp(TopDeviceObject->StackSize, 0); + if (!Irp) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + Irp->AssociatedIrp.SystemBuffer = InputBuffer; + Irp->UserBuffer = OutputBuffer; + Irp->Flags |= IRP_BUFFERED_IO | IRP_INPUT_OPERATION; + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + + /* + * Submit an asynchronous evaluation request. + * Note that _STM should be evaluated while the device queue has been paused, + * so we must not cause *any* page faults and use IOCTL_ACPI_EVAL_METHOD. + * AtaAcpiEvaluateObject() also has to be non-pageable. + */ + IoStack = IoGetNextIrpStackLocation(Irp); + IoStack->MajorFunction = IRP_MJ_DEVICE_CONTROL; + IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_ACPI_ASYNC_EVAL_METHOD; + IoStack->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength; + IoStack->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength; + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + IoSetCompletionRoutine(Irp, + PciIdeXPdoCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE); + + Status = IoCallDriver(TopDeviceObject, Irp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = Irp->IoStatus.Status; + } + + RtlCopyMemory(OutputBuffer, InputBuffer, min(Irp->IoStatus.Information, OutputBufferLength)); + + if (OutputBuffer && NT_SUCCESS(Status)) + { + ASSERT(OutputBuffer->Signature == ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE); + + if ((OutputBuffer->Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE) || + (OutputBuffer->Count == 0)) + { + ERR("Invalid ACPI output buffer\n"); + + Status = STATUS_ACPI_INVALID_DATA; + } + } + +Exit: + ObDereferenceObject(TopDeviceObject); + return Status; +} + +BOOLEAN +AtaAcpiGetTimingMode( + _In_ PDEVICE_OBJECT DeviceObject, + _Out_ PIDE_ACPI_TIMING_MODE_BLOCK TimingMode) +{ + UCHAR Buffer[FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) + + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(*TimingMode))]; + ACPI_EVAL_INPUT_BUFFER InputBuffer; + NTSTATUS Status; + PACPI_EVAL_OUTPUT_BUFFER OutputBuffer = (PACPI_EVAL_OUTPUT_BUFFER)Buffer; + + RtlZeroMemory(Buffer, sizeof(Buffer)); + + InputBuffer.MethodNameAsUlong = 'MTG_'; // _GTM + InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE; + + /* Evaluate the _GTM method */ + Status = AtaAcpiEvaluateObject(DeviceObject, + &InputBuffer, + sizeof(InputBuffer), + OutputBuffer, + sizeof(Buffer)); + + if (!NT_SUCCESS(Status)) + { + ASSERT(Status != STATUS_BUFFER_OVERFLOW); + + TRACE("Failed to evaluate the _GTM method, status 0x%lx\n", Status); + return FALSE; + } + + if (OutputBuffer->Argument[0].DataLength < sizeof(*TimingMode)) + { + ERR("Buffer too small, size %u/%u\n", + OutputBuffer->Argument[0].DataLength, sizeof(*TimingMode)); + return FALSE; + } + + if (OutputBuffer->Argument[0].Type != ACPI_METHOD_ARGUMENT_BUFFER) + { + ERR("Unexpected method type %u\n", OutputBuffer->Argument[0].Type); + return FALSE; + } + + RtlCopyMemory(TimingMode, OutputBuffer->Argument[0].Data, sizeof(*TimingMode)); + + return TRUE; +} + +NTSTATUS +AtaAcpiSetTimingMode( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIDE_ACPI_TIMING_MODE_BLOCK TimingMode, + _In_opt_ PIDENTIFY_DEVICE_DATA IdBlock1, + _In_opt_ PIDENTIFY_DEVICE_DATA IdBlock2) +{ + PACPI_EVAL_INPUT_BUFFER_COMPLEX InputBuffer; + PACPI_METHOD_ARGUMENT Argument; + NTSTATUS Status; + ULONG InputSize; + + InputSize = FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument) + + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(*TimingMode)) + + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(*IdBlock1)) + + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(*IdBlock2)); + + InputBuffer = ExAllocatePoolUninitialized(NonPagedPool, InputSize, TAG_PCIIDEX); + if (!InputBuffer) + { + ERR("Failed to allocate memory\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + InputBuffer->MethodNameAsUlong = 'MTS_'; // _STM + InputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE; + InputBuffer->ArgumentCount = 3; + InputBuffer->Size = InputSize; + + /* Argument 1: The channel timing information block */ + Argument = InputBuffer->Argument; + ACPI_METHOD_SET_ARGUMENT_BUFFER(Argument, TimingMode, sizeof(*TimingMode)); + + /* Argument 2: The ATA drive ID block */ + Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument); + Argument->Type = ACPI_METHOD_ARGUMENT_BUFFER; + Argument->DataLength = sizeof(*IdBlock1); + if (IdBlock1) + RtlCopyMemory(Argument->Data, IdBlock1, sizeof(*IdBlock1)); + else + RtlZeroMemory(Argument->Data, sizeof(*IdBlock1)); + + /* Argument 3: The ATA drive ID block */ + Argument = ACPI_METHOD_NEXT_ARGUMENT(Argument); + Argument->Type = ACPI_METHOD_ARGUMENT_BUFFER; + Argument->DataLength = sizeof(*IdBlock2); + if (IdBlock2) + RtlCopyMemory(Argument->Data, IdBlock2, sizeof(*IdBlock2)); + else + RtlZeroMemory(Argument->Data, sizeof(*IdBlock2)); + + /* Evaluate the _STM method */ + Status = AtaAcpiEvaluateObject(DeviceObject, InputBuffer, InputSize, NULL, 0); + if (!NT_SUCCESS(Status)) + { + if (Status == STATUS_OBJECT_NAME_NOT_FOUND) + INFO("Failed to set transfer timings, status 0x%lx\n", Status); + else + ERR("Failed to set transfer timings, status 0x%lx\n", Status); + } + + ExFreePoolWithTag(InputBuffer, TAG_PCIIDEX); + return Status; +} + +CODE_SEG("PAGE") +PVOID +AtaAcpiGetTaskFile( + _In_ PDEVICE_OBJECT DeviceObject) +{ + ACPI_EVAL_INPUT_BUFFER InputBuffer; + PACPI_EVAL_OUTPUT_BUFFER OutputBuffer; + ULONG RetryCount, OutputSize; + NTSTATUS Status; + + PAGED_CODE(); + /* + * We invoke this routine within the PnP START handler only, + * and thus specify that the code is in a pageable section. + */ + ASSERT(PsGetCurrentProcess() == PsInitialSystemProcess); + + InputBuffer.MethodNameAsUlong = 'FTG_'; // _GTF + InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE; + + /* + * The output buffer must be large enough to hold the list of ATA commands to the drive. + * We assume that 10 commands is the common case. + */ + OutputSize = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) + + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ATA_ACPI_TASK_FILE) * 10); + + for (RetryCount = 0; RetryCount < 2; ++RetryCount) + { + OutputBuffer = ExAllocatePoolZero(NonPagedPool, OutputSize, TAG_PCIIDEX); + if (!OutputBuffer) + { + ERR("Failed to allocate memory of size %lu\n", OutputSize); + return NULL; + } + + /* Evaluate the _GTF method */ + Status = AtaAcpiEvaluateObject(DeviceObject, + &InputBuffer, + sizeof(InputBuffer), + OutputBuffer, + OutputSize); + + /* Increase the allocation size if it's too small */ + if (Status == STATUS_BUFFER_OVERFLOW) + { + OutputSize = OutputBuffer->Length; + + ExFreePoolWithTag(OutputBuffer, TAG_PCIIDEX); + continue; + } + + break; + } + + if (!NT_SUCCESS(Status)) + goto Cleanup; + + if (OutputBuffer->Argument[0].Type != ACPI_METHOD_ARGUMENT_BUFFER) + { + ERR("Unexpected method type %u\n", OutputBuffer->Argument[0].Type); + goto Cleanup; + } + + if (OutputBuffer->Argument[0].DataLength % sizeof(ATA_ACPI_TASK_FILE)) + { + ERR("Incorrect command stream length %u\n", OutputBuffer->Argument[0].DataLength); + goto Cleanup; + } + + if (OutputBuffer->Argument[0].DataLength == 0) + { + WARN("Empty command list\n"); + goto Cleanup; + } + + return OutputBuffer; + +Cleanup: + ExFreePoolWithTag(OutputBuffer, TAG_PCIIDEX); + + return NULL; +} + +CODE_SEG("PAGE") +VOID +AtaAcpiSetDeviceData( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIDENTIFY_DEVICE_DATA IdBlock) +{ + PACPI_EVAL_INPUT_BUFFER_COMPLEX InputBuffer; + PACPI_METHOD_ARGUMENT Argument; + NTSTATUS Status; + ULONG InputSize; + + PAGED_CODE(); + ASSERT(PsGetCurrentProcess() == PsInitialSystemProcess); + + InputSize = FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument) + + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(*IdBlock)); + + InputBuffer = ExAllocatePoolUninitialized(NonPagedPool, InputSize, TAG_PCIIDEX); + if (!InputBuffer) + { + ERR("Failed to allocate memory\n"); + return; + } + InputBuffer->MethodNameAsUlong = 'DDS_'; // _SDD + InputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE; + InputBuffer->ArgumentCount = 1; + InputBuffer->Size = InputSize; + + /* Argument 1: The ATA drive ID block */ + Argument = InputBuffer->Argument; + ACPI_METHOD_SET_ARGUMENT_BUFFER(Argument, IdBlock, sizeof(*IdBlock)); + + /* Evaluate the _SDD method */ + Status = AtaAcpiEvaluateObject(DeviceObject, InputBuffer, InputSize, NULL, 0); + if (!NT_SUCCESS(Status)) + { + if (Status == STATUS_OBJECT_NAME_NOT_FOUND) + TRACE("Failed to set device data, status 0x%lx\n", Status); + else + WARN("Failed to set device data, status 0x%lx\n", Status); + } + + ExFreePoolWithTag(InputBuffer, TAG_PCIIDEX); +} diff --git a/drivers/storage/ide/pciidex/ahci.h b/drivers/storage/ide/pciidex/ahci.h new file mode 100644 index 00000000000..c7684a260cd --- /dev/null +++ b/drivers/storage/ide/pciidex/ahci.h @@ -0,0 +1,563 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: AHCI header file + * COPYRIGHT: Copyright 2026 + */ + +#pragma once + +#define AHCI_MAX_PORTS 32 +#define AHCI_MAX_PORT_DEVICES 1 +#define AHCI_MAX_PMP_DEVICES 15 +#define AHCI_MAX_COMMAND_SLOTS 32 + +#define AHCI_MAX_PRDT_ENTRIES 0x000FFFFF // 65535 +#define AHCI_MAX_PRD_LENGTH 0x003FFFFF // 4MB + +#define AHCI_PMP_CONTROL_PORT 15 + +#define AHCI_COMMAND_TABLE_ALIGNMENT 128 +#define AHCI_RECEIVED_FIS_ALIGNMENT 256 +#define AHCI_COMMAND_LIST_ALIGNMENT 1024 +#define AHCI_RECEIVED_FIS_FBS_ALIGNMENT 4096 + +#define AHCI_FBS_RECEIVE_AREA_SIZE 4096 + +#define AHCI_DELAY_1_SECOND (1000 / PORT_TIMER_TICK_MS) +#define AHCI_DELAY_CR_START_STOP (500 / PORT_TIMER_TICK_MS) +#define AHCI_DELAY_FR_START_STOP (500 / PORT_TIMER_TICK_MS) +#define AHCI_DELAY_DET_PRESENCE (40 / PORT_TIMER_TICK_MS) +#define AHCI_DELAY_DET_STABLE (200 / PORT_TIMER_TICK_MS) +#define AHCI_DELAY_READY_DRIVE (10000 / PORT_TIMER_TICK_MS) +#define AHCI_DELAY_PMP_READY_DRIVE (400 / PORT_TIMER_TICK_MS) +#define AHCI_DELAY_PMP_DET_PRESENSE (100 / PORT_TIMER_TICK_MS) +#define AHCI_DELAY_PMP_DET_STABLE (300 / PORT_TIMER_TICK_MS) +#define AHCI_DELAY_CLO_CLEAR (10 / PORT_TIMER_TICK_MS) +#define AHCI_DELAY_INTERFACE_CHANGE (10 / PORT_TIMER_TICK_MS) + +#define AHCI_FIS_REGISTER_HOST_TO_DEVICE 0x27 +#define AHCI_FIS_REGISTER_DEVICE_TO_HOST 0x34 +#define AHCI_FIS_DMA_ACTIVATE_DEVICE_TO_HOST 0x39 +#define AHCI_FIS_DMA_SETUP 0x41 +#define AHCI_FIS_DATA 0x46 +#define AHCI_FIS_BIST_ACTIVATE 0x58 +#define AHCI_FIS_PIO_SETUP_DEVICE_TO_HOST 0x5F +#define AHCI_FIS_SET_DEVICE_BITS_DEVICE_TO_HOST 0xA1 + +#define IDE_COMMAND_READ_PORT_MULTIPLIER 0xE4 +#define IDE_COMMAND_WRITE_PORT_MULTIPLIER 0xE8 + +typedef enum _SATA_SCR_REGISTER +{ + ATA_SSTATUS = 0, + ATA_SERROR = 1, + ATA_SCONTROL = 2, + ATA_SACTIVE = 3, + ATA_SNOTIFICATION = 4, +} SATA_SCR_REGISTER; + +#define AHCI_INTERNAL_SLOT 0 + +/** + * Port Multiplier Registers + */ +typedef enum _AHCI_PORT_MULTIPLIER_REGISTER +{ + PmpProductId = 0, + PmpRevisionInfo = 1, + PmpPortInfo = 2, + PmpErrorInfo = 32, + PmpErrorControl = 33, + PmpPhyEventCounterControl = 34, + PmpCapabilities = 64, + PmpFeaturesEnabled = 96, +#define AHCI_PMP_BIST 0x00000001 +#define AHCI_PMP_PMREQ 0x00000002 +#define AHCI_PMP_SSC 0x00000004 +#define AHCI_PMP_SNTF 0x00000008 +} AHCI_PORT_MULTIPLIER_REGISTER; + +typedef enum _AHCI_HOST_BUS_ADAPTER_REGISTER +{ + HbaCapabilities = 0x00, + HbaGlobalControl = 0x04, + HbaInterruptStatus = 0x08, + HbaPortBitmap = 0x0C, + HbaAhciVersion = 0x10, + HbaCoalescingControl = 0x14, + HbaCoalescingPorts = 0x18, + HbaEnclosureManagementLocation = 0x1C, + HbaEnclosureManagementControl = 0x20, + HbaCapabilitiesEx = 0x24, + HbaBiosHandoffControl = 0x28, +} AHCI_HOST_BUS_ADAPTER_REGISTER; + +typedef enum _AHCI_PORT_REGISTER +{ + PxCommandListBaseLow = 0x00, + PxCommandListBaseHigh = 0x04, + PxFisBaseLow = 0x08, + PxFisBaseHigh = 0x0C, + PxInterruptStatus = 0x10, + PxInterruptEnable = 0x14, + PxCmdStatus = 0x18, + PxTaskFileData = 0x20, + PxSignature = 0x24, + PxSataStatus = 0x28, + PxSataControl = 0x2C, + PxSataError = 0x30, + PxSataActive = 0x34, + PxCommandIssue = 0x38, + PxSataNotification = 0x3C, + PxFisSwitchingControl = 0x40, + PxDeviceSleep = 0x44, +} AHCI_PORT_REGISTER; + +/* + * Interrupt Status/Enable Register + */ +#define AHCI_PXIRQ_DHRS 0x00000001 +#define AHCI_PXIRQ_PSS 0x00000002 +#define AHCI_PXIRQ_DSS 0x00000004 +#define AHCI_PXIRQ_SDBS 0x00000008 +#define AHCI_PXIRQ_UFS 0x00000010 +#define AHCI_PXIRQ_DPS 0x00000020 +#define AHCI_PXIRQ_PCS 0x00000040 +#define AHCI_PXIRQ_DMPS 0x00000080 +#define AHCI_PXIRQ_RSV1 0x003FFF00 +#define AHCI_PXIRQ_PRCS 0x00400000 +#define AHCI_PXIRQ_IPMS 0x00800000 +#define AHCI_PXIRQ_OFS 0x01000000 +#define AHCI_PXIRQ_RSV2 0x02000000 +#define AHCI_PXIRQ_INFS 0x04000000 +#define AHCI_PXIRQ_IFS 0x08000000 +#define AHCI_PXIRQ_HBDS 0x10000000 +#define AHCI_PXIRQ_HBFS 0x20000000 +#define AHCI_PXIRQ_TFES 0x40000000 +#define AHCI_PXIRQ_CPDS 0x80000000 + +#define AHCI_PXIRQ_FATAL_ERROR \ + (AHCI_PXIRQ_TFES | AHCI_PXIRQ_IFS | AHCI_PXIRQ_HBDS | AHCI_PXIRQ_HBFS) + +#define AHCI_PXIRQ_PORT_STATUS \ + (AHCI_PXIRQ_PCS | AHCI_PXIRQ_PRCS | AHCI_PXIRQ_DMPS) + +/* + * Command and Status Register + */ +#define AHCI_PXCMD_ST 0x00000001 +#define AHCI_PXCMD_SUD 0x00000002 +#define AHCI_PXCMD_POD 0x00000004 +#define AHCI_PXCMD_CLO 0x00000008 +#define AHCI_PXCMD_FRE 0x00000010 +#define AHCI_PXCMD_RSV 0x000000E0 +#define AHCI_PXCMD_CCS_MASK 0x00001F00 +#define AHCI_PXCMD_MPSS 0x00002000 +#define AHCI_PXCMD_FR 0x00004000 +#define AHCI_PXCMD_CR 0x00008000 +#define AHCI_PXCMD_CPS 0x00010000 +#define AHCI_PXCMD_PMA 0x00020000 +#define AHCI_PXCMD_HPCP 0x00040000 +#define AHCI_PXCMD_MPSP 0x00080000 +#define AHCI_PXCMD_CPD 0x00100000 +#define AHCI_PXCMD_ESP 0x00200000 +#define AHCI_PXCMD_FBSCP 0x00400000 +#define AHCI_PXCMD_APSTE 0x00800000 +#define AHCI_PXCMD_ATAPI 0x01000000 +#define AHCI_PXCMD_DLAE 0x02000000 +#define AHCI_PXCMD_ALPE 0x04000000 +#define AHCI_PXCMD_ASP 0x08000000 +#define AHCI_PXCMD_ICC_MASK 0xF0000000 + +#define AHCI_PXCMD_ICC_IDLE 0x00000000 +#define AHCI_PXCMD_ICC_ACTIVE 0x10000000 +#define AHCI_PXCMD_ICC_PARTIAL 0x20000000 +#define AHCI_PXCMD_ICC_SLUMBER 0x60000000 +#define AHCI_PXCMD_ICC_DEVSLEEP 0x80000000 + +#define AHCI_PXCMD_ICC_DEVSLEEP 0x80000000 + +#define AHCI_PXCMD_CCS_SHIFT 8 + +#define AHCI_PXCMD_CCS(Value) (((Value) & AHCI_PXCMD_CCS_MASK) >> AHCI_PXCMD_CCS_SHIFT) + +/* + * Task File Data Register + */ +#define AHCI_PXTFD_STATUS_MASK 0x000000FF +#define AHCI_PXTFD_ERROR_MASK 0x0000FF00 + +#define AHCI_PXTFD_ERROR_SHIFT 8 + +/* + * Signature Register + */ +#define AHCI_PXSIG_INVALID 0xFFFFFFFF +#define AHCI_PXSIG_ATAPI 0xEB140101 +#define AHCI_PXSIG_PMP 0x96690101 + +/* + * Some PATA ATAPI devices (NEC CDR-C251) do not update the Sector Count and LBA Low registers. + * Just to be safe, the contents of these registers is also ignored for AHCI devices. + */ +#define AHCI_PXSIG_MASK 0xFFFF0000 + +/* + * Serial ATA Status Register + */ +#define AHCI_PXSSTS_DET_MASK 0x0000000F +#define AHCI_PXSSTS_SPD_MASK 0x000000F0 +#define AHCI_PXSSTS_IPM_MASK 0x00000F00 + +#define AHCI_PXSSTS_DET_NO_DEVICE 0x00000000 +#define AHCI_PXSSTS_DET_PHY_NOT_READY 0x00000001 +#define AHCI_PXSSTS_DET_PHY_OK 0x00000003 +#define AHCI_PXSSTS_DET_PHY_OFFLINE 0x00000004 + +#define AHCI_PXSSTS_SPD_UNKNOWN 0x00000000 +#define AHCI_PXSSTS_SPD_SATA1 0x00000010 +#define AHCI_PXSSTS_SPD_SATA2 0x00000020 +#define AHCI_PXSSTS_SPD_SATA3 0x00000030 + +#define AHCI_PXSSTS_IPM_NO_DEVICE 0x00000000 +#define AHCI_PXSSTS_IPM_ACTIVE 0x00000100 +#define AHCI_PXSSTS_IPM_PARTIAL 0x00000200 +#define AHCI_PXSSTS_IPM_SLUMBER 0x00000600 +#define AHCI_PXSSTS_IPM_DEVSLEEP 0x00000800 + +/* + * Serial ATA Control Register + */ +#define AHCI_PXCTL_DET_MASK 0x0000000F +#define AHCI_PXCTL_SPD_MASK 0x000000F0 +#define AHCI_PXCTL_IPM_MASK 0x00000F00 + +#define AHCI_PXCTL_DET_IDLE 0x00000000 +#define AHCI_PXCTL_DET_RESET 0x00000001 +#define AHCI_PXCTL_DET_DISABLE_SATA 0x00000004 + +#define AHCI_PXCTL_SPD_LIMIT_NONE 0x00000000 +#define AHCI_PXCTL_SPD_LIMIT_SATA1 0x00000010 +#define AHCI_PXCTL_SPD_LIMIT_SATA2 0x00000020 +#define AHCI_PXCTL_SPD_LIMIT_SATA3 0x00000030 + +#define AHCI_PXCTL_SPD_LIMIT_LEVEL 0x00000010 + +#define AHCI_PXCTL_IPM_DISABLE_NONE 0x00000000 +#define AHCI_PXCTL_IPM_DISABLE_PARTIAL 0x00000100 +#define AHCI_PXCTL_IPM_DISABLE_SLUMBER 0x00000200 +#define AHCI_PXCTL_IPM_DISABLE_DEVSLEEP 0x00000400 + +#define AHCI_PXCTL_IPM_DISABLE_ALL 0x00000700 + +/* + * FIS-based Switching Control Register + */ +#define AHCI_FBS_ENABLE 0x00000001 +#define AHCI_FBS_DEV_ERROR_CLEAR 0x00000002 +#define AHCI_FBS_SINGLE_DEV_ERROR 0x00000004 +#define AHCI_FBS_ISSUE_MASK 0x00000F00 +#define AHCI_FBS_ACTIVE_DEV_OPT_MASK 0x0000F000 +#define AHCI_FBS_DEV_WITH_ERROR_MASK 0x000F0000 + +#define AHCI_FBS_ISSUE_SHIFT 8 + +/* + * Device Sleep Register + */ +#define AHCI_PXDEVSLP_ADSE 0x00000001 +#define AHCI_PXDEVSLP_DSP 0x00000002 +#define AHCI_PXDEVSLP_DETO_MASK 0x000003FC +#define AHCI_PXDEVSLP_MDAT_MASK 0x00007C00 +#define AHCI_PXDEVSLP_DITO_MASK 0x01FF8000 +#define AHCI_PXDEVSLP_DM_MASK 0x1E000000 + +#define AHCI_PORT_INTERRUPT_MASK \ + (AHCI_PXIRQ_DHRS | \ + AHCI_PXIRQ_PSS | \ + AHCI_PXIRQ_DSS | \ + AHCI_PXIRQ_SDBS | \ + AHCI_PXIRQ_UFS | \ + AHCI_PXIRQ_DPS | \ + AHCI_PXIRQ_PCS | \ + AHCI_PXIRQ_DMPS | \ + AHCI_PXIRQ_PRCS | \ + AHCI_PXIRQ_IPMS | \ + AHCI_PXIRQ_OFS | \ + AHCI_PXIRQ_INFS | \ + AHCI_PXIRQ_IFS | \ + AHCI_PXIRQ_HBDS | \ + AHCI_PXIRQ_HBFS | \ + AHCI_PXIRQ_TFES | \ + AHCI_PXIRQ_CPDS) + +/* + * AHCI Version Register + */ +#define AHCI_VERSION_0_95 0x00000905 +#define AHCI_VERSION_1_0 0x00010000 +#define AHCI_VERSION_1_2 0x00010200 +#define AHCI_VERSION_1_3_0 0x00010300 +#define AHCI_VERSION_1_3_1 0x00010301 + +/* + * Global HBA Control Register + */ +#define AHCI_GHC_HR 0x00000001 +#define AHCI_GHC_IE 0x00000002 +#define AHCI_GHC_MRSM 0x00000004 +#define AHCI_GHC_AE 0x80000000 + +/* + * HBA Capabilities Register + */ +#define AHCI_CAP_NP 0x0000001F +#define AHCI_CAP_SXS 0x00000020 +#define AHCI_CAP_EMS 0x00000040 +#define AHCI_CAP_CCCS 0x00000080 +#define AHCI_CAP_NCS 0x00001F00 +#define AHCI_CAP_PSC 0x00002000 +#define AHCI_CAP_SSC 0x00004000 +#define AHCI_CAP_PMD 0x00008000 +#define AHCI_CAP_FBSS 0x00010000 +#define AHCI_CAP_SPM 0x00020000 +#define AHCI_CAP_SAM 0x00040000 +#define AHCI_CAP_RSV 0x00080000 +#define AHCI_CAP_ISS 0x00F00000 +#define AHCI_CAP_SCLO 0x01000000 +#define AHCI_CAP_SAL 0x02000000 +#define AHCI_CAP_SALP 0x04000000 +#define AHCI_CAP_SSS 0x08000000 +#define AHCI_CAP_SMPS 0x10000000 +#define AHCI_CAP_SSNTF 0x20000000 +#define AHCI_CAP_SNCQ 0x40000000 +#define AHCI_CAP_S64A 0x80000000 + +/* + * HBA Capabilities Extended Register + */ +#define AHCI_CAP2_BOH 0x00000001 +#define AHCI_CAP2_NVMP 0x00000002 +#define AHCI_CAP2_APST 0x00000004 +#define AHCI_CAP2_SDS 0x00000008 +#define AHCI_CAP2_SADM 0x00000010 +#define AHCI_CAP2_DESO 0x00000020 + +#define AHCI_BOHC_BIOS_SEMAPHORE 0x00000001 +#define AHCI_BOHC_OS_SEMAPHORE 0x00000002 +#define AHCI_BOHC_SMI_ON_OS_OWNERSHIP_CHANGE 0x00000004 +#define AHCI_BOHC_OS_OWNERSHIP_CHANGE 0x00000008 +#define AHCI_BOHC_BIOS_BUSY 0x00000010 + +#include + +typedef struct _AHCI_FIS_HOST_TO_DEVICE +{ + UCHAR Type; // 0x27 + + UCHAR Flags; +#define UPDATE_COMMAND 0x80 +#define PMP_NUMBER 0x0F + + UCHAR Command; + UCHAR Features; + UCHAR LbaLow; + UCHAR LbaMid; + UCHAR LbaHigh; + UCHAR Device; + UCHAR LbaLowEx; + UCHAR LbaMidEx; + UCHAR LbaHighEx; + UCHAR FeaturesEx; + UCHAR SectorCount; + UCHAR SectorCountEx; + UCHAR Icc; + UCHAR Control; + ULONG Auxiliary; +} AHCI_FIS_HOST_TO_DEVICE, *PAHCI_FIS_HOST_TO_DEVICE; + +C_ASSERT(sizeof(AHCI_FIS_HOST_TO_DEVICE) == 20); + +typedef struct _AHCI_FIS_PIO_SETUP +{ + UCHAR Type; // 0x5F + + UCHAR Flags; + + UCHAR Status; + UCHAR Error; + UCHAR LbaLow; + UCHAR LbaMid; + UCHAR LbaHigh; + UCHAR Device; + UCHAR LbaLowEx; + UCHAR LbaMidEx; + UCHAR LbaHighEx; + UCHAR Reserved; + UCHAR SectorCount; + UCHAR SectorCountEx; + UCHAR Reserved1; + UCHAR EStatus; + USHORT TransferCount; + USHORT Reserved2; +} AHCI_FIS_PIO_SETUP, *PAHCI_FIS_PIO_SETUP; + +C_ASSERT(sizeof(AHCI_FIS_PIO_SETUP) == 20); + +typedef struct _AHCI_FIS_DEVICE_TO_HOST +{ + UCHAR Type; // 0x34 + + UCHAR Flags; + + UCHAR Status; + UCHAR Error; + UCHAR LbaLow; + UCHAR LbaMid; + UCHAR LbaHigh; + UCHAR Device; + UCHAR LbaLowEx; + UCHAR LbaMidEx; + UCHAR LbaHighEx; + UCHAR Reserved; + UCHAR SectorCount; + UCHAR SectorCountEx; + UCHAR Reserved1; + UCHAR Reserved2; + ULONG Reserved3; +} AHCI_FIS_DEVICE_TO_HOST, *PAHCI_FIS_DEVICE_TO_HOST; + +C_ASSERT(sizeof(AHCI_FIS_DEVICE_TO_HOST) == 20); + +typedef struct _AHCI_FIS_SET_DEVICE_BITS +{ + UCHAR Type; // 0xA1 + + UCHAR Flags; + + UCHAR Status; + UCHAR Error; + ULONG Reserved; +} AHCI_FIS_SET_DEVICE_BITS, *PAHCI_FIS_SET_DEVICE_BITS; + +C_ASSERT(sizeof(AHCI_FIS_SET_DEVICE_BITS) == 8); + +typedef struct _AHCI_RECEIVED_FIS +{ + UCHAR DmaSetupFis[0x1C]; + ULONG Reserved; + + AHCI_FIS_PIO_SETUP PioSetupFis; + ULONG Reserved2[3]; + + AHCI_FIS_DEVICE_TO_HOST DeviceToHostFis; + ULONG Reserved3; + + AHCI_FIS_SET_DEVICE_BITS SetDeviceBitsFis; + + UCHAR UnknownFis[0x40]; + + UCHAR Reserved4[0x60]; +} AHCI_RECEIVED_FIS, *PAHCI_RECEIVED_FIS; + +C_ASSERT(sizeof(AHCI_RECEIVED_FIS) == 256); + +typedef struct _AHCI_COMMAND_HEADER +{ + ULONG Control; +#define AHCI_COMMAND_HEADER_COMMAND_FIS_LENGTH 0x0000001F +#define AHCI_COMMAND_HEADER_ATAPI 0x00000020 +#define AHCI_COMMAND_HEADER_WRITE 0x00000040 +#define AHCI_COMMAND_HEADER_PREFETCHABLE 0x00000080 +#define AHCI_COMMAND_HEADER_RESET 0x00000100 +#define AHCI_COMMAND_HEADER_BIST 0x00000200 +#define AHCI_COMMAND_HEADER_CLEAR_BUSY_UPON_OK 0x00000400 +#define AHCI_COMMAND_HEADER_PMP 0x0000F000 +#define AHCI_COMMAND_HEADER_PRDT_LENGTH 0xFFFF0000 + +#define AHCI_COMMAND_HEADER_PRDT_LENGTH_SHIFT 16 +#define AHCI_COMMAND_HEADER_PMP_SHIFT 12 + + ULONG PrdByteCount; + ULONG CommandTableBaseLow; + ULONG CommandTableBaseHigh; + ULONG Reserved[4]; +} AHCI_COMMAND_HEADER, *PAHCI_COMMAND_HEADER; + +C_ASSERT(sizeof(AHCI_COMMAND_HEADER) == 32); + +typedef struct _AHCI_COMMAND_LIST +{ + AHCI_COMMAND_HEADER CommandHeader[ANYSIZE_ARRAY]; +} AHCI_COMMAND_LIST, *PAHCI_COMMAND_LIST; + +typedef struct _AHCI_PRD_TABLE_ENTRY +{ + ULONG DataBaseLow; + ULONG DataBaseHigh; + ULONG Reserved; + + ULONG ByteCount; +#define AHCI_PRD_INTERRUPT_ON_COMPLETION 0x80000000 + +} AHCI_PRD_TABLE_ENTRY, *PAHCI_PRD_TABLE_ENTRY; + +C_ASSERT(sizeof(AHCI_PRD_TABLE_ENTRY) == 16); + +typedef struct _AHCI_COMMAND_TABLE +{ + union + { + AHCI_FIS_HOST_TO_DEVICE HostToDeviceFis; + UCHAR CommandFis[64]; + }; + UCHAR AtapiCommand[16]; + UCHAR Reserved[48]; + AHCI_PRD_TABLE_ENTRY PrdTable[ANYSIZE_ARRAY]; +} AHCI_COMMAND_TABLE, *PAHCI_COMMAND_TABLE; + +C_ASSERT(FIELD_OFFSET(AHCI_COMMAND_TABLE, PrdTable) == 128); + +#include + +#define AHCI_PORT_BASE(HbaIoBase, PortNumber) \ + (PULONG)((ULONG_PTR)(HbaIoBase) + (PortNumber) * 0x80 + 0x100) + +FORCEINLINE +ULONG +AHCI_HBA_READ( + _In_ PVOID HbaIoBase, + _In_ AHCI_HOST_BUS_ADAPTER_REGISTER Register) +{ + return READ_REGISTER_ULONG((PULONG)((ULONG_PTR)HbaIoBase + Register)); +} + +FORCEINLINE +VOID +AHCI_HBA_WRITE( + _In_ PVOID HbaIoBase, + _In_ AHCI_HOST_BUS_ADAPTER_REGISTER Register, + _In_ ULONG Value) +{ + WRITE_REGISTER_ULONG((PULONG)((ULONG_PTR)HbaIoBase + Register), Value); +} + +FORCEINLINE +ULONG +AHCI_PORT_READ( + _In_ PVOID PortIoBase, + _In_ AHCI_PORT_REGISTER Register) +{ + return READ_REGISTER_ULONG((PULONG)((ULONG_PTR)PortIoBase + Register)); +} + +FORCEINLINE +VOID +AHCI_PORT_WRITE( + _In_ PVOID PortIoBase, + _In_ AHCI_PORT_REGISTER Register, + _In_ ULONG Value) +{ + WRITE_REGISTER_ULONG((PULONG)((ULONG_PTR)PortIoBase + Register), Value); +} diff --git a/drivers/storage/ide/pciidex/chipset/ahci_generic.c b/drivers/storage/ide/pciidex/chipset/ahci_generic.c new file mode 100644 index 00000000000..0318fad2478 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/ahci_generic.c @@ -0,0 +1,752 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: AHCI controller minidriver + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* GLOBALS ********************************************************************/ + +PCIIDEX_PAGED_DATA +static const struct +{ + USHORT VendorID; + USHORT DeviceID; +} AhciControllerList[] = +{ + { PCI_VEN_NVIDIA, 0x0550 }, + { PCI_VEN_NVIDIA, 0x0584 }, +}; + +/* FUNCTIONS ******************************************************************/ + +static +CODE_SEG("PAGE") +NTSTATUS +AtaAhciAllocateMemory( + _In_ PVOID ChannelContext) +{ + PCHANNEL_DATA_AHCI ChanData = ChannelContext; + PDMA_OPERATIONS DmaOperations = ChanData->DmaAdapter->DmaOperations; + ULONG i, j, SlotNumber, CommandSlots, BlockSize; + ULONG CommandListSize, CommandTableLength, CommandTablesPerPage; + PVOID Buffer; + ULONG_PTR BufferVa; + ULONG64 BufferPa; + PHYSICAL_ADDRESS PhysicalAddress; + + PAGED_CODE(); + + /* The maximum size of the command list is 1024 bytes */ + CommandSlots = ChanData->Controller->QueueDepth; + CommandListSize = FIELD_OFFSET(AHCI_COMMAND_LIST, CommandHeader[CommandSlots]); + BlockSize = CommandListSize + (AHCI_COMMAND_LIST_ALIGNMENT - 1); + + /* Add the receive area structure (256 bytes) */ + if (!(ChanData->ChanInfo & CHANNEL_FLAG_HAS_FBS)) + { + BlockSize += sizeof(AHCI_RECEIVED_FIS); + + /* The command list is 1024-byte aligned, which saves us some bytes of allocation size */ + BlockSize += ALIGN_UP_BY(CommandListSize, AHCI_RECEIVED_FIS_ALIGNMENT) - CommandListSize; + } + + Buffer = DmaOperations->AllocateCommonBuffer(ChanData->DmaAdapter, + BlockSize, + &PhysicalAddress, + TRUE); // Cached + if (!Buffer) + return STATUS_INSUFFICIENT_RESOURCES; + RtlZeroMemory(Buffer, BlockSize); + + ChanData->Mem.CommandListSize = BlockSize; + ChanData->Mem.CommandListOriginal = Buffer; + ChanData->Mem.CommandListPhysOriginal.QuadPart = PhysicalAddress.QuadPart; + + BufferVa = (ULONG_PTR)Buffer; + BufferPa = PhysicalAddress.QuadPart; + + /* Command list */ + BufferVa = ALIGN_UP_BY(BufferVa, AHCI_COMMAND_LIST_ALIGNMENT); + BufferPa = ALIGN_UP_BY(BufferPa, AHCI_COMMAND_LIST_ALIGNMENT); + ChanData->CommandList = (PVOID)BufferVa; + ChanData->Mem.CommandListPhys = BufferPa; + BufferVa += CommandListSize; + BufferPa += CommandListSize; + + /* Alignment requirement */ + ASSERT((ULONG_PTR)ChanData->Mem.CommandListPhys % AHCI_COMMAND_LIST_ALIGNMENT == 0); + + /* Received FIS structure */ + if (!(ChanData->ChanInfo & CHANNEL_FLAG_HAS_FBS)) + { + BufferVa = ALIGN_UP_BY(BufferVa, AHCI_RECEIVED_FIS_ALIGNMENT); + BufferPa = ALIGN_UP_BY(BufferPa, AHCI_RECEIVED_FIS_ALIGNMENT); + ChanData->ReceivedFis = (PVOID)BufferVa; + ChanData->Mem.ReceivedFisPhys = BufferPa; + BufferVa += sizeof(AHCI_RECEIVED_FIS); + BufferPa += sizeof(AHCI_RECEIVED_FIS); + + /* Alignment requirement */ + ASSERT((ULONG_PTR)ChanData->Mem.ReceivedFisPhys % AHCI_RECEIVED_FIS_ALIGNMENT == 0); + } + + if (ChanData->ChanInfo & CHANNEL_FLAG_HAS_FBS) + { + /* The FBS receive area is 4kB, allocate a page which is also 4kB-aligned */ + BlockSize = PAGE_SIZE; + + /* + * Some other architectures, like ia64, use a different page size, + * that is a multiple of 4096. + */ + C_ASSERT(PAGE_SIZE % AHCI_RECEIVED_FIS_FBS_ALIGNMENT == 0); + + Buffer = DmaOperations->AllocateCommonBuffer(ChanData->DmaAdapter, + BlockSize, + &PhysicalAddress, + TRUE); + if (!Buffer) + return STATUS_INSUFFICIENT_RESOURCES; + RtlZeroMemory(Buffer, BlockSize); + + ChanData->Mem.ReceivedFisOriginal = Buffer; + ChanData->Mem.ReceivedFisPhysOriginal.QuadPart = PhysicalAddress.QuadPart; + + BufferVa = (ULONG_PTR)Buffer; + BufferPa = PhysicalAddress.QuadPart; + + ChanData->ReceivedFis = (PVOID)BufferVa; + ChanData->Mem.ReceivedFisPhys = BufferPa; + + /* Alignment requirement */ + ASSERT(BufferPa % AHCI_RECEIVED_FIS_FBS_ALIGNMENT == 0); + } + + /* 32-bit DMA */ + if (!(ChanData->ChanInfo & CHANNEL_FLAG_64_BIT_DMA)) + { + ASSERT((ULONG)(ChanData->Mem.CommandListPhys >> 32) == 0); + ASSERT((ULONG)(ChanData->Mem.ReceivedFisPhys >> 32) == 0); + } + + CommandTableLength = FIELD_OFFSET(AHCI_COMMAND_TABLE, PrdTable[ChanData->MaximumPhysicalPages]); + + ASSERT(ChanData->MaximumPhysicalPages != 0 && + ChanData->MaximumPhysicalPages <= AHCI_MAX_PRDT_ENTRIES); + + /* + * See ATA_MAX_TRANSFER_LENGTH, currently the MaximumPhysicalPages is restricted to + * a maximum of (0x20000 / PAGE_SIZE) + 1 = 33 pages. + * Each command table will require us 128 + 16 * 33 + (128 - 1) = 783 bytes of shared memory. + */ + ASSERT(PAGE_SIZE > (CommandTableLength + (AHCI_COMMAND_TABLE_ALIGNMENT - 1))); + + /* Allocate one-page chunks to avoid having a large chunk of contiguous memory */ + CommandTablesPerPage = PAGE_SIZE / (CommandTableLength + (AHCI_COMMAND_TABLE_ALIGNMENT - 1)); + + /* Command tables allocation loop */ + SlotNumber = 0; + i = CommandSlots; + while (i > 0) + { + ULONG TableCount; + + TableCount = min(i, CommandTablesPerPage); + BlockSize = (CommandTableLength + (AHCI_COMMAND_TABLE_ALIGNMENT - 1)) * TableCount; + + /* Allocate a chunk of memory */ + Buffer = DmaOperations->AllocateCommonBuffer(ChanData->DmaAdapter, + BlockSize, + &PhysicalAddress, + TRUE); + if (!Buffer) + return STATUS_INSUFFICIENT_RESOURCES; + RtlZeroMemory(Buffer, BlockSize); + + ChanData->Mem.CommandTableOriginal[SlotNumber] = Buffer; + ChanData->Mem.CommandTablePhysOriginal[SlotNumber].QuadPart = PhysicalAddress.QuadPart; + ChanData->Mem.CommandTableSize[SlotNumber] = BlockSize; + + BufferVa = (ULONG_PTR)Buffer; + BufferPa = PhysicalAddress.QuadPart; + + /* Split the allocation into command tables */ + for (j = 0; j < TableCount; ++j) + { + PAHCI_COMMAND_HEADER CommandHeader; + + BufferVa = ALIGN_UP_BY(BufferVa, AHCI_COMMAND_TABLE_ALIGNMENT); + BufferPa = ALIGN_UP_BY(BufferPa, AHCI_COMMAND_TABLE_ALIGNMENT); + + /* Alignment requirement */ + ASSERT(BufferPa % AHCI_COMMAND_TABLE_ALIGNMENT == 0); + + /* 32-bit DMA */ + if (!(ChanData->ChanInfo & CHANNEL_FLAG_64_BIT_DMA)) + { + ASSERT((ULONG)(BufferPa >> 32) == 0); + } + + ChanData->CommandTable[SlotNumber] = (PAHCI_COMMAND_TABLE)BufferVa; + + CommandHeader = &ChanData->CommandList->CommandHeader[SlotNumber]; + CommandHeader->CommandTableBaseLow = (ULONG)BufferPa; + CommandHeader->CommandTableBaseHigh = (ULONG)(BufferPa >> 32); + + ++SlotNumber; + BufferVa += CommandTableLength; + BufferPa += CommandTableLength; + } + + i -= TableCount; + } + + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +VOID +AtaAhciFreeMemory( + _In_ PVOID ChannelContext) +{ + PCHANNEL_DATA_AHCI ChanData = ChannelContext; + PDMA_ADAPTER DmaAdapter = ChanData->DmaAdapter; + PDMA_OPERATIONS DmaOperations = ChanData->DmaAdapter->DmaOperations; + ULONG i; + + PAGED_CODE(); + + if (ChanData->Mem.CommandListOriginal) + { + DmaOperations->FreeCommonBuffer(DmaAdapter, + ChanData->Mem.CommandListSize, + ChanData->Mem.CommandListPhysOriginal, + ChanData->Mem.CommandListOriginal, + TRUE); // Cached + ChanData->Mem.CommandListOriginal = NULL; + } + + if (ChanData->Mem.ReceivedFisOriginal) + { + DmaOperations->FreeCommonBuffer(DmaAdapter, + PAGE_SIZE, + ChanData->Mem.ReceivedFisPhysOriginal, + ChanData->Mem.ReceivedFisOriginal, + TRUE); + ChanData->Mem.ReceivedFisOriginal = NULL; + } + + for (i = 0; i < AHCI_MAX_COMMAND_SLOTS; ++i) + { + if (ChanData->Mem.CommandTableOriginal[i]) + { + DmaOperations->FreeCommonBuffer(DmaAdapter, + ChanData->Mem.CommandTableSize[i], + ChanData->Mem.CommandTablePhysOriginal[i], + ChanData->Mem.CommandTableOriginal[i], + TRUE); + ChanData->Mem.CommandTableOriginal[i] = NULL; + } + } +} + +#if DBG +static +CODE_SEG("PAGE") +BOOLEAN +AtaAhciIsVbox(VOID) +{ + UCHAR Buffer[RTL_SIZEOF_THROUGH_FIELD(PCI_COMMON_HEADER, DeviceID)]; + PPCI_COMMON_HEADER PciData = (PPCI_COMMON_HEADER)Buffer; // Partial PCI header + ULONG BytesRead; + PCI_SLOT_NUMBER Slot; + + PAGED_CODE(); + + Slot.u.AsULONG = 0; + Slot.u.bits.DeviceNumber = 4; + Slot.u.bits.FunctionNumber = 0; + + BytesRead = HalGetBusDataByOffset(PCIConfiguration, + 0, + Slot.u.AsULONG, + &Buffer, + FIELD_OFFSET(PCI_COMMON_HEADER, VendorID), + sizeof(Buffer)); + return (BytesRead == sizeof(Buffer)) && + (PciData->VendorID == 0x80EE) && + (PciData->DeviceID == 0xCAFE); +} +#endif + +static +CODE_SEG("PAGE") +VOID +AtaAhciHbaRequestOsOwnership( + _In_ PVOID IoBase) +{ + ULONG i, Control; + + PAGED_CODE(); + + Control = AHCI_HBA_READ(IoBase, HbaBiosHandoffControl); + if (Control & AHCI_BOHC_OS_SEMAPHORE) + return; + + INFO("HBA ownership change\n"); + + AHCI_HBA_WRITE(IoBase, HbaBiosHandoffControl, Control | AHCI_BOHC_OS_SEMAPHORE); + + /* Wait up to 2 seconds */ + for (i = 0; i < 200000; ++i) + { + Control = AHCI_HBA_READ(IoBase, HbaBiosHandoffControl); + + if (!(Control & (AHCI_BOHC_BIOS_BUSY | AHCI_BOHC_BIOS_SEMAPHORE))) + return; + + KeStallExecutionProcessor(10); + } + + WARN("Unable to acquire the OS semaphore %08lx\n", Control); +} + +/** + * See eSATA paper + * http://download.microsoft.com/download/7/E/7/7E7662CF-CBEA-470B-A97E-CE7CE0D98DC2/eSATA.docx + */ +static +CODE_SEG("PAGE") +BOOLEAN +AtaAhciIsPortRemovable( + _In_ ULONG AhciCapabilities, + _In_ ULONG CmdStatus) +{ + PAGED_CODE(); + + if (CmdStatus & AHCI_PXCMD_HPCP) + return TRUE; + + if ((AhciCapabilities & AHCI_CAP_SXS) && (CmdStatus & AHCI_PXCMD_ESP)) + return TRUE; + + if ((AhciCapabilities & AHCI_CAP_SMPS) && (CmdStatus & AHCI_PXCMD_MPSP)) + return TRUE; + + return FALSE; +} + +static +CODE_SEG("PAGE") +NTSTATUS +AtaAhciCreateChannelData( + _In_ PATA_CONTROLLER Controller) +{ + PCHANNEL_DATA_AHCI ChanData; + ULONG i; + + PAGED_CODE(); + ASSERT(Controller->MaxChannels != 0); + + ChanData = ExAllocatePoolZero(NonPagedPool, + sizeof(*ChanData) * Controller->MaxChannels, + TAG_PCIIDEX); + if (!ChanData) + return STATUS_INSUFFICIENT_RESOURCES; + + Controller->ChanDataBlock = ChanData; + + for (i = 0; i < AHCI_MAX_PORTS; ++i) + { + ULONG CmdStatus; + + if (!(Controller->ChannelBitmap & (1 << i))) + continue; + + Controller->Channels[i] = ChanData; + + ChanData->Channel = i; + ChanData->Controller = Controller; + + ChanData->AllocateMemory = AtaAhciAllocateMemory; + ChanData->FreeMemory = AtaAhciFreeMemory; + ChanData->EnableInterrupts = AtaAhciEnableInterrupts; + ChanData->PreparePrdTable = AtaAhciPreparePrdTable; + ChanData->PrepareIo = AtaAhciPrepareIo; + ChanData->StartIo = AtaAhciStartIo; + ChanData->SetTransferMode = SataSetTransferMode; + ChanData->TransferModeSupported = SATA_ALL; + + ChanData->IoBase = AHCI_PORT_BASE(Controller->IoBase, i); + + ChanData->EnableInterrupts(ChanData, FALSE); + + /* Begin the process of stopping the command list DMA engine for later initialization */ + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + if (CmdStatus & AHCI_PXCMD_ST) + { + CmdStatus &= ~AHCI_PXCMD_ST; + AHCI_PORT_WRITE(ChanData->IoBase, PxCmdStatus, CmdStatus); + } + + /* The AHCI HBA can only perform DMA I/O and PIO is not supported */ + ChanData->ChanInfo = CHANNEL_FLAG_PIO_VIA_DMA; + + if (Controller->AhciCapabilities & AHCI_CAP_S64A) + ChanData->ChanInfo |= CHANNEL_FLAG_64_BIT_DMA; + + if (Controller->AhciCapabilities & AHCI_CAP_SNCQ) + ChanData->ChanInfo |= CHANNEL_FLAG_HAS_NCQ; + + /* Check for the FIS-based switching feature support */ + if ((Controller->AhciCapabilities & AHCI_CAP_SPM) && + (Controller->AhciCapabilities & AHCI_CAP_FBSS) && + (CmdStatus & AHCI_PXCMD_FBSCP)) + { + INFO("CH %lu: FBS supported\n", ChanData->Channel); + ChanData->ChanInfo |= CHANNEL_FLAG_HAS_FBS; + } + + if (AtaAhciIsPortRemovable(Controller->AhciCapabilities, CmdStatus)) + { + INFO("CH %lu: Port is external\n", ChanData->Channel); + ChanData->ChanInfo |= CHANNEL_FLAG_IS_EXTERNAL; + } + + ++ChanData; + } + + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +PVOID +AtaAhciGetAbar( + _Inout_ PATA_CONTROLLER Controller) +{ + PVOID Abar; + ULONG i, Index; + PCIIDEX_PAGED_DATA static const struct + { + USHORT VendorID; + USHORT DeviceID; + ULONG Index; + } AbarLocations[] = + { + { PCI_VEN_CAVIUM, 0xA01C, 0 }, + }; + + PAGED_CODE(); + + /* Default index */ + Index = 5; + + for (i = 0; i < RTL_NUMBER_OF(AbarLocations); ++i) + { + if ((Controller->Pci.VendorID == AbarLocations[i].VendorID) && + (Controller->Pci.DeviceID == AbarLocations[i].DeviceID)) + { + Index = AbarLocations[i].Index; + break; + } + } + + if (!(Controller->AccessRange[Index].Flags & RANGE_IS_MEMORY)) + return NULL; + + Abar = AtaCtrlPciMapBar(Controller, Index, 0); + if (!Abar) + return NULL; + + return Abar; +} + +static +CODE_SEG("PAGE") +NTSTATUS +AtaAhciAttachChannel( + _In_ PVOID ChannelContext, + _In_ BOOLEAN Attach) +{ + PCHANNEL_DATA_AHCI ChanData = ChannelContext; + + PAGED_CODE(); + + if (Attach) + { + /* We do enable interrupts in the QBR handler */ + } + else + { + AtaChanEnableInterruptsSync(ChanData, FALSE); + AtaAhciStopDma(ChanData); + } + + return STATUS_SUCCESS; +} + +static +VOID +AtaAhciHbaStart( + _In_ PATA_CONTROLLER Controller) +{ + ULONG GlobalControl; + + GlobalControl = AHCI_HBA_READ(Controller->IoBase, HbaGlobalControl); + if (!(GlobalControl & AHCI_GHC_AE)) + { + /* Set AE on power up */ + GlobalControl |= AHCI_GHC_AE; + AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl); + } + + /* Clear HBA interrupts */ + AHCI_HBA_WRITE(Controller->IoBase, HbaInterruptStatus, 0xFFFFFFFF); + + /* Enable interrupts */ + GlobalControl |= AHCI_GHC_IE; + AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl); +} + +static +VOID +AtaAhciHbaStop( + _In_ PATA_CONTROLLER Controller) +{ + ULONG GlobalControl; + KIRQL OldIrql; + + /* Failed to connect interrupt */ + if (!Controller->InterruptObject) + return; + + OldIrql = KeAcquireInterruptSpinLock(Controller->InterruptObject); + + /* Disable interrupts */ + GlobalControl = AHCI_HBA_READ(Controller->IoBase, HbaGlobalControl); + GlobalControl &= ~AHCI_GHC_IE; + AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl); + + /* Clear HBA interrupts */ + AHCI_HBA_WRITE(Controller->IoBase, HbaInterruptStatus, 0xFFFFFFFF); + + KeReleaseInterruptSpinLock(Controller->InterruptObject, OldIrql); +} + +static +CODE_SEG("PAGE") +VOID +AtaAhciHbaFreeResouces( + _In_ PATA_CONTROLLER Controller) +{ + PAGED_CODE(); + + if (Controller->InterruptObject) + { + IoDisconnectInterrupt(Controller->InterruptObject); + Controller->InterruptObject = NULL; + } +} + +static +CODE_SEG("PAGE") +BOOLEAN +AhciControllerIsSubClassCheckNeeded( + _In_ PATA_CONTROLLER Controller) +{ + PAGED_CODE(); + + if (Controller->Pci.VendorID == PCI_VEN_INTEL) + { + /* IDE or AHCI */ + if ((Controller->Pci.DeviceID == 0x2652) || (Controller->Pci.DeviceID == 0x2653)) + return TRUE; + } + + /* IDE, RAID, or AHCI */ + if ((Controller->Pci.VendorID == PCI_VEN_VIA) && (Controller->Pci.DeviceID == 0x3349)) + return TRUE; + + return FALSE; +} + +static +CODE_SEG("PAGE") +BOOLEAN +AhciMatchController( + _In_ PATA_CONTROLLER Controller) +{ + ULONG i; + + PAGED_CODE(); + + /* Some controllers share the same PCI ID between AHCI and IDE/RAID modes */ + if (AhciControllerIsSubClassCheckNeeded(Controller)) + { + if (Controller->Pci.SubClass != PCI_SUBCLASS_MSC_AHCI_CTLR) + return FALSE; + } + + /* + * Match the controller through the PCI ID. + * We do not want to check the PCI subclass code because of + * some AHCI controllers that support AHCI even in IDE emulation mode. + * For example, nVidia 10DE:0550 uses the same PCI ID between AHCI and IDE modes, + * but BAR5 will always be available and AHCI can be enabled by setting GHC.AE to 1. + */ + for (i = 0; i < RTL_NUMBER_OF(AhciControllerList); ++i) + { + if ((Controller->Pci.VendorID == AhciControllerList[i].VendorID) && + (Controller->Pci.DeviceID == AhciControllerList[i].DeviceID)) + { + return TRUE; + } + } + + /* Check for generic PCI AHCI controller */ + if ((Controller->Pci.BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) && + (Controller->Pci.SubClass == PCI_SUBCLASS_MSC_AHCI_CTLR)) + { + return TRUE; + } + + return FALSE; +} + +CODE_SEG("PAGE") +NTSTATUS +AhciGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptDesc = &Controller->InterruptDesc; + ULONG i, GlobalControl; + NTSTATUS Status; + + PAGED_CODE(); + + if (!AhciMatchController(Controller)) + return STATUS_NO_MATCH; + + if (Controller->InterruptDesc.Type != CmResourceTypeInterrupt) + { + ERR("No interrupt resource\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Controller->IoBase = AtaAhciGetAbar(Controller); + if (!Controller->IoBase) + return STATUS_NO_MATCH; // Try IDE/RAID + + Controller->Flags = CTRL_FLAG_IS_AHCI | CTRL_FLAG_SATA_HBA_ACPI; + Controller->Start = AtaAhciHbaStart; + Controller->Stop = AtaAhciHbaStop; + Controller->FreeResources = AtaAhciHbaFreeResouces; + Controller->AttachChannel = AtaAhciAttachChannel; + + /* Set AE before accessing other AHCI registers */ + GlobalControl = AHCI_HBA_READ(Controller->IoBase, HbaGlobalControl); + GlobalControl |= AHCI_GHC_AE; + AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl); + + Controller->AhciCapabilities = AHCI_HBA_READ(Controller->IoBase, HbaCapabilities); + Controller->ChannelBitmap = AHCI_HBA_READ(Controller->IoBase, HbaPortBitmap); + Controller->MaxChannels = CountSetBits(Controller->ChannelBitmap); + if (Controller->MaxChannels == 0) + { + ASSERT(Controller->MaxChannels == 0); + return STATUS_DEVICE_HARDWARE_ERROR; + } + + Controller->AhciVersion = AHCI_HBA_READ(Controller->IoBase, HbaAhciVersion); + if (Controller->AhciVersion >= AHCI_VERSION_1_2) + { + Controller->AhciCapabilitiesEx = AHCI_HBA_READ(Controller->IoBase, HbaCapabilitiesEx); + + if (Controller->AhciCapabilitiesEx & AHCI_CAP2_BOH) + AtaAhciHbaRequestOsOwnership(Controller->IoBase); + } + + /* Reset the HBA into a consistent state */ + GlobalControl = AHCI_HBA_READ(Controller->IoBase, HbaGlobalControl); + GlobalControl |= AHCI_GHC_HR; + AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl); + + /* HBA reset may take up to 1 second */ + for (i = 100000; i > 0; i--) + { + GlobalControl = AHCI_HBA_READ(Controller->IoBase, HbaGlobalControl); + if (!(GlobalControl & AHCI_GHC_HR)) + break; + + KeStallExecutionProcessor(10); + } + if (i == 0) + { + ERR("HBA reset failed %08lx\n", GlobalControl); + return STATUS_IO_TIMEOUT; + } + + /* Re-enable AE */ + GlobalControl |= AHCI_GHC_AE; + AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl); + + /* Disable interrupts */ + GlobalControl = AHCI_HBA_READ(Controller->IoBase, HbaGlobalControl); + if (GlobalControl & AHCI_GHC_IE) + { + GlobalControl &= ~AHCI_GHC_IE; + AHCI_HBA_WRITE(Controller->IoBase, HbaGlobalControl, GlobalControl); + } + +#if DBG + /* On virtual machines, this will allow us to test the PMP support code */ + if (AtaAhciIsVbox()) + Controller->AhciCapabilities |= AHCI_CAP_SPM; +#endif + + Status = AtaAhciCreateChannelData(Controller); + if (!NT_SUCCESS(Status)) + return Status; + + Controller->QueueDepth = ((Controller->AhciCapabilities & AHCI_CAP_NCS) >> 8) + 1; + + INFO("%04X:%04X.%02X: Ver %08lX, PI %08lX, CAP %08lX, CAP2 %08lX\n", + Controller->Pci.VendorID, + Controller->Pci.DeviceID, + Controller->Pci.RevisionID, + Controller->AhciVersion, + Controller->ChannelBitmap, + Controller->AhciCapabilities, + Controller->AhciCapabilitiesEx); + + /* Clear HBA interrupts */ + AHCI_HBA_WRITE(Controller->IoBase, HbaInterruptStatus, 0xFFFFFFFF); + + Status = IoConnectInterrupt(&Controller->InterruptObject, + AtaAhciHbaIsr, + Controller, + NULL, + InterruptDesc->u.Interrupt.Vector, + InterruptDesc->u.Interrupt.Level, + InterruptDesc->u.Interrupt.Level, + (InterruptDesc->Flags & CM_RESOURCE_INTERRUPT_LATCHED) + ? Latched : LevelSensitive, + (InterruptDesc->ShareDisposition == CmResourceShareShared), + InterruptDesc->u.Interrupt.Affinity, + FALSE); + if (!NT_SUCCESS(Status)) + { + ERR("Could not connect to interrupt %lu, status 0x%lx\n", + InterruptDesc->u.Interrupt.Vector, Status); + return Status; + } + + return STATUS_SUCCESS; +} diff --git a/drivers/storage/ide/pciidex/chipset/ahci_hw.c b/drivers/storage/ide/pciidex/chipset/ahci_hw.c new file mode 100644 index 00000000000..bb1142e4f96 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/ahci_hw.c @@ -0,0 +1,1579 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: AHCI hardware support + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* + * The PMP configuration code derives from the FreeBSD ATA driver + * Copyright (c) 2009 Alexander Motin + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* FUNCTIONS ******************************************************************/ + +static +BOOLEAN +AtaAhciPollRegister( + _In_ PVOID IoBase, + _In_ AHCI_PORT_REGISTER Register, + _In_ ULONG Mask, + _In_ ULONG Value, + _In_range_(>, 0) ULONG TimeOut) +{ + ULONG i, Data; + + ASSUME(TimeOut > 0); + + /* Do a quick check first (100 us) */ + for (i = 0; i < 10; ++i) + { + Data = AHCI_PORT_READ(IoBase, Register); + if ((Data & Mask) == Value) + return TRUE; + + KeStallExecutionProcessor(10); + } + + /* Retry after the time interval */ + for (i = 0; i < TimeOut; i++) + { + Data = AHCI_PORT_READ(IoBase, Register); + if ((Data & Mask) == Value) + return TRUE; + + AtaSleep(); + } + + return FALSE; +} + +VOID +AtaAhciEnableInterrupts( + _In_ PVOID ChannelContext, + _In_ BOOLEAN Enable) +{ + PCHANNEL_DATA_AHCI ChanData = ChannelContext; + + INFO("CH %lu: %sble interrupts\n", ChanData->Channel, Enable ? "Ena" : "Disa"); + + /* Clear port interrupts */ + AHCI_PORT_WRITE(ChanData->IoBase, PxSataError, 0xFFFFFFFF); + AHCI_PORT_WRITE(ChanData->IoBase, PxInterruptStatus, 0xFFFFFFFF); + AHCI_HBA_WRITE(ChanData->Controller->IoBase, HbaInterruptStatus, 1 << ChanData->Channel); + + AHCI_PORT_WRITE(ChanData->IoBase, PxInterruptEnable, Enable ? AHCI_PORT_INTERRUPT_MASK : 0); +} + +static +BOOLEAN +AtaAhciIsHbaHotRemoved( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + PATA_CONTROLLER Controller = ChanData->Controller; + BOOLEAN WasRemoved; + + /* Check if the HBA has been hot-removed to avoid timeouts on all ports */ + WasRemoved = (AHCI_HBA_READ(Controller->IoBase, HbaAhciVersion) == 0xFFFFFFFF); + if (WasRemoved) + { + ERR("CH %lu: AHCI controller %04X:%04X is gone\n", + ChanData->Channel, + Controller->Pci.VendorID, + Controller->Pci.DeviceID); + } + return WasRemoved; +} + +static +VOID +AtaAhciSendComReset( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ULONG SataControl; + + INFO("CH %lu: Transmit a COMRESET on the interface\n", ChanData->Channel); + + /* Clear errors */ + AHCI_PORT_WRITE(ChanData->IoBase, PxSataError, 0xFFFFFFFF); + + SataControl = AHCI_PORT_READ(ChanData->IoBase, PxSataControl); + SataControl &= ~AHCI_PXCTL_DET_MASK; + SataControl |= AHCI_PXCTL_DET_RESET; + AHCI_PORT_WRITE(ChanData->IoBase, PxSataControl, SataControl); + + KeStallExecutionProcessor(1000); + + SataControl = AHCI_PORT_READ(ChanData->IoBase, PxSataControl); + SataControl &= ~AHCI_PXCTL_DET_MASK; + SataControl |= AHCI_PXCTL_DET_IDLE; + AHCI_PORT_WRITE(ChanData->IoBase, PxSataControl, SataControl); + + /* Clear errors */ + AHCI_PORT_WRITE(ChanData->IoBase, PxSataError, 0xFFFFFFFF); +} + +static +VOID +AtaAhciStopCommandListProcess( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ULONG CmdStatus; + + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + if (CmdStatus & AHCI_PXCMD_ST) + { + CmdStatus &= ~AHCI_PXCMD_ST; + AHCI_PORT_WRITE(ChanData->IoBase, PxCmdStatus, CmdStatus); + } +} + +static +VOID +AtaAhciStartCommandListProcess( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ULONG i, CmdStatus; + + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + CmdStatus |= AHCI_PXCMD_ST; + AHCI_PORT_WRITE(ChanData->IoBase, PxCmdStatus, CmdStatus); + + /* + * We are supposed to wait for the AHCI_PXCMD_CR bit to be set + * before interacting with the port again, + * but on some AHCI controllers this bit never becomes set. + */ + for (i = 0; i < 10; ++i) + { + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + if (CmdStatus & AHCI_PXCMD_CR) + break; + + KeStallExecutionProcessor(5); + } +} + +static +BOOLEAN +AtaAhciStopCommandListProcessAndWait( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + AtaAhciStopCommandListProcess(ChanData); + + if (AtaAhciIsHbaHotRemoved(ChanData)) + return FALSE; + + return AtaAhciPollRegister(ChanData->IoBase, + PxCmdStatus, + AHCI_PXCMD_CR, + 0, + AHCI_DELAY_CR_START_STOP); +} + +static +VOID +AtaAhciStopFisReceiveProcess( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ULONG CmdStatus; + + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + if (CmdStatus & AHCI_PXCMD_FRE) + { + CmdStatus &= ~AHCI_PXCMD_FRE; + AHCI_PORT_WRITE(ChanData->IoBase, PxCmdStatus, CmdStatus); + } +} + +static +VOID +AtaAhciStartFisReceiveProcess( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ULONG CmdStatus; + + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + CmdStatus |= AHCI_PXCMD_FRE; + AHCI_PORT_WRITE(ChanData->IoBase, PxCmdStatus, CmdStatus); +} + +static +BOOLEAN +AtaAhciStopFisReceiveProcessAndWait( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + AtaAhciStopFisReceiveProcess(ChanData); + + if (AtaAhciIsHbaHotRemoved(ChanData)) + return FALSE; + + return AtaAhciPollRegister(ChanData->IoBase, + PxCmdStatus, + AHCI_PXCMD_FR, + 0, + AHCI_DELAY_FR_START_STOP); +} + +static +VOID +AtaAhciStartFisReceiveProcessAndWait( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + AtaAhciStartFisReceiveProcess(ChanData); + + if (!AtaAhciPollRegister(ChanData->IoBase, + PxCmdStatus, + AHCI_PXCMD_FR, + AHCI_PXCMD_FR, + AHCI_DELAY_FR_START_STOP)) + { + /* Ignore timeouts, on some AHCI controllers the FR bit never becomes set */ + WARN("CH %lx: Failed to start the FIS Receive DMA engine %08lx\n", + ChanData->Channel, + AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus)); + } +} + +static +VOID +AtaAhciPhyEnterListenMode( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ULONG CmdStatus, SataControl; + + if (!(ChanData->Controller->AhciCapabilities & AHCI_CAP_SSS)) + return; + + INFO("CH %lu: Enter listen mode\n", ChanData->Channel); + + AtaAhciStopCommandListProcessAndWait(ChanData); + + SataControl = AHCI_PORT_READ(ChanData->IoBase, PxSataControl); + SataControl &= ~AHCI_PXCTL_DET_MASK; + AHCI_PORT_WRITE(ChanData->IoBase, PxSataControl, SataControl); + + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + CmdStatus &= ~(AHCI_PXCMD_SUD | AHCI_PXCMD_ICC_MASK); + AHCI_PORT_WRITE(ChanData->IoBase, PxCmdStatus, CmdStatus); +} + +static +BOOLEAN +AtaAhciPerformCommandListOverride( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ULONG CmdStatus; + + if (!(ChanData->Controller->AhciCapabilities & AHCI_CAP_SCLO)) + return TRUE; + + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + CmdStatus |= AHCI_PXCMD_CLO; + AHCI_PORT_WRITE(ChanData->IoBase, PxCmdStatus, CmdStatus); + + return AtaAhciPollRegister(ChanData->IoBase, + PxCmdStatus, + AHCI_PXCMD_CLO, + 0, + AHCI_DELAY_CLO_CLEAR); +} + +static +VOID +AtaAhciAtapiLedControl( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ BOOLEAN DoEnable) +{ + ULONG CmdStatus, NewCmdStatus; + + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + if (DoEnable) + NewCmdStatus = CmdStatus | AHCI_PXCMD_ATAPI; + else + NewCmdStatus = CmdStatus & ~AHCI_PXCMD_ATAPI; + + if (CmdStatus != NewCmdStatus) + AHCI_PORT_WRITE(ChanData->IoBase, PxCmdStatus, NewCmdStatus); +} + +static +VOID +AtaAhciFbsControl( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ BOOLEAN DoEnable) +{ + ULONG FbsControl, NewFbsControl; + + if (!(ChanData->ChanInfo & CHANNEL_FLAG_HAS_FBS)) + return; + + ChanData->LastFbsDeviceNumber = 0xFF; + + FbsControl = AHCI_PORT_READ(ChanData->IoBase, PxFisSwitchingControl); + if (DoEnable) + { + NewFbsControl = FbsControl | AHCI_FBS_ENABLE; + } + else + { + NewFbsControl = FbsControl & ~AHCI_FBS_ENABLE; + ChanData->ChanInfo &= ~CHANNEL_FLAG_FBS_ENABLED; + } + + if ((FbsControl & NewFbsControl) ^ AHCI_FBS_ENABLE) + { + AHCI_PORT_WRITE(ChanData->IoBase, PxFisSwitchingControl, NewFbsControl); + } + + if (!DoEnable) + return; + + /* Make sure we can read back the new value */ + FbsControl = AHCI_PORT_READ(ChanData->IoBase, PxFisSwitchingControl); + if (FbsControl & AHCI_FBS_ENABLE) + { + ChanData->ChanInfo |= CHANNEL_FLAG_FBS_ENABLED; + INFO("CH %lu: FBS enabled\n", ChanData->Channel); + } + else + { + WARN("CH %lu: Unable to enable FIS-based switching\n", ChanData->Channel); + } +} + +static +BOOLEAN +AtaAhciEnterIdleState( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ULONG CmdStatus; + + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + + /* Already in an idle state */ + if (!(CmdStatus & (AHCI_PXCMD_ST | AHCI_PXCMD_CR | AHCI_PXCMD_FRE | AHCI_PXCMD_FR))) + return TRUE; + + /* Stop the command list DMA engine */ + if (!AtaAhciStopCommandListProcessAndWait(ChanData)) + { + WARN("CH %lu: Failed to stop the command list DMA engine %08lx\n", + ChanData->Channel, + AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus)); + return FALSE; + } + + AtaAhciPerformCommandListOverride(ChanData); + + /* Stop the FIS Receive DMA engine */ + if (!AtaAhciStopFisReceiveProcessAndWait(ChanData)) + { + WARN("CH %lu: Failed to stop the FIS Receive DMA engine %08lx\n", + ChanData->Channel, + AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus)); + return FALSE; + } + + return TRUE; +} + +static +VOID +AtaAhciSetupDmaMemoryAddress( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + /* Physical address of the allocated command list */ + AHCI_PORT_WRITE(ChanData->IoBase, PxCommandListBaseLow, (ULONG)ChanData->Mem.CommandListPhys); + if (ChanData->Controller->AhciCapabilities & AHCI_CAP_S64A) + { + AHCI_PORT_WRITE(ChanData->IoBase, + PxCommandListBaseHigh, + (ULONG)(ChanData->Mem.CommandListPhys >> 32)); + } + + /* Physical address of the allocated FIS receive area */ + AHCI_PORT_WRITE(ChanData->IoBase, PxFisBaseLow, (ULONG)ChanData->Mem.ReceivedFisPhys); + if (ChanData->Controller->AhciCapabilities & AHCI_CAP_S64A) + { + AHCI_PORT_WRITE(ChanData->IoBase, + PxFisBaseHigh, + (ULONG)(ChanData->Mem.ReceivedFisPhys >> 32)); + } +} + +static +VOID +AtaAhciSpinUp( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ULONG CmdStatus; + + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + + /* Clear the PMA bit once the DMA engine has been stopped */ + CmdStatus &= ~AHCI_PXCMD_PMA; + + /* Move to the active interface state for the DET value be accurate */ + CmdStatus &= ~AHCI_PXCMD_ICC_MASK; + CmdStatus |= AHCI_PXCMD_ICC_ACTIVE; + + AHCI_PORT_WRITE(ChanData->IoBase, PxCmdStatus, CmdStatus); + + AtaAhciPollRegister(ChanData->IoBase, + PxCmdStatus, + AHCI_PXCMD_ICC_MASK, + AHCI_PXCMD_ICC_IDLE, + AHCI_DELAY_INTERFACE_CHANGE); + + /* Spin-up and power up the device */ + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + if (ChanData->Controller->AhciCapabilities & AHCI_CAP_SSS) + { + CmdStatus |= AHCI_PXCMD_SUD; + } + if (CmdStatus & AHCI_PXCMD_CPD) + { + CmdStatus |= AHCI_PXCMD_POD; + } + AHCI_PORT_WRITE(ChanData->IoBase, PxCmdStatus, CmdStatus); +} + +static +BOOLEAN +AtaAhciPhyCheckDevicePresence( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ULONG i, SataStatus; + + /* Do a quick check first (100 us) */ + for (i = 0; i < 10; ++i) + { + SataStatus = AHCI_PORT_READ(ChanData->IoBase, PxSataStatus) & AHCI_PXSSTS_DET_MASK; + if (SataStatus != AHCI_PXSSTS_DET_NO_DEVICE) + return TRUE; + + KeStallExecutionProcessor(10); + } + + /* Retry after the time interval */ + for (i = 0; i < AHCI_DELAY_DET_PRESENCE; ++i) + { + SataStatus = AHCI_PORT_READ(ChanData->IoBase, PxSataStatus) & AHCI_PXSSTS_DET_MASK; + if (SataStatus != AHCI_PXSSTS_DET_NO_DEVICE) + return TRUE; + + AtaSleep(); + } + + return FALSE; +} + +static +BOOLEAN +AtaAhciPhyWaitForReady( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + return AtaAhciPollRegister(ChanData->IoBase, + PxSataStatus, + AHCI_PXSSTS_DET_MASK, + AHCI_PXSSTS_DET_PHY_OK, + AHCI_DELAY_DET_STABLE); +} + +static +BOOLEAN +AtaAhciWaitForDeviceReady( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ ULONG TimeOut) +{ + ULONG i, TaskFileData; + + /* Do a quick check first (100 us) */ + for (i = 0; i < 10; ++i) + { + TaskFileData = AHCI_PORT_READ(ChanData->IoBase, PxTaskFileData); + if (!(TaskFileData & (IDE_STATUS_BUSY | IDE_STATUS_DRQ))) + return TRUE; + + KeStallExecutionProcessor(10); + } + + /* Retry after the time interval */ + for (i = 0; i < TimeOut; ++i) + { + TaskFileData = AHCI_PORT_READ(ChanData->IoBase, PxTaskFileData); + if (!(TaskFileData & (IDE_STATUS_BUSY | IDE_STATUS_DRQ))) + return TRUE; + + /* Keep clearing the port errors after every 1 second interval */ + if (((i % AHCI_DELAY_1_SECOND) == 0) && (i != 0)) + { + if (AtaAhciIsHbaHotRemoved(ChanData)) + return FALSE; + + WARN("CH %lu: Device is busy %08lx, trying to recover %lu\n", + ChanData->Channel, + TaskFileData, + i); + + AHCI_PORT_WRITE(ChanData->IoBase, PxSataError, 0xFFFFFFFF); + } + + AtaSleep(); + } + + return FALSE; +} + +static +UCHAR +AtaAhciPostRequestPolled( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_range_(>, 0) ULONG TimeOutMs) +{ + ULONG InterruptStatus, TaskFileData; + UCHAR SrbStatus; + + ASSERT((ChanData->ActiveSlotsBitmap == 0) && (ChanData->ActiveQueuedSlotsBitmap == 0)); + + AHCI_PORT_WRITE(ChanData->IoBase, PxSataError, 0xFFFFFFFF); + + AHCI_PORT_WRITE(ChanData->IoBase, PxCommandIssue, 1 << AHCI_INTERNAL_SLOT); + if (!AtaAhciPollRegister(ChanData->IoBase, + PxCommandIssue, + 1 << AHCI_INTERNAL_SLOT, + 0, + TimeOutMs / PORT_TIMER_TICK_MS)) + { + ERR("CH %lu: Internal request timed out\n", ChanData->Channel); + + /* Clear the active DMA command */ + AtaAhciStopCommandListProcessAndWait(ChanData); + AtaAhciPerformCommandListOverride(ChanData); + AtaAhciStartCommandListProcess(ChanData); + + SrbStatus = SRB_STATUS_TIMEOUT; + goto Done; + } + + InterruptStatus = AHCI_PORT_READ(ChanData->IoBase, PxInterruptStatus); + + /* Minimal basic recovery for port */ + if (InterruptStatus & AHCI_PXIRQ_FATAL_ERROR) + { + if (!AtaAhciStopCommandListProcessAndWait(ChanData)) + { + ERR("CH %lu: Failed to stop the command list DMA engine\n", ChanData->Channel); + SrbStatus = SRB_STATUS_TIMEOUT; + goto Done; + } + + TaskFileData = AHCI_PORT_READ(ChanData->IoBase, PxTaskFileData); + if (TaskFileData & (IDE_STATUS_BUSY | IDE_STATUS_DRQ)) + { + ERR("CH %lu: Busy TFD %08lx\n", ChanData->Channel, TaskFileData); + SrbStatus = SRB_STATUS_TIMEOUT; + goto Done; + } + + AHCI_PORT_WRITE(ChanData->IoBase, PxSataError, + AHCI_PORT_READ(ChanData->IoBase, PxSataError)); + + AtaAhciStartCommandListProcess(ChanData); + + SrbStatus = SRB_STATUS_ERROR; + goto Done; + } + + SrbStatus = SRB_STATUS_SUCCESS; + +Done: + if (SrbStatus == SRB_STATUS_SUCCESS) + TRACE("CH %lu: Completed internal request\n", ChanData->Channel); + else + INFO("CH %lu: Internal request failed %02x\n", ChanData->Channel, SrbStatus); + + /* Clear port interrupts */ + AHCI_PORT_WRITE(ChanData->IoBase, PxSataError, 0xFFFFFFFF); + AHCI_PORT_WRITE(ChanData->IoBase, PxInterruptStatus, 0xFFFFFFFF); + AHCI_HBA_WRITE(ChanData->Controller->IoBase, HbaInterruptStatus, 1 << ChanData->Channel); + + return SrbStatus; +} + +static +UCHAR +AtaAhciSendResetFis( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ UCHAR PortNumber, + _In_ BOOLEAN AssertSRST, + _In_range_(>, 0) ULONG TimeOutMs) +{ + PAHCI_COMMAND_TABLE CommandTable = ChanData->CommandTable[AHCI_INTERNAL_SLOT]; + PAHCI_COMMAND_HEADER CommandHeader = &ChanData->CommandList->CommandHeader[AHCI_INTERNAL_SLOT]; + PAHCI_FIS_HOST_TO_DEVICE H2dFis = &CommandTable->HostToDeviceFis; + + RtlZeroMemory(H2dFis, sizeof(*H2dFis)); + H2dFis->Type = AHCI_FIS_REGISTER_HOST_TO_DEVICE; + H2dFis->Flags = PortNumber; + H2dFis->Control = IDE_DC_ALWAYS; + + CommandHeader->PrdByteCount = 0; + CommandHeader->Control = (sizeof(*H2dFis) / sizeof(ULONG)) | + (PortNumber << AHCI_COMMAND_HEADER_PMP_SHIFT); + + if (AssertSRST) + { + H2dFis->Control |= IDE_DC_RESET_CONTROLLER; + CommandHeader->Control |= AHCI_COMMAND_HEADER_RESET | + AHCI_COMMAND_HEADER_CLEAR_BUSY_UPON_OK; + } + + return AtaAhciPostRequestPolled(ChanData, TimeOutMs); +} + +static +UCHAR +AtaAhciPmpRead( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ UCHAR PortNumber, + _In_ USHORT Register, + _Out_ PULONG Result) +{ + PAHCI_COMMAND_TABLE CommandTable = ChanData->CommandTable[AHCI_INTERNAL_SLOT]; + PAHCI_COMMAND_HEADER CommandHeader = &ChanData->CommandList->CommandHeader[AHCI_INTERNAL_SLOT]; + PAHCI_FIS_HOST_TO_DEVICE H2dFis = &CommandTable->HostToDeviceFis; + PAHCI_FIS_DEVICE_TO_HOST D2hFis = &ChanData->ReceivedFis->DeviceToHostFis; + UCHAR SrbStatus; + + RtlZeroMemory(H2dFis, sizeof(*H2dFis)); + H2dFis->Type = AHCI_FIS_REGISTER_HOST_TO_DEVICE; + H2dFis->Flags = UPDATE_COMMAND | AHCI_PMP_CONTROL_PORT; + H2dFis->Control = IDE_DC_ALWAYS; + H2dFis->Device = IDE_DRIVE_SELECT | PortNumber; + H2dFis->Features = (UCHAR)Register; + H2dFis->FeaturesEx = (UCHAR)(Register >> 8); + H2dFis->Command = IDE_COMMAND_READ_PORT_MULTIPLIER; + + CommandHeader->PrdByteCount = 0; + CommandHeader->Control = (sizeof(*H2dFis) / sizeof(ULONG)) | + (AHCI_PMP_CONTROL_PORT << AHCI_COMMAND_HEADER_PMP_SHIFT); + + SrbStatus = AtaAhciPostRequestPolled(ChanData, 1000); + + *Result = D2hFis->SectorCount | + (D2hFis->LbaLow << 8) | + (D2hFis->LbaMid << 16) | + (D2hFis->LbaHigh << 24); + + return SrbStatus; +} + +static +UCHAR +AtaAhciPmpWrite( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ UCHAR PortNumber, + _In_ USHORT Register, + _In_ ULONG Value) +{ + PAHCI_COMMAND_TABLE CommandTable = ChanData->CommandTable[AHCI_INTERNAL_SLOT]; + PAHCI_COMMAND_HEADER CommandHeader = &ChanData->CommandList->CommandHeader[AHCI_INTERNAL_SLOT]; + PAHCI_FIS_HOST_TO_DEVICE H2dFis = &CommandTable->HostToDeviceFis; + + RtlZeroMemory(H2dFis, sizeof(*H2dFis)); + H2dFis->Type = AHCI_FIS_REGISTER_HOST_TO_DEVICE; + H2dFis->Flags = UPDATE_COMMAND | AHCI_PMP_CONTROL_PORT; + H2dFis->Control = IDE_DC_ALWAYS; + H2dFis->Device = IDE_DRIVE_SELECT | PortNumber; + H2dFis->Features = (UCHAR)Register; + H2dFis->FeaturesEx = (UCHAR)(Register >> 8); + H2dFis->SectorCount = (UCHAR)Value; + H2dFis->LbaLow = (UCHAR)(Value >> 8); + H2dFis->LbaMid = (UCHAR)(Value >> 16); + H2dFis->LbaHigh = (UCHAR)(Value >> 24); + H2dFis->Command = IDE_COMMAND_WRITE_PORT_MULTIPLIER; + + CommandHeader->PrdByteCount = 0; + CommandHeader->Control = (sizeof(*H2dFis) / sizeof(ULONG)) | + (AHCI_PMP_CONTROL_PORT << AHCI_COMMAND_HEADER_PMP_SHIFT); + + return AtaAhciPostRequestPolled(ChanData, 1000); +} + +static +BOOLEAN +AtaAhciPmpDisableSilXmitEarlyAck( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + UCHAR SrbStatus; + ULONG Value; + + SrbStatus = AtaAhciPmpRead(ChanData, AHCI_PMP_CONTROL_PORT, 129, &Value); + if (SrbStatus != SRB_STATUS_SUCCESS) + return FALSE; + + if (Value & 1) + { + Value &= ~1; + SrbStatus = AtaAhciPmpWrite(ChanData, AHCI_PMP_CONTROL_PORT, 129, Value); + if (SrbStatus != SRB_STATUS_SUCCESS) + return FALSE; + } + + return TRUE; +} + +static +BOOLEAN +AtaAhciPmpIdentifyPmp( + _In_ PCHANNEL_DATA_AHCI ChanData, + _Out_ PULONG PortCountResult) +{ + ULONG ProductId, RevisionInfo, PortCount; + UCHAR SrbStatus; + + *PortCountResult = 0; + + SrbStatus = AtaAhciPmpRead(ChanData, AHCI_PMP_CONTROL_PORT, PmpProductId, &ProductId); + if (SrbStatus != SRB_STATUS_SUCCESS) + return FALSE; + + SrbStatus = AtaAhciPmpRead(ChanData, AHCI_PMP_CONTROL_PORT, PmpRevisionInfo, &RevisionInfo); + if (SrbStatus != SRB_STATUS_SUCCESS) + return FALSE; + + SrbStatus = AtaAhciPmpRead(ChanData, AHCI_PMP_CONTROL_PORT, PmpPortInfo, &PortCount); + if (SrbStatus != SRB_STATUS_SUCCESS) + return FALSE; + + INFO("CH %lu: PMP %08lX:%08lX %lu ports\n", + ChanData->Channel, + ProductId, + RevisionInfo, + PortCount); + + PortCount &= AHCI_MAX_PMP_DEVICES; + + /* From FreeBSD: Hide pseudo ATA devices from the driver */ + switch (ProductId) + { + case 0x37261095: + case 0x38261095: + { + if (!AtaAhciPmpDisableSilXmitEarlyAck(ChanData)) + return FALSE; + + if (PortCount == 6) + PortCount = 5; + break; + } + + case 0x47261095: + if (PortCount == 7) + PortCount = 5; + break; + + case 0x57231095: + case 0x57331095: + case 0x57341095: + case 0x57441095: + if (PortCount > 0) + --PortCount; + break; + + default: + break; + } + *PortCountResult = PortCount; + + return TRUE; +} + +static +ATA_CONNECTION_STATUS +AtaAhciPmpDetect( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ PBOOLEAN CheckForPmp) +{ + ULONG CmdStatus, Signature; + UCHAR SrbStatus; + + /* Indicate a PMP */ + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + CmdStatus |= AHCI_PXCMD_PMA; + AHCI_PORT_WRITE(ChanData->IoBase, PxCmdStatus, CmdStatus); + + /* Turn off FIS-based switching prior to issuing the software reset */ + AtaAhciFbsControl(ChanData, FALSE); + + /* Start the command list DMA engine */ + AtaAhciPerformCommandListOverride(ChanData); + AtaAhciStartCommandListProcess(ChanData); + + /* BSY and DRQ must be cleared prior to issuing the software reset */ + if (!AtaAhciWaitForDeviceReady(ChanData, AHCI_DELAY_PMP_READY_DRIVE)) + { + WARN("CH %lu: Wait for ready failed %08lx\n", + ChanData->Channel, + AHCI_PORT_READ(ChanData->IoBase, PxTaskFileData)); + + /* Transmit a COMRESET to recover */ + AtaAhciSendComReset(ChanData); + + return CONN_STATUS_FAILURE; + } + + /* + * Issue a software reset. + * + * Real PMP devices come up faster than ATA/ATAPI devices, + * keep the timeout as small as possible. + */ + SrbStatus = AtaAhciSendResetFis(ChanData, AHCI_PMP_CONTROL_PORT, TRUE, 1000); + if (SrbStatus != SRB_STATUS_SUCCESS) + { + INFO("CH %lu: Soft reset failed %02x\n", ChanData->Channel, SrbStatus); + + /* + * This command may timeout if a PMP-capable port indicates device presence + * and the connected device is not a port multiplier. + * As a result, the system boot time may increase by (ActivePortCount * TimeOutMs). + * We skip PMP detection upon the first timeout, there is nothing more that can be done. + */ + if (SrbStatus == SRB_STATUS_TIMEOUT) + { + /* Not a PMP device */ + *CheckForPmp = FALSE; + INFO("CH %lu: Seems to have no PMP device\n", ChanData->Channel); + } + return CONN_STATUS_FAILURE; + } + + /* SRST pulse width */ + KeStallExecutionProcessor(20); + + SrbStatus = AtaAhciSendResetFis(ChanData, AHCI_PMP_CONTROL_PORT, FALSE, 2000); + if (SrbStatus != SRB_STATUS_SUCCESS) + { + INFO("CH %lu: Soft reset failed %02x\n", ChanData->Channel, SrbStatus); + + if (SrbStatus == SRB_STATUS_TIMEOUT) + { + /* Not a PMP device */ + *CheckForPmp = FALSE; + INFO("CH %lu: Seems to have no PMP device\n", ChanData->Channel); + } + return CONN_STATUS_FAILURE; + } + + /* SRST pulse width */ + KeStallExecutionProcessor(20); + + Signature = AHCI_PORT_READ(ChanData->IoBase, PxSignature); + + INFO("CH %lu: Received signature %08lx\n", ChanData->Channel, Signature); + if (Signature != AHCI_PXSIG_PMP) + return CONN_STATUS_NO_DEVICE; + + return CONN_STATUS_DEV_UNKNOWN; +} + +static +ATA_CONNECTION_STATUS +AtaAhciPhyCheckConnection( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ATA_CONNECTION_STATUS ConnectionStatus; + ULONG RetryCount; + + for (RetryCount = 0; RetryCount < 2; ++RetryCount) + { + /* Suspend DMA engine and wait for port idle */ + if (!AtaAhciEnterIdleState(ChanData)) + { + /* + * We can skip the ST bit clearing part while the port in a fatal error condition. + * Transmit a COMRESET to recover. + */ + AtaAhciSendComReset(ChanData); + + ConnectionStatus = CONN_STATUS_FAILURE; + continue; + } + + AtaAhciSetupDmaMemoryAddress(ChanData); + AtaAhciSpinUp(ChanData); + + /* Enable FIS reception from the device */ + AtaAhciStartFisReceiveProcessAndWait(ChanData); + + /* Start device detection */ + AtaAhciSendComReset(ChanData); + + if (!AtaAhciPhyCheckDevicePresence(ChanData)) + { + INFO("CH %lu: Device not connected %08lx\n", + ChanData->Channel, + AHCI_PORT_READ(ChanData->IoBase, PxSataStatus)); + + ConnectionStatus = CONN_STATUS_NO_DEVICE; + break; + } + + INFO("CH %lu: Link up %08lx\n", + ChanData->Channel, + AHCI_PORT_READ(ChanData->IoBase, PxSataStatus)); + + /* Determine if communication is established */ + if (!AtaAhciPhyWaitForReady(ChanData)) + { + WARN("CH %lu: Unable to establish link %08lx\n", + ChanData->Channel, + AHCI_PORT_READ(ChanData->IoBase, PxSataStatus)); + + ConnectionStatus = CONN_STATUS_FAILURE; + continue; + } + + /* Required for a D2H FIS */ + AHCI_PORT_WRITE(ChanData->IoBase, PxSataError, 0xFFFFFFFF); + /* AtaChanEnableInterruptsSync(ChanData, TRUE); */ + + if (!AtaAhciWaitForDeviceReady(ChanData, AHCI_DELAY_READY_DRIVE)) + { + WARN("CH %lu: Wait for ready failed %08lx\n", + ChanData->Channel, + AHCI_PORT_READ(ChanData->IoBase, PxTaskFileData)); + + ConnectionStatus = CONN_STATUS_FAILURE; + continue; + } + + INFO("CH %lu: Device is ready %08lx\n", + ChanData->Channel, + AHCI_PORT_READ(ChanData->IoBase, PxTaskFileData)); + + ConnectionStatus = CONN_STATUS_DEV_UNKNOWN; + break; + } + + return ConnectionStatus; +} + +ULONG +AtaAhciEnumerateChannel( + _In_ PVOID ChannelContext) +{ + PCHANNEL_DATA_AHCI ChanData = ChannelContext; + ULONG RetryCount, PortCount; + ATA_CONNECTION_STATUS ConnectionStatus; + BOOLEAN CheckForPmp = TRUE; + + if (AtaAhciIsHbaHotRemoved(ChanData)) + return 0; + + AtaChanEnableInterruptsSync(ChanData, FALSE); + + /* COMRESET acts as a bus reset for the SATA port */ + ChanData->PortNotification(AtaResetDetected, ChanData->PortContext, 0xFFFFFFFF); + + for (RetryCount = 3; RetryCount > 0; RetryCount--) + { + ChanData->ChanInfo &= ~CHANNEL_FLAG_IS_PMP; + + ConnectionStatus = AtaAhciPhyCheckConnection(ChanData); + if (ConnectionStatus != CONN_STATUS_DEV_UNKNOWN) + { + PortCount = 0; + break; + } + + /* + * Check for a Port Multiplier. + * + * If the last discovery resulted in a failure, + * threat this device as a non-port multiplier device + * (i.e. a SATA device that is connected to device Port 0). + */ + if ((ChanData->Controller->AhciCapabilities & AHCI_CAP_SPM) && + (RetryCount > 1) && CheckForPmp) + { + INFO("CH %lu: Trying to detect PMP\n", ChanData->Channel); + + ConnectionStatus = AtaAhciPmpDetect(ChanData, &CheckForPmp); + if (ConnectionStatus == CONN_STATUS_FAILURE) + continue; + + if (ConnectionStatus == CONN_STATUS_DEV_UNKNOWN) + { + INFO("CH %lu: Discovered a Port Multiplier\n", ChanData->Channel); + + ChanData->ChanInfo |= CHANNEL_FLAG_IS_PMP; + + if (!AtaAhciPmpIdentifyPmp(ChanData, &PortCount)) + continue; + + break; + } + } + else + { + AtaAhciStartCommandListProcess(ChanData); + } + + PortCount = 1; + break; + } + + if (ConnectionStatus == CONN_STATUS_FAILURE) + { + /* Channel reset failed, clear active DMA commands, so we can release pending IRPs safely */ + AtaAhciStopCommandListProcessAndWait(ChanData); + AtaAhciPerformCommandListOverride(ChanData); + } + else if (PortCount == 0) + { + AtaAhciPhyEnterListenMode(ChanData); + } + + AtaChanEnableInterruptsSync(ChanData, TRUE); + + ChanData->TotalPortCount = PortCount; + return PortCount; +} + +static +BOOLEAN +AtaAhciPmpCheckSendComReset( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ ULONG PortNumber) +{ + ULONG SataControl; + UCHAR SrbStatus; + + INFO("CH %lu: Transmit a COMRESET on the interface\n", ChanData->Channel); + + /* Clear errors */ + SrbStatus = AtaAhciPmpWrite(ChanData, PortNumber, ATA_SERROR, 0xFFFFFFFF); + if (SrbStatus != SRB_STATUS_SUCCESS) + return FALSE; + + SataControl = AHCI_PXCTL_DET_RESET; + SrbStatus = AtaAhciPmpWrite(ChanData, PortNumber, ATA_SCONTROL, SataControl); + if (SrbStatus != SRB_STATUS_SUCCESS) + return FALSE; + + KeStallExecutionProcessor(1000); + + SataControl &= ~AHCI_PXCTL_DET_MASK; + SataControl |= AHCI_PXCTL_DET_IDLE; + SrbStatus = AtaAhciPmpWrite(ChanData, PortNumber, ATA_SCONTROL, SataControl); + if (SrbStatus != SRB_STATUS_SUCCESS) + return FALSE; + + /* Clear errors */ + SrbStatus = AtaAhciPmpWrite(ChanData, PortNumber, ATA_SERROR, 0xFFFFFFFF); + if (SrbStatus != SRB_STATUS_SUCCESS) + return FALSE; + + return TRUE; +} + +static +ATA_CONNECTION_STATUS +AtaAhciPmpPhyCheckDevicePresence( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ ULONG PortNumber) +{ + ULONG i, SataStatus; + UCHAR SrbStatus; + + for (i = 0; i < AHCI_DELAY_PMP_DET_PRESENSE; ++i) + { + SrbStatus = AtaAhciPmpRead(ChanData, PortNumber, ATA_SSTATUS, &SataStatus); + if (SrbStatus == SRB_STATUS_ERROR) + continue; + else if (SrbStatus != SRB_STATUS_SUCCESS) + return CONN_STATUS_FAILURE; + + if ((SataStatus & AHCI_PXSSTS_DET_MASK) != AHCI_PXSSTS_DET_NO_DEVICE) + { + INFO("CH %lu: PMP device %lu link up %08lx\n", + ChanData->Channel, + PortNumber, + SataStatus); + return CONN_STATUS_DEV_UNKNOWN; + } + + AtaSleep(); + } + + INFO("CH %lu: PMP device %lu not connected %08lx\n", + ChanData->Channel, + PortNumber, + SataStatus); + return CONN_STATUS_NO_DEVICE; +} + +static +ATA_CONNECTION_STATUS +AtaAhciPmpPhyWaitForReady( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ ULONG PortNumber) +{ + ULONG i, SataStatus; + UCHAR SrbStatus; + + for (i = 0; i < AHCI_DELAY_PMP_DET_PRESENSE; ++i) + { + SrbStatus = AtaAhciPmpRead(ChanData, PortNumber, ATA_SSTATUS, &SataStatus); + if (SrbStatus == SRB_STATUS_ERROR) + continue; + else if (SrbStatus != SRB_STATUS_SUCCESS) + return CONN_STATUS_FAILURE; + + if ((SataStatus & AHCI_PXSSTS_DET_MASK) == AHCI_PXSSTS_DET_PHY_OK) + { + INFO("CH %lu: PMP device %lu link up %08lx\n", + ChanData->Channel, + PortNumber, + SataStatus); + return CONN_STATUS_DEV_UNKNOWN; + } + + AtaSleep(); + } + + INFO("CH %lu: PMP device %lu unable to establish link %08lx\n", + ChanData->Channel, + PortNumber, + SataStatus); + return CONN_STATUS_NO_DEVICE; +} + +static +ATA_CONNECTION_STATUS +AtaPmpCheckConnection( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ ULONG PortNumber) +{ + ATA_CONNECTION_STATUS ConnectionStatus; + + INFO("CH %lu: Reset PMP port %lu\n", ChanData->Channel, PortNumber); + + ChanData->PortNotification(AtaResetDetected, ChanData->PortContext, 1 << PortNumber); + + if (!AtaAhciPmpCheckSendComReset(ChanData, PortNumber)) + return CONN_STATUS_FAILURE; + + ConnectionStatus = AtaAhciPmpPhyCheckDevicePresence(ChanData, PortNumber); + if (ConnectionStatus != CONN_STATUS_DEV_UNKNOWN) + return ConnectionStatus; + + ConnectionStatus = AtaAhciPmpPhyWaitForReady(ChanData, PortNumber); + if (ConnectionStatus != CONN_STATUS_DEV_UNKNOWN) + return ConnectionStatus; + + return CONN_STATUS_DEV_UNKNOWN; +} + +static +ATA_CONNECTION_STATUS +AtaAhciPmpIdentifyDeviceBehindPmp( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ ULONG PortNumber) +{ + ATA_CONNECTION_STATUS ConnectionStatus; + UCHAR SrbStatus; + ULONG RetryCount; + + ASSERT(!(ChanData->ChanInfo & CHANNEL_FLAG_FBS_ENABLED)); + + ConnectionStatus = AtaPmpCheckConnection(ChanData, PortNumber); + if (ConnectionStatus != CONN_STATUS_DEV_UNKNOWN) + return ConnectionStatus; + + /* Issue a soft reset */ + for (RetryCount = 0; RetryCount < 2; RetryCount++) + { + SrbStatus = AtaAhciPmpWrite(ChanData, PortNumber, ATA_SERROR, 0xFFFFFFFF); + if (SrbStatus == SRB_STATUS_ERROR) + continue; + else if (SrbStatus != SRB_STATUS_SUCCESS) + goto Failure; + + SrbStatus = AtaAhciSendResetFis(ChanData, PortNumber, TRUE, 1000); + if (SrbStatus == SRB_STATUS_ERROR) + continue; + else if (SrbStatus != SRB_STATUS_SUCCESS) + goto Failure; + + /* SRST pulse width */ + KeStallExecutionProcessor(20); + + SrbStatus = AtaAhciSendResetFis(ChanData, PortNumber, FALSE, 1000); + if (SrbStatus == SRB_STATUS_ERROR) + continue; + else if (SrbStatus != SRB_STATUS_SUCCESS) + goto Failure; + } + + return CONN_STATUS_DEV_UNKNOWN; + +Failure: + WARN("CH %lu: PMP port %lu soft reset failed %02x\n", + ChanData->Channel, PortNumber, SrbStatus); + return CONN_STATUS_FAILURE; +} + +ATA_CONNECTION_STATUS +AtaAhciIdentifyDevice( + _In_ PVOID ChannelContext, + _In_ ULONG DeviceNumber) +{ + PCHANNEL_DATA_AHCI ChanData = ChannelContext; + ULONG Signature; + + if (ChanData->ChanInfo & CHANNEL_FLAG_IS_PMP) + { + ATA_CONNECTION_STATUS ConnectionStatus; + + ConnectionStatus = AtaAhciPmpIdentifyDeviceBehindPmp(ChanData, DeviceNumber); + + /* Enable use of FIS-based switching once the port multiplier has enumerated */ + if ((ChanData->ChanInfo & CHANNEL_FLAG_HAS_FBS) && + (DeviceNumber == (ChanData->TotalPortCount - 1))) + { + AtaAhciStopCommandListProcessAndWait(ChanData); + AtaAhciFbsControl(ChanData, TRUE); + AtaAhciStartCommandListProcess(ChanData); + } + + if (ConnectionStatus != CONN_STATUS_DEV_UNKNOWN) + return ConnectionStatus; + } + else + { + ASSERT(DeviceNumber == 0); + } + + Signature = AHCI_PORT_READ(ChanData->IoBase, PxSignature); + INFO("CH %lu: Received signature %08lx\n", ChanData->Channel, Signature); + + if ((Signature & AHCI_PXSIG_MASK) == (AHCI_PXSIG_ATAPI & AHCI_PXSIG_MASK)) + { + AtaAhciAtapiLedControl(ChanData, TRUE); + return CONN_STATUS_DEV_ATAPI; + } + + AtaAhciAtapiLedControl(ChanData, FALSE); + return CONN_STATUS_DEV_ATA; +} + +VOID +AtaAhciStopDma( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + AtaAhciStopCommandListProcessAndWait(ChanData); + AtaAhciPerformCommandListOverride(ChanData); + AtaAhciStopFisReceiveProcessAndWait(ChanData); +} + +VOID +AtaAhciResetChannel( + _In_ PVOID ChannelContext) +{ + /* The channel reset is done unconditionally in AtaAhciEnumerateChannel() */ + NOTHING; +} + +/** + * @brief Captures and saves a dump of the AHCI task file registers, + * *except* the values in the Fis->Status and Fis->Error fields. + */ +static +VOID +AtaAhciSaveReceivedFisArea( + _In_ AHCI_FIS_DEVICE_TO_HOST* __restrict Fis, + _Inout_ ATA_DEVICE_REQUEST* __restrict Request) +{ + PATA_TASKFILE TaskFile = &Request->Output; + + TaskFile->SectorCount = Fis->SectorCount; + TaskFile->LowLba = Fis->LbaLow; + TaskFile->MidLba = Fis->LbaMid; + TaskFile->HighLba = Fis->LbaHigh; + TaskFile->DriveSelect = Fis->Device; + + if (Request->Flags & REQUEST_FLAG_LBA48) + { + TaskFile->FeatureEx = 0; // FIS byte 11 is reserved + TaskFile->SectorCountEx = Fis->SectorCountEx; + TaskFile->LowLbaEx = Fis->LbaLowEx; + TaskFile->MidLbaEx = Fis->LbaMidEx; + TaskFile->HighLbaEx = Fis->LbaHighEx; + } +} + +VOID +AtaAhciSaveTaskFile( + _In_ PCHANNEL_DATA_AHCI ChanData, + _Inout_ PATA_DEVICE_REQUEST Request, + _In_ BOOLEAN ProcessErrorStatus) +{ + union + { + PAHCI_FIS_PIO_SETUP PioSetup; + PAHCI_FIS_DEVICE_TO_HOST DeviceToHost; + } Fis; + PAHCI_RECEIVED_FIS ReceivedFis; + + ReceivedFis = ChanData->ReceivedFis; + + if (ChanData->ChanInfo & CHANNEL_FLAG_FBS_ENABLED) + ReceivedFis += DEV_NUMBER(Request->Device); + + Request->Flags |= REQUEST_FLAG_HAS_TASK_FILE; + + if (ProcessErrorStatus) + { + /* + * Device to Host FIS on failed commands. + * The Request->Output.Status and Request->Output.Error fields should have saved earlier. + */ + Fis.DeviceToHost = &ReceivedFis->DeviceToHostFis; + } + else + { + /* Check for successful queued commands (see SATA 3.5 specification 11.14) */ + if (Request->Flags & REQUEST_FLAG_NCQ) + { + PAHCI_FIS_SET_DEVICE_BITS SetDeviceBitsFis; + + /* + * The SDB FIS was received and we do not have enough information + * for the rest of the registers. Emulate the content of the task file registers. + */ + RtlCopyMemory(&Request->Output, &Request->TaskFile, sizeof(Request->TaskFile)); + + SetDeviceBitsFis = &ReceivedFis->SetDeviceBitsFis; + Request->Output.Status = SetDeviceBitsFis->Status; + Request->Output.Error = SetDeviceBitsFis->Error; + return; + } + /* Check for successful PIO Data-In commands (see SATA 3.5 specification 11.8) */ + else if (!(Request->Flags & REQUEST_FLAG_DMA) && (Request->Flags & REQUEST_FLAG_DATA_IN)) + { + /* In this case we have received a PIO Setup FIS */ + Fis.PioSetup = &ReceivedFis->PioSetupFis; + + Request->Output.Status = Fis.PioSetup->EStatus; + Request->Output.Error = Fis.PioSetup->Error; + } + else + { + ULONG TaskFileData; + + /* Otherwise, we have received a Device to Host FIS */ + Fis.DeviceToHost = &ReceivedFis->DeviceToHostFis; + + TaskFileData = AHCI_PORT_READ(ChanData->IoBase, PxTaskFileData); + Request->Output.Status = (TaskFileData & AHCI_PXTFD_STATUS_MASK); + Request->Output.Error = + (TaskFileData & AHCI_PXTFD_ERROR_MASK) >> AHCI_PXTFD_ERROR_SHIFT; + } + } + + AtaAhciSaveReceivedFisArea(Fis.DeviceToHost, Request); +} + +VOID +AtaAhciHandleFatalError( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ULONG CurrentCommandSlot, TaskFileData; + ULONG i, CmdStatus, SlotsBitmap, FailedSlot; + PATA_DEVICE_REQUEST Request; + + Request = NULL; + CurrentCommandSlot = AHCI_PXCMD_CCS(AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus)); + + /* Clear the ST bit. This also clears PxCI, PxSACT, and PxCMD.CCS */ + AtaAhciStopCommandListProcess(ChanData); + + if (AtaAhciIsHbaHotRemoved(ChanData)) + goto Done; + + /* Wait for 500ms */ + for (i = 50000; i > 0; i--) + { + CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + if (!(CmdStatus & AHCI_PXCMD_CR)) + break; + + KeStallExecutionProcessor(10); + } + if (i == 0) + { + /* The interface is hung */ + ERR("CH %lu: Failed to stop the command list DMA engine %08lx\n", + ChanData->Channel, + CmdStatus); + goto Done; + } + + TaskFileData = AHCI_PORT_READ(ChanData->IoBase, PxTaskFileData); + if (TaskFileData & (IDE_STATUS_BUSY | IDE_STATUS_DRQ)) + { + /* Put the device to the idle state */ + ERR("CH %lu: Busy TFD %08lx\n", ChanData->Channel, TaskFileData); + goto Done; + } + + /* Clear errors */ + AHCI_PORT_WRITE(ChanData->IoBase, PxSataError, + AHCI_PORT_READ(ChanData->IoBase, PxSataError)); + + /* NCQ recovery is needed */ + if (ChanData->ActiveQueuedSlotsBitmap != 0) + { + AtaAhciStartCommandListProcess(ChanData); + + /* Find some active command */ + NT_VERIFY(_BitScanForward(&FailedSlot, ChanData->ActiveQueuedSlotsBitmap) != 0); + + Request = ChanData->Slots[FailedSlot]; + ASSERT(Request); + ASSERT(Request->Slot == FailedSlot); + ASSERT(Request->Flags & REQUEST_FLAG_NCQ); + goto Done; + } + + if (IsPowerOfTwo(ChanData->ActiveSlotsBitmap)) + { + /* We have exactly one slot is outstanding */ + SlotsBitmap = ChanData->ActiveSlotsBitmap; + } + else if (ChanData->ActiveSlotsBitmap & (1 << CurrentCommandSlot)) + { + SlotsBitmap = 1 << CurrentCommandSlot; + } + else + { + /* Indicates that the error bit is spurious *or* the exact slot is not known */ + if (ChanData->ActiveSlotsBitmap != 0) + { + ERR("CH %lu: Invalid slot received from the HBA %08lX --> %08lX\n", + ChanData->Channel, + ChanData->ActiveSlotsBitmap, + 1 << CurrentCommandSlot); + } + else + { + WARN("CH %lu: Spurious error interrupt %08lX\n", + ChanData->Channel, + AHCI_PORT_READ(ChanData->IoBase, PxInterruptStatus)); + } + goto Done; + } + + AtaAhciStartCommandListProcess(ChanData); + + NT_VERIFY(_BitScanForward(&FailedSlot, SlotsBitmap) != 0); + + Request = ChanData->Slots[FailedSlot]; + ASSERT(Request); + ASSERT(Request->Slot == FailedSlot); + + /* Save the error */ + Request->SrbStatus = SRB_STATUS_ERROR; + Request->Output.Status = (TaskFileData & AHCI_PXTFD_STATUS_MASK); + Request->Output.Error = (TaskFileData & AHCI_PXTFD_ERROR_MASK) >> AHCI_PXTFD_ERROR_SHIFT; + + /* Save the current task file for the "ATA LBA field" (SAT-6 11.7) */ + if (!(Request->Device->TransportFlags & DEVICE_IS_ATAPI) || + (Request->Flags & REQUEST_FLAG_SAVE_TASK_FILE)) + { + AtaAhciSaveTaskFile(ChanData, Request, TRUE); + } + +Done: + /* Request arbitration from the port worker */ + ChanData->PortNotification(AtaRequestFailed, ChanData->PortContext, Request); +} + +VOID +AtaAhciHandlePortStateChange( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ ULONG InterruptStatus) +{ + ULONG SataStatus; + + if (InterruptStatus & AHCI_PXIRQ_DMPS) + { + ULONG CmdStatus = AHCI_PORT_READ(ChanData->IoBase, PxCmdStatus); + + if (CmdStatus & AHCI_PXCMD_MPSP) + { + WARN("CH %lu: Mechanical presence switch has changed %08lx\n", + ChanData->Channel, CmdStatus); + goto Notify; + } + } + + SataStatus = AHCI_PORT_READ(ChanData->IoBase, PxSataStatus) & AHCI_PXSSTS_DET_MASK; + if (SataStatus == AHCI_PXSSTS_DET_PHY_OK) + INFO("CH %lu: Device hot plug detected %08lx\n", ChanData->Channel, SataStatus); + else + INFO("CH %lu: Device removal detected %08lx\n", ChanData->Channel, SataStatus); + +Notify: + /* Schedule a port reset */ + ChanData->PortNotification(AtaBusChangeDetected, ChanData->PortContext); +} + +BOOLEAN +AtaAhciDowngradeInterfaceSpeed( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ULONG SataSpeed, SataControl; + + SataSpeed = AHCI_PORT_READ(ChanData->IoBase, PxSataStatus) & AHCI_PXSSTS_SPD_MASK; + if (SataSpeed != AHCI_PXSSTS_SPD_SATA1) + { + SataControl = AHCI_PORT_READ(ChanData->IoBase, PxSataControl); + + if ((SataControl & AHCI_PXCTL_SPD_MASK) != AHCI_PXCTL_SPD_LIMIT_NONE) + { + SataControl &= ~AHCI_PXCTL_SPD_MASK; + SataControl |= SataSpeed - AHCI_PXCTL_SPD_LIMIT_LEVEL; + + WARN("CH %lu: Downgrading interface speed to %08lx\n", ChanData->Channel, SataControl); + + AHCI_PORT_WRITE(ChanData->IoBase, PxSataControl, SataControl); + return TRUE; + } + } + + INFO("CH %lu: Unable to downgrade interface speed %08lx\n", ChanData->Channel, SataSpeed); + return FALSE; +} + +ULONG +AtaAhciChannelGetMaximumDeviceCount( + _In_ PVOID ChannelContext) +{ + PCHANNEL_DATA_AHCI ChanData = ChannelContext; + PATA_CONTROLLER Controller = ChanData->Controller; + + if (Controller->AhciCapabilities & AHCI_CAP_SPM) + return AHCI_MAX_PMP_DEVICES; + + return AHCI_MAX_PORT_DEVICES; +} diff --git a/drivers/storage/ide/pciidex/chipset/ahci_io.c b/drivers/storage/ide/pciidex/chipset/ahci_io.c new file mode 100644 index 00000000000..ce6f371bb67 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/ahci_io.c @@ -0,0 +1,429 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: AHCI I/O request handling + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* FUNCTIONS ******************************************************************/ + +static +VOID +AtaAhciBeginHostToDeviceFis( + _In_ ATA_DEVICE_REQUEST* __restrict Request, + _Out_ AHCI_FIS_HOST_TO_DEVICE* __restrict Fis) +{ + RtlZeroMemory(Fis, sizeof(*Fis)); + + Fis->Type = AHCI_FIS_REGISTER_HOST_TO_DEVICE; + Fis->Flags = UPDATE_COMMAND | DEV_NUMBER(Request->Device); + + if (Request->Flags & REQUEST_FLAG_SET_DEVICE_REGISTER) + Fis->Device = Request->TaskFile.DriveSelect; + else + Fis->Device = IDE_DRIVE_SELECT; + + Fis->Control = IDE_DC_ALWAYS; +} + +static +VOID +AtaAhciBuildPacketCommandFis( + _In_ ATA_DEVICE_REQUEST* __restrict Request, + _Out_ AHCI_FIS_HOST_TO_DEVICE* __restrict Fis) +{ + Fis->Command = IDE_COMMAND_ATAPI_PACKET; + + if (Request->Flags & REQUEST_FLAG_DMA) + { + /* DMA transfer */ + if ((Request->Device->TransportFlags & DEVICE_NEED_DMA_DIRECTION) && + (Request->Flags & REQUEST_FLAG_DATA_IN)) + { + /* Some SATA-to-PATA bridges require the DMADIR bit to be set */ + Fis->Features = IDE_FEATURE_DMA | IDE_FEATURE_DMADIR; + } + else + { + Fis->Features = IDE_FEATURE_DMA; + } + } + else + { + USHORT ByteCount; + + /* PIO transfer */ + ByteCount = min(Request->DataTransferLength, ATAPI_MAX_DRQ_DATA_BLOCK); + Fis->LbaMid = (UCHAR)ByteCount; + Fis->LbaHigh = ByteCount >> 8; + } +} + +static +VOID +AtaAhciBuildAtaCommandFis( + _In_ ATA_DEVICE_REQUEST* __restrict Request, + _Out_ AHCI_FIS_HOST_TO_DEVICE* __restrict Fis) +{ + PATA_TASKFILE TaskFile = &Request->TaskFile; + + Fis->Command = TaskFile->Command; + Fis->Features = TaskFile->Feature; + Fis->LbaLow = TaskFile->LowLba; + Fis->LbaMid = TaskFile->MidLba; + Fis->LbaHigh = TaskFile->HighLba; + Fis->SectorCount = TaskFile->SectorCount; + + /* Unique queue tag */ + if (Request->Flags & REQUEST_FLAG_NCQ) + Fis->SectorCount |= Request->Slot << 3; + + if (Request->Flags & REQUEST_FLAG_LBA48) + { + Fis->LbaLowEx = TaskFile->LowLbaEx; + Fis->LbaMidEx = TaskFile->MidLbaEx; + Fis->LbaHighEx = TaskFile->HighLbaEx; + Fis->FeaturesEx = TaskFile->FeatureEx; + Fis->SectorCountEx = TaskFile->SectorCountEx; + } + + if (Request->Flags & REQUEST_FLAG_SET_ICC_FIELD) + Fis->Icc = TaskFile->Icc; + + if (Request->Flags & REQUEST_FLAG_SET_AUXILIARY_FIELD) + Fis->Auxiliary = TaskFile->Auxiliary; +} + +/** Copy the CDB bytes (12 or 16 bytes) */ +static +VOID +AtaAhciTransferACMDRegion( + _In_ VOID* __restrict Destination, + _In_ VOID* __restrict Source, + _In_ ATA_IO_CONTEXT_COMMON* __restrict Device) +{ + /* Both addresses are 8-byte aligned */ +#if defined(_WIN64) + PULONG64 Dest = Destination, Src = Source; + + *Dest++ = *Src++; + + if ((Device->CdbSize & (sizeof(ULONG64) - 1)) == 0) + *Dest = *Src; + else + *(PULONG)Dest = *(PULONG)Src; +#else + PULONG Dest = Destination, Src = Source; + + *Dest++ = *Src++; + *Dest++ = *Src++; + *Dest++ = *Src++; + + if ((Device->CdbSize & (sizeof(ULONG64) - 1)) == 0) + *Dest = *Src; +#endif +} +C_ASSERT(FIELD_OFFSET(ATA_DEVICE_REQUEST, Cdb) % sizeof(ULONG64) == 0); +C_ASSERT(FIELD_OFFSET(AHCI_COMMAND_TABLE, AtapiCommand) % sizeof(ULONG64) == 0); + +BOOLEAN +AtaAhciStartIo( + _In_ PVOID ChannelContext, + _In_ PATA_DEVICE_REQUEST Request) +{ + PCHANNEL_DATA_AHCI ChanData = ChannelContext; + PULONG IoBase = ChanData->IoBase; + ULONG IssueSlot; + + IssueSlot = 1 << Request->Slot; + + ChanData->ActiveSlotsBitmap |= IssueSlot; + + if (Request->Flags & REQUEST_FLAG_NCQ) + { + ChanData->ActiveQueuedSlotsBitmap |= IssueSlot; + AHCI_PORT_WRITE(IoBase, PxSataActive, IssueSlot); + } + + if ((ChanData->ChanInfo & CHANNEL_FLAG_FBS_ENABLED) && + (ChanData->LastFbsDeviceNumber != DEV_NUMBER(Request->Device))) + { + ULONG Control; + + ChanData->LastFbsDeviceNumber = DEV_NUMBER(Request->Device); + + Control = AHCI_FBS_ENABLE; + Control |= DEV_NUMBER(Request->Device) << AHCI_FBS_ISSUE_SHIFT; + AHCI_PORT_WRITE(IoBase, PxFisSwitchingControl, Control); + } + + AHCI_PORT_WRITE(IoBase, PxCommandIssue, IssueSlot); + return FALSE; +} + +VOID +AtaAhciPrepareIo( + _In_ PVOID ChannelContext, + _In_ PATA_DEVICE_REQUEST Request) +{ + PCHANNEL_DATA_AHCI ChanData = ChannelContext; + PAHCI_COMMAND_TABLE CommandTable; + PAHCI_COMMAND_HEADER CommandHeader; + PAHCI_FIS_HOST_TO_DEVICE Fis; + ULONG Control; + + Control = sizeof(*Fis) / sizeof(ULONG); + Control |= DEV_NUMBER(Request->Device) << AHCI_COMMAND_HEADER_PMP_SHIFT; + + if (Request->Flags & (REQUEST_FLAG_DATA_IN | REQUEST_FLAG_DATA_OUT)) + { + ASSERT(Request->Flags & REQUEST_FLAG_HAS_SG_LIST); + + Control |= Request->SgList->NumberOfElements << AHCI_COMMAND_HEADER_PRDT_LENGTH_SHIFT; + + if (Request->Flags & REQUEST_FLAG_DATA_OUT) + { + Control |= AHCI_COMMAND_HEADER_WRITE; + } + } + + CommandTable = ChanData->CommandTable[Request->Slot]; + + Fis = &CommandTable->HostToDeviceFis; + + AtaAhciBeginHostToDeviceFis(Request, Fis); + + if (Request->Flags & REQUEST_FLAG_PACKET_COMMAND) + { + AtaAhciBuildPacketCommandFis(Request, Fis); + AtaAhciTransferACMDRegion(CommandTable->AtapiCommand, + Request->Cdb, + Request->Device); + + Control |= AHCI_COMMAND_HEADER_ATAPI; + } + else + { + AtaAhciBuildAtaCommandFis(Request, Fis); + } + + CommandHeader = &ChanData->CommandList->CommandHeader[Request->Slot]; + CommandHeader->Control = Control; + CommandHeader->PrdByteCount = 0; + + KeFlushIoBuffers(Request->Mdl, !!(Request->Flags & REQUEST_FLAG_DATA_IN), TRUE); +} + +VOID +AtaAhciPreparePrdTable( + _In_ PVOID ChannelContext, + _In_ PATA_DEVICE_REQUEST Request, + _In_ SCATTER_GATHER_LIST* __restrict SgList) +{ + PCHANNEL_DATA_AHCI ChanData = ChannelContext; + PAHCI_PRD_TABLE_ENTRY PrdTableEntry; + ULONG i; +#if DBG + BOOLEAN Is64BitDma = !!(ChanData->Controller->AhciCapabilities & AHCI_CAP_S64A); +#endif + + ASSUME(SgList->NumberOfElements > 0); + ASSERT(SgList->NumberOfElements < AHCI_MAX_PRDT_ENTRIES); + + PrdTableEntry = ChanData->CommandTable[Request->Slot]->PrdTable; + + for (i = 0; i < SgList->NumberOfElements; ++i) + { + ASSERT(SgList->Elements[i].Length != 0); + ASSERT(SgList->Elements[i].Length < AHCI_MAX_PRD_LENGTH); + ASSERT((SgList->Elements[i].Length & ATA_MIN_BUFFER_ALIGNMENT) == 0); + ASSERT(Is64BitDma || (SgList->Elements[i].Address.HighPart == 0)); + ASSERT(i < ChanData->MaximumPhysicalPages); + + PrdTableEntry->DataBaseLow = SgList->Elements[i].Address.LowPart; + PrdTableEntry->DataBaseHigh = SgList->Elements[i].Address.HighPart; + PrdTableEntry->ByteCount = SgList->Elements[i].Length - 1; + + ++PrdTableEntry; + } + + /* Enable IRQ on last entry */ + --PrdTableEntry; + PrdTableEntry->ByteCount |= AHCI_PRD_INTERRUPT_ON_COMPLETION; +} + +BOOLEAN +AtaAhciAllocateSlot( + _In_ PVOID ChannelContext, + _In_ PATA_DEVICE_REQUEST Request, + _In_ BOOLEAN Allocate) +{ + PCHANNEL_DATA_AHCI ChanData = ChannelContext; + + if (Allocate) + { + /* + * We cannot issue commands to more than one device behind the Port Multiplier + * that supports only command-based switching. + */ + if ((ChanData->ChanInfo & CHANNEL_FLAG_IS_PMP) && + !(ChanData->ChanInfo & CHANNEL_FLAG_FBS_ENABLED)) + { + if ((ChanData->LastPmpDeviceNumber != 0xFF) && + (ChanData->LastPmpDeviceNumber != DEV_NUMBER(Request->Device))) + { + return FALSE; + } + + ChanData->LastPmpDeviceNumber = DEV_NUMBER(Request->Device); + + Request->Flags |= REQUEST_FLAG_DEVICE_EXCLUSIVE_ACCESS; + } + } + else + { + if (Request->Flags & REQUEST_FLAG_DEVICE_EXCLUSIVE_ACCESS) + { + ChanData->LastPmpDeviceNumber = 0xFF; + } + } + + return TRUE; +} + +static +VOID +AtaAhciPortCompleteCommands( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ ULONG CommandsCompleted) +{ + ULONG Slot; + + while (_BitScanForward(&Slot, CommandsCompleted) != 0) + { + PATA_DEVICE_REQUEST Request; + UCHAR SrbStatus; + + CommandsCompleted &= ~(1 << Slot); + + Request = ChanData->Slots[Slot]; + ASSERT(Request); + ASSERT(Request->Slot == Slot); + + SrbStatus = SRB_STATUS_SUCCESS; + + if (!(Request->Flags & REQUEST_FLAG_NCQ) && + Request->Flags & (REQUEST_FLAG_DATA_IN | REQUEST_FLAG_DATA_OUT)) + { + PAHCI_COMMAND_HEADER CommandHeader = &ChanData->CommandList->CommandHeader[Slot]; + + /* This indicates a residual underrun */ + if (CommandHeader->PrdByteCount < Request->DataTransferLength) + { + Request->DataTransferLength = CommandHeader->PrdByteCount; + SrbStatus = SRB_STATUS_DATA_OVERRUN; + } + } + Request->SrbStatus = SrbStatus; + + /* Save the latest copy of the task file registers */ + if (Request->Flags & REQUEST_FLAG_SAVE_TASK_FILE) + { + AtaAhciSaveTaskFile(ChanData, Request, FALSE); + } + } +} + +static +VOID +AtaAhciPortHandleInterrupt( + _In_ PCHANNEL_DATA_AHCI ChanData) +{ + ULONG InterruptStatus, CommandsIssued, CommandsCompleted; + AHCI_PORT_REGISTER CommandRegister; + + /* Clear all pending events */ + InterruptStatus = AHCI_PORT_READ(ChanData->IoBase, PxInterruptStatus); + AHCI_PORT_WRITE(ChanData->IoBase, PxInterruptStatus, InterruptStatus); + + TRACE("CH %lu: Intr %08lx\n", ChanData->Channel, InterruptStatus); + + /* Clear interface errors */ + AHCI_PORT_WRITE(ChanData->IoBase, PxSataError, + AHCI_PORT_READ(ChanData->IoBase, PxSataError)); + + /* Determine commands that have completed */ + if (ChanData->ActiveQueuedSlotsBitmap != 0) + CommandRegister = PxSataActive; + else + CommandRegister = PxCommandIssue; + CommandsIssued = AHCI_PORT_READ(ChanData->IoBase, CommandRegister); + CommandsCompleted = ~CommandsIssued & ChanData->ActiveSlotsBitmap; + + /* Complete processed commands */ + if (CommandsCompleted != 0) + { + ChanData->ActiveSlotsBitmap &= ~CommandsCompleted; + ChanData->ActiveQueuedSlotsBitmap &= ~CommandsCompleted; + + AtaAhciPortCompleteCommands(ChanData, CommandsCompleted); + ChanData->PortNotification(AtaRequestComplete, ChanData->PortContext, CommandsCompleted); + } + + /* Asynchronous notification received */ + if (InterruptStatus & AHCI_PXIRQ_SDBS) + { + ULONG Message = AHCI_PORT_READ(ChanData->IoBase, PxSataNotification); + + if (Message != 0) + { + AHCI_PORT_WRITE(ChanData->IoBase, PxSataNotification, Message); + + WARN("CH %lu: Notification %08lx arrived\n", ChanData->Channel, Message); + + ChanData->PortNotification(AtaAsyncNotificationDetected, ChanData->PortContext, 0x1); + } + } + + /* Handle various errors and link change events */ + if (InterruptStatus & AHCI_PXIRQ_PORT_STATUS) + { + AtaAhciHandlePortStateChange(ChanData, InterruptStatus); + } + + if (InterruptStatus & AHCI_PXIRQ_FATAL_ERROR) + { + AtaAhciHandleFatalError(ChanData); + } +} + +BOOLEAN +NTAPI +AtaAhciHbaIsr( + _In_ PKINTERRUPT Interrupt, + _In_ PVOID Context) +{ + PATA_CONTROLLER Controller = Context; + ULONG Port, InterruptStatus, PortInterruptBitmap; + + InterruptStatus = AHCI_HBA_READ(Controller->IoBase, HbaInterruptStatus); + if (InterruptStatus == 0) + return FALSE; + + PortInterruptBitmap = InterruptStatus & Controller->ChannelBitmap; + while (_BitScanForward(&Port, PortInterruptBitmap) != 0) + { + PortInterruptBitmap &= ~(1 << Port); + + AtaAhciPortHandleInterrupt(Controller->Channels[Port]); + } + + /* Clear pending HBA interrupts */ + AHCI_HBA_WRITE(Controller->IoBase, HbaInterruptStatus, InterruptStatus); + + return TRUE; +} diff --git a/drivers/storage/ide/pciidex/chipset/amd.c b/drivers/storage/ide/pciidex/chipset/amd.c new file mode 100644 index 00000000000..6d38a80f8d7 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/amd.c @@ -0,0 +1,430 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: AMD ATA controller minidriver + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* GLOBALS ********************************************************************/ + +#define PCI_DEV_AMD_756 0x7409 +#define PCI_DEV_AMD_766 0x7411 +#define PCI_DEV_AMD_768 0x7441 +#define PCI_DEV_AMD_8111 0x7469 +#define PCI_DEV_AMD_CS5536 0x2092 +#define PCI_DEV_AMD_CS5536_2 0x209A + +#define PCI_DEV_NFORCE_IDE 0x01BC +#define PCI_DEV_NFORCE2_IDE 0x0065 +#define PCI_DEV_NFORCE2_IDE_2 0x0085 +#define PCI_DEV_NFORCE3_IDE 0x00D5 +#define PCI_DEV_NFORCE3_IDE_2 0x00E5 +#define PCI_DEV_CK804_IDE 0x0053 +#define PCI_DEV_MCP04_IDE 0x0035 +#define PCI_DEV_MCP51_IDE 0x0265 +#define PCI_DEV_MCP55_IDE 0x036E +#define PCI_DEV_MCP61_IDE 0x03EC +#define PCI_DEV_MCP65_IDE 0x0448 +#define PCI_DEV_MCP67_IDE 0x0560 +#define PCI_DEV_MCP73_IDE 0x056C +#define PCI_DEV_MCP77_IDE 0x0759 + +#define PCI_DEV_NFORCE2_SATA 0x008E +#define PCI_DEV_NFORCE3_SATA 0x00E3 +#define PCI_DEV_NFORCE3_SATA_2 0x00EE +#define PCI_DEV_CK804_SATA 0x0054 +#define PCI_DEV_CK804_SATA_2 0x0055 +#define PCI_DEV_MCP04_SATA 0x0036 +#define PCI_DEV_MCP04_SATA_2 0x003E +#define PCI_DEV_MCP51_SATA 0x0266 +#define PCI_DEV_MCP51_SATA_2 0x0267 +#define PCI_DEV_MCP55_SATA 0x037E +#define PCI_DEV_MCP55_SATA_2 0x037F +#define PCI_DEV_MCP61_SATA 0x03E7 +#define PCI_DEV_MCP61_SATA_2 0x03F6 +#define PCI_DEV_MCP61_SATA_3 0x03F7 +#define PCI_DEV_MCP89_SATA 0x0D85 + +#define PCI_SUBSYSTEM_AMD_SERENADE 0x36C0 + +#define AMD_CONFIG_BASE 0x40 +#define NV_CONFIG_BASE 0x50 + +#define AMD_PCI_CLOCK 30000 + +#define AMD_REG_CONFIG_PREFETCH(IoBase) ((IoBase) + 0x01) +#define AMD_REG_CONFIG_CR(IoBase) ((IoBase) + 0x02) +#define AMD_REG_TIMING_CTRL(IoBase) ((IoBase) + 0x08) +#define AMD_REG_ADDRESS_SETUP(IoBase) ((IoBase) + 0x0C) +#define AMD_REG_UDMA(IoBase, Channel) ((IoBase) + 0x10 + (2 - ((Channel) * 2))) + +#define AMD_CONFIG_PREFETCH(Channel) (0xC0 >> ((Channel) * 2)) +#define AMD_CONFIG_CR(Channel) (0x03 << ((Channel) * 2)) + +#define AMD_UDMA_TIME(x) (x) +#define AMD_UDMA_EN 0xC0 + +#define AMD_UDMA_CTRL(Drive, Value) ((Value) << ((1 - (Drive)) * 8)) + +PCIIDEX_PAGED_DATA +static const struct +{ + USHORT VendorID; + USHORT DeviceID; + USHORT Flags; +#define HW_FLAGS_UDMA4 0x0001 +#define HW_FLAGS_UDMA5 0x0002 +#define HW_FLAGS_CHECK_SYSBOARD 0x0004 +#define HW_FLAGS_NO_PREFETCH 0x0008 +#define HW_FLAGS_SATA 0x0010 +} AmdControllerList[] = +{ + { PCI_VEN_AMD, PCI_DEV_AMD_756, HW_FLAGS_UDMA4 }, + { PCI_VEN_AMD, PCI_DEV_AMD_766, HW_FLAGS_UDMA5 | HW_FLAGS_NO_PREFETCH }, + { PCI_VEN_AMD, PCI_DEV_AMD_768, HW_FLAGS_UDMA5 }, + { PCI_VEN_AMD, PCI_DEV_AMD_8111, HW_FLAGS_CHECK_SYSBOARD }, + { PCI_VEN_AMD, PCI_DEV_AMD_CS5536, HW_FLAGS_UDMA5 }, + { PCI_VEN_AMD, PCI_DEV_AMD_CS5536_2, HW_FLAGS_UDMA5 }, + { PCI_VEN_NVIDIA, PCI_DEV_NFORCE_IDE, HW_FLAGS_UDMA5 }, + { PCI_VEN_NVIDIA, PCI_DEV_NFORCE2_IDE, 0 }, + { PCI_VEN_NVIDIA, PCI_DEV_NFORCE2_IDE_2, 0 }, + { PCI_VEN_NVIDIA, PCI_DEV_NFORCE3_IDE, 0 }, + { PCI_VEN_NVIDIA, PCI_DEV_NFORCE3_IDE_2, 0 }, + { PCI_VEN_NVIDIA, PCI_DEV_CK804_IDE, 0 }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP04_IDE, 0 }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP51_IDE, 0 }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP55_IDE, 0 }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP61_IDE, 0 }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP65_IDE, 0 }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP67_IDE, 0 }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP73_IDE, 0 }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP77_IDE, 0 }, + { PCI_VEN_NVIDIA, PCI_DEV_NFORCE2_SATA, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_NFORCE3_SATA, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_NFORCE3_SATA_2, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_CK804_SATA, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_CK804_SATA_2, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP04_SATA, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP04_SATA_2, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP51_SATA, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP51_SATA_2, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP55_SATA, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP55_SATA_2, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP61_SATA, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP61_SATA_2, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP61_SATA_3, HW_FLAGS_SATA }, + { PCI_VEN_NVIDIA, PCI_DEV_MCP89_SATA, HW_FLAGS_SATA }, +}; + +static const UCHAR AmdUdmaSettings[] = +{ + AMD_UDMA_EN | AMD_UDMA_TIME(2), // 0 + AMD_UDMA_EN | AMD_UDMA_TIME(1), // 1 + AMD_UDMA_EN | AMD_UDMA_TIME(0), // 2 + AMD_UDMA_EN | AMD_UDMA_TIME(4), // 3 + AMD_UDMA_EN | AMD_UDMA_TIME(5), // 4 + AMD_UDMA_EN | AMD_UDMA_TIME(6), // 5 + AMD_UDMA_EN | AMD_UDMA_TIME(7), // 6 +}; + +static const ULONG AmdTimingControlShift[MAX_IDE_CHANNEL][MAX_IDE_DEVICE] = +{ + // M S + { 24, 16 }, // Pri + { 8, 0 }, // Sec +}; +static const ULONG AmdAddressSetupShift[MAX_IDE_CHANNEL][MAX_IDE_DEVICE] = +{ + // M S + { 6, 4 }, // Pri + { 2, 0 }, // Sec +}; +static const ULONG AmdPortTimShift[MAX_IDE_CHANNEL] = +{ + 24, // Pri + 16 // Sec +}; + +PCIIDEX_PAGED_DATA +static const ATA_PCI_ENABLE_BITS AmdEnableBits[MAX_IDE_CHANNEL] = +{ + { AMD_CONFIG_BASE, 0x02, 0x02 }, + { AMD_CONFIG_BASE, 0x01, 0x01 }, +}; + +PCIIDEX_PAGED_DATA +static const ATA_PCI_ENABLE_BITS NvEnableBits[MAX_IDE_CHANNEL] = +{ + { NV_CONFIG_BASE, 0x02, 0x02 }, + { NV_CONFIG_BASE, 0x01, 0x01 }, +}; + +/* FUNCTIONS ******************************************************************/ + +static +ULONG +AmdGetPciConfigIoBase( + _In_ PATA_CONTROLLER Controller) +{ + if (Controller->Pci.VendorID == PCI_VEN_AMD) + return AMD_CONFIG_BASE; + + return NV_CONFIG_BASE; +} + +static +VOID +AmdEnablePostedWriteBuffer( + _In_ PATA_CONTROLLER Controller, + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList, + _In_ ULONG IoBase) +{ + UCHAR ConfigReg; + ULONG i; + + ConfigReg = PciRead8(Controller, AMD_REG_CONFIG_PREFETCH(IoBase)); + if (ChanData->HwFlags & HW_FLAGS_NO_PREFETCH) + { + /* Errata: IDE Read / Write Prefetch Hangs PCI Bus */ + ConfigReg &= ~(AMD_CONFIG_PREFETCH(0) | AMD_CONFIG_PREFETCH(1)); + } + else + { + ConfigReg |= AMD_CONFIG_PREFETCH(Channel); + + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + + if (Device && !Device->IsFixedDisk) + { + ConfigReg &= ~AMD_CONFIG_PREFETCH(Channel); + break; + } + } + } + PciWrite8(Controller, AMD_REG_CONFIG_PREFETCH(IoBase), ConfigReg); +} + +static +VOID +AmdSetTransferMode( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + PCHANNEL_DATA_PATA ChanData = Controller->Channels[Channel]; + const ULONG IoBase = AmdGetPciConfigIoBase(Controller); + ATA_TIMING DeviceTimings[MAX_IDE_DEVICE]; + ULONG i, DriveTimReg, PortTimReg, UdmaTimReg; + + AtaSelectTimings(DeviceList, DeviceTimings, AMD_PCI_CLOCK, SHARED_CMD_TIMINGS); + + DriveTimReg = PciRead32(Controller, AMD_REG_TIMING_CTRL(IoBase)); + PortTimReg = PciRead32(Controller, AMD_REG_ADDRESS_SETUP(IoBase)); + UdmaTimReg = PciRead32(Controller, AMD_REG_UDMA(IoBase, 1)); + + INFO("CH %lu: Config (before)\n" + "DRV %08lX\n" + "PORT %08lX\n" + "UDMA %08lX\n" + "CFG %08lX\n", + Channel, DriveTimReg, PortTimReg, UdmaTimReg, PciRead32(Controller, IoBase)); + + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + PATA_TIMING Timing = &DeviceTimings[i]; + ULONG Value; + + /* UDMA timings */ + if (Device && (Device->DmaMode >= UDMA_MODE(0))) + { + Value = AmdUdmaSettings[Device->DmaMode - UDMA_MODE(0)]; + } + else + { + /* Set "Slow UDMA mode 0" by default */ + if (Controller->Pci.VendorID == PCI_VEN_AMD) + Value = AMD_UDMA_TIME(3); + else + Value = 0; + } + UdmaTimReg &= ~(0xFF << AmdTimingControlShift[Channel][i]); + UdmaTimReg |= Value << AmdTimingControlShift[Channel][i]; + + if (!Device) + continue; + + /* PIO and DMA timings */ + ViaClampTimings(Timing); + + Value = Timing->AddressSetup; + PortTimReg &= ~(0x03 << AmdAddressSetupShift[Channel][i]); + PortTimReg |= Value << AmdAddressSetupShift[Channel][i]; + + Value = (Timing->CmdActive << 4) | Timing->CmdRecovery; + PortTimReg &= ~(0xFF << AmdPortTimShift[Channel]); + PortTimReg |= Value << AmdPortTimShift[Channel]; + + Value = (Timing->DataActive << 4) | Timing->DataRecovery; + DriveTimReg &= ~(0xFF << AmdTimingControlShift[Channel][i]); + DriveTimReg |= Value << AmdTimingControlShift[Channel][i]; + } + + AmdEnablePostedWriteBuffer(Controller, ChanData, Channel, DeviceList, IoBase); + + PciWrite32(Controller, AMD_REG_TIMING_CTRL(IoBase), DriveTimReg); + PciWrite32(Controller, AMD_REG_ADDRESS_SETUP(IoBase), PortTimReg); + PciWrite32(Controller, AMD_REG_UDMA(IoBase, 1), UdmaTimReg); + + INFO("CH %lu: Config (after)\n" + "DRV %08lX\n" + "PORT %08lX\n" + "UDMA %08lX\n" + "CFG %08lX\n", + Channel, DriveTimReg, PortTimReg, UdmaTimReg, PciRead32(Controller, IoBase)); +} + +static +VOID +AmdControllerStart( + _In_ PATA_CONTROLLER Controller) +{ + UCHAR ConfigReg; + + ASSERT(Controller->Pci.VendorID == PCI_VEN_AMD); + + /* Initialize controller before issuing the identify command */ + ConfigReg = PciRead8(Controller, AMD_REG_CONFIG_PREFETCH(AMD_CONFIG_BASE)); + ConfigReg &= ~(AMD_CONFIG_PREFETCH(0) | AMD_CONFIG_PREFETCH(1)); + PciWrite8(Controller, AMD_REG_CONFIG_PREFETCH(AMD_CONFIG_BASE), ConfigReg); +} + +static +BOOLEAN +CODE_SEG("PAGE") +NvHasUdmaCable( + _In_ USHORT UdmaTimReg, + _In_ ULONG Drive) +{ + PAGED_CODE(); + + /* UDMA was disabled by BIOS */ + if ((AMD_UDMA_CTRL(Drive, UdmaTimReg) & AMD_UDMA_EN) != AMD_UDMA_EN) + return FALSE; + + /* Check clock settings, see if UDMA3 or higher mode is active */ + return !!((AMD_UDMA_CTRL(Drive, UdmaTimReg) & 0x7) & AMD_UDMA_TIME(4)); +} + +CODE_SEG("PAGE") +NTSTATUS +AmdGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + NTSTATUS Status; + ULONG i, HwFlags, SupportedMode; + + PAGED_CODE(); + ASSERT(Controller->Pci.VendorID == PCI_VEN_AMD || Controller->Pci.VendorID == PCI_VEN_NVIDIA); + + for (i = 0; i < RTL_NUMBER_OF(AmdControllerList); ++i) + { + HwFlags = AmdControllerList[i].Flags; + + if ((Controller->Pci.VendorID == AmdControllerList[i].VendorID) && + (Controller->Pci.DeviceID == AmdControllerList[i].DeviceID)) + { + break; + } + } + if (i == RTL_NUMBER_OF(AmdControllerList)) + return STATUS_NO_MATCH; + + Status = PciIdeCreateChannelData(Controller, 0); + if (!NT_SUCCESS(Status)) + return Status; + + if (HwFlags & HW_FLAGS_SATA) + { + SupportedMode = SATA_ALL; + } + else + { + SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_ALL; + + if (HwFlags & HW_FLAGS_CHECK_SYSBOARD) + { + if (Controller->Pci.SubSystemID == PCI_SUBSYSTEM_AMD_SERENADE) + SupportedMode &= ~UDMA_MODE6; + } + else + { + if (HwFlags & HW_FLAGS_UDMA5) + SupportedMode &= ~UDMA_MODE6; + else if (HwFlags & HW_FLAGS_UDMA4) + SupportedMode &= ~UDMA_MODES(5, 6); + } + + if (Controller->Pci.VendorID == PCI_VEN_AMD) + { + Controller->ChannelEnableBits = AmdEnableBits; + Controller->Start = AmdControllerStart; + } + else + { + Controller->ChannelEnableBits = NvEnableBits; + } + } + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + ChanData->HwFlags = HwFlags; + ChanData->TransferModeSupported = SupportedMode; + + if (HwFlags & HW_FLAGS_SATA) + { + ChanData->ChanInfo |= CHANNEL_FLAG_NO_SLAVE; + ChanData->SetTransferMode = SataSetTransferMode; + } + else + { + ChanData->SetTransferMode = AmdSetTransferMode; + + /* Check for 80-conductor cable */ + if (Controller->Pci.VendorID == PCI_VEN_AMD) + { + UCHAR ConfigReg = PciRead8(Controller, AMD_REG_CONFIG_CR(AMD_CONFIG_BASE)); + + if (!(ConfigReg & AMD_REG_CONFIG_CR(i))) + { + INFO("CH %lu: BIOS detected 40-conductor cable\n", i); + ChanData->TransferModeSupported &= ~UDMA_80C_ALL; + } + } + else + { + USHORT UdmaTimReg = PciRead16(Controller, AMD_REG_UDMA(NV_CONFIG_BASE, i)); + + if (!NvHasUdmaCable(UdmaTimReg, 0) && !NvHasUdmaCable(UdmaTimReg, 1)) + { + INFO("CH %lu: BIOS hasn't selected mode faster than UDMA 2, " + "assume 40-conductor cable\n", + ChanData->Channel); + ChanData->TransferModeSupported &= ~UDMA_80C_ALL; + } + } + } + } + + return STATUS_SUCCESS; +} diff --git a/drivers/storage/ide/pciidex/chipset/ati.c b/drivers/storage/ide/pciidex/chipset/ati.c new file mode 100644 index 00000000000..d2c8fbaf2b9 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/ati.c @@ -0,0 +1,83 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: BSD-2-Clause (https://spdx.org/licenses/BSD-2-Clause) + * PURPOSE: ATI ATA controller minidriver + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* + * Adapted from the FreeBSD ata-ati driver + * Copyright (c) 1998-2008 Søren Schmidt + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* GLOBALS ********************************************************************/ + +#define PCI_DEV_IXP200_IDE 0x4349 +#define PCI_DEV_IXP300_IDE 0x4369 +#define PCI_DEV_IXP400_IDE 0x4376 +#define PCI_DEV_IXP600_IDE 0x438C +#define PCI_DEV_IXP700_IDE 0x439C + +PCIIDEX_PAGED_DATA +static const ATA_PCI_ENABLE_BITS AtiEnableBits[MAX_IDE_CHANNEL] = +{ + { 0x48, 0x01, 0x00 }, + { 0x48, 0x08, 0x00 }, +}; + +/* FUNCTIONS ******************************************************************/ + +CODE_SEG("PAGE") +NTSTATUS +AtiGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + NTSTATUS Status; + ULONG i; + + PAGED_CODE(); + ASSERT(Controller->Pci.VendorID == PCI_VEN_ATI); + + if (Controller->Pci.DeviceID != PCI_DEV_IXP200_IDE && + Controller->Pci.DeviceID != PCI_DEV_IXP300_IDE && + Controller->Pci.DeviceID != PCI_DEV_IXP400_IDE && + Controller->Pci.DeviceID != PCI_DEV_IXP600_IDE && + Controller->Pci.DeviceID != PCI_DEV_IXP700_IDE) + { + return STATUS_NO_MATCH; + } + + Controller->ChannelEnableBits = AtiEnableBits; + + if (Controller->Pci.DeviceID == PCI_DEV_IXP600_IDE) + Controller->MaxChannels = 1; + + Status = PciIdeCreateChannelData(Controller, 0); + if (!NT_SUCCESS(Status)) + return Status; + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + ChanData->SetTransferMode = SvwSetTransferMode; + ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 5); + + if (Controller->Pci.DeviceID == PCI_DEV_IXP200_IDE) + ChanData->TransferModeSupported &= ~UDMA_MODE5; + + if (!SvwHasUdmaCable(Controller, i)) + { + INFO("CH %lu: BIOS hasn't selected mode faster than UDMA 2, " + "assume 40-conductor cable\n", + ChanData->Channel); + ChanData->TransferModeSupported &= ~UDMA_80C_ALL; + } + } + + return STATUS_SUCCESS; +} diff --git a/drivers/storage/ide/pciidex/chipset/cmd.c b/drivers/storage/ide/pciidex/chipset/cmd.c new file mode 100644 index 00000000000..bfaa6039f9d --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/cmd.c @@ -0,0 +1,577 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: CMD PCI IDE controller minidriver + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* + * The CMD-640 does not support 32-bit PCI configuration space writes. + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* GLOBALS ********************************************************************/ + +#define PCI_DEV_PCI0640 0x0640 +#define PCI_DEV_PCI0643 0x0643 +#define PCI_DEV_PCI0646 0x0646 +#define PCI_DEV_PCI0648 0x0648 +#define PCI_DEV_CMD0649 0x0649 + +#define HW_FLAGS_PRIMARY_ENABLED 0x0001 +#define HW_FLAGS_HAS_UDMA_REG 0x0002 +#define HW_FLAGS_NEED_PIO_FIX 0x0004 +#define HW_FLAGS_NO_PREFETCH 0x0008 + +#define CMD_PCI_CLOCK 30000 + +#define CMD_REG_CFR 0x50 +#define CMD_REG_CNTRL 0x51 +#define CMD_REG_CMDTIM 0x52 +#define CMD_REG_ARTTIM0 0x53 +#define CMD_REG_DRWTIM0 0x54 +#define CMD_REG_ARTTIM1 0x55 +#define CMD_REG_DRWTIM1 0x56 +#define CMD_REG_ARTTIM23 0x57 +#define CMD_REG_DRWTIM2 0x58 +#define CMD_REG_DRWTIM23 0x58 // 640 only +#define CMD_REG_DRWTIM3 0x5B +#define CMD_REG_MRDMODE 0x71 +#define CMD_REG_BMIDECSR 0x79 +#define CMD_REG_UDIDETCR(Channel) (((Channel) == 0) ? 0x73 : 0x7B) + +#define CMD_CFR_INTR(Channel) (((Channel) == 0) ? 0x04 : 0x10) + +#define CMD_CNTRL_CHAN_EN(Channel) (0x04 << (Channel)) + +#define CMD_ARTTIM_ART_MASK 0xC0 + +#define CMD_MRDMODE_READ_MULTIPLE 0x01 +#define CMD_MRDMODE_INTR(Channel) (0x04 << (Channel)) +#define CMD_MRDMODE_INTR_CH0_BLOCK 0x10 +#define CMD_MRDMODE_INTR_CH1_BLOCK 0x20 + +#define CMD_BMIDECSR_CR(Channel) (0x01 << (Channel)) + +#define CMD_UDIDETCR_CLEAR(Drive) (((Drive) == 0) ? 0x35 : 0xCA) +#define CMD_UDIDETCR_EN(Drive) (0x01 << (Drive)) + +typedef struct _CMD_HW_EXTENSION +{ + ATATIM CmdActive[MAX_IDE_CHANNEL * MAX_IDE_DEVICE]; + ATATIM CmdRecovery[MAX_IDE_CHANNEL * MAX_IDE_DEVICE]; +} CMD_HW_EXTENSION, *PCMD_HW_EXTENSION; + +static const UCHAR CmdPrefetchDisable[MAX_IDE_CHANNEL][MAX_IDE_DEVICE] = +{ + // M S + { 0x40, 0x80 }, // Pri + { 0x04, 0x08 } // Sec +}; + +static const ULONG CmdPrefetchRegs[MAX_IDE_CHANNEL] = +{ + // Pri Sec + CMD_REG_CNTRL, CMD_REG_ARTTIM23 +}; + +static const ULONG CmdDrwTimRegs[MAX_IDE_CHANNEL][MAX_IDE_DEVICE] = +{ + // M S + { CMD_REG_DRWTIM0, CMD_REG_DRWTIM1 }, // Pri + { CMD_REG_DRWTIM2, CMD_REG_DRWTIM3 } // Sec +}; + +static const ULONG CmdArtTimRegs[MAX_IDE_CHANNEL][MAX_IDE_DEVICE] = +{ + // M S + { CMD_REG_ARTTIM0, CMD_REG_ARTTIM1 }, // Pri + { CMD_REG_ARTTIM23, CMD_REG_ARTTIM23 } // Sec +}; + +static const UCHAR CmdArtTimings[] = +{ + 0x40, // 0 + 0x40, // 1 + 0x40, // 2 + 0x80, // 3 + 0x00, // 4 + 0xC0 // 5 +}; + +static const UCHAR CmdUdmaTimings[6][MAX_IDE_DEVICE] = +{ + // M S + { 0x31, 0xC2 }, // 0 + { 0x21, 0x82 }, // 1 + { 0x11, 0x42 }, // 2 + { 0x25, 0x8A }, // 3 + { 0x15, 0x4A }, // 4 + { 0x05, 0x0A } // 5 +}; + +/* FUNCTIONS ******************************************************************/ + +static +VOID +CmdFixRecoveryTiming( + _In_ PCHANNEL_DEVICE_CONFIG Device, + _Inout_ PATA_TIMING Timing, + _In_ ATATIM CycleTimeClocks, + _In_ ATATIM CmdActiveClocks, + _In_ ATATIM CmdRecoveryClocks, + _In_ ATATIM DataActiveClocks, + _In_ ATATIM DataRecoveryClocks) +{ + if (((Timing->DataActive + Timing->DataRecovery) > CycleTimeClocks) || // Cycle time not met + (Device->DmaMode == MWDMA_MODE(0))) // Data active time not met + { + if (Timing->CmdActive < CmdActiveClocks) + Timing->CmdActive = CmdActiveClocks; + if (Timing->CmdRecovery < CmdRecoveryClocks) + Timing->CmdRecovery = CmdRecoveryClocks; + + if (Timing->DataActive < DataActiveClocks) + Timing->DataActive = DataActiveClocks; + if (Timing->DataRecovery < DataRecoveryClocks) + Timing->DataRecovery = DataRecoveryClocks; + } + else + { + /* Use recommended values */ + Timing->CmdActive = CmdActiveClocks; + Timing->CmdRecovery = CmdRecoveryClocks; + Timing->DataActive = DataActiveClocks; + Timing->DataRecovery = DataRecoveryClocks; + } +} + +static +VOID +CmdDerateTimings( + _In_ PCHANNEL_DEVICE_CONFIG Device, + _Inout_ PATA_TIMING Timing) +{ + /* Make the timings run slower to address the PCI Bus Hang errata */ + switch (Device->PioMode) + { + case PIO_MODE(0): + { + /* + * 600ns is minimum in PIO mode 0, but program the chip to 570ns. + * 480 + 90 = 570 + */ + CmdFixRecoveryTiming(Device, Timing, 20, 16, 3, 16, 3); + break; + } + case PIO_MODE(1): + { + /* 300 + 90 = 390 */ + CmdFixRecoveryTiming(Device, Timing, 13, 10, 3, 10, 3); + break; + } + case PIO_MODE(2): + { + /* + * Cmd 300 + 90 = 390 + * Data 150 + 90 = 240 + */ + CmdFixRecoveryTiming(Device, Timing, 8, 10, 3, 5, 3); + break; + } + + /* PIO 3 & 4 unaffected by the errata */ + default: + break; + } +} + +static +UCHAR +CmdPackTimings( + _In_ PATA_CONTROLLER Controller, + _In_ ATATIM Active, + _In_ ATATIM Recovery) +{ + if (Controller->Pci.DeviceID == PCI_DEV_PCI0640) + { + if (Active >= 16) + Active = 0; + else if (Active < 2) + Active = 2; + + if (Controller->Pci.RevisionID > 1) + { + /* 640B */ + if (Recovery >= 17) + Recovery = 0; + else if (Recovery > 2) + --Recovery; + else + Recovery = 2; + } + else + { + /* 640A */ + if (Recovery >= 16) + Recovery = 0; + else if (Recovery < 2) + Recovery = 2; + } + } + else + { + if (Active >= 16) + Active = 0; + + if (Recovery >= 16) + Recovery = 0; + else if (Recovery > 1) + --Recovery; + else + Recovery = 15; + } + + return (Active << 4) | Recovery; +} + +static +VOID +CmdSetTransferMode( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + PCHANNEL_DATA_PATA ChanData = Controller->Channels[Channel]; + PCMD_HW_EXTENSION HwExt = Controller->HwExt; + ATA_TIMING DeviceTimings[MAX_IDE_DEVICE]; + ULONG i, TimingFlags; + UCHAR Value; + + /* Some timings are shared between both drives drives on the secondary channel */ + TimingFlags = 0; + if (Channel != 0) + { + TimingFlags |= SHARED_ADDR_TIMINGS; + + if (Controller->Pci.DeviceID == PCI_DEV_PCI0640) + TimingFlags |= SHARED_DATA_TIMINGS; + } + AtaSelectTimings(DeviceList, DeviceTimings, CMD_PCI_CLOCK, TimingFlags); + + /* Enable or disable prefetch mode */ + Value = PciRead8(Controller, CmdPrefetchRegs[Channel]); + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + + if (!Device) + continue; + + if ((ChanData->HwFlags & HW_FLAGS_NO_PREFETCH) || !Device->IsFixedDisk) + Value |= CmdPrefetchDisable[Channel][i]; + else + Value &= ~CmdPrefetchDisable[Channel][i]; + } + PciWrite8(Controller, CmdPrefetchRegs[Channel], Value); + + /* Program timing settings */ + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + PATA_TIMING Timing = &DeviceTimings[i]; + UCHAR UdmaTimReg; + ULONG Register; + + /* UDMA timings */ + if (ChanData->HwFlags & HW_FLAGS_HAS_UDMA_REG) + { + UdmaTimReg = PciRead8(Controller, CMD_REG_UDIDETCR(Channel)); + if (Device && (Device->DmaMode >= UDMA_MODE(0))) + { + UdmaTimReg &= ~CMD_UDIDETCR_CLEAR(i); + UdmaTimReg |= CmdUdmaTimings[Device->DmaMode - UDMA_MODE(0)][i]; + } + else + { + UdmaTimReg &= ~CMD_UDIDETCR_EN(i); + } + PciWrite8(Controller, CMD_REG_UDIDETCR(Channel), UdmaTimReg); + } + + if (!Device) + { + HwExt->CmdActive[i + Channel * MAX_IDE_DEVICE] = 0; + HwExt->CmdRecovery[i + Channel * MAX_IDE_DEVICE] = 0; + continue; + } + + if (ChanData->HwFlags & HW_FLAGS_NEED_PIO_FIX) + CmdDerateTimings(Device, Timing); + + /* 8-bit timings */ + HwExt->CmdActive[i + Channel * MAX_IDE_DEVICE] = Timing->CmdActive; + HwExt->CmdRecovery[i + Channel * MAX_IDE_DEVICE] = Timing->CmdRecovery; + + /* Address setup timing */ + if (Timing->AddressSetup > RTL_NUMBER_OF(CmdArtTimings) - 1) + Timing->AddressSetup = RTL_NUMBER_OF(CmdArtTimings) - 1; + Value = PciRead8(Controller, CmdArtTimRegs[Channel][i]); + Value &= ~CMD_ARTTIM_ART_MASK; + Value |= CmdArtTimings[Timing->AddressSetup]; + PciWrite8(Controller, CmdArtTimRegs[Channel][i], Value); + + /* 16-bit timings */ + Value = CmdPackTimings(Controller, Timing->DataActive, Timing->DataRecovery); + if ((Controller->Pci.DeviceID == PCI_DEV_PCI0640) && (Channel != 0)) + Register = CMD_REG_DRWTIM23; + else + Register = CmdDrwTimRegs[Channel][i]; + PciWrite8(Controller, Register, Value); + } + + /* 8-bit timings are shared between both the primary and secondary channels */ + for (i = 0; i < MAX_IDE_CHANNEL * MAX_IDE_DEVICE; ++i) + { + if (HwExt->CmdActive[i] > DeviceTimings[0].CmdActive) + DeviceTimings[0].CmdActive = HwExt->CmdActive[i]; + + if (HwExt->CmdRecovery[i] > DeviceTimings[0].CmdRecovery) + DeviceTimings[0].CmdRecovery = HwExt->CmdRecovery[i]; + } + if ((DeviceTimings[0].CmdActive != 0) || (DeviceTimings[0].CmdRecovery != 0)) + { + Value = CmdPackTimings(Controller, + DeviceTimings[0].CmdActive, + DeviceTimings[0].CmdRecovery); + PciWrite8(Controller, CMD_REG_CMDTIM, Value); + } +} + +static +IDE_CHANNEL_STATE +CmdChannelEnabledTest( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel) +{ + PCHANNEL_DATA_PATA ChanData = Controller->Channels[Channel]; + UCHAR Control; + + /* Some controllers lack the primary channel enable bit */ + if ((Channel == 0) && (ChanData->HwFlags & HW_FLAGS_PRIMARY_ENABLED)) + return ChannelEnabled; + + Control = PciRead8(Controller, CMD_REG_CNTRL); + if (Control & CMD_CNTRL_CHAN_EN(Channel)) + return ChannelEnabled; + + return ChannelDisabled; +} + +static +BOOLEAN +CmdCheckInterruptMrdMode( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + PATA_CONTROLLER Controller = ChanData->Controller; + UCHAR Control, InterruptMask; + + if (ChanData->Regs.Dma) + Control = ATA_READ(ChanData->Regs.Dma + 1, ChanData, MRES_DMA); + else + Control = PciRead8(Controller, CMD_REG_MRDMODE); + + InterruptMask = CMD_MRDMODE_INTR(ChanData->Channel); + if (!(Control & InterruptMask)) + return FALSE; + + Control &= ~(CMD_MRDMODE_INTR(0) | CMD_MRDMODE_INTR(1)); + Control |= InterruptMask; + /* Make sure we do not clear the write-only bits */ + Control |= CMD_MRDMODE_READ_MULTIPLE; + + /* Clear the interrupt */ + if (ChanData->Regs.Dma) + ATA_WRITE(ChanData->Regs.Dma + 1, Control, ChanData, MRES_DMA); + else + PciWrite8(Controller, CMD_REG_MRDMODE, Control); + + return TRUE; +} + +static +BOOLEAN +CmdCheckInterruptPci( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + PATA_CONTROLLER Controller = ChanData->Controller; + UCHAR Status, InterruptMask; + ULONG Register; + + InterruptMask = CMD_CFR_INTR(ChanData->Channel); + Register = (ChanData->Channel == 0) ? CMD_REG_CFR : CMD_REG_ARTTIM23; + + /* Read CFR or REG_ARTTIM23 to clear the interrupt */ + Status = PciRead8(Controller, Register); + return !!(Status & InterruptMask); +} + +static +VOID +CmdControllerStart( + _In_ PATA_CONTROLLER Controller) +{ + ULONG i; + UCHAR Control; + + /* + * Initialize the controller to compatible PIO timings. + * We dynamically adjust these timings depending on attached devices. + */ + PciWrite8(Controller, CMD_REG_CMDTIM, 0); + + if (Controller->Pci.DeviceID == PCI_DEV_PCI0640) + { + /* + * Errata: Disable the Read-Ahead Mode. + * https://www.mindprod.com/jgloss/eideflaw.html + */ + for (i = 0; i < RTL_NUMBER_OF(CmdPrefetchRegs); ++i) + { + Control = PciRead8(Controller, CmdPrefetchRegs[i]); + Control |= CmdPrefetchDisable[i][0] | CmdPrefetchDisable[i][1]; + PciWrite8(Controller, CmdPrefetchRegs[i], Control); + } + } + else + { + Control = PciRead8(Controller, CMD_REG_MRDMODE); + Control &= ~(CMD_MRDMODE_INTR_CH0_BLOCK | CMD_MRDMODE_INTR_CH1_BLOCK); + Control |= CMD_MRDMODE_READ_MULTIPLE; + PciWrite8(Controller, CMD_REG_MRDMODE, Control); + } +} + +CODE_SEG("PAGE") +NTSTATUS +CmdGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + NTSTATUS Status; + ULONG i, SupportedMode, HwFlags = 0; + PCHANNEL_CHECK_INTERRUPT CheckInterrupt = CmdCheckInterruptPci; + + PAGED_CODE(); + ASSERT(Controller->Pci.VendorID == PCI_VEN_CMD); + + switch (Controller->Pci.DeviceID) + { + case PCI_DEV_PCI0640: + /* + * Errata: The CMD-640 has one set of task file registers per controller + * and thus the two channels cannot be used simultaneously. + * https://www.mindprod.com/jgloss/eideflaw.html + */ + Controller->Flags |= CTRL_FLAG_IS_SIMPLEX; + + SupportedMode = PIO_ALL; + HwFlags |= HW_FLAGS_PRIMARY_ENABLED | HW_FLAGS_NO_PREFETCH; + break; + + case PCI_DEV_PCI0643: + SupportedMode = PIO_ALL | SWDMA_ALL | MWDMA_ALL; + + if (Controller->Pci.RevisionID < 6) + HwFlags |= HW_FLAGS_PRIMARY_ENABLED; + break; + + case PCI_DEV_PCI0646: + SupportedMode = PIO_ALL | SWDMA_ALL | MWDMA_ALL; + HwFlags |= HW_FLAGS_HAS_UDMA_REG; + + if (Controller->Pci.RevisionID < 3) + HwFlags |= HW_FLAGS_PRIMARY_ENABLED; + else + CheckInterrupt = CmdCheckInterruptMrdMode; + + if (Controller->Pci.RevisionID == 5 || Controller->Pci.RevisionID == 6) + { + /* + * Early 646U2 revisions can support UDMA2 only at a PCI bus speed of 33MHz. + * When it runs at 25 MHz or 30 MHZ, the transfer speed must be limited to UDMA1. + */ + SupportedMode |= UDMA_MODES(0, 1); + } + else if (Controller->Pci.RevisionID > 6) + { + SupportedMode |= UDMA_MODES(0, 2); + } + break; + + case PCI_DEV_PCI0648: + SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 4); + /* Prefetch bits have different meaning on this controller */ + HwFlags |= HW_FLAGS_HAS_UDMA_REG | HW_FLAGS_NO_PREFETCH; + CheckInterrupt = CmdCheckInterruptMrdMode; + break; + + case PCI_DEV_CMD0649: + SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 5); + HwFlags |= HW_FLAGS_HAS_UDMA_REG; + + if (Controller->Pci.RevisionID == 2) + HwFlags |= HW_FLAGS_NEED_PIO_FIX; + + CheckInterrupt = CmdCheckInterruptMrdMode; + break; + + default: + return STATUS_NO_MATCH; + } + + Controller->Start = CmdControllerStart; + + Controller->Flags |= CTRL_FLAG_USE_TEST_FUNCTION; + Controller->ChannelEnabledTest = CmdChannelEnabledTest; + + Status = PciIdeCreateChannelData(Controller, 0); + if (!NT_SUCCESS(Status)) + return Status; + + Controller->HwExt = ExAllocatePoolZero(NonPagedPool, sizeof(CMD_HW_EXTENSION), TAG_PCIIDEX); + if (!Controller->HwExt) + return STATUS_INSUFFICIENT_RESOURCES; + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + ChanData->HwFlags = HwFlags; + ChanData->CheckInterrupt = CheckInterrupt; + ChanData->SetTransferMode = CmdSetTransferMode; + ChanData->TransferModeSupported = SupportedMode; + + if (Controller->Pci.DeviceID == PCI_DEV_PCI0640) + { + ChanData->ChanInfo &= ~CHANNEL_FLAG_IO32; + } + else + { + /* Check for 80-conductor cable */ + if (ChanData->TransferModeSupported & UDMA_80C_ALL) + { + UCHAR BmControl = PciRead8(Controller, CMD_REG_BMIDECSR); + if (!(BmControl & CMD_BMIDECSR_CR(i))) + { + INFO("CH %lu: BIOS detected 40-conductor cable\n", ChanData->Channel); + ChanData->TransferModeSupported &= ~UDMA_80C_ALL; + } + } + } + } + + return STATUS_SUCCESS; +} diff --git a/drivers/storage/ide/pciidex/chipset/intel.c b/drivers/storage/ide/pciidex/chipset/intel.c new file mode 100644 index 00000000000..18308176940 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/intel.c @@ -0,0 +1,1048 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Intel ATA controller minidriver + * COPYRIGHT: Copyright 2026 Dmitry Borisov + * + * REFERENCES: For more details, see: + * "Intel ICH0~ICH5 Programmer's Reference Manual (PRM) 298600-004" + */ + +/* + * 0x1230 PIIX - no separate timings + * 0x1234 MPIIX - no separate timings, no PCI I/O resources, bridge device, single-channel + * 0x811A SCH - single-channel, no enable bits + * 0x2652, 0x2653 ICH6 - share the same PCI ID between AHCI and PATA mode + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* GLOBALS ********************************************************************/ + +#define PCI_DEV_PIIX_82371FB 0x1230 +#define PCI_DEV_MPIIX_82371MX 0x1234 + +#define PCI_DEV_PIIX3_82371SB 0x7010 + +#define PCI_DEV_PIIX4_82371AB 0x7111 +#define PCI_DEV_PIIX4E_82372FB 0x7601 +#define PCI_DEV_PIIX4E_82443MX 0x7199 +#define PCI_DEV_PIIX4E_82451NX 0x84CA + +#define PCI_DEV_ICH_82801AA 0x2411 + +#define PCI_DEV_ICH0_82801AB 0x2421 + +#define PCI_DEV_ICH2_82801BAM 0x244A +#define PCI_DEV_ICH2_82801BA 0x244B + +#define PCI_DEV_C_ICH_82801E 0x245B + +#define PCI_DEV_ICH3_M_82801CAM 0x248A +#define PCI_DEV_ICH3_S_82801CA 0x248B + +#define PCI_DEV_ICH4_L_82801DBL 0x24C1 +#define PCI_DEV_ICH4_M_82801DBM 0x24CA +#define PCI_DEV_ICH4_82801DB 0x24CB + +#define PCI_DEV_ICH5_82801EB_SATA 0x24D1 +#define PCI_DEV_ICH5_82801EB_IDE 0x24DB +#define PCI_DEV_ICH5_R_82801ER 0x24DF +#define PCI_DEV_ICH5_6300ESB_IDE 0x25A2 +#define PCI_DEV_ICH5_6300ESB_SATA 0x25A3 +#define PCI_DEV_ICH5_6300ESB_RAID 0x25B0 + +#define PCI_DEV_ICH6_82801FB_SATA 0x2651 +#define PCI_DEV_ICH6_R_82801FB 0x2652 +#define PCI_DEV_ICH6_M_82801FBM 0x2653 +#define PCI_DEV_ICH6_82801FB_IDE 0x266F + +#define PCI_DEV_ESB2_63XXESB 0x2680 + +#define PCI_DEV_ICH7_6321ESB 0x269E +#define PCI_DEV_ICH7_82801GB 0x27C0 +#define PCI_DEV_ICH7_M_82801GBM 0x27C4 +#define PCI_DEV_ICH7_82801G 0x27DF + +#define PCI_DEV_ICH8_82801H 0x2820 +#define PCI_DEV_ICH8_R_82801HR 0x2825 +#define PCI_DEV_ICH8_M_82801HM 0x2828 +#define PCI_DEV_ICH8_M_82801HBM 0x2850 + +#define PCI_DEV_ICH9_R_82801IR_1 0x2920 +#define PCI_DEV_ICH9_R_82801IR_2 0x2921 +#define PCI_DEV_ICH9_82801I 0x2926 +#define PCI_DEV_ICH9_M_82801IBM_1 0x2928 +#define PCI_DEV_ICH9_M_82801IBM_2 0x292D +#define PCI_DEV_ICH9_M_82801IBM_3 0x292E + +#define PCI_DEV_ICH10_82801JD_1 0x3A00 +#define PCI_DEV_ICH10_82801JD_2 0x3A06 +#define PCI_DEV_ICH10_82801JI_1 0x3A20 +#define PCI_DEV_ICH10_82801JI_2 0x3A26 + +#define PCI_DEV_SCH_ATOM_Z5XX 0x811A + +#define PCI_DEV_EP80579 0x5028 + +#define PCI_DEV_PCH_5SERIES_1 0x3B20 +#define PCI_DEV_PCH_5SERIES_2 0x3B21 +#define PCI_DEV_PCH_5SERIES_3 0x3B26 +#define PCI_DEV_PCH_5SERIES_4 0x3B28 +#define PCI_DEV_PCH_5SERIES_5 0x3B2D +#define PCI_DEV_PCH_5SERIES_6 0x3B2E + +#define PCI_DEV_PCH_6SERIES_1 0x1C00 +#define PCI_DEV_PCH_6SERIES_2 0x1C01 +#define PCI_DEV_PCH_6SERIES_3 0x1C08 +#define PCI_DEV_PCH_6SERIES_4 0x1C09 + +#define PCI_DEV_PCH_7SERIES_1 0x1E00 +#define PCI_DEV_PCH_7SERIES_2 0x1E01 +#define PCI_DEV_PCH_7SERIES_3 0x1E08 +#define PCI_DEV_PCH_7SERIES_4 0x1E09 + +#define PCI_DEV_PCH_X79_1 0x1D00 +#define PCI_DEV_PCH_X79_2 0x1D08 + +#define PCI_DEV_PCH_8900_1 0x2326 +#define PCI_DEV_PCH_8900_2 0x23A6 + +#define PCI_DEV_ATOM_C2000_1 0x1F20 +#define PCI_DEV_ATOM_C2000_2 0x1F21 +#define PCI_DEV_ATOM_C2000_3 0x1F30 +#define PCI_DEV_ATOM_C2000_4 0x1F31 + +#define PCI_DEV_PCH_8SERIES_1 0x8C00 +#define PCI_DEV_PCH_8SERIES_2 0x8C01 +#define PCI_DEV_PCH_8SERIES_3 0x8C08 +#define PCI_DEV_PCH_8SERIES_4 0x8C09 +#define PCI_DEV_PCH_8SERIES_5 0x9C00 +#define PCI_DEV_PCH_8SERIES_6 0x9C01 +#define PCI_DEV_PCH_8SERIES_7 0x9C08 +#define PCI_DEV_PCH_8SERIES_8 0x9C09 + +#define PCI_DEV_ATOM_E3800_1 0x0F20 +#define PCI_DEV_ATOM_E3800_2 0x0F21 + +#define PCI_DEV_PCH_X99_1 0x8D00 +#define PCI_DEV_PCH_X99_2 0x8D08 +#define PCI_DEV_PCH_X99_3 0x8D60 +#define PCI_DEV_PCH_X99_4 0x8D68 + +#define PCI_DEV_PCH_9SERIES_1 0x8C80 +#define PCI_DEV_PCH_9SERIES_2 0x8C81 +#define PCI_DEV_PCH_9SERIES_3 0x8C88 +#define PCI_DEV_PCH_9SERIES_4 0x8C89 + +#define PCI_DEV_BRIDGE_450KX 0x84C4 +#define PCI_DEV_BRIDGE_450NX 0x84CB + +#define BRIDGE_450KX_REV_B0 0x04 + +#define BRIDGE_450NX_REV_B0 0x00 +#define BRIDGE_450NX_REV_B1 0x02 +#define BRIDGE_450NX_REV_C0 0x04 + +/* + * PXB configuration register (450NX) + */ +#define PXB_REG_CONFIG 0x40 + +#define PXB_CONFIG_BUF_RESTREAM 0x0040 // Re-streaming Buffer Enable +#define PXB_CONFIG_PCI_BUS_LOCK 0x4000 // PCI Bus Lock Enable + +/* + * IDE timing register (PIIX, MPIIX, PIIX3/4, ICH) + * + * 0x40-0x41 - primary, 0x42-0x43 - secondary. + * 0x6C-0x6D - primary and secondary (MPIIX). + */ +#define PIIX_REG_IDETIM(Channel) (0x40 + ((Channel) * 2)) +#define MPIIX_REG_IDETIM 0x6C + +#define PIIX_IDETIM_TIME(Drive) (0x0001 << (4 * (Drive))) // Fast Timing Bank Enable +#define PIIX_IDETIM_IE(Drive) (0x0002 << (4 * (Drive))) // IORDY Sample Point Enable +#define PIIX_IDETIM_PPE(Drive) (0x0004 << (4 * (Drive))) // Prefetch/Posting Enable +#define PIIX_IDETIM_DTE(Drive) (0x0008 << (4 * (Drive))) // DMA Timing Only (except for MPIIX) + +#define PIIX_IDETIM_RCT_MASK 0x0300 // Recovery Time +#define PIIX_IDETIM_RSV_MASK 0x0C00 // Reserved +#define PIIX_IDETIM_ISP_MASK 0x3000 // IORDY Sample Point +#define PIIX_IDETIM_SITRE 0x4000 // Slave IDE Timing Register Enable (PIIX3/4, ICH) +#define PIIX_IDETIM_SECONDARY 0x4000 // IDE Decode Enable for secondary channel (MPIIX only) +#define PIIX_IDETIM_IDE 0x8000 // IDE Decode Enable + +#define PIIX_IDETIM_RCT(Value) ((Value) << 8) // Recovery Time +#define PIIX_IDETIM_ISP(Value) ((Value) << 12) // IORDY Sample Point +#define PIIX_IDETIM_SETTINGS(Drive, Value) ((Value) << (4 * (Drive))) // TIME, IE, PPE, DTE + +/* + * Slave IDE timing register (PIIX3/4, ICH) + */ +#define PIIX_REG_SIDETIM 0x44 + +#define PIIX_SIDETIM_ISPRCT_MASK(Channel) (0x0F << ((Channel) * 4)) +#define PIIX_SIDETIM_RCT(Channel, Value) ((Value) << ((Channel) * 4)) // Recovery Time +#define PIIX_SIDETIM_ISP(Channel, Value) ((Value) << (((Channel) * 4) + 2)) // IORDY Sample Point + +/* + * Ultra DMA control register (PIIX4, ICH) + */ +#define PIIX_REG_UDMACTL 0x48 + +#define PIIX_UDMACTL_EN_MASK(Channel) (0x03 << ((Channel) * 2)) // Ultra DMA Mode Enable + +/* Ultra DMA Mode Enable */ +#define PIIX_UDMACTL_EN(Channel, Drive) (0x01 << ((Channel) * 2 + (Drive))) + +/* + * Ultra DMA timing register (PIIX4, ICH) + * + * 0x4A - primary, 0x4B - secondary. + */ +#define PIIX_REG_UDMATIM(Channel) (0x4A + (Channel)) + +#define PIIX_UDMATIM_CT_MASK 0x33 // Cycle Time and Ready to Pause time + +/* Cycle Time and Ready to Pause time */ +#define PIIX_UDMATIM_CT(Drive, Value) ((Value) << ((Drive) * 4)) + +/* + * IDE config register (ICH) + */ +#define PIIX_REG_CONFIG 0x54 + +#define PIIX_CONFIG_KEEP_MASK 0x0FF0 // Save the reserved, cable, and ping-pong bits + +/* Cable Reporting for drives 0-1 */ +#define PIIX_CONFIG_CR(Channel) (0x0030 << ((Channel) * 2)) +/* 66 MHz Clock */ +#define PIIX_CONFIG_CLOCK_UDMA66(Channel, Drive) (0x0001 << (((Channel) * 2) + Drive)) +/* 100 MHz Clock */ +#define PIIX_CONFIG_CLOCK_UDMA100(Channel, Drive) (0x1000 << (((Channel) * 2) + Drive)) +/* PIO Ping-Pong enable */ +#define PIIX_CONFIG_WR_PING_PONG 0x0400 + +/* + * Device 0/1 Timing Register (SCH) + */ +#define SCH_REG_DTIM(Drive) (0x80 + (Drive) * 4) + +#define SCH_DTIM_PM_MASK 0x00000007 // PIO Mode +#define SCH_DTIM_MDM_MASK 0x00000300 // Mutli-word DMA Mode +#define SCH_DTIM_UDM_MASK 0x00070000 // Ultra DMA Mode +#define SCH_DTIM_PPE 0x40000000 // Prefetch/Post Enable +#define SCH_DTIM_USD 0x80000000 // Use Synchronous DMA + +#define SCH_DTIM_PM(Mode) (Mode) +#define SCH_DTIM_MDM(Mode) ((Mode) << 8) +#define SCH_DTIM_UDM(Mode) ((Mode) << 16) + +#define HW_FLAGS_TYPE_MASK 0x000F +#define HW_FLAGS_DISABLE_DMA 0x0010 +#define HW_FLAGS_HAS_CFG_REG 0x0080 +#define HW_FLAGS_HAS_UDMA_REG 0x8000 + +typedef struct _INTEL_CONTROLLER_INFO +{ + USHORT DeviceID; + USHORT Type; +#define TYPE_MPIIX 0 +#define TYPE_PIIX 1 +#define TYPE_PIIX3 2 +#define TYPE_PIIX4 (3 | HW_FLAGS_HAS_UDMA_REG) +#define TYPE_ICH (4 | HW_FLAGS_HAS_UDMA_REG | HW_FLAGS_HAS_CFG_REG) +#define TYPE_SATA 5 +#define TYPE_SCH 6 +} INTEL_CONTROLLER_INFO, *PINTEL_CONTROLLER_INFO; + +typedef struct _INTEL_HW_EXTENSION +{ + ULONG Register; + USHORT CurrentTiming; + struct + { + USHORT Timing[2]; + } Device[MAX_IDE_DEVICE]; +} INTEL_HW_EXTENSION, *PINTEL_HW_EXTENSION; + +PCIIDEX_PAGED_DATA +static const INTEL_CONTROLLER_INFO IntelControllerList[] = +{ + { PCI_DEV_PIIX_82371FB, TYPE_PIIX, }, + { PCI_DEV_MPIIX_82371MX, TYPE_MPIIX, }, + { PCI_DEV_PIIX3_82371SB, TYPE_PIIX3, }, + { PCI_DEV_PIIX4_82371AB, TYPE_PIIX4, }, + { PCI_DEV_PIIX4E_82372FB, TYPE_PIIX4, }, + { PCI_DEV_PIIX4E_82443MX, TYPE_PIIX4, }, + { PCI_DEV_PIIX4E_82451NX, TYPE_PIIX4, }, + { PCI_DEV_ICH_82801AA, TYPE_ICH, }, + { PCI_DEV_ICH0_82801AB, TYPE_ICH, }, + { PCI_DEV_ICH2_82801BAM, TYPE_ICH, }, + { PCI_DEV_ICH2_82801BA, TYPE_ICH, }, + { PCI_DEV_C_ICH_82801E, TYPE_ICH, }, + { PCI_DEV_ICH3_M_82801CAM, TYPE_ICH, }, + { PCI_DEV_ICH3_S_82801CA, TYPE_ICH, }, + { PCI_DEV_ICH4_L_82801DBL, TYPE_ICH, }, + { PCI_DEV_ICH4_M_82801DBM, TYPE_ICH, }, + { PCI_DEV_ICH4_82801DB, TYPE_ICH, }, + { PCI_DEV_ICH5_82801EB_SATA, TYPE_SATA, }, + { PCI_DEV_ICH5_82801EB_IDE, TYPE_ICH, }, + { PCI_DEV_ICH5_R_82801ER, TYPE_SATA, }, + { PCI_DEV_ICH5_6300ESB_IDE, TYPE_ICH, }, + { PCI_DEV_ICH5_6300ESB_SATA, TYPE_SATA, }, + { PCI_DEV_ICH5_6300ESB_RAID, TYPE_SATA, }, + { PCI_DEV_ICH6_82801FB_SATA, TYPE_SATA, }, + { PCI_DEV_ICH6_R_82801FB, TYPE_SATA, }, + { PCI_DEV_ICH6_M_82801FBM, TYPE_SATA, }, + { PCI_DEV_ICH6_82801FB_IDE, TYPE_ICH, }, + { PCI_DEV_ESB2_63XXESB, TYPE_SATA, }, + { PCI_DEV_ICH7_6321ESB, TYPE_ICH, }, + { PCI_DEV_ICH7_82801GB, TYPE_SATA, }, + { PCI_DEV_ICH7_M_82801GBM, TYPE_SATA, }, + { PCI_DEV_ICH7_82801G, TYPE_ICH, }, + { PCI_DEV_ICH8_82801H, TYPE_SATA, }, + { PCI_DEV_ICH8_R_82801HR, TYPE_SATA, }, + { PCI_DEV_ICH8_M_82801HM, TYPE_SATA, }, + { PCI_DEV_ICH8_M_82801HBM, TYPE_ICH, }, + { PCI_DEV_ICH9_R_82801IR_1, TYPE_SATA, }, + { PCI_DEV_ICH9_R_82801IR_2, TYPE_SATA, }, + { PCI_DEV_ICH9_82801I, TYPE_SATA, }, + { PCI_DEV_ICH9_M_82801IBM_1, TYPE_SATA, }, + { PCI_DEV_ICH9_M_82801IBM_2, TYPE_SATA, }, + { PCI_DEV_ICH9_M_82801IBM_3, TYPE_SATA, }, + { PCI_DEV_ICH10_82801JD_1, TYPE_SATA, }, + { PCI_DEV_ICH10_82801JD_2, TYPE_SATA, }, + { PCI_DEV_ICH10_82801JI_1, TYPE_SATA, }, + { PCI_DEV_ICH10_82801JI_2, TYPE_SATA, }, + { PCI_DEV_SCH_ATOM_Z5XX, TYPE_SCH, }, + { PCI_DEV_EP80579, TYPE_SATA, }, + { PCI_DEV_PCH_5SERIES_1, TYPE_SATA, }, + { PCI_DEV_PCH_5SERIES_2, TYPE_SATA, }, + { PCI_DEV_PCH_5SERIES_3, TYPE_SATA, }, + { PCI_DEV_PCH_5SERIES_4, TYPE_SATA, }, + { PCI_DEV_PCH_5SERIES_5, TYPE_SATA, }, + { PCI_DEV_PCH_5SERIES_6, TYPE_SATA, }, + { PCI_DEV_PCH_6SERIES_1, TYPE_SATA, }, + { PCI_DEV_PCH_6SERIES_2, TYPE_SATA, }, + { PCI_DEV_PCH_6SERIES_3, TYPE_SATA, }, + { PCI_DEV_PCH_6SERIES_4, TYPE_SATA, }, + { PCI_DEV_PCH_7SERIES_1, TYPE_SATA, }, + { PCI_DEV_PCH_7SERIES_2, TYPE_SATA, }, + { PCI_DEV_PCH_7SERIES_3, TYPE_SATA, }, + { PCI_DEV_PCH_7SERIES_4, TYPE_SATA, }, + { PCI_DEV_PCH_X79_1, TYPE_SATA, }, + { PCI_DEV_PCH_X79_2, TYPE_SATA, }, + { PCI_DEV_PCH_8900_1, TYPE_SATA, }, + { PCI_DEV_PCH_8900_2, TYPE_SATA, }, + { PCI_DEV_ATOM_C2000_1, TYPE_SATA, }, + { PCI_DEV_ATOM_C2000_2, TYPE_SATA, }, + { PCI_DEV_ATOM_C2000_3, TYPE_SATA, }, + { PCI_DEV_ATOM_C2000_4, TYPE_SATA, }, + { PCI_DEV_PCH_8SERIES_1, TYPE_SATA, }, + { PCI_DEV_PCH_8SERIES_2, TYPE_SATA, }, + { PCI_DEV_PCH_8SERIES_3, TYPE_SATA, }, + { PCI_DEV_PCH_8SERIES_4, TYPE_SATA, }, + { PCI_DEV_PCH_8SERIES_5, TYPE_SATA, }, + { PCI_DEV_PCH_8SERIES_6, TYPE_SATA, }, + { PCI_DEV_PCH_8SERIES_7, TYPE_SATA, }, + { PCI_DEV_PCH_8SERIES_8, TYPE_SATA, }, + { PCI_DEV_ATOM_E3800_1, TYPE_SATA, }, + { PCI_DEV_ATOM_E3800_2, TYPE_SATA, }, + { PCI_DEV_PCH_X99_1, TYPE_SATA, }, + { PCI_DEV_PCH_X99_2, TYPE_SATA, }, + { PCI_DEV_PCH_X99_3, TYPE_SATA, }, + { PCI_DEV_PCH_X99_4, TYPE_SATA, }, + { PCI_DEV_PCH_9SERIES_1, TYPE_SATA, }, + { PCI_DEV_PCH_9SERIES_2, TYPE_SATA, }, + { PCI_DEV_PCH_9SERIES_3, TYPE_SATA, }, + { PCI_DEV_PCH_9SERIES_4, TYPE_SATA, }, +}; + +PCIIDEX_PAGED_DATA +static const ATA_PCI_ENABLE_BITS IntelPiixEnableBits[MAX_IDE_CHANNEL] = +{ + { 0x41, 0x80, 0x80 }, + { 0x43, 0x80, 0x80 }, +}; + +PCIIDEX_PAGED_DATA +static const ATA_PCI_ENABLE_BITS IntelMpiixEnableBits[MAX_IDE_CHANNEL] = +{ + { 0x6D, 0xC0, 0x80 }, + { 0x6D, 0xC0, 0xC0 }, +}; + +static const UCHAR IntelClockSettings[5][2] = +{ + // ISP, RCT + { 0, 0 }, // Mode 0 ISP = 5 RCT = 4 NOTE: Some old chips do ISP 6 and RCT 14 = 600 ns + { 0, 0 }, // Mode 1 Ditto + { 1, 0 }, // Mode 2 ISP = 4 RCT = 4 + { 2, 1 }, // Mode 3 ISP = 3 RCT = 3 + { 2, 3 }, // Mode 4 ISP = 3 RCT = 1 +}; + +/** @sa IntelClockSettings */ +static const ULONG IntelTimingModeToCycleTime[5] = +{ + 900, // Mode 0 NOTE: Some old chips do ISP 6 and RCT 14 = 600 ns + 900, // Mode 1 Ditto + 240, // Mode 2 + 180, // Mode 3 + 120, // Mode 4 +}; + +static const UCHAR IntelModeSettings[] = +{ + 0, // Mode 0 + 0, // Mode 1 + PIIX_IDETIM_TIME(0), // Mode 2 + PIIX_IDETIM_TIME(0) | PIIX_IDETIM_IE(0), // Mode 3 + PIIX_IDETIM_TIME(0) | PIIX_IDETIM_IE(0), // Mode 4 +}; + +static const UCHAR IntelUdmaSettings[] = +{ + 0, // UDMA 0 + 1, // UDMA 1 + 2, // UDMA 2 + 1, // UDMA 3 + 2, // UDMA 4 + 1 // UDMA 5 +}; + +static const ULONG IntelDmaModeToTimingMode[] = { + PIO_MODE(2), // SWDMA_MODE(2) + PIO_MODE(0), // Unused, MWDMA0 is not supported by the chip + PIO_MODE(3), // MWDMA_MODE(1) + PIO_MODE(4) // MWDMA_MODE(2) +}; + +/* FUNCTIONS ******************************************************************/ + +/* + * Make sure that the selected PIO and DMA speeds + * match the cycle time of IntelClockSettings[]. + */ +static +VOID +IntelPiixChooseDeviceSpeed( + _In_ ULONG Channel, + _In_ PCHANNEL_DEVICE_CONFIG Device) +{ + ULONG Mode; + + /* PIO speed */ + for (Mode = Device->PioMode; Mode > PIO_MODE(0); Mode--) + { + if (!(Device->SupportedModes & (1 << Mode))) + continue; + + if (IntelTimingModeToCycleTime[Mode] >= Device->MinPioCycleTime) + break; + } + if (Mode != Device->PioMode) + INFO("CH %lu: Downgrade PIO speed from %lu to %lu\n", Channel, Device->PioMode, Mode); + Device->PioMode = Mode; + + /* UDMA works independently of any PIO mode */ + if (Device->DmaMode == PIO_MODE(0) || Device->DmaMode >= UDMA_MODE(0)) + return; + + /* DMA speed */ + for (Mode = Device->DmaMode; Mode > PIO_MODE(0); Mode--) + { + ULONG MinimumCycleTime, TimingMode; + + if (!(Device->SupportedModes & ~PIO_ALL & (1 << Mode))) + continue; + + ASSERT((Mode == SWDMA_MODE(2)) || (Mode == MWDMA_MODE(1)) || (Mode == MWDMA_MODE(2))); + + if (Device->DmaMode >= MWDMA_MODE(0)) + MinimumCycleTime = Device->MinMwDmaCycleTime; + else + MinimumCycleTime = Device->MinSwDmaCycleTime; + + TimingMode = IntelDmaModeToTimingMode[Mode - SWDMA_MODE(2)]; + + if (IntelTimingModeToCycleTime[TimingMode] >= MinimumCycleTime) + break; + } + if (Mode != Device->DmaMode) + { + if (Mode == PIO_MODE(0)) + WARN("CH %lu: Too slow device '%s', disabling DMA\n", Channel, Device->FriendlyName); + else + INFO("CH %lu: Downgrade DMA speed from %lu to %lu\n", Channel, Device->DmaMode, Mode); + } + Device->DmaMode = Mode; +} + +static +VOID +IntelPiixSetTransferMode( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + PCHANNEL_DATA_PATA ChanData = Controller->Channels[Channel]; + USHORT IdeTimReg, IdeConfigReg; + UCHAR IdeSlaveTimReg, IdeUdmaCtrlReg, IdeUdmaTimReg; + ULONG i; + + IdeUdmaCtrlReg = 0; + IdeUdmaTimReg = 0; + IdeConfigReg = 0; + + IdeTimReg = PciRead16(Controller, PIIX_REG_IDETIM(Channel)); + IdeSlaveTimReg = PciRead8(Controller, PIIX_REG_SIDETIM); + if (ChanData->HwFlags & HW_FLAGS_HAS_UDMA_REG) + { + IdeUdmaCtrlReg = PciRead8(Controller, PIIX_REG_UDMACTL); + IdeUdmaTimReg = PciRead8(Controller, PIIX_REG_UDMATIM(Channel)); + if (ChanData->HwFlags & HW_FLAGS_HAS_CFG_REG) + { + IdeConfigReg = PciRead16(Controller, PIIX_REG_CONFIG); + } + } + + INFO("CH %lu: Config (before)\n" + "IDETIM %04X\n" + "SIDETIM %02X\n" + "UDMACTL %02X\n" + "UDMATIM %02X\n" + "CONFIG %04X\n", + Channel, IdeTimReg, IdeSlaveTimReg, IdeUdmaCtrlReg, IdeUdmaTimReg, IdeConfigReg); + + /* Clear the current mode configuration */ + IdeTimReg &= PIIX_IDETIM_IDE | PIIX_IDETIM_RSV_MASK; + IdeSlaveTimReg &= ~PIIX_SIDETIM_ISPRCT_MASK(Channel); + IdeUdmaCtrlReg &= ~PIIX_UDMACTL_EN_MASK(Channel); + IdeUdmaTimReg &= ~PIIX_UDMATIM_CT_MASK; + IdeConfigReg &= PIIX_CONFIG_KEEP_MASK; + + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + ULONG TimingMode; + + if (!Device) + continue; + + IntelPiixChooseDeviceSpeed(Channel, Device); + + TimingMode = Device->PioMode; + + /* UDMA timings */ + if (Device->DmaMode >= UDMA_MODE(0)) + { + /* UDMA works independently of any PIO mode */ + IdeUdmaTimReg |= PIIX_UDMATIM_CT(i, IntelUdmaSettings[Device->DmaMode - UDMA_MODE(0)]); + IdeUdmaCtrlReg |= PIIX_UDMACTL_EN(Channel, i); + + if (Device->DmaMode == UDMA_MODE(5)) + IdeConfigReg |= PIIX_CONFIG_CLOCK_UDMA100(Channel, i); + else if (Device->DmaMode > UDMA_MODE(2)) + IdeConfigReg |= PIIX_CONFIG_CLOCK_UDMA66(Channel, i); + } + /* DMA timings */ + else if (Device->DmaMode != PIO_MODE(0)) + { + ASSERT((Device->DmaMode == SWDMA_MODE(2)) || + (Device->DmaMode == MWDMA_MODE(1)) || + (Device->DmaMode == MWDMA_MODE(2))); + + /* Find the fastest mode while satisfying PIO timings */ + TimingMode = IntelDmaModeToTimingMode[Device->DmaMode - SWDMA_MODE(2)]; + if (TimingMode < Device->PioMode) + { + /* + * No common mode, we have to slow down the device's PIO speed + * by forcing the compatible PIO timings for PIO transfers. + */ + IdeTimReg |= PIIX_IDETIM_DTE(i); + } + } + + /* DMA and PIO timings */ + if (i == 0) + { + IdeTimReg |= PIIX_IDETIM_ISP(IntelClockSettings[TimingMode][0]); + IdeTimReg |= PIIX_IDETIM_RCT(IntelClockSettings[TimingMode][1]); + } + else + { + IdeSlaveTimReg |= PIIX_SIDETIM_ISP(Channel, IntelClockSettings[TimingMode][0]); + IdeSlaveTimReg |= PIIX_SIDETIM_RCT(Channel, IntelClockSettings[TimingMode][1]); + + /* Enable effect of the PIIX_REG_SIDETIM register */ + IdeTimReg |= PIIX_IDETIM_SITRE; + } + + IdeTimReg |= PIIX_IDETIM_SETTINGS(i, IntelModeSettings[TimingMode]); + + if (((TimingMode == PIO_MODE(2)) && Device->IoReadySupported)) + IdeTimReg |= PIIX_IDETIM_IE(i); + + if (Device->IsFixedDisk) + IdeTimReg |= PIIX_IDETIM_PPE(i); + } + + PciWrite8(Controller, PIIX_REG_SIDETIM, IdeSlaveTimReg); + PciWrite16(Controller, PIIX_REG_IDETIM(Channel), IdeTimReg); + if (ChanData->HwFlags & HW_FLAGS_HAS_UDMA_REG) + { + PciWrite8(Controller, PIIX_REG_UDMACTL, IdeUdmaCtrlReg); + PciWrite8(Controller, PIIX_REG_UDMATIM(Channel), IdeUdmaTimReg); + if (ChanData->HwFlags & HW_FLAGS_HAS_CFG_REG) + { + /* Enable this feature for performance enhancement */ + IdeConfigReg |= PIIX_CONFIG_WR_PING_PONG; + + PciWrite16(Controller, PIIX_REG_CONFIG, IdeConfigReg); + } + } + + INFO("CH %lu: Config (after)\n" + "IDETIM %04X\n" + "SIDETIM %02X\n" + "UDMACTL %02X\n" + "UDMATIM %02X\n" + "CONFIG %04X\n", + Channel, IdeTimReg, IdeSlaveTimReg, IdeUdmaCtrlReg, IdeUdmaTimReg, IdeConfigReg); +} + +static +VOID +IntelSchSetTransferMode( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + ULONG i; + + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + ULONG DeviceTimingReg; + + if (!Device) + continue; + + DeviceTimingReg = PciRead32(Controller, SCH_REG_DTIM(i)); + DeviceTimingReg &= ~(SCH_DTIM_PM_MASK | + SCH_DTIM_MDM_MASK | + SCH_DTIM_UDM_MASK | + SCH_DTIM_PPE | + SCH_DTIM_USD); + + if (Device->IsFixedDisk) + DeviceTimingReg |= SCH_DTIM_PPE; + + /* DMA timings */ + if (Device->DmaMode >= UDMA_MODE(0)) + DeviceTimingReg |= SCH_DTIM_USD | SCH_DTIM_UDM(Device->DmaMode - UDMA_MODE(0)); + else if (Device->DmaMode >= MWDMA_MODE(0)) + DeviceTimingReg |= SCH_DTIM_MDM(Device->DmaMode - MWDMA_MODE(0)); + + /* PIO timings */ + DeviceTimingReg |= SCH_DTIM_PM(Device->PioMode); + + PciWrite32(Controller, SCH_REG_DTIM(i), DeviceTimingReg); + } +} + +static +VOID +IntelPiixLegacyPrepareIo( + _In_ PVOID ChannelContext, + _In_ PATA_DEVICE_REQUEST Request) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + PINTEL_HW_EXTENSION HwExt = (PVOID)ChanData->HwExt; + ULONG Index = (Request->Flags & REQUEST_FLAG_PROGRAM_DMA) ? 1 : 0; + USHORT NewTimingReg = HwExt->Device[DEV_NUMBER(Request->Device)].Timing[Index]; + + /* Set the proper device timings */ + if (HwExt->CurrentTiming != NewTimingReg) + { + HwExt->CurrentTiming = NewTimingReg; + + PciWrite16(ChanData->Controller, HwExt->Register, NewTimingReg); + TRACE("CH %lu: IDETIM %04X\n", ChanData->Channel, NewTimingReg); + } + + PataPrepareIo(ChanData, Request); +} + +static +USHORT +IntelPiixLegacyComputeIdeTiming( + _In_ PCHANNEL_DEVICE_CONFIG Device, + _In_ ULONG Number, + _In_ ULONG Mode) +{ + USHORT IdeTimReg; + ULONG TimingMode; + + if (Mode > PIO_MODE(4)) + { + ASSERT((Mode == SWDMA_MODE(2)) || (Mode == MWDMA_MODE(1)) || (Mode == MWDMA_MODE(2))); + + TimingMode = IntelDmaModeToTimingMode[Mode - SWDMA_MODE(2)]; + } + else + { + TimingMode = Mode; + } + + IdeTimReg = PIIX_IDETIM_ISP(IntelClockSettings[TimingMode][0]); + IdeTimReg |= PIIX_IDETIM_RCT(IntelClockSettings[TimingMode][1]); + IdeTimReg |= PIIX_IDETIM_SETTINGS(Number, IntelModeSettings[TimingMode]); + + if (Device->IsFixedDisk) + IdeTimReg |= PIIX_IDETIM_PPE(Number); + + if (((TimingMode == PIO_MODE(2)) && Device->IoReadySupported)) + IdeTimReg |= PIIX_IDETIM_IE(Number); + + return IdeTimReg; +} + +/* + * Unfortunately, the first PIIX chip cannot specify separate device timings (>PIO1) + * for both of the drives. The PIIX_IDETIM_SITRE bit is reserved here. + * This problem can be solved in two ways: + * + * 1) If we have a common timing mode for all devices use it, otherwise take the lower mode. + * 2) Snoop an ATA command by software and run with the proper timings for that device. + * + * The code below deals with the second solution. + */ +static +VOID +IntelPiixLegacySetTransferMode( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + PCHANNEL_DATA_PATA ChanData = Controller->Channels[Channel]; + PINTEL_HW_EXTENSION HwExt = (PVOID)ChanData->HwExt; + USHORT IdeTimReg; + ULONG i; + + /* Clear the current mode configuration */ + IdeTimReg = PciRead16(Controller, HwExt->Register); + IdeTimReg &= PIIX_IDETIM_IDE | PIIX_IDETIM_SITRE | PIIX_IDETIM_RSV_MASK; + PciWrite16(Controller, HwExt->Register, IdeTimReg); + + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + + if (!Device) + continue; + + IntelPiixChooseDeviceSpeed(Channel, Device); + + /* Compute the timing bits for this device */ + HwExt->Device[i].Timing[0] = IntelPiixLegacyComputeIdeTiming(Device, i, Device->PioMode); + HwExt->Device[i].Timing[1] = IntelPiixLegacyComputeIdeTiming(Device, i, Device->DmaMode); + + INFO("CH %lu: Drive #%lu PIO:%04X DMA:04X\n", + Channel, i, HwExt->Device[i].Timing[0], HwExt->Device[i].Timing[1]); + } + + /* Force reload of timings */ + HwExt->CurrentTiming = 0; +} + +/* + * Intel 450KX/GX PCIset Specification Update 243109-015 + * Intel 450NX PCIset Specification Update 243848-009 + */ +static +CODE_SEG("PAGE") +BOOLEAN +IntelPciBridgeErrataMatch( + _In_ PVOID Context, + _In_ ULONG BusNumber, + _In_ PCI_SLOT_NUMBER PciSlot, + _In_ PPCI_COMMON_HEADER PciConfig) +{ + UNREFERENCED_PARAMETER(Context); + + PAGED_CODE(); + + if (PciConfig->VendorID != PCI_VEN_INTEL) + return FALSE; + + /* 450KX errata: 0-Byte Length Write failure */ + if ((PciConfig->DeviceID == PCI_DEV_BRIDGE_450KX) && + (PciConfig->RevisionID < BRIDGE_450KX_REV_B0)) + { + WARN("Enabling workaround for the 450KX #2\n"); + return TRUE; + } + + /* 450NX errata */ + if (PciConfig->DeviceID == PCI_DEV_BRIDGE_450NX) + { + USHORT ConfigReg; + + /* PXB livelock */ + if (PciConfig->RevisionID == BRIDGE_450NX_REV_B0) + { + WARN("Enabling workaround for the 450NX #19\n"); + return TRUE; + } + + HalGetBusDataByOffset(PCIConfiguration, + BusNumber, + PciSlot.u.AsULONG, + &ConfigReg, + PXB_REG_CONFIG, + sizeof(ConfigReg)); + INFO("PXB CFG: %04X\n", ConfigReg); + + /* PXB arbiter deadlock */ + if ((PciConfig->RevisionID != BRIDGE_450NX_REV_B1) && + (ConfigReg & PXB_CONFIG_PCI_BUS_LOCK)) + { + WARN("Enabling workaround for the 450NX #20\n"); + return TRUE; + } + + /* PCI data corruption */ + if ((PciConfig->RevisionID == BRIDGE_450NX_REV_C0) && + (ConfigReg & PXB_CONFIG_BUF_RESTREAM)) + { + WARN("Enabling workaround for the 450NX #25\n"); + return TRUE; + } + } + + return FALSE; +} + +static +CODE_SEG("PAGE") +VOID +IntelInitChannel( + _In_ PATA_CONTROLLER Controller, + _In_ const INTEL_CONTROLLER_INFO* ControllerInfo, + _In_ PCHANNEL_DATA_PATA ChanData) +{ + ULONG SupportedMode; + + PAGED_CODE(); + + switch (ControllerInfo->Type) + { + case TYPE_MPIIX: + case TYPE_PIIX: + { + PINTEL_HW_EXTENSION HwExt = (PVOID)ChanData->HwExt; + + if (ControllerInfo->Type == TYPE_MPIIX) + HwExt->Register = MPIIX_REG_IDETIM; + else + HwExt->Register = PIIX_REG_IDETIM(ChanData->Channel); + + ChanData->SetTransferMode = IntelPiixLegacySetTransferMode; + ChanData->PrepareIo = IntelPiixLegacyPrepareIo; + break; + } + + case TYPE_PIIX3: + case TYPE_PIIX4: + case TYPE_ICH: + ChanData->SetTransferMode = IntelPiixSetTransferMode; + break; + + case TYPE_SCH: + ChanData->SetTransferMode = IntelSchSetTransferMode; + break; + + default: + ChanData->SetTransferMode = SataSetTransferMode; + break; + } + + switch (ControllerInfo->Type) + { + case TYPE_MPIIX: + SupportedMode = PIO_ALL; + break; + + case TYPE_PIIX: + case TYPE_PIIX3: + SupportedMode = PIO_ALL | SWDMA_MODE2 | MWDMA_MODES(1, 2); + break; + + case TYPE_PIIX4: + SupportedMode = PIO_ALL | SWDMA_MODE2 | MWDMA_MODES(1, 2) | UDMA_MODES(0, 2); + break; + + case TYPE_ICH: + { + /* Errata: MW DMA Mode-1 Tdh (unable to drive MWDMA1 and SWDMA2 timings) */ + SupportedMode = PIO_ALL | MWDMA_MODE2 | UDMA_MODES(0, 5); + + if (Controller->Pci.DeviceID == PCI_DEV_ICH_82801AA || + Controller->Pci.DeviceID == PCI_DEV_ICH0_82801AB) + { + /* Errata: IDE Bus Master Concurrency */ + if (Controller->Pci.RevisionID == 0) + { + SupportedMode = PIO_ALL; + } + else + { + /* ICH is UDMA4 */ + SupportedMode &= ~UDMA_MODE5; + + /* ICH0 is UDMA2 */ + if (Controller->Pci.DeviceID == PCI_DEV_ICH0_82801AB) + SupportedMode &= ~(UDMA_MODE3 | UDMA_MODE4); + } + } + + /* Check for 80-conductor cable */ + if (SupportedMode & UDMA_80C_ALL) + { + USHORT IdeConfigReg = PciRead16(Controller, PIIX_REG_CONFIG); + if (!(IdeConfigReg & PIIX_CONFIG_CR(ChanData->Channel))) + { + INFO("CH %lu: BIOS detected 40-conductor cable\n", ChanData->Channel); + SupportedMode &= ~UDMA_80C_ALL; + } + } + break; + } + + case TYPE_SCH: + { + ULONG DeviceTimingReg[2]; + + SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 5); + + /* Check for 80-conductor cable */ + PciRead(Controller, &DeviceTimingReg, SCH_REG_DTIM(0), sizeof(DeviceTimingReg)); + if ((DeviceTimingReg[0] & SCH_DTIM_UDM_MASK) <= SCH_DTIM_UDM(2) && + (DeviceTimingReg[1] & SCH_DTIM_UDM_MASK) <= SCH_DTIM_UDM(2)) + { + INFO("CH %lu: BIOS hasn't selected mode faster than UDMA 2, " + "assume 40-conductor cable\n", + ChanData->Channel); + SupportedMode &= ~UDMA_80C_ALL; + } + break; + } + + case TYPE_SATA: + { + SupportedMode = SATA_ALL; + + // TODO: Read the port map register and set CHANNEL_FLAG_NO_SLAVE when appropriate + ERR("CH %lu: PMR %02X PCS %04X\n", + ChanData->Channel, + PciRead8(Controller, 0x90), + PciRead16(Controller, 0x92)); + break; + } + + default: + ASSERT(FALSE); + UNREACHABLE; + } + ChanData->TransferModeSupported = SupportedMode; +} + +CODE_SEG("PAGE") +NTSTATUS +IntelGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + const INTEL_CONTROLLER_INFO* ControllerInfo; + ULONG i, ExtensionSize, HwFlags; + NTSTATUS Status; + + PAGED_CODE(); + ASSERT(Controller->Pci.VendorID == PCI_VEN_INTEL); + + for (i = 0; i < RTL_NUMBER_OF(IntelControllerList); ++i) + { + ControllerInfo = &IntelControllerList[i]; + + if (Controller->Pci.DeviceID == ControllerInfo->DeviceID) + break; + } + if (i == RTL_NUMBER_OF(IntelControllerList)) + return STATUS_NO_MATCH; + + Controller->Flags |= CTRL_FLAG_DMA_INTERRUPT; + + if (ControllerInfo->Type == TYPE_SCH) + Controller->MaxChannels = 1; + + if (ControllerInfo->Type == TYPE_PIIX || ControllerInfo->Type == TYPE_MPIIX) + ExtensionSize = sizeof(INTEL_HW_EXTENSION); + else + ExtensionSize = 0; + + Status = PciIdeCreateChannelData(Controller, ExtensionSize); + if (!NT_SUCCESS(Status)) + return Status; + + switch (ControllerInfo->Type) + { + case TYPE_MPIIX: + /* + * This is a bridge device and there are no PCI BAR resources allocated, + * so mark the controller as a legacy device. + */ + Controller->Flags &= ~CTRL_FLAG_NATIVE_PCI; + Controller->ChannelEnableBits = IntelMpiixEnableBits; + break; + + case TYPE_PIIX: + case TYPE_PIIX3: + case TYPE_PIIX4: + case TYPE_ICH: + Controller->ChannelEnableBits = IntelPiixEnableBits; + break; + + case TYPE_SATA: + // TODO: Implement the map support + //Controller->ChannelEnabledTest = IntelCombinedEnabledTest; + //HwFlags = TYPE_ICH; + break; + + default: + break; + } + HwFlags = ControllerInfo->Type; + + if (PciFindDevice(IntelPciBridgeErrataMatch, NULL)) + HwFlags |= HW_FLAGS_DISABLE_DMA; + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + ChanData->HwFlags = HwFlags; + + IntelInitChannel(Controller, ControllerInfo, ChanData); + + if (HwFlags & HW_FLAGS_DISABLE_DMA) + ChanData->TransferModeSupported &= PIO_ALL; + } + + return STATUS_SUCCESS; +} diff --git a/drivers/storage/ide/pciidex/chipset/pata_generic.c b/drivers/storage/ide/pciidex/chipset/pata_generic.c new file mode 100644 index 00000000000..1be2a965f32 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/pata_generic.c @@ -0,0 +1,1244 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Generic PCI IDE controller minidriver + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* GLOBALS ********************************************************************/ + +static const ULONG AtapModeToCycleTime[] = +{ + /* 0 1 2 3 4 PIO_MODE */ + 600, 383, 240, 180, 120, + + /* 0 1 2 SWDMA_MODE */ + 960, 480, 240, + + /* 0 1 2 MWDMA_MODE */ + 480, 150, 120, + + /* 0 1 2 3 4 5 6 UDMA_MODE */ + 120, 80, 60, 45, 30, 20, 15 +}; + +static const ATA_TIMING AtapTimingTable[] = +{ + /* PIO_MODE */ + { 70, 290, 240, 165, 150 }, // 0 + { 50, 290, 93, 125, 100 }, // 1 + { 30, 290, 40, 100, 90 }, // 2 + { 30, 80, 70, 80, 70 }, // 3 + { 25, 70, 25, 70, 25 }, // 4 + + /* SWDMA_MODE */ + { 120, 0, 0, 480, 480 }, // 0 + { 90, 0, 0, 240, 240 }, // 1 + { 60, 0, 0, 120, 120 }, // 2 + + /* MWDMA_MODE */ + { 60, 0, 0, 215, 215 }, // 0 + { 45, 0, 0, 80, 50 }, // 1 + { 25, 0, 0, 70, 25 }, // 2 +}; + +/* FUNCTIONS ******************************************************************/ + +static +ATATIM +AtaGetClocks( + _In_ ATATIM TimingValueNs, + _In_ ULONG ClockPeriodPs) +{ + return ((TimingValueNs * 1000) + ClockPeriodPs - 1) / ClockPeriodPs; +} + +static +VOID +AtaCalculateTimings( + _In_ PCHANNEL_DEVICE_CONFIG Device, + _Out_ PATA_TIMING Timing, + _In_ ULONG Mode, + _In_ ULONG ClockPeriodPs) +{ + ATATIM DataCycleTime, CmdCycleTime; + + ASSERT(Mode < RTL_NUMBER_OF(AtapTimingTable)); + + Timing->AddressSetup = AtaGetClocks(AtapTimingTable[Mode].AddressSetup, ClockPeriodPs); + Timing->CmdActive = AtaGetClocks(AtapTimingTable[Mode].CmdActive, ClockPeriodPs); + Timing->CmdRecovery = AtaGetClocks(AtapTimingTable[Mode].CmdRecovery, ClockPeriodPs); + Timing->DataActive = AtaGetClocks(AtapTimingTable[Mode].DataActive, ClockPeriodPs); + Timing->DataRecovery = AtaGetClocks(AtapTimingTable[Mode].DataRecovery, ClockPeriodPs); + + if (Mode >= MWDMA_MODE(0)) + DataCycleTime = Device->MinMwDmaCycleTime; + else if (Mode >= SWDMA_MODE(0)) + DataCycleTime = Device->MinSwDmaCycleTime; + else + { + DataCycleTime = Device->MinPioCycleTime; + + /* The t0 for register transfer is the same except for mode 2 */ + if (Mode == PIO_MODE(2)) + CmdCycleTime = max(DataCycleTime, 330); + else + CmdCycleTime = max(DataCycleTime, AtapModeToCycleTime[Mode]); + } + DataCycleTime = max(DataCycleTime, AtapModeToCycleTime[Mode]); + + /* + * The minimum total cycle time requirement t0 + * should be equal to or greater than the sum of t2 and t2i. + */ + DataCycleTime = AtaGetClocks(DataCycleTime, ClockPeriodPs); + if ((Timing->DataActive + Timing->DataRecovery) < DataCycleTime) + { + Timing->DataActive += (DataCycleTime - (Timing->DataActive + Timing->DataRecovery)) / 2; + Timing->DataRecovery = DataCycleTime - Timing->DataActive; + } + if (Mode < SWDMA_MODE(0)) // PIO modes + { + CmdCycleTime = AtaGetClocks(CmdCycleTime, ClockPeriodPs); + if ((Timing->CmdActive + Timing->CmdRecovery) < CmdCycleTime) + { + Timing->CmdActive += (CmdCycleTime - (Timing->CmdActive + Timing->CmdRecovery)) / 2; + Timing->CmdRecovery = CmdCycleTime - Timing->CmdActive; + } + } +} + +VOID +AtaSelectTimings( + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList, + _Out_writes_all_(MAX_IDE_DEVICE) PATA_TIMING Timings, + _In_range_(>, 0) ULONG ClockPeriodPs, + _In_ ULONG Flags) +{ + ULONG i; + + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + PATA_TIMING Timing = &Timings[i]; + ATA_TIMING DmaTiming; + + if (!Device) + { + RtlZeroMemory(Timing, sizeof(*Timing)); + continue; + } + + /* PIO mode */ + AtaCalculateTimings(Device, Timing, Device->PioMode, ClockPeriodPs); + + /* UDMA works independently of any PIO mode */ + if (Device->DmaMode == PIO_MODE(0) || Device->DmaMode >= UDMA_MODE(0)) + continue; + + /* DMA mode */ + AtaCalculateTimings(Device, &DmaTiming, Device->DmaMode, ClockPeriodPs); + + /* + * Typically, the ATA port driver uses PIO commands along with DMA ones. + * Given that we program the chipset only once after device enumeration, + * we want to make sure the PIO commands would always work. + */ + Timing->AddressSetup = max(Timing->AddressSetup, DmaTiming.AddressSetup); + Timing->CmdActive = max(Timing->CmdActive, DmaTiming.CmdActive); + Timing->CmdRecovery = max(Timing->CmdRecovery, DmaTiming.CmdRecovery); + Timing->DataActive = max(Timing->DataActive, DmaTiming.DataActive); + Timing->DataRecovery = max(Timing->DataRecovery, DmaTiming.DataRecovery); + } + + /* Merge timings shared between both drives */ + if (Flags & SHARED_ADDR_TIMINGS) + { + Timings[0].AddressSetup = max(Timings[0].AddressSetup, Timings[1].AddressSetup); + + Timings[1].AddressSetup = Timings[0].AddressSetup; + } + if (Flags & SHARED_CMD_TIMINGS) + { + Timings[0].CmdActive = max(Timings[0].CmdActive, Timings[1].CmdActive); + Timings[0].CmdRecovery = max(Timings[0].CmdRecovery, Timings[1].CmdRecovery); + + Timings[1].CmdActive = Timings[0].CmdActive; + Timings[1].CmdRecovery = Timings[0].CmdRecovery; + } + if (Flags & SHARED_DATA_TIMINGS) + { + Timings[0].DataActive = max(Timings[0].DataActive, Timings[1].DataActive); + Timings[0].DataRecovery = max(Timings[0].DataRecovery, Timings[1].DataRecovery); + + Timings[1].DataActive = Timings[0].DataActive; + Timings[1].DataRecovery = Timings[0].DataRecovery; + } +} + +VOID +SataSetTransferMode( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + UNREFERENCED_PARAMETER(Controller); + UNREFERENCED_PARAMETER(Channel); + UNREFERENCED_PARAMETER(DeviceList); + + /* + * Nothing to do here, just keep the selected Device->PioMode and Device->DmaMode + * because SATA hardware snoops the SET FEATURES command. + */ + NOTHING; +} + +static +VOID +AtaAcpiFindModeForCycleTime( + _In_ ULONG GtmCycleTime, + _In_ ULONG SupportedModesBitmap, + _In_ ULONG CurrentModesBitmap, + _In_ ULONG MinimumMode, + _In_ ULONG MaximumMode, + _Out_ PULONG BestCycleTime, + _Out_ PULONG BestMode) +{ + LONG i; + + if (GtmCycleTime == IDE_ACPI_TIMING_MODE_NOT_SUPPORTED) + { + *BestCycleTime = IDE_ACPI_TIMING_MODE_NOT_SUPPORTED; + *BestMode = PIO_MODE(0); + return; + } + + for (i = MaximumMode; i >= MinimumMode; i--) + { + if ((SupportedModesBitmap & (1 << i)) && (AtapModeToCycleTime[i] >= GtmCycleTime)) + { + *BestCycleTime = AtapModeToCycleTime[i]; + *BestMode = i; + return; + } + } + + /* + * This method can fail due to the first _GTM result + * may return compatible timings for a device, + * although the DMA mode is currently enabled by BIOS. + * For instance, this has been observed on a VPC 2007 ACPI firmware: + * Drive[0].PioSpeed 900 ns + * Drive[0].DmaSpeed 900 ns + * So we try to determine the transfer mode by other means below. + */ + INFO("Failed to find mode for %ld ns cycle time from %lu in 0x%08lX\n", + (LONG)GtmCycleTime, + MaximumMode, + SupportedModesBitmap); + + /* Search for the best enabled mode */ + if (_BitScanReverse(BestMode, CurrentModesBitmap)) + { + ASSERT(*BestMode < RTL_NUMBER_OF(AtapModeToCycleTime)); + *BestCycleTime = AtapModeToCycleTime[*BestMode]; + } + else + { + *BestCycleTime = IDE_ACPI_TIMING_MODE_NOT_SUPPORTED; + *BestMode = PIO_MODE(0); + } +} + +static +VOID +PciIdeAcpiSetTransferMode( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + PIDE_ACPI_TIMING_MODE_BLOCK CurrentMode = &ChanData->CurrentTimingMode; + IDE_ACPI_TIMING_MODE_BLOCK NewMode; + NTSTATUS Status; + ULONG i; + + ASSERT(ChanData->ChanInfo & CHANNEL_FLAG_HAS_ACPI_GTM); + + NewMode.ModeFlags = CurrentMode->ModeFlags; + + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + BOOLEAN HasUltraDMA; + + if (!Device) + { + NewMode.Drive[i].PioSpeed = IDE_ACPI_TIMING_MODE_NOT_SUPPORTED; + NewMode.Drive[i].DmaSpeed = IDE_ACPI_TIMING_MODE_NOT_SUPPORTED; + continue; + } + + /* PIO timing */ + AtaAcpiFindModeForCycleTime(CurrentMode->Drive[i].PioSpeed, + Device->SupportedModes, + Device->CurrentModes & PIO_ALL, + PIO_MODE(0), + PIO_MODE(4), + &NewMode.Drive[i].PioSpeed, + &Device->PioMode); + + /* DMA timing */ + HasUltraDMA = !!(CurrentMode->ModeFlags & (IDE_ACPI_TIMING_MODE_FLAG_UDMA(i))); + AtaAcpiFindModeForCycleTime(CurrentMode->Drive[i].DmaSpeed, + Device->SupportedModes, + Device->CurrentModes & ~(PIO_ALL | SWDMA_ALL), + MWDMA_MODE(0), + HasUltraDMA ? UDMA_MODE(6) : MWDMA_MODE(2), + &NewMode.Drive[i].DmaSpeed, + &Device->DmaMode); + + if (Device->DmaMode >= UDMA_MODE(0)) + NewMode.ModeFlags |= IDE_ACPI_TIMING_MODE_FLAG_UDMA(i); + else + NewMode.ModeFlags &= ~IDE_ACPI_TIMING_MODE_FLAG_UDMA(i); + } + + /* + * The underlying chipset might not allow the devices to be independently configured. + * + * For example, the Intel PIIX (0x8086:0x1230) cannot specify separate device timings. + * Please refer to PchSata.asi (PSIT bit) + * from the edk2-platforms repository for an example ASL implementation. + * + * It is possible to snoop an ATA command by software and run _STM each time + * the command being executed but an ACPI evaluation request can fail. + * For this reason, the timing setup only needs to be done once. + */ + if (!(NewMode.ModeFlags & IDE_ACPI_TIMING_MODE_FLAG_INDEPENDENT_TIMINGS) && + DeviceList[0] && DeviceList[1]) + { + /* If we have a common timing mode for all devices use it, otherwise take the lower mode */ + if (NewMode.Drive[0].DmaSpeed != IDE_ACPI_TIMING_MODE_NOT_SUPPORTED && + NewMode.Drive[1].DmaSpeed != IDE_ACPI_TIMING_MODE_NOT_SUPPORTED) + { + NewMode.Drive[0].DmaSpeed = max(NewMode.Drive[0].DmaSpeed, NewMode.Drive[1].DmaSpeed); + DeviceList[0]->DmaMode = min(DeviceList[0]->DmaMode, DeviceList[1]->DmaMode); + + NewMode.Drive[1].DmaSpeed = NewMode.Drive[0].DmaSpeed; + DeviceList[1]->DmaMode = DeviceList[0]->DmaMode; + } + NewMode.Drive[0].PioSpeed = max(NewMode.Drive[0].PioSpeed, NewMode.Drive[1].PioSpeed); + DeviceList[0]->PioMode = min(DeviceList[0]->PioMode, DeviceList[1]->PioMode); + + NewMode.Drive[1].PioSpeed = NewMode.Drive[0].PioSpeed; + DeviceList[1]->PioMode = DeviceList[0]->PioMode; + } + + INFO("CH %lu: Old timings %-2lX 0:%-3ld 0:%-3ld 1:%-3ld 1:%-3ld\n", + ChanData->Channel, + CurrentMode->ModeFlags, + (LONG)CurrentMode->Drive[0].PioSpeed, + (LONG)CurrentMode->Drive[0].DmaSpeed, + (LONG)CurrentMode->Drive[1].PioSpeed, + (LONG)CurrentMode->Drive[1].DmaSpeed); + + INFO("CH %lu: New timings %-2lX 0:%-3ld 0:%-3ld 1:%-3ld 1:%-3ld\n", + ChanData->Channel, + NewMode.ModeFlags, + (LONG)NewMode.Drive[0].PioSpeed, + (LONG)NewMode.Drive[0].DmaSpeed, + (LONG)NewMode.Drive[1].PioSpeed, + (LONG)NewMode.Drive[1].DmaSpeed); + + /* Evaluate _STM */ + Status = AtaAcpiSetTimingMode(ChanData->PdoExt->Common.Self, + &NewMode, + DeviceList[0] ? DeviceList[0]->IdentifyDeviceData : NULL, + DeviceList[1] ? DeviceList[1]->IdentifyDeviceData : NULL); + if (!NT_SUCCESS(Status)) + { + /* ACPI request failed, fall back to PIO */ + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + + if (!Device) + continue; + + Device->DmaMode = PIO_MODE(0); + NT_VERIFY(_BitScanReverse(&Device->PioMode, Device->CurrentModes & PIO_ALL)); + } + return; + } + + /* Save the result */ + AtaAcpiGetTimingMode(ChanData->PdoExt->Common.Self, CurrentMode); +} + +static +VOID +PciIdeBiosSetTransferMode( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + ULONG i; + + /* Update manually using BIOS boot settings */ + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + ULONG DmaFlags; + + if (!Device) + continue; + + /* Get the PIO and DMA modes set by BIOS */ + if (!_BitScanReverse(&Device->DmaMode, Device->CurrentModes & ~PIO_ALL)) + Device->DmaMode = PIO_MODE(0); + NT_VERIFY(_BitScanReverse(&Device->PioMode, Device->CurrentModes & PIO_ALL)); + + if (i == 0) + DmaFlags = CHANNEL_FLAG_DRIVE0_DMA_CAPABLE; + else + DmaFlags = CHANNEL_FLAG_DRIVE1_DMA_CAPABLE; + + /* Disable DMA when when it is not supported */ + if (!(ChanData->ChanInfo & DmaFlags)) + Device->DmaMode = PIO_MODE(0); + } +} + +static +VOID +PciIdeGenericSetTransferMode( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + PCHANNEL_DATA_PATA ChanData = Controller->Channels[Channel]; + + /* Set the PATA transfer timings through ACPI if possible */ + if (ChanData->ChanInfo & CHANNEL_FLAG_HAS_ACPI_GTM) + PciIdeAcpiSetTransferMode(ChanData, DeviceList); + else + PciIdeBiosSetTransferMode(ChanData, DeviceList); +} + +static +CODE_SEG("PAGE") +VOID +PataReleaseLegacyAddressRanges( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + PCONFIGURATION_INFORMATION ConfigInfo = IoGetConfigurationInformation(); + + PAGED_CODE(); + + if (ChanData->ChanInfo & CHANNEL_FLAG_PRIMARY_ADDRESS_CLAIMED) + ConfigInfo->AtDiskPrimaryAddressClaimed = FALSE; + if (ChanData->ChanInfo & CHANNEL_FLAG_SECONDARY_ADDRESS_CLAIMED) + ConfigInfo->AtDiskSecondaryAddressClaimed = FALSE; + + ChanData->ChanInfo &= ~(CHANNEL_FLAG_PRIMARY_ADDRESS_CLAIMED | + CHANNEL_FLAG_SECONDARY_ADDRESS_CLAIMED); +} + +static +CODE_SEG("PAGE") +VOID +PataClaimLegacyAddressRanges( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + PCONFIGURATION_INFORMATION ConfigInfo = IoGetConfigurationInformation(); + + PAGED_CODE(); + + if (IsNEC_98) + { + if (ChanData->ChanInfo & CHANNEL_FLAG_CBUS) + { + ConfigInfo->AtDiskPrimaryAddressClaimed = TRUE; + ConfigInfo->AtDiskSecondaryAddressClaimed = TRUE; + + ChanData->ChanInfo |= CHANNEL_FLAG_PRIMARY_ADDRESS_CLAIMED | + CHANNEL_FLAG_SECONDARY_ADDRESS_CLAIMED; + } + } + else if (ChanData->Regs.Data == UlongToPtr(PCIIDE_LEGACY_PRIMARY_COMMAND_BASE)) + { + ConfigInfo->AtDiskPrimaryAddressClaimed = TRUE; + ChanData->ChanInfo |= CHANNEL_FLAG_PRIMARY_ADDRESS_CLAIMED; + } + else if (ChanData->Regs.Data == UlongToPtr(PCIIDE_LEGACY_SECONDARY_COMMAND_BASE)) + { + ConfigInfo->AtDiskSecondaryAddressClaimed = TRUE; + ChanData->ChanInfo |= CHANNEL_FLAG_SECONDARY_ADDRESS_CLAIMED; + } +} + +static +CODE_SEG("PAGE") +VOID +PciIdeFreeMemory( + _In_ PVOID ChannelContext) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + PDMA_ADAPTER DmaAdapter = ChanData->DmaAdapter; + PDMA_OPERATIONS DmaOperations = ChanData->DmaAdapter->DmaOperations; + PHYSICAL_ADDRESS PrdTablePhysicalAddress; + + PAGED_CODE(); + + if (!ChanData->PrdTable) + return; + + PrdTablePhysicalAddress.QuadPart = ChanData->PrdTablePhysicalAddress; + + DmaOperations->FreeCommonBuffer(DmaAdapter, + sizeof(*ChanData->PrdTable) * ChanData->MaximumPhysicalPages, + PrdTablePhysicalAddress, + ChanData->PrdTable, + TRUE); // Cached + ChanData->PrdTable = NULL; +} + +static +CODE_SEG("PAGE") +NTSTATUS +PciIdeAllocateMemory( + _In_ PVOID ChannelContext) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + PDMA_OPERATIONS DmaOperations = ChanData->DmaAdapter->DmaOperations; + PHYSICAL_ADDRESS LogicalAddress; + ULONG BlockSize; + + PAGED_CODE(); + + BlockSize = sizeof(*ChanData->PrdTable) * ChanData->MaximumPhysicalPages; + ChanData->PrdTable = DmaOperations->AllocateCommonBuffer(ChanData->DmaAdapter, + BlockSize, + &LogicalAddress, + TRUE); // Cached + if (!ChanData->PrdTable) + return STATUS_INSUFFICIENT_RESOURCES; + RtlZeroMemory(ChanData->PrdTable, BlockSize); + + ChanData->PrdTablePhysicalAddress = LogicalAddress.LowPart; + + /* 32-bit DMA */ + ASSERT(LogicalAddress.HighPart == 0); + + /* The descriptor table must be 4 byte aligned */ + ASSERT((LogicalAddress.LowPart % sizeof(ULONG)) == 0); + + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +BOOLEAN +PciIdeIsDmaStatusValid( + _In_ UCHAR DmaStatus) +{ + PAGED_CODE(); + + /* The status bits [3:4] must return 0 on reads */ + if (DmaStatus & (PCIIDE_DMA_STATUS_RESERVED1 | PCIIDE_DMA_STATUS_RESERVED2)) + { + /* + * Not a PCI IDE DMA device. This may happen when the channel is disabled + * (e.g. 8086:8C00 returns 0xFF for disabled SATA channels). + */ + return FALSE; + } + + return TRUE; +} + +static +CODE_SEG("PAGE") +BOOLEAN +PciIdeControllerInitDma( + _In_ PATA_CONTROLLER Controller) +{ + ULONG i; + + PAGED_CODE(); + + if (!(Controller->Pci.Command & PCI_ENABLE_BUS_MASTER)) + { + INFO("PCI bus mastering disabled\n"); + return FALSE; + } + + if ((Controller->Pci.BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) && + !(Controller->Pci.ProgIf & PCIIDE_PROGIF_DMA_CAPABLE)) + { + INFO("Non DMA capable controller detected\n"); + return FALSE; + } + + /* Assign DMA resources */ + if (!(Controller->Flags & CTRL_FLAG_MANUAL_RES)) + { + PUCHAR IoBase; + + /* Check if BAR4 is available */ + IoBase = AtaCtrlPciMapBar(Controller, PCIIDE_DMA_IO_BAR, PCIIDE_DMA_IO_RANGE_LENGTH); + if (!IoBase) + { + if (Controller->AccessRange[PCIIDE_DMA_IO_BAR].Flags & RANGE_IS_VALID) + ERR("Failed to map bus master registers"); + else + INFO("No bus master registers"); + return FALSE; + } + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + if (Controller->AccessRange[PCIIDE_DMA_IO_BAR].Flags & RANGE_IS_MAPPED) + ChanData->ChanInfo |= CHANNEL_FLAG_MRES_DMA; + + if (!IS_PRIMARY_CHANNEL(ChanData)) + IoBase += PCIIDE_DMA_SECONDARY_CHANNEL_OFFSET; + + /* + * Save the DMA I/O address *in any case*. Some PATA controllers (Intel ICH) + * assert the DMA interrupt even if the current command is a PIO command, + * so we have to always clear the DMA interrupt. + */ + ChanData->Regs.Dma = IoBase; + + if (Controller->Flags & CTRL_FLAG_DMA_INTERRUPT) + ChanData->CheckInterrupt = PciIdeCheckInterrupt; + } + } + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + UCHAR DmaStatus; + + /* This channel does not support DMA */ + if (!(ChanData->TransferModeSupported & ~PIO_ALL)) + continue; + + ASSERT(ChanData->Regs.Dma != NULL); + + DmaStatus = ATA_READ(ChanData->Regs.Dma + PCIIDE_DMA_STATUS, ChanData, MRES_DMA); + if (!PciIdeIsDmaStatusValid(DmaStatus)) + { + WARN("CH %lu: %p DMA 0x%02X\n", ChanData->Channel, ChanData->Regs.Dma, DmaStatus); + ChanData->TransferModeSupported &= PIO_ALL; + continue; + } + + INFO("CH %lu: %p DMA 0x%02X\n", ChanData->Channel, ChanData->Regs.Dma, DmaStatus); + + /* We look at the primary channel status register to determine the simplex mode */ + if ((i == 0) && (Controller->MaxChannels > 1)) + { + if (DmaStatus & PCIIDE_DMA_STATUS_SIMPLEX) + Controller->Flags |= CTRL_FLAG_IS_SIMPLEX; + } + + /* The status bits 5:6 are set by the BIOS firmware at boot */ + if (DmaStatus & PCIIDE_DMA_STATUS_DRIVE0_DMA_CAPABLE) + ChanData->ChanInfo |= CHANNEL_FLAG_DRIVE0_DMA_CAPABLE; + if (DmaStatus & PCIIDE_DMA_STATUS_DRIVE1_DMA_CAPABLE) + ChanData->ChanInfo |= CHANNEL_FLAG_DRIVE1_DMA_CAPABLE; + } + + return TRUE; +} + +CODE_SEG("PAGE") +VOID +PciIdeInitTaskFileIoResources( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ ULONG_PTR CommandPortBase, + _In_ ULONG_PTR ControlPortBase, + _In_ ULONG CommandBlockSpare) +{ + PIDE_REGISTERS Registers = &ChanData->Regs; + + PAGED_CODE(); + + /* Standard PATA register layout */ + Registers->Data = (PVOID)(CommandPortBase + 0 * CommandBlockSpare); + Registers->Error = (PVOID)(CommandPortBase + 1 * CommandBlockSpare); + Registers->SectorCount = (PVOID)(CommandPortBase + 2 * CommandBlockSpare); + Registers->LbaLow = (PVOID)(CommandPortBase + 3 * CommandBlockSpare); + Registers->LbaMid = (PVOID)(CommandPortBase + 4 * CommandBlockSpare); + Registers->LbaHigh = (PVOID)(CommandPortBase + 5 * CommandBlockSpare); + Registers->Device = (PVOID)(CommandPortBase + 6 * CommandBlockSpare); + Registers->Status = (PVOID)(CommandPortBase + 7 * CommandBlockSpare); + + Registers->Control = (PVOID)ControlPortBase; +} + +static +CODE_SEG("PAGE") +NTSTATUS +PciIdeAssignNativeResources( + _In_ PATA_CONTROLLER Controller, + _In_ PCHANNEL_DATA_PATA ChanData) +{ + PVOID CommandPortBase, ControlPortBase; + const ULONG BarIndex = ChanData->Channel * 2; + + PAGED_CODE(); + + CommandPortBase = AtaCtrlPciMapBar(Controller, BarIndex, PCIIDE_COMMAND_IO_RANGE_LENGTH); + if (!CommandPortBase) + return STATUS_INSUFFICIENT_RESOURCES; + + ControlPortBase = AtaCtrlPciMapBar(Controller, BarIndex + 1, PCIIDE_CONTROL_IO_RANGE_LENGTH); + if (!ControlPortBase) + return STATUS_INSUFFICIENT_RESOURCES; + + if (Controller->AccessRange[BarIndex].Flags & RANGE_IS_MAPPED) + ChanData->ChanInfo |= CHANNEL_FLAG_MRES_TF; + + if (Controller->AccessRange[BarIndex + 1].Flags & RANGE_IS_MAPPED) + ChanData->ChanInfo |= CHANNEL_FLAG_MRES_CTRL; + + PciIdeInitTaskFileIoResources(ChanData, + (ULONG_PTR)CommandPortBase, + (ULONG_PTR)ControlPortBase + PCIIDE_CONTROL_IO_BAR_OFFSET, + 1); + + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +PciIdeAssignLegacyResources( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ PCM_RESOURCE_LIST ResourcesTranslated) +{ + PCM_PARTIAL_RESOURCE_DESCRIPTOR CommandPortDesc = NULL; + PCM_PARTIAL_RESOURCE_DESCRIPTOR ControlPortDesc = NULL; + PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptDesc = NULL; + ULONG i, Spare; + + PAGED_CODE(); + + if (!ResourcesTranslated) + return STATUS_INSUFFICIENT_RESOURCES; + + for (i = 0; i < ResourcesTranslated->List[0].PartialResourceList.Count; ++i) + { + PCM_PARTIAL_RESOURCE_DESCRIPTOR Desc; + + Desc = &ResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[i]; + switch (Desc->Type) + { + case CmResourceTypePort: + case CmResourceTypeMemory: + { + if (IsNEC_98 && (ResourcesTranslated->List[0].PartialResourceList.Count >= 12)) + { + if ((Desc->u.Port.Start.QuadPart == 0x640)) + { + CommandPortDesc = Desc; + ChanData->ChanInfo |= CHANNEL_FLAG_CBUS; + } + else if ((Desc->u.Port.Start.QuadPart == 0x74C)) + { + ControlPortDesc = Desc; + ChanData->ChanInfo |= CHANNEL_FLAG_CBUS; + } + } + else if (Desc->u.Port.Length == PCIIDE_LEGACY_CONTROL_IO_RANGE_LENGTH) + { + if (!ControlPortDesc) + ControlPortDesc = Desc; + } + else if (Desc->u.Port.Length == PCIIDE_LEGACY_COMMAND_IO_RANGE_LENGTH) + { + if (!CommandPortDesc) + CommandPortDesc = Desc; + } + + break; + } + + case CmResourceTypeInterrupt: + { + if (!InterruptDesc) + InterruptDesc = Desc; + break; + } + + default: + break; + } + } + + if (!CommandPortDesc || !ControlPortDesc || !InterruptDesc) + return STATUS_DEVICE_CONFIGURATION_ERROR; + + if (CommandPortDesc->Type == CmResourceTypeMemory) + ChanData->ChanInfo |= CHANNEL_FLAG_MRES_TF; + + if (ControlPortDesc->Type == CmResourceTypeMemory) + ChanData->ChanInfo |= CHANNEL_FLAG_MRES_CTRL; + + RtlCopyMemory(&ChanData->InterruptDesc, InterruptDesc, sizeof(*InterruptDesc)); + +#if defined(_M_IX86) + if (ChanData->ChanInfo & CHANNEL_FLAG_CBUS) + { + Spare = 2; + } + else +#endif + { + Spare = 1; + } + PciIdeInitTaskFileIoResources(ChanData, + (ULONG_PTR)CommandPortDesc->u.Port.Start.QuadPart, + (ULONG_PTR)ControlPortDesc->u.Port.Start.QuadPart, + Spare); + + return STATUS_SUCCESS; +} + +/* + * PCI IDE Notes: + * + * When the PCI IDE controller operates in compatibility mode, + * the pciidex driver assigns a list of boot resources to each IDE channel. + * + * _GTM, _STM _GTF + * PCI0-->IDE0---------------------------+-->CHN0---------------------------+-->DRV0 + * IO: Start 0:FFA0, Len 10 | IO: Start 0:1F0, Len 8 | + * | IO: Start 0:3F6, Len 1 \-->DRV1 + * | INT: Lev A Vec A Aff FFFFFFFF + * | + * \-->CHN1---------------------------+-->DRV0 + * IO: Start 0:170, Len 8 | + * IO: Start 0:376, Len 1 \-->DRV1 + * INT: Lvl F Vec F Aff FFFFFFFF + * + * In native-PCI mode, the PCI IDE controller acts as a true PCI device + * and no resources are assigned to the channel device object at all. + * + * _GTM, _STM _GTF + * PCI0-->IDE0---------------------------+-->CHN0---------------------------+-->DRV0 + * IO: Start 0:FFC0, Len 8 | | + * IO: Start 0:FF8C, Len 4 | \-->DRV1 + * IO: Start 0:FF80, Len 8 | + * IO: Start 0:FF88, Len 4 | + * IO: Start 0:FFA0, Len 10 \-->CHN1---------------------------+-->DRV0 + * INT: Lvl B Vec B Aff FFFFFFFF | + * \-->DRV1 + * + * Note that the NT architecture does not support switching only one IDE channel to native mode. + */ +CODE_SEG("PAGE") +NTSTATUS +PciIdeParseResources( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ PCM_RESOURCE_LIST ResourcesTranslated) +{ + PATA_CONTROLLER Controller = ChanData->Controller; + NTSTATUS Status; + + PAGED_CODE(); + + ASSERT(ChanData->Channel < 2); + + if (Controller->Flags & CTRL_FLAG_NATIVE_PCI) + Status = PciIdeAssignNativeResources(Controller, ChanData); + else + Status = PciIdeAssignLegacyResources(ChanData, ResourcesTranslated); + if (!NT_SUCCESS(Status)) + { + ERR("CH %lu: Failed to assign I/O resources 0x%lx\n", ChanData->Channel, Status); + return Status; + } + + PataClaimLegacyAddressRanges(ChanData); + + return STATUS_SUCCESS; +} + +CODE_SEG("PAGE") +VOID +PciIdeFreeResources( + _In_ PVOID ChannelContext) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + + PAGED_CODE(); + + if (ChanData->InterruptObject) + { + AtaChanEnableInterruptsSync(ChanData, FALSE); + + IoDisconnectInterrupt(ChanData->InterruptObject); + ChanData->InterruptObject = NULL; + } + + ChanData->Regs.Dma = NULL; + + ChanData->ChanInfo &= ~(CHANNEL_FLAG_DRIVE0_DMA_CAPABLE | + CHANNEL_FLAG_DRIVE1_DMA_CAPABLE); + + PataReleaseLegacyAddressRanges(ChanData); +} + +CODE_SEG("PAGE") +IDE_CHANNEL_STATE +PciIdeGetChannelState( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel) +{ + PAGED_CODE(); + + /* SFF-8038i compliant hardware */ + if (Controller->Flags & CTRL_FLAG_NATIVE_PCI) + { + /* + * If any native PCI IDE channel is enabled, + * its PCI I/O resources should be allocated as well: + * + * [BAR 0] I/O ports at FFC0 [size=8] Channel 0 task file registers + * [BAR 1] I/O ports at FF8C [size=4] Channel 0 device control + * [BAR 2] I/O ports at FF80 [size=8] Channel 1 task file registers + * [BAR 3] I/O ports at FF88 [size=4] Channel 1 device control + * [BAR 4] I/O ports at FFA0 [size=16] Bus Mastering registers + * [BAR 5] Memory at FE780000 Aux registers + */ + if (!(Controller->Flags & CTRL_FLAG_MANUAL_RES)) + { + const ULONG BarIndex = Channel * 2; + + ASSERT(Channel < 2); + + /* Check task file registers */ + if (!(Controller->AccessRange[BarIndex].Flags & RANGE_IS_VALID)) + return ChannelDisabled; + + /* Check device control */ + if (!(Controller->AccessRange[BarIndex + 1].Flags & RANGE_IS_VALID)) + return ChannelDisabled; + } + } + + if (Controller->ChannelEnableBits) + { + if (Controller->Flags & CTRL_FLAG_USE_TEST_FUNCTION) + { + /* Device-specific test */ + return Controller->ChannelEnabledTest(Controller, Channel); + } + else + { + const ATA_PCI_ENABLE_BITS* EnableBits = &Controller->ChannelEnableBits[Channel]; + UCHAR RegisterValue; + + /* Test PCI bits */ + RegisterValue = PciRead8(Controller, EnableBits->Register); + if ((RegisterValue & EnableBits->Mask) == EnableBits->ValueEnabled) + return ChannelEnabled; + else + return ChannelDisabled; + } + } + + /* This channel is always enabled */ + return ChannelStateUnknown; +} + +static +VOID +PataEnableInterrupts( + _In_ PVOID ChannelContext, + _In_ BOOLEAN Enable) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + UCHAR Control; + + INFO("CH %lu: %sable interrupts\n", ChanData->Channel, Enable ? "En" : "Dis"); + + Control = IDE_DC_ALWAYS; + if (Enable) + Control |= IDE_DC_REENABLE_CONTROLLER; + else + Control |= IDE_DC_DISABLE_INTERRUPTS; + +#if defined(_M_IX86) + if (ChanData->ChanInfo & CHANNEL_FLAG_CBUS) + { + WRITE_PORT_UCHAR((PUCHAR)PC98_ATA_BANK, 1); + ATA_WRITE(ChanData->Regs.Control, Control, ChanData, MRES_CTRL); + + /* Clear interrupts */ + if (ChanData->CheckInterrupt) + ChanData->CheckInterrupt(ChanData); + ChanData->ReadStatus(ChanData); + + WRITE_PORT_UCHAR((PUCHAR)PC98_ATA_BANK, 0); + ChanData->LastAtaBankId = 0xFF; + } +#endif + ATA_WRITE(ChanData->Regs.Control, Control, ChanData, MRES_CTRL); + + /* Clear interrupts */ + if (ChanData->CheckInterrupt) + ChanData->CheckInterrupt(ChanData); + ChanData->ReadStatus(ChanData); + + if (ChanData->Regs.Dma != NULL) + { + UCHAR Status = ATA_READ(ChanData->Regs.Dma + PCIIDE_DMA_STATUS, ChanData, MRES_DMA); + + ATA_WRITE(ChanData->Regs.Dma + PCIIDE_DMA_STATUS, Status, ChanData, MRES_DMA); + } +} + +CODE_SEG("PAGE") +NTSTATUS +PciIdeConnectInterrupt( + _In_ PVOID ChannelContext) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + PATA_CONTROLLER Controller = ChanData->Controller; + PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptDesc; + PKSERVICE_ROUTINE IsrHandler; + NTSTATUS Status; + + PAGED_CODE(); + + if (Controller->Flags & CTRL_FLAG_NATIVE_PCI) + InterruptDesc = &Controller->InterruptDesc; + else + InterruptDesc = &ChanData->InterruptDesc; + ASSERT(InterruptDesc); + + if (ChanData->Regs.Dma != NULL) + IsrHandler = PciIdeChannelIsr; + else + IsrHandler = PataChannelIsr; + Status = IoConnectInterrupt(&ChanData->InterruptObject, + IsrHandler, + ChanData, + NULL, + InterruptDesc->u.Interrupt.Vector, + InterruptDesc->u.Interrupt.Level, + InterruptDesc->u.Interrupt.Level, + (InterruptDesc->Flags & CM_RESOURCE_INTERRUPT_LATCHED) + ? Latched : LevelSensitive, + (InterruptDesc->ShareDisposition == CmResourceShareShared), + InterruptDesc->u.Interrupt.Affinity, + FALSE); + if (!NT_SUCCESS(Status)) + { + ERR("Could not connect to interrupt %lu, status 0x%lx\n", + InterruptDesc->u.Interrupt.Vector, Status); + return Status; + } + + return STATUS_SUCCESS; +} + +CODE_SEG("PAGE") +NTSTATUS +PciIdeAttachChannel( + _In_ PVOID ChannelContext, + _In_ BOOLEAN Attach) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + + PAGED_CODE(); + + AtaChanEnableInterruptsSync(ChanData, Attach); + + return STATUS_SUCCESS; +} + +CODE_SEG("PAGE") +NTSTATUS +PciIdeCreateChannelData( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG HwExtensionSize) +{ + PCHANNEL_DATA_PATA ChanData; + ULONG i, Size; + + PAGED_CODE(); + ASSERT(Controller->MaxChannels != 0); + + Size = FIELD_OFFSET(CHANNEL_DATA_PATA, HwExt) + HwExtensionSize; + ChanData = ExAllocatePoolZero(NonPagedPool, Size * Controller->MaxChannels, TAG_PCIIDEX); + if (!ChanData) + return STATUS_INSUFFICIENT_RESOURCES; + + Controller->ChanDataBlock = ChanData; + Controller->ChannelBitmap = NUM_TO_BITMAP(Controller->MaxChannels); + + for (i = 0; i < Controller->MaxChannels; ++i) + { + Controller->Channels[i] = ChanData; + + ChanData->Channel = i; + ChanData->Controller = Controller; + + /* Set default values */ + ChanData->ChanInfo = CHANNEL_FLAG_IO32; + ChanData->AllocateMemory = PciIdeAllocateMemory; + ChanData->FreeMemory = PciIdeFreeMemory; + ChanData->EnableInterrupts = PataEnableInterrupts; + ChanData->PreparePrdTable = PciIdePreparePrdTable; + ChanData->PrepareIo = PataPrepareIo; + ChanData->StartIo = PataStartIo; + ChanData->LoadTaskFile = PataLoadTaskFile; + ChanData->SaveTaskFile = PataSaveTaskFile; + ChanData->ReadStatus = PataReadStatus; + ChanData->SetTransferMode = PciIdeGenericSetTransferMode; + ChanData->TransferModeSupported = PIO_ALL | SWDMA_ALL | MWDMA_ALL | UDMA_ALL; + + KeInitializeDpc(&ChanData->PollingTimerDpc, PataPollingTimerDpc, ChanData); + + ChanData = (PVOID)((ULONG_PTR)ChanData + Size); + } + + return STATUS_SUCCESS; +} + +CODE_SEG("PAGE") +NTSTATUS +PciIdeGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + NTSTATUS Status; + ULONG i; + + PAGED_CODE(); + + Controller->MaxChannels = MAX_IDE_CHANNEL; + + /* Match the controller through the PCI ID */ + switch (Controller->Pci.VendorID) + { + case PCI_VEN_ATI: + Status = AtiGetControllerProperties(Controller); + break; + case PCI_VEN_AMD: + case PCI_VEN_NVIDIA: + Status = AmdGetControllerProperties(Controller); + break; + case PCI_VEN_CMD: + Status = CmdGetControllerProperties(Controller); + if (Status == STATUS_NO_MATCH) + Status = Sil680GetControllerProperties(Controller); + break; + case PCI_VEN_INTEL: + Status = IntelGetControllerProperties(Controller); + break; + case PCI_VEN_PC_TECH: + Status = PcTechGetControllerProperties(Controller); + break; + case PCI_VEN_SERVERWORKS: + Status = SvwPataGetControllerProperties(Controller); + if (Status == STATUS_NO_MATCH) + Status = SvwSataGetControllerProperties(Controller); + break; + case PCI_VEN_TOSHIBA: + Status = ToshibaGetControllerProperties(Controller); + break; + case PCI_VEN_VIA: + Status = ViaGetControllerProperties(Controller); + break; + + default: + Status = STATUS_NO_MATCH; + break; + } + + /* Try to use the generic PCI IDE minidriver */ + if (Status == STATUS_NO_MATCH) + { + if ((Controller->Pci.BaseClass != PCI_CLASS_MASS_STORAGE_CTLR) || + ((Controller->Pci.SubClass != PCI_SUBCLASS_MSC_IDE_CTLR) && + (Controller->Pci.SubClass != PCI_SUBCLASS_MSC_RAID_CTLR))) + { + ERR("Unsupported controller\n"); + return Status; + } + + WARN("%04X:%04X.%02X: Using generic PCI IDE minidriver\n", + Controller->Pci.VendorID, + Controller->Pci.DeviceID, + Controller->Pci.RevisionID); + + Status = PciIdeCreateChannelData(Controller, 0); + if (!NT_SUCCESS(Status)) + return Status; + + /* Disable 32-bit I/O for unknown controllers */ + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + ChanData->ChanInfo &= ~CHANNEL_FLAG_IO32; + } + } + + if (!NT_SUCCESS(Status)) + return Status; + + /* + * Check for a legacy PCI IDE device. + * NOTE: NT architecture does not support switching only one IDE channel to native mode. + */ + if ((Controller->Pci.BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) && + (Controller->Pci.SubClass == PCI_SUBCLASS_MSC_IDE_CTLR)) + { + if (!(Controller->Pci.ProgIf & PCIIDE_PROGIF_PRIMARY_CHANNEL_NATIVE_MODE) || + !(Controller->Pci.ProgIf & PCIIDE_PROGIF_SECONDARY_CHANNEL_NATIVE_MODE)) + { + Controller->Flags &= ~CTRL_FLAG_NATIVE_PCI; + } + } + + if (Controller->Flags & CTRL_FLAG_NATIVE_PCI) + { + if (Controller->InterruptDesc.Type != CmResourceTypeInterrupt) + { + ERR("No interrupt resource\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (!PciIdeControllerInitDma(Controller)) + { + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + ChanData->TransferModeSupported &= PIO_ALL; + } + } + + if (Controller->Flags & CTRL_FLAG_IS_SIMPLEX) + { + WARN("Sync access for hardware is required\n"); + + Controller->HwSyncObject = IoCreateController(0); + if (!Controller->HwSyncObject) + return STATUS_INSUFFICIENT_RESOURCES; + } + + return STATUS_SUCCESS; +} diff --git a/drivers/storage/ide/pciidex/chipset/pata_hw.c b/drivers/storage/ide/pciidex/chipset/pata_hw.c new file mode 100644 index 00000000000..ae7b1b862d4 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/pata_hw.c @@ -0,0 +1,259 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: PATA hardware support + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* FUNCTIONS ******************************************************************/ + +static +BOOLEAN +PataIsDevicePresent( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ ULONG DeviceNumber) +{ + UCHAR IdeStatus; + + /* Select the device */ + ATA_SELECT_DEVICE(ChanData, DeviceNumber, IDE_DRIVE_SELECT | ((DeviceNumber & 1) << 4)); + + /* Do a quick check first */ + IdeStatus = ChanData->ReadStatus(ChanData); + INFO("CH %lu: Device %lu status %02x\n", ChanData->Channel, DeviceNumber, IdeStatus); + if (IdeStatus == 0xFF || IdeStatus == 0x7F) + return FALSE; + + /* Look at controller */ + ATA_WRITE(ChanData->Regs.ByteCountLow, 0x55, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.ByteCountLow, 0xAA, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.ByteCountLow, 0x55, ChanData, MRES_TF); + if (ATA_READ(ChanData->Regs.ByteCountLow, ChanData, MRES_TF) != 0x55) + return FALSE; + ATA_WRITE(ChanData->Regs.ByteCountHigh, 0xAA, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.ByteCountHigh, 0x55, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.ByteCountHigh, 0xAA, ChanData, MRES_TF); + if (ATA_READ(ChanData->Regs.ByteCountHigh, ChanData, MRES_TF) != 0xAA) + return FALSE; + + return TRUE; +} + +static +VOID +PataIssueSoftwareReset( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + ATA_WRITE(ChanData->Regs.Control, IDE_DC_RESET_CONTROLLER | IDE_DC_ALWAYS, + ChanData, MRES_CTRL); + KeStallExecutionProcessor(20); + ATA_WRITE(ChanData->Regs.Control, IDE_DC_DISABLE_INTERRUPTS | IDE_DC_ALWAYS, + ChanData, MRES_CTRL); + KeStallExecutionProcessor(20); +} + +static +VOID +PataDrainDeviceBuffer( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + ULONG i; + USHORT LocalBuffer; + + /* Try to clear the DRQ indication */ + for (i = 0; i < ATA_MAX_TRANSFER_LENGTH / sizeof(USHORT); ++i) + { + UCHAR IdeStatus = ChanData->ReadStatus(ChanData); + + if (!(IdeStatus & IDE_STATUS_DRQ)) + break; + + ATA_READ_BLOCK_16((PUSHORT)ChanData->Regs.Data, + &LocalBuffer, + 1, + ChanData, + MRES_TF); + } + + if (i > 0) + INFO("CH %lu: Total drained %lu bytes\n", ChanData->Channel, i * sizeof(USHORT)); +} + +ULONG +PataChannelGetMaximumDeviceCount( + _In_ PVOID ChannelContext) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + +#if defined(_M_IX86) + if (ChanData->ChanInfo & CHANNEL_FLAG_CBUS) + return 4; +#endif + + return (ChanData->ChanInfo & CHANNEL_FLAG_NO_SLAVE) ? 1 : 2; +} + +VOID +PataResetChannel( + _In_ PVOID ChannelContext) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + ULONG i, DeviceNumber, DeviceCount, DeviceBitmap = 0; + + ChanData->IsPollingActive = FALSE; + +#if defined(_M_IX86) + if (ChanData->ChanInfo & CHANNEL_FLAG_CBUS) + ChanData->LastAtaBankId = 0xFF; +#endif + + /* + * Reset the start/stop bus master bit, also needed on some chips to recover, + * see for example Intel 82371AB/EB/MB Specification Update 297738-017 #10. + */ + if (ChanData->Regs.Dma != NULL) + PciIdeDmaStop(ChanData); + + PataDrainDeviceBuffer(ChanData); + + DeviceCount = PataChannelGetMaximumDeviceCount(ChanData); + for (DeviceNumber = 0; DeviceNumber < DeviceCount; ++DeviceNumber) + { + if (PataIsDevicePresent(ChanData, DeviceNumber)) + DeviceBitmap |= 1 << DeviceNumber; + } + /* Restore the device selection */ + ATA_SELECT_DEVICE(ChanData, 0, IDE_DRIVE_SELECT); + + WARN("CH %lu: Resetting IDE channel, devices map 0x%lx\n", ChanData->Channel, DeviceBitmap); + +#if defined(_M_IX86) + if (ChanData->ChanInfo & CHANNEL_FLAG_CBUS) + { + /* Reset the secondary IDE channel if present */ + if (DeviceBitmap & ((1 << 2) | (1 << 3))) + { + WRITE_PORT_UCHAR((PUCHAR)PC98_ATA_BANK, 1); + PataIssueSoftwareReset(ChanData); + } + + WRITE_PORT_UCHAR((PUCHAR)PC98_ATA_BANK, 0); + ChanData->LastAtaBankId = 0xFF; + } +#endif + PataIssueSoftwareReset(ChanData); + + for (DeviceNumber = 0; DeviceNumber < DeviceCount; ++DeviceNumber) + { + UCHAR IdeStatus; + + /* The reset will cause the master device to be selected */ + if ((DeviceNumber % 2) != 0) + { + /* Always check BSY for the master device regardless of its presence */ + if (!(DeviceBitmap & (1 << DeviceNumber))) + continue; + + for (i = ATA_TIME_RESET_SELECT; i > 0; i--) + { + /* Select the device again */ + ATA_SELECT_DEVICE(ChanData, + DeviceNumber, + IDE_DRIVE_SELECT | ((DeviceNumber & 1) << 4)); + + /* Check whether the selection was successful */ + ATA_WRITE(ChanData->Regs.ByteCountLow, 0xAA, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.ByteCountLow, 0x55, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.ByteCountLow, 0xAA, ChanData, MRES_TF); + if (ATA_READ(ChanData->Regs.ByteCountLow, ChanData, MRES_TF) == 0xAA) + break; + + AtaSleep(); + } + if (i == 0) + { + ERR("CH %lu: Device %lu selection timeout %02x\n", + ChanData->Channel, DeviceNumber, ChanData->ReadStatus(ChanData)); + continue; + } + } + + /* Now wait for busy to clear */ + for (i = 0; i < ATA_TIME_BUSY_RESET; ++i) + { + IdeStatus = ChanData->ReadStatus(ChanData); + if (IdeStatus == 0xFF) + break; + + if (!(IdeStatus & IDE_STATUS_BUSY)) + break; + + AtaSleep(); + } + + if (IdeStatus & IDE_STATUS_BUSY) + { + ERR("CH %lu: Failed to reset device %lu, status %02x\n", + ChanData->Channel, DeviceNumber, IdeStatus); + } + else + { + INFO("CH %lu: Device %lu online with status %02x\n", + ChanData->Channel, DeviceNumber, IdeStatus); + } + } + + AtaChanEnableInterruptsSync(ChanData, TRUE); +} + +ATA_CONNECTION_STATUS +PataIdentifyDevice( + _In_ PVOID ChannelContext, + _In_ ULONG DeviceNumber) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + UCHAR IdeStatus; + + if (!PataIsDevicePresent(ChanData, DeviceNumber)) + return CONN_STATUS_NO_DEVICE; + + /* Wait for busy to clear */ + IdeStatus = ATA_WAIT(ChanData, ATA_TIME_BUSY_SELECT, IDE_STATUS_BUSY, 0); + if (IdeStatus & (IDE_STATUS_BUSY | IDE_STATUS_DRQ)) + { + /* + * This status indicates that the previous command hasn't been completed + * and the device is left in an unexpected state. + * A device reset is required to recover. + */ + WARN("CH %lu: Device %lu is busy %02x\n", ChanData->Channel, DeviceNumber, IdeStatus); + return CONN_STATUS_FAILURE; + } + + /* + * Also we do not check the device signature, + * because early ATAPI drives (NEC CDR-260, and some other devices) + * report an ATA signature. + */ + return CONN_STATUS_DEV_UNKNOWN; +} + +ULONG +PataEnumerateChannel( + _In_ PVOID ChannelContext) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + + /* + * By default, we do not issue the software reset to detect the device signature, + * since attached devices may reset their current transfer mode. + * We need to know what transfer mode is actually set by BIOS in order to + * select any transfer mode correctly by our generic PATA chipset driver + * (PciIdeAcpiSetTransferMode() or PciIdeBiosSetTransferMode()). + */ + return PataChannelGetMaximumDeviceCount(ChanData); +} diff --git a/drivers/storage/ide/pciidex/chipset/pata_io.c b/drivers/storage/ide/pciidex/chipset/pata_io.c new file mode 100644 index 00000000000..7ac65ff0065 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/pata_io.c @@ -0,0 +1,1218 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: PATA I/O request handling + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* FUNCTIONS ******************************************************************/ + +VOID +AtaWriteBlock16( + _In_ PUSHORT Port, + _In_ PUSHORT Buffer, + _In_ ULONG Count, + _In_ ULONG MmioFlags) +{ + ASSERT(Buffer != NULL); + ASSERT(Count != 0); + + if (MmioFlags) + WRITE_REGISTER_BUFFER_USHORT(Port, Buffer, Count); + else + WRITE_PORT_BUFFER_USHORT(Port, Buffer, Count); +} + +VOID +AtaReadBlock16( + _In_ PUSHORT Port, + _In_ PUSHORT Buffer, + _In_ ULONG Count, + _In_ ULONG MmioFlags) +{ + ASSERT(Buffer != NULL); + ASSERT(Count != 0); + + if (MmioFlags) + READ_REGISTER_BUFFER_USHORT(Port, Buffer, Count); + else + READ_PORT_BUFFER_USHORT(Port, Buffer, Count); +} + +VOID +AtaWriteBlock32( + _In_ PULONG Port, + _In_ PULONG Buffer, + _In_ ULONG Count, + _In_ ULONG MmioFlags) +{ + ASSERT(Buffer != NULL); + ASSERT(Count != 0); + + if (MmioFlags) + WRITE_REGISTER_BUFFER_ULONG(Port, Buffer, Count); + else + WRITE_PORT_BUFFER_ULONG(Port, Buffer, Count); +} + +VOID +AtaReadBlock32( + _In_ PULONG Port, + _In_ PULONG Buffer, + _In_ ULONG Count, + _In_ ULONG MmioFlags) +{ + ASSERT(Buffer != NULL); + ASSERT(Count != 0); + + if (MmioFlags) + READ_REGISTER_BUFFER_ULONG(Port, Buffer, Count); + else + READ_PORT_BUFFER_ULONG(Port, Buffer, Count); +} + +UCHAR +AtaReadPortUchar( + _In_ PUCHAR Port, + _In_ ULONG MmioFlags) +{ + if (MmioFlags) + return READ_REGISTER_UCHAR(Port); + else + return READ_PORT_UCHAR(Port); +} + +VOID +AtaWritePortUchar( + _In_ PUCHAR Port, + _In_ UCHAR Value, + _In_ ULONG MmioFlags) +{ + if (MmioFlags) + WRITE_REGISTER_UCHAR(Port, Value); + else + WRITE_PORT_UCHAR(Port, Value); +} + +VOID +AtaWritePortUlong( + _In_ PULONG Port, + _In_ ULONG Value, + _In_ ULONG MmioFlags) +{ + if (MmioFlags) + WRITE_REGISTER_ULONG(Port, Value); + else + WRITE_PORT_ULONG(Port, Value); +} + +VOID +PataSaveTaskFile( + _In_ PCHANNEL_DATA_PATA ChanData, + _Inout_ PATA_DEVICE_REQUEST Request) +{ + PATA_TASKFILE TaskFile = &Request->Output; + + TaskFile->Error = ATA_READ(ChanData->Regs.Error, ChanData, MRES_TF); + TaskFile->SectorCount = ATA_READ(ChanData->Regs.SectorCount, ChanData, MRES_TF); + TaskFile->LowLba = ATA_READ(ChanData->Regs.LbaLow, ChanData, MRES_TF); + TaskFile->MidLba = ATA_READ(ChanData->Regs.LbaMid, ChanData, MRES_TF); + TaskFile->HighLba = ATA_READ(ChanData->Regs.LbaHigh, ChanData, MRES_TF); + TaskFile->DriveSelect = ATA_READ(ChanData->Regs.Device, ChanData, MRES_TF); + TaskFile->Command = ATA_READ(ChanData->Regs.Command, ChanData, MRES_TF); + + if (Request->Flags & REQUEST_FLAG_LBA48) + { + UCHAR Control = ATA_READ(ChanData->Regs.Control, ChanData, MRES_CTRL) | IDE_DC_ALWAYS; + + /* Read the extra information from the second byte of FIFO */ + ATA_WRITE(ChanData->Regs.Control, Control | IDE_HIGH_ORDER_BYTE, ChanData, MRES_CTRL); + + TaskFile->FeatureEx = ATA_READ(ChanData->Regs.Features, ChanData, MRES_TF); + TaskFile->SectorCountEx = ATA_READ(ChanData->Regs.SectorCount, ChanData, MRES_TF); + TaskFile->LowLbaEx = ATA_READ(ChanData->Regs.LbaLow, ChanData, MRES_TF); + TaskFile->MidLbaEx = ATA_READ(ChanData->Regs.LbaMid, ChanData, MRES_TF); + TaskFile->HighLbaEx = ATA_READ(ChanData->Regs.LbaHigh, ChanData, MRES_TF); + + ATA_WRITE(ChanData->Regs.Control, Control, ChanData, MRES_CTRL); + } +} + +UCHAR +PataReadStatus( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + return ATA_READ(ChanData->Regs.Status, ChanData, MRES_TF); +} + +static +VOID +PataCompleteCommand( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ UCHAR IdeStatus, + _In_ UCHAR SrbStatus) +{ + PATA_DEVICE_REQUEST Request = ChanData->Slots[PATA_CHANNEL_SLOT]; + + Request->SrbStatus = SrbStatus; + + /* Handle failed commands */ + if (SrbStatus != SRB_STATUS_SUCCESS && SrbStatus != SRB_STATUS_DATA_OVERRUN) + { + if (!(Request->Device->TransportFlags & DEVICE_IS_ATAPI) || + (Request->Flags & REQUEST_FLAG_SAVE_TASK_FILE)) + { + /* Save the current task file for the "ATA LBA field" (SAT-6 11.7) */ + ChanData->SaveTaskFile(ChanData, Request); + Request->Flags |= REQUEST_FLAG_HAS_TASK_FILE; + } + else + { + Request->Output.Status = IdeStatus; + Request->Output.Error = ATA_READ(ChanData->Regs.Error, ChanData, MRES_TF); + } + + /* Request arbitration from the port worker */ + ChanData->PortNotification(AtaRequestFailed, ChanData->PortContext, Request); + return; + } + + ChanData->ActiveSlotsBitmap &= ~(1 << PATA_CHANNEL_SLOT); + + /* Save the latest copy of the task file registers */ + if (Request->Flags & REQUEST_FLAG_SAVE_TASK_FILE) + { + ChanData->SaveTaskFile(ChanData, Request); + Request->Flags |= REQUEST_FLAG_HAS_TASK_FILE; + } + + ChanData->PortNotification(AtaRequestComplete, ChanData->PortContext, 1 << PATA_CHANNEL_SLOT); +} + +static +inline +UCHAR +PciIdeDmaReadStatus( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + return ATA_READ(ChanData->Regs.Dma + PCIIDE_DMA_STATUS, ChanData, MRES_DMA); +} + +static +VOID +PciIdeDmaPrepare( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + PUCHAR IoBase = ChanData->Regs.Dma; + + /* Clear the interrupt and error bits in the status register */ + ATA_WRITE(IoBase + PCIIDE_DMA_COMMAND, + PCIIDE_DMA_COMMAND_STOP, ChanData, MRES_DMA); + ATA_WRITE(IoBase + PCIIDE_DMA_STATUS, + PCIIDE_DMA_STATUS_INTERRUPT | PCIIDE_DMA_STATUS_ERROR, ChanData, MRES_DMA); + + /* Set the address to the beginning of the physical region descriptor table */ + ATA_WRITE_ULONG((PULONG)(IoBase + PCIIDE_DMA_PRDT_PHYSICAL_ADDRESS), + ChanData->PrdTablePhysicalAddress, + ChanData, + MRES_DMA); +} + +static +VOID +PciIdeDmaStart( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ PATA_DEVICE_REQUEST Request) +{ + UCHAR Command; + + ASSERT(Request->Flags & REQUEST_FLAG_HAS_SG_LIST); + + /* Transfer direction */ + if (Request->Flags & REQUEST_FLAG_DATA_IN) + Command = PCIIDE_DMA_COMMAND_WRITE_TO_SYSTEM_MEMORY; + else + Command = PCIIDE_DMA_COMMAND_READ_FROM_SYSTEM_MEMORY; + Command |= PCIIDE_DMA_COMMAND_START; + + /* Begin transaction */ + ATA_WRITE(ChanData->Regs.Dma + PCIIDE_DMA_COMMAND, Command, ChanData, MRES_DMA); +} + +VOID +PciIdeDmaStop( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + PUCHAR IoBase = ChanData->Regs.Dma; + + /* Wait one PIO transfer cycle */ + (VOID)ATA_READ(IoBase + PCIIDE_DMA_STATUS, ChanData, MRES_DMA); + + /* Clear the interrupt bit in the status register */ + ATA_WRITE(IoBase + PCIIDE_DMA_COMMAND, PCIIDE_DMA_COMMAND_STOP, ChanData, MRES_DMA); + ATA_WRITE(IoBase + PCIIDE_DMA_STATUS, PCIIDE_DMA_STATUS_INTERRUPT, ChanData, MRES_DMA); +} + +static +VOID +PciIdeDmaClearInterrupt( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ UCHAR DmaStatus) +{ + ATA_WRITE(ChanData->Regs.Dma + PCIIDE_DMA_STATUS, DmaStatus, ChanData, MRES_DMA); +} + +static +VOID +PataChangeInterruptMode( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ PATA_IO_CONTEXT_COMMON Device, + _In_ PATA_DEVICE_REQUEST Request) +{ + UCHAR Control; + + if (Request->Flags & REQUEST_FLAG_POLL) + { + ChanData->IsPollingActive = TRUE; + Control = IDE_DC_DISABLE_INTERRUPTS; + } + else + { + ChanData->IsPollingActive = FALSE; + Control = 0; + } + TRACE("Interrupt mode %s\n", Control ? "disable" : "enable"); + + ATA_WRITE(ChanData->Regs.Control, Control | IDE_DC_ALWAYS, ChanData, MRES_CTRL); + + /* Seems to be needed */ + ATA_IO_WAIT(); + + /* Workaround for VIA chipsets that reset the DEV bit after interrupt mode change */ + if (ChanData->Controller->Pci.VendorID == PCI_VEN_VIA) + { + ATA_SELECT_DEVICE(ChanData, DEV_NUMBER(Device), Device->DeviceSelect); + ATA_WAIT(ChanData, ATA_TIME_BUSY_SELECT, IDE_STATUS_BUSY, 0); + } +} + +static +VOID +PataSendCdb( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ PATA_DEVICE_REQUEST Request) +{ + /* Command packet transfer */ + if (ChanData->ChanInfo & CHANNEL_FLAG_IO32) + { + ATA_WRITE_BLOCK_32((PULONG)ChanData->Regs.Data, + (PULONG)Request->Cdb, + Request->Device->CdbSize / sizeof(USHORT), + ChanData, + MRES_TF); + } + else + { + ATA_WRITE_BLOCK_16((PUSHORT)ChanData->Regs.Data, + (PUSHORT)Request->Cdb, + Request->Device->CdbSize, + ChanData, + MRES_TF); + } +} + +static +VOID +PataPioDataIn( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ ULONG ByteCount) +{ + ByteCount = min(ByteCount, ChanData->BytesToTransfer); + + TRACE("Read block %lu %lu %p\n", + ByteCount, ChanData->BytesToTransfer, ChanData->DataBuffer); + + /* Transfer the data block */ + if (!(ByteCount & (sizeof(ULONG) - 1)) && + (ChanData->ChanInfo & CHANNEL_FLAG_IO32)) + { + ATA_READ_BLOCK_32((PULONG)ChanData->Regs.Data, + (PULONG)ChanData->DataBuffer, + ByteCount / sizeof(ULONG), + ChanData, + MRES_TF); + } + else + { + ATA_READ_BLOCK_16((PUSHORT)ChanData->Regs.Data, + (PUSHORT)ChanData->DataBuffer, + ByteCount / sizeof(USHORT), + ChanData, + MRES_TF); + + /* Read one last byte */ + if (ByteCount & (sizeof(USHORT) - 1)) + { + DECLSPEC_ALIGN(2) UCHAR LocalBuffer[2]; + PUCHAR BufferEnd; + + /* Some hardware (Intel SCH) always expect 16- or 32-bit accesses for the data port */ + ATA_READ_BLOCK_16((PUSHORT)ChanData->Regs.Data, + (PUSHORT)LocalBuffer, + 1, + ChanData, + MRES_TF); + + BufferEnd = ChanData->DataBuffer + ByteCount - 1; + *BufferEnd = LocalBuffer[0]; + } + } + + ChanData->DataBuffer += ByteCount; + ChanData->BytesToTransfer -= ByteCount; +} + +static +VOID +PataPioDataOut( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ ULONG ByteCount) +{ + ByteCount = min(ByteCount, ChanData->BytesToTransfer); + + TRACE("Write block %lu %lu %p\n", + ByteCount, ChanData->BytesToTransfer, ChanData->DataBuffer); + + /* Transfer the data block */ + if (!(ByteCount & (sizeof(ULONG) - 1)) && + (ChanData->ChanInfo & CHANNEL_FLAG_IO32)) + { + ATA_WRITE_BLOCK_32((PULONG)ChanData->Regs.Data, + (PULONG)ChanData->DataBuffer, + ByteCount / sizeof(ULONG), + ChanData, + MRES_TF); + } + else + { + ATA_WRITE_BLOCK_16((PUSHORT)ChanData->Regs.Data, + (PUSHORT)ChanData->DataBuffer, + ByteCount / sizeof(USHORT), + ChanData, + MRES_TF); + + /* Write one last byte */ + if (ByteCount & (sizeof(USHORT) - 1)) + { + DECLSPEC_ALIGN(2) UCHAR LocalBuffer[2]; + PUCHAR BufferEnd; + + BufferEnd = ChanData->DataBuffer + ByteCount - 1; + LocalBuffer[0] = *BufferEnd; + LocalBuffer[1] = 0; + + ATA_WRITE_BLOCK_16((PUSHORT)ChanData->Regs.Data, + (PUSHORT)LocalBuffer, + 1, + ChanData, + MRES_TF); + } + } + + ChanData->DataBuffer += ByteCount; + ChanData->BytesToTransfer -= ByteCount; +} + +static +UCHAR +PataProcessAtapiRequest( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ PATA_DEVICE_REQUEST Request, + _In_ UCHAR IdeStatus) +{ + UCHAR SrbStatus, InterruptReason; + ULONG ByteCount; + + InterruptReason = ATA_READ(ChanData->Regs.InterruptReason, ChanData, MRES_TF); + InterruptReason &= ATAPI_INT_REASON_MASK; + InterruptReason |= IdeStatus & IDE_STATUS_DRQ; + + /* + * The NEC CDR-C251 drive is not fully ATAPI-compliant + * and clears BSY before raising the DRQ bit and updating the interrupt reason register. + * As a workaround, we will wait a bit more in the case the valid IR is not quite there yet. + */ + if ((InterruptReason == ATAPI_INT_REASON_COD) || (InterruptReason == ATAPI_INT_REASON_IO)) + { +#if DBG + static SIZE_T WarningsGiven = 0; + if (WarningsGiven++ < 2) + WARN("Not fully compliant ATAPI device %02x %02x\n", IdeStatus, InterruptReason); +#endif + IdeStatus = ATA_WAIT(ChanData, ATA_TIME_DRQ_ASSERT, IDE_STATUS_DRQ, IDE_STATUS_DRQ); + + InterruptReason = ATA_READ(ChanData->Regs.InterruptReason, ChanData, MRES_TF); + InterruptReason &= ATAPI_INT_REASON_MASK; + InterruptReason |= IdeStatus & IDE_STATUS_DRQ; + } + + switch (InterruptReason) + { + case ATAPI_INT_REASON_AWAIT_CDB: + { + if (!(ChanData->CommandFlags & CMD_FLAG_AWAIT_CDB)) + { + WARN("Invalid interrupt reason %02x %02x, flags %08lx\n", + InterruptReason, + IdeStatus, + ChanData->CommandFlags); + + SrbStatus = SRB_STATUS_BUS_RESET; + break; + } + ChanData->CommandFlags &= ~CMD_FLAG_AWAIT_CDB; + + PataSendCdb(ChanData, Request); + + /* Start the DMA engine */ + if (Request->Flags & REQUEST_FLAG_DMA) + { + ChanData->CommandFlags |= CMD_FLAG_DMA_TRANSFER; + PciIdeDmaStart(ChanData, Request); + } + + SrbStatus = SRB_STATUS_PENDING; + break; + } + + case ATAPI_INT_REASON_DATA_IN: + { + if (ChanData->CommandFlags & (CMD_FLAG_DATA_OUT | CMD_FLAG_AWAIT_CDB)) + { + WARN("Invalid interrupt reason %02x %02x, flags %08lx\n", + InterruptReason, + IdeStatus, + ChanData->CommandFlags); + + SrbStatus = SRB_STATUS_BUS_RESET; + break; + } + + ByteCount = ATA_READ(ChanData->Regs.ByteCountLow, ChanData, MRES_TF); + ByteCount |= ATA_READ(ChanData->Regs.ByteCountHigh, ChanData, MRES_TF) << 8; + + PataPioDataIn(ChanData, ByteCount); + SrbStatus = SRB_STATUS_PENDING; + break; + } + + case ATAPI_INT_REASON_DATA_OUT: + { + if (ChanData->CommandFlags & (CMD_FLAG_DATA_IN | CMD_FLAG_AWAIT_CDB)) + { + WARN("Invalid interrupt reason %02x %02x, flags %08lx\n", + InterruptReason, + IdeStatus, + ChanData->CommandFlags); + + SrbStatus = SRB_STATUS_BUS_RESET; + break; + } + + ByteCount = ATA_READ(ChanData->Regs.ByteCountLow, ChanData, MRES_TF); + ByteCount |= ATA_READ(ChanData->Regs.ByteCountHigh, ChanData, MRES_TF) << 8; + + PataPioDataOut(ChanData, ByteCount); + SrbStatus = SRB_STATUS_PENDING; + break; + } + + case ATAPI_INT_REASON_STATUS_NEC: + { + /* The NEC CDR-260 drive always clears CoD and IO on command completion */ + if (!(Request->Device->TransportFlags & DEVICE_IS_NEC_CDR260)) + { + WARN("Invalid interrupt reason %02x %02x, flags %08lx\n", + InterruptReason, + IdeStatus, + ChanData->CommandFlags); + + SrbStatus = SRB_STATUS_BUS_RESET; + break; + } + __fallthrough; + } + case ATAPI_INT_REASON_STATUS: + { + if (IdeStatus & (IDE_STATUS_ERROR | IDE_STATUS_DEVICE_FAULT)) + { + SrbStatus = SRB_STATUS_ERROR; + } + else if (ChanData->BytesToTransfer != 0) + { + /* This indicates a residual underrun */ + ASSERT(Request->DataTransferLength >= ChanData->BytesToTransfer); + Request->DataTransferLength -= ChanData->BytesToTransfer; + + SrbStatus = SRB_STATUS_DATA_OVERRUN; + } + else + { + SrbStatus = SRB_STATUS_SUCCESS; + } + break; + } + + default: + { + WARN("Invalid interrupt reason %02x %02x, flags %08lx\n", + InterruptReason, + IdeStatus, + ChanData->CommandFlags); + + SrbStatus = SRB_STATUS_BUS_RESET; + break; + } + } + + return SrbStatus; +} + +static +UCHAR +PataProcessAtaRequest( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ PATA_DEVICE_REQUEST Request, + _In_ UCHAR IdeStatus) +{ + /* Check for errors */ + if (IdeStatus & (IDE_STATUS_ERROR | IDE_STATUS_DEVICE_FAULT)) + { + if (IdeStatus & IDE_STATUS_DRQ) + { + ERR("DRQ not cleared, status 0x%02x\n", IdeStatus); + return SRB_STATUS_BUS_RESET; + } + else + { + return SRB_STATUS_ERROR; + } + } + + /* Non-data ATA command */ + if (!(ChanData->CommandFlags & (CMD_FLAG_DATA_IN | CMD_FLAG_DATA_OUT))) + { + // HACK: NP21/W IDE emulation bug, needs to be fixed on the emulator side + if (IsNEC_98) + IdeStatus &= ~IDE_STATUS_DRQ; + + if (IdeStatus & IDE_STATUS_DRQ) + { + ERR("DRQ not cleared, status 0x%02x\n", IdeStatus); + return SRB_STATUS_BUS_RESET; + } + else + { + return SRB_STATUS_SUCCESS; + } + } + + /* Read command */ + if (ChanData->CommandFlags & CMD_FLAG_DATA_IN) + { + if (!(IdeStatus & IDE_STATUS_DRQ)) + { + if ((IdeStatus == 0) && + ((Request->TaskFile.Command == IDE_COMMAND_ATAPI_IDENTIFY) || + (Request->TaskFile.Command == IDE_COMMAND_IDENTIFY))) + { + /* + * Some controllers indicate status 0x00 + * when the selected device does not exist, + * no point in going further. + */ + return SRB_STATUS_ERROR; + } + else + { +#if DBG + static SIZE_T WarningsGiven = 0; + if (WarningsGiven++ < 2) + WARN("Not fully compliant ATA device %02x\n", IdeStatus); +#endif + + /* + * The NEC CDR-C251 drive clears BSY before raising the DRQ bit + * while processing the ATAPI identify command. + * Give the device a chance to assert that bit. + */ + IdeStatus = ATA_WAIT(ChanData, + ATA_TIME_DRQ_ASSERT, + IDE_STATUS_DRQ, + IDE_STATUS_DRQ); + if (!(IdeStatus & IDE_STATUS_DRQ)) + { + + ERR("DRQ not set, status 0x%02x\n", IdeStatus); + return SRB_STATUS_BUS_RESET; + } + } + } + + /* Read the next data block */ + PataPioDataIn(ChanData, ChanData->DrqByteCount); + + if (ChanData->BytesToTransfer != 0) + return SRB_STATUS_PENDING; + + /* All data has been transferred, wait for DRQ to clear */ + IdeStatus = ATA_WAIT(ChanData, + ATA_TIME_DRQ_CLEAR, + IDE_STATUS_BUSY | IDE_STATUS_DRQ, + 0); + if (IdeStatus & (IDE_STATUS_BUSY | IDE_STATUS_DRQ)) + { + ERR("DRQ not cleared, status 0x%02x\n", IdeStatus); + return SRB_STATUS_BUS_RESET; + } + + if (IdeStatus & (IDE_STATUS_ERROR | IDE_STATUS_DEVICE_FAULT)) + return SRB_STATUS_ERROR; + + return SRB_STATUS_SUCCESS; + } + + /* Write command */ + if (ChanData->BytesToTransfer == 0) + { + if (IdeStatus & IDE_STATUS_DRQ) + { + ERR("DRQ not cleared, status 0x%02x\n", IdeStatus); + return SRB_STATUS_BUS_RESET; + } + else + { + return SRB_STATUS_SUCCESS; + } + } + + if (!(IdeStatus & IDE_STATUS_DRQ)) + { + ERR("DRQ not set, status 0x%02x %lu/%lu\n", + IdeStatus, + ChanData->BytesToTransfer, + Request->DataTransferLength); + return SRB_STATUS_BUS_RESET; + } + + /* Write the next data block */ + PataPioDataOut(ChanData, ChanData->DrqByteCount); + return SRB_STATUS_PENDING; +} + +static +BOOLEAN +PataProcessRequest( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ UCHAR IdeStatus, + _In_ UCHAR DmaStatus) +{ + PATA_DEVICE_REQUEST Request = ChanData->Slots[PATA_CHANNEL_SLOT]; + UCHAR SrbStatus; + + ASSERT(!(IdeStatus & IDE_STATUS_BUSY)); + + TRACE("%u Status 0x%02x, DMA 0x%02x, REQ 0x%lx\n", + DEV_NUMBER(Request->Device), + IdeStatus, + DmaStatus, + ChanData->CommandFlags); + + switch (ChanData->CommandFlags & CMD_FLAG_TRANSFER_MASK) + { + case CMD_FLAG_DMA_TRANSFER: + { + if ((DmaStatus & PCIIDE_DMA_STATUS_ERROR) || + (IdeStatus & (IDE_STATUS_ERROR | IDE_STATUS_DEVICE_FAULT))) + { + SrbStatus = SRB_STATUS_ERROR; + } + else + { + SrbStatus = SRB_STATUS_SUCCESS; + } + break; + } + + case CMD_FLAG_ATA_PIO_TRANSFER: + { + SrbStatus = PataProcessAtaRequest(ChanData, Request, IdeStatus); + break; + } + + case CMD_FLAG_ATAPI_PIO_TRANSFER: + { + SrbStatus = PataProcessAtapiRequest(ChanData, Request, IdeStatus); + break; + } + + default: + { + ASSERT(FALSE); + UNREACHABLE; + } + } + + if (SrbStatus == SRB_STATUS_PENDING) + return FALSE; + + PataCompleteCommand(ChanData, IdeStatus, SrbStatus); + return TRUE; +} + +VOID +PataLoadTaskFile( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ PATA_DEVICE_REQUEST Request) +{ + if (Request->Flags & REQUEST_FLAG_LBA48) + { + ATA_WRITE(ChanData->Regs.Features, Request->TaskFile.FeatureEx, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.SectorCount, Request->TaskFile.SectorCountEx, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.LbaLow, Request->TaskFile.LowLbaEx, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.LbaMid, Request->TaskFile.MidLbaEx, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.LbaHigh, Request->TaskFile.HighLbaEx, ChanData, MRES_TF); + + /* Store the extra information in the second byte of FIFO: */ + } + ATA_WRITE(ChanData->Regs.Features, Request->TaskFile.Feature, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.SectorCount, Request->TaskFile.SectorCount, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.LbaLow, Request->TaskFile.LowLba, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.LbaMid, Request->TaskFile.MidLba, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.LbaHigh, Request->TaskFile.HighLba, ChanData, MRES_TF); + + if (Request->Flags & REQUEST_FLAG_SET_DEVICE_REGISTER) + { + ATA_WRITE(ChanData->Regs.Device, Request->TaskFile.DriveSelect, ChanData, MRES_TF); + } +} + +static +BOOLEAN +PataExecutePacketCommand( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ PATA_IO_CONTEXT_COMMON Device, + _In_ PATA_DEVICE_REQUEST Request) +{ + ULONG CommandFlags; + USHORT ByteCount; + UCHAR Features; + + CommandFlags = (Request->Flags & (REQUEST_FLAG_DATA_IN | REQUEST_FLAG_DATA_OUT)); + CommandFlags |= ~Request->Flags & REQUEST_FLAG_POLL; + CommandFlags |= CMD_FLAG_ATAPI_PIO_TRANSFER | CMD_FLAG_AWAIT_CDB; + ChanData->CommandFlags = CommandFlags; + + /* Prepare to transfer a device command */ + if (Request->Flags & REQUEST_FLAG_DMA) + { + /* DMA transfer */ + ByteCount = 0; + Features = IDE_FEATURE_DMA; + } + else + { + /* PIO transfer */ + ByteCount = min(ChanData->BytesToTransfer, ATAPI_MAX_DRQ_DATA_BLOCK); + Features = IDE_FEATURE_PIO; + } + ATA_WRITE(ChanData->Regs.ByteCountLow, (UCHAR)(ByteCount >> 0), ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.ByteCountHigh, (UCHAR)(ByteCount >> 8), ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.Features, Features, ChanData, MRES_TF); + ATA_WRITE(ChanData->Regs.Command, IDE_COMMAND_ATAPI_PACKET, ChanData, MRES_TF); + + /* Wait for an interrupt that signals that device is ready to accept the command packet */ + if (Device->TransportFlags & DEVICE_HAS_CDB_INTERRUPT) + return FALSE; + + return TRUE; +} + +static +BOOLEAN +PataExecuteAtaCommand( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ PATA_IO_CONTEXT_COMMON Device, + _In_ PATA_DEVICE_REQUEST Request) +{ + ULONG CommandFlags, BlockCount; + + if ((ChanData->ChanInfo & CHANNEL_FLAG_DMA_BEFORE_CMD) && (Request->Flags & REQUEST_FLAG_DMA)) + PciIdeDmaStart(ChanData, Request); + + ChanData->LoadTaskFile(ChanData, Request); + + ATA_WRITE(ChanData->Regs.Command, Request->TaskFile.Command, ChanData, MRES_TF); + + CommandFlags = (Request->Flags & (REQUEST_FLAG_DATA_IN | REQUEST_FLAG_DATA_OUT)); + CommandFlags |= ~Request->Flags & REQUEST_FLAG_POLL; + + /* DMA transfer */ + if (Request->Flags & REQUEST_FLAG_DMA) + { + ChanData->CommandFlags = CommandFlags | CMD_FLAG_DMA_TRANSFER; + + /* Start the DMA engine */ + PciIdeDmaStart(ChanData, Request); + return FALSE; + } + + /* PIO transfer */ + ChanData->CommandFlags = CommandFlags | CMD_FLAG_ATA_PIO_TRANSFER; + + /* Set the byte count per DRQ data block */ + if (Request->Flags & REQUEST_FLAG_READ_WRITE_MULTIPLE) + BlockCount = Device->MultiSectorCount; + else + BlockCount = 1; + ChanData->DrqByteCount = BlockCount * Device->SectorSize; + + /* + * For PIO writes we need to send the first data block + * to make the device start generating interrupts. + */ + if (Request->Flags & REQUEST_FLAG_DATA_OUT) + return TRUE; + + return FALSE; +} + +BOOLEAN +PataStartIo( + _In_ PVOID ChannelContext, + _In_ PATA_DEVICE_REQUEST Request) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + PATA_IO_CONTEXT_COMMON Device = Request->Device; + UCHAR IdeStatus; + BOOLEAN IsPollingNeeded; + + ChanData->ActiveSlotsBitmap |= 1 << PATA_CHANNEL_SLOT; + + /* Select the device */ + ATA_SELECT_DEVICE(ChanData, DEV_NUMBER(Device), Device->DeviceSelect); + + /* Wait for busy to clear */ + IdeStatus = ATA_WAIT(ChanData, ATA_TIME_BUSY_SELECT, IDE_STATUS_BUSY, 0); + if (IdeStatus & IDE_STATUS_BUSY) + { + WARN("Device is busy 0x%02x\n", IdeStatus); + PataCompleteCommand(ChanData, IdeStatus, SRB_STATUS_SELECTION_TIMEOUT); + return TRUE; + } + + /* Disable interrupts for the polled commands */ + if (!!(Request->Flags & REQUEST_FLAG_POLL) != ChanData->IsPollingActive) + { + PataChangeInterruptMode(ChanData, Device, Request); + } + + /* Execute the command */ + if (Request->Flags & REQUEST_FLAG_PACKET_COMMAND) + IsPollingNeeded = PataExecutePacketCommand(ChanData, Device, Request); + else + IsPollingNeeded = PataExecuteAtaCommand(ChanData, Device, Request); + + if (Request->Flags & REQUEST_FLAG_POLL) + { + /* Queue a DPC to poll for the command */ + KeInsertQueueDpc(&ChanData->PollingTimerDpc, NULL, NULL); + return FALSE; + } + + ChanData->CommandFlags |= CMD_FLAG_AWAIT_INTERRUPT; + + if (!IsPollingNeeded) + return FALSE; + + /* Need to wait for a valid status */ + ATA_IO_WAIT(); + + /* Wait for busy to clear */ + IdeStatus = ATA_WAIT(ChanData, ATA_TIME_BUSY_NORMAL, IDE_STATUS_BUSY, 0); + if (IdeStatus & IDE_STATUS_BUSY) + { + ERR("BSY timeout, status 0x%02x\n", IdeStatus); + PataCompleteCommand(ChanData, IdeStatus, SRB_STATUS_BUS_RESET); + return TRUE; + } + + return PataProcessRequest(ChanData, IdeStatus, 0); +} + +VOID +PataPrepareIo( + _In_ PVOID ChannelContext, + _In_ PATA_DEVICE_REQUEST Request) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + + /* + * HACK: In an emulated PC-98 system IDE interrupts are often lost. + * The reason for this problem is unknown (not the driver's fault). + * Always poll for the command completion as a workaround. + */ + if (IsNEC_98) + Request->Flags |= REQUEST_FLAG_POLL; + + ChanData->BytesToTransfer = Request->DataTransferLength; + ChanData->DataBuffer = Request->DataBuffer; +} + +VOID +PciIdePreparePrdTable( + _In_ PVOID ChannelContext, + _In_ PATA_DEVICE_REQUEST Request, + _In_ SCATTER_GATHER_LIST* __restrict SgList) +{ + PCHANNEL_DATA_PATA ChanData = ChannelContext; + PPCIIDE_PRD_TABLE_ENTRY PrdTableEntry; + ULONG i; + ULONG DescriptorNumber = 0; + + DBG_UNREFERENCED_LOCAL_VARIABLE(DescriptorNumber); + + ASSUME(SgList->NumberOfElements > 0); + + PrdTableEntry = ChanData->PrdTable; + + for (i = 0; i < SgList->NumberOfElements; ++i) + { + ULONG Address = SgList->Elements[i].Address.LowPart; + ULONG Length = SgList->Elements[i].Length; + + ASSUME(Length != 0); + + /* 32-bit DMA */ + ASSERT(SgList->Elements[i].Address.HighPart == 0); + + /* The address of memory region must be word aligned */ + ASSERT((Address & ATA_MIN_BUFFER_ALIGNMENT) == 0); + + while (Length > 0) + { + ULONG TransferLength; + + /* If the memory region would cross a 64 kB boundary, split it */ + TransferLength = PCIIDE_PRD_LIMIT - (Address & (PCIIDE_PRD_LIMIT - 1)); + TransferLength = min(TransferLength, Length); + + /* The byte count must be word aligned */ + ASSERT((TransferLength % sizeof(USHORT)) == 0); + + /* Make sure we do not write beyond the end of the PRD table */ + ASSERT(DescriptorNumber < ChanData->MaximumPhysicalPages); + DescriptorNumber++; + + PrdTableEntry->Address = Address; + PrdTableEntry->Length = TransferLength & PCIIDE_PRD_LENGTH_MASK; + + ++PrdTableEntry; + + Address += TransferLength; + Length -= TransferLength; + } + } + + /* The PRD table cannot cross a 64 kB boundary */ + ASSERT((DescriptorNumber * sizeof(*PrdTableEntry)) < PCIIDE_PRD_LIMIT); + + /* Last entry */ + --PrdTableEntry; + PrdTableEntry->Length |= PCIIDE_PRD_END_OF_TABLE; + + KeFlushIoBuffers(Request->Mdl, !!(Request->Flags & REQUEST_FLAG_DATA_IN), TRUE); + + PciIdeDmaPrepare(ChanData); +} + +BOOLEAN +PataAllocateSlot( + _In_ PVOID ChannelContext, + _In_ PATA_DEVICE_REQUEST Request, + _In_ BOOLEAN Allocate) +{ + UNREFERENCED_PARAMETER(ChannelContext); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(Allocate); + + return TRUE; +} + +BOOLEAN +NTAPI +PciIdeChannelIsr( + _In_ PKINTERRUPT Interrupt, + _In_ PVOID Context) +{ + PCHANNEL_DATA_PATA ChanData = Context; + UCHAR IdeStatus, DmaStatus; + BOOLEAN WasCleared; + + UNREFERENCED_PARAMETER(Interrupt); + + if (ChanData->CheckInterrupt && !(ChanData->CheckInterrupt(ChanData))) + return FALSE; + + DmaStatus = PciIdeDmaReadStatus(ChanData); + if (DmaStatus & PCIIDE_DMA_STATUS_INTERRUPT) + { + /* Always clear the DMA interrupt, even when the current command is a PIO command */ + if ((ChanData->CommandFlags & CMD_FLAG_TRANSFER_MASK) == CMD_FLAG_DMA_TRANSFER) + PciIdeDmaStop(ChanData); + else + PciIdeDmaClearInterrupt(ChanData, DmaStatus); + + WasCleared = TRUE; + } + else + { + if ((ChanData->CommandFlags & CMD_FLAG_TRANSFER_MASK) == CMD_FLAG_DMA_TRANSFER) + return FALSE; + + WasCleared = FALSE; + } + + /* Acknowledge the IDE interrupt */ + IdeStatus = ChanData->ReadStatus(ChanData); + + /* This interrupt is not ours */ + if (!(ChanData->ActiveSlotsBitmap & (1 << PATA_CHANNEL_SLOT)) || + !(ChanData->CommandFlags & CMD_FLAG_AWAIT_INTERRUPT) || + (IdeStatus & IDE_STATUS_BUSY)) + { + /* Do not log PCI shared interrupts */ + if (WasCleared) + { + WARN("Spurious bus-master interrupt %02x, %02x, flags %08lx\n", + IdeStatus, DmaStatus, ChanData->CommandFlags); + } + + return WasCleared; + } + + PataProcessRequest(ChanData, IdeStatus, DmaStatus); + return TRUE; +} + +BOOLEAN +NTAPI +PataChannelIsr( + _In_ PKINTERRUPT Interrupt, + _In_ PVOID Context) +{ + PCHANNEL_DATA_PATA ChanData = Context; + UCHAR IdeStatus; + + UNREFERENCED_PARAMETER(Interrupt); + + if (ChanData->CheckInterrupt && !(ChanData->CheckInterrupt(ChanData))) + return FALSE; + + /* Acknowledge the IDE interrupt */ + IdeStatus = ChanData->ReadStatus(ChanData); + + /* This interrupt is spurious or not ours */ + if (!(ChanData->ActiveSlotsBitmap & (1 << PATA_CHANNEL_SLOT)) || + !(ChanData->CommandFlags & CMD_FLAG_AWAIT_INTERRUPT) || + (IdeStatus & IDE_STATUS_BUSY)) + { + WARN("Spurious IDE interrupt %02x, flags %08lx\n", IdeStatus, ChanData->CommandFlags); + return FALSE; + } + + PataProcessRequest(ChanData, IdeStatus, 0); + return TRUE; +} + +BOOLEAN +PciIdeCheckInterrupt( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + UCHAR DmaStatus = PciIdeDmaReadStatus(ChanData); + + /* + * On some PATA controllers the DMA interrupt bit reflects the state + * of the IDE interrupt pin. Such controllers assert the DMA interrupt + * even if the current command is a PIO command. + * We use this capability for better handling of PCI shared interrupts. + */ + return !!(DmaStatus & PCIIDE_DMA_STATUS_INTERRUPT); +} + +static +BOOLEAN +PataPoll( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + BOOLEAN IsRequestCompleted = FALSE; + UCHAR IdeStatus; + + while (TRUE) + { + /* Need to wait for a valid status */ + ATA_IO_WAIT(); + + /* Do a quick check here for a busy */ + IdeStatus = ATA_WAIT(ChanData, ATA_TIME_BUSY_POLL, IDE_STATUS_BUSY, 0); + if (IdeStatus & IDE_STATUS_BUSY) + { + /* Device is still busy, schedule a timer to retry the poll */ + break; + } + + IsRequestCompleted = PataProcessRequest(ChanData, IdeStatus, 0); + if (IsRequestCompleted) + break; + } + + return IsRequestCompleted; +} + +VOID +NTAPI +PataPollingTimerDpc( + _In_ PKDPC Dpc, + _In_opt_ PVOID DeferredContext, + _In_opt_ PVOID SystemArgument1, + _In_opt_ PVOID SystemArgument2) +{ + PCHANNEL_DATA_PATA ChanData = DeferredContext; + KIRQL OldIrql; + BOOLEAN IsRequestCompleted; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument1); + UNREFERENCED_PARAMETER(SystemArgument2); + + OldIrql = KeAcquireInterruptSpinLock(ChanData->InterruptObject); + + if (!(ChanData->ActiveSlotsBitmap & (1 << PATA_CHANNEL_SLOT)) || + (ChanData->CommandFlags & CMD_FLAG_AWAIT_INTERRUPT)) + { + IsRequestCompleted = FALSE; + } + else + { + IsRequestCompleted = PataPoll(ChanData); + } + + KeReleaseInterruptSpinLock(ChanData->InterruptObject, OldIrql); + + if (!IsRequestCompleted) + KeInsertQueueDpc(&ChanData->PollingTimerDpc, NULL, NULL); +} diff --git a/drivers/storage/ide/pciidex/chipset/pata_legacy.c b/drivers/storage/ide/pciidex/chipset/pata_legacy.c new file mode 100644 index 00000000000..27d2aa4520f --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/pata_legacy.c @@ -0,0 +1,60 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Legacy (non-PnP) IDE controllers support + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* FUNCTIONS ******************************************************************/ + +CODE_SEG("PAGE") +NTSTATUS +PataGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller, + _In_ PCM_RESOURCE_LIST ResourcesTranslated) +{ + PCHANNEL_DATA_PATA ChanData; + NTSTATUS Status; + + PAGED_CODE(); + ASSERT(Controller->Flags & CTRL_FLAG_NON_PNP); + + Controller->MaxChannels = 1; + Controller->ChannelBitmap = 0x1; + Controller->Flags &= ~CTRL_FLAG_NATIVE_PCI; + Controller->Flags |= CTRL_FLAG_MANUAL_RES; + + Status = PciIdeCreateChannelData(Controller, 0); + if (!NT_SUCCESS(Status)) + return Status; + + ChanData = Controller->Channels[0]; + ChanData->ChanInfo &= ~CHANNEL_FLAG_IO32; + ChanData->TransferModeSupported = PIO_ALL; + + Status = PciIdeParseResources(ChanData, ResourcesTranslated); + if (!NT_SUCCESS(Status)) + return Status; + +#if defined(_M_IX86) + if (ChanData->ChanInfo & CHANNEL_FLAG_CBUS) + { + UCHAR Value, Result; + + /* Check whether the 32-bit data port can be enabled on the southbridge */ + Value = READ_PORT_UCHAR((PUCHAR)PC98_ATA_BANK); + WRITE_PORT_UCHAR((PUCHAR)PC98_ATA_BANK, Value ^ PC98_ATA_BANK_32BIT_PORT); + Result = READ_PORT_UCHAR((PUCHAR)PC98_ATA_BANK); + if ((Result ^ Value) & PC98_ATA_BANK_32BIT_PORT) + { + ChanData->ChanInfo |= CHANNEL_FLAG_IO32; + } + } +#endif + + return STATUS_SUCCESS; +} diff --git a/drivers/storage/ide/pciidex/chipset/pctech.c b/drivers/storage/ide/pciidex/chipset/pctech.c new file mode 100644 index 00000000000..1e8da8ad791 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/pctech.c @@ -0,0 +1,67 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: PC-Tech RZ1000 PCI IDE controller minidriver + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* GLOBALS ********************************************************************/ + +#define PCI_DEV_RZ1000 0x1000 +#define PCI_DEV_RZ1001 0x1001 + +#define REG_CONFIG 0x40 +#define CONFIG_PREFETCH 0x2000 + +/* FUNCTIONS ******************************************************************/ + +/* + * Errata: Disable the Read-Ahead Mode. + * https://web.archive.org/web/19961222024423/http://www.intel.com/procs/support/rz1000/rztech.htm + * https://web.archive.org/web/20250324132451/https://www.mindprod.com/jgloss/eideflaw.html + */ +static +VOID +PcTechControllerStart( + _In_ PATA_CONTROLLER Controller) +{ + USHORT ConfigReg; + + ConfigReg = PciRead16(Controller, REG_CONFIG); + ConfigReg &= ~CONFIG_PREFETCH; + PciWrite16(Controller, REG_CONFIG, ConfigReg); +} + +CODE_SEG("PAGE") +NTSTATUS +PcTechGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + NTSTATUS Status; + ULONG i; + + PAGED_CODE(); + ASSERT(Controller->Pci.VendorID == PCI_VEN_PC_TECH); + + if (Controller->Pci.DeviceID != PCI_DEV_RZ1000 && Controller->Pci.DeviceID != PCI_DEV_RZ1001) + return STATUS_NO_MATCH; + + Status = PciIdeCreateChannelData(Controller, 0); + if (!NT_SUCCESS(Status)) + return Status; + + Controller->Start = PcTechControllerStart; + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + ChanData->ChanInfo &= ~CHANNEL_FLAG_IO32; + } + + return STATUS_SUCCESS; +} diff --git a/drivers/storage/ide/pciidex/chipset/sil680.c b/drivers/storage/ide/pciidex/chipset/sil680.c new file mode 100644 index 00000000000..e087473c973 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/sil680.c @@ -0,0 +1,249 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Silicon Image 0680 PCI IDE controller minidriver + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* GLOBALS ********************************************************************/ + +#define PCI_DEV_SIL0680 0x0680 + +#define SIL_REG_XFER_MODE(Channel) (0x80 + ((Channel) << 2)) +#define SIL_REG_SYS_CFG 0x8A +#define SIL_REG_CFG(Channel) (0xA0 + ((Channel) << 4)) +#define SIL_REG_STATUS(Channel) (0xA1 + ((Channel) << 4)) +#define SIL_REG_TF_TIM(Channel) (0xA2 + ((Channel) << 4)) +#define SIL_REG_PIO_TIMING(Channel, Drive) (0xA4 + ((Channel) << 4) + ((Drive) << 1)) +#define SIL_REG_DMA_TIMING(Channel, Drive) (0xA8 + ((Channel) << 4) + ((Drive) << 1)) +#define SIL_REG_UDMA_TIMING(Channel, Drive) (0xAC + ((Channel) << 4) + ((Drive) << 1)) + +#define SIL_CFG_CABLE_80 0x00000001 +#define SIL_CFG_MONITOR_IORDY 0x00000200 +#define SIL_STATUS_INTR 0x08 + +#define SIL_CLK_MASK 0x30 +#define SIL_CLK_100MHZ 0x00 +#define SIL_CLK_133MHZ 0x10 +#define SIL_CLK_X2 0x20 +#define SIL_CLK_DISABLED 0x30 + +static const USHORT Sil680TaskFileTimings[] = +{ + 0x328A, // 0 + 0x2283, // 1 + 0x1281, // 2 + 0x10C3, // 3 + 0x10C1 // 4 +}; + +static const USHORT Sil680PioTimings[] = +{ + 0x328A, // 0 + 0x2283, // 1 + 0x1104, // 2 + 0x10C3, // 3 + 0x10C1 // 4 +}; + +static const USHORT Sil680MwDmaTimings[] = +{ + 0x2208, // 0 + 0x10C2, // 1 + 0x10C1, // 2 +}; + +static const UCHAR Sil680UdmaTimings[2][7] = +{ + // 0 1 2 3 4 5 6 + { 0x0B, 0x07, 0x05, 0x04, 0x02, 0x01 }, // 100MHz + { 0x0F, 0x0B, 0x07, 0x05, 0x03, 0x02, 0x01 }, // 133MHz +}; + +/* FUNCTIONS ******************************************************************/ + +static +VOID +Sil680SetTransferMode( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + ULONG i, SlowestPioMode, Config; + UCHAR ModeReg; + BOOLEAN MonitorIoReady = FALSE; + + INFO("CH %lu: Config (before)\n" + "MODE %08lX\n" + "CFG %08lX\n" + "PIO %08lX\n" + "DMA %08lX\n" + "UDMA %08lX\n", + Channel, + PciRead32(Controller, SIL_REG_XFER_MODE(Channel)), + PciRead32(Controller, SIL_REG_CFG(Channel)), + PciRead32(Controller, SIL_REG_PIO_TIMING(Channel, 0)), + PciRead32(Controller, SIL_REG_DMA_TIMING(Channel, 0)), + PciRead32(Controller, SIL_REG_UDMA_TIMING(Channel, 0))); + + SlowestPioMode = UDMA_MODE(0); + ModeReg = PciRead8(Controller, SIL_REG_XFER_MODE(Channel)); + + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + UCHAR XferMode, SysConfig; + + if (!Device) + continue; + + /* PIO timings */ + SlowestPioMode = min(SlowestPioMode, Device->PioMode); + PciWrite16(Controller, SIL_REG_PIO_TIMING(Channel, i), Sil680PioTimings[Device->PioMode]); + + if (Device->PioMode >= PIO_MODE(3)) + MonitorIoReady = TRUE; + + if (Device->DmaMode >= UDMA_MODE(0)) + { + SysConfig = PciRead8(Controller, SIL_REG_SYS_CFG); + + /* Try to enable the 133MHz IDE clock */ + if ((SysConfig & SIL_CLK_MASK) == SIL_CLK_100MHZ) + { + PciWrite8(Controller, SIL_REG_SYS_CFG, SysConfig | SIL_CLK_133MHZ); + + SysConfig = PciRead8(Controller, SIL_REG_SYS_CFG); + } + + /* If we are unable to do so, skip UDMA6 selection */ + if ((Device->DmaMode == UDMA_MODE(6)) && + (SysConfig & SIL_CLK_MASK) == SIL_CLK_100MHZ) + { + ULONG SupportedModes = Device->SupportedModes & ~(PIO_ALL | UDMA_MODE6); + + if (!_BitScanReverse(&Device->DmaMode, SupportedModes)) + Device->DmaMode = PIO_MODE(0); + } + } + + /* UDMA timings */ + if (Device->DmaMode >= UDMA_MODE(0)) + { + ULONG ClockIndex = ((SysConfig & SIL_CLK_MASK) == SIL_CLK_100MHZ) ? 0 : 1; + USHORT UdmaTim; + + XferMode = 3; + + UdmaTim = PciRead16(Controller, SIL_REG_UDMA_TIMING(Channel, i)); + UdmaTim &= ~0x003F; + UdmaTim |= Sil680UdmaTimings[ClockIndex][Device->DmaMode - UDMA_MODE(0)]; + PciWrite16(Controller, SIL_REG_UDMA_TIMING(Channel, i), UdmaTim); + } + /* DMA timings */ + else if (Device->DmaMode != PIO_MODE(0)) + { + ULONG ModeIndex = Device->DmaMode - MWDMA_MODE(0); + + XferMode = 2; + + PciWrite16(Controller, SIL_REG_DMA_TIMING(Channel, i), Sil680MwDmaTimings[ModeIndex]); + } + else + { + if (Device->IoReadySupported) + XferMode = 1; + else + XferMode = 0; + } + + ModeReg &= ~(0x03 << (i * 4)); + ModeReg |= XferMode << (i * 4); + } + + /* Transfer mode */ + PciWrite8(Controller, SIL_REG_XFER_MODE(Channel), ModeReg); + + Config = PciRead32(Controller, SIL_REG_CFG(Channel)); + if (MonitorIoReady) + Config |= SIL_CFG_MONITOR_IORDY; + else + Config &= ~SIL_CFG_MONITOR_IORDY; + + /* Task file timings */ + if (SlowestPioMode != UDMA_MODE(0)) + { + Config &= ~0xFFFF0000; + Config |= Sil680TaskFileTimings[SlowestPioMode] << 16; + } + + PciWrite32(Controller, SIL_REG_CFG(Channel), Config); + + INFO("CH %lu: Config (after)\n" + "MODE %08lX\n" + "CFG %08lX\n" + "PIO %08lX\n" + "DMA %08lX\n" + "UDMA %08lX\n", + Channel, + PciRead32(Controller, SIL_REG_XFER_MODE(Channel)), + Config, + PciRead32(Controller, SIL_REG_PIO_TIMING(Channel, 0)), + PciRead32(Controller, SIL_REG_DMA_TIMING(Channel, 0)), + PciRead32(Controller, SIL_REG_UDMA_TIMING(Channel, 0))); +} + +static +BOOLEAN +Sil680CheckInterrupt( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + PATA_CONTROLLER Controller = ChanData->Controller; + UCHAR Status; + + Status = PciRead8(Controller, SIL_REG_STATUS(ChanData->Channel)); + return !!(Status & SIL_STATUS_INTR); +} + +CODE_SEG("PAGE") +NTSTATUS +Sil680GetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + NTSTATUS Status; + ULONG i; + + PAGED_CODE(); + ASSERT(Controller->Pci.VendorID == PCI_VEN_CMD); + + if (Controller->Pci.VendorID != PCI_DEV_SIL0680) + return STATUS_NO_MATCH; + + Status = PciIdeCreateChannelData(Controller, 0); + if (!NT_SUCCESS(Status)) + return Status; + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + UCHAR Config; + + ChanData->CheckInterrupt = Sil680CheckInterrupt; + ChanData->SetTransferMode = Sil680SetTransferMode; + ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 6); + + /* Check for 80-conductor cable */ + Config = PciRead8(Controller, SIL_REG_CFG(i)); + if (!(Config & SIL_CFG_CABLE_80)) + { + INFO("CH %lu: BIOS detected 40-conductor cable\n", ChanData->Channel); + ChanData->TransferModeSupported &= ~UDMA_80C_ALL; + } + } + + return STATUS_NOT_IMPLEMENTED; +} diff --git a/drivers/storage/ide/pciidex/chipset/svw_pata.c b/drivers/storage/ide/pciidex/chipset/svw_pata.c new file mode 100644 index 00000000000..bb1071bd182 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/svw_pata.c @@ -0,0 +1,361 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: BSD-2-Clause (https://spdx.org/licenses/BSD-2-Clause) + * PURPOSE: ServerWorks/Broadcom ATA controller minidriver + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* + * Adapted from the FreeBSD ata-serverworks driver + * Copyright (c) 1998-2008 Søren Schmidt + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* GLOBALS ********************************************************************/ + +#define PCI_DEV_OSB4_IDE 0x0211 +#define PCI_DEV_CSB5_IDE 0x0212 +#define PCI_DEV_CSB6_IDE 0x0213 +#define PCI_DEV_CSB6_IDE_THIRD 0x0217 +#define PCI_DEV_HT1000_IDE 0x0214 + +#define PCI_DEV_OSB4_BRIDGE 0x0210 + +#define SVW_REG_PIO_TIMING 0x40 +#define SVW_REG_DMA_TIMING 0x44 +#define SVW_REG_PIO_MODE 0x4A // CSB5 and later +#define SVW_REG_UDMA_ENABLE 0x54 +#define SVW_REG_UDMA_MODE 0x56 +#define SVW_REG_UDMA_CONTROL 0x5A // Absent on ATI + +#define SVW_UDMA_CTRL_MODE_MASK 0x03 +#define SVW_UDMA_CTRL_DISABLE 0x40 + +#define SVW_UDMA_CTRL_MODE_UDMA4 0x02 +#define SVW_UDMA_CTRL_MODE_UDMA5 0x03 + +static const struct +{ + UCHAR Value; + ULONG CycleTime; +} SvwPioTimings[] = +{ + { 0x5D, 600 }, // 0 + { 0x47, 390 }, // 1 + { 0x34, 270 }, // 2 + { 0x22, 180 }, // 3 + { 0x20, 120 }, // 4 +}; + +static const struct +{ + UCHAR Value; + ULONG CycleTime; +} SvwMwDmaTimings[] = +{ + { 0x77, 480 }, // 0 + { 0x21, 150 }, // 1 + { 0x20, 120 }, // 2 +}; + +/* FUNCTIONS ******************************************************************/ + +static +VOID +SvwChooseDeviceSpeed( + _In_ ULONG Channel, + _In_ PCHANNEL_DEVICE_CONFIG Device) +{ + ULONG Mode; + + /* PIO speed */ + for (Mode = Device->PioMode; Mode > PIO_MODE(0); Mode--) + { + if (!(Device->SupportedModes & (1 << Mode))) + continue; + + if (SvwPioTimings[Mode].CycleTime >= Device->MinPioCycleTime) + break; + } + if (Mode != Device->PioMode) + INFO("CH %lu: Downgrade PIO speed from %lu to %lu\n", Channel, Device->PioMode, Mode); + Device->PioMode = Mode; + + /* UDMA works independently of any PIO mode */ + if (Device->DmaMode == PIO_MODE(0) || Device->DmaMode >= UDMA_MODE(0)) + return; + + /* DMA speed */ + for (Mode = Device->DmaMode; Mode > PIO_MODE(0); Mode--) + { + if (!(Device->SupportedModes & ~PIO_ALL & (1 << Mode))) + continue; + + ASSERT((1 << Mode) & MWDMA_ALL); + + if (SvwMwDmaTimings[Mode - MWDMA_MODE(0)].CycleTime >= Device->MinMwDmaCycleTime) + break; + } + if (Mode != Device->DmaMode) + { + if (Mode == PIO_MODE(0)) + WARN("CH %lu: Too slow device '%s', disabling DMA\n", Channel, Device->FriendlyName); + else + INFO("CH %lu: Downgrade DMA speed from %lu to %lu\n", Channel, Device->DmaMode, Mode); + } + Device->DmaMode = Mode; +} + +VOID +SvwSetTransferMode( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + USHORT PioModeReg, UdmaModeReg; + UCHAR UdmaEnableReg; + ULONG i, PioTimReg, DmaTimReg; + + if (Controller->Pci.DeviceID != PCI_DEV_OSB4_IDE) + PioModeReg = PciRead16(Controller, SVW_REG_PIO_MODE); + else + PioModeReg = 0; + + PioTimReg = PciRead32(Controller, SVW_REG_PIO_TIMING); + DmaTimReg = PciRead32(Controller, SVW_REG_DMA_TIMING); + UdmaEnableReg = PciRead8(Controller, SVW_REG_UDMA_ENABLE); + UdmaModeReg = PciRead16(Controller, SVW_REG_UDMA_MODE); + + INFO("CH %lu: Config (before)\n" + "DMA Timing %08lX\n" + "PIO Timing %08lX\n" + "PIO Mode %04X\n" + "UDMA Mode %04X\n", + "UDMA Enable %02X\n", + Channel, + DmaTimReg, + PioTimReg, + PioModeReg, + UdmaModeReg, + UdmaEnableReg); + + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + const ULONG DeviceIndex = (Channel << 1) + i; // 0 1 2 3 + const ULONG Shift = (DeviceIndex ^ 1) << 3; // 8 0 24 16 + + UdmaModeReg &= ~(0x0F << (DeviceIndex << 2)); + UdmaEnableReg &= ~(0x01 << DeviceIndex); + + if (!Device) + continue; + + /* Make sure that the selected PIO and DMA speeds match the cycle time */ + SvwChooseDeviceSpeed(Channel, Device); + + /* UDMA timings */ + if (Device->DmaMode >= UDMA_MODE(0)) + { + ULONG ModeIndex = Device->DmaMode - UDMA_MODE(0); + + UdmaModeReg |= ModeIndex << (DeviceIndex << 2); + UdmaEnableReg |= 1 << DeviceIndex; + + DmaTimReg &= ~(0xFF << Shift); + DmaTimReg |= SvwMwDmaTimings[2].Value << Shift; + } + /* DMA timings */ + else if (Device->DmaMode != PIO_MODE(0)) + { + ULONG ModeIndex = Device->DmaMode - MWDMA_MODE(0); + + DmaTimReg &= ~(0xFF << Shift); + DmaTimReg |= SvwMwDmaTimings[ModeIndex].Value << Shift; + } + + /* PIO timings */ + PioModeReg &= ~(0x0F << (DeviceIndex << 2)); + PioModeReg |= Device->PioMode << (DeviceIndex << 2); + + PioTimReg &= ~(0xFF << Shift); + PioTimReg |= SvwPioTimings[Device->PioMode].Value << Shift; + } + + PciWrite32(Controller, SVW_REG_PIO_TIMING, PioTimReg); + + if (Controller->Pci.DeviceID != PCI_DEV_OSB4_IDE) + PciWrite16(Controller, SVW_REG_PIO_MODE, PioModeReg); + + PciWrite32(Controller, SVW_REG_DMA_TIMING, DmaTimReg); + PciWrite16(Controller, SVW_REG_UDMA_MODE, UdmaModeReg); + PciWrite8(Controller, SVW_REG_UDMA_ENABLE, UdmaEnableReg); + + INFO("CH %lu: Config (after)\n" + "DMA Timing %08lX\n" + "PIO Timing %08lX\n" + "PIO Mode %04X\n" + "UDMA Mode %04X\n", + "UDMA Enable %02X\n", + Channel, + DmaTimReg, + PioTimReg, + PioModeReg, + UdmaModeReg, + UdmaEnableReg); +} + +CODE_SEG("PAGE") +BOOLEAN +SvwHasUdmaCable( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel) +{ + UCHAR UdmaModeReg; + + PAGED_CODE(); + + UdmaModeReg = PciRead8(Controller, SVW_REG_UDMA_MODE + Channel); + + /* Check mode settings, see if UDMA3 or higher mode is active */ + return ((UdmaModeReg & 0x07) > 2) || ((UdmaModeReg & 0x70) > 0x20); +} + +static +BOOLEAN +SvwOsb4PciBridgeInit( + _In_ PVOID Context, + _In_ ULONG BusNumber, + _In_ PCI_SLOT_NUMBER PciSlot, + _In_ PPCI_COMMON_HEADER PciConfig) +{ + ULONG Value; + + UNREFERENCED_PARAMETER(Context); + + if (PciConfig->VendorID != PCI_VEN_SERVERWORKS || + PciConfig->DeviceID != PCI_DEV_OSB4_BRIDGE) + { + return FALSE; + } + + HalGetBusDataByOffset(PCIConfiguration, + BusNumber, + PciSlot.u.AsULONG, + &Value, + 0x64, + sizeof(Value)); + Value &= ~0x00002000; // Disable 600ns interrupt mask + Value |= 0x00004000; // Enable UDMA/33 support + HalSetBusDataByOffset(PCIConfiguration, + BusNumber, + PciSlot.u.AsULONG, + &Value, + 0x64, + sizeof(Value)); + return TRUE; +} + +static +VOID +SvwPataControllerStart( + _In_ PATA_CONTROLLER Controller) +{ + if (Controller->Pci.DeviceID == PCI_DEV_OSB4_IDE) + { + PciFindDevice(SvwOsb4PciBridgeInit, NULL); + } + else + { + PCHANNEL_DATA_COMMON ChanData = Controller->Channels[0]; + UCHAR UdmaCtrlReg = PciRead8(Controller, SVW_REG_UDMA_CONTROL); + + UdmaCtrlReg &= ~(SVW_UDMA_CTRL_DISABLE | SVW_UDMA_CTRL_MODE_MASK); + if (ChanData->TransferModeSupported & UDMA_MODE5) + UdmaCtrlReg |= SVW_UDMA_CTRL_MODE_UDMA5; + else + UdmaCtrlReg |= SVW_UDMA_CTRL_MODE_UDMA4; + + PciWrite8(Controller, SVW_REG_UDMA_CONTROL, UdmaCtrlReg); + } +} + +CODE_SEG("PAGE") +NTSTATUS +SvwPataGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + NTSTATUS Status; + ULONG i, SupportedMode; + BOOLEAN No64KDma = FALSE; + + PAGED_CODE(); + ASSERT(Controller->Pci.VendorID == PCI_VEN_SERVERWORKS); + + switch (Controller->Pci.DeviceID) + { + case PCI_DEV_OSB4_IDE: + /* UDMA is supported but its use is discouraged */ + SupportedMode = PIO_ALL | MWDMA_ALL; + + No64KDma = TRUE; + break; + + case PCI_DEV_CSB5_IDE: + if (Controller->Pci.RevisionID < 0x92) + SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 4); + else + SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 5); + Controller->Flags |= CTRL_FLAG_DMA_INTERRUPT; + break; + + case PCI_DEV_CSB6_IDE_THIRD: + SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 4); + Controller->MaxChannels = 1; + break; + + case PCI_DEV_CSB6_IDE: + SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 5); + break; + + case PCI_DEV_HT1000_IDE: + SupportedMode = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 5); + Controller->MaxChannels = 1; + No64KDma = TRUE; + break; + + default: + return STATUS_NO_MATCH; + } + + Status = PciIdeCreateChannelData(Controller, 0); + if (!NT_SUCCESS(Status)) + return Status; + + Controller->Start = SvwPataControllerStart; + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + if (No64KDma) + ChanData->MaximumTransferLength = PCIIDE_PRD_LIMIT - 0x4000; + + ChanData->SetTransferMode = SvwSetTransferMode; + ChanData->TransferModeSupported = SupportedMode; + + /* Check for 80-conductor cable */ + if ((ChanData->TransferModeSupported & UDMA_80C_ALL) && !SvwHasUdmaCable(Controller, i)) + { + INFO("CH %lu: BIOS hasn't selected mode faster than UDMA 2, " + "assume 40-conductor cable\n", + ChanData->Channel); + ChanData->TransferModeSupported &= ~UDMA_80C_ALL; + } + } + + return STATUS_SUCCESS; +} diff --git a/drivers/storage/ide/pciidex/chipset/svw_sata.c b/drivers/storage/ide/pciidex/chipset/svw_sata.c new file mode 100644 index 00000000000..1168e52b323 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/svw_sata.c @@ -0,0 +1,289 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: BSD-2-Clause (https://spdx.org/licenses/BSD-2-Clause) + * PURPOSE: ServerWorks/Broadcom SATA controller minidriver + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* + * Adapted from the FreeBSD ata-serverworks driver + * Copyright (c) 1998-2008 Søren Schmidt + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* GLOBALS ********************************************************************/ + +#define PCI_DEV_K2_SATA 0x0240 +#define PCI_DEV_BCM5770_SATA 0x0241 +#define PCI_DEV_BCM5770R_SATA 0x0242 +#define PCI_DEV_HT1000_SATA 0x024A +#define PCI_DEV_HT1000_SATA_2 0x024B +#define PCI_DEV_HT1100_SATA 0x0410 +#define PCI_DEV_HT1100_SATA_2 0x0411 + +#define SVW_SATA_PORT_BASE_OFFSET 0x100 +#define SVW_SATA_PORT_CONTROL_OFFSET 0x20 +#define SVW_SATA_PORT_DMA_OFFSET 0x30 +#define SVW_SATA_PORT_SCR_OFFSET 0x40 +#define SVW_SATA_PORT_TF_STRIDE 4 + +#define HW_FLAGS_8_PORTS 0x01 +#define HW_FLAGS_NO_ATAPI_DMA 0x02 + +PCIIDEX_PAGED_DATA +static const struct +{ + USHORT DeviceID; + USHORT Flags; +} SvwSataControllerList[] = +{ + { PCI_DEV_K2_SATA, HW_FLAGS_NO_ATAPI_DMA }, + { PCI_DEV_BCM5770_SATA, HW_FLAGS_NO_ATAPI_DMA | HW_FLAGS_8_PORTS }, + { PCI_DEV_BCM5770R_SATA, HW_FLAGS_NO_ATAPI_DMA }, + { PCI_DEV_HT1000_SATA, HW_FLAGS_NO_ATAPI_DMA }, + { PCI_DEV_HT1000_SATA_2, HW_FLAGS_NO_ATAPI_DMA }, + { PCI_DEV_HT1100_SATA, 0 }, + { PCI_DEV_HT1100_SATA_2, 0 }, +}; + +/* FUNCTIONS ******************************************************************/ + +static +BOOLEAN +SvwSataScrRead( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ SATA_SCR_REGISTER Register, + _In_ ULONG PortNumber, + _In_ PULONG Result) +{ + UNREFERENCED_PARAMETER(PortNumber); + + if (Register > ATA_SCONTROL) + return FALSE; + + *Result = READ_REGISTER_ULONG((PULONG)(ChanData->Regs.Scr + (Register * 4))); + return TRUE; +} + +static +BOOLEAN +SvwSataScrWrite( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ SATA_SCR_REGISTER Register, + _In_ ULONG PortNumber, + _In_ ULONG Value) +{ + UNREFERENCED_PARAMETER(PortNumber); + + if (Register > ATA_SCONTROL) + return FALSE; + + WRITE_REGISTER_ULONG((PULONG)(ChanData->Regs.Scr + (Register * 4)), Value); + return TRUE; +} + +static +inline +ULONG +SvwSataGetIoBarIndex( + _In_ PATA_CONTROLLER Controller) +{ + return (Controller->Pci.DeviceID == PCI_DEV_HT1100_SATA) ? 3 : 5; +} + +static +BOOLEAN +SvwSataCheckInterrupt( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + PATA_CONTROLLER Controller = ChanData->Controller; + PUCHAR IoBase = Controller->AccessRange[SvwSataGetIoBarIndex(Controller)].IoBase; + ULONG InterruptStatus; + + InterruptStatus = READ_REGISTER_ULONG((PULONG)(IoBase + 0x1F80)); + return !!(InterruptStatus & (1 << ChanData->Channel)); +} + +static +UCHAR +SvwSataReadStatus( + _In_ PCHANNEL_DATA_PATA ChanData) +{ + /* + * The SATA hardware expects 32-bit reads on the status register + * to get it latched propertly. + */ + return READ_REGISTER_ULONG((PULONG)ChanData->Regs.Status); +} + +static +VOID +SvwSataLoadTaskFile( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ PATA_DEVICE_REQUEST Request) +{ + USHORT Features, SectorCount, LbaLow, LbaMid, LbaHigh; + + Features = Request->TaskFile.Feature; + SectorCount = Request->TaskFile.SectorCount; + LbaLow = Request->TaskFile.LowLba; + LbaMid = Request->TaskFile.MidLba; + LbaHigh = Request->TaskFile.HighLba; + + if (Request->Flags & REQUEST_FLAG_LBA48) + { + Features |= (USHORT)Request->TaskFile.FeatureEx << 8; + SectorCount |= (USHORT)Request->TaskFile.SectorCountEx << 8; + LbaLow |= (USHORT)Request->TaskFile.LowLbaEx << 8; + LbaMid |= (USHORT)Request->TaskFile.MidLbaEx << 8; + LbaHigh |= (USHORT)Request->TaskFile.HighLbaEx << 8; + } + + /* The SATA hardware needs 16-bit accesses for the second byte of FIFO */ + WRITE_REGISTER_USHORT((PUSHORT)ChanData->Regs.Features, Features); + WRITE_REGISTER_USHORT((PUSHORT)ChanData->Regs.SectorCount, SectorCount); + WRITE_REGISTER_USHORT((PUSHORT)ChanData->Regs.LbaLow, LbaLow); + WRITE_REGISTER_USHORT((PUSHORT)ChanData->Regs.LbaMid, LbaMid); + WRITE_REGISTER_USHORT((PUSHORT)ChanData->Regs.LbaHigh, LbaHigh); + + if (Request->Flags & REQUEST_FLAG_SET_DEVICE_REGISTER) + WRITE_REGISTER_UCHAR(ChanData->Regs.Device, Request->TaskFile.DriveSelect); +} + +static +VOID +SvwSataSaveTaskFile( + _In_ PCHANNEL_DATA_PATA ChanData, + _Inout_ PATA_DEVICE_REQUEST Request) +{ + PATA_TASKFILE TaskFile = &Request->Output; + USHORT Error, SectorCount, LbaLow, LbaMid, LbaHigh; + + TaskFile->DriveSelect = READ_REGISTER_UCHAR(ChanData->Regs.Device); + TaskFile->Command = READ_REGISTER_UCHAR(ChanData->Regs.Command); + + Error = READ_REGISTER_USHORT((PUSHORT)ChanData->Regs.Error); + SectorCount = READ_REGISTER_USHORT((PUSHORT)ChanData->Regs.SectorCount); + LbaLow = READ_REGISTER_USHORT((PUSHORT)ChanData->Regs.LbaLow); + LbaMid = READ_REGISTER_USHORT((PUSHORT)ChanData->Regs.LbaMid); + LbaHigh = READ_REGISTER_USHORT((PUSHORT)ChanData->Regs.LbaHigh); + + TaskFile->Error = Error & 0xFF; + TaskFile->SectorCount = SectorCount & 0xFF; + TaskFile->LowLba = LbaLow & 0xFF; + TaskFile->MidLba = LbaMid & 0xFF; + TaskFile->HighLba = LbaHigh & 0xFF; + + if (Request->Flags & REQUEST_FLAG_LBA48) + { + TaskFile->FeatureEx = Error >> 8; + TaskFile->SectorCountEx = SectorCount >> 8; + TaskFile->LowLbaEx = LbaLow >> 8; + TaskFile->MidLbaEx = LbaMid >> 8; + TaskFile->HighLbaEx = LbaHigh >> 8; + } +} + +static +CODE_SEG("PAGE") +VOID +SvwSataParseResources( + _Inout_ PCHANNEL_DATA_PATA ChanData, + _In_ PUCHAR IoBase, + _In_ ULONG PortIndex) +{ + PUCHAR ChanBase = IoBase + (PortIndex * SVW_SATA_PORT_BASE_OFFSET); + + PAGED_CODE(); + + ChanData->ChanInfo |= CHANNEL_FLAG_MRES_TF | CHANNEL_FLAG_MRES_CTRL | CHANNEL_FLAG_MRES_DMA; + + ChanData->Regs.Dma = ChanBase + SVW_SATA_PORT_DMA_OFFSET; + ChanData->Regs.Scr = ChanBase + SVW_SATA_PORT_SCR_OFFSET; + + PciIdeInitTaskFileIoResources(ChanData, + (ULONG_PTR)ChanBase, + (ULONG_PTR)ChanBase + SVW_SATA_PORT_CONTROL_OFFSET, + SVW_SATA_PORT_TF_STRIDE); +} + +CODE_SEG("PAGE") +NTSTATUS +SvwSataGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + NTSTATUS Status; + ULONG i, HwFlags; + PUCHAR IoBase; + + PAGED_CODE(); + ASSERT(Controller->Pci.VendorID == PCI_VEN_SERVERWORKS); + + for (i = 0; i < RTL_NUMBER_OF(SvwSataControllerList); ++i) + { + HwFlags = SvwSataControllerList[i].Flags; + + if (Controller->Pci.DeviceID == SvwSataControllerList[i].DeviceID) + break; + } + if (i == RTL_NUMBER_OF(SvwSataControllerList)) + return STATUS_NO_MATCH; + + if (HwFlags & HW_FLAGS_8_PORTS) + Controller->MaxChannels = 8; + else + Controller->MaxChannels = 4; + + Controller->Flags |= CTRL_FLAG_MANUAL_RES; + + Status = PciIdeCreateChannelData(Controller, 0); + if (!NT_SUCCESS(Status)) + return Status; + + IoBase = AtaCtrlPciMapBar(Controller, SvwSataGetIoBarIndex(Controller), 0); + if (!IoBase) + return STATUS_DEVICE_CONFIGURATION_ERROR; + + /* + * Errata: Set SICR registers to turn off waiting for a status message + * before sending FIS, fixes some issues with Seagate hard drives. + */ + WRITE_REGISTER_ULONG((PULONG)(IoBase + 0x80), + READ_REGISTER_ULONG((PULONG)(IoBase + 0x80)) & ~0x00040000); + + /* Clear interrupts */ + WRITE_REGISTER_ULONG((PULONG)(IoBase + 0x44), 0xFFFFFFFF); + WRITE_REGISTER_ULONG((PULONG)(IoBase + 0x88), 0); + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + ChanData->TransferModeSupported = SATA_ALL; + ChanData->SetTransferMode = SataSetTransferMode; + + if (HwFlags & HW_FLAGS_NO_ATAPI_DMA) + ChanData->ChanInfo |= CHANNEL_FLAG_NO_ATAPI_DMA; + + /* + * Errata: Enable the DMA engine before sending the drive command + * to avoid data corruption. + */ + ChanData->ChanInfo |= CHANNEL_FLAG_DMA_BEFORE_CMD; + + ChanData->ChanInfo |= CHANNEL_FLAG_NO_SLAVE; + ChanData->ReadStatus = SvwSataReadStatus; + ChanData->LoadTaskFile = SvwSataLoadTaskFile; + ChanData->SaveTaskFile = SvwSataSaveTaskFile; + ChanData->CheckInterrupt = SvwSataCheckInterrupt; + ChanData->ScrRead = SvwSataScrRead; + ChanData->ScrWrite = SvwSataScrWrite; + + SvwSataParseResources(ChanData, IoBase, i); + } + + return STATUS_SUCCESS; +} diff --git a/drivers/storage/ide/pciidex/chipset/toshiba.c b/drivers/storage/ide/pciidex/chipset/toshiba.c new file mode 100644 index 00000000000..1ef4308a48a --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/toshiba.c @@ -0,0 +1,134 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: BSD-2-Clause (https://spdx.org/licenses/BSD-2-Clause) + * PURPOSE: Toshiba PCI IDE controller minidriver + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* + * Adapted from the NetBSD toshide driver + * Copyright (c) 2009 The NetBSD Foundation, Inc + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* GLOBALS ********************************************************************/ + +#define PCI_DEV_PICCOLO_1 0x0101 +#define PCI_DEV_PICCOLO_2 0x0102 +#define PCI_DEV_PICCOLO_3 0x0103 +#define PCI_DEV_PICCOLO_5 0x0105 + +#define PICCOLO_REG_PIO_TIMING 0x50 +#define PICCOLO_REG_DMA_TIMING 0x5C + +#define PICCOLO_PIO_MASK 0xFFFFE088 +#define PICCOLO_DMA_MASK 0xFFFFE088 +#define PICCOLO_UDMA_MASK 0x78FFE088 + +static const ULONG PiccoloPioTimings[] = +{ + 0x0566, // Mode 0 + 0x0433, // Mode 1 + 0x0311, // Mode 2 + 0x0201, // Mode 3 + 0x0200 // Mode 4 +}; + +static const ULONG PiccoloMwDmaTimings[] = +{ + 0x0655, // Mode 0 + 0x0200, // Mode 1 + 0x0200, // Mode 2 +}; + +static const ULONG PiccoloUdmaTimings[] = +{ + 0x84000222, // Mode 0 + 0x83000111, // Mode 1 + 0x82000000, // Mode 2 +}; + +/* FUNCTIONS ******************************************************************/ + +static +VOID +ToshibaSetTransferMode( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + ULONG i; + + UNREFERENCED_PARAMETER(Channel); + + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + ULONG TimingReg; + + if (!Device) + continue; + + TimingReg = PciRead32(Controller, PICCOLO_REG_PIO_TIMING); + TimingReg &= PICCOLO_PIO_MASK; + TimingReg |= PiccoloPioTimings[Device->PioMode]; + PciWrite32(Controller, PICCOLO_REG_PIO_TIMING, TimingReg); + + if (Device->DmaMode != PIO_MODE(0)) + { + TimingReg = PciRead32(Controller, PICCOLO_REG_DMA_TIMING); + + if (Device->DmaMode >= UDMA_MODE(0)) + { + TimingReg &= PICCOLO_UDMA_MASK; + TimingReg |= PiccoloUdmaTimings[Device->DmaMode - UDMA_MODE(0)]; + } + else + { + TimingReg &= PICCOLO_DMA_MASK; + TimingReg |= PiccoloMwDmaTimings[Device->DmaMode - MWDMA_MODE(0)]; + } + + PciWrite32(Controller, PICCOLO_REG_DMA_TIMING, TimingReg); + } + } +} + +CODE_SEG("PAGE") +NTSTATUS +ToshibaGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + NTSTATUS Status; + ULONG i; + + PAGED_CODE(); + ASSERT(Controller->Pci.VendorID == PCI_VEN_TOSHIBA); + + if (Controller->Pci.DeviceID != PCI_DEV_PICCOLO_1 && + Controller->Pci.DeviceID != PCI_DEV_PICCOLO_2 && + Controller->Pci.DeviceID != PCI_DEV_PICCOLO_3 && + Controller->Pci.DeviceID != PCI_DEV_PICCOLO_5) + { + return STATUS_NO_MATCH; + } + + Controller->MaxChannels = 1; + + Status = PciIdeCreateChannelData(Controller, 0); + if (!NT_SUCCESS(Status)) + return Status; + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 2); + ChanData->SetTransferMode = ToshibaSetTransferMode; + } + + return STATUS_SUCCESS; +} diff --git a/drivers/storage/ide/pciidex/chipset/via.c b/drivers/storage/ide/pciidex/chipset/via.c new file mode 100644 index 00000000000..70dfd268042 --- /dev/null +++ b/drivers/storage/ide/pciidex/chipset/via.c @@ -0,0 +1,899 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: BSD-2-Clause (https://spdx.org/licenses/BSD-2-Clause) + * PURPOSE: VIA ATA controller minidriver + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +/* + * Some code taken from the NetBSD viaide driver + * Copyright (c) 1999, 2000, 2001 Manuel Bouyer + */ + +/* INCLUDES *******************************************************************/ + +#include "pciidex.h" + +/* GLOBALS ********************************************************************/ + +#define PCI_DEV_VT82C576M_IDE 0x1571 +#define PCI_DEV_VT82C586A_IDE 0x0571 +#define PCI_DEV_VT6410_IDE_RAID 0x3164 +#define PCI_DEV_VT6415_IDE 0x0415 +#define PCI_DEV_VT6420_IDE 0x4149 +#define PCI_DEV_VT6421A_RAID 0x3249 +#define PCI_DEV_CX700M2_IDE_SATA 0x5324 +#define PCI_DEV_CX700M2_IDE_SATA_RAID 0x0581 +#define PCI_DEV_VX855_IDE 0xC409 + +#define PCI_DEV_VT8237_SATA 0x3149 +#define PCI_DEV_VT8237A_SATA 0x0591 +#define PCI_DEV_VT8237A_SATA_2 0x5337 +#define PCI_DEV_VT8237S_SATA 0x5372 +#define PCI_DEV_VT8237S_SATA_RAID 0x7372 +#define PCI_DEV_VT8251_SATA_AHCI 0x3349 +#define PCI_DEV_VT8251_SATA_2 0x5287 +#define PCI_DEV_VT8261_SATA 0x9000 +#define PCI_DEV_VT8261_SATA_RAID 0x9040 +#define PCI_DEV_VX900_SATA 0x9001 +#define PCI_DEV_VX900_SATA_RAID 0x9041 + +#define PCI_DEV_BRIDGE_VT82C586 0x0586 +#define PCI_DEV_BRIDGE_VT82C596A 0x0596 +#define PCI_DEV_BRIDGE_VT82C686A 0x0686 +#define PCI_DEV_BRIDGE_VT8231 0x8231 +#define PCI_DEV_BRIDGE_VT8233 0x3074 +#define PCI_DEV_BRIDGE_VT8233A 0x3147 +#define PCI_DEV_BRIDGE_VT8233C 0x3109 +#define PCI_DEV_BRIDGE_VT8235 0x3177 +#define PCI_DEV_BRIDGE_VT8237 0x3227 +#define PCI_DEV_BRIDGE_VT8237A 0x3337 +#define PCI_DEV_BRIDGE_VT8237S 0x3372 +#define PCI_DEV_BRIDGE_VT8251 0x3287 +#define PCI_DEV_BRIDGE_VT8261 0x3402 + +#define HW_FLAGS_TYPE_MASK 0x000F +#define HW_FLAGS_CHECK_BRIDGE 0x0010 +#define HW_FLAGS_HAS_UDMA_CLOCK 0x0020 +#define HW_FLAGS_SINGLE_CHAN 0x0040 +#define HW_FLAGS_SINGLE_PORT 0x0080 +#define HW_FLAGS_PCI_SCR 0x0100 + +#define TYPE_33 0 +#define TYPE_66 1 +#define TYPE_100 2 +#define TYPE_133 3 +#define TYPE_MWDMA 4 +#define TYPE_SATA 5 + +#define GET_TYPE(Flags) ((Flags) & HW_FLAGS_TYPE_MASK) + +#define VIA_PCI_CLOCK 30000 + +#define VIA_REG_ENABLE_CTRL 0x40 +#define VIA_REG_DRIVE_TIMING 0x48 +#define VIA_REG_PORT_TIMING(Channel) (0x4F - (Channel)) +#define VIA_REG_ADDRESS_SETUP 0x4C +#define VIA_REG_UDMA_CTRL 0x50 +#define VIA_REG_SSTATUS 0xA0 +#define VIA_REG_SCONTROL 0xA4 +#define VIA_REG_SERROR 0xA8 +#define VIA_REG_SERROR_VT8237 0xB0 + +#define VIA_UDMA_CLOCK_UDMA66 0x08 +#define VIA_UDMA_CABLE_BITS 0x10 +#define VIA_UDMA_CLEAR_MASK 0xEF // Save the cable bits + +#define VIA_UDMA_CLOCK_ENABLED(Reg32, Channel) \ + (((Reg32) & (0x00080000 >> ((Channel) * 16))) != 0) + +#define VIA_UDMA_CABLE_PRESENT(Reg32, Channel) \ + (((Reg32) & (0x10100000 >> ((Channel) * 16))) != 0) + +#define VIA_SINGLE_CHAN_UDMA_CABLE_PRESENT(Controller) \ + ((PciRead16(Controller, 0x50) & 0x1010) != 0) + +/* Note that the cable bits are inverted */ +#define VT6421A_UDMA_CABLE_PRESENT(Controller) \ + ((PciRead16(Controller, 0xB2) & 0x1010) == 0) + +#define VIA_REG_SATA_PORT_MAP 0x49 + +#define VT6421A_REG_SATA_CTRL 0x52 // Also for VT8237 +#define VT6421A_REG_TIMING_CTRL(Drive) (0xAB - (Drive)) +#define VT6421A_REG_UDMA_CTRL(Drive) (0xB3 - (Drive)) + +#define VT6421A_UDMA_SLOW 0x0F +#define VT6421A_FIFO_WATERMARK_64DW 0x04 + +PCIIDEX_PAGED_DATA +static const struct +{ + USHORT DeviceID; + USHORT Flags; +} ViaControllerList[] = +{ + { PCI_DEV_VT82C576M_IDE, TYPE_MWDMA }, + { PCI_DEV_VT82C586A_IDE, HW_FLAGS_CHECK_BRIDGE }, + { PCI_DEV_VT6410_IDE_RAID, TYPE_133 }, + { PCI_DEV_VT6415_IDE, TYPE_133 | HW_FLAGS_SINGLE_CHAN }, + { PCI_DEV_VT6420_IDE, TYPE_133 | HW_FLAGS_SINGLE_CHAN }, + { PCI_DEV_VX855_IDE, TYPE_133 | HW_FLAGS_SINGLE_CHAN }, + { PCI_DEV_VT8237_SATA, TYPE_SATA | HW_FLAGS_SINGLE_PORT }, + { PCI_DEV_VT8237A_SATA, TYPE_SATA | HW_FLAGS_SINGLE_PORT }, + { PCI_DEV_VT8237A_SATA_2, TYPE_SATA | HW_FLAGS_SINGLE_PORT }, + { PCI_DEV_VT8237S_SATA, TYPE_SATA | HW_FLAGS_SINGLE_PORT }, + { PCI_DEV_VT8237S_SATA_RAID, TYPE_SATA | HW_FLAGS_SINGLE_PORT | HW_FLAGS_PCI_SCR }, + { PCI_DEV_VT8251_SATA_AHCI, TYPE_SATA | HW_FLAGS_PCI_SCR }, + { PCI_DEV_VT8251_SATA_2, TYPE_SATA | HW_FLAGS_PCI_SCR }, + { PCI_DEV_VT8261_SATA, TYPE_SATA | HW_FLAGS_PCI_SCR }, + { PCI_DEV_VT8261_SATA_RAID, TYPE_SATA | HW_FLAGS_PCI_SCR }, + { PCI_DEV_VX900_SATA, TYPE_SATA | HW_FLAGS_PCI_SCR }, + { PCI_DEV_VX900_SATA_RAID, TYPE_SATA | HW_FLAGS_PCI_SCR }, +}; + +static const struct +{ + UCHAR DefaultValue; + UCHAR Data[7]; +} ViaUdmaTimings[] = +{ + // UDMA 0 1 2 3 4 5 6 + { 0x03, { 0xE2, 0xE1, 0xE0 } }, // TYPE_33 + { 0x03, { 0xE6, 0xE4, 0xE2, 0xE1, 0xE0 } }, // TYPE_66 + { 0x07, { 0xEA, 0xE6, 0xE4, 0xE2, 0xE1, 0xE0 } }, // TYPE_100 + { 0x07, { 0xEE, 0xE8, 0xE6, 0xE4, 0xE2, 0xE1, 0xE0 } }, // TYPE_133 +}; + +PCIIDEX_PAGED_DATA +static const ATA_PCI_ENABLE_BITS ViaPataEnableBits[MAX_IDE_CHANNEL] = +{ + { 0x40, 0x02, 0x02 }, + { 0x40, 0x01, 0x01 }, +}; + +PCIIDEX_PAGED_DATA +static const ATA_PCI_ENABLE_BITS ViaCx700EnableBits[MAX_IDE_CHANNEL] = +{ + { 0x40, 0x02, 0x02 }, + { 0xC0, 0x01, 0x01 }, +}; + +/* FUNCTIONS ******************************************************************/ + +VOID +ViaClampTimings( + _Inout_ PATA_TIMING Timing) +{ + Timing->AddressSetup = CLAMP_TIMING(Timing->AddressSetup, 1, 4) - 1; + Timing->CmdRecovery = CLAMP_TIMING(Timing->CmdRecovery, 1, 16) - 1; + Timing->CmdActive = CLAMP_TIMING(Timing->CmdActive, 1, 16) - 1; + Timing->DataRecovery = CLAMP_TIMING(Timing->DataRecovery, 1, 16) - 1; + Timing->DataActive = CLAMP_TIMING(Timing->DataActive, 1, 16) - 1; +} + +static +VOID +ViaSetTransferMode( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + PCHANNEL_DATA_PATA ChanData = Controller->Channels[Channel]; + ATA_TIMING DeviceTimings[MAX_IDE_DEVICE]; + ULONG i; + ULONG DriveTimReg, UdmaTimReg; + UCHAR PortTimReg; + + AtaSelectTimings(DeviceList, DeviceTimings, VIA_PCI_CLOCK, SHARED_CMD_TIMINGS); + + INFO("CH %lu: Config (before)\n" + "DRV %08lX\n" + "PORT %08lX\n" + "UDMA %08lX\n" + "CFG %08lX\n", + Channel, + PciRead32(Controller, 0x48), + PciRead32(Controller, 0x4C), + PciRead32(Controller, 0x50), + PciRead32(Controller, 0x40)); + + DriveTimReg = PciRead32(Controller, VIA_REG_DRIVE_TIMING); + PortTimReg = PciRead8(Controller, VIA_REG_PORT_TIMING(Channel)); + + if (GET_TYPE(ChanData->HwFlags) != TYPE_MWDMA) + UdmaTimReg = PciRead32(Controller, VIA_REG_UDMA_CTRL); + + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + PATA_TIMING Timing = &DeviceTimings[i]; + const ULONG DeviceIndex = (Channel << 1) + i; // 0 1 2 3 + const ULONG Shift = (3 - DeviceIndex) << 3; // 24 16 8 0 + ULONG Value; + + /* UDMA timings */ + if (GET_TYPE(ChanData->HwFlags) != TYPE_MWDMA) + { + if (Device && (Device->DmaMode >= UDMA_MODE(0))) + { + ULONG ModeIndex = Device->DmaMode - UDMA_MODE(0); + Value = ViaUdmaTimings[GET_TYPE(ChanData->HwFlags)].Data[ModeIndex]; + } + else + { + Value = ViaUdmaTimings[GET_TYPE(ChanData->HwFlags)].DefaultValue; + } + + /* Turn on the 66MHz UDMA clock */ + if ((i != 0) && (GET_TYPE(ChanData->HwFlags) == TYPE_66)) + Value |= VIA_UDMA_CLOCK_UDMA66; + + UdmaTimReg &= ~(VIA_UDMA_CLEAR_MASK << Shift); + UdmaTimReg |= Value << Shift; + } + + if (!Device) + continue; + + /* PIO and DMA timings */ + ViaClampTimings(Timing); + + Value = (Timing->DataActive << 4) | Timing->DataRecovery; + DriveTimReg &= ~(0xFF << Shift); + DriveTimReg |= Value << Shift; + + PortTimReg = (Timing->CmdActive << 4) | Timing->CmdRecovery; + } + + PciWrite8(Controller, VIA_REG_PORT_TIMING(Channel), PortTimReg); + PciWrite32(Controller, VIA_REG_DRIVE_TIMING, DriveTimReg); + + if (GET_TYPE(ChanData->HwFlags) != TYPE_MWDMA) + PciWrite32(Controller, VIA_REG_UDMA_CTRL, UdmaTimReg); + + INFO("CH %lu: Config (after)\n" + "DRV %08lX\n" + "PORT %08lX\n" + "UDMA %08lX\n" + "CFG %08lX\n", + Channel, + PciRead32(Controller, 0x48), + PciRead32(Controller, 0x4C), + PciRead32(Controller, 0x50), + PciRead32(Controller, 0x40)); +} + +static +VOID +ViaSataSetTransferMode( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + UCHAR SataControl = PciRead8(Controller, VT6421A_REG_SATA_CTRL); + ULONG i; + + if (!(SataControl & VT6421A_FIFO_WATERMARK_64DW)) + { + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + + if (!Device) + continue; + + /* Fix freezes with Western Digital drives. See CORE-5897 for more details */ + if (_strnicmp(Device->FriendlyName, "WD", 2) == 0) + { + /* This will slow down I/O performance */ + WARN("Enabling workaround for the '%s' drive\n", Device->FriendlyName); + SataControl |= VT6421A_FIFO_WATERMARK_64DW; + PciWrite8(Controller, VT6421A_REG_SATA_CTRL, SataControl); + break; + } + } + } + + SataSetTransferMode(Controller, Channel, DeviceList); +} + +static +VOID +Via6421SetTransferMode( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + ATA_TIMING DeviceTimings[MAX_IDE_DEVICE]; + ULONG i; + + UNREFERENCED_PARAMETER(Channel); + + AtaSelectTimings(DeviceList, DeviceTimings, VIA_PCI_CLOCK, SHARED_CMD_TIMINGS); + + for (i = 0; i < MAX_IDE_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + PATA_TIMING Timing = &DeviceTimings[i]; + UCHAR UdmaTimReg; + + /* UDMA timings */ + UdmaTimReg = PciRead8(Controller, VT6421A_REG_UDMA_CTRL(i)); + UdmaTimReg &= VIA_UDMA_CABLE_BITS; + if (Device && (Device->DmaMode >= UDMA_MODE(0))) + UdmaTimReg |= ViaUdmaTimings[TYPE_133].Data[Device->DmaMode - UDMA_MODE(0)]; + else + UdmaTimReg |= VT6421A_UDMA_SLOW; + PciWrite8(Controller, VT6421A_REG_UDMA_CTRL(i), UdmaTimReg); + + if (!Device) + continue; + + ViaClampTimings(Timing); + + /* PIO timings */ + PciWrite8(Controller, + VT6421A_REG_TIMING_CTRL(i), + (Timing->DataActive << 4) | Timing->DataRecovery); + } +} + +static +ULONG +ViaGetSerrOffset( + _In_ PATA_CONTROLLER Controller) +{ + if ((Controller->Pci.DeviceID == PCI_DEV_VT8237S_SATA_RAID)) + return VIA_REG_SERROR_VT8237; + + return VIA_REG_SERROR; +} + +static +BOOLEAN +ViaScrReadPci( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ SATA_SCR_REGISTER Register, + _In_ ULONG PortNumber, + _In_ PULONG Result) +{ + PATA_CONTROLLER Controller = ChanData->Controller; + ULONG Value, Offset = 2 * ChanData->Channel + PortNumber; + UCHAR Reg; + + switch (Register) + { + case ATA_SSTATUS: + { + Reg = PciRead8(Controller, VIA_REG_SSTATUS + Offset); + + Value = Reg & 0x03; + + if (Reg & 0x04) + Value |= AHCI_PXSSTS_IPM_PARTIAL; + else if (Reg & 0x08) + Value |= AHCI_PXSSTS_IPM_SLUMBER; + else + Value |= AHCI_PXSSTS_IPM_ACTIVE; + + if (Reg & 0x10) + Value |= AHCI_PXSSTS_SPD_SATA2; + else + Value |= AHCI_PXSSTS_SPD_SATA1; + break; + } + case ATA_SERROR: + { + Value = PciRead32(Controller, ViaGetSerrOffset(Controller) + Offset * 4); + break; + } + case ATA_SCONTROL: + { + Value = 0; + Reg = PciRead8(Controller, VIA_REG_SCONTROL + Offset); + if (Reg & 0x01) + Value |= AHCI_PXCTL_DET_RESET; + if (Reg & 0x02) + Value |= AHCI_PXCTL_DET_DISABLE_SATA; + if (Reg & 0x04) + Value |= AHCI_PXCTL_IPM_DISABLE_PARTIAL; + if (Reg & 0x08) + Value |= AHCI_PXCTL_IPM_DISABLE_SLUMBER; + break; + } + + default: + return FALSE; + } + + *Result = Value; + return TRUE; +} + +static +BOOLEAN +ViaScrWritePci( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ SATA_SCR_REGISTER Register, + _In_ ULONG PortNumber, + _In_ ULONG Value) +{ + PATA_CONTROLLER Controller = ChanData->Controller; + ULONG Offset = 2 * ChanData->Channel + PortNumber; + + switch (Register) + { + case ATA_SERROR: + { + PciWrite32(Controller, ViaGetSerrOffset(Controller) + Offset * 4, Value); + break; + } + case ATA_SCONTROL: + { + UCHAR Val = 0; + if (Value & AHCI_PXCTL_DET_RESET) + Val |= 0x01; + if (Value & AHCI_PXCTL_DET_DISABLE_SATA) + Val |= 0x02; + if (Value & AHCI_PXCTL_IPM_DISABLE_PARTIAL) + Val |= 0x04; + if (Value & AHCI_PXCTL_IPM_DISABLE_SLUMBER) + Val |= 0x08; + + PciWrite8(Controller, VIA_REG_SCONTROL + Offset, Val); + break; + } + + default: + return FALSE; + } + + return TRUE; +} + +static +BOOLEAN +ViaScrReadIoPort( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ SATA_SCR_REGISTER Register, + _In_ ULONG PortNumber, + _In_ PULONG Result) +{ + UNREFERENCED_PARAMETER(PortNumber); + + if (Register > ATA_SCONTROL) + return FALSE; + + *Result = READ_PORT_ULONG((PULONG)(ChanData->Regs.Scr + (Register * 4))); + return TRUE; +} + +static +BOOLEAN +ViaScrWriteIoPort( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ SATA_SCR_REGISTER Register, + _In_ ULONG PortNumber, + _In_ ULONG Value) +{ + UNREFERENCED_PARAMETER(PortNumber); + + if (Register > ATA_SCONTROL) + return FALSE; + + WRITE_PORT_ULONG((PULONG)(ChanData->Regs.Scr + (Register * 4)), Value); + return TRUE; +} + +static +CODE_SEG("PAGE") +NTSTATUS +Via6421ParseResources( + _Inout_ PCHANNEL_DATA_PATA ChanData, + _In_ ULONG PortIndex) +{ + PUCHAR CommandPortBase, ControlPortBase, DmaBase, ScrBase; + + PAGED_CODE(); + + /* + * [BAR 0]: I/O ports at E150 [size=16] Channel 0 + * [BAR 1]: I/O ports at E140 [size=16] Channel 1 + * [BAR 2]: I/O ports at E130 [size=16] Channel 2 + * [BAR 3]: I/O ports at E120 [size=16] + * [BAR 4]: I/O ports at E100 [size=32] BM DMA + * [BAR 5]: I/O ports at E000 [size=256] SATA PHY (minimum size = 128) + */ + + CommandPortBase = AtaCtrlPciMapBar(ChanData->Controller, PortIndex, 16); + if (!CommandPortBase) + return STATUS_DEVICE_CONFIGURATION_ERROR; + + DmaBase = AtaCtrlPciMapBar(ChanData->Controller, 4, 32); + if (!DmaBase) + return STATUS_DEVICE_CONFIGURATION_ERROR; + + ScrBase = AtaCtrlPciMapBar(ChanData->Controller, 5, 128); + if (!ScrBase) + return STATUS_DEVICE_CONFIGURATION_ERROR; + + ChanData->Regs.Dma = DmaBase + (PortIndex * 8); + ChanData->Regs.Scr = ScrBase + (PortIndex * 64); + + ControlPortBase = CommandPortBase + PCIIDE_COMMAND_IO_RANGE_LENGTH; + + PciIdeInitTaskFileIoResources(ChanData, + (ULONG_PTR)CommandPortBase, + (ULONG_PTR)ControlPortBase + PCIIDE_CONTROL_IO_BAR_OFFSET, + 1); + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +Via6421GetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + NTSTATUS Status; + ULONG i; + + PAGED_CODE(); + + /* + * Channel 0: SATA port #0 (M only) + * Channel 1: SATA port #1 (M only) + * Channel 2: PATA channel + */ + Controller->MaxChannels = 3; + Controller->Flags |= CTRL_FLAG_MANUAL_RES; + + Status = PciIdeCreateChannelData(Controller, 0); + if (!NT_SUCCESS(Status)) + return Status; + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + if (i == 2) + { + /* No MWDMA support */ + ChanData->TransferModeSupported = PIO_ALL | UDMA_ALL; + ChanData->SetTransferMode = Via6421SetTransferMode; + + if (!VT6421A_UDMA_CABLE_PRESENT(Controller)) + { + INFO("CH %lu: BIOS detected 40-conductor cable\n", ChanData->Channel); + ChanData->TransferModeSupported &= ~UDMA_80C_ALL; + } + } + else + { + ChanData->ChanInfo |= CHANNEL_FLAG_NO_SLAVE; + ChanData->SetTransferMode = ViaSataSetTransferMode; + ChanData->TransferModeSupported = SATA_ALL; + ChanData->ScrRead = ViaScrReadIoPort; + ChanData->ScrWrite = ViaScrWriteIoPort; + } + + Status = Via6421ParseResources(ChanData, i); + if (!NT_SUCCESS(Status)) + return Status; + } + + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +ViaCx700GetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + NTSTATUS Status; + ULONG i; + + PAGED_CODE(); + + Status = PciIdeCreateChannelData(Controller, 0); + if (!NT_SUCCESS(Status)) + return Status; + + Controller->ChannelEnableBits = ViaCx700EnableBits; + + /* + * Channel 0: 2 SATA ports (M/S emulation) + * Channel 1: PATA channel + */ + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + if (i == 0) + { + ChanData->SetTransferMode = SataSetTransferMode; + ChanData->TransferModeSupported = SATA_ALL; + ChanData->ScrRead = ViaScrReadPci; + ChanData->ScrWrite = ViaScrWritePci; + } + else + { + ChanData->HwFlags |= TYPE_133; + ChanData->SetTransferMode = ViaSetTransferMode; + ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_ALL; + + if (!VIA_SINGLE_CHAN_UDMA_CABLE_PRESENT(Controller)) + { + INFO("CH %lu: BIOS detected 40-conductor cable\n", ChanData->Channel); + ChanData->TransferModeSupported &= ~UDMA_80C_ALL; + } + } + } + + return STATUS_SUCCESS; +} + +static +VOID +Via6410ControllerStart( + _In_ PATA_CONTROLLER Controller) +{ + UCHAR Value; + + /* + * Enable the primary and secondary IDE channels. + * + * The VT6410 can be either a controller soldered onto the motherboard + * or added on an external PCI card. These PCI cards usually come + * without an option ROM on their own and have these bits set to 0s by default. + */ + Value = PciRead8(Controller, VIA_REG_ENABLE_CTRL); + Value |= 0x03; + PciWrite8(Controller, VIA_REG_ENABLE_CTRL, Value); +} + +static +CODE_SEG("PAGE") +BOOLEAN +ViaQuerySouthBridgeInformation( + _In_ PVOID Context, + _In_ ULONG BusNumber, + _In_ PCI_SLOT_NUMBER PciSlot, + _In_ PPCI_COMMON_HEADER PciConfig) +{ + PULONG HwFlags = Context; + ULONG i; + PCIIDEX_PAGED_DATA + static const struct + { + USHORT DeviceID; + UCHAR MinimumRevisionID; + UCHAR Flags; + } ViaBridgeList[] = + { + { PCI_DEV_BRIDGE_VT82C586, 0x20, TYPE_33 }, + { PCI_DEV_BRIDGE_VT82C586, 0, TYPE_MWDMA }, + { PCI_DEV_BRIDGE_VT82C596A, 0x10, TYPE_66 | HW_FLAGS_HAS_UDMA_CLOCK }, + { PCI_DEV_BRIDGE_VT82C596A, 0, TYPE_33 }, + { PCI_DEV_BRIDGE_VT82C686A, 0x40, TYPE_100 }, + { PCI_DEV_BRIDGE_VT82C686A, 0, TYPE_66 | HW_FLAGS_HAS_UDMA_CLOCK }, + { PCI_DEV_BRIDGE_VT8231, 0, TYPE_100 }, + { PCI_DEV_BRIDGE_VT8233, 0, TYPE_100 }, + { PCI_DEV_BRIDGE_VT8233A, 0, TYPE_133 }, + { PCI_DEV_BRIDGE_VT8233C, 0, TYPE_100 }, + { PCI_DEV_BRIDGE_VT8235, 0, TYPE_133 }, + { PCI_DEV_BRIDGE_VT8237, 0, TYPE_133 }, + { PCI_DEV_BRIDGE_VT8237A, 0, TYPE_133 }, + { PCI_DEV_BRIDGE_VT8237S, 0, TYPE_133 }, + { PCI_DEV_BRIDGE_VT8251, 0, TYPE_133 }, + { PCI_DEV_BRIDGE_VT8261, 0, TYPE_133 }, + }; + + UNREFERENCED_PARAMETER(BusNumber); + UNREFERENCED_PARAMETER(PciSlot); + + PAGED_CODE(); + + if (PciConfig->VendorID != PCI_VEN_VIA) + return FALSE; + + for (i = 0; i < RTL_NUMBER_OF(ViaBridgeList); ++i) + { + if ((PciConfig->DeviceID == ViaBridgeList[i].DeviceID) && + (PciConfig->RevisionID >= ViaBridgeList[i].MinimumRevisionID)) + { + INFO("Found %04X:%04X.%02X VIA bridge\n", + PciConfig->VendorID, PciConfig->DeviceID, PciConfig->RevisionID); + + *HwFlags = ViaBridgeList[i].Flags; + return TRUE; + } + } + + return FALSE; +} + +static +CODE_SEG("PAGE") +NTSTATUS +ViaPataGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller, + _In_ ULONG HwFlags) +{ + ULONG i, UdmaTimReg; + + PAGED_CODE(); + + /* + * Look for a PCI-ISA bridge to see what features are available. + * Early VIA PATA controllers share the same PCI device ID + * but have very different capabilities. + */ + if (HwFlags & HW_FLAGS_CHECK_BRIDGE) + { + if (!PciFindDevice(ViaQuerySouthBridgeInformation, &HwFlags)) + { + ERR("Unable to find the VIA bridge\n"); + ASSERT(FALSE); + return STATUS_NO_MATCH; + } + } + + if (Controller->Pci.DeviceID == PCI_DEV_VT6410_IDE_RAID) + Controller->Start = Via6410ControllerStart; + + /* The VT6415 has no channel enable bits */ + if (Controller->Pci.DeviceID != PCI_DEV_VT6415_IDE) + Controller->ChannelEnableBits = ViaPataEnableBits; + + if (GET_TYPE(HwFlags) != TYPE_MWDMA) + UdmaTimReg = PciRead32(Controller, VIA_REG_UDMA_CTRL); + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + ChanData->HwFlags = HwFlags; + ChanData->SetTransferMode = ViaSetTransferMode; + + switch (GET_TYPE(ChanData->HwFlags)) + { + case TYPE_MWDMA: + { + ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL; + break; + } + case TYPE_33: + { + ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 2); + break; + } + case TYPE_66: + { + ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 4); + + /* Check to see if the BIOS has enabled the 66MHz UDMA clock for us */ + if (!VIA_UDMA_CLOCK_ENABLED(UdmaTimReg, i)) + { + /* It seems that the clock can also be disabled due to hardware errata */ + INFO("CH %lu: BIOS detected 40-conductor cable " + "or this chip is ATA/33 capable\n", ChanData->Channel); + ChanData->TransferModeSupported &= ~UDMA_80C_ALL; + + /* Use timing table based on a 33MHz UDMA clock */ + ChanData->HwFlags &= ~HW_FLAGS_TYPE_MASK; + ChanData->HwFlags |= TYPE_33; + } + break; + } + case TYPE_100: + { + ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 5); + + if (!VIA_UDMA_CABLE_PRESENT(UdmaTimReg, i)) + { + INFO("CH %lu: BIOS detected 40-conductor cable\n", ChanData->Channel); + ChanData->TransferModeSupported &= ~UDMA_80C_ALL; + } + break; + } + case TYPE_133: + { + ChanData->TransferModeSupported = PIO_ALL | MWDMA_ALL | UDMA_MODES(0, 6); + + if (!VIA_UDMA_CABLE_PRESENT(UdmaTimReg, i)) + { + INFO("CH %lu: BIOS detected 40-conductor cable\n", ChanData->Channel); + ChanData->TransferModeSupported &= ~UDMA_80C_ALL; + } + break; + } + + default: + ASSERT(FALSE); + UNREACHABLE; + } + } + + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +ViaSataGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller, + _In_ ULONG HwFlags) +{ + ULONG i; + + PAGED_CODE(); + + INFO("Port map: %02X\n", PciRead8(Controller, VIA_REG_SATA_PORT_MAP)); + + for (i = 0; i < Controller->MaxChannels; ++i) + { + PCHANNEL_DATA_PATA ChanData = Controller->Channels[i]; + + ChanData->HwFlags = HwFlags; + ChanData->TransferModeSupported = SATA_ALL; + ChanData->SetTransferMode = SataSetTransferMode; + + if (HwFlags & HW_FLAGS_SINGLE_PORT) + ChanData->ChanInfo |= CHANNEL_FLAG_NO_SLAVE; + + if (HwFlags & HW_FLAGS_PCI_SCR) + { + ChanData->ScrRead = ViaScrReadPci; + ChanData->ScrWrite = ViaScrWritePci; + } + } + + return STATUS_SUCCESS; +} + +CODE_SEG("PAGE") +NTSTATUS +ViaGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller) +{ + NTSTATUS Status; + ULONG i, HwFlags; + + PAGED_CODE(); + ASSERT(Controller->Pci.VendorID == PCI_VEN_VIA); + + if (Controller->Pci.DeviceID == PCI_DEV_VT6421A_RAID) + { + return Via6421GetControllerProperties(Controller); + } + else if ((Controller->Pci.DeviceID == PCI_DEV_CX700M2_IDE_SATA) || + (Controller->Pci.DeviceID == PCI_DEV_CX700M2_IDE_SATA_RAID)) + { + return ViaCx700GetControllerProperties(Controller); + } + + for (i = 0; i < RTL_NUMBER_OF(ViaControllerList); ++i) + { + HwFlags = ViaControllerList[i].Flags; + + if (Controller->Pci.DeviceID == ViaControllerList[i].DeviceID) + break; + } + if (i == RTL_NUMBER_OF(ViaControllerList)) + return STATUS_NO_MATCH; + + if (HwFlags & HW_FLAGS_SINGLE_CHAN) + Controller->MaxChannels = 1; + + Status = PciIdeCreateChannelData(Controller, 0); + if (!NT_SUCCESS(Status)) + return Status; + + if (GET_TYPE(HwFlags) == TYPE_SATA) + return ViaSataGetControllerProperties(Controller, HwFlags); + + return ViaPataGetControllerProperties(Controller, HwFlags); +} diff --git a/drivers/storage/ide/pciidex/debug.h b/drivers/storage/ide/pciidex/debug.h new file mode 100644 index 00000000000..5025ba422ce --- /dev/null +++ b/drivers/storage/ide/pciidex/debug.h @@ -0,0 +1,95 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Debug support header file + * COPYRIGHT: Copyright 2026 Dmitry Borisov + */ + +#pragma once + +#ifndef __RELFILE__ +#define __RELFILE__ __FILE__ +#endif + +#if DBG + +// #define DEBUG_TRACE +// #define DEBUG_INFO +#define DEBUG_WARN +#define DEBUG_ERR + +#ifdef DEBUG_TRACE +#define TRACE(fmt, ...) \ + do { \ + if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \ + DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \ + } while (0) + +#else +#if defined(_MSC_VER) +#define TRACE __noop +#else +#define TRACE(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0) +#endif +#endif + +#ifdef DEBUG_INFO +#define INFO(fmt, ...) \ + do { \ + if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \ + DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \ + } while (0) + +#else +#if defined(_MSC_VER) +#define INFO __noop +#else +#define INFO(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0) +#endif +#endif + +#ifdef DEBUG_WARN +#define WARN(fmt, ...) \ + do { \ + if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \ + DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \ + } while (0) + +#else +#if defined(_MSC_VER) +#define WARN __noop +#else +#define WARN(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0) +#endif +#endif + +#ifdef DEBUG_ERR +#define ERR(fmt, ...) \ + do { \ + if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \ + DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \ + } while (0) + +#else +#if defined(_MSC_VER) +#define ERR __noop +#else +#define ERR(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0) +#endif +#endif + +#else + +#if defined(_MSC_VER) +#define TRACE __noop +#define INFO __noop +#define WARN __noop +#define ERR __noop +#else +#define TRACE(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0) +#define INFO(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0) +#define WARN(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0) +#define ERR(...) do { if(0) { DbgPrint(__VA_ARGS__); } } while(0) +#endif + +#endif diff --git a/drivers/storage/ide/pciidex/fdo.c b/drivers/storage/ide/pciidex/fdo.c index b85cbfae748..9e5e5e98450 100644 --- a/drivers/storage/ide/pciidex/fdo.c +++ b/drivers/storage/ide/pciidex/fdo.c @@ -1,121 +1,518 @@ /* * PROJECT: PCI IDE bus driver extension * LICENSE: See COPYING in the top level directory - * PURPOSE: IRP_MJ_PNP operations for FDOs + * PURPOSE: ATA controller FDO dispatch routines * COPYRIGHT: Copyright 2005 HervĂ© Poussineau * Copyright 2023 Dmitry Borisov */ +/* INCLUDES *******************************************************************/ + #include "pciidex.h" -#define NDEBUG -#include +/* FUNCTIONS ******************************************************************/ + +static +DECLSPEC_NOINLINE_FROM_PAGED +VOID +AtaCtrlCallStartController( + _In_ PATA_CONTROLLER Controller) +{ + KIRQL OldIrql; + + KeAcquireSpinLock(&Controller->Lock, &OldIrql); + Controller->Start(Controller); + KeReleaseSpinLock(&Controller->Lock, OldIrql); +} + +static +DECLSPEC_NOINLINE_FROM_PAGED +VOID +AtaCtrlCallStopController( + _In_ PATA_CONTROLLER Controller) +{ + KIRQL OldIrql; + + KeAcquireSpinLock(&Controller->Lock, &OldIrql); + Controller->Stop(Controller); + KeReleaseSpinLock(&Controller->Lock, OldIrql); +} + +extern +VOID +DumpTestMiniport( + _Inout_ PFDO_DEVICE_EXTENSION FdoExt); + +CODE_SEG("PAGE") +NTSTATUS +AtaCtrlAttachChannel( + _In_ PVOID ChannelContext, + _In_ BOOLEAN Attach) +{ + PCHANNEL_DATA_COMMON ChanData = ChannelContext; + PATA_CONTROLLER Controller = ChanData->Controller; + + PAGED_CODE(); + + return Controller->AttachChannel(ChanData, Attach); +} + +DECLSPEC_NOINLINE_FROM_PAGED +VOID +AtaChanEnableInterruptsSync( + _In_ PVOID ChannelContext, + _In_ BOOLEAN Enable) +{ + PCHANNEL_DATA_COMMON ChanData = ChannelContext; + PATA_CONTROLLER Controller = ChanData->Controller; + PKINTERRUPT InterruptObject; + KIRQL OldIrql; + + if (Controller->Flags & CTRL_FLAG_IS_AHCI) + InterruptObject = Controller->InterruptObject; + else + InterruptObject = ChanData->InterruptObject; + ASSERT(InterruptObject); + + OldIrql = KeAcquireInterruptSpinLock(InterruptObject); + ChanData->EnableInterrupts(ChanData, Enable); + KeReleaseInterruptSpinLock(InterruptObject, OldIrql); +} + +VOID +AtaCtrlSetTransferMode( + _In_ PVOID ChannelContext, + _In_reads_(ATA_MAX_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList) +{ + PCHANNEL_DATA_COMMON ChanData = ChannelContext; + PATA_CONTROLLER Controller = ChanData->Controller; + KIRQL OldIrql; + ULONG i; + BOOLEAN DiscoveredNewDevice = FALSE; + + for (i = 0; i < ATA_MAX_DEVICE; ++i) + { + PCHANNEL_DEVICE_CONFIG Device = DeviceList[i]; + + if (!Device) + continue; + + if (Device->IsNewDevice) + DiscoveredNewDevice = TRUE; + + /* Apply the channel limit */ + Device->SupportedModes &= ChanData->Current.TransferModeSupported; + + if ((ChanData->ChanInfo & CHANNEL_FLAG_NO_ATAPI_DMA) && !Device->IsFixedDisk) + Device->SupportedModes &= PIO_ALL; + + /* + * If we are booting in minimal safe mode we disable DMA for ATAPI devices + * to improve system stability. + */ + if (InitSafeBootMode == 1) + { + if (!(ChanData->ChanInfo & CHANNEL_FLAG_PIO_VIA_DMA) && !Device->IsFixedDisk) + Device->SupportedModes &= PIO_ALL; + } + + /* Set initial values for mode selection: Find the fastest supported PIO and DMA mode */ + if (!_BitScanReverse(&Device->DmaMode, Device->SupportedModes & ~PIO_ALL)) + Device->DmaMode = PIO_MODE(0); + NT_VERIFY(_BitScanReverse(&Device->PioMode, Device->SupportedModes & PIO_ALL)); + } + + KeAcquireSpinLock(&Controller->Lock, &OldIrql); + + /* + * _GTF should be executed after _STM has been evaluated, + * because it is expected that ACPI BIOS will use the identity data buffers + * to construct the list of ATA commands to the drive. + * + * Therefore for any new device we have to evaluate a dummy _STM + * when the whole configuration of the channel's transfer timings + * is done at the controller minidriver. + */ + if (DiscoveredNewDevice && (ChanData->ChanInfo & CHANNEL_FLAG_HAS_ACPI_GTM)) + { + PCHANNEL_DATA_PATA PataData = ChannelContext; + + /* Evaluate _STM */ + AtaAcpiSetTimingMode(PataData->PdoExt->Common.Self, + &PataData->CurrentTimingMode, + DeviceList[0] ? DeviceList[0]->IdentifyDeviceData : NULL, + DeviceList[1] ? DeviceList[1]->IdentifyDeviceData : NULL); + } + + /* Set the PATA transfer timings */ + ChanData->SetTransferMode(Controller, ChanData->Channel, DeviceList); + + KeReleaseSpinLock(&Controller->Lock, OldIrql); +} + +CODE_SEG("PAGE") +VOID +AtaCtrlSetDeviceData( + _In_ PVOID ChannelContext, + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIDENTIFY_DEVICE_DATA IdentifyDeviceData) +{ + PCHANNEL_DATA_COMMON ChanData = ChannelContext; + PATA_CONTROLLER Controller = ChanData->Controller; + + PAGED_CODE(); + + if (!(Controller->Flags & CTRL_FLAG_SATA_HBA_ACPI)) + return; + + AtaAcpiSetDeviceData(DeviceObject, IdentifyDeviceData); +} + +CODE_SEG("PAGE") +PVOID +AtaCtrlGetInitTaskFile( + _In_ PVOID ChannelContext, + _In_ PDEVICE_OBJECT DeviceObject) +{ + PCHANNEL_DATA_COMMON ChanData = ChannelContext; + PATA_CONTROLLER Controller = ChanData->Controller; + + PAGED_CODE(); + + if ((Controller->Flags & CTRL_FLAG_SATA_HBA_ACPI) || + (ChanData->ChanInfo & CHANNEL_FLAG_HAS_ACPI_GTM)) + { + return AtaAcpiGetTaskFile(DeviceObject); + } + + return NULL; +} + +BOOLEAN +AtaCtrlDowngradeInterfaceSpeed( + _In_ PVOID ChannelContext) +{ + PCHANNEL_DATA_COMMON ChanData = ChannelContext; + PATA_CONTROLLER Controller = ChanData->Controller; + + PAGED_CODE(); + + if (Controller->Flags & CTRL_FLAG_IS_AHCI) + return AtaAhciDowngradeInterfaceSpeed(ChannelContext); + + /* Optionally, implement a SATA framework at some point in the future */ + return FALSE; +} + +VOID +AtaCtrlAbortChannel( + _In_ PVOID ChannelContext, + _In_ BOOLEAN DisableInterrupts) +{ + PCHANNEL_DATA_COMMON ChanData = ChannelContext; + + ASSERT(KeGetCurrentIrql() > DISPATCH_LEVEL); + + ChanData->ActiveSlotsBitmap = 0; + ChanData->ActiveQueuedSlotsBitmap = 0; + + /* Disable and clear pending interrupts */ + if (DisableInterrupts) + { + ChanData->EnableInterrupts(ChanData, FALSE); + +#if DBG + if (ChanData->Controller->Flags & CTRL_FLAG_IS_AHCI) + { + PVOID IoBase = ((PCHANNEL_DATA_AHCI)ChanData)->IoBase; + + DbgPrint("PxIS 0x%08lX\n", AHCI_PORT_READ(IoBase, PxInterruptStatus)); + DbgPrint("PxIE 0x%08lX\n", AHCI_PORT_READ(IoBase, PxInterruptEnable)); + DbgPrint("PxCMD 0x%08lX\n", AHCI_PORT_READ(IoBase, PxCmdStatus)); + DbgPrint("PxTFD 0x%08lX\n", AHCI_PORT_READ(IoBase, PxTaskFileData)); + DbgPrint("PxSIG 0x%08lX\n", AHCI_PORT_READ(IoBase, PxSignature)); + DbgPrint("PxSSTS 0x%08lX\n", AHCI_PORT_READ(IoBase, PxSataStatus)); + DbgPrint("PxSCTL 0x%08lX\n", AHCI_PORT_READ(IoBase, PxSataControl)); + DbgPrint("PxSERR 0x%08lX\n", AHCI_PORT_READ(IoBase, PxSataError)); + DbgPrint("PxSACT 0x%08lX\n", AHCI_PORT_READ(IoBase, PxSataActive)); + DbgPrint("PxCI 0x%08lX\n", AHCI_PORT_READ(IoBase, PxCommandIssue)); + DbgPrint("PxSNTF 0x%08lX\n", AHCI_PORT_READ(IoBase, PxSataNotification)); + DbgPrint("PxFBS 0x%08lX\n", AHCI_PORT_READ(IoBase, PxFisSwitchingControl)); + DbgPrint("PxDEVSLP 0x%08lX\n", AHCI_PORT_READ(IoBase, PxDeviceSleep)); + } +#endif + } +} + +CODE_SEG("PAGE") +PVOID +AtaCtrlPciMapBar( + _In_ PATA_CONTROLLER Controller, + _In_range_(0, PCI_TYPE0_ADDRESSES) ULONG Index, + _In_ ULONG MinimumIoLength) +{ + PAGED_CODE(); + + if (!(Controller->AccessRange[Index].Flags & RANGE_IS_VALID)) + return NULL; + + /* Validate the BAR length */ + if (MinimumIoLength != 0) + { + if (Controller->AccessRange[Index].Length < MinimumIoLength) + { + ERR("%04X:%04X.%02X: Unexpected PCI BAR #%lu length 0x%lx, minimum required 0x%lx\n", + Controller->Pci.VendorID, + Controller->Pci.DeviceID, + Controller->Pci.RevisionID, + Index, + Controller->AccessRange[Index].Length, + MinimumIoLength); + return NULL; + } + } + + if ((Controller->AccessRange[Index].Flags & RANGE_IS_MEMORY) && + !(Controller->AccessRange[Index].Flags & RANGE_IS_MAPPED)) + { + PHYSICAL_ADDRESS PhysicalAddress; + + Controller->AccessRange[Index].Flags |= RANGE_IS_MAPPED; + + PhysicalAddress.QuadPart = (ULONG_PTR)Controller->AccessRange[Index].IoBase; + Controller->AccessRange[Index].IoBase = MmMapIoSpace(PhysicalAddress, + Controller->AccessRange[Index].Length, + MmNonCached); + } + + return Controller->AccessRange[Index].IoBase; +} static CODE_SEG("PAGE") -NTSTATUS -PciIdeXFdoParseResources( - _In_ PFDO_DEVICE_EXTENSION FdoExtension, - _In_ PCM_RESOURCE_LIST ResourcesTranslated) +BOOLEAN +AtaCtrlPciGetNextBarIndex( + _In_ PPCI_COMMON_HEADER PciData, + _Inout_ PULONG Bar) { - PCM_PARTIAL_RESOURCE_DESCRIPTOR BusMasterDescriptor = NULL; - PVOID IoBase; - ULONG i; + PAGED_CODE(); + + for (; *Bar < PCI_TYPE0_ADDRESSES; (*Bar)++) + { + ULONG IoBase = PciData->u.type0.BaseAddresses[*Bar]; + + if (IoBase & PCI_ADDRESS_IO_SPACE) + IoBase &= ~PCI_ADDRESS_IO_SPACE; + else + IoBase &= ~(PCI_ADDRESS_MEMORY_TYPE_MASK | PCI_ADDRESS_MEMORY_PREFETCHABLE); + + /* Look for a valid BAR */ + if (IoBase == 0) + continue; + + return TRUE; + } + + return FALSE; +} + +static +CODE_SEG("PAGE") +VOID +AtaCtrlPciAssignResources( + _In_ PATA_CONTROLLER Controller, + _In_ PCM_RESOURCE_LIST ResourcesTranslated, + _In_ PPCI_COMMON_HEADER PciData) +{ + ULONG i, Bar; PAGED_CODE(); if (!ResourcesTranslated) - return STATUS_INVALID_PARAMETER; + return; for (i = 0; i < ResourcesTranslated->List[0].PartialResourceList.Count; ++i) { - PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; + PCM_PARTIAL_RESOURCE_DESCRIPTOR Desc; - Descriptor = &ResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[i]; - switch (Descriptor->Type) + Desc = &ResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[i]; + if (Desc->Type == CmResourceTypeInterrupt) { - case CmResourceTypePort: - case CmResourceTypeMemory: - { - switch (Descriptor->u.Port.Length) - { - /* Bus master port base */ - case 16: - { - if (!BusMasterDescriptor) - BusMasterDescriptor = Descriptor; - break; - } - - default: - break; - } - } - - default: - break; + RtlCopyMemory(&Controller->InterruptDesc, Desc, sizeof(*Desc)); + break; } } - if (!BusMasterDescriptor) - return STATUS_DEVICE_CONFIGURATION_ERROR; - - if ((BusMasterDescriptor->Type == CmResourceTypePort) && - (BusMasterDescriptor->Flags & CM_RESOURCE_PORT_IO)) + for (i = 0, Bar = 0; i < ResourcesTranslated->List[0].PartialResourceList.Count; ++i) { - IoBase = (PVOID)(ULONG_PTR)BusMasterDescriptor->u.Port.Start.QuadPart; - } - else - { - IoBase = MmMapIoSpace(BusMasterDescriptor->u.Memory.Start, 16, MmNonCached); - if (!IoBase) - return STATUS_INSUFFICIENT_RESOURCES; + PCM_PARTIAL_RESOURCE_DESCRIPTOR Desc; + ULONG CurrBar, NextBar; - FdoExtension->IoBaseMapped = TRUE; - } - FdoExtension->BusMasterPortBase = IoBase; + Desc = &ResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[i]; + if ((Desc->Type != CmResourceTypePort) && (Desc->Type != CmResourceTypeMemory)) + continue; - return STATUS_SUCCESS; + if (!AtaCtrlPciGetNextBarIndex(PciData, &Bar)) + break; + + CurrBar = PciData->u.type0.BaseAddresses[Bar]; + if (Bar < (PCI_TYPE0_ADDRESSES - 1)) + NextBar = PciData->u.type0.BaseAddresses[Bar + 1]; + else + NextBar = 0; + + if (CurrBar & PCI_ADDRESS_IO_SPACE) + { + if (Desc->Type != CmResourceTypePort) + continue; + + if (Desc->u.Port.Start.QuadPart != (CurrBar & PCI_ADDRESS_IO_ADDRESS_MASK)) + continue; + + INFO("BAR[%lu]: I/O %I64X Len %lX\n", + Bar, Desc->u.Port.Start.QuadPart, Desc->u.Port.Length); + Controller->AccessRange[Bar].Flags |= RANGE_IS_VALID; + Controller->AccessRange[Bar].IoBase = (PVOID)(ULONG_PTR)Desc->u.Port.Start.QuadPart; + Controller->AccessRange[Bar].Length = Desc->u.Port.Length; + } + else + { + if (Desc->Type != CmResourceTypeMemory) + continue; + + if (Desc->u.Memory.Start.LowPart != (CurrBar & PCI_ADDRESS_MEMORY_ADDRESS_MASK)) + continue; + + if ((CurrBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT) + { + if (Desc->u.Memory.Start.HighPart != NextBar) + continue; + } + else + { + if (Desc->u.Memory.Start.HighPart != 0) + continue; + } + + INFO("BAR[%lu]: MEM %I64X Len %lX\n", + Bar, Desc->u.Memory.Start.QuadPart, Desc->u.Memory.Length); + Controller->AccessRange[Bar].Flags |= RANGE_IS_VALID | RANGE_IS_MEMORY; + Controller->AccessRange[Bar].IoBase = (PVOID)(ULONG_PTR)Desc->u.Memory.Start.QuadPart; + Controller->AccessRange[Bar].Length = Desc->u.Memory.Length; + } + + ++Bar; + } +} + +static +CODE_SEG("PAGE") +VOID +AtaCtrlPciSaveData( + _Out_ PATA_CONTROLLER Controller, + _In_ PPCI_COMMON_HEADER PciData) +{ + PAGED_CODE(); + + Controller->Pci.VendorID = PciData->VendorID; + Controller->Pci.DeviceID = PciData->DeviceID; + Controller->Pci.Command = PciData->Command; + Controller->Pci.RevisionID = PciData->RevisionID; + Controller->Pci.ProgIf = PciData->ProgIf; + Controller->Pci.SubClass = PciData->SubClass; + Controller->Pci.BaseClass = PciData->BaseClass; + Controller->Pci.CacheLineSize = PciData->CacheLineSize; + Controller->Pci.SubVendorID = PciData->u.type0.SubVendorID; + Controller->Pci.SubSystemID = PciData->u.type0.SubSystemID; } static CODE_SEG("PAGE") NTSTATUS -PciIdeXFdoStartDevice( - _In_ PFDO_DEVICE_EXTENSION FdoExtension, - _In_ PIRP Irp) +AtaCtrlPciCollectInformation( + _In_ PFDO_DEVICE_EXTENSION FdoExt, + _In_ PCM_RESOURCE_LIST ResourcesTranslated) { - NTSTATUS Status; - PIO_STACK_LOCATION IoStack; + PATA_CONTROLLER Controller = &FdoExt->Controller; + UCHAR Buffer[RTL_SIZEOF_THROUGH_FIELD(PCI_COMMON_HEADER, u.type0.SubSystemID)]; + PPCI_COMMON_HEADER PciData = (PPCI_COMMON_HEADER)Buffer; // Partial PCI header + ULONG BytesRead; PAGED_CODE(); - if (!NT_VERIFY(IoForwardIrpSynchronously(FdoExtension->Ldo, Irp))) + BytesRead = Controller->GetBusData(Controller->BusInterfaceContext, + PCI_WHICHSPACE_CONFIG, + Buffer, + 0, + sizeof(Buffer)); + if (BytesRead != sizeof(Buffer)) + return STATUS_IO_DEVICE_ERROR; + + AtaCtrlPciSaveData(Controller, PciData); + AtaCtrlPciAssignResources(Controller, ResourcesTranslated, PciData); + + return STATUS_SUCCESS; +} + +CODE_SEG("PAGE") +NTSTATUS +PciIdeXFdoStartDevice( + _In_ PVOID ControllerContext, + _In_ PCM_RESOURCE_LIST ResourcesTranslated) +{ + PFDO_DEVICE_EXTENSION FdoExt = ControllerContext; + PATA_CONTROLLER Controller = &FdoExt->Controller; + NTSTATUS Status; + + PAGED_CODE(); + + Controller->Flags &= CTRL_FLAG_NON_PNP; + Controller->Flags |= CTRL_FLAG_NATIVE_PCI; + Controller->QueueDepth = 1; // PATA_CHANNEL_SLOT + Controller->AlignmentRequirement = ATA_MIN_BUFFER_ALIGNMENT; + Controller->AttachChannel = PciIdeAttachChannel; + Controller->FreeResources = NULL; + Controller->Start = NULL; + Controller->Stop = NULL; + Controller->ChannelEnableBits = NULL; + +#if defined(ATA_DETECT_LEGACY_DEVICES) + if (Controller->Flags & CTRL_FLAG_NON_PNP) { - return STATUS_UNSUCCESSFUL; + Status = PataGetControllerProperties(Controller, ResourcesTranslated); + } + else +#endif + { + Status = AtaCtrlPciCollectInformation(FdoExt, ResourcesTranslated); + if (!NT_SUCCESS(Status)) + { + ERR("Failed to collect PCI information 0x%lx\n", Status); + return Status; + } + + INFO("Starting controller %04X:%04X.%02X-%04X.%04X-%02X.%02X.%02X\n", + Controller->Pci.VendorID, + Controller->Pci.DeviceID, + Controller->Pci.RevisionID, + Controller->Pci.SubVendorID, + Controller->Pci.SubSystemID, + Controller->Pci.BaseClass, + Controller->Pci.SubClass, + Controller->Pci.ProgIf); + + Status = AhciGetControllerProperties(Controller); + if (Status == STATUS_NO_MATCH) + Status = PciIdeGetControllerProperties(Controller); } - Status = Irp->IoStatus.Status; if (!NT_SUCCESS(Status)) { + ERR("Failed to match ATA controller with status %lx\n", Status); return Status; } - IoStack = IoGetCurrentIrpStackLocation(Irp); - - Status = PciIdeXFdoParseResources(FdoExtension, - IoStack->Parameters.StartDevice.AllocatedResourcesTranslated); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to parse resources 0x%lx\n", Status); - return Status; - } - - Status = PciIdeXStartMiniport(FdoExtension); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Miniport initialization failed 0x%lx\n", Status); - return Status; - } + if (Controller->Start) + AtaCtrlCallStartController(Controller); return STATUS_SUCCESS; } @@ -126,13 +523,49 @@ VOID PciIdeXFdoFreeResources( _In_ PFDO_DEVICE_EXTENSION FdoExtension) { + PATA_CONTROLLER Controller = &FdoExtension->Controller; + ULONG i; + PAGED_CODE(); - if (FdoExtension->IoBaseMapped) + if (Controller->Stop) + AtaCtrlCallStopController(Controller); + + if (Controller->FreeResources) + Controller->FreeResources(Controller); + + Controller->InterruptObject = NULL; + + for (i = 0; i < RTL_NUMBER_OF(Controller->AccessRange); ++i) { - MmUnmapIoSpace(FdoExtension->BusMasterPortBase, 16); - FdoExtension->IoBaseMapped = FALSE; + if ((Controller->AccessRange[i].Flags & RANGE_IS_VALID) && + (Controller->AccessRange[i].Flags & RANGE_IS_MAPPED)) + { + MmUnmapIoSpace(Controller->AccessRange[i].IoBase, Controller->AccessRange[i].Length); + } + + Controller->AccessRange[i].Flags = 0; } + + if (Controller->ChanDataBlock) + { + ExFreePoolWithTag(Controller->ChanDataBlock, TAG_PCIIDEX); + Controller->ChanDataBlock = NULL; + } + + if (Controller->HwSyncObject) + { + IoDeleteController(Controller->HwSyncObject); + Controller->HwSyncObject = NULL; + } + + if (Controller->HwExt) + { + ExFreePoolWithTag(Controller->HwExt, TAG_PCIIDEX); + Controller->HwExt = NULL; + } + + Controller->InterruptDesc.Type = 0; } static @@ -148,67 +581,33 @@ PciIdeXFdoStopDevice( return STATUS_SUCCESS; } -static CODE_SEG("PAGE") -NTSTATUS +VOID PciIdeXFdoRemoveDevice( - _In_ PFDO_DEVICE_EXTENSION FdoExtension, - _In_ PIRP Irp) + _In_ PVOID ControllerContext) { - PPDO_DEVICE_EXTENSION PdoExtension; - NTSTATUS Status; - ULONG i; + PFDO_DEVICE_EXTENSION FdoExt = ControllerContext; + PLIST_ENTRY ListEntry; PAGED_CODE(); - PciIdeXFdoFreeResources(FdoExtension); + ExAcquireFastMutex(&FdoExt->PdoListSyncMutex); - ExAcquireFastMutex(&FdoExtension->DeviceSyncMutex); - - for (i = 0; i < MAX_IDE_CHANNEL; ++i) + for (ListEntry = FdoExt->PdoListHead.Flink; + ListEntry != &FdoExt->PdoListHead; + ListEntry = ListEntry->Flink) { - PdoExtension = FdoExtension->Channels[i]; + PPDO_DEVICE_EXTENSION PdoExt; - if (PdoExtension) - { - IoDeleteDevice(PdoExtension->Common.Self); + PdoExt = CONTAINING_RECORD(ListEntry, PDO_DEVICE_EXTENSION, ListEntry); - FdoExtension->Channels[i] = NULL; - break; - } + PdoExt->Flags |= PDO_FLAG_REPORTED_MISSING; + PciIdeXPdoRemoveDevice(PdoExt, NULL, TRUE); } - ExReleaseFastMutex(&FdoExtension->DeviceSyncMutex); + ExReleaseFastMutex(&FdoExt->PdoListSyncMutex); - Irp->IoStatus.Status = STATUS_SUCCESS; - IoSkipCurrentIrpStackLocation(Irp); - Status = IoCallDriver(FdoExtension->Ldo, Irp); - - IoDetachDevice(FdoExtension->Ldo); - IoDeleteDevice(FdoExtension->Common.Self); - - return Status; -} - -static -CODE_SEG("PAGE") -NTSTATUS -PciIdeXFdoQueryPnpDeviceState( - _In_ PFDO_DEVICE_EXTENSION FdoExtension, - _In_ PIRP Irp) -{ - PAGED_CODE(); - - if (FdoExtension->Common.PageFiles || - FdoExtension->Common.HibernateFiles || - FdoExtension->Common.DumpFiles) - { - Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE; - } - - Irp->IoStatus.Status = STATUS_SUCCESS; - - return STATUS_SUCCESS; + PciIdeXFdoFreeResources(FdoExt); } static @@ -220,20 +619,20 @@ PciIdeXPdoCreateDevice( { NTSTATUS Status; UNICODE_STRING DeviceName; - WCHAR DeviceNameBuffer[sizeof("\\Device\\Ide\\PciIde999Channel9-FFF")]; + WCHAR DeviceNameBuffer[sizeof("\\Device\\Ide\\PciIde99999Channel99-FFF")]; PDEVICE_OBJECT Pdo; PPDO_DEVICE_EXTENSION PdoExtension; ULONG Alignment; - static ULONG DeviceNumber = 0; + static ULONG PdoNumber = 0; PAGED_CODE(); Status = RtlStringCbPrintfW(DeviceNameBuffer, sizeof(DeviceNameBuffer), - L"\\Device\\Ide\\PciIde%uChannel%u-%x", + L"\\Device\\Ide\\PciIde%luChannel%lu-%lx", FdoExtension->ControllerNumber, ChannelNumber, - DeviceNumber++); + PdoNumber++); ASSERT(NT_SUCCESS(Status)); RtlInitUnicodeString(&DeviceName, DeviceNameBuffer); @@ -246,139 +645,158 @@ PciIdeXPdoCreateDevice( &Pdo); if (!NT_SUCCESS(Status)) { - DPRINT1("Failed to create PDO 0x%lx\n", Status); + ERR("Failed to create PDO 0x%lx\n", Status); return NULL; } - DPRINT("Created device object %p '%wZ'\n", Pdo, &DeviceName); + INFO("Created device object %p '%wZ'\n", Pdo, &DeviceName); /* DMA buffers alignment */ - Alignment = FdoExtension->Properties.AlignmentRequirement; + Alignment = FdoExtension->Controller.AlignmentRequirement; Alignment = max(Alignment, FdoExtension->Common.Self->AlignmentRequirement); - Alignment = max(Alignment, FILE_WORD_ALIGNMENT); + Alignment = max(Alignment, ATA_MIN_BUFFER_ALIGNMENT); Pdo->AlignmentRequirement = Alignment; PdoExtension = Pdo->DeviceExtension; RtlZeroMemory(PdoExtension, sizeof(*PdoExtension)); PdoExtension->Common.Self = Pdo; + PdoExtension->Common.FdoExt = FdoExtension; + IoInitializeRemoveLock(&PdoExtension->Common.RemoveLock, TAG_PCIIDEX, 0, 0); + PdoExtension->Channel = ChannelNumber; - PdoExtension->ParentController = FdoExtension; + PdoExtension->ChanData = FdoExtension->Controller.Channels[ChannelNumber]; + ASSERT(PdoExtension->ChanData); + PdoExtension->ChanData->PdoExt = PdoExtension; Pdo->Flags &= ~DO_DEVICE_INITIALIZING; return PdoExtension; } +CODE_SEG("PAGE") +IDE_CHANNEL_STATE +PciIdeXGetChannelState( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel) +{ + PAGED_CODE(); + + if (Controller->Flags & CTRL_FLAG_IS_AHCI) + return ChannelStateUnknown; + + return PciIdeGetChannelState(Controller, Channel); +} + static CODE_SEG("PAGE") NTSTATUS PciIdeXFdoQueryBusRelations( - _In_ PFDO_DEVICE_EXTENSION FdoExtension, + _In_ PFDO_DEVICE_EXTENSION FdoExt, _In_ PIRP Irp) { - PPDO_DEVICE_EXTENSION PdoExtension; - IDE_CHANNEL_STATE ChannelState; + PATA_CONTROLLER Controller = &FdoExt->Controller; PDEVICE_RELATIONS DeviceRelations; - ULONG i; + PLIST_ENTRY ListEntry; + ULONG i, Size, ChannelBitmap = 0; PAGED_CODE(); - DeviceRelations = ExAllocatePoolWithTag(PagedPool, - FIELD_OFFSET(DEVICE_RELATIONS, - Objects[MAX_IDE_CHANNEL]), - TAG_PCIIDEX); + /* + * Preallocate the device relations structure we need for the QBR IRP, + * because we don't want to be in a state where allocation fails. + */ + Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects[MAX_CHANNELS]); + DeviceRelations = ExAllocatePoolUninitialized(PagedPool, Size, TAG_PCIIDEX); if (!DeviceRelations) return STATUS_INSUFFICIENT_RESOURCES; - DeviceRelations->Count = 0; + ExAcquireFastMutex(&FdoExt->PdoListSyncMutex); - ExAcquireFastMutex(&FdoExtension->DeviceSyncMutex); - - for (i = 0; i < MAX_IDE_CHANNEL; ++i) + /* Check existing ports */ + for (ListEntry = FdoExt->PdoListHead.Flink; + ListEntry != &FdoExt->PdoListHead; + ListEntry = ListEntry->Flink) { - PdoExtension = FdoExtension->Channels[i]; + PPDO_DEVICE_EXTENSION PdoExt; + IDE_CHANNEL_STATE ChannelState; + ULONG Channel; - /* Ignore disabled channels */ - ChannelState = PciIdeXChannelState(FdoExtension, i); + PdoExt = CONTAINING_RECORD(ListEntry, PDO_DEVICE_EXTENSION, ListEntry); + Channel = PdoExt->Channel; + + ChannelBitmap |= 1 << Channel; + + ChannelState = PciIdeXGetChannelState(Controller, Channel); if (ChannelState == ChannelDisabled) { - if (PdoExtension) - { - PdoExtension->ReportedMissing = TRUE; - } + INFO("%04X:%04X.%02X: Skip disabled channel %lu\n", + Controller->Pci.VendorID, + Controller->Pci.DeviceID, + Controller->Pci.RevisionID, + Channel); - DPRINT("Channel %lu is disabled\n", i); + PdoExt->Flags |= PDO_FLAG_NOT_PRESENT; + continue; + } + } + + /* Enumerate the remaining ports */ + ChannelBitmap = Controller->ChannelBitmap & ~ChannelBitmap; + for (i = 0; i < MAX_CHANNELS; ++i) + { + PPDO_DEVICE_EXTENSION PdoExt; + IDE_CHANNEL_STATE ChannelState; + + if (!(ChannelBitmap & (1 << i))) + continue; + + ChannelState = PciIdeXGetChannelState(Controller, i); + if (ChannelState == ChannelDisabled) + { + INFO("%04X:%04X.%02X: Skip disabled channel %lu\n", + Controller->Pci.VendorID, + Controller->Pci.DeviceID, + Controller->Pci.RevisionID, + i); continue; } /* Need to create a PDO */ - if (!PdoExtension) + PdoExt = PciIdeXPdoCreateDevice(FdoExt, i); + if (!PdoExt) { - PdoExtension = PciIdeXPdoCreateDevice(FdoExtension, i); - - FdoExtension->Channels[i] = PdoExtension; + /* We are out of memory, trying to continue process the QBR IRP anyway */ + continue; } - if (PdoExtension && !PdoExtension->ReportedMissing) - { - DeviceRelations->Objects[DeviceRelations->Count++] = PdoExtension->Common.Self; - ObReferenceObject(PdoExtension->Common.Self); - } + InsertTailList(&FdoExt->PdoListHead, &PdoExt->ListEntry); } - ExReleaseFastMutex(&FdoExtension->DeviceSyncMutex); + /* Initialize the device relations structure */ + DeviceRelations->Count = 0; + for (ListEntry = FdoExt->PdoListHead.Flink; + ListEntry != &FdoExt->PdoListHead; + ListEntry = ListEntry->Flink) + { + PPDO_DEVICE_EXTENSION PdoExt; + + PdoExt = CONTAINING_RECORD(ListEntry, PDO_DEVICE_EXTENSION, ListEntry); + if (PdoExt->Flags & PDO_FLAG_NOT_PRESENT) + { + PdoExt->Flags |= PDO_FLAG_REPORTED_MISSING; + continue; + } + + DeviceRelations->Objects[DeviceRelations->Count++] = PdoExt->Common.Self; + ObReferenceObject(PdoExt->Common.Self); + } + + ExReleaseFastMutex(&FdoExt->PdoListSyncMutex); Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations; return STATUS_SUCCESS; } -static -CODE_SEG("PAGE") -NTSTATUS -PciIdeXFdoQueryDeviceUsageNotification( - _In_ PFDO_DEVICE_EXTENSION FdoExtension, - _In_ PIRP Irp) -{ - PIO_STACK_LOCATION IoStack; - NTSTATUS Status; - volatile LONG* Counter; - - PAGED_CODE(); - - if (!NT_VERIFY(IoForwardIrpSynchronously(FdoExtension->Ldo, Irp))) - { - return STATUS_UNSUCCESSFUL; - } - Status = Irp->IoStatus.Status; - if (!NT_SUCCESS(Status)) - { - return Status; - } - - IoStack = IoGetCurrentIrpStackLocation(Irp); - switch (IoStack->Parameters.UsageNotification.Type) - { - case DeviceUsageTypePaging: - Counter = &FdoExtension->Common.PageFiles; - break; - - case DeviceUsageTypeHibernation: - Counter = &FdoExtension->Common.HibernateFiles; - break; - - case DeviceUsageTypeDumpFile: - Counter = &FdoExtension->Common.DumpFiles; - break; - - default: - return Status; - } - - IoAdjustPagingPathCount(Counter, IoStack->Parameters.UsageNotification.InPath); - - return STATUS_SUCCESS; -} - static CODE_SEG("PAGE") NTSTATUS @@ -391,32 +809,102 @@ PciIdeXFdoQueryInterface( if (IsEqualGUIDAligned(IoStack->Parameters.QueryInterface.InterfaceType, &GUID_TRANSLATOR_INTERFACE_STANDARD)) { + PATA_CONTROLLER Controller = &FdoExtension->Controller; + PTRANSLATOR_INTERFACE TranslatorInterface; CM_RESOURCE_TYPE ResourceType; ULONG BusNumber; - ResourceType = (ULONG_PTR)IoStack->Parameters.QueryInterface.InterfaceSpecificData; - /* In native mode the IDE controller does not use any legacy interrupt resources */ - if (FdoExtension->InNativeMode || - ResourceType != CmResourceTypeInterrupt || - IoStack->Parameters.QueryInterface.Size < sizeof(TRANSLATOR_INTERFACE)) - { + if (Controller->Flags & (CTRL_FLAG_NATIVE_PCI | CTRL_FLAG_NON_PNP)) return STATUS_NOT_SUPPORTED; - } + + if (IoStack->Parameters.QueryInterface.Size < sizeof(*TranslatorInterface)) + return STATUS_NOT_SUPPORTED; + + ResourceType = PtrToUlong(IoStack->Parameters.QueryInterface.InterfaceSpecificData); + if (ResourceType != CmResourceTypeInterrupt) + return STATUS_NOT_SUPPORTED; + + TranslatorInterface = (PTRANSLATOR_INTERFACE)IoStack->Parameters.QueryInterface.Interface; return HalGetInterruptTranslator(PCIBus, 0, InterfaceTypeUndefined, - sizeof(TRANSLATOR_INTERFACE), + sizeof(*TranslatorInterface), IoStack->Parameters.QueryInterface.Version, - (PTRANSLATOR_INTERFACE)IoStack-> - Parameters.QueryInterface.Interface, + TranslatorInterface, &BusNumber); } return STATUS_NOT_SUPPORTED; } +static +CODE_SEG("PAGE") +NTSTATUS +PciIdeXFdoQueryId( + _In_ PFDO_DEVICE_EXTENSION FdoExt, + _In_ PIRP Irp, + _In_ PIO_STACK_LOCATION IoStack) +{ + NTSTATUS Status; + + PAGED_CODE(); + + switch (IoStack->Parameters.QueryId.IdType) + { + case BusQueryHardwareIDs: + case BusQueryCompatibleIDs: + { + if (!NT_VERIFY(IoForwardIrpSynchronously(FdoExt->Common.LowerDeviceObject, Irp))) + { + Status = STATUS_UNSUCCESSFUL; + break; + } + Status = Irp->IoStatus.Status; + + /* + * We provide a generic identifier necessary to install the device + * in case our FDO is root-enumerated device. + */ + if (Status == STATUS_NOT_SUPPORTED) + { + static const WCHAR IdeGenericId[] = L"*PNP0600\0"; // multi-string + PWCHAR Buffer; + + Buffer = ExAllocatePoolUninitialized(PagedPool, sizeof(IdeGenericId), TAG_PCIIDEX); + if (!Buffer) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + RtlCopyMemory(Buffer, IdeGenericId, sizeof(IdeGenericId)); + + INFO("Hardware/Compatible ID: '%S'\n", Buffer); + + Irp->IoStatus.Information = (ULONG_PTR)Buffer; + Status = STATUS_SUCCESS; + } + break; + } + + default: + { + IoSkipCurrentIrpStackLocation(Irp); + Status = IoCallDriver(FdoExt->Common.LowerDeviceObject, Irp); + + IoReleaseRemoveLock(&FdoExt->Common.RemoveLock, Irp); + return Status; + } + } + + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + IoReleaseRemoveLock(&FdoExt->Common.RemoveLock, Irp); + return Status; +} + CODE_SEG("PAGE") NTSTATUS PciIdeXFdoDispatchPnp( @@ -428,17 +916,33 @@ PciIdeXFdoDispatchPnp( PAGED_CODE(); + Status = IoAcquireRemoveLock(&FdoExtension->Common.RemoveLock, Irp); + if (!NT_SUCCESS(Status)) + { + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; + } + IoStack = IoGetCurrentIrpStackLocation(Irp); switch (IoStack->MinorFunction) { case IRP_MN_START_DEVICE: { - Status = PciIdeXFdoStartDevice(FdoExtension, Irp); + if (!NT_VERIFY(IoForwardIrpSynchronously(FdoExtension->Common.LowerDeviceObject, Irp))) + { + Status = STATUS_UNSUCCESSFUL; + goto CompleteIrp; + } + Status = Irp->IoStatus.Status; + if (!NT_SUCCESS(Status)) + goto CompleteIrp; - Irp->IoStatus.Status = Status; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return Status; + Status = PciIdeXFdoStartDevice(FdoExtension, + IoStack->Parameters. + StartDevice.AllocatedResourcesTranslated); + goto CompleteIrp; } case IRP_MN_STOP_DEVICE: @@ -448,11 +952,23 @@ PciIdeXFdoDispatchPnp( } case IRP_MN_REMOVE_DEVICE: - return PciIdeXFdoRemoveDevice(FdoExtension, Irp); + { + IoReleaseRemoveLockAndWait(&FdoExtension->Common.RemoveLock, Irp); + + PciIdeXFdoRemoveDevice(FdoExtension); + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoSkipCurrentIrpStackLocation(Irp); + Status = IoCallDriver(FdoExtension->Common.LowerDeviceObject, Irp); + + IoDetachDevice(FdoExtension->Common.LowerDeviceObject); + IoDeleteDevice(FdoExtension->Common.Self); + return Status; + } case IRP_MN_QUERY_PNP_DEVICE_STATE: { - Status = PciIdeXFdoQueryPnpDeviceState(FdoExtension, Irp); + Status = PciIdeXPnpQueryPnpDeviceState(&FdoExtension->Common, Irp); break; } @@ -463,12 +979,7 @@ PciIdeXFdoDispatchPnp( Status = PciIdeXFdoQueryBusRelations(FdoExtension, Irp); if (!NT_SUCCESS(Status)) - { - Irp->IoStatus.Status = Status; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return Status; - } + goto CompleteIrp; Irp->IoStatus.Status = Status; break; @@ -476,7 +987,7 @@ PciIdeXFdoDispatchPnp( case IRP_MN_DEVICE_USAGE_NOTIFICATION: { - Status = PciIdeXFdoQueryDeviceUsageNotification(FdoExtension, Irp); + Status = PciIdeXPnpQueryDeviceUsageNotification(&FdoExtension->Common, Irp); break; } @@ -498,10 +1009,23 @@ PciIdeXFdoDispatchPnp( Irp->IoStatus.Status = STATUS_SUCCESS; break; + case IRP_MN_QUERY_ID: + return PciIdeXFdoQueryId(FdoExtension, Irp, IoStack); + default: break; } IoSkipCurrentIrpStackLocation(Irp); - return IoCallDriver(FdoExtension->Ldo, Irp); + Status = IoCallDriver(FdoExtension->Common.LowerDeviceObject, Irp); + + IoReleaseRemoveLock(&FdoExtension->Common.RemoveLock, Irp); + return Status; + +CompleteIrp: + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + IoReleaseRemoveLock(&FdoExtension->Common.RemoveLock, Irp); + return Status; } diff --git a/drivers/storage/ide/pciidex/miniport.c b/drivers/storage/ide/pciidex/miniport.c index 0d8ed8044fb..97dee8bbe9d 100644 --- a/drivers/storage/ide/pciidex/miniport.c +++ b/drivers/storage/ide/pciidex/miniport.c @@ -7,42 +7,9 @@ #include "pciidex.h" -#define NDEBUG -#include - /** @brief Global debugging level. Valid values are between 0 (Error) and 3 (Trace). */ ULONG PciIdeDebug = 0; -CODE_SEG("PAGE") -NTSTATUS -PciIdeXStartMiniport( - _In_ PFDO_DEVICE_EXTENSION FdoExtension) -{ - PPCIIDEX_DRIVER_EXTENSION DriverExtension; - NTSTATUS Status; - - PAGED_CODE(); - - if (FdoExtension->MiniportStarted) - return STATUS_SUCCESS; - - DPRINT("Starting miniport\n"); - - DriverExtension = IoGetDriverObjectExtension(FdoExtension->DriverObject, - FdoExtension->DriverObject); - ASSERT(DriverExtension); - - FdoExtension->Properties.Size = sizeof(IDE_CONTROLLER_PROPERTIES); - FdoExtension->Properties.ExtensionSize = DriverExtension->MiniControllerExtensionSize; - Status = DriverExtension->HwGetControllerProperties(FdoExtension->MiniControllerExtension, - &FdoExtension->Properties); - if (!NT_SUCCESS(Status)) - return Status; - - FdoExtension->MiniportStarted = TRUE; - return STATUS_SUCCESS; -} - CODE_SEG("PAGE") IDE_CHANNEL_STATE PciIdeXChannelState( @@ -95,21 +62,21 @@ PciIdeXGetBusData( _In_ ULONG ConfigDataOffset, _In_ ULONG BufferLength) { - PFDO_DEVICE_EXTENSION FdoExtension; + PFDO_DEVICE_EXTENSION FdoExt; + PATA_CONTROLLER Controller; ULONG BytesRead; - DPRINT("PciIdeXGetBusData(%p %p 0x%lx 0x%lx)\n", - DeviceExtension, Buffer, ConfigDataOffset, BufferLength); + INFO("PciIdeXGetBusData(%p %p 0x%lx 0x%lx)\n", + DeviceExtension, Buffer, ConfigDataOffset, BufferLength); - FdoExtension = CONTAINING_RECORD(DeviceExtension, - FDO_DEVICE_EXTENSION, - MiniControllerExtension); + FdoExt = CONTAINING_RECORD(DeviceExtension, FDO_DEVICE_EXTENSION, MiniControllerExtension); + Controller = &FdoExt->Controller; - BytesRead = (*FdoExtension->BusInterface.GetBusData)(FdoExtension->BusInterface.Context, - PCI_WHICHSPACE_CONFIG, - Buffer, - ConfigDataOffset, - BufferLength); + BytesRead = Controller->GetBusData(Controller->BusInterfaceContext, + PCI_WHICHSPACE_CONFIG, + Buffer, + ConfigDataOffset, + BufferLength); if (BytesRead != BufferLength) return STATUS_UNSUCCESSFUL; @@ -126,24 +93,38 @@ PciIdeXSetBusData( _In_ ULONG ConfigDataOffset, _In_ ULONG BufferLength) { - PFDO_DEVICE_EXTENSION FdoExtension; + PFDO_DEVICE_EXTENSION FdoExt; + PATA_CONTROLLER Controller; + UCHAR LocalBuffer[4]; ULONG i, BytesWritten; PUCHAR CurrentBuffer; KIRQL OldIrql; NTSTATUS Status; - DPRINT("PciIdeXSetBusData(%p %p %p 0x%lx 0x%lx)\n", - DeviceExtension, Buffer, DataMask, ConfigDataOffset, BufferLength); + INFO("PciIdeXSetBusData(%p %p %p 0x%lx 0x%lx)\n", + DeviceExtension, Buffer, DataMask, ConfigDataOffset, BufferLength); - CurrentBuffer = ExAllocatePoolWithTag(NonPagedPool, BufferLength, TAG_PCIIDEX); - if (!CurrentBuffer) - return STATUS_INSUFFICIENT_RESOURCES; + /* Optimize the common case (4-byte PCI access) */ + if (BufferLength <= sizeof(LocalBuffer)) + { + CurrentBuffer = LocalBuffer; + } + else + { + CurrentBuffer = ExAllocatePoolUninitialized(NonPagedPool, BufferLength, TAG_PCIIDEX); + if (!CurrentBuffer) + return STATUS_INSUFFICIENT_RESOURCES; + } - FdoExtension = CONTAINING_RECORD(DeviceExtension, - FDO_DEVICE_EXTENSION, - MiniControllerExtension); + FdoExt = CONTAINING_RECORD(DeviceExtension, FDO_DEVICE_EXTENSION, MiniControllerExtension); + Controller = &FdoExt->Controller; - KeAcquireSpinLock(&FdoExtension->BusDataLock, &OldIrql); + /* + * This spinlock protects the PCI configuration space against concurrent modifications. + * For example, the PIIX register SIDETIM (0x44) is a byte-sized register + * and controls both of the IDE channels. + */ + KeAcquireSpinLock(&Controller->Lock, &OldIrql); Status = PciIdeXGetBusData(DeviceExtension, Buffer, ConfigDataOffset, BufferLength); if (!NT_SUCCESS(Status)) @@ -155,19 +136,40 @@ PciIdeXSetBusData( (((PUCHAR)DataMask)[i] & ((PUCHAR)Buffer)[i]); } - BytesWritten = (*FdoExtension->BusInterface.SetBusData)(FdoExtension->BusInterface.Context, - PCI_WHICHSPACE_CONFIG, - CurrentBuffer, - ConfigDataOffset, - BufferLength); + BytesWritten = Controller->SetBusData(Controller->BusInterfaceContext, + PCI_WHICHSPACE_CONFIG, + CurrentBuffer, + ConfigDataOffset, + BufferLength); if (BytesWritten != BufferLength) Status = STATUS_UNSUCCESSFUL; else Status = STATUS_SUCCESS; Cleanup: - KeReleaseSpinLock(&FdoExtension->BusDataLock, OldIrql); + KeReleaseSpinLock(&Controller->Lock, OldIrql); - ExFreePoolWithTag(CurrentBuffer, TAG_PCIIDEX); + if (CurrentBuffer != LocalBuffer) + ExFreePoolWithTag(CurrentBuffer, TAG_PCIIDEX); + return Status; +} + +NTSTATUS +NTAPI +PciIdexGetBusLocation( + _In_ PVOID DeviceExtension, + _Out_ PULONG BusLocation) +{ + PFDO_DEVICE_EXTENSION FdoExt; + ULONG Length; + NTSTATUS Status; + + FdoExt = CONTAINING_RECORD(DeviceExtension, FDO_DEVICE_EXTENSION, MiniControllerExtension); + + Status = IoGetDeviceProperty(FdoExt->Pdo, + DevicePropertyAddress, + sizeof(*BusLocation), + BusLocation, + &Length); return Status; } diff --git a/drivers/storage/ide/pciidex/pata.h b/drivers/storage/ide/pciidex/pata.h new file mode 100644 index 00000000000..dda0e17813b --- /dev/null +++ b/drivers/storage/ide/pciidex/pata.h @@ -0,0 +1,323 @@ +/* + * PROJECT: ReactOS ATA Bus Driver + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: PATA definitions + * COPYRIGHT: Copyright 2026 + */ + +#pragma once + +#define PCI_VEN_ATI 0x1002 +#define PCI_VEN_AMD 0x1022 +#define PCI_VEN_NVIDIA 0x10DE +#define PCI_VEN_PC_TECH 0x1042 +#define PCI_VEN_CMD 0x1095 +#define PCI_VEN_VIA 0x1106 +#define PCI_VEN_SERVERWORKS 0x1166 +#define PCI_VEN_TOSHIBA 0x1179 +#define PCI_VEN_CAVIUM 0x177D +#define PCI_VEN_INTEL 0x8086 + +/** Master/Slave devices */ +#define CHANNEL_PCAT_MAX_DEVICES 2 + +/** Master/Slave devices for Bank 0 and Bank 1 */ +#define CHANNEL_PC98_MAX_DEVICES 4 + +/** PC-98 ATA bank register */ +#define PC98_ATA_BANK 0x432 +#define PC98_ATA_BANK_32BIT_PORT 0x08 + +/** Delay of 400ns */ +#define ATA_IO_WAIT() KeStallExecutionProcessor(1) + +#define NUM_TO_BITMAP(num) (0xFFFFFFFF >> (RTL_BITS_OF(ULONG) - (num))) + +/** Supported transfer modes by SATA devices */ +#define SATA_ALL \ + (PIO_ALL | MWDMA_ALL | UDMA_ALL) + +/** UDMA modes that require the presence of an 80-conductor ATA cable */ +#define UDMA_80C_ALL \ + (UDMA_MODE3 | UDMA_MODE4 | UDMA_MODE5 | UDMA_MODE6) + +/** Used to specify a range of MWDMA modes */ +#define MWDMA_MODES(MinMode, MaxMode) \ + (NUM_TO_BITMAP(MWDMA_MODE((MaxMode) + 1)) & ~NUM_TO_BITMAP(MWDMA_MODE(MinMode))) + +/** Used to specify a range of UDMA modes */ +#define UDMA_MODES(MinMode, MaxMode) \ + (NUM_TO_BITMAP(UDMA_MODE((MaxMode) + 1)) & ~NUM_TO_BITMAP(UDMA_MODE(MinMode))) + +#define IDE_DC_ALWAYS 0x08 +#define IDE_DRIVE_SELECT 0xA0 + +#define IDE_HIGH_ORDER_BYTE 0x80 + +#define IDE_FEATURE_PIO 0x00 +#define IDE_FEATURE_DMA 0x01 +#define IDE_FEATURE_DMADIR 0x04 + +/** + * If a larger transfer is attempted, the 16-bit ByteCount register might overflow. + * In this case we round down the length to the closest multiple of 2. + */ +#define ATAPI_MAX_DRQ_DATA_BLOCK 0xFFFE + +/** + * Legacy ranges and interrupts + */ +/*@{*/ +#define PCIIDE_LEGACY_RESOURCE_COUNT 3 +#define PCIIDE_LEGACY_COMMAND_IO_RANGE_LENGTH 8 +#define PCIIDE_LEGACY_CONTROL_IO_RANGE_LENGTH 1 +#define PCIIDE_LEGACY_PRIMARY_COMMAND_BASE 0x1F0 +#define PCIIDE_LEGACY_PRIMARY_CONTROL_BASE 0x3F6 +#define PCIIDE_LEGACY_PRIMARY_IRQ 14 +#define PCIIDE_LEGACY_SECONDARY_COMMAND_BASE 0x170 +#define PCIIDE_LEGACY_SECONDARY_CONTROL_BASE 0x376 +#define PCIIDE_LEGACY_SECONDARY_IRQ 15 +/*@}*/ + +#define PCIIDE_COMMAND_IO_RANGE_LENGTH 8 +#define PCIIDE_CONTROL_IO_RANGE_LENGTH 4 +#define PCIIDE_CONTROL_IO_BAR_OFFSET 2 +#define PCIIDE_DMA_IO_BAR 4 +#define PCIIDE_DMA_IO_RANGE_LENGTH 16 + +/** Offset from base address */ +#define PCIIDE_DMA_SECONDARY_CHANNEL_OFFSET 8 + +/** + * Programming Interface Register + */ +/*@{*/ +#define PCIIDE_PROGIF_PRIMARY_CHANNEL_NATIVE_MODE 0x01 +#define PCIIDE_PROGIF_PRIMARY_CHANNEL_NATIVE_MODE_CAPABLE 0x02 +#define PCIIDE_PROGIF_SECONDARY_CHANNEL_NATIVE_MODE 0x04 +#define PCIIDE_PROGIF_SECONDARY_CHANNEL_NATIVE_MODE_CAPABLE 0x08 +#define PCIIDE_PROGIF_DMA_CAPABLE 0x80 +/*@}*/ + +/** + * IDE Bus Master I/O Registers + */ +/*@{*/ +#define PCIIDE_DMA_COMMAND 0 +#define PCIIDE_DMA_STATUS 2 +#define PCIIDE_DMA_PRDT_PHYSICAL_ADDRESS 4 +/*@}*/ + +/** + * IDE Bus Master Command Register + */ +/*@{*/ +#define PCIIDE_DMA_COMMAND_STOP 0x00 +#define PCIIDE_DMA_COMMAND_START 0x01 +#define PCIIDE_DMA_COMMAND_READ_FROM_SYSTEM_MEMORY 0x00 +#define PCIIDE_DMA_COMMAND_WRITE_TO_SYSTEM_MEMORY 0x08 +/*@}*/ + +/** + * IDE Bus Master Status Register + */ +/*@{*/ +#define PCIIDE_DMA_STATUS_ACTIVE 0x01 +#define PCIIDE_DMA_STATUS_ERROR 0x02 +#define PCIIDE_DMA_STATUS_INTERRUPT 0x04 +#define PCIIDE_DMA_STATUS_RESERVED1 0x08 +#define PCIIDE_DMA_STATUS_RESERVED2 0x10 +#define PCIIDE_DMA_STATUS_DRIVE0_DMA_CAPABLE 0x20 +#define PCIIDE_DMA_STATUS_DRIVE1_DMA_CAPABLE 0x40 +#define PCIIDE_DMA_STATUS_SIMPLEX 0x80 +/*@}*/ + +/** + * ATAPI Interrupt Status + */ +/*@{*/ +/** Command or Data */ +#define ATAPI_INT_REASON_COD 0x01 + +/** Read direction */ +#define ATAPI_INT_REASON_IO 0x02 + +/** Bus release */ +#define ATAPI_INT_REASON_RELEASE 0x04 + +/** Command Tag for the command */ +#define ATAPI_INT_REASON_TAG 0xF8 + +#define ATAPI_INT_REASON_MASK (ATAPI_INT_REASON_IO | ATAPI_INT_REASON_COD) + +/** Status - Register contains Completion Status (NEC CDR-260) */ +#define ATAPI_INT_REASON_STATUS_NEC 0x00 + +/** Status - Register contains Completion Status */ +#define ATAPI_INT_REASON_STATUS (ATAPI_INT_REASON_IO | ATAPI_INT_REASON_COD) + +/** Data From Host - Receive command parameter data from the host */ +#define ATAPI_INT_REASON_DATA_OUT IDE_STATUS_DRQ + +/** CDB - Ready for Command Packet */ +#define ATAPI_INT_REASON_AWAIT_CDB (IDE_STATUS_DRQ | ATAPI_INT_REASON_COD) + +/** Data To Host - Send command parameter data to the host */ +#define ATAPI_INT_REASON_DATA_IN (ATAPI_INT_REASON_IO | IDE_STATUS_DRQ) +/*@}*/ + +#define ATA_TIME_BUSY_SELECT 3000 ///< 30 ms +#define ATA_TIME_BUSY_NORMAL 50000 ///< 500 ms +#define ATA_TIME_BUSY_POLL 5 ///< 50 us +#define ATA_TIME_DRQ_CLEAR 1000 ///< 10 ms +#define ATA_TIME_PHASE_CHANGE 100 ///< 1 ms + +/* We keep the value as small as possible, since large timeout can break our error handling */ +#define ATA_TIME_DRQ_ASSERT 15 ///< 150 us + +#define ATA_TIME_RESET_SELECT (2000 / PORT_TIMER_TICK_MS) ///< 2 s +#define ATA_TIME_BUSY_RESET (10000 / PORT_TIMER_TICK_MS) ///< 10 s + +#define CMD_FLAG_NONE 0x00000000 +#define CMD_FLAG_TRANSFER_MASK 0x00000003 +#define CMD_FLAG_AWAIT_CDB 0x00000004 +#define CMD_FLAG_DATA_IN 0x00000040 +#define CMD_FLAG_DATA_OUT 0x00000080 +#define CMD_FLAG_AWAIT_INTERRUPT 0x80000000 + +#define CMD_FLAG_ATAPI_PIO_TRANSFER 0x00000001 +#define CMD_FLAG_ATA_PIO_TRANSFER 0x00000002 +#define CMD_FLAG_DMA_TRANSFER 0x00000003 + +/** The IDE interface has one slot per channel */ +#define PATA_CHANNEL_SLOT 0 +#define PATA_CHANNEL_QUEUE_DEPTH 1 + +C_ASSERT(CMD_FLAG_DMA_TRANSFER == (CMD_FLAG_ATAPI_PIO_TRANSFER | CMD_FLAG_ATA_PIO_TRANSFER)); +C_ASSERT(CMD_FLAG_DATA_IN == REQUEST_FLAG_DATA_IN); +C_ASSERT(CMD_FLAG_DATA_OUT == REQUEST_FLAG_DATA_OUT); +C_ASSERT(CMD_FLAG_AWAIT_INTERRUPT == REQUEST_FLAG_POLL); + +#include +/** + * Physical Region Descriptor Table Entry + */ +typedef struct _PCIIDE_PRD_TABLE_ENTRY +{ + ULONG Address; + ULONG Length; //!< 0 means 0x10000 bytes +#define PCIIDE_PRD_LENGTH_MASK 0xFFFF +#define PCIIDE_PRD_END_OF_TABLE 0x80000000 +} PCIIDE_PRD_TABLE_ENTRY, *PPCIIDE_PRD_TABLE_ENTRY; +#include + +/** 64 kB boundary */ +#define PCIIDE_PRD_LIMIT 0x10000 + +typedef USHORT ATATIM; + +typedef struct _ATA_TIMING +{ + ATATIM AddressSetup; // t1 + /* Register transfers (8-bit) */ + ATATIM CmdActive; // t2 + ATATIM CmdRecovery; // t2i + /* PIO data transfers (16-bit) */ + ATATIM DataActive; // t2 + ATATIM DataRecovery; // t2i +} ATA_TIMING, *PATA_TIMING; + +#define SHARED_CMD_TIMINGS 0x00000001 +#define SHARED_DATA_TIMINGS 0x00000002 +#define SHARED_ADDR_TIMINGS 0x00000004 + +FORCEINLINE +ATATIM +CLAMP_TIMING( + _In_ ATATIM Value, + _In_ ATATIM Minimum, + _In_ ATATIM Maximum) +{ + if (Value < Minimum) + return Minimum; + if (Value > Maximum) + return Maximum; + return Value; +} + +#define ATA_READ_BLOCK_16(Port, Buffer, Count, Ctx, MmioFlag) \ + AtaReadBlock16(Port, Buffer, Count, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag) + +#define ATA_WRITE_BLOCK_16(Port, Buffer, Count, Ctx, MmioFlag) \ + AtaWriteBlock16(Port, Buffer, Count, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag) + +#define ATA_READ_BLOCK_32(Port, Buffer, Count, Ctx, MmioFlag) \ + AtaReadBlock32(Port, Buffer, Count, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag) + +#define ATA_WRITE_BLOCK_32(Port, Buffer, Count, Ctx, MmioFlag) \ + AtaWriteBlock32(Port, Buffer, Count, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag) + +#define ATA_READ(Port, Ctx, MmioFlag) \ + AtaReadPortUchar(Port, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag) + +#define ATA_WRITE(Port, Value, Ctx, MmioFlag) \ + AtaWritePortUchar(Port, Value, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag) + +#define ATA_WRITE_ULONG(Port, Value, Ctx, MmioFlag) \ + AtaWritePortUlong(Port, Value, (Ctx)->ChanInfo & CHANNEL_FLAG_##MmioFlag) + +FORCEINLINE +VOID +ATA_SELECT_DEVICE( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ UCHAR DeviceNumber, + _In_ UCHAR DeviceSelect) +{ +#if defined(_M_IX86) + /* NEC extension to allow 4 drives per channel */ + if ((ChanData->ChanInfo & CHANNEL_FLAG_CBUS) && + (ChanData->LastAtaBankId != DeviceNumber)) + { + UCHAR AtaBank; + + ChanData->LastAtaBankId = DeviceNumber; + + /* The 0x432 port is used to select the primary (0) or secondary (1) IDE channel */ + AtaBank = DeviceNumber >> 1; + + if (ChanData->ChanInfo & CHANNEL_FLAG_IO32) + AtaBank |= PC98_ATA_BANK_32BIT_PORT; + + WRITE_PORT_UCHAR((PUCHAR)PC98_ATA_BANK, AtaBank); + } +#endif + + ATA_WRITE(ChanData->Regs.Device, DeviceSelect, ChanData, MRES_TF); + ATA_IO_WAIT(); +} + +FORCEINLINE +UCHAR +ATA_WAIT( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_range_(>, 0) ULONG Timeout, + _In_ UCHAR Mask, + _In_ UCHAR Value) +{ + UCHAR IdeStatus; + ULONG i; + + for (i = 0; i < Timeout; ++i) + { + IdeStatus = ChanData->ReadStatus(ChanData); + if ((IdeStatus & Mask) == Value) + break; + + if (IdeStatus == 0xFF) + break; + + KeStallExecutionProcessor(10); + } + + return IdeStatus; +} diff --git a/drivers/storage/ide/pciidex/pciidex.c b/drivers/storage/ide/pciidex/pciidex.c index 9f4bdb9768f..6f6bb5171bf 100644 --- a/drivers/storage/ide/pciidex/pciidex.c +++ b/drivers/storage/ide/pciidex/pciidex.c @@ -7,11 +7,110 @@ #include "pciidex.h" -#define NDEBUG -#include - ULONG PciIdeControllerNumber = 0; +BOOLEAN +PciFindDevice( + _In_ __callback PATA_PCI_MATCH_FN MatchFunction, + _In_ PVOID Context) +{ + ULONG BusNumber, DeviceNumber, FunctionNumber; + + for (BusNumber = 0; BusNumber < 0xFF; ++BusNumber) + { + for (DeviceNumber = 0; DeviceNumber < PCI_MAX_DEVICES; ++DeviceNumber) + { + for (FunctionNumber = 0; FunctionNumber < PCI_MAX_FUNCTION; ++FunctionNumber) + { + UCHAR Buffer[RTL_SIZEOF_THROUGH_FIELD(PCI_COMMON_HEADER, RevisionID)]; + PPCI_COMMON_HEADER PciConfig = (PPCI_COMMON_HEADER)Buffer; // Partial PCI header + PCI_SLOT_NUMBER PciSlot; + ULONG BytesRead; + + PciSlot.u.AsULONG = 0; + PciSlot.u.bits.DeviceNumber = DeviceNumber; + PciSlot.u.bits.FunctionNumber = FunctionNumber; + + BytesRead = HalGetBusDataByOffset(PCIConfiguration, + BusNumber, + PciSlot.u.AsULONG, + &Buffer, + 0, + sizeof(Buffer)); + if (BytesRead != sizeof(Buffer) || + PciConfig->VendorID == PCI_INVALID_VENDORID || + PciConfig->VendorID == 0) + { + if (FunctionNumber == 0) + { + /* This slot has no single- or a multi-function device */ + break; + } + else + { + /* Continue scanning the functions */ + continue; + } + } + + if (MatchFunction(Context, BusNumber, PciSlot, PciConfig)) + return TRUE; + + if (!PCI_MULTIFUNCTION_DEVICE(PciConfig)) + { + /* The device is a single function device */ + break; + } + } + } + } + + return FALSE; +} + +VOID +PciRead( + _In_ PATA_CONTROLLER Controller, + _Out_writes_bytes_all_(BufferLength) PVOID Buffer, + _In_ ULONG ConfigDataOffset, + _In_ ULONG BufferLength) +{ + Controller->GetBusData(Controller->BusInterfaceContext, + PCI_WHICHSPACE_CONFIG, + Buffer, + ConfigDataOffset, + BufferLength); +} + +VOID +PciWrite( + _In_ PATA_CONTROLLER Controller, + _In_reads_bytes_(BufferLength) PVOID Buffer, + _In_ ULONG ConfigDataOffset, + _In_ ULONG BufferLength) +{ + Controller->SetBusData(Controller->BusInterfaceContext, + PCI_WHICHSPACE_CONFIG, + Buffer, + ConfigDataOffset, + BufferLength); +} + +VOID +AtaSleep(VOID) +{ + KTIMER Timer; + LARGE_INTEGER DueTime; + + ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); + + DueTime.QuadPart = PORT_TIMER_TICK_MS * -10000LL; + + KeInitializeTimer(&Timer); + KeSetTimer(&Timer, DueTime, 0); + KeWaitForSingleObject(&Timer, Executive, KernelMode, FALSE, 0); +} + CODE_SEG("PAGE") NTSTATUS NTAPI @@ -28,7 +127,7 @@ PciIdeXDispatchWmi( PFDO_DEVICE_EXTENSION FdoExtension = DeviceObject->DeviceExtension; IoSkipCurrentIrpStackLocation(Irp); - Status = IoCallDriver(FdoExtension->Ldo, Irp); + Status = IoCallDriver(FdoExtension->Common.LowerDeviceObject, Irp); } else { @@ -52,13 +151,156 @@ PciIdeXUnload( NOTHING; } +CODE_SEG("PAGE") +NTSTATUS +PciIdeXPnpRepeatRequest( + _In_ PCOMMON_DEVICE_EXTENSION CommonExt, + _In_ PIRP Irp, + _In_opt_ PDEVICE_CAPABILITIES DeviceCapabilities) +{ + PCOMMON_DEVICE_EXTENSION FdoExt = CommonExt->FdoExt; + PDEVICE_OBJECT TopDeviceObject; + PIO_STACK_LOCATION IoStack, SubStack; + PIRP SubIrp; + KEVENT Event; + NTSTATUS Status; + + PAGED_CODE(); + ASSERT(!IS_FDO(CommonExt)); + + TopDeviceObject = IoGetAttachedDeviceReference(FdoExt->Self); + + SubIrp = IoAllocateIrp(TopDeviceObject->StackSize, FALSE); + if (!SubIrp) + { + ObDereferenceObject(TopDeviceObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + IoStack = IoGetCurrentIrpStackLocation(Irp); + SubStack = IoGetNextIrpStackLocation(SubIrp); + RtlCopyMemory(SubStack, IoStack, sizeof(*SubStack)); + + if (DeviceCapabilities) + SubStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities; + + IoSetCompletionRoutine(SubIrp, + PciIdeXPdoCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE); + + SubIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; + + Status = IoCallDriver(TopDeviceObject, SubIrp); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + } + + ObDereferenceObject(TopDeviceObject); + + Status = SubIrp->IoStatus.Status; + IoFreeIrp(SubIrp); + + return Status; +} + +CODE_SEG("PAGE") +NTSTATUS +PciIdeXPnpQueryDeviceUsageNotification( + _In_ PCOMMON_DEVICE_EXTENSION CommonExt, + _In_ PIRP Irp) +{ + PIO_STACK_LOCATION IoStack; + NTSTATUS Status; + volatile LONG* Counter; + + PAGED_CODE(); + + if (!IS_FDO(CommonExt)) + { + Status = PciIdeXPnpRepeatRequest(CommonExt, Irp, NULL); + } + else + { + if (!NT_VERIFY(IoForwardIrpSynchronously(CommonExt->LowerDeviceObject, Irp))) + return STATUS_UNSUCCESSFUL; + Status = Irp->IoStatus.Status; + } + if (!NT_SUCCESS(Status)) + return Status; + + IoStack = IoGetCurrentIrpStackLocation(Irp); + switch (IoStack->Parameters.UsageNotification.Type) + { + case DeviceUsageTypePaging: + Counter = &CommonExt->PageFiles; + break; + + case DeviceUsageTypeHibernation: + Counter = &CommonExt->HibernateFiles; + break; + + case DeviceUsageTypeDumpFile: + Counter = &CommonExt->DumpFiles; + break; + + default: + return Status; + } + + IoAdjustPagingPathCount(Counter, IoStack->Parameters.UsageNotification.InPath); + + if (!IS_FDO(CommonExt)) + IoInvalidateDeviceState(CommonExt->Self); + + return STATUS_SUCCESS; +} + +CODE_SEG("PAGE") +NTSTATUS +PciIdeXPnpQueryPnpDeviceState( + _In_ PCOMMON_DEVICE_EXTENSION CommonExt, + _In_ PIRP Irp) +{ + PAGED_CODE(); + + if (CommonExt->PageFiles || CommonExt->HibernateFiles || CommonExt->DumpFiles) + Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE; + + if (IS_FDO(CommonExt)) + Irp->IoStatus.Status = STATUS_SUCCESS; + + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +PciIdeXPdoCompletionRoutine( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Context) +{ + UNREFERENCED_PARAMETER(DeviceObject); + + if (Irp->PendingReturned) + KeSetEvent(Context, IO_NO_INCREMENT, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + static CODE_SEG("PAGE") NTSTATUS -PciIdeXQueryInterface( - _In_ PFDO_DEVICE_EXTENSION FdoExtension, +PciIdeXPnpQueryInterface( + _In_ PCOMMON_DEVICE_EXTENSION CommonExt, _In_ const GUID* Guid, _Out_ PVOID Interface, + _In_ ULONG Version, _In_ ULONG Size) { KEVENT Event; @@ -72,7 +314,7 @@ PciIdeXQueryInterface( KeInitializeEvent(&Event, SynchronizationEvent, FALSE); Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, - FdoExtension->Ldo, + CommonExt->LowerDeviceObject, NULL, 0, NULL, @@ -87,12 +329,12 @@ PciIdeXQueryInterface( Stack = IoGetNextIrpStackLocation(Irp); Stack->MinorFunction = IRP_MN_QUERY_INTERFACE; Stack->Parameters.QueryInterface.InterfaceType = Guid; - Stack->Parameters.QueryInterface.Version = 1; + Stack->Parameters.QueryInterface.Version = Version; Stack->Parameters.QueryInterface.Size = Size; Stack->Parameters.QueryInterface.Interface = Interface; Stack->Parameters.QueryInterface.InterfaceSpecificData = NULL; - Status = IoCallDriver(FdoExtension->Ldo, Irp); + Status = IoCallDriver(CommonExt->LowerDeviceObject, Irp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); @@ -105,86 +347,40 @@ PciIdeXQueryInterface( static CODE_SEG("PAGE") NTSTATUS -PciIdeXGetConfigurationInfo( - _In_ PFDO_DEVICE_EXTENSION FdoExtension) -{ - UCHAR Buffer[RTL_SIZEOF_THROUGH_FIELD(PCI_COMMON_HEADER, BaseClass)]; - PPCI_COMMON_HEADER PciConfig = (PPCI_COMMON_HEADER)Buffer; - ULONG BytesRead; - - PAGED_CODE(); - - BytesRead = (*FdoExtension->BusInterface.GetBusData)(FdoExtension->BusInterface.Context, - PCI_WHICHSPACE_CONFIG, - Buffer, - 0, - sizeof(Buffer)); - if (BytesRead != sizeof(Buffer)) - return STATUS_IO_DEVICE_ERROR; - - FdoExtension->VendorId = PciConfig->VendorID; - FdoExtension->DeviceId = PciConfig->DeviceID; - - if (PciConfig->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) - { - if (PciConfig->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR) - { - /* Both IDE channels in native mode */ - FdoExtension->InNativeMode = - (PciConfig->ProgIf & PCIIDE_PROGIF_PRIMARY_CHANNEL_NATIVE_MODE) && - (PciConfig->ProgIf & PCIIDE_PROGIF_SECONDARY_CHANNEL_NATIVE_MODE); - } - else if (PciConfig->SubClass == PCI_SUBCLASS_MSC_RAID_CTLR) - { - FdoExtension->InNativeMode = TRUE; - } - } - - DPRINT("Controller %04x:%04x, Interface byte 0x%02x, Native mode %d\n", - FdoExtension->VendorId, - FdoExtension->DeviceId, - PciConfig->ProgIf, - FdoExtension->InNativeMode); - - return STATUS_SUCCESS; -} - -CODE_SEG("PAGE") -NTSTATUS -NTAPI -PciIdeXAddDevice( +PciIdeXAddDeviceEx( _In_ PDRIVER_OBJECT DriverObject, - _In_ PDEVICE_OBJECT PhysicalDeviceObject) + _In_ PDEVICE_OBJECT PhysicalDeviceObject, + _Out_ PVOID *ControllerContext) { PPCIIDEX_DRIVER_EXTENSION DriverExtension; PFDO_DEVICE_EXTENSION FdoExtension; ULONG DeviceExtensionSize; PDEVICE_OBJECT Fdo; UNICODE_STRING DeviceName; - WCHAR DeviceNameBuffer[sizeof("\\Device\\Ide\\PciIde999")]; + WCHAR DeviceNameBuffer[sizeof("\\Device\\Ide\\PciIde99999")]; NTSTATUS Status; PAGED_CODE(); Status = RtlStringCbPrintfW(DeviceNameBuffer, sizeof(DeviceNameBuffer), - L"\\Device\\Ide\\PciIde%u", + L"\\Device\\Ide\\PciIde%lu", PciIdeControllerNumber); ASSERT(NT_SUCCESS(Status)); RtlInitUnicodeString(&DeviceName, DeviceNameBuffer); - DPRINT("%s(%p, %p) '%wZ'\n", __FUNCTION__, DriverObject, PhysicalDeviceObject, &DeviceName); + INFO("%s(%p, %p, %p) '%wZ'\n", + __FUNCTION__, DriverObject, PhysicalDeviceObject, ControllerContext, &DeviceName); DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject); ASSERT(DriverExtension); Status = RtlULongAdd(DriverExtension->MiniControllerExtensionSize, - sizeof(FDO_DEVICE_EXTENSION), + sizeof(*FdoExtension), &DeviceExtensionSize); if (!NT_SUCCESS(Status)) { - DPRINT1("Invalid miniport extension size %lx\n", - DriverExtension->MiniControllerExtensionSize); + ERR("Invalid miniport extension size %lx\n", DriverExtension->MiniControllerExtensionSize); return Status; } @@ -197,61 +393,90 @@ PciIdeXAddDevice( &Fdo); if (!NT_SUCCESS(Status)) { - DPRINT1("Failed to create FDO 0x%lx\n", Status); + ERR("Failed to create FDO 0x%lx\n", Status); return Status; } FdoExtension = Fdo->DeviceExtension; - RtlZeroMemory(FdoExtension, sizeof(FDO_DEVICE_EXTENSION)); - FdoExtension->Common.IsFDO = TRUE; + RtlZeroMemory(FdoExtension, sizeof(*FdoExtension)); + FdoExtension->Common.Flags = DO_IS_FDO; FdoExtension->Common.Self = Fdo; + IoInitializeRemoveLock(&FdoExtension->Common.RemoveLock, TAG_PCIIDEX, 0, 0); + FdoExtension->DriverObject = DriverObject; + FdoExtension->Pdo = PhysicalDeviceObject; FdoExtension->ControllerNumber = PciIdeControllerNumber++; - KeInitializeSpinLock(&FdoExtension->BusDataLock); - ExInitializeFastMutex(&FdoExtension->DeviceSyncMutex); + ExInitializeFastMutex(&FdoExtension->PdoListSyncMutex); + InitializeListHead(&FdoExtension->PdoListHead); - Status = IoAttachDeviceToDeviceStackSafe(Fdo, PhysicalDeviceObject, &FdoExtension->Ldo); + KeInitializeSpinLock(&FdoExtension->Controller.Lock); + + Status = IoAttachDeviceToDeviceStackSafe(Fdo, + PhysicalDeviceObject, + &FdoExtension->Common.LowerDeviceObject); if (!NT_SUCCESS(Status)) { - DPRINT("Failed to attach FDO 0x%lx\n", Status); + ERR("Failed to attach FDO 0x%lx\n", Status); goto Failure; } /* DMA buffers alignment */ - Fdo->AlignmentRequirement = max(FdoExtension->Ldo->AlignmentRequirement, FILE_WORD_ALIGNMENT); + Fdo->AlignmentRequirement = FdoExtension->Common.LowerDeviceObject->AlignmentRequirement; + Fdo->AlignmentRequirement = max(Fdo->AlignmentRequirement, ATA_MIN_BUFFER_ALIGNMENT); - Status = PciIdeXQueryInterface(FdoExtension, - &GUID_BUS_INTERFACE_STANDARD, - &FdoExtension->BusInterface, - sizeof(BUS_INTERFACE_STANDARD)); - if (!NT_SUCCESS(Status)) +#if defined(ATA_DETECT_LEGACY_DEVICES) + if (ControllerContext) { - DPRINT1("No bus interface 0x%lx\n", Status); - goto Failure; + FdoExtension->Controller.Flags |= CTRL_FLAG_NON_PNP; + *ControllerContext = FdoExtension; } - - Status = PciIdeXGetConfigurationInfo(FdoExtension); - if (!NT_SUCCESS(Status)) + else +#endif { - DPRINT1("Unable to retrieve the configuration info %lx\n", Status); - goto Failure; + BUS_INTERFACE_STANDARD BusInterface; + + Status = PciIdeXPnpQueryInterface(&FdoExtension->Common, + &GUID_BUS_INTERFACE_STANDARD, + &BusInterface, + PCI_BUS_INTERFACE_STANDARD_VERSION, + sizeof(BusInterface)); + if (!NT_SUCCESS(Status)) + { + ERR("No PCI bus interface 0x%lx\n", Status); + goto Failure; + } + + FdoExtension->Controller.BusInterfaceContext = BusInterface.Context; + FdoExtension->Controller.SetBusData = BusInterface.SetBusData; + FdoExtension->Controller.GetBusData = BusInterface.GetBusData; } Fdo->Flags &= ~DO_DEVICE_INITIALIZING; - return STATUS_SUCCESS; Failure: - if (FdoExtension->Ldo) - IoDetachDevice(FdoExtension->Ldo); + if (FdoExtension->Common.LowerDeviceObject) + IoDetachDevice(FdoExtension->Common.LowerDeviceObject); IoDeleteDevice(Fdo); return Status; } +CODE_SEG("PAGE") +NTSTATUS +NTAPI +PciIdeXAddDevice( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PDEVICE_OBJECT PhysicalDeviceObject) +{ + PAGED_CODE(); + + return PciIdeXAddDeviceEx(DriverObject, PhysicalDeviceObject, NULL); +} + static CODE_SEG("PAGE") VOID @@ -297,17 +522,35 @@ PciIdeXInitialize( PAGED_CODE(); - DPRINT("PciIdeXInitialize(%p '%wZ' %p 0x%lx)\n", - DriverObject, RegistryPath, HwGetControllerProperties, ExtensionSize); + INFO("PciIdeXInitialize(%p '%wZ' %p 0x%lx)\n", + DriverObject, RegistryPath, HwGetControllerProperties, ExtensionSize); + + /* ReactOS-specific: Check for our legacy detection magic */ + if (ExtensionSize == PCIIDEX_GET_CONTROLLER_INTERFACE_SIGNATURE) + { +#if defined(ATA_DETECT_LEGACY_DEVICES) + PPCIIDEX_LEGACY_CONTROLLER_INTERFACE ControllerInferface; + + ControllerInferface = (PVOID)HwGetControllerProperties; + ControllerInferface->Version = PCIIDEX_INTERFACE_VERSION; + ControllerInferface->AddDevice = PciIdeXAddDeviceEx; + ControllerInferface->StartDevice = PciIdeXFdoStartDevice; + ControllerInferface->RemoveDevice = PciIdeXFdoRemoveDevice; + + ExtensionSize = 0; +#else + return STATUS_NOT_SUPPORTED; +#endif + } Status = IoAllocateDriverObjectExtension(DriverObject, DriverObject, - sizeof(PCIIDEX_DRIVER_EXTENSION), + sizeof(*DriverExtension), (PVOID*)&DriverExtension); if (!NT_SUCCESS(Status)) return Status; - RtlZeroMemory(DriverExtension, sizeof(PCIIDEX_DRIVER_EXTENSION)); + RtlZeroMemory(DriverExtension, sizeof(*DriverExtension)); DriverExtension->MiniControllerExtensionSize = ExtensionSize; DriverExtension->HwGetControllerProperties = HwGetControllerProperties; diff --git a/drivers/storage/ide/pciidex/pciidex.h b/drivers/storage/ide/pciidex/pciidex.h index 2a546d61a8a..43027b45b06 100644 --- a/drivers/storage/ide/pciidex/pciidex.h +++ b/drivers/storage/ide/pciidex/pciidex.h @@ -6,47 +6,346 @@ * Copyright 2023 Dmitry Borisov */ -#ifndef _PCIIDEX_PCH_ -#define _PCIIDEX_PCH_ +#pragma once #include #include #include #include #include + +#include #include +#include +#include +#include +#include "debug.h" +#include "ahci.h" + +#include + +typedef struct _CHANNEL_DATA_PATA CHANNEL_DATA_PATA, *PCHANNEL_DATA_PATA; +typedef struct _CHANNEL_DATA_COMMON CHANNEL_DATA_COMMON, *PCHANNEL_DATA_COMMON; +typedef struct _PCIIDE_PRD_TABLE_ENTRY PCIIDE_PRD_TABLE_ENTRY, *PPCIIDE_PRD_TABLE_ENTRY; + #define TAG_PCIIDEX 'XedI' -#define IS_FDO(p) (((PCOMMON_DEVICE_EXTENSION)(p))->IsFDO) +#define ASSUME(cond) \ + do { \ + ASSERT(cond); \ + __assume(cond); \ + } while (0) + +#define IS_FDO(p) \ + ((((PCOMMON_DEVICE_EXTENSION)(p))->Flags & DO_IS_FDO) != 0) #define IS_PRIMARY_CHANNEL(PdoExtension) (PdoExtension->Channel == 0) -/* - * Legacy ranges and interrupts - */ -#define PCIIDE_LEGACY_RESOURCE_COUNT 3 -#define PCIIDE_LEGACY_COMMAND_IO_RANGE_LENGTH 8 -#define PCIIDE_LEGACY_CONTROL_IO_RANGE_LENGTH 1 -#define PCIIDE_LEGACY_PRIMARY_COMMAND_BASE 0x1F0 -#define PCIIDE_LEGACY_PRIMARY_CONTROL_BASE 0x3F6 -#define PCIIDE_LEGACY_PRIMARY_IRQ 14 -#define PCIIDE_LEGACY_SECONDARY_COMMAND_BASE 0x170 -#define PCIIDE_LEGACY_SECONDARY_CONTROL_BASE 0x376 -#define PCIIDE_LEGACY_SECONDARY_IRQ 15 +#if defined(_MSC_VER) +#pragma section("PAGECONS", read) +#endif -/* - * Programming Interface Register - */ -#define PCIIDE_PROGIF_PRIMARY_CHANNEL_NATIVE_MODE 0x01 -#define PCIIDE_PROGIF_PRIMARY_CHANNEL_NATIVE_MODE_CAPABLE 0x02 -#define PCIIDE_PROGIF_SECONDARY_CHANNEL_NATIVE_MODE 0x04 -#define PCIIDE_PROGIF_SECONDARY_CHANNEL_NATIVE_MODE_CAPABLE 0x08 -#define PCIIDE_PROGIF_DMA_CAPABLE 0x80 +/** Pageable read-only data */ +#define PCIIDEX_PAGED_DATA DATA_SEG("PAGECONS") -#define BM_SECONDARY_CHANNEL_OFFSET 8 +#define MAX_CHANNELS 32 + +#define PORT_TIMER_TICK_MS 10 // Timer interval is 10ms + +#define DEV_NUMBER(Device) ((Device)->TransportFlags & DEVICE_NUMBER_MASK) typedef struct _PDO_DEVICE_EXTENSION PDO_DEVICE_EXTENSION, *PPDO_DEVICE_EXTENSION; +typedef struct _ATA_CONTROLLER ATA_CONTROLLER, *PATA_CONTROLLER; + +_IRQL_requires_max_(APC_LEVEL) +typedef BOOLEAN +(ATA_PCI_MATCH_FN)( + _In_ PVOID Context, + _In_ ULONG BusNumber, + _In_ PCI_SLOT_NUMBER PciSlot, + _In_ PPCI_COMMON_HEADER PciConfig); +typedef ATA_PCI_MATCH_FN *PATA_PCI_MATCH_FN; + +_IRQL_requires_max_(APC_LEVEL) +typedef IDE_CHANNEL_STATE +(CONTROLLER_CHANNEL_ENABLED)( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel); +typedef CONTROLLER_CHANNEL_ENABLED *PCONTROLLER_CHANNEL_ENABLED; + +_IRQL_requires_max_(DISPATCH_LEVEL) +typedef VOID +(CONTROLLER_START)( + _In_ PATA_CONTROLLER Controller); +typedef CONTROLLER_START *PCONTROLLER_START; + +_IRQL_requires_max_(DISPATCH_LEVEL) +typedef VOID +(CONTROLLER_STOP)( + _In_ PATA_CONTROLLER Controller); +typedef CONTROLLER_STOP *PCONTROLLER_STOP; + +typedef VOID +(CONTROLLER_FREE_RESOURCES)( + _In_ PATA_CONTROLLER Controller); +typedef CONTROLLER_FREE_RESOURCES *PCONTROLLER_FREE_RESOURCES; + +typedef NTSTATUS +(CONTROLLER_ATTACH_CHANNEL_EX)( + _In_ PVOID ChannelContext, + _In_ BOOLEAN Attach); +typedef CONTROLLER_ATTACH_CHANNEL_EX *PCONTROLLER_ATTACH_CHANNEL_EX; + +_IRQL_requires_(DISPATCH_LEVEL) +typedef VOID +(CHANNEL_SET_MODE_EX)( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel, + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList); +typedef CHANNEL_SET_MODE_EX *PCHANNEL_SET_MODE_EX; + +typedef NTSTATUS +(CHANNEL_ALLOCATE_MEMORY)( + _In_ PVOID ChannelContext); +typedef CHANNEL_ALLOCATE_MEMORY *PCHANNEL_ALLOCATE_MEMORY; + +typedef VOID +(CHANNEL_FREE_MEMORY)( + _In_ PVOID ChannelContext); +typedef CHANNEL_FREE_MEMORY *PCHANNEL_FREE_MEMORY; + +typedef VOID +(CHANNEL_ENABLE_INTERRUPTS)( + _In_ PVOID ChannelContext, + _In_ BOOLEAN Enable); +typedef CHANNEL_ENABLE_INTERRUPTS *PCHANNEL_ENABLE_INTERRUPTS; + +typedef BOOLEAN +(CHANNEL_CHECK_INTERRUPT)( + _In_ PCHANNEL_DATA_PATA ChanData); +typedef CHANNEL_CHECK_INTERRUPT *PCHANNEL_CHECK_INTERRUPT; + +typedef VOID +(CHANNEL_LOAD_TASK_FILE)( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ PATA_DEVICE_REQUEST Request); +typedef CHANNEL_LOAD_TASK_FILE *PCHANNEL_LOAD_TASK_FILE; + +typedef VOID +(CHANNEL_SAVE_TASK_FILE)( + _In_ PCHANNEL_DATA_PATA ChanData, + _Inout_ PATA_DEVICE_REQUEST Request); +typedef CHANNEL_SAVE_TASK_FILE *PCHANNEL_SAVE_TASK_FILE; + +typedef UCHAR +(CHANNEL_READ_STATUS)( + _In_ PCHANNEL_DATA_PATA ChanData); +typedef CHANNEL_READ_STATUS *PCHANNEL_READ_STATUS; + +typedef BOOLEAN +(CHANNEL_READ_SCR)( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ SATA_SCR_REGISTER Register, + _In_ ULONG PortNumber, + _In_ PULONG Result); +typedef CHANNEL_READ_SCR *PCHANNEL_READ_SCR; + +typedef BOOLEAN +(CHANNEL_WRITE_SCR)( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ SATA_SCR_REGISTER Register, + _In_ ULONG PortNumber, + _In_ ULONG Value); +typedef CHANNEL_WRITE_SCR *PCHANNEL_WRITE_SCR; + +typedef struct _ATA_PCI_ENABLE_BITS +{ + UCHAR Register; + UCHAR Mask; + UCHAR ValueEnabled; +} ATA_PCI_ENABLE_BITS, *PATA_PCI_ENABLE_BITS; + +typedef struct _ATA_CONTROLLER +{ + CM_PARTIAL_RESOURCE_DESCRIPTOR InterruptDesc; + PKINTERRUPT InterruptObject; + struct + { + USHORT VendorID; + USHORT DeviceID; + USHORT Command; + UCHAR RevisionID; + UCHAR ProgIf; + UCHAR SubClass; + UCHAR BaseClass; + UCHAR CacheLineSize; + USHORT SubVendorID; + USHORT SubSystemID; + } Pci; + + struct + { + ULONG Flags; +#define RANGE_IS_VALID 0x01 +#define RANGE_IS_MEMORY 0x02 +#define RANGE_IS_MAPPED 0x04 + ULONG Length; + PVOID IoBase; + } AccessRange[PCI_TYPE0_ADDRESSES]; + + PGET_SET_DEVICE_DATA SetBusData; + PGET_SET_DEVICE_DATA GetBusData; + PVOID BusInterfaceContext; + PVOID ChanDataBlock; + PCONTROLLER_OBJECT HwSyncObject; + PCONTROLLER_START Start; + PCONTROLLER_STOP Stop; + PCONTROLLER_FREE_RESOURCES FreeResources; + PCONTROLLER_ATTACH_CHANNEL_EX AttachChannel; + union + { + const ATA_PCI_ENABLE_BITS* ChannelEnableBits; + PCONTROLLER_CHANNEL_ENABLED ChannelEnabledTest; + }; + PVOID HwExt; + ULONG AlignmentRequirement; + ULONG MaxChannels; + ULONG ChannelBitmap; + + PUCHAR IoBase; + ULONG AhciVersion; + ULONG AhciCapabilities; + ULONG AhciCapabilitiesEx; + + ULONG QueueDepth; + ULONG Flags; +#define CTRL_FLAG_NATIVE_PCI 0x00000001 +#define CTRL_FLAG_IS_SIMPLEX 0x00000002 +#define CTRL_FLAG_SATA_HBA_ACPI 0x00000002 +#define CTRL_FLAG_USE_TEST_FUNCTION 0x00000004 +#define CTRL_FLAG_IS_AHCI 0x00000008 +#define CTRL_FLAG_NON_PNP 0x00000010 +#define CTRL_FLAG_MANUAL_RES 0x00000020 +#define CTRL_FLAG_DMA_INTERRUPT 0x00000040 + + KSPIN_LOCK Lock; + PVOID Channels[MAX_CHANNELS]; +} ATA_CONTROLLER, *PATA_CONTROLLER; + +typedef struct _CHANNEL_DATA_COMMON +{ + PPDO_DEVICE_EXTENSION PdoExt; + CM_PARTIAL_RESOURCE_DESCRIPTOR InterruptDesc; + PKINTERRUPT InterruptObject; + PCHANNEL_ALLOCATE_MEMORY AllocateMemory; + PCHANNEL_FREE_MEMORY FreeMemory; + PCHANNEL_SET_MODE_EX SetTransferMode; + PCHANNEL_PREPARE_PRD_TABLE PreparePrdTable; + PCHANNEL_PREPARE_IO PrepareIo; + PCHANNEL_START_IO StartIo; + PCHANNEL_ENABLE_INTERRUPTS EnableInterrupts; + PATA_CONTROLLER Controller; + PDMA_ADAPTER DmaAdapter; + ULONG TransferModeSupported; + ULONG MaximumTransferLength; + ULONG Channel; + struct + { + ULONG TransferModeSupported; + ULONG MaximumTransferLength; + } Current; + ULONG MaximumPhysicalPages; + ULONG ChanInfo; +#define CHANNEL_FLAG_IO32 0x00000001 +#define CHANNEL_FLAG_DRIVE0_DMA_CAPABLE 0x00000002 +#define CHANNEL_FLAG_DRIVE1_DMA_CAPABLE 0x00000004 +#define CHANNEL_FLAG_CONTROL_PORT_BASE_MAPPED 0x00000008 +#define CHANNEL_FLAG_COMMAND_PORT_BASE_MAPPED 0x00000010 +#define CHANNEL_FLAG_DMA_PORT_BASE_MAPPED 0x00000020 +#define CHANNEL_FLAG_64_BIT_DMA 0x00000040 +#define CHANNEL_FLAG_PRIMARY_ADDRESS_CLAIMED 0x00000080 +#define CHANNEL_FLAG_SECONDARY_ADDRESS_CLAIMED 0x00000100 +#define CHANNEL_FLAG_PIO_VIA_DMA 0x00000200 +#define CHANNEL_FLAG_NO_SLAVE 0x00000400 +#define CHANNEL_FLAG_IS_EXTERNAL 0x00000800 +#define CHANNEL_FLAG_IS_PMP 0x00001000 +#define CHANNEL_FLAG_HAS_FBS 0x00002000 +#define CHANNEL_FLAG_FBS_ENABLED 0x00004000 +#define CHANNEL_FLAG_HAS_ACPI_GTM 0x00008000 +#define CHANNEL_FLAG_HAS_NCQ 0x00010000 +#define CHANNEL_FLAG_PIO_FOR_LBA48_XFER 0x00020000 // TODO: Not used yet +#define CHANNEL_FLAG_NO_ATAPI_DMA 0x00040000 +#define CHANNEL_FLAG_DMA_BEFORE_CMD 0x00080000 +#define CHANNEL_FLAG_MRES_TF 0x00100000 // MRES_TF +#define CHANNEL_FLAG_MRES_DMA 0x00200000 // MRES_DMA +#define CHANNEL_FLAG_MRES_CTRL 0x00400000 // MRES_CTRL +#define CHANNEL_FLAG_CBUS 0x80000000 + + PVOID PortContext; + PPORT_NOTIFICATION PortNotification; + ULONG ActiveSlotsBitmap; + ULONG ActiveQueuedSlotsBitmap; + PATA_DEVICE_REQUEST* Slots; +} CHANNEL_DATA_COMMON, *PCHANNEL_DATA_COMMON; + +typedef struct _CHANNEL_DATA_PATA +{ + /** Common data, must be the first member. */ + CHANNEL_DATA_COMMON; + + PCHANNEL_LOAD_TASK_FILE LoadTaskFile; + PCHANNEL_SAVE_TASK_FILE SaveTaskFile; + PCHANNEL_READ_STATUS ReadStatus; + PPCIIDE_PRD_TABLE_ENTRY PrdTable; + ULONG PrdTablePhysicalAddress; + IDE_REGISTERS Regs; + PCHANNEL_CHECK_INTERRUPT CheckInterrupt; + PUCHAR DataBuffer; + ULONG BytesToTransfer; + ULONG CommandFlags; + ULONG DrqByteCount; +#if defined(_M_IX86) + UCHAR LastAtaBankId; +#endif + BOOLEAN IsPollingActive; + KDPC PollingTimerDpc; + PCHANNEL_READ_SCR ScrRead; + PCHANNEL_WRITE_SCR ScrWrite; + IDE_ACPI_TIMING_MODE_BLOCK CurrentTimingMode; + ULONG HwFlags; + PUCHAR HwExt[ANYSIZE_ARRAY]; +} CHANNEL_DATA_PATA, *PCHANNEL_DATA_PATA; + +typedef struct _CHANNEL_INFO_AHCI +{ + ULONG64 ReceivedFisPhys; + ULONG64 CommandListPhys; + PVOID ReceivedFisOriginal; + PVOID CommandListOriginal; + PVOID CommandTableOriginal[AHCI_MAX_COMMAND_SLOTS]; + ULONG CommandTableSize[AHCI_MAX_COMMAND_SLOTS]; + ULONG CommandListSize; + PHYSICAL_ADDRESS ReceivedFisPhysOriginal; + PHYSICAL_ADDRESS CommandListPhysOriginal; + PHYSICAL_ADDRESS CommandTablePhysOriginal[AHCI_MAX_COMMAND_SLOTS]; + PVOID LocalBuffer; +} CHANNEL_INFO_AHCI, *PCHANNEL_INFO_AHCI; + +typedef struct _CHANNEL_DATA_AHCI +{ + /** Common data, must be the first member. */ + CHANNEL_DATA_COMMON; + + PVOID IoBase; + PAHCI_RECEIVED_FIS ReceivedFis; + PAHCI_COMMAND_LIST CommandList; + PAHCI_COMMAND_TABLE CommandTable[AHCI_MAX_COMMAND_SLOTS]; + UCHAR LastPmpDeviceNumber; + UCHAR LastFbsDeviceNumber; + ULONG TotalPortCount; + CHANNEL_INFO_AHCI Mem; +} CHANNEL_DATA_AHCI, *PCHANNEL_DATA_AHCI; typedef struct _PCIIDEX_DRIVER_EXTENSION { @@ -56,9 +355,6 @@ typedef struct _PCIIDEX_DRIVER_EXTENSION typedef struct _COMMON_DEVICE_EXTENSION { - BOOLEAN IsFDO; - PDEVICE_OBJECT Self; - _Write_guarded_by_(_Global_interlock_) volatile LONG PageFiles; @@ -67,30 +363,36 @@ typedef struct _COMMON_DEVICE_EXTENSION _Write_guarded_by_(_Global_interlock_) volatile LONG DumpFiles; + + union + { + /** Lower device object. This applies to FDO only. */ + PDEVICE_OBJECT LowerDeviceObject; + + /** Parent FDO. This applies to PDO only. */ + PVOID FdoExt; + }; + + PDEVICE_OBJECT Self; + + ULONG Flags; +#define DO_IS_FDO 0x80000000 + + IO_REMOVE_LOCK RemoveLock; } COMMON_DEVICE_EXTENSION, *PCOMMON_DEVICE_EXTENSION; typedef struct _FDO_DEVICE_EXTENSION { + /** Common data, must be the first member. */ COMMON_DEVICE_EXTENSION Common; - PDEVICE_OBJECT Ldo; - ULONG ControllerNumber; - BOOLEAN InNativeMode; - BOOLEAN IoBaseMapped; - BOOLEAN MiniportStarted; - - FAST_MUTEX DeviceSyncMutex; - _Guarded_by_(DeviceSyncMutex) - PPDO_DEVICE_EXTENSION Channels[MAX_IDE_CHANNEL]; - - USHORT VendorId; - USHORT DeviceId; PDRIVER_OBJECT DriverObject; - PUCHAR BusMasterPortBase; - - KSPIN_LOCK BusDataLock; - BUS_INTERFACE_STANDARD BusInterface; - + PDEVICE_OBJECT Pdo; + ULONG ControllerNumber; + FAST_MUTEX PdoListSyncMutex; + _Guarded_by_(PdoListSyncMutex) + LIST_ENTRY PdoListHead; + ATA_CONTROLLER Controller; IDE_CONTROLLER_PROPERTIES Properties; /* Must be the last entry */ @@ -99,13 +401,110 @@ typedef struct _FDO_DEVICE_EXTENSION typedef struct _PDO_DEVICE_EXTENSION { + /** Common data, must be the first member. */ COMMON_DEVICE_EXTENSION Common; + ULONG Channel; - PFDO_DEVICE_EXTENSION ParentController; - BOOLEAN ReportedMissing; - PUCHAR IoBase; + ULONG Flags; +#define PDO_FLAG_NOT_PRESENT 0x00000001 +#define PDO_FLAG_REPORTED_MISSING 0x00000002 + + PCHANNEL_DATA_COMMON ChanData; + + LIST_ENTRY ListEntry; } PDO_DEVICE_EXTENSION, *PPDO_DEVICE_EXTENSION; +VOID +AtaWritePortUchar( + _In_ PUCHAR Port, + _In_ UCHAR Value, + _In_ ULONG MmioFlags); + +#include "pata.h" + +extern NTSYSAPI ULONG InitSafeBootMode; + +/* acpi.c *********************************************************************/ + +BOOLEAN +AtaAcpiGetTimingMode( + _In_ PDEVICE_OBJECT DeviceObject, + _Out_ PIDE_ACPI_TIMING_MODE_BLOCK TimingMode); + +NTSTATUS +AtaAcpiSetTimingMode( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIDE_ACPI_TIMING_MODE_BLOCK TimingMode, + _In_opt_ PIDENTIFY_DEVICE_DATA IdBlock1, + _In_opt_ PIDENTIFY_DEVICE_DATA IdBlock2); + +CODE_SEG("PAGE") +PVOID +AtaAcpiGetTaskFile( + _In_ PDEVICE_OBJECT DeviceObject); + +CODE_SEG("PAGE") +VOID +AtaAcpiSetDeviceData( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIDENTIFY_DEVICE_DATA IdBlock); + +/* fdo.c **********************************************************************/ + +CODE_SEG("PAGE") +CONTROLLER_ATTACH_CHANNEL AtaCtrlAttachChannel; + +CODE_SEG("PAGE") +CHANNEL_SET_DEVICE_DATA AtaCtrlSetDeviceData; + +CODE_SEG("PAGE") +CHANNEL_GET_INIT_TASK_FILE AtaCtrlGetInitTaskFile; + +CHANNEL_SET_MODE AtaCtrlSetTransferMode; +CHANNEL_DOWNGRADE_INTERFACE_SPEED AtaCtrlDowngradeInterfaceSpeed; +CHANNEL_ABORT_CHANNEL AtaCtrlAbortChannel; + +CODE_SEG("PAGE") +CONTROLLER_PNP_START_DEVICE PciIdeXFdoStartDevice; + +CODE_SEG("PAGE") +CONTROLLER_PNP_REMOVE_DEVICE PciIdeXFdoRemoveDevice; + +DECLSPEC_NOINLINE_FROM_PAGED +VOID +AtaChanEnableInterruptsSync( + _In_ PVOID ChannelContext, + _In_ BOOLEAN Enable); + +CODE_SEG("PAGE") +PVOID +AtaCtrlPciMapBar( + _In_ PATA_CONTROLLER Controller, + _In_range_(0, PCI_TYPE0_ADDRESSES) ULONG Index, + _In_ ULONG MinimumIoLength); + +CODE_SEG("PAGE") +IDE_CHANNEL_STATE +PciIdeXGetChannelState( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel); + +CODE_SEG("PAGE") +NTSTATUS +PciIdeXFdoDispatchPnp( + _In_ PFDO_DEVICE_EXTENSION FdoExtension, + _Inout_ PIRP Irp); + +/* miniport.c *****************************************************************/ + +CODE_SEG("PAGE") +IDE_CHANNEL_STATE +PciIdeXChannelState( + _In_ PFDO_DEVICE_EXTENSION FdoExtension, + _In_ ULONG Channel); + +/* pciidex.c ******************************************************************/ + CODE_SEG("PAGE") DRIVER_INITIALIZE DriverEntry; @@ -115,32 +514,396 @@ DRIVER_UNLOAD PciIdeXUnload; CODE_SEG("PAGE") DRIVER_ADD_DEVICE PciIdeXAddDevice; -_Dispatch_type_(IRP_MJ_PNP) -CODE_SEG("PAGE") -DRIVER_DISPATCH_PAGED PciIdeXDispatchPnp; - _Dispatch_type_(IRP_MJ_SYSTEM_CONTROL) CODE_SEG("PAGE") DRIVER_DISPATCH_PAGED PciIdeXDispatchWmi; +IO_COMPLETION_ROUTINE PciIdeXPdoCompletionRoutine; + +BOOLEAN +PciFindDevice( + _In_ __callback PATA_PCI_MATCH_FN MatchFunction, + _In_ PVOID Context); + +VOID +PciRead( + _In_ PATA_CONTROLLER Controller, + _Out_writes_bytes_all_(BufferLength) PVOID Buffer, + _In_ ULONG ConfigDataOffset, + _In_ ULONG BufferLength); + +VOID +PciWrite( + _In_ PATA_CONTROLLER Controller, + _In_reads_bytes_(BufferLength) PVOID Buffer, + _In_ ULONG ConfigDataOffset, + _In_ ULONG BufferLength); + +VOID +AtaSleep(VOID); + +CODE_SEG("PAGE") +NTSTATUS +PciIdeXPnpRepeatRequest( + _In_ PCOMMON_DEVICE_EXTENSION CommonExt, + _In_ PIRP Irp, + _In_opt_ PDEVICE_CAPABILITIES DeviceCapabilities); + +CODE_SEG("PAGE") +NTSTATUS +PciIdeXPnpQueryDeviceUsageNotification( + _In_ PCOMMON_DEVICE_EXTENSION CommonExt, + _In_ PIRP Irp); + +CODE_SEG("PAGE") +NTSTATUS +PciIdeXPnpQueryPnpDeviceState( + _In_ PCOMMON_DEVICE_EXTENSION CommonExt, + _In_ PIRP Irp); + +/* pdo.c **********************************************************************/ + +_Dispatch_type_(IRP_MJ_PNP) +CODE_SEG("PAGE") +DRIVER_DISPATCH_PAGED PciIdeXDispatchPnp; + +CODE_SEG("PAGE") +NTSTATUS +PciIdeXPdoRemoveDevice( + _In_ PPDO_DEVICE_EXTENSION PdoExtension, + _In_ PIRP Irp, + _In_ BOOLEAN FinalRemove); + +/* power.c ********************************************************************/ + _Dispatch_type_(IRP_MJ_POWER) DRIVER_DISPATCH_RAISED PciIdeXDispatchPower; -CODE_SEG("PAGE") -NTSTATUS -PciIdeXFdoDispatchPnp( - _In_ PFDO_DEVICE_EXTENSION FdoExtension, - _Inout_ PIRP Irp); +/* ahci_generic.c *************************************************************/ CODE_SEG("PAGE") NTSTATUS -PciIdeXStartMiniport( - _In_ PFDO_DEVICE_EXTENSION FdoExtension); +AhciGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller); + +/* ahci_hw.c ******************************************************************/ + +CHANNEL_ENABLE_INTERRUPTS AtaAhciEnableInterrupts; +CHANNEL_ENUMERATE_CHANNEL AtaAhciEnumerateChannel; +CHANNEL_IDENTIFY_DEVICE AtaAhciIdentifyDevice; +CHANNEL_RESET_CHANNEL AtaAhciResetChannel; + +VOID +AtaAhciStopDma( + _In_ PCHANNEL_DATA_AHCI ChanData); + +VOID +AtaAhciSaveTaskFile( + _In_ PCHANNEL_DATA_AHCI ChanData, + _Inout_ PATA_DEVICE_REQUEST Request, + _In_ BOOLEAN ProcessErrorStatus); + +VOID +AtaAhciHandleFatalError( + _In_ PCHANNEL_DATA_AHCI ChanData); + +VOID +AtaAhciHandlePortStateChange( + _In_ PCHANNEL_DATA_AHCI ChanData, + _In_ ULONG InterruptStatus); + +BOOLEAN +AtaAhciDowngradeInterfaceSpeed( + _In_ PCHANNEL_DATA_AHCI ChanData); + +ULONG +AtaAhciChannelGetMaximumDeviceCount( + _In_ PVOID ChannelContext); + +/* ahci_io.c ******************************************************************/ + +CHANNEL_START_IO AtaAhciStartIo; +CHANNEL_PREPARE_IO AtaAhciPrepareIo; +CHANNEL_PREPARE_PRD_TABLE AtaAhciPreparePrdTable; +CHANNEL_ALLOCATE_SLOT AtaAhciAllocateSlot; +KSERVICE_ROUTINE AtaAhciHbaIsr; + +/* pata_generic.c *************************************************************/ + +CHANNEL_SET_MODE_EX SataSetTransferMode; + +CODE_SEG("PAGE") +CONTROLLER_ATTACH_CHANNEL_EX PciIdeAttachChannel; + +VOID +AtaSelectTimings( + _In_reads_(MAX_IDE_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList, + _Out_writes_all_(MAX_IDE_DEVICE) PATA_TIMING Timings, + _In_range_(>, 0) ULONG ClockPeriodPs, + _In_ ULONG Flags); + +CODE_SEG("PAGE") +VOID +PciIdeInitTaskFileIoResources( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ ULONG_PTR CommandPortBase, + _In_ ULONG_PTR ControlPortBase, + _In_ ULONG CommandBlockSpare); CODE_SEG("PAGE") IDE_CHANNEL_STATE -PciIdeXChannelState( - _In_ PFDO_DEVICE_EXTENSION FdoExtension, +PciIdeGetChannelState( + _In_ PATA_CONTROLLER Controller, _In_ ULONG Channel); -#endif /* _PCIIDEX_PCH_ */ +CODE_SEG("PAGE") +NTSTATUS +PciIdeParseResources( + _In_ PCHANNEL_DATA_PATA ChanData, + _In_ PCM_RESOURCE_LIST ResourcesTranslated); + +CODE_SEG("PAGE") +VOID +PciIdeFreeResources( + _In_ PVOID ChannelContext); + +CODE_SEG("PAGE") +NTSTATUS +PciIdeConnectInterrupt( + _In_ PVOID ChannelContext); + +CODE_SEG("PAGE") +NTSTATUS +PciIdeCreateChannelData( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG HwExtensionSize); + +CODE_SEG("PAGE") +NTSTATUS +PciIdeGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller); + +/* pata_hw.c ******************************************************************/ + +CHANNEL_RESET_CHANNEL PataResetChannel; +CHANNEL_ENUMERATE_CHANNEL PataEnumerateChannel; +CHANNEL_IDENTIFY_DEVICE PataIdentifyDevice; + +ULONG +PataChannelGetMaximumDeviceCount( + _In_ PVOID ChannelContext); + +/* pata_io.c ******************************************************************/ + +CHANNEL_ALLOCATE_SLOT PataAllocateSlot; +CHANNEL_PREPARE_PRD_TABLE PciIdePreparePrdTable; +CHANNEL_PREPARE_IO PataPrepareIo; +CHANNEL_START_IO PataStartIo; +KSERVICE_ROUTINE PataChannelIsr; +KSERVICE_ROUTINE PciIdeChannelIsr; +CHANNEL_CHECK_INTERRUPT PciIdeCheckInterrupt; +CHANNEL_LOAD_TASK_FILE PataLoadTaskFile; +CHANNEL_SAVE_TASK_FILE PataSaveTaskFile; +CHANNEL_READ_STATUS PataReadStatus; +KDEFERRED_ROUTINE PataPollingTimerDpc; + +VOID +AtaWriteBlock16( + _In_ PUSHORT Port, + _In_ PUSHORT Buffer, + _In_ ULONG Count, + _In_ ULONG MmioFlags); + +VOID +AtaReadBlock16( + _In_ PUSHORT Port, + _In_ PUSHORT Buffer, + _In_ ULONG Count, + _In_ ULONG MmioFlags); + +VOID +AtaWriteBlock32( + _In_ PULONG Port, + _In_ PULONG Buffer, + _In_ ULONG Count, + _In_ ULONG MmioFlags); + +VOID +AtaReadBlock32( + _In_ PULONG Port, + _In_ PULONG Buffer, + _In_ ULONG Count, + _In_ ULONG MmioFlags); + +UCHAR +AtaReadPortUchar( + _In_ PUCHAR Port, + _In_ ULONG MmioFlags); + +VOID +AtaWritePortUlong( + _In_ PULONG Port, + _In_ ULONG Value, + _In_ ULONG MmioFlags); + +VOID +PciIdeDmaStop( + _In_ PCHANNEL_DATA_PATA ChanData); + +/* misc ***********************************************************************/ + +#if defined(ATA_DETECT_LEGACY_DEVICES) +CODE_SEG("PAGE") +NTSTATUS +PataGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller, + _In_ PCM_RESOURCE_LIST ResourcesTranslated); +#endif + +CODE_SEG("PAGE") +NTSTATUS +AmdGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller); + +CODE_SEG("PAGE") +NTSTATUS +AtiGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller); + +CODE_SEG("PAGE") +NTSTATUS +CmdGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller); + +CODE_SEG("PAGE") +NTSTATUS +Sil680GetControllerProperties( + _Inout_ PATA_CONTROLLER Controller); + +CODE_SEG("PAGE") +NTSTATUS +IntelGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller); + +CODE_SEG("PAGE") +NTSTATUS +PcTechGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller); + +CODE_SEG("PAGE") +NTSTATUS +SvwPataGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller); + +CODE_SEG("PAGE") +NTSTATUS +SvwSataGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller); + +CODE_SEG("PAGE") +NTSTATUS +ToshibaGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller); + +CODE_SEG("PAGE") +NTSTATUS +ViaGetControllerProperties( + _Inout_ PATA_CONTROLLER Controller); + +CHANNEL_SET_MODE_EX SvwSetTransferMode; + +CODE_SEG("PAGE") +BOOLEAN +SvwHasUdmaCable( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG Channel); + +VOID +ViaClampTimings( + _Inout_ PATA_TIMING Timing); + +FORCEINLINE +ULONG +CountSetBits( + _In_ ULONG x) +{ + x -= x >> 1 & 0x55555555; + x = (x & 0x33333333) + (x >> 2 & 0x33333333); + + return ((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101 >> 24; +} + +FORCEINLINE +BOOLEAN +IsPowerOfTwo( + _In_ ULONG x) +{ + /* Also exclude zero numbers */ + return (x != 0) && ((x & (x - 1)) == 0); +} + +FORCEINLINE +UCHAR +PciRead8( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG ConfigDataOffset) +{ + UCHAR Result; + + PciRead(Controller, &Result, ConfigDataOffset, sizeof(Result)); + return Result; +} + +FORCEINLINE +USHORT +PciRead16( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG ConfigDataOffset) +{ + USHORT Result; + + PciRead(Controller, &Result, ConfigDataOffset, sizeof(Result)); + return Result; +} + +FORCEINLINE +ULONG +PciRead32( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG ConfigDataOffset) +{ + ULONG Result; + + PciRead(Controller, &Result, ConfigDataOffset, sizeof(Result)); + return Result; +} + +FORCEINLINE +VOID +PciWrite8( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG ConfigDataOffset, + _In_ UCHAR Value) +{ + PciWrite(Controller, &Value, ConfigDataOffset, sizeof(Value)); +} + +FORCEINLINE +VOID +PciWrite16( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG ConfigDataOffset, + _In_ USHORT Value) +{ + PciWrite(Controller, &Value, ConfigDataOffset, sizeof(Value)); +} + +FORCEINLINE +VOID +PciWrite32( + _In_ PATA_CONTROLLER Controller, + _In_ ULONG ConfigDataOffset, + _In_ ULONG Value) +{ + PciWrite(Controller, &Value, ConfigDataOffset, sizeof(Value)); +} diff --git a/drivers/storage/ide/pciidex/pciidex.rc b/drivers/storage/ide/pciidex/pciidex.rc index cd3f3f4d374..199e1ee53f2 100644 --- a/drivers/storage/ide/pciidex/pciidex.rc +++ b/drivers/storage/ide/pciidex/pciidex.rc @@ -1,5 +1,5 @@ #define REACTOS_VERSION_DLL -#define REACTOS_STR_FILE_DESCRIPTION "PCI IDE bus driver extension" +#define REACTOS_STR_FILE_DESCRIPTION "ATA Bus Driver Extension" #define REACTOS_STR_INTERNAL_NAME "pciidex" #define REACTOS_STR_ORIGINAL_FILENAME "pciidex.sys" #include diff --git a/drivers/storage/ide/pciidex/pciidex.spec b/drivers/storage/ide/pciidex/pciidex.spec index a5dd343ba7a..e3d16a9903a 100644 --- a/drivers/storage/ide/pciidex/pciidex.spec +++ b/drivers/storage/ide/pciidex/pciidex.spec @@ -2,3 +2,4 @@ @ stdcall PciIdeXGetBusData(ptr ptr long long) @ stdcall PciIdeXInitialize(ptr ptr ptr long) @ stdcall PciIdeXSetBusData(ptr ptr ptr long long) +@ stdcall -version=0x600+ PciIdexGetBusLocation(ptr ptr) diff --git a/drivers/storage/ide/pciidex/pdo.c b/drivers/storage/ide/pciidex/pdo.c index 780d52891ba..4213d1f24ee 100644 --- a/drivers/storage/ide/pciidex/pdo.c +++ b/drivers/storage/ide/pciidex/pdo.c @@ -1,33 +1,170 @@ /* * PROJECT: PCI IDE bus driver extension * LICENSE: See COPYING in the top level directory - * PURPOSE: IRP_MJ_PNP operations for PDOs + * PURPOSE: ATA controller PDO dispatch routines * COPYRIGHT: Copyright 2005 HervĂ© Poussineau * Copyright 2023 Dmitry Borisov */ #include "pciidex.h" -#define NDEBUG -#include +static +CODE_SEG("PAGE") +VOID +PciIdeXPdoFreeDmaResources( + _In_ PCHANNEL_DATA_COMMON ChanData) +{ + PAGED_CODE(); + + if (ChanData->DmaAdapter) + { + ChanData->FreeMemory(ChanData); + + ChanData->DmaAdapter->DmaOperations->PutDmaAdapter(ChanData->DmaAdapter); + ChanData->DmaAdapter = NULL; + } +} + +static +CODE_SEG("PAGE") +PDMA_ADAPTER +PciIdeXPdoGetDmaAdapter( + _In_ PFDO_DEVICE_EXTENSION FdoExt, + _In_ PCHANNEL_DATA_COMMON ChanData) +{ + DEVICE_DESCRIPTION DeviceDescription = { 0 }; + + PAGED_CODE(); + + DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION; + DeviceDescription.Master = TRUE; + DeviceDescription.ScatterGather = TRUE; + DeviceDescription.Dma32BitAddresses = !(ChanData->ChanInfo & CHANNEL_FLAG_64_BIT_DMA); + DeviceDescription.InterfaceType = PCIBus; + DeviceDescription.MaximumLength = ChanData->MaximumTransferLength; + + return IoGetDmaAdapter(FdoExt->Common.LowerDeviceObject, + &DeviceDescription, + &ChanData->MaximumPhysicalPages); +} + +static +CODE_SEG("PAGE") +BOOLEAN +PciIdeXPdoInitDma( + _In_ PFDO_DEVICE_EXTENSION FdoExt, + _In_ PCHANNEL_DATA_COMMON ChanData) +{ + NTSTATUS Status; + + ChanData->DmaAdapter = PciIdeXPdoGetDmaAdapter(FdoExt, ChanData); + if (!ChanData->DmaAdapter) + { + WARN("CH %lu: Unable to get DMA adapter\n", ChanData->Channel); + return FALSE; + } + + Status = ChanData->AllocateMemory(ChanData); + if (!NT_SUCCESS(Status)) + { + WARN("CH %lu: Unable to allocate DMA memory %lx\n", ChanData->Channel, Status); + PciIdeXPdoFreeDmaResources(ChanData); + return FALSE; + } + + /* See what transfer length we actually got. Round down in case we have some extra pages */ + ChanData->Current.MaximumTransferLength = min(ChanData->Current.MaximumTransferLength, + ChanData->MaximumPhysicalPages << PAGE_SHIFT); + INFO("CH %lu: Allocated %lu PRD pages, maximum transfer length 0x%lx\n", + ChanData->Channel, + ChanData->MaximumPhysicalPages, + ChanData->Current.MaximumTransferLength); + + return TRUE; +} static CODE_SEG("PAGE") NTSTATUS PciIdeXPdoStartDevice( - _In_ PPDO_DEVICE_EXTENSION PdoExtension, - _In_ PCM_RESOURCE_LIST ResourceList) + _In_ PPDO_DEVICE_EXTENSION PdoExt, + _In_ PCM_RESOURCE_LIST ResourcesTranslated) { - PUCHAR IoBase; + PFDO_DEVICE_EXTENSION FdoExt = PdoExt->Common.FdoExt; + PATA_CONTROLLER Controller = &FdoExt->Controller; + PCHANNEL_DATA_COMMON ChanData = PdoExt->ChanData; + NTSTATUS Status; PAGED_CODE(); - IoBase = PdoExtension->ParentController->BusMasterPortBase; - if (!IS_PRIMARY_CHANNEL(PdoExtension)) + /* Init default values */ + if (ChanData->MaximumTransferLength == 0) + ChanData->MaximumTransferLength = ATA_MAX_TRANSFER_LENGTH; + ChanData->Current.MaximumTransferLength = ChanData->MaximumTransferLength; + ChanData->Current.TransferModeSupported = ChanData->TransferModeSupported; + + if (!(Controller->Flags & CTRL_FLAG_IS_AHCI)) { - IoBase += BM_SECONDARY_CHANNEL_OFFSET; + PCHANNEL_DATA_PATA PataData = (PCHANNEL_DATA_PATA)ChanData; + + if (!(Controller->Flags & CTRL_FLAG_MANUAL_RES)) + { + Status = PciIdeParseResources(PataData, ResourcesTranslated); + if (!NT_SUCCESS(Status)) + { + ERR("CH %lu: Failed to parse resources 0x%lx\n", ChanData->Channel, Status); + return Status; + } + } + + /* Query the first _GTM data */ + if (!(Controller->Flags & CTRL_FLAG_SATA_HBA_ACPI) && + !(ChanData->ChanInfo & CHANNEL_FLAG_HAS_ACPI_GTM)) + { + if (AtaAcpiGetTimingMode(PdoExt->Common.Self, &PataData->CurrentTimingMode)) + { + INFO("CH %lu: ACPI available\n", ChanData->Channel); + ChanData->ChanInfo |= CHANNEL_FLAG_HAS_ACPI_GTM; + } + } + } + + /* Allocate DMA resources */ + if (ChanData->Current.TransferModeSupported & ~PIO_ALL) + { + if (!PciIdeXPdoInitDma(FdoExt, ChanData)) + { + /* Fall back to PIO */ + ChanData->Current.TransferModeSupported &= PIO_ALL; + INFO("CH %lu: Non DMA channel\n", ChanData->Channel); + } + } + + if (!ChanData->DmaAdapter) + { + /* Check for ATA controllers that do not support PIO operations */ + if (ChanData->ChanInfo & CHANNEL_FLAG_PIO_VIA_DMA) + { + ERR("CH %lu: Unable to fall back to PIO mode\n", ChanData->Channel); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Get maximum physical pages for non DMA channel */ + ChanData->MaximumPhysicalPages = BYTES_TO_PAGES(ChanData->Current.MaximumTransferLength); + } + + if (!(Controller->Flags & CTRL_FLAG_IS_AHCI)) + { + Status = PciIdeConnectInterrupt(ChanData); + if (!NT_SUCCESS(Status)) + { + ERR("CH %lu: Could not connect to interrupt\n", ChanData->Channel, Status); + return Status; + } + + if (!(ChanData->ChanInfo & CHANNEL_FLAG_IO32)) + INFO("CH %lu: 32-bit I/O not supported\n", ChanData->Channel); } - DPRINT("Bus Master Base %p\n", IoBase); return STATUS_SUCCESS; } @@ -36,44 +173,62 @@ static CODE_SEG("PAGE") NTSTATUS PciIdeXPdoStopDevice( - _In_ PPDO_DEVICE_EXTENSION PdoExtension) + _In_ PPDO_DEVICE_EXTENSION PdoExt) { + PCHANNEL_DATA_COMMON ChanData = PdoExt->ChanData; + PATA_CONTROLLER Controller = ChanData->Controller; + PAGED_CODE(); + if (!(Controller->Flags & CTRL_FLAG_IS_AHCI)) + PciIdeFreeResources(ChanData); + + PciIdeXPdoFreeDmaResources(ChanData); + return STATUS_SUCCESS; } -static CODE_SEG("PAGE") NTSTATUS PciIdeXPdoRemoveDevice( _In_ PPDO_DEVICE_EXTENSION PdoExtension, + _In_ PIRP Irp, _In_ BOOLEAN FinalRemove) { - PFDO_DEVICE_EXTENSION FdoExtension = PdoExtension->ParentController; - ULONG i; + PFDO_DEVICE_EXTENSION FdoExtension = PdoExtension->Common.FdoExt; + NTSTATUS Status; + BOOLEAN WasRemoved = FALSE; PAGED_CODE(); - if (FinalRemove && PdoExtension->ReportedMissing) + PciIdeXPdoStopDevice(PdoExtension); + + if (FinalRemove && (PdoExtension->Flags & PDO_FLAG_REPORTED_MISSING)) { - ExAcquireFastMutex(&FdoExtension->DeviceSyncMutex); + IoReleaseRemoveLockAndWait(&PdoExtension->Common.RemoveLock, Irp); - for (i = 0; i < MAX_IDE_CHANNEL; ++i) - { - if (FdoExtension->Channels[i] == PdoExtension) - { - FdoExtension->Channels[i] = NULL; - break; - } - } + if (Irp) + ExAcquireFastMutex(&FdoExtension->PdoListSyncMutex); - ExReleaseFastMutex(&FdoExtension->DeviceSyncMutex); + RemoveEntryList(&PdoExtension->ListEntry); + + if (Irp) + ExReleaseFastMutex(&FdoExtension->PdoListSyncMutex); IoDeleteDevice(PdoExtension->Common.Self); + WasRemoved = TRUE; } - return STATUS_SUCCESS; + Status = STATUS_SUCCESS; + if (Irp) + { + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + if (!WasRemoved) + IoReleaseRemoveLock(&PdoExtension->Common.RemoveLock, Irp); + return Status; } static @@ -105,9 +260,9 @@ PciIdeXPdoQueryTargetDeviceRelations( PAGED_CODE(); - DeviceRelations = ExAllocatePoolWithTag(PagedPool, - sizeof(DEVICE_RELATIONS), - TAG_PCIIDEX); + DeviceRelations = ExAllocatePoolUninitialized(PagedPool, + sizeof(*DeviceRelations), + TAG_PCIIDEX); if (!DeviceRelations) return STATUS_INSUFFICIENT_RESOURCES; @@ -119,82 +274,6 @@ PciIdeXPdoQueryTargetDeviceRelations( return STATUS_SUCCESS; } -static IO_COMPLETION_ROUTINE PciIdeXOnRepeaterCompletion; - -static -NTSTATUS -NTAPI -PciIdeXOnRepeaterCompletion( - _In_ PDEVICE_OBJECT DeviceObject, - _In_ PIRP Irp, - _In_reads_opt_(_Inexpressible_("varies")) PVOID Context) -{ - UNREFERENCED_PARAMETER(DeviceObject); - - if (Irp->PendingReturned) - KeSetEvent(Context, IO_NO_INCREMENT, FALSE); - - return STATUS_MORE_PROCESSING_REQUIRED; -} - -static -CODE_SEG("PAGE") -NTSTATUS -PciIdeXPdoRepeatRequest( - _In_ PPDO_DEVICE_EXTENSION PdoExtension, - _In_ PIRP Irp, - _In_opt_ PDEVICE_CAPABILITIES DeviceCapabilities) -{ - PDEVICE_OBJECT Fdo, TopDeviceObject; - PIO_STACK_LOCATION IoStack, SubStack; - PIRP SubIrp; - KEVENT Event; - NTSTATUS Status; - - PAGED_CODE(); - - Fdo = PdoExtension->ParentController->Common.Self; - TopDeviceObject = IoGetAttachedDeviceReference(Fdo); - - SubIrp = IoAllocateIrp(TopDeviceObject->StackSize, FALSE); - if (!SubIrp) - { - ObDereferenceObject(TopDeviceObject); - return STATUS_INSUFFICIENT_RESOURCES; - } - - KeInitializeEvent(&Event, NotificationEvent, FALSE); - - IoStack = IoGetCurrentIrpStackLocation(Irp); - SubStack = IoGetNextIrpStackLocation(SubIrp); - RtlCopyMemory(SubStack, IoStack, sizeof(IO_STACK_LOCATION)); - - if (DeviceCapabilities) - SubStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities; - - IoSetCompletionRoutine(SubIrp, - PciIdeXOnRepeaterCompletion, - &Event, - TRUE, - TRUE, - TRUE); - - SubIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; - - Status = IoCallDriver(TopDeviceObject, SubIrp); - if (Status == STATUS_PENDING) - { - KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); - } - - ObDereferenceObject(TopDeviceObject); - - Status = SubIrp->IoStatus.Status; - IoFreeIrp(SubIrp); - - return Status; -} - static CODE_SEG("PAGE") NTSTATUS @@ -202,6 +281,8 @@ PciIdeXPdoQueryCapabilities( _In_ PPDO_DEVICE_EXTENSION PdoExtension, _In_ PIRP Irp) { + PFDO_DEVICE_EXTENSION FdoExt; + PATA_CONTROLLER Controller; DEVICE_CAPABILITIES ParentCapabilities; PDEVICE_CAPABILITIES DeviceCapabilities; PIO_STACK_LOCATION IoStack; @@ -215,7 +296,7 @@ PciIdeXPdoQueryCapabilities( ParentCapabilities.Version = 1; ParentCapabilities.Address = MAXULONG; ParentCapabilities.UINumber = MAXULONG; - Status = PciIdeXPdoRepeatRequest(PdoExtension, Irp, &ParentCapabilities); + Status = PciIdeXPnpRepeatRequest(&PdoExtension->Common, Irp, &ParentCapabilities); if (!NT_SUCCESS(Status)) return Status; @@ -227,26 +308,52 @@ PciIdeXPdoQueryCapabilities( DeviceCapabilities->UniqueID = FALSE; DeviceCapabilities->Address = PdoExtension->Channel; + FdoExt = PdoExtension->Common.FdoExt; + Controller = &FdoExt->Controller; + if (Controller->Flags & CTRL_FLAG_SATA_HBA_ACPI) + { + /* Convert to a root port number */ + DeviceCapabilities->Address <<= 16; + DeviceCapabilities->Address |= 0xFFFF; + } + return STATUS_SUCCESS; } static CODE_SEG("PAGE") -NTSTATUS -PciIdeXPdoQueryPnpDeviceState( - _In_ PPDO_DEVICE_EXTENSION PdoExtension, - _In_ PIRP Irp) +VOID +PciIdeXMakePortResource( + _Out_ PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor, + _In_ ULONG IoBase, + _In_ ULONG Length) { PAGED_CODE(); - if (PdoExtension->Common.PageFiles || - PdoExtension->Common.HibernateFiles || - PdoExtension->Common.DumpFiles) - { - Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE; - } + Descriptor->Type = CmResourceTypePort; + Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; + Descriptor->Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE; + Descriptor->u.Port.Length = Length; + Descriptor->u.Port.Start.LowPart = IoBase; +} - return STATUS_SUCCESS; +static +CODE_SEG("PAGE") +VOID +PciIdeXMakePortRequirement( + _Out_ PIO_RESOURCE_DESCRIPTOR Descriptor, + _In_ ULONG IoBase, + _In_ ULONG Length) +{ + PAGED_CODE(); + + Descriptor->Type = CmResourceTypePort; + Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; + Descriptor->Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE; + Descriptor->u.Port.Length = Length; + Descriptor->u.Port.Alignment = 1; + Descriptor->u.Port.MinimumAddress.LowPart = IoBase; + Descriptor->u.Port.MaximumAddress.LowPart = IoBase + Length - 1; } static @@ -256,25 +363,36 @@ PciIdeXPdoQueryResources( _In_ PPDO_DEVICE_EXTENSION PdoExtension, _In_ PIRP Irp) { - PFDO_DEVICE_EXTENSION FdoExtension; + PFDO_DEVICE_EXTENSION FdoExtension = PdoExtension->Common.FdoExt; + PATA_CONTROLLER Controller = &FdoExtension->Controller; IDE_CHANNEL_STATE ChannelState; PCM_RESOURCE_LIST ResourceList; PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; - ULONG CommandPortBase, ControlPortBase, InterruptVector; - ULONG ListSize; + ULONG CommandPortBase, ControlPortBase, InterruptVector, ListSize; + BOOLEAN AppendSecondaryResources; PAGED_CODE(); - FdoExtension = PdoExtension->ParentController; - if (FdoExtension->InNativeMode) + if (Controller->Flags & (CTRL_FLAG_NATIVE_PCI | CTRL_FLAG_NON_PNP)) return Irp->IoStatus.Status; - ChannelState = PciIdeXChannelState(FdoExtension, PdoExtension->Channel); + ChannelState = PciIdeXGetChannelState(Controller, PdoExtension->Channel); if (ChannelState == ChannelDisabled) return Irp->IoStatus.Status; - ListSize = sizeof(CM_RESOURCE_LIST) + - sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * (PCIIDE_LEGACY_RESOURCE_COUNT - 1); + /* + * There are single-channel IDE controllers that only support the primary channel + * (e.g. Intel SCH or VIA VT6415). In that case, + * some secondary channels may still decode its hardware resources. + * To handle all cases, append a dummy resource block to the primary channel + * so that the PnP manager does not allocate this I/O range to other devices in the system. + */ + AppendSecondaryResources = (Controller->MaxChannels == 1); + + ListSize = FIELD_OFFSET(CM_RESOURCE_LIST, List[0].PartialResourceList.PartialDescriptors) + + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * PCIIDE_LEGACY_RESOURCE_COUNT; + if (AppendSecondaryResources) + ListSize += sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * 2; ResourceList = ExAllocatePoolZero(PagedPool, ListSize, TAG_PCIIDEX); if (!ResourceList) return STATUS_INSUFFICIENT_RESOURCES; @@ -285,6 +403,8 @@ PciIdeXPdoQueryResources( ResourceList->List[0].PartialResourceList.Version = 1; ResourceList->List[0].PartialResourceList.Revision = 1; ResourceList->List[0].PartialResourceList.Count = PCIIDE_LEGACY_RESOURCE_COUNT; + if (AppendSecondaryResources) + ResourceList->List[0].PartialResourceList.Count += 2; if (IS_PRIMARY_CHANNEL(PdoExtension)) { @@ -302,20 +422,10 @@ PciIdeXPdoQueryResources( Descriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[0]; /* Command port base */ - Descriptor->Type = CmResourceTypePort; - Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; - Descriptor->Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE; - Descriptor->u.Port.Length = PCIIDE_LEGACY_COMMAND_IO_RANGE_LENGTH; - Descriptor->u.Port.Start.LowPart = CommandPortBase; - ++Descriptor; + PciIdeXMakePortResource(Descriptor++, CommandPortBase, PCIIDE_LEGACY_COMMAND_IO_RANGE_LENGTH); /* Control port base */ - Descriptor->Type = CmResourceTypePort; - Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; - Descriptor->Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE; - Descriptor->u.Port.Length = PCIIDE_LEGACY_CONTROL_IO_RANGE_LENGTH; - Descriptor->u.Port.Start.LowPart = ControlPortBase; - ++Descriptor; + PciIdeXMakePortResource(Descriptor++, ControlPortBase, PCIIDE_LEGACY_CONTROL_IO_RANGE_LENGTH); /* Interrupt */ Descriptor->Type = CmResourceTypeInterrupt; @@ -325,6 +435,17 @@ PciIdeXPdoQueryResources( Descriptor->u.Interrupt.Vector = InterruptVector; Descriptor->u.Interrupt.Affinity = (KAFFINITY)-1; + if (AppendSecondaryResources) + { + PciIdeXMakePortResource(++Descriptor, + PCIIDE_LEGACY_SECONDARY_COMMAND_BASE, + PCIIDE_LEGACY_COMMAND_IO_RANGE_LENGTH); + + PciIdeXMakePortResource(++Descriptor, + PCIIDE_LEGACY_SECONDARY_CONTROL_BASE, + PCIIDE_LEGACY_CONTROL_IO_RANGE_LENGTH); + } + Irp->IoStatus.Information = (ULONG_PTR)ResourceList; return STATUS_SUCCESS; } @@ -336,25 +457,29 @@ PciIdeXPdoQueryResourceRequirements( _In_ PPDO_DEVICE_EXTENSION PdoExtension, _In_ PIRP Irp) { - PFDO_DEVICE_EXTENSION FdoExtension; + PFDO_DEVICE_EXTENSION FdoExtension = PdoExtension->Common.FdoExt; + PATA_CONTROLLER Controller = &FdoExtension->Controller; PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList; PIO_RESOURCE_DESCRIPTOR Descriptor; IDE_CHANNEL_STATE ChannelState; - ULONG CommandPortBase, ControlPortBase, InterruptVector; - ULONG ListSize; + ULONG CommandPortBase, ControlPortBase, InterruptVector, ListSize; + BOOLEAN AppendSecondaryResources; PAGED_CODE(); - FdoExtension = PdoExtension->ParentController; - if (FdoExtension->InNativeMode) + if (Controller->Flags & (CTRL_FLAG_NATIVE_PCI | CTRL_FLAG_NON_PNP)) return Irp->IoStatus.Status; - ChannelState = PciIdeXChannelState(FdoExtension, PdoExtension->Channel); + ChannelState = PciIdeXGetChannelState(Controller, PdoExtension->Channel); if (ChannelState == ChannelDisabled) return Irp->IoStatus.Status; - ListSize = sizeof(IO_RESOURCE_REQUIREMENTS_LIST) + - sizeof(IO_RESOURCE_DESCRIPTOR) * (PCIIDE_LEGACY_RESOURCE_COUNT - 1); + AppendSecondaryResources = (Controller->MaxChannels == 1); + + ListSize = FIELD_OFFSET(IO_RESOURCE_REQUIREMENTS_LIST, List[0].Descriptors) + + sizeof(IO_RESOURCE_DESCRIPTOR) * PCIIDE_LEGACY_RESOURCE_COUNT; + if (AppendSecondaryResources) + ListSize += sizeof(IO_RESOURCE_DESCRIPTOR) * 2; RequirementsList = ExAllocatePoolZero(PagedPool, ListSize, TAG_PCIIDEX); if (!RequirementsList) return STATUS_INSUFFICIENT_RESOURCES; @@ -366,6 +491,8 @@ PciIdeXPdoQueryResourceRequirements( RequirementsList->List[0].Version = 1; RequirementsList->List[0].Revision = 1; RequirementsList->List[0].Count = PCIIDE_LEGACY_RESOURCE_COUNT; + if (AppendSecondaryResources) + RequirementsList->List[0].Count += 2; if (IS_PRIMARY_CHANNEL(PdoExtension)) { @@ -383,26 +510,14 @@ PciIdeXPdoQueryResourceRequirements( Descriptor = &RequirementsList->List[0].Descriptors[0]; /* Command port base */ - Descriptor->Type = CmResourceTypePort; - Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; - Descriptor->Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE; - Descriptor->u.Port.Length = PCIIDE_LEGACY_COMMAND_IO_RANGE_LENGTH; - Descriptor->u.Port.Alignment = 1; - Descriptor->u.Port.MinimumAddress.LowPart = CommandPortBase; - Descriptor->u.Port.MaximumAddress.LowPart = CommandPortBase + - PCIIDE_LEGACY_COMMAND_IO_RANGE_LENGTH - 1; - ++Descriptor; + PciIdeXMakePortRequirement(Descriptor++, + CommandPortBase, + PCIIDE_LEGACY_COMMAND_IO_RANGE_LENGTH); /* Control port base */ - Descriptor->Type = CmResourceTypePort; - Descriptor->ShareDisposition = CmResourceShareDeviceExclusive; - Descriptor->Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE; - Descriptor->u.Port.Length = PCIIDE_LEGACY_CONTROL_IO_RANGE_LENGTH; - Descriptor->u.Port.Alignment = 1; - Descriptor->u.Port.MinimumAddress.LowPart = ControlPortBase; - Descriptor->u.Port.MaximumAddress.LowPart = ControlPortBase + - PCIIDE_LEGACY_CONTROL_IO_RANGE_LENGTH - 1; - ++Descriptor; + PciIdeXMakePortRequirement(Descriptor++, + ControlPortBase, + PCIIDE_LEGACY_CONTROL_IO_RANGE_LENGTH); /* Interrupt */ Descriptor->Type = CmResourceTypeInterrupt; @@ -411,6 +526,17 @@ PciIdeXPdoQueryResourceRequirements( Descriptor->u.Interrupt.MinimumVector = InterruptVector; Descriptor->u.Interrupt.MaximumVector = InterruptVector; + if (AppendSecondaryResources) + { + PciIdeXMakePortRequirement(++Descriptor, + PCIIDE_LEGACY_SECONDARY_COMMAND_BASE, + PCIIDE_LEGACY_COMMAND_IO_RANGE_LENGTH); + + PciIdeXMakePortRequirement(++Descriptor, + PCIIDE_LEGACY_SECONDARY_CONTROL_BASE, + PCIIDE_LEGACY_CONTROL_IO_RANGE_LENGTH); + } + Irp->IoStatus.Information = (ULONG_PTR)RequirementsList; return STATUS_SUCCESS; } @@ -423,7 +549,7 @@ PciIdeXGetControllerVendorId( { PAGED_CODE(); - switch (FdoExtension->VendorId) + switch (FdoExtension->Controller.Pci.VendorID) { case 0x0E11: return L"Compaq"; @@ -455,9 +581,9 @@ PciIdeXGetControllerDeviceId( PAGED_CODE(); /* Intel */ - if (FdoExtension->VendorId == 0x8086) + if (FdoExtension->Controller.Pci.VendorID == 0x8086) { - switch (FdoExtension->DeviceId) + switch (FdoExtension->Controller.Pci.DeviceID) { case 0x1230: return L"PIIX"; @@ -471,6 +597,7 @@ PciIdeXGetControllerDeviceId( } } + /* Only certain controllers have a non-numeric identifier */ return NULL; } @@ -492,209 +619,165 @@ PciIdeXPdoQueryId( IoStack = IoGetCurrentIrpStackLocation(Irp); switch (IoStack->Parameters.QueryId.IdType) { - case BusQueryDeviceID: - { - static const WCHAR PciIdeDeviceId[] = L"PCIIDE\\IDEChannel"; - - Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(PciIdeDeviceId), TAG_PCIIDEX); - if (!Buffer) - return STATUS_INSUFFICIENT_RESOURCES; - - RtlCopyMemory(Buffer, PciIdeDeviceId, sizeof(PciIdeDeviceId)); - - DPRINT("Device ID: '%S'\n", Buffer); - break; - } - - case BusQueryHardwareIDs: - { - PFDO_DEVICE_EXTENSION FdoExtension; - PCWSTR VendorString; - PWCHAR IdStart; - - DBG_UNREFERENCED_LOCAL_VARIABLE(IdStart); - - /* Maximum string length */ - CharCount = sizeof("WinBond-1234") + - sizeof("Secondary_IDE_Channel") + - sizeof(IdeCompatibleId) + - sizeof(ANSI_NULL); /* multi-string */ - - Buffer = ExAllocatePoolWithTag(PagedPool, - CharCount * sizeof(WCHAR), - TAG_PCIIDEX); - if (!Buffer) - return STATUS_INSUFFICIENT_RESOURCES; - - FdoExtension = PdoExtension->ParentController; - VendorString = PciIdeXGetControllerVendorId(FdoExtension); - - DPRINT("HardwareIDs:\n"); - - /* ID 1 */ - if (VendorString) - { - PCWSTR DeviceString = PciIdeXGetControllerDeviceId(FdoExtension); - - if (DeviceString) - { - Status = RtlStringCchPrintfExW(Buffer, - CharCount, - &End, - &Remaining, - 0, - L"%ls-%ls", - VendorString, - DeviceString); - } - else - { - Status = RtlStringCchPrintfExW(Buffer, - CharCount, - &End, - &Remaining, - 0, - L"%ls-%04x", - VendorString, - FdoExtension->DeviceId); - } - } - else - { - Status = RtlStringCchPrintfExW(Buffer, - CharCount, - &End, - &Remaining, - 0, - L"%04x-%04x", - FdoExtension->VendorId, - FdoExtension->DeviceId); - } - ASSERT(NT_SUCCESS(Status)); - - DPRINT(" '%S'\n", Buffer); - - ++End; - --Remaining; - - /* ID 2 */ - IdStart = End; - Status = RtlStringCchPrintfExW(End, - Remaining, - &End, - &Remaining, - 0, - L"%ls", - IS_PRIMARY_CHANNEL(PdoExtension) ? - L"Primary_IDE_Channel" : - L"Secondary_IDE_Channel"); - ASSERT(NT_SUCCESS(Status)); - - DPRINT(" '%S'\n", IdStart); - - ++End; - --Remaining; - - /* ID 3 */ - IdStart = End; - Status = RtlStringCchPrintfExW(End, - Remaining, - &End, - &Remaining, - 0, - L"%ls", - IdeCompatibleId); - ASSERT(NT_SUCCESS(Status)); - - DPRINT(" '%S'\n", IdStart); - - *++End = UNICODE_NULL; /* multi-string */ - break; - } - - case BusQueryCompatibleIDs: - { - Buffer = ExAllocatePoolWithTag(PagedPool, - sizeof(IdeCompatibleId) + sizeof(UNICODE_NULL), - TAG_PCIIDEX); - if (!Buffer) - return STATUS_INSUFFICIENT_RESOURCES; - - RtlCopyMemory(Buffer, IdeCompatibleId, sizeof(IdeCompatibleId)); - - Buffer[sizeof(IdeCompatibleId) / sizeof(WCHAR)] = UNICODE_NULL; /* multi-string */ - - DPRINT("Compatible ID: '%S'\n", Buffer); - break; - } - - case BusQueryInstanceID: - { - CharCount = sizeof("0"); - - Buffer = ExAllocatePoolWithTag(PagedPool, - CharCount * sizeof(WCHAR), - TAG_PCIIDEX); - if (!Buffer) - return STATUS_INSUFFICIENT_RESOURCES; - - Status = RtlStringCchPrintfExW(Buffer, - CharCount, - NULL, - NULL, - 0, - L"%lu", - PdoExtension->Channel); - ASSERT(NT_SUCCESS(Status)); - - DPRINT("Instance ID: '%S'\n", Buffer); - break; - } - - default: - return Irp->IoStatus.Status; - } - - Irp->IoStatus.Information = (ULONG_PTR)Buffer; - return STATUS_SUCCESS; -} - -static -CODE_SEG("PAGE") -NTSTATUS -PciIdeXPdoQueryDeviceText( - _In_ PPDO_DEVICE_EXTENSION PdoExtension, - _In_ PIRP Irp) -{ - PIO_STACK_LOCATION IoStack; - PWCHAR Buffer; - ULONG Size; - - PAGED_CODE(); - - IoStack = IoGetCurrentIrpStackLocation(Irp); - switch (IoStack->Parameters.QueryDeviceText.DeviceTextType) - { - case DeviceTextLocationInformation: + case BusQueryDeviceID: { - static const WCHAR PrimaryChannelText[] = L"Primary channel"; - static const WCHAR SecondaryChannelText[] = L"Secondary channel"; + static const WCHAR PciIdeDeviceId[] = L"PCIIDE\\IDEChannel"; - if (IS_PRIMARY_CHANNEL(PdoExtension)) - Size = sizeof(PrimaryChannelText); - else - Size = sizeof(SecondaryChannelText); - - Buffer = ExAllocatePoolWithTag(PagedPool, Size, TAG_PCIIDEX); + Buffer = ExAllocatePoolUninitialized(PagedPool, sizeof(PciIdeDeviceId), TAG_PCIIDEX); if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES; - RtlCopyMemory(Buffer, - IS_PRIMARY_CHANNEL(PdoExtension) ? - PrimaryChannelText : SecondaryChannelText, - Size); + RtlCopyMemory(Buffer, PciIdeDeviceId, sizeof(PciIdeDeviceId)); - DPRINT("Device ID: '%S'\n", Buffer); + INFO("Device ID: '%S'\n", Buffer); + break; + } + + case BusQueryHardwareIDs: + { + PFDO_DEVICE_EXTENSION FdoExtension; + PWCHAR IdStart; + + DBG_UNREFERENCED_LOCAL_VARIABLE(IdStart); + + /* Maximum string length */ + CharCount = sizeof("WinBond-1234") + + sizeof("Internal_IDE_Channel") + + RTL_NUMBER_OF(IdeCompatibleId) + + sizeof(ANSI_NULL); /* multi-string */ + + Buffer = ExAllocatePoolUninitialized(PagedPool, + CharCount * sizeof(WCHAR), + TAG_PCIIDEX); + if (!Buffer) + return STATUS_INSUFFICIENT_RESOURCES; + + INFO("HardwareIDs:\n"); + + FdoExtension = PdoExtension->Common.FdoExt; + End = Buffer; + Remaining = CharCount; + + if (!(FdoExtension->Controller.Flags & CTRL_FLAG_NON_PNP)) + { + PCWSTR VendorString = PciIdeXGetControllerVendorId(FdoExtension); + + /* ID 1 */ + if (VendorString) + { + PCWSTR DeviceString = PciIdeXGetControllerDeviceId(FdoExtension); + + if (DeviceString) + { + Status = RtlStringCchPrintfExW(End, + Remaining, + &End, + &Remaining, + 0, + L"%ls-%ls", + VendorString, + DeviceString); + } + else + { + Status = RtlStringCchPrintfExW(End, + Remaining, + &End, + &Remaining, + 0, + L"%ls-%04x", + VendorString, + FdoExtension->Controller.Pci.DeviceID); + } + } + else + { + Status = RtlStringCchPrintfExW(End, + Remaining, + &End, + &Remaining, + 0, + L"%04x-%04x", + FdoExtension->Controller.Pci.VendorID, + FdoExtension->Controller.Pci.DeviceID); + } + ASSERT(NT_SUCCESS(Status)); + + INFO(" '%S'\n", Buffer); + + ++End; + --Remaining; + } + + /* ID 2 */ + IdStart = End; + Status = RtlStringCchPrintfExW(End, + Remaining, + &End, + &Remaining, + 0, + L"%hs", + "Internal_IDE_Channel"); + ASSERT(NT_SUCCESS(Status)); + + INFO(" '%S'\n", IdStart); + + ++End; + --Remaining; + + /* ID 3 */ + IdStart = End; + Status = RtlStringCchPrintfExW(End, + Remaining, + &End, + &Remaining, + 0, + L"%ls", + IdeCompatibleId); + ASSERT(NT_SUCCESS(Status)); + + INFO(" '%S'\n", IdStart); + + *++End = UNICODE_NULL; /* multi-string */ + break; + } + + case BusQueryCompatibleIDs: + { + Buffer = ExAllocatePoolUninitialized(PagedPool, + sizeof(IdeCompatibleId) + sizeof(UNICODE_NULL), + TAG_PCIIDEX); + if (!Buffer) + return STATUS_INSUFFICIENT_RESOURCES; + + RtlCopyMemory(Buffer, IdeCompatibleId, sizeof(IdeCompatibleId)); + + Buffer[sizeof(IdeCompatibleId) / sizeof(WCHAR)] = UNICODE_NULL; /* multi-string */ + + INFO("Compatible ID: '%S'\n", Buffer); + break; + } + + case BusQueryInstanceID: + { + CharCount = sizeof("00"); + + Buffer = ExAllocatePoolUninitialized(PagedPool, + CharCount * sizeof(WCHAR), + TAG_PCIIDEX); + if (!Buffer) + return STATUS_INSUFFICIENT_RESOURCES; + + Status = RtlStringCchPrintfExW(Buffer, + CharCount, + NULL, + NULL, + 0, + L"%lu", + PdoExtension->Channel); + ASSERT(NT_SUCCESS(Status)); + + INFO("Instance ID: '%S'\n", Buffer); break; } @@ -709,45 +792,190 @@ PciIdeXPdoQueryDeviceText( static CODE_SEG("PAGE") NTSTATUS -PciIdeXPdoQueryDeviceUsageNotification( +PciIdeXPdoQueryDeviceText( _In_ PPDO_DEVICE_EXTENSION PdoExtension, _In_ PIRP Irp) { PIO_STACK_LOCATION IoStack; NTSTATUS Status; - volatile LONG* Counter; + PWCHAR Buffer; + size_t CharCount; PAGED_CODE(); - Status = PciIdeXPdoRepeatRequest(PdoExtension, Irp, NULL); - if (!NT_SUCCESS(Status)) - return Status; - IoStack = IoGetCurrentIrpStackLocation(Irp); - switch (IoStack->Parameters.UsageNotification.Type) + switch (IoStack->Parameters.QueryDeviceText.DeviceTextType) { - case DeviceUsageTypePaging: - Counter = &PdoExtension->Common.PageFiles; - break; + case DeviceTextDescription: + { + CharCount = sizeof("ATA Channel 99"); - case DeviceUsageTypeHibernation: - Counter = &PdoExtension->Common.HibernateFiles; - break; + Buffer = ExAllocatePoolUninitialized(PagedPool, + CharCount * sizeof(WCHAR), + TAG_PCIIDEX); + if (!Buffer) + return STATUS_INSUFFICIENT_RESOURCES; - case DeviceUsageTypeDumpFile: - Counter = &PdoExtension->Common.DumpFiles; + Status = RtlStringCchPrintfExW(Buffer, + CharCount, + NULL, + NULL, + 0, + L"ATA Channel %lu", + PdoExtension->Channel); + ASSERT(NT_SUCCESS(Status)); + + INFO("Device Description: '%S'\n", Buffer); break; + } + + case DeviceTextLocationInformation: + { + CharCount = sizeof("Channel 99"); + + Buffer = ExAllocatePoolUninitialized(PagedPool, + CharCount * sizeof(WCHAR), + TAG_PCIIDEX); + if (!Buffer) + return STATUS_INSUFFICIENT_RESOURCES; + + Status = RtlStringCchPrintfExW(Buffer, + CharCount, + NULL, + NULL, + 0, + L"Channel %lu", + PdoExtension->Channel); + ASSERT(NT_SUCCESS(Status)); + + INFO("Device Location: '%S'\n", Buffer); + break; + } default: - return Status; + return Irp->IoStatus.Status; } - IoAdjustPagingPathCount(Counter, IoStack->Parameters.UsageNotification.InPath); - IoInvalidateDeviceState(PdoExtension->Common.Self); + Irp->IoStatus.Information = (ULONG_PTR)Buffer; + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +PciIdeXQueryPciIdeInterface( + _In_ PPDO_DEVICE_EXTENSION PdoExt, + _In_ PIRP Irp, + _In_ PIO_STACK_LOCATION IoStack) +{ + PFDO_DEVICE_EXTENSION FdoExt = PdoExt->Common.FdoExt; + PATA_CONTROLLER Controller = &FdoExt->Controller; + PCHANNEL_DATA_COMMON ChanData; + PPCIIDEX_CHANNEL_INTERFACE ChannelInterface; + + PAGED_CODE(); + + if (IoStack->Parameters.QueryInterface.Size < sizeof(*ChannelInterface)) + return Irp->IoStatus.Status; + + if (IoStack->Parameters.QueryInterface.Version != PCIIDEX_INTERFACE_VERSION) + return Irp->IoStatus.Status; + + ChanData = PdoExt->ChanData; + + ChannelInterface = (PPCIIDEX_CHANNEL_INTERFACE)IoStack->Parameters.QueryInterface.Interface; + ChannelInterface->AttachChannel = AtaCtrlAttachChannel; + ChannelInterface->ChannelContext = ChanData; + ChannelInterface->Channel = ChanData->Channel; + ChannelInterface->MaximumTransferLength = ChanData->Current.MaximumTransferLength; + ChannelInterface->TransferModeSupported = ChanData->Current.TransferModeSupported; + ChannelInterface->SetTransferMode = AtaCtrlSetTransferMode; + ChannelInterface->SetDeviceData = AtaCtrlSetDeviceData; + ChannelInterface->GetInitTaskFile = AtaCtrlGetInitTaskFile; + ChannelInterface->DowngradeInterfaceSpeed = AtaCtrlDowngradeInterfaceSpeed; + ChannelInterface->PreparePrdTable = ChanData->PreparePrdTable; + ChannelInterface->PrepareIo = ChanData->PrepareIo; + ChannelInterface->StartIo = ChanData->StartIo; + ChannelInterface->MaximumPhysicalPages = ChanData->MaximumPhysicalPages; + ChannelInterface->QueueDepth = Controller->QueueDepth; + ChannelInterface->DmaAdapter = ChanData->DmaAdapter; + ChannelInterface->ChannelObject = PdoExt->Common.Self; + ChannelInterface->AbortChannel = AtaCtrlAbortChannel; + + if (Controller->Flags & CTRL_FLAG_IS_AHCI) + { + ChannelInterface->AllocateSlot = AtaAhciAllocateSlot; + ChannelInterface->ResetChannel = AtaAhciResetChannel; + ChannelInterface->EnumerateChannel = AtaAhciEnumerateChannel; + ChannelInterface->IdentifyDevice = AtaAhciIdentifyDevice; + ChannelInterface->MaxTargetId = AtaAhciChannelGetMaximumDeviceCount(ChanData); + } + else + { + ChannelInterface->AllocateSlot = PataAllocateSlot; + ChannelInterface->ResetChannel = PataResetChannel; + ChannelInterface->EnumerateChannel = PataEnumerateChannel; + ChannelInterface->IdentifyDevice = PataIdentifyDevice; + ChannelInterface->MaxTargetId = PataChannelGetMaximumDeviceCount(ChanData); + } + + ChannelInterface->PortContext = &ChanData->PortContext; + ChannelInterface->PortNotification = &ChanData->PortNotification; + ChannelInterface->Slots = &ChanData->Slots; + + if (Controller->Flags & CTRL_FLAG_IS_AHCI) + ChannelInterface->InterruptObject = Controller->InterruptObject; + else + ChannelInterface->InterruptObject = ChanData->InterruptObject; + + if (ChanData->ChanInfo & CHANNEL_FLAG_PIO_VIA_DMA) + ChannelInterface->Flags |= ATA_CHANNEL_FLAG_PIO_VIA_DMA; + + if (ChanData->ChanInfo & CHANNEL_FLAG_IS_EXTERNAL) + ChannelInterface->Flags |= ATA_CHANNEL_FLAG_IS_EXTERNAL; + + if (ChanData->ChanInfo & CHANNEL_FLAG_HAS_NCQ) + ChannelInterface->Flags |= ATA_CHANNEL_FLAG_NCQ; + + if (Controller->Flags & CTRL_FLAG_IS_AHCI) + ChannelInterface->Flags |= ATA_CHANNEL_FLAG_IS_AHCI; + + if (ChanData->ChanInfo & CHANNEL_FLAG_PIO_FOR_LBA48_XFER) + ChannelInterface->Flags |= ATA_CHANNEL_FLAG_PIO_FOR_LBA48_XFER; + + if (Controller->Flags & CTRL_FLAG_IS_SIMPLEX) + ChannelInterface->HwSyncObject = Controller->HwSyncObject; return STATUS_SUCCESS; } +static +CODE_SEG("PAGE") +NTSTATUS +PciIdeXPdoQueryInterface( + _In_ PPDO_DEVICE_EXTENSION PdoExtension, + _Inout_ PIRP Irp) +{ + NTSTATUS Status; + PIO_STACK_LOCATION IoStack; + + PAGED_CODE(); + + IoStack = IoGetCurrentIrpStackLocation(Irp); + + if (IsEqualGUIDAligned(IoStack->Parameters.QueryInterface.InterfaceType, + &GUID_PCIIDE_INTERFACE_ROS)) + { + Status = PciIdeXQueryPciIdeInterface(PdoExtension, Irp, IoStack); + } + else + { + Status = Irp->IoStatus.Status; + } + + return Status; +} + static CODE_SEG("PAGE") NTSTATUS @@ -760,12 +988,21 @@ PciIdeXPdoDispatchPnp( PAGED_CODE(); + Status = IoAcquireRemoveLock(&PdoExtension->Common.RemoveLock, Irp); + if (!NT_SUCCESS(Status)) + { + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; + } + IoStack = IoGetCurrentIrpStackLocation(Irp); switch (IoStack->MinorFunction) { case IRP_MN_START_DEVICE: Status = PciIdeXPdoStartDevice(PdoExtension, - IoStack->Parameters.StartDevice.AllocatedResources); + IoStack->Parameters.StartDevice.AllocatedResourcesTranslated); break; case IRP_MN_STOP_DEVICE: @@ -784,9 +1021,9 @@ PciIdeXPdoDispatchPnp( case IRP_MN_SURPRISE_REMOVAL: case IRP_MN_REMOVE_DEVICE: - Status = PciIdeXPdoRemoveDevice(PdoExtension, - IoStack->MinorFunction == IRP_MN_REMOVE_DEVICE); - break; + return PciIdeXPdoRemoveDevice(PdoExtension, + Irp, + IoStack->MinorFunction == IRP_MN_REMOVE_DEVICE); case IRP_MN_QUERY_DEVICE_RELATIONS: if (IoStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation) @@ -800,7 +1037,7 @@ PciIdeXPdoDispatchPnp( break; case IRP_MN_QUERY_PNP_DEVICE_STATE: - Status = PciIdeXPdoQueryPnpDeviceState(PdoExtension, Irp); + Status = PciIdeXPnpQueryPnpDeviceState(&PdoExtension->Common, Irp); break; case IRP_MN_QUERY_RESOURCES: @@ -820,7 +1057,11 @@ PciIdeXPdoDispatchPnp( break; case IRP_MN_DEVICE_USAGE_NOTIFICATION: - Status = PciIdeXPdoQueryDeviceUsageNotification(PdoExtension, Irp); + Status = PciIdeXPnpQueryDeviceUsageNotification(&PdoExtension->Common, Irp); + break; + + case IRP_MN_QUERY_INTERFACE: + Status = PciIdeXPdoQueryInterface(PdoExtension, Irp); break; default: @@ -831,6 +1072,8 @@ PciIdeXPdoDispatchPnp( Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); + IoReleaseRemoveLock(&PdoExtension->Common.RemoveLock, Irp); + return Status; } diff --git a/drivers/storage/ide/pciidex/power.c b/drivers/storage/ide/pciidex/power.c index 63075c27bf7..7979b425e8c 100644 --- a/drivers/storage/ide/pciidex/power.c +++ b/drivers/storage/ide/pciidex/power.c @@ -7,9 +7,6 @@ #include "pciidex.h" -#define NDEBUG -#include - static NTSTATUS PciIdeXPdoDispatchPower( @@ -48,7 +45,7 @@ PciIdeXFdoDispatchPower( { PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); - return PoCallDriver(FdoExtension->Ldo, Irp); + return PoCallDriver(FdoExtension->Common.LowerDeviceObject, Irp); } NTSTATUS @@ -57,10 +54,24 @@ PciIdeXDispatchPower( _In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp) { - PVOID DeviceExtension = DeviceObject->DeviceExtension; + PCOMMON_DEVICE_EXTENSION CommonExt = DeviceObject->DeviceExtension; + NTSTATUS Status; - if (IS_FDO(DeviceExtension)) - return PciIdeXFdoDispatchPower(DeviceExtension, Irp); + Status = IoAcquireRemoveLock(&CommonExt->RemoveLock, Irp); + if (!NT_SUCCESS(Status)) + { + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; + } + + if (IS_FDO(CommonExt)) + Status = PciIdeXFdoDispatchPower((PVOID)CommonExt, Irp); else - return PciIdeXPdoDispatchPower(DeviceExtension, Irp); + Status = PciIdeXPdoDispatchPower((PVOID)CommonExt, Irp); + + IoReleaseRemoveLock(&CommonExt->RemoveLock, Irp); + + return Status; } diff --git a/sdk/include/ddk/ide.h b/sdk/include/ddk/ide.h index 83e416a5f57..893e93793a1 100644 --- a/sdk/include/ddk/ide.h +++ b/sdk/include/ddk/ide.h @@ -294,6 +294,7 @@ PciIdeXSetBusData( #define UDMA_MODE3 (1 << 14) #define UDMA_MODE4 (1 << 15) #define UDMA_MODE5 (1 << 16) +#define UDMA_MODE6 (1 << 17) #ifdef __cplusplus } diff --git a/sdk/include/reactos/drivers/ata/ata_shared.h b/sdk/include/reactos/drivers/ata/ata_shared.h new file mode 100644 index 00000000000..0ab01421093 --- /dev/null +++ b/sdk/include/reactos/drivers/ata/ata_shared.h @@ -0,0 +1,547 @@ +/* + * PROJECT: ReactOS Storage Stack + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * or MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Internal shared ATA driver definitions + * COPYRIGHT: Copyright 2026 Dmitry Borisov (di.sean@protonmail.com) + */ + +#pragma once + +/** @sa PCIIDE_INTERFACE */ +#define PCIIDEX_INTERFACE_VERSION 1 + +/** Maximum number of devices (target ID) per channel */ +#define ATA_MAX_DEVICE 15 + +/** + * @brief 256 sectors of 512 bytes (128 kB). + * + * This ensures that the sector count register will not overflow in LBA-28 and CHS modes. + * In the case of the sector count (0x100) being truncated to 8-bits, 0 still means 256 sectors. + */ +/*@{*/ +#define ATA_MIN_SECTOR_SIZE 512 +#define ATA_MAX_SECTORS_PER_IO 0x100 +#define ATA_MAX_TRANSFER_LENGTH (ATA_MAX_SECTORS_PER_IO * ATA_MIN_SECTOR_SIZE) // 0x20000 +/*@}*/ + +/** Minimum DMA buffer alignment because of the reserved PRDT byte 0 */ +#define ATA_MIN_BUFFER_ALIGNMENT FILE_WORD_ALIGNMENT + +#include + +/** IDE channel timing information block */ +typedef struct _IDE_ACPI_TIMING_MODE_BLOCK +{ + struct + { + ULONG PioSpeed; ///< PIO cycle timing in ns + ULONG DmaSpeed; ///< DMA cycle timing in ns +/** The mode is not supported */ +#define IDE_ACPI_TIMING_MODE_NOT_SUPPORTED 0xFFFFFFFF // aka -1 ns + } Drive[MAX_IDE_DEVICE]; + + ULONG ModeFlags; +/** Use the UDMA mode on drive 0/1 */ +#define IDE_ACPI_TIMING_MODE_FLAG_UDMA(Drive) (0x01 << (2 * (Drive))) + +/** Enable the IORDY signal on drive 0/1 */ +#define IDE_ACPI_TIMING_MODE_FLAG_IORDY(Drive) (0x02 << (2 * (Drive))) + +/** Independent timing available */ +#define IDE_ACPI_TIMING_MODE_FLAG_INDEPENDENT_TIMINGS 0x10 +} IDE_ACPI_TIMING_MODE_BLOCK, *PIDE_ACPI_TIMING_MODE_BLOCK; + +/** _GTF data buffer */ +typedef struct _ATA_ACPI_TASK_FILE +{ + UCHAR Feature; + UCHAR SectorCount; + UCHAR LowLba; + UCHAR MidLba; + UCHAR HighLba; + UCHAR DriveSelect; + UCHAR Command; +} ATA_ACPI_TASK_FILE, *PATA_ACPI_TASK_FILE; + +#include + +typedef struct _ATA_DEVICE_REQUEST ATA_DEVICE_REQUEST, *PATA_DEVICE_REQUEST; + +typedef enum _PORT_NOTIFICATION_TYPE +{ + AtaRequestComplete = 0, + AtaResetDetected, + AtaBusChangeDetected, + AtaRequestFailed, + AtaAsyncNotificationDetected, +} PORT_NOTIFICATION_TYPE, *PPORT_NOTIFICATION_TYPE; + +typedef enum _ATA_CONNECTION_STATUS +{ + CONN_STATUS_FAILURE, + CONN_STATUS_NO_DEVICE, + CONN_STATUS_DEV_UNKNOWN, + CONN_STATUS_DEV_ATA, + CONN_STATUS_DEV_ATAPI, +} ATA_CONNECTION_STATUS; + +typedef struct _IDE_REGISTERS +{ + PUCHAR Data; + union + { + PUCHAR Features; + PUCHAR Error; + }; + union + { + PUCHAR SectorCount; + PUCHAR InterruptReason; + }; + PUCHAR LbaLow; ///< LBA bits 0-7, 24-31 + union + { + PUCHAR LbaMid; ///< LBA bits 8-15, 32-39 + PUCHAR ByteCountLow; + PUCHAR SignatureLow; + }; + union + { + PUCHAR LbaHigh; ///< LBA bits 16-23, 40-47 + PUCHAR ByteCountHigh; + PUCHAR SignatureHigh; + }; + PUCHAR Device; + union + { + PUCHAR Command; + PUCHAR Status; + }; + union + { + PUCHAR Control; + PUCHAR AlternateStatus; + }; + PUCHAR Dma; + PUCHAR Scr; +} IDE_REGISTERS, *PIDE_REGISTERS; + +typedef struct _CHANNEL_DEVICE_CONFIG +{ + ULONG SupportedModes; + ULONG PioMode; + ULONG DmaMode; + ULONG MinPioCycleTime; + ULONG MinMwDmaCycleTime; + ULONG MinSwDmaCycleTime; + ULONG CurrentModes; + BOOLEAN IsFixedDisk; + BOOLEAN IoReadySupported; + BOOLEAN IsNewDevice; + PCSTR FriendlyName; + PIDENTIFY_DEVICE_DATA IdentifyDeviceData; +} CHANNEL_DEVICE_CONFIG, *PCHANNEL_DEVICE_CONFIG; + +typedef enum +{ + COMPLETE_IRP = 0, + COMPLETE_NO_IRP, + COMPLETE_START_AGAIN, +} ATA_COMPLETION_ACTION; + +typedef ATA_COMPLETION_ACTION +(REQUEST_COMPLETION_ROUTINE)( + _In_ PATA_DEVICE_REQUEST Request); +typedef REQUEST_COMPLETION_ROUTINE *PREQUEST_COMPLETION_ROUTINE; + +typedef struct _ATA_IO_CONTEXT_COMMON +{ + ULONG SectorSize; + + ULONG TransportFlags; +#define DEVICE_NUMBER_MASK 0x0000000F +#define DEVICE_IS_ATAPI 0x00000010 +#define DEVICE_HAS_CDB_INTERRUPT 0x00000020 +#define DEVICE_NEED_DMA_DIRECTION 0x00000040 +#define DEVICE_IS_NEC_CDR260 0x00000080 +#define DEVICE_QUEUE_DEPTH_MASK 0x0000FF00 + +#define DEVICE_QUEUE_DEPTH_SHIFT 8 + + UCHAR MultiSectorCount; + UCHAR CdbSize; + UCHAR DeviceSelect; +} ATA_IO_CONTEXT_COMMON, *PATA_IO_CONTEXT_COMMON; + +/** ATA Task File interface */ +typedef struct _ATA_TASKFILE +{ + union + { + UCHAR Command; + UCHAR Status; + }; + union + { + UCHAR Feature; + UCHAR Error; + }; + UCHAR LowLba; ///< LBA bits 0-7 + UCHAR MidLba; ///< LBA bits 8-15 + UCHAR HighLba; ///< LBA bits 16-23 + UCHAR DriveSelect; + UCHAR LowLbaEx; ///< LBA bits 24-31 + UCHAR MidLbaEx; ///< LBA bits 32-39 + UCHAR HighLbaEx; ///< LBA bits 40-47 + UCHAR FeatureEx; + UCHAR SectorCount; + UCHAR SectorCountEx; + UCHAR Icc; ///< Isochronous Command Completion + ULONG Auxiliary; +} ATA_TASKFILE, *PATA_TASKFILE; + +/** ATA device request context */ +typedef struct _ATA_DEVICE_REQUEST +{ + union + { + /** Completion queue entry */ + SLIST_ENTRY CompletionEntry; + + /** Port queue entry */ + LIST_ENTRY PortEntry; + }; + union + { + UCHAR Cdb[16]; + ATA_TASKFILE TaskFile; + }; + PVOID DataBuffer; + PSCATTER_GATHER_LIST SgList; + PATA_IO_CONTEXT_COMMON Device; + ULONG DataTransferLength; + ULONG Flags; + PMDL Mdl; + PIRP Irp; + PSCSI_REQUEST_BLOCK Srb; + UCHAR SrbStatus; + PREQUEST_COMPLETION_ROUTINE Complete; + ULONG Tag; + ULONG Slot; + ULONG TimeOut; + union + { + struct + { + UCHAR InternalState; + UCHAR TranslationState; + UCHAR Reserved1; + UCHAR Reserved2; + }; + ULONG State; + }; + + /** + * @brief ATA normal/error outputs. + * + * The contents of registers are only valid + * if flags has the REQUEST_FLAG_HAS_TASK_FILE bit set. + * + * The driver always updates it on ATA errors as we have to set the ATA LBA field. + * For ATAPI errors only the Status and Error fields are updated, as an optimization. + */ + ATA_TASKFILE Output; + +#if DBG + ULONG Signature; +#define ATA_DEVICE_REQUEST_SIGNATURE 'rATA' +#endif +} ATA_DEVICE_REQUEST, *PATA_DEVICE_REQUEST; + +/** Initial state */ +#define REQUEST_STATE_NONE 0 + +/** SRB not translated into the device request or translaton failed */ +#define REQUEST_STATE_NOT_STARTED 1 + +/** Requeue the device request */ +#define REQUEST_STATE_REQUEUE 2 + +/** Freeze the device queue upon completion of IRP */ +#define REQUEST_STATE_FREEZE_QUEUE 3 + +/** ATA protocols */ +/*@{*/ +/** DMA ATA command */ +#define REQUEST_FLAG_DMA 0x00000001 + +/** ATAPI PACKET command */ +#define REQUEST_FLAG_PACKET_COMMAND 0x00000002 + +/** DMA Queued command */ +#define REQUEST_FLAG_NCQ 0x00000004 + +/** Data-In command */ +#define REQUEST_FLAG_DATA_IN 0x00000040 + +/** Data-Out command */ +#define REQUEST_FLAG_DATA_OUT 0x00000080 + +/** Software Reset command (AHCI only) */ +#define REQUEST_FLAG_RST_COMMAND 0x00040000 +/*@}*/ + +/** 48-bit command */ +#define REQUEST_FLAG_LBA48 0x00000008 + +/** Multiple mode command */ +#define REQUEST_FLAG_READ_WRITE_MULTIPLE 0x00000010 + +/** ATA read/write command */ +#define REQUEST_FLAG_READ_WRITE 0x00000020 + +/** Forced unit access command */ +#define REQUEST_FLAG_FUA 0x00000100 + +/** Return the contents of task file registers to the caller */ +#define REQUEST_FLAG_SAVE_TASK_FILE 0x00000200 + +/** Holds the saved contents of task file registers */ +#define REQUEST_FLAG_HAS_TASK_FILE 0x00000400 + +/** Has extra bits in the device register */ +#define REQUEST_FLAG_SET_DEVICE_REGISTER 0x00000800 + +/** Has extra bits in the Auxiliary field */ +#define REQUEST_FLAG_SET_AUXILIARY_FIELD 0x00001000 + +/** Has extra bits in the Isochronous Command Completion field */ +#define REQUEST_FLAG_SET_ICC_FIELD 0x00002000 + +/** Exclusive port access required */ +#define REQUEST_FLAG_EXCLUSIVE 0x00004000 + +/** The request owns the local buffer */ +#define REQUEST_FLAG_HAS_LOCAL_BUFFER 0x00008000 + +/** The request owns the S/G list and will also release it */ +#define REQUEST_FLAG_HAS_SG_LIST 0x00010000 + +/** The request owns the MDL and will also release it */ +#define REQUEST_FLAG_HAS_MDL 0x00020000 + +/** The request owns the reserved memory mapping and will also release it */ +#define REQUEST_FLAG_HAS_RESERVED_MAPPING 0x00080000 + +/** Copy of SRB_FLAGS_NO_KEEP_AWAKE */ +#define REQUEST_FLAG_NO_KEEP_AWAKE 0x00100000 + +/** Use the DMA engine for the transfer */ +#define REQUEST_FLAG_PROGRAM_DMA 0x00200000 + +/** Internal command */ +#define REQUEST_FLAG_INTERNAL 0x00400000 + +/** ATA or SCSI pass-through command */ +#define REQUEST_FLAG_PASSTHROUGH 0x00800000 + +#define REQUEST_FLAG_DEVICE_EXCLUSIVE_ACCESS 0x01000000 + +/** Polled command */ +#define REQUEST_FLAG_POLL 0x80000000 + +/** + * Exclusive port access required. + * + * 1) Since there is only one Received FIS structure, it should be necessary to + * synchronize RFIS reads amongst multiple I/O requests. + * + * 2) We also mark all requests that modify the device extension state + * (IDE_COMMAND_IDENTIFY, IDE_COMMAND_ATAPI_IDENTIFY, + * IDE_COMMAND_SET_FEATURE, and others) as REQUEST_FLAG_EXCLUSIVE. + */ +#define REQUEST_EXCLUSIVE_ACCESS_FLAGS \ + (REQUEST_FLAG_SAVE_TASK_FILE | REQUEST_FLAG_EXCLUSIVE) + +/** DMA command translation */ +#define REQUEST_DMA_FLAGS \ + (REQUEST_FLAG_DMA | REQUEST_FLAG_PROGRAM_DMA) + +typedef VOID +(__cdecl PORT_NOTIFICATION)( + _In_ PORT_NOTIFICATION_TYPE NotificationType, + _In_ PVOID PortContext, + ...); +typedef PORT_NOTIFICATION *PPORT_NOTIFICATION; + +typedef NTSTATUS +(CONTROLLER_ATTACH_CHANNEL)( + _In_ PVOID ChannelContext, + _In_ BOOLEAN Attach); +typedef CONTROLLER_ATTACH_CHANNEL *PCONTROLLER_ATTACH_CHANNEL; + +typedef VOID +(CHANNEL_SET_DEVICE_DATA)( + _In_ PVOID ChannelContext, + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIDENTIFY_DEVICE_DATA IdentifyDeviceData); +typedef CHANNEL_SET_DEVICE_DATA *PCHANNEL_SET_DEVICE_DATA; + +typedef PVOID +(CHANNEL_GET_INIT_TASK_FILE)( + _In_ PVOID ChannelContext, + _In_ PDEVICE_OBJECT DeviceObject); +typedef CHANNEL_GET_INIT_TASK_FILE *PCHANNEL_GET_INIT_TASK_FILE; + +typedef BOOLEAN +(CHANNEL_DOWNGRADE_INTERFACE_SPEED)( + _In_ PVOID ChannelContext); +typedef CHANNEL_DOWNGRADE_INTERFACE_SPEED *PCHANNEL_DOWNGRADE_INTERFACE_SPEED; + +typedef VOID +(CHANNEL_ABORT_CHANNEL)( + _In_ PVOID ChannelContext, + _In_ BOOLEAN DisableInterrupts); +typedef CHANNEL_ABORT_CHANNEL *PCHANNEL_ABORT_CHANNEL; + +typedef VOID +(CHANNEL_RESET_CHANNEL)( + _In_ PVOID ChannelContext); +typedef CHANNEL_RESET_CHANNEL *PCHANNEL_RESET_CHANNEL; + +typedef ULONG +(CHANNEL_ENUMERATE_CHANNEL)( + _In_ PVOID ChannelContext); +typedef CHANNEL_ENUMERATE_CHANNEL *PCHANNEL_ENUMERATE_CHANNEL; + +typedef ATA_CONNECTION_STATUS +(CHANNEL_IDENTIFY_DEVICE)( + _In_ PVOID ChannelContext, + _In_ ULONG DeviceNumber); +typedef CHANNEL_IDENTIFY_DEVICE *PCHANNEL_IDENTIFY_DEVICE; + +_IRQL_requires_(DISPATCH_LEVEL) +typedef VOID +(CHANNEL_SET_MODE)( + _In_ PVOID ChannelContext, + _In_reads_(ATA_MAX_DEVICE) PCHANNEL_DEVICE_CONFIG* DeviceList); +typedef CHANNEL_SET_MODE *PCHANNEL_SET_MODE; + +_IRQL_requires_(DISPATCH_LEVEL) +typedef BOOLEAN +(CHANNEL_ALLOCATE_SLOT)( + _In_ PVOID ChannelContext, + _In_ PATA_DEVICE_REQUEST Request, + _In_ BOOLEAN Allocate); +typedef CHANNEL_ALLOCATE_SLOT *PCHANNEL_ALLOCATE_SLOT; + +_IRQL_requires_(DISPATCH_LEVEL) +typedef VOID +(CHANNEL_PREPARE_PRD_TABLE)( + _In_ PVOID ChannelContext, + _In_ PATA_DEVICE_REQUEST Request, + _In_ SCATTER_GATHER_LIST* __restrict SgList); +typedef CHANNEL_PREPARE_PRD_TABLE *PCHANNEL_PREPARE_PRD_TABLE; + +_IRQL_requires_(DISPATCH_LEVEL) +typedef VOID +(CHANNEL_PREPARE_IO)( + _In_ PVOID ChannelContext, + _In_ PATA_DEVICE_REQUEST Request); +typedef CHANNEL_PREPARE_IO *PCHANNEL_PREPARE_IO; + +_IRQL_requires_(HIGH_LEVEL) +typedef BOOLEAN +(CHANNEL_START_IO)( + _In_ PVOID ChannelContext, + _In_ PATA_DEVICE_REQUEST Request); +typedef CHANNEL_START_IO *PCHANNEL_START_IO; + +typedef NTSTATUS +(CONTROLLER_PNP_ADD_DEVICE)( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PDEVICE_OBJECT PhysicalDeviceObject, + _Out_ PVOID *ControllerContext); +typedef CONTROLLER_PNP_ADD_DEVICE *PCONTROLLER_PNP_ADD_DEVICE; + +typedef NTSTATUS +(CONTROLLER_PNP_START_DEVICE)( + _In_ PVOID ControllerContext, + _In_ PCM_RESOURCE_LIST ResourcesTranslated); +typedef CONTROLLER_PNP_START_DEVICE *PCONTROLLER_PNP_START_DEVICE; + +typedef VOID +(CONTROLLER_PNP_REMOVE_DEVICE)( + _In_ PVOID ControllerContext); +typedef CONTROLLER_PNP_REMOVE_DEVICE *PCONTROLLER_PNP_REMOVE_DEVICE; + +/* ReactOS-specific legacy detection magic */ +#define PCIIDEX_GET_CONTROLLER_INTERFACE_SIGNATURE (0xFFFFFFFF - 0x1000) + +/** + * @brief Legacy detection interface with the PCIIDEX driver. + * + * @note This interface is ROS-specific. + */ +typedef struct _PCIIDEX_LEGACY_CONTROLLER_INTERFACE +{ + ULONG Version; + PCONTROLLER_PNP_ADD_DEVICE AddDevice; + PCONTROLLER_PNP_START_DEVICE StartDevice; + PCONTROLLER_PNP_REMOVE_DEVICE RemoveDevice; +} PCIIDEX_LEGACY_CONTROLLER_INTERFACE, *PPCIIDEX_LEGACY_CONTROLLER_INTERFACE; + +/** + * @brief Channel interface with the PCIIDEX driver. + * + * This interface is ROS-specific. + */ +typedef struct _PCIIDEX_CHANNEL_INTERFACE +{ + /* Common interface header */ + USHORT Size; + USHORT Version; + PVOID Context; + PINTERFACE_REFERENCE InterfaceReference; + PINTERFACE_DEREFERENCE InterfaceDereference; + + PVOID ChannelContext; + PCONTROLLER_ATTACH_CHANNEL AttachChannel; + PKINTERRUPT InterruptObject; + ULONG Channel; + ULONG TransferModeSupported; + ULONG MaximumTransferLength; + ULONG MaximumPhysicalPages; + ULONG QueueDepth; + ULONG MaxTargetId; + PCHANNEL_SET_DEVICE_DATA SetDeviceData; + PCHANNEL_GET_INIT_TASK_FILE GetInitTaskFile; + PCHANNEL_DOWNGRADE_INTERFACE_SPEED DowngradeInterfaceSpeed; + PCHANNEL_ABORT_CHANNEL AbortChannel; + PCHANNEL_RESET_CHANNEL ResetChannel; + PCHANNEL_ENUMERATE_CHANNEL EnumerateChannel; + PCHANNEL_IDENTIFY_DEVICE IdentifyDevice; + PCHANNEL_SET_MODE SetTransferMode; + PCHANNEL_ALLOCATE_SLOT AllocateSlot; + PCHANNEL_PREPARE_PRD_TABLE PreparePrdTable; + PCHANNEL_PREPARE_IO PrepareIo; + PCHANNEL_START_IO StartIo; + PCONTROLLER_OBJECT HwSyncObject; + PDMA_ADAPTER DmaAdapter; + PDEVICE_OBJECT ChannelObject; + ULONG Flags; +#define ATA_CHANNEL_FLAG_PIO_VIA_DMA 0x00000001 +#define ATA_CHANNEL_FLAG_IS_EXTERNAL 0x00000002 +#define ATA_CHANNEL_FLAG_NCQ 0x00000004 +#define ATA_CHANNEL_FLAG_IS_AHCI 0x00000008 +#define ATA_CHANNEL_FLAG_PIO_FOR_LBA48_XFER 0x00000010 + + PVOID* PortContext; + PPORT_NOTIFICATION* PortNotification; + PATA_DEVICE_REQUEST** Slots; +} PCIIDEX_CHANNEL_INTERFACE, *PPCIIDEX_CHANNEL_INTERFACE; + +DEFINE_GUID(GUID_PCIIDE_INTERFACE_ROS, + 0xD677FBCF, 0xABED, 0x47C8, 0x80, 0xA3, 0xE4, 0x34, 0x7E, 0xA4, 0x96, 0x47); diff --git a/sdk/include/reactos/drivers/ata/ata_user.h b/sdk/include/reactos/drivers/ata/ata_user.h new file mode 100644 index 00000000000..64a0adaadf9 --- /dev/null +++ b/sdk/include/reactos/drivers/ata/ata_user.h @@ -0,0 +1,52 @@ +/* + * PROJECT: ReactOS Storage Stack + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * or MIT (https://spdx.org/licenses/MIT) + * PURPOSE: ATA driver user mode interface + * COPYRIGHT: Copyright 2026 Dmitry Borisov (di.sean@protonmail.com) + */ + +#pragma once + +#define DD_ATA_REG_ATA_DEVICE_TYPE L"DeviceType" +#define DD_ATA_REG_SCSI_DEVICE_TYPE L"ScsiDeviceType" +#define DD_ATA_REG_MAX_TARGET_ID L"MaxTargetId" +#define DD_ATA_REG_XFER_MODE_ALLOWED L"UserTimingModeAllowed" +#define DD_ATA_REG_XFER_MODE_SUPPORTED L"DeviceTimingModeSupported" +#define DD_ATA_REG_XFER_MODE_SELECTED L"DeviceTimingMode" + +/** PIO modes 0-4 */ +#define PIO_ALL \ + (PIO_MODE0 | PIO_MODE1 | PIO_MODE2 | PIO_MODE3 | PIO_MODE4) + +/** SWDMA modes 0-2 */ +#define SWDMA_ALL \ + (SWDMA_MODE0 | SWDMA_MODE1 | SWDMA_MODE2) + +/** MWDMA modes 0-2 */ +#define MWDMA_ALL \ + (MWDMA_MODE0 | MWDMA_MODE1 | MWDMA_MODE2) + +/** UDMA modes 0-6 */ +#define UDMA_ALL \ + (UDMA_MODE0 | UDMA_MODE1 | UDMA_MODE2 | UDMA_MODE3 | UDMA_MODE4 | UDMA_MODE5 | UDMA_MODE6) + +/** Converts the provided mode number into a mode index in the bit map */ +/*@{*/ +#define PIO_MODE(n) (n) +#define SWDMA_MODE(n) (5 + (n)) +#define MWDMA_MODE(n) (8 + (n)) +#define UDMA_MODE(n) (11 + (n)) +/*@}*/ + +/** + * @brief Private enum between the ATA driver and storprop.dll + * @sa DD_ATA_REG_ATA_DEVICE_TYPE + */ +typedef enum _ATA_DEVICE_TYPE +{ + DEV_UNKNOWN = 0, + DEV_ATA = 1, + DEV_ATAPI = 2, + DEV_NONE = 3 +} ATA_DEVICE_TYPE;