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