]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c
OvmfPkg/BaseMemEncryptSevLib: Enable protection for newly added page table
[mirror_edk2.git] / OvmfPkg / Library / BaseMemEncryptSevLib / X64 / VirtualMemory.c
CommitLineData
a1f22614
BS
1/** @file\r
2\r
3 Virtual Memory Management Services to set or clear the memory encryption bit\r
4\r
5Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>\r
6Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
7\r
8This program and the accompanying materials\r
9are licensed and made available under the terms and conditions of the BSD License\r
10which accompanies this distribution. The full text of the license may be found at\r
11http://opensource.org/licenses/bsd-license.php\r
12\r
13THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
14WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
15\r
16Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c\r
17\r
18**/\r
19\r
20#include <Library/CpuLib.h>\r
21#include <Register/Cpuid.h>\r
22#include <Register/Amd/Cpuid.h>\r
23\r
24#include "VirtualMemory.h"\r
25\r
26STATIC BOOLEAN mAddressEncMaskChecked = FALSE;\r
27STATIC UINT64 mAddressEncMask;\r
b721aa74 28STATIC PAGE_TABLE_POOL *mPageTablePool = NULL;\r
a1f22614
BS
29\r
30typedef enum {\r
31 SetCBit,\r
32 ClearCBit\r
33} MAP_RANGE_MODE;\r
34\r
35/**\r
36 Get the memory encryption mask\r
37\r
38 @param[out] EncryptionMask contains the pte mask.\r
39\r
40**/\r
41STATIC\r
42UINT64\r
43GetMemEncryptionAddressMask (\r
44 VOID\r
45 )\r
46{\r
47 UINT64 EncryptionMask;\r
48 CPUID_MEMORY_ENCRYPTION_INFO_EBX Ebx;\r
49\r
50 if (mAddressEncMaskChecked) {\r
51 return mAddressEncMask;\r
52 }\r
53\r
54 //\r
55 // CPUID Fn8000_001F[EBX] Bit 0:5 (memory encryption bit position)\r
56 //\r
57 AsmCpuid (CPUID_MEMORY_ENCRYPTION_INFO, NULL, &Ebx.Uint32, NULL, NULL);\r
58 EncryptionMask = LShiftU64 (1, Ebx.Bits.PtePosBits);\r
59\r
60 mAddressEncMask = EncryptionMask & PAGING_1G_ADDRESS_MASK_64;\r
61 mAddressEncMaskChecked = TRUE;\r
62\r
63 return mAddressEncMask;\r
64}\r
65\r
b721aa74
BS
66/**\r
67 Initialize a buffer pool for page table use only.\r
68\r
69 To reduce the potential split operation on page table, the pages reserved for\r
70 page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and\r
71 at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always\r
72 initialized with number of pages greater than or equal to the given PoolPages.\r
73\r
74 Once the pages in the pool are used up, this method should be called again to\r
75 reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. Usually this won't happen\r
76 often in practice.\r
77\r
78 @param[in] PoolPages The least page number of the pool to be created.\r
79\r
80 @retval TRUE The pool is initialized successfully.\r
81 @retval FALSE The memory is out of resource.\r
82**/\r
83STATIC\r
84BOOLEAN\r
85InitializePageTablePool (\r
86 IN UINTN PoolPages\r
87 )\r
88{\r
89 VOID *Buffer;\r
90\r
91 //\r
92 // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for\r
93 // header.\r
94 //\r
95 PoolPages += 1; // Add one page for header.\r
96 PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) *\r
97 PAGE_TABLE_POOL_UNIT_PAGES;\r
98 Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);\r
99 if (Buffer == NULL) {\r
100 DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));\r
101 return FALSE;\r
102 }\r
103\r
104 //\r
105 // Link all pools into a list for easier track later.\r
106 //\r
107 if (mPageTablePool == NULL) {\r
108 mPageTablePool = Buffer;\r
109 mPageTablePool->NextPool = mPageTablePool;\r
110 } else {\r
111 ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool;\r
112 mPageTablePool->NextPool = Buffer;\r
113 mPageTablePool = Buffer;\r
114 }\r
115\r
116 //\r
117 // Reserve one page for pool header.\r
118 //\r
119 mPageTablePool->FreePages = PoolPages - 1;\r
120 mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1);\r
121\r
122 return TRUE;\r
123}\r
124\r
125/**\r
126 This API provides a way to allocate memory for page table.\r
127\r
128 This API can be called more than once to allocate memory for page tables.\r
129\r
130 Allocates the number of 4KB pages and returns a pointer to the allocated\r
131 buffer. The buffer returned is aligned on a 4KB boundary.\r
132\r
133 If Pages is 0, then NULL is returned.\r
134 If there is not enough memory remaining to satisfy the request, then NULL is\r
135 returned.\r
136\r
137 @param Pages The number of 4 KB pages to allocate.\r
138\r
139 @return A pointer to the allocated buffer or NULL if allocation fails.\r
140\r
141**/\r
142STATIC\r
143VOID *\r
144EFIAPI\r
145AllocatePageTableMemory (\r
146 IN UINTN Pages\r
147 )\r
148{\r
149 VOID *Buffer;\r
150\r
151 if (Pages == 0) {\r
152 return NULL;\r
153 }\r
154\r
155 //\r
156 // Renew the pool if necessary.\r
157 //\r
158 if (mPageTablePool == NULL ||\r
159 Pages > mPageTablePool->FreePages) {\r
160 if (!InitializePageTablePool (Pages)) {\r
161 return NULL;\r
162 }\r
163 }\r
164\r
165 Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;\r
166\r
167 mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages);\r
168 mPageTablePool->FreePages -= Pages;\r
169\r
170 DEBUG ((\r
171 DEBUG_VERBOSE,\r
172 "%a:%a: Buffer=0x%Lx Pages=%ld\n",\r
173 gEfiCallerBaseName,\r
174 __FUNCTION__,\r
175 Buffer,\r
176 Pages\r
177 ));\r
178\r
179 return Buffer;\r
180}\r
181\r
182\r
a1f22614
BS
183/**\r
184 Split 2M page to 4K.\r
185\r
186 @param[in] PhysicalAddress Start physical address the 2M page covered.\r
187 @param[in, out] PageEntry2M Pointer to 2M page entry.\r
188 @param[in] StackBase Stack base address.\r
189 @param[in] StackSize Stack size.\r
190\r
191**/\r
192STATIC\r
193VOID\r
194Split2MPageTo4K (\r
195 IN PHYSICAL_ADDRESS PhysicalAddress,\r
196 IN OUT UINT64 *PageEntry2M,\r
197 IN PHYSICAL_ADDRESS StackBase,\r
198 IN UINTN StackSize\r
199 )\r
200{\r
201 PHYSICAL_ADDRESS PhysicalAddress4K;\r
202 UINTN IndexOfPageTableEntries;\r
203 PAGE_TABLE_4K_ENTRY *PageTableEntry, *PageTableEntry1;\r
204 UINT64 AddressEncMask;\r
205\r
b721aa74 206 PageTableEntry = AllocatePageTableMemory(1);\r
a1f22614
BS
207\r
208 PageTableEntry1 = PageTableEntry;\r
209\r
210 AddressEncMask = GetMemEncryptionAddressMask ();\r
211\r
212 ASSERT (PageTableEntry != NULL);\r
213 ASSERT (*PageEntry2M & AddressEncMask);\r
214\r
215 PhysicalAddress4K = PhysicalAddress;\r
216 for (IndexOfPageTableEntries = 0; IndexOfPageTableEntries < 512; IndexOfPageTableEntries++, PageTableEntry++, PhysicalAddress4K += SIZE_4KB) {\r
217 //\r
218 // Fill in the Page Table entries\r
219 //\r
220 PageTableEntry->Uint64 = (UINT64) PhysicalAddress4K | AddressEncMask;\r
221 PageTableEntry->Bits.ReadWrite = 1;\r
222 PageTableEntry->Bits.Present = 1;\r
223 if ((PhysicalAddress4K >= StackBase) && (PhysicalAddress4K < StackBase + StackSize)) {\r
224 //\r
225 // Set Nx bit for stack.\r
226 //\r
227 PageTableEntry->Bits.Nx = 1;\r
228 }\r
229 }\r
230\r
231 //\r
232 // Fill in 2M page entry.\r
233 //\r
234 *PageEntry2M = (UINT64) (UINTN) PageTableEntry1 | IA32_PG_P | IA32_PG_RW | AddressEncMask;\r
235}\r
236\r
b721aa74
BS
237/**\r
238 Set one page of page table pool memory to be read-only.\r
239\r
240 @param[in] PageTableBase Base address of page table (CR3).\r
241 @param[in] Address Start address of a page to be set as read-only.\r
242 @param[in] Level4Paging Level 4 paging flag.\r
243\r
244**/\r
245STATIC\r
246VOID\r
247SetPageTablePoolReadOnly (\r
248 IN UINTN PageTableBase,\r
249 IN EFI_PHYSICAL_ADDRESS Address,\r
250 IN BOOLEAN Level4Paging\r
251 )\r
252{\r
253 UINTN Index;\r
254 UINTN EntryIndex;\r
255 UINT64 AddressEncMask;\r
256 EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
257 UINT64 *PageTable;\r
258 UINT64 *NewPageTable;\r
259 UINT64 PageAttr;\r
260 UINT64 LevelSize[5];\r
261 UINT64 LevelMask[5];\r
262 UINTN LevelShift[5];\r
263 UINTN Level;\r
264 UINT64 PoolUnitSize;\r
265\r
266 ASSERT (PageTableBase != 0);\r
267\r
268 //\r
269 // Since the page table is always from page table pool, which is always\r
270 // located at the boundary of PcdPageTablePoolAlignment, we just need to\r
271 // set the whole pool unit to be read-only.\r
272 //\r
273 Address = Address & PAGE_TABLE_POOL_ALIGN_MASK;\r
274\r
275 LevelShift[1] = PAGING_L1_ADDRESS_SHIFT;\r
276 LevelShift[2] = PAGING_L2_ADDRESS_SHIFT;\r
277 LevelShift[3] = PAGING_L3_ADDRESS_SHIFT;\r
278 LevelShift[4] = PAGING_L4_ADDRESS_SHIFT;\r
279\r
280 LevelMask[1] = PAGING_4K_ADDRESS_MASK_64;\r
281 LevelMask[2] = PAGING_2M_ADDRESS_MASK_64;\r
282 LevelMask[3] = PAGING_1G_ADDRESS_MASK_64;\r
283 LevelMask[4] = PAGING_1G_ADDRESS_MASK_64;\r
284\r
285 LevelSize[1] = SIZE_4KB;\r
286 LevelSize[2] = SIZE_2MB;\r
287 LevelSize[3] = SIZE_1GB;\r
288 LevelSize[4] = SIZE_512GB;\r
289\r
290 AddressEncMask = GetMemEncryptionAddressMask() &\r
291 PAGING_1G_ADDRESS_MASK_64;\r
292 PageTable = (UINT64 *)(UINTN)PageTableBase;\r
293 PoolUnitSize = PAGE_TABLE_POOL_UNIT_SIZE;\r
294\r
295 for (Level = (Level4Paging) ? 4 : 3; Level > 0; --Level) {\r
296 Index = ((UINTN)RShiftU64 (Address, LevelShift[Level]));\r
297 Index &= PAGING_PAE_INDEX_MASK;\r
298\r
299 PageAttr = PageTable[Index];\r
300 if ((PageAttr & IA32_PG_PS) == 0) {\r
301 //\r
302 // Go to next level of table.\r
303 //\r
304 PageTable = (UINT64 *)(UINTN)(PageAttr & ~AddressEncMask &\r
305 PAGING_4K_ADDRESS_MASK_64);\r
306 continue;\r
307 }\r
308\r
309 if (PoolUnitSize >= LevelSize[Level]) {\r
310 //\r
311 // Clear R/W bit if current page granularity is not larger than pool unit\r
312 // size.\r
313 //\r
314 if ((PageAttr & IA32_PG_RW) != 0) {\r
315 while (PoolUnitSize > 0) {\r
316 //\r
317 // PAGE_TABLE_POOL_UNIT_SIZE and PAGE_TABLE_POOL_ALIGNMENT are fit in\r
318 // one page (2MB). Then we don't need to update attributes for pages\r
319 // crossing page directory. ASSERT below is for that purpose.\r
320 //\r
321 ASSERT (Index < EFI_PAGE_SIZE/sizeof (UINT64));\r
322\r
323 PageTable[Index] &= ~(UINT64)IA32_PG_RW;\r
324 PoolUnitSize -= LevelSize[Level];\r
325\r
326 ++Index;\r
327 }\r
328 }\r
329\r
330 break;\r
331\r
332 } else {\r
333 //\r
334 // The smaller granularity of page must be needed.\r
335 //\r
336 ASSERT (Level > 1);\r
337\r
338 NewPageTable = AllocatePageTableMemory (1);\r
339 ASSERT (NewPageTable != NULL);\r
340\r
341 PhysicalAddress = PageAttr & LevelMask[Level];\r
342 for (EntryIndex = 0;\r
343 EntryIndex < EFI_PAGE_SIZE/sizeof (UINT64);\r
344 ++EntryIndex) {\r
345 NewPageTable[EntryIndex] = PhysicalAddress | AddressEncMask |\r
346 IA32_PG_P | IA32_PG_RW;\r
347 if (Level > 2) {\r
348 NewPageTable[EntryIndex] |= IA32_PG_PS;\r
349 }\r
350 PhysicalAddress += LevelSize[Level - 1];\r
351 }\r
352\r
353 PageTable[Index] = (UINT64)(UINTN)NewPageTable | AddressEncMask |\r
354 IA32_PG_P | IA32_PG_RW;\r
355 PageTable = NewPageTable;\r
356 }\r
357 }\r
358}\r
359\r
360/**\r
361 Prevent the memory pages used for page table from been overwritten.\r
362\r
363 @param[in] PageTableBase Base address of page table (CR3).\r
364 @param[in] Level4Paging Level 4 paging flag.\r
365\r
366**/\r
367STATIC\r
368VOID\r
369EnablePageTableProtection (\r
370 IN UINTN PageTableBase,\r
371 IN BOOLEAN Level4Paging\r
372 )\r
373{\r
374 PAGE_TABLE_POOL *HeadPool;\r
375 PAGE_TABLE_POOL *Pool;\r
376 UINT64 PoolSize;\r
377 EFI_PHYSICAL_ADDRESS Address;\r
378\r
379 if (mPageTablePool == NULL) {\r
380 return;\r
381 }\r
382\r
383 //\r
384 // SetPageTablePoolReadOnly might update mPageTablePool. It's safer to\r
385 // remember original one in advance.\r
386 //\r
387 HeadPool = mPageTablePool;\r
388 Pool = HeadPool;\r
389 do {\r
390 Address = (EFI_PHYSICAL_ADDRESS)(UINTN)Pool;\r
391 PoolSize = Pool->Offset + EFI_PAGES_TO_SIZE (Pool->FreePages);\r
392\r
393 //\r
394 // The size of one pool must be multiple of PAGE_TABLE_POOL_UNIT_SIZE, which\r
395 // is one of page size of the processor (2MB by default). Let's apply the\r
396 // protection to them one by one.\r
397 //\r
398 while (PoolSize > 0) {\r
399 SetPageTablePoolReadOnly(PageTableBase, Address, Level4Paging);\r
400 Address += PAGE_TABLE_POOL_UNIT_SIZE;\r
401 PoolSize -= PAGE_TABLE_POOL_UNIT_SIZE;\r
402 }\r
403\r
404 Pool = Pool->NextPool;\r
405 } while (Pool != HeadPool);\r
406\r
407}\r
408\r
409\r
a1f22614
BS
410/**\r
411 Split 1G page to 2M.\r
412\r
413 @param[in] PhysicalAddress Start physical address the 1G page covered.\r
414 @param[in, out] PageEntry1G Pointer to 1G page entry.\r
415 @param[in] StackBase Stack base address.\r
416 @param[in] StackSize Stack size.\r
417\r
418**/\r
419STATIC\r
420VOID\r
421Split1GPageTo2M (\r
422 IN PHYSICAL_ADDRESS PhysicalAddress,\r
423 IN OUT UINT64 *PageEntry1G,\r
424 IN PHYSICAL_ADDRESS StackBase,\r
425 IN UINTN StackSize\r
426 )\r
427{\r
428 PHYSICAL_ADDRESS PhysicalAddress2M;\r
429 UINTN IndexOfPageDirectoryEntries;\r
430 PAGE_TABLE_ENTRY *PageDirectoryEntry;\r
431 UINT64 AddressEncMask;\r
432\r
b721aa74 433 PageDirectoryEntry = AllocatePageTableMemory(1);\r
a1f22614
BS
434\r
435 AddressEncMask = GetMemEncryptionAddressMask ();\r
436 ASSERT (PageDirectoryEntry != NULL);\r
437 ASSERT (*PageEntry1G & GetMemEncryptionAddressMask ());\r
438 //\r
439 // Fill in 1G page entry.\r
440 //\r
441 *PageEntry1G = (UINT64) (UINTN) PageDirectoryEntry | IA32_PG_P | IA32_PG_RW | AddressEncMask;\r
442\r
443 PhysicalAddress2M = PhysicalAddress;\r
444 for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress2M += SIZE_2MB) {\r
445 if ((PhysicalAddress2M < StackBase + StackSize) && ((PhysicalAddress2M + SIZE_2MB) > StackBase)) {\r
446 //\r
447 // Need to split this 2M page that covers stack range.\r
448 //\r
449 Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, StackBase, StackSize);\r
450 } else {\r
451 //\r
452 // Fill in the Page Directory entries\r
453 //\r
454 PageDirectoryEntry->Uint64 = (UINT64) PhysicalAddress2M | AddressEncMask;\r
455 PageDirectoryEntry->Bits.ReadWrite = 1;\r
456 PageDirectoryEntry->Bits.Present = 1;\r
457 PageDirectoryEntry->Bits.MustBe1 = 1;\r
458 }\r
459 }\r
460}\r
461\r
462\r
463/**\r
464 Set or Clear the memory encryption bit\r
465\r
466 @param[in] PagetablePoint Page table entry pointer (PTE).\r
467 @param[in] Mode Set or Clear encryption bit\r
468\r
469**/\r
470STATIC VOID\r
471SetOrClearCBit(\r
472 IN OUT UINT64* PageTablePointer,\r
473 IN MAP_RANGE_MODE Mode\r
474 )\r
475{\r
476 UINT64 AddressEncMask;\r
477\r
478 AddressEncMask = GetMemEncryptionAddressMask ();\r
479\r
480 if (Mode == SetCBit) {\r
481 *PageTablePointer |= AddressEncMask;\r
482 } else {\r
483 *PageTablePointer &= ~AddressEncMask;\r
484 }\r
485\r
486}\r
487\r
b721aa74
BS
488/**\r
489 Check the WP status in CR0 register. This bit is used to lock or unlock write\r
490 access to pages marked as read-only.\r
491\r
492 @retval TRUE Write protection is enabled.\r
493 @retval FALSE Write protection is disabled.\r
494**/\r
495STATIC\r
496BOOLEAN\r
497IsReadOnlyPageWriteProtected (\r
498 VOID\r
499 )\r
500{\r
501 return ((AsmReadCr0 () & BIT16) != 0);\r
502}\r
503\r
504\r
505/**\r
506 Disable Write Protect on pages marked as read-only.\r
507**/\r
508STATIC\r
509VOID\r
510DisableReadOnlyPageWriteProtect (\r
511 VOID\r
512 )\r
513{\r
514 AsmWriteCr0 (AsmReadCr0() & ~BIT16);\r
515}\r
516\r
517/**\r
518 Enable Write Protect on pages marked as read-only.\r
519**/\r
520VOID\r
521EnableReadOnlyPageWriteProtect (\r
522 VOID\r
523 )\r
524{\r
525 AsmWriteCr0 (AsmReadCr0() | BIT16);\r
526}\r
527\r
528\r
a1f22614
BS
529/**\r
530 This function either sets or clears memory encryption bit for the memory region\r
531 specified by PhysicalAddress and length from the current page table context.\r
532\r
533 The function iterates through the physicalAddress one page at a time, and set\r
534 or clears the memory encryption mask in the page table. If it encounters\r
535 that a given physical address range is part of large page then it attempts to\r
536 change the attribute at one go (based on size), otherwise it splits the\r
537 large pages into smaller (e.g 2M page into 4K pages) and then try to set or\r
538 clear the encryption bit on the smallest page size.\r
539\r
540 @param[in] PhysicalAddress The physical address that is the start\r
541 address of a memory region.\r
542 @param[in] Length The length of memory region\r
543 @param[in] Mode Set or Clear mode\r
544 @param[in] Flush Flush the caches before applying the\r
545 encryption mask\r
546\r
547 @retval RETURN_SUCCESS The attributes were cleared for the memory\r
548 region.\r
549 @retval RETURN_INVALID_PARAMETER Number of pages is zero.\r
550 @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute is\r
551 not supported\r
552**/\r
553\r
554STATIC\r
555RETURN_STATUS\r
556EFIAPI\r
557SetMemoryEncDec (\r
558 IN PHYSICAL_ADDRESS Cr3BaseAddress,\r
559 IN PHYSICAL_ADDRESS PhysicalAddress,\r
560 IN UINTN Length,\r
561 IN MAP_RANGE_MODE Mode,\r
562 IN BOOLEAN CacheFlush\r
563 )\r
564{\r
565 PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;\r
566 PAGE_MAP_AND_DIRECTORY_POINTER *PageUpperDirectoryPointerEntry;\r
567 PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;\r
568 PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry;\r
569 PAGE_TABLE_ENTRY *PageDirectory2MEntry;\r
570 PAGE_TABLE_4K_ENTRY *PageTableEntry;\r
571 UINT64 PgTableMask;\r
572 UINT64 AddressEncMask;\r
b721aa74
BS
573 BOOLEAN IsWpEnabled;\r
574 RETURN_STATUS Status;\r
a1f22614 575\r
70063aec
LE
576 DEBUG ((\r
577 DEBUG_VERBOSE,\r
578 "%a:%a: Cr3Base=0x%Lx Physical=0x%Lx Length=0x%Lx Mode=%a CacheFlush=%u\n",\r
579 gEfiCallerBaseName,\r
580 __FUNCTION__,\r
581 Cr3BaseAddress,\r
582 PhysicalAddress,\r
583 (UINT64)Length,\r
584 (Mode == SetCBit) ? "Encrypt" : "Decrypt",\r
585 (UINT32)CacheFlush\r
586 ));\r
587\r
a1f22614
BS
588 //\r
589 // Check if we have a valid memory encryption mask\r
590 //\r
591 AddressEncMask = GetMemEncryptionAddressMask ();\r
592 if (!AddressEncMask) {\r
593 return RETURN_ACCESS_DENIED;\r
594 }\r
595\r
596 PgTableMask = AddressEncMask | EFI_PAGE_MASK;\r
597\r
598 if (Length == 0) {\r
599 return RETURN_INVALID_PARAMETER;\r
600 }\r
601\r
602 //\r
603 // We are going to change the memory encryption attribute from C=0 -> C=1 or\r
604 // vice versa Flush the caches to ensure that data is written into memory with\r
605 // correct C-bit\r
606 //\r
607 if (CacheFlush) {\r
608 WriteBackInvalidateDataCacheRange((VOID*) (UINTN)PhysicalAddress, Length);\r
609 }\r
610\r
b721aa74
BS
611 //\r
612 // Make sure that the page table is changeable.\r
613 //\r
614 IsWpEnabled = IsReadOnlyPageWriteProtected ();\r
615 if (IsWpEnabled) {\r
616 DisableReadOnlyPageWriteProtect ();\r
617 }\r
618\r
619 Status = EFI_SUCCESS;\r
620\r
a1f22614
BS
621 while (Length)\r
622 {\r
623 //\r
624 // If Cr3BaseAddress is not specified then read the current CR3\r
625 //\r
626 if (Cr3BaseAddress == 0) {\r
627 Cr3BaseAddress = AsmReadCr3();\r
628 }\r
629\r
630 PageMapLevel4Entry = (VOID*) (Cr3BaseAddress & ~PgTableMask);\r
631 PageMapLevel4Entry += PML4_OFFSET(PhysicalAddress);\r
632 if (!PageMapLevel4Entry->Bits.Present) {\r
6692af92 633 DEBUG ((\r
3728ea5a
LE
634 DEBUG_ERROR,\r
635 "%a:%a: bad PML4 for Physical=0x%Lx\n",\r
6692af92
LE
636 gEfiCallerBaseName,\r
637 __FUNCTION__,\r
638 PhysicalAddress\r
639 ));\r
b721aa74
BS
640 Status = RETURN_NO_MAPPING;\r
641 goto Done;\r
a1f22614
BS
642 }\r
643\r
644 PageDirectory1GEntry = (VOID*) ((PageMapLevel4Entry->Bits.PageTableBaseAddress<<12) & ~PgTableMask);\r
645 PageDirectory1GEntry += PDP_OFFSET(PhysicalAddress);\r
646 if (!PageDirectory1GEntry->Bits.Present) {\r
6692af92 647 DEBUG ((\r
3728ea5a
LE
648 DEBUG_ERROR,\r
649 "%a:%a: bad PDPE for Physical=0x%Lx\n",\r
6692af92
LE
650 gEfiCallerBaseName,\r
651 __FUNCTION__,\r
652 PhysicalAddress\r
653 ));\r
b721aa74
BS
654 Status = RETURN_NO_MAPPING;\r
655 goto Done;\r
a1f22614
BS
656 }\r
657\r
658 //\r
659 // If the MustBe1 bit is not 1, it's not actually a 1GB entry\r
660 //\r
661 if (PageDirectory1GEntry->Bits.MustBe1) {\r
662 //\r
663 // Valid 1GB page\r
664 // If we have at least 1GB to go, we can just update this entry\r
665 //\r
666 if (!(PhysicalAddress & (BIT30 - 1)) && Length >= BIT30) {\r
667 SetOrClearCBit(&PageDirectory1GEntry->Uint64, Mode);\r
6692af92
LE
668 DEBUG ((\r
669 DEBUG_VERBOSE,\r
5597edfa 670 "%a:%a: updated 1GB entry for Physical=0x%Lx\n",\r
6692af92
LE
671 gEfiCallerBaseName,\r
672 __FUNCTION__,\r
673 PhysicalAddress\r
674 ));\r
a1f22614
BS
675 PhysicalAddress += BIT30;\r
676 Length -= BIT30;\r
677 } else {\r
678 //\r
679 // We must split the page\r
680 //\r
6692af92
LE
681 DEBUG ((\r
682 DEBUG_VERBOSE,\r
d8d33741 683 "%a:%a: splitting 1GB page for Physical=0x%Lx\n",\r
6692af92 684 gEfiCallerBaseName,\r
631bd7e0
LE
685 __FUNCTION__,\r
686 PhysicalAddress\r
6692af92 687 ));\r
a1f22614
BS
688 Split1GPageTo2M(((UINT64)PageDirectory1GEntry->Bits.PageTableBaseAddress)<<30, (UINT64*) PageDirectory1GEntry, 0, 0);\r
689 continue;\r
690 }\r
691 } else {\r
692 //\r
693 // Actually a PDP\r
694 //\r
695 PageUpperDirectoryPointerEntry = (PAGE_MAP_AND_DIRECTORY_POINTER*) PageDirectory1GEntry;\r
696 PageDirectory2MEntry = (VOID*) ((PageUpperDirectoryPointerEntry->Bits.PageTableBaseAddress<<12) & ~PgTableMask);\r
697 PageDirectory2MEntry += PDE_OFFSET(PhysicalAddress);\r
698 if (!PageDirectory2MEntry->Bits.Present) {\r
6692af92 699 DEBUG ((\r
3728ea5a
LE
700 DEBUG_ERROR,\r
701 "%a:%a: bad PDE for Physical=0x%Lx\n",\r
6692af92
LE
702 gEfiCallerBaseName,\r
703 __FUNCTION__,\r
704 PhysicalAddress\r
705 ));\r
b721aa74
BS
706 Status = RETURN_NO_MAPPING;\r
707 goto Done;\r
a1f22614
BS
708 }\r
709 //\r
710 // If the MustBe1 bit is not a 1, it's not a 2MB entry\r
711 //\r
712 if (PageDirectory2MEntry->Bits.MustBe1) {\r
713 //\r
714 // Valid 2MB page\r
715 // If we have at least 2MB left to go, we can just update this entry\r
716 //\r
717 if (!(PhysicalAddress & (BIT21-1)) && Length >= BIT21) {\r
718 SetOrClearCBit (&PageDirectory2MEntry->Uint64, Mode);\r
719 PhysicalAddress += BIT21;\r
720 Length -= BIT21;\r
721 } else {\r
722 //\r
723 // We must split up this page into 4K pages\r
724 //\r
6692af92
LE
725 DEBUG ((\r
726 DEBUG_VERBOSE,\r
d8d33741 727 "%a:%a: splitting 2MB page for Physical=0x%Lx\n",\r
6692af92
LE
728 gEfiCallerBaseName,\r
729 __FUNCTION__,\r
730 PhysicalAddress\r
731 ));\r
a1f22614
BS
732 Split2MPageTo4K (((UINT64)PageDirectory2MEntry->Bits.PageTableBaseAddress) << 21, (UINT64*) PageDirectory2MEntry, 0, 0);\r
733 continue;\r
734 }\r
735 } else {\r
736 PageDirectoryPointerEntry = (PAGE_MAP_AND_DIRECTORY_POINTER*) PageDirectory2MEntry;\r
737 PageTableEntry = (VOID*) (PageDirectoryPointerEntry->Bits.PageTableBaseAddress<<12 & ~PgTableMask);\r
738 PageTableEntry += PTE_OFFSET(PhysicalAddress);\r
739 if (!PageTableEntry->Bits.Present) {\r
6692af92 740 DEBUG ((\r
3728ea5a
LE
741 DEBUG_ERROR,\r
742 "%a:%a: bad PTE for Physical=0x%Lx\n",\r
6692af92
LE
743 gEfiCallerBaseName,\r
744 __FUNCTION__,\r
745 PhysicalAddress\r
746 ));\r
b721aa74
BS
747 Status = RETURN_NO_MAPPING;\r
748 goto Done;\r
a1f22614
BS
749 }\r
750 SetOrClearCBit (&PageTableEntry->Uint64, Mode);\r
751 PhysicalAddress += EFI_PAGE_SIZE;\r
752 Length -= EFI_PAGE_SIZE;\r
753 }\r
754 }\r
755 }\r
756\r
b721aa74
BS
757 //\r
758 // Protect the page table by marking the memory used for page table to be\r
759 // read-only.\r
760 //\r
761 if (IsWpEnabled) {\r
762 EnablePageTableProtection ((UINTN)PageMapLevel4Entry, TRUE);\r
763 }\r
764\r
a1f22614
BS
765 //\r
766 // Flush TLB\r
767 //\r
768 CpuFlushTlb();\r
769\r
b721aa74
BS
770Done:\r
771 //\r
772 // Restore page table write protection, if any.\r
773 //\r
774 if (IsWpEnabled) {\r
775 EnableReadOnlyPageWriteProtect ();\r
776 }\r
777\r
778 return Status;\r
a1f22614
BS
779}\r
780\r
781/**\r
782 This function clears memory encryption bit for the memory region specified by\r
783 PhysicalAddress and length from the current page table context.\r
784\r
785 @param[in] PhysicalAddress The physical address that is the start\r
786 address of a memory region.\r
787 @param[in] Length The length of memory region\r
788 @param[in] Flush Flush the caches before applying the\r
789 encryption mask\r
790\r
791 @retval RETURN_SUCCESS The attributes were cleared for the memory\r
792 region.\r
793 @retval RETURN_INVALID_PARAMETER Number of pages is zero.\r
794 @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute is\r
795 not supported\r
796**/\r
797RETURN_STATUS\r
798EFIAPI\r
799InternalMemEncryptSevSetMemoryDecrypted (\r
800 IN PHYSICAL_ADDRESS Cr3BaseAddress,\r
801 IN PHYSICAL_ADDRESS PhysicalAddress,\r
802 IN UINTN Length,\r
803 IN BOOLEAN Flush\r
804 )\r
805{\r
806\r
a1f22614
BS
807 return SetMemoryEncDec (Cr3BaseAddress, PhysicalAddress, Length, ClearCBit, Flush);\r
808}\r
809\r
810/**\r
811 This function sets memory encryption bit for the memory region specified by\r
812 PhysicalAddress and length from the current page table context.\r
813\r
814 @param[in] PhysicalAddress The physical address that is the start address\r
815 of a memory region.\r
816 @param[in] Length The length of memory region\r
817 @param[in] Flush Flush the caches before applying the\r
818 encryption mask\r
819\r
820 @retval RETURN_SUCCESS The attributes were cleared for the memory\r
821 region.\r
822 @retval RETURN_INVALID_PARAMETER Number of pages is zero.\r
823 @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute is\r
824 not supported\r
825**/\r
826RETURN_STATUS\r
827EFIAPI\r
828InternalMemEncryptSevSetMemoryEncrypted (\r
829 IN PHYSICAL_ADDRESS Cr3BaseAddress,\r
830 IN PHYSICAL_ADDRESS PhysicalAddress,\r
831 IN UINTN Length,\r
832 IN BOOLEAN Flush\r
833 )\r
834{\r
a1f22614
BS
835 return SetMemoryEncDec (Cr3BaseAddress, PhysicalAddress, Length, SetCBit, Flush);\r
836}\r