3 This file contains the implementation for a Platform Runtime Mechanism (PRM) configuration driver.
5 Copyright (c) Microsoft Corporation
6 Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include <Library/BaseLib.h>
12 #include <Library/BaseMemoryLib.h>
13 #include <Library/DebugLib.h>
14 #include <Library/DxeServicesTableLib.h>
15 #include <Library/MemoryAllocationLib.h>
16 #include <Library/UefiBootServicesTableLib.h>
17 #include <Library/UefiRuntimeServicesTableLib.h>
18 #include <Library/UefiLib.h>
21 #include <PrmContextBuffer.h>
22 #include <PrmDataBuffer.h>
24 #include <Protocol/PrmConfig.h>
26 #define _DBGMSGID_ "[PRMCONFIG]"
28 STATIC UINTN mMaxRuntimeMmioRangeCount
;
30 GLOBAL_REMOVE_IF_UNREFERENCED STATIC PRM_RUNTIME_MMIO_RANGES
**mRuntimeMmioRanges
;
33 Converts the runtime memory range physical addresses to virtual addresses.
35 @param[in] RuntimeMmioRanges A pointer to a PRM_RUNTIME_MMIO_RANGES buffer.
39 ConvertRuntimeMemoryRangeAddresses (
40 IN PRM_RUNTIME_MMIO_RANGES
*RuntimeMmioRanges
45 if ((RuntimeMmioRanges
== NULL
) || (RuntimeMmioRanges
->Count
== 0)) {
49 for (Index
= 0; Index
< (UINTN
)RuntimeMmioRanges
->Count
; Index
++) {
50 RuntimeMmioRanges
->Range
[Index
].VirtualBaseAddress
= RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
;
51 gRT
->ConvertPointer (0x0, (VOID
**)&(RuntimeMmioRanges
->Range
[Index
].VirtualBaseAddress
));
56 Sets the runtime memory range attributes.
58 The EFI_MEMORY_RUNTIME attribute is set for each PRM_RUNTIME_MMIO_RANGE present
59 in the buffer provided.
61 @param[in] RuntimeMmioRanges A pointer to a PRM_RUNTIME_MMIO_RANGES buffer.
65 SetRuntimeMemoryRangeAttributes (
66 IN PRM_RUNTIME_MMIO_RANGES
*RuntimeMmioRanges
72 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
74 DEBUG ((DEBUG_INFO
, "%a %a - Entry.\n", _DBGMSGID_
, __FUNCTION__
));
76 if ((RuntimeMmioRanges
== NULL
) || (RuntimeMmioRanges
->Count
== 0)) {
80 for (Index
= 0; Index
< (UINTN
)RuntimeMmioRanges
->Count
; Index
++) {
83 " %a %a: Runtime MMIO Range [%d].\n",
90 " %a %a: Physical address = 0x%016x. Length = 0x%x.\n",
93 RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
,
94 RuntimeMmioRanges
->Range
[Index
].Length
97 // Runtime memory ranges should cover ranges on a page boundary
98 ASSERT ((RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
& EFI_PAGE_MASK
) == 0);
99 ASSERT ((RuntimeMmioRanges
->Range
[Index
].Length
& EFI_PAGE_MASK
) == 0);
101 Status2
= EFI_NOT_FOUND
;
102 Status
= gDS
->GetMemorySpaceDescriptor (RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
, &Descriptor
);
103 if (!EFI_ERROR (Status
) &&
105 ((Descriptor
.GcdMemoryType
!= EfiGcdMemoryTypeMemoryMappedIo
) && (Descriptor
.GcdMemoryType
!= EfiGcdMemoryTypeReserved
)) ||
106 ((Descriptor
.Length
& EFI_PAGE_MASK
) != 0)
110 Status2
= gDS
->RemoveMemorySpace (
111 RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
,
116 if ((Status
== EFI_NOT_FOUND
) || !EFI_ERROR (Status2
)) {
117 Status
= gDS
->AddMemorySpace (
118 EfiGcdMemoryTypeMemoryMappedIo
,
119 RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
,
120 (UINT64
)RuntimeMmioRanges
->Range
[Index
].Length
,
121 EFI_MEMORY_UC
| EFI_MEMORY_RUNTIME
123 ASSERT_EFI_ERROR (Status
);
125 Status
= gDS
->AllocateMemorySpace (
126 EfiGcdAllocateAddress
,
127 EfiGcdMemoryTypeMemoryMappedIo
,
129 (UINT64
)RuntimeMmioRanges
->Range
[Index
].Length
,
130 &RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
,
134 ASSERT_EFI_ERROR (Status
);
137 Status
= gDS
->GetMemorySpaceDescriptor (RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
, &Descriptor
);
138 ASSERT_EFI_ERROR (Status
);
139 if (EFI_ERROR (Status
)) {
142 " %a %a: Error [%r] finding descriptor for runtime memory range 0x%016x.\n",
146 RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
151 if ((Descriptor
.Attributes
& EFI_MEMORY_RUNTIME
) != 0) {
155 Status
= gDS
->SetMemorySpaceAttributes (
156 RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
,
157 (UINT64
)RuntimeMmioRanges
->Range
[Index
].Length
,
158 Descriptor
.Attributes
| EFI_MEMORY_RUNTIME
160 ASSERT_EFI_ERROR (Status
);
161 if (EFI_ERROR (Status
)) {
164 " %a %a: Error [%r] setting descriptor for runtime memory range 0x%016x.\n",
168 RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
171 DEBUG ((DEBUG_INFO
, " %a %a: Successfully set runtime attribute for the MMIO range.\n", _DBGMSGID_
, __FUNCTION__
));
177 Stores pointers or pointer to resources that should be converted in the virtual address change event.
181 StoreVirtualMemoryAddressChangePointers (
189 EFI_HANDLE
*HandleBuffer
;
190 PRM_CONFIG_PROTOCOL
*PrmConfigProtocol
;
192 DEBUG ((DEBUG_INFO
, "%a %a - Entry.\n", _DBGMSGID_
, __FUNCTION__
));
196 mRuntimeMmioRanges
= AllocateRuntimeZeroPool (sizeof (*mRuntimeMmioRanges
) * mMaxRuntimeMmioRangeCount
);
197 if ((mRuntimeMmioRanges
== NULL
) && (mMaxRuntimeMmioRangeCount
> 0)) {
200 " %a %a: Memory allocation for runtime MMIO pointer array failed.\n",
209 Status
= gBS
->LocateHandleBuffer (
211 &gPrmConfigProtocolGuid
,
216 if (!EFI_ERROR (Status
)) {
217 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
218 Status
= gBS
->HandleProtocol (
219 HandleBuffer
[HandleIndex
],
220 &gPrmConfigProtocolGuid
,
221 (VOID
**)&PrmConfigProtocol
223 ASSERT_EFI_ERROR (Status
);
224 if (EFI_ERROR (Status
) || (PrmConfigProtocol
== NULL
)) {
228 if (PrmConfigProtocol
->ModuleContextBuffers
.RuntimeMmioRanges
!= NULL
) {
229 if (RangeIndex
>= mMaxRuntimeMmioRangeCount
) {
230 Status
= EFI_BUFFER_TOO_SMALL
;
233 " %a %a: Index out of bounds - Actual count (%d) of runtime MMIO ranges exceeds maximum count (%d).\n",
237 mMaxRuntimeMmioRangeCount
239 ASSERT_EFI_ERROR (Status
);
243 mRuntimeMmioRanges
[RangeIndex
++] = PrmConfigProtocol
->ModuleContextBuffers
.RuntimeMmioRanges
;
249 " %a %a: %d MMIO ranges buffers saved for future virtual memory conversion.\n",
258 Validates a data buffer for a PRM module.
260 Verifies the buffer header signature is valid and the length meets the minimum size.
262 @param[in] PrmDataBuffer A pointer to the data buffer for this PRM module.
264 @retval EFI_SUCCESS The data buffer was validated successfully.
265 @retval EFI_INVALID_PARAMETER The pointer given for PrmDataBuffer is NULL.
266 @retval EFI_NOT_FOUND The data buffer signature is not valid.
267 @retval EFI_BUFFER_TOO_SMALL The buffer size is too small.
271 ValidatePrmDataBuffer (
272 IN CONST PRM_DATA_BUFFER
*PrmDataBuffer
275 if (PrmDataBuffer
== NULL
) {
276 return EFI_INVALID_PARAMETER
;
279 if (PrmDataBuffer
->Header
.Signature
!= PRM_DATA_BUFFER_HEADER_SIGNATURE
) {
280 DEBUG ((DEBUG_ERROR
, " %a %a: The PRM data buffer signature is invalid. PRM module.\n", _DBGMSGID_
, __FUNCTION__
));
281 return EFI_NOT_FOUND
;
284 if (PrmDataBuffer
->Header
.Length
< sizeof (PRM_DATA_BUFFER_HEADER
)) {
285 DEBUG ((DEBUG_ERROR
, " %a %a: The PRM data buffer length is invalid.\n", _DBGMSGID_
, __FUNCTION__
));
286 return EFI_BUFFER_TOO_SMALL
;
293 Validates a PRM context buffer.
295 Verifies the buffer header signature is valid and the GUID is set to a non-zero value.
297 @param[in] PrmContextBuffer A pointer to the context buffer for this PRM handler.
299 @retval EFI_SUCCESS The context buffer was validated successfully.
300 @retval EFI_INVALID_PARAMETER The pointer given for ContextBuffer is NULL.
301 @retval EFI_NOT_FOUND The proper value for a field was not found.
305 ValidatePrmContextBuffer (
306 IN CONST PRM_CONTEXT_BUFFER
*PrmContextBuffer
309 if (PrmContextBuffer
== NULL
) {
310 return EFI_INVALID_PARAMETER
;
313 if (PrmContextBuffer
->Signature
!= PRM_CONTEXT_BUFFER_SIGNATURE
) {
314 DEBUG ((DEBUG_ERROR
, " %a %a: The PRM context buffer signature is invalid.\n", _DBGMSGID_
, __FUNCTION__
));
315 return EFI_NOT_FOUND
;
318 if (IsZeroGuid (&PrmContextBuffer
->HandlerGuid
)) {
319 DEBUG ((DEBUG_ERROR
, " %a %a: The PRM context buffer GUID is zero.\n", _DBGMSGID_
, __FUNCTION__
));
320 return EFI_NOT_FOUND
;
323 if ((PrmContextBuffer
->StaticDataBuffer
!= NULL
) && EFI_ERROR (ValidatePrmDataBuffer (PrmContextBuffer
->StaticDataBuffer
))) {
326 " %a %a: Error in static buffer for PRM handler %g.\n",
329 &PrmContextBuffer
->HandlerGuid
331 return EFI_NOT_FOUND
;
338 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
340 This is notification function converts any registered PRM_RUNTIME_MMIO_RANGE
341 addresses to a virtual address.
343 @param[in] Event Event whose notification function is being invoked.
344 @param[in] Context Pointer to the notification function's context.
349 PrmConfigVirtualAddressChangeEvent (
357 // Convert runtime MMIO ranges
359 for (Index
= 0; Index
< mMaxRuntimeMmioRangeCount
; Index
++) {
360 ConvertRuntimeMemoryRangeAddresses (mRuntimeMmioRanges
[Index
]);
365 The PRM Config END_OF_DXE protocol notification event handler.
367 Finds all of the PRM_CONFIG_PROTOCOL instances installed at end of DXE and
368 marks all PRM_RUNTIME_MMIO_RANGE entries as EFI_MEMORY_RUNTIME.
370 @param[in] Event Event whose notification function is being invoked.
371 @param[in] Context The pointer to the notification function's context,
372 which is implementation-dependent.
377 PrmConfigEndOfDxeNotification (
386 EFI_HANDLE
*HandleBuffer
;
387 PRM_CONTEXT_BUFFER
*CurrentContextBuffer
;
388 PRM_CONFIG_PROTOCOL
*PrmConfigProtocol
;
390 DEBUG ((DEBUG_INFO
, "%a %a - Entry.\n", _DBGMSGID_
, __FUNCTION__
));
393 Status
= gBS
->LocateHandleBuffer (
395 &gPrmConfigProtocolGuid
,
400 if (!EFI_ERROR (Status
)) {
401 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
402 Status
= gBS
->HandleProtocol (
403 HandleBuffer
[HandleIndex
],
404 &gPrmConfigProtocolGuid
,
405 (VOID
**)&PrmConfigProtocol
407 ASSERT_EFI_ERROR (Status
);
408 if (EFI_ERROR (Status
) || (PrmConfigProtocol
== NULL
)) {
414 " %a %a: Found PRM configuration protocol for PRM module %g.\n",
417 &PrmConfigProtocol
->ModuleContextBuffers
.ModuleGuid
420 DEBUG ((DEBUG_INFO
, " %a %a: Validating module context buffers...\n", _DBGMSGID_
, __FUNCTION__
));
421 for (BufferIndex
= 0; BufferIndex
< PrmConfigProtocol
->ModuleContextBuffers
.BufferCount
; BufferIndex
++) {
422 CurrentContextBuffer
= &(PrmConfigProtocol
->ModuleContextBuffers
.Buffer
[BufferIndex
]);
424 Status
= ValidatePrmContextBuffer (CurrentContextBuffer
);
425 if (EFI_ERROR (Status
)) {
428 " %a %a: Context buffer validation failed for PRM handler %g.\n",
431 CurrentContextBuffer
->HandlerGuid
436 DEBUG ((DEBUG_INFO
, " %a %a: Module context buffer validation complete.\n", _DBGMSGID_
, __FUNCTION__
));
438 if (PrmConfigProtocol
->ModuleContextBuffers
.RuntimeMmioRanges
!= NULL
) {
441 " %a %a: Found %d PRM runtime MMIO ranges.\n",
444 PrmConfigProtocol
->ModuleContextBuffers
.RuntimeMmioRanges
->Count
446 SetRuntimeMemoryRangeAttributes (PrmConfigProtocol
->ModuleContextBuffers
.RuntimeMmioRanges
);
447 mMaxRuntimeMmioRangeCount
++;
451 StoreVirtualMemoryAddressChangePointers ();
454 if (HandleBuffer
!= NULL
) {
455 gBS
->FreePool (HandleBuffer
);
458 gBS
->CloseEvent (Event
);
462 The entry point for this module.
464 @param[in] ImageHandle The firmware allocated handle for the EFI image.
465 @param[in] SystemTable A pointer to the EFI System Table.
467 @retval EFI_SUCCESS The entry point is executed successfully.
468 @retval Others An error occurred when executing this entry point.
473 PrmConfigEntryPoint (
474 IN EFI_HANDLE ImageHandle
,
475 IN EFI_SYSTEM_TABLE
*SystemTable
481 DEBUG ((DEBUG_INFO
, "%a %a - Entry.\n", _DBGMSGID_
, __FUNCTION__
));
484 // Register a notification function to change memory attributes at end of DXE
487 Status
= gBS
->CreateEventEx (
490 PrmConfigEndOfDxeNotification
,
492 &gEfiEndOfDxeEventGroupGuid
,
495 ASSERT_EFI_ERROR (Status
);
498 // Register a notification function for virtual address change
501 Status
= gBS
->CreateEventEx (
504 PrmConfigVirtualAddressChangeEvent
,
506 &gEfiEventVirtualAddressChangeGuid
,
509 ASSERT_EFI_ERROR (Status
);