2 TCG MOR (Memory Overwrite Request) Lock Control support (SMM version).
4 This module initilizes MemoryOverwriteRequestControlLock variable.
5 This module adds Variable Hook and check MemoryOverwriteRequestControlLock.
7 Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
8 Copyright (c) Microsoft Corporation.
9 SPDX-License-Identifier: BSD-2-Clause-Patent
14 #include <Guid/MemoryOverwriteControl.h>
15 #include <IndustryStandard/MemoryOverwriteRequestControlLock.h>
16 #include <Library/DebugLib.h>
17 #include <Library/BaseLib.h>
18 #include <Library/BaseMemoryLib.h>
21 #include <Protocol/VariablePolicy.h>
22 #include <Library/VariablePolicyHelperLib.h>
23 #include <Library/VariablePolicyLib.h>
30 VARIABLE_TYPE mMorVariableType
[] = {
31 { MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME
, &gEfiMemoryOverwriteControlDataGuid
},
32 { MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME
, &gEfiMemoryOverwriteRequestControlLockGuid
},
35 BOOLEAN mMorPassThru
= FALSE
;
37 #define MOR_LOCK_DATA_UNLOCKED 0x0
38 #define MOR_LOCK_DATA_LOCKED_WITHOUT_KEY 0x1
39 #define MOR_LOCK_DATA_LOCKED_WITH_KEY 0x2
41 #define MOR_LOCK_V1_SIZE 1
42 #define MOR_LOCK_V2_KEY_SIZE 8
45 MorLockStateUnlocked
= 0,
46 MorLockStateLocked
= 1,
49 BOOLEAN mMorLockInitializationRequired
= FALSE
;
50 UINT8 mMorLockKey
[MOR_LOCK_V2_KEY_SIZE
];
51 BOOLEAN mMorLockKeyEmpty
= TRUE
;
52 BOOLEAN mMorLockPassThru
= FALSE
;
53 MOR_LOCK_STATE mMorLockState
= MorLockStateUnlocked
;
56 Returns if this is MOR related variable.
58 @param VariableName the name of the vendor's variable, it's a Null-Terminated Unicode String
59 @param VendorGuid Unify identifier for vendor.
61 @retval TRUE The variable is MOR related.
62 @retval FALSE The variable is NOT MOR related.
66 IN CHAR16
*VariableName
,
67 IN EFI_GUID
*VendorGuid
72 for (Index
= 0; Index
< sizeof (mMorVariableType
)/sizeof (mMorVariableType
[0]); Index
++) {
73 if ((StrCmp (VariableName
, mMorVariableType
[Index
].VariableName
) == 0) &&
74 (CompareGuid (VendorGuid
, mMorVariableType
[Index
].VendorGuid
)))
84 Returns if this is MOR lock variable.
86 @param VariableName the name of the vendor's variable, it's a Null-Terminated Unicode String
87 @param VendorGuid Unify identifier for vendor.
89 @retval TRUE The variable is MOR lock variable.
90 @retval FALSE The variable is NOT MOR lock variable.
94 IN CHAR16
*VariableName
,
95 IN EFI_GUID
*VendorGuid
98 if ((StrCmp (VariableName
, MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME
) == 0) &&
99 (CompareGuid (VendorGuid
, &gEfiMemoryOverwriteRequestControlLockGuid
)))
108 Set MOR lock variable.
110 @param Data MOR Lock variable data.
112 @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as
113 defined by the Attributes.
114 @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied, or the
115 DataSize exceeds the maximum allowed.
116 @retval EFI_INVALID_PARAMETER VariableName is an empty Unicode string.
117 @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data.
118 @retval EFI_DEVICE_ERROR The variable could not be saved due to a hardware failure.
119 @retval EFI_WRITE_PROTECTED The variable in question is read-only.
120 @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted.
121 @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
122 set but the AuthInfo does NOT pass the validation check carried
124 @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found.
133 mMorLockPassThru
= TRUE
;
134 Status
= VariableServiceSetVariable (
135 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME
,
136 &gEfiMemoryOverwriteRequestControlLockGuid
,
137 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
,
141 mMorLockPassThru
= FALSE
;
146 This service is an MorLock checker handler for the SetVariable().
148 @param VariableName the name of the vendor's variable, as a
149 Null-Terminated Unicode String
150 @param VendorGuid Unify identifier for vendor.
151 @param Attributes Point to memory location to return the attributes of variable. If the point
152 is NULL, the parameter would be ignored.
153 @param DataSize The size in bytes of Data-Buffer.
154 @param Data Point to the content of the variable.
156 @retval EFI_SUCCESS The MorLock check pass, and Variable driver can store the variable data.
157 @retval EFI_INVALID_PARAMETER The MorLock data or data size or attributes is not allowed.
158 @retval EFI_ACCESS_DENIED The MorLock is locked.
159 @retval EFI_WRITE_PROTECTED The MorLock deletion is not allowed.
160 @retval EFI_ALREADY_STARTED The MorLock variable is handled inside this function.
161 Variable driver can just return EFI_SUCCESS.
164 SetVariableCheckHandlerMorLock (
165 IN CHAR16
*VariableName
,
166 IN EFI_GUID
*VendorGuid
,
167 IN UINT32 Attributes
,
177 if ((Attributes
== 0) || (DataSize
== 0) || (Data
== NULL
)) {
179 // Permit deletion for passthru request, deny it otherwise.
181 return mMorLockPassThru
? EFI_SUCCESS
: EFI_WRITE_PROTECTED
;
184 if ((Attributes
!= (EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
)) ||
185 ((DataSize
!= MOR_LOCK_V1_SIZE
) && (DataSize
!= MOR_LOCK_V2_KEY_SIZE
)))
187 return EFI_INVALID_PARAMETER
;
191 // Do not check if the request is passthru.
193 if (mMorLockPassThru
) {
197 if (mMorLockState
== MorLockStateUnlocked
) {
201 if (DataSize
== MOR_LOCK_V1_SIZE
) {
203 // V1 - lock permanently
205 if (*(UINT8
*)Data
== MOR_LOCK_DATA_UNLOCKED
) {
209 Status
= SetMorLockVariable (MOR_LOCK_DATA_UNLOCKED
);
210 if (!EFI_ERROR (Status
)) {
212 // return EFI_ALREADY_STARTED to skip variable set.
214 return EFI_ALREADY_STARTED
;
221 } else if (*(UINT8
*)Data
== MOR_LOCK_DATA_LOCKED_WITHOUT_KEY
) {
225 Status
= SetMorLockVariable (MOR_LOCK_DATA_LOCKED_WITHOUT_KEY
);
226 if (!EFI_ERROR (Status
)) {
230 mMorLockState
= MorLockStateLocked
;
232 // return EFI_ALREADY_STARTED to skip variable set.
234 return EFI_ALREADY_STARTED
;
242 return EFI_INVALID_PARAMETER
;
244 } else if (DataSize
== MOR_LOCK_V2_KEY_SIZE
) {
246 // V2 lock and provision the key
250 // Need set here because the data value on flash is different
252 Status
= SetMorLockVariable (MOR_LOCK_DATA_LOCKED_WITH_KEY
);
253 if (EFI_ERROR (Status
)) {
255 // SetVar fail, do not provision the key
260 // Lock success, provision the key
262 mMorLockKeyEmpty
= FALSE
;
263 CopyMem (mMorLockKey
, Data
, MOR_LOCK_V2_KEY_SIZE
);
264 mMorLockState
= MorLockStateLocked
;
266 // return EFI_ALREADY_STARTED to skip variable set.
268 return EFI_ALREADY_STARTED
;
272 return EFI_OUT_OF_RESOURCES
;
278 if (mMorLockKeyEmpty
|| (DataSize
!= MOR_LOCK_V2_KEY_SIZE
)) {
279 return EFI_ACCESS_DENIED
;
282 if ((CompareMem (Data
, mMorLockKey
, MOR_LOCK_V2_KEY_SIZE
) == 0)) {
284 // Key match - unlock
288 // Need set here because the data value on flash is different
290 Status
= SetMorLockVariable (MOR_LOCK_DATA_UNLOCKED
);
291 if (EFI_ERROR (Status
)) {
300 mMorLockState
= MorLockStateUnlocked
;
301 mMorLockKeyEmpty
= TRUE
;
302 ZeroMem (mMorLockKey
, sizeof (mMorLockKey
));
304 // return EFI_ALREADY_STARTED to skip variable set.
306 return EFI_ALREADY_STARTED
;
310 // Key mismatch - Prevent Dictionary Attack
312 mMorLockState
= MorLockStateLocked
;
313 mMorLockKeyEmpty
= TRUE
;
314 ZeroMem (mMorLockKey
, sizeof (mMorLockKey
));
315 return EFI_ACCESS_DENIED
;
321 This service is an MOR/MorLock checker handler for the SetVariable().
323 @param[in] VariableName the name of the vendor's variable, as a
324 Null-Terminated Unicode String
325 @param[in] VendorGuid Unify identifier for vendor.
326 @param[in] Attributes Attributes bitmask to set for the variable.
327 @param[in] DataSize The size in bytes of Data-Buffer.
328 @param[in] Data Point to the content of the variable.
330 @retval EFI_SUCCESS The MOR/MorLock check pass, and Variable
331 driver can store the variable data.
332 @retval EFI_INVALID_PARAMETER The MOR/MorLock data or data size or
333 attributes is not allowed for MOR variable.
334 @retval EFI_ACCESS_DENIED The MOR/MorLock is locked.
335 @retval EFI_ALREADY_STARTED The MorLock variable is handled inside this
336 function. Variable driver can just return
340 SetVariableCheckHandlerMor (
341 IN CHAR16
*VariableName
,
342 IN EFI_GUID
*VendorGuid
,
343 IN UINT32 Attributes
,
349 // do not handle non-MOR variable
351 if (!IsAnyMorVariable (VariableName
, VendorGuid
)) {
355 // Permit deletion when policy is disabled.
356 if (!IsVariablePolicyEnabled () && ((Attributes
== 0) || (DataSize
== 0))) {
363 if (IsMorLockVariable (VariableName
, VendorGuid
)) {
364 return SetVariableCheckHandlerMorLock (
378 // Permit deletion for passthru request.
380 if (((Attributes
== 0) || (DataSize
== 0)) && mMorPassThru
) {
387 if ((Attributes
!= (EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
)) ||
388 (DataSize
!= sizeof (UINT8
)) ||
391 return EFI_INVALID_PARAMETER
;
394 if (mMorLockState
== MorLockStateLocked
) {
396 // If lock, deny access
398 return EFI_ACCESS_DENIED
;
408 Initialization for MOR Control Lock.
410 @retval EFI_SUCCESS MorLock initialization success.
411 @return Others Some error occurs.
418 mMorLockInitializationRequired
= TRUE
;
423 Delayed initialization for MOR Control Lock at EndOfDxe.
425 This function performs any operations queued by MorLockInit().
428 MorLockInitAtEndOfDxe (
433 EFI_STATUS MorStatus
;
435 VARIABLE_POLICY_ENTRY
*NewPolicy
;
437 if (!mMorLockInitializationRequired
) {
439 // The EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL has never been installed, thus
440 // the variable write service is unavailable. This should never happen.
447 // Check if the MOR variable exists.
450 MorStatus
= VariableServiceGetVariable (
451 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME
,
452 &gEfiMemoryOverwriteControlDataGuid
,
458 // We provided a zero-sized buffer, so the above call can never succeed.
460 ASSERT (EFI_ERROR (MorStatus
));
462 if (MorStatus
== EFI_BUFFER_TOO_SMALL
) {
464 // The MOR variable exists.
466 // Some OSes don't follow the TCG's Platform Reset Attack Mitigation spec
467 // in that the OS should never create the MOR variable, only read and write
468 // it -- these OSes (unintentionally) create MOR if the platform firmware
469 // does not produce it. Whether this is the case (from the last OS boot)
470 // can be deduced from the absence of the TCG / TCG2 protocols, as edk2's
471 // MOR implementation depends on (one of) those protocols.
473 if (VariableHaveTcgProtocols ()) {
475 // The MOR variable originates from the platform firmware; set the MOR
476 // Control Lock variable to report the locking capability to the OS.
478 SetMorLockVariable (0);
483 // The MOR variable's origin is inexplicable; delete it.
487 "%a: deleting unexpected / unsupported variable %g:%s\n",
489 &gEfiMemoryOverwriteControlDataGuid
,
490 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME
494 VariableServiceSetVariable (
495 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME
,
496 &gEfiMemoryOverwriteControlDataGuid
,
501 mMorPassThru
= FALSE
;
505 // The MOR variable is absent; the platform firmware does not support it.
506 // Lock the variable so that no other module may create it.
509 Status
= CreateBasicVariablePolicy (
510 &gEfiMemoryOverwriteControlDataGuid
,
511 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME
,
512 VARIABLE_POLICY_NO_MIN_SIZE
,
513 VARIABLE_POLICY_NO_MAX_SIZE
,
514 VARIABLE_POLICY_NO_MUST_ATTR
,
515 VARIABLE_POLICY_NO_CANT_ATTR
,
516 VARIABLE_POLICY_TYPE_LOCK_NOW
,
519 if (!EFI_ERROR (Status
)) {
520 Status
= RegisterVariablePolicy (NewPolicy
);
523 if (EFI_ERROR (Status
)) {
524 DEBUG ((DEBUG_ERROR
, "%a - Failed to lock variable %s! %r\n", __FUNCTION__
, MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME
, Status
));
525 ASSERT_EFI_ERROR (Status
);
528 if (NewPolicy
!= NULL
) {
529 FreePool (NewPolicy
);
533 // Delete the MOR Control Lock variable too (should it exists for some
534 // reason) and prevent other modules from creating it.
536 mMorLockPassThru
= TRUE
;
537 VariableServiceSetVariable (
538 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME
,
539 &gEfiMemoryOverwriteRequestControlLockGuid
,
544 mMorLockPassThru
= FALSE
;
547 Status
= CreateBasicVariablePolicy (
548 &gEfiMemoryOverwriteRequestControlLockGuid
,
549 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME
,
550 VARIABLE_POLICY_NO_MIN_SIZE
,
551 VARIABLE_POLICY_NO_MAX_SIZE
,
552 VARIABLE_POLICY_NO_MUST_ATTR
,
553 VARIABLE_POLICY_NO_CANT_ATTR
,
554 VARIABLE_POLICY_TYPE_LOCK_NOW
,
557 if (!EFI_ERROR (Status
)) {
558 Status
= RegisterVariablePolicy (NewPolicy
);
561 if (EFI_ERROR (Status
)) {
562 DEBUG ((DEBUG_ERROR
, "%a - Failed to lock variable %s! %r\n", __FUNCTION__
, MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME
, Status
));
563 ASSERT_EFI_ERROR (Status
);
566 if (NewPolicy
!= NULL
) {
567 FreePool (NewPolicy
);