]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/BaseMemEncryptSevLib/X64/VirtualMemory.c
OvmfPkg/BaseMemEncryptSevLib: promote DEBUG_WARN levels to DEBUG_ERROR
[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
28\r
29typedef enum {\r
30 SetCBit,\r
31 ClearCBit\r
32} MAP_RANGE_MODE;\r
33\r
34/**\r
35 Get the memory encryption mask\r
36\r
37 @param[out] EncryptionMask contains the pte mask.\r
38\r
39**/\r
40STATIC\r
41UINT64\r
42GetMemEncryptionAddressMask (\r
43 VOID\r
44 )\r
45{\r
46 UINT64 EncryptionMask;\r
47 CPUID_MEMORY_ENCRYPTION_INFO_EBX Ebx;\r
48\r
49 if (mAddressEncMaskChecked) {\r
50 return mAddressEncMask;\r
51 }\r
52\r
53 //\r
54 // CPUID Fn8000_001F[EBX] Bit 0:5 (memory encryption bit position)\r
55 //\r
56 AsmCpuid (CPUID_MEMORY_ENCRYPTION_INFO, NULL, &Ebx.Uint32, NULL, NULL);\r
57 EncryptionMask = LShiftU64 (1, Ebx.Bits.PtePosBits);\r
58\r
59 mAddressEncMask = EncryptionMask & PAGING_1G_ADDRESS_MASK_64;\r
60 mAddressEncMaskChecked = TRUE;\r
61\r
62 return mAddressEncMask;\r
63}\r
64\r
65/**\r
66 Split 2M page to 4K.\r
67\r
68 @param[in] PhysicalAddress Start physical address the 2M page covered.\r
69 @param[in, out] PageEntry2M Pointer to 2M page entry.\r
70 @param[in] StackBase Stack base address.\r
71 @param[in] StackSize Stack size.\r
72\r
73**/\r
74STATIC\r
75VOID\r
76Split2MPageTo4K (\r
77 IN PHYSICAL_ADDRESS PhysicalAddress,\r
78 IN OUT UINT64 *PageEntry2M,\r
79 IN PHYSICAL_ADDRESS StackBase,\r
80 IN UINTN StackSize\r
81 )\r
82{\r
83 PHYSICAL_ADDRESS PhysicalAddress4K;\r
84 UINTN IndexOfPageTableEntries;\r
85 PAGE_TABLE_4K_ENTRY *PageTableEntry, *PageTableEntry1;\r
86 UINT64 AddressEncMask;\r
87\r
88 PageTableEntry = AllocatePages(1);\r
89\r
90 PageTableEntry1 = PageTableEntry;\r
91\r
92 AddressEncMask = GetMemEncryptionAddressMask ();\r
93\r
94 ASSERT (PageTableEntry != NULL);\r
95 ASSERT (*PageEntry2M & AddressEncMask);\r
96\r
97 PhysicalAddress4K = PhysicalAddress;\r
98 for (IndexOfPageTableEntries = 0; IndexOfPageTableEntries < 512; IndexOfPageTableEntries++, PageTableEntry++, PhysicalAddress4K += SIZE_4KB) {\r
99 //\r
100 // Fill in the Page Table entries\r
101 //\r
102 PageTableEntry->Uint64 = (UINT64) PhysicalAddress4K | AddressEncMask;\r
103 PageTableEntry->Bits.ReadWrite = 1;\r
104 PageTableEntry->Bits.Present = 1;\r
105 if ((PhysicalAddress4K >= StackBase) && (PhysicalAddress4K < StackBase + StackSize)) {\r
106 //\r
107 // Set Nx bit for stack.\r
108 //\r
109 PageTableEntry->Bits.Nx = 1;\r
110 }\r
111 }\r
112\r
113 //\r
114 // Fill in 2M page entry.\r
115 //\r
116 *PageEntry2M = (UINT64) (UINTN) PageTableEntry1 | IA32_PG_P | IA32_PG_RW | AddressEncMask;\r
117}\r
118\r
119/**\r
120 Split 1G page to 2M.\r
121\r
122 @param[in] PhysicalAddress Start physical address the 1G page covered.\r
123 @param[in, out] PageEntry1G Pointer to 1G page entry.\r
124 @param[in] StackBase Stack base address.\r
125 @param[in] StackSize Stack size.\r
126\r
127**/\r
128STATIC\r
129VOID\r
130Split1GPageTo2M (\r
131 IN PHYSICAL_ADDRESS PhysicalAddress,\r
132 IN OUT UINT64 *PageEntry1G,\r
133 IN PHYSICAL_ADDRESS StackBase,\r
134 IN UINTN StackSize\r
135 )\r
136{\r
137 PHYSICAL_ADDRESS PhysicalAddress2M;\r
138 UINTN IndexOfPageDirectoryEntries;\r
139 PAGE_TABLE_ENTRY *PageDirectoryEntry;\r
140 UINT64 AddressEncMask;\r
141\r
142 PageDirectoryEntry = AllocatePages(1);\r
143\r
144 AddressEncMask = GetMemEncryptionAddressMask ();\r
145 ASSERT (PageDirectoryEntry != NULL);\r
146 ASSERT (*PageEntry1G & GetMemEncryptionAddressMask ());\r
147 //\r
148 // Fill in 1G page entry.\r
149 //\r
150 *PageEntry1G = (UINT64) (UINTN) PageDirectoryEntry | IA32_PG_P | IA32_PG_RW | AddressEncMask;\r
151\r
152 PhysicalAddress2M = PhysicalAddress;\r
153 for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress2M += SIZE_2MB) {\r
154 if ((PhysicalAddress2M < StackBase + StackSize) && ((PhysicalAddress2M + SIZE_2MB) > StackBase)) {\r
155 //\r
156 // Need to split this 2M page that covers stack range.\r
157 //\r
158 Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, StackBase, StackSize);\r
159 } else {\r
160 //\r
161 // Fill in the Page Directory entries\r
162 //\r
163 PageDirectoryEntry->Uint64 = (UINT64) PhysicalAddress2M | AddressEncMask;\r
164 PageDirectoryEntry->Bits.ReadWrite = 1;\r
165 PageDirectoryEntry->Bits.Present = 1;\r
166 PageDirectoryEntry->Bits.MustBe1 = 1;\r
167 }\r
168 }\r
169}\r
170\r
171\r
172/**\r
173 Set or Clear the memory encryption bit\r
174\r
175 @param[in] PagetablePoint Page table entry pointer (PTE).\r
176 @param[in] Mode Set or Clear encryption bit\r
177\r
178**/\r
179STATIC VOID\r
180SetOrClearCBit(\r
181 IN OUT UINT64* PageTablePointer,\r
182 IN MAP_RANGE_MODE Mode\r
183 )\r
184{\r
185 UINT64 AddressEncMask;\r
186\r
187 AddressEncMask = GetMemEncryptionAddressMask ();\r
188\r
189 if (Mode == SetCBit) {\r
190 *PageTablePointer |= AddressEncMask;\r
191 } else {\r
192 *PageTablePointer &= ~AddressEncMask;\r
193 }\r
194\r
195}\r
196\r
197/**\r
198 This function either sets or clears memory encryption bit for the memory region\r
199 specified by PhysicalAddress and length from the current page table context.\r
200\r
201 The function iterates through the physicalAddress one page at a time, and set\r
202 or clears the memory encryption mask in the page table. If it encounters\r
203 that a given physical address range is part of large page then it attempts to\r
204 change the attribute at one go (based on size), otherwise it splits the\r
205 large pages into smaller (e.g 2M page into 4K pages) and then try to set or\r
206 clear the encryption bit on the smallest page size.\r
207\r
208 @param[in] PhysicalAddress The physical address that is the start\r
209 address of a memory region.\r
210 @param[in] Length The length of memory region\r
211 @param[in] Mode Set or Clear mode\r
212 @param[in] Flush Flush the caches before applying the\r
213 encryption mask\r
214\r
215 @retval RETURN_SUCCESS The attributes were cleared for the memory\r
216 region.\r
217 @retval RETURN_INVALID_PARAMETER Number of pages is zero.\r
218 @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute is\r
219 not supported\r
220**/\r
221\r
222STATIC\r
223RETURN_STATUS\r
224EFIAPI\r
225SetMemoryEncDec (\r
226 IN PHYSICAL_ADDRESS Cr3BaseAddress,\r
227 IN PHYSICAL_ADDRESS PhysicalAddress,\r
228 IN UINTN Length,\r
229 IN MAP_RANGE_MODE Mode,\r
230 IN BOOLEAN CacheFlush\r
231 )\r
232{\r
233 PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;\r
234 PAGE_MAP_AND_DIRECTORY_POINTER *PageUpperDirectoryPointerEntry;\r
235 PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;\r
236 PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry;\r
237 PAGE_TABLE_ENTRY *PageDirectory2MEntry;\r
238 PAGE_TABLE_4K_ENTRY *PageTableEntry;\r
239 UINT64 PgTableMask;\r
240 UINT64 AddressEncMask;\r
241\r
70063aec
LE
242 DEBUG ((\r
243 DEBUG_VERBOSE,\r
244 "%a:%a: Cr3Base=0x%Lx Physical=0x%Lx Length=0x%Lx Mode=%a CacheFlush=%u\n",\r
245 gEfiCallerBaseName,\r
246 __FUNCTION__,\r
247 Cr3BaseAddress,\r
248 PhysicalAddress,\r
249 (UINT64)Length,\r
250 (Mode == SetCBit) ? "Encrypt" : "Decrypt",\r
251 (UINT32)CacheFlush\r
252 ));\r
253\r
a1f22614
BS
254 //\r
255 // Check if we have a valid memory encryption mask\r
256 //\r
257 AddressEncMask = GetMemEncryptionAddressMask ();\r
258 if (!AddressEncMask) {\r
259 return RETURN_ACCESS_DENIED;\r
260 }\r
261\r
262 PgTableMask = AddressEncMask | EFI_PAGE_MASK;\r
263\r
264 if (Length == 0) {\r
265 return RETURN_INVALID_PARAMETER;\r
266 }\r
267\r
268 //\r
269 // We are going to change the memory encryption attribute from C=0 -> C=1 or\r
270 // vice versa Flush the caches to ensure that data is written into memory with\r
271 // correct C-bit\r
272 //\r
273 if (CacheFlush) {\r
274 WriteBackInvalidateDataCacheRange((VOID*) (UINTN)PhysicalAddress, Length);\r
275 }\r
276\r
277 while (Length)\r
278 {\r
279 //\r
280 // If Cr3BaseAddress is not specified then read the current CR3\r
281 //\r
282 if (Cr3BaseAddress == 0) {\r
283 Cr3BaseAddress = AsmReadCr3();\r
284 }\r
285\r
286 PageMapLevel4Entry = (VOID*) (Cr3BaseAddress & ~PgTableMask);\r
287 PageMapLevel4Entry += PML4_OFFSET(PhysicalAddress);\r
288 if (!PageMapLevel4Entry->Bits.Present) {\r
6692af92 289 DEBUG ((\r
3728ea5a
LE
290 DEBUG_ERROR,\r
291 "%a:%a: bad PML4 for Physical=0x%Lx\n",\r
6692af92
LE
292 gEfiCallerBaseName,\r
293 __FUNCTION__,\r
294 PhysicalAddress\r
295 ));\r
a1f22614
BS
296 return RETURN_NO_MAPPING;\r
297 }\r
298\r
299 PageDirectory1GEntry = (VOID*) ((PageMapLevel4Entry->Bits.PageTableBaseAddress<<12) & ~PgTableMask);\r
300 PageDirectory1GEntry += PDP_OFFSET(PhysicalAddress);\r
301 if (!PageDirectory1GEntry->Bits.Present) {\r
6692af92 302 DEBUG ((\r
3728ea5a
LE
303 DEBUG_ERROR,\r
304 "%a:%a: bad PDPE for Physical=0x%Lx\n",\r
6692af92
LE
305 gEfiCallerBaseName,\r
306 __FUNCTION__,\r
307 PhysicalAddress\r
308 ));\r
a1f22614
BS
309 return RETURN_NO_MAPPING;\r
310 }\r
311\r
312 //\r
313 // If the MustBe1 bit is not 1, it's not actually a 1GB entry\r
314 //\r
315 if (PageDirectory1GEntry->Bits.MustBe1) {\r
316 //\r
317 // Valid 1GB page\r
318 // If we have at least 1GB to go, we can just update this entry\r
319 //\r
320 if (!(PhysicalAddress & (BIT30 - 1)) && Length >= BIT30) {\r
321 SetOrClearCBit(&PageDirectory1GEntry->Uint64, Mode);\r
6692af92
LE
322 DEBUG ((\r
323 DEBUG_VERBOSE,\r
631bd7e0 324 "%a:%a: Updated 1GB entry for Physical=0x%Lx\n",\r
6692af92
LE
325 gEfiCallerBaseName,\r
326 __FUNCTION__,\r
327 PhysicalAddress\r
328 ));\r
a1f22614
BS
329 PhysicalAddress += BIT30;\r
330 Length -= BIT30;\r
331 } else {\r
332 //\r
333 // We must split the page\r
334 //\r
6692af92
LE
335 DEBUG ((\r
336 DEBUG_VERBOSE,\r
631bd7e0 337 "%a:%a: Spliting 1GB page for Physical=0x%Lx\n",\r
6692af92 338 gEfiCallerBaseName,\r
631bd7e0
LE
339 __FUNCTION__,\r
340 PhysicalAddress\r
6692af92 341 ));\r
a1f22614
BS
342 Split1GPageTo2M(((UINT64)PageDirectory1GEntry->Bits.PageTableBaseAddress)<<30, (UINT64*) PageDirectory1GEntry, 0, 0);\r
343 continue;\r
344 }\r
345 } else {\r
346 //\r
347 // Actually a PDP\r
348 //\r
349 PageUpperDirectoryPointerEntry = (PAGE_MAP_AND_DIRECTORY_POINTER*) PageDirectory1GEntry;\r
350 PageDirectory2MEntry = (VOID*) ((PageUpperDirectoryPointerEntry->Bits.PageTableBaseAddress<<12) & ~PgTableMask);\r
351 PageDirectory2MEntry += PDE_OFFSET(PhysicalAddress);\r
352 if (!PageDirectory2MEntry->Bits.Present) {\r
6692af92 353 DEBUG ((\r
3728ea5a
LE
354 DEBUG_ERROR,\r
355 "%a:%a: bad PDE for Physical=0x%Lx\n",\r
6692af92
LE
356 gEfiCallerBaseName,\r
357 __FUNCTION__,\r
358 PhysicalAddress\r
359 ));\r
a1f22614
BS
360 return RETURN_NO_MAPPING;\r
361 }\r
362 //\r
363 // If the MustBe1 bit is not a 1, it's not a 2MB entry\r
364 //\r
365 if (PageDirectory2MEntry->Bits.MustBe1) {\r
366 //\r
367 // Valid 2MB page\r
368 // If we have at least 2MB left to go, we can just update this entry\r
369 //\r
370 if (!(PhysicalAddress & (BIT21-1)) && Length >= BIT21) {\r
371 SetOrClearCBit (&PageDirectory2MEntry->Uint64, Mode);\r
372 PhysicalAddress += BIT21;\r
373 Length -= BIT21;\r
374 } else {\r
375 //\r
376 // We must split up this page into 4K pages\r
377 //\r
6692af92
LE
378 DEBUG ((\r
379 DEBUG_VERBOSE,\r
631bd7e0 380 "%a:%a: Spliting 2MB page for Physical=0x%Lx\n",\r
6692af92
LE
381 gEfiCallerBaseName,\r
382 __FUNCTION__,\r
383 PhysicalAddress\r
384 ));\r
a1f22614
BS
385 Split2MPageTo4K (((UINT64)PageDirectory2MEntry->Bits.PageTableBaseAddress) << 21, (UINT64*) PageDirectory2MEntry, 0, 0);\r
386 continue;\r
387 }\r
388 } else {\r
389 PageDirectoryPointerEntry = (PAGE_MAP_AND_DIRECTORY_POINTER*) PageDirectory2MEntry;\r
390 PageTableEntry = (VOID*) (PageDirectoryPointerEntry->Bits.PageTableBaseAddress<<12 & ~PgTableMask);\r
391 PageTableEntry += PTE_OFFSET(PhysicalAddress);\r
392 if (!PageTableEntry->Bits.Present) {\r
6692af92 393 DEBUG ((\r
3728ea5a
LE
394 DEBUG_ERROR,\r
395 "%a:%a: bad PTE for Physical=0x%Lx\n",\r
6692af92
LE
396 gEfiCallerBaseName,\r
397 __FUNCTION__,\r
398 PhysicalAddress\r
399 ));\r
a1f22614
BS
400 return RETURN_NO_MAPPING;\r
401 }\r
402 SetOrClearCBit (&PageTableEntry->Uint64, Mode);\r
403 PhysicalAddress += EFI_PAGE_SIZE;\r
404 Length -= EFI_PAGE_SIZE;\r
405 }\r
406 }\r
407 }\r
408\r
409 //\r
410 // Flush TLB\r
411 //\r
412 CpuFlushTlb();\r
413\r
414 return RETURN_SUCCESS;\r
415}\r
416\r
417/**\r
418 This function clears memory encryption bit for the memory region specified by\r
419 PhysicalAddress and length from the current page table context.\r
420\r
421 @param[in] PhysicalAddress The physical address that is the start\r
422 address of a memory region.\r
423 @param[in] Length The length of memory region\r
424 @param[in] Flush Flush the caches before applying the\r
425 encryption mask\r
426\r
427 @retval RETURN_SUCCESS The attributes were cleared for the memory\r
428 region.\r
429 @retval RETURN_INVALID_PARAMETER Number of pages is zero.\r
430 @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute is\r
431 not supported\r
432**/\r
433RETURN_STATUS\r
434EFIAPI\r
435InternalMemEncryptSevSetMemoryDecrypted (\r
436 IN PHYSICAL_ADDRESS Cr3BaseAddress,\r
437 IN PHYSICAL_ADDRESS PhysicalAddress,\r
438 IN UINTN Length,\r
439 IN BOOLEAN Flush\r
440 )\r
441{\r
442\r
a1f22614
BS
443 return SetMemoryEncDec (Cr3BaseAddress, PhysicalAddress, Length, ClearCBit, Flush);\r
444}\r
445\r
446/**\r
447 This function sets memory encryption bit for the memory region specified by\r
448 PhysicalAddress and length from the current page table context.\r
449\r
450 @param[in] PhysicalAddress The physical address that is the start address\r
451 of a memory region.\r
452 @param[in] Length The length of memory region\r
453 @param[in] Flush Flush the caches before applying the\r
454 encryption mask\r
455\r
456 @retval RETURN_SUCCESS The attributes were cleared for the memory\r
457 region.\r
458 @retval RETURN_INVALID_PARAMETER Number of pages is zero.\r
459 @retval RETURN_UNSUPPORTED Setting the memory encyrption attribute is\r
460 not supported\r
461**/\r
462RETURN_STATUS\r
463EFIAPI\r
464InternalMemEncryptSevSetMemoryEncrypted (\r
465 IN PHYSICAL_ADDRESS Cr3BaseAddress,\r
466 IN PHYSICAL_ADDRESS PhysicalAddress,\r
467 IN UINTN Length,\r
468 IN BOOLEAN Flush\r
469 )\r
470{\r
a1f22614
BS
471 return SetMemoryEncDec (Cr3BaseAddress, PhysicalAddress, Length, SetCBit, Flush);\r
472}\r