]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c
1. Fix TOCTOU issue in VariableSmm, FtwSmm, FpdtSmm, SmmCorePerformance SMM handler...
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / RuntimeDxe / VariableSmm.c
CommitLineData
8a2d4996 1/** @file\r
2\r
3 The sample implementation for SMM variable protocol. And this driver \r
4 implements an SMI handler to communicate with the DXE runtime driver \r
5 to provide variable services.\r
6\r
2445a70e 7 Caution: This module requires additional review when modified.\r
8 This driver will have external input - variable data and communicate buffer in SMM mode.\r
9 This external input must be validated carefully to avoid security issue like\r
10 buffer overflow, integer overflow.\r
11\r
12 SmmVariableHandler() will receive untrusted input and do basic validation.\r
13\r
14 Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(), \r
15 VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(), \r
16 SmmVariableGetStatistics() should also do validation based on its own knowledge.\r
17\r
5e5bb2a9 18Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved.<BR>\r
2445a70e 19This program and the accompanying materials \r
20are licensed and made available under the terms and conditions of the BSD License \r
21which accompanies this distribution. The full text of the license may be found at \r
22http://opensource.org/licenses/bsd-license.php\r
8a2d4996 23\r
24THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
25WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
26\r
27**/\r
d00ed85e 28#include <Protocol/SmmVariable.h>\r
29#include <Protocol/SmmFirmwareVolumeBlock.h>\r
8a2d4996 30#include <Protocol/SmmFaultTolerantWrite.h>\r
2445a70e 31#include <Protocol/SmmAccess2.h>\r
32\r
8a2d4996 33#include <Library/SmmServicesTableLib.h>\r
34\r
d00ed85e 35#include <Guid/VariableFormat.h>\r
36#include <Guid/SmmVariableCommon.h>\r
8a2d4996 37#include "Variable.h"\r
8a2d4996 38\r
2445a70e 39EFI_SMRAM_DESCRIPTOR *mSmramRanges;\r
40UINTN mSmramRangeCount;\r
41\r
d00ed85e 42extern VARIABLE_INFO_ENTRY *gVariableInfo;\r
8a2d4996 43EFI_HANDLE mSmmVariableHandle = NULL;\r
44EFI_HANDLE mVariableHandle = NULL;\r
45BOOLEAN mAtRuntime = FALSE;\r
46EFI_GUID mZeroGuid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}};\r
5e5bb2a9
SZ
47UINT8 *mVariableBufferPayload = NULL;\r
48UINTN mVariableBufferPayloadSize;\r
49\r
8a2d4996 50EFI_SMM_VARIABLE_PROTOCOL gSmmVariable = {\r
51 VariableServiceGetVariable,\r
52 VariableServiceGetNextVariableName,\r
53 VariableServiceSetVariable,\r
54 VariableServiceQueryVariableInfo\r
55};\r
56\r
57\r
58/**\r
59 Return TRUE if ExitBootServices () has been called.\r
60 \r
61 @retval TRUE If ExitBootServices () has been called.\r
62**/\r
63BOOLEAN\r
64AtRuntime (\r
65 VOID\r
66 )\r
67{\r
68 return mAtRuntime;\r
69}\r
70\r
2445a70e 71/**\r
72 This function check if the address is in SMRAM.\r
73\r
74 @param Buffer the buffer address to be checked.\r
75 @param Length the buffer length to be checked.\r
76\r
77 @retval TRUE this address is in SMRAM.\r
78 @retval FALSE this address is NOT in SMRAM.\r
79**/\r
80BOOLEAN\r
81InternalIsAddressInSmram (\r
82 IN EFI_PHYSICAL_ADDRESS Buffer,\r
83 IN UINT64 Length\r
84 )\r
85{\r
86 UINTN Index;\r
87\r
88 for (Index = 0; Index < mSmramRangeCount; Index ++) {\r
89 if (((Buffer >= mSmramRanges[Index].CpuStart) && (Buffer < mSmramRanges[Index].CpuStart + mSmramRanges[Index].PhysicalSize)) ||\r
90 ((mSmramRanges[Index].CpuStart >= Buffer) && (mSmramRanges[Index].CpuStart < Buffer + Length))) {\r
91 return TRUE;\r
92 }\r
93 }\r
94\r
95 return FALSE;\r
96}\r
97\r
9d00d20e
SZ
98/**\r
99 This function check if the address refered by Buffer and Length is valid.\r
100\r
101 @param Buffer the buffer address to be checked.\r
102 @param Length the buffer length to be checked.\r
103\r
104 @retval TRUE this address is valid.\r
105 @retval FALSE this address is NOT valid.\r
106**/\r
107BOOLEAN\r
108InternalIsAddressValid (\r
109 IN UINTN Buffer,\r
110 IN UINTN Length\r
111 )\r
112{\r
113 if (Buffer > (MAX_ADDRESS - Length)) {\r
114 //\r
115 // Overflow happen\r
116 //\r
117 return FALSE;\r
118 }\r
119 if (InternalIsAddressInSmram ((EFI_PHYSICAL_ADDRESS)Buffer, (UINT64)Length)) {\r
120 return FALSE;\r
121 }\r
122 return TRUE;\r
123}\r
2445a70e 124\r
8a2d4996 125/**\r
126 Initializes a basic mutual exclusion lock.\r
127\r
128 This function initializes a basic mutual exclusion lock to the released state \r
129 and returns the lock. Each lock provides mutual exclusion access at its task \r
130 priority level. Since there is no preemption or multiprocessor support in EFI,\r
131 acquiring the lock only consists of raising to the locks TPL.\r
132 If Lock is NULL, then ASSERT().\r
133 If Priority is not a valid TPL value, then ASSERT().\r
134\r
135 @param Lock A pointer to the lock data structure to initialize.\r
136 @param Priority EFI TPL is associated with the lock.\r
137\r
138 @return The lock.\r
139\r
140**/\r
141EFI_LOCK *\r
142InitializeLock (\r
143 IN OUT EFI_LOCK *Lock,\r
144 IN EFI_TPL Priority\r
145 )\r
146{\r
147 return Lock;\r
148}\r
149\r
150/**\r
151 Acquires lock only at boot time. Simply returns at runtime.\r
152\r
153 This is a temperary function that will be removed when\r
154 EfiAcquireLock() in UefiLib can handle the call in UEFI\r
155 Runtimer driver in RT phase.\r
156 It calls EfiAcquireLock() at boot time, and simply returns\r
157 at runtime.\r
158\r
159 @param Lock A pointer to the lock to acquire.\r
160\r
161**/\r
162VOID\r
163AcquireLockOnlyAtBootTime (\r
164 IN EFI_LOCK *Lock\r
165 )\r
166{\r
167\r
168}\r
169\r
170\r
171/**\r
172 Releases lock only at boot time. Simply returns at runtime.\r
173\r
174 This is a temperary function which will be removed when\r
175 EfiReleaseLock() in UefiLib can handle the call in UEFI\r
176 Runtimer driver in RT phase.\r
177 It calls EfiReleaseLock() at boot time and simply returns\r
178 at runtime.\r
179\r
180 @param Lock A pointer to the lock to release.\r
181\r
182**/\r
183VOID\r
184ReleaseLockOnlyAtBootTime (\r
185 IN EFI_LOCK *Lock\r
186 )\r
187{\r
188\r
189}\r
190\r
191/**\r
192 Retrive the SMM Fault Tolerent Write protocol interface.\r
193\r
194 @param[out] FtwProtocol The interface of SMM Ftw protocol\r
195\r
196 @retval EFI_SUCCESS The SMM FTW protocol instance was found and returned in FtwProtocol.\r
197 @retval EFI_NOT_FOUND The SMM FTW protocol instance was not found.\r
198 @retval EFI_INVALID_PARAMETER SarProtocol is NULL.\r
199\r
200**/\r
201EFI_STATUS\r
202GetFtwProtocol (\r
203 OUT VOID **FtwProtocol\r
204 )\r
205{\r
206 EFI_STATUS Status;\r
207\r
208 //\r
209 // Locate Smm Fault Tolerent Write protocol\r
210 //\r
211 Status = gSmst->SmmLocateProtocol (\r
212 &gEfiSmmFaultTolerantWriteProtocolGuid, \r
213 NULL, \r
214 FtwProtocol\r
215 );\r
216 return Status;\r
217}\r
218\r
219\r
220/**\r
221 Retrive the SMM FVB protocol interface by HANDLE.\r
222\r
223 @param[in] FvBlockHandle The handle of SMM FVB protocol that provides services for\r
224 reading, writing, and erasing the target block.\r
225 @param[out] FvBlock The interface of SMM FVB protocol\r
226\r
227 @retval EFI_SUCCESS The interface information for the specified protocol was returned.\r
228 @retval EFI_UNSUPPORTED The device does not support the SMM FVB protocol.\r
229 @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.\r
230\r
231**/\r
232EFI_STATUS\r
233GetFvbByHandle (\r
234 IN EFI_HANDLE FvBlockHandle,\r
235 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock\r
236 )\r
237{\r
238 //\r
239 // To get the SMM FVB protocol interface on the handle\r
240 //\r
241 return gSmst->SmmHandleProtocol (\r
242 FvBlockHandle,\r
243 &gEfiSmmFirmwareVolumeBlockProtocolGuid,\r
244 (VOID **) FvBlock\r
245 );\r
246}\r
247\r
248\r
249/**\r
250 Function returns an array of handles that support the SMM FVB protocol\r
251 in a buffer allocated from pool. \r
252\r
253 @param[out] NumberHandles The number of handles returned in Buffer.\r
254 @param[out] Buffer A pointer to the buffer to return the requested\r
255 array of handles that support SMM FVB protocol.\r
256\r
257 @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of\r
258 handles in Buffer was returned in NumberHandles.\r
259 @retval EFI_NOT_FOUND No SMM FVB handle was found.\r
260 @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results.\r
261 @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.\r
262\r
263**/\r
264EFI_STATUS\r
265GetFvbCountAndBuffer (\r
266 OUT UINTN *NumberHandles,\r
267 OUT EFI_HANDLE **Buffer\r
268 )\r
269{\r
270 EFI_STATUS Status;\r
271 UINTN BufferSize;\r
272\r
273 if ((NumberHandles == NULL) || (Buffer == NULL)) {\r
274 return EFI_INVALID_PARAMETER;\r
275 }\r
276\r
277 BufferSize = 0;\r
278 *NumberHandles = 0;\r
279 *Buffer = NULL;\r
280 Status = gSmst->SmmLocateHandle (\r
281 ByProtocol,\r
282 &gEfiSmmFirmwareVolumeBlockProtocolGuid,\r
283 NULL,\r
284 &BufferSize,\r
285 *Buffer\r
286 );\r
287 if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) {\r
288 return EFI_NOT_FOUND;\r
289 }\r
290\r
291 *Buffer = AllocatePool (BufferSize);\r
292 if (*Buffer == NULL) {\r
293 return EFI_OUT_OF_RESOURCES;\r
294 }\r
295\r
296 Status = gSmst->SmmLocateHandle (\r
297 ByProtocol,\r
298 &gEfiSmmFirmwareVolumeBlockProtocolGuid,\r
299 NULL,\r
300 &BufferSize,\r
301 *Buffer\r
302 );\r
303\r
304 *NumberHandles = BufferSize / sizeof(EFI_HANDLE);\r
305 if (EFI_ERROR(Status)) {\r
306 *NumberHandles = 0;\r
5e5bb2a9
SZ
307 FreePool (*Buffer);\r
308 *Buffer = NULL;\r
8a2d4996 309 }\r
310\r
311 return Status;\r
312}\r
313\r
314\r
315/**\r
316 Get the variable statistics information from the information buffer pointed by gVariableInfo.\r
317\r
2445a70e 318 Caution: This function may be invoked at SMM runtime.\r
319 InfoEntry and InfoSize are external input. Care must be taken to make sure not security issue at runtime.\r
320\r
321 @param[in, out] InfoEntry A pointer to the buffer of variable information entry.\r
322 On input, point to the variable information returned last time. if \r
323 InfoEntry->VendorGuid is zero, return the first information.\r
324 On output, point to the next variable information.\r
325 @param[in, out] InfoSize On input, the size of the variable information buffer.\r
326 On output, the returned variable information size.\r
8a2d4996 327\r
328 @retval EFI_SUCCESS The variable information is found and returned successfully.\r
329 @retval EFI_UNSUPPORTED No variable inoformation exists in variable driver. The \r
330 PcdVariableCollectStatistics should be set TRUE to support it.\r
331 @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the next variable information.\r
332\r
333**/\r
334EFI_STATUS\r
335SmmVariableGetStatistics (\r
d00ed85e 336 IN OUT VARIABLE_INFO_ENTRY *InfoEntry,\r
8a2d4996 337 IN OUT UINTN *InfoSize\r
338 )\r
339{\r
d00ed85e 340 VARIABLE_INFO_ENTRY *VariableInfo;\r
8a2d4996 341 UINTN NameLength;\r
342 UINTN StatisticsInfoSize;\r
343 CHAR16 *InfoName;\r
5e5bb2a9
SZ
344 EFI_GUID VendorGuid;\r
345\r
8a2d4996 346 ASSERT (InfoEntry != NULL);\r
347 VariableInfo = gVariableInfo; \r
348 if (VariableInfo == NULL) {\r
349 return EFI_UNSUPPORTED;\r
350 }\r
351\r
d00ed85e 352 StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name);\r
eb96e4f2 353 if (*InfoSize < StatisticsInfoSize) {\r
8a2d4996 354 *InfoSize = StatisticsInfoSize;\r
355 return EFI_BUFFER_TOO_SMALL;\r
356 }\r
357 InfoName = (CHAR16 *)(InfoEntry + 1);\r
358\r
5e5bb2a9
SZ
359 CopyGuid (&VendorGuid, &InfoEntry->VendorGuid);\r
360\r
361 if (CompareGuid (&VendorGuid, &mZeroGuid)) {\r
8a2d4996 362 //\r
363 // Return the first variable info\r
364 //\r
d00ed85e 365 CopyMem (InfoEntry, VariableInfo, sizeof (VARIABLE_INFO_ENTRY));\r
8a2d4996 366 CopyMem (InfoName, VariableInfo->Name, StrSize (VariableInfo->Name));\r
367 *InfoSize = StatisticsInfoSize;\r
368 return EFI_SUCCESS;\r
369 }\r
370\r
371 //\r
372 // Get the next variable info\r
373 //\r
374 while (VariableInfo != NULL) {\r
5e5bb2a9 375 if (CompareGuid (&VariableInfo->VendorGuid, &VendorGuid)) {\r
8a2d4996 376 NameLength = StrSize (VariableInfo->Name);\r
377 if (NameLength == StrSize (InfoName)) {\r
378 if (CompareMem (VariableInfo->Name, InfoName, NameLength) == 0) {\r
379 //\r
380 // Find the match one\r
381 //\r
382 VariableInfo = VariableInfo->Next;\r
383 break;\r
384 }\r
385 }\r
386 }\r
387 VariableInfo = VariableInfo->Next;\r
388 };\r
389 \r
390 if (VariableInfo == NULL) {\r
391 *InfoSize = 0;\r
392 return EFI_SUCCESS;\r
393 }\r
394\r
395 //\r
396 // Output the new variable info\r
397 //\r
d00ed85e 398 StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name);\r
8a2d4996 399 if (*InfoSize < StatisticsInfoSize) {\r
400 *InfoSize = StatisticsInfoSize;\r
401 return EFI_BUFFER_TOO_SMALL;\r
402 }\r
403\r
d00ed85e 404 CopyMem (InfoEntry, VariableInfo, sizeof (VARIABLE_INFO_ENTRY));\r
8a2d4996 405 CopyMem (InfoName, VariableInfo->Name, StrSize (VariableInfo->Name));\r
406 *InfoSize = StatisticsInfoSize;\r
407 \r
408 return EFI_SUCCESS;\r
409}\r
410\r
411\r
412/**\r
413 Communication service SMI Handler entry.\r
414\r
415 This SMI handler provides services for the variable wrapper driver.\r
416\r
2445a70e 417 Caution: This function may receive untrusted input.\r
418 This variable data and communicate buffer are external input, so this function will do basic validation.\r
419 Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(), \r
420 VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(), \r
421 SmmVariableGetStatistics() should also do validation based on its own knowledge.\r
422\r
8a2d4996 423 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().\r
424 @param[in] RegisterContext Points to an optional handler context which was specified when the\r
425 handler was registered.\r
426 @param[in, out] CommBuffer A pointer to a collection of data in memory that will\r
427 be conveyed from a non-SMM environment into an SMM environment.\r
428 @param[in, out] CommBufferSize The size of the CommBuffer.\r
429\r
430 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers \r
431 should still be called.\r
432 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should \r
433 still be called.\r
434 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still \r
435 be called.\r
436 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.\r
437**/\r
438EFI_STATUS\r
439EFIAPI\r
440SmmVariableHandler (\r
441 IN EFI_HANDLE DispatchHandle,\r
442 IN CONST VOID *RegisterContext,\r
443 IN OUT VOID *CommBuffer,\r
444 IN OUT UINTN *CommBufferSize\r
445 )\r
446{\r
447 EFI_STATUS Status;\r
448 SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader;\r
449 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader;\r
450 SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *GetNextVariableName;\r
451 SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *QueryVariableInfo;\r
d00ed85e 452 VARIABLE_INFO_ENTRY *VariableInfo;\r
8a2d4996 453 UINTN InfoSize;\r
9d00d20e 454 UINTN NameBufferSize;\r
5e5bb2a9 455 UINTN CommBufferPayloadSize;\r
8a2d4996 456\r
2445a70e 457 //\r
458 // If input is invalid, stop processing this SMI\r
459 //\r
460 if (CommBuffer == NULL || CommBufferSize == NULL) {\r
461 return EFI_SUCCESS;\r
462 }\r
463\r
d294b9a4 464 if (*CommBufferSize < SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {\r
5e5bb2a9
SZ
465 DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer size invalid!\n"));\r
466 return EFI_SUCCESS;\r
467 }\r
468 CommBufferPayloadSize = *CommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;\r
469 if (CommBufferPayloadSize > mVariableBufferPayloadSize) {\r
470 DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer payload size invalid!\n"));\r
2445a70e 471 return EFI_SUCCESS;\r
472 }\r
473\r
9d00d20e 474 if (!InternalIsAddressValid ((UINTN)CommBuffer, *CommBufferSize)) {\r
5e5bb2a9 475 DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer in SMRAM or overflow!\n"));\r
2445a70e 476 return EFI_SUCCESS;\r
477 }\r
8a2d4996 478\r
479 SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *)CommBuffer;\r
480 switch (SmmVariableFunctionHeader->Function) {\r
481 case SMM_VARIABLE_FUNCTION_GET_VARIABLE:\r
5e5bb2a9
SZ
482 if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) {\r
483 DEBUG ((EFI_D_ERROR, "GetVariable: SMM communication buffer size invalid!\n"));\r
484 return EFI_SUCCESS;\r
485 }\r
486 //\r
487 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.\r
488 //\r
489 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);\r
490 SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) mVariableBufferPayload;\r
9d00d20e
SZ
491 if (((UINTN)(~0) - SmmVariableHeader->DataSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||\r
492 ((UINTN)(~0) - SmmVariableHeader->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) {\r
493 //\r
494 // Prevent InfoSize overflow happen\r
495 //\r
496 Status = EFI_ACCESS_DENIED;\r
497 goto EXIT;\r
498 }\r
2445a70e 499 InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) \r
500 + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize;\r
501\r
502 //\r
503 // SMRAM range check already covered before\r
504 //\r
5e5bb2a9
SZ
505 if (InfoSize > CommBufferPayloadSize) {\r
506 DEBUG ((EFI_D_ERROR, "GetVariable: Data size exceed communication buffer size limit!\n"));\r
2445a70e 507 Status = EFI_ACCESS_DENIED;\r
508 goto EXIT;\r
509 }\r
510\r
9d00d20e
SZ
511 if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize/sizeof (CHAR16) - 1] != L'\0') {\r
512 //\r
513 // Make sure VariableName is A Null-terminated string.\r
514 //\r
515 Status = EFI_ACCESS_DENIED;\r
516 goto EXIT;\r
517 }\r
518\r
8a2d4996 519 Status = VariableServiceGetVariable (\r
520 SmmVariableHeader->Name,\r
521 &SmmVariableHeader->Guid,\r
522 &SmmVariableHeader->Attributes,\r
523 &SmmVariableHeader->DataSize,\r
524 (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize\r
525 );\r
5e5bb2a9 526 CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize);\r
8a2d4996 527 break;\r
528 \r
529 case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME:\r
5e5bb2a9
SZ
530 if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {\r
531 DEBUG ((EFI_D_ERROR, "GetNextVariableName: SMM communication buffer size invalid!\n"));\r
532 return EFI_SUCCESS;\r
533 }\r
534 //\r
535 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.\r
536 //\r
537 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);\r
538 GetNextVariableName = (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *) mVariableBufferPayload;\r
9d00d20e
SZ
539 if ((UINTN)(~0) - GetNextVariableName->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {\r
540 //\r
541 // Prevent InfoSize overflow happen\r
542 //\r
543 Status = EFI_ACCESS_DENIED;\r
544 goto EXIT;\r
545 }\r
2445a70e 546 InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + GetNextVariableName->NameSize;\r
547\r
548 //\r
549 // SMRAM range check already covered before\r
550 //\r
5e5bb2a9
SZ
551 if (InfoSize > CommBufferPayloadSize) {\r
552 DEBUG ((EFI_D_ERROR, "GetNextVariableName: Data size exceed communication buffer size limit!\n"));\r
2445a70e 553 Status = EFI_ACCESS_DENIED;\r
554 goto EXIT;\r
555 }\r
556\r
5e5bb2a9 557 NameBufferSize = CommBufferPayloadSize - OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name);\r
9d00d20e
SZ
558 if (NameBufferSize < sizeof (CHAR16) || GetNextVariableName->Name[NameBufferSize/sizeof (CHAR16) - 1] != L'\0') {\r
559 //\r
560 // Make sure input VariableName is A Null-terminated string.\r
561 //\r
562 Status = EFI_ACCESS_DENIED;\r
563 goto EXIT;\r
564 }\r
565\r
8a2d4996 566 Status = VariableServiceGetNextVariableName (\r
567 &GetNextVariableName->NameSize,\r
568 GetNextVariableName->Name,\r
569 &GetNextVariableName->Guid\r
570 );\r
5e5bb2a9 571 CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize);\r
8a2d4996 572 break;\r
573 \r
574 case SMM_VARIABLE_FUNCTION_SET_VARIABLE:\r
5e5bb2a9
SZ
575 if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) {\r
576 DEBUG ((EFI_D_ERROR, "SetVariable: SMM communication buffer size invalid!\n"));\r
577 return EFI_SUCCESS;\r
578 }\r
579 //\r
580 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.\r
581 //\r
582 CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);\r
583 SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) mVariableBufferPayload;\r
9d00d20e
SZ
584 if (((UINTN)(~0) - SmmVariableHeader->DataSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||\r
585 ((UINTN)(~0) - SmmVariableHeader->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) {\r
586 //\r
587 // Prevent InfoSize overflow happen\r
588 //\r
589 Status = EFI_ACCESS_DENIED;\r
590 goto EXIT;\r
591 }\r
d17c4eac 592 InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)\r
593 + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize;\r
594\r
595 //\r
596 // SMRAM range check already covered before\r
597 // Data buffer should not contain SMM range\r
598 //\r
5e5bb2a9
SZ
599 if (InfoSize > CommBufferPayloadSize) {\r
600 DEBUG ((EFI_D_ERROR, "SetVariable: Data size exceed communication buffer size limit!\n"));\r
d17c4eac 601 Status = EFI_ACCESS_DENIED;\r
602 goto EXIT;\r
603 }\r
604\r
9d00d20e
SZ
605 if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize/sizeof (CHAR16) - 1] != L'\0') {\r
606 //\r
607 // Make sure VariableName is A Null-terminated string.\r
608 //\r
609 Status = EFI_ACCESS_DENIED;\r
610 goto EXIT;\r
611 }\r
612\r
8a2d4996 613 Status = VariableServiceSetVariable (\r
614 SmmVariableHeader->Name,\r
615 &SmmVariableHeader->Guid,\r
616 SmmVariableHeader->Attributes,\r
617 SmmVariableHeader->DataSize,\r
618 (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize\r
619 );\r
620 break;\r
621 \r
622 case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO:\r
5e5bb2a9
SZ
623 if (CommBufferPayloadSize < sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO)) {\r
624 DEBUG ((EFI_D_ERROR, "QueryVariableInfo: SMM communication buffer size invalid!\n"));\r
625 return EFI_SUCCESS;\r
2445a70e 626 }\r
5e5bb2a9 627 QueryVariableInfo = (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *) SmmVariableFunctionHeader->Data;\r
2445a70e 628\r
8a2d4996 629 Status = VariableServiceQueryVariableInfo (\r
630 QueryVariableInfo->Attributes,\r
631 &QueryVariableInfo->MaximumVariableStorageSize,\r
632 &QueryVariableInfo->RemainingVariableStorageSize,\r
633 &QueryVariableInfo->MaximumVariableSize\r
634 );\r
635 break;\r
636\r
637 case SMM_VARIABLE_FUNCTION_READY_TO_BOOT:\r
876ac395 638 if (AtRuntime()) {\r
639 Status = EFI_UNSUPPORTED;\r
640 break;\r
641 }\r
8a2d4996 642 ReclaimForOS ();\r
643 Status = EFI_SUCCESS;\r
644 break;\r
645 \r
646 case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE:\r
647 mAtRuntime = TRUE;\r
648 Status = EFI_SUCCESS;\r
649 break;\r
650\r
651 case SMM_VARIABLE_FUNCTION_GET_STATISTICS:\r
d00ed85e 652 VariableInfo = (VARIABLE_INFO_ENTRY *) SmmVariableFunctionHeader->Data;\r
3f5c168f 653 InfoSize = *CommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;\r
2445a70e 654\r
655 //\r
656 // Do not need to check SmmVariableFunctionHeader->Data in SMRAM here. \r
657 // It is covered by previous CommBuffer check \r
658 //\r
659 \r
660 if (InternalIsAddressInSmram ((EFI_PHYSICAL_ADDRESS)(UINTN)CommBufferSize, sizeof(UINTN))) {\r
5e5bb2a9 661 DEBUG ((EFI_D_ERROR, "GetStatistics: SMM communication buffer in SMRAM!\n"));\r
2445a70e 662 Status = EFI_ACCESS_DENIED;\r
663 goto EXIT;\r
664 } \r
665\r
8a2d4996 666 Status = SmmVariableGetStatistics (VariableInfo, &InfoSize);\r
3f5c168f 667 *CommBufferSize = InfoSize + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;\r
8a2d4996 668 break;\r
669\r
670 default:\r
8a2d4996 671 Status = EFI_UNSUPPORTED;\r
672 }\r
673\r
2445a70e 674EXIT:\r
675\r
8a2d4996 676 SmmVariableFunctionHeader->ReturnStatus = Status;\r
677\r
678 return EFI_SUCCESS;\r
679}\r
680\r
681\r
682/**\r
683 SMM Fault Tolerant Write protocol notification event handler.\r
684\r
685 Non-Volatile variable write may needs FTW protocol to reclaim when \r
686 writting variable.\r
687 \r
688 @param Protocol Points to the protocol's unique identifier\r
689 @param Interface Points to the interface instance\r
690 @param Handle The handle on which the interface was installed\r
691\r
692 @retval EFI_SUCCESS SmmEventCallback runs successfully\r
693 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.\r
694 \r
695 **/\r
696EFI_STATUS\r
697EFIAPI\r
698SmmFtwNotificationEvent (\r
699 IN CONST EFI_GUID *Protocol,\r
700 IN VOID *Interface,\r
701 IN EFI_HANDLE Handle\r
702 )\r
703{\r
704 EFI_STATUS Status;\r
705 EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol;\r
706 EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol;\r
707 EFI_PHYSICAL_ADDRESS NvStorageVariableBase;\r
708 \r
709 if (mVariableModuleGlobal->FvbInstance != NULL) {\r
710 return EFI_SUCCESS;\r
711 }\r
712\r
713 //\r
714 // Ensure SMM FTW protocol is installed.\r
715 //\r
5c7fa429 716 Status = GetFtwProtocol ((VOID **)&FtwProtocol);\r
8a2d4996 717 if (EFI_ERROR (Status)) {\r
718 return Status;\r
719 }\r
720\r
721 //\r
722 // Find the proper FVB protocol for variable.\r
723 //\r
724 NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageVariableBase64);\r
725 if (NvStorageVariableBase == 0) {\r
726 NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageVariableBase);\r
727 }\r
728 Status = GetFvbInfoByAddress (NvStorageVariableBase, NULL, &FvbProtocol);\r
729 if (EFI_ERROR (Status)) {\r
730 return EFI_NOT_FOUND;\r
731 }\r
732\r
733 mVariableModuleGlobal->FvbInstance = FvbProtocol;\r
734 \r
735 Status = VariableWriteServiceInitialize ();\r
736 ASSERT_EFI_ERROR (Status);\r
737 \r
738 //\r
739 // Notify the variable wrapper driver the variable write service is ready\r
740 //\r
741 Status = gBS->InstallProtocolInterface (\r
742 &mSmmVariableHandle,\r
d00ed85e 743 &gSmmVariableWriteGuid,\r
8a2d4996 744 EFI_NATIVE_INTERFACE,\r
745 NULL\r
746 );\r
747 ASSERT_EFI_ERROR (Status);\r
748 \r
749 return EFI_SUCCESS;\r
750}\r
751\r
752\r
753/**\r
754 Variable Driver main entry point. The Variable driver places the 4 EFI\r
755 runtime services in the EFI System Table and installs arch protocols \r
d00ed85e 756 for variable read and write services being available. It also registers\r
8a2d4996 757 a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.\r
758\r
759 @param[in] ImageHandle The firmware allocated handle for the EFI image. \r
760 @param[in] SystemTable A pointer to the EFI System Table.\r
761 \r
762 @retval EFI_SUCCESS Variable service successfully initialized.\r
763\r
764**/\r
765EFI_STATUS\r
766EFIAPI\r
767VariableServiceInitialize (\r
768 IN EFI_HANDLE ImageHandle,\r
769 IN EFI_SYSTEM_TABLE *SystemTable\r
770 )\r
771{\r
772 EFI_STATUS Status;\r
773 EFI_HANDLE VariableHandle;\r
774 VOID *SmmFtwRegistration;\r
2445a70e 775 EFI_SMM_ACCESS2_PROTOCOL *SmmAccess;\r
776 UINTN Size;\r
777\r
8a2d4996 778 //\r
779 // Variable initialize.\r
780 //\r
781 Status = VariableCommonInitialize ();\r
782 ASSERT_EFI_ERROR (Status);\r
783\r
784 //\r
785 // Install the Smm Variable Protocol on a new handle.\r
786 //\r
787 VariableHandle = NULL;\r
788 Status = gSmst->SmmInstallProtocolInterface (\r
789 &VariableHandle,\r
790 &gEfiSmmVariableProtocolGuid,\r
791 EFI_NATIVE_INTERFACE,\r
792 &gSmmVariable\r
793 );\r
794 ASSERT_EFI_ERROR (Status);\r
795\r
2445a70e 796 //\r
797 // Get SMRAM information\r
798 //\r
799 Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess);\r
800 ASSERT_EFI_ERROR (Status);\r
801\r
802 Size = 0;\r
803 Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);\r
804 ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
805\r
806 Status = gSmst->SmmAllocatePool (\r
807 EfiRuntimeServicesData,\r
808 Size,\r
809 (VOID **)&mSmramRanges\r
810 );\r
811 ASSERT_EFI_ERROR (Status);\r
812\r
813 Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmramRanges);\r
814 ASSERT_EFI_ERROR (Status);\r
815\r
816 mSmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);\r
817\r
5e5bb2a9
SZ
818 mVariableBufferPayloadSize = MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize)) +\r
819 OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - sizeof (VARIABLE_HEADER);\r
820\r
821 Status = gSmst->SmmAllocatePool (\r
822 EfiRuntimeServicesData,\r
823 mVariableBufferPayloadSize,\r
824 (VOID **)&mVariableBufferPayload\r
825 );\r
826 ASSERT_EFI_ERROR (Status);\r
827\r
8a2d4996 828 ///\r
829 /// Register SMM variable SMI handler\r
830 ///\r
831 VariableHandle = NULL;\r
832 Status = gSmst->SmiHandlerRegister (SmmVariableHandler, &gEfiSmmVariableProtocolGuid, &VariableHandle);\r
833 ASSERT_EFI_ERROR (Status);\r
834 \r
835 //\r
836 // Notify the variable wrapper driver the variable service is ready\r
837 //\r
838 Status = SystemTable->BootServices->InstallProtocolInterface (\r
839 &mVariableHandle,\r
840 &gEfiSmmVariableProtocolGuid,\r
841 EFI_NATIVE_INTERFACE,\r
842 &gSmmVariable\r
843 );\r
844 ASSERT_EFI_ERROR (Status);\r
845 \r
846 //\r
847 // Register FtwNotificationEvent () notify function.\r
848 // \r
849 Status = gSmst->SmmRegisterProtocolNotify (\r
850 &gEfiSmmFaultTolerantWriteProtocolGuid,\r
851 SmmFtwNotificationEvent,\r
852 &SmmFtwRegistration\r
853 );\r
854 ASSERT_EFI_ERROR (Status);\r
855\r
856 SmmFtwNotificationEvent (NULL, NULL, NULL);\r
857 \r
858 return EFI_SUCCESS;\r
859}\r
860\r
861\r