]> git.proxmox.com Git - mirror_edk2.git/blob - PrmPkg/PrmConfigDxe/PrmConfigDxe.c
PrmPkg: Enforce stricter types
[mirror_edk2.git] / PrmPkg / PrmConfigDxe / PrmConfigDxe.c
1 /** @file
2
3 This file contains the implementation for a Platform Runtime Mechanism (PRM) configuration driver.
4
5 Copyright (c) Microsoft Corporation
6 Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
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>
19
20 #include <PiDxe.h>
21 #include <PrmContextBuffer.h>
22 #include <PrmDataBuffer.h>
23 #include <PrmMmio.h>
24 #include <Protocol/PrmConfig.h>
25
26 #define _DBGMSGID_ "[PRMCONFIG]"
27
28 STATIC UINTN mMaxRuntimeMmioRangeCount;
29
30 GLOBAL_REMOVE_IF_UNREFERENCED STATIC PRM_RUNTIME_MMIO_RANGES **mRuntimeMmioRanges;
31
32 /**
33 Converts the runtime memory range physical addresses to virtual addresses.
34
35 @param[in] RuntimeMmioRanges A pointer to a PRM_RUNTIME_MMIO_RANGES buffer.
36
37 **/
38 VOID
39 ConvertRuntimeMemoryRangeAddresses (
40 IN PRM_RUNTIME_MMIO_RANGES *RuntimeMmioRanges
41 )
42 {
43 UINTN Index;
44
45 if (RuntimeMmioRanges == NULL || RuntimeMmioRanges->Count == 0) {
46 return;
47 }
48
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));
52 }
53 }
54
55 /**
56 Sets the runtime memory range attributes.
57
58 The EFI_MEMORY_RUNTIME attribute is set for each PRM_RUNTIME_MMIO_RANGE present
59 in the buffer provided.
60
61 @param[in] RuntimeMmioRanges A pointer to a PRM_RUNTIME_MMIO_RANGES buffer.
62
63 **/
64 VOID
65 SetRuntimeMemoryRangeAttributes (
66 IN PRM_RUNTIME_MMIO_RANGES *RuntimeMmioRanges
67 )
68 {
69 EFI_STATUS Status;
70 EFI_STATUS Status2;
71 UINTN Index;
72 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
73
74 DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
75
76 if (RuntimeMmioRanges == NULL || RuntimeMmioRanges->Count == 0) {
77 return;
78 }
79
80 for (Index = 0; Index < (UINTN) RuntimeMmioRanges->Count; Index++) {
81 DEBUG ((
82 DEBUG_INFO, " %a %a: Runtime MMIO Range [%d].\n", _DBGMSGID_, __FUNCTION__, Index));
83 DEBUG ((
84 DEBUG_INFO,
85 " %a %a: Physical address = 0x%016x. Length = 0x%x.\n",
86 _DBGMSGID_,
87 __FUNCTION__,
88 RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
89 RuntimeMmioRanges->Range[Index].Length
90 ));
91
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);
95
96 Status2 = EFI_NOT_FOUND;
97 Status = gDS->GetMemorySpaceDescriptor (RuntimeMmioRanges->Range[Index].PhysicalBaseAddress, &Descriptor);
98 if (!EFI_ERROR (Status) &&
99 (
100 (Descriptor.GcdMemoryType != EfiGcdMemoryTypeMemoryMappedIo && Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) ||
101 ((Descriptor.Length & EFI_PAGE_MASK) != 0)
102 )
103 ) {
104 Status2 = gDS->RemoveMemorySpace (
105 RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
106 Descriptor.Length
107 );
108 }
109
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
116 );
117 ASSERT_EFI_ERROR (Status);
118
119 Status = gDS->AllocateMemorySpace (
120 EfiGcdAllocateAddress,
121 EfiGcdMemoryTypeMemoryMappedIo,
122 0,
123 (UINT64) RuntimeMmioRanges->Range[Index].Length,
124 &RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
125 gImageHandle,
126 NULL
127 );
128 ASSERT_EFI_ERROR (Status);
129 }
130
131 Status = gDS->GetMemorySpaceDescriptor (RuntimeMmioRanges->Range[Index].PhysicalBaseAddress, &Descriptor);
132 ASSERT_EFI_ERROR (Status);
133 if (EFI_ERROR (Status)) {
134 DEBUG ((
135 DEBUG_ERROR,
136 " %a %a: Error [%r] finding descriptor for runtime memory range 0x%016x.\n",
137 _DBGMSGID_,
138 __FUNCTION__,
139 Status,
140 RuntimeMmioRanges->Range[Index].PhysicalBaseAddress
141 ));
142 continue;
143 }
144 if ((Descriptor.Attributes & EFI_MEMORY_RUNTIME) != 0) {
145 continue;
146 }
147
148 Status = gDS->SetMemorySpaceAttributes (
149 RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
150 (UINT64) RuntimeMmioRanges->Range[Index].Length,
151 Descriptor.Attributes | EFI_MEMORY_RUNTIME
152 );
153 ASSERT_EFI_ERROR (Status);
154 if (EFI_ERROR (Status)) {
155 DEBUG ((
156 DEBUG_ERROR,
157 " %a %a: Error [%r] setting descriptor for runtime memory range 0x%016x.\n",
158 _DBGMSGID_,
159 __FUNCTION__,
160 Status,
161 RuntimeMmioRanges->Range[Index].PhysicalBaseAddress
162 ));
163 } else {
164 DEBUG ((DEBUG_INFO, " %a %a: Successfully set runtime attribute for the MMIO range.\n", _DBGMSGID_, __FUNCTION__));
165 }
166 }
167 }
168
169 /**
170 Stores pointers or pointer to resources that should be converted in the virtual address change event.
171
172 **/
173 VOID
174 StoreVirtualMemoryAddressChangePointers (
175 VOID
176 )
177 {
178 EFI_STATUS Status;
179 UINTN HandleCount;
180 UINTN HandleIndex;
181 UINTN RangeIndex;
182 EFI_HANDLE *HandleBuffer;
183 PRM_CONFIG_PROTOCOL *PrmConfigProtocol;
184
185 DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
186
187 RangeIndex = 0;
188
189 mRuntimeMmioRanges = AllocateRuntimeZeroPool (sizeof (*mRuntimeMmioRanges) * mMaxRuntimeMmioRangeCount);
190 if (mRuntimeMmioRanges == NULL && mMaxRuntimeMmioRangeCount > 0) {
191 DEBUG ((
192 DEBUG_ERROR,
193 " %a %a: Memory allocation for runtime MMIO pointer array failed.\n",
194 _DBGMSGID_,
195 __FUNCTION__
196 ));
197 ASSERT (FALSE);
198 return;
199 }
200
201 HandleBuffer = NULL;
202 Status = gBS->LocateHandleBuffer (
203 ByProtocol,
204 &gPrmConfigProtocolGuid,
205 NULL,
206 &HandleCount,
207 &HandleBuffer
208 );
209 if (!EFI_ERROR (Status)) {
210 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
211 Status = gBS->HandleProtocol (
212 HandleBuffer[HandleIndex],
213 &gPrmConfigProtocolGuid,
214 (VOID **) &PrmConfigProtocol
215 );
216 ASSERT_EFI_ERROR (Status);
217 if (EFI_ERROR (Status) || PrmConfigProtocol == NULL) {
218 continue;
219 }
220
221 if (PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges != NULL) {
222 if (RangeIndex >= mMaxRuntimeMmioRangeCount) {
223 Status = EFI_BUFFER_TOO_SMALL;
224 DEBUG ((
225 DEBUG_ERROR,
226 " %a %a: Index out of bounds - Actual count (%d) of runtime MMIO ranges exceeds maximum count (%d).\n",
227 _DBGMSGID_,
228 __FUNCTION__,
229 RangeIndex + 1,
230 mMaxRuntimeMmioRangeCount
231 ));
232 ASSERT_EFI_ERROR (Status);
233 return;
234 }
235 mRuntimeMmioRanges[RangeIndex++] = PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges;
236 }
237 }
238 DEBUG ((
239 DEBUG_INFO,
240 " %a %a: %d MMIO ranges buffers saved for future virtual memory conversion.\n",
241 _DBGMSGID_,
242 __FUNCTION__,
243 RangeIndex
244 ));
245 }
246 }
247
248 /**
249 Validates a data buffer for a PRM module.
250
251 Verifies the buffer header signature is valid and the length meets the minimum size.
252
253 @param[in] PrmDataBuffer A pointer to the data buffer for this PRM module.
254
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.
259
260 **/
261 EFI_STATUS
262 ValidatePrmDataBuffer (
263 IN CONST PRM_DATA_BUFFER *PrmDataBuffer
264 )
265 {
266 if (PrmDataBuffer == NULL) {
267 return EFI_INVALID_PARAMETER;
268 }
269
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;
273 }
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;
277 }
278
279 return EFI_SUCCESS;
280 }
281
282 /**
283 Validates a PRM context buffer.
284
285 Verifies the buffer header signature is valid and the GUID is set to a non-zero value.
286
287 @param[in] PrmContextBuffer A pointer to the context buffer for this PRM handler.
288
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.
292
293 **/
294 EFI_STATUS
295 ValidatePrmContextBuffer (
296 IN CONST PRM_CONTEXT_BUFFER *PrmContextBuffer
297 )
298 {
299 if (PrmContextBuffer == NULL) {
300 return EFI_INVALID_PARAMETER;
301 }
302
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;
306 }
307
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;
311 }
312
313 if (PrmContextBuffer->StaticDataBuffer != NULL && EFI_ERROR (ValidatePrmDataBuffer (PrmContextBuffer->StaticDataBuffer))) {
314 DEBUG ((
315 DEBUG_ERROR,
316 " %a %a: Error in static buffer for PRM handler %g.\n",
317 _DBGMSGID_,
318 __FUNCTION__,
319 &PrmContextBuffer->HandlerGuid
320 ));
321 return EFI_NOT_FOUND;
322 }
323
324 return EFI_SUCCESS;
325 }
326
327 /**
328 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
329
330 This is notification function converts any registered PRM_RUNTIME_MMIO_RANGE
331 addresses to a virtual address.
332
333 @param[in] Event Event whose notification function is being invoked.
334 @param[in] Context Pointer to the notification function's context.
335
336 **/
337 VOID
338 EFIAPI
339 PrmConfigVirtualAddressChangeEvent (
340 IN EFI_EVENT Event,
341 IN VOID *Context
342 )
343 {
344 UINTN Index;
345
346 //
347 // Convert runtime MMIO ranges
348 //
349 for (Index = 0; Index < mMaxRuntimeMmioRangeCount; Index++) {
350 ConvertRuntimeMemoryRangeAddresses (mRuntimeMmioRanges[Index]);
351 }
352 }
353
354 /**
355 The PRM Config END_OF_DXE protocol notification event handler.
356
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.
359
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.
363
364 **/
365 VOID
366 EFIAPI
367 PrmConfigEndOfDxeNotification (
368 IN EFI_EVENT Event,
369 IN VOID *Context
370 )
371 {
372 EFI_STATUS Status;
373 UINTN HandleCount;
374 UINTN BufferIndex;
375 UINTN HandleIndex;
376 EFI_HANDLE *HandleBuffer;
377 PRM_CONTEXT_BUFFER *CurrentContextBuffer;
378 PRM_CONFIG_PROTOCOL *PrmConfigProtocol;
379
380 DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
381
382 HandleBuffer = NULL;
383 Status = gBS->LocateHandleBuffer (
384 ByProtocol,
385 &gPrmConfigProtocolGuid,
386 NULL,
387 &HandleCount,
388 &HandleBuffer
389 );
390 if (!EFI_ERROR (Status)) {
391 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
392 Status = gBS->HandleProtocol (
393 HandleBuffer[HandleIndex],
394 &gPrmConfigProtocolGuid,
395 (VOID **) &PrmConfigProtocol
396 );
397 ASSERT_EFI_ERROR (Status);
398 if (EFI_ERROR (Status) || PrmConfigProtocol == NULL) {
399 continue;
400 }
401
402 DEBUG ((
403 DEBUG_INFO,
404 " %a %a: Found PRM configuration protocol for PRM module %g.\n",
405 _DBGMSGID_,
406 __FUNCTION__,
407 &PrmConfigProtocol->ModuleContextBuffers.ModuleGuid
408 ));
409
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]);
413
414 Status = ValidatePrmContextBuffer (CurrentContextBuffer);
415 if (EFI_ERROR (Status)) {
416 DEBUG ((
417 DEBUG_ERROR,
418 " %a %a: Context buffer validation failed for PRM handler %g.\n",
419 _DBGMSGID_,
420 __FUNCTION__,
421 CurrentContextBuffer->HandlerGuid
422 ));
423 }
424 }
425 DEBUG ((DEBUG_INFO, " %a %a: Module context buffer validation complete.\n", _DBGMSGID_, __FUNCTION__));
426
427 if (PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges != NULL) {
428 DEBUG ((
429 DEBUG_INFO,
430 " %a %a: Found %d PRM runtime MMIO ranges.\n",
431 _DBGMSGID_,
432 __FUNCTION__,
433 PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges->Count
434 ));
435 SetRuntimeMemoryRangeAttributes (PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges);
436 mMaxRuntimeMmioRangeCount++;
437 }
438 }
439
440 StoreVirtualMemoryAddressChangePointers ();
441 }
442
443 if (HandleBuffer != NULL) {
444 gBS->FreePool (HandleBuffer);
445 }
446 gBS->CloseEvent(Event);
447 }
448
449 /**
450 The entry point for this module.
451
452 @param[in] ImageHandle The firmware allocated handle for the EFI image.
453 @param[in] SystemTable A pointer to the EFI System Table.
454
455 @retval EFI_SUCCESS The entry point is executed successfully.
456 @retval Others An error occurred when executing this entry point.
457
458 **/
459 EFI_STATUS
460 EFIAPI
461 PrmConfigEntryPoint (
462 IN EFI_HANDLE ImageHandle,
463 IN EFI_SYSTEM_TABLE *SystemTable
464 )
465 {
466 EFI_STATUS Status;
467 EFI_EVENT Event;
468
469 DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
470
471 //
472 // Register a notification function to change memory attributes at end of DXE
473 //
474 Event = NULL;
475 Status = gBS->CreateEventEx (
476 EVT_NOTIFY_SIGNAL,
477 TPL_CALLBACK,
478 PrmConfigEndOfDxeNotification,
479 NULL,
480 &gEfiEndOfDxeEventGroupGuid,
481 &Event
482 );
483 ASSERT_EFI_ERROR (Status);
484
485 //
486 // Register a notification function for virtual address change
487 //
488 Event = NULL;
489 Status = gBS->CreateEventEx (
490 EVT_NOTIFY_SIGNAL,
491 TPL_NOTIFY,
492 PrmConfigVirtualAddressChangeEvent,
493 NULL,
494 &gEfiEventVirtualAddressChangeGuid,
495 &Event
496 );
497 ASSERT_EFI_ERROR (Status);
498
499 return EFI_SUCCESS;
500 }