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