]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Bhyve/Library/PlatformBootManagerLib/BdsPlatform.c
Add BhyvePkg, to support the bhyve hypervisor
[mirror_edk2.git] / OvmfPkg / Bhyve / Library / PlatformBootManagerLib / BdsPlatform.c
CommitLineData
656419f9
RC
1/** @file\r
2 Platform BDS customizations.\r
3\r
4 Copyright (c) 2020, Rebecca Cran <rebecca@bsdio.com>\r
5 Copyright (c) 2004 - 2019, Intel Corporation. All rights reserved.<BR>\r
6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
7\r
8**/\r
9\r
10#include "BdsPlatform.h"\r
11#include <Guid/RootBridgesConnectedEventGroup.h>\r
12#include <Protocol/FirmwareVolume2.h>\r
13#include <Library/PlatformBmPrintScLib.h>\r
14#include <Library/Tcg2PhysicalPresenceLib.h>\r
15\r
16#include <Protocol/BlockIo.h>\r
17\r
18//\r
19// Global data\r
20//\r
21\r
22VOID *mEfiDevPathNotifyReg;\r
23EFI_EVENT mEfiDevPathEvent;\r
24VOID *mEmuVariableEventReg;\r
25EFI_EVENT mEmuVariableEvent;\r
26UINT16 mHostBridgeDevId;\r
27\r
28//\r
29// Table of host IRQs matching PCI IRQs A-D\r
30// (for configuring PCI Interrupt Line register)\r
31//\r
32CONST UINT8 PciHostIrqs[] = {\r
33 0x0a, 0x0a, 0x0b, 0x0b\r
34};\r
35\r
36//\r
37// Type definitions\r
38//\r
39\r
40typedef\r
41EFI_STATUS\r
42(EFIAPI *PROTOCOL_INSTANCE_CALLBACK)(\r
43 IN EFI_HANDLE Handle,\r
44 IN VOID *Instance,\r
45 IN VOID *Context\r
46 );\r
47\r
48/**\r
49 @param[in] Handle - Handle of PCI device instance\r
50 @param[in] PciIo - PCI IO protocol instance\r
51 @param[in] Pci - PCI Header register block\r
52**/\r
53typedef\r
54EFI_STATUS\r
55(EFIAPI *VISIT_PCI_INSTANCE_CALLBACK)(\r
56 IN EFI_HANDLE Handle,\r
57 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
58 IN PCI_TYPE00 *Pci\r
59 );\r
60\r
61\r
62//\r
63// Function prototypes\r
64//\r
65\r
66EFI_STATUS\r
67VisitAllInstancesOfProtocol (\r
68 IN EFI_GUID *Id,\r
69 IN PROTOCOL_INSTANCE_CALLBACK CallBackFunction,\r
70 IN VOID *Context\r
71 );\r
72\r
73EFI_STATUS\r
74VisitAllPciInstancesOfProtocol (\r
75 IN VISIT_PCI_INSTANCE_CALLBACK CallBackFunction\r
76 );\r
77\r
78VOID\r
79InstallDevicePathCallback (\r
80 VOID\r
81 );\r
82\r
83VOID\r
84PlatformRegisterFvBootOption (\r
85 EFI_GUID *FileGuid,\r
86 CHAR16 *Description,\r
87 UINT32 Attributes\r
88 )\r
89{\r
90 EFI_STATUS Status;\r
91 INTN OptionIndex;\r
92 EFI_BOOT_MANAGER_LOAD_OPTION NewOption;\r
93 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
94 UINTN BootOptionCount;\r
95 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;\r
96 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
97 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
98\r
99 Status = gBS->HandleProtocol (\r
100 gImageHandle,\r
101 &gEfiLoadedImageProtocolGuid,\r
102 (VOID **) &LoadedImage\r
103 );\r
104 ASSERT_EFI_ERROR (Status);\r
105\r
106 EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);\r
107 DevicePath = DevicePathFromHandle (LoadedImage->DeviceHandle);\r
108 ASSERT (DevicePath != NULL);\r
109 DevicePath = AppendDevicePathNode (\r
110 DevicePath,\r
111 (EFI_DEVICE_PATH_PROTOCOL *) &FileNode\r
112 );\r
113 ASSERT (DevicePath != NULL);\r
114\r
115 Status = EfiBootManagerInitializeLoadOption (\r
116 &NewOption,\r
117 LoadOptionNumberUnassigned,\r
118 LoadOptionTypeBoot,\r
119 Attributes,\r
120 Description,\r
121 DevicePath,\r
122 NULL,\r
123 0\r
124 );\r
125 ASSERT_EFI_ERROR (Status);\r
126 FreePool (DevicePath);\r
127\r
128 BootOptions = EfiBootManagerGetLoadOptions (\r
129 &BootOptionCount, LoadOptionTypeBoot\r
130 );\r
131\r
132 OptionIndex = EfiBootManagerFindLoadOption (\r
133 &NewOption, BootOptions, BootOptionCount\r
134 );\r
135\r
136 if (OptionIndex == -1) {\r
137 Status = EfiBootManagerAddLoadOptionVariable (&NewOption, MAX_UINTN);\r
138 ASSERT_EFI_ERROR (Status);\r
139 }\r
140 EfiBootManagerFreeLoadOption (&NewOption);\r
141 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
142}\r
143\r
144/**\r
145 Remove all MemoryMapped(...)/FvFile(...) and Fv(...)/FvFile(...) boot options\r
146 whose device paths do not resolve exactly to an FvFile in the system.\r
147\r
148 This removes any boot options that point to binaries built into the firmware\r
149 and have become stale due to any of the following:\r
150 - DXEFV's base address or size changed (historical),\r
151 - DXEFV's FvNameGuid changed,\r
152 - the FILE_GUID of the pointed-to binary changed,\r
153 - the referenced binary is no longer built into the firmware.\r
154\r
155 EfiBootManagerFindLoadOption() used in PlatformRegisterFvBootOption() only\r
156 avoids exact duplicates.\r
157**/\r
158VOID\r
159RemoveStaleFvFileOptions (\r
160 VOID\r
161 )\r
162{\r
163 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
164 UINTN BootOptionCount;\r
165 UINTN Index;\r
166\r
167 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount,\r
168 LoadOptionTypeBoot);\r
169\r
170 for (Index = 0; Index < BootOptionCount; ++Index) {\r
171 EFI_DEVICE_PATH_PROTOCOL *Node1, *Node2, *SearchNode;\r
172 EFI_STATUS Status;\r
173 EFI_HANDLE FvHandle;\r
174\r
175 //\r
176 // If the device path starts with neither MemoryMapped(...) nor Fv(...),\r
177 // then keep the boot option.\r
178 //\r
179 Node1 = BootOptions[Index].FilePath;\r
180 if (!(DevicePathType (Node1) == HARDWARE_DEVICE_PATH &&\r
181 DevicePathSubType (Node1) == HW_MEMMAP_DP) &&\r
182 !(DevicePathType (Node1) == MEDIA_DEVICE_PATH &&\r
183 DevicePathSubType (Node1) == MEDIA_PIWG_FW_VOL_DP)) {\r
184 continue;\r
185 }\r
186\r
187 //\r
188 // If the second device path node is not FvFile(...), then keep the boot\r
189 // option.\r
190 //\r
191 Node2 = NextDevicePathNode (Node1);\r
192 if (DevicePathType (Node2) != MEDIA_DEVICE_PATH ||\r
193 DevicePathSubType (Node2) != MEDIA_PIWG_FW_FILE_DP) {\r
194 continue;\r
195 }\r
196\r
197 //\r
198 // Locate the Firmware Volume2 protocol instance that is denoted by the\r
199 // boot option. If this lookup fails (i.e., the boot option references a\r
200 // firmware volume that doesn't exist), then we'll proceed to delete the\r
201 // boot option.\r
202 //\r
203 SearchNode = Node1;\r
204 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid,\r
205 &SearchNode, &FvHandle);\r
206\r
207 if (!EFI_ERROR (Status)) {\r
208 //\r
209 // The firmware volume was found; now let's see if it contains the FvFile\r
210 // identified by GUID.\r
211 //\r
212 EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;\r
213 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFileNode;\r
214 UINTN BufferSize;\r
215 EFI_FV_FILETYPE FoundType;\r
216 EFI_FV_FILE_ATTRIBUTES FileAttributes;\r
217 UINT32 AuthenticationStatus;\r
218\r
219 Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid,\r
220 (VOID **)&FvProtocol);\r
221 ASSERT_EFI_ERROR (Status);\r
222\r
223 FvFileNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)Node2;\r
224 //\r
225 // Buffer==NULL means we request metadata only: BufferSize, FoundType,\r
226 // FileAttributes.\r
227 //\r
228 Status = FvProtocol->ReadFile (\r
229 FvProtocol,\r
230 &FvFileNode->FvFileName, // NameGuid\r
231 NULL, // Buffer\r
232 &BufferSize,\r
233 &FoundType,\r
234 &FileAttributes,\r
235 &AuthenticationStatus\r
236 );\r
237 if (!EFI_ERROR (Status)) {\r
238 //\r
239 // The FvFile was found. Keep the boot option.\r
240 //\r
241 continue;\r
242 }\r
243 }\r
244\r
245 //\r
246 // Delete the boot option.\r
247 //\r
248 Status = EfiBootManagerDeleteLoadOptionVariable (\r
249 BootOptions[Index].OptionNumber, LoadOptionTypeBoot);\r
250 DEBUG_CODE (\r
251 CHAR16 *DevicePathString;\r
252\r
253 DevicePathString = ConvertDevicePathToText(BootOptions[Index].FilePath,\r
254 FALSE, FALSE);\r
255 DEBUG ((\r
256 EFI_ERROR (Status) ? EFI_D_WARN : DEBUG_VERBOSE,\r
257 "%a: removing stale Boot#%04x %s: %r\n",\r
258 __FUNCTION__,\r
259 (UINT32)BootOptions[Index].OptionNumber,\r
260 DevicePathString == NULL ? L"<unavailable>" : DevicePathString,\r
261 Status\r
262 ));\r
263 if (DevicePathString != NULL) {\r
264 FreePool (DevicePathString);\r
265 }\r
266 );\r
267 }\r
268\r
269 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
270}\r
271\r
272VOID\r
273PlatformRegisterOptionsAndKeys (\r
274 VOID\r
275 )\r
276{\r
277 EFI_STATUS Status;\r
278 EFI_INPUT_KEY Enter;\r
279 EFI_INPUT_KEY F2;\r
280 EFI_INPUT_KEY Esc;\r
281 EFI_BOOT_MANAGER_LOAD_OPTION BootOption;\r
282\r
283 //\r
284 // Register ENTER as CONTINUE key\r
285 //\r
286 Enter.ScanCode = SCAN_NULL;\r
287 Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;\r
288 Status = EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);\r
289 ASSERT_EFI_ERROR (Status);\r
290\r
291 //\r
292 // Map F2 to Boot Manager Menu\r
293 //\r
294 F2.ScanCode = SCAN_F2;\r
295 F2.UnicodeChar = CHAR_NULL;\r
296 Esc.ScanCode = SCAN_ESC;\r
297 Esc.UnicodeChar = CHAR_NULL;\r
298 Status = EfiBootManagerGetBootManagerMenu (&BootOption);\r
299 ASSERT_EFI_ERROR (Status);\r
300 Status = EfiBootManagerAddKeyOptionVariable (\r
301 NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL\r
302 );\r
303 ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);\r
304 Status = EfiBootManagerAddKeyOptionVariable (\r
305 NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL\r
306 );\r
307 ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);\r
308}\r
309\r
310EFI_STATUS\r
311EFIAPI\r
312ConnectRootBridge (\r
313 IN EFI_HANDLE RootBridgeHandle,\r
314 IN VOID *Instance,\r
315 IN VOID *Context\r
316 );\r
317\r
318STATIC\r
319EFI_STATUS\r
320EFIAPI\r
321ConnectVirtioPciRng (\r
322 IN EFI_HANDLE Handle,\r
323 IN VOID *Instance,\r
324 IN VOID *Context\r
325 );\r
326\r
327STATIC\r
328VOID\r
329SaveS3BootScript (\r
330 VOID\r
331 );\r
332\r
333//\r
334// BDS Platform Functions\r
335//\r
336/**\r
337 Do the platform init, can be customized by OEM/IBV\r
338\r
339 Possible things that can be done in PlatformBootManagerBeforeConsole:\r
340\r
341 > Update console variable: 1. include hot-plug devices;\r
342 > 2. Clear ConIn and add SOL for AMT\r
343 > Register new Driver#### or Boot####\r
344 > Register new Key####: e.g.: F12\r
345 > Signal ReadyToLock event\r
346 > Authentication action: 1. connect Auth devices;\r
347 > 2. Identify auto logon user.\r
348**/\r
349VOID\r
350EFIAPI\r
351PlatformBootManagerBeforeConsole (\r
352 VOID\r
353 )\r
354{\r
355 EFI_HANDLE Handle;\r
356 EFI_STATUS Status;\r
357\r
358 DEBUG ((DEBUG_INFO, "PlatformBootManagerBeforeConsole\n"));\r
359 InstallDevicePathCallback ();\r
360\r
361 VisitAllInstancesOfProtocol (&gEfiPciRootBridgeIoProtocolGuid,\r
362 ConnectRootBridge, NULL);\r
363\r
364 //\r
365 // Signal the ACPI platform driver that it can download QEMU ACPI tables.\r
366 //\r
367 EfiEventGroupSignal (&gRootBridgesConnectedEventGroupGuid);\r
368\r
369 //\r
370 // We can't signal End-of-Dxe earlier than this. Namely, End-of-Dxe triggers\r
371 // the preparation of S3 system information. That logic has a hard dependency\r
372 // on the presence of the FACS ACPI table. Since our ACPI tables are only\r
373 // installed after PCI enumeration completes, we must not trigger the S3 save\r
374 // earlier, hence we can't signal End-of-Dxe earlier.\r
375 //\r
376 EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid);\r
377\r
378 //\r
379 // Prevent further changes to LockBoxes or SMRAM.\r
380 //\r
381 Handle = NULL;\r
382 Status = gBS->InstallProtocolInterface (&Handle,\r
383 &gEfiDxeSmmReadyToLockProtocolGuid, EFI_NATIVE_INTERFACE,\r
384 NULL);\r
385 ASSERT_EFI_ERROR (Status);\r
386\r
387 //\r
388 // Dispatch deferred images after EndOfDxe event and ReadyToLock\r
389 // installation.\r
390 //\r
391 EfiBootManagerDispatchDeferredImages ();\r
392\r
393 PlatformInitializeConsole (gPlatformConsole);\r
394\r
395 PlatformRegisterOptionsAndKeys ();\r
396\r
397 //\r
398 // Install both VIRTIO_DEVICE_PROTOCOL and (dependent) EFI_RNG_PROTOCOL\r
399 // instances on Virtio PCI RNG devices.\r
400 //\r
401 VisitAllInstancesOfProtocol (&gEfiPciIoProtocolGuid, ConnectVirtioPciRng,\r
402 NULL);\r
403}\r
404\r
405\r
406EFI_STATUS\r
407EFIAPI\r
408ConnectRootBridge (\r
409 IN EFI_HANDLE RootBridgeHandle,\r
410 IN VOID *Instance,\r
411 IN VOID *Context\r
412 )\r
413{\r
414 EFI_STATUS Status;\r
415\r
416 //\r
417 // Make the PCI bus driver connect the root bridge, non-recursively. This\r
418 // will produce a number of child handles with PciIo on them.\r
419 //\r
420 Status = gBS->ConnectController (\r
421 RootBridgeHandle, // ControllerHandle\r
422 NULL, // DriverImageHandle\r
423 NULL, // RemainingDevicePath -- produce all\r
424 // children\r
425 FALSE // Recursive\r
426 );\r
427 return Status;\r
428}\r
429\r
430\r
431STATIC\r
432EFI_STATUS\r
433EFIAPI\r
434ConnectVirtioPciRng (\r
435 IN EFI_HANDLE Handle,\r
436 IN VOID *Instance,\r
437 IN VOID *Context\r
438 )\r
439{\r
440 EFI_PCI_IO_PROTOCOL *PciIo;\r
441 EFI_STATUS Status;\r
442 UINT16 VendorId;\r
443 UINT16 DeviceId;\r
444 UINT8 RevisionId;\r
445 BOOLEAN Virtio10;\r
446 UINT16 SubsystemId;\r
447\r
448 PciIo = Instance;\r
449\r
450 //\r
451 // Read and check VendorId.\r
452 //\r
453 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_VENDOR_ID_OFFSET,\r
454 1, &VendorId);\r
455 if (EFI_ERROR (Status)) {\r
456 goto Error;\r
457 }\r
458 if (VendorId != VIRTIO_VENDOR_ID) {\r
459 return EFI_SUCCESS;\r
460 }\r
461\r
462 //\r
463 // Read DeviceId and RevisionId.\r
464 //\r
465 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_DEVICE_ID_OFFSET,\r
466 1, &DeviceId);\r
467 if (EFI_ERROR (Status)) {\r
468 goto Error;\r
469 }\r
470 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_REVISION_ID_OFFSET,\r
471 1, &RevisionId);\r
472 if (EFI_ERROR (Status)) {\r
473 goto Error;\r
474 }\r
475\r
476 //\r
477 // From DeviceId and RevisionId, determine whether the device is a\r
478 // modern-only Virtio 1.0 device. In case of Virtio 1.0, DeviceId can\r
479 // immediately be restricted to VIRTIO_SUBSYSTEM_ENTROPY_SOURCE, and\r
480 // SubsystemId will only play a sanity-check role. Otherwise, DeviceId can\r
481 // only be sanity-checked, and SubsystemId will decide.\r
482 //\r
483 if (DeviceId == 0x1040 + VIRTIO_SUBSYSTEM_ENTROPY_SOURCE &&\r
484 RevisionId >= 0x01) {\r
485 Virtio10 = TRUE;\r
486 } else if (DeviceId >= 0x1000 && DeviceId <= 0x103F && RevisionId == 0x00) {\r
487 Virtio10 = FALSE;\r
488 } else {\r
489 return EFI_SUCCESS;\r
490 }\r
491\r
492 //\r
493 // Read and check SubsystemId as dictated by Virtio10.\r
494 //\r
495 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16,\r
496 PCI_SUBSYSTEM_ID_OFFSET, 1, &SubsystemId);\r
497 if (EFI_ERROR (Status)) {\r
498 goto Error;\r
499 }\r
500 if ((Virtio10 && SubsystemId >= 0x40) ||\r
501 (!Virtio10 && SubsystemId == VIRTIO_SUBSYSTEM_ENTROPY_SOURCE)) {\r
502 Status = gBS->ConnectController (\r
503 Handle, // ControllerHandle\r
504 NULL, // DriverImageHandle -- connect all drivers\r
505 NULL, // RemainingDevicePath -- produce all child handles\r
506 FALSE // Recursive -- don't follow child handles\r
507 );\r
508 if (EFI_ERROR (Status)) {\r
509 goto Error;\r
510 }\r
511 }\r
512 return EFI_SUCCESS;\r
513\r
514Error:\r
515 DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, Status));\r
516 return Status;\r
517}\r
518\r
519\r
520/**\r
521 Add IsaKeyboard to ConIn; add IsaSerial to ConOut, ConIn, ErrOut.\r
522\r
523 @param[in] DeviceHandle Handle of the LPC Bridge device.\r
524\r
525 @retval EFI_SUCCESS Console devices on the LPC bridge have been added to\r
526 ConOut, ConIn, and ErrOut.\r
527\r
528 @return Error codes, due to EFI_DEVICE_PATH_PROTOCOL missing\r
529 from DeviceHandle.\r
530**/\r
531EFI_STATUS\r
532PrepareLpcBridgeDevicePath (\r
533 IN EFI_HANDLE DeviceHandle\r
534 )\r
535{\r
536 EFI_STATUS Status;\r
537 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
538 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
539 CHAR16 *DevPathStr;\r
540\r
541 DevicePath = NULL;\r
542 Status = gBS->HandleProtocol (\r
543 DeviceHandle,\r
544 &gEfiDevicePathProtocolGuid,\r
545 (VOID*)&DevicePath\r
546 );\r
547 if (EFI_ERROR (Status)) {\r
548 return Status;\r
549 }\r
550 TempDevicePath = DevicePath;\r
551\r
552 //\r
553 // Register Keyboard\r
554 //\r
555 DevicePath = AppendDevicePathNode (DevicePath,\r
556 (EFI_DEVICE_PATH_PROTOCOL *)&gPnpPs2KeyboardDeviceNode);\r
557\r
558 EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);\r
559\r
560 //\r
561 // Register COM1\r
562 //\r
563 DevicePath = TempDevicePath;\r
564 gPnp16550ComPortDeviceNode.UID = 0;\r
565\r
566 DevicePath = AppendDevicePathNode (DevicePath,\r
567 (EFI_DEVICE_PATH_PROTOCOL *)&gPnp16550ComPortDeviceNode);\r
568 DevicePath = AppendDevicePathNode (DevicePath,\r
569 (EFI_DEVICE_PATH_PROTOCOL *)&gUartDeviceNode);\r
570 DevicePath = AppendDevicePathNode (DevicePath,\r
571 (EFI_DEVICE_PATH_PROTOCOL *)&gTerminalTypeDeviceNode);\r
572\r
573 //\r
574 // Print Device Path\r
575 //\r
576 DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE);\r
577 if (DevPathStr != NULL) {\r
578 DEBUG((\r
579 DEBUG_INFO,\r
580 "BdsPlatform.c+%d: COM%d DevPath: %s\n",\r
581 __LINE__,\r
582 gPnp16550ComPortDeviceNode.UID + 1,\r
583 DevPathStr\r
584 ));\r
585 FreePool(DevPathStr);\r
586 }\r
587\r
588 EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);\r
589 EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);\r
590 EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);\r
591\r
592 // Don't register COM2 which can be used for DBG instead so keep it clean\r
593\r
594 return EFI_SUCCESS;\r
595}\r
596\r
597EFI_STATUS\r
598GetGopDevicePath (\r
599 IN EFI_DEVICE_PATH_PROTOCOL *PciDevicePath,\r
600 OUT EFI_DEVICE_PATH_PROTOCOL **GopDevicePath\r
601 )\r
602{\r
603 UINTN Index;\r
604 EFI_STATUS Status;\r
605 EFI_HANDLE PciDeviceHandle;\r
606 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
607 EFI_DEVICE_PATH_PROTOCOL *TempPciDevicePath;\r
608 UINTN GopHandleCount;\r
609 EFI_HANDLE *GopHandleBuffer;\r
610\r
611 if (PciDevicePath == NULL || GopDevicePath == NULL) {\r
612 return EFI_INVALID_PARAMETER;\r
613 }\r
614\r
615 //\r
616 // Initialize the GopDevicePath to be PciDevicePath\r
617 //\r
618 *GopDevicePath = PciDevicePath;\r
619 TempPciDevicePath = PciDevicePath;\r
620\r
621 Status = gBS->LocateDevicePath (\r
622 &gEfiDevicePathProtocolGuid,\r
623 &TempPciDevicePath,\r
624 &PciDeviceHandle\r
625 );\r
626 if (EFI_ERROR (Status)) {\r
627 return Status;\r
628 }\r
629\r
630 //\r
631 // Try to connect this handle, so that GOP driver could start on this\r
632 // device and create child handles with GraphicsOutput Protocol installed\r
633 // on them, then we get device paths of these child handles and select\r
634 // them as possible console device.\r
635 //\r
636 gBS->ConnectController (PciDeviceHandle, NULL, NULL, FALSE);\r
637\r
638 Status = gBS->LocateHandleBuffer (\r
639 ByProtocol,\r
640 &gEfiGraphicsOutputProtocolGuid,\r
641 NULL,\r
642 &GopHandleCount,\r
643 &GopHandleBuffer\r
644 );\r
645 if (!EFI_ERROR (Status)) {\r
646 //\r
647 // Add all the child handles as possible Console Device\r
648 //\r
649 for (Index = 0; Index < GopHandleCount; Index++) {\r
650 Status = gBS->HandleProtocol (GopHandleBuffer[Index],\r
651 &gEfiDevicePathProtocolGuid, (VOID*)&TempDevicePath);\r
652 if (EFI_ERROR (Status)) {\r
653 continue;\r
654 }\r
655 if (CompareMem (\r
656 PciDevicePath,\r
657 TempDevicePath,\r
658 GetDevicePathSize (PciDevicePath) - END_DEVICE_PATH_LENGTH\r
659 ) == 0) {\r
660 //\r
661 // In current implementation, we only enable one of the child handles\r
662 // as console device, i.e. sotre one of the child handle's device\r
663 // path to variable "ConOut"\r
664 // In future, we could select all child handles to be console device\r
665 //\r
666\r
667 *GopDevicePath = TempDevicePath;\r
668\r
669 //\r
670 // Delete the PCI device's path that added by\r
671 // GetPlugInPciVgaDevicePath(). Add the integrity GOP device path.\r
672 //\r
673 EfiBootManagerUpdateConsoleVariable (ConOutDev, NULL, PciDevicePath);\r
674 EfiBootManagerUpdateConsoleVariable (ConOutDev, TempDevicePath, NULL);\r
675 }\r
676 }\r
677 gBS->FreePool (GopHandleBuffer);\r
678 }\r
679\r
680 return EFI_SUCCESS;\r
681}\r
682\r
683/**\r
684 Add PCI display to ConOut.\r
685\r
686 @param[in] DeviceHandle Handle of the PCI display device.\r
687\r
688 @retval EFI_SUCCESS The PCI display device has been added to ConOut.\r
689\r
690 @return Error codes, due to EFI_DEVICE_PATH_PROTOCOL missing\r
691 from DeviceHandle.\r
692**/\r
693EFI_STATUS\r
694PreparePciDisplayDevicePath (\r
695 IN EFI_HANDLE DeviceHandle\r
696 )\r
697{\r
698 EFI_STATUS Status;\r
699 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
700 EFI_DEVICE_PATH_PROTOCOL *GopDevicePath;\r
701\r
702 DevicePath = NULL;\r
703 GopDevicePath = NULL;\r
704 Status = gBS->HandleProtocol (\r
705 DeviceHandle,\r
706 &gEfiDevicePathProtocolGuid,\r
707 (VOID*)&DevicePath\r
708 );\r
709 if (EFI_ERROR (Status)) {\r
710 return Status;\r
711 }\r
712\r
713 GetGopDevicePath (DevicePath, &GopDevicePath);\r
714 DevicePath = GopDevicePath;\r
715\r
716 EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);\r
717\r
718 return EFI_SUCCESS;\r
719}\r
720\r
721/**\r
722 Add PCI Serial to ConOut, ConIn, ErrOut.\r
723\r
724 @param[in] DeviceHandle Handle of the PCI serial device.\r
725\r
726 @retval EFI_SUCCESS The PCI serial device has been added to ConOut, ConIn,\r
727 ErrOut.\r
728\r
729 @return Error codes, due to EFI_DEVICE_PATH_PROTOCOL missing\r
730 from DeviceHandle.\r
731**/\r
732EFI_STATUS\r
733PreparePciSerialDevicePath (\r
734 IN EFI_HANDLE DeviceHandle\r
735 )\r
736{\r
737 EFI_STATUS Status;\r
738 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
739\r
740 DevicePath = NULL;\r
741 Status = gBS->HandleProtocol (\r
742 DeviceHandle,\r
743 &gEfiDevicePathProtocolGuid,\r
744 (VOID*)&DevicePath\r
745 );\r
746 if (EFI_ERROR (Status)) {\r
747 return Status;\r
748 }\r
749\r
750 DevicePath = AppendDevicePathNode (DevicePath,\r
751 (EFI_DEVICE_PATH_PROTOCOL *)&gUartDeviceNode);\r
752 DevicePath = AppendDevicePathNode (DevicePath,\r
753 (EFI_DEVICE_PATH_PROTOCOL *)&gTerminalTypeDeviceNode);\r
754\r
755 EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);\r
756 EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL);\r
757 EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);\r
758\r
759 return EFI_SUCCESS;\r
760}\r
761\r
762EFI_STATUS\r
763VisitAllInstancesOfProtocol (\r
764 IN EFI_GUID *Id,\r
765 IN PROTOCOL_INSTANCE_CALLBACK CallBackFunction,\r
766 IN VOID *Context\r
767 )\r
768{\r
769 EFI_STATUS Status;\r
770 UINTN HandleCount;\r
771 EFI_HANDLE *HandleBuffer;\r
772 UINTN Index;\r
773 VOID *Instance;\r
774\r
775 //\r
776 // Start to check all the PciIo to find all possible device\r
777 //\r
778 HandleCount = 0;\r
779 HandleBuffer = NULL;\r
780 Status = gBS->LocateHandleBuffer (\r
781 ByProtocol,\r
782 Id,\r
783 NULL,\r
784 &HandleCount,\r
785 &HandleBuffer\r
786 );\r
787 if (EFI_ERROR (Status)) {\r
788 return Status;\r
789 }\r
790\r
791 for (Index = 0; Index < HandleCount; Index++) {\r
792 Status = gBS->HandleProtocol (HandleBuffer[Index], Id, &Instance);\r
793 if (EFI_ERROR (Status)) {\r
794 continue;\r
795 }\r
796\r
797 Status = (*CallBackFunction) (\r
798 HandleBuffer[Index],\r
799 Instance,\r
800 Context\r
801 );\r
802 }\r
803\r
804 gBS->FreePool (HandleBuffer);\r
805\r
806 return EFI_SUCCESS;\r
807}\r
808\r
809\r
810EFI_STATUS\r
811EFIAPI\r
812VisitingAPciInstance (\r
813 IN EFI_HANDLE Handle,\r
814 IN VOID *Instance,\r
815 IN VOID *Context\r
816 )\r
817{\r
818 EFI_STATUS Status;\r
819 EFI_PCI_IO_PROTOCOL *PciIo;\r
820 PCI_TYPE00 Pci;\r
821\r
822 PciIo = (EFI_PCI_IO_PROTOCOL*) Instance;\r
823\r
824 //\r
825 // Check for all PCI device\r
826 //\r
827 Status = PciIo->Pci.Read (\r
828 PciIo,\r
829 EfiPciIoWidthUint32,\r
830 0,\r
831 sizeof (Pci) / sizeof (UINT32),\r
832 &Pci\r
833 );\r
834 if (EFI_ERROR (Status)) {\r
835 return Status;\r
836 }\r
837\r
838 return (*(VISIT_PCI_INSTANCE_CALLBACK)(UINTN) Context) (\r
839 Handle,\r
840 PciIo,\r
841 &Pci\r
842 );\r
843\r
844}\r
845\r
846\r
847\r
848EFI_STATUS\r
849VisitAllPciInstances (\r
850 IN VISIT_PCI_INSTANCE_CALLBACK CallBackFunction\r
851 )\r
852{\r
853 return VisitAllInstancesOfProtocol (\r
854 &gEfiPciIoProtocolGuid,\r
855 VisitingAPciInstance,\r
856 (VOID*)(UINTN) CallBackFunction\r
857 );\r
858}\r
859\r
860\r
861/**\r
862 Do platform specific PCI Device check and add them to\r
863 ConOut, ConIn, ErrOut.\r
864\r
865 @param[in] Handle - Handle of PCI device instance\r
866 @param[in] PciIo - PCI IO protocol instance\r
867 @param[in] Pci - PCI Header register block\r
868\r
869 @retval EFI_SUCCESS - PCI Device check and Console variable update\r
870 successfully.\r
871 @retval EFI_STATUS - PCI Device check or Console variable update fail.\r
872\r
873**/\r
874EFI_STATUS\r
875EFIAPI\r
876DetectAndPreparePlatformPciDevicePath (\r
877 IN EFI_HANDLE Handle,\r
878 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
879 IN PCI_TYPE00 *Pci\r
880 )\r
881{\r
882 EFI_STATUS Status;\r
883\r
884 Status = PciIo->Attributes (\r
885 PciIo,\r
886 EfiPciIoAttributeOperationEnable,\r
887 EFI_PCI_DEVICE_ENABLE,\r
888 NULL\r
889 );\r
890 ASSERT_EFI_ERROR (Status);\r
891\r
892 //\r
893 // Here we decide whether it is LPC Bridge\r
894 //\r
895 if ((IS_PCI_LPC (Pci)) ||\r
896 ((IS_PCI_ISA_PDECODE (Pci)) &&\r
897 (Pci->Hdr.VendorId == 0x8086) &&\r
898 (Pci->Hdr.DeviceId == 0x7000)\r
899 )\r
900 ) {\r
901 //\r
902 // Add IsaKeyboard to ConIn,\r
903 // add IsaSerial to ConOut, ConIn, ErrOut\r
904 //\r
905 DEBUG ((DEBUG_INFO, "Found LPC Bridge device\n"));\r
906 PrepareLpcBridgeDevicePath (Handle);\r
907 return EFI_SUCCESS;\r
908 }\r
909 //\r
910 // Here we decide which Serial device to enable in PCI bus\r
911 //\r
912 if (IS_PCI_16550SERIAL (Pci)) {\r
913 //\r
914 // Add them to ConOut, ConIn, ErrOut.\r
915 //\r
916 DEBUG ((DEBUG_INFO, "Found PCI 16550 SERIAL device\n"));\r
917 PreparePciSerialDevicePath (Handle);\r
918 return EFI_SUCCESS;\r
919 }\r
920\r
921 //\r
922 // Here we decide which display device to enable in PCI bus\r
923 //\r
924 if (IS_PCI_DISPLAY (Pci)) {\r
925 //\r
926 // Add them to ConOut.\r
927 //\r
928 DEBUG ((DEBUG_INFO, "Found PCI display device\n"));\r
929 PreparePciDisplayDevicePath (Handle);\r
930 return EFI_SUCCESS;\r
931 }\r
932\r
933 return Status;\r
934}\r
935\r
936\r
937/**\r
938 Connect the predefined platform default console device.\r
939\r
940 Always try to find and enable PCI display devices.\r
941\r
942 @param[in] PlatformConsole Predefined platform default console device array.\r
943**/\r
944VOID\r
945PlatformInitializeConsole (\r
946 IN PLATFORM_CONSOLE_CONNECT_ENTRY *PlatformConsole\r
947 )\r
948{\r
949 UINTN Index;\r
950\r
951 //\r
952 // Do platform specific PCI Device check and add them to ConOut, ConIn,\r
953 // ErrOut\r
954 //\r
955 VisitAllPciInstances (DetectAndPreparePlatformPciDevicePath);\r
956\r
957 //\r
958 // Have chance to connect the platform default console,\r
959 // the platform default console is the minimum device group\r
960 // the platform should support\r
961 //\r
962 for (Index = 0; PlatformConsole[Index].DevicePath != NULL; ++Index) {\r
963 //\r
964 // Update the console variable with the connect type\r
965 //\r
966 if ((PlatformConsole[Index].ConnectType & CONSOLE_IN) == CONSOLE_IN) {\r
967 EfiBootManagerUpdateConsoleVariable (ConIn,\r
968 PlatformConsole[Index].DevicePath, NULL);\r
969 }\r
970 if ((PlatformConsole[Index].ConnectType & CONSOLE_OUT) == CONSOLE_OUT) {\r
971 EfiBootManagerUpdateConsoleVariable (ConOut,\r
972 PlatformConsole[Index].DevicePath, NULL);\r
973 }\r
974 if ((PlatformConsole[Index].ConnectType & STD_ERROR) == STD_ERROR) {\r
975 EfiBootManagerUpdateConsoleVariable (ErrOut,\r
976 PlatformConsole[Index].DevicePath, NULL);\r
977 }\r
978 }\r
979}\r
980\r
981\r
982/**\r
983 Configure PCI Interrupt Line register for applicable devices\r
984 Ported from SeaBIOS, src/fw/pciinit.c, *_pci_slot_get_irq()\r
985\r
986 @param[in] Handle - Handle of PCI device instance\r
987 @param[in] PciIo - PCI IO protocol instance\r
988 @param[in] PciHdr - PCI Header register block\r
989\r
990 @retval EFI_SUCCESS - PCI Interrupt Line register configured successfully.\r
991\r
992**/\r
993EFI_STATUS\r
994EFIAPI\r
995SetPciIntLine (\r
996 IN EFI_HANDLE Handle,\r
997 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
998 IN PCI_TYPE00 *PciHdr\r
999 )\r
1000{\r
1001 EFI_DEVICE_PATH_PROTOCOL *DevPathNode;\r
1002 EFI_DEVICE_PATH_PROTOCOL *DevPath;\r
1003 UINTN RootSlot;\r
1004 UINTN Idx;\r
1005 UINT8 IrqLine;\r
1006 EFI_STATUS Status;\r
1007 UINT32 RootBusNumber;\r
1008\r
1009 Status = EFI_SUCCESS;\r
1010\r
1011 if (PciHdr->Device.InterruptPin != 0) {\r
1012\r
1013 DevPathNode = DevicePathFromHandle (Handle);\r
1014 ASSERT (DevPathNode != NULL);\r
1015 DevPath = DevPathNode;\r
1016\r
1017 RootBusNumber = 0;\r
1018 if (DevicePathType (DevPathNode) == ACPI_DEVICE_PATH &&\r
1019 DevicePathSubType (DevPathNode) == ACPI_DP &&\r
1020 ((ACPI_HID_DEVICE_PATH *)DevPathNode)->HID == EISA_PNP_ID(0x0A03)) {\r
1021 RootBusNumber = ((ACPI_HID_DEVICE_PATH *)DevPathNode)->UID;\r
1022 }\r
1023\r
1024 //\r
1025 // Compute index into PciHostIrqs[] table by walking\r
1026 // the device path and adding up all device numbers\r
1027 //\r
1028 Status = EFI_NOT_FOUND;\r
1029 RootSlot = 0;\r
1030 Idx = PciHdr->Device.InterruptPin - 1;\r
1031 while (!IsDevicePathEnd (DevPathNode)) {\r
1032 if (DevicePathType (DevPathNode) == HARDWARE_DEVICE_PATH &&\r
1033 DevicePathSubType (DevPathNode) == HW_PCI_DP) {\r
1034\r
1035 Idx += ((PCI_DEVICE_PATH *)DevPathNode)->Device;\r
1036\r
1037 //\r
1038 // Unlike SeaBIOS, which starts climbing from the leaf device\r
1039 // up toward the root, we traverse the device path starting at\r
1040 // the root moving toward the leaf node.\r
1041 // The slot number of the top-level parent bridge is needed for\r
1042 // Q35 cases with more than 24 slots on the root bus.\r
1043 //\r
1044 if (Status != EFI_SUCCESS) {\r
1045 Status = EFI_SUCCESS;\r
1046 RootSlot = ((PCI_DEVICE_PATH *)DevPathNode)->Device;\r
1047 }\r
1048 }\r
1049\r
1050 DevPathNode = NextDevicePathNode (DevPathNode);\r
1051 }\r
1052 if (EFI_ERROR (Status)) {\r
1053 return Status;\r
1054 }\r
1055 if (RootBusNumber == 0 && RootSlot == 0) {\r
1056 DEBUG((\r
1057 DEBUG_ERROR,\r
1058 "%a: PCI host bridge (00:00.0) should have no interrupts!\n",\r
1059 __FUNCTION__\r
1060 ));\r
1061 ASSERT (FALSE);\r
1062 }\r
1063\r
1064 //\r
1065 // Final PciHostIrqs[] index calculation depends on the platform\r
1066 // and should match SeaBIOS src/fw/pciinit.c *_pci_slot_get_irq()\r
1067 //\r
1068 switch (mHostBridgeDevId) {\r
1069 case 0x1275: // BHYVE\r
1070 case INTEL_82441_DEVICE_ID:\r
1071 Idx -= 1;\r
1072 break;\r
1073 case INTEL_Q35_MCH_DEVICE_ID:\r
1074 //\r
1075 // SeaBIOS contains the following comment:\r
1076 // "Slots 0-24 rotate slot:pin mapping similar to piix above, but\r
1077 // with a different starting index - see q35-acpi-dsdt.dsl.\r
1078 //\r
1079 // Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H)"\r
1080 //\r
1081 if (RootSlot > 24) {\r
1082 //\r
1083 // in this case, subtract back out RootSlot from Idx\r
1084 // (SeaBIOS never adds it to begin with, but that would make our\r
1085 // device path traversal loop above too awkward)\r
1086 //\r
1087 Idx -= RootSlot;\r
1088 }\r
1089 break;\r
1090 default:\r
1091 ASSERT (FALSE); // should never get here\r
1092 }\r
1093 Idx %= ARRAY_SIZE (PciHostIrqs);\r
1094 IrqLine = PciHostIrqs[Idx];\r
1095\r
1096 DEBUG_CODE_BEGIN ();\r
1097 {\r
1098 CHAR16 *DevPathString;\r
1099 STATIC CHAR16 Fallback[] = L"<failed to convert>";\r
1100 UINTN Segment, Bus, Device, Function;\r
1101\r
1102 DevPathString = ConvertDevicePathToText (DevPath, FALSE, FALSE);\r
1103 if (DevPathString == NULL) {\r
1104 DevPathString = Fallback;\r
1105 }\r
1106 Status = PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);\r
1107 ASSERT_EFI_ERROR (Status);\r
1108\r
1109 DEBUG ((DEBUG_VERBOSE, "%a: [%02x:%02x.%x] %s -> 0x%02x\n", __FUNCTION__,\r
1110 (UINT32)Bus, (UINT32)Device, (UINT32)Function, DevPathString,\r
1111 IrqLine));\r
1112\r
1113 if (DevPathString != Fallback) {\r
1114 FreePool (DevPathString);\r
1115 }\r
1116 }\r
1117 DEBUG_CODE_END ();\r
1118\r
1119 //\r
1120 // Set PCI Interrupt Line register for this device to PciHostIrqs[Idx]\r
1121 //\r
1122 Status = PciIo->Pci.Write (\r
1123 PciIo,\r
1124 EfiPciIoWidthUint8,\r
1125 PCI_INT_LINE_OFFSET,\r
1126 1,\r
1127 &IrqLine\r
1128 );\r
1129 }\r
1130\r
1131 return Status;\r
1132}\r
1133\r
1134\r
1135VOID\r
1136PciAcpiInitialization (\r
1137 )\r
1138{\r
1139 UINTN Pmba;\r
1140\r
1141 //\r
1142 // Query Host Bridge DID to determine platform type\r
1143 //\r
1144 mHostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);\r
1145 switch (mHostBridgeDevId) {\r
1146 case 0x1275: // BHYVE\r
1147 case INTEL_82441_DEVICE_ID:\r
1148 Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);\r
1149 //\r
1150 // 00:01.0 ISA Bridge (PIIX4) LNK routing targets\r
1151 //\r
1152 PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), 0x0b); // A\r
1153 PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), 0x0b); // B\r
1154 PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), 0x0a); // C\r
1155 PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), 0x0a); // D\r
1156 break;\r
1157 case INTEL_Q35_MCH_DEVICE_ID:\r
1158 Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);\r
1159 //\r
1160 // 00:1f.0 LPC Bridge (Q35) LNK routing targets\r
1161 //\r
1162 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x60), 0x0a); // A\r
1163 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x61), 0x0a); // B\r
1164 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x62), 0x0b); // C\r
1165 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x63), 0x0b); // D\r
1166 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x68), 0x0a); // E\r
1167 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x69), 0x0a); // F\r
1168 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6a), 0x0b); // G\r
1169 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6b), 0x0b); // H\r
1170 break;\r
1171 default:\r
1172 DEBUG ((DEBUG_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n",\r
1173 __FUNCTION__, mHostBridgeDevId));\r
1174 ASSERT (FALSE);\r
1175 return;\r
1176 }\r
1177\r
1178 //\r
1179 // Initialize PCI_INTERRUPT_LINE for applicable present PCI devices\r
1180 //\r
1181 VisitAllPciInstances (SetPciIntLine);\r
1182\r
1183 //\r
1184 // Set ACPI SCI_EN bit in PMCNTRL\r
1185 //\r
1186 IoOr16 ((PciRead32 (Pmba) & ~BIT0) + 4, BIT0);\r
1187}\r
1188\r
1189EFI_STATUS\r
1190EFIAPI\r
1191ConnectRecursivelyIfPciMassStorage (\r
1192 IN EFI_HANDLE Handle,\r
1193 IN EFI_PCI_IO_PROTOCOL *Instance,\r
1194 IN PCI_TYPE00 *PciHeader\r
1195 )\r
1196{\r
1197 EFI_STATUS Status;\r
1198 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
1199 CHAR16 *DevPathStr;\r
1200\r
1201 //\r
1202 // Recognize PCI Mass Storage, and Xen PCI devices\r
1203 //\r
1204 if (IS_CLASS1 (PciHeader, PCI_CLASS_MASS_STORAGE)) {\r
1205 DevicePath = NULL;\r
1206 Status = gBS->HandleProtocol (\r
1207 Handle,\r
1208 &gEfiDevicePathProtocolGuid,\r
1209 (VOID*)&DevicePath\r
1210 );\r
1211 if (EFI_ERROR (Status)) {\r
1212 return Status;\r
1213 }\r
1214\r
1215 //\r
1216 // Print Device Path\r
1217 //\r
1218 DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE);\r
1219 if (DevPathStr != NULL) {\r
1220 DEBUG((\r
1221 DEBUG_INFO,\r
1222 "Found %s device: %s\n",\r
1223 (IS_CLASS1 (PciHeader, PCI_CLASS_MASS_STORAGE) ?\r
1224 L"Mass Storage" :\r
1225 L"Xen"\r
1226 ),\r
1227 DevPathStr\r
1228 ));\r
1229 FreePool(DevPathStr);\r
1230 }\r
1231\r
1232 Status = gBS->ConnectController (Handle, NULL, NULL, TRUE);\r
1233 if (EFI_ERROR (Status)) {\r
1234 return Status;\r
1235 }\r
1236\r
1237 }\r
1238\r
1239 return EFI_SUCCESS;\r
1240}\r
1241\r
1242\r
1243/**\r
1244 This notification function is invoked when the\r
1245 EMU Variable FVB has been changed.\r
1246\r
1247 @param Event The event that occurred\r
1248 @param Context For EFI compatibility. Not used.\r
1249\r
1250**/\r
1251VOID\r
1252EFIAPI\r
1253EmuVariablesUpdatedCallback (\r
1254 IN EFI_EVENT Event,\r
1255 IN VOID *Context\r
1256 )\r
1257{\r
1258 DEBUG ((DEBUG_INFO, "EmuVariablesUpdatedCallback\n"));\r
1259 UpdateNvVarsOnFileSystem ();\r
1260}\r
1261\r
1262\r
1263EFI_STATUS\r
1264EFIAPI\r
1265VisitingFileSystemInstance (\r
1266 IN EFI_HANDLE Handle,\r
1267 IN VOID *Instance,\r
1268 IN VOID *Context\r
1269 )\r
1270{\r
1271 EFI_STATUS Status;\r
1272 STATIC BOOLEAN ConnectedToFileSystem = FALSE;\r
1273 RETURN_STATUS PcdStatus;\r
1274\r
1275 if (ConnectedToFileSystem) {\r
1276 return EFI_ALREADY_STARTED;\r
1277 }\r
1278\r
1279 Status = ConnectNvVarsToFileSystem (Handle);\r
1280 if (EFI_ERROR (Status)) {\r
1281 return Status;\r
1282 }\r
1283\r
1284 ConnectedToFileSystem = TRUE;\r
1285 mEmuVariableEvent =\r
1286 EfiCreateProtocolNotifyEvent (\r
1287 &gEfiDevicePathProtocolGuid,\r
1288 TPL_CALLBACK,\r
1289 EmuVariablesUpdatedCallback,\r
1290 NULL,\r
1291 &mEmuVariableEventReg\r
1292 );\r
1293 PcdStatus = PcdSet64S (PcdEmuVariableEvent,\r
1294 (UINT64)(UINTN) mEmuVariableEvent);\r
1295 ASSERT_RETURN_ERROR (PcdStatus);\r
1296\r
1297 return EFI_SUCCESS;\r
1298}\r
1299\r
1300\r
1301VOID\r
1302PlatformBdsRestoreNvVarsFromHardDisk (\r
1303 )\r
1304{\r
1305 VisitAllPciInstances (ConnectRecursivelyIfPciMassStorage);\r
1306 VisitAllInstancesOfProtocol (\r
1307 &gEfiSimpleFileSystemProtocolGuid,\r
1308 VisitingFileSystemInstance,\r
1309 NULL\r
1310 );\r
1311\r
1312}\r
1313\r
1314/**\r
1315 Connect with predefined platform connect sequence.\r
1316\r
1317 The OEM/IBV can customize with their own connect sequence.\r
1318**/\r
1319VOID\r
1320PlatformBdsConnectSequence (\r
1321 VOID\r
1322 )\r
1323{\r
1324 UINTN Index;\r
1325\r
1326 DEBUG ((DEBUG_INFO, "PlatformBdsConnectSequence\n"));\r
1327\r
1328 Index = 0;\r
1329\r
1330 //\r
1331 // Here we can get the customized platform connect sequence\r
1332 // Notes: we can connect with new variable which record the\r
1333 // last time boots connect device path sequence\r
1334 //\r
1335 while (gPlatformConnectSequence[Index] != NULL) {\r
1336 //\r
1337 // Build the platform boot option\r
1338 //\r
1339 EfiBootManagerConnectDevicePath (gPlatformConnectSequence[Index], NULL);\r
1340 Index++;\r
1341 }\r
1342\r
1343 //\r
1344 // Just use the simple policy to connect all devices\r
1345 //\r
1346 DEBUG ((DEBUG_INFO, "EfiBootManagerConnectAll\n"));\r
1347 EfiBootManagerConnectAll ();\r
1348}\r
1349\r
1350/**\r
1351 Save the S3 boot script.\r
1352\r
1353 Note that DxeSmmReadyToLock must be signaled after this function returns;\r
1354 otherwise the script wouldn't be saved actually.\r
1355**/\r
1356#if defined(__GNUC__)\r
1357__attribute__((unused))\r
1358#endif\r
1359STATIC\r
1360VOID\r
1361SaveS3BootScript (\r
1362 VOID\r
1363 )\r
1364{\r
1365 EFI_STATUS Status;\r
1366 EFI_S3_SAVE_STATE_PROTOCOL *BootScript;\r
1367 STATIC CONST UINT8 Info[] = { 0xDE, 0xAD, 0xBE, 0xEF };\r
1368\r
1369 Status = gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid, NULL,\r
1370 (VOID **) &BootScript);\r
1371 ASSERT_EFI_ERROR (Status);\r
1372\r
1373 //\r
1374 // Despite the opcode documentation in the PI spec, the protocol\r
1375 // implementation embeds a deep copy of the info in the boot script, rather\r
1376 // than storing just a pointer to runtime or NVS storage.\r
1377 //\r
1378 Status = BootScript->Write(BootScript, EFI_BOOT_SCRIPT_INFORMATION_OPCODE,\r
1379 (UINT32) sizeof Info,\r
1380 (EFI_PHYSICAL_ADDRESS)(UINTN) &Info);\r
1381 ASSERT_EFI_ERROR (Status);\r
1382}\r
1383\r
1384\r
1385/**\r
1386 Do the platform specific action after the console is ready\r
1387\r
1388 Possible things that can be done in PlatformBootManagerAfterConsole:\r
1389\r
1390 > Console post action:\r
1391 > Dynamically switch output mode from 100x31 to 80x25 for certain senarino\r
1392 > Signal console ready platform customized event\r
1393 > Run diagnostics like memory testing\r
1394 > Connect certain devices\r
1395 > Dispatch aditional option roms\r
1396 > Special boot: e.g.: USB boot, enter UI\r
1397**/\r
1398VOID\r
1399EFIAPI\r
1400PlatformBootManagerAfterConsole (\r
1401 VOID\r
1402 )\r
1403{\r
1404 EFI_BOOT_MODE BootMode;\r
1405\r
1406 DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole\n"));\r
1407\r
1408 if (PcdGetBool (PcdOvmfFlashVariablesEnable)) {\r
1409 DEBUG ((DEBUG_INFO, "PlatformBdsPolicyBehavior: not restoring NvVars "\r
1410 "from disk since flash variables appear to be supported.\n"));\r
1411 } else {\r
1412 //\r
1413 // Try to restore variables from the hard disk early so\r
1414 // they can be used for the other BDS connect operations.\r
1415 //\r
1416 /* XXX Calling this causes Keyboard to be removed from ConIn which\r
1417 results in unresponsive guest boot loaders in the GUI. Restore it\r
1418 when we figure out what is needed to get NvVars storage done\r
1419 properly.\r
1420 */\r
1421 /*PlatformBdsRestoreNvVarsFromHardDisk ();*/\r
1422 }\r
1423\r
1424 //\r
1425 // Get current Boot Mode\r
1426 //\r
1427 BootMode = GetBootModeHob ();\r
1428 DEBUG ((DEBUG_INFO, "Boot Mode:%x\n", BootMode));\r
1429\r
1430 //\r
1431 // Go the different platform policy with different boot mode\r
1432 // Notes: this part code can be change with the table policy\r
1433 //\r
1434 ASSERT (BootMode == BOOT_WITH_FULL_CONFIGURATION);\r
1435\r
1436 //\r
1437 // Logo show\r
1438 //\r
1439 BootLogoEnableLogo ();\r
1440\r
1441 //\r
1442 // Set PCI Interrupt Line registers and ACPI SCI_EN\r
1443 //\r
1444 PciAcpiInitialization ();\r
1445\r
1446 //\r
1447 // Process TPM PPI request\r
1448 //\r
1449 Tcg2PhysicalPresenceLibProcessRequest (NULL);\r
1450\r
1451 //\r
1452 // Perform some platform specific connect sequence\r
1453 //\r
1454 PlatformBdsConnectSequence ();\r
1455\r
1456 EfiBootManagerRefreshAllBootOption ();\r
1457\r
1458 //\r
1459 // Register UEFI Shell\r
1460 //\r
1461 PlatformRegisterFvBootOption (\r
1462 &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE\r
1463 );\r
1464\r
1465 RemoveStaleFvFileOptions ();\r
1466\r
1467 PlatformBmPrintScRegisterHandler ();\r
1468}\r
1469\r
1470/**\r
1471 This notification function is invoked when an instance of the\r
1472 EFI_DEVICE_PATH_PROTOCOL is produced.\r
1473\r
1474 @param Event The event that occurred\r
1475 @param Context For EFI compatibility. Not used.\r
1476\r
1477**/\r
1478VOID\r
1479EFIAPI\r
1480NotifyDevPath (\r
1481 IN EFI_EVENT Event,\r
1482 IN VOID *Context\r
1483 )\r
1484{\r
1485 EFI_HANDLE Handle;\r
1486 EFI_STATUS Status;\r
1487 UINTN BufferSize;\r
1488 EFI_DEVICE_PATH_PROTOCOL *DevPathNode;\r
1489 ATAPI_DEVICE_PATH *Atapi;\r
1490\r
1491 //\r
1492 // Examine all new handles\r
1493 //\r
1494 for (;;) {\r
1495 //\r
1496 // Get the next handle\r
1497 //\r
1498 BufferSize = sizeof (Handle);\r
1499 Status = gBS->LocateHandle (\r
1500 ByRegisterNotify,\r
1501 NULL,\r
1502 mEfiDevPathNotifyReg,\r
1503 &BufferSize,\r
1504 &Handle\r
1505 );\r
1506\r
1507 //\r
1508 // If not found, we're done\r
1509 //\r
1510 if (EFI_NOT_FOUND == Status) {\r
1511 break;\r
1512 }\r
1513\r
1514 if (EFI_ERROR (Status)) {\r
1515 continue;\r
1516 }\r
1517\r
1518 //\r
1519 // Get the DevicePath protocol on that handle\r
1520 //\r
1521 Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid,\r
1522 (VOID **)&DevPathNode);\r
1523 ASSERT_EFI_ERROR (Status);\r
1524\r
1525 while (!IsDevicePathEnd (DevPathNode)) {\r
1526 //\r
1527 // Find the handler to dump this device path node\r
1528 //\r
1529 if (\r
1530 (DevicePathType(DevPathNode) == MESSAGING_DEVICE_PATH) &&\r
1531 (DevicePathSubType(DevPathNode) == MSG_ATAPI_DP)\r
1532 ) {\r
1533 Atapi = (ATAPI_DEVICE_PATH*) DevPathNode;\r
1534 PciOr16 (\r
1535 PCI_LIB_ADDRESS (\r
1536 0,\r
1537 1,\r
1538 1,\r
1539 (Atapi->PrimarySecondary == 1) ? 0x42: 0x40\r
1540 ),\r
1541 BIT15\r
1542 );\r
1543 }\r
1544\r
1545 //\r
1546 // Next device path node\r
1547 //\r
1548 DevPathNode = NextDevicePathNode (DevPathNode);\r
1549 }\r
1550 }\r
1551\r
1552 return;\r
1553}\r
1554\r
1555\r
1556VOID\r
1557InstallDevicePathCallback (\r
1558 VOID\r
1559 )\r
1560{\r
1561 DEBUG ((DEBUG_INFO, "Registered NotifyDevPath Event\n"));\r
1562 mEfiDevPathEvent = EfiCreateProtocolNotifyEvent (\r
1563 &gEfiDevicePathProtocolGuid,\r
1564 TPL_CALLBACK,\r
1565 NotifyDevPath,\r
1566 NULL,\r
1567 &mEfiDevPathNotifyReg\r
1568 );\r
1569}\r
1570\r
1571/**\r
1572 This function is called each second during the boot manager waits the\r
1573 timeout.\r
1574\r
1575 @param TimeoutRemain The remaining timeout.\r
1576**/\r
1577VOID\r
1578EFIAPI\r
1579PlatformBootManagerWaitCallback (\r
1580 UINT16 TimeoutRemain\r
1581 )\r
1582{\r
1583 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black;\r
1584 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White;\r
1585 UINT16 Timeout;\r
1586\r
1587 Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
1588\r
1589 Black.Raw = 0x00000000;\r
1590 White.Raw = 0x00FFFFFF;\r
1591\r
1592 BootLogoUpdateProgress (\r
1593 White.Pixel,\r
1594 Black.Pixel,\r
1595 L"Start boot option",\r
1596 White.Pixel,\r
1597 (Timeout - TimeoutRemain) * 100 / Timeout,\r
1598 0\r
1599 );\r
1600}\r
1601\r
1602/**\r
1603 The function is called when no boot option could be launched,\r
1604 including platform recovery options and options pointing to applications\r
1605 built into firmware volumes.\r
1606\r
1607 If this function returns, BDS attempts to enter an infinite loop.\r
1608**/\r
1609VOID\r
1610EFIAPI\r
1611PlatformBootManagerUnableToBoot (\r
1612 VOID\r
1613 )\r
1614{\r
1615 EFI_STATUS Status;\r
1616 EFI_INPUT_KEY Key;\r
1617 EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;\r
1618 UINTN Index;\r
1619\r
1620 //\r
1621 // BootManagerMenu doesn't contain the correct information when return status\r
1622 // is EFI_NOT_FOUND.\r
1623 //\r
1624 Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);\r
1625 if (EFI_ERROR (Status)) {\r
1626 return;\r
1627 }\r
1628 //\r
1629 // Normally BdsDxe does not print anything to the system console, but this is\r
1630 // a last resort -- the end-user will likely not see any DEBUG messages\r
1631 // logged in this situation.\r
1632 //\r
1633 // AsciiPrint() will NULL-check gST->ConOut internally. We check gST->ConIn\r
1634 // here to see if it makes sense to request and wait for a keypress.\r
1635 //\r
1636 if (gST->ConIn != NULL) {\r
1637 AsciiPrint (\r
1638 "%a: No bootable option or device was found.\n"\r
1639 "%a: Press any key to enter the Boot Manager Menu.\n",\r
1640 gEfiCallerBaseName,\r
1641 gEfiCallerBaseName\r
1642 );\r
1643 Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index);\r
1644 ASSERT_EFI_ERROR (Status);\r
1645 ASSERT (Index == 0);\r
1646\r
1647 //\r
1648 // Drain any queued keys.\r
1649 //\r
1650 while (!EFI_ERROR (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key))) {\r
1651 //\r
1652 // just throw away Key\r
1653 //\r
1654 }\r
1655 }\r
1656\r
1657 for (;;) {\r
1658 EfiBootManagerBoot (&BootManagerMenu);\r
1659 }\r
1660}\r