+/** @file\r
+*\r
+* Copyright (c) 2017, Linaro, Ltd. All rights reserved.\r
+*\r
+* This program and the accompanying materials are licensed and made available\r
+* under the terms and conditions of the BSD License which accompanies this\r
+* distribution. The full text of the license may be found at\r
+* http://opensource.org/licenses/bsd-license.php\r
+*\r
+* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+*\r
+**/\r
+\r
+#include <Uefi.h>\r
+#include <IndustryStandard/Acpi.h>\r
+#include <libfdt.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/HiiLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiDriverEntryPoint.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+\r
+#include <Protocol/AcpiTable.h>\r
+#include <Protocol/AcpiSystemDescriptionTable.h>\r
+\r
+#include "ConsolePrefDxe.h"\r
+\r
+#define SPCR_SIG EFI_ACPI_2_0_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_SIGNATURE\r
+\r
+extern UINT8 ConsolePrefHiiBin[];\r
+extern UINT8 ConsolePrefDxeStrings[];\r
+\r
+typedef struct {\r
+ VENDOR_DEVICE_PATH VendorDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL End;\r
+} HII_VENDOR_DEVICE_PATH;\r
+\r
+STATIC HII_VENDOR_DEVICE_PATH mConsolePrefDxeVendorDevicePath = {\r
+ {\r
+ {\r
+ HARDWARE_DEVICE_PATH,\r
+ HW_VENDOR_DP,\r
+ {\r
+ (UINT8) (sizeof (VENDOR_DEVICE_PATH)),\r
+ (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)\r
+ }\r
+ },\r
+ CONSOLE_PREF_FORMSET_GUID\r
+ },\r
+ {\r
+ END_DEVICE_PATH_TYPE,\r
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+ {\r
+ (UINT8) (END_DEVICE_PATH_LENGTH),\r
+ (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)\r
+ }\r
+ }\r
+};\r
+\r
+STATIC EFI_EVENT mReadyToBootEvent;\r
+\r
+STATIC\r
+EFI_STATUS\r
+InstallHiiPages (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HII_HANDLE HiiHandle;\r
+ EFI_HANDLE DriverHandle;\r
+\r
+ DriverHandle = NULL;\r
+ Status = gBS->InstallMultipleProtocolInterfaces (&DriverHandle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ &mConsolePrefDxeVendorDevicePath,\r
+ NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ HiiHandle = HiiAddPackages (&gConsolePrefFormSetGuid,\r
+ DriverHandle,\r
+ ConsolePrefDxeStrings,\r
+ ConsolePrefHiiBin,\r
+ NULL);\r
+\r
+ if (HiiHandle == NULL) {\r
+ gBS->UninstallMultipleProtocolInterfaces (DriverHandle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ &mConsolePrefDxeVendorDevicePath,\r
+ NULL);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+VOID\r
+RemoveDtStdoutPath (\r
+ VOID\r
+)\r
+{\r
+ VOID *Dtb;\r
+ INT32 Node;\r
+ INT32 Error;\r
+ EFI_STATUS Status;\r
+\r
+ Status = EfiGetSystemConfigurationTable (&gFdtTableGuid, &Dtb);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_INFO, "%a: could not retrieve DT blob - %r\n", __FUNCTION__,\r
+ Status));\r
+ return;\r
+ }\r
+\r
+ Node = fdt_path_offset (Dtb, "/chosen");\r
+ if (Node < 0) {\r
+ return;\r
+ }\r
+\r
+ Error = fdt_delprop (Dtb, Node, "stdout-path");\r
+ if (Error) {\r
+ DEBUG ((DEBUG_INFO, "%a: Failed to delete 'stdout-path' property: %a\n",\r
+ __FUNCTION__, fdt_strerror (Error)));\r
+ }\r
+}\r
+\r
+STATIC\r
+VOID\r
+RemoveSpcrTable (\r
+ VOID\r
+ )\r
+{\r
+ EFI_ACPI_SDT_PROTOCOL *Sdt;\r
+ EFI_ACPI_TABLE_PROTOCOL *AcpiTable;\r
+ EFI_STATUS Status;\r
+ UINTN TableIndex;\r
+ EFI_ACPI_SDT_HEADER *TableHeader;\r
+ EFI_ACPI_TABLE_VERSION TableVersion;\r
+ UINTN TableKey;\r
+\r
+ Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL,\r
+ (VOID **)&AcpiTable);\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+\r
+ Status = gBS->LocateProtocol (&gEfiAcpiSdtProtocolGuid, NULL, (VOID **)&Sdt);\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+\r
+ TableIndex = 0;\r
+ TableKey = 0;\r
+ TableHeader = NULL;\r
+\r
+ do {\r
+ Status = Sdt->GetAcpiTable (TableIndex++, &TableHeader, &TableVersion,\r
+ &TableKey);\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ if (TableHeader->Signature != SPCR_SIG) {\r
+ continue;\r
+ }\r
+\r
+ Status = AcpiTable->UninstallAcpiTable (AcpiTable, TableKey);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_WARN, "%a: failed to uninstall SPCR table - %r\n",\r
+ __FUNCTION__, Status));\r
+ }\r
+ break;\r
+ } while (TRUE);\r
+}\r
+\r
+STATIC\r
+VOID\r
+OnReadyToBoot (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ CONSOLE_PREF_VARSTORE_DATA ConsolePref;\r
+ UINTN BufferSize;\r
+ EFI_STATUS Status;\r
+ VOID *Gop;\r
+\r
+ BufferSize = sizeof (ConsolePref);\r
+ Status = gRT->GetVariable (CONSOLE_PREF_VARIABLE_NAME,\r
+ &gConsolePrefFormSetGuid, NULL, &BufferSize, &ConsolePref);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR,\r
+ "%a: variable '%s' could not be read - bailing!\n", __FUNCTION__,\r
+ CONSOLE_PREF_VARIABLE_NAME));\r
+ return;\r
+ }\r
+\r
+ if (ConsolePref.Console == CONSOLE_PREF_SERIAL) {\r
+ DEBUG ((DEBUG_INFO,\r
+ "%a: serial console preferred - doing nothing\n", __FUNCTION__));\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Check if any GOP instances exist: if so, disable stdout-path and SPCR\r
+ //\r
+ Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid, NULL, &Gop);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_INFO,\r
+ "%a: no GOP instances found - doing nothing (%r)\n", __FUNCTION__,\r
+ Status));\r
+ return;\r
+ }\r
+\r
+ RemoveDtStdoutPath ();\r
+ RemoveSpcrTable ();\r
+}\r
+\r
+/**\r
+ The entry point for ConsolePrefDxe driver.\r
+\r
+ @param[in] ImageHandle The image handle of the driver.\r
+ @param[in] SystemTable The system table.\r
+\r
+ @retval EFI_ALREADY_STARTED The driver already exists in system.\r
+ @retval EFI_OUT_OF_RESOURCES Fail to execute entry point due to lack of\r
+ resources.\r
+ @retval EFI_SUCCES All the related protocols are installed on\r
+ the driver.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ConsolePrefDxeEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CONSOLE_PREF_VARSTORE_DATA ConsolePref;\r
+ UINTN BufferSize;\r
+\r
+ //\r
+ // Get the current console preference from the ConsolePref variable.\r
+ //\r
+ BufferSize = sizeof (ConsolePref);\r
+ Status = gRT->GetVariable (CONSOLE_PREF_VARIABLE_NAME,\r
+ &gConsolePrefFormSetGuid, NULL, &BufferSize, &ConsolePref);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_INFO,\r
+ "%a: no console preference found, defaulting to graphical\n",\r
+ __FUNCTION__));\r
+ ConsolePref.Console = CONSOLE_PREF_GRAPHICAL;\r
+ }\r
+\r
+ if (!EFI_ERROR (Status) &&\r
+ ConsolePref.Console != CONSOLE_PREF_GRAPHICAL &&\r
+ ConsolePref.Console != CONSOLE_PREF_SERIAL) {\r
+ DEBUG ((DEBUG_WARN, "%a: invalid value for %s, defaulting to graphical\n",\r
+ __FUNCTION__, CONSOLE_PREF_VARIABLE_NAME));\r
+ ConsolePref.Console = CONSOLE_PREF_GRAPHICAL;\r
+ Status = EFI_INVALID_PARAMETER; // trigger setvar below\r
+ }\r
+\r
+ //\r
+ // Write the newly selected value back to the variable store.\r
+ //\r
+ if (EFI_ERROR (Status)) {\r
+ ZeroMem (&ConsolePref.Reserved, sizeof (ConsolePref.Reserved));\r
+ Status = gRT->SetVariable (CONSOLE_PREF_VARIABLE_NAME,\r
+ &gConsolePrefFormSetGuid,\r
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ sizeof (ConsolePref), &ConsolePref);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: gRT->SetVariable () failed - %r\n",\r
+ __FUNCTION__, Status));\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK,\r
+ OnReadyToBoot, NULL, &gEfiEventReadyToBootGuid,\r
+ &mReadyToBootEvent);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return InstallHiiPages ();\r
+}\r