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