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