1 /** @file
2   Local APIC Library.
3 
4   This local APIC library instance supports x2APIC capable processors
5   which have xAPIC and x2APIC modes.
6 
7   Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR>
8   This program and the accompanying materials
9   are licensed and made available under the terms and conditions of the BSD License
10   which accompanies this distribution.  The full text of the license may be found at
11   http://opensource.org/licenses/bsd-license.php
12 
13   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 
16 **/
17 
18 #include <Register/Cpuid.h>
19 #include <Register/LocalApic.h>
20 
21 #include <Library/BaseLib.h>
22 #include <Library/DebugLib.h>
23 #include <Library/LocalApicLib.h>
24 #include <Library/IoLib.h>
25 #include <Library/TimerLib.h>
26 #include <Library/PcdLib.h>
27 
28 //
29 // Library internal functions
30 //
31 
32 /**
33   Determine if the CPU supports the Local APIC Base Address MSR.
34 
35   @retval TRUE  The CPU supports the Local APIC Base Address MSR.
36   @retval FALSE The CPU does not support the Local APIC Base Address MSR.
37 
38 **/
39 BOOLEAN
LocalApicBaseAddressMsrSupported(VOID)40 LocalApicBaseAddressMsrSupported (
41   VOID
42   )
43 {
44   UINT32  RegEax;
45   UINTN   FamilyId;
46 
47   AsmCpuid (1, &RegEax, NULL, NULL, NULL);
48   FamilyId = BitFieldRead32 (RegEax, 8, 11);
49   if (FamilyId == 0x04 || FamilyId == 0x05) {
50     //
51     // CPUs with a FamilyId of 0x04 or 0x05 do not support the
52     // Local APIC Base Address MSR
53     //
54     return FALSE;
55   }
56   return TRUE;
57 }
58 
59 /**
60   Retrieve the base address of local APIC.
61 
62   @return The base address of local APIC.
63 
64 **/
65 UINTN
66 EFIAPI
GetLocalApicBaseAddress(VOID)67 GetLocalApicBaseAddress (
68   VOID
69   )
70 {
71   MSR_IA32_APIC_BASE  ApicBaseMsr;
72 
73   if (!LocalApicBaseAddressMsrSupported ()) {
74     //
75     // If CPU does not support Local APIC Base Address MSR, then retrieve
76     // Local APIC Base Address from PCD
77     //
78     return PcdGet32 (PcdCpuLocalApicBaseAddress);
79   }
80 
81   ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS);
82 
83   return (UINTN)(LShiftU64 ((UINT64) ApicBaseMsr.Bits.ApicBaseHigh, 32)) +
84            (((UINTN)ApicBaseMsr.Bits.ApicBaseLow) << 12);
85 }
86 
87 /**
88   Set the base address of local APIC.
89 
90   If BaseAddress is not aligned on a 4KB boundary, then ASSERT().
91 
92   @param[in] BaseAddress   Local APIC base address to be set.
93 
94 **/
95 VOID
96 EFIAPI
SetLocalApicBaseAddress(IN UINTN BaseAddress)97 SetLocalApicBaseAddress (
98   IN UINTN                BaseAddress
99   )
100 {
101   MSR_IA32_APIC_BASE  ApicBaseMsr;
102 
103   ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);
104 
105   if (!LocalApicBaseAddressMsrSupported ()) {
106     //
107     // Ignore set request of the CPU does not support APIC Base Address MSR
108     //
109     return;
110   }
111 
112   ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS);
113 
114   ApicBaseMsr.Bits.ApicBaseLow  = (UINT32) (BaseAddress >> 12);
115   ApicBaseMsr.Bits.ApicBaseHigh = (UINT32) (RShiftU64((UINT64) BaseAddress, 32));
116 
117   AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64);
118 }
119 
120 /**
121   Read from a local APIC register.
122 
123   This function reads from a local APIC register either in xAPIC or x2APIC mode.
124   It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be
125   accessed using multiple 32-bit loads or stores, so this function only performs
126   32-bit read.
127 
128   @param  MmioOffset  The MMIO offset of the local APIC register in xAPIC mode.
129                       It must be 16-byte aligned.
130 
131   @return 32-bit      Value read from the register.
132 **/
133 UINT32
134 EFIAPI
ReadLocalApicReg(IN UINTN MmioOffset)135 ReadLocalApicReg (
136   IN UINTN  MmioOffset
137   )
138 {
139   UINT32 MsrIndex;
140 
141   ASSERT ((MmioOffset & 0xf) == 0);
142 
143   if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {
144     return MmioRead32 (GetLocalApicBaseAddress() + MmioOffset);
145   } else {
146     //
147     // DFR is not supported in x2APIC mode.
148     //
149     ASSERT (MmioOffset != XAPIC_ICR_DFR_OFFSET);
150     //
151     // Note that in x2APIC mode, ICR is a 64-bit MSR that needs special treatment. It
152     // is not supported in this function for simplicity.
153     //
154     ASSERT (MmioOffset != XAPIC_ICR_HIGH_OFFSET);
155 
156     MsrIndex = (UINT32)(MmioOffset >> 4) + X2APIC_MSR_BASE_ADDRESS;
157     return AsmReadMsr32 (MsrIndex);
158   }
159 }
160 
161 /**
162   Write to a local APIC register.
163 
164   This function writes to a local APIC register either in xAPIC or x2APIC mode.
165   It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be
166   accessed using multiple 32-bit loads or stores, so this function only performs
167   32-bit write.
168 
169   if the register index is invalid or unsupported in current APIC mode, then ASSERT.
170 
171   @param  MmioOffset  The MMIO offset of the local APIC register in xAPIC mode.
172                       It must be 16-byte aligned.
173   @param  Value       Value to be written to the register.
174 **/
175 VOID
176 EFIAPI
WriteLocalApicReg(IN UINTN MmioOffset,IN UINT32 Value)177 WriteLocalApicReg (
178   IN UINTN  MmioOffset,
179   IN UINT32 Value
180   )
181 {
182   UINT32 MsrIndex;
183 
184   ASSERT ((MmioOffset & 0xf) == 0);
185 
186   if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {
187     MmioWrite32 (GetLocalApicBaseAddress() + MmioOffset, Value);
188   } else {
189     //
190     // DFR is not supported in x2APIC mode.
191     //
192     ASSERT (MmioOffset != XAPIC_ICR_DFR_OFFSET);
193     //
194     // Note that in x2APIC mode, ICR is a 64-bit MSR that needs special treatment. It
195     // is not supported in this function for simplicity.
196     //
197     ASSERT (MmioOffset != XAPIC_ICR_HIGH_OFFSET);
198     ASSERT (MmioOffset != XAPIC_ICR_LOW_OFFSET);
199 
200     MsrIndex =  (UINT32)(MmioOffset >> 4) + X2APIC_MSR_BASE_ADDRESS;
201     //
202     // The serializing semantics of WRMSR are relaxed when writing to the APIC registers.
203     // Use memory fence here to force the serializing semantics to be consisent with xAPIC mode.
204     //
205     MemoryFence ();
206     AsmWriteMsr32 (MsrIndex, Value);
207   }
208 }
209 
210 /**
211   Send an IPI by writing to ICR.
212 
213   This function returns after the IPI has been accepted by the target processor.
214 
215   @param  IcrLow 32-bit value to be written to the low half of ICR.
216   @param  ApicId APIC ID of the target processor if this IPI is targeted for a specific processor.
217 **/
218 VOID
SendIpi(IN UINT32 IcrLow,IN UINT32 ApicId)219 SendIpi (
220   IN UINT32          IcrLow,
221   IN UINT32          ApicId
222   )
223 {
224   UINT64             MsrValue;
225   LOCAL_APIC_ICR_LOW IcrLowReg;
226   UINTN              LocalApciBaseAddress;
227   UINT32             IcrHigh;
228   BOOLEAN            InterruptState;
229 
230   //
231   // Legacy APIC or X2APIC?
232   //
233   if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {
234     ASSERT (ApicId <= 0xff);
235 
236     InterruptState = SaveAndDisableInterrupts ();
237 
238     //
239     // Get base address of this LAPIC
240     //
241     LocalApciBaseAddress = GetLocalApicBaseAddress();
242 
243     //
244     // Save existing contents of ICR high 32 bits
245     //
246     IcrHigh = MmioRead32 (LocalApciBaseAddress + XAPIC_ICR_HIGH_OFFSET);
247 
248     //
249     // Wait for DeliveryStatus clear in case a previous IPI
250     //  is still being sent
251     //
252     do {
253       IcrLowReg.Uint32 = MmioRead32 (LocalApciBaseAddress + XAPIC_ICR_LOW_OFFSET);
254     } while (IcrLowReg.Bits.DeliveryStatus != 0);
255 
256     //
257     // For xAPIC, the act of writing to the low doubleword of the ICR causes the IPI to be sent.
258     //
259     MmioWrite32 (LocalApciBaseAddress + XAPIC_ICR_HIGH_OFFSET, ApicId << 24);
260     MmioWrite32 (LocalApciBaseAddress + XAPIC_ICR_LOW_OFFSET, IcrLow);
261 
262     //
263     // Wait for DeliveryStatus clear again
264     //
265     do {
266       IcrLowReg.Uint32 = MmioRead32 (LocalApciBaseAddress + XAPIC_ICR_LOW_OFFSET);
267     } while (IcrLowReg.Bits.DeliveryStatus != 0);
268 
269     //
270     // And restore old contents of ICR high
271     //
272     MmioWrite32 (LocalApciBaseAddress + XAPIC_ICR_HIGH_OFFSET, IcrHigh);
273 
274     SetInterruptState (InterruptState);
275 
276   } else {
277     //
278     // For x2APIC, A single MSR write to the Interrupt Command Register is required for dispatching an
279     // interrupt in x2APIC mode.
280     //
281     MsrValue = LShiftU64 ((UINT64) ApicId, 32) | IcrLow;
282     AsmWriteMsr64 (X2APIC_MSR_ICR_ADDRESS, MsrValue);
283   }
284 }
285 
286 //
287 // Library API implementation functions
288 //
289 
290 /**
291   Get the current local APIC mode.
292 
293   If local APIC is disabled, then ASSERT.
294 
295   @retval LOCAL_APIC_MODE_XAPIC  current APIC mode is xAPIC.
296   @retval LOCAL_APIC_MODE_X2APIC current APIC mode is x2APIC.
297 **/
298 UINTN
299 EFIAPI
GetApicMode(VOID)300 GetApicMode (
301   VOID
302   )
303 {
304   MSR_IA32_APIC_BASE  ApicBaseMsr;
305 
306   if (!LocalApicBaseAddressMsrSupported ()) {
307     //
308     // If CPU does not support APIC Base Address MSR, then return XAPIC mode
309     //
310     return LOCAL_APIC_MODE_XAPIC;
311   }
312 
313   ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS);
314   //
315   // Local APIC should have been enabled
316   //
317   ASSERT (ApicBaseMsr.Bits.En != 0);
318   if (ApicBaseMsr.Bits.Extd != 0) {
319     return LOCAL_APIC_MODE_X2APIC;
320   } else {
321     return LOCAL_APIC_MODE_XAPIC;
322   }
323 }
324 
325 /**
326   Set the current local APIC mode.
327 
328   If the specified local APIC mode is not valid, then ASSERT.
329   If the specified local APIC mode can't be set as current, then ASSERT.
330 
331   @param ApicMode APIC mode to be set.
332 
333   @note  This API must not be called from an interrupt handler or SMI handler.
334          It may result in unpredictable behavior.
335 **/
336 VOID
337 EFIAPI
SetApicMode(IN UINTN ApicMode)338 SetApicMode (
339   IN UINTN  ApicMode
340   )
341 {
342   UINTN               CurrentMode;
343   MSR_IA32_APIC_BASE  ApicBaseMsr;
344 
345   if (!LocalApicBaseAddressMsrSupported ()) {
346     //
347     // Ignore set request if the CPU does not support APIC Base Address MSR
348     //
349     return;
350   }
351 
352   CurrentMode = GetApicMode ();
353   if (CurrentMode == LOCAL_APIC_MODE_XAPIC) {
354     switch (ApicMode) {
355       case LOCAL_APIC_MODE_XAPIC:
356         break;
357       case LOCAL_APIC_MODE_X2APIC:
358         ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS);
359         ApicBaseMsr.Bits.Extd = 1;
360         AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64);
361         break;
362       default:
363         ASSERT (FALSE);
364     }
365   } else {
366     switch (ApicMode) {
367       case LOCAL_APIC_MODE_XAPIC:
368         //
369         //  Transition from x2APIC mode to xAPIC mode is a two-step process:
370         //    x2APIC -> Local APIC disabled -> xAPIC
371         //
372         ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS);
373         ApicBaseMsr.Bits.Extd = 0;
374         ApicBaseMsr.Bits.En = 0;
375         AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64);
376         ApicBaseMsr.Bits.En = 1;
377         AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64);
378         break;
379       case LOCAL_APIC_MODE_X2APIC:
380         break;
381       default:
382         ASSERT (FALSE);
383     }
384   }
385 }
386 
387 /**
388   Get the initial local APIC ID of the executing processor assigned by hardware upon power on or reset.
389 
390   In xAPIC mode, the initial local APIC ID may be different from current APIC ID.
391   In x2APIC mode, the local APIC ID can't be changed and there is no concept of initial APIC ID. In this case,
392   the 32-bit local APIC ID is returned as initial APIC ID.
393 
394   @return  32-bit initial local APIC ID of the executing processor.
395 **/
396 UINT32
397 EFIAPI
GetInitialApicId(VOID)398 GetInitialApicId (
399   VOID
400   )
401 {
402   UINT32 ApicId;
403   UINT32 MaxCpuIdIndex;
404   UINT32 RegEbx;
405 
406   if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {
407     //
408     // Get the max index of basic CPUID
409     //
410     AsmCpuid (CPUID_SIGNATURE, &MaxCpuIdIndex, NULL, NULL, NULL);
411     //
412     // If CPUID Leaf B is supported,
413     // Then the initial 32-bit APIC ID = CPUID.0BH:EDX
414     // Else the initial 8-bit APIC ID = CPUID.1:EBX[31:24]
415     //
416     if (MaxCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) {
417       AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 0, NULL, NULL, NULL, &ApicId);
418       return ApicId;
419     }
420     AsmCpuid (CPUID_VERSION_INFO, NULL, &RegEbx, NULL, NULL);
421     return RegEbx >> 24;
422   } else {
423     return GetApicId ();
424   }
425 }
426 
427 /**
428   Get the local APIC ID of the executing processor.
429 
430   @return  32-bit local APIC ID of the executing processor.
431 **/
432 UINT32
433 EFIAPI
GetApicId(VOID)434 GetApicId (
435   VOID
436   )
437 {
438   UINT32 ApicId;
439   UINT32 InitApicId;
440 
441   ApicId = ReadLocalApicReg (XAPIC_ID_OFFSET);
442   if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {
443     ApicId = ((InitApicId = GetInitialApicId ()) < 0x100) ? (ApicId >> 24) : InitApicId;
444   }
445 
446   return ApicId;
447 }
448 
449 /**
450   Get the value of the local APIC version register.
451 
452   @return  the value of the local APIC version register.
453 **/
454 UINT32
455 EFIAPI
GetApicVersion(VOID)456 GetApicVersion (
457   VOID
458   )
459 {
460   return ReadLocalApicReg (XAPIC_VERSION_OFFSET);
461 }
462 
463 /**
464   Send a Fixed IPI to a specified target processor.
465 
466   This function returns after the IPI has been accepted by the target processor.
467 
468   @param  ApicId   The local APIC ID of the target processor.
469   @param  Vector   The vector number of the interrupt being sent.
470 **/
471 VOID
472 EFIAPI
SendFixedIpi(IN UINT32 ApicId,IN UINT8 Vector)473 SendFixedIpi (
474   IN UINT32          ApicId,
475   IN UINT8           Vector
476   )
477 {
478   LOCAL_APIC_ICR_LOW IcrLow;
479 
480   IcrLow.Uint32 = 0;
481   IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_FIXED;
482   IcrLow.Bits.Level = 1;
483   IcrLow.Bits.Vector = Vector;
484   SendIpi (IcrLow.Uint32, ApicId);
485 }
486 
487 /**
488   Send a Fixed IPI to all processors excluding self.
489 
490   This function returns after the IPI has been accepted by the target processors.
491 
492   @param  Vector   The vector number of the interrupt being sent.
493 **/
494 VOID
495 EFIAPI
SendFixedIpiAllExcludingSelf(IN UINT8 Vector)496 SendFixedIpiAllExcludingSelf (
497   IN UINT8           Vector
498   )
499 {
500   LOCAL_APIC_ICR_LOW IcrLow;
501 
502   IcrLow.Uint32 = 0;
503   IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_FIXED;
504   IcrLow.Bits.Level = 1;
505   IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF;
506   IcrLow.Bits.Vector = Vector;
507   SendIpi (IcrLow.Uint32, 0);
508 }
509 
510 /**
511   Send a SMI IPI to a specified target processor.
512 
513   This function returns after the IPI has been accepted by the target processor.
514 
515   @param  ApicId   Specify the local APIC ID of the target processor.
516 **/
517 VOID
518 EFIAPI
SendSmiIpi(IN UINT32 ApicId)519 SendSmiIpi (
520   IN UINT32          ApicId
521   )
522 {
523   LOCAL_APIC_ICR_LOW IcrLow;
524 
525   IcrLow.Uint32 = 0;
526   IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_SMI;
527   IcrLow.Bits.Level = 1;
528   SendIpi (IcrLow.Uint32, ApicId);
529 }
530 
531 /**
532   Send a SMI IPI to all processors excluding self.
533 
534   This function returns after the IPI has been accepted by the target processors.
535 **/
536 VOID
537 EFIAPI
SendSmiIpiAllExcludingSelf(VOID)538 SendSmiIpiAllExcludingSelf (
539   VOID
540   )
541 {
542   LOCAL_APIC_ICR_LOW IcrLow;
543 
544   IcrLow.Uint32 = 0;
545   IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_SMI;
546   IcrLow.Bits.Level = 1;
547   IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF;
548   SendIpi (IcrLow.Uint32, 0);
549 }
550 
551 /**
552   Send an INIT IPI to a specified target processor.
553 
554   This function returns after the IPI has been accepted by the target processor.
555 
556   @param  ApicId   Specify the local APIC ID of the target processor.
557 **/
558 VOID
559 EFIAPI
SendInitIpi(IN UINT32 ApicId)560 SendInitIpi (
561   IN UINT32          ApicId
562   )
563 {
564   LOCAL_APIC_ICR_LOW IcrLow;
565 
566   IcrLow.Uint32 = 0;
567   IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_INIT;
568   IcrLow.Bits.Level = 1;
569   SendIpi (IcrLow.Uint32, ApicId);
570 }
571 
572 /**
573   Send an INIT IPI to all processors excluding self.
574 
575   This function returns after the IPI has been accepted by the target processors.
576 **/
577 VOID
578 EFIAPI
SendInitIpiAllExcludingSelf(VOID)579 SendInitIpiAllExcludingSelf (
580   VOID
581   )
582 {
583   LOCAL_APIC_ICR_LOW IcrLow;
584 
585   IcrLow.Uint32 = 0;
586   IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_INIT;
587   IcrLow.Bits.Level = 1;
588   IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF;
589   SendIpi (IcrLow.Uint32, 0);
590 }
591 
592 /**
593   Send an INIT-Start-up-Start-up IPI sequence to a specified target processor.
594 
595   This function returns after the IPI has been accepted by the target processor.
596 
597   if StartupRoutine >= 1M, then ASSERT.
598   if StartupRoutine is not multiple of 4K, then ASSERT.
599 
600   @param  ApicId          Specify the local APIC ID of the target processor.
601   @param  StartupRoutine  Points to a start-up routine which is below 1M physical
602                           address and 4K aligned.
603 **/
604 VOID
605 EFIAPI
SendInitSipiSipi(IN UINT32 ApicId,IN UINT32 StartupRoutine)606 SendInitSipiSipi (
607   IN UINT32          ApicId,
608   IN UINT32          StartupRoutine
609   )
610 {
611   LOCAL_APIC_ICR_LOW IcrLow;
612 
613   ASSERT (StartupRoutine < 0x100000);
614   ASSERT ((StartupRoutine & 0xfff) == 0);
615 
616   SendInitIpi (ApicId);
617   MicroSecondDelay (PcdGet32(PcdCpuInitIpiDelayInMicroSeconds));
618   IcrLow.Uint32 = 0;
619   IcrLow.Bits.Vector = (StartupRoutine >> 12);
620   IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP;
621   IcrLow.Bits.Level = 1;
622   SendIpi (IcrLow.Uint32, ApicId);
623   MicroSecondDelay (200);
624   SendIpi (IcrLow.Uint32, ApicId);
625 }
626 
627 /**
628   Send an INIT-Start-up-Start-up IPI sequence to all processors excluding self.
629 
630   This function returns after the IPI has been accepted by the target processors.
631 
632   if StartupRoutine >= 1M, then ASSERT.
633   if StartupRoutine is not multiple of 4K, then ASSERT.
634 
635   @param  StartupRoutine    Points to a start-up routine which is below 1M physical
636                             address and 4K aligned.
637 **/
638 VOID
639 EFIAPI
SendInitSipiSipiAllExcludingSelf(IN UINT32 StartupRoutine)640 SendInitSipiSipiAllExcludingSelf (
641   IN UINT32          StartupRoutine
642   )
643 {
644   LOCAL_APIC_ICR_LOW IcrLow;
645 
646   ASSERT (StartupRoutine < 0x100000);
647   ASSERT ((StartupRoutine & 0xfff) == 0);
648 
649   SendInitIpiAllExcludingSelf ();
650   MicroSecondDelay (PcdGet32(PcdCpuInitIpiDelayInMicroSeconds));
651   IcrLow.Uint32 = 0;
652   IcrLow.Bits.Vector = (StartupRoutine >> 12);
653   IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP;
654   IcrLow.Bits.Level = 1;
655   IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF;
656   SendIpi (IcrLow.Uint32, 0);
657   MicroSecondDelay (200);
658   SendIpi (IcrLow.Uint32, 0);
659 }
660 
661 /**
662   Initialize the state of the SoftwareEnable bit in the Local APIC
663   Spurious Interrupt Vector register.
664 
665   @param  Enable  If TRUE, then set SoftwareEnable to 1
666                   If FALSE, then set SoftwareEnable to 0.
667 
668 **/
669 VOID
670 EFIAPI
InitializeLocalApicSoftwareEnable(IN BOOLEAN Enable)671 InitializeLocalApicSoftwareEnable (
672   IN BOOLEAN  Enable
673   )
674 {
675   LOCAL_APIC_SVR  Svr;
676 
677   //
678   // Set local APIC software-enabled bit.
679   //
680   Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET);
681   if (Enable) {
682     if (Svr.Bits.SoftwareEnable == 0) {
683       Svr.Bits.SoftwareEnable = 1;
684       WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32);
685     }
686   } else {
687     if (Svr.Bits.SoftwareEnable == 1) {
688       Svr.Bits.SoftwareEnable = 0;
689       WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32);
690     }
691   }
692 }
693 
694 /**
695   Programming Virtual Wire Mode.
696 
697   This function programs the local APIC for virtual wire mode following
698   the example described in chapter A.3 of the MP 1.4 spec.
699 
700   IOxAPIC is not involved in this type of virtual wire mode.
701 **/
702 VOID
703 EFIAPI
ProgramVirtualWireMode(VOID)704 ProgramVirtualWireMode (
705   VOID
706   )
707 {
708   LOCAL_APIC_SVR      Svr;
709   LOCAL_APIC_LVT_LINT Lint;
710 
711   //
712   // Enable the APIC via SVR and set the spurious interrupt to use Int 00F.
713   //
714   Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET);
715   Svr.Bits.SpuriousVector = 0xf;
716   Svr.Bits.SoftwareEnable = 1;
717   WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32);
718 
719   //
720   // Program the LINT0 vector entry as ExtInt. Not masked, edge, active high.
721   //
722   Lint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT0_OFFSET);
723   Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_EXTINT;
724   Lint.Bits.InputPinPolarity = 0;
725   Lint.Bits.TriggerMode = 0;
726   Lint.Bits.Mask = 0;
727   WriteLocalApicReg (XAPIC_LVT_LINT0_OFFSET, Lint.Uint32);
728 
729   //
730   // Program the LINT0 vector entry as NMI. Not masked, edge, active high.
731   //
732   Lint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT1_OFFSET);
733   Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_NMI;
734   Lint.Bits.InputPinPolarity = 0;
735   Lint.Bits.TriggerMode = 0;
736   Lint.Bits.Mask = 0;
737   WriteLocalApicReg (XAPIC_LVT_LINT1_OFFSET, Lint.Uint32);
738 }
739 
740 /**
741   Disable LINT0 & LINT1 interrupts.
742 
743   This function sets the mask flag in the LVT LINT0 & LINT1 registers.
744 **/
745 VOID
746 EFIAPI
DisableLvtInterrupts(VOID)747 DisableLvtInterrupts (
748   VOID
749   )
750 {
751   LOCAL_APIC_LVT_LINT LvtLint;
752 
753   LvtLint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT0_OFFSET);
754   LvtLint.Bits.Mask = 1;
755   WriteLocalApicReg (XAPIC_LVT_LINT0_OFFSET, LvtLint.Uint32);
756 
757   LvtLint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT1_OFFSET);
758   LvtLint.Bits.Mask = 1;
759   WriteLocalApicReg (XAPIC_LVT_LINT1_OFFSET, LvtLint.Uint32);
760 }
761 
762 /**
763   Read the initial count value from the init-count register.
764 
765   @return The initial count value read from the init-count register.
766 **/
767 UINT32
768 EFIAPI
GetApicTimerInitCount(VOID)769 GetApicTimerInitCount (
770   VOID
771   )
772 {
773   return ReadLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET);
774 }
775 
776 /**
777   Read the current count value from the current-count register.
778 
779   @return The current count value read from the current-count register.
780 **/
781 UINT32
782 EFIAPI
GetApicTimerCurrentCount(VOID)783 GetApicTimerCurrentCount (
784   VOID
785   )
786 {
787   return ReadLocalApicReg (XAPIC_TIMER_CURRENT_COUNT_OFFSET);
788 }
789 
790 /**
791   Initialize the local APIC timer.
792 
793   The local APIC timer is initialized and enabled.
794 
795   @param DivideValue   The divide value for the DCR. It is one of 1,2,4,8,16,32,64,128.
796                        If it is 0, then use the current divide value in the DCR.
797   @param InitCount     The initial count value.
798   @param PeriodicMode  If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot.
799   @param Vector        The timer interrupt vector number.
800 **/
801 VOID
802 EFIAPI
InitializeApicTimer(IN UINTN DivideValue,IN UINT32 InitCount,IN BOOLEAN PeriodicMode,IN UINT8 Vector)803 InitializeApicTimer (
804   IN UINTN   DivideValue,
805   IN UINT32  InitCount,
806   IN BOOLEAN PeriodicMode,
807   IN UINT8   Vector
808   )
809 {
810   LOCAL_APIC_DCR       Dcr;
811   LOCAL_APIC_LVT_TIMER LvtTimer;
812   UINT32               Divisor;
813 
814   //
815   // Ensure local APIC is in software-enabled state.
816   //
817   InitializeLocalApicSoftwareEnable (TRUE);
818 
819   //
820   // Program init-count register.
821   //
822   WriteLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET, InitCount);
823 
824   if (DivideValue != 0) {
825     ASSERT (DivideValue <= 128);
826     ASSERT (DivideValue == GetPowerOfTwo32((UINT32)DivideValue));
827     Divisor = (UINT32)((HighBitSet32 ((UINT32)DivideValue) - 1) & 0x7);
828 
829     Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET);
830     Dcr.Bits.DivideValue1 = (Divisor & 0x3);
831     Dcr.Bits.DivideValue2 = (Divisor >> 2);
832     WriteLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET, Dcr.Uint32);
833   }
834 
835   //
836   // Enable APIC timer interrupt with specified timer mode.
837   //
838   LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
839   if (PeriodicMode) {
840     LvtTimer.Bits.TimerMode = 1;
841   } else {
842     LvtTimer.Bits.TimerMode = 0;
843   }
844   LvtTimer.Bits.Mask = 0;
845   LvtTimer.Bits.Vector = Vector;
846   WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32);
847 }
848 
849 /**
850   Get the state of the local APIC timer.
851 
852   This function will ASSERT if the local APIC is not software enabled.
853 
854   @param DivideValue   Return the divide value for the DCR. It is one of 1,2,4,8,16,32,64,128.
855   @param PeriodicMode  Return the timer mode. If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot.
856   @param Vector        Return the timer interrupt vector number.
857 **/
858 VOID
859 EFIAPI
GetApicTimerState(OUT UINTN * DivideValue OPTIONAL,OUT BOOLEAN * PeriodicMode OPTIONAL,OUT UINT8 * Vector OPTIONAL)860 GetApicTimerState (
861   OUT UINTN    *DivideValue  OPTIONAL,
862   OUT BOOLEAN  *PeriodicMode  OPTIONAL,
863   OUT UINT8    *Vector  OPTIONAL
864   )
865 {
866   UINT32 Divisor;
867   LOCAL_APIC_DCR Dcr;
868   LOCAL_APIC_LVT_TIMER LvtTimer;
869 
870   //
871   // Check the APIC Software Enable/Disable bit (bit 8) in Spurious-Interrupt
872   // Vector Register.
873   // This bit will be 1, if local APIC is software enabled.
874   //
875   ASSERT ((ReadLocalApicReg(XAPIC_SPURIOUS_VECTOR_OFFSET) & BIT8) != 0);
876 
877   if (DivideValue != NULL) {
878     Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET);
879     Divisor = Dcr.Bits.DivideValue1 | (Dcr.Bits.DivideValue2 << 2);
880     Divisor = (Divisor + 1) & 0x7;
881     *DivideValue = ((UINTN)1) << Divisor;
882   }
883 
884   if (PeriodicMode != NULL || Vector != NULL) {
885     LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
886     if (PeriodicMode != NULL) {
887       if (LvtTimer.Bits.TimerMode == 1) {
888         *PeriodicMode = TRUE;
889       } else {
890         *PeriodicMode = FALSE;
891       }
892     }
893     if (Vector != NULL) {
894       *Vector = (UINT8) LvtTimer.Bits.Vector;
895     }
896   }
897 }
898 
899 /**
900   Enable the local APIC timer interrupt.
901 **/
902 VOID
903 EFIAPI
EnableApicTimerInterrupt(VOID)904 EnableApicTimerInterrupt (
905   VOID
906   )
907 {
908   LOCAL_APIC_LVT_TIMER LvtTimer;
909 
910   LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
911   LvtTimer.Bits.Mask = 0;
912   WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32);
913 }
914 
915 /**
916   Disable the local APIC timer interrupt.
917 **/
918 VOID
919 EFIAPI
DisableApicTimerInterrupt(VOID)920 DisableApicTimerInterrupt (
921   VOID
922   )
923 {
924   LOCAL_APIC_LVT_TIMER LvtTimer;
925 
926   LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
927   LvtTimer.Bits.Mask = 1;
928   WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32);
929 }
930 
931 /**
932   Get the local APIC timer interrupt state.
933 
934   @retval TRUE  The local APIC timer interrupt is enabled.
935   @retval FALSE The local APIC timer interrupt is disabled.
936 **/
937 BOOLEAN
938 EFIAPI
GetApicTimerInterruptState(VOID)939 GetApicTimerInterruptState (
940   VOID
941   )
942 {
943   LOCAL_APIC_LVT_TIMER LvtTimer;
944 
945   LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
946   return (BOOLEAN)(LvtTimer.Bits.Mask == 0);
947 }
948 
949 /**
950   Send EOI to the local APIC.
951 **/
952 VOID
953 EFIAPI
SendApicEoi(VOID)954 SendApicEoi (
955   VOID
956   )
957 {
958   WriteLocalApicReg (XAPIC_EOI_OFFSET, 0);
959 }
960 
961 /**
962   Get the 32-bit address that a device should use to send a Message Signaled
963   Interrupt (MSI) to the Local APIC of the currently executing processor.
964 
965   @return 32-bit address used to send an MSI to the Local APIC.
966 **/
967 UINT32
968 EFIAPI
GetApicMsiAddress(VOID)969 GetApicMsiAddress (
970   VOID
971   )
972 {
973   LOCAL_APIC_MSI_ADDRESS  MsiAddress;
974 
975   //
976   // Return address for an MSI interrupt to be delivered only to the APIC ID
977   // of the currently executing processor.
978   //
979   MsiAddress.Uint32             = 0;
980   MsiAddress.Bits.BaseAddress   = 0xFEE;
981   MsiAddress.Bits.DestinationId = GetApicId ();
982   return MsiAddress.Uint32;
983 }
984 
985 /**
986   Get the 64-bit data value that a device should use to send a Message Signaled
987   Interrupt (MSI) to the Local APIC of the currently executing processor.
988 
989   If Vector is not in range 0x10..0xFE, then ASSERT().
990   If DeliveryMode is not supported, then ASSERT().
991 
992   @param  Vector          The 8-bit interrupt vector associated with the MSI.
993                           Must be in the range 0x10..0xFE
994   @param  DeliveryMode    A 3-bit value that specifies how the recept of the MSI
995                           is handled.  The only supported values are:
996                             0: LOCAL_APIC_DELIVERY_MODE_FIXED
997                             1: LOCAL_APIC_DELIVERY_MODE_LOWEST_PRIORITY
998                             2: LOCAL_APIC_DELIVERY_MODE_SMI
999                             4: LOCAL_APIC_DELIVERY_MODE_NMI
1000                             5: LOCAL_APIC_DELIVERY_MODE_INIT
1001                             7: LOCAL_APIC_DELIVERY_MODE_EXTINT
1002 
1003   @param  LevelTriggered  TRUE specifies a level triggered interrupt.
1004                           FALSE specifies an edge triggered interrupt.
1005   @param  AssertionLevel  Ignored if LevelTriggered is FALSE.
1006                           TRUE specifies a level triggered interrupt that active
1007                           when the interrupt line is asserted.
1008                           FALSE specifies a level triggered interrupt that active
1009                           when the interrupt line is deasserted.
1010 
1011   @return 64-bit data value used to send an MSI to the Local APIC.
1012 **/
1013 UINT64
1014 EFIAPI
GetApicMsiValue(IN UINT8 Vector,IN UINTN DeliveryMode,IN BOOLEAN LevelTriggered,IN BOOLEAN AssertionLevel)1015 GetApicMsiValue (
1016   IN UINT8    Vector,
1017   IN UINTN    DeliveryMode,
1018   IN BOOLEAN  LevelTriggered,
1019   IN BOOLEAN  AssertionLevel
1020   )
1021 {
1022   LOCAL_APIC_MSI_DATA  MsiData;
1023 
1024   ASSERT (Vector >= 0x10 && Vector <= 0xFE);
1025   ASSERT (DeliveryMode < 8 && DeliveryMode != 6 && DeliveryMode != 3);
1026 
1027   MsiData.Uint64            = 0;
1028   MsiData.Bits.Vector       = Vector;
1029   MsiData.Bits.DeliveryMode = (UINT32)DeliveryMode;
1030   if (LevelTriggered) {
1031     MsiData.Bits.TriggerMode = 1;
1032     if (AssertionLevel) {
1033       MsiData.Bits.Level = 1;
1034     }
1035   }
1036   return MsiData.Uint64;
1037 }
1038