1 /** @file 2 SMM Memory pool management functions. 3 4 Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR> 5 This program and the accompanying materials are licensed and made available 6 under the terms and conditions of the BSD License which accompanies this 7 distribution. The full text of the license may be found at 8 http://opensource.org/licenses/bsd-license.php 9 10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 13 **/ 14 15 #include "PiSmmCore.h" 16 17 LIST_ENTRY mSmmPoolLists[MAX_POOL_INDEX]; 18 // 19 // To cache the SMRAM base since when Loading modules At fixed address feature is enabled, 20 // all module is assigned an offset relative the SMRAM base in build time. 21 // 22 GLOBAL_REMOVE_IF_UNREFERENCED EFI_PHYSICAL_ADDRESS gLoadModuleAtFixAddressSmramBase = 0; 23 24 /** 25 Called to initialize the memory service. 26 27 @param SmramRangeCount Number of SMRAM Regions 28 @param SmramRanges Pointer to SMRAM Descriptors 29 30 **/ 31 VOID 32 SmmInitializeMemoryServices ( 33 IN UINTN SmramRangeCount, 34 IN EFI_SMRAM_DESCRIPTOR *SmramRanges 35 ) 36 { 37 UINTN Index; 38 UINT64 SmmCodeSize; 39 UINTN CurrentSmramRangesIndex; 40 UINT64 MaxSize; 41 42 // 43 // Initialize Pool list 44 // 45 for (Index = sizeof (mSmmPoolLists) / sizeof (*mSmmPoolLists); Index > 0;) { 46 InitializeListHead (&mSmmPoolLists[--Index]); 47 } 48 CurrentSmramRangesIndex = 0; 49 // 50 // If Loadding Module At fixed Address feature is enabled, cache the SMRAM base here 51 // 52 if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) { 53 // 54 // Build tool will calculate the smm code size and then patch the PcdLoadFixAddressSmmCodePageNumber 55 // 56 SmmCodeSize = LShiftU64 (PcdGet32(PcdLoadFixAddressSmmCodePageNumber), EFI_PAGE_SHIFT); 57 58 // 59 // Find the largest SMRAM range between 1MB and 4GB that is at least 256KB - 4K in size 60 // 61 for (Index = 0, MaxSize = SIZE_256KB - EFI_PAGE_SIZE; Index < SmramRangeCount; Index++) { 62 // 63 // Skip any SMRAM region that is already allocated, needs testing, or needs ECC initialization 64 // 65 if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) { 66 continue; 67 } 68 69 if (SmramRanges[Index].CpuStart >= BASE_1MB) { 70 if ((SmramRanges[Index].CpuStart + SmramRanges[Index].PhysicalSize) <= BASE_4GB) { 71 if (SmramRanges[Index].PhysicalSize >= MaxSize) { 72 MaxSize = SmramRanges[Index].PhysicalSize; 73 CurrentSmramRangesIndex = Index; 74 } 75 } 76 } 77 } 78 gLoadModuleAtFixAddressSmramBase = SmramRanges[CurrentSmramRangesIndex].CpuStart; 79 80 // 81 // cut out a memory range from this SMRAM range with the size SmmCodeSize to hold SMM driver code 82 // A notable thing is that SMM core is already loaded into this range. 83 // 84 SmramRanges[CurrentSmramRangesIndex].CpuStart = SmramRanges[CurrentSmramRangesIndex].CpuStart + SmmCodeSize; 85 SmramRanges[CurrentSmramRangesIndex].PhysicalSize = SmramRanges[CurrentSmramRangesIndex].PhysicalSize - SmmCodeSize; 86 } 87 // 88 // Initialize free SMRAM regions 89 // 90 for (Index = 0; Index < SmramRangeCount; Index++) { 91 SmmAddMemoryRegion ( 92 SmramRanges[Index].CpuStart, 93 SmramRanges[Index].PhysicalSize, 94 EfiConventionalMemory, 95 SmramRanges[Index].RegionState 96 ); 97 } 98 99 } 100 101 /** 102 Internal Function. Allocate a pool by specified PoolIndex. 103 104 @param PoolIndex Index which indicate the Pool size. 105 @param FreePoolHdr The returned Free pool. 106 107 @retval EFI_OUT_OF_RESOURCES Allocation failed. 108 @retval EFI_SUCCESS Pool successfully allocated. 109 110 **/ 111 EFI_STATUS 112 InternalAllocPoolByIndex ( 113 IN UINTN PoolIndex, 114 OUT FREE_POOL_HEADER **FreePoolHdr 115 ) 116 { 117 EFI_STATUS Status; 118 FREE_POOL_HEADER *Hdr; 119 EFI_PHYSICAL_ADDRESS Address; 120 121 ASSERT (PoolIndex <= MAX_POOL_INDEX); 122 Status = EFI_SUCCESS; 123 Hdr = NULL; 124 if (PoolIndex == MAX_POOL_INDEX) { 125 Status = SmmInternalAllocatePages (AllocateAnyPages, EfiRuntimeServicesData, EFI_SIZE_TO_PAGES (MAX_POOL_SIZE << 1), &Address); 126 if (EFI_ERROR (Status)) { 127 return EFI_OUT_OF_RESOURCES; 128 } 129 Hdr = (FREE_POOL_HEADER *) (UINTN) Address; 130 } else if (!IsListEmpty (&mSmmPoolLists[PoolIndex])) { 131 Hdr = BASE_CR (GetFirstNode (&mSmmPoolLists[PoolIndex]), FREE_POOL_HEADER, Link); 132 RemoveEntryList (&Hdr->Link); 133 } else { 134 Status = InternalAllocPoolByIndex (PoolIndex + 1, &Hdr); 135 if (!EFI_ERROR (Status)) { 136 Hdr->Header.Size >>= 1; 137 Hdr->Header.Available = TRUE; 138 InsertHeadList (&mSmmPoolLists[PoolIndex], &Hdr->Link); 139 Hdr = (FREE_POOL_HEADER*)((UINT8*)Hdr + Hdr->Header.Size); 140 } 141 } 142 143 if (!EFI_ERROR (Status)) { 144 Hdr->Header.Size = MIN_POOL_SIZE << PoolIndex; 145 Hdr->Header.Available = FALSE; 146 } 147 148 *FreePoolHdr = Hdr; 149 return Status; 150 } 151 152 /** 153 Internal Function. Free a pool by specified PoolIndex. 154 155 @param FreePoolHdr The pool to free. 156 157 @retval EFI_SUCCESS Pool successfully freed. 158 159 **/ 160 EFI_STATUS 161 InternalFreePoolByIndex ( 162 IN FREE_POOL_HEADER *FreePoolHdr 163 ) 164 { 165 UINTN PoolIndex; 166 167 ASSERT ((FreePoolHdr->Header.Size & (FreePoolHdr->Header.Size - 1)) == 0); 168 ASSERT (((UINTN)FreePoolHdr & (FreePoolHdr->Header.Size - 1)) == 0); 169 ASSERT (FreePoolHdr->Header.Size >= MIN_POOL_SIZE); 170 171 PoolIndex = (UINTN) (HighBitSet32 ((UINT32)FreePoolHdr->Header.Size) - MIN_POOL_SHIFT); 172 FreePoolHdr->Header.Available = TRUE; 173 ASSERT (PoolIndex < MAX_POOL_INDEX); 174 InsertHeadList (&mSmmPoolLists[PoolIndex], &FreePoolHdr->Link); 175 return EFI_SUCCESS; 176 } 177 178 /** 179 Allocate pool of a particular type. 180 181 @param PoolType Type of pool to allocate. 182 @param Size The amount of pool to allocate. 183 @param Buffer The address to return a pointer to the allocated 184 pool. 185 186 @retval EFI_INVALID_PARAMETER PoolType not valid. 187 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed. 188 @retval EFI_SUCCESS Pool successfully allocated. 189 190 **/ 191 EFI_STATUS 192 EFIAPI 193 SmmInternalAllocatePool ( 194 IN EFI_MEMORY_TYPE PoolType, 195 IN UINTN Size, 196 OUT VOID **Buffer 197 ) 198 { 199 POOL_HEADER *PoolHdr; 200 FREE_POOL_HEADER *FreePoolHdr; 201 EFI_STATUS Status; 202 EFI_PHYSICAL_ADDRESS Address; 203 UINTN PoolIndex; 204 205 if (PoolType != EfiRuntimeServicesCode && 206 PoolType != EfiRuntimeServicesData) { 207 return EFI_INVALID_PARAMETER; 208 } 209 210 Size += sizeof (*PoolHdr); 211 if (Size > MAX_POOL_SIZE) { 212 Size = EFI_SIZE_TO_PAGES (Size); 213 Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, Size, &Address); 214 if (EFI_ERROR (Status)) { 215 return Status; 216 } 217 218 PoolHdr = (POOL_HEADER*)(UINTN)Address; 219 PoolHdr->Size = EFI_PAGES_TO_SIZE (Size); 220 PoolHdr->Available = FALSE; 221 *Buffer = PoolHdr + 1; 222 return Status; 223 } 224 225 Size = (Size + MIN_POOL_SIZE - 1) >> MIN_POOL_SHIFT; 226 PoolIndex = (UINTN) HighBitSet32 ((UINT32)Size); 227 if ((Size & (Size - 1)) != 0) { 228 PoolIndex++; 229 } 230 231 Status = InternalAllocPoolByIndex (PoolIndex, &FreePoolHdr); 232 if (!EFI_ERROR(Status)) { 233 *Buffer = &FreePoolHdr->Header + 1; 234 } 235 return Status; 236 } 237 238 /** 239 Allocate pool of a particular type. 240 241 @param PoolType Type of pool to allocate. 242 @param Size The amount of pool to allocate. 243 @param Buffer The address to return a pointer to the allocated 244 pool. 245 246 @retval EFI_INVALID_PARAMETER PoolType not valid. 247 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed. 248 @retval EFI_SUCCESS Pool successfully allocated. 249 250 **/ 251 EFI_STATUS 252 EFIAPI 253 SmmAllocatePool ( 254 IN EFI_MEMORY_TYPE PoolType, 255 IN UINTN Size, 256 OUT VOID **Buffer 257 ) 258 { 259 EFI_STATUS Status; 260 261 Status = SmmInternalAllocatePool (PoolType, Size, Buffer); 262 if (!EFI_ERROR (Status)) { 263 SmmCoreUpdateProfile ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionAllocatePool, PoolType, Size, *Buffer); 264 } 265 return Status; 266 } 267 268 /** 269 Frees pool. 270 271 @param Buffer The allocated pool entry to free. 272 273 @retval EFI_INVALID_PARAMETER Buffer is not a valid value. 274 @retval EFI_SUCCESS Pool successfully freed. 275 276 **/ 277 EFI_STATUS 278 EFIAPI 279 SmmInternalFreePool ( 280 IN VOID *Buffer 281 ) 282 { 283 FREE_POOL_HEADER *FreePoolHdr; 284 285 if (Buffer == NULL) { 286 return EFI_INVALID_PARAMETER; 287 } 288 289 FreePoolHdr = (FREE_POOL_HEADER*)((POOL_HEADER*)Buffer - 1); 290 ASSERT (!FreePoolHdr->Header.Available); 291 292 if (FreePoolHdr->Header.Size > MAX_POOL_SIZE) { 293 ASSERT (((UINTN)FreePoolHdr & EFI_PAGE_MASK) == 0); 294 ASSERT ((FreePoolHdr->Header.Size & EFI_PAGE_MASK) == 0); 295 return SmmInternalFreePages ( 296 (EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr, 297 EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size) 298 ); 299 } 300 return InternalFreePoolByIndex (FreePoolHdr); 301 } 302 303 /** 304 Frees pool. 305 306 @param Buffer The allocated pool entry to free. 307 308 @retval EFI_INVALID_PARAMETER Buffer is not a valid value. 309 @retval EFI_SUCCESS Pool successfully freed. 310 311 **/ 312 EFI_STATUS 313 EFIAPI 314 SmmFreePool ( 315 IN VOID *Buffer 316 ) 317 { 318 EFI_STATUS Status; 319 320 Status = SmmInternalFreePool (Buffer); 321 if (!EFI_ERROR (Status)) { 322 SmmCoreUpdateProfile ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionFreePool, 0, 0, Buffer); 323 } 324 return Status; 325 } 326