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