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