]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c
MdeModulePkg/Variable: Consolidate common parsing functions
[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 - 2019, Intel Corporation. All rights reserved.<BR>
18 Copyright (c) 2018, Linaro, Ltd. All rights reserved.<BR>
19 SPDX-License-Identifier: BSD-2-Clause-Patent
20
21 **/
22
23 #include <Protocol/SmmVariable.h>
24 #include <Protocol/SmmFirmwareVolumeBlock.h>
25 #include <Protocol/SmmFaultTolerantWrite.h>
26 #include <Protocol/MmEndOfDxe.h>
27 #include <Protocol/SmmVarCheck.h>
28
29 #include <Library/MmServicesTableLib.h>
30
31 #include <Guid/SmmVariableCommon.h>
32 #include "Variable.h"
33 #include "VariableParsing.h"
34
35 BOOLEAN mAtRuntime = FALSE;
36 UINT8 *mVariableBufferPayload = NULL;
37 UINTN mVariableBufferPayloadSize;
38
39 /**
40 SecureBoot Hook for SetVariable.
41
42 @param[in] VariableName Name of Variable to be found.
43 @param[in] VendorGuid Variable vendor GUID.
44
45 **/
46 VOID
47 EFIAPI
48 SecureBootHook (
49 IN CHAR16 *VariableName,
50 IN EFI_GUID *VendorGuid
51 )
52 {
53 return ;
54 }
55
56 /**
57
58 This code sets variable in storage blocks (Volatile or Non-Volatile).
59
60 @param VariableName Name of Variable to be found.
61 @param VendorGuid Variable vendor GUID.
62 @param Attributes Attribute value of the variable found
63 @param DataSize Size of Data found. If size is less than the
64 data, this value contains the required size.
65 @param Data Data pointer.
66
67 @return EFI_INVALID_PARAMETER Invalid parameter.
68 @return EFI_SUCCESS Set successfully.
69 @return EFI_OUT_OF_RESOURCES Resource not enough to set variable.
70 @return EFI_NOT_FOUND Not found.
71 @return EFI_WRITE_PROTECTED Variable is read-only.
72
73 **/
74 EFI_STATUS
75 EFIAPI
76 SmmVariableSetVariable (
77 IN CHAR16 *VariableName,
78 IN EFI_GUID *VendorGuid,
79 IN UINT32 Attributes,
80 IN UINTN DataSize,
81 IN VOID *Data
82 )
83 {
84 EFI_STATUS Status;
85
86 //
87 // Disable write protection when the calling SetVariable() through EFI_SMM_VARIABLE_PROTOCOL.
88 //
89 mRequestSource = VarCheckFromTrusted;
90 Status = VariableServiceSetVariable (
91 VariableName,
92 VendorGuid,
93 Attributes,
94 DataSize,
95 Data
96 );
97 mRequestSource = VarCheckFromUntrusted;
98 return Status;
99 }
100
101 EFI_SMM_VARIABLE_PROTOCOL gSmmVariable = {
102 VariableServiceGetVariable,
103 VariableServiceGetNextVariableName,
104 SmmVariableSetVariable,
105 VariableServiceQueryVariableInfo
106 };
107
108 EDKII_SMM_VAR_CHECK_PROTOCOL mSmmVarCheck = { VarCheckRegisterSetVariableCheckHandler,
109 VarCheckVariablePropertySet,
110 VarCheckVariablePropertyGet };
111
112 /**
113 Return TRUE if ExitBootServices () has been called.
114
115 @retval TRUE If ExitBootServices () has been called.
116 **/
117 BOOLEAN
118 AtRuntime (
119 VOID
120 )
121 {
122 return mAtRuntime;
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 Retrieve 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 = gMmst->MmLocateProtocol (
212 &gEfiSmmFaultTolerantWriteProtocolGuid,
213 NULL,
214 FtwProtocol
215 );
216 return Status;
217 }
218
219
220 /**
221 Retrieve 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 gMmst->MmHandleProtocol (
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 = gMmst->MmLocateHandle (
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 = gMmst->MmLocateHandle (
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 NameSize;
343 UINTN StatisticsInfoSize;
344 CHAR16 *InfoName;
345 UINTN InfoNameMaxSize;
346 EFI_GUID VendorGuid;
347
348 if (InfoEntry == NULL) {
349 return EFI_INVALID_PARAMETER;
350 }
351
352 VariableInfo = gVariableInfo;
353 if (VariableInfo == NULL) {
354 return EFI_UNSUPPORTED;
355 }
356
357 StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY);
358 if (*InfoSize < StatisticsInfoSize) {
359 *InfoSize = StatisticsInfoSize;
360 return EFI_BUFFER_TOO_SMALL;
361 }
362 InfoName = (CHAR16 *)(InfoEntry + 1);
363 InfoNameMaxSize = (*InfoSize - sizeof (VARIABLE_INFO_ENTRY));
364
365 CopyGuid (&VendorGuid, &InfoEntry->VendorGuid);
366
367 if (IsZeroGuid (&VendorGuid)) {
368 //
369 // Return the first variable info
370 //
371 NameSize = StrSize (VariableInfo->Name);
372 StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + NameSize;
373 if (*InfoSize < StatisticsInfoSize) {
374 *InfoSize = StatisticsInfoSize;
375 return EFI_BUFFER_TOO_SMALL;
376 }
377 CopyMem (InfoEntry, VariableInfo, sizeof (VARIABLE_INFO_ENTRY));
378 CopyMem (InfoName, VariableInfo->Name, NameSize);
379 *InfoSize = StatisticsInfoSize;
380 return EFI_SUCCESS;
381 }
382
383 //
384 // Get the next variable info
385 //
386 while (VariableInfo != NULL) {
387 if (CompareGuid (&VariableInfo->VendorGuid, &VendorGuid)) {
388 NameSize = StrSize (VariableInfo->Name);
389 if (NameSize <= InfoNameMaxSize) {
390 if (CompareMem (VariableInfo->Name, InfoName, NameSize) == 0) {
391 //
392 // Find the match one
393 //
394 VariableInfo = VariableInfo->Next;
395 break;
396 }
397 }
398 }
399 VariableInfo = VariableInfo->Next;
400 };
401
402 if (VariableInfo == NULL) {
403 *InfoSize = 0;
404 return EFI_SUCCESS;
405 }
406
407 //
408 // Output the new variable info
409 //
410 NameSize = StrSize (VariableInfo->Name);
411 StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + NameSize;
412 if (*InfoSize < StatisticsInfoSize) {
413 *InfoSize = StatisticsInfoSize;
414 return EFI_BUFFER_TOO_SMALL;
415 }
416
417 CopyMem (InfoEntry, VariableInfo, sizeof (VARIABLE_INFO_ENTRY));
418 CopyMem (InfoName, VariableInfo->Name, NameSize);
419 *InfoSize = StatisticsInfoSize;
420
421 return EFI_SUCCESS;
422 }
423
424
425 /**
426 Communication service SMI Handler entry.
427
428 This SMI handler provides services for the variable wrapper driver.
429
430 Caution: This function may receive untrusted input.
431 This variable data and communicate buffer are external input, so this function will do basic validation.
432 Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(),
433 VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
434 SmmVariableGetStatistics() should also do validation based on its own knowledge.
435
436 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
437 @param[in] RegisterContext Points to an optional handler context which was specified when the
438 handler was registered.
439 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
440 be conveyed from a non-SMM environment into an SMM environment.
441 @param[in, out] CommBufferSize The size of the CommBuffer.
442
443 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
444 should still be called.
445 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
446 still be called.
447 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
448 be called.
449 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
450 **/
451 EFI_STATUS
452 EFIAPI
453 SmmVariableHandler (
454 IN EFI_HANDLE DispatchHandle,
455 IN CONST VOID *RegisterContext,
456 IN OUT VOID *CommBuffer,
457 IN OUT UINTN *CommBufferSize
458 )
459 {
460 EFI_STATUS Status;
461 SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader;
462 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader;
463 SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *GetNextVariableName;
464 SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *QueryVariableInfo;
465 SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *GetPayloadSize;
466 VARIABLE_INFO_ENTRY *VariableInfo;
467 SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *VariableToLock;
468 SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *CommVariableProperty;
469 UINTN InfoSize;
470 UINTN NameBufferSize;
471 UINTN CommBufferPayloadSize;
472 UINTN TempCommBufferSize;
473
474 //
475 // If input is invalid, stop processing this SMI
476 //
477 if (CommBuffer == NULL || CommBufferSize == NULL) {
478 return EFI_SUCCESS;
479 }
480
481 TempCommBufferSize = *CommBufferSize;
482
483 if (TempCommBufferSize < SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {
484 DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer size invalid!\n"));
485 return EFI_SUCCESS;
486 }
487 CommBufferPayloadSize = TempCommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
488 if (CommBufferPayloadSize > mVariableBufferPayloadSize) {
489 DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer payload size invalid!\n"));
490 return EFI_SUCCESS;
491 }
492
493 if (!VariableSmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {
494 DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer in SMRAM or overflow!\n"));
495 return EFI_SUCCESS;
496 }
497
498 SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *)CommBuffer;
499 switch (SmmVariableFunctionHeader->Function) {
500 case SMM_VARIABLE_FUNCTION_GET_VARIABLE:
501 if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) {
502 DEBUG ((EFI_D_ERROR, "GetVariable: SMM communication buffer size invalid!\n"));
503 return EFI_SUCCESS;
504 }
505 //
506 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
507 //
508 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
509 SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) mVariableBufferPayload;
510 if (((UINTN)(~0) - SmmVariableHeader->DataSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||
511 ((UINTN)(~0) - SmmVariableHeader->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) {
512 //
513 // Prevent InfoSize overflow happen
514 //
515 Status = EFI_ACCESS_DENIED;
516 goto EXIT;
517 }
518 InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)
519 + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize;
520
521 //
522 // SMRAM range check already covered before
523 //
524 if (InfoSize > CommBufferPayloadSize) {
525 DEBUG ((EFI_D_ERROR, "GetVariable: Data size exceed communication buffer size limit!\n"));
526 Status = EFI_ACCESS_DENIED;
527 goto EXIT;
528 }
529
530 //
531 // The VariableSpeculationBarrier() call here is to ensure the previous
532 // range/content checks for the CommBuffer have been completed before the
533 // subsequent consumption of the CommBuffer content.
534 //
535 VariableSpeculationBarrier ();
536 if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize/sizeof (CHAR16) - 1] != L'\0') {
537 //
538 // Make sure VariableName is A Null-terminated string.
539 //
540 Status = EFI_ACCESS_DENIED;
541 goto EXIT;
542 }
543
544 Status = VariableServiceGetVariable (
545 SmmVariableHeader->Name,
546 &SmmVariableHeader->Guid,
547 &SmmVariableHeader->Attributes,
548 &SmmVariableHeader->DataSize,
549 (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize
550 );
551 CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize);
552 break;
553
554 case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME:
555 if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {
556 DEBUG ((EFI_D_ERROR, "GetNextVariableName: SMM communication buffer size invalid!\n"));
557 return EFI_SUCCESS;
558 }
559 //
560 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
561 //
562 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
563 GetNextVariableName = (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *) mVariableBufferPayload;
564 if ((UINTN)(~0) - GetNextVariableName->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {
565 //
566 // Prevent InfoSize overflow happen
567 //
568 Status = EFI_ACCESS_DENIED;
569 goto EXIT;
570 }
571 InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + GetNextVariableName->NameSize;
572
573 //
574 // SMRAM range check already covered before
575 //
576 if (InfoSize > CommBufferPayloadSize) {
577 DEBUG ((EFI_D_ERROR, "GetNextVariableName: Data size exceed communication buffer size limit!\n"));
578 Status = EFI_ACCESS_DENIED;
579 goto EXIT;
580 }
581
582 NameBufferSize = CommBufferPayloadSize - OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name);
583 if (NameBufferSize < sizeof (CHAR16) || GetNextVariableName->Name[NameBufferSize/sizeof (CHAR16) - 1] != L'\0') {
584 //
585 // Make sure input VariableName is A Null-terminated string.
586 //
587 Status = EFI_ACCESS_DENIED;
588 goto EXIT;
589 }
590
591 Status = VariableServiceGetNextVariableName (
592 &GetNextVariableName->NameSize,
593 GetNextVariableName->Name,
594 &GetNextVariableName->Guid
595 );
596 CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize);
597 break;
598
599 case SMM_VARIABLE_FUNCTION_SET_VARIABLE:
600 if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) {
601 DEBUG ((EFI_D_ERROR, "SetVariable: SMM communication buffer size invalid!\n"));
602 return EFI_SUCCESS;
603 }
604 //
605 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
606 //
607 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
608 SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) mVariableBufferPayload;
609 if (((UINTN)(~0) - SmmVariableHeader->DataSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||
610 ((UINTN)(~0) - SmmVariableHeader->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) {
611 //
612 // Prevent InfoSize overflow happen
613 //
614 Status = EFI_ACCESS_DENIED;
615 goto EXIT;
616 }
617 InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)
618 + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize;
619
620 //
621 // SMRAM range check already covered before
622 // Data buffer should not contain SMM range
623 //
624 if (InfoSize > CommBufferPayloadSize) {
625 DEBUG ((EFI_D_ERROR, "SetVariable: Data size exceed communication buffer size limit!\n"));
626 Status = EFI_ACCESS_DENIED;
627 goto EXIT;
628 }
629
630 //
631 // The VariableSpeculationBarrier() call here is to ensure the previous
632 // range/content checks for the CommBuffer have been completed before the
633 // subsequent consumption of the CommBuffer content.
634 //
635 VariableSpeculationBarrier ();
636 if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize/sizeof (CHAR16) - 1] != L'\0') {
637 //
638 // Make sure VariableName is A Null-terminated string.
639 //
640 Status = EFI_ACCESS_DENIED;
641 goto EXIT;
642 }
643
644 Status = VariableServiceSetVariable (
645 SmmVariableHeader->Name,
646 &SmmVariableHeader->Guid,
647 SmmVariableHeader->Attributes,
648 SmmVariableHeader->DataSize,
649 (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize
650 );
651 break;
652
653 case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO:
654 if (CommBufferPayloadSize < sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO)) {
655 DEBUG ((EFI_D_ERROR, "QueryVariableInfo: SMM communication buffer size invalid!\n"));
656 return EFI_SUCCESS;
657 }
658 QueryVariableInfo = (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *) SmmVariableFunctionHeader->Data;
659
660 Status = VariableServiceQueryVariableInfo (
661 QueryVariableInfo->Attributes,
662 &QueryVariableInfo->MaximumVariableStorageSize,
663 &QueryVariableInfo->RemainingVariableStorageSize,
664 &QueryVariableInfo->MaximumVariableSize
665 );
666 break;
667
668 case SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE:
669 if (CommBufferPayloadSize < sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE)) {
670 DEBUG ((EFI_D_ERROR, "GetPayloadSize: SMM communication buffer size invalid!\n"));
671 return EFI_SUCCESS;
672 }
673 GetPayloadSize = (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *) SmmVariableFunctionHeader->Data;
674 GetPayloadSize->VariablePayloadSize = mVariableBufferPayloadSize;
675 Status = EFI_SUCCESS;
676 break;
677
678 case SMM_VARIABLE_FUNCTION_READY_TO_BOOT:
679 if (AtRuntime()) {
680 Status = EFI_UNSUPPORTED;
681 break;
682 }
683 if (!mEndOfDxe) {
684 MorLockInitAtEndOfDxe ();
685 mEndOfDxe = TRUE;
686 VarCheckLibInitializeAtEndOfDxe (NULL);
687 //
688 // The initialization for variable quota.
689 //
690 InitializeVariableQuota ();
691 }
692 ReclaimForOS ();
693 Status = EFI_SUCCESS;
694 break;
695
696 case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE:
697 mAtRuntime = TRUE;
698 Status = EFI_SUCCESS;
699 break;
700
701 case SMM_VARIABLE_FUNCTION_GET_STATISTICS:
702 VariableInfo = (VARIABLE_INFO_ENTRY *) SmmVariableFunctionHeader->Data;
703 InfoSize = TempCommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
704
705 //
706 // Do not need to check SmmVariableFunctionHeader->Data in SMRAM here.
707 // It is covered by previous CommBuffer check
708 //
709
710 //
711 // Do not need to check CommBufferSize buffer as it should point to SMRAM
712 // that was used by SMM core to cache CommSize from SmmCommunication protocol.
713 //
714
715 Status = SmmVariableGetStatistics (VariableInfo, &InfoSize);
716 *CommBufferSize = InfoSize + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
717 break;
718
719 case SMM_VARIABLE_FUNCTION_LOCK_VARIABLE:
720 if (mEndOfDxe) {
721 Status = EFI_ACCESS_DENIED;
722 } else {
723 VariableToLock = (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *) SmmVariableFunctionHeader->Data;
724 Status = VariableLockRequestToLock (
725 NULL,
726 VariableToLock->Name,
727 &VariableToLock->Guid
728 );
729 }
730 break;
731 case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET:
732 if (mEndOfDxe) {
733 Status = EFI_ACCESS_DENIED;
734 } else {
735 CommVariableProperty = (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *) SmmVariableFunctionHeader->Data;
736 Status = VarCheckVariablePropertySet (
737 CommVariableProperty->Name,
738 &CommVariableProperty->Guid,
739 &CommVariableProperty->VariableProperty
740 );
741 }
742 break;
743 case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET:
744 if (CommBufferPayloadSize < OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) {
745 DEBUG ((EFI_D_ERROR, "VarCheckVariablePropertyGet: SMM communication buffer size invalid!\n"));
746 return EFI_SUCCESS;
747 }
748 //
749 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
750 //
751 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
752 CommVariableProperty = (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *) mVariableBufferPayload;
753 if ((UINTN) (~0) - CommVariableProperty->NameSize < OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) {
754 //
755 // Prevent InfoSize overflow happen
756 //
757 Status = EFI_ACCESS_DENIED;
758 goto EXIT;
759 }
760 InfoSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) + CommVariableProperty->NameSize;
761
762 //
763 // SMRAM range check already covered before
764 //
765 if (InfoSize > CommBufferPayloadSize) {
766 DEBUG ((EFI_D_ERROR, "VarCheckVariablePropertyGet: Data size exceed communication buffer size limit!\n"));
767 Status = EFI_ACCESS_DENIED;
768 goto EXIT;
769 }
770
771 //
772 // The VariableSpeculationBarrier() call here is to ensure the previous
773 // range/content checks for the CommBuffer have been completed before the
774 // subsequent consumption of the CommBuffer content.
775 //
776 VariableSpeculationBarrier ();
777 if (CommVariableProperty->NameSize < sizeof (CHAR16) || CommVariableProperty->Name[CommVariableProperty->NameSize/sizeof (CHAR16) - 1] != L'\0') {
778 //
779 // Make sure VariableName is A Null-terminated string.
780 //
781 Status = EFI_ACCESS_DENIED;
782 goto EXIT;
783 }
784
785 Status = VarCheckVariablePropertyGet (
786 CommVariableProperty->Name,
787 &CommVariableProperty->Guid,
788 &CommVariableProperty->VariableProperty
789 );
790 CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize);
791 break;
792
793 default:
794 Status = EFI_UNSUPPORTED;
795 }
796
797 EXIT:
798
799 SmmVariableFunctionHeader->ReturnStatus = Status;
800
801 return EFI_SUCCESS;
802 }
803
804 /**
805 SMM END_OF_DXE protocol notification event handler.
806
807 @param Protocol Points to the protocol's unique identifier
808 @param Interface Points to the interface instance
809 @param Handle The handle on which the interface was installed
810
811 @retval EFI_SUCCESS SmmEndOfDxeCallback runs successfully
812
813 **/
814 EFI_STATUS
815 EFIAPI
816 SmmEndOfDxeCallback (
817 IN CONST EFI_GUID *Protocol,
818 IN VOID *Interface,
819 IN EFI_HANDLE Handle
820 )
821 {
822 DEBUG ((EFI_D_INFO, "[Variable]SMM_END_OF_DXE is signaled\n"));
823 MorLockInitAtEndOfDxe ();
824 mEndOfDxe = TRUE;
825 VarCheckLibInitializeAtEndOfDxe (NULL);
826 //
827 // The initialization for variable quota.
828 //
829 InitializeVariableQuota ();
830 if (PcdGetBool (PcdReclaimVariableSpaceAtEndOfDxe)) {
831 ReclaimForOS ();
832 }
833
834 return EFI_SUCCESS;
835 }
836
837 /**
838 Initializes variable write service for SMM.
839
840 **/
841 VOID
842 VariableWriteServiceInitializeSmm (
843 VOID
844 )
845 {
846 EFI_STATUS Status;
847
848 Status = VariableWriteServiceInitialize ();
849 if (EFI_ERROR (Status)) {
850 DEBUG ((DEBUG_ERROR, "Variable write service initialization failed. Status = %r\n", Status));
851 }
852
853 //
854 // Notify the variable wrapper driver the variable write service is ready
855 //
856 VariableNotifySmmWriteReady ();
857 }
858
859 /**
860 SMM Fault Tolerant Write protocol notification event handler.
861
862 Non-Volatile variable write may needs FTW protocol to reclaim when
863 writting variable.
864
865 @param Protocol Points to the protocol's unique identifier
866 @param Interface Points to the interface instance
867 @param Handle The handle on which the interface was installed
868
869 @retval EFI_SUCCESS SmmEventCallback runs successfully
870 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
871
872 **/
873 EFI_STATUS
874 EFIAPI
875 SmmFtwNotificationEvent (
876 IN CONST EFI_GUID *Protocol,
877 IN VOID *Interface,
878 IN EFI_HANDLE Handle
879 )
880 {
881 EFI_STATUS Status;
882 EFI_PHYSICAL_ADDRESS VariableStoreBase;
883 EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol;
884 EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol;
885 EFI_PHYSICAL_ADDRESS NvStorageVariableBase;
886 UINTN FtwMaxBlockSize;
887
888 if (mVariableModuleGlobal->FvbInstance != NULL) {
889 return EFI_SUCCESS;
890 }
891
892 //
893 // Ensure SMM FTW protocol is installed.
894 //
895 Status = GetFtwProtocol ((VOID **)&FtwProtocol);
896 if (EFI_ERROR (Status)) {
897 return Status;
898 }
899
900 Status = FtwProtocol->GetMaxBlockSize (FtwProtocol, &FtwMaxBlockSize);
901 if (!EFI_ERROR (Status)) {
902 ASSERT (PcdGet32 (PcdFlashNvStorageVariableSize) <= FtwMaxBlockSize);
903 }
904
905 NvStorageVariableBase = NV_STORAGE_VARIABLE_BASE;
906 VariableStoreBase = NvStorageVariableBase + mNvFvHeaderCache->HeaderLength;
907
908 //
909 // Let NonVolatileVariableBase point to flash variable store base directly after FTW ready.
910 //
911 mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase = VariableStoreBase;
912
913 //
914 // Find the proper FVB protocol for variable.
915 //
916 Status = GetFvbInfoByAddress (NvStorageVariableBase, NULL, &FvbProtocol);
917 if (EFI_ERROR (Status)) {
918 return EFI_NOT_FOUND;
919 }
920
921 mVariableModuleGlobal->FvbInstance = FvbProtocol;
922
923 //
924 // Initializes variable write service after FTW was ready.
925 //
926 VariableWriteServiceInitializeSmm ();
927
928 return EFI_SUCCESS;
929 }
930
931
932 /**
933 Variable Driver main entry point. The Variable driver places the 4 EFI
934 runtime services in the EFI System Table and installs arch protocols
935 for variable read and write services being available. It also registers
936 a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
937
938 @retval EFI_SUCCESS Variable service successfully initialized.
939
940 **/
941 EFI_STATUS
942 EFIAPI
943 MmVariableServiceInitialize (
944 VOID
945 )
946 {
947 EFI_STATUS Status;
948 EFI_HANDLE VariableHandle;
949 VOID *SmmFtwRegistration;
950 VOID *SmmEndOfDxeRegistration;
951
952 //
953 // Variable initialize.
954 //
955 Status = VariableCommonInitialize ();
956 ASSERT_EFI_ERROR (Status);
957
958 //
959 // Install the Smm Variable Protocol on a new handle.
960 //
961 VariableHandle = NULL;
962 Status = gMmst->MmInstallProtocolInterface (
963 &VariableHandle,
964 &gEfiSmmVariableProtocolGuid,
965 EFI_NATIVE_INTERFACE,
966 &gSmmVariable
967 );
968 ASSERT_EFI_ERROR (Status);
969
970 Status = gMmst->MmInstallProtocolInterface (
971 &VariableHandle,
972 &gEdkiiSmmVarCheckProtocolGuid,
973 EFI_NATIVE_INTERFACE,
974 &mSmmVarCheck
975 );
976 ASSERT_EFI_ERROR (Status);
977
978 mVariableBufferPayloadSize = GetMaxVariableSize () +
979 OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) - GetVariableHeaderSize ();
980
981 Status = gMmst->MmAllocatePool (
982 EfiRuntimeServicesData,
983 mVariableBufferPayloadSize,
984 (VOID **)&mVariableBufferPayload
985 );
986 ASSERT_EFI_ERROR (Status);
987
988 ///
989 /// Register SMM variable SMI handler
990 ///
991 VariableHandle = NULL;
992 Status = gMmst->MmiHandlerRegister (SmmVariableHandler, &gEfiSmmVariableProtocolGuid, &VariableHandle);
993 ASSERT_EFI_ERROR (Status);
994
995 //
996 // Notify the variable wrapper driver the variable service is ready
997 //
998 VariableNotifySmmReady ();
999
1000 //
1001 // Register EFI_SMM_END_OF_DXE_PROTOCOL_GUID notify function.
1002 //
1003 Status = gMmst->MmRegisterProtocolNotify (
1004 &gEfiMmEndOfDxeProtocolGuid,
1005 SmmEndOfDxeCallback,
1006 &SmmEndOfDxeRegistration
1007 );
1008 ASSERT_EFI_ERROR (Status);
1009
1010 if (!PcdGetBool (PcdEmuVariableNvModeEnable)) {
1011 //
1012 // Register FtwNotificationEvent () notify function.
1013 //
1014 Status = gMmst->MmRegisterProtocolNotify (
1015 &gEfiSmmFaultTolerantWriteProtocolGuid,
1016 SmmFtwNotificationEvent,
1017 &SmmFtwRegistration
1018 );
1019 ASSERT_EFI_ERROR (Status);
1020
1021 SmmFtwNotificationEvent (NULL, NULL, NULL);
1022 } else {
1023 //
1024 // Emulated non-volatile variable mode does not depend on FVB and FTW.
1025 //
1026 VariableWriteServiceInitializeSmm ();
1027 }
1028
1029 return EFI_SUCCESS;
1030 }
1031
1032