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