1 /** @file
2   This file implements functions related to Config Access Protocols installed by
3   by HII Thunk Modules. These Config access Protocols are used to thunk UEFI Config
4   Access Callback to Framework HII Callback and EFI Variable Set/Get operations.
5 
6 Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution.  The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11 
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 
15 **/
16 
17 #include "HiiDatabase.h"
18 #include "UefiIfrParser.h"
19 
20 BOOLEAN            mHiiPackageListUpdated = FALSE;
21 
22 HII_VENDOR_DEVICE_PATH  mUefiHiiVendorDevicePath = {
23   {
24     {
25       {
26         HARDWARE_DEVICE_PATH,
27         HW_VENDOR_DP,
28         {
29           (UINT8) (sizeof (HII_VENDOR_DEVICE_PATH_NODE)),
30           (UINT8) ((sizeof (HII_VENDOR_DEVICE_PATH_NODE)) >> 8)
31         }
32       },
33       EFI_CALLER_ID_GUID
34     },
35     0,
36     0
37   },
38   {
39     END_DEVICE_PATH_TYPE,
40     END_ENTIRE_DEVICE_PATH_SUBTYPE,
41     {
42       (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL)),
43       (UINT8) ((sizeof (EFI_DEVICE_PATH_PROTOCOL)) >> 8)
44     }
45   }
46 };
47 
48 CONFIG_ACCESS_PRIVATE gConfigAccessPrivateTempate = {
49   CONFIG_ACCESS_PRIVATE_SIGNATURE,
50   {
51     ThunkExtractConfig,
52     ThunkRouteConfig,
53     ThunkCallback
54   }, //ConfigAccessProtocol
55   NULL, //FormCallbackProtocol
56   NULL
57 };
58 
59 /**
60   Get the first EFI_IFR_VARSTORE from the FormSet.
61 
62   @param FormSet                  The Form Set.
63 
64   @retval FORMSET_STORAGE *       Return the first EFI_IFR_VARSTORE.
65   @retval NULL                    If the Form Set does not have EFI_IFR_VARSTORE.
66 **/
67 FORMSET_STORAGE *
GetFirstStorageOfFormSet(IN CONST FORM_BROWSER_FORMSET * FormSet)68 GetFirstStorageOfFormSet (
69   IN CONST FORM_BROWSER_FORMSET * FormSet
70   )
71 {
72   LIST_ENTRY             *StorageList;
73   FORMSET_STORAGE        *Storage;
74 
75   StorageList = GetFirstNode (&FormSet->StorageListHead);
76 
77   while (!IsNull (&FormSet->StorageListHead, StorageList)) {
78     Storage = FORMSET_STORAGE_FROM_LINK (StorageList);
79     if (Storage->Type == EFI_HII_VARSTORE_BUFFER) {
80       return Storage;
81     }
82     StorageList = GetNextNode (&FormSet->StorageListHead, StorageList);
83   }
84 
85   return NULL;
86 }
87 
88 /**
89   Get the FORM_BROWSER_STATEMENT that matches the Question's value.
90 
91   @param FormSet                  The Form Set.
92   @param QuestionId               QuestionId
93 
94   @retval FORM_BROWSER_STATEMENT*   FORM_BROWSER_STATEMENT that match Question's value.
95   @retval NULL                      If the Form Set does not have EFI_IFR_VARSTORE.
96 **/
97 FORM_BROWSER_STATEMENT *
GetStorageFromQuestionId(IN CONST FORM_BROWSER_FORMSET * FormSet,IN EFI_QUESTION_ID QuestionId)98 GetStorageFromQuestionId (
99   IN CONST FORM_BROWSER_FORMSET * FormSet,
100   IN       EFI_QUESTION_ID        QuestionId
101   )
102 {
103   LIST_ENTRY             *FormList;
104   LIST_ENTRY             *StatementList;
105   FORM_BROWSER_FORM      *Form;
106   FORM_BROWSER_STATEMENT *Statement;
107 
108   FormList = GetFirstNode (&FormSet->FormListHead);
109 
110   while (!IsNull (&FormSet->FormListHead, FormList)) {
111     Form = FORM_BROWSER_FORM_FROM_LINK (FormList);
112 
113     StatementList = GetFirstNode (&Form->StatementListHead);
114 
115     while (!IsNull (&Form->StatementListHead, StatementList)) {
116       Statement = FORM_BROWSER_STATEMENT_FROM_LINK (StatementList);
117       if ((QuestionId == Statement->QuestionId) && (Statement->Storage != NULL)) {
118         //
119         // UEFI Question ID is unique in a FormSet.
120         //
121         ASSERT (Statement->Storage->Type == EFI_HII_VARSTORE_BUFFER);
122         return Statement;
123       }
124       StatementList = GetNextNode (&Form->StatementListHead, StatementList);
125     }
126 
127     FormList = GetNextNode (&FormSet->FormListHead, FormList);
128   }
129 
130   return NULL;
131 }
132 
133 /**
134   Get the EFI_IFR_VARSTORE based the <ConfigHdr> string in a <ConfigRequest>
135   or a <ConfigResp> string.
136 
137   @param FormSet                  The Form Set.
138   @param ConfigString             The Configuration String which is defined by UEFI HII.
139 
140   @retval FORMSET_STORAGE *       The EFI_IFR_VARSTORE where the Question's value is stored.
141   @retval NULL                    If the Form Set does not have EFI_IFR_VARSTORE with such ID.
142 **/
143 FORMSET_STORAGE *
GetStorageFromConfigString(IN CONST FORM_BROWSER_FORMSET * FormSet,IN CONST EFI_STRING ConfigString)144 GetStorageFromConfigString (
145   IN CONST FORM_BROWSER_FORMSET *FormSet,
146   IN  CONST EFI_STRING          ConfigString
147   )
148 {
149   LIST_ENTRY             *StorageList;
150   FORMSET_STORAGE        *Storage;
151   CHAR16                 *Name;
152 
153   if (ConfigString == NULL) {
154     return NULL;
155   }
156 
157   StorageList = GetFirstNode (&FormSet->StorageListHead);
158 
159   while (!IsNull (&FormSet->StorageListHead, StorageList)) {
160     Storage = FORMSET_STORAGE_FROM_LINK (StorageList);
161     StorageList = GetNextNode (&FormSet->StorageListHead, StorageList);
162     if (Storage->Type != EFI_HII_VARSTORE_BUFFER) {
163       continue;
164     }
165 
166     if ((Storage->VarStoreId == FormSet->DefaultVarStoreId) && (FormSet->OriginalDefaultVarStoreName != NULL)) {
167       Name = FormSet->OriginalDefaultVarStoreName;
168     } else {
169       Name = Storage->Name;
170     }
171 
172     if (HiiIsConfigHdrMatch (ConfigString, &Storage->Guid, Name)) {
173       return Storage;
174     }
175   }
176 
177   return NULL;
178 }
179 
180 /**
181   This function installs a EFI_CONFIG_ACCESS_PROTOCOL instance for a form package registered
182   by a module using Framework HII Protocol Interfaces.
183 
184   UEFI HII require EFI_HII_CONFIG_ACCESS_PROTOCOL to be installed on a EFI_HANDLE, so
185   that Setup Utility can load the Buffer Storage using this protocol.
186 
187   @param Packages             The Package List.
188   @param ThunkContext         The Thunk Context.
189 
190   @retval  EFI_SUCCESS        The Config Access Protocol is installed successfully.
191   @retval  EFI_OUT_RESOURCE   There is not enough memory.
192 
193 **/
194 EFI_STATUS
InstallDefaultConfigAccessProtocol(IN CONST EFI_HII_PACKAGES * Packages,IN OUT HII_THUNK_CONTEXT * ThunkContext)195 InstallDefaultConfigAccessProtocol (
196   IN  CONST EFI_HII_PACKAGES                    *Packages,
197   IN  OUT   HII_THUNK_CONTEXT                   *ThunkContext
198   )
199 {
200   EFI_STATUS                                  Status;
201   CONFIG_ACCESS_PRIVATE                       *ConfigAccessInstance;
202   HII_VENDOR_DEVICE_PATH                      *HiiVendorPath;
203 
204   ASSERT (ThunkContext->IfrPackageCount != 0);
205 
206   ConfigAccessInstance = AllocateCopyPool (
207                            sizeof (CONFIG_ACCESS_PRIVATE),
208                            &gConfigAccessPrivateTempate
209                            );
210   ASSERT (ConfigAccessInstance != NULL);
211 
212   //
213   // Use memory address as unique ID to distinguish from different device paths
214   // This function may be called multi times by the framework HII driver.
215   //
216   HiiVendorPath = AllocateCopyPool (
217                            sizeof (HII_VENDOR_DEVICE_PATH),
218                            &mUefiHiiVendorDevicePath
219                            );
220   ASSERT (HiiVendorPath != NULL);
221 
222   HiiVendorPath->Node.UniqueId = (UINT64) ((UINTN) HiiVendorPath);
223 
224   Status = gBS->InstallMultipleProtocolInterfaces (
225           &ThunkContext->UefiHiiDriverHandle,
226           &gEfiDevicePathProtocolGuid,
227           HiiVendorPath,
228           &gEfiHiiConfigAccessProtocolGuid,
229           &ConfigAccessInstance->ConfigAccessProtocol,
230           NULL
231           );
232   ASSERT_EFI_ERROR (Status);
233 
234   ConfigAccessInstance->ThunkContext = ThunkContext;
235 
236   return EFI_SUCCESS;
237 }
238 
239 /**
240   This function un-installs the EFI_CONFIG_ACCESS_PROTOCOL instance for a form package registered
241   by a module using Framework HII Protocol Interfaces.
242 
243   ASSERT if no Config Access is found for such pakcage list or failed to uninstall the protocol.
244 
245   @param ThunkContext         The Thunk Context.
246 
247 **/
248 VOID
UninstallDefaultConfigAccessProtocol(IN HII_THUNK_CONTEXT * ThunkContext)249 UninstallDefaultConfigAccessProtocol (
250   IN  HII_THUNK_CONTEXT                   *ThunkContext
251   )
252 {
253   EFI_STATUS                      Status;
254   EFI_HII_CONFIG_ACCESS_PROTOCOL  *ConfigAccess;
255   HII_VENDOR_DEVICE_PATH          *HiiVendorPath;
256 
257   Status = gBS->HandleProtocol (
258                   ThunkContext->UefiHiiDriverHandle,
259                   &gEfiHiiConfigAccessProtocolGuid,
260                   (VOID **) &ConfigAccess
261                   );
262   ASSERT_EFI_ERROR (Status);
263 
264   Status = gBS->HandleProtocol (
265                   ThunkContext->UefiHiiDriverHandle,
266                   &gEfiDevicePathProtocolGuid,
267                   (VOID **) &HiiVendorPath
268                   );
269   ASSERT_EFI_ERROR (Status);
270 
271   Status = gBS->UninstallMultipleProtocolInterfaces (
272                   ThunkContext->UefiHiiDriverHandle,
273                   &gEfiDevicePathProtocolGuid,
274                   HiiVendorPath,
275                   &gEfiHiiConfigAccessProtocolGuid,
276                   ConfigAccess,
277                   NULL
278                   );
279   ASSERT_EFI_ERROR (Status);
280 
281 }
282 
283 
284 /**
285    Wrap the EFI_HII_CONFIG_ACCESS_PROTOCOL.ExtractConfig to a call to EFI_FORM_CALLBACK_PROTOCOL.NvRead.
286 
287    @param BufferStorage         The key with all attributes needed to call EFI_FORM_CALLBACK_PROTOCOL.NvRead.
288    @param FwFormCallBack    The EFI_FORM_CALLBACK_PROTOCOL registered by Framework HII module.
289    @param Data                     The data read.
290    @param DataSize                 The size of data.
291 
292    @retval EFI_STATUS              The status returned by the EFI_FORM_CALLBACK_PROTOCOL.NvWrite.
293    @retval EFI_INVALID_PARAMETER   If the EFI_FORM_CALLBACK_PROTOCOL.NvRead return the size information of the data
294                                    does not match what has been recorded early in he BUFFER_STORAGE_ENTRY.
295  **/
296 EFI_STATUS
CallFormCallBack(IN FORMSET_STORAGE * BufferStorage,IN EFI_FORM_CALLBACK_PROTOCOL * FwFormCallBack,OUT VOID ** Data,OUT UINTN * DataSize)297 CallFormCallBack (
298   IN       FORMSET_STORAGE                            *BufferStorage,
299   IN       EFI_FORM_CALLBACK_PROTOCOL                 *FwFormCallBack,
300   OUT      VOID                                       **Data,
301   OUT      UINTN                                      *DataSize
302   )
303 {
304   EFI_STATUS          Status;
305 
306   *DataSize = 0;
307   *Data     = NULL;
308 
309   Status = FwFormCallBack->NvRead (
310               FwFormCallBack,
311               BufferStorage->Name,
312               &BufferStorage->Guid,
313               NULL,
314               DataSize,
315               *Data
316               );
317   if (Status == EFI_BUFFER_TOO_SMALL) {
318     if (BufferStorage->Size != *DataSize) {
319       ASSERT (FALSE);
320       return EFI_INVALID_PARAMETER;
321     }
322 
323     *Data = AllocateZeroPool (*DataSize);
324     if (*Data == NULL) {
325       return EFI_OUT_OF_RESOURCES;
326     }
327 
328     Status = FwFormCallBack->NvRead (
329                   FwFormCallBack,
330                   BufferStorage->Name,
331                   &BufferStorage->Guid,
332                   NULL,
333                   DataSize,
334                   *Data
335                   );
336   }
337 
338   return Status;
339 }
340 
341 
342 /**
343    Wrap the EFI_HII_CONFIG_ACCESS_PROTOCOL.ExtractConfig to a call to UEFI Variable Get Service.
344 
345    @param BufferStorage        The key with all attributes needed to call a UEFI Variable Get Service.
346    @param Data                    The data read.
347    @param DataSize                The size of data.
348 
349    If the UEFI Variable Get Service return the size information of the data
350    does not match what has been recorded early in he BUFFER_STORAGE_ENTRY.
351    then ASSERT.
352 
353    @retval EFI_STATUS              The status returned by the UEFI Variable Get Service.
354    @retval EFI_INVALID_PARAMETER   If the UEFI Variable Get Service return the size information of the data
355                                    does not match what has been recorded early in he BUFFER_STORAGE_ENTRY.
356  **/
357 EFI_STATUS
GetUefiVariable(IN FORMSET_STORAGE * BufferStorage,OUT VOID ** Data,OUT UINTN * DataSize)358 GetUefiVariable (
359   IN       FORMSET_STORAGE                            *BufferStorage,
360   OUT      VOID                                       **Data,
361   OUT      UINTN                                      *DataSize
362   )
363 {
364   EFI_STATUS          Status;
365 
366   *DataSize = 0;
367   *Data = NULL;
368   Status = gRT->GetVariable (
369               BufferStorage->Name,
370               &BufferStorage->Guid,
371               NULL,
372               DataSize,
373               *Data
374               );
375   if (Status == EFI_BUFFER_TOO_SMALL) {
376 
377     if (BufferStorage->Size != *DataSize) {
378       ASSERT (FALSE);
379       return EFI_INVALID_PARAMETER;
380     }
381 
382     *Data = AllocateZeroPool (*DataSize);
383     if (*Data == NULL) {
384       return EFI_OUT_OF_RESOURCES;
385     }
386 
387     Status = gRT->GetVariable (
388                 BufferStorage->Name,
389                 &BufferStorage->Guid,
390                 NULL,
391                 DataSize,
392                 *Data
393                 );
394   }
395 
396   return Status;
397 }
398 
399 /**
400 
401   This function implement the EFI_HII_CONFIG_ACCESS_PROTOCOL.ExtractConfig
402   so that data can be read from the data storage such as UEFI Variable or module's
403   customized storage exposed by EFI_FRAMEWORK_CALLBACK.
404 
405    @param This        Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL
406    @param Request     A null-terminated Unicode string in <ConfigRequest> format. Note that this
407                       includes the routing information as well as the configurable name / value pairs. It is
408                       invalid for this string to be in <MultiConfigRequest> format.
409 
410    @param Progress    On return, points to a character in the Request string. Points to the string's null
411                       terminator if request was successful. Points to the most recent '&' before the first
412                       failing name / value pair (or the beginning of the string if the failure is in the first
413                       name / value pair) if the request was not successful
414    @param Results     A null-terminated Unicode string in <ConfigAltResp> format which has all
415                       values filled in for the names in the Request string. String to be allocated by the called
416                       function.
417 
418    @retval EFI_INVALID_PARAMETER   If there is no Buffer Storage for this Config Access instance.
419    @retval EFI_SUCCESS             The setting is retrived successfully.
420    @retval !EFI_SUCCESS            The error returned by UEFI Get Variable or Framework Form Callback Nvread.
421  **/
422 EFI_STATUS
423 EFIAPI
ThunkExtractConfig(IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL * This,IN CONST EFI_STRING Request,OUT EFI_STRING * Progress,OUT EFI_STRING * Results)424 ThunkExtractConfig (
425   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
426   IN  CONST EFI_STRING                       Request,
427   OUT EFI_STRING                             *Progress,
428   OUT EFI_STRING                             *Results
429   )
430 {
431   EFI_STATUS                       Status;
432   CONFIG_ACCESS_PRIVATE            *ConfigAccess;
433   FORMSET_STORAGE                  *BufferStorage;
434   VOID                             *Data;
435   UINTN                            DataSize;
436   FORM_BROWSER_FORMSET             *FormSetContext;
437   CHAR16                           *VarStoreName;
438   EFI_STRING                       ConfigRequestHdr;
439   EFI_STRING                       ConfigRequest;
440   UINTN                            Size;
441   BOOLEAN                          AllocatedRequest;
442   LIST_ENTRY                       *StorageList;
443   EFI_STRING                       SingleResult;
444   EFI_STRING                       FinalResults;
445   EFI_STRING                       StrPointer;
446 
447   if (Progress == NULL || Results == NULL) {
448     return EFI_INVALID_PARAMETER;
449   }
450   *Progress = Request;
451 
452   Status         = EFI_SUCCESS;
453   Data           = NULL;
454   StrPointer     = NULL;
455   SingleResult   = NULL;
456   FinalResults   = NULL;
457   ConfigAccess   = CONFIG_ACCESS_PRIVATE_FROM_PROTOCOL (This);
458   FormSetContext = ConfigAccess->ThunkContext->FormSet;
459   if (IsListEmpty (&FormSetContext->StorageListHead)) {
460     //
461     // No VarStorage does exist in this form.
462     //
463     return EFI_NOT_FOUND;
464   }
465   StorageList    = GetFirstNode (&FormSetContext->StorageListHead);
466 
467   do {
468     if (Request != NULL) {
469       BufferStorage = GetStorageFromConfigString (ConfigAccess->ThunkContext->FormSet, Request);
470       if (BufferStorage == NULL) {
471         return EFI_NOT_FOUND;
472       }
473     } else {
474       if (IsNull (&FormSetContext->StorageListHead, StorageList)) {
475         //
476         // No Storage to be extracted into the results.
477         //
478         break;
479       }
480       BufferStorage = FORMSET_STORAGE_FROM_LINK (StorageList);
481       StorageList = GetNextNode (&FormSetContext->StorageListHead, StorageList);
482       if (BufferStorage->Type != EFI_HII_VARSTORE_BUFFER) {
483         //
484         // BufferStorage type should be EFI_HII_VARSTORE_BUFFER
485         //
486         continue;
487       }
488     }
489 
490     VarStoreName     = NULL;
491     ConfigRequestHdr = NULL;
492     ConfigRequest    = NULL;
493     Size             = 0;
494     AllocatedRequest = FALSE;
495 
496     if (ConfigAccess->ThunkContext->NvMapOverride == NULL) {
497       //
498       // NvMapOverride is not used. Get the Storage data from EFI Variable or Framework Form Callback.
499       //
500       if (ConfigAccess->FormCallbackProtocol == NULL ||
501           ConfigAccess->FormCallbackProtocol->NvRead == NULL) {
502         Status = GetUefiVariable (
503                    BufferStorage,
504                    &Data,
505                    &DataSize
506                    );
507       } else {
508         Status = CallFormCallBack (
509                    BufferStorage,
510                    ConfigAccess->FormCallbackProtocol,
511                     &Data,
512                     &DataSize
513                    );
514       }
515     } else {
516       //
517       // Use the NvMapOverride.
518       //
519       DataSize = BufferStorage->Size;
520       Data = AllocateCopyPool (DataSize, ConfigAccess->ThunkContext->NvMapOverride);
521 
522       if (Data != NULL) {
523         Status = EFI_SUCCESS;
524       } else {
525         Status = EFI_OUT_OF_RESOURCES;
526       }
527     }
528 
529     if (!EFI_ERROR (Status)) {
530       ConfigRequest = Request;
531       if (Request == NULL || (StrStr (Request, L"OFFSET") == NULL)) {
532         //
533         // Request is without any request element, construct full request string.
534         //
535 
536         if ((BufferStorage->VarStoreId == FormSetContext->DefaultVarStoreId) && (FormSetContext->OriginalDefaultVarStoreName != NULL)) {
537           VarStoreName = FormSetContext->OriginalDefaultVarStoreName;
538         } else {
539           VarStoreName = BufferStorage->Name;
540         }
541 
542         //
543         // First Set ConfigRequestHdr string.
544         //
545         ConfigRequestHdr = HiiConstructConfigHdr (&BufferStorage->Guid, VarStoreName, ConfigAccess->ThunkContext->UefiHiiDriverHandle);
546         ASSERT (ConfigRequestHdr != NULL);
547 
548         //
549         // Allocate and fill a buffer large enough to hold the <ConfigHdr> template
550         // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator
551         //
552         Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
553         ConfigRequest = AllocateZeroPool (Size);
554         ASSERT (ConfigRequest != NULL);
555         AllocatedRequest = TRUE;
556         UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)DataSize);
557         FreePool (ConfigRequestHdr);
558       }
559       Status = mHiiConfigRoutingProtocol->BlockToConfig (
560                                               mHiiConfigRoutingProtocol,
561                                               ConfigRequest,
562                                               Data,
563                                               DataSize,
564                                               &SingleResult,
565                                               Progress
566                                               );
567       //
568       // Free the allocated config request string.
569       //
570       if (AllocatedRequest) {
571         FreePool (ConfigRequest);
572         ConfigRequest = NULL;
573       }
574     }
575     //
576     // Free the allocated Data
577     //
578     if (Data != NULL) {
579       FreePool (Data);
580     }
581     //
582     // Directly return when meet with error
583     //
584     if (EFI_ERROR (Status)) {
585       break;
586     }
587     //
588     // Merge result into the final results.
589     //
590     if (FinalResults == NULL) {
591       FinalResults = SingleResult;
592       SingleResult = NULL;
593     } else {
594       Size = StrLen (FinalResults);
595       Size = Size + 1;
596       Size = Size + StrLen (SingleResult) + 1;
597       StrPointer = AllocateZeroPool (Size * sizeof (CHAR16));
598       ASSERT (StrPointer != NULL);
599       StrCpy (StrPointer, FinalResults);
600       FreePool (FinalResults);
601       FinalResults = StrPointer;
602       StrPointer  = StrPointer + StrLen (StrPointer);
603       *StrPointer = L'&';
604       StrCpy (StrPointer + 1, SingleResult);
605       FreePool (SingleResult);
606     }
607   } while (Request == NULL);
608 
609   if (!EFI_ERROR (Status)) {
610     *Results = FinalResults;
611   } else {
612     if (FinalResults != NULL) {
613       FreePool (FinalResults);
614     }
615   }
616   //
617   // Set Progress string to the original request string.
618   //
619   if (Request == NULL) {
620     *Progress = NULL;
621   } else if (StrStr (Request, L"OFFSET") == NULL) {
622     *Progress = Request + StrLen (Request);
623   }
624 
625   return Status;
626 }
627 
628 /**
629   This function implement the EFI_HII_CONFIG_ACCESS_PROTOCOL.RouteConfig
630   so that data can be written to the data storage such as UEFI Variable or module's
631   customized storage exposed by EFI_FRAMEWORK_CALLBACK.
632 
633    @param This             Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL
634    @param Configuration    A null-terminated Unicode string in <ConfigResp> format.
635    @param Progress         A pointer to a string filled in with the offset of the most recent '&' before the first
636                            failing name / value pair (or the beginning of the string if the failure is in the first
637                            name / value pair) or the terminating NULL if all was successful.
638 
639    @retval EFI_INVALID_PARAMETER   If there is no Buffer Storage for this Config Access instance.
640    @retval EFI_SUCCESS             The setting is saved successfully.
641    @retval !EFI_SUCCESS            The error returned by UEFI Set Variable or Framework Form Callback Nvwrite.
642 **/
643 EFI_STATUS
644 EFIAPI
ThunkRouteConfig(IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL * This,IN CONST EFI_STRING Configuration,OUT EFI_STRING * Progress)645 ThunkRouteConfig (
646   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
647   IN  CONST EFI_STRING                       Configuration,
648   OUT EFI_STRING                             *Progress
649   )
650 {
651   EFI_STATUS                                  Status;
652   CONFIG_ACCESS_PRIVATE                       *ConfigAccess;
653   FORMSET_STORAGE                             *BufferStorage;
654   VOID                                        *Data;
655   UINTN                                       DataSize;
656   UINTN                                       DataSize2;
657   BOOLEAN                                     ResetRequired;
658   BOOLEAN                                     DataAllocated;
659 
660   if (Configuration == NULL) {
661     return EFI_INVALID_PARAMETER;
662   }
663 
664   Data = NULL;
665   ConfigAccess = CONFIG_ACCESS_PRIVATE_FROM_PROTOCOL (This);
666 
667   BufferStorage = GetStorageFromConfigString (ConfigAccess->ThunkContext->FormSet, Configuration);
668 
669   if (BufferStorage == NULL) {
670     *Progress = Configuration;
671     return EFI_NOT_FOUND;
672   }
673 
674   DataSize2     = BufferStorage->Size;
675   if (ConfigAccess->ThunkContext->NvMapOverride == NULL) {
676     DataAllocated = TRUE;
677     if (ConfigAccess->FormCallbackProtocol == NULL ||
678         ConfigAccess->FormCallbackProtocol->NvRead == NULL) {
679       Status = GetUefiVariable (
680                  BufferStorage,
681                  &Data,
682                  &DataSize
683                  );
684     } else {
685       Status = CallFormCallBack (
686                  BufferStorage,
687                  ConfigAccess->FormCallbackProtocol,
688                   &Data,
689                   &DataSize
690                  );
691     }
692   } else {
693     //
694     // ConfigToBlock will convert the Config String and update the NvMapOverride accordingly.
695     //
696     Status = EFI_SUCCESS;
697     Data = ConfigAccess->ThunkContext->NvMapOverride;
698     DataSize      = DataSize2;
699     DataAllocated = FALSE;
700   }
701   if (EFI_ERROR (Status) || (DataSize2 != DataSize)) {
702     if (Data == NULL) {
703       Data = AllocateZeroPool (DataSize2);
704     }
705   }
706 
707   DataSize = DataSize2;
708   Status = mHiiConfigRoutingProtocol->ConfigToBlock (
709                                           mHiiConfigRoutingProtocol,
710                                           Configuration,
711                                           Data,
712                                           &DataSize,
713                                           Progress
714                                           );
715   if (EFI_ERROR (Status)) {
716     goto Done;
717   }
718 
719   if (ConfigAccess->ThunkContext->NvMapOverride == NULL) {
720     if (ConfigAccess->FormCallbackProtocol == NULL ||
721         ConfigAccess->FormCallbackProtocol->NvWrite == NULL) {
722       Status = gRT->SetVariable (
723                     BufferStorage->Name,
724                     &BufferStorage->Guid,
725                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
726                     DataSize2,
727                     Data
728                     );
729     } else {
730       Status = ConfigAccess->FormCallbackProtocol->NvWrite (
731                     ConfigAccess->FormCallbackProtocol,
732                     BufferStorage->Name,
733                     &BufferStorage->Guid,
734                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
735                     DataSize2,
736                     Data,
737                     &ResetRequired
738                     );
739     }
740   }
741 
742 Done:
743   if (DataAllocated && (Data != NULL)) {
744     FreePool (Data);
745   }
746 
747   return Status;
748 }
749 
750 /**
751   Build the EFI_IFR_DATA_ARRAY which will be used to pass to
752   EFI_FORM_CALLBACK_PROTOCOL.Callback. Check definition of EFI_IFR_DATA_ARRAY
753   for details.
754 
755   ASSERT if the Question Type is not EFI_IFR_TYPE_NUM_SIZE_* or EFI_IFR_TYPE_STRING.
756 
757    @param ConfigAccess     Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL
758    @param QuestionId       The Question ID.
759    @param Type             The Question Type.
760    @param Value            The Question Value.
761    @param NvMapAllocated   On output indicates if a buffer is allocated for NvMap.
762 
763    @return A pointer to EFI_IFR_DATA_ARRAY. The caller must free this buffer allocated.
764 **/
765 EFI_IFR_DATA_ARRAY *
CreateIfrDataArray(IN CONFIG_ACCESS_PRIVATE * ConfigAccess,IN EFI_QUESTION_ID QuestionId,IN UINT8 Type,IN EFI_IFR_TYPE_VALUE * Value,OUT BOOLEAN * NvMapAllocated)766 CreateIfrDataArray (
767   IN    CONFIG_ACCESS_PRIVATE         *ConfigAccess,
768   IN    EFI_QUESTION_ID               QuestionId,
769   IN    UINT8                         Type,
770   IN    EFI_IFR_TYPE_VALUE            *Value,
771   OUT   BOOLEAN                       *NvMapAllocated
772   )
773 {
774   EFI_IFR_DATA_ARRAY                *IfrDataArray;
775   EFI_IFR_DATA_ENTRY                *IfrDataEntry;
776   UINTN                             BrowserDataSize;
777   FORMSET_STORAGE                   *BufferStorage;
778   UINTN                             Size;
779   EFI_STRING                        String;
780   FORM_BROWSER_STATEMENT            *Statement;
781 
782   *NvMapAllocated = FALSE;
783 
784   String = NULL;
785 
786   switch (Type) {
787     case EFI_IFR_TYPE_NUM_SIZE_8:
788     case EFI_IFR_TYPE_NUM_SIZE_16:
789     case EFI_IFR_TYPE_NUM_SIZE_32:
790     case EFI_IFR_TYPE_NUM_SIZE_64:
791     case EFI_IFR_TYPE_BOOLEAN:
792       Size = sizeof (*Value);
793       break;
794 
795     case EFI_IFR_TYPE_STRING:
796       if (Value->string == 0) {
797         Size = 0;
798       } else {
799         String = HiiGetString (ConfigAccess->ThunkContext->UefiHiiHandle, Value->string, NULL);
800         ASSERT (String != NULL);
801 
802         Size = StrSize (String);
803       }
804       break;
805 
806     case EFI_IFR_TYPE_ACTION:
807     case EFI_IFR_TYPE_UNDEFINED:
808       Size = 0;
809       break;
810 
811     default:
812       ASSERT (FALSE);
813       Size = 0;
814       break;
815   }
816 
817   IfrDataArray = AllocateZeroPool (sizeof (EFI_IFR_DATA_ARRAY) + sizeof (EFI_IFR_DATA_ENTRY) + Size);
818   ASSERT (IfrDataArray != NULL);
819   IfrDataArray->EntryCount = 1;
820   IfrDataEntry             = (EFI_IFR_DATA_ENTRY *) (IfrDataArray + 1);
821 
822   Statement = GetStorageFromQuestionId (ConfigAccess->ThunkContext->FormSet, QuestionId);
823 
824   if (Statement == NULL || Statement->Storage == NULL) {
825     //
826     // The QuestionId is not associated with a Buffer Storage.
827     // Try to get the first Buffer Storage then.
828     //
829     BufferStorage = GetFirstStorageOfFormSet (ConfigAccess->ThunkContext->FormSet);
830   } else {
831     BufferStorage        = Statement->Storage;
832     IfrDataEntry->OpCode = Statement->Operand;
833   }
834 
835   if (BufferStorage != NULL) {
836     BrowserDataSize      = BufferStorage->Size;
837     IfrDataEntry->Length = (UINT8) (sizeof (EFI_IFR_DATA_ENTRY) + Size);
838 
839     if (ConfigAccess->ThunkContext->NvMapOverride == NULL) {
840       *NvMapAllocated = TRUE;
841       IfrDataArray->NvRamMap = AllocateZeroPool (BrowserDataSize);
842     } else {
843       *NvMapAllocated = FALSE;
844       IfrDataArray->NvRamMap = ConfigAccess->ThunkContext->NvMapOverride;
845     }
846 
847     ASSERT (HiiGetBrowserData (&BufferStorage->Guid, BufferStorage->Name, BrowserDataSize, (UINT8 *) IfrDataArray->NvRamMap));
848 
849     switch (Type) {
850       case EFI_IFR_TYPE_NUM_SIZE_8:
851       case EFI_IFR_TYPE_NUM_SIZE_16:
852       case EFI_IFR_TYPE_NUM_SIZE_32:
853       case EFI_IFR_TYPE_NUM_SIZE_64:
854       case EFI_IFR_TYPE_BOOLEAN:
855         CopyMem (&IfrDataEntry->Data, &(Value->u8), sizeof (*Value));
856         break;
857 
858       case EFI_IFR_TYPE_STRING:
859         if (Size != 0) {
860           ASSERT (String != NULL);
861           StrCpy ((CHAR16 *) &IfrDataEntry->Data, String);
862           FreePool (String);
863         }
864         break;
865 
866       case EFI_IFR_TYPE_ACTION:
867       case EFI_IFR_TYPE_UNDEFINED:
868         break;
869 
870       default:
871         ASSERT (FALSE);
872         break;
873     }
874 
875     //
876     // Need to fiil in the information for the rest of field for EFI_IFR_DATA_ENTRY.
877     // It seems that no implementation is found to use other fields. Leave them uninitialized for now.
878     //
879     //UINT8   OpCode;           // Likely a string, numeric, or one-of
880     //UINT8   Length;           // Length of the EFI_IFR_DATA_ENTRY packet
881     //UINT16  Flags;            // Flags settings to determine what behavior is desired from the browser after the callback
882     //VOID    *Data;            // The data in the form based on the op-code type - this is not a pointer to the data, the data follows immediately
883     // If the OpCode is a OneOf or Numeric type - Data is a UINT16 value
884     // If the OpCode is a String type - Data is a CHAR16[x] type
885     // If the OpCode is a Checkbox type - Data is a UINT8 value
886     // If the OpCode is a NV Access type - Data is a FRAMEWORK_EFI_IFR_NV_DATA structure
887   }
888 
889   return IfrDataArray;
890 }
891 
892 /**
893   If a NvMapOverride is passed in to EFI_FORM_BROWSER_PROTOCOL.SendForm, the Form Browser
894   needs to be informed when data changed in NvMapOverride. This function will invoke
895   SetBrowserData () to set internal data of Form Browser.
896 
897   @param  ConfigAccess   The Config Access Private Context.
898   @param  QuestionId     The Question Id that invokes the callback.
899 
900 
901 **/
902 VOID
SyncBrowserDataForNvMapOverride(IN CONST CONFIG_ACCESS_PRIVATE * ConfigAccess,IN EFI_QUESTION_ID QuestionId)903 SyncBrowserDataForNvMapOverride (
904   IN    CONST CONFIG_ACCESS_PRIVATE         *ConfigAccess,
905   IN          EFI_QUESTION_ID               QuestionId
906   )
907 {
908   FORMSET_STORAGE   *BufferStorage;
909   BOOLEAN           CheckFlag;
910   UINTN             BrowserDataSize;
911   FORM_BROWSER_STATEMENT *Statement;
912 
913   if (ConfigAccess->ThunkContext->NvMapOverride != NULL) {
914 
915     Statement = GetStorageFromQuestionId (ConfigAccess->ThunkContext->FormSet, QuestionId);
916 
917     if (Statement == NULL || Statement->Storage == NULL) {
918       //
919       // QuestionId is a statement without Storage.
920       // 1) It is a Goto.
921       //
922       //
923       BufferStorage = GetFirstStorageOfFormSet (ConfigAccess->ThunkContext->FormSet);
924     } else {
925       BufferStorage = Statement->Storage;
926     }
927 
928     //
929     // If NvMapOverride is not NULL, this Form must have at least one Buffer Type Variable Storage.
930     //
931     ASSERT (BufferStorage != NULL);
932 
933     BrowserDataSize = BufferStorage->Size;
934 
935     CheckFlag = HiiSetBrowserData (&BufferStorage->Guid, BufferStorage->Name, BrowserDataSize, ConfigAccess->ThunkContext->NvMapOverride, NULL);
936     ASSERT (CheckFlag);
937   }
938 
939 }
940 
941 /**
942   Free up resource allocated for a EFI_IFR_DATA_ARRAY by CreateIfrDataArray ().
943 
944   @param Array              The EFI_IFR_DATA_ARRAY allocated.
945   @param NvMapAllocated     If the NvRamMap is allocated for EFI_IFR_DATA_ARRAY.
946 
947 **/
948 VOID
DestroyIfrDataArray(IN EFI_IFR_DATA_ARRAY * Array,IN BOOLEAN NvMapAllocated)949 DestroyIfrDataArray (
950   IN  EFI_IFR_DATA_ARRAY           *Array,
951   IN  BOOLEAN                      NvMapAllocated
952   )
953 {
954   if (Array != NULL) {
955     if (NvMapAllocated) {
956       FreePool (Array->NvRamMap);
957     }
958 
959     FreePool (Array);
960   }
961 }
962 
963 /**
964   Get the ONE_OF_OPTION_MAP_ENTRY for a QuestionId that invokes the
965   EFI_FORM_CALLBACK_PROTOCOL.Callback. The information is needed as
966   the callback mechanism for EFI_IFR_ONE_OF_OPTION is changed from
967   EFI_IFR_ONE_OF_OPTION in Framework IFR. Check EFI_IFR_GUID_OPTIONKEY
968   for detailed information.
969 
970   @param ThunkContext   The Thunk Context.
971   @param QuestionId     The Question Id.
972   @param Type           The Question Type.
973   @param Value          The One Of Option's value.
974 
975   @return The ONE_OF_OPTION_MAP_ENTRY found.
976   @retval NULL If no entry is found.
977 **/
978 ONE_OF_OPTION_MAP_ENTRY *
GetOneOfOptionMapEntry(IN HII_THUNK_CONTEXT * ThunkContext,IN EFI_QUESTION_ID QuestionId,IN UINT8 Type,IN EFI_IFR_TYPE_VALUE * Value)979 GetOneOfOptionMapEntry (
980   IN  HII_THUNK_CONTEXT              *ThunkContext,
981   IN  EFI_QUESTION_ID                QuestionId,
982   IN  UINT8                          Type,
983   IN  EFI_IFR_TYPE_VALUE             *Value
984   )
985 {
986   LIST_ENTRY              *Link;
987   LIST_ENTRY              *Link2;
988   ONE_OF_OPTION_MAP_ENTRY *OneOfOptionMapEntry;
989   ONE_OF_OPTION_MAP       *OneOfOptionMap;
990   FORM_BROWSER_FORMSET    *FormSet;
991 
992   FormSet = ThunkContext->FormSet;
993 
994   Link = GetFirstNode (&FormSet->OneOfOptionMapListHead);
995 
996   while (!IsNull (&FormSet->OneOfOptionMapListHead, Link)) {
997     OneOfOptionMap = ONE_OF_OPTION_MAP_FROM_LINK(Link);
998     if (OneOfOptionMap->QuestionId == QuestionId) {
999       ASSERT (OneOfOptionMap->ValueType == Type);
1000 
1001       Link2 = GetFirstNode (&OneOfOptionMap->OneOfOptionMapEntryListHead);
1002 
1003       while (!IsNull (&OneOfOptionMap->OneOfOptionMapEntryListHead, Link2)) {
1004         OneOfOptionMapEntry = ONE_OF_OPTION_MAP_ENTRY_FROM_LINK (Link2);
1005 
1006         if (CompareMem (Value, &OneOfOptionMapEntry->Value, sizeof (EFI_IFR_TYPE_VALUE)) == 0) {
1007           return OneOfOptionMapEntry;
1008         }
1009 
1010         Link2 = GetNextNode (&OneOfOptionMap->OneOfOptionMapEntryListHead, Link2);
1011       }
1012     }
1013 
1014     Link = GetNextNode (&FormSet->OneOfOptionMapListHead, Link);
1015   }
1016 
1017 
1018   return NULL;
1019 }
1020 
1021 /**
1022   Functions which are registered to receive notification of
1023   database events have this prototype. The actual event is encoded
1024   in NotifyType. The following table describes how PackageType,
1025   PackageGuid, Handle, and Package are used for each of the
1026   notification types.
1027 
1028   If any Pakcage List in database is updated, mHiiPackageListUpdated
1029   will be set. If mHiiPackageListUpdated is set, Framework ThunkCallback()
1030   will force the UEFI Setup Browser to save the uncommitted data. This
1031   is needed as Framework's Callback function may dynamically update
1032   opcode in a Package List. UEFI Setup Browser will quit itself and reparse
1033   the Package List's IFR and display it. UEFI Config Access's implementation
1034   is required to save the modified (SetBrowserData or directly save the data
1035   to NV storage). But Framework HII Modules is not aware of this rule. Therefore,
1036   we will enforce the rule in ThunkCallback (). The side effect of force saving
1037   of NV data is the NV flag in browser may not flag a update as data has already
1038   been saved to NV storage.
1039 
1040   @param PackageType  Package type of the notification.
1041 
1042   @param PackageGuid  If PackageType is
1043                       EFI_HII_PACKAGE_TYPE_GUID, then this is
1044                       the pointer to the GUID from the Guid
1045                       field of EFI_HII_PACKAGE_GUID_HEADER.
1046                       Otherwise, it must be NULL.
1047 
1048   @param Package  Points to the package referred to by the
1049                   notification Handle The handle of the package
1050                   list which contains the specified package.
1051 
1052   @param Handle       The HII handle.
1053 
1054   @param NotifyType   The type of change concerning the
1055                       database. See
1056                       EFI_HII_DATABASE_NOTIFY_TYPE.
1057 
1058 **/
1059 EFI_STATUS
1060 EFIAPI
FormUpdateNotify(IN UINT8 PackageType,IN CONST EFI_GUID * PackageGuid,IN CONST EFI_HII_PACKAGE_HEADER * Package,IN EFI_HII_HANDLE Handle,IN EFI_HII_DATABASE_NOTIFY_TYPE NotifyType)1061 FormUpdateNotify (
1062   IN UINT8                              PackageType,
1063   IN CONST EFI_GUID                     *PackageGuid,
1064   IN CONST EFI_HII_PACKAGE_HEADER       *Package,
1065   IN EFI_HII_HANDLE                     Handle,
1066   IN EFI_HII_DATABASE_NOTIFY_TYPE       NotifyType
1067   )
1068 {
1069   mHiiPackageListUpdated = TRUE;
1070 
1071   return EFI_SUCCESS;
1072 }
1073 
1074 /**
1075   Wrap the EFI_HII_CONFIG_ACCESS_PROTOCOL.CallBack to EFI_FORM_CALLBACK_PROTOCOL.Callback. Therefor,
1076   the framework HII module willl do no porting and work with a UEFI HII SetupBrowser.
1077 
1078    @param This                      Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
1079    @param Action                    Specifies the type of action taken by the browser. See EFI_BROWSER_ACTION_x.
1080    @param QuestionId                A unique value which is sent to the original exporting driver so that it can identify the
1081                                     type of data to expect. The format of the data tends to vary based on the opcode that
1082                                     generated the callback.
1083    @param Type                      The type of value for the question. See EFI_IFR_TYPE_x in
1084                                     EFI_IFR_ONE_OF_OPTION.
1085    @param Value                     A pointer to the data being sent to the original exporting driver. The type is specified
1086                                     by Type. Type EFI_IFR_TYPE_VALUE is defined in
1087                                     EFI_IFR_ONE_OF_OPTION.
1088    @param ActionRequest             On return, points to the action requested by the callback function. Type
1089                                     EFI_BROWSER_ACTION_REQUEST is specified in SendForm() in the Form
1090                                     Browser Protocol.
1091 
1092    @retval EFI_UNSUPPORTED        If the Framework HII module does not register Callback although it specify the opcode under
1093                                   focuse to be INTERRACTIVE.
1094    @retval EFI_SUCCESS            The callback complete successfully.
1095    @retval !EFI_SUCCESS           The error code returned by EFI_FORM_CALLBACK_PROTOCOL.Callback.
1096 
1097  **/
1098 EFI_STATUS
1099 EFIAPI
ThunkCallback(IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL * This,IN EFI_BROWSER_ACTION Action,IN EFI_QUESTION_ID QuestionId,IN UINT8 Type,IN EFI_IFR_TYPE_VALUE * Value,OUT EFI_BROWSER_ACTION_REQUEST * ActionRequest)1100 ThunkCallback (
1101   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
1102   IN  EFI_BROWSER_ACTION                     Action,
1103   IN  EFI_QUESTION_ID                        QuestionId,
1104   IN  UINT8                                  Type,
1105   IN  EFI_IFR_TYPE_VALUE                     *Value,
1106   OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest
1107   )
1108 {
1109   EFI_STATUS                                  Status;
1110   CONFIG_ACCESS_PRIVATE                       *ConfigAccess;
1111   EFI_FORM_CALLBACK_PROTOCOL                  *FormCallbackProtocol;
1112   EFI_HII_CALLBACK_PACKET                     *Packet;
1113   EFI_IFR_DATA_ARRAY                          *Data;
1114   EFI_IFR_DATA_ENTRY                          *DataEntry;
1115   UINT16                                      KeyValue;
1116   ONE_OF_OPTION_MAP_ENTRY                     *OneOfOptionMapEntry;
1117   EFI_HANDLE                                  NotifyHandle;
1118   EFI_INPUT_KEY                               Key;
1119   BOOLEAN                                     NvMapAllocated;
1120 
1121   if (Action == EFI_BROWSER_ACTION_CHANGING) {
1122     ASSERT (This != NULL);
1123     ASSERT (Value != NULL);
1124     ASSERT (ActionRequest != NULL);
1125 
1126     *ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE;
1127 
1128     ConfigAccess = CONFIG_ACCESS_PRIVATE_FROM_PROTOCOL (This);
1129 
1130     FormCallbackProtocol = ConfigAccess->FormCallbackProtocol;
1131     if (FormCallbackProtocol == NULL) {
1132       ASSERT (FALSE);
1133       return EFI_UNSUPPORTED;
1134     }
1135 
1136     //
1137     // Check if the QuestionId match a OneOfOption.
1138     //
1139     OneOfOptionMapEntry = GetOneOfOptionMapEntry (ConfigAccess->ThunkContext, QuestionId, Type, Value);
1140 
1141     if (OneOfOptionMapEntry == NULL) {
1142       //
1143       // This is not a One-Of-Option opcode. QuestionId is the KeyValue
1144       //
1145       KeyValue = QuestionId;
1146     } else {
1147       //
1148       // Otherwise, use the original Key specified in One Of Option in the Framework VFR syntax.
1149       //
1150       KeyValue = OneOfOptionMapEntry->FwKey;
1151     }
1152 
1153     //
1154     // Build the EFI_IFR_DATA_ARRAY
1155     //
1156     Data = CreateIfrDataArray (ConfigAccess, QuestionId, Type, Value, &NvMapAllocated);
1157 
1158     Status = mHiiDatabase->RegisterPackageNotify (
1159                              mHiiDatabase,
1160                              EFI_HII_PACKAGE_FORMS,
1161                              NULL,
1162                              FormUpdateNotify,
1163                              EFI_HII_DATABASE_NOTIFY_REMOVE_PACK,
1164                              &NotifyHandle
1165                              );
1166     //
1167     //Call the Framework Callback function.
1168     //
1169     Packet =  NULL;
1170     Status =  FormCallbackProtocol->Callback (
1171                 FormCallbackProtocol,
1172                 KeyValue,
1173                 Data,
1174                 &Packet
1175                 );
1176     SyncBrowserDataForNvMapOverride (ConfigAccess, QuestionId);
1177 
1178     //
1179     // Callback require browser to perform action
1180     //
1181     if (EFI_ERROR (Status)) {
1182       if (Packet != NULL) {
1183         do {
1184           CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, Packet->String, NULL);
1185         } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
1186       }
1187       //
1188       // Error Code in Status is discarded.
1189       //
1190     } else {
1191       if (Packet != NULL) {
1192           if (Packet->DataArray.EntryCount  == 1 && Packet->DataArray.NvRamMap == NULL) {
1193             DataEntry = (EFI_IFR_DATA_ENTRY *) ((UINT8 *) Packet + sizeof (EFI_IFR_DATA_ARRAY));
1194             if ((DataEntry->Flags & EXIT_REQUIRED) == EXIT_REQUIRED) {
1195                 *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
1196             }
1197 
1198             if ((DataEntry->Flags & SAVE_REQUIRED) == SAVE_REQUIRED) {
1199               Status = ConfigAccess->ConfigAccessProtocol.RouteConfig (
1200                                         &ConfigAccess->ConfigAccessProtocol,
1201                                         NULL,
1202                                         NULL
1203                                         );
1204             }
1205           }
1206           FreePool (Packet);
1207       }
1208     }
1209 
1210     //
1211     // Unregister notify for Form package update
1212     //
1213     Status = mHiiDatabase->UnregisterPackageNotify (
1214                              mHiiDatabase,
1215                              NotifyHandle
1216                              );
1217     //
1218     // UEFI SetupBrowser behaves differently with Framework SetupBrowser when call back function
1219     // update any forms in HII database. UEFI SetupBrowser will re-parse the displaying form package and load
1220     // the values from variable storages. Framework SetupBrowser will only re-parse the displaying form packages.
1221     // To make sure customer's previous changes is saved and the changing question behaves as expected, we
1222     // issue a EFI_BROWSER_ACTION_REQUEST_SUBMIT to ask UEFI SetupBrowser to save the changes proceed to re-parse
1223     // the form and load all the variable storages.
1224     //
1225     if (*ActionRequest == EFI_BROWSER_ACTION_REQUEST_NONE && mHiiPackageListUpdated) {
1226       mHiiPackageListUpdated= FALSE;
1227       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
1228     } else {
1229       if (ConfigAccess->ThunkContext->FormSet->SubClass == EFI_FRONT_PAGE_SUBCLASS ||
1230           ConfigAccess->ThunkContext->FormSet->SubClass == EFI_SINGLE_USE_SUBCLASS) {
1231         *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
1232       }
1233     }
1234 
1235     //
1236     // Clean up.
1237     //
1238     DestroyIfrDataArray (Data, NvMapAllocated);
1239 
1240     return Status;
1241   }
1242 
1243   //
1244   // All other action return unsupported.
1245   //
1246   return EFI_UNSUPPORTED;
1247 }
1248 
1249