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.

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