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