Leaked source code of windows server 2003
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.

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