Home | History | Annotate | Download | only in DebugUncachedMemoryAllocationLib
      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