1 /**@file
2 
3 Copyright (c) 2006 - 2009, Intel Corporation. All rights reserved.<BR>
4 Portions copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
5 Portions copyright (c) 2011 - 2018, ARM Ltd. All rights reserved.<BR>
6 
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8 
9 **/
10 
11 #include <PiDxe.h>
12 
13 #include <Library/ArmMmuLib.h>
14 #include <Library/BaseLib.h>
15 #include <Library/BaseMemoryLib.h>
16 #include <Library/DebugLib.h>
17 #include <Library/PcdLib.h>
18 #include <Library/PeCoffLib.h>
19 #include <Library/PeCoffExtraActionLib.h>
20 
21 typedef RETURN_STATUS (*REGION_PERMISSION_UPDATE_FUNC) (
22   IN  EFI_PHYSICAL_ADDRESS      BaseAddress,
23   IN  UINT64                    Length
24   );
25 
26 STATIC
27 RETURN_STATUS
UpdatePeCoffPermissions(IN CONST PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext,IN REGION_PERMISSION_UPDATE_FUNC NoExecUpdater,IN REGION_PERMISSION_UPDATE_FUNC ReadOnlyUpdater)28 UpdatePeCoffPermissions (
29   IN  CONST PE_COFF_LOADER_IMAGE_CONTEXT      *ImageContext,
30   IN  REGION_PERMISSION_UPDATE_FUNC           NoExecUpdater,
31   IN  REGION_PERMISSION_UPDATE_FUNC           ReadOnlyUpdater
32   )
33 {
34   RETURN_STATUS                         Status;
35   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr;
36   EFI_IMAGE_OPTIONAL_HEADER_UNION       HdrData;
37   UINTN                                 Size;
38   UINTN                                 ReadSize;
39   UINT32                                SectionHeaderOffset;
40   UINTN                                 NumberOfSections;
41   UINTN                                 Index;
42   EFI_IMAGE_SECTION_HEADER              SectionHeader;
43   PE_COFF_LOADER_IMAGE_CONTEXT          TmpContext;
44   EFI_PHYSICAL_ADDRESS                  Base;
45 
46   //
47   // We need to copy ImageContext since PeCoffLoaderGetImageInfo ()
48   // will mangle the ImageAddress field
49   //
50   CopyMem (&TmpContext, ImageContext, sizeof (TmpContext));
51 
52   if (TmpContext.PeCoffHeaderOffset == 0) {
53     Status = PeCoffLoaderGetImageInfo (&TmpContext);
54     if (RETURN_ERROR (Status)) {
55       DEBUG ((DEBUG_ERROR,
56         "%a: PeCoffLoaderGetImageInfo () failed (Status = %r)\n",
57         __FUNCTION__, Status));
58       return Status;
59     }
60   }
61 
62   if (TmpContext.IsTeImage &&
63       TmpContext.ImageAddress == ImageContext->ImageAddress) {
64     DEBUG ((DEBUG_INFO, "%a: ignoring XIP TE image at 0x%lx\n", __FUNCTION__,
65       ImageContext->ImageAddress));
66     return RETURN_SUCCESS;
67   }
68 
69   if (TmpContext.SectionAlignment < EFI_PAGE_SIZE) {
70     //
71     // The sections need to be at least 4 KB aligned, since that is the
72     // granularity at which we can tighten permissions. So just clear the
73     // noexec permissions on the entire region.
74     //
75     if (!TmpContext.IsTeImage) {
76       DEBUG ((DEBUG_WARN,
77         "%a: non-TE Image at 0x%lx has SectionAlignment < 4 KB (%lu)\n",
78         __FUNCTION__, ImageContext->ImageAddress, TmpContext.SectionAlignment));
79     }
80     Base = ImageContext->ImageAddress & ~(EFI_PAGE_SIZE - 1);
81     Size = ImageContext->ImageAddress - Base + ImageContext->ImageSize;
82     return NoExecUpdater (Base, ALIGN_VALUE (Size, EFI_PAGE_SIZE));
83   }
84 
85   //
86   // Read the PE/COFF Header. For PE32 (32-bit) this will read in too much
87   // data, but that should not hurt anything. Hdr.Pe32->OptionalHeader.Magic
88   // determines if this is a PE32 or PE32+ image. The magic is in the same
89   // location in both images.
90   //
91   Hdr.Union = &HdrData;
92   Size = sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION);
93   ReadSize = Size;
94   Status = TmpContext.ImageRead (TmpContext.Handle,
95                          TmpContext.PeCoffHeaderOffset, &Size, Hdr.Pe32);
96   if (RETURN_ERROR (Status) || (Size != ReadSize)) {
97     DEBUG ((DEBUG_ERROR,
98       "%a: TmpContext.ImageRead () failed (Status = %r)\n",
99       __FUNCTION__, Status));
100     return Status;
101   }
102 
103   ASSERT (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE);
104 
105   SectionHeaderOffset = TmpContext.PeCoffHeaderOffset + sizeof (UINT32) +
106                         sizeof (EFI_IMAGE_FILE_HEADER);
107   NumberOfSections    = (UINTN)(Hdr.Pe32->FileHeader.NumberOfSections);
108 
109   switch (Hdr.Pe32->OptionalHeader.Magic) {
110     case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC:
111       SectionHeaderOffset += Hdr.Pe32->FileHeader.SizeOfOptionalHeader;
112       break;
113     case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC:
114       SectionHeaderOffset += Hdr.Pe32Plus->FileHeader.SizeOfOptionalHeader;
115       break;
116     default:
117       ASSERT (FALSE);
118   }
119 
120   //
121   // Iterate over the sections
122   //
123   for (Index = 0; Index < NumberOfSections; Index++) {
124     //
125     // Read section header from file
126     //
127     Size = sizeof (EFI_IMAGE_SECTION_HEADER);
128     ReadSize = Size;
129     Status = TmpContext.ImageRead (TmpContext.Handle, SectionHeaderOffset,
130                                    &Size, &SectionHeader);
131     if (RETURN_ERROR (Status) || (Size != ReadSize)) {
132       DEBUG ((DEBUG_ERROR,
133         "%a: TmpContext.ImageRead () failed (Status = %r)\n",
134         __FUNCTION__, Status));
135       return Status;
136     }
137 
138     Base = TmpContext.ImageAddress + SectionHeader.VirtualAddress;
139 
140     if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) == 0) {
141 
142       if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_MEM_WRITE) == 0) {
143 
144         DEBUG ((DEBUG_INFO,
145           "%a: Mapping section %d of image at 0x%lx with RO-XN permissions and size 0x%x\n",
146           __FUNCTION__, Index, Base, SectionHeader.Misc.VirtualSize));
147         ReadOnlyUpdater (Base, SectionHeader.Misc.VirtualSize);
148       } else {
149         DEBUG ((DEBUG_WARN,
150           "%a: Mapping section %d of image at 0x%lx with RW-XN permissions and size 0x%x\n",
151           __FUNCTION__, Index, Base, SectionHeader.Misc.VirtualSize));
152       }
153     } else {
154         DEBUG ((DEBUG_INFO,
155           "%a: Mapping section %d of image at 0x%lx with RO-X permissions and size 0x%x\n",
156           __FUNCTION__, Index, Base, SectionHeader.Misc.VirtualSize));
157         ReadOnlyUpdater (Base, SectionHeader.Misc.VirtualSize);
158         NoExecUpdater (Base, SectionHeader.Misc.VirtualSize);
159     }
160 
161     SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
162   }
163   return RETURN_SUCCESS;
164 }
165 
166 /**
167   Performs additional actions after a PE/COFF image has been loaded and relocated.
168 
169   If ImageContext is NULL, then ASSERT().
170 
171   @param  ImageContext  Pointer to the image context structure that describes the
172                         PE/COFF image that has already been loaded and relocated.
173 
174 **/
175 VOID
176 EFIAPI
PeCoffLoaderRelocateImageExtraAction(IN OUT PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext)177 PeCoffLoaderRelocateImageExtraAction (
178   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
179   )
180 {
181   UpdatePeCoffPermissions (
182     ImageContext,
183     ArmClearMemoryRegionNoExec,
184     ArmSetMemoryRegionReadOnly
185     );
186 }
187 
188 
189 
190 /**
191   Performs additional actions just before a PE/COFF image is unloaded.  Any resources
192   that were allocated by PeCoffLoaderRelocateImageExtraAction() must be freed.
193 
194   If ImageContext is NULL, then ASSERT().
195 
196   @param  ImageContext  Pointer to the image context structure that describes the
197                         PE/COFF image that is being unloaded.
198 
199 **/
200 VOID
201 EFIAPI
PeCoffLoaderUnloadImageExtraAction(IN OUT PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext)202 PeCoffLoaderUnloadImageExtraAction (
203   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
204   )
205 {
206   UpdatePeCoffPermissions (
207     ImageContext,
208     ArmSetMemoryRegionNoExec,
209     ArmClearMemoryRegionReadOnly
210     );
211 }
212