]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
070228bc7265685b911d7f54f258900d1eef7ece
[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 STATIC
662 UINT64
663 GetPermissionAttributeForMemoryType (
664 IN EFI_MEMORY_TYPE MemoryType
665 )
666 {
667 UINT64 TestBit;
668
669 if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) {
670 TestBit = BIT63;
671 } else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {
672 TestBit = BIT62;
673 } else {
674 TestBit = LShiftU64 (1, MemoryType);
675 }
676
677 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & TestBit) != 0) {
678 return EFI_MEMORY_XP;
679 } else {
680 return 0;
681 }
682 }
683
684 /**
685 Sort memory map entries based upon PhysicalStart, from low to high.
686
687 @param MemoryMap A pointer to the buffer in which firmware places
688 the current memory map.
689 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
690 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
691 **/
692 STATIC
693 VOID
694 SortMemoryMap (
695 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
696 IN UINTN MemoryMapSize,
697 IN UINTN DescriptorSize
698 )
699 {
700 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
701 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
702 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
703 EFI_MEMORY_DESCRIPTOR TempMemoryMap;
704
705 MemoryMapEntry = MemoryMap;
706 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
707 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
708 while (MemoryMapEntry < MemoryMapEnd) {
709 while (NextMemoryMapEntry < MemoryMapEnd) {
710 if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {
711 CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
712 CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
713 CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR));
714 }
715
716 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
717 }
718
719 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
720 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
721 }
722 }
723
724 /**
725 Merge adjacent memory map entries if they use the same memory protection policy
726
727 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
728 the current memory map.
729 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
730 MemoryMap buffer. On input, this is the size of
731 the current memory map. On output,
732 it is the size of new memory map after merge.
733 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
734 **/
735 STATIC
736 VOID
737 MergeMemoryMapForProtectionPolicy (
738 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
739 IN OUT UINTN *MemoryMapSize,
740 IN UINTN DescriptorSize
741 )
742 {
743 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
744 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
745 UINT64 MemoryBlockLength;
746 EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry;
747 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
748 UINT64 Attributes;
749
750 SortMemoryMap (MemoryMap, *MemoryMapSize, DescriptorSize);
751
752 MemoryMapEntry = MemoryMap;
753 NewMemoryMapEntry = MemoryMap;
754 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize);
755 while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
756 CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
757 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
758
759 do {
760 MemoryBlockLength = (UINT64) (EFI_PAGES_TO_SIZE((UINTN)MemoryMapEntry->NumberOfPages));
761 Attributes = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);
762
763 if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&
764 Attributes == GetPermissionAttributeForMemoryType (NextMemoryMapEntry->Type) &&
765 ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) {
766 MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
767 if (NewMemoryMapEntry != MemoryMapEntry) {
768 NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
769 }
770
771 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
772 continue;
773 } else {
774 MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
775 break;
776 }
777 } while (TRUE);
778
779 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
780 NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);
781 }
782
783 *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;
784
785 return ;
786 }
787
788
789 /**
790 Remove exec permissions from all regions whose type is identified by
791 PcdDxeNxMemoryProtectionPolicy
792 **/
793 STATIC
794 VOID
795 InitializeDxeNxMemoryProtectionPolicy (
796 VOID
797 )
798 {
799 UINTN MemoryMapSize;
800 UINTN MapKey;
801 UINTN DescriptorSize;
802 UINT32 DescriptorVersion;
803 EFI_MEMORY_DESCRIPTOR *MemoryMap;
804 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
805 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
806 EFI_STATUS Status;
807 UINT64 Attributes;
808 LIST_ENTRY *Link;
809 EFI_GCD_MAP_ENTRY *Entry;
810
811 //
812 // Get the EFI memory map.
813 //
814 MemoryMapSize = 0;
815 MemoryMap = NULL;
816
817 Status = gBS->GetMemoryMap (
818 &MemoryMapSize,
819 MemoryMap,
820 &MapKey,
821 &DescriptorSize,
822 &DescriptorVersion
823 );
824 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
825 do {
826 MemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (MemoryMapSize);
827 ASSERT (MemoryMap != NULL);
828 Status = gBS->GetMemoryMap (
829 &MemoryMapSize,
830 MemoryMap,
831 &MapKey,
832 &DescriptorSize,
833 &DescriptorVersion
834 );
835 if (EFI_ERROR (Status)) {
836 FreePool (MemoryMap);
837 }
838 } while (Status == EFI_BUFFER_TOO_SMALL);
839 ASSERT_EFI_ERROR (Status);
840
841 DEBUG((DEBUG_ERROR, "%a: applying strict permissions to active memory regions\n",
842 __FUNCTION__));
843
844 MergeMemoryMapForProtectionPolicy (MemoryMap, &MemoryMapSize, DescriptorSize);
845
846 MemoryMapEntry = MemoryMap;
847 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
848 while ((UINTN) MemoryMapEntry < (UINTN) MemoryMapEnd) {
849
850 Attributes = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);
851 if (Attributes != 0) {
852 SetUefiImageMemoryAttributes (
853 MemoryMapEntry->PhysicalStart,
854 LShiftU64 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SHIFT),
855 Attributes);
856 }
857 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
858 }
859 FreePool (MemoryMap);
860
861 //
862 // Apply the policy for RAM regions that we know are present and
863 // accessible, but have not been added to the UEFI memory map (yet).
864 //
865 if (GetPermissionAttributeForMemoryType (EfiConventionalMemory) != 0) {
866 DEBUG((DEBUG_ERROR,
867 "%a: applying strict permissions to inactive memory regions\n",
868 __FUNCTION__));
869
870 CoreAcquireGcdMemoryLock ();
871
872 Link = mGcdMemorySpaceMap.ForwardLink;
873 while (Link != &mGcdMemorySpaceMap) {
874
875 Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
876
877 if (Entry->GcdMemoryType == EfiGcdMemoryTypeReserved &&
878 Entry->EndAddress < MAX_ADDRESS &&
879 (Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
880 (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)) {
881
882 Attributes = GetPermissionAttributeForMemoryType (EfiConventionalMemory) |
883 (Entry->Attributes & CACHE_ATTRIBUTE_MASK);
884
885 DEBUG ((DEBUG_INFO,
886 "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",
887 Entry->BaseAddress, Entry->EndAddress - Entry->BaseAddress + 1,
888 Attributes));
889
890 ASSERT(gCpu != NULL);
891 gCpu->SetMemoryAttributes (gCpu, Entry->BaseAddress,
892 Entry->EndAddress - Entry->BaseAddress + 1, Attributes);
893 }
894
895 Link = Link->ForwardLink;
896 }
897 CoreReleaseGcdMemoryLock ();
898 }
899 }
900
901
902 /**
903 A notification for CPU_ARCH protocol.
904
905 @param[in] Event Event whose notification function is being invoked.
906 @param[in] Context Pointer to the notification function's context,
907 which is implementation-dependent.
908
909 **/
910 VOID
911 EFIAPI
912 MemoryProtectionCpuArchProtocolNotify (
913 IN EFI_EVENT Event,
914 IN VOID *Context
915 )
916 {
917 EFI_STATUS Status;
918 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
919 EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath;
920 UINTN NoHandles;
921 EFI_HANDLE *HandleBuffer;
922 UINTN Index;
923
924 DEBUG ((DEBUG_INFO, "MemoryProtectionCpuArchProtocolNotify:\n"));
925 Status = CoreLocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gCpu);
926 if (EFI_ERROR (Status)) {
927 return;
928 }
929
930 //
931 // Apply the memory protection policy on non-BScode/RTcode regions.
932 //
933 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0) {
934 InitializeDxeNxMemoryProtectionPolicy ();
935 }
936
937 if (mImageProtectionPolicy == 0) {
938 return;
939 }
940
941 Status = gBS->LocateHandleBuffer (
942 ByProtocol,
943 &gEfiLoadedImageProtocolGuid,
944 NULL,
945 &NoHandles,
946 &HandleBuffer
947 );
948 if (EFI_ERROR (Status) && (NoHandles == 0)) {
949 return ;
950 }
951
952 for (Index = 0; Index < NoHandles; Index++) {
953 Status = gBS->HandleProtocol (
954 HandleBuffer[Index],
955 &gEfiLoadedImageProtocolGuid,
956 (VOID **)&LoadedImage
957 );
958 if (EFI_ERROR(Status)) {
959 continue;
960 }
961 Status = gBS->HandleProtocol (
962 HandleBuffer[Index],
963 &gEfiLoadedImageDevicePathProtocolGuid,
964 (VOID **)&LoadedImageDevicePath
965 );
966 if (EFI_ERROR(Status)) {
967 LoadedImageDevicePath = NULL;
968 }
969
970 ProtectUefiImage (LoadedImage, LoadedImageDevicePath);
971 }
972
973 CoreCloseEvent (Event);
974 return;
975 }
976
977 /**
978 ExitBootServices Callback function for memory protection.
979 **/
980 VOID
981 MemoryProtectionExitBootServicesCallback (
982 VOID
983 )
984 {
985 EFI_RUNTIME_IMAGE_ENTRY *RuntimeImage;
986 LIST_ENTRY *Link;
987
988 //
989 // We need remove the RT protection, because RT relocation need write code segment
990 // at SetVirtualAddressMap(). We cannot assume OS/Loader has taken over page table at that time.
991 //
992 // Firmware does not own page tables after ExitBootServices(), so the OS would
993 // have to relax protection of RT code pages across SetVirtualAddressMap(), or
994 // delay setting protections on RT code pages until after SetVirtualAddressMap().
995 // OS may set protection on RT based upon EFI_MEMORY_ATTRIBUTES_TABLE later.
996 //
997 if (mImageProtectionPolicy != 0) {
998 for (Link = gRuntime->ImageHead.ForwardLink; Link != &gRuntime->ImageHead; Link = Link->ForwardLink) {
999 RuntimeImage = BASE_CR (Link, EFI_RUNTIME_IMAGE_ENTRY, Link);
1000 SetUefiImageMemoryAttributes ((UINT64)(UINTN)RuntimeImage->ImageBase, ALIGN_VALUE(RuntimeImage->ImageSize, EFI_PAGE_SIZE), 0);
1001 }
1002 }
1003 }
1004
1005 /**
1006 Initialize Memory Protection support.
1007 **/
1008 VOID
1009 EFIAPI
1010 CoreInitializeMemoryProtection (
1011 VOID
1012 )
1013 {
1014 EFI_STATUS Status;
1015 EFI_EVENT Event;
1016 VOID *Registration;
1017
1018 mImageProtectionPolicy = PcdGet32(PcdImageProtectionPolicy);
1019
1020 //
1021 // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
1022 // - code regions should have no EFI_MEMORY_XP attribute
1023 // - EfiConventionalMemory and EfiBootServicesData should use the
1024 // same attribute
1025 //
1026 ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode) & EFI_MEMORY_XP) == 0);
1027 ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode) & EFI_MEMORY_XP) == 0);
1028 ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode) & EFI_MEMORY_XP) == 0);
1029 ASSERT (GetPermissionAttributeForMemoryType (EfiBootServicesData) ==
1030 GetPermissionAttributeForMemoryType (EfiConventionalMemory));
1031
1032 if (mImageProtectionPolicy != 0 || PcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0) {
1033 Status = CoreCreateEvent (
1034 EVT_NOTIFY_SIGNAL,
1035 TPL_CALLBACK,
1036 MemoryProtectionCpuArchProtocolNotify,
1037 NULL,
1038 &Event
1039 );
1040 ASSERT_EFI_ERROR(Status);
1041
1042 //
1043 // Register for protocol notifactions on this event
1044 //
1045 Status = CoreRegisterProtocolNotify (
1046 &gEfiCpuArchProtocolGuid,
1047 Event,
1048 &Registration
1049 );
1050 ASSERT_EFI_ERROR(Status);
1051 }
1052 return ;
1053 }
1054
1055 /**
1056 Returns whether we are currently executing in SMM mode
1057 **/
1058 STATIC
1059 BOOLEAN
1060 IsInSmm (
1061 VOID
1062 )
1063 {
1064 BOOLEAN InSmm;
1065
1066 InSmm = FALSE;
1067 if (gSmmBase2 != NULL) {
1068 gSmmBase2->InSmm (gSmmBase2, &InSmm);
1069 }
1070 return InSmm;
1071 }
1072
1073 /**
1074 Manage memory permission attributes on a memory range, according to the
1075 configured DXE memory protection policy.
1076
1077 @param OldType The old memory type of the range
1078 @param NewType The new memory type of the range
1079 @param Memory The base address of the range
1080 @param Length The size of the range (in bytes)
1081
1082 @return EFI_SUCCESS If we are executing in SMM mode. No permission attributes
1083 are updated in this case
1084 @return EFI_SUCCESS If the the CPU arch protocol is not installed yet
1085 @return EFI_SUCCESS If no DXE memory protection policy has been configured
1086 @return EFI_SUCCESS If OldType and NewType use the same permission attributes
1087 @return other Return value of gCpu->SetMemoryAttributes()
1088
1089 **/
1090 EFI_STATUS
1091 EFIAPI
1092 ApplyMemoryProtectionPolicy (
1093 IN EFI_MEMORY_TYPE OldType,
1094 IN EFI_MEMORY_TYPE NewType,
1095 IN EFI_PHYSICAL_ADDRESS Memory,
1096 IN UINT64 Length
1097 )
1098 {
1099 UINT64 OldAttributes;
1100 UINT64 NewAttributes;
1101
1102 //
1103 // The policy configured in PcdDxeNxMemoryProtectionPolicy
1104 // does not apply to allocations performed in SMM mode.
1105 //
1106 if (IsInSmm ()) {
1107 return EFI_SUCCESS;
1108 }
1109
1110 //
1111 // If the CPU arch protocol is not installed yet, we cannot manage memory
1112 // permission attributes, and it is the job of the driver that installs this
1113 // protocol to set the permissions on existing allocations.
1114 //
1115 if (gCpu == NULL) {
1116 return EFI_SUCCESS;
1117 }
1118
1119 //
1120 // Check if a DXE memory protection policy has been configured
1121 //
1122 if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) == 0) {
1123 return EFI_SUCCESS;
1124 }
1125
1126 //
1127 // Update the executable permissions according to the DXE memory
1128 // protection policy, but only if
1129 // - the policy is different between the old and the new type, or
1130 // - this is a newly added region (OldType == EfiMaxMemoryType)
1131 //
1132 NewAttributes = GetPermissionAttributeForMemoryType (NewType);
1133
1134 if (OldType != EfiMaxMemoryType) {
1135 OldAttributes = GetPermissionAttributeForMemoryType (OldType);
1136 if (OldAttributes == NewAttributes) {
1137 // policy is the same between OldType and NewType
1138 return EFI_SUCCESS;
1139 }
1140 } else if (NewAttributes == 0) {
1141 // newly added region of a type that does not require protection
1142 return EFI_SUCCESS;
1143 }
1144
1145 return gCpu->SetMemoryAttributes (gCpu, Memory, Length, NewAttributes);
1146 }