TianoCore EDK2 master
Loading...
Searching...
No Matches
CpuPaging.c
Go to the documentation of this file.
1
11#include <Register/Intel/Msr.h>
13#include <Library/CpuLib.h>
14#include <Library/BaseLib.h>
15#include <Guid/MigratedFvInfo.h>
16
17#include "CpuMpPei.h"
18
19#define IA32_PG_P BIT0
20#define IA32_PG_RW BIT1
21#define IA32_PG_U BIT2
22#define IA32_PG_A BIT5
23#define IA32_PG_D BIT6
24#define IA32_PG_PS BIT7
25#define IA32_PG_NX BIT63
26
27#define PAGE_ATTRIBUTE_BITS (IA32_PG_RW | IA32_PG_P)
28#define PAGE_PROGATE_BITS (IA32_PG_D | IA32_PG_A | IA32_PG_NX | IA32_PG_U | \
29 PAGE_ATTRIBUTE_BITS)
30
31#define PAGING_PAE_INDEX_MASK 0x1FF
32#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull
33#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull
34#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
35#define PAGING_512G_ADDRESS_MASK_64 0x000FFF8000000000ull
36
37typedef enum {
38 PageNone = 0,
39 PageMin = 1,
40 Page4K = PageMin,
41 Page2M = 2,
42 Page1G = 3,
43 Page512G = 4,
44 PageMax = Page512G
45} PAGE_ATTRIBUTE;
46
47typedef struct {
48 PAGE_ATTRIBUTE Attribute;
49 UINT64 Length;
50 UINT64 AddressMask;
51 UINTN AddressBitOffset;
52 UINTN AddressBitLength;
54
55PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
56 { PageNone, 0, 0, 0, 0 },
57 { Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64, 12, 9 },
58 { Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64, 21, 9 },
59 { Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64, 30, 9 },
60 { Page512G, SIZE_512GB, PAGING_512G_ADDRESS_MASK_64, 39, 9 },
61};
62
63EFI_PEI_NOTIFY_DESCRIPTOR mPostMemNotifyList[] = {
64 {
65 (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
66 &gEfiPeiMemoryDiscoveredPpiGuid,
68 }
69};
70
78BOOLEAN
80 VOID
81 )
82{
83 UINT32 RegEax;
85
87 if (RegEax >= CPUID_VERSION_INFO) {
89 if (RegEdx.Bits.PAE != 0) {
90 return TRUE;
91 }
92 }
93
94 return FALSE;
95}
96
105VOID *
107 IN UINTN Pages
108 )
109{
110 VOID *Address;
111
112 Address = AllocatePages (Pages);
113 if (Address != NULL) {
114 ZeroMem (Address, EFI_PAGES_TO_SIZE (Pages));
115 }
116
117 return Address;
118}
119
127PAGE_ATTRIBUTE
129 VOID
130 )
131{
133
135
136 return (MsrEfer.Bits.LMA == 1) ? Page512G : Page1G;
137}
138
147VOID *
149 IN PHYSICAL_ADDRESS Address,
150 OUT PAGE_ATTRIBUTE *PageAttribute
151 )
152{
153 INTN Level;
154 UINTN Index;
155 UINT64 *PageTable;
156 UINT64 AddressEncMask;
157
158 AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask);
159 PageTable = (UINT64 *)(UINTN)(AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);
160 for (Level = (INTN)GetPageTableTopLevelType (); Level > 0; --Level) {
161 Index = (UINTN)RShiftU64 (Address, mPageAttributeTable[Level].AddressBitOffset);
162 Index &= PAGING_PAE_INDEX_MASK;
163
164 //
165 // No mapping?
166 //
167 if (PageTable[Index] == 0) {
168 *PageAttribute = PageNone;
169 return NULL;
170 }
171
172 //
173 // Page memory?
174 //
175 if (((PageTable[Index] & IA32_PG_PS) != 0) || (Level == PageMin)) {
176 *PageAttribute = (PAGE_ATTRIBUTE)Level;
177 return &PageTable[Index];
178 }
179
180 //
181 // Page directory or table
182 //
183 PageTable = (UINT64 *)(UINTN)(PageTable[Index] &
184 ~AddressEncMask &
185 PAGING_4K_ADDRESS_MASK_64);
186 }
187
188 *PageAttribute = PageNone;
189 return NULL;
190}
191
204RETURN_STATUS
206 IN UINT64 *PageEntry,
207 IN PAGE_ATTRIBUTE PageAttribute,
208 IN PAGE_ATTRIBUTE SplitAttribute,
209 IN BOOLEAN Recursively
210 )
211{
212 UINT64 BaseAddress;
213 UINT64 *NewPageEntry;
214 UINTN Index;
215 UINT64 AddressEncMask;
216 PAGE_ATTRIBUTE SplitTo;
217
218 if ((SplitAttribute == PageNone) || (SplitAttribute >= PageAttribute)) {
219 ASSERT (SplitAttribute != PageNone);
220 ASSERT (SplitAttribute < PageAttribute);
222 }
223
224 NewPageEntry = AllocatePageTableMemory (1);
225 if (NewPageEntry == NULL) {
226 ASSERT (NewPageEntry != NULL);
228 }
229
230 //
231 // One level down each step to achieve more compact page table.
232 //
233 SplitTo = PageAttribute - 1;
234 AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) &
235 mPageAttributeTable[SplitTo].AddressMask;
236 BaseAddress = *PageEntry &
237 ~PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) &
238 mPageAttributeTable[PageAttribute].AddressMask;
239 for (Index = 0; Index < SIZE_4KB / sizeof (UINT64); Index++) {
240 NewPageEntry[Index] = BaseAddress | AddressEncMask |
241 ((*PageEntry) & PAGE_PROGATE_BITS);
242
243 if (SplitTo != PageMin) {
244 NewPageEntry[Index] |= IA32_PG_PS;
245 }
246
247 if (Recursively && (SplitTo > SplitAttribute)) {
248 SplitPage (&NewPageEntry[Index], SplitTo, SplitAttribute, Recursively);
249 }
250
251 BaseAddress += mPageAttributeTable[SplitTo].Length;
252 }
253
254 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | PAGE_ATTRIBUTE_BITS;
255
256 return RETURN_SUCCESS;
257}
258
281RETURN_STATUS
282EFIAPI
284 IN PHYSICAL_ADDRESS BaseAddress,
285 IN UINT64 Length,
286 IN UINT64 Attributes
287 )
288{
289 UINT64 *PageEntry;
290 PAGE_ATTRIBUTE PageAttribute;
291 RETURN_STATUS Status;
292 EFI_PHYSICAL_ADDRESS MaximumAddress;
293
294 if ((Length == 0) ||
295 ((BaseAddress & (SIZE_4KB - 1)) != 0) ||
296 ((Length & (SIZE_4KB - 1)) != 0))
297 {
298 ASSERT (Length > 0);
299 ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);
300 ASSERT ((Length & (SIZE_4KB - 1)) == 0);
301
303 }
304
305 MaximumAddress = (EFI_PHYSICAL_ADDRESS)MAX_UINT32;
306 if ((BaseAddress > MaximumAddress) ||
307 (Length > MaximumAddress) ||
308 (BaseAddress > MaximumAddress - (Length - 1)))
309 {
310 return RETURN_UNSUPPORTED;
311 }
312
313 //
314 // Below logic is to check 2M/4K page to make sure we do not waste memory.
315 //
316 while (Length != 0) {
317 PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute);
318 if (PageEntry == NULL) {
319 return RETURN_UNSUPPORTED;
320 }
321
322 if (PageAttribute != Page4K) {
323 Status = SplitPage (PageEntry, PageAttribute, Page4K, FALSE);
324 if (RETURN_ERROR (Status)) {
325 return Status;
326 }
327
328 //
329 // Do it again until the page is 4K.
330 //
331 continue;
332 }
333
334 //
335 // Just take care of 'present' bit for Stack Guard.
336 //
337 if ((Attributes & IA32_PG_P) != 0) {
338 *PageEntry |= (UINT64)IA32_PG_P;
339 } else {
340 *PageEntry &= ~((UINT64)IA32_PG_P);
341 }
342
343 //
344 // Convert success, move to next
345 //
346 BaseAddress += SIZE_4KB;
347 Length -= SIZE_4KB;
348 }
349
350 return RETURN_SUCCESS;
351}
352
362 VOID
363 )
364{
365 EFI_STATUS Status;
366
367 UINTN PageTable;
368 VOID *Buffer;
369 UINTN BufferSize;
370 IA32_MAP_ATTRIBUTE MapAttribute;
371 IA32_MAP_ATTRIBUTE MapMask;
372
373 PageTable = 0;
374 Buffer = NULL;
375 BufferSize = 0;
376 MapAttribute.Uint64 = 0;
377 MapMask.Uint64 = MAX_UINT64;
378 MapAttribute.Bits.Present = 1;
379 MapAttribute.Bits.ReadWrite = 1;
380
381 //
382 // 1:1 map 4GB in 32bit mode
383 //
384 Status = PageTableMap (&PageTable, PagingPae, 0, &BufferSize, 0, SIZE_4GB, &MapAttribute, &MapMask, NULL);
385 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
386 if (Status != EFI_BUFFER_TOO_SMALL) {
387 return Status;
388 }
389
390 //
391 // Allocate required Buffer.
392 //
393 Buffer = AllocatePageTableMemory (EFI_SIZE_TO_PAGES (BufferSize));
394 ASSERT (Buffer != NULL);
395 if (Buffer == NULL) {
396 return EFI_OUT_OF_RESOURCES;
397 }
398
399 Status = PageTableMap (&PageTable, PagingPae, Buffer, &BufferSize, 0, SIZE_4GB, &MapAttribute, &MapMask, NULL);
400 ASSERT_EFI_ERROR (Status);
401 if (EFI_ERROR (Status) || (PageTable == 0)) {
402 return EFI_OUT_OF_RESOURCES;
403 }
404
405 //
406 // Write the Pagetable to CR3.
407 //
408 AsmWriteCr3 (PageTable);
409
410 //
411 // Enable CR4.PAE
412 //
413 AsmWriteCr4 (AsmReadCr4 () | BIT5);
414
415 //
416 // Enable CR0.PG
417 //
418 AsmWriteCr0 (AsmReadCr0 () | BIT31);
419
420 DEBUG ((
421 DEBUG_INFO,
422 "EnablePaePageTable: Created PageTable = 0x%x, BufferSize = %x\n",
423 PageTable,
424 BufferSize
425 ));
426
427 return Status;
428}
429
442VOID
443EFIAPI
445 IN OUT VOID *Buffer
446 )
447{
448 EFI_PHYSICAL_ADDRESS StackBase;
449
450 StackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)&StackBase;
451 StackBase += BASE_4KB;
452 StackBase &= ~((EFI_PHYSICAL_ADDRESS)BASE_4KB - 1);
453 StackBase -= PcdGet32 (PcdCpuApStackSize);
454
455 *(EFI_PHYSICAL_ADDRESS *)Buffer = StackBase;
456}
457
463VOID
465 VOID
466 )
467{
469 EFI_PHYSICAL_ADDRESS StackBase;
470 UINTN NumberOfProcessors;
471 UINTN Bsp;
472 UINTN Index;
473 EFI_STATUS Status;
474
475 //
476 // One extra page at the bottom of the stack is needed for Guard page.
477 //
478 if (PcdGet32 (PcdCpuApStackSize) <= EFI_PAGE_SIZE) {
479 DEBUG ((DEBUG_ERROR, "PcdCpuApStackSize is not big enough for Stack Guard!\n"));
480 ASSERT (FALSE);
481 }
482
483 Status = MpInitLibGetNumberOfProcessors (&NumberOfProcessors, NULL);
484 ASSERT_EFI_ERROR (Status);
485
486 if (EFI_ERROR (Status)) {
487 NumberOfProcessors = 1;
488 }
489
490 MpInitLibWhoAmI (&Bsp);
491 for (Index = 0; Index < NumberOfProcessors; ++Index) {
492 StackBase = 0;
493
494 if (Index == Bsp) {
495 Hob.Raw = GetHobList ();
496 while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) {
497 if (CompareGuid (
498 &gEfiHobMemoryAllocStackGuid,
499 &(Hob.MemoryAllocationStack->AllocDescriptor.Name)
500 ))
501 {
502 StackBase = Hob.MemoryAllocationStack->AllocDescriptor.MemoryBaseAddress;
503 break;
504 }
505
506 Hob.Raw = GET_NEXT_HOB (Hob);
507 }
508 } else {
509 //
510 // Ask AP to return is stack base address.
511 //
512 MpInitLibStartupThisAP (GetStackBase, Index, NULL, 0, (VOID *)&StackBase, NULL);
513 }
514
515 ASSERT (StackBase != 0);
516 //
517 // Set Guard page at stack base address.
518 //
519 ConvertMemoryPageAttributes (StackBase, EFI_PAGE_SIZE, 0);
520 DEBUG ((
521 DEBUG_INFO,
522 "Stack Guard set at %lx [cpu%lu]!\n",
523 (UINT64)StackBase,
524 (UINT64)Index
525 ));
526 }
527
528 //
529 // Publish the changes of page table.
530 //
531 CpuFlushTlb ();
532}
533
548EFIAPI
550 IN EFI_PEI_SERVICES **PeiServices,
551 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
552 IN VOID *Ppi
553 )
554{
555 EFI_STATUS Status;
556 BOOLEAN InitStackGuard;
557 EDKII_MIGRATED_FV_INFO *MigratedFvInfo;
559 IA32_CR0 Cr0;
560
561 //
562 // Paging must be setup first. Otherwise the exception TSS setup during MP
563 // initialization later will not contain paging information and then fail
564 // the task switch (for the sake of stack switch).
565 //
566 InitStackGuard = FALSE;
567 Hob.Raw = NULL;
568 if (IsIa32PaeSupported ()) {
569 Hob.Raw = GetFirstGuidHob (&gEdkiiMigratedFvInfoGuid);
570 InitStackGuard = PcdGetBool (PcdCpuStackGuard);
571 }
572
573 //
574 // Some security features depend on the page table enabling. So, here
575 // is to enable paging if it is not enabled (only in 32bit mode).
576 //
577 Cr0.UintN = AsmReadCr0 ();
578 if ((Cr0.Bits.PG == 0) && (InitStackGuard || (Hob.Raw != NULL))) {
579 ASSERT (sizeof (UINTN) == sizeof (UINT32));
580
581 Status = EnablePaePageTable ();
582 if (EFI_ERROR (Status)) {
583 DEBUG ((DEBUG_ERROR, "MemoryDiscoveredPpiNotifyCallback: Failed to enable PAE page table: %r.\n", Status));
584 CpuDeadLoop ();
585 }
586 }
587
588 Status = InitializeCpuMpWorker ((CONST EFI_PEI_SERVICES **)PeiServices);
589 ASSERT_EFI_ERROR (Status);
590
591 if (InitStackGuard) {
593 }
594
595 while (Hob.Raw != NULL) {
596 MigratedFvInfo = GET_GUID_HOB_DATA (Hob);
597
598 //
599 // Enable #PF exception, so if the code access SPI after disable NEM, it will generate
600 // the exception to avoid potential vulnerability.
601 //
602 ConvertMemoryPageAttributes (MigratedFvInfo->FvOrgBase, MigratedFvInfo->FvLength, 0);
603
604 Hob.Raw = GET_NEXT_HOB (Hob);
605 Hob.Raw = GetNextGuidHob (&gEdkiiMigratedFvInfoGuid, Hob.Raw);
606 }
607
608 CpuFlushTlb ();
609
610 return Status;
611}
UINT64 UINTN
INT64 INTN
VOID *EFIAPI GetFirstGuidHob(IN CONST EFI_GUID *Guid)
Definition: HobLib.c:215
VOID *EFIAPI GetNextHob(IN UINT16 Type, IN CONST VOID *HobStart)
Definition: HobLib.c:103
VOID *EFIAPI GetNextGuidHob(IN CONST EFI_GUID *Guid, IN CONST VOID *HobStart)
Definition: HobLib.c:176
VOID *EFIAPI GetHobList(VOID)
Definition: HobLib.c:76
#define NULL
Definition: Base.h:319
#define CONST
Definition: Base.h:259
#define RETURN_ERROR(StatusCode)
Definition: Base.h:1061
#define RETURN_UNSUPPORTED
Definition: Base.h:1081
#define RETURN_OUT_OF_RESOURCES
Definition: Base.h:1114
#define RETURN_SUCCESS
Definition: Base.h:1066
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define RETURN_INVALID_PARAMETER
Definition: Base.h:1076
VOID EFIAPI CpuDeadLoop(VOID)
Definition: CpuDeadLoop.c:23
UINT64 EFIAPI RShiftU64(IN UINT64 Operand, IN UINTN Count)
Definition: RShiftU64.c:28
BOOLEAN EFIAPI CompareGuid(IN CONST GUID *Guid1, IN CONST GUID *Guid2)
Definition: MemLibGuid.c:73
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
#define MSR_CORE_IA32_EFER
Definition: CoreMsr.h:996
VOID EFIAPI CpuFlushTlb(VOID)
EFI_STATUS InitializeCpuMpWorker(IN CONST EFI_PEI_SERVICES **PeiServices)
Definition: CpuMpPei.c:554
RETURN_STATUS EFIAPI PageTableMap(IN OUT UINTN *PageTable OPTIONAL, IN PAGING_MODE PagingMode, IN VOID *Buffer, IN OUT UINTN *BufferSize, IN UINT64 LinearAddress, IN UINT64 Length, IN IA32_MAP_ATTRIBUTE *Attribute, IN IA32_MAP_ATTRIBUTE *Mask, OUT BOOLEAN *IsModified OPTIONAL)
PAGE_ATTRIBUTE GetPageTableTopLevelType(VOID)
Definition: CpuPaging.c:128
VOID SetupStackGuardPage(VOID)
Definition: CpuPaging.c:464
BOOLEAN IsIa32PaeSupported(VOID)
Definition: CpuPaging.c:79
VOID * GetPageTableEntry(IN PHYSICAL_ADDRESS Address, OUT PAGE_ATTRIBUTE *PageAttribute)
Definition: CpuPaging.c:148
EFI_STATUS EFIAPI MemoryDiscoveredPpiNotifyCallback(IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, IN VOID *Ppi)
Definition: CpuPaging.c:549
RETURN_STATUS EFIAPI ConvertMemoryPageAttributes(IN PHYSICAL_ADDRESS BaseAddress, IN UINT64 Length, IN UINT64 Attributes)
Definition: CpuPaging.c:283
VOID * AllocatePageTableMemory(IN UINTN Pages)
Definition: CpuPaging.c:106
EFI_STATUS EnablePaePageTable(VOID)
Definition: CpuPaging.c:361
VOID EFIAPI GetStackBase(IN OUT VOID *Buffer)
Definition: CpuPaging.c:444
RETURN_STATUS SplitPage(IN UINT64 *PageEntry, IN PAGE_ATTRIBUTE PageAttribute, IN PAGE_ATTRIBUTE SplitAttribute, IN BOOLEAN Recursively)
Definition: CpuPaging.c:205
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:445
#define DEBUG(Expression)
Definition: DebugLib.h:422
UINTN EFIAPI AsmReadCr3(VOID)
UINT64 EFIAPI AsmReadMsr64(IN UINT32 Index)
Definition: GccInlinePriv.c:60
UINTN EFIAPI AsmWriteCr3(UINTN Cr3)
UINTN EFIAPI AsmWriteCr4(UINTN Cr4)
UINTN EFIAPI AsmReadCr0(VOID)
UINTN EFIAPI AsmWriteCr0(UINTN Cr0)
UINTN EFIAPI AsmReadCr4(VOID)
#define CPUID_SIGNATURE
Definition: Cpuid.h:45
#define CPUID_VERSION_INFO
Definition: Cpuid.h:81
UINT32 EFIAPI AsmCpuid(IN UINT32 Index, OUT UINT32 *RegisterEax OPTIONAL, OUT UINT32 *RegisterEbx OPTIONAL, OUT UINT32 *RegisterEcx OPTIONAL, OUT UINT32 *RegisterEdx OPTIONAL)
Definition: CpuId.c:36
EFI_STATUS EFIAPI MpInitLibWhoAmI(OUT UINTN *ProcessorNumber)
Definition: MpLib.c:2631
EFI_STATUS EFIAPI MpInitLibGetNumberOfProcessors(OUT UINTN *NumberOfProcessors OPTIONAL, OUT UINTN *NumberOfEnabledProcessors OPTIONAL)
Definition: MpLib.c:2668
EFI_STATUS EFIAPI MpInitLibStartupThisAP(IN EFI_AP_PROCEDURE Procedure, IN UINTN ProcessorNumber, IN EFI_EVENT WaitEvent OPTIONAL, IN UINTN TimeoutInMicroseconds, IN VOID *ProcedureArgument OPTIONAL, OUT BOOLEAN *Finished OPTIONAL)
Definition: DxeMpLib.c:845
#define PcdGet64(TokenName)
Definition: PcdLib.h:375
#define PcdGet32(TokenName)
Definition: PcdLib.h:362
#define PcdGetBool(TokenName)
Definition: PcdLib.h:401
VOID *EFIAPI AllocatePages(IN UINTN Pages)
UINT64 EFI_PHYSICAL_ADDRESS
Definition: UefiBaseType.h:50
#define EFI_PAGES_TO_SIZE(Pages)
Definition: UefiBaseType.h:213
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SIZE_TO_PAGES(Size)
Definition: UefiBaseType.h:200
EFI_PHYSICAL_ADDRESS MemoryBaseAddress
Definition: PiHob.h:119
EFI_HOB_MEMORY_ALLOCATION_HEADER AllocDescriptor
Definition: PiHob.h:174
struct CPUID_VERSION_INFO_EDX::@633 Bits
struct MSR_IA32_EFER_REGISTER::@628 Bits