/* * PROJECT: ReactOS ATA Port Driver * LICENSE: MIT (https://spdx.org/licenses/MIT) * PURPOSE: Driver entrypoint and utility functions * COPYRIGHT: Copyright 2026 Dmitry Borisov */ /* INCLUDES *******************************************************************/ #include "atapi.h" /* GLOBALS ********************************************************************/ UNICODE_STRING AtapDriverRegistryPath; BOOLEAN AtapInPEMode; ATAPORT_PAGED_DATA static const struct { PCSTR DeviceType; PCSTR GenericType; PCSTR PeripheralId; } AtapGenericDeviceNames[] = { {"Disk", "GenDisk", "DiskPeripheral" }, // DIRECT_ACCESS_DEVICE {"Sequential", "GenSequential", "TapePeripheral" }, // SEQUENTIAL_ACCESS_DEVICE {"Printer", "GenPrinter", "PrinterPeripheral" }, // PRINTER_DEVICE {"Processor", "GenProcessor", "ProcessorPeripheral" }, // PROCESSOR_DEVICE {"Worm", "GenWorm", "WormPeripheral" }, // WRITE_ONCE_READ_MULTIPLE_DEVICE {"CdRom", "GenCdRom", "CdRomPeripheral" }, // READ_ONLY_DIRECT_ACCESS_DEVICE {"Scanner", "GenScanner", "ScannerPeripheral" }, // SCANNER_DEVICE {"Optical", "GenOptical", "OpticalDiskPeripheral" }, // OPTICAL_DEVICE {"Changer", "GenChanger", "MediumChangerPeripheral" }, // MEDIUM_CHANGER {"Net", "GenNet", "CommunicationsPeripheral"}, // COMMUNICATION_DEVICE {"Other", "IdeOther", "OtherPeripheral" }, // 10 }; /* FUNCTIONS ******************************************************************/ CODE_SEG("PAGE") PCSTR AtaTypeCodeToName( _In_ PATAPORT_DEVICE_EXTENSION DevExt, _In_ DEVICE_TYPE_NAME Type) { ULONG DeviceType; PAGED_CODE(); DeviceType = DevExt->InquiryData.DeviceType; DeviceType = min(DeviceType, RTL_NUMBER_OF(AtapGenericDeviceNames) - 1); switch (Type) { case GetDeviceType: return AtapGenericDeviceNames[DeviceType].DeviceType; case GetPeripheralId: return AtapGenericDeviceNames[DeviceType].PeripheralId; case GetGenericType: if (DevExt->Device.DeviceFlags & DEVICE_IS_SUPER_FLOPPY) return "GenSFloppy"; // Install the Super Floppy storage class driver else return AtapGenericDeviceNames[DeviceType].GenericType; default: ASSERT(FALSE); UNREACHABLE; } } CODE_SEG("PAGE") NTSTATUS AtaOpenRegistryKey( _Out_ PHANDLE KeyHandle, _In_ HANDLE RootKey, _In_ PUNICODE_STRING KeyName, _In_ BOOLEAN Create) { NTSTATUS Status; ULONG Disposition; OBJECT_ATTRIBUTES ObjectAttributes; PAGED_CODE(); InitializeObjectAttributes(&ObjectAttributes, KeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, RootKey, NULL); if (Create) { Status = ZwCreateKey(KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, REG_OPTION_VOLATILE, &Disposition); } else { Status = ZwOpenKey(KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes); } return Status; } CODE_SEG("PAGE") VOID AtaGetRegistryKey( _In_ PATAPORT_CHANNEL_EXTENSION ChanExt, _In_ UCHAR TargetId, _In_ PCWSTR KeyName, _Out_ PULONG KeyValue, _In_ ULONG DefaultValue) { UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(*KeyValue)]; HANDLE HwKeyHandle, TargetKeyHandle; UNICODE_STRING ValueName, TargetKeyName; NTSTATUS Status; WCHAR TargetKeyBuffer[sizeof("Target99")]; ULONG ResultLength; PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)Buffer; PAGED_CODE(); Status = IoOpenDeviceRegistryKey(ChanExt->Pdo, PLUGPLAY_REGKEY_DEVICE, KEY_ALL_ACCESS, &HwKeyHandle); if (!NT_SUCCESS(Status)) { TRACE("Failed to open device hardware key, status 0x%lx\n", Status); return; } /* Open or create the 'TargetX' key */ Status = RtlStringCbPrintfW(TargetKeyBuffer, sizeof(TargetKeyBuffer), L"Target%u", TargetId); ASSERT(NT_SUCCESS(Status)); RtlInitUnicodeString(&TargetKeyName, TargetKeyBuffer); Status = AtaOpenRegistryKey(&TargetKeyHandle, HwKeyHandle, &TargetKeyName, TRUE); if (!NT_SUCCESS(Status)) { TRACE("Failed to create '%wZ' key, status 0x%lx\n", &TargetKeyName, Status); goto Cleanup; } RtlInitUnicodeString(&ValueName, KeyName); Status = ZwQueryValueKey(TargetKeyHandle, &ValueName, KeyValuePartialInformation, PartialInfo, sizeof(Buffer), &ResultLength); ZwClose(TargetKeyHandle); if (!NT_SUCCESS(Status) || (PartialInfo->Type != REG_DWORD) || (PartialInfo->DataLength != sizeof(*KeyValue))) { TRACE("Failed to read '%wZ' key, status 0x%lx\n", &ValueName, Status); *KeyValue = DefaultValue; goto Cleanup; } *KeyValue = *(PULONG)&PartialInfo->Data; Cleanup: ZwClose(HwKeyHandle); } CODE_SEG("PAGE") VOID AtaSetRegistryKey( _In_ PATAPORT_CHANNEL_EXTENSION ChanExt, _In_ UCHAR TargetId, _In_ PCWSTR KeyName, _In_ ULONG KeyValue) { HANDLE HwKeyHandle, TargetKeyHandle; UNICODE_STRING ValueName, TargetKeyName; NTSTATUS Status; WCHAR TargetKeyBuffer[sizeof("Target99")]; PAGED_CODE(); Status = IoOpenDeviceRegistryKey(ChanExt->Pdo, PLUGPLAY_REGKEY_DEVICE, KEY_ALL_ACCESS, &HwKeyHandle); if (!NT_SUCCESS(Status)) { TRACE("Failed to open device hardware key, status 0x%lx\n", Status); return; } /* Open or create the 'TargetX' key */ Status = RtlStringCbPrintfW(TargetKeyBuffer, sizeof(TargetKeyBuffer), L"Target%u", TargetId); ASSERT(NT_SUCCESS(Status)); RtlInitUnicodeString(&TargetKeyName, TargetKeyBuffer); Status = AtaOpenRegistryKey(&TargetKeyHandle, HwKeyHandle, &TargetKeyName, TRUE); if (!NT_SUCCESS(Status)) { TRACE("Failed to create '%wZ' key, status 0x%lx\n", &TargetKeyName, Status); goto Cleanup; } RtlInitUnicodeString(&ValueName, KeyName); Status = ZwSetValueKey(TargetKeyHandle, &ValueName, 0, REG_DWORD, &KeyValue, sizeof(KeyValue)); ZwClose(TargetKeyHandle); if (!NT_SUCCESS(Status)) { TRACE("Failed to set '%wZ' key, status 0x%lx\n", &ValueName, Status); } Cleanup: ZwClose(HwKeyHandle); } CODE_SEG("PAGE") VOID AtaSetPortRegistryKey( _In_ PATAPORT_CHANNEL_EXTENSION ChanExt, _In_ PCWSTR KeyName, _In_ ULONG KeyValue) { HANDLE HwKeyHandle; UNICODE_STRING ValueName; NTSTATUS Status; PAGED_CODE(); Status = IoOpenDeviceRegistryKey(ChanExt->Pdo, PLUGPLAY_REGKEY_DEVICE, KEY_ALL_ACCESS, &HwKeyHandle); if (!NT_SUCCESS(Status)) { TRACE("Failed to open device hardware key, status 0x%lx\n", Status); return; } RtlInitUnicodeString(&ValueName, KeyName); Status = ZwSetValueKey(HwKeyHandle, &ValueName, 0, REG_DWORD, &KeyValue, sizeof(KeyValue)); ZwClose(HwKeyHandle); if (!NT_SUCCESS(Status)) { TRACE("Failed to set '%wZ' key, status 0x%lx\n", &ValueName, Status); } } CODE_SEG("PAGE") NTSTATUS NTAPI AtaDispatchCreateClose( _In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp) { PAGED_CODE(); Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } CODE_SEG("PAGE") VOID NTAPI AtaUnload( _In_ PDRIVER_OBJECT DriverObject) { PAGED_CODE(); RtlFreeUnicodeString(&AtapDriverRegistryPath); } VOID NTAPI AtaStorageNotificationWorker( _In_ PDEVICE_OBJECT DeviceObject, _In_opt_ PVOID Context) { PIO_WORKITEM WorkItem = Context; PDEVICE_OBJECT TopDeviceObject; PIRP Irp; KEVENT Event; IO_STATUS_BLOCK IoStatus; STORAGE_EVENT_NOTIFICATION EventObject; NTSTATUS Status; TopDeviceObject = IoGetAttachedDeviceReference(DeviceObject); KeInitializeEvent(&Event, NotificationEvent, FALSE); EventObject.Version = STORAGE_EVENT_NOTIFICATION_VERSION_V1; EventObject.Size = sizeof(EventObject); EventObject.Events = STORAGE_EVENT_ALL; Irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_EVENT_NOTIFICATION, TopDeviceObject, &EventObject, sizeof(EventObject), NULL, 0, FALSE, &Event, &IoStatus); if (!Irp) { ERR("IoBuildDeviceIoControlRequest() failed\n"); goto Exit; } Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL); Status = IoStatus.Status; } TRACE("Notification result %08lx\n", Status); Exit: ObDereferenceObject(TopDeviceObject); IoFreeWorkItem(WorkItem); } VOID NTAPI AtaStorageNotificationlDpc( _In_ PKDPC Dpc, _In_opt_ PVOID DeferredContext, _In_opt_ PVOID SystemArgument1, _In_opt_ PVOID SystemArgument2) { PATAPORT_PORT_DATA PortData = DeferredContext; ULONG DeviceBitmap = PtrToUlong(SystemArgument1); PATAPORT_CHANNEL_EXTENSION ChanExt; ULONG i; UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(SystemArgument2); ChanExt = CONTAINING_RECORD(PortData, ATAPORT_CHANNEL_EXTENSION, PortData); for (i = 0; i < ATA_MAX_DEVICE; ++i) { PATAPORT_DEVICE_EXTENSION DevExt; PIO_WORKITEM WorkItem; if (!(DeviceBitmap & (1 << i))) continue; DevExt = AtaFdoFindDeviceByPath(ChanExt, AtaMarshallScsiAddress(PortData->PortNumber, i, 0), AtaStorageNotificationlDpc); if (!DevExt) continue; WorkItem = IoAllocateWorkItem(DevExt->Common.Self); if (!WorkItem) { ERR("Failed to allocate workitem"); } else { IoQueueWorkItem(WorkItem, AtaStorageNotificationWorker, CriticalWorkQueue, WorkItem); } IoReleaseRemoveLock(&DevExt->Common.RemoveLock, AtaStorageNotificationlDpc); } } CODE_SEG("PAGE") NTSTATUS AtaPnpQueryInterface( _In_ PATAPORT_COMMON_EXTENSION CommonExt, _In_ const GUID* Guid, _Out_ PVOID Interface, _In_ ULONG Version, _In_ ULONG Size) { KEVENT Event; PIRP Irp; IO_STATUS_BLOCK IoStatusBlock; PIO_STACK_LOCATION IoStack; NTSTATUS Status; PAGED_CODE(); KeInitializeEvent(&Event, NotificationEvent, FALSE); Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, CommonExt->LowerDeviceObject, NULL, 0, NULL, &Event, &IoStatusBlock); if (!Irp) return STATUS_INSUFFICIENT_RESOURCES; Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; IoStack = IoGetNextIrpStackLocation(Irp); IoStack->MinorFunction = IRP_MN_QUERY_INTERFACE; IoStack->Parameters.QueryInterface.InterfaceType = Guid; IoStack->Parameters.QueryInterface.Size = Size; IoStack->Parameters.QueryInterface.Version = Version; IoStack->Parameters.QueryInterface.Interface = Interface; IoStack->Parameters.QueryInterface.InterfaceSpecificData = NULL; Status = IoCallDriver(CommonExt->LowerDeviceObject, Irp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } return Status; } CODE_SEG("PAGE") NTSTATUS AtaPnpRepeatRequest( _In_ PATAPORT_COMMON_EXTENSION CommonExt, _In_ PIRP Irp, _In_opt_ PDEVICE_CAPABILITIES DeviceCapabilities) { PATAPORT_COMMON_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, AtaPdoCompletionRoutine, &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 AtaPnpQueryDeviceUsageNotification( _In_ PATAPORT_COMMON_EXTENSION CommonExt, _In_ PIRP Irp) { PIO_STACK_LOCATION IoStack; NTSTATUS Status; volatile LONG* Counter; PAGED_CODE(); if (!IS_FDO(CommonExt)) { Status = AtaPnpRepeatRequest(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 AtaPnpQueryPnpDeviceState( _In_ PATAPORT_COMMON_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; } CODE_SEG("PAGE") VOID AtaPnpInitializeCommonExtension( _In_ PATAPORT_COMMON_EXTENSION CommonExt, _In_ PDEVICE_OBJECT SelfDeviceObject, _In_ ULONG Flags) { PAGED_CODE(); CommonExt->Flags = Flags; CommonExt->Self = SelfDeviceObject; CommonExt->DevicePowerState = PowerDeviceD0; CommonExt->SystemPowerState = PowerSystemWorking; IoInitializeRemoveLock(&CommonExt->RemoveLock, ATAPORT_TAG, 0, 0); } CODE_SEG("PAGE") NTSTATUS NTAPI AtaAddChannel( _In_ PDRIVER_OBJECT DriverObject, _In_ PDEVICE_OBJECT PhysicalDeviceObject) { NTSTATUS Status; PDEVICE_OBJECT Fdo; UNICODE_STRING DeviceName; PATAPORT_CHANNEL_EXTENSION ChanExt; WCHAR DeviceNameBuffer[sizeof("\\Device\\Ide\\IdePort99999")]; DECLARE_PAGED_WSTRING(FdoFormat, L"\\Device\\Ide\\IdePort%lu"); static ULONG AtapFdoNumber = 0; PAGED_CODE(); Status = RtlStringCbPrintfW(DeviceNameBuffer, sizeof(DeviceNameBuffer), FdoFormat, AtapFdoNumber); ASSERT(NT_SUCCESS(Status)); RtlInitUnicodeString(&DeviceName, DeviceNameBuffer); INFO("%s(%p, %p) '%wZ'\n", __FUNCTION__, DriverObject, PhysicalDeviceObject, &DeviceName); Status = IoCreateDevice(DriverObject, sizeof(*ChanExt), &DeviceName, FILE_DEVICE_CONTROLLER, FILE_DEVICE_SECURE_OPEN, FALSE, &Fdo); if (!NT_SUCCESS(Status)) { ERR("Failed to create the FDO with status 0x%lx\n", Status); return Status; } ChanExt = Fdo->DeviceExtension; RtlZeroMemory(ChanExt, sizeof(*ChanExt)); AtaPnpInitializeCommonExtension(&ChanExt->Common, Fdo, DO_IS_FDO); ChanExt->Common.LowerDeviceObject = IoAttachDeviceToDeviceStack(Fdo, PhysicalDeviceObject); if (!ChanExt->Common.LowerDeviceObject) { ERR("Failed to attach the FDO\n"); Status = STATUS_DEVICE_REMOVED; goto Failure; } ChanExt->DeviceObjectNumber = AtapFdoNumber++; ChanExt->Pdo = PhysicalDeviceObject; /* DMA buffers alignment */ Fdo->AlignmentRequirement = ChanExt->Common.LowerDeviceObject->AlignmentRequirement; Fdo->AlignmentRequirement = max(Fdo->AlignmentRequirement, ATA_MIN_BUFFER_ALIGNMENT); KeInitializeSpinLock(&ChanExt->PdoListLock); Fdo->Flags &= ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; Failure: if (ChanExt->Common.LowerDeviceObject) IoDetachDevice(ChanExt->Common.LowerDeviceObject); IoDeleteDevice(Fdo); return Status; } static CODE_SEG("INIT") BOOLEAN AtaInPEMode(VOID) { OBJECT_ATTRIBUTES ObjectAttributes; HANDLE KeyHandle; NTSTATUS Status; DECLARE_PAGED_UNICODE_STRING( KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\MiniNT"); PAGED_CODE(); InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(Status)) { ZwClose(KeyHandle); return TRUE; } return FALSE; } static CODE_SEG("INIT") VOID AtaCreateIdeDirectory(VOID) { HANDLE Handle; NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; DECLARE_PAGED_UNICODE_STRING(DirectoryName, L"\\Device\\Ide"); PAGED_CODE(); InitializeObjectAttributes(&ObjectAttributes, &DirectoryName, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT | OBJ_KERNEL_HANDLE, NULL, NULL); Status = ZwCreateDirectoryObject(&Handle, DIRECTORY_ALL_ACCESS, &ObjectAttributes); if (NT_SUCCESS(Status)) { /* We don't need a handle for a permanent object */ ZwClose(Handle); } /* * Ignore directory creation failures (don't report them as a driver initialization error) * as the directory may have already been created by another driver. * We will handle fatal errors later via IoCreateDevice() call. */ } CODE_SEG("INIT") NTSTATUS NTAPI DriverEntry( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) { INFO("ATAPI driver entry\n"); // FIXME: No crashdump/hibernation support if (!DriverObject) return STATUS_NOT_IMPLEMENTED; /* Make a copy of the registry path */ AtapDriverRegistryPath.MaximumLength = RegistryPath->Length + sizeof(UNICODE_NULL); AtapDriverRegistryPath.Buffer = ExAllocatePoolUninitialized(NonPagedPool, AtapDriverRegistryPath.MaximumLength, ATAPORT_TAG); if (!AtapDriverRegistryPath.Buffer) { return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyUnicodeString(&AtapDriverRegistryPath, RegistryPath); AtapDriverRegistryPath.Buffer[RegistryPath->Length / sizeof(WCHAR)] = UNICODE_NULL; DriverObject->MajorFunction[IRP_MJ_CREATE] = AtaDispatchCreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = AtaDispatchCreateClose; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = AtaDispatchDeviceControl; DriverObject->MajorFunction[IRP_MJ_SCSI] = AtaDispatchScsi; DriverObject->MajorFunction[IRP_MJ_POWER] = AtaDispatchPower; DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = AtaDispatchWmi; DriverObject->MajorFunction[IRP_MJ_PNP] = AtaDispatchPnp; DriverObject->DriverExtension->AddDevice = AtaAddChannel; DriverObject->DriverUnload = AtaUnload; KeInitializeDpc(&AtapCompletionDpc, AtaReqCompletionDpc, NULL); InitializeSListHead(&AtapCompletionQueueList); /* Create a directory to hold the driver's device objects */ AtaCreateIdeDirectory(); AtapInPEMode = AtaInPEMode(); return STATUS_SUCCESS; }