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.

1133 lines
22 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. mpipi.c
  5. Abstract:
  6. This module provides the HAL support for interprocessor interrupts and
  7. processor initialization for MPS systems.
  8. Author:
  9. Forrest Foltz (forrestf) 27-Oct-2000
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. --*/
  14. #include "halcmn.h"
  15. #define HAL_FORCEINLINE __forceinline
  16. //
  17. // External functions
  18. //
  19. VOID
  20. HalpResetThisProcessor (
  21. VOID
  22. );
  23. ULONG
  24. DetectAcpiMP (
  25. OUT PBOOLEAN IsConfiguredMp,
  26. IN PLOADER_PARAMETER_BLOCK LoaderBlock
  27. );
  28. //
  29. // External data
  30. //
  31. extern KAFFINITY HalpNodeAffinity[];
  32. extern INTERRUPT_DEST HalpIpiDestinationMap[sizeof(KAFFINITY)][256];
  33. extern BOOLEAN HalpStaticIntAffinity;
  34. extern UCHAR rgzBadHal[];
  35. //
  36. // Local types
  37. //
  38. typedef
  39. VOID
  40. (*HAL_GENERIC_IPI_FUNCTION) (
  41. ULONG_PTR Context
  42. );
  43. //
  44. // Local prototypes
  45. //
  46. VOID
  47. HalInitApicInterruptHandlers(
  48. VOID
  49. );
  50. //
  51. // External data
  52. //
  53. extern UCHAR HalpPICINTToVector[16];
  54. //
  55. // Local data and defines
  56. //
  57. KSPIN_LOCK HalpBroadcastLock;
  58. KAFFINITY volatile HalpBroadcastTargets;
  59. ULONG_PTR HalpBroadcastContext;
  60. HAL_GENERIC_IPI_FUNCTION HalpBroadcastFunction;
  61. PKPCR HalpProcessorPCR[MAXIMUM_PROCESSORS];
  62. //
  63. // HalpGlobal8259Mask is used to avoid reading the PIC to get the current
  64. // interrupt mask; format is the same as for SET_8259_MASK, i.i.,
  65. // bits 7:0 -> PIC1, 15:8 -> PIC2
  66. //
  67. USHORT HalpGlobal8259Mask = 0;
  68. #define GENERIC_IPI (DELIVER_FIXED | LOGICAL_DESTINATION | ICR_USE_DEST_FIELD | APIC_GENERIC_VECTOR)
  69. #define APIC_IPI (DELIVER_FIXED | LOGICAL_DESTINATION | ICR_USE_DEST_FIELD | APIC_IPI_VECTOR)
  70. //
  71. // Globals and constants used to log local apic errors
  72. //
  73. #define LogApicErrors TRUE
  74. #if LogApicErrors
  75. //
  76. // Structure defining the layout of an apic error record.
  77. //
  78. typedef struct _APIC_ERROR {
  79. union {
  80. struct {
  81. UCHAR SendChecksum:1;
  82. UCHAR ReceiveChecksum:1;
  83. UCHAR SendAccept:1;
  84. UCHAR ReceiveAccept:1;
  85. UCHAR Reserved1:1;
  86. UCHAR SendVector:1;
  87. UCHAR ReceiveVector:1;
  88. UCHAR RegisterAddress:1;
  89. };
  90. UCHAR AsByte;
  91. };
  92. UCHAR Processor;
  93. } APIC_ERROR, *PAPIC_ERROR;
  94. #define APIC_ERROR_LOG_SIZE 128
  95. //
  96. // Count of local apic errors.
  97. //
  98. ULONG HalpLocalApicErrorCount = 0;
  99. //
  100. // Apic error log. This is circular, indexed by
  101. // HalpLocalApicErrorCount % APIC_ERROR_LOG_SIZE.
  102. //
  103. APIC_ERROR HalpApicErrorLog[APIC_ERROR_LOG_SIZE];
  104. //
  105. // Spinlock used to protect access to HalpLocalApicErrorCount.
  106. //
  107. KSPIN_LOCK HalpLocalApicErrorLock;
  108. #endif
  109. HAL_FORCEINLINE
  110. VOID
  111. HalpSendIpiWorker (
  112. IN UCHAR TargetSet,
  113. IN ULONG Command
  114. )
  115. /*++
  116. Routine Description:
  117. This routine is called to send an IPI command to a set of processors
  118. on a single node.
  119. Parameters:
  120. TargetSet - Specifies the processor identifiers within the node.
  121. Command - Specifies the IPI command to send.
  122. Return Value:
  123. None.
  124. --*/
  125. {
  126. ULONG destination;
  127. //
  128. // Only high byte of the destination is used. Wait until the Apic is
  129. // not busy before sending. Continue without waiting, there will be
  130. // another wait after all IPIs have been submitted.
  131. //
  132. destination = (ULONG)TargetSet << DESTINATION_SHIFT;
  133. HalpStallWhileApicBusy();
  134. LOCAL_APIC(LU_INT_CMD_HIGH) = destination;
  135. LOCAL_APIC(LU_INT_CMD_LOW) = Command;
  136. }
  137. HAL_FORCEINLINE
  138. VOID
  139. HalpSendNodeIpi (
  140. IN KAFFINITY Affinity,
  141. IN ULONG Command
  142. )
  143. /*++
  144. Routine Description:
  145. Parameters:
  146. Affinity - Specifies the set of processors to receive the IPI.
  147. Command - Specifies the IPI command to send.
  148. Return Value:
  149. None.
  150. --*/
  151. {
  152. KAFFINITY remainingProcessors;
  153. PKAFFINITY nodeAffinity;
  154. ULONG chunkNo;
  155. INTERRUPT_DEST intDest;
  156. INTERRUPT_DEST intDestSum;
  157. UCHAR affinityChunk;
  158. //
  159. // Declare a local union that can be used to access an affinity
  160. // both chunk-wise and as a whole.
  161. //
  162. union {
  163. UCHAR Chunks[sizeof(KAFFINITY)];
  164. KAFFINITY Whole;
  165. } affinity;
  166. //
  167. // Affinity has some number of target processors indicated. Each
  168. // target processor is a member of a cluster of processors, or "node".
  169. //
  170. // For each node, determine whether it contains any of the target
  171. // processors. If so, send an IPI command targeting those processors
  172. // and send it to the node.
  173. //
  174. nodeAffinity = HalpNodeAffinity;
  175. remainingProcessors = Affinity & HalpActiveProcessors;
  176. while (remainingProcessors != 0) {
  177. //
  178. // Iterate through the node affinities here until a node containing
  179. // at least some of the targeted processors is encountered.
  180. //
  181. do {
  182. //
  183. // Determine the set of target CPUs that can be found in this
  184. // node.
  185. //
  186. affinity.Whole = *nodeAffinity & remainingProcessors;
  187. //
  188. // Point nodeAffinity at the affinity for the next cluster.
  189. //
  190. nodeAffinity += 1;
  191. ASSERT((nodeAffinity - HalpNodeAffinity) < MAX_NODES);
  192. } while (affinity.Whole == 0);
  193. //
  194. // Remove the processors that will be processed on this node from
  195. // the set of processors remaining to be processed.
  196. //
  197. remainingProcessors ^= affinity.Whole;
  198. //
  199. // Accumulate the logical target mask
  200. //
  201. intDestSum.LogicalId = 0;
  202. chunkNo = 0;
  203. do {
  204. //
  205. // Isolate a chunk of the processor affinity mask.
  206. //
  207. affinityChunk = affinity.Chunks[chunkNo];
  208. //
  209. // Use that chunk as an index into HalpIpiDestinationMap[][],
  210. // retrieving the logical sum of all node/processor IDs that
  211. // are associated with each bit that is set in that affinity
  212. // chunk.
  213. //
  214. intDest = HalpIpiDestinationMap[chunkNo][affinityChunk];
  215. //
  216. // Now, sum it with the logical ID mask that is being accumulated
  217. // for each affinity chunk.
  218. //
  219. intDestSum.LogicalId |= intDest.LogicalId;
  220. //
  221. // Indicate that the processors represented in this chunk
  222. // have been processed. When there are no more processors
  223. // left to process for this node, send the IPI and proceed
  224. // to the next node.
  225. //
  226. affinity.Chunks[chunkNo] = 0;
  227. chunkNo += 1;
  228. } while (affinity.Whole != 0);
  229. //
  230. // intDest contains an accumulated set of hardware processor
  231. // identifiers, representing all of the processors on this node that
  232. // should receive the command.
  233. //
  234. HalpSendIpiWorker(intDestSum.LogicalId,Command);
  235. }
  236. }
  237. VOID
  238. HalpSendIpi (
  239. IN KAFFINITY Affinity,
  240. IN ULONG Command
  241. )
  242. /*++
  243. Routine Description:
  244. Affinity - Specifies the set of processors to receive the IPI.
  245. Command - Specifies the IPI command to send.
  246. Parameters:
  247. None.
  248. Return Value:
  249. None.
  250. --*/
  251. {
  252. ULONG flags;
  253. //
  254. // Disable interrupts and call the appropriate routine.
  255. //
  256. flags = HalpDisableInterrupts();
  257. if (HalpMaxProcsPerCluster == 0) {
  258. //
  259. // We know that the maximum number of processors is 8,
  260. // so send the IPI directly.
  261. //
  262. ASSERT((Affinity & 0xFF) == Affinity);
  263. HalpSendIpiWorker((UCHAR)Affinity,Command);
  264. } else {
  265. //
  266. // Send an IPI to one or mode nodes.
  267. //
  268. HalpSendNodeIpi(Affinity,Command);
  269. }
  270. //
  271. // Stall until the last IPI has been sent, restore interrupts and
  272. // return.
  273. //
  274. HalpStallWhileApicBusy();
  275. HalpRestoreInterrupts(flags);
  276. }
  277. VOID
  278. HalInitializeProcessor(
  279. ULONG ProcessorNumber,
  280. PLOADER_PARAMETER_BLOCK LoaderBlock
  281. )
  282. /*++
  283. Routine Description:
  284. Initialize hal pcr values for current processor (if any)
  285. (called shortly after processor reaches kernel, before
  286. HalInitSystem if P0)
  287. IPI's and KeReadir/LowerIrq's must be available once this function
  288. returns. (IPI's are only used once two or more processors are
  289. available)
  290. . Enable IPI interrupt (makes sense for P1, P2, ...).
  291. . Save Processor Number in PCR.
  292. . if (P0)
  293. . determine if the system is a PC+MP,
  294. . if not a PC+MP System Halt;
  295. . Enable IPI's on CPU.
  296. Arguments:
  297. Number - Logical processor number of calling processor
  298. Return Value:
  299. None.
  300. --*/
  301. {
  302. PKPCR pcr;
  303. KAFFINITY affinity;
  304. KAFFINITY oldAffinity;
  305. ULONG detectAcpiResult;
  306. BOOLEAN isMp;
  307. affinity = (KAFFINITY)1 << ProcessorNumber;
  308. pcr = KeGetPcr();
  309. //
  310. // Mark all interrupts as disabled, and store the processor number and
  311. // the default stall scale factor in the pcr.
  312. //
  313. pcr->Idr = 0xFFFFFFFF;
  314. pcr->Number = (UCHAR)ProcessorNumber;
  315. pcr->StallScaleFactor = INITIAL_STALL_COUNT;
  316. //
  317. // Record the pcr pointer in our lookup table and set the affinity
  318. // bit in our set of active processors.
  319. //
  320. HalpProcessorPCR[ProcessorNumber] = pcr;
  321. HalpActiveProcessors |= affinity;
  322. if (HalpStaticIntAffinity == 0) {
  323. //
  324. // Interrupts can go to any processor
  325. //
  326. HalpDefaultInterruptAffinity |= affinity;
  327. } else {
  328. //
  329. // Interrupts go only to the highest numbered processor
  330. //
  331. if (HalpDefaultInterruptAffinity < affinity) {
  332. HalpDefaultInterruptAffinity = affinity;
  333. }
  334. }
  335. if (ProcessorNumber == 0) {
  336. KeInitializeSpinLock(&HalpBroadcastLock);
  337. #if LogApicErrors
  338. KeInitializeSpinLock(&HalpLocalApicErrorLock);
  339. #endif
  340. //
  341. // Determine whether the system we are on is an MPS system.
  342. //
  343. // DetectMPS has a parameter we don't currently use. It's a boolean
  344. // which is set to TRUE if the system we're on is an MP system.
  345. // We could have a UP MPS system.
  346. //
  347. // The DetectMPS routine also allocates virtual addresses for all of
  348. // the APICs in the system.
  349. //
  350. #if defined(ACPI_HAL)
  351. detectAcpiResult = DetectAcpiMP(&isMp,LoaderBlock);
  352. #else
  353. detectAcpiResult = DetectMPS(&isMp);
  354. #endif
  355. if (detectAcpiResult == FALSE) {
  356. HalDisplayString(rgzBadHal);
  357. HalpDisableInterrupts();
  358. while (TRUE) {
  359. HalpHalt();
  360. }
  361. }
  362. HalpRegisterKdSupportFunctions(LoaderBlock);
  363. //
  364. // Mask all PIC interrupts
  365. //
  366. HalpGlobal8259Mask = 0xFFFF;
  367. SET_8259_MASK(HalpGlobal8259Mask);
  368. }
  369. //
  370. // All processors execute this code
  371. //
  372. HalInitApicInterruptHandlers();
  373. HalpInitializeLocalUnit();
  374. }
  375. VOID
  376. HalInitApicInterruptHandlers(
  377. VOID
  378. )
  379. /*++
  380. Routine Description:
  381. This routine installs the interrupt vector in the IDT for the APIC
  382. spurious interrupt.
  383. Arguments:
  384. None.
  385. Return Value:
  386. None.
  387. --*/
  388. {
  389. PKPCR pcr;
  390. PKIDTENTRY64 idt;
  391. KiSetHandlerAddressToIDTIrql(PIC1_SPURIOUS_VECTOR,
  392. PicSpuriousService37,
  393. NULL,
  394. 0);
  395. KiSetHandlerAddressToIDTIrql(APIC_SPURIOUS_VECTOR,
  396. HalpApicSpuriousService,
  397. NULL,
  398. 0);
  399. }
  400. __forceinline
  401. VOID
  402. HalpPollForBroadcast (
  403. VOID
  404. )
  405. /*++
  406. Routine Description:
  407. Checks whether the current processor has a broadcast function pending
  408. and, if so, clears it's pending bit and calls the function.
  409. Arguments:
  410. None.
  411. Return Value:
  412. None.
  413. --*/
  414. {
  415. KAFFINITY affinity;
  416. ULONG_PTR broadcastContext;
  417. HAL_GENERIC_IPI_FUNCTION broadcastFunction;
  418. KAFFINITY broadcastTargets;
  419. affinity = KeGetPcr()->CurrentPrcb->SetMember;
  420. if ((HalpBroadcastTargets & affinity) != 0) {
  421. //
  422. // A pending generic IPI call appears to be pending for this
  423. // processor. Pick up the function pointer and context locally.
  424. //
  425. broadcastFunction = HalpBroadcastFunction;
  426. broadcastContext = HalpBroadcastContext;
  427. //
  428. // Atomically acknowledge the broadcast. If the broadcast is still
  429. // pending for this processor, then call it.
  430. //
  431. // BUGBUG
  432. // broadcastTargets = InterlockedAnd64(&HalpBroadcastTargets,~affinity);
  433. broadcastTargets &= ~affinity;
  434. if ((broadcastTargets & affinity) != 0) {
  435. broadcastFunction(broadcastContext);
  436. }
  437. }
  438. }
  439. VOID
  440. HalpGenericCall(
  441. IN HAL_GENERIC_IPI_FUNCTION BroadcastFunction,
  442. IN ULONG Context,
  443. IN KAFFINITY TargetProcessors
  444. )
  445. /*++
  446. Routine Description:
  447. Causes the WorkerFunction to be called on the specified target
  448. processors. The WorkerFunction is called at CLOCK2_LEVEL-1
  449. (Must be below IPI_LEVEL in order to prevent system deadlocks).
  450. Enviroment:
  451. Must be called with interrupts enabled.
  452. Must be called with IRQL = CLOCK2_LEVEL-1
  453. --*/
  454. {
  455. //
  456. // Nothing to do if no target processors have been specified.
  457. //
  458. if (TargetProcessors == 0) {
  459. return;
  460. }
  461. //
  462. // Acquire the broadcast lock, polling for broadcasts while spinning.
  463. //
  464. while (KeTryToAcquireSpinLockAtDpcLevel(&HalpBroadcastLock) == FALSE) {
  465. do {
  466. HalpPollForBroadcast();
  467. } while (KeTestSpinLock(&HalpBroadcastLock) == FALSE);
  468. }
  469. //
  470. // We own the broadcast lock. Store the broadcast parameters
  471. // into the broadcast prameters and send the generic IPI.
  472. //
  473. HalpBroadcastFunction = BroadcastFunction;
  474. HalpBroadcastContext = Context;
  475. HalpBroadcastTargets = TargetProcessors;
  476. HalpSendIpi(TargetProcessors,GENERIC_IPI);
  477. //
  478. // Wait for all processors to pick up the IPI and process the generic
  479. // call, then release the broadcast lock.
  480. //
  481. do {
  482. HalpPollForBroadcast();
  483. } while (HalpBroadcastTargets != 0);
  484. KeReleaseSpinLockFromDpcLevel(&HalpBroadcastLock);
  485. }
  486. ULONG
  487. HalpWaitForPending (
  488. IN ULONG Count,
  489. IN ULONG volatile *ICR
  490. )
  491. /*++
  492. Routine Description:
  493. Spins waiting for the DELIVERY_PENDING bit in the ICR to clear or
  494. until spinning Count times.
  495. Arguments:
  496. Count - Number of times through the loop before giving up.
  497. ICR - Pointer to the ICR register containing the DELIVERY_PENDING
  498. status bit.
  499. Return Value:
  500. Zero if the DELIVERY_PENDING bit has cleared within the number of
  501. test cycles, non-zero otherwise.
  502. --*/
  503. {
  504. ULONG countRemaining;
  505. countRemaining = Count;
  506. while (countRemaining > 0) {
  507. if ((*ICR & DELIVERY_PENDING) != 0) {
  508. break;
  509. }
  510. countRemaining -= 1;
  511. }
  512. return countRemaining;
  513. }
  514. VOID
  515. HalRequestIpi (
  516. IN KAFFINITY Affinity
  517. )
  518. /*++
  519. Routine Description:
  520. Requests an interprocessor interrupt
  521. Arguments:
  522. Affinity - Supplies the set of processors to be interrupted
  523. Return Value:
  524. None.
  525. --*/
  526. {
  527. HalpSendIpi(Affinity,APIC_IPI);
  528. }
  529. BOOLEAN
  530. HalpApicRebootService (
  531. IN PKINTERRUPT Interrupt,
  532. IN PVOID ServiceContext
  533. )
  534. /*++
  535. Routine Description:
  536. This is the ISR that handles Reboot interrupts.
  537. Arguments:
  538. Interrupt - Supplies a pointer to the kernel interrupt object
  539. ServiceContext - Supplies the service context
  540. Return Value:
  541. None. This routine does not return.
  542. --*/
  543. {
  544. UNREFERENCED_PARAMETER(Interrupt);
  545. UNREFERENCED_PARAMETER(ServiceContext);
  546. LOCAL_APIC(LU_TPR) = APIC_REBOOT_VECTOR;
  547. //
  548. // EOI the local APIC. Warm reset does not reset the 82489 APIC
  549. // so if we don't EOI here we'll never see an interrupt after the
  550. // reboot.
  551. //
  552. LOCAL_APIC(LU_EOI) = 0;
  553. //
  554. // Reset this processor. This function will not return.
  555. //
  556. HalpResetThisProcessor();
  557. ASSERT(FALSE);
  558. return TRUE;
  559. }
  560. BOOLEAN
  561. HalpBroadcastCallService (
  562. IN PKINTERRUPT Interrupt,
  563. IN PVOID ServiceContext
  564. )
  565. /*++
  566. Routine Description:
  567. This is the ISR that handles broadcast call interrupts.
  568. Arguments:
  569. Interrupt - Supplies a pointer to the kernel interrupt object
  570. ServiceContext - Supplies the service context
  571. Return Value:
  572. TRUE
  573. --*/
  574. {
  575. UNREFERENCED_PARAMETER(Interrupt);
  576. UNREFERENCED_PARAMETER(ServiceContext);
  577. HalpPollForBroadcast();
  578. return TRUE;
  579. }
  580. BOOLEAN
  581. HalpIpiHandler (
  582. IN PKINTERRUPT Interrupt,
  583. IN PVOID ServiceContext
  584. )
  585. /*++
  586. Routine Description:
  587. This routine is entered as the result of an interrupt generated by
  588. interprocessor communication.
  589. Arguments:
  590. Interrupt - Supplies a pointer to the kernel interrupt object
  591. ServiceContext - Supplies the service context
  592. Return Value:
  593. TRUE
  594. --*/
  595. {
  596. UNREFERENCED_PARAMETER(Interrupt);
  597. KiIpiServiceRoutine(Interrupt->TrapFrame,NULL);
  598. return TRUE;
  599. }
  600. BOOLEAN
  601. HalpLocalApicErrorService (
  602. IN PKINTERRUPT Interrupt,
  603. IN PVOID ServiceContext
  604. )
  605. /*++
  606. Routine Description:
  607. This routine is entered as the result of an interrupt generated by
  608. a local apic error. It clears the error and, if apic error logging
  609. is turned on, records information about the error.
  610. Arguments:
  611. Interrupt - Supplies a pointer to the kernel interrupt object
  612. ServiceContext - Supplies the service context
  613. Return Value:
  614. TRUE
  615. --*/
  616. {
  617. ULONG flags;
  618. PAPIC_ERROR apicError;
  619. ULONG index;
  620. ULONG errorStatus;
  621. PKPCR pcr;
  622. #if LogApicErrors
  623. //
  624. // Take the apic error log lock, get a pointer to the next available
  625. // error log slot, and increment the error count.
  626. //
  627. flags = HalpAcquireHighLevelLock(&HalpLocalApicErrorLock);
  628. index = HalpLocalApicErrorCount % APIC_ERROR_LOG_SIZE;
  629. apicError = &HalpApicErrorLog[index];
  630. HalpLocalApicErrorCount += 1;
  631. #endif
  632. //
  633. // The Apic EDS (Rev 4.0) says you have to write before you read.
  634. // This doesn't work. The write clears the status bits, but the P6 works
  635. // according to the EDS.
  636. //
  637. // For AMD64, for now assume that things work according to the EDS spec.
  638. //
  639. LOCAL_APIC(LU_ERROR_STATUS) = 0;
  640. errorStatus = LOCAL_APIC(LU_ERROR_STATUS);
  641. #if LogApicErrors
  642. //
  643. // Fill in the error log and release the apic error log lock.
  644. //
  645. pcr = KeGetPcr();
  646. apicError->AsByte = (UCHAR)errorStatus;
  647. apicError->Processor = pcr->Number;
  648. HalpReleaseHighLevelLock(&HalpLocalApicErrorLock,flags);
  649. #endif
  650. return TRUE;
  651. }
  652. BOOLEAN
  653. PicNopHandlerInt (
  654. IN PKINTERRUPT Interrupt,
  655. IN PVOID Context
  656. )
  657. /*++
  658. Routine Description:
  659. This handler is designed to be installed on a system to field any PIC
  660. interrupts when there are not supposed to be any delivered.
  661. This routine EOIs the PIC and returns.
  662. Arguments:
  663. Interrupt - Supplies a pointer to the kernel interrupt object
  664. ServiceContext - Supplies the service context
  665. Return Value:
  666. TRUE
  667. --*/
  668. {
  669. UCHAR irq;
  670. AMD64_COVERAGE_TRAP();
  671. //
  672. // Context is the PIC IRQ
  673. //
  674. ASSERT((ULONG_PTR)Context <= 15);
  675. irq = (UCHAR)(ULONG_PTR)(Context);
  676. if (irq <= 7) {
  677. WRITE_PORT_UCHAR(PIC1_PORT0,irq | OCW2_SPECIFIC_EOI);
  678. } else {
  679. if (irq == 0x0D) {
  680. WRITE_PORT_UCHAR(I386_80387_BUSY_PORT, 0);
  681. }
  682. WRITE_PORT_UCHAR(PIC2_PORT0,OCW2_NON_SPECIFIC_EOI);
  683. WRITE_PORT_UCHAR(PIC1_PORT0,OCW2_SPECIFIC_EOI | PIC_SLAVE_IRQ);
  684. }
  685. return TRUE;
  686. }
  687. BOOLEAN
  688. PicInterruptHandlerInt (
  689. IN PKINTERRUPT Interrupt,
  690. IN PVOID Context
  691. )
  692. /*++
  693. Routine Description:
  694. These handlers receive interrupts from the PIC and reissues them via a
  695. vector at the proper priority level. This is used to provide a symetric
  696. interrupt distribution on a non symetric system.
  697. The PIC interrupts will normally only be received (in the PC+MP Hal) via
  698. an interrupt input from on either the IO Unit or the Local unit which has
  699. been programed as EXTINT. EXTINT interrupts are received outside of the
  700. APIC priority structure (the PIC provides the vector). We use the APIC
  701. ICR to generate interrupts to the proper handler at the proper priority.
  702. The EXTINT interrupts are directed to a single processor, currently P0.
  703. There is no good reason why they can't be directed to another processor.
  704. Since one processor must absorb the overhead of redistributing PIC
  705. interrupts the interrupt handling on a system using EXTINT interrupts is
  706. not symetric.
  707. Arguments:
  708. Interrupt - Supplies a pointer to the kernel interrupt object
  709. ServiceContext - Supplies the service context
  710. Return Value:
  711. TRUE
  712. --*/
  713. {
  714. UCHAR irq;
  715. UCHAR isrRegister;
  716. UCHAR ipiVector;
  717. AMD64_COVERAGE_TRAP();
  718. //
  719. // Context is the PIC IRQ
  720. //
  721. ASSERT((ULONG_PTR)Context <= 15);
  722. irq = (UCHAR)(ULONG_PTR)(Context);
  723. if (irq == 7) {
  724. //
  725. // Check to see if this is a spurious interrupt
  726. //
  727. WRITE_PORT_UCHAR(PIC1_PORT0,OCW3_READ_ISR);
  728. IO_DELAY();
  729. isrRegister = READ_PORT_UCHAR(PIC1_PORT0);
  730. if ((isrRegister & 0x80) == 0) {
  731. //
  732. // Spurious.
  733. //
  734. return TRUE;
  735. }
  736. }
  737. if (irq == 0x0D) {
  738. WRITE_PORT_UCHAR(I386_80387_BUSY_PORT,0);
  739. } else if (irq == 0x1F) {
  740. WRITE_PORT_UCHAR(PIC2_PORT0,OCW3_READ_ISR);
  741. IO_DELAY();
  742. isrRegister = READ_PORT_UCHAR(PIC2_PORT0);
  743. if ((isrRegister & 0x80) == 0) {
  744. //
  745. // Spurious.
  746. //
  747. return TRUE;
  748. }
  749. }
  750. if (irq <= 7) {
  751. //
  752. // Master PIC
  753. //
  754. WRITE_PORT_UCHAR(PIC1_PORT0,irq | OCW2_SPECIFIC_EOI);
  755. } else {
  756. //
  757. // Slave PIC
  758. //
  759. WRITE_PORT_UCHAR(PIC2_PORT0,OCW2_NON_SPECIFIC_EOI);
  760. WRITE_PORT_UCHAR(PIC1_PORT0,OCW2_SPECIFIC_EOI | PIC_SLAVE_IRQ);
  761. }
  762. ipiVector = HalpPICINTToVector[irq];
  763. if (ipiVector != 0) {
  764. HalpStallWhileApicBusy();
  765. if (irq == 8) {
  766. //
  767. // Clock interrupt
  768. //
  769. LOCAL_APIC(LU_INT_CMD_LOW) =
  770. DELIVER_FIXED | ICR_SELF | APIC_CLOCK_VECTOR;
  771. } else {
  772. //
  773. // Write the IPI command to the Memory Mapped Register
  774. //
  775. LOCAL_APIC(LU_INT_CMD_HIGH) = DESTINATION_ALL_CPUS;
  776. LOCAL_APIC(LU_INT_CMD_LOW) = ipiVector;
  777. }
  778. }
  779. return TRUE;
  780. }