| /** @file | |
| * | |
| * Copyright (c) 2017, Linaro, Ltd. All rights reserved. | |
| * | |
| * 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 <Uefi.h> | |
| #include <IndustryStandard/Acpi.h> | |
| #include <libfdt.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/DevicePathLib.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", __FUNCTION__, | |
| Status)); | |
| return; | |
| } | |
| Node = fdt_path_offset (Dtb, "/chosen"); | |
| if (Node < 0) { | |
| return; | |
| } | |
| Error = fdt_delprop (Dtb, Node, "stdout-path"); | |
| if (Error) { | |
| DEBUG ((DEBUG_INFO, "%a: Failed to delete 'stdout-path' property: %a\n", | |
| __FUNCTION__, fdt_strerror (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", | |
| __FUNCTION__, Status)); | |
| } | |
| break; | |
| } while (TRUE); | |
| } | |
| STATIC | |
| VOID | |
| 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", __FUNCTION__, | |
| CONSOLE_PREF_VARIABLE_NAME)); | |
| return; | |
| } | |
| if (ConsolePref.Console == CONSOLE_PREF_SERIAL) { | |
| DEBUG ((DEBUG_INFO, | |
| "%a: serial console preferred - doing nothing\n", __FUNCTION__)); | |
| 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", __FUNCTION__, | |
| 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_SUCCES 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", | |
| __FUNCTION__)); | |
| 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", | |
| __FUNCTION__, 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", | |
| __FUNCTION__, Status)); | |
| return Status; | |
| } | |
| } | |
| Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, | |
| OnReadyToBoot, NULL, &gEfiEventReadyToBootGuid, | |
| &mReadyToBootEvent); | |
| ASSERT_EFI_ERROR (Status); | |
| return InstallHiiPages (); | |
| } |