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