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