]> git.proxmox.com Git - mirror_edk2.git/blob - PrmPkg/Library/DxePrmModuleDiscoveryLib/DxePrmModuleDiscoveryLib.c
PrmPkg/DxePrmModuleDiscoveryLib: Add initial host-based unit tests
[mirror_edk2.git] / PrmPkg / Library / DxePrmModuleDiscoveryLib / DxePrmModuleDiscoveryLib.c
1 /** @file
2
3 The PRM Module Discovery library provides functionality to discover PRM modules installed by platform firmware.
4
5 Copyright (c) Microsoft Corporation
6 Copyright (c) 2020 - 2022, Intel Corporation. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 #include <PiMm.h>
12 #include <Protocol/MmAccess.h>
13 #include <Library/BaseLib.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/DebugLib.h>
16 #include <Library/MemoryAllocationLib.h>
17 #include <Library/PrmModuleDiscoveryLib.h>
18 #include <Library/PrmPeCoffLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Protocol/LoadedImage.h>
21
22 #include "PrmModuleDiscovery.h"
23
24 #define _DBGMSGID_ "[PRMMODULEDISCOVERYLIB]"
25
26 LIST_ENTRY mPrmModuleList;
27
28 /**
29 Gets the next PRM module discovered after the given PRM module.
30
31 @param[in,out] ModuleImageContext A pointer to a pointer to a PRM module image context structure.
32 ModuleImageContext should point to a pointer that points to NULL to
33 get the first PRM module discovered.
34
35 @retval EFI_SUCCESS The next PRM module was found successfully.
36 @retval EFI_INVALID_PARAMETER The given ModuleImageContext structure is invalid or the pointer is NULL.
37 @retval EFI_NOT_FOUND The next PRM module was not found.
38
39 **/
40 EFI_STATUS
41 EFIAPI
42 GetNextPrmModuleEntry (
43 IN OUT PRM_MODULE_IMAGE_CONTEXT **ModuleImageContext
44 )
45 {
46 LIST_ENTRY *CurrentLink;
47 LIST_ENTRY *ForwardLink;
48 PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *CurrentListEntry;
49 PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *ForwardListEntry;
50
51 DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
52
53 if (ModuleImageContext == NULL) {
54 return EFI_INVALID_PARAMETER;
55 }
56
57 if (*ModuleImageContext == NULL) {
58 ForwardLink = GetFirstNode (&mPrmModuleList);
59 } else {
60 CurrentListEntry = NULL;
61 CurrentListEntry = CR (*ModuleImageContext, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY, Context, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE);
62 if (CurrentListEntry == NULL || CurrentListEntry->Signature != PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE) {
63 return EFI_INVALID_PARAMETER;
64 }
65
66 CurrentLink = &CurrentListEntry->Link;
67 ForwardLink = GetNextNode (&mPrmModuleList, CurrentLink);
68
69 if (ForwardLink == &mPrmModuleList) {
70 return EFI_NOT_FOUND;
71 }
72 }
73
74 ForwardListEntry = BASE_CR (ForwardLink, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY, Link);
75 if (ForwardListEntry->Signature == PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE) {
76 *ModuleImageContext = &ForwardListEntry->Context;
77 return EFI_SUCCESS;
78 }
79
80 return EFI_NOT_FOUND;
81 }
82
83 /**
84 Creates a new PRM Module Image Context linked list entry.
85
86 @retval PrmModuleImageContextListEntry If successful, a pointer a PRM Module Image Context linked list entry
87 otherwise, NULL is returned.
88
89 **/
90 PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *
91 CreateNewPrmModuleImageContextListEntry (
92 VOID
93 )
94 {
95 PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *PrmModuleImageContextListEntry;
96
97 DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
98
99 PrmModuleImageContextListEntry = AllocateZeroPool (sizeof (*PrmModuleImageContextListEntry));
100 if (PrmModuleImageContextListEntry == NULL) {
101 return NULL;
102 }
103 DEBUG ((
104 DEBUG_INFO,
105 " %a %a: Allocated PrmModuleImageContextListEntry at 0x%x of size 0x%x bytes.\n",
106 _DBGMSGID_,
107 __FUNCTION__,
108 (UINTN) PrmModuleImageContextListEntry,
109 sizeof (*PrmModuleImageContextListEntry)
110 ));
111
112 PrmModuleImageContextListEntry->Signature = PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE;
113
114 return PrmModuleImageContextListEntry;
115 }
116
117 /**
118 Check whether the address is within any of the MMRAM ranges.
119
120 @param[in] Address The address to be checked.
121 @param[in] MmramRanges Pointer to MMRAM descriptor.
122 @param[in] MmramRangeCount MMRAM range count.
123
124 @retval TRUE The address is in MMRAM ranges.
125 @retval FALSE The address is out of MMRAM ranges.
126 **/
127 BOOLEAN
128 EFIAPI
129 IsAddressInMmram (
130 IN EFI_PHYSICAL_ADDRESS Address,
131 IN EFI_MMRAM_DESCRIPTOR *MmramRanges,
132 IN UINTN MmramRangeCount
133 )
134 {
135 UINTN Index;
136
137 for (Index = 0; Index < MmramRangeCount; Index++) {
138 if ((Address >= MmramRanges[Index].CpuStart) &&
139 (Address < (MmramRanges[Index].CpuStart + MmramRanges[Index].PhysicalSize)))
140 {
141 return TRUE;
142 }
143 }
144
145 return FALSE;
146 }
147
148 /**
149 Discovers all PRM Modules loaded during boot.
150
151 Each PRM Module discovered is placed into a linked list so the list can br processsed in the future.
152
153 @param[out] ModuleCount An optional pointer parameter that, if provided, is set to the number
154 of PRM modules discovered.
155 @param[out] HandlerCount An optional pointer parameter that, if provided, is set to the number
156 of PRM handlers discovered.
157
158 @retval EFI_SUCCESS All PRM Modules were discovered successfully.
159 @retval EFI_INVALID_PARAMETER An actual pointer parameter was passed as NULL.
160 @retval EFI_NOT_FOUND The gEfiLoadedImageProtocolGuid protocol could not be found.
161 @retval EFI_OUT_OF_RESOURCES Insufficient memory resources to allocate the new PRM Context
162 linked list nodes.
163 @retval EFI_ALREADY_STARTED The function was called previously and already discovered the PRM modules
164 loaded on this boot.
165
166 **/
167 EFI_STATUS
168 EFIAPI
169 DiscoverPrmModules (
170 OUT UINTN *ModuleCount OPTIONAL,
171 OUT UINTN *HandlerCount OPTIONAL
172 )
173 {
174 EFI_STATUS Status;
175 PRM_MODULE_IMAGE_CONTEXT TempPrmModuleImageContext;
176 PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *PrmModuleImageContextListEntry;
177 EFI_LOADED_IMAGE_PROTOCOL *LoadedImageProtocol;
178 EFI_HANDLE *HandleBuffer;
179 UINTN HandleCount;
180 UINTN Index;
181 UINTN PrmHandlerCount;
182 UINTN PrmModuleCount;
183 EFI_MM_ACCESS_PROTOCOL *MmAccess;
184 UINTN Size;
185 EFI_MMRAM_DESCRIPTOR *MmramRanges;
186 UINTN MmramRangeCount;
187
188 DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
189
190 PrmHandlerCount = 0;
191 PrmModuleCount = 0;
192
193 if (!IsListEmpty (&mPrmModuleList)) {
194 return EFI_ALREADY_STARTED;
195 }
196
197 Status = gBS->LocateHandleBuffer (
198 ByProtocol,
199 &gEfiLoadedImageProtocolGuid,
200 NULL,
201 &HandleCount,
202 &HandleBuffer
203 );
204 if (EFI_ERROR (Status) && (HandleCount == 0)) {
205 DEBUG ((DEBUG_ERROR, "%a %a: No LoadedImageProtocol instances found!\n", _DBGMSGID_, __FUNCTION__));
206 return EFI_NOT_FOUND;
207 }
208
209 MmramRanges = NULL;
210 MmramRangeCount = 0;
211 Status = gBS->LocateProtocol (
212 &gEfiMmAccessProtocolGuid,
213 NULL,
214 (VOID **)&MmAccess
215 );
216 if (Status == EFI_SUCCESS) {
217 //
218 // Get MMRAM range information
219 //
220 Size = 0;
221 Status = MmAccess->GetCapabilities (MmAccess, &Size, NULL);
222 if ((Status == EFI_BUFFER_TOO_SMALL) && (Size != 0)) {
223 MmramRanges = (EFI_MMRAM_DESCRIPTOR *)AllocatePool (Size);
224 if (MmramRanges != NULL) {
225 Status = MmAccess->GetCapabilities (MmAccess, &Size, MmramRanges);
226 if (Status == EFI_SUCCESS) {
227 MmramRangeCount = Size / sizeof (EFI_MMRAM_DESCRIPTOR);
228 }
229 }
230 }
231 }
232
233 for (Index = 0; Index < HandleCount; Index++) {
234 Status = gBS->HandleProtocol (
235 HandleBuffer[Index],
236 &gEfiLoadedImageProtocolGuid,
237 (VOID **) &LoadedImageProtocol
238 );
239 if (EFI_ERROR (Status)) {
240 continue;
241 }
242
243 if (IsAddressInMmram ((EFI_PHYSICAL_ADDRESS)(UINTN)(LoadedImageProtocol->ImageBase), MmramRanges, MmramRangeCount)) {
244 continue;
245 }
246
247 ZeroMem (&TempPrmModuleImageContext, sizeof (TempPrmModuleImageContext));
248 TempPrmModuleImageContext.PeCoffImageContext.Handle = LoadedImageProtocol->ImageBase;
249 TempPrmModuleImageContext.PeCoffImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
250
251 Status = PeCoffLoaderGetImageInfo (&TempPrmModuleImageContext.PeCoffImageContext);
252 if (EFI_ERROR (Status) || TempPrmModuleImageContext.PeCoffImageContext.ImageError != IMAGE_ERROR_SUCCESS) {
253 DEBUG ((
254 DEBUG_WARN,
255 "%a %a: ImageHandle 0x%016lx is not a valid PE/COFF image. It cannot be considered a PRM module.\n",
256 _DBGMSGID_,
257 __FUNCTION__,
258 (EFI_PHYSICAL_ADDRESS) (UINTN) LoadedImageProtocol->ImageBase
259 ));
260 continue;
261 }
262 if (TempPrmModuleImageContext.PeCoffImageContext.IsTeImage) {
263 // A PRM Module is not allowed to be a TE image
264 continue;
265 }
266
267 // Attempt to find an export table in this image
268 Status = GetExportDirectoryInPeCoffImage (
269 LoadedImageProtocol->ImageBase,
270 &TempPrmModuleImageContext.PeCoffImageContext,
271 &TempPrmModuleImageContext.ExportDirectory
272 );
273 if (EFI_ERROR (Status)) {
274 continue;
275 }
276
277 // Attempt to find the PRM Module Export Descriptor in the export table
278 Status = GetPrmModuleExportDescriptorTable (
279 TempPrmModuleImageContext.ExportDirectory,
280 &TempPrmModuleImageContext.PeCoffImageContext,
281 &TempPrmModuleImageContext.ExportDescriptor
282 );
283 if (EFI_ERROR (Status) || TempPrmModuleImageContext.ExportDescriptor == NULL) {
284 continue;
285 }
286 // A PRM Module Export Descriptor was successfully found, this is considered a PRM Module.
287
288 //
289 // Create a new PRM Module image context node
290 //
291 PrmModuleImageContextListEntry = CreateNewPrmModuleImageContextListEntry ();
292 if (PrmModuleImageContextListEntry == NULL) {
293 return EFI_OUT_OF_RESOURCES;
294 }
295 CopyMem (
296 &PrmModuleImageContextListEntry->Context,
297 &TempPrmModuleImageContext,
298 sizeof (PrmModuleImageContextListEntry->Context)
299 );
300 InsertTailList (&mPrmModuleList, &PrmModuleImageContextListEntry->Link);
301 PrmHandlerCount += TempPrmModuleImageContext.ExportDescriptor->Header.NumberPrmHandlers;
302 PrmModuleCount++;
303 DEBUG ((DEBUG_INFO, "%a %a: New PRM Module inserted into list to be processed.\n", _DBGMSGID_, __FUNCTION__));
304 }
305
306 if (HandlerCount != NULL) {
307 *HandlerCount = PrmHandlerCount;
308 }
309 if (ModuleCount != NULL) {
310 *ModuleCount = PrmModuleCount;
311 }
312
313 if (MmramRanges != NULL) {
314 FreePool (MmramRanges);
315 }
316
317 return EFI_SUCCESS;
318 }
319
320 /**
321 The destructor function for this library instance.
322
323 Frees global resources allocated by this library instance.
324
325 @param ImageHandle The firmware allocated handle for the EFI image.
326 @param SystemTable A pointer to the EFI System Table.
327
328 @retval EFI_SUCCESS The destructor always returns EFI_SUCCESS.
329
330 **/
331 EFI_STATUS
332 EFIAPI
333 PrmModuleDiscoveryLibDestructor (
334 IN EFI_HANDLE ImageHandle,
335 IN EFI_SYSTEM_TABLE *SystemTable
336 )
337 {
338 LIST_ENTRY *Link;
339 LIST_ENTRY *NextLink;
340 PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *ListEntry;
341
342 if (IsListEmpty (&mPrmModuleList)) {
343 return EFI_SUCCESS;
344 }
345
346 Link = GetFirstNode (&mPrmModuleList);
347 while (!IsNull (&mPrmModuleList, Link)) {
348 ListEntry = CR (Link, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY, Link, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE);
349 NextLink = GetNextNode (&mPrmModuleList, Link);
350
351 RemoveEntryList (Link);
352 FreePool (ListEntry);
353
354 Link = NextLink;
355 }
356
357 return EFI_SUCCESS;
358 }
359
360 /**
361 The constructor function for this library instance.
362
363 Internally initializes data structures used later during library execution.
364
365 @param ImageHandle The firmware allocated handle for the EFI image.
366 @param SystemTable A pointer to the EFI System Table.
367
368 @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
369
370 **/
371 EFI_STATUS
372 EFIAPI
373 PrmModuleDiscoveryLibConstructor (
374 IN EFI_HANDLE ImageHandle,
375 IN EFI_SYSTEM_TABLE *SystemTable
376 )
377 {
378 InitializeListHead (&mPrmModuleList);
379
380 return EFI_SUCCESS;
381 }