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