| /** @file | |
| * | |
| * Copyright (c) 2017, Linaro, Ltd. All rights reserved. | |
| * | |
| * SPDX-License-Identifier: BSD-2-Clause-Patent | |
| * | |
| **/ | |
| #include <Uefi.h> | |
| #include <IndustryStandard/Acpi.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/DevicePathLib.h> | |
| #include <Library/FdtLib.h> | |
| #include <Library/HiiLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Library/UefiDriverEntryPoint.h> | |
| #include <Library/UefiLib.h> | |
| #include <Library/UefiRuntimeServicesTableLib.h> | |
| #include <Protocol/AcpiTable.h> | |
| #include <Protocol/AcpiSystemDescriptionTable.h> | |
| #include "ConsolePrefDxe.h" | |
| #define SPCR_SIG EFI_ACPI_2_0_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_SIGNATURE | |
| extern UINT8 ConsolePrefHiiBin[]; | |
| extern UINT8 ConsolePrefDxeStrings[]; | |
| typedef struct { | |
| VENDOR_DEVICE_PATH VendorDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL End; | |
| } HII_VENDOR_DEVICE_PATH; | |
| STATIC HII_VENDOR_DEVICE_PATH mConsolePrefDxeVendorDevicePath = { | |
| { | |
| { | |
| HARDWARE_DEVICE_PATH, | |
| HW_VENDOR_DP, | |
| { | |
| (UINT8)(sizeof (VENDOR_DEVICE_PATH)), | |
| (UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8) | |
| } | |
| }, | |
| CONSOLE_PREF_FORMSET_GUID | |
| }, | |
| { | |
| END_DEVICE_PATH_TYPE, | |
| END_ENTIRE_DEVICE_PATH_SUBTYPE, | |
| { | |
| (UINT8)(END_DEVICE_PATH_LENGTH), | |
| (UINT8)((END_DEVICE_PATH_LENGTH) >> 8) | |
| } | |
| } | |
| }; | |
| STATIC EFI_EVENT mReadyToBootEvent; | |
| STATIC | |
| EFI_STATUS | |
| InstallHiiPages ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_HII_HANDLE HiiHandle; | |
| EFI_HANDLE DriverHandle; | |
| DriverHandle = NULL; | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &DriverHandle, | |
| &gEfiDevicePathProtocolGuid, | |
| &mConsolePrefDxeVendorDevicePath, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| HiiHandle = HiiAddPackages ( | |
| &gConsolePrefFormSetGuid, | |
| DriverHandle, | |
| ConsolePrefDxeStrings, | |
| ConsolePrefHiiBin, | |
| NULL | |
| ); | |
| if (HiiHandle == NULL) { | |
| gBS->UninstallMultipleProtocolInterfaces ( | |
| DriverHandle, | |
| &gEfiDevicePathProtocolGuid, | |
| &mConsolePrefDxeVendorDevicePath, | |
| NULL | |
| ); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| STATIC | |
| VOID | |
| RemoveDtStdoutPath ( | |
| VOID | |
| ) | |
| { | |
| VOID *Dtb; | |
| INT32 Node; | |
| INT32 Error; | |
| EFI_STATUS Status; | |
| Status = EfiGetSystemConfigurationTable (&gFdtTableGuid, &Dtb); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a: could not retrieve DT blob - %r\n", | |
| __func__, | |
| Status | |
| )); | |
| return; | |
| } | |
| Node = FdtPathOffset (Dtb, "/chosen"); | |
| if (Node < 0) { | |
| return; | |
| } | |
| Error = FdtDelProp (Dtb, Node, "stdout-path"); | |
| if (Error) { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a: Failed to delete 'stdout-path' property: %a\n", | |
| __func__, | |
| FdtStrerror (Error) | |
| )); | |
| } | |
| } | |
| STATIC | |
| VOID | |
| RemoveSpcrTable ( | |
| VOID | |
| ) | |
| { | |
| EFI_ACPI_SDT_PROTOCOL *Sdt; | |
| EFI_ACPI_TABLE_PROTOCOL *AcpiTable; | |
| EFI_STATUS Status; | |
| UINTN TableIndex; | |
| EFI_ACPI_SDT_HEADER *TableHeader; | |
| EFI_ACPI_TABLE_VERSION TableVersion; | |
| UINTN TableKey; | |
| Status = gBS->LocateProtocol ( | |
| &gEfiAcpiTableProtocolGuid, | |
| NULL, | |
| (VOID **)&AcpiTable | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return; | |
| } | |
| Status = gBS->LocateProtocol (&gEfiAcpiSdtProtocolGuid, NULL, (VOID **)&Sdt); | |
| if (EFI_ERROR (Status)) { | |
| return; | |
| } | |
| TableIndex = 0; | |
| TableKey = 0; | |
| TableHeader = NULL; | |
| do { | |
| Status = Sdt->GetAcpiTable ( | |
| TableIndex++, | |
| &TableHeader, | |
| &TableVersion, | |
| &TableKey | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| break; | |
| } | |
| if (TableHeader->Signature != SPCR_SIG) { | |
| continue; | |
| } | |
| Status = AcpiTable->UninstallAcpiTable (AcpiTable, TableKey); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_WARN, | |
| "%a: failed to uninstall SPCR table - %r\n", | |
| __func__, | |
| Status | |
| )); | |
| } | |
| break; | |
| } while (TRUE); | |
| } | |
| STATIC | |
| VOID | |
| EFIAPI | |
| OnReadyToBoot ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| CONSOLE_PREF_VARSTORE_DATA ConsolePref; | |
| UINTN BufferSize; | |
| EFI_STATUS Status; | |
| VOID *Gop; | |
| BufferSize = sizeof (ConsolePref); | |
| Status = gRT->GetVariable ( | |
| CONSOLE_PREF_VARIABLE_NAME, | |
| &gConsolePrefFormSetGuid, | |
| NULL, | |
| &BufferSize, | |
| &ConsolePref | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: variable '%s' could not be read - bailing!\n", | |
| __func__, | |
| CONSOLE_PREF_VARIABLE_NAME | |
| )); | |
| return; | |
| } | |
| if (ConsolePref.Console == CONSOLE_PREF_SERIAL) { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a: serial console preferred - doing nothing\n", | |
| __func__ | |
| )); | |
| return; | |
| } | |
| // | |
| // Check if any GOP instances exist: if so, disable stdout-path and SPCR | |
| // | |
| Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid, NULL, &Gop); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a: no GOP instances found - doing nothing (%r)\n", | |
| __func__, | |
| Status | |
| )); | |
| return; | |
| } | |
| RemoveDtStdoutPath (); | |
| RemoveSpcrTable (); | |
| } | |
| /** | |
| The entry point for ConsolePrefDxe driver. | |
| @param[in] ImageHandle The image handle of the driver. | |
| @param[in] SystemTable The system table. | |
| @retval EFI_ALREADY_STARTED The driver already exists in system. | |
| @retval EFI_OUT_OF_RESOURCES Fail to execute entry point due to lack of | |
| resources. | |
| @retval EFI_SUCCESS All the related protocols are installed on | |
| the driver. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| ConsolePrefDxeEntryPoint ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| CONSOLE_PREF_VARSTORE_DATA ConsolePref; | |
| UINTN BufferSize; | |
| // | |
| // Get the current console preference from the ConsolePref variable. | |
| // | |
| BufferSize = sizeof (ConsolePref); | |
| Status = gRT->GetVariable ( | |
| CONSOLE_PREF_VARIABLE_NAME, | |
| &gConsolePrefFormSetGuid, | |
| NULL, | |
| &BufferSize, | |
| &ConsolePref | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a: no console preference found, defaulting to graphical\n", | |
| __func__ | |
| )); | |
| ConsolePref.Console = CONSOLE_PREF_GRAPHICAL; | |
| } | |
| if (!EFI_ERROR (Status) && | |
| (ConsolePref.Console != CONSOLE_PREF_GRAPHICAL) && | |
| (ConsolePref.Console != CONSOLE_PREF_SERIAL)) | |
| { | |
| DEBUG (( | |
| DEBUG_WARN, | |
| "%a: invalid value for %s, defaulting to graphical\n", | |
| __func__, | |
| CONSOLE_PREF_VARIABLE_NAME | |
| )); | |
| ConsolePref.Console = CONSOLE_PREF_GRAPHICAL; | |
| Status = EFI_INVALID_PARAMETER; // trigger setvar below | |
| } | |
| // | |
| // Write the newly selected value back to the variable store. | |
| // | |
| if (EFI_ERROR (Status)) { | |
| ZeroMem (&ConsolePref.Reserved, sizeof (ConsolePref.Reserved)); | |
| Status = gRT->SetVariable ( | |
| CONSOLE_PREF_VARIABLE_NAME, | |
| &gConsolePrefFormSetGuid, | |
| EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, | |
| sizeof (ConsolePref), | |
| &ConsolePref | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "%a: gRT->SetVariable () failed - %r\n", | |
| __func__, | |
| Status | |
| )); | |
| return Status; | |
| } | |
| } | |
| Status = gBS->CreateEventEx ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_CALLBACK, | |
| OnReadyToBoot, | |
| NULL, | |
| &gEfiEventReadyToBootGuid, | |
| &mReadyToBootEvent | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| return InstallHiiPages (); | |
| } |