]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c
Fix a bug in GetOptionalStringByIndex() that doesn't handle the case when Index ...
[mirror_edk2.git] / UefiCpuPkg / Library / BaseXApicX2ApicLib / BaseXApicX2ApicLib.c
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, 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/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 Read from a local APIC register.
33
34 This function reads from a local APIC register either in xAPIC or x2APIC mode.
35 It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be
36 accessed using multiple 32-bit loads or stores, so this function only performs
37 32-bit read.
38
39 @param MmioOffset The MMIO offset of the local APIC register in xAPIC mode.
40 It must be 16-byte aligned.
41
42 @return 32-bit Value read from the register.
43 **/
44 UINT32
45 EFIAPI
46 ReadLocalApicReg (
47 IN UINTN MmioOffset
48 )
49 {
50 UINT32 MsrIndex;
51
52 ASSERT ((MmioOffset & 0xf) == 0);
53
54 if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {
55 return MmioRead32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + MmioOffset);
56 } else {
57 //
58 // DFR is not supported in x2APIC mode.
59 //
60 ASSERT (MmioOffset != XAPIC_ICR_DFR_OFFSET);
61 //
62 // Note that in x2APIC mode, ICR is a 64-bit MSR that needs special treatment. It
63 // is not supported in this function for simplicity.
64 //
65 ASSERT (MmioOffset != XAPIC_ICR_HIGH_OFFSET);
66
67 MsrIndex = (UINT32)(MmioOffset >> 4) + X2APIC_MSR_BASE_ADDRESS;
68 return AsmReadMsr32 (MsrIndex);
69 }
70 }
71
72 /**
73 Write to a local APIC register.
74
75 This function writes to a local APIC register either in xAPIC or x2APIC mode.
76 It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be
77 accessed using multiple 32-bit loads or stores, so this function only performs
78 32-bit write.
79
80 if the register index is invalid or unsupported in current APIC mode, then ASSERT.
81
82 @param MmioOffset The MMIO offset of the local APIC register in xAPIC mode.
83 It must be 16-byte aligned.
84 @param Value Value to be written to the register.
85 **/
86 VOID
87 EFIAPI
88 WriteLocalApicReg (
89 IN UINTN MmioOffset,
90 IN UINT32 Value
91 )
92 {
93 UINT32 MsrIndex;
94
95 ASSERT ((MmioOffset & 0xf) == 0);
96
97 if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {
98 MmioWrite32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + MmioOffset, Value);
99 } else {
100 //
101 // DFR is not supported in x2APIC mode.
102 //
103 ASSERT (MmioOffset != XAPIC_ICR_DFR_OFFSET);
104 //
105 // Note that in x2APIC mode, ICR is a 64-bit MSR that needs special treatment. It
106 // is not supported in this function for simplicity.
107 //
108 ASSERT (MmioOffset != XAPIC_ICR_HIGH_OFFSET);
109 ASSERT (MmioOffset != XAPIC_ICR_LOW_OFFSET);
110
111 MsrIndex = (UINT32)(MmioOffset >> 4) + X2APIC_MSR_BASE_ADDRESS;
112 //
113 // The serializing semantics of WRMSR are relaxed when writing to the APIC registers.
114 // Use memory fence here to force the serializing semantics to be consisent with xAPIC mode.
115 //
116 MemoryFence ();
117 AsmWriteMsr32 (MsrIndex, Value);
118 }
119 }
120
121 /**
122 Send an IPI by writing to ICR.
123
124 This function returns after the IPI has been accepted by the target processor.
125
126 @param IcrLow 32-bit value to be written to the low half of ICR.
127 @param ApicId APIC ID of the target processor if this IPI is targeted for a specific processor.
128 **/
129 VOID
130 SendIpi (
131 IN UINT32 IcrLow,
132 IN UINT32 ApicId
133 )
134 {
135 UINT64 MsrValue;
136 LOCAL_APIC_ICR_LOW IcrLowReg;
137
138 if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {
139 ASSERT (ApicId <= 0xff);
140
141 //
142 // For xAPIC, the act of writing to the low doubleword of the ICR causes the IPI to be sent.
143 //
144 MmioWrite32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + XAPIC_ICR_HIGH_OFFSET, ApicId << 24);
145 MmioWrite32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + XAPIC_ICR_LOW_OFFSET, IcrLow);
146 do {
147 IcrLowReg.Uint32 = MmioRead32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + XAPIC_ICR_LOW_OFFSET);
148 } while (IcrLowReg.Bits.DeliveryStatus != 0);
149 } else {
150 //
151 // For x2APIC, A single MSR write to the Interrupt Command Register is required for dispatching an
152 // interrupt in x2APIC mode.
153 //
154 MsrValue = (((UINT64)ApicId) << 32) | IcrLow;
155 AsmWriteMsr64 (X2APIC_MSR_ICR_ADDRESS, MsrValue);
156 }
157 }
158
159 //
160 // Library API implementation functions
161 //
162
163 /**
164 Get the current local APIC mode.
165
166 If local APIC is disabled, then ASSERT.
167
168 @retval LOCAL_APIC_MODE_XAPIC current APIC mode is xAPIC.
169 @retval LOCAL_APIC_MODE_X2APIC current APIC mode is x2APIC.
170 **/
171 UINTN
172 EFIAPI
173 GetApicMode (
174 VOID
175 )
176 {
177 MSR_IA32_APIC_BASE ApicBaseMsr;
178
179 ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS);
180 //
181 // Local APIC should have been enabled
182 //
183 ASSERT (ApicBaseMsr.Bits.En != 0);
184 if (ApicBaseMsr.Bits.Extd != 0) {
185 return LOCAL_APIC_MODE_X2APIC;
186 } else {
187 return LOCAL_APIC_MODE_XAPIC;
188 }
189 }
190
191 /**
192 Set the current local APIC mode.
193
194 If the specified local APIC mode is not valid, then ASSERT.
195 If the specified local APIC mode can't be set as current, then ASSERT.
196
197 @param ApicMode APIC mode to be set.
198 **/
199 VOID
200 EFIAPI
201 SetApicMode (
202 IN UINTN ApicMode
203 )
204 {
205 UINTN CurrentMode;
206 MSR_IA32_APIC_BASE ApicBaseMsr;
207
208 CurrentMode = GetApicMode ();
209 if (CurrentMode == LOCAL_APIC_MODE_XAPIC) {
210 switch (ApicMode) {
211 case LOCAL_APIC_MODE_XAPIC:
212 break;
213 case LOCAL_APIC_MODE_X2APIC:
214 ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS);
215 ApicBaseMsr.Bits.Extd = 1;
216 AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64);
217 break;
218 default:
219 ASSERT (FALSE);
220 }
221 } else {
222 switch (ApicMode) {
223 case LOCAL_APIC_MODE_XAPIC:
224 //
225 // Transition from x2APIC mode to xAPIC mode is a two-step process:
226 // x2APIC -> Local APIC disabled -> xAPIC
227 //
228 ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS);
229 ApicBaseMsr.Bits.Extd = 0;
230 ApicBaseMsr.Bits.En = 0;
231 AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64);
232 ApicBaseMsr.Bits.En = 1;
233 AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64);
234 break;
235 case LOCAL_APIC_MODE_X2APIC:
236 break;
237 default:
238 ASSERT (FALSE);
239 }
240 }
241 }
242
243 /**
244 Get the initial local APIC ID of the executing processor assigned by hardware upon power on or reset.
245
246 In xAPIC mode, the initial local APIC ID is 8-bit, and may be different from current APIC ID.
247 In x2APIC mode, the local APIC ID can't be changed and there is no concept of initial APIC ID. In this case,
248 the 32-bit local APIC ID is returned as initial APIC ID.
249
250 @return 32-bit initial local APIC ID of the executing processor.
251 **/
252 UINT32
253 EFIAPI
254 GetInitialApicId (
255 VOID
256 )
257 {
258 UINT32 RegEbx;
259
260 if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {
261 AsmCpuid (CPUID_VERSION_INFO, NULL, &RegEbx, NULL, NULL);
262 return RegEbx >> 24;
263 } else {
264 return GetApicId ();
265 }
266 }
267
268 /**
269 Get the local APIC ID of the executing processor.
270
271 @return 32-bit local APIC ID of the executing processor.
272 **/
273 UINT32
274 EFIAPI
275 GetApicId (
276 VOID
277 )
278 {
279 UINT32 ApicId;
280
281 ApicId = ReadLocalApicReg (XAPIC_ID_OFFSET);
282 if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {
283 ApicId >>= 24;
284 }
285 return ApicId;
286 }
287
288 /**
289 Send a SMI IPI to a specified target processor.
290
291 This function returns after the IPI has been accepted by the target processor.
292
293 @param ApicId Specify the local APIC ID of the target processor.
294 **/
295 VOID
296 EFIAPI
297 SendSmiIpi (
298 IN UINT32 ApicId
299 )
300 {
301 LOCAL_APIC_ICR_LOW IcrLow;
302
303 IcrLow.Uint32 = 0;
304 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_SMI;
305 IcrLow.Bits.Level = 1;
306 SendIpi (IcrLow.Uint32, ApicId);
307 }
308
309 /**
310 Send a SMI IPI to all processors excluding self.
311
312 This function returns after the IPI has been accepted by the target processors.
313 **/
314 VOID
315 EFIAPI
316 SendSmiIpiAllExcludingSelf (
317 VOID
318 )
319 {
320 LOCAL_APIC_ICR_LOW IcrLow;
321
322 IcrLow.Uint32 = 0;
323 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_SMI;
324 IcrLow.Bits.Level = 1;
325 IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF;
326 SendIpi (IcrLow.Uint32, 0);
327 }
328
329 /**
330 Send an INIT IPI to a specified target processor.
331
332 This function returns after the IPI has been accepted by the target processor.
333
334 @param ApicId Specify the local APIC ID of the target processor.
335 **/
336 VOID
337 EFIAPI
338 SendInitIpi (
339 IN UINT32 ApicId
340 )
341 {
342 LOCAL_APIC_ICR_LOW IcrLow;
343
344 IcrLow.Uint32 = 0;
345 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_INIT;
346 IcrLow.Bits.Level = 1;
347 SendIpi (IcrLow.Uint32, ApicId);
348 }
349
350 /**
351 Send an INIT IPI to all processors excluding self.
352
353 This function returns after the IPI has been accepted by the target processors.
354 **/
355 VOID
356 EFIAPI
357 SendInitIpiAllExcludingSelf (
358 VOID
359 )
360 {
361 LOCAL_APIC_ICR_LOW IcrLow;
362
363 IcrLow.Uint32 = 0;
364 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_INIT;
365 IcrLow.Bits.Level = 1;
366 IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF;
367 SendIpi (IcrLow.Uint32, 0);
368 }
369
370 /**
371 Send an INIT-Start-up-Start-up IPI sequence to a specified target processor.
372
373 This function returns after the IPI has been accepted by the target processor.
374
375 if StartupRoutine >= 1M, then ASSERT.
376 if StartupRoutine is not multiple of 4K, then ASSERT.
377
378 @param ApicId Specify the local APIC ID of the target processor.
379 @param StartupRoutine Points to a start-up routine which is below 1M physical
380 address and 4K aligned.
381 **/
382 VOID
383 EFIAPI
384 SendInitSipiSipi (
385 IN UINT32 ApicId,
386 IN UINT32 StartupRoutine
387 )
388 {
389 LOCAL_APIC_ICR_LOW IcrLow;
390
391 ASSERT (StartupRoutine < 0x100000);
392 ASSERT ((StartupRoutine & 0xfff) == 0);
393
394 SendInitIpi (ApicId);
395 MicroSecondDelay (10);
396 IcrLow.Uint32 = 0;
397 IcrLow.Bits.Vector = (StartupRoutine >> 12);
398 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP;
399 IcrLow.Bits.Level = 1;
400 SendIpi (IcrLow.Uint32, ApicId);
401 MicroSecondDelay (200);
402 SendIpi (IcrLow.Uint32, ApicId);
403 }
404
405 /**
406 Send an INIT-Start-up-Start-up IPI sequence to all processors excluding self.
407
408 This function returns after the IPI has been accepted by the target processors.
409
410 if StartupRoutine >= 1M, then ASSERT.
411 if StartupRoutine is not multiple of 4K, then ASSERT.
412
413 @param StartupRoutine Points to a start-up routine which is below 1M physical
414 address and 4K aligned.
415 **/
416 VOID
417 EFIAPI
418 SendInitSipiSipiAllExcludingSelf (
419 IN UINT32 StartupRoutine
420 )
421 {
422 LOCAL_APIC_ICR_LOW IcrLow;
423
424 ASSERT (StartupRoutine < 0x100000);
425 ASSERT ((StartupRoutine & 0xfff) == 0);
426
427 SendInitIpiAllExcludingSelf ();
428 MicroSecondDelay (10);
429 IcrLow.Uint32 = 0;
430 IcrLow.Bits.Vector = (StartupRoutine >> 12);
431 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP;
432 IcrLow.Bits.Level = 1;
433 IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF;
434 SendIpi (IcrLow.Uint32, 0);
435 MicroSecondDelay (200);
436 SendIpi (IcrLow.Uint32, 0);
437 }
438
439 /**
440 Programming Virtual Wire Mode.
441
442 This function programs the local APIC for virtual wire mode following
443 the example described in chapter A.3 of the MP 1.4 spec.
444
445 IOxAPIC is not involved in this type of virtual wire mode.
446 **/
447 VOID
448 EFIAPI
449 ProgramVirtualWireMode (
450 VOID
451 )
452 {
453 LOCAL_APIC_SVR Svr;
454 LOCAL_APIC_LVT_LINT Lint;
455
456 //
457 // Enable the APIC via SVR and set the spurious interrupt to use Int 00F.
458 //
459 Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET);
460 Svr.Bits.SpuriousVector = 0xf;
461 Svr.Bits.SoftwareEnable = 1;
462 WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32);
463
464 //
465 // Program the LINT0 vector entry as ExtInt. Not masked, edge, active high.
466 //
467 Lint.Uint32 = ReadLocalApicReg (XAPIC_LINT0_VECTOR_OFFSET);
468 Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_EXTINT;
469 Lint.Bits.InputPinPolarity = 0;
470 Lint.Bits.TriggerMode = 0;
471 Lint.Bits.Mask = 0;
472 WriteLocalApicReg (XAPIC_LINT0_VECTOR_OFFSET, Lint.Uint32);
473
474 //
475 // Program the LINT0 vector entry as NMI. Not masked, edge, active high.
476 //
477 Lint.Uint32 = ReadLocalApicReg (XAPIC_LINT1_VECTOR_OFFSET);
478 Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_NMI;
479 Lint.Bits.InputPinPolarity = 0;
480 Lint.Bits.TriggerMode = 0;
481 Lint.Bits.Mask = 0;
482 WriteLocalApicReg (XAPIC_LINT1_VECTOR_OFFSET, Lint.Uint32);
483 }
484
485 /**
486 Get the divide value from the DCR (Divide Configuration Register) by which
487 the processor's bus clock is divided to form the time base for the APIC timer.
488
489 @return The divide value is one of 1,2,4,8,16,32,64,128.
490 **/
491 UINTN
492 EFIAPI
493 GetApicTimerDivisor (
494 VOID
495 )
496 {
497 UINT32 DivideValue;
498 LOCAL_APIC_DCR Dcr;
499
500 Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET);
501 DivideValue = Dcr.Bits.DivideValue1 | (Dcr.Bits.DivideValue2 << 2);
502 DivideValue = (DivideValue + 1) & 0x7;
503 return ((UINTN)1) << DivideValue;
504 }
505
506 /**
507 Read the initial count value from the init-count register.
508
509 @return The initial count value read from the init-count register.
510 **/
511 UINT32
512 EFIAPI
513 GetApicTimerInitCount (
514 VOID
515 )
516 {
517 return ReadLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET);
518 }
519
520 /**
521 Read the current count value from the current-count register.
522
523 @return The current count value read from the current-count register.
524 **/
525 UINT32
526 EFIAPI
527 GetApicTimerCurrentCount (
528 VOID
529 )
530 {
531 return ReadLocalApicReg (XAPIC_TIMER_CURRENT_COUNT_OFFSET);
532 }
533
534 /**
535 Initialize the local APIC timer.
536
537 The local APIC timer is initialized and enabled.
538
539 @param DivideValue The divide value for the DCR. It is one of 1,2,4,8,16,32,64,128.
540 If it is 0, then use the current divide value in the DCR.
541 @param InitCount The initial count value.
542 @param PeriodicMode If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot.
543 @param Vector The timer interrupt vector number.
544 **/
545 VOID
546 EFIAPI
547 InitializeApicTimer (
548 IN UINTN DivideValue,
549 IN UINT32 InitCount,
550 IN BOOLEAN PeriodicMode,
551 IN UINT8 Vector
552 )
553 {
554 LOCAL_APIC_SVR Svr;
555 LOCAL_APIC_DCR Dcr;
556 LOCAL_APIC_LVT_TIMER LvtTimer;
557 UINT32 Divisor;
558
559 //
560 // Ensure local APIC is in software-enabled state.
561 //
562 Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET);
563 Svr.Bits.SoftwareEnable = 1;
564 WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32);
565
566 //
567 // Program init-count register.
568 //
569 WriteLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET, InitCount);
570
571 if (DivideValue != 0) {
572 ASSERT (DivideValue <= 128);
573 ASSERT (DivideValue == GetPowerOfTwo32((UINT32)DivideValue));
574 Divisor = (UINT32)((HighBitSet32 ((UINT32)DivideValue) - 1) & 0x7);
575
576 Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET);
577 Dcr.Bits.DivideValue1 = (Divisor & 0x3);
578 Dcr.Bits.DivideValue2 = (Divisor >> 2);
579 WriteLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET, Dcr.Uint32);
580 }
581
582 //
583 // Enable APIC timer interrupt with specified timer mode.
584 //
585 LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
586 if (PeriodicMode) {
587 LvtTimer.Bits.TimerMode = 1;
588 } else {
589 LvtTimer.Bits.TimerMode = 0;
590 }
591 LvtTimer.Bits.Mask = 0;
592 LvtTimer.Bits.Vector = Vector;
593 WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32);
594 }
595
596 /**
597 Enable the local APIC timer interrupt.
598 **/
599 VOID
600 EFIAPI
601 EnableApicTimerInterrupt (
602 VOID
603 )
604 {
605 LOCAL_APIC_LVT_TIMER LvtTimer;
606
607 LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
608 LvtTimer.Bits.Mask = 0;
609 WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32);
610 }
611
612 /**
613 Disable the local APIC timer interrupt.
614 **/
615 VOID
616 EFIAPI
617 DisableApicTimerInterrupt (
618 VOID
619 )
620 {
621 LOCAL_APIC_LVT_TIMER LvtTimer;
622
623 LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
624 LvtTimer.Bits.Mask = 1;
625 WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32);
626 }
627
628 /**
629 Get the local APIC timer interrupt state.
630
631 @retval TRUE The local APIC timer interrupt is enabled.
632 @retval FALSE The local APIC timer interrupt is disabled.
633 **/
634 BOOLEAN
635 EFIAPI
636 GetApicTimerInterruptState (
637 VOID
638 )
639 {
640 LOCAL_APIC_LVT_TIMER LvtTimer;
641
642 LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
643 return (BOOLEAN)(LvtTimer.Bits.Mask == 0);
644 }
645
646 /**
647 Send EOI to the local APIC.
648 **/
649 VOID
650 EFIAPI
651 SendApicEoi (
652 VOID
653 )
654 {
655 WriteLocalApicReg (XAPIC_EOI_OFFSET, 0);
656 }
657