]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/PlatformBootManagerLibBhyve/BdsPlatform.c
OvmfPkg/Bhyve: Add support for the AMD host bridge
[mirror_edk2.git] / OvmfPkg / Library / PlatformBootManagerLibBhyve / 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
02967794
RC
1069 case 0x7432: // BHYVE (AMD hostbridge)\r
1070 case 0x1275: // BHYVE (Intel hostbridge)\r
656419f9
RC
1071 case INTEL_82441_DEVICE_ID:\r
1072 Idx -= 1;\r
1073 break;\r
1074 case INTEL_Q35_MCH_DEVICE_ID:\r
1075 //\r
1076 // SeaBIOS contains the following comment:\r
1077 // "Slots 0-24 rotate slot:pin mapping similar to piix above, but\r
1078 // with a different starting index - see q35-acpi-dsdt.dsl.\r
1079 //\r
1080 // Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H)"\r
1081 //\r
1082 if (RootSlot > 24) {\r
1083 //\r
1084 // in this case, subtract back out RootSlot from Idx\r
1085 // (SeaBIOS never adds it to begin with, but that would make our\r
1086 // device path traversal loop above too awkward)\r
1087 //\r
1088 Idx -= RootSlot;\r
1089 }\r
1090 break;\r
1091 default:\r
1092 ASSERT (FALSE); // should never get here\r
1093 }\r
1094 Idx %= ARRAY_SIZE (PciHostIrqs);\r
1095 IrqLine = PciHostIrqs[Idx];\r
1096\r
1097 DEBUG_CODE_BEGIN ();\r
1098 {\r
1099 CHAR16 *DevPathString;\r
1100 STATIC CHAR16 Fallback[] = L"<failed to convert>";\r
1101 UINTN Segment, Bus, Device, Function;\r
1102\r
1103 DevPathString = ConvertDevicePathToText (DevPath, FALSE, FALSE);\r
1104 if (DevPathString == NULL) {\r
1105 DevPathString = Fallback;\r
1106 }\r
1107 Status = PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);\r
1108 ASSERT_EFI_ERROR (Status);\r
1109\r
1110 DEBUG ((DEBUG_VERBOSE, "%a: [%02x:%02x.%x] %s -> 0x%02x\n", __FUNCTION__,\r
1111 (UINT32)Bus, (UINT32)Device, (UINT32)Function, DevPathString,\r
1112 IrqLine));\r
1113\r
1114 if (DevPathString != Fallback) {\r
1115 FreePool (DevPathString);\r
1116 }\r
1117 }\r
1118 DEBUG_CODE_END ();\r
1119\r
1120 //\r
1121 // Set PCI Interrupt Line register for this device to PciHostIrqs[Idx]\r
1122 //\r
1123 Status = PciIo->Pci.Write (\r
1124 PciIo,\r
1125 EfiPciIoWidthUint8,\r
1126 PCI_INT_LINE_OFFSET,\r
1127 1,\r
1128 &IrqLine\r
1129 );\r
1130 }\r
1131\r
1132 return Status;\r
1133}\r
1134\r
1135\r
1136VOID\r
1137PciAcpiInitialization (\r
1138 )\r
1139{\r
1140 UINTN Pmba;\r
1141\r
1142 //\r
1143 // Query Host Bridge DID to determine platform type\r
1144 //\r
1145 mHostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);\r
1146 switch (mHostBridgeDevId) {\r
02967794
RC
1147 case 0x7432: // BHYVE (AMD hostbridge)\r
1148 case 0x1275: // BHYVE (Intel hostbridge)\r
656419f9
RC
1149 case INTEL_82441_DEVICE_ID:\r
1150 Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);\r
1151 //\r
1152 // 00:01.0 ISA Bridge (PIIX4) LNK routing targets\r
1153 //\r
1154 PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), 0x0b); // A\r
1155 PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), 0x0b); // B\r
1156 PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), 0x0a); // C\r
1157 PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), 0x0a); // D\r
1158 break;\r
1159 case INTEL_Q35_MCH_DEVICE_ID:\r
1160 Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);\r
1161 //\r
1162 // 00:1f.0 LPC Bridge (Q35) LNK routing targets\r
1163 //\r
1164 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x60), 0x0a); // A\r
1165 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x61), 0x0a); // B\r
1166 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x62), 0x0b); // C\r
1167 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x63), 0x0b); // D\r
1168 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x68), 0x0a); // E\r
1169 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x69), 0x0a); // F\r
1170 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6a), 0x0b); // G\r
1171 PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6b), 0x0b); // H\r
1172 break;\r
1173 default:\r
1174 DEBUG ((DEBUG_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n",\r
1175 __FUNCTION__, mHostBridgeDevId));\r
1176 ASSERT (FALSE);\r
1177 return;\r
1178 }\r
1179\r
1180 //\r
1181 // Initialize PCI_INTERRUPT_LINE for applicable present PCI devices\r
1182 //\r
1183 VisitAllPciInstances (SetPciIntLine);\r
1184\r
1185 //\r
1186 // Set ACPI SCI_EN bit in PMCNTRL\r
1187 //\r
1188 IoOr16 ((PciRead32 (Pmba) & ~BIT0) + 4, BIT0);\r
1189}\r
1190\r
1191EFI_STATUS\r
1192EFIAPI\r
1193ConnectRecursivelyIfPciMassStorage (\r
1194 IN EFI_HANDLE Handle,\r
1195 IN EFI_PCI_IO_PROTOCOL *Instance,\r
1196 IN PCI_TYPE00 *PciHeader\r
1197 )\r
1198{\r
1199 EFI_STATUS Status;\r
1200 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
1201 CHAR16 *DevPathStr;\r
1202\r
1203 //\r
1204 // Recognize PCI Mass Storage, and Xen PCI devices\r
1205 //\r
1206 if (IS_CLASS1 (PciHeader, PCI_CLASS_MASS_STORAGE)) {\r
1207 DevicePath = NULL;\r
1208 Status = gBS->HandleProtocol (\r
1209 Handle,\r
1210 &gEfiDevicePathProtocolGuid,\r
1211 (VOID*)&DevicePath\r
1212 );\r
1213 if (EFI_ERROR (Status)) {\r
1214 return Status;\r
1215 }\r
1216\r
1217 //\r
1218 // Print Device Path\r
1219 //\r
1220 DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE);\r
1221 if (DevPathStr != NULL) {\r
1222 DEBUG((\r
1223 DEBUG_INFO,\r
1224 "Found %s device: %s\n",\r
1225 (IS_CLASS1 (PciHeader, PCI_CLASS_MASS_STORAGE) ?\r
1226 L"Mass Storage" :\r
1227 L"Xen"\r
1228 ),\r
1229 DevPathStr\r
1230 ));\r
1231 FreePool(DevPathStr);\r
1232 }\r
1233\r
1234 Status = gBS->ConnectController (Handle, NULL, NULL, TRUE);\r
1235 if (EFI_ERROR (Status)) {\r
1236 return Status;\r
1237 }\r
1238\r
1239 }\r
1240\r
1241 return EFI_SUCCESS;\r
1242}\r
1243\r
1244\r
1245/**\r
1246 This notification function is invoked when the\r
1247 EMU Variable FVB has been changed.\r
1248\r
1249 @param Event The event that occurred\r
1250 @param Context For EFI compatibility. Not used.\r
1251\r
1252**/\r
1253VOID\r
1254EFIAPI\r
1255EmuVariablesUpdatedCallback (\r
1256 IN EFI_EVENT Event,\r
1257 IN VOID *Context\r
1258 )\r
1259{\r
1260 DEBUG ((DEBUG_INFO, "EmuVariablesUpdatedCallback\n"));\r
1261 UpdateNvVarsOnFileSystem ();\r
1262}\r
1263\r
1264\r
1265EFI_STATUS\r
1266EFIAPI\r
1267VisitingFileSystemInstance (\r
1268 IN EFI_HANDLE Handle,\r
1269 IN VOID *Instance,\r
1270 IN VOID *Context\r
1271 )\r
1272{\r
1273 EFI_STATUS Status;\r
1274 STATIC BOOLEAN ConnectedToFileSystem = FALSE;\r
1275 RETURN_STATUS PcdStatus;\r
1276\r
1277 if (ConnectedToFileSystem) {\r
1278 return EFI_ALREADY_STARTED;\r
1279 }\r
1280\r
1281 Status = ConnectNvVarsToFileSystem (Handle);\r
1282 if (EFI_ERROR (Status)) {\r
1283 return Status;\r
1284 }\r
1285\r
1286 ConnectedToFileSystem = TRUE;\r
1287 mEmuVariableEvent =\r
1288 EfiCreateProtocolNotifyEvent (\r
1289 &gEfiDevicePathProtocolGuid,\r
1290 TPL_CALLBACK,\r
1291 EmuVariablesUpdatedCallback,\r
1292 NULL,\r
1293 &mEmuVariableEventReg\r
1294 );\r
1295 PcdStatus = PcdSet64S (PcdEmuVariableEvent,\r
1296 (UINT64)(UINTN) mEmuVariableEvent);\r
1297 ASSERT_RETURN_ERROR (PcdStatus);\r
1298\r
1299 return EFI_SUCCESS;\r
1300}\r
1301\r
1302\r
1303VOID\r
1304PlatformBdsRestoreNvVarsFromHardDisk (\r
1305 )\r
1306{\r
1307 VisitAllPciInstances (ConnectRecursivelyIfPciMassStorage);\r
1308 VisitAllInstancesOfProtocol (\r
1309 &gEfiSimpleFileSystemProtocolGuid,\r
1310 VisitingFileSystemInstance,\r
1311 NULL\r
1312 );\r
1313\r
1314}\r
1315\r
1316/**\r
1317 Connect with predefined platform connect sequence.\r
1318\r
1319 The OEM/IBV can customize with their own connect sequence.\r
1320**/\r
1321VOID\r
1322PlatformBdsConnectSequence (\r
1323 VOID\r
1324 )\r
1325{\r
1326 UINTN Index;\r
1327\r
1328 DEBUG ((DEBUG_INFO, "PlatformBdsConnectSequence\n"));\r
1329\r
1330 Index = 0;\r
1331\r
1332 //\r
1333 // Here we can get the customized platform connect sequence\r
1334 // Notes: we can connect with new variable which record the\r
1335 // last time boots connect device path sequence\r
1336 //\r
1337 while (gPlatformConnectSequence[Index] != NULL) {\r
1338 //\r
1339 // Build the platform boot option\r
1340 //\r
1341 EfiBootManagerConnectDevicePath (gPlatformConnectSequence[Index], NULL);\r
1342 Index++;\r
1343 }\r
1344\r
1345 //\r
1346 // Just use the simple policy to connect all devices\r
1347 //\r
1348 DEBUG ((DEBUG_INFO, "EfiBootManagerConnectAll\n"));\r
1349 EfiBootManagerConnectAll ();\r
1350}\r
1351\r
1352/**\r
1353 Save the S3 boot script.\r
1354\r
1355 Note that DxeSmmReadyToLock must be signaled after this function returns;\r
1356 otherwise the script wouldn't be saved actually.\r
1357**/\r
1358#if defined(__GNUC__)\r
1359__attribute__((unused))\r
1360#endif\r
1361STATIC\r
1362VOID\r
1363SaveS3BootScript (\r
1364 VOID\r
1365 )\r
1366{\r
1367 EFI_STATUS Status;\r
1368 EFI_S3_SAVE_STATE_PROTOCOL *BootScript;\r
1369 STATIC CONST UINT8 Info[] = { 0xDE, 0xAD, 0xBE, 0xEF };\r
1370\r
1371 Status = gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid, NULL,\r
1372 (VOID **) &BootScript);\r
1373 ASSERT_EFI_ERROR (Status);\r
1374\r
1375 //\r
1376 // Despite the opcode documentation in the PI spec, the protocol\r
1377 // implementation embeds a deep copy of the info in the boot script, rather\r
1378 // than storing just a pointer to runtime or NVS storage.\r
1379 //\r
1380 Status = BootScript->Write(BootScript, EFI_BOOT_SCRIPT_INFORMATION_OPCODE,\r
1381 (UINT32) sizeof Info,\r
1382 (EFI_PHYSICAL_ADDRESS)(UINTN) &Info);\r
1383 ASSERT_EFI_ERROR (Status);\r
1384}\r
1385\r
1386\r
1387/**\r
1388 Do the platform specific action after the console is ready\r
1389\r
1390 Possible things that can be done in PlatformBootManagerAfterConsole:\r
1391\r
1392 > Console post action:\r
1393 > Dynamically switch output mode from 100x31 to 80x25 for certain senarino\r
1394 > Signal console ready platform customized event\r
1395 > Run diagnostics like memory testing\r
1396 > Connect certain devices\r
1397 > Dispatch aditional option roms\r
1398 > Special boot: e.g.: USB boot, enter UI\r
1399**/\r
1400VOID\r
1401EFIAPI\r
1402PlatformBootManagerAfterConsole (\r
1403 VOID\r
1404 )\r
1405{\r
1406 EFI_BOOT_MODE BootMode;\r
1407\r
1408 DEBUG ((DEBUG_INFO, "PlatformBootManagerAfterConsole\n"));\r
1409\r
1410 if (PcdGetBool (PcdOvmfFlashVariablesEnable)) {\r
1411 DEBUG ((DEBUG_INFO, "PlatformBdsPolicyBehavior: not restoring NvVars "\r
1412 "from disk since flash variables appear to be supported.\n"));\r
1413 } else {\r
1414 //\r
1415 // Try to restore variables from the hard disk early so\r
1416 // they can be used for the other BDS connect operations.\r
1417 //\r
1418 /* XXX Calling this causes Keyboard to be removed from ConIn which\r
1419 results in unresponsive guest boot loaders in the GUI. Restore it\r
1420 when we figure out what is needed to get NvVars storage done\r
1421 properly.\r
1422 */\r
1423 /*PlatformBdsRestoreNvVarsFromHardDisk ();*/\r
1424 }\r
1425\r
1426 //\r
1427 // Get current Boot Mode\r
1428 //\r
1429 BootMode = GetBootModeHob ();\r
1430 DEBUG ((DEBUG_INFO, "Boot Mode:%x\n", BootMode));\r
1431\r
1432 //\r
1433 // Go the different platform policy with different boot mode\r
1434 // Notes: this part code can be change with the table policy\r
1435 //\r
1436 ASSERT (BootMode == BOOT_WITH_FULL_CONFIGURATION);\r
1437\r
1438 //\r
1439 // Logo show\r
1440 //\r
1441 BootLogoEnableLogo ();\r
1442\r
1443 //\r
1444 // Set PCI Interrupt Line registers and ACPI SCI_EN\r
1445 //\r
1446 PciAcpiInitialization ();\r
1447\r
1448 //\r
1449 // Process TPM PPI request\r
1450 //\r
1451 Tcg2PhysicalPresenceLibProcessRequest (NULL);\r
1452\r
1453 //\r
1454 // Perform some platform specific connect sequence\r
1455 //\r
1456 PlatformBdsConnectSequence ();\r
1457\r
1458 EfiBootManagerRefreshAllBootOption ();\r
1459\r
1460 //\r
1461 // Register UEFI Shell\r
1462 //\r
1463 PlatformRegisterFvBootOption (\r
1464 &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE\r
1465 );\r
1466\r
1467 RemoveStaleFvFileOptions ();\r
1468\r
1469 PlatformBmPrintScRegisterHandler ();\r
1470}\r
1471\r
1472/**\r
1473 This notification function is invoked when an instance of the\r
1474 EFI_DEVICE_PATH_PROTOCOL is produced.\r
1475\r
1476 @param Event The event that occurred\r
1477 @param Context For EFI compatibility. Not used.\r
1478\r
1479**/\r
1480VOID\r
1481EFIAPI\r
1482NotifyDevPath (\r
1483 IN EFI_EVENT Event,\r
1484 IN VOID *Context\r
1485 )\r
1486{\r
1487 EFI_HANDLE Handle;\r
1488 EFI_STATUS Status;\r
1489 UINTN BufferSize;\r
1490 EFI_DEVICE_PATH_PROTOCOL *DevPathNode;\r
1491 ATAPI_DEVICE_PATH *Atapi;\r
1492\r
1493 //\r
1494 // Examine all new handles\r
1495 //\r
1496 for (;;) {\r
1497 //\r
1498 // Get the next handle\r
1499 //\r
1500 BufferSize = sizeof (Handle);\r
1501 Status = gBS->LocateHandle (\r
1502 ByRegisterNotify,\r
1503 NULL,\r
1504 mEfiDevPathNotifyReg,\r
1505 &BufferSize,\r
1506 &Handle\r
1507 );\r
1508\r
1509 //\r
1510 // If not found, we're done\r
1511 //\r
1512 if (EFI_NOT_FOUND == Status) {\r
1513 break;\r
1514 }\r
1515\r
1516 if (EFI_ERROR (Status)) {\r
1517 continue;\r
1518 }\r
1519\r
1520 //\r
1521 // Get the DevicePath protocol on that handle\r
1522 //\r
1523 Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid,\r
1524 (VOID **)&DevPathNode);\r
1525 ASSERT_EFI_ERROR (Status);\r
1526\r
1527 while (!IsDevicePathEnd (DevPathNode)) {\r
1528 //\r
1529 // Find the handler to dump this device path node\r
1530 //\r
1531 if (\r
1532 (DevicePathType(DevPathNode) == MESSAGING_DEVICE_PATH) &&\r
1533 (DevicePathSubType(DevPathNode) == MSG_ATAPI_DP)\r
1534 ) {\r
1535 Atapi = (ATAPI_DEVICE_PATH*) DevPathNode;\r
1536 PciOr16 (\r
1537 PCI_LIB_ADDRESS (\r
1538 0,\r
1539 1,\r
1540 1,\r
1541 (Atapi->PrimarySecondary == 1) ? 0x42: 0x40\r
1542 ),\r
1543 BIT15\r
1544 );\r
1545 }\r
1546\r
1547 //\r
1548 // Next device path node\r
1549 //\r
1550 DevPathNode = NextDevicePathNode (DevPathNode);\r
1551 }\r
1552 }\r
1553\r
1554 return;\r
1555}\r
1556\r
1557\r
1558VOID\r
1559InstallDevicePathCallback (\r
1560 VOID\r
1561 )\r
1562{\r
1563 DEBUG ((DEBUG_INFO, "Registered NotifyDevPath Event\n"));\r
1564 mEfiDevPathEvent = EfiCreateProtocolNotifyEvent (\r
1565 &gEfiDevicePathProtocolGuid,\r
1566 TPL_CALLBACK,\r
1567 NotifyDevPath,\r
1568 NULL,\r
1569 &mEfiDevPathNotifyReg\r
1570 );\r
1571}\r
1572\r
1573/**\r
1574 This function is called each second during the boot manager waits the\r
1575 timeout.\r
1576\r
1577 @param TimeoutRemain The remaining timeout.\r
1578**/\r
1579VOID\r
1580EFIAPI\r
1581PlatformBootManagerWaitCallback (\r
1582 UINT16 TimeoutRemain\r
1583 )\r
1584{\r
1585 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black;\r
1586 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White;\r
1587 UINT16 Timeout;\r
1588\r
1589 Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
1590\r
1591 Black.Raw = 0x00000000;\r
1592 White.Raw = 0x00FFFFFF;\r
1593\r
1594 BootLogoUpdateProgress (\r
1595 White.Pixel,\r
1596 Black.Pixel,\r
1597 L"Start boot option",\r
1598 White.Pixel,\r
1599 (Timeout - TimeoutRemain) * 100 / Timeout,\r
1600 0\r
1601 );\r
1602}\r
1603\r
1604/**\r
1605 The function is called when no boot option could be launched,\r
1606 including platform recovery options and options pointing to applications\r
1607 built into firmware volumes.\r
1608\r
1609 If this function returns, BDS attempts to enter an infinite loop.\r
1610**/\r
1611VOID\r
1612EFIAPI\r
1613PlatformBootManagerUnableToBoot (\r
1614 VOID\r
1615 )\r
1616{\r
1617 EFI_STATUS Status;\r
1618 EFI_INPUT_KEY Key;\r
1619 EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;\r
1620 UINTN Index;\r
1621\r
1622 //\r
1623 // BootManagerMenu doesn't contain the correct information when return status\r
1624 // is EFI_NOT_FOUND.\r
1625 //\r
1626 Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);\r
1627 if (EFI_ERROR (Status)) {\r
1628 return;\r
1629 }\r
1630 //\r
1631 // Normally BdsDxe does not print anything to the system console, but this is\r
1632 // a last resort -- the end-user will likely not see any DEBUG messages\r
1633 // logged in this situation.\r
1634 //\r
1635 // AsciiPrint() will NULL-check gST->ConOut internally. We check gST->ConIn\r
1636 // here to see if it makes sense to request and wait for a keypress.\r
1637 //\r
1638 if (gST->ConIn != NULL) {\r
1639 AsciiPrint (\r
1640 "%a: No bootable option or device was found.\n"\r
1641 "%a: Press any key to enter the Boot Manager Menu.\n",\r
1642 gEfiCallerBaseName,\r
1643 gEfiCallerBaseName\r
1644 );\r
1645 Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index);\r
1646 ASSERT_EFI_ERROR (Status);\r
1647 ASSERT (Index == 0);\r
1648\r
1649 //\r
1650 // Drain any queued keys.\r
1651 //\r
1652 while (!EFI_ERROR (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key))) {\r
1653 //\r
1654 // just throw away Key\r
1655 //\r
1656 }\r
1657 }\r
1658\r
1659 for (;;) {\r
1660 EfiBootManagerBoot (&BootManagerMenu);\r
1661 }\r
1662}\r