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.

856 lines
17 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. //
  16. // External functions
  17. //
  18. VOID
  19. HalpResetThisProcessor (
  20. VOID
  21. );
  22. VOID
  23. HalpSendIpi (
  24. IN KAFFINITY Affinity,
  25. IN ULONG Command
  26. );
  27. ULONG
  28. DetectAcpiMP (
  29. OUT PBOOLEAN IsConfiguredMp,
  30. IN PLOADER_PARAMETER_BLOCK LoaderBlock
  31. );
  32. //
  33. // External data
  34. //
  35. extern BOOLEAN HalpStaticIntAffinity;
  36. extern UCHAR rgzBadHal[];
  37. extern UCHAR HalpPICINTToVector[16];
  38. //
  39. // Local types
  40. //
  41. typedef
  42. VOID
  43. (*HAL_GENERIC_IPI_FUNCTION) (
  44. ULONG_PTR Context
  45. );
  46. //
  47. // Local prototypes
  48. //
  49. VOID
  50. HalInitApicInterruptHandlers(
  51. VOID
  52. );
  53. //
  54. // Local data and defines
  55. //
  56. ULONG_PTR HalpBroadcastContext;
  57. HAL_GENERIC_IPI_FUNCTION HalpBroadcastFunction;
  58. KSPIN_LOCK HalpBroadcastLock;
  59. KAFFINITY volatile HalpBroadcastTargets;
  60. PKPCR HalpProcessorPCR[MAXIMUM_PROCESSORS];
  61. //
  62. // HalpGlobal8259Mask is used to avoid reading the PIC to get the current
  63. // interrupt mask; format is the same as for SET_8259_MASK, i.i.,
  64. // bits 7:0 -> PIC1, 15:8 -> PIC2
  65. //
  66. USHORT HalpGlobal8259Mask = 0;
  67. #define GENERIC_IPI (DELIVER_FIXED | LOGICAL_DESTINATION | ICR_USE_DEST_FIELD | APIC_GENERIC_VECTOR)
  68. //
  69. // Globals and constants used to log local apic errors
  70. //
  71. #define LogApicErrors TRUE
  72. #if LogApicErrors
  73. //
  74. // Structure defining the layout of an apic error record.
  75. //
  76. typedef struct _APIC_ERROR {
  77. union {
  78. struct {
  79. UCHAR SendChecksum:1;
  80. UCHAR ReceiveChecksum:1;
  81. UCHAR SendAccept:1;
  82. UCHAR ReceiveAccept:1;
  83. UCHAR Reserved1:1;
  84. UCHAR SendVector:1;
  85. UCHAR ReceiveVector:1;
  86. UCHAR RegisterAddress:1;
  87. };
  88. UCHAR AsByte;
  89. };
  90. UCHAR Processor;
  91. } APIC_ERROR, *PAPIC_ERROR;
  92. #define APIC_ERROR_LOG_SIZE 128
  93. //
  94. // Count of local apic errors.
  95. //
  96. ULONG HalpLocalApicErrorCount = 0;
  97. //
  98. // Apic error log. This is circular, indexed by
  99. // HalpLocalApicErrorCount % APIC_ERROR_LOG_SIZE.
  100. //
  101. APIC_ERROR HalpApicErrorLog[APIC_ERROR_LOG_SIZE];
  102. //
  103. // Spinlock used to protect access to HalpLocalApicErrorCount.
  104. //
  105. KSPIN_LOCK HalpLocalApicErrorLock;
  106. #endif
  107. VOID
  108. HalInitializeProcessor(
  109. ULONG ProcessorNumber,
  110. PLOADER_PARAMETER_BLOCK LoaderBlock
  111. )
  112. /*++
  113. Routine Description:
  114. Initialize hal pcr values for current processor (if any)
  115. (called shortly after processor reaches kernel, before
  116. HalInitSystem if P0)
  117. IPI's and KeReadir/LowerIrq's must be available once this function
  118. returns. (IPI's are only used once two or more processors are
  119. available)
  120. . Enable IPI interrupt (makes sense for P1, P2, ...).
  121. . Save Processor Number in PCR.
  122. . if (P0)
  123. . determine if the system is a PC+MP,
  124. . if not a PC+MP System Halt;
  125. . Enable IPI's on CPU.
  126. Arguments:
  127. Number - Logical processor number of calling processor
  128. Return Value:
  129. None.
  130. --*/
  131. {
  132. PKPCR pcr;
  133. KAFFINITY affinity;
  134. KAFFINITY oldAffinity;
  135. ULONG detectAcpiResult;
  136. BOOLEAN isMp;
  137. affinity = (KAFFINITY)1 << ProcessorNumber;
  138. pcr = KeGetPcr();
  139. //
  140. // Mark all interrupts as disabled, and store the processor number and
  141. // the default stall scale factor in the pcr.
  142. //
  143. pcr->Idr = 0xFFFFFFFF;
  144. pcr->Number = (UCHAR)ProcessorNumber;
  145. pcr->StallScaleFactor = INITIAL_STALL_COUNT;
  146. //
  147. // Record the pcr pointer in our lookup table and set the affinity
  148. // bit in our set of active processors.
  149. //
  150. HalpProcessorPCR[ProcessorNumber] = pcr;
  151. HalpActiveProcessors |= affinity;
  152. if (HalpStaticIntAffinity == 0) {
  153. //
  154. // Interrupts can go to any processor
  155. //
  156. HalpDefaultInterruptAffinity |= affinity;
  157. } else {
  158. //
  159. // Interrupts go only to the highest numbered processor
  160. //
  161. if (HalpDefaultInterruptAffinity < affinity) {
  162. HalpDefaultInterruptAffinity = affinity;
  163. }
  164. }
  165. if (ProcessorNumber == 0) {
  166. KeInitializeSpinLock(&HalpBroadcastLock);
  167. #if LogApicErrors
  168. KeInitializeSpinLock(&HalpLocalApicErrorLock);
  169. #endif
  170. //
  171. // Determine whether the system we are on is an MPS system.
  172. //
  173. // DetectAcpiMP has a parameter we don't currently use. It's a boolean
  174. // which is set to TRUE if the system we're on is an MP system.
  175. // We could have a UP MPS system.
  176. //
  177. // The DetectAcpiMP routine also allocates virtual addresses for all of
  178. // the APICs in the system.
  179. //
  180. detectAcpiResult = DetectAcpiMP(&isMp,LoaderBlock);
  181. if (detectAcpiResult == FALSE) {
  182. HalDisplayString(rgzBadHal);
  183. HalpDisableInterrupts();
  184. while (TRUE) {
  185. HalpHalt();
  186. }
  187. }
  188. HalpRegisterKdSupportFunctions(LoaderBlock);
  189. //
  190. // Mask all PIC interrupts
  191. //
  192. HalpGlobal8259Mask = 0xFFFF;
  193. SET_8259_MASK(HalpGlobal8259Mask);
  194. }
  195. //
  196. // All processors execute this code
  197. //
  198. HalInitApicInterruptHandlers();
  199. HalpInitializeLocalUnit();
  200. }
  201. VOID
  202. HalInitApicInterruptHandlers(
  203. VOID
  204. )
  205. /*++
  206. Routine Description:
  207. This routine installs the interrupt vector in the IDT for the APIC
  208. spurious interrupt.
  209. Arguments:
  210. None.
  211. Return Value:
  212. None.
  213. --*/
  214. {
  215. PKPCR pcr;
  216. PKIDTENTRY64 idt;
  217. KiSetHandlerAddressToIDTIrql(PIC1_SPURIOUS_VECTOR,
  218. PicSpuriousService37,
  219. NULL,
  220. 0);
  221. KiSetHandlerAddressToIDTIrql(APIC_SPURIOUS_VECTOR,
  222. HalpApicSpuriousService,
  223. NULL,
  224. 0);
  225. }
  226. __forceinline
  227. VOID
  228. HalpPollForBroadcast (
  229. VOID
  230. )
  231. /*++
  232. Routine Description:
  233. Checks whether the current processor has a broadcast function pending
  234. and, if so, clears it's pending bit and calls the function.
  235. Arguments:
  236. None.
  237. Return Value:
  238. None.
  239. --*/
  240. {
  241. KAFFINITY affinity;
  242. ULONG_PTR broadcastContext;
  243. HAL_GENERIC_IPI_FUNCTION broadcastFunction;
  244. KAFFINITY broadcastTargets;
  245. affinity = KeGetPcr()->CurrentPrcb->SetMember;
  246. if ((HalpBroadcastTargets & affinity) != 0) {
  247. //
  248. // A generic IPI call appears to be pending for this processor.
  249. // Pick up the function pointer and context locally.
  250. //
  251. broadcastFunction = HalpBroadcastFunction;
  252. broadcastContext = HalpBroadcastContext;
  253. //
  254. // Atomically acknowledge the broadcast. If the broadcast is still
  255. // pending for this processor, then call it.
  256. //
  257. broadcastTargets = InterlockedAnd64(&HalpBroadcastTargets,~affinity);
  258. if ((broadcastTargets & affinity) != 0) {
  259. broadcastFunction(broadcastContext);
  260. }
  261. }
  262. }
  263. VOID
  264. HalpGenericCall(
  265. IN HAL_GENERIC_IPI_FUNCTION BroadcastFunction,
  266. IN ULONG Context,
  267. IN KAFFINITY TargetProcessors
  268. )
  269. /*++
  270. Routine Description:
  271. Causes the WorkerFunction to be called on the specified target
  272. processors. The WorkerFunction is called at CLOCK2_LEVEL-1
  273. (Must be below IPI_LEVEL in order to prevent system deadlocks).
  274. Enviroment:
  275. Must be called with interrupts enabled.
  276. Must be called with IRQL = CLOCK2_LEVEL-1
  277. --*/
  278. {
  279. //
  280. // Nothing to do if no target processors have been specified.
  281. //
  282. if (TargetProcessors == 0) {
  283. return;
  284. }
  285. //
  286. // Acquire the broadcast lock, polling for broadcasts while spinning.
  287. //
  288. while (KeTryToAcquireSpinLockAtDpcLevel(&HalpBroadcastLock) == FALSE) {
  289. do {
  290. HalpPollForBroadcast();
  291. } while (KeTestSpinLock(&HalpBroadcastLock) == FALSE);
  292. }
  293. //
  294. // We own the broadcast lock. Store the broadcast parameters
  295. // into the broadcast prameters and send the generic IPI.
  296. //
  297. HalpBroadcastFunction = BroadcastFunction;
  298. HalpBroadcastContext = Context;
  299. HalpBroadcastTargets = TargetProcessors;
  300. HalpSendIpi(TargetProcessors,GENERIC_IPI);
  301. //
  302. // Wait for all processors to pick up the IPI and process the generic
  303. // call, then release the broadcast lock.
  304. //
  305. do {
  306. HalpPollForBroadcast();
  307. } while (HalpBroadcastTargets != 0);
  308. KeReleaseSpinLockFromDpcLevel(&HalpBroadcastLock);
  309. }
  310. ULONG
  311. HalpWaitForPending (
  312. IN ULONG Count,
  313. IN ULONG volatile *ICR
  314. )
  315. /*++
  316. Routine Description:
  317. Spins waiting for the DELIVERY_PENDING bit in the ICR to clear or
  318. until spinning Count times.
  319. Arguments:
  320. Count - Number of times through the loop before giving up.
  321. ICR - Pointer to the ICR register containing the DELIVERY_PENDING
  322. status bit.
  323. Return Value:
  324. Zero if the DELIVERY_PENDING bit has cleared within the number of
  325. test cycles, non-zero otherwise.
  326. --*/
  327. {
  328. ULONG countRemaining;
  329. countRemaining = Count;
  330. while (countRemaining > 0) {
  331. if ((*ICR & DELIVERY_PENDING) == 0) {
  332. break;
  333. }
  334. countRemaining -= 1;
  335. }
  336. return countRemaining;
  337. }
  338. BOOLEAN
  339. HalpApicRebootService (
  340. IN PKINTERRUPT Interrupt,
  341. IN PVOID ServiceContext
  342. )
  343. /*++
  344. Routine Description:
  345. This is the ISR that handles Reboot interrupts.
  346. Arguments:
  347. Interrupt - Supplies a pointer to the kernel interrupt object
  348. ServiceContext - Supplies the service context
  349. Return Value:
  350. None. This routine does not return.
  351. --*/
  352. {
  353. UNREFERENCED_PARAMETER(Interrupt);
  354. UNREFERENCED_PARAMETER(ServiceContext);
  355. LOCAL_APIC(LU_TPR) = APIC_REBOOT_VECTOR;
  356. //
  357. // EOI the local APIC. Warm reset does not reset the 82489 APIC
  358. // so if we don't EOI here we'll never see an interrupt after the
  359. // reboot.
  360. //
  361. LOCAL_APIC(LU_EOI) = 0;
  362. //
  363. // Reset this processor. This function will not return.
  364. //
  365. HalpResetThisProcessor();
  366. ASSERT(FALSE);
  367. return TRUE;
  368. }
  369. BOOLEAN
  370. HalpBroadcastCallService (
  371. IN PKINTERRUPT Interrupt,
  372. IN PVOID ServiceContext
  373. )
  374. /*++
  375. Routine Description:
  376. This is the ISR that handles broadcast call interrupts.
  377. Arguments:
  378. Interrupt - Supplies a pointer to the kernel interrupt object
  379. ServiceContext - Supplies the service context
  380. Return Value:
  381. TRUE
  382. --*/
  383. {
  384. UNREFERENCED_PARAMETER(Interrupt);
  385. UNREFERENCED_PARAMETER(ServiceContext);
  386. HalpPollForBroadcast();
  387. return TRUE;
  388. }
  389. BOOLEAN
  390. HalpIpiHandler (
  391. IN PKINTERRUPT Interrupt,
  392. IN PVOID ServiceContext
  393. )
  394. /*++
  395. Routine Description:
  396. This routine is entered as the result of an interrupt generated by
  397. interprocessor communication.
  398. Arguments:
  399. Interrupt - Supplies a pointer to the kernel interrupt object
  400. ServiceContext - Supplies the service context
  401. Return Value:
  402. TRUE
  403. --*/
  404. {
  405. UNREFERENCED_PARAMETER(Interrupt);
  406. KeIpiInterrupt(Interrupt->TrapFrame);
  407. return TRUE;
  408. }
  409. BOOLEAN
  410. HalpLocalApicErrorService (
  411. IN PKINTERRUPT Interrupt,
  412. IN PVOID ServiceContext
  413. )
  414. /*++
  415. Routine Description:
  416. This routine is entered as the result of an interrupt generated by
  417. a local apic error. It clears the error and, if apic error logging
  418. is turned on, records information about the error.
  419. Arguments:
  420. Interrupt - Supplies a pointer to the kernel interrupt object
  421. ServiceContext - Supplies the service context
  422. Return Value:
  423. TRUE
  424. --*/
  425. {
  426. ULONG flags;
  427. PAPIC_ERROR apicError;
  428. ULONG index;
  429. ULONG errorStatus;
  430. PKPCR pcr;
  431. #if LogApicErrors
  432. //
  433. // Take the apic error log lock, get a pointer to the next available
  434. // error log slot, and increment the error count.
  435. //
  436. flags = HalpAcquireHighLevelLock(&HalpLocalApicErrorLock);
  437. index = HalpLocalApicErrorCount % APIC_ERROR_LOG_SIZE;
  438. apicError = &HalpApicErrorLog[index];
  439. HalpLocalApicErrorCount += 1;
  440. #endif
  441. //
  442. // The Apic EDS (Rev 4.0) says you have to write before you read.
  443. // This doesn't work. The write clears the status bits, but the P6 works
  444. // according to the EDS.
  445. //
  446. // For AMD64, for now assume that things work according to the EDS spec.
  447. //
  448. LOCAL_APIC(LU_ERROR_STATUS) = 0;
  449. errorStatus = LOCAL_APIC(LU_ERROR_STATUS);
  450. #if LogApicErrors
  451. //
  452. // Fill in the error log and release the apic error log lock.
  453. //
  454. pcr = KeGetPcr();
  455. apicError->AsByte = (UCHAR)errorStatus;
  456. apicError->Processor = pcr->Number;
  457. HalpReleaseHighLevelLock(&HalpLocalApicErrorLock,flags);
  458. #endif
  459. return TRUE;
  460. }
  461. BOOLEAN
  462. PicNopHandlerInt (
  463. IN PKINTERRUPT Interrupt,
  464. IN PVOID Context
  465. )
  466. /*++
  467. Routine Description:
  468. This handler is designed to be installed on a system to field any PIC
  469. interrupts when there are not supposed to be any delivered.
  470. This routine EOIs the PIC and returns.
  471. Arguments:
  472. Interrupt - Supplies a pointer to the kernel interrupt object
  473. ServiceContext - Supplies the service context
  474. Return Value:
  475. TRUE
  476. --*/
  477. {
  478. UCHAR irq;
  479. AMD64_COVERAGE_TRAP();
  480. //
  481. // Context is the PIC IRQ
  482. //
  483. ASSERT((ULONG_PTR)Context <= 15);
  484. irq = (UCHAR)(ULONG_PTR)(Context);
  485. if (irq <= 7) {
  486. WRITE_PORT_UCHAR(PIC1_PORT0,irq | OCW2_SPECIFIC_EOI);
  487. } else {
  488. if (irq == 0x0D) {
  489. WRITE_PORT_UCHAR(I386_80387_BUSY_PORT, 0);
  490. }
  491. WRITE_PORT_UCHAR(PIC2_PORT0,OCW2_NON_SPECIFIC_EOI);
  492. WRITE_PORT_UCHAR(PIC1_PORT0,OCW2_SPECIFIC_EOI | PIC_SLAVE_IRQ);
  493. }
  494. return TRUE;
  495. }
  496. BOOLEAN
  497. PicInterruptHandlerInt (
  498. IN PKINTERRUPT Interrupt,
  499. IN PVOID Context
  500. )
  501. /*++
  502. Routine Description:
  503. These handlers receive interrupts from the PIC and reissues them via a
  504. vector at the proper priority level. This is used to provide a symetric
  505. interrupt distribution on a non symetric system.
  506. The PIC interrupts will normally only be received (in the PC+MP Hal) via
  507. an interrupt input from on either the IO Unit or the Local unit which has
  508. been programed as EXTINT. EXTINT interrupts are received outside of the
  509. APIC priority structure (the PIC provides the vector). We use the APIC
  510. ICR to generate interrupts to the proper handler at the proper priority.
  511. The EXTINT interrupts are directed to a single processor, currently P0.
  512. There is no good reason why they can't be directed to another processor.
  513. Since one processor must absorb the overhead of redistributing PIC
  514. interrupts the interrupt handling on a system using EXTINT interrupts is
  515. not symetric.
  516. Arguments:
  517. Interrupt - Supplies a pointer to the kernel interrupt object
  518. ServiceContext - Supplies the service context
  519. Return Value:
  520. TRUE
  521. --*/
  522. {
  523. UCHAR irq;
  524. UCHAR isrRegister;
  525. UCHAR ipiVector;
  526. AMD64_COVERAGE_TRAP();
  527. //
  528. // Context is the PIC IRQ
  529. //
  530. ASSERT((ULONG_PTR)Context <= 15);
  531. irq = (UCHAR)(ULONG_PTR)(Context);
  532. if (irq == 7) {
  533. //
  534. // Check to see if this is a spurious interrupt
  535. //
  536. WRITE_PORT_UCHAR(PIC1_PORT0,OCW3_READ_ISR);
  537. IO_DELAY();
  538. isrRegister = READ_PORT_UCHAR(PIC1_PORT0);
  539. if ((isrRegister & 0x80) == 0) {
  540. //
  541. // Spurious.
  542. //
  543. return TRUE;
  544. }
  545. }
  546. if (irq == 0x0D) {
  547. WRITE_PORT_UCHAR(I386_80387_BUSY_PORT,0);
  548. } else if (irq == 0x1F) {
  549. WRITE_PORT_UCHAR(PIC2_PORT0,OCW3_READ_ISR);
  550. IO_DELAY();
  551. isrRegister = READ_PORT_UCHAR(PIC2_PORT0);
  552. if ((isrRegister & 0x80) == 0) {
  553. //
  554. // Spurious.
  555. //
  556. return TRUE;
  557. }
  558. }
  559. if (irq <= 7) {
  560. //
  561. // Master PIC
  562. //
  563. WRITE_PORT_UCHAR(PIC1_PORT0,irq | OCW2_SPECIFIC_EOI);
  564. } else {
  565. //
  566. // Slave PIC
  567. //
  568. WRITE_PORT_UCHAR(PIC2_PORT0,OCW2_NON_SPECIFIC_EOI);
  569. WRITE_PORT_UCHAR(PIC1_PORT0,OCW2_SPECIFIC_EOI | PIC_SLAVE_IRQ);
  570. }
  571. ipiVector = HalpPICINTToVector[irq];
  572. if (ipiVector != 0) {
  573. HalpStallWhileApicBusy();
  574. if (irq == 8) {
  575. //
  576. // Clock interrupt
  577. //
  578. LOCAL_APIC(LU_INT_CMD_LOW) =
  579. DELIVER_FIXED | ICR_SELF | APIC_CLOCK_VECTOR;
  580. } else {
  581. //
  582. // Write the IPI command to the Memory Mapped Register
  583. //
  584. LOCAL_APIC(LU_INT_CMD_HIGH) = DESTINATION_ALL_CPUS;
  585. LOCAL_APIC(LU_INT_CMD_LOW) = ipiVector;
  586. }
  587. }
  588. return TRUE;
  589. }