summaryrefslogtreecommitdiffstats
path: root/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.c
diff options
context:
space:
mode:
authorArd Biesheuvel <ard.biesheuvel@linaro.org>2017-10-20 14:30:11 +0100
committerArd Biesheuvel <ard.biesheuvel@linaro.org>2017-10-23 12:14:00 +0100
commitd8e36289cef7bde628b023219cd65fa8e8d4562a (patch)
treea20e19445910d5bfe5b9b9e14bfb77502ab1fcaf /EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.c
parent6e3287442774c1a4bc83f127694700eeb07c18dc (diff)
downloadedk2-d8e36289cef7bde628b023219cd65fa8e8d4562a.tar.gz
edk2-d8e36289cef7bde628b023219cd65fa8e8d4562a.tar.bz2
edk2-d8e36289cef7bde628b023219cd65fa8e8d4562a.zip
EmbeddedPkg: add driver to set graphical/serial console preference
Linux on ARM/arm64 will infer from the presence of a /chosen/stdout-path DT property or of a SPCR ACPI table that the primary console is the serial port, even if a graphical console is available as well. So let's introduce a driver that allows the user to set a preference between graphical and serial if both are available. If the preference is set to 'Graphical', and any GOP protocol instances have been installed by the time the ReadyToBoot event is signalled, remove the DT property and/or the SPCR table entirely. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>
Diffstat (limited to 'EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.c')
-rw-r--r--EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.c293
1 files changed, 293 insertions, 0 deletions
diff --git a/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.c b/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.c
new file mode 100644
index 0000000000..996d1a6036
--- /dev/null
+++ b/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.c
@@ -0,0 +1,293 @@
+/** @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 ();
+}