Initial import.
[people/mcb30/edk2.git] / edk2 / EdkModulePkg / Library / EdkIfrSupportLib / IfrCommon.c
1 /*++\r
2 Copyright (c) 2006, Intel Corporation                                                         \r
3 All rights reserved. This program and the accompanying materials                          \r
4 are licensed and made available under the terms and conditions of the BSD License         \r
5 which accompanies this distribution.  The full text of the license may be found at        \r
6 http://opensource.org/licenses/bsd-license.php                                            \r
7                                                                                           \r
8 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
9 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             \r
10 \r
11 Module Name:\r
12   IfrCommon.c\r
13 \r
14 Abstract:\r
15 \r
16   Common Library Routines to assist in IFR creation on-the-fly\r
17 \r
18 Revision History:\r
19 \r
20 --*/\r
21 \r
22 EFI_STATUS\r
23 IfrLibConstruct (\r
24   IN EFI_HANDLE        ImageHandle,\r
25   IN EFI_SYSTEM_TABLE  *SystemTable\r
26   )\r
27 {\r
28   return EFI_SUCCESS;\r
29 }\r
30 \r
31 EFI_STATUS\r
32 GetCurrentLanguage (\r
33   OUT     CHAR16              *Lang\r
34   )\r
35 /*++\r
36 \r
37 Routine Description:\r
38 \r
39   Determine what is the current language setting\r
40   \r
41 Arguments:\r
42   \r
43   Lang      - Pointer of system language\r
44   \r
45 Returns: \r
46 \r
47   Status code\r
48 \r
49 --*/\r
50 {\r
51   EFI_STATUS  Status;\r
52   UINTN       Size;\r
53   UINTN       Index;\r
54   CHAR8       Language[4];\r
55 \r
56   //\r
57   // Getting the system language and placing it into our Global Data\r
58   //\r
59   Size = sizeof (Language);\r
60 \r
61   Status = gRT->GetVariable (\r
62                   (CHAR16 *) L"Lang",\r
63                   &gEfiGlobalVariableGuid,\r
64                   NULL,\r
65                   &Size,\r
66                   Language\r
67                   );\r
68 \r
69   if (EFI_ERROR (Status)) {\r
70     AsciiStrCpy (Language, "eng");\r
71   }\r
72 \r
73   for (Index = 0; Language[Index] != 0; Index++) {\r
74     //\r
75     // Bitwise AND ascii value with 0xDF yields an uppercase value.\r
76     // Sign extend into a unicode value\r
77     //\r
78     Lang[Index] = (CHAR16) (Language[Index] & 0xDF);\r
79   }\r
80 \r
81   //\r
82   // Null-terminate the value\r
83   //\r
84   Lang[3] = (CHAR16) 0;\r
85 \r
86   return Status;\r
87 }\r
88 \r
89 \r
90 EFI_STATUS\r
91 AddString (\r
92   IN      VOID                *StringBuffer,\r
93   IN      CHAR16              *Language,\r
94   IN      CHAR16              *String,\r
95   IN OUT  STRING_REF          *StringToken\r
96   )\r
97 /*++\r
98 \r
99 Routine Description:\r
100 \r
101   Add a string to the incoming buffer and return the token and offset data\r
102   \r
103 Arguments:\r
104   \r
105   StringBuffer      - The incoming buffer\r
106   \r
107   Language          - Currrent language\r
108   \r
109   String            - The string to be added\r
110   \r
111   StringToken       - The index where the string placed\r
112   \r
113 Returns: \r
114 \r
115   EFI_OUT_OF_RESOURCES    - No enough buffer to allocate\r
116   \r
117   EFI_SUCCESS             - String successfully added to the incoming buffer\r
118 \r
119 --*/\r
120 {\r
121   EFI_HII_STRING_PACK *StringPack;\r
122   EFI_HII_STRING_PACK *StringPackBuffer;\r
123   VOID                *NewBuffer;\r
124   RELOFST             *PackSource;\r
125   RELOFST             *PackDestination;\r
126   UINT8               *Source;\r
127   UINT8               *Destination;\r
128   UINTN               Index;\r
129   BOOLEAN             Finished;\r
130 \r
131   StringPack  = (EFI_HII_STRING_PACK *) StringBuffer;\r
132   Finished    = FALSE;\r
133 \r
134   //\r
135   // Pre-allocate a buffer sufficient for us to work on.\r
136   // We will use it as a destination scratch pad to build data on\r
137   // and when complete shift the data back to the original buffer\r
138   //\r
139   NewBuffer = AllocateZeroPool (DEFAULT_STRING_BUFFER_SIZE);\r
140   if (NewBuffer == NULL) {\r
141     return EFI_OUT_OF_RESOURCES;\r
142   }\r
143 \r
144   StringPackBuffer = (EFI_HII_STRING_PACK *) NewBuffer;\r
145 \r
146   //\r
147   // StringPack is terminated with a length 0 entry\r
148   //\r
149   for (; StringPack->Header.Length != 0;) {\r
150     //\r
151     // If this stringpack's language is same as CurrentLanguage, use it\r
152     //\r
153     if (CompareMem ((VOID *) ((CHAR8 *) (StringPack) + StringPack->LanguageNameString), Language, 3) == 0) {\r
154       //\r
155       // We have some data in this string pack, copy the string package up to the string data\r
156       //\r
157       CopyMem (&StringPackBuffer->Header, &StringPack->Header, sizeof (StringPack));\r
158 \r
159       //\r
160       // These are references in the structure to tokens, need to increase them by the space occupied by an additional StringPointer\r
161       //\r
162       StringPackBuffer->LanguageNameString = (UINT16) (StringPackBuffer->LanguageNameString + (UINT16) sizeof (RELOFST));\r
163       StringPackBuffer->PrintableLanguageName = (UINT16) (StringPackBuffer->PrintableLanguageName + (UINT16) sizeof (RELOFST));\r
164 \r
165       PackSource      = (RELOFST *) (StringPack + 1);\r
166       PackDestination = (RELOFST *) (StringPackBuffer + 1);\r
167       for (Index = 0; PackSource[Index] != 0x0000; Index++) {\r
168         //\r
169         // Copy the stringpointers from old to new buffer\r
170         // remember that we are adding a string, so the string offsets will all go up by sizeof (RELOFST)\r
171         //\r
172         PackDestination[Index] = (UINT16) (PackDestination[Index] + sizeof (RELOFST));\r
173       }\r
174       \r
175       //\r
176       // Add a new stringpointer in the new buffer since we are adding a string.  Null terminate it\r
177       //\r
178       PackDestination[Index] = (UINT16)(PackDestination[Index-1] + \r
179                                         StrSize((CHAR16 *)((CHAR8 *)(StringPack) + PackSource[Index-1])));\r
180       PackDestination[Index + 1] = (UINT16) 0;\r
181 \r
182       //\r
183       // Index is the token value for the new string\r
184       //\r
185       *StringToken = (UINT16) Index;\r
186 \r
187       //\r
188       // Source now points to the beginning of the old buffer strings\r
189       // Destination now points to the beginning of the new buffer strings\r
190       //\r
191       Source      = (UINT8 *) &PackSource[Index + 1];\r
192       Destination = (UINT8 *) &PackDestination[Index + 2];\r
193 \r
194       //\r
195       // This should copy all the strings from the old buffer to the new buffer\r
196       //\r
197       for (; Index != 0; Index--) {\r
198         //\r
199         // Copy Source string to destination buffer\r
200         //\r
201         StrCpy ((CHAR16 *) Destination, (CHAR16 *) Source);\r
202 \r
203         //\r
204         // Adjust the source/destination to the next string location\r
205         //\r
206         Destination = Destination + StrSize ((CHAR16 *) Source);\r
207         Source      = Source + StrSize ((CHAR16 *) Source);\r
208       }\r
209       \r
210       //\r
211       // This copies the new string to the destination buffer\r
212       //\r
213       StrCpy ((CHAR16 *) Destination, (CHAR16 *) String);\r
214 \r
215       //\r
216       // Adjust the size of the changed string pack by adding the size of the new string\r
217       // along with the size of the additional offset entry for the new string\r
218       //\r
219       StringPackBuffer->Header.Length = (UINT32) ((UINTN) StringPackBuffer->Header.Length + StrSize (String) + sizeof (RELOFST));\r
220 \r
221       //\r
222       // Advance the buffers to point to the next spots.\r
223       //\r
224       StringPackBuffer  = (EFI_HII_STRING_PACK *) ((CHAR8 *) (StringPackBuffer) + StringPackBuffer->Header.Length);\r
225       StringPack        = (EFI_HII_STRING_PACK *) ((CHAR8 *) (StringPack) + StringPack->Header.Length);\r
226       Finished          = TRUE;\r
227       continue;\r
228     }\r
229     //\r
230     // This isn't the language of the stringpack we were asked to add a string to\r
231     // so we need to copy it to the new buffer.\r
232     //\r
233     CopyMem (&StringPackBuffer->Header, &StringPack->Header, StringPack->Header.Length);\r
234 \r
235     //\r
236     // Advance the buffers to point to the next spots.\r
237     //\r
238     StringPackBuffer  = (EFI_HII_STRING_PACK *) ((CHAR8 *) (StringPackBuffer) + StringPack->Header.Length);\r
239     StringPack        = (EFI_HII_STRING_PACK *) ((CHAR8 *) (StringPack) + StringPack->Header.Length);\r
240   }\r
241   \r
242   //\r
243   // If we didn't copy the new data to a stringpack yet\r
244   //\r
245   if (!Finished) {\r
246     PackDestination = (RELOFST *) (StringPackBuffer + 1);\r
247     //\r
248     // Pointing to a new string pack location\r
249     //\r
250     StringPackBuffer->Header.Length = (UINT32)\r
251       (\r
252         sizeof (EFI_HII_STRING_PACK) -\r
253         sizeof (EFI_STRING) +\r
254         sizeof (RELOFST) +\r
255         sizeof (RELOFST) +\r
256         StrSize (Language) +\r
257         StrSize (String)\r
258       );\r
259     StringPackBuffer->Header.Type           = EFI_HII_STRING;\r
260     StringPackBuffer->LanguageNameString    = (UINT16) ((UINTN) &PackDestination[3] - (UINTN) StringPackBuffer);\r
261     StringPackBuffer->PrintableLanguageName = (UINT16) ((UINTN) &PackDestination[3] - (UINTN) StringPackBuffer);\r
262     StringPackBuffer->Attributes            = 0;\r
263     PackDestination[0]                      = (UINT16) ((UINTN) &PackDestination[3] - (UINTN) StringPackBuffer);\r
264     PackDestination[1]                      = (UINT16) (PackDestination[0] + StrSize (Language));\r
265     PackDestination[2]                      = (UINT16) 0;\r
266 \r
267     //\r
268     // The first string location will be set to destination.  The minimum number of strings\r
269     // associated with a stringpack will always be token 0 stored as the languagename (e.g. ENG, SPA, etc)\r
270     // and token 1 as the new string being added and and null entry for the stringpointers\r
271     //\r
272     Destination = (UINT8 *) &PackDestination[3];\r
273 \r
274     //\r
275     // Copy the language name string to the new buffer\r
276     //\r
277     StrCpy ((CHAR16 *) Destination, Language);\r
278 \r
279     //\r
280     // Advance the destination to the new empty spot\r
281     //\r
282     Destination = Destination + StrSize (Language);\r
283 \r
284     //\r
285     // Copy the string to the new buffer\r
286     //\r
287     StrCpy ((CHAR16 *) Destination, String);\r
288 \r
289     //\r
290     // Since we are starting with a new string pack - we know the new string is token 1\r
291     //\r
292     *StringToken = (UINT16) 1;\r
293   }\r
294 \r
295   //\r
296   // Zero out the original buffer and copy the updated data in the new buffer to the old buffer\r
297   //\r
298   ZeroMem (StringBuffer, DEFAULT_STRING_BUFFER_SIZE);\r
299   CopyMem (StringBuffer, NewBuffer, DEFAULT_STRING_BUFFER_SIZE);\r
300 \r
301   //\r
302   // Free the newly created buffer since we don't need it anymore\r
303   //\r
304   gBS->FreePool (NewBuffer);\r
305   return EFI_SUCCESS;\r
306 }\r
307 \r
308 \r
309 EFI_STATUS\r
310 AddOpCode (\r
311   IN      VOID                *FormBuffer,\r
312   IN OUT  VOID                *OpCodeData\r
313   )\r
314 /*++\r
315 \r
316 Routine Description:\r
317 \r
318   Add op-code data to the FormBuffer\r
319   \r
320 Arguments:\r
321   \r
322   FormBuffer      - Form buffer to be inserted to\r
323   \r
324   OpCodeData      - Op-code data to be inserted\r
325   \r
326 Returns: \r
327 \r
328   EFI_OUT_OF_RESOURCES    - No enough buffer to allocate\r
329   \r
330   EFI_SUCCESS             - Op-code data successfully inserted\r
331 \r
332 --*/\r
333 {\r
334   EFI_HII_PACK_HEADER *NewBuffer;\r
335   UINT8               *Source;\r
336   UINT8               *Destination;\r
337 \r
338   //\r
339   // Pre-allocate a buffer sufficient for us to work on.\r
340   // We will use it as a destination scratch pad to build data on\r
341   // and when complete shift the data back to the original buffer\r
342   //\r
343   NewBuffer = AllocateZeroPool (DEFAULT_FORM_BUFFER_SIZE);\r
344   if (NewBuffer == NULL) {\r
345     return EFI_OUT_OF_RESOURCES;\r
346   }\r
347 \r
348   Source      = (UINT8 *) FormBuffer;\r
349   Destination = (UINT8 *) NewBuffer;\r
350 \r
351   //\r
352   // Copy the IFR Package header to the new buffer\r
353   //\r
354   CopyMem (Destination, Source, sizeof (EFI_HII_PACK_HEADER));\r
355 \r
356   //\r
357   // Advance Source and Destination to next op-code\r
358   //\r
359   Source      = Source + sizeof (EFI_HII_PACK_HEADER);\r
360   Destination = Destination + sizeof (EFI_HII_PACK_HEADER);\r
361 \r
362   //\r
363   // Copy data to the new buffer until we run into the end_form\r
364   //\r
365   for (; ((EFI_IFR_OP_HEADER *) Source)->OpCode != EFI_IFR_END_FORM_OP;) {\r
366     //\r
367     // If the this opcode is an end_form_set we better be creating and endform\r
368     // Nonetheless, we will add data before the end_form_set.  This also provides\r
369     // for interesting behavior in the code we will run, but has no bad side-effects\r
370     // since we will possibly do a 0 byte copy in this particular end-case.\r
371     //\r
372     if (((EFI_IFR_OP_HEADER *) Source)->OpCode == EFI_IFR_END_FORM_SET_OP) {\r
373       break;\r
374     }\r
375 \r
376     //\r
377     // Copy data to new buffer\r
378     //\r
379     CopyMem (Destination, Source, ((EFI_IFR_OP_HEADER *) Source)->Length);\r
380 \r
381     //\r
382     // Adjust Source/Destination to next op-code location\r
383     //\r
384     Destination = Destination + (UINTN) ((EFI_IFR_OP_HEADER *) Source)->Length;\r
385     Source      = Source + (UINTN) ((EFI_IFR_OP_HEADER *) Source)->Length;\r
386   }\r
387 \r
388   //\r
389   // Prior to the end_form is where we insert the new op-code data\r
390   //\r
391   CopyMem (Destination, OpCodeData, ((EFI_IFR_OP_HEADER *) OpCodeData)->Length);\r
392   Destination       = Destination + (UINTN) ((EFI_IFR_OP_HEADER *) OpCodeData)->Length;\r
393 \r
394   NewBuffer->Length = (UINT32) (NewBuffer->Length + (UINT32) (((EFI_IFR_OP_HEADER *) OpCodeData)->Length));\r
395 \r
396   //\r
397   // Copy end-form data to new buffer\r
398   //\r
399   CopyMem (Destination, Source, ((EFI_IFR_OP_HEADER *) Source)->Length);\r
400 \r
401   //\r
402   // Adjust Source/Destination to next op-code location\r
403   //\r
404   Destination = Destination + (UINTN) ((EFI_IFR_OP_HEADER *) Source)->Length;\r
405   Source      = Source + (UINTN) ((EFI_IFR_OP_HEADER *) Source)->Length;\r
406 \r
407   //\r
408   // Copy end-formset data to new buffer\r
409   //\r
410   CopyMem (Destination, Source, ((EFI_IFR_OP_HEADER *) Source)->Length);\r
411 \r
412   //\r
413   // Zero out the original buffer and copy the updated data in the new buffer to the old buffer\r
414   //\r
415   ZeroMem (FormBuffer, DEFAULT_FORM_BUFFER_SIZE);\r
416   CopyMem (FormBuffer, NewBuffer, DEFAULT_FORM_BUFFER_SIZE);\r
417 \r
418   //\r
419   // Free the newly created buffer since we don't need it anymore\r
420   //\r
421   gBS->FreePool (NewBuffer);\r
422   return EFI_SUCCESS;\r
423 }\r
424 \r
425 \r
426 EFI_STATUS\r
427 GetHiiInterface (\r
428   OUT     EFI_HII_PROTOCOL    **Hii\r
429   )\r
430 /*++\r
431 \r
432 Routine Description:\r
433 \r
434   Get the HII protocol interface\r
435   \r
436 Arguments:\r
437   \r
438   Hii     - HII protocol interface\r
439   \r
440 Returns: \r
441 \r
442   Status code\r
443 \r
444 --*/\r
445 {\r
446   EFI_STATUS  Status;\r
447 \r
448   //\r
449   // There should only be one HII protocol\r
450   //\r
451   Status = gBS->LocateProtocol (\r
452                   &gEfiHiiProtocolGuid,\r
453                   NULL,\r
454                   (VOID **) Hii\r
455                   );\r
456 \r
457   return Status;;\r
458 }\r
459 \r
460 \r
461 EFI_STATUS\r
462 ExtractDataFromHiiHandle (\r
463   IN      EFI_HII_HANDLE      HiiHandle,\r
464   IN OUT  UINT16              *ImageLength,\r
465   OUT     UINT8               *DefaultImage,\r
466   OUT     EFI_GUID            *Guid\r
467   )\r
468 /*++\r
469 \r
470 Routine Description:\r
471 \r
472   Extract information pertaining to the HiiHandle\r
473   \r
474 Arguments:\r
475   \r
476   HiiHandle       - Hii handle\r
477   \r
478   ImageLength     - For input, length of DefaultImage;\r
479                     For output, length of actually required\r
480                     \r
481   DefaultImage    - Image buffer prepared by caller\r
482   \r
483   Guid            - Guid information about the form\r
484   \r
485 Returns: \r
486 \r
487   EFI_OUT_OF_RESOURCES    - No enough buffer to allocate\r
488   \r
489   EFI_BUFFER_TOO_SMALL    - DefualtImage has no enough ImageLength\r
490   \r
491   EFI_SUCCESS             - Successfully extract data from Hii database.\r
492   \r
493   \r
494 --*/\r
495 {\r
496   EFI_STATUS        Status;\r
497   EFI_HII_PROTOCOL  *Hii;\r
498   UINTN             DataLength;\r
499   UINT8             *RawData;\r
500   UINT8             *OldData;\r
501   UINTN             Index;\r
502   UINTN             Temp;\r
503   UINTN             SizeOfNvStore;\r
504   UINTN             CachedStart;\r
505 \r
506   DataLength    = DEFAULT_FORM_BUFFER_SIZE;\r
507   SizeOfNvStore = 0;\r
508   CachedStart   = 0;\r
509 \r
510   Status        = GetHiiInterface (&Hii);\r
511 \r
512   if (EFI_ERROR (Status)) {\r
513     return Status;\r
514   }\r
515 \r
516   //\r
517   // Allocate space for retrieval of IFR data\r
518   //\r
519   RawData = AllocateZeroPool (DataLength);\r
520   if (RawData == NULL) {\r
521     return EFI_OUT_OF_RESOURCES;\r
522   }\r
523 \r
524   //\r
525   // Get all the forms associated with this HiiHandle\r
526   //\r
527   Status = Hii->GetForms (Hii, HiiHandle, 0, &DataLength, RawData);\r
528 \r
529   if (EFI_ERROR (Status)) {\r
530     gBS->FreePool (RawData);\r
531 \r
532     //\r
533     // Allocate space for retrieval of IFR data\r
534     //\r
535     RawData = AllocateZeroPool (DataLength);\r
536     if (RawData == NULL) {\r
537       return EFI_OUT_OF_RESOURCES;\r
538     }\r
539 \r
540     //\r
541     // Get all the forms associated with this HiiHandle\r
542     //\r
543     Status = Hii->GetForms (Hii, HiiHandle, 0, &DataLength, RawData);\r
544   }\r
545 \r
546   OldData = RawData;\r
547 \r
548   //\r
549   // Point RawData to the beginning of the form data\r
550   //\r
551   RawData = (UINT8 *) ((UINTN) RawData + sizeof (EFI_HII_PACK_HEADER));\r
552 \r
553   for (Index = 0; RawData[Index] != EFI_IFR_END_FORM_SET_OP;) {\r
554     switch (RawData[Index]) {\r
555     case EFI_IFR_FORM_SET_OP:\r
556       //\r
557       // Copy the GUID information from this handle\r
558       //\r
559       CopyMem (Guid, &((EFI_IFR_FORM_SET *) &RawData[Index])->Guid, sizeof (EFI_GUID));\r
560       break;\r
561 \r
562     case EFI_IFR_ONE_OF_OP:\r
563     case EFI_IFR_CHECKBOX_OP:\r
564     case EFI_IFR_NUMERIC_OP:\r
565     case EFI_IFR_DATE_OP:\r
566     case EFI_IFR_TIME_OP:\r
567     case EFI_IFR_PASSWORD_OP:\r
568     case EFI_IFR_STRING_OP:\r
569       //\r
570       // Remember, multiple op-codes may reference the same item, so let's keep a running\r
571       // marker of what the highest QuestionId that wasn't zero length.  This will accurately\r
572       // maintain the Size of the NvStore\r
573       //\r
574       if (((EFI_IFR_ONE_OF *) &RawData[Index])->Width != 0) {\r
575         Temp = ((EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId + ((EFI_IFR_ONE_OF *) &RawData[Index])->Width;\r
576         if (SizeOfNvStore < Temp) {\r
577           SizeOfNvStore = ((EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId + ((EFI_IFR_ONE_OF *) &RawData[Index])->Width;\r
578         }\r
579       }\r
580     }\r
581 \r
582     Index = RawData[Index + 1] + Index;\r
583   }\r
584     \r
585   //\r
586   // Return an error if buffer is too small\r
587   //\r
588   if (SizeOfNvStore > *ImageLength) {\r
589     gBS->FreePool (OldData);\r
590     *ImageLength = (UINT16) SizeOfNvStore;\r
591     return EFI_BUFFER_TOO_SMALL;\r
592   }\r
593 \r
594   if (DefaultImage != NULL) {\r
595     ZeroMem (DefaultImage, SizeOfNvStore);\r
596   }\r
597 \r
598   //\r
599   // Copy the default image information to the user's buffer\r
600   //\r
601   for (Index = 0; RawData[Index] != EFI_IFR_END_FORM_SET_OP;) {\r
602     switch (RawData[Index]) {\r
603     case EFI_IFR_ONE_OF_OP:\r
604       CachedStart = ((EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId;\r
605       break;\r
606 \r
607     case EFI_IFR_ONE_OF_OPTION_OP:\r
608       if (((EFI_IFR_ONE_OF_OPTION *) &RawData[Index])->Flags & EFI_IFR_FLAG_DEFAULT) {\r
609         CopyMem (&DefaultImage[CachedStart], &((EFI_IFR_ONE_OF_OPTION *) &RawData[Index])->Value, 2);\r
610       }\r
611       break;\r
612 \r
613     case EFI_IFR_CHECKBOX_OP:\r
614       DefaultImage[((EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId] = ((EFI_IFR_CHECKBOX *) &RawData[Index])->Flags;\r
615       break;\r
616 \r
617     case EFI_IFR_NUMERIC_OP:\r
618       CopyMem (\r
619         &DefaultImage[((EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId],\r
620         &((EFI_IFR_NUMERIC *) &RawData[Index])->Default,\r
621         2\r
622         );\r
623       break;\r
624 \r
625     }\r
626 \r
627     Index = RawData[Index + 1] + Index;\r
628   }\r
629 \r
630   *ImageLength = (UINT16) SizeOfNvStore;\r
631 \r
632   //\r
633   // Free our temporary repository of form data\r
634   //\r
635   gBS->FreePool (OldData);\r
636 \r
637   return EFI_SUCCESS;\r
638 }\r
639 \r
640 \r
641 EFI_HII_HANDLE\r
642 FindHiiHandle (\r
643   IN OUT EFI_HII_PROTOCOL    **HiiProtocol, OPTIONAL\r
644   IN     EFI_GUID            *Guid\r
645   )\r
646 /*++\r
647 \r
648 Routine Description:\r
649   Finds HII handle for given pack GUID previously registered with the HII.\r
650 \r
651 Arguments:\r
652   HiiProtocol - pointer to pointer to HII protocol interface. \r
653                 If NULL, the interface will be found but not returned.\r
654                 If it points to NULL, the interface will be found and \r
655                 written back to the pointer that is pointed to.\r
656   Guid        - The GUID of the pack that registered with the HII.\r
657 \r
658 Returns:\r
659   Handle to the HII pack previously registered by the memory driver.\r
660 \r
661 --*/\r
662 {\r
663   EFI_STATUS        Status;\r
664 \r
665   EFI_HII_HANDLE    *HiiHandleBuffer;\r
666   EFI_HII_HANDLE    HiiHandle;\r
667   UINT16            HiiHandleBufferLength;\r
668   UINT32            NumberOfHiiHandles;\r
669   EFI_GUID          HiiGuid;\r
670   EFI_HII_PROTOCOL  *HiiProt;\r
671   UINT32            Index;\r
672   UINT16            Length;\r
673 \r
674   HiiHandle = 0;\r
675   if ((HiiProtocol != NULL) && (*HiiProtocol != NULL)) {\r
676     //\r
677     // The protocol has been passed in\r
678     //\r
679     HiiProt = *HiiProtocol;\r
680   } else {\r
681     gBS->LocateProtocol (\r
682           &gEfiHiiProtocolGuid,\r
683           NULL,\r
684           (VOID **) &HiiProt\r
685           );\r
686     if (HiiProt == NULL) {\r
687       return HiiHandle;\r
688     }\r
689 \r
690     if (HiiProtocol != NULL) {\r
691       //\r
692       // Return back the HII protocol for the caller as promissed\r
693       //\r
694       *HiiProtocol = HiiProt;\r
695     }\r
696   }\r
697   //\r
698   // Allocate buffer\r
699   //\r
700   HiiHandleBufferLength = 10;\r
701   HiiHandleBuffer       = AllocatePool (HiiHandleBufferLength);\r
702   ASSERT (HiiHandleBuffer != NULL);\r
703 \r
704   //\r
705   // Get the Handles of the packages that were registered with Hii\r
706   //\r
707   Status = HiiProt->FindHandles (\r
708                       HiiProt,\r
709                       &HiiHandleBufferLength,\r
710                       HiiHandleBuffer\r
711                       );\r
712 \r
713   //\r
714   // Get a bigger bugffer if this one is to small, and try again\r
715   //\r
716   if (Status == EFI_BUFFER_TOO_SMALL) {\r
717 \r
718     gBS->FreePool (HiiHandleBuffer);\r
719 \r
720     HiiHandleBuffer = AllocatePool (HiiHandleBufferLength);\r
721     ASSERT (HiiHandleBuffer != NULL);\r
722 \r
723     Status = HiiProt->FindHandles (\r
724                         HiiProt,\r
725                         &HiiHandleBufferLength,\r
726                         HiiHandleBuffer\r
727                         );\r
728   }\r
729 \r
730   if (EFI_ERROR (Status)) {\r
731     goto lbl_exit;\r
732   }\r
733 \r
734   NumberOfHiiHandles = HiiHandleBufferLength / sizeof (EFI_HII_HANDLE);\r
735 \r
736   //\r
737   // Iterate Hii handles and look for the one that matches our Guid\r
738   //\r
739   for (Index = 0; Index < NumberOfHiiHandles; Index++) {\r
740 \r
741     Length = 0;\r
742     ExtractDataFromHiiHandle (HiiHandleBuffer[Index], &Length, NULL, &HiiGuid);\r
743 \r
744     if (CompareGuid (&HiiGuid, Guid)) {\r
745 \r
746       HiiHandle = HiiHandleBuffer[Index];\r
747       break;\r
748     }\r
749   }\r
750 \r
751 lbl_exit:\r
752   gBS->FreePool (HiiHandleBuffer);\r
753   return HiiHandle;\r
754 }\r
755 \r
756 \r
757 EFI_STATUS\r
758 ValidateDataFromHiiHandle (\r
759   IN      EFI_HII_HANDLE      HiiHandle,\r
760   OUT     BOOLEAN             *Results\r
761   )\r
762 /*++\r
763 \r
764 Routine Description:\r
765 \r
766   Validate that the data associated with the HiiHandle in NVRAM is within\r
767   the reasonable parameters for that FormSet.  Values for strings and passwords\r
768   are not verified due to their not having the equivalent of valid range settings.\r
769   \r
770 Arguments:\r
771 \r
772   HiiHandle -   Handle of the HII database entry to query\r
773 \r
774   Results -     If return Status is EFI_SUCCESS, Results provides valid data\r
775                 TRUE  = NVRAM Data is within parameters\r
776                 FALSE = NVRAM Data is NOT within parameters\r
777   \r
778 Returns: \r
779 \r
780   EFI_OUT_OF_RESOURCES      - No enough buffer to allocate\r
781   \r
782   EFI_SUCCESS               - Data successfully validated\r
783 --*/\r
784 {\r
785   EFI_STATUS        Status;\r
786   EFI_HII_PROTOCOL  *Hii;\r
787   EFI_GUID          Guid;\r
788   UINT8             *RawData;\r
789   UINT8             *OldData;\r
790   UINTN             RawDataLength;\r
791   UINT8             *VariableData;\r
792   UINTN             Index;\r
793   UINTN             Temp;\r
794   UINTN             SizeOfNvStore;\r
795   UINTN             CachedStart;\r
796   BOOLEAN           GotMatch;\r
797 \r
798   RawDataLength = DEFAULT_FORM_BUFFER_SIZE;\r
799   SizeOfNvStore = 0;\r
800   CachedStart   = 0;\r
801   GotMatch      = FALSE;\r
802   *Results      = TRUE;\r
803 \r
804   Status        = GetHiiInterface (&Hii);\r
805 \r
806   if (EFI_ERROR (Status)) {\r
807     return Status;\r
808   }\r
809 \r
810   //\r
811   // Allocate space for retrieval of IFR data\r
812   //\r
813   RawData = AllocateZeroPool (RawDataLength);\r
814   if (RawData == NULL) {\r
815     return EFI_OUT_OF_RESOURCES;\r
816   }\r
817 \r
818   //\r
819   // Get all the forms associated with this HiiHandle\r
820   //\r
821   Status = Hii->GetForms (Hii, HiiHandle, 0, &RawDataLength, RawData);\r
822 \r
823   if (EFI_ERROR (Status)) {\r
824     gBS->FreePool (RawData);\r
825 \r
826     //\r
827     // Allocate space for retrieval of IFR data\r
828     //\r
829     RawData = AllocateZeroPool (RawDataLength);\r
830     if (RawData == NULL) {\r
831       return EFI_OUT_OF_RESOURCES;\r
832     }\r
833 \r
834     //\r
835     // Get all the forms associated with this HiiHandle\r
836     //\r
837     Status = Hii->GetForms (Hii, HiiHandle, 0, &RawDataLength, RawData);\r
838   }\r
839 \r
840   OldData = RawData;\r
841 \r
842   //\r
843   // Point RawData to the beginning of the form data\r
844   //\r
845   RawData = (UINT8 *) ((UINTN) RawData + sizeof (EFI_HII_PACK_HEADER));\r
846 \r
847   for (Index = 0; RawData[Index] != EFI_IFR_END_FORM_SET_OP;) {\r
848     if (RawData[Index] == EFI_IFR_FORM_SET_OP) {\r
849       CopyMem (&Guid, &((EFI_IFR_FORM_SET *) &RawData[Index])->Guid, sizeof (EFI_GUID));\r
850       break;\r
851     }\r
852 \r
853     Index = RawData[Index + 1] + Index;\r
854   }\r
855 \r
856   for (Index = 0; RawData[Index] != EFI_IFR_END_FORM_SET_OP;) {\r
857     switch (RawData[Index]) {\r
858     case EFI_IFR_FORM_SET_OP:\r
859       break;\r
860 \r
861     case EFI_IFR_ONE_OF_OP:\r
862     case EFI_IFR_CHECKBOX_OP:\r
863     case EFI_IFR_NUMERIC_OP:\r
864     case EFI_IFR_DATE_OP:\r
865     case EFI_IFR_TIME_OP:\r
866     case EFI_IFR_PASSWORD_OP:\r
867     case EFI_IFR_STRING_OP:\r
868       //\r
869       // Remember, multiple op-codes may reference the same item, so let's keep a running\r
870       // marker of what the highest QuestionId that wasn't zero length.  This will accurately\r
871       // maintain the Size of the NvStore\r
872       //\r
873       if (((EFI_IFR_ONE_OF *) &RawData[Index])->Width != 0) {\r
874         Temp = ((EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId + ((EFI_IFR_ONE_OF *) &RawData[Index])->Width;\r
875         if (SizeOfNvStore < Temp) {\r
876           SizeOfNvStore = ((EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId + ((EFI_IFR_ONE_OF *) &RawData[Index])->Width;\r
877         }\r
878       }\r
879     }\r
880 \r
881     Index = RawData[Index + 1] + Index;\r
882   }\r
883     \r
884   //\r
885   // Allocate memory for our File Form Tags\r
886   //\r
887   VariableData = AllocateZeroPool (SizeOfNvStore);\r
888   if (VariableData == NULL) {\r
889     return EFI_OUT_OF_RESOURCES;\r
890   }\r
891 \r
892   Status = gRT->GetVariable (\r
893                   (CHAR16 *) L"Setup",\r
894                   &Guid,\r
895                   NULL,\r
896                   &SizeOfNvStore,\r
897                   (VOID *) VariableData\r
898                   );\r
899 \r
900   if (EFI_ERROR (Status)) {\r
901 \r
902     //\r
903     // If there is a variable that exists already and it is larger than what we calculated the\r
904     // storage needs to be, we must assume the variable size from GetVariable is correct and not\r
905     // allow the truncation of the variable.  It is very possible that the user who created the IFR\r
906     // we are cracking is not referring to a variable that was in a previous map, however we cannot\r
907     // allow it's truncation.\r
908     //\r
909     if (Status == EFI_BUFFER_TOO_SMALL) {\r
910       //\r
911       // Free the buffer that was allocated that was too small\r
912       //\r
913       gBS->FreePool (VariableData);\r
914 \r
915       VariableData = AllocatePool (SizeOfNvStore);\r
916       if (VariableData == NULL) {\r
917         return EFI_OUT_OF_RESOURCES;\r
918       }\r
919 \r
920       Status = gRT->GetVariable (\r
921                       (CHAR16 *) L"Setup",\r
922                       &Guid,\r
923                       NULL,\r
924                       &SizeOfNvStore,\r
925                       (VOID *) VariableData\r
926                       );\r
927     }\r
928   }\r
929 \r
930   //\r
931   // Walk through the form and see that the variable data it refers to is ok.\r
932   // This allows for the possibility of stale (obsoleted) data in the variable\r
933   // can be overlooked without causing an error\r
934   //\r
935   for (Index = 0; RawData[Index] != EFI_IFR_END_FORM_SET_OP;) {\r
936     switch (RawData[Index]) {\r
937     case EFI_IFR_ONE_OF_OP:\r
938       //\r
939       // A one_of has no data, its the option that does - cache the storage Id\r
940       //\r
941       CachedStart = ((EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId;\r
942       break;\r
943 \r
944     case EFI_IFR_ONE_OF_OPTION_OP:\r
945       //\r
946       // A one_of_option can be any value\r
947       //\r
948       if (VariableData[CachedStart] == ((EFI_IFR_ONE_OF_OPTION *) &RawData[Index])->Value) {\r
949         GotMatch = TRUE;\r
950       }\r
951       break;\r
952 \r
953     case EFI_IFR_END_ONE_OF_OP:\r
954       //\r
955       // At this point lets make sure that the data value in the NVRAM matches one of the options\r
956       //\r
957       if (!GotMatch) {\r
958         *Results = FALSE;\r
959         return EFI_SUCCESS;\r
960       }\r
961       break;\r
962 \r
963     case EFI_IFR_CHECKBOX_OP:\r
964       //\r
965       // A checkbox is a boolean, so 0 and 1 are valid\r
966       // Remember, QuestionId corresponds to the offset location of the data in the variable\r
967       //\r
968       if (VariableData[((EFI_IFR_CHECKBOX *) &RawData[Index])->QuestionId] > 1) {\r
969         *Results = FALSE;\r
970         return EFI_SUCCESS;\r
971       }\r
972       break;\r
973 \r
974     case EFI_IFR_NUMERIC_OP:\r
975         if ((VariableData[((EFI_IFR_NUMERIC *)&RawData[Index])->QuestionId] < ((EFI_IFR_NUMERIC *)&RawData[Index])->Minimum) ||\r
976             (VariableData[((EFI_IFR_NUMERIC *)&RawData[Index])->QuestionId] > ((EFI_IFR_NUMERIC *)&RawData[Index])->Maximum)) {\r
977         *Results = FALSE;\r
978         return EFI_SUCCESS;\r
979       }\r
980       break;\r
981 \r
982     }\r
983 \r
984     Index = RawData[Index + 1] + Index;\r
985   }\r
986 \r
987   //\r
988   // Free our temporary repository of form data\r
989   //\r
990   gBS->FreePool (OldData);\r
991   gBS->FreePool (VariableData);\r
992 \r
993   return EFI_SUCCESS;\r
994 }\r
995 \r