+ Mtrrs[*MtrrCount].BaseAddress = BaseAddress;\r
+ Mtrrs[*MtrrCount].Length = Length;\r
+ Mtrrs[*MtrrCount].Type = Type;\r
+ (*MtrrCount)++;\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+/**\r
+ Return the memory type that has the least precedence.\r
+\r
+ @param TypeBits Bit mask of memory type.\r
+\r
+ @retval Memory type that has the least precedence.\r
+**/\r
+MTRR_MEMORY_CACHE_TYPE\r
+MtrrLibLowestType (\r
+ IN UINT8 TypeBits\r
+)\r
+{\r
+ INT8 Type;\r
+\r
+ ASSERT (TypeBits != 0);\r
+ for (Type = 7; (INT8)TypeBits > 0; Type--, TypeBits <<= 1);\r
+ return (MTRR_MEMORY_CACHE_TYPE)Type;\r
+}\r
+\r
+/**\r
+ Return TRUE when the Operand is exactly power of 2.\r
+\r
+ @retval TRUE Operand is exactly power of 2.\r
+ @retval FALSE Operand is not power of 2.\r
+**/\r
+BOOLEAN\r
+MtrrLibIsPowerOfTwo (\r
+ IN UINT64 Operand\r
+)\r
+{\r
+ ASSERT (Operand != 0);\r
+ return (BOOLEAN) ((Operand & (Operand - 1)) == 0);\r
+}\r
+\r
+/**\r
+ Calculate the subtractive path from vertex Start to Stop.\r
+\r
+ @param DefaultType Default memory type.\r
+ @param A0 Alignment to use when base address is 0.\r
+ @param Ranges Array holding memory type settings for all memory regions.\r
+ @param RangeCount The count of memory ranges the array holds.\r
+ @param VertexCount The count of vertices in the graph.\r
+ @param Vertices Array holding all vertices.\r
+ @param Weight 2-dimention array holding weights between vertices.\r
+ @param Start Start vertex.\r
+ @param Stop Stop vertex.\r
+ @param Types Type bit mask of memory range from Start to Stop.\r
+ @param TypeCount Number of different memory types from Start to Stop.\r
+ @param Mtrrs Array holding all MTRR settings.\r
+ @param MtrrCapacity Capacity of the MTRR array.\r
+ @param MtrrCount The count of MTRR settings in array.\r
+\r
+ @retval RETURN_SUCCESS The subtractive path is calculated successfully.\r
+ @retval RETURN_OUT_OF_RESOURCES The MTRR setting array is full.\r
+\r
+**/\r
+RETURN_STATUS\r
+MtrrLibCalculateSubtractivePath (\r
+ IN MTRR_MEMORY_CACHE_TYPE DefaultType,\r
+ IN UINT64 A0,\r
+ IN CONST MTRR_MEMORY_RANGE *Ranges,\r
+ IN UINTN RangeCount,\r
+ IN UINT16 VertexCount,\r
+ IN MTRR_LIB_ADDRESS *Vertices,\r
+ IN OUT UINT8 *Weight,\r
+ IN UINT16 Start,\r
+ IN UINT16 Stop,\r
+ IN UINT8 Types,\r
+ IN UINT8 TypeCount,\r
+ IN OUT MTRR_MEMORY_RANGE *Mtrrs, OPTIONAL\r
+ IN UINT32 MtrrCapacity, OPTIONAL\r
+ IN OUT UINT32 *MtrrCount OPTIONAL\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+ UINT64 Base;\r
+ UINT64 Length;\r
+ UINT8 PrecedentTypes;\r
+ UINTN Index;\r
+ UINT64 HBase;\r
+ UINT64 HLength;\r
+ UINT64 SubLength;\r
+ UINT16 SubStart;\r
+ UINT16 SubStop;\r
+ UINT16 Cur;\r
+ UINT16 Pre;\r
+ MTRR_MEMORY_CACHE_TYPE LowestType;\r
+ MTRR_MEMORY_CACHE_TYPE LowestPrecedentType;\r
+\r
+ Base = Vertices[Start].Address;\r
+ Length = Vertices[Stop].Address - Base;\r
+\r
+ LowestType = MtrrLibLowestType (Types);\r
+\r
+ //\r
+ // Clear the lowest type (highest bit) to get the precedent types\r
+ //\r
+ PrecedentTypes = ~(1 << LowestType) & Types;\r
+ LowestPrecedentType = MtrrLibLowestType (PrecedentTypes);\r
+\r
+ if (Mtrrs == NULL) {\r
+ Weight[M(Start, Stop)] = ((LowestType == DefaultType) ? 0 : 1);\r
+ Weight[O(Start, Stop)] = ((LowestType == DefaultType) ? 1 : 0);\r
+ }\r
+\r
+ // Add all high level ranges\r
+ HBase = MAX_UINT64;\r
+ HLength = 0;\r
+ for (Index = 0; Index < RangeCount; Index++) {\r
+ if (Length == 0) {\r
+ break;\r
+ }\r
+ if ((Base < Ranges[Index].BaseAddress) || (Ranges[Index].BaseAddress + Ranges[Index].Length <= Base)) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Base is in the Range[Index]\r
+ //\r
+ if (Base + Length > Ranges[Index].BaseAddress + Ranges[Index].Length) {\r
+ SubLength = Ranges[Index].BaseAddress + Ranges[Index].Length - Base;\r
+ } else {\r
+ SubLength = Length;\r
+ }\r
+ if (((1 << Ranges[Index].Type) & PrecedentTypes) != 0) {\r
+ //\r
+ // Meet a range whose types take precedence.\r
+ // Update the [HBase, HBase + HLength) to include the range,\r
+ // [HBase, HBase + HLength) may contain sub ranges with 2 different types, and both take precedence.\r
+ //\r
+ if (HBase == MAX_UINT64) {\r
+ HBase = Base;\r
+ }\r
+ HLength += SubLength;\r
+ }\r
+\r
+ Base += SubLength;\r
+ Length -= SubLength;\r
+\r
+ if (HLength == 0) {\r
+ continue;\r
+ }\r
+\r
+ if ((Ranges[Index].Type == LowestType) || (Length == 0)) { // meet low type or end\r
+\r
+ //\r
+ // Add the MTRRs for each high priority type range\r
+ // the range[HBase, HBase + HLength) contains only two types.\r
+ // We might use positive or subtractive, depending on which way uses less MTRR\r
+ //\r
+ for (SubStart = Start; SubStart <= Stop; SubStart++) {\r
+ if (Vertices[SubStart].Address == HBase) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ for (SubStop = SubStart; SubStop <= Stop; SubStop++) {\r
+ if (Vertices[SubStop].Address == HBase + HLength) {\r
+ break;\r
+ }\r
+ }\r
+ ASSERT (Vertices[SubStart].Address == HBase);\r
+ ASSERT (Vertices[SubStop].Address == HBase + HLength);\r
+\r
+ if ((TypeCount == 2) || (SubStart == SubStop - 1)) {\r
+ //\r
+ // add subtractive MTRRs for [HBase, HBase + HLength)\r
+ // [HBase, HBase + HLength) contains only one type.\r
+ // while - loop is to split the range to MTRR - compliant aligned range.\r
+ //\r
+ if (Mtrrs == NULL) {\r
+ Weight[M (Start, Stop)] += (UINT8)(SubStop - SubStart);\r
+ } else {\r
+ while (SubStart != SubStop) {\r
+ Status = MtrrLibAppendVariableMtrr (\r
+ Mtrrs, MtrrCapacity, MtrrCount,\r
+ Vertices[SubStart].Address, Vertices[SubStart].Length, Vertices[SubStart].Type\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ SubStart++;\r
+ }\r
+ }\r
+ } else {\r
+ ASSERT (TypeCount == 3);\r
+ MtrrLibCalculateLeastMtrrs (VertexCount, Vertices, Weight, SubStart, SubStop, TRUE);\r
+\r
+ if (Mtrrs == NULL) {\r
+ Weight[M (Start, Stop)] += Vertices[SubStop].Weight;\r
+ } else {\r
+ // When we need to collect the optimal path from SubStart to SubStop\r
+ while (SubStop != SubStart) {\r
+ Cur = SubStop;\r
+ Pre = Vertices[Cur].Previous;\r
+ SubStop = Pre;\r
+\r
+ if (Weight[M (Pre, Cur)] + Weight[O (Pre, Cur)] != 0) {\r
+ Status = MtrrLibAppendVariableMtrr (\r
+ Mtrrs, MtrrCapacity, MtrrCount,\r
+ Vertices[Pre].Address, Vertices[Cur].Address - Vertices[Pre].Address,\r
+ (Pre != Cur - 1) ? LowestPrecedentType : Vertices[Pre].Type\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ if (Pre != Cur - 1) {\r
+ Status = MtrrLibCalculateSubtractivePath (\r
+ DefaultType, A0,\r
+ Ranges, RangeCount,\r
+ VertexCount, Vertices, Weight,\r
+ Pre, Cur, PrecedentTypes, 2,\r
+ Mtrrs, MtrrCapacity, MtrrCount\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+ //\r
+ // Reset HBase, HLength\r
+ //\r
+ HBase = MAX_UINT64;\r
+ HLength = 0;\r
+ }\r
+ }\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+/**\r
+ Calculate MTRR settings to cover the specified memory ranges.\r
+\r
+ @param DefaultType Default memory type.\r
+ @param A0 Alignment to use when base address is 0.\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 Scratch A temporary scratch buffer that is used to perform the calculation.\r
+ This is an optional parameter that may be NULL.\r
+ @param ScratchSize Pointer to the size in bytes of the scratch buffer.\r
+ It may be updated to the actual required size when the calculation\r
+ needs more scratch buffer.\r
+ @param Mtrrs Array holding all MTRR settings.\r
+ @param MtrrCapacity Capacity of the MTRR array.\r
+ @param MtrrCount The count of MTRR settings in array.\r
+\r
+ @retval RETURN_SUCCESS Variable MTRRs are allocated successfully.\r
+ @retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.\r
+ @retval RETURN_BUFFER_TOO_SMALL The scratch buffer is too small for MTRR calculation.\r
+**/\r
+RETURN_STATUS\r
+MtrrLibCalculateMtrrs (\r
+ IN MTRR_MEMORY_CACHE_TYPE DefaultType,\r
+ IN UINT64 A0,\r
+ IN CONST MTRR_MEMORY_RANGE *Ranges,\r
+ IN UINTN RangeCount,\r
+ IN VOID *Scratch,\r
+ IN OUT UINTN *ScratchSize,\r
+ IN OUT MTRR_MEMORY_RANGE *Mtrrs,\r
+ IN UINT32 MtrrCapacity,\r
+ IN OUT UINT32 *MtrrCount\r
+ )\r
+{\r
+ UINT64 Base0;\r
+ UINT64 Base1;\r
+ UINTN Index;\r
+ UINT64 Base;\r
+ UINT64 Length;\r
+ UINT64 Alignment;\r
+ UINT64 SubLength;\r
+ MTRR_LIB_ADDRESS *Vertices;\r
+ UINT8 *Weight;\r
+ UINT32 VertexIndex;\r
+ UINT32 VertexCount;\r
+ UINTN RequiredScratchSize;\r
+ UINT8 TypeCount;\r
+ UINT16 Start;\r
+ UINT16 Stop;\r
+ UINT8 Type;\r
+ RETURN_STATUS Status;\r
+\r
+ Base0 = Ranges[0].BaseAddress;\r
+ Base1 = Ranges[RangeCount - 1].BaseAddress + Ranges[RangeCount - 1].Length;\r
+ MTRR_LIB_ASSERT_ALIGNED (Base0, Base1 - Base0);\r
+\r
+ //\r
+ // Count the number of vertices.\r
+ //\r
+ Vertices = (MTRR_LIB_ADDRESS*)Scratch;\r
+ for (VertexIndex = 0, Index = 0; Index < RangeCount; Index++) {\r
+ Base = Ranges[Index].BaseAddress;\r
+ Length = Ranges[Index].Length;\r
+ while (Length != 0) {\r
+ Alignment = MtrrLibBiggestAlignment (Base, A0);\r
+ SubLength = Alignment;\r
+ if (SubLength > Length) {\r
+ SubLength = GetPowerOfTwo64 (Length);\r
+ }\r
+ if (VertexIndex < *ScratchSize / sizeof (*Vertices)) {\r
+ Vertices[VertexIndex].Address = Base;\r
+ Vertices[VertexIndex].Alignment = Alignment;\r
+ Vertices[VertexIndex].Type = Ranges[Index].Type;\r
+ Vertices[VertexIndex].Length = SubLength;\r
+ }\r
+ Base += SubLength;\r
+ Length -= SubLength;\r
+ VertexIndex++;\r
+ }\r
+ }\r
+ //\r
+ // Vertices[VertexIndex] = Base1, so whole vertex count is (VertexIndex + 1).\r
+ //\r
+ VertexCount = VertexIndex + 1;\r
+ DEBUG ((\r
+ DEBUG_CACHE, " Count of vertices (%016llx - %016llx) = %d\n",\r
+ Ranges[0].BaseAddress, Ranges[RangeCount - 1].BaseAddress + Ranges[RangeCount - 1].Length, VertexCount\r
+ ));\r
+ ASSERT (VertexCount < MAX_UINT16);\r
+\r
+ RequiredScratchSize = VertexCount * sizeof (*Vertices) + VertexCount * VertexCount * sizeof (*Weight);\r
+ if (*ScratchSize < RequiredScratchSize) {\r
+ *ScratchSize = RequiredScratchSize;\r
+ return RETURN_BUFFER_TOO_SMALL;\r
+ }\r
+ Vertices[VertexCount - 1].Address = Base1;\r
+\r
+ Weight = (UINT8 *) &Vertices[VertexCount];\r
+ for (VertexIndex = 0; VertexIndex < VertexCount; VertexIndex++) {\r
+ //\r
+ // Set optional weight between vertices and self->self to 0\r
+ //\r
+ SetMem (&Weight[M(VertexIndex, 0)], VertexIndex + 1, 0);\r
+ //\r
+ // Set mandatory weight between vertices to MAX_WEIGHT\r
+ //\r
+ SetMem (&Weight[M (VertexIndex, VertexIndex + 1)], VertexCount - VertexIndex - 1, MAX_WEIGHT);\r
+\r
+ // Final result looks like:\r
+ // 00 FF FF FF\r
+ // 00 00 FF FF\r
+ // 00 00 00 FF\r
+ // 00 00 00 00\r
+ }\r
+\r
+ //\r
+ // Set mandatory weight and optional weight for adjacent vertices\r
+ //\r
+ for (VertexIndex = 0; VertexIndex < VertexCount - 1; VertexIndex++) {\r
+ if (Vertices[VertexIndex].Type != DefaultType) {\r
+ Weight[M (VertexIndex, VertexIndex + 1)] = 1;\r
+ Weight[O (VertexIndex, VertexIndex + 1)] = 0;\r
+ } else {\r
+ Weight[M (VertexIndex, VertexIndex + 1)] = 0;\r
+ Weight[O (VertexIndex, VertexIndex + 1)] = 1;\r
+ }\r
+ }\r
+\r
+ for (TypeCount = 2; TypeCount <= 3; TypeCount++) {\r
+ for (Start = 0; Start < VertexCount; Start++) {\r
+ for (Stop = Start + 2; Stop < VertexCount; Stop++) {\r
+ ASSERT (Vertices[Stop].Address > Vertices[Start].Address);\r
+ Length = Vertices[Stop].Address - Vertices[Start].Address;\r
+ if (Length > Vertices[Start].Alignment) {\r
+ //\r
+ // Pickup a new Start when [Start, Stop) cannot be described by one MTRR.\r
+ //\r
+ break;\r
+ }\r
+ if ((Weight[M(Start, Stop)] == MAX_WEIGHT) && MtrrLibIsPowerOfTwo (Length)) {\r
+ if (MtrrLibGetNumberOfTypes (\r
+ Ranges, RangeCount, Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, &Type\r
+ ) == TypeCount) {\r
+ //\r
+ // Update the Weight[Start, Stop] using subtractive path.\r
+ //\r
+ MtrrLibCalculateSubtractivePath (\r
+ DefaultType, A0,\r
+ Ranges, RangeCount,\r
+ (UINT16)VertexCount, Vertices, Weight,\r
+ Start, Stop, Type, TypeCount,\r
+ NULL, 0, NULL\r
+ );\r
+ } else if (TypeCount == 2) {\r
+ //\r
+ // Pick up a new Start when we expect 2-type range, but 3-type range is met.\r
+ // Because no matter how Stop is increased, we always meet 3-type range.\r
+ //\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ Status = RETURN_SUCCESS;\r
+ MtrrLibCalculateLeastMtrrs ((UINT16) VertexCount, Vertices, Weight, 0, (UINT16) VertexCount - 1, FALSE);\r
+ Stop = (UINT16) VertexCount - 1;\r
+ while (Stop != 0) {\r
+ Start = Vertices[Stop].Previous;\r
+ TypeCount = MAX_UINT8;\r
+ Type = 0;\r
+ if (Weight[M(Start, Stop)] != 0) {\r
+ TypeCount = MtrrLibGetNumberOfTypes (Ranges, RangeCount, Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, &Type);\r
+ Status = MtrrLibAppendVariableMtrr (\r
+ Mtrrs, MtrrCapacity, MtrrCount,\r
+ Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address,\r
+ MtrrLibLowestType (Type)\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Start != Stop - 1) {\r
+ //\r
+ // substractive path\r
+ //\r
+ if (TypeCount == MAX_UINT8) {\r
+ TypeCount = MtrrLibGetNumberOfTypes (\r
+ Ranges, RangeCount, Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, &Type\r
+ );\r
+ }\r
+ Status = MtrrLibCalculateSubtractivePath (\r
+ DefaultType, A0,\r
+ Ranges, RangeCount,\r
+ (UINT16) VertexCount, Vertices, Weight, Start, Stop,\r
+ Type, TypeCount,\r
+ Mtrrs, MtrrCapacity, MtrrCount\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ break;\r
+ }\r
+ }\r
+ Stop = Start;\r
+ }\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Apply the fixed MTRR settings to memory range array.\r
+\r
+ @param Fixed The fixed MTRR settings.\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
+MtrrLibApplyFixedMtrrs (\r
+ IN MTRR_FIXED_SETTINGS *Fixed,\r
+ IN OUT MTRR_MEMORY_RANGE *Ranges,\r
+ IN UINTN RangeCapacity,\r
+ IN OUT UINTN *RangeCount\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+ UINTN MsrIndex;\r
+ UINTN Index;\r
+ MTRR_MEMORY_CACHE_TYPE MemoryType;\r
+ UINT64 Base;\r
+\r
+ Base = 0;\r
+ for (MsrIndex = 0; MsrIndex < ARRAY_SIZE (mMtrrLibFixedMtrrTable); MsrIndex++) {\r
+ ASSERT (Base == mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress);\r
+ for (Index = 0; Index < sizeof (UINT64); Index++) {\r
+ MemoryType = (MTRR_MEMORY_CACHE_TYPE)((UINT8 *)(&Fixed->Mtrr[MsrIndex]))[Index];\r
+ Status = MtrrLibSetMemoryType (\r
+ Ranges, RangeCapacity, RangeCount, Base, mMtrrLibFixedMtrrTable[MsrIndex].Length, MemoryType\r
+ );\r
+ if (Status == RETURN_OUT_OF_RESOURCES) {\r
+ return Status;\r
+ }\r
+ Base += mMtrrLibFixedMtrrTable[MsrIndex].Length;\r
+ }\r
+ }\r
+ ASSERT (Base == BASE_1MB);\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+/**\r
+ Apply the variable MTRR settings to memory range array.\r
+\r
+ @param VariableMtrr The variable MTRR array.\r
+ @param VariableMtrrCount The count of variable MTRRs.\r
+ @param Ranges Return the memory range array with new MTRR settings applied.\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
+MtrrLibApplyVariableMtrrs (\r
+ IN CONST MTRR_MEMORY_RANGE *VariableMtrr,\r
+ IN UINT32 VariableMtrrCount,\r
+ IN OUT MTRR_MEMORY_RANGE *Ranges,\r
+ IN UINTN RangeCapacity,\r
+ IN OUT UINTN *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
+ // 1. Set WB\r
+ //\r
+ for (Index = 0; Index < VariableMtrrCount; Index++) {\r
+ if ((VariableMtrr[Index].Length != 0) && (VariableMtrr[Index].Type == CacheWriteBack)) {\r
+ Status = MtrrLibSetMemoryType (\r
+ Ranges, RangeCapacity, RangeCount,\r
+ VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, VariableMtrr[Index].Type\r
+ );\r
+ if (Status == RETURN_OUT_OF_RESOURCES) {\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].Length != 0) &&\r
+ (VariableMtrr[Index].Type != CacheWriteBack) && (VariableMtrr[Index].Type != CacheUncacheable)) {\r
+ Status = MtrrLibSetMemoryType (\r
+ Ranges, RangeCapacity, RangeCount,\r
+ VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, VariableMtrr[Index].Type\r
+ );\r
+ if (Status == RETURN_OUT_OF_RESOURCES) {\r
+ return Status;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // 3. Set UC\r
+ //\r
+ for (Index = 0; Index < VariableMtrrCount; Index++) {\r
+ if (VariableMtrr[Index].Length != 0 && VariableMtrr[Index].Type == CacheUncacheable) {\r
+ Status = MtrrLibSetMemoryType (\r
+ Ranges, RangeCapacity, RangeCount,\r
+ VariableMtrr[Index].BaseAddress, VariableMtrr[Index].Length, VariableMtrr[Index].Type\r
+ );\r
+ if (Status == RETURN_OUT_OF_RESOURCES) {\r
+ return Status;\r
+ }\r
+ }\r
+ }\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+/**\r
+ Return the memory type bit mask that's compatible to first type in the Ranges.\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
+\r
+ @return Compatible memory type bit mask.\r
+**/\r
+UINT8\r
+MtrrLibGetCompatibleTypes (\r
+ IN CONST MTRR_MEMORY_RANGE *Ranges,\r
+ IN UINTN RangeCount\r
+ )\r
+{\r
+ ASSERT (RangeCount != 0);\r
+\r
+ switch (Ranges[0].Type) {\r
+ case CacheWriteBack:\r
+ case CacheWriteThrough:\r
+ return (1 << CacheWriteBack) | (1 << CacheWriteThrough) | (1 << CacheUncacheable);\r
+ break;\r
+\r
+ case CacheWriteCombining:\r
+ case CacheWriteProtected:\r
+ return (1 << Ranges[0].Type) | (1 << CacheUncacheable);\r
+ break;\r
+\r
+ case CacheUncacheable:\r
+ if (RangeCount == 1) {\r
+ return (1 << CacheUncacheable);\r
+ }\r
+ return MtrrLibGetCompatibleTypes (&Ranges[1], RangeCount - 1);\r
+ break;\r
+\r
+ case CacheInvalid:\r
+ default:\r
+ ASSERT (FALSE);\r
+ break;\r
+ }\r
+ return 0;\r
+}\r
+\r
+/**\r
+ Overwrite the destination MTRR settings with the source MTRR settings.\r
+ This routine is to make sure the modification to destination MTRR settings\r
+ is as small as possible.\r
+\r
+ @param DstMtrrs Destination MTRR settings.\r
+ @param DstMtrrCount Count of destination MTRR settings.\r
+ @param SrcMtrrs Source MTRR settings.\r
+ @param SrcMtrrCount Count of source MTRR settings.\r
+ @param Modified Flag array to indicate which destination MTRR setting is modified.\r
+**/\r
+VOID\r
+MtrrLibMergeVariableMtrr (\r
+ MTRR_MEMORY_RANGE *DstMtrrs,\r
+ UINT32 DstMtrrCount,\r
+ MTRR_MEMORY_RANGE *SrcMtrrs,\r
+ UINT32 SrcMtrrCount,\r
+ BOOLEAN *Modified\r
+ )\r
+{\r
+ UINT32 DstIndex;\r
+ UINT32 SrcIndex;\r
+\r
+ ASSERT (SrcMtrrCount <= DstMtrrCount);\r
+\r
+ for (DstIndex = 0; DstIndex < DstMtrrCount; DstIndex++) {\r
+ Modified[DstIndex] = FALSE;\r
+\r
+ if (DstMtrrs[DstIndex].Length == 0) {\r
+ continue;\r
+ }\r
+ for (SrcIndex = 0; SrcIndex < SrcMtrrCount; SrcIndex++) {\r
+ if (DstMtrrs[DstIndex].BaseAddress == SrcMtrrs[SrcIndex].BaseAddress &&\r
+ DstMtrrs[DstIndex].Length == SrcMtrrs[SrcIndex].Length &&\r
+ DstMtrrs[DstIndex].Type == SrcMtrrs[SrcIndex].Type) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (SrcIndex == SrcMtrrCount) {\r
+ //\r
+ // Remove the one from DstMtrrs which is not in SrcMtrrs\r
+ //\r
+ DstMtrrs[DstIndex].Length = 0;\r
+ Modified[DstIndex] = TRUE;\r
+ } else {\r
+ //\r
+ // Remove the one from SrcMtrrs which is also in DstMtrrs\r
+ //\r
+ SrcMtrrs[SrcIndex].Length = 0;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Now valid MTRR only exists in either DstMtrrs or SrcMtrrs.\r
+ // Merge MTRRs from SrcMtrrs to DstMtrrs\r
+ //\r
+ DstIndex = 0;\r
+ for (SrcIndex = 0; SrcIndex < SrcMtrrCount; SrcIndex++) {\r
+ if (SrcMtrrs[SrcIndex].Length != 0) {\r
+\r
+ //\r
+ // Find the empty slot in DstMtrrs\r
+ //\r
+ while (DstIndex < DstMtrrCount) {\r
+ if (DstMtrrs[DstIndex].Length == 0) {\r
+ break;\r
+ }\r
+ DstIndex++;\r
+ }\r
+ ASSERT (DstIndex < DstMtrrCount);\r
+ CopyMem (&DstMtrrs[DstIndex], &SrcMtrrs[SrcIndex], sizeof (SrcMtrrs[0]));\r
+ Modified[DstIndex] = TRUE;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Calculate the variable MTRR settings for all memory ranges.\r
+\r
+ @param DefaultType Default memory type.\r
+ @param A0 Alignment to use when base address is 0.\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 Scratch Scratch buffer to be used in MTRR calculation.\r
+ @param ScratchSize Pointer to the size of scratch buffer.\r
+ @param VariableMtrr Array holding all MTRR settings.\r
+ @param VariableMtrrCapacity Capacity of the MTRR array.\r
+ @param VariableMtrrCount The count of MTRR settings in array.\r
+\r
+ @retval RETURN_SUCCESS Variable MTRRs are allocated successfully.\r
+ @retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.\r
+ @retval RETURN_BUFFER_TOO_SMALL The scratch buffer is too small for MTRR calculation.\r
+ The required scratch buffer size is returned through ScratchSize.\r
+**/\r
+RETURN_STATUS\r
+MtrrLibSetMemoryRanges (\r
+ IN MTRR_MEMORY_CACHE_TYPE DefaultType,\r
+ IN UINT64 A0,\r
+ IN MTRR_MEMORY_RANGE *Ranges,\r
+ IN UINTN RangeCount,\r
+ IN VOID *Scratch,\r
+ IN OUT UINTN *ScratchSize,\r
+ OUT MTRR_MEMORY_RANGE *VariableMtrr,\r
+ IN UINT32 VariableMtrrCapacity,\r
+ OUT UINT32 *VariableMtrrCount\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+ UINT32 Index;\r
+ UINT64 Base0;\r
+ UINT64 Base1;\r
+ UINT64 Alignment;\r
+ UINT8 CompatibleTypes;\r
+ UINT64 Length;\r
+ UINT32 End;\r
+ UINTN ActualScratchSize;\r
+ UINTN BiggestScratchSize;\r
+\r
+ *VariableMtrrCount = 0;\r
+\r
+ //\r
+ // Since the whole ranges need multiple calls of MtrrLibCalculateMtrrs().\r
+ // Each call needs different scratch buffer size.\r
+ // When the provided scratch buffer size is not sufficient in any call,\r
+ // set the GetActualScratchSize to TRUE, and following calls will only\r
+ // calculate the actual scratch size for the caller.\r
+ //\r
+ BiggestScratchSize = 0;\r
+\r
+ for (Index = 0; Index < RangeCount;) {\r
+ Base0 = Ranges[Index].BaseAddress;\r
+\r
+ //\r
+ // Full step is optimal\r
+ //\r
+ while (Index < RangeCount) {\r
+ ASSERT (Ranges[Index].BaseAddress == Base0);\r
+ Alignment = MtrrLibBiggestAlignment (Base0, A0);\r
+ while (Base0 + Alignment <= Ranges[Index].BaseAddress + Ranges[Index].Length) {\r
+ if ((BiggestScratchSize <= *ScratchSize) && (Ranges[Index].Type != DefaultType)) {\r
+ Status = MtrrLibAppendVariableMtrr (\r
+ VariableMtrr, VariableMtrrCapacity, VariableMtrrCount,\r
+ Base0, Alignment, Ranges[Index].Type\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ Base0 += Alignment;\r
+ Alignment = MtrrLibBiggestAlignment (Base0, A0);\r
+ }\r
+\r
+ //\r
+ // Remove the above range from Ranges[Index]\r
+ //\r
+ Ranges[Index].Length -= Base0 - Ranges[Index].BaseAddress;\r
+ Ranges[Index].BaseAddress = Base0;\r
+ if (Ranges[Index].Length != 0) {\r
+ break;\r
+ } else {\r
+ Index++;\r
+ }\r
+ }\r
+\r
+ if (Index == RangeCount) {\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Find continous ranges [Base0, Base1) which could be combined by MTRR.\r
+ // Per SDM, the compatible types between[B0, B1) are:\r
+ // UC, *\r
+ // WB, WT\r
+ // UC, WB, WT\r
+ //\r
+ CompatibleTypes = MtrrLibGetCompatibleTypes (&Ranges[Index], RangeCount - Index);\r
+\r
+ End = Index; // End points to last one that matches the CompatibleTypes.\r
+ while (End + 1 < RangeCount) {\r
+ if (((1 << Ranges[End + 1].Type) & CompatibleTypes) == 0) {\r
+ break;\r
+ }\r
+ End++;\r
+ }\r
+ Alignment = MtrrLibBiggestAlignment (Base0, A0);\r
+ Length = GetPowerOfTwo64 (Ranges[End].BaseAddress + Ranges[End].Length - Base0);\r
+ Base1 = Base0 + MIN (Alignment, Length);\r
+\r
+ //\r
+ // Base1 may not in Ranges[End]. Update End to the range Base1 belongs to.\r
+ //\r
+ End = Index;\r
+ while (End + 1 < RangeCount) {\r
+ if (Base1 <= Ranges[End + 1].BaseAddress) {\r
+ break;\r
+ }\r
+ End++;\r
+ }\r
+\r
+ Length = Ranges[End].Length;\r
+ Ranges[End].Length = Base1 - Ranges[End].BaseAddress;\r
+ ActualScratchSize = *ScratchSize;\r
+ Status = MtrrLibCalculateMtrrs (\r
+ DefaultType, A0,\r
+ &Ranges[Index], End + 1 - Index,\r
+ Scratch, &ActualScratchSize,\r
+ VariableMtrr, VariableMtrrCapacity, VariableMtrrCount\r
+ );\r
+ if (Status == RETURN_BUFFER_TOO_SMALL) {\r
+ BiggestScratchSize = MAX (BiggestScratchSize, ActualScratchSize);\r
+ //\r
+ // Ignore this error, because we need to calculate the biggest\r
+ // scratch buffer size.\r
+ //\r
+ Status = RETURN_SUCCESS;\r
+ }\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (Length != Ranges[End].Length) {\r
+ Ranges[End].BaseAddress = Base1;\r
+ Ranges[End].Length = Length - Ranges[End].Length;\r
+ Index = End;\r
+ } else {\r
+ Index = End + 1;\r
+ }\r
+ }\r
+\r
+ if (*ScratchSize < BiggestScratchSize) {\r
+ *ScratchSize = BiggestScratchSize;\r
+ return RETURN_BUFFER_TOO_SMALL;\r
+ }\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+/**\r
+ Set the below-1MB memory attribute to fixed MTRR buffer.\r
+ Modified flag array indicates which fixed MTRR is modified.\r
+\r
+ @param [in, out] FixedSettings Fixed MTRR buffer.\r
+ @param [out] Modified Flag array indicating which MTRR is modified.\r
+ @param [in] BaseAddress Base address.\r
+ @param [in] Length Length.\r
+ @param [in] Type Memory type.\r
+\r
+ @retval RETURN_SUCCESS The memory attribute is set successfully.\r
+ @retval RETURN_UNSUPPORTED The requested range or cache type was invalid\r
+ for the fixed MTRRs.\r
+**/\r
+RETURN_STATUS\r
+MtrrLibSetBelow1MBMemoryAttribute (\r
+ IN OUT MTRR_FIXED_SETTINGS *FixedSettings,\r
+ OUT BOOLEAN *Modified,\r
+ IN PHYSICAL_ADDRESS BaseAddress,\r
+ IN UINT64 Length,\r
+ IN MTRR_MEMORY_CACHE_TYPE Type\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+ UINT32 MsrIndex;\r
+ UINT64 ClearMask;\r
+ UINT64 OrMask;\r
+ UINT64 ClearMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];\r
+ UINT64 OrMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];\r
+ BOOLEAN LocalModified[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];\r
+\r
+ ASSERT (BaseAddress < BASE_1MB);\r
+\r
+ SetMem (LocalModified, sizeof (LocalModified), FALSE);\r
+\r
+ //\r
+ // (Value & ~0 | 0) still equals to (Value)\r
+ //\r
+ SetMem (ClearMasks, sizeof (ClearMasks), 0);\r
+ SetMem (OrMasks, sizeof (OrMasks), 0);\r
+\r
+ MsrIndex = (UINT32)-1;\r
+ while ((BaseAddress < BASE_1MB) && (Length != 0)) {\r
+ Status = MtrrLibProgramFixedMtrr (Type, &BaseAddress, &Length, &MsrIndex, &ClearMask, &OrMask);\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ ClearMasks[MsrIndex] = ClearMask;\r
+ OrMasks[MsrIndex] = OrMask;\r
+ Modified[MsrIndex] = TRUE;\r
+ LocalModified[MsrIndex] = TRUE;\r
+ }\r
+\r
+ for (MsrIndex = 0; MsrIndex < ARRAY_SIZE (mMtrrLibFixedMtrrTable); MsrIndex++) {\r
+ if (LocalModified[MsrIndex]) {\r
+ FixedSettings->Mtrr[MsrIndex] = (FixedSettings->Mtrr[MsrIndex] & ~ClearMasks[MsrIndex]) | OrMasks[MsrIndex];\r
+ }\r
+ }\r
+ return RETURN_SUCCESS;\r
+}\r
+\r
+/**\r
+ This function attempts to set the attributes into MTRR setting buffer for multiple memory ranges.\r
+\r
+ @param[in, out] MtrrSetting MTRR setting buffer to be set.\r
+ @param[in] Scratch A temporary scratch buffer that is used to perform the calculation.\r
+ @param[in, out] ScratchSize Pointer to the size in bytes of the scratch buffer.\r
+ It may be updated to the actual required size when the calculation\r
+ needs more scratch buffer.\r
+ @param[in] Ranges Pointer to an array of MTRR_MEMORY_RANGE.\r
+ When range overlap happens, the last one takes higher priority.\r
+ When the function returns, either all the attributes are set successfully,\r
+ or none of them is set.\r
+ @param[in] RangeCount Count of MTRR_MEMORY_RANGE.\r
+\r
+ @retval RETURN_SUCCESS The attributes were set for all the memory ranges.\r
+ @retval RETURN_INVALID_PARAMETER Length in any range is zero.\r
+ @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the\r
+ memory resource range specified by BaseAddress and Length in any range.\r
+ @retval RETURN_UNSUPPORTED The bit mask of attributes is not support for the memory resource\r
+ range specified by BaseAddress and Length in any range.\r
+ @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
+ the memory resource ranges.\r
+ @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by\r
+ BaseAddress and Length cannot be modified.\r
+ @retval RETURN_BUFFER_TOO_SMALL The scratch buffer is too small for MTRR calculation.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+MtrrSetMemoryAttributesInMtrrSettings (\r
+ IN OUT MTRR_SETTINGS *MtrrSetting,\r
+ IN VOID *Scratch,\r
+ IN OUT UINTN *ScratchSize,\r
+ IN CONST MTRR_MEMORY_RANGE *Ranges,\r
+ IN UINTN RangeCount\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+ UINT32 Index;\r
+ UINT64 BaseAddress;\r
+ UINT64 Length;\r
+ BOOLEAN Above1MbExist;\r
+\r
+ UINT64 MtrrValidBitsMask;\r
+ UINT64 MtrrValidAddressMask;\r
+ MTRR_MEMORY_CACHE_TYPE DefaultType;\r
+ MTRR_VARIABLE_SETTINGS VariableSettings;\r
+ MTRR_MEMORY_RANGE WorkingRanges[2 * ARRAY_SIZE (MtrrSetting->Variables.Mtrr) + 2];\r
+ UINTN WorkingRangeCount;\r
+ BOOLEAN Modified;\r
+ MTRR_VARIABLE_SETTING VariableSetting;\r
+ UINT32 OriginalVariableMtrrCount;\r
+ UINT32 FirmwareVariableMtrrCount;\r
+ UINT32 WorkingVariableMtrrCount;\r
+ MTRR_MEMORY_RANGE OriginalVariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];\r
+ MTRR_MEMORY_RANGE WorkingVariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];\r
+ BOOLEAN VariableSettingModified[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];\r
+\r
+ BOOLEAN FixedSettingsModified[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];\r
+ MTRR_FIXED_SETTINGS WorkingFixedSettings;\r
+\r
+ MTRR_CONTEXT MtrrContext;\r
+ BOOLEAN MtrrContextValid;\r
+\r
+ Status = RETURN_SUCCESS;\r
+ MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask);\r
+\r