| /** @file | |
| Platform BDS customizations. | |
| Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "BdsPlatform.h" | |
| #include <Guid/XenInfo.h> | |
| #include <Guid/RootBridgesConnectedEventGroup.h> | |
| #include <Protocol/FirmwareVolume2.h> | |
| // | |
| // Global data | |
| // | |
| VOID *mEfiDevPathNotifyReg; | |
| EFI_EVENT mEfiDevPathEvent; | |
| VOID *mEmuVariableEventReg; | |
| EFI_EVENT mEmuVariableEvent; | |
| BOOLEAN mDetectVgaOnly; | |
| UINT16 mHostBridgeDevId; | |
| // | |
| // Table of host IRQs matching PCI IRQs A-D | |
| // (for configuring PCI Interrupt Line register) | |
| // | |
| CONST UINT8 PciHostIrqs[] = { | |
| 0x0a, 0x0a, 0x0b, 0x0b | |
| }; | |
| // | |
| // Type definitions | |
| // | |
| typedef | |
| EFI_STATUS | |
| (EFIAPI *PROTOCOL_INSTANCE_CALLBACK)( | |
| IN EFI_HANDLE Handle, | |
| IN VOID *Instance, | |
| IN VOID *Context | |
| ); | |
| /** | |
| @param[in] Handle - Handle of PCI device instance | |
| @param[in] PciIo - PCI IO protocol instance | |
| @param[in] Pci - PCI Header register block | |
| **/ | |
| typedef | |
| EFI_STATUS | |
| (EFIAPI *VISIT_PCI_INSTANCE_CALLBACK)( | |
| IN EFI_HANDLE Handle, | |
| IN EFI_PCI_IO_PROTOCOL *PciIo, | |
| IN PCI_TYPE00 *Pci | |
| ); | |
| // | |
| // Function prototypes | |
| // | |
| EFI_STATUS | |
| VisitAllInstancesOfProtocol ( | |
| IN EFI_GUID *Id, | |
| IN PROTOCOL_INSTANCE_CALLBACK CallBackFunction, | |
| IN VOID *Context | |
| ); | |
| EFI_STATUS | |
| VisitAllPciInstancesOfProtocol ( | |
| IN VISIT_PCI_INSTANCE_CALLBACK CallBackFunction | |
| ); | |
| VOID | |
| InstallDevicePathCallback ( | |
| VOID | |
| ); | |
| VOID | |
| PlatformRegisterFvBootOption ( | |
| EFI_GUID *FileGuid, | |
| CHAR16 *Description, | |
| UINT32 Attributes | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| INTN OptionIndex; | |
| EFI_BOOT_MANAGER_LOAD_OPTION NewOption; | |
| EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; | |
| UINTN BootOptionCount; | |
| MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode; | |
| EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| Status = gBS->HandleProtocol ( | |
| gImageHandle, | |
| &gEfiLoadedImageProtocolGuid, | |
| (VOID **) &LoadedImage | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid); | |
| DevicePath = DevicePathFromHandle (LoadedImage->DeviceHandle); | |
| ASSERT (DevicePath != NULL); | |
| DevicePath = AppendDevicePathNode ( | |
| DevicePath, | |
| (EFI_DEVICE_PATH_PROTOCOL *) &FileNode | |
| ); | |
| ASSERT (DevicePath != NULL); | |
| Status = EfiBootManagerInitializeLoadOption ( | |
| &NewOption, | |
| LoadOptionNumberUnassigned, | |
| LoadOptionTypeBoot, | |
| Attributes, | |
| Description, | |
| DevicePath, | |
| NULL, | |
| 0 | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| FreePool (DevicePath); | |
| BootOptions = EfiBootManagerGetLoadOptions ( | |
| &BootOptionCount, LoadOptionTypeBoot | |
| ); | |
| OptionIndex = EfiBootManagerFindLoadOption ( | |
| &NewOption, BootOptions, BootOptionCount | |
| ); | |
| if (OptionIndex == -1) { | |
| Status = EfiBootManagerAddLoadOptionVariable (&NewOption, MAX_UINTN); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| EfiBootManagerFreeLoadOption (&NewOption); | |
| EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); | |
| } | |
| /** | |
| Remove all MemoryMapped(...)/FvFile(...) and Fv(...)/FvFile(...) boot options | |
| whose device paths do not resolve exactly to an FvFile in the system. | |
| This removes any boot options that point to binaries built into the firmware | |
| and have become stale due to any of the following: | |
| - DXEFV's base address or size changed (historical), | |
| - DXEFV's FvNameGuid changed, | |
| - the FILE_GUID of the pointed-to binary changed, | |
| - the referenced binary is no longer built into the firmware. | |
| EfiBootManagerFindLoadOption() used in PlatformRegisterFvBootOption() only | |
| avoids exact duplicates. | |
| **/ | |
| VOID | |
| RemoveStaleFvFileOptions ( | |
| VOID | |
| ) | |
| { | |
| EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; | |
| UINTN BootOptionCount; | |
| UINTN Index; | |
| BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, | |
| LoadOptionTypeBoot); | |
| for (Index = 0; Index < BootOptionCount; ++Index) { | |
| EFI_DEVICE_PATH_PROTOCOL *Node1, *Node2, *SearchNode; | |
| EFI_STATUS Status; | |
| EFI_HANDLE FvHandle; | |
| // | |
| // If the device path starts with neither MemoryMapped(...) nor Fv(...), | |
| // then keep the boot option. | |
| // | |
| Node1 = BootOptions[Index].FilePath; | |
| if (!(DevicePathType (Node1) == HARDWARE_DEVICE_PATH && | |
| DevicePathSubType (Node1) == HW_MEMMAP_DP) && | |
| !(DevicePathType (Node1) == MEDIA_DEVICE_PATH && | |
| DevicePathSubType (Node1) == MEDIA_PIWG_FW_VOL_DP)) { | |
| continue; | |
| } | |
| // | |
| // If the second device path node is not FvFile(...), then keep the boot | |
| // option. | |
| // | |
| Node2 = NextDevicePathNode (Node1); | |
| if (DevicePathType (Node2) != MEDIA_DEVICE_PATH || | |
| DevicePathSubType (Node2) != MEDIA_PIWG_FW_FILE_DP) { | |
| continue; | |
| } | |
| // | |
| // Locate the Firmware Volume2 protocol instance that is denoted by the | |
| // boot option. If this lookup fails (i.e., the boot option references a | |
| // firmware volume that doesn't exist), then we'll proceed to delete the | |
| // boot option. | |
| // | |
| SearchNode = Node1; | |
| Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, | |
| &SearchNode, &FvHandle); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // The firmware volume was found; now let's see if it contains the FvFile | |
| // identified by GUID. | |
| // | |
| EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol; | |
| MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFileNode; | |
| UINTN BufferSize; | |
| EFI_FV_FILETYPE FoundType; | |
| EFI_FV_FILE_ATTRIBUTES FileAttributes; | |
| UINT32 AuthenticationStatus; | |
| Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid, | |
| (VOID **)&FvProtocol); | |
| ASSERT_EFI_ERROR (Status); | |
| FvFileNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)Node2; | |
| // | |
| // Buffer==NULL means we request metadata only: BufferSize, FoundType, | |
| // FileAttributes. | |
| // | |
| Status = FvProtocol->ReadFile ( | |
| FvProtocol, | |
| &FvFileNode->FvFileName, // NameGuid | |
| NULL, // Buffer | |
| &BufferSize, | |
| &FoundType, | |
| &FileAttributes, | |
| &AuthenticationStatus | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // The FvFile was found. Keep the boot option. | |
| // | |
| continue; | |
| } | |
| } | |
| // | |
| // Delete the boot option. | |
| // | |
| Status = EfiBootManagerDeleteLoadOptionVariable ( | |
| BootOptions[Index].OptionNumber, LoadOptionTypeBoot); | |
| DEBUG_CODE ( | |
| CHAR16 *DevicePathString; | |
| DevicePathString = ConvertDevicePathToText(BootOptions[Index].FilePath, | |
| FALSE, FALSE); | |
| DEBUG (( | |
| EFI_ERROR (Status) ? EFI_D_WARN : EFI_D_VERBOSE, | |
| "%a: removing stale Boot#%04x %s: %r\n", | |
| __FUNCTION__, | |
| (UINT32)BootOptions[Index].OptionNumber, | |
| DevicePathString == NULL ? L"<unavailable>" : DevicePathString, | |
| Status | |
| )); | |
| if (DevicePathString != NULL) { | |
| FreePool (DevicePathString); | |
| } | |
| ); | |
| } | |
| EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); | |
| } | |
| VOID | |
| PlatformRegisterOptionsAndKeys ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_INPUT_KEY Enter; | |
| EFI_INPUT_KEY F2; | |
| EFI_INPUT_KEY Esc; | |
| EFI_BOOT_MANAGER_LOAD_OPTION BootOption; | |
| // | |
| // Register ENTER as CONTINUE key | |
| // | |
| Enter.ScanCode = SCAN_NULL; | |
| Enter.UnicodeChar = CHAR_CARRIAGE_RETURN; | |
| Status = EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Map F2 to Boot Manager Menu | |
| // | |
| F2.ScanCode = SCAN_F2; | |
| F2.UnicodeChar = CHAR_NULL; | |
| Esc.ScanCode = SCAN_ESC; | |
| Esc.UnicodeChar = CHAR_NULL; | |
| Status = EfiBootManagerGetBootManagerMenu (&BootOption); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = EfiBootManagerAddKeyOptionVariable ( | |
| NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL | |
| ); | |
| ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED); | |
| Status = EfiBootManagerAddKeyOptionVariable ( | |
| NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL | |
| ); | |
| ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED); | |
| } | |
| EFI_STATUS | |
| EFIAPI | |
| ConnectRootBridge ( | |
| IN EFI_HANDLE RootBridgeHandle, | |
| IN VOID *Instance, | |
| IN VOID *Context | |
| ); | |
| STATIC | |
| VOID | |
| SaveS3BootScript ( | |
| VOID | |
| ); | |
| // | |
| // BDS Platform Functions | |
| // | |
| VOID | |
| EFIAPI | |
| PlatformBootManagerBeforeConsole ( | |
| VOID | |
| ) | |
| /*++ | |
| Routine Description: | |
| Platform Bds init. Include the platform firmware vendor, revision | |
| and so crc check. | |
| Arguments: | |
| Returns: | |
| None. | |
| --*/ | |
| { | |
| EFI_HANDLE Handle; | |
| EFI_STATUS Status; | |
| RETURN_STATUS PcdStatus; | |
| DEBUG ((EFI_D_INFO, "PlatformBootManagerBeforeConsole\n")); | |
| InstallDevicePathCallback (); | |
| VisitAllInstancesOfProtocol (&gEfiPciRootBridgeIoProtocolGuid, | |
| ConnectRootBridge, NULL); | |
| // | |
| // Signal the ACPI platform driver that it can download QEMU ACPI tables. | |
| // | |
| EfiEventGroupSignal (&gRootBridgesConnectedEventGroupGuid); | |
| // | |
| // We can't signal End-of-Dxe earlier than this. Namely, End-of-Dxe triggers | |
| // the preparation of S3 system information. That logic has a hard dependency | |
| // on the presence of the FACS ACPI table. Since our ACPI tables are only | |
| // installed after PCI enumeration completes, we must not trigger the S3 save | |
| // earlier, hence we can't signal End-of-Dxe earlier. | |
| // | |
| EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid); | |
| if (QemuFwCfgS3Enabled ()) { | |
| // | |
| // Save the boot script too. Note that this will require us to emit the | |
| // DxeSmmReadyToLock event just below, which in turn locks down SMM. | |
| // | |
| SaveS3BootScript (); | |
| } | |
| // | |
| // Prevent further changes to LockBoxes or SMRAM. | |
| // | |
| Handle = NULL; | |
| Status = gBS->InstallProtocolInterface (&Handle, | |
| &gEfiDxeSmmReadyToLockProtocolGuid, EFI_NATIVE_INTERFACE, | |
| NULL); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Dispatch deferred images after EndOfDxe event and ReadyToLock installation. | |
| // | |
| EfiBootManagerDispatchDeferredImages (); | |
| PlatformInitializeConsole (gPlatformConsole); | |
| PcdStatus = PcdSet16S (PcdPlatformBootTimeOut, | |
| GetFrontPageTimeoutFromQemu ()); | |
| ASSERT_RETURN_ERROR (PcdStatus); | |
| PlatformRegisterOptionsAndKeys (); | |
| } | |
| EFI_STATUS | |
| EFIAPI | |
| ConnectRootBridge ( | |
| IN EFI_HANDLE RootBridgeHandle, | |
| IN VOID *Instance, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Make the PCI bus driver connect the root bridge, non-recursively. This | |
| // will produce a number of child handles with PciIo on them. | |
| // | |
| Status = gBS->ConnectController ( | |
| RootBridgeHandle, // ControllerHandle | |
| NULL, // DriverImageHandle | |
| NULL, // RemainingDevicePath -- produce all | |
| // children | |
| FALSE // Recursive | |
| ); | |
| return Status; | |
| } | |
| EFI_STATUS | |
| PrepareLpcBridgeDevicePath ( | |
| IN EFI_HANDLE DeviceHandle | |
| ) | |
| /*++ | |
| Routine Description: | |
| Add IsaKeyboard to ConIn, | |
| add IsaSerial to ConOut, ConIn, ErrOut. | |
| LPC Bridge: 06 01 00 | |
| Arguments: | |
| DeviceHandle - Handle of PCIIO protocol. | |
| Returns: | |
| EFI_SUCCESS - LPC bridge is added to ConOut, ConIn, and ErrOut. | |
| EFI_STATUS - No LPC bridge is added. | |
| --*/ | |
| { | |
| EFI_STATUS Status; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| CHAR16 *DevPathStr; | |
| DevicePath = NULL; | |
| Status = gBS->HandleProtocol ( | |
| DeviceHandle, | |
| &gEfiDevicePathProtocolGuid, | |
| (VOID*)&DevicePath | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| TempDevicePath = DevicePath; | |
| // | |
| // Register Keyboard | |
| // | |
| DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&gPnpPs2KeyboardDeviceNode); | |
| EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL); | |
| // | |
| // Register COM1 | |
| // | |
| DevicePath = TempDevicePath; | |
| gPnp16550ComPortDeviceNode.UID = 0; | |
| DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&gPnp16550ComPortDeviceNode); | |
| DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&gUartDeviceNode); | |
| DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&gTerminalTypeDeviceNode); | |
| // | |
| // Print Device Path | |
| // | |
| DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE); | |
| if (DevPathStr != NULL) { | |
| DEBUG(( | |
| EFI_D_INFO, | |
| "BdsPlatform.c+%d: COM%d DevPath: %s\n", | |
| __LINE__, | |
| gPnp16550ComPortDeviceNode.UID + 1, | |
| DevPathStr | |
| )); | |
| FreePool(DevPathStr); | |
| } | |
| EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL); | |
| EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL); | |
| EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL); | |
| // | |
| // Register COM2 | |
| // | |
| DevicePath = TempDevicePath; | |
| gPnp16550ComPortDeviceNode.UID = 1; | |
| DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&gPnp16550ComPortDeviceNode); | |
| DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&gUartDeviceNode); | |
| DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&gTerminalTypeDeviceNode); | |
| // | |
| // Print Device Path | |
| // | |
| DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE); | |
| if (DevPathStr != NULL) { | |
| DEBUG(( | |
| EFI_D_INFO, | |
| "BdsPlatform.c+%d: COM%d DevPath: %s\n", | |
| __LINE__, | |
| gPnp16550ComPortDeviceNode.UID + 1, | |
| DevPathStr | |
| )); | |
| FreePool(DevPathStr); | |
| } | |
| EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL); | |
| EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL); | |
| EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL); | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| GetGopDevicePath ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *PciDevicePath, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **GopDevicePath | |
| ) | |
| { | |
| UINTN Index; | |
| EFI_STATUS Status; | |
| EFI_HANDLE PciDeviceHandle; | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *TempPciDevicePath; | |
| UINTN GopHandleCount; | |
| EFI_HANDLE *GopHandleBuffer; | |
| if (PciDevicePath == NULL || GopDevicePath == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Initialize the GopDevicePath to be PciDevicePath | |
| // | |
| *GopDevicePath = PciDevicePath; | |
| TempPciDevicePath = PciDevicePath; | |
| Status = gBS->LocateDevicePath ( | |
| &gEfiDevicePathProtocolGuid, | |
| &TempPciDevicePath, | |
| &PciDeviceHandle | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Try to connect this handle, so that GOP driver could start on this | |
| // device and create child handles with GraphicsOutput Protocol installed | |
| // on them, then we get device paths of these child handles and select | |
| // them as possible console device. | |
| // | |
| gBS->ConnectController (PciDeviceHandle, NULL, NULL, FALSE); | |
| Status = gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiGraphicsOutputProtocolGuid, | |
| NULL, | |
| &GopHandleCount, | |
| &GopHandleBuffer | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Add all the child handles as possible Console Device | |
| // | |
| for (Index = 0; Index < GopHandleCount; Index++) { | |
| Status = gBS->HandleProtocol (GopHandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID*)&TempDevicePath); | |
| if (EFI_ERROR (Status)) { | |
| continue; | |
| } | |
| if (CompareMem ( | |
| PciDevicePath, | |
| TempDevicePath, | |
| GetDevicePathSize (PciDevicePath) - END_DEVICE_PATH_LENGTH | |
| ) == 0) { | |
| // | |
| // In current implementation, we only enable one of the child handles | |
| // as console device, i.e. sotre one of the child handle's device | |
| // path to variable "ConOut" | |
| // In future, we could select all child handles to be console device | |
| // | |
| *GopDevicePath = TempDevicePath; | |
| // | |
| // Delete the PCI device's path that added by GetPlugInPciVgaDevicePath() | |
| // Add the integrity GOP device path. | |
| // | |
| EfiBootManagerUpdateConsoleVariable (ConOutDev, NULL, PciDevicePath); | |
| EfiBootManagerUpdateConsoleVariable (ConOutDev, TempDevicePath, NULL); | |
| } | |
| } | |
| gBS->FreePool (GopHandleBuffer); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| PreparePciDisplayDevicePath ( | |
| IN EFI_HANDLE DeviceHandle | |
| ) | |
| /*++ | |
| Routine Description: | |
| Add PCI VGA to ConOut. | |
| PCI VGA: 03 00 00 | |
| Arguments: | |
| DeviceHandle - Handle of PCIIO protocol. | |
| Returns: | |
| EFI_SUCCESS - PCI VGA is added to ConOut. | |
| EFI_STATUS - No PCI VGA device is added. | |
| --*/ | |
| { | |
| EFI_STATUS Status; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *GopDevicePath; | |
| DevicePath = NULL; | |
| GopDevicePath = NULL; | |
| Status = gBS->HandleProtocol ( | |
| DeviceHandle, | |
| &gEfiDevicePathProtocolGuid, | |
| (VOID*)&DevicePath | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| GetGopDevicePath (DevicePath, &GopDevicePath); | |
| DevicePath = GopDevicePath; | |
| EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL); | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| PreparePciSerialDevicePath ( | |
| IN EFI_HANDLE DeviceHandle | |
| ) | |
| /*++ | |
| Routine Description: | |
| Add PCI Serial to ConOut, ConIn, ErrOut. | |
| PCI Serial: 07 00 02 | |
| Arguments: | |
| DeviceHandle - Handle of PCIIO protocol. | |
| Returns: | |
| EFI_SUCCESS - PCI Serial is added to ConOut, ConIn, and ErrOut. | |
| EFI_STATUS - No PCI Serial device is added. | |
| --*/ | |
| { | |
| EFI_STATUS Status; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| DevicePath = NULL; | |
| Status = gBS->HandleProtocol ( | |
| DeviceHandle, | |
| &gEfiDevicePathProtocolGuid, | |
| (VOID*)&DevicePath | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&gUartDeviceNode); | |
| DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&gTerminalTypeDeviceNode); | |
| EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL); | |
| EfiBootManagerUpdateConsoleVariable (ConIn, DevicePath, NULL); | |
| EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL); | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| VisitAllInstancesOfProtocol ( | |
| IN EFI_GUID *Id, | |
| IN PROTOCOL_INSTANCE_CALLBACK CallBackFunction, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN HandleCount; | |
| EFI_HANDLE *HandleBuffer; | |
| UINTN Index; | |
| VOID *Instance; | |
| // | |
| // Start to check all the PciIo to find all possible device | |
| // | |
| HandleCount = 0; | |
| HandleBuffer = NULL; | |
| Status = gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| Id, | |
| NULL, | |
| &HandleCount, | |
| &HandleBuffer | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| for (Index = 0; Index < HandleCount; Index++) { | |
| Status = gBS->HandleProtocol (HandleBuffer[Index], Id, &Instance); | |
| if (EFI_ERROR (Status)) { | |
| continue; | |
| } | |
| Status = (*CallBackFunction) ( | |
| HandleBuffer[Index], | |
| Instance, | |
| Context | |
| ); | |
| } | |
| gBS->FreePool (HandleBuffer); | |
| return EFI_SUCCESS; | |
| } | |
| EFI_STATUS | |
| EFIAPI | |
| VisitingAPciInstance ( | |
| IN EFI_HANDLE Handle, | |
| IN VOID *Instance, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_PCI_IO_PROTOCOL *PciIo; | |
| PCI_TYPE00 Pci; | |
| PciIo = (EFI_PCI_IO_PROTOCOL*) Instance; | |
| // | |
| // Check for all PCI device | |
| // | |
| Status = PciIo->Pci.Read ( | |
| PciIo, | |
| EfiPciIoWidthUint32, | |
| 0, | |
| sizeof (Pci) / sizeof (UINT32), | |
| &Pci | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| return (*(VISIT_PCI_INSTANCE_CALLBACK)(UINTN) Context) ( | |
| Handle, | |
| PciIo, | |
| &Pci | |
| ); | |
| } | |
| EFI_STATUS | |
| VisitAllPciInstances ( | |
| IN VISIT_PCI_INSTANCE_CALLBACK CallBackFunction | |
| ) | |
| { | |
| return VisitAllInstancesOfProtocol ( | |
| &gEfiPciIoProtocolGuid, | |
| VisitingAPciInstance, | |
| (VOID*)(UINTN) CallBackFunction | |
| ); | |
| } | |
| /** | |
| Do platform specific PCI Device check and add them to | |
| ConOut, ConIn, ErrOut. | |
| @param[in] Handle - Handle of PCI device instance | |
| @param[in] PciIo - PCI IO protocol instance | |
| @param[in] Pci - PCI Header register block | |
| @retval EFI_SUCCESS - PCI Device check and Console variable update successfully. | |
| @retval EFI_STATUS - PCI Device check or Console variable update fail. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DetectAndPreparePlatformPciDevicePath ( | |
| IN EFI_HANDLE Handle, | |
| IN EFI_PCI_IO_PROTOCOL *PciIo, | |
| IN PCI_TYPE00 *Pci | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = PciIo->Attributes ( | |
| PciIo, | |
| EfiPciIoAttributeOperationEnable, | |
| EFI_PCI_DEVICE_ENABLE, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| if (!mDetectVgaOnly) { | |
| // | |
| // Here we decide whether it is LPC Bridge | |
| // | |
| if ((IS_PCI_LPC (Pci)) || | |
| ((IS_PCI_ISA_PDECODE (Pci)) && | |
| (Pci->Hdr.VendorId == 0x8086) && | |
| (Pci->Hdr.DeviceId == 0x7000) | |
| ) | |
| ) { | |
| // | |
| // Add IsaKeyboard to ConIn, | |
| // add IsaSerial to ConOut, ConIn, ErrOut | |
| // | |
| DEBUG ((EFI_D_INFO, "Found LPC Bridge device\n")); | |
| PrepareLpcBridgeDevicePath (Handle); | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Here we decide which Serial device to enable in PCI bus | |
| // | |
| if (IS_PCI_16550SERIAL (Pci)) { | |
| // | |
| // Add them to ConOut, ConIn, ErrOut. | |
| // | |
| DEBUG ((EFI_D_INFO, "Found PCI 16550 SERIAL device\n")); | |
| PreparePciSerialDevicePath (Handle); | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| // | |
| // Here we decide which display device to enable in PCI bus | |
| // | |
| if (IS_PCI_DISPLAY (Pci)) { | |
| // | |
| // Add them to ConOut. | |
| // | |
| DEBUG ((EFI_D_INFO, "Found PCI display device\n")); | |
| PreparePciDisplayDevicePath (Handle); | |
| return EFI_SUCCESS; | |
| } | |
| return Status; | |
| } | |
| /** | |
| Do platform specific PCI Device check and add them to ConOut, ConIn, ErrOut | |
| @param[in] DetectVgaOnly - Only detect VGA device if it's TRUE. | |
| @retval EFI_SUCCESS - PCI Device check and Console variable update successfully. | |
| @retval EFI_STATUS - PCI Device check or Console variable update fail. | |
| **/ | |
| EFI_STATUS | |
| DetectAndPreparePlatformPciDevicePaths ( | |
| BOOLEAN DetectVgaOnly | |
| ) | |
| { | |
| mDetectVgaOnly = DetectVgaOnly; | |
| return VisitAllPciInstances (DetectAndPreparePlatformPciDevicePath); | |
| } | |
| VOID | |
| PlatformInitializeConsole ( | |
| IN PLATFORM_CONSOLE_CONNECT_ENTRY *PlatformConsole | |
| ) | |
| /*++ | |
| Routine Description: | |
| Connect the predefined platform default console device. Always try to find | |
| and enable the vga device if have. | |
| Arguments: | |
| PlatformConsole - Predefined platform default console device array. | |
| --*/ | |
| { | |
| UINTN Index; | |
| EFI_DEVICE_PATH_PROTOCOL *VarConout; | |
| EFI_DEVICE_PATH_PROTOCOL *VarConin; | |
| // | |
| // Connect RootBridge | |
| // | |
| GetEfiGlobalVariable2 (EFI_CON_OUT_VARIABLE_NAME, (VOID **) &VarConout, NULL); | |
| GetEfiGlobalVariable2 (EFI_CON_IN_VARIABLE_NAME, (VOID **) &VarConin, NULL); | |
| if (VarConout == NULL || VarConin == NULL) { | |
| // | |
| // Do platform specific PCI Device check and add them to ConOut, ConIn, ErrOut | |
| // | |
| DetectAndPreparePlatformPciDevicePaths (FALSE); | |
| // | |
| // Have chance to connect the platform default console, | |
| // the platform default console is the minimum device group | |
| // the platform should support | |
| // | |
| for (Index = 0; PlatformConsole[Index].DevicePath != NULL; ++Index) { | |
| // | |
| // Update the console variable with the connect type | |
| // | |
| if ((PlatformConsole[Index].ConnectType & CONSOLE_IN) == CONSOLE_IN) { | |
| EfiBootManagerUpdateConsoleVariable (ConIn, PlatformConsole[Index].DevicePath, NULL); | |
| } | |
| if ((PlatformConsole[Index].ConnectType & CONSOLE_OUT) == CONSOLE_OUT) { | |
| EfiBootManagerUpdateConsoleVariable (ConOut, PlatformConsole[Index].DevicePath, NULL); | |
| } | |
| if ((PlatformConsole[Index].ConnectType & STD_ERROR) == STD_ERROR) { | |
| EfiBootManagerUpdateConsoleVariable (ErrOut, PlatformConsole[Index].DevicePath, NULL); | |
| } | |
| } | |
| } else { | |
| // | |
| // Only detect VGA device and add them to ConOut | |
| // | |
| DetectAndPreparePlatformPciDevicePaths (TRUE); | |
| } | |
| } | |
| /** | |
| Configure PCI Interrupt Line register for applicable devices | |
| Ported from SeaBIOS, src/fw/pciinit.c, *_pci_slot_get_irq() | |
| @param[in] Handle - Handle of PCI device instance | |
| @param[in] PciIo - PCI IO protocol instance | |
| @param[in] PciHdr - PCI Header register block | |
| @retval EFI_SUCCESS - PCI Interrupt Line register configured successfully. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SetPciIntLine ( | |
| IN EFI_HANDLE Handle, | |
| IN EFI_PCI_IO_PROTOCOL *PciIo, | |
| IN PCI_TYPE00 *PciHdr | |
| ) | |
| { | |
| EFI_DEVICE_PATH_PROTOCOL *DevPathNode; | |
| EFI_DEVICE_PATH_PROTOCOL *DevPath; | |
| UINTN RootSlot; | |
| UINTN Idx; | |
| UINT8 IrqLine; | |
| EFI_STATUS Status; | |
| UINT32 RootBusNumber; | |
| Status = EFI_SUCCESS; | |
| if (PciHdr->Device.InterruptPin != 0) { | |
| DevPathNode = DevicePathFromHandle (Handle); | |
| ASSERT (DevPathNode != NULL); | |
| DevPath = DevPathNode; | |
| RootBusNumber = 0; | |
| if (DevicePathType (DevPathNode) == ACPI_DEVICE_PATH && | |
| DevicePathSubType (DevPathNode) == ACPI_DP && | |
| ((ACPI_HID_DEVICE_PATH *)DevPathNode)->HID == EISA_PNP_ID(0x0A03)) { | |
| RootBusNumber = ((ACPI_HID_DEVICE_PATH *)DevPathNode)->UID; | |
| } | |
| // | |
| // Compute index into PciHostIrqs[] table by walking | |
| // the device path and adding up all device numbers | |
| // | |
| Status = EFI_NOT_FOUND; | |
| RootSlot = 0; | |
| Idx = PciHdr->Device.InterruptPin - 1; | |
| while (!IsDevicePathEnd (DevPathNode)) { | |
| if (DevicePathType (DevPathNode) == HARDWARE_DEVICE_PATH && | |
| DevicePathSubType (DevPathNode) == HW_PCI_DP) { | |
| Idx += ((PCI_DEVICE_PATH *)DevPathNode)->Device; | |
| // | |
| // Unlike SeaBIOS, which starts climbing from the leaf device | |
| // up toward the root, we traverse the device path starting at | |
| // the root moving toward the leaf node. | |
| // The slot number of the top-level parent bridge is needed for | |
| // Q35 cases with more than 24 slots on the root bus. | |
| // | |
| if (Status != EFI_SUCCESS) { | |
| Status = EFI_SUCCESS; | |
| RootSlot = ((PCI_DEVICE_PATH *)DevPathNode)->Device; | |
| } | |
| } | |
| DevPathNode = NextDevicePathNode (DevPathNode); | |
| } | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| if (RootBusNumber == 0 && RootSlot == 0) { | |
| DEBUG(( | |
| EFI_D_ERROR, | |
| "%a: PCI host bridge (00:00.0) should have no interrupts!\n", | |
| __FUNCTION__ | |
| )); | |
| ASSERT (FALSE); | |
| } | |
| // | |
| // Final PciHostIrqs[] index calculation depends on the platform | |
| // and should match SeaBIOS src/fw/pciinit.c *_pci_slot_get_irq() | |
| // | |
| switch (mHostBridgeDevId) { | |
| case INTEL_82441_DEVICE_ID: | |
| Idx -= 1; | |
| break; | |
| case INTEL_Q35_MCH_DEVICE_ID: | |
| // | |
| // SeaBIOS contains the following comment: | |
| // "Slots 0-24 rotate slot:pin mapping similar to piix above, but | |
| // with a different starting index - see q35-acpi-dsdt.dsl. | |
| // | |
| // Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H)" | |
| // | |
| if (RootSlot > 24) { | |
| // | |
| // in this case, subtract back out RootSlot from Idx | |
| // (SeaBIOS never adds it to begin with, but that would make our | |
| // device path traversal loop above too awkward) | |
| // | |
| Idx -= RootSlot; | |
| } | |
| break; | |
| default: | |
| ASSERT (FALSE); // should never get here | |
| } | |
| Idx %= ARRAY_SIZE (PciHostIrqs); | |
| IrqLine = PciHostIrqs[Idx]; | |
| DEBUG_CODE_BEGIN (); | |
| { | |
| CHAR16 *DevPathString; | |
| STATIC CHAR16 Fallback[] = L"<failed to convert>"; | |
| UINTN Segment, Bus, Device, Function; | |
| DevPathString = ConvertDevicePathToText (DevPath, FALSE, FALSE); | |
| if (DevPathString == NULL) { | |
| DevPathString = Fallback; | |
| } | |
| Status = PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function); | |
| ASSERT_EFI_ERROR (Status); | |
| DEBUG ((EFI_D_VERBOSE, "%a: [%02x:%02x.%x] %s -> 0x%02x\n", __FUNCTION__, | |
| (UINT32)Bus, (UINT32)Device, (UINT32)Function, DevPathString, | |
| IrqLine)); | |
| if (DevPathString != Fallback) { | |
| FreePool (DevPathString); | |
| } | |
| } | |
| DEBUG_CODE_END (); | |
| // | |
| // Set PCI Interrupt Line register for this device to PciHostIrqs[Idx] | |
| // | |
| Status = PciIo->Pci.Write ( | |
| PciIo, | |
| EfiPciIoWidthUint8, | |
| PCI_INT_LINE_OFFSET, | |
| 1, | |
| &IrqLine | |
| ); | |
| } | |
| return Status; | |
| } | |
| VOID | |
| PciAcpiInitialization ( | |
| ) | |
| { | |
| UINTN Pmba; | |
| // | |
| // Query Host Bridge DID to determine platform type | |
| // | |
| mHostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId); | |
| switch (mHostBridgeDevId) { | |
| case INTEL_82441_DEVICE_ID: | |
| Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA); | |
| // | |
| // 00:01.0 ISA Bridge (PIIX4) LNK routing targets | |
| // | |
| PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), 0x0b); // A | |
| PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), 0x0b); // B | |
| PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), 0x0a); // C | |
| PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), 0x0a); // D | |
| break; | |
| case INTEL_Q35_MCH_DEVICE_ID: | |
| Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE); | |
| // | |
| // 00:1f.0 LPC Bridge (Q35) LNK routing targets | |
| // | |
| PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x60), 0x0a); // A | |
| PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x61), 0x0a); // B | |
| PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x62), 0x0b); // C | |
| PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x63), 0x0b); // D | |
| PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x68), 0x0a); // E | |
| PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x69), 0x0a); // F | |
| PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6a), 0x0b); // G | |
| PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6b), 0x0b); // H | |
| break; | |
| default: | |
| DEBUG ((EFI_D_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n", | |
| __FUNCTION__, mHostBridgeDevId)); | |
| ASSERT (FALSE); | |
| return; | |
| } | |
| // | |
| // Initialize PCI_INTERRUPT_LINE for applicable present PCI devices | |
| // | |
| VisitAllPciInstances (SetPciIntLine); | |
| // | |
| // Set ACPI SCI_EN bit in PMCNTRL | |
| // | |
| IoOr16 ((PciRead32 (Pmba) & ~BIT0) + 4, BIT0); | |
| } | |
| /** | |
| This function detects if OVMF is running on Xen. | |
| **/ | |
| STATIC | |
| BOOLEAN | |
| XenDetected ( | |
| VOID | |
| ) | |
| { | |
| EFI_HOB_GUID_TYPE *GuidHob; | |
| STATIC INTN FoundHob = -1; | |
| if (FoundHob == 0) { | |
| return FALSE; | |
| } else if (FoundHob == 1) { | |
| return TRUE; | |
| } | |
| // | |
| // See if a XenInfo HOB is available | |
| // | |
| GuidHob = GetFirstGuidHob (&gEfiXenInfoGuid); | |
| if (GuidHob == NULL) { | |
| FoundHob = 0; | |
| return FALSE; | |
| } | |
| FoundHob = 1; | |
| return TRUE; | |
| } | |
| EFI_STATUS | |
| EFIAPI | |
| ConnectRecursivelyIfPciMassStorage ( | |
| IN EFI_HANDLE Handle, | |
| IN EFI_PCI_IO_PROTOCOL *Instance, | |
| IN PCI_TYPE00 *PciHeader | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| CHAR16 *DevPathStr; | |
| // | |
| // Recognize PCI Mass Storage, and Xen PCI devices | |
| // | |
| if (IS_CLASS1 (PciHeader, PCI_CLASS_MASS_STORAGE) || | |
| (XenDetected() && IS_CLASS2 (PciHeader, 0xFF, 0x80))) { | |
| DevicePath = NULL; | |
| Status = gBS->HandleProtocol ( | |
| Handle, | |
| &gEfiDevicePathProtocolGuid, | |
| (VOID*)&DevicePath | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Print Device Path | |
| // | |
| DevPathStr = ConvertDevicePathToText (DevicePath, FALSE, FALSE); | |
| if (DevPathStr != NULL) { | |
| DEBUG(( | |
| EFI_D_INFO, | |
| "Found %s device: %s\n", | |
| IS_CLASS1 (PciHeader, PCI_CLASS_MASS_STORAGE) ? L"Mass Storage" : L"Xen", | |
| DevPathStr | |
| )); | |
| FreePool(DevPathStr); | |
| } | |
| Status = gBS->ConnectController (Handle, NULL, NULL, TRUE); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| This notification function is invoked when the | |
| EMU Variable FVB has been changed. | |
| @param Event The event that occurred | |
| @param Context For EFI compatibility. Not used. | |
| **/ | |
| VOID | |
| EFIAPI | |
| EmuVariablesUpdatedCallback ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| DEBUG ((EFI_D_INFO, "EmuVariablesUpdatedCallback\n")); | |
| UpdateNvVarsOnFileSystem (); | |
| } | |
| EFI_STATUS | |
| EFIAPI | |
| VisitingFileSystemInstance ( | |
| IN EFI_HANDLE Handle, | |
| IN VOID *Instance, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| STATIC BOOLEAN ConnectedToFileSystem = FALSE; | |
| RETURN_STATUS PcdStatus; | |
| if (ConnectedToFileSystem) { | |
| return EFI_ALREADY_STARTED; | |
| } | |
| Status = ConnectNvVarsToFileSystem (Handle); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| ConnectedToFileSystem = TRUE; | |
| mEmuVariableEvent = | |
| EfiCreateProtocolNotifyEvent ( | |
| &gEfiDevicePathProtocolGuid, | |
| TPL_CALLBACK, | |
| EmuVariablesUpdatedCallback, | |
| NULL, | |
| &mEmuVariableEventReg | |
| ); | |
| PcdStatus = PcdSet64S (PcdEmuVariableEvent, | |
| (UINT64)(UINTN) mEmuVariableEvent); | |
| ASSERT_RETURN_ERROR (PcdStatus); | |
| return EFI_SUCCESS; | |
| } | |
| VOID | |
| PlatformBdsRestoreNvVarsFromHardDisk ( | |
| ) | |
| { | |
| VisitAllPciInstances (ConnectRecursivelyIfPciMassStorage); | |
| VisitAllInstancesOfProtocol ( | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| VisitingFileSystemInstance, | |
| NULL | |
| ); | |
| } | |
| VOID | |
| PlatformBdsConnectSequence ( | |
| VOID | |
| ) | |
| /*++ | |
| Routine Description: | |
| Connect with predefined platform connect sequence, | |
| the OEM/IBV can customize with their own connect sequence. | |
| Arguments: | |
| None. | |
| Returns: | |
| None. | |
| --*/ | |
| { | |
| UINTN Index; | |
| DEBUG ((EFI_D_INFO, "PlatformBdsConnectSequence\n")); | |
| Index = 0; | |
| // | |
| // Here we can get the customized platform connect sequence | |
| // Notes: we can connect with new variable which record the | |
| // last time boots connect device path sequence | |
| // | |
| while (gPlatformConnectSequence[Index] != NULL) { | |
| // | |
| // Build the platform boot option | |
| // | |
| EfiBootManagerConnectDevicePath (gPlatformConnectSequence[Index], NULL); | |
| Index++; | |
| } | |
| // | |
| // Just use the simple policy to connect all devices | |
| // | |
| DEBUG ((EFI_D_INFO, "EfiBootManagerConnectAll\n")); | |
| EfiBootManagerConnectAll (); | |
| PciAcpiInitialization (); | |
| } | |
| /** | |
| Save the S3 boot script. | |
| Note that DxeSmmReadyToLock must be signaled after this function returns; | |
| otherwise the script wouldn't be saved actually. | |
| **/ | |
| STATIC | |
| VOID | |
| SaveS3BootScript ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_S3_SAVE_STATE_PROTOCOL *BootScript; | |
| STATIC CONST UINT8 Info[] = { 0xDE, 0xAD, 0xBE, 0xEF }; | |
| Status = gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid, NULL, | |
| (VOID **) &BootScript); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Despite the opcode documentation in the PI spec, the protocol | |
| // implementation embeds a deep copy of the info in the boot script, rather | |
| // than storing just a pointer to runtime or NVS storage. | |
| // | |
| Status = BootScript->Write(BootScript, EFI_BOOT_SCRIPT_INFORMATION_OPCODE, | |
| (UINT32) sizeof Info, | |
| (EFI_PHYSICAL_ADDRESS)(UINTN) &Info); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| VOID | |
| EFIAPI | |
| PlatformBootManagerAfterConsole ( | |
| VOID | |
| ) | |
| /*++ | |
| Routine Description: | |
| The function will execute with as the platform policy, current policy | |
| is driven by boot mode. IBV/OEM can customize this code for their specific | |
| policy action. | |
| --*/ | |
| { | |
| EFI_BOOT_MODE BootMode; | |
| DEBUG ((EFI_D_INFO, "PlatformBootManagerAfterConsole\n")); | |
| if (PcdGetBool (PcdOvmfFlashVariablesEnable)) { | |
| DEBUG ((EFI_D_INFO, "PlatformBdsPolicyBehavior: not restoring NvVars " | |
| "from disk since flash variables appear to be supported.\n")); | |
| } else { | |
| // | |
| // Try to restore variables from the hard disk early so | |
| // they can be used for the other BDS connect operations. | |
| // | |
| PlatformBdsRestoreNvVarsFromHardDisk (); | |
| } | |
| // | |
| // Get current Boot Mode | |
| // | |
| BootMode = GetBootModeHob (); | |
| DEBUG ((EFI_D_ERROR, "Boot Mode:%x\n", BootMode)); | |
| // | |
| // Go the different platform policy with different boot mode | |
| // Notes: this part code can be change with the table policy | |
| // | |
| ASSERT (BootMode == BOOT_WITH_FULL_CONFIGURATION); | |
| // | |
| // Logo show | |
| // | |
| BootLogoEnableLogo (); | |
| // | |
| // Perform some platform specific connect sequence | |
| // | |
| PlatformBdsConnectSequence (); | |
| // | |
| // Process QEMU's -kernel command line option | |
| // | |
| TryRunningQemuKernel (); | |
| EfiBootManagerRefreshAllBootOption (); | |
| // | |
| // Register UEFI Shell | |
| // | |
| PlatformRegisterFvBootOption ( | |
| PcdGetPtr (PcdShellFile), L"EFI Internal Shell", LOAD_OPTION_ACTIVE | |
| ); | |
| RemoveStaleFvFileOptions (); | |
| SetBootOrderFromQemu (); | |
| } | |
| /** | |
| This notification function is invoked when an instance of the | |
| EFI_DEVICE_PATH_PROTOCOL is produced. | |
| @param Event The event that occurred | |
| @param Context For EFI compatibility. Not used. | |
| **/ | |
| VOID | |
| EFIAPI | |
| NotifyDevPath ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_HANDLE Handle; | |
| EFI_STATUS Status; | |
| UINTN BufferSize; | |
| EFI_DEVICE_PATH_PROTOCOL *DevPathNode; | |
| ATAPI_DEVICE_PATH *Atapi; | |
| // | |
| // Examine all new handles | |
| // | |
| for (;;) { | |
| // | |
| // Get the next handle | |
| // | |
| BufferSize = sizeof (Handle); | |
| Status = gBS->LocateHandle ( | |
| ByRegisterNotify, | |
| NULL, | |
| mEfiDevPathNotifyReg, | |
| &BufferSize, | |
| &Handle | |
| ); | |
| // | |
| // If not found, we're done | |
| // | |
| if (EFI_NOT_FOUND == Status) { | |
| break; | |
| } | |
| if (EFI_ERROR (Status)) { | |
| continue; | |
| } | |
| // | |
| // Get the DevicePath protocol on that handle | |
| // | |
| Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&DevPathNode); | |
| ASSERT_EFI_ERROR (Status); | |
| while (!IsDevicePathEnd (DevPathNode)) { | |
| // | |
| // Find the handler to dump this device path node | |
| // | |
| if ( | |
| (DevicePathType(DevPathNode) == MESSAGING_DEVICE_PATH) && | |
| (DevicePathSubType(DevPathNode) == MSG_ATAPI_DP) | |
| ) { | |
| Atapi = (ATAPI_DEVICE_PATH*) DevPathNode; | |
| PciOr16 ( | |
| PCI_LIB_ADDRESS ( | |
| 0, | |
| 1, | |
| 1, | |
| (Atapi->PrimarySecondary == 1) ? 0x42: 0x40 | |
| ), | |
| BIT15 | |
| ); | |
| } | |
| // | |
| // Next device path node | |
| // | |
| DevPathNode = NextDevicePathNode (DevPathNode); | |
| } | |
| } | |
| return; | |
| } | |
| VOID | |
| InstallDevicePathCallback ( | |
| VOID | |
| ) | |
| { | |
| DEBUG ((EFI_D_INFO, "Registered NotifyDevPath Event\n")); | |
| mEfiDevPathEvent = EfiCreateProtocolNotifyEvent ( | |
| &gEfiDevicePathProtocolGuid, | |
| TPL_CALLBACK, | |
| NotifyDevPath, | |
| NULL, | |
| &mEfiDevPathNotifyReg | |
| ); | |
| } | |
| /** | |
| This function is called each second during the boot manager waits the timeout. | |
| @param TimeoutRemain The remaining timeout. | |
| **/ | |
| VOID | |
| EFIAPI | |
| PlatformBootManagerWaitCallback ( | |
| UINT16 TimeoutRemain | |
| ) | |
| { | |
| EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black; | |
| EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White; | |
| UINT16 Timeout; | |
| Timeout = PcdGet16 (PcdPlatformBootTimeOut); | |
| Black.Raw = 0x00000000; | |
| White.Raw = 0x00FFFFFF; | |
| BootLogoUpdateProgress ( | |
| White.Pixel, | |
| Black.Pixel, | |
| L"Start boot option", | |
| White.Pixel, | |
| (Timeout - TimeoutRemain) * 100 / Timeout, | |
| 0 | |
| ); | |
| } | |