1 /** @file 2 SMM Memory pool management functions. 3 4 Copyright (c) 2009 - 2016, 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[SmmPoolTypeMax][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 Convert a UEFI memory type to SMM pool type. 26 27 @param[in] MemoryType Type of pool to allocate. 28 29 @return SMM pool type 30 **/ 31 SMM_POOL_TYPE 32 UefiMemoryTypeToSmmPoolType ( 33 IN EFI_MEMORY_TYPE MemoryType 34 ) 35 { 36 ASSERT ((MemoryType == EfiRuntimeServicesCode) || (MemoryType == EfiRuntimeServicesData)); 37 switch (MemoryType) { 38 case EfiRuntimeServicesCode: 39 return SmmPoolTypeCode; 40 case EfiRuntimeServicesData: 41 return SmmPoolTypeData; 42 default: 43 return SmmPoolTypeMax; 44 } 45 } 46 47 48 /** 49 Called to initialize the memory service. 50 51 @param SmramRangeCount Number of SMRAM Regions 52 @param SmramRanges Pointer to SMRAM Descriptors 53 54 **/ 55 VOID 56 SmmInitializeMemoryServices ( 57 IN UINTN SmramRangeCount, 58 IN EFI_SMRAM_DESCRIPTOR *SmramRanges 59 ) 60 { 61 UINTN Index; 62 EFI_STATUS Status; 63 UINTN SmmPoolTypeIndex; 64 EFI_LOAD_FIXED_ADDRESS_CONFIGURATION_TABLE *LMFAConfigurationTable; 65 66 // 67 // Initialize Pool list 68 // 69 for (SmmPoolTypeIndex = 0; SmmPoolTypeIndex < SmmPoolTypeMax; SmmPoolTypeIndex++) { 70 for (Index = 0; Index < ARRAY_SIZE (mSmmPoolLists[SmmPoolTypeIndex]); Index++) { 71 InitializeListHead (&mSmmPoolLists[SmmPoolTypeIndex][Index]); 72 } 73 } 74 75 Status = EfiGetSystemConfigurationTable ( 76 &gLoadFixedAddressConfigurationTableGuid, 77 (VOID **) &LMFAConfigurationTable 78 ); 79 if (!EFI_ERROR (Status) && LMFAConfigurationTable != NULL) { 80 gLoadModuleAtFixAddressSmramBase = LMFAConfigurationTable->SmramBase; 81 } 82 83 // 84 // Add Free SMRAM regions 85 // Need add Free memory at first, to let gSmmMemoryMap record data 86 // 87 for (Index = 0; Index < SmramRangeCount; Index++) { 88 if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) { 89 continue; 90 } 91 SmmAddMemoryRegion ( 92 SmramRanges[Index].CpuStart, 93 SmramRanges[Index].PhysicalSize, 94 EfiConventionalMemory, 95 SmramRanges[Index].RegionState 96 ); 97 } 98 99 // 100 // Add the allocated SMRAM regions 101 // 102 for (Index = 0; Index < SmramRangeCount; Index++) { 103 if ((SmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) == 0) { 104 continue; 105 } 106 SmmAddMemoryRegion ( 107 SmramRanges[Index].CpuStart, 108 SmramRanges[Index].PhysicalSize, 109 EfiConventionalMemory, 110 SmramRanges[Index].RegionState 111 ); 112 } 113 114 } 115 116 /** 117 Internal Function. Allocate a pool by specified PoolIndex. 118 119 @param PoolType Type of pool to allocate. 120 @param PoolIndex Index which indicate the Pool size. 121 @param FreePoolHdr The returned Free pool. 122 123 @retval EFI_OUT_OF_RESOURCES Allocation failed. 124 @retval EFI_SUCCESS Pool successfully allocated. 125 126 **/ 127 EFI_STATUS 128 InternalAllocPoolByIndex ( 129 IN EFI_MEMORY_TYPE PoolType, 130 IN UINTN PoolIndex, 131 OUT FREE_POOL_HEADER **FreePoolHdr 132 ) 133 { 134 EFI_STATUS Status; 135 FREE_POOL_HEADER *Hdr; 136 EFI_PHYSICAL_ADDRESS Address; 137 SMM_POOL_TYPE SmmPoolType; 138 139 SmmPoolType = UefiMemoryTypeToSmmPoolType(PoolType); 140 141 ASSERT (PoolIndex <= MAX_POOL_INDEX); 142 Status = EFI_SUCCESS; 143 Hdr = NULL; 144 if (PoolIndex == MAX_POOL_INDEX) { 145 Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, EFI_SIZE_TO_PAGES (MAX_POOL_SIZE << 1), &Address); 146 if (EFI_ERROR (Status)) { 147 return EFI_OUT_OF_RESOURCES; 148 } 149 Hdr = (FREE_POOL_HEADER *) (UINTN) Address; 150 } else if (!IsListEmpty (&mSmmPoolLists[SmmPoolType][PoolIndex])) { 151 Hdr = BASE_CR (GetFirstNode (&mSmmPoolLists[SmmPoolType][PoolIndex]), FREE_POOL_HEADER, Link); 152 RemoveEntryList (&Hdr->Link); 153 } else { 154 Status = InternalAllocPoolByIndex (PoolType, PoolIndex + 1, &Hdr); 155 if (!EFI_ERROR (Status)) { 156 Hdr->Header.Size >>= 1; 157 Hdr->Header.Available = TRUE; 158 Hdr->Header.Type = PoolType; 159 InsertHeadList (&mSmmPoolLists[SmmPoolType][PoolIndex], &Hdr->Link); 160 Hdr = (FREE_POOL_HEADER*)((UINT8*)Hdr + Hdr->Header.Size); 161 } 162 } 163 164 if (!EFI_ERROR (Status)) { 165 Hdr->Header.Size = MIN_POOL_SIZE << PoolIndex; 166 Hdr->Header.Available = FALSE; 167 Hdr->Header.Type = PoolType; 168 } 169 170 *FreePoolHdr = Hdr; 171 return Status; 172 } 173 174 /** 175 Internal Function. Free a pool by specified PoolIndex. 176 177 @param FreePoolHdr The pool to free. 178 179 @retval EFI_SUCCESS Pool successfully freed. 180 181 **/ 182 EFI_STATUS 183 InternalFreePoolByIndex ( 184 IN FREE_POOL_HEADER *FreePoolHdr 185 ) 186 { 187 UINTN PoolIndex; 188 SMM_POOL_TYPE SmmPoolType; 189 190 ASSERT ((FreePoolHdr->Header.Size & (FreePoolHdr->Header.Size - 1)) == 0); 191 ASSERT (((UINTN)FreePoolHdr & (FreePoolHdr->Header.Size - 1)) == 0); 192 ASSERT (FreePoolHdr->Header.Size >= MIN_POOL_SIZE); 193 194 SmmPoolType = UefiMemoryTypeToSmmPoolType(FreePoolHdr->Header.Type); 195 196 PoolIndex = (UINTN) (HighBitSet32 ((UINT32)FreePoolHdr->Header.Size) - MIN_POOL_SHIFT); 197 FreePoolHdr->Header.Available = TRUE; 198 ASSERT (PoolIndex < MAX_POOL_INDEX); 199 InsertHeadList (&mSmmPoolLists[SmmPoolType][PoolIndex], &FreePoolHdr->Link); 200 return EFI_SUCCESS; 201 } 202 203 /** 204 Allocate pool of a particular type. 205 206 @param PoolType Type of pool to allocate. 207 @param Size The amount of pool to allocate. 208 @param Buffer The address to return a pointer to the allocated 209 pool. 210 211 @retval EFI_INVALID_PARAMETER PoolType not valid. 212 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed. 213 @retval EFI_SUCCESS Pool successfully allocated. 214 215 **/ 216 EFI_STATUS 217 EFIAPI 218 SmmInternalAllocatePool ( 219 IN EFI_MEMORY_TYPE PoolType, 220 IN UINTN Size, 221 OUT VOID **Buffer 222 ) 223 { 224 POOL_HEADER *PoolHdr; 225 FREE_POOL_HEADER *FreePoolHdr; 226 EFI_STATUS Status; 227 EFI_PHYSICAL_ADDRESS Address; 228 UINTN PoolIndex; 229 230 if (PoolType != EfiRuntimeServicesCode && 231 PoolType != EfiRuntimeServicesData) { 232 return EFI_INVALID_PARAMETER; 233 } 234 235 Size += sizeof (*PoolHdr); 236 if (Size > MAX_POOL_SIZE) { 237 Size = EFI_SIZE_TO_PAGES (Size); 238 Status = SmmInternalAllocatePages (AllocateAnyPages, PoolType, Size, &Address); 239 if (EFI_ERROR (Status)) { 240 return Status; 241 } 242 243 PoolHdr = (POOL_HEADER*)(UINTN)Address; 244 PoolHdr->Size = EFI_PAGES_TO_SIZE (Size); 245 PoolHdr->Available = FALSE; 246 PoolHdr->Type = PoolType; 247 *Buffer = PoolHdr + 1; 248 return Status; 249 } 250 251 Size = (Size + MIN_POOL_SIZE - 1) >> MIN_POOL_SHIFT; 252 PoolIndex = (UINTN) HighBitSet32 ((UINT32)Size); 253 if ((Size & (Size - 1)) != 0) { 254 PoolIndex++; 255 } 256 257 Status = InternalAllocPoolByIndex (PoolType, PoolIndex, &FreePoolHdr); 258 if (!EFI_ERROR(Status)) { 259 *Buffer = &FreePoolHdr->Header + 1; 260 } 261 return Status; 262 } 263 264 /** 265 Allocate pool of a particular type. 266 267 @param PoolType Type of pool to allocate. 268 @param Size The amount of pool to allocate. 269 @param Buffer The address to return a pointer to the allocated 270 pool. 271 272 @retval EFI_INVALID_PARAMETER PoolType not valid. 273 @retval EFI_OUT_OF_RESOURCES Size exceeds max pool size or allocation failed. 274 @retval EFI_SUCCESS Pool successfully allocated. 275 276 **/ 277 EFI_STATUS 278 EFIAPI 279 SmmAllocatePool ( 280 IN EFI_MEMORY_TYPE PoolType, 281 IN UINTN Size, 282 OUT VOID **Buffer 283 ) 284 { 285 EFI_STATUS Status; 286 287 Status = SmmInternalAllocatePool (PoolType, Size, Buffer); 288 if (!EFI_ERROR (Status)) { 289 SmmCoreUpdateProfile ( 290 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), 291 MemoryProfileActionAllocatePool, 292 PoolType, 293 Size, 294 *Buffer, 295 NULL 296 ); 297 } 298 return Status; 299 } 300 301 /** 302 Frees pool. 303 304 @param Buffer The allocated pool entry to free. 305 306 @retval EFI_INVALID_PARAMETER Buffer is not a valid value. 307 @retval EFI_SUCCESS Pool successfully freed. 308 309 **/ 310 EFI_STATUS 311 EFIAPI 312 SmmInternalFreePool ( 313 IN VOID *Buffer 314 ) 315 { 316 FREE_POOL_HEADER *FreePoolHdr; 317 318 if (Buffer == NULL) { 319 return EFI_INVALID_PARAMETER; 320 } 321 322 FreePoolHdr = (FREE_POOL_HEADER*)((POOL_HEADER*)Buffer - 1); 323 ASSERT (!FreePoolHdr->Header.Available); 324 325 if (FreePoolHdr->Header.Size > MAX_POOL_SIZE) { 326 ASSERT (((UINTN)FreePoolHdr & EFI_PAGE_MASK) == 0); 327 ASSERT ((FreePoolHdr->Header.Size & EFI_PAGE_MASK) == 0); 328 return SmmInternalFreePages ( 329 (EFI_PHYSICAL_ADDRESS)(UINTN)FreePoolHdr, 330 EFI_SIZE_TO_PAGES (FreePoolHdr->Header.Size) 331 ); 332 } 333 return InternalFreePoolByIndex (FreePoolHdr); 334 } 335 336 /** 337 Frees pool. 338 339 @param Buffer The allocated pool entry to free. 340 341 @retval EFI_INVALID_PARAMETER Buffer is not a valid value. 342 @retval EFI_SUCCESS Pool successfully freed. 343 344 **/ 345 EFI_STATUS 346 EFIAPI 347 SmmFreePool ( 348 IN VOID *Buffer 349 ) 350 { 351 EFI_STATUS Status; 352 353 Status = SmmInternalFreePool (Buffer); 354 if (!EFI_ERROR (Status)) { 355 SmmCoreUpdateProfile ( 356 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), 357 MemoryProfileActionFreePool, 358 EfiMaxMemoryType, 359 0, 360 Buffer, 361 NULL 362 ); 363 } 364 return Status; 365 } 366