]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c
Add SMM Variable implementation.
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / RuntimeDxe / VariableSmm.c
1 /** @file
2
3 The sample implementation for SMM variable protocol. And this driver
4 implements an SMI handler to communicate with the DXE runtime driver
5 to provide variable services.
6
7 Copyright (c) 2010, 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 <Protocol/SmmFaultTolerantWrite.h>
18 #include <Library/SmmServicesTableLib.h>
19
20 #include "Variable.h"
21 #include "VariableSmmCommon.h"
22
23 extern SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY *gVariableInfo;
24 EFI_HANDLE mSmmVariableHandle = NULL;
25 EFI_HANDLE mVariableHandle = NULL;
26 BOOLEAN mAtRuntime = FALSE;
27 EFI_GUID mZeroGuid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}};
28 EFI_GUID mSmmVariableWriteGuid = EFI_SMM_VARIABLE_WRITE_GUID;
29
30 EFI_SMM_VARIABLE_PROTOCOL gSmmVariable = {
31 VariableServiceGetVariable,
32 VariableServiceGetNextVariableName,
33 VariableServiceSetVariable,
34 VariableServiceQueryVariableInfo
35 };
36
37
38 /**
39 Return TRUE if ExitBootServices () has been called.
40
41 @retval TRUE If ExitBootServices () has been called.
42 **/
43 BOOLEAN
44 AtRuntime (
45 VOID
46 )
47 {
48 return mAtRuntime;
49 }
50
51 /**
52 Initializes a basic mutual exclusion lock.
53
54 This function initializes a basic mutual exclusion lock to the released state
55 and returns the lock. Each lock provides mutual exclusion access at its task
56 priority level. Since there is no preemption or multiprocessor support in EFI,
57 acquiring the lock only consists of raising to the locks TPL.
58 If Lock is NULL, then ASSERT().
59 If Priority is not a valid TPL value, then ASSERT().
60
61 @param Lock A pointer to the lock data structure to initialize.
62 @param Priority EFI TPL is associated with the lock.
63
64 @return The lock.
65
66 **/
67 EFI_LOCK *
68 InitializeLock (
69 IN OUT EFI_LOCK *Lock,
70 IN EFI_TPL Priority
71 )
72 {
73 return Lock;
74 }
75
76 /**
77 Acquires lock only at boot time. Simply returns at runtime.
78
79 This is a temperary function that will be removed when
80 EfiAcquireLock() in UefiLib can handle the call in UEFI
81 Runtimer driver in RT phase.
82 It calls EfiAcquireLock() at boot time, and simply returns
83 at runtime.
84
85 @param Lock A pointer to the lock to acquire.
86
87 **/
88 VOID
89 AcquireLockOnlyAtBootTime (
90 IN EFI_LOCK *Lock
91 )
92 {
93
94 }
95
96
97 /**
98 Releases lock only at boot time. Simply returns at runtime.
99
100 This is a temperary function which will be removed when
101 EfiReleaseLock() in UefiLib can handle the call in UEFI
102 Runtimer driver in RT phase.
103 It calls EfiReleaseLock() at boot time and simply returns
104 at runtime.
105
106 @param Lock A pointer to the lock to release.
107
108 **/
109 VOID
110 ReleaseLockOnlyAtBootTime (
111 IN EFI_LOCK *Lock
112 )
113 {
114
115 }
116
117 /**
118 Retrive the SMM Fault Tolerent Write protocol interface.
119
120 @param[out] FtwProtocol The interface of SMM Ftw protocol
121
122 @retval EFI_SUCCESS The SMM FTW protocol instance was found and returned in FtwProtocol.
123 @retval EFI_NOT_FOUND The SMM FTW protocol instance was not found.
124 @retval EFI_INVALID_PARAMETER SarProtocol is NULL.
125
126 **/
127 EFI_STATUS
128 GetFtwProtocol (
129 OUT VOID **FtwProtocol
130 )
131 {
132 EFI_STATUS Status;
133
134 //
135 // Locate Smm Fault Tolerent Write protocol
136 //
137 Status = gSmst->SmmLocateProtocol (
138 &gEfiSmmFaultTolerantWriteProtocolGuid,
139 NULL,
140 FtwProtocol
141 );
142 return Status;
143 }
144
145
146 /**
147 Retrive the SMM FVB protocol interface by HANDLE.
148
149 @param[in] FvBlockHandle The handle of SMM FVB protocol that provides services for
150 reading, writing, and erasing the target block.
151 @param[out] FvBlock The interface of SMM FVB protocol
152
153 @retval EFI_SUCCESS The interface information for the specified protocol was returned.
154 @retval EFI_UNSUPPORTED The device does not support the SMM FVB protocol.
155 @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.
156
157 **/
158 EFI_STATUS
159 GetFvbByHandle (
160 IN EFI_HANDLE FvBlockHandle,
161 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock
162 )
163 {
164 //
165 // To get the SMM FVB protocol interface on the handle
166 //
167 return gSmst->SmmHandleProtocol (
168 FvBlockHandle,
169 &gEfiSmmFirmwareVolumeBlockProtocolGuid,
170 (VOID **) FvBlock
171 );
172 }
173
174
175 /**
176 Function returns an array of handles that support the SMM FVB protocol
177 in a buffer allocated from pool.
178
179 @param[out] NumberHandles The number of handles returned in Buffer.
180 @param[out] Buffer A pointer to the buffer to return the requested
181 array of handles that support SMM FVB protocol.
182
183 @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of
184 handles in Buffer was returned in NumberHandles.
185 @retval EFI_NOT_FOUND No SMM FVB handle was found.
186 @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results.
187 @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.
188
189 **/
190 EFI_STATUS
191 GetFvbCountAndBuffer (
192 OUT UINTN *NumberHandles,
193 OUT EFI_HANDLE **Buffer
194 )
195 {
196 EFI_STATUS Status;
197 UINTN BufferSize;
198
199 if ((NumberHandles == NULL) || (Buffer == NULL)) {
200 return EFI_INVALID_PARAMETER;
201 }
202
203 BufferSize = 0;
204 *NumberHandles = 0;
205 *Buffer = NULL;
206 Status = gSmst->SmmLocateHandle (
207 ByProtocol,
208 &gEfiSmmFirmwareVolumeBlockProtocolGuid,
209 NULL,
210 &BufferSize,
211 *Buffer
212 );
213 if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) {
214 return EFI_NOT_FOUND;
215 }
216
217 *Buffer = AllocatePool (BufferSize);
218 if (*Buffer == NULL) {
219 return EFI_OUT_OF_RESOURCES;
220 }
221
222 Status = gSmst->SmmLocateHandle (
223 ByProtocol,
224 &gEfiSmmFirmwareVolumeBlockProtocolGuid,
225 NULL,
226 &BufferSize,
227 *Buffer
228 );
229
230 *NumberHandles = BufferSize / sizeof(EFI_HANDLE);
231 if (EFI_ERROR(Status)) {
232 *NumberHandles = 0;
233 }
234
235 return Status;
236 }
237
238
239 /**
240 Get the variable statistics information from the information buffer pointed by gVariableInfo.
241
242 @param[in, out] InfoEntry A pointer to the buffer of variable information entry.
243 On input, point to the variable information returned last time. if
244 InfoEntry->VendorGuid is zero, return the first information.
245 On output, point to the next variable information.
246 @param[in, out] InfoSize On input, the size of the variable information buffer.
247 On output, the returned variable information size.
248
249 @retval EFI_SUCCESS The variable information is found and returned successfully.
250 @retval EFI_UNSUPPORTED No variable inoformation exists in variable driver. The
251 PcdVariableCollectStatistics should be set TRUE to support it.
252 @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the next variable information.
253
254 **/
255 EFI_STATUS
256 SmmVariableGetStatistics (
257 IN OUT SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY *InfoEntry,
258 IN OUT UINTN *InfoSize
259 )
260 {
261 SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY *VariableInfo;
262 UINTN NameLength;
263 UINTN StatisticsInfoSize;
264 CHAR16 *InfoName;
265
266 ASSERT (InfoEntry != NULL);
267 VariableInfo = gVariableInfo;
268 if (VariableInfo == NULL) {
269 return EFI_UNSUPPORTED;
270 }
271
272 StatisticsInfoSize = sizeof (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name);
273 if (*InfoSize < sizeof (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY)) {
274 *InfoSize = StatisticsInfoSize;
275 return EFI_BUFFER_TOO_SMALL;
276 }
277 InfoName = (CHAR16 *)(InfoEntry + 1);
278
279 if (CompareGuid (&InfoEntry->VendorGuid, &mZeroGuid)) {
280 //
281 // Return the first variable info
282 //
283 CopyMem (InfoEntry, VariableInfo, sizeof (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY));
284 CopyMem (InfoName, VariableInfo->Name, StrSize (VariableInfo->Name));
285 *InfoSize = StatisticsInfoSize;
286 return EFI_SUCCESS;
287 }
288
289 //
290 // Get the next variable info
291 //
292 while (VariableInfo != NULL) {
293 if (CompareGuid (&VariableInfo->VendorGuid, &InfoEntry->VendorGuid)) {
294 NameLength = StrSize (VariableInfo->Name);
295 if (NameLength == StrSize (InfoName)) {
296 if (CompareMem (VariableInfo->Name, InfoName, NameLength) == 0) {
297 //
298 // Find the match one
299 //
300 VariableInfo = VariableInfo->Next;
301 break;
302 }
303 }
304 }
305 VariableInfo = VariableInfo->Next;
306 };
307
308 if (VariableInfo == NULL) {
309 *InfoSize = 0;
310 return EFI_SUCCESS;
311 }
312
313 //
314 // Output the new variable info
315 //
316 StatisticsInfoSize = sizeof (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name);
317 if (*InfoSize < StatisticsInfoSize) {
318 *InfoSize = StatisticsInfoSize;
319 return EFI_BUFFER_TOO_SMALL;
320 }
321
322 CopyMem (InfoEntry, VariableInfo, sizeof (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY));
323 CopyMem (InfoName, VariableInfo->Name, StrSize (VariableInfo->Name));
324 *InfoSize = StatisticsInfoSize;
325
326 return EFI_SUCCESS;
327 }
328
329
330 /**
331 Communication service SMI Handler entry.
332
333 This SMI handler provides services for the variable wrapper driver.
334
335 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
336 @param[in] RegisterContext Points to an optional handler context which was specified when the
337 handler was registered.
338 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
339 be conveyed from a non-SMM environment into an SMM environment.
340 @param[in, out] CommBufferSize The size of the CommBuffer.
341
342 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
343 should still be called.
344 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
345 still be called.
346 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
347 be called.
348 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
349 **/
350 EFI_STATUS
351 EFIAPI
352 SmmVariableHandler (
353 IN EFI_HANDLE DispatchHandle,
354 IN CONST VOID *RegisterContext,
355 IN OUT VOID *CommBuffer,
356 IN OUT UINTN *CommBufferSize
357 )
358 {
359 EFI_STATUS Status;
360 SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader;
361 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader;
362 SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *GetNextVariableName;
363 SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *QueryVariableInfo;
364 SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY *VariableInfo;
365 UINTN InfoSize;
366
367 ASSERT (CommBuffer != NULL);
368
369 SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *)CommBuffer;
370 switch (SmmVariableFunctionHeader->Function) {
371 case SMM_VARIABLE_FUNCTION_GET_VARIABLE:
372 SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) SmmVariableFunctionHeader->Data;
373 Status = VariableServiceGetVariable (
374 SmmVariableHeader->Name,
375 &SmmVariableHeader->Guid,
376 &SmmVariableHeader->Attributes,
377 &SmmVariableHeader->DataSize,
378 (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize
379 );
380 break;
381
382 case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME:
383 GetNextVariableName = (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *) SmmVariableFunctionHeader->Data;
384 Status = VariableServiceGetNextVariableName (
385 &GetNextVariableName->NameSize,
386 GetNextVariableName->Name,
387 &GetNextVariableName->Guid
388 );
389 break;
390
391 case SMM_VARIABLE_FUNCTION_SET_VARIABLE:
392 SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) SmmVariableFunctionHeader->Data;
393 Status = VariableServiceSetVariable (
394 SmmVariableHeader->Name,
395 &SmmVariableHeader->Guid,
396 SmmVariableHeader->Attributes,
397 SmmVariableHeader->DataSize,
398 (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize
399 );
400 break;
401
402 case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO:
403 QueryVariableInfo = (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *) SmmVariableFunctionHeader->Data;
404 Status = VariableServiceQueryVariableInfo (
405 QueryVariableInfo->Attributes,
406 &QueryVariableInfo->MaximumVariableStorageSize,
407 &QueryVariableInfo->RemainingVariableStorageSize,
408 &QueryVariableInfo->MaximumVariableSize
409 );
410 break;
411
412 case SMM_VARIABLE_FUNCTION_READY_TO_BOOT:
413 ReclaimForOS ();
414 Status = EFI_SUCCESS;
415 break;
416
417 case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE:
418 mAtRuntime = TRUE;
419 Status = EFI_SUCCESS;
420 break;
421
422 case SMM_VARIABLE_FUNCTION_GET_STATISTICS:
423 VariableInfo = (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY *) SmmVariableFunctionHeader->Data;
424 InfoSize = *CommBufferSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_HEADER, Data);
425 Status = SmmVariableGetStatistics (VariableInfo, &InfoSize);
426 *CommBufferSize = InfoSize + OFFSET_OF (SMM_VARIABLE_COMMUNICATE_HEADER, Data);
427 break;
428
429 default:
430 ASSERT (FALSE);
431 Status = EFI_UNSUPPORTED;
432 }
433
434 SmmVariableFunctionHeader->ReturnStatus = Status;
435
436 return EFI_SUCCESS;
437 }
438
439
440 /**
441 SMM Fault Tolerant Write protocol notification event handler.
442
443 Non-Volatile variable write may needs FTW protocol to reclaim when
444 writting variable.
445
446 @param Protocol Points to the protocol's unique identifier
447 @param Interface Points to the interface instance
448 @param Handle The handle on which the interface was installed
449
450 @retval EFI_SUCCESS SmmEventCallback runs successfully
451 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
452
453 **/
454 EFI_STATUS
455 EFIAPI
456 SmmFtwNotificationEvent (
457 IN CONST EFI_GUID *Protocol,
458 IN VOID *Interface,
459 IN EFI_HANDLE Handle
460 )
461 {
462 EFI_STATUS Status;
463 EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol;
464 EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol;
465 EFI_PHYSICAL_ADDRESS NvStorageVariableBase;
466
467 if (mVariableModuleGlobal->FvbInstance != NULL) {
468 return EFI_SUCCESS;
469 }
470
471 //
472 // Ensure SMM FTW protocol is installed.
473 //
474 Status = GetFtwProtocol (&FtwProtocol);
475 if (EFI_ERROR (Status)) {
476 return Status;
477 }
478
479 //
480 // Find the proper FVB protocol for variable.
481 //
482 NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageVariableBase64);
483 if (NvStorageVariableBase == 0) {
484 NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageVariableBase);
485 }
486 Status = GetFvbInfoByAddress (NvStorageVariableBase, NULL, &FvbProtocol);
487 if (EFI_ERROR (Status)) {
488 return EFI_NOT_FOUND;
489 }
490
491 mVariableModuleGlobal->FvbInstance = FvbProtocol;
492
493 Status = VariableWriteServiceInitialize ();
494 ASSERT_EFI_ERROR (Status);
495
496 //
497 // Notify the variable wrapper driver the variable write service is ready
498 //
499 Status = gBS->InstallProtocolInterface (
500 &mSmmVariableHandle,
501 &mSmmVariableWriteGuid,
502 EFI_NATIVE_INTERFACE,
503 NULL
504 );
505 ASSERT_EFI_ERROR (Status);
506
507 return EFI_SUCCESS;
508 }
509
510
511 /**
512 Variable Driver main entry point. The Variable driver places the 4 EFI
513 runtime services in the EFI System Table and installs arch protocols
514 for variable read and write services being availible. It also registers
515 a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
516
517 @param[in] ImageHandle The firmware allocated handle for the EFI image.
518 @param[in] SystemTable A pointer to the EFI System Table.
519
520 @retval EFI_SUCCESS Variable service successfully initialized.
521
522 **/
523 EFI_STATUS
524 EFIAPI
525 VariableServiceInitialize (
526 IN EFI_HANDLE ImageHandle,
527 IN EFI_SYSTEM_TABLE *SystemTable
528 )
529 {
530 EFI_STATUS Status;
531 EFI_HANDLE VariableHandle;
532 VOID *SmmFtwRegistration;
533
534 //
535 // Variable initialize.
536 //
537 Status = VariableCommonInitialize ();
538 ASSERT_EFI_ERROR (Status);
539
540 //
541 // Install the Smm Variable Protocol on a new handle.
542 //
543 VariableHandle = NULL;
544 Status = gSmst->SmmInstallProtocolInterface (
545 &VariableHandle,
546 &gEfiSmmVariableProtocolGuid,
547 EFI_NATIVE_INTERFACE,
548 &gSmmVariable
549 );
550 ASSERT_EFI_ERROR (Status);
551
552 ///
553 /// Register SMM variable SMI handler
554 ///
555 VariableHandle = NULL;
556 Status = gSmst->SmiHandlerRegister (SmmVariableHandler, &gEfiSmmVariableProtocolGuid, &VariableHandle);
557 ASSERT_EFI_ERROR (Status);
558
559 //
560 // Notify the variable wrapper driver the variable service is ready
561 //
562 Status = SystemTable->BootServices->InstallProtocolInterface (
563 &mVariableHandle,
564 &gEfiSmmVariableProtocolGuid,
565 EFI_NATIVE_INTERFACE,
566 &gSmmVariable
567 );
568 ASSERT_EFI_ERROR (Status);
569
570 //
571 // Register FtwNotificationEvent () notify function.
572 //
573 Status = gSmst->SmmRegisterProtocolNotify (
574 &gEfiSmmFaultTolerantWriteProtocolGuid,
575 SmmFtwNotificationEvent,
576 &SmmFtwRegistration
577 );
578 ASSERT_EFI_ERROR (Status);
579
580 SmmFtwNotificationEvent (NULL, NULL, NULL);
581
582 return EFI_SUCCESS;
583 }
584
585