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
++) {
82 DEBUG_INFO
, " %a %a: Runtime MMIO Range [%d].\n", _DBGMSGID_
, __FUNCTION__
, Index
));
85 " %a %a: Physical address = 0x%016x. Length = 0x%x.\n",
88 RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
,
89 RuntimeMmioRanges
->Range
[Index
].Length
92 // Runtime memory ranges should cover ranges on a page boundary
93 ASSERT ((RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
& EFI_PAGE_MASK
) == 0);
94 ASSERT ((RuntimeMmioRanges
->Range
[Index
].Length
& EFI_PAGE_MASK
) == 0);
96 Status2
= EFI_NOT_FOUND
;
97 Status
= gDS
->GetMemorySpaceDescriptor (RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
, &Descriptor
);
98 if (!EFI_ERROR (Status
) &&
100 (Descriptor
.GcdMemoryType
!= EfiGcdMemoryTypeMemoryMappedIo
&& Descriptor
.GcdMemoryType
!= EfiGcdMemoryTypeReserved
) ||
101 ((Descriptor
.Length
& EFI_PAGE_MASK
) != 0)
104 Status2
= gDS
->RemoveMemorySpace (
105 RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
,
110 if (Status
== EFI_NOT_FOUND
|| !EFI_ERROR (Status2
)) {
111 Status
= gDS
->AddMemorySpace (
112 EfiGcdMemoryTypeMemoryMappedIo
,
113 RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
,
114 (UINT64
) RuntimeMmioRanges
->Range
[Index
].Length
,
115 EFI_MEMORY_UC
| EFI_MEMORY_RUNTIME
117 ASSERT_EFI_ERROR (Status
);
119 Status
= gDS
->AllocateMemorySpace (
120 EfiGcdAllocateAddress
,
121 EfiGcdMemoryTypeMemoryMappedIo
,
123 (UINT64
) RuntimeMmioRanges
->Range
[Index
].Length
,
124 &RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
,
128 ASSERT_EFI_ERROR (Status
);
131 Status
= gDS
->GetMemorySpaceDescriptor (RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
, &Descriptor
);
132 ASSERT_EFI_ERROR (Status
);
133 if (EFI_ERROR (Status
)) {
136 " %a %a: Error [%r] finding descriptor for runtime memory range 0x%016x.\n",
140 RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
144 if ((Descriptor
.Attributes
& EFI_MEMORY_RUNTIME
) != 0) {
148 Status
= gDS
->SetMemorySpaceAttributes (
149 RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
,
150 (UINT64
) RuntimeMmioRanges
->Range
[Index
].Length
,
151 Descriptor
.Attributes
| EFI_MEMORY_RUNTIME
153 ASSERT_EFI_ERROR (Status
);
154 if (EFI_ERROR (Status
)) {
157 " %a %a: Error [%r] setting descriptor for runtime memory range 0x%016x.\n",
161 RuntimeMmioRanges
->Range
[Index
].PhysicalBaseAddress
164 DEBUG ((DEBUG_INFO
, " %a %a: Successfully set runtime attribute for the MMIO range.\n", _DBGMSGID_
, __FUNCTION__
));
170 Stores pointers or pointer to resources that should be converted in the virtual address change event.
174 StoreVirtualMemoryAddressChangePointers (
182 EFI_HANDLE
*HandleBuffer
;
183 PRM_CONFIG_PROTOCOL
*PrmConfigProtocol
;
185 DEBUG ((DEBUG_INFO
, "%a %a - Entry.\n", _DBGMSGID_
, __FUNCTION__
));
189 mRuntimeMmioRanges
= AllocateRuntimeZeroPool (sizeof (*mRuntimeMmioRanges
) * mMaxRuntimeMmioRangeCount
);
190 if (mRuntimeMmioRanges
== NULL
&& mMaxRuntimeMmioRangeCount
> 0) {
193 " %a %a: Memory allocation for runtime MMIO pointer array failed.\n",
202 Status
= gBS
->LocateHandleBuffer (
204 &gPrmConfigProtocolGuid
,
209 if (!EFI_ERROR (Status
)) {
210 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
211 Status
= gBS
->HandleProtocol (
212 HandleBuffer
[HandleIndex
],
213 &gPrmConfigProtocolGuid
,
214 (VOID
**) &PrmConfigProtocol
216 ASSERT_EFI_ERROR (Status
);
217 if (EFI_ERROR (Status
) || PrmConfigProtocol
== NULL
) {
221 if (PrmConfigProtocol
->ModuleContextBuffers
.RuntimeMmioRanges
!= NULL
) {
222 if (RangeIndex
>= mMaxRuntimeMmioRangeCount
) {
223 Status
= EFI_BUFFER_TOO_SMALL
;
226 " %a %a: Index out of bounds - Actual count (%d) of runtime MMIO ranges exceeds maximum count (%d).\n",
230 mMaxRuntimeMmioRangeCount
232 ASSERT_EFI_ERROR (Status
);
235 mRuntimeMmioRanges
[RangeIndex
++] = PrmConfigProtocol
->ModuleContextBuffers
.RuntimeMmioRanges
;
240 " %a %a: %d MMIO ranges buffers saved for future virtual memory conversion.\n",
249 Validates a data buffer for a PRM module.
251 Verifies the buffer header signature is valid and the length meets the minimum size.
253 @param[in] PrmDataBuffer A pointer to the data buffer for this PRM module.
255 @retval EFI_SUCCESS The data buffer was validated successfully.
256 @retval EFI_INVALID_PARAMETER The pointer given for PrmDataBuffer is NULL.
257 @retval EFI_NOT_FOUND The data buffer signature is not valid.
258 @retval EFI_BUFFER_TOO_SMALL The buffer size is too small.
262 ValidatePrmDataBuffer (
263 IN CONST PRM_DATA_BUFFER
*PrmDataBuffer
266 if (PrmDataBuffer
== NULL
) {
267 return EFI_INVALID_PARAMETER
;
270 if (PrmDataBuffer
->Header
.Signature
!= PRM_DATA_BUFFER_HEADER_SIGNATURE
) {
271 DEBUG ((DEBUG_ERROR
, " %a %a: The PRM data buffer signature is invalid. PRM module.\n", _DBGMSGID_
, __FUNCTION__
));
272 return EFI_NOT_FOUND
;
274 if (PrmDataBuffer
->Header
.Length
< sizeof (PRM_DATA_BUFFER_HEADER
)) {
275 DEBUG ((DEBUG_ERROR
, " %a %a: The PRM data buffer length is invalid.\n", _DBGMSGID_
, __FUNCTION__
));
276 return EFI_BUFFER_TOO_SMALL
;
283 Validates a PRM context buffer.
285 Verifies the buffer header signature is valid and the GUID is set to a non-zero value.
287 @param[in] PrmContextBuffer A pointer to the context buffer for this PRM handler.
289 @retval EFI_SUCCESS The context buffer was validated successfully.
290 @retval EFI_INVALID_PARAMETER The pointer given for ContextBuffer is NULL.
291 @retval EFI_NOT_FOUND The proper value for a field was not found.
295 ValidatePrmContextBuffer (
296 IN CONST PRM_CONTEXT_BUFFER
*PrmContextBuffer
299 if (PrmContextBuffer
== NULL
) {
300 return EFI_INVALID_PARAMETER
;
303 if (PrmContextBuffer
->Signature
!= PRM_CONTEXT_BUFFER_SIGNATURE
) {
304 DEBUG ((DEBUG_ERROR
, " %a %a: The PRM context buffer signature is invalid.\n", _DBGMSGID_
, __FUNCTION__
));
305 return EFI_NOT_FOUND
;
308 if (IsZeroGuid (&PrmContextBuffer
->HandlerGuid
)) {
309 DEBUG ((DEBUG_ERROR
, " %a %a: The PRM context buffer GUID is zero.\n", _DBGMSGID_
, __FUNCTION__
));
310 return EFI_NOT_FOUND
;
313 if (PrmContextBuffer
->StaticDataBuffer
!= NULL
&& EFI_ERROR (ValidatePrmDataBuffer (PrmContextBuffer
->StaticDataBuffer
))) {
316 " %a %a: Error in static buffer for PRM handler %g.\n",
319 &PrmContextBuffer
->HandlerGuid
321 return EFI_NOT_FOUND
;
328 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
330 This is notification function converts any registered PRM_RUNTIME_MMIO_RANGE
331 addresses to a virtual address.
333 @param[in] Event Event whose notification function is being invoked.
334 @param[in] Context Pointer to the notification function's context.
339 PrmConfigVirtualAddressChangeEvent (
347 // Convert runtime MMIO ranges
349 for (Index
= 0; Index
< mMaxRuntimeMmioRangeCount
; Index
++) {
350 ConvertRuntimeMemoryRangeAddresses (mRuntimeMmioRanges
[Index
]);
355 The PRM Config END_OF_DXE protocol notification event handler.
357 Finds all of the PRM_CONFIG_PROTOCOL instances installed at end of DXE and
358 marks all PRM_RUNTIME_MMIO_RANGE entries as EFI_MEMORY_RUNTIME.
360 @param[in] Event Event whose notification function is being invoked.
361 @param[in] Context The pointer to the notification function's context,
362 which is implementation-dependent.
367 PrmConfigEndOfDxeNotification (
376 EFI_HANDLE
*HandleBuffer
;
377 PRM_CONTEXT_BUFFER
*CurrentContextBuffer
;
378 PRM_CONFIG_PROTOCOL
*PrmConfigProtocol
;
380 DEBUG ((DEBUG_INFO
, "%a %a - Entry.\n", _DBGMSGID_
, __FUNCTION__
));
383 Status
= gBS
->LocateHandleBuffer (
385 &gPrmConfigProtocolGuid
,
390 if (!EFI_ERROR (Status
)) {
391 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
392 Status
= gBS
->HandleProtocol (
393 HandleBuffer
[HandleIndex
],
394 &gPrmConfigProtocolGuid
,
395 (VOID
**) &PrmConfigProtocol
397 ASSERT_EFI_ERROR (Status
);
398 if (EFI_ERROR (Status
) || PrmConfigProtocol
== NULL
) {
404 " %a %a: Found PRM configuration protocol for PRM module %g.\n",
407 &PrmConfigProtocol
->ModuleContextBuffers
.ModuleGuid
410 DEBUG ((DEBUG_INFO
, " %a %a: Validating module context buffers...\n", _DBGMSGID_
, __FUNCTION__
));
411 for (BufferIndex
= 0; BufferIndex
< PrmConfigProtocol
->ModuleContextBuffers
.BufferCount
; BufferIndex
++) {
412 CurrentContextBuffer
= &(PrmConfigProtocol
->ModuleContextBuffers
.Buffer
[BufferIndex
]);
414 Status
= ValidatePrmContextBuffer (CurrentContextBuffer
);
415 if (EFI_ERROR (Status
)) {
418 " %a %a: Context buffer validation failed for PRM handler %g.\n",
421 CurrentContextBuffer
->HandlerGuid
425 DEBUG ((DEBUG_INFO
, " %a %a: Module context buffer validation complete.\n", _DBGMSGID_
, __FUNCTION__
));
427 if (PrmConfigProtocol
->ModuleContextBuffers
.RuntimeMmioRanges
!= NULL
) {
430 " %a %a: Found %d PRM runtime MMIO ranges.\n",
433 PrmConfigProtocol
->ModuleContextBuffers
.RuntimeMmioRanges
->Count
435 SetRuntimeMemoryRangeAttributes (PrmConfigProtocol
->ModuleContextBuffers
.RuntimeMmioRanges
);
436 mMaxRuntimeMmioRangeCount
++;
440 StoreVirtualMemoryAddressChangePointers ();
443 if (HandleBuffer
!= NULL
) {
444 gBS
->FreePool (HandleBuffer
);
446 gBS
->CloseEvent(Event
);
450 The entry point for this module.
452 @param[in] ImageHandle The firmware allocated handle for the EFI image.
453 @param[in] SystemTable A pointer to the EFI System Table.
455 @retval EFI_SUCCESS The entry point is executed successfully.
456 @retval Others An error occurred when executing this entry point.
461 PrmConfigEntryPoint (
462 IN EFI_HANDLE ImageHandle
,
463 IN EFI_SYSTEM_TABLE
*SystemTable
469 DEBUG ((DEBUG_INFO
, "%a %a - Entry.\n", _DBGMSGID_
, __FUNCTION__
));
472 // Register a notification function to change memory attributes at end of DXE
475 Status
= gBS
->CreateEventEx (
478 PrmConfigEndOfDxeNotification
,
480 &gEfiEndOfDxeEventGroupGuid
,
483 ASSERT_EFI_ERROR (Status
);
486 // Register a notification function for virtual address change
489 Status
= gBS
->CreateEventEx (
492 PrmConfigVirtualAddressChangeEvent
,
494 &gEfiEventVirtualAddressChangeGuid
,
497 ASSERT_EFI_ERROR (Status
);