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.

777 lines
20 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. intobj.c
  5. Abstract:
  6. This module implements the kernel interrupt object. Functions are provided
  7. to initialize, connect, and disconnect interrupt objects.
  8. Author:
  9. David N. Cutler (davec) 30-Jul-1989
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. 23-Jan-1990 shielint
  14. Modified for NT386 interrupt manager
  15. --*/
  16. #include "ki.h"
  17. //
  18. // Externs from trap.asm used to compute and set handlers for unexpected
  19. // hardware interrupts.
  20. //
  21. extern ULONG KiStartUnexpectedRange(VOID);
  22. extern ULONG KiEndUnexpectedRange(VOID);
  23. extern ULONG KiUnexpectedEntrySize;
  24. VOID
  25. KiInterruptDispatch2ndLvl(
  26. VOID
  27. );
  28. VOID
  29. KiChainedDispatch2ndLvl(
  30. VOID
  31. );
  32. typedef enum {
  33. NoConnect,
  34. NormalConnect,
  35. ChainConnect,
  36. UnkownConnect
  37. } CONNECT_TYPE, *PCONNECT_TYPE;
  38. typedef struct {
  39. CONNECT_TYPE Type;
  40. PKINTERRUPT Interrupt;
  41. PKINTERRUPT_ROUTINE NoDispatch;
  42. PKINTERRUPT_ROUTINE InterruptDispatch;
  43. PKINTERRUPT_ROUTINE FloatingDispatch;
  44. PKINTERRUPT_ROUTINE ChainedDispatch;
  45. PKINTERRUPT_ROUTINE *FlatDispatch;
  46. } DISPATCH_INFO, *PDISPATCH_INFO;
  47. VOID
  48. KiGetVectorInfo (
  49. IN ULONG Vector,
  50. OUT PDISPATCH_INFO DispatchInfo
  51. );
  52. VOID
  53. KiConnectVectorAndInterruptObject (
  54. IN PKINTERRUPT Interrupt,
  55. IN CONNECT_TYPE Type
  56. );
  57. VOID
  58. KeInitializeInterrupt (
  59. IN PKINTERRUPT Interrupt,
  60. IN PKSERVICE_ROUTINE ServiceRoutine,
  61. IN PVOID ServiceContext,
  62. IN PKSPIN_LOCK SpinLock OPTIONAL,
  63. IN ULONG Vector,
  64. IN KIRQL Irql,
  65. IN KIRQL SynchronizeIrql,
  66. IN KINTERRUPT_MODE InterruptMode,
  67. IN BOOLEAN ShareVector,
  68. IN CCHAR ProcessorNumber,
  69. IN BOOLEAN FloatingSave
  70. )
  71. /*++
  72. Routine Description:
  73. This function initializes a kernel interrupt object. The service routine,
  74. service context, spin lock, vector, IRQL, SynchronizeIrql, and floating
  75. context save flag are initialized.
  76. Arguments:
  77. Interrupt - Supplies a pointer to a control object of type interrupt.
  78. ServiceRoutine - Supplies a pointer to a function that is to be
  79. executed when an interrupt occurs via the specified interrupt
  80. vector.
  81. ServiceContext - Supplies a pointer to an arbitrary data structure which is
  82. to be passed to the function specified by the ServiceRoutine parameter.
  83. SpinLock - Supplies a pointer to an executive spin lock.
  84. Vector - Supplies the index of the entry in the Interrupt Dispatch Table
  85. that is to be associated with the ServiceRoutine function.
  86. Irql - Supplies the request priority of the interrupting source.
  87. SynchronizeIrql - The request priority that the interrupt should be
  88. synchronized with.
  89. InterruptMode - Supplies the mode of the interrupt; LevelSensitive or
  90. ShareVector - Supplies a boolean value that specifies whether the
  91. vector can be shared with other interrupt objects or not. If FALSE
  92. then the vector may not be shared, if TRUE it may be.
  93. Latched.
  94. ProcessorNumber - Supplies the number of the processor to which the
  95. interrupt will be connected.
  96. FloatingSave - Supplies a boolean value that determines whether the
  97. floating point registers and pipe line are to be saved before calling
  98. the ServiceRoutine function.
  99. Return Value:
  100. None.
  101. --*/
  102. {
  103. LONG Index;
  104. PULONG pl;
  105. PULONG NormalDispatchCode;
  106. //
  107. // Initialize standard control object header.
  108. //
  109. Interrupt->Type = InterruptObject;
  110. Interrupt->Size = sizeof(KINTERRUPT);
  111. //
  112. // Initialize the address of the service routine,
  113. // the service context, the address of the spin lock, the vector
  114. // number, the IRQL of the interrupting source, the Irql used for
  115. // synchronize execution, the interrupt mode, the processor
  116. // number, and the floating context save flag.
  117. //
  118. Interrupt->ServiceRoutine = ServiceRoutine;
  119. Interrupt->ServiceContext = ServiceContext;
  120. if (ARGUMENT_PRESENT(SpinLock)) {
  121. Interrupt->ActualLock = SpinLock;
  122. } else {
  123. KeInitializeSpinLock (&Interrupt->SpinLock);
  124. Interrupt->ActualLock = &Interrupt->SpinLock;
  125. }
  126. Interrupt->Vector = Vector;
  127. Interrupt->Irql = Irql;
  128. Interrupt->SynchronizeIrql = SynchronizeIrql;
  129. Interrupt->Mode = InterruptMode;
  130. Interrupt->ShareVector = ShareVector;
  131. Interrupt->Number = ProcessorNumber;
  132. Interrupt->FloatingSave = FloatingSave;
  133. //
  134. // Initialize fields for the interrupt storm detection. Set these
  135. // to -1 so that the first time through the interrupt dispatch they
  136. // will be reset correctly.
  137. //
  138. Interrupt->TickCount = (ULONG)-1;
  139. Interrupt->DispatchCount = (ULONG)-1;
  140. //
  141. // Copy the interrupt dispatch code template into the interrupt object
  142. // and edit the machine code stored in the structure (please see
  143. // _KiInterruptTemplate in intsup.asm.) Finally, flush the dcache
  144. // on all processors that the current thread can
  145. // run on to ensure that the code is actually in memory.
  146. //
  147. NormalDispatchCode = &(Interrupt->DispatchCode[0]);
  148. pl = NormalDispatchCode;
  149. for (Index = 0; Index < NORMAL_DISPATCH_LENGTH; Index += 1) {
  150. *NormalDispatchCode++ = KiInterruptTemplate[Index];
  151. }
  152. //
  153. // The following two instructions set the address of current interrupt
  154. // object the the NORMAL dispatching code.
  155. //
  156. pl = (PULONG)((PUCHAR)pl + ((PUCHAR)&KiInterruptTemplateObject -
  157. (PUCHAR)KiInterruptTemplate) -4);
  158. *pl = (ULONG)Interrupt;
  159. KeSweepDcache(FALSE);
  160. //
  161. // Set the connected state of the interrupt object to FALSE.
  162. //
  163. Interrupt->Connected = FALSE;
  164. return;
  165. }
  166. BOOLEAN
  167. KeConnectInterrupt (
  168. IN PKINTERRUPT Interrupt
  169. )
  170. /*++
  171. Routine Description:
  172. This function connects an interrupt object to the interrupt vector
  173. specified by the interrupt object. If the interrupt object is already
  174. connected, or an attempt is made to connect to an interrupt that cannot
  175. be connected, then a value of FALSE is returned. Else the specified
  176. interrupt object is connected to the interrupt vector, the connected
  177. state is set to TRUE, and TRUE is returned as the function value.
  178. Arguments:
  179. Interrupt - Supplies a pointer to a control object of type interrupt.
  180. Return Value:
  181. If the interrupt object is already connected or an attempt is made to
  182. connect to an interrupt vector that cannot be connected, then a value
  183. of FALSE is returned. Else a value of TRUE is returned.
  184. --*/
  185. {
  186. DISPATCH_INFO DispatchInfo;
  187. BOOLEAN Connected;
  188. BOOLEAN ConnectError;
  189. BOOLEAN Enabled;
  190. KIRQL Irql;
  191. CCHAR Number;
  192. KIRQL OldIrql;
  193. ULONG Vector;
  194. //
  195. // If the interrupt object is already connected, the interrupt vector
  196. // number is invalid, an attempt is being made to connect to a vector
  197. // that cannot be connected, the interrupt request level is invalid, or
  198. // the processor number is invalid, then do not connect the interrupt
  199. // object. Else connect interrupt object to the specified vector and
  200. // establish the proper interrupt dispatcher.
  201. //
  202. Connected = FALSE;
  203. ConnectError = FALSE;
  204. Irql = Interrupt->Irql;
  205. Number = Interrupt->Number;
  206. Vector = Interrupt->Vector;
  207. if ( !((Irql > HIGH_LEVEL) ||
  208. (Number >= KeNumberProcessors) ||
  209. (Interrupt->SynchronizeIrql < Irql) ||
  210. (Interrupt->FloatingSave) // R0 x87 usage not supported on x86
  211. )
  212. ) {
  213. //
  214. //
  215. // Set system affinity to the specified processor.
  216. //
  217. KeSetSystemAffinityThread((KAFFINITY)(1<<Number));
  218. //
  219. // Raise IRQL to dispatcher level and lock dispatcher database.
  220. //
  221. KiLockDispatcherDatabase(&OldIrql);
  222. //
  223. // Is interrupt object already connected?
  224. //
  225. if (!Interrupt->Connected) {
  226. //
  227. // Determine interrupt dispatch vector
  228. //
  229. KiGetVectorInfo (
  230. Vector,
  231. &DispatchInfo
  232. );
  233. //
  234. // If dispatch vector is not connected, then connect it
  235. //
  236. if (DispatchInfo.Type == NoConnect) {
  237. Connected = TRUE;
  238. Interrupt->Connected = TRUE;
  239. //
  240. // Connect interrupt dispatch to interrupt object dispatch code
  241. //
  242. InitializeListHead(&Interrupt->InterruptListEntry);
  243. KiConnectVectorAndInterruptObject (Interrupt, NormalConnect);
  244. //
  245. // Enabled system vector
  246. //
  247. Enabled = HalEnableSystemInterrupt(Vector, Irql, Interrupt->Mode);
  248. if (!Enabled) {
  249. ConnectError = TRUE;
  250. }
  251. } else if (DispatchInfo.Type != UnkownConnect &&
  252. Interrupt->ShareVector &&
  253. DispatchInfo.Interrupt->ShareVector &&
  254. DispatchInfo.Interrupt->Mode == Interrupt->Mode) {
  255. //
  256. // Vector is already connected as sharable. New vector is sharable
  257. // and modes match. Chain new vector.
  258. //
  259. Connected = TRUE;
  260. Interrupt->Connected = TRUE;
  261. ASSERT (Irql <= SYNCH_LEVEL);
  262. //
  263. // If not already using chained dispatch handler, set it up
  264. //
  265. if (DispatchInfo.Type != ChainConnect) {
  266. KiConnectVectorAndInterruptObject (DispatchInfo.Interrupt, ChainConnect);
  267. }
  268. //
  269. // Add to tail of chained dispatch
  270. //
  271. InsertTailList(
  272. &DispatchInfo.Interrupt->InterruptListEntry,
  273. &Interrupt->InterruptListEntry
  274. );
  275. }
  276. }
  277. //
  278. // Unlock dispatcher database and lower IRQL to its previous value.
  279. //
  280. KiUnlockDispatcherDatabase(OldIrql);
  281. //
  282. // Set system affinity back to the original value.
  283. //
  284. KeRevertToUserAffinityThread();
  285. }
  286. if (Connected && ConnectError) {
  287. #if DBG
  288. DbgPrint ("HalEnableSystemInterrupt failed\n");
  289. #endif
  290. KeDisconnectInterrupt (Interrupt);
  291. Connected = FALSE;
  292. }
  293. //
  294. // Return whether interrupt was connected to the specified vector.
  295. //
  296. return Connected;
  297. }
  298. BOOLEAN
  299. KeDisconnectInterrupt (
  300. IN PKINTERRUPT Interrupt
  301. )
  302. /*++
  303. Routine Description:
  304. This function disconnects an interrupt object from the interrupt vector
  305. specified by the interrupt object. If the interrupt object is not
  306. connected, then a value of FALSE is returned. Else the specified interrupt
  307. object is disconnected from the interrupt vector, the connected state is
  308. set to FALSE, and TRUE is returned as the function value.
  309. Arguments:
  310. Interrupt - Supplies a pointer to a control object of type interrupt.
  311. Return Value:
  312. If the interrupt object is not connected, then a value of FALSE is
  313. returned. Else a value of TRUE is returned.
  314. --*/
  315. {
  316. DISPATCH_INFO DispatchInfo;
  317. BOOLEAN Connected;
  318. PKINTERRUPT Interrupty;
  319. KIRQL Irql;
  320. KIRQL OldIrql;
  321. ULONG Vector;
  322. //
  323. // Set system affinity to the specified processor.
  324. //
  325. KeSetSystemAffinityThread((KAFFINITY)(1<<Interrupt->Number));
  326. //
  327. // Raise IRQL to dispatcher level and lock dispatcher database.
  328. //
  329. KiLockDispatcherDatabase(&OldIrql);
  330. //
  331. // If the interrupt object is connected, then disconnect it from the
  332. // specified vector.
  333. //
  334. Connected = Interrupt->Connected;
  335. if (Connected) {
  336. Irql = Interrupt->Irql;
  337. Vector = Interrupt->Vector;
  338. //
  339. // If the specified interrupt vector is not connected to the chained
  340. // interrupt dispatcher, then disconnect it by setting its dispatch
  341. // address to the unexpected interrupt routine. Else remove the
  342. // interrupt object from the interrupt chain. If there is only
  343. // one entry remaining in the list, then reestablish the dispatch
  344. // address.
  345. //
  346. //
  347. // Determine interrupt dispatch vector
  348. //
  349. KiGetVectorInfo (
  350. Vector,
  351. &DispatchInfo
  352. );
  353. //
  354. // Is dispatch a chained handler?
  355. //
  356. if (DispatchInfo.Type == ChainConnect) {
  357. ASSERT (Irql <= SYNCH_LEVEL);
  358. //
  359. // Is interrupt being removed from head?
  360. //
  361. if (Interrupt == DispatchInfo.Interrupt) {
  362. //
  363. // Update next interrupt object to be head
  364. //
  365. DispatchInfo.Interrupt = CONTAINING_RECORD(
  366. DispatchInfo.Interrupt->InterruptListEntry.Flink,
  367. KINTERRUPT,
  368. InterruptListEntry
  369. );
  370. KiConnectVectorAndInterruptObject (DispatchInfo.Interrupt, ChainConnect);
  371. }
  372. //
  373. // Remove interrupt object
  374. //
  375. RemoveEntryList(&Interrupt->InterruptListEntry);
  376. //
  377. // If there's only one interrupt object left on this vector,
  378. // determine proper interrupt dispatcher
  379. //
  380. Interrupty = CONTAINING_RECORD(
  381. DispatchInfo.Interrupt->InterruptListEntry.Flink,
  382. KINTERRUPT,
  383. InterruptListEntry
  384. );
  385. if (DispatchInfo.Interrupt == Interrupty) {
  386. KiConnectVectorAndInterruptObject (Interrupty, NormalConnect);
  387. }
  388. } else {
  389. //
  390. // Removing last interrupt object from the vector. Disable the
  391. // vector, and set it to unconnected
  392. //
  393. HalDisableSystemInterrupt(Interrupt->Vector, Irql);
  394. KiConnectVectorAndInterruptObject (Interrupt, NoConnect);
  395. }
  396. KeSweepIcache(TRUE);
  397. Interrupt->Connected = FALSE;
  398. }
  399. //
  400. // Unlock dispatcher database and lower IRQL to its previous value.
  401. //
  402. KiUnlockDispatcherDatabase(OldIrql);
  403. //
  404. // Set system affinity back to the original value.
  405. //
  406. KeRevertToUserAffinityThread();
  407. //
  408. // Return whether interrupt was disconnected from the specified vector.
  409. //
  410. return Connected;
  411. }
  412. VOID
  413. KiGetVectorInfo (
  414. IN ULONG Vector,
  415. OUT PDISPATCH_INFO DispatchInfo
  416. )
  417. {
  418. PKINTERRUPT_ROUTINE Dispatch;
  419. ULONG CurrentDispatch;
  420. ULONG DispatchType;
  421. UCHAR IDTEntry;
  422. //
  423. // Get second level dispatch point
  424. //
  425. DispatchType = HalSystemVectorDispatchEntry (
  426. Vector,
  427. &DispatchInfo->FlatDispatch,
  428. &DispatchInfo->NoDispatch
  429. );
  430. //
  431. // Get vector info
  432. //
  433. switch (DispatchType) {
  434. case 0:
  435. //
  436. // Primary dispatch
  437. //
  438. IDTEntry = HalVectorToIDTEntry(Vector);
  439. DispatchInfo->NoDispatch = (PKINTERRUPT_ROUTINE) (((ULONG) &KiStartUnexpectedRange) +
  440. (IDTEntry - PRIMARY_VECTOR_BASE) * KiUnexpectedEntrySize);
  441. DispatchInfo->InterruptDispatch = KiInterruptDispatch;
  442. DispatchInfo->FloatingDispatch = KiFloatingDispatch;
  443. DispatchInfo->ChainedDispatch = KiChainedDispatch;
  444. DispatchInfo->FlatDispatch = NULL;
  445. CurrentDispatch = (ULONG) KiReturnHandlerAddressFromIDT(Vector);
  446. DispatchInfo->Interrupt = CONTAINING_RECORD (
  447. CurrentDispatch,
  448. KINTERRUPT,
  449. DispatchCode
  450. );
  451. break;
  452. case 1:
  453. //
  454. // Secondardy dispatch.
  455. //
  456. DispatchInfo->InterruptDispatch = KiInterruptDispatch2ndLvl;
  457. DispatchInfo->FloatingDispatch = KiInterruptDispatch2ndLvl;
  458. DispatchInfo->ChainedDispatch = KiChainedDispatch2ndLvl;
  459. CurrentDispatch = (ULONG) *DispatchInfo->FlatDispatch;
  460. DispatchInfo->Interrupt = (PKINTERRUPT) ( (PUCHAR) CurrentDispatch -
  461. (PUCHAR) KiInterruptTemplate +
  462. (PUCHAR) &KiInterruptTemplate2ndDispatch
  463. );
  464. break;
  465. default:
  466. // Other values reserved
  467. KeBugCheck (MISMATCHED_HAL);
  468. }
  469. //
  470. // Determine dispatch type
  471. //
  472. if (((PKINTERRUPT_ROUTINE) CurrentDispatch) == DispatchInfo->NoDispatch) {
  473. //
  474. // Is connected to the NoDispatch function
  475. //
  476. DispatchInfo->Type = NoConnect;
  477. } else {
  478. Dispatch = DispatchInfo->Interrupt->DispatchAddress;
  479. if (Dispatch == DispatchInfo->ChainedDispatch) {
  480. //
  481. // Is connected to the chained handler
  482. //
  483. DispatchInfo->Type = ChainConnect;
  484. } else if (Dispatch == DispatchInfo->InterruptDispatch ||
  485. Dispatch == DispatchInfo->FloatingDispatch) {
  486. //
  487. // If connection to the non-chained handler
  488. //
  489. DispatchInfo->Type = NormalConnect;
  490. } else {
  491. //
  492. // Unkown connection
  493. //
  494. DispatchInfo->Type = UnkownConnect;
  495. #if DBG
  496. DbgPrint ("KiGetVectorInfo not understood\n");
  497. #endif
  498. }
  499. }
  500. }
  501. VOID
  502. KiConnectVectorAndInterruptObject (
  503. IN PKINTERRUPT Interrupt,
  504. IN CONNECT_TYPE Type
  505. )
  506. {
  507. PKINTERRUPT_ROUTINE DispatchAddress;
  508. DISPATCH_INFO DispatchInfo;
  509. PULONG pl;
  510. //
  511. // Get current connect info
  512. //
  513. KiGetVectorInfo (
  514. Interrupt->Vector,
  515. &DispatchInfo
  516. );
  517. //
  518. // If disconnecting, set vector to NoDispatch
  519. //
  520. if (Type == NoConnect) {
  521. DispatchAddress = DispatchInfo.NoDispatch;
  522. } else {
  523. //
  524. // Set interrupt objects dispatch for new type
  525. //
  526. DispatchAddress = DispatchInfo.ChainedDispatch;
  527. if (Type == NormalConnect) {
  528. DispatchAddress = DispatchInfo.InterruptDispatch;
  529. if (Interrupt->FloatingSave) {
  530. DispatchAddress = DispatchInfo.FloatingDispatch;
  531. }
  532. }
  533. Interrupt->DispatchAddress = DispatchAddress;
  534. //
  535. // Set interrupt objects dispatch code to kernel dispatcher
  536. //
  537. pl = &(Interrupt->DispatchCode[0]);
  538. pl = (PULONG)((PUCHAR)pl +
  539. ((PUCHAR)&KiInterruptTemplateDispatch -
  540. (PUCHAR)KiInterruptTemplate) -4);
  541. *pl = (ULONG)DispatchAddress-(ULONG)((PUCHAR)pl+4);
  542. //
  543. // Set dispatch vector to proper address dispatch code location
  544. //
  545. if (DispatchInfo.FlatDispatch) {
  546. //
  547. // Connect to flat dispatch
  548. //
  549. DispatchAddress = (PKINTERRUPT_ROUTINE)
  550. ((PUCHAR) &(Interrupt->DispatchCode[0]) +
  551. ((PUCHAR) &KiInterruptTemplate2ndDispatch -
  552. (PUCHAR) KiInterruptTemplate));
  553. } else {
  554. //
  555. // Connect to enter_all dispatch
  556. //
  557. DispatchAddress = (PKINTERRUPT_ROUTINE) &Interrupt->DispatchCode;
  558. }
  559. }
  560. if (DispatchInfo.FlatDispatch) {
  561. //
  562. // Connect to flat dispatch
  563. //
  564. *DispatchInfo.FlatDispatch = DispatchAddress;
  565. } else {
  566. //
  567. // Connect to IDT
  568. //
  569. KiSetHandlerAddressToIDT (Interrupt->Vector, DispatchAddress);
  570. }
  571. }