VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/UefiCpuPkg/CpuMpPei/CpuPaging.c@ 105670

Last change on this file since 105670 was 105670, checked in by vboxsync, 7 months ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

  • Property svn:eol-style set to native
File size: 11.6 KB
Line 
1/** @file
2 Basic paging support for the CPU to enable Stack Guard.
3
4Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
5
6SPDX-License-Identifier: BSD-2-Clause-Patent
7
8**/
9
10#include <Register/Intel/Cpuid.h>
11#include <Register/Intel/Msr.h>
12#include <Library/MemoryAllocationLib.h>
13#include <Library/CpuLib.h>
14#include <Library/BaseLib.h>
15#include <Guid/MigratedFvInfo.h>
16
17#include "CpuMpPei.h"
18#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull
19
20EFI_PEI_NOTIFY_DESCRIPTOR mPostMemNotifyList[] = {
21 {
22 (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
23 &gEfiPeiMemoryDiscoveredPpiGuid,
24 MemoryDiscoveredPpiNotifyCallback
25 }
26};
27
28/**
29 The function will check if IA32 PAE is supported.
30
31 @retval TRUE IA32 PAE is supported.
32 @retval FALSE IA32 PAE is not supported.
33
34**/
35BOOLEAN
36IsIa32PaeSupported (
37 VOID
38 )
39{
40 UINT32 RegEax;
41 CPUID_VERSION_INFO_EDX RegEdx;
42
43 AsmCpuid (CPUID_SIGNATURE, &RegEax, NULL, NULL, NULL);
44 if (RegEax >= CPUID_VERSION_INFO) {
45 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx.Uint32);
46 if (RegEdx.Bits.PAE != 0) {
47 return TRUE;
48 }
49 }
50
51 return FALSE;
52}
53
54/**
55 This API provides a way to allocate memory for page table.
56
57 @param Pages The number of 4 KB pages to allocate.
58
59 @return A pointer to the allocated buffer or NULL if allocation fails.
60
61**/
62VOID *
63AllocatePageTableMemory (
64 IN UINTN Pages
65 )
66{
67 VOID *Address;
68
69 Address = AllocatePages (Pages);
70 if (Address != NULL) {
71 ZeroMem (Address, EFI_PAGES_TO_SIZE (Pages));
72 }
73
74 return Address;
75}
76
77/**
78 This function modifies the page attributes for the memory region specified
79 by BaseAddress and Length to not present. This function only change page
80 table, but not flush TLB. Caller have the responsbility to flush TLB.
81
82 Caller should make sure BaseAddress and Length is at page boundary.
83
84 @param[in] BaseAddress Start address of a memory region.
85 @param[in] Length Size in bytes of the memory region.
86
87 @retval RETURN_SUCCESS The memory region is changed to not present.
88 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify
89 the attributes.
90 @retval RETURN_UNSUPPORTED Cannot modify the attributes of given memory.
91
92**/
93RETURN_STATUS
94ConvertMemoryPageToNotPresent (
95 IN PHYSICAL_ADDRESS BaseAddress,
96 IN UINT64 Length
97 )
98{
99 EFI_STATUS Status;
100 UINTN PageTable;
101 EFI_PHYSICAL_ADDRESS Buffer;
102 UINTN BufferSize;
103 IA32_MAP_ATTRIBUTE MapAttribute;
104 IA32_MAP_ATTRIBUTE MapMask;
105 PAGING_MODE PagingMode;
106 IA32_CR4 Cr4;
107 BOOLEAN Page5LevelSupport;
108 UINT32 RegEax;
109 BOOLEAN Page1GSupport;
110 CPUID_EXTENDED_CPU_SIG_EDX RegEdx;
111
112 if (sizeof (UINTN) == sizeof (UINT64)) {
113 //
114 // Check Page5Level Support or not.
115 //
116 Cr4.UintN = AsmReadCr4 ();
117 Page5LevelSupport = (Cr4.Bits.LA57 ? TRUE : FALSE);
118
119 //
120 // Check Page1G Support or not.
121 //
122 Page1GSupport = FALSE;
123 AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
124 if (RegEax >= CPUID_EXTENDED_CPU_SIG) {
125 AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx.Uint32);
126 if (RegEdx.Bits.Page1GB != 0) {
127 Page1GSupport = TRUE;
128 }
129 }
130
131 //
132 // Decide Paging Mode according Page5LevelSupport & Page1GSupport.
133 //
134 if (Page5LevelSupport) {
135 PagingMode = Page1GSupport ? Paging5Level1GB : Paging5Level;
136 } else {
137 PagingMode = Page1GSupport ? Paging4Level1GB : Paging4Level;
138 }
139 } else {
140 PagingMode = PagingPae;
141 }
142
143 MapAttribute.Uint64 = 0;
144 MapMask.Uint64 = 0;
145 MapMask.Bits.Present = 1;
146 PageTable = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
147 BufferSize = 0;
148
149 //
150 // Get required buffer size for the pagetable that will be created.
151 //
152 Status = PageTableMap (&PageTable, PagingMode, 0, &BufferSize, BaseAddress, Length, &MapAttribute, &MapMask, NULL);
153 if (Status == EFI_BUFFER_TOO_SMALL) {
154 //
155 // Allocate required Buffer.
156 //
157 Status = PeiServicesAllocatePages (
158 EfiBootServicesData,
159 EFI_SIZE_TO_PAGES (BufferSize),
160 &Buffer
161 );
162 ASSERT_EFI_ERROR (Status);
163 if (EFI_ERROR (Status)) {
164 return EFI_OUT_OF_RESOURCES;
165 }
166
167 Status = PageTableMap (&PageTable, PagingMode, (VOID *)(UINTN)Buffer, &BufferSize, BaseAddress, Length, &MapAttribute, &MapMask, NULL);
168 }
169
170 ASSERT_EFI_ERROR (Status);
171 return Status;
172}
173
174/**
175 Enable PAE Page Table.
176
177 @retval EFI_SUCCESS The PAE Page Table was enabled successfully.
178 @retval EFI_OUT_OF_RESOURCES The PAE Page Table could not be enabled due to lack of available memory.
179
180**/
181EFI_STATUS
182EnablePaePageTable (
183 VOID
184 )
185{
186 EFI_STATUS Status;
187
188 UINTN PageTable;
189 VOID *Buffer;
190 UINTN BufferSize;
191 IA32_MAP_ATTRIBUTE MapAttribute;
192 IA32_MAP_ATTRIBUTE MapMask;
193
194 PageTable = 0;
195 Buffer = NULL;
196 BufferSize = 0;
197 MapAttribute.Uint64 = 0;
198 MapMask.Uint64 = MAX_UINT64;
199 MapAttribute.Bits.Present = 1;
200 MapAttribute.Bits.ReadWrite = 1;
201
202 //
203 // 1:1 map 4GB in 32bit mode
204 //
205 Status = PageTableMap (&PageTable, PagingPae, 0, &BufferSize, 0, SIZE_4GB, &MapAttribute, &MapMask, NULL);
206 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
207 if (Status != EFI_BUFFER_TOO_SMALL) {
208 return Status;
209 }
210
211 //
212 // Allocate required Buffer.
213 //
214 Buffer = AllocatePageTableMemory (EFI_SIZE_TO_PAGES (BufferSize));
215 ASSERT (Buffer != NULL);
216 if (Buffer == NULL) {
217 return EFI_OUT_OF_RESOURCES;
218 }
219
220 Status = PageTableMap (&PageTable, PagingPae, Buffer, &BufferSize, 0, SIZE_4GB, &MapAttribute, &MapMask, NULL);
221 ASSERT_EFI_ERROR (Status);
222 if (EFI_ERROR (Status) || (PageTable == 0)) {
223 return EFI_OUT_OF_RESOURCES;
224 }
225
226 //
227 // Write the Pagetable to CR3.
228 //
229 AsmWriteCr3 (PageTable);
230
231 //
232 // Enable CR4.PAE
233 //
234 AsmWriteCr4 (AsmReadCr4 () | BIT5);
235
236 //
237 // Enable CR0.PG
238 //
239 AsmWriteCr0 (AsmReadCr0 () | BIT31);
240
241 DEBUG ((
242 DEBUG_INFO,
243 "EnablePaePageTable: Created PageTable = 0x%x, BufferSize = %x\n",
244 PageTable,
245 BufferSize
246 ));
247
248 return Status;
249}
250
251/**
252 Get the base address of current AP's stack.
253
254 This function is called in AP's context and assumes that whole calling stacks
255 (till this function) consumed by AP's wakeup procedure will not exceed 4KB.
256
257 PcdCpuApStackSize must be configured with value taking the Guard page into
258 account.
259
260 @param[in,out] Buffer The pointer to private data buffer.
261
262**/
263VOID
264EFIAPI
265GetStackBase (
266 IN OUT VOID *Buffer
267 )
268{
269 EFI_PHYSICAL_ADDRESS StackBase;
270 UINTN Index;
271
272 MpInitLibWhoAmI (&Index);
273 StackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)&StackBase;
274 StackBase += BASE_4KB;
275 StackBase &= ~((EFI_PHYSICAL_ADDRESS)BASE_4KB - 1);
276 StackBase -= PcdGet32 (PcdCpuApStackSize);
277
278 *((EFI_PHYSICAL_ADDRESS *)Buffer + Index) = StackBase;
279}
280
281/**
282 Setup stack Guard page at the stack base of each processor. BSP and APs have
283 different way to get stack base address.
284
285**/
286VOID
287SetupStackGuardPage (
288 VOID
289 )
290{
291 EFI_PEI_HOB_POINTERS Hob;
292 EFI_PHYSICAL_ADDRESS *StackBase;
293 UINTN NumberOfProcessors;
294 UINTN Bsp;
295 UINTN Index;
296 EFI_STATUS Status;
297
298 //
299 // One extra page at the bottom of the stack is needed for Guard page.
300 //
301 if (PcdGet32 (PcdCpuApStackSize) <= EFI_PAGE_SIZE) {
302 DEBUG ((DEBUG_ERROR, "PcdCpuApStackSize is not big enough for Stack Guard!\n"));
303 ASSERT (FALSE);
304 }
305
306 Status = MpInitLibGetNumberOfProcessors (&NumberOfProcessors, NULL);
307 ASSERT_EFI_ERROR (Status);
308
309 if (EFI_ERROR (Status)) {
310 NumberOfProcessors = 1;
311 }
312
313 StackBase = (EFI_PHYSICAL_ADDRESS *)AllocatePages (EFI_SIZE_TO_PAGES (sizeof (EFI_PHYSICAL_ADDRESS) * NumberOfProcessors));
314 ASSERT (StackBase != NULL);
315 if (StackBase == NULL) {
316 return;
317 }
318
319 ZeroMem (StackBase, sizeof (EFI_PHYSICAL_ADDRESS) * NumberOfProcessors);
320 MpInitLibStartupAllAPs (GetStackBase, FALSE, NULL, 0, (VOID *)StackBase, NULL);
321 MpInitLibWhoAmI (&Bsp);
322 Hob.Raw = GetHobList ();
323 while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) {
324 if (CompareGuid (
325 &gEfiHobMemoryAllocStackGuid,
326 &(Hob.MemoryAllocationStack->AllocDescriptor.Name)
327 ))
328 {
329 StackBase[Bsp] = Hob.MemoryAllocationStack->AllocDescriptor.MemoryBaseAddress;
330 break;
331 }
332
333 Hob.Raw = GET_NEXT_HOB (Hob);
334 }
335
336 for (Index = 0; Index < NumberOfProcessors; ++Index) {
337 ASSERT (StackBase[Index] != 0);
338 //
339 // Set Guard page at stack base address.
340 //
341 ConvertMemoryPageToNotPresent (StackBase[Index], EFI_PAGE_SIZE);
342 DEBUG ((
343 DEBUG_INFO,
344 "Stack Guard set at %lx [cpu%lu]!\n",
345 (UINT64)StackBase[Index],
346 (UINT64)Index
347 ));
348 }
349
350 FreePages (StackBase, EFI_SIZE_TO_PAGES (sizeof (EFI_PHYSICAL_ADDRESS) * NumberOfProcessors));
351 //
352 // Publish the changes of page table.
353 //
354 CpuFlushTlb ();
355}
356
357/**
358 Enable/setup stack guard for each processor if PcdCpuStackGuard is set to TRUE.
359
360 Doing this in the memory-discovered callback is to make sure the Stack Guard
361 feature to cover as most PEI code as possible.
362
363 @param[in] PeiServices General purpose services available to every PEIM.
364 @param[in] NotifyDescriptor The notification structure this PEIM registered on install.
365 @param[in] Ppi The memory discovered PPI. Not used.
366
367 @retval EFI_SUCCESS The function completed successfully.
368 @retval others There's error in MP initialization.
369**/
370EFI_STATUS
371EFIAPI
372MemoryDiscoveredPpiNotifyCallback (
373 IN EFI_PEI_SERVICES **PeiServices,
374 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
375 IN VOID *Ppi
376 )
377{
378 EFI_STATUS Status;
379 BOOLEAN InitStackGuard;
380 EDKII_MIGRATED_FV_INFO *MigratedFvInfo;
381 EFI_PEI_HOB_POINTERS Hob;
382 IA32_CR0 Cr0;
383
384 //
385 // Paging must be setup first. Otherwise the exception TSS setup during MP
386 // initialization later will not contain paging information and then fail
387 // the task switch (for the sake of stack switch).
388 //
389 InitStackGuard = FALSE;
390 Hob.Raw = NULL;
391 if (IsIa32PaeSupported ()) {
392 Hob.Raw = GetFirstGuidHob (&gEdkiiMigratedFvInfoGuid);
393 InitStackGuard = PcdGetBool (PcdCpuStackGuard);
394 }
395
396 //
397 // Some security features depend on the page table enabling. So, here
398 // is to enable paging if it is not enabled (only in 32bit mode).
399 //
400 Cr0.UintN = AsmReadCr0 ();
401 if ((Cr0.Bits.PG == 0) && (InitStackGuard || (Hob.Raw != NULL))) {
402 ASSERT (sizeof (UINTN) == sizeof (UINT32));
403
404 Status = EnablePaePageTable ();
405 if (EFI_ERROR (Status)) {
406 DEBUG ((DEBUG_ERROR, "MemoryDiscoveredPpiNotifyCallback: Failed to enable PAE page table: %r.\n", Status));
407 CpuDeadLoop ();
408 }
409 }
410
411 Status = InitializeCpuMpWorker ((CONST EFI_PEI_SERVICES **)PeiServices);
412 ASSERT_EFI_ERROR (Status);
413
414 if (InitStackGuard) {
415 SetupStackGuardPage ();
416 }
417
418 while (Hob.Raw != NULL) {
419 MigratedFvInfo = GET_GUID_HOB_DATA (Hob);
420
421 //
422 // Enable #PF exception, so if the code access SPI after disable NEM, it will generate
423 // the exception to avoid potential vulnerability.
424 //
425 ConvertMemoryPageToNotPresent (MigratedFvInfo->FvOrgBase, MigratedFvInfo->FvLength);
426
427 Hob.Raw = GET_NEXT_HOB (Hob);
428 Hob.Raw = GetNextGuidHob (&gEdkiiMigratedFvInfoGuid, Hob.Raw);
429 }
430
431 CpuFlushTlb ();
432
433 return Status;
434}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette