]> git.proxmox.com Git - mirror_edk2.git/blob - PrmPkg/PrmLoaderDxe/PrmLoaderDxe.c
PrmPkg: Add initial PrmSsdtInstallDxe module
[mirror_edk2.git] / PrmPkg / PrmLoaderDxe / PrmLoaderDxe.c
1 /** @file
2
3 This file contains the implementation for a Platform Runtime Mechanism (PRM)
4 loader driver.
5
6 Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
7 Copyright (c) Microsoft Corporation
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9
10 **/
11
12 #include "PrmAcpiTable.h"
13 #include "PrmLoader.h"
14
15 #include <IndustryStandard/Acpi.h>
16 #include <Library/BaseLib.h>
17 #include <Library/BaseMemoryLib.h>
18 #include <Library/DebugLib.h>
19 #include <Library/MemoryAllocationLib.h>
20 #include <Library/PrmContextBufferLib.h>
21 #include <Library/UefiBootServicesTableLib.h>
22 #include <Library/UefiLib.h>
23 #include <Protocol/AcpiTable.h>
24 #include <Protocol/LoadedImage.h>
25 #include <Protocol/PrmConfig.h>
26
27 #include <PrmContextBuffer.h>
28 #include <PrmMmio.h>
29 #include <PrmModuleUpdate.h>
30
31 LIST_ENTRY mPrmModuleList;
32
33 // Todo: Potentially refactor mPrmHandlerCount and mPrmModuleCount into localized structures
34 // in the future.
35 UINT32 mPrmHandlerCount;
36 UINT32 mPrmModuleCount;
37
38 /**
39 Gets a pointer to the export directory in a given PE/COFF image.
40
41 @param[in] ImageExportDirectory A pointer to an export directory table in a PE/COFF image.
42 @param[in] PeCoffLoaderImageContext A pointer to a PE_COFF_LOADER_IMAGE_CONTEXT structure that contains the
43 PE/COFF image context for the Image containing the PRM Module Export
44 Descriptor table.
45 @param[out] ExportDescriptor A pointer to a pointer to the PRM Module Export Descriptor table found
46 in the ImageExportDirectory given.
47
48 @retval EFI_SUCCESS The PRM Module Export Descriptor table was found successfully.
49 @retval EFI_INVALID_PARAMETER A required parameter is NULL.
50 @retval EFI_NOT_FOUND The PRM Module Export Descriptor table was not found in the given
51 ImageExportDirectory.
52
53 **/
54 EFI_STATUS
55 GetPrmModuleExportDescriptorTable (
56 IN EFI_IMAGE_EXPORT_DIRECTORY *ImageExportDirectory,
57 IN PE_COFF_LOADER_IMAGE_CONTEXT *PeCoffLoaderImageContext,
58 OUT PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT **ExportDescriptor
59 )
60 {
61 UINTN Index;
62 EFI_PHYSICAL_ADDRESS CurrentImageAddress;
63 UINT16 PrmModuleExportDescriptorOrdinal;
64 CONST CHAR8 *CurrentExportName;
65 UINT16 *OrdinalTable;
66 UINT32 *ExportNamePointerTable;
67 UINT32 *ExportAddressTable;
68 PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT *TempExportDescriptor;
69
70 DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
71
72 *ExportDescriptor = NULL;
73
74 if (ImageExportDirectory == NULL ||
75 PeCoffLoaderImageContext == NULL ||
76 PeCoffLoaderImageContext->ImageAddress == 0 ||
77 ExportDescriptor == NULL) {
78 return EFI_INVALID_PARAMETER;
79 }
80
81 DEBUG ((
82 DEBUG_INFO,
83 " %a %a: %d exported names found in this image.\n",
84 _DBGMSGID_,
85 __FUNCTION__,
86 ImageExportDirectory->NumberOfNames
87 ));
88
89 //
90 // The export name pointer table and export ordinal table form two parallel arrays associated by index.
91 //
92 CurrentImageAddress = PeCoffLoaderImageContext->ImageAddress;
93 ExportAddressTable = (UINT32 *) ((UINTN) CurrentImageAddress + ImageExportDirectory->AddressOfFunctions);
94 ExportNamePointerTable = (UINT32 *) ((UINTN) CurrentImageAddress + ImageExportDirectory->AddressOfNames);
95 OrdinalTable = (UINT16 *) ((UINTN) CurrentImageAddress + ImageExportDirectory->AddressOfNameOrdinals);
96
97 for (Index = 0; Index < ImageExportDirectory->NumberOfNames; Index++) {
98 CurrentExportName = (CONST CHAR8 *) ((UINTN) CurrentImageAddress + ExportNamePointerTable[Index]);
99 DEBUG ((
100 DEBUG_INFO,
101 " %a %a: Export Name[0x%x] - %a.\n",
102 _DBGMSGID_,
103 __FUNCTION__,
104 Index,
105 CurrentExportName
106 ));
107 if (
108 AsciiStrnCmp (
109 PRM_STRING(PRM_MODULE_EXPORT_DESCRIPTOR_NAME),
110 CurrentExportName,
111 AsciiStrLen (PRM_STRING(PRM_MODULE_EXPORT_DESCRIPTOR_NAME))
112 ) == 0) {
113 PrmModuleExportDescriptorOrdinal = OrdinalTable[Index];
114 DEBUG ((
115 DEBUG_INFO,
116 " %a %a: PRM Module Export Descriptor found. Ordinal = %d.\n",
117 _DBGMSGID_,
118 __FUNCTION__,
119 PrmModuleExportDescriptorOrdinal
120 ));
121 if (PrmModuleExportDescriptorOrdinal >= ImageExportDirectory->NumberOfFunctions) {
122 DEBUG ((DEBUG_ERROR, "%a %a: The PRM Module Export Descriptor ordinal value is invalid.\n", _DBGMSGID_, __FUNCTION__));
123 return EFI_NOT_FOUND;
124 }
125 TempExportDescriptor = (PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT *) ((UINTN) CurrentImageAddress + ExportAddressTable[PrmModuleExportDescriptorOrdinal]);
126 if (TempExportDescriptor->Header.Signature == PRM_MODULE_EXPORT_DESCRIPTOR_SIGNATURE) {
127 *ExportDescriptor = TempExportDescriptor;
128 DEBUG ((DEBUG_INFO, " %a %a: PRM Module Export Descriptor found at 0x%x.\n", _DBGMSGID_, __FUNCTION__, (UINTN) ExportDescriptor));
129 } else {
130 DEBUG ((
131 DEBUG_INFO,
132 " %a %a: PRM Module Export Descriptor found at 0x%x but signature check failed.\n",
133 _DBGMSGID_,
134 __FUNCTION__,
135 (UINTN) TempExportDescriptor
136 ));
137 }
138 DEBUG ((DEBUG_INFO, " %a %a: Exiting export iteration since export descriptor found.\n", _DBGMSGID_, __FUNCTION__));
139 return EFI_SUCCESS;
140 }
141 }
142
143 return EFI_NOT_FOUND;
144 }
145
146 /**
147 Gets a pointer to the export directory in a given PE/COFF image.
148
149 @param[in] Image A pointer to a PE32/COFF image base address that is loaded into memory
150 and already relocated to the memory base address. RVAs in the image given
151 should be valid.
152 @param[in] PeCoffLoaderImageContext A pointer to a PE_COFF_LOADER_IMAGE_CONTEXT structure that contains the
153 PE/COFF image context for the Image given.
154 @param[out] ImageExportDirectory A pointer to a pointer to the export directory found in the Image given.
155
156 @retval EFI_SUCCESS The export directory was found successfully.
157 @retval EFI_INVALID_PARAMETER A required parameter is NULL.
158 @retval EFI_UNSUPPORTED The PE/COFF image given is not supported as a PRM Module.
159 @retval EFI_NOT_FOUND The image export directory could not be found for this image.
160
161 **/
162 EFI_STATUS
163 GetExportDirectoryInPeCoffImage (
164 IN VOID *Image,
165 IN PE_COFF_LOADER_IMAGE_CONTEXT *PeCoffLoaderImageContext,
166 OUT EFI_IMAGE_EXPORT_DIRECTORY **ImageExportDirectory
167 )
168 {
169 UINT16 Magic;
170 UINT32 NumberOfRvaAndSizes;
171 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION OptionalHeaderPtrUnion;
172 EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry;
173 EFI_IMAGE_EXPORT_DIRECTORY *ExportDirectory;
174 EFI_IMAGE_SECTION_HEADER *SectionHeader;
175
176 if (Image == NULL || PeCoffLoaderImageContext == NULL || ImageExportDirectory == NULL) {
177 return EFI_INVALID_PARAMETER;
178 }
179
180 DirectoryEntry = NULL;
181 ExportDirectory = NULL;
182
183 //
184 // NOTE: For backward compatibility, use the Machine field to identify a PE32/PE32+
185 // image instead of using the Magic field. Some systems might generate a PE32+
186 // image with PE32 magic.
187 //
188 switch (PeCoffLoaderImageContext->Machine) {
189 case EFI_IMAGE_MACHINE_IA32:
190 // Todo: Add EFI_IMAGE_MACHINE_ARMT
191 //
192 // Assume PE32 image with IA32 Machine field.
193 //
194 Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;
195 break;
196 case EFI_IMAGE_MACHINE_X64:
197 //
198 // Assume PE32+ image with X64 Machine field
199 //
200 Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
201 break;
202 default:
203 //
204 // For unknown Machine field, use Magic in optional header
205 //
206 DEBUG ((
207 DEBUG_WARN,
208 "%a %a: The machine type for this image is not valid for a PRM module.\n",
209 _DBGMSGID_,
210 __FUNCTION__
211 ));
212 return EFI_UNSUPPORTED;
213 }
214
215 OptionalHeaderPtrUnion.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (
216 (UINTN) Image +
217 PeCoffLoaderImageContext->PeCoffHeaderOffset
218 );
219
220 //
221 // Check the PE/COFF Header Signature. Determine if the image is valid and/or a TE image.
222 //
223 if (OptionalHeaderPtrUnion.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
224 DEBUG ((DEBUG_ERROR, "%a %a: The PE signature is not valid for the current image.\n", _DBGMSGID_, __FUNCTION__));
225 return EFI_UNSUPPORTED;
226 }
227
228 SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (
229 (UINTN) Image +
230 PeCoffLoaderImageContext->PeCoffHeaderOffset +
231 sizeof (UINT32) +
232 sizeof (EFI_IMAGE_FILE_HEADER) +
233 PeCoffLoaderImageContext->SizeOfHeaders
234 );
235 if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
236 //
237 // Use the PE32 offset to get the Export Directory Entry
238 //
239 NumberOfRvaAndSizes = OptionalHeaderPtrUnion.Pe32->OptionalHeader.NumberOfRvaAndSizes;
240 DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *) &(OptionalHeaderPtrUnion.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT]);
241 } else if (OptionalHeaderPtrUnion.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
242 //
243 // Use the PE32+ offset get the Export Directory Entry
244 //
245 NumberOfRvaAndSizes = OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
246 DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *) &(OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_EXPORT]);
247 } else {
248 return EFI_UNSUPPORTED;
249 }
250
251 if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_EXPORT || DirectoryEntry->VirtualAddress == 0) {
252 //
253 // The export directory is not present
254 //
255 return EFI_NOT_FOUND;
256 } else if (((UINT32) (~0) - DirectoryEntry->VirtualAddress) < DirectoryEntry->Size) {
257 //
258 // The directory address overflows
259 //
260 DEBUG ((DEBUG_ERROR, "%a %a: The export directory entry in this image results in overflow.\n", _DBGMSGID_, __FUNCTION__));
261 return EFI_UNSUPPORTED;
262 } else {
263 DEBUG ((DEBUG_INFO, "%a %a: Export Directory Entry found in the image at 0x%x.\n", _DBGMSGID_, __FUNCTION__, (UINTN) OptionalHeaderPtrUnion.Pe32));
264 DEBUG ((DEBUG_INFO, " %a %a: Directory Entry Virtual Address = 0x%x.\n", _DBGMSGID_, __FUNCTION__, DirectoryEntry->VirtualAddress));
265
266 ExportDirectory = (EFI_IMAGE_EXPORT_DIRECTORY *) ((UINTN) Image + DirectoryEntry->VirtualAddress);
267 DEBUG ((
268 DEBUG_INFO,
269 " %a %a: Export Directory Table found successfully at 0x%x. Name address = 0x%x. Name = %a.\n",
270 _DBGMSGID_,
271 __FUNCTION__,
272 (UINTN) ExportDirectory,
273 ((UINTN) Image + ExportDirectory->Name),
274 (CHAR8 *) ((UINTN) Image + ExportDirectory->Name)
275 ));
276 }
277 *ImageExportDirectory = ExportDirectory;
278
279 return EFI_SUCCESS;
280 }
281
282 /**
283 Returns the image major and image minor version in a given PE/COFF image.
284
285 @param[in] Image A pointer to a PE32/COFF image base address that is loaded into memory
286 and already relocated to the memory base address. RVAs in the image given
287 should be valid.
288 @param[in] PeCoffLoaderImageContext A pointer to a PE_COFF_LOADER_IMAGE_CONTEXT structure that contains the
289 PE/COFF image context for the Image given.
290 @param[out] ImageMajorVersion A pointer to a UINT16 buffer to hold the image major version.
291 @param[out] ImageMinorVersion A pointer to a UINT16 buffer to hold the image minor version.
292
293 @retval EFI_SUCCESS The image version was read successfully.
294 @retval EFI_INVALID_PARAMETER A required parameter is NULL.
295 @retval EFI_UNSUPPORTED The PE/COFF image given is not supported.
296
297 **/
298 EFI_STATUS
299 GetImageVersionInPeCoffImage (
300 IN VOID *Image,
301 IN PE_COFF_LOADER_IMAGE_CONTEXT *PeCoffLoaderImageContext,
302 OUT UINT16 *ImageMajorVersion,
303 OUT UINT16 *ImageMinorVersion
304 )
305 {
306 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION OptionalHeaderPtrUnion;
307 UINT16 Magic;
308
309 DEBUG ((DEBUG_INFO, " %a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
310
311 if (Image == NULL || PeCoffLoaderImageContext == NULL || ImageMajorVersion == NULL || ImageMinorVersion == NULL) {
312 return EFI_INVALID_PARAMETER;
313 }
314
315 //
316 // NOTE: For backward compatibility, use the Machine field to identify a PE32/PE32+
317 // image instead of using the Magic field. Some systems might generate a PE32+
318 // image with PE32 magic.
319 //
320 switch (PeCoffLoaderImageContext->Machine) {
321 case EFI_IMAGE_MACHINE_IA32:
322 // Todo: Add EFI_IMAGE_MACHINE_ARMT
323 //
324 // Assume PE32 image with IA32 Machine field.
325 //
326 Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;
327 break;
328 case EFI_IMAGE_MACHINE_X64:
329 //
330 // Assume PE32+ image with X64 Machine field
331 //
332 Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
333 break;
334 default:
335 //
336 // For unknown Machine field, use Magic in optional header
337 //
338 DEBUG ((
339 DEBUG_WARN,
340 "%a %a: The machine type for this image is not valid for a PRM module.\n",
341 _DBGMSGID_,
342 __FUNCTION__
343 ));
344 return EFI_UNSUPPORTED;
345 }
346
347 OptionalHeaderPtrUnion.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (
348 (UINTN) Image +
349 PeCoffLoaderImageContext->PeCoffHeaderOffset
350 );
351 //
352 // Check the PE/COFF Header Signature. Determine if the image is valid and/or a TE image.
353 //
354 if (OptionalHeaderPtrUnion.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
355 DEBUG ((DEBUG_ERROR, "%a %a: The PE signature is not valid for the current image.\n", _DBGMSGID_, __FUNCTION__));
356 return EFI_UNSUPPORTED;
357 }
358
359 if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
360 //
361 // Use the PE32 offset to get the Export Directory Entry
362 //
363 *ImageMajorVersion = OptionalHeaderPtrUnion.Pe32->OptionalHeader.MajorImageVersion;
364 *ImageMinorVersion = OptionalHeaderPtrUnion.Pe32->OptionalHeader.MinorImageVersion;
365 } else {
366 //
367 // Use the PE32+ offset to get the Export Directory Entry
368 //
369 *ImageMajorVersion = OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.MajorImageVersion;
370 *ImageMinorVersion = OptionalHeaderPtrUnion.Pe32Plus->OptionalHeader.MinorImageVersion;
371 }
372
373 DEBUG ((DEBUG_INFO, " %a %a - Image Major Version: 0x%02x.\n", _DBGMSGID_, __FUNCTION__, *ImageMajorVersion));
374 DEBUG ((DEBUG_INFO, " %a %a - Image Minor Version: 0x%02x.\n", _DBGMSGID_, __FUNCTION__, *ImageMinorVersion));
375
376 return EFI_SUCCESS;
377 }
378
379 /**
380 Creates a new PRM Module Image Context linked list entry.
381
382 @retval PrmModuleImageContextListEntry If successful, a pointer a PRM Module Image Context linked list entry
383 otherwise, NULL is returned.
384
385 **/
386 STATIC
387 PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *
388 CreateNewPrmModuleImageContextListEntry (
389 VOID
390 )
391 {
392 PRM_MODULE_IMAGE_CONTEXT *PrmModuleImageContext;
393 PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *PrmModuleImageContextListEntry;
394
395 DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
396
397 PrmModuleImageContext = AllocateZeroPool (sizeof (*PrmModuleImageContext));
398 if (PrmModuleImageContext == NULL) {
399 return NULL;
400 }
401 DEBUG ((
402 DEBUG_INFO,
403 " %a %a: Allocated PrmModuleImageContext at 0x%x of size 0x%x bytes.\n",
404 _DBGMSGID_,
405 __FUNCTION__,
406 (UINTN) PrmModuleImageContext,
407 sizeof (*PrmModuleImageContext)
408 ));
409
410 PrmModuleImageContextListEntry = AllocateZeroPool (sizeof (*PrmModuleImageContextListEntry));
411 if (PrmModuleImageContextListEntry == NULL) {
412 FreePool (PrmModuleImageContext);
413 return NULL;
414 }
415 DEBUG ((
416 DEBUG_INFO,
417 " %a %a: Allocated PrmModuleImageContextListEntry at 0x%x of size 0x%x bytes.\n",
418 _DBGMSGID_,
419 __FUNCTION__,
420 (UINTN) PrmModuleImageContextListEntry,
421 sizeof (*PrmModuleImageContextListEntry)
422 ));
423
424 PrmModuleImageContextListEntry->Signature = PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE;
425 PrmModuleImageContextListEntry->Context = PrmModuleImageContext;
426
427 return PrmModuleImageContextListEntry;
428 }
429
430 /**
431 Discovers all PRM Modules loaded during the DXE boot phase.
432
433 Each PRM Module discovered is placed into a linked list so the list can br processsed in the future.
434
435 @retval EFI_SUCCESS All PRM Modules were discovered successfully.
436 @retval EFI_NOT_FOUND The gEfiLoadedImageProtocolGuid protocol could not be found.
437 @retval EFI_OUT_OF_RESOURCES Insufficient memory resources to allocate the new PRM Context
438 linked list nodes.
439
440 **/
441 EFI_STATUS
442 DiscoverPrmModules (
443 VOID
444 )
445 {
446 EFI_STATUS Status;
447 PRM_MODULE_IMAGE_CONTEXT TempPrmModuleImageContext;
448 PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *PrmModuleImageContextListEntry;
449 EFI_LOADED_IMAGE_PROTOCOL *LoadedImageProtocol;
450 EFI_HANDLE *HandleBuffer;
451 UINTN HandleCount;
452 UINTN Index;
453
454 DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
455
456 Status = gBS->LocateHandleBuffer (
457 ByProtocol,
458 &gEfiLoadedImageProtocolGuid,
459 NULL,
460 &HandleCount,
461 &HandleBuffer
462 );
463 if (EFI_ERROR (Status) && (HandleCount == 0)) {
464 DEBUG ((DEBUG_ERROR, "%a %a: No LoadedImageProtocol instances found!\n", _DBGMSGID_, __FUNCTION__));
465 return EFI_NOT_FOUND;
466 }
467
468 for (Index = 0; Index < HandleCount; Index++) {
469 Status = gBS->HandleProtocol (
470 HandleBuffer[Index],
471 &gEfiLoadedImageProtocolGuid,
472 (VOID **) &LoadedImageProtocol
473 );
474 if (EFI_ERROR (Status)) {
475 continue;
476 }
477
478 ZeroMem (&TempPrmModuleImageContext, sizeof (TempPrmModuleImageContext));
479 TempPrmModuleImageContext.PeCoffImageContext.Handle = LoadedImageProtocol->ImageBase;
480 TempPrmModuleImageContext.PeCoffImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
481
482 Status = PeCoffLoaderGetImageInfo (&TempPrmModuleImageContext.PeCoffImageContext);
483 if (EFI_ERROR (Status) || TempPrmModuleImageContext.PeCoffImageContext.ImageError != IMAGE_ERROR_SUCCESS) {
484 DEBUG ((
485 DEBUG_WARN,
486 "%a %a: ImageHandle 0x%016lx is not a valid PE/COFF image. It cannot be considered a PRM module.\n",
487 _DBGMSGID_,
488 __FUNCTION__,
489 (EFI_PHYSICAL_ADDRESS) (UINTN) LoadedImageProtocol->ImageBase
490 ));
491 continue;
492 }
493 if (TempPrmModuleImageContext.PeCoffImageContext.IsTeImage) {
494 // A PRM Module is not allowed to be a TE image
495 continue;
496 }
497
498 // Attempt to find an export table in this image
499 Status = GetExportDirectoryInPeCoffImage (
500 LoadedImageProtocol->ImageBase,
501 &TempPrmModuleImageContext.PeCoffImageContext,
502 &TempPrmModuleImageContext.ExportDirectory
503 );
504 if (EFI_ERROR (Status)) {
505 continue;
506 }
507
508 // Attempt to find the PRM Module Export Descriptor in the export table
509 Status = GetPrmModuleExportDescriptorTable (
510 TempPrmModuleImageContext.ExportDirectory,
511 &TempPrmModuleImageContext.PeCoffImageContext,
512 &TempPrmModuleImageContext.ExportDescriptor
513 );
514 if (EFI_ERROR (Status)) {
515 continue;
516 }
517 // A PRM Module Export Descriptor was successfully found, this is considered a PRM Module.
518
519 //
520 // Create a new PRM Module image context node
521 //
522 PrmModuleImageContextListEntry = CreateNewPrmModuleImageContextListEntry ();
523 if (PrmModuleImageContextListEntry == NULL) {
524 return EFI_OUT_OF_RESOURCES;
525 }
526 CopyMem (
527 PrmModuleImageContextListEntry->Context,
528 &TempPrmModuleImageContext,
529 sizeof (*(PrmModuleImageContextListEntry->Context))
530 );
531 InsertTailList (&mPrmModuleList, &PrmModuleImageContextListEntry->Link);
532 mPrmHandlerCount += TempPrmModuleImageContext.ExportDescriptor->Header.NumberPrmHandlers;
533 mPrmModuleCount++; // Todo: Match with global variable refactor change in the future
534 DEBUG ((DEBUG_INFO, "%a %a: New PRM Module inserted into list to be processed.\n", _DBGMSGID_, __FUNCTION__));
535 }
536
537 return EFI_SUCCESS;
538 }
539
540 /**
541 Gets the address of an entry in an image export table by ASCII name.
542
543 @param[in] ExportName A pointer to an ASCII name string of the entry name.
544 @param[in] ImageBaseAddress The base address of the PE/COFF image.
545 @param[in] ImageExportDirectory A pointer to the export directory in the image.
546 @param[out] ExportPhysicalAddress A pointer that will be updated with the address of the address of the
547 export entry if found.
548
549 @retval EFI_SUCCESS The export entry was found successfully.
550 @retval EFI_INVALID_PARAMETER A required pointer argument is NULL.
551 @retval EFI_NOT_FOUND An entry with the given ExportName was not found.
552
553 **/
554 EFI_STATUS
555 GetExportEntryAddress (
556 IN CONST CHAR8 *ExportName,
557 IN EFI_PHYSICAL_ADDRESS ImageBaseAddress,
558 IN EFI_IMAGE_EXPORT_DIRECTORY *ImageExportDirectory,
559 OUT EFI_PHYSICAL_ADDRESS *ExportPhysicalAddress
560 )
561 {
562 UINTN ExportNameIndex;
563 UINT16 CurrentExportOrdinal;
564 UINT32 *ExportAddressTable;
565 UINT32 *ExportNamePointerTable;
566 UINT16 *OrdinalTable;
567 CONST CHAR8 *ExportNameTablePointerName;
568
569 if (ExportName == NULL || ImageBaseAddress == 0 || ImageExportDirectory == NULL || ExportPhysicalAddress == NULL) {
570 return EFI_INVALID_PARAMETER;
571 }
572 *ExportPhysicalAddress = 0;
573
574 ExportAddressTable = (UINT32 *) ((UINTN) ImageBaseAddress + ImageExportDirectory->AddressOfFunctions);
575 ExportNamePointerTable = (UINT32 *) ((UINTN) ImageBaseAddress + ImageExportDirectory->AddressOfNames);
576 OrdinalTable = (UINT16 *) ((UINTN) ImageBaseAddress + ImageExportDirectory->AddressOfNameOrdinals);
577
578 for (ExportNameIndex = 0; ExportNameIndex < ImageExportDirectory->NumberOfNames; ExportNameIndex++) {
579 ExportNameTablePointerName = (CONST CHAR8 *) ((UINTN) ImageBaseAddress + ExportNamePointerTable[ExportNameIndex]);
580
581 if (AsciiStrnCmp (ExportName, ExportNameTablePointerName, PRM_HANDLER_NAME_MAXIMUM_LENGTH) == 0) {
582 CurrentExportOrdinal = OrdinalTable[ExportNameIndex];
583
584 ASSERT (CurrentExportOrdinal < ImageExportDirectory->NumberOfFunctions);
585 if (CurrentExportOrdinal >= ImageExportDirectory->NumberOfFunctions) {
586 DEBUG ((DEBUG_ERROR, " %a %a: The export ordinal value is invalid.\n", _DBGMSGID_, __FUNCTION__));
587 break;
588 }
589
590 *ExportPhysicalAddress = (EFI_PHYSICAL_ADDRESS) ((UINTN) ImageBaseAddress + ExportAddressTable[CurrentExportOrdinal]);
591 return EFI_SUCCESS;
592 }
593 }
594
595 return EFI_NOT_FOUND;
596 }
597
598 /**
599 Processes a list of PRM context entries to build a PRM ACPI table.
600
601 The ACPI table buffer is allocated and the table structure is built inside this function.
602
603 @param[out] PrmAcpiDescriptionTable A pointer to a pointer to a buffer that is allocated within this function
604 and will contain the PRM ACPI table. In case of an error in this function,
605 *PrmAcpiDescriptorTable will be NULL.
606
607 @retval EFI_SUCCESS All PRM Modules were processed to construct the PRM ACPI table successfully.
608 @retval EFI_INVALID_PARAMETER THe parameter PrmAcpiDescriptionTable is NULL.
609 @retval EFI_OUT_OF_RESOURCES Insufficient memory resources to allocate the PRM ACPI table boot services
610 memory data buffer.
611
612 **/
613 EFI_STATUS
614 ProcessPrmModules (
615 OUT PRM_ACPI_DESCRIPTION_TABLE **PrmAcpiDescriptionTable
616 )
617 {
618 EFI_IMAGE_EXPORT_DIRECTORY *CurrentImageExportDirectory;
619 PRM_MODULE_EXPORT_DESCRIPTOR_STRUCT *CurrentExportDescriptorStruct;
620 LIST_ENTRY *Link;
621 PRM_ACPI_DESCRIPTION_TABLE *PrmAcpiTable;
622 PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY *TempListEntry;
623 CONST CHAR8 *CurrentExportDescriptorHandlerName;
624
625 PRM_CONTEXT_BUFFER *CurrentContextBuffer;
626 PRM_MODULE_CONTEXT_BUFFERS *CurrentModuleContextBuffers;
627 PRM_MODULE_INFORMATION_STRUCT *CurrentModuleInfoStruct;
628 PRM_HANDLER_INFORMATION_STRUCT *CurrentHandlerInfoStruct;
629
630 EFI_STATUS Status;
631 EFI_PHYSICAL_ADDRESS CurrentImageAddress;
632 UINTN HandlerIndex;
633 UINT32 PrmAcpiDescriptionTableBufferSize;
634
635 UINT64 HandlerPhysicalAddress;
636
637 DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
638
639 if (PrmAcpiDescriptionTable == NULL) {
640 return EFI_INVALID_PARAMETER;
641 }
642 Link = NULL;
643 *PrmAcpiDescriptionTable = NULL;
644
645 DEBUG ((DEBUG_INFO, " %a %a: %d total PRM modules to process.\n", _DBGMSGID_, __FUNCTION__, mPrmModuleCount));
646 DEBUG ((DEBUG_INFO, " %a %a: %d total PRM handlers to process.\n", _DBGMSGID_, __FUNCTION__, mPrmHandlerCount));
647
648 PrmAcpiDescriptionTableBufferSize = (OFFSET_OF (PRM_ACPI_DESCRIPTION_TABLE, PrmModuleInfoStructure) +
649 (OFFSET_OF (PRM_MODULE_INFORMATION_STRUCT, HandlerInfoStructure) * mPrmModuleCount) +
650 (sizeof (PRM_HANDLER_INFORMATION_STRUCT) * mPrmHandlerCount)
651 );
652 DEBUG ((DEBUG_INFO, " %a %a: Total PRM ACPI table size: 0x%x.\n", _DBGMSGID_, __FUNCTION__, PrmAcpiDescriptionTableBufferSize));
653
654 PrmAcpiTable = AllocateZeroPool ((UINTN) PrmAcpiDescriptionTableBufferSize);
655 if (PrmAcpiTable == NULL) {
656 return EFI_OUT_OF_RESOURCES;
657 }
658
659 PrmAcpiTable->Header.Signature = PRM_TABLE_SIGNATURE;
660 PrmAcpiTable->Header.Length = PrmAcpiDescriptionTableBufferSize;
661 PrmAcpiTable->Header.Revision = PRM_TABLE_REVISION;
662 PrmAcpiTable->Header.Checksum = 0x0;
663 CopyMem (&PrmAcpiTable->Header.OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (PrmAcpiTable->Header.OemId));
664 PrmAcpiTable->Header.OemTableId = PcdGet64 (PcdAcpiDefaultOemTableId);
665 PrmAcpiTable->Header.OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision);
666 PrmAcpiTable->Header.CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId);
667 PrmAcpiTable->Header.CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision);
668 PrmAcpiTable->PrmModuleInfoOffset = OFFSET_OF (PRM_ACPI_DESCRIPTION_TABLE, PrmModuleInfoStructure);
669 PrmAcpiTable->PrmModuleInfoCount = mPrmModuleCount;
670
671 //
672 // Iterate across all PRM Modules on the list
673 //
674 CurrentModuleInfoStruct = &PrmAcpiTable->PrmModuleInfoStructure[0];
675 EFI_LIST_FOR_EACH(Link, &mPrmModuleList)
676 {
677 TempListEntry = CR(Link, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY, Link, PRM_MODULE_IMAGE_CONTEXT_LIST_ENTRY_SIGNATURE);
678 CurrentImageAddress = TempListEntry->Context->PeCoffImageContext.ImageAddress;
679 CurrentImageExportDirectory = TempListEntry->Context->ExportDirectory;
680 CurrentExportDescriptorStruct = TempListEntry->Context->ExportDescriptor;
681
682 DEBUG ((
683 DEBUG_INFO,
684 " %a %a: PRM Module - %a with %d handlers.\n",
685 _DBGMSGID_,
686 __FUNCTION__,
687 (CHAR8 *) ((UINTN) CurrentImageAddress + CurrentImageExportDirectory->Name),
688 CurrentExportDescriptorStruct->Header.NumberPrmHandlers
689 ));
690
691 CurrentModuleInfoStruct->StructureRevision = PRM_MODULE_INFORMATION_STRUCT_REVISION;
692 CurrentModuleInfoStruct->StructureLength = (
693 OFFSET_OF (PRM_MODULE_INFORMATION_STRUCT, HandlerInfoStructure) +
694 (CurrentExportDescriptorStruct->Header.NumberPrmHandlers * sizeof (PRM_HANDLER_INFORMATION_STRUCT))
695 );
696 CopyGuid (&CurrentModuleInfoStruct->Identifier, &CurrentExportDescriptorStruct->Header.ModuleGuid);
697 CurrentModuleInfoStruct->HandlerCount = (UINT32) CurrentExportDescriptorStruct->Header.NumberPrmHandlers;
698 CurrentModuleInfoStruct->HandlerInfoOffset = OFFSET_OF (PRM_MODULE_INFORMATION_STRUCT, HandlerInfoStructure);
699
700 CurrentModuleInfoStruct->MajorRevision = 0;
701 CurrentModuleInfoStruct->MinorRevision = 0;
702 Status = GetImageVersionInPeCoffImage (
703 (VOID *) (UINTN) CurrentImageAddress,
704 &TempListEntry->Context->PeCoffImageContext,
705 &CurrentModuleInfoStruct->MajorRevision,
706 &CurrentModuleInfoStruct->MinorRevision
707 );
708 ASSERT_EFI_ERROR (Status);
709
710 Status = GetExportEntryAddress (
711 PRM_STRING (PRM_MODULE_UPDATE_LOCK_DESCRIPTOR_NAME),
712 CurrentImageAddress,
713 CurrentImageExportDirectory,
714 (EFI_PHYSICAL_ADDRESS *) &(CurrentModuleInfoStruct->ModuleUpdateLock)
715 );
716 ASSERT_EFI_ERROR (Status);
717 if (!EFI_ERROR (Status)) {
718 DEBUG ((
719 DEBUG_INFO,
720 " %a %a: Found PRM module update lock physical address at 0x%016x.\n",
721 _DBGMSGID_,
722 __FUNCTION__,
723 CurrentModuleInfoStruct->ModuleUpdateLock
724 ));
725 }
726
727 // It is currently valid for a PRM module not to use a context buffer
728 Status = GetModuleContextBuffers (
729 ByModuleGuid,
730 &CurrentModuleInfoStruct->Identifier,
731 &CurrentModuleContextBuffers
732 );
733 ASSERT (!EFI_ERROR (Status) || Status == EFI_NOT_FOUND);
734 if (!EFI_ERROR (Status) && CurrentModuleContextBuffers != NULL) {
735 CurrentModuleInfoStruct->RuntimeMmioRanges = (UINT64) (UINTN) CurrentModuleContextBuffers->RuntimeMmioRanges;
736 }
737
738 //
739 // Iterate across all PRM handlers in the PRM Module
740 //
741 for (HandlerIndex = 0; HandlerIndex < CurrentExportDescriptorStruct->Header.NumberPrmHandlers; HandlerIndex++) {
742 CurrentHandlerInfoStruct = &(CurrentModuleInfoStruct->HandlerInfoStructure[HandlerIndex]);
743
744 CurrentHandlerInfoStruct->StructureRevision = PRM_HANDLER_INFORMATION_STRUCT_REVISION;
745 CurrentHandlerInfoStruct->StructureLength = sizeof (PRM_HANDLER_INFORMATION_STRUCT);
746 CopyGuid (
747 &CurrentHandlerInfoStruct->Identifier,
748 &CurrentExportDescriptorStruct->PrmHandlerExportDescriptors[HandlerIndex].PrmHandlerGuid
749 );
750
751 CurrentExportDescriptorHandlerName = (CONST CHAR8 *) CurrentExportDescriptorStruct->PrmHandlerExportDescriptors[HandlerIndex].PrmHandlerName;
752
753 Status = GetContextBuffer (
754 &CurrentHandlerInfoStruct->Identifier,
755 CurrentModuleContextBuffers,
756 &CurrentContextBuffer
757 );
758 if (!EFI_ERROR (Status)) {
759 #ifdef ALLOCATE_CONTEXT_BUFFER_IN_FW
760 CurrentHandlerInfoStruct->PrmContextBuffer = (UINT64) (UINTN) CurrentContextBuffer;
761 #else
762 CurrentHandlerInfoStruct->StaticDataBuffer = (UINT64) (UINTN) CurrentContextBuffer->StaticDataBuffer;
763 #endif
764 }
765
766 Status = GetExportEntryAddress (
767 CurrentExportDescriptorHandlerName,
768 CurrentImageAddress,
769 CurrentImageExportDirectory,
770 &HandlerPhysicalAddress
771 );
772 ASSERT_EFI_ERROR (Status);
773 if (!EFI_ERROR (Status)) {
774 CurrentHandlerInfoStruct->PhysicalAddress = HandlerPhysicalAddress;
775 DEBUG ((
776 DEBUG_INFO,
777 " %a %a: Found %a handler physical address at 0x%016x.\n",
778 _DBGMSGID_,
779 __FUNCTION__,
780 CurrentExportDescriptorHandlerName,
781 CurrentHandlerInfoStruct->PhysicalAddress
782 ));
783 }
784 }
785 CurrentModuleInfoStruct = (PRM_MODULE_INFORMATION_STRUCT *) ((UINTN) CurrentModuleInfoStruct + CurrentModuleInfoStruct->StructureLength);
786 }
787 *PrmAcpiDescriptionTable = PrmAcpiTable;
788
789 return EFI_SUCCESS;
790 }
791
792 /**
793 Publishes the PRM ACPI table (PRMT).
794
795 @param[in] PrmAcpiDescriptionTable A pointer to a buffer with a completely populated and valid PRM
796 ACPI description table.
797
798 @retval EFI_SUCCESS The PRM ACPI was installed successfully.
799 @retval EFI_INVALID_PARAMETER THe parameter PrmAcpiDescriptionTable is NULL or the table signature
800 in the table provided is invalid.
801 @retval EFI_NOT_FOUND The protocol gEfiAcpiTableProtocolGuid could not be found.
802 @retval EFI_OUT_OF_RESOURCES Insufficient memory resources to allocate the PRM ACPI table buffer.
803
804 **/
805 EFI_STATUS
806 PublishPrmAcpiTable (
807 IN PRM_ACPI_DESCRIPTION_TABLE *PrmAcpiDescriptionTable
808 )
809 {
810 EFI_STATUS Status;
811 EFI_ACPI_TABLE_PROTOCOL *AcpiTableProtocol;
812 UINTN TableKey;
813
814 if (PrmAcpiDescriptionTable == NULL || PrmAcpiDescriptionTable->Header.Signature != PRM_TABLE_SIGNATURE) {
815 return EFI_INVALID_PARAMETER;
816 }
817
818 Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTableProtocol);
819 if (!EFI_ERROR (Status)) {
820 TableKey = 0;
821 //
822 // Publish the PRM ACPI table. The table checksum will be computed during installation.
823 //
824 Status = AcpiTableProtocol->InstallAcpiTable (
825 AcpiTableProtocol,
826 PrmAcpiDescriptionTable,
827 PrmAcpiDescriptionTable->Header.Length,
828 &TableKey
829 );
830 if (!EFI_ERROR (Status)) {
831 DEBUG ((DEBUG_INFO, "%a %a: The PRMT ACPI table was installed successfully.\n", _DBGMSGID_, __FUNCTION__));
832 }
833 }
834 ASSERT_EFI_ERROR (Status);
835
836 return Status;
837 }
838
839 /**
840 The PRM Loader END_OF_DXE protocol notification event handler.
841
842 All PRM Modules that are eligible for dispatch should have been loaded the DXE Dispatcher at the
843 time of this function invocation.
844
845 The main responsibilities of the PRM Loader are executed from this function which include 3 phases:
846 1.) Disover PRM Modules - Find all PRM modules loaded during DXE dispatch and insert a PRM Module
847 Context entry into a linked list to be handed off to phase 2.
848 2.) Process PRM Modules - Build a GUID to PRM handler mapping for each module that is described in the
849 PRM ACPI table so the OS can resolve a PRM Handler GUID to the corresponding PRM Handler physical address.
850 3.) Publish PRM ACPI Table - Publish the PRM ACPI table with the information gathered in the phase 2.
851
852 @param[in] Event Event whose notification function is being invoked.
853 @param[in] Context The pointer to the notification function's context,
854 which is implementation-dependent.
855
856 @retval EFI_SUCCESS The function executed successfully
857
858 **/
859 EFI_STATUS
860 EFIAPI
861 PrmLoaderEndOfDxeNotification (
862 IN EFI_EVENT Event,
863 IN VOID *Context
864 )
865 {
866 EFI_STATUS Status;
867 PRM_ACPI_DESCRIPTION_TABLE *PrmAcpiDescriptionTable;
868
869 DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
870
871 InitializeListHead (&mPrmModuleList);
872
873 Status = DiscoverPrmModules ();
874 ASSERT_EFI_ERROR (Status);
875
876 Status = ProcessPrmModules (&PrmAcpiDescriptionTable);
877 ASSERT_EFI_ERROR (Status);
878
879 Status = PublishPrmAcpiTable (PrmAcpiDescriptionTable);
880 ASSERT_EFI_ERROR (Status);
881
882 if (PrmAcpiDescriptionTable != NULL) {
883 FreePool (PrmAcpiDescriptionTable);
884 }
885 gBS->CloseEvent (Event);
886
887 return EFI_SUCCESS;
888 }
889
890 /**
891 The entry point for this module.
892
893 @param ImageHandle The firmware allocated handle for the EFI image.
894 @param SystemTable A pointer to the EFI System Table.
895
896 @retval EFI_SUCCESS The entry point is executed successfully.
897 @retval Others An error occurred when executing this entry point.
898
899 **/
900 EFI_STATUS
901 EFIAPI
902 PrmLoaderEntryPoint (
903 IN EFI_HANDLE ImageHandle,
904 IN EFI_SYSTEM_TABLE *SystemTable
905 )
906 {
907 EFI_STATUS Status;
908 EFI_EVENT EndOfDxeEvent;
909
910 DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
911
912 //
913 // Discover and process installed PRM modules at the End of DXE
914 // The PRM ACPI table is published if one or PRM modules are discovered
915 //
916 Status = gBS->CreateEventEx(
917 EVT_NOTIFY_SIGNAL,
918 TPL_CALLBACK,
919 PrmLoaderEndOfDxeNotification,
920 NULL,
921 &gEfiEndOfDxeEventGroupGuid,
922 &EndOfDxeEvent
923 );
924 if (EFI_ERROR (Status)) {
925 DEBUG ((DEBUG_ERROR, "%a %a: EndOfDxe callback registration failed! %r.\n", _DBGMSGID_, __FUNCTION__, Status));
926 ASSERT_EFI_ERROR (Status);
927 }
928
929 return EFI_SUCCESS;
930 }