]> git.proxmox.com Git - mirror_edk2.git/blob - SecurityPkg/VariableAuthenticated/RuntimeDxe/VariableSmm.c
Use SmmMemLib to check communication buffer.
[mirror_edk2.git] / SecurityPkg / VariableAuthenticated / RuntimeDxe / VariableSmm.c
1 /** @file
2 The sample implementation for SMM variable protocol. And this driver
3 implements an SMI handler to communicate with the DXE runtime driver
4 to provide variable services.
5
6 Caution: This module requires additional review when modified.
7 This driver will have external input - variable data and communicate buffer in SMM mode.
8 This external input must be validated carefully to avoid security issue like
9 buffer overflow, integer overflow.
10
11 SmmVariableHandler() will receive untrusted input and do basic validation.
12
13 Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(),
14 VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
15 SmmVariableGetStatistics() should also do validation based on its own knowledge.
16
17 Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR>
18 This program and the accompanying materials
19 are licensed and made available under the terms and conditions of the BSD License
20 which accompanies this distribution. The full text of the license may be found at
21 http://opensource.org/licenses/bsd-license.php
22
23 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
24 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
25
26 **/
27
28 #include <Protocol/SmmVariable.h>
29 #include <Protocol/SmmFirmwareVolumeBlock.h>
30 #include <Protocol/SmmFaultTolerantWrite.h>
31 #include <Protocol/SmmAccess2.h>
32 #include <Protocol/SmmEndOfDxe.h>
33 #include <Protocol/SmmVarCheck.h>
34
35 #include <Library/SmmServicesTableLib.h>
36 #include <Library/SmmMemLib.h>
37
38 #include <Guid/AuthenticatedVariableFormat.h>
39 #include <Guid/SmmVariableCommon.h>
40 #include "Variable.h"
41
42 extern VARIABLE_INFO_ENTRY *gVariableInfo;
43 EFI_HANDLE mSmmVariableHandle = NULL;
44 EFI_HANDLE mVariableHandle = NULL;
45 BOOLEAN mAtRuntime = FALSE;
46 EFI_GUID mZeroGuid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}};
47 UINT8 *mVariableBufferPayload = NULL;
48 UINTN mVariableBufferPayloadSize;
49 extern BOOLEAN mEndOfDxe;
50 extern BOOLEAN mEnableLocking;
51
52 /**
53 SecureBoot Hook for SetVariable.
54
55 @param[in] VariableName Name of Variable to be found.
56 @param[in] VendorGuid Variable vendor GUID.
57
58 **/
59 VOID
60 EFIAPI
61 SecureBootHook (
62 IN CHAR16 *VariableName,
63 IN EFI_GUID *VendorGuid
64 )
65 {
66 return ;
67 }
68
69 /**
70
71 This code sets variable in storage blocks (Volatile or Non-Volatile).
72
73 @param VariableName Name of Variable to be found.
74 @param VendorGuid Variable vendor GUID.
75 @param Attributes Attribute value of the variable found
76 @param DataSize Size of Data found. If size is less than the
77 data, this value contains the required size.
78 @param Data Data pointer.
79
80 @return EFI_INVALID_PARAMETER Invalid parameter.
81 @return EFI_SUCCESS Set successfully.
82 @return EFI_OUT_OF_RESOURCES Resource not enough to set variable.
83 @return EFI_NOT_FOUND Not found.
84 @return EFI_WRITE_PROTECTED Variable is read-only.
85
86 **/
87 EFI_STATUS
88 EFIAPI
89 SmmVariableSetVariable (
90 IN CHAR16 *VariableName,
91 IN EFI_GUID *VendorGuid,
92 IN UINT32 Attributes,
93 IN UINTN DataSize,
94 IN VOID *Data
95 )
96 {
97 EFI_STATUS Status;
98
99 //
100 // Disable write protection when the calling SetVariable() through EFI_SMM_VARIABLE_PROTOCOL.
101 //
102 mEnableLocking = FALSE;
103 Status = VariableServiceSetVariable (
104 VariableName,
105 VendorGuid,
106 Attributes,
107 DataSize,
108 Data
109 );
110 mEnableLocking = TRUE;
111 return Status;
112 }
113
114 EFI_SMM_VARIABLE_PROTOCOL gSmmVariable = {
115 VariableServiceGetVariable,
116 VariableServiceGetNextVariableName,
117 SmmVariableSetVariable,
118 VariableServiceQueryVariableInfo
119 };
120
121 EDKII_SMM_VAR_CHECK_PROTOCOL mSmmVarCheck = { VarCheckRegisterSetVariableCheckHandler,
122 VarCheckVariablePropertySet,
123 VarCheckVariablePropertyGet };
124
125 /**
126 Return TRUE if ExitBootServices () has been called.
127
128 @retval TRUE If ExitBootServices () has been called.
129 **/
130 BOOLEAN
131 AtRuntime (
132 VOID
133 )
134 {
135 return mAtRuntime;
136 }
137
138 /**
139 Initializes a basic mutual exclusion lock.
140
141 This function initializes a basic mutual exclusion lock to the released state
142 and returns the lock. Each lock provides mutual exclusion access at its task
143 priority level. Since there is no preemption or multiprocessor support in EFI,
144 acquiring the lock only consists of raising to the locks TPL.
145 If Lock is NULL, then ASSERT().
146 If Priority is not a valid TPL value, then ASSERT().
147
148 @param Lock A pointer to the lock data structure to initialize.
149 @param Priority EFI TPL is associated with the lock.
150
151 @return The lock.
152
153 **/
154 EFI_LOCK *
155 InitializeLock (
156 IN OUT EFI_LOCK *Lock,
157 IN EFI_TPL Priority
158 )
159 {
160 return Lock;
161 }
162
163 /**
164 Acquires lock only at boot time. Simply returns at runtime.
165
166 This is a temperary function that will be removed when
167 EfiAcquireLock() in UefiLib can handle the call in UEFI
168 Runtimer driver in RT phase.
169 It calls EfiAcquireLock() at boot time, and simply returns
170 at runtime.
171
172 @param Lock A pointer to the lock to acquire.
173
174 **/
175 VOID
176 AcquireLockOnlyAtBootTime (
177 IN EFI_LOCK *Lock
178 )
179 {
180
181 }
182
183
184 /**
185 Releases lock only at boot time. Simply returns at runtime.
186
187 This is a temperary function which will be removed when
188 EfiReleaseLock() in UefiLib can handle the call in UEFI
189 Runtimer driver in RT phase.
190 It calls EfiReleaseLock() at boot time and simply returns
191 at runtime.
192
193 @param Lock A pointer to the lock to release.
194
195 **/
196 VOID
197 ReleaseLockOnlyAtBootTime (
198 IN EFI_LOCK *Lock
199 )
200 {
201
202 }
203
204 /**
205 Retrive the SMM Fault Tolerent Write protocol interface.
206
207 @param[out] FtwProtocol The interface of SMM Ftw protocol
208
209 @retval EFI_SUCCESS The SMM FTW protocol instance was found and returned in FtwProtocol.
210 @retval EFI_NOT_FOUND The SMM FTW protocol instance was not found.
211 @retval EFI_INVALID_PARAMETER SarProtocol is NULL.
212
213 **/
214 EFI_STATUS
215 GetFtwProtocol (
216 OUT VOID **FtwProtocol
217 )
218 {
219 EFI_STATUS Status;
220
221 //
222 // Locate Smm Fault Tolerent Write protocol
223 //
224 Status = gSmst->SmmLocateProtocol (
225 &gEfiSmmFaultTolerantWriteProtocolGuid,
226 NULL,
227 FtwProtocol
228 );
229 return Status;
230 }
231
232
233 /**
234 Retrive the SMM FVB protocol interface by HANDLE.
235
236 @param[in] FvBlockHandle The handle of SMM FVB protocol that provides services for
237 reading, writing, and erasing the target block.
238 @param[out] FvBlock The interface of SMM FVB protocol
239
240 @retval EFI_SUCCESS The interface information for the specified protocol was returned.
241 @retval EFI_UNSUPPORTED The device does not support the SMM FVB protocol.
242 @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.
243
244 **/
245 EFI_STATUS
246 GetFvbByHandle (
247 IN EFI_HANDLE FvBlockHandle,
248 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
249 )
250 {
251 //
252 // To get the SMM FVB protocol interface on the handle
253 //
254 return gSmst->SmmHandleProtocol (
255 FvBlockHandle,
256 &gEfiSmmFirmwareVolumeBlockProtocolGuid,
257 (VOID **) FvBlock
258 );
259 }
260
261
262 /**
263 Function returns an array of handles that support the SMM FVB protocol
264 in a buffer allocated from pool.
265
266 @param[out] NumberHandles The number of handles returned in Buffer.
267 @param[out] Buffer A pointer to the buffer to return the requested
268 array of handles that support SMM FVB protocol.
269
270 @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of
271 handles in Buffer was returned in NumberHandles.
272 @retval EFI_NOT_FOUND No SMM FVB handle was found.
273 @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results.
274 @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.
275
276 **/
277 EFI_STATUS
278 GetFvbCountAndBuffer (
279 OUT UINTN *NumberHandles,
280 OUT EFI_HANDLE **Buffer
281 )
282 {
283 EFI_STATUS Status;
284 UINTN BufferSize;
285
286 if ((NumberHandles == NULL) || (Buffer == NULL)) {
287 return EFI_INVALID_PARAMETER;
288 }
289
290 BufferSize = 0;
291 *NumberHandles = 0;
292 *Buffer = NULL;
293 Status = gSmst->SmmLocateHandle (
294 ByProtocol,
295 &gEfiSmmFirmwareVolumeBlockProtocolGuid,
296 NULL,
297 &BufferSize,
298 *Buffer
299 );
300 if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) {
301 return EFI_NOT_FOUND;
302 }
303
304 *Buffer = AllocatePool (BufferSize);
305 if (*Buffer == NULL) {
306 return EFI_OUT_OF_RESOURCES;
307 }
308
309 Status = gSmst->SmmLocateHandle (
310 ByProtocol,
311 &gEfiSmmFirmwareVolumeBlockProtocolGuid,
312 NULL,
313 &BufferSize,
314 *Buffer
315 );
316
317 *NumberHandles = BufferSize / sizeof(EFI_HANDLE);
318 if (EFI_ERROR(Status)) {
319 *NumberHandles = 0;
320 FreePool (*Buffer);
321 *Buffer = NULL;
322 }
323
324 return Status;
325 }
326
327
328 /**
329 Get the variable statistics information from the information buffer pointed by gVariableInfo.
330
331 Caution: This function may be invoked at SMM runtime.
332 InfoEntry and InfoSize are external input. Care must be taken to make sure not security issue at runtime.
333
334 @param[in, out] InfoEntry A pointer to the buffer of variable information entry.
335 On input, point to the variable information returned last time. if
336 InfoEntry->VendorGuid is zero, return the first information.
337 On output, point to the next variable information.
338 @param[in, out] InfoSize On input, the size of the variable information buffer.
339 On output, the returned variable information size.
340
341 @retval EFI_SUCCESS The variable information is found and returned successfully.
342 @retval EFI_UNSUPPORTED No variable inoformation exists in variable driver. The
343 PcdVariableCollectStatistics should be set TRUE to support it.
344 @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the next variable information.
345 @retval EFI_INVALID_PARAMETER Input parameter is invalid.
346
347 **/
348 EFI_STATUS
349 SmmVariableGetStatistics (
350 IN OUT VARIABLE_INFO_ENTRY *InfoEntry,
351 IN OUT UINTN *InfoSize
352 )
353 {
354 VARIABLE_INFO_ENTRY *VariableInfo;
355 UINTN NameLength;
356 UINTN StatisticsInfoSize;
357 CHAR16 *InfoName;
358 EFI_GUID VendorGuid;
359
360 if (InfoEntry == NULL) {
361 return EFI_INVALID_PARAMETER;
362 }
363
364 VariableInfo = gVariableInfo;
365 if (VariableInfo == NULL) {
366 return EFI_UNSUPPORTED;
367 }
368
369 StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name);
370 if (*InfoSize < StatisticsInfoSize) {
371 *InfoSize = StatisticsInfoSize;
372 return EFI_BUFFER_TOO_SMALL;
373 }
374 InfoName = (CHAR16 *)(InfoEntry + 1);
375
376 CopyGuid (&VendorGuid, &InfoEntry->VendorGuid);
377
378 if (CompareGuid (&VendorGuid, &mZeroGuid)) {
379 //
380 // Return the first variable info
381 //
382 CopyMem (InfoEntry, VariableInfo, sizeof (VARIABLE_INFO_ENTRY));
383 CopyMem (InfoName, VariableInfo->Name, StrSize (VariableInfo->Name));
384 *InfoSize = StatisticsInfoSize;
385 return EFI_SUCCESS;
386 }
387
388 //
389 // Get the next variable info
390 //
391 while (VariableInfo != NULL) {
392 if (CompareGuid (&VariableInfo->VendorGuid, &VendorGuid)) {
393 NameLength = StrSize (VariableInfo->Name);
394 if (NameLength == StrSize (InfoName)) {
395 if (CompareMem (VariableInfo->Name, InfoName, NameLength) == 0) {
396 //
397 // Find the match one
398 //
399 VariableInfo = VariableInfo->Next;
400 break;
401 }
402 }
403 }
404 VariableInfo = VariableInfo->Next;
405 };
406
407 if (VariableInfo == NULL) {
408 *InfoSize = 0;
409 return EFI_SUCCESS;
410 }
411
412 //
413 // Output the new variable info
414 //
415 StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name);
416 if (*InfoSize < StatisticsInfoSize) {
417 *InfoSize = StatisticsInfoSize;
418 return EFI_BUFFER_TOO_SMALL;
419 }
420
421 CopyMem (InfoEntry, VariableInfo, sizeof (VARIABLE_INFO_ENTRY));
422 CopyMem (InfoName, VariableInfo->Name, StrSize (VariableInfo->Name));
423 *InfoSize = StatisticsInfoSize;
424
425 return EFI_SUCCESS;
426 }
427
428
429 /**
430 Communication service SMI Handler entry.
431
432 This SMI handler provides services for the variable wrapper driver.
433
434 Caution: This function may receive untrusted input.
435 This variable data and communicate buffer are external input, so this function will do basic validation.
436 Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(),
437 VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
438 SmmVariableGetStatistics() should also do validation based on its own knowledge.
439
440 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
441 @param[in] RegisterContext Points to an optional handler context which was specified when the
442 handler was registered.
443 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
444 be conveyed from a non-SMM environment into an SMM environment.
445 @param[in, out] CommBufferSize The size of the CommBuffer.
446
447 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
448 should still be called.
449 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
450 still be called.
451 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
452 be called.
453 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
454
455 **/
456 EFI_STATUS
457 EFIAPI
458 SmmVariableHandler (
459 IN EFI_HANDLE DispatchHandle,
460 IN CONST VOID *RegisterContext,
461 IN OUT VOID *CommBuffer,
462 IN OUT UINTN *CommBufferSize
463 )
464 {
465 EFI_STATUS Status;
466 SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader;
467 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader;
468 SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *GetNextVariableName;
469 SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *QueryVariableInfo;
470 VARIABLE_INFO_ENTRY *VariableInfo;
471 SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *VariableToLock;
472 SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *CommVariableProperty;
473 UINTN InfoSize;
474 UINTN NameBufferSize;
475 UINTN CommBufferPayloadSize;
476 UINTN TempCommBufferSize;
477
478 //
479 // If input is invalid, stop processing this SMI
480 //
481 if (CommBuffer == NULL || CommBufferSize == NULL) {
482 return EFI_SUCCESS;
483 }
484
485 TempCommBufferSize = *CommBufferSize;
486
487 if (TempCommBufferSize < SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {
488 DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer size invalid!\n"));
489 return EFI_SUCCESS;
490 }
491 CommBufferPayloadSize = TempCommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
492 if (CommBufferPayloadSize > mVariableBufferPayloadSize) {
493 DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer payload size invalid!\n"));
494 return EFI_SUCCESS;
495 }
496
497 if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {
498 DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer in SMRAM or overflow!\n"));
499 return EFI_SUCCESS;
500 }
501
502 SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *)CommBuffer;
503
504 switch (SmmVariableFunctionHeader->Function) {
505 case SMM_VARIABLE_FUNCTION_GET_VARIABLE:
506 if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) {
507 DEBUG ((EFI_D_ERROR, "GetVariable: SMM communication buffer size invalid!\n"));
508 return EFI_SUCCESS;
509 }
510 //
511 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
512 //
513 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
514 SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) mVariableBufferPayload;
515 if (((UINTN)(~0) - SmmVariableHeader->DataSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||
516 ((UINTN)(~0) - SmmVariableHeader->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) {
517 //
518 // Prevent InfoSize overflow happen
519 //
520 Status = EFI_ACCESS_DENIED;
521 goto EXIT;
522 }
523 InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)
524 + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize;
525
526 //
527 // SMRAM range check already covered before
528 //
529 if (InfoSize > CommBufferPayloadSize) {
530 DEBUG ((EFI_D_ERROR, "GetVariable: Data size exceed communication buffer size limit!\n"));
531 Status = EFI_ACCESS_DENIED;
532 goto EXIT;
533 }
534
535 if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize/sizeof (CHAR16) - 1] != L'\0') {
536 //
537 // Make sure VariableName is A Null-terminated string.
538 //
539 Status = EFI_ACCESS_DENIED;
540 goto EXIT;
541 }
542
543 Status = VariableServiceGetVariable (
544 SmmVariableHeader->Name,
545 &SmmVariableHeader->Guid,
546 &SmmVariableHeader->Attributes,
547 &SmmVariableHeader->DataSize,
548 (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize
549 );
550 CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize);
551 break;
552
553 case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME:
554 if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {
555 DEBUG ((EFI_D_ERROR, "GetNextVariableName: SMM communication buffer size invalid!\n"));
556 return EFI_SUCCESS;
557 }
558 //
559 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
560 //
561 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
562 GetNextVariableName = (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *) mVariableBufferPayload;
563 if ((UINTN)(~0) - GetNextVariableName->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {
564 //
565 // Prevent InfoSize overflow happen
566 //
567 Status = EFI_ACCESS_DENIED;
568 goto EXIT;
569 }
570 InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + GetNextVariableName->NameSize;
571
572 //
573 // SMRAM range check already covered before
574 //
575 if (InfoSize > CommBufferPayloadSize) {
576 DEBUG ((EFI_D_ERROR, "GetNextVariableName: Data size exceed communication buffer size limit!\n"));
577 Status = EFI_ACCESS_DENIED;
578 goto EXIT;
579 }
580
581 NameBufferSize = CommBufferPayloadSize - OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name);
582 if (NameBufferSize < sizeof (CHAR16) || GetNextVariableName->Name[NameBufferSize/sizeof (CHAR16) - 1] != L'\0') {
583 //
584 // Make sure input VariableName is A Null-terminated string.
585 //
586 Status = EFI_ACCESS_DENIED;
587 goto EXIT;
588 }
589
590 Status = VariableServiceGetNextVariableName (
591 &GetNextVariableName->NameSize,
592 GetNextVariableName->Name,
593 &GetNextVariableName->Guid
594 );
595 CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize);
596 break;
597
598 case SMM_VARIABLE_FUNCTION_SET_VARIABLE:
599 if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) {
600 DEBUG ((EFI_D_ERROR, "SetVariable: SMM communication buffer size invalid!\n"));
601 return EFI_SUCCESS;
602 }
603 //
604 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
605 //
606 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
607 SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) mVariableBufferPayload;
608 if (((UINTN)(~0) - SmmVariableHeader->DataSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||
609 ((UINTN)(~0) - SmmVariableHeader->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) {
610 //
611 // Prevent InfoSize overflow happen
612 //
613 Status = EFI_ACCESS_DENIED;
614 goto EXIT;
615 }
616 InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)
617 + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize;
618
619 //
620 // SMRAM range check already covered before
621 // Data buffer should not contain SMM range
622 //
623 if (InfoSize > CommBufferPayloadSize) {
624 DEBUG ((EFI_D_ERROR, "SetVariable: Data size exceed communication buffer size limit!\n"));
625 Status = EFI_ACCESS_DENIED;
626 goto EXIT;
627 }
628
629 if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize/sizeof (CHAR16) - 1] != L'\0') {
630 //
631 // Make sure VariableName is A Null-terminated string.
632 //
633 Status = EFI_ACCESS_DENIED;
634 goto EXIT;
635 }
636
637 Status = VariableServiceSetVariable (
638 SmmVariableHeader->Name,
639 &SmmVariableHeader->Guid,
640 SmmVariableHeader->Attributes,
641 SmmVariableHeader->DataSize,
642 (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize
643 );
644 break;
645
646 case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO:
647 if (CommBufferPayloadSize < sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO)) {
648 DEBUG ((EFI_D_ERROR, "QueryVariableInfo: SMM communication buffer size invalid!\n"));
649 return EFI_SUCCESS;
650 }
651 QueryVariableInfo = (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *) SmmVariableFunctionHeader->Data;
652
653 Status = VariableServiceQueryVariableInfo (
654 QueryVariableInfo->Attributes,
655 &QueryVariableInfo->MaximumVariableStorageSize,
656 &QueryVariableInfo->RemainingVariableStorageSize,
657 &QueryVariableInfo->MaximumVariableSize
658 );
659 break;
660
661 case SMM_VARIABLE_FUNCTION_READY_TO_BOOT:
662 mEndOfDxe = TRUE;
663 //
664 // The initialization for variable quota.
665 //
666 InitializeVariableQuota ();
667 if (AtRuntime()) {
668 Status = EFI_UNSUPPORTED;
669 break;
670 }
671 ReclaimForOS ();
672 Status = EFI_SUCCESS;
673 break;
674
675 case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE:
676 mAtRuntime = TRUE;
677 Status = EFI_SUCCESS;
678 break;
679
680 case SMM_VARIABLE_FUNCTION_GET_STATISTICS:
681 VariableInfo = (VARIABLE_INFO_ENTRY *) SmmVariableFunctionHeader->Data;
682 InfoSize = TempCommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
683
684 //
685 // Do not need to check SmmVariableFunctionHeader->Data in SMRAM here.
686 // It is covered by previous CommBuffer check
687 //
688
689 if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)CommBufferSize, sizeof(UINTN))) {
690 DEBUG ((EFI_D_ERROR, "GetStatistics: SMM communication buffer in SMRAM!\n"));
691 Status = EFI_ACCESS_DENIED;
692 goto EXIT;
693 }
694
695 Status = SmmVariableGetStatistics (VariableInfo, &InfoSize);
696 *CommBufferSize = InfoSize + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
697 break;
698
699 case SMM_VARIABLE_FUNCTION_LOCK_VARIABLE:
700 if (mEndOfDxe) {
701 Status = EFI_ACCESS_DENIED;
702 } else {
703 VariableToLock = (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *) SmmVariableFunctionHeader->Data;
704 Status = VariableLockRequestToLock (
705 NULL,
706 VariableToLock->Name,
707 &VariableToLock->Guid
708 );
709 }
710 break;
711 case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET:
712 if (mEndOfDxe) {
713 Status = EFI_ACCESS_DENIED;
714 } else {
715 CommVariableProperty = (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *) SmmVariableFunctionHeader->Data;
716 Status = VarCheckVariablePropertySet (
717 CommVariableProperty->Name,
718 &CommVariableProperty->Guid,
719 &CommVariableProperty->VariableProperty
720 );
721 }
722 break;
723 case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET:
724 if (CommBufferPayloadSize < OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) {
725 DEBUG ((EFI_D_ERROR, "VarCheckVariablePropertyGet: SMM communication buffer size invalid!\n"));
726 return EFI_SUCCESS;
727 }
728 //
729 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
730 //
731 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
732 CommVariableProperty = (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *) mVariableBufferPayload;
733 if ((UINTN) (~0) - CommVariableProperty->NameSize < OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) {
734 //
735 // Prevent InfoSize overflow happen
736 //
737 Status = EFI_ACCESS_DENIED;
738 goto EXIT;
739 }
740 InfoSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) + CommVariableProperty->NameSize;
741
742 //
743 // SMRAM range check already covered before
744 //
745 if (InfoSize > CommBufferPayloadSize) {
746 DEBUG ((EFI_D_ERROR, "VarCheckVariablePropertyGet: Data size exceed communication buffer size limit!\n"));
747 Status = EFI_ACCESS_DENIED;
748 goto EXIT;
749 }
750
751 if (CommVariableProperty->NameSize < sizeof (CHAR16) || CommVariableProperty->Name[CommVariableProperty->NameSize/sizeof (CHAR16) - 1] != L'\0') {
752 //
753 // Make sure VariableName is A Null-terminated string.
754 //
755 Status = EFI_ACCESS_DENIED;
756 goto EXIT;
757 }
758
759 Status = VarCheckVariablePropertyGet (
760 CommVariableProperty->Name,
761 &CommVariableProperty->Guid,
762 &CommVariableProperty->VariableProperty
763 );
764 CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize);
765 break;
766
767 default:
768 Status = EFI_UNSUPPORTED;
769 }
770
771 EXIT:
772
773 SmmVariableFunctionHeader->ReturnStatus = Status;
774 return EFI_SUCCESS;
775 }
776
777 /**
778 SMM END_OF_DXE protocol notification event handler.
779
780 @param Protocol Points to the protocol's unique identifier
781 @param Interface Points to the interface instance
782 @param Handle The handle on which the interface was installed
783
784 @retval EFI_SUCCESS SmmEndOfDxeCallback runs successfully
785
786 **/
787 EFI_STATUS
788 EFIAPI
789 SmmEndOfDxeCallback (
790 IN CONST EFI_GUID *Protocol,
791 IN VOID *Interface,
792 IN EFI_HANDLE Handle
793 )
794 {
795 DEBUG ((EFI_D_INFO, "[Variable]END_OF_DXE is signaled\n"));
796 mEndOfDxe = TRUE;
797 //
798 // The initialization for variable quota.
799 //
800 InitializeVariableQuota ();
801 if (PcdGetBool (PcdReclaimVariableSpaceAtEndOfDxe)) {
802 ReclaimForOS ();
803 }
804 return EFI_SUCCESS;
805 }
806
807 /**
808 SMM Fault Tolerant Write protocol notification event handler.
809
810 Non-Volatile variable write may needs FTW protocol to reclaim when
811 writting variable.
812
813 @param Protocol Points to the protocol's unique identifier
814 @param Interface Points to the interface instance
815 @param Handle The handle on which the interface was installed
816
817 @retval EFI_SUCCESS SmmEventCallback runs successfully
818 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
819
820 **/
821 EFI_STATUS
822 EFIAPI
823 SmmFtwNotificationEvent (
824 IN CONST EFI_GUID *Protocol,
825 IN VOID *Interface,
826 IN EFI_HANDLE Handle
827 )
828 {
829 EFI_STATUS Status;
830 EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol;
831 EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol;
832 EFI_PHYSICAL_ADDRESS NvStorageVariableBase;
833 UINTN FtwMaxBlockSize;
834
835 if (mVariableModuleGlobal->FvbInstance != NULL) {
836 return EFI_SUCCESS;
837 }
838
839 //
840 // Ensure SMM FTW protocol is installed.
841 //
842 Status = GetFtwProtocol ((VOID **)&FtwProtocol);
843 if (EFI_ERROR (Status)) {
844 return Status;
845 }
846
847 Status = FtwProtocol->GetMaxBlockSize (FtwProtocol, &FtwMaxBlockSize);
848 if (!EFI_ERROR (Status)) {
849 ASSERT (PcdGet32 (PcdFlashNvStorageVariableSize) <= FtwMaxBlockSize);
850 }
851
852 //
853 // Find the proper FVB protocol for variable.
854 //
855 NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageVariableBase64);
856 if (NvStorageVariableBase == 0) {
857 NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageVariableBase);
858 }
859 Status = GetFvbInfoByAddress (NvStorageVariableBase, NULL, &FvbProtocol);
860 if (EFI_ERROR (Status)) {
861 return EFI_NOT_FOUND;
862 }
863
864 mVariableModuleGlobal->FvbInstance = FvbProtocol;
865
866 Status = VariableWriteServiceInitialize ();
867 if (EFI_ERROR (Status)) {
868 DEBUG ((DEBUG_ERROR, "Variable write service initialization failed. Status = %r\n", Status));
869 }
870
871 //
872 // Notify the variable wrapper driver the variable write service is ready
873 //
874 Status = gBS->InstallProtocolInterface (
875 &mSmmVariableHandle,
876 &gSmmVariableWriteGuid,
877 EFI_NATIVE_INTERFACE,
878 NULL
879 );
880 ASSERT_EFI_ERROR (Status);
881
882 return EFI_SUCCESS;
883 }
884
885
886 /**
887 Variable Driver main entry point. The Variable driver places the 4 EFI
888 runtime services in the EFI System Table and installs arch protocols
889 for variable read and write services being available. It also registers
890 a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
891
892 @param[in] ImageHandle The firmware allocated handle for the EFI image.
893 @param[in] SystemTable A pointer to the EFI System Table.
894
895 @retval EFI_SUCCESS Variable service successfully initialized.
896
897 **/
898 EFI_STATUS
899 EFIAPI
900 VariableServiceInitialize (
901 IN EFI_HANDLE ImageHandle,
902 IN EFI_SYSTEM_TABLE *SystemTable
903 )
904 {
905 EFI_STATUS Status;
906 EFI_HANDLE VariableHandle;
907 VOID *SmmFtwRegistration;
908 VOID *SmmEndOfDxeRegistration;
909
910 //
911 // Variable initialize.
912 //
913 Status = VariableCommonInitialize ();
914 ASSERT_EFI_ERROR (Status);
915
916 //
917 // Install the Smm Variable Protocol on a new handle.
918 //
919 VariableHandle = NULL;
920 Status = gSmst->SmmInstallProtocolInterface (
921 &VariableHandle,
922 &gEfiSmmVariableProtocolGuid,
923 EFI_NATIVE_INTERFACE,
924 &gSmmVariable
925 );
926 ASSERT_EFI_ERROR (Status);
927
928 Status = gSmst->SmmInstallProtocolInterface (
929 &VariableHandle,
930 &gEdkiiSmmVarCheckProtocolGuid,
931 EFI_NATIVE_INTERFACE,
932 &mSmmVarCheck
933 );
934 ASSERT_EFI_ERROR (Status);
935
936 mVariableBufferPayloadSize = MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize)) +
937 OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) - sizeof (VARIABLE_HEADER);
938
939 Status = gSmst->SmmAllocatePool (
940 EfiRuntimeServicesData,
941 mVariableBufferPayloadSize,
942 (VOID **)&mVariableBufferPayload
943 );
944 ASSERT_EFI_ERROR (Status);
945
946 ///
947 /// Register SMM variable SMI handler
948 ///
949 VariableHandle = NULL;
950 Status = gSmst->SmiHandlerRegister (SmmVariableHandler, &gEfiSmmVariableProtocolGuid, &VariableHandle);
951 ASSERT_EFI_ERROR (Status);
952
953 //
954 // Notify the variable wrapper driver the variable service is ready
955 //
956 Status = SystemTable->BootServices->InstallProtocolInterface (
957 &mVariableHandle,
958 &gEfiSmmVariableProtocolGuid,
959 EFI_NATIVE_INTERFACE,
960 &gSmmVariable
961 );
962 ASSERT_EFI_ERROR (Status);
963
964 //
965 // Register EFI_SMM_END_OF_DXE_PROTOCOL_GUID notify function.
966 //
967 Status = gSmst->SmmRegisterProtocolNotify (
968 &gEfiSmmEndOfDxeProtocolGuid,
969 SmmEndOfDxeCallback,
970 &SmmEndOfDxeRegistration
971 );
972 ASSERT_EFI_ERROR (Status);
973
974 //
975 // Register FtwNotificationEvent () notify function.
976 //
977 Status = gSmst->SmmRegisterProtocolNotify (
978 &gEfiSmmFaultTolerantWriteProtocolGuid,
979 SmmFtwNotificationEvent,
980 &SmmFtwRegistration
981 );
982 ASSERT_EFI_ERROR (Status);
983
984 SmmFtwNotificationEvent (NULL, NULL, NULL);
985
986 return EFI_SUCCESS;
987 }
988
989