+/**\r
+ Update the Ranges array to change the specified range identified by\r
+ BaseAddress and Length to Type.\r
+\r
+ @param Ranges Array holding memory type settings for all memory regions.\r
+ @param Capacity The maximum count of memory ranges the array can hold.\r
+ @param Count Return the new memory range count in the array.\r
+ @param BaseAddress The base address of the memory range to change type.\r
+ @param Length The length of the memory range to change type.\r
+ @param Type The new type of the specified memory range.\r
+\r
+ @retval RETURN_SUCCESS The type of the specified memory range is\r
+ changed successfully.\r
+ @retval RETURN_OUT_OF_RESOURCES The new type set causes the count of memory\r
+ range exceeds capacity.\r
+**/\r
+RETURN_STATUS\r
+MtrrLibSetMemoryType (\r
+ IN MEMORY_RANGE *Ranges,\r
+ IN UINT32 Capacity,\r
+ IN OUT UINT32 *Count,\r
+ IN UINT64 BaseAddress,\r
+ IN UINT64 Length,\r
+ IN MTRR_MEMORY_CACHE_TYPE Type\r
+ )\r
+{\r
+ UINT32 Index;\r
+ UINT64 Limit;\r
+ UINT64 LengthLeft;\r
+ UINT64 LengthRight;\r
+ UINT32 StartIndex;\r
+ UINT32 EndIndex;\r
+ UINT32 DeltaCount;\r
+\r
+ Limit = BaseAddress + Length;\r
+ StartIndex = *Count;\r
+ EndIndex = *Count;\r
+ for (Index = 0; Index < *Count; Index++) {\r
+ if ((StartIndex == *Count) &&\r
+ (Ranges[Index].BaseAddress <= BaseAddress) &&\r
+ (BaseAddress < Ranges[Index].BaseAddress + Ranges[Index].Length)) {\r
+ StartIndex = Index;\r
+ LengthLeft = BaseAddress - Ranges[Index].BaseAddress;\r
+ }\r
+\r
+ if ((EndIndex == *Count) &&\r
+ (Ranges[Index].BaseAddress < Limit) &&\r
+ (Limit <= Ranges[Index].BaseAddress + Ranges[Index].Length)) {\r
+ EndIndex = Index;\r
+ LengthRight = Ranges[Index].BaseAddress + Ranges[Index].Length - Limit;\r
+ break;\r
+ }\r
+ }\r
+\r
+ ASSERT (StartIndex != *Count && EndIndex != *Count);\r
+ if (StartIndex == EndIndex && Ranges[StartIndex].Type == Type) {\r
+ return RETURN_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // The type change may cause merging with previous range or next range.\r
+ // Update the StartIndex, EndIndex, BaseAddress, Length so that following\r
+ // logic doesn't need to consider merging.\r
+ //\r
+ if (StartIndex != 0) {\r
+ if (LengthLeft == 0 && Ranges[StartIndex - 1].Type == Type) {\r
+ StartIndex--;\r
+ Length += Ranges[StartIndex].Length;\r
+ BaseAddress -= Ranges[StartIndex].Length;\r
+ }\r
+ }\r
+ if (EndIndex != (*Count) - 1) {\r
+ if (LengthRight == 0 && Ranges[EndIndex + 1].Type == Type) {\r
+ EndIndex++;\r
+ Length += Ranges[EndIndex].Length;\r
+ }\r
+ }\r
+\r
+ //\r
+ // |- 0 -|- 1 -|- 2 -|- 3 -| StartIndex EndIndex DeltaCount Count (Count = 4)\r
+ // |++++++++++++++++++| 0 3 1=3-0-2 3\r
+ // |+++++++| 0 1 -1=1-0-2 5\r
+ // |+| 0 0 -2=0-0-2 6\r
+ // |+++| 0 0 -1=0-0-2+1 5\r
+ //\r
+ //\r
+ DeltaCount = EndIndex - StartIndex - 2;\r
+ if (LengthLeft == 0) {\r
+ DeltaCount++;\r
+ }\r
+ if (LengthRight == 0) {\r
+ DeltaCount++;\r
+ }\r
+ if (*Count - DeltaCount > Capacity) {\r
+ return RETURN_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Reserve (-DeltaCount) space\r
+ //\r
+ CopyMem (&Ranges[EndIndex + 1 - DeltaCount], &Ranges[EndIndex + 1], (*Count - EndIndex - 1) * sizeof (Ranges[0]));\r
+ *Count -= DeltaCount;\r
+\r
+ if (LengthLeft != 0) {\r
+ Ranges[StartIndex].Length = LengthLeft;\r
+ StartIndex++;\r
+ }\r
+ if (LengthRight != 0) {\r
+ Ranges[EndIndex - DeltaCount].BaseAddress = BaseAddress + Length;\r
+ Ranges[EndIndex - DeltaCount].Length = LengthRight;\r
+ Ranges[EndIndex - DeltaCount].Type = Ranges[EndIndex].Type;\r
+ }\r
+ Ranges[StartIndex].BaseAddress = BaseAddress;\r
+ Ranges[StartIndex].Length = Length;\r
+ Ranges[StartIndex].Type = Type;\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+/**\r
+ Allocate one or more variable MTRR to cover the range identified by\r
+ BaseAddress and Length.\r
+\r
+ @param Ranges Memory range array holding the memory type\r
+ settings for all memory address.\r
+ @param RangeCount Count of memory ranges.\r
+ @param VariableMtrr Variable MTRR array.\r
+ @param VariableMtrrCapacity Capacity of variable MTRR array.\r
+ @param VariableMtrrCount Count of variable MTRR.\r
+ @param BaseAddress Base address of the memory range.\r
+ @param Length Length of the memory range.\r
+ @param Type MTRR type of the memory range.\r
+ @param Alignment0 Alignment of 0.\r
+\r
+ @retval RETURN_SUCCESS Variable MTRRs are allocated successfully.\r
+ @retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.\r
+**/\r
+RETURN_STATUS\r
+MtrrLibSetMemoryAttributeInVariableMtrr (\r
+ IN CONST MEMORY_RANGE *Ranges,\r
+ IN UINT32 RangeCount,\r
+ IN OUT VARIABLE_MTRR *VariableMtrr,\r
+ IN UINT32 VariableMtrrCapacity,\r
+ IN OUT UINT32 *VariableMtrrCount,\r
+ IN UINT64 BaseAddress,\r
+ IN UINT64 Length,\r
+ IN MTRR_MEMORY_CACHE_TYPE Type,\r
+ IN UINT64 Alignment0\r
+ );\r
+\r
+/**\r
+ Allocate one or more variable MTRR to cover the range identified by\r
+ BaseAddress and Length.\r
+\r
+ The routine recursively calls MtrrLibSetMemoryAttributeInVariableMtrr()\r
+ to allocate variable MTRRs when the range contains several sub-ranges\r
+ with different attributes.\r
+\r
+ @param Ranges Memory range array holding the memory type\r
+ settings for all memory address.\r
+ @param RangeCount Count of memory ranges.\r
+ @param VariableMtrr Variable MTRR array.\r
+ @param VariableMtrrCapacity Capacity of variable MTRR array.\r
+ @param VariableMtrrCount Count of variable MTRR.\r
+ @param BaseAddress Base address of the memory range.\r
+ @param Length Length of the memory range.\r
+ @param Type MTRR type of the range.\r
+ If it's CacheInvalid, the memory range may\r
+ contains several sub-ranges with different\r
+ attributes.\r
+ @param Alignment0 Alignment of 0.\r
+\r
+ @retval RETURN_SUCCESS Variable MTRRs are allocated successfully.\r
+ @retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.\r
+**/\r
+RETURN_STATUS\r
+MtrrLibAddVariableMtrr (\r
+ IN CONST MEMORY_RANGE *Ranges,\r
+ IN UINT32 RangeCount,\r
+ IN OUT VARIABLE_MTRR *VariableMtrr,\r
+ IN UINT32 VariableMtrrCapacity,\r
+ IN OUT UINT32 *VariableMtrrCount,\r
+ IN PHYSICAL_ADDRESS BaseAddress,\r
+ IN UINT64 Length,\r
+ IN MTRR_MEMORY_CACHE_TYPE Type,\r
+ IN UINT64 Alignment0\r
+)\r
+{\r
+ RETURN_STATUS Status;\r
+ UINT32 Index;\r
+ UINT64 SubLength;\r
+\r
+ MTRR_LIB_ASSERT_ALIGNED (BaseAddress, Length);\r
+ if (Type == CacheInvalid) {\r
+ for (Index = 0; Index < RangeCount; Index++) {\r
+ if (Ranges[Index].BaseAddress <= BaseAddress && BaseAddress < Ranges[Index].BaseAddress + Ranges[Index].Length) {\r
+\r
+ //\r
+ // Because the Length may not be aligned to BaseAddress, below code calls\r
+ // MtrrLibSetMemoryAttributeInVariableMtrr() instead of itself.\r
+ // MtrrLibSetMemoryAttributeInVariableMtrr() splits the range to several\r
+ // aligned ranges.\r
+ //\r
+ if (Ranges[Index].BaseAddress + Ranges[Index].Length >= BaseAddress + Length) {\r
+ return MtrrLibSetMemoryAttributeInVariableMtrr (\r
+ Ranges, RangeCount, VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,\r
+ BaseAddress, Length, Ranges[Index].Type, Alignment0\r
+ );\r
+ } else {\r
+ SubLength = Ranges[Index].BaseAddress + Ranges[Index].Length - BaseAddress;\r
+ Status = MtrrLibSetMemoryAttributeInVariableMtrr (\r
+ Ranges, RangeCount, VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,\r
+ BaseAddress, SubLength, Ranges[Index].Type, Alignment0\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ BaseAddress += SubLength;\r
+ Length -= SubLength;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Because memory ranges cover all the memory addresses, it's impossible to be here.\r
+ //\r
+ ASSERT (FALSE);\r
+ return RETURN_DEVICE_ERROR;\r
+ } else {\r
+ for (Index = 0; Index < *VariableMtrrCount; Index++) {\r
+ if (VariableMtrr[Index].BaseAddress == BaseAddress && VariableMtrr[Index].Length == Length) {\r
+ ASSERT (VariableMtrr[Index].Type == Type);\r
+ break;\r
+ }\r
+ }\r
+ if (Index == *VariableMtrrCount) {\r
+ if (*VariableMtrrCount == VariableMtrrCapacity) {\r
+ return RETURN_OUT_OF_RESOURCES;\r
+ }\r
+ VariableMtrr[Index].BaseAddress = BaseAddress;\r
+ VariableMtrr[Index].Length = Length;\r
+ VariableMtrr[Index].Type = Type;\r
+ VariableMtrr[Index].Valid = TRUE;\r
+ VariableMtrr[Index].Used = TRUE;\r
+ (*VariableMtrrCount)++;\r
+ }\r
+ return RETURN_SUCCESS;\r
+ }\r
+}\r
+\r
+/**\r
+ Allocate one or more variable MTRR to cover the range identified by\r
+ BaseAddress and Length.\r
+\r
+ @param Ranges Memory range array holding the memory type\r
+ settings for all memory address.\r
+ @param RangeCount Count of memory ranges.\r
+ @param VariableMtrr Variable MTRR array.\r
+ @param VariableMtrrCapacity Capacity of variable MTRR array.\r
+ @param VariableMtrrCount Count of variable MTRR.\r
+ @param BaseAddress Base address of the memory range.\r
+ @param Length Length of the memory range.\r
+ @param Type MTRR type of the memory range.\r
+ @param Alignment0 Alignment of 0.\r
+\r
+ @retval RETURN_SUCCESS Variable MTRRs are allocated successfully.\r
+ @retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.\r
+**/\r
+RETURN_STATUS\r
+MtrrLibSetMemoryAttributeInVariableMtrr (\r
+ IN CONST MEMORY_RANGE *Ranges,\r
+ IN UINT32 RangeCount,\r
+ IN OUT VARIABLE_MTRR *VariableMtrr,\r
+ IN UINT32 VariableMtrrCapacity,\r
+ IN OUT UINT32 *VariableMtrrCount,\r
+ IN UINT64 BaseAddress,\r
+ IN UINT64 Length,\r
+ IN MTRR_MEMORY_CACHE_TYPE Type,\r
+ IN UINT64 Alignment0\r
+)\r
+{\r
+ UINT64 Alignment;\r
+ UINT32 MtrrNumber;\r
+ UINT32 SubtractiveLeft;\r
+ UINT32 SubtractiveRight;\r
+ BOOLEAN UseLeastAlignment;\r
+\r
+ MtrrNumber = MtrrLibGetMtrrNumber (Ranges, RangeCount, VariableMtrr, *VariableMtrrCount,\r
+ BaseAddress, Length, Type, Alignment0, &SubtractiveLeft, &SubtractiveRight);\r
+\r
+ if (MtrrNumber + *VariableMtrrCount > VariableMtrrCapacity) {\r
+ return RETURN_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ while (SubtractiveLeft-- != 0) {\r
+ Alignment = MtrrLibLeastAlignment (BaseAddress, Alignment0);\r
+ ASSERT (Alignment <= Length);\r
+\r
+ MtrrLibAddVariableMtrr (Ranges, RangeCount, VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,\r
+ BaseAddress - Alignment, Alignment, CacheInvalid, Alignment0);\r
+ BaseAddress -= Alignment;\r
+ Length += Alignment;\r
+ }\r
+\r
+ while (Length != 0) {\r
+ Alignment = MtrrLibLeastAlignment (BaseAddress, Alignment0);\r
+ if (Alignment > Length) {\r
+ break;\r
+ }\r
+ MtrrLibAddVariableMtrr (NULL, 0, VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,\r
+ BaseAddress, Alignment, Type, Alignment0);\r
+ BaseAddress += Alignment;\r
+ Length -= Alignment;\r
+ }\r
+\r
+ while (SubtractiveRight-- != 0) {\r
+ Alignment = MtrrLibLeastAlignment (BaseAddress + Length, Alignment0);\r
+ MtrrLibAddVariableMtrr (Ranges, RangeCount, VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,\r
+ BaseAddress + Length, Alignment, CacheInvalid, Alignment0);\r
+ Length += Alignment;\r
+ }\r
+\r
+ UseLeastAlignment = TRUE;\r
+ while (Length != 0) {\r
+ if (UseLeastAlignment) {\r
+ Alignment = MtrrLibLeastAlignment (BaseAddress, Alignment0);\r
+ if (Alignment > Length) {\r
+ UseLeastAlignment = FALSE;\r
+ }\r
+ }\r
+\r
+ if (!UseLeastAlignment) {\r
+ Alignment = GetPowerOfTwo64 (Length);\r
+ }\r
+\r
+ MtrrLibAddVariableMtrr (NULL, 0, VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,\r
+ BaseAddress, Alignment, Type, Alignment0);\r
+ BaseAddress += Alignment;\r
+ Length -= Alignment;\r
+ }\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+/**\r
+ Return an array of memory ranges holding memory type settings for all memory\r
+ address.\r
+\r
+ @param DefaultType The default memory type.\r
+ @param TotalLength The total length of the memory.\r
+ @param VariableMtrr The variable MTRR array.\r
+ @param VariableMtrrCount The count of variable MTRRs.\r
+ @param Ranges Return the memory range array holding memory type\r
+ settings for all memory address.\r
+ @param RangeCapacity The capacity of memory range array.\r
+ @param RangeCount Return the count of memory range.\r
+\r
+ @retval RETURN_SUCCESS The memory range array is returned successfully.\r
+ @retval RETURN_OUT_OF_RESOURCES The count of memory ranges exceeds capacity.\r
+**/\r
+RETURN_STATUS\r
+MtrrLibGetMemoryTypes (\r
+ IN MTRR_MEMORY_CACHE_TYPE DefaultType,\r
+ IN UINT64 TotalLength,\r
+ IN CONST VARIABLE_MTRR *VariableMtrr,\r
+ IN UINT32 VariableMtrrCount,\r
+ OUT MEMORY_RANGE *Ranges,\r
+ IN UINT32 RangeCapacity,\r
+ OUT UINT32 *RangeCount\r
+)\r
+{\r
+ RETURN_STATUS Status;\r
+ UINTN Index;\r
+\r
+ //\r
+ // WT > WB\r
+ // UC > *\r
+ // UC > * (except WB, UC) > WB\r
+ //\r
+\r
+ //\r
+ // 0. Set whole range as DefaultType\r
+ //\r
+ *RangeCount = 1;\r
+ Ranges[0].BaseAddress = 0;\r
+ Ranges[0].Length = TotalLength;\r
+ Ranges[0].Type = DefaultType;\r
+\r
+ //\r
+ // 1. Set WB\r
+ //\r
+ for (Index = 0; Index < VariableMtrrCount; Index++) {\r
+ if (VariableMtrr[Index].Valid && VariableMtrr[Index].Type == CacheWriteBack) {\r
+ Status = MtrrLibSetMemoryType (\r
+ Ranges, RangeCapacity, RangeCount,\r
+ VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, (MTRR_MEMORY_CACHE_TYPE) VariableMtrr[Index].Type\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // 2. Set other types than WB or UC\r
+ //\r
+ for (Index = 0; Index < VariableMtrrCount; Index++) {\r
+ if (VariableMtrr[Index].Valid && VariableMtrr[Index].Type != CacheWriteBack && VariableMtrr[Index].Type != CacheUncacheable) {\r
+ Status = MtrrLibSetMemoryType (\r
+ Ranges, RangeCapacity, RangeCount,\r
+ VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, (MTRR_MEMORY_CACHE_TYPE) VariableMtrr[Index].Type\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // 3. Set UC\r
+ //\r
+ for (Index = 0; Index < VariableMtrrCount; Index++) {\r
+ if (VariableMtrr[Index].Valid && VariableMtrr[Index].Type == CacheUncacheable) {\r
+ Status = MtrrLibSetMemoryType (\r
+ Ranges, RangeCapacity, RangeCount,\r
+ VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, (MTRR_MEMORY_CACHE_TYPE) VariableMtrr[Index].Type\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ }\r
+ return RETURN_SUCCESS;\r
+}\r