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