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