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.

1610 lines
38 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Copyright (c) 1992 Intel Corporation
  4. All rights reserved
  5. INTEL CORPORATION PROPRIETARY INFORMATION
  6. This software is supplied to Microsoft under the terms
  7. of a license agreement with Intel Corporation and may not be
  8. copied nor disclosed except in accordance with the terms
  9. of that agreement.
  10. Module Name:
  11. mpsys.c
  12. Abstract:
  13. This module implements the initialization of the system dependent
  14. functions that define the Hardware Architecture Layer (HAL) for a
  15. PC+MP system.
  16. Author:
  17. Ron Mosgrove (Intel)
  18. Environment:
  19. Kernel mode only.
  20. Revision History:
  21. */
  22. #include "halp.h"
  23. #include "apic.inc"
  24. #include "pcmp_nt.inc"
  25. #define STATIC // functions used internal to this module
  26. HAL_INTERRUPT_SERVICE_PROTOTYPE(HalpApicSpuriousService);
  27. HAL_INTERRUPT_SERVICE_PROTOTYPE(HalpLocalApicErrorService);
  28. VOID
  29. HalpBuildIpiDestinationMap (
  30. VOID
  31. );
  32. VOID
  33. HalpInitializeLocalUnit (
  34. VOID
  35. );
  36. STATIC UCHAR
  37. HalpPcMpIoApicById (
  38. IN UCHAR IoApicId
  39. );
  40. UCHAR
  41. HalpAddInterruptDest(
  42. IN UCHAR CurrentDest,
  43. IN UCHAR ThisCpu
  44. );
  45. UCHAR
  46. HalpRemoveInterruptDest(
  47. IN UCHAR CurrentDest,
  48. IN UCHAR ThisCpu
  49. );
  50. UCHAR
  51. HalpMapNtToHwProcessorId(
  52. IN UCHAR Number
  53. );
  54. VOID
  55. HalpRestoreIoApicRedirTable (
  56. VOID
  57. );
  58. #if defined(_AMD64_)
  59. BOOLEAN
  60. PicNopHandlerInt (
  61. IN PKINTERRUPT Interrupt,
  62. IN PVOID Context
  63. );
  64. BOOLEAN
  65. PicInterruptHandlerInt (
  66. IN PKINTERRUPT Interrupt,
  67. IN PVOID Context
  68. );
  69. #endif
  70. KAFFINITY HalpNodeAffinity[MAX_NODES];
  71. ULONG HalpMaxNode = 1;
  72. //
  73. // Counters used to determine the number of interrupt enables that
  74. // require the Local APIC Lint0 Extint enabled
  75. //
  76. UCHAR Halp8259Counts[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  77. //
  78. // All possible I/O APIC Sources, arranged linearly from first I/O APIC to
  79. // last. Divisions between I/O APICs are implied by HalpMaxApicInti[N]
  80. //
  81. INTI_INFO HalpIntiInfo[MAX_INTI];
  82. //
  83. // Number of sources in I/O APIC [n]
  84. //
  85. USHORT HalpMaxApicInti[MAX_IOAPICS];
  86. INTERRUPT_DEST HalpIntDestMap[MAX_PROCESSORS];
  87. extern BOOLEAN HalpHiberInProgress;
  88. #ifdef ALLOC_PRAGMA
  89. #pragma alloc_text(INIT, HalpCheckELCR)
  90. #pragma alloc_text(PAGELK, HalpInitializeIOUnits)
  91. #pragma alloc_text(PAGELK, HalpInitializeLocalUnit)
  92. #pragma alloc_text(PAGELK, HalpEnableNMI)
  93. #pragma alloc_text(PAGELK, HalpEnablePerfInterupt)
  94. #pragma alloc_text(PAGELK, HalpRestoreIoApicRedirTable)
  95. #pragma alloc_text(PAGELK, HalpUnMapIOApics)
  96. #pragma alloc_text(PAGELK, HalpPostSleepMP)
  97. #endif
  98. //
  99. // BEWARE -- this has to match the structure ADDRESS_USAGE.
  100. #pragma pack(push, 1)
  101. struct {
  102. struct _HalAddressUsage *Next;
  103. CM_RESOURCE_TYPE Type;
  104. UCHAR Flags;
  105. struct {
  106. ULONG Start;
  107. ULONG Length;
  108. } Element[MAX_IOAPICS+2];
  109. } HalpApicUsage;
  110. #pragma pack(pop)
  111. VOID
  112. HalpCheckELCR (
  113. VOID
  114. )
  115. {
  116. USHORT elcr;
  117. ULONG IsaIrq;
  118. USHORT Inti;
  119. if (HalpELCRChecked) {
  120. return ;
  121. }
  122. HalpELCRChecked = TRUE;
  123. //
  124. // It turns out interrupts which are fed through the ELCR before
  125. // going to the IOAPIC get inverted. So... here we *assume*
  126. // the polarity of any ELCR level inti not declared in the MPS linti
  127. // table as being active_high instead of what they should be (which
  128. // is active_low). Any system which correctly delivers these intis
  129. // to an IOAPIC will need to declared the correct polarity in the
  130. // MPS table.
  131. //
  132. elcr = READ_PORT_UCHAR ((PUCHAR) 0x4d1) << 8 | READ_PORT_UCHAR((PUCHAR) 0x4d0);
  133. if (elcr == 0xffff) {
  134. return ;
  135. }
  136. for (IsaIrq = 0; elcr; IsaIrq++, elcr >>= 1) {
  137. if (!(elcr & 1)) {
  138. continue;
  139. }
  140. if (HalpGetApicInterruptDesc (Eisa, 0, IsaIrq, &Inti)) {
  141. //
  142. // If the MPS passed Polarity for this Inti
  143. // is "bus default" change it to be "active high".
  144. //
  145. if (HalpIntiInfo[Inti].Polarity == 0) {
  146. HalpIntiInfo[Inti].Polarity = 1;
  147. }
  148. }
  149. }
  150. }
  151. STATIC VOID
  152. HalpSetRedirEntry (
  153. IN USHORT InterruptInput,
  154. IN ULONG Entry,
  155. IN ULONG Destination
  156. )
  157. /*++
  158. Routine Description:
  159. This procedure sets a IO Unit Redirection Table Entry
  160. Arguments:
  161. IoUnit - The IO Unit to modify (zero Based)
  162. InterruptInput - The input line we're interested in
  163. Entry - the lower 32 bits of the redir table
  164. Destination - the upper 32 bits on the entry
  165. Return Value:
  166. None.
  167. --*/
  168. {
  169. struct ApicIoUnit *IoUnitPtr;
  170. ULONG RedirRegister;
  171. UCHAR IoUnit;
  172. for (IoUnit=0; IoUnit < MAX_IOAPICS; IoUnit++) {
  173. if (InterruptInput+1 <= HalpMaxApicInti[IoUnit]) {
  174. break;
  175. }
  176. InterruptInput -= HalpMaxApicInti[IoUnit];
  177. }
  178. ASSERT (IoUnit < MAX_IOAPICS);
  179. IoUnitPtr = (struct ApicIoUnit *) HalpMpInfoTable.IoApicBase[IoUnit];
  180. RedirRegister = InterruptInput*2 + IO_REDIR_00_LOW;
  181. IoUnitPtr->RegisterSelect = RedirRegister+1;
  182. IoUnitPtr->RegisterWindow = Destination;
  183. IoUnitPtr->RegisterSelect = RedirRegister;
  184. IoUnitPtr->RegisterWindow = Entry;
  185. }
  186. STATIC VOID
  187. HalpGetRedirEntry (
  188. IN USHORT InterruptInput,
  189. IN PULONG Entry,
  190. IN PULONG Destination
  191. )
  192. /*++
  193. Routine Description:
  194. This procedure sets a IO Unit Redirection Table Entry
  195. Arguments:
  196. IoUnit - The IO Unit to modify (zero Based)
  197. InterruptInput - The input line we're interested in
  198. Entry - the lower 32 bits of the redir table
  199. Destination - the upper 32 bits on the entry
  200. Return Value:
  201. None.
  202. --*/
  203. {
  204. struct ApicIoUnit *IoUnitPtr;
  205. ULONG RedirRegister;
  206. UCHAR IoUnit;
  207. for (IoUnit=0; IoUnit < MAX_IOAPICS; IoUnit++) {
  208. if (InterruptInput+1 <= HalpMaxApicInti[IoUnit]) {
  209. break;
  210. }
  211. InterruptInput -= HalpMaxApicInti[IoUnit];
  212. }
  213. ASSERT (IoUnit < MAX_IOAPICS);
  214. IoUnitPtr = (struct ApicIoUnit *) HalpMpInfoTable.IoApicBase[IoUnit];
  215. RedirRegister = InterruptInput*2 + IO_REDIR_00_LOW;
  216. IoUnitPtr->RegisterSelect = RedirRegister+1;
  217. *Destination = IoUnitPtr->RegisterWindow;
  218. IoUnitPtr->RegisterSelect = RedirRegister;
  219. *Entry = IoUnitPtr->RegisterWindow;
  220. }
  221. STATIC VOID
  222. HalpEnableRedirEntry(
  223. IN USHORT InterruptInput,
  224. IN ULONG Entry,
  225. IN UCHAR Cpu
  226. )
  227. /*++
  228. Routine Description:
  229. This procedure enables an interrupt via IO Unit
  230. Redirection Table Entry
  231. Arguments:
  232. InterruptInput - The input line we're interested in
  233. Entry - the lower 32 bits of the redir table
  234. Destination - the upper 32 bits of the entry
  235. Return Value:
  236. None.
  237. --*/
  238. {
  239. ULONG Destination;
  240. //
  241. // bump Enable Count for this INTI
  242. //
  243. HalpIntiInfo[InterruptInput].Entry = (USHORT) Entry;
  244. HalpIntiInfo[InterruptInput].Destinations = (UCHAR)HalpAddInterruptDest(
  245. HalpIntiInfo[InterruptInput].Destinations, Cpu);
  246. Destination = HalpIntiInfo[InterruptInput].Destinations;
  247. Destination = (Destination << DESTINATION_SHIFT);
  248. HalpSetRedirEntry (
  249. InterruptInput,
  250. Entry,
  251. Destination
  252. );
  253. }
  254. VOID
  255. HalpRestoreIoApicRedirTable (
  256. VOID
  257. )
  258. /*++
  259. Routine Description:
  260. This procedure resets any IoApic inti that is enabled for
  261. any processor. This is used during the system wake procedure.
  262. Arguments:
  263. None.
  264. Return Value:
  265. None.
  266. --*/
  267. {
  268. USHORT InterruptInput;
  269. KIRQL OldIrql;
  270. for(InterruptInput=0; InterruptInput < MAX_INTI; InterruptInput++) {
  271. if (HalpIntiInfo[InterruptInput].Destinations) {
  272. HalpSetRedirEntry (
  273. InterruptInput,
  274. HalpIntiInfo[InterruptInput].Entry,
  275. HalpIntiInfo[InterruptInput].Destinations << DESTINATION_SHIFT
  276. );
  277. }
  278. }
  279. }
  280. STATIC VOID
  281. HalpDisableRedirEntry(
  282. IN USHORT InterruptInput,
  283. IN UCHAR Cpu
  284. )
  285. /*++
  286. Routine Description:
  287. This procedure disables a IO Unit Redirection Table Entry
  288. by setting the mask bit in the Redir Entry.
  289. Arguments:
  290. InterruptInput - The input line we're interested in
  291. Return Value:
  292. None.
  293. --*/
  294. {
  295. ULONG Entry;
  296. ULONG Destination;
  297. //
  298. // Turn of the Destination bit for this Cpu
  299. //
  300. HalpIntiInfo[InterruptInput].Destinations = HalpRemoveInterruptDest(
  301. HalpIntiInfo[InterruptInput].Destinations, Cpu);
  302. //
  303. // Get the old entry, the only thing we want is the Entry field
  304. //
  305. HalpGetRedirEntry (
  306. InterruptInput,
  307. &Entry,
  308. &Destination
  309. );
  310. //
  311. // Only perform the disable if we've transitioned to zero enables
  312. //
  313. if ( HalpIntiInfo[InterruptInput].Destinations == 0) {
  314. //
  315. // Disable the interrupt if no Cpu has it enabled
  316. //
  317. Entry |= INTERRUPT_MASKED;
  318. } else {
  319. //
  320. // Create the new destination field sans this Cpu
  321. //
  322. Destination = HalpIntiInfo[InterruptInput].Destinations;
  323. Destination = (Destination << DESTINATION_SHIFT);
  324. }
  325. HalpSetRedirEntry (
  326. InterruptInput,
  327. Entry,
  328. Destination
  329. );
  330. }
  331. VOID
  332. HalpSet8259Mask (
  333. IN USHORT Mask
  334. )
  335. /*++
  336. Routine Description:
  337. This procedure sets the 8259 Mask to the value passed in
  338. Arguments:
  339. Mask - The mask bits to set
  340. Return Value:
  341. None.
  342. --*/
  343. {
  344. WRITE_PORT_UCHAR(UlongToPtr(PIC1_PORT1),(UCHAR)Mask);
  345. WRITE_PORT_UCHAR(UlongToPtr(PIC2_PORT1),(UCHAR)(Mask >> 8));
  346. }
  347. #define PIC1_BASE 0x30
  348. STATIC VOID
  349. SetPicInterruptHandler(
  350. IN USHORT InterruptInput
  351. )
  352. /*++
  353. Routine Description:
  354. This procedure sets a handler for a PIC inti
  355. Arguments:
  356. InterruptInput - The input line we're interested in
  357. Return Value:
  358. None.
  359. --*/
  360. {
  361. #if defined(_AMD64_)
  362. KiSetHandlerAddressToIDTIrql(PIC1_BASE + InterruptInput,
  363. PicInterruptHandlerInt,
  364. (PVOID)(InterruptInput),
  365. HIGH_LEVEL);
  366. #else
  367. extern VOID (*PicExtintIntiHandlers[])(VOID);
  368. VOID (*Hp)(VOID) = PicExtintIntiHandlers[InterruptInput];
  369. KiSetHandlerAddressToIDT(PIC1_BASE + InterruptInput, Hp);
  370. #endif
  371. }
  372. STATIC VOID
  373. ResetPicInterruptHandler(
  374. IN USHORT InterruptInput
  375. )
  376. /*++
  377. Routine Description:
  378. This procedure sets a handler for a PIC inti to a NOP handler
  379. Arguments:
  380. InterruptInput - The input line we're interested in
  381. Return Value:
  382. None.
  383. --*/
  384. {
  385. #if defined(_AMD64_)
  386. KiSetHandlerAddressToIDTIrql(PIC1_BASE + InterruptInput,
  387. PicNopHandlerInt,
  388. (PVOID)InterruptInput,
  389. HIGH_LEVEL);
  390. #else
  391. extern VOID (*PicNopIntiHandlers[])(VOID);
  392. VOID (*Hp)(VOID) = PicNopIntiHandlers[InterruptInput];
  393. KiSetHandlerAddressToIDT(PIC1_BASE + InterruptInput, Hp);
  394. #endif
  395. }
  396. STATIC VOID
  397. HalpEnablePicInti (
  398. IN USHORT InterruptInput
  399. )
  400. /*++
  401. Routine Description:
  402. This procedure enables a PIC interrupt
  403. Arguments:
  404. InterruptInput - The input line we're interested in
  405. Return Value:
  406. None.
  407. --*/
  408. {
  409. USHORT PicMask;
  410. ASSERT(InterruptInput < 16);
  411. //
  412. // bump Enable Count for this INTI
  413. //
  414. Halp8259Counts[InterruptInput]++;
  415. //
  416. // Only actually perform the enable if we've transitioned
  417. // from zero to one enables
  418. //
  419. if ( Halp8259Counts[InterruptInput] == 1) {
  420. //
  421. // Set the Interrupt Handler for PIC inti, this is
  422. // the routine that fields the EXTINT vector and issues
  423. // an APIC vector
  424. //
  425. SetPicInterruptHandler(InterruptInput);
  426. PicMask = HalpGlobal8259Mask;
  427. PicMask &= (USHORT) ~(1 << InterruptInput);
  428. if (InterruptInput > 7)
  429. PicMask &= (USHORT) ~(1 << PIC_SLAVE_IRQ);
  430. HalpGlobal8259Mask = PicMask;
  431. HalpSet8259Mask ((USHORT) PicMask);
  432. }
  433. }
  434. STATIC VOID
  435. HalpDisablePicInti(
  436. IN USHORT InterruptInput
  437. )
  438. /*++
  439. Routine Description:
  440. This procedure enables a PIC interrupt
  441. Arguments:
  442. InterruptInput - The input line we're interested in
  443. Return Value:
  444. None.
  445. --*/
  446. {
  447. USHORT PicMask;
  448. ASSERT(InterruptInput < 16);
  449. //
  450. // decrement Enable Count for this INTI
  451. //
  452. Halp8259Counts[InterruptInput]--;
  453. //
  454. // Only disable if we have zero enables
  455. //
  456. if ( Halp8259Counts[InterruptInput] == 0) {
  457. //
  458. // Disable the Interrupt Handler for PIC inti
  459. //
  460. ResetPicInterruptHandler(InterruptInput);
  461. PicMask = HalpGlobal8259Mask;
  462. PicMask |= (1 << InterruptInput);
  463. if (InterruptInput > 7) {
  464. //
  465. // This inti is on the slave, see if any other
  466. // inti's are enabled. If none are then disable the
  467. // slave
  468. //
  469. if ((PicMask & 0xff00) == 0xff00)
  470. //
  471. // All inti's on the slave are disabled
  472. //
  473. PicMask |= PIC_SLAVE_IRQ;
  474. }
  475. HalpSet8259Mask ((USHORT) PicMask);
  476. HalpGlobal8259Mask = PicMask;
  477. }
  478. }
  479. BOOLEAN
  480. HalEnableSystemInterrupt(
  481. IN ULONG Vector,
  482. IN KIRQL Irql,
  483. IN KINTERRUPT_MODE InterruptMode
  484. )
  485. /*++
  486. Routine Description:
  487. This procedure enables a system interrupt
  488. Some early implementations using the 82489DX only allow a processor
  489. to access the IO Unit on it's own 82489DX. Since we use a single IO
  490. Unit (P0's) to distribute all interrupts, we have a problem when Pn
  491. wants to enable an interrupt on these type of systems.
  492. In order to get around this problem we can take advantage of the fact
  493. that the kernel calls Enable/Disable on each processor which has a bit
  494. set in the Affinity mask for the interrupt. Since we have only one IO
  495. Unit in use and that Unit is addressable from P0 only, we must set the
  496. P0 affinity bit for all interrupts. We can then ignore Enable/Disable
  497. requests from processors other than P0 since we will always get called
  498. for P0.
  499. The right way to do this assuming a single IO Unit accessable to all
  500. processors, would be to use global counters to determine if the
  501. interrupt has not been enabled on the IO Unit. Then enable the IO Unit
  502. when we transition from no processors to one processor that have the
  503. interrupt enabled.
  504. Arguments:
  505. Vector - vector of the interrupt to be enabled
  506. Irql - interrupt level of the interrupt to be enabled.
  507. Return Value:
  508. None.
  509. --*/
  510. {
  511. PKPCR pPCR;
  512. UCHAR ThisCpu, DevLevel;
  513. USHORT InterruptInput;
  514. ULONG Entry;
  515. ULONG OldLevel;
  516. INTI_INFO Inti;
  517. ASSERT(Vector < (1+MAX_NODES)*0x100-1);
  518. ASSERT(Irql <= HIGH_LEVEL);
  519. if ( (InterruptInput = HalpVectorToINTI[Vector]) == 0xffff ) {
  520. //
  521. // There is no external device associated with this interrupt
  522. //
  523. return(FALSE);
  524. }
  525. Inti = HalpIntiInfo[InterruptInput];
  526. DevLevel = HalpDevLevel
  527. [InterruptMode == LevelSensitive ? CFG_LEVEL : CFG_EDGE]
  528. [Inti.Level];
  529. if (DevLevel & CFG_ERROR) {
  530. DBGMSG ("HAL: Warning device interrupt mode overridden\n");
  531. }
  532. //
  533. // Block interrupts & synchronize until we're done
  534. //
  535. OldLevel = HalpAcquireHighLevelLock (&HalpAccountingLock);
  536. pPCR = KeGetPcr();
  537. ThisCpu = CurrentPrcb(pPCR)->Number;
  538. switch (Inti.Type) {
  539. case INT_TYPE_INTR: {
  540. //
  541. // enable the interrupt in the I/O unit redirection table
  542. //
  543. switch (Vector) {
  544. case APIC_CLOCK_VECTOR:
  545. ASSERT(ThisCpu == 0);
  546. Entry = APIC_CLOCK_VECTOR | DELIVER_FIXED | LOGICAL_DESTINATION;
  547. break;
  548. case NMI_VECTOR:
  549. return FALSE;
  550. default:
  551. Entry = HalVectorToIDTEntry(Vector) | DELIVER_LOW_PRIORITY | LOGICAL_DESTINATION;
  552. break;
  553. } // switch (Vector)
  554. Entry |= CFG_TYPE(DevLevel) == CFG_EDGE ? EDGE_TRIGGERED : LEVEL_TRIGGERED;
  555. Entry |= HalpDevPolarity[Inti.Polarity][CFG_TYPE(DevLevel)] ==
  556. CFG_LOW ? ACTIVE_LOW : ACTIVE_HIGH;
  557. HalpEnableRedirEntry (
  558. InterruptInput,
  559. Entry,
  560. (UCHAR) ThisCpu
  561. );
  562. break;
  563. } // case INT_TYPE_INTR:
  564. case INT_TYPE_EXTINT: {
  565. //
  566. // This is an interrupt that uses the IO APIC to route PIC
  567. // events. In this case the IO unit has to be enabled and
  568. // the PIC must be enabled.
  569. //
  570. HalpEnableRedirEntry (
  571. 0, // WARNING: kenr - assuming 0
  572. DELIVER_EXTINT | LOGICAL_DESTINATION,
  573. (UCHAR) ThisCpu
  574. );
  575. HalpEnablePicInti(InterruptInput);
  576. break;
  577. } // case INT_TYPE_EXTINT
  578. default:
  579. DBGMSG ("HalEnableSystemInterrupt: Unkown Inti Type\n");
  580. break;
  581. } // switch (IntiType)
  582. HalpReleaseHighLevelLock (&HalpAccountingLock, OldLevel);
  583. return TRUE;
  584. }
  585. VOID
  586. HalDisableSystemInterrupt(
  587. IN ULONG Vector,
  588. IN KIRQL Irql
  589. )
  590. /*++
  591. Routine Description:
  592. Disables a system interrupt.
  593. Some early implementations using the 82489DX only allow a processor
  594. to access the IO Unit on it's own 82489DX. Since we use a single IO
  595. Unit (P0's) to distribute all interrupts, we have a problem when Pn
  596. wants to enable an interrupt on these type of systems.
  597. In order to get around this problem we can take advantage of the fact
  598. that the kernel calls Enable/Disable on each processor which has a bit
  599. set in the Affinity mask for the interrupt. Since we have only one IO
  600. Unit in use and that Unit is addressable from P0 only, we must set the
  601. P0 affinity bit for all interrupts. We can then ignore Enable/Disable
  602. requests from processors other than P0 since we will always get called
  603. for P0.
  604. The right way to do this assuming a single IO Unit accessable to all
  605. processors, would be to use global counters to determine if the
  606. interrupt has not been enabled on the IO Unit. Then enable the IO Unit
  607. when we transition from no processors to one processor that have the
  608. interrupt enabled.
  609. Arguments:
  610. Vector - Supplies the vector of the interrupt to be disabled
  611. Irql - Supplies the interrupt level of the interrupt to be disabled
  612. Return Value:
  613. None.
  614. --*/
  615. {
  616. PKPCR pPCR;
  617. USHORT InterruptInput;
  618. UCHAR ThisCpu;
  619. ULONG OldLevel;
  620. ASSERT(Vector < (1+MAX_NODES)*0x100-1);
  621. ASSERT(Irql <= HIGH_LEVEL);
  622. if ( (InterruptInput = HalpVectorToINTI[Vector]) == 0xffff ) {
  623. //
  624. // There is no external device associated with this interrupt
  625. //
  626. return;
  627. }
  628. //
  629. // Block interrupts & synchronize until we're done
  630. //
  631. OldLevel = HalpAcquireHighLevelLock (&HalpAccountingLock);
  632. pPCR = KeGetPcr();
  633. ThisCpu = CurrentPrcb(pPCR)->Number;
  634. switch (HalpIntiInfo[InterruptInput].Type) {
  635. case INT_TYPE_INTR: {
  636. //
  637. // enable the interrupt in the I/O unit redirection table
  638. //
  639. HalpDisableRedirEntry( InterruptInput, ThisCpu );
  640. break;
  641. } // case INT_TYPE_INTR:
  642. case INT_TYPE_EXTINT: {
  643. //
  644. // This is an interrupt that uses the IO APIC to route PIC
  645. // events. In this case the IO unit has to be enabled and
  646. // the PIC must be enabled.
  647. //
  648. //
  649. // WARNING: The PIC is assumed to be routed only through
  650. // IoApic[0]Inti[0]
  651. //
  652. HalpDisablePicInti(InterruptInput);
  653. break;
  654. }
  655. default:
  656. DBGMSG ("HalDisableSystemInterrupt: Unkown Inti Type\n");
  657. break;
  658. }
  659. HalpReleaseHighLevelLock (&HalpAccountingLock, OldLevel);
  660. return;
  661. }
  662. VOID
  663. HalpInitializeIOUnits (
  664. VOID
  665. )
  666. /*
  667. Routine Description:
  668. This routine initializes the IO APIC. It only programs the APIC ID Register.
  669. HalEnableSystemInterrupt programs the Redirection table.
  670. Arguments:
  671. None
  672. Return Value:
  673. None.
  674. */
  675. {
  676. ULONG IoApicId;
  677. struct ApicIoUnit *IoUnitPtr;
  678. ULONG i, j, max, regVal;
  679. for(i=0; i < HalpMpInfoTable.IOApicCount; i++) {
  680. IoUnitPtr = (struct ApicIoUnit *) HalpMpInfoTable.IoApicBase[i];
  681. //
  682. // write the I/O unit APIC-ID - Since we are using the Processor
  683. // Numbers for the local unit ID's we need to set the IO unit
  684. // to a high (out of Processor Number range) value.
  685. //
  686. IoUnitPtr->RegisterSelect = IO_ID_REGISTER;
  687. IoApicId = HalpGetIoApicId(i);
  688. regVal = IoUnitPtr->RegisterWindow;
  689. regVal &= ~APIC_ID_MASK;
  690. IoUnitPtr->RegisterWindow = (IoApicId << APIC_ID_SHIFT) | regVal;
  691. //
  692. // mask all vectors on the ioapic
  693. //
  694. IoUnitPtr->RegisterSelect = IO_VERS_REGISTER;
  695. max = ((IoUnitPtr->RegisterWindow >> 16) & 0xff) * 2;
  696. for (j=0; j <= max; j += 2) {
  697. IoUnitPtr->RegisterSelect = IO_REDIR_00_LOW + j;
  698. IoUnitPtr->RegisterWindow |= INT_VECTOR_MASK | INTERRUPT_MASKED;
  699. }
  700. }
  701. if (HalpHiberInProgress) {
  702. return;
  703. }
  704. //
  705. // Add resources consumed by APICs
  706. //
  707. HalpApicUsage.Next = NULL;
  708. HalpApicUsage.Type = CmResourceTypeMemory;
  709. HalpApicUsage.Flags = DeviceUsage;
  710. HalpApicUsage.Element[0].Start = HalpMpInfoTable.LocalApicBase;
  711. HalpApicUsage.Element[0].Length = 0x400;
  712. ASSERT (HalpMpInfoTable.IOApicCount <= MAX_IOAPICS);
  713. for (i=0; i < HalpMpInfoTable.IOApicCount; i++) {
  714. HalpApicUsage.Element[i+1].Start = (ULONG)HalpMpInfoTable.IoApicPhys[i];
  715. HalpApicUsage.Element[i+1].Length = 0x400;
  716. }
  717. HalpApicUsage.Element[i+1].Start = 0;
  718. HalpApicUsage.Element[i+1].Length = 0;
  719. HalpRegisterAddressUsage ((ADDRESS_USAGE*)&HalpApicUsage);
  720. }
  721. VOID
  722. HalpEnableNMI (
  723. VOID
  724. )
  725. /*
  726. Routine Description:
  727. Enable & connect NMI sources for the calling processor.
  728. */
  729. {
  730. PKPCR pPCR;
  731. USHORT InterruptInput;
  732. UCHAR ThisCpu;
  733. ULONG OldLevel;
  734. ULONG Entry;
  735. pPCR = KeGetPcr();
  736. ThisCpu = CurrentPrcb(pPCR)->Number;
  737. OldLevel = HalpAcquireHighLevelLock (&HalpAccountingLock);
  738. HalpEnableLocalNmiSources();
  739. //
  740. // Enable any NMI sources found on IOAPICs
  741. //
  742. for (InterruptInput=0; InterruptInput < MAX_INTI; InterruptInput++) {
  743. if (HalpIntiInfo[InterruptInput].Type == INT_TYPE_NMI) {
  744. Entry = NMI_VECTOR | DELIVER_NMI | LOGICAL_DESTINATION;
  745. //
  746. // Halmps has had a bug in it for a long time. It always connects
  747. // NMI signals on I/O APICs as level-triggered, active-high. This
  748. // hack preserves that behavior for halmps and actually fixes the bug
  749. // on halacpi.
  750. //
  751. #ifdef ACPI_HAL
  752. #define POLARITY_HIGH 1
  753. #define POLARITY_LOW 3
  754. #define POLARITY_CONFORMS_WITH_BUS 0
  755. Entry |= ((HalpIntiInfo[InterruptInput].Level == CFG_EDGE) ? EDGE_TRIGGERED : LEVEL_TRIGGERED);
  756. Entry |= (((HalpIntiInfo[InterruptInput].Polarity == POLARITY_CONFORMS_WITH_BUS) ||
  757. (HalpIntiInfo[InterruptInput].Polarity == POLARITY_HIGH))
  758. ? ACTIVE_HIGH : ACTIVE_LOW);
  759. #else
  760. Entry |= LEVEL_TRIGGERED;
  761. #endif
  762. HalpEnableRedirEntry (
  763. InterruptInput,
  764. Entry,
  765. (UCHAR) ThisCpu
  766. );
  767. }
  768. }
  769. HalpReleaseHighLevelLock (&HalpAccountingLock, OldLevel);
  770. return;
  771. }
  772. VOID
  773. HalpEnablePerfInterupt (
  774. ULONG_PTR Context
  775. )
  776. {
  777. //
  778. // Enable local processor perf interrupt source
  779. //
  780. pLocalApic[LU_PERF_VECTOR/4] = (LEVEL_TRIGGERED | APIC_PERF_VECTOR |
  781. DELIVER_FIXED | ACTIVE_LOW);
  782. }
  783. UCHAR
  784. HalpAddInterruptDest(
  785. IN UCHAR CurrentDest,
  786. IN UCHAR ThisCpu
  787. )
  788. /*++
  789. Routine Description:
  790. This routine adds a CPU to the destination processor set of device
  791. interrupts.
  792. Arguments:
  793. CurrentDest - The present processor destination set for the interrupt
  794. ThisCpu - The logical NT processor number which has to be added to the
  795. interrupt destination mask
  796. Return Value:
  797. The bitmask corresponding to the new destiantion. This bitmask is suitable
  798. to be written into the hardware.
  799. --*/
  800. {
  801. PINTERRUPT_DEST Destination;
  802. if (HalpMaxProcsPerCluster == 0) {
  803. return(HalpIntDestMap[ThisCpu].LogicalId | CurrentDest);
  804. } else {
  805. //
  806. // The current destination is a hardware cluster & destination ID
  807. //
  808. Destination = (PINTERRUPT_DEST)&CurrentDest;
  809. if (HalpIntDestMap[ThisCpu].Cluster.Hw.ClusterId ==
  810. Destination->Cluster.Hw.ClusterId) {
  811. Destination->Cluster.Hw.DestId |=
  812. HalpIntDestMap[ThisCpu].Cluster.Hw.DestId;
  813. return(Destination->Cluster.AsUchar);
  814. } else {
  815. //
  816. // In cluster mode, each interrupt can be routed only to a single
  817. // cluster. Replace the existing destination cluster with this one.
  818. //
  819. return(HalpIntDestMap[ThisCpu].Cluster.AsUchar);
  820. }
  821. }
  822. }
  823. UCHAR
  824. HalpRemoveInterruptDest(
  825. IN UCHAR CurrentDest,
  826. IN UCHAR ThisCpu
  827. )
  828. /*++
  829. Routine Description:
  830. This routine removes a CPU from the destination processor set of device
  831. interrupts.
  832. Arguments:
  833. CurrentDest - The present processor destination set for the interrupt
  834. ThisCpu - The logical NT processor number which has to be removed from the
  835. interrupt destination mask
  836. Return Value:
  837. The bitmask corresponding to the new destiantion. This bitmask is suitable
  838. to be written into the hardware.
  839. --*/
  840. {
  841. PINTERRUPT_DEST Destination;
  842. if (HalpMaxProcsPerCluster == 0) {
  843. CurrentDest &= ~(HalpIntDestMap[ThisCpu].LogicalId);
  844. return(CurrentDest);
  845. } else {
  846. Destination = (PINTERRUPT_DEST)&CurrentDest;
  847. if (HalpIntDestMap[ThisCpu].Cluster.Hw.ClusterId !=
  848. Destination->Cluster.Hw.ClusterId) {
  849. //
  850. // We are being asked to remove a processor which is not part
  851. // of the destination processor set for this interrupt
  852. //
  853. return(CurrentDest);
  854. } else {
  855. //
  856. // Remove this processor and check if it is the last processor
  857. // in the destination set
  858. //
  859. Destination->Cluster.Hw.DestId &=
  860. ~(HalpIntDestMap[ThisCpu].Cluster.Hw.DestId);
  861. if (Destination->Cluster.Hw.DestId) {
  862. return(Destination->Cluster.AsUchar);
  863. } else {
  864. //
  865. // There are no processors left in the destination mask.
  866. // Return 0 so the caller can disable the entry in the IO APIC
  867. //
  868. return(0);
  869. }
  870. }
  871. }
  872. }
  873. UCHAR
  874. HalpMapNtToHwProcessorId(
  875. IN UCHAR Number
  876. )
  877. /*
  878. Routine Description:
  879. This routine maps the logical NT processor number to the hardware cluster
  880. ID and processor ID for MPS systems.
  881. Arguments:
  882. Number: Logical NT processor number(zero based).
  883. Return Value:
  884. Bitmask representing the hardware cluster number and processor number for
  885. this processor. The return value is programmed into the hardware.
  886. */
  887. {
  888. INTERRUPT_DEST IntDest;
  889. if (HalpMaxProcsPerCluster == 0) {
  890. return(1 << Number);
  891. } else {
  892. //
  893. // In systems with heirarchical APIC buses, the BIOS/MPS table has to
  894. // inform the OS of the underlying topology so we can do this mapping.
  895. // For now, just assign consecutive cluster IDs starting from 0.
  896. //
  897. IntDest.Cluster.Hw.ClusterId = (Number/HalpMaxProcsPerCluster);
  898. IntDest.Cluster.Hw.DestId = 1 << (Number % HalpMaxProcsPerCluster);
  899. return(IntDest.Cluster.AsUchar);
  900. }
  901. }
  902. VOID
  903. HalpInitializeApicAddressing(
  904. IN UCHAR Number
  905. )
  906. {
  907. if (HalpMaxProcsPerCluster == 0) {
  908. pLocalApic[LU_DEST_FORMAT/4] = LU_DEST_FORMAT_FLAT;
  909. } else {
  910. ASSERT(Number <= (HalpMaxProcsPerCluster * MAX_CLUSTERS));
  911. pLocalApic[LU_DEST_FORMAT/4] = LU_DEST_FORMAT_CLUSTER;
  912. }
  913. HalpIntDestMap[Number].LogicalId = HalpMapNtToHwProcessorId(Number);
  914. //
  915. // At this point the Logical ID is a bit map of the processor number
  916. // the actual ID is the upper byte of the logical destination register
  917. // Note that this is not strictly true of 82489's. The 82489 has 32 bits
  918. // available for the logical ID, but since we want software compatability
  919. // between the two types of APICs we'll only use the upper byte.
  920. //
  921. // Shift the mask into the ID field and write it.
  922. //
  923. pLocalApic[LU_LOGICAL_DEST/4] = (ULONG)
  924. (HalpIntDestMap[Number].LogicalId << DESTINATION_SHIFT);
  925. }
  926. UCHAR
  927. HalpNodeNumber(
  928. PKPCR pPCR
  929. )
  930. /*
  931. Routine Description:
  932. This routine divines the Node number for the current CPU.
  933. Node numbers start at 1, and represent the granularity of interrupt
  934. routing decisions.
  935. Arguments:
  936. pPCR - A pointer to the PCR of the current processor. (This implies
  937. the caller must have masked interrupts.)
  938. Return Value:
  939. None.
  940. */
  941. {
  942. // One Node per cluster.
  943. if (HalpMaxProcsPerCluster != 0) {
  944. // One Node per Cluster.
  945. return(CurrentPrcb(pPCR)->Number/HalpMaxProcsPerCluster + 1);
  946. } else {
  947. // One Node per machine.
  948. return(1);
  949. }
  950. #if 0
  951. ULONG localApicId;
  952. // One Node per physical CPU package.
  953. localApicId = *(PVULONG)(LOCALAPIC + LU_ID_REGISTER);
  954. localApicId &= APIC_ID_MASK;
  955. localApicId >>= APIC_ID_SHIFT;
  956. // TODO: Implement cpuid stuff here to determine shift
  957. return((localApicId>>1) + 1);
  958. #endif
  959. }
  960. VOID
  961. HalpInitializeLocalUnit (
  962. VOID
  963. )
  964. /*
  965. Routine Description:
  966. This routine initializes the interrupt structures for the local unit
  967. of the APIC. This procedure is called by HalInitializeProcessor and
  968. is executed by each CPU.
  969. Arguments:
  970. None
  971. Return Value:
  972. None.
  973. */
  974. {
  975. PKPCR pPCR;
  976. PKPRCB prcb;
  977. ULONG SavedFlags;
  978. UCHAR Node;
  979. SavedFlags = HalpDisableInterrupts();
  980. pPCR = KeGetPcr();
  981. prcb = CurrentPrcb(pPCR);
  982. if (prcb->Number ==0) {
  983. //
  984. // enable APIC mode
  985. //
  986. // PC+MP Spec has a port defined (IMCR - Interrupt Mode Control
  987. // Port) That is used to enable APIC mode. The APIC could already
  988. // be enabled, but per the spec this is safe.
  989. //
  990. if (HalpMpInfoTable.IMCRPresent)
  991. {
  992. #if defined(NEC_98)
  993. WRITE_PORT_UCHAR(UlongToPtr(ImcrDataPortAddr),ImcrEnableApic);
  994. #else // defined(NEC_98)
  995. WRITE_PORT_UCHAR(UlongToPtr(ImcrRegPortAddr),ImcrPort);
  996. WRITE_PORT_UCHAR(UlongToPtr(ImcrDataPortAddr),ImcrEnableApic);
  997. #endif // defined(NEC_98)
  998. }
  999. //
  1000. // By default, use flat logical APIC addressing. If we have more
  1001. // than 8 processors, we must use cluster mode APIC addressing
  1002. //
  1003. if( (HalpMaxProcsPerCluster > 4) ||
  1004. ((HalpMpInfoTable.ProcessorCount > 8) &&
  1005. (HalpMaxProcsPerCluster == 0)) ) {
  1006. HalpMaxProcsPerCluster = 4;
  1007. }
  1008. if (HalpMpInfoTable.ApicVersion == APIC_82489DX) {
  1009. //
  1010. // Ignore user's attempt to force cluster mode if running
  1011. // on 82489DX external APIC interrupt controller.
  1012. //
  1013. ASSERT(HalpMpInfoTable.ProcessorCount <= 8);
  1014. HalpMaxProcsPerCluster = 0;
  1015. }
  1016. }
  1017. //
  1018. // Add the current processor to the Node tables.
  1019. //
  1020. Node = HalpNodeNumber(pPCR);
  1021. if (HalpMaxNode < Node) {
  1022. HalpMaxNode = Node;
  1023. }
  1024. HalpNodeAffinity[Node-1] |= (KAFFINITY)1 << prcb->Number;
  1025. //
  1026. // Program the TPR to mask all events
  1027. //
  1028. pLocalApic[LU_TPR/4] = 0xff;
  1029. HalpInitializeApicAddressing(prcb->Number);
  1030. //
  1031. // Initialize spurious interrupt handling
  1032. //
  1033. KiSetHandlerAddressToIDTIrql(APIC_SPURIOUS_VECTOR,
  1034. HalpApicSpuriousService,
  1035. NULL,
  1036. HIGH_LEVEL);
  1037. pLocalApic[LU_SPURIOUS_VECTOR/4] = (APIC_SPURIOUS_VECTOR | LU_UNIT_ENABLED);
  1038. if (HalpMpInfoTable.ApicVersion != APIC_82489DX) {
  1039. //
  1040. // Initialize Local Apic Fault handling
  1041. //
  1042. KiSetHandlerAddressToIDTIrql(APIC_FAULT_VECTOR,
  1043. HalpLocalApicErrorService,
  1044. NULL,
  1045. HIGH_LEVEL);
  1046. pLocalApic[LU_FAULT_VECTOR/4] = APIC_FAULT_VECTOR;
  1047. }
  1048. //
  1049. // Disable APIC Timer Vector, will be enabled later if needed
  1050. // We have to program a valid vector otherwise we get an APIC
  1051. // error.
  1052. //
  1053. pLocalApic[LU_TIMER_VECTOR/4] = (APIC_PROFILE_VECTOR |PERIODIC_TIMER | INTERRUPT_MASKED);
  1054. //
  1055. // Disable APIC PERF Vector, will be enabled later if needed.
  1056. // We have to program a valid vector otherwise we get an APIC
  1057. // error.
  1058. //
  1059. pLocalApic[LU_PERF_VECTOR/4] = (APIC_PERF_VECTOR | INTERRUPT_MASKED);
  1060. //
  1061. // Disable LINT0, if we were in Virtual Wire mode then this will
  1062. // have been enabled on the BSP, it may be enabled later by the
  1063. // EnableSystemInterrupt code
  1064. //
  1065. pLocalApic[LU_INT_VECTOR_0/4] = (APIC_SPURIOUS_VECTOR | INTERRUPT_MASKED);
  1066. //
  1067. // Program NMI Handling, it will be enabled on P0 only
  1068. // RLM Enable System Interrupt should do this
  1069. //
  1070. pLocalApic[LU_INT_VECTOR_1/4] = ( LEVEL_TRIGGERED | ACTIVE_HIGH | DELIVER_NMI |
  1071. INTERRUPT_MASKED | ACTIVE_HIGH | NMI_VECTOR);
  1072. //
  1073. // Synchronize Apic IDs - InitDeassertCommand is sent to all APIC
  1074. // local units to force synchronization of arbitration-IDs with APIC-IDs.
  1075. //
  1076. // NOTE: we don't have to worry about synchronizing access to the ICR
  1077. // at this point.
  1078. //
  1079. pLocalApic[LU_INT_CMD_LOW/4] = (DELIVER_INIT | LEVEL_TRIGGERED |
  1080. ICR_ALL_INCL_SELF | ICR_LEVEL_DEASSERTED);
  1081. HalpBuildIpiDestinationMap();
  1082. //
  1083. // we're done - set TPR to a low value and return
  1084. //
  1085. pLocalApic[LU_TPR/4] = ZERO_VECTOR;
  1086. HalpRestoreInterrupts(SavedFlags);
  1087. }
  1088. VOID
  1089. HalpUnMapIOApics(
  1090. VOID
  1091. )
  1092. /*++
  1093. Routine Description:
  1094. This routine unmaps the IOAPIC's and is primarily used
  1095. to prevent loss of VA space during hibernation
  1096. Arguments:
  1097. None:
  1098. Return Value:
  1099. None
  1100. */
  1101. {
  1102. UCHAR i;
  1103. for(i=0; i < HalpMpInfoTable.IOApicCount; i++) {
  1104. if (HalpMpInfoTable.IoApicBase[i]) {
  1105. HalpUnmapVirtualAddress(HalpMpInfoTable.IoApicBase[i],1);
  1106. }
  1107. }
  1108. }
  1109. VOID
  1110. HalpPostSleepMP(
  1111. IN LONG NumberProcessors,
  1112. IN volatile PLONG Number
  1113. )
  1114. /*++
  1115. Routine Description:
  1116. This routine does the part of MP re-init that needs to
  1117. happen after hibernation or sleeping.
  1118. Arguments:
  1119. None:
  1120. Return Value:
  1121. None
  1122. */
  1123. {
  1124. volatile ULONG ThisProcessor;
  1125. ULONG localApicId;
  1126. KIRQL OldIrql;
  1127. //
  1128. // Boot processor and the newly woken processors come here
  1129. //
  1130. ThisProcessor = CurrentPrcb(KeGetPcr())->Number;
  1131. if (ThisProcessor != 0) {
  1132. HalpInitializeLocalUnit ();
  1133. KeRaiseIrql(HIGH_LEVEL, &OldIrql);
  1134. }
  1135. //
  1136. // Fill in this processor's Apic ID.
  1137. //
  1138. localApicId = *(PVULONG)(LOCALAPIC + LU_ID_REGISTER);
  1139. localApicId &= APIC_ID_MASK;
  1140. localApicId >>= APIC_ID_SHIFT;
  1141. ((PHALPRCB)CurrentPrcb(KeGetPcr())->HalReserved)->PCMPApicID = (UCHAR)localApicId;
  1142. //
  1143. // Initialize the processor machine check registers
  1144. //
  1145. if ((HalpFeatureBits & HAL_MCE_PRESENT) ||
  1146. (HalpFeatureBits & HAL_MCA_PRESENT)) {
  1147. HalpMcaCurrentProcessorSetConfig();
  1148. }
  1149. //
  1150. // Enable NMI vectors in the local APIC
  1151. //
  1152. HalpEnableNMI();
  1153. //
  1154. // Enable perf event in local APIC
  1155. //
  1156. if (HalpFeatureBits & HAL_PERF_EVENTS) {
  1157. HalpEnablePerfInterupt(0);
  1158. }
  1159. //
  1160. // Wait for all processors to finish initialization.
  1161. //
  1162. InterlockedIncrement(Number);
  1163. while (*Number != NumberProcessors);
  1164. //
  1165. // The following global hardware state needs to be set after all processors
  1166. // have been woken up and initialized
  1167. //
  1168. if (CurrentPrcb(KeGetPcr())->Number == 0) {
  1169. //
  1170. // Restore clock interrupt
  1171. //
  1172. HalpInitializeClock();
  1173. HalpSet8259Mask(HalpGlobal8259Mask);
  1174. HalpHiberInProgress = FALSE;
  1175. //
  1176. // We are now ready to send IPIs again if more than
  1177. // one processor
  1178. //
  1179. if (NumberProcessors > 1) {
  1180. HalpIpiClock = 0xff;
  1181. }
  1182. }
  1183. }