Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5120 lines
146 KiB

  1. /*++
  2. Copyright (c) 1996-2000 Microsoft Corporation
  3. Module Name:
  4. enum.c
  5. Abstract:
  6. This module contains functions associated with enumerating the
  7. PCI buses.
  8. Author:
  9. Peter Johnston (peterj) 20-Nov-1996
  10. Revision History:
  11. Elliot Shmukler (t-ellios) 7-15-1998 Added support for MSI-capable devices.
  12. --*/
  13. #include "pcip.h"
  14. NTSTATUS
  15. PciScanBus(
  16. IN PPCI_FDO_EXTENSION FdoExtension
  17. );
  18. VOID
  19. PciFreeIoRequirementsList(
  20. IN PIO_RESOURCE_REQUIREMENTS_LIST List
  21. );
  22. PCM_RESOURCE_LIST
  23. PciAllocateCmResourceList(
  24. IN ULONG ResourceCount,
  25. IN ULONG BusNumber
  26. );
  27. PCI_CONFIGURATOR PciConfigurators[] = {
  28. {
  29. Device_MassageHeaderForLimitsDetermination,
  30. Device_RestoreCurrent,
  31. Device_SaveLimits,
  32. Device_SaveCurrentSettings,
  33. Device_ChangeResourceSettings,
  34. Device_GetAdditionalResourceDescriptors,
  35. Device_ResetDevice
  36. },
  37. {
  38. PPBridge_MassageHeaderForLimitsDetermination,
  39. PPBridge_RestoreCurrent,
  40. PPBridge_SaveLimits,
  41. PPBridge_SaveCurrentSettings,
  42. PPBridge_ChangeResourceSettings,
  43. PPBridge_GetAdditionalResourceDescriptors,
  44. PPBridge_ResetDevice
  45. },
  46. {
  47. Cardbus_MassageHeaderForLimitsDetermination,
  48. Cardbus_RestoreCurrent,
  49. Cardbus_SaveLimits,
  50. Cardbus_SaveCurrentSettings,
  51. Cardbus_ChangeResourceSettings,
  52. Cardbus_GetAdditionalResourceDescriptors,
  53. Cardbus_ResetDevice
  54. },
  55. };
  56. //
  57. // When dealing with devices whose configuration is totally
  58. // unknown to us, we may want to emit the device but not its
  59. // resources,... or, we may not want to see the device at all.
  60. //
  61. typedef enum {
  62. EnumHackConfigSpace,
  63. EnumBusScan,
  64. EnumResourceDetermination,
  65. EnumStartDevice
  66. } ENUM_OPERATION_TYPE;
  67. PIO_RESOURCE_REQUIREMENTS_LIST PciZeroIoResourceRequirements;
  68. extern PULONG InitSafeBootMode;
  69. //
  70. // Prototypes for functions contained and only used in this module.
  71. //
  72. NTSTATUS
  73. PciGetFunctionLimits(
  74. IN PPCI_PDO_EXTENSION PdoExtension,
  75. IN PPCI_COMMON_CONFIG CurrentConfig,
  76. IN ULONGLONG DeviceFlags
  77. );
  78. NTSTATUS
  79. PcipGetFunctionLimits(
  80. IN PPCI_CONFIGURABLE_OBJECT This
  81. );
  82. BOOLEAN
  83. PciSkipThisFunction(
  84. IN PPCI_COMMON_CONFIG Config,
  85. IN PCI_SLOT_NUMBER Slot,
  86. IN ENUM_OPERATION_TYPE Operation,
  87. IN ULONGLONG DeviceFlags
  88. );
  89. VOID
  90. PciPrivateResourceInitialize(
  91. IN PIO_RESOURCE_DESCRIPTOR Descriptor,
  92. IN PCI_PRIVATE_RESOURCE_TYPES PrivateResourceType,
  93. IN ULONG Data
  94. );
  95. VOID
  96. PciGetInUseRanges(
  97. IN PPCI_PDO_EXTENSION PdoExtension,
  98. IN PPCI_COMMON_CONFIG CurrentConfig,
  99. IN PCM_PARTIAL_RESOURCE_DESCRIPTOR InUse
  100. );
  101. VOID
  102. PciWriteLimitsAndRestoreCurrent(
  103. IN PPCI_CONFIGURABLE_OBJECT This
  104. );
  105. VOID
  106. PciGetEnhancedCapabilities(
  107. IN PPCI_PDO_EXTENSION PdoExtension,
  108. IN PPCI_COMMON_CONFIG Config
  109. );
  110. BOOLEAN
  111. PcipIsSameDevice(
  112. IN PPCI_PDO_EXTENSION PdoExtension,
  113. IN PPCI_COMMON_CONFIG CommonConfig
  114. );
  115. BOOLEAN
  116. PciConfigureIdeController(
  117. IN PPCI_PDO_EXTENSION PdoExtension,
  118. IN OUT PPCI_COMMON_CONFIG Config,
  119. IN BOOLEAN TurnOffAllNative
  120. );
  121. VOID
  122. PciBuildGraduatedWindow(
  123. IN PIO_RESOURCE_DESCRIPTOR PrototypeDescriptor,
  124. IN ULONG WindowMax,
  125. IN ULONG WindowCount,
  126. OUT PIO_RESOURCE_DESCRIPTOR OutputDescriptor
  127. );
  128. #ifdef ALLOC_PRAGMA
  129. #pragma alloc_text(PAGE, PciAllocateCmResourceList)
  130. #pragma alloc_text(PAGE, PciComputeNewCurrentSettings)
  131. #pragma alloc_text(PAGE, PciFreeIoRequirementsList)
  132. #pragma alloc_text(PAGE, PciGetInUseRanges)
  133. #pragma alloc_text(PAGE, PciQueryDeviceRelations)
  134. #pragma alloc_text(PAGE, PciQueryTargetDeviceRelations)
  135. #pragma alloc_text(PAGE, PciQueryRequirements)
  136. #pragma alloc_text(PAGE, PciQueryResources)
  137. #pragma alloc_text(PAGE, PciScanBus)
  138. #pragma alloc_text(PAGE, PciBuildRequirementsList)
  139. #pragma alloc_text(PAGE, PciGetFunctionLimits)
  140. #pragma alloc_text(PAGE, PcipGetFunctionLimits)
  141. #pragma alloc_text(PAGE, PciPrivateResourceInitialize)
  142. #pragma alloc_text(PAGE, PciWriteLimitsAndRestoreCurrent)
  143. #pragma alloc_text(PAGE, PciGetEnhancedCapabilities)
  144. #pragma alloc_text(PAGE, PciBuildGraduatedWindow)
  145. #endif
  146. BOOLEAN
  147. PciSkipThisFunction(
  148. IN PPCI_COMMON_CONFIG Config,
  149. IN PCI_SLOT_NUMBER Slot,
  150. IN ENUM_OPERATION_TYPE Operation,
  151. IN ULONGLONG RegistryFlags
  152. )
  153. /*++
  154. Routine Description:
  155. Check for known defective parts and return TRUE if this driver
  156. should do no further processing on this function.
  157. Arguments:
  158. Config Pointer to a copy of the common configuration header as
  159. read from the function's configuration space.
  160. Return Value:
  161. TRUE is this function is not known to cause problems, FALSE
  162. if the function should be skipped altogether.
  163. --*/
  164. {
  165. ULONGLONG flags = RegistryFlags;
  166. #define SKIP_IF_FLAG(f, skip) if (flags & (f)) goto skip
  167. #define FLAG_SET(f) (flags & (f))
  168. switch (Operation) {
  169. case EnumBusScan:
  170. //
  171. // Test the flags we care about during a bus scan.
  172. // Eg: the ones that say "pretend we never saw this device".
  173. //
  174. SKIP_IF_FLAG(PCI_HACK_NO_ENUM_AT_ALL, skipFunction);
  175. if (FLAG_SET(PCI_HACK_DOUBLE_DECKER) &&
  176. (Slot.u.bits.DeviceNumber >= 16)) {
  177. //
  178. // This device seems to look at only the lower 4 bits of
  179. // its DEVSEL lines, ie it is mirrored in the upper half
  180. // if the buss's device domain.
  181. //
  182. PciDebugPrint(
  183. PciDbgInformative,
  184. " Device (Ven %04x Dev %04x (d=0x%x, f=0x%x)) is a ghost.\n",
  185. Config->VendorID,
  186. Config->DeviceID,
  187. Slot.u.bits.DeviceNumber,
  188. Slot.u.bits.FunctionNumber
  189. );
  190. goto skipFunction;
  191. }
  192. break;
  193. case EnumResourceDetermination:
  194. //
  195. // Limit the flags to those applicable to resource determination.
  196. //
  197. SKIP_IF_FLAG(PCI_HACK_ENUM_NO_RESOURCE, skipFunction);
  198. break;
  199. default:
  200. ASSERTMSG("PCI Skip Function - Operation type unknown.", 0);
  201. //
  202. // Don't know how to apply flags here.
  203. //
  204. }
  205. switch (Config->BaseClass) {
  206. case PCI_CLASS_NOT_DEFINED:
  207. //
  208. // Currently we get this from VendorID = 8086, DeviceID = 0008,
  209. // which reports a bunch of bogus resources.
  210. //
  211. // We have no idea what it really is either.
  212. //
  213. PciDebugPrint(
  214. PciDbgInformative,
  215. " Vendor %04x, Device %04x has class code of PCI_CLASS_NOT_DEFINED\n",
  216. Config->VendorID,
  217. Config->DeviceID
  218. );
  219. // This case should be added to the registry.
  220. if ((Config->VendorID == 0x8086) &&
  221. (Config->DeviceID == 0x0008)) {
  222. goto skipFunction;
  223. }
  224. break;
  225. case PCI_CLASS_BRIDGE_DEV:
  226. switch (Config->SubClass) {
  227. case PCI_SUBCLASS_BR_HOST:
  228. //
  229. // It's a host bridge, emit the PDO in case there is
  230. // a (miniport) driver for it, but under no circumstances
  231. // should we attempt to figure out what resources it
  232. // consumes (we don't know the format of its configuration
  233. // space).
  234. //
  235. case PCI_SUBCLASS_BR_ISA:
  236. case PCI_SUBCLASS_BR_EISA:
  237. case PCI_SUBCLASS_BR_MCA:
  238. //
  239. // Microchannel bridges report their resource usage
  240. // like good citizens. Unfortunately we really want
  241. // them to behave like ISA bridges and consume no
  242. // resources themselves. Their children are subtractive
  243. // from the parent bus. Enumerate the device but not
  244. // its resources.
  245. //
  246. if (Operation == EnumResourceDetermination) {
  247. goto skipFunction;
  248. }
  249. break;
  250. }
  251. }
  252. //
  253. // Verify we understand the header type.
  254. //
  255. if (PciGetConfigurationType(Config) > PCI_MAX_CONFIG_TYPE) {
  256. goto skipFunction;
  257. }
  258. //
  259. // Nothing interesting,
  260. //
  261. return FALSE;
  262. skipFunction:
  263. PciDebugPrint(PciDbgPrattling, " Device skipped (not enumerated).\n");
  264. return TRUE;
  265. }
  266. VOID
  267. PciApplyHacks(
  268. IN PPCI_FDO_EXTENSION FdoExtension,
  269. IN PPCI_COMMON_CONFIG Config,
  270. IN PCI_SLOT_NUMBER Slot,
  271. IN ENUM_OPERATION_TYPE Operation,
  272. IN PPCI_PDO_EXTENSION PdoExtension OPTIONAL
  273. )
  274. {
  275. switch (Operation) {
  276. case EnumHackConfigSpace:
  277. ASSERT(PdoExtension == NULL);
  278. //
  279. // Some devices (e.g. pre-2.0 devices) do not report reasonable class
  280. // codes. Update the class code for a given set of devices so that we
  281. // don't have to special-case these devices throughout the driver.
  282. //
  283. switch (Config->VendorID) {
  284. //
  285. // Intel
  286. //
  287. case 0x8086:
  288. switch (Config->DeviceID) {
  289. //
  290. // PCEB - PCI/EISA Bridge (pre 2.0)
  291. //
  292. case 0x0482:
  293. Config->BaseClass = PCI_CLASS_BRIDGE_DEV;
  294. Config->SubClass = PCI_SUBCLASS_BR_EISA;
  295. #if DBG
  296. if (PdoExtension != NULL) {
  297. PdoExtension->ExpectedWritebackFailure = TRUE;
  298. }
  299. #endif
  300. break;
  301. //
  302. // SIO - PCI/ISA Bridge (pre 2.0)
  303. //
  304. case 0x0484:
  305. Config->BaseClass = PCI_CLASS_BRIDGE_DEV;
  306. Config->SubClass = PCI_SUBCLASS_BR_ISA;
  307. #if DBG
  308. if (PdoExtension != NULL) {
  309. PdoExtension->ExpectedWritebackFailure = TRUE;
  310. }
  311. #endif
  312. break;
  313. }
  314. break;
  315. }
  316. break;
  317. case EnumBusScan:
  318. ASSERT(PdoExtension);
  319. if ((Config->VendorID == 0x1045) &&
  320. (Config->DeviceID == 0xc621)) {
  321. //
  322. // Bug 131482. Force this device into legacy mode for
  323. // the purposes of detection anyway.
  324. //
  325. Config->ProgIf &= ~(PCI_IDE_PRIMARY_NATIVE_MODE
  326. | PCI_IDE_SECONDARY_NATIVE_MODE);
  327. #if DBG
  328. //
  329. // This field is not actually writeable so don't tell
  330. // people the writeback failed (we don't care).
  331. //
  332. PdoExtension->ExpectedWritebackFailure = TRUE;
  333. #endif
  334. } else if (Config->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR
  335. && Config->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR) {
  336. //
  337. // Check with the BIOS to ensure that it can deal with the mode change
  338. // This is indicated by the *parent* of the device having a method
  339. // called NATA which returns a package of integers which are slots
  340. // in _ADR format that can be switched to native mode.
  341. //
  342. // Enabling native mode might expose BIOS bugs like interrupt routing problems
  343. // that could prevent the machine from booting, so don't do this for
  344. // safe mode, so that the user has some way to boot.
  345. //
  346. // For XP SP1, also check for a system wide hack flag that will not be set
  347. // by a service pack installed through update.exe. This way we
  348. // know we will only be enabling this feature on slipstreamed builds.
  349. //
  350. if ((PciEnableNativeModeATA != 0) &&
  351. (*InitSafeBootMode == 0) &&
  352. PciIsSlotPresentInParentMethod(PdoExtension, (ULONG)'ATAN')) {
  353. PdoExtension->BIOSAllowsIDESwitchToNativeMode = TRUE;
  354. } else {
  355. PdoExtension->BIOSAllowsIDESwitchToNativeMode = FALSE;
  356. }
  357. //
  358. // Config is updated to reflect the results of the switch
  359. // if any. Relied upon below.
  360. //
  361. PdoExtension->SwitchedIDEToNativeMode =
  362. PciConfigureIdeController(PdoExtension, Config, TRUE);
  363. }
  364. //
  365. // If the controller is (still) in legacy mode then it
  366. // consume 2 ISA interrupts whatever its interrupt pin says
  367. // force the driver to figure out interrupts on its own.
  368. //
  369. // Examine Base Class, Sub Class and Programming Interface,
  370. // if legacy mode, pretend PIN == 0.
  371. //
  372. if (PCI_IS_LEGACY_IDE_CONTROLLER(Config)) {
  373. //
  374. // Legacy mode. Pretend there is no PCI interrupt.
  375. //
  376. Config->u.type0.InterruptPin = 0;
  377. }
  378. //
  379. // This hack doesn't change the config space for this device but enables
  380. // a hack in the PCI arbiters to reserve a large number IO ranges for
  381. // broken S3 and ATI cards. These legacy cards don't function behind a
  382. // bridge so we only perform the check on a root bus and only perform it
  383. // once
  384. //
  385. if ((PdoExtension->HackFlags & PCI_HACK_VIDEO_LEGACY_DECODE)
  386. && PCI_IS_ROOT_FDO(FdoExtension)
  387. && !FdoExtension->BrokenVideoHackApplied) {
  388. ario_ApplyBrokenVideoHack(FdoExtension);
  389. }
  390. //
  391. // Check if this is the broken Compaq hot-plug controller that
  392. // is integrated into the Profusion chipset. It only does a 32bit
  393. // decode in a 64bit address space... Does this seem familiar to anyone...
  394. // can you say ISA aliasing!
  395. //
  396. // The solution is to disable the memory decode. But so that the user
  397. // gets to keep the hot-plug functionality we need to still enumerate
  398. // but prune out the memory requirement and rely on the fact that the
  399. // registers can be accessed through config space. This is done later
  400. // in PciGetRequirements.
  401. //
  402. // Only do this on machines with PAE enabled as they can have > 4GB.
  403. // Note that this will only work on x86 machines but this is an x86 only
  404. // chipset. Only revision 0x11 was broken.
  405. //
  406. if (Config->VendorID == 0x0e11
  407. && Config->DeviceID == 0xa0f7
  408. && Config->RevisionID == 0x11
  409. && ExIsProcessorFeaturePresent(PF_PAE_ENABLED)) {
  410. Config->Command &= ~(PCI_ENABLE_MEMORY_SPACE
  411. | PCI_ENABLE_BUS_MASTER
  412. | PCI_ENABLE_IO_SPACE);
  413. PciSetCommandRegister(PdoExtension, Config->Command);
  414. PdoExtension->CommandEnables &= ~(PCI_ENABLE_MEMORY_SPACE
  415. | PCI_ENABLE_BUS_MASTER
  416. | PCI_ENABLE_IO_SPACE);
  417. PdoExtension->HackFlags |= PCI_HACK_PRESERVE_COMMAND;
  418. }
  419. //
  420. // If this is a cardbus controller force it into Cardbus mode by writing 0
  421. // to the LegacyModeBaseAddressRegister.
  422. //
  423. if (PCI_CONFIGURATION_TYPE(Config) == PCI_CARDBUS_BRIDGE_TYPE) {
  424. ULONG zeroLMBA = 0;
  425. PciWriteDeviceConfig(PdoExtension,
  426. &zeroLMBA,
  427. CARDBUS_LMBA_OFFSET,
  428. sizeof(zeroLMBA)
  429. );
  430. }
  431. break;
  432. case EnumStartDevice:
  433. ASSERT(PdoExtension);
  434. //
  435. // IBM built a bridge (Kirin) that does both positive and subtractive decode
  436. // - we don't do that so set it to totally subtractive mode (info is from
  437. // NT bug 267076)
  438. //
  439. // NB - this relies on the fact that Kirin has a ProgIf of 1.
  440. //
  441. if (PdoExtension->VendorId == 0x1014 && PdoExtension->DeviceId == 0x0095) {
  442. UCHAR regE0;
  443. USHORT cmd;
  444. //
  445. // Turn off the hardware as we are going to mess with it
  446. //
  447. PciGetCommandRegister(PdoExtension, &cmd);
  448. PciDecodeEnable(PdoExtension, FALSE, &cmd);
  449. //
  450. // This is a Kirin
  451. //
  452. // Offset E0h - bit 0 : Subtractive Decode enable/disable
  453. // = 1 .. Enable
  454. // = 0 .. Disable
  455. // bit 1 : Subtractive Decode Timing
  456. // = 0 : Subtractive Timing
  457. // = 1 : Slow Timing
  458. //
  459. PciReadDeviceConfig(PdoExtension, &regE0, 0xE0, 1);
  460. //
  461. // Set subtractive with subtractive timing.
  462. //
  463. regE0 |= 0x1;
  464. regE0 &= ~0x2;
  465. PciWriteDeviceConfig(PdoExtension, &regE0, 0xE0, 1);
  466. //
  467. // Put the command register back as we found it
  468. //
  469. PciSetCommandRegister(PdoExtension, cmd);
  470. }
  471. //
  472. // Subtractive decode bridges are not meant to have writeable window
  473. // register - some do so if this is a subtractive bridge then close
  474. // these windows by setting base > limit.
  475. //
  476. if (PdoExtension->HeaderType == PCI_BRIDGE_TYPE
  477. && PdoExtension->Dependent.type1.SubtractiveDecode
  478. && !PCI_IS_INTEL_ICH(PdoExtension)) {
  479. //
  480. // Now close all the windows on this bridge - if the registers are read only
  481. // this is a NOP.
  482. //
  483. Config->u.type1.IOBase = 0xFF;
  484. Config->u.type1.IOLimit = 0;
  485. Config->u.type1.MemoryBase = 0xFFFF;
  486. Config->u.type1.MemoryLimit = 0;
  487. Config->u.type1.PrefetchBase = 0xFFFF;
  488. Config->u.type1.PrefetchLimit = 0;
  489. Config->u.type1.PrefetchBaseUpper32 = 0;
  490. Config->u.type1.PrefetchLimitUpper32 = 0;
  491. Config->u.type1.IOBaseUpper16 = 0;
  492. Config->u.type1.IOLimitUpper16 = 0;
  493. }
  494. //
  495. // If this is a cardbus controller force it into Cardbus mode by writing 0
  496. // to the LegacyModeBaseAddressRegister.
  497. //
  498. if (Config->HeaderType == PCI_CARDBUS_BRIDGE_TYPE) {
  499. ULONG zeroLMBA = 0;
  500. PciWriteDeviceConfig(PdoExtension,
  501. &zeroLMBA,
  502. CARDBUS_LMBA_OFFSET,
  503. sizeof(zeroLMBA)
  504. );
  505. }
  506. break;
  507. }
  508. }
  509. PIO_RESOURCE_REQUIREMENTS_LIST
  510. PciAllocateIoRequirementsList(
  511. IN ULONG ResourceCount,
  512. IN ULONG BusNumber,
  513. IN ULONG SlotNumber
  514. )
  515. {
  516. PIO_RESOURCE_REQUIREMENTS_LIST list;
  517. ULONG size;
  518. //
  519. // Allocate space for (and zero) the resource requirements list.
  520. //
  521. size = ((ResourceCount - 1) * sizeof(IO_RESOURCE_DESCRIPTOR)) +
  522. sizeof(IO_RESOURCE_REQUIREMENTS_LIST);
  523. if (ResourceCount == 0) {
  524. //
  525. // We should not be called for a resource count of zero, except
  526. // once for the empty list. In any case, it should work.
  527. //
  528. size = sizeof(IO_RESOURCE_REQUIREMENTS_LIST);
  529. }
  530. list = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, size);
  531. if (list != NULL) {
  532. RtlZeroMemory(list, size);
  533. //
  534. // Initialize the list structure header.
  535. //
  536. // Driver constant-
  537. //
  538. list->InterfaceType = PCIBus;
  539. list->AlternativeLists = 1;
  540. list->List[0].Version = PCI_CM_RESOURCE_VERSION;
  541. list->List[0].Revision = PCI_CM_RESOURCE_REVISION;
  542. //
  543. // Call dependent.
  544. //
  545. list->BusNumber = BusNumber;
  546. list->SlotNumber = SlotNumber;
  547. list->ListSize = size;
  548. list->List[0].Count = ResourceCount;
  549. }
  550. return list;
  551. }
  552. VOID
  553. PciFreeIoRequirementsList(
  554. IN PIO_RESOURCE_REQUIREMENTS_LIST List
  555. )
  556. {
  557. //
  558. // Don't free the empty list and don't free NULL which is
  559. // also allowed.
  560. //
  561. if ((List == NULL) || (List == PciZeroIoResourceRequirements)) {
  562. return;
  563. }
  564. ExFreePool(List);
  565. }
  566. PCM_RESOURCE_LIST
  567. PciAllocateCmResourceList(
  568. IN ULONG ResourceCount,
  569. IN ULONG BusNumber
  570. )
  571. {
  572. PCM_RESOURCE_LIST list;
  573. ULONG size;
  574. PCM_PARTIAL_RESOURCE_LIST partial;
  575. //
  576. // CM_RESOURCE_LIST includes space for one descriptor. If there's
  577. // more than one (in the resource list) increase the allocation by
  578. // that amount.
  579. //
  580. size = sizeof(CM_RESOURCE_LIST);
  581. if (ResourceCount > 1) {
  582. size += (ResourceCount - 1) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
  583. }
  584. //
  585. // Get memory for the resource list.
  586. //
  587. list = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, size);
  588. if (list != NULL) {
  589. //
  590. // Initialize the resource list.
  591. //
  592. list->Count = 1;
  593. list->List[0].InterfaceType = PCIBus;
  594. list->List[0].BusNumber = BusNumber;
  595. partial = &list->List[0].PartialResourceList;
  596. partial->Version = PCI_CM_RESOURCE_VERSION;
  597. partial->Revision = PCI_CM_RESOURCE_REVISION;
  598. partial->Count = ResourceCount;
  599. RtlZeroMemory(
  600. partial->PartialDescriptors,
  601. size - ((ULONG_PTR)partial->PartialDescriptors - (ULONG_PTR)list)
  602. );
  603. }
  604. return list;
  605. }
  606. VOID
  607. PciPrivateResourceInitialize(
  608. IN PIO_RESOURCE_DESCRIPTOR Descriptor,
  609. IN PCI_PRIVATE_RESOURCE_TYPES PrivateResourceType,
  610. IN ULONG Data
  611. )
  612. {
  613. Descriptor->Type = CmResourceTypeDevicePrivate;
  614. Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
  615. Descriptor->Option = 0;
  616. Descriptor->Flags = 0;
  617. Descriptor->u.DevicePrivate.Data[0] = PrivateResourceType;
  618. Descriptor->u.DevicePrivate.Data[1] = Data;
  619. }
  620. VOID
  621. PciGetInUseRanges(
  622. IN PPCI_PDO_EXTENSION PdoExtension,
  623. IN PPCI_COMMON_CONFIG CurrentConfig,
  624. IN PCM_PARTIAL_RESOURCE_DESCRIPTOR InUse
  625. )
  626. /*++
  627. Routine Description:
  628. Builds an array of CM Partial Resource descriptors containing
  629. valid entries only where the corresponding PCI address range
  630. is in use, NULL otherwise.
  631. Arguments:
  632. PdoExtension - Pointer to the Physical Device Object Extension for
  633. the device whose requirements list is needed.
  634. CurrentConfig - Existing contents of configuration space.
  635. Partial - Pointer to an array of CM_PARTIAL_RESOURCE_DESCRIPTORs.
  636. Return Value:
  637. None.
  638. --*/
  639. {
  640. PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
  641. PIO_RESOURCE_DESCRIPTOR ioResourceDescriptor;
  642. BOOLEAN enabledPciIo;
  643. BOOLEAN enabledPciMem;
  644. BOOLEAN passing = FALSE;
  645. ULONG index;
  646. partial = PdoExtension->Resources->Current;
  647. ioResourceDescriptor = PdoExtension->Resources->Limit;
  648. enabledPciIo = BITS_SET(CurrentConfig->Command, PCI_ENABLE_IO_SPACE)
  649. || BITS_SET(PdoExtension->InitialCommand, PCI_ENABLE_IO_SPACE);
  650. enabledPciMem = BITS_SET(CurrentConfig->Command, PCI_ENABLE_MEMORY_SPACE)
  651. || BITS_SET(PdoExtension->InitialCommand, PCI_ENABLE_MEMORY_SPACE);
  652. for (index = 0;
  653. index < PCI_MAX_RANGE_COUNT;
  654. index++, InUse++, partial++, ioResourceDescriptor++) {
  655. //
  656. // Default to not in use.
  657. //
  658. InUse->Type = CmResourceTypeNull;
  659. //
  660. // If the resource type in the limits array is
  661. // CmResourceTypeNull, this resource is not implemented.
  662. //
  663. if (ioResourceDescriptor->Type != CmResourceTypeNull) {
  664. //
  665. // Not NULL, only options are Port or Memory, we will
  666. // consider this entry if the approptiate resource
  667. // (Port or Memory) is currently enabled.
  668. //
  669. if (((partial->Type == CmResourceTypePort) && enabledPciIo) ||
  670. ((partial->Type == CmResourceTypeMemory) && enabledPciMem)) {
  671. if (partial->u.Generic.Length != 0) {
  672. //
  673. // Length is non-zero, if base is also non-zero, OR
  674. // if this is a bridge and the resource type is IO,
  675. // allow it.
  676. //
  677. if ((partial->u.Generic.Start.QuadPart != 0) ||
  678. ((PciGetConfigurationType(CurrentConfig) == PCI_BRIDGE_TYPE) &&
  679. (partial->Type == CmResourceTypePort))) {
  680. //
  681. // This resource describes a valid range that is
  682. // currently enabled by hardware.
  683. //
  684. *InUse = *partial;
  685. }
  686. }
  687. }
  688. }
  689. }
  690. }
  691. VOID
  692. PciBuildGraduatedWindow(
  693. IN PIO_RESOURCE_DESCRIPTOR PrototypeDescriptor,
  694. IN ULONG WindowMax,
  695. IN ULONG WindowCount,
  696. OUT PIO_RESOURCE_DESCRIPTOR OutputDescriptor
  697. )
  698. /*++
  699. Routine Description:
  700. Builds an array of IO Resource descriptors containing
  701. graduated requirements from WindowMax for WindowCount
  702. Descriptors dividing the length required in half each time.
  703. eg If WindowMax is 64Mb and WindowCount is 7 we end up with
  704. the progression 64Mb, 32Mb, 16Mb, 8Mb, 4Mb, 2Mb, 1Mb
  705. This only works for IO and Memory descriptors.
  706. Arguments:
  707. PrototypeDescriptor - this is used to initialize each
  708. requirement descriptor then the lenght is modified
  709. WindowMax - the maximum size of the window (where we build
  710. the progression from)
  711. WindowCount - the number of descriptors in the progression
  712. OutputDescriptor - pointer to the first of WindowCount
  713. descriptors to be populated by this function.
  714. Return Value:
  715. None.
  716. --*/
  717. {
  718. ULONG window, count;
  719. PIO_RESOURCE_DESCRIPTOR current;
  720. PAGED_CODE();
  721. ASSERT(PrototypeDescriptor->Type == CmResourceTypePort
  722. || PrototypeDescriptor->Type == CmResourceTypeMemory);
  723. window = WindowMax;
  724. current = OutputDescriptor;
  725. for (count = 0; count < WindowCount; count++) {
  726. RtlCopyMemory(current, PrototypeDescriptor, sizeof(IO_RESOURCE_DESCRIPTOR));
  727. //
  728. // Update the length
  729. //
  730. current->u.Generic.Length = window;
  731. //
  732. // If this is an alternative then mark it so
  733. //
  734. if (count > 0) {
  735. current->Option = IO_RESOURCE_ALTERNATIVE;
  736. }
  737. current++;
  738. //
  739. // Divide window and repeat
  740. //
  741. window /= 2;
  742. ASSERT(window > 1);
  743. }
  744. //
  745. // Return the number of descriptors filled in
  746. //
  747. ASSERT((current - OutputDescriptor) == WindowCount);
  748. }
  749. NTSTATUS
  750. PciBuildRequirementsList(
  751. IN PPCI_PDO_EXTENSION PdoExtension,
  752. IN PPCI_COMMON_CONFIG CurrentConfig,
  753. OUT PIO_RESOURCE_REQUIREMENTS_LIST *FinalReqList
  754. )
  755. /*++
  756. Routine Description:
  757. Build the IO_RESOURCE_REQUIREMENTS_LIST structure for this device.
  758. This structure contains the devices limits and requirements, for
  759. example, IO space in the range 0x100 to 0x1ff, Length 10.
  760. Arguments:
  761. PdoExtension - Pointer to the Physical Device Object Extension for
  762. the device whose requirements list is needed.
  763. CurrentConfig - Existing contents of configuration space.
  764. Return Value:
  765. Returns a pointer to the IO_RESOURCE_REQUIREMENTS_LIST for this
  766. device/function if all went well. NULL otherwise.
  767. --*/
  768. {
  769. //
  770. // Each base resource requires three extended resource descriptors.
  771. // the total RESOURCES_PER_BAR are
  772. //
  773. // 1. Base Resource Descriptor. eg PCI Memory or I/O space.
  774. // 2. Ext Resource Descriptor, DevicePrivate. This is used to
  775. // keep track of which BAR this resource is derived from.
  776. #define RESOURCES_PER_BAR 2
  777. #define PCI_GRADUATED_WINDOW_COUNT 7 // 64, 32, 16, 8, 4, 2, 1
  778. #define PCI_GRADUATED_WINDOW_MAX (64 * 1024 * 1024) // 64Mb
  779. ULONG index;
  780. ULONG baseResourceCount = 0;
  781. ULONG configType;
  782. ULONG interruptMin, interruptMax;
  783. ULONG iterationCount;
  784. BOOLEAN generatesInterrupt = FALSE;
  785. NTSTATUS status;
  786. PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
  787. PIO_RESOURCE_DESCRIPTOR ioResourceDescriptor;
  788. PIO_RESOURCE_DESCRIPTOR resource;
  789. PIO_RESOURCE_REQUIREMENTS_LIST reqList;
  790. CM_PARTIAL_RESOURCE_DESCRIPTOR inUse[PCI_MAX_RANGE_COUNT];
  791. PPCI_CONFIGURATOR configurator;
  792. PciDebugPrint(PciDbgInformative,
  793. "PciBuildRequirementsList: Bus 0x%x, Dev 0x%x, Func 0x%x.\n",
  794. PCI_PARENT_FDOX(PdoExtension)->BaseBus,
  795. PdoExtension->Slot.u.bits.DeviceNumber,
  796. PdoExtension->Slot.u.bits.FunctionNumber);
  797. if (PdoExtension->Resources == NULL) {
  798. //
  799. // If this function implements no BARs, we won't have a
  800. // resource structure for it.
  801. //
  802. iterationCount = 0;
  803. } else {
  804. iterationCount = PCI_MAX_RANGE_COUNT;
  805. partial = inUse;
  806. ioResourceDescriptor = PdoExtension->Resources->Limit;
  807. PciGetInUseRanges(PdoExtension, CurrentConfig, partial);
  808. }
  809. configurator =
  810. &PciConfigurators[PciGetConfigurationType(CurrentConfig)];
  811. //
  812. // First pass, figure out how large the resource requirements
  813. // list needs to be.
  814. //
  815. for (index = 0;
  816. index < iterationCount;
  817. index++, partial++, ioResourceDescriptor++) {
  818. //
  819. // If the resource type in the limits array is
  820. // CmResourceTypeNull, this resource is not implemented.
  821. //
  822. if (ioResourceDescriptor->Type != CmResourceTypeNull) {
  823. if (partial->Type != CmResourceTypeNull) {
  824. if ((ioResourceDescriptor->u.Generic.Length == 0) // bridge
  825. #if PCI_BOOT_CONFIG_PREFERRED
  826. || (1) // always
  827. #endif
  828. )
  829. {
  830. //
  831. // Count one for the preferred setting.
  832. //
  833. baseResourceCount++;
  834. PciDebugPrint(PciDbgObnoxious,
  835. " Index %d, Preferred = TRUE\n",
  836. index
  837. );
  838. }
  839. } else {
  840. //
  841. // This range is not being passed so we will not
  842. // generate a preferred setting for it.
  843. //
  844. // Bridges have variable length ranges so there is
  845. // no meaningful value that we could have stored in
  846. // the base descriptor other than 0. We use this
  847. // fact to determine if it's a bridged range.
  848. //
  849. // If this range is a bridge range, we do not want
  850. // to generate a base descriptor for it either.
  851. //
  852. // Unless we are providing default minimum settings.
  853. // (Only do this for PCI-PCI bridges, CardBus gets
  854. // to figure out what it wants).
  855. //
  856. if (ioResourceDescriptor->u.Generic.Length == 0) {
  857. //
  858. // Generating prefered settings,... unless,...
  859. // If the bridge IO is enabled and VGA is enabled,
  860. // (and the IO range isn't programmed,... which is
  861. // how we got here), then the VGA ranges are enough,
  862. // don't try to add a range.
  863. //
  864. if ((ioResourceDescriptor->Type == CmResourceTypePort) &&
  865. PdoExtension->Dependent.type1.VgaBitSet) {
  866. continue;
  867. }
  868. //
  869. // If this is a memory window then make space for a graduated
  870. // requirement and a device private to follow it
  871. //
  872. if (ioResourceDescriptor->Type == CmResourceTypeMemory) {
  873. baseResourceCount += PCI_GRADUATED_WINDOW_COUNT + 1;
  874. continue;
  875. }
  876. }
  877. //
  878. // If this resource is a ROM which is not currently
  879. // enabled, don't report it at all.
  880. //
  881. // Note: The ROM requirement exists in the io resource
  882. // descriptors so we know what to do if we get a read
  883. // config for the ROM.
  884. //
  885. if ((ioResourceDescriptor->Type == CmResourceTypeMemory) &&
  886. (ioResourceDescriptor->Flags == CM_RESOURCE_MEMORY_READ_ONLY)) {
  887. continue;
  888. }
  889. }
  890. //
  891. // Count one for the base resource, and any per resource
  892. // special ones (eg Device Private).
  893. //
  894. baseResourceCount += RESOURCES_PER_BAR;
  895. PciDebugPrint(PciDbgObnoxious,
  896. " Index %d, Base Resource = TRUE\n",
  897. index
  898. );
  899. }
  900. }
  901. //
  902. // One base type for Interrupts if enabled.
  903. //
  904. status = PciGetInterruptAssignment(PdoExtension,
  905. &interruptMin,
  906. &interruptMax);
  907. if (NT_SUCCESS(status)) {
  908. generatesInterrupt = TRUE;
  909. baseResourceCount += RESOURCES_PER_BAR - 1;
  910. }
  911. //
  912. // If the header type dependent resource routines indicated
  913. // additional resources are required, add them in here.
  914. //
  915. baseResourceCount += PdoExtension->AdditionalResourceCount;
  916. PciDebugPrint(PciDbgPrattling,
  917. "PCI - build resource reqs - baseResourceCount = %d\n",
  918. baseResourceCount);
  919. if (baseResourceCount == 0) {
  920. //
  921. // This device consumes no resources. Succeed the request but
  922. // return a pointer to our private empty list. This will never
  923. // actually be given to anyone else but having an empty list
  924. // removes a bunch of special case code for handling a NULL
  925. // pointer.
  926. //
  927. if (PciZeroIoResourceRequirements == NULL) {
  928. PciZeroIoResourceRequirements = PciAllocateIoRequirementsList(
  929. 0, // resource count
  930. 0, // bus
  931. 0 // slot
  932. );
  933. }
  934. *FinalReqList = PciZeroIoResourceRequirements;
  935. PciDebugPrint(PciDbgPrattling,
  936. "PCI - build resource reqs - early out, 0 resources\n");
  937. return STATUS_SUCCESS;
  938. }
  939. //
  940. // Allocate and (bulk) initialize the IO resource requirements list.
  941. //
  942. reqList = PciAllocateIoRequirementsList(
  943. baseResourceCount,
  944. PCI_PARENT_FDOX(PdoExtension)->BaseBus,
  945. PdoExtension->Slot.u.AsULONG);
  946. if (reqList == NULL) {
  947. //
  948. // Not much we can do about this, bail.
  949. //
  950. return STATUS_INSUFFICIENT_RESOURCES;
  951. }
  952. //
  953. // Second pass, build the resource list.
  954. //
  955. if (iterationCount != 0) {
  956. partial = inUse;
  957. ioResourceDescriptor = PdoExtension->Resources->Limit;
  958. }
  959. resource = reqList->List[0].Descriptors;
  960. for (index = 0;
  961. index < iterationCount;
  962. index++, partial++, ioResourceDescriptor++) {
  963. BOOLEAN passing;
  964. ULONG genericLength;
  965. ULONG genericAlignment;
  966. if (ioResourceDescriptor->Type == CmResourceTypeNull) {
  967. //
  968. // Nothing here.
  969. //
  970. continue;
  971. }
  972. //
  973. // Try to determine if the current setting for this resource
  974. // is (a) active (eg is's an IO resource and IO is enabled
  975. // for this device), and (b) valid.
  976. //
  977. passing = FALSE;
  978. genericLength = ioResourceDescriptor->u.Generic.Length;
  979. genericAlignment = ioResourceDescriptor->u.Generic.Alignment;
  980. if (partial->Type == CmResourceTypeNull) {
  981. //
  982. // Current setting is either not enabled or it's invalid
  983. // (h/w invalid ie the h/w will not see it as enabled).
  984. //
  985. // The base resource is for a bridged range, skip it
  986. // altogether.
  987. //
  988. if (genericLength == 0) {
  989. //
  990. // There is no boot setting for this bridge resource,
  991. // If the VGA bit is set, no need for a normal range.
  992. //
  993. if ((ioResourceDescriptor->Type == CmResourceTypeMemory) ||
  994. ((ioResourceDescriptor->Type == CmResourceTypePort) &&
  995. (PdoExtension->Dependent.type1.VgaBitSet == FALSE))) {
  996. switch (PciClassifyDeviceType(PdoExtension)) {
  997. case PciTypePciBridge:
  998. if (ioResourceDescriptor->Type == CmResourceTypeMemory) {
  999. PciBuildGraduatedWindow(ioResourceDescriptor,
  1000. PCI_GRADUATED_WINDOW_MAX,
  1001. PCI_GRADUATED_WINDOW_COUNT,
  1002. resource);
  1003. resource += PCI_GRADUATED_WINDOW_COUNT;
  1004. PciPrivateResourceInitialize(resource,
  1005. PciPrivateBar,
  1006. index);
  1007. resource++;
  1008. continue;
  1009. } else {
  1010. //
  1011. // Do the minium for IO space which is 4kb
  1012. //
  1013. genericLength = 0x1000;
  1014. genericAlignment = 0x1000;
  1015. }
  1016. break;
  1017. case PciTypeCardbusBridge:
  1018. if (ioResourceDescriptor->Type == CmResourceTypeMemory) {
  1019. PciBuildGraduatedWindow(ioResourceDescriptor,
  1020. PCI_GRADUATED_WINDOW_MAX,
  1021. PCI_GRADUATED_WINDOW_COUNT,
  1022. resource);
  1023. resource += PCI_GRADUATED_WINDOW_COUNT;
  1024. PciPrivateResourceInitialize(resource,
  1025. PciPrivateBar,
  1026. index);
  1027. resource++;
  1028. continue;
  1029. } else {
  1030. //
  1031. // Do the minium for IO space which is 256 bytes
  1032. //
  1033. genericLength = 0x100;
  1034. genericAlignment = 0x100;
  1035. }
  1036. break;
  1037. default:
  1038. //
  1039. // I don't know what this is.
  1040. // N.B. Can't actually get here.
  1041. //
  1042. continue;
  1043. }
  1044. } else {
  1045. //
  1046. // Not IO or memory? - Skip it altogether.
  1047. //
  1048. continue;
  1049. }
  1050. } else {
  1051. //
  1052. // Could be that it's a ROM that we don't actually want
  1053. // to report.
  1054. //
  1055. if ((ioResourceDescriptor->Type == CmResourceTypeMemory) &&
  1056. (ioResourceDescriptor->Flags == CM_RESOURCE_MEMORY_READ_ONLY)) {
  1057. continue;
  1058. }
  1059. }
  1060. } else {
  1061. //
  1062. // Current setting IS being passed. If it is a bridge,
  1063. // we will provide the current setting as preferred
  1064. // regardless. This may change one day, if it does
  1065. // we MUST get the length into the generic setting
  1066. // before we pass it into IO in the resource descriptor.
  1067. //
  1068. if ((genericLength == 0) // bridge
  1069. #if PCI_BOOT_CONFIG_PREFERRED
  1070. || (1) // always
  1071. #endif
  1072. )
  1073. {
  1074. passing = TRUE;
  1075. genericLength = partial->u.Generic.Length;
  1076. }
  1077. }
  1078. PciDebugPrint(PciDbgObnoxious,
  1079. " Index %d, Setting Base Resource,%s setting preferred.\n",
  1080. index,
  1081. passing ? "": " not"
  1082. );
  1083. ASSERT((resource + RESOURCES_PER_BAR + (passing ? 1 : 0) -
  1084. reqList->List[0].Descriptors) <= (LONG)baseResourceCount);
  1085. //
  1086. // Fill in the base resource descriptor.
  1087. //
  1088. *resource = *ioResourceDescriptor;
  1089. resource->ShareDisposition = CmResourceShareDeviceExclusive;
  1090. resource->u.Generic.Length = genericLength;
  1091. resource->u.Generic.Alignment = genericAlignment;
  1092. //
  1093. // Set the positive decode bit and 16 bit decode for all IO requirements
  1094. //
  1095. if (ioResourceDescriptor->Type == CmResourceTypePort) {
  1096. resource->Flags |= (CM_RESOURCE_PORT_POSITIVE_DECODE
  1097. | CM_RESOURCE_PORT_16_BIT_DECODE);
  1098. }
  1099. //
  1100. // If this device is decoding IO or Memory, and this resource
  1101. // is of that type, include the prefered settings in the list.
  1102. //
  1103. if (passing) {
  1104. extern BOOLEAN PciLockDeviceResources;
  1105. //
  1106. // Copy the set of descriptors we just created.
  1107. //
  1108. PciDebugPrint(PciDbgVerbose, " Duplicating for preferred locn.\n");
  1109. *(resource + 1) = *resource;
  1110. //
  1111. // Change the original to indicate it is the preferred
  1112. // setting and put the current settings into minimum
  1113. // address field, current setting + length into the max.
  1114. //
  1115. resource->Option = IO_RESOURCE_PREFERRED;
  1116. resource->u.Generic.MinimumAddress = partial->u.Generic.Start;
  1117. resource->u.Generic.MaximumAddress.QuadPart =
  1118. resource->u.Generic.MinimumAddress.QuadPart +
  1119. (resource->u.Generic.Length - 1);
  1120. //
  1121. // The preferred setting is fixed (Start + Length - 1 == End) and
  1122. // so alignment is not a restricting factor.
  1123. //
  1124. resource->u.Generic.Alignment = 1;
  1125. if (PciLockDeviceResources == TRUE ||
  1126. PdoExtension->LegacyDriver == TRUE ||
  1127. PdoExtension->OnDebugPath ||
  1128. (PCI_PARENT_FDOX(PdoExtension)->BusHackFlags & PCI_BUS_HACK_LOCK_RESOURCES) ||
  1129. //
  1130. // This is a work around for a pnp bug which affects Toshiba
  1131. // Satellite machines. We end up moving the PCI modem off its
  1132. // boot config because it is reserved (on 2f8 or 3f8) and then
  1133. // put a PCMCIA modem on top of it before it has been turned off.
  1134. // Stops before Starts would fix this but the driver folks say
  1135. // they can't deal with that so we need to fix this as part
  1136. // of the rebalance cleanup in 5.1
  1137. //
  1138. #if PCI_NO_MOVE_MODEM_IN_TOSHIBA
  1139. (PdoExtension->VendorId == 0x11c1
  1140. && PdoExtension->DeviceId == 0x0441
  1141. && PdoExtension->SubsystemVendorId == 0x1179
  1142. && (PdoExtension->SubsystemId == 0x0001 || PdoExtension->SubsystemId == 0x0002))
  1143. #endif
  1144. ) {
  1145. //
  1146. // Restrict the alternatives to the current settings.
  1147. //
  1148. *(resource + 1) = *resource;
  1149. }
  1150. (resource + 1)->Option = IO_RESOURCE_ALTERNATIVE;
  1151. //
  1152. // bump resource by one to allow for the one we just added.
  1153. //
  1154. resource++;
  1155. }
  1156. //
  1157. // A devicePrivateResource is used to keep track of which
  1158. // BAR this resource is derived from. Record it now because
  1159. // index may get bumped if this is a 64 bit memory BAR.
  1160. //
  1161. PciPrivateResourceInitialize(resource + 1,
  1162. PciPrivateBar,
  1163. index);
  1164. resource += RESOURCES_PER_BAR;
  1165. }
  1166. //
  1167. // Assign descriptors for interrupts.
  1168. //
  1169. if (generatesInterrupt) {
  1170. PciDebugPrint(PciDbgVerbose, " Assigning INT descriptor\n");
  1171. //
  1172. // Finally, fill in the base resource descriptor.
  1173. //
  1174. resource->Type = CmResourceTypeInterrupt;
  1175. resource->ShareDisposition = CmResourceShareShared;
  1176. resource->Option = 0;
  1177. resource->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
  1178. resource->u.Interrupt.MinimumVector = interruptMin;
  1179. resource->u.Interrupt.MaximumVector = interruptMax;
  1180. resource += (RESOURCES_PER_BAR - 1);
  1181. }
  1182. if (PdoExtension->AdditionalResourceCount != 0) {
  1183. //
  1184. // The header type dependent code indicated that it has
  1185. // resources to add. Call it back and allow it do add
  1186. // them now.
  1187. //
  1188. configurator->GetAdditionalResourceDescriptors(
  1189. PdoExtension,
  1190. CurrentConfig,
  1191. resource
  1192. );
  1193. resource += PdoExtension->AdditionalResourceCount;
  1194. }
  1195. //
  1196. // Done.
  1197. //
  1198. ASSERT(reqList->ListSize == (ULONG_PTR)resource - (ULONG_PTR)reqList);
  1199. #if DBG
  1200. PciDebugPrint(PciDbgPrattling,
  1201. "PCI build resource req - final resource count == %d\n",
  1202. resource - reqList->List[0].Descriptors);
  1203. ASSERT((resource - reqList->List[0].Descriptors) != 0);
  1204. #endif
  1205. //
  1206. // Return the address of the resource list and a successful status
  1207. // back to the caller.
  1208. //
  1209. *FinalReqList = reqList;
  1210. return STATUS_SUCCESS;
  1211. }
  1212. VOID
  1213. PciWriteLimitsAndRestoreCurrent(
  1214. IN PPCI_CONFIGURABLE_OBJECT This
  1215. )
  1216. //
  1217. // Overwrite the device's configuration space with the adjusted
  1218. // version then read it back to see what the device did with it.
  1219. //
  1220. {
  1221. if (This->PdoExtension->OnDebugPath) {
  1222. //
  1223. // If our debugger is bus mastering then dont clear this bit as it
  1224. // will blow away the DMA engine on the card and we dont currently
  1225. // re-program the card when we call KdEnableDebugger.
  1226. //
  1227. if (This->Command & PCI_ENABLE_BUS_MASTER){
  1228. This->Working->Command |= PCI_ENABLE_BUS_MASTER;
  1229. This->Current->Command |= PCI_ENABLE_BUS_MASTER;
  1230. }
  1231. KdDisableDebugger();
  1232. }
  1233. //
  1234. // Write out all those F's to work out which bits are sticky
  1235. //
  1236. PciSetConfigData(This->PdoExtension, This->Working);
  1237. //
  1238. // Read in which bits stuck
  1239. //
  1240. PciGetConfigData(This->PdoExtension, This->Working);
  1241. //
  1242. // Return the device to it's previous state by writing the
  1243. // original values back into it.
  1244. //
  1245. // Note: Don't enable anything up front (ie, Command = 0)
  1246. // because the Command register will get wriiten before
  1247. // the BARs are updated which might enable translations
  1248. // at undesirable locations.
  1249. //
  1250. PciSetConfigData(This->PdoExtension, This->Current);
  1251. //
  1252. // Now, return the command register to it's previous state.
  1253. //
  1254. This->Current->Command = This->Command;
  1255. //
  1256. // Only do the write if we are actually going to change the
  1257. // value of the command field.
  1258. //
  1259. if (This->Command != 0) {
  1260. PciSetCommandRegister(This->PdoExtension, This->Command);
  1261. }
  1262. //
  1263. // Restore the status field in the caller's buffer.
  1264. //
  1265. This->Current->Status = This->Status;
  1266. //
  1267. // Restore any type specific fields.
  1268. //
  1269. This->Configurator->RestoreCurrent(This);
  1270. if (This->PdoExtension->OnDebugPath) {
  1271. KdEnableDebugger();
  1272. }
  1273. #if DBG
  1274. //
  1275. // Check that what was written back stuck.
  1276. //
  1277. if (This->PdoExtension->ExpectedWritebackFailure == FALSE) {
  1278. PPCI_COMMON_CONFIG verifyConfig;
  1279. ULONG len;
  1280. verifyConfig = (PPCI_COMMON_CONFIG)
  1281. ((ULONG_PTR)This->Working + PCI_COMMON_HDR_LENGTH);
  1282. PciGetConfigData(This->PdoExtension, verifyConfig);
  1283. if ((len = (ULONG)RtlCompareMemory(
  1284. verifyConfig,
  1285. This->Current,
  1286. PCI_COMMON_HDR_LENGTH)) != PCI_COMMON_HDR_LENGTH) {
  1287. //
  1288. // Compare failed.
  1289. //
  1290. PciDebugPrint(PciDbgInformative,
  1291. "PCI - CFG space write verify failed at offset 0x%x\n",
  1292. len);
  1293. PciDebugDumpCommonConfig(verifyConfig);
  1294. }
  1295. }
  1296. #endif
  1297. }
  1298. NTSTATUS
  1299. PcipGetFunctionLimits(
  1300. IN PPCI_CONFIGURABLE_OBJECT This
  1301. )
  1302. /*++
  1303. Description:
  1304. Determine the limits for the Base Address Registers (BARs) for a
  1305. given Bus/Device/Function.
  1306. This is done by writing all ones the the BARs, reading them
  1307. back again. The hardware will adjust the values to it's limits
  1308. so we just store these new values away.
  1309. Arguments:
  1310. This - Pointer to the configuration object.
  1311. Return Value:
  1312. Returns status indicating the success or failure of this routine.
  1313. --*/
  1314. {
  1315. ULONG configType;
  1316. PPCI_COMMON_CONFIG current = This->Current;
  1317. PPCI_COMMON_CONFIG working = This->Working;
  1318. ULONG count;
  1319. PIO_RESOURCE_DESCRIPTOR ioResourceDescriptor;
  1320. PAGED_CODE();
  1321. //
  1322. // The first 16 bytes of configuration space are required by the
  1323. // PCI specification to be of the following format.
  1324. //
  1325. // 3 2 1 0
  1326. // +------------+------------+------------+------------+
  1327. // | Device ID | Vendor ID |
  1328. // +------------+------------+------------+------------+
  1329. // | Status | Command |
  1330. // +------------+------------+------------+------------+
  1331. // | Base Class | Sub Class | Progr. I/F | Revision ID|
  1332. // +------------+------------+------------+------------+
  1333. // | BIST | Header Type| Latency | Cache Ln Sz|
  1334. // +------------+------------+------------+------------+
  1335. //
  1336. // The status field in PCI Configuration space has its bits cleared
  1337. // by writing a one to each bit to be cleared. Zero the status
  1338. // field in the image of the current configuration space so writing
  1339. // to the hardware will not change it.
  1340. //
  1341. This->Status = current->Status;
  1342. current->Status = 0;
  1343. //
  1344. // Disable the device while it's configuration is being messed
  1345. // with.
  1346. //
  1347. This->Command = current->Command;
  1348. current->Command &= ~(PCI_ENABLE_IO_SPACE |
  1349. PCI_ENABLE_MEMORY_SPACE |
  1350. PCI_ENABLE_BUS_MASTER);
  1351. //
  1352. // Make a copy of the configuration space that was handed in.
  1353. // This copy will be modified and written to/read back from the
  1354. // device's configuration space to allow us to determine the
  1355. // limits for the device.
  1356. //
  1357. RtlCopyMemory(working, current, PCI_COMMON_HDR_LENGTH);
  1358. //
  1359. // Get the configuration type from the function's header.
  1360. // NOTE: We have already checked that it is valid so no
  1361. // further checking is needed here.
  1362. //
  1363. configType = PciGetConfigurationType(current);
  1364. //
  1365. // Set the configuration type dispatch table.
  1366. //
  1367. This->Configurator = &PciConfigurators[configType];
  1368. //
  1369. // Modify the "Working" copy of config space such that writing
  1370. // it to the hardware and reading it back again will enable us
  1371. // to determine the "limits" of this hardware's configurability.
  1372. //
  1373. This->Configurator->MassageHeaderForLimitsDetermination(This);
  1374. //
  1375. // Overwrite the device's configuration space with the adjusted
  1376. // version then read it back to see what the device did with it.
  1377. //
  1378. PciWriteLimitsAndRestoreCurrent(This);
  1379. //
  1380. // Allocate memory for limits and current usage.
  1381. //
  1382. // Note: This should NOT have been done already.
  1383. //
  1384. ASSERT(This->PdoExtension->Resources == NULL);
  1385. This->PdoExtension->Resources = ExAllocatePool(
  1386. NonPagedPool,
  1387. sizeof(PCI_FUNCTION_RESOURCES)
  1388. );
  1389. if (This->PdoExtension->Resources == NULL) {
  1390. //
  1391. // Couldn't get memory for this???
  1392. //
  1393. return STATUS_INSUFFICIENT_RESOURCES;
  1394. }
  1395. //
  1396. // Clear these structures.
  1397. //
  1398. // CmResourceTypeNull == 0, otherwise we need to init the Limits
  1399. // and current settings structures seperately.
  1400. //
  1401. RtlZeroMemory(
  1402. This->PdoExtension->Resources,
  1403. sizeof(PCI_FUNCTION_RESOURCES)
  1404. );
  1405. #if CmResourceTypeNull
  1406. for (count = 0; count < PCI_MAX_RANGE_COUNT; count++) {
  1407. This->PdoExtension->Resources->Limit[count].Type = CmResourceTypeNull;
  1408. This->PdoExtension->Resources->Current[count].Type = CmResourceTypeNull;
  1409. }
  1410. #endif
  1411. //
  1412. // Copy the limits and current settings into our device extension.
  1413. //
  1414. This->Configurator->SaveLimits(This);
  1415. This->Configurator->SaveCurrentSettings(This);
  1416. //
  1417. // If SaveLimits didn't find any resources, we can free the
  1418. // memory allocated to both limits and current settings. Note
  1419. // that we still must call SaveCurrentSettings because that
  1420. // routine is responsible for saving type specific data.
  1421. //
  1422. count = 0;
  1423. ioResourceDescriptor = This->PdoExtension->Resources->Limit +
  1424. PCI_MAX_RANGE_COUNT;
  1425. do {
  1426. ioResourceDescriptor--;
  1427. if (ioResourceDescriptor->Type != CmResourceTypeNull) {
  1428. //
  1429. // Some resource exists, get out.
  1430. //
  1431. count++;
  1432. break;
  1433. }
  1434. } while (ioResourceDescriptor != This->PdoExtension->Resources->Limit);
  1435. if (count == 0) {
  1436. //
  1437. // No resources.
  1438. //
  1439. ExFreePool(This->PdoExtension->Resources);
  1440. This->PdoExtension->Resources = NULL;
  1441. }
  1442. return STATUS_SUCCESS;
  1443. }
  1444. NTSTATUS
  1445. PciGetFunctionLimits(
  1446. IN PPCI_PDO_EXTENSION PdoExtension,
  1447. IN PPCI_COMMON_CONFIG CurrentConfig,
  1448. IN ULONGLONG Flags
  1449. )
  1450. /*++
  1451. Description:
  1452. Determine the limits for the Base Address Registers (BARs) for a
  1453. given Bus/Device/Function.
  1454. The work is really done by PcipGetFunctionLimits. This function is
  1455. a wrapper to handle the allocation/deallocation of working memory.
  1456. Arguments:
  1457. PdoExtension - PDO Extension for the device object to obtain the
  1458. limits for.
  1459. CurrentConfig - Existing contents of the PCI Common Configuration
  1460. Space for this function.
  1461. Return Value:
  1462. Returns status indicating the success or failure of this routine.
  1463. --*/
  1464. {
  1465. PPCI_COMMON_CONFIG workingConfig;
  1466. NTSTATUS status;
  1467. ULONG size;
  1468. PCI_CONFIGURABLE_OBJECT this;
  1469. PAGED_CODE();
  1470. //
  1471. // Check for anything the registry says we should not try
  1472. // to figure out the resources on. Examples are devices
  1473. // that don't consume resources but return garbage in their
  1474. // base address registers.
  1475. //
  1476. if (PciSkipThisFunction(CurrentConfig,
  1477. PdoExtension->Slot,
  1478. EnumResourceDetermination,
  1479. Flags) == TRUE) {
  1480. return STATUS_SUCCESS;
  1481. }
  1482. size = PCI_COMMON_HDR_LENGTH;
  1483. #if DBG
  1484. //
  1485. // If a checked build, we will verify the writeback of the
  1486. // orginal contents of configuration space. Allow enough
  1487. // space for a verification copy.
  1488. //
  1489. size *= 2;
  1490. #endif
  1491. workingConfig = ExAllocatePool(NonPagedPool, size);
  1492. if (workingConfig == NULL) {
  1493. //
  1494. // Failed to get memory to work with, bail.
  1495. //
  1496. return STATUS_INSUFFICIENT_RESOURCES;
  1497. }
  1498. this.Current = CurrentConfig;
  1499. this.Working = workingConfig;
  1500. this.PdoExtension = PdoExtension;
  1501. status = PcipGetFunctionLimits(&this);
  1502. ExFreePool(workingConfig);
  1503. return status;
  1504. }
  1505. VOID
  1506. PciProcessBus(
  1507. IN PPCI_FDO_EXTENSION ParentFdo
  1508. )
  1509. /*++
  1510. Routine Description:
  1511. Walk the child devices enumerated by PciScanBus and perform any processing
  1512. the needs to be done once all the children have been enumerated
  1513. Arguments:
  1514. ParentFdo - Our extension for the PCI bus functional device object.
  1515. Return Value:
  1516. NT status.
  1517. --*/
  1518. {
  1519. PPCI_PDO_EXTENSION current, vgaBridge = NULL, parentBridge = NULL;
  1520. PAGED_CODE();
  1521. if (!PCI_IS_ROOT_FDO(ParentFdo)) {
  1522. parentBridge = PCI_BRIDGE_PDO(ParentFdo);
  1523. }
  1524. //
  1525. // If our parent is a bridge with the ISA bit set, then set the ISA bit on
  1526. // all child bridges unless they are subtractive in which case we set the
  1527. // IsaRequired bit
  1528. //
  1529. if (parentBridge
  1530. && PciClassifyDeviceType(parentBridge) == PciTypePciBridge
  1531. && (parentBridge->Dependent.type1.IsaBitSet || parentBridge->Dependent.type1.IsaBitRequired)) {
  1532. for (current = ParentFdo->ChildBridgePdoList;
  1533. current;
  1534. current = current->NextBridge) {
  1535. //
  1536. // For now we only set the ISA bit on PCI-PCI bridges
  1537. //
  1538. if (PciClassifyDeviceType(current) == PciTypePciBridge) {
  1539. if (current->Dependent.type1.SubtractiveDecode) {
  1540. current->Dependent.type1.IsaBitRequired = TRUE;
  1541. } else {
  1542. current->Dependent.type1.IsaBitSet = TRUE;
  1543. current->UpdateHardware = TRUE;
  1544. }
  1545. }
  1546. }
  1547. } else {
  1548. //
  1549. // Scan the bridges enumerated to see if we need to set the ISA bit
  1550. //
  1551. for (current = ParentFdo->ChildBridgePdoList;
  1552. current;
  1553. current = current->NextBridge) {
  1554. if (current->Dependent.type1.VgaBitSet) {
  1555. vgaBridge = current;
  1556. break;
  1557. }
  1558. }
  1559. //
  1560. // If we have a bridge with the VGA bit set - set the ISA bit on all other
  1561. // bridges on this bus and force this to be written out to the hardware on
  1562. // the start.
  1563. //
  1564. if (vgaBridge) {
  1565. for (current = ParentFdo->ChildBridgePdoList;
  1566. current;
  1567. current = current->NextBridge) {
  1568. if (current != vgaBridge
  1569. && PciClassifyDeviceType(current) == PciTypePciBridge) {
  1570. //
  1571. // If this device is already started then we had better have already set the ISA bit
  1572. //
  1573. if (current->DeviceState == PciStarted) {
  1574. ASSERT(current->Dependent.type1.IsaBitRequired || current->Dependent.type1.IsaBitSet);
  1575. }
  1576. //
  1577. // If its a subtrative decode bridge remember we would have
  1578. // set the ISA bit so any children can inhereit it, otherwise
  1579. // set it and force it out to the hardware.
  1580. //
  1581. if (current->Dependent.type1.SubtractiveDecode) {
  1582. current->Dependent.type1.IsaBitRequired = TRUE;
  1583. } else {
  1584. current->Dependent.type1.IsaBitSet = TRUE;
  1585. current->UpdateHardware = TRUE;
  1586. }
  1587. }
  1588. }
  1589. }
  1590. }
  1591. //
  1592. // Check to see if there are any bridges in need of bus numbers and assign
  1593. // them if we are running on a machine where this is a good idea.
  1594. //
  1595. if (PciAssignBusNumbers) {
  1596. PciConfigureBusNumbers(ParentFdo);
  1597. }
  1598. }
  1599. NTSTATUS
  1600. PciScanBus(
  1601. IN PPCI_FDO_EXTENSION FdoExtension
  1602. )
  1603. /*++
  1604. Routine Description:
  1605. Scan the bus (detailed in FdoExtension) for any PCI devices/functions
  1606. elligible for control via a WDM driver.
  1607. Arguments:
  1608. FdoExtension - Our extension for the PCI bus functional device object.
  1609. Return Value:
  1610. NT status.
  1611. --*/
  1612. {
  1613. NTSTATUS status;
  1614. PCI_COMMON_HEADER commonHeader[2];
  1615. PPCI_COMMON_CONFIG commonConfig = (PPCI_COMMON_CONFIG)&commonHeader[0];
  1616. PPCI_COMMON_CONFIG biosConfig = (PPCI_COMMON_CONFIG)&commonHeader[1];
  1617. PDEVICE_OBJECT physicalDeviceObject;
  1618. PPCI_PDO_EXTENSION pdoExtension;
  1619. PCI_SLOT_NUMBER slot;
  1620. ULONG deviceNumber;
  1621. ULONG functionNumber;
  1622. ULONG pass;
  1623. USHORT SubVendorID, SubSystemID;
  1624. BOOLEAN isRoot;
  1625. ULONGLONG hackFlags;
  1626. ULONG maximumDevices;
  1627. PIO_RESOURCE_REQUIREMENTS_LIST tempRequirementsList;
  1628. BOOLEAN newDevices = FALSE;
  1629. UCHAR secondary;
  1630. PciDebugPrint(PciDbgPrattling,
  1631. "PCI Scan Bus: FDO Extension @ 0x%x, Base Bus = 0x%x\n",
  1632. FdoExtension,
  1633. FdoExtension->BaseBus);
  1634. isRoot = PCI_IS_ROOT_FDO(FdoExtension);
  1635. //
  1636. // Examine each possible device on this bus.
  1637. //
  1638. maximumDevices = PCI_MAX_DEVICES;
  1639. if (!isRoot) {
  1640. //
  1641. // Examine the PDO extension for the bridge device and see
  1642. // if it's broken.
  1643. //
  1644. pdoExtension = (PPCI_PDO_EXTENSION)
  1645. FdoExtension->PhysicalDeviceObject->DeviceExtension;
  1646. ASSERT_PCI_PDO_EXTENSION(pdoExtension);
  1647. if (pdoExtension->HackFlags & PCI_HACK_ONE_CHILD) {
  1648. maximumDevices = 1;
  1649. }
  1650. //
  1651. // NEC program the bus number in their _DCK method, unfortunatley we have already
  1652. // done it! So detect someone else reprogramming the bus number and restore
  1653. // the correct one!
  1654. //
  1655. PciReadDeviceConfig(pdoExtension,
  1656. &secondary,
  1657. FIELD_OFFSET(PCI_COMMON_CONFIG, u.type1.SecondaryBus),
  1658. sizeof(UCHAR)
  1659. );
  1660. if (secondary != pdoExtension->Dependent.type1.SecondaryBus) {
  1661. DbgPrint("PCI: Bus numbers have been changed! Restoring originals.\n");
  1662. PciSetBusNumbers(pdoExtension,
  1663. pdoExtension->Dependent.type1.PrimaryBus,
  1664. pdoExtension->Dependent.type1.SecondaryBus,
  1665. pdoExtension->Dependent.type1.SubordinateBus
  1666. );
  1667. }
  1668. }
  1669. slot.u.AsULONG = 0;
  1670. for (deviceNumber = 0;
  1671. deviceNumber < maximumDevices;
  1672. deviceNumber++) {
  1673. slot.u.bits.DeviceNumber = deviceNumber;
  1674. //
  1675. // Examine each possible function on this device.
  1676. // N.B. Early out if function 0 not present.
  1677. //
  1678. for (functionNumber = 0;
  1679. functionNumber < PCI_MAX_FUNCTION;
  1680. functionNumber++) {
  1681. #if defined(_AMD64_SIMULATOR_)
  1682. if (deviceNumber == 7 && functionNumber == 2) {
  1683. //
  1684. // The simulator is reporting an IDE controller here, but
  1685. // it's really a USB controller. Ignore this one.
  1686. //
  1687. break;
  1688. }
  1689. #endif
  1690. slot.u.bits.FunctionNumber = functionNumber;
  1691. PciReadSlotConfig(FdoExtension,
  1692. slot,
  1693. commonConfig,
  1694. 0,
  1695. sizeof(commonConfig->VendorID)
  1696. );
  1697. if (commonConfig->VendorID == 0xFFFF ||
  1698. commonConfig->VendorID == 0) {
  1699. if (functionNumber == 0) {
  1700. //
  1701. // Didn't get any data on function zero of this
  1702. // device, no point in checking other functions.
  1703. //
  1704. break;
  1705. } else {
  1706. //
  1707. // Check next function.
  1708. //
  1709. continue;
  1710. }
  1711. }
  1712. //
  1713. // We have a device so get the rest of its config space
  1714. //
  1715. PciReadSlotConfig(FdoExtension,
  1716. slot,
  1717. &commonConfig->DeviceID,
  1718. FIELD_OFFSET(PCI_COMMON_CONFIG, DeviceID),
  1719. sizeof(PCI_COMMON_HEADER)
  1720. - sizeof(commonConfig->VendorID)
  1721. );
  1722. //
  1723. // Munge the config space if necessary
  1724. //
  1725. PciApplyHacks(FdoExtension,
  1726. commonConfig,
  1727. slot,
  1728. EnumHackConfigSpace,
  1729. NULL
  1730. );
  1731. #if DBG
  1732. {
  1733. ULONG i;
  1734. PWSTR descr;
  1735. i = 0x8000000 |
  1736. (FdoExtension->BaseBus << 16) |
  1737. (deviceNumber << 11) |
  1738. (functionNumber << 8);
  1739. PciDebugPrint(PciDbgPrattling,
  1740. "Scan Found Device 0x%x (b=0x%x, d=0x%x, f=0x%x)\n",
  1741. i,
  1742. FdoExtension->BaseBus,
  1743. deviceNumber,
  1744. functionNumber);
  1745. PciDebugDumpCommonConfig(commonConfig);
  1746. descr = PciGetDeviceDescriptionMessage(
  1747. commonConfig->BaseClass,
  1748. commonConfig->SubClass);
  1749. PciDebugPrint(PciDbgPrattling,
  1750. "Device Description \"%S\".\n",
  1751. descr ? descr : L"(NULL)");
  1752. if (descr) {
  1753. ExFreePool(descr);
  1754. }
  1755. }
  1756. #endif
  1757. if ((PciGetConfigurationType(commonConfig) == PCI_DEVICE_TYPE) &&
  1758. (commonConfig->BaseClass != PCI_CLASS_BRIDGE_DEV)) {
  1759. SubVendorID = commonConfig->u.type0.SubVendorID;
  1760. SubSystemID = commonConfig->u.type0.SubSystemID;
  1761. } else {
  1762. SubVendorID = 0;
  1763. SubSystemID = 0;
  1764. }
  1765. hackFlags = PciGetHackFlags(commonConfig->VendorID,
  1766. commonConfig->DeviceID,
  1767. SubVendorID,
  1768. SubSystemID,
  1769. commonConfig->RevisionID
  1770. );
  1771. if (PciSkipThisFunction(commonConfig,
  1772. slot,
  1773. EnumBusScan,
  1774. hackFlags)) {
  1775. //
  1776. // Skip this function
  1777. //
  1778. continue;
  1779. }
  1780. //
  1781. // In case we are rescanning the bus, check to see if
  1782. // a PDO for this device already exists as a child of
  1783. // the FDO.
  1784. //
  1785. pdoExtension = PciFindPdoByFunction(
  1786. FdoExtension,
  1787. slot,
  1788. commonConfig);
  1789. if (pdoExtension == NULL) {
  1790. //
  1791. // Create a PDO for this new device.
  1792. //
  1793. newDevices = TRUE;
  1794. status = PciPdoCreate(FdoExtension,
  1795. slot,
  1796. &physicalDeviceObject);
  1797. if (!NT_SUCCESS(status)) {
  1798. ASSERT(NT_SUCCESS(status));
  1799. return status;
  1800. }
  1801. pdoExtension = (PPCI_PDO_EXTENSION)
  1802. physicalDeviceObject->DeviceExtension;
  1803. if (hackFlags & PCI_HACK_FAKE_CLASS_CODE) {
  1804. commonConfig->BaseClass = PCI_CLASS_BASE_SYSTEM_DEV;
  1805. commonConfig->SubClass = PCI_SUBCLASS_SYS_OTHER;
  1806. #if DBG
  1807. pdoExtension->ExpectedWritebackFailure = TRUE;
  1808. #endif
  1809. }
  1810. //
  1811. // Record device identification and type info.
  1812. //
  1813. pdoExtension->VendorId = commonConfig->VendorID;
  1814. pdoExtension->DeviceId = commonConfig->DeviceID;
  1815. pdoExtension->RevisionId = commonConfig->RevisionID;
  1816. pdoExtension->ProgIf = commonConfig->ProgIf;
  1817. pdoExtension->SubClass = commonConfig->SubClass;
  1818. pdoExtension->BaseClass = commonConfig->BaseClass;
  1819. pdoExtension->HeaderType =
  1820. PciGetConfigurationType(commonConfig);
  1821. //
  1822. // If this is a bridge (PCI-PCI or Cardbus) then insert into
  1823. // the list of child bridges for this bus
  1824. //
  1825. if (pdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV
  1826. && (pdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI
  1827. || pdoExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS)) {
  1828. PPCI_PDO_EXTENSION *current;
  1829. //
  1830. // Insert at the end of the list
  1831. //
  1832. ExAcquireFastMutex(&FdoExtension->ChildListMutex);
  1833. current = &FdoExtension->ChildBridgePdoList;
  1834. while (*current) {
  1835. current = &((*current)->NextBridge);
  1836. }
  1837. *current = pdoExtension;
  1838. ASSERT(pdoExtension->NextBridge == NULL);
  1839. ExReleaseFastMutex(&FdoExtension->ChildListMutex);
  1840. }
  1841. //
  1842. // See if we have already cached info for this device
  1843. //
  1844. status = PciGetBiosConfig(pdoExtension,
  1845. biosConfig
  1846. );
  1847. if (NT_SUCCESS(status)) {
  1848. //
  1849. // Check if its the same device
  1850. //
  1851. if (PcipIsSameDevice(pdoExtension, biosConfig)) {
  1852. //
  1853. // Write the BiosConfig InterruptLine out to the hardware
  1854. // now and don't wait until start as many HALs will fail in
  1855. // PciGetAdjustedInterruptLine if we don't
  1856. //
  1857. if (biosConfig->u.type1.InterruptLine
  1858. != commonConfig->u.type1.InterruptLine) {
  1859. PciWriteDeviceConfig(pdoExtension,
  1860. &biosConfig->u.type1.InterruptLine,
  1861. FIELD_OFFSET(PCI_COMMON_CONFIG,
  1862. u.type1.InterruptLine),
  1863. sizeof(UCHAR)
  1864. );
  1865. }
  1866. pdoExtension->RawInterruptLine
  1867. = biosConfig->u.type0.InterruptLine;
  1868. pdoExtension->InitialCommand = biosConfig->Command;
  1869. } else {
  1870. //
  1871. // Its a different device so blow away the old bios
  1872. // config
  1873. //
  1874. status = STATUS_UNSUCCESSFUL;
  1875. }
  1876. }
  1877. if (!NT_SUCCESS(status)) {
  1878. //
  1879. // Write out the BiosConfig from the config space we just
  1880. // read from the hardware
  1881. //
  1882. status = PciSaveBiosConfig(pdoExtension,
  1883. commonConfig
  1884. );
  1885. ASSERT(NT_SUCCESS(status));
  1886. pdoExtension->RawInterruptLine
  1887. = commonConfig->u.type0.InterruptLine;
  1888. pdoExtension->InitialCommand = commonConfig->Command;
  1889. }
  1890. //
  1891. // Save the command register so we can restore the appropriate bits
  1892. //
  1893. pdoExtension->CommandEnables = commonConfig->Command;
  1894. //
  1895. // Save the device flags so we don't need to go to
  1896. // the registry all the time.
  1897. //
  1898. pdoExtension->HackFlags = hackFlags;
  1899. //
  1900. // See if we have any capabilities for this device
  1901. //
  1902. PciGetEnhancedCapabilities(pdoExtension, commonConfig);
  1903. //
  1904. // Before we calculate the Bar length, or get the capabilities
  1905. // we may need to set the device to D0. N.B. This does *not*
  1906. // update the power state stored in the pdoExtension.
  1907. //
  1908. PciSetPowerManagedDevicePowerState(
  1909. pdoExtension,
  1910. PowerDeviceD0,
  1911. FALSE
  1912. );
  1913. //
  1914. // Apply any hacks we know about for this device
  1915. //
  1916. PciApplyHacks(FdoExtension,
  1917. commonConfig,
  1918. slot,
  1919. EnumBusScan,
  1920. pdoExtension
  1921. );
  1922. //
  1923. // The interrupt number we report in the config data is obtained
  1924. // from the HAL rather than the hardware's config space.
  1925. //
  1926. pdoExtension->InterruptPin = commonConfig->u.type0.InterruptPin;
  1927. pdoExtension->AdjustedInterruptLine = PciGetAdjustedInterruptLine(pdoExtension);
  1928. //
  1929. // Work out if we are on the debug path
  1930. //
  1931. pdoExtension->OnDebugPath = PciIsDeviceOnDebugPath(pdoExtension);
  1932. //
  1933. // Get the IO and MEMORY limits for this device. This
  1934. // is a hardware thing and will never change so we keep
  1935. // it in the PDO extension for future reference.
  1936. //
  1937. status = PciGetFunctionLimits(pdoExtension,
  1938. commonConfig,
  1939. hackFlags);
  1940. //
  1941. // NTRAID #62636 - 4/20/2000 - andrewth
  1942. // We are going to expose a PDO. Why not just let the OS put
  1943. // into whatever Dstate it feels?
  1944. //
  1945. PciSetPowerManagedDevicePowerState(
  1946. pdoExtension,
  1947. pdoExtension->PowerState.CurrentDeviceState,
  1948. FALSE
  1949. );
  1950. //
  1951. // Currently, this only returns errors on memory allocation.
  1952. //
  1953. if (!NT_SUCCESS(status)) {
  1954. ASSERT(NT_SUCCESS(status));
  1955. PciPdoDestroy(physicalDeviceObject);
  1956. return status;
  1957. }
  1958. //
  1959. // If the device's SubSystem ID fields are not
  1960. // guaranteed to be the same when we enumerate
  1961. // the device after reapplying power to it (ie
  1962. // they depend on the BIOS to initialize it),
  1963. // then pretend it doesn't have a SubSystem ID
  1964. // at all.
  1965. //
  1966. if (hackFlags & PCI_HACK_NO_SUBSYSTEM) {
  1967. pdoExtension->SubsystemVendorId = 0;
  1968. pdoExtension->SubsystemId = 0;
  1969. }
  1970. #if DBG
  1971. //
  1972. // Dump the capabilities list.
  1973. //
  1974. {
  1975. union _cap_buffer {
  1976. PCI_CAPABILITIES_HEADER header;
  1977. PCI_PM_CAPABILITY pm;
  1978. PCI_AGP_CAPABILITY agp;
  1979. } cap;
  1980. UCHAR capOffset = pdoExtension->CapabilitiesPtr;
  1981. PUCHAR capStr;
  1982. ULONG nshort;
  1983. PUSHORT capData;
  1984. //
  1985. // Run the list.
  1986. //
  1987. while (capOffset != 0) {
  1988. UCHAR tmpOffset;
  1989. tmpOffset = PciReadDeviceCapability(
  1990. pdoExtension,
  1991. capOffset,
  1992. 0, // match ANY ID
  1993. &cap,
  1994. sizeof(cap.header)
  1995. );
  1996. if (tmpOffset != capOffset) {
  1997. //
  1998. // Sanity check only, this can't happen.
  1999. //
  2000. PciDebugPrint(
  2001. PciDbgAlways,
  2002. "PCI - Failed to read PCI capability at offset 0x%02x\n",
  2003. capOffset
  2004. );
  2005. ASSERT(tmpOffset == capOffset);
  2006. break;
  2007. }
  2008. //
  2009. // Depending on the Capability ID, the amount
  2010. // of data varies.
  2011. //
  2012. switch (cap.header.CapabilityID) {
  2013. case PCI_CAPABILITY_ID_POWER_MANAGEMENT:
  2014. capStr = "POWER";
  2015. nshort = 3;
  2016. tmpOffset = PciReadDeviceCapability(
  2017. pdoExtension,
  2018. capOffset,
  2019. cap.header.CapabilityID,
  2020. &cap,
  2021. sizeof(cap.pm)
  2022. );
  2023. break;
  2024. case PCI_CAPABILITY_ID_AGP:
  2025. capStr = "AGP";
  2026. nshort = 5;
  2027. tmpOffset = PciReadDeviceCapability(
  2028. pdoExtension,
  2029. capOffset,
  2030. cap.header.CapabilityID,
  2031. &cap,
  2032. sizeof(cap.agp)
  2033. );
  2034. break;
  2035. default:
  2036. capStr = "UNKNOWN CAPABILITY";
  2037. nshort = 0;
  2038. break;
  2039. }
  2040. PciDebugPrint(
  2041. PciDbgPrattling,
  2042. "CAP @%02x ID %02x (%s)",
  2043. capOffset,
  2044. cap.header.CapabilityID,
  2045. capStr
  2046. );
  2047. if (tmpOffset != capOffset) {
  2048. //
  2049. // Sanity check only, this can't happen.
  2050. //
  2051. PciDebugPrint(
  2052. PciDbgAlways,
  2053. "- Failed to read capability data. ***\n"
  2054. );
  2055. ASSERT(tmpOffset == capOffset);
  2056. break;
  2057. }
  2058. capData = ((PUSHORT)&cap) + 1;
  2059. while (nshort--) {
  2060. PciDebugPrint(
  2061. PciDbgPrattling,
  2062. " %04x",
  2063. *capData++
  2064. );
  2065. }
  2066. PciDebugPrint(PciDbgPrattling, "\n");
  2067. //
  2068. // Advance to the next entry in the list.
  2069. //
  2070. capOffset = cap.header.Next;
  2071. }
  2072. }
  2073. #endif
  2074. //
  2075. // Don't allow Power Down to legacy type busses (ISA/EISA/
  2076. // MCA). Who knows what sort of unenumerated devices
  2077. // might be out there that the system is dependent on.
  2078. //
  2079. #ifdef PCIIDE_HACKS
  2080. //
  2081. // NTRAID #103766 - 4/20/2000 - andrewth
  2082. // This needs to be removed
  2083. // Also, don't allow an IDE device to power itself off.
  2084. //
  2085. if (pdoExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR &&
  2086. pdoExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR) {
  2087. pdoExtension->DisablePowerDown = TRUE;
  2088. }
  2089. #endif
  2090. if ((pdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV &&
  2091. (pdoExtension->SubClass == PCI_SUBCLASS_BR_ISA ||
  2092. pdoExtension->SubClass == PCI_SUBCLASS_BR_EISA ||
  2093. pdoExtension->SubClass == PCI_SUBCLASS_BR_MCA)) ||
  2094. (pdoExtension->VendorId == 0x8086 &&
  2095. pdoExtension->DeviceId == 0x0482)) {
  2096. pdoExtension->DisablePowerDown = TRUE;
  2097. }
  2098. //
  2099. // Try to determine if this device looks like it was hot plugged
  2100. // we assume that if IO, Mem and BusMaster bits are off and no
  2101. // one has initialized either the latency timer or the cache line
  2102. // size they should be initialized.
  2103. //
  2104. if (((pdoExtension->CommandEnables & (PCI_ENABLE_IO_SPACE
  2105. | PCI_ENABLE_MEMORY_SPACE
  2106. | PCI_ENABLE_BUS_MASTER)) == 0)
  2107. && commonConfig->LatencyTimer == 0
  2108. && commonConfig->CacheLineSize == 0) {
  2109. PciDebugPrint(
  2110. PciDbgConfigParam,
  2111. "PCI - ScanBus, PDOx %x found unconfigured\n",
  2112. pdoExtension
  2113. );
  2114. //
  2115. // Remember we need to configure this in PciSetResources
  2116. //
  2117. pdoExtension->NeedsHotPlugConfiguration = TRUE;
  2118. }
  2119. //
  2120. // Save the Latency Timer and Cache Line Size
  2121. // registers. These were set by the BIOS on
  2122. // power up but might need to be reset by the
  2123. // OS if the device is powered down/up by the
  2124. // OS without a reboot.
  2125. //
  2126. pdoExtension->SavedLatencyTimer =
  2127. commonConfig->LatencyTimer;
  2128. pdoExtension->SavedCacheLineSize =
  2129. commonConfig->CacheLineSize;
  2130. //
  2131. // We are able to receive IRPs for this device now.
  2132. //
  2133. physicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
  2134. } else {
  2135. //
  2136. // A PDO already exists for this device.
  2137. //
  2138. pdoExtension->NotPresent = FALSE;
  2139. ASSERT(pdoExtension->DeviceState != PciDeleted);
  2140. }
  2141. if ( (functionNumber == 0) &&
  2142. !PCI_MULTIFUNCTION_DEVICE(commonConfig) ) {
  2143. //
  2144. // Not a multifunction adapter, skip other functions on
  2145. // this device.
  2146. //
  2147. break;
  2148. }
  2149. } // function loop
  2150. } // device loop
  2151. //
  2152. // Perform any post processing on the devices for this bridge if we found any
  2153. // new devices
  2154. //
  2155. if (newDevices) {
  2156. PciProcessBus(FdoExtension);
  2157. }
  2158. return STATUS_SUCCESS;
  2159. }
  2160. NTSTATUS
  2161. PciQueryRequirements(
  2162. IN PPCI_PDO_EXTENSION PdoExtension,
  2163. OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementsList
  2164. )
  2165. /*++
  2166. Routine Description:
  2167. Calculate the device's resource requirements from PCI Config space.
  2168. Arguments:
  2169. PdoExtension - Pdo Extension for the device (object) whose
  2170. requirements are needed.
  2171. RequirementsList - Returns the address of the requirements list.
  2172. Return Value:
  2173. NT status.
  2174. --*/
  2175. {
  2176. NTSTATUS status;
  2177. PCI_COMMON_HEADER commonHeader;
  2178. PPCI_COMMON_CONFIG commonConfig = (PPCI_COMMON_CONFIG)&commonHeader;
  2179. PAGED_CODE();
  2180. //
  2181. // Early out, if the device has no CM or IO resources and doesn't
  2182. // use interrupts,... it doesn't have any resource requirements.
  2183. //
  2184. if ((PdoExtension->Resources == NULL) &&
  2185. (PdoExtension->InterruptPin == 0)) {
  2186. PciDebugPrint(
  2187. PciDbgPrattling,
  2188. "PciQueryRequirements returning NULL requirements list\n");
  2189. *RequirementsList = NULL;
  2190. return STATUS_SUCCESS;
  2191. }
  2192. //
  2193. // Get the config space for the device (still needed to gen
  2194. // a requirements list. This should be changed so the PDOx
  2195. // has enough info to do it without going to the h/w again).
  2196. //
  2197. PciGetConfigData(PdoExtension, commonConfig);
  2198. status = PciBuildRequirementsList(PdoExtension,
  2199. commonConfig,
  2200. RequirementsList);
  2201. if (!NT_SUCCESS(status)) {
  2202. return status;
  2203. }
  2204. //
  2205. // Check if this is the broken Compaq hot-plug controller that
  2206. // is integrated into the Profusion chipset. It only does a 32bit
  2207. // decode in a 64bit address space... Does this seem familiar to anyone...
  2208. // can you say ISA aliasing!
  2209. //
  2210. // The solution is to disable the memory decode. This was done earlier in
  2211. // PciApplyHacks from PciScan Bus. But so that the user gets to keep the
  2212. // hot-plug functionality we need to still enumerate but prune out the
  2213. // memory requirement and rely on the fact that the registers can be
  2214. // accessed through config space.
  2215. //
  2216. // Only do this on machines with PAE enabled as they can have > 4GB.
  2217. // Note that this will only work on x86 machines but this is an x86 only
  2218. // chipset. Only revision 0x11 was broken.
  2219. //
  2220. if (commonConfig->VendorID == 0x0e11
  2221. && commonConfig->DeviceID == 0xa0f7
  2222. && commonConfig->RevisionID == 0x11
  2223. && ExIsProcessorFeaturePresent(PF_PAE_ENABLED)) {
  2224. PIO_RESOURCE_DESCRIPTOR current;
  2225. //
  2226. // Prune out the memory requirement
  2227. //
  2228. FOR_ALL_IN_ARRAY((*RequirementsList)->List[0].Descriptors,
  2229. (*RequirementsList)->List[0].Count,
  2230. current) {
  2231. if (current->Type == CmResourceTypeMemory) {
  2232. PIO_RESOURCE_DESCRIPTOR lookahead = current + 1;
  2233. current->Type = CmResourceTypeNull;
  2234. if (lookahead < ((*RequirementsList)->List[0].Descriptors +
  2235. (*RequirementsList)->List[0].Count)) {
  2236. if (lookahead->Type == CmResourceTypeDevicePrivate) {
  2237. lookahead->Type = CmResourceTypeNull;
  2238. current++;
  2239. }
  2240. }
  2241. }
  2242. }
  2243. }
  2244. if (*RequirementsList == PciZeroIoResourceRequirements) {
  2245. //
  2246. // This device (function) has no resources, return NULL
  2247. // intead of our zero list.
  2248. //
  2249. *RequirementsList = NULL;
  2250. #if DBG
  2251. PciDebugPrint(PciDbgPrattling, "Returning NULL requirements list\n");
  2252. } else {
  2253. PciDebugPrintIoResReqList(*RequirementsList);
  2254. #endif
  2255. }
  2256. return STATUS_SUCCESS;
  2257. }
  2258. NTSTATUS
  2259. PciQueryResources(
  2260. IN PPCI_PDO_EXTENSION PdoExtension,
  2261. OUT PCM_RESOURCE_LIST *ResourceList
  2262. )
  2263. /*++
  2264. Routine Description:
  2265. Given a pointer to a PCI PDO, this routine allocates and returns a pointer
  2266. to the resource description of that PDO.
  2267. Arguments:
  2268. PdoExtension - Our extension for the PCI-enumerated physical device object.
  2269. ResourceList - Used to return a pointer to the resource list.
  2270. Return Value:
  2271. NT status.
  2272. --*/
  2273. {
  2274. ULONG i;
  2275. ULONG resourceCount;
  2276. ULONG statusCommand;
  2277. PCM_RESOURCE_LIST cmResourceList;
  2278. PCM_PARTIAL_RESOURCE_DESCRIPTOR resource, lastResource;
  2279. PCM_PARTIAL_RESOURCE_DESCRIPTOR current;
  2280. BOOLEAN enabledMemory;
  2281. BOOLEAN enabledIo;
  2282. BOOLEAN deviceIsABridge = FALSE;
  2283. PCI_OBJECT_TYPE deviceType;
  2284. USHORT command;
  2285. PAGED_CODE();
  2286. *ResourceList = NULL;
  2287. //
  2288. // Get a count of the resources.
  2289. //
  2290. if (PdoExtension->Resources == NULL) {
  2291. //
  2292. // This device has no resources, successfully return
  2293. // a NULL resource list.
  2294. //
  2295. return STATUS_SUCCESS;
  2296. }
  2297. //
  2298. // Seeing as other drivers (esp VideoPort for multimon) can change the
  2299. // enables for this device re-read the hardware to ensure we are correct.
  2300. //
  2301. PciGetCommandRegister(PdoExtension, &command);
  2302. enabledMemory = BITS_SET(command, PCI_ENABLE_MEMORY_SPACE);
  2303. enabledIo = BITS_SET(command, PCI_ENABLE_IO_SPACE);
  2304. resourceCount = 0;
  2305. current = PdoExtension->Resources->Current;
  2306. for (i = 0; i < PCI_MAX_RANGE_COUNT; i++, current++) {
  2307. if ((enabledMemory && (current->Type == CmResourceTypeMemory))
  2308. || (enabledIo && (current->Type == CmResourceTypePort))) {
  2309. resourceCount++;
  2310. }
  2311. }
  2312. if (PdoExtension->InterruptPin && (enabledMemory || enabledIo)) {
  2313. if (PdoExtension->AdjustedInterruptLine != 0 && PdoExtension->AdjustedInterruptLine != 0xFF) {
  2314. resourceCount += 1;
  2315. }
  2316. }
  2317. if (resourceCount == 0) {
  2318. //
  2319. // Device has no resources currently enabled.
  2320. //
  2321. return STATUS_SUCCESS;
  2322. }
  2323. //
  2324. // Allocate a CM Resource List large enough to handle this
  2325. // device's resources.
  2326. //
  2327. cmResourceList = PciAllocateCmResourceList(
  2328. resourceCount,
  2329. PCI_PARENT_FDOX(PdoExtension)->BaseBus
  2330. );
  2331. if (cmResourceList == NULL) {
  2332. return STATUS_INSUFFICIENT_RESOURCES;
  2333. }
  2334. resource = PciFirstCmResource(cmResourceList);
  2335. lastResource = resource + resourceCount;
  2336. //
  2337. // Copy the resources from the PDO's in-use resource table to
  2338. // the output resource list - the ISA bit is set will be dealt with in
  2339. // the arbiters - just as for resource requirements.
  2340. //
  2341. current = PdoExtension->Resources->Current;
  2342. for (i = 0; i < PCI_MAX_RANGE_COUNT; i++, current++) {
  2343. if (enabledMemory && (current->Type == CmResourceTypeMemory)) {
  2344. *resource++ = *current;
  2345. } else if (enabledIo && (current->Type == CmResourceTypePort)) {
  2346. *resource++ = *current;
  2347. }
  2348. }
  2349. if (PdoExtension->InterruptPin && (enabledMemory || enabledIo)) {
  2350. if (PdoExtension->AdjustedInterruptLine != 0 && PdoExtension->AdjustedInterruptLine != 0xFF) {
  2351. ASSERT(resource < lastResource);
  2352. resource->Type = CmResourceTypeInterrupt;
  2353. resource->ShareDisposition = CmResourceShareShared;
  2354. resource->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;;
  2355. resource->u.Interrupt.Level =
  2356. resource->u.Interrupt.Vector = PdoExtension->AdjustedInterruptLine;
  2357. resource->u.Interrupt.Affinity = (ULONG)-1;
  2358. }
  2359. }
  2360. //
  2361. // Return the list and indicate success.
  2362. //
  2363. *ResourceList = cmResourceList;
  2364. return STATUS_SUCCESS;
  2365. }
  2366. NTSTATUS
  2367. PciQueryDeviceRelations(
  2368. IN PPCI_FDO_EXTENSION FdoExtension,
  2369. IN OUT PDEVICE_RELATIONS *PDeviceRelations
  2370. )
  2371. /*++
  2372. Routine Description:
  2373. This function builds a DEVICE_RELATIONS structure containing an array
  2374. of pointers to physical device objects for the devices of the specified
  2375. type on the bus indicated by FdoExtension.
  2376. Arguments:
  2377. FdoExtension - Pointer to the FDO Extension for the bus itself.
  2378. PDeviceRelations - Used to return the pointer to the allocated
  2379. device relations structure.
  2380. Return Value:
  2381. Returns the status of the operation.
  2382. --*/
  2383. {
  2384. ULONG pdoCount;
  2385. PPCI_PDO_EXTENSION childPdo;
  2386. PDEVICE_RELATIONS deviceRelations;
  2387. PDEVICE_RELATIONS oldDeviceRelations;
  2388. ULONG deviceRelationsSize;
  2389. PDEVICE_OBJECT physicalDeviceObject;
  2390. PDEVICE_OBJECT *object;
  2391. NTSTATUS status;
  2392. PAGED_CODE();
  2393. //
  2394. // Check that it reasonable to perform this operation now.
  2395. //
  2396. if (FdoExtension->DeviceState != PciStarted) {
  2397. ASSERT(FdoExtension->DeviceState == PciStarted);
  2398. return STATUS_INVALID_DEVICE_REQUEST;
  2399. }
  2400. //
  2401. // We're going to mess with the child pdo list - lock the state...
  2402. //
  2403. status = PCI_ACQUIRE_STATE_LOCK(FdoExtension);
  2404. if (!NT_SUCCESS(status)) {
  2405. return status;
  2406. }
  2407. //
  2408. // Run down the existing child list and flag each child as
  2409. // not present. This flag will be cleared by the bus
  2410. // scan when (/if) the device is still present. Any pdo
  2411. // with the flag still present after the scan is no longer
  2412. // in the system (could be powered off).
  2413. //
  2414. childPdo = FdoExtension->ChildPdoList;
  2415. while (childPdo != NULL) {
  2416. childPdo->NotPresent = TRUE;
  2417. childPdo = childPdo->Next;
  2418. }
  2419. //
  2420. // Enumerate the bus.
  2421. //
  2422. status = PciScanBus(FdoExtension);
  2423. if (!NT_SUCCESS(status)) {
  2424. ASSERT(NT_SUCCESS(status));
  2425. goto cleanup;
  2426. }
  2427. //
  2428. // First count the child PDOs
  2429. //
  2430. pdoCount = 0;
  2431. childPdo = FdoExtension->ChildPdoList;
  2432. while (childPdo != NULL) {
  2433. if (childPdo->NotPresent == FALSE) {
  2434. pdoCount++;
  2435. } else {
  2436. childPdo->ReportedMissing = TRUE;
  2437. #if DBG
  2438. PciDebugPrint(
  2439. PciDbgObnoxious,
  2440. "PCI - Old device (pdox) %08x not found on rescan.\n",
  2441. childPdo
  2442. );
  2443. #endif
  2444. }
  2445. childPdo = childPdo->Next;
  2446. }
  2447. //
  2448. // Calculate the amount of memory required to hold the DEVICE_RELATIONS
  2449. // structure along with the array
  2450. //
  2451. deviceRelationsSize = FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
  2452. pdoCount * sizeof(PDEVICE_OBJECT);
  2453. //
  2454. // We could be either (a) creating the DEVICE_RELATIONS structure
  2455. // (list) here, or (b) adding our PDOs to an existing list.
  2456. //
  2457. oldDeviceRelations = *PDeviceRelations;
  2458. if (oldDeviceRelations != NULL) {
  2459. //
  2460. // List already exists, allow enough space for both the old
  2461. // and the new.
  2462. //
  2463. deviceRelationsSize += oldDeviceRelations->Count *
  2464. sizeof(PDEVICE_OBJECT);
  2465. }
  2466. deviceRelations = ExAllocatePool(NonPagedPool, deviceRelationsSize);
  2467. if (deviceRelations == NULL) {
  2468. status = STATUS_INSUFFICIENT_RESOURCES;
  2469. goto cleanup;
  2470. }
  2471. deviceRelations->Count = 0;
  2472. if (oldDeviceRelations != NULL) {
  2473. //
  2474. // Copy and free the old list.
  2475. //
  2476. RtlCopyMemory(deviceRelations,
  2477. oldDeviceRelations,
  2478. FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
  2479. oldDeviceRelations->Count * sizeof(PDEVICE_OBJECT));
  2480. ExFreePool(oldDeviceRelations);
  2481. }
  2482. //
  2483. // Set object to point at the DeviceRelations list entry being
  2484. // added, walk our PDO list adding entries until we reach the
  2485. // end of the list.
  2486. //
  2487. object = &deviceRelations->Objects[deviceRelations->Count];
  2488. childPdo = FdoExtension->ChildPdoList;
  2489. PciDebugPrint(
  2490. PciDbgObnoxious,
  2491. "PCI QueryDeviceRelations/BusRelations FDOx %08x (bus 0x%02x)\n",
  2492. FdoExtension,
  2493. FdoExtension->BaseBus
  2494. );
  2495. while (childPdo) {
  2496. PciDebugPrint(
  2497. PciDbgObnoxious,
  2498. " QDR PDO %08x (x %08x)%s\n",
  2499. childPdo->PhysicalDeviceObject,
  2500. childPdo,
  2501. childPdo->NotPresent ? " <Omitted, device flaged not present>" : ""
  2502. );
  2503. if (childPdo->NotPresent == FALSE) {
  2504. physicalDeviceObject = childPdo->PhysicalDeviceObject;
  2505. ObReferenceObject(physicalDeviceObject);
  2506. *object++ = physicalDeviceObject;
  2507. }
  2508. childPdo = childPdo->Next;
  2509. }
  2510. PciDebugPrint(
  2511. PciDbgObnoxious,
  2512. " QDR Total PDO count = %d (%d already in list)\n",
  2513. deviceRelations->Count + pdoCount,
  2514. deviceRelations->Count
  2515. );
  2516. deviceRelations->Count += pdoCount;
  2517. *PDeviceRelations = deviceRelations;
  2518. status = STATUS_SUCCESS;
  2519. cleanup:
  2520. //
  2521. // Unlock
  2522. //
  2523. PCI_RELEASE_STATE_LOCK(FdoExtension);
  2524. return status;
  2525. }
  2526. NTSTATUS
  2527. PciQueryTargetDeviceRelations(
  2528. IN PPCI_PDO_EXTENSION PdoExtension,
  2529. IN OUT PDEVICE_RELATIONS *PDeviceRelations
  2530. )
  2531. /*++
  2532. Routine Description:
  2533. This function builds a DEVICE_RELATIONS structure containing a
  2534. one element array of pointers to the device object for which
  2535. PdoExtension is the device extension.
  2536. Arguments:
  2537. PdoExtension - Pointer to the PDO Extension for the device itself.
  2538. PDeviceRelations - Used to return the pointer to the allocated
  2539. device relations structure.
  2540. Return Value:
  2541. Returns the status of the operation.
  2542. --*/
  2543. {
  2544. PDEVICE_RELATIONS deviceRelations;
  2545. PAGED_CODE();
  2546. if (*PDeviceRelations != NULL) {
  2547. //
  2548. // The caller kindly supplied a device relations structure,
  2549. // it's either too small or exactly the right size. Throw
  2550. // it away.
  2551. //
  2552. ExFreePool(*PDeviceRelations);
  2553. }
  2554. deviceRelations = ExAllocatePool(NonPagedPool, sizeof(DEVICE_RELATIONS));
  2555. if (deviceRelations == NULL) {
  2556. return STATUS_INSUFFICIENT_RESOURCES;
  2557. }
  2558. deviceRelations->Count = 1;
  2559. deviceRelations->Objects[0] = PdoExtension->PhysicalDeviceObject;
  2560. *PDeviceRelations = deviceRelations;
  2561. ObReferenceObject(deviceRelations->Objects[0]);
  2562. return STATUS_SUCCESS;
  2563. }
  2564. BOOLEAN
  2565. PcipIsSameDevice(
  2566. IN PPCI_PDO_EXTENSION PdoExtension,
  2567. IN PPCI_COMMON_CONFIG CommonConfig
  2568. )
  2569. {
  2570. //
  2571. // Verify the data we got, was for the same device
  2572. //
  2573. if ((CommonConfig->VendorID != PdoExtension->VendorId) ||
  2574. (CommonConfig->DeviceID != PdoExtension->DeviceId) ||
  2575. (CommonConfig->RevisionID != PdoExtension->RevisionId)) {
  2576. return FALSE;
  2577. }
  2578. //
  2579. // If the device has a subsystem ID make sure that's the same too.
  2580. //
  2581. if ((PciGetConfigurationType(CommonConfig) == PCI_DEVICE_TYPE) &&
  2582. (PdoExtension->BaseClass != PCI_CLASS_BRIDGE_DEV) &&
  2583. ((PdoExtension->HackFlags & PCI_HACK_NO_SUBSYSTEM) == 0)&&
  2584. ((PdoExtension->HackFlags & PCI_HACK_NO_SUBSYSTEM_AFTER_D3) == 0)) {
  2585. if ((PdoExtension->SubsystemVendorId !=
  2586. CommonConfig->u.type0.SubVendorID) ||
  2587. (PdoExtension->SubsystemId !=
  2588. CommonConfig->u.type0.SubSystemID)) {
  2589. return FALSE;
  2590. }
  2591. }
  2592. //
  2593. // Done
  2594. //
  2595. return TRUE;
  2596. }
  2597. NTSTATUS
  2598. PciQueryEjectionRelations(
  2599. IN PPCI_PDO_EXTENSION PdoExtension,
  2600. IN OUT PDEVICE_RELATIONS *PDeviceRelations
  2601. )
  2602. /*++
  2603. Routine Description:
  2604. This function builds a DEVICE_RELATIONS structure containing an array
  2605. of pointers to the device objects that would presumably leave if this
  2606. device were ejected. This is constructed from all the functions of a device.
  2607. Arguments:
  2608. PdoExtension - Pointer to the PDO Extension for the device itself.
  2609. PDeviceRelations - Used to return the pointer to the allocated
  2610. device relations structure.
  2611. Return Value:
  2612. Returns the status of the operation.
  2613. --*/
  2614. {
  2615. PPCI_FDO_EXTENSION fdoExtension;
  2616. PPCI_PDO_EXTENSION siblingExtension;
  2617. PDEVICE_RELATIONS ejectionRelations;
  2618. ULONG additionalNodes, relationCount;
  2619. additionalNodes = 0;
  2620. fdoExtension = PCI_PARENT_FDOX(PdoExtension);
  2621. //
  2622. // Search the child Pdo list.
  2623. //
  2624. ExAcquireFastMutex(&fdoExtension->ChildListMutex);
  2625. for ( siblingExtension = fdoExtension->ChildPdoList;
  2626. siblingExtension;
  2627. siblingExtension = siblingExtension->Next ) {
  2628. //
  2629. // Is this someone who should be in the list?
  2630. //
  2631. if ((siblingExtension != PdoExtension) &&
  2632. (!siblingExtension->NotPresent) &&
  2633. (siblingExtension->Slot.u.bits.DeviceNumber ==
  2634. PdoExtension->Slot.u.bits.DeviceNumber)) {
  2635. additionalNodes++;
  2636. }
  2637. }
  2638. if (!additionalNodes) {
  2639. ExReleaseFastMutex(&fdoExtension->ChildListMutex);
  2640. return STATUS_NOT_SUPPORTED;
  2641. }
  2642. relationCount = (*PDeviceRelations) ? (*PDeviceRelations)->Count : 0;
  2643. ejectionRelations = (PDEVICE_RELATIONS) ExAllocatePool(
  2644. NonPagedPool,
  2645. sizeof(DEVICE_RELATIONS)+
  2646. (relationCount+additionalNodes-1)*sizeof(PDEVICE_OBJECT)
  2647. );
  2648. if (ejectionRelations == NULL) {
  2649. ExReleaseFastMutex(&fdoExtension->ChildListMutex);
  2650. return STATUS_NOT_SUPPORTED;
  2651. }
  2652. if (*PDeviceRelations) {
  2653. RtlCopyMemory(
  2654. ejectionRelations,
  2655. *PDeviceRelations,
  2656. sizeof(DEVICE_RELATIONS)+
  2657. (relationCount-1)*sizeof(PDEVICE_OBJECT)
  2658. );
  2659. ExFreePool(*PDeviceRelations);
  2660. } else {
  2661. ejectionRelations->Count = 0;
  2662. }
  2663. for ( siblingExtension = fdoExtension->ChildPdoList;
  2664. siblingExtension;
  2665. siblingExtension = siblingExtension->Next ) {
  2666. //
  2667. // Is this someone who should be in the list?
  2668. //
  2669. if ((siblingExtension != PdoExtension) &&
  2670. (!siblingExtension->NotPresent) &&
  2671. (siblingExtension->Slot.u.bits.DeviceNumber ==
  2672. PdoExtension->Slot.u.bits.DeviceNumber)) {
  2673. ObReferenceObject(siblingExtension->PhysicalDeviceObject);
  2674. ejectionRelations->Objects[ejectionRelations->Count++] =
  2675. siblingExtension->PhysicalDeviceObject;
  2676. }
  2677. }
  2678. *PDeviceRelations = ejectionRelations;
  2679. ExReleaseFastMutex(&fdoExtension->ChildListMutex);
  2680. return STATUS_SUCCESS;
  2681. }
  2682. BOOLEAN
  2683. PciIsSameDevice(
  2684. IN PPCI_PDO_EXTENSION PdoExtension
  2685. )
  2686. {
  2687. NTSTATUS status;
  2688. PCI_COMMON_HEADER commonHeader;
  2689. //
  2690. // Get the devices pci data
  2691. //
  2692. PciGetConfigData(PdoExtension, &commonHeader);
  2693. return PcipIsSameDevice(PdoExtension, (PPCI_COMMON_CONFIG)&commonHeader);
  2694. }
  2695. BOOLEAN
  2696. PciComputeNewCurrentSettings(
  2697. IN PPCI_PDO_EXTENSION PdoExtension,
  2698. IN PCM_RESOURCE_LIST ResourceList
  2699. )
  2700. /*++
  2701. Routine Description:
  2702. Determine the new "device settings" based on the incoming
  2703. resource list.
  2704. Arguments:
  2705. PdoExtension - Pointer to the PDO Extension for the PDO.
  2706. ResourceList - The set of resources the device is to be configured
  2707. to use.
  2708. Return Value:
  2709. Returns TRUE if the devices new settings are not the same as
  2710. the settings programmed into the device (FALSE otherwise).
  2711. --*/
  2712. {
  2713. NTSTATUS status;
  2714. CM_PARTIAL_RESOURCE_DESCRIPTOR newResources[PCI_MAX_RANGE_COUNT];
  2715. PCM_FULL_RESOURCE_DESCRIPTOR fullList;
  2716. PCM_PARTIAL_RESOURCE_DESCRIPTOR oldPartial;
  2717. PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
  2718. PCM_PARTIAL_RESOURCE_DESCRIPTOR nextPartial;
  2719. PCM_PARTIAL_RESOURCE_DESCRIPTOR interruptResource = NULL;
  2720. BOOLEAN configurationChanged = FALSE;
  2721. ULONG listCount;
  2722. ULONG count;
  2723. ULONG bar;
  2724. PAGED_CODE();
  2725. //
  2726. // We should never get a Count of anything other that 1 but if so deal with 0 gracefully
  2727. //
  2728. ASSERT(ResourceList == NULL || ResourceList->Count == 1);
  2729. if (ResourceList == NULL || ResourceList->Count == 0) {
  2730. //
  2731. // No incoming resource list,.. == no change unless we've previously
  2732. // decided we must update the hardware.
  2733. //
  2734. return PdoExtension->UpdateHardware;
  2735. }
  2736. #if DBG
  2737. PciDebugPrintCmResList(PciDbgSetRes, ResourceList);
  2738. #endif
  2739. //
  2740. // Produce a new "Current Resources Array" based on the
  2741. // incoming resource list and compare it to the devices
  2742. // current resource list. First init it to nothing.
  2743. //
  2744. for (count = 0; count < PCI_MAX_RANGE_COUNT; count++) {
  2745. newResources[count].Type = CmResourceTypeNull;
  2746. }
  2747. listCount = ResourceList->Count;
  2748. fullList = ResourceList->List;
  2749. //
  2750. // In the CM Resource list, IO will have copied the device
  2751. // private (extended) resource that we gave it in the
  2752. // resource requirements list handed in earlier.
  2753. //
  2754. // Find that BAR number. (Note: It's not there for interrupts).
  2755. //
  2756. while (listCount--) {
  2757. PCM_PARTIAL_RESOURCE_LIST partialList = &fullList->PartialResourceList;
  2758. ULONG drainPartial = 0;
  2759. PCM_PARTIAL_RESOURCE_DESCRIPTOR baseResource;
  2760. CM_PARTIAL_RESOURCE_DESCRIPTOR tempResource;
  2761. #if DBG
  2762. baseResource = NULL;
  2763. #endif
  2764. count = partialList->Count;
  2765. nextPartial = partialList->PartialDescriptors;
  2766. while (count--) {
  2767. partial = nextPartial;
  2768. nextPartial = PciNextPartialDescriptor(partial);
  2769. if (drainPartial != 0) {
  2770. //
  2771. // We encountered a device private indicating
  2772. // we should skip some number of descriptors.
  2773. //
  2774. drainPartial--;
  2775. continue;
  2776. }
  2777. switch (partial->Type) {
  2778. case CmResourceTypeInterrupt:
  2779. ASSERT(interruptResource == NULL); // once only please
  2780. ASSERT(partial->u.Interrupt.Level ==
  2781. partial->u.Interrupt.Vector);
  2782. ASSERT((partial->u.Interrupt.Level & ~0xff) == 0);
  2783. interruptResource = partial;
  2784. PdoExtension->AdjustedInterruptLine =
  2785. (UCHAR)partial->u.Interrupt.Level;
  2786. continue;
  2787. case CmResourceTypeMemory:
  2788. case CmResourceTypePort:
  2789. //
  2790. // Is this expected at this time?
  2791. //
  2792. ASSERT(baseResource == NULL);
  2793. baseResource = partial;
  2794. continue;
  2795. case CmResourceTypeDevicePrivate:
  2796. switch (partial->u.DevicePrivate.Data[0]) {
  2797. case PciPrivateIsaBar:
  2798. ASSERT(baseResource != NULL);
  2799. //
  2800. // This private resource tells us which BAR
  2801. // is associated with this base resource AND
  2802. // modifies the length of the base resource.
  2803. // It is created in conjunction with the set
  2804. // of partial resources that make up a larger
  2805. // resource on a bridge when the bridge's ISA
  2806. // mode bit is set.
  2807. //
  2808. // What's really coming down the pipe is the
  2809. // set of descriptors that describe the ISA
  2810. // holes in the range. These are 0x100 bytes
  2811. // every 0x400 bytes for the entire range.
  2812. //
  2813. // Make a copy of the base resource we have just
  2814. // seen. Its starting address is the start of
  2815. // the entire range. Adjust its length to the
  2816. // entire range.
  2817. //
  2818. tempResource = *baseResource;
  2819. //
  2820. // A little paranoia is sometimes a good thing.
  2821. // This can only happen on an IO resource which
  2822. // is the length of an ISA hole, ie 0x100 bytes.
  2823. //
  2824. ASSERT((tempResource.Type == CmResourceTypePort) &&
  2825. (tempResource.u.Generic.Length == 0x100)
  2826. );
  2827. //
  2828. // Excessive paranoia.
  2829. //
  2830. ASSERT((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
  2831. (PdoExtension->Dependent.type1.IsaBitSet == TRUE)
  2832. );
  2833. //
  2834. // Get the new length.
  2835. //
  2836. drainPartial = partial->u.DevicePrivate.Data[2];
  2837. tempResource.u.Generic.Length = drainPartial;
  2838. //
  2839. // Skip the remaining descriptors that make up this
  2840. // range.
  2841. //
  2842. drainPartial = (drainPartial / 0x400) - 1;
  2843. #if DBG
  2844. {
  2845. PCM_PARTIAL_RESOURCE_DESCRIPTOR lastOne;
  2846. lastOne = baseResource + drainPartial + 1;
  2847. ASSERT(lastOne->Type == CmResourceTypePort);
  2848. ASSERT(lastOne->u.Generic.Length == 0x100);
  2849. ASSERT(lastOne->u.Generic.Start.QuadPart ==
  2850. (tempResource.u.Generic.Start.QuadPart +
  2851. tempResource.u.Generic.Length - 0x400)
  2852. );
  2853. }
  2854. #endif
  2855. //
  2856. // Finally, shift out pointer to our temp (adjusted)
  2857. // copy of the resource.
  2858. //
  2859. baseResource = &tempResource;
  2860. // fall thru.
  2861. case PciPrivateBar:
  2862. ASSERT(baseResource != NULL);
  2863. //
  2864. // This private resource tells us which BAR
  2865. // to is associated with this resource.
  2866. //
  2867. bar = partial->u.DevicePrivate.Data[1];
  2868. //
  2869. // Copy this descriptor into the new array.
  2870. //
  2871. newResources[bar] = *baseResource;
  2872. #if DBG
  2873. baseResource = NULL;
  2874. #endif
  2875. continue;
  2876. case PciPrivateSkipList:
  2877. ASSERT(baseResource == NULL);
  2878. //
  2879. // The remainder of this list is device
  2880. // specific stuff we can't change anyway.
  2881. //
  2882. drainPartial = partial->u.DevicePrivate.Data[1];
  2883. ASSERT(drainPartial); // sanity check
  2884. continue;
  2885. }
  2886. }
  2887. }
  2888. ASSERT(baseResource == NULL);
  2889. //
  2890. // Advance to next partial list.
  2891. //
  2892. fullList = (PCM_FULL_RESOURCE_DESCRIPTOR)partial;
  2893. }
  2894. //
  2895. // If we have no I/O or memory resources, then there is no need to look
  2896. // any further.
  2897. //
  2898. if (PdoExtension->Resources == NULL) {
  2899. return FALSE;
  2900. }
  2901. //
  2902. // Ok, we now have a new list of resources in the same order as
  2903. // the "current" set. See if anything changed.
  2904. //
  2905. partial = newResources;
  2906. oldPartial = PdoExtension->Resources->Current;
  2907. #if DBG
  2908. if (PciDebug & PciDbgSetResChange) {
  2909. BOOLEAN dbgConfigurationChanged = FALSE;
  2910. for (count = 0;
  2911. count < PCI_MAX_RANGE_COUNT;
  2912. count++, partial++, oldPartial++) {
  2913. if ((partial->Type != oldPartial->Type) ||
  2914. ((partial->Type != CmResourceTypeNull) &&
  2915. ((partial->u.Generic.Start.QuadPart !=
  2916. oldPartial->u.Generic.Start.QuadPart) ||
  2917. (partial->u.Generic.Length != oldPartial->u.Generic.Length)))) {
  2918. //
  2919. // Devices settings have changed.
  2920. //
  2921. dbgConfigurationChanged = TRUE;
  2922. PciDebugPrint(
  2923. PciDbgAlways,
  2924. "PCI - PDO(b=0x%x, d=0x%x, f=0x%x) changing resource settings.\n",
  2925. PCI_PARENT_FDOX(PdoExtension)->BaseBus,
  2926. PdoExtension->Slot.u.bits.DeviceNumber,
  2927. PdoExtension->Slot.u.bits.FunctionNumber
  2928. );
  2929. break;
  2930. }
  2931. }
  2932. partial = newResources;
  2933. oldPartial = PdoExtension->Resources->Current;
  2934. if (dbgConfigurationChanged == TRUE) {
  2935. PciDebugPrint(
  2936. PciDbgAlways,
  2937. "PCI - SetResources, old state, new state\n"
  2938. );
  2939. for (count = 0; count < PCI_MAX_RANGE_COUNT; count++) {
  2940. PCM_PARTIAL_RESOURCE_DESCRIPTOR old = oldPartial + count;
  2941. PCM_PARTIAL_RESOURCE_DESCRIPTOR new = partial + count;
  2942. if ((old->Type == new->Type) &&
  2943. (new->Type == CmResourceTypeNull)) {
  2944. PciDebugPrint(
  2945. PciDbgAlways,
  2946. "00 <unused>\n"
  2947. );
  2948. continue;
  2949. }
  2950. PciDebugPrint(
  2951. PciDbgAlways,
  2952. "%02x %08x%08x %08x -> %02x %08x%08x %08x\n",
  2953. old->Type,
  2954. old->u.Generic.Start.HighPart,
  2955. old->u.Generic.Start.LowPart,
  2956. old->u.Generic.Length,
  2957. new->Type,
  2958. new->u.Generic.Start.HighPart,
  2959. new->u.Generic.Start.LowPart,
  2960. new->u.Generic.Length
  2961. );
  2962. ASSERT((old->Type == new->Type) ||
  2963. (old->Type == CmResourceTypeNull) ||
  2964. (new->Type == CmResourceTypeNull));
  2965. }
  2966. }
  2967. }
  2968. #endif
  2969. for (count = 0;
  2970. count < PCI_MAX_RANGE_COUNT;
  2971. count++, partial++, oldPartial++) {
  2972. //
  2973. // If the resource type changed, OR, if any of the resources
  2974. // settings changed (this latter only if type != NULL) ...
  2975. //
  2976. if ((partial->Type != oldPartial->Type) ||
  2977. ((partial->Type != CmResourceTypeNull) &&
  2978. ((partial->u.Generic.Start.QuadPart !=
  2979. oldPartial->u.Generic.Start.QuadPart) ||
  2980. (partial->u.Generic.Length != oldPartial->u.Generic.Length)))) {
  2981. //
  2982. // Devices settings have changed.
  2983. //
  2984. configurationChanged = TRUE;
  2985. #if DBG
  2986. if (oldPartial->Type != CmResourceTypeNull) {
  2987. PciDebugPrint(PciDbgSetResChange,
  2988. " Old range-\n");
  2989. PciDebugPrintPartialResource(PciDbgSetResChange, oldPartial);
  2990. } else {
  2991. PciDebugPrint(PciDbgSetResChange,
  2992. " Previously unset range\n");
  2993. }
  2994. PciDebugPrint(PciDbgSetResChange,
  2995. " changed to\n");
  2996. PciDebugPrintPartialResource(PciDbgSetResChange, partial);
  2997. #endif
  2998. //
  2999. // Copy the new setting into the "current" settings
  3000. // array. This will then be written to the h/w.
  3001. //
  3002. oldPartial->Type = partial->Type;
  3003. oldPartial->u.Generic = partial->u.Generic;
  3004. }
  3005. }
  3006. return configurationChanged || PdoExtension->UpdateHardware;
  3007. }
  3008. NTSTATUS
  3009. PciSetResources(
  3010. IN PPCI_PDO_EXTENSION PdoExtension,
  3011. IN BOOLEAN PowerOn,
  3012. IN BOOLEAN StartDeviceIrp
  3013. )
  3014. /*++
  3015. Routine Description:
  3016. Called to change a devices resource settings to those in the
  3017. incoming list.
  3018. Arguments:
  3019. PdoExtension - Pointer to the PDO Extension for the PDO.
  3020. Change - TRUE is the resources are to be written.
  3021. PowerOn - TRUE if the device is having power restored
  3022. and extraneous config space registers should
  3023. be restored. (PowerOn implies Change).
  3024. StartDeviceIrp - TRUE if this call is the result of a PNP START_DEVICE
  3025. IRP.
  3026. Return Value:
  3027. Returns the status of the operation.
  3028. --*/
  3029. {
  3030. PCI_COMMON_HEADER commonHeader;
  3031. PPCI_COMMON_CONFIG commonConfig = (PPCI_COMMON_CONFIG)&commonHeader;
  3032. PCI_MSI_CAPABILITY msiCapability;
  3033. PPCI_FDO_EXTENSION fdoExtension = PCI_PARENT_FDOX(PdoExtension);
  3034. ULONG configType;
  3035. //
  3036. // Get the common configuration data.
  3037. //
  3038. // N.B. This is done using RAW access to config space so that
  3039. // (a) no pageable code is used, and
  3040. // (b) the actual contents of the interrupt line register is
  3041. // returned/written.
  3042. //
  3043. PciGetConfigData(PdoExtension, commonConfig);
  3044. if (!PcipIsSameDevice(PdoExtension, commonConfig)) {
  3045. ASSERTMSG("PCI Set resources - not same device", 0);
  3046. return STATUS_DEVICE_DOES_NOT_EXIST;
  3047. }
  3048. //
  3049. // If this is a host bridge, bail. We don't want to touch host bridge
  3050. // config space. This is a hack and should be fixed.
  3051. //
  3052. if (PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV
  3053. && PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) {
  3054. return STATUS_SUCCESS;
  3055. }
  3056. if (PowerOn) {
  3057. //
  3058. // If this is an IDE controller then attempt to switch it to
  3059. // native mode
  3060. //
  3061. if (PdoExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR
  3062. && PdoExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR) {
  3063. BOOLEAN switched;
  3064. switched = PciConfigureIdeController(PdoExtension, commonConfig, FALSE);
  3065. ASSERT(switched == PdoExtension->SwitchedIDEToNativeMode);
  3066. }
  3067. }
  3068. //
  3069. // Get part of the MSI capability structure for supported devices
  3070. //
  3071. //
  3072. // NOTE: This code is UNTESTED due to the unavailability of MSI devices
  3073. //
  3074. #if MSI_SUPPORTED
  3075. if(PdoExtension->CapableMSI && PdoExtension->MsiInfo.MessageAddress) {
  3076. //
  3077. // Make sure we have an offset for the Capability structure
  3078. //
  3079. ASSERT(PdoExtension->MsiInfo.CapabilityOffset);
  3080. //
  3081. // We just need the message control register for configuration purposes
  3082. //
  3083. PciReadDeviceConfig(
  3084. PdoExtension,
  3085. &(msiCapability.MessageControl),
  3086. PdoExtension->MsiInfo.CapabilityOffset +
  3087. FIELD_OFFSET(PCI_MSI_CAPABILITY, MessageControl),
  3088. sizeof(msiCapability.MessageControl)
  3089. );
  3090. }
  3091. #endif
  3092. //
  3093. // If this device is marked as needing hot plug configuration and we have a
  3094. // clue of what to do...
  3095. //
  3096. if (PdoExtension->NeedsHotPlugConfiguration && fdoExtension->HotPlugParameters.Acquired) {
  3097. UCHAR readCacheLineSize;
  3098. USHORT newCmdBits = 0;
  3099. //
  3100. // Save away our new latency timer so it gets written out below
  3101. //
  3102. PdoExtension->SavedLatencyTimer = fdoExtension->HotPlugParameters.LatencyTimer;
  3103. PciDebugPrint(
  3104. PciDbgConfigParam,
  3105. "PCI - SetResources, PDOx %x current CacheLineSize is %x, Want %x\n",
  3106. PdoExtension,
  3107. (ULONG)commonConfig->CacheLineSize,
  3108. (ULONG)fdoExtension->HotPlugParameters.CacheLineSize
  3109. );
  3110. //
  3111. // Write out out suggested cache line size
  3112. //
  3113. PciWriteDeviceConfig(
  3114. PdoExtension,
  3115. &fdoExtension->HotPlugParameters.CacheLineSize,
  3116. FIELD_OFFSET(PCI_COMMON_CONFIG, CacheLineSize),
  3117. sizeof(fdoExtension->HotPlugParameters.CacheLineSize)
  3118. );
  3119. //
  3120. // Check if the cache line size stuck which means the hardware liked it
  3121. //
  3122. PciReadDeviceConfig(
  3123. PdoExtension,
  3124. &readCacheLineSize,
  3125. FIELD_OFFSET(PCI_COMMON_CONFIG, CacheLineSize),
  3126. sizeof(readCacheLineSize)
  3127. );
  3128. PciDebugPrint(
  3129. PciDbgConfigParam,
  3130. "PCI - SetResources, PDOx %x After write, CacheLineSize %x\n",
  3131. PdoExtension,
  3132. (ULONG)readCacheLineSize
  3133. );
  3134. if ((readCacheLineSize == fdoExtension->HotPlugParameters.CacheLineSize) &&
  3135. (readCacheLineSize != 0)) {
  3136. PciDebugPrint(
  3137. PciDbgConfigParam,
  3138. "PCI - SetResources, PDOx %x cache line size stuck, set MWI\n",
  3139. PdoExtension
  3140. );
  3141. //
  3142. // First stash this so that when we power manage the device we set
  3143. // it back correctly and that we want to set MWI...
  3144. //
  3145. PdoExtension->SavedCacheLineSize = fdoExtension->HotPlugParameters.CacheLineSize;
  3146. newCmdBits |= PCI_ENABLE_WRITE_AND_INVALIDATE;
  3147. //
  3148. // ISSUE-3/16/2000-andrewth
  3149. // If we get our PDO blown away (ie removed parent) then we forget that we need to
  3150. // set MWI...
  3151. //
  3152. } else {
  3153. PciDebugPrint(
  3154. PciDbgConfigParam,
  3155. "PCI - SetResources, PDOx %x cache line size non-sticky\n",
  3156. PdoExtension
  3157. );
  3158. }
  3159. //
  3160. // Now deal with SERR and PERR - abandon hope all ye who set these bits on
  3161. // flaky PC hardware...
  3162. //
  3163. if (fdoExtension->HotPlugParameters.EnableSERR) {
  3164. newCmdBits |= PCI_ENABLE_SERR;
  3165. }
  3166. if (fdoExtension->HotPlugParameters.EnablePERR) {
  3167. newCmdBits |= PCI_ENABLE_PARITY;
  3168. }
  3169. //
  3170. // Update the command enables so we write this out correctly after a PM op
  3171. //
  3172. PdoExtension->CommandEnables |= newCmdBits;
  3173. }
  3174. //
  3175. // Write the resources out to the hardware...
  3176. //
  3177. configType = PciGetConfigurationType(commonConfig);
  3178. PciInvalidateResourceInfoCache(PdoExtension);
  3179. //
  3180. // Call the device type dependent routine to set the new
  3181. // configuration.
  3182. //
  3183. PciConfigurators[configType].ChangeResourceSettings(
  3184. PdoExtension,
  3185. commonConfig
  3186. );
  3187. //
  3188. // If we explicitly wanted the hardware updated (UpdateHardware flag)
  3189. // this its done now...
  3190. //
  3191. PdoExtension->UpdateHardware = FALSE;
  3192. if (PowerOn) {
  3193. PciConfigurators[configType].ResetDevice(
  3194. PdoExtension,
  3195. commonConfig
  3196. );
  3197. //
  3198. // Restore InterruptLine register too. (InterruptLine is
  3199. // at same offset for header type 0, 1 and 2).
  3200. //
  3201. commonConfig->u.type0.InterruptLine =
  3202. PdoExtension->RawInterruptLine;
  3203. }
  3204. //
  3205. // Restore Maximum Latency and Cache Line Size.
  3206. //
  3207. #if DBG
  3208. if (commonConfig->LatencyTimer != PdoExtension->SavedLatencyTimer) {
  3209. PciDebugPrint(
  3210. PciDbgConfigParam,
  3211. "PCI (pdox %08x) changing latency from %02x to %02x.\n",
  3212. PdoExtension,
  3213. commonConfig->LatencyTimer,
  3214. PdoExtension->SavedLatencyTimer
  3215. );
  3216. }
  3217. if (commonConfig->CacheLineSize != PdoExtension->SavedCacheLineSize) {
  3218. PciDebugPrint(
  3219. PciDbgConfigParam,
  3220. "PCI (pdox %08x) changing cache line size from %02x to %02x.\n",
  3221. PdoExtension,
  3222. commonConfig->CacheLineSize,
  3223. PdoExtension->SavedCacheLineSize
  3224. );
  3225. }
  3226. #endif
  3227. //
  3228. // Restore random registers
  3229. //
  3230. commonConfig->LatencyTimer = PdoExtension->SavedLatencyTimer;
  3231. commonConfig->CacheLineSize = PdoExtension->SavedCacheLineSize;
  3232. commonConfig->u.type0.InterruptLine = PdoExtension->RawInterruptLine;
  3233. //
  3234. // Restore the command register we remember in the power down case
  3235. //
  3236. commonConfig->Command = PdoExtension->CommandEnables;
  3237. //
  3238. // Disable the device while we write the rest of its config
  3239. // space. Also, don't write any non-zero value to it's status
  3240. // register.
  3241. //
  3242. if ((PdoExtension->HackFlags & PCI_HACK_PRESERVE_COMMAND) == 0) {
  3243. commonConfig->Command &= ~(PCI_ENABLE_IO_SPACE |
  3244. PCI_ENABLE_MEMORY_SPACE |
  3245. PCI_ENABLE_BUS_MASTER |
  3246. PCI_ENABLE_WRITE_AND_INVALIDATE);
  3247. }
  3248. commonConfig->Status = 0;
  3249. //
  3250. // Call out and apply any hacks necessary
  3251. //
  3252. PciApplyHacks(
  3253. PCI_PARENT_FDOX(PdoExtension),
  3254. commonConfig,
  3255. PdoExtension->Slot,
  3256. EnumStartDevice,
  3257. PdoExtension
  3258. );
  3259. //
  3260. // Write it out to the hardware
  3261. //
  3262. PciSetConfigData(PdoExtension, commonConfig);
  3263. #if MSI_SUPPORTED
  3264. //
  3265. // Program MSI devices with their new message interrupt resources
  3266. //
  3267. // NOTE: This code is UNTESTED due to the unavailability of MSI devices
  3268. //
  3269. if (PdoExtension->CapableMSI && PdoExtension->MsiInfo.MessageAddress) {
  3270. PciDebugPrint(
  3271. PciDbgInformative,
  3272. "PCI: Device %08x being reprogrammed for MSI.\n",
  3273. PdoExtension->PhysicalDeviceObject
  3274. );
  3275. //
  3276. // Set the proper resources in the MSI capability structure
  3277. // and write them to Hardware.
  3278. //
  3279. // Message Address
  3280. //
  3281. ASSERT(PdoExtension->MsiInfo.MessageAddress);
  3282. msiCapability.MessageAddress.Raw = PdoExtension->MsiInfo.MessageAddress;
  3283. //
  3284. // Must be DWORD aligned address
  3285. //
  3286. ASSERT(msiCapability.MessageAddress.Register.Reserved == 0);
  3287. //
  3288. // Write the Message Address register in Hardware.
  3289. //
  3290. PciWriteDeviceConfig(
  3291. PdoExtension,
  3292. &(msiCapability.MessageAddress),
  3293. PdoExtension->MsiInfo.CapabilityOffset +
  3294. FIELD_OFFSET(PCI_MSI_CAPABILITY,MessageAddress),
  3295. sizeof(msiCapability.MessageAddress)
  3296. );
  3297. //
  3298. // Message Upper Address
  3299. //
  3300. if(msiCapability.MessageControl.CapableOf64Bits) {
  3301. // All the APICs we know live below 4GB so their upper address component
  3302. // is always 0.
  3303. msiCapability.Data.Bit64.MessageUpperAddress = 0;
  3304. PciWriteDeviceConfig(
  3305. PdoExtension,
  3306. &(msiCapability.Data.Bit64.MessageUpperAddress),
  3307. PdoExtension->MsiInfo.CapabilityOffset +
  3308. FIELD_OFFSET(PCI_MSI_CAPABILITY,Data.Bit64.MessageUpperAddress),
  3309. sizeof(msiCapability.Data.Bit64.MessageUpperAddress)
  3310. );
  3311. //
  3312. // Message Data
  3313. //
  3314. msiCapability.Data.Bit64.MessageData = PdoExtension->MsiInfo.MessageData;
  3315. PciWriteDeviceConfig(
  3316. PdoExtension,
  3317. &(msiCapability.Data.Bit64.MessageData),
  3318. PdoExtension->MsiInfo.CapabilityOffset +
  3319. FIELD_OFFSET(PCI_MSI_CAPABILITY,Data.Bit64.MessageData),
  3320. sizeof(msiCapability.Data.Bit64.MessageData)
  3321. );
  3322. } else {
  3323. //
  3324. // Message Data
  3325. //
  3326. msiCapability.Data.Bit32.MessageData = PdoExtension->MsiInfo.MessageData;
  3327. //
  3328. // Write to hardware.
  3329. //
  3330. PciWriteDeviceConfig(
  3331. PdoExtension,
  3332. &(msiCapability.Data.Bit32.MessageData),
  3333. PdoExtension->MsiInfo.CapabilityOffset +
  3334. FIELD_OFFSET(PCI_MSI_CAPABILITY,Data.Bit32.MessageData),
  3335. sizeof(msiCapability.Data.Bit32.MessageData)
  3336. );
  3337. }
  3338. // # of Messages granted
  3339. //
  3340. // We have the arbiter allocate only 1 interrupt for us, so we
  3341. // are allocating just 1 message.
  3342. //
  3343. msiCapability.MessageControl.MultipleMessageEnable = 1;
  3344. //
  3345. // Enable bit
  3346. //
  3347. msiCapability.MessageControl.MSIEnable = 1;
  3348. //
  3349. // Write the message control register to Hardware
  3350. //
  3351. PciWriteDeviceConfig(
  3352. PdoExtension,
  3353. &(msiCapability.MessageControl),
  3354. PdoExtension->MsiInfo.CapabilityOffset +
  3355. FIELD_OFFSET(PCI_MSI_CAPABILITY,MessageControl),
  3356. sizeof(msiCapability.MessageControl)
  3357. );
  3358. }
  3359. #endif // MSI_SUPPORTED
  3360. //
  3361. // Update our concept of the RawInterruptLine (either as read from
  3362. // the h/w or restored by us). Note: InterruptLine is at the same
  3363. // offset for types 0, 1 and 2 PCI config space headers.
  3364. //
  3365. PdoExtension->RawInterruptLine = commonConfig->u.type0.InterruptLine;
  3366. //
  3367. // New values written to config space, now re-enable the
  3368. // device (as indicated in the CommandEnables)
  3369. //
  3370. PciDecodeEnable(PdoExtension, TRUE, &PdoExtension->CommandEnables);
  3371. //
  3372. // If it needed configuration its done by now!
  3373. //
  3374. PdoExtension->NeedsHotPlugConfiguration = FALSE;
  3375. return STATUS_SUCCESS;
  3376. }
  3377. VOID
  3378. PciGetEnhancedCapabilities(
  3379. IN PPCI_PDO_EXTENSION PdoExtension,
  3380. IN PPCI_COMMON_CONFIG Config
  3381. )
  3382. /*++
  3383. Routine Description:
  3384. This routine sets the appropriate fields in the Pdo extension relating
  3385. to capabilities and power. If no power management registers are available
  3386. the power state is based off of the decode fields. PCI bus reset code
  3387. depends on this, and to prevent excessive resets this routine should only
  3388. be called immediately after a new PDO is created.
  3389. NOTE:
  3390. We should rename this function to something with GetInitialState in the
  3391. title and so it can't be confused with IRP_MN_QUERY_CAPABILITIES.
  3392. Arguments:
  3393. PdoExtension - Pointer to the PDO Extension for the PDO.
  3394. Config - Pointer to the common portion of the config space.
  3395. Return Value:
  3396. None.
  3397. --*/
  3398. {
  3399. ULONG configType;
  3400. UCHAR capPtr = 0;
  3401. PCI_MSI_CAPABILITY msi;
  3402. UCHAR msicapptr;
  3403. PAGED_CODE();
  3404. //
  3405. // If this function supports a capabilities list, record the
  3406. // capabilities pointer.
  3407. //
  3408. PdoExtension->PowerState.DeviceWakeLevel = PowerDeviceUnspecified;
  3409. if (!(Config->Status & PCI_STATUS_CAPABILITIES_LIST)) {
  3410. //
  3411. // If we don't have a capability bit we can't do MSI or Power management
  3412. //
  3413. PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
  3414. PdoExtension->CapabilitiesPtr = 0;
  3415. #if MSI_SUPPORTED
  3416. PdoExtension->CapableMSI = FALSE;
  3417. #endif
  3418. goto PciGetCapabilitiesExit;
  3419. }
  3420. switch (PciGetConfigurationType(Config)) {
  3421. case PCI_DEVICE_TYPE:
  3422. capPtr = Config->u.type0.CapabilitiesPtr;
  3423. break;
  3424. case PCI_BRIDGE_TYPE:
  3425. capPtr = Config->u.type1.CapabilitiesPtr;
  3426. break;
  3427. case PCI_CARDBUS_BRIDGE_TYPE:
  3428. capPtr = Config->u.type2.CapabilitiesPtr;
  3429. break;
  3430. }
  3431. //
  3432. // Capabilities pointers are a new feature so we verify a
  3433. // little that the h/w folks built the right thing. Must
  3434. // be a DWORD offset, must not point into common header.
  3435. // (Zero is allowable, means not used).
  3436. //
  3437. if (capPtr) {
  3438. if (((capPtr & 0x3) == 0) && (capPtr >= PCI_COMMON_HDR_LENGTH)) {
  3439. PdoExtension->CapabilitiesPtr = capPtr;
  3440. } else {
  3441. ASSERT(((capPtr & 0x3) == 0) && (capPtr >= PCI_COMMON_HDR_LENGTH));
  3442. }
  3443. }
  3444. #if MSI_SUPPORTED
  3445. //
  3446. // Search for the MSI capability structure
  3447. // Just get the structure header since we don't look at the structure here.
  3448. //
  3449. msicapptr = PciReadDeviceCapability(
  3450. PdoExtension,
  3451. PdoExtension->CapabilitiesPtr,
  3452. PCI_CAPABILITY_ID_MSI,
  3453. &msi,
  3454. sizeof(PCI_CAPABILITIES_HEADER)
  3455. );
  3456. if (msicapptr != 0) {
  3457. PciDebugPrint(PciDbgInformative,"PCI: MSI Capability Found for device %p\n",
  3458. PdoExtension->PhysicalDeviceObject);
  3459. //
  3460. // Cache the capability address in the PDO extension
  3461. // and initialize MSI routing info.
  3462. //
  3463. PdoExtension->MsiInfo.CapabilityOffset = msicapptr;
  3464. PdoExtension->MsiInfo.MessageAddress = 0;
  3465. PdoExtension->MsiInfo.MessageData = 0;
  3466. //
  3467. // Mark this PDO as capable of MSI.
  3468. //
  3469. PdoExtension->CapableMSI = TRUE;
  3470. }
  3471. #endif // MSI_SUPPORTED
  3472. //
  3473. // See if the device is Power Management capable.
  3474. //
  3475. if (!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)) {
  3476. PCI_PM_CAPABILITY pm;
  3477. UCHAR pmcapptr;
  3478. pmcapptr = PciReadDeviceCapability(
  3479. PdoExtension,
  3480. PdoExtension->CapabilitiesPtr,
  3481. PCI_CAPABILITY_ID_POWER_MANAGEMENT,
  3482. &pm,
  3483. sizeof(pm)
  3484. );
  3485. if (pmcapptr != 0) {
  3486. //
  3487. // Found a PM capability structure.
  3488. //
  3489. // Select "most powered off state" this device can
  3490. // issue a PME from.
  3491. //
  3492. DEVICE_POWER_STATE ds = PowerDeviceUnspecified;
  3493. if (pm.PMC.Capabilities.Support.PMED0 ) ds = PowerDeviceD0;
  3494. if (pm.PMC.Capabilities.Support.PMED1 ) ds = PowerDeviceD1;
  3495. if (pm.PMC.Capabilities.Support.PMED2 ) ds = PowerDeviceD2;
  3496. if (pm.PMC.Capabilities.Support.PMED3Hot ) ds = PowerDeviceD3;
  3497. if (pm.PMC.Capabilities.Support.PMED3Cold) ds = PowerDeviceD3;
  3498. PdoExtension->PowerState.DeviceWakeLevel = ds;
  3499. //
  3500. // Record the current power state.
  3501. // Note: D0 = 0, thru D3 = 3, convert to
  3502. // PowerDeviceD0 thru PowerDeviceD3. It's
  3503. // only a two bit field (in h/w) so no other
  3504. // values are possible.
  3505. //
  3506. PdoExtension->PowerState.CurrentDeviceState =
  3507. pm.PMCSR.ControlStatus.PowerState +
  3508. PowerDeviceD0;
  3509. //
  3510. // Remember the power capabilities
  3511. //
  3512. PdoExtension->PowerCapabilities = pm.PMC.Capabilities;
  3513. } else {
  3514. //
  3515. // Device has capabilities but not Power
  3516. // Management capabilities. Cheat a little
  3517. // by pretending the registry flag is set
  3518. // that says this. (This speeds saves us
  3519. // hunting through the h/w next time we
  3520. // want to look at the PM caps).
  3521. //
  3522. PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
  3523. }
  3524. }
  3525. PciGetCapabilitiesExit:
  3526. if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS) {
  3527. //
  3528. // In this case we only support D0 and D3. D3 is defined as decodes
  3529. // off.
  3530. //
  3531. if ((Config->Command & (PCI_ENABLE_IO_SPACE |
  3532. PCI_ENABLE_MEMORY_SPACE |
  3533. PCI_ENABLE_BUS_MASTER)) != 0) {
  3534. PdoExtension->PowerState.CurrentDeviceState = PowerDeviceD0;
  3535. } else {
  3536. PdoExtension->PowerState.CurrentDeviceState = PowerDeviceD3;
  3537. }
  3538. }
  3539. }
  3540. NTSTATUS
  3541. PciScanHibernatedBus(
  3542. IN PPCI_FDO_EXTENSION FdoExtension
  3543. )
  3544. /*++
  3545. Routine Description:
  3546. Scan the bus (detailed in FdoExtension) for any new PCI devices
  3547. that were not there when we hibernated and turn them off if doing so seems
  3548. like a good idea.
  3549. Arguments:
  3550. FdoExtension - Our extension for the PCI bus functional device object.
  3551. Return Value:
  3552. NT status.
  3553. --*/
  3554. {
  3555. NTSTATUS status;
  3556. PCI_COMMON_HEADER commonHeader;
  3557. PPCI_COMMON_CONFIG commonConfig = (PPCI_COMMON_CONFIG)&commonHeader;
  3558. PDEVICE_OBJECT physicalDeviceObject;
  3559. PPCI_PDO_EXTENSION pdoExtension;
  3560. PCI_SLOT_NUMBER slot;
  3561. ULONG deviceNumber;
  3562. ULONG functionNumber;
  3563. USHORT SubVendorID, SubSystemID;
  3564. BOOLEAN isRoot;
  3565. ULONGLONG hackFlags;
  3566. ULONG maximumDevices;
  3567. BOOLEAN newDevices = FALSE;
  3568. PciDebugPrint(PciDbgPrattling,
  3569. "PCI Scan Bus: FDO Extension @ 0x%x, Base Bus = 0x%x\n",
  3570. FdoExtension,
  3571. FdoExtension->BaseBus);
  3572. isRoot = PCI_IS_ROOT_FDO(FdoExtension);
  3573. //
  3574. // Examine each possible device on this bus.
  3575. //
  3576. maximumDevices = PCI_MAX_DEVICES;
  3577. if (!isRoot) {
  3578. //
  3579. // Examine the PDO extension for the bridge device and see
  3580. // if it's broken.
  3581. //
  3582. pdoExtension = (PPCI_PDO_EXTENSION)
  3583. FdoExtension->PhysicalDeviceObject->DeviceExtension;
  3584. ASSERT_PCI_PDO_EXTENSION(pdoExtension);
  3585. if (pdoExtension->HackFlags & PCI_HACK_ONE_CHILD) {
  3586. maximumDevices = 1;
  3587. }
  3588. }
  3589. slot.u.AsULONG = 0;
  3590. for (deviceNumber = 0;
  3591. deviceNumber < maximumDevices;
  3592. deviceNumber++) {
  3593. slot.u.bits.DeviceNumber = deviceNumber;
  3594. //
  3595. // Examine each possible function on this device.
  3596. // N.B. Early out if function 0 not present.
  3597. //
  3598. for (functionNumber = 0;
  3599. functionNumber < PCI_MAX_FUNCTION;
  3600. functionNumber++) {
  3601. slot.u.bits.FunctionNumber = functionNumber;
  3602. PciReadSlotConfig(FdoExtension,
  3603. slot,
  3604. commonConfig,
  3605. 0,
  3606. sizeof(commonConfig->VendorID)
  3607. );
  3608. if (commonConfig->VendorID == 0xFFFF ||
  3609. commonConfig->VendorID == 0) {
  3610. if (functionNumber == 0) {
  3611. //
  3612. // Didn't get any data on function zero of this
  3613. // device, no point in checking other functions.
  3614. //
  3615. break;
  3616. } else {
  3617. //
  3618. // Check next function.
  3619. //
  3620. continue;
  3621. }
  3622. }
  3623. //
  3624. // We have a device so get the rest of its config space
  3625. //
  3626. PciReadSlotConfig(FdoExtension,
  3627. slot,
  3628. &commonConfig->DeviceID,
  3629. FIELD_OFFSET(PCI_COMMON_CONFIG, DeviceID),
  3630. sizeof(PCI_COMMON_HEADER)
  3631. - sizeof(commonConfig->VendorID)
  3632. );
  3633. //
  3634. // Munge the config space if necessary
  3635. //
  3636. PciApplyHacks(FdoExtension,
  3637. commonConfig,
  3638. slot,
  3639. EnumHackConfigSpace,
  3640. NULL
  3641. );
  3642. if ((PciGetConfigurationType(commonConfig) == PCI_DEVICE_TYPE) &&
  3643. (commonConfig->BaseClass != PCI_CLASS_BRIDGE_DEV)) {
  3644. SubVendorID = commonConfig->u.type0.SubVendorID;
  3645. SubSystemID = commonConfig->u.type0.SubSystemID;
  3646. } else {
  3647. SubVendorID = 0;
  3648. SubSystemID = 0;
  3649. }
  3650. hackFlags = PciGetHackFlags(commonConfig->VendorID,
  3651. commonConfig->DeviceID,
  3652. SubVendorID,
  3653. SubSystemID,
  3654. commonConfig->RevisionID
  3655. );
  3656. if (PciSkipThisFunction(commonConfig,
  3657. slot,
  3658. EnumBusScan,
  3659. hackFlags)) {
  3660. //
  3661. // Skip this function
  3662. //
  3663. continue;
  3664. }
  3665. //
  3666. // In case we are rescanning the bus, check to see if
  3667. // a PDO for this device already exists as a child of
  3668. // the FDO.
  3669. //
  3670. pdoExtension = PciFindPdoByFunction(
  3671. FdoExtension,
  3672. slot,
  3673. commonConfig);
  3674. if (pdoExtension == NULL) {
  3675. newDevices = TRUE;
  3676. //
  3677. // This is a new device disable it if we can
  3678. //
  3679. if (PciCanDisableDecodes(NULL, commonConfig, hackFlags, 0)) {
  3680. commonConfig->Command &= ~(PCI_ENABLE_IO_SPACE |
  3681. PCI_ENABLE_MEMORY_SPACE |
  3682. PCI_ENABLE_BUS_MASTER);
  3683. PciWriteSlotConfig(FdoExtension,
  3684. slot,
  3685. &commonConfig->Command,
  3686. FIELD_OFFSET(PCI_COMMON_CONFIG, Command),
  3687. sizeof(commonConfig->Command)
  3688. );
  3689. }
  3690. } else {
  3691. //
  3692. // We already know about this device so leave well alone!
  3693. //
  3694. }
  3695. if ( (functionNumber == 0) &&
  3696. !PCI_MULTIFUNCTION_DEVICE(commonConfig) ) {
  3697. //
  3698. // Not a multifunction adapter, skip other functions on
  3699. // this device.
  3700. //
  3701. break;
  3702. }
  3703. } // function loop
  3704. } // device loop
  3705. //
  3706. // Tell pnp we found some new devices
  3707. //
  3708. if (newDevices) {
  3709. IoInvalidateDeviceRelations(FdoExtension->PhysicalDeviceObject, BusRelations);
  3710. }
  3711. return STATUS_SUCCESS;
  3712. }
  3713. BOOLEAN
  3714. PciConfigureIdeController(
  3715. IN PPCI_PDO_EXTENSION PdoExtension,
  3716. IN OUT PPCI_COMMON_CONFIG Config,
  3717. IN BOOLEAN TurnOffAllNative
  3718. )
  3719. /*++
  3720. Routine Description:
  3721. If this is an IDE contoller that can be switched to native mode
  3722. and its not already there, we change the programming interface
  3723. (yes PCI 2.x does say its read only) and check if it sticks.
  3724. Assuming all went well we update the Config to reflect the change.
  3725. Arguments:
  3726. PdoExtension - PDO for the IDE controller to be switched
  3727. Config - Config header for said device
  3728. TurnOffAllNative - If TRUE indicates that we are calling this from
  3729. the initial bus scan and so we should turn
  3730. thid native capable IDE controllers. If
  3731. FALSE we should turn off only if the we have
  3732. accessed the PCI_NATIVE_IDE_INTERFACE.
  3733. Return Value:
  3734. TRUE if we sucessfully switched, FALSE otherwise
  3735. Note:
  3736. We support three styles of PCI IDE controller:
  3737. - Compatible mode controllers that consume 2 ISA interrupts
  3738. and decode fixed legacy resources, together with an optional
  3739. relocateable bus master register
  3740. - Native mode controller which uses all 5 bars and the PCI
  3741. interrupt for both channels
  3742. - Controllers which can be switched between modes.
  3743. We do NOT support running one channel in native mode and one in
  3744. compatible mode.
  3745. --*/
  3746. {
  3747. BOOLEAN primaryChangeable, secondaryChangeable, primaryNative, secondaryNative, switched = FALSE;
  3748. UCHAR progIf, tempProgIf;
  3749. USHORT command;
  3750. primaryChangeable = BITS_SET(Config->ProgIf, PCI_IDE_PRIMARY_MODE_CHANGEABLE);
  3751. secondaryChangeable = BITS_SET(Config->ProgIf, PCI_IDE_SECONDARY_MODE_CHANGEABLE);
  3752. primaryNative = BITS_SET(Config->ProgIf, PCI_IDE_PRIMARY_NATIVE_MODE);
  3753. secondaryNative = BITS_SET(Config->ProgIf, PCI_IDE_SECONDARY_NATIVE_MODE);
  3754. //
  3755. // Don't touch controllers we don't support - leave ATAPI to deal with it!
  3756. //
  3757. if ((primaryNative != secondaryNative)
  3758. || (primaryChangeable != secondaryChangeable)) {
  3759. DbgPrint("PCI: Warning unsupported IDE controller configuration for VEN_%04x&DEV_%04x!",
  3760. PdoExtension->VendorId,
  3761. PdoExtension->DeviceId
  3762. );
  3763. return FALSE;
  3764. } else if (primaryNative && secondaryNative
  3765. && (TurnOffAllNative || PdoExtension->IoSpaceUnderNativeIdeControl)) {
  3766. //
  3767. // For a fully native mode controller turn off the IO decode.
  3768. // In recent controllers MSFT has requested that this prevent
  3769. // the PCI interrupt from being asserted to close a race condition
  3770. // that can occur if an IDE device interrupts before the IDE driver
  3771. // has been loaded on the PCI device. This is not a issue for
  3772. // compatible mode controllers because they use edge triggered
  3773. // interrupts that can be dismissed as spurious at the interrupt
  3774. // controller, unlike the shared, level triggered, PCI interrups
  3775. // of native mode.
  3776. //
  3777. // Once loaded and having connected its interrupt the IDE driver
  3778. // will renable IO space access.
  3779. //
  3780. // We only do this during the initial bus scan or if the IDE driver
  3781. // has requested it through the PCI_NATIVE_IDE_INTERFACE. This is
  3782. // to avoid not enabling IoSpace for 3rd party native IDE controllers
  3783. // with their own drivers.
  3784. //
  3785. PciGetCommandRegister(PdoExtension, &command);
  3786. command &= ~PCI_ENABLE_IO_SPACE;
  3787. PciSetCommandRegister(PdoExtension, command);
  3788. Config->Command = command;
  3789. } else if (primaryChangeable && secondaryChangeable
  3790. && (PdoExtension->BIOSAllowsIDESwitchToNativeMode
  3791. && !(PdoExtension->HackFlags & PCI_HACK_BAD_NATIVE_IDE))) {
  3792. //
  3793. // If we aren't already in native mode, the controller can change modes
  3794. // and the bios is ammenable then do so...
  3795. //
  3796. PciDecodeEnable(PdoExtension, FALSE, NULL);
  3797. PciGetCommandRegister(PdoExtension, &Config->Command);
  3798. progIf = Config->ProgIf | (PCI_IDE_PRIMARY_NATIVE_MODE
  3799. | PCI_IDE_SECONDARY_NATIVE_MODE);
  3800. PciWriteDeviceConfig(PdoExtension,
  3801. &progIf,
  3802. FIELD_OFFSET(PCI_COMMON_CONFIG, ProgIf),
  3803. sizeof(progIf)
  3804. );
  3805. //
  3806. // Check if it stuck
  3807. //
  3808. PciReadDeviceConfig(PdoExtension,
  3809. &tempProgIf,
  3810. FIELD_OFFSET(PCI_COMMON_CONFIG, ProgIf),
  3811. sizeof(tempProgIf)
  3812. );
  3813. if (tempProgIf == progIf) {
  3814. //
  3815. // If it stuck, remember we did this
  3816. //
  3817. Config->ProgIf = progIf;
  3818. PdoExtension->ProgIf = progIf;
  3819. switched = TRUE;
  3820. //
  3821. // Zero the first 4 bars in the config space because they might have
  3822. // bogus values in them...
  3823. //
  3824. RtlZeroMemory(Config->u.type0.BaseAddresses,
  3825. 4 * sizeof(Config->u.type0.BaseAddresses[0]));
  3826. PciWriteDeviceConfig(PdoExtension,
  3827. &Config->u.type0.BaseAddresses,
  3828. FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.BaseAddresses),
  3829. 4 * sizeof(Config->u.type0.BaseAddresses[0])
  3830. );
  3831. //
  3832. // Read back what stuck into the config which we are going to generate
  3833. // requirements from
  3834. //
  3835. PciReadDeviceConfig(PdoExtension,
  3836. &Config->u.type0.BaseAddresses,
  3837. FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.BaseAddresses),
  3838. 4 * sizeof(Config->u.type0.BaseAddresses[0])
  3839. );
  3840. PciReadDeviceConfig(PdoExtension,
  3841. &Config->u.type0.InterruptPin,
  3842. FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.InterruptPin),
  3843. sizeof(Config->u.type0.InterruptPin)
  3844. );
  3845. } else {
  3846. DbgPrint("PCI: Warning failed switch to native mode for IDE controller VEN_%04x&DEV_%04x!",
  3847. Config->VendorID,
  3848. Config->DeviceID
  3849. );
  3850. }
  3851. }
  3852. return switched;
  3853. }