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