Initial import.
[mirror_edk2.git] / EdkModulePkg / Core / DxeIplPeim / x64 / VirtualMemory.c
1 /*++
2
3 Copyright (c) 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13 VirtualMemory.c
14
15 Abstract:
16
17 x64 Virtual Memory Management Services in the form of an IA-32 driver.
18 Used to establish a 1:1 Virtual to Physical Mapping that is required to
19 enter Long Mode (x64 64-bit mode).
20
21 While we make a 1:1 mapping (identity mapping) for all physical pages
22 we still need to use the MTRR's to ensure that the cachability attirbutes
23 for all memory regions is correct.
24
25 The basic idea is to use 2MB page table entries where ever possible. If
26 more granularity of cachability is required then 4K page tables are used.
27
28 References:
29 1) IA-32 Intel(R) Atchitecture Software Developer's Manual Volume 1:Basic Architecture, Intel
30 2) IA-32 Intel(R) Atchitecture Software Developer's Manual Volume 2:Instruction Set Reference, Intel
31 3) IA-32 Intel(R) Atchitecture Software Developer's Manual Volume 3:System Programmer's Guide, Intel
32
33 --*/
34
35 #include "VirtualMemory.h"
36
37 x64_MTRR_VARIABLE_RANGE *mMTRRVariableRange;
38 x64_MTRR_FIXED_RANGE mMTRRFixedRange;
39
40
41 //
42 // Physial memory limit values for each of the 11 fixed MTRRs
43 //
44 UINTN mFixedRangeLimit[] = {
45 0x7FFFF, // Fixed MTRR #0 describes 0x00000..0x7FFFF
46 0x9FFFF, // Fixed MTRR #1 describes 0x80000..0x9FFFF
47 0xBFFFF, // Fixed MTRR #2 describes 0xA0000..0xBFFFF
48 0xC7FFF, // Fixed MTRR #3 describes 0xC0000..0xC7FFF
49 0xCFFFF, // Fixed MTRR #4 describes 0xC8000..0xCFFFF
50 0xD7FFF, // Fixed MTRR #5 describes 0xD0000..0xD7FFF
51 0xDFFFF, // Fixed MTRR #6 describes 0xD8000..0xDFFFF
52 0xE7FFF, // Fixed MTRR #7 describes 0xE0000..0xE7FFF
53 0xEFFFF, // Fixed MTRR #8 describes 0xE8000..0xEFFFF
54 0xF7FFF, // Fixed MTRR #9 describes 0xF0000..0xF7FFF
55 0xFFFFF // Fixed MTRR #10 describes 0xF8000..0xFFFFF
56 };
57
58 //
59 // The size, in bits, of each of the 11 fixed MTRR.
60 //
61 UINTN mFixedRangeShift[] = {
62 16, // Fixed MTRR #0 describes 8, 64 KB ranges
63 14, // Fixed MTRR #1 describes 8, 16 KB ranges
64 14, // Fixed MTRR #2 describes 8, 16 KB ranges
65 12, // Fixed MTRR #3 describes 8, 4 KB ranges
66 12, // Fixed MTRR #4 describes 8, 4 KB ranges
67 12, // Fixed MTRR #5 describes 8, 4 KB ranges
68 12, // Fixed MTRR #6 describes 8, 4 KB ranges
69 12, // Fixed MTRR #7 describes 8, 4 KB ranges
70 12, // Fixed MTRR #8 describes 8, 4 KB ranges
71 12, // Fixed MTRR #9 describes 8, 4 KB ranges
72 12 // Fixed MTRR #10 describes 8, 4 KB ranges
73 };
74
75
76 UINTN mPowerOf2[] = {
77 1,
78 2,
79 4,
80 8,
81 16,
82 32,
83 64,
84 128,
85 256,
86 512
87 };
88
89 x64_MTRR_MEMORY_TYPE
90 EfiGetMTRRMemoryType (
91 IN EFI_PHYSICAL_ADDRESS Address
92 )
93 /*++
94
95 Routine Description:
96
97 Retrieves the memory type from the MTRR that describes a physical address.
98
99 Arguments:
100
101 VariableRange - Set of Variable MTRRs
102
103 FixedRange - Set of Fixed MTRRs
104
105 Address - The physical address for which the MTRR memory type is being retrieved
106
107 Returns:
108
109 The MTRR Memory Type for the physical memory specified by Address.
110
111 --*/
112 {
113 UINTN Index;
114 UINTN TypeIndex;
115 BOOLEAN Found;
116 x64_MTRR_MEMORY_TYPE VariableType;
117 EFI_PHYSICAL_ADDRESS MaskBase;
118 EFI_PHYSICAL_ADDRESS PhysMask;
119
120 //
121 // If the MTRRs are disabled, then return the Uncached Memory Type
122 //
123 if (mMTRRFixedRange.DefaultType.Bits.E == 0) {
124 return Uncached;
125 }
126
127 //
128 // If the CPU supports Fixed MTRRs and the Fixed MTRRs are enabled, then
129 // see if Address falls into one of the Fixed MTRRs
130 //
131 if (mMTRRFixedRange.Capabilities.Bits.FIX && mMTRRFixedRange.DefaultType.Bits.FE) {
132 //
133 // Loop though 11 fixed MTRRs
134 //
135 for (Index = 0; Index < 11; Index++) {
136 //
137 // Check for a matching range
138 //
139 if (Address <= mFixedRangeLimit[Index]) {
140 //
141 // Compute the offset address into the MTRR bu subtrating the base address of the MTRR
142 //
143 if (Index > 0) {
144 Address = Address - (mFixedRangeLimit[Index-1] + 1);
145 }
146 //
147 // Retrieve the index into the MTRR to extract the memory type. The range is 0..7
148 //
149 TypeIndex = (UINTN)RShiftU64 (Address, mFixedRangeShift[Index]);
150
151 //
152 // Retrieve and return the memory type for the matching range
153 //
154 return mMTRRFixedRange.Fixed[Index].Type[TypeIndex];
155 }
156 }
157 }
158
159 //
160 // If Address was not found in a Fixed MTRR, then search the Variable MTRRs
161 //
162 for (Index = 0, Found = FALSE, VariableType = WriteBack; Index < mMTRRFixedRange.Capabilities.Bits.VCNT; Index++) {
163 //
164 // BugBug: __aullshr complier error
165 //
166 if ((mMTRRVariableRange[Index].PhysMask.Uint64 & 0x800) == 0x800) {
167 //if (mMTRRVariableRange[Index].PhysMask.Bits.Valid == 1) {
168 PhysMask = mMTRRVariableRange[Index].PhysMask.Uint64 & ~0xfff;
169 MaskBase = PhysMask & (mMTRRVariableRange[Index].PhysBase.Uint64 & ~0xfff);
170 if (MaskBase == (PhysMask & Address)) {
171 //
172 // Check to see how many matches we find
173 //
174 Found = TRUE;
175 if ((mMTRRVariableRange[Index].PhysBase.Bits.Type == Uncached) || (VariableType == Uncached)) {
176 //
177 // If any matching region uses UC, the memory region is UC
178 //
179 VariableType = Uncached;
180 } else if ((mMTRRVariableRange[Index].PhysBase.Bits.Type == WriteThrough) || (VariableType == WriteThrough)){
181 //
182 // If it's WT and WB then set it to WT. If it's WT and other type it's undefined
183 //
184 VariableType = WriteThrough;
185 } else {
186 VariableType = mMTRRVariableRange[Index].PhysBase.Bits.Type;
187 }
188 }
189 }
190 }
191
192 if (Found) {
193 return VariableType;
194 }
195
196 //
197 // Address was not found in the Fixed or Variable MTRRs, so return the default memory type
198 //
199 return mMTRRFixedRange.DefaultType.Bits.Type;
200 }
201
202
203 BOOLEAN
204 CanNotUse2MBPage (
205 IN EFI_PHYSICAL_ADDRESS BaseAddress
206 )
207 /*++
208
209 Routine Description:
210 Test to see if a 2MB aligned page has all the same attributes. If a 2MB page
211 has more than one attibute type it needs to be split into multiple 4K pages.
212
213 Arguments:
214 BaseAddress - 2MB aligned address to check out
215
216 Returns:
217 TRUE - This 2MB address range (BaseAddress) can NOT be mapped by a 2MB page
218 FALSE - This 2MB address range can be mapped by a 2MB page
219
220 --*/
221 {
222 UINTN Index;
223 x64_MTRR_MEMORY_TYPE MemoryType;
224 x64_MTRR_MEMORY_TYPE PreviousMemoryType;
225
226 //
227 // Address needs to be 2MB aligned
228 //
229 ASSERT ((BaseAddress & 0x1fffff) == 0);
230
231 PreviousMemoryType = -1;
232 for (Index = 0; Index < 512; Index++, BaseAddress += 0x1000) {
233 MemoryType = EfiGetMTRRMemoryType (BaseAddress);
234 if ((Index != 0) && (MemoryType != PreviousMemoryType)) {
235 return TRUE;
236 }
237
238 PreviousMemoryType = MemoryType;
239 }
240
241 //
242 // All the pages had the same type
243 //
244 return FALSE;
245 }
246
247
248
249
250 VOID
251 Convert2MBPageTo4KPages (
252 IN x64_PAGE_TABLE_ENTRY_2M *PageDirectoryEntry2MB,
253 IN EFI_PHYSICAL_ADDRESS PageAddress
254 )
255 /*++
256
257 Routine Description:
258 Convert a single 2MB page entry to 512 4K page entries. The attributes for
259 the 4K pages are read from the MTRR registers.
260
261 Arguments:
262 PageDirectoryEntry2MB - Page directory entry for PageAddress
263 PageAddress - 2MB algined address of region to convert
264
265 Returns:
266 None
267
268 --*/
269 {
270 EFI_PHYSICAL_ADDRESS Address;
271 x64_PAGE_DIRECTORY_ENTRY_4K *PageDirectoryEntry4k;
272 x64_PAGE_TABLE_ENTRY_4K *PageTableEntry;
273 UINTN Index1;
274
275 //
276 // Allocate the page table entry for the 4K pages
277 //
278 PageTableEntry = (x64_PAGE_TABLE_ENTRY_4K *) AllocatePages (1);
279
280 ASSERT (PageTableEntry != NULL);
281
282 //
283 // Convert PageDirectoryEntry2MB into a 4K Page Directory
284 //
285 PageDirectoryEntry4k = (x64_PAGE_DIRECTORY_ENTRY_4K *)PageDirectoryEntry2MB;
286 PageDirectoryEntry2MB->Uint64 = (UINT64)PageTableEntry;
287 PageDirectoryEntry2MB->Bits.ReadWrite = 1;
288 PageDirectoryEntry2MB->Bits.Present = 1;
289
290 //
291 // Fill in the 4K page entries with the attributes from the MTRRs
292 //
293 for (Index1 = 0, Address = PageAddress; Index1 < 512; Index1++, PageTableEntry++, Address += 0x1000) {
294 PageTableEntry->Uint64 = (UINT64)Address;
295 PageTableEntry->Bits.ReadWrite = 1;
296 PageTableEntry->Bits.Present = 1;
297 }
298 }
299
300
301 EFI_PHYSICAL_ADDRESS
302 CreateIdentityMappingPageTables (
303 IN UINT32 NumberOfProcessorPhysicalAddressBits
304 )
305 /*++
306
307 Routine Description:
308
309 Allocates and fills in the Page Directory and Page Table Entries to
310 establish a 1:1 Virtual to Physical mapping for physical memory from
311 0 to 4GB. Memory above 4GB is not mapped. The MTRRs are used to
312 determine the cachability of the physical memory regions
313
314 Arguments:
315
316 NumberOfProcessorPhysicalAddressBits - Number of processor address bits to use.
317 Limits the number of page table entries
318 to the physical address space.
319
320 Returns:
321 EFI_OUT_OF_RESOURCES There are not enough resources to allocate the Page Tables
322
323 EFI_SUCCESS The 1:1 Virtual to Physical identity mapping was created
324
325 --*/
326 {
327 EFI_PHYSICAL_ADDRESS PageAddress;
328 UINTN Index;
329 UINTN MaxBitsSupported;
330 UINTN Index1;
331 UINTN Index2;
332 x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K *PageMapLevel4Entry;
333 x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K *PageMap;
334 x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K *PageDirectoryPointerEntry;
335 x64_PAGE_TABLE_ENTRY_2M *PageDirectoryEntry2MB;
336
337
338 //
339 // Page Table structure 4 level 4K, 3 level 2MB.
340 //
341 // PageMapLevel4Entry : bits 47-39
342 // PageDirectoryPointerEntry : bits 38-30
343 // Page Table 2MB : PageDirectoryEntry2M : bits 29-21
344 // Page Table 4K : PageDirectoryEntry4K : bits 29 - 21
345 // PageTableEntry : bits 20 - 12
346 //
347 // Strategy is to map every thing in the processor address space using
348 // 2MB pages. If more granularity is required the 2MB page will get
349 // converted to set of 4K pages.
350 //
351
352 //
353 // By architecture only one PageMapLevel4 exists - so lets allocate storgage for it.
354 //
355 PageMap = PageMapLevel4Entry = (x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K *) AllocatePages (1);
356 ASSERT (PageMap != NULL);
357 PageAddress = 0;
358
359 //
360 // The number of page-map Level-4 Offset entries is based on the number of
361 // physical address bits. Less than equal to 38 bits only takes one entry.
362 // 512 entries represents 48 address bits.
363 //
364 if (NumberOfProcessorPhysicalAddressBits <= 38) {
365 MaxBitsSupported = 1;
366 } else {
367 MaxBitsSupported = mPowerOf2[NumberOfProcessorPhysicalAddressBits - 39];
368 }
369
370 for (Index = 0; Index < MaxBitsSupported; Index++, PageMapLevel4Entry++) {
371 //
372 // Each PML4 entry points to a page of Page Directory Pointer entires.
373 // So lets allocate space for them and fill them in in the Index1 loop.
374 //
375 PageDirectoryPointerEntry = (x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K *) AllocatePages (1);
376 ASSERT (PageDirectoryPointerEntry != NULL);
377
378 //
379 // Make a PML4 Entry
380 //
381 PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry;
382 PageMapLevel4Entry->Bits.ReadWrite = 1;
383 PageMapLevel4Entry->Bits.Present = 1;
384
385 for (Index1 = 0; Index1 < 512; Index1++, PageDirectoryPointerEntry++) {
386 //
387 // Each Directory Pointer entries points to a page of Page Directory entires.
388 // So lets allocate space for them and fill them in in the Index2 loop.
389 //
390 PageDirectoryEntry2MB = (x64_PAGE_TABLE_ENTRY_2M *) AllocatePages (1);
391 ASSERT (PageDirectoryEntry2MB != NULL);
392
393 //
394 // Fill in a Page Directory Pointer Entries
395 //
396 PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry2MB;
397 PageDirectoryPointerEntry->Bits.ReadWrite = 1;
398 PageDirectoryPointerEntry->Bits.Present = 1;
399
400 for (Index2 = 0; Index2 < 512; Index2++, PageDirectoryEntry2MB++, PageAddress += 0x200000) {
401 //
402 // Fill in the Page Directory entries
403 //
404 PageDirectoryEntry2MB->Uint64 = (UINT64)PageAddress;
405 PageDirectoryEntry2MB->Bits.ReadWrite = 1;
406 PageDirectoryEntry2MB->Bits.Present = 1;
407 PageDirectoryEntry2MB->Bits.MustBe1 = 1;
408
409 if (CanNotUse2MBPage (PageAddress)) {
410 //
411 // Check to see if all 2MB has the same mapping. If not convert
412 // to 4K pages by adding the 4th level of page table entries
413 //
414 Convert2MBPageTo4KPages (PageDirectoryEntry2MB, PageAddress);
415 }
416 }
417 }
418 }
419
420 //
421 // For the PML4 entries we are not using fill in a null entry.
422 // for now we just copy the first entry.
423 //
424 for (; Index < 512; Index++, PageMapLevel4Entry++) {
425 // EfiCopyMem (PageMapLevel4Entry, PageMap, sizeof (x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K));
426 CopyMem (PageMapLevel4Entry,
427 PageMap,
428 sizeof (x64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K)
429 );
430 }
431
432 return (EFI_PHYSICAL_ADDRESS)PageMap;
433 }
434