Sync with trunk r64222.
[reactos.git] / drivers / bus / pcix / enum.c
1 /*
2 * PROJECT: ReactOS PCI Bus Driver
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: drivers/bus/pci/enum.c
5 * PURPOSE: PCI Bus/Device Enumeration
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <pci.h>
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS ********************************************************************/
17
18 PIO_RESOURCE_REQUIREMENTS_LIST PciZeroIoResourceRequirements;
19
20 PCI_CONFIGURATOR PciConfigurators[] =
21 {
22 {
23 Device_MassageHeaderForLimitsDetermination,
24 Device_RestoreCurrent,
25 Device_SaveLimits,
26 Device_SaveCurrentSettings,
27 Device_ChangeResourceSettings,
28 Device_GetAdditionalResourceDescriptors,
29 Device_ResetDevice
30 },
31 {
32 PPBridge_MassageHeaderForLimitsDetermination,
33 PPBridge_RestoreCurrent,
34 PPBridge_SaveLimits,
35 PPBridge_SaveCurrentSettings,
36 PPBridge_ChangeResourceSettings,
37 PPBridge_GetAdditionalResourceDescriptors,
38 PPBridge_ResetDevice
39 },
40 {
41 Cardbus_MassageHeaderForLimitsDetermination,
42 Cardbus_RestoreCurrent,
43 Cardbus_SaveLimits,
44 Cardbus_SaveCurrentSettings,
45 Cardbus_ChangeResourceSettings,
46 Cardbus_GetAdditionalResourceDescriptors,
47 Cardbus_ResetDevice
48 }
49 };
50
51 /* FUNCTIONS ******************************************************************/
52
53 BOOLEAN
54 NTAPI
55 PciComputeNewCurrentSettings(IN PPCI_PDO_EXTENSION PdoExtension,
56 IN PCM_RESOURCE_LIST ResourceList)
57 {
58 PCM_PARTIAL_RESOURCE_DESCRIPTOR Partial, InterruptResource;
59 PCM_PARTIAL_RESOURCE_DESCRIPTOR BaseResource, CurrentDescriptor;
60 PCM_PARTIAL_RESOURCE_DESCRIPTOR PreviousDescriptor;
61 CM_PARTIAL_RESOURCE_DESCRIPTOR ResourceArray[7];
62 PCM_FULL_RESOURCE_DESCRIPTOR FullList;
63 BOOLEAN DrainPartial, RangeChange;
64 ULONG i, j;
65 PPCI_FUNCTION_RESOURCES PciResources;
66 PAGED_CODE();
67
68 /* Make sure we have either no resources, or at least one */
69 ASSERT((ResourceList == NULL) || (ResourceList->Count == 1));
70
71 /* Initialize no partial, interrupt descriptor, or range change */
72 Partial = NULL;
73 InterruptResource = NULL;
74 RangeChange = FALSE;
75
76 /* Check if there's not actually any resources */
77 if (!(ResourceList) || !(ResourceList->Count))
78 {
79 /* Then just return the hardware update state */
80 return PdoExtension->UpdateHardware;
81 }
82
83 /* Print the new specified resource list */
84 PciDebugPrintCmResList(ResourceList);
85
86 /* Clear the temporary resource array */
87 for (i = 0; i < 7; i++) ResourceArray[i].Type = CmResourceTypeNull;
88
89 /* Loop the full resource descriptor */
90 FullList = ResourceList->List;
91 for (i = 0; i < ResourceList->Count; i++)
92 {
93 /* Initialize loop variables */
94 DrainPartial = FALSE;
95 BaseResource = NULL;
96
97 /* Loop the partial descriptors */
98 Partial = FullList->PartialResourceList.PartialDescriptors;
99 for (j = 0; j < FullList->PartialResourceList.Count; j++)
100 {
101 /* Check if we were supposed to drain a partial due to device data */
102 if (DrainPartial)
103 {
104 /* Draining complete, move on to the next descriptor then */
105 DrainPartial--;
106 continue;
107 }
108
109 /* Check what kind of descriptor this was */
110 switch (Partial->Type)
111 {
112 /* Base BAR resources */
113 case CmResourceTypePort:
114 case CmResourceTypeMemory:
115
116 /* Set it as the base */
117 ASSERT(BaseResource == NULL);
118 BaseResource = Partial;
119 break;
120
121 /* Interrupt resource */
122 case CmResourceTypeInterrupt:
123
124 /* Make sure it's a compatible (and the only) PCI interrupt */
125 ASSERT(InterruptResource == NULL);
126 ASSERT(Partial->u.Interrupt.Level == Partial->u.Interrupt.Vector);
127 InterruptResource = Partial;
128
129 /* Only 255 interrupts on x86/x64 hardware */
130 if (Partial->u.Interrupt.Level < 256)
131 {
132 /* Use the passed interrupt line */
133 PdoExtension->AdjustedInterruptLine = Partial->u.Interrupt.Level;
134 }
135 else
136 {
137 /* Invalid vector, so ignore it */
138 PdoExtension->AdjustedInterruptLine = 0;
139 }
140
141 break;
142
143 /* Check for specific device data */
144 case CmResourceTypeDevicePrivate:
145
146 /* Check what kind of data this was */
147 switch (Partial->u.DevicePrivate.Data[0])
148 {
149 /* Not used in the driver yet */
150 case 1:
151 UNIMPLEMENTED_DBGBREAK();
152 break;
153
154 /* Not used in the driver yet */
155 case 2:
156 UNIMPLEMENTED_DBGBREAK();
157 break;
158
159 /* A drain request */
160 case 3:
161 /* Shouldn't be a base resource, this is a drain */
162 ASSERT(BaseResource == NULL);
163 DrainPartial = Partial->u.DevicePrivate.Data[1];
164 ASSERT(DrainPartial == TRUE);
165 break;
166 }
167 break;
168 }
169
170 /* Move to the next descriptor */
171 Partial = PciNextPartialDescriptor(Partial);
172 }
173
174 /* We should be starting a new list now */
175 ASSERT(BaseResource == NULL);
176 FullList = (PVOID)Partial;
177 }
178
179 /* Check the current assigned PCI resources */
180 PciResources = PdoExtension->Resources;
181 if (!PciResources) return FALSE;
182
183 //if... // MISSING CODE
184 UNIMPLEMENTED;
185 DPRINT1("Missing sanity checking code!\n");
186
187 /* Loop all the PCI function resources */
188 for (i = 0; i < 7; i++)
189 {
190 /* Get the current function resource descriptor, and the new one */
191 CurrentDescriptor = &PciResources->Current[i];
192 Partial = &ResourceArray[i];
193
194 /* Previous is current during the first loop iteration */
195 PreviousDescriptor = &PciResources->Current[(i == 0) ? (0) : (i - 1)];
196
197 /* Check if this new descriptor is different than the old one */
198 if (((Partial->Type != CurrentDescriptor->Type) ||
199 (Partial->Type != CmResourceTypeNull)) &&
200 ((Partial->u.Generic.Start.QuadPart !=
201 CurrentDescriptor->u.Generic.Start.QuadPart) ||
202 (Partial->u.Generic.Length != CurrentDescriptor->u.Generic.Length)))
203 {
204 /* Record a change */
205 RangeChange = TRUE;
206
207 /* Was there a range before? */
208 if (CurrentDescriptor->Type != CmResourceTypeNull)
209 {
210 /* Print it */
211 DbgPrint(" Old range-\n");
212 PciDebugPrintPartialResource(CurrentDescriptor);
213 }
214 else
215 {
216 /* There was no range */
217 DbgPrint(" Previously unset range\n");
218 }
219
220 /* Print new one */
221 DbgPrint(" changed to\n");
222 PciDebugPrintPartialResource(Partial);
223
224 /* Update to new range */
225 CurrentDescriptor->Type = Partial->Type;
226 PreviousDescriptor->u.Generic.Start = Partial->u.Generic.Start;
227 PreviousDescriptor->u.Generic.Length = Partial->u.Generic.Length;
228 CurrentDescriptor = PreviousDescriptor;
229 }
230 }
231
232 /* Either the hardware was updated, or a resource range changed */
233 return ((RangeChange) || (PdoExtension->UpdateHardware));
234 }
235
236 VOID
237 NTAPI
238 PcipUpdateHardware(IN PVOID Context,
239 IN PVOID Context2)
240 {
241 PPCI_PDO_EXTENSION PdoExtension = Context;
242 PPCI_COMMON_HEADER PciData = Context2;
243
244 /* Check if we're allowed to disable decodes */
245 PciData->Command = PdoExtension->CommandEnables;
246 if (!(PdoExtension->HackFlags & PCI_HACK_PRESERVE_COMMAND))
247 {
248 /* Disable all decodes */
249 PciData->Command &= ~(PCI_ENABLE_IO_SPACE |
250 PCI_ENABLE_MEMORY_SPACE |
251 PCI_ENABLE_BUS_MASTER |
252 PCI_ENABLE_WRITE_AND_INVALIDATE);
253 }
254
255 /* Update the device configuration */
256 PciData->Status = 0;
257 PciWriteDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH);
258
259 /* Turn decodes back on */
260 PciDecodeEnable(PdoExtension, TRUE, &PdoExtension->CommandEnables);
261 }
262
263 VOID
264 NTAPI
265 PciUpdateHardware(IN PPCI_PDO_EXTENSION PdoExtension,
266 IN PPCI_COMMON_HEADER PciData)
267 {
268 PCI_IPI_CONTEXT Context;
269
270 /* Check for critical devices and PCI Debugging devices */
271 if ((PdoExtension->HackFlags & PCI_HACK_CRITICAL_DEVICE) ||
272 (PdoExtension->OnDebugPath))
273 {
274 /* Build the context and send an IPI */
275 Context.RunCount = 1;
276 Context.Barrier = 1;
277 Context.Context = PciData;
278 Context.Function = PcipUpdateHardware;
279 Context.DeviceExtension = PdoExtension;
280 KeIpiGenericCall(PciExecuteCriticalSystemRoutine, (ULONG_PTR)&Context);
281 }
282 else
283 {
284 /* Just to the update inline */
285 PcipUpdateHardware(PdoExtension, PciData);
286 }
287 }
288
289 PIO_RESOURCE_REQUIREMENTS_LIST
290 NTAPI
291 PciAllocateIoRequirementsList(IN ULONG Count,
292 IN ULONG BusNumber,
293 IN ULONG SlotNumber)
294 {
295 SIZE_T Size;
296 PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList;
297
298 /* Calculate the final size of the list, including each descriptor */
299 Size = sizeof(IO_RESOURCE_REQUIREMENTS_LIST);
300 if (Count > 1) Size = sizeof(IO_RESOURCE_DESCRIPTOR) * (Count - 1) +
301 sizeof(IO_RESOURCE_REQUIREMENTS_LIST);
302
303 /* Allocate the list */
304 RequirementsList = ExAllocatePoolWithTag(PagedPool, Size, 'BicP');
305 if (!RequirementsList) return NULL;
306
307 /* Initialize it */
308 RtlZeroMemory(RequirementsList, Size);
309 RequirementsList->AlternativeLists = 1;
310 RequirementsList->BusNumber = BusNumber;
311 RequirementsList->SlotNumber = SlotNumber;
312 RequirementsList->InterfaceType = PCIBus;
313 RequirementsList->ListSize = Size;
314 RequirementsList->List[0].Count = Count;
315 RequirementsList->List[0].Version = 1;
316 RequirementsList->List[0].Revision = 1;
317
318 /* Return it */
319 return RequirementsList;
320 }
321
322 PCM_RESOURCE_LIST
323 NTAPI
324 PciAllocateCmResourceList(IN ULONG Count,
325 IN ULONG BusNumber)
326 {
327 SIZE_T Size;
328 PCM_RESOURCE_LIST ResourceList;
329
330 /* Calculate the final size of the list, including each descriptor */
331 Size = sizeof(CM_RESOURCE_LIST);
332 if (Count > 1) Size = sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * (Count - 1) +
333 sizeof(CM_RESOURCE_LIST);
334
335 /* Allocate the list */
336 ResourceList = ExAllocatePoolWithTag(PagedPool, Size, 'BicP');
337 if (!ResourceList) return NULL;
338
339 /* Initialize it */
340 RtlZeroMemory(ResourceList, Size);
341 ResourceList->Count = 1;
342 ResourceList->List[0].BusNumber = BusNumber;
343 ResourceList->List[0].InterfaceType = PCIBus;
344 ResourceList->List[0].PartialResourceList.Version = 1;
345 ResourceList->List[0].PartialResourceList.Revision = 1;
346 ResourceList->List[0].PartialResourceList.Count = Count;
347
348 /* Return it */
349 return ResourceList;
350 }
351
352 NTSTATUS
353 NTAPI
354 PciQueryResources(IN PPCI_PDO_EXTENSION PdoExtension,
355 OUT PCM_RESOURCE_LIST *Buffer)
356 {
357 PPCI_FUNCTION_RESOURCES PciResources;
358 BOOLEAN HaveVga, HaveMemSpace, HaveIoSpace;
359 USHORT BridgeControl, PciCommand;
360 ULONG Count, i;
361 PCM_PARTIAL_RESOURCE_DESCRIPTOR Partial, Resource, LastResource;
362 PCM_RESOURCE_LIST ResourceList;
363 UCHAR InterruptLine;
364 PAGED_CODE();
365
366 /* Assume failure */
367 Count = 0;
368 HaveVga = FALSE;
369 *Buffer = NULL;
370
371 /* Make sure there's some resources to query */
372 PciResources = PdoExtension->Resources;
373 if (!PciResources) return STATUS_SUCCESS;
374
375 /* Read the decodes */
376 PciReadDeviceConfig(PdoExtension,
377 &PciCommand,
378 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
379 sizeof(USHORT));
380
381 /* Check which ones are turned on */
382 HaveIoSpace = PciCommand & PCI_ENABLE_IO_SPACE;
383 HaveMemSpace = PciCommand & PCI_ENABLE_MEMORY_SPACE;
384
385 /* Loop maximum possible descriptors */
386 for (i = 0; i < 7; i++)
387 {
388 /* Check if the decode for this descriptor is actually turned on */
389 Partial = &PciResources->Current[i];
390 if (((HaveMemSpace) && (Partial->Type == CmResourceTypeMemory)) ||
391 ((HaveIoSpace) && (Partial->Type == CmResourceTypePort)))
392 {
393 /* One more fully active descriptor */
394 Count++;
395 }
396 }
397
398 /* If there's an interrupt pin associated, check at least one decode is on */
399 if ((PdoExtension->InterruptPin) && ((HaveMemSpace) || (HaveIoSpace)))
400 {
401 /* Read the interrupt line for the pin, add a descriptor if it's valid */
402 InterruptLine = PdoExtension->AdjustedInterruptLine;
403 if ((InterruptLine) && (InterruptLine != -1)) Count++;
404 }
405
406 /* Check for PCI bridge */
407 if (PdoExtension->HeaderType == PCI_BRIDGE_TYPE)
408 {
409 /* Read bridge settings, check if VGA is present */
410 PciReadDeviceConfig(PdoExtension,
411 &BridgeControl,
412 FIELD_OFFSET(PCI_COMMON_HEADER, u.type1.BridgeControl),
413 sizeof(USHORT));
414 if (BridgeControl & PCI_ENABLE_BRIDGE_VGA)
415 {
416 /* Remember for later */
417 HaveVga = TRUE;
418
419 /* One memory descriptor for 0xA0000, plus the two I/O port ranges */
420 if (HaveMemSpace) Count++;
421 if (HaveIoSpace) Count += 2;
422 }
423 }
424
425 /* If there's no descriptors in use, there's no resources, so return */
426 if (!Count) return STATUS_SUCCESS;
427
428 /* Allocate a resource list to hold the resources */
429 ResourceList = PciAllocateCmResourceList(Count,
430 PdoExtension->ParentFdoExtension->BaseBus);
431 if (!ResourceList) return STATUS_INSUFFICIENT_RESOURCES;
432
433 /* This is where the descriptors will be copied into */
434 Resource = ResourceList->List[0].PartialResourceList.PartialDescriptors;
435 LastResource = Resource + Count + 1;
436
437 /* Loop maximum possible descriptors */
438 for (i = 0; i < 7; i++)
439 {
440 /* Check if the decode for this descriptor is actually turned on */
441 Partial = &PciResources->Current[i];
442 if (((HaveMemSpace) && (Partial->Type == CmResourceTypeMemory)) ||
443 ((HaveIoSpace) && (Partial->Type == CmResourceTypePort)))
444 {
445 /* Copy the descriptor into the resource list */
446 *Resource++ = *Partial;
447 }
448 }
449
450 /* Check if earlier the code detected this was a PCI bridge with VGA on it */
451 if (HaveVga)
452 {
453 /* Are the memory decodes enabled? */
454 if (HaveMemSpace)
455 {
456 /* Build a memory descriptor for a 128KB framebuffer at 0xA0000 */
457 Resource->Flags = CM_RESOURCE_MEMORY_READ_WRITE;
458 Resource->u.Generic.Start.HighPart = 0;
459 Resource->Type = CmResourceTypeMemory;
460 Resource->u.Generic.Start.LowPart = 0xA0000;
461 Resource->u.Generic.Length = 0x20000;
462 Resource++;
463 }
464
465 /* Are the I/O decodes enabled? */
466 if (HaveIoSpace)
467 {
468 /* Build an I/O descriptor for the graphic ports at 0x3B0 */
469 Resource->Type = CmResourceTypePort;
470 Resource->Flags = CM_RESOURCE_PORT_POSITIVE_DECODE | CM_RESOURCE_PORT_10_BIT_DECODE;
471 Resource->u.Port.Start.QuadPart = 0x3B0u;
472 Resource->u.Port.Length = 0xC;
473 Resource++;
474
475 /* Build an I/O descriptor for the graphic ports at 0x3C0 */
476 Resource->Type = CmResourceTypePort;
477 Resource->Flags = CM_RESOURCE_PORT_POSITIVE_DECODE | CM_RESOURCE_PORT_10_BIT_DECODE;
478 Resource->u.Port.Start.QuadPart = 0x3C0u;
479 Resource->u.Port.Length = 0x20;
480 Resource++;
481 }
482 }
483
484 /* If there's an interrupt pin associated, check at least one decode is on */
485 if ((PdoExtension->InterruptPin) && ((HaveMemSpace) || (HaveIoSpace)))
486 {
487 /* Read the interrupt line for the pin, check if it's valid */
488 InterruptLine = PdoExtension->AdjustedInterruptLine;
489 if ((InterruptLine) && (InterruptLine != -1))
490 {
491 /* Make sure there's still space */
492 ASSERT(Resource < LastResource);
493
494 /* Add the interrupt descriptor */
495 Resource->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
496 Resource->Type = CmResourceTypeInterrupt;
497 Resource->ShareDisposition = CmResourceShareShared;
498 Resource->u.Interrupt.Affinity = -1;
499 Resource->u.Interrupt.Level = InterruptLine;
500 Resource->u.Interrupt.Vector = InterruptLine;
501 }
502 }
503
504 /* Return the resouce list */
505 *Buffer = ResourceList;
506 return STATUS_SUCCESS;
507 }
508
509 NTSTATUS
510 NTAPI
511 PciQueryTargetDeviceRelations(IN PPCI_PDO_EXTENSION PdoExtension,
512 IN OUT PDEVICE_RELATIONS *pDeviceRelations)
513 {
514 PDEVICE_RELATIONS DeviceRelations;
515 PAGED_CODE();
516
517 /* If there were existing relations, free them */
518 if (*pDeviceRelations) ExFreePoolWithTag(*pDeviceRelations, 0);
519
520 /* Allocate a new structure for the relations */
521 DeviceRelations = ExAllocatePoolWithTag(NonPagedPool,
522 sizeof(DEVICE_RELATIONS),
523 'BicP');
524 if (!DeviceRelations) return STATUS_INSUFFICIENT_RESOURCES;
525
526 /* Only one relation: the PDO */
527 DeviceRelations->Count = 1;
528 DeviceRelations->Objects[0] = PdoExtension->PhysicalDeviceObject;
529 ObReferenceObject(DeviceRelations->Objects[0]);
530
531 /* Return the new relations */
532 *pDeviceRelations = DeviceRelations;
533 return STATUS_SUCCESS;
534 }
535
536 NTSTATUS
537 NTAPI
538 PciQueryEjectionRelations(IN PPCI_PDO_EXTENSION PdoExtension,
539 IN OUT PDEVICE_RELATIONS *pDeviceRelations)
540 {
541 UNREFERENCED_PARAMETER(PdoExtension);
542 UNREFERENCED_PARAMETER(pDeviceRelations);
543
544 /* Not yet implemented */
545 UNIMPLEMENTED_DBGBREAK();
546 return STATUS_NOT_IMPLEMENTED;
547 }
548
549 NTSTATUS
550 NTAPI
551 PciBuildRequirementsList(IN PPCI_PDO_EXTENSION PdoExtension,
552 IN PPCI_COMMON_HEADER PciData,
553 OUT PIO_RESOURCE_REQUIREMENTS_LIST* Buffer)
554 {
555 PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList;
556
557 UNREFERENCED_PARAMETER(PdoExtension);
558 UNREFERENCED_PARAMETER(PciData);
559
560 {
561 /* There aren't, so use the zero descriptor */
562 RequirementsList = PciZeroIoResourceRequirements;
563
564 /* Does it actually exist yet? */
565 if (!PciZeroIoResourceRequirements)
566 {
567 /* Allocate it, and use it for future use */
568 RequirementsList = PciAllocateIoRequirementsList(0, 0, 0);
569 PciZeroIoResourceRequirements = RequirementsList;
570 if (!PciZeroIoResourceRequirements) return STATUS_INSUFFICIENT_RESOURCES;
571 }
572
573 /* Return the zero requirements list to the caller */
574 *Buffer = RequirementsList;
575 DPRINT1("PCI - build resource reqs - early out, 0 resources\n");
576 return STATUS_SUCCESS;
577 }
578 return STATUS_SUCCESS;
579 }
580
581 NTSTATUS
582 NTAPI
583 PciQueryRequirements(IN PPCI_PDO_EXTENSION PdoExtension,
584 IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementsList)
585 {
586 NTSTATUS Status;
587 PCI_COMMON_HEADER PciHeader;
588 PAGED_CODE();
589
590 /* Check if the PDO has any resources, or at least an interrupt pin */
591 if ((PdoExtension->Resources) || (PdoExtension->InterruptPin))
592 {
593 /* Read the current PCI header */
594 PciReadDeviceConfig(PdoExtension, &PciHeader, 0, PCI_COMMON_HDR_LENGTH);
595
596 /* Use it to build a list of requirements */
597 Status = PciBuildRequirementsList(PdoExtension, &PciHeader, RequirementsList);
598 if (!NT_SUCCESS(Status)) return Status;
599
600 /* Is this a Compaq PCI Hotplug Controller (r17) on a PAE system ? */
601 if ((PciHeader.VendorID == 0xE11) &&
602 (PciHeader.DeviceID == 0xA0F7) &&
603 (PciHeader.RevisionID == 17) &&
604 (ExIsProcessorFeaturePresent(PF_PAE_ENABLED)))
605 {
606 /* Have not tested this on eVb's machine yet */
607 UNIMPLEMENTED_DBGBREAK();
608 }
609
610 /* Check if the requirements are actually the zero list */
611 if (*RequirementsList == PciZeroIoResourceRequirements)
612 {
613 /* A simple NULL will sufficie for the PnP Manager */
614 *RequirementsList = NULL;
615 DPRINT1("Returning NULL requirements list\n");
616 }
617 else
618 {
619 /* Otherwise, print out the requirements list */
620 PciDebugPrintIoResReqList(*RequirementsList);
621 }
622 }
623 else
624 {
625 /* There aren't any resources, so simply return NULL */
626 DPRINT1("PciQueryRequirements returning NULL requirements list\n");
627 *RequirementsList = NULL;
628 }
629
630 /* This call always succeeds (but maybe with no requirements) */
631 return STATUS_SUCCESS;
632 }
633
634 /*
635 * 7. The IO/MEM/Busmaster decodes are disabled for the device.
636 * 8. The PCI bus driver sets the operating mode bits of the Programming
637 * Interface byte to switch the controller to native mode.
638 *
639 * Important: When the controller is set to native mode, it must quiet itself
640 * and must not decode I/O resources or generate interrupts until the operating
641 * system has enabled the ports in the PCI configuration header.
642 * The IO/MEM/BusMaster bits will be disabled before the mode change, but it
643 * is not possible to disable interrupts on the device. The device must not
644 * generate interrupts (either legacy or native mode) while the decodes are
645 * disabled in the command register.
646 *
647 * This operation is expected to be instantaneous and the operating system does
648 * not stall afterward. It is also expected that the interrupt pin register in
649 * the PCI Configuration space for this device is accurate. The operating system
650 * re-reads this data after previously ignoring it.
651 */
652 BOOLEAN
653 NTAPI
654 PciConfigureIdeController(IN PPCI_PDO_EXTENSION PdoExtension,
655 IN PPCI_COMMON_HEADER PciData,
656 IN BOOLEAN Initial)
657 {
658 UCHAR MasterMode, SlaveMode, MasterFixed, SlaveFixed, ProgIf, NewProgIf;
659 BOOLEAN Switched;
660 USHORT Command;
661
662 /* Assume it won't work */
663 Switched = FALSE;
664
665 /* Get master and slave current settings, and programmability flag */
666 ProgIf = PciData->ProgIf;
667 MasterMode = (ProgIf & 1) == 1;
668 MasterFixed = (ProgIf & 2) == 0;
669 SlaveMode = (ProgIf & 4) == 4;
670 SlaveFixed = (ProgIf & 8) == 0;
671
672 /*
673 * [..] In order for Windows XP SP1 and Windows Server 2003 to switch an ATA
674 * ATA controller from compatible mode to native mode, the following must be
675 * true:
676 *
677 * - The controller must indicate in its programming interface that both channels
678 * can be switched to native mode. Windows XP SP1 and Windows Server 2003 do
679 * not support switching only one IDE channel to native mode. See the PCI IDE
680 * Controller Specification Revision 1.0 for details.
681 */
682 if ((MasterMode != SlaveMode) || (MasterFixed != SlaveFixed))
683 {
684 /* Windows does not support this configuration, fail */
685 DPRINT1("PCI: Warning unsupported IDE controller configuration for VEN_%04x&DEV_%04x!",
686 PdoExtension->VendorId,
687 PdoExtension->DeviceId);
688 return Switched;
689 }
690
691 /* Check if the controller is already in native mode */
692 if ((MasterMode) && (SlaveMode))
693 {
694 /* Check if I/O decodes should be disabled */
695 if ((Initial) || (PdoExtension->IoSpaceUnderNativeIdeControl))
696 {
697 /* Read the current command */
698 PciReadDeviceConfig(PdoExtension,
699 &Command,
700 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
701 sizeof(USHORT));
702
703 /* Disable I/O space decode */
704 Command &= ~PCI_ENABLE_IO_SPACE;
705
706 /* Update new command in PCI IDE controller */
707 PciWriteDeviceConfig(PdoExtension,
708 &Command,
709 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
710 sizeof(USHORT));
711
712 /* Save updated command value */
713 PciData->Command = Command;
714 }
715
716 /* The controller is now in native mode */
717 Switched = TRUE;
718 }
719 else if (!(MasterFixed) &&
720 !(SlaveFixed) &&
721 (PdoExtension->BIOSAllowsIDESwitchToNativeMode) &&
722 !(PdoExtension->HackFlags & PCI_HACK_DISABLE_IDE_NATIVE_MODE))
723 {
724 /* Turn off decodes */
725 PciDecodeEnable(PdoExtension, FALSE, NULL);
726
727 /* Update the current command */
728 PciReadDeviceConfig(PdoExtension,
729 &PciData->Command,
730 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
731 sizeof(USHORT));
732
733 /* Enable native mode */
734 ProgIf = PciData->ProgIf | 5;
735 PciWriteDeviceConfig(PdoExtension,
736 &ProgIf,
737 FIELD_OFFSET(PCI_COMMON_HEADER, ProgIf),
738 sizeof(UCHAR));
739
740 /* Verify the setting "stuck" */
741 PciReadDeviceConfig(PdoExtension,
742 &NewProgIf,
743 FIELD_OFFSET(PCI_COMMON_HEADER, ProgIf),
744 sizeof(UCHAR));
745 if (NewProgIf == ProgIf)
746 {
747 /* Update the header and PDO data with the new programming mode */
748 PciData->ProgIf = ProgIf;
749 PdoExtension->ProgIf = NewProgIf;
750
751 /* Clear the first four BARs to reset current BAR setttings */
752 PciData->u.type0.BaseAddresses[0] = 0;
753 PciData->u.type0.BaseAddresses[1] = 0;
754 PciData->u.type0.BaseAddresses[2] = 0;
755 PciData->u.type0.BaseAddresses[3] = 0;
756 PciWriteDeviceConfig(PdoExtension,
757 PciData->u.type0.BaseAddresses,
758 FIELD_OFFSET(PCI_COMMON_HEADER,
759 u.type0.BaseAddresses),
760 4 * sizeof(ULONG));
761
762 /* Re-read the BARs to have the latest data for native mode IDE */
763 PciReadDeviceConfig(PdoExtension,
764 PciData->u.type0.BaseAddresses,
765 FIELD_OFFSET(PCI_COMMON_HEADER,
766 u.type0.BaseAddresses),
767 4 * sizeof(ULONG));
768
769 /* Re-read the interrupt pin used for native mode IDE */
770 PciReadDeviceConfig(PdoExtension,
771 &PciData->u.type0.InterruptPin,
772 FIELD_OFFSET(PCI_COMMON_HEADER,
773 u.type0.InterruptPin),
774 sizeof(UCHAR));
775
776 /* The IDE Controller is now in native mode */
777 Switched = TRUE;
778 }
779 else
780 {
781 /* Settings did not work, fail */
782 DPRINT1("PCI: Warning failed switch to native mode for IDE controller VEN_%04x&DEV_%04x!",
783 PciData->VendorID,
784 PciData->DeviceID);
785 }
786 }
787
788 /* Return whether or not native mode was enabled on the IDE controller */
789 return Switched;
790 }
791
792 VOID
793 NTAPI
794 PciApplyHacks(IN PPCI_FDO_EXTENSION DeviceExtension,
795 IN PPCI_COMMON_HEADER PciData,
796 IN PCI_SLOT_NUMBER SlotNumber,
797 IN ULONG OperationType,
798 PPCI_PDO_EXTENSION PdoExtension)
799 {
800 ULONG LegacyBaseAddress;
801 USHORT Command;
802 UCHAR RegValue;
803
804 UNREFERENCED_PARAMETER(SlotNumber);
805
806 /* Check what kind of hack operation this is */
807 switch (OperationType)
808 {
809 /*
810 * This is mostly concerned with fixing up incorrect class data that can
811 * exist on certain PCI hardware before the 2.0 spec was ratified.
812 */
813 case PCI_HACK_FIXUP_BEFORE_CONFIGURATION:
814
815 /* Note that the i82375 PCI/EISA and the i82378 PCI/ISA bridges that
816 * are present on certain DEC/NT Alpha machines are pre-PCI 2.0 devices
817 * and appear as non-classified, so their correct class/subclass data
818 * is written here instead.
819 */
820 if ((PciData->VendorID == 0x8086) &&
821 ((PciData->DeviceID == 0x482) || (PciData->DeviceID == 0x484)))
822 {
823 /* Note that 0x482 is the i82375 (EISA), 0x484 is the i82378 (ISA) */
824 PciData->SubClass = PciData->DeviceID == 0x482 ?
825 PCI_SUBCLASS_BR_EISA : PCI_SUBCLASS_BR_ISA;
826 PciData->BaseClass = PCI_CLASS_BRIDGE_DEV;
827
828 /*
829 * Because the software is modifying the actual header data from
830 * the BIOS, this flag tells the driver to ignore failures when
831 * comparing the original BIOS data with the PCI data.
832 */
833 if (PdoExtension) PdoExtension->ExpectedWritebackFailure = TRUE;
834 }
835
836 /* Note that in this case, an immediate return is issued */
837 return;
838
839 /*
840 * This is concerned with setting up interrupts correctly for native IDE
841 * mode, but will also handle broken VGA decoding on older bridges as
842 * well as a PAE-specific hack for certain Compaq Hot-Plug Controllers.
843 */
844 case PCI_HACK_FIXUP_AFTER_CONFIGURATION:
845
846 /* There should always be a PDO extension passed in */
847 ASSERT(PdoExtension);
848
849 /*
850 * On the OPTi Viper-M IDE controller, Linux doesn't support IDE-DMA
851 * and FreeBSD bug reports indicate that the system crashes when the
852 * feature is enabled (so it's disabled on that OS as well). In the
853 * NT PCI Bus Driver, it seems Microsoft too, completely disables
854 * Native IDE functionality on this controller, so it would seem OPTi
855 * simply frelled up this controller.
856 */
857 if ((PciData->VendorID == 0x1045) && (PciData->DeviceID != 0xC621))
858 {
859 /* Disable native mode */
860 PciData->ProgIf &= ~5;
861 PciData->u.type0.InterruptPin = 0;
862
863 /*
864 * Because the software is modifying the actual header data from
865 * the BIOS, this flag tells the driver to ignore failures when
866 * comparing the original BIOS data with the PCI data.
867 */
868 PdoExtension->ExpectedWritebackFailure = TRUE;
869 }
870 else if ((PciData->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) &&
871 (PciData->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR))
872 {
873 /* For other IDE controllers, start out in compatible mode */
874 PdoExtension->BIOSAllowsIDESwitchToNativeMode = FALSE;
875
876 /*
877 * Registry must have enabled native mode (typically as a result
878 * of an INF file directive part of the IDE controller's driver)
879 * and the system must not be booted in Safe Mode. If that checks
880 * out, then evaluate the ACPI NATA method to see if the platform
881 * supports this. See the section "BIOS and Platform Prerequisites
882 * for Switching a Native-Mode-Capable Controller" in the Storage
883 * section of the Windows Driver Kit for more details:
884 *
885 * 5. For each ATA controller enumerated, the PCI bus driver checks
886 * the Programming Interface register of the IDE controller to
887 * see if it supports switching both channels to native mode.
888 * 6. The PCI bus driver checks whether the BIOS/platform supports
889 * switching the controller by checking the NATA method described
890 * earlier in this article.
891 *
892 * If an ATA controller does not indicate that it is native
893 * mode-capable, or if the BIOS NATA control method is missing
894 * or does not list that device, the PCI bus driver does not
895 * switch the controller and it is assigned legacy resources.
896 *
897 * If both the controller and the BIOS indicate that the controller
898 * can be switched, the process of switching the controller begins
899 * with the next step.
900 */
901 if ((PciEnableNativeModeATA) &&
902 !(InitSafeBootMode) &&
903 (PciIsSlotPresentInParentMethod(PdoExtension, 'ATAN')))
904 {
905 /* The platform supports it, remember that */
906 PdoExtension->BIOSAllowsIDESwitchToNativeMode = TRUE;
907
908 /*
909 * Now switch the controller into native mode if both channels
910 * support native IDE mode. See "How Windows Switches an ATA
911 * Controller to Native Mode" in the Storage section of the
912 * Windows Driver Kit for more details.
913 */
914 PdoExtension->IDEInNativeMode =
915 PciConfigureIdeController(PdoExtension, PciData, TRUE);
916 }
917
918 /* Is native mode enabled after all? */
919 if ((PciData->ProgIf & 5) != 5)
920 {
921 /* Compatible mode, so force ISA-style IRQ14 and IRQ 15 */
922 PciData->u.type0.InterruptPin = 0;
923 }
924 }
925
926 /* Is this a PCI device with legacy VGA card decodes on the root bus? */
927 if ((PdoExtension->HackFlags & PCI_HACK_VIDEO_LEGACY_DECODE) &&
928 (PCI_IS_ROOT_FDO(DeviceExtension)) &&
929 !(DeviceExtension->BrokenVideoHackApplied))
930 {
931 /* Tell the arbiter to apply a hack for these older devices */
932 ario_ApplyBrokenVideoHack(DeviceExtension);
933 }
934
935 /* Is this a Compaq PCI Hotplug Controller (r17) on a PAE system ? */
936 if ((PciData->VendorID == 0xE11) &&
937 (PciData->DeviceID == 0xA0F7) &&
938 (PciData->RevisionID == 17) &&
939 (ExIsProcessorFeaturePresent(PF_PAE_ENABLED)))
940 {
941 /* Turn off the decodes immediately */
942 PciData->Command &= ~(PCI_ENABLE_IO_SPACE |
943 PCI_ENABLE_MEMORY_SPACE |
944 PCI_ENABLE_BUS_MASTER);
945 PciWriteDeviceConfig(PdoExtension,
946 &PciData->Command,
947 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
948 sizeof(USHORT));
949
950 /* Do not EVER turn them on again, this will blow up the system */
951 PdoExtension->CommandEnables &= ~(PCI_ENABLE_IO_SPACE |
952 PCI_ENABLE_MEMORY_SPACE |
953 PCI_ENABLE_BUS_MASTER);
954 PdoExtension->HackFlags |= PCI_HACK_PRESERVE_COMMAND;
955 }
956 break;
957
958 /*
959 * This is called whenever resources are changed and hardware needs to be
960 * updated. It is concerned with two highly specific erratas on an IBM
961 * hot-plug docking bridge used on the Thinkpad 600 Series and on Intel's
962 * ICH PCI Bridges.
963 */
964 case PCI_HACK_FIXUP_BEFORE_UPDATE:
965
966 /* There should always be a PDO extension passed in */
967 ASSERT(PdoExtension);
968
969 /* Is this an IBM 20H2999 PCI Docking Bridge, used on Thinkpads? */
970 if ((PdoExtension->VendorId == 0x1014) &&
971 (PdoExtension->DeviceId == 0x95))
972 {
973 /* Read the current command */
974 PciReadDeviceConfig(PdoExtension,
975 &Command,
976 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
977 sizeof(USHORT));
978
979 /* Turn off the decodes */
980 PciDecodeEnable(PdoExtension, FALSE, &Command);
981
982 /* Apply the required IBM workaround */
983 PciReadDeviceConfig(PdoExtension, &RegValue, 0xE0, sizeof(UCHAR));
984 RegValue &= ~2;
985 RegValue |= 1;
986 PciWriteDeviceConfig(PdoExtension, &RegValue, 0xE0, sizeof(UCHAR));
987
988 /* Restore the command to its original value */
989 PciWriteDeviceConfig(PdoExtension,
990 &Command,
991 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
992 sizeof(USHORT));
993
994 }
995
996 /*
997 * Check for Intel ICH PCI-to-PCI (i82801) bridges (used on the i810,
998 * i820, i840, i845 Chipsets) that have subtractive decode enabled,
999 * and whose hack flags do not specifiy that this support is broken.
1000 */
1001 if ((PdoExtension->HeaderType == PCI_BRIDGE_TYPE) &&
1002 (PdoExtension->Dependent.type1.SubtractiveDecode) &&
1003 ((PdoExtension->VendorId == 0x8086) &&
1004 ((PdoExtension->DeviceId == 0x2418) ||
1005 (PdoExtension->DeviceId == 0x2428) ||
1006 (PdoExtension->DeviceId == 0x244E) ||
1007 (PdoExtension->DeviceId == 0x2448))) &&
1008 !(PdoExtension->HackFlags & PCI_HACK_BROKEN_SUBTRACTIVE_DECODE))
1009 {
1010 /*
1011 * The positive decode window shouldn't be used, these values are
1012 * normally all read-only or initialized to 0 by the BIOS, but
1013 * it appears Intel doesn't do this, so the PCI Bus Driver will
1014 * do it in software instead. Note that this is used to prevent
1015 * certain non-compliant PCI devices from breaking down due to the
1016 * fact that these ICH bridges have a known "quirk" (which Intel
1017 * documents as a known "erratum", although it's not not really
1018 * an ICH bug since the PCI specification does allow for it) in
1019 * that they will sometimes send non-zero addresses during special
1020 * cycles (ie: non-zero data during the address phase). These
1021 * broken PCI cards will mistakenly attempt to claim the special
1022 * cycle and corrupt their I/O and RAM ranges. Again, in Intel's
1023 * defense, the PCI specification only requires stable data, not
1024 * necessarily zero data, during the address phase.
1025 */
1026 PciData->u.type1.MemoryBase = 0xFFFF;
1027 PciData->u.type1.PrefetchBase = 0xFFFF;
1028 PciData->u.type1.IOBase = 0xFF;
1029 PciData->u.type1.IOLimit = 0;
1030 PciData->u.type1.MemoryLimit = 0;
1031 PciData->u.type1.PrefetchLimit = 0;
1032 PciData->u.type1.PrefetchBaseUpper32 = 0;
1033 PciData->u.type1.PrefetchLimitUpper32 = 0;
1034 PciData->u.type1.IOBaseUpper16 = 0;
1035 PciData->u.type1.IOLimitUpper16 = 0;
1036 }
1037 break;
1038
1039 default:
1040 return;
1041 }
1042
1043 /* Finally, also check if this is this a CardBUS device? */
1044 if (PCI_CONFIGURATION_TYPE(PciData) == PCI_CARDBUS_BRIDGE_TYPE)
1045 {
1046 /*
1047 * At offset 44h the LegacyBaseAddress is stored, which is cleared by
1048 * ACPI-aware versions of Windows, to disable legacy-mode I/O access to
1049 * CardBus controllers. For more information, see "Supporting CardBus
1050 * Controllers under ACPI" in the "CardBus Controllers and Windows"
1051 * Whitepaper on WHDC.
1052 */
1053 LegacyBaseAddress = 0;
1054 PciWriteDeviceConfig(PdoExtension,
1055 &LegacyBaseAddress,
1056 sizeof(PCI_COMMON_HEADER) + sizeof(ULONG),
1057 sizeof(ULONG));
1058 }
1059 }
1060
1061 BOOLEAN
1062 NTAPI
1063 PcipIsSameDevice(IN PPCI_PDO_EXTENSION DeviceExtension,
1064 IN PPCI_COMMON_HEADER PciData)
1065 {
1066 BOOLEAN IdMatch, RevMatch, SubsysMatch;
1067 ULONGLONG HackFlags = DeviceExtension->HackFlags;
1068
1069 /* Check if the IDs match */
1070 IdMatch = (PciData->VendorID == DeviceExtension->VendorId) &&
1071 (PciData->DeviceID == DeviceExtension->DeviceId);
1072 if (!IdMatch) return FALSE;
1073
1074 /* If the device has a valid revision, check if it matches */
1075 RevMatch = (HackFlags & PCI_HACK_NO_REVISION_AFTER_D3) ||
1076 (PciData->RevisionID == DeviceExtension->RevisionId);
1077 if (!RevMatch) return FALSE;
1078
1079 /* For multifunction devices, this is enough to assume they're the same */
1080 if (PCI_MULTIFUNCTION_DEVICE(PciData)) return TRUE;
1081
1082 /* For bridge devices, there's also nothing else that can be checked */
1083 if (DeviceExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) return TRUE;
1084
1085 /* Devices, on the other hand, have subsystem data that can be compared */
1086 SubsysMatch = (HackFlags & (PCI_HACK_NO_SUBSYSTEM |
1087 PCI_HACK_NO_SUBSYSTEM_AFTER_D3)) ||
1088 ((DeviceExtension->SubsystemVendorId ==
1089 PciData->u.type0.SubVendorID) &&
1090 (DeviceExtension->SubsystemId ==
1091 PciData->u.type0.SubSystemID));
1092 return SubsysMatch;
1093 }
1094
1095 BOOLEAN
1096 NTAPI
1097 PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData,
1098 IN PCI_SLOT_NUMBER Slot,
1099 IN UCHAR OperationType,
1100 IN ULONGLONG HackFlags)
1101 {
1102 do
1103 {
1104 /* Check if this is device enumeration */
1105 if (OperationType == PCI_SKIP_DEVICE_ENUMERATION)
1106 {
1107 /* Check if there's a hackflag saying not to enumerate this device */
1108 if (HackFlags & PCI_HACK_NO_ENUM_AT_ALL) break;
1109
1110 /* Check if this is the high end of a double decker device */
1111 if ((HackFlags & PCI_HACK_DOUBLE_DECKER) &&
1112 (Slot.u.bits.DeviceNumber >= 16))
1113 {
1114 /* It belongs to the same device, so skip it */
1115 DPRINT1(" Device (Ven %04x Dev %04x (d=0x%x, f=0x%x)) is a ghost.\n",
1116 PciData->VendorID,
1117 PciData->DeviceID,
1118 Slot.u.bits.DeviceNumber,
1119 Slot.u.bits.FunctionNumber);
1120 break;
1121 }
1122 }
1123 else if (OperationType == PCI_SKIP_RESOURCE_ENUMERATION)
1124 {
1125 /* Resource enumeration, check for a hackflag saying not to do it */
1126 if (HackFlags & PCI_HACK_ENUM_NO_RESOURCE) break;
1127 }
1128 else
1129 {
1130 /* Logic error in the driver */
1131 ASSERTMSG("PCI Skip Function - Operation type unknown.", FALSE);
1132 }
1133
1134 /* Check for legacy bridges during resource enumeration */
1135 if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
1136 (PciData->SubClass <= PCI_SUBCLASS_BR_MCA) &&
1137 (OperationType == PCI_SKIP_RESOURCE_ENUMERATION))
1138 {
1139 /* Their resources are not enumerated, only PCI and Cardbus/PCMCIA */
1140 break;
1141 }
1142 else if (PciData->BaseClass == PCI_CLASS_NOT_DEFINED)
1143 {
1144 /* Undefined base class (usually a PCI BIOS/ROM bug) */
1145 DPRINT1(" Vendor %04x, Device %04x has class code of PCI_CLASS_NOT_DEFINED\n",
1146 PciData->VendorID,
1147 PciData->DeviceID);
1148
1149 /*
1150 * The Alder has an Intel Extended Express System Support Controller
1151 * which presents apparently spurious BARs. When the PCI resource
1152 * code tries to reassign these BARs, the second IO-APIC gets
1153 * disabled (with disastrous consequences). The first BAR is the
1154 * actual IO-APIC, the remaining five bars seem to be spurious
1155 * resources, so ignore this device completely.
1156 */
1157 if ((PciData->VendorID == 0x8086) && (PciData->DeviceID == 8)) break;
1158 }
1159
1160 /* Other normal PCI cards and bridges are enumerated */
1161 if (PCI_CONFIGURATION_TYPE(PciData) <= PCI_CARDBUS_BRIDGE_TYPE) return FALSE;
1162 } while (FALSE);
1163
1164 /* Hit one of the known bugs/hackflags, or this is a new kind of PCI unit */
1165 DPRINT1(" Device skipped (not enumerated).\n");
1166 return TRUE;
1167 }
1168
1169 VOID
1170 NTAPI
1171 PciGetEnhancedCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,
1172 IN PPCI_COMMON_HEADER PciData)
1173 {
1174 ULONG HeaderType, CapPtr, TargetAgpCapabilityId;
1175 DEVICE_POWER_STATE WakeLevel;
1176 PCI_CAPABILITIES_HEADER AgpCapability;
1177 PCI_PM_CAPABILITY PowerCapabilities;
1178 PAGED_CODE();
1179
1180 /* Assume no known wake level */
1181 PdoExtension->PowerState.DeviceWakeLevel = PowerDeviceUnspecified;
1182
1183 /* Make sure the device has capabilities */
1184 if (!(PciData->Status & PCI_STATUS_CAPABILITIES_LIST))
1185 {
1186 /* If it doesn't, there will be no power management */
1187 PdoExtension->CapabilitiesPtr = 0;
1188 PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
1189 }
1190 else
1191 {
1192 /* There's capabilities, need to figure out where to get the offset */
1193 HeaderType = PCI_CONFIGURATION_TYPE(PciData);
1194 if (HeaderType == PCI_CARDBUS_BRIDGE_TYPE)
1195 {
1196 /* Use the bridge's header */
1197 CapPtr = PciData->u.type2.CapabilitiesPtr;
1198 }
1199 else
1200 {
1201 /* Use the device header */
1202 ASSERT(HeaderType <= PCI_CARDBUS_BRIDGE_TYPE);
1203 CapPtr = PciData->u.type0.CapabilitiesPtr;
1204 }
1205
1206 /* Skip garbage capabilities pointer */
1207 if (((CapPtr & 0x3) != 0) || (CapPtr < PCI_COMMON_HDR_LENGTH))
1208 {
1209 /* Report no extended capabilities */
1210 PdoExtension->CapabilitiesPtr = 0;
1211 PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
1212 }
1213 else
1214 {
1215 DPRINT1("Device has capabilities at: %lx\n", CapPtr);
1216 PdoExtension->CapabilitiesPtr = CapPtr;
1217
1218 /* Check for PCI-to-PCI Bridges and AGP bridges */
1219 if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
1220 ((PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) ||
1221 (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI)))
1222 {
1223 /* Query either the raw AGP capabilitity, or the Target AGP one */
1224 TargetAgpCapabilityId = (PdoExtension->SubClass ==
1225 PCI_SUBCLASS_BR_PCI_TO_PCI) ?
1226 PCI_CAPABILITY_ID_AGP_TARGET :
1227 PCI_CAPABILITY_ID_AGP;
1228 if (PciReadDeviceCapability(PdoExtension,
1229 PdoExtension->CapabilitiesPtr,
1230 TargetAgpCapabilityId,
1231 &AgpCapability,
1232 sizeof(PCI_CAPABILITIES_HEADER)))
1233 {
1234 /* AGP target ID was found, store it */
1235 DPRINT1("AGP ID: %lx\n", TargetAgpCapabilityId);
1236 PdoExtension->TargetAgpCapabilityId = TargetAgpCapabilityId;
1237 }
1238 }
1239
1240 /* Check for devices that are known not to have proper power management */
1241 if (!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS))
1242 {
1243 /* Query if this device supports power management */
1244 if (!PciReadDeviceCapability(PdoExtension,
1245 PdoExtension->CapabilitiesPtr,
1246 PCI_CAPABILITY_ID_POWER_MANAGEMENT,
1247 &PowerCapabilities.Header,
1248 sizeof(PCI_PM_CAPABILITY)))
1249 {
1250 /* No power management, so act as if it had the hackflag set */
1251 DPRINT1("No PM caps, disabling PM\n");
1252 PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
1253 }
1254 else
1255 {
1256 /* Otherwise, pick the highest wake level that is supported */
1257 WakeLevel = PowerDeviceUnspecified;
1258 if (PowerCapabilities.PMC.Capabilities.Support.PMED0)
1259 WakeLevel = PowerDeviceD0;
1260 if (PowerCapabilities.PMC.Capabilities.Support.PMED1)
1261 WakeLevel = PowerDeviceD1;
1262 if (PowerCapabilities.PMC.Capabilities.Support.PMED2)
1263 WakeLevel = PowerDeviceD2;
1264 if (PowerCapabilities.PMC.Capabilities.Support.PMED3Hot)
1265 WakeLevel = PowerDeviceD3;
1266 if (PowerCapabilities.PMC.Capabilities.Support.PMED3Cold)
1267 WakeLevel = PowerDeviceD3;
1268 PdoExtension->PowerState.DeviceWakeLevel = WakeLevel;
1269
1270 /* Convert the PCI power state to the NT power state */
1271 PdoExtension->PowerState.CurrentDeviceState =
1272 PowerCapabilities.PMCSR.ControlStatus.PowerState + 1;
1273
1274 /* Save all the power capabilities */
1275 PdoExtension->PowerCapabilities = PowerCapabilities.PMC.Capabilities;
1276 DPRINT1("PM Caps Found! Wake Level: %d Power State: %d\n",
1277 WakeLevel, PdoExtension->PowerState.CurrentDeviceState);
1278 }
1279 }
1280 }
1281 }
1282
1283 /* At the very end of all this, does this device not have power management? */
1284 if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)
1285 {
1286 /* Then guess the current state based on whether the decodes are on */
1287 PdoExtension->PowerState.CurrentDeviceState =
1288 PciData->Command & (PCI_ENABLE_IO_SPACE |
1289 PCI_ENABLE_MEMORY_SPACE |
1290 PCI_ENABLE_BUS_MASTER) ?
1291 PowerDeviceD0: PowerDeviceD3;
1292 DPRINT1("PM is off, so assumed device is: %d based on enables\n",
1293 PdoExtension->PowerState.CurrentDeviceState);
1294 }
1295 }
1296
1297 VOID
1298 NTAPI
1299 PciWriteLimitsAndRestoreCurrent(IN PVOID Reserved,
1300 IN PVOID Context2)
1301 {
1302 PPCI_CONFIGURATOR_CONTEXT Context = Context2;
1303 PPCI_COMMON_HEADER PciData, Current;
1304 PPCI_PDO_EXTENSION PdoExtension;
1305
1306 UNREFERENCED_PARAMETER(Reserved);
1307
1308 /* Grab all parameters from the context */
1309 PdoExtension = Context->PdoExtension;
1310 Current = Context->Current;
1311 PciData = Context->PciData;
1312
1313 /* Write the limit discovery header */
1314 PciWriteDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH);
1315
1316 /* Now read what the device indicated the limits are */
1317 PciReadDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH);
1318
1319 /* Then write back the original configuration header */
1320 PciWriteDeviceConfig(PdoExtension, Current, 0, PCI_COMMON_HDR_LENGTH);
1321
1322 /* Copy back the original command that was saved in the context */
1323 Current->Command = Context->Command;
1324 if (Context->Command)
1325 {
1326 /* Program it back into the device */
1327 PciWriteDeviceConfig(PdoExtension,
1328 &Context->Command,
1329 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
1330 sizeof(USHORT));
1331 }
1332
1333 /* Copy back the original status that was saved as well */
1334 Current->Status = Context->Status;
1335
1336 /* Call the configurator to restore any other data that might've changed */
1337 Context->Configurator->RestoreCurrent(Context);
1338 }
1339
1340 NTSTATUS
1341 NTAPI
1342 PcipGetFunctionLimits(IN PPCI_CONFIGURATOR_CONTEXT Context)
1343 {
1344 PPCI_CONFIGURATOR Configurator;
1345 PPCI_COMMON_HEADER PciData, Current;
1346 PPCI_PDO_EXTENSION PdoExtension;
1347 PCI_IPI_CONTEXT IpiContext;
1348 PIO_RESOURCE_DESCRIPTOR IoDescriptor;
1349 ULONG Offset;
1350 PAGED_CODE();
1351
1352 /* Grab all parameters from the context */
1353 PdoExtension = Context->PdoExtension;
1354 Current = Context->Current;
1355 PciData = Context->PciData;
1356
1357 /* Save the current PCI Command and Status word */
1358 Context->Status = Current->Status;
1359 Context->Command = Current->Command;
1360
1361 /* Now that they're saved, clear the status, and disable all decodes */
1362 Current->Status = 0;
1363 Current->Command &= ~(PCI_ENABLE_IO_SPACE |
1364 PCI_ENABLE_MEMORY_SPACE |
1365 PCI_ENABLE_BUS_MASTER);
1366
1367 /* Make a copy of the current PCI configuration header (with decodes off) */
1368 RtlCopyMemory(PciData, Current, PCI_COMMON_HDR_LENGTH);
1369
1370 /* Locate the correct resource configurator for this type of device */
1371 Configurator = &PciConfigurators[PdoExtension->HeaderType];
1372 Context->Configurator = Configurator;
1373
1374 /* Initialize it, which will typically setup the BARs for limit discovery */
1375 Configurator->Initialize(Context);
1376
1377 /* Check for critical devices and PCI Debugging devices */
1378 if ((PdoExtension->HackFlags & PCI_HACK_CRITICAL_DEVICE) ||
1379 (PdoExtension->OnDebugPath))
1380 {
1381 /* Specifically check for a PCI Debugging device */
1382 if (PdoExtension->OnDebugPath)
1383 {
1384 /* Was it enabled for bus mastering? */
1385 if (Context->Command & PCI_ENABLE_BUS_MASTER)
1386 {
1387 /* This decode needs to be re-enabled so debugging can work */
1388 PciData->Command |= PCI_ENABLE_BUS_MASTER;
1389 Current->Command |= PCI_ENABLE_BUS_MASTER;
1390 }
1391
1392 /* Disable the debugger while the discovery is happening */
1393 KdDisableDebugger();
1394 }
1395
1396 /* For these devices, an IPI must be sent to force high-IRQL discovery */
1397 IpiContext.Barrier = 1;
1398 IpiContext.RunCount = 1;
1399 IpiContext.DeviceExtension = PdoExtension;
1400 IpiContext.Function = PciWriteLimitsAndRestoreCurrent;
1401 IpiContext.Context = Context;
1402 KeIpiGenericCall(PciExecuteCriticalSystemRoutine, (ULONG_PTR)&IpiContext);
1403
1404 /* Re-enable the debugger if this was a PCI Debugging Device */
1405 if (PdoExtension->OnDebugPath) KdEnableDebugger();
1406 }
1407 else
1408 {
1409 /* Otherwise, it's safe to do this in-line at low IRQL */
1410 PciWriteLimitsAndRestoreCurrent(PdoExtension, Context);
1411 }
1412
1413 /*
1414 * Check if it's valid to compare the headers to see if limit discovery mode
1415 * has properly exited (the expected case is that the PCI header would now
1416 * be equal to what it was before). In some cases, it is known that this will
1417 * fail, because during PciApplyHacks (among other places), software hacks
1418 * had to be applied to the header, which the hardware-side will not see, and
1419 * thus the headers would appear "different".
1420 */
1421 if (!PdoExtension->ExpectedWritebackFailure)
1422 {
1423 /* Read the current PCI header now, after discovery has completed */
1424 PciReadDeviceConfig(PdoExtension, PciData + 1, 0, PCI_COMMON_HDR_LENGTH);
1425
1426 /* Check if the current header at entry, is equal to the header now */
1427 Offset = RtlCompareMemory(PciData + 1, Current, PCI_COMMON_HDR_LENGTH);
1428 if (Offset != PCI_COMMON_HDR_LENGTH)
1429 {
1430 /* It's not, which means configuration somehow changed, dump this */
1431 DPRINT1("PCI - CFG space write verify failed at offset 0x%x\n", Offset);
1432 PciDebugDumpCommonConfig(PciData + 1);
1433 DPRINT1("----------\n");
1434 PciDebugDumpCommonConfig(Current);
1435 }
1436 }
1437
1438 /* This PDO should not already have resources, since this is only done once */
1439 ASSERT(PdoExtension->Resources == NULL);
1440
1441 /* Allocate the structure that will hold the discovered resources and limits */
1442 PdoExtension->Resources = ExAllocatePoolWithTag(NonPagedPool,
1443 sizeof(PCI_FUNCTION_RESOURCES),
1444 'BicP');
1445 if (!PdoExtension->Resources) return STATUS_INSUFFICIENT_RESOURCES;
1446
1447 /* Clear it out for now */
1448 RtlZeroMemory(PdoExtension->Resources, sizeof(PCI_FUNCTION_RESOURCES));
1449
1450 /* Now call the configurator, which will first store the limits... */
1451 Configurator->SaveLimits(Context);
1452
1453 /* ...and then store the current resources being used */
1454 Configurator->SaveCurrentSettings(Context);
1455
1456 /* Loop all the limit descriptors backwards */
1457 IoDescriptor = &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1];
1458 while (TRUE)
1459 {
1460 /* Keep going until a non-null descriptor is found */
1461 IoDescriptor--;
1462 if (IoDescriptor->Type != CmResourceTypeNull) break;
1463
1464 /* This is a null descriptor, is it the last one? */
1465 if (IoDescriptor == &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1])
1466 {
1467 /* This means the descriptor is NULL, which means discovery failed */
1468 DPRINT1("PCI Resources fail!\n");
1469
1470 /* No resources will be assigned for the device */
1471 ExFreePoolWithTag(PdoExtension->Resources, 0);
1472 PdoExtension->Resources = NULL;
1473 break;
1474 }
1475 }
1476
1477 /* Return success here, even if the device has no assigned resources */
1478 return STATUS_SUCCESS;
1479 }
1480
1481 NTSTATUS
1482 NTAPI
1483 PciGetFunctionLimits(IN PPCI_PDO_EXTENSION PdoExtension,
1484 IN PPCI_COMMON_HEADER Current,
1485 IN ULONGLONG HackFlags)
1486 {
1487 NTSTATUS Status;
1488 PPCI_COMMON_HEADER PciData;
1489 PCI_CONFIGURATOR_CONTEXT Context;
1490 PAGED_CODE();
1491
1492 /* Do the hackflags indicate this device should be skipped? */
1493 if (PciSkipThisFunction(Current,
1494 PdoExtension->Slot,
1495 PCI_SKIP_RESOURCE_ENUMERATION,
1496 HackFlags))
1497 {
1498 /* Do not process its resources */
1499 return STATUS_SUCCESS;
1500 }
1501
1502 /* Allocate a buffer to hold two PCI configuration headers */
1503 PciData = ExAllocatePoolWithTag(0, 2 * PCI_COMMON_HDR_LENGTH, 'BicP');
1504 if (!PciData) return STATUS_INSUFFICIENT_RESOURCES;
1505
1506 /* Set up the context for the resource enumeration, and do it */
1507 Context.Current = Current;
1508 Context.PciData = PciData;
1509 Context.PdoExtension = PdoExtension;
1510 Status = PcipGetFunctionLimits(&Context);
1511
1512 /* Enumeration is completed, free the PCI headers and return the status */
1513 ExFreePoolWithTag(PciData, 0);
1514 return Status;
1515 }
1516
1517 VOID
1518 NTAPI
1519 PciProcessBus(IN PPCI_FDO_EXTENSION DeviceExtension)
1520 {
1521 PPCI_PDO_EXTENSION PdoExtension;
1522 PDEVICE_OBJECT PhysicalDeviceObject;
1523 PAGED_CODE();
1524
1525 /* Get the PDO Extension */
1526 PhysicalDeviceObject = DeviceExtension->PhysicalDeviceObject;
1527 PdoExtension = (PPCI_PDO_EXTENSION)PhysicalDeviceObject->DeviceExtension;
1528
1529 /* Cheeck if this is the root bus */
1530 if (!PCI_IS_ROOT_FDO(DeviceExtension))
1531 {
1532 /* Not really handling this year */
1533 UNIMPLEMENTED_DBGBREAK();
1534
1535 /* Check for PCI bridges with the ISA bit set, or required */
1536 if ((PdoExtension) &&
1537 (PciClassifyDeviceType(PdoExtension) == PciTypePciBridge) &&
1538 ((PdoExtension->Dependent.type1.IsaBitRequired) ||
1539 (PdoExtension->Dependent.type1.IsaBitSet)))
1540 {
1541 /* We'll need to do some legacy support */
1542 UNIMPLEMENTED_DBGBREAK();
1543 }
1544 }
1545 else
1546 {
1547 /* Scan all of the root bus' children bridges */
1548 for (PdoExtension = DeviceExtension->ChildBridgePdoList;
1549 PdoExtension;
1550 PdoExtension = PdoExtension->NextBridge)
1551 {
1552 /* Find any that have the VGA decode bit on */
1553 if (PdoExtension->Dependent.type1.VgaBitSet)
1554 {
1555 /* Again, some more legacy support we'll have to do */
1556 UNIMPLEMENTED_DBGBREAK();
1557 }
1558 }
1559 }
1560
1561 /* Check for ACPI systems where the OS assigns bus numbers */
1562 if (PciAssignBusNumbers)
1563 {
1564 /* Not yet supported */
1565 UNIMPLEMENTED_DBGBREAK();
1566 }
1567 }
1568
1569 NTSTATUS
1570 NTAPI
1571 PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension)
1572 {
1573 ULONG MaxDevice = PCI_MAX_DEVICES;
1574 BOOLEAN ProcessFlag = FALSE;
1575 ULONG i, j, k, Size;
1576 USHORT CapOffset, TempOffset;
1577 LONGLONG HackFlags;
1578 PDEVICE_OBJECT DeviceObject;
1579 UCHAR Buffer[PCI_COMMON_HDR_LENGTH];
1580 UCHAR BiosBuffer[PCI_COMMON_HDR_LENGTH];
1581 PPCI_COMMON_HEADER PciData = (PVOID)Buffer;
1582 PPCI_COMMON_HEADER BiosData = (PVOID)BiosBuffer;
1583 PCI_SLOT_NUMBER PciSlot;
1584 PCHAR Name;
1585 NTSTATUS Status;
1586 PPCI_PDO_EXTENSION PdoExtension, NewExtension;
1587 PPCI_PDO_EXTENSION* BridgeExtension;
1588 PWCHAR DescriptionText;
1589 USHORT SubVendorId, SubSystemId;
1590 PCI_CAPABILITIES_HEADER CapHeader, PcixCapHeader;
1591 UCHAR SecondaryBus;
1592 DPRINT1("PCI Scan Bus: FDO Extension @ 0x%p, Base Bus = 0x%x\n",
1593 DeviceExtension, DeviceExtension->BaseBus);
1594
1595 /* Is this the root FDO? */
1596 if (!PCI_IS_ROOT_FDO(DeviceExtension))
1597 {
1598 /* Get the PDO for the child bus */
1599 PdoExtension = DeviceExtension->PhysicalDeviceObject->DeviceExtension;
1600 ASSERT_PDO(PdoExtension);
1601
1602 /* Check for hack which only allows bus to have one child device */
1603 if (PdoExtension->HackFlags & PCI_HACK_ONE_CHILD) MaxDevice = 1;
1604
1605 /* Check if the secondary bus number has changed */
1606 PciReadDeviceConfig(PdoExtension,
1607 &SecondaryBus,
1608 FIELD_OFFSET(PCI_COMMON_HEADER, u.type1.SecondaryBus),
1609 sizeof(UCHAR));
1610 if (SecondaryBus != PdoExtension->Dependent.type1.SecondaryBus)
1611 {
1612 UNIMPLEMENTED_DBGBREAK("PCI: Bus numbers have been changed! Restoring originals.\n");
1613 }
1614 }
1615
1616 /* Loop every device on the bus */
1617 PciSlot.u.bits.Reserved = 0;
1618 i = DeviceExtension->BaseBus;
1619 for (j = 0; j < MaxDevice; j++)
1620 {
1621 /* Loop every function of each device */
1622 PciSlot.u.bits.DeviceNumber = j;
1623 for (k = 0; k < PCI_MAX_FUNCTION; k++)
1624 {
1625 /* Build the final slot structure */
1626 PciSlot.u.bits.FunctionNumber = k;
1627
1628 /* Read the vendor for this slot */
1629 PciReadSlotConfig(DeviceExtension,
1630 PciSlot,
1631 PciData,
1632 0,
1633 sizeof(USHORT));
1634
1635 /* Skip invalid device */
1636 if (PciData->VendorID == PCI_INVALID_VENDORID) continue;
1637
1638 /* Now read the whole header */
1639 PciReadSlotConfig(DeviceExtension,
1640 PciSlot,
1641 &PciData->DeviceID,
1642 sizeof(USHORT),
1643 PCI_COMMON_HDR_LENGTH - sizeof(USHORT));
1644
1645 /* Apply any hacks before even analyzing the configuration header */
1646 PciApplyHacks(DeviceExtension,
1647 PciData,
1648 PciSlot,
1649 PCI_HACK_FIXUP_BEFORE_CONFIGURATION,
1650 NULL);
1651
1652 /* Dump device that was found */
1653 DPRINT1("Scan Found Device 0x%x (b=0x%x, d=0x%x, f=0x%x)\n",
1654 PciSlot.u.AsULONG,
1655 i,
1656 j,
1657 k);
1658
1659 /* Dump the device's header */
1660 PciDebugDumpCommonConfig(PciData);
1661
1662 /* Find description for this device for the debugger's sake */
1663 DescriptionText = PciGetDeviceDescriptionMessage(PciData->BaseClass,
1664 PciData->SubClass);
1665 DPRINT1("Device Description \"%S\".\n",
1666 DescriptionText ? DescriptionText : L"(NULL)");
1667 if (DescriptionText) ExFreePoolWithTag(DescriptionText, 0);
1668
1669 /* Check if there is an ACPI Watchdog Table */
1670 if (WdTable)
1671 {
1672 /* Check if this PCI device is the ACPI Watchdog Device... */
1673 UNIMPLEMENTED_DBGBREAK();
1674 }
1675
1676 /* Check for non-simple devices */
1677 if ((PCI_MULTIFUNCTION_DEVICE(PciData)) ||
1678 (PciData->BaseClass == PCI_CLASS_BRIDGE_DEV))
1679 {
1680 /* No subsystem data defined for these kinds of bridges */
1681 SubVendorId = 0;
1682 SubSystemId = 0;
1683 }
1684 else
1685 {
1686 /* Read the subsystem information from the PCI header */
1687 SubVendorId = PciData->u.type0.SubVendorID;
1688 SubSystemId = PciData->u.type0.SubSystemID;
1689 }
1690
1691 /* Get any hack flags for this device */
1692 HackFlags = PciGetHackFlags(PciData->VendorID,
1693 PciData->DeviceID,
1694 SubVendorId,
1695 SubSystemId,
1696 PciData->RevisionID);
1697
1698 /* Check if this device is considered critical by the OS */
1699 if (PciIsCriticalDeviceClass(PciData->BaseClass, PciData->SubClass))
1700 {
1701 /* Check if normally the decodes would be disabled */
1702 if (!(HackFlags & PCI_HACK_DONT_DISABLE_DECODES))
1703 {
1704 /* Because this device is critical, don't disable them */
1705 DPRINT1("Not allowing PM Because device is critical\n");
1706 HackFlags |= PCI_HACK_CRITICAL_DEVICE;
1707 }
1708 }
1709
1710 /* PCI bridges with a VGA card are also considered critical */
1711 if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
1712 (PciData->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) &&
1713 (PciData->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA) &&
1714 !(HackFlags & PCI_HACK_DONT_DISABLE_DECODES))
1715 {
1716 /* Do not disable their decodes either */
1717 DPRINT1("Not allowing PM because device is VGA\n");
1718 HackFlags |= PCI_HACK_CRITICAL_DEVICE;
1719 }
1720
1721 /* Check if the device should be skipped for whatever reason */
1722 if (PciSkipThisFunction(PciData,
1723 PciSlot,
1724 PCI_SKIP_DEVICE_ENUMERATION,
1725 HackFlags))
1726 {
1727 /* Skip this device */
1728 continue;
1729 }
1730
1731 /* Check if a PDO has already been created for this device */
1732 PdoExtension = PciFindPdoByFunction(DeviceExtension,
1733 PciSlot.u.AsULONG,
1734 PciData);
1735 if (PdoExtension)
1736 {
1737 /* Rescan scenarios are not yet implemented */
1738 UNIMPLEMENTED_DBGBREAK();
1739 }
1740
1741 /* Bus processing will need to happen */
1742 ProcessFlag = TRUE;
1743
1744 /* Create the PDO for this device */
1745 Status = PciPdoCreate(DeviceExtension, PciSlot, &DeviceObject);
1746 ASSERT(NT_SUCCESS(Status));
1747 NewExtension = (PPCI_PDO_EXTENSION)DeviceObject->DeviceExtension;
1748
1749 /* Check for broken devices with wrong/no class codes */
1750 if (HackFlags & PCI_HACK_FAKE_CLASS_CODE)
1751 {
1752 /* Setup a default one */
1753 PciData->BaseClass = PCI_CLASS_BASE_SYSTEM_DEV;
1754 PciData->SubClass = PCI_SUBCLASS_SYS_OTHER;
1755
1756 /* Device will behave erratically when reading back data */
1757 NewExtension->ExpectedWritebackFailure = TRUE;
1758 }
1759
1760 /* Clone all the information from the header */
1761 NewExtension->VendorId = PciData->VendorID;
1762 NewExtension->DeviceId = PciData->DeviceID;
1763 NewExtension->RevisionId = PciData->RevisionID;
1764 NewExtension->ProgIf = PciData->ProgIf;
1765 NewExtension->SubClass = PciData->SubClass;
1766 NewExtension->BaseClass = PciData->BaseClass;
1767 NewExtension->HeaderType = PCI_CONFIGURATION_TYPE(PciData);
1768
1769 /* Check for modern bridge types, which are managed by the driver */
1770 if ((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
1771 ((NewExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) ||
1772 (NewExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS)))
1773 {
1774 /* Acquire this device's lock */
1775 KeEnterCriticalRegion();
1776 KeWaitForSingleObject(&DeviceExtension->ChildListLock,
1777 Executive,
1778 KernelMode,
1779 FALSE,
1780 NULL);
1781
1782 /* Scan the bridge list until the first free entry */
1783 for (BridgeExtension = &DeviceExtension->ChildBridgePdoList;
1784 *BridgeExtension;
1785 BridgeExtension = &(*BridgeExtension)->NextBridge);
1786
1787 /* Add this PDO as a bridge */
1788 *BridgeExtension = NewExtension;
1789 ASSERT(NewExtension->NextBridge == NULL);
1790
1791 /* Release this device's lock */
1792 KeSetEvent(&DeviceExtension->ChildListLock,
1793 IO_NO_INCREMENT,
1794 FALSE);
1795 KeLeaveCriticalRegion();
1796 }
1797
1798 /* Get the PCI BIOS configuration saved in the registry */
1799 Status = PciGetBiosConfig(NewExtension, BiosData);
1800 if (NT_SUCCESS(Status))
1801 {
1802 /* This path has not yet been fully tested by eVb */
1803 DPRINT1("Have BIOS configuration!\n");
1804 UNIMPLEMENTED;
1805
1806 /* Check if the PCI BIOS configuration has changed */
1807 if (!PcipIsSameDevice(NewExtension, BiosData))
1808 {
1809 /* This is considered failure, and new data will be saved */
1810 Status = STATUS_UNSUCCESSFUL;
1811 }
1812 else
1813 {
1814 /* Data is still correct, check for interrupt line change */
1815 if (BiosData->u.type0.InterruptLine !=
1816 PciData->u.type0.InterruptLine)
1817 {
1818 /* Update the current BIOS with the saved interrupt line */
1819 PciWriteDeviceConfig(NewExtension,
1820 &BiosData->u.type0.InterruptLine,
1821 FIELD_OFFSET(PCI_COMMON_HEADER,
1822 u.type0.InterruptLine),
1823 sizeof(UCHAR));
1824 }
1825
1826 /* Save the BIOS interrupt line and the initial command */
1827 NewExtension->RawInterruptLine = BiosData->u.type0.InterruptLine;
1828 NewExtension->InitialCommand = BiosData->Command;
1829 }
1830 }
1831
1832 /* Check if no saved data was present or if it was a mismatch */
1833 if (!NT_SUCCESS(Status))
1834 {
1835 /* Save the new data */
1836 Status = PciSaveBiosConfig(NewExtension, PciData);
1837 ASSERT(NT_SUCCESS(Status));
1838
1839 /* Save the interrupt line and command from the device */
1840 NewExtension->RawInterruptLine = PciData->u.type0.InterruptLine;
1841 NewExtension->InitialCommand = PciData->Command;
1842 }
1843
1844 /* Save original command from the device and hack flags */
1845 NewExtension->CommandEnables = PciData->Command;
1846 NewExtension->HackFlags = HackFlags;
1847
1848 /* Get power, AGP, and other capability data */
1849 PciGetEnhancedCapabilities(NewExtension, PciData);
1850
1851 /* Now configure the BARs */
1852 Status = PciGetFunctionLimits(NewExtension, PciData, HackFlags);
1853
1854 /* Power up the device */
1855 PciSetPowerManagedDevicePowerState(NewExtension, PowerDeviceD0, FALSE);
1856
1857 /* Apply any device hacks required for enumeration */
1858 PciApplyHacks(DeviceExtension,
1859 PciData,
1860 PciSlot,
1861 PCI_HACK_FIXUP_AFTER_CONFIGURATION,
1862 NewExtension);
1863
1864 /* Save interrupt pin */
1865 NewExtension->InterruptPin = PciData->u.type0.InterruptPin;
1866
1867 /*
1868 * Use either this device's actual IRQ line or, if it's connected on
1869 * a master bus whose IRQ line is actually connected to the host, use
1870 * the HAL to query the bus' IRQ line and store that as the adjusted
1871 * interrupt line instead
1872 */
1873 NewExtension->AdjustedInterruptLine = PciGetAdjustedInterruptLine(NewExtension);
1874
1875 /* Check if this device is used for PCI debugger cards */
1876 NewExtension->OnDebugPath = PciIsDeviceOnDebugPath(NewExtension);
1877
1878 /* Check for devices with invalid/bogus subsystem data */
1879 if (HackFlags & PCI_HACK_NO_SUBSYSTEM)
1880 {
1881 /* Set the subsystem information to zero instead */
1882 NewExtension->SubsystemVendorId = 0;
1883 NewExtension->SubsystemId = 0;
1884 }
1885
1886 /* Scan all capabilities */
1887 CapOffset = NewExtension->CapabilitiesPtr;
1888 while (CapOffset)
1889 {
1890 /* Read this header */
1891 TempOffset = PciReadDeviceCapability(NewExtension,
1892 CapOffset,
1893 0,
1894 &CapHeader,
1895 sizeof(PCI_CAPABILITIES_HEADER));
1896 if (TempOffset != CapOffset)
1897 {
1898 /* This is a strange issue that shouldn't happen normally */
1899 DPRINT1("PCI - Failed to read PCI capability at offset 0x%02x\n",
1900 CapOffset);
1901 ASSERT(TempOffset == CapOffset);
1902 }
1903
1904 /* Check for capabilities that this driver cares about */
1905 switch (CapHeader.CapabilityID)
1906 {
1907 /* Power management capability is heavily used by the bus */
1908 case PCI_CAPABILITY_ID_POWER_MANAGEMENT:
1909
1910 /* Dump the capability */
1911 Name = "POWER";
1912 Size = sizeof(PCI_PM_CAPABILITY);
1913 break;
1914
1915 /* AGP capability is required for AGP bus functionality */
1916 case PCI_CAPABILITY_ID_AGP:
1917
1918 /* Dump the capability */
1919 Name = "AGP";
1920 Size = sizeof(PCI_AGP_CAPABILITY);
1921 break;
1922
1923 /* This driver doesn't really use anything other than that */
1924 default:
1925
1926 /* Windows prints this, we could do a translation later */
1927 Name = "UNKNOWN CAPABILITY";
1928 Size = 0;
1929 break;
1930 }
1931
1932 /* Check if this is a capability that should be dumped */
1933 if (Size)
1934 {
1935 /* Read the whole capability data */
1936 TempOffset = PciReadDeviceCapability(NewExtension,
1937 CapOffset,
1938 CapHeader.CapabilityID,
1939 &CapHeader,
1940 Size);
1941
1942 if (TempOffset != CapOffset)
1943 {
1944 /* Again, a strange issue that shouldn't be seen */
1945 DPRINT1("- Failed to read capability data. ***\n");
1946 ASSERT(TempOffset == CapOffset);
1947 }
1948 }
1949
1950 /* Dump this capability */
1951 DPRINT1("CAP @%02x ID %02x (%s)\n",
1952 CapOffset, CapHeader.CapabilityID, Name);
1953 for (i = 0; i < Size; i += 2)
1954 DPRINT1(" %04x\n", *(PUSHORT)((ULONG_PTR)&CapHeader + i));
1955 DPRINT1("\n");
1956
1957 /* Check the next capability */
1958 CapOffset = CapHeader.Next;
1959 }
1960
1961 /* Check for IDE controllers */
1962 if ((NewExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) &&
1963 (NewExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR))
1964 {
1965 /* Do not allow them to power down completely */
1966 NewExtension->DisablePowerDown = TRUE;
1967 }
1968
1969 /*
1970 * Check if this is a legacy bridge. Note that the i82375 PCI/EISA
1971 * bridge that is present on certain NT Alpha machines appears as
1972 * non-classified so detect it manually by scanning for its VID/PID.
1973 */
1974 if (((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
1975 ((NewExtension->SubClass == PCI_SUBCLASS_BR_ISA) ||
1976 (NewExtension->SubClass == PCI_SUBCLASS_BR_EISA) ||
1977 (NewExtension->SubClass == PCI_SUBCLASS_BR_MCA))) ||
1978 ((NewExtension->VendorId == 0x8086) &&
1979 (NewExtension->DeviceId == 0x482)))
1980 {
1981 /* Do not allow these legacy bridges to be powered down */
1982 NewExtension->DisablePowerDown = TRUE;
1983 }
1984
1985 /* Check if the BIOS did not configure a cache line size */
1986 if (!PciData->CacheLineSize)
1987 {
1988 /* Check if the device is disabled */
1989 if (!(NewExtension->CommandEnables & (PCI_ENABLE_IO_SPACE |
1990 PCI_ENABLE_MEMORY_SPACE |
1991 PCI_ENABLE_BUS_MASTER)))
1992 {
1993 /* Check if this is a PCI-X device*/
1994 TempOffset = PciReadDeviceCapability(NewExtension,
1995 NewExtension->CapabilitiesPtr,
1996 PCI_CAPABILITY_ID_PCIX,
1997 &PcixCapHeader,
1998 sizeof(PCI_CAPABILITIES_HEADER));
1999
2000 /*
2001 * A device with default cache line size and latency timer
2002 * settings is considered to be unconfigured. Note that on
2003 * PCI-X, the reset value of the latency timer field in the
2004 * header is 64, not 0, hence why the check for PCI-X caps
2005 * was required, and the value used here below.
2006 */
2007 if (!(PciData->LatencyTimer) ||
2008 ((TempOffset) && (PciData->LatencyTimer == 64)))
2009 {
2010 /* Keep track of the fact that it needs configuration */
2011 DPRINT1("PCI - ScanBus, PDOx %p found unconfigured\n",
2012 NewExtension);
2013 NewExtension->NeedsHotPlugConfiguration = TRUE;
2014 }
2015 }
2016 }
2017
2018 /* Save latency and cache size information */
2019 NewExtension->SavedLatencyTimer = PciData->LatencyTimer;
2020 NewExtension->SavedCacheLineSize = PciData->CacheLineSize;
2021
2022 /* The PDO is now ready to go */
2023 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
2024 }
2025 }
2026
2027 /* Enumeration completed, do a final pass now that all devices are found */
2028 if (ProcessFlag) PciProcessBus(DeviceExtension);
2029 return STATUS_SUCCESS;
2030 }
2031
2032 NTSTATUS
2033 NTAPI
2034 PciQueryDeviceRelations(IN PPCI_FDO_EXTENSION DeviceExtension,
2035 IN OUT PDEVICE_RELATIONS *pDeviceRelations)
2036 {
2037 NTSTATUS Status;
2038 PPCI_PDO_EXTENSION PdoExtension;
2039 ULONG PdoCount = 0;
2040 PDEVICE_RELATIONS DeviceRelations, NewRelations;
2041 SIZE_T Size;
2042 PDEVICE_OBJECT DeviceObject, *ObjectArray;
2043 PAGED_CODE();
2044
2045 /* Make sure the FDO is started */
2046 ASSERT(DeviceExtension->DeviceState == PciStarted);
2047
2048 /* Synchronize while we enumerate the bus */
2049 Status = PciBeginStateTransition(DeviceExtension, PciSynchronizedOperation);
2050 if (!NT_SUCCESS(Status)) return Status;
2051
2052 /* Scan all children PDO */
2053 for (PdoExtension = DeviceExtension->ChildPdoList;
2054 PdoExtension;
2055 PdoExtension = PdoExtension->Next)
2056 {
2057 /* Invalidate them */
2058 PdoExtension->NotPresent = TRUE;
2059 }
2060
2061 /* Scan the PCI Bus */
2062 Status = PciScanBus(DeviceExtension);
2063 ASSERT(NT_SUCCESS(Status));
2064
2065 /* Enumerate all children PDO again */
2066 for (PdoExtension = DeviceExtension->ChildPdoList;
2067 PdoExtension;
2068 PdoExtension = PdoExtension->Next)
2069 {
2070 /* Check for PDOs that are still invalidated */
2071 if (PdoExtension->NotPresent)
2072 {
2073 /* This means this PDO existed before, but not anymore */
2074 PdoExtension->ReportedMissing = TRUE;
2075 DPRINT1("PCI - Old device (pdox) %p not found on rescan.\n",
2076 PdoExtension);
2077 }
2078 else
2079 {
2080 /* Increase count of detected PDOs */
2081 PdoCount++;
2082 }
2083 }
2084
2085 /* Read the current relations and add the newly discovered relations */
2086 DeviceRelations = *pDeviceRelations;
2087 Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
2088 PdoCount * sizeof(PDEVICE_OBJECT);
2089 if (DeviceRelations) Size += sizeof(PDEVICE_OBJECT) * DeviceRelations->Count;
2090
2091 /* Allocate the device relations */
2092 NewRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(0, Size, 'BicP');
2093 if (!NewRelations)
2094 {
2095 /* Out of space, cancel the operation */
2096 PciCancelStateTransition(DeviceExtension, PciSynchronizedOperation);
2097 return STATUS_INSUFFICIENT_RESOURCES;
2098 }
2099
2100 /* Check if there were any older relations */
2101 NewRelations->Count = 0;
2102 if (DeviceRelations)
2103 {
2104 /* Copy the old relations into the new buffer, then free the old one */
2105 RtlCopyMemory(NewRelations,
2106 DeviceRelations,
2107 FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
2108 DeviceRelations->Count * sizeof(PDEVICE_OBJECT));
2109 ExFreePoolWithTag(DeviceRelations, 0);
2110 }
2111
2112 /* Print out that we're ready to dump relations */
2113 DPRINT1("PCI QueryDeviceRelations/BusRelations FDOx %p (bus 0x%02x)\n",
2114 DeviceExtension,
2115 DeviceExtension->BaseBus);
2116
2117 /* Loop the current PDO children and the device relation object array */
2118 PdoExtension = DeviceExtension->ChildPdoList;
2119 ObjectArray = &NewRelations->Objects[NewRelations->Count];
2120 while (PdoExtension)
2121 {
2122 /* Dump this relation */
2123 DPRINT1(" QDR PDO %p (x %p)%s\n",
2124 PdoExtension->PhysicalDeviceObject,
2125 PdoExtension,
2126 PdoExtension->NotPresent ?
2127 "<Omitted, device flaged not present>" : "");
2128
2129 /* Is this PDO present? */
2130 if (!PdoExtension->NotPresent)
2131 {
2132 /* Reference it and add it to the array */
2133 DeviceObject = PdoExtension->PhysicalDeviceObject;
2134 ObReferenceObject(DeviceObject);
2135 *ObjectArray++ = DeviceObject;
2136 }
2137
2138 /* Go to the next PDO */
2139 PdoExtension = PdoExtension->Next;
2140 }
2141
2142 /* Terminate dumping the relations */
2143 DPRINT1(" QDR Total PDO count = %u (%u already in list)\n",
2144 NewRelations->Count + PdoCount,
2145 NewRelations->Count);
2146
2147 /* Return the final count and the new buffer */
2148 NewRelations->Count += PdoCount;
2149 *pDeviceRelations = NewRelations;
2150 return STATUS_SUCCESS;
2151 }
2152
2153 NTSTATUS
2154 NTAPI
2155 PciSetResources(IN PPCI_PDO_EXTENSION PdoExtension,
2156 IN BOOLEAN DoReset,
2157 IN BOOLEAN SomethingSomethingDarkSide)
2158 {
2159 PPCI_FDO_EXTENSION FdoExtension;
2160 UCHAR NewCacheLineSize, NewLatencyTimer;
2161 PCI_COMMON_HEADER PciData;
2162 BOOLEAN Native;
2163 PPCI_CONFIGURATOR Configurator;
2164
2165 UNREFERENCED_PARAMETER(SomethingSomethingDarkSide);
2166
2167 /* Get the FDO and read the configuration data */
2168 FdoExtension = PdoExtension->ParentFdoExtension;
2169 PciReadDeviceConfig(PdoExtension, &PciData, 0, PCI_COMMON_HDR_LENGTH);
2170
2171 /* Make sure this is still the same device */
2172 if (!PcipIsSameDevice(PdoExtension, &PciData))
2173 {
2174 /* Fail */
2175 ASSERTMSG("PCI Set resources - not same device", FALSE);
2176 return STATUS_DEVICE_DOES_NOT_EXIST;
2177 }
2178
2179 /* Nothing to set for a host bridge */
2180 if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
2181 (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST))
2182 {
2183 /* Fake success */
2184 return STATUS_SUCCESS;
2185 }
2186
2187 /* Check if an IDE controller is being reset */
2188 if ((DoReset) &&
2189 (PdoExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) &&
2190 (PdoExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR))
2191 {
2192 /* Turn off native mode */
2193 Native = PciConfigureIdeController(PdoExtension, &PciData, FALSE);
2194 ASSERT(Native == PdoExtension->IDEInNativeMode);
2195 }
2196
2197 /* Check for update of a hotplug device, or first configuration of one */
2198 if ((PdoExtension->NeedsHotPlugConfiguration) &&
2199 (FdoExtension->HotPlugParameters.Acquired))
2200 {
2201 /* Don't have hotplug devices to test with yet, QEMU 0.14 should */
2202 UNIMPLEMENTED_DBGBREAK();
2203 }
2204
2205 /* Locate the correct resource configurator for this type of device */
2206 Configurator = &PciConfigurators[PdoExtension->HeaderType];
2207
2208 /* Apply the settings change */
2209 Configurator->ChangeResourceSettings(PdoExtension, &PciData);
2210
2211 /* Assume no update needed */
2212 PdoExtension->UpdateHardware = FALSE;
2213
2214 /* Check if a reset is needed */
2215 if (DoReset)
2216 {
2217 /* Reset resources */
2218 Configurator->ResetDevice(PdoExtension, &PciData);
2219 PciData.u.type0.InterruptLine = PdoExtension->RawInterruptLine;
2220 }
2221
2222 /* Check if the latency timer changed */
2223 NewLatencyTimer = PdoExtension->SavedLatencyTimer;
2224 if (PciData.LatencyTimer != NewLatencyTimer)
2225 {
2226 /* Debug notification */
2227 DPRINT1("PCI (pdox %p) changing latency from %02x to %02x.\n",
2228 PdoExtension,
2229 PciData.LatencyTimer,
2230 NewLatencyTimer);
2231 }
2232
2233 /* Check if the cache line changed */
2234 NewCacheLineSize = PdoExtension->SavedCacheLineSize;
2235 if (PciData.CacheLineSize != NewCacheLineSize)
2236 {
2237 /* Debug notification */
2238 DPRINT1("PCI (pdox %p) changing cache line size from %02x to %02x.\n",
2239 PdoExtension,
2240 PciData.CacheLineSize,
2241 NewCacheLineSize);
2242 }
2243
2244 /* Inherit data from PDO extension */
2245 PciData.LatencyTimer = PdoExtension->SavedLatencyTimer;
2246 PciData.CacheLineSize = PdoExtension->SavedCacheLineSize;
2247 PciData.u.type0.InterruptLine = PdoExtension->RawInterruptLine;
2248
2249 /* Apply any resource hacks required */
2250 PciApplyHacks(FdoExtension,
2251 &PciData,
2252 PdoExtension->Slot,
2253 PCI_HACK_FIXUP_BEFORE_UPDATE,
2254 PdoExtension);
2255
2256 /* Check if I/O space was disabled by administrator or driver */
2257 if (PdoExtension->IoSpaceNotRequired)
2258 {
2259 /* Don't turn on the decode */
2260 PdoExtension->CommandEnables &= ~PCI_ENABLE_IO_SPACE;
2261 }
2262
2263 /* Update the device with the new settings */
2264 PciUpdateHardware(PdoExtension, &PciData);
2265
2266 /* Update complete */
2267 PdoExtension->RawInterruptLine = PciData.u.type0.InterruptLine;
2268 PdoExtension->NeedsHotPlugConfiguration = FALSE;
2269 return STATUS_SUCCESS;
2270 }
2271
2272 /* EOF */