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