]> git.proxmox.com Git - mirror_edk2.git/blob - SecurityPkg/VariableAuthenticated/RuntimeDxe/VariableSmm.c
1. Fix TOCTOU issue in VariableSmm, FtwSmm, FpdtSmm, SmmCorePerformance SMM handler...
[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 - 2013, 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
33 #include <Library/SmmServicesTableLib.h>
34
35 #include <Guid/AuthenticatedVariableFormat.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 @retval EFI_INVALID_PARAMETER Input parameter is invalid.
333
334 **/
335 EFI_STATUS
336 SmmVariableGetStatistics (
337 IN OUT VARIABLE_INFO_ENTRY *InfoEntry,
338 IN OUT UINTN *InfoSize
339 )
340 {
341 VARIABLE_INFO_ENTRY *VariableInfo;
342 UINTN NameLength;
343 UINTN StatisticsInfoSize;
344 CHAR16 *InfoName;
345 EFI_GUID VendorGuid;
346
347 if (InfoEntry == NULL) {
348 return EFI_INVALID_PARAMETER;
349 }
350
351 VariableInfo = gVariableInfo;
352 if (VariableInfo == NULL) {
353 return EFI_UNSUPPORTED;
354 }
355
356 StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name);
357 if (*InfoSize < StatisticsInfoSize) {
358 *InfoSize = StatisticsInfoSize;
359 return EFI_BUFFER_TOO_SMALL;
360 }
361 InfoName = (CHAR16 *)(InfoEntry + 1);
362
363 CopyGuid (&VendorGuid, &InfoEntry->VendorGuid);
364
365 if (CompareGuid (&VendorGuid, &mZeroGuid)) {
366 //
367 // Return the first variable info
368 //
369 CopyMem (InfoEntry, VariableInfo, sizeof (VARIABLE_INFO_ENTRY));
370 CopyMem (InfoName, VariableInfo->Name, StrSize (VariableInfo->Name));
371 *InfoSize = StatisticsInfoSize;
372 return EFI_SUCCESS;
373 }
374
375 //
376 // Get the next variable info
377 //
378 while (VariableInfo != NULL) {
379 if (CompareGuid (&VariableInfo->VendorGuid, &VendorGuid)) {
380 NameLength = StrSize (VariableInfo->Name);
381 if (NameLength == StrSize (InfoName)) {
382 if (CompareMem (VariableInfo->Name, InfoName, NameLength) == 0) {
383 //
384 // Find the match one
385 //
386 VariableInfo = VariableInfo->Next;
387 break;
388 }
389 }
390 }
391 VariableInfo = VariableInfo->Next;
392 };
393
394 if (VariableInfo == NULL) {
395 *InfoSize = 0;
396 return EFI_SUCCESS;
397 }
398
399 //
400 // Output the new variable info
401 //
402 StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name);
403 if (*InfoSize < StatisticsInfoSize) {
404 *InfoSize = StatisticsInfoSize;
405 return EFI_BUFFER_TOO_SMALL;
406 }
407
408 CopyMem (InfoEntry, VariableInfo, sizeof (VARIABLE_INFO_ENTRY));
409 CopyMem (InfoName, VariableInfo->Name, StrSize (VariableInfo->Name));
410 *InfoSize = StatisticsInfoSize;
411
412 return EFI_SUCCESS;
413 }
414
415
416 /**
417 Communication service SMI Handler entry.
418
419 This SMI handler provides services for the variable wrapper driver.
420
421 Caution: This function may receive untrusted input.
422 This variable data and communicate buffer are external input, so this function will do basic validation.
423 Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(),
424 VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
425 SmmVariableGetStatistics() should also do validation based on its own knowledge.
426
427 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
428 @param[in] RegisterContext Points to an optional handler context which was specified when the
429 handler was registered.
430 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
431 be conveyed from a non-SMM environment into an SMM environment.
432 @param[in, out] CommBufferSize The size of the CommBuffer.
433
434 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
435 should still be called.
436 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
437 still be called.
438 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
439 be called.
440 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
441
442 **/
443 EFI_STATUS
444 EFIAPI
445 SmmVariableHandler (
446 IN EFI_HANDLE DispatchHandle,
447 IN CONST VOID *RegisterContext,
448 IN OUT VOID *CommBuffer,
449 IN OUT UINTN *CommBufferSize
450 )
451 {
452 EFI_STATUS Status;
453 SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader;
454 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader;
455 SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *GetNextVariableName;
456 SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *QueryVariableInfo;
457 VARIABLE_INFO_ENTRY *VariableInfo;
458 UINTN InfoSize;
459 UINTN NameBufferSize;
460 UINTN CommBufferPayloadSize;
461
462 //
463 // If input is invalid, stop processing this SMI
464 //
465 if (CommBuffer == NULL || CommBufferSize == NULL) {
466 return EFI_SUCCESS;
467 }
468
469 if (*CommBufferSize < SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {
470 DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer size invalid!\n"));
471 return EFI_SUCCESS;
472 }
473 CommBufferPayloadSize = *CommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
474 if (CommBufferPayloadSize > mVariableBufferPayloadSize) {
475 DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer payload size invalid!\n"));
476 return EFI_SUCCESS;
477 }
478
479 if (!InternalIsAddressValid ((UINTN)CommBuffer, *CommBufferSize)) {
480 DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer in SMRAM or overflow!\n"));
481 return EFI_SUCCESS;
482 }
483
484 SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *)CommBuffer;
485
486 switch (SmmVariableFunctionHeader->Function) {
487 case SMM_VARIABLE_FUNCTION_GET_VARIABLE:
488 if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) {
489 DEBUG ((EFI_D_ERROR, "GetVariable: SMM communication buffer size invalid!\n"));
490 return EFI_SUCCESS;
491 }
492 //
493 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
494 //
495 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
496 SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) mVariableBufferPayload;
497 if (((UINTN)(~0) - SmmVariableHeader->DataSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||
498 ((UINTN)(~0) - SmmVariableHeader->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) {
499 //
500 // Prevent InfoSize overflow happen
501 //
502 Status = EFI_ACCESS_DENIED;
503 goto EXIT;
504 }
505 InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)
506 + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize;
507
508 //
509 // SMRAM range check already covered before
510 //
511 if (InfoSize > CommBufferPayloadSize) {
512 DEBUG ((EFI_D_ERROR, "GetVariable: Data size exceed communication buffer size limit!\n"));
513 Status = EFI_ACCESS_DENIED;
514 goto EXIT;
515 }
516
517 if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize/sizeof (CHAR16) - 1] != L'\0') {
518 //
519 // Make sure VariableName is A Null-terminated string.
520 //
521 Status = EFI_ACCESS_DENIED;
522 goto EXIT;
523 }
524
525 Status = VariableServiceGetVariable (
526 SmmVariableHeader->Name,
527 &SmmVariableHeader->Guid,
528 &SmmVariableHeader->Attributes,
529 &SmmVariableHeader->DataSize,
530 (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize
531 );
532 CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize);
533 break;
534
535 case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME:
536 if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {
537 DEBUG ((EFI_D_ERROR, "GetNextVariableName: SMM communication buffer size invalid!\n"));
538 return EFI_SUCCESS;
539 }
540 //
541 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
542 //
543 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
544 GetNextVariableName = (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *) mVariableBufferPayload;
545 if ((UINTN)(~0) - GetNextVariableName->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {
546 //
547 // Prevent InfoSize overflow happen
548 //
549 Status = EFI_ACCESS_DENIED;
550 goto EXIT;
551 }
552 InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + GetNextVariableName->NameSize;
553
554 //
555 // SMRAM range check already covered before
556 //
557 if (InfoSize > CommBufferPayloadSize) {
558 DEBUG ((EFI_D_ERROR, "GetNextVariableName: Data size exceed communication buffer size limit!\n"));
559 Status = EFI_ACCESS_DENIED;
560 goto EXIT;
561 }
562
563 NameBufferSize = CommBufferPayloadSize - OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name);
564 if (NameBufferSize < sizeof (CHAR16) || GetNextVariableName->Name[NameBufferSize/sizeof (CHAR16) - 1] != L'\0') {
565 //
566 // Make sure input VariableName is A Null-terminated string.
567 //
568 Status = EFI_ACCESS_DENIED;
569 goto EXIT;
570 }
571
572 Status = VariableServiceGetNextVariableName (
573 &GetNextVariableName->NameSize,
574 GetNextVariableName->Name,
575 &GetNextVariableName->Guid
576 );
577 CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize);
578 break;
579
580 case SMM_VARIABLE_FUNCTION_SET_VARIABLE:
581 if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) {
582 DEBUG ((EFI_D_ERROR, "SetVariable: SMM communication buffer size invalid!\n"));
583 return EFI_SUCCESS;
584 }
585 //
586 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
587 //
588 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
589 SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) mVariableBufferPayload;
590 if (((UINTN)(~0) - SmmVariableHeader->DataSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||
591 ((UINTN)(~0) - SmmVariableHeader->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) {
592 //
593 // Prevent InfoSize overflow happen
594 //
595 Status = EFI_ACCESS_DENIED;
596 goto EXIT;
597 }
598 InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)
599 + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize;
600
601 //
602 // SMRAM range check already covered before
603 // Data buffer should not contain SMM range
604 //
605 if (InfoSize > CommBufferPayloadSize) {
606 DEBUG ((EFI_D_ERROR, "SetVariable: Data size exceed communication buffer size limit!\n"));
607 Status = EFI_ACCESS_DENIED;
608 goto EXIT;
609 }
610
611 if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize/sizeof (CHAR16) - 1] != L'\0') {
612 //
613 // Make sure VariableName is A Null-terminated string.
614 //
615 Status = EFI_ACCESS_DENIED;
616 goto EXIT;
617 }
618
619 Status = VariableServiceSetVariable (
620 SmmVariableHeader->Name,
621 &SmmVariableHeader->Guid,
622 SmmVariableHeader->Attributes,
623 SmmVariableHeader->DataSize,
624 (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize
625 );
626 break;
627
628 case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO:
629 if (CommBufferPayloadSize < sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO)) {
630 DEBUG ((EFI_D_ERROR, "QueryVariableInfo: SMM communication buffer size invalid!\n"));
631 return EFI_SUCCESS;
632 }
633 QueryVariableInfo = (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *) SmmVariableFunctionHeader->Data;
634
635 Status = VariableServiceQueryVariableInfo (
636 QueryVariableInfo->Attributes,
637 &QueryVariableInfo->MaximumVariableStorageSize,
638 &QueryVariableInfo->RemainingVariableStorageSize,
639 &QueryVariableInfo->MaximumVariableSize
640 );
641 break;
642
643 case SMM_VARIABLE_FUNCTION_READY_TO_BOOT:
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 = *CommBufferSize - 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 (InternalIsAddressInSmram ((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 default:
677 Status = EFI_UNSUPPORTED;
678 }
679
680 EXIT:
681
682 SmmVariableFunctionHeader->ReturnStatus = Status;
683 return EFI_SUCCESS;
684 }
685
686
687 /**
688 SMM Fault Tolerant Write protocol notification event handler.
689
690 Non-Volatile variable write may needs FTW protocol to reclaim when
691 writting variable.
692
693 @param Protocol Points to the protocol's unique identifier
694 @param Interface Points to the interface instance
695 @param Handle The handle on which the interface was installed
696
697 @retval EFI_SUCCESS SmmEventCallback runs successfully
698 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
699
700 **/
701 EFI_STATUS
702 EFIAPI
703 SmmFtwNotificationEvent (
704 IN CONST EFI_GUID *Protocol,
705 IN VOID *Interface,
706 IN EFI_HANDLE Handle
707 )
708 {
709 EFI_STATUS Status;
710 EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol;
711 EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol;
712 EFI_PHYSICAL_ADDRESS NvStorageVariableBase;
713
714 if (mVariableModuleGlobal->FvbInstance != NULL) {
715 return EFI_SUCCESS;
716 }
717
718 //
719 // Ensure SMM FTW protocol is installed.
720 //
721 Status = GetFtwProtocol ((VOID **)&FtwProtocol);
722 if (EFI_ERROR (Status)) {
723 return Status;
724 }
725
726 //
727 // Find the proper FVB protocol for variable.
728 //
729 NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageVariableBase64);
730 if (NvStorageVariableBase == 0) {
731 NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageVariableBase);
732 }
733 Status = GetFvbInfoByAddress (NvStorageVariableBase, NULL, &FvbProtocol);
734 if (EFI_ERROR (Status)) {
735 return EFI_NOT_FOUND;
736 }
737
738 mVariableModuleGlobal->FvbInstance = FvbProtocol;
739
740 Status = VariableWriteServiceInitialize ();
741 ASSERT_EFI_ERROR (Status);
742
743 //
744 // Notify the variable wrapper driver the variable write service is ready
745 //
746 Status = gBS->InstallProtocolInterface (
747 &mSmmVariableHandle,
748 &gSmmVariableWriteGuid,
749 EFI_NATIVE_INTERFACE,
750 NULL
751 );
752 ASSERT_EFI_ERROR (Status);
753
754 return EFI_SUCCESS;
755 }
756
757
758 /**
759 Variable Driver main entry point. The Variable driver places the 4 EFI
760 runtime services in the EFI System Table and installs arch protocols
761 for variable read and write services being available. It also registers
762 a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
763
764 @param[in] ImageHandle The firmware allocated handle for the EFI image.
765 @param[in] SystemTable A pointer to the EFI System Table.
766
767 @retval EFI_SUCCESS Variable service successfully initialized.
768
769 **/
770 EFI_STATUS
771 EFIAPI
772 VariableServiceInitialize (
773 IN EFI_HANDLE ImageHandle,
774 IN EFI_SYSTEM_TABLE *SystemTable
775 )
776 {
777 EFI_STATUS Status;
778 EFI_HANDLE VariableHandle;
779 VOID *SmmFtwRegistration;
780 EFI_SMM_ACCESS2_PROTOCOL *SmmAccess;
781 UINTN Size;
782
783 //
784 // Variable initialize.
785 //
786 Status = VariableCommonInitialize ();
787 ASSERT_EFI_ERROR (Status);
788
789 //
790 // Install the Smm Variable Protocol on a new handle.
791 //
792 VariableHandle = NULL;
793 Status = gSmst->SmmInstallProtocolInterface (
794 &VariableHandle,
795 &gEfiSmmVariableProtocolGuid,
796 EFI_NATIVE_INTERFACE,
797 &gSmmVariable
798 );
799 ASSERT_EFI_ERROR (Status);
800
801 //
802 // Get SMRAM information
803 //
804 Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess);
805 ASSERT_EFI_ERROR (Status);
806
807 Size = 0;
808 Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);
809 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
810
811 Status = gSmst->SmmAllocatePool (
812 EfiRuntimeServicesData,
813 Size,
814 (VOID **)&mSmramRanges
815 );
816 ASSERT_EFI_ERROR (Status);
817
818 Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmramRanges);
819 ASSERT_EFI_ERROR (Status);
820
821 mSmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
822
823 mVariableBufferPayloadSize = MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize)) +
824 OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - sizeof (VARIABLE_HEADER);
825
826 Status = gSmst->SmmAllocatePool (
827 EfiRuntimeServicesData,
828 mVariableBufferPayloadSize,
829 (VOID **)&mVariableBufferPayload
830 );
831 ASSERT_EFI_ERROR (Status);
832
833 ///
834 /// Register SMM variable SMI handler
835 ///
836 VariableHandle = NULL;
837 Status = gSmst->SmiHandlerRegister (SmmVariableHandler, &gEfiSmmVariableProtocolGuid, &VariableHandle);
838 ASSERT_EFI_ERROR (Status);
839
840 //
841 // Notify the variable wrapper driver the variable service is ready
842 //
843 Status = SystemTable->BootServices->InstallProtocolInterface (
844 &mVariableHandle,
845 &gEfiSmmVariableProtocolGuid,
846 EFI_NATIVE_INTERFACE,
847 &gSmmVariable
848 );
849 ASSERT_EFI_ERROR (Status);
850
851 //
852 // Register FtwNotificationEvent () notify function.
853 //
854 Status = gSmst->SmmRegisterProtocolNotify (
855 &gEfiSmmFaultTolerantWriteProtocolGuid,
856 SmmFtwNotificationEvent,
857 &SmmFtwRegistration
858 );
859 ASSERT_EFI_ERROR (Status);
860
861 SmmFtwNotificationEvent (NULL, NULL, NULL);
862
863 return EFI_SUCCESS;
864 }
865
866