/** @file | |
* | |
* Copyright (c) 2017, Linaro, Ltd. All rights reserved. | |
* | |
* SPDX-License-Identifier: BSD-2-Clause-Patent | |
* | |
**/ | |
#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 | |
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", | |
__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_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", | |
__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 (); | |
} |