]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/PlatformDxe/Platform.c
OvmfPkg: PlatformDxe: connect ExtractConfig() to platform data
[mirror_edk2.git] / OvmfPkg / PlatformDxe / Platform.c
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
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
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 STATIC
262 EFI_STATUS
263 EFIAPI
264 RouteConfig (
265 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
266 IN CONST EFI_STRING Configuration,
267 OUT EFI_STRING *Progress
268 )
269 {
270 DEBUG ((EFI_D_VERBOSE, "%a: Configuration=\"%s\"\n", __FUNCTION__,
271 Configuration));
272 return EFI_SUCCESS;
273 }
274
275
276 STATIC
277 EFI_STATUS
278 EFIAPI
279 Callback (
280 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
281 IN EFI_BROWSER_ACTION Action,
282 IN EFI_QUESTION_ID QuestionId,
283 IN UINT8 Type,
284 IN OUT EFI_IFR_TYPE_VALUE *Value,
285 OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
286 )
287 {
288 DEBUG ((EFI_D_VERBOSE, "%a: Action=0x%Lx QuestionId=%d Type=%d\n",
289 __FUNCTION__, (UINT64) Action, QuestionId, Type));
290
291 if (Action != EFI_BROWSER_ACTION_CHANGED) {
292 return EFI_UNSUPPORTED;
293 }
294
295 switch (QuestionId) {
296 case QUESTION_SAVE_EXIT:
297 *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT;
298 break;
299
300 case QUESTION_DISCARD_EXIT:
301 *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT;
302 break;
303
304 default:
305 break;
306 }
307
308 return EFI_SUCCESS;
309 }
310
311
312 /**
313 Query and save all resolutions supported by the GOP.
314
315 @param[in] Gop The Graphics Output Protocol instance to query.
316
317 @param[out] NumGopModes The number of modes supported by the GOP. On output,
318 this parameter will be positive.
319
320 @param[out] GopModes On output, a dynamically allocated array containing
321 the resolutions returned by the GOP. The caller is
322 responsible for freeing the array after use.
323
324 @retval EFI_UNSUPPORTED No modes found.
325 @retval EFI_OUT_OF_RESOURCES Failed to allocate GopModes.
326 @return Error codes from Gop->QueryMode().
327
328 **/
329 STATIC
330 EFI_STATUS
331 EFIAPI
332 QueryGopModes (
333 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop,
334 OUT UINTN *NumGopModes,
335 OUT GOP_MODE **GopModes
336 )
337 {
338 EFI_STATUS Status;
339 UINT32 ModeNumber;
340
341 if (Gop->Mode->MaxMode == 0) {
342 return EFI_UNSUPPORTED;
343 }
344 *NumGopModes = Gop->Mode->MaxMode;
345
346 *GopModes = AllocatePool (Gop->Mode->MaxMode * sizeof **GopModes);
347 if (*GopModes == NULL) {
348 return EFI_OUT_OF_RESOURCES;
349 }
350
351 for (ModeNumber = 0; ModeNumber < Gop->Mode->MaxMode; ++ModeNumber) {
352 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
353 UINTN SizeOfInfo;
354
355 Status = Gop->QueryMode (Gop, ModeNumber, &SizeOfInfo, &Info);
356 if (EFI_ERROR (Status)) {
357 goto FreeGopModes;
358 }
359
360 (*GopModes)[ModeNumber].X = Info->HorizontalResolution;
361 (*GopModes)[ModeNumber].Y = Info->VerticalResolution;
362 FreePool (Info);
363 }
364
365 return EFI_SUCCESS;
366
367 FreeGopModes:
368 FreePool (*GopModes);
369
370 return Status;
371 }
372
373
374 /**
375 Create a set of "one-of-many" (ie. "drop down list") option IFR opcodes,
376 based on available GOP resolutions, to be placed under a "one-of-many" (ie.
377 "drop down list") opcode.
378
379 @param[in] PackageList The package list with the formset and form for
380 which the drop down options are produced. Option
381 names are added as new strings to PackageList.
382
383 @param[out] OpCodeBuffer On output, a dynamically allocated opcode buffer
384 with drop down list options corresponding to GOP
385 resolutions. The caller is responsible for freeing
386 OpCodeBuffer with HiiFreeOpCodeHandle() after use.
387
388 @param[in] NumGopModes Number of entries in GopModes.
389
390 @param[in] GopModes Array of resolutions retrieved from the GOP.
391
392 @retval EFI_SUCESS Opcodes have been successfully produced.
393
394 @return Status codes from underlying functions. PackageList may
395 have been extended with new strings. OpCodeBuffer is
396 unchanged.
397 **/
398 STATIC
399 EFI_STATUS
400 EFIAPI
401 CreateResolutionOptions (
402 IN EFI_HII_HANDLE *PackageList,
403 OUT VOID **OpCodeBuffer,
404 IN UINTN NumGopModes,
405 IN GOP_MODE *GopModes
406 )
407 {
408 EFI_STATUS Status;
409 VOID *OutputBuffer;
410 UINTN ModeNumber;
411
412 OutputBuffer = HiiAllocateOpCodeHandle ();
413 if (OutputBuffer == NULL) {
414 return EFI_OUT_OF_RESOURCES;
415 }
416
417 for (ModeNumber = 0; ModeNumber < NumGopModes; ++ModeNumber) {
418 CHAR16 Desc[MAXSIZE_RES_CUR];
419 EFI_STRING_ID NewString;
420 VOID *OpCode;
421
422 UnicodeSPrintAsciiFormat (Desc, sizeof Desc, "%Ldx%Ld",
423 (INT64) GopModes[ModeNumber].X, (INT64) GopModes[ModeNumber].Y);
424 NewString = HiiSetString (PackageList, 0 /* new string */, Desc,
425 NULL /* for all languages */);
426 if (NewString == 0) {
427 Status = EFI_OUT_OF_RESOURCES;
428 goto FreeOutputBuffer;
429 }
430 OpCode = HiiCreateOneOfOptionOpCode (OutputBuffer, NewString,
431 0 /* Flags */, EFI_IFR_NUMERIC_SIZE_4, ModeNumber);
432 if (OpCode == NULL) {
433 Status = EFI_OUT_OF_RESOURCES;
434 goto FreeOutputBuffer;
435 }
436 }
437
438 *OpCodeBuffer = OutputBuffer;
439 return EFI_SUCCESS;
440
441 FreeOutputBuffer:
442 HiiFreeOpCodeHandle (OutputBuffer);
443
444 return Status;
445 }
446
447
448 /**
449 Populate the form identified by the (PackageList, FormSetGuid, FormId)
450 triplet.
451
452 The drop down list of video resolutions is generated from (NumGopModes,
453 GopModes).
454
455 @retval EFI_SUCESS Form successfully updated.
456 @return Status codes from underlying functions.
457
458 **/
459 STATIC
460 EFI_STATUS
461 EFIAPI
462 PopulateForm (
463 IN EFI_HII_HANDLE *PackageList,
464 IN EFI_GUID *FormSetGuid,
465 IN EFI_FORM_ID FormId,
466 IN UINTN NumGopModes,
467 IN GOP_MODE *GopModes
468 )
469 {
470 EFI_STATUS Status;
471 VOID *OpCodeBuffer;
472 VOID *OpCode;
473 EFI_IFR_GUID_LABEL *Anchor;
474 VOID *OpCodeBuffer2;
475
476 OpCodeBuffer2 = NULL;
477
478 //
479 // 1. Allocate an empty opcode buffer.
480 //
481 OpCodeBuffer = HiiAllocateOpCodeHandle ();
482 if (OpCodeBuffer == NULL) {
483 return EFI_OUT_OF_RESOURCES;
484 }
485
486 //
487 // 2. Create a label opcode (which is a Tiano extension) inside the buffer.
488 // The label's number must match the "anchor" label in the form.
489 //
490 OpCode = HiiCreateGuidOpCode (OpCodeBuffer, &gEfiIfrTianoGuid,
491 NULL /* optional copy origin */, sizeof *Anchor);
492 if (OpCode == NULL) {
493 Status = EFI_OUT_OF_RESOURCES;
494 goto FreeOpCodeBuffer;
495 }
496 Anchor = OpCode;
497 Anchor->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
498 Anchor->Number = LABEL_RES_NEXT;
499
500 //
501 // 3. Create the opcodes inside the buffer that are to be inserted into the
502 // form.
503 //
504 // 3.1. Get a list of resolutions.
505 //
506 Status = CreateResolutionOptions (PackageList, &OpCodeBuffer2,
507 NumGopModes, GopModes);
508 if (EFI_ERROR (Status)) {
509 goto FreeOpCodeBuffer;
510 }
511
512 //
513 // 3.2. Create a one-of-many question with the above options.
514 //
515 OpCode = HiiCreateOneOfOpCode (
516 OpCodeBuffer, // create opcode inside this
517 // opcode buffer,
518 QUESTION_RES_NEXT, // ID of question,
519 FORMSTATEID_MAIN_FORM, // identifies form state
520 // storage,
521 (UINT16) OFFSET_OF (MAIN_FORM_STATE, // value of question stored
522 NextPreferredResolution), // at this offset,
523 STRING_TOKEN (STR_RES_NEXT), // Prompt,
524 STRING_TOKEN (STR_RES_NEXT_HELP), // Help,
525 0, // QuestionFlags,
526 EFI_IFR_NUMERIC_SIZE_4, // see sizeof
527 // NextPreferredResolution,
528 OpCodeBuffer2, // buffer with possible
529 // choices,
530 NULL // DEFAULT opcodes
531 );
532 if (OpCode == NULL) {
533 Status = EFI_OUT_OF_RESOURCES;
534 goto FreeOpCodeBuffer2;
535 }
536
537 //
538 // 4. Update the form with the opcode buffer.
539 //
540 Status = HiiUpdateForm (PackageList, FormSetGuid, FormId,
541 OpCodeBuffer, // buffer with head anchor, and new contents to be
542 // inserted at it
543 NULL // buffer with tail anchor, for deleting old
544 // contents up to it
545 );
546
547 FreeOpCodeBuffer2:
548 HiiFreeOpCodeHandle (OpCodeBuffer2);
549
550 FreeOpCodeBuffer:
551 HiiFreeOpCodeHandle (OpCodeBuffer);
552
553 return Status;
554 }
555
556
557 /**
558 Load and execute the platform configuration.
559
560 @retval EFI_SUCCESS Configuration loaded and executed.
561 @return Status codes from PlatformConfigLoad().
562 **/
563 STATIC
564 EFI_STATUS
565 EFIAPI
566 ExecutePlatformConfig (
567 VOID
568 )
569 {
570 EFI_STATUS Status;
571 PLATFORM_CONFIG PlatformConfig;
572 UINT64 OptionalElements;
573
574 Status = PlatformConfigLoad (&PlatformConfig, &OptionalElements);
575 if (EFI_ERROR (Status)) {
576 DEBUG (((Status == EFI_NOT_FOUND) ? EFI_D_VERBOSE : EFI_D_ERROR,
577 "%a: failed to load platform config: %r\n", __FUNCTION__, Status));
578 return Status;
579 }
580
581 if (OptionalElements & PLATFORM_CONFIG_F_GRAPHICS_RESOLUTION) {
582 //
583 // Pass the preferred resolution to GraphicsConsoleDxe via dynamic PCDs.
584 //
585 PcdSet32 (PcdVideoHorizontalResolution,
586 PlatformConfig.HorizontalResolution);
587 PcdSet32 (PcdVideoVerticalResolution,
588 PlatformConfig.VerticalResolution);
589 }
590
591 return EFI_SUCCESS;
592 }
593
594
595 /**
596 Notification callback for GOP interface installation.
597
598 @param[in] Event Event whose notification function is being invoked.
599
600 @param[in] Context The pointer to the notification function's context, which
601 is implementation-dependent.
602 **/
603 STATIC
604 VOID
605 EFIAPI
606 GopInstalled (
607 IN EFI_EVENT Event,
608 IN VOID *Context
609 )
610 {
611 EFI_STATUS Status;
612 EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
613
614 ASSERT (Event == mGopEvent);
615
616 //
617 // Check further GOPs.
618 //
619 for (;;) {
620 mNumGopModes = 0;
621 mGopModes = NULL;
622
623 Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid, mGopTracker,
624 (VOID **) &Gop);
625 if (EFI_ERROR (Status)) {
626 return;
627 }
628
629 Status = QueryGopModes (Gop, &mNumGopModes, &mGopModes);
630 if (EFI_ERROR (Status)) {
631 continue;
632 }
633
634 Status = PopulateForm (mInstalledPackages, &gOvmfPlatformConfigGuid,
635 FORMID_MAIN_FORM, mNumGopModes, mGopModes);
636 if (EFI_ERROR (Status)) {
637 FreePool (mGopModes);
638 continue;
639 }
640
641 break;
642 }
643
644 //
645 // Success -- so uninstall this callback. Closing the event removes all
646 // pending notifications and all protocol registrations.
647 //
648 Status = gBS->CloseEvent (mGopEvent);
649 ASSERT_EFI_ERROR (Status);
650 mGopEvent = NULL;
651 mGopTracker = NULL;
652 }
653
654
655 /**
656 Entry point for this driver.
657
658 @param[in] ImageHandle Image handle of this driver.
659 @param[in] SystemTable Pointer to SystemTable.
660
661 @retval EFI_SUCESS Driver has loaded successfully.
662 @retval EFI_OUT_OF_RESOURCES Failed to install HII packages.
663 @return Error codes from lower level functions.
664
665 **/
666 EFI_STATUS
667 EFIAPI
668 PlatformInit (
669 IN EFI_HANDLE ImageHandle,
670 IN EFI_SYSTEM_TABLE *SystemTable
671 )
672 {
673 EFI_STATUS Status;
674
675 ExecutePlatformConfig ();
676
677 mConfigAccess.ExtractConfig = &ExtractConfig;
678 mConfigAccess.RouteConfig = &RouteConfig;
679 mConfigAccess.Callback = &Callback;
680
681 //
682 // Declare ourselves suitable for HII communication.
683 //
684 Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle,
685 &gEfiDevicePathProtocolGuid, &mPkgDevicePath,
686 &gEfiHiiConfigAccessProtocolGuid, &mConfigAccess,
687 NULL);
688 if (EFI_ERROR (Status)) {
689 return Status;
690 }
691
692 //
693 // Publish the HII package list to HII Database.
694 //
695 mInstalledPackages = HiiAddPackages (
696 &gEfiCallerIdGuid, // PackageListGuid
697 ImageHandle, // associated DeviceHandle
698 PlatformDxeStrings, // 1st package
699 PlatformFormsBin, // 2nd package
700 NULL // terminator
701 );
702 if (mInstalledPackages == NULL) {
703 Status = EFI_OUT_OF_RESOURCES;
704 goto UninstallProtocols;
705 }
706
707 Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, &GopInstalled,
708 NULL /* Context */, &mGopEvent);
709 if (EFI_ERROR (Status)) {
710 goto RemovePackages;
711 }
712
713 Status = gBS->RegisterProtocolNotify (&gEfiGraphicsOutputProtocolGuid,
714 mGopEvent, &mGopTracker);
715 if (EFI_ERROR (Status)) {
716 goto CloseGopEvent;
717 }
718
719 //
720 // Check already installed GOPs.
721 //
722 Status = gBS->SignalEvent (mGopEvent);
723 ASSERT_EFI_ERROR (Status);
724
725 return EFI_SUCCESS;
726
727 CloseGopEvent:
728 gBS->CloseEvent (mGopEvent);
729
730 RemovePackages:
731 HiiRemovePackages (mInstalledPackages);
732
733 UninstallProtocols:
734 gBS->UninstallMultipleProtocolInterfaces (ImageHandle,
735 &gEfiDevicePathProtocolGuid, &mPkgDevicePath,
736 &gEfiHiiConfigAccessProtocolGuid, &mConfigAccess,
737 NULL);
738 return Status;
739 }
740
741 /**
742 Unload the driver.
743
744 @param[in] ImageHandle Handle that identifies the image to evict.
745
746 @retval EFI_SUCCESS The image has been unloaded.
747 **/
748 EFI_STATUS
749 EFIAPI
750 PlatformUnload (
751 IN EFI_HANDLE ImageHandle
752 )
753 {
754 if (mGopEvent == NULL) {
755 //
756 // The GOP callback ran successfully and unregistered itself. Release the
757 // resources allocated there.
758 //
759 ASSERT (mGopModes != NULL);
760 FreePool (mGopModes);
761 } else {
762 //
763 // Otherwise we need to unregister the callback.
764 //
765 ASSERT (mGopModes == NULL);
766 gBS->CloseEvent (mGopEvent);
767 }
768
769 //
770 // Release resources allocated by the entry point.
771 //
772 HiiRemovePackages (mInstalledPackages);
773 gBS->UninstallMultipleProtocolInterfaces (ImageHandle,
774 &gEfiDevicePathProtocolGuid, &mPkgDevicePath,
775 &gEfiHiiConfigAccessProtocolGuid, &mConfigAccess,
776 NULL);
777 return EFI_SUCCESS;
778 }