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>
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
))) {
82 Returns if this is MOR lock variable.
84 @param VariableName the name of the vendor's variable, it's a Null-Terminated Unicode String
85 @param VendorGuid Unify identifier for vendor.
87 @retval TRUE The variable is MOR lock variable.
88 @retval FALSE The variable is NOT MOR lock variable.
92 IN CHAR16
*VariableName
,
93 IN EFI_GUID
*VendorGuid
96 if ((StrCmp (VariableName
, MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME
) == 0) &&
97 (CompareGuid (VendorGuid
, &gEfiMemoryOverwriteRequestControlLockGuid
))) {
104 Set MOR lock variable.
106 @param Data MOR Lock variable data.
108 @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as
109 defined by the Attributes.
110 @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied, or the
111 DataSize exceeds the maximum allowed.
112 @retval EFI_INVALID_PARAMETER VariableName is an empty Unicode string.
113 @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data.
114 @retval EFI_DEVICE_ERROR The variable could not be saved due to a hardware failure.
115 @retval EFI_WRITE_PROTECTED The variable in question is read-only.
116 @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted.
117 @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
118 set but the AuthInfo does NOT pass the validation check carried
120 @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found.
129 mMorLockPassThru
= TRUE
;
130 Status
= VariableServiceSetVariable (
131 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME
,
132 &gEfiMemoryOverwriteRequestControlLockGuid
,
133 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
,
137 mMorLockPassThru
= FALSE
;
142 This service is an MorLock checker handler for the SetVariable().
144 @param VariableName the name of the vendor's variable, as a
145 Null-Terminated Unicode String
146 @param VendorGuid Unify identifier for vendor.
147 @param Attributes Point to memory location to return the attributes of variable. If the point
148 is NULL, the parameter would be ignored.
149 @param DataSize The size in bytes of Data-Buffer.
150 @param Data Point to the content of the variable.
152 @retval EFI_SUCCESS The MorLock check pass, and Variable driver can store the variable data.
153 @retval EFI_INVALID_PARAMETER The MorLock data or data size or attributes is not allowed.
154 @retval EFI_ACCESS_DENIED The MorLock is locked.
155 @retval EFI_WRITE_PROTECTED The MorLock deletion is not allowed.
156 @retval EFI_ALREADY_STARTED The MorLock variable is handled inside this function.
157 Variable driver can just return EFI_SUCCESS.
160 SetVariableCheckHandlerMorLock (
161 IN CHAR16
*VariableName
,
162 IN EFI_GUID
*VendorGuid
,
163 IN UINT32 Attributes
,
173 if (Attributes
== 0 || DataSize
== 0 || Data
== NULL
) {
175 // Permit deletion for passthru request, deny it otherwise.
177 return mMorLockPassThru
? EFI_SUCCESS
: EFI_WRITE_PROTECTED
;
180 if ((Attributes
!= (EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
)) ||
181 ((DataSize
!= MOR_LOCK_V1_SIZE
) && (DataSize
!= MOR_LOCK_V2_KEY_SIZE
))) {
182 return EFI_INVALID_PARAMETER
;
186 // Do not check if the request is passthru.
188 if (mMorLockPassThru
) {
192 if (mMorLockState
== MorLockStateUnlocked
) {
196 if (DataSize
== MOR_LOCK_V1_SIZE
) {
198 // V1 - lock permanently
200 if (*(UINT8
*)Data
== MOR_LOCK_DATA_UNLOCKED
) {
204 Status
= SetMorLockVariable (MOR_LOCK_DATA_UNLOCKED
);
205 if (!EFI_ERROR (Status
)) {
207 // return EFI_ALREADY_STARTED to skip variable set.
209 return EFI_ALREADY_STARTED
;
216 } else if (*(UINT8
*)Data
== MOR_LOCK_DATA_LOCKED_WITHOUT_KEY
) {
220 Status
= SetMorLockVariable (MOR_LOCK_DATA_LOCKED_WITHOUT_KEY
);
221 if (!EFI_ERROR (Status
)) {
225 mMorLockState
= MorLockStateLocked
;
227 // return EFI_ALREADY_STARTED to skip variable set.
229 return EFI_ALREADY_STARTED
;
237 return EFI_INVALID_PARAMETER
;
239 } else if (DataSize
== MOR_LOCK_V2_KEY_SIZE
) {
241 // V2 lock and provision the key
245 // Need set here because the data value on flash is different
247 Status
= SetMorLockVariable (MOR_LOCK_DATA_LOCKED_WITH_KEY
);
248 if (EFI_ERROR(Status
)) {
250 // SetVar fail, do not provision the key
255 // Lock success, provision the key
257 mMorLockKeyEmpty
= FALSE
;
258 CopyMem (mMorLockKey
, Data
, MOR_LOCK_V2_KEY_SIZE
);
259 mMorLockState
= MorLockStateLocked
;
261 // return EFI_ALREADY_STARTED to skip variable set.
263 return EFI_ALREADY_STARTED
;
267 return EFI_OUT_OF_RESOURCES
;
273 if (mMorLockKeyEmpty
|| (DataSize
!= MOR_LOCK_V2_KEY_SIZE
)) {
274 return EFI_ACCESS_DENIED
;
276 if ((CompareMem (Data
, mMorLockKey
, MOR_LOCK_V2_KEY_SIZE
) == 0)) {
278 // Key match - unlock
282 // Need set here because the data value on flash is different
284 Status
= SetMorLockVariable (MOR_LOCK_DATA_UNLOCKED
);
285 if (EFI_ERROR (Status
)) {
294 mMorLockState
= MorLockStateUnlocked
;
295 mMorLockKeyEmpty
= TRUE
;
296 ZeroMem (mMorLockKey
, sizeof(mMorLockKey
));
298 // return EFI_ALREADY_STARTED to skip variable set.
300 return EFI_ALREADY_STARTED
;
304 // Key mismatch - Prevent Dictionary Attack
306 mMorLockState
= MorLockStateLocked
;
307 mMorLockKeyEmpty
= TRUE
;
308 ZeroMem (mMorLockKey
, sizeof(mMorLockKey
));
309 return EFI_ACCESS_DENIED
;
315 This service is an MOR/MorLock checker handler for the SetVariable().
317 @param[in] VariableName the name of the vendor's variable, as a
318 Null-Terminated Unicode String
319 @param[in] VendorGuid Unify identifier for vendor.
320 @param[in] Attributes Attributes bitmask to set for the variable.
321 @param[in] DataSize The size in bytes of Data-Buffer.
322 @param[in] Data Point to the content of the variable.
324 @retval EFI_SUCCESS The MOR/MorLock check pass, and Variable
325 driver can store the variable data.
326 @retval EFI_INVALID_PARAMETER The MOR/MorLock data or data size or
327 attributes is not allowed for MOR variable.
328 @retval EFI_ACCESS_DENIED The MOR/MorLock is locked.
329 @retval EFI_ALREADY_STARTED The MorLock variable is handled inside this
330 function. Variable driver can just return
334 SetVariableCheckHandlerMor (
335 IN CHAR16
*VariableName
,
336 IN EFI_GUID
*VendorGuid
,
337 IN UINT32 Attributes
,
343 // do not handle non-MOR variable
345 if (!IsAnyMorVariable (VariableName
, VendorGuid
)) {
349 // Permit deletion when policy is disabled.
350 if (!IsVariablePolicyEnabled() && ((Attributes
== 0) || (DataSize
== 0))) {
357 if (IsMorLockVariable (VariableName
, VendorGuid
)) {
358 return SetVariableCheckHandlerMorLock (
372 // Permit deletion for passthru request.
374 if (((Attributes
== 0) || (DataSize
== 0)) && mMorPassThru
) {
381 if ((Attributes
!= (EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
)) ||
382 (DataSize
!= sizeof(UINT8
)) ||
384 return EFI_INVALID_PARAMETER
;
386 if (mMorLockState
== MorLockStateLocked
) {
388 // If lock, deny access
390 return EFI_ACCESS_DENIED
;
399 Initialization for MOR Control Lock.
401 @retval EFI_SUCCESS MorLock initialization success.
402 @return Others Some error occurs.
409 mMorLockInitializationRequired
= TRUE
;
414 Delayed initialization for MOR Control Lock at EndOfDxe.
416 This function performs any operations queued by MorLockInit().
419 MorLockInitAtEndOfDxe (
424 EFI_STATUS MorStatus
;
426 if (!mMorLockInitializationRequired
) {
428 // The EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL has never been installed, thus
429 // the variable write service is unavailable. This should never happen.
436 // Check if the MOR variable exists.
439 MorStatus
= VariableServiceGetVariable (
440 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME
,
441 &gEfiMemoryOverwriteControlDataGuid
,
447 // We provided a zero-sized buffer, so the above call can never succeed.
449 ASSERT (EFI_ERROR (MorStatus
));
451 if (MorStatus
== EFI_BUFFER_TOO_SMALL
) {
453 // The MOR variable exists.
455 // Some OSes don't follow the TCG's Platform Reset Attack Mitigation spec
456 // in that the OS should never create the MOR variable, only read and write
457 // it -- these OSes (unintentionally) create MOR if the platform firmware
458 // does not produce it. Whether this is the case (from the last OS boot)
459 // can be deduced from the absence of the TCG / TCG2 protocols, as edk2's
460 // MOR implementation depends on (one of) those protocols.
462 if (VariableHaveTcgProtocols ()) {
464 // The MOR variable originates from the platform firmware; set the MOR
465 // Control Lock variable to report the locking capability to the OS.
467 SetMorLockVariable (0);
472 // The MOR variable's origin is inexplicable; delete it.
476 "%a: deleting unexpected / unsupported variable %g:%s\n",
478 &gEfiMemoryOverwriteControlDataGuid
,
479 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME
483 VariableServiceSetVariable (
484 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME
,
485 &gEfiMemoryOverwriteControlDataGuid
,
490 mMorPassThru
= FALSE
;
494 // The MOR variable is absent; the platform firmware does not support it.
495 // Lock the variable so that no other module may create it.
497 VariableLockRequestToLock (
499 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME
,
500 &gEfiMemoryOverwriteControlDataGuid
504 // Delete the MOR Control Lock variable too (should it exists for some
505 // reason) and prevent other modules from creating it.
507 mMorLockPassThru
= TRUE
;
508 VariableServiceSetVariable (
509 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME
,
510 &gEfiMemoryOverwriteRequestControlLockGuid
,
515 mMorLockPassThru
= FALSE
;
517 VariableLockRequestToLock (
519 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME
,
520 &gEfiMemoryOverwriteRequestControlLockGuid