• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   This driver effectuates OVMF's platform configuration settings and exposes
3   them via HII.
4 
5   Copyright (C) 2014, Red Hat, Inc.
6   Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
7 
8   This program and the accompanying materials are licensed and made available
9   under the terms and conditions of the BSD License which accompanies this
10   distribution.  The full text of the license may be found at
11   http://opensource.org/licenses/bsd-license.php
12 
13   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
14   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 **/
16 
17 #include <Library/BaseLib.h>
18 #include <Library/BaseMemoryLib.h>
19 #include <Library/DebugLib.h>
20 #include <Library/DevicePathLib.h>
21 #include <Library/HiiLib.h>
22 #include <Library/MemoryAllocationLib.h>
23 #include <Library/PrintLib.h>
24 #include <Library/UefiBootServicesTableLib.h>
25 #include <Library/UefiHiiServicesLib.h>
26 #include <Protocol/DevicePath.h>
27 #include <Protocol/GraphicsOutput.h>
28 #include <Protocol/HiiConfigAccess.h>
29 #include <Guid/MdeModuleHii.h>
30 #include <Guid/OvmfPlatformConfig.h>
31 
32 #include "Platform.h"
33 #include "PlatformConfig.h"
34 
35 //
36 // The HiiAddPackages() library function requires that any controller (or
37 // image) handle, to be associated with the HII packages under installation, be
38 // "decorated" with a device path. The tradition seems to be a vendor device
39 // path.
40 //
41 // We'd like to associate our HII packages with the driver's image handle. The
42 // first idea is to use the driver image's device path. Unfortunately, loaded
43 // images only come with an EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL (not the
44 // usual EFI_DEVICE_PATH_PROTOCOL), ie. a different GUID. In addition, even the
45 // EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL interface may be NULL, if the image
46 // has been loaded from an "unnamed" memory source buffer.
47 //
48 // Hence let's just stick with the tradition -- use a dedicated vendor device
49 // path, with the driver's FILE_GUID.
50 //
51 #pragma pack(1)
52 typedef struct {
53   VENDOR_DEVICE_PATH       VendorDevicePath;
54   EFI_DEVICE_PATH_PROTOCOL End;
55 } PKG_DEVICE_PATH;
56 #pragma pack()
57 
58 STATIC PKG_DEVICE_PATH mPkgDevicePath = {
59   {
60     {
61       HARDWARE_DEVICE_PATH,
62       HW_VENDOR_DP,
63       {
64         (UINT8) (sizeof (VENDOR_DEVICE_PATH)     ),
65         (UINT8) (sizeof (VENDOR_DEVICE_PATH) >> 8)
66       }
67     },
68     EFI_CALLER_ID_GUID
69   },
70   {
71     END_DEVICE_PATH_TYPE,
72     END_ENTIRE_DEVICE_PATH_SUBTYPE,
73     {
74       (UINT8) (END_DEVICE_PATH_LENGTH     ),
75       (UINT8) (END_DEVICE_PATH_LENGTH >> 8)
76     }
77   }
78 };
79 
80 //
81 // The configuration interface between the HII engine (form display etc) and
82 // this driver.
83 //
84 STATIC EFI_HII_CONFIG_ACCESS_PROTOCOL mConfigAccess;
85 
86 //
87 // The handle representing our list of packages after installation.
88 //
89 STATIC EFI_HII_HANDLE mInstalledPackages;
90 
91 //
92 // The arrays below constitute our HII package list. They are auto-generated by
93 // the VFR compiler and linked into the driver image during the build.
94 //
95 // - The strings package receives its C identifier from the driver's BASE_NAME,
96 //   plus "Strings".
97 //
98 // - The forms package receives its C identifier from the VFR file's basename,
99 //   plus "Bin".
100 //
101 //
102 extern UINT8 PlatformDxeStrings[];
103 extern UINT8 PlatformFormsBin[];
104 
105 //
106 // We want to be notified about GOP installations until we find one GOP
107 // interface that lets us populate the form.
108 //
109 STATIC EFI_EVENT mGopEvent;
110 
111 //
112 // The registration record underneath this pointer allows us to iterate through
113 // the GOP instances one by one.
114 //
115 STATIC VOID *mGopTracker;
116 
117 //
118 // Cache the resolutions we get from the GOP.
119 //
120 typedef struct {
121   UINT32 X;
122   UINT32 Y;
123 } GOP_MODE;
124 
125 STATIC UINTN    mNumGopModes;
126 STATIC GOP_MODE *mGopModes;
127 
128 
129 /**
130   Load the persistent platform configuration and translate it to binary form
131   state.
132 
133   If the platform configuration is missing, then the function fills in a
134   default state.
135 
136   @param[out] MainFormState  Binary form/widget state after translation.
137 
138   @retval EFI_SUCCESS  Form/widget state ready.
139   @return              Error codes from underlying functions.
140 **/
141 STATIC
142 EFI_STATUS
143 EFIAPI
PlatformConfigToFormState(OUT MAIN_FORM_STATE * MainFormState)144 PlatformConfigToFormState (
145   OUT MAIN_FORM_STATE *MainFormState
146   )
147 {
148   EFI_STATUS      Status;
149   PLATFORM_CONFIG PlatformConfig;
150   UINT64          OptionalElements;
151   UINTN           ModeNumber;
152 
153   ZeroMem (MainFormState, sizeof *MainFormState);
154 
155   Status = PlatformConfigLoad (&PlatformConfig, &OptionalElements);
156   switch (Status) {
157   case EFI_SUCCESS:
158     if (OptionalElements & PLATFORM_CONFIG_F_GRAPHICS_RESOLUTION) {
159       //
160       // Format the preferred resolution as text.
161       //
162       UnicodeSPrintAsciiFormat (
163         (CHAR16 *) MainFormState->CurrentPreferredResolution,
164         sizeof MainFormState->CurrentPreferredResolution,
165         "%Ldx%Ld",
166         (INT64) PlatformConfig.HorizontalResolution,
167         (INT64) PlatformConfig.VerticalResolution);
168 
169       //
170       // Try to locate it in the drop-down list too. This may not succeed, but
171       // that's fine.
172       //
173       for (ModeNumber = 0; ModeNumber < mNumGopModes; ++ModeNumber) {
174         if (mGopModes[ModeNumber].X == PlatformConfig.HorizontalResolution &&
175             mGopModes[ModeNumber].Y == PlatformConfig.VerticalResolution) {
176           MainFormState->NextPreferredResolution = (UINT32) ModeNumber;
177           break;
178         }
179       }
180 
181       break;
182     }
183     //
184     // fall through otherwise
185     //
186 
187   case EFI_NOT_FOUND:
188     UnicodeSPrintAsciiFormat (
189       (CHAR16 *) MainFormState->CurrentPreferredResolution,
190       sizeof MainFormState->CurrentPreferredResolution,
191       "Unset");
192     break;
193 
194   default:
195     return Status;
196   }
197 
198   return EFI_SUCCESS;
199 }
200 
201 
202 /**
203   This function is called by the HII machinery when it fetches the form state.
204 
205   See the precise documentation in the UEFI spec.
206 
207   @param[in]  This      The Config Access Protocol instance.
208 
209   @param[in]  Request   A <ConfigRequest> format UCS-2 string describing the
210                         query.
211 
212   @param[out] Progress  A pointer into Request on output, identifying the query
213                         element where processing failed.
214 
215   @param[out] Results   A <MultiConfigAltResp> format UCS-2 string that has
216                         all values filled in for the names in the Request
217                         string.
218 
219   @retval EFI_SUCCESS  Extraction of form state in <MultiConfigAltResp>
220                        encoding successful.
221   @return              Status codes from underlying functions.
222 
223 **/
224 STATIC
225 EFI_STATUS
226 EFIAPI
ExtractConfig(IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL * This,IN CONST EFI_STRING Request,OUT EFI_STRING * Progress,OUT EFI_STRING * Results)227 ExtractConfig (
228   IN CONST  EFI_HII_CONFIG_ACCESS_PROTOCOL  *This,
229   IN CONST  EFI_STRING                      Request,
230   OUT       EFI_STRING                      *Progress,
231   OUT       EFI_STRING                      *Results
232 )
233 {
234   MAIN_FORM_STATE MainFormState;
235   EFI_STATUS      Status;
236 
237   DEBUG ((EFI_D_VERBOSE, "%a: Request=\"%s\"\n", __FUNCTION__, Request));
238 
239   Status = PlatformConfigToFormState (&MainFormState);
240   if (EFI_ERROR (Status)) {
241     *Progress = Request;
242     return Status;
243   }
244 
245   //
246   // Answer the textual request keying off the binary form state.
247   //
248   Status = gHiiConfigRouting->BlockToConfig (gHiiConfigRouting, Request,
249                                 (VOID *) &MainFormState, sizeof MainFormState,
250                                 Results, Progress);
251   if (EFI_ERROR (Status)) {
252     DEBUG ((EFI_D_ERROR, "%a: BlockToConfig(): %r, Progress=\"%s\"\n",
253       __FUNCTION__, Status, (Status == EFI_DEVICE_ERROR) ? NULL : *Progress));
254   } else {
255     DEBUG ((EFI_D_VERBOSE, "%a: Results=\"%s\"\n", __FUNCTION__, *Results));
256   }
257   return Status;
258 }
259 
260 
261 /**
262   Interpret the binary form state and save it as persistent platform
263   configuration.
264 
265   @param[in] MainFormState  Binary form/widget state to verify and save.
266 
267   @retval EFI_SUCCESS  Platform configuration saved.
268   @return              Error codes from underlying functions.
269 **/
270 STATIC
271 EFI_STATUS
272 EFIAPI
FormStateToPlatformConfig(IN CONST MAIN_FORM_STATE * MainFormState)273 FormStateToPlatformConfig (
274   IN CONST MAIN_FORM_STATE *MainFormState
275   )
276 {
277   EFI_STATUS      Status;
278   PLATFORM_CONFIG PlatformConfig;
279   CONST GOP_MODE  *GopMode;
280 
281   //
282   // There's nothing to do with the textual CurrentPreferredResolution field.
283   // We verify and translate the selection in the drop-down list.
284   //
285   if (MainFormState->NextPreferredResolution >= mNumGopModes) {
286     return EFI_INVALID_PARAMETER;
287   }
288   GopMode = mGopModes + MainFormState->NextPreferredResolution;
289 
290   ZeroMem (&PlatformConfig, sizeof PlatformConfig);
291   PlatformConfig.HorizontalResolution = GopMode->X;
292   PlatformConfig.VerticalResolution   = GopMode->Y;
293 
294   Status = PlatformConfigSave (&PlatformConfig);
295   return Status;
296 }
297 
298 
299 /**
300   This function is called by the HII machinery when it wants the driver to
301   interpret and persist the form state.
302 
303   See the precise documentation in the UEFI spec.
304 
305   @param[in]  This           The Config Access Protocol instance.
306 
307   @param[in]  Configuration  A <ConfigResp> format UCS-2 string describing the
308                              form state.
309 
310   @param[out] Progress       A pointer into Configuration on output,
311                              identifying the element where processing failed.
312 
313   @retval EFI_SUCCESS  Configuration verified, state permanent.
314 
315   @return              Status codes from underlying functions.
316 **/
317 STATIC
318 EFI_STATUS
319 EFIAPI
RouteConfig(IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL * This,IN CONST EFI_STRING Configuration,OUT EFI_STRING * Progress)320 RouteConfig (
321   IN CONST  EFI_HII_CONFIG_ACCESS_PROTOCOL  *This,
322   IN CONST  EFI_STRING                      Configuration,
323   OUT       EFI_STRING                      *Progress
324 )
325 {
326   MAIN_FORM_STATE MainFormState;
327   UINTN           BlockSize;
328   EFI_STATUS      Status;
329 
330   DEBUG ((EFI_D_VERBOSE, "%a: Configuration=\"%s\"\n", __FUNCTION__,
331     Configuration));
332 
333   //
334   // the "read" step in RMW
335   //
336   Status = PlatformConfigToFormState (&MainFormState);
337   if (EFI_ERROR (Status)) {
338     *Progress = Configuration;
339     return Status;
340   }
341 
342   //
343   // the "modify" step in RMW
344   //
345   // (Update the binary form state. This update may be partial, which is why in
346   // general we must pre-load the form state from the platform config.)
347   //
348   BlockSize = sizeof MainFormState;
349   Status = gHiiConfigRouting->ConfigToBlock (gHiiConfigRouting, Configuration,
350                                 (VOID *) &MainFormState, &BlockSize, Progress);
351   if (EFI_ERROR (Status)) {
352     DEBUG ((EFI_D_ERROR, "%a: ConfigToBlock(): %r, Progress=\"%s\"\n",
353       __FUNCTION__, Status,
354       (Status == EFI_BUFFER_TOO_SMALL) ? NULL : *Progress));
355     return Status;
356   }
357 
358   //
359   // the "write" step in RMW
360   //
361   Status = FormStateToPlatformConfig (&MainFormState);
362   if (EFI_ERROR (Status)) {
363     *Progress = Configuration;
364   }
365   return Status;
366 }
367 
368 
369 STATIC
370 EFI_STATUS
371 EFIAPI
Callback(IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL * This,IN EFI_BROWSER_ACTION Action,IN EFI_QUESTION_ID QuestionId,IN UINT8 Type,IN OUT EFI_IFR_TYPE_VALUE * Value,OUT EFI_BROWSER_ACTION_REQUEST * ActionRequest)372 Callback (
373   IN     CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
374   IN     EFI_BROWSER_ACTION                     Action,
375   IN     EFI_QUESTION_ID                        QuestionId,
376   IN     UINT8                                  Type,
377   IN OUT EFI_IFR_TYPE_VALUE                     *Value,
378   OUT    EFI_BROWSER_ACTION_REQUEST             *ActionRequest
379   )
380 {
381   DEBUG ((EFI_D_VERBOSE, "%a: Action=0x%Lx QuestionId=%d Type=%d\n",
382     __FUNCTION__, (UINT64) Action, QuestionId, Type));
383 
384   if (Action != EFI_BROWSER_ACTION_CHANGED) {
385     return EFI_UNSUPPORTED;
386   }
387 
388   switch (QuestionId) {
389   case QUESTION_SAVE_EXIT:
390     *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT;
391     break;
392 
393   case QUESTION_DISCARD_EXIT:
394     *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT;
395     break;
396 
397   default:
398     break;
399   }
400 
401   return EFI_SUCCESS;
402 }
403 
404 
405 /**
406   Query and save all resolutions supported by the GOP.
407 
408   @param[in]  Gop          The Graphics Output Protocol instance to query.
409 
410   @param[out] NumGopModes  The number of modes supported by the GOP. On output,
411                            this parameter will be positive.
412 
413   @param[out] GopModes     On output, a dynamically allocated array containing
414                            the resolutions returned by the GOP. The caller is
415                            responsible for freeing the array after use.
416 
417   @retval EFI_UNSUPPORTED       No modes found.
418   @retval EFI_OUT_OF_RESOURCES  Failed to allocate GopModes.
419   @return                       Error codes from Gop->QueryMode().
420 
421 **/
422 STATIC
423 EFI_STATUS
424 EFIAPI
QueryGopModes(IN EFI_GRAPHICS_OUTPUT_PROTOCOL * Gop,OUT UINTN * NumGopModes,OUT GOP_MODE ** GopModes)425 QueryGopModes (
426   IN  EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop,
427   OUT UINTN                        *NumGopModes,
428   OUT GOP_MODE                     **GopModes
429   )
430 {
431   EFI_STATUS Status;
432   UINT32     ModeNumber;
433 
434   if (Gop->Mode->MaxMode == 0) {
435     return EFI_UNSUPPORTED;
436   }
437   *NumGopModes = Gop->Mode->MaxMode;
438 
439   *GopModes = AllocatePool (Gop->Mode->MaxMode * sizeof **GopModes);
440   if (*GopModes == NULL) {
441     return EFI_OUT_OF_RESOURCES;
442   }
443 
444   for (ModeNumber = 0; ModeNumber < Gop->Mode->MaxMode; ++ModeNumber) {
445     EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
446     UINTN                                SizeOfInfo;
447 
448     Status = Gop->QueryMode (Gop, ModeNumber, &SizeOfInfo, &Info);
449     if (EFI_ERROR (Status)) {
450       goto FreeGopModes;
451     }
452 
453     (*GopModes)[ModeNumber].X = Info->HorizontalResolution;
454     (*GopModes)[ModeNumber].Y = Info->VerticalResolution;
455     FreePool (Info);
456   }
457 
458   return EFI_SUCCESS;
459 
460 FreeGopModes:
461   FreePool (*GopModes);
462 
463   return Status;
464 }
465 
466 
467 /**
468   Create a set of "one-of-many" (ie. "drop down list") option IFR opcodes,
469   based on available GOP resolutions, to be placed under a "one-of-many" (ie.
470   "drop down list") opcode.
471 
472   @param[in]  PackageList   The package list with the formset and form for
473                             which the drop down options are produced. Option
474                             names are added as new strings to PackageList.
475 
476   @param[out] OpCodeBuffer  On output, a dynamically allocated opcode buffer
477                             with drop down list options corresponding to GOP
478                             resolutions. The caller is responsible for freeing
479                             OpCodeBuffer with HiiFreeOpCodeHandle() after use.
480 
481   @param[in]  NumGopModes   Number of entries in GopModes.
482 
483   @param[in]  GopModes      Array of resolutions retrieved from the GOP.
484 
485   @retval EFI_SUCESS  Opcodes have been successfully produced.
486 
487   @return             Status codes from underlying functions. PackageList may
488                       have been extended with new strings. OpCodeBuffer is
489                       unchanged.
490 **/
491 STATIC
492 EFI_STATUS
493 EFIAPI
CreateResolutionOptions(IN EFI_HII_HANDLE * PackageList,OUT VOID ** OpCodeBuffer,IN UINTN NumGopModes,IN GOP_MODE * GopModes)494 CreateResolutionOptions (
495   IN  EFI_HII_HANDLE  *PackageList,
496   OUT VOID            **OpCodeBuffer,
497   IN  UINTN           NumGopModes,
498   IN  GOP_MODE        *GopModes
499   )
500 {
501   EFI_STATUS Status;
502   VOID       *OutputBuffer;
503   UINTN      ModeNumber;
504 
505   OutputBuffer = HiiAllocateOpCodeHandle ();
506   if (OutputBuffer == NULL) {
507     return EFI_OUT_OF_RESOURCES;
508   }
509 
510   for (ModeNumber = 0; ModeNumber < NumGopModes; ++ModeNumber) {
511     CHAR16        Desc[MAXSIZE_RES_CUR];
512     EFI_STRING_ID NewString;
513     VOID          *OpCode;
514 
515     UnicodeSPrintAsciiFormat (Desc, sizeof Desc, "%Ldx%Ld",
516       (INT64) GopModes[ModeNumber].X, (INT64) GopModes[ModeNumber].Y);
517     NewString = HiiSetString (PackageList, 0 /* new string */, Desc,
518                   NULL /* for all languages */);
519     if (NewString == 0) {
520       Status = EFI_OUT_OF_RESOURCES;
521       goto FreeOutputBuffer;
522     }
523     OpCode = HiiCreateOneOfOptionOpCode (OutputBuffer, NewString,
524                0 /* Flags */, EFI_IFR_NUMERIC_SIZE_4, ModeNumber);
525     if (OpCode == NULL) {
526       Status = EFI_OUT_OF_RESOURCES;
527       goto FreeOutputBuffer;
528     }
529   }
530 
531   *OpCodeBuffer = OutputBuffer;
532   return EFI_SUCCESS;
533 
534 FreeOutputBuffer:
535   HiiFreeOpCodeHandle (OutputBuffer);
536 
537   return Status;
538 }
539 
540 
541 /**
542   Populate the form identified by the (PackageList, FormSetGuid, FormId)
543   triplet.
544 
545   The drop down list of video resolutions is generated from (NumGopModes,
546   GopModes).
547 
548   @retval EFI_SUCESS  Form successfully updated.
549   @return             Status codes from underlying functions.
550 
551 **/
552 STATIC
553 EFI_STATUS
554 EFIAPI
PopulateForm(IN EFI_HII_HANDLE * PackageList,IN EFI_GUID * FormSetGuid,IN EFI_FORM_ID FormId,IN UINTN NumGopModes,IN GOP_MODE * GopModes)555 PopulateForm (
556   IN  EFI_HII_HANDLE  *PackageList,
557   IN  EFI_GUID        *FormSetGuid,
558   IN  EFI_FORM_ID     FormId,
559   IN  UINTN           NumGopModes,
560   IN  GOP_MODE        *GopModes
561   )
562 {
563   EFI_STATUS         Status;
564   VOID               *OpCodeBuffer;
565   VOID               *OpCode;
566   EFI_IFR_GUID_LABEL *Anchor;
567   VOID               *OpCodeBuffer2;
568 
569   OpCodeBuffer2 = NULL;
570 
571   //
572   // 1. Allocate an empty opcode buffer.
573   //
574   OpCodeBuffer = HiiAllocateOpCodeHandle ();
575   if (OpCodeBuffer == NULL) {
576     return EFI_OUT_OF_RESOURCES;
577   }
578 
579   //
580   // 2. Create a label opcode (which is a Tiano extension) inside the buffer.
581   // The label's number must match the "anchor" label in the form.
582   //
583   OpCode = HiiCreateGuidOpCode (OpCodeBuffer, &gEfiIfrTianoGuid,
584              NULL /* optional copy origin */, sizeof *Anchor);
585   if (OpCode == NULL) {
586     Status = EFI_OUT_OF_RESOURCES;
587     goto FreeOpCodeBuffer;
588   }
589   Anchor               = OpCode;
590   Anchor->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
591   Anchor->Number       = LABEL_RES_NEXT;
592 
593   //
594   // 3. Create the opcodes inside the buffer that are to be inserted into the
595   // form.
596   //
597   // 3.1. Get a list of resolutions.
598   //
599   Status = CreateResolutionOptions (PackageList, &OpCodeBuffer2,
600              NumGopModes, GopModes);
601   if (EFI_ERROR (Status)) {
602     goto FreeOpCodeBuffer;
603   }
604 
605   //
606   // 3.2. Create a one-of-many question with the above options.
607   //
608   OpCode = HiiCreateOneOfOpCode (
609              OpCodeBuffer,                        // create opcode inside this
610                                                   //   opcode buffer,
611              QUESTION_RES_NEXT,                   // ID of question,
612              FORMSTATEID_MAIN_FORM,               // identifies form state
613                                                   //   storage,
614              (UINT16) OFFSET_OF (MAIN_FORM_STATE, // value of question stored
615                         NextPreferredResolution), //   at this offset,
616              STRING_TOKEN (STR_RES_NEXT),         // Prompt,
617              STRING_TOKEN (STR_RES_NEXT_HELP),    // Help,
618              0,                                   // QuestionFlags,
619              EFI_IFR_NUMERIC_SIZE_4,              // see sizeof
620                                                   //   NextPreferredResolution,
621              OpCodeBuffer2,                       // buffer with possible
622                                                   //   choices,
623              NULL                                 // DEFAULT opcodes
624              );
625   if (OpCode == NULL) {
626     Status = EFI_OUT_OF_RESOURCES;
627     goto FreeOpCodeBuffer2;
628   }
629 
630   //
631   // 4. Update the form with the opcode buffer.
632   //
633   Status = HiiUpdateForm (PackageList, FormSetGuid, FormId,
634              OpCodeBuffer, // buffer with head anchor, and new contents to be
635                            // inserted at it
636              NULL          // buffer with tail anchor, for deleting old
637                            // contents up to it
638              );
639 
640 FreeOpCodeBuffer2:
641   HiiFreeOpCodeHandle (OpCodeBuffer2);
642 
643 FreeOpCodeBuffer:
644   HiiFreeOpCodeHandle (OpCodeBuffer);
645 
646   return Status;
647 }
648 
649 
650 /**
651   Load and execute the platform configuration.
652 
653   @retval EFI_SUCCESS            Configuration loaded and executed.
654   @return                        Status codes from PlatformConfigLoad().
655 **/
656 STATIC
657 EFI_STATUS
658 EFIAPI
ExecutePlatformConfig(VOID)659 ExecutePlatformConfig (
660   VOID
661   )
662 {
663   EFI_STATUS      Status;
664   PLATFORM_CONFIG PlatformConfig;
665   UINT64          OptionalElements;
666 
667   Status = PlatformConfigLoad (&PlatformConfig, &OptionalElements);
668   if (EFI_ERROR (Status)) {
669     DEBUG (((Status == EFI_NOT_FOUND) ? EFI_D_VERBOSE : EFI_D_ERROR,
670       "%a: failed to load platform config: %r\n", __FUNCTION__, Status));
671     return Status;
672   }
673 
674   if (OptionalElements & PLATFORM_CONFIG_F_GRAPHICS_RESOLUTION) {
675     //
676     // Pass the preferred resolution to GraphicsConsoleDxe via dynamic PCDs.
677     //
678     PcdSet32 (PcdVideoHorizontalResolution,
679       PlatformConfig.HorizontalResolution);
680     PcdSet32 (PcdVideoVerticalResolution,
681       PlatformConfig.VerticalResolution);
682   }
683 
684   return EFI_SUCCESS;
685 }
686 
687 
688 /**
689   Notification callback for GOP interface installation.
690 
691   @param[in] Event    Event whose notification function is being invoked.
692 
693   @param[in] Context  The pointer to the notification function's context, which
694                       is implementation-dependent.
695 **/
696 STATIC
697 VOID
698 EFIAPI
GopInstalled(IN EFI_EVENT Event,IN VOID * Context)699 GopInstalled (
700   IN EFI_EVENT Event,
701   IN VOID      *Context
702   )
703 {
704   EFI_STATUS                   Status;
705   EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
706 
707   ASSERT (Event == mGopEvent);
708 
709   //
710   // Check further GOPs.
711   //
712   for (;;) {
713     mNumGopModes = 0;
714     mGopModes = NULL;
715 
716     Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid, mGopTracker,
717                     (VOID **) &Gop);
718     if (EFI_ERROR (Status)) {
719       return;
720     }
721 
722     Status = QueryGopModes (Gop, &mNumGopModes, &mGopModes);
723     if (EFI_ERROR (Status)) {
724       continue;
725     }
726 
727     Status = PopulateForm (mInstalledPackages, &gOvmfPlatformConfigGuid,
728                FORMID_MAIN_FORM, mNumGopModes, mGopModes);
729     if (EFI_ERROR (Status)) {
730       FreePool (mGopModes);
731       continue;
732     }
733 
734     break;
735   }
736 
737   //
738   // Success -- so uninstall this callback. Closing the event removes all
739   // pending notifications and all protocol registrations.
740   //
741   Status = gBS->CloseEvent (mGopEvent);
742   ASSERT_EFI_ERROR (Status);
743   mGopEvent = NULL;
744   mGopTracker = NULL;
745 }
746 
747 
748 /**
749   Entry point for this driver.
750 
751   @param[in] ImageHandle  Image handle of this driver.
752   @param[in] SystemTable  Pointer to SystemTable.
753 
754   @retval EFI_SUCESS            Driver has loaded successfully.
755   @retval EFI_OUT_OF_RESOURCES  Failed to install HII packages.
756   @return                       Error codes from lower level functions.
757 
758 **/
759 EFI_STATUS
760 EFIAPI
PlatformInit(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)761 PlatformInit (
762   IN  EFI_HANDLE        ImageHandle,
763   IN  EFI_SYSTEM_TABLE  *SystemTable
764   )
765 {
766   EFI_STATUS Status;
767 
768   ExecutePlatformConfig ();
769 
770   mConfigAccess.ExtractConfig = &ExtractConfig;
771   mConfigAccess.RouteConfig   = &RouteConfig;
772   mConfigAccess.Callback      = &Callback;
773 
774   //
775   // Declare ourselves suitable for HII communication.
776   //
777   Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle,
778                   &gEfiDevicePathProtocolGuid,      &mPkgDevicePath,
779                   &gEfiHiiConfigAccessProtocolGuid, &mConfigAccess,
780                   NULL);
781   if (EFI_ERROR (Status)) {
782     return Status;
783   }
784 
785   //
786   // Publish the HII package list to HII Database.
787   //
788   mInstalledPackages = HiiAddPackages (
789                          &gEfiCallerIdGuid,  // PackageListGuid
790                          ImageHandle,        // associated DeviceHandle
791                          PlatformDxeStrings, // 1st package
792                          PlatformFormsBin,   // 2nd package
793                          NULL                // terminator
794                          );
795   if (mInstalledPackages == NULL) {
796     Status = EFI_OUT_OF_RESOURCES;
797     goto UninstallProtocols;
798   }
799 
800   Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, &GopInstalled,
801                   NULL /* Context */, &mGopEvent);
802   if (EFI_ERROR (Status)) {
803     goto RemovePackages;
804   }
805 
806   Status = gBS->RegisterProtocolNotify (&gEfiGraphicsOutputProtocolGuid,
807                   mGopEvent, &mGopTracker);
808   if (EFI_ERROR (Status)) {
809     goto CloseGopEvent;
810   }
811 
812   //
813   // Check already installed GOPs.
814   //
815   Status = gBS->SignalEvent (mGopEvent);
816   ASSERT_EFI_ERROR (Status);
817 
818   return EFI_SUCCESS;
819 
820 CloseGopEvent:
821   gBS->CloseEvent (mGopEvent);
822 
823 RemovePackages:
824   HiiRemovePackages (mInstalledPackages);
825 
826 UninstallProtocols:
827   gBS->UninstallMultipleProtocolInterfaces (ImageHandle,
828          &gEfiDevicePathProtocolGuid,      &mPkgDevicePath,
829          &gEfiHiiConfigAccessProtocolGuid, &mConfigAccess,
830          NULL);
831   return Status;
832 }
833 
834 /**
835   Unload the driver.
836 
837   @param[in]  ImageHandle  Handle that identifies the image to evict.
838 
839   @retval EFI_SUCCESS  The image has been unloaded.
840 **/
841 EFI_STATUS
842 EFIAPI
PlatformUnload(IN EFI_HANDLE ImageHandle)843 PlatformUnload (
844   IN  EFI_HANDLE  ImageHandle
845   )
846 {
847   if (mGopEvent == NULL) {
848     //
849     // The GOP callback ran successfully and unregistered itself. Release the
850     // resources allocated there.
851     //
852     ASSERT (mGopModes != NULL);
853     FreePool (mGopModes);
854   } else {
855     //
856     // Otherwise we need to unregister the callback.
857     //
858     ASSERT (mGopModes == NULL);
859     gBS->CloseEvent (mGopEvent);
860   }
861 
862   //
863   // Release resources allocated by the entry point.
864   //
865   HiiRemovePackages (mInstalledPackages);
866   gBS->UninstallMultipleProtocolInterfaces (ImageHandle,
867          &gEfiDevicePathProtocolGuid,      &mPkgDevicePath,
868          &gEfiHiiConfigAccessProtocolGuid, &mConfigAccess,
869          NULL);
870   return EFI_SUCCESS;
871 }
872