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