xref: /reactos/drivers/storage/ide/pciidex/fdo.c (revision c0c57e23)
1 /*
2  * PROJECT:     PCI IDE bus driver extension
3  * LICENSE:     See COPYING in the top level directory
4  * PURPOSE:     IRP_MJ_PNP operations for FDOs
5  * COPYRIGHT:   Copyright 2005 Hervé Poussineau <hpoussin@reactos.org>
6  *              Copyright 2023 Dmitry Borisov <di.sean@protonmail.com>
7  */
8 
9 #include "pciidex.h"
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 static
15 CODE_SEG("PAGE")
16 NTSTATUS
PciIdeXFdoParseResources(_In_ PFDO_DEVICE_EXTENSION FdoExtension,_In_ PCM_RESOURCE_LIST ResourcesTranslated)17 PciIdeXFdoParseResources(
18     _In_ PFDO_DEVICE_EXTENSION FdoExtension,
19     _In_ PCM_RESOURCE_LIST ResourcesTranslated)
20 {
21     PCM_PARTIAL_RESOURCE_DESCRIPTOR BusMasterDescriptor = NULL;
22     PVOID IoBase;
23     ULONG i;
24 
25     PAGED_CODE();
26 
27     if (!ResourcesTranslated)
28         return STATUS_INVALID_PARAMETER;
29 
30     for (i = 0; i < ResourcesTranslated->List[0].PartialResourceList.Count; ++i)
31     {
32         PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
33 
34         Descriptor = &ResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[i];
35         switch (Descriptor->Type)
36         {
37             case CmResourceTypePort:
38             case CmResourceTypeMemory:
39             {
40                 switch (Descriptor->u.Port.Length)
41                 {
42                     /* Bus master port base */
43                     case 16:
44                     {
45                         if (!BusMasterDescriptor)
46                             BusMasterDescriptor = Descriptor;
47                         break;
48                     }
49 
50                     default:
51                         break;
52                 }
53             }
54 
55             default:
56                 break;
57         }
58     }
59 
60     if (!BusMasterDescriptor)
61         return STATUS_DEVICE_CONFIGURATION_ERROR;
62 
63     if ((BusMasterDescriptor->Type == CmResourceTypePort) &&
64         (BusMasterDescriptor->Flags & CM_RESOURCE_PORT_IO))
65     {
66         IoBase = (PVOID)(ULONG_PTR)BusMasterDescriptor->u.Port.Start.QuadPart;
67     }
68     else
69     {
70         IoBase = MmMapIoSpace(BusMasterDescriptor->u.Memory.Start, 16, MmNonCached);
71         if (!IoBase)
72             return STATUS_INSUFFICIENT_RESOURCES;
73 
74         FdoExtension->IoBaseMapped = TRUE;
75     }
76     FdoExtension->BusMasterPortBase = IoBase;
77 
78     return STATUS_SUCCESS;
79 }
80 
81 static
82 CODE_SEG("PAGE")
83 NTSTATUS
PciIdeXFdoStartDevice(_In_ PFDO_DEVICE_EXTENSION FdoExtension,_In_ PIRP Irp)84 PciIdeXFdoStartDevice(
85     _In_ PFDO_DEVICE_EXTENSION FdoExtension,
86     _In_ PIRP Irp)
87 {
88     NTSTATUS Status;
89     PIO_STACK_LOCATION IoStack;
90 
91     PAGED_CODE();
92 
93     if (!NT_VERIFY(IoForwardIrpSynchronously(FdoExtension->Ldo, Irp)))
94     {
95         return STATUS_UNSUCCESSFUL;
96     }
97     Status = Irp->IoStatus.Status;
98     if (!NT_SUCCESS(Status))
99     {
100         return Status;
101     }
102 
103     IoStack = IoGetCurrentIrpStackLocation(Irp);
104 
105     Status = PciIdeXFdoParseResources(FdoExtension,
106                                       IoStack->Parameters.StartDevice.AllocatedResourcesTranslated);
107     if (!NT_SUCCESS(Status))
108     {
109         DPRINT1("Failed to parse resources 0x%lx\n", Status);
110         return Status;
111     }
112 
113     Status = PciIdeXStartMiniport(FdoExtension);
114     if (!NT_SUCCESS(Status))
115     {
116         DPRINT1("Miniport initialization failed 0x%lx\n", Status);
117         return Status;
118     }
119 
120     return STATUS_SUCCESS;
121 }
122 
123 static
124 CODE_SEG("PAGE")
125 VOID
PciIdeXFdoFreeResources(_In_ PFDO_DEVICE_EXTENSION FdoExtension)126 PciIdeXFdoFreeResources(
127     _In_ PFDO_DEVICE_EXTENSION FdoExtension)
128 {
129     PAGED_CODE();
130 
131     if (FdoExtension->IoBaseMapped)
132     {
133         MmUnmapIoSpace(FdoExtension->BusMasterPortBase, 16);
134         FdoExtension->IoBaseMapped = FALSE;
135     }
136 }
137 
138 static
139 CODE_SEG("PAGE")
140 NTSTATUS
PciIdeXFdoStopDevice(_In_ PFDO_DEVICE_EXTENSION FdoExtension)141 PciIdeXFdoStopDevice(
142     _In_ PFDO_DEVICE_EXTENSION FdoExtension)
143 {
144     PAGED_CODE();
145 
146     PciIdeXFdoFreeResources(FdoExtension);
147 
148     return STATUS_SUCCESS;
149 }
150 
151 static
152 CODE_SEG("PAGE")
153 NTSTATUS
PciIdeXFdoRemoveDevice(_In_ PFDO_DEVICE_EXTENSION FdoExtension,_In_ PIRP Irp)154 PciIdeXFdoRemoveDevice(
155     _In_ PFDO_DEVICE_EXTENSION FdoExtension,
156     _In_ PIRP Irp)
157 {
158     PPDO_DEVICE_EXTENSION PdoExtension;
159     NTSTATUS Status;
160     ULONG i;
161 
162     PAGED_CODE();
163 
164     PciIdeXFdoFreeResources(FdoExtension);
165 
166     ExAcquireFastMutex(&FdoExtension->DeviceSyncMutex);
167 
168     for (i = 0; i < MAX_IDE_CHANNEL; ++i)
169     {
170         PdoExtension = FdoExtension->Channels[i];
171 
172         if (PdoExtension)
173         {
174             IoDeleteDevice(PdoExtension->Common.Self);
175 
176             FdoExtension->Channels[i] = NULL;
177             break;
178         }
179     }
180 
181     ExReleaseFastMutex(&FdoExtension->DeviceSyncMutex);
182 
183     Irp->IoStatus.Status = STATUS_SUCCESS;
184     IoSkipCurrentIrpStackLocation(Irp);
185     Status = IoCallDriver(FdoExtension->Ldo, Irp);
186 
187     IoDetachDevice(FdoExtension->Ldo);
188     IoDeleteDevice(FdoExtension->Common.Self);
189 
190     return Status;
191 }
192 
193 static
194 CODE_SEG("PAGE")
195 NTSTATUS
PciIdeXFdoQueryPnpDeviceState(_In_ PFDO_DEVICE_EXTENSION FdoExtension,_In_ PIRP Irp)196 PciIdeXFdoQueryPnpDeviceState(
197     _In_ PFDO_DEVICE_EXTENSION FdoExtension,
198     _In_ PIRP Irp)
199 {
200     PAGED_CODE();
201 
202     if (FdoExtension->Common.PageFiles ||
203         FdoExtension->Common.HibernateFiles ||
204         FdoExtension->Common.DumpFiles)
205     {
206         Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE;
207     }
208 
209     Irp->IoStatus.Status = STATUS_SUCCESS;
210 
211     return STATUS_SUCCESS;
212 }
213 
214 static
215 CODE_SEG("PAGE")
216 PPDO_DEVICE_EXTENSION
PciIdeXPdoCreateDevice(_In_ PFDO_DEVICE_EXTENSION FdoExtension,_In_ ULONG ChannelNumber)217 PciIdeXPdoCreateDevice(
218     _In_ PFDO_DEVICE_EXTENSION FdoExtension,
219     _In_ ULONG ChannelNumber)
220 {
221     NTSTATUS Status;
222     UNICODE_STRING DeviceName;
223     WCHAR DeviceNameBuffer[sizeof("\\Device\\Ide\\PciIde999Channel9-FFF")];
224     PDEVICE_OBJECT Pdo;
225     PPDO_DEVICE_EXTENSION PdoExtension;
226     ULONG Alignment;
227     static ULONG DeviceNumber = 0;
228 
229     PAGED_CODE();
230 
231     Status = RtlStringCbPrintfW(DeviceNameBuffer,
232                                 sizeof(DeviceNameBuffer),
233                                 L"\\Device\\Ide\\PciIde%uChannel%u-%x",
234                                 FdoExtension->ControllerNumber,
235                                 ChannelNumber,
236                                 DeviceNumber++);
237     ASSERT(NT_SUCCESS(Status));
238     RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);
239 
240     Status = IoCreateDevice(FdoExtension->Common.Self->DriverObject,
241                             sizeof(*PdoExtension),
242                             &DeviceName,
243                             FILE_DEVICE_CONTROLLER,
244                             FILE_DEVICE_SECURE_OPEN,
245                             FALSE,
246                             &Pdo);
247     if (!NT_SUCCESS(Status))
248     {
249         DPRINT1("Failed to create PDO 0x%lx\n", Status);
250         return NULL;
251     }
252 
253     DPRINT("Created device object %p '%wZ'\n", Pdo, &DeviceName);
254 
255     /* DMA buffers alignment */
256     Alignment = FdoExtension->Properties.AlignmentRequirement;
257     Alignment = max(Alignment, FdoExtension->Common.Self->AlignmentRequirement);
258     Alignment = max(Alignment, FILE_WORD_ALIGNMENT);
259     Pdo->AlignmentRequirement = Alignment;
260 
261     PdoExtension = Pdo->DeviceExtension;
262 
263     RtlZeroMemory(PdoExtension, sizeof(*PdoExtension));
264     PdoExtension->Common.Self = Pdo;
265     PdoExtension->Channel = ChannelNumber;
266     PdoExtension->ParentController = FdoExtension;
267 
268     Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
269     return PdoExtension;
270 }
271 
272 static
273 CODE_SEG("PAGE")
274 NTSTATUS
PciIdeXFdoQueryBusRelations(_In_ PFDO_DEVICE_EXTENSION FdoExtension,_In_ PIRP Irp)275 PciIdeXFdoQueryBusRelations(
276     _In_ PFDO_DEVICE_EXTENSION FdoExtension,
277     _In_ PIRP Irp)
278 {
279     PPDO_DEVICE_EXTENSION PdoExtension;
280     IDE_CHANNEL_STATE ChannelState;
281     PDEVICE_RELATIONS DeviceRelations;
282     ULONG i;
283 
284     PAGED_CODE();
285 
286     DeviceRelations = ExAllocatePoolWithTag(PagedPool,
287                                             FIELD_OFFSET(DEVICE_RELATIONS,
288                                                          Objects[MAX_IDE_CHANNEL]),
289                                             TAG_PCIIDEX);
290     if (!DeviceRelations)
291         return STATUS_INSUFFICIENT_RESOURCES;
292 
293     DeviceRelations->Count = 0;
294 
295     ExAcquireFastMutex(&FdoExtension->DeviceSyncMutex);
296 
297     for (i = 0; i < MAX_IDE_CHANNEL; ++i)
298     {
299         PdoExtension = FdoExtension->Channels[i];
300 
301         /* Ignore disabled channels */
302         ChannelState = PciIdeXChannelState(FdoExtension, i);
303         if (ChannelState == ChannelDisabled)
304         {
305             if (PdoExtension)
306             {
307                 PdoExtension->ReportedMissing = TRUE;
308             }
309 
310             DPRINT("Channel %lu is disabled\n", i);
311             continue;
312         }
313 
314         /* Need to create a PDO */
315         if (!PdoExtension)
316         {
317             PdoExtension = PciIdeXPdoCreateDevice(FdoExtension, i);
318 
319             FdoExtension->Channels[i] = PdoExtension;
320         }
321 
322         if (PdoExtension && !PdoExtension->ReportedMissing)
323         {
324             DeviceRelations->Objects[DeviceRelations->Count++] = PdoExtension->Common.Self;
325             ObReferenceObject(PdoExtension->Common.Self);
326         }
327     }
328 
329     ExReleaseFastMutex(&FdoExtension->DeviceSyncMutex);
330 
331     Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
332     return STATUS_SUCCESS;
333 }
334 
335 static
336 CODE_SEG("PAGE")
337 NTSTATUS
PciIdeXFdoQueryDeviceUsageNotification(_In_ PFDO_DEVICE_EXTENSION FdoExtension,_In_ PIRP Irp)338 PciIdeXFdoQueryDeviceUsageNotification(
339     _In_ PFDO_DEVICE_EXTENSION FdoExtension,
340     _In_ PIRP Irp)
341 {
342     PIO_STACK_LOCATION IoStack;
343     NTSTATUS Status;
344     volatile LONG* Counter;
345 
346     PAGED_CODE();
347 
348     if (!NT_VERIFY(IoForwardIrpSynchronously(FdoExtension->Ldo, Irp)))
349     {
350         return STATUS_UNSUCCESSFUL;
351     }
352     Status = Irp->IoStatus.Status;
353     if (!NT_SUCCESS(Status))
354     {
355         return Status;
356     }
357 
358     IoStack = IoGetCurrentIrpStackLocation(Irp);
359     switch (IoStack->Parameters.UsageNotification.Type)
360     {
361         case DeviceUsageTypePaging:
362             Counter = &FdoExtension->Common.PageFiles;
363             break;
364 
365         case DeviceUsageTypeHibernation:
366             Counter = &FdoExtension->Common.HibernateFiles;
367             break;
368 
369         case DeviceUsageTypeDumpFile:
370             Counter = &FdoExtension->Common.DumpFiles;
371             break;
372 
373         default:
374             return Status;
375     }
376 
377     IoAdjustPagingPathCount(Counter, IoStack->Parameters.UsageNotification.InPath);
378 
379     return STATUS_SUCCESS;
380 }
381 
382 static
383 CODE_SEG("PAGE")
384 NTSTATUS
PciIdeXFdoQueryInterface(_In_ PFDO_DEVICE_EXTENSION FdoExtension,_In_ PIO_STACK_LOCATION IoStack)385 PciIdeXFdoQueryInterface(
386     _In_ PFDO_DEVICE_EXTENSION FdoExtension,
387     _In_ PIO_STACK_LOCATION IoStack)
388 {
389     PAGED_CODE();
390 
391     if (IsEqualGUIDAligned(IoStack->Parameters.QueryInterface.InterfaceType,
392                            &GUID_TRANSLATOR_INTERFACE_STANDARD))
393     {
394         CM_RESOURCE_TYPE ResourceType;
395         ULONG BusNumber;
396 
397         ResourceType = (ULONG_PTR)IoStack->Parameters.QueryInterface.InterfaceSpecificData;
398 
399         /* In native mode the IDE controller does not use any legacy interrupt resources */
400         if (FdoExtension->InNativeMode ||
401             ResourceType != CmResourceTypeInterrupt ||
402             IoStack->Parameters.QueryInterface.Size < sizeof(TRANSLATOR_INTERFACE))
403         {
404             return STATUS_NOT_SUPPORTED;
405         }
406 
407         return HalGetInterruptTranslator(PCIBus,
408                                          0,
409                                          InterfaceTypeUndefined,
410                                          sizeof(TRANSLATOR_INTERFACE),
411                                          IoStack->Parameters.QueryInterface.Version,
412                                          (PTRANSLATOR_INTERFACE)IoStack->
413                                          Parameters.QueryInterface.Interface,
414                                          &BusNumber);
415     }
416 
417     return STATUS_NOT_SUPPORTED;
418 }
419 
420 CODE_SEG("PAGE")
421 NTSTATUS
PciIdeXFdoDispatchPnp(_In_ PFDO_DEVICE_EXTENSION FdoExtension,_Inout_ PIRP Irp)422 PciIdeXFdoDispatchPnp(
423     _In_ PFDO_DEVICE_EXTENSION FdoExtension,
424     _Inout_ PIRP Irp)
425 {
426     PIO_STACK_LOCATION IoStack;
427     NTSTATUS Status;
428 
429     PAGED_CODE();
430 
431     IoStack = IoGetCurrentIrpStackLocation(Irp);
432     switch (IoStack->MinorFunction)
433     {
434         case IRP_MN_START_DEVICE:
435         {
436             Status = PciIdeXFdoStartDevice(FdoExtension, Irp);
437 
438             Irp->IoStatus.Status = Status;
439             IoCompleteRequest(Irp, IO_NO_INCREMENT);
440 
441             return Status;
442         }
443 
444         case IRP_MN_STOP_DEVICE:
445         {
446             Status = PciIdeXFdoStopDevice(FdoExtension);
447             break;
448         }
449 
450         case IRP_MN_REMOVE_DEVICE:
451             return PciIdeXFdoRemoveDevice(FdoExtension, Irp);
452 
453         case IRP_MN_QUERY_PNP_DEVICE_STATE:
454         {
455             Status = PciIdeXFdoQueryPnpDeviceState(FdoExtension, Irp);
456             break;
457         }
458 
459         case IRP_MN_QUERY_DEVICE_RELATIONS:
460         {
461             if (IoStack->Parameters.QueryDeviceRelations.Type != BusRelations)
462                 break;
463 
464             Status = PciIdeXFdoQueryBusRelations(FdoExtension, Irp);
465             if (!NT_SUCCESS(Status))
466             {
467                 Irp->IoStatus.Status = Status;
468                 IoCompleteRequest(Irp, IO_NO_INCREMENT);
469 
470                 return Status;
471             }
472 
473             Irp->IoStatus.Status = Status;
474             break;
475         }
476 
477         case IRP_MN_DEVICE_USAGE_NOTIFICATION:
478         {
479             Status = PciIdeXFdoQueryDeviceUsageNotification(FdoExtension, Irp);
480             break;
481         }
482 
483         case IRP_MN_QUERY_INTERFACE:
484         {
485             Status = PciIdeXFdoQueryInterface(FdoExtension, IoStack);
486             if (Status == STATUS_NOT_SUPPORTED)
487                 break;
488 
489             Irp->IoStatus.Status = Status;
490             break;
491         }
492 
493         case IRP_MN_QUERY_STOP_DEVICE:
494         case IRP_MN_QUERY_REMOVE_DEVICE:
495         case IRP_MN_SURPRISE_REMOVAL:
496         case IRP_MN_CANCEL_STOP_DEVICE:
497         case IRP_MN_CANCEL_REMOVE_DEVICE:
498             Irp->IoStatus.Status = STATUS_SUCCESS;
499             break;
500 
501         default:
502             break;
503     }
504 
505     IoSkipCurrentIrpStackLocation(Irp);
506     return IoCallDriver(FdoExtension->Ldo, Irp);
507 }
508