1 /** @file
2   MTRR setting library
3 
4   @par Note:
5     Most of services in this library instance are suggested to be invoked by BSP only,
6     except for MtrrSetAllMtrrs() which is used to sync BSP's MTRR setting to APs.
7 
8   Copyright (c) 2008 - 2020, Intel Corporation. All rights reserved.<BR>
9   SPDX-License-Identifier: BSD-2-Clause-Patent
10 
11 **/
12 
13 #include <Uefi.h>
14 #include <Register/Intel/Cpuid.h>
15 #include <Register/Intel/Msr.h>
16 
17 #include <Library/MtrrLib.h>
18 #include <Library/BaseLib.h>
19 #include <Library/CpuLib.h>
20 #include <Library/BaseMemoryLib.h>
21 #include <Library/DebugLib.h>
22 
23 #define OR_SEED      0x0101010101010101ull
24 #define CLEAR_SEED   0xFFFFFFFFFFFFFFFFull
25 #define MAX_WEIGHT   MAX_UINT8
26 #define SCRATCH_BUFFER_SIZE           (4 * SIZE_4KB)
27 #define MTRR_LIB_ASSERT_ALIGNED(B, L) ASSERT ((B & ~(L - 1)) == B);
28 
29 #define M(x,y) ((x) * VertexCount + (y))
30 #define O(x,y) ((y) * VertexCount + (x))
31 
32 //
33 // Context to save and restore when MTRRs are programmed
34 //
35 typedef struct {
36   UINTN    Cr4;
37   BOOLEAN  InterruptState;
38 } MTRR_CONTEXT;
39 
40 typedef struct {
41   UINT64                 Address;
42   UINT64                 Alignment;
43   UINT64                 Length;
44   MTRR_MEMORY_CACHE_TYPE Type : 7;
45 
46   //
47   // Temprary use for calculating the best MTRR settings.
48   //
49   BOOLEAN                Visited : 1;
50   UINT8                  Weight;
51   UINT16                 Previous;
52 } MTRR_LIB_ADDRESS;
53 
54 //
55 // This table defines the offset, base and length of the fixed MTRRs
56 //
57 CONST FIXED_MTRR  mMtrrLibFixedMtrrTable[] = {
58   {
59     MSR_IA32_MTRR_FIX64K_00000,
60     0,
61     SIZE_64KB
62   },
63   {
64     MSR_IA32_MTRR_FIX16K_80000,
65     0x80000,
66     SIZE_16KB
67   },
68   {
69     MSR_IA32_MTRR_FIX16K_A0000,
70     0xA0000,
71     SIZE_16KB
72   },
73   {
74     MSR_IA32_MTRR_FIX4K_C0000,
75     0xC0000,
76     SIZE_4KB
77   },
78   {
79     MSR_IA32_MTRR_FIX4K_C8000,
80     0xC8000,
81     SIZE_4KB
82   },
83   {
84     MSR_IA32_MTRR_FIX4K_D0000,
85     0xD0000,
86     SIZE_4KB
87   },
88   {
89     MSR_IA32_MTRR_FIX4K_D8000,
90     0xD8000,
91     SIZE_4KB
92   },
93   {
94     MSR_IA32_MTRR_FIX4K_E0000,
95     0xE0000,
96     SIZE_4KB
97   },
98   {
99     MSR_IA32_MTRR_FIX4K_E8000,
100     0xE8000,
101     SIZE_4KB
102   },
103   {
104     MSR_IA32_MTRR_FIX4K_F0000,
105     0xF0000,
106     SIZE_4KB
107   },
108   {
109     MSR_IA32_MTRR_FIX4K_F8000,
110     0xF8000,
111     SIZE_4KB
112   }
113 };
114 
115 //
116 // Lookup table used to print MTRRs
117 //
118 GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mMtrrMemoryCacheTypeShortName[] = {
119   "UC",  // CacheUncacheable
120   "WC",  // CacheWriteCombining
121   "R*",  // Invalid
122   "R*",  // Invalid
123   "WT",  // CacheWriteThrough
124   "WP",  // CacheWriteProtected
125   "WB",  // CacheWriteBack
126   "R*"   // Invalid
127 };
128 
129 
130 /**
131   Worker function prints all MTRRs for debugging.
132 
133   If MtrrSetting is not NULL, print MTRR settings from input MTRR
134   settings buffer.
135   If MtrrSetting is NULL, print MTRR settings from MTRRs.
136 
137   @param  MtrrSetting    A buffer holding all MTRRs content.
138 **/
139 VOID
140 MtrrDebugPrintAllMtrrsWorker (
141   IN MTRR_SETTINGS    *MtrrSetting
142   );
143 
144 /**
145   Worker function returns the variable MTRR count for the CPU.
146 
147   @return Variable MTRR count
148 
149 **/
150 UINT32
GetVariableMtrrCountWorker(VOID)151 GetVariableMtrrCountWorker (
152   VOID
153   )
154 {
155   MSR_IA32_MTRRCAP_REGISTER MtrrCap;
156 
157   MtrrCap.Uint64 = AsmReadMsr64 (MSR_IA32_MTRRCAP);
158   ASSERT (MtrrCap.Bits.VCNT <= ARRAY_SIZE (((MTRR_VARIABLE_SETTINGS *) 0)->Mtrr));
159   return MtrrCap.Bits.VCNT;
160 }
161 
162 /**
163   Returns the variable MTRR count for the CPU.
164 
165   @return Variable MTRR count
166 
167 **/
168 UINT32
169 EFIAPI
GetVariableMtrrCount(VOID)170 GetVariableMtrrCount (
171   VOID
172   )
173 {
174   if (!IsMtrrSupported ()) {
175     return 0;
176   }
177   return GetVariableMtrrCountWorker ();
178 }
179 
180 /**
181   Worker function returns the firmware usable variable MTRR count for the CPU.
182 
183   @return Firmware usable variable MTRR count
184 
185 **/
186 UINT32
GetFirmwareVariableMtrrCountWorker(VOID)187 GetFirmwareVariableMtrrCountWorker (
188   VOID
189   )
190 {
191   UINT32  VariableMtrrCount;
192   UINT32  ReservedMtrrNumber;
193 
194   VariableMtrrCount = GetVariableMtrrCountWorker ();
195   ReservedMtrrNumber = PcdGet32 (PcdCpuNumberOfReservedVariableMtrrs);
196   if (VariableMtrrCount < ReservedMtrrNumber) {
197     return 0;
198   }
199 
200   return VariableMtrrCount - ReservedMtrrNumber;
201 }
202 
203 /**
204   Returns the firmware usable variable MTRR count for the CPU.
205 
206   @return Firmware usable variable MTRR count
207 
208 **/
209 UINT32
210 EFIAPI
GetFirmwareVariableMtrrCount(VOID)211 GetFirmwareVariableMtrrCount (
212   VOID
213   )
214 {
215   if (!IsMtrrSupported ()) {
216     return 0;
217   }
218   return GetFirmwareVariableMtrrCountWorker ();
219 }
220 
221 /**
222   Worker function returns the default MTRR cache type for the system.
223 
224   If MtrrSetting is not NULL, returns the default MTRR cache type from input
225   MTRR settings buffer.
226   If MtrrSetting is NULL, returns the default MTRR cache type from MSR.
227 
228   @param[in]  MtrrSetting    A buffer holding all MTRRs content.
229 
230   @return  The default MTRR cache type.
231 
232 **/
233 MTRR_MEMORY_CACHE_TYPE
MtrrGetDefaultMemoryTypeWorker(IN MTRR_SETTINGS * MtrrSetting)234 MtrrGetDefaultMemoryTypeWorker (
235   IN MTRR_SETTINGS      *MtrrSetting
236   )
237 {
238   MSR_IA32_MTRR_DEF_TYPE_REGISTER DefType;
239 
240   if (MtrrSetting == NULL) {
241     DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
242   } else {
243     DefType.Uint64 = MtrrSetting->MtrrDefType;
244   }
245 
246   return (MTRR_MEMORY_CACHE_TYPE) DefType.Bits.Type;
247 }
248 
249 
250 /**
251   Returns the default MTRR cache type for the system.
252 
253   @return  The default MTRR cache type.
254 
255 **/
256 MTRR_MEMORY_CACHE_TYPE
257 EFIAPI
MtrrGetDefaultMemoryType(VOID)258 MtrrGetDefaultMemoryType (
259   VOID
260   )
261 {
262   if (!IsMtrrSupported ()) {
263     return CacheUncacheable;
264   }
265   return MtrrGetDefaultMemoryTypeWorker (NULL);
266 }
267 
268 /**
269   Preparation before programming MTRR.
270 
271   This function will do some preparation for programming MTRRs:
272   disable cache, invalid cache and disable MTRR caching functionality
273 
274   @param[out] MtrrContext  Pointer to context to save
275 
276 **/
277 VOID
MtrrLibPreMtrrChange(OUT MTRR_CONTEXT * MtrrContext)278 MtrrLibPreMtrrChange (
279   OUT MTRR_CONTEXT  *MtrrContext
280   )
281 {
282   MSR_IA32_MTRR_DEF_TYPE_REGISTER  DefType;
283   //
284   // Disable interrupts and save current interrupt state
285   //
286   MtrrContext->InterruptState = SaveAndDisableInterrupts();
287 
288   //
289   // Enter no fill cache mode, CD=1(Bit30), NW=0 (Bit29)
290   //
291   AsmDisableCache ();
292 
293   //
294   // Save original CR4 value and clear PGE flag (Bit 7)
295   //
296   MtrrContext->Cr4 = AsmReadCr4 ();
297   AsmWriteCr4 (MtrrContext->Cr4 & (~BIT7));
298 
299   //
300   // Flush all TLBs
301   //
302   CpuFlushTlb ();
303 
304   //
305   // Disable MTRRs
306   //
307   DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
308   DefType.Bits.E = 0;
309   AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, DefType.Uint64);
310 }
311 
312 /**
313   Cleaning up after programming MTRRs.
314 
315   This function will do some clean up after programming MTRRs:
316   Flush all TLBs,  re-enable caching, restore CR4.
317 
318   @param[in] MtrrContext  Pointer to context to restore
319 
320 **/
321 VOID
MtrrLibPostMtrrChangeEnableCache(IN MTRR_CONTEXT * MtrrContext)322 MtrrLibPostMtrrChangeEnableCache (
323   IN MTRR_CONTEXT  *MtrrContext
324   )
325 {
326   //
327   // Flush all TLBs
328   //
329   CpuFlushTlb ();
330 
331   //
332   // Enable Normal Mode caching CD=NW=0, CD(Bit30), NW(Bit29)
333   //
334   AsmEnableCache ();
335 
336   //
337   // Restore original CR4 value
338   //
339   AsmWriteCr4 (MtrrContext->Cr4);
340 
341   //
342   // Restore original interrupt state
343   //
344   SetInterruptState (MtrrContext->InterruptState);
345 }
346 
347 /**
348   Cleaning up after programming MTRRs.
349 
350   This function will do some clean up after programming MTRRs:
351   enable MTRR caching functionality, and enable cache
352 
353   @param[in] MtrrContext  Pointer to context to restore
354 
355 **/
356 VOID
MtrrLibPostMtrrChange(IN MTRR_CONTEXT * MtrrContext)357 MtrrLibPostMtrrChange (
358   IN MTRR_CONTEXT  *MtrrContext
359   )
360 {
361   MSR_IA32_MTRR_DEF_TYPE_REGISTER  DefType;
362   //
363   // Enable Cache MTRR
364   //
365   DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
366   DefType.Bits.E = 1;
367   DefType.Bits.FE = 1;
368   AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, DefType.Uint64);
369 
370   MtrrLibPostMtrrChangeEnableCache (MtrrContext);
371 }
372 
373 /**
374   Worker function gets the content in fixed MTRRs
375 
376   @param[out]  FixedSettings  A buffer to hold fixed MTRRs content.
377 
378   @retval The pointer of FixedSettings
379 
380 **/
381 MTRR_FIXED_SETTINGS*
MtrrGetFixedMtrrWorker(OUT MTRR_FIXED_SETTINGS * FixedSettings)382 MtrrGetFixedMtrrWorker (
383   OUT MTRR_FIXED_SETTINGS         *FixedSettings
384   )
385 {
386   UINT32  Index;
387 
388   for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
389       FixedSettings->Mtrr[Index] =
390         AsmReadMsr64 (mMtrrLibFixedMtrrTable[Index].Msr);
391   }
392 
393   return FixedSettings;
394 }
395 
396 
397 /**
398   This function gets the content in fixed MTRRs
399 
400   @param[out]  FixedSettings  A buffer to hold fixed MTRRs content.
401 
402   @retval The pointer of FixedSettings
403 
404 **/
405 MTRR_FIXED_SETTINGS*
406 EFIAPI
MtrrGetFixedMtrr(OUT MTRR_FIXED_SETTINGS * FixedSettings)407 MtrrGetFixedMtrr (
408   OUT MTRR_FIXED_SETTINGS         *FixedSettings
409   )
410 {
411   if (!IsMtrrSupported ()) {
412     return FixedSettings;
413   }
414 
415   return MtrrGetFixedMtrrWorker (FixedSettings);
416 }
417 
418 
419 /**
420   Worker function will get the raw value in variable MTRRs
421 
422   If MtrrSetting is not NULL, gets the variable MTRRs raw value from input
423   MTRR settings buffer.
424   If MtrrSetting is NULL, gets the variable MTRRs raw value from MTRRs.
425 
426   @param[in]  MtrrSetting        A buffer holding all MTRRs content.
427   @param[in]  VariableMtrrCount  Number of variable MTRRs.
428   @param[out] VariableSettings   A buffer to hold variable MTRRs content.
429 
430   @return The VariableSettings input pointer
431 
432 **/
433 MTRR_VARIABLE_SETTINGS*
MtrrGetVariableMtrrWorker(IN MTRR_SETTINGS * MtrrSetting,IN UINT32 VariableMtrrCount,OUT MTRR_VARIABLE_SETTINGS * VariableSettings)434 MtrrGetVariableMtrrWorker (
435   IN  MTRR_SETTINGS           *MtrrSetting,
436   IN  UINT32                  VariableMtrrCount,
437   OUT MTRR_VARIABLE_SETTINGS  *VariableSettings
438   )
439 {
440   UINT32  Index;
441 
442   ASSERT (VariableMtrrCount <= ARRAY_SIZE (VariableSettings->Mtrr));
443 
444   for (Index = 0; Index < VariableMtrrCount; Index++) {
445     if (MtrrSetting == NULL) {
446       VariableSettings->Mtrr[Index].Base =
447         AsmReadMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (Index << 1));
448       VariableSettings->Mtrr[Index].Mask =
449         AsmReadMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1));
450     } else {
451       VariableSettings->Mtrr[Index].Base = MtrrSetting->Variables.Mtrr[Index].Base;
452       VariableSettings->Mtrr[Index].Mask = MtrrSetting->Variables.Mtrr[Index].Mask;
453     }
454   }
455 
456   return  VariableSettings;
457 }
458 
459 /**
460   Programs fixed MTRRs registers.
461 
462   @param[in]      Type             The memory type to set.
463   @param[in, out] Base             The base address of memory range.
464   @param[in, out] Length           The length of memory range.
465   @param[in, out] LastMsrIndex     On input, the last index of the fixed MTRR MSR to program.
466                                    On return, the current index of the fixed MTRR MSR to program.
467   @param[out]     ClearMask        The bits to clear in the fixed MTRR MSR.
468   @param[out]     OrMask           The bits to set in the fixed MTRR MSR.
469 
470   @retval RETURN_SUCCESS      The cache type was updated successfully
471   @retval RETURN_UNSUPPORTED  The requested range or cache type was invalid
472                               for the fixed MTRRs.
473 
474 **/
475 RETURN_STATUS
MtrrLibProgramFixedMtrr(IN MTRR_MEMORY_CACHE_TYPE Type,IN OUT UINT64 * Base,IN OUT UINT64 * Length,IN OUT UINT32 * LastMsrIndex,OUT UINT64 * ClearMask,OUT UINT64 * OrMask)476 MtrrLibProgramFixedMtrr (
477   IN     MTRR_MEMORY_CACHE_TYPE  Type,
478   IN OUT UINT64                  *Base,
479   IN OUT UINT64                  *Length,
480   IN OUT UINT32                  *LastMsrIndex,
481   OUT    UINT64                  *ClearMask,
482   OUT    UINT64                  *OrMask
483   )
484 {
485   UINT32  MsrIndex;
486   UINT32  LeftByteShift;
487   UINT32  RightByteShift;
488   UINT64  SubLength;
489 
490   //
491   // Find the fixed MTRR index to be programmed
492   //
493   for (MsrIndex = *LastMsrIndex + 1; MsrIndex < ARRAY_SIZE (mMtrrLibFixedMtrrTable); MsrIndex++) {
494     if ((*Base >= mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress) &&
495         (*Base <
496             (
497               mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress +
498               (8 * mMtrrLibFixedMtrrTable[MsrIndex].Length)
499             )
500           )
501         ) {
502       break;
503     }
504   }
505 
506   ASSERT (MsrIndex != ARRAY_SIZE (mMtrrLibFixedMtrrTable));
507 
508   //
509   // Find the begin offset in fixed MTRR and calculate byte offset of left shift
510   //
511   if ((((UINT32)*Base - mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress) % mMtrrLibFixedMtrrTable[MsrIndex].Length) != 0) {
512     //
513     // Base address should be aligned to the begin of a certain Fixed MTRR range.
514     //
515     return RETURN_UNSUPPORTED;
516   }
517   LeftByteShift = ((UINT32)*Base - mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress) / mMtrrLibFixedMtrrTable[MsrIndex].Length;
518   ASSERT (LeftByteShift < 8);
519 
520   //
521   // Find the end offset in fixed MTRR and calculate byte offset of right shift
522   //
523   SubLength = mMtrrLibFixedMtrrTable[MsrIndex].Length * (8 - LeftByteShift);
524   if (*Length >= SubLength) {
525     RightByteShift = 0;
526   } else {
527     if (((UINT32)(*Length) % mMtrrLibFixedMtrrTable[MsrIndex].Length) != 0) {
528       //
529       // Length should be aligned to the end of a certain Fixed MTRR range.
530       //
531       return RETURN_UNSUPPORTED;
532     }
533     RightByteShift = 8 - LeftByteShift - (UINT32)(*Length) / mMtrrLibFixedMtrrTable[MsrIndex].Length;
534     //
535     // Update SubLength by actual length
536     //
537     SubLength = *Length;
538   }
539 
540   *ClearMask = CLEAR_SEED;
541   *OrMask    = MultU64x32 (OR_SEED, (UINT32) Type);
542 
543   if (LeftByteShift != 0) {
544     //
545     // Clear the low bits by LeftByteShift
546     //
547     *ClearMask &= LShiftU64 (*ClearMask, LeftByteShift * 8);
548     *OrMask    &= LShiftU64 (*OrMask,    LeftByteShift * 8);
549   }
550 
551   if (RightByteShift != 0) {
552     //
553     // Clear the high bits by RightByteShift
554     //
555     *ClearMask &= RShiftU64 (*ClearMask, RightByteShift * 8);
556     *OrMask    &= RShiftU64 (*OrMask,    RightByteShift * 8);
557   }
558 
559   *Length -= SubLength;
560   *Base   += SubLength;
561 
562   *LastMsrIndex    = MsrIndex;
563 
564   return RETURN_SUCCESS;
565 }
566 
567 
568 /**
569   Worker function gets the attribute of variable MTRRs.
570 
571   This function shadows the content of variable MTRRs into an
572   internal array: VariableMtrr.
573 
574   @param[in]   VariableSettings      The variable MTRR values to shadow
575   @param[in]   VariableMtrrCount     The number of variable MTRRs
576   @param[in]   MtrrValidBitsMask     The mask for the valid bit of the MTRR
577   @param[in]   MtrrValidAddressMask  The valid address mask for MTRR
578   @param[out]  VariableMtrr          The array to shadow variable MTRRs content
579 
580   @return      Number of MTRRs which has been used.
581 
582 **/
583 UINT32
MtrrGetMemoryAttributeInVariableMtrrWorker(IN MTRR_VARIABLE_SETTINGS * VariableSettings,IN UINTN VariableMtrrCount,IN UINT64 MtrrValidBitsMask,IN UINT64 MtrrValidAddressMask,OUT VARIABLE_MTRR * VariableMtrr)584 MtrrGetMemoryAttributeInVariableMtrrWorker (
585   IN  MTRR_VARIABLE_SETTINGS  *VariableSettings,
586   IN  UINTN                   VariableMtrrCount,
587   IN  UINT64                  MtrrValidBitsMask,
588   IN  UINT64                  MtrrValidAddressMask,
589   OUT VARIABLE_MTRR           *VariableMtrr
590   )
591 {
592   UINTN   Index;
593   UINT32  UsedMtrr;
594 
595   ZeroMem (VariableMtrr, sizeof (VARIABLE_MTRR) * ARRAY_SIZE (VariableSettings->Mtrr));
596   for (Index = 0, UsedMtrr = 0; Index < VariableMtrrCount; Index++) {
597     if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *) &VariableSettings->Mtrr[Index].Mask)->Bits.V != 0) {
598       VariableMtrr[Index].Msr         = (UINT32)Index;
599       VariableMtrr[Index].BaseAddress = (VariableSettings->Mtrr[Index].Base & MtrrValidAddressMask);
600       VariableMtrr[Index].Length      =
601         ((~(VariableSettings->Mtrr[Index].Mask & MtrrValidAddressMask)) & MtrrValidBitsMask) + 1;
602       VariableMtrr[Index].Type        = (VariableSettings->Mtrr[Index].Base & 0x0ff);
603       VariableMtrr[Index].Valid       = TRUE;
604       VariableMtrr[Index].Used        = TRUE;
605       UsedMtrr++;
606     }
607   }
608   return UsedMtrr;
609 }
610 
611 /**
612   Convert variable MTRRs to a RAW MTRR_MEMORY_RANGE array.
613   One MTRR_MEMORY_RANGE element is created for each MTRR setting.
614   The routine doesn't remove the overlap or combine the near-by region.
615 
616   @param[in]   VariableSettings      The variable MTRR values to shadow
617   @param[in]   VariableMtrrCount     The number of variable MTRRs
618   @param[in]   MtrrValidBitsMask     The mask for the valid bit of the MTRR
619   @param[in]   MtrrValidAddressMask  The valid address mask for MTRR
620   @param[out]  VariableMtrr          The array to shadow variable MTRRs content
621 
622   @return      Number of MTRRs which has been used.
623 
624 **/
625 UINT32
MtrrLibGetRawVariableRanges(IN MTRR_VARIABLE_SETTINGS * VariableSettings,IN UINTN VariableMtrrCount,IN UINT64 MtrrValidBitsMask,IN UINT64 MtrrValidAddressMask,OUT MTRR_MEMORY_RANGE * VariableMtrr)626 MtrrLibGetRawVariableRanges (
627   IN  MTRR_VARIABLE_SETTINGS  *VariableSettings,
628   IN  UINTN                   VariableMtrrCount,
629   IN  UINT64                  MtrrValidBitsMask,
630   IN  UINT64                  MtrrValidAddressMask,
631   OUT MTRR_MEMORY_RANGE       *VariableMtrr
632   )
633 {
634   UINTN   Index;
635   UINT32  UsedMtrr;
636 
637   ZeroMem (VariableMtrr, sizeof (MTRR_MEMORY_RANGE) * ARRAY_SIZE (VariableSettings->Mtrr));
638   for (Index = 0, UsedMtrr = 0; Index < VariableMtrrCount; Index++) {
639     if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *) &VariableSettings->Mtrr[Index].Mask)->Bits.V != 0) {
640       VariableMtrr[Index].BaseAddress = (VariableSettings->Mtrr[Index].Base & MtrrValidAddressMask);
641       VariableMtrr[Index].Length      =
642         ((~(VariableSettings->Mtrr[Index].Mask & MtrrValidAddressMask)) & MtrrValidBitsMask) + 1;
643       VariableMtrr[Index].Type        = (MTRR_MEMORY_CACHE_TYPE)(VariableSettings->Mtrr[Index].Base & 0x0ff);
644       UsedMtrr++;
645     }
646   }
647   return UsedMtrr;
648 }
649 
650 /**
651   Gets the attribute of variable MTRRs.
652 
653   This function shadows the content of variable MTRRs into an
654   internal array: VariableMtrr.
655 
656   @param[in]   MtrrValidBitsMask     The mask for the valid bit of the MTRR
657   @param[in]   MtrrValidAddressMask  The valid address mask for MTRR
658   @param[out]  VariableMtrr          The array to shadow variable MTRRs content
659 
660   @return                       The return value of this parameter indicates the
661                                 number of MTRRs which has been used.
662 
663 **/
664 UINT32
665 EFIAPI
MtrrGetMemoryAttributeInVariableMtrr(IN UINT64 MtrrValidBitsMask,IN UINT64 MtrrValidAddressMask,OUT VARIABLE_MTRR * VariableMtrr)666 MtrrGetMemoryAttributeInVariableMtrr (
667   IN  UINT64                    MtrrValidBitsMask,
668   IN  UINT64                    MtrrValidAddressMask,
669   OUT VARIABLE_MTRR             *VariableMtrr
670   )
671 {
672   MTRR_VARIABLE_SETTINGS  VariableSettings;
673 
674   if (!IsMtrrSupported ()) {
675     return 0;
676   }
677 
678   MtrrGetVariableMtrrWorker (
679     NULL,
680     GetVariableMtrrCountWorker (),
681     &VariableSettings
682     );
683 
684   return MtrrGetMemoryAttributeInVariableMtrrWorker (
685            &VariableSettings,
686            GetFirmwareVariableMtrrCountWorker (),
687            MtrrValidBitsMask,
688            MtrrValidAddressMask,
689            VariableMtrr
690            );
691 }
692 
693 /**
694   Return the biggest alignment (lowest set bit) of address.
695   The function is equivalent to: 1 << LowBitSet64 (Address).
696 
697   @param Address    The address to return the alignment.
698   @param Alignment0 The alignment to return when Address is 0.
699 
700   @return The least alignment of the Address.
701 **/
702 UINT64
MtrrLibBiggestAlignment(UINT64 Address,UINT64 Alignment0)703 MtrrLibBiggestAlignment (
704   UINT64    Address,
705   UINT64    Alignment0
706 )
707 {
708   if (Address == 0) {
709     return Alignment0;
710   }
711 
712   return Address & ((~Address) + 1);
713 }
714 
715 /**
716   Return whether the left MTRR type precedes the right MTRR type.
717 
718   The MTRR type precedence rules are:
719     1. UC precedes any other type
720     2. WT precedes WB
721   For further details, please refer the IA32 Software Developer's Manual,
722   Volume 3, Section "MTRR Precedences".
723 
724   @param Left  The left MTRR type.
725   @param Right The right MTRR type.
726 
727   @retval TRUE  Left precedes Right.
728   @retval FALSE Left doesn't precede Right.
729 **/
730 BOOLEAN
MtrrLibTypeLeftPrecedeRight(IN MTRR_MEMORY_CACHE_TYPE Left,IN MTRR_MEMORY_CACHE_TYPE Right)731 MtrrLibTypeLeftPrecedeRight (
732   IN MTRR_MEMORY_CACHE_TYPE  Left,
733   IN MTRR_MEMORY_CACHE_TYPE  Right
734 )
735 {
736   return (BOOLEAN) (Left == CacheUncacheable || (Left == CacheWriteThrough && Right == CacheWriteBack));
737 }
738 
739 /**
740   Initializes the valid bits mask and valid address mask for MTRRs.
741 
742   This function initializes the valid bits mask and valid address mask for MTRRs.
743 
744   @param[out]  MtrrValidBitsMask     The mask for the valid bit of the MTRR
745   @param[out]  MtrrValidAddressMask  The valid address mask for the MTRR
746 
747 **/
748 VOID
MtrrLibInitializeMtrrMask(OUT UINT64 * MtrrValidBitsMask,OUT UINT64 * MtrrValidAddressMask)749 MtrrLibInitializeMtrrMask (
750   OUT UINT64 *MtrrValidBitsMask,
751   OUT UINT64 *MtrrValidAddressMask
752   )
753 {
754   UINT32                          MaxExtendedFunction;
755   CPUID_VIR_PHY_ADDRESS_SIZE_EAX  VirPhyAddressSize;
756 
757 
758   AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedFunction, NULL, NULL, NULL);
759 
760   if (MaxExtendedFunction >= CPUID_VIR_PHY_ADDRESS_SIZE) {
761     AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &VirPhyAddressSize.Uint32, NULL, NULL, NULL);
762   } else {
763     VirPhyAddressSize.Bits.PhysicalAddressBits = 36;
764   }
765 
766   *MtrrValidBitsMask = LShiftU64 (1, VirPhyAddressSize.Bits.PhysicalAddressBits) - 1;
767   *MtrrValidAddressMask = *MtrrValidBitsMask & 0xfffffffffffff000ULL;
768 }
769 
770 
771 /**
772   Determines the real attribute of a memory range.
773 
774   This function is to arbitrate the real attribute of the memory when
775   there are 2 MTRRs covers the same memory range. For further details,
776   please refer the IA32 Software Developer's Manual, Volume 3,
777   Section "MTRR Precedences".
778 
779   @param[in]  MtrrType1    The first kind of Memory type
780   @param[in]  MtrrType2    The second kind of memory type
781 
782 **/
783 MTRR_MEMORY_CACHE_TYPE
MtrrLibPrecedence(IN MTRR_MEMORY_CACHE_TYPE MtrrType1,IN MTRR_MEMORY_CACHE_TYPE MtrrType2)784 MtrrLibPrecedence (
785   IN MTRR_MEMORY_CACHE_TYPE    MtrrType1,
786   IN MTRR_MEMORY_CACHE_TYPE    MtrrType2
787   )
788 {
789   if (MtrrType1 == MtrrType2) {
790     return MtrrType1;
791   }
792 
793   ASSERT (
794     MtrrLibTypeLeftPrecedeRight (MtrrType1, MtrrType2) ||
795     MtrrLibTypeLeftPrecedeRight (MtrrType2, MtrrType1)
796   );
797 
798   if (MtrrLibTypeLeftPrecedeRight (MtrrType1, MtrrType2)) {
799     return MtrrType1;
800   } else {
801     return MtrrType2;
802   }
803 }
804 
805 /**
806   Worker function will get the memory cache type of the specific address.
807 
808   If MtrrSetting is not NULL, gets the memory cache type from input
809   MTRR settings buffer.
810   If MtrrSetting is NULL, gets the memory cache type from MTRRs.
811 
812   @param[in]  MtrrSetting        A buffer holding all MTRRs content.
813   @param[in]  Address            The specific address
814 
815   @return Memory cache type of the specific address
816 
817 **/
818 MTRR_MEMORY_CACHE_TYPE
MtrrGetMemoryAttributeByAddressWorker(IN MTRR_SETTINGS * MtrrSetting,IN PHYSICAL_ADDRESS Address)819 MtrrGetMemoryAttributeByAddressWorker (
820   IN MTRR_SETTINGS      *MtrrSetting,
821   IN PHYSICAL_ADDRESS   Address
822   )
823 {
824   MSR_IA32_MTRR_DEF_TYPE_REGISTER DefType;
825   UINT64                          FixedMtrr;
826   UINTN                           Index;
827   UINTN                           SubIndex;
828   MTRR_MEMORY_CACHE_TYPE          MtrrType;
829   MTRR_MEMORY_RANGE               VariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];
830   UINT64                          MtrrValidBitsMask;
831   UINT64                          MtrrValidAddressMask;
832   UINT32                          VariableMtrrCount;
833   MTRR_VARIABLE_SETTINGS          VariableSettings;
834 
835   //
836   // Check if MTRR is enabled, if not, return UC as attribute
837   //
838   if (MtrrSetting == NULL) {
839     DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
840   } else {
841     DefType.Uint64 = MtrrSetting->MtrrDefType;
842   }
843 
844   if (DefType.Bits.E == 0) {
845     return CacheUncacheable;
846   }
847 
848   //
849   // If address is less than 1M, then try to go through the fixed MTRR
850   //
851   if (Address < BASE_1MB) {
852     if (DefType.Bits.FE != 0) {
853       //
854       // Go through the fixed MTRR
855       //
856       for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
857         if (Address >= mMtrrLibFixedMtrrTable[Index].BaseAddress &&
858             Address < mMtrrLibFixedMtrrTable[Index].BaseAddress +
859             (mMtrrLibFixedMtrrTable[Index].Length * 8)) {
860           SubIndex =
861             ((UINTN) Address - mMtrrLibFixedMtrrTable[Index].BaseAddress) /
862             mMtrrLibFixedMtrrTable[Index].Length;
863           if (MtrrSetting == NULL) {
864             FixedMtrr = AsmReadMsr64 (mMtrrLibFixedMtrrTable[Index].Msr);
865           } else {
866             FixedMtrr = MtrrSetting->Fixed.Mtrr[Index];
867           }
868           return (MTRR_MEMORY_CACHE_TYPE) (RShiftU64 (FixedMtrr, SubIndex * 8) & 0xFF);
869         }
870       }
871     }
872   }
873 
874   VariableMtrrCount = GetVariableMtrrCountWorker ();
875   ASSERT (VariableMtrrCount <= ARRAY_SIZE (MtrrSetting->Variables.Mtrr));
876   MtrrGetVariableMtrrWorker (MtrrSetting, VariableMtrrCount, &VariableSettings);
877 
878   MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask);
879   MtrrLibGetRawVariableRanges (
880     &VariableSettings,
881     VariableMtrrCount,
882     MtrrValidBitsMask,
883     MtrrValidAddressMask,
884     VariableMtrr
885   );
886 
887   //
888   // Go through the variable MTRR
889   //
890   MtrrType = CacheInvalid;
891   for (Index = 0; Index < VariableMtrrCount; Index++) {
892     if (VariableMtrr[Index].Length != 0) {
893       if (Address >= VariableMtrr[Index].BaseAddress &&
894           Address < VariableMtrr[Index].BaseAddress + VariableMtrr[Index].Length) {
895         if (MtrrType == CacheInvalid) {
896           MtrrType = (MTRR_MEMORY_CACHE_TYPE) VariableMtrr[Index].Type;
897         } else {
898           MtrrType = MtrrLibPrecedence (MtrrType, (MTRR_MEMORY_CACHE_TYPE) VariableMtrr[Index].Type);
899         }
900       }
901     }
902   }
903 
904   //
905   // If there is no MTRR which covers the Address, use the default MTRR type.
906   //
907   if (MtrrType == CacheInvalid) {
908     MtrrType = (MTRR_MEMORY_CACHE_TYPE) DefType.Bits.Type;
909   }
910 
911   return MtrrType;
912 }
913 
914 
915 /**
916   This function will get the memory cache type of the specific address.
917 
918   This function is mainly for debug purpose.
919 
920   @param[in]  Address   The specific address
921 
922   @return Memory cache type of the specific address
923 
924 **/
925 MTRR_MEMORY_CACHE_TYPE
926 EFIAPI
MtrrGetMemoryAttribute(IN PHYSICAL_ADDRESS Address)927 MtrrGetMemoryAttribute (
928   IN PHYSICAL_ADDRESS   Address
929   )
930 {
931   if (!IsMtrrSupported ()) {
932     return CacheUncacheable;
933   }
934 
935   return MtrrGetMemoryAttributeByAddressWorker (NULL, Address);
936 }
937 
938 /**
939   Update the Ranges array to change the specified range identified by
940   BaseAddress and Length to Type.
941 
942   @param Ranges      Array holding memory type settings for all memory regions.
943   @param Capacity    The maximum count of memory ranges the array can hold.
944   @param Count       Return the new memory range count in the array.
945   @param BaseAddress The base address of the memory range to change type.
946   @param Length      The length of the memory range to change type.
947   @param Type        The new type of the specified memory range.
948 
949   @retval RETURN_SUCCESS          The type of the specified memory range is
950                                   changed successfully.
951   @retval RETURN_ALREADY_STARTED  The type of the specified memory range equals
952                                   to the desired type.
953   @retval RETURN_OUT_OF_RESOURCES The new type set causes the count of memory
954                                   range exceeds capacity.
955 **/
956 RETURN_STATUS
MtrrLibSetMemoryType(IN MTRR_MEMORY_RANGE * Ranges,IN UINTN Capacity,IN OUT UINTN * Count,IN UINT64 BaseAddress,IN UINT64 Length,IN MTRR_MEMORY_CACHE_TYPE Type)957 MtrrLibSetMemoryType (
958   IN MTRR_MEMORY_RANGE             *Ranges,
959   IN UINTN                         Capacity,
960   IN OUT UINTN                     *Count,
961   IN UINT64                        BaseAddress,
962   IN UINT64                        Length,
963   IN MTRR_MEMORY_CACHE_TYPE        Type
964   )
965 {
966   UINTN                            Index;
967   UINT64                           Limit;
968   UINT64                           LengthLeft;
969   UINT64                           LengthRight;
970   UINTN                            StartIndex;
971   UINTN                            EndIndex;
972   UINTN                            DeltaCount;
973 
974   LengthRight = 0;
975   LengthLeft  = 0;
976   Limit = BaseAddress + Length;
977   StartIndex = *Count;
978   EndIndex = *Count;
979   for (Index = 0; Index < *Count; Index++) {
980     if ((StartIndex == *Count) &&
981         (Ranges[Index].BaseAddress <= BaseAddress) &&
982         (BaseAddress < Ranges[Index].BaseAddress + Ranges[Index].Length)) {
983       StartIndex = Index;
984       LengthLeft = BaseAddress - Ranges[Index].BaseAddress;
985     }
986 
987     if ((EndIndex == *Count) &&
988         (Ranges[Index].BaseAddress < Limit) &&
989         (Limit <= Ranges[Index].BaseAddress + Ranges[Index].Length)) {
990       EndIndex = Index;
991       LengthRight = Ranges[Index].BaseAddress + Ranges[Index].Length - Limit;
992       break;
993     }
994   }
995 
996   ASSERT (StartIndex != *Count && EndIndex != *Count);
997   if (StartIndex == EndIndex && Ranges[StartIndex].Type == Type) {
998     return RETURN_ALREADY_STARTED;
999   }
1000 
1001   //
1002   // The type change may cause merging with previous range or next range.
1003   // Update the StartIndex, EndIndex, BaseAddress, Length so that following
1004   // logic doesn't need to consider merging.
1005   //
1006   if (StartIndex != 0) {
1007     if (LengthLeft == 0 && Ranges[StartIndex - 1].Type == Type) {
1008       StartIndex--;
1009       Length += Ranges[StartIndex].Length;
1010       BaseAddress -= Ranges[StartIndex].Length;
1011     }
1012   }
1013   if (EndIndex != (*Count) - 1) {
1014     if (LengthRight == 0 && Ranges[EndIndex + 1].Type == Type) {
1015       EndIndex++;
1016       Length += Ranges[EndIndex].Length;
1017     }
1018   }
1019 
1020   //
1021   // |- 0 -|- 1 -|- 2 -|- 3 -| StartIndex EndIndex DeltaCount  Count (Count = 4)
1022   //   |++++++++++++++++++|    0          3         1=3-0-2    3
1023   //   |+++++++|               0          1        -1=1-0-2    5
1024   //   |+|                     0          0        -2=0-0-2    6
1025   // |+++|                     0          0        -1=0-0-2+1  5
1026   //
1027   //
1028   DeltaCount = EndIndex - StartIndex - 2;
1029   if (LengthLeft == 0) {
1030     DeltaCount++;
1031   }
1032   if (LengthRight == 0) {
1033     DeltaCount++;
1034   }
1035   if (*Count - DeltaCount > Capacity) {
1036     return RETURN_OUT_OF_RESOURCES;
1037   }
1038 
1039   //
1040   // Reserve (-DeltaCount) space
1041   //
1042   CopyMem (&Ranges[EndIndex + 1 - DeltaCount], &Ranges[EndIndex + 1], (*Count - EndIndex - 1) * sizeof (Ranges[0]));
1043   *Count -= DeltaCount;
1044 
1045   if (LengthLeft != 0) {
1046     Ranges[StartIndex].Length = LengthLeft;
1047     StartIndex++;
1048   }
1049   if (LengthRight != 0) {
1050     Ranges[EndIndex - DeltaCount].BaseAddress = BaseAddress + Length;
1051     Ranges[EndIndex - DeltaCount].Length = LengthRight;
1052     Ranges[EndIndex - DeltaCount].Type = Ranges[EndIndex].Type;
1053   }
1054   Ranges[StartIndex].BaseAddress = BaseAddress;
1055   Ranges[StartIndex].Length = Length;
1056   Ranges[StartIndex].Type = Type;
1057   return RETURN_SUCCESS;
1058 }
1059 
1060 /**
1061   Return the number of memory types in range [BaseAddress, BaseAddress + Length).
1062 
1063   @param Ranges      Array holding memory type settings for all memory regions.
1064   @param RangeCount  The count of memory ranges the array holds.
1065   @param BaseAddress Base address.
1066   @param Length      Length.
1067   @param Types       Return bit mask to indicate all memory types in the specified range.
1068 
1069   @retval  Number of memory types.
1070 **/
1071 UINT8
MtrrLibGetNumberOfTypes(IN CONST MTRR_MEMORY_RANGE * Ranges,IN UINTN RangeCount,IN UINT64 BaseAddress,IN UINT64 Length,IN OUT UINT8 * Types OPTIONAL)1072 MtrrLibGetNumberOfTypes (
1073   IN CONST MTRR_MEMORY_RANGE     *Ranges,
1074   IN UINTN                       RangeCount,
1075   IN UINT64                      BaseAddress,
1076   IN UINT64                      Length,
1077   IN OUT UINT8                   *Types  OPTIONAL
1078   )
1079 {
1080   UINTN                          Index;
1081   UINT8                          TypeCount;
1082   UINT8                          LocalTypes;
1083 
1084   TypeCount = 0;
1085   LocalTypes = 0;
1086   for (Index = 0; Index < RangeCount; Index++) {
1087     if ((Ranges[Index].BaseAddress <= BaseAddress) &&
1088         (BaseAddress < Ranges[Index].BaseAddress + Ranges[Index].Length)
1089         ) {
1090       if ((LocalTypes & (1 << Ranges[Index].Type)) == 0) {
1091         LocalTypes |= (UINT8)(1 << Ranges[Index].Type);
1092         TypeCount++;
1093       }
1094 
1095       if (BaseAddress + Length > Ranges[Index].BaseAddress + Ranges[Index].Length) {
1096         Length -= Ranges[Index].BaseAddress + Ranges[Index].Length - BaseAddress;
1097         BaseAddress = Ranges[Index].BaseAddress + Ranges[Index].Length;
1098       } else {
1099         break;
1100       }
1101     }
1102   }
1103 
1104   if (Types != NULL) {
1105     *Types = LocalTypes;
1106   }
1107   return TypeCount;
1108 }
1109 
1110 /**
1111   Calculate the least MTRR number from vertex Start to Stop and update
1112   the Previous of all vertices from Start to Stop is updated to reflect
1113   how the memory range is covered by MTRR.
1114 
1115   @param VertexCount     The count of vertices in the graph.
1116   @param Vertices        Array holding all vertices.
1117   @param Weight          2-dimention array holding weights between vertices.
1118   @param Start           Start vertex.
1119   @param Stop            Stop vertex.
1120   @param IncludeOptional TRUE to count the optional weight.
1121 **/
1122 VOID
MtrrLibCalculateLeastMtrrs(IN UINT16 VertexCount,IN MTRR_LIB_ADDRESS * Vertices,IN OUT CONST UINT8 * Weight,IN UINT16 Start,IN UINT16 Stop,IN BOOLEAN IncludeOptional)1123 MtrrLibCalculateLeastMtrrs (
1124   IN UINT16                      VertexCount,
1125   IN MTRR_LIB_ADDRESS            *Vertices,
1126   IN OUT CONST UINT8             *Weight,
1127   IN UINT16                      Start,
1128   IN UINT16                      Stop,
1129   IN BOOLEAN                     IncludeOptional
1130   )
1131 {
1132   UINT16                         Index;
1133   UINT8                          MinWeight;
1134   UINT16                         MinI;
1135   UINT8                          Mandatory;
1136   UINT8                          Optional;
1137 
1138   for (Index = Start; Index <= Stop; Index++) {
1139     Vertices[Index].Visited = FALSE;
1140     Mandatory = Weight[M(Start,Index)];
1141     Vertices[Index].Weight = Mandatory;
1142     if (Mandatory != MAX_WEIGHT) {
1143       Optional = IncludeOptional ? Weight[O(Start, Index)] : 0;
1144       Vertices[Index].Weight += Optional;
1145       ASSERT (Vertices[Index].Weight >= Optional);
1146     }
1147   }
1148 
1149   MinI = Start;
1150   MinWeight = 0;
1151   while (!Vertices[Stop].Visited) {
1152     //
1153     // Update the weight from the shortest vertex to other unvisited vertices
1154     //
1155     for (Index = Start + 1; Index <= Stop; Index++) {
1156       if (!Vertices[Index].Visited) {
1157         Mandatory = Weight[M(MinI, Index)];
1158         if (Mandatory != MAX_WEIGHT) {
1159           Optional = IncludeOptional ? Weight[O(MinI, Index)] : 0;
1160           if (MinWeight + Mandatory + Optional <= Vertices[Index].Weight) {
1161             Vertices[Index].Weight   = MinWeight + Mandatory + Optional;
1162             Vertices[Index].Previous = MinI; // Previous is Start based.
1163           }
1164         }
1165       }
1166     }
1167 
1168     //
1169     // Find the shortest vertex from Start
1170     //
1171     MinI      = VertexCount;
1172     MinWeight = MAX_WEIGHT;
1173     for (Index = Start + 1; Index <= Stop; Index++) {
1174       if (!Vertices[Index].Visited && MinWeight > Vertices[Index].Weight) {
1175         MinI      = Index;
1176         MinWeight = Vertices[Index].Weight;
1177       }
1178     }
1179 
1180     //
1181     // Mark the shortest vertex from Start as visited
1182     //
1183     Vertices[MinI].Visited = TRUE;
1184   }
1185 }
1186 
1187 /**
1188   Append the MTRR setting to MTRR setting array.
1189 
1190   @param Mtrrs        Array holding all MTRR settings.
1191   @param MtrrCapacity Capacity of the MTRR array.
1192   @param MtrrCount    The count of MTRR settings in array.
1193   @param BaseAddress  Base address.
1194   @param Length       Length.
1195   @param Type         Memory type.
1196 
1197   @retval RETURN_SUCCESS          MTRR setting is appended to array.
1198   @retval RETURN_OUT_OF_RESOURCES Array is full.
1199 **/
1200 RETURN_STATUS
MtrrLibAppendVariableMtrr(IN OUT MTRR_MEMORY_RANGE * Mtrrs,IN UINT32 MtrrCapacity,IN OUT UINT32 * MtrrCount,IN UINT64 BaseAddress,IN UINT64 Length,IN MTRR_MEMORY_CACHE_TYPE Type)1201 MtrrLibAppendVariableMtrr (
1202   IN OUT MTRR_MEMORY_RANGE       *Mtrrs,
1203   IN     UINT32                  MtrrCapacity,
1204   IN OUT UINT32                  *MtrrCount,
1205   IN     UINT64                  BaseAddress,
1206   IN     UINT64                  Length,
1207   IN     MTRR_MEMORY_CACHE_TYPE  Type
1208   )
1209 {
1210   if (*MtrrCount == MtrrCapacity) {
1211     return RETURN_OUT_OF_RESOURCES;
1212   }
1213 
1214   Mtrrs[*MtrrCount].BaseAddress = BaseAddress;
1215   Mtrrs[*MtrrCount].Length      = Length;
1216   Mtrrs[*MtrrCount].Type        = Type;
1217   (*MtrrCount)++;
1218   return RETURN_SUCCESS;
1219 }
1220 
1221 /**
1222   Return the memory type that has the least precedence.
1223 
1224   @param TypeBits  Bit mask of memory type.
1225 
1226   @retval  Memory type that has the least precedence.
1227 **/
1228 MTRR_MEMORY_CACHE_TYPE
MtrrLibLowestType(IN UINT8 TypeBits)1229 MtrrLibLowestType (
1230   IN      UINT8                    TypeBits
1231 )
1232 {
1233   INT8                             Type;
1234 
1235   ASSERT (TypeBits != 0);
1236   for (Type = 7; (INT8)TypeBits > 0; Type--, TypeBits <<= 1);
1237   return (MTRR_MEMORY_CACHE_TYPE)Type;
1238 }
1239 
1240 /**
1241   Return TRUE when the Operand is exactly power of 2.
1242 
1243   @retval TRUE  Operand is exactly power of 2.
1244   @retval FALSE Operand is not power of 2.
1245 **/
1246 BOOLEAN
MtrrLibIsPowerOfTwo(IN UINT64 Operand)1247 MtrrLibIsPowerOfTwo (
1248   IN     UINT64                  Operand
1249 )
1250 {
1251   ASSERT (Operand != 0);
1252   return (BOOLEAN) ((Operand & (Operand - 1)) == 0);
1253 }
1254 
1255 /**
1256   Calculate the subtractive path from vertex Start to Stop.
1257 
1258   @param DefaultType  Default memory type.
1259   @param A0           Alignment to use when base address is 0.
1260   @param Ranges       Array holding memory type settings for all memory regions.
1261   @param RangeCount   The count of memory ranges the array holds.
1262   @param VertexCount  The count of vertices in the graph.
1263   @param Vertices     Array holding all vertices.
1264   @param Weight       2-dimention array holding weights between vertices.
1265   @param Start        Start vertex.
1266   @param Stop         Stop vertex.
1267   @param Types        Type bit mask of memory range from Start to Stop.
1268   @param TypeCount    Number of different memory types from Start to Stop.
1269   @param Mtrrs        Array holding all MTRR settings.
1270   @param MtrrCapacity Capacity of the MTRR array.
1271   @param MtrrCount    The count of MTRR settings in array.
1272 
1273   @retval RETURN_SUCCESS          The subtractive path is calculated successfully.
1274   @retval RETURN_OUT_OF_RESOURCES The MTRR setting array is full.
1275 
1276 **/
1277 RETURN_STATUS
MtrrLibCalculateSubtractivePath(IN MTRR_MEMORY_CACHE_TYPE DefaultType,IN UINT64 A0,IN CONST MTRR_MEMORY_RANGE * Ranges,IN UINTN RangeCount,IN UINT16 VertexCount,IN MTRR_LIB_ADDRESS * Vertices,IN OUT UINT8 * Weight,IN UINT16 Start,IN UINT16 Stop,IN UINT8 Types,IN UINT8 TypeCount,IN OUT MTRR_MEMORY_RANGE * Mtrrs,OPTIONAL IN UINT32 MtrrCapacity,OPTIONAL IN OUT UINT32 * MtrrCount OPTIONAL)1278 MtrrLibCalculateSubtractivePath (
1279   IN MTRR_MEMORY_CACHE_TYPE      DefaultType,
1280   IN UINT64                      A0,
1281   IN CONST MTRR_MEMORY_RANGE     *Ranges,
1282   IN UINTN                       RangeCount,
1283   IN UINT16                      VertexCount,
1284   IN MTRR_LIB_ADDRESS            *Vertices,
1285   IN OUT UINT8                   *Weight,
1286   IN UINT16                      Start,
1287   IN UINT16                      Stop,
1288   IN UINT8                       Types,
1289   IN UINT8                       TypeCount,
1290   IN OUT MTRR_MEMORY_RANGE       *Mtrrs,       OPTIONAL
1291   IN UINT32                      MtrrCapacity, OPTIONAL
1292   IN OUT UINT32                  *MtrrCount    OPTIONAL
1293   )
1294 {
1295   RETURN_STATUS                  Status;
1296   UINT64                         Base;
1297   UINT64                         Length;
1298   UINT8                          PrecedentTypes;
1299   UINTN                          Index;
1300   UINT64                         HBase;
1301   UINT64                         HLength;
1302   UINT64                         SubLength;
1303   UINT16                         SubStart;
1304   UINT16                         SubStop;
1305   UINT16                         Cur;
1306   UINT16                         Pre;
1307   MTRR_MEMORY_CACHE_TYPE         LowestType;
1308   MTRR_MEMORY_CACHE_TYPE         LowestPrecedentType;
1309 
1310   Base   = Vertices[Start].Address;
1311   Length = Vertices[Stop].Address - Base;
1312 
1313   LowestType = MtrrLibLowestType (Types);
1314 
1315   //
1316   // Clear the lowest type (highest bit) to get the precedent types
1317   //
1318   PrecedentTypes = ~(1 << LowestType) & Types;
1319   LowestPrecedentType = MtrrLibLowestType (PrecedentTypes);
1320 
1321   if (Mtrrs == NULL) {
1322     Weight[M(Start, Stop)] = ((LowestType == DefaultType) ? 0 : 1);
1323     Weight[O(Start, Stop)] = ((LowestType == DefaultType) ? 1 : 0);
1324   }
1325 
1326   // Add all high level ranges
1327   HBase = MAX_UINT64;
1328   HLength = 0;
1329   for (Index = 0; Index < RangeCount; Index++) {
1330     if (Length == 0) {
1331       break;
1332     }
1333     if ((Base < Ranges[Index].BaseAddress) || (Ranges[Index].BaseAddress + Ranges[Index].Length <= Base)) {
1334       continue;
1335     }
1336 
1337     //
1338     // Base is in the Range[Index]
1339     //
1340     if (Base + Length > Ranges[Index].BaseAddress + Ranges[Index].Length) {
1341       SubLength = Ranges[Index].BaseAddress + Ranges[Index].Length - Base;
1342     } else {
1343       SubLength = Length;
1344     }
1345     if (((1 << Ranges[Index].Type) & PrecedentTypes) != 0) {
1346       //
1347       // Meet a range whose types take precedence.
1348       // Update the [HBase, HBase + HLength) to include the range,
1349       // [HBase, HBase + HLength) may contain sub ranges with 2 different types, and both take precedence.
1350       //
1351       if (HBase == MAX_UINT64) {
1352         HBase = Base;
1353       }
1354       HLength += SubLength;
1355     }
1356 
1357     Base += SubLength;
1358     Length -= SubLength;
1359 
1360     if (HLength == 0) {
1361       continue;
1362     }
1363 
1364     if ((Ranges[Index].Type == LowestType) || (Length == 0)) { // meet low type or end
1365 
1366       //
1367       // Add the MTRRs for each high priority type range
1368       // the range[HBase, HBase + HLength) contains only two types.
1369       // We might use positive or subtractive, depending on which way uses less MTRR
1370       //
1371       for (SubStart = Start; SubStart <= Stop; SubStart++) {
1372         if (Vertices[SubStart].Address == HBase) {
1373           break;
1374         }
1375       }
1376 
1377       for (SubStop = SubStart; SubStop <= Stop; SubStop++) {
1378         if (Vertices[SubStop].Address == HBase + HLength) {
1379           break;
1380         }
1381       }
1382       ASSERT (Vertices[SubStart].Address == HBase);
1383       ASSERT (Vertices[SubStop].Address == HBase + HLength);
1384 
1385       if ((TypeCount == 2) || (SubStart == SubStop - 1)) {
1386         //
1387         // add subtractive MTRRs for [HBase, HBase + HLength)
1388         // [HBase, HBase + HLength) contains only one type.
1389         // while - loop is to split the range to MTRR - compliant aligned range.
1390         //
1391         if (Mtrrs == NULL) {
1392           Weight[M (Start, Stop)] += (UINT8)(SubStop - SubStart);
1393         } else {
1394           while (SubStart != SubStop) {
1395             Status = MtrrLibAppendVariableMtrr (
1396               Mtrrs, MtrrCapacity, MtrrCount,
1397               Vertices[SubStart].Address, Vertices[SubStart].Length, Vertices[SubStart].Type
1398             );
1399             if (RETURN_ERROR (Status)) {
1400               return Status;
1401             }
1402             SubStart++;
1403           }
1404         }
1405       } else {
1406         ASSERT (TypeCount == 3);
1407         MtrrLibCalculateLeastMtrrs (VertexCount, Vertices, Weight, SubStart, SubStop, TRUE);
1408 
1409         if (Mtrrs == NULL) {
1410           Weight[M (Start, Stop)] += Vertices[SubStop].Weight;
1411         } else {
1412           // When we need to collect the optimal path from SubStart to SubStop
1413           while (SubStop != SubStart) {
1414             Cur = SubStop;
1415             Pre = Vertices[Cur].Previous;
1416             SubStop = Pre;
1417 
1418             if (Weight[M (Pre, Cur)] + Weight[O (Pre, Cur)] != 0) {
1419               Status = MtrrLibAppendVariableMtrr (
1420                 Mtrrs, MtrrCapacity, MtrrCount,
1421                 Vertices[Pre].Address, Vertices[Cur].Address - Vertices[Pre].Address,
1422                 (Pre != Cur - 1) ? LowestPrecedentType : Vertices[Pre].Type
1423               );
1424               if (RETURN_ERROR (Status)) {
1425                 return Status;
1426               }
1427             }
1428             if (Pre != Cur - 1) {
1429               Status = MtrrLibCalculateSubtractivePath (
1430                 DefaultType, A0,
1431                 Ranges, RangeCount,
1432                 VertexCount, Vertices, Weight,
1433                 Pre, Cur, PrecedentTypes, 2,
1434                 Mtrrs, MtrrCapacity, MtrrCount
1435               );
1436               if (RETURN_ERROR (Status)) {
1437                 return Status;
1438               }
1439             }
1440           }
1441         }
1442 
1443       }
1444       //
1445       // Reset HBase, HLength
1446       //
1447       HBase = MAX_UINT64;
1448       HLength = 0;
1449     }
1450   }
1451   return RETURN_SUCCESS;
1452 }
1453 
1454 /**
1455   Calculate MTRR settings to cover the specified memory ranges.
1456 
1457   @param DefaultType  Default memory type.
1458   @param A0           Alignment to use when base address is 0.
1459   @param Ranges       Memory range array holding the memory type
1460                       settings for all memory address.
1461   @param RangeCount   Count of memory ranges.
1462   @param Scratch      A temporary scratch buffer that is used to perform the calculation.
1463                       This is an optional parameter that may be NULL.
1464   @param ScratchSize  Pointer to the size in bytes of the scratch buffer.
1465                       It may be updated to the actual required size when the calculation
1466                       needs more scratch buffer.
1467   @param Mtrrs        Array holding all MTRR settings.
1468   @param MtrrCapacity Capacity of the MTRR array.
1469   @param MtrrCount    The count of MTRR settings in array.
1470 
1471   @retval RETURN_SUCCESS          Variable MTRRs are allocated successfully.
1472   @retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.
1473   @retval RETURN_BUFFER_TOO_SMALL The scratch buffer is too small for MTRR calculation.
1474 **/
1475 RETURN_STATUS
MtrrLibCalculateMtrrs(IN MTRR_MEMORY_CACHE_TYPE DefaultType,IN UINT64 A0,IN CONST MTRR_MEMORY_RANGE * Ranges,IN UINTN RangeCount,IN VOID * Scratch,IN OUT UINTN * ScratchSize,IN OUT MTRR_MEMORY_RANGE * Mtrrs,IN UINT32 MtrrCapacity,IN OUT UINT32 * MtrrCount)1476 MtrrLibCalculateMtrrs (
1477   IN MTRR_MEMORY_CACHE_TYPE  DefaultType,
1478   IN UINT64                  A0,
1479   IN CONST MTRR_MEMORY_RANGE *Ranges,
1480   IN UINTN                   RangeCount,
1481   IN VOID                    *Scratch,
1482   IN OUT UINTN               *ScratchSize,
1483   IN OUT MTRR_MEMORY_RANGE   *Mtrrs,
1484   IN UINT32                  MtrrCapacity,
1485   IN OUT UINT32              *MtrrCount
1486   )
1487 {
1488   UINT64                    Base0;
1489   UINT64                    Base1;
1490   UINTN                     Index;
1491   UINT64                    Base;
1492   UINT64                    Length;
1493   UINT64                    Alignment;
1494   UINT64                    SubLength;
1495   MTRR_LIB_ADDRESS          *Vertices;
1496   UINT8                     *Weight;
1497   UINT32                    VertexIndex;
1498   UINT32                    VertexCount;
1499   UINTN                     RequiredScratchSize;
1500   UINT8                     TypeCount;
1501   UINT16                    Start;
1502   UINT16                    Stop;
1503   UINT8                     Type;
1504   RETURN_STATUS             Status;
1505 
1506   Base0 = Ranges[0].BaseAddress;
1507   Base1 = Ranges[RangeCount - 1].BaseAddress + Ranges[RangeCount - 1].Length;
1508   MTRR_LIB_ASSERT_ALIGNED (Base0, Base1 - Base0);
1509 
1510   //
1511   // Count the number of vertices.
1512   //
1513   Vertices = (MTRR_LIB_ADDRESS*)Scratch;
1514   for (VertexIndex = 0, Index = 0; Index < RangeCount; Index++) {
1515     Base = Ranges[Index].BaseAddress;
1516     Length = Ranges[Index].Length;
1517     while (Length != 0) {
1518       Alignment = MtrrLibBiggestAlignment (Base, A0);
1519       SubLength = Alignment;
1520       if (SubLength > Length) {
1521         SubLength = GetPowerOfTwo64 (Length);
1522       }
1523       if (VertexIndex < *ScratchSize / sizeof (*Vertices)) {
1524         Vertices[VertexIndex].Address   = Base;
1525         Vertices[VertexIndex].Alignment = Alignment;
1526         Vertices[VertexIndex].Type      = Ranges[Index].Type;
1527         Vertices[VertexIndex].Length    = SubLength;
1528       }
1529       Base   += SubLength;
1530       Length -= SubLength;
1531       VertexIndex++;
1532     }
1533   }
1534   //
1535   // Vertices[VertexIndex] = Base1, so whole vertex count is (VertexIndex + 1).
1536   //
1537   VertexCount = VertexIndex + 1;
1538   DEBUG ((
1539     DEBUG_CACHE, "  Count of vertices (%016llx - %016llx) = %d\n",
1540     Ranges[0].BaseAddress, Ranges[RangeCount - 1].BaseAddress + Ranges[RangeCount - 1].Length, VertexCount
1541     ));
1542   ASSERT (VertexCount < MAX_UINT16);
1543 
1544   RequiredScratchSize = VertexCount * sizeof (*Vertices) + VertexCount * VertexCount * sizeof (*Weight);
1545   if (*ScratchSize < RequiredScratchSize) {
1546     *ScratchSize = RequiredScratchSize;
1547     return RETURN_BUFFER_TOO_SMALL;
1548   }
1549   Vertices[VertexCount - 1].Address = Base1;
1550 
1551   Weight = (UINT8 *) &Vertices[VertexCount];
1552   for (VertexIndex = 0; VertexIndex < VertexCount; VertexIndex++) {
1553     //
1554     // Set optional weight between vertices and self->self to 0
1555     //
1556     SetMem (&Weight[M(VertexIndex, 0)], VertexIndex + 1, 0);
1557     //
1558     // Set mandatory weight between vertices to MAX_WEIGHT
1559     //
1560     SetMem (&Weight[M (VertexIndex, VertexIndex + 1)], VertexCount - VertexIndex - 1, MAX_WEIGHT);
1561 
1562     // Final result looks like:
1563     //   00 FF FF FF
1564     //   00 00 FF FF
1565     //   00 00 00 FF
1566     //   00 00 00 00
1567   }
1568 
1569   //
1570   // Set mandatory weight and optional weight for adjacent vertices
1571   //
1572   for (VertexIndex = 0; VertexIndex < VertexCount - 1; VertexIndex++) {
1573     if (Vertices[VertexIndex].Type != DefaultType) {
1574       Weight[M (VertexIndex, VertexIndex + 1)] = 1;
1575       Weight[O (VertexIndex, VertexIndex + 1)] = 0;
1576     } else {
1577       Weight[M (VertexIndex, VertexIndex + 1)] = 0;
1578       Weight[O (VertexIndex, VertexIndex + 1)] = 1;
1579     }
1580   }
1581 
1582   for (TypeCount = 2; TypeCount <= 3; TypeCount++) {
1583     for (Start = 0; Start < VertexCount; Start++) {
1584       for (Stop = Start + 2; Stop < VertexCount; Stop++) {
1585         ASSERT (Vertices[Stop].Address > Vertices[Start].Address);
1586         Length = Vertices[Stop].Address - Vertices[Start].Address;
1587         if (Length > Vertices[Start].Alignment) {
1588           //
1589           // Pickup a new Start when [Start, Stop) cannot be described by one MTRR.
1590           //
1591           break;
1592         }
1593         if ((Weight[M(Start, Stop)] == MAX_WEIGHT) && MtrrLibIsPowerOfTwo (Length)) {
1594           if (MtrrLibGetNumberOfTypes (
1595                 Ranges, RangeCount, Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, &Type
1596                 ) == TypeCount) {
1597             //
1598             // Update the Weight[Start, Stop] using subtractive path.
1599             //
1600             MtrrLibCalculateSubtractivePath (
1601               DefaultType, A0,
1602               Ranges, RangeCount,
1603               (UINT16)VertexCount, Vertices, Weight,
1604               Start, Stop, Type, TypeCount,
1605               NULL, 0, NULL
1606               );
1607           } else if (TypeCount == 2) {
1608             //
1609             // Pick up a new Start when we expect 2-type range, but 3-type range is met.
1610             // Because no matter how Stop is increased, we always meet 3-type range.
1611             //
1612             break;
1613           }
1614         }
1615       }
1616     }
1617   }
1618 
1619   Status = RETURN_SUCCESS;
1620   MtrrLibCalculateLeastMtrrs ((UINT16) VertexCount, Vertices, Weight, 0, (UINT16) VertexCount - 1, FALSE);
1621   Stop = (UINT16) VertexCount - 1;
1622   while (Stop != 0) {
1623     Start = Vertices[Stop].Previous;
1624     TypeCount = MAX_UINT8;
1625     Type = 0;
1626     if (Weight[M(Start, Stop)] != 0) {
1627       TypeCount = MtrrLibGetNumberOfTypes (Ranges, RangeCount, Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, &Type);
1628       Status = MtrrLibAppendVariableMtrr (
1629         Mtrrs, MtrrCapacity, MtrrCount,
1630         Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address,
1631         MtrrLibLowestType (Type)
1632         );
1633       if (RETURN_ERROR (Status)) {
1634         break;
1635       }
1636     }
1637 
1638     if (Start != Stop - 1) {
1639       //
1640       // substractive path
1641       //
1642       if (TypeCount == MAX_UINT8) {
1643         TypeCount = MtrrLibGetNumberOfTypes (
1644                       Ranges, RangeCount, Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, &Type
1645                       );
1646       }
1647       Status = MtrrLibCalculateSubtractivePath (
1648                  DefaultType, A0,
1649                  Ranges, RangeCount,
1650                  (UINT16) VertexCount, Vertices, Weight, Start, Stop,
1651                  Type, TypeCount,
1652                  Mtrrs, MtrrCapacity, MtrrCount
1653                  );
1654       if (RETURN_ERROR (Status)) {
1655         break;
1656       }
1657     }
1658     Stop = Start;
1659   }
1660   return Status;
1661 }
1662 
1663 
1664 /**
1665   Apply the fixed MTRR settings to memory range array.
1666 
1667   @param Fixed             The fixed MTRR settings.
1668   @param Ranges            Return the memory range array holding memory type
1669                            settings for all memory address.
1670   @param RangeCapacity     The capacity of memory range array.
1671   @param RangeCount        Return the count of memory range.
1672 
1673   @retval RETURN_SUCCESS          The memory range array is returned successfully.
1674   @retval RETURN_OUT_OF_RESOURCES The count of memory ranges exceeds capacity.
1675 **/
1676 RETURN_STATUS
MtrrLibApplyFixedMtrrs(IN MTRR_FIXED_SETTINGS * Fixed,IN OUT MTRR_MEMORY_RANGE * Ranges,IN UINTN RangeCapacity,IN OUT UINTN * RangeCount)1677 MtrrLibApplyFixedMtrrs (
1678   IN     MTRR_FIXED_SETTINGS  *Fixed,
1679   IN OUT MTRR_MEMORY_RANGE    *Ranges,
1680   IN     UINTN                RangeCapacity,
1681   IN OUT UINTN                *RangeCount
1682   )
1683 {
1684   RETURN_STATUS               Status;
1685   UINTN                       MsrIndex;
1686   UINTN                       Index;
1687   MTRR_MEMORY_CACHE_TYPE      MemoryType;
1688   UINT64                      Base;
1689 
1690   Base = 0;
1691   for (MsrIndex = 0; MsrIndex < ARRAY_SIZE (mMtrrLibFixedMtrrTable); MsrIndex++) {
1692     ASSERT (Base == mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress);
1693     for (Index = 0; Index < sizeof (UINT64); Index++) {
1694       MemoryType = (MTRR_MEMORY_CACHE_TYPE)((UINT8 *)(&Fixed->Mtrr[MsrIndex]))[Index];
1695       Status = MtrrLibSetMemoryType (
1696                  Ranges, RangeCapacity, RangeCount, Base, mMtrrLibFixedMtrrTable[MsrIndex].Length, MemoryType
1697                  );
1698       if (Status == RETURN_OUT_OF_RESOURCES) {
1699         return Status;
1700       }
1701       Base += mMtrrLibFixedMtrrTable[MsrIndex].Length;
1702     }
1703   }
1704   ASSERT (Base == BASE_1MB);
1705   return RETURN_SUCCESS;
1706 }
1707 
1708 /**
1709   Apply the variable MTRR settings to memory range array.
1710 
1711   @param VariableMtrr      The variable MTRR array.
1712   @param VariableMtrrCount The count of variable MTRRs.
1713   @param Ranges            Return the memory range array with new MTRR settings applied.
1714   @param RangeCapacity     The capacity of memory range array.
1715   @param RangeCount        Return the count of memory range.
1716 
1717   @retval RETURN_SUCCESS          The memory range array is returned successfully.
1718   @retval RETURN_OUT_OF_RESOURCES The count of memory ranges exceeds capacity.
1719 **/
1720 RETURN_STATUS
MtrrLibApplyVariableMtrrs(IN CONST MTRR_MEMORY_RANGE * VariableMtrr,IN UINT32 VariableMtrrCount,IN OUT MTRR_MEMORY_RANGE * Ranges,IN UINTN RangeCapacity,IN OUT UINTN * RangeCount)1721 MtrrLibApplyVariableMtrrs (
1722   IN     CONST MTRR_MEMORY_RANGE *VariableMtrr,
1723   IN     UINT32                  VariableMtrrCount,
1724   IN OUT MTRR_MEMORY_RANGE       *Ranges,
1725   IN     UINTN                   RangeCapacity,
1726   IN OUT UINTN                   *RangeCount
1727   )
1728 {
1729   RETURN_STATUS                  Status;
1730   UINTN                          Index;
1731 
1732   //
1733   // WT > WB
1734   // UC > *
1735   // UC > * (except WB, UC) > WB
1736   //
1737 
1738   //
1739   // 1. Set WB
1740   //
1741   for (Index = 0; Index < VariableMtrrCount; Index++) {
1742     if ((VariableMtrr[Index].Length != 0) && (VariableMtrr[Index].Type == CacheWriteBack)) {
1743       Status = MtrrLibSetMemoryType (
1744         Ranges, RangeCapacity, RangeCount,
1745         VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, VariableMtrr[Index].Type
1746       );
1747       if (Status == RETURN_OUT_OF_RESOURCES) {
1748         return Status;
1749       }
1750     }
1751   }
1752 
1753   //
1754   // 2. Set other types than WB or UC
1755   //
1756   for (Index = 0; Index < VariableMtrrCount; Index++) {
1757     if ((VariableMtrr[Index].Length != 0) &&
1758         (VariableMtrr[Index].Type != CacheWriteBack) && (VariableMtrr[Index].Type != CacheUncacheable)) {
1759       Status = MtrrLibSetMemoryType (
1760                  Ranges, RangeCapacity, RangeCount,
1761                  VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, VariableMtrr[Index].Type
1762                  );
1763       if (Status == RETURN_OUT_OF_RESOURCES) {
1764         return Status;
1765       }
1766     }
1767   }
1768 
1769   //
1770   // 3. Set UC
1771   //
1772   for (Index = 0; Index < VariableMtrrCount; Index++) {
1773     if (VariableMtrr[Index].Length != 0 && VariableMtrr[Index].Type == CacheUncacheable) {
1774       Status = MtrrLibSetMemoryType (
1775                  Ranges, RangeCapacity, RangeCount,
1776                  VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, VariableMtrr[Index].Type
1777                  );
1778       if (Status == RETURN_OUT_OF_RESOURCES) {
1779         return Status;
1780       }
1781     }
1782   }
1783   return RETURN_SUCCESS;
1784 }
1785 
1786 /**
1787   Return the memory type bit mask that's compatible to first type in the Ranges.
1788 
1789   @param Ranges     Memory range array holding the memory type
1790                     settings for all memory address.
1791   @param RangeCount Count of memory ranges.
1792 
1793   @return Compatible memory type bit mask.
1794 **/
1795 UINT8
MtrrLibGetCompatibleTypes(IN CONST MTRR_MEMORY_RANGE * Ranges,IN UINTN RangeCount)1796 MtrrLibGetCompatibleTypes (
1797   IN CONST MTRR_MEMORY_RANGE *Ranges,
1798   IN UINTN                   RangeCount
1799   )
1800 {
1801   ASSERT (RangeCount != 0);
1802 
1803   switch (Ranges[0].Type) {
1804   case CacheWriteBack:
1805   case CacheWriteThrough:
1806     return (1 << CacheWriteBack) | (1 << CacheWriteThrough) | (1 << CacheUncacheable);
1807     break;
1808 
1809   case CacheWriteCombining:
1810   case CacheWriteProtected:
1811     return (1 << Ranges[0].Type) | (1 << CacheUncacheable);
1812     break;
1813 
1814   case CacheUncacheable:
1815     if (RangeCount == 1) {
1816       return (1 << CacheUncacheable);
1817     }
1818     return MtrrLibGetCompatibleTypes (&Ranges[1], RangeCount - 1);
1819     break;
1820 
1821   case CacheInvalid:
1822   default:
1823     ASSERT (FALSE);
1824     break;
1825   }
1826   return 0;
1827 }
1828 
1829 /**
1830   Overwrite the destination MTRR settings with the source MTRR settings.
1831   This routine is to make sure the modification to destination MTRR settings
1832   is as small as possible.
1833 
1834   @param DstMtrrs     Destination MTRR settings.
1835   @param DstMtrrCount Count of destination MTRR settings.
1836   @param SrcMtrrs     Source MTRR settings.
1837   @param SrcMtrrCount Count of source MTRR settings.
1838   @param Modified     Flag array to indicate which destination MTRR setting is modified.
1839 **/
1840 VOID
MtrrLibMergeVariableMtrr(MTRR_MEMORY_RANGE * DstMtrrs,UINT32 DstMtrrCount,MTRR_MEMORY_RANGE * SrcMtrrs,UINT32 SrcMtrrCount,BOOLEAN * Modified)1841 MtrrLibMergeVariableMtrr (
1842   MTRR_MEMORY_RANGE *DstMtrrs,
1843   UINT32            DstMtrrCount,
1844   MTRR_MEMORY_RANGE *SrcMtrrs,
1845   UINT32            SrcMtrrCount,
1846   BOOLEAN           *Modified
1847   )
1848 {
1849   UINT32          DstIndex;
1850   UINT32          SrcIndex;
1851 
1852   ASSERT (SrcMtrrCount <= DstMtrrCount);
1853 
1854   for (DstIndex = 0; DstIndex < DstMtrrCount; DstIndex++) {
1855     Modified[DstIndex] = FALSE;
1856 
1857     if (DstMtrrs[DstIndex].Length == 0) {
1858       continue;
1859     }
1860     for (SrcIndex = 0; SrcIndex < SrcMtrrCount; SrcIndex++) {
1861       if (DstMtrrs[DstIndex].BaseAddress == SrcMtrrs[SrcIndex].BaseAddress &&
1862         DstMtrrs[DstIndex].Length == SrcMtrrs[SrcIndex].Length &&
1863         DstMtrrs[DstIndex].Type == SrcMtrrs[SrcIndex].Type) {
1864         break;
1865       }
1866     }
1867 
1868     if (SrcIndex == SrcMtrrCount) {
1869       //
1870       // Remove the one from DstMtrrs which is not in SrcMtrrs
1871       //
1872       DstMtrrs[DstIndex].Length = 0;
1873       Modified[DstIndex] = TRUE;
1874     } else {
1875       //
1876       // Remove the one from SrcMtrrs which is also in DstMtrrs
1877       //
1878       SrcMtrrs[SrcIndex].Length = 0;
1879     }
1880   }
1881 
1882   //
1883   // Now valid MTRR only exists in either DstMtrrs or SrcMtrrs.
1884   // Merge MTRRs from SrcMtrrs to DstMtrrs
1885   //
1886   DstIndex = 0;
1887   for (SrcIndex = 0; SrcIndex < SrcMtrrCount; SrcIndex++) {
1888     if (SrcMtrrs[SrcIndex].Length != 0) {
1889 
1890       //
1891       // Find the empty slot in DstMtrrs
1892       //
1893       while (DstIndex < DstMtrrCount) {
1894         if (DstMtrrs[DstIndex].Length == 0) {
1895           break;
1896         }
1897         DstIndex++;
1898       }
1899       ASSERT (DstIndex < DstMtrrCount);
1900       CopyMem (&DstMtrrs[DstIndex], &SrcMtrrs[SrcIndex], sizeof (SrcMtrrs[0]));
1901       Modified[DstIndex] = TRUE;
1902     }
1903   }
1904 }
1905 
1906 /**
1907   Calculate the variable MTRR settings for all memory ranges.
1908 
1909   @param DefaultType          Default memory type.
1910   @param A0                   Alignment to use when base address is 0.
1911   @param Ranges               Memory range array holding the memory type
1912                               settings for all memory address.
1913   @param RangeCount           Count of memory ranges.
1914   @param Scratch              Scratch buffer to be used in MTRR calculation.
1915   @param ScratchSize          Pointer to the size of scratch buffer.
1916   @param VariableMtrr         Array holding all MTRR settings.
1917   @param VariableMtrrCapacity Capacity of the MTRR array.
1918   @param VariableMtrrCount    The count of MTRR settings in array.
1919 
1920   @retval RETURN_SUCCESS          Variable MTRRs are allocated successfully.
1921   @retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.
1922   @retval RETURN_BUFFER_TOO_SMALL The scratch buffer is too small for MTRR calculation.
1923                                   The required scratch buffer size is returned through ScratchSize.
1924 **/
1925 RETURN_STATUS
MtrrLibSetMemoryRanges(IN MTRR_MEMORY_CACHE_TYPE DefaultType,IN UINT64 A0,IN MTRR_MEMORY_RANGE * Ranges,IN UINTN RangeCount,IN VOID * Scratch,IN OUT UINTN * ScratchSize,OUT MTRR_MEMORY_RANGE * VariableMtrr,IN UINT32 VariableMtrrCapacity,OUT UINT32 * VariableMtrrCount)1926 MtrrLibSetMemoryRanges (
1927   IN MTRR_MEMORY_CACHE_TYPE DefaultType,
1928   IN UINT64                 A0,
1929   IN MTRR_MEMORY_RANGE      *Ranges,
1930   IN UINTN                  RangeCount,
1931   IN VOID                   *Scratch,
1932   IN OUT UINTN              *ScratchSize,
1933   OUT MTRR_MEMORY_RANGE     *VariableMtrr,
1934   IN UINT32                 VariableMtrrCapacity,
1935   OUT UINT32                *VariableMtrrCount
1936   )
1937 {
1938   RETURN_STATUS             Status;
1939   UINT32                    Index;
1940   UINT64                    Base0;
1941   UINT64                    Base1;
1942   UINT64                    Alignment;
1943   UINT8                     CompatibleTypes;
1944   UINT64                    Length;
1945   UINT32                    End;
1946   UINTN                     ActualScratchSize;
1947   UINTN                     BiggestScratchSize;
1948 
1949   *VariableMtrrCount = 0;
1950 
1951   //
1952   // Since the whole ranges need multiple calls of MtrrLibCalculateMtrrs().
1953   // Each call needs different scratch buffer size.
1954   // When the provided scratch buffer size is not sufficient in any call,
1955   // set the GetActualScratchSize to TRUE, and following calls will only
1956   // calculate the actual scratch size for the caller.
1957   //
1958   BiggestScratchSize = 0;
1959 
1960   for (Index = 0; Index < RangeCount;) {
1961     Base0 = Ranges[Index].BaseAddress;
1962 
1963     //
1964     // Full step is optimal
1965     //
1966     while (Index < RangeCount) {
1967       ASSERT (Ranges[Index].BaseAddress == Base0);
1968       Alignment = MtrrLibBiggestAlignment (Base0, A0);
1969       while (Base0 + Alignment <= Ranges[Index].BaseAddress + Ranges[Index].Length) {
1970         if ((BiggestScratchSize <= *ScratchSize) && (Ranges[Index].Type != DefaultType)) {
1971           Status = MtrrLibAppendVariableMtrr (
1972             VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,
1973             Base0, Alignment, Ranges[Index].Type
1974             );
1975           if (RETURN_ERROR (Status)) {
1976             return Status;
1977           }
1978         }
1979         Base0 += Alignment;
1980         Alignment = MtrrLibBiggestAlignment (Base0, A0);
1981       }
1982 
1983       //
1984       // Remove the above range from Ranges[Index]
1985       //
1986       Ranges[Index].Length -= Base0 - Ranges[Index].BaseAddress;
1987       Ranges[Index].BaseAddress = Base0;
1988       if (Ranges[Index].Length != 0) {
1989         break;
1990       } else {
1991         Index++;
1992       }
1993     }
1994 
1995     if (Index == RangeCount) {
1996       break;
1997     }
1998 
1999     //
2000     // Find continous ranges [Base0, Base1) which could be combined by MTRR.
2001     // Per SDM, the compatible types between[B0, B1) are:
2002     //   UC, *
2003     //   WB, WT
2004     //   UC, WB, WT
2005     //
2006     CompatibleTypes = MtrrLibGetCompatibleTypes (&Ranges[Index], RangeCount - Index);
2007 
2008     End = Index; // End points to last one that matches the CompatibleTypes.
2009     while (End + 1 < RangeCount) {
2010       if (((1 << Ranges[End + 1].Type) & CompatibleTypes) == 0) {
2011         break;
2012       }
2013       End++;
2014     }
2015     Alignment = MtrrLibBiggestAlignment (Base0, A0);
2016     Length    = GetPowerOfTwo64 (Ranges[End].BaseAddress + Ranges[End].Length - Base0);
2017     Base1     = Base0 + MIN (Alignment, Length);
2018 
2019     //
2020     // Base1 may not in Ranges[End]. Update End to the range Base1 belongs to.
2021     //
2022     End = Index;
2023     while (End + 1 < RangeCount) {
2024       if (Base1 <= Ranges[End + 1].BaseAddress) {
2025         break;
2026       }
2027       End++;
2028     }
2029 
2030     Length = Ranges[End].Length;
2031     Ranges[End].Length = Base1 - Ranges[End].BaseAddress;
2032     ActualScratchSize  = *ScratchSize;
2033     Status = MtrrLibCalculateMtrrs (
2034                DefaultType, A0,
2035                &Ranges[Index], End + 1 - Index,
2036                Scratch, &ActualScratchSize,
2037                VariableMtrr, VariableMtrrCapacity, VariableMtrrCount
2038                );
2039     if (Status == RETURN_BUFFER_TOO_SMALL) {
2040       BiggestScratchSize = MAX (BiggestScratchSize, ActualScratchSize);
2041       //
2042       // Ignore this error, because we need to calculate the biggest
2043       // scratch buffer size.
2044       //
2045       Status = RETURN_SUCCESS;
2046     }
2047     if (RETURN_ERROR (Status)) {
2048       return Status;
2049     }
2050 
2051     if (Length != Ranges[End].Length) {
2052       Ranges[End].BaseAddress = Base1;
2053       Ranges[End].Length = Length - Ranges[End].Length;
2054       Index = End;
2055     } else {
2056       Index = End + 1;
2057     }
2058   }
2059 
2060   if (*ScratchSize < BiggestScratchSize) {
2061     *ScratchSize = BiggestScratchSize;
2062     return RETURN_BUFFER_TOO_SMALL;
2063   }
2064   return RETURN_SUCCESS;
2065 }
2066 
2067 /**
2068   Set the below-1MB memory attribute to fixed MTRR buffer.
2069   Modified flag array indicates which fixed MTRR is modified.
2070 
2071   @param [in, out] ClearMasks    The bits (when set) to clear in the fixed MTRR MSR.
2072   @param [in, out] OrMasks       The bits to set in the fixed MTRR MSR.
2073   @param [in]      BaseAddress   Base address.
2074   @param [in]      Length        Length.
2075   @param [in]      Type          Memory type.
2076 
2077   @retval RETURN_SUCCESS      The memory attribute is set successfully.
2078   @retval RETURN_UNSUPPORTED  The requested range or cache type was invalid
2079                               for the fixed MTRRs.
2080 **/
2081 RETURN_STATUS
MtrrLibSetBelow1MBMemoryAttribute(IN OUT UINT64 * ClearMasks,IN OUT UINT64 * OrMasks,IN PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN MTRR_MEMORY_CACHE_TYPE Type)2082 MtrrLibSetBelow1MBMemoryAttribute (
2083   IN OUT UINT64                  *ClearMasks,
2084   IN OUT UINT64                  *OrMasks,
2085   IN PHYSICAL_ADDRESS            BaseAddress,
2086   IN UINT64                      Length,
2087   IN MTRR_MEMORY_CACHE_TYPE      Type
2088   )
2089 {
2090   RETURN_STATUS             Status;
2091   UINT32                    MsrIndex;
2092   UINT64                    ClearMask;
2093   UINT64                    OrMask;
2094 
2095   ASSERT (BaseAddress < BASE_1MB);
2096 
2097   MsrIndex = (UINT32)-1;
2098   while ((BaseAddress < BASE_1MB) && (Length != 0)) {
2099     Status = MtrrLibProgramFixedMtrr (Type, &BaseAddress, &Length, &MsrIndex, &ClearMask, &OrMask);
2100     if (RETURN_ERROR (Status)) {
2101       return Status;
2102     }
2103     ClearMasks[MsrIndex] = ClearMasks[MsrIndex] | ClearMask;
2104     OrMasks[MsrIndex]    = (OrMasks[MsrIndex] & ~ClearMask) | OrMask;
2105   }
2106   return RETURN_SUCCESS;
2107 }
2108 
2109 /**
2110   This function attempts to set the attributes into MTRR setting buffer for multiple memory ranges.
2111 
2112   @param[in, out]  MtrrSetting  MTRR setting buffer to be set.
2113   @param[in]       Scratch      A temporary scratch buffer that is used to perform the calculation.
2114   @param[in, out]  ScratchSize  Pointer to the size in bytes of the scratch buffer.
2115                                 It may be updated to the actual required size when the calculation
2116                                 needs more scratch buffer.
2117   @param[in]       Ranges       Pointer to an array of MTRR_MEMORY_RANGE.
2118                                 When range overlap happens, the last one takes higher priority.
2119                                 When the function returns, either all the attributes are set successfully,
2120                                 or none of them is set.
2121   @param[in]       RangeCount   Count of MTRR_MEMORY_RANGE.
2122 
2123   @retval RETURN_SUCCESS            The attributes were set for all the memory ranges.
2124   @retval RETURN_INVALID_PARAMETER  Length in any range is zero.
2125   @retval RETURN_UNSUPPORTED        The processor does not support one or more bytes of the
2126                                     memory resource range specified by BaseAddress and Length in any range.
2127   @retval RETURN_UNSUPPORTED        The bit mask of attributes is not support for the memory resource
2128                                     range specified by BaseAddress and Length in any range.
2129   @retval RETURN_OUT_OF_RESOURCES   There are not enough system resources to modify the attributes of
2130                                     the memory resource ranges.
2131   @retval RETURN_ACCESS_DENIED      The attributes for the memory resource range specified by
2132                                     BaseAddress and Length cannot be modified.
2133   @retval RETURN_BUFFER_TOO_SMALL   The scratch buffer is too small for MTRR calculation.
2134 **/
2135 RETURN_STATUS
2136 EFIAPI
MtrrSetMemoryAttributesInMtrrSettings(IN OUT MTRR_SETTINGS * MtrrSetting,IN VOID * Scratch,IN OUT UINTN * ScratchSize,IN CONST MTRR_MEMORY_RANGE * Ranges,IN UINTN RangeCount)2137 MtrrSetMemoryAttributesInMtrrSettings (
2138   IN OUT MTRR_SETTINGS           *MtrrSetting,
2139   IN     VOID                    *Scratch,
2140   IN OUT UINTN                   *ScratchSize,
2141   IN     CONST MTRR_MEMORY_RANGE *Ranges,
2142   IN     UINTN                   RangeCount
2143   )
2144 {
2145   RETURN_STATUS             Status;
2146   UINT32                    Index;
2147   UINT64                    BaseAddress;
2148   UINT64                    Length;
2149   BOOLEAN                   Above1MbExist;
2150 
2151   UINT64                    MtrrValidBitsMask;
2152   UINT64                    MtrrValidAddressMask;
2153   MTRR_MEMORY_CACHE_TYPE    DefaultType;
2154   MTRR_VARIABLE_SETTINGS    VariableSettings;
2155   MTRR_MEMORY_RANGE         WorkingRanges[2 * ARRAY_SIZE (MtrrSetting->Variables.Mtrr) + 2];
2156   UINTN                     WorkingRangeCount;
2157   BOOLEAN                   Modified;
2158   MTRR_VARIABLE_SETTING     VariableSetting;
2159   UINT32                    OriginalVariableMtrrCount;
2160   UINT32                    FirmwareVariableMtrrCount;
2161   UINT32                    WorkingVariableMtrrCount;
2162   MTRR_MEMORY_RANGE         OriginalVariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];
2163   MTRR_MEMORY_RANGE         WorkingVariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];
2164   BOOLEAN                   VariableSettingModified[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];
2165 
2166   UINT64                    ClearMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];
2167   UINT64                    OrMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];
2168 
2169   MTRR_CONTEXT              MtrrContext;
2170   BOOLEAN                   MtrrContextValid;
2171 
2172   Status = RETURN_SUCCESS;
2173   MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask);
2174 
2175   //
2176   // TRUE indicating the accordingly Variable setting needs modificaiton in OriginalVariableMtrr.
2177   //
2178   SetMem (VariableSettingModified, ARRAY_SIZE (VariableSettingModified), FALSE);
2179 
2180   //
2181   // TRUE indicating the caller requests to set variable MTRRs.
2182   //
2183   Above1MbExist             = FALSE;
2184   OriginalVariableMtrrCount = 0;
2185 
2186   //
2187   // 0. Dump the requests.
2188   //
2189   DEBUG_CODE (
2190     DEBUG ((DEBUG_CACHE, "Mtrr: Set Mem Attribute to %a, ScratchSize = %x%a",
2191             (MtrrSetting == NULL) ? "Hardware" : "Buffer", *ScratchSize,
2192             (RangeCount <= 1) ? "," : "\n"
2193             ));
2194     for (Index = 0; Index < RangeCount; Index++) {
2195       DEBUG ((DEBUG_CACHE, " %a: [%016lx, %016lx)\n",
2196               mMtrrMemoryCacheTypeShortName[MIN (Ranges[Index].Type, CacheInvalid)],
2197               Ranges[Index].BaseAddress, Ranges[Index].BaseAddress + Ranges[Index].Length
2198               ));
2199     }
2200   );
2201 
2202   //
2203   // 1. Validate the parameters.
2204   //
2205   if (!IsMtrrSupported ()) {
2206     Status = RETURN_UNSUPPORTED;
2207     goto Exit;
2208   }
2209 
2210   for (Index = 0; Index < RangeCount; Index++) {
2211     if (Ranges[Index].Length == 0) {
2212       Status = RETURN_INVALID_PARAMETER;
2213       goto Exit;
2214     }
2215     if (((Ranges[Index].BaseAddress & ~MtrrValidAddressMask) != 0) ||
2216         ((((Ranges[Index].BaseAddress + Ranges[Index].Length) & ~MtrrValidAddressMask) != 0) &&
2217           (Ranges[Index].BaseAddress + Ranges[Index].Length) != MtrrValidBitsMask + 1)
2218         ) {
2219       //
2220       // Either the BaseAddress or the Limit doesn't follow the alignment requirement.
2221       // Note: It's still valid if Limit doesn't follow the alignment requirement but equals to MAX Address.
2222       //
2223       Status = RETURN_UNSUPPORTED;
2224       goto Exit;
2225     }
2226     if ((Ranges[Index].Type != CacheUncacheable) &&
2227         (Ranges[Index].Type != CacheWriteCombining) &&
2228         (Ranges[Index].Type != CacheWriteThrough) &&
2229         (Ranges[Index].Type != CacheWriteProtected) &&
2230         (Ranges[Index].Type != CacheWriteBack)) {
2231       Status = RETURN_INVALID_PARAMETER;
2232       goto Exit;
2233     }
2234     if (Ranges[Index].BaseAddress + Ranges[Index].Length > BASE_1MB) {
2235       Above1MbExist = TRUE;
2236     }
2237   }
2238 
2239   //
2240   // 2. Apply the above-1MB memory attribute settings.
2241   //
2242   if (Above1MbExist) {
2243     //
2244     // 2.1. Read all variable MTRRs and convert to Ranges.
2245     //
2246     OriginalVariableMtrrCount = GetVariableMtrrCountWorker ();
2247     MtrrGetVariableMtrrWorker (MtrrSetting, OriginalVariableMtrrCount, &VariableSettings);
2248     MtrrLibGetRawVariableRanges (
2249       &VariableSettings, OriginalVariableMtrrCount,
2250       MtrrValidBitsMask, MtrrValidAddressMask, OriginalVariableMtrr
2251       );
2252 
2253     DefaultType = MtrrGetDefaultMemoryTypeWorker (MtrrSetting);
2254     WorkingRangeCount = 1;
2255     WorkingRanges[0].BaseAddress = 0;
2256     WorkingRanges[0].Length      = MtrrValidBitsMask + 1;
2257     WorkingRanges[0].Type        = DefaultType;
2258 
2259     Status = MtrrLibApplyVariableMtrrs (
2260                OriginalVariableMtrr, OriginalVariableMtrrCount,
2261                WorkingRanges, ARRAY_SIZE (WorkingRanges), &WorkingRangeCount);
2262     ASSERT_RETURN_ERROR (Status);
2263 
2264     ASSERT (OriginalVariableMtrrCount >= PcdGet32 (PcdCpuNumberOfReservedVariableMtrrs));
2265     FirmwareVariableMtrrCount = OriginalVariableMtrrCount - PcdGet32 (PcdCpuNumberOfReservedVariableMtrrs);
2266     ASSERT (WorkingRangeCount <= 2 * FirmwareVariableMtrrCount + 1);
2267 
2268     //
2269     // 2.2. Force [0, 1M) to UC, so that it doesn't impact subtraction algorithm.
2270     //
2271     Status = MtrrLibSetMemoryType (
2272                WorkingRanges, ARRAY_SIZE (WorkingRanges), &WorkingRangeCount,
2273                0, SIZE_1MB, CacheUncacheable
2274                );
2275     ASSERT (Status != RETURN_OUT_OF_RESOURCES);
2276 
2277     //
2278     // 2.3. Apply the new memory attribute settings to Ranges.
2279     //
2280     Modified = FALSE;
2281     for (Index = 0; Index < RangeCount; Index++) {
2282       BaseAddress = Ranges[Index].BaseAddress;
2283       Length = Ranges[Index].Length;
2284       if (BaseAddress < BASE_1MB) {
2285         if (Length <= BASE_1MB - BaseAddress) {
2286           continue;
2287         }
2288         Length -= BASE_1MB - BaseAddress;
2289         BaseAddress = BASE_1MB;
2290       }
2291       Status = MtrrLibSetMemoryType (
2292                  WorkingRanges, ARRAY_SIZE (WorkingRanges), &WorkingRangeCount,
2293                  BaseAddress, Length, Ranges[Index].Type
2294                  );
2295       if (Status == RETURN_ALREADY_STARTED) {
2296         Status = RETURN_SUCCESS;
2297       } else if (Status == RETURN_OUT_OF_RESOURCES) {
2298         goto Exit;
2299       } else {
2300         ASSERT_RETURN_ERROR (Status);
2301         Modified = TRUE;
2302       }
2303     }
2304 
2305     if (Modified) {
2306       //
2307       // 2.4. Calculate the Variable MTRR settings based on the Ranges.
2308       //      Buffer Too Small may be returned if the scratch buffer size is insufficient.
2309       //
2310       Status = MtrrLibSetMemoryRanges (
2311                  DefaultType, LShiftU64 (1, (UINTN)HighBitSet64 (MtrrValidBitsMask)), WorkingRanges, WorkingRangeCount,
2312                  Scratch, ScratchSize,
2313                  WorkingVariableMtrr, FirmwareVariableMtrrCount + 1, &WorkingVariableMtrrCount
2314                  );
2315       if (RETURN_ERROR (Status)) {
2316         goto Exit;
2317       }
2318 
2319       //
2320       // 2.5. Remove the [0, 1MB) MTRR if it still exists (not merged with other range)
2321       //
2322       for (Index = 0; Index < WorkingVariableMtrrCount; Index++) {
2323         if (WorkingVariableMtrr[Index].BaseAddress == 0 && WorkingVariableMtrr[Index].Length == SIZE_1MB) {
2324           ASSERT (WorkingVariableMtrr[Index].Type == CacheUncacheable);
2325           WorkingVariableMtrrCount--;
2326           CopyMem (
2327             &WorkingVariableMtrr[Index], &WorkingVariableMtrr[Index + 1],
2328             (WorkingVariableMtrrCount - Index) * sizeof (WorkingVariableMtrr[0])
2329             );
2330           break;
2331         }
2332       }
2333 
2334       if (WorkingVariableMtrrCount > FirmwareVariableMtrrCount) {
2335         Status = RETURN_OUT_OF_RESOURCES;
2336         goto Exit;
2337       }
2338 
2339       //
2340       // 2.6. Merge the WorkingVariableMtrr to OriginalVariableMtrr
2341       //      Make sure least modification is made to OriginalVariableMtrr.
2342       //
2343       MtrrLibMergeVariableMtrr (
2344         OriginalVariableMtrr, OriginalVariableMtrrCount,
2345         WorkingVariableMtrr, WorkingVariableMtrrCount,
2346         VariableSettingModified
2347       );
2348     }
2349   }
2350 
2351   //
2352   // 3. Apply the below-1MB memory attribute settings.
2353   //
2354   // (Value & ~0 | 0) still equals to (Value)
2355   //
2356   ZeroMem (ClearMasks, sizeof (ClearMasks));
2357   ZeroMem (OrMasks, sizeof (OrMasks));
2358   for (Index = 0; Index < RangeCount; Index++) {
2359     if (Ranges[Index].BaseAddress >= BASE_1MB) {
2360       continue;
2361     }
2362 
2363     Status = MtrrLibSetBelow1MBMemoryAttribute (
2364                ClearMasks, OrMasks,
2365                Ranges[Index].BaseAddress, Ranges[Index].Length, Ranges[Index].Type
2366                );
2367     if (RETURN_ERROR (Status)) {
2368       goto Exit;
2369     }
2370   }
2371 
2372   MtrrContextValid = FALSE;
2373   //
2374   // 4. Write fixed MTRRs that have been modified
2375   //
2376   for (Index = 0; Index < ARRAY_SIZE (ClearMasks); Index++) {
2377     if (ClearMasks[Index] != 0) {
2378       if (MtrrSetting != NULL) {
2379         MtrrSetting->Fixed.Mtrr[Index] = (MtrrSetting->Fixed.Mtrr[Index] & ~ClearMasks[Index]) | OrMasks[Index];
2380       } else {
2381         if (!MtrrContextValid) {
2382           MtrrLibPreMtrrChange (&MtrrContext);
2383           MtrrContextValid = TRUE;
2384         }
2385         AsmMsrAndThenOr64 (mMtrrLibFixedMtrrTable[Index].Msr, ~ClearMasks[Index], OrMasks[Index]);
2386       }
2387     }
2388   }
2389 
2390   //
2391   // 5. Write variable MTRRs that have been modified
2392   //
2393   for (Index = 0; Index < OriginalVariableMtrrCount; Index++) {
2394     if (VariableSettingModified[Index]) {
2395       if (OriginalVariableMtrr[Index].Length != 0) {
2396         VariableSetting.Base = (OriginalVariableMtrr[Index].BaseAddress & MtrrValidAddressMask)
2397                              | (UINT8)OriginalVariableMtrr[Index].Type;
2398         VariableSetting.Mask = ((~(OriginalVariableMtrr[Index].Length - 1)) & MtrrValidAddressMask) | BIT11;
2399       } else {
2400         VariableSetting.Base = 0;
2401         VariableSetting.Mask = 0;
2402       }
2403       if (MtrrSetting != NULL) {
2404         CopyMem (&MtrrSetting->Variables.Mtrr[Index], &VariableSetting, sizeof (VariableSetting));
2405       } else {
2406         if (!MtrrContextValid) {
2407           MtrrLibPreMtrrChange (&MtrrContext);
2408           MtrrContextValid = TRUE;
2409         }
2410         AsmWriteMsr64 (
2411           MSR_IA32_MTRR_PHYSBASE0 + (Index << 1),
2412           VariableSetting.Base
2413         );
2414         AsmWriteMsr64 (
2415           MSR_IA32_MTRR_PHYSMASK0 + (Index << 1),
2416           VariableSetting.Mask
2417         );
2418       }
2419     }
2420   }
2421 
2422   if (MtrrSetting != NULL) {
2423     ((MSR_IA32_MTRR_DEF_TYPE_REGISTER *)&MtrrSetting->MtrrDefType)->Bits.E = 1;
2424     ((MSR_IA32_MTRR_DEF_TYPE_REGISTER *)&MtrrSetting->MtrrDefType)->Bits.FE = 1;
2425   } else {
2426     if (MtrrContextValid) {
2427       MtrrLibPostMtrrChange (&MtrrContext);
2428     }
2429   }
2430 
2431 Exit:
2432   DEBUG ((DEBUG_CACHE, "  Result = %r\n", Status));
2433   if (!RETURN_ERROR (Status)) {
2434     MtrrDebugPrintAllMtrrsWorker (MtrrSetting);
2435   }
2436   return Status;
2437 }
2438 
2439 /**
2440   This function attempts to set the attributes into MTRR setting buffer for a memory range.
2441 
2442   @param[in, out]  MtrrSetting  MTRR setting buffer to be set.
2443   @param[in]       BaseAddress  The physical address that is the start address
2444                                 of a memory range.
2445   @param[in]       Length       The size in bytes of the memory range.
2446   @param[in]       Attribute    The bit mask of attributes to set for the
2447                                 memory range.
2448 
2449   @retval RETURN_SUCCESS            The attributes were set for the memory range.
2450   @retval RETURN_INVALID_PARAMETER  Length is zero.
2451   @retval RETURN_UNSUPPORTED        The processor does not support one or more bytes of the
2452                                     memory resource range specified by BaseAddress and Length.
2453   @retval RETURN_UNSUPPORTED        The bit mask of attributes is not support for the memory resource
2454                                     range specified by BaseAddress and Length.
2455   @retval RETURN_ACCESS_DENIED      The attributes for the memory resource range specified by
2456                                     BaseAddress and Length cannot be modified.
2457   @retval RETURN_OUT_OF_RESOURCES   There are not enough system resources to modify the attributes of
2458                                     the memory resource range.
2459                                     Multiple memory range attributes setting by calling this API multiple
2460                                     times may fail with status RETURN_OUT_OF_RESOURCES. It may not mean
2461                                     the number of CPU MTRRs are too small to set such memory attributes.
2462                                     Pass the multiple memory range attributes to one call of
2463                                     MtrrSetMemoryAttributesInMtrrSettings() may succeed.
2464   @retval RETURN_BUFFER_TOO_SMALL   The fixed internal scratch buffer is too small for MTRR calculation.
2465                                     Caller should use MtrrSetMemoryAttributesInMtrrSettings() to specify
2466                                     external scratch buffer.
2467 **/
2468 RETURN_STATUS
2469 EFIAPI
MtrrSetMemoryAttributeInMtrrSettings(IN OUT MTRR_SETTINGS * MtrrSetting,IN PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN MTRR_MEMORY_CACHE_TYPE Attribute)2470 MtrrSetMemoryAttributeInMtrrSettings (
2471   IN OUT MTRR_SETTINGS       *MtrrSetting,
2472   IN PHYSICAL_ADDRESS        BaseAddress,
2473   IN UINT64                  Length,
2474   IN MTRR_MEMORY_CACHE_TYPE  Attribute
2475   )
2476 {
2477   UINT8                      Scratch[SCRATCH_BUFFER_SIZE];
2478   UINTN                      ScratchSize;
2479   MTRR_MEMORY_RANGE          Range;
2480 
2481   Range.BaseAddress = BaseAddress;
2482   Range.Length      = Length;
2483   Range.Type        = Attribute;
2484   ScratchSize = sizeof (Scratch);
2485   return MtrrSetMemoryAttributesInMtrrSettings (MtrrSetting, Scratch, &ScratchSize, &Range, 1);
2486 }
2487 
2488 /**
2489   This function attempts to set the attributes for a memory range.
2490 
2491   @param[in]  BaseAddress        The physical address that is the start
2492                                  address of a memory range.
2493   @param[in]  Length             The size in bytes of the memory range.
2494   @param[in]  Attributes         The bit mask of attributes to set for the
2495                                  memory range.
2496 
2497   @retval RETURN_SUCCESS            The attributes were set for the memory
2498                                     range.
2499   @retval RETURN_INVALID_PARAMETER  Length is zero.
2500   @retval RETURN_UNSUPPORTED        The processor does not support one or
2501                                     more bytes of the memory resource range
2502                                     specified by BaseAddress and Length.
2503   @retval RETURN_UNSUPPORTED        The bit mask of attributes is not support
2504                                     for the memory resource range specified
2505                                     by BaseAddress and Length.
2506   @retval RETURN_ACCESS_DENIED      The attributes for the memory resource
2507                                     range specified by BaseAddress and Length
2508                                     cannot be modified.
2509   @retval RETURN_OUT_OF_RESOURCES   There are not enough system resources to
2510                                     modify the attributes of the memory
2511                                     resource range.
2512                                     Multiple memory range attributes setting by calling this API multiple
2513                                     times may fail with status RETURN_OUT_OF_RESOURCES. It may not mean
2514                                     the number of CPU MTRRs are too small to set such memory attributes.
2515                                     Pass the multiple memory range attributes to one call of
2516                                     MtrrSetMemoryAttributesInMtrrSettings() may succeed.
2517   @retval RETURN_BUFFER_TOO_SMALL   The fixed internal scratch buffer is too small for MTRR calculation.
2518                                     Caller should use MtrrSetMemoryAttributesInMtrrSettings() to specify
2519                                     external scratch buffer.
2520 **/
2521 RETURN_STATUS
2522 EFIAPI
MtrrSetMemoryAttribute(IN PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN MTRR_MEMORY_CACHE_TYPE Attribute)2523 MtrrSetMemoryAttribute (
2524   IN PHYSICAL_ADDRESS        BaseAddress,
2525   IN UINT64                  Length,
2526   IN MTRR_MEMORY_CACHE_TYPE  Attribute
2527   )
2528 {
2529   return MtrrSetMemoryAttributeInMtrrSettings (NULL, BaseAddress, Length, Attribute);
2530 }
2531 
2532 /**
2533   Worker function setting variable MTRRs
2534 
2535   @param[in]  VariableSettings   A buffer to hold variable MTRRs content.
2536 
2537 **/
2538 VOID
MtrrSetVariableMtrrWorker(IN MTRR_VARIABLE_SETTINGS * VariableSettings)2539 MtrrSetVariableMtrrWorker (
2540   IN MTRR_VARIABLE_SETTINGS         *VariableSettings
2541   )
2542 {
2543   UINT32  Index;
2544   UINT32  VariableMtrrCount;
2545 
2546   VariableMtrrCount = GetVariableMtrrCountWorker ();
2547   ASSERT (VariableMtrrCount <= ARRAY_SIZE (VariableSettings->Mtrr));
2548 
2549   for (Index = 0; Index < VariableMtrrCount; Index++) {
2550     AsmWriteMsr64 (
2551       MSR_IA32_MTRR_PHYSBASE0 + (Index << 1),
2552       VariableSettings->Mtrr[Index].Base
2553       );
2554     AsmWriteMsr64 (
2555       MSR_IA32_MTRR_PHYSMASK0 + (Index << 1),
2556       VariableSettings->Mtrr[Index].Mask
2557       );
2558   }
2559 }
2560 
2561 /**
2562   Worker function setting fixed MTRRs
2563 
2564   @param[in]  FixedSettings  A buffer to hold fixed MTRRs content.
2565 
2566 **/
2567 VOID
MtrrSetFixedMtrrWorker(IN MTRR_FIXED_SETTINGS * FixedSettings)2568 MtrrSetFixedMtrrWorker (
2569   IN MTRR_FIXED_SETTINGS          *FixedSettings
2570   )
2571 {
2572   UINT32  Index;
2573 
2574   for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
2575      AsmWriteMsr64 (
2576        mMtrrLibFixedMtrrTable[Index].Msr,
2577        FixedSettings->Mtrr[Index]
2578        );
2579   }
2580 }
2581 
2582 
2583 /**
2584   This function gets the content in all MTRRs (variable and fixed)
2585 
2586   @param[out]  MtrrSetting  A buffer to hold all MTRRs content.
2587 
2588   @retval the pointer of MtrrSetting
2589 
2590 **/
2591 MTRR_SETTINGS *
2592 EFIAPI
MtrrGetAllMtrrs(OUT MTRR_SETTINGS * MtrrSetting)2593 MtrrGetAllMtrrs (
2594   OUT MTRR_SETTINGS                *MtrrSetting
2595   )
2596 {
2597   if (!IsMtrrSupported ()) {
2598     return MtrrSetting;
2599   }
2600 
2601   //
2602   // Get fixed MTRRs
2603   //
2604   MtrrGetFixedMtrrWorker (&MtrrSetting->Fixed);
2605 
2606   //
2607   // Get variable MTRRs
2608   //
2609   MtrrGetVariableMtrrWorker (
2610     NULL,
2611     GetVariableMtrrCountWorker (),
2612     &MtrrSetting->Variables
2613     );
2614 
2615   //
2616   // Get MTRR_DEF_TYPE value
2617   //
2618   MtrrSetting->MtrrDefType = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
2619 
2620   return MtrrSetting;
2621 }
2622 
2623 
2624 /**
2625   This function sets all MTRRs (variable and fixed)
2626 
2627   @param[in]  MtrrSetting  A buffer holding all MTRRs content.
2628 
2629   @retval The pointer of MtrrSetting
2630 
2631 **/
2632 MTRR_SETTINGS *
2633 EFIAPI
MtrrSetAllMtrrs(IN MTRR_SETTINGS * MtrrSetting)2634 MtrrSetAllMtrrs (
2635   IN MTRR_SETTINGS                *MtrrSetting
2636   )
2637 {
2638   MTRR_CONTEXT  MtrrContext;
2639 
2640   if (!IsMtrrSupported ()) {
2641     return MtrrSetting;
2642   }
2643 
2644   MtrrLibPreMtrrChange (&MtrrContext);
2645 
2646   //
2647   // Set fixed MTRRs
2648   //
2649   MtrrSetFixedMtrrWorker (&MtrrSetting->Fixed);
2650 
2651   //
2652   // Set variable MTRRs
2653   //
2654   MtrrSetVariableMtrrWorker (&MtrrSetting->Variables);
2655 
2656   //
2657   // Set MTRR_DEF_TYPE value
2658   //
2659   AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, MtrrSetting->MtrrDefType);
2660 
2661   MtrrLibPostMtrrChangeEnableCache (&MtrrContext);
2662 
2663   return MtrrSetting;
2664 }
2665 
2666 
2667 /**
2668   Checks if MTRR is supported.
2669 
2670   @retval TRUE  MTRR is supported.
2671   @retval FALSE MTRR is not supported.
2672 
2673 **/
2674 BOOLEAN
2675 EFIAPI
IsMtrrSupported(VOID)2676 IsMtrrSupported (
2677   VOID
2678   )
2679 {
2680   CPUID_VERSION_INFO_EDX    Edx;
2681   MSR_IA32_MTRRCAP_REGISTER MtrrCap;
2682 
2683   //
2684   // Check CPUID(1).EDX[12] for MTRR capability
2685   //
2686   AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &Edx.Uint32);
2687   if (Edx.Bits.MTRR == 0) {
2688     return FALSE;
2689   }
2690 
2691   //
2692   // Check number of variable MTRRs and fixed MTRRs existence.
2693   // If number of variable MTRRs is zero, or fixed MTRRs do not
2694   // exist, return false.
2695   //
2696   MtrrCap.Uint64 = AsmReadMsr64 (MSR_IA32_MTRRCAP);
2697   if ((MtrrCap.Bits.VCNT == 0) || (MtrrCap.Bits.FIX == 0)) {
2698     return FALSE;
2699   }
2700   return TRUE;
2701 }
2702 
2703 
2704 /**
2705   Worker function prints all MTRRs for debugging.
2706 
2707   If MtrrSetting is not NULL, print MTRR settings from input MTRR
2708   settings buffer.
2709   If MtrrSetting is NULL, print MTRR settings from MTRRs.
2710 
2711   @param  MtrrSetting    A buffer holding all MTRRs content.
2712 **/
2713 VOID
MtrrDebugPrintAllMtrrsWorker(IN MTRR_SETTINGS * MtrrSetting)2714 MtrrDebugPrintAllMtrrsWorker (
2715   IN MTRR_SETTINGS    *MtrrSetting
2716   )
2717 {
2718   DEBUG_CODE (
2719     MTRR_SETTINGS     LocalMtrrs;
2720     MTRR_SETTINGS     *Mtrrs;
2721     UINTN             Index;
2722     UINTN             RangeCount;
2723     UINT64            MtrrValidBitsMask;
2724     UINT64            MtrrValidAddressMask;
2725     UINT32            VariableMtrrCount;
2726     BOOLEAN           ContainVariableMtrr;
2727     MTRR_MEMORY_RANGE Ranges[
2728       ARRAY_SIZE (mMtrrLibFixedMtrrTable) * sizeof (UINT64) + 2 * ARRAY_SIZE (Mtrrs->Variables.Mtrr) + 1
2729       ];
2730     MTRR_MEMORY_RANGE RawVariableRanges[ARRAY_SIZE (Mtrrs->Variables.Mtrr)];
2731 
2732     if (!IsMtrrSupported ()) {
2733       return;
2734     }
2735 
2736     VariableMtrrCount = GetVariableMtrrCountWorker ();
2737 
2738     if (MtrrSetting != NULL) {
2739       Mtrrs = MtrrSetting;
2740     } else {
2741       MtrrGetAllMtrrs (&LocalMtrrs);
2742       Mtrrs = &LocalMtrrs;
2743     }
2744 
2745     //
2746     // Dump RAW MTRR contents
2747     //
2748     DEBUG ((DEBUG_CACHE, "MTRR Settings:\n"));
2749     DEBUG ((DEBUG_CACHE, "=============\n"));
2750     DEBUG ((DEBUG_CACHE, "MTRR Default Type: %016lx\n", Mtrrs->MtrrDefType));
2751     for (Index = 0; Index < ARRAY_SIZE (mMtrrLibFixedMtrrTable); Index++) {
2752       DEBUG ((DEBUG_CACHE, "Fixed MTRR[%02d]   : %016lx\n", Index, Mtrrs->Fixed.Mtrr[Index]));
2753     }
2754     ContainVariableMtrr = FALSE;
2755     for (Index = 0; Index < VariableMtrrCount; Index++) {
2756       if ((Mtrrs->Variables.Mtrr[Index].Mask & BIT11) == 0) {
2757         //
2758         // If mask is not valid, then do not display range
2759         //
2760         continue;
2761       }
2762       ContainVariableMtrr = TRUE;
2763       DEBUG ((DEBUG_CACHE, "Variable MTRR[%02d]: Base=%016lx Mask=%016lx\n",
2764         Index,
2765         Mtrrs->Variables.Mtrr[Index].Base,
2766         Mtrrs->Variables.Mtrr[Index].Mask
2767         ));
2768     }
2769     if (!ContainVariableMtrr) {
2770       DEBUG ((DEBUG_CACHE, "Variable MTRR    : None.\n"));
2771     }
2772     DEBUG((DEBUG_CACHE, "\n"));
2773 
2774     //
2775     // Dump MTRR setting in ranges
2776     //
2777     DEBUG((DEBUG_CACHE, "Memory Ranges:\n"));
2778     DEBUG((DEBUG_CACHE, "====================================\n"));
2779     MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask);
2780     Ranges[0].BaseAddress = 0;
2781     Ranges[0].Length      = MtrrValidBitsMask + 1;
2782     Ranges[0].Type        = MtrrGetDefaultMemoryTypeWorker (Mtrrs);
2783     RangeCount = 1;
2784 
2785     MtrrLibGetRawVariableRanges (
2786       &Mtrrs->Variables, VariableMtrrCount,
2787       MtrrValidBitsMask, MtrrValidAddressMask, RawVariableRanges
2788       );
2789     MtrrLibApplyVariableMtrrs (
2790       RawVariableRanges, VariableMtrrCount,
2791       Ranges, ARRAY_SIZE (Ranges), &RangeCount
2792       );
2793 
2794     MtrrLibApplyFixedMtrrs (&Mtrrs->Fixed, Ranges, ARRAY_SIZE (Ranges), &RangeCount);
2795 
2796     for (Index = 0; Index < RangeCount; Index++) {
2797       DEBUG ((DEBUG_CACHE, "%a:%016lx-%016lx\n",
2798         mMtrrMemoryCacheTypeShortName[Ranges[Index].Type],
2799         Ranges[Index].BaseAddress, Ranges[Index].BaseAddress + Ranges[Index].Length - 1
2800         ));
2801     }
2802   );
2803 }
2804 
2805 /**
2806   This function prints all MTRRs for debugging.
2807 **/
2808 VOID
2809 EFIAPI
MtrrDebugPrintAllMtrrs(VOID)2810 MtrrDebugPrintAllMtrrs (
2811   VOID
2812   )
2813 {
2814   MtrrDebugPrintAllMtrrsWorker (NULL);
2815 }
2816