]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Variable/RuntimeDxe/VariablePolicySmmDxe.c
MdeModulePkg/PlatformDriOverrideDxe: Fix overflow condition check
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / RuntimeDxe / VariablePolicySmmDxe.c
1 /** @file -- VariablePolicySmmDxe.c
2 This protocol allows communication with Variable Policy Engine.
3
4 Copyright (c) Microsoft Corporation.
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include <Library/BaseLib.h>
10 #include <Library/UefiLib.h>
11 #include <Library/DebugLib.h>
12 #include <Library/SafeIntLib.h>
13 #include <Library/UefiBootServicesTableLib.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/MemoryAllocationLib.h>
16
17 #include <Protocol/VariablePolicy.h>
18 #include <Protocol/MmCommunication2.h>
19
20 #include <Guid/VarCheckPolicyMmi.h>
21
22 #include "Variable.h"
23
24 EDKII_VARIABLE_POLICY_PROTOCOL mVariablePolicyProtocol;
25 EFI_MM_COMMUNICATION2_PROTOCOL *mMmCommunication;
26
27 VOID *mMmCommunicationBuffer;
28 UINTN mMmCommunicationBufferSize;
29 EFI_LOCK mMmCommunicationLock;
30
31 /**
32 Internal helper function to consolidate communication method.
33
34 @param[in,out] CommBuffer
35 @param[in,out] CommSize Size of the CommBuffer.
36
37 @retval EFI_STATUS Result from communication method.
38
39 **/
40 STATIC
41 EFI_STATUS
42 InternalMmCommunicate (
43 IN OUT VOID *CommBuffer,
44 IN OUT UINTN *CommSize
45 )
46 {
47 EFI_STATUS Status;
48 if (CommBuffer == NULL || CommSize == NULL) {
49 return EFI_INVALID_PARAMETER;
50 }
51 Status = mMmCommunication->Communicate (mMmCommunication, CommBuffer, CommBuffer, CommSize);
52 return Status;
53 }
54
55
56 /**
57 This API function disables the variable policy enforcement. If it's
58 already been called once, will return EFI_ALREADY_STARTED.
59
60 @retval EFI_SUCCESS
61 @retval EFI_ALREADY_STARTED Has already been called once this boot.
62 @retval EFI_WRITE_PROTECTED Interface has been locked until reboot.
63 @retval EFI_WRITE_PROTECTED Interface option is disabled by platform PCD.
64
65 **/
66 STATIC
67 EFI_STATUS
68 EFIAPI
69 ProtocolDisableVariablePolicy (
70 VOID
71 )
72 {
73 EFI_STATUS Status;
74 EFI_MM_COMMUNICATE_HEADER *CommHeader;
75 VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader;
76 UINTN BufferSize;
77
78 // Check the PCD for convenience.
79 // This would also be rejected by the lib, but why go to MM if we don't have to?
80 if (!PcdGetBool (PcdAllowVariablePolicyEnforcementDisable)) {
81 return EFI_WRITE_PROTECTED;
82 }
83
84 AcquireLockOnlyAtBootTime (&mMmCommunicationLock);
85
86 // Set up the MM communication.
87 BufferSize = mMmCommunicationBufferSize;
88 CommHeader = mMmCommunicationBuffer;
89 PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data;
90 CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid );
91 CommHeader->MessageLength = BufferSize;
92 PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG;
93 PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION;
94 PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_DISABLE;
95
96 Status = InternalMmCommunicate (CommHeader, &BufferSize);
97 DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status ));
98
99 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);
100
101 return (EFI_ERROR( Status )) ? Status : PolicyHeader->Result;
102 }
103
104
105 /**
106 This API function returns whether or not the policy engine is
107 currently being enforced.
108
109 @param[out] State Pointer to a return value for whether the policy enforcement
110 is currently enabled.
111
112 @retval EFI_SUCCESS
113 @retval Others An error has prevented this command from completing.
114
115 **/
116 STATIC
117 EFI_STATUS
118 EFIAPI
119 ProtocolIsVariablePolicyEnabled (
120 OUT BOOLEAN *State
121 )
122 {
123 EFI_STATUS Status;
124 EFI_MM_COMMUNICATE_HEADER *CommHeader;
125 VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader;
126 VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS *CommandParams;
127 UINTN BufferSize;
128
129 if (State == NULL) {
130 return EFI_INVALID_PARAMETER;
131 }
132
133 AcquireLockOnlyAtBootTime (&mMmCommunicationLock);
134
135 // Set up the MM communication.
136 BufferSize = mMmCommunicationBufferSize;
137 CommHeader = mMmCommunicationBuffer;
138 PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data;
139 CommandParams = (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS*)(PolicyHeader + 1);
140 CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid );
141 CommHeader->MessageLength = BufferSize;
142 PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG;
143 PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION;
144 PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_IS_ENABLED;
145
146 Status = InternalMmCommunicate (CommHeader, &BufferSize);
147 DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status ));
148
149 if (!EFI_ERROR( Status )) {
150 Status = PolicyHeader->Result;
151 *State = CommandParams->State;
152 }
153
154 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);
155
156 return Status;
157 }
158
159
160 /**
161 This API function validates and registers a new policy with
162 the policy enforcement engine.
163
164 @param[in] NewPolicy Pointer to the incoming policy structure.
165
166 @retval EFI_SUCCESS
167 @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent.
168 @retval EFI_ALREADY_STARTED An identical matching policy already exists.
169 @retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot.
170 @retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies.
171 @retval EFI_ABORTED A calculation error has prevented this function from completing.
172 @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies.
173
174 **/
175 STATIC
176 EFI_STATUS
177 EFIAPI
178 ProtocolRegisterVariablePolicy (
179 IN CONST VARIABLE_POLICY_ENTRY *NewPolicy
180 )
181 {
182 EFI_STATUS Status;
183 EFI_MM_COMMUNICATE_HEADER *CommHeader;
184 VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader;
185 VOID *PolicyBuffer;
186 UINTN BufferSize;
187 UINTN RequiredSize;
188
189 if (NewPolicy == NULL) {
190 return EFI_INVALID_PARAMETER;
191 }
192
193 // First, make sure that the required size does not exceed the capabilities
194 // of the MmCommunication buffer.
195 RequiredSize = OFFSET_OF(EFI_MM_COMMUNICATE_HEADER, Data) + sizeof(VAR_CHECK_POLICY_COMM_HEADER);
196 Status = SafeUintnAdd( RequiredSize, NewPolicy->Size, &RequiredSize );
197 if (EFI_ERROR( Status ) || RequiredSize > mMmCommunicationBufferSize) {
198 DEBUG(( DEBUG_ERROR, "%a - Policy too large for buffer! %r, %d > %d \n", __FUNCTION__,
199 Status, RequiredSize, mMmCommunicationBufferSize ));
200 return EFI_OUT_OF_RESOURCES;
201 }
202
203 AcquireLockOnlyAtBootTime (&mMmCommunicationLock);
204
205 // Set up the MM communication.
206 BufferSize = mMmCommunicationBufferSize;
207 CommHeader = mMmCommunicationBuffer;
208 PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data;
209 PolicyBuffer = (VOID*)(PolicyHeader + 1);
210 CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid );
211 CommHeader->MessageLength = BufferSize;
212 PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG;
213 PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION;
214 PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_REGISTER;
215
216 // Copy the policy into place. This copy is safe because we've already tested above.
217 CopyMem( PolicyBuffer, NewPolicy, NewPolicy->Size );
218
219 Status = InternalMmCommunicate (CommHeader, &BufferSize);
220 DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status ));
221
222 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);
223
224 return (EFI_ERROR( Status )) ? Status : PolicyHeader->Result;
225 }
226
227
228 /**
229 This helper function takes care of the overhead of formatting, sending, and interpreting
230 the results for a single DumpVariablePolicy request.
231
232 @param[in] PageRequested The page of the paginated results from MM. 0 for metadata.
233 @param[out] TotalSize The total size of the entire buffer. Returned as part of metadata.
234 @param[out] PageSize The size of the current page being returned. Not valid as part of metadata.
235 @param[out] HasMore A flag indicating whether there are more pages after this one.
236 @param[out] Buffer The start of the current page from MM.
237
238 @retval EFI_SUCCESS Output params have been updated (either metadata or dump page).
239 @retval EFI_INVALID_PARAMETER One of the output params is NULL.
240 @retval Others Response from MM handler.
241
242 **/
243 STATIC
244 EFI_STATUS
245 DumpVariablePolicyHelper (
246 IN UINT32 PageRequested,
247 OUT UINT32 *TotalSize,
248 OUT UINT32 *PageSize,
249 OUT BOOLEAN *HasMore,
250 OUT UINT8 **Buffer
251 )
252 {
253 EFI_STATUS Status;
254 EFI_MM_COMMUNICATE_HEADER *CommHeader;
255 VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader;
256 VAR_CHECK_POLICY_COMM_DUMP_PARAMS *CommandParams;
257 UINTN BufferSize;
258
259 if (TotalSize == NULL || PageSize == NULL || HasMore == NULL || Buffer == NULL) {
260 return EFI_INVALID_PARAMETER;
261 }
262
263 // Set up the MM communication.
264 BufferSize = mMmCommunicationBufferSize;
265 CommHeader = mMmCommunicationBuffer;
266 PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data;
267 CommandParams = (VAR_CHECK_POLICY_COMM_DUMP_PARAMS*)(PolicyHeader + 1);
268 CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid );
269 CommHeader->MessageLength = BufferSize;
270 PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG;
271 PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION;
272 PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_DUMP;
273
274 CommandParams->PageRequested = PageRequested;
275
276 Status = InternalMmCommunicate (CommHeader, &BufferSize);
277 DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status ));
278
279 if (!EFI_ERROR( Status )) {
280 Status = PolicyHeader->Result;
281 *TotalSize = CommandParams->TotalSize;
282 *PageSize = CommandParams->PageSize;
283 *HasMore = CommandParams->HasMore;
284 *Buffer = (UINT8*)(CommandParams + 1);
285 }
286
287 return Status;
288 }
289
290
291 /**
292 This API function will dump the entire contents of the variable policy table.
293
294 Similar to GetVariable, the first call can be made with a 0 size and it will return
295 the size of the buffer required to hold the entire table.
296
297 @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0.
298 @param[in,out] Size On input, the size of the output buffer. On output, the size
299 of the data returned.
300
301 @retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated.
302 @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL.
303 @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size.
304
305 **/
306 STATIC
307 EFI_STATUS
308 EFIAPI
309 ProtocolDumpVariablePolicy (
310 OUT UINT8 *Policy OPTIONAL,
311 IN OUT UINT32 *Size
312 )
313 {
314 EFI_STATUS Status;
315 UINT8 *Source;
316 UINT8 *Destination;
317 UINT32 PolicySize;
318 UINT32 PageSize;
319 BOOLEAN HasMore;
320 UINT32 PageIndex;
321
322 if (Size == NULL || (*Size > 0 && Policy == NULL)) {
323 return EFI_INVALID_PARAMETER;
324 }
325
326 AcquireLockOnlyAtBootTime (&mMmCommunicationLock);
327
328 // Repeat this whole process until we either have a failure case or get the entire buffer.
329 do {
330 // First, we must check the zero page to determine the buffer size and
331 // reset the internal state.
332 PolicySize = 0;
333 PageSize = 0;
334 HasMore = FALSE;
335 Status = DumpVariablePolicyHelper (0, &PolicySize, &PageSize, &HasMore, &Source);
336 if (EFI_ERROR (Status)) {
337 break;
338 }
339
340 // If we're good, we can at least check the required size now.
341 if (*Size < PolicySize) {
342 *Size = PolicySize;
343 Status = EFI_BUFFER_TOO_SMALL;
344 break;
345 }
346
347 // On further thought, let's update the size either way.
348 *Size = PolicySize;
349 // And get ready to ROCK.
350 Destination = Policy;
351
352 // Keep looping and copying until we're either done or freak out.
353 for (PageIndex = 1; !EFI_ERROR (Status) && HasMore && PageIndex < MAX_UINT32; PageIndex++) {
354 Status = DumpVariablePolicyHelper (PageIndex, &PolicySize, &PageSize, &HasMore, &Source);
355 if (!EFI_ERROR (Status)) {
356 CopyMem (Destination, Source, PageSize);
357 Destination += PageSize;
358 }
359 }
360
361 // Next, we check to see whether
362 } while (Status == EFI_TIMEOUT);
363
364 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);
365
366 // There's currently no use for this, but it shouldn't be hard to implement.
367 return Status;
368 }
369
370
371 /**
372 This API function locks the interface so that no more policy updates
373 can be performed or changes made to the enforcement until the next boot.
374
375 @retval EFI_SUCCESS
376 @retval Others An error has prevented this command from completing.
377
378 **/
379 STATIC
380 EFI_STATUS
381 EFIAPI
382 ProtocolLockVariablePolicy (
383 VOID
384 )
385 {
386 EFI_STATUS Status;
387 EFI_MM_COMMUNICATE_HEADER *CommHeader;
388 VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader;
389 UINTN BufferSize;
390
391 AcquireLockOnlyAtBootTime (&mMmCommunicationLock);
392
393 // Set up the MM communication.
394 BufferSize = mMmCommunicationBufferSize;
395 CommHeader = mMmCommunicationBuffer;
396 PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data;
397 CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid );
398 CommHeader->MessageLength = BufferSize;
399 PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG;
400 PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION;
401 PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_LOCK;
402
403 Status = InternalMmCommunicate (CommHeader, &BufferSize);
404 DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status ));
405
406 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock);
407
408 return (EFI_ERROR( Status )) ? Status : PolicyHeader->Result;
409 }
410
411
412 /**
413 This helper function locates the shared comm buffer and assigns it to input pointers.
414
415 @param[in,out] BufferSize On input, the minimum buffer size required INCLUDING the MM communicate header.
416 On output, the size of the matching buffer found.
417 @param[out] LocatedBuffer A pointer to the matching buffer.
418
419 @retval EFI_SUCCESS
420 @retval EFI_INVALID_PARAMETER One of the output pointers was NULL.
421 @retval EFI_OUT_OF_RESOURCES Not enough memory to allocate a comm buffer.
422
423 **/
424 STATIC
425 EFI_STATUS
426 InitMmCommonCommBuffer (
427 IN OUT UINTN *BufferSize,
428 OUT VOID **LocatedBuffer
429 )
430 {
431 EFI_STATUS Status;
432
433 Status = EFI_SUCCESS;
434
435 // Make sure that we're working with good pointers.
436 if (BufferSize == NULL || LocatedBuffer == NULL) {
437 return EFI_INVALID_PARAMETER;
438 }
439
440 // Allocate the runtime memory for the comm buffer.
441 *LocatedBuffer = AllocateRuntimePool (*BufferSize);
442 if (*LocatedBuffer == NULL) {
443 Status = EFI_OUT_OF_RESOURCES;
444 *BufferSize = 0;
445 }
446
447 EfiInitializeLock (&mMmCommunicationLock, TPL_NOTIFY);
448
449 return Status;
450 }
451
452
453 /**
454 Convert internal pointer addresses to virtual addresses.
455
456 @param[in] Event Event whose notification function is being invoked.
457 @param[in] Context The pointer to the notification function's context, which
458 is implementation-dependent.
459 **/
460 STATIC
461 VOID
462 EFIAPI
463 VariablePolicyVirtualAddressCallback (
464 IN EFI_EVENT Event,
465 IN VOID *Context
466 )
467 {
468 EfiConvertPointer (0, (VOID **)&mMmCommunication);
469 EfiConvertPointer (0, (VOID **)&mMmCommunicationBuffer);
470 }
471
472
473 /**
474 The driver's entry point.
475
476 @param[in] ImageHandle The firmware allocated handle for the EFI image.
477 @param[in] SystemTable A pointer to the EFI System Table.
478
479 @retval EFI_SUCCESS The entry point executed successfully.
480 @retval other Some error occured when executing this entry point.
481
482 **/
483 EFI_STATUS
484 EFIAPI
485 VariablePolicySmmDxeMain (
486 IN EFI_HANDLE ImageHandle,
487 IN EFI_SYSTEM_TABLE *SystemTable
488 )
489 {
490 EFI_STATUS Status;
491 BOOLEAN ProtocolInstalled;
492 BOOLEAN VirtualAddressChangeRegistered;
493 EFI_EVENT VirtualAddressChangeEvent;
494
495 Status = EFI_SUCCESS;
496 ProtocolInstalled = FALSE;
497 VirtualAddressChangeRegistered = FALSE;
498
499 // Update the minimum buffer size.
500 mMmCommunicationBufferSize = VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE;
501 // Locate the shared comm buffer to use for sending MM commands.
502 Status = InitMmCommonCommBuffer( &mMmCommunicationBufferSize, &mMmCommunicationBuffer );
503 if (EFI_ERROR( Status )) {
504 DEBUG((DEBUG_ERROR, "%a - Failed to locate a viable MM comm buffer! %r\n", __FUNCTION__, Status));
505 ASSERT_EFI_ERROR( Status );
506 return Status;
507 }
508
509 // Locate the MmCommunication protocol.
510 Status = gBS->LocateProtocol( &gEfiMmCommunication2ProtocolGuid, NULL, (VOID**)&mMmCommunication );
511 if (EFI_ERROR( Status )) {
512 DEBUG((DEBUG_ERROR, "%a - Failed to locate MmCommunication protocol! %r\n", __FUNCTION__, Status));
513 ASSERT_EFI_ERROR( Status );
514 return Status;
515 }
516
517 // Configure the VariablePolicy protocol structure.
518 mVariablePolicyProtocol.Revision = EDKII_VARIABLE_POLICY_PROTOCOL_REVISION;
519 mVariablePolicyProtocol.DisableVariablePolicy = ProtocolDisableVariablePolicy;
520 mVariablePolicyProtocol.IsVariablePolicyEnabled = ProtocolIsVariablePolicyEnabled;
521 mVariablePolicyProtocol.RegisterVariablePolicy = ProtocolRegisterVariablePolicy;
522 mVariablePolicyProtocol.DumpVariablePolicy = ProtocolDumpVariablePolicy;
523 mVariablePolicyProtocol.LockVariablePolicy = ProtocolLockVariablePolicy;
524
525 // Register all the protocols and return the status.
526 Status = gBS->InstallMultipleProtocolInterfaces( &ImageHandle,
527 &gEdkiiVariablePolicyProtocolGuid, &mVariablePolicyProtocol,
528 NULL );
529 if (EFI_ERROR( Status )) {
530 DEBUG(( DEBUG_ERROR, "%a - Failed to install protocol! %r\n", __FUNCTION__, Status ));
531 goto Exit;
532 }
533 else {
534 ProtocolInstalled = TRUE;
535 }
536
537 // Normally, we might want to register a callback
538 // to lock the interface, but this is integrated
539 // into the existing callbacks in VaraiableSmm.c
540 // and VariableDxe.c.
541
542 //
543 // Register a VirtualAddressChange callback for the MmComm protocol and Comm buffer.
544 Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL,
545 TPL_NOTIFY,
546 VariablePolicyVirtualAddressCallback,
547 NULL,
548 &gEfiEventVirtualAddressChangeGuid,
549 &VirtualAddressChangeEvent);
550 if (EFI_ERROR( Status )) {
551 DEBUG(( DEBUG_ERROR, "%a - Failed to create VirtualAddressChange event! %r\n", __FUNCTION__, Status ));
552 goto Exit;
553 }
554 else {
555 VirtualAddressChangeRegistered = TRUE;
556 }
557
558
559 Exit:
560 //
561 // If we're about to return a failed status (and unload this driver), we must first undo anything that
562 // has been successfully done.
563 if (EFI_ERROR( Status )) {
564 if (ProtocolInstalled) {
565 gBS->UninstallProtocolInterface( &ImageHandle, &gEdkiiVariablePolicyProtocolGuid, &mVariablePolicyProtocol );
566 }
567 if (VirtualAddressChangeRegistered) {
568 gBS->CloseEvent( VirtualAddressChangeEvent );
569 }
570 }
571
572 return Status;
573 }