Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1219 lines
32 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. // Data used for interrupt timing, aka, long ISR trapping.
  19. // The initial value for KiIsrTscLimit is to prevent the trap from
  20. // firing until a reasonable value is determined.
  21. //
  22. ULONGLONG KiIsrTscLimit = 0xFFFFFFFFFFFFFFFF;
  23. ULONG KiTimeLimitDpcMicroseconds;
  24. ULONG KiTimeLimitIsrMicroseconds;
  25. //
  26. // Externs from trap.asm used to compute and set handlers for unexpected
  27. // hardware interrupts.
  28. //
  29. extern ULONG KiStartUnexpectedRange(VOID);
  30. extern ULONG KiEndUnexpectedRange(VOID);
  31. extern ULONG KiUnexpectedEntrySize;
  32. VOID
  33. KiInterruptDispatch2ndLvl(
  34. VOID
  35. );
  36. VOID
  37. KiChainedDispatch2ndLvl(
  38. VOID
  39. );
  40. typedef enum {
  41. NoConnect,
  42. NormalConnect,
  43. ChainConnect,
  44. UnkownConnect
  45. } CONNECT_TYPE, *PCONNECT_TYPE;
  46. typedef struct {
  47. CONNECT_TYPE Type;
  48. PKINTERRUPT Interrupt;
  49. PKINTERRUPT_ROUTINE NoDispatch;
  50. PKINTERRUPT_ROUTINE InterruptDispatch;
  51. PKINTERRUPT_ROUTINE FloatingDispatch;
  52. PKINTERRUPT_ROUTINE ChainedDispatch;
  53. PKINTERRUPT_ROUTINE *FlatDispatch;
  54. } DISPATCH_INFO, *PDISPATCH_INFO;
  55. VOID
  56. KiGetVectorInfo (
  57. IN ULONG Vector,
  58. OUT PDISPATCH_INFO DispatchInfo
  59. );
  60. VOID
  61. KiConnectVectorAndInterruptObject (
  62. IN PKINTERRUPT Interrupt,
  63. IN CONNECT_TYPE Type
  64. );
  65. VOID
  66. KeInitializeInterrupt (
  67. IN PKINTERRUPT Interrupt,
  68. IN PKSERVICE_ROUTINE ServiceRoutine,
  69. IN PVOID ServiceContext,
  70. IN PKSPIN_LOCK SpinLock OPTIONAL,
  71. IN ULONG Vector,
  72. IN KIRQL Irql,
  73. IN KIRQL SynchronizeIrql,
  74. IN KINTERRUPT_MODE InterruptMode,
  75. IN BOOLEAN ShareVector,
  76. IN CCHAR ProcessorNumber,
  77. IN BOOLEAN FloatingSave
  78. )
  79. /*++
  80. Routine Description:
  81. This function initializes a kernel interrupt object. The service routine,
  82. service context, spin lock, vector, IRQL, SynchronizeIrql, and floating
  83. context save flag are initialized.
  84. Arguments:
  85. Interrupt - Supplies a pointer to a control object of type interrupt.
  86. ServiceRoutine - Supplies a pointer to a function that is to be
  87. executed when an interrupt occurs via the specified interrupt
  88. vector.
  89. ServiceContext - Supplies a pointer to an arbitrary data structure which is
  90. to be passed to the function specified by the ServiceRoutine parameter.
  91. SpinLock - Supplies a pointer to an executive spin lock.
  92. Vector - Supplies the index of the entry in the Interrupt Dispatch Table
  93. that is to be associated with the ServiceRoutine function.
  94. Irql - Supplies the request priority of the interrupting source.
  95. SynchronizeIrql - The request priority that the interrupt should be
  96. synchronized with.
  97. InterruptMode - Supplies the mode of the interrupt; LevelSensitive or
  98. ShareVector - Supplies a boolean value that specifies whether the
  99. vector can be shared with other interrupt objects or not. If FALSE
  100. then the vector may not be shared, if TRUE it may be.
  101. Latched.
  102. ProcessorNumber - Supplies the number of the processor to which the
  103. interrupt will be connected.
  104. FloatingSave - Supplies a boolean value that determines whether the
  105. floating point registers and pipe line are to be saved before calling
  106. the ServiceRoutine function.
  107. Return Value:
  108. None.
  109. --*/
  110. {
  111. LONG Index;
  112. PULONG pl;
  113. PULONG NormalDispatchCode;
  114. //
  115. // Initialize standard control object header.
  116. //
  117. Interrupt->Type = InterruptObject;
  118. Interrupt->Size = sizeof(KINTERRUPT);
  119. //
  120. // Initialize the address of the service routine,
  121. // the service context, the address of the spin lock, the vector
  122. // number, the IRQL of the interrupting source, the Irql used for
  123. // synchronize execution, the interrupt mode, the processor
  124. // number, and the floating context save flag.
  125. //
  126. Interrupt->ServiceRoutine = ServiceRoutine;
  127. Interrupt->ServiceContext = ServiceContext;
  128. if (ARGUMENT_PRESENT(SpinLock)) {
  129. Interrupt->ActualLock = SpinLock;
  130. } else {
  131. KeInitializeSpinLock (&Interrupt->SpinLock);
  132. Interrupt->ActualLock = &Interrupt->SpinLock;
  133. }
  134. Interrupt->Vector = Vector;
  135. Interrupt->Irql = Irql;
  136. Interrupt->SynchronizeIrql = SynchronizeIrql;
  137. Interrupt->Mode = InterruptMode;
  138. Interrupt->ShareVector = ShareVector;
  139. Interrupt->Number = ProcessorNumber;
  140. Interrupt->FloatingSave = FloatingSave;
  141. //
  142. // Initialize fields for the interrupt storm detection. Set these
  143. // to -1 so that the first time through the interrupt dispatch they
  144. // will be reset correctly.
  145. //
  146. Interrupt->TickCount = (ULONG)-1;
  147. Interrupt->DispatchCount = (ULONG)-1;
  148. //
  149. // Copy the interrupt dispatch code template into the interrupt object
  150. // and edit the machine code stored in the structure (please see
  151. // _KiInterruptTemplate in intsup.asm.) Finally, flush the dcache
  152. // on all processors that the current thread can
  153. // run on to ensure that the code is actually in memory.
  154. //
  155. NormalDispatchCode = &(Interrupt->DispatchCode[0]);
  156. pl = NormalDispatchCode;
  157. for (Index = 0; Index < NORMAL_DISPATCH_LENGTH; Index += 1) {
  158. *NormalDispatchCode++ = KiInterruptTemplate[Index];
  159. }
  160. //
  161. // The following two instructions set the address of current interrupt
  162. // object the the NORMAL dispatching code.
  163. //
  164. pl = (PULONG)((PUCHAR)pl + ((PUCHAR)&KiInterruptTemplateObject -
  165. (PUCHAR)KiInterruptTemplate) -4);
  166. *pl = (ULONG)Interrupt;
  167. KeSweepDcache(FALSE);
  168. //
  169. // Set the connected state of the interrupt object to FALSE.
  170. //
  171. Interrupt->Connected = FALSE;
  172. return;
  173. }
  174. BOOLEAN
  175. KeConnectInterrupt (
  176. IN PKINTERRUPT Interrupt
  177. )
  178. /*++
  179. Routine Description:
  180. This function connects an interrupt object to the interrupt vector
  181. specified by the interrupt object. If the interrupt object is already
  182. connected, or an attempt is made to connect to an interrupt that cannot
  183. be connected, then a value of FALSE is returned. Else the specified
  184. interrupt object is connected to the interrupt vector, the connected
  185. state is set to TRUE, and TRUE is returned as the function value.
  186. Arguments:
  187. Interrupt - Supplies a pointer to a control object of type interrupt.
  188. Return Value:
  189. If the interrupt object is already connected or an attempt is made to
  190. connect to an interrupt vector that cannot be connected, then a value
  191. of FALSE is returned. Else a value of TRUE is returned.
  192. --*/
  193. {
  194. DISPATCH_INFO DispatchInfo;
  195. BOOLEAN Connected;
  196. BOOLEAN ConnectError;
  197. BOOLEAN Enabled;
  198. KIRQL Irql;
  199. CCHAR Number;
  200. KIRQL OldIrql;
  201. ULONG Vector;
  202. //
  203. // If the interrupt object is already connected, the interrupt vector
  204. // number is invalid, an attempt is being made to connect to a vector
  205. // that cannot be connected, the interrupt request level is invalid, or
  206. // the processor number is invalid, then do not connect the interrupt
  207. // object. Else connect interrupt object to the specified vector and
  208. // establish the proper interrupt dispatcher.
  209. //
  210. Connected = FALSE;
  211. ConnectError = FALSE;
  212. Irql = Interrupt->Irql;
  213. Number = Interrupt->Number;
  214. Vector = Interrupt->Vector;
  215. if ( !((Irql > HIGH_LEVEL) ||
  216. (Number >= KeNumberProcessors) ||
  217. (Interrupt->SynchronizeIrql < Irql) ||
  218. (Interrupt->FloatingSave) // R0 x87 usage not supported on x86
  219. )
  220. ) {
  221. //
  222. //
  223. // Set system affinity to the specified processor.
  224. //
  225. KeSetSystemAffinityThread((KAFFINITY)(1<<Number));
  226. //
  227. // Raise IRQL to dispatcher level and lock dispatcher database.
  228. //
  229. KiLockDispatcherDatabase(&OldIrql);
  230. //
  231. // Is interrupt object already connected?
  232. //
  233. if (!Interrupt->Connected) {
  234. //
  235. // Determine interrupt dispatch vector
  236. //
  237. KiGetVectorInfo (
  238. Vector,
  239. &DispatchInfo
  240. );
  241. //
  242. // If dispatch vector is not connected, then connect it
  243. //
  244. if (DispatchInfo.Type == NoConnect) {
  245. Connected = TRUE;
  246. Interrupt->Connected = TRUE;
  247. //
  248. // Connect interrupt dispatch to interrupt object dispatch code
  249. //
  250. InitializeListHead(&Interrupt->InterruptListEntry);
  251. KiConnectVectorAndInterruptObject (Interrupt, NormalConnect);
  252. //
  253. // Enabled system vector
  254. //
  255. Enabled = HalEnableSystemInterrupt(Vector, Irql, Interrupt->Mode);
  256. if (!Enabled) {
  257. ConnectError = TRUE;
  258. }
  259. } else if (DispatchInfo.Type != UnkownConnect &&
  260. Interrupt->ShareVector &&
  261. DispatchInfo.Interrupt->ShareVector &&
  262. DispatchInfo.Interrupt->Mode == Interrupt->Mode) {
  263. //
  264. // Vector is already connected as sharable. New vector is sharable
  265. // and modes match. Chain new vector.
  266. //
  267. Connected = TRUE;
  268. Interrupt->Connected = TRUE;
  269. ASSERT (Irql <= SYNCH_LEVEL);
  270. //
  271. // If not already using chained dispatch handler, set it up
  272. //
  273. if (DispatchInfo.Type != ChainConnect) {
  274. KiConnectVectorAndInterruptObject (DispatchInfo.Interrupt, ChainConnect);
  275. }
  276. //
  277. // Add to tail of chained dispatch
  278. //
  279. InsertTailList(
  280. &DispatchInfo.Interrupt->InterruptListEntry,
  281. &Interrupt->InterruptListEntry
  282. );
  283. }
  284. }
  285. //
  286. // Unlock dispatcher database and lower IRQL to its previous value.
  287. //
  288. KiUnlockDispatcherDatabase(OldIrql);
  289. //
  290. // Set system affinity back to the original value.
  291. //
  292. KeRevertToUserAffinityThread();
  293. }
  294. if (Connected && ConnectError) {
  295. #if DBG
  296. DbgPrint ("HalEnableSystemInterrupt failed\n");
  297. #endif
  298. KeDisconnectInterrupt (Interrupt);
  299. Connected = FALSE;
  300. }
  301. //
  302. // Return whether interrupt was connected to the specified vector.
  303. //
  304. return Connected;
  305. }
  306. BOOLEAN
  307. KeDisconnectInterrupt (
  308. IN PKINTERRUPT Interrupt
  309. )
  310. /*++
  311. Routine Description:
  312. This function disconnects an interrupt object from the interrupt vector
  313. specified by the interrupt object. If the interrupt object is not
  314. connected, then a value of FALSE is returned. Else the specified interrupt
  315. object is disconnected from the interrupt vector, the connected state is
  316. set to FALSE, and TRUE is returned as the function value.
  317. Arguments:
  318. Interrupt - Supplies a pointer to a control object of type interrupt.
  319. Return Value:
  320. If the interrupt object is not connected, then a value of FALSE is
  321. returned. Else a value of TRUE is returned.
  322. --*/
  323. {
  324. DISPATCH_INFO DispatchInfo;
  325. BOOLEAN Connected;
  326. PKINTERRUPT Interrupty;
  327. KIRQL Irql;
  328. KIRQL OldIrql;
  329. ULONG Vector;
  330. //
  331. // Set system affinity to the specified processor.
  332. //
  333. KeSetSystemAffinityThread((KAFFINITY)(1<<Interrupt->Number));
  334. //
  335. // Raise IRQL to dispatcher level and lock dispatcher database.
  336. //
  337. KiLockDispatcherDatabase(&OldIrql);
  338. //
  339. // If the interrupt object is connected, then disconnect it from the
  340. // specified vector.
  341. //
  342. Connected = Interrupt->Connected;
  343. if (Connected) {
  344. Irql = Interrupt->Irql;
  345. Vector = Interrupt->Vector;
  346. //
  347. // If the specified interrupt vector is not connected to the chained
  348. // interrupt dispatcher, then disconnect it by setting its dispatch
  349. // address to the unexpected interrupt routine. Else remove the
  350. // interrupt object from the interrupt chain. If there is only
  351. // one entry remaining in the list, then reestablish the dispatch
  352. // address.
  353. //
  354. //
  355. // Determine interrupt dispatch vector
  356. //
  357. KiGetVectorInfo (
  358. Vector,
  359. &DispatchInfo
  360. );
  361. //
  362. // Is dispatch a chained handler?
  363. //
  364. if (DispatchInfo.Type == ChainConnect) {
  365. ASSERT (Irql <= SYNCH_LEVEL);
  366. //
  367. // Is interrupt being removed from head?
  368. //
  369. if (Interrupt == DispatchInfo.Interrupt) {
  370. //
  371. // Update next interrupt object to be head
  372. //
  373. DispatchInfo.Interrupt = CONTAINING_RECORD(
  374. DispatchInfo.Interrupt->InterruptListEntry.Flink,
  375. KINTERRUPT,
  376. InterruptListEntry
  377. );
  378. KiConnectVectorAndInterruptObject (DispatchInfo.Interrupt, ChainConnect);
  379. }
  380. //
  381. // Remove interrupt object
  382. //
  383. RemoveEntryList(&Interrupt->InterruptListEntry);
  384. //
  385. // If there's only one interrupt object left on this vector,
  386. // determine proper interrupt dispatcher
  387. //
  388. Interrupty = CONTAINING_RECORD(
  389. DispatchInfo.Interrupt->InterruptListEntry.Flink,
  390. KINTERRUPT,
  391. InterruptListEntry
  392. );
  393. if (DispatchInfo.Interrupt == Interrupty) {
  394. KiConnectVectorAndInterruptObject (Interrupty, NormalConnect);
  395. }
  396. } else {
  397. //
  398. // Removing last interrupt object from the vector. Disable the
  399. // vector, and set it to unconnected
  400. //
  401. HalDisableSystemInterrupt(Interrupt->Vector, Irql);
  402. KiConnectVectorAndInterruptObject (Interrupt, NoConnect);
  403. }
  404. KeSweepIcache(TRUE);
  405. Interrupt->Connected = FALSE;
  406. }
  407. //
  408. // Unlock dispatcher database and lower IRQL to its previous value.
  409. //
  410. KiUnlockDispatcherDatabase(OldIrql);
  411. //
  412. // Set system affinity back to the original value.
  413. //
  414. KeRevertToUserAffinityThread();
  415. //
  416. // Return whether interrupt was disconnected from the specified vector.
  417. //
  418. return Connected;
  419. }
  420. VOID
  421. KiGetVectorInfo (
  422. IN ULONG Vector,
  423. OUT PDISPATCH_INFO DispatchInfo
  424. )
  425. {
  426. PKINTERRUPT_ROUTINE Dispatch;
  427. ULONG CurrentDispatch;
  428. ULONG DispatchType;
  429. UCHAR IDTEntry;
  430. //
  431. // Get second level dispatch point
  432. //
  433. DispatchType = HalSystemVectorDispatchEntry (
  434. Vector,
  435. &DispatchInfo->FlatDispatch,
  436. &DispatchInfo->NoDispatch
  437. );
  438. //
  439. // Get vector info
  440. //
  441. switch (DispatchType) {
  442. case 0:
  443. //
  444. // Primary dispatch
  445. //
  446. IDTEntry = HalVectorToIDTEntry(Vector);
  447. DispatchInfo->NoDispatch = (PKINTERRUPT_ROUTINE) (((ULONG) &KiStartUnexpectedRange) +
  448. (IDTEntry - PRIMARY_VECTOR_BASE) * KiUnexpectedEntrySize);
  449. DispatchInfo->InterruptDispatch = KiInterruptDispatch;
  450. DispatchInfo->FloatingDispatch = KiFloatingDispatch;
  451. DispatchInfo->ChainedDispatch = KiChainedDispatch;
  452. DispatchInfo->FlatDispatch = NULL;
  453. CurrentDispatch = (ULONG) KiReturnHandlerAddressFromIDT(Vector);
  454. DispatchInfo->Interrupt = CONTAINING_RECORD (
  455. CurrentDispatch,
  456. KINTERRUPT,
  457. DispatchCode
  458. );
  459. break;
  460. case 1:
  461. //
  462. // Secondardy dispatch.
  463. //
  464. DispatchInfo->InterruptDispatch = KiInterruptDispatch2ndLvl;
  465. DispatchInfo->FloatingDispatch = KiInterruptDispatch2ndLvl;
  466. DispatchInfo->ChainedDispatch = KiChainedDispatch2ndLvl;
  467. CurrentDispatch = (ULONG) *DispatchInfo->FlatDispatch;
  468. DispatchInfo->Interrupt = (PKINTERRUPT) ( (PUCHAR) CurrentDispatch -
  469. (PUCHAR) KiInterruptTemplate +
  470. (PUCHAR) &KiInterruptTemplate2ndDispatch
  471. );
  472. break;
  473. default:
  474. // Other values reserved
  475. KeBugCheck (MISMATCHED_HAL);
  476. }
  477. //
  478. // Determine dispatch type
  479. //
  480. if (((PKINTERRUPT_ROUTINE) CurrentDispatch) == DispatchInfo->NoDispatch) {
  481. //
  482. // Is connected to the NoDispatch function
  483. //
  484. DispatchInfo->Type = NoConnect;
  485. } else {
  486. Dispatch = DispatchInfo->Interrupt->DispatchAddress;
  487. if (Dispatch == DispatchInfo->ChainedDispatch) {
  488. //
  489. // Is connected to the chained handler
  490. //
  491. DispatchInfo->Type = ChainConnect;
  492. } else if (Dispatch == DispatchInfo->InterruptDispatch ||
  493. Dispatch == DispatchInfo->FloatingDispatch) {
  494. //
  495. // If connection to the non-chained handler
  496. //
  497. DispatchInfo->Type = NormalConnect;
  498. } else {
  499. //
  500. // Unkown connection
  501. //
  502. DispatchInfo->Type = UnkownConnect;
  503. #if DBG
  504. DbgPrint ("KiGetVectorInfo not understood\n");
  505. #endif
  506. }
  507. }
  508. }
  509. VOID
  510. KiConnectVectorAndInterruptObject (
  511. IN PKINTERRUPT Interrupt,
  512. IN CONNECT_TYPE Type
  513. )
  514. {
  515. PKINTERRUPT_ROUTINE DispatchAddress;
  516. DISPATCH_INFO DispatchInfo;
  517. PULONG pl;
  518. //
  519. // Get current connect info
  520. //
  521. KiGetVectorInfo (
  522. Interrupt->Vector,
  523. &DispatchInfo
  524. );
  525. //
  526. // If disconnecting, set vector to NoDispatch
  527. //
  528. if (Type == NoConnect) {
  529. DispatchAddress = DispatchInfo.NoDispatch;
  530. } else {
  531. //
  532. // Set interrupt objects dispatch for new type
  533. //
  534. DispatchAddress = DispatchInfo.ChainedDispatch;
  535. if (Type == NormalConnect) {
  536. DispatchAddress = DispatchInfo.InterruptDispatch;
  537. if (Interrupt->FloatingSave) {
  538. DispatchAddress = DispatchInfo.FloatingDispatch;
  539. }
  540. }
  541. Interrupt->DispatchAddress = DispatchAddress;
  542. //
  543. // Set interrupt objects dispatch code to kernel dispatcher
  544. //
  545. pl = &(Interrupt->DispatchCode[0]);
  546. pl = (PULONG)((PUCHAR)pl +
  547. ((PUCHAR)&KiInterruptTemplateDispatch -
  548. (PUCHAR)KiInterruptTemplate) -4);
  549. *pl = (ULONG)DispatchAddress-(ULONG)((PUCHAR)pl+4);
  550. //
  551. // Set dispatch vector to proper address dispatch code location
  552. //
  553. if (DispatchInfo.FlatDispatch) {
  554. //
  555. // Connect to flat dispatch
  556. //
  557. DispatchAddress = (PKINTERRUPT_ROUTINE) (ULONG_PTR)
  558. ((PUCHAR) &(Interrupt->DispatchCode[0]) +
  559. ((PUCHAR) &KiInterruptTemplate2ndDispatch -
  560. (PUCHAR) KiInterruptTemplate));
  561. } else {
  562. //
  563. // Connect to enter_all dispatch
  564. //
  565. DispatchAddress = (PKINTERRUPT_ROUTINE) (ULONG_PTR) &Interrupt->DispatchCode;
  566. }
  567. }
  568. if (DispatchInfo.FlatDispatch) {
  569. //
  570. // Connect to flat dispatch
  571. //
  572. *DispatchInfo.FlatDispatch = DispatchAddress;
  573. } else {
  574. //
  575. // Connect to IDT
  576. //
  577. KiSetHandlerAddressToIDT (Interrupt->Vector, DispatchAddress);
  578. }
  579. }
  580. VOID
  581. FASTCALL
  582. KiTimedChainedDispatch2ndLvl(
  583. PKINTERRUPT Interrupt
  584. )
  585. /*++
  586. Routine Description:
  587. This function performs the same function as KiChainedDispatch2ndLvl
  588. except that it is written in C instead of assembly code and includes
  589. code for timing ISRs.
  590. I'd be interested in seeing some benchmarks to show if the assembly
  591. code is actually faster. The Acquire/Release spinlock could be
  592. inlined fairly easily.
  593. Arguments:
  594. Interrupt - Supplies a pointer to a control object of type interrupt.
  595. Return Value:
  596. None.
  597. --*/
  598. {
  599. BOOLEAN Handled = FALSE;
  600. PVOID ListEnd = &Interrupt->InterruptListEntry.Flink;
  601. //
  602. //BEGINTIMING
  603. PKPRCB Prcb = KeGetCurrentPrcb();
  604. ULONGLONG StartTimeHigher;
  605. ULONGLONG StartTime;
  606. ULONGLONG TimeHigher;
  607. ULONGLONG ElapsedTime;
  608. //BEGINTIMINGend
  609. //
  610. // For each interrupt on this chain.
  611. //
  612. do {
  613. //
  614. // If the current IRQL (IRQL raised to by nature of taking this
  615. // interrupt) is not equal to the Synchronization IRQL required
  616. // for this interrupt, raise to the appropriate level.
  617. //
  618. if (Interrupt->Irql != Interrupt->SynchronizeIrql) {
  619. KfRaiseIrql(Interrupt->SynchronizeIrql);
  620. }
  621. //BEGINTIMING
  622. StartTimeHigher = Prcb->IsrTime;
  623. StartTime = RDTSC();
  624. //BEGINTIMINGend
  625. //
  626. // Acquire the interrupt lock.
  627. //
  628. KiAcquireSpinLock(Interrupt->ActualLock);
  629. //
  630. // Call the Interrupt Service Routine.
  631. //
  632. Handled |= Interrupt->ServiceRoutine(Interrupt,
  633. Interrupt->ServiceContext);
  634. //
  635. // Release the interrupt lock.
  636. //
  637. KiReleaseSpinLock(Interrupt->ActualLock);
  638. //ENDTIMING
  639. //
  640. // ElapsedTime is time since we started looking at this element
  641. // on the chain. (ie the current interrupt object).
  642. //
  643. ElapsedTime = RDTSC() - StartTime;
  644. //
  645. // TimeHigher is the amount Prcb->IsrTime has increased since we
  646. // begain servicing this interrupt object, ie the amount of time
  647. // spent in higher level ISRs.
  648. //
  649. TimeHigher = Prcb->IsrTime - StartTimeHigher;
  650. //
  651. // Adjust ElapsedTime to time spent on this interrupt object, excluding
  652. // higher level ISRs.
  653. //
  654. ElapsedTime -= TimeHigher;
  655. if (ElapsedTime > KiIsrTscLimit) {
  656. //
  657. // If there is a debugger attached, breakin. Otherwise do nothing.
  658. // N.B. bugchecking is another possibility.
  659. //
  660. if (KdDebuggerEnabled) {
  661. DbgPrint("KE; ISR time limit exceeded (intobj %p)\n",
  662. Interrupt);
  663. DbgBreakPoint();
  664. }
  665. }
  666. //
  667. // Update time spent processing interrupts. This doesn't need
  668. // to be atomic as it doesn't matter if it's a little bit lossy.
  669. // (Though a simple atomic add would do, it's per processor and
  670. // at IRQL > DISPATCH_LEVEL so it doesn't need to be locked).
  671. //
  672. Prcb->IsrTime += ElapsedTime;
  673. //ENDTIMINGend
  674. //
  675. // If IRQL was raised, lower to the previous level.
  676. //
  677. if (Interrupt->Irql != Interrupt->SynchronizeIrql) {
  678. KfLowerIrql(Interrupt->Irql);
  679. }
  680. if ((Handled != FALSE) &&
  681. (Interrupt->Mode == LevelSensitive)) {
  682. //
  683. // The interrupt has been handled.
  684. //
  685. return;
  686. }
  687. //
  688. // If this is the last entry on the chain, get out, otherwise
  689. // advance to the next entry.
  690. //
  691. if (Interrupt->InterruptListEntry.Flink == ListEnd) {
  692. ASSERT(Interrupt->Mode != LevelSensitive);
  693. //
  694. // We should only get to the end of the list if
  695. // (a) interrupts are on this chain are level sensitive and
  696. // no ISR handled the request. This is a system fatal
  697. // condition, or,
  698. // (b) the chain has edge triggered interrupts in which case
  699. // we must run the chain repeatedly until no ISR services
  700. // the request.
  701. //
  702. // Question: Do we actually have chained edge triggered
  703. // interrupts anymore?
  704. //
  705. if (Handled == FALSE) {
  706. break;
  707. }
  708. }
  709. Interrupt = CONTAINING_RECORD(Interrupt->InterruptListEntry.Flink,
  710. KINTERRUPT,
  711. InterruptListEntry);
  712. } while (TRUE);
  713. }
  714. VOID
  715. FASTCALL
  716. KiTimedInterruptDispatch(
  717. PKINTERRUPT Interrupt
  718. )
  719. /*++
  720. Routine Description:
  721. This function is a wrapper for the guts of KiDispatchInterrupt. It
  722. is called when the system has been patched to time interrupts.
  723. Arguments:
  724. Interrupt - Supplies a pointer to a control object of type interrupt.
  725. Return Value:
  726. None.
  727. --*/
  728. {
  729. //BEGINTIMING
  730. PKPRCB Prcb = KeGetCurrentPrcb();
  731. ULONGLONG StartTimeHigher = Prcb->IsrTime;
  732. ULONGLONG StartTime = RDTSC();
  733. ULONGLONG TimeHigher;
  734. ULONGLONG ElapsedTime;
  735. //BEGINTIMINGend
  736. //
  737. // Acquire the interrupt lock.
  738. //
  739. KiAcquireSpinLock(Interrupt->ActualLock);
  740. //
  741. // Call the Interrupt Service Routine.
  742. //
  743. Interrupt->ServiceRoutine(Interrupt,
  744. Interrupt->ServiceContext);
  745. //
  746. // Release the interrupt lock.
  747. //
  748. KiReleaseSpinLock(Interrupt->ActualLock);
  749. //ENDTIMING
  750. //
  751. // ElapsedTime is time since we entered this routine.
  752. //
  753. ElapsedTime = RDTSC() - StartTime;
  754. //
  755. // TimeHigher is the amount Prcb->IsrTime has increased since we
  756. // entered this rouine, ie the amount of time spent in higher level
  757. // ISRs.
  758. //
  759. TimeHigher = Prcb->IsrTime - StartTimeHigher;
  760. //
  761. // Adjust ElapsedTime to time spent in this routine, excluding
  762. // higher level ISRs.
  763. //
  764. ElapsedTime -= TimeHigher;
  765. if (ElapsedTime > KiIsrTscLimit) {
  766. //
  767. // If there is a debugger attached, breakin. Otherwise do nothing.
  768. // N.B. bugchecking is another possibility.
  769. //
  770. if (KdDebuggerEnabled) {
  771. DbgPrint("KE; ISR time limit exceeded (intobj %p)\n", Interrupt);
  772. DbgBreakPoint();
  773. }
  774. }
  775. //
  776. // Update time spent processing interrupts. This doesn't need
  777. // to be atomic as it doesn't matter if it's a little bit lossy.
  778. // (Though a simple atomic add would do, it's per processor and
  779. // at IRQL > DISPATCH_LEVEL so it doesn't need to be locked).
  780. //
  781. Prcb->IsrTime += ElapsedTime;
  782. //ENDTIMINGend
  783. }
  784. //
  785. // KiInitializeInterruptTimers but not KiInitializeInterruptTimersDpc
  786. // should be in the INIT section.
  787. //
  788. typedef struct {
  789. KTIMER SampleTimer;
  790. KDPC Dpc;
  791. ULONGLONG InitialTime;
  792. } KISRTIMERINIT, *PKISRTIMERINIT;
  793. PKISRTIMERINIT KiIsrTimerInit;
  794. VOID
  795. KiInitializeInterruptTimersDpc (
  796. IN PKDPC Dpc,
  797. IN PVOID DeferredContext,
  798. IN PVOID SystemArgument1,
  799. IN PVOID SystemArgument2
  800. )
  801. /*++
  802. Routine Description:
  803. This DPC is run twice on timer expiration. The time between
  804. runs us used to determine the frequency of the processor's
  805. Time Stamp Counter (TSC) in order to calculate a TSC delta
  806. equivalent to the ISR timeout value which is in microseconds.
  807. Arguments:
  808. Dpc - Supplies a pointer to a DPC object - not used.
  809. DeferredContext - Supplies the DPC context - not used.
  810. SystemArgument1 - Supplies the first system argument - note used.
  811. SystemArgument2 - Supplies the second system argument - note used.
  812. Return Value:
  813. None.
  814. --*/
  815. {
  816. ULONGLONG Delta;
  817. UNREFERENCED_PARAMETER(Dpc);
  818. UNREFERENCED_PARAMETER(DeferredContext);
  819. UNREFERENCED_PARAMETER(SystemArgument1);
  820. UNREFERENCED_PARAMETER(SystemArgument2);
  821. if (KiIsrTscLimit == 0xFFFFFFFFFFFFFFFF) {
  822. //
  823. // First pass. Get starting TSC value.
  824. //
  825. KiIsrTimerInit->InitialTime = RDTSC();
  826. KiIsrTscLimit = 0xFFFFFFFFFFFFFFFE;
  827. } else {
  828. //
  829. // Second pass. Get ending TSC value, cancel the periodic
  830. // timer controlling this DPC and free the memory associated
  831. // with the timer and the DPC.
  832. //
  833. Delta = RDTSC() - KiIsrTimerInit->InitialTime;
  834. KeCancelTimer(&KiIsrTimerInit->SampleTimer);
  835. ExFreePool(KiIsrTimerInit);
  836. //
  837. // Delta is now the number of TSC clock ticks that occured in
  838. // 10 seconds. We choose such a large number to minimize error.
  839. //
  840. // Calculate the number of TSC clock ticks in KiTimeLimitIsrMicroseconds
  841. // microseconds.
  842. //
  843. Delta *= KiTimeLimitIsrMicroseconds;
  844. Delta /= (10 * 1000 * 1000);
  845. KiIsrTscLimit = Delta;
  846. }
  847. }
  848. VOID
  849. KiInitializeInterruptTimers(
  850. VOID
  851. )
  852. {
  853. LARGE_INTEGER DueTime;
  854. //
  855. // If not timing ISRs, nothing to do.
  856. //
  857. if (KiTimeLimitIsrMicroseconds == 0) {
  858. return;
  859. }
  860. //
  861. // The kernel is initialized. Use a timer to determine the amount
  862. // the Time Stamp Counter advances by in 10 seconds, then use that
  863. // result to set the ISR time limit.
  864. //
  865. if ((KeFeatureBits & KF_RDTSC) == 0) {
  866. //
  867. // Processor doesn't support the RDTSC instruction, don't attempt
  868. // to time ISRs.
  869. //
  870. return;
  871. }
  872. KiIsrTimerInit = ExAllocatePoolWithTag(NonPagedPool,
  873. sizeof(*KiIsrTimerInit),
  874. ' eK');
  875. if (KiIsrTimerInit == NULL) {
  876. //
  877. // Couldn't allocate memory for timer? Skip ISR timing.
  878. //
  879. return;
  880. }
  881. KeInitializeTimerEx(&KiIsrTimerInit->SampleTimer, SynchronizationTimer);
  882. KeInitializeDpc(&KiIsrTimerInit->Dpc, &KiInitializeInterruptTimersDpc, NULL);
  883. //
  884. // Relative time in 100 nanoseconds = 10 seconds.
  885. //
  886. DueTime.QuadPart = -(10 * 10 * 1000 * 1000);
  887. KeSetTimerEx(&KiIsrTimerInit->SampleTimer,
  888. DueTime, //
  889. 10000, // repeat in 10 seconds.
  890. &KiIsrTimerInit->Dpc);
  891. }