]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/CpuDxe/CpuPageTable.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / UefiCpuPkg / CpuDxe / CpuPageTable.c
CommitLineData
22292ed3
JY
1/** @file\r
2 Page table management support.\r
3\r
29355b4e 4 Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR>\r
627dcba3
LD
5 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
6\r
0acd8697 7 SPDX-License-Identifier: BSD-2-Clause-Patent\r
22292ed3
JY
8\r
9**/\r
10\r
11#include <Base.h>\r
12#include <Uefi.h>\r
dcc02621
JW
13#include <Library/PeCoffGetEntryPointLib.h>\r
14#include <Library/SerialPortLib.h>\r
15#include <Library/SynchronizationLib.h>\r
16#include <Library/PrintLib.h>\r
2a1408d1 17#include <Protocol/SmmBase2.h>\r
01acb06c
RN
18#include <Register/Intel/Cpuid.h>\r
19#include <Register/Intel/Msr.h>\r
c1cab54c
JW
20\r
21#include "CpuDxe.h"\r
22292ed3
JY
22#include "CpuPageTable.h"\r
23\r
24///\r
25/// Page Table Entry\r
26///\r
053e878b
MK
27#define IA32_PG_P BIT0\r
28#define IA32_PG_RW BIT1\r
29#define IA32_PG_U BIT2\r
30#define IA32_PG_WT BIT3\r
31#define IA32_PG_CD BIT4\r
32#define IA32_PG_A BIT5\r
33#define IA32_PG_D BIT6\r
34#define IA32_PG_PS BIT7\r
35#define IA32_PG_PAT_2M BIT12\r
36#define IA32_PG_PAT_4K IA32_PG_PS\r
37#define IA32_PG_PMNT BIT62\r
38#define IA32_PG_NX BIT63\r
39\r
4bfd668e
RN
40#define PAGE_ATTRIBUTE_BITS (IA32_PG_D | IA32_PG_A | IA32_PG_U | IA32_PG_RW | IA32_PG_P)\r
41#define PAGE_ATTRIBUTE_BITS_POST_SPLIT (IA32_PG_RW | IA32_PG_P)\r
42\r
22292ed3
JY
43//\r
44// Bits 1, 2, 5, 6 are reserved in the IA32 PAE PDPTE\r
45// X64 PAE PDPTE does not have such restriction\r
46//\r
053e878b 47#define IA32_PAE_PDPTE_ATTRIBUTE_BITS (IA32_PG_P)\r
22292ed3 48\r
053e878b 49#define PAGE_PROGATE_BITS (IA32_PG_NX | PAGE_ATTRIBUTE_BITS)\r
22292ed3
JY
50\r
51#define PAGING_4K_MASK 0xFFF\r
52#define PAGING_2M_MASK 0x1FFFFF\r
53#define PAGING_1G_MASK 0x3FFFFFFF\r
54\r
55#define PAGING_PAE_INDEX_MASK 0x1FF\r
56\r
053e878b
MK
57#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull\r
58#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull\r
59#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull\r
22292ed3 60\r
dcc02621
JW
61#define MAX_PF_ENTRY_COUNT 10\r
62#define MAX_DEBUG_MESSAGE_LENGTH 0x100\r
63#define IA32_PF_EC_ID BIT4\r
64\r
22292ed3
JY
65typedef enum {\r
66 PageNone,\r
67 Page4K,\r
68 Page2M,\r
69 Page1G,\r
70} PAGE_ATTRIBUTE;\r
71\r
72typedef struct {\r
053e878b
MK
73 PAGE_ATTRIBUTE Attribute;\r
74 UINT64 Length;\r
75 UINT64 AddressMask;\r
22292ed3
JY
76} PAGE_ATTRIBUTE_TABLE;\r
77\r
78typedef enum {\r
79 PageActionAssign,\r
80 PageActionSet,\r
81 PageActionClear,\r
82} PAGE_ACTION;\r
83\r
053e878b
MK
84PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {\r
85 { Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64 },\r
86 { Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64 },\r
87 { Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64 },\r
22292ed3
JY
88};\r
89\r
053e878b
MK
90PAGE_TABLE_POOL *mPageTablePool = NULL;\r
91BOOLEAN mPageTablePoolLock = FALSE;\r
92PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext;\r
93EFI_SMM_BASE2_PROTOCOL *mSmmBase2 = NULL;\r
2a1408d1 94\r
dcc02621
JW
95//\r
96// Record the page fault exception count for one instruction execution.\r
97//\r
053e878b 98UINTN *mPFEntryCount;\r
dcc02621
JW
99UINT64 *(*mLastPFEntryPointer)[MAX_PF_ENTRY_COUNT];\r
100\r
2a1408d1
JW
101/**\r
102 Check if current execution environment is in SMM mode or not, via\r
103 EFI_SMM_BASE2_PROTOCOL.\r
104\r
105 This is necessary because of the fact that MdePkg\Library\SmmMemoryAllocationLib\r
106 supports to free memory outside SMRAM. The library will call gBS->FreePool() or\r
107 gBS->FreePages() and then SetMemorySpaceAttributes interface in turn to change\r
108 memory paging attributes during free operation, if some memory related features\r
109 are enabled (like Heap Guard).\r
110\r
111 This means that SetMemorySpaceAttributes() has chance to run in SMM mode. This\r
112 will cause incorrect result because SMM mode always loads its own page tables,\r
113 which are usually different from DXE. This function can be used to detect such\r
114 situation and help to avoid further misoperations.\r
115\r
116 @retval TRUE In SMM mode.\r
117 @retval FALSE Not in SMM mode.\r
118**/\r
119BOOLEAN\r
120IsInSmm (\r
121 VOID\r
122 )\r
123{\r
053e878b 124 BOOLEAN InSmm;\r
2a1408d1
JW
125\r
126 InSmm = FALSE;\r
127 if (mSmmBase2 == NULL) {\r
128 gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **)&mSmmBase2);\r
129 }\r
130\r
131 if (mSmmBase2 != NULL) {\r
132 mSmmBase2->InSmm (mSmmBase2, &InSmm);\r
133 }\r
134\r
b72f4873
JW
135 //\r
136 // mSmmBase2->InSmm() can only detect if the caller is running in SMRAM\r
137 // or from SMM driver. It cannot tell if the caller is running in SMM mode.\r
138 // Check page table base address to guarantee that because SMM mode willl\r
139 // load its own page table.\r
140 //\r
141 return (InSmm &&\r
053e878b 142 mPagingContext.ContextData.X64.PageTableBase != (UINT64)AsmReadCr3 ());\r
2a1408d1 143}\r
147fd35c 144\r
22292ed3
JY
145/**\r
146 Return current paging context.\r
147\r
148 @param[in,out] PagingContext The paging context.\r
149**/\r
150VOID\r
151GetCurrentPagingContext (\r
053e878b 152 IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext\r
22292ed3
JY
153 )\r
154{\r
053e878b
MK
155 UINT32 RegEax;\r
156 CPUID_EXTENDED_CPU_SIG_EDX RegEdx;\r
157 MSR_IA32_EFER_REGISTER MsrEfer;\r
158 IA32_CR4 Cr4;\r
159 IA32_CR0 Cr0;\r
160 UINT32 *Attributes;\r
161 UINTN *PageTableBase;\r
22292ed3 162\r
2a1408d1
JW
163 //\r
164 // Don't retrieve current paging context from processor if in SMM mode.\r
165 //\r
166 if (!IsInSmm ()) {\r
053e878b
MK
167 ZeroMem (&mPagingContext, sizeof (mPagingContext));\r
168 if (sizeof (UINTN) == sizeof (UINT64)) {\r
2a1408d1
JW
169 mPagingContext.MachineType = IMAGE_FILE_MACHINE_X64;\r
170 } else {\r
171 mPagingContext.MachineType = IMAGE_FILE_MACHINE_I386;\r
172 }\r
29355b4e 173\r
c70fef96
ED
174 GetPagingDetails (&mPagingContext.ContextData, &PageTableBase, &Attributes);\r
175\r
29355b4e
RN
176 Cr0.UintN = AsmReadCr0 ();\r
177 Cr4.UintN = AsmReadCr4 ();\r
178\r
179 if (Cr0.Bits.PG != 0) {\r
c70fef96 180 *PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);\r
2a1408d1 181 } else {\r
c70fef96 182 *PageTableBase = 0;\r
2a1408d1 183 }\r
053e878b 184\r
29355b4e 185 if (Cr0.Bits.WP != 0) {\r
c70fef96 186 *Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE;\r
29355b4e 187 }\r
053e878b 188\r
29355b4e 189 if (Cr4.Bits.PSE != 0) {\r
c70fef96 190 *Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE;\r
2a1408d1 191 }\r
053e878b 192\r
29355b4e 193 if (Cr4.Bits.PAE != 0) {\r
c70fef96 194 *Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE;\r
2a1408d1 195 }\r
053e878b 196\r
e58aa474 197 if (Cr4.Bits.LA57 != 0) {\r
c70fef96 198 *Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_5_LEVEL;\r
e58aa474 199 }\r
22292ed3 200\r
d106cf71
JW
201 AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);\r
202 if (RegEax >= CPUID_EXTENDED_CPU_SIG) {\r
203 AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx.Uint32);\r
204\r
205 if (RegEdx.Bits.NX != 0) {\r
2a1408d1 206 // XD supported\r
053e878b 207 MsrEfer.Uint64 = AsmReadMsr64 (MSR_CORE_IA32_EFER);\r
d106cf71 208 if (MsrEfer.Bits.NXE != 0) {\r
2a1408d1 209 // XD activated\r
c70fef96 210 *Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED;\r
2a1408d1
JW
211 }\r
212 }\r
d106cf71
JW
213\r
214 if (RegEdx.Bits.Page1GB != 0) {\r
c70fef96 215 *Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT;\r
22292ed3 216 }\r
22292ed3
JY
217 }\r
218 }\r
2a1408d1
JW
219\r
220 //\r
221 // This can avoid getting SMM paging context if in SMM mode. We cannot assume\r
222 // SMM mode shares the same paging context as DXE.\r
223 //\r
224 CopyMem (PagingContext, &mPagingContext, sizeof (mPagingContext));\r
22292ed3
JY
225}\r
226\r
227/**\r
228 Return length according to page attributes.\r
229\r
230 @param[in] PageAttributes The page attribute of the page entry.\r
231\r
232 @return The length of page entry.\r
233**/\r
234UINTN\r
235PageAttributeToLength (\r
236 IN PAGE_ATTRIBUTE PageAttribute\r
237 )\r
238{\r
239 UINTN Index;\r
053e878b
MK
240\r
241 for (Index = 0; Index < sizeof (mPageAttributeTable)/sizeof (mPageAttributeTable[0]); Index++) {\r
22292ed3
JY
242 if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
243 return (UINTN)mPageAttributeTable[Index].Length;\r
244 }\r
245 }\r
053e878b 246\r
22292ed3
JY
247 return 0;\r
248}\r
249\r
250/**\r
251 Return address mask according to page attributes.\r
252\r
253 @param[in] PageAttributes The page attribute of the page entry.\r
254\r
255 @return The address mask of page entry.\r
256**/\r
257UINTN\r
258PageAttributeToMask (\r
259 IN PAGE_ATTRIBUTE PageAttribute\r
260 )\r
261{\r
262 UINTN Index;\r
053e878b
MK
263\r
264 for (Index = 0; Index < sizeof (mPageAttributeTable)/sizeof (mPageAttributeTable[0]); Index++) {\r
22292ed3
JY
265 if (PageAttribute == mPageAttributeTable[Index].Attribute) {\r
266 return (UINTN)mPageAttributeTable[Index].AddressMask;\r
267 }\r
268 }\r
053e878b 269\r
22292ed3
JY
270 return 0;\r
271}\r
272\r
273/**\r
274 Return page table entry to match the address.\r
275\r
276 @param[in] PagingContext The paging context.\r
277 @param[in] Address The address to be checked.\r
278 @param[out] PageAttributes The page attribute of the page entry.\r
279\r
280 @return The page entry.\r
281**/\r
282VOID *\r
283GetPageTableEntry (\r
053e878b
MK
284 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext,\r
285 IN PHYSICAL_ADDRESS Address,\r
286 OUT PAGE_ATTRIBUTE *PageAttribute\r
22292ed3
JY
287 )\r
288{\r
053e878b
MK
289 UINTN Index1;\r
290 UINTN Index2;\r
291 UINTN Index3;\r
292 UINTN Index4;\r
293 UINTN Index5;\r
294 UINT64 *L1PageTable;\r
295 UINT64 *L2PageTable;\r
296 UINT64 *L3PageTable;\r
297 UINT64 *L4PageTable;\r
298 UINT64 *L5PageTable;\r
299 UINT64 AddressEncMask;\r
22292ed3
JY
300\r
301 ASSERT (PagingContext != NULL);\r
302\r
e58aa474 303 Index5 = ((UINTN)RShiftU64 (Address, 48)) & PAGING_PAE_INDEX_MASK;\r
22292ed3
JY
304 Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;\r
305 Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;\r
306 Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;\r
307 Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;\r
308\r
627dcba3
LD
309 // Make sure AddressEncMask is contained to smallest supported address field.\r
310 //\r
311 AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;\r
cc3620f3
MX
312 if (AddressEncMask == 0) {\r
313 AddressEncMask = PcdGet64 (PcdTdxSharedBitMask) & PAGING_1G_ADDRESS_MASK_64;\r
314 }\r
627dcba3 315\r
22292ed3 316 if (PagingContext->MachineType == IMAGE_FILE_MACHINE_X64) {\r
e58aa474
RN
317 if ((PagingContext->ContextData.X64.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_5_LEVEL) != 0) {\r
318 L5PageTable = (UINT64 *)(UINTN)PagingContext->ContextData.X64.PageTableBase;\r
319 if (L5PageTable[Index5] == 0) {\r
320 *PageAttribute = PageNone;\r
321 return NULL;\r
322 }\r
323\r
324 L4PageTable = (UINT64 *)(UINTN)(L5PageTable[Index5] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
325 } else {\r
326 L4PageTable = (UINT64 *)(UINTN)PagingContext->ContextData.X64.PageTableBase;\r
327 }\r
053e878b 328\r
22292ed3
JY
329 if (L4PageTable[Index4] == 0) {\r
330 *PageAttribute = PageNone;\r
331 return NULL;\r
332 }\r
333\r
627dcba3 334 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
22292ed3 335 } else {\r
053e878b 336 ASSERT ((PagingContext->ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) != 0);\r
22292ed3
JY
337 L3PageTable = (UINT64 *)(UINTN)PagingContext->ContextData.Ia32.PageTableBase;\r
338 }\r
053e878b 339\r
22292ed3
JY
340 if (L3PageTable[Index3] == 0) {\r
341 *PageAttribute = PageNone;\r
342 return NULL;\r
343 }\r
053e878b 344\r
22292ed3
JY
345 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {\r
346 // 1G\r
347 *PageAttribute = Page1G;\r
348 return &L3PageTable[Index3];\r
349 }\r
350\r
627dcba3 351 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
22292ed3
JY
352 if (L2PageTable[Index2] == 0) {\r
353 *PageAttribute = PageNone;\r
354 return NULL;\r
355 }\r
053e878b 356\r
22292ed3
JY
357 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {\r
358 // 2M\r
359 *PageAttribute = Page2M;\r
360 return &L2PageTable[Index2];\r
361 }\r
362\r
363 // 4k\r
627dcba3 364 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~AddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
22292ed3
JY
365 if ((L1PageTable[Index1] == 0) && (Address != 0)) {\r
366 *PageAttribute = PageNone;\r
367 return NULL;\r
368 }\r
053e878b 369\r
22292ed3
JY
370 *PageAttribute = Page4K;\r
371 return &L1PageTable[Index1];\r
372}\r
373\r
374/**\r
375 Return memory attributes of page entry.\r
376\r
377 @param[in] PageEntry The page entry.\r
378\r
379 @return Memory attributes of page entry.\r
380**/\r
381UINT64\r
382GetAttributesFromPageEntry (\r
053e878b 383 IN UINT64 *PageEntry\r
22292ed3
JY
384 )\r
385{\r
386 UINT64 Attributes;\r
053e878b 387\r
22292ed3
JY
388 Attributes = 0;\r
389 if ((*PageEntry & IA32_PG_P) == 0) {\r
390 Attributes |= EFI_MEMORY_RP;\r
391 }\r
053e878b 392\r
22292ed3
JY
393 if ((*PageEntry & IA32_PG_RW) == 0) {\r
394 Attributes |= EFI_MEMORY_RO;\r
395 }\r
053e878b 396\r
22292ed3
JY
397 if ((*PageEntry & IA32_PG_NX) != 0) {\r
398 Attributes |= EFI_MEMORY_XP;\r
399 }\r
053e878b 400\r
22292ed3
JY
401 return Attributes;\r
402}\r
403\r
404/**\r
405 Modify memory attributes of page entry.\r
406\r
407 @param[in] PagingContext The paging context.\r
408 @param[in] PageEntry The page entry.\r
409 @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
410 @param[in] PageAction The page action.\r
411 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
412**/\r
413VOID\r
414ConvertPageEntryAttribute (\r
053e878b
MK
415 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext,\r
416 IN UINT64 *PageEntry,\r
417 IN UINT64 Attributes,\r
418 IN PAGE_ACTION PageAction,\r
419 OUT BOOLEAN *IsModified\r
22292ed3
JY
420 )\r
421{\r
422 UINT64 CurrentPageEntry;\r
423 UINT64 NewPageEntry;\r
c70fef96 424 UINT32 *PageAttributes;\r
22292ed3
JY
425\r
426 CurrentPageEntry = *PageEntry;\r
053e878b 427 NewPageEntry = CurrentPageEntry;\r
22292ed3
JY
428 if ((Attributes & EFI_MEMORY_RP) != 0) {\r
429 switch (PageAction) {\r
053e878b
MK
430 case PageActionAssign:\r
431 case PageActionSet:\r
432 NewPageEntry &= ~(UINT64)IA32_PG_P;\r
433 break;\r
434 case PageActionClear:\r
435 NewPageEntry |= IA32_PG_P;\r
436 break;\r
22292ed3
JY
437 }\r
438 } else {\r
439 switch (PageAction) {\r
053e878b
MK
440 case PageActionAssign:\r
441 NewPageEntry |= IA32_PG_P;\r
442 break;\r
443 case PageActionSet:\r
444 case PageActionClear:\r
445 break;\r
22292ed3
JY
446 }\r
447 }\r
053e878b 448\r
22292ed3
JY
449 if ((Attributes & EFI_MEMORY_RO) != 0) {\r
450 switch (PageAction) {\r
053e878b
MK
451 case PageActionAssign:\r
452 case PageActionSet:\r
453 NewPageEntry &= ~(UINT64)IA32_PG_RW;\r
454 break;\r
455 case PageActionClear:\r
456 NewPageEntry |= IA32_PG_RW;\r
457 break;\r
22292ed3
JY
458 }\r
459 } else {\r
460 switch (PageAction) {\r
053e878b
MK
461 case PageActionAssign:\r
462 NewPageEntry |= IA32_PG_RW;\r
463 break;\r
464 case PageActionSet:\r
465 case PageActionClear:\r
466 break;\r
22292ed3
JY
467 }\r
468 }\r
c70fef96
ED
469\r
470 GetPagingDetails (&PagingContext->ContextData, NULL, &PageAttributes);\r
471\r
472 if ((*PageAttributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED) != 0) {\r
22292ed3
JY
473 if ((Attributes & EFI_MEMORY_XP) != 0) {\r
474 switch (PageAction) {\r
053e878b
MK
475 case PageActionAssign:\r
476 case PageActionSet:\r
477 NewPageEntry |= IA32_PG_NX;\r
478 break;\r
479 case PageActionClear:\r
480 NewPageEntry &= ~IA32_PG_NX;\r
481 break;\r
22292ed3
JY
482 }\r
483 } else {\r
484 switch (PageAction) {\r
053e878b
MK
485 case PageActionAssign:\r
486 NewPageEntry &= ~IA32_PG_NX;\r
487 break;\r
488 case PageActionSet:\r
489 case PageActionClear:\r
490 break;\r
22292ed3
JY
491 }\r
492 }\r
493 }\r
053e878b 494\r
22292ed3
JY
495 *PageEntry = NewPageEntry;\r
496 if (CurrentPageEntry != NewPageEntry) {\r
497 *IsModified = TRUE;\r
827330cc
JW
498 DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));\r
499 DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry));\r
22292ed3
JY
500 } else {\r
501 *IsModified = FALSE;\r
502 }\r
503}\r
504\r
505/**\r
506 This function returns if there is need to split page entry.\r
507\r
508 @param[in] BaseAddress The base address to be checked.\r
509 @param[in] Length The length to be checked.\r
510 @param[in] PageEntry The page entry to be checked.\r
511 @param[in] PageAttribute The page attribute of the page entry.\r
512\r
513 @retval SplitAttributes on if there is need to split page entry.\r
514**/\r
515PAGE_ATTRIBUTE\r
516NeedSplitPage (\r
053e878b
MK
517 IN PHYSICAL_ADDRESS BaseAddress,\r
518 IN UINT64 Length,\r
519 IN UINT64 *PageEntry,\r
520 IN PAGE_ATTRIBUTE PageAttribute\r
22292ed3
JY
521 )\r
522{\r
053e878b 523 UINT64 PageEntryLength;\r
22292ed3
JY
524\r
525 PageEntryLength = PageAttributeToLength (PageAttribute);\r
526\r
527 if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {\r
528 return PageNone;\r
529 }\r
530\r
531 if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {\r
532 return Page4K;\r
533 }\r
534\r
535 return Page2M;\r
536}\r
537\r
538/**\r
539 This function splits one page entry to small page entries.\r
540\r
541 @param[in] PageEntry The page entry to be splitted.\r
542 @param[in] PageAttribute The page attribute of the page entry.\r
543 @param[in] SplitAttribute How to split the page entry.\r
544 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.\r
545\r
546 @retval RETURN_SUCCESS The page entry is splitted.\r
547 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.\r
548 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.\r
549**/\r
550RETURN_STATUS\r
551SplitPage (\r
053e878b
MK
552 IN UINT64 *PageEntry,\r
553 IN PAGE_ATTRIBUTE PageAttribute,\r
554 IN PAGE_ATTRIBUTE SplitAttribute,\r
555 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc\r
22292ed3
JY
556 )\r
557{\r
053e878b
MK
558 UINT64 BaseAddress;\r
559 UINT64 *NewPageEntry;\r
560 UINTN Index;\r
561 UINT64 AddressEncMask;\r
22292ed3
JY
562\r
563 ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);\r
564\r
565 ASSERT (AllocatePagesFunc != NULL);\r
566\r
627dcba3
LD
567 // Make sure AddressEncMask is contained to smallest supported address field.\r
568 //\r
569 AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;\r
570\r
22292ed3
JY
571 if (PageAttribute == Page2M) {\r
572 //\r
573 // Split 2M to 4K\r
574 //\r
575 ASSERT (SplitAttribute == Page4K);\r
576 if (SplitAttribute == Page4K) {\r
577 NewPageEntry = AllocatePagesFunc (1);\r
7c7c8190 578 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));\r
22292ed3
JY
579 if (NewPageEntry == NULL) {\r
580 return RETURN_OUT_OF_RESOURCES;\r
581 }\r
053e878b 582\r
627dcba3 583 BaseAddress = *PageEntry & ~AddressEncMask & PAGING_2M_ADDRESS_MASK_64;\r
053e878b 584 for (Index = 0; Index < SIZE_4KB / sizeof (UINT64); Index++) {\r
627dcba3 585 NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
22292ed3 586 }\r
053e878b 587\r
4bfd668e 588 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | PAGE_ATTRIBUTE_BITS_POST_SPLIT;\r
22292ed3
JY
589 return RETURN_SUCCESS;\r
590 } else {\r
591 return RETURN_UNSUPPORTED;\r
592 }\r
593 } else if (PageAttribute == Page1G) {\r
594 //\r
595 // Split 1G to 2M\r
596 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.\r
597 //\r
598 ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);\r
053e878b 599 if (((SplitAttribute == Page2M) || (SplitAttribute == Page4K))) {\r
22292ed3 600 NewPageEntry = AllocatePagesFunc (1);\r
7c7c8190 601 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));\r
22292ed3
JY
602 if (NewPageEntry == NULL) {\r
603 return RETURN_OUT_OF_RESOURCES;\r
604 }\r
053e878b 605\r
627dcba3 606 BaseAddress = *PageEntry & ~AddressEncMask & PAGING_1G_ADDRESS_MASK_64;\r
053e878b 607 for (Index = 0; Index < SIZE_4KB / sizeof (UINT64); Index++) {\r
627dcba3 608 NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | AddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS);\r
22292ed3 609 }\r
053e878b 610\r
4bfd668e 611 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | PAGE_ATTRIBUTE_BITS_POST_SPLIT;\r
22292ed3
JY
612 return RETURN_SUCCESS;\r
613 } else {\r
614 return RETURN_UNSUPPORTED;\r
615 }\r
616 } else {\r
617 return RETURN_UNSUPPORTED;\r
618 }\r
619}\r
620\r
147fd35c
JW
621/**\r
622 Check the WP status in CR0 register. This bit is used to lock or unlock write\r
623 access to pages marked as read-only.\r
624\r
625 @retval TRUE Write protection is enabled.\r
626 @retval FALSE Write protection is disabled.\r
627**/\r
628BOOLEAN\r
629IsReadOnlyPageWriteProtected (\r
630 VOID\r
631 )\r
632{\r
29355b4e 633 IA32_CR0 Cr0;\r
053e878b 634\r
2a1408d1
JW
635 //\r
636 // To avoid unforseen consequences, don't touch paging settings in SMM mode\r
637 // in this driver.\r
638 //\r
639 if (!IsInSmm ()) {\r
29355b4e 640 Cr0.UintN = AsmReadCr0 ();\r
053e878b 641 return (BOOLEAN)(Cr0.Bits.WP != 0);\r
2a1408d1 642 }\r
053e878b 643\r
2a1408d1 644 return FALSE;\r
147fd35c
JW
645}\r
646\r
147fd35c
JW
647/**\r
648 Disable Write Protect on pages marked as read-only.\r
649**/\r
650VOID\r
651DisableReadOnlyPageWriteProtect (\r
652 VOID\r
653 )\r
654{\r
29355b4e 655 IA32_CR0 Cr0;\r
053e878b 656\r
2a1408d1
JW
657 //\r
658 // To avoid unforseen consequences, don't touch paging settings in SMM mode\r
659 // in this driver.\r
660 //\r
661 if (!IsInSmm ()) {\r
053e878b 662 Cr0.UintN = AsmReadCr0 ();\r
29355b4e
RN
663 Cr0.Bits.WP = 0;\r
664 AsmWriteCr0 (Cr0.UintN);\r
2a1408d1 665 }\r
147fd35c
JW
666}\r
667\r
668/**\r
669 Enable Write Protect on pages marked as read-only.\r
670**/\r
671VOID\r
672EnableReadOnlyPageWriteProtect (\r
673 VOID\r
674 )\r
675{\r
29355b4e 676 IA32_CR0 Cr0;\r
053e878b 677\r
2a1408d1
JW
678 //\r
679 // To avoid unforseen consequences, don't touch paging settings in SMM mode\r
680 // in this driver.\r
681 //\r
682 if (!IsInSmm ()) {\r
053e878b 683 Cr0.UintN = AsmReadCr0 ();\r
29355b4e
RN
684 Cr0.Bits.WP = 1;\r
685 AsmWriteCr0 (Cr0.UintN);\r
2a1408d1 686 }\r
147fd35c
JW
687}\r
688\r
22292ed3
JY
689/**\r
690 This function modifies the page attributes for the memory region specified by BaseAddress and\r
691 Length from their current attributes to the attributes specified by Attributes.\r
692\r
693 Caller should make sure BaseAddress and Length is at page boundary.\r
694\r
695 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.\r
696 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
697 @param[in] Length The size in bytes of the memory region.\r
698 @param[in] Attributes The bit mask of attributes to modify for the memory region.\r
699 @param[in] PageAction The page action.\r
700 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.\r
701 NULL mean page split is unsupported.\r
702 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.\r
703 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.\r
704\r
705 @retval RETURN_SUCCESS The attributes were modified for the memory region.\r
706 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by\r
707 BaseAddress and Length cannot be modified.\r
708 @retval RETURN_INVALID_PARAMETER Length is zero.\r
709 Attributes specified an illegal combination of attributes that\r
710 cannot be set together.\r
711 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
712 the memory resource range.\r
713 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory\r
714 resource range specified by BaseAddress and Length.\r
715 The bit mask of attributes is not support for the memory resource\r
716 range specified by BaseAddress and Length.\r
717**/\r
718RETURN_STATUS\r
719ConvertMemoryPageAttributes (\r
053e878b
MK
720 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext OPTIONAL,\r
721 IN PHYSICAL_ADDRESS BaseAddress,\r
722 IN UINT64 Length,\r
723 IN UINT64 Attributes,\r
724 IN PAGE_ACTION PageAction,\r
725 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL,\r
726 OUT BOOLEAN *IsSplitted OPTIONAL,\r
727 OUT BOOLEAN *IsModified OPTIONAL\r
22292ed3
JY
728 )\r
729{\r
053e878b
MK
730 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext;\r
731 UINT64 *PageEntry;\r
732 PAGE_ATTRIBUTE PageAttribute;\r
733 UINTN PageEntryLength;\r
734 PAGE_ATTRIBUTE SplitAttribute;\r
735 RETURN_STATUS Status;\r
736 BOOLEAN IsEntryModified;\r
737 BOOLEAN IsWpEnabled;\r
22292ed3
JY
738\r
739 if ((BaseAddress & (SIZE_4KB - 1)) != 0) {\r
740 DEBUG ((DEBUG_ERROR, "BaseAddress(0x%lx) is not aligned!\n", BaseAddress));\r
741 return EFI_UNSUPPORTED;\r
742 }\r
053e878b 743\r
22292ed3
JY
744 if ((Length & (SIZE_4KB - 1)) != 0) {\r
745 DEBUG ((DEBUG_ERROR, "Length(0x%lx) is not aligned!\n", Length));\r
746 return EFI_UNSUPPORTED;\r
747 }\r
053e878b 748\r
22292ed3
JY
749 if (Length == 0) {\r
750 DEBUG ((DEBUG_ERROR, "Length is 0!\n"));\r
751 return RETURN_INVALID_PARAMETER;\r
752 }\r
753\r
e77966b3 754 if ((Attributes & ~EFI_MEMORY_ATTRIBUTE_MASK) != 0) {\r
22292ed3
JY
755 DEBUG ((DEBUG_ERROR, "Attributes(0x%lx) has unsupported bit\n", Attributes));\r
756 return EFI_UNSUPPORTED;\r
757 }\r
758\r
759 if (PagingContext == NULL) {\r
760 GetCurrentPagingContext (&CurrentPagingContext);\r
761 } else {\r
053e878b 762 CopyMem (&CurrentPagingContext, PagingContext, sizeof (CurrentPagingContext));\r
22292ed3 763 }\r
053e878b
MK
764\r
765 switch (CurrentPagingContext.MachineType) {\r
766 case IMAGE_FILE_MACHINE_I386:\r
767 if (CurrentPagingContext.ContextData.Ia32.PageTableBase == 0) {\r
768 if (Attributes == 0) {\r
769 return EFI_SUCCESS;\r
770 } else {\r
771 DEBUG ((DEBUG_ERROR, "PageTable is 0!\n"));\r
772 return EFI_UNSUPPORTED;\r
773 }\r
774 }\r
775\r
776 if ((CurrentPagingContext.ContextData.Ia32.Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) == 0) {\r
777 DEBUG ((DEBUG_ERROR, "Non-PAE Paging!\n"));\r
22292ed3
JY
778 return EFI_UNSUPPORTED;\r
779 }\r
053e878b
MK
780\r
781 if ((BaseAddress + Length) > BASE_4GB) {\r
782 DEBUG ((DEBUG_ERROR, "Beyond 4GB memory in 32-bit mode!\n"));\r
783 return EFI_UNSUPPORTED;\r
784 }\r
785\r
786 break;\r
787 case IMAGE_FILE_MACHINE_X64:\r
788 ASSERT (CurrentPagingContext.ContextData.X64.PageTableBase != 0);\r
789 break;\r
790 default:\r
791 ASSERT (FALSE);\r
4f10654e 792 return EFI_UNSUPPORTED;\r
053e878b 793 break;\r
22292ed3
JY
794 }\r
795\r
053e878b 796 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));\r
22292ed3
JY
797\r
798 if (IsSplitted != NULL) {\r
799 *IsSplitted = FALSE;\r
800 }\r
053e878b 801\r
22292ed3
JY
802 if (IsModified != NULL) {\r
803 *IsModified = FALSE;\r
804 }\r
053e878b 805\r
147fd35c
JW
806 if (AllocatePagesFunc == NULL) {\r
807 AllocatePagesFunc = AllocatePageTableMemory;\r
808 }\r
809\r
810 //\r
811 // Make sure that the page table is changeable.\r
812 //\r
813 IsWpEnabled = IsReadOnlyPageWriteProtected ();\r
814 if (IsWpEnabled) {\r
815 DisableReadOnlyPageWriteProtect ();\r
816 }\r
22292ed3
JY
817\r
818 //\r
f60f4cfe 819 // Below logic is to check 2M/4K page to make sure we do not waste memory.\r
22292ed3 820 //\r
147fd35c 821 Status = EFI_SUCCESS;\r
22292ed3
JY
822 while (Length != 0) {\r
823 PageEntry = GetPageTableEntry (&CurrentPagingContext, BaseAddress, &PageAttribute);\r
824 if (PageEntry == NULL) {\r
147fd35c
JW
825 Status = RETURN_UNSUPPORTED;\r
826 goto Done;\r
22292ed3 827 }\r
053e878b 828\r
22292ed3 829 PageEntryLength = PageAttributeToLength (PageAttribute);\r
053e878b 830 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);\r
22292ed3
JY
831 if (SplitAttribute == PageNone) {\r
832 ConvertPageEntryAttribute (&CurrentPagingContext, PageEntry, Attributes, PageAction, &IsEntryModified);\r
833 if (IsEntryModified) {\r
834 if (IsModified != NULL) {\r
835 *IsModified = TRUE;\r
836 }\r
837 }\r
053e878b 838\r
22292ed3
JY
839 //\r
840 // Convert success, move to next\r
841 //\r
842 BaseAddress += PageEntryLength;\r
053e878b 843 Length -= PageEntryLength;\r
22292ed3
JY
844 } else {\r
845 if (AllocatePagesFunc == NULL) {\r
147fd35c
JW
846 Status = RETURN_UNSUPPORTED;\r
847 goto Done;\r
22292ed3 848 }\r
053e878b 849\r
22292ed3
JY
850 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute, AllocatePagesFunc);\r
851 if (RETURN_ERROR (Status)) {\r
147fd35c
JW
852 Status = RETURN_UNSUPPORTED;\r
853 goto Done;\r
22292ed3 854 }\r
053e878b 855\r
22292ed3
JY
856 if (IsSplitted != NULL) {\r
857 *IsSplitted = TRUE;\r
858 }\r
053e878b 859\r
22292ed3
JY
860 if (IsModified != NULL) {\r
861 *IsModified = TRUE;\r
862 }\r
053e878b 863\r
22292ed3
JY
864 //\r
865 // Just split current page\r
866 // Convert success in next around\r
867 //\r
868 }\r
869 }\r
870\r
147fd35c
JW
871Done:\r
872 //\r
873 // Restore page table write protection, if any.\r
874 //\r
875 if (IsWpEnabled) {\r
876 EnableReadOnlyPageWriteProtect ();\r
877 }\r
053e878b 878\r
147fd35c 879 return Status;\r
22292ed3
JY
880}\r
881\r
882/**\r
883 This function assigns the page attributes for the memory region specified by BaseAddress and\r
884 Length from their current attributes to the attributes specified by Attributes.\r
885\r
886 Caller should make sure BaseAddress and Length is at page boundary.\r
887\r
f60f4cfe 888 Caller need guarantee the TPL <= TPL_NOTIFY, if there is split page request.\r
22292ed3
JY
889\r
890 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.\r
891 @param[in] BaseAddress The physical address that is the start address of a memory region.\r
892 @param[in] Length The size in bytes of the memory region.\r
893 @param[in] Attributes The bit mask of attributes to set for the memory region.\r
894 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.\r
895 NULL mean page split is unsupported.\r
896\r
897 @retval RETURN_SUCCESS The attributes were cleared for the memory region.\r
898 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by\r
899 BaseAddress and Length cannot be modified.\r
900 @retval RETURN_INVALID_PARAMETER Length is zero.\r
901 Attributes specified an illegal combination of attributes that\r
902 cannot be set together.\r
903 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of\r
904 the memory resource range.\r
905 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory\r
906 resource range specified by BaseAddress and Length.\r
907 The bit mask of attributes is not support for the memory resource\r
908 range specified by BaseAddress and Length.\r
909**/\r
910RETURN_STATUS\r
911EFIAPI\r
912AssignMemoryPageAttributes (\r
053e878b
MK
913 IN PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext OPTIONAL,\r
914 IN PHYSICAL_ADDRESS BaseAddress,\r
915 IN UINT64 Length,\r
916 IN UINT64 Attributes,\r
917 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL\r
22292ed3
JY
918 )\r
919{\r
920 RETURN_STATUS Status;\r
921 BOOLEAN IsModified;\r
922 BOOLEAN IsSplitted;\r
923\r
053e878b 924 // DEBUG((DEBUG_INFO, "AssignMemoryPageAttributes: 0x%lx - 0x%lx (0x%lx)\n", BaseAddress, Length, Attributes));\r
22292ed3 925 Status = ConvertMemoryPageAttributes (PagingContext, BaseAddress, Length, Attributes, PageActionAssign, AllocatePagesFunc, &IsSplitted, &IsModified);\r
053e878b 926 if (!EFI_ERROR (Status)) {\r
22292ed3
JY
927 if ((PagingContext == NULL) && IsModified) {\r
928 //\r
41a9c3fd
JW
929 // Flush TLB as last step.\r
930 //\r
931 // Note: Since APs will always init CR3 register in HLT loop mode or do\r
932 // TLB flush in MWAIT loop mode, there's no need to flush TLB for them\r
933 // here.\r
22292ed3 934 //\r
053e878b 935 CpuFlushTlb ();\r
22292ed3
JY
936 }\r
937 }\r
938\r
939 return Status;\r
940}\r
941\r
768bd967
JW
942/**\r
943 Check if Execute Disable feature is enabled or not.\r
944**/\r
945BOOLEAN\r
946IsExecuteDisableEnabled (\r
947 VOID\r
948 )\r
949{\r
053e878b 950 MSR_CORE_IA32_EFER_REGISTER MsrEfer;\r
768bd967
JW
951\r
952 MsrEfer.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);\r
953 return (MsrEfer.Bits.NXE == 1);\r
954}\r
955\r
c1cab54c
JW
956/**\r
957 Update GCD memory space attributes according to current page table setup.\r
958**/\r
959VOID\r
960RefreshGcdMemoryAttributesFromPaging (\r
961 VOID\r
962 )\r
963{\r
053e878b
MK
964 EFI_STATUS Status;\r
965 UINTN NumberOfDescriptors;\r
966 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;\r
967 PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext;\r
968 PAGE_ATTRIBUTE PageAttribute;\r
969 UINT64 *PageEntry;\r
970 UINT64 PageLength;\r
971 UINT64 MemorySpaceLength;\r
972 UINT64 Length;\r
973 UINT64 BaseAddress;\r
974 UINT64 PageStartAddress;\r
975 UINT64 Attributes;\r
976 UINT64 Capabilities;\r
977 UINT64 NewAttributes;\r
978 UINTN Index;\r
c1cab54c
JW
979\r
980 //\r
981 // Assuming that memory space map returned is sorted already; otherwise sort\r
982 // them in the order of lowest address to highest address.\r
983 //\r
984 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
985 ASSERT_EFI_ERROR (Status);\r
986\r
987 GetCurrentPagingContext (&PagingContext);\r
988\r
053e878b
MK
989 Attributes = 0;\r
990 NewAttributes = 0;\r
991 BaseAddress = 0;\r
992 PageLength = 0;\r
768bd967
JW
993\r
994 if (IsExecuteDisableEnabled ()) {\r
995 Capabilities = EFI_MEMORY_RO | EFI_MEMORY_RP | EFI_MEMORY_XP;\r
996 } else {\r
997 Capabilities = EFI_MEMORY_RO | EFI_MEMORY_RP;\r
998 }\r
96207191 999\r
c1cab54c
JW
1000 for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
1001 if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {\r
1002 continue;\r
1003 }\r
1004\r
768bd967
JW
1005 //\r
1006 // Sync the actual paging related capabilities back to GCD service first.\r
1007 // As a side effect (good one), this can also help to avoid unnecessary\r
1008 // memory map entries due to the different capabilities of the same type\r
1009 // memory, such as multiple RT_CODE and RT_DATA entries in memory map,\r
1010 // which could cause boot failure of some old Linux distro (before v4.3).\r
1011 //\r
1012 Status = gDS->SetMemorySpaceCapabilities (\r
1013 MemorySpaceMap[Index].BaseAddress,\r
1014 MemorySpaceMap[Index].Length,\r
1015 MemorySpaceMap[Index].Capabilities | Capabilities\r
1016 );\r
1017 if (EFI_ERROR (Status)) {\r
1018 //\r
f60f4cfe 1019 // If we cannot update the capabilities, we cannot update its\r
768bd967
JW
1020 // attributes either. So just simply skip current block of memory.\r
1021 //\r
1022 DEBUG ((\r
1023 DEBUG_WARN,\r
1024 "Failed to update capability: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n",\r
053e878b
MK
1025 (UINT64)Index,\r
1026 MemorySpaceMap[Index].BaseAddress,\r
768bd967
JW
1027 MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - 1,\r
1028 MemorySpaceMap[Index].Capabilities,\r
1029 MemorySpaceMap[Index].Capabilities | Capabilities\r
1030 ));\r
1031 continue;\r
1032 }\r
1033\r
c1cab54c
JW
1034 if (MemorySpaceMap[Index].BaseAddress >= (BaseAddress + PageLength)) {\r
1035 //\r
1036 // Current memory space starts at a new page. Resetting PageLength will\r
1037 // trigger a retrieval of page attributes at new address.\r
1038 //\r
1039 PageLength = 0;\r
1040 } else {\r
1041 //\r
1042 // In case current memory space is not adjacent to last one\r
1043 //\r
1044 PageLength -= (MemorySpaceMap[Index].BaseAddress - BaseAddress);\r
1045 }\r
1046\r
768bd967
JW
1047 //\r
1048 // Sync actual page attributes to GCD\r
1049 //\r
c1cab54c
JW
1050 BaseAddress = MemorySpaceMap[Index].BaseAddress;\r
1051 MemorySpaceLength = MemorySpaceMap[Index].Length;\r
1052 while (MemorySpaceLength > 0) {\r
1053 if (PageLength == 0) {\r
1054 PageEntry = GetPageTableEntry (&PagingContext, BaseAddress, &PageAttribute);\r
1055 if (PageEntry == NULL) {\r
1056 break;\r
1057 }\r
1058\r
1059 //\r
1060 // Note current memory space might start in the middle of a page\r
1061 //\r
053e878b
MK
1062 PageStartAddress = (*PageEntry) & (UINT64)PageAttributeToMask (PageAttribute);\r
1063 PageLength = PageAttributeToLength (PageAttribute) - (BaseAddress - PageStartAddress);\r
1064 Attributes = GetAttributesFromPageEntry (PageEntry);\r
c1cab54c
JW
1065 }\r
1066\r
1067 Length = MIN (PageLength, MemorySpaceLength);\r
768bd967 1068 if (Attributes != (MemorySpaceMap[Index].Attributes &\r
053e878b
MK
1069 EFI_MEMORY_ATTRIBUTE_MASK))\r
1070 {\r
768bd967 1071 NewAttributes = (MemorySpaceMap[Index].Attributes &\r
e77966b3 1072 ~EFI_MEMORY_ATTRIBUTE_MASK) | Attributes;\r
768bd967
JW
1073 Status = gDS->SetMemorySpaceAttributes (\r
1074 BaseAddress,\r
1075 Length,\r
1076 NewAttributes\r
1077 );\r
1078 ASSERT_EFI_ERROR (Status);\r
1079 DEBUG ((\r
fbe2c4b9 1080 DEBUG_VERBOSE,\r
768bd967 1081 "Updated memory space attribute: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n",\r
053e878b
MK
1082 (UINT64)Index,\r
1083 BaseAddress,\r
1084 BaseAddress + Length - 1,\r
768bd967
JW
1085 MemorySpaceMap[Index].Attributes,\r
1086 NewAttributes\r
1087 ));\r
c1cab54c
JW
1088 }\r
1089\r
1090 PageLength -= Length;\r
1091 MemorySpaceLength -= Length;\r
1092 BaseAddress += Length;\r
1093 }\r
1094 }\r
1095\r
1096 FreePool (MemorySpaceMap);\r
1097}\r
1098\r
147fd35c
JW
1099/**\r
1100 Initialize a buffer pool for page table use only.\r
1101\r
1102 To reduce the potential split operation on page table, the pages reserved for\r
1103 page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and\r
1104 at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always\r
1105 initialized with number of pages greater than or equal to the given PoolPages.\r
1106\r
1107 Once the pages in the pool are used up, this method should be called again to\r
1108 reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. Usually this won't happen\r
1109 often in practice.\r
1110\r
1111 @param[in] PoolPages The least page number of the pool to be created.\r
1112\r
1113 @retval TRUE The pool is initialized successfully.\r
1114 @retval FALSE The memory is out of resource.\r
1115**/\r
1116BOOLEAN\r
1117InitializePageTablePool (\r
053e878b 1118 IN UINTN PoolPages\r
147fd35c
JW
1119 )\r
1120{\r
053e878b
MK
1121 VOID *Buffer;\r
1122 BOOLEAN IsModified;\r
147fd35c 1123\r
54efcfea
JW
1124 //\r
1125 // Do not allow re-entrance.\r
1126 //\r
1127 if (mPageTablePoolLock) {\r
1128 return FALSE;\r
1129 }\r
1130\r
1131 mPageTablePoolLock = TRUE;\r
053e878b 1132 IsModified = FALSE;\r
54efcfea 1133\r
147fd35c
JW
1134 //\r
1135 // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for\r
1136 // header.\r
1137 //\r
1138 PoolPages += 1; // Add one page for header.\r
053e878b
MK
1139 PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) *\r
1140 PAGE_TABLE_POOL_UNIT_PAGES;\r
147fd35c
JW
1141 Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);\r
1142 if (Buffer == NULL) {\r
1143 DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));\r
54efcfea 1144 goto Done;\r
147fd35c
JW
1145 }\r
1146\r
54efcfea
JW
1147 DEBUG ((\r
1148 DEBUG_INFO,\r
1149 "Paging: added %lu pages to page table pool\r\n",\r
1150 (UINT64)PoolPages\r
1151 ));\r
1152\r
147fd35c
JW
1153 //\r
1154 // Link all pools into a list for easier track later.\r
1155 //\r
1156 if (mPageTablePool == NULL) {\r
053e878b 1157 mPageTablePool = Buffer;\r
147fd35c
JW
1158 mPageTablePool->NextPool = mPageTablePool;\r
1159 } else {\r
1160 ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool;\r
053e878b
MK
1161 mPageTablePool->NextPool = Buffer;\r
1162 mPageTablePool = Buffer;\r
147fd35c
JW
1163 }\r
1164\r
1165 //\r
1166 // Reserve one page for pool header.\r
1167 //\r
053e878b
MK
1168 mPageTablePool->FreePages = PoolPages - 1;\r
1169 mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1);\r
147fd35c
JW
1170\r
1171 //\r
1172 // Mark the whole pool pages as read-only.\r
1173 //\r
1174 ConvertMemoryPageAttributes (\r
1175 NULL,\r
1176 (PHYSICAL_ADDRESS)(UINTN)Buffer,\r
1177 EFI_PAGES_TO_SIZE (PoolPages),\r
1178 EFI_MEMORY_RO,\r
1179 PageActionSet,\r
1180 AllocatePageTableMemory,\r
1181 NULL,\r
1182 &IsModified\r
1183 );\r
1184 ASSERT (IsModified == TRUE);\r
1185\r
54efcfea
JW
1186Done:\r
1187 mPageTablePoolLock = FALSE;\r
1188 return IsModified;\r
147fd35c
JW
1189}\r
1190\r
1191/**\r
1192 This API provides a way to allocate memory for page table.\r
1193\r
1194 This API can be called more than once to allocate memory for page tables.\r
1195\r
1196 Allocates the number of 4KB pages and returns a pointer to the allocated\r
1197 buffer. The buffer returned is aligned on a 4KB boundary.\r
1198\r
1199 If Pages is 0, then NULL is returned.\r
1200 If there is not enough memory remaining to satisfy the request, then NULL is\r
1201 returned.\r
1202\r
1203 @param Pages The number of 4 KB pages to allocate.\r
1204\r
1205 @return A pointer to the allocated buffer or NULL if allocation fails.\r
1206\r
1207**/\r
1208VOID *\r
1209EFIAPI\r
1210AllocatePageTableMemory (\r
053e878b 1211 IN UINTN Pages\r
147fd35c
JW
1212 )\r
1213{\r
053e878b 1214 VOID *Buffer;\r
147fd35c
JW
1215\r
1216 if (Pages == 0) {\r
1217 return NULL;\r
1218 }\r
1219\r
1220 //\r
1221 // Renew the pool if necessary.\r
1222 //\r
053e878b
MK
1223 if ((mPageTablePool == NULL) ||\r
1224 (Pages > mPageTablePool->FreePages))\r
1225 {\r
147fd35c
JW
1226 if (!InitializePageTablePool (Pages)) {\r
1227 return NULL;\r
1228 }\r
1229 }\r
1230\r
1231 Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;\r
1232\r
053e878b
MK
1233 mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages);\r
1234 mPageTablePool->FreePages -= Pages;\r
147fd35c
JW
1235\r
1236 return Buffer;\r
1237}\r
1238\r
dcc02621
JW
1239/**\r
1240 Special handler for #DB exception, which will restore the page attributes\r
1241 (not-present). It should work with #PF handler which will set pages to\r
1242 'present'.\r
1243\r
1244 @param ExceptionType Exception type.\r
1245 @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.\r
1246\r
1247**/\r
1248VOID\r
1249EFIAPI\r
1250DebugExceptionHandler (\r
053e878b
MK
1251 IN EFI_EXCEPTION_TYPE ExceptionType,\r
1252 IN EFI_SYSTEM_CONTEXT SystemContext\r
dcc02621
JW
1253 )\r
1254{\r
053e878b
MK
1255 UINTN CpuIndex;\r
1256 UINTN PFEntry;\r
1257 BOOLEAN IsWpEnabled;\r
dcc02621
JW
1258\r
1259 MpInitLibWhoAmI (&CpuIndex);\r
1260\r
1261 //\r
1262 // Clear last PF entries\r
1263 //\r
1264 IsWpEnabled = IsReadOnlyPageWriteProtected ();\r
1265 if (IsWpEnabled) {\r
1266 DisableReadOnlyPageWriteProtect ();\r
1267 }\r
1268\r
1269 for (PFEntry = 0; PFEntry < mPFEntryCount[CpuIndex]; PFEntry++) {\r
1270 if (mLastPFEntryPointer[CpuIndex][PFEntry] != NULL) {\r
8e2018f9 1271 *mLastPFEntryPointer[CpuIndex][PFEntry] &= ~(UINT64)IA32_PG_P;\r
dcc02621
JW
1272 }\r
1273 }\r
1274\r
1275 if (IsWpEnabled) {\r
1276 EnableReadOnlyPageWriteProtect ();\r
1277 }\r
1278\r
1279 //\r
1280 // Reset page fault exception count for next page fault.\r
1281 //\r
1282 mPFEntryCount[CpuIndex] = 0;\r
1283\r
1284 //\r
1285 // Flush TLB\r
1286 //\r
1287 CpuFlushTlb ();\r
1288\r
1289 //\r
1290 // Clear TF in EFLAGS\r
1291 //\r
1292 if (mPagingContext.MachineType == IMAGE_FILE_MACHINE_I386) {\r
053e878b 1293 SystemContext.SystemContextIa32->Eflags &= (UINT32) ~BIT8;\r
dcc02621 1294 } else {\r
053e878b 1295 SystemContext.SystemContextX64->Rflags &= (UINT64) ~BIT8;\r
dcc02621
JW
1296 }\r
1297}\r
1298\r
1299/**\r
1300 Special handler for #PF exception, which will set the pages which caused\r
1301 #PF to be 'present'. The attribute of those pages should be restored in\r
1302 the subsequent #DB handler.\r
1303\r
1304 @param ExceptionType Exception type.\r
1305 @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.\r
1306\r
1307**/\r
1308VOID\r
1309EFIAPI\r
1310PageFaultExceptionHandler (\r
053e878b
MK
1311 IN EFI_EXCEPTION_TYPE ExceptionType,\r
1312 IN EFI_SYSTEM_CONTEXT SystemContext\r
dcc02621
JW
1313 )\r
1314{\r
053e878b
MK
1315 EFI_STATUS Status;\r
1316 UINT64 PFAddress;\r
1317 PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext;\r
1318 PAGE_ATTRIBUTE PageAttribute;\r
1319 UINT64 Attributes;\r
1320 UINT64 *PageEntry;\r
1321 UINTN Index;\r
1322 UINTN CpuIndex;\r
1323 UINTN PageNumber;\r
1324 BOOLEAN NonStopMode;\r
dcc02621
JW
1325\r
1326 PFAddress = AsmReadCr2 () & ~EFI_PAGE_MASK;\r
1327 if (PFAddress < BASE_4KB) {\r
1328 NonStopMode = NULL_DETECTION_NONSTOP_MODE ? TRUE : FALSE;\r
1329 } else {\r
1330 NonStopMode = HEAP_GUARD_NONSTOP_MODE ? TRUE : FALSE;\r
1331 }\r
1332\r
1333 if (NonStopMode) {\r
1334 MpInitLibWhoAmI (&CpuIndex);\r
1335 GetCurrentPagingContext (&PagingContext);\r
1336 //\r
1337 // Memory operation cross page boundary, like "rep mov" instruction, will\r
1338 // cause infinite loop between this and Debug Trap handler. We have to make\r
1339 // sure that current page and the page followed are both in PRESENT state.\r
1340 //\r
1341 PageNumber = 2;\r
1342 while (PageNumber > 0) {\r
1343 PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute);\r
053e878b 1344 ASSERT (PageEntry != NULL);\r
dcc02621
JW
1345\r
1346 if (PageEntry != NULL) {\r
1347 Attributes = GetAttributesFromPageEntry (PageEntry);\r
1348 if ((Attributes & EFI_MEMORY_RP) != 0) {\r
1349 Attributes &= ~EFI_MEMORY_RP;\r
053e878b
MK
1350 Status = AssignMemoryPageAttributes (\r
1351 &PagingContext,\r
1352 PFAddress,\r
1353 EFI_PAGE_SIZE,\r
1354 Attributes,\r
1355 NULL\r
1356 );\r
1357 if (!EFI_ERROR (Status)) {\r
dcc02621
JW
1358 Index = mPFEntryCount[CpuIndex];\r
1359 //\r
1360 // Re-retrieve page entry because above calling might update page\r
1361 // table due to table split.\r
1362 //\r
053e878b 1363 PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute);\r
dcc02621 1364 mLastPFEntryPointer[CpuIndex][Index++] = PageEntry;\r
053e878b 1365 mPFEntryCount[CpuIndex] = Index;\r
dcc02621
JW
1366 }\r
1367 }\r
1368 }\r
1369\r
1370 PFAddress += EFI_PAGE_SIZE;\r
1371 --PageNumber;\r
1372 }\r
1373 }\r
1374\r
1375 //\r
1376 // Initialize the serial port before dumping.\r
1377 //\r
1378 SerialPortInitialize ();\r
1379 //\r
1380 // Display ExceptionType, CPU information and Image information\r
1381 //\r
1382 DumpCpuContext (ExceptionType, SystemContext);\r
2a93cccc
JW
1383 if (NonStopMode) {\r
1384 //\r
1385 // Set TF in EFLAGS\r
1386 //\r
1387 if (mPagingContext.MachineType == IMAGE_FILE_MACHINE_I386) {\r
1388 SystemContext.SystemContextIa32->Eflags |= (UINT32)BIT8;\r
1389 } else {\r
1390 SystemContext.SystemContextX64->Rflags |= (UINT64)BIT8;\r
1391 }\r
1392 } else {\r
dcc02621
JW
1393 CpuDeadLoop ();\r
1394 }\r
1395}\r
1396\r
22292ed3
JY
1397/**\r
1398 Initialize the Page Table lib.\r
1399**/\r
1400VOID\r
1401InitializePageTableLib (\r
1402 VOID\r
1403 )\r
1404{\r
053e878b
MK
1405 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext;\r
1406 UINT32 *Attributes;\r
1407 UINTN *PageTableBase;\r
22292ed3
JY
1408\r
1409 GetCurrentPagingContext (&CurrentPagingContext);\r
147fd35c 1410\r
c70fef96
ED
1411 GetPagingDetails (&CurrentPagingContext.ContextData, &PageTableBase, &Attributes);\r
1412\r
147fd35c
JW
1413 //\r
1414 // Reserve memory of page tables for future uses, if paging is enabled.\r
1415 //\r
c70fef96 1416 if ((*PageTableBase != 0) &&\r
053e878b
MK
1417 ((*Attributes & PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE) != 0))\r
1418 {\r
147fd35c
JW
1419 DisableReadOnlyPageWriteProtect ();\r
1420 InitializePageTablePool (1);\r
1421 EnableReadOnlyPageWriteProtect ();\r
1422 }\r
1423\r
dcc02621
JW
1424 if (HEAP_GUARD_NONSTOP_MODE || NULL_DETECTION_NONSTOP_MODE) {\r
1425 mPFEntryCount = (UINTN *)AllocateZeroPool (sizeof (UINTN) * mNumberOfProcessors);\r
1426 ASSERT (mPFEntryCount != NULL);\r
1427\r
1428 mLastPFEntryPointer = (UINT64 *(*)[MAX_PF_ENTRY_COUNT])\r
1429 AllocateZeroPool (sizeof (mLastPFEntryPointer[0]) * mNumberOfProcessors);\r
1430 ASSERT (mLastPFEntryPointer != NULL);\r
1431 }\r
1432\r
c70fef96 1433 DEBUG ((DEBUG_INFO, "CurrentPagingContext:\n"));\r
22292ed3 1434 DEBUG ((DEBUG_INFO, " MachineType - 0x%x\n", CurrentPagingContext.MachineType));\r
c70fef96
ED
1435 DEBUG ((DEBUG_INFO, " PageTableBase - 0x%Lx\n", (UINT64)*PageTableBase));\r
1436 DEBUG ((DEBUG_INFO, " Attributes - 0x%x\n", *Attributes));\r
22292ed3 1437\r
053e878b 1438 return;\r
22292ed3 1439}\r