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