1 /** @file
2 Local APIC Library.
3
4 This local APIC library instance supports xAPIC mode only.
5
6 Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR>
7 Copyright (c) 2017, AMD Inc. All rights reserved.<BR>
8
9 SPDX-License-Identifier: BSD-2-Clause-Patent
10
11 **/
12
13 #include <Register/Intel/Cpuid.h>
14 #include <Register/Amd/Cpuid.h>
15 #include <Register/Intel/Msr.h>
16 #include <Register/Intel/LocalApic.h>
17
18 #include <Library/BaseLib.h>
19 #include <Library/DebugLib.h>
20 #include <Library/LocalApicLib.h>
21 #include <Library/IoLib.h>
22 #include <Library/TimerLib.h>
23 #include <Library/PcdLib.h>
24
25 //
26 // Library internal functions
27 //
28
29 /**
30 Determine if the standard CPU signature is "AuthenticAMD".
31
32 @retval TRUE The CPU signature matches.
33 @retval FALSE The CPU signature does not match.
34
35 **/
36 BOOLEAN
StandardSignatureIsAuthenticAMD(VOID)37 StandardSignatureIsAuthenticAMD (
38 VOID
39 )
40 {
41 UINT32 RegEbx;
42 UINT32 RegEcx;
43 UINT32 RegEdx;
44
45 AsmCpuid (CPUID_SIGNATURE, NULL, &RegEbx, &RegEcx, &RegEdx);
46 return (RegEbx == CPUID_SIGNATURE_AUTHENTIC_AMD_EBX &&
47 RegEcx == CPUID_SIGNATURE_AUTHENTIC_AMD_ECX &&
48 RegEdx == CPUID_SIGNATURE_AUTHENTIC_AMD_EDX);
49 }
50
51 /**
52 Determine if the CPU supports the Local APIC Base Address MSR.
53
54 @retval TRUE The CPU supports the Local APIC Base Address MSR.
55 @retval FALSE The CPU does not support the Local APIC Base Address MSR.
56
57 **/
58 BOOLEAN
LocalApicBaseAddressMsrSupported(VOID)59 LocalApicBaseAddressMsrSupported (
60 VOID
61 )
62 {
63 UINT32 RegEax;
64 UINTN FamilyId;
65
66 AsmCpuid (1, &RegEax, NULL, NULL, NULL);
67 FamilyId = BitFieldRead32 (RegEax, 8, 11);
68 if (FamilyId == 0x04 || FamilyId == 0x05) {
69 //
70 // CPUs with a FamilyId of 0x04 or 0x05 do not support the
71 // Local APIC Base Address MSR
72 //
73 return FALSE;
74 }
75 return TRUE;
76 }
77
78 /**
79 Retrieve the base address of local APIC.
80
81 @return The base address of local APIC.
82
83 **/
84 UINTN
85 EFIAPI
GetLocalApicBaseAddress(VOID)86 GetLocalApicBaseAddress (
87 VOID
88 )
89 {
90 MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr;
91
92 if (!LocalApicBaseAddressMsrSupported ()) {
93 //
94 // If CPU does not support Local APIC Base Address MSR, then retrieve
95 // Local APIC Base Address from PCD
96 //
97 return PcdGet32 (PcdCpuLocalApicBaseAddress);
98 }
99
100 ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);
101
102 return (UINTN)(LShiftU64 ((UINT64) ApicBaseMsr.Bits.ApicBaseHi, 32)) +
103 (((UINTN)ApicBaseMsr.Bits.ApicBase) << 12);
104 }
105
106 /**
107 Set the base address of local APIC.
108
109 If BaseAddress is not aligned on a 4KB boundary, then ASSERT().
110
111 @param[in] BaseAddress Local APIC base address to be set.
112
113 **/
114 VOID
115 EFIAPI
SetLocalApicBaseAddress(IN UINTN BaseAddress)116 SetLocalApicBaseAddress (
117 IN UINTN BaseAddress
118 )
119 {
120 MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr;
121
122 ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);
123
124 if (!LocalApicBaseAddressMsrSupported ()) {
125 //
126 // Ignore set request if the CPU does not support APIC Base Address MSR
127 //
128 return;
129 }
130
131 ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);
132
133 ApicBaseMsr.Bits.ApicBase = (UINT32) (BaseAddress >> 12);
134 ApicBaseMsr.Bits.ApicBaseHi = (UINT32) (RShiftU64((UINT64) BaseAddress, 32));
135
136 AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);
137 }
138
139 /**
140 Read from a local APIC register.
141
142 This function reads from a local APIC register either in xAPIC or x2APIC mode.
143 It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be
144 accessed using multiple 32-bit loads or stores, so this function only performs
145 32-bit read.
146
147 @param MmioOffset The MMIO offset of the local APIC register in xAPIC mode.
148 It must be 16-byte aligned.
149
150 @return 32-bit Value read from the register.
151 **/
152 UINT32
153 EFIAPI
ReadLocalApicReg(IN UINTN MmioOffset)154 ReadLocalApicReg (
155 IN UINTN MmioOffset
156 )
157 {
158 ASSERT ((MmioOffset & 0xf) == 0);
159 ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC);
160
161 return MmioRead32 (GetLocalApicBaseAddress() + MmioOffset);
162 }
163
164 /**
165 Write to a local APIC register.
166
167 This function writes to a local APIC register either in xAPIC or x2APIC mode.
168 It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be
169 accessed using multiple 32-bit loads or stores, so this function only performs
170 32-bit write.
171
172 if the register index is invalid or unsupported in current APIC mode, then ASSERT.
173
174 @param MmioOffset The MMIO offset of the local APIC register in xAPIC mode.
175 It must be 16-byte aligned.
176 @param Value Value to be written to the register.
177 **/
178 VOID
179 EFIAPI
WriteLocalApicReg(IN UINTN MmioOffset,IN UINT32 Value)180 WriteLocalApicReg (
181 IN UINTN MmioOffset,
182 IN UINT32 Value
183 )
184 {
185 ASSERT ((MmioOffset & 0xf) == 0);
186 ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC);
187
188 MmioWrite32 (GetLocalApicBaseAddress() + MmioOffset, Value);
189 }
190
191 /**
192 Send an IPI by writing to ICR.
193
194 This function returns after the IPI has been accepted by the target processor.
195
196 @param IcrLow 32-bit value to be written to the low half of ICR.
197 @param ApicId APIC ID of the target processor if this IPI is targeted for a specific processor.
198 **/
199 VOID
SendIpi(IN UINT32 IcrLow,IN UINT32 ApicId)200 SendIpi (
201 IN UINT32 IcrLow,
202 IN UINT32 ApicId
203 )
204 {
205 LOCAL_APIC_ICR_LOW IcrLowReg;
206 UINT32 IcrHigh;
207 BOOLEAN InterruptState;
208
209 ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC);
210 ASSERT (ApicId <= 0xff);
211
212 InterruptState = SaveAndDisableInterrupts ();
213
214 //
215 // Save existing contents of ICR high 32 bits
216 //
217 IcrHigh = ReadLocalApicReg (XAPIC_ICR_HIGH_OFFSET);
218
219 //
220 // Wait for DeliveryStatus clear in case a previous IPI
221 // is still being sent
222 //
223 do {
224 IcrLowReg.Uint32 = ReadLocalApicReg (XAPIC_ICR_LOW_OFFSET);
225 } while (IcrLowReg.Bits.DeliveryStatus != 0);
226
227 //
228 // For xAPIC, the act of writing to the low doubleword of the ICR causes the IPI to be sent.
229 //
230 WriteLocalApicReg (XAPIC_ICR_HIGH_OFFSET, ApicId << 24);
231 WriteLocalApicReg (XAPIC_ICR_LOW_OFFSET, IcrLow);
232
233 //
234 // Wait for DeliveryStatus clear again
235 //
236 do {
237 IcrLowReg.Uint32 = ReadLocalApicReg (XAPIC_ICR_LOW_OFFSET);
238 } while (IcrLowReg.Bits.DeliveryStatus != 0);
239
240 //
241 // And restore old contents of ICR high
242 //
243 WriteLocalApicReg (XAPIC_ICR_HIGH_OFFSET, IcrHigh);
244
245 SetInterruptState (InterruptState);
246
247 }
248
249 //
250 // Library API implementation functions
251 //
252
253 /**
254 Get the current local APIC mode.
255
256 If local APIC is disabled, then ASSERT.
257
258 @retval LOCAL_APIC_MODE_XAPIC current APIC mode is xAPIC.
259 @retval LOCAL_APIC_MODE_X2APIC current APIC mode is x2APIC.
260 **/
261 UINTN
262 EFIAPI
GetApicMode(VOID)263 GetApicMode (
264 VOID
265 )
266 {
267 DEBUG_CODE (
268 {
269 MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr;
270
271 //
272 // Check to see if the CPU supports the APIC Base Address MSR
273 //
274 if (LocalApicBaseAddressMsrSupported ()) {
275 ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);
276 //
277 // Local APIC should have been enabled
278 //
279 ASSERT (ApicBaseMsr.Bits.EN != 0);
280 ASSERT (ApicBaseMsr.Bits.EXTD == 0);
281 }
282 }
283 );
284 return LOCAL_APIC_MODE_XAPIC;
285 }
286
287 /**
288 Set the current local APIC mode.
289
290 If the specified local APIC mode is not valid, then ASSERT.
291 If the specified local APIC mode can't be set as current, then ASSERT.
292
293 @param ApicMode APIC mode to be set.
294
295 @note This API must not be called from an interrupt handler or SMI handler.
296 It may result in unpredictable behavior.
297 **/
298 VOID
299 EFIAPI
SetApicMode(IN UINTN ApicMode)300 SetApicMode (
301 IN UINTN ApicMode
302 )
303 {
304 ASSERT (ApicMode == LOCAL_APIC_MODE_XAPIC);
305 ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC);
306 }
307
308 /**
309 Get the initial local APIC ID of the executing processor assigned by hardware upon power on or reset.
310
311 In xAPIC mode, the initial local APIC ID may be different from current APIC ID.
312 In x2APIC mode, the local APIC ID can't be changed and there is no concept of initial APIC ID. In this case,
313 the 32-bit local APIC ID is returned as initial APIC ID.
314
315 @return 32-bit initial local APIC ID of the executing processor.
316 **/
317 UINT32
318 EFIAPI
GetInitialApicId(VOID)319 GetInitialApicId (
320 VOID
321 )
322 {
323 UINT32 ApicId;
324 UINT32 MaxCpuIdIndex;
325 UINT32 RegEbx;
326
327 ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC);
328
329 //
330 // Get the max index of basic CPUID
331 //
332 AsmCpuid (CPUID_SIGNATURE, &MaxCpuIdIndex, NULL, NULL, NULL);
333
334 //
335 // If CPUID Leaf B is supported,
336 // And CPUID.0BH:EBX[15:0] reports a non-zero value,
337 // Then the initial 32-bit APIC ID = CPUID.0BH:EDX
338 // Else the initial 8-bit APIC ID = CPUID.1:EBX[31:24]
339 //
340 if (MaxCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) {
341 AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 0, NULL, &RegEbx, NULL, &ApicId);
342 if ((RegEbx & (BIT16 - 1)) != 0) {
343 return ApicId;
344 }
345 }
346
347 AsmCpuid (CPUID_VERSION_INFO, NULL, &RegEbx, NULL, NULL);
348 return RegEbx >> 24;
349 }
350
351 /**
352 Get the local APIC ID of the executing processor.
353
354 @return 32-bit local APIC ID of the executing processor.
355 **/
356 UINT32
357 EFIAPI
GetApicId(VOID)358 GetApicId (
359 VOID
360 )
361 {
362 UINT32 ApicId;
363
364 ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC);
365
366 if ((ApicId = GetInitialApicId ()) < 0x100) {
367 //
368 // If the initial local APIC ID is less 0x100, read APIC ID from
369 // XAPIC_ID_OFFSET, otherwise return the initial local APIC ID.
370 //
371 ApicId = ReadLocalApicReg (XAPIC_ID_OFFSET);
372 ApicId >>= 24;
373 }
374 return ApicId;
375 }
376
377 /**
378 Get the value of the local APIC version register.
379
380 @return the value of the local APIC version register.
381 **/
382 UINT32
383 EFIAPI
GetApicVersion(VOID)384 GetApicVersion (
385 VOID
386 )
387 {
388 return ReadLocalApicReg (XAPIC_VERSION_OFFSET);
389 }
390
391 /**
392 Send a Fixed IPI to a specified target processor.
393
394 This function returns after the IPI has been accepted by the target processor.
395
396 @param ApicId The local APIC ID of the target processor.
397 @param Vector The vector number of the interrupt being sent.
398 **/
399 VOID
400 EFIAPI
SendFixedIpi(IN UINT32 ApicId,IN UINT8 Vector)401 SendFixedIpi (
402 IN UINT32 ApicId,
403 IN UINT8 Vector
404 )
405 {
406 LOCAL_APIC_ICR_LOW IcrLow;
407
408 IcrLow.Uint32 = 0;
409 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_FIXED;
410 IcrLow.Bits.Level = 1;
411 IcrLow.Bits.Vector = Vector;
412 SendIpi (IcrLow.Uint32, ApicId);
413 }
414
415 /**
416 Send a Fixed IPI to all processors excluding self.
417
418 This function returns after the IPI has been accepted by the target processors.
419
420 @param Vector The vector number of the interrupt being sent.
421 **/
422 VOID
423 EFIAPI
SendFixedIpiAllExcludingSelf(IN UINT8 Vector)424 SendFixedIpiAllExcludingSelf (
425 IN UINT8 Vector
426 )
427 {
428 LOCAL_APIC_ICR_LOW IcrLow;
429
430 IcrLow.Uint32 = 0;
431 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_FIXED;
432 IcrLow.Bits.Level = 1;
433 IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF;
434 IcrLow.Bits.Vector = Vector;
435 SendIpi (IcrLow.Uint32, 0);
436 }
437
438 /**
439 Send a SMI IPI to a specified target processor.
440
441 This function returns after the IPI has been accepted by the target processor.
442
443 @param ApicId Specify the local APIC ID of the target processor.
444 **/
445 VOID
446 EFIAPI
SendSmiIpi(IN UINT32 ApicId)447 SendSmiIpi (
448 IN UINT32 ApicId
449 )
450 {
451 LOCAL_APIC_ICR_LOW IcrLow;
452
453 IcrLow.Uint32 = 0;
454 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_SMI;
455 IcrLow.Bits.Level = 1;
456 SendIpi (IcrLow.Uint32, ApicId);
457 }
458
459 /**
460 Send a SMI IPI to all processors excluding self.
461
462 This function returns after the IPI has been accepted by the target processors.
463 **/
464 VOID
465 EFIAPI
SendSmiIpiAllExcludingSelf(VOID)466 SendSmiIpiAllExcludingSelf (
467 VOID
468 )
469 {
470 LOCAL_APIC_ICR_LOW IcrLow;
471
472 IcrLow.Uint32 = 0;
473 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_SMI;
474 IcrLow.Bits.Level = 1;
475 IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF;
476 SendIpi (IcrLow.Uint32, 0);
477 }
478
479 /**
480 Send an INIT IPI to a specified target processor.
481
482 This function returns after the IPI has been accepted by the target processor.
483
484 @param ApicId Specify the local APIC ID of the target processor.
485 **/
486 VOID
487 EFIAPI
SendInitIpi(IN UINT32 ApicId)488 SendInitIpi (
489 IN UINT32 ApicId
490 )
491 {
492 LOCAL_APIC_ICR_LOW IcrLow;
493
494 IcrLow.Uint32 = 0;
495 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_INIT;
496 IcrLow.Bits.Level = 1;
497 SendIpi (IcrLow.Uint32, ApicId);
498 }
499
500 /**
501 Send an INIT IPI to all processors excluding self.
502
503 This function returns after the IPI has been accepted by the target processors.
504 **/
505 VOID
506 EFIAPI
SendInitIpiAllExcludingSelf(VOID)507 SendInitIpiAllExcludingSelf (
508 VOID
509 )
510 {
511 LOCAL_APIC_ICR_LOW IcrLow;
512
513 IcrLow.Uint32 = 0;
514 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_INIT;
515 IcrLow.Bits.Level = 1;
516 IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF;
517 SendIpi (IcrLow.Uint32, 0);
518 }
519
520 /**
521 Send an INIT-Start-up-Start-up IPI sequence to a specified target processor.
522
523 This function returns after the IPI has been accepted by the target processor.
524
525 if StartupRoutine >= 1M, then ASSERT.
526 if StartupRoutine is not multiple of 4K, then ASSERT.
527
528 @param ApicId Specify the local APIC ID of the target processor.
529 @param StartupRoutine Points to a start-up routine which is below 1M physical
530 address and 4K aligned.
531 **/
532 VOID
533 EFIAPI
SendInitSipiSipi(IN UINT32 ApicId,IN UINT32 StartupRoutine)534 SendInitSipiSipi (
535 IN UINT32 ApicId,
536 IN UINT32 StartupRoutine
537 )
538 {
539 LOCAL_APIC_ICR_LOW IcrLow;
540
541 ASSERT (StartupRoutine < 0x100000);
542 ASSERT ((StartupRoutine & 0xfff) == 0);
543
544 SendInitIpi (ApicId);
545 MicroSecondDelay (PcdGet32(PcdCpuInitIpiDelayInMicroSeconds));
546 IcrLow.Uint32 = 0;
547 IcrLow.Bits.Vector = (StartupRoutine >> 12);
548 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP;
549 IcrLow.Bits.Level = 1;
550 SendIpi (IcrLow.Uint32, ApicId);
551 if (!StandardSignatureIsAuthenticAMD ()) {
552 MicroSecondDelay (200);
553 SendIpi (IcrLow.Uint32, ApicId);
554 }
555 }
556
557 /**
558 Send an INIT-Start-up-Start-up IPI sequence to all processors excluding self.
559
560 This function returns after the IPI has been accepted by the target processors.
561
562 if StartupRoutine >= 1M, then ASSERT.
563 if StartupRoutine is not multiple of 4K, then ASSERT.
564
565 @param StartupRoutine Points to a start-up routine which is below 1M physical
566 address and 4K aligned.
567 **/
568 VOID
569 EFIAPI
SendInitSipiSipiAllExcludingSelf(IN UINT32 StartupRoutine)570 SendInitSipiSipiAllExcludingSelf (
571 IN UINT32 StartupRoutine
572 )
573 {
574 LOCAL_APIC_ICR_LOW IcrLow;
575
576 ASSERT (StartupRoutine < 0x100000);
577 ASSERT ((StartupRoutine & 0xfff) == 0);
578
579 SendInitIpiAllExcludingSelf ();
580 MicroSecondDelay (PcdGet32(PcdCpuInitIpiDelayInMicroSeconds));
581 IcrLow.Uint32 = 0;
582 IcrLow.Bits.Vector = (StartupRoutine >> 12);
583 IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP;
584 IcrLow.Bits.Level = 1;
585 IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF;
586 SendIpi (IcrLow.Uint32, 0);
587 if (!StandardSignatureIsAuthenticAMD ()) {
588 MicroSecondDelay (200);
589 SendIpi (IcrLow.Uint32, 0);
590 }
591 }
592
593 /**
594 Initialize the state of the SoftwareEnable bit in the Local APIC
595 Spurious Interrupt Vector register.
596
597 @param Enable If TRUE, then set SoftwareEnable to 1
598 If FALSE, then set SoftwareEnable to 0.
599
600 **/
601 VOID
602 EFIAPI
InitializeLocalApicSoftwareEnable(IN BOOLEAN Enable)603 InitializeLocalApicSoftwareEnable (
604 IN BOOLEAN Enable
605 )
606 {
607 LOCAL_APIC_SVR Svr;
608
609 //
610 // Set local APIC software-enabled bit.
611 //
612 Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET);
613 if (Enable) {
614 if (Svr.Bits.SoftwareEnable == 0) {
615 Svr.Bits.SoftwareEnable = 1;
616 WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32);
617 }
618 } else {
619 if (Svr.Bits.SoftwareEnable == 1) {
620 Svr.Bits.SoftwareEnable = 0;
621 WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32);
622 }
623 }
624 }
625
626 /**
627 Programming Virtual Wire Mode.
628
629 This function programs the local APIC for virtual wire mode following
630 the example described in chapter A.3 of the MP 1.4 spec.
631
632 IOxAPIC is not involved in this type of virtual wire mode.
633 **/
634 VOID
635 EFIAPI
ProgramVirtualWireMode(VOID)636 ProgramVirtualWireMode (
637 VOID
638 )
639 {
640 LOCAL_APIC_SVR Svr;
641 LOCAL_APIC_LVT_LINT Lint;
642
643 //
644 // Enable the APIC via SVR and set the spurious interrupt to use Int 00F.
645 //
646 Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET);
647 Svr.Bits.SpuriousVector = 0xf;
648 Svr.Bits.SoftwareEnable = 1;
649 WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32);
650
651 //
652 // Program the LINT0 vector entry as ExtInt. Not masked, edge, active high.
653 //
654 Lint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT0_OFFSET);
655 Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_EXTINT;
656 Lint.Bits.InputPinPolarity = 0;
657 Lint.Bits.TriggerMode = 0;
658 Lint.Bits.Mask = 0;
659 WriteLocalApicReg (XAPIC_LVT_LINT0_OFFSET, Lint.Uint32);
660
661 //
662 // Program the LINT0 vector entry as NMI. Not masked, edge, active high.
663 //
664 Lint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT1_OFFSET);
665 Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_NMI;
666 Lint.Bits.InputPinPolarity = 0;
667 Lint.Bits.TriggerMode = 0;
668 Lint.Bits.Mask = 0;
669 WriteLocalApicReg (XAPIC_LVT_LINT1_OFFSET, Lint.Uint32);
670 }
671
672 /**
673 Disable LINT0 & LINT1 interrupts.
674
675 This function sets the mask flag in the LVT LINT0 & LINT1 registers.
676 **/
677 VOID
678 EFIAPI
DisableLvtInterrupts(VOID)679 DisableLvtInterrupts (
680 VOID
681 )
682 {
683 LOCAL_APIC_LVT_LINT LvtLint;
684
685 LvtLint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT0_OFFSET);
686 LvtLint.Bits.Mask = 1;
687 WriteLocalApicReg (XAPIC_LVT_LINT0_OFFSET, LvtLint.Uint32);
688
689 LvtLint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT1_OFFSET);
690 LvtLint.Bits.Mask = 1;
691 WriteLocalApicReg (XAPIC_LVT_LINT1_OFFSET, LvtLint.Uint32);
692 }
693
694 /**
695 Read the initial count value from the init-count register.
696
697 @return The initial count value read from the init-count register.
698 **/
699 UINT32
700 EFIAPI
GetApicTimerInitCount(VOID)701 GetApicTimerInitCount (
702 VOID
703 )
704 {
705 return ReadLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET);
706 }
707
708 /**
709 Read the current count value from the current-count register.
710
711 @return The current count value read from the current-count register.
712 **/
713 UINT32
714 EFIAPI
GetApicTimerCurrentCount(VOID)715 GetApicTimerCurrentCount (
716 VOID
717 )
718 {
719 return ReadLocalApicReg (XAPIC_TIMER_CURRENT_COUNT_OFFSET);
720 }
721
722 /**
723 Initialize the local APIC timer.
724
725 The local APIC timer is initialized and enabled.
726
727 @param DivideValue The divide value for the DCR. It is one of 1,2,4,8,16,32,64,128.
728 If it is 0, then use the current divide value in the DCR.
729 @param InitCount The initial count value.
730 @param PeriodicMode If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot.
731 @param Vector The timer interrupt vector number.
732 **/
733 VOID
734 EFIAPI
InitializeApicTimer(IN UINTN DivideValue,IN UINT32 InitCount,IN BOOLEAN PeriodicMode,IN UINT8 Vector)735 InitializeApicTimer (
736 IN UINTN DivideValue,
737 IN UINT32 InitCount,
738 IN BOOLEAN PeriodicMode,
739 IN UINT8 Vector
740 )
741 {
742 LOCAL_APIC_DCR Dcr;
743 LOCAL_APIC_LVT_TIMER LvtTimer;
744 UINT32 Divisor;
745
746 //
747 // Ensure local APIC is in software-enabled state.
748 //
749 InitializeLocalApicSoftwareEnable (TRUE);
750
751 //
752 // Program init-count register.
753 //
754 WriteLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET, InitCount);
755
756 if (DivideValue != 0) {
757 ASSERT (DivideValue <= 128);
758 ASSERT (DivideValue == GetPowerOfTwo32((UINT32)DivideValue));
759 Divisor = (UINT32)((HighBitSet32 ((UINT32)DivideValue) - 1) & 0x7);
760
761 Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET);
762 Dcr.Bits.DivideValue1 = (Divisor & 0x3);
763 Dcr.Bits.DivideValue2 = (Divisor >> 2);
764 WriteLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET, Dcr.Uint32);
765 }
766
767 //
768 // Enable APIC timer interrupt with specified timer mode.
769 //
770 LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
771 if (PeriodicMode) {
772 LvtTimer.Bits.TimerMode = 1;
773 } else {
774 LvtTimer.Bits.TimerMode = 0;
775 }
776 LvtTimer.Bits.Mask = 0;
777 LvtTimer.Bits.Vector = Vector;
778 WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32);
779 }
780
781 /**
782 Get the state of the local APIC timer.
783
784 This function will ASSERT if the local APIC is not software enabled.
785
786 @param DivideValue Return the divide value for the DCR. It is one of 1,2,4,8,16,32,64,128.
787 @param PeriodicMode Return the timer mode. If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot.
788 @param Vector Return the timer interrupt vector number.
789 **/
790 VOID
791 EFIAPI
GetApicTimerState(OUT UINTN * DivideValue OPTIONAL,OUT BOOLEAN * PeriodicMode OPTIONAL,OUT UINT8 * Vector OPTIONAL)792 GetApicTimerState (
793 OUT UINTN *DivideValue OPTIONAL,
794 OUT BOOLEAN *PeriodicMode OPTIONAL,
795 OUT UINT8 *Vector OPTIONAL
796 )
797 {
798 UINT32 Divisor;
799 LOCAL_APIC_DCR Dcr;
800 LOCAL_APIC_LVT_TIMER LvtTimer;
801
802 //
803 // Check the APIC Software Enable/Disable bit (bit 8) in Spurious-Interrupt
804 // Vector Register.
805 // This bit will be 1, if local APIC is software enabled.
806 //
807 ASSERT ((ReadLocalApicReg(XAPIC_SPURIOUS_VECTOR_OFFSET) & BIT8) != 0);
808
809 if (DivideValue != NULL) {
810 Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET);
811 Divisor = Dcr.Bits.DivideValue1 | (Dcr.Bits.DivideValue2 << 2);
812 Divisor = (Divisor + 1) & 0x7;
813 *DivideValue = ((UINTN)1) << Divisor;
814 }
815
816 if (PeriodicMode != NULL || Vector != NULL) {
817 LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
818 if (PeriodicMode != NULL) {
819 if (LvtTimer.Bits.TimerMode == 1) {
820 *PeriodicMode = TRUE;
821 } else {
822 *PeriodicMode = FALSE;
823 }
824 }
825 if (Vector != NULL) {
826 *Vector = (UINT8) LvtTimer.Bits.Vector;
827 }
828 }
829 }
830
831 /**
832 Enable the local APIC timer interrupt.
833 **/
834 VOID
835 EFIAPI
EnableApicTimerInterrupt(VOID)836 EnableApicTimerInterrupt (
837 VOID
838 )
839 {
840 LOCAL_APIC_LVT_TIMER LvtTimer;
841
842 LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
843 LvtTimer.Bits.Mask = 0;
844 WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32);
845 }
846
847 /**
848 Disable the local APIC timer interrupt.
849 **/
850 VOID
851 EFIAPI
DisableApicTimerInterrupt(VOID)852 DisableApicTimerInterrupt (
853 VOID
854 )
855 {
856 LOCAL_APIC_LVT_TIMER LvtTimer;
857
858 LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
859 LvtTimer.Bits.Mask = 1;
860 WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32);
861 }
862
863 /**
864 Get the local APIC timer interrupt state.
865
866 @retval TRUE The local APIC timer interrupt is enabled.
867 @retval FALSE The local APIC timer interrupt is disabled.
868 **/
869 BOOLEAN
870 EFIAPI
GetApicTimerInterruptState(VOID)871 GetApicTimerInterruptState (
872 VOID
873 )
874 {
875 LOCAL_APIC_LVT_TIMER LvtTimer;
876
877 LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET);
878 return (BOOLEAN)(LvtTimer.Bits.Mask == 0);
879 }
880
881 /**
882 Send EOI to the local APIC.
883 **/
884 VOID
885 EFIAPI
SendApicEoi(VOID)886 SendApicEoi (
887 VOID
888 )
889 {
890 WriteLocalApicReg (XAPIC_EOI_OFFSET, 0);
891 }
892
893 /**
894 Get the 32-bit address that a device should use to send a Message Signaled
895 Interrupt (MSI) to the Local APIC of the currently executing processor.
896
897 @return 32-bit address used to send an MSI to the Local APIC.
898 **/
899 UINT32
900 EFIAPI
GetApicMsiAddress(VOID)901 GetApicMsiAddress (
902 VOID
903 )
904 {
905 LOCAL_APIC_MSI_ADDRESS MsiAddress;
906
907 //
908 // Return address for an MSI interrupt to be delivered only to the APIC ID
909 // of the currently executing processor.
910 //
911 MsiAddress.Uint32 = 0;
912 MsiAddress.Bits.BaseAddress = 0xFEE;
913 MsiAddress.Bits.DestinationId = GetApicId ();
914 return MsiAddress.Uint32;
915 }
916
917 /**
918 Get the 64-bit data value that a device should use to send a Message Signaled
919 Interrupt (MSI) to the Local APIC of the currently executing processor.
920
921 If Vector is not in range 0x10..0xFE, then ASSERT().
922 If DeliveryMode is not supported, then ASSERT().
923
924 @param Vector The 8-bit interrupt vector associated with the MSI.
925 Must be in the range 0x10..0xFE
926 @param DeliveryMode A 3-bit value that specifies how the recept of the MSI
927 is handled. The only supported values are:
928 0: LOCAL_APIC_DELIVERY_MODE_FIXED
929 1: LOCAL_APIC_DELIVERY_MODE_LOWEST_PRIORITY
930 2: LOCAL_APIC_DELIVERY_MODE_SMI
931 4: LOCAL_APIC_DELIVERY_MODE_NMI
932 5: LOCAL_APIC_DELIVERY_MODE_INIT
933 7: LOCAL_APIC_DELIVERY_MODE_EXTINT
934
935 @param LevelTriggered TRUE specifies a level triggered interrupt.
936 FALSE specifies an edge triggered interrupt.
937 @param AssertionLevel Ignored if LevelTriggered is FALSE.
938 TRUE specifies a level triggered interrupt that active
939 when the interrupt line is asserted.
940 FALSE specifies a level triggered interrupt that active
941 when the interrupt line is deasserted.
942
943 @return 64-bit data value used to send an MSI to the Local APIC.
944 **/
945 UINT64
946 EFIAPI
GetApicMsiValue(IN UINT8 Vector,IN UINTN DeliveryMode,IN BOOLEAN LevelTriggered,IN BOOLEAN AssertionLevel)947 GetApicMsiValue (
948 IN UINT8 Vector,
949 IN UINTN DeliveryMode,
950 IN BOOLEAN LevelTriggered,
951 IN BOOLEAN AssertionLevel
952 )
953 {
954 LOCAL_APIC_MSI_DATA MsiData;
955
956 ASSERT (Vector >= 0x10 && Vector <= 0xFE);
957 ASSERT (DeliveryMode < 8 && DeliveryMode != 6 && DeliveryMode != 3);
958
959 MsiData.Uint64 = 0;
960 MsiData.Bits.Vector = Vector;
961 MsiData.Bits.DeliveryMode = (UINT32)DeliveryMode;
962 if (LevelTriggered) {
963 MsiData.Bits.TriggerMode = 1;
964 if (AssertionLevel) {
965 MsiData.Bits.Level = 1;
966 }
967 }
968 return MsiData.Uint64;
969 }
970
971 /**
972 Get Package ID/Core ID/Thread ID of a processor.
973
974 The algorithm assumes the target system has symmetry across physical
975 package boundaries with respect to the number of logical processors
976 per package, number of cores per package.
977
978 @param[in] InitialApicId Initial APIC ID of the target logical processor.
979 @param[out] Package Returns the processor package ID.
980 @param[out] Core Returns the processor core ID.
981 @param[out] Thread Returns the processor thread ID.
982 **/
983 VOID
984 EFIAPI
GetProcessorLocationByApicId(IN UINT32 InitialApicId,OUT UINT32 * Package OPTIONAL,OUT UINT32 * Core OPTIONAL,OUT UINT32 * Thread OPTIONAL)985 GetProcessorLocationByApicId (
986 IN UINT32 InitialApicId,
987 OUT UINT32 *Package OPTIONAL,
988 OUT UINT32 *Core OPTIONAL,
989 OUT UINT32 *Thread OPTIONAL
990 )
991 {
992 BOOLEAN TopologyLeafSupported;
993 CPUID_VERSION_INFO_EBX VersionInfoEbx;
994 CPUID_VERSION_INFO_EDX VersionInfoEdx;
995 CPUID_CACHE_PARAMS_EAX CacheParamsEax;
996 CPUID_EXTENDED_TOPOLOGY_EAX ExtendedTopologyEax;
997 CPUID_EXTENDED_TOPOLOGY_EBX ExtendedTopologyEbx;
998 CPUID_EXTENDED_TOPOLOGY_ECX ExtendedTopologyEcx;
999 CPUID_AMD_EXTENDED_CPU_SIG_ECX AmdExtendedCpuSigEcx;
1000 CPUID_AMD_PROCESSOR_TOPOLOGY_EBX AmdProcessorTopologyEbx;
1001 CPUID_AMD_VIR_PHY_ADDRESS_SIZE_ECX AmdVirPhyAddressSizeEcx;
1002 UINT32 MaxStandardCpuIdIndex;
1003 UINT32 MaxExtendedCpuIdIndex;
1004 UINT32 SubIndex;
1005 UINTN LevelType;
1006 UINT32 MaxLogicProcessorsPerPackage;
1007 UINT32 MaxCoresPerPackage;
1008 UINTN ThreadBits;
1009 UINTN CoreBits;
1010
1011 //
1012 // Check if the processor is capable of supporting more than one logical processor.
1013 //
1014 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32);
1015 if (VersionInfoEdx.Bits.HTT == 0) {
1016 if (Thread != NULL) {
1017 *Thread = 0;
1018 }
1019 if (Core != NULL) {
1020 *Core = 0;
1021 }
1022 if (Package != NULL) {
1023 *Package = 0;
1024 }
1025 return;
1026 }
1027
1028 //
1029 // Assume three-level mapping of APIC ID: Package|Core|Thread.
1030 //
1031 ThreadBits = 0;
1032 CoreBits = 0;
1033
1034 //
1035 // Get max index of CPUID
1036 //
1037 AsmCpuid (CPUID_SIGNATURE, &MaxStandardCpuIdIndex, NULL, NULL, NULL);
1038 AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedCpuIdIndex, NULL, NULL, NULL);
1039
1040 //
1041 // If the extended topology enumeration leaf is available, it
1042 // is the preferred mechanism for enumerating topology.
1043 //
1044 TopologyLeafSupported = FALSE;
1045 if (MaxStandardCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) {
1046 AsmCpuidEx(
1047 CPUID_EXTENDED_TOPOLOGY,
1048 0,
1049 &ExtendedTopologyEax.Uint32,
1050 &ExtendedTopologyEbx.Uint32,
1051 &ExtendedTopologyEcx.Uint32,
1052 NULL
1053 );
1054 //
1055 // If CPUID.(EAX=0BH, ECX=0H):EBX returns zero and maximum input value for
1056 // basic CPUID information is greater than 0BH, then CPUID.0BH leaf is not
1057 // supported on that processor.
1058 //
1059 if (ExtendedTopologyEbx.Uint32 != 0) {
1060 TopologyLeafSupported = TRUE;
1061
1062 //
1063 // Sub-leaf index 0 (ECX= 0 as input) provides enumeration parameters to extract
1064 // the SMT sub-field of x2APIC ID.
1065 //
1066 LevelType = ExtendedTopologyEcx.Bits.LevelType;
1067 ASSERT (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT);
1068 ThreadBits = ExtendedTopologyEax.Bits.ApicIdShift;
1069
1070 //
1071 // Software must not assume any "level type" encoding
1072 // value to be related to any sub-leaf index, except sub-leaf 0.
1073 //
1074 SubIndex = 1;
1075 do {
1076 AsmCpuidEx (
1077 CPUID_EXTENDED_TOPOLOGY,
1078 SubIndex,
1079 &ExtendedTopologyEax.Uint32,
1080 NULL,
1081 &ExtendedTopologyEcx.Uint32,
1082 NULL
1083 );
1084 LevelType = ExtendedTopologyEcx.Bits.LevelType;
1085 if (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE) {
1086 CoreBits = ExtendedTopologyEax.Bits.ApicIdShift - ThreadBits;
1087 break;
1088 }
1089 SubIndex++;
1090 } while (LevelType != CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID);
1091 }
1092 }
1093
1094 if (!TopologyLeafSupported) {
1095 //
1096 // Get logical processor count
1097 //
1098 AsmCpuid (CPUID_VERSION_INFO, NULL, &VersionInfoEbx.Uint32, NULL, NULL);
1099 MaxLogicProcessorsPerPackage = VersionInfoEbx.Bits.MaximumAddressableIdsForLogicalProcessors;
1100
1101 //
1102 // Assume single-core processor
1103 //
1104 MaxCoresPerPackage = 1;
1105
1106 //
1107 // Check for topology extensions on AMD processor
1108 //
1109 if (StandardSignatureIsAuthenticAMD()) {
1110 if (MaxExtendedCpuIdIndex >= CPUID_AMD_PROCESSOR_TOPOLOGY) {
1111 AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, &AmdExtendedCpuSigEcx.Uint32, NULL);
1112 if (AmdExtendedCpuSigEcx.Bits.TopologyExtensions != 0) {
1113 //
1114 // Account for max possible thread count to decode ApicId
1115 //
1116 AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, NULL, NULL, &AmdVirPhyAddressSizeEcx.Uint32, NULL);
1117 MaxLogicProcessorsPerPackage = 1 << AmdVirPhyAddressSizeEcx.Bits.ApicIdCoreIdSize;
1118
1119 //
1120 // Get cores per processor package
1121 //
1122 AsmCpuid (CPUID_AMD_PROCESSOR_TOPOLOGY, NULL, &AmdProcessorTopologyEbx.Uint32, NULL, NULL);
1123 MaxCoresPerPackage = MaxLogicProcessorsPerPackage / (AmdProcessorTopologyEbx.Bits.ThreadsPerCore + 1);
1124 }
1125 }
1126 }
1127 else {
1128 //
1129 // Extract core count based on CACHE information
1130 //
1131 if (MaxStandardCpuIdIndex >= CPUID_CACHE_PARAMS) {
1132 AsmCpuidEx (CPUID_CACHE_PARAMS, 0, &CacheParamsEax.Uint32, NULL, NULL, NULL);
1133 if (CacheParamsEax.Uint32 != 0) {
1134 MaxCoresPerPackage = CacheParamsEax.Bits.MaximumAddressableIdsForLogicalProcessors + 1;
1135 }
1136 }
1137 }
1138
1139 ThreadBits = (UINTN)(HighBitSet32(MaxLogicProcessorsPerPackage / MaxCoresPerPackage - 1) + 1);
1140 CoreBits = (UINTN)(HighBitSet32(MaxCoresPerPackage - 1) + 1);
1141 }
1142
1143 if (Thread != NULL) {
1144 *Thread = InitialApicId & ((1 << ThreadBits) - 1);
1145 }
1146 if (Core != NULL) {
1147 *Core = (InitialApicId >> ThreadBits) & ((1 << CoreBits) - 1);
1148 }
1149 if (Package != NULL) {
1150 *Package = (InitialApicId >> (ThreadBits + CoreBits));
1151 }
1152 }
1153
1154 /**
1155 Get Package ID/Die ID/Tile ID/Module ID/Core ID/Thread ID of a processor.
1156
1157 The algorithm assumes the target system has symmetry across physical
1158 package boundaries with respect to the number of threads per core, number of
1159 cores per module, number of modules per tile, number of tiles per die, number
1160 of dies per package.
1161
1162 @param[in] InitialApicId Initial APIC ID of the target logical processor.
1163 @param[out] Package Returns the processor package ID.
1164 @param[out] Die Returns the processor die ID.
1165 @param[out] Tile Returns the processor tile ID.
1166 @param[out] Module Returns the processor module ID.
1167 @param[out] Core Returns the processor core ID.
1168 @param[out] Thread Returns the processor thread ID.
1169 **/
1170 VOID
1171 EFIAPI
GetProcessorLocation2ByApicId(IN UINT32 InitialApicId,OUT UINT32 * Package OPTIONAL,OUT UINT32 * Die OPTIONAL,OUT UINT32 * Tile OPTIONAL,OUT UINT32 * Module OPTIONAL,OUT UINT32 * Core OPTIONAL,OUT UINT32 * Thread OPTIONAL)1172 GetProcessorLocation2ByApicId (
1173 IN UINT32 InitialApicId,
1174 OUT UINT32 *Package OPTIONAL,
1175 OUT UINT32 *Die OPTIONAL,
1176 OUT UINT32 *Tile OPTIONAL,
1177 OUT UINT32 *Module OPTIONAL,
1178 OUT UINT32 *Core OPTIONAL,
1179 OUT UINT32 *Thread OPTIONAL
1180 )
1181 {
1182 CPUID_EXTENDED_TOPOLOGY_EAX ExtendedTopologyEax;
1183 CPUID_EXTENDED_TOPOLOGY_ECX ExtendedTopologyEcx;
1184 UINT32 MaxStandardCpuIdIndex;
1185 UINT32 Index;
1186 UINTN LevelType;
1187 UINT32 Bits[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 2];
1188 UINT32 *Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 2];
1189
1190 for (LevelType = 0; LevelType < ARRAY_SIZE (Bits); LevelType++) {
1191 Bits[LevelType] = 0;
1192 }
1193
1194 //
1195 // Get max index of CPUID
1196 //
1197 AsmCpuid (CPUID_SIGNATURE, &MaxStandardCpuIdIndex, NULL, NULL, NULL);
1198 if (MaxStandardCpuIdIndex < CPUID_V2_EXTENDED_TOPOLOGY) {
1199 if (Die != NULL) {
1200 *Die = 0;
1201 }
1202 if (Tile != NULL) {
1203 *Tile = 0;
1204 }
1205 if (Module != NULL) {
1206 *Module = 0;
1207 }
1208 GetProcessorLocationByApicId (InitialApicId, Package, Core, Thread);
1209 return;
1210 }
1211
1212 //
1213 // If the V2 extended topology enumeration leaf is available, it
1214 // is the preferred mechanism for enumerating topology.
1215 //
1216 for (Index = 0; ; Index++) {
1217 AsmCpuidEx(
1218 CPUID_V2_EXTENDED_TOPOLOGY,
1219 Index,
1220 &ExtendedTopologyEax.Uint32,
1221 NULL,
1222 &ExtendedTopologyEcx.Uint32,
1223 NULL
1224 );
1225
1226 LevelType = ExtendedTopologyEcx.Bits.LevelType;
1227
1228 //
1229 // first level reported should be SMT.
1230 //
1231 ASSERT ((Index != 0) || (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT));
1232 if (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID) {
1233 break;
1234 }
1235 ASSERT (LevelType < ARRAY_SIZE (Bits));
1236 Bits[LevelType] = ExtendedTopologyEax.Bits.ApicIdShift;
1237 }
1238
1239 for (LevelType = CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE; LevelType < ARRAY_SIZE (Bits); LevelType++) {
1240 //
1241 // If there are more levels between level-1 (low-level) and level-2 (high-level), the unknown levels will be ignored
1242 // and treated as an extension of the last known level (i.e., level-1 in this case).
1243 //
1244 if (Bits[LevelType] == 0) {
1245 Bits[LevelType] = Bits[LevelType - 1];
1246 }
1247 }
1248
1249 Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 1] = Package;
1250 Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE ] = Die;
1251 Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_TILE ] = Tile;
1252 Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_MODULE ] = Module;
1253 Location[CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE ] = Core;
1254 Location[CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT ] = Thread;
1255
1256 Bits[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 1] = 32;
1257
1258 for ( LevelType = CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT
1259 ; LevelType <= CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 1
1260 ; LevelType ++
1261 ) {
1262 if (Location[LevelType] != NULL) {
1263 //
1264 // Bits[i] holds the number of bits to shift right on x2APIC ID to get a unique
1265 // topology ID of the next level type.
1266 //
1267 *Location[LevelType] = InitialApicId >> Bits[LevelType - 1];
1268
1269 //
1270 // Bits[i] - Bits[i-1] holds the number of bits for the next ONE level type.
1271 //
1272 *Location[LevelType] &= (1 << (Bits[LevelType] - Bits[LevelType - 1])) - 1;
1273 }
1274 }
1275 }
1276