VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/OvmfPkg/IoMmuDxe/IoMmuBuffer.c@ 105675

Last change on this file since 105675 was 105670, checked in by vboxsync, 8 months ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

  • Property svn:eol-style set to native
File size: 15.7 KB
Line 
1/** @file
2
3 Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
4
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7**/
8#include <Library/BaseLib.h>
9#include <Library/BaseMemoryLib.h>
10#include <Library/DebugLib.h>
11#include <Library/MemoryAllocationLib.h>
12#include <Library/MemEncryptSevLib.h>
13#include <Library/MemEncryptTdxLib.h>
14#include <Library/PcdLib.h>
15#include <Library/SynchronizationLib.h>
16#include <Library/UefiBootServicesTableLib.h>
17#include "IoMmuInternal.h"
18
19extern BOOLEAN mReservedSharedMemSupported;
20
21#define SIZE_OF_MEM_RANGE(MemRange) (MemRange->HeaderSize + MemRange->DataSize)
22
23#define RESERVED_MEM_BITMAP_4K_MASK 0xf
24#define RESERVED_MEM_BITMAP_32K_MASK 0xff0
25#define RESERVED_MEM_BITMAP_128K_MASK 0x3000
26#define RESERVED_MEM_BITMAP_1M_MASK 0x40000
27#define RESERVED_MEM_BITMAP_2M_MASK 0x180000
28#define RESERVED_MEM_BITMAP_MASK 0x1fffff
29
30/**
31 * mReservedMemRanges describes the layout of the reserved memory.
32 * The reserved memory consists of disfferent size of memory region.
33 * The pieces of memory with the same size are managed by one entry
34 * in the mReservedMemRanges. All the pieces of memories are managed by
35 * mReservedMemBitmap which is a UINT32. It means it can manage at most
36 * 32 pieces of memory. Because of the layout of CommonBuffer
37 * (1-page header + n-page data), a piece of reserved memory consists of
38 * 2 parts: Header + Data.
39 *
40 * So put all these together, mReservedMemRanges and mReservedMemBitmap
41 * are designed to manage the reserved memory.
42 *
43 * Use the second entry of mReservedMemRanges as an example.
44 * { RESERVED_MEM_BITMAP_32K_MASK, 4, 8, SIZE_32KB, SIZE_4KB, 0 },
45 * - RESERVED_MEM_BITMAP_32K_MASK is 0xff0. It means bit4-11 in mReservedMemBitmap
46 * is reserved for 32K size memory.
47 * - 4 is the shift of mReservedMemBitmap.
48 * - 8 means there are 8 pieces of 32K size memory.
49 * - SIZE_32KB indicates the size of Data part.
50 * - SIZE_4KB is the size of Header part.
51 * - 0 is the start address of this memory range which will be populated when
52 * the reserved memory is initialized.
53 *
54 * The size and count of the memory region are derived from the experience. For
55 * a typical grub boot, there are about 5100 IoMmu/DMA operation. Most of these
56 * DMA operation require the memory with size less than 32K (~5080). But we find
57 * in grub boot there may be 2 DMA operation which require for the memory larger
58 * than 1M. And these 2 DMA operation occur concurrently. So we reserve 2 pieces
59 * of memory with size of SIZE_2MB. This is for the best boot performance.
60 *
61 * If all the reserved memory are exausted, then it will fall back to the legacy
62 * memory allocation as before.
63 */
64STATIC IOMMU_RESERVED_MEM_RANGE mReservedMemRanges[] = {
65 { RESERVED_MEM_BITMAP_4K_MASK, 0, 4, SIZE_4KB, SIZE_4KB, 0 },
66 { RESERVED_MEM_BITMAP_32K_MASK, 4, 8, SIZE_32KB, SIZE_4KB, 0 },
67 { RESERVED_MEM_BITMAP_128K_MASK, 12, 2, SIZE_128KB, SIZE_4KB, 0 },
68 { RESERVED_MEM_BITMAP_1M_MASK, 14, 1, SIZE_1MB, SIZE_4KB, 0 },
69 { RESERVED_MEM_BITMAP_2M_MASK, 15, 2, SIZE_2MB, SIZE_4KB, 0 },
70};
71
72//
73// Bitmap of the allocation of reserved memory.
74//
75STATIC UINT32 mReservedMemBitmap = 0;
76
77//
78// Start address of the reserved memory region.
79//
80STATIC EFI_PHYSICAL_ADDRESS mReservedSharedMemAddress = 0;
81
82//
83// Total size of the reserved memory region.
84//
85STATIC UINT32 mReservedSharedMemSize = 0;
86
87/**
88 * Calculate the size of reserved memory.
89 *
90 * @retval UINT32 Size of the reserved memory
91 */
92STATIC
93UINT32
94CalcuateReservedMemSize (
95 VOID
96 )
97{
98 UINT32 Index;
99 IOMMU_RESERVED_MEM_RANGE *MemRange;
100
101 if (mReservedSharedMemSize != 0) {
102 return mReservedSharedMemSize;
103 }
104
105 for (Index = 0; Index < ARRAY_SIZE (mReservedMemRanges); Index++) {
106 MemRange = &mReservedMemRanges[Index];
107 mReservedSharedMemSize += (SIZE_OF_MEM_RANGE (MemRange) * MemRange->Slots);
108 }
109
110 return mReservedSharedMemSize;
111}
112
113/**
114 * Allocate a memory region and convert it to be shared. This memory region will be
115 * used in the DMA operation.
116 *
117 * The pre-alloc memory contains pieces of memory regions with different size. The
118 * allocation of the shared memory regions are indicated by a 32-bit bitmap (mReservedMemBitmap).
119 *
120 * The memory regions are consumed by IoMmuAllocateBuffer (in which CommonBuffer is allocated) and
121 * IoMmuMap (in which bounce buffer is allocated).
122 *
123 * The CommonBuffer contains 2 parts, one page for CommonBufferHeader which is private memory,
124 * the other part is shared memory. So the layout of a piece of memory region after initialization
125 * looks like:
126 *
127 * |------------|----------------------------|
128 * | Header | Data | <-- a piece of pre-alloc memory region
129 * | 4k, private| 4k/32k/128k/etc, shared |
130 * |-----------------------------------------|
131 *
132 * @retval EFI_SUCCESS Successfully initialize the reserved memory.
133 * @retval EFI_UNSUPPORTED This feature is not supported.
134 */
135EFI_STATUS
136IoMmuInitReservedSharedMem (
137 VOID
138 )
139{
140 EFI_STATUS Status;
141 UINT32 Index1, Index2;
142 UINTN TotalPages;
143 IOMMU_RESERVED_MEM_RANGE *MemRange;
144 EFI_PHYSICAL_ADDRESS PhysicalAddress;
145 UINT64 SharedAddress;
146
147 if (!mReservedSharedMemSupported) {
148 return EFI_UNSUPPORTED;
149 }
150
151 TotalPages = EFI_SIZE_TO_PAGES (CalcuateReservedMemSize ());
152
153 PhysicalAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocatePages (TotalPages);
154 DEBUG ((
155 DEBUG_VERBOSE,
156 "%a: ReservedMem (%d pages) address = 0x%llx\n",
157 __func__,
158 TotalPages,
159 PhysicalAddress
160 ));
161
162 mReservedMemBitmap = 0;
163 mReservedSharedMemAddress = PhysicalAddress;
164
165 for (Index1 = 0; Index1 < ARRAY_SIZE (mReservedMemRanges); Index1++) {
166 MemRange = &mReservedMemRanges[Index1];
167 MemRange->StartAddressOfMemRange = PhysicalAddress;
168
169 for (Index2 = 0; Index2 < MemRange->Slots; Index2++) {
170 SharedAddress = (UINT64)(UINTN)(MemRange->StartAddressOfMemRange + Index2 * SIZE_OF_MEM_RANGE (MemRange) + MemRange->HeaderSize);
171
172 if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
173 Status = MemEncryptSevClearPageEncMask (
174 0,
175 SharedAddress,
176 EFI_SIZE_TO_PAGES (MemRange->DataSize)
177 );
178 ASSERT (!EFI_ERROR (Status));
179 } else if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
180 Status = MemEncryptTdxSetPageSharedBit (
181 0,
182 SharedAddress,
183 EFI_SIZE_TO_PAGES (MemRange->DataSize)
184 );
185 ASSERT (!EFI_ERROR (Status));
186 } else {
187 ASSERT (FALSE);
188 }
189 }
190
191 PhysicalAddress += (MemRange->Slots * SIZE_OF_MEM_RANGE (MemRange));
192 }
193
194 return EFI_SUCCESS;
195}
196
197/**
198 * Release the pre-alloc shared memory.
199 *
200 * @retval EFI_SUCCESS Successfully release the shared memory
201 */
202EFI_STATUS
203IoMmuReleaseReservedSharedMem (
204 BOOLEAN MemoryMapLocked
205 )
206{
207 EFI_STATUS Status;
208 UINT32 Index1, Index2;
209 IOMMU_RESERVED_MEM_RANGE *MemRange;
210 UINT64 SharedAddress;
211
212 if (!mReservedSharedMemSupported) {
213 return EFI_SUCCESS;
214 }
215
216 for (Index1 = 0; Index1 < ARRAY_SIZE (mReservedMemRanges); Index1++) {
217 MemRange = &mReservedMemRanges[Index1];
218 for (Index2 = 0; Index2 < MemRange->Slots; Index2++) {
219 SharedAddress = (UINT64)(UINTN)(MemRange->StartAddressOfMemRange + Index2 * SIZE_OF_MEM_RANGE (MemRange) + MemRange->HeaderSize);
220
221 if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
222 Status = MemEncryptSevSetPageEncMask (
223 0,
224 SharedAddress,
225 EFI_SIZE_TO_PAGES (MemRange->DataSize)
226 );
227 ASSERT (!EFI_ERROR (Status));
228 } else if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {
229 Status = MemEncryptTdxClearPageSharedBit (
230 0,
231 SharedAddress,
232 EFI_SIZE_TO_PAGES (MemRange->DataSize)
233 );
234 ASSERT (!EFI_ERROR (Status));
235 } else {
236 ASSERT (FALSE);
237 }
238 }
239 }
240
241 if (!MemoryMapLocked) {
242 FreePages ((VOID *)(UINTN)mReservedSharedMemAddress, EFI_SIZE_TO_PAGES (CalcuateReservedMemSize ()));
243 mReservedSharedMemAddress = 0;
244 mReservedMemBitmap = 0;
245 }
246
247 mReservedSharedMemSupported = FALSE;
248
249 return EFI_SUCCESS;
250}
251
252/**
253 * Allocate from the reserved memory pool.
254 * If the reserved shared memory is exausted or there is no suitalbe size, it turns
255 * to the LegacyAllocateBuffer.
256 *
257 * @param Type Allocate type
258 * @param MemoryType The memory type to be allocated
259 * @param Pages Pages to be allocated.
260 * @param ReservedMemBitmap Bitmap of the allocated memory region
261 * @param PhysicalAddress Pointer to the data part of allocated memory region
262 *
263 * @retval EFI_SUCCESS Successfully allocate the buffer
264 * @retval Other As the error code indicates
265 */
266STATIC
267EFI_STATUS
268InternalAllocateBuffer (
269 IN EFI_ALLOCATE_TYPE Type,
270 IN EFI_MEMORY_TYPE MemoryType,
271 IN UINTN Pages,
272 OUT UINT32 *ReservedMemBit,
273 IN OUT EFI_PHYSICAL_ADDRESS *PhysicalAddress
274 )
275{
276 UINT32 MemBitmap;
277 UINT32 ReservedMemBitmap;
278 UINT8 Index;
279 IOMMU_RESERVED_MEM_RANGE *MemRange;
280 UINTN PagesOfLastMemRange;
281
282 *ReservedMemBit = 0;
283
284 if (Pages == 0) {
285 ASSERT (FALSE);
286 return EFI_INVALID_PARAMETER;
287 }
288
289 if (!mReservedSharedMemSupported) {
290 goto LegacyAllocateBuffer;
291 }
292
293 if (mReservedSharedMemAddress == 0) {
294 goto LegacyAllocateBuffer;
295 }
296
297 PagesOfLastMemRange = 0;
298
299 for (Index = 0; Index < ARRAY_SIZE (mReservedMemRanges); Index++) {
300 if ((Pages > PagesOfLastMemRange) && (Pages <= EFI_SIZE_TO_PAGES (mReservedMemRanges[Index].DataSize))) {
301 break;
302 }
303
304 PagesOfLastMemRange = EFI_SIZE_TO_PAGES (mReservedMemRanges[Index].DataSize);
305 }
306
307 if (Index == ARRAY_SIZE (mReservedMemRanges)) {
308 // There is no suitable size of reserved memory. Turn to legacy allocate.
309 goto LegacyAllocateBuffer;
310 }
311
312 MemRange = &mReservedMemRanges[Index];
313
314 do {
315 ReservedMemBitmap = mReservedMemBitmap;
316
317 if ((ReservedMemBitmap & MemRange->BitmapMask) == MemRange->BitmapMask) {
318 // The reserved memory is exhausted. Turn to legacy allocate.
319 goto LegacyAllocateBuffer;
320 }
321
322 MemBitmap = (ReservedMemBitmap & MemRange->BitmapMask) >> MemRange->Shift;
323
324 for (Index = 0; Index < MemRange->Slots; Index++) {
325 if ((MemBitmap & (UINT8)(1<<Index)) == 0) {
326 break;
327 }
328 }
329
330 ASSERT (Index != MemRange->Slots);
331
332 *PhysicalAddress = MemRange->StartAddressOfMemRange + Index * SIZE_OF_MEM_RANGE (MemRange) + MemRange->HeaderSize;
333 *ReservedMemBit = (UINT32)(1 << (Index + MemRange->Shift));
334 } while (ReservedMemBitmap != InterlockedCompareExchange32 (
335 &mReservedMemBitmap,
336 ReservedMemBitmap,
337 ReservedMemBitmap | *ReservedMemBit
338 ));
339
340 DEBUG ((
341 DEBUG_VERBOSE,
342 "%a: range-size: %lx, start-address=0x%llx, pages=0x%llx, bits=0x%lx, bitmap: %lx => %lx\n",
343 __func__,
344 MemRange->DataSize,
345 *PhysicalAddress,
346 Pages,
347 *ReservedMemBit,
348 ReservedMemBitmap,
349 ReservedMemBitmap | *ReservedMemBit
350 ));
351
352 return EFI_SUCCESS;
353
354LegacyAllocateBuffer:
355
356 *ReservedMemBit = 0;
357 return gBS->AllocatePages (Type, MemoryType, Pages, PhysicalAddress);
358}
359
360/**
361 * Allocate reserved shared memory for bounce buffer.
362 *
363 * @param Type Allocate type
364 * @param MemoryType The memory type to be allocated
365 * @param MapInfo Pointer to the MAP_INFO
366 *
367 * @retval EFI_SUCCESS Successfully allocate the bounce buffer
368 * @retval Other As the error code indicates
369
370 */
371EFI_STATUS
372IoMmuAllocateBounceBuffer (
373 IN EFI_ALLOCATE_TYPE Type,
374 IN EFI_MEMORY_TYPE MemoryType,
375 IN OUT MAP_INFO *MapInfo
376 )
377{
378 EFI_STATUS Status;
379
380 Status = InternalAllocateBuffer (
381 Type,
382 MemoryType,
383 MapInfo->NumberOfPages,
384 &MapInfo->ReservedMemBitmap,
385 &MapInfo->PlainTextAddress
386 );
387 ASSERT (Status == EFI_SUCCESS);
388
389 return Status;
390}
391
392/**
393 * Clear a bit in the reserved memory bitmap in a thread safe manner
394 *
395 * @param ReservedMemBit The bit to clear
396 */
397STATIC
398VOID
399ClearReservedMemBit (
400 IN UINT32 ReservedMemBit
401 )
402{
403 UINT32 ReservedMemBitmap;
404
405 do {
406 ReservedMemBitmap = mReservedMemBitmap;
407 } while (ReservedMemBitmap != InterlockedCompareExchange32 (
408 &mReservedMemBitmap,
409 ReservedMemBitmap,
410 ReservedMemBitmap & ~ReservedMemBit
411 ));
412}
413
414/**
415 * Free the bounce buffer allocated in IoMmuAllocateBounceBuffer.
416 *
417 * @param MapInfo Pointer to the MAP_INFO
418 * @return EFI_SUCCESS Successfully free the bounce buffer.
419 */
420EFI_STATUS
421IoMmuFreeBounceBuffer (
422 IN OUT MAP_INFO *MapInfo
423 )
424{
425 if (MapInfo->ReservedMemBitmap == 0) {
426 gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages);
427 } else {
428 DEBUG ((
429 DEBUG_VERBOSE,
430 "%a: PlainTextAddress=0x%Lx, bits=0x%Lx, bitmap: %Lx => %Lx\n",
431 __func__,
432 MapInfo->PlainTextAddress,
433 MapInfo->ReservedMemBitmap,
434 mReservedMemBitmap,
435 mReservedMemBitmap & ((UINT32)(~MapInfo->ReservedMemBitmap))
436 ));
437 ClearReservedMemBit (MapInfo->ReservedMemBitmap);
438 MapInfo->PlainTextAddress = 0;
439 MapInfo->ReservedMemBitmap = 0;
440 }
441
442 return EFI_SUCCESS;
443}
444
445/**
446 * Allocate CommonBuffer from pre-allocated shared memory.
447 *
448 * @param MemoryType Memory type
449 * @param CommonBufferPages Pages of CommonBuffer
450 * @param PhysicalAddress Allocated physical address
451 * @param ReservedMemBitmap Bitmap which indicates the allocation of reserved memory
452 *
453 * @retval EFI_SUCCESS Successfully allocate the common buffer
454 * @retval Other As the error code indicates
455 */
456EFI_STATUS
457IoMmuAllocateCommonBuffer (
458 IN EFI_MEMORY_TYPE MemoryType,
459 IN UINTN CommonBufferPages,
460 OUT EFI_PHYSICAL_ADDRESS *PhysicalAddress,
461 OUT UINT32 *ReservedMemBitmap
462 )
463{
464 EFI_STATUS Status;
465
466 Status = InternalAllocateBuffer (
467 AllocateMaxAddress,
468 MemoryType,
469 CommonBufferPages,
470 ReservedMemBitmap,
471 PhysicalAddress
472 );
473 ASSERT (Status == EFI_SUCCESS);
474
475 if (*ReservedMemBitmap != 0) {
476 *PhysicalAddress -= SIZE_4KB;
477 }
478
479 return Status;
480}
481
482/**
483 * Free CommonBuffer which is allocated by IoMmuAllocateCommonBuffer().
484 *
485 * @param CommonBufferHeader Pointer to the CommonBufferHeader
486 * @param CommonBufferPages Pages of CommonBuffer
487 *
488 * @retval EFI_SUCCESS Successfully free the common buffer
489 * @retval Other As the error code indicates
490 */
491EFI_STATUS
492IoMmuFreeCommonBuffer (
493 IN COMMON_BUFFER_HEADER *CommonBufferHeader,
494 IN UINTN CommonBufferPages
495 )
496{
497 if (!mReservedSharedMemSupported) {
498 goto LegacyFreeCommonBuffer;
499 }
500
501 if (CommonBufferHeader->ReservedMemBitmap == 0) {
502 goto LegacyFreeCommonBuffer;
503 }
504
505 DEBUG ((
506 DEBUG_VERBOSE,
507 "%a: CommonBuffer=0x%Lx, bits=0x%Lx, bitmap: %Lx => %Lx\n",
508 __func__,
509 (UINT64)(UINTN)CommonBufferHeader + SIZE_4KB,
510 CommonBufferHeader->ReservedMemBitmap,
511 mReservedMemBitmap,
512 mReservedMemBitmap & ((UINT32)(~CommonBufferHeader->ReservedMemBitmap))
513 ));
514
515 ClearReservedMemBit (CommonBufferHeader->ReservedMemBitmap);
516 return EFI_SUCCESS;
517
518LegacyFreeCommonBuffer:
519 return gBS->FreePages ((UINTN)CommonBufferHeader, CommonBufferPages);
520}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette