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