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
177 EFIAPI
OnReadyToBoot(IN EFI_EVENT Event,IN VOID * Context)178 OnReadyToBoot (
179   IN EFI_EVENT  Event,
180   IN VOID       *Context
181   )
182 {
183   CONSOLE_PREF_VARSTORE_DATA      ConsolePref;
184   UINTN                           BufferSize;
185   EFI_STATUS                      Status;
186   VOID                            *Gop;
187 
188   BufferSize = sizeof (ConsolePref);
189   Status = gRT->GetVariable (CONSOLE_PREF_VARIABLE_NAME,
190                   &gConsolePrefFormSetGuid, NULL, &BufferSize, &ConsolePref);
191   if (EFI_ERROR (Status)) {
192     DEBUG ((DEBUG_ERROR,
193       "%a: variable '%s' could not be read - bailing!\n", __FUNCTION__,
194       CONSOLE_PREF_VARIABLE_NAME));
195     return;
196   }
197 
198   if (ConsolePref.Console == CONSOLE_PREF_SERIAL) {
199     DEBUG ((DEBUG_INFO,
200       "%a: serial console preferred - doing nothing\n", __FUNCTION__));
201     return;
202   }
203 
204   //
205   // Check if any GOP instances exist: if so, disable stdout-path and SPCR
206   //
207   Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid, NULL, &Gop);
208   if (EFI_ERROR (Status)) {
209     DEBUG ((DEBUG_INFO,
210       "%a: no GOP instances found - doing nothing (%r)\n", __FUNCTION__,
211       Status));
212     return;
213   }
214 
215   RemoveDtStdoutPath ();
216   RemoveSpcrTable ();
217 }
218 
219 /**
220   The entry point for ConsolePrefDxe driver.
221 
222   @param[in] ImageHandle     The image handle of the driver.
223   @param[in] SystemTable     The system table.
224 
225   @retval EFI_ALREADY_STARTED     The driver already exists in system.
226   @retval EFI_OUT_OF_RESOURCES    Fail to execute entry point due to lack of
227                                   resources.
228   @retval EFI_SUCCESS             All the related protocols are installed on
229                                   the driver.
230 
231 **/
232 EFI_STATUS
233 EFIAPI
ConsolePrefDxeEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)234 ConsolePrefDxeEntryPoint (
235   IN EFI_HANDLE                   ImageHandle,
236   IN EFI_SYSTEM_TABLE             *SystemTable
237   )
238 {
239   EFI_STATUS                      Status;
240   CONSOLE_PREF_VARSTORE_DATA      ConsolePref;
241   UINTN                           BufferSize;
242 
243   //
244   // Get the current console preference from the ConsolePref variable.
245   //
246   BufferSize = sizeof (ConsolePref);
247   Status = gRT->GetVariable (CONSOLE_PREF_VARIABLE_NAME,
248                   &gConsolePrefFormSetGuid, NULL, &BufferSize, &ConsolePref);
249   if (EFI_ERROR (Status)) {
250     DEBUG ((DEBUG_INFO,
251       "%a: no console preference found, defaulting to graphical\n",
252       __FUNCTION__));
253     ConsolePref.Console = CONSOLE_PREF_GRAPHICAL;
254   }
255 
256   if (!EFI_ERROR (Status) &&
257       ConsolePref.Console != CONSOLE_PREF_GRAPHICAL &&
258       ConsolePref.Console != CONSOLE_PREF_SERIAL) {
259     DEBUG ((DEBUG_WARN, "%a: invalid value for %s, defaulting to graphical\n",
260       __FUNCTION__, CONSOLE_PREF_VARIABLE_NAME));
261     ConsolePref.Console = CONSOLE_PREF_GRAPHICAL;
262     Status = EFI_INVALID_PARAMETER; // trigger setvar below
263   }
264 
265   //
266   // Write the newly selected value back to the variable store.
267   //
268   if (EFI_ERROR (Status)) {
269     ZeroMem (&ConsolePref.Reserved, sizeof (ConsolePref.Reserved));
270     Status = gRT->SetVariable (CONSOLE_PREF_VARIABLE_NAME,
271                     &gConsolePrefFormSetGuid,
272                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
273                     sizeof (ConsolePref), &ConsolePref);
274 
275     if (EFI_ERROR (Status)) {
276       DEBUG ((DEBUG_ERROR, "%a: gRT->SetVariable () failed - %r\n",
277         __FUNCTION__, Status));
278       return Status;
279     }
280   }
281 
282   Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
283                   OnReadyToBoot, NULL, &gEfiEventReadyToBootGuid,
284                   &mReadyToBootEvent);
285   ASSERT_EFI_ERROR (Status);
286 
287   return InstallHiiPages ();
288 }
289