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