1 /** @file 2 SMM Memory page management functions. 3 4 Copyright (c) 2009 - 2014, 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 #define TRUNCATE_TO_PAGES(a) ((a) >> EFI_PAGE_SHIFT) 18 19 LIST_ENTRY mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap); 20 21 /** 22 Internal Function. Allocate n pages from given free page node. 23 24 @param Pages The free page node. 25 @param NumberOfPages Number of pages to be allocated. 26 @param MaxAddress Request to allocate memory below this address. 27 28 @return Memory address of allocated pages. 29 30 **/ 31 UINTN 32 InternalAllocPagesOnOneNode ( 33 IN OUT FREE_PAGE_LIST *Pages, 34 IN UINTN NumberOfPages, 35 IN UINTN MaxAddress 36 ) 37 { 38 UINTN Top; 39 UINTN Bottom; 40 FREE_PAGE_LIST *Node; 41 42 Top = TRUNCATE_TO_PAGES (MaxAddress + 1 - (UINTN)Pages); 43 if (Top > Pages->NumberOfPages) { 44 Top = Pages->NumberOfPages; 45 } 46 Bottom = Top - NumberOfPages; 47 48 if (Top < Pages->NumberOfPages) { 49 Node = (FREE_PAGE_LIST*)((UINTN)Pages + EFI_PAGES_TO_SIZE (Top)); 50 Node->NumberOfPages = Pages->NumberOfPages - Top; 51 InsertHeadList (&Pages->Link, &Node->Link); 52 } 53 54 if (Bottom > 0) { 55 Pages->NumberOfPages = Bottom; 56 } else { 57 RemoveEntryList (&Pages->Link); 58 } 59 60 return (UINTN)Pages + EFI_PAGES_TO_SIZE (Bottom); 61 } 62 63 /** 64 Internal Function. Allocate n pages from free page list below MaxAddress. 65 66 @param FreePageList The free page node. 67 @param NumberOfPages Number of pages to be allocated. 68 @param MaxAddress Request to allocate memory below this address. 69 70 @return Memory address of allocated pages. 71 72 **/ 73 UINTN 74 InternalAllocMaxAddress ( 75 IN OUT LIST_ENTRY *FreePageList, 76 IN UINTN NumberOfPages, 77 IN UINTN MaxAddress 78 ) 79 { 80 LIST_ENTRY *Node; 81 FREE_PAGE_LIST *Pages; 82 83 for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) { 84 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); 85 if (Pages->NumberOfPages >= NumberOfPages && 86 (UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress) { 87 return InternalAllocPagesOnOneNode (Pages, NumberOfPages, MaxAddress); 88 } 89 } 90 return (UINTN)(-1); 91 } 92 93 /** 94 Internal Function. Allocate n pages from free page list at given address. 95 96 @param FreePageList The free page node. 97 @param NumberOfPages Number of pages to be allocated. 98 @param MaxAddress Request to allocate memory below this address. 99 100 @return Memory address of allocated pages. 101 102 **/ 103 UINTN 104 InternalAllocAddress ( 105 IN OUT LIST_ENTRY *FreePageList, 106 IN UINTN NumberOfPages, 107 IN UINTN Address 108 ) 109 { 110 UINTN EndAddress; 111 LIST_ENTRY *Node; 112 FREE_PAGE_LIST *Pages; 113 114 if ((Address & EFI_PAGE_MASK) != 0) { 115 return ~Address; 116 } 117 118 EndAddress = Address + EFI_PAGES_TO_SIZE (NumberOfPages); 119 for (Node = FreePageList->BackLink; Node!= FreePageList; Node = Node->BackLink) { 120 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); 121 if ((UINTN)Pages <= Address) { 122 if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) < EndAddress) { 123 break; 124 } 125 return InternalAllocPagesOnOneNode (Pages, NumberOfPages, EndAddress); 126 } 127 } 128 return ~Address; 129 } 130 131 /** 132 Allocates pages from the memory map. 133 134 @param Type The type of allocation to perform. 135 @param MemoryType The type of memory to turn the allocated pages 136 into. 137 @param NumberOfPages The number of pages to allocate. 138 @param Memory A pointer to receive the base allocated memory 139 address. 140 141 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec. 142 @retval EFI_NOT_FOUND Could not allocate pages match the requirement. 143 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. 144 @retval EFI_SUCCESS Pages successfully allocated. 145 146 **/ 147 EFI_STATUS 148 EFIAPI 149 SmmInternalAllocatePages ( 150 IN EFI_ALLOCATE_TYPE Type, 151 IN EFI_MEMORY_TYPE MemoryType, 152 IN UINTN NumberOfPages, 153 OUT EFI_PHYSICAL_ADDRESS *Memory 154 ) 155 { 156 UINTN RequestedAddress; 157 158 if (MemoryType != EfiRuntimeServicesCode && 159 MemoryType != EfiRuntimeServicesData) { 160 return EFI_INVALID_PARAMETER; 161 } 162 163 if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) { 164 return EFI_OUT_OF_RESOURCES; 165 } 166 167 // 168 // We don't track memory type in SMM 169 // 170 RequestedAddress = (UINTN)*Memory; 171 switch (Type) { 172 case AllocateAnyPages: 173 RequestedAddress = (UINTN)(-1); 174 case AllocateMaxAddress: 175 *Memory = InternalAllocMaxAddress ( 176 &mSmmMemoryMap, 177 NumberOfPages, 178 RequestedAddress 179 ); 180 if (*Memory == (UINTN)-1) { 181 return EFI_OUT_OF_RESOURCES; 182 } 183 break; 184 case AllocateAddress: 185 *Memory = InternalAllocAddress ( 186 &mSmmMemoryMap, 187 NumberOfPages, 188 RequestedAddress 189 ); 190 if (*Memory != RequestedAddress) { 191 return EFI_NOT_FOUND; 192 } 193 break; 194 default: 195 return EFI_INVALID_PARAMETER; 196 } 197 return EFI_SUCCESS; 198 } 199 200 /** 201 Allocates pages from the memory map. 202 203 @param Type The type of allocation to perform. 204 @param MemoryType The type of memory to turn the allocated pages 205 into. 206 @param NumberOfPages The number of pages to allocate. 207 @param Memory A pointer to receive the base allocated memory 208 address. 209 210 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec. 211 @retval EFI_NOT_FOUND Could not allocate pages match the requirement. 212 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. 213 @retval EFI_SUCCESS Pages successfully allocated. 214 215 **/ 216 EFI_STATUS 217 EFIAPI 218 SmmAllocatePages ( 219 IN EFI_ALLOCATE_TYPE Type, 220 IN EFI_MEMORY_TYPE MemoryType, 221 IN UINTN NumberOfPages, 222 OUT EFI_PHYSICAL_ADDRESS *Memory 223 ) 224 { 225 EFI_STATUS Status; 226 227 Status = SmmInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory); 228 if (!EFI_ERROR (Status)) { 229 SmmCoreUpdateProfile ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionAllocatePages, MemoryType, EFI_PAGES_TO_SIZE (NumberOfPages), (VOID *) (UINTN) *Memory); 230 } 231 return Status; 232 } 233 234 /** 235 Internal Function. Merge two adjacent nodes. 236 237 @param First The first of two nodes to merge. 238 239 @return Pointer to node after merge (if success) or pointer to next node (if fail). 240 241 **/ 242 FREE_PAGE_LIST * 243 InternalMergeNodes ( 244 IN FREE_PAGE_LIST *First 245 ) 246 { 247 FREE_PAGE_LIST *Next; 248 249 Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link); 250 ASSERT ( 251 TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages); 252 253 if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) { 254 First->NumberOfPages += Next->NumberOfPages; 255 RemoveEntryList (&Next->Link); 256 Next = First; 257 } 258 return Next; 259 } 260 261 /** 262 Frees previous allocated pages. 263 264 @param Memory Base address of memory being freed. 265 @param NumberOfPages The number of pages to free. 266 267 @retval EFI_NOT_FOUND Could not find the entry that covers the range. 268 @retval EFI_INVALID_PARAMETER Address not aligned. 269 @return EFI_SUCCESS Pages successfully freed. 270 271 **/ 272 EFI_STATUS 273 EFIAPI 274 SmmInternalFreePages ( 275 IN EFI_PHYSICAL_ADDRESS Memory, 276 IN UINTN NumberOfPages 277 ) 278 { 279 LIST_ENTRY *Node; 280 FREE_PAGE_LIST *Pages; 281 282 if ((Memory & EFI_PAGE_MASK) != 0) { 283 return EFI_INVALID_PARAMETER; 284 } 285 286 Pages = NULL; 287 Node = mSmmMemoryMap.ForwardLink; 288 while (Node != &mSmmMemoryMap) { 289 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); 290 if (Memory < (UINTN)Pages) { 291 break; 292 } 293 Node = Node->ForwardLink; 294 } 295 296 if (Node != &mSmmMemoryMap && 297 Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) { 298 return EFI_INVALID_PARAMETER; 299 } 300 301 if (Node->BackLink != &mSmmMemoryMap) { 302 Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link); 303 if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) { 304 return EFI_INVALID_PARAMETER; 305 } 306 } 307 308 Pages = (FREE_PAGE_LIST*)(UINTN)Memory; 309 Pages->NumberOfPages = NumberOfPages; 310 InsertTailList (Node, &Pages->Link); 311 312 if (Pages->Link.BackLink != &mSmmMemoryMap) { 313 Pages = InternalMergeNodes ( 314 BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link) 315 ); 316 } 317 318 if (Node != &mSmmMemoryMap) { 319 InternalMergeNodes (Pages); 320 } 321 322 return EFI_SUCCESS; 323 } 324 325 /** 326 Frees previous allocated pages. 327 328 @param Memory Base address of memory being freed. 329 @param NumberOfPages The number of pages to free. 330 331 @retval EFI_NOT_FOUND Could not find the entry that covers the range. 332 @retval EFI_INVALID_PARAMETER Address not aligned. 333 @return EFI_SUCCESS Pages successfully freed. 334 335 **/ 336 EFI_STATUS 337 EFIAPI 338 SmmFreePages ( 339 IN EFI_PHYSICAL_ADDRESS Memory, 340 IN UINTN NumberOfPages 341 ) 342 { 343 EFI_STATUS Status; 344 345 Status = SmmInternalFreePages (Memory, NumberOfPages); 346 if (!EFI_ERROR (Status)) { 347 SmmCoreUpdateProfile ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionFreePages, 0, EFI_PAGES_TO_SIZE (NumberOfPages), (VOID *) (UINTN) Memory); 348 } 349 return Status; 350 } 351 352 /** 353 Add free SMRAM region for use by memory service. 354 355 @param MemBase Base address of memory region. 356 @param MemLength Length of the memory region. 357 @param Type Memory type. 358 @param Attributes Memory region state. 359 360 **/ 361 VOID 362 SmmAddMemoryRegion ( 363 IN EFI_PHYSICAL_ADDRESS MemBase, 364 IN UINT64 MemLength, 365 IN EFI_MEMORY_TYPE Type, 366 IN UINT64 Attributes 367 ) 368 { 369 UINTN AlignedMemBase; 370 371 // 372 // Do not add memory regions that is already allocated, needs testing, or needs ECC initialization 373 // 374 if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) { 375 return; 376 } 377 378 // 379 // Align range on an EFI_PAGE_SIZE boundary 380 // 381 AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; 382 MemLength -= AlignedMemBase - MemBase; 383 SmmFreePages (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength)); 384 } 385