]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
MdeModulePkg: Add New Memory Attributes
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / Misc / MemoryProtection.c
CommitLineData
d0e92aad
JY
1/** @file\r
2 UEFI Memory Protection support.\r
3\r
4 If the UEFI image is page aligned, the image code section is set to read only\r
5 and the image data section is set to non-executable.\r
6\r
7 1) This policy is applied for all UEFI image including boot service driver,\r
8 runtime driver or application.\r
9 2) This policy is applied only if the UEFI image meets the page alignment\r
10 requirement.\r
11 3) This policy is applied only if the Source UEFI image matches the\r
12 PcdImageProtectionPolicy definition.\r
13 4) This policy is not applied to the non-PE image region.\r
14\r
15 The DxeCore calls CpuArchProtocol->SetMemoryAttributes() to protect\r
16 the image. If the CpuArch protocol is not installed yet, the DxeCore\r
17 enqueues the protection request. Once the CpuArch is installed, the\r
18 DxeCore dequeues the protection request and applies policy.\r
19\r
20 Once the image is unloaded, the protection is removed automatically.\r
21\r
a10acf0e 22Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR>\r
9d510e61 23SPDX-License-Identifier: BSD-2-Clause-Patent\r
d0e92aad
JY
24\r
25**/\r
26\r
27#include <PiDxe.h>\r
28#include <Library/BaseLib.h>\r
29#include <Library/BaseMemoryLib.h>\r
30#include <Library/MemoryAllocationLib.h>\r
31#include <Library/UefiBootServicesTableLib.h>\r
32#include <Library/DxeServicesTableLib.h>\r
33#include <Library/DebugLib.h>\r
34#include <Library/UefiLib.h>\r
35\r
36#include <Guid/EventGroup.h>\r
37#include <Guid/MemoryAttributesTable.h>\r
d0e92aad
JY
38\r
39#include <Protocol/FirmwareVolume2.h>\r
d0e92aad
JY
40#include <Protocol/SimpleFileSystem.h>\r
41\r
42#include "DxeMain.h"\r
a6a0a597 43#include "Mem/HeapGuard.h"\r
d0e92aad 44\r
d0e92aad
JY
45//\r
46// Image type definitions\r
47//\r
48#define IMAGE_UNKNOWN 0x00000001\r
49#define IMAGE_FROM_FV 0x00000002\r
50\r
51//\r
52// Protection policy bit definition\r
53//\r
54#define DO_NOT_PROTECT 0x00000000\r
55#define PROTECT_IF_ALIGNED_ELSE_ALLOW 0x00000001\r
56\r
7eb927db
AB
57#define MEMORY_TYPE_OS_RESERVED_MIN 0x80000000\r
58#define MEMORY_TYPE_OEM_RESERVED_MIN 0x70000000\r
59\r
60#define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \\r
61 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))\r
62\r
d0e92aad
JY
63UINT32 mImageProtectionPolicy;\r
64\r
7eb927db
AB
65extern LIST_ENTRY mGcdMemorySpaceMap;\r
66\r
5920a9d1
AB
67STATIC LIST_ENTRY mProtectedImageRecordList;\r
68\r
d0e92aad
JY
69/**\r
70 Sort code section in image record, based upon CodeSegmentBase from low to high.\r
71\r
72 @param ImageRecord image record to be sorted\r
73**/\r
74VOID\r
75SortImageRecordCodeSection (\r
76 IN IMAGE_PROPERTIES_RECORD *ImageRecord\r
77 );\r
78\r
79/**\r
80 Check if code section in image record is valid.\r
81\r
82 @param ImageRecord image record to be checked\r
83\r
84 @retval TRUE image record is valid\r
85 @retval FALSE image record is invalid\r
86**/\r
87BOOLEAN\r
88IsImageRecordCodeSectionValid (\r
89 IN IMAGE_PROPERTIES_RECORD *ImageRecord\r
90 );\r
91\r
92/**\r
93 Get the image type.\r
94\r
95 @param[in] File This is a pointer to the device path of the file that is\r
96 being dispatched.\r
97\r
98 @return UINT32 Image Type\r
99**/\r
100UINT32\r
101GetImageType (\r
102 IN CONST EFI_DEVICE_PATH_PROTOCOL *File\r
103 )\r
104{\r
105 EFI_STATUS Status;\r
106 EFI_HANDLE DeviceHandle;\r
107 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
108\r
109 if (File == NULL) {\r
110 return IMAGE_UNKNOWN;\r
111 }\r
112\r
113 //\r
114 // First check to see if File is from a Firmware Volume\r
115 //\r
116 DeviceHandle = NULL;\r
117 TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;\r
118 Status = gBS->LocateDevicePath (\r
119 &gEfiFirmwareVolume2ProtocolGuid,\r
120 &TempDevicePath,\r
121 &DeviceHandle\r
122 );\r
123 if (!EFI_ERROR (Status)) {\r
124 Status = gBS->OpenProtocol (\r
125 DeviceHandle,\r
126 &gEfiFirmwareVolume2ProtocolGuid,\r
127 NULL,\r
128 NULL,\r
129 NULL,\r
130 EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
131 );\r
132 if (!EFI_ERROR (Status)) {\r
133 return IMAGE_FROM_FV;\r
134 }\r
135 }\r
136 return IMAGE_UNKNOWN;\r
137}\r
138\r
139/**\r
140 Get UEFI image protection policy based upon image type.\r
141\r
142 @param[in] ImageType The UEFI image type\r
143\r
144 @return UEFI image protection policy\r
145**/\r
146UINT32\r
147GetProtectionPolicyFromImageType (\r
148 IN UINT32 ImageType\r
149 )\r
150{\r
151 if ((ImageType & mImageProtectionPolicy) == 0) {\r
152 return DO_NOT_PROTECT;\r
153 } else {\r
154 return PROTECT_IF_ALIGNED_ELSE_ALLOW;\r
155 }\r
156}\r
157\r
158/**\r
159 Get UEFI image protection policy based upon loaded image device path.\r
160\r
161 @param[in] LoadedImage The loaded image protocol\r
162 @param[in] LoadedImageDevicePath The loaded image device path protocol\r
163\r
164 @return UEFI image protection policy\r
165**/\r
166UINT32\r
167GetUefiImageProtectionPolicy (\r
168 IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage,\r
169 IN EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath\r
170 )\r
171{\r
172 BOOLEAN InSmm;\r
173 UINT32 ImageType;\r
174 UINT32 ProtectionPolicy;\r
175\r
176 //\r
177 // Check SMM\r
178 //\r
179 InSmm = FALSE;\r
180 if (gSmmBase2 != NULL) {\r
181 gSmmBase2->InSmm (gSmmBase2, &InSmm);\r
182 }\r
183 if (InSmm) {\r
184 return FALSE;\r
185 }\r
186\r
187 //\r
188 // Check DevicePath\r
189 //\r
190 if (LoadedImage == gDxeCoreLoadedImage) {\r
191 ImageType = IMAGE_FROM_FV;\r
192 } else {\r
193 ImageType = GetImageType (LoadedImageDevicePath);\r
194 }\r
195 ProtectionPolicy = GetProtectionPolicyFromImageType (ImageType);\r
196 return ProtectionPolicy;\r
197}\r
198\r
199\r
200/**\r
201 Set UEFI image memory attributes.\r
202\r
203 @param[in] BaseAddress Specified start address\r
204 @param[in] Length Specified length\r
205 @param[in] Attributes Specified attributes\r
206**/\r
207VOID\r
208SetUefiImageMemoryAttributes (\r
209 IN UINT64 BaseAddress,\r
210 IN UINT64 Length,\r
211 IN UINT64 Attributes\r
212 )\r
213{\r
214 EFI_STATUS Status;\r
215 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;\r
216 UINT64 FinalAttributes;\r
217\r
218 Status = CoreGetMemorySpaceDescriptor(BaseAddress, &Descriptor);\r
219 ASSERT_EFI_ERROR(Status);\r
220\r
3bd5c994 221 FinalAttributes = (Descriptor.Attributes & EFI_CACHE_ATTRIBUTE_MASK) | (Attributes & EFI_MEMORY_ATTRIBUTE_MASK);\r
d0e92aad
JY
222\r
223 DEBUG ((DEBUG_INFO, "SetUefiImageMemoryAttributes - 0x%016lx - 0x%016lx (0x%016lx)\n", BaseAddress, Length, FinalAttributes));\r
224\r
225 ASSERT(gCpu != NULL);\r
226 gCpu->SetMemoryAttributes (gCpu, BaseAddress, Length, FinalAttributes);\r
227}\r
228\r
229/**\r
230 Set UEFI image protection attributes.\r
231\r
232 @param[in] ImageRecord A UEFI image record\r
d0e92aad
JY
233**/\r
234VOID\r
235SetUefiImageProtectionAttributes (\r
5920a9d1 236 IN IMAGE_PROPERTIES_RECORD *ImageRecord\r
d0e92aad
JY
237 )\r
238{\r
239 IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection;\r
240 LIST_ENTRY *ImageRecordCodeSectionLink;\r
241 LIST_ENTRY *ImageRecordCodeSectionEndLink;\r
242 LIST_ENTRY *ImageRecordCodeSectionList;\r
243 UINT64 CurrentBase;\r
244 UINT64 ImageEnd;\r
d0e92aad
JY
245\r
246 ImageRecordCodeSectionList = &ImageRecord->CodeSegmentList;\r
247\r
248 CurrentBase = ImageRecord->ImageBase;\r
249 ImageEnd = ImageRecord->ImageBase + ImageRecord->ImageSize;\r
250\r
251 ImageRecordCodeSectionLink = ImageRecordCodeSectionList->ForwardLink;\r
252 ImageRecordCodeSectionEndLink = ImageRecordCodeSectionList;\r
253 while (ImageRecordCodeSectionLink != ImageRecordCodeSectionEndLink) {\r
254 ImageRecordCodeSection = CR (\r
255 ImageRecordCodeSectionLink,\r
256 IMAGE_PROPERTIES_RECORD_CODE_SECTION,\r
257 Link,\r
258 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE\r
259 );\r
260 ImageRecordCodeSectionLink = ImageRecordCodeSectionLink->ForwardLink;\r
261\r
262 ASSERT (CurrentBase <= ImageRecordCodeSection->CodeSegmentBase);\r
263 if (CurrentBase < ImageRecordCodeSection->CodeSegmentBase) {\r
264 //\r
265 // DATA\r
266 //\r
d0e92aad
JY
267 SetUefiImageMemoryAttributes (\r
268 CurrentBase,\r
269 ImageRecordCodeSection->CodeSegmentBase - CurrentBase,\r
5920a9d1 270 EFI_MEMORY_XP\r
d0e92aad
JY
271 );\r
272 }\r
273 //\r
274 // CODE\r
275 //\r
d0e92aad
JY
276 SetUefiImageMemoryAttributes (\r
277 ImageRecordCodeSection->CodeSegmentBase,\r
278 ImageRecordCodeSection->CodeSegmentSize,\r
5920a9d1 279 EFI_MEMORY_RO\r
d0e92aad
JY
280 );\r
281 CurrentBase = ImageRecordCodeSection->CodeSegmentBase + ImageRecordCodeSection->CodeSegmentSize;\r
282 }\r
283 //\r
284 // Last DATA\r
285 //\r
286 ASSERT (CurrentBase <= ImageEnd);\r
287 if (CurrentBase < ImageEnd) {\r
288 //\r
289 // DATA\r
290 //\r
d0e92aad
JY
291 SetUefiImageMemoryAttributes (\r
292 CurrentBase,\r
293 ImageEnd - CurrentBase,\r
5920a9d1 294 EFI_MEMORY_XP\r
d0e92aad
JY
295 );\r
296 }\r
297 return ;\r
298}\r
299\r
300/**\r
301 Return if the PE image section is aligned.\r
302\r
303 @param[in] SectionAlignment PE/COFF section alignment\r
304 @param[in] MemoryType PE/COFF image memory type\r
305\r
306 @retval TRUE The PE image section is aligned.\r
307 @retval FALSE The PE image section is not aligned.\r
308**/\r
309BOOLEAN\r
310IsMemoryProtectionSectionAligned (\r
311 IN UINT32 SectionAlignment,\r
312 IN EFI_MEMORY_TYPE MemoryType\r
313 )\r
314{\r
315 UINT32 PageAlignment;\r
316\r
317 switch (MemoryType) {\r
318 case EfiRuntimeServicesCode:\r
319 case EfiACPIMemoryNVS:\r
d4731a98 320 PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;\r
d0e92aad
JY
321 break;\r
322 case EfiRuntimeServicesData:\r
323 case EfiACPIReclaimMemory:\r
324 ASSERT (FALSE);\r
d4731a98 325 PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;\r
d0e92aad
JY
326 break;\r
327 case EfiBootServicesCode:\r
328 case EfiLoaderCode:\r
329 case EfiReservedMemoryType:\r
330 PageAlignment = EFI_PAGE_SIZE;\r
331 break;\r
332 default:\r
333 ASSERT (FALSE);\r
334 PageAlignment = EFI_PAGE_SIZE;\r
335 break;\r
336 }\r
337\r
338 if ((SectionAlignment & (PageAlignment - 1)) != 0) {\r
339 return FALSE;\r
340 } else {\r
341 return TRUE;\r
342 }\r
343}\r
344\r
345/**\r
346 Free Image record.\r
347\r
348 @param[in] ImageRecord A UEFI image record\r
349**/\r
350VOID\r
351FreeImageRecord (\r
352 IN IMAGE_PROPERTIES_RECORD *ImageRecord\r
353 )\r
354{\r
355 LIST_ENTRY *CodeSegmentListHead;\r
356 IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection;\r
357\r
358 CodeSegmentListHead = &ImageRecord->CodeSegmentList;\r
359 while (!IsListEmpty (CodeSegmentListHead)) {\r
360 ImageRecordCodeSection = CR (\r
361 CodeSegmentListHead->ForwardLink,\r
362 IMAGE_PROPERTIES_RECORD_CODE_SECTION,\r
363 Link,\r
364 IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE\r
365 );\r
366 RemoveEntryList (&ImageRecordCodeSection->Link);\r
367 FreePool (ImageRecordCodeSection);\r
368 }\r
369\r
370 if (ImageRecord->Link.ForwardLink != NULL) {\r
371 RemoveEntryList (&ImageRecord->Link);\r
372 }\r
373 FreePool (ImageRecord);\r
374}\r
375\r
376/**\r
13425af7 377 Protect UEFI PE/COFF image.\r
d0e92aad
JY
378\r
379 @param[in] LoadedImage The loaded image protocol\r
380 @param[in] LoadedImageDevicePath The loaded image device path protocol\r
d0e92aad
JY
381**/\r
382VOID\r
5920a9d1 383ProtectUefiImage (\r
d0e92aad 384 IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage,\r
5920a9d1 385 IN EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath\r
d0e92aad
JY
386 )\r
387{\r
388 VOID *ImageAddress;\r
389 EFI_IMAGE_DOS_HEADER *DosHdr;\r
390 UINT32 PeCoffHeaderOffset;\r
391 UINT32 SectionAlignment;\r
392 EFI_IMAGE_SECTION_HEADER *Section;\r
393 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;\r
394 UINT8 *Name;\r
395 UINTN Index;\r
396 IMAGE_PROPERTIES_RECORD *ImageRecord;\r
397 CHAR8 *PdbPointer;\r
398 IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection;\r
d0e92aad
JY
399 BOOLEAN IsAligned;\r
400 UINT32 ProtectionPolicy;\r
401\r
402 DEBUG ((DEBUG_INFO, "ProtectUefiImageCommon - 0x%x\n", LoadedImage));\r
403 DEBUG ((DEBUG_INFO, " - 0x%016lx - 0x%016lx\n", (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase, LoadedImage->ImageSize));\r
404\r
405 if (gCpu == NULL) {\r
406 return ;\r
407 }\r
408\r
409 ProtectionPolicy = GetUefiImageProtectionPolicy (LoadedImage, LoadedImageDevicePath);\r
410 switch (ProtectionPolicy) {\r
411 case DO_NOT_PROTECT:\r
412 return ;\r
413 case PROTECT_IF_ALIGNED_ELSE_ALLOW:\r
414 break;\r
415 default:\r
416 ASSERT(FALSE);\r
417 return ;\r
418 }\r
419\r
420 ImageRecord = AllocateZeroPool (sizeof(*ImageRecord));\r
421 if (ImageRecord == NULL) {\r
422 return ;\r
423 }\r
424 ImageRecord->Signature = IMAGE_PROPERTIES_RECORD_SIGNATURE;\r
425\r
426 //\r
427 // Step 1: record whole region\r
428 //\r
429 ImageRecord->ImageBase = (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase;\r
430 ImageRecord->ImageSize = LoadedImage->ImageSize;\r
431\r
432 ImageAddress = LoadedImage->ImageBase;\r
433\r
434 PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);\r
435 if (PdbPointer != NULL) {\r
436 DEBUG ((DEBUG_VERBOSE, " Image - %a\n", PdbPointer));\r
437 }\r
438\r
439 //\r
440 // Check PE/COFF image\r
441 //\r
442 DosHdr = (EFI_IMAGE_DOS_HEADER *) (UINTN) ImageAddress;\r
443 PeCoffHeaderOffset = 0;\r
444 if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {\r
445 PeCoffHeaderOffset = DosHdr->e_lfanew;\r
446 }\r
447\r
448 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset);\r
449 if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {\r
450 DEBUG ((DEBUG_VERBOSE, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr.Pe32->Signature));\r
451 // It might be image in SMM.\r
452 goto Finish;\r
453 }\r
454\r
455 //\r
456 // Get SectionAlignment\r
457 //\r
05cfd73f 458 if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
d0e92aad
JY
459 SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment;\r
460 } else {\r
461 SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;\r
462 }\r
463\r
464 IsAligned = IsMemoryProtectionSectionAligned (SectionAlignment, LoadedImage->ImageCodeType);\r
465 if (!IsAligned) {\r
466 DEBUG ((DEBUG_VERBOSE, "!!!!!!!! ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect !!!!!!!!\n",\r
467 SectionAlignment));\r
468 PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);\r
469 if (PdbPointer != NULL) {\r
470 DEBUG ((DEBUG_VERBOSE, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer));\r
471 }\r
472 goto Finish;\r
473 }\r
474\r
475 Section = (EFI_IMAGE_SECTION_HEADER *) (\r
476 (UINT8 *) (UINTN) ImageAddress +\r
477 PeCoffHeaderOffset +\r
478 sizeof(UINT32) +\r
479 sizeof(EFI_IMAGE_FILE_HEADER) +\r
480 Hdr.Pe32->FileHeader.SizeOfOptionalHeader\r
481 );\r
482 ImageRecord->CodeSegmentCount = 0;\r
483 InitializeListHead (&ImageRecord->CodeSegmentList);\r
484 for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {\r
485 Name = Section[Index].Name;\r
486 DEBUG ((\r
487 DEBUG_VERBOSE,\r
488 " Section - '%c%c%c%c%c%c%c%c'\n",\r
489 Name[0],\r
490 Name[1],\r
491 Name[2],\r
492 Name[3],\r
493 Name[4],\r
494 Name[5],\r
495 Name[6],\r
496 Name[7]\r
497 ));\r
498\r
a2ed40c0
AB
499 //\r
500 // Instead of assuming that a PE/COFF section of type EFI_IMAGE_SCN_CNT_CODE\r
501 // can always be mapped read-only, classify a section as a code section only\r
502 // if it has the executable attribute set and the writable attribute cleared.\r
503 //\r
504 // This adheres more closely to the PE/COFF spec, and avoids issues with\r
505 // Linux OS loaders that may consist of a single read/write/execute section.\r
506 //\r
507 if ((Section[Index].Characteristics & (EFI_IMAGE_SCN_MEM_WRITE | EFI_IMAGE_SCN_MEM_EXECUTE)) == EFI_IMAGE_SCN_MEM_EXECUTE) {\r
d0e92aad
JY
508 DEBUG ((DEBUG_VERBOSE, " VirtualSize - 0x%08x\n", Section[Index].Misc.VirtualSize));\r
509 DEBUG ((DEBUG_VERBOSE, " VirtualAddress - 0x%08x\n", Section[Index].VirtualAddress));\r
510 DEBUG ((DEBUG_VERBOSE, " SizeOfRawData - 0x%08x\n", Section[Index].SizeOfRawData));\r
511 DEBUG ((DEBUG_VERBOSE, " PointerToRawData - 0x%08x\n", Section[Index].PointerToRawData));\r
512 DEBUG ((DEBUG_VERBOSE, " PointerToRelocations - 0x%08x\n", Section[Index].PointerToRelocations));\r
513 DEBUG ((DEBUG_VERBOSE, " PointerToLinenumbers - 0x%08x\n", Section[Index].PointerToLinenumbers));\r
514 DEBUG ((DEBUG_VERBOSE, " NumberOfRelocations - 0x%08x\n", Section[Index].NumberOfRelocations));\r
515 DEBUG ((DEBUG_VERBOSE, " NumberOfLinenumbers - 0x%08x\n", Section[Index].NumberOfLinenumbers));\r
516 DEBUG ((DEBUG_VERBOSE, " Characteristics - 0x%08x\n", Section[Index].Characteristics));\r
517\r
518 //\r
519 // Step 2: record code section\r
520 //\r
521 ImageRecordCodeSection = AllocatePool (sizeof(*ImageRecordCodeSection));\r
522 if (ImageRecordCodeSection == NULL) {\r
523 return ;\r
524 }\r
525 ImageRecordCodeSection->Signature = IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE;\r
526\r
527 ImageRecordCodeSection->CodeSegmentBase = (UINTN)ImageAddress + Section[Index].VirtualAddress;\r
528 ImageRecordCodeSection->CodeSegmentSize = ALIGN_VALUE(Section[Index].SizeOfRawData, SectionAlignment);\r
529\r
530 DEBUG ((DEBUG_VERBOSE, "ImageCode: 0x%016lx - 0x%016lx\n", ImageRecordCodeSection->CodeSegmentBase, ImageRecordCodeSection->CodeSegmentSize));\r
531\r
532 InsertTailList (&ImageRecord->CodeSegmentList, &ImageRecordCodeSection->Link);\r
533 ImageRecord->CodeSegmentCount++;\r
534 }\r
535 }\r
536\r
537 if (ImageRecord->CodeSegmentCount == 0) {\r
38b15ebe
LE
538 //\r
539 // If a UEFI executable consists of a single read+write+exec PE/COFF\r
540 // section, that isn't actually an error. The image can be launched\r
541 // alright, only image protection cannot be applied to it fully.\r
542 //\r
543 // One example that elicits this is (some) Linux kernels (with the EFI stub\r
544 // of course).\r
545 //\r
546 DEBUG ((DEBUG_WARN, "!!!!!!!! ProtectUefiImageCommon - CodeSegmentCount is 0 !!!!!!!!\n"));\r
d0e92aad
JY
547 PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);\r
548 if (PdbPointer != NULL) {\r
38b15ebe 549 DEBUG ((DEBUG_WARN, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer));\r
d0e92aad
JY
550 }\r
551 goto Finish;\r
552 }\r
553\r
554 //\r
555 // Final\r
556 //\r
557 SortImageRecordCodeSection (ImageRecord);\r
558 //\r
559 // Check overlap all section in ImageBase/Size\r
560 //\r
561 if (!IsImageRecordCodeSectionValid (ImageRecord)) {\r
562 DEBUG ((DEBUG_ERROR, "IsImageRecordCodeSectionValid - FAIL\n"));\r
563 goto Finish;\r
564 }\r
565\r
566 //\r
567 // Round up the ImageSize, some CPU arch may return EFI_UNSUPPORTED if ImageSize is not aligned.\r
568 // Given that the loader always allocates full pages, we know the space after the image is not used.\r
569 //\r
570 ImageRecord->ImageSize = ALIGN_VALUE(LoadedImage->ImageSize, EFI_PAGE_SIZE);\r
571\r
572 //\r
573 // CPU ARCH present. Update memory attribute directly.\r
574 //\r
5920a9d1 575 SetUefiImageProtectionAttributes (ImageRecord);\r
d0e92aad
JY
576\r
577 //\r
5920a9d1 578 // Record the image record in the list so we can undo the protections later\r
d0e92aad 579 //\r
5920a9d1 580 InsertTailList (&mProtectedImageRecordList, &ImageRecord->Link);\r
d0e92aad
JY
581\r
582Finish:\r
583 return ;\r
584}\r
585\r
d0e92aad
JY
586/**\r
587 Unprotect UEFI image.\r
588\r
589 @param[in] LoadedImage The loaded image protocol\r
590 @param[in] LoadedImageDevicePath The loaded image device path protocol\r
591**/\r
592VOID\r
593UnprotectUefiImage (\r
594 IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage,\r
595 IN EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath\r
596 )\r
597{\r
5920a9d1
AB
598 IMAGE_PROPERTIES_RECORD *ImageRecord;\r
599 LIST_ENTRY *ImageRecordLink;\r
600\r
d0e92aad 601 if (PcdGet32(PcdImageProtectionPolicy) != 0) {\r
5920a9d1
AB
602 for (ImageRecordLink = mProtectedImageRecordList.ForwardLink;\r
603 ImageRecordLink != &mProtectedImageRecordList;\r
604 ImageRecordLink = ImageRecordLink->ForwardLink) {\r
605 ImageRecord = CR (\r
606 ImageRecordLink,\r
607 IMAGE_PROPERTIES_RECORD,\r
608 Link,\r
609 IMAGE_PROPERTIES_RECORD_SIGNATURE\r
610 );\r
611\r
612 if (ImageRecord->ImageBase == (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase) {\r
613 SetUefiImageMemoryAttributes (ImageRecord->ImageBase,\r
614 ImageRecord->ImageSize,\r
615 0);\r
616 FreeImageRecord (ImageRecord);\r
617 return;\r
618 }\r
619 }\r
d0e92aad
JY
620 }\r
621}\r
622\r
7eb927db
AB
623/**\r
624 Return the EFI memory permission attribute associated with memory\r
625 type 'MemoryType' under the configured DXE memory protection policy.\r
7babb437
BD
626\r
627 @param MemoryType Memory type.\r
7eb927db
AB
628**/\r
629STATIC\r
630UINT64\r
631GetPermissionAttributeForMemoryType (\r
632 IN EFI_MEMORY_TYPE MemoryType\r
633 )\r
634{\r
635 UINT64 TestBit;\r
636\r
637 if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) {\r
638 TestBit = BIT63;\r
639 } else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {\r
640 TestBit = BIT62;\r
641 } else {\r
642 TestBit = LShiftU64 (1, MemoryType);\r
643 }\r
644\r
645 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & TestBit) != 0) {\r
646 return EFI_MEMORY_XP;\r
647 } else {\r
648 return 0;\r
649 }\r
650}\r
651\r
652/**\r
653 Sort memory map entries based upon PhysicalStart, from low to high.\r
654\r
655 @param MemoryMap A pointer to the buffer in which firmware places\r
656 the current memory map.\r
657 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.\r
658 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.\r
659**/\r
660STATIC\r
661VOID\r
662SortMemoryMap (\r
663 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,\r
664 IN UINTN MemoryMapSize,\r
665 IN UINTN DescriptorSize\r
666 )\r
667{\r
668 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;\r
669 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;\r
670 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;\r
671 EFI_MEMORY_DESCRIPTOR TempMemoryMap;\r
672\r
673 MemoryMapEntry = MemoryMap;\r
674 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
675 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);\r
676 while (MemoryMapEntry < MemoryMapEnd) {\r
677 while (NextMemoryMapEntry < MemoryMapEnd) {\r
678 if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {\r
679 CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));\r
680 CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));\r
681 CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR));\r
682 }\r
683\r
684 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
685 }\r
686\r
687 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
688 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
689 }\r
690}\r
691\r
692/**\r
693 Merge adjacent memory map entries if they use the same memory protection policy\r
694\r
695 @param[in, out] MemoryMap A pointer to the buffer in which firmware places\r
696 the current memory map.\r
697 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the\r
698 MemoryMap buffer. On input, this is the size of\r
699 the current memory map. On output,\r
700 it is the size of new memory map after merge.\r
701 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.\r
702**/\r
703STATIC\r
704VOID\r
705MergeMemoryMapForProtectionPolicy (\r
706 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,\r
707 IN OUT UINTN *MemoryMapSize,\r
708 IN UINTN DescriptorSize\r
709 )\r
710{\r
711 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;\r
712 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;\r
713 UINT64 MemoryBlockLength;\r
714 EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry;\r
715 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;\r
716 UINT64 Attributes;\r
717\r
718 SortMemoryMap (MemoryMap, *MemoryMapSize, DescriptorSize);\r
719\r
720 MemoryMapEntry = MemoryMap;\r
721 NewMemoryMapEntry = MemoryMap;\r
722 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize);\r
723 while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {\r
724 CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));\r
725 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
726\r
727 do {\r
728 MemoryBlockLength = (UINT64) (EFI_PAGES_TO_SIZE((UINTN)MemoryMapEntry->NumberOfPages));\r
729 Attributes = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);\r
730\r
731 if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&\r
732 Attributes == GetPermissionAttributeForMemoryType (NextMemoryMapEntry->Type) &&\r
733 ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) {\r
734 MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;\r
735 if (NewMemoryMapEntry != MemoryMapEntry) {\r
736 NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;\r
737 }\r
738\r
739 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
740 continue;\r
741 } else {\r
742 MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
743 break;\r
744 }\r
745 } while (TRUE);\r
746\r
747 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
748 NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);\r
749 }\r
750\r
751 *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;\r
752\r
753 return ;\r
754}\r
755\r
756\r
757/**\r
758 Remove exec permissions from all regions whose type is identified by\r
7babb437 759 PcdDxeNxMemoryProtectionPolicy.\r
7eb927db
AB
760**/\r
761STATIC\r
762VOID\r
763InitializeDxeNxMemoryProtectionPolicy (\r
764 VOID\r
765 )\r
766{\r
767 UINTN MemoryMapSize;\r
768 UINTN MapKey;\r
769 UINTN DescriptorSize;\r
770 UINT32 DescriptorVersion;\r
771 EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
772 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;\r
773 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;\r
774 EFI_STATUS Status;\r
775 UINT64 Attributes;\r
776 LIST_ENTRY *Link;\r
777 EFI_GCD_MAP_ENTRY *Entry;\r
7db1458f
HW
778 EFI_PEI_HOB_POINTERS Hob;\r
779 EFI_HOB_MEMORY_ALLOCATION *MemoryHob;\r
780 EFI_PHYSICAL_ADDRESS StackBase;\r
7eb927db
AB
781\r
782 //\r
783 // Get the EFI memory map.\r
784 //\r
785 MemoryMapSize = 0;\r
786 MemoryMap = NULL;\r
787\r
788 Status = gBS->GetMemoryMap (\r
789 &MemoryMapSize,\r
790 MemoryMap,\r
791 &MapKey,\r
792 &DescriptorSize,\r
793 &DescriptorVersion\r
794 );\r
795 ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
796 do {\r
797 MemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (MemoryMapSize);\r
798 ASSERT (MemoryMap != NULL);\r
799 Status = gBS->GetMemoryMap (\r
800 &MemoryMapSize,\r
801 MemoryMap,\r
802 &MapKey,\r
803 &DescriptorSize,\r
804 &DescriptorVersion\r
805 );\r
806 if (EFI_ERROR (Status)) {\r
807 FreePool (MemoryMap);\r
808 }\r
809 } while (Status == EFI_BUFFER_TOO_SMALL);\r
810 ASSERT_EFI_ERROR (Status);\r
811\r
7db1458f
HW
812 StackBase = 0;\r
813 if (PcdGetBool (PcdCpuStackGuard)) {\r
814 //\r
815 // Get the base of stack from Hob.\r
816 //\r
817 Hob.Raw = GetHobList ();\r
818 while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) {\r
819 MemoryHob = Hob.MemoryAllocation;\r
820 if (CompareGuid(&gEfiHobMemoryAllocStackGuid, &MemoryHob->AllocDescriptor.Name)) {\r
821 DEBUG ((\r
822 DEBUG_INFO,\r
823 "%a: StackBase = 0x%016lx StackSize = 0x%016lx\n",\r
824 __FUNCTION__,\r
825 MemoryHob->AllocDescriptor.MemoryBaseAddress,\r
826 MemoryHob->AllocDescriptor.MemoryLength\r
827 ));\r
828\r
829 StackBase = MemoryHob->AllocDescriptor.MemoryBaseAddress;\r
830 //\r
831 // Ensure the base of the stack is page-size aligned.\r
832 //\r
833 ASSERT ((StackBase & EFI_PAGE_MASK) == 0);\r
834 break;\r
835 }\r
836 Hob.Raw = GET_NEXT_HOB (Hob);\r
837 }\r
838\r
839 //\r
840 // Ensure the base of stack can be found from Hob when stack guard is\r
841 // enabled.\r
842 //\r
843 ASSERT (StackBase != 0);\r
844 }\r
845\r
a9212288
LE
846 DEBUG ((\r
847 DEBUG_INFO,\r
848 "%a: applying strict permissions to active memory regions\n",\r
849 __FUNCTION__\r
850 ));\r
7eb927db
AB
851\r
852 MergeMemoryMapForProtectionPolicy (MemoryMap, &MemoryMapSize, DescriptorSize);\r
853\r
854 MemoryMapEntry = MemoryMap;\r
855 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);\r
856 while ((UINTN) MemoryMapEntry < (UINTN) MemoryMapEnd) {\r
857\r
858 Attributes = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);\r
859 if (Attributes != 0) {\r
a10acf0e
HW
860 SetUefiImageMemoryAttributes (\r
861 MemoryMapEntry->PhysicalStart,\r
862 LShiftU64 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SHIFT),\r
863 Attributes);\r
864\r
7db1458f
HW
865 //\r
866 // Add EFI_MEMORY_RP attribute for page 0 if NULL pointer detection is\r
867 // enabled.\r
868 //\r
9b17c651
JW
869 if (MemoryMapEntry->PhysicalStart == 0 &&\r
870 PcdGet8 (PcdNullPointerDetectionPropertyMask) != 0) {\r
871\r
872 ASSERT (MemoryMapEntry->NumberOfPages > 0);\r
9b17c651 873 SetUefiImageMemoryAttributes (\r
a10acf0e
HW
874 0,\r
875 EFI_PAGES_TO_SIZE (1),\r
876 EFI_MEMORY_RP | Attributes);\r
9b17c651 877 }\r
7db1458f
HW
878\r
879 //\r
880 // Add EFI_MEMORY_RP attribute for the first page of the stack if stack\r
881 // guard is enabled.\r
882 //\r
883 if (StackBase != 0 &&\r
884 (StackBase >= MemoryMapEntry->PhysicalStart &&\r
885 StackBase < MemoryMapEntry->PhysicalStart +\r
886 LShiftU64 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SHIFT)) &&\r
887 PcdGetBool (PcdCpuStackGuard)) {\r
888\r
889 SetUefiImageMemoryAttributes (\r
890 StackBase,\r
891 EFI_PAGES_TO_SIZE (1),\r
892 EFI_MEMORY_RP | Attributes);\r
893 }\r
894\r
7eb927db
AB
895 }\r
896 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
897 }\r
898 FreePool (MemoryMap);\r
899\r
900 //\r
901 // Apply the policy for RAM regions that we know are present and\r
902 // accessible, but have not been added to the UEFI memory map (yet).\r
903 //\r
904 if (GetPermissionAttributeForMemoryType (EfiConventionalMemory) != 0) {\r
a9212288
LE
905 DEBUG ((\r
906 DEBUG_INFO,\r
7eb927db 907 "%a: applying strict permissions to inactive memory regions\n",\r
a9212288
LE
908 __FUNCTION__\r
909 ));\r
7eb927db
AB
910\r
911 CoreAcquireGcdMemoryLock ();\r
912\r
913 Link = mGcdMemorySpaceMap.ForwardLink;\r
914 while (Link != &mGcdMemorySpaceMap) {\r
915\r
916 Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);\r
917\r
918 if (Entry->GcdMemoryType == EfiGcdMemoryTypeReserved &&\r
919 Entry->EndAddress < MAX_ADDRESS &&\r
920 (Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==\r
921 (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)) {\r
922\r
923 Attributes = GetPermissionAttributeForMemoryType (EfiConventionalMemory) |\r
3bd5c994 924 (Entry->Attributes & EFI_CACHE_ATTRIBUTE_MASK);\r
7eb927db
AB
925\r
926 DEBUG ((DEBUG_INFO,\r
927 "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",\r
928 Entry->BaseAddress, Entry->EndAddress - Entry->BaseAddress + 1,\r
929 Attributes));\r
930\r
931 ASSERT(gCpu != NULL);\r
932 gCpu->SetMemoryAttributes (gCpu, Entry->BaseAddress,\r
933 Entry->EndAddress - Entry->BaseAddress + 1, Attributes);\r
934 }\r
935\r
936 Link = Link->ForwardLink;\r
937 }\r
938 CoreReleaseGcdMemoryLock ();\r
939 }\r
940}\r
941\r
942\r
d0e92aad
JY
943/**\r
944 A notification for CPU_ARCH protocol.\r
945\r
946 @param[in] Event Event whose notification function is being invoked.\r
947 @param[in] Context Pointer to the notification function's context,\r
948 which is implementation-dependent.\r
949\r
950**/\r
951VOID\r
952EFIAPI\r
953MemoryProtectionCpuArchProtocolNotify (\r
954 IN EFI_EVENT Event,\r
955 IN VOID *Context\r
956 )\r
957{\r
958 EFI_STATUS Status;\r
959 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
960 EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath;\r
961 UINTN NoHandles;\r
962 EFI_HANDLE *HandleBuffer;\r
963 UINTN Index;\r
964\r
965 DEBUG ((DEBUG_INFO, "MemoryProtectionCpuArchProtocolNotify:\n"));\r
966 Status = CoreLocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gCpu);\r
967 if (EFI_ERROR (Status)) {\r
8a7ff892 968 goto Done;\r
d0e92aad
JY
969 }\r
970\r
7eb927db
AB
971 //\r
972 // Apply the memory protection policy on non-BScode/RTcode regions.\r
973 //\r
974 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0) {\r
975 InitializeDxeNxMemoryProtectionPolicy ();\r
976 }\r
977\r
7fef06af
JW
978 //\r
979 // Call notify function meant for Heap Guard.\r
980 //\r
981 HeapGuardCpuArchProtocolNotify ();\r
982\r
7eb927db 983 if (mImageProtectionPolicy == 0) {\r
8a7ff892 984 goto Done;\r
7eb927db
AB
985 }\r
986\r
d0e92aad
JY
987 Status = gBS->LocateHandleBuffer (\r
988 ByProtocol,\r
989 &gEfiLoadedImageProtocolGuid,\r
990 NULL,\r
991 &NoHandles,\r
992 &HandleBuffer\r
993 );\r
994 if (EFI_ERROR (Status) && (NoHandles == 0)) {\r
8a7ff892 995 goto Done;\r
d0e92aad
JY
996 }\r
997\r
998 for (Index = 0; Index < NoHandles; Index++) {\r
999 Status = gBS->HandleProtocol (\r
1000 HandleBuffer[Index],\r
1001 &gEfiLoadedImageProtocolGuid,\r
1002 (VOID **)&LoadedImage\r
1003 );\r
1004 if (EFI_ERROR(Status)) {\r
1005 continue;\r
1006 }\r
1007 Status = gBS->HandleProtocol (\r
1008 HandleBuffer[Index],\r
1009 &gEfiLoadedImageDevicePathProtocolGuid,\r
1010 (VOID **)&LoadedImageDevicePath\r
1011 );\r
1012 if (EFI_ERROR(Status)) {\r
1013 LoadedImageDevicePath = NULL;\r
1014 }\r
1015\r
1016 ProtectUefiImage (LoadedImage, LoadedImageDevicePath);\r
1017 }\r
8a7ff892 1018 FreePool (HandleBuffer);\r
d0e92aad 1019\r
8a7ff892 1020Done:\r
d0e92aad 1021 CoreCloseEvent (Event);\r
d0e92aad
JY
1022}\r
1023\r
1024/**\r
1025 ExitBootServices Callback function for memory protection.\r
1026**/\r
1027VOID\r
1028MemoryProtectionExitBootServicesCallback (\r
1029 VOID\r
1030 )\r
1031{\r
1032 EFI_RUNTIME_IMAGE_ENTRY *RuntimeImage;\r
1033 LIST_ENTRY *Link;\r
1034\r
1035 //\r
1036 // We need remove the RT protection, because RT relocation need write code segment\r
1037 // at SetVirtualAddressMap(). We cannot assume OS/Loader has taken over page table at that time.\r
1038 //\r
1039 // Firmware does not own page tables after ExitBootServices(), so the OS would\r
1040 // have to relax protection of RT code pages across SetVirtualAddressMap(), or\r
1041 // delay setting protections on RT code pages until after SetVirtualAddressMap().\r
1042 // OS may set protection on RT based upon EFI_MEMORY_ATTRIBUTES_TABLE later.\r
1043 //\r
1044 if (mImageProtectionPolicy != 0) {\r
1045 for (Link = gRuntime->ImageHead.ForwardLink; Link != &gRuntime->ImageHead; Link = Link->ForwardLink) {\r
1046 RuntimeImage = BASE_CR (Link, EFI_RUNTIME_IMAGE_ENTRY, Link);\r
1047 SetUefiImageMemoryAttributes ((UINT64)(UINTN)RuntimeImage->ImageBase, ALIGN_VALUE(RuntimeImage->ImageSize, EFI_PAGE_SIZE), 0);\r
1048 }\r
1049 }\r
1050}\r
1051\r
a7181d95
JW
1052/**\r
1053 Disable NULL pointer detection after EndOfDxe. This is a workaround resort in\r
1054 order to skip unfixable NULL pointer access issues detected in OptionROM or\r
1055 boot loaders.\r
1056\r
1057 @param[in] Event The Event this notify function registered to.\r
1058 @param[in] Context Pointer to the context data registered to the Event.\r
1059**/\r
1060VOID\r
1061EFIAPI\r
1062DisableNullDetectionAtTheEndOfDxe (\r
1063 EFI_EVENT Event,\r
1064 VOID *Context\r
1065 )\r
1066{\r
1067 EFI_STATUS Status;\r
1068 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Desc;\r
1069\r
1070 DEBUG ((DEBUG_INFO, "DisableNullDetectionAtTheEndOfDxe(): start\r\n"));\r
1071 //\r
1072 // Disable NULL pointer detection by enabling first 4K page\r
1073 //\r
1074 Status = CoreGetMemorySpaceDescriptor (0, &Desc);\r
1075 ASSERT_EFI_ERROR (Status);\r
1076\r
1077 if ((Desc.Capabilities & EFI_MEMORY_RP) == 0) {\r
1078 Status = CoreSetMemorySpaceCapabilities (\r
1079 0,\r
1080 EFI_PAGE_SIZE,\r
1081 Desc.Capabilities | EFI_MEMORY_RP\r
1082 );\r
1083 ASSERT_EFI_ERROR (Status);\r
1084 }\r
1085\r
1086 Status = CoreSetMemorySpaceAttributes (\r
1087 0,\r
1088 EFI_PAGE_SIZE,\r
1089 Desc.Attributes & ~EFI_MEMORY_RP\r
1090 );\r
1091 ASSERT_EFI_ERROR (Status);\r
1092\r
fb92fe9e
JW
1093 //\r
1094 // Page 0 might have be allocated to avoid misuses. Free it here anyway.\r
1095 //\r
1096 CoreFreePages (0, 1);\r
1097\r
a7181d95
JW
1098 CoreCloseEvent (Event);\r
1099 DEBUG ((DEBUG_INFO, "DisableNullDetectionAtTheEndOfDxe(): end\r\n"));\r
1100\r
1101 return;\r
1102}\r
1103\r
d0e92aad
JY
1104/**\r
1105 Initialize Memory Protection support.\r
1106**/\r
1107VOID\r
1108EFIAPI\r
1109CoreInitializeMemoryProtection (\r
1110 VOID\r
1111 )\r
1112{\r
1113 EFI_STATUS Status;\r
1114 EFI_EVENT Event;\r
a7181d95 1115 EFI_EVENT EndOfDxeEvent;\r
d0e92aad
JY
1116 VOID *Registration;\r
1117\r
1118 mImageProtectionPolicy = PcdGet32(PcdImageProtectionPolicy);\r
1119\r
5920a9d1
AB
1120 InitializeListHead (&mProtectedImageRecordList);\r
1121\r
7eb927db
AB
1122 //\r
1123 // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:\r
1124 // - code regions should have no EFI_MEMORY_XP attribute\r
1125 // - EfiConventionalMemory and EfiBootServicesData should use the\r
1126 // same attribute\r
1127 //\r
1128 ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode) & EFI_MEMORY_XP) == 0);\r
1129 ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode) & EFI_MEMORY_XP) == 0);\r
1130 ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode) & EFI_MEMORY_XP) == 0);\r
1131 ASSERT (GetPermissionAttributeForMemoryType (EfiBootServicesData) ==\r
1132 GetPermissionAttributeForMemoryType (EfiConventionalMemory));\r
1133\r
0adc6eae
JW
1134 Status = CoreCreateEvent (\r
1135 EVT_NOTIFY_SIGNAL,\r
1136 TPL_CALLBACK,\r
1137 MemoryProtectionCpuArchProtocolNotify,\r
1138 NULL,\r
1139 &Event\r
1140 );\r
1141 ASSERT_EFI_ERROR(Status);\r
d0e92aad 1142\r
0adc6eae
JW
1143 //\r
1144 // Register for protocol notifactions on this event\r
1145 //\r
1146 Status = CoreRegisterProtocolNotify (\r
1147 &gEfiCpuArchProtocolGuid,\r
1148 Event,\r
1149 &Registration\r
1150 );\r
1151 ASSERT_EFI_ERROR(Status);\r
a7181d95
JW
1152\r
1153 //\r
1154 // Register a callback to disable NULL pointer detection at EndOfDxe\r
1155 //\r
1156 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & (BIT0|BIT7))\r
1157 == (BIT0|BIT7)) {\r
1158 Status = CoreCreateEventEx (\r
1159 EVT_NOTIFY_SIGNAL,\r
1160 TPL_NOTIFY,\r
1161 DisableNullDetectionAtTheEndOfDxe,\r
1162 NULL,\r
1163 &gEfiEndOfDxeEventGroupGuid,\r
1164 &EndOfDxeEvent\r
1165 );\r
1166 ASSERT_EFI_ERROR (Status);\r
1167 }\r
1168\r
d0e92aad
JY
1169 return ;\r
1170}\r
7eb927db
AB
1171\r
1172/**\r
7babb437 1173 Returns whether we are currently executing in SMM mode.\r
7eb927db
AB
1174**/\r
1175STATIC\r
1176BOOLEAN\r
1177IsInSmm (\r
1178 VOID\r
1179 )\r
1180{\r
1181 BOOLEAN InSmm;\r
1182\r
1183 InSmm = FALSE;\r
1184 if (gSmmBase2 != NULL) {\r
1185 gSmmBase2->InSmm (gSmmBase2, &InSmm);\r
1186 }\r
1187 return InSmm;\r
1188}\r
1189\r
1190/**\r
1191 Manage memory permission attributes on a memory range, according to the\r
1192 configured DXE memory protection policy.\r
1193\r
1194 @param OldType The old memory type of the range\r
1195 @param NewType The new memory type of the range\r
1196 @param Memory The base address of the range\r
1197 @param Length The size of the range (in bytes)\r
1198\r
1199 @return EFI_SUCCESS If we are executing in SMM mode. No permission attributes\r
1200 are updated in this case\r
1201 @return EFI_SUCCESS If the the CPU arch protocol is not installed yet\r
1202 @return EFI_SUCCESS If no DXE memory protection policy has been configured\r
1203 @return EFI_SUCCESS If OldType and NewType use the same permission attributes\r
1204 @return other Return value of gCpu->SetMemoryAttributes()\r
1205\r
1206**/\r
1207EFI_STATUS\r
1208EFIAPI\r
1209ApplyMemoryProtectionPolicy (\r
1210 IN EFI_MEMORY_TYPE OldType,\r
1211 IN EFI_MEMORY_TYPE NewType,\r
1212 IN EFI_PHYSICAL_ADDRESS Memory,\r
1213 IN UINT64 Length\r
1214 )\r
1215{\r
1216 UINT64 OldAttributes;\r
1217 UINT64 NewAttributes;\r
1218\r
1219 //\r
1220 // The policy configured in PcdDxeNxMemoryProtectionPolicy\r
1221 // does not apply to allocations performed in SMM mode.\r
1222 //\r
1223 if (IsInSmm ()) {\r
1224 return EFI_SUCCESS;\r
1225 }\r
1226\r
1227 //\r
1228 // If the CPU arch protocol is not installed yet, we cannot manage memory\r
1229 // permission attributes, and it is the job of the driver that installs this\r
1230 // protocol to set the permissions on existing allocations.\r
1231 //\r
1232 if (gCpu == NULL) {\r
1233 return EFI_SUCCESS;\r
1234 }\r
1235\r
1236 //\r
1237 // Check if a DXE memory protection policy has been configured\r
1238 //\r
1239 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) == 0) {\r
1240 return EFI_SUCCESS;\r
1241 }\r
1242\r
a6a0a597
JW
1243 //\r
1244 // Don't overwrite Guard pages, which should be the first and/or last page,\r
1245 // if any.\r
1246 //\r
63ebde8e 1247 if (IsHeapGuardEnabled (GUARD_HEAP_TYPE_PAGE|GUARD_HEAP_TYPE_POOL)) {\r
a6a0a597
JW
1248 if (IsGuardPage (Memory)) {\r
1249 Memory += EFI_PAGE_SIZE;\r
1250 Length -= EFI_PAGE_SIZE;\r
1251 if (Length == 0) {\r
1252 return EFI_SUCCESS;\r
1253 }\r
1254 }\r
1255\r
1256 if (IsGuardPage (Memory + Length - EFI_PAGE_SIZE)) {\r
1257 Length -= EFI_PAGE_SIZE;\r
1258 if (Length == 0) {\r
1259 return EFI_SUCCESS;\r
1260 }\r
1261 }\r
1262 }\r
1263\r
7eb927db
AB
1264 //\r
1265 // Update the executable permissions according to the DXE memory\r
1266 // protection policy, but only if\r
1267 // - the policy is different between the old and the new type, or\r
1268 // - this is a newly added region (OldType == EfiMaxMemoryType)\r
1269 //\r
1270 NewAttributes = GetPermissionAttributeForMemoryType (NewType);\r
1271\r
1272 if (OldType != EfiMaxMemoryType) {\r
1273 OldAttributes = GetPermissionAttributeForMemoryType (OldType);\r
1274 if (OldAttributes == NewAttributes) {\r
1275 // policy is the same between OldType and NewType\r
1276 return EFI_SUCCESS;\r
1277 }\r
1278 } else if (NewAttributes == 0) {\r
1279 // newly added region of a type that does not require protection\r
1280 return EFI_SUCCESS;\r
1281 }\r
1282\r
1283 return gCpu->SetMemoryAttributes (gCpu, Memory, Length, NewAttributes);\r
1284}\r