]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/PlatformDxe/Platform.c
OvmfPkg: PlatformDxe: add save and discard buttons to the form
[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/DebugLib.h>
19 #include <Library/DevicePathLib.h>
20 #include <Library/HiiLib.h>
21 #include <Library/MemoryAllocationLib.h>
22 #include <Library/PrintLib.h>
23 #include <Library/UefiBootServicesTableLib.h>
24 #include <Library/UefiHiiServicesLib.h>
25 #include <Protocol/DevicePath.h>
26 #include <Protocol/GraphicsOutput.h>
27 #include <Protocol/HiiConfigAccess.h>
28 #include <Guid/MdeModuleHii.h>
29 #include <Guid/OvmfPlatformConfig.h>
30
31 #include "Platform.h"
32 #include "PlatformConfig.h"
33
34 //
35 // The HiiAddPackages() library function requires that any controller (or
36 // image) handle, to be associated with the HII packages under installation, be
37 // "decorated" with a device path. The tradition seems to be a vendor device
38 // path.
39 //
40 // We'd like to associate our HII packages with the driver's image handle. The
41 // first idea is to use the driver image's device path. Unfortunately, loaded
42 // images only come with an EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL (not the
43 // usual EFI_DEVICE_PATH_PROTOCOL), ie. a different GUID. In addition, even the
44 // EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL interface may be NULL, if the image
45 // has been loaded from an "unnamed" memory source buffer.
46 //
47 // Hence let's just stick with the tradition -- use a dedicated vendor device
48 // path, with the driver's FILE_GUID.
49 //
50 #pragma pack(1)
51 typedef struct {
52 VENDOR_DEVICE_PATH VendorDevicePath;
53 EFI_DEVICE_PATH_PROTOCOL End;
54 } PKG_DEVICE_PATH;
55 #pragma pack()
56
57 STATIC PKG_DEVICE_PATH mPkgDevicePath = {
58 {
59 {
60 HARDWARE_DEVICE_PATH,
61 HW_VENDOR_DP,
62 {
63 (UINT8) (sizeof (VENDOR_DEVICE_PATH) ),
64 (UINT8) (sizeof (VENDOR_DEVICE_PATH) >> 8)
65 }
66 },
67 EFI_CALLER_ID_GUID
68 },
69 {
70 END_DEVICE_PATH_TYPE,
71 END_ENTIRE_DEVICE_PATH_SUBTYPE,
72 {
73 (UINT8) (END_DEVICE_PATH_LENGTH ),
74 (UINT8) (END_DEVICE_PATH_LENGTH >> 8)
75 }
76 }
77 };
78
79 //
80 // The configuration interface between the HII engine (form display etc) and
81 // this driver.
82 //
83 STATIC EFI_HII_CONFIG_ACCESS_PROTOCOL mConfigAccess;
84
85 //
86 // The handle representing our list of packages after installation.
87 //
88 STATIC EFI_HII_HANDLE mInstalledPackages;
89
90 //
91 // The arrays below constitute our HII package list. They are auto-generated by
92 // the VFR compiler and linked into the driver image during the build.
93 //
94 // - The strings package receives its C identifier from the driver's BASE_NAME,
95 // plus "Strings".
96 //
97 // - The forms package receives its C identifier from the VFR file's basename,
98 // plus "Bin".
99 //
100 //
101 extern UINT8 PlatformDxeStrings[];
102 extern UINT8 PlatformFormsBin[];
103
104 //
105 // We want to be notified about GOP installations until we find one GOP
106 // interface that lets us populate the form.
107 //
108 STATIC EFI_EVENT mGopEvent;
109
110 //
111 // The registration record underneath this pointer allows us to iterate through
112 // the GOP instances one by one.
113 //
114 STATIC VOID *mGopTracker;
115
116 //
117 // Cache the resolutions we get from the GOP.
118 //
119 typedef struct {
120 UINT32 X;
121 UINT32 Y;
122 } GOP_MODE;
123
124 STATIC UINTN mNumGopModes;
125 STATIC GOP_MODE *mGopModes;
126
127
128 /**
129 This function is called by the HII machinery when it fetches the form state.
130
131 See the precise documentation in the UEFI spec.
132
133 @param[in] This The Config Access Protocol instance.
134
135 @param[in] Request A <ConfigRequest> format UCS-2 string describing the
136 query.
137
138 @param[out] Progress A pointer into Request on output, identifying the query
139 element where processing failed.
140
141 @param[out] Results A <MultiConfigAltResp> format UCS-2 string that has
142 all values filled in for the names in the Request
143 string.
144
145 @return Status codes from gHiiConfigRouting->BlockToConfig().
146
147 **/
148 STATIC
149 EFI_STATUS
150 EFIAPI
151 ExtractConfig (
152 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
153 IN CONST EFI_STRING Request,
154 OUT EFI_STRING *Progress,
155 OUT EFI_STRING *Results
156 )
157 {
158 MAIN_FORM_STATE MainFormState;
159 EFI_STATUS Status;
160
161 DEBUG ((EFI_D_VERBOSE, "%a: Request=\"%s\"\n", __FUNCTION__, Request));
162
163 StrnCpy ((CHAR16 *) MainFormState.CurrentPreferredResolution,
164 L"Unset", MAXSIZE_RES_CUR);
165 MainFormState.NextPreferredResolution = 0;
166 Status = gHiiConfigRouting->BlockToConfig (gHiiConfigRouting, Request,
167 (VOID *) &MainFormState, sizeof MainFormState,
168 Results, Progress);
169 if (EFI_ERROR (Status)) {
170 DEBUG ((EFI_D_ERROR, "%a: BlockToConfig(): %r, Progress=\"%s\"\n",
171 __FUNCTION__, Status, (Status == EFI_DEVICE_ERROR) ? NULL : *Progress));
172 } else {
173 DEBUG ((EFI_D_VERBOSE, "%a: Results=\"%s\"\n", __FUNCTION__, *Results));
174 }
175 return Status;
176 }
177
178
179 STATIC
180 EFI_STATUS
181 EFIAPI
182 RouteConfig (
183 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
184 IN CONST EFI_STRING Configuration,
185 OUT EFI_STRING *Progress
186 )
187 {
188 DEBUG ((EFI_D_VERBOSE, "%a: Configuration=\"%s\"\n", __FUNCTION__,
189 Configuration));
190 return EFI_SUCCESS;
191 }
192
193
194 STATIC
195 EFI_STATUS
196 EFIAPI
197 Callback (
198 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
199 IN EFI_BROWSER_ACTION Action,
200 IN EFI_QUESTION_ID QuestionId,
201 IN UINT8 Type,
202 IN OUT EFI_IFR_TYPE_VALUE *Value,
203 OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
204 )
205 {
206 DEBUG ((EFI_D_VERBOSE, "%a: Action=0x%Lx QuestionId=%d Type=%d\n",
207 __FUNCTION__, (UINT64) Action, QuestionId, Type));
208
209 if (Action != EFI_BROWSER_ACTION_CHANGED) {
210 return EFI_UNSUPPORTED;
211 }
212
213 switch (QuestionId) {
214 case QUESTION_SAVE_EXIT:
215 *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT;
216 break;
217
218 case QUESTION_DISCARD_EXIT:
219 *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT;
220 break;
221
222 default:
223 break;
224 }
225
226 return EFI_SUCCESS;
227 }
228
229
230 /**
231 Query and save all resolutions supported by the GOP.
232
233 @param[in] Gop The Graphics Output Protocol instance to query.
234
235 @param[out] NumGopModes The number of modes supported by the GOP. On output,
236 this parameter will be positive.
237
238 @param[out] GopModes On output, a dynamically allocated array containing
239 the resolutions returned by the GOP. The caller is
240 responsible for freeing the array after use.
241
242 @retval EFI_UNSUPPORTED No modes found.
243 @retval EFI_OUT_OF_RESOURCES Failed to allocate GopModes.
244 @return Error codes from Gop->QueryMode().
245
246 **/
247 STATIC
248 EFI_STATUS
249 EFIAPI
250 QueryGopModes (
251 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop,
252 OUT UINTN *NumGopModes,
253 OUT GOP_MODE **GopModes
254 )
255 {
256 EFI_STATUS Status;
257 UINT32 ModeNumber;
258
259 if (Gop->Mode->MaxMode == 0) {
260 return EFI_UNSUPPORTED;
261 }
262 *NumGopModes = Gop->Mode->MaxMode;
263
264 *GopModes = AllocatePool (Gop->Mode->MaxMode * sizeof **GopModes);
265 if (*GopModes == NULL) {
266 return EFI_OUT_OF_RESOURCES;
267 }
268
269 for (ModeNumber = 0; ModeNumber < Gop->Mode->MaxMode; ++ModeNumber) {
270 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
271 UINTN SizeOfInfo;
272
273 Status = Gop->QueryMode (Gop, ModeNumber, &SizeOfInfo, &Info);
274 if (EFI_ERROR (Status)) {
275 goto FreeGopModes;
276 }
277
278 (*GopModes)[ModeNumber].X = Info->HorizontalResolution;
279 (*GopModes)[ModeNumber].Y = Info->VerticalResolution;
280 FreePool (Info);
281 }
282
283 return EFI_SUCCESS;
284
285 FreeGopModes:
286 FreePool (*GopModes);
287
288 return Status;
289 }
290
291
292 /**
293 Create a set of "one-of-many" (ie. "drop down list") option IFR opcodes,
294 based on available GOP resolutions, to be placed under a "one-of-many" (ie.
295 "drop down list") opcode.
296
297 @param[in] PackageList The package list with the formset and form for
298 which the drop down options are produced. Option
299 names are added as new strings to PackageList.
300
301 @param[out] OpCodeBuffer On output, a dynamically allocated opcode buffer
302 with drop down list options corresponding to GOP
303 resolutions. The caller is responsible for freeing
304 OpCodeBuffer with HiiFreeOpCodeHandle() after use.
305
306 @param[in] NumGopModes Number of entries in GopModes.
307
308 @param[in] GopModes Array of resolutions retrieved from the GOP.
309
310 @retval EFI_SUCESS Opcodes have been successfully produced.
311
312 @return Status codes from underlying functions. PackageList may
313 have been extended with new strings. OpCodeBuffer is
314 unchanged.
315 **/
316 STATIC
317 EFI_STATUS
318 EFIAPI
319 CreateResolutionOptions (
320 IN EFI_HII_HANDLE *PackageList,
321 OUT VOID **OpCodeBuffer,
322 IN UINTN NumGopModes,
323 IN GOP_MODE *GopModes
324 )
325 {
326 EFI_STATUS Status;
327 VOID *OutputBuffer;
328 UINTN ModeNumber;
329
330 OutputBuffer = HiiAllocateOpCodeHandle ();
331 if (OutputBuffer == NULL) {
332 return EFI_OUT_OF_RESOURCES;
333 }
334
335 for (ModeNumber = 0; ModeNumber < NumGopModes; ++ModeNumber) {
336 CHAR16 Desc[MAXSIZE_RES_CUR];
337 EFI_STRING_ID NewString;
338 VOID *OpCode;
339
340 UnicodeSPrintAsciiFormat (Desc, sizeof Desc, "%Ldx%Ld",
341 (INT64) GopModes[ModeNumber].X, (INT64) GopModes[ModeNumber].Y);
342 NewString = HiiSetString (PackageList, 0 /* new string */, Desc,
343 NULL /* for all languages */);
344 if (NewString == 0) {
345 Status = EFI_OUT_OF_RESOURCES;
346 goto FreeOutputBuffer;
347 }
348 OpCode = HiiCreateOneOfOptionOpCode (OutputBuffer, NewString,
349 0 /* Flags */, EFI_IFR_NUMERIC_SIZE_4, ModeNumber);
350 if (OpCode == NULL) {
351 Status = EFI_OUT_OF_RESOURCES;
352 goto FreeOutputBuffer;
353 }
354 }
355
356 *OpCodeBuffer = OutputBuffer;
357 return EFI_SUCCESS;
358
359 FreeOutputBuffer:
360 HiiFreeOpCodeHandle (OutputBuffer);
361
362 return Status;
363 }
364
365
366 /**
367 Populate the form identified by the (PackageList, FormSetGuid, FormId)
368 triplet.
369
370 The drop down list of video resolutions is generated from (NumGopModes,
371 GopModes).
372
373 @retval EFI_SUCESS Form successfully updated.
374 @return Status codes from underlying functions.
375
376 **/
377 STATIC
378 EFI_STATUS
379 EFIAPI
380 PopulateForm (
381 IN EFI_HII_HANDLE *PackageList,
382 IN EFI_GUID *FormSetGuid,
383 IN EFI_FORM_ID FormId,
384 IN UINTN NumGopModes,
385 IN GOP_MODE *GopModes
386 )
387 {
388 EFI_STATUS Status;
389 VOID *OpCodeBuffer;
390 VOID *OpCode;
391 EFI_IFR_GUID_LABEL *Anchor;
392 VOID *OpCodeBuffer2;
393
394 OpCodeBuffer2 = NULL;
395
396 //
397 // 1. Allocate an empty opcode buffer.
398 //
399 OpCodeBuffer = HiiAllocateOpCodeHandle ();
400 if (OpCodeBuffer == NULL) {
401 return EFI_OUT_OF_RESOURCES;
402 }
403
404 //
405 // 2. Create a label opcode (which is a Tiano extension) inside the buffer.
406 // The label's number must match the "anchor" label in the form.
407 //
408 OpCode = HiiCreateGuidOpCode (OpCodeBuffer, &gEfiIfrTianoGuid,
409 NULL /* optional copy origin */, sizeof *Anchor);
410 if (OpCode == NULL) {
411 Status = EFI_OUT_OF_RESOURCES;
412 goto FreeOpCodeBuffer;
413 }
414 Anchor = OpCode;
415 Anchor->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
416 Anchor->Number = LABEL_RES_NEXT;
417
418 //
419 // 3. Create the opcodes inside the buffer that are to be inserted into the
420 // form.
421 //
422 // 3.1. Get a list of resolutions.
423 //
424 Status = CreateResolutionOptions (PackageList, &OpCodeBuffer2,
425 NumGopModes, GopModes);
426 if (EFI_ERROR (Status)) {
427 goto FreeOpCodeBuffer;
428 }
429
430 //
431 // 3.2. Create a one-of-many question with the above options.
432 //
433 OpCode = HiiCreateOneOfOpCode (
434 OpCodeBuffer, // create opcode inside this
435 // opcode buffer,
436 QUESTION_RES_NEXT, // ID of question,
437 FORMSTATEID_MAIN_FORM, // identifies form state
438 // storage,
439 (UINT16) OFFSET_OF (MAIN_FORM_STATE, // value of question stored
440 NextPreferredResolution), // at this offset,
441 STRING_TOKEN (STR_RES_NEXT), // Prompt,
442 STRING_TOKEN (STR_RES_NEXT_HELP), // Help,
443 0, // QuestionFlags,
444 EFI_IFR_NUMERIC_SIZE_4, // see sizeof
445 // NextPreferredResolution,
446 OpCodeBuffer2, // buffer with possible
447 // choices,
448 NULL // DEFAULT opcodes
449 );
450 if (OpCode == NULL) {
451 Status = EFI_OUT_OF_RESOURCES;
452 goto FreeOpCodeBuffer2;
453 }
454
455 //
456 // 4. Update the form with the opcode buffer.
457 //
458 Status = HiiUpdateForm (PackageList, FormSetGuid, FormId,
459 OpCodeBuffer, // buffer with head anchor, and new contents to be
460 // inserted at it
461 NULL // buffer with tail anchor, for deleting old
462 // contents up to it
463 );
464
465 FreeOpCodeBuffer2:
466 HiiFreeOpCodeHandle (OpCodeBuffer2);
467
468 FreeOpCodeBuffer:
469 HiiFreeOpCodeHandle (OpCodeBuffer);
470
471 return Status;
472 }
473
474
475 /**
476 Load and execute the platform configuration.
477
478 @retval EFI_SUCCESS Configuration loaded and executed.
479 @return Status codes from PlatformConfigLoad().
480 **/
481 STATIC
482 EFI_STATUS
483 EFIAPI
484 ExecutePlatformConfig (
485 VOID
486 )
487 {
488 EFI_STATUS Status;
489 PLATFORM_CONFIG PlatformConfig;
490 UINT64 OptionalElements;
491
492 Status = PlatformConfigLoad (&PlatformConfig, &OptionalElements);
493 if (EFI_ERROR (Status)) {
494 DEBUG (((Status == EFI_NOT_FOUND) ? EFI_D_VERBOSE : EFI_D_ERROR,
495 "%a: failed to load platform config: %r\n", __FUNCTION__, Status));
496 return Status;
497 }
498
499 if (OptionalElements & PLATFORM_CONFIG_F_GRAPHICS_RESOLUTION) {
500 //
501 // Pass the preferred resolution to GraphicsConsoleDxe via dynamic PCDs.
502 //
503 PcdSet32 (PcdVideoHorizontalResolution,
504 PlatformConfig.HorizontalResolution);
505 PcdSet32 (PcdVideoVerticalResolution,
506 PlatformConfig.VerticalResolution);
507 }
508
509 return EFI_SUCCESS;
510 }
511
512
513 /**
514 Notification callback for GOP interface installation.
515
516 @param[in] Event Event whose notification function is being invoked.
517
518 @param[in] Context The pointer to the notification function's context, which
519 is implementation-dependent.
520 **/
521 STATIC
522 VOID
523 EFIAPI
524 GopInstalled (
525 IN EFI_EVENT Event,
526 IN VOID *Context
527 )
528 {
529 EFI_STATUS Status;
530 EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
531
532 ASSERT (Event == mGopEvent);
533
534 //
535 // Check further GOPs.
536 //
537 for (;;) {
538 mNumGopModes = 0;
539 mGopModes = NULL;
540
541 Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid, mGopTracker,
542 (VOID **) &Gop);
543 if (EFI_ERROR (Status)) {
544 return;
545 }
546
547 Status = QueryGopModes (Gop, &mNumGopModes, &mGopModes);
548 if (EFI_ERROR (Status)) {
549 continue;
550 }
551
552 Status = PopulateForm (mInstalledPackages, &gOvmfPlatformConfigGuid,
553 FORMID_MAIN_FORM, mNumGopModes, mGopModes);
554 if (EFI_ERROR (Status)) {
555 FreePool (mGopModes);
556 continue;
557 }
558
559 break;
560 }
561
562 //
563 // Success -- so uninstall this callback. Closing the event removes all
564 // pending notifications and all protocol registrations.
565 //
566 Status = gBS->CloseEvent (mGopEvent);
567 ASSERT_EFI_ERROR (Status);
568 mGopEvent = NULL;
569 mGopTracker = NULL;
570 }
571
572
573 /**
574 Entry point for this driver.
575
576 @param[in] ImageHandle Image handle of this driver.
577 @param[in] SystemTable Pointer to SystemTable.
578
579 @retval EFI_SUCESS Driver has loaded successfully.
580 @retval EFI_OUT_OF_RESOURCES Failed to install HII packages.
581 @return Error codes from lower level functions.
582
583 **/
584 EFI_STATUS
585 EFIAPI
586 PlatformInit (
587 IN EFI_HANDLE ImageHandle,
588 IN EFI_SYSTEM_TABLE *SystemTable
589 )
590 {
591 EFI_STATUS Status;
592
593 ExecutePlatformConfig ();
594
595 mConfigAccess.ExtractConfig = &ExtractConfig;
596 mConfigAccess.RouteConfig = &RouteConfig;
597 mConfigAccess.Callback = &Callback;
598
599 //
600 // Declare ourselves suitable for HII communication.
601 //
602 Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle,
603 &gEfiDevicePathProtocolGuid, &mPkgDevicePath,
604 &gEfiHiiConfigAccessProtocolGuid, &mConfigAccess,
605 NULL);
606 if (EFI_ERROR (Status)) {
607 return Status;
608 }
609
610 //
611 // Publish the HII package list to HII Database.
612 //
613 mInstalledPackages = HiiAddPackages (
614 &gEfiCallerIdGuid, // PackageListGuid
615 ImageHandle, // associated DeviceHandle
616 PlatformDxeStrings, // 1st package
617 PlatformFormsBin, // 2nd package
618 NULL // terminator
619 );
620 if (mInstalledPackages == NULL) {
621 Status = EFI_OUT_OF_RESOURCES;
622 goto UninstallProtocols;
623 }
624
625 Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, &GopInstalled,
626 NULL /* Context */, &mGopEvent);
627 if (EFI_ERROR (Status)) {
628 goto RemovePackages;
629 }
630
631 Status = gBS->RegisterProtocolNotify (&gEfiGraphicsOutputProtocolGuid,
632 mGopEvent, &mGopTracker);
633 if (EFI_ERROR (Status)) {
634 goto CloseGopEvent;
635 }
636
637 //
638 // Check already installed GOPs.
639 //
640 Status = gBS->SignalEvent (mGopEvent);
641 ASSERT_EFI_ERROR (Status);
642
643 return EFI_SUCCESS;
644
645 CloseGopEvent:
646 gBS->CloseEvent (mGopEvent);
647
648 RemovePackages:
649 HiiRemovePackages (mInstalledPackages);
650
651 UninstallProtocols:
652 gBS->UninstallMultipleProtocolInterfaces (ImageHandle,
653 &gEfiDevicePathProtocolGuid, &mPkgDevicePath,
654 &gEfiHiiConfigAccessProtocolGuid, &mConfigAccess,
655 NULL);
656 return Status;
657 }
658
659 /**
660 Unload the driver.
661
662 @param[in] ImageHandle Handle that identifies the image to evict.
663
664 @retval EFI_SUCCESS The image has been unloaded.
665 **/
666 EFI_STATUS
667 EFIAPI
668 PlatformUnload (
669 IN EFI_HANDLE ImageHandle
670 )
671 {
672 if (mGopEvent == NULL) {
673 //
674 // The GOP callback ran successfully and unregistered itself. Release the
675 // resources allocated there.
676 //
677 ASSERT (mGopModes != NULL);
678 FreePool (mGopModes);
679 } else {
680 //
681 // Otherwise we need to unregister the callback.
682 //
683 ASSERT (mGopModes == NULL);
684 gBS->CloseEvent (mGopEvent);
685 }
686
687 //
688 // Release resources allocated by the entry point.
689 //
690 HiiRemovePackages (mInstalledPackages);
691 gBS->UninstallMultipleProtocolInterfaces (ImageHandle,
692 &gEfiDevicePathProtocolGuid, &mPkgDevicePath,
693 &gEfiHiiConfigAccessProtocolGuid, &mConfigAccess,
694 NULL);
695 return EFI_SUCCESS;
696 }