mirror of
https://github.com/acidanthera/audk.git
synced 2025-08-18 08:08:09 +02:00
Instead of relying on raising the TPL to protect the critical sections that manipulate the global bitmask that keeps track of bounce buffer allocations, use compare-and-exchange to manage the global variable, and tweak the logic to line up with that. Given that IoMmuDxe implements a singleton protocol that is shared between multiple drivers, and considering the elaborate and confusing requirements in the UEFP spec regarding TPL levels at which protocol methods may be invoked, not relying on TPL levels at all is a more robust approach in this case. Link: https://bugzilla.redhat.com/show_bug.cgi?id=2211060 Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Acked-by: Pedro Falcato <pedro.falcato@gmail.com>
521 lines
16 KiB
C
521 lines
16 KiB
C
/** @file
|
|
|
|
Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/MemEncryptSevLib.h>
|
|
#include <Library/MemEncryptTdxLib.h>
|
|
#include <Library/PcdLib.h>
|
|
#include <Library/SynchronizationLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include "IoMmuInternal.h"
|
|
|
|
extern BOOLEAN mReservedSharedMemSupported;
|
|
|
|
#define SIZE_OF_MEM_RANGE(MemRange) (MemRange->HeaderSize + MemRange->DataSize)
|
|
|
|
#define RESERVED_MEM_BITMAP_4K_MASK 0xf
|
|
#define RESERVED_MEM_BITMAP_32K_MASK 0xff0
|
|
#define RESERVED_MEM_BITMAP_128K_MASK 0x3000
|
|
#define RESERVED_MEM_BITMAP_1M_MASK 0x40000
|
|
#define RESERVED_MEM_BITMAP_2M_MASK 0x180000
|
|
#define RESERVED_MEM_BITMAP_MASK 0x1fffff
|
|
|
|
/**
|
|
* mReservedMemRanges describes the layout of the reserved memory.
|
|
* The reserved memory consists of disfferent size of memory region.
|
|
* The pieces of memory with the same size are managed by one entry
|
|
* in the mReservedMemRanges. All the pieces of memories are managed by
|
|
* mReservedMemBitmap which is a UINT32. It means it can manage at most
|
|
* 32 pieces of memory. Because of the layout of CommonBuffer
|
|
* (1-page header + n-page data), a piece of reserved memory consists of
|
|
* 2 parts: Header + Data.
|
|
*
|
|
* So put all these together, mReservedMemRanges and mReservedMemBitmap
|
|
* are designed to manage the reserved memory.
|
|
*
|
|
* Use the second entry of mReservedMemRanges as an example.
|
|
* { RESERVED_MEM_BITMAP_32K_MASK, 4, 8, SIZE_32KB, SIZE_4KB, 0 },
|
|
* - RESERVED_MEM_BITMAP_32K_MASK is 0xff0. It means bit4-11 in mReservedMemBitmap
|
|
* is reserved for 32K size memory.
|
|
* - 4 is the shift of mReservedMemBitmap.
|
|
* - 8 means there are 8 pieces of 32K size memory.
|
|
* - SIZE_32KB indicates the size of Data part.
|
|
* - SIZE_4KB is the size of Header part.
|
|
* - 0 is the start address of this memory range which will be populated when
|
|
* the reserved memory is initialized.
|
|
*
|
|
* The size and count of the memory region are derived from the experience. For
|
|
* a typical grub boot, there are about 5100 IoMmu/DMA operation. Most of these
|
|
* DMA operation require the memory with size less than 32K (~5080). But we find
|
|
* in grub boot there may be 2 DMA operation which require for the memory larger
|
|
* than 1M. And these 2 DMA operation occur concurrently. So we reserve 2 pieces
|
|
* of memory with size of SIZE_2MB. This is for the best boot performance.
|
|
*
|
|
* If all the reserved memory are exausted, then it will fall back to the legacy
|
|
* memory allocation as before.
|
|
*/
|
|
STATIC IOMMU_RESERVED_MEM_RANGE mReservedMemRanges[] = {
|
|
{ RESERVED_MEM_BITMAP_4K_MASK, 0, 4, SIZE_4KB, SIZE_4KB, 0 },
|
|
{ RESERVED_MEM_BITMAP_32K_MASK, 4, 8, SIZE_32KB, SIZE_4KB, 0 },
|
|
{ RESERVED_MEM_BITMAP_128K_MASK, 12, 2, SIZE_128KB, SIZE_4KB, 0 },
|
|
{ RESERVED_MEM_BITMAP_1M_MASK, 14, 1, SIZE_1MB, SIZE_4KB, 0 },
|
|
{ RESERVED_MEM_BITMAP_2M_MASK, 15, 2, SIZE_2MB, SIZE_4KB, 0 },
|
|
};
|
|
|
|
//
|
|
// Bitmap of the allocation of reserved memory.
|
|
//
|
|
STATIC UINT32 mReservedMemBitmap = 0;
|
|
|
|
//
|
|
// Start address of the reserved memory region.
|
|
//
|
|
STATIC EFI_PHYSICAL_ADDRESS mReservedSharedMemAddress = 0;
|
|
|
|
//
|
|
// Total size of the reserved memory region.
|
|
//
|
|
STATIC UINT32 mReservedSharedMemSize = 0;
|
|
|
|
/**
|
|
* Calculate the size of reserved memory.
|
|
*
|
|
* @retval UINT32 Size of the reserved memory
|
|
*/
|
|
STATIC
|
|
UINT32
|
|
CalcuateReservedMemSize (
|
|
VOID
|
|
)
|
|
{
|
|
UINT32 Index;
|
|
IOMMU_RESERVED_MEM_RANGE *MemRange;
|
|
|
|
if (mReservedSharedMemSize != 0) {
|
|
return mReservedSharedMemSize;
|
|
}
|
|
|
|
for (Index = 0; Index < ARRAY_SIZE (mReservedMemRanges); Index++) {
|
|
MemRange = &mReservedMemRanges[Index];
|
|
mReservedSharedMemSize += (SIZE_OF_MEM_RANGE (MemRange) * MemRange->Slots);
|
|
}
|
|
|
|
return mReservedSharedMemSize;
|
|
}
|
|
|
|
/**
|
|
* Allocate a memory region and convert it to be shared. This memory region will be
|
|
* used in the DMA operation.
|
|
*
|
|
* The pre-alloc memory contains pieces of memory regions with different size. The
|
|
* allocation of the shared memory regions are indicated by a 32-bit bitmap (mReservedMemBitmap).
|
|
*
|
|
* The memory regions are consumed by IoMmuAllocateBuffer (in which CommonBuffer is allocated) and
|
|
* IoMmuMap (in which bounce buffer is allocated).
|
|
*
|
|
* The CommonBuffer contains 2 parts, one page for CommonBufferHeader which is private memory,
|
|
* the other part is shared memory. So the layout of a piece of memory region after initialization
|
|
* looks like:
|
|
*
|
|
* |------------|----------------------------|
|
|
* | Header | Data | <-- a piece of pre-alloc memory region
|
|
* | 4k, private| 4k/32k/128k/etc, shared |
|
|
* |-----------------------------------------|
|
|
*
|
|
* @retval EFI_SUCCESS Successfully initialize the reserved memory.
|
|
* @retval EFI_UNSUPPORTED This feature is not supported.
|
|
*/
|
|
EFI_STATUS
|
|
IoMmuInitReservedSharedMem (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 Index1, Index2;
|
|
UINTN TotalPages;
|
|
IOMMU_RESERVED_MEM_RANGE *MemRange;
|
|
EFI_PHYSICAL_ADDRESS PhysicalAddress;
|
|
UINT64 SharedAddress;
|
|
|
|
if (!mReservedSharedMemSupported) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
TotalPages = EFI_SIZE_TO_PAGES (CalcuateReservedMemSize ());
|
|
|
|
PhysicalAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePages (TotalPages);
|
|
DEBUG ((
|
|
DEBUG_VERBOSE,
|
|
"%a: ReservedMem (%d pages) address = 0x%llx\n",
|
|
__func__,
|
|
TotalPages,
|
|
PhysicalAddress
|
|
));
|
|
|
|
mReservedMemBitmap = 0;
|
|
mReservedSharedMemAddress = PhysicalAddress;
|
|
|
|
for (Index1 = 0; Index1 < ARRAY_SIZE (mReservedMemRanges); Index1++) {
|
|
MemRange = &mReservedMemRanges[Index1];
|
|
MemRange->StartAddressOfMemRange = PhysicalAddress;
|
|
|
|
for (Index2 = 0; Index2 < MemRange->Slots; Index2++) {
|
|
SharedAddress = (UINT64)(UINTN)(MemRange->StartAddressOfMemRange + Index2 * SIZE_OF_MEM_RANGE (MemRange) + MemRange->HeaderSize);
|
|
|
|
if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
|
|
Status = MemEncryptSevClearPageEncMask (
|
|
0,
|
|
SharedAddress,
|
|
EFI_SIZE_TO_PAGES (MemRange->DataSize)
|
|
);
|
|
ASSERT (!EFI_ERROR (Status));
|
|
} else if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
|
|
Status = MemEncryptTdxSetPageSharedBit (
|
|
0,
|
|
SharedAddress,
|
|
EFI_SIZE_TO_PAGES (MemRange->DataSize)
|
|
);
|
|
ASSERT (!EFI_ERROR (Status));
|
|
} else {
|
|
ASSERT (FALSE);
|
|
}
|
|
}
|
|
|
|
PhysicalAddress += (MemRange->Slots * SIZE_OF_MEM_RANGE (MemRange));
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Release the pre-alloc shared memory.
|
|
*
|
|
* @retval EFI_SUCCESS Successfully release the shared memory
|
|
*/
|
|
EFI_STATUS
|
|
IoMmuReleaseReservedSharedMem (
|
|
BOOLEAN MemoryMapLocked
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 Index1, Index2;
|
|
IOMMU_RESERVED_MEM_RANGE *MemRange;
|
|
UINT64 SharedAddress;
|
|
|
|
if (!mReservedSharedMemSupported) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
for (Index1 = 0; Index1 < ARRAY_SIZE (mReservedMemRanges); Index1++) {
|
|
MemRange = &mReservedMemRanges[Index1];
|
|
for (Index2 = 0; Index2 < MemRange->Slots; Index2++) {
|
|
SharedAddress = (UINT64)(UINTN)(MemRange->StartAddressOfMemRange + Index2 * SIZE_OF_MEM_RANGE (MemRange) + MemRange->HeaderSize);
|
|
|
|
if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
|
|
Status = MemEncryptSevSetPageEncMask (
|
|
0,
|
|
SharedAddress,
|
|
EFI_SIZE_TO_PAGES (MemRange->DataSize)
|
|
);
|
|
ASSERT (!EFI_ERROR (Status));
|
|
} else if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
|
|
Status = MemEncryptTdxClearPageSharedBit (
|
|
0,
|
|
SharedAddress,
|
|
EFI_SIZE_TO_PAGES (MemRange->DataSize)
|
|
);
|
|
ASSERT (!EFI_ERROR (Status));
|
|
} else {
|
|
ASSERT (FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!MemoryMapLocked) {
|
|
FreePages ((VOID *)(UINTN)mReservedSharedMemAddress, EFI_SIZE_TO_PAGES (CalcuateReservedMemSize ()));
|
|
mReservedSharedMemAddress = 0;
|
|
mReservedMemBitmap = 0;
|
|
}
|
|
|
|
mReservedSharedMemSupported = FALSE;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Allocate from the reserved memory pool.
|
|
* If the reserved shared memory is exausted or there is no suitalbe size, it turns
|
|
* to the LegacyAllocateBuffer.
|
|
*
|
|
* @param Type Allocate type
|
|
* @param MemoryType The memory type to be allocated
|
|
* @param Pages Pages to be allocated.
|
|
* @param ReservedMemBitmap Bitmap of the allocated memory region
|
|
* @param PhysicalAddress Pointer to the data part of allocated memory region
|
|
*
|
|
* @retval EFI_SUCCESS Successfully allocate the buffer
|
|
* @retval Other As the error code indicates
|
|
*/
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalAllocateBuffer (
|
|
IN EFI_ALLOCATE_TYPE Type,
|
|
IN EFI_MEMORY_TYPE MemoryType,
|
|
IN UINTN Pages,
|
|
OUT UINT32 *ReservedMemBit,
|
|
IN OUT EFI_PHYSICAL_ADDRESS *PhysicalAddress
|
|
)
|
|
{
|
|
UINT32 MemBitmap;
|
|
UINT32 ReservedMemBitmap;
|
|
UINT8 Index;
|
|
IOMMU_RESERVED_MEM_RANGE *MemRange;
|
|
UINTN PagesOfLastMemRange;
|
|
|
|
*ReservedMemBit = 0;
|
|
|
|
if (Pages == 0) {
|
|
ASSERT (FALSE);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (!mReservedSharedMemSupported) {
|
|
goto LegacyAllocateBuffer;
|
|
}
|
|
|
|
if (mReservedSharedMemAddress == 0) {
|
|
goto LegacyAllocateBuffer;
|
|
}
|
|
|
|
PagesOfLastMemRange = 0;
|
|
|
|
for (Index = 0; Index < ARRAY_SIZE (mReservedMemRanges); Index++) {
|
|
if ((Pages > PagesOfLastMemRange) && (Pages <= EFI_SIZE_TO_PAGES (mReservedMemRanges[Index].DataSize))) {
|
|
break;
|
|
}
|
|
|
|
PagesOfLastMemRange = EFI_SIZE_TO_PAGES (mReservedMemRanges[Index].DataSize);
|
|
}
|
|
|
|
if (Index == ARRAY_SIZE (mReservedMemRanges)) {
|
|
// There is no suitable size of reserved memory. Turn to legacy allocate.
|
|
goto LegacyAllocateBuffer;
|
|
}
|
|
|
|
MemRange = &mReservedMemRanges[Index];
|
|
|
|
do {
|
|
ReservedMemBitmap = mReservedMemBitmap;
|
|
|
|
if ((ReservedMemBitmap & MemRange->BitmapMask) == MemRange->BitmapMask) {
|
|
// The reserved memory is exhausted. Turn to legacy allocate.
|
|
goto LegacyAllocateBuffer;
|
|
}
|
|
|
|
MemBitmap = (ReservedMemBitmap & MemRange->BitmapMask) >> MemRange->Shift;
|
|
|
|
for (Index = 0; Index < MemRange->Slots; Index++) {
|
|
if ((MemBitmap & (UINT8)(1<<Index)) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT (Index != MemRange->Slots);
|
|
|
|
*PhysicalAddress = MemRange->StartAddressOfMemRange + Index * SIZE_OF_MEM_RANGE (MemRange) + MemRange->HeaderSize;
|
|
*ReservedMemBit = (UINT32)(1 << (Index + MemRange->Shift));
|
|
} while (ReservedMemBitmap != InterlockedCompareExchange32 (
|
|
&mReservedMemBitmap,
|
|
ReservedMemBitmap,
|
|
ReservedMemBitmap | *ReservedMemBit
|
|
));
|
|
|
|
DEBUG ((
|
|
DEBUG_VERBOSE,
|
|
"%a: range-size: %lx, start-address=0x%llx, pages=0x%llx, bits=0x%lx, bitmap: %lx => %lx\n",
|
|
__func__,
|
|
MemRange->DataSize,
|
|
*PhysicalAddress,
|
|
Pages,
|
|
*ReservedMemBit,
|
|
ReservedMemBitmap,
|
|
ReservedMemBitmap | *ReservedMemBit
|
|
));
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
LegacyAllocateBuffer:
|
|
|
|
*ReservedMemBit = 0;
|
|
return gBS->AllocatePages (Type, MemoryType, Pages, PhysicalAddress);
|
|
}
|
|
|
|
/**
|
|
* Allocate reserved shared memory for bounce buffer.
|
|
*
|
|
* @param Type Allocate type
|
|
* @param MemoryType The memory type to be allocated
|
|
* @param MapInfo Pointer to the MAP_INFO
|
|
*
|
|
* @retval EFI_SUCCESS Successfully allocate the bounce buffer
|
|
* @retval Other As the error code indicates
|
|
|
|
*/
|
|
EFI_STATUS
|
|
IoMmuAllocateBounceBuffer (
|
|
IN EFI_ALLOCATE_TYPE Type,
|
|
IN EFI_MEMORY_TYPE MemoryType,
|
|
IN OUT MAP_INFO *MapInfo
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = InternalAllocateBuffer (
|
|
Type,
|
|
MemoryType,
|
|
MapInfo->NumberOfPages,
|
|
&MapInfo->ReservedMemBitmap,
|
|
&MapInfo->PlainTextAddress
|
|
);
|
|
ASSERT (Status == EFI_SUCCESS);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
* Clear a bit in the reserved memory bitmap in a thread safe manner
|
|
*
|
|
* @param ReservedMemBit The bit to clear
|
|
*/
|
|
STATIC
|
|
VOID
|
|
ClearReservedMemBit (
|
|
IN UINT32 ReservedMemBit
|
|
)
|
|
{
|
|
UINT32 ReservedMemBitmap;
|
|
|
|
do {
|
|
ReservedMemBitmap = mReservedMemBitmap;
|
|
} while (ReservedMemBitmap != InterlockedCompareExchange32 (
|
|
&mReservedMemBitmap,
|
|
ReservedMemBitmap,
|
|
ReservedMemBitmap & ~ReservedMemBit
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Free the bounce buffer allocated in IoMmuAllocateBounceBuffer.
|
|
*
|
|
* @param MapInfo Pointer to the MAP_INFO
|
|
* @return EFI_SUCCESS Successfully free the bounce buffer.
|
|
*/
|
|
EFI_STATUS
|
|
IoMmuFreeBounceBuffer (
|
|
IN OUT MAP_INFO *MapInfo
|
|
)
|
|
{
|
|
if (MapInfo->ReservedMemBitmap == 0) {
|
|
gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages);
|
|
} else {
|
|
DEBUG ((
|
|
DEBUG_VERBOSE,
|
|
"%a: PlainTextAddress=0x%Lx, bits=0x%Lx, bitmap: %Lx => %Lx\n",
|
|
__func__,
|
|
MapInfo->PlainTextAddress,
|
|
MapInfo->ReservedMemBitmap,
|
|
mReservedMemBitmap,
|
|
mReservedMemBitmap & ((UINT32)(~MapInfo->ReservedMemBitmap))
|
|
));
|
|
ClearReservedMemBit (MapInfo->ReservedMemBitmap);
|
|
MapInfo->PlainTextAddress = 0;
|
|
MapInfo->ReservedMemBitmap = 0;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Allocate CommonBuffer from pre-allocated shared memory.
|
|
*
|
|
* @param MemoryType Memory type
|
|
* @param CommonBufferPages Pages of CommonBuffer
|
|
* @param PhysicalAddress Allocated physical address
|
|
* @param ReservedMemBitmap Bitmap which indicates the allocation of reserved memory
|
|
*
|
|
* @retval EFI_SUCCESS Successfully allocate the common buffer
|
|
* @retval Other As the error code indicates
|
|
*/
|
|
EFI_STATUS
|
|
IoMmuAllocateCommonBuffer (
|
|
IN EFI_MEMORY_TYPE MemoryType,
|
|
IN UINTN CommonBufferPages,
|
|
OUT EFI_PHYSICAL_ADDRESS *PhysicalAddress,
|
|
OUT UINT32 *ReservedMemBitmap
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = InternalAllocateBuffer (
|
|
AllocateMaxAddress,
|
|
MemoryType,
|
|
CommonBufferPages,
|
|
ReservedMemBitmap,
|
|
PhysicalAddress
|
|
);
|
|
ASSERT (Status == EFI_SUCCESS);
|
|
|
|
if (*ReservedMemBitmap != 0) {
|
|
*PhysicalAddress -= SIZE_4KB;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
* Free CommonBuffer which is allocated by IoMmuAllocateCommonBuffer().
|
|
*
|
|
* @param CommonBufferHeader Pointer to the CommonBufferHeader
|
|
* @param CommonBufferPages Pages of CommonBuffer
|
|
*
|
|
* @retval EFI_SUCCESS Successfully free the common buffer
|
|
* @retval Other As the error code indicates
|
|
*/
|
|
EFI_STATUS
|
|
IoMmuFreeCommonBuffer (
|
|
IN COMMON_BUFFER_HEADER *CommonBufferHeader,
|
|
IN UINTN CommonBufferPages
|
|
)
|
|
{
|
|
if (!mReservedSharedMemSupported) {
|
|
goto LegacyFreeCommonBuffer;
|
|
}
|
|
|
|
if (CommonBufferHeader->ReservedMemBitmap == 0) {
|
|
goto LegacyFreeCommonBuffer;
|
|
}
|
|
|
|
DEBUG ((
|
|
DEBUG_VERBOSE,
|
|
"%a: CommonBuffer=0x%Lx, bits=0x%Lx, bitmap: %Lx => %Lx\n",
|
|
__func__,
|
|
(UINT64)(UINTN)CommonBufferHeader + SIZE_4KB,
|
|
CommonBufferHeader->ReservedMemBitmap,
|
|
mReservedMemBitmap,
|
|
mReservedMemBitmap & ((UINT32)(~CommonBufferHeader->ReservedMemBitmap))
|
|
));
|
|
|
|
ClearReservedMemBit (CommonBufferHeader->ReservedMemBitmap);
|
|
return EFI_SUCCESS;
|
|
|
|
LegacyFreeCommonBuffer:
|
|
return gBS->FreePages ((UINTN)CommonBufferHeader, CommonBufferPages);
|
|
}
|