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