1 /** @file
2 *
3 *  Copyright (c) 2017, Linaro, Ltd. All rights reserved.
4 *
5 *  SPDX-License-Identifier: BSD-2-Clause-Patent
6 *
7 **/
8 
9 #include <Uefi.h>
10 #include <IndustryStandard/Acpi.h>
11 #include <libfdt.h>
12 #include <Library/BaseLib.h>
13 #include <Library/DebugLib.h>
14 #include <Library/DevicePathLib.h>
15 #include <Library/HiiLib.h>
16 #include <Library/UefiBootServicesTableLib.h>
17 #include <Library/UefiBootServicesTableLib.h>
18 #include <Library/UefiDriverEntryPoint.h>
19 #include <Library/UefiLib.h>
20 #include <Library/UefiRuntimeServicesTableLib.h>
21 
22 #include <Protocol/AcpiTable.h>
23 #include <Protocol/AcpiSystemDescriptionTable.h>
24 
25 #include "ConsolePrefDxe.h"
26 
27 #define SPCR_SIG    EFI_ACPI_2_0_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_SIGNATURE
28 
29 extern  UINT8                     ConsolePrefHiiBin[];
30 extern  UINT8                     ConsolePrefDxeStrings[];
31 
32 typedef struct {
33   VENDOR_DEVICE_PATH              VendorDevicePath;
34   EFI_DEVICE_PATH_PROTOCOL        End;
35 } HII_VENDOR_DEVICE_PATH;
36 
37 STATIC HII_VENDOR_DEVICE_PATH     mConsolePrefDxeVendorDevicePath = {
38   {
39     {
40       HARDWARE_DEVICE_PATH,
41       HW_VENDOR_DP,
42       {
43         (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
44         (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
45       }
46     },
47     CONSOLE_PREF_FORMSET_GUID
48   },
49   {
50     END_DEVICE_PATH_TYPE,
51     END_ENTIRE_DEVICE_PATH_SUBTYPE,
52     {
53       (UINT8) (END_DEVICE_PATH_LENGTH),
54       (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
55     }
56   }
57 };
58 
59 STATIC EFI_EVENT                  mReadyToBootEvent;
60 
61 STATIC
62 EFI_STATUS
InstallHiiPages(VOID)63 InstallHiiPages (
64   VOID
65   )
66 {
67   EFI_STATUS                      Status;
68   EFI_HII_HANDLE                  HiiHandle;
69   EFI_HANDLE                      DriverHandle;
70 
71   DriverHandle = NULL;
72   Status = gBS->InstallMultipleProtocolInterfaces (&DriverHandle,
73                   &gEfiDevicePathProtocolGuid,
74                   &mConsolePrefDxeVendorDevicePath,
75                   NULL);
76   if (EFI_ERROR (Status)) {
77     return Status;
78   }
79 
80   HiiHandle = HiiAddPackages (&gConsolePrefFormSetGuid,
81                               DriverHandle,
82                               ConsolePrefDxeStrings,
83                               ConsolePrefHiiBin,
84                               NULL);
85 
86   if (HiiHandle == NULL) {
87     gBS->UninstallMultipleProtocolInterfaces (DriverHandle,
88            &gEfiDevicePathProtocolGuid,
89            &mConsolePrefDxeVendorDevicePath,
90            NULL);
91     return EFI_OUT_OF_RESOURCES;
92   }
93   return EFI_SUCCESS;
94 }
95 
96 STATIC
97 VOID
RemoveDtStdoutPath(VOID)98 RemoveDtStdoutPath (
99   VOID
100 )
101 {
102   VOID        *Dtb;
103   INT32       Node;
104   INT32       Error;
105   EFI_STATUS  Status;
106 
107   Status = EfiGetSystemConfigurationTable (&gFdtTableGuid, &Dtb);
108   if (EFI_ERROR (Status)) {
109     DEBUG ((DEBUG_INFO, "%a: could not retrieve DT blob - %r\n", __FUNCTION__,
110       Status));
111     return;
112   }
113 
114   Node = fdt_path_offset (Dtb, "/chosen");
115   if (Node < 0) {
116     return;
117   }
118 
119   Error = fdt_delprop (Dtb, Node, "stdout-path");
120   if (Error) {
121     DEBUG ((DEBUG_INFO, "%a: Failed to delete 'stdout-path' property: %a\n",
122       __FUNCTION__, fdt_strerror (Error)));
123   }
124 }
125 
126 STATIC
127 VOID
RemoveSpcrTable(VOID)128 RemoveSpcrTable (
129   VOID
130   )
131 {
132   EFI_ACPI_SDT_PROTOCOL           *Sdt;
133   EFI_ACPI_TABLE_PROTOCOL         *AcpiTable;
134   EFI_STATUS                      Status;
135   UINTN                           TableIndex;
136   EFI_ACPI_SDT_HEADER             *TableHeader;
137   EFI_ACPI_TABLE_VERSION          TableVersion;
138   UINTN                           TableKey;
139 
140   Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL,
141                   (VOID **)&AcpiTable);
142   if (EFI_ERROR (Status)) {
143     return;
144   }
145 
146   Status = gBS->LocateProtocol (&gEfiAcpiSdtProtocolGuid, NULL, (VOID **)&Sdt);
147   if (EFI_ERROR (Status)) {
148     return;
149   }
150 
151   TableIndex  = 0;
152   TableKey    = 0;
153   TableHeader = NULL;
154 
155   do {
156     Status = Sdt->GetAcpiTable (TableIndex++, &TableHeader, &TableVersion,
157                     &TableKey);
158     if (EFI_ERROR (Status)) {
159       break;
160     }
161 
162     if (TableHeader->Signature != SPCR_SIG) {
163       continue;
164     }
165 
166     Status = AcpiTable->UninstallAcpiTable (AcpiTable, TableKey);
167     if (EFI_ERROR (Status)) {
168       DEBUG ((DEBUG_WARN, "%a: failed to uninstall SPCR table - %r\n",
169         __FUNCTION__, Status));
170     }
171     break;
172   } while (TRUE);
173 }
174 
175 STATIC
176 VOID
OnReadyToBoot(IN EFI_EVENT Event,IN VOID * Context)177 OnReadyToBoot (
178   IN EFI_EVENT  Event,
179   IN VOID       *Context
180   )
181 {
182   CONSOLE_PREF_VARSTORE_DATA      ConsolePref;
183   UINTN                           BufferSize;
184   EFI_STATUS                      Status;
185   VOID                            *Gop;
186 
187   BufferSize = sizeof (ConsolePref);
188   Status = gRT->GetVariable (CONSOLE_PREF_VARIABLE_NAME,
189                   &gConsolePrefFormSetGuid, NULL, &BufferSize, &ConsolePref);
190   if (EFI_ERROR (Status)) {
191     DEBUG ((DEBUG_ERROR,
192       "%a: variable '%s' could not be read - bailing!\n", __FUNCTION__,
193       CONSOLE_PREF_VARIABLE_NAME));
194     return;
195   }
196 
197   if (ConsolePref.Console == CONSOLE_PREF_SERIAL) {
198     DEBUG ((DEBUG_INFO,
199       "%a: serial console preferred - doing nothing\n", __FUNCTION__));
200     return;
201   }
202 
203   //
204   // Check if any GOP instances exist: if so, disable stdout-path and SPCR
205   //
206   Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid, NULL, &Gop);
207   if (EFI_ERROR (Status)) {
208     DEBUG ((DEBUG_INFO,
209       "%a: no GOP instances found - doing nothing (%r)\n", __FUNCTION__,
210       Status));
211     return;
212   }
213 
214   RemoveDtStdoutPath ();
215   RemoveSpcrTable ();
216 }
217 
218 /**
219   The entry point for ConsolePrefDxe driver.
220 
221   @param[in] ImageHandle     The image handle of the driver.
222   @param[in] SystemTable     The system table.
223 
224   @retval EFI_ALREADY_STARTED     The driver already exists in system.
225   @retval EFI_OUT_OF_RESOURCES    Fail to execute entry point due to lack of
226                                   resources.
227   @retval EFI_SUCCES              All the related protocols are installed on
228                                   the driver.
229 
230 **/
231 EFI_STATUS
232 EFIAPI
ConsolePrefDxeEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)233 ConsolePrefDxeEntryPoint (
234   IN EFI_HANDLE                   ImageHandle,
235   IN EFI_SYSTEM_TABLE             *SystemTable
236   )
237 {
238   EFI_STATUS                      Status;
239   CONSOLE_PREF_VARSTORE_DATA      ConsolePref;
240   UINTN                           BufferSize;
241 
242   //
243   // Get the current console preference from the ConsolePref variable.
244   //
245   BufferSize = sizeof (ConsolePref);
246   Status = gRT->GetVariable (CONSOLE_PREF_VARIABLE_NAME,
247                   &gConsolePrefFormSetGuid, NULL, &BufferSize, &ConsolePref);
248   if (EFI_ERROR (Status)) {
249     DEBUG ((DEBUG_INFO,
250       "%a: no console preference found, defaulting to graphical\n",
251       __FUNCTION__));
252     ConsolePref.Console = CONSOLE_PREF_GRAPHICAL;
253   }
254 
255   if (!EFI_ERROR (Status) &&
256       ConsolePref.Console != CONSOLE_PREF_GRAPHICAL &&
257       ConsolePref.Console != CONSOLE_PREF_SERIAL) {
258     DEBUG ((DEBUG_WARN, "%a: invalid value for %s, defaulting to graphical\n",
259       __FUNCTION__, CONSOLE_PREF_VARIABLE_NAME));
260     ConsolePref.Console = CONSOLE_PREF_GRAPHICAL;
261     Status = EFI_INVALID_PARAMETER; // trigger setvar below
262   }
263 
264   //
265   // Write the newly selected value back to the variable store.
266   //
267   if (EFI_ERROR (Status)) {
268     ZeroMem (&ConsolePref.Reserved, sizeof (ConsolePref.Reserved));
269     Status = gRT->SetVariable (CONSOLE_PREF_VARIABLE_NAME,
270                     &gConsolePrefFormSetGuid,
271                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
272                     sizeof (ConsolePref), &ConsolePref);
273 
274     if (EFI_ERROR (Status)) {
275       DEBUG ((DEBUG_ERROR, "%a: gRT->SetVariable () failed - %r\n",
276         __FUNCTION__, Status));
277       return Status;
278     }
279   }
280 
281   Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
282                   OnReadyToBoot, NULL, &gEfiEventReadyToBootGuid,
283                   &mReadyToBootEvent);
284   ASSERT_EFI_ERROR (Status);
285 
286   return InstallHiiPages ();
287 }
288