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