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

5240 lines
141 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. irqarb.c
  5. Abstract:
  6. This module implements an arbiter for IRQs.
  7. In a traditional machine, the BIOS sets up the
  8. mapping of PCI interrupt sources (i.e. Bus 0, slot 4,
  9. funtion 1, INT B maps to IRQ 10.) This mapping is
  10. then forever fixed. On the other hand, an ACPI
  11. machine can possibly change these mappings by
  12. manipulating the "link nodes" in the AML namespace.
  13. Since the ACPI driver is the agent of change, it is the
  14. place to implement an arbiter.
  15. Author:
  16. Jake Oshins (jakeo) 6-2-97
  17. Environment:
  18. NT Kernel Model Driver only
  19. Revision History:
  20. --*/
  21. #include "pch.h"
  22. #include "string.h"
  23. #include "stdlib.h"
  24. #include "stdio.h"
  25. #if DBG
  26. extern LONG ArbDebugLevel;
  27. #define DEBUG_PRINT(Level, Message) \
  28. if (ArbDebugLevel >= Level) DbgPrint Message
  29. #else
  30. #define DEBUG_PRINT(Level, Message)
  31. #endif
  32. #define PciBridgeSwizzle(device, pin) \
  33. ((((pin - 1) + (device % 4)) % 4) + 1)
  34. NTSTATUS
  35. AcpiArbUnpackRequirement(
  36. IN PIO_RESOURCE_DESCRIPTOR Descriptor,
  37. OUT PULONGLONG Minimum,
  38. OUT PULONGLONG Maximum,
  39. OUT PULONG Length,
  40. OUT PULONG Alignment
  41. );
  42. NTSTATUS
  43. AcpiArbPackResource(
  44. IN PIO_RESOURCE_DESCRIPTOR Requirement,
  45. IN ULONGLONG Start,
  46. OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor
  47. );
  48. LONG
  49. AcpiArbScoreRequirement(
  50. IN PIO_RESOURCE_DESCRIPTOR Descriptor
  51. );
  52. NTSTATUS
  53. AcpiArbUnpackResource(
  54. IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
  55. OUT PULONGLONG Start,
  56. OUT PULONG Length
  57. );
  58. NTSTATUS
  59. AcpiArbTestAllocation(
  60. IN PARBITER_INSTANCE Arbiter,
  61. IN OUT PLIST_ENTRY ArbitrationList
  62. );
  63. NTSTATUS
  64. AcpiArbBootAllocation(
  65. IN PARBITER_INSTANCE Arbiter,
  66. IN OUT PLIST_ENTRY ArbitrationList
  67. );
  68. NTSTATUS
  69. AcpiArbRetestAllocation(
  70. IN PARBITER_INSTANCE Arbiter,
  71. IN OUT PLIST_ENTRY ArbitrationList
  72. );
  73. NTSTATUS
  74. AcpiArbRollbackAllocation(
  75. PARBITER_INSTANCE Arbiter
  76. );
  77. NTSTATUS
  78. AcpiArbCommitAllocation(
  79. PARBITER_INSTANCE Arbiter
  80. );
  81. BOOLEAN
  82. AcpiArbGetNextAllocationRange(
  83. IN PARBITER_INSTANCE Arbiter,
  84. IN OUT PARBITER_ALLOCATION_STATE State
  85. );
  86. NTSTATUS
  87. AcpiArbCrackPRT(
  88. IN PDEVICE_OBJECT Pdo,
  89. IN OUT PNSOBJ *LinkNode,
  90. IN OUT ULONG *Vector
  91. );
  92. PDEVICE_OBJECT
  93. AcpiGetFilter(
  94. IN PDEVICE_OBJECT Root,
  95. IN PDEVICE_OBJECT Pdo
  96. );
  97. BOOLEAN
  98. ArbFindSuitableRange(
  99. PARBITER_INSTANCE Arbiter,
  100. PARBITER_ALLOCATION_STATE State
  101. );
  102. BOOLEAN
  103. AcpiArbFindSuitableRange(
  104. PARBITER_INSTANCE Arbiter,
  105. PARBITER_ALLOCATION_STATE State
  106. );
  107. VOID
  108. AcpiArbAddAllocation(
  109. IN PARBITER_INSTANCE Arbiter,
  110. IN PARBITER_ALLOCATION_STATE State
  111. );
  112. VOID
  113. AcpiArbBacktrackAllocation(
  114. IN PARBITER_INSTANCE Arbiter,
  115. IN PARBITER_ALLOCATION_STATE State
  116. );
  117. BOOLEAN
  118. AcpiArbOverrideConflict(
  119. IN PARBITER_INSTANCE Arbiter,
  120. IN PARBITER_ALLOCATION_STATE State
  121. );
  122. BOOLEAN
  123. LinkNodeInUse(
  124. IN PARBITER_INSTANCE Arbiter,
  125. IN PNSOBJ LinkNode,
  126. IN OUT ULONG *Irq, OPTIONAL
  127. IN OUT UCHAR *Flags OPTIONAL
  128. );
  129. NTSTATUS
  130. AcpiArbReferenceLinkNode(
  131. IN PARBITER_INSTANCE Arbiter,
  132. IN PNSOBJ LinkNode,
  133. IN ULONG Irq
  134. );
  135. NTSTATUS
  136. AcpiArbDereferenceLinkNode(
  137. IN PARBITER_INSTANCE Arbiter,
  138. IN PNSOBJ LinkNode
  139. );
  140. NTSTATUS
  141. AcpiArbSetLinkNodeIrq(
  142. IN PNSOBJ LinkNode,
  143. IN PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq
  144. );
  145. NTSTATUS
  146. EXPORT
  147. AcpiArbSetLinkNodeIrqWorker(
  148. IN PNSOBJ AcpiObject,
  149. IN NTSTATUS Status,
  150. IN POBJDATA Result,
  151. IN PVOID Context
  152. );
  153. NTSTATUS
  154. AcpiArbSetLinkNodeIrqAsync(
  155. IN PNSOBJ LinkNode,
  156. IN PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq,
  157. IN PFNACB CompletionHandler,
  158. IN PVOID CompletionContext
  159. );
  160. NTSTATUS
  161. AcpiArbPreprocessEntry(
  162. IN PARBITER_INSTANCE Arbiter,
  163. IN PARBITER_ALLOCATION_STATE State
  164. );
  165. NTSTATUS
  166. AcpiArbQueryConflict(
  167. IN PARBITER_INSTANCE Arbiter,
  168. IN PDEVICE_OBJECT PhysicalDeviceObject,
  169. IN PIO_RESOURCE_DESCRIPTOR ConflictingResource,
  170. OUT PULONG ConflictCount,
  171. OUT PARBITER_CONFLICT_INFO *Conflicts
  172. );
  173. NTSTATUS
  174. EXPORT
  175. IrqArbRestoreIrqRoutingWorker(
  176. IN PNSOBJ AcpiObject,
  177. IN NTSTATUS Status,
  178. IN POBJDATA Result,
  179. IN PVOID Context
  180. );
  181. NTSTATUS
  182. DisableLinkNodesAsync(
  183. IN PNSOBJ Root,
  184. IN PFNACB CompletionHandler,
  185. IN PVOID CompletionContext
  186. );
  187. NTSTATUS
  188. EXPORT
  189. DisableLinkNodesAsyncWorker(
  190. IN PNSOBJ AcpiObject,
  191. IN NTSTATUS Status,
  192. IN POBJDATA Result,
  193. IN PVOID Context
  194. );
  195. NTSTATUS
  196. UnreferenceArbitrationList(
  197. IN PARBITER_INSTANCE Arbiter,
  198. IN OUT PLIST_ENTRY ArbitrationList
  199. );
  200. NTSTATUS
  201. ClearTempLinkNodeCounts(
  202. IN PARBITER_INSTANCE Arbiter
  203. );
  204. NTSTATUS
  205. MakeTempLinkNodeCountsPermanent(
  206. IN PARBITER_INSTANCE Arbiter
  207. );
  208. PVECTOR_BLOCK
  209. HashVector(
  210. IN ULONG Vector
  211. );
  212. NTSTATUS
  213. AddVectorToTable(
  214. IN ULONG Vector,
  215. IN UCHAR ReferenceCount,
  216. IN UCHAR TempRefCount,
  217. IN UCHAR Flags
  218. );
  219. VOID
  220. ClearTempVectorCounts(
  221. VOID
  222. );
  223. VOID
  224. MakeTempVectorCountsPermanent(
  225. VOID
  226. );
  227. VOID
  228. DumpVectorTable(
  229. VOID
  230. );
  231. VOID
  232. DereferenceVector(
  233. IN ULONG Vector
  234. );
  235. VOID
  236. ReferenceVector(
  237. IN ULONG Vector,
  238. IN UCHAR Flags
  239. );
  240. NTSTATUS
  241. LookupIsaVectorOverride(
  242. IN ULONG IsaVector,
  243. IN OUT ULONG *RedirectionVector OPTIONAL,
  244. IN OUT UCHAR *Flags OPTIONAL
  245. );
  246. NTSTATUS
  247. GetLinkNodeFlags(
  248. IN PARBITER_INSTANCE Arbiter,
  249. IN PNSOBJ LinkNode,
  250. IN OUT UCHAR *Flags
  251. );
  252. NTSTATUS
  253. GetIsaVectorFlags(
  254. IN ULONG Vector,
  255. IN OUT UCHAR *Flags
  256. );
  257. VOID
  258. TrackDevicesConnectedToLinkNode(
  259. IN PNSOBJ LinkNode,
  260. IN PDEVICE_OBJECT Pdo
  261. );
  262. NTSTATUS
  263. FindVectorInAlternatives(
  264. IN PARBITER_INSTANCE Arbiter,
  265. IN PARBITER_ALLOCATION_STATE State,
  266. IN ULONGLONG Vector,
  267. OUT ULONG *Alternative
  268. );
  269. NTSTATUS
  270. FindBootConfig(
  271. IN PARBITER_INSTANCE Arbiter,
  272. IN PARBITER_ALLOCATION_STATE State,
  273. IN ULONGLONG *Vector
  274. );
  275. //
  276. // The following is a hash table. It is VECTOR_HASH_TABLE_LENGTH entries
  277. // long and VECTOR_HASH_TABLE_WIDTH entries wide. We hash on the numerical
  278. // value of the IRQ modulo the length of the table. We look across the
  279. // table until we find the entry that matches the vector. If we get to
  280. // the end of the row and find an entry marked with TOKEN_VALUE, we follow
  281. // the pointer to an extension of this row in the table.
  282. //
  283. // ---------------------------------------------------------------------
  284. //| (IRQ number, ref counts, flags) | (IRQ number, ref counts, flags)
  285. //| (IRQ number, ref counts, flags) | (IRQ number, ref counts, flags)
  286. //| (IRQ number, ref counts, flags) | (TOKEN_VALUE, pointer to new table row)
  287. //| (IRQ number, ref counts, flags) | (unused entry (0))
  288. //| (IRQ number, ref counts, flags) | (IRQ number, ref counts, flags)
  289. //----------------------------------------------------------------------
  290. //
  291. // New table row, pointed to by pointer following TOKEN_VALUE:
  292. //
  293. //----------------------------------------------------------
  294. //| (IRQ number, ref counts, flags) | (unused entry (0))
  295. //----------------------------------------------------------
  296. //
  297. #define HASH_ENTRY(x, y) \
  298. (IrqHashTable + (x * VECTOR_HASH_TABLE_WIDTH) + y)
  299. PVECTOR_BLOCK IrqHashTable;
  300. ULONG InterruptModel = 0;
  301. ULONG AcpiSciVector;
  302. UCHAR AcpiIrqDefaultBootConfig = 0;
  303. UCHAR AcpiArbPciAlternativeRotation = 0;
  304. BOOLEAN AcpiArbCardbusPresent = FALSE;
  305. enum {
  306. AcpiIrqDistributionDispositionDontCare = 0,
  307. AcpiIrqDistributionDispositionSpreadOut,
  308. AcpiIrqDistributionDispositionStackUp
  309. } AcpiIrqDistributionDisposition = 0;
  310. typedef enum {
  311. AcpiIrqNextRangeMinState = 0xfff,
  312. AcpiIrqNextRangeInit,
  313. AcpiIrqNextRangeInitPolicyNeutral,
  314. AcpiIrqNextRangeInitPic,
  315. AcpiIrqNextRangeInitLegacy,
  316. AcpiIrqNextRangeBootRegAlternative,
  317. AcpiIrqNextRangeSciAlternative,
  318. AcpiIrqNextRangeUseBootConfig,
  319. AcpiIrqNextRangeAlternativeZero,
  320. AcpiIrqNextRangeAlternativeN,
  321. AcpiIrqNextRangeMaxState
  322. } NEXT_RANGE_STATE, *PNEXT_RANGE_STATE;
  323. #define ARBITER_INTERRUPT_LEVEL_SENSATIVE 0x10
  324. #define ARBITER_INTERRUPT_LATCHED 0x20
  325. #define ARBITER_INTERRUPT_BITS (ARBITER_INTERRUPT_LATCHED | ARBITER_INTERRUPT_LEVEL_SENSATIVE)
  326. #define ISA_PIC_VECTORS 16
  327. #define ALTERNATIVE_SHUFFLE_SIZE 0x10
  328. extern BOOLEAN AcpiInterruptRoutingFailed;
  329. extern PACPIInformation AcpiInformation;
  330. #ifdef ALLOC_PRAGMA
  331. #pragma alloc_text(PAGE, AcpiInitIrqArbiter)
  332. #pragma alloc_text(PAGE, AcpiArbInitializePciRouting)
  333. #pragma alloc_text(PAGE, AcpiArbUnpackRequirement)
  334. #pragma alloc_text(PAGE, AcpiArbPackResource)
  335. #pragma alloc_text(PAGE, AcpiArbScoreRequirement)
  336. #pragma alloc_text(PAGE, AcpiArbUnpackResource)
  337. #pragma alloc_text(PAGE, AcpiArbFindSuitableRange)
  338. #pragma alloc_text(PAGE, AcpiArbAddAllocation)
  339. #pragma alloc_text(PAGE, AcpiArbBacktrackAllocation)
  340. #pragma alloc_text(PAGE, AcpiArbGetLinkNodeOptions)
  341. #pragma alloc_text(PAGE, AcpiArbTestAllocation)
  342. #pragma alloc_text(PAGE, AcpiArbBootAllocation)
  343. #pragma alloc_text(PAGE, AcpiArbRetestAllocation)
  344. #pragma alloc_text(PAGE, AcpiArbRollbackAllocation)
  345. #pragma alloc_text(PAGE, AcpiArbCommitAllocation)
  346. #pragma alloc_text(PAGE, AcpiArbReferenceLinkNode)
  347. #pragma alloc_text(PAGE, AcpiArbDereferenceLinkNode)
  348. #pragma alloc_text(PAGE, AcpiArbSetLinkNodeIrq)
  349. #pragma alloc_text(PAGE, AcpiArbPreprocessEntry)
  350. #pragma alloc_text(PAGE, AcpiArbOverrideConflict)
  351. #pragma alloc_text(PAGE, AcpiArbQueryConflict)
  352. #pragma alloc_text(PAGE, AcpiArbGetNextAllocationRange)
  353. #pragma alloc_text(PAGE, LinkNodeInUse)
  354. #pragma alloc_text(PAGE, GetLinkNodeFlags)
  355. #pragma alloc_text(PAGE, UnreferenceArbitrationList)
  356. #pragma alloc_text(PAGE, ClearTempLinkNodeCounts)
  357. #pragma alloc_text(PAGE, MakeTempLinkNodeCountsPermanent)
  358. #pragma alloc_text(PAGE, HashVector)
  359. #pragma alloc_text(PAGE, GetVectorProperties)
  360. #pragma alloc_text(PAGE, AddVectorToTable)
  361. #pragma alloc_text(PAGE, ReferenceVector)
  362. #pragma alloc_text(PAGE, DereferenceVector)
  363. #pragma alloc_text(PAGE, ClearTempVectorCounts)
  364. #pragma alloc_text(PAGE, MakeTempVectorCountsPermanent)
  365. #pragma alloc_text(PAGE, TrackDevicesConnectedToLinkNode)
  366. #pragma alloc_text(PAGE, LookupIsaVectorOverride)
  367. #pragma alloc_text(PAGE, GetIsaVectorFlags)
  368. #pragma alloc_text(PAGE, FindVectorInAlternatives)
  369. #pragma alloc_text(PAGE, FindBootConfig)
  370. #endif
  371. NTSTATUS
  372. AcpiInitIrqArbiter(
  373. PDEVICE_OBJECT RootFdo
  374. )
  375. {
  376. AMLISUPP_CONTEXT_PASSIVE context;
  377. PARBITER_EXTENSION arbExt;
  378. NTSTATUS status;
  379. ULONG rawVector, adjVector, level;
  380. UCHAR flags;
  381. UCHAR buffer[PCI_COMMON_HDR_LENGTH];
  382. PPCI_COMMON_CONFIG pciData;
  383. BOOLEAN foundBootConfig, noBootConfigAgreement;
  384. ULONG deviceNum, funcNum;
  385. UCHAR lastBus, currentBus;
  386. PCI_SLOT_NUMBER pciSlot;
  387. UNICODE_STRING driverKey;
  388. HANDLE driverKeyHandle = NULL;
  389. PKEY_VALUE_PARTIAL_INFORMATION_ALIGN64 regValue=NULL;
  390. PAGED_CODE();
  391. //
  392. // Set up arbiter.
  393. //
  394. arbExt = ExAllocatePoolWithTag(NonPagedPool, sizeof(ARBITER_EXTENSION), ACPI_ARBITER_POOLTAG);
  395. if (!arbExt) {
  396. return STATUS_INSUFFICIENT_RESOURCES;
  397. }
  398. RtlZeroMemory(arbExt, sizeof(ARBITER_EXTENSION));
  399. InitializeListHead(&(arbExt->LinkNodeHead));
  400. AcpiArbiter.ArbiterState.Extension = arbExt;
  401. AcpiArbiter.DeviceObject = RootFdo;
  402. AcpiArbiter.ArbiterState.UnpackRequirement = AcpiArbUnpackRequirement;
  403. AcpiArbiter.ArbiterState.PackResource = AcpiArbPackResource;
  404. AcpiArbiter.ArbiterState.UnpackResource = AcpiArbUnpackResource;
  405. AcpiArbiter.ArbiterState.ScoreRequirement = AcpiArbScoreRequirement;
  406. AcpiArbiter.ArbiterState.FindSuitableRange = AcpiArbFindSuitableRange;
  407. AcpiArbiter.ArbiterState.TestAllocation = AcpiArbTestAllocation;
  408. AcpiArbiter.ArbiterState.BootAllocation = AcpiArbBootAllocation;
  409. AcpiArbiter.ArbiterState.RetestAllocation = AcpiArbRetestAllocation;
  410. AcpiArbiter.ArbiterState.RollbackAllocation = AcpiArbRollbackAllocation;
  411. AcpiArbiter.ArbiterState.CommitAllocation = AcpiArbCommitAllocation;
  412. AcpiArbiter.ArbiterState.AddAllocation = AcpiArbAddAllocation;
  413. AcpiArbiter.ArbiterState.BacktrackAllocation = AcpiArbBacktrackAllocation;
  414. AcpiArbiter.ArbiterState.PreprocessEntry = AcpiArbPreprocessEntry;
  415. AcpiArbiter.ArbiterState.OverrideConflict = AcpiArbOverrideConflict;
  416. AcpiArbiter.ArbiterState.QueryConflict = AcpiArbQueryConflict;
  417. AcpiArbiter.ArbiterState.GetNextAllocationRange = AcpiArbGetNextAllocationRange;
  418. IrqHashTable = ExAllocatePoolWithTag(PagedPool,
  419. VECTOR_HASH_TABLE_SIZE,
  420. ACPI_ARBITER_POOLTAG
  421. );
  422. if (!IrqHashTable) {
  423. status = STATUS_INSUFFICIENT_RESOURCES;
  424. goto AcpiInitIrqArbiterError;
  425. }
  426. RtlFillMemory(IrqHashTable,
  427. VECTOR_HASH_TABLE_SIZE,
  428. (UCHAR)(EMPTY_BLOCK_VALUE & 0xff));
  429. //
  430. // Do the generic part of initialization.
  431. //
  432. status = ArbInitializeArbiterInstance(&AcpiArbiter.ArbiterState,
  433. RootFdo,
  434. CmResourceTypeInterrupt,
  435. L"ACPI_IRQ",
  436. L"Root",
  437. NULL
  438. );
  439. if (!NT_SUCCESS(status)) {
  440. status = STATUS_UNSUCCESSFUL;
  441. goto AcpiInitIrqArbiterError;
  442. }
  443. //
  444. // Now claim the IRQ that ACPI itself is using.
  445. //
  446. rawVector = AcpiInformation->FixedACPIDescTable->sci_int_vector;
  447. //
  448. // Assume that the ACPI vector is active low,
  449. // level triggered. (This may be changed
  450. // by the MAPIC table.)
  451. //
  452. flags = VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
  453. adjVector = rawVector;
  454. LookupIsaVectorOverride(adjVector,
  455. &adjVector,
  456. &flags);
  457. RtlAddRange(AcpiArbiter.ArbiterState.Allocation,
  458. (ULONGLONG)adjVector,
  459. (ULONGLONG)adjVector,
  460. 0,
  461. RTL_RANGE_LIST_ADD_SHARED,
  462. NULL,
  463. ((PDEVICE_EXTENSION)RootFdo->DeviceExtension)->PhysicalDeviceObject
  464. );
  465. //
  466. // Record the status for this vector
  467. //
  468. ReferenceVector(adjVector,
  469. flags);
  470. AcpiSciVector = adjVector;
  471. MakeTempVectorCountsPermanent();
  472. //
  473. // Disable all the link nodes in the namespace so that we
  474. // have a fresh slate to work with.
  475. //
  476. KeInitializeEvent(&context.Event, SynchronizationEvent, FALSE);
  477. context.Status = STATUS_UNSUCCESSFUL;
  478. status = DisableLinkNodesAsync(((PDEVICE_EXTENSION)RootFdo->DeviceExtension)->AcpiObject,
  479. AmlisuppCompletePassive,
  480. (PVOID)&context);
  481. if (status == STATUS_PENDING) {
  482. KeWaitForSingleObject(&context.Event,
  483. Executive,
  484. KernelMode,
  485. FALSE,
  486. NULL);
  487. status = context.Status;
  488. }
  489. //
  490. // Scan the machine looking at its initial configuration. If
  491. // it a) has a cardbus controller and b) all the boot configs
  492. // for PCI devices are the same, then record that boot config
  493. // vector for use in AcpiArbGetNextAllocationRange.
  494. //
  495. // Note: This algorithm only scans the first PCI root and
  496. // its children. The assumption is that multiple root machines
  497. // will be running in APIC mode or have many different boot
  498. // configs.
  499. //
  500. pciData = (PPCI_COMMON_CONFIG)buffer;
  501. lastBus = 0;
  502. currentBus = 0;
  503. foundBootConfig = FALSE;
  504. noBootConfigAgreement = FALSE;
  505. while (TRUE) {
  506. pciSlot.u.AsULONG = 0;
  507. for (deviceNum = 0; deviceNum < PCI_MAX_DEVICES; deviceNum++) {
  508. for (funcNum = 0; funcNum < PCI_MAX_FUNCTION; funcNum++) {
  509. pciSlot.u.bits.DeviceNumber = deviceNum;
  510. pciSlot.u.bits.FunctionNumber = funcNum;
  511. HalPciInterfaceReadConfig(NULL,
  512. currentBus,
  513. pciSlot.u.AsULONG,
  514. pciData,
  515. 0,
  516. PCI_COMMON_HDR_LENGTH);
  517. if (pciData->VendorID != PCI_INVALID_VENDORID) {
  518. if (PCI_CONFIGURATION_TYPE(pciData) == PCI_DEVICE_TYPE) {
  519. if (pciData->u.type0.InterruptPin) {
  520. //
  521. // This device generates an interrupt.
  522. //
  523. if ((pciData->u.type0.InterruptLine > 0) &&
  524. (pciData->u.type0.InterruptLine < 0xff)) {
  525. //
  526. // And it has a boot config.
  527. //
  528. if (foundBootConfig) {
  529. if (pciData->u.type0.InterruptLine != AcpiIrqDefaultBootConfig) {
  530. noBootConfigAgreement = TRUE;
  531. break;
  532. }
  533. } else {
  534. //
  535. // Record this boot config
  536. //
  537. AcpiIrqDefaultBootConfig = pciData->u.type0.InterruptLine;
  538. foundBootConfig = TRUE;
  539. }
  540. }
  541. }
  542. } else {
  543. //
  544. // This is a bridge. Update lastBus with the Subordinate
  545. // bus if it is higher.
  546. //
  547. lastBus = lastBus > pciData->u.type1.SubordinateBus ?
  548. lastBus : pciData->u.type1.SubordinateBus;
  549. if (PCI_CONFIGURATION_TYPE(pciData) == PCI_CARDBUS_BRIDGE_TYPE) {
  550. AcpiArbCardbusPresent = TRUE;
  551. }
  552. }
  553. if (!PCI_MULTIFUNCTION_DEVICE(pciData) &&
  554. (funcNum == 0)) {
  555. break;
  556. }
  557. } else {
  558. break;
  559. }
  560. }
  561. }
  562. if (lastBus == currentBus++) {
  563. break;
  564. }
  565. }
  566. if (!foundBootConfig ||
  567. noBootConfigAgreement ||
  568. !AcpiArbCardbusPresent) {
  569. //
  570. // There is no single default boot config.
  571. //
  572. AcpiIrqDefaultBootConfig = 0;
  573. }
  574. //
  575. // Now look in the registry for configuration flags.
  576. //
  577. RtlInitUnicodeString( &driverKey,
  578. L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\ACPI\\Parameters");
  579. status = OSOpenUnicodeHandle(
  580. &driverKey,
  581. NULL,
  582. &driverKeyHandle);
  583. if (NT_SUCCESS(status)) {
  584. status = OSGetRegistryValue(
  585. driverKeyHandle,
  586. L"IRQDistribution",
  587. &regValue);
  588. if (NT_SUCCESS(status)) {
  589. if ((regValue->DataLength != 0) &&
  590. (regValue->Type == REG_DWORD)) {
  591. //
  592. // We have successfully found the key for
  593. // IRQ Distribution Disposition.
  594. //
  595. AcpiIrqDistributionDisposition =
  596. *((ULONG*)( ((PUCHAR)regValue->Data)));
  597. }
  598. ExFreePool(regValue);
  599. }
  600. status = OSGetRegistryValue(
  601. driverKeyHandle,
  602. L"ForcePCIBootConfig",
  603. &regValue);
  604. if (NT_SUCCESS(status)) {
  605. if ((regValue->DataLength != 0) &&
  606. (regValue->Type == REG_DWORD)) {
  607. //
  608. // We have successfully found the key for
  609. // PCI Boot Configs.
  610. //
  611. AcpiIrqDefaultBootConfig =
  612. *(PUCHAR)regValue->Data;
  613. }
  614. ExFreePool(regValue);
  615. }
  616. OSCloseHandle(driverKeyHandle);
  617. }
  618. return STATUS_SUCCESS;
  619. AcpiInitIrqArbiterError:
  620. if (arbExt) ExFreePool(arbExt);
  621. if (IrqHashTable) ExFreePool(IrqHashTable);
  622. if (driverKeyHandle) OSCloseHandle(driverKeyHandle);
  623. if (regValue) ExFreePool(regValue);
  624. return status;
  625. }
  626. NTSTATUS
  627. AcpiArbInitializePciRouting(
  628. PDEVICE_OBJECT PciPdo
  629. )
  630. {
  631. PINT_ROUTE_INTERFACE_STANDARD interface;
  632. NTSTATUS status;
  633. IO_STACK_LOCATION irpSp;
  634. PWSTR buffer;
  635. PDEVICE_OBJECT topDeviceInStack;
  636. PAGED_CODE();
  637. //
  638. // Send an IRP to the PCI driver to get the Interrupt Routing Interface.
  639. //
  640. RtlZeroMemory( &irpSp, sizeof(IO_STACK_LOCATION) );
  641. interface = ExAllocatePoolWithTag(NonPagedPool, sizeof(INT_ROUTE_INTERFACE_STANDARD), ACPI_ARBITER_POOLTAG);
  642. if (!interface) {
  643. return STATUS_INSUFFICIENT_RESOURCES;
  644. }
  645. topDeviceInStack = IoGetAttachedDeviceReference(PciPdo);
  646. //
  647. // Set the function codes and parameters.
  648. //
  649. irpSp.MajorFunction = IRP_MJ_PNP;
  650. irpSp.MinorFunction = IRP_MN_QUERY_INTERFACE;
  651. irpSp.Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_INT_ROUTE_INTERFACE_STANDARD;
  652. irpSp.Parameters.QueryInterface.Version = PCI_INT_ROUTE_INTRF_STANDARD_VER;
  653. irpSp.Parameters.QueryInterface.Size = sizeof (INT_ROUTE_INTERFACE_STANDARD);
  654. irpSp.Parameters.QueryInterface.Interface = (PINTERFACE) interface;
  655. irpSp.Parameters.QueryInterface.InterfaceSpecificData = NULL;
  656. //
  657. // Call the PCI driver (indirectly.)
  658. //
  659. status = ACPIInternalSendSynchronousIrp(topDeviceInStack,
  660. &irpSp,
  661. &buffer);
  662. if (NT_SUCCESS(status)) {
  663. //
  664. // Attach this interface to the Arbiter Extension.
  665. //
  666. ((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting = interface;
  667. //
  668. // Reference it.
  669. //
  670. interface->InterfaceReference(interface->Context);
  671. PciInterfacesInstantiated = TRUE;
  672. } else {
  673. ExFreePool(interface);
  674. }
  675. ObDereferenceObject(topDeviceInStack);
  676. return status;
  677. }
  678. //
  679. // Arbiter callbacks
  680. //
  681. NTSTATUS
  682. AcpiArbUnpackRequirement(
  683. IN PIO_RESOURCE_DESCRIPTOR Descriptor,
  684. OUT PULONGLONG Minimum,
  685. OUT PULONGLONG Maximum,
  686. OUT PULONG Length,
  687. OUT PULONG Alignment
  688. )
  689. {
  690. PAGED_CODE();
  691. ASSERT(Descriptor);
  692. ASSERT(Descriptor->Type == CmResourceTypeInterrupt);
  693. *Minimum = (ULONGLONG) Descriptor->u.Interrupt.MinimumVector;
  694. *Maximum = (ULONGLONG) Descriptor->u.Interrupt.MaximumVector;
  695. *Length = 1;
  696. *Alignment = 1;
  697. return STATUS_SUCCESS;
  698. }
  699. LONG
  700. AcpiArbScoreRequirement(
  701. IN PIO_RESOURCE_DESCRIPTOR Descriptor
  702. )
  703. {
  704. LONG score;
  705. ASSERT(Descriptor);
  706. ASSERT(Descriptor->Type == CmResourceTypeInterrupt);
  707. //
  708. // TEMPTEMP HACKHACK
  709. // (Possibly) temporary hack that allows the PnP
  710. // manager to include invalid resources in the
  711. // arbitration list.
  712. //
  713. if (Descriptor->u.Interrupt.MinimumVector >
  714. Descriptor->u.Interrupt.MaximumVector) {
  715. return 0;
  716. }
  717. ASSERT(Descriptor->u.Interrupt.MinimumVector <=
  718. Descriptor->u.Interrupt.MaximumVector);
  719. score = Descriptor->u.Interrupt.MaximumVector -
  720. Descriptor->u.Interrupt.MinimumVector + 1;
  721. //
  722. // Give a little boost to any request above the
  723. // traditional ISA range.
  724. // N.B. This will probably never matter, as
  725. // most machines will present all the choices
  726. // either inside or outside of the ISA range.
  727. //
  728. if (Descriptor->u.Interrupt.MaximumVector >= 16) {
  729. score += 5;
  730. }
  731. return score;
  732. }
  733. NTSTATUS
  734. AcpiArbPackResource(
  735. IN PIO_RESOURCE_DESCRIPTOR Requirement,
  736. IN ULONGLONG Start,
  737. OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor
  738. )
  739. {
  740. PAGED_CODE();
  741. ASSERT(Descriptor);
  742. ASSERT(Start < ((ULONG)-1));
  743. ASSERT(Requirement);
  744. ASSERT(Requirement->Type == CmResourceTypeInterrupt);
  745. Descriptor->Type = CmResourceTypeInterrupt;
  746. Descriptor->Flags = Requirement->Flags;
  747. Descriptor->ShareDisposition = Requirement->ShareDisposition;
  748. Descriptor->u.Interrupt.Vector = (ULONG) Start;
  749. Descriptor->u.Interrupt.Level = (ULONG) Start;
  750. Descriptor->u.Interrupt.Affinity = 0xFFFFFFFF;
  751. return STATUS_SUCCESS;
  752. }
  753. NTSTATUS
  754. AcpiArbUnpackResource(
  755. IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
  756. OUT PULONGLONG Start,
  757. OUT PULONG Length
  758. )
  759. {
  760. ASSERT(Descriptor);
  761. ASSERT(Descriptor->Type == CmResourceTypeInterrupt);
  762. *Start = Descriptor->u.Interrupt.Vector;
  763. *Length = 1;
  764. return STATUS_SUCCESS;
  765. }
  766. BOOLEAN
  767. AcpiArbOverrideConflict(
  768. IN PARBITER_INSTANCE Arbiter,
  769. IN PARBITER_ALLOCATION_STATE State
  770. )
  771. {
  772. //
  773. // Self-conflicts are not allowable with this arbiter.
  774. //
  775. PAGED_CODE();
  776. return FALSE;
  777. }
  778. NTSTATUS
  779. AcpiArbPreprocessEntry(
  780. IN PARBITER_INSTANCE Arbiter,
  781. IN PARBITER_ALLOCATION_STATE State
  782. )
  783. /*++
  784. Routine Description:
  785. This routine is called from AllocateEntry to allow preprocessing of
  786. entries
  787. Arguments:
  788. Arbiter - The instance data of the arbiter who was called.
  789. State - The state of the current arbitration.
  790. Return Value:
  791. None.
  792. --*/
  793. {
  794. #define CM_RESOURE_INTERRUPT_LEVEL_LATCHED_BITS 0x0001
  795. PARBITER_ALTERNATIVE current;
  796. USHORT flags;
  797. PAGED_CODE();
  798. //
  799. // Check if this is a level (PCI) or latched (ISA) interrupt and set
  800. // RangeAttributes accordingly so we set the appropriate flag when we add the
  801. // range
  802. //
  803. if ((State->Alternatives[0].Descriptor->Flags
  804. & CM_RESOURE_INTERRUPT_LEVEL_LATCHED_BITS)
  805. == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE) {
  806. State->RangeAttributes &= ~ARBITER_INTERRUPT_BITS;
  807. State->RangeAttributes |= ARBITER_INTERRUPT_LEVEL_SENSATIVE;
  808. flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
  809. } else {
  810. ASSERT(State->Alternatives[0].Descriptor->Flags
  811. & CM_RESOURCE_INTERRUPT_LATCHED);
  812. State->RangeAttributes &= ~ARBITER_INTERRUPT_BITS;
  813. State->RangeAttributes |= ARBITER_INTERRUPT_LATCHED;
  814. flags = CM_RESOURCE_INTERRUPT_LATCHED;
  815. }
  816. #if 0
  817. //
  818. // Make sure that all the alternatives are of the same type
  819. //
  820. FOR_ALL_IN_ARRAY(State->Alternatives,
  821. State->Entry->AlternativeCount,
  822. current) {
  823. ASSERT((current->Descriptor->Flags
  824. & CM_RESOURE_INTERRUPT_LEVEL_LATCHED_BITS) == flags);
  825. }
  826. #endif
  827. return STATUS_SUCCESS;
  828. }
  829. BOOLEAN
  830. AcpiArbFindSuitableRange(
  831. PARBITER_INSTANCE Arbiter,
  832. PARBITER_ALLOCATION_STATE State
  833. )
  834. /*++
  835. Routine Description:
  836. This routine finds an IRQ for a device object. For
  837. non-PCI devices, this is as simple as returning the
  838. result from PnpFindSuitableRange. For PCI devices,
  839. this is done by examining the state of the "link
  840. nodes" described in the ACPI namespace.
  841. Arguments:
  842. Arbiter - the ACPI IRQ arbiter
  843. State - the current allocation under consideration
  844. Return Value:
  845. TRUE if a suitable vector has been found,
  846. FALSE otherwise.
  847. Notes:
  848. Statement of algorithm for PCI devices:
  849. 1) Find the entry in the _PRT that corresponds
  850. to the device for which we are arbitrating
  851. resources.
  852. 2) Determine, from the _PRT information, whether
  853. this device is connected to a "link node."
  854. (A PCI device will typically be connected to
  855. a link node while in PIC mode but not in
  856. APIC mode.)
  857. 3) If it is not, use the static mapping from the
  858. _PRT.
  859. 4) If it is connected to a "link node," check
  860. to see if the "link node" is in use.
  861. 5) If the link node is in use, then this
  862. device must use the same IRQ that the link node is currently
  863. using. This implies that there is already
  864. some other device in the system connected to
  865. this interrupt and now the two (or more) will
  866. be sharing. This is acceptable and inevitable.
  867. Two devices that have their interrupt lines
  868. wire-or'd should be sharing. Two devices that
  869. don't have their interrupt lines wire-or'd
  870. should be represented by separate link nodes
  871. in the namespace.
  872. 6) If the link node is not in use, pick an IRQ
  873. from the list that the link node can support
  874. and grant it to the device. There is some
  875. attempt to pick an IRQ that is not currently
  876. in use.
  877. --*/
  878. {
  879. PCM_PARTIAL_RESOURCE_DESCRIPTOR potentialIrq;
  880. PIO_RESOURCE_DESCRIPTOR alternative;
  881. PARBITER_EXTENSION arbExtension;
  882. PCM_RESOURCE_LIST linkNodeResList = NULL;
  883. NTSTATUS status;
  884. BOOLEAN possibleAllocation;
  885. PNSOBJ linkNode = NULL;
  886. ULONG deviceIrq = 0;
  887. ULONG linkNodeIrqCount, i;
  888. UCHAR vectorFlags, deviceFlags;
  889. PAGED_CODE();
  890. arbExtension = (PARBITER_EXTENSION)Arbiter->Extension;
  891. ASSERT(arbExtension);
  892. //
  893. // First, see if this resource could even be made to work at all.
  894. // (I.e. Has something already claimed this as non-sharable?)
  895. //
  896. possibleAllocation = ArbFindSuitableRange(Arbiter, State);
  897. if (!possibleAllocation) {
  898. return FALSE;
  899. }
  900. //
  901. // Is this Device connected to a link node?
  902. //
  903. status = AcpiArbCrackPRT(State->Entry->PhysicalDeviceObject,
  904. &linkNode,
  905. &deviceIrq);
  906. //
  907. // If this PDO is connected to a link node, we want to clip
  908. // the list of possible IRQ settings down.
  909. //
  910. switch (status) {
  911. case STATUS_SUCCESS:
  912. //
  913. // AcpiArbCrackPRT fills in either linkNode or deviceIrq.
  914. // If linkNode is filled in, then we need to look at it.
  915. // If deviceIrq is filled in, then we only need to clip
  916. // the list to that single IRQ.
  917. //
  918. if (linkNode) {
  919. //
  920. // If the link node is currently in use, then we can
  921. // just connect this device to the IRQ that the link
  922. // node is currently using.
  923. //
  924. if (LinkNodeInUse(Arbiter, linkNode, &deviceIrq, NULL)) {
  925. if ((State->CurrentMinimum <= deviceIrq) &&
  926. (State->CurrentMaximum >= deviceIrq)) {
  927. State->Start = deviceIrq;
  928. State->End = deviceIrq;
  929. State->CurrentAlternative->Length = 1;
  930. DEBUG_PRINT(1, ("FindSuitableRange found %x from a link node that is in use.\n",
  931. (ULONG)(State->Start & 0xffffffff)));
  932. ASSERT(HalIsVectorValid(deviceIrq));
  933. return TRUE;
  934. } else {
  935. DEBUG_PRINT(1, ("FindSuitableRange found %x from a link node that is in use.\n",
  936. deviceIrq));
  937. DEBUG_PRINT(1, (" This was, however, not within the range of possibilites (%x-%x).\n",
  938. (ULONG)(State->Start & 0xffffffff),
  939. (ULONG)(State->End & 0xffffffff)));
  940. return FALSE;
  941. }
  942. } else {
  943. //
  944. // Get the set of IRQs that this link node can
  945. // connect to.
  946. //
  947. status = AcpiArbGetLinkNodeOptions(linkNode,
  948. &linkNodeResList,
  949. &deviceFlags);
  950. DEBUG_PRINT(1, ("Link node contained CM(%p)\n", linkNodeResList));
  951. if (NT_SUCCESS(status)) {
  952. ASSERT(linkNodeResList->Count == 1);
  953. linkNodeIrqCount =
  954. linkNodeResList->List[0].PartialResourceList.Count;
  955. for (i = 0; i < linkNodeIrqCount; i++) {
  956. potentialIrq =
  957. &(linkNodeResList->List[0].PartialResourceList.PartialDescriptors[(i + AcpiArbPciAlternativeRotation) % linkNodeIrqCount]);
  958. ASSERT(potentialIrq->Type == CmResourceTypeInterrupt);
  959. //
  960. // Check for a conflict in mode.
  961. //
  962. status = GetVectorProperties(potentialIrq->u.Interrupt.Vector,
  963. &vectorFlags);
  964. if (NT_SUCCESS(status)) {
  965. //
  966. // Success here means that this vector is currently allocated
  967. // to somebody. Check to see whether the link node being
  968. // considered has the same mode and polarity as the other
  969. // thing(s) assigned to this vector.
  970. //
  971. if (deviceFlags != vectorFlags) {
  972. //
  973. // The flags don't match. So skip this possibility.
  974. //
  975. continue;
  976. }
  977. }
  978. if ((potentialIrq->u.Interrupt.Vector >= State->CurrentMinimum) &&
  979. (potentialIrq->u.Interrupt.Vector <= State->CurrentMaximum)) {
  980. if (!HalIsVectorValid(potentialIrq->u.Interrupt.Vector)) {
  981. deviceIrq = potentialIrq->u.Interrupt.Vector;
  982. ExFreePool(linkNodeResList);
  983. goto FindSuitableRangeError;
  984. }
  985. State->Start = potentialIrq->u.Interrupt.Vector;
  986. State->End = potentialIrq->u.Interrupt.Vector;
  987. State->CurrentAlternative->Length = 1;
  988. DEBUG_PRINT(1, ("FindSuitableRange found %x from an unused link node.\n",
  989. (ULONG)(State->Start & 0xffffffff)));
  990. ExFreePool(linkNodeResList);
  991. //
  992. // Record the link node that we got this from.
  993. //
  994. arbExtension->CurrentLinkNode = linkNode;
  995. //
  996. // Record this as the last PCI IRQ that we are handing out.
  997. //
  998. arbExtension->LastPciIrq[arbExtension->LastPciIrqIndex] =
  999. State->Start;
  1000. arbExtension->LastPciIrqIndex =
  1001. (arbExtension->LastPciIrqIndex + 1) % LAST_PCI_IRQ_BUFFER_SIZE;
  1002. return TRUE;
  1003. }
  1004. }
  1005. ExFreePool(linkNodeResList);
  1006. }
  1007. DEBUG_PRINT(1, ("FindSuitableRange: AcpiArbGetLinkNodeOptions returned %x.\n\tlinkNodeResList: %p\n",
  1008. status, linkNodeResList));
  1009. // We didn't find a match.
  1010. return FALSE;
  1011. }
  1012. } else {
  1013. //
  1014. // This is the case where the _PRT contains a static mapping. Static
  1015. // Mappings imply active-low, level-triggered interrupts.
  1016. //
  1017. status = GetVectorProperties(deviceIrq,
  1018. &vectorFlags);
  1019. if (NT_SUCCESS(status)) {
  1020. //
  1021. // The vector is in use.
  1022. //
  1023. if (((vectorFlags & VECTOR_MODE) != VECTOR_LEVEL) ||
  1024. ((vectorFlags & VECTOR_POLARITY) != VECTOR_ACTIVE_LOW)) {
  1025. //
  1026. // And it's flags don't match.
  1027. //
  1028. return FALSE;
  1029. }
  1030. }
  1031. // Valid static vector
  1032. if ((State->CurrentMinimum <= deviceIrq) &&
  1033. (State->CurrentMaximum >= deviceIrq)) {
  1034. DEBUG_PRINT(1, ("FindSuitableRange found %x from a static mapping.\n",
  1035. (ULONG)(State->Start & 0xffffffff)));
  1036. if (!HalIsVectorValid(deviceIrq)) {
  1037. goto FindSuitableRangeError;
  1038. }
  1039. State->Start = deviceIrq;
  1040. State->End = deviceIrq;
  1041. State->CurrentAlternative->Length = 1;
  1042. return TRUE;
  1043. } else {
  1044. return FALSE;
  1045. }
  1046. }
  1047. break;
  1048. case STATUS_UNSUCCESSFUL:
  1049. return FALSE;
  1050. break;
  1051. case STATUS_RESOURCE_REQUIREMENTS_CHANGED:
  1052. //
  1053. // Fall through to default.
  1054. //
  1055. default:
  1056. //
  1057. // Not PCI.
  1058. //
  1059. for (deviceIrq = (ULONG)(State->Start & 0xffffffff);
  1060. deviceIrq <= (ULONG)(State->End & 0xffffffff); deviceIrq++) {
  1061. status = GetIsaVectorFlags((ULONG)deviceIrq,
  1062. &deviceFlags);
  1063. if (!NT_SUCCESS(status)) {
  1064. //
  1065. // Not overridden. Assume that the device flags conform to bus.
  1066. //
  1067. deviceFlags = (State->CurrentAlternative->Descriptor->Flags
  1068. == CM_RESOURCE_INTERRUPT_LATCHED) ?
  1069. VECTOR_EDGE | VECTOR_ACTIVE_HIGH :
  1070. VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
  1071. }
  1072. status = GetVectorProperties((ULONG)deviceIrq,
  1073. &vectorFlags);
  1074. if (NT_SUCCESS(status)) {
  1075. //
  1076. // This vector is currently in use. So if this is to be a suitable
  1077. // range, then the flags must match.
  1078. //
  1079. if (deviceFlags != vectorFlags) {
  1080. continue;
  1081. }
  1082. }
  1083. if (!HalIsVectorValid(deviceIrq)) {
  1084. goto FindSuitableRangeError;
  1085. }
  1086. State->Start = deviceIrq;
  1087. State->End = deviceIrq;
  1088. State->CurrentAlternative->Length = 1;
  1089. return TRUE;
  1090. }
  1091. return FALSE;
  1092. }
  1093. return FALSE;
  1094. FindSuitableRangeError:
  1095. {
  1096. UNICODE_STRING vectorName;
  1097. PWCHAR prtEntry[2];
  1098. WCHAR IRQARBname[20];
  1099. WCHAR vectorBuff[10];
  1100. //
  1101. // Make an errorlog entry saying that the chosen IRQ doesn't
  1102. // exist.
  1103. //
  1104. swprintf( IRQARBname, L"IRQARB");
  1105. RtlInitUnicodeString(&vectorName, vectorBuff);
  1106. if (!NT_SUCCESS(RtlIntegerToUnicodeString(deviceIrq, 0, &vectorName))) {
  1107. return FALSE;
  1108. }
  1109. prtEntry[0] = IRQARBname;
  1110. prtEntry[1] = vectorBuff;
  1111. ACPIWriteEventLogEntry(ACPI_ERR_ILLEGAL_IRQ_NUMBER,
  1112. &prtEntry,
  1113. 2,
  1114. NULL,
  1115. 0);
  1116. }
  1117. return FALSE;
  1118. }
  1119. VOID
  1120. AcpiArbAddAllocation(
  1121. IN PARBITER_INSTANCE Arbiter,
  1122. IN PARBITER_ALLOCATION_STATE State
  1123. )
  1124. {
  1125. NTSTATUS status;
  1126. PNSOBJ linkNode;
  1127. ULONG sourceIndex;
  1128. ULONG rangeFlags = 0;
  1129. PVOID referencedNode = NULL;
  1130. UCHAR flags, previousFlags;
  1131. ROUTING_TOKEN token;
  1132. BOOLEAN inUse;
  1133. UCHAR attributes = 0;
  1134. PAGED_CODE();
  1135. ASSERT(State->CurrentAlternative->Descriptor->Type == CmResourceTypeInterrupt);
  1136. DEBUG_PRINT(1, ("Adding allocation for IRQ %x for device %p\n",
  1137. (ULONG)(State->Start & 0xffffffff),
  1138. State->Entry->PhysicalDeviceObject));
  1139. //
  1140. // Identify the potential link node.
  1141. //
  1142. status = AcpiArbCrackPRT(State->Entry->PhysicalDeviceObject,
  1143. &linkNode,
  1144. &sourceIndex);
  1145. if (NT_SUCCESS(status)) {
  1146. //
  1147. // PCI device. Default flags are standard for PCI.
  1148. //
  1149. flags = VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
  1150. ASSERT(State->Start == State->End);
  1151. if (!(State->Flags & ARBITER_STATE_FLAG_BOOT)) {
  1152. //
  1153. // Only keep track of link nodes manipulation if this is not
  1154. // a boot config allocation.
  1155. //
  1156. //
  1157. // If this device is connected to a link node, reference it.
  1158. //
  1159. if (linkNode) {
  1160. AcpiArbReferenceLinkNode(Arbiter,
  1161. linkNode,
  1162. (ULONG)State->Start);
  1163. referencedNode = (PVOID)linkNode;
  1164. //
  1165. // Find out what the flags for this link node are.
  1166. // Note that this is only guaranteed to be valid
  1167. // after we have referenced the link node.
  1168. //
  1169. inUse = LinkNodeInUse(Arbiter,
  1170. linkNode,
  1171. NULL,
  1172. &flags);
  1173. ASSERT(inUse);
  1174. ASSERT((flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
  1175. ASSERT(State->CurrentAlternative->Descriptor->Flags
  1176. == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE ?
  1177. (flags & VECTOR_MODE) == VECTOR_LEVEL :
  1178. (flags & VECTOR_MODE) == VECTOR_EDGE);
  1179. #if DBG
  1180. TrackDevicesConnectedToLinkNode(linkNode,
  1181. State->Entry->PhysicalDeviceObject);
  1182. status = GetVectorProperties((ULONG)State->Start,
  1183. &previousFlags);
  1184. //
  1185. // This next bit is a hack. We need to make sure that
  1186. // the boot config code doesn't try to allocate the same
  1187. // vector for two different devices that need conflicting
  1188. // modes. This should never happen, as translation
  1189. // should filter out the problemating ones before we
  1190. // get to arbitration.
  1191. //
  1192. if (NT_SUCCESS(status)) {
  1193. //
  1194. // This vector is already in use for something.
  1195. //
  1196. ASSERT((previousFlags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
  1197. ASSERT(flags == previousFlags);
  1198. }
  1199. #endif
  1200. } else {
  1201. //
  1202. // This is a PCI device that is not connected to a
  1203. // link node.
  1204. //
  1205. ASSERT(sourceIndex == State->Start);
  1206. }
  1207. } else {
  1208. if (InterruptModel == 1) {
  1209. //
  1210. // We are running in APIC mode. And we know that
  1211. // the PCI driver builds boot configs based on
  1212. // the Interrupt Line register, which only relates
  1213. // to PIC mode. So just say no to boot configs.
  1214. //
  1215. DEBUG_PRINT(1, ("Skipping this allocation. It's for a PCI device in APIC mode\n"));
  1216. return;
  1217. }
  1218. }
  1219. } else {
  1220. //
  1221. // Not a PCI device.
  1222. //
  1223. status = GetIsaVectorFlags((ULONG)State->Start,
  1224. &flags);
  1225. if (!NT_SUCCESS(status)) {
  1226. //
  1227. // Not overridden. Assume that the device flags conform to bus.
  1228. //
  1229. flags = (State->CurrentAlternative->Descriptor->Flags
  1230. == CM_RESOURCE_INTERRUPT_LATCHED) ?
  1231. VECTOR_EDGE | VECTOR_ACTIVE_HIGH :
  1232. VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
  1233. }
  1234. ASSERT((flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
  1235. }
  1236. //
  1237. // There is a possibility that this allocation is impossible.
  1238. // (We may be setting a boot-config for a device after we
  1239. // have already started using the vector elsewhere.) Just
  1240. // don't do it.
  1241. //
  1242. if (State->Flags & ARBITER_STATE_FLAG_BOOT) {
  1243. attributes |= ARBITER_RANGE_BOOT_ALLOCATED;
  1244. status = GetVectorProperties((ULONG)State->Start,
  1245. &previousFlags);
  1246. if ((NT_SUCCESS(status)) &&
  1247. ((flags & ~VECTOR_TYPE) != (previousFlags & ~VECTOR_TYPE))) {
  1248. DEBUG_PRINT(1, ("Skipping this allocation. It's for a vector that's incompatible.\n"));
  1249. return;
  1250. }
  1251. }
  1252. ReferenceVector((ULONG)State->Start,
  1253. flags);
  1254. // Figure out what flags we need to add the range
  1255. if ((flags & VECTOR_TYPE) == VECTOR_SIGNAL) {
  1256. // Non-MSI vectors can sometimes be shared and thus can have range conflicts, etc.
  1257. rangeFlags = RTL_RANGE_LIST_ADD_IF_CONFLICT +
  1258. (State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED
  1259. ? RTL_RANGE_LIST_ADD_SHARED : 0);
  1260. }
  1261. //
  1262. // Now do what the default function would do, marking this
  1263. // allocation as new.
  1264. //
  1265. status = RtlAddRange(
  1266. Arbiter->PossibleAllocation,
  1267. State->Start,
  1268. State->End,
  1269. attributes,
  1270. rangeFlags,
  1271. referencedNode, // This line is different from the default function
  1272. State->Entry->PhysicalDeviceObject
  1273. );
  1274. ASSERT(NT_SUCCESS(status));
  1275. }
  1276. VOID
  1277. AcpiArbBacktrackAllocation(
  1278. IN PARBITER_INSTANCE Arbiter,
  1279. IN PARBITER_ALLOCATION_STATE State
  1280. )
  1281. {
  1282. RTL_RANGE_LIST_ITERATOR iterator;
  1283. PRTL_RANGE current;
  1284. PNSOBJ linkNode;
  1285. PAGED_CODE();
  1286. DEBUG_PRINT(1, ("Backtracking allocation for IRQ %x for device %p\n",
  1287. State->CurrentAlternative->Descriptor->u.Interrupt.MinimumVector,
  1288. State->Entry->PhysicalDeviceObject));
  1289. ASSERT(!(State->Flags & ARBITER_STATE_FLAG_BOOT));
  1290. //
  1291. // Backtrack this assignment in the Edge/Level table.
  1292. //
  1293. DereferenceVector((ULONG)State->Start);
  1294. //
  1295. // Look for the range that we are backing out.
  1296. //
  1297. FOR_ALL_RANGES(Arbiter->PossibleAllocation, &iterator, current) {
  1298. if ((State->Entry->PhysicalDeviceObject == current->Owner) &&
  1299. (State->End == current->End) &&
  1300. (State->Start == current->Start)) {
  1301. //
  1302. // We stash the link node that we refereneced
  1303. // into Workspace.
  1304. //
  1305. linkNode = (PNSOBJ)current->UserData;
  1306. if (linkNode) {
  1307. //
  1308. // Dereference the link node that we referenced in
  1309. // AcpiArbAddAllocation.
  1310. //
  1311. AcpiArbDereferenceLinkNode(Arbiter,
  1312. linkNode);
  1313. }
  1314. break;
  1315. }
  1316. }
  1317. //
  1318. // Now call the default function.
  1319. //
  1320. ArbBacktrackAllocation(Arbiter, State);
  1321. }
  1322. NTSTATUS
  1323. UnreferenceArbitrationList(
  1324. IN PARBITER_INSTANCE Arbiter,
  1325. IN OUT PLIST_ENTRY ArbitrationList
  1326. )
  1327. {
  1328. RTL_RANGE_LIST_ITERATOR iterator;
  1329. PARBITER_LIST_ENTRY currentListEntry;
  1330. PRTL_RANGE currentRange;
  1331. NTSTATUS status;
  1332. PNSOBJ linkNode;
  1333. ULONG vector;
  1334. UCHAR flags;
  1335. PAGED_CODE();
  1336. //
  1337. // In order to keep the reference counts in line,
  1338. // we need to remove the counts that were added
  1339. // by previous allocations for this device. I.e.
  1340. // if the device is being rebalanced, we need to
  1341. // start by getting rid of the reference to the old
  1342. // vector.
  1343. //
  1344. // This is also necessary for references that were
  1345. // added as part of boot configs. But boot configs
  1346. // are a special case. There are a (very) few devices
  1347. // that want more than one IRQ. And it is possible
  1348. // that the boot config only reserved a single IRQ.
  1349. // (There is a Lucent Winmodem that actually does
  1350. // this.) We need to make sure that we only
  1351. // dereference the vector as many times as it was
  1352. // previously referenced.
  1353. //
  1354. // Note that there are still a few cases that this
  1355. // function doesn't handle. If a device lowers
  1356. // the number of IRQs that it wants dynamically,
  1357. // separate from its boot config, we will get out
  1358. // of synch. If a vector has both a boot config
  1359. // and a device that is not boot config'd on it,
  1360. // then we may get out of synch, depending on
  1361. // what combinations the PnP manager throws at us.
  1362. // Fixing either of these would involve tagging all
  1363. // vectors with a list of the PDOs that are connected
  1364. // which would be a lot of work. So unless these
  1365. // situations actually exist in the future, I'm not
  1366. // bothering to code for them now. 11/14/2000
  1367. //
  1368. FOR_ALL_RANGES(Arbiter->Allocation, &iterator, currentRange) {
  1369. DEBUG_PRINT(4, ("Looking at range: %x-%x %p\n",
  1370. (ULONG)(currentRange->Start & 0xffffffff),
  1371. (ULONG)(currentRange->End & 0xffffffff),
  1372. currentRange->Owner));
  1373. FOR_ALL_IN_LIST(ARBITER_LIST_ENTRY, ArbitrationList, currentListEntry) {
  1374. DEBUG_PRINT(2, ("Unreferencing allocations for device %p\n",
  1375. currentListEntry->PhysicalDeviceObject));
  1376. if (currentRange->Owner == currentListEntry->PhysicalDeviceObject) {
  1377. //
  1378. // Dereference the vector until there are no more
  1379. // references.
  1380. //
  1381. for (vector = (ULONG)(currentRange->Start & 0xffffffff);
  1382. vector <= (ULONG)(currentRange->End & 0xffffffff);
  1383. vector++) {
  1384. status = GetVectorProperties(vector, &flags);
  1385. if (NT_SUCCESS(status)) {
  1386. DEBUG_PRINT(2, ("Dereferencing %x\n", vector));
  1387. DereferenceVector(vector);
  1388. }
  1389. }
  1390. if (!(currentRange->Attributes & ARBITER_RANGE_BOOT_ALLOCATED)) {
  1391. //
  1392. // Now find out if we have to dereference a link node too.
  1393. //
  1394. status = AcpiArbCrackPRT(currentListEntry->PhysicalDeviceObject,
  1395. &linkNode,
  1396. &vector);
  1397. if (NT_SUCCESS(status)) {
  1398. if (linkNode) {
  1399. //
  1400. // This device is connected to a link node. So temporarily
  1401. // dereference this node.
  1402. //
  1403. ASSERT(LinkNodeInUse(Arbiter,
  1404. linkNode,
  1405. NULL,
  1406. NULL));
  1407. AcpiArbDereferenceLinkNode(Arbiter,
  1408. linkNode);
  1409. }
  1410. }
  1411. }
  1412. }
  1413. }
  1414. }
  1415. return STATUS_SUCCESS;
  1416. }
  1417. NTSTATUS
  1418. AcpiArbBootAllocation(
  1419. IN PARBITER_INSTANCE Arbiter,
  1420. IN OUT PLIST_ENTRY ArbitrationList
  1421. )
  1422. {
  1423. NTSTATUS status;
  1424. PAGED_CODE();
  1425. //
  1426. // Start a new arbiter transaction and clear any
  1427. // temporary vector counts.
  1428. //
  1429. // N.B. This arbiter doesn't keep track of link
  1430. // node data for boot configs. This means that we don't have
  1431. // to worry about link node counts in this funtion.
  1432. //
  1433. ClearTempVectorCounts();
  1434. status = ArbBootAllocation(Arbiter, ArbitrationList);
  1435. MakeTempVectorCountsPermanent();
  1436. return status;
  1437. }
  1438. NTSTATUS
  1439. AcpiArbTestAllocation(
  1440. IN PARBITER_INSTANCE Arbiter,
  1441. IN OUT PLIST_ENTRY ArbitrationList
  1442. )
  1443. {
  1444. NTSTATUS status;
  1445. PAGED_CODE();
  1446. //
  1447. // ArbTestAllocation is the beginning of a
  1448. // new arbitration transaction. So clear
  1449. // out all temporary edge-level status and
  1450. // link node ref counts.
  1451. //
  1452. ClearTempVectorCounts();
  1453. status = ClearTempLinkNodeCounts(Arbiter);
  1454. ASSERT(NT_SUCCESS(status));
  1455. status = UnreferenceArbitrationList(Arbiter, ArbitrationList);
  1456. ASSERT(NT_SUCCESS(status));
  1457. return ArbTestAllocation(Arbiter, ArbitrationList);
  1458. }
  1459. NTSTATUS
  1460. AcpiArbRetestAllocation(
  1461. IN PARBITER_INSTANCE Arbiter,
  1462. IN OUT PLIST_ENTRY ArbitrationList
  1463. )
  1464. {
  1465. NTSTATUS status;
  1466. PAGED_CODE();
  1467. //
  1468. // ArbRetestAllocation (also) is the beginning
  1469. // of a new arbitration transaction. So clear
  1470. // out all temporary edge-level status and
  1471. // link node ref counts.
  1472. //
  1473. ClearTempVectorCounts();
  1474. status = ClearTempLinkNodeCounts(Arbiter);
  1475. ASSERT(NT_SUCCESS(status));
  1476. status = UnreferenceArbitrationList(Arbiter, ArbitrationList);
  1477. ASSERT(NT_SUCCESS(status));
  1478. return ArbRetestAllocation(Arbiter, ArbitrationList);
  1479. }
  1480. NTSTATUS
  1481. AcpiArbRollbackAllocation(
  1482. PARBITER_INSTANCE Arbiter
  1483. )
  1484. {
  1485. PAGED_CODE();
  1486. return ArbRollbackAllocation(Arbiter);
  1487. }
  1488. NTSTATUS
  1489. AcpiArbCommitAllocation(
  1490. PARBITER_INSTANCE Arbiter
  1491. )
  1492. /*++
  1493. Routine Description:
  1494. This provides the implementation of the CommitAllocation
  1495. action. It frees the old allocation and replaces it with
  1496. the new allocation. After that, it dereferences all the
  1497. link nodes in the old allocation and references the ones
  1498. in the new allocation. This potentially results in the
  1499. IRQ router being reprogrammed to match the new set of
  1500. allocations.
  1501. Parameters:
  1502. Arbiter - The arbiter instance data for the arbiter being called.
  1503. Return Value:
  1504. Status code that indicates whether or not the function was successful.
  1505. --*/
  1506. {
  1507. PINT_ROUTE_INTERFACE_STANDARD pciInterface = NULL;
  1508. RTL_RANGE_LIST_ITERATOR iterator;
  1509. PRTL_RANGE_LIST temp;
  1510. PRTL_RANGE current;
  1511. NTSTATUS status;
  1512. PNSOBJ linkNode;
  1513. ULONG sourceIndex;
  1514. ULONG pciBus;
  1515. PCI_SLOT_NUMBER pciSlot;
  1516. UCHAR interruptLine;
  1517. ULONG_PTR dummy;
  1518. ROUTING_TOKEN token;
  1519. PAGED_CODE();
  1520. if (PciInterfacesInstantiated) {
  1521. pciInterface = ((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting;
  1522. ASSERT(pciInterface);
  1523. FOR_ALL_RANGES(Arbiter->PossibleAllocation, &iterator, current) {
  1524. if (current->Owner) {
  1525. //
  1526. // Make sure that the InterruptLine register
  1527. // matches the assignment. (This is so that
  1528. // broken drivers that rely on the contents
  1529. // of the InterruptLine register instead of
  1530. // the resources handed back in a StartDevice
  1531. // still work.
  1532. //
  1533. pciBus = (ULONG)-1;
  1534. pciSlot.u.AsULONG = (ULONG)-1;
  1535. status = pciInterface->GetInterruptRouting(current->Owner,
  1536. &pciBus,
  1537. &pciSlot.u.AsULONG,
  1538. &interruptLine,
  1539. (PUCHAR)&dummy,
  1540. (PUCHAR)&dummy,
  1541. (PUCHAR)&dummy,
  1542. (PDEVICE_OBJECT*)&dummy,
  1543. &token,
  1544. (PUCHAR)&dummy);
  1545. if (NT_SUCCESS(status)) {
  1546. if (interruptLine != (UCHAR)current->Start) {
  1547. //
  1548. // We need to update the hardware.
  1549. //
  1550. ASSERT(current->Start < MAXUCHAR);
  1551. pciInterface->UpdateInterruptLine(current->Owner,
  1552. (UCHAR)current->Start
  1553. );
  1554. }
  1555. }
  1556. }
  1557. }
  1558. }
  1559. //
  1560. // Free up the current allocation
  1561. //
  1562. RtlFreeRangeList(Arbiter->Allocation);
  1563. //
  1564. // Swap the allocated and duplicate lists
  1565. //
  1566. temp = Arbiter->Allocation;
  1567. Arbiter->Allocation = Arbiter->PossibleAllocation;
  1568. Arbiter->PossibleAllocation = temp;
  1569. //
  1570. // Since we have committed the new allocation, we
  1571. // need to make the edge-level state and the new
  1572. // link node counts permanent.
  1573. //
  1574. MakeTempVectorCountsPermanent();
  1575. status = MakeTempLinkNodeCountsPermanent(Arbiter);
  1576. return status;
  1577. }
  1578. NTSTATUS
  1579. AcpiArbQueryConflict(
  1580. IN PARBITER_INSTANCE Arbiter,
  1581. IN PDEVICE_OBJECT PhysicalDeviceObject,
  1582. IN PIO_RESOURCE_DESCRIPTOR ConflictingResource,
  1583. OUT PULONG ConflictCount,
  1584. OUT PARBITER_CONFLICT_INFO *Conflicts
  1585. )
  1586. {
  1587. PINT_ROUTE_INTERFACE_STANDARD pciInterface = NULL;
  1588. NTSTATUS status;
  1589. ROUTING_TOKEN routingToken;
  1590. ULONG_PTR dummy;
  1591. PAGED_CODE();
  1592. if (PciInterfacesInstantiated) {
  1593. pciInterface = ((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting;
  1594. ASSERT(pciInterface);
  1595. status = pciInterface->GetInterruptRouting(PhysicalDeviceObject,
  1596. (PULONG)&dummy,
  1597. (PULONG)&dummy,
  1598. &(UCHAR)dummy,
  1599. &(UCHAR)dummy,
  1600. &(UCHAR)dummy,
  1601. &(UCHAR)dummy,
  1602. (PDEVICE_OBJECT*)&dummy,
  1603. &routingToken,
  1604. &(UCHAR)dummy);
  1605. if (NT_SUCCESS(status)) {
  1606. //
  1607. // This is a PCI device. It's interrupt should not ever
  1608. // show a conflict.
  1609. //
  1610. *ConflictCount = 0;
  1611. return STATUS_SUCCESS;
  1612. }
  1613. }
  1614. //
  1615. // This isn't a PCI device. Call the base arbiter code.
  1616. //
  1617. return ArbQueryConflict(Arbiter,
  1618. PhysicalDeviceObject,
  1619. ConflictingResource,
  1620. ConflictCount,
  1621. Conflicts);
  1622. }
  1623. NTSTATUS
  1624. FindVectorInAlternatives(
  1625. IN PARBITER_INSTANCE Arbiter,
  1626. IN PARBITER_ALLOCATION_STATE State,
  1627. IN ULONGLONG Vector,
  1628. OUT ULONG *Alternative
  1629. )
  1630. {
  1631. ULONG alt;
  1632. for (alt = 0; alt < State->AlternativeCount; alt++) {
  1633. if ((State->Alternatives[alt].Minimum <= Vector) &&
  1634. (State->Alternatives[alt].Maximum >= Vector)) {
  1635. *Alternative = alt;
  1636. return STATUS_SUCCESS;
  1637. }
  1638. }
  1639. return STATUS_NOT_FOUND;
  1640. }
  1641. NTSTATUS
  1642. FindBootConfig(
  1643. IN PARBITER_INSTANCE Arbiter,
  1644. IN PARBITER_ALLOCATION_STATE State,
  1645. IN ULONGLONG *Vector
  1646. )
  1647. {
  1648. RTL_RANGE_LIST_ITERATOR iterator;
  1649. PRTL_RANGE currentRange;
  1650. FOR_ALL_RANGES(Arbiter->Allocation, &iterator, currentRange) {
  1651. if (currentRange->Attributes & ARBITER_RANGE_BOOT_ALLOCATED) {
  1652. //
  1653. // We're only interested in boot configs.
  1654. //
  1655. if (State->Entry->PhysicalDeviceObject == currentRange->Owner) {
  1656. //
  1657. // This boot config is the one we are looking for.
  1658. //
  1659. ASSERT(currentRange->Start == currentRange->End);
  1660. *Vector = currentRange->Start;
  1661. return STATUS_SUCCESS;
  1662. }
  1663. }
  1664. }
  1665. return STATUS_NOT_FOUND;
  1666. }
  1667. BOOLEAN
  1668. AcpiArbGetNextAllocationRange(
  1669. IN PARBITER_INSTANCE Arbiter,
  1670. IN OUT PARBITER_ALLOCATION_STATE State
  1671. )
  1672. {
  1673. BOOLEAN nextRange = FALSE;
  1674. PINT_ROUTE_INTERFACE_STANDARD pciInterface;
  1675. NTSTATUS status;
  1676. ROUTING_TOKEN routingToken;
  1677. ULONG_PTR dummy;
  1678. BOOLEAN legacyFreeMachine;
  1679. ULONGLONG vector;
  1680. ULONG alternative;
  1681. PAGED_CODE();
  1682. if (State->Entry->PhysicalDeviceObject->DriverObject == AcpiDriverObject) {
  1683. //
  1684. // This is one of our PDOs.
  1685. //
  1686. ASSERT(((PDEVICE_EXTENSION)State->Entry->PhysicalDeviceObject->DeviceExtension)->Flags & DEV_TYPE_PDO);
  1687. ASSERT(((PDEVICE_EXTENSION)State->Entry->PhysicalDeviceObject->DeviceExtension)->Signature == ACPI_SIGNATURE);
  1688. if (((PDEVICE_EXTENSION)State->Entry->PhysicalDeviceObject->DeviceExtension)->Flags & DEV_CAP_PCI) {
  1689. //
  1690. // It's a PCI PDO, which means a root PCI bus,
  1691. // which means that we should just handle this
  1692. // as an ISA device.
  1693. //
  1694. return ArbGetNextAllocationRange(Arbiter, State);
  1695. }
  1696. }
  1697. status = STATUS_NOT_FOUND;
  1698. if (PciInterfacesInstantiated) {
  1699. pciInterface = ((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting;
  1700. ASSERT(pciInterface);
  1701. status = pciInterface->GetInterruptRouting(State->Entry->PhysicalDeviceObject,
  1702. (PULONG)&dummy,
  1703. (PULONG)&dummy,
  1704. &(UCHAR)dummy,
  1705. &(UCHAR)dummy,
  1706. &(UCHAR)dummy,
  1707. &(UCHAR)dummy,
  1708. (PDEVICE_OBJECT*)&dummy,
  1709. &routingToken,
  1710. &(UCHAR)dummy);
  1711. }
  1712. if (status != STATUS_SUCCESS) {
  1713. //
  1714. // This is not a PCI device. Use the base function.
  1715. //
  1716. return ArbGetNextAllocationRange(Arbiter, State);
  1717. }
  1718. #if defined(_X86_)
  1719. legacyFreeMachine = (AcpiInformation->FixedACPIDescTable->Header.Revision > 1) &&
  1720. !(AcpiInformation->FixedACPIDescTable->boot_arch & LEGACY_DEVICES);
  1721. #else
  1722. legacyFreeMachine = TRUE;
  1723. #endif
  1724. //
  1725. // A PCI device.
  1726. //
  1727. if (!State->CurrentAlternative) {
  1728. //
  1729. // This is the first time we've called this function
  1730. // with this alternative list. Set up the state machine.
  1731. //
  1732. State->WorkSpace = AcpiIrqNextRangeInit;
  1733. }
  1734. while (TRUE) {
  1735. ASSERT((State->WorkSpace > AcpiIrqNextRangeMinState) &&
  1736. (State->WorkSpace < AcpiIrqNextRangeMaxState));
  1737. DEBUG_PRINT(4, ("GetNextRange, State: %x\n", State->WorkSpace));
  1738. switch (State->WorkSpace) {
  1739. case AcpiIrqNextRangeInit:
  1740. //
  1741. // Top of the state machine. See if the registry
  1742. // contained policy.
  1743. //
  1744. switch (AcpiIrqDistributionDisposition) {
  1745. case AcpiIrqDistributionDispositionSpreadOut:
  1746. State->WorkSpace = AcpiIrqNextRangeAlternativeZero;
  1747. break;
  1748. case AcpiIrqDistributionDispositionStackUp:
  1749. State->WorkSpace = AcpiIrqNextRangeInitLegacy;
  1750. break;
  1751. case AcpiIrqDistributionDispositionDontCare:
  1752. default:
  1753. State->WorkSpace = AcpiIrqNextRangeInitPolicyNeutral;
  1754. break;
  1755. }
  1756. break;
  1757. case AcpiIrqNextRangeInitPolicyNeutral:
  1758. //
  1759. // Look at the interrupt controller model.
  1760. //
  1761. if (InterruptModel == 0) {
  1762. State->WorkSpace = AcpiIrqNextRangeInitPic;
  1763. } else {
  1764. State->WorkSpace = AcpiIrqNextRangeUseBootConfig;
  1765. }
  1766. break;
  1767. case AcpiIrqNextRangeInitPic:
  1768. //
  1769. // There is a PIC interrupt controller. So we are somewhat
  1770. // IRQ constrained. If this is a legacy-free machine, or if there
  1771. // is no cardbus controller, we want to spread interrupts.
  1772. //
  1773. if (legacyFreeMachine || !AcpiArbCardbusPresent) {
  1774. State->WorkSpace = AcpiIrqNextRangeUseBootConfig;
  1775. } else {
  1776. State->WorkSpace = AcpiIrqNextRangeInitLegacy;
  1777. }
  1778. break;
  1779. case AcpiIrqNextRangeInitLegacy:
  1780. //
  1781. // See if all the devices were boot configged on the same
  1782. // vector, or if there was a registry override specifying
  1783. // the vector that we should favor.
  1784. //
  1785. if (AcpiIrqDefaultBootConfig) {
  1786. State->WorkSpace = AcpiIrqNextRangeBootRegAlternative;
  1787. } else {
  1788. State->WorkSpace = AcpiIrqNextRangeSciAlternative;
  1789. }
  1790. break;
  1791. case AcpiIrqNextRangeBootRegAlternative:
  1792. //
  1793. // If we re-enter this state machine after this state,
  1794. // then it means that this alternative wasn't available.
  1795. // So set the next state to AcpiIrqNextRangeAlternativeZero,
  1796. // assuming that we failed.
  1797. //
  1798. State->WorkSpace = AcpiIrqNextRangeAlternativeZero;
  1799. //
  1800. // See if the machine-wide boot config or the registry
  1801. // override is within the alternatives.
  1802. //
  1803. status = FindVectorInAlternatives(Arbiter,
  1804. State,
  1805. (ULONGLONG)AcpiIrqDefaultBootConfig,
  1806. &alternative);
  1807. if (NT_SUCCESS(status)) {
  1808. State->CurrentAlternative = &State->Alternatives[alternative];
  1809. State->CurrentMinimum = (ULONGLONG)AcpiIrqDefaultBootConfig;
  1810. State->CurrentMaximum = (ULONGLONG)AcpiIrqDefaultBootConfig;
  1811. goto GetNextAllocationSuccess;
  1812. }
  1813. break;
  1814. case AcpiIrqNextRangeSciAlternative:
  1815. //
  1816. // If we re-enter this state machine after this state,
  1817. // then it means that this alternative wasn't available.
  1818. // So set the next state to AcpiIrqNextRangeUseBootConfig,
  1819. // assuming that we failed.
  1820. //
  1821. State->WorkSpace = AcpiIrqNextRangeUseBootConfig;
  1822. //
  1823. // See if the SCI vector is within the alternatives.
  1824. //
  1825. status = FindVectorInAlternatives(Arbiter,
  1826. State,
  1827. (ULONGLONG)AcpiSciVector,
  1828. &alternative);
  1829. if (NT_SUCCESS(status)) {
  1830. State->CurrentAlternative = &State->Alternatives[alternative];
  1831. State->CurrentMinimum = (ULONGLONG)AcpiSciVector;
  1832. State->CurrentMaximum = (ULONGLONG)AcpiSciVector;
  1833. goto GetNextAllocationSuccess;
  1834. }
  1835. break;
  1836. case AcpiIrqNextRangeUseBootConfig:
  1837. //
  1838. // If we re-enter this state machine after this state,
  1839. // then it means that this alternative wasn't available.
  1840. // So set the next state to AcpiIrqNextRangeAlternativeZero,
  1841. // assuming that we failed.
  1842. //
  1843. State->WorkSpace = AcpiIrqNextRangeAlternativeZero;
  1844. //
  1845. // See if there is a boot config for this device
  1846. // within the alternatives.
  1847. //
  1848. status = FindBootConfig(Arbiter,
  1849. State,
  1850. &vector);
  1851. if (NT_SUCCESS(status)) {
  1852. status = FindVectorInAlternatives(Arbiter,
  1853. State,
  1854. vector,
  1855. &alternative);
  1856. if (NT_SUCCESS(status)) {
  1857. State->CurrentAlternative = &State->Alternatives[alternative];
  1858. State->CurrentMinimum = vector;
  1859. State->CurrentMaximum = vector;
  1860. goto GetNextAllocationSuccess;
  1861. }
  1862. }
  1863. break;
  1864. case AcpiIrqNextRangeAlternativeZero:
  1865. //
  1866. // If we re-enter this state machine after this state,
  1867. // then it means that this alternative wasn't available.
  1868. // So set the next state to AcpiIrqNextRangeAlternativeN,
  1869. // assuming that we failed.
  1870. //
  1871. State->WorkSpace = AcpiIrqNextRangeAlternativeN;
  1872. //
  1873. // Try alternative 0.
  1874. //
  1875. State->CurrentAlternative = &State->Alternatives[0];
  1876. State->CurrentMinimum = State->CurrentAlternative->Minimum;
  1877. State->CurrentMaximum = State->CurrentAlternative->Maximum;
  1878. goto GetNextAllocationSuccess;
  1879. break;
  1880. case AcpiIrqNextRangeAlternativeN:
  1881. if (++State->CurrentAlternative < &State->Alternatives[State->AlternativeCount]) {
  1882. //
  1883. // There are multiple ranges. Cycle through them.
  1884. //
  1885. DEBUG_PRINT(3, ("No next allocation range, exhausted all %08X alternatives", State->AlternativeCount));
  1886. State->CurrentMinimum = State->CurrentAlternative->Minimum;
  1887. State->CurrentMaximum = State->CurrentAlternative->Maximum;
  1888. goto GetNextAllocationSuccess;
  1889. } else {
  1890. //
  1891. // We're done. There is no solution among these alternatives.
  1892. //
  1893. return FALSE;
  1894. }
  1895. }
  1896. }
  1897. GetNextAllocationSuccess:
  1898. DEBUG_PRINT(3, ("Next allocation range 0x%I64x-0x%I64x\n", State->CurrentMinimum, State->CurrentMaximum));
  1899. AcpiArbPciAlternativeRotation++;
  1900. return TRUE;
  1901. }
  1902. VOID
  1903. ReferenceVector(
  1904. IN ULONG Vector,
  1905. IN UCHAR Flags
  1906. )
  1907. /*++
  1908. Routine Description:
  1909. This routine adds one to either the permanent or the
  1910. temporary reference count.
  1911. Parameters:
  1912. Vector - the IRQ
  1913. Flags - mode and polarity
  1914. Return Value:
  1915. none
  1916. --*/
  1917. {
  1918. PVECTOR_BLOCK block;
  1919. PAGED_CODE();
  1920. ASSERT((Flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
  1921. block = HashVector(Vector);
  1922. DEBUG_PRINT(5, ("Referencing vector %x : %d %d\n", Vector,
  1923. block ? block->Entry.Count : 0,
  1924. block ? block->Entry.TempCount : 0));
  1925. if (block == NULL) {
  1926. AddVectorToTable(Vector,
  1927. 0,
  1928. 1,
  1929. Flags);
  1930. return;
  1931. }
  1932. if ((block->Entry.TempCount + block->Entry.Count) == 0) {
  1933. //
  1934. // This vector has been temporarily set to an
  1935. // aggregate count of zero. This means that the arbiter
  1936. // is re-allocating it. Record the new flags.
  1937. //
  1938. block->Entry.TempFlags = Flags;
  1939. }
  1940. block->Entry.TempCount++;
  1941. ASSERT(Flags == block->Entry.TempFlags);
  1942. ASSERT(block->Entry.Count <= 255);
  1943. }
  1944. VOID
  1945. DereferenceVector(
  1946. IN ULONG Vector
  1947. )
  1948. {
  1949. PVECTOR_BLOCK block;
  1950. PAGED_CODE();
  1951. block = HashVector(Vector);
  1952. ASSERT(block);
  1953. DEBUG_PRINT(5, ("Dereferencing vector %x : %d %d\n", Vector,
  1954. block->Entry.Count,
  1955. block->Entry.TempCount));
  1956. block->Entry.TempCount--;
  1957. ASSERT((block->Entry.TempCount * -1) <= block->Entry.Count);
  1958. }
  1959. PVECTOR_BLOCK
  1960. HashVector(
  1961. IN ULONG Vector
  1962. )
  1963. /*++
  1964. Routine Description:
  1965. This function takes a "Global System Interrupt Vector"
  1966. and returns a pointer to its entry in the hash table.
  1967. Arguments:
  1968. Vector - an IRQ
  1969. Return Value:
  1970. pointer to the entry in the hash table, or NULL if not found
  1971. --*/
  1972. {
  1973. PVECTOR_BLOCK block;
  1974. ULONG row, column;
  1975. PAGED_CODE();
  1976. row = Vector % VECTOR_HASH_TABLE_LENGTH;
  1977. block = HASH_ENTRY(row, 0);
  1978. while (TRUE) {
  1979. //
  1980. // Search across the hash table looking for our Vector
  1981. //
  1982. for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
  1983. //
  1984. // Check to see if we should follow a chain
  1985. //
  1986. if (block->Chain.Token == TOKEN_VALUE) {
  1987. break;
  1988. }
  1989. if (block->Entry.Vector == Vector) {
  1990. return block;
  1991. }
  1992. if ((block->Entry.Vector == EMPTY_BLOCK_VALUE) ||
  1993. (column == VECTOR_HASH_TABLE_WIDTH - 1)) {
  1994. //
  1995. // Didn't find this vector in the table.
  1996. //
  1997. return NULL;
  1998. }
  1999. block += 1;
  2000. }
  2001. ASSERT(block->Chain.Token == TOKEN_VALUE);
  2002. block = block->Chain.Next;
  2003. }
  2004. return NULL;
  2005. }
  2006. NTSTATUS
  2007. GetVectorProperties(
  2008. IN ULONG Vector,
  2009. OUT UCHAR *Flags
  2010. )
  2011. /*++
  2012. Routine Description:
  2013. This function takes a "Global System Interrupt Vector"
  2014. and returns the associated flags.
  2015. N.B. This function returns flags based on the
  2016. *temporary* reference count. I.e. if the vector
  2017. has been temporarily dereferenced, then the
  2018. funtion will indicate that the vector is available
  2019. for allocation by returning STATUS_NOT_FOUND.
  2020. Arguments:
  2021. Vector - an IRQ
  2022. Flags - to be filled in with the flags
  2023. Return Value:
  2024. status
  2025. --*/
  2026. {
  2027. PVECTOR_BLOCK block;
  2028. PAGED_CODE();
  2029. block = HashVector(Vector);
  2030. if (!block) {
  2031. return STATUS_NOT_FOUND;
  2032. }
  2033. if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
  2034. return STATUS_NOT_FOUND;
  2035. }
  2036. ASSERT(block->Entry.Vector == Vector);
  2037. if (block->Entry.Count + block->Entry.TempCount == 0) {
  2038. //
  2039. // This vector has an aggregate reference count of
  2040. // zero. This means that it is effectively
  2041. // unallocated.
  2042. //
  2043. return STATUS_NOT_FOUND;
  2044. }
  2045. *Flags = block->Entry.TempFlags;
  2046. return STATUS_SUCCESS;
  2047. }
  2048. NTSTATUS
  2049. AddVectorToTable(
  2050. IN ULONG Vector,
  2051. IN UCHAR ReferenceCount,
  2052. IN UCHAR TempRefCount,
  2053. IN UCHAR Flags
  2054. )
  2055. {
  2056. PVECTOR_BLOCK block, newRow;
  2057. ULONG row, column;
  2058. PAGED_CODE();
  2059. ASSERT((Flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
  2060. row = Vector % VECTOR_HASH_TABLE_LENGTH;
  2061. block = HASH_ENTRY(row, 0);
  2062. while (TRUE) {
  2063. //
  2064. // Search across the hash table looking for our Vector
  2065. //
  2066. for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
  2067. //
  2068. // Check to see if we should follow a chain
  2069. //
  2070. if (block->Chain.Token == TOKEN_VALUE) {
  2071. break;
  2072. }
  2073. if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
  2074. block->Entry.Vector = Vector;
  2075. block->Entry.Count = ReferenceCount;
  2076. block->Entry.TempCount = TempRefCount;
  2077. block->Entry.Flags = Flags;
  2078. block->Entry.TempFlags = Flags;
  2079. return STATUS_SUCCESS;
  2080. }
  2081. if (column == VECTOR_HASH_TABLE_WIDTH - 1) {
  2082. //
  2083. // We have just looked at the last entry in
  2084. // the row and it wasn't empty. Create
  2085. // an extension to this row.
  2086. //
  2087. newRow = ExAllocatePoolWithTag(PagedPool,
  2088. sizeof(VECTOR_BLOCK)
  2089. * VECTOR_HASH_TABLE_WIDTH,
  2090. ACPI_ARBITER_POOLTAG
  2091. );
  2092. if (!newRow) {
  2093. return STATUS_INSUFFICIENT_RESOURCES;
  2094. }
  2095. RtlFillMemory(newRow,
  2096. sizeof(VECTOR_BLOCK) * VECTOR_HASH_TABLE_WIDTH,
  2097. (UCHAR)(EMPTY_BLOCK_VALUE & 0xff));
  2098. //
  2099. // Move last entry into new row.
  2100. //
  2101. RtlMoveMemory(newRow, block, sizeof(VECTOR_BLOCK));
  2102. //
  2103. // Chain the old row to the new row.
  2104. //
  2105. block->Chain.Token = TOKEN_VALUE;
  2106. block->Chain.Next = newRow;
  2107. break;
  2108. }
  2109. block += 1;
  2110. }
  2111. block = block->Chain.Next;
  2112. }
  2113. return STATUS_INSUFFICIENT_RESOURCES;
  2114. }
  2115. VOID
  2116. ClearTempVectorCounts(
  2117. VOID
  2118. )
  2119. {
  2120. PVECTOR_BLOCK block;
  2121. ULONG row, column;
  2122. PAGED_CODE();
  2123. for (row = 0; row < VECTOR_HASH_TABLE_LENGTH; row++) {
  2124. block = HASH_ENTRY(row, 0);
  2125. //
  2126. // Search across the hash table looking for our Vector
  2127. //
  2128. ClearTempCountsStartRow:
  2129. for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
  2130. //
  2131. // Check to see if we should follow a chain
  2132. //
  2133. if (block->Chain.Token == TOKEN_VALUE) {
  2134. block = block->Chain.Next;
  2135. goto ClearTempCountsStartRow;
  2136. }
  2137. if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
  2138. break;
  2139. }
  2140. //
  2141. // This must be a valid entry.
  2142. //
  2143. block->Entry.TempCount = 0;
  2144. block->Entry.TempFlags = block->Entry.Flags;
  2145. block += 1;
  2146. }
  2147. }
  2148. }
  2149. VOID
  2150. MakeTempVectorCountsPermanent(
  2151. VOID
  2152. )
  2153. {
  2154. PVECTOR_BLOCK block;
  2155. ULONG row, column;
  2156. PAGED_CODE();
  2157. for (row = 0; row < VECTOR_HASH_TABLE_LENGTH; row++) {
  2158. block = HASH_ENTRY(row, 0);
  2159. //
  2160. // Search across the hash table looking for our Vector
  2161. //
  2162. MakeTempVectorCountsPermanentStartRow:
  2163. for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
  2164. //
  2165. // Check to see if we should follow a chain
  2166. //
  2167. if (block->Chain.Token == TOKEN_VALUE) {
  2168. block = block->Chain.Next;
  2169. goto MakeTempVectorCountsPermanentStartRow;
  2170. }
  2171. if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
  2172. break;
  2173. }
  2174. //
  2175. // This must be a valid entry.
  2176. //
  2177. if ((block->Entry.Count + block->Entry.TempCount != 0) &&
  2178. ((block->Entry.Count == 0) ||
  2179. (block->Entry.TempFlags != block->Entry.Flags))) {
  2180. //
  2181. // This vector has just been allocated or it has
  2182. // been re-allocated. Tell the HAL which flags
  2183. // to use.
  2184. //
  2185. HalSetVectorState(block->Entry.Vector,
  2186. block->Entry.TempFlags);
  2187. }
  2188. //
  2189. // Record new flags and aggregate count.
  2190. //
  2191. block->Entry.Flags = block->Entry.TempFlags;
  2192. block->Entry.Count += block->Entry.TempCount;
  2193. block += 1;
  2194. }
  2195. }
  2196. }
  2197. #ifdef DBG
  2198. VOID
  2199. DumpVectorTable(
  2200. VOID
  2201. )
  2202. {
  2203. PVECTOR_BLOCK block;
  2204. ULONG row, column;
  2205. PAGED_CODE();
  2206. DEBUG_PRINT(1, ("\nIRQARB: Dumping vector table\n"));
  2207. for (row = 0; row < VECTOR_HASH_TABLE_LENGTH; row++) {
  2208. block = HASH_ENTRY(row, 0);
  2209. //
  2210. // Search across the hash table looking for our Vector
  2211. //
  2212. DumpVectorTableStartRow:
  2213. for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
  2214. //
  2215. // Check to see if we should follow a chain
  2216. //
  2217. if (block->Chain.Token == TOKEN_VALUE) {
  2218. block = block->Chain.Next;
  2219. goto DumpVectorTableStartRow;
  2220. }
  2221. if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
  2222. break;
  2223. }
  2224. DEBUG_PRINT(1, ("Vector: %x\tP: %d T: %d\t%s %s\n",
  2225. block->Entry.Vector,
  2226. block->Entry.Count,
  2227. (LONG)block->Entry.TempCount,
  2228. IS_LEVEL_TRIGGERED(block->Entry.Flags) ? "level" : "edge",
  2229. IS_ACTIVE_LOW(block->Entry.Flags) ? "low" : "high"));
  2230. block += 1;
  2231. }
  2232. }
  2233. }
  2234. #endif
  2235. //
  2236. // This section of the file contains functions used for
  2237. // reading and manipulating the AML code.
  2238. //
  2239. NTSTATUS
  2240. AcpiArbGetLinkNodeOptions(
  2241. IN PNSOBJ LinkNode,
  2242. IN OUT PCM_RESOURCE_LIST *LinkNodeIrqs,
  2243. IN OUT UCHAR *Flags
  2244. )
  2245. /*++
  2246. Routine Description:
  2247. This routine looks in the AML namespace for the named
  2248. link node and returns the range of IRQs that it can
  2249. trigger.
  2250. Arguments:
  2251. LinkNodeName - The name of the IRQ router (link node)
  2252. LInkNodeIrqs - The list of possible settings for the link node
  2253. Flags - flags associated with this link node
  2254. Return Value:
  2255. NTSTATUS
  2256. --*/
  2257. {
  2258. PIO_RESOURCE_REQUIREMENTS_LIST ioList = NULL;
  2259. PCM_RESOURCE_LIST cmList = NULL;
  2260. PUCHAR prsBuff = NULL;
  2261. NTSTATUS status;
  2262. PULONG polarity;
  2263. PAGED_CODE();
  2264. ASSERT(LinkNode);
  2265. //
  2266. // Read the _PRS
  2267. //
  2268. ACPIGetNSBufferSync(
  2269. LinkNode,
  2270. PACKED_PRS,
  2271. &prsBuff,
  2272. NULL);
  2273. if (!prsBuff) {
  2274. return STATUS_NOT_FOUND;
  2275. }
  2276. status = PnpBiosResourcesToNtResources(prsBuff, 0, &ioList);
  2277. ExFreePool(prsBuff);
  2278. if (!NT_SUCCESS(status)) {
  2279. return status;
  2280. }
  2281. //
  2282. // Huge HACK! Get the polarity for the Flags.
  2283. //
  2284. // An IO_RES_LIST has no real way of representing the polarity
  2285. // of an interrupt. So, in PnpiBiosExtendedIrqToIoDescriptor I
  2286. // stuck the information in the DWORD past 'MaximumVector.'
  2287. //
  2288. *Flags = 0;
  2289. ASSERT(ioList->AlternativeLists == 1);
  2290. polarity = (PULONG)(&ioList->List[0].Descriptors[0].u.Interrupt.MaximumVector) + 1;
  2291. *Flags |= (UCHAR)*polarity;
  2292. //
  2293. // Get the mode for the flags.
  2294. //
  2295. *Flags |= (ioList->List[0].Descriptors[0].Flags == CM_RESOURCE_INTERRUPT_LATCHED) ?
  2296. VECTOR_EDGE : VECTOR_LEVEL;
  2297. //
  2298. // Turn the list into a CM_RESOURCE_LIST
  2299. //
  2300. status = PnpIoResourceListToCmResourceList(
  2301. ioList,
  2302. &cmList
  2303. );
  2304. ExFreePool(ioList);
  2305. if (!NT_SUCCESS(status)) {
  2306. return status;
  2307. }
  2308. *LinkNodeIrqs = cmList;
  2309. return STATUS_SUCCESS;
  2310. }
  2311. typedef enum {
  2312. StateInitial,
  2313. StateGotPrs,
  2314. StateRanSrs
  2315. } SET_LINK_WORKER_STATE;
  2316. typedef struct {
  2317. PNSOBJ LinkNode;
  2318. PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq;
  2319. PUCHAR PrsBuff;
  2320. PUCHAR SrsBuff;
  2321. SET_LINK_WORKER_STATE State;
  2322. LONG RunCompletionHandler;
  2323. OBJDATA ObjData;
  2324. PFNACB CompletionHandler;
  2325. PVOID CompletionContext;
  2326. } SET_LINK_NODE_STATE, *PSET_LINK_NODE_STATE;
  2327. NTSTATUS
  2328. AcpiArbSetLinkNodeIrq(
  2329. IN PNSOBJ LinkNode,
  2330. IN PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq
  2331. )
  2332. /*++
  2333. Routine Description:
  2334. This routine sets the named link node to trigger
  2335. a particular IRQ.
  2336. N.B. This routine could simply build the right
  2337. buffer and call the _SRS method, but there
  2338. isn't enough information in a
  2339. CM_PARTIAL_RESOURCE_DESCRIPTOR to know whether
  2340. an interrupt will be delivered active-high
  2341. or active-low. So the algorithm here runs
  2342. the _PRS method and copies the buffer returned
  2343. by _PRS into the buffer sent to _SRS. This
  2344. way all the flags are preserved.
  2345. Arguments:
  2346. LinkNodeName - The name of the IRQ router (link node)
  2347. LinkNodeIrq - The IRQ that the link node will be programmed
  2348. to trigger. If it is NULL, then the link node
  2349. will be disabled.
  2350. Return Value:
  2351. NTSTATUS
  2352. --*/
  2353. {
  2354. AMLISUPP_CONTEXT_PASSIVE getDataContext;
  2355. NTSTATUS status;
  2356. PAGED_CODE();
  2357. KeInitializeEvent(&getDataContext.Event, SynchronizationEvent, FALSE);
  2358. getDataContext.Status = STATUS_NOT_FOUND;
  2359. status = AcpiArbSetLinkNodeIrqAsync(LinkNode,
  2360. LinkNodeIrq,
  2361. AmlisuppCompletePassive,
  2362. (PVOID)&getDataContext
  2363. );
  2364. if (status == STATUS_PENDING) {
  2365. KeWaitForSingleObject(&getDataContext.Event,
  2366. Executive,
  2367. KernelMode,
  2368. FALSE,
  2369. NULL);
  2370. status = getDataContext.Status;
  2371. }
  2372. return status;
  2373. }
  2374. NTSTATUS
  2375. AcpiArbSetLinkNodeIrqAsync(
  2376. IN PNSOBJ LinkNode,
  2377. IN PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq,
  2378. IN PFNACB CompletionHandler,
  2379. IN PVOID CompletionContext
  2380. )
  2381. {
  2382. PSET_LINK_NODE_STATE state;
  2383. NTSTATUS status;
  2384. ASSERT(LinkNode);
  2385. state = ExAllocatePoolWithTag(NonPagedPool,
  2386. sizeof(SET_LINK_NODE_STATE),
  2387. ACPI_ARBITER_POOLTAG);
  2388. if (!state) {
  2389. return STATUS_INSUFFICIENT_RESOURCES;
  2390. }
  2391. RtlZeroMemory(state, sizeof(SET_LINK_NODE_STATE));
  2392. state->LinkNode = LinkNode;
  2393. state->LinkNodeIrq = LinkNodeIrq;
  2394. state->CompletionHandler = CompletionHandler;
  2395. state->CompletionContext = CompletionContext;
  2396. state->State = StateInitial;
  2397. state->RunCompletionHandler = INITIAL_RUN_COMPLETION;
  2398. return AcpiArbSetLinkNodeIrqWorker(LinkNode,
  2399. STATUS_SUCCESS,
  2400. NULL,
  2401. (PVOID)state
  2402. );
  2403. }
  2404. NTSTATUS
  2405. EXPORT
  2406. AcpiArbSetLinkNodeIrqWorker(
  2407. IN PNSOBJ AcpiObject,
  2408. IN NTSTATUS Status,
  2409. IN POBJDATA Result,
  2410. IN PVOID Context
  2411. )
  2412. {
  2413. BOOLEAN foundTag = FALSE;
  2414. BOOLEAN useEndChecksum = FALSE;
  2415. BOOLEAN useExtendedTag = FALSE;
  2416. NTSTATUS status;
  2417. PNSOBJ childobj;
  2418. PPNP_EXTENDED_IRQ_DESCRIPTOR largeIrq;
  2419. PPNP_IRQ_DESCRIPTOR smallIrq;
  2420. PSET_LINK_NODE_STATE state;
  2421. PUCHAR resource = NULL;
  2422. PUCHAR irqTag = NULL;
  2423. PUCHAR sumchar;
  2424. UCHAR sum = 0;
  2425. UCHAR tagName;
  2426. ULONG length = 0;
  2427. USHORT increment;
  2428. USHORT irqTagLength = 0;
  2429. state = (PSET_LINK_NODE_STATE)Context;
  2430. if (!NT_SUCCESS(Status)) {
  2431. status = Status;
  2432. goto AcpiArbSetLinkNodeIrqWorkerExit;
  2433. }
  2434. ASSERT(state->LinkNodeIrq->Type == CmResourceTypeInterrupt);
  2435. //
  2436. // Entering this function twice with the same state
  2437. // means that we need to run the completion routine.
  2438. //
  2439. InterlockedIncrement(&state->RunCompletionHandler);
  2440. switch (state->State) {
  2441. case StateInitial:
  2442. //
  2443. // Read the _PRS, so that we can choose the appropriate
  2444. // entry and write that back into the _SRS.
  2445. //
  2446. state->State = StateGotPrs;
  2447. status = ACPIGetNSBufferAsync(
  2448. state->LinkNode,
  2449. PACKED_PRS,
  2450. AcpiArbSetLinkNodeIrqWorker,
  2451. (PVOID)state,
  2452. &state->PrsBuff,
  2453. NULL
  2454. );
  2455. if (status == STATUS_PENDING) {
  2456. return status;
  2457. } else if (!NT_SUCCESS(status)) {
  2458. goto AcpiArbSetLinkNodeIrqWorkerExit;
  2459. }
  2460. //
  2461. // Fallthrough to next state
  2462. //
  2463. case StateGotPrs:
  2464. state->State = StateRanSrs;
  2465. if (!state->PrsBuff) {
  2466. status = STATUS_NOT_FOUND;
  2467. goto AcpiArbSetLinkNodeIrqWorkerExit;
  2468. }
  2469. DEBUG_PRINT(7, ("Read _PRS buffer %p\n", state->PrsBuff));
  2470. resource = state->PrsBuff;
  2471. while ( *resource ) {
  2472. tagName = *resource;
  2473. if ( !(tagName & LARGE_RESOURCE_TAG)) {
  2474. increment = (USHORT) (tagName & SMALL_TAG_SIZE_MASK) + 1;
  2475. tagName &= SMALL_TAG_MASK;
  2476. } else {
  2477. increment = ( *(USHORT UNALIGNED *)(resource + 1) ) + 3;
  2478. }
  2479. if (tagName == TAG_END) {
  2480. length += increment;
  2481. if (increment > 1) {
  2482. useEndChecksum = TRUE;
  2483. }
  2484. break;
  2485. }
  2486. //
  2487. // This is the check to see if find a resource that correctly
  2488. // matches the assignment
  2489. //
  2490. // This code is weak. It need to check to see
  2491. // if the flags and interrupt match the descriptor we just found.
  2492. // It is possible for a vendor to use overlapping descriptors that
  2493. // would describe different interrupt settings.
  2494. //
  2495. if (tagName == TAG_IRQ || tagName == TAG_EXTENDED_IRQ) {
  2496. irqTag = resource;
  2497. if (tagName == TAG_EXTENDED_IRQ) {
  2498. irqTagLength = sizeof(PNP_EXTENDED_IRQ_DESCRIPTOR);
  2499. useExtendedTag = TRUE;
  2500. } else {
  2501. irqTagLength = increment;
  2502. }
  2503. length += (ULONG) irqTagLength;
  2504. foundTag = TRUE;
  2505. }
  2506. resource += increment;
  2507. }
  2508. //
  2509. // Did we find the tag that we are looking for?
  2510. //
  2511. if (foundTag == FALSE) {
  2512. ExFreePool( state->PrsBuff );
  2513. status = STATUS_NOT_FOUND;
  2514. goto AcpiArbSetLinkNodeIrqWorkerExit;
  2515. }
  2516. //
  2517. // The next task is to fashion a buffer containing an ACPI-style
  2518. // resource descriptor with exactly one interrupt destination in
  2519. // it. We do this by allocating one
  2520. //
  2521. state->SrsBuff = ExAllocatePoolWithTag(
  2522. NonPagedPool,
  2523. length,
  2524. ACPI_ARBITER_POOLTAG
  2525. );
  2526. if (!state->SrsBuff) {
  2527. ExFreePool(state->PrsBuff);
  2528. status = STATUS_INSUFFICIENT_RESOURCES;
  2529. goto AcpiArbSetLinkNodeIrqWorkerExit;
  2530. }
  2531. ASSERT(irqTagLength <= length);
  2532. RtlCopyMemory(state->SrsBuff, irqTag, irqTagLength);
  2533. ExFreePool(state->PrsBuff);
  2534. //
  2535. // Change the buffer to reflect our choice of interrupts.
  2536. //
  2537. if (!useExtendedTag) {
  2538. // small IRQ
  2539. smallIrq = (PPNP_IRQ_DESCRIPTOR)state->SrsBuff;
  2540. smallIrq->IrqMask = (USHORT)(1 << state->LinkNodeIrq->u.Interrupt.Level);
  2541. } else {
  2542. DEBUG_PRINT(7, ("Found large IRQ descriptor\n"));
  2543. // large IRQ
  2544. largeIrq = (PPNP_EXTENDED_IRQ_DESCRIPTOR)state->SrsBuff;
  2545. largeIrq->Length = irqTagLength - 3;
  2546. largeIrq->TableSize = 1;
  2547. largeIrq->Table[0] = state->LinkNodeIrq->u.Interrupt.Level;
  2548. }
  2549. //
  2550. // Work on the END descriptor
  2551. //
  2552. resource = (state->SrsBuff + irqTagLength);
  2553. *resource = TAG_END;
  2554. if (useEndChecksum) {
  2555. *resource |= 1; // The one is to represent the checksum
  2556. //
  2557. // Calculate the Checksum
  2558. sumchar = state->SrsBuff;
  2559. while (*sumchar != *resource) {
  2560. sum = *sumchar++;
  2561. }
  2562. *(resource+1) = 256 - sum;
  2563. }
  2564. //
  2565. // Now run the _SRS method with this buffer
  2566. //
  2567. //
  2568. // Get the object that we are looking for
  2569. //
  2570. childobj = ACPIAmliGetNamedChild(
  2571. state->LinkNode,
  2572. PACKED_SRS
  2573. );
  2574. if (childobj == NULL) {
  2575. status = STATUS_OBJECT_NAME_NOT_FOUND;
  2576. ExFreePool( state->SrsBuff );
  2577. goto AcpiArbSetLinkNodeIrqWorkerExit;
  2578. }
  2579. state->ObjData.dwDataType = OBJTYPE_BUFFDATA;
  2580. state->ObjData.dwDataLen = length;
  2581. state->ObjData.pbDataBuff = state->SrsBuff;
  2582. DEBUG_PRINT(7, ("Running _SRS\n"));
  2583. status = AMLIAsyncEvalObject(
  2584. childobj,
  2585. NULL,
  2586. 1,
  2587. &state->ObjData,
  2588. AcpiArbSetLinkNodeIrqWorker,
  2589. (PVOID)state
  2590. );
  2591. if (status == STATUS_PENDING) {
  2592. return status;
  2593. } else if (!NT_SUCCESS(status)) {
  2594. goto AcpiArbSetLinkNodeIrqWorkerExit;
  2595. }
  2596. case StateRanSrs:
  2597. //
  2598. // We are done.
  2599. //
  2600. ExFreePool(state->SrsBuff);
  2601. status = STATUS_SUCCESS;
  2602. break;
  2603. default:
  2604. ACPIInternalError( ACPI_IRQARB );
  2605. }
  2606. AcpiArbSetLinkNodeIrqWorkerExit:
  2607. if (state->RunCompletionHandler) {
  2608. state->CompletionHandler(
  2609. AcpiObject,
  2610. status,
  2611. NULL,
  2612. state->CompletionContext
  2613. );
  2614. }
  2615. ExFreePool(state);
  2616. return status;
  2617. }
  2618. NTSTATUS
  2619. AcpiArbCrackPRT(
  2620. IN PDEVICE_OBJECT Pdo,
  2621. IN OUT PNSOBJ *LinkNode,
  2622. IN OUT ULONG *Vector
  2623. )
  2624. /*++
  2625. Routine Description:
  2626. This routine takes a PDO for a device and returns the
  2627. associated link node, if any. The ACPI spec says that
  2628. a _PRT can optionally return a single interrupt vector
  2629. instead of a link node. If this is the case, this function
  2630. returns that vector.
  2631. Arguments:
  2632. Pdo - The PDO of the device that needs to be granted an
  2633. IRQ.
  2634. LinkNode - A pointer to the link node, or NULL if the device
  2635. isn't connected to a link node.
  2636. Vector - The global system interrupt vector that this PCI
  2637. device is connected to. This is meaningless if
  2638. LinkNode is not NULL.
  2639. Return Value:
  2640. If we find a link node or a vector for this device, STATUS_SUCCESS.
  2641. If this isn't a PCI device, STATUS_DEVICE_NOT_FOUND.
  2642. If this is an IDE device, then we have to treat it specially, so
  2643. we return STATUS_RESOURCE_REQUIREMENTS_CHANGED.
  2644. --*/
  2645. {
  2646. PINT_ROUTE_INTERFACE_STANDARD pciInterface;
  2647. NTSTATUS status;
  2648. PDEVICE_OBJECT filter;
  2649. PDEVICE_OBJECT parent;
  2650. PDEVICE_EXTENSION filterExtension;
  2651. OBJDATA adrData;
  2652. OBJDATA pinData;
  2653. OBJDATA prtData;
  2654. OBJDATA linkData;
  2655. OBJDATA indexData;
  2656. PNSOBJ pciBusObj;
  2657. PNSOBJ prtObj;
  2658. ULONG prtElement = 0;
  2659. BOOLEAN found = FALSE;
  2660. KIRQL oldIrql;
  2661. PCI_SLOT_NUMBER pciSlot;
  2662. PCI_SLOT_NUMBER parentSlot;
  2663. ULONG pciBus;
  2664. UCHAR interruptLine;
  2665. UCHAR interruptPin;
  2666. UCHAR parentPin;
  2667. UCHAR classCode;
  2668. UCHAR subClassCode;
  2669. UCHAR flags;
  2670. UCHAR interfaceByte;
  2671. ROUTING_TOKEN routingToken;
  2672. ULONG dummy;
  2673. ULONG bus;
  2674. if (Pdo->DriverObject == AcpiDriverObject) {
  2675. //
  2676. // This is one of our PDOs.
  2677. //
  2678. ASSERT(((PDEVICE_EXTENSION)Pdo->DeviceExtension)->Flags & DEV_TYPE_PDO);
  2679. ASSERT(((PDEVICE_EXTENSION)Pdo->DeviceExtension)->Signature == ACPI_SIGNATURE);
  2680. if (((PDEVICE_EXTENSION)Pdo->DeviceExtension)->Flags & DEV_CAP_PCI) {
  2681. //
  2682. // It's a PCI PDO, which means a root PCI bus,
  2683. // which means that we should just handle this
  2684. // as an ISA device.
  2685. //
  2686. return STATUS_NOT_FOUND;
  2687. }
  2688. }
  2689. ASSERT(PciInterfacesInstantiated);
  2690. *LinkNode = NULL;
  2691. pciInterface = ((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting;
  2692. ASSERT(pciInterface);
  2693. //
  2694. // Call into the PCI driver to find out what we are dealing with.
  2695. //
  2696. pciBus = (ULONG)-1;
  2697. pciSlot.u.AsULONG = (ULONG)-1;
  2698. status = pciInterface->GetInterruptRouting(Pdo,
  2699. &pciBus,
  2700. &pciSlot.u.AsULONG,
  2701. &interruptLine,
  2702. &interruptPin,
  2703. &classCode,
  2704. &subClassCode,
  2705. &parent,
  2706. &routingToken,
  2707. &flags);
  2708. if (!NT_SUCCESS(status)) {
  2709. return STATUS_NOT_FOUND;
  2710. }
  2711. if ((classCode == PCI_CLASS_MASS_STORAGE_CTLR) &&
  2712. (subClassCode == PCI_SUBCLASS_MSC_IDE_CTLR)) {
  2713. HalPciInterfaceReadConfig(NULL,
  2714. (UCHAR)pciBus,
  2715. pciSlot.u.AsULONG,
  2716. &interfaceByte,
  2717. FIELD_OFFSET (PCI_COMMON_CONFIG,
  2718. ProgIf),
  2719. 1);
  2720. if ((interfaceByte & 0x5) == 0) {
  2721. //
  2722. // PCI IDE devices in legacy mode don't use interrupts
  2723. // the PCI way. So bail if this is an IDE device without
  2724. // any native-mode bits set.
  2725. //
  2726. return STATUS_RESOURCE_REQUIREMENTS_CHANGED;
  2727. }
  2728. }
  2729. //
  2730. // See if we have cached this lookup.
  2731. //
  2732. if ((routingToken.LinkNode != 0) ||
  2733. (routingToken.Flags & PCI_STATIC_ROUTING)) {
  2734. if (routingToken.LinkNode) {
  2735. *LinkNode = routingToken.LinkNode;
  2736. } else {
  2737. *Vector = routingToken.StaticVector;
  2738. }
  2739. return STATUS_SUCCESS;
  2740. }
  2741. //
  2742. // Now look for a parent PCI bus that has a _PRT. We may have to
  2743. // look up the tree a bit.
  2744. //
  2745. while (TRUE) {
  2746. //
  2747. // Find the parent's filter
  2748. //
  2749. KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
  2750. filter = AcpiGetFilter(AcpiArbiter.DeviceObject, parent);
  2751. KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
  2752. if (filter) {
  2753. //
  2754. // This is a PCI bus that we either enumerated or
  2755. // filtered.
  2756. //
  2757. ASSERT(IsPciBus(filter));
  2758. filterExtension = filter->DeviceExtension;
  2759. pciBusObj = filterExtension->AcpiObject;
  2760. //
  2761. // Look for a _PRT for this PCI bus.
  2762. //
  2763. prtObj = ACPIAmliGetNamedChild(pciBusObj, PACKED_PRT);
  2764. if (prtObj) {
  2765. //
  2766. // We found the _PRT we are looking for.
  2767. //
  2768. break;
  2769. }
  2770. }
  2771. //
  2772. // We didn't find a _PRT. So go up the PCI tree one
  2773. // and look again.
  2774. //
  2775. bus = (ULONG)-1;
  2776. parentSlot.u.AsULONG = (ULONG)-1;
  2777. status = pciInterface->GetInterruptRouting(parent,
  2778. &bus,
  2779. &parentSlot.u.AsULONG,
  2780. (PUCHAR)&dummy,
  2781. &parentPin,
  2782. &classCode,
  2783. &subClassCode,
  2784. &parent,
  2785. &routingToken,
  2786. (PUCHAR)&dummy);
  2787. if (!NT_SUCCESS(status) ||
  2788. classCode != PCI_CLASS_BRIDGE_DEV) {
  2789. //
  2790. // The parent was not also a PCI device. So
  2791. // this means that there is no _PRT related to
  2792. // this device. Just return the contents of
  2793. // the Interrupt Line register.
  2794. //
  2795. *Vector = interruptLine;
  2796. AcpiInterruptRoutingFailed = TRUE;
  2797. return STATUS_SUCCESS;
  2798. }
  2799. if (subClassCode == PCI_SUBCLASS_BR_PCI_TO_PCI) {
  2800. //
  2801. // Swizzle the interrupt pin according to
  2802. // the PCI-PCI bridge spec.
  2803. //
  2804. interruptPin = PciBridgeSwizzle((UCHAR)pciSlot.u.bits.DeviceNumber, interruptPin);
  2805. pciSlot.u.AsULONG = parentSlot.u.AsULONG;
  2806. } else if (subClassCode == PCI_SUBCLASS_BR_CARDBUS) {
  2807. //
  2808. // Swizzle the interrupt pin according to
  2809. // the Cardbus bridge spec.
  2810. //
  2811. interruptPin = parentPin;
  2812. pciSlot.u.AsULONG = parentSlot.u.AsULONG;
  2813. } else {
  2814. //
  2815. // Bail.
  2816. //
  2817. *Vector = interruptLine;
  2818. AcpiInterruptRoutingFailed = TRUE;
  2819. return STATUS_SUCCESS;
  2820. }
  2821. }
  2822. if (AcpiInterruptRoutingFailed == TRUE) {
  2823. //
  2824. // We succeeded in finding a _PRT to work with,
  2825. // but we have failed in the past. This situation
  2826. // is unrecoverable because we now have dependencies
  2827. // on IRQ routers that we might now accidentally
  2828. // change
  2829. //
  2830. KeBugCheckEx(ACPI_BIOS_ERROR,
  2831. ACPI_CANNOT_ROUTE_INTERRUPTS,
  2832. (ULONG_PTR) Pdo,
  2833. (ULONG_PTR)parent,
  2834. (ULONG_PTR)prtObj);
  2835. }
  2836. // convert interrupt pin from PCI units to ACPI units
  2837. interruptPin--;
  2838. DEBUG_PRINT(2, ("PCI Device %p had _ADR of %x\n", Pdo, pciSlot.u.AsULONG));
  2839. DEBUG_PRINT(2, ("This device connected to Pin %x\n", interruptPin));
  2840. DEBUG_PRINT(2, ("prtObj: %p\n", prtObj));
  2841. //
  2842. // Cycle through all the elements in the _PRT package
  2843. // (each one of which is also a package) looking for
  2844. // the one that describes the link node that we are
  2845. // looking for.
  2846. //
  2847. do {
  2848. status = AMLIEvalPackageElement(prtObj,
  2849. prtElement++,
  2850. &prtData);
  2851. if (!NT_SUCCESS(status)) break;
  2852. ASSERT(prtData.dwDataType == OBJTYPE_PKGDATA);
  2853. if (NT_SUCCESS(AMLIEvalPkgDataElement(&prtData,
  2854. 0,
  2855. &adrData))) {
  2856. if (pciSlot.u.bits.DeviceNumber == (adrData.uipDataValue >> 16)) {
  2857. if ((adrData.uipDataValue & 0xffff) != 0xffff) {
  2858. ////
  2859. //// An _ADR in a _PRT must be of the form xxxxFFFF,
  2860. //// which means that the PCI Device Number is specified,
  2861. //// but the Function Number isn't. If it isn't done this
  2862. //// way, then the machine vendor can introduce
  2863. //// dangerous ambiguities. (Beside that, Pierre makes
  2864. //// Memphis bugcheck if it sees this and I'm trying to
  2865. //// be consistent.) So bugcheck.
  2866. ////
  2867. //KeBugCheckEx(ACPI_BIOS_ERROR,
  2868. // ACPI_PRT_HAS_INVALID_FUNCTION_NUMBERS,
  2869. // (ULONG_PTR)prtObj,
  2870. // prtElement,
  2871. // adrData.uipDataValue);
  2872. DEBUG_PRINT(0, ("PRT entry has ambiguous address %x\n", adrData.uipDataValue));
  2873. status = STATUS_INVALID_PARAMETER;
  2874. pciSlot.u.bits.DeviceNumber = (ULONG)(adrData.uipDataValue >> 16) & 0xffff;
  2875. pciSlot.u.bits.FunctionNumber = (ULONG)(adrData.uipDataValue & 0xffff);
  2876. AMLIFreeDataBuffs(&adrData, 1);
  2877. AMLIFreeDataBuffs(&prtData, 1);
  2878. goto AcpiArbCrackPRTError;
  2879. }
  2880. //
  2881. // This sub-package does refer to the PCI device
  2882. // that we are concerned with. Now look to see if
  2883. // we have found the link node that is connected
  2884. // to the PCI interrupt PIN that this device will trigger.
  2885. //
  2886. // N.B. We only have to compare the top 16 bits
  2887. // because the function number is irrelevent
  2888. // when considering interrupts. We get the
  2889. // pin from config space.
  2890. //
  2891. if (NT_SUCCESS(AMLIEvalPkgDataElement(&prtData,
  2892. 1,
  2893. &pinData))) {
  2894. if (pinData.uipDataValue == interruptPin) {
  2895. //
  2896. // This is the package that describes the link node we
  2897. // are interested in. Get the name of the link node.
  2898. //
  2899. if (NT_SUCCESS(AMLIEvalPkgDataElement(&prtData,
  2900. 2,
  2901. &linkData))) {
  2902. found = TRUE;
  2903. }
  2904. //
  2905. // Look at the Source Index, too.
  2906. //
  2907. if (NT_SUCCESS(AMLIEvalPkgDataElement(&prtData,
  2908. 3,
  2909. &indexData))) {
  2910. found = TRUE;
  2911. }
  2912. }
  2913. AMLIFreeDataBuffs(&pinData, 1);
  2914. }
  2915. }
  2916. AMLIFreeDataBuffs(&adrData, 1);
  2917. }
  2918. AMLIFreeDataBuffs(&prtData, 1);
  2919. } while (found == FALSE);
  2920. status = STATUS_NOT_FOUND;
  2921. if (found) {
  2922. //
  2923. // First check to see if linkData is valid. If it is,
  2924. // then we use it.
  2925. //
  2926. if (linkData.dwDataType == OBJTYPE_STRDATA) {
  2927. if (linkData.pbDataBuff) {
  2928. status = AMLIGetNameSpaceObject(linkData.pbDataBuff,
  2929. prtObj,
  2930. LinkNode,
  2931. 0);
  2932. if (NT_SUCCESS(status)) {
  2933. routingToken.LinkNode = *LinkNode;
  2934. routingToken.StaticVector = 0;
  2935. routingToken.Flags = 0;
  2936. pciInterface->SetInterruptRoutingToken(Pdo,
  2937. &routingToken);
  2938. goto AcpiArbCrackPRTExit;
  2939. }
  2940. status = STATUS_OBJECT_NAME_NOT_FOUND;
  2941. goto AcpiArbCrackPRTError;
  2942. }
  2943. }
  2944. //
  2945. // If linkData didn't pan out, then use indexData.
  2946. //
  2947. if (indexData.dwDataType == OBJTYPE_INTDATA) {
  2948. //
  2949. // We have an integer which describes the "Global System Interrupt Vector"
  2950. // that this PCI device will trigger.
  2951. //
  2952. *Vector = (ULONG)indexData.uipDataValue;
  2953. status = STATUS_SUCCESS;
  2954. routingToken.LinkNode = 0;
  2955. routingToken.StaticVector = *Vector;
  2956. routingToken.Flags = PCI_STATIC_ROUTING;
  2957. pciInterface->SetInterruptRoutingToken(Pdo,
  2958. &routingToken);
  2959. goto AcpiArbCrackPRTExit;
  2960. }
  2961. status = STATUS_INVALID_IMAGE_FORMAT;
  2962. AcpiArbCrackPRTExit:
  2963. AMLIFreeDataBuffs(&linkData, 1);
  2964. AMLIFreeDataBuffs(&indexData, 1);
  2965. }
  2966. else
  2967. AcpiArbCrackPRTError:
  2968. {
  2969. ANSI_STRING ansiString;
  2970. UNICODE_STRING unicodeString;
  2971. UNICODE_STRING slotName;
  2972. UNICODE_STRING funcName;
  2973. PWCHAR prtEntry[4];
  2974. WCHAR IRQARBname[20];
  2975. WCHAR slotBuff[10];
  2976. WCHAR funcBuff[10];
  2977. swprintf( IRQARBname, L"IRQARB");
  2978. RtlInitUnicodeString(&slotName, slotBuff);
  2979. RtlInitUnicodeString(&funcName, funcBuff);
  2980. if (!NT_SUCCESS(RtlIntegerToUnicodeString(pciSlot.u.bits.DeviceNumber, 0, &slotName))) {
  2981. return status;
  2982. }
  2983. if (!NT_SUCCESS(RtlIntegerToUnicodeString(pciSlot.u.bits.FunctionNumber, 0, &funcName))) {
  2984. return status;
  2985. }
  2986. prtEntry[0] = IRQARBname;
  2987. prtEntry[1] = slotBuff;
  2988. prtEntry[2] = funcBuff;
  2989. switch (status) {
  2990. case STATUS_OBJECT_NAME_NOT_FOUND:
  2991. RtlInitAnsiString(&ansiString,
  2992. linkData.pbDataBuff);
  2993. RtlAnsiStringToUnicodeString(&unicodeString,
  2994. &ansiString,
  2995. TRUE);
  2996. prtEntry[3] = unicodeString.Buffer;
  2997. ACPIWriteEventLogEntry(ACPI_ERR_MISSING_LINK_NODE,
  2998. &prtEntry,
  2999. 4,
  3000. NULL,
  3001. 0);
  3002. RtlFreeUnicodeString(&unicodeString);
  3003. DEBUG_PRINT(0, ("Couldn't find link node (%s)\n", linkData.pbDataBuff));
  3004. //KeBugCheckEx(ACPI_BIOS_ERROR,
  3005. // ACPI_PRT_CANNOT_FIND_LINK_NODE,
  3006. // (ULONG_PTR)Pdo,
  3007. // (ULONG_PTR)linkData.pbDataBuff,
  3008. // (ULONG_PTR)prtObj);
  3009. break;
  3010. case STATUS_NOT_FOUND:
  3011. ACPIWriteEventLogEntry(ACPI_ERR_MISSING_PRT_ENTRY,
  3012. &prtEntry,
  3013. 3,
  3014. NULL,
  3015. 0);
  3016. DEBUG_PRINT(0, ("The ACPI _PRT package didn't contain a mapping for the PCI\n"));
  3017. DEBUG_PRINT(0, ("device at _ADR %x\n", pciSlot.u.AsULONG));
  3018. //KeBugCheckEx(ACPI_BIOS_ERROR,
  3019. // ACPI_PRT_CANNOT_FIND_DEVICE_ENTRY,
  3020. // (ULONG_PTR)Pdo,
  3021. // pciSlot.u.AsULONG,
  3022. // (ULONG_PTR)prtObj);
  3023. break;
  3024. case STATUS_INVALID_PARAMETER:
  3025. ACPIWriteEventLogEntry(ACPI_ERR_AMBIGUOUS_DEVICE_ADDRESS,
  3026. &prtEntry,
  3027. 3,
  3028. NULL,
  3029. 0);
  3030. break;
  3031. }
  3032. status = STATUS_UNSUCCESSFUL;
  3033. }
  3034. return status;
  3035. }
  3036. PDEVICE_OBJECT
  3037. AcpiGetFilter(
  3038. IN PDEVICE_OBJECT Root,
  3039. IN PDEVICE_OBJECT Pdo
  3040. )
  3041. /*++
  3042. Routine Description:
  3043. This routine takes a PDO for a device and returns the
  3044. DO of the filter that ACPI has slapped onto it. In the
  3045. case that this PDO belongs to the ACPI driver, then
  3046. it is returned.
  3047. Arguments:
  3048. Root - The device object that we are using as the
  3049. root of the search.
  3050. Pdo - The PDO of the device who's filter we seek
  3051. Return Value:
  3052. a DEVICE_OBJECT, if ACPI is filtering this Pdo, NULL otherwise
  3053. --*/
  3054. {
  3055. PDEVICE_EXTENSION deviceExtension;
  3056. PDEVICE_EXTENSION childExtension;
  3057. PDEVICE_EXTENSION firstChild;
  3058. PDEVICE_OBJECT filter;
  3059. deviceExtension = Root->DeviceExtension;
  3060. //
  3061. // If Root is the filter, we are done.
  3062. //
  3063. if (((deviceExtension->Flags & DEV_TYPE_PDO) ||
  3064. (deviceExtension->Flags & DEV_TYPE_FILTER)) &&
  3065. (deviceExtension->PhysicalDeviceObject == Pdo)) {
  3066. ASSERT(Root->Type == IO_TYPE_DEVICE);
  3067. return Root;
  3068. }
  3069. //
  3070. // Return NULL if this device has no children,
  3071. // (which is signified by the ChildDeviceList pointer
  3072. // pointing to itself.
  3073. //
  3074. if (deviceExtension->ChildDeviceList.Flink ==
  3075. (PVOID)&(deviceExtension->ChildDeviceList.Flink)) {
  3076. return NULL;
  3077. }
  3078. firstChild = (PDEVICE_EXTENSION) CONTAINING_RECORD(
  3079. deviceExtension->ChildDeviceList.Flink,
  3080. DEVICE_EXTENSION,
  3081. SiblingDeviceList );
  3082. childExtension = firstChild;
  3083. do {
  3084. //
  3085. // Make sure the device extension is complete.
  3086. //
  3087. if (childExtension->DeviceObject) {
  3088. filter = AcpiGetFilter(childExtension->DeviceObject, Pdo);
  3089. if (filter) {
  3090. return filter;
  3091. }
  3092. }
  3093. childExtension = (PDEVICE_EXTENSION) CONTAINING_RECORD(
  3094. childExtension->SiblingDeviceList.Flink,
  3095. DEVICE_EXTENSION,
  3096. SiblingDeviceList );
  3097. } while (childExtension != firstChild);
  3098. //
  3099. // Must not be on this branch...
  3100. //
  3101. return NULL;
  3102. }
  3103. BOOLEAN
  3104. LinkNodeInUse(
  3105. IN PARBITER_INSTANCE Arbiter,
  3106. IN PNSOBJ LinkNode,
  3107. IN OUT ULONG *Irq, OPTIONAL
  3108. IN OUT UCHAR *Flags OPTIONAL
  3109. )
  3110. /*++
  3111. Routine Description:
  3112. This routine indicates whether a link node is current
  3113. in use and, if so, returns the IRQ that it is currently
  3114. connected to.
  3115. Arguments:
  3116. Arbiter - current arbiter state
  3117. LinkNode - link node in question
  3118. Irq - "Global System Interrupt Vector" that
  3119. the link node is currently using.
  3120. Flags - flags associated with the vector that
  3121. the link node is connected to.
  3122. Return Value:
  3123. TRUE if the link node is currently being used.
  3124. --*/
  3125. {
  3126. PLIST_ENTRY linkNodes;
  3127. PLINK_NODE linkNode;
  3128. NTSTATUS status;
  3129. PAGED_CODE();
  3130. ASSERT(LinkNode);
  3131. linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
  3132. if (IsListEmpty(linkNodes)) {
  3133. //
  3134. // There are no link nodes in use.
  3135. //
  3136. DEBUG_PRINT(3, ("LinkNode list empty\n"));
  3137. return FALSE;
  3138. }
  3139. linkNode = (PLINK_NODE)linkNodes->Flink;
  3140. while (linkNode != (PLINK_NODE)linkNodes) {
  3141. //
  3142. // Is this the node we were looking for?
  3143. //
  3144. if (linkNode->NameSpaceObject == LinkNode) {
  3145. if((LONG)(linkNode->ReferenceCount + linkNode->TempRefCount) > 0) {
  3146. //
  3147. // This link node is on the list and it is currently referenced.
  3148. //
  3149. if (Irq) *Irq = (ULONG)linkNode->TempIrq;
  3150. if (Flags) *Flags = linkNode->Flags;
  3151. DEBUG_PRINT(3, ("Link Node %p is in use\n", LinkNode));
  3152. return TRUE;
  3153. } else {
  3154. DEBUG_PRINT(3, ("Link Node %p is currently unreferenced\n", LinkNode));
  3155. return FALSE;
  3156. }
  3157. }
  3158. linkNode = (PLINK_NODE)linkNode->List.Flink;
  3159. }
  3160. DEBUG_PRINT(3, ("Didn't find our link node (%p) on the Link Node List\n", LinkNode));
  3161. //
  3162. // Didn't ever find the link node we were looking for.
  3163. //
  3164. return FALSE;
  3165. }
  3166. NTSTATUS
  3167. GetLinkNodeFlags(
  3168. IN PARBITER_INSTANCE Arbiter,
  3169. IN PNSOBJ LinkNode,
  3170. IN OUT UCHAR *Flags
  3171. )
  3172. {
  3173. NTSTATUS status;
  3174. BOOLEAN inUse;
  3175. PAGED_CODE();
  3176. //
  3177. // This guarantees that LinkNodeInUse will succeed
  3178. // and will contain the valid flags.
  3179. //
  3180. status = AcpiArbReferenceLinkNode(Arbiter,
  3181. LinkNode,
  3182. 0);
  3183. if (!NT_SUCCESS(status)) {
  3184. return status;
  3185. }
  3186. inUse = LinkNodeInUse(Arbiter,
  3187. LinkNode,
  3188. NULL,
  3189. Flags);
  3190. ASSERT(inUse);
  3191. //
  3192. // Set the state back to the way we found it.
  3193. //
  3194. status = AcpiArbDereferenceLinkNode(Arbiter,
  3195. LinkNode);
  3196. ASSERT(NT_SUCCESS(status));
  3197. return STATUS_SUCCESS;
  3198. }
  3199. NTSTATUS
  3200. AcpiArbReferenceLinkNode(
  3201. IN PARBITER_INSTANCE Arbiter,
  3202. IN PNSOBJ LinkNode,
  3203. IN ULONG Irq
  3204. )
  3205. /*++
  3206. Routine Description:
  3207. This routine keeps two reference counts. The first
  3208. is a permanent count, representing hardware resources
  3209. that have been committed. The second is a delta
  3210. representing what is currently under consideration.
  3211. Arguments:
  3212. Arbiter - current arbiter state
  3213. LinkNode - link node in question
  3214. Irq - "Global System Interrupt Vector" that
  3215. the link node is connected to.
  3216. Permanently - indicates whether this reference is
  3217. for a committed allocation
  3218. Return Value:
  3219. status
  3220. --*/
  3221. {
  3222. PCM_RESOURCE_LIST resList = NULL;
  3223. PLIST_ENTRY linkNodes;
  3224. PLINK_NODE linkNode;
  3225. BOOLEAN found = FALSE;
  3226. NTSTATUS status;
  3227. UCHAR flags;
  3228. PAGED_CODE();
  3229. DEBUG_PRINT(3, ("Referencing link node %p, Irq: %x\n",
  3230. LinkNode,
  3231. Irq));
  3232. ASSERT(LinkNode);
  3233. linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
  3234. linkNode = (PLINK_NODE)linkNodes->Flink;
  3235. //
  3236. // Search to see if we are already know about this link node.
  3237. //
  3238. while (linkNode != (PLINK_NODE)linkNodes) {
  3239. if (linkNode->NameSpaceObject == LinkNode) {
  3240. found = TRUE;
  3241. break;
  3242. }
  3243. linkNode = (PLINK_NODE)linkNode->List.Flink;
  3244. }
  3245. //
  3246. // If not, then we need to keep track of it. And
  3247. // the hardware needs to be made to match it.
  3248. //
  3249. if (!found) {
  3250. //
  3251. // This is the first permanent reference. So
  3252. // program the link node hardware.
  3253. //
  3254. linkNode = ExAllocatePoolWithTag(NonPagedPool, sizeof(LINK_NODE), ACPI_ARBITER_POOLTAG);
  3255. if (!linkNode) {
  3256. return STATUS_INSUFFICIENT_RESOURCES;
  3257. }
  3258. RtlZeroMemory(linkNode, sizeof(LINK_NODE));
  3259. linkNode->NameSpaceObject = LinkNode;
  3260. linkNode->CurrentIrq = Irq;
  3261. linkNode->TempIrq = Irq;
  3262. linkNode->AttachedDevices.Next = (PSINGLE_LIST_ENTRY)&linkNode->AttachedDevices;
  3263. InsertTailList(linkNodes, ((PLIST_ENTRY)(linkNode)));
  3264. //
  3265. // Figure out what the flags ought to be.
  3266. //
  3267. status = AcpiArbGetLinkNodeOptions(LinkNode,
  3268. &resList,
  3269. &flags);
  3270. if (NT_SUCCESS(status)) {
  3271. ExFreePool(resList); // not actually needed here
  3272. //
  3273. // Record the flags associated with this link node.
  3274. //
  3275. linkNode->Flags = flags;
  3276. } else {
  3277. ASSERT(NT_SUCCESS(status));
  3278. //
  3279. // Something is wrong. Make up reasonable flags.
  3280. //
  3281. linkNode->Flags = VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
  3282. }
  3283. DEBUG_PRINT(3, ("Link node object connected to vector %x\n", Irq));
  3284. }
  3285. #if DBG
  3286. else {
  3287. if (!((linkNode->ReferenceCount == 0) &&
  3288. (linkNode->TempRefCount == 0))) {
  3289. //
  3290. // Make sure that we maintain consistency
  3291. // with the flags.
  3292. //
  3293. //
  3294. // Check to see that the link node hasn't changed.
  3295. //
  3296. status = AcpiArbGetLinkNodeOptions(LinkNode,
  3297. &resList,
  3298. &flags);
  3299. if (resList) ExFreePool(resList); // not actually needed here
  3300. ASSERT(NT_SUCCESS(status));
  3301. ASSERT(flags == linkNode->Flags);
  3302. }
  3303. }
  3304. #endif
  3305. DEBUG_PRINT(3, (" %d:%d\n", linkNode->ReferenceCount, linkNode->TempRefCount));
  3306. //
  3307. // Increase its reference count.
  3308. //
  3309. linkNode->TempIrq = Irq;
  3310. linkNode->TempRefCount++;
  3311. return STATUS_SUCCESS;
  3312. }
  3313. NTSTATUS
  3314. AcpiArbDereferenceLinkNode(
  3315. IN PARBITER_INSTANCE Arbiter,
  3316. IN PNSOBJ LinkNode
  3317. )
  3318. /*++
  3319. Routine Description:
  3320. This routine is the converse of the one above.
  3321. Arguments:
  3322. Arbiter - current arbiter state
  3323. LinkNode - link node in question
  3324. Permanently - indicates whether this reference is
  3325. for a committed allocation
  3326. Return Value:
  3327. status
  3328. --*/
  3329. {
  3330. PSINGLE_LIST_ENTRY attachedDev;
  3331. PLIST_ENTRY linkNodes;
  3332. PLINK_NODE linkNode;
  3333. BOOLEAN found = FALSE;
  3334. PAGED_CODE();
  3335. ASSERT(LinkNode);
  3336. linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
  3337. linkNode = (PLINK_NODE)linkNodes->Flink;
  3338. //
  3339. // Search for this link node.
  3340. //
  3341. while (linkNode != (PLINK_NODE)linkNodes) {
  3342. if (linkNode->NameSpaceObject == LinkNode) {
  3343. found = TRUE;
  3344. break;
  3345. }
  3346. linkNode = (PLINK_NODE)linkNode->List.Flink;
  3347. }
  3348. ASSERT(found);
  3349. DEBUG_PRINT(3, ("Dereferencing link node %p %d:%d\n", LinkNode, linkNode->ReferenceCount, linkNode->TempRefCount));
  3350. linkNode->TempRefCount--;
  3351. return STATUS_SUCCESS;
  3352. }
  3353. NTSTATUS
  3354. ClearTempLinkNodeCounts(
  3355. IN PARBITER_INSTANCE Arbiter
  3356. )
  3357. /*++
  3358. Routine Description:
  3359. This routine resets all the temporary counts (deltas)
  3360. to zero because the allocations being considered are
  3361. being thrown away instead of being committed.
  3362. Arguments:
  3363. Arbiter - current arbiter state
  3364. Return Value:
  3365. status
  3366. --*/
  3367. {
  3368. PLIST_ENTRY linkNodes;
  3369. PLINK_NODE linkNode;
  3370. PAGED_CODE();
  3371. linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
  3372. linkNode = (PLINK_NODE)linkNodes->Flink;
  3373. //
  3374. // Run through the link nodes.
  3375. //
  3376. while (linkNode != (PLINK_NODE)linkNodes) {
  3377. linkNode->TempRefCount = 0;
  3378. linkNode->TempIrq = linkNode->CurrentIrq;
  3379. linkNode = (PLINK_NODE)linkNode->List.Flink;
  3380. }
  3381. return STATUS_SUCCESS;
  3382. }
  3383. NTSTATUS
  3384. MakeTempLinkNodeCountsPermanent(
  3385. IN PARBITER_INSTANCE Arbiter
  3386. )
  3387. /*++
  3388. Routine Description:
  3389. This routine reconciles the temporary and
  3390. permanent references because the resources being
  3391. considered are being committed.
  3392. Arguments:
  3393. Arbiter - current arbiter state
  3394. Return Value:
  3395. status
  3396. --*/
  3397. {
  3398. CM_PARTIAL_RESOURCE_DESCRIPTOR irqDesc;
  3399. PLIST_ENTRY linkNodes;
  3400. PLINK_NODE linkNode, nextNode;
  3401. UCHAR flags;
  3402. PNSOBJ dis;
  3403. PAGED_CODE();
  3404. DEBUG_PRINT(3, ("MakeTempLinkNodeCountsPermanent\n"));
  3405. //
  3406. // Run through the link nodes.
  3407. //
  3408. linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
  3409. linkNode = (PLINK_NODE)linkNodes->Flink;
  3410. while (linkNode != (PLINK_NODE)linkNodes) {
  3411. nextNode = (PLINK_NODE)linkNode->List.Flink;
  3412. DEBUG_PRINT(3, ("LinkNode: %p -- Perm: %d, Temp: %d\n",
  3413. linkNode,
  3414. linkNode->ReferenceCount,
  3415. linkNode->TempRefCount));
  3416. //
  3417. // Attempt to sanity check this link node.
  3418. //
  3419. ASSERT(linkNode);
  3420. ASSERT(linkNode->List.Flink);
  3421. ASSERT(linkNode->ReferenceCount <= 70);
  3422. ASSERT(linkNode->TempRefCount <= 70);
  3423. ASSERT(linkNode->TempRefCount >= -70);
  3424. ASSERT(linkNode->CurrentIrq < 0x80000000);
  3425. ASSERT((linkNode->Flags & ~(VECTOR_MODE | VECTOR_POLARITY)) == 0);
  3426. //
  3427. // Program the link node if either the previous reference count
  3428. // was 0 or if the previous IRQ was different. *And* the current
  3429. // reference count is non-zero.
  3430. //
  3431. if (((linkNode->ReferenceCount == 0) ||
  3432. (linkNode->CurrentIrq != linkNode->TempIrq)) &&
  3433. ((linkNode->ReferenceCount + linkNode->TempRefCount) != 0)) {
  3434. irqDesc.Type = CmResourceTypeInterrupt;
  3435. irqDesc.ShareDisposition = CmResourceShareShared;
  3436. irqDesc.Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
  3437. irqDesc.u.Interrupt.Level = (ULONG)linkNode->TempIrq;
  3438. irqDesc.u.Interrupt.Vector = (ULONG)linkNode->TempIrq;
  3439. irqDesc.u.Interrupt.Affinity = 0xffffffff;
  3440. AcpiArbSetLinkNodeIrq(linkNode->NameSpaceObject,
  3441. &irqDesc);
  3442. }
  3443. if ((linkNode->ReferenceCount + linkNode->TempRefCount) == 0) {
  3444. //
  3445. // This link node has no more references. Disable it.
  3446. //
  3447. dis = ACPIAmliGetNamedChild(linkNode->NameSpaceObject, PACKED_DIS);
  3448. if (dis) {
  3449. AMLIEvalNameSpaceObject(dis, NULL, 0, NULL);
  3450. }
  3451. }
  3452. linkNode->ReferenceCount = linkNode->ReferenceCount +
  3453. linkNode->TempRefCount;
  3454. linkNode->TempRefCount = 0;
  3455. linkNode->CurrentIrq = linkNode->TempIrq;
  3456. linkNode = nextNode;
  3457. }
  3458. return STATUS_SUCCESS;
  3459. }
  3460. #ifdef DBG
  3461. VOID
  3462. TrackDevicesConnectedToLinkNode(
  3463. IN PNSOBJ LinkNode,
  3464. IN PDEVICE_OBJECT Pdo
  3465. )
  3466. {
  3467. PLINK_NODE_ATTACHED_DEVICES attachedDevs, newPdo;
  3468. PLIST_ENTRY linkNodes;
  3469. PLINK_NODE linkNode, nextNode;
  3470. BOOLEAN found = FALSE;
  3471. PAGED_CODE();
  3472. //
  3473. // Run through the link nodes.
  3474. //
  3475. linkNodes = &((PARBITER_EXTENSION)(AcpiArbiter.ArbiterState.Extension))->LinkNodeHead;
  3476. linkNode = (PLINK_NODE)linkNodes->Flink;
  3477. while (linkNode != (PLINK_NODE)linkNodes) {
  3478. if (linkNode->NameSpaceObject == LinkNode) {
  3479. found = TRUE;
  3480. break;
  3481. }
  3482. linkNode = (PLINK_NODE)linkNode->List.Flink;
  3483. }
  3484. if (found) {
  3485. attachedDevs = (PLINK_NODE_ATTACHED_DEVICES)linkNode->AttachedDevices.Next;
  3486. found = FALSE;
  3487. while (attachedDevs != (PLINK_NODE_ATTACHED_DEVICES)&linkNode->AttachedDevices.Next) {
  3488. if (attachedDevs->Pdo == Pdo) {
  3489. found = TRUE;
  3490. break;
  3491. }
  3492. attachedDevs = (PLINK_NODE_ATTACHED_DEVICES)attachedDevs->List.Next;
  3493. }
  3494. if (!found) {
  3495. newPdo = ExAllocatePoolWithTag(PagedPool,
  3496. sizeof(LINK_NODE_ATTACHED_DEVICES),
  3497. ACPI_ARBITER_POOLTAG);
  3498. if (!newPdo) {
  3499. return;
  3500. }
  3501. RtlZeroMemory(newPdo, sizeof(LINK_NODE_ATTACHED_DEVICES));
  3502. newPdo->Pdo = Pdo;
  3503. PushEntryList(&linkNode->AttachedDevices,
  3504. (PSINGLE_LIST_ENTRY)newPdo);
  3505. }
  3506. }
  3507. }
  3508. #endif
  3509. typedef enum {
  3510. RestoreStateInitial,
  3511. RestoreStateDisabled,
  3512. RestoreStateEnabled
  3513. } RESTORE_IRQ_STATE, *PRESTORE_IRQ_STATE;
  3514. typedef struct {
  3515. CM_PARTIAL_RESOURCE_DESCRIPTOR IrqDesc;
  3516. PLIST_ENTRY LinkNodes;
  3517. PLINK_NODE LinkNode;
  3518. RESTORE_IRQ_STATE State;
  3519. KSPIN_LOCK SpinLock;
  3520. KIRQL OldIrql;
  3521. BOOLEAN CompletingSetLink;
  3522. LONG RunCompletion;
  3523. PFNACB CompletionHandler;
  3524. PVOID CompletionContext;
  3525. } RESTORE_ROUTING_STATE, *PRESTORE_ROUTING_STATE;
  3526. NTSTATUS
  3527. IrqArbRestoreIrqRouting(
  3528. PFNACB CompletionHandler,
  3529. PVOID CompletionContext
  3530. )
  3531. /*++
  3532. Routine Description:
  3533. This routine will set all the IRQ router settings
  3534. to whatever is described in the Link Node list.
  3535. This is useful when the machine is coming out
  3536. of hibernation.
  3537. Arguments:
  3538. Return Value:
  3539. status
  3540. Notes:
  3541. This function is expected to run at DPC level
  3542. during machine wakeup. It is assumed that no
  3543. other part of the arbiter code will be running
  3544. at that time. Since we can't wait for the
  3545. arbiter lock at DPC level, we will have to
  3546. assume it is not taken.
  3547. --*/
  3548. {
  3549. PRESTORE_ROUTING_STATE state;
  3550. PARBITER_INSTANCE arbiter;
  3551. NTSTATUS status;
  3552. //
  3553. // First check to see if there is any work to do.
  3554. //
  3555. if (HalPicStateIntact()) {
  3556. return STATUS_SUCCESS;
  3557. }
  3558. state = ExAllocatePoolWithTag(NonPagedPool,
  3559. sizeof(RESTORE_ROUTING_STATE),
  3560. ACPI_ARBITER_POOLTAG);
  3561. if (!state) {
  3562. return STATUS_INSUFFICIENT_RESOURCES;
  3563. }
  3564. RtlZeroMemory(state, sizeof(RESTORE_ROUTING_STATE));
  3565. state->State = RestoreStateInitial;
  3566. state->RunCompletion = INITIAL_RUN_COMPLETION;
  3567. state->CompletionHandler = CompletionHandler;
  3568. state->CompletionContext = CompletionContext;
  3569. state->IrqDesc.Type = CmResourceTypeInterrupt;
  3570. state->IrqDesc.ShareDisposition = CmResourceShareShared;
  3571. state->IrqDesc.Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
  3572. state->IrqDesc.u.Interrupt.Affinity = 0xffffffff;
  3573. arbiter = &AcpiArbiter.ArbiterState;
  3574. state->LinkNodes = &((PARBITER_EXTENSION)(arbiter->Extension))->LinkNodeHead;
  3575. state->LinkNode = (PLINK_NODE)state->LinkNodes->Flink;
  3576. KeInitializeSpinLock(&state->SpinLock);
  3577. return IrqArbRestoreIrqRoutingWorker(state->LinkNode->NameSpaceObject,
  3578. STATUS_SUCCESS,
  3579. NULL,
  3580. (PVOID)state
  3581. );
  3582. }
  3583. NTSTATUS
  3584. EXPORT
  3585. IrqArbRestoreIrqRoutingWorker(
  3586. IN PNSOBJ AcpiObject,
  3587. IN NTSTATUS Status,
  3588. IN POBJDATA Result,
  3589. IN PVOID Context
  3590. )
  3591. {
  3592. NTSTATUS status = Status;
  3593. PDEVICE_EXTENSION deviceExtension;
  3594. PRESTORE_ROUTING_STATE state;
  3595. state = (PRESTORE_ROUTING_STATE)Context;
  3596. //
  3597. // Entering this function twice with the same state
  3598. // means that we need to run the completion routine.
  3599. //
  3600. InterlockedIncrement(&state->RunCompletion);
  3601. switch (state->State) {
  3602. case RestoreStateInitial:
  3603. state->State = RestoreStateDisabled;
  3604. deviceExtension = ACPIInternalGetDeviceExtension( AcpiArbiter.DeviceObject );
  3605. status = DisableLinkNodesAsync(
  3606. deviceExtension->AcpiObject,
  3607. IrqArbRestoreIrqRoutingWorker,
  3608. (PVOID)state);
  3609. if (status == STATUS_PENDING) {
  3610. return status;
  3611. }
  3612. //
  3613. // Fall through
  3614. //
  3615. case RestoreStateDisabled:
  3616. KeAcquireSpinLock(&state->SpinLock,
  3617. &state->OldIrql);
  3618. while (state->LinkNode != (PLINK_NODE)state->LinkNodes) {
  3619. if (state->LinkNode->ReferenceCount > 0) {
  3620. //
  3621. // Program the link node.
  3622. //
  3623. state->IrqDesc.u.Interrupt.Level = (ULONG)state->LinkNode->CurrentIrq;
  3624. state->IrqDesc.u.Interrupt.Vector = (ULONG)state->LinkNode->CurrentIrq;
  3625. if (!state->CompletingSetLink) {
  3626. status = AcpiArbSetLinkNodeIrqAsync(state->LinkNode->NameSpaceObject,
  3627. &state->IrqDesc,
  3628. IrqArbRestoreIrqRoutingWorker,
  3629. (PVOID)state
  3630. );
  3631. if (status == STATUS_PENDING) {
  3632. state->CompletingSetLink = TRUE;
  3633. KeReleaseSpinLock(&state->SpinLock,
  3634. state->OldIrql);
  3635. return status;
  3636. }
  3637. }
  3638. }
  3639. state->CompletingSetLink = FALSE;
  3640. state->LinkNode = (PLINK_NODE)state->LinkNode->List.Flink;
  3641. }
  3642. state->State = RestoreStateEnabled;
  3643. KeReleaseSpinLock(&state->SpinLock,
  3644. state->OldIrql);
  3645. case RestoreStateEnabled:
  3646. //
  3647. // Now that we are done programming all the link nodes,
  3648. // we need to restore the ELCR and unmask all the
  3649. // device interrupts.
  3650. //
  3651. HalRestorePicState();
  3652. if (state->RunCompletion) {
  3653. state->CompletionHandler(AcpiObject,
  3654. status,
  3655. NULL,
  3656. state->CompletionContext
  3657. );
  3658. }
  3659. }
  3660. ExFreePool(state);
  3661. return status;
  3662. }
  3663. typedef enum {
  3664. DisableStateInitial,
  3665. DisableStateGotHid,
  3666. DisableStateRanDis,
  3667. DisableStateGetChild,
  3668. DisableStateRecursing
  3669. } DISABLE_LINK_NODES_STATE;
  3670. typedef struct {
  3671. DISABLE_LINK_NODES_STATE State;
  3672. PNSOBJ RootDevice;
  3673. PUCHAR Hid;
  3674. PNSOBJ Dis;
  3675. PNSOBJ Sibling;
  3676. PNSOBJ NextSibling;
  3677. LONG RunCompletionHandler;
  3678. PFNACB CompletionHandler;
  3679. PVOID CompletionContext;
  3680. } DISABLE_LINK_NODES_CONTEXT, *PDISABLE_LINK_NODES_CONTEXT;
  3681. NTSTATUS
  3682. DisableLinkNodesAsync(
  3683. IN PNSOBJ Root,
  3684. IN PFNACB CompletionHandler,
  3685. IN PVOID CompletionContext
  3686. )
  3687. {
  3688. PDISABLE_LINK_NODES_CONTEXT context;
  3689. NTSTATUS status;
  3690. context = ExAllocatePoolWithTag(NonPagedPool,
  3691. sizeof(DISABLE_LINK_NODES_CONTEXT),
  3692. ACPI_ARBITER_POOLTAG);
  3693. if (!context) {
  3694. return STATUS_INSUFFICIENT_RESOURCES;
  3695. }
  3696. RtlZeroMemory(context, sizeof(DISABLE_LINK_NODES_CONTEXT));
  3697. context->State = DisableStateInitial;
  3698. context->RootDevice = Root;
  3699. context->CompletionHandler = CompletionHandler;
  3700. context->CompletionContext = CompletionContext;
  3701. context->RunCompletionHandler = INITIAL_RUN_COMPLETION;
  3702. return DisableLinkNodesAsyncWorker(Root,
  3703. STATUS_SUCCESS,
  3704. NULL,
  3705. (PVOID)context
  3706. );
  3707. }
  3708. NTSTATUS
  3709. EXPORT
  3710. DisableLinkNodesAsyncWorker(
  3711. IN PNSOBJ AcpiObject,
  3712. IN NTSTATUS Status,
  3713. IN POBJDATA Result,
  3714. IN PVOID Context
  3715. )
  3716. {
  3717. PDISABLE_LINK_NODES_CONTEXT context;
  3718. NTSTATUS status = STATUS_SUCCESS;
  3719. PNSOBJ sib;
  3720. PNSOBJ dis;
  3721. context = (PDISABLE_LINK_NODES_CONTEXT)Context;
  3722. ASSERT(context);
  3723. //
  3724. // Entering this function twice with the same state
  3725. // means that we need to run the completion routine.
  3726. //
  3727. InterlockedIncrement(&context->RunCompletionHandler);
  3728. DisableLinkNodeStartState:
  3729. switch (context->State) {
  3730. case DisableStateInitial:
  3731. //
  3732. // Get the _HID of this device to see if
  3733. // it is a link node.
  3734. //
  3735. context->State = DisableStateGotHid;
  3736. status = ACPIGetNSPnpIDAsync(
  3737. context->RootDevice,
  3738. DisableLinkNodesAsyncWorker,
  3739. context,
  3740. &context->Hid,
  3741. NULL);
  3742. if (status == STATUS_PENDING) {
  3743. return status;
  3744. } else if (!NT_SUCCESS(status)) {
  3745. context->State = DisableStateGetChild;
  3746. goto DisableLinkNodeStartState;
  3747. }
  3748. //
  3749. // Fall through to next state.
  3750. //
  3751. case DisableStateGotHid:
  3752. context->State = DisableStateGetChild;
  3753. if (context->Hid) {
  3754. if (strstr(context->Hid, LINK_NODE_PNP_ID)) {
  3755. //
  3756. // We found a _HID of PNP0C0F, which is a
  3757. // link node. So disable it.
  3758. //
  3759. dis = ACPIAmliGetNamedChild(context->RootDevice,
  3760. PACKED_DIS);
  3761. if (dis) {
  3762. context->State = DisableStateRanDis;
  3763. status = AMLIAsyncEvalObject(dis,
  3764. NULL,
  3765. 0,
  3766. NULL,
  3767. DisableLinkNodesAsyncWorker,
  3768. (PVOID)context
  3769. );
  3770. if (status == STATUS_PENDING) {
  3771. return status;
  3772. } else if (NT_SUCCESS(status)) {
  3773. //
  3774. // We're done. Jump to the cleanup code.
  3775. //
  3776. break;
  3777. }
  3778. } else {
  3779. //
  3780. // Link nodes must be disablable.
  3781. //
  3782. KeBugCheckEx(ACPI_BIOS_ERROR,
  3783. ACPI_LINK_NODE_CANNOT_BE_DISABLED,
  3784. (ULONG_PTR)context->RootDevice,
  3785. 0,
  3786. 0);
  3787. }
  3788. }
  3789. }
  3790. case DisableStateGetChild:
  3791. //
  3792. // Recurse to all of the children. Propagate any errors,
  3793. // but don't stop for them.
  3794. //
  3795. context->Sibling = NSGETFIRSTCHILD(context->RootDevice);
  3796. if (!context->Sibling) {
  3797. status = STATUS_SUCCESS;
  3798. break;
  3799. }
  3800. context->State = DisableStateRecursing;
  3801. case DisableStateRecursing:
  3802. while (context->Sibling) {
  3803. //
  3804. // Cycle through all the children (child and its
  3805. // siblings)
  3806. //
  3807. sib = context->Sibling;
  3808. context->Sibling = NSGETNEXTSIBLING(context->Sibling);
  3809. switch (NSGETOBJTYPE(sib)) {
  3810. case OBJTYPE_DEVICE:
  3811. //
  3812. // This name child of Root is also a device.
  3813. // Recurse.
  3814. //
  3815. status = DisableLinkNodesAsync(sib,
  3816. DisableLinkNodesAsyncWorker,
  3817. (PVOID)context);
  3818. break;
  3819. default:
  3820. break;
  3821. }
  3822. if (status == STATUS_PENDING) {
  3823. return status;
  3824. }
  3825. }
  3826. case DisableStateRanDis:
  3827. break;
  3828. }
  3829. //
  3830. // Done. Clean up and return.
  3831. //
  3832. if (context->RunCompletionHandler) {
  3833. context->CompletionHandler(context->RootDevice,
  3834. status,
  3835. NULL,
  3836. context->CompletionContext
  3837. );
  3838. }
  3839. if (context->Hid) ExFreePool(context->Hid);
  3840. ExFreePool(context);
  3841. return status;
  3842. }
  3843. NTSTATUS
  3844. GetIsaVectorFlags(
  3845. IN ULONG Vector,
  3846. IN OUT UCHAR *Flags
  3847. )
  3848. {
  3849. ULONG i;
  3850. ULONG irq;
  3851. UCHAR flags;
  3852. NTSTATUS returnStatus = STATUS_NOT_FOUND;
  3853. NTSTATUS status;
  3854. PAGED_CODE();
  3855. for (i = 0; i < ISA_PIC_VECTORS; i++) {
  3856. status = LookupIsaVectorOverride(i,
  3857. &irq,
  3858. &flags);
  3859. if (NT_SUCCESS(status)) {
  3860. if (irq == Vector) {
  3861. //
  3862. // This vector's flags have been overriden.
  3863. //
  3864. *Flags = flags;
  3865. ASSERT((*Flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
  3866. returnStatus = STATUS_SUCCESS;
  3867. break;
  3868. }
  3869. }
  3870. }
  3871. return returnStatus;
  3872. }
  3873. NTSTATUS
  3874. LookupIsaVectorOverride(
  3875. IN ULONG IsaVector,
  3876. IN OUT ULONG *RedirectionVector OPTIONAL,
  3877. IN OUT UCHAR *Flags OPTIONAL
  3878. )
  3879. /*++
  3880. Routine Description:
  3881. This function looks to see if this vector has
  3882. been overridden in the MAPIC table.
  3883. Arguments:
  3884. IsaVector - ISA vector
  3885. RedirectionVector - vector that the ISA vector
  3886. will actually trigger
  3887. Flags - flags in the override table
  3888. Return Value:
  3889. STATUS_SUCCESS if the ISA vector exists in the
  3890. MAPIC table
  3891. STATUS_NOT_FOUND if it doesn't
  3892. --*/
  3893. {
  3894. PAPICTABLE ApicEntry;
  3895. PISA_VECTOR IsaEntry;
  3896. PUCHAR TraversePtr;
  3897. PMAPIC ApicTable;
  3898. USHORT entryFlags;
  3899. ULONG_PTR TableEnd;
  3900. PAGED_CODE();
  3901. if (InterruptModel == 0) {
  3902. //
  3903. // This machine is running in PIC mode, so
  3904. // we should ignore anything from an APIC table.
  3905. //
  3906. return STATUS_NOT_FOUND;
  3907. }
  3908. if (IsaVector >= ISA_PIC_VECTORS) {
  3909. //
  3910. // This vector was never an ISA vector.
  3911. //
  3912. return STATUS_NOT_FOUND;
  3913. }
  3914. //
  3915. // Walk the MAPIC table.
  3916. //
  3917. ApicTable = AcpiInformation->MultipleApicTable;
  3918. if (!ApicTable) {
  3919. //
  3920. // This machine didn't have an MAPIC table. So it
  3921. // must not be running in APIC mode. So there must
  3922. // not be any overrides.
  3923. //
  3924. return STATUS_NOT_FOUND;
  3925. }
  3926. TraversePtr = (PUCHAR)ApicTable->APICTables;
  3927. TableEnd = (ULONG_PTR)ApicTable +ApicTable->Header.Length;
  3928. while ((ULONG_PTR)TraversePtr < TableEnd) {
  3929. ApicEntry = (PAPICTABLE) TraversePtr;
  3930. if (ApicEntry->Type == ISA_VECTOR_OVERRIDE &&
  3931. ApicEntry->Length == ISA_VECTOR_OVERRIDE_LENGTH) {
  3932. //
  3933. // Found an ISA vector redirection entry.
  3934. //
  3935. IsaEntry = (PISA_VECTOR) TraversePtr;
  3936. if (IsaEntry->Source == IsaVector) {
  3937. if (RedirectionVector) {
  3938. *RedirectionVector = IsaEntry->GlobalSystemInterruptVector;
  3939. }
  3940. if (Flags) {
  3941. entryFlags = IsaEntry->Flags;
  3942. *Flags = 0;
  3943. if (((entryFlags & PO_BITS) == POLARITY_HIGH) ||
  3944. ((entryFlags & PO_BITS) == POLARITY_CONFORMS_WITH_BUS)) {
  3945. *Flags |= VECTOR_ACTIVE_HIGH;
  3946. } else {
  3947. *Flags |= VECTOR_ACTIVE_LOW;
  3948. }
  3949. if (((entryFlags & EL_BITS) == EL_EDGE_TRIGGERED) ||
  3950. ((entryFlags & EL_BITS) == EL_CONFORMS_WITH_BUS)) {
  3951. *Flags |= VECTOR_EDGE;
  3952. } else {
  3953. *Flags |= VECTOR_LEVEL;
  3954. }
  3955. }
  3956. return STATUS_SUCCESS;
  3957. }
  3958. }
  3959. //
  3960. // Sanity check to make sure that we abort tables with bogus length
  3961. // entries
  3962. //
  3963. if (ApicEntry->Length == 0) {
  3964. break;
  3965. }
  3966. TraversePtr += (ApicEntry->Length);
  3967. }
  3968. return STATUS_NOT_FOUND;
  3969. }