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