1 /** @file
2 
3   Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
4 
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "UefiPayloadEntry.h"
10 
11 /**
12   Allocate pages for code.
13 
14   @param[in] Pages      Number of pages to be allocated.
15 
16   @return Allocated memory.
17 **/
18 VOID*
AllocateCodePages(IN UINTN Pages)19 AllocateCodePages (
20   IN  UINTN     Pages
21   )
22 {
23   VOID                    *Alloc;
24   EFI_PEI_HOB_POINTERS    Hob;
25 
26   Alloc = AllocatePages (Pages);
27   if (Alloc == NULL) {
28     return NULL;
29   }
30 
31   // find the HOB we just created, and change the type to EfiBootServicesCode
32   Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
33   while (Hob.Raw != NULL) {
34     if (Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress == (UINTN)Alloc) {
35       Hob.MemoryAllocation->AllocDescriptor.MemoryType = EfiBootServicesCode;
36       return Alloc;
37     }
38     Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, GET_NEXT_HOB (Hob));
39   }
40 
41   ASSERT (FALSE);
42 
43   FreePages (Alloc, Pages);
44   return NULL;
45 }
46 
47 
48 /**
49     Loads and relocates a PE/COFF image
50 
51   @param[in]  PeCoffImage     Point to a Pe/Coff image.
52   @param[out]  ImageAddress   The image memory address after relocation.
53   @param[out]  ImageSize      The image size.
54   @param[out]  EntryPoint     The image entry point.
55 
56   @return EFI_SUCCESS    If the image is loaded and relocated successfully.
57   @return Others         If the image failed to load or relocate.
58 **/
59 EFI_STATUS
LoadPeCoffImage(IN VOID * PeCoffImage,OUT EFI_PHYSICAL_ADDRESS * ImageAddress,OUT UINT64 * ImageSize,OUT EFI_PHYSICAL_ADDRESS * EntryPoint)60 LoadPeCoffImage (
61   IN  VOID                          *PeCoffImage,
62   OUT EFI_PHYSICAL_ADDRESS          *ImageAddress,
63   OUT UINT64                        *ImageSize,
64   OUT EFI_PHYSICAL_ADDRESS          *EntryPoint
65   )
66 {
67   RETURN_STATUS                     Status;
68   PE_COFF_LOADER_IMAGE_CONTEXT      ImageContext;
69   VOID                              *Buffer;
70 
71   ZeroMem (&ImageContext, sizeof (ImageContext));
72 
73   ImageContext.Handle    = PeCoffImage;
74   ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
75 
76   Status = PeCoffLoaderGetImageInfo (&ImageContext);
77   if (EFI_ERROR (Status)) {
78     ASSERT_EFI_ERROR (Status);
79     return Status;
80   }
81 
82   //
83   // Allocate Memory for the image
84   //
85   Buffer = AllocateCodePages (EFI_SIZE_TO_PAGES((UINT32)ImageContext.ImageSize));
86   if (Buffer == NULL) {
87     return EFI_OUT_OF_RESOURCES;
88   }
89   ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
90 
91   //
92   // Load the image to our new buffer
93   //
94   Status = PeCoffLoaderLoadImage (&ImageContext);
95   if (EFI_ERROR (Status)) {
96     ASSERT_EFI_ERROR (Status);
97     return Status;
98   }
99 
100   //
101   // Relocate the image in our new buffer
102   //
103   Status = PeCoffLoaderRelocateImage (&ImageContext);
104   if (EFI_ERROR (Status)) {
105     ASSERT_EFI_ERROR (Status);
106     return Status;
107   }
108 
109   *ImageAddress = ImageContext.ImageAddress;
110   *ImageSize    = ImageContext.ImageSize;
111   *EntryPoint   = ImageContext.EntryPoint;
112 
113   return EFI_SUCCESS;
114 }
115 
116 /**
117   This function searchs a given file type within a valid FV.
118 
119   @param FvHeader        A pointer to firmware volume header that contains the set of files
120                          to be searched.
121   @param FileType        File type to be searched.
122   @param FileHeader      A pointer to the discovered file, if successful.
123 
124   @retval EFI_SUCCESS    Successfully found FileType
125   @retval EFI_NOT_FOUND  File type can't be found.
126 **/
127 EFI_STATUS
FvFindFile(IN EFI_FIRMWARE_VOLUME_HEADER * FvHeader,IN EFI_FV_FILETYPE FileType,OUT EFI_FFS_FILE_HEADER ** FileHeader)128 FvFindFile (
129   IN  EFI_FIRMWARE_VOLUME_HEADER  *FvHeader,
130   IN  EFI_FV_FILETYPE             FileType,
131   OUT EFI_FFS_FILE_HEADER         **FileHeader
132   )
133 {
134   EFI_PHYSICAL_ADDRESS        CurrentAddress;
135   EFI_PHYSICAL_ADDRESS        EndOfFirmwareVolume;
136   EFI_FFS_FILE_HEADER         *File;
137   UINT32                      Size;
138   EFI_PHYSICAL_ADDRESS        EndOfFile;
139 
140   CurrentAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) FvHeader;
141   EndOfFirmwareVolume = CurrentAddress + FvHeader->FvLength;
142 
143   //
144   // Loop through the FFS files
145   //
146   for (EndOfFile = CurrentAddress + FvHeader->HeaderLength; ; ) {
147     CurrentAddress = (EndOfFile + 7) & 0xfffffffffffffff8ULL;
148     if (CurrentAddress > EndOfFirmwareVolume) {
149       break;
150     }
151 
152     File = (EFI_FFS_FILE_HEADER*)(UINTN) CurrentAddress;
153     if (IS_FFS_FILE2 (File)) {
154       Size = FFS_FILE2_SIZE (File);
155       if (Size <= 0x00FFFFFF) {
156         break;
157       }
158     } else {
159       Size = FFS_FILE_SIZE (File);
160       if (Size < sizeof (EFI_FFS_FILE_HEADER)) {
161         break;
162       }
163     }
164 
165     EndOfFile = CurrentAddress + Size;
166     if (EndOfFile > EndOfFirmwareVolume) {
167       break;
168     }
169 
170     //
171     // Look for file type
172     //
173     if (File->Type == FileType) {
174       *FileHeader = File;
175       return EFI_SUCCESS;
176     }
177   }
178 
179   return EFI_NOT_FOUND;
180 }
181 
182 
183 /**
184   This function searchs a given section type within a valid FFS file.
185 
186   @param  FileHeader            A pointer to the file header that contains the set of sections to
187                                 be searched.
188   @param  SearchType            The value of the section type to search.
189   @param  SectionData           A pointer to the discovered section, if successful.
190 
191   @retval EFI_SUCCESS           The section was found.
192   @retval EFI_NOT_FOUND         The section was not found.
193 
194 **/
195 EFI_STATUS
FileFindSection(IN EFI_FFS_FILE_HEADER * FileHeader,IN EFI_SECTION_TYPE SectionType,OUT VOID ** SectionData)196 FileFindSection (
197   IN EFI_FFS_FILE_HEADER        *FileHeader,
198   IN EFI_SECTION_TYPE           SectionType,
199   OUT VOID                      **SectionData
200   )
201 {
202   UINT32                        FileSize;
203   EFI_COMMON_SECTION_HEADER     *Section;
204   UINT32                        SectionSize;
205   UINT32                        Index;
206 
207   if (IS_FFS_FILE2 (FileHeader)) {
208     FileSize = FFS_FILE2_SIZE (FileHeader);
209   } else {
210     FileSize = FFS_FILE_SIZE (FileHeader);
211   }
212   FileSize  -= sizeof (EFI_FFS_FILE_HEADER);
213 
214   Section    = (EFI_COMMON_SECTION_HEADER *)(FileHeader + 1);
215   Index      = 0;
216   while (Index < FileSize) {
217     if (Section->Type == SectionType) {
218       if (IS_SECTION2 (Section)) {
219         *SectionData = (VOID *)((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER2));
220       } else {
221         *SectionData = (VOID *)((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER));
222       }
223       return EFI_SUCCESS;
224     }
225 
226     if (IS_SECTION2 (Section)) {
227       SectionSize = SECTION2_SIZE (Section);
228     } else {
229       SectionSize = SECTION_SIZE (Section);
230     }
231 
232     SectionSize = GET_OCCUPIED_SIZE (SectionSize, 4);
233     ASSERT (SectionSize != 0);
234     Index += SectionSize;
235 
236     Section = (EFI_COMMON_SECTION_HEADER *)((UINT8 *)Section + SectionSize);
237   }
238 
239   return EFI_NOT_FOUND;
240 }
241 
242 
243 /**
244   Find DXE core from FV and build DXE core HOBs.
245 
246   @param[out]  DxeCoreEntryPoint     DXE core entry point
247 
248   @retval EFI_SUCCESS        If it completed successfully.
249   @retval EFI_NOT_FOUND      If it failed to load DXE FV.
250 **/
251 EFI_STATUS
LoadDxeCore(OUT PHYSICAL_ADDRESS * DxeCoreEntryPoint)252 LoadDxeCore (
253   OUT PHYSICAL_ADDRESS        *DxeCoreEntryPoint
254   )
255 {
256   EFI_STATUS                  Status;
257   EFI_FIRMWARE_VOLUME_HEADER  *PayloadFv;
258   EFI_FIRMWARE_VOLUME_HEADER  *DxeCoreFv;
259   EFI_FFS_FILE_HEADER         *FileHeader;
260   VOID                        *PeCoffImage;
261   EFI_PHYSICAL_ADDRESS        ImageAddress;
262   UINT64                      ImageSize;
263 
264   PayloadFv = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)PcdGet32 (PcdPayloadFdMemBase);
265 
266   //
267   // DXE FV is inside Payload FV. Here find DXE FV from Payload FV
268   //
269   Status = FvFindFile (PayloadFv, EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, &FileHeader);
270   if (EFI_ERROR (Status)) {
271     return Status;
272   }
273   Status = FileFindSection (FileHeader, EFI_SECTION_FIRMWARE_VOLUME_IMAGE, (VOID **)&DxeCoreFv);
274   if (EFI_ERROR (Status)) {
275     return Status;
276   }
277 
278   //
279   // Report DXE FV to DXE core
280   //
281   BuildFvHob ((EFI_PHYSICAL_ADDRESS) (UINTN) DxeCoreFv, DxeCoreFv->FvLength);
282 
283   //
284   // Find DXE core file from DXE FV
285   //
286   Status = FvFindFile (DxeCoreFv, EFI_FV_FILETYPE_DXE_CORE, &FileHeader);
287   if (EFI_ERROR (Status)) {
288     return Status;
289   }
290 
291   Status = FileFindSection (FileHeader, EFI_SECTION_PE32, (VOID **)&PeCoffImage);
292   if (EFI_ERROR (Status)) {
293     return Status;
294   }
295 
296   //
297   // Get DXE core info
298   //
299   Status = LoadPeCoffImage (PeCoffImage, &ImageAddress, &ImageSize, DxeCoreEntryPoint);
300   if (EFI_ERROR (Status)) {
301     return Status;
302   }
303 
304   BuildModuleHob (&FileHeader->Name, ImageAddress, EFI_SIZE_TO_PAGES ((UINT32) ImageSize) * EFI_PAGE_SIZE, *DxeCoreEntryPoint);
305 
306   return EFI_SUCCESS;
307 }
308