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