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.

495 lines
13 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. ixpciint.c
  5. Abstract:
  6. All PCI bus interrupt mapping is in this module, so that a real
  7. system which doesn't have all the limitations which PC PCI
  8. systems have can replaced this code easly.
  9. (bus memory & i/o address mappings can also be fix here)
  10. Author:
  11. Ken Reneris
  12. Environment:
  13. Kernel mode
  14. Revision History:
  15. --*/
  16. #include "halp.h"
  17. #include "pci.h"
  18. #include "pcip.h"
  19. #ifdef WANT_IRQ_ROUTING
  20. #include "ixpciir.h"
  21. #endif
  22. ULONG HalpEisaELCR;
  23. BOOLEAN HalpDoingCrashDump;
  24. BOOLEAN HalpPciLockSettings;
  25. #ifdef ALLOC_PRAGMA
  26. #pragma alloc_text(PAGE,HalpGetPCIIntOnISABus)
  27. #pragma alloc_text(PAGE,HalpAdjustPCIResourceList)
  28. #pragma alloc_text(PAGE,HalpGetISAFixedPCIIrq)
  29. #endif
  30. ULONG
  31. HalpGetPCIIntOnISABus (
  32. IN PBUS_HANDLER BusHandler,
  33. IN PBUS_HANDLER RootHandler,
  34. IN ULONG BusInterruptLevel,
  35. IN ULONG BusInterruptVector,
  36. OUT PKIRQL Irql,
  37. OUT PKAFFINITY Affinity
  38. )
  39. {
  40. if (BusInterruptLevel < 1) {
  41. // bogus bus level
  42. return 0;
  43. }
  44. //
  45. // Current PCI buses just map their IRQs ontop of the ISA space,
  46. // so foreward this to the isa handler for the isa vector
  47. // (the isa vector was saved away at either HalSetBusData or
  48. // IoAssignReosurces time - if someone is trying to connect a
  49. // PCI interrupt without performing one of those operations first,
  50. // they are broken).
  51. //
  52. return HalGetInterruptVector (
  53. #ifndef MCA
  54. Isa, 0,
  55. #else
  56. MicroChannel, 0,
  57. #endif
  58. BusInterruptLevel ^ IRQXOR,
  59. 0,
  60. Irql,
  61. Affinity
  62. );
  63. }
  64. VOID
  65. HalpPCIPin2ISALine (
  66. IN PBUS_HANDLER BusHandler,
  67. IN PBUS_HANDLER RootHandler,
  68. IN PCI_SLOT_NUMBER SlotNumber,
  69. IN PPCI_COMMON_CONFIG PciData
  70. )
  71. /*++
  72. This function maps the device's InterruptPin to an InterruptLine
  73. value.
  74. On the current PC implementations, the bios has already filled in
  75. InterruptLine as it's ISA value and there's no portable way to
  76. change it.
  77. On a DBG build we adjust InterruptLine just to ensure driver's
  78. don't connect to it without translating it on the PCI bus.
  79. --*/
  80. {
  81. if (!PciData->u.type0.InterruptPin) {
  82. return ;
  83. }
  84. //
  85. // On a PC there's no Slot/Pin/Line mapping which needs to
  86. // be done.
  87. //
  88. PciData->u.type0.InterruptLine ^= IRQXOR;
  89. }
  90. VOID
  91. HalpPCIISALine2Pin (
  92. IN PBUS_HANDLER BusHandler,
  93. IN PBUS_HANDLER RootHandler,
  94. IN PCI_SLOT_NUMBER SlotNumber,
  95. IN PPCI_COMMON_CONFIG PciNewData,
  96. IN PPCI_COMMON_CONFIG PciOldData
  97. )
  98. /*++
  99. This functions maps the device's InterruptLine to it's
  100. device specific InterruptPin value.
  101. On the current PC implementations, this information is
  102. fixed by the BIOS. Just make sure the value isn't being
  103. editted since PCI doesn't tell us how to dynically
  104. connect the interrupt.
  105. --*/
  106. {
  107. if (!PciNewData->u.type0.InterruptPin) {
  108. return ;
  109. }
  110. PciNewData->u.type0.InterruptLine ^= IRQXOR;
  111. #if DBG
  112. if (PciNewData->u.type0.InterruptLine != PciOldData->u.type0.InterruptLine ||
  113. PciNewData->u.type0.InterruptPin != PciOldData->u.type0.InterruptPin) {
  114. DbgPrint ("HalpPCILine2Pin: System does not support changing the PCI device interrupt routing\n");
  115. DbgBreakPoint ();
  116. }
  117. #endif
  118. }
  119. #if !defined(SUBCLASSPCI)
  120. VOID
  121. HalpPCIAcquireType2Lock (
  122. PKSPIN_LOCK SpinLock,
  123. PKIRQL Irql
  124. )
  125. {
  126. if (!HalpDoingCrashDump) {
  127. *Irql = KfRaiseIrql (HIGH_LEVEL);
  128. KiAcquireSpinLock (SpinLock);
  129. } else {
  130. *Irql = HIGH_LEVEL;
  131. }
  132. }
  133. VOID
  134. HalpPCIReleaseType2Lock (
  135. PKSPIN_LOCK SpinLock,
  136. KIRQL Irql
  137. )
  138. {
  139. if (!HalpDoingCrashDump) {
  140. KiReleaseSpinLock (SpinLock);
  141. KfLowerIrql (Irql);
  142. }
  143. }
  144. #endif
  145. NTSTATUS
  146. HalpAdjustPCIResourceList (
  147. IN PBUS_HANDLER BusHandler,
  148. IN PBUS_HANDLER RootHandler,
  149. IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList
  150. )
  151. /*++
  152. Rewrite the callers requested resource list to fit within
  153. the supported ranges of this bus
  154. --*/
  155. {
  156. NTSTATUS Status;
  157. PPCIPBUSDATA BusData;
  158. PCI_SLOT_NUMBER PciSlot;
  159. PSUPPORTED_RANGE Interrupt;
  160. PSUPPORTED_RANGE Range, HoldRange;
  161. PSUPPORTED_RANGES SupportedRanges;
  162. PPCI_COMMON_CONFIG PciData, PciOrigData;
  163. UCHAR buffer[PCI_COMMON_HDR_LENGTH];
  164. UCHAR buffer2[PCI_COMMON_HDR_LENGTH];
  165. BOOLEAN UseBusRanges;
  166. ULONG i, j, RomIndex, length, ebit;
  167. ULONG Base[PCI_TYPE0_ADDRESSES + 1];
  168. PULONG BaseAddress[PCI_TYPE0_ADDRESSES + 1];
  169. BusData = (PPCIPBUSDATA) BusHandler->BusData;
  170. PciSlot = *((PPCI_SLOT_NUMBER) &(*pResourceList)->SlotNumber);
  171. //
  172. // Determine PCI device's interrupt restrictions
  173. //
  174. Status = BusData->GetIrqRange(BusHandler, RootHandler, PciSlot, &Interrupt);
  175. if (!NT_SUCCESS(Status)) {
  176. return Status;
  177. }
  178. SupportedRanges = NULL;
  179. UseBusRanges = TRUE;
  180. Status = STATUS_INSUFFICIENT_RESOURCES;
  181. if (HalpPciLockSettings) {
  182. PciData = (PPCI_COMMON_CONFIG) buffer;
  183. PciOrigData = (PPCI_COMMON_CONFIG) buffer2;
  184. HalpReadPCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH);
  185. //
  186. // If this is a device, and it current has its decodes enabled,
  187. // then use the currently programmed ranges only
  188. //
  189. if (PCI_CONFIG_TYPE(PciData) == 0 &&
  190. (PciData->Command & (PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE))) {
  191. //
  192. // Save current settings
  193. //
  194. RtlMoveMemory (PciOrigData, PciData, PCI_COMMON_HDR_LENGTH);
  195. for (j=0; j < PCI_TYPE0_ADDRESSES; j++) {
  196. BaseAddress[j] = &PciData->u.type0.BaseAddresses[j];
  197. }
  198. BaseAddress[j] = &PciData->u.type0.ROMBaseAddress;
  199. RomIndex = j;
  200. //
  201. // Write all one-bits to determine lengths for each address
  202. //
  203. for (j=0; j < PCI_TYPE0_ADDRESSES + 1; j++) {
  204. Base[j] = *BaseAddress[j];
  205. *BaseAddress[j] = 0xFFFFFFFF;
  206. }
  207. PciData->Command &= ~(PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE);
  208. *BaseAddress[RomIndex] &= ~PCI_ROMADDRESS_ENABLED;
  209. HalpWritePCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH);
  210. HalpReadPCIConfig (BusHandler, PciSlot, PciData, 0, PCI_COMMON_HDR_LENGTH);
  211. //
  212. // restore original settings
  213. //
  214. HalpWritePCIConfig (
  215. BusHandler,
  216. PciSlot,
  217. &PciOrigData->Status,
  218. FIELD_OFFSET (PCI_COMMON_CONFIG, Status),
  219. PCI_COMMON_HDR_LENGTH - FIELD_OFFSET (PCI_COMMON_CONFIG, Status)
  220. );
  221. HalpWritePCIConfig (
  222. BusHandler,
  223. PciSlot,
  224. PciOrigData,
  225. 0,
  226. FIELD_OFFSET (PCI_COMMON_CONFIG, Status)
  227. );
  228. //
  229. // Build a memory & io range list of just the ranges already
  230. // programmed into the device
  231. //
  232. UseBusRanges = FALSE;
  233. SupportedRanges = HalpAllocateNewRangeList();
  234. if (!SupportedRanges) {
  235. goto CleanUp;
  236. }
  237. *BaseAddress[RomIndex] &= ~PCI_ADDRESS_IO_SPACE;
  238. for (j=0; j < PCI_TYPE0_ADDRESSES + 1; j++) {
  239. i = *BaseAddress[j];
  240. if (i & PCI_ADDRESS_IO_SPACE) {
  241. length = 1 << 2;
  242. Range = &SupportedRanges->IO;
  243. ebit = PCI_ENABLE_IO_SPACE;
  244. } else {
  245. length = 1 << 4;
  246. Range = &SupportedRanges->Memory;
  247. ebit = PCI_ENABLE_MEMORY_SPACE;
  248. if (i & PCI_ADDRESS_MEMORY_PREFETCHABLE) {
  249. Range = &SupportedRanges->PrefetchMemory;
  250. }
  251. }
  252. Base[j] &= ~(length-1);
  253. while (!(i & length) && length) {
  254. length <<= 1;
  255. }
  256. if (j == RomIndex &&
  257. !(PciOrigData->u.type0.ROMBaseAddress & PCI_ROMADDRESS_ENABLED)) {
  258. // range not enabled, don't use it
  259. length = 0;
  260. }
  261. if (length) {
  262. if (!(PciOrigData->Command & ebit)) {
  263. // range not enabled, don't use preprogrammed values
  264. UseBusRanges = TRUE;
  265. }
  266. if (Range->Limit >= Range->Base) {
  267. HoldRange = Range->Next;
  268. Range->Next = ExAllocatePoolWithTag(
  269. PagedPool,
  270. sizeof(SUPPORTED_RANGE),
  271. HAL_POOL_TAG
  272. );
  273. Range = Range->Next;
  274. if (!Range) {
  275. goto CleanUp;
  276. }
  277. Range->Next = HoldRange;
  278. }
  279. Range->Base = Base[j];
  280. Range->Limit = Base[j] + length - 1;
  281. }
  282. if (Is64BitBaseAddress(i)) {
  283. // skip upper half of 64 bit address since this processor
  284. // only supports 32 bits of address space
  285. j++;
  286. }
  287. }
  288. }
  289. }
  290. //
  291. // Adjust resources
  292. //
  293. Status = HaliAdjustResourceListRange (
  294. UseBusRanges ? BusHandler->BusAddresses : SupportedRanges,
  295. Interrupt,
  296. pResourceList
  297. );
  298. CleanUp:
  299. if (SupportedRanges) {
  300. HalpFreeRangeList (SupportedRanges);
  301. }
  302. ExFreePool (Interrupt);
  303. return Status;
  304. }
  305. NTSTATUS
  306. HalpGetISAFixedPCIIrq (
  307. IN PBUS_HANDLER BusHandler,
  308. IN PBUS_HANDLER RootHandler,
  309. IN PCI_SLOT_NUMBER PciSlot,
  310. OUT PSUPPORTED_RANGE *Interrupt
  311. )
  312. {
  313. UCHAR buffer[PCI_COMMON_HDR_LENGTH];
  314. PPCI_COMMON_CONFIG PciData;
  315. PciData = (PPCI_COMMON_CONFIG) buffer;
  316. HalGetBusData (
  317. PCIConfiguration,
  318. BusHandler->BusNumber,
  319. PciSlot.u.AsULONG,
  320. PciData,
  321. PCI_COMMON_HDR_LENGTH
  322. );
  323. if (PciData->VendorID == PCI_INVALID_VENDORID) {
  324. return STATUS_UNSUCCESSFUL;
  325. }
  326. *Interrupt = ExAllocatePoolWithTag(PagedPool,
  327. sizeof(SUPPORTED_RANGE),
  328. HAL_POOL_TAG);
  329. if (!*Interrupt) {
  330. return STATUS_INSUFFICIENT_RESOURCES;
  331. }
  332. RtlZeroMemory (*Interrupt, sizeof (SUPPORTED_RANGE));
  333. (*Interrupt)->Base = 1; // base = 1, limit = 0
  334. if (!PciData->u.type0.InterruptPin) {
  335. return STATUS_SUCCESS;
  336. }
  337. #ifdef WANT_IRQ_ROUTING
  338. //
  339. // Let the arbiter decide which Irq this device gets.
  340. //
  341. if (IsPciIrqRoutingEnabled()) {
  342. //
  343. // If a video card has been enabled by the BIOS
  344. // and the BIOS did not assign any interrupt to it
  345. // then assume this device does not need an interrupt.
  346. //
  347. if (PciData->Command & (PCI_ENABLE_IO_SPACE | PCI_ENABLE_MEMORY_SPACE)) {
  348. if ( (PciData->BaseClass == PCI_CLASS_PRE_20 && PciData->SubClass == PCI_SUBCLASS_VID_XGA_CTLR) ||
  349. (PciData->BaseClass == PCI_CLASS_DISPLAY_CTLR &&
  350. (PciData->SubClass == PCI_SUBCLASS_VID_VGA_CTLR || PciData->SubClass == PCI_SUBCLASS_VID_XGA_CTLR))) {
  351. if ( PciData->u.type0.InterruptLine == (0 ^ IRQXOR) ||
  352. PciData->u.type0.InterruptLine == (0xFF ^ IRQXOR)) {
  353. #if DBG
  354. DbgPrint ("HalpGetValidPCIFixedIrq: BIOS did not assign an interrupt to the video device %04X%04X\n", PciData->VendorID, PciData->DeviceID);
  355. #endif
  356. //
  357. // We need to let the caller continue, since the caller may
  358. // not care that the interrupt vector is connected or not
  359. //
  360. return STATUS_SUCCESS;
  361. }
  362. }
  363. }
  364. //
  365. // Return all possible interrupts since Pci Irq Routing is enabled.
  366. //
  367. (*Interrupt)->Base = 0;
  368. (*Interrupt)->Limit = 0xFF;
  369. return STATUS_SUCCESS;
  370. }
  371. #endif
  372. if (PciData->u.type0.InterruptLine == (0 ^ IRQXOR) ||
  373. PciData->u.type0.InterruptLine == (0xFF ^ IRQXOR)) {
  374. #if DBG
  375. DbgPrint ("HalpGetValidPCIFixedIrq: BIOS did not assign an interrupt vector for the device\n");
  376. #endif
  377. //
  378. // We need to let the caller continue, since the caller may
  379. // not care that the interrupt vector is connected or not
  380. //
  381. return STATUS_SUCCESS;
  382. }
  383. (*Interrupt)->Base = PciData->u.type0.InterruptLine;
  384. (*Interrupt)->Limit = PciData->u.type0.InterruptLine;
  385. return STATUS_SUCCESS;
  386. }