• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   x64 Virtual Memory Management Services in the form of an IA-32 driver.
3   Used to establish a 1:1 Virtual to Physical Mapping that is required to
4   enter Long Mode (x64 64-bit mode).
5 
6   While we make a 1:1 mapping (identity mapping) for all physical pages
7   we still need to use the MTRR's to ensure that the cachability attributes
8   for all memory regions is correct.
9 
10   The basic idea is to use 2MB page table entries where ever possible. If
11   more granularity of cachability is required then 4K page tables are used.
12 
13   References:
14     1) IA-32 Intel(R) Architecture Software Developer's Manual Volume 1:Basic Architecture, Intel
15     2) IA-32 Intel(R) Architecture Software Developer's Manual Volume 2:Instruction Set Reference, Intel
16     3) IA-32 Intel(R) Architecture Software Developer's Manual Volume 3:System Programmer's Guide, Intel
17 
18 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
19 This program and the accompanying materials
20 are licensed and made available under the terms and conditions of the BSD License
21 which accompanies this distribution.  The full text of the license may be found at
22 http://opensource.org/licenses/bsd-license.php
23 
24 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
25 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
26 
27 **/
28 
29 #include "DxeIpl.h"
30 #include "VirtualMemory.h"
31 
32 /**
33   Enable Execute Disable Bit.
34 
35 **/
36 VOID
EnableExecuteDisableBit(VOID)37 EnableExecuteDisableBit (
38   VOID
39   )
40 {
41   UINT64           MsrRegisters;
42 
43   MsrRegisters = AsmReadMsr64 (0xC0000080);
44   MsrRegisters |= BIT11;
45   AsmWriteMsr64 (0xC0000080, MsrRegisters);
46 }
47 
48 /**
49   Split 2M page to 4K.
50 
51   @param[in]      PhysicalAddress       Start physical address the 2M page covered.
52   @param[in, out] PageEntry2M           Pointer to 2M page entry.
53   @param[in]      StackBase             Stack base address.
54   @param[in]      StackSize             Stack size.
55 
56 **/
57 VOID
Split2MPageTo4K(IN EFI_PHYSICAL_ADDRESS PhysicalAddress,IN OUT UINT64 * PageEntry2M,IN EFI_PHYSICAL_ADDRESS StackBase,IN UINTN StackSize)58 Split2MPageTo4K (
59   IN EFI_PHYSICAL_ADDRESS               PhysicalAddress,
60   IN OUT UINT64                         *PageEntry2M,
61   IN EFI_PHYSICAL_ADDRESS               StackBase,
62   IN UINTN                              StackSize
63   )
64 {
65   EFI_PHYSICAL_ADDRESS                  PhysicalAddress4K;
66   UINTN                                 IndexOfPageTableEntries;
67   PAGE_TABLE_4K_ENTRY                   *PageTableEntry;
68 
69   PageTableEntry = AllocatePages (1);
70   //
71   // Fill in 2M page entry.
72   //
73   *PageEntry2M = (UINT64) (UINTN) PageTableEntry | IA32_PG_P | IA32_PG_RW;
74 
75   PhysicalAddress4K = PhysicalAddress;
76   for (IndexOfPageTableEntries = 0; IndexOfPageTableEntries < 512; IndexOfPageTableEntries++, PageTableEntry++, PhysicalAddress4K += SIZE_4KB) {
77     //
78     // Fill in the Page Table entries
79     //
80     PageTableEntry->Uint64 = (UINT64) PhysicalAddress4K;
81     PageTableEntry->Bits.ReadWrite = 1;
82     PageTableEntry->Bits.Present = 1;
83     if ((PhysicalAddress4K >= StackBase) && (PhysicalAddress4K < StackBase + StackSize)) {
84       //
85       // Set Nx bit for stack.
86       //
87       PageTableEntry->Bits.Nx = 1;
88     }
89   }
90 }
91 
92 /**
93   Split 1G page to 2M.
94 
95   @param[in]      PhysicalAddress       Start physical address the 1G page covered.
96   @param[in, out] PageEntry1G           Pointer to 1G page entry.
97   @param[in]      StackBase             Stack base address.
98   @param[in]      StackSize             Stack size.
99 
100 **/
101 VOID
Split1GPageTo2M(IN EFI_PHYSICAL_ADDRESS PhysicalAddress,IN OUT UINT64 * PageEntry1G,IN EFI_PHYSICAL_ADDRESS StackBase,IN UINTN StackSize)102 Split1GPageTo2M (
103   IN EFI_PHYSICAL_ADDRESS               PhysicalAddress,
104   IN OUT UINT64                         *PageEntry1G,
105   IN EFI_PHYSICAL_ADDRESS               StackBase,
106   IN UINTN                              StackSize
107   )
108 {
109   EFI_PHYSICAL_ADDRESS                  PhysicalAddress2M;
110   UINTN                                 IndexOfPageDirectoryEntries;
111   PAGE_TABLE_ENTRY                      *PageDirectoryEntry;
112 
113   PageDirectoryEntry = AllocatePages (1);
114   //
115   // Fill in 1G page entry.
116   //
117   *PageEntry1G = (UINT64) (UINTN) PageDirectoryEntry | IA32_PG_P | IA32_PG_RW;
118 
119   PhysicalAddress2M = PhysicalAddress;
120   for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress2M += SIZE_2MB) {
121     if ((PhysicalAddress2M < StackBase + StackSize) && ((PhysicalAddress2M + SIZE_2MB) > StackBase)) {
122       //
123       // Need to split this 2M page that covers stack range.
124       //
125       Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, StackBase, StackSize);
126     } else {
127       //
128       // Fill in the Page Directory entries
129       //
130       PageDirectoryEntry->Uint64 = (UINT64) PhysicalAddress2M;
131       PageDirectoryEntry->Bits.ReadWrite = 1;
132       PageDirectoryEntry->Bits.Present = 1;
133       PageDirectoryEntry->Bits.MustBe1 = 1;
134     }
135   }
136 }
137 
138 /**
139   Allocates and fills in the Page Directory and Page Table Entries to
140   establish a 1:1 Virtual to Physical mapping.
141 
142   @param[in] StackBase  Stack base address.
143   @param[in] StackSize  Stack size.
144 
145   @return The address of 4 level page map.
146 
147 **/
148 UINTN
CreateIdentityMappingPageTables(IN EFI_PHYSICAL_ADDRESS StackBase,IN UINTN StackSize)149 CreateIdentityMappingPageTables (
150   IN EFI_PHYSICAL_ADDRESS   StackBase,
151   IN UINTN                  StackSize
152   )
153 {
154   UINT32                                        RegEax;
155   UINT32                                        RegEdx;
156   UINT8                                         PhysicalAddressBits;
157   EFI_PHYSICAL_ADDRESS                          PageAddress;
158   UINTN                                         IndexOfPml4Entries;
159   UINTN                                         IndexOfPdpEntries;
160   UINTN                                         IndexOfPageDirectoryEntries;
161   UINT32                                        NumberOfPml4EntriesNeeded;
162   UINT32                                        NumberOfPdpEntriesNeeded;
163   PAGE_MAP_AND_DIRECTORY_POINTER                *PageMapLevel4Entry;
164   PAGE_MAP_AND_DIRECTORY_POINTER                *PageMap;
165   PAGE_MAP_AND_DIRECTORY_POINTER                *PageDirectoryPointerEntry;
166   PAGE_TABLE_ENTRY                              *PageDirectoryEntry;
167   UINTN                                         TotalPagesNum;
168   UINTN                                         BigPageAddress;
169   VOID                                          *Hob;
170   BOOLEAN                                       Page1GSupport;
171   PAGE_TABLE_1G_ENTRY                           *PageDirectory1GEntry;
172 
173   Page1GSupport = FALSE;
174   if (PcdGetBool(PcdUse1GPageTable)) {
175     AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
176     if (RegEax >= 0x80000001) {
177       AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
178       if ((RegEdx & BIT26) != 0) {
179         Page1GSupport = TRUE;
180       }
181     }
182   }
183 
184   //
185   // Get physical address bits supported.
186   //
187   Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
188   if (Hob != NULL) {
189     PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
190   } else {
191     AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
192     if (RegEax >= 0x80000008) {
193       AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
194       PhysicalAddressBits = (UINT8) RegEax;
195     } else {
196       PhysicalAddressBits = 36;
197     }
198   }
199 
200   //
201   // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
202   //
203   ASSERT (PhysicalAddressBits <= 52);
204   if (PhysicalAddressBits > 48) {
205     PhysicalAddressBits = 48;
206   }
207 
208   //
209   // Calculate the table entries needed.
210   //
211   if (PhysicalAddressBits <= 39 ) {
212     NumberOfPml4EntriesNeeded = 1;
213     NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
214   } else {
215     NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
216     NumberOfPdpEntriesNeeded = 512;
217   }
218 
219   //
220   // Pre-allocate big pages to avoid later allocations.
221   //
222   if (!Page1GSupport) {
223     TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
224   } else {
225     TotalPagesNum = NumberOfPml4EntriesNeeded + 1;
226   }
227   BigPageAddress = (UINTN) AllocatePages (TotalPagesNum);
228   ASSERT (BigPageAddress != 0);
229 
230   //
231   // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
232   //
233   PageMap         = (VOID *) BigPageAddress;
234   BigPageAddress += SIZE_4KB;
235 
236   PageMapLevel4Entry = PageMap;
237   PageAddress        = 0;
238   for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {
239     //
240     // Each PML4 entry points to a page of Page Directory Pointer entires.
241     // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
242     //
243     PageDirectoryPointerEntry = (VOID *) BigPageAddress;
244     BigPageAddress += SIZE_4KB;
245 
246     //
247     // Make a PML4 Entry
248     //
249     PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry;
250     PageMapLevel4Entry->Bits.ReadWrite = 1;
251     PageMapLevel4Entry->Bits.Present = 1;
252 
253     if (Page1GSupport) {
254       PageDirectory1GEntry = (VOID *) PageDirectoryPointerEntry;
255 
256       for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {
257         if (PcdGetBool (PcdSetNxForStack) && (PageAddress < StackBase + StackSize) && ((PageAddress + SIZE_1GB) > StackBase)) {
258           Split1GPageTo2M (PageAddress, (UINT64 *) PageDirectory1GEntry, StackBase, StackSize);
259         } else {
260           //
261           // Fill in the Page Directory entries
262           //
263           PageDirectory1GEntry->Uint64 = (UINT64)PageAddress;
264           PageDirectory1GEntry->Bits.ReadWrite = 1;
265           PageDirectory1GEntry->Bits.Present = 1;
266           PageDirectory1GEntry->Bits.MustBe1 = 1;
267         }
268       }
269     } else {
270       for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
271         //
272         // Each Directory Pointer entries points to a page of Page Directory entires.
273         // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
274         //
275         PageDirectoryEntry = (VOID *) BigPageAddress;
276         BigPageAddress += SIZE_4KB;
277 
278         //
279         // Fill in a Page Directory Pointer Entries
280         //
281         PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry;
282         PageDirectoryPointerEntry->Bits.ReadWrite = 1;
283         PageDirectoryPointerEntry->Bits.Present = 1;
284 
285         for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {
286           if (PcdGetBool (PcdSetNxForStack) && (PageAddress < StackBase + StackSize) && ((PageAddress + SIZE_2MB) > StackBase)) {
287             //
288             // Need to split this 2M page that covers stack range.
289             //
290             Split2MPageTo4K (PageAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize);
291           } else {
292             //
293             // Fill in the Page Directory entries
294             //
295             PageDirectoryEntry->Uint64 = (UINT64)PageAddress;
296             PageDirectoryEntry->Bits.ReadWrite = 1;
297             PageDirectoryEntry->Bits.Present = 1;
298             PageDirectoryEntry->Bits.MustBe1 = 1;
299           }
300         }
301       }
302 
303       for (; IndexOfPdpEntries < 512; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
304         ZeroMem (
305           PageDirectoryPointerEntry,
306           sizeof(PAGE_MAP_AND_DIRECTORY_POINTER)
307           );
308       }
309     }
310   }
311 
312   //
313   // For the PML4 entries we are not using fill in a null entry.
314   //
315   for (; IndexOfPml4Entries < 512; IndexOfPml4Entries++, PageMapLevel4Entry++) {
316     ZeroMem (
317       PageMapLevel4Entry,
318       sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)
319       );
320   }
321 
322   if (PcdGetBool (PcdSetNxForStack)) {
323     EnableExecuteDisableBit ();
324   }
325 
326   return (UINTN)PageMap;
327 }
328 
329