1 /** @file -- VariablePolicySmmDxe.c
2 This protocol allows communication with Variable Policy Engine.
4 Copyright (c) Microsoft Corporation.
5 SPDX-License-Identifier: BSD-2-Clause-Patent
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>
17 #include <Protocol/VariablePolicy.h>
18 #include <Protocol/MmCommunication2.h>
20 #include <Guid/VarCheckPolicyMmi.h>
24 EDKII_VARIABLE_POLICY_PROTOCOL mVariablePolicyProtocol
;
25 EFI_MM_COMMUNICATION2_PROTOCOL
*mMmCommunication
;
27 VOID
*mMmCommunicationBuffer
;
28 UINTN mMmCommunicationBufferSize
;
29 EFI_LOCK mMmCommunicationLock
;
32 Internal helper function to consolidate communication method.
34 @param[in,out] CommBuffer
35 @param[in,out] CommSize Size of the CommBuffer.
37 @retval EFI_STATUS Result from communication method.
42 InternalMmCommunicate (
43 IN OUT VOID
*CommBuffer
,
44 IN OUT UINTN
*CommSize
49 if ((CommBuffer
== NULL
) || (CommSize
== NULL
)) {
50 return EFI_INVALID_PARAMETER
;
53 Status
= mMmCommunication
->Communicate (mMmCommunication
, CommBuffer
, CommBuffer
, CommSize
);
58 This API function disables the variable policy enforcement. If it's
59 already been called once, will return EFI_ALREADY_STARTED.
62 @retval EFI_ALREADY_STARTED Has already been called once this boot.
63 @retval EFI_WRITE_PROTECTED Interface has been locked until reboot.
64 @retval EFI_WRITE_PROTECTED Interface option is disabled by platform PCD.
70 ProtocolDisableVariablePolicy (
75 EFI_MM_COMMUNICATE_HEADER
*CommHeader
;
76 VAR_CHECK_POLICY_COMM_HEADER
*PolicyHeader
;
79 // Check the PCD for convenience.
80 // This would also be rejected by the lib, but why go to MM if we don't have to?
81 if (!PcdGetBool (PcdAllowVariablePolicyEnforcementDisable
)) {
82 return EFI_WRITE_PROTECTED
;
85 AcquireLockOnlyAtBootTime (&mMmCommunicationLock
);
87 // Set up the MM communication.
88 BufferSize
= mMmCommunicationBufferSize
;
89 CommHeader
= mMmCommunicationBuffer
;
90 PolicyHeader
= (VAR_CHECK_POLICY_COMM_HEADER
*)&CommHeader
->Data
;
91 CopyGuid (&CommHeader
->HeaderGuid
, &gVarCheckPolicyLibMmiHandlerGuid
);
92 CommHeader
->MessageLength
= BufferSize
;
93 PolicyHeader
->Signature
= VAR_CHECK_POLICY_COMM_SIG
;
94 PolicyHeader
->Revision
= VAR_CHECK_POLICY_COMM_REVISION
;
95 PolicyHeader
->Command
= VAR_CHECK_POLICY_COMMAND_DISABLE
;
97 Status
= InternalMmCommunicate (CommHeader
, &BufferSize
);
98 DEBUG ((DEBUG_VERBOSE
, "%a - MmCommunication returned %r.\n", __FUNCTION__
, Status
));
100 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock
);
102 return (EFI_ERROR (Status
)) ? Status
: PolicyHeader
->Result
;
106 This API function returns whether or not the policy engine is
107 currently being enforced.
109 @param[out] State Pointer to a return value for whether the policy enforcement
110 is currently enabled.
113 @retval Others An error has prevented this command from completing.
119 ProtocolIsVariablePolicyEnabled (
124 EFI_MM_COMMUNICATE_HEADER
*CommHeader
;
125 VAR_CHECK_POLICY_COMM_HEADER
*PolicyHeader
;
126 VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS
*CommandParams
;
130 return EFI_INVALID_PARAMETER
;
133 AcquireLockOnlyAtBootTime (&mMmCommunicationLock
);
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
;
146 Status
= InternalMmCommunicate (CommHeader
, &BufferSize
);
147 DEBUG ((DEBUG_VERBOSE
, "%a - MmCommunication returned %r.\n", __FUNCTION__
, Status
));
149 if (!EFI_ERROR (Status
)) {
150 Status
= PolicyHeader
->Result
;
151 *State
= CommandParams
->State
;
154 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock
);
160 This API function validates and registers a new policy with
161 the policy enforcement engine.
163 @param[in] NewPolicy Pointer to the incoming policy structure.
166 @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent.
167 @retval EFI_ALREADY_STARTED An identical matching policy already exists.
168 @retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot.
169 @retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies.
170 @retval EFI_ABORTED A calculation error has prevented this function from completing.
171 @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies.
177 ProtocolRegisterVariablePolicy (
178 IN CONST VARIABLE_POLICY_ENTRY
*NewPolicy
182 EFI_MM_COMMUNICATE_HEADER
*CommHeader
;
183 VAR_CHECK_POLICY_COMM_HEADER
*PolicyHeader
;
188 if (NewPolicy
== NULL
) {
189 return EFI_INVALID_PARAMETER
;
192 // First, make sure that the required size does not exceed the capabilities
193 // of the MmCommunication buffer.
194 RequiredSize
= OFFSET_OF (EFI_MM_COMMUNICATE_HEADER
, Data
) + sizeof (VAR_CHECK_POLICY_COMM_HEADER
);
195 Status
= SafeUintnAdd (RequiredSize
, NewPolicy
->Size
, &RequiredSize
);
196 if (EFI_ERROR (Status
) || (RequiredSize
> mMmCommunicationBufferSize
)) {
199 "%a - Policy too large for buffer! %r, %d > %d \n",
203 mMmCommunicationBufferSize
205 return EFI_OUT_OF_RESOURCES
;
208 AcquireLockOnlyAtBootTime (&mMmCommunicationLock
);
210 // Set up the MM communication.
211 BufferSize
= mMmCommunicationBufferSize
;
212 CommHeader
= mMmCommunicationBuffer
;
213 PolicyHeader
= (VAR_CHECK_POLICY_COMM_HEADER
*)&CommHeader
->Data
;
214 PolicyBuffer
= (VOID
*)(PolicyHeader
+ 1);
215 CopyGuid (&CommHeader
->HeaderGuid
, &gVarCheckPolicyLibMmiHandlerGuid
);
216 CommHeader
->MessageLength
= BufferSize
;
217 PolicyHeader
->Signature
= VAR_CHECK_POLICY_COMM_SIG
;
218 PolicyHeader
->Revision
= VAR_CHECK_POLICY_COMM_REVISION
;
219 PolicyHeader
->Command
= VAR_CHECK_POLICY_COMMAND_REGISTER
;
221 // Copy the policy into place. This copy is safe because we've already tested above.
222 CopyMem (PolicyBuffer
, NewPolicy
, NewPolicy
->Size
);
224 Status
= InternalMmCommunicate (CommHeader
, &BufferSize
);
225 DEBUG ((DEBUG_VERBOSE
, "%a - MmCommunication returned %r.\n", __FUNCTION__
, Status
));
227 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock
);
229 return (EFI_ERROR (Status
)) ? Status
: PolicyHeader
->Result
;
233 This helper function takes care of the overhead of formatting, sending, and interpreting
234 the results for a single DumpVariablePolicy request.
236 @param[in] PageRequested The page of the paginated results from MM. 0 for metadata.
237 @param[out] TotalSize The total size of the entire buffer. Returned as part of metadata.
238 @param[out] PageSize The size of the current page being returned. Not valid as part of metadata.
239 @param[out] HasMore A flag indicating whether there are more pages after this one.
240 @param[out] Buffer The start of the current page from MM.
242 @retval EFI_SUCCESS Output params have been updated (either metadata or dump page).
243 @retval EFI_INVALID_PARAMETER One of the output params is NULL.
244 @retval Others Response from MM handler.
249 DumpVariablePolicyHelper (
250 IN UINT32 PageRequested
,
251 OUT UINT32
*TotalSize
,
252 OUT UINT32
*PageSize
,
253 OUT BOOLEAN
*HasMore
,
258 EFI_MM_COMMUNICATE_HEADER
*CommHeader
;
259 VAR_CHECK_POLICY_COMM_HEADER
*PolicyHeader
;
260 VAR_CHECK_POLICY_COMM_DUMP_PARAMS
*CommandParams
;
263 if ((TotalSize
== NULL
) || (PageSize
== NULL
) || (HasMore
== NULL
) || (Buffer
== NULL
)) {
264 return EFI_INVALID_PARAMETER
;
267 // Set up the MM communication.
268 BufferSize
= mMmCommunicationBufferSize
;
269 CommHeader
= mMmCommunicationBuffer
;
270 PolicyHeader
= (VAR_CHECK_POLICY_COMM_HEADER
*)&CommHeader
->Data
;
271 CommandParams
= (VAR_CHECK_POLICY_COMM_DUMP_PARAMS
*)(PolicyHeader
+ 1);
272 CopyGuid (&CommHeader
->HeaderGuid
, &gVarCheckPolicyLibMmiHandlerGuid
);
273 CommHeader
->MessageLength
= BufferSize
;
274 PolicyHeader
->Signature
= VAR_CHECK_POLICY_COMM_SIG
;
275 PolicyHeader
->Revision
= VAR_CHECK_POLICY_COMM_REVISION
;
276 PolicyHeader
->Command
= VAR_CHECK_POLICY_COMMAND_DUMP
;
278 CommandParams
->PageRequested
= PageRequested
;
280 Status
= InternalMmCommunicate (CommHeader
, &BufferSize
);
281 DEBUG ((DEBUG_VERBOSE
, "%a - MmCommunication returned %r.\n", __FUNCTION__
, Status
));
283 if (!EFI_ERROR (Status
)) {
284 Status
= PolicyHeader
->Result
;
285 *TotalSize
= CommandParams
->TotalSize
;
286 *PageSize
= CommandParams
->PageSize
;
287 *HasMore
= CommandParams
->HasMore
;
288 *Buffer
= (UINT8
*)(CommandParams
+ 1);
295 This API function will dump the entire contents of the variable policy table.
297 Similar to GetVariable, the first call can be made with a 0 size and it will return
298 the size of the buffer required to hold the entire table.
300 @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0.
301 @param[in,out] Size On input, the size of the output buffer. On output, the size
302 of the data returned.
304 @retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated.
305 @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL.
306 @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size.
312 ProtocolDumpVariablePolicy (
313 OUT UINT8
*Policy OPTIONAL
,
325 if ((Size
== NULL
) || ((*Size
> 0) && (Policy
== NULL
))) {
326 return EFI_INVALID_PARAMETER
;
329 AcquireLockOnlyAtBootTime (&mMmCommunicationLock
);
331 // Repeat this whole process until we either have a failure case or get the entire buffer.
333 // First, we must check the zero page to determine the buffer size and
334 // reset the internal state.
338 Status
= DumpVariablePolicyHelper (0, &PolicySize
, &PageSize
, &HasMore
, &Source
);
339 if (EFI_ERROR (Status
)) {
343 // If we're good, we can at least check the required size now.
344 if (*Size
< PolicySize
) {
346 Status
= EFI_BUFFER_TOO_SMALL
;
350 // On further thought, let's update the size either way.
352 // And get ready to ROCK.
353 Destination
= Policy
;
355 // Keep looping and copying until we're either done or freak out.
356 for (PageIndex
= 1; !EFI_ERROR (Status
) && HasMore
&& PageIndex
< MAX_UINT32
; PageIndex
++) {
357 Status
= DumpVariablePolicyHelper (PageIndex
, &PolicySize
, &PageSize
, &HasMore
, &Source
);
358 if (!EFI_ERROR (Status
)) {
359 CopyMem (Destination
, Source
, PageSize
);
360 Destination
+= PageSize
;
364 // Next, we check to see whether
365 } while (Status
== EFI_TIMEOUT
);
367 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock
);
369 // There's currently no use for this, but it shouldn't be hard to implement.
374 This API function locks the interface so that no more policy updates
375 can be performed or changes made to the enforcement until the next boot.
378 @retval Others An error has prevented this command from completing.
384 ProtocolLockVariablePolicy (
389 EFI_MM_COMMUNICATE_HEADER
*CommHeader
;
390 VAR_CHECK_POLICY_COMM_HEADER
*PolicyHeader
;
393 AcquireLockOnlyAtBootTime (&mMmCommunicationLock
);
395 // Set up the MM communication.
396 BufferSize
= mMmCommunicationBufferSize
;
397 CommHeader
= mMmCommunicationBuffer
;
398 PolicyHeader
= (VAR_CHECK_POLICY_COMM_HEADER
*)&CommHeader
->Data
;
399 CopyGuid (&CommHeader
->HeaderGuid
, &gVarCheckPolicyLibMmiHandlerGuid
);
400 CommHeader
->MessageLength
= BufferSize
;
401 PolicyHeader
->Signature
= VAR_CHECK_POLICY_COMM_SIG
;
402 PolicyHeader
->Revision
= VAR_CHECK_POLICY_COMM_REVISION
;
403 PolicyHeader
->Command
= VAR_CHECK_POLICY_COMMAND_LOCK
;
405 Status
= InternalMmCommunicate (CommHeader
, &BufferSize
);
406 DEBUG ((DEBUG_VERBOSE
, "%a - MmCommunication returned %r.\n", __FUNCTION__
, Status
));
408 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock
);
410 return (EFI_ERROR (Status
)) ? Status
: PolicyHeader
->Result
;
414 This helper function locates the shared comm buffer and assigns it to input pointers.
416 @param[in,out] BufferSize On input, the minimum buffer size required INCLUDING the MM communicate header.
417 On output, the size of the matching buffer found.
418 @param[out] LocatedBuffer A pointer to the matching buffer.
421 @retval EFI_INVALID_PARAMETER One of the output pointers was NULL.
422 @retval EFI_OUT_OF_RESOURCES Not enough memory to allocate a comm buffer.
427 InitMmCommonCommBuffer (
428 IN OUT UINTN
*BufferSize
,
429 OUT VOID
**LocatedBuffer
434 Status
= EFI_SUCCESS
;
436 // Make sure that we're working with good pointers.
437 if ((BufferSize
== NULL
) || (LocatedBuffer
== NULL
)) {
438 return EFI_INVALID_PARAMETER
;
441 // Allocate the runtime memory for the comm buffer.
442 *LocatedBuffer
= AllocateRuntimePool (*BufferSize
);
443 if (*LocatedBuffer
== NULL
) {
444 Status
= EFI_OUT_OF_RESOURCES
;
448 EfiInitializeLock (&mMmCommunicationLock
, TPL_NOTIFY
);
454 Convert internal pointer addresses to virtual addresses.
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.
463 VariablePolicyVirtualAddressCallback (
468 EfiConvertPointer (0, (VOID
**)&mMmCommunication
);
469 EfiConvertPointer (0, (VOID
**)&mMmCommunicationBuffer
);
473 The driver's entry point.
475 @param[in] ImageHandle The firmware allocated handle for the EFI image.
476 @param[in] SystemTable A pointer to the EFI System Table.
478 @retval EFI_SUCCESS The entry point executed successfully.
479 @retval other Some error occured when executing this entry point.
484 VariablePolicySmmDxeMain (
485 IN EFI_HANDLE ImageHandle
,
486 IN EFI_SYSTEM_TABLE
*SystemTable
490 BOOLEAN ProtocolInstalled
;
491 BOOLEAN VirtualAddressChangeRegistered
;
492 EFI_EVENT VirtualAddressChangeEvent
;
494 Status
= EFI_SUCCESS
;
495 ProtocolInstalled
= FALSE
;
496 VirtualAddressChangeRegistered
= FALSE
;
498 // Update the minimum buffer size.
499 mMmCommunicationBufferSize
= VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE
;
500 // Locate the shared comm buffer to use for sending MM commands.
501 Status
= InitMmCommonCommBuffer (&mMmCommunicationBufferSize
, &mMmCommunicationBuffer
);
502 if (EFI_ERROR (Status
)) {
503 DEBUG ((DEBUG_ERROR
, "%a - Failed to locate a viable MM comm buffer! %r\n", __FUNCTION__
, Status
));
504 ASSERT_EFI_ERROR (Status
);
508 // Locate the MmCommunication protocol.
509 Status
= gBS
->LocateProtocol (&gEfiMmCommunication2ProtocolGuid
, NULL
, (VOID
**)&mMmCommunication
);
510 if (EFI_ERROR (Status
)) {
511 DEBUG ((DEBUG_ERROR
, "%a - Failed to locate MmCommunication protocol! %r\n", __FUNCTION__
, Status
));
512 ASSERT_EFI_ERROR (Status
);
516 // Configure the VariablePolicy protocol structure.
517 mVariablePolicyProtocol
.Revision
= EDKII_VARIABLE_POLICY_PROTOCOL_REVISION
;
518 mVariablePolicyProtocol
.DisableVariablePolicy
= ProtocolDisableVariablePolicy
;
519 mVariablePolicyProtocol
.IsVariablePolicyEnabled
= ProtocolIsVariablePolicyEnabled
;
520 mVariablePolicyProtocol
.RegisterVariablePolicy
= ProtocolRegisterVariablePolicy
;
521 mVariablePolicyProtocol
.DumpVariablePolicy
= ProtocolDumpVariablePolicy
;
522 mVariablePolicyProtocol
.LockVariablePolicy
= ProtocolLockVariablePolicy
;
524 // Register all the protocols and return the status.
525 Status
= gBS
->InstallMultipleProtocolInterfaces (
527 &gEdkiiVariablePolicyProtocolGuid
,
528 &mVariablePolicyProtocol
,
531 if (EFI_ERROR (Status
)) {
532 DEBUG ((DEBUG_ERROR
, "%a - Failed to install protocol! %r\n", __FUNCTION__
, Status
));
535 ProtocolInstalled
= TRUE
;
538 // Normally, we might want to register a callback
539 // to lock the interface, but this is integrated
540 // into the existing callbacks in VaraiableSmm.c
541 // and VariableDxe.c.
544 // Register a VirtualAddressChange callback for the MmComm protocol and Comm buffer.
545 Status
= gBS
->CreateEventEx (
548 VariablePolicyVirtualAddressCallback
,
550 &gEfiEventVirtualAddressChangeGuid
,
551 &VirtualAddressChangeEvent
553 if (EFI_ERROR (Status
)) {
554 DEBUG ((DEBUG_ERROR
, "%a - Failed to create VirtualAddressChange event! %r\n", __FUNCTION__
, Status
));
557 VirtualAddressChangeRegistered
= TRUE
;
562 // If we're about to return a failed status (and unload this driver), we must first undo anything that
563 // has been successfully done.
564 if (EFI_ERROR (Status
)) {
565 if (ProtocolInstalled
) {
566 gBS
->UninstallProtocolInterface (&ImageHandle
, &gEdkiiVariablePolicyProtocolGuid
, &mVariablePolicyProtocol
);
569 if (VirtualAddressChangeRegistered
) {
570 gBS
->CloseEvent (VirtualAddressChangeEvent
);