]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / RuntimeDxe / TcgMorLockSmm.c
1 /** @file
2 TCG MOR (Memory Overwrite Request) Lock Control support (SMM version).
3
4 This module initilizes MemoryOverwriteRequestControlLock variable.
5 This module adds Variable Hook and check MemoryOverwriteRequestControlLock.
6
7 Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9
10 **/
11
12 #include <PiDxe.h>
13 #include <Guid/MemoryOverwriteControl.h>
14 #include <IndustryStandard/MemoryOverwriteRequestControlLock.h>
15 #include <Library/DebugLib.h>
16 #include <Library/BaseLib.h>
17 #include <Library/BaseMemoryLib.h>
18 #include "Variable.h"
19
20 typedef struct {
21 CHAR16 *VariableName;
22 EFI_GUID *VendorGuid;
23 } VARIABLE_TYPE;
24
25 VARIABLE_TYPE mMorVariableType[] = {
26 {MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, &gEfiMemoryOverwriteControlDataGuid},
27 {MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME, &gEfiMemoryOverwriteRequestControlLockGuid},
28 };
29
30 BOOLEAN mMorPassThru = FALSE;
31
32 #define MOR_LOCK_DATA_UNLOCKED 0x0
33 #define MOR_LOCK_DATA_LOCKED_WITHOUT_KEY 0x1
34 #define MOR_LOCK_DATA_LOCKED_WITH_KEY 0x2
35
36 #define MOR_LOCK_V1_SIZE 1
37 #define MOR_LOCK_V2_KEY_SIZE 8
38
39 typedef enum {
40 MorLockStateUnlocked = 0,
41 MorLockStateLocked = 1,
42 } MOR_LOCK_STATE;
43
44 BOOLEAN mMorLockInitializationRequired = FALSE;
45 UINT8 mMorLockKey[MOR_LOCK_V2_KEY_SIZE];
46 BOOLEAN mMorLockKeyEmpty = TRUE;
47 BOOLEAN mMorLockPassThru = FALSE;
48 MOR_LOCK_STATE mMorLockState = MorLockStateUnlocked;
49
50 /**
51 Returns if this is MOR related variable.
52
53 @param VariableName the name of the vendor's variable, it's a Null-Terminated Unicode String
54 @param VendorGuid Unify identifier for vendor.
55
56 @retval TRUE The variable is MOR related.
57 @retval FALSE The variable is NOT MOR related.
58 **/
59 BOOLEAN
60 IsAnyMorVariable (
61 IN CHAR16 *VariableName,
62 IN EFI_GUID *VendorGuid
63 )
64 {
65 UINTN Index;
66
67 for (Index = 0; Index < sizeof(mMorVariableType)/sizeof(mMorVariableType[0]); Index++) {
68 if ((StrCmp (VariableName, mMorVariableType[Index].VariableName) == 0) &&
69 (CompareGuid (VendorGuid, mMorVariableType[Index].VendorGuid))) {
70 return TRUE;
71 }
72 }
73 return FALSE;
74 }
75
76 /**
77 Returns if this is MOR lock variable.
78
79 @param VariableName the name of the vendor's variable, it's a Null-Terminated Unicode String
80 @param VendorGuid Unify identifier for vendor.
81
82 @retval TRUE The variable is MOR lock variable.
83 @retval FALSE The variable is NOT MOR lock variable.
84 **/
85 BOOLEAN
86 IsMorLockVariable (
87 IN CHAR16 *VariableName,
88 IN EFI_GUID *VendorGuid
89 )
90 {
91 if ((StrCmp (VariableName, MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME) == 0) &&
92 (CompareGuid (VendorGuid, &gEfiMemoryOverwriteRequestControlLockGuid))) {
93 return TRUE;
94 }
95 return FALSE;
96 }
97
98 /**
99 Set MOR lock variable.
100
101 @param Data MOR Lock variable data.
102
103 @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as
104 defined by the Attributes.
105 @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied, or the
106 DataSize exceeds the maximum allowed.
107 @retval EFI_INVALID_PARAMETER VariableName is an empty Unicode string.
108 @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data.
109 @retval EFI_DEVICE_ERROR The variable could not be saved due to a hardware failure.
110 @retval EFI_WRITE_PROTECTED The variable in question is read-only.
111 @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted.
112 @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
113 set but the AuthInfo does NOT pass the validation check carried
114 out by the firmware.
115 @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found.
116 **/
117 EFI_STATUS
118 SetMorLockVariable (
119 IN UINT8 Data
120 )
121 {
122 EFI_STATUS Status;
123
124 mMorLockPassThru = TRUE;
125 Status = VariableServiceSetVariable (
126 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME,
127 &gEfiMemoryOverwriteRequestControlLockGuid,
128 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
129 sizeof(Data),
130 &Data
131 );
132 mMorLockPassThru = FALSE;
133 return Status;
134 }
135
136 /**
137 This service is an MorLock checker handler for the SetVariable().
138
139 @param VariableName the name of the vendor's variable, as a
140 Null-Terminated Unicode String
141 @param VendorGuid Unify identifier for vendor.
142 @param Attributes Point to memory location to return the attributes of variable. If the point
143 is NULL, the parameter would be ignored.
144 @param DataSize The size in bytes of Data-Buffer.
145 @param Data Point to the content of the variable.
146
147 @retval EFI_SUCCESS The MorLock check pass, and Variable driver can store the variable data.
148 @retval EFI_INVALID_PARAMETER The MorLock data or data size or attributes is not allowed.
149 @retval EFI_ACCESS_DENIED The MorLock is locked.
150 @retval EFI_WRITE_PROTECTED The MorLock deletion is not allowed.
151 @retval EFI_ALREADY_STARTED The MorLock variable is handled inside this function.
152 Variable driver can just return EFI_SUCCESS.
153 **/
154 EFI_STATUS
155 SetVariableCheckHandlerMorLock (
156 IN CHAR16 *VariableName,
157 IN EFI_GUID *VendorGuid,
158 IN UINT32 Attributes,
159 IN UINTN DataSize,
160 IN VOID *Data
161 )
162 {
163 EFI_STATUS Status;
164
165 //
166 // Basic Check
167 //
168 if (Attributes == 0 || DataSize == 0 || Data == NULL) {
169 //
170 // Permit deletion for passthru request, deny it otherwise.
171 //
172 return mMorLockPassThru ? EFI_SUCCESS : EFI_WRITE_PROTECTED;
173 }
174
175 if ((Attributes != (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS)) ||
176 ((DataSize != MOR_LOCK_V1_SIZE) && (DataSize != MOR_LOCK_V2_KEY_SIZE))) {
177 return EFI_INVALID_PARAMETER;
178 }
179
180 //
181 // Do not check if the request is passthru.
182 //
183 if (mMorLockPassThru) {
184 return EFI_SUCCESS;
185 }
186
187 if (mMorLockState == MorLockStateUnlocked) {
188 //
189 // In Unlocked State
190 //
191 if (DataSize == MOR_LOCK_V1_SIZE) {
192 //
193 // V1 - lock permanently
194 //
195 if (*(UINT8 *)Data == MOR_LOCK_DATA_UNLOCKED) {
196 //
197 // Unlock
198 //
199 Status = SetMorLockVariable (MOR_LOCK_DATA_UNLOCKED);
200 if (!EFI_ERROR (Status)) {
201 //
202 // return EFI_ALREADY_STARTED to skip variable set.
203 //
204 return EFI_ALREADY_STARTED;
205 } else {
206 //
207 // SetVar fail
208 //
209 return Status;
210 }
211 } else if (*(UINT8 *)Data == MOR_LOCK_DATA_LOCKED_WITHOUT_KEY) {
212 //
213 // Lock without key
214 //
215 Status = SetMorLockVariable (MOR_LOCK_DATA_LOCKED_WITHOUT_KEY);
216 if (!EFI_ERROR (Status)) {
217 //
218 // Lock success
219 //
220 mMorLockState = MorLockStateLocked;
221 //
222 // return EFI_ALREADY_STARTED to skip variable set.
223 //
224 return EFI_ALREADY_STARTED;
225 } else {
226 //
227 // SetVar fail
228 //
229 return Status;
230 }
231 } else {
232 return EFI_INVALID_PARAMETER;
233 }
234 } else if (DataSize == MOR_LOCK_V2_KEY_SIZE) {
235 //
236 // V2 lock and provision the key
237 //
238
239 //
240 // Need set here because the data value on flash is different
241 //
242 Status = SetMorLockVariable (MOR_LOCK_DATA_LOCKED_WITH_KEY);
243 if (EFI_ERROR(Status)) {
244 //
245 // SetVar fail, do not provision the key
246 //
247 return Status;
248 } else {
249 //
250 // Lock success, provision the key
251 //
252 mMorLockKeyEmpty = FALSE;
253 CopyMem (mMorLockKey, Data, MOR_LOCK_V2_KEY_SIZE);
254 mMorLockState = MorLockStateLocked;
255 //
256 // return EFI_ALREADY_STARTED to skip variable set.
257 //
258 return EFI_ALREADY_STARTED;
259 }
260 } else {
261 ASSERT (FALSE);
262 return EFI_OUT_OF_RESOURCES;
263 }
264 } else {
265 //
266 // In Locked State
267 //
268 if (mMorLockKeyEmpty || (DataSize != MOR_LOCK_V2_KEY_SIZE)) {
269 return EFI_ACCESS_DENIED;
270 }
271 if ((CompareMem (Data, mMorLockKey, MOR_LOCK_V2_KEY_SIZE) == 0)) {
272 //
273 // Key match - unlock
274 //
275
276 //
277 // Need set here because the data value on flash is different
278 //
279 Status = SetMorLockVariable (MOR_LOCK_DATA_UNLOCKED);
280 if (EFI_ERROR (Status)) {
281 //
282 // SetVar fail
283 //
284 return Status;
285 } else {
286 //
287 // Unlock Success
288 //
289 mMorLockState = MorLockStateUnlocked;
290 mMorLockKeyEmpty = TRUE;
291 ZeroMem (mMorLockKey, sizeof(mMorLockKey));
292 //
293 // return EFI_ALREADY_STARTED to skip variable set.
294 //
295 return EFI_ALREADY_STARTED;
296 }
297 } else {
298 //
299 // Key mismatch - Prevent Dictionary Attack
300 //
301 mMorLockState = MorLockStateLocked;
302 mMorLockKeyEmpty = TRUE;
303 ZeroMem (mMorLockKey, sizeof(mMorLockKey));
304 return EFI_ACCESS_DENIED;
305 }
306 }
307 }
308
309 /**
310 This service is an MOR/MorLock checker handler for the SetVariable().
311
312 @param[in] VariableName the name of the vendor's variable, as a
313 Null-Terminated Unicode String
314 @param[in] VendorGuid Unify identifier for vendor.
315 @param[in] Attributes Attributes bitmask to set for the variable.
316 @param[in] DataSize The size in bytes of Data-Buffer.
317 @param[in] Data Point to the content of the variable.
318
319 @retval EFI_SUCCESS The MOR/MorLock check pass, and Variable
320 driver can store the variable data.
321 @retval EFI_INVALID_PARAMETER The MOR/MorLock data or data size or
322 attributes is not allowed for MOR variable.
323 @retval EFI_ACCESS_DENIED The MOR/MorLock is locked.
324 @retval EFI_ALREADY_STARTED The MorLock variable is handled inside this
325 function. Variable driver can just return
326 EFI_SUCCESS.
327 **/
328 EFI_STATUS
329 SetVariableCheckHandlerMor (
330 IN CHAR16 *VariableName,
331 IN EFI_GUID *VendorGuid,
332 IN UINT32 Attributes,
333 IN UINTN DataSize,
334 IN VOID *Data
335 )
336 {
337 //
338 // do not handle non-MOR variable
339 //
340 if (!IsAnyMorVariable (VariableName, VendorGuid)) {
341 return EFI_SUCCESS;
342 }
343
344 //
345 // MorLock variable
346 //
347 if (IsMorLockVariable (VariableName, VendorGuid)) {
348 return SetVariableCheckHandlerMorLock (
349 VariableName,
350 VendorGuid,
351 Attributes,
352 DataSize,
353 Data
354 );
355 }
356
357 //
358 // Mor Variable
359 //
360
361 //
362 // Permit deletion for passthru request.
363 //
364 if (((Attributes == 0) || (DataSize == 0)) && mMorPassThru) {
365 return EFI_SUCCESS;
366 }
367
368 //
369 // Basic Check
370 //
371 if ((Attributes != (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS)) ||
372 (DataSize != sizeof(UINT8)) ||
373 (Data == NULL)) {
374 return EFI_INVALID_PARAMETER;
375 }
376 if (mMorLockState == MorLockStateLocked) {
377 //
378 // If lock, deny access
379 //
380 return EFI_ACCESS_DENIED;
381 }
382 //
383 // grant access
384 //
385 return EFI_SUCCESS;
386 }
387
388 /**
389 Initialization for MOR Control Lock.
390
391 @retval EFI_SUCCESS MorLock initialization success.
392 @return Others Some error occurs.
393 **/
394 EFI_STATUS
395 MorLockInit (
396 VOID
397 )
398 {
399 mMorLockInitializationRequired = TRUE;
400 return EFI_SUCCESS;
401 }
402
403 /**
404 Delayed initialization for MOR Control Lock at EndOfDxe.
405
406 This function performs any operations queued by MorLockInit().
407 **/
408 VOID
409 MorLockInitAtEndOfDxe (
410 VOID
411 )
412 {
413 UINTN MorSize;
414 EFI_STATUS MorStatus;
415
416 if (!mMorLockInitializationRequired) {
417 //
418 // The EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL has never been installed, thus
419 // the variable write service is unavailable. This should never happen.
420 //
421 ASSERT (FALSE);
422 return;
423 }
424
425 //
426 // Check if the MOR variable exists.
427 //
428 MorSize = 0;
429 MorStatus = VariableServiceGetVariable (
430 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,
431 &gEfiMemoryOverwriteControlDataGuid,
432 NULL, // Attributes
433 &MorSize,
434 NULL // Data
435 );
436 //
437 // We provided a zero-sized buffer, so the above call can never succeed.
438 //
439 ASSERT (EFI_ERROR (MorStatus));
440
441 if (MorStatus == EFI_BUFFER_TOO_SMALL) {
442 //
443 // The MOR variable exists.
444 //
445 // Some OSes don't follow the TCG's Platform Reset Attack Mitigation spec
446 // in that the OS should never create the MOR variable, only read and write
447 // it -- these OSes (unintentionally) create MOR if the platform firmware
448 // does not produce it. Whether this is the case (from the last OS boot)
449 // can be deduced from the absence of the TCG / TCG2 protocols, as edk2's
450 // MOR implementation depends on (one of) those protocols.
451 //
452 if (VariableHaveTcgProtocols ()) {
453 //
454 // The MOR variable originates from the platform firmware; set the MOR
455 // Control Lock variable to report the locking capability to the OS.
456 //
457 SetMorLockVariable (0);
458 return;
459 }
460
461 //
462 // The MOR variable's origin is inexplicable; delete it.
463 //
464 DEBUG ((
465 DEBUG_WARN,
466 "%a: deleting unexpected / unsupported variable %g:%s\n",
467 __FUNCTION__,
468 &gEfiMemoryOverwriteControlDataGuid,
469 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME
470 ));
471
472 mMorPassThru = TRUE;
473 VariableServiceSetVariable (
474 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,
475 &gEfiMemoryOverwriteControlDataGuid,
476 0, // Attributes
477 0, // DataSize
478 NULL // Data
479 );
480 mMorPassThru = FALSE;
481 }
482
483 //
484 // The MOR variable is absent; the platform firmware does not support it.
485 // Lock the variable so that no other module may create it.
486 //
487 VariableLockRequestToLock (
488 NULL, // This
489 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,
490 &gEfiMemoryOverwriteControlDataGuid
491 );
492
493 //
494 // Delete the MOR Control Lock variable too (should it exists for some
495 // reason) and prevent other modules from creating it.
496 //
497 mMorLockPassThru = TRUE;
498 VariableServiceSetVariable (
499 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME,
500 &gEfiMemoryOverwriteRequestControlLockGuid,
501 0, // Attributes
502 0, // DataSize
503 NULL // Data
504 );
505 mMorLockPassThru = FALSE;
506
507 VariableLockRequestToLock (
508 NULL, // This
509 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME,
510 &gEfiMemoryOverwriteRequestControlLockGuid
511 );
512 }