1 /** @file 2 Debug version of the UncachedMemoryAllocation lib that uses the VirtualUncachedPages 3 protocol, produced by the DXE CPU driver, to produce debuggable uncached memory buffers. 4 5 The DMA rules for EFI contain the concept of a PCI (DMA master) address for memory and 6 a CPU (C code) address for the memory buffer that don't have to be the same. There seem to 7 be common errors out there with folks mixing up the two addresses. This library causes 8 the PCI (DMA master) address to not be mapped into system memory so if the CPU (C code) 9 uses the wrong pointer it will generate a page fault. The CPU (C code) version of the buffer 10 has a virtual address that does not match the physical address. The virtual address has 11 PcdArmUncachedMemoryMask ored into the physical address. 12 13 Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR> 14 15 This program and the accompanying materials 16 are licensed and made available under the terms and conditions of the BSD License 17 which accompanies this distribution. The full text of the license may be found at 18 http://opensource.org/licenses/bsd-license.php 19 20 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 21 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 22 23 **/ 24 25 #include <Base.h> 26 #include <Library/BaseLib.h> 27 #include <Library/BaseMemoryLib.h> 28 #include <Library/MemoryAllocationLib.h> 29 #include <Library/DebugLib.h> 30 #include <Library/UefiBootServicesTableLib.h> 31 #include <Library/UncachedMemoryAllocationLib.h> 32 #include <Library/PcdLib.h> 33 #include <Library/ArmLib.h> 34 35 #include <Protocol/Cpu.h> 36 #include <Protocol/VirtualUncachedPages.h> 37 38 VOID * 39 UncachedInternalAllocatePages ( 40 IN EFI_MEMORY_TYPE MemoryType, 41 IN UINTN Pages 42 ); 43 44 VOID * 45 UncachedInternalAllocateAlignedPages ( 46 IN EFI_MEMORY_TYPE MemoryType, 47 IN UINTN Pages, 48 IN UINTN Alignment 49 ); 50 51 52 53 EFI_CPU_ARCH_PROTOCOL *gDebugUncachedCpu; 54 VIRTUAL_UNCACHED_PAGES_PROTOCOL *gVirtualUncachedPages; 55 56 // 57 // Assume all of memory has the same cache attributes, unless we do our magic 58 // 59 UINT64 gAttributes; 60 61 typedef struct { 62 VOID *Buffer; 63 VOID *Allocation; 64 UINTN Pages; 65 LIST_ENTRY Link; 66 } FREE_PAGE_NODE; 67 68 LIST_ENTRY mPageList = INITIALIZE_LIST_HEAD_VARIABLE (mPageList); 69 70 VOID 71 AddPagesToList ( 72 IN VOID *Buffer, 73 IN VOID *Allocation, 74 UINTN Pages 75 ) 76 { 77 FREE_PAGE_NODE *NewNode; 78 79 NewNode = AllocatePool (sizeof (LIST_ENTRY)); 80 if (NewNode == NULL) { 81 ASSERT (FALSE); 82 return; 83 } 84 85 NewNode->Buffer = Buffer; 86 NewNode->Allocation = Allocation; 87 NewNode->Pages = Pages; 88 89 InsertTailList (&mPageList, &NewNode->Link); 90 } 91 92 93 VOID 94 RemovePagesFromList ( 95 IN VOID *Buffer, 96 OUT VOID **Allocation, 97 OUT UINTN *Pages 98 ) 99 { 100 LIST_ENTRY *Link; 101 FREE_PAGE_NODE *OldNode; 102 103 *Allocation = NULL; 104 *Pages = 0; 105 106 for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) { 107 OldNode = BASE_CR (Link, FREE_PAGE_NODE, Link); 108 if (OldNode->Buffer == Buffer) { 109 *Allocation = OldNode->Allocation; 110 *Pages = OldNode->Pages; 111 112 RemoveEntryList (&OldNode->Link); 113 FreePool (OldNode); 114 return; 115 } 116 } 117 118 return; 119 } 120 121 122 123 EFI_PHYSICAL_ADDRESS 124 ConvertToPhysicalAddress ( 125 IN VOID *VirtualAddress 126 ) 127 { 128 UINTN UncachedMemoryMask = (UINTN)PcdGet64 (PcdArmUncachedMemoryMask); 129 UINTN PhysicalAddress; 130 131 PhysicalAddress = (UINTN)VirtualAddress & ~UncachedMemoryMask; 132 133 return (EFI_PHYSICAL_ADDRESS)PhysicalAddress; 134 } 135 136 137 VOID * 138 ConvertToUncachedAddress ( 139 IN VOID *Address 140 ) 141 { 142 UINTN UncachedMemoryMask = (UINTN)PcdGet64 (PcdArmUncachedMemoryMask); 143 UINTN UncachedAddress; 144 145 UncachedAddress = (UINTN)Address | UncachedMemoryMask; 146 147 return (VOID *)UncachedAddress; 148 } 149 150 151 152 VOID * 153 UncachedInternalAllocatePages ( 154 IN EFI_MEMORY_TYPE MemoryType, 155 IN UINTN Pages 156 ) 157 { 158 return UncachedInternalAllocateAlignedPages (MemoryType, Pages, EFI_PAGE_SIZE); 159 } 160 161 162 VOID * 163 EFIAPI 164 UncachedAllocatePages ( 165 IN UINTN Pages 166 ) 167 { 168 return UncachedInternalAllocatePages (EfiBootServicesData, Pages); 169 } 170 171 VOID * 172 EFIAPI 173 UncachedAllocateRuntimePages ( 174 IN UINTN Pages 175 ) 176 { 177 return UncachedInternalAllocatePages (EfiRuntimeServicesData, Pages); 178 } 179 180 VOID * 181 EFIAPI 182 UncachedAllocateReservedPages ( 183 IN UINTN Pages 184 ) 185 { 186 return UncachedInternalAllocatePages (EfiReservedMemoryType, Pages); 187 } 188 189 190 191 VOID 192 EFIAPI 193 UncachedFreePages ( 194 IN VOID *Buffer, 195 IN UINTN Pages 196 ) 197 { 198 UncachedFreeAlignedPages (Buffer, Pages); 199 return; 200 } 201 202 203 VOID * 204 UncachedInternalAllocateAlignedPages ( 205 IN EFI_MEMORY_TYPE MemoryType, 206 IN UINTN Pages, 207 IN UINTN Alignment 208 ) 209 { 210 EFI_STATUS Status; 211 EFI_PHYSICAL_ADDRESS Memory; 212 EFI_PHYSICAL_ADDRESS AlignedMemory; 213 UINTN AlignmentMask; 214 UINTN UnalignedPages; 215 UINTN RealPages; 216 217 // 218 // Alignment must be a power of two or zero. 219 // 220 ASSERT ((Alignment & (Alignment - 1)) == 0); 221 222 if (Pages == 0) { 223 return NULL; 224 } 225 if (Alignment > EFI_PAGE_SIZE) { 226 // 227 // Caculate the total number of pages since alignment is larger than page size. 228 // 229 AlignmentMask = Alignment - 1; 230 RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment); 231 // 232 // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow. 233 // 234 ASSERT (RealPages > Pages); 235 236 Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, RealPages, &Memory); 237 if (EFI_ERROR (Status)) { 238 return NULL; 239 } 240 AlignedMemory = ((UINTN) Memory + AlignmentMask) & ~AlignmentMask; 241 UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN) Memory); 242 if (UnalignedPages > 0) { 243 // 244 // Free first unaligned page(s). 245 // 246 Status = gBS->FreePages (Memory, UnalignedPages); 247 ASSERT_EFI_ERROR (Status); 248 } 249 Memory = (EFI_PHYSICAL_ADDRESS) (AlignedMemory + EFI_PAGES_TO_SIZE (Pages)); 250 UnalignedPages = RealPages - Pages - UnalignedPages; 251 if (UnalignedPages > 0) { 252 // 253 // Free last unaligned page(s). 254 // 255 Status = gBS->FreePages (Memory, UnalignedPages); 256 ASSERT_EFI_ERROR (Status); 257 } 258 } else { 259 // 260 // Do not over-allocate pages in this case. 261 // 262 Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, Pages, &Memory); 263 if (EFI_ERROR (Status)) { 264 return NULL; 265 } 266 AlignedMemory = (UINTN) Memory; 267 } 268 269 Status = gVirtualUncachedPages->ConvertPages (gVirtualUncachedPages, AlignedMemory, Pages * EFI_PAGE_SIZE, PcdGet64 (PcdArmUncachedMemoryMask), &gAttributes); 270 if (EFI_ERROR (Status)) { 271 return NULL; 272 } 273 274 AlignedMemory = (EFI_PHYSICAL_ADDRESS)(UINTN)ConvertToUncachedAddress ((VOID *)(UINTN)AlignedMemory); 275 276 return (VOID *)(UINTN)AlignedMemory; 277 } 278 279 280 VOID 281 EFIAPI 282 UncachedFreeAlignedPages ( 283 IN VOID *Buffer, 284 IN UINTN Pages 285 ) 286 { 287 EFI_STATUS Status; 288 EFI_PHYSICAL_ADDRESS Memory; 289 290 ASSERT (Pages != 0); 291 292 Memory = ConvertToPhysicalAddress (Buffer); 293 294 Status = gVirtualUncachedPages->RevertPages (gVirtualUncachedPages, Memory, Pages * EFI_PAGE_SIZE, PcdGet64 (PcdArmUncachedMemoryMask), gAttributes); 295 296 297 Status = gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) Memory, Pages); 298 ASSERT_EFI_ERROR (Status); 299 } 300 301 302 303 304 VOID * 305 UncachedInternalAllocateAlignedPool ( 306 IN EFI_MEMORY_TYPE PoolType, 307 IN UINTN AllocationSize, 308 IN UINTN Alignment 309 ) 310 { 311 VOID *AlignedAddress; 312 313 // 314 // Alignment must be a power of two or zero. 315 // 316 ASSERT ((Alignment & (Alignment - 1)) == 0); 317 318 if (Alignment < EFI_PAGE_SIZE) { 319 Alignment = EFI_PAGE_SIZE; 320 } 321 322 AlignedAddress = UncachedInternalAllocateAlignedPages (PoolType, EFI_SIZE_TO_PAGES (AllocationSize), Alignment); 323 if (AlignedAddress == NULL) { 324 return NULL; 325 } 326 327 AddPagesToList ((VOID *)(UINTN)ConvertToPhysicalAddress (AlignedAddress), (VOID *)(UINTN)AlignedAddress, EFI_SIZE_TO_PAGES (AllocationSize)); 328 329 return (VOID *) AlignedAddress; 330 } 331 332 VOID * 333 EFIAPI 334 UncachedAllocateAlignedPool ( 335 IN UINTN AllocationSize, 336 IN UINTN Alignment 337 ) 338 { 339 return UncachedInternalAllocateAlignedPool (EfiBootServicesData, AllocationSize, Alignment); 340 } 341 342 VOID * 343 EFIAPI 344 UncachedAllocateAlignedRuntimePool ( 345 IN UINTN AllocationSize, 346 IN UINTN Alignment 347 ) 348 { 349 return UncachedInternalAllocateAlignedPool (EfiRuntimeServicesData, AllocationSize, Alignment); 350 } 351 352 VOID * 353 EFIAPI 354 UncachedAllocateAlignedReservedPool ( 355 IN UINTN AllocationSize, 356 IN UINTN Alignment 357 ) 358 { 359 return UncachedInternalAllocateAlignedPool (EfiReservedMemoryType, AllocationSize, Alignment); 360 } 361 362 VOID * 363 UncachedInternalAllocateAlignedZeroPool ( 364 IN EFI_MEMORY_TYPE PoolType, 365 IN UINTN AllocationSize, 366 IN UINTN Alignment 367 ) 368 { 369 VOID *Memory; 370 Memory = UncachedInternalAllocateAlignedPool (PoolType, AllocationSize, Alignment); 371 if (Memory != NULL) { 372 Memory = ZeroMem (Memory, AllocationSize); 373 } 374 return Memory; 375 } 376 377 VOID * 378 EFIAPI 379 UncachedAllocateAlignedZeroPool ( 380 IN UINTN AllocationSize, 381 IN UINTN Alignment 382 ) 383 { 384 return UncachedInternalAllocateAlignedZeroPool (EfiBootServicesData, AllocationSize, Alignment); 385 } 386 387 VOID * 388 EFIAPI 389 UncachedAllocateAlignedRuntimeZeroPool ( 390 IN UINTN AllocationSize, 391 IN UINTN Alignment 392 ) 393 { 394 return UncachedInternalAllocateAlignedZeroPool (EfiRuntimeServicesData, AllocationSize, Alignment); 395 } 396 397 VOID * 398 EFIAPI 399 UncachedAllocateAlignedReservedZeroPool ( 400 IN UINTN AllocationSize, 401 IN UINTN Alignment 402 ) 403 { 404 return UncachedInternalAllocateAlignedZeroPool (EfiReservedMemoryType, AllocationSize, Alignment); 405 } 406 407 VOID * 408 UncachedInternalAllocateAlignedCopyPool ( 409 IN EFI_MEMORY_TYPE PoolType, 410 IN UINTN AllocationSize, 411 IN CONST VOID *Buffer, 412 IN UINTN Alignment 413 ) 414 { 415 VOID *Memory; 416 417 ASSERT (Buffer != NULL); 418 ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN) Buffer + 1)); 419 420 Memory = UncachedInternalAllocateAlignedPool (PoolType, AllocationSize, Alignment); 421 if (Memory != NULL) { 422 Memory = CopyMem (Memory, Buffer, AllocationSize); 423 } 424 return Memory; 425 } 426 427 VOID * 428 EFIAPI 429 UncachedAllocateAlignedCopyPool ( 430 IN UINTN AllocationSize, 431 IN CONST VOID *Buffer, 432 IN UINTN Alignment 433 ) 434 { 435 return UncachedInternalAllocateAlignedCopyPool (EfiBootServicesData, AllocationSize, Buffer, Alignment); 436 } 437 438 VOID * 439 EFIAPI 440 UncachedAllocateAlignedRuntimeCopyPool ( 441 IN UINTN AllocationSize, 442 IN CONST VOID *Buffer, 443 IN UINTN Alignment 444 ) 445 { 446 return UncachedInternalAllocateAlignedCopyPool (EfiRuntimeServicesData, AllocationSize, Buffer, Alignment); 447 } 448 449 VOID * 450 EFIAPI 451 UncachedAllocateAlignedReservedCopyPool ( 452 IN UINTN AllocationSize, 453 IN CONST VOID *Buffer, 454 IN UINTN Alignment 455 ) 456 { 457 return UncachedInternalAllocateAlignedCopyPool (EfiReservedMemoryType, AllocationSize, Buffer, Alignment); 458 } 459 460 VOID 461 EFIAPI 462 UncachedFreeAlignedPool ( 463 IN VOID *Buffer 464 ) 465 { 466 VOID *Allocation; 467 UINTN Pages; 468 469 RemovePagesFromList (Buffer, &Allocation, &Pages); 470 471 UncachedFreePages (Allocation, Pages); 472 } 473 474 VOID * 475 UncachedInternalAllocatePool ( 476 IN EFI_MEMORY_TYPE MemoryType, 477 IN UINTN AllocationSize 478 ) 479 { 480 UINTN CacheLineLength = ArmDataCacheLineLength (); 481 return UncachedInternalAllocateAlignedPool (MemoryType, AllocationSize, CacheLineLength); 482 } 483 484 VOID * 485 EFIAPI 486 UncachedAllocatePool ( 487 IN UINTN AllocationSize 488 ) 489 { 490 return UncachedInternalAllocatePool (EfiBootServicesData, AllocationSize); 491 } 492 493 VOID * 494 EFIAPI 495 UncachedAllocateRuntimePool ( 496 IN UINTN AllocationSize 497 ) 498 { 499 return UncachedInternalAllocatePool (EfiRuntimeServicesData, AllocationSize); 500 } 501 502 VOID * 503 EFIAPI 504 UncachedAllocateReservedPool ( 505 IN UINTN AllocationSize 506 ) 507 { 508 return UncachedInternalAllocatePool (EfiReservedMemoryType, AllocationSize); 509 } 510 511 VOID * 512 UncachedInternalAllocateZeroPool ( 513 IN EFI_MEMORY_TYPE PoolType, 514 IN UINTN AllocationSize 515 ) 516 { 517 VOID *Memory; 518 519 Memory = UncachedInternalAllocatePool (PoolType, AllocationSize); 520 if (Memory != NULL) { 521 Memory = ZeroMem (Memory, AllocationSize); 522 } 523 return Memory; 524 } 525 526 VOID * 527 EFIAPI 528 UncachedAllocateZeroPool ( 529 IN UINTN AllocationSize 530 ) 531 { 532 return UncachedInternalAllocateZeroPool (EfiBootServicesData, AllocationSize); 533 } 534 535 VOID * 536 EFIAPI 537 UncachedAllocateRuntimeZeroPool ( 538 IN UINTN AllocationSize 539 ) 540 { 541 return UncachedInternalAllocateZeroPool (EfiRuntimeServicesData, AllocationSize); 542 } 543 544 VOID * 545 EFIAPI 546 UncachedAllocateReservedZeroPool ( 547 IN UINTN AllocationSize 548 ) 549 { 550 return UncachedInternalAllocateZeroPool (EfiReservedMemoryType, AllocationSize); 551 } 552 553 VOID * 554 UncachedInternalAllocateCopyPool ( 555 IN EFI_MEMORY_TYPE PoolType, 556 IN UINTN AllocationSize, 557 IN CONST VOID *Buffer 558 ) 559 { 560 VOID *Memory; 561 562 ASSERT (Buffer != NULL); 563 ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN) Buffer + 1)); 564 565 Memory = UncachedInternalAllocatePool (PoolType, AllocationSize); 566 if (Memory != NULL) { 567 Memory = CopyMem (Memory, Buffer, AllocationSize); 568 } 569 return Memory; 570 } 571 572 VOID * 573 EFIAPI 574 UncachedAllocateCopyPool ( 575 IN UINTN AllocationSize, 576 IN CONST VOID *Buffer 577 ) 578 { 579 return UncachedInternalAllocateCopyPool (EfiBootServicesData, AllocationSize, Buffer); 580 } 581 582 VOID * 583 EFIAPI 584 UncachedAllocateRuntimeCopyPool ( 585 IN UINTN AllocationSize, 586 IN CONST VOID *Buffer 587 ) 588 { 589 return UncachedInternalAllocateCopyPool (EfiRuntimeServicesData, AllocationSize, Buffer); 590 } 591 592 VOID * 593 EFIAPI 594 UncachedAllocateReservedCopyPool ( 595 IN UINTN AllocationSize, 596 IN CONST VOID *Buffer 597 ) 598 { 599 return UncachedInternalAllocateCopyPool (EfiReservedMemoryType, AllocationSize, Buffer); 600 } 601 602 VOID 603 EFIAPI 604 UncachedFreePool ( 605 IN VOID *Buffer 606 ) 607 { 608 UncachedFreeAlignedPool (Buffer); 609 } 610 611 VOID 612 EFIAPI 613 UncachedSafeFreePool ( 614 IN VOID *Buffer 615 ) 616 { 617 if (Buffer != NULL) { 618 UncachedFreePool (Buffer); 619 Buffer = NULL; 620 } 621 } 622 623 /** 624 The constructor function caches the pointer of DXE Services Table. 625 626 The constructor function caches the pointer of DXE Services Table. 627 It will ASSERT() if that operation fails. 628 It will ASSERT() if the pointer of DXE Services Table is NULL. 629 It will always return EFI_SUCCESS. 630 631 @param ImageHandle The firmware allocated handle for the EFI image. 632 @param SystemTable A pointer to the EFI System Table. 633 634 @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. 635 636 **/ 637 EFI_STATUS 638 EFIAPI 639 DebugUncachedMemoryAllocationLibConstructor ( 640 IN EFI_HANDLE ImageHandle, 641 IN EFI_SYSTEM_TABLE *SystemTable 642 ) 643 { 644 EFI_STATUS Status; 645 646 Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gDebugUncachedCpu); 647 ASSERT_EFI_ERROR(Status); 648 649 Status = gBS->LocateProtocol (&gVirtualUncachedPagesProtocolGuid, NULL, (VOID **)&gVirtualUncachedPages); 650 ASSERT_EFI_ERROR(Status); 651 652 return Status; 653 } 654 655 656 657