]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / RuntimeDxe / VariableSmmRuntimeDxe.c
1 /** @file
2 Implement all four UEFI Runtime Variable services for the nonvolatile
3 and volatile storage space and install variable architecture protocol
4 based on SMM variable module.
5
6 Caution: This module requires additional review when modified.
7 This driver will have external input - variable data.
8 This external input must be validated carefully to avoid security issue like
9 buffer overflow, integer overflow.
10
11 RuntimeServiceGetVariable() and RuntimeServiceSetVariable() are external API
12 to receive data buffer. The size should be checked carefully.
13
14 InitCommunicateBuffer() is really function to check the variable data size.
15
16 Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.<BR>
17 SPDX-License-Identifier: BSD-2-Clause-Patent
18
19 **/
20 #include <PiDxe.h>
21 #include <Protocol/VariableWrite.h>
22 #include <Protocol/Variable.h>
23 #include <Protocol/SmmCommunication.h>
24 #include <Protocol/SmmVariable.h>
25 #include <Protocol/VariableLock.h>
26 #include <Protocol/VarCheck.h>
27
28 #include <Library/UefiBootServicesTableLib.h>
29 #include <Library/UefiRuntimeServicesTableLib.h>
30 #include <Library/MemoryAllocationLib.h>
31 #include <Library/UefiDriverEntryPoint.h>
32 #include <Library/UefiRuntimeLib.h>
33 #include <Library/BaseMemoryLib.h>
34 #include <Library/DebugLib.h>
35 #include <Library/UefiLib.h>
36 #include <Library/BaseLib.h>
37
38 #include <Guid/EventGroup.h>
39 #include <Guid/SmmVariableCommon.h>
40
41 #include "PrivilegePolymorphic.h"
42
43 EFI_HANDLE mHandle = NULL;
44 EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable = NULL;
45 EFI_EVENT mVirtualAddressChangeEvent = NULL;
46 EFI_SMM_COMMUNICATION_PROTOCOL *mSmmCommunication = NULL;
47 UINT8 *mVariableBuffer = NULL;
48 UINT8 *mVariableBufferPhysical = NULL;
49 UINTN mVariableBufferSize;
50 UINTN mVariableBufferPayloadSize;
51 EFI_LOCK mVariableServicesLock;
52 EDKII_VARIABLE_LOCK_PROTOCOL mVariableLock;
53 EDKII_VAR_CHECK_PROTOCOL mVarCheck;
54
55 /**
56 Some Secure Boot Policy Variable may update following other variable changes(SecureBoot follows PK change, etc).
57 Record their initial State when variable write service is ready.
58
59 **/
60 VOID
61 EFIAPI
62 RecordSecureBootPolicyVarData(
63 VOID
64 );
65
66 /**
67 Acquires lock only at boot time. Simply returns at runtime.
68
69 This is a temperary function that will be removed when
70 EfiAcquireLock() in UefiLib can handle the call in UEFI
71 Runtimer driver in RT phase.
72 It calls EfiAcquireLock() at boot time, and simply returns
73 at runtime.
74
75 @param Lock A pointer to the lock to acquire.
76
77 **/
78 VOID
79 AcquireLockOnlyAtBootTime (
80 IN EFI_LOCK *Lock
81 )
82 {
83 if (!EfiAtRuntime ()) {
84 EfiAcquireLock (Lock);
85 }
86 }
87
88 /**
89 Releases lock only at boot time. Simply returns at runtime.
90
91 This is a temperary function which will be removed when
92 EfiReleaseLock() in UefiLib can handle the call in UEFI
93 Runtimer driver in RT phase.
94 It calls EfiReleaseLock() at boot time and simply returns
95 at runtime.
96
97 @param Lock A pointer to the lock to release.
98
99 **/
100 VOID
101 ReleaseLockOnlyAtBootTime (
102 IN EFI_LOCK *Lock
103 )
104 {
105 if (!EfiAtRuntime ()) {
106 EfiReleaseLock (Lock);
107 }
108 }
109
110 /**
111 Initialize the communicate buffer using DataSize and Function.
112
113 The communicate size is: SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE +
114 DataSize.
115
116 Caution: This function may receive untrusted input.
117 The data size external input, so this function will validate it carefully to avoid buffer overflow.
118
119 @param[out] DataPtr Points to the data in the communicate buffer.
120 @param[in] DataSize The data size to send to SMM.
121 @param[in] Function The function number to initialize the communicate header.
122
123 @retval EFI_INVALID_PARAMETER The data size is too big.
124 @retval EFI_SUCCESS Find the specified variable.
125
126 **/
127 EFI_STATUS
128 InitCommunicateBuffer (
129 OUT VOID **DataPtr OPTIONAL,
130 IN UINTN DataSize,
131 IN UINTN Function
132 )
133 {
134 EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
135 SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader;
136
137
138 if (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE > mVariableBufferSize) {
139 return EFI_INVALID_PARAMETER;
140 }
141
142 SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) mVariableBuffer;
143 CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmVariableProtocolGuid);
144 SmmCommunicateHeader->MessageLength = DataSize + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
145
146 SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data;
147 SmmVariableFunctionHeader->Function = Function;
148 if (DataPtr != NULL) {
149 *DataPtr = SmmVariableFunctionHeader->Data;
150 }
151
152 return EFI_SUCCESS;
153 }
154
155
156 /**
157 Send the data in communicate buffer to SMM.
158
159 @param[in] DataSize This size of the function header and the data.
160
161 @retval EFI_SUCCESS Success is returned from the functin in SMM.
162 @retval Others Failure is returned from the function in SMM.
163
164 **/
165 EFI_STATUS
166 SendCommunicateBuffer (
167 IN UINTN DataSize
168 )
169 {
170 EFI_STATUS Status;
171 UINTN CommSize;
172 EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
173 SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader;
174
175 CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
176 Status = mSmmCommunication->Communicate (mSmmCommunication, mVariableBufferPhysical, &CommSize);
177 ASSERT_EFI_ERROR (Status);
178
179 SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) mVariableBuffer;
180 SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *)SmmCommunicateHeader->Data;
181 return SmmVariableFunctionHeader->ReturnStatus;
182 }
183
184 /**
185 Mark a variable that will become read-only after leaving the DXE phase of execution.
186
187 @param[in] This The VARIABLE_LOCK_PROTOCOL instance.
188 @param[in] VariableName A pointer to the variable name that will be made read-only subsequently.
189 @param[in] VendorGuid A pointer to the vendor GUID that will be made read-only subsequently.
190
191 @retval EFI_SUCCESS The variable specified by the VariableName and the VendorGuid was marked
192 as pending to be read-only.
193 @retval EFI_INVALID_PARAMETER VariableName or VendorGuid is NULL.
194 Or VariableName is an empty string.
195 @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has
196 already been signaled.
197 @retval EFI_OUT_OF_RESOURCES There is not enough resource to hold the lock request.
198 **/
199 EFI_STATUS
200 EFIAPI
201 VariableLockRequestToLock (
202 IN CONST EDKII_VARIABLE_LOCK_PROTOCOL *This,
203 IN CHAR16 *VariableName,
204 IN EFI_GUID *VendorGuid
205 )
206 {
207 EFI_STATUS Status;
208 UINTN VariableNameSize;
209 UINTN PayloadSize;
210 SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *VariableToLock;
211
212 if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) {
213 return EFI_INVALID_PARAMETER;
214 }
215
216 VariableNameSize = StrSize (VariableName);
217 VariableToLock = NULL;
218
219 //
220 // If VariableName exceeds SMM payload limit. Return failure
221 //
222 if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE, Name)) {
223 return EFI_INVALID_PARAMETER;
224 }
225
226 AcquireLockOnlyAtBootTime(&mVariableServicesLock);
227
228 //
229 // Init the communicate buffer. The buffer data size is:
230 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
231 //
232 PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE, Name) + VariableNameSize;
233 Status = InitCommunicateBuffer ((VOID **) &VariableToLock, PayloadSize, SMM_VARIABLE_FUNCTION_LOCK_VARIABLE);
234 if (EFI_ERROR (Status)) {
235 goto Done;
236 }
237 ASSERT (VariableToLock != NULL);
238
239 CopyGuid (&VariableToLock->Guid, VendorGuid);
240 VariableToLock->NameSize = VariableNameSize;
241 CopyMem (VariableToLock->Name, VariableName, VariableToLock->NameSize);
242
243 //
244 // Send data to SMM.
245 //
246 Status = SendCommunicateBuffer (PayloadSize);
247
248 Done:
249 ReleaseLockOnlyAtBootTime (&mVariableServicesLock);
250 return Status;
251 }
252
253 /**
254 Register SetVariable check handler.
255
256 @param[in] Handler Pointer to check handler.
257
258 @retval EFI_SUCCESS The SetVariable check handler was registered successfully.
259 @retval EFI_INVALID_PARAMETER Handler is NULL.
260 @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has
261 already been signaled.
262 @retval EFI_OUT_OF_RESOURCES There is not enough resource for the SetVariable check handler register request.
263 @retval EFI_UNSUPPORTED This interface is not implemented.
264 For example, it is unsupported in VarCheck protocol if both VarCheck and SmmVarCheck protocols are present.
265
266 **/
267 EFI_STATUS
268 EFIAPI
269 VarCheckRegisterSetVariableCheckHandler (
270 IN VAR_CHECK_SET_VARIABLE_CHECK_HANDLER Handler
271 )
272 {
273 return EFI_UNSUPPORTED;
274 }
275
276 /**
277 Variable property set.
278
279 @param[in] Name Pointer to the variable name.
280 @param[in] Guid Pointer to the vendor GUID.
281 @param[in] VariableProperty Pointer to the input variable property.
282
283 @retval EFI_SUCCESS The property of variable specified by the Name and Guid was set successfully.
284 @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string,
285 or the fields of VariableProperty are not valid.
286 @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has
287 already been signaled.
288 @retval EFI_OUT_OF_RESOURCES There is not enough resource for the variable property set request.
289
290 **/
291 EFI_STATUS
292 EFIAPI
293 VarCheckVariablePropertySet (
294 IN CHAR16 *Name,
295 IN EFI_GUID *Guid,
296 IN VAR_CHECK_VARIABLE_PROPERTY *VariableProperty
297 )
298 {
299 EFI_STATUS Status;
300 UINTN VariableNameSize;
301 UINTN PayloadSize;
302 SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *CommVariableProperty;
303
304 if (Name == NULL || Name[0] == 0 || Guid == NULL) {
305 return EFI_INVALID_PARAMETER;
306 }
307
308 if (VariableProperty == NULL) {
309 return EFI_INVALID_PARAMETER;
310 }
311
312 if (VariableProperty->Revision != VAR_CHECK_VARIABLE_PROPERTY_REVISION) {
313 return EFI_INVALID_PARAMETER;
314 }
315
316 VariableNameSize = StrSize (Name);
317 CommVariableProperty = NULL;
318
319 //
320 // If VariableName exceeds SMM payload limit. Return failure
321 //
322 if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) {
323 return EFI_INVALID_PARAMETER;
324 }
325
326 AcquireLockOnlyAtBootTime (&mVariableServicesLock);
327
328 //
329 // Init the communicate buffer. The buffer data size is:
330 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
331 //
332 PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) + VariableNameSize;
333 Status = InitCommunicateBuffer ((VOID **) &CommVariableProperty, PayloadSize, SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET);
334 if (EFI_ERROR (Status)) {
335 goto Done;
336 }
337 ASSERT (CommVariableProperty != NULL);
338
339 CopyGuid (&CommVariableProperty->Guid, Guid);
340 CopyMem (&CommVariableProperty->VariableProperty, VariableProperty, sizeof (*VariableProperty));
341 CommVariableProperty->NameSize = VariableNameSize;
342 CopyMem (CommVariableProperty->Name, Name, CommVariableProperty->NameSize);
343
344 //
345 // Send data to SMM.
346 //
347 Status = SendCommunicateBuffer (PayloadSize);
348
349 Done:
350 ReleaseLockOnlyAtBootTime (&mVariableServicesLock);
351 return Status;
352 }
353
354 /**
355 Variable property get.
356
357 @param[in] Name Pointer to the variable name.
358 @param[in] Guid Pointer to the vendor GUID.
359 @param[out] VariableProperty Pointer to the output variable property.
360
361 @retval EFI_SUCCESS The property of variable specified by the Name and Guid was got successfully.
362 @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string.
363 @retval EFI_NOT_FOUND The property of variable specified by the Name and Guid was not found.
364
365 **/
366 EFI_STATUS
367 EFIAPI
368 VarCheckVariablePropertyGet (
369 IN CHAR16 *Name,
370 IN EFI_GUID *Guid,
371 OUT VAR_CHECK_VARIABLE_PROPERTY *VariableProperty
372 )
373 {
374 EFI_STATUS Status;
375 UINTN VariableNameSize;
376 UINTN PayloadSize;
377 SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *CommVariableProperty;
378
379 if (Name == NULL || Name[0] == 0 || Guid == NULL) {
380 return EFI_INVALID_PARAMETER;
381 }
382
383 if (VariableProperty == NULL) {
384 return EFI_INVALID_PARAMETER;
385 }
386
387 VariableNameSize = StrSize (Name);
388 CommVariableProperty = NULL;
389
390 //
391 // If VariableName exceeds SMM payload limit. Return failure
392 //
393 if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) {
394 return EFI_INVALID_PARAMETER;
395 }
396
397 AcquireLockOnlyAtBootTime (&mVariableServicesLock);
398
399 //
400 // Init the communicate buffer. The buffer data size is:
401 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
402 //
403 PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) + VariableNameSize;
404 Status = InitCommunicateBuffer ((VOID **) &CommVariableProperty, PayloadSize, SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET);
405 if (EFI_ERROR (Status)) {
406 goto Done;
407 }
408 ASSERT (CommVariableProperty != NULL);
409
410 CopyGuid (&CommVariableProperty->Guid, Guid);
411 CommVariableProperty->NameSize = VariableNameSize;
412 CopyMem (CommVariableProperty->Name, Name, CommVariableProperty->NameSize);
413
414 //
415 // Send data to SMM.
416 //
417 Status = SendCommunicateBuffer (PayloadSize);
418 if (Status == EFI_SUCCESS) {
419 CopyMem (VariableProperty, &CommVariableProperty->VariableProperty, sizeof (*VariableProperty));
420 }
421
422 Done:
423 ReleaseLockOnlyAtBootTime (&mVariableServicesLock);
424 return Status;
425 }
426
427 /**
428 This code finds variable in storage blocks (Volatile or Non-Volatile).
429
430 Caution: This function may receive untrusted input.
431 The data size is external input, so this function will validate it carefully to avoid buffer overflow.
432
433 @param[in] VariableName Name of Variable to be found.
434 @param[in] VendorGuid Variable vendor GUID.
435 @param[out] Attributes Attribute value of the variable found.
436 @param[in, out] DataSize Size of Data found. If size is less than the
437 data, this value contains the required size.
438 @param[out] Data Data pointer.
439
440 @retval EFI_INVALID_PARAMETER Invalid parameter.
441 @retval EFI_SUCCESS Find the specified variable.
442 @retval EFI_NOT_FOUND Not found.
443 @retval EFI_BUFFER_TO_SMALL DataSize is too small for the result.
444
445 **/
446 EFI_STATUS
447 EFIAPI
448 RuntimeServiceGetVariable (
449 IN CHAR16 *VariableName,
450 IN EFI_GUID *VendorGuid,
451 OUT UINT32 *Attributes OPTIONAL,
452 IN OUT UINTN *DataSize,
453 OUT VOID *Data
454 )
455 {
456 EFI_STATUS Status;
457 UINTN PayloadSize;
458 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader;
459 UINTN TempDataSize;
460 UINTN VariableNameSize;
461
462 if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) {
463 return EFI_INVALID_PARAMETER;
464 }
465
466 TempDataSize = *DataSize;
467 VariableNameSize = StrSize (VariableName);
468 SmmVariableHeader = NULL;
469
470 //
471 // If VariableName exceeds SMM payload limit. Return failure
472 //
473 if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) {
474 return EFI_INVALID_PARAMETER;
475 }
476
477 AcquireLockOnlyAtBootTime(&mVariableServicesLock);
478
479 //
480 // Init the communicate buffer. The buffer data size is:
481 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
482 //
483 if (TempDataSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize) {
484 //
485 // If output data buffer exceed SMM payload limit. Trim output buffer to SMM payload size
486 //
487 TempDataSize = mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize;
488 }
489 PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + VariableNameSize + TempDataSize;
490
491 Status = InitCommunicateBuffer ((VOID **)&SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_GET_VARIABLE);
492 if (EFI_ERROR (Status)) {
493 goto Done;
494 }
495 ASSERT (SmmVariableHeader != NULL);
496
497 CopyGuid (&SmmVariableHeader->Guid, VendorGuid);
498 SmmVariableHeader->DataSize = TempDataSize;
499 SmmVariableHeader->NameSize = VariableNameSize;
500 if (Attributes == NULL) {
501 SmmVariableHeader->Attributes = 0;
502 } else {
503 SmmVariableHeader->Attributes = *Attributes;
504 }
505 CopyMem (SmmVariableHeader->Name, VariableName, SmmVariableHeader->NameSize);
506
507 //
508 // Send data to SMM.
509 //
510 Status = SendCommunicateBuffer (PayloadSize);
511
512 //
513 // Get data from SMM.
514 //
515 if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) {
516 //
517 // SMM CommBuffer DataSize can be a trimed value
518 // Only update DataSize when needed
519 //
520 *DataSize = SmmVariableHeader->DataSize;
521 }
522 if (Attributes != NULL) {
523 *Attributes = SmmVariableHeader->Attributes;
524 }
525
526 if (EFI_ERROR (Status)) {
527 goto Done;
528 }
529
530 if (Data != NULL) {
531 CopyMem (Data, (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize, SmmVariableHeader->DataSize);
532 } else {
533 Status = EFI_INVALID_PARAMETER;
534 }
535
536 Done:
537 ReleaseLockOnlyAtBootTime (&mVariableServicesLock);
538 return Status;
539 }
540
541
542 /**
543 This code Finds the Next available variable.
544
545 @param[in, out] VariableNameSize Size of the variable name.
546 @param[in, out] VariableName Pointer to variable name.
547 @param[in, out] VendorGuid Variable Vendor Guid.
548
549 @retval EFI_INVALID_PARAMETER Invalid parameter.
550 @retval EFI_SUCCESS Find the specified variable.
551 @retval EFI_NOT_FOUND Not found.
552 @retval EFI_BUFFER_TO_SMALL DataSize is too small for the result.
553
554 **/
555 EFI_STATUS
556 EFIAPI
557 RuntimeServiceGetNextVariableName (
558 IN OUT UINTN *VariableNameSize,
559 IN OUT CHAR16 *VariableName,
560 IN OUT EFI_GUID *VendorGuid
561 )
562 {
563 EFI_STATUS Status;
564 UINTN PayloadSize;
565 SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *SmmGetNextVariableName;
566 UINTN OutVariableNameSize;
567 UINTN InVariableNameSize;
568
569 if (VariableNameSize == NULL || VariableName == NULL || VendorGuid == NULL) {
570 return EFI_INVALID_PARAMETER;
571 }
572
573 OutVariableNameSize = *VariableNameSize;
574 InVariableNameSize = StrSize (VariableName);
575 SmmGetNextVariableName = NULL;
576
577 //
578 // If input string exceeds SMM payload limit. Return failure
579 //
580 if (InVariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {
581 return EFI_INVALID_PARAMETER;
582 }
583
584 AcquireLockOnlyAtBootTime(&mVariableServicesLock);
585
586 //
587 // Init the communicate buffer. The buffer data size is:
588 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
589 //
590 if (OutVariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {
591 //
592 // If output buffer exceed SMM payload limit. Trim output buffer to SMM payload size
593 //
594 OutVariableNameSize = mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name);
595 }
596 //
597 // Payload should be Guid + NameSize + MAX of Input & Output buffer
598 //
599 PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + MAX (OutVariableNameSize, InVariableNameSize);
600
601 Status = InitCommunicateBuffer ((VOID **)&SmmGetNextVariableName, PayloadSize, SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME);
602 if (EFI_ERROR (Status)) {
603 goto Done;
604 }
605 ASSERT (SmmGetNextVariableName != NULL);
606
607 //
608 // SMM comm buffer->NameSize is buffer size for return string
609 //
610 SmmGetNextVariableName->NameSize = OutVariableNameSize;
611
612 CopyGuid (&SmmGetNextVariableName->Guid, VendorGuid);
613 //
614 // Copy whole string
615 //
616 CopyMem (SmmGetNextVariableName->Name, VariableName, InVariableNameSize);
617 if (OutVariableNameSize > InVariableNameSize) {
618 ZeroMem ((UINT8 *) SmmGetNextVariableName->Name + InVariableNameSize, OutVariableNameSize - InVariableNameSize);
619 }
620
621 //
622 // Send data to SMM
623 //
624 Status = SendCommunicateBuffer (PayloadSize);
625
626 //
627 // Get data from SMM.
628 //
629 if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) {
630 //
631 // SMM CommBuffer NameSize can be a trimed value
632 // Only update VariableNameSize when needed
633 //
634 *VariableNameSize = SmmGetNextVariableName->NameSize;
635 }
636 if (EFI_ERROR (Status)) {
637 goto Done;
638 }
639
640 CopyGuid (VendorGuid, &SmmGetNextVariableName->Guid);
641 CopyMem (VariableName, SmmGetNextVariableName->Name, SmmGetNextVariableName->NameSize);
642
643 Done:
644 ReleaseLockOnlyAtBootTime (&mVariableServicesLock);
645 return Status;
646 }
647
648 /**
649 This code sets variable in storage blocks (Volatile or Non-Volatile).
650
651 Caution: This function may receive untrusted input.
652 The data size and data are external input, so this function will validate it carefully to avoid buffer overflow.
653
654 @param[in] VariableName Name of Variable to be found.
655 @param[in] VendorGuid Variable vendor GUID.
656 @param[in] Attributes Attribute value of the variable found
657 @param[in] DataSize Size of Data found. If size is less than the
658 data, this value contains the required size.
659 @param[in] Data Data pointer.
660
661 @retval EFI_INVALID_PARAMETER Invalid parameter.
662 @retval EFI_SUCCESS Set successfully.
663 @retval EFI_OUT_OF_RESOURCES Resource not enough to set variable.
664 @retval EFI_NOT_FOUND Not found.
665 @retval EFI_WRITE_PROTECTED Variable is read-only.
666
667 **/
668 EFI_STATUS
669 EFIAPI
670 RuntimeServiceSetVariable (
671 IN CHAR16 *VariableName,
672 IN EFI_GUID *VendorGuid,
673 IN UINT32 Attributes,
674 IN UINTN DataSize,
675 IN VOID *Data
676 )
677 {
678 EFI_STATUS Status;
679 UINTN PayloadSize;
680 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader;
681 UINTN VariableNameSize;
682
683 //
684 // Check input parameters.
685 //
686 if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) {
687 return EFI_INVALID_PARAMETER;
688 }
689
690 if (DataSize != 0 && Data == NULL) {
691 return EFI_INVALID_PARAMETER;
692 }
693
694 VariableNameSize = StrSize (VariableName);
695 SmmVariableHeader = NULL;
696
697 //
698 // If VariableName or DataSize exceeds SMM payload limit. Return failure
699 //
700 if ((VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||
701 (DataSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize)){
702 return EFI_INVALID_PARAMETER;
703 }
704
705 AcquireLockOnlyAtBootTime(&mVariableServicesLock);
706
707 //
708 // Init the communicate buffer. The buffer data size is:
709 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
710 //
711 PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + VariableNameSize + DataSize;
712 Status = InitCommunicateBuffer ((VOID **)&SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_SET_VARIABLE);
713 if (EFI_ERROR (Status)) {
714 goto Done;
715 }
716 ASSERT (SmmVariableHeader != NULL);
717
718 CopyGuid ((EFI_GUID *) &SmmVariableHeader->Guid, VendorGuid);
719 SmmVariableHeader->DataSize = DataSize;
720 SmmVariableHeader->NameSize = VariableNameSize;
721 SmmVariableHeader->Attributes = Attributes;
722 CopyMem (SmmVariableHeader->Name, VariableName, SmmVariableHeader->NameSize);
723 CopyMem ((UINT8 *) SmmVariableHeader->Name + SmmVariableHeader->NameSize, Data, DataSize);
724
725 //
726 // Send data to SMM.
727 //
728 Status = SendCommunicateBuffer (PayloadSize);
729
730 Done:
731 ReleaseLockOnlyAtBootTime (&mVariableServicesLock);
732
733 if (!EfiAtRuntime ()) {
734 if (!EFI_ERROR (Status)) {
735 SecureBootHook (
736 VariableName,
737 VendorGuid
738 );
739 }
740 }
741 return Status;
742 }
743
744
745 /**
746 This code returns information about the EFI variables.
747
748 @param[in] Attributes Attributes bitmask to specify the type of variables
749 on which to return information.
750 @param[out] MaximumVariableStorageSize Pointer to the maximum size of the storage space available
751 for the EFI variables associated with the attributes specified.
752 @param[out] RemainingVariableStorageSize Pointer to the remaining size of the storage space available
753 for EFI variables associated with the attributes specified.
754 @param[out] MaximumVariableSize Pointer to the maximum size of an individual EFI variables
755 associated with the attributes specified.
756
757 @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied.
758 @retval EFI_SUCCESS Query successfully.
759 @retval EFI_UNSUPPORTED The attribute is not supported on this platform.
760
761 **/
762 EFI_STATUS
763 EFIAPI
764 RuntimeServiceQueryVariableInfo (
765 IN UINT32 Attributes,
766 OUT UINT64 *MaximumVariableStorageSize,
767 OUT UINT64 *RemainingVariableStorageSize,
768 OUT UINT64 *MaximumVariableSize
769 )
770 {
771 EFI_STATUS Status;
772 UINTN PayloadSize;
773 SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *SmmQueryVariableInfo;
774
775 SmmQueryVariableInfo = NULL;
776
777 if(MaximumVariableStorageSize == NULL || RemainingVariableStorageSize == NULL || MaximumVariableSize == NULL || Attributes == 0) {
778 return EFI_INVALID_PARAMETER;
779 }
780
781 AcquireLockOnlyAtBootTime(&mVariableServicesLock);
782
783 //
784 // Init the communicate buffer. The buffer data size is:
785 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize;
786 //
787 PayloadSize = sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO);
788 Status = InitCommunicateBuffer ((VOID **)&SmmQueryVariableInfo, PayloadSize, SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO);
789 if (EFI_ERROR (Status)) {
790 goto Done;
791 }
792 ASSERT (SmmQueryVariableInfo != NULL);
793
794 SmmQueryVariableInfo->Attributes = Attributes;
795
796 //
797 // Send data to SMM.
798 //
799 Status = SendCommunicateBuffer (PayloadSize);
800 if (EFI_ERROR (Status)) {
801 goto Done;
802 }
803
804 //
805 // Get data from SMM.
806 //
807 *MaximumVariableSize = SmmQueryVariableInfo->MaximumVariableSize;
808 *MaximumVariableStorageSize = SmmQueryVariableInfo->MaximumVariableStorageSize;
809 *RemainingVariableStorageSize = SmmQueryVariableInfo->RemainingVariableStorageSize;
810
811 Done:
812 ReleaseLockOnlyAtBootTime (&mVariableServicesLock);
813 return Status;
814 }
815
816
817 /**
818 Exit Boot Services Event notification handler.
819
820 Notify SMM variable driver about the event.
821
822 @param[in] Event Event whose notification function is being invoked.
823 @param[in] Context Pointer to the notification function's context.
824
825 **/
826 VOID
827 EFIAPI
828 OnExitBootServices (
829 IN EFI_EVENT Event,
830 IN VOID *Context
831 )
832 {
833 //
834 // Init the communicate buffer. The buffer data size is:
835 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE.
836 //
837 InitCommunicateBuffer (NULL, 0, SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE);
838
839 //
840 // Send data to SMM.
841 //
842 SendCommunicateBuffer (0);
843 }
844
845
846 /**
847 On Ready To Boot Services Event notification handler.
848
849 Notify SMM variable driver about the event.
850
851 @param[in] Event Event whose notification function is being invoked
852 @param[in] Context Pointer to the notification function's context
853
854 **/
855 VOID
856 EFIAPI
857 OnReadyToBoot (
858 IN EFI_EVENT Event,
859 IN VOID *Context
860 )
861 {
862 //
863 // Init the communicate buffer. The buffer data size is:
864 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE.
865 //
866 InitCommunicateBuffer (NULL, 0, SMM_VARIABLE_FUNCTION_READY_TO_BOOT);
867
868 //
869 // Send data to SMM.
870 //
871 SendCommunicateBuffer (0);
872
873 gBS->CloseEvent (Event);
874 }
875
876
877 /**
878 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
879
880 This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
881 It convers pointer to new virtual address.
882
883 @param[in] Event Event whose notification function is being invoked.
884 @param[in] Context Pointer to the notification function's context.
885
886 **/
887 VOID
888 EFIAPI
889 VariableAddressChangeEvent (
890 IN EFI_EVENT Event,
891 IN VOID *Context
892 )
893 {
894 EfiConvertPointer (0x0, (VOID **) &mVariableBuffer);
895 EfiConvertPointer (0x0, (VOID **) &mSmmCommunication);
896 }
897
898 /**
899 This code gets variable payload size.
900
901 @param[out] VariablePayloadSize Output pointer to variable payload size.
902
903 @retval EFI_SUCCESS Get successfully.
904 @retval Others Get unsuccessfully.
905
906 **/
907 EFI_STATUS
908 EFIAPI
909 GetVariablePayloadSize (
910 OUT UINTN *VariablePayloadSize
911 )
912 {
913 EFI_STATUS Status;
914 SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *SmmGetPayloadSize;
915 EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader;
916 SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader;
917 UINTN CommSize;
918 UINT8 *CommBuffer;
919
920 SmmGetPayloadSize = NULL;
921 CommBuffer = NULL;
922
923 if(VariablePayloadSize == NULL) {
924 return EFI_INVALID_PARAMETER;
925 }
926
927 AcquireLockOnlyAtBootTime(&mVariableServicesLock);
928
929 //
930 // Init the communicate buffer. The buffer data size is:
931 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE);
932 //
933 CommSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE);
934 CommBuffer = AllocateZeroPool (CommSize);
935 if (CommBuffer == NULL) {
936 Status = EFI_OUT_OF_RESOURCES;
937 goto Done;
938 }
939
940 SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) CommBuffer;
941 CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmVariableProtocolGuid);
942 SmmCommunicateHeader->MessageLength = SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE);
943
944 SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data;
945 SmmVariableFunctionHeader->Function = SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE;
946 SmmGetPayloadSize = (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *) SmmVariableFunctionHeader->Data;
947
948 //
949 // Send data to SMM.
950 //
951 Status = mSmmCommunication->Communicate (mSmmCommunication, CommBuffer, &CommSize);
952 ASSERT_EFI_ERROR (Status);
953
954 Status = SmmVariableFunctionHeader->ReturnStatus;
955 if (EFI_ERROR (Status)) {
956 goto Done;
957 }
958
959 //
960 // Get data from SMM.
961 //
962 *VariablePayloadSize = SmmGetPayloadSize->VariablePayloadSize;
963
964 Done:
965 if (CommBuffer != NULL) {
966 FreePool (CommBuffer);
967 }
968 ReleaseLockOnlyAtBootTime (&mVariableServicesLock);
969 return Status;
970 }
971
972 /**
973 Initialize variable service and install Variable Architectural protocol.
974
975 @param[in] Event Event whose notification function is being invoked.
976 @param[in] Context Pointer to the notification function's context.
977
978 **/
979 VOID
980 EFIAPI
981 SmmVariableReady (
982 IN EFI_EVENT Event,
983 IN VOID *Context
984 )
985 {
986 EFI_STATUS Status;
987
988 Status = gBS->LocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID **)&mSmmVariable);
989 if (EFI_ERROR (Status)) {
990 return;
991 }
992
993 Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &mSmmCommunication);
994 ASSERT_EFI_ERROR (Status);
995
996 //
997 // Allocate memory for variable communicate buffer.
998 //
999 Status = GetVariablePayloadSize (&mVariableBufferPayloadSize);
1000 ASSERT_EFI_ERROR (Status);
1001 mVariableBufferSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + mVariableBufferPayloadSize;
1002 mVariableBuffer = AllocateRuntimePool (mVariableBufferSize);
1003 ASSERT (mVariableBuffer != NULL);
1004
1005 //
1006 // Save the buffer physical address used for SMM conmunication.
1007 //
1008 mVariableBufferPhysical = mVariableBuffer;
1009
1010 gRT->GetVariable = RuntimeServiceGetVariable;
1011 gRT->GetNextVariableName = RuntimeServiceGetNextVariableName;
1012 gRT->SetVariable = RuntimeServiceSetVariable;
1013 gRT->QueryVariableInfo = RuntimeServiceQueryVariableInfo;
1014
1015 //
1016 // Install the Variable Architectural Protocol on a new handle.
1017 //
1018 Status = gBS->InstallProtocolInterface (
1019 &mHandle,
1020 &gEfiVariableArchProtocolGuid,
1021 EFI_NATIVE_INTERFACE,
1022 NULL
1023 );
1024 ASSERT_EFI_ERROR (Status);
1025
1026 mVariableLock.RequestToLock = VariableLockRequestToLock;
1027 Status = gBS->InstallMultipleProtocolInterfaces (
1028 &mHandle,
1029 &gEdkiiVariableLockProtocolGuid,
1030 &mVariableLock,
1031 NULL
1032 );
1033 ASSERT_EFI_ERROR (Status);
1034
1035 mVarCheck.RegisterSetVariableCheckHandler = VarCheckRegisterSetVariableCheckHandler;
1036 mVarCheck.VariablePropertySet = VarCheckVariablePropertySet;
1037 mVarCheck.VariablePropertyGet = VarCheckVariablePropertyGet;
1038 Status = gBS->InstallMultipleProtocolInterfaces (
1039 &mHandle,
1040 &gEdkiiVarCheckProtocolGuid,
1041 &mVarCheck,
1042 NULL
1043 );
1044 ASSERT_EFI_ERROR (Status);
1045
1046 gBS->CloseEvent (Event);
1047 }
1048
1049
1050 /**
1051 SMM Non-Volatile variable write service is ready notify event handler.
1052
1053 @param[in] Event Event whose notification function is being invoked.
1054 @param[in] Context Pointer to the notification function's context.
1055
1056 **/
1057 VOID
1058 EFIAPI
1059 SmmVariableWriteReady (
1060 IN EFI_EVENT Event,
1061 IN VOID *Context
1062 )
1063 {
1064 EFI_STATUS Status;
1065 VOID *ProtocolOps;
1066
1067 //
1068 // Check whether the protocol is installed or not.
1069 //
1070 Status = gBS->LocateProtocol (&gSmmVariableWriteGuid, NULL, (VOID **) &ProtocolOps);
1071 if (EFI_ERROR (Status)) {
1072 return;
1073 }
1074
1075 //
1076 // Some Secure Boot Policy Var (SecureBoot, etc) updates following other
1077 // Secure Boot Policy Variable change. Record their initial value.
1078 //
1079 RecordSecureBootPolicyVarData();
1080
1081 Status = gBS->InstallProtocolInterface (
1082 &mHandle,
1083 &gEfiVariableWriteArchProtocolGuid,
1084 EFI_NATIVE_INTERFACE,
1085 NULL
1086 );
1087 ASSERT_EFI_ERROR (Status);
1088
1089 gBS->CloseEvent (Event);
1090 }
1091
1092
1093 /**
1094 Variable Driver main entry point. The Variable driver places the 4 EFI
1095 runtime services in the EFI System Table and installs arch protocols
1096 for variable read and write services being available. It also registers
1097 a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
1098
1099 @param[in] ImageHandle The firmware allocated handle for the EFI image.
1100 @param[in] SystemTable A pointer to the EFI System Table.
1101
1102 @retval EFI_SUCCESS Variable service successfully initialized.
1103
1104 **/
1105 EFI_STATUS
1106 EFIAPI
1107 VariableSmmRuntimeInitialize (
1108 IN EFI_HANDLE ImageHandle,
1109 IN EFI_SYSTEM_TABLE *SystemTable
1110 )
1111 {
1112 VOID *SmmVariableRegistration;
1113 VOID *SmmVariableWriteRegistration;
1114 EFI_EVENT OnReadyToBootEvent;
1115 EFI_EVENT ExitBootServiceEvent;
1116 EFI_EVENT LegacyBootEvent;
1117
1118 EfiInitializeLock (&mVariableServicesLock, TPL_NOTIFY);
1119
1120 //
1121 // Smm variable service is ready
1122 //
1123 EfiCreateProtocolNotifyEvent (
1124 &gEfiSmmVariableProtocolGuid,
1125 TPL_CALLBACK,
1126 SmmVariableReady,
1127 NULL,
1128 &SmmVariableRegistration
1129 );
1130
1131 //
1132 // Smm Non-Volatile variable write service is ready
1133 //
1134 EfiCreateProtocolNotifyEvent (
1135 &gSmmVariableWriteGuid,
1136 TPL_CALLBACK,
1137 SmmVariableWriteReady,
1138 NULL,
1139 &SmmVariableWriteRegistration
1140 );
1141
1142 //
1143 // Register the event to reclaim variable for OS usage.
1144 //
1145 EfiCreateEventReadyToBootEx (
1146 TPL_NOTIFY,
1147 OnReadyToBoot,
1148 NULL,
1149 &OnReadyToBootEvent
1150 );
1151
1152 //
1153 // Register the event to inform SMM variable that it is at runtime.
1154 //
1155 gBS->CreateEventEx (
1156 EVT_NOTIFY_SIGNAL,
1157 TPL_NOTIFY,
1158 OnExitBootServices,
1159 NULL,
1160 &gEfiEventExitBootServicesGuid,
1161 &ExitBootServiceEvent
1162 );
1163
1164 //
1165 // Register the event to inform SMM variable that it is at runtime for legacy boot.
1166 // Reuse OnExitBootServices() here.
1167 //
1168 EfiCreateEventLegacyBootEx(
1169 TPL_NOTIFY,
1170 OnExitBootServices,
1171 NULL,
1172 &LegacyBootEvent
1173 );
1174
1175 //
1176 // Register the event to convert the pointer for runtime.
1177 //
1178 gBS->CreateEventEx (
1179 EVT_NOTIFY_SIGNAL,
1180 TPL_NOTIFY,
1181 VariableAddressChangeEvent,
1182 NULL,
1183 &gEfiEventVirtualAddressChangeGuid,
1184 &mVirtualAddressChangeEvent
1185 );
1186
1187 return EFI_SUCCESS;
1188 }
1189