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
48 if (CommBuffer
== NULL
|| CommSize
== NULL
) {
49 return EFI_INVALID_PARAMETER
;
51 Status
= mMmCommunication
->Communicate (mMmCommunication
, CommBuffer
, CommBuffer
, CommSize
);
57 This API function disables the variable policy enforcement. If it's
58 already been called once, will return EFI_ALREADY_STARTED.
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.
69 ProtocolDisableVariablePolicy (
74 EFI_MM_COMMUNICATE_HEADER
*CommHeader
;
75 VAR_CHECK_POLICY_COMM_HEADER
*PolicyHeader
;
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
;
84 AcquireLockOnlyAtBootTime (&mMmCommunicationLock
);
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
;
96 Status
= InternalMmCommunicate (CommHeader
, &BufferSize
);
97 DEBUG(( DEBUG_VERBOSE
, "%a - MmCommunication returned %r.\n", __FUNCTION__
, Status
));
99 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock
);
101 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
);
161 This API function validates and registers a new policy with
162 the policy enforcement engine.
164 @param[in] NewPolicy Pointer to the incoming policy structure.
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.
178 ProtocolRegisterVariablePolicy (
179 IN CONST VARIABLE_POLICY_ENTRY
*NewPolicy
183 EFI_MM_COMMUNICATE_HEADER
*CommHeader
;
184 VAR_CHECK_POLICY_COMM_HEADER
*PolicyHeader
;
189 if (NewPolicy
== NULL
) {
190 return EFI_INVALID_PARAMETER
;
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
;
203 AcquireLockOnlyAtBootTime (&mMmCommunicationLock
);
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
;
216 // Copy the policy into place. This copy is safe because we've already tested above.
217 CopyMem( PolicyBuffer
, NewPolicy
, NewPolicy
->Size
);
219 Status
= InternalMmCommunicate (CommHeader
, &BufferSize
);
220 DEBUG(( DEBUG_VERBOSE
, "%a - MmCommunication returned %r.\n", __FUNCTION__
, Status
));
222 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock
);
224 return (EFI_ERROR( Status
)) ? Status
: PolicyHeader
->Result
;
229 This helper function takes care of the overhead of formatting, sending, and interpreting
230 the results for a single DumpVariablePolicy request.
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.
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.
245 DumpVariablePolicyHelper (
246 IN UINT32 PageRequested
,
247 OUT UINT32
*TotalSize
,
248 OUT UINT32
*PageSize
,
249 OUT BOOLEAN
*HasMore
,
254 EFI_MM_COMMUNICATE_HEADER
*CommHeader
;
255 VAR_CHECK_POLICY_COMM_HEADER
*PolicyHeader
;
256 VAR_CHECK_POLICY_COMM_DUMP_PARAMS
*CommandParams
;
259 if (TotalSize
== NULL
|| PageSize
== NULL
|| HasMore
== NULL
|| Buffer
== NULL
) {
260 return EFI_INVALID_PARAMETER
;
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
;
274 CommandParams
->PageRequested
= PageRequested
;
276 Status
= InternalMmCommunicate (CommHeader
, &BufferSize
);
277 DEBUG(( DEBUG_VERBOSE
, "%a - MmCommunication returned %r.\n", __FUNCTION__
, Status
));
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);
292 This API function will dump the entire contents of the variable policy table.
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.
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.
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.
309 ProtocolDumpVariablePolicy (
310 OUT UINT8
*Policy OPTIONAL
,
322 if (Size
== NULL
|| (*Size
> 0 && Policy
== NULL
)) {
323 return EFI_INVALID_PARAMETER
;
326 AcquireLockOnlyAtBootTime (&mMmCommunicationLock
);
328 // Repeat this whole process until we either have a failure case or get the entire buffer.
330 // First, we must check the zero page to determine the buffer size and
331 // reset the internal state.
335 Status
= DumpVariablePolicyHelper (0, &PolicySize
, &PageSize
, &HasMore
, &Source
);
336 if (EFI_ERROR (Status
)) {
340 // If we're good, we can at least check the required size now.
341 if (*Size
< PolicySize
) {
343 Status
= EFI_BUFFER_TOO_SMALL
;
347 // On further thought, let's update the size either way.
349 // And get ready to ROCK.
350 Destination
= Policy
;
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
;
361 // Next, we check to see whether
362 } while (Status
== EFI_TIMEOUT
);
364 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock
);
366 // There's currently no use for this, but it shouldn't be hard to implement.
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.
376 @retval Others An error has prevented this command from completing.
382 ProtocolLockVariablePolicy (
387 EFI_MM_COMMUNICATE_HEADER
*CommHeader
;
388 VAR_CHECK_POLICY_COMM_HEADER
*PolicyHeader
;
391 AcquireLockOnlyAtBootTime (&mMmCommunicationLock
);
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
;
403 Status
= InternalMmCommunicate (CommHeader
, &BufferSize
);
404 DEBUG(( DEBUG_VERBOSE
, "%a - MmCommunication returned %r.\n", __FUNCTION__
, Status
));
406 ReleaseLockOnlyAtBootTime (&mMmCommunicationLock
);
408 return (EFI_ERROR( Status
)) ? Status
: PolicyHeader
->Result
;
413 This helper function locates the shared comm buffer and assigns it to input pointers.
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.
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.
426 InitMmCommonCommBuffer (
427 IN OUT UINTN
*BufferSize
,
428 OUT VOID
**LocatedBuffer
433 Status
= EFI_SUCCESS
;
435 // Make sure that we're working with good pointers.
436 if (BufferSize
== NULL
|| LocatedBuffer
== NULL
) {
437 return EFI_INVALID_PARAMETER
;
440 // Allocate the runtime memory for the comm buffer.
441 *LocatedBuffer
= AllocateRuntimePool (*BufferSize
);
442 if (*LocatedBuffer
== NULL
) {
443 Status
= EFI_OUT_OF_RESOURCES
;
447 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
);
474 The driver's entry point.
476 @param[in] ImageHandle The firmware allocated handle for the EFI image.
477 @param[in] SystemTable A pointer to the EFI System Table.
479 @retval EFI_SUCCESS The entry point executed successfully.
480 @retval other Some error occured when executing this entry point.
485 VariablePolicySmmDxeMain (
486 IN EFI_HANDLE ImageHandle
,
487 IN EFI_SYSTEM_TABLE
*SystemTable
491 BOOLEAN ProtocolInstalled
;
492 BOOLEAN VirtualAddressChangeRegistered
;
493 EFI_EVENT VirtualAddressChangeEvent
;
495 Status
= EFI_SUCCESS
;
496 ProtocolInstalled
= FALSE
;
497 VirtualAddressChangeRegistered
= FALSE
;
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
);
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
);
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
;
525 // Register all the protocols and return the status.
526 Status
= gBS
->InstallMultipleProtocolInterfaces( &ImageHandle
,
527 &gEdkiiVariablePolicyProtocolGuid
, &mVariablePolicyProtocol
,
529 if (EFI_ERROR( Status
)) {
530 DEBUG(( DEBUG_ERROR
, "%a - Failed to install protocol! %r\n", __FUNCTION__
, Status
));
534 ProtocolInstalled
= TRUE
;
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.
543 // Register a VirtualAddressChange callback for the MmComm protocol and Comm buffer.
544 Status
= gBS
->CreateEventEx (EVT_NOTIFY_SIGNAL
,
546 VariablePolicyVirtualAddressCallback
,
548 &gEfiEventVirtualAddressChangeGuid
,
549 &VirtualAddressChangeEvent
);
550 if (EFI_ERROR( Status
)) {
551 DEBUG(( DEBUG_ERROR
, "%a - Failed to create VirtualAddressChange event! %r\n", __FUNCTION__
, Status
));
555 VirtualAddressChangeRegistered
= TRUE
;
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
);
567 if (VirtualAddressChangeRegistered
) {
568 gBS
->CloseEvent( VirtualAddressChangeEvent
);