More PciScanBus codes now to check saved PCI BIOS config header saved in registry...
[reactos.git] / reactos / 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 /* FUNCTIONS ******************************************************************/
18
19 BOOLEAN
20 NTAPI
21 PcipIsSameDevice(IN PPCI_PDO_EXTENSION DeviceExtension,
22 IN PPCI_COMMON_HEADER PciData)
23 {
24 BOOLEAN IdMatch, RevMatch, SubsysMatch;
25 ULONGLONG HackFlags = DeviceExtension->HackFlags;
26
27 /* Check if the IDs match */
28 IdMatch = (PciData->VendorID == DeviceExtension->VendorId) &&
29 (PciData->DeviceID == DeviceExtension->DeviceId);
30 if (!IdMatch) return FALSE;
31
32 /* If the device has a valid revision, check if it matches */
33 RevMatch = (HackFlags & PCI_HACK_NO_REVISION_AFTER_D3) ||
34 (PciData->RevisionID == DeviceExtension->RevisionId);
35 if (!RevMatch) return FALSE;
36
37 /* For multifunction devices, this is enough to assume they're the same */
38 if (PCI_MULTIFUNCTION_DEVICE(PciData)) return TRUE;
39
40 /* For bridge devices, there's also nothing else that can be checked */
41 if (DeviceExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) return TRUE;
42
43 /* Devices, on the other hand, have subsystem data that can be compared */
44 SubsysMatch = (HackFlags & (PCI_HACK_NO_SUBSYSTEM |
45 PCI_HACK_NO_SUBSYSTEM_AFTER_D3)) ||
46 ((DeviceExtension->SubsystemVendorId ==
47 PciData->u.type0.SubVendorID) &&
48 (DeviceExtension->SubsystemId ==
49 PciData->u.type0.SubSystemID));
50 return SubsysMatch;
51 }
52
53 BOOLEAN
54 NTAPI
55 PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData,
56 IN PCI_SLOT_NUMBER Slot,
57 IN UCHAR OperationType,
58 IN ULONGLONG HackFlags)
59 {
60 do
61 {
62 /* Check if this is device enumeration */
63 if (OperationType == PCI_SKIP_DEVICE_ENUMERATION)
64 {
65 /* Check if there's a hackflag saying not to enumerate this device */
66 if (HackFlags & PCI_HACK_NO_ENUM_AT_ALL) break;
67
68 /* Check if this is the high end of a double decker device */
69 if ((HackFlags & PCI_HACK_DOUBLE_DECKER) &&
70 (Slot.u.bits.DeviceNumber >= 16))
71 {
72 /* It belongs to the same device, so skip it */
73 DPRINT1(" Device (Ven %04x Dev %04x (d=0x%x, f=0x%x)) is a ghost.\n",
74 PciData->VendorID,
75 PciData->DeviceID,
76 Slot.u.bits.DeviceNumber,
77 Slot.u.bits.FunctionNumber);
78 break;
79 }
80 }
81 else if (OperationType == PCI_SKIP_RESOURCE_ENUMERATION)
82 {
83 /* Resource enumeration, check for a hackflag saying not to do it */
84 if (HackFlags & PCI_HACK_ENUM_NO_RESOURCE) break;
85 }
86 else
87 {
88 /* Logic error in the driver */
89 ASSERTMSG(FALSE, "PCI Skip Function - Operation type unknown.");
90 }
91
92 /* Check for legacy bridges during resource enumeration */
93 if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
94 (PciData->SubClass <= PCI_SUBCLASS_BR_MCA) &&
95 (OperationType == PCI_SKIP_RESOURCE_ENUMERATION))
96 {
97 /* Their resources are not enumerated, only PCI and Cardbus/PCMCIA */
98 break;
99 }
100 else if (PciData->BaseClass == PCI_CLASS_NOT_DEFINED)
101 {
102 /* Undefined base class (usually a PCI BIOS/ROM bug) */
103 DPRINT1(" Vendor %04x, Device %04x has class code of PCI_CLASS_NOT_DEFINED\n",
104 PciData->VendorID,
105 PciData->DeviceID);
106
107 /*
108 * The Alder has an Intel Extended Express System Support Controller
109 * which presents apparently spurious BARs. When the PCI resource
110 * code tries to reassign these BARs, the second IO-APIC gets
111 * disabled (with disastrous consequences). The first BAR is the
112 * actual IO-APIC, the remaining five bars seem to be spurious
113 * resources, so ignore this device completely.
114 */
115 if ((PciData->VendorID == 0x8086) && (PciData->DeviceID == 8)) break;
116 }
117
118 /* Other normal PCI cards and bridges are enumerated */
119 if (PCI_CONFIGURATION_TYPE(PciData) <= PCI_CARDBUS_BRIDGE_TYPE) return FALSE;
120 } while (FALSE);
121
122 /* Hit one of the known bugs/hackflags, or this is a new kind of PCI unit */
123 DPRINT1(" Device skipped (not enumerated).\n");
124 return TRUE;
125 }
126
127 NTSTATUS
128 NTAPI
129 PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension)
130 {
131 ULONG MaxDevice = PCI_MAX_DEVICES;
132 BOOLEAN ProcessFlag = FALSE;
133 ULONG i, j, k;
134 LONGLONG HackFlags;
135 PDEVICE_OBJECT DeviceObject;
136 UCHAR Buffer[PCI_COMMON_HDR_LENGTH];
137 UCHAR BiosBuffer[PCI_COMMON_HDR_LENGTH];
138 PPCI_COMMON_HEADER PciData = (PVOID)Buffer;
139 PPCI_COMMON_HEADER BiosData = (PVOID)BiosBuffer;
140 PCI_SLOT_NUMBER PciSlot;
141 NTSTATUS Status;
142 PPCI_PDO_EXTENSION PdoExtension, NewExtension;
143 PPCI_PDO_EXTENSION* BridgeExtension;
144 PWCHAR DescriptionText;
145 USHORT SubVendorId, SubSystemId;
146 DPRINT1("PCI Scan Bus: FDO Extension @ 0x%x, Base Bus = 0x%x\n",
147 DeviceExtension, DeviceExtension->BaseBus);
148
149 /* Is this the root FDO? */
150 if (!PCI_IS_ROOT_FDO(DeviceExtension))
151 {
152 /* Other FDOs are not currently supported */
153 UNIMPLEMENTED;
154 while (TRUE);
155 }
156
157 /* Loop every device on the bus */
158 PciSlot.u.bits.Reserved = 0;
159 i = DeviceExtension->BaseBus;
160 for (j = 0; j < MaxDevice; j++)
161 {
162 /* Loop every function of each device */
163 PciSlot.u.bits.DeviceNumber = j;
164 for (k = 0; k < PCI_MAX_FUNCTION; k++)
165 {
166 /* Build the final slot structure */
167 PciSlot.u.bits.FunctionNumber = k;
168
169 /* Read the vendor for this slot */
170 PciReadSlotConfig(DeviceExtension,
171 PciSlot,
172 PciData,
173 0,
174 sizeof(USHORT));
175
176 /* Skip invalid device */
177 if (PciData->VendorID == PCI_INVALID_VENDORID) continue;
178
179 /* Now read the whole header */
180 PciReadSlotConfig(DeviceExtension,
181 PciSlot,
182 &PciData->DeviceID,
183 sizeof(USHORT),
184 PCI_COMMON_HDR_LENGTH - sizeof(USHORT));
185
186 /* Dump device that was found */
187 DPRINT1("Scan Found Device 0x%x (b=0x%x, d=0x%x, f=0x%x)\n",
188 PciSlot.u.AsULONG,
189 i,
190 j,
191 k);
192
193 /* Dump the device's header */
194 PciDebugDumpCommonConfig(PciData);
195
196 /* Find description for this device for the debugger's sake */
197 DescriptionText = PciGetDeviceDescriptionMessage(PciData->BaseClass,
198 PciData->SubClass);
199 DPRINT1("Device Description \"%S\".\n",
200 DescriptionText ? DescriptionText : L"(NULL)");
201 if (DescriptionText) ExFreePoolWithTag(DescriptionText, 0);
202
203 /* Check if there is an ACPI Watchdog Table */
204 if (WdTable)
205 {
206 /* Check if this PCI device is the ACPI Watchdog Device... */
207 UNIMPLEMENTED;
208 while (TRUE);
209 }
210
211 /* Check for non-simple devices */
212 if ((PCI_MULTIFUNCTION_DEVICE(PciData)) ||
213 (PciData->BaseClass == PCI_CLASS_BRIDGE_DEV))
214 {
215 /* No subsystem data defined for these kinds of bridges */
216 SubVendorId = 0;
217 SubSystemId = 0;
218 }
219 else
220 {
221 /* Read the subsystem information from the PCI header */
222 SubVendorId = PciData->u.type0.SubVendorID;
223 SubSystemId = PciData->u.type0.SubSystemID;
224 }
225
226 /* Get any hack flags for this device */
227 HackFlags = PciGetHackFlags(PciData->VendorID,
228 PciData->DeviceID,
229 SubVendorId,
230 SubSystemId,
231 PciData->RevisionID);
232
233 /* Check if this device is considered critical by the OS */
234 if (PciIsCriticalDeviceClass(PciData->BaseClass, PciData->SubClass))
235 {
236 /* Check if normally the decodes would be disabled */
237 if (!(HackFlags & PCI_HACK_DONT_DISABLE_DECODES))
238 {
239 /* Because this device is critical, don't disable them */
240 DPRINT1("Not allowing PM Because device is critical\n");
241 HackFlags |= PCI_HACK_CRITICAL_DEVICE;
242 }
243 }
244
245 /* PCI bridges with a VGA card are also considered critical */
246 if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
247 (PciData->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) &&
248 (PciData->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA) &&
249 !(HackFlags & PCI_HACK_DONT_DISABLE_DECODES))
250 {
251 /* Do not disable their decodes either */
252 DPRINT1("Not allowing PM because device is VGA\n");
253 HackFlags |= PCI_HACK_CRITICAL_DEVICE;
254 }
255
256 /* Also skip devices that should not be enumerated */
257 if (PciSkipThisFunction(PciData, PciSlot, 1, HackFlags)) continue;
258
259 /* Check if a PDO has already been created for this device */
260 PdoExtension = PciFindPdoByFunction(DeviceExtension,
261 PciSlot.u.AsULONG,
262 PciData);
263 if (PdoExtension)
264 {
265 /* Rescan scenarios are not yet implemented */
266 UNIMPLEMENTED;
267 while (TRUE);
268 }
269
270 /* Bus processing will need to happen */
271 ProcessFlag = TRUE;
272
273 /* Create the PDO for this device */
274 Status = PciPdoCreate(DeviceExtension, PciSlot, &DeviceObject);
275 ASSERT(NT_SUCCESS(Status));
276 NewExtension = (PPCI_PDO_EXTENSION)DeviceObject->DeviceExtension;
277
278 /* Check for broken devices with wrong/no class codes */
279 if (HackFlags & PCI_HACK_FAKE_CLASS_CODE)
280 {
281 /* Setup a default one */
282 PciData->BaseClass = PCI_CLASS_BASE_SYSTEM_DEV;
283 PciData->SubClass = PCI_SUBCLASS_SYS_OTHER;
284
285 /* Device will behave erratically when reading back data */
286 NewExtension->ExpectedWritebackFailure = TRUE;
287 }
288
289 /* Clone all the information from the header */
290 NewExtension->VendorId = PciData->VendorID;
291 NewExtension->DeviceId = PciData->DeviceID;
292 NewExtension->RevisionId = PciData->RevisionID;
293 NewExtension->ProgIf = PciData->ProgIf;
294 NewExtension->SubClass = PciData->SubClass;
295 NewExtension->BaseClass = PciData->BaseClass;
296 NewExtension->HeaderType = PCI_CONFIGURATION_TYPE(PciData);
297
298 /* Check for modern bridge types, which are managed by the driver */
299 if ((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
300 ((NewExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) ||
301 (NewExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS)))
302 {
303 /* Acquire this device's lock */
304 KeEnterCriticalRegion();
305 KeWaitForSingleObject(&DeviceExtension->ChildListLock,
306 Executive,
307 KernelMode,
308 FALSE,
309 NULL);
310
311 /* Scan the bridge list until the first free entry */
312 for (BridgeExtension = &DeviceExtension->ChildBridgePdoList;
313 *BridgeExtension;
314 BridgeExtension = &(*BridgeExtension)->NextBridge);
315
316 /* Add this PDO as a bridge */
317 *BridgeExtension = NewExtension;
318 ASSERT(NewExtension->NextBridge == NULL);
319
320 /* Release this device's lock */
321 KeSetEvent(&DeviceExtension->ChildListLock,
322 IO_NO_INCREMENT,
323 FALSE);
324 KeLeaveCriticalRegion();
325 }
326
327 /* Get the PCI BIOS configuration saved in the registry */
328 Status = PciGetBiosConfig(NewExtension, BiosData);
329 if (NT_SUCCESS(Status))
330 {
331 /* This path has not yet been fully tested by eVb */
332 DPRINT1("Have BIOS configuration!\n");
333 UNIMPLEMENTED;
334
335 /* Check if the PCI BIOS configuration has changed */
336 if (!PcipIsSameDevice(NewExtension, BiosData))
337 {
338 /* This is considered failure, and new data will be saved */
339 Status = STATUS_UNSUCCESSFUL;
340 }
341 else
342 {
343 /* Data is still correct, check for interrupt line change */
344 if (BiosData->u.type0.InterruptLine !=
345 PciData->u.type0.InterruptLine)
346 {
347 /* Update the current BIOS with the saved interrupt line */
348 PciWriteDeviceConfig(NewExtension,
349 &BiosData->u.type0.InterruptLine,
350 FIELD_OFFSET(PCI_COMMON_HEADER,
351 u.type0.InterruptLine),
352 sizeof(UCHAR));
353 }
354
355 /* Save the BIOS interrupt line and the initial command */
356 NewExtension->RawInterruptLine = BiosData->u.type0.InterruptLine;
357 NewExtension->InitialCommand = BiosData->Command;
358 }
359 }
360
361 /* Check if no saved data was present or if it was a mismatch */
362 if (!NT_SUCCESS(Status))
363 {
364 /* Save the new data */
365 Status = PciSaveBiosConfig(NewExtension, PciData);
366 ASSERT(NT_SUCCESS(Status));
367
368 /* Save the interrupt line and command from the device */
369 NewExtension->RawInterruptLine = PciData->u.type0.InterruptLine;
370 NewExtension->InitialCommand = PciData->Command;
371 }
372
373 /* Save original command from the device and hack flags */
374 NewExtension->CommandEnables = PciData->Command;
375 NewExtension->HackFlags = HackFlags;
376
377 /* Save interrupt pin */
378 NewExtension->InterruptPin = PciData->u.type0.InterruptPin;
379
380 /*
381 * Use either this device's actual IRQ line or, if it's connected on
382 * a master bus whose IRQ line is actually connected to the host, use
383 * the HAL to query the bus' IRQ line and store that as the adjusted
384 * interrupt line instead
385 */
386 NewExtension->AdjustedInterruptLine = PciGetAdjustedInterruptLine(NewExtension);
387
388 /* Check if this device is used for PCI debugger cards */
389 NewExtension->OnDebugPath = PciIsDeviceOnDebugPath(NewExtension);
390
391 /* Check for devices with invalid/bogus subsystem data */
392 if (HackFlags & PCI_HACK_NO_SUBSYSTEM)
393 {
394 /* Set the subsystem information to zero instead */
395 NewExtension->SubsystemVendorId = 0;
396 NewExtension->SubsystemId = 0;
397 }
398
399 /* Check for IDE controllers */
400 if ((NewExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) &&
401 (NewExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR))
402 {
403 /* Do not allow them to power down completely */
404 NewExtension->DisablePowerDown = TRUE;
405 }
406
407 /*
408 * Check if this is a legacy bridge. Note that the i82375 PCI/EISA
409 * bridge that is present on certain NT Alpha machines appears as
410 * non-classified so detect it manually by scanning for its VID/PID.
411 */
412 if (((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
413 ((NewExtension->SubClass == PCI_SUBCLASS_BR_ISA) ||
414 (NewExtension->SubClass == PCI_SUBCLASS_BR_EISA) ||
415 (NewExtension->SubClass == PCI_SUBCLASS_BR_MCA))) ||
416 ((NewExtension->VendorId == 0x8086) &&
417 (NewExtension->DeviceId == 0x482)))
418 {
419 /* Do not allow these legacy bridges to be powered down */
420 NewExtension->DisablePowerDown = TRUE;
421 }
422
423 /* Save latency and cache size information */
424 NewExtension->SavedLatencyTimer = PciData->LatencyTimer;
425 NewExtension->SavedCacheLineSize = PciData->CacheLineSize;
426
427 /* The PDO is now ready to go */
428 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
429 }
430 }
431
432 /* Enumeration is completed */
433 return STATUS_SUCCESS;
434 }
435
436 NTSTATUS
437 NTAPI
438 PciQueryDeviceRelations(IN PPCI_FDO_EXTENSION DeviceExtension,
439 IN OUT PDEVICE_RELATIONS *pDeviceRelations)
440 {
441 NTSTATUS Status;
442 PPCI_PDO_EXTENSION PdoExtension;
443 ULONG PdoCount = 0;
444 PDEVICE_RELATIONS DeviceRelations, NewRelations;
445 SIZE_T Size;
446 PDEVICE_OBJECT DeviceObject, *ObjectArray;
447 PAGED_CODE();
448
449 /* Make sure the FDO is started */
450 ASSERT(DeviceExtension->DeviceState == PciStarted);
451
452 /* Synchronize while we enumerate the bus */
453 Status = PciBeginStateTransition(DeviceExtension, PciSynchronizedOperation);
454 if (!NT_SUCCESS(Status)) return Status;
455
456 /* Scan all children PDO */
457 for (PdoExtension = DeviceExtension->ChildPdoList;
458 PdoExtension;
459 PdoExtension = PdoExtension->Next)
460 {
461 /* Invalidate them */
462 PdoExtension->NotPresent = TRUE;
463 }
464
465 /* Scan the PCI Bus */
466 Status = PciScanBus(DeviceExtension);
467 ASSERT(NT_SUCCESS(Status));
468
469 /* Enumerate all children PDO again */
470 for (PdoExtension = DeviceExtension->ChildPdoList;
471 PdoExtension;
472 PdoExtension = PdoExtension->Next)
473 {
474 /* Check for PDOs that are still invalidated */
475 if (PdoExtension->NotPresent)
476 {
477 /* This means this PDO existed before, but not anymore */
478 PdoExtension->ReportedMissing = TRUE;
479 DPRINT1("PCI - Old device (pdox) %08x not found on rescan.\n",
480 PdoExtension);
481 }
482 else
483 {
484 /* Increase count of detected PDOs */
485 PdoCount++;
486 }
487 }
488
489 /* Read the current relations and add the newly discovered relations */
490 DeviceRelations = *pDeviceRelations;
491 Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
492 PdoCount * sizeof(PDEVICE_OBJECT);
493 if (DeviceRelations) Size += sizeof(PDEVICE_OBJECT) * DeviceRelations->Count;
494
495 /* Allocate the device relations */
496 NewRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(0, Size, 'BicP');
497 if (!NewRelations)
498 {
499 /* Out of space, cancel the operation */
500 PciCancelStateTransition(DeviceExtension, PciSynchronizedOperation);
501 return STATUS_INSUFFICIENT_RESOURCES;
502 }
503
504 /* Check if there were any older relations */
505 NewRelations->Count = 0;
506 if (DeviceRelations)
507 {
508 /* Copy the old relations into the new buffer, then free the old one */
509 RtlCopyMemory(NewRelations,
510 DeviceRelations,
511 FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
512 DeviceRelations->Count * sizeof(PDEVICE_OBJECT));
513 ExFreePoolWithTag(DeviceRelations, 0);
514 }
515
516 /* Print out that we're ready to dump relations */
517 DPRINT1("PCI QueryDeviceRelations/BusRelations FDOx %08x (bus 0x%02x)\n",
518 DeviceExtension,
519 DeviceExtension->BaseBus);
520
521 /* Loop the current PDO children and the device relation object array */
522 PdoExtension = DeviceExtension->ChildPdoList;
523 ObjectArray = &NewRelations->Objects[NewRelations->Count];
524 while (PdoExtension)
525 {
526 /* Dump this relation */
527 DPRINT1(" QDR PDO %08x (x %08x)%s\n",
528 PdoExtension->PhysicalDeviceObject,
529 PdoExtension,
530 PdoExtension->NotPresent ?
531 "<Omitted, device flaged not present>" : "");
532
533 /* Is this PDO present? */
534 if (!PdoExtension->NotPresent)
535 {
536 /* Reference it and add it to the array */
537 DeviceObject = PdoExtension->PhysicalDeviceObject;
538 ObfReferenceObject(DeviceObject);
539 *ObjectArray++ = DeviceObject;
540 }
541
542 /* Go to the next PDO */
543 PdoExtension = PdoExtension->Next;
544 }
545
546 /* Terminate dumping the relations */
547 DPRINT1(" QDR Total PDO count = %d (%d already in list)\n",
548 NewRelations->Count + PdoCount,
549 NewRelations->Count);
550
551 /* Return the final count and the new buffer */
552 NewRelations->Count += PdoCount;
553 *pDeviceRelations = NewRelations;
554 return STATUS_SUCCESS;
555 }
556
557 /* EOF */