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