ArmPlatformPkg/Bds: Connect all drivers if the SimpleTextIO is not available
[mirror_edk2.git] / ArmPlatformPkg / Bds / BdsEntry.c
1 /** @file
2 *
3 * Copyright (c) 2011, ARM Limited. All rights reserved.
4 *
5 * This program and the accompanying materials
6 * are licensed and made available under the terms and conditions of the BSD License
7 * which accompanies this distribution. The full text of the license may be found at
8 * http://opensource.org/licenses/bsd-license.php
9 *
10 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 *
13 **/
14
15 #include <PiDxe.h>
16 #include <Library/UefiBootServicesTableLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/MemoryAllocationLib.h>
19 #include <Library/UefiLib.h>
20 #include <Library/PerformanceLib.h>
21 #include <Library/UefiRuntimeServicesTableLib.h>
22 #include <Library/PcdLib.h>
23 #include <Library/BdsUnixLib.h>
24
25 #include <Protocol/Bds.h>
26 #include <Protocol/DevicePathToText.h>
27
28 #include <Guid/GlobalVariable.h>
29
30 #define MAX_CMD_LINE 256
31
32 VOID
33 EFIAPI
34 BdsEntry (
35 IN EFI_BDS_ARCH_PROTOCOL *This
36 );
37
38 EFI_HANDLE mBdsImageHandle = NULL;
39 EFI_BDS_ARCH_PROTOCOL gBdsProtocol = {
40 BdsEntry,
41 };
42
43 EFI_STATUS GetEnvironmentVariable (
44 IN CONST CHAR16* VariableName,
45 IN VOID* DefaultValue,
46 IN UINTN DefaultSize,
47 OUT VOID** Value)
48 {
49 EFI_STATUS Status;
50 UINTN Size;
51
52 // Try to get the variable size.
53 *Value = NULL;
54 Size = 0;
55 Status = gRT->GetVariable ((CHAR16 *) VariableName, &gEfiGlobalVariableGuid, NULL, &Size, *Value);
56 if (Status == EFI_NOT_FOUND) {
57 // If the environment variable does not exist yet then set it with the default value
58 Status = gRT->SetVariable (
59 (CHAR16*)VariableName,
60 &gEfiGlobalVariableGuid,
61 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
62 DefaultSize,
63 DefaultValue
64 );
65 *Value = DefaultValue;
66 } else if (Status == EFI_BUFFER_TOO_SMALL) {
67 // Get the environment variable value
68 *Value = AllocatePool (Size);
69 if (*Value == NULL) {
70 return EFI_OUT_OF_RESOURCES;
71 }
72
73 Status = gRT->GetVariable ((CHAR16 *)VariableName, &gEfiGlobalVariableGuid, NULL, &Size, *Value);
74 if (EFI_ERROR (Status)) {
75 FreePool(*Value);
76 return EFI_INVALID_PARAMETER;
77 }
78 } else {
79 *Value = DefaultValue;
80 return Status;
81 }
82
83 return EFI_SUCCESS;
84 }
85
86 EFI_STATUS
87 InitializeConsole (
88 VOID
89 )
90 {
91 EFI_STATUS Status;
92 UINTN NoHandles;
93 EFI_HANDLE *Buffer;
94 BOOLEAN AllDriversConnected;
95
96 AllDriversConnected = FALSE;
97
98 //
99 // Now we need to setup the EFI System Table with information about the console devices.
100 // This code is normally in the console spliter driver on platforms that support multiple
101 // consoles at the same time
102 //
103 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleTextOutProtocolGuid, NULL, &NoHandles, &Buffer);
104 if (EFI_ERROR (Status)) {
105 BdsConnectAllDrivers();
106 AllDriversConnected = TRUE;
107 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleTextOutProtocolGuid, NULL, &NoHandles, &Buffer);
108 }
109
110 if (!EFI_ERROR (Status)) {
111 // Use the first SimpleTextOut we find and update the EFI System Table
112 gST->ConsoleOutHandle = Buffer[0];
113 gST->StandardErrorHandle = Buffer[0];
114 Status = gBS->HandleProtocol (Buffer[0], &gEfiSimpleTextOutProtocolGuid, (VOID **)&gST->ConOut);
115 ASSERT_EFI_ERROR (Status);
116
117 gST->StdErr = gST->ConOut;
118
119 FreePool (Buffer);
120 } else {
121 return Status;
122 }
123
124 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleTextInProtocolGuid, NULL, &NoHandles, &Buffer);
125 if (EFI_ERROR (Status) && !AllDriversConnected) {
126 BdsConnectAllDrivers();
127 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleTextInProtocolGuid, NULL, &NoHandles, &Buffer);
128 }
129
130 if (!EFI_ERROR (Status)) {
131 // Use the first SimpleTextIn we find and update the EFI System Table
132 gST->ConsoleInHandle = Buffer[0];
133 Status = gBS->HandleProtocol (Buffer[0], &gEfiSimpleTextInProtocolGuid, (VOID **)&gST->ConIn);
134 ASSERT_EFI_ERROR (Status);
135
136 FreePool (Buffer);
137 } else {
138 return Status;
139 }
140
141 return EFI_SUCCESS;
142 }
143
144 EFI_STATUS
145 GetHIInputAscii (
146 CHAR8 *CmdLine,
147 UINTN MaxCmdLine
148 ) {
149 UINTN CmdLineIndex;
150 UINTN WaitIndex;
151 CHAR8 Char;
152 EFI_INPUT_KEY Key;
153 EFI_STATUS Status;
154
155 CmdLine[0] = '\0';
156
157 for (CmdLineIndex = 0; CmdLineIndex < MaxCmdLine; ) {
158 Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex);
159 ASSERT_EFI_ERROR (Status);
160
161 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
162 ASSERT_EFI_ERROR (Status);
163
164 Char = (CHAR8)Key.UnicodeChar;
165 if ((Char == '\n') || (Char == '\r') || (Char == 0x7f)) {
166 CmdLine[CmdLineIndex] = '\0';
167 AsciiPrint ("\n");
168 return EFI_SUCCESS;
169 } else if ((Char == '\b') || (Key.ScanCode == SCAN_LEFT) || (Key.ScanCode == SCAN_DELETE)){
170 if (CmdLineIndex != 0) {
171 CmdLineIndex--;
172 AsciiPrint ("\b \b");
173 }
174 } else {
175 CmdLine[CmdLineIndex++] = Char;
176 AsciiPrint ("%c", Char);
177 }
178 }
179
180 return EFI_SUCCESS;
181 }
182
183 VOID
184 ListDevicePaths (
185 IN BOOLEAN AllDrivers
186 ) {
187 EFI_STATUS Status;
188 UINTN HandleCount;
189 EFI_HANDLE *HandleBuffer;
190 UINTN Index;
191 EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol;
192 CHAR16* String;
193 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* EfiDevicePathToTextProtocol;
194
195 if (AllDrivers) {
196 BdsConnectAllDrivers();
197 }
198
199 Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&EfiDevicePathToTextProtocol);
200 if (EFI_ERROR (Status)) {
201 AsciiPrint ("Did not find the DevicePathToTextProtocol.\n");
202 return;
203 }
204
205 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiDevicePathProtocolGuid, NULL, &HandleCount, &HandleBuffer);
206 if (EFI_ERROR (Status)) {
207 AsciiPrint ("No device path found\n");
208 return;
209 }
210
211 for (Index = 0; Index < HandleCount; Index++) {
212 Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol);
213 String = EfiDevicePathToTextProtocol->ConvertDevicePathToText(DevicePathProtocol,TRUE,TRUE);
214 Print (L"\t- [%d] %s\n",Index, String);
215 }
216 }
217
218 INTN BdsComparefile (
219 IN CHAR16 *DeviceFilePath1,
220 IN CHAR16 *DeviceFilePath2,
221 VOID **FileImage1,VOID **FileImage2,UINTN* FileSize
222 );
223
224 /**
225 This function uses policy data from the platform to determine what operating
226 system or system utility should be loaded and invoked. This function call
227 also optionally make the use of user input to determine the operating system
228 or system utility to be loaded and invoked. When the DXE Core has dispatched
229 all the drivers on the dispatch queue, this function is called. This
230 function will attempt to connect the boot devices required to load and invoke
231 the selected operating system or system utility. During this process,
232 additional firmware volumes may be discovered that may contain addition DXE
233 drivers that can be dispatched by the DXE Core. If a boot device cannot be
234 fully connected, this function calls the DXE Service Dispatch() to allow the
235 DXE drivers from any newly discovered firmware volumes to be dispatched.
236 Then the boot device connection can be attempted again. If the same boot
237 device connection operation fails twice in a row, then that boot device has
238 failed, and should be skipped. This function should never return.
239
240 @param This The EFI_BDS_ARCH_PROTOCOL instance.
241
242 @return None.
243
244 **/
245 VOID
246 EFIAPI
247 BdsEntry (
248 IN EFI_BDS_ARCH_PROTOCOL *This
249 )
250 {
251 EFI_STATUS Status;
252 CHAR8 CmdLine[MAX_CMD_LINE];
253 VOID* DefaultVariableValue;
254 UINTN DefaultVariableSize;
255 CHAR16 *LinuxKernelDP;
256 CHAR8 *LinuxAtag;
257 CHAR16 *FdtDP;
258
259 PERF_END (NULL, "DXE", NULL, 0);
260 PERF_START (NULL, "BDS", NULL, 0);
261
262 Status = InitializeConsole();
263 ASSERT_EFI_ERROR(Status);
264
265 while (1) {
266 // Get the Linux Kernel Device Path from Environment Variable
267 DefaultVariableValue = (VOID*)PcdGetPtr(PcdLinuxKernelDP);
268 DefaultVariableSize = StrSize((CHAR16*)DefaultVariableValue);
269 GetEnvironmentVariable(L"LinuxKernelDP",DefaultVariableValue,DefaultVariableSize,(VOID**)&LinuxKernelDP);
270
271 // Get the Linux ATAG from Environment Variable
272 DefaultVariableValue = (VOID*)PcdGetPtr(PcdLinuxAtag);
273 DefaultVariableSize = AsciiStrSize((CHAR8*)DefaultVariableValue);
274 GetEnvironmentVariable(L"LinuxAtag",DefaultVariableValue,DefaultVariableSize,(VOID**)&LinuxAtag);
275
276 // Get the FDT Device Path from Environment Variable
277 DefaultVariableValue = (VOID*)PcdGetPtr(PcdFdtDP);
278 DefaultVariableSize = StrSize((CHAR16*)DefaultVariableValue);
279 GetEnvironmentVariable(L"FdtDP",DefaultVariableValue,DefaultVariableSize,(VOID**)&FdtDP);
280
281 AsciiPrint ("1. Start EBL\n\r");
282 AsciiPrint ("2. List Device Paths of all the drivers\n");
283 AsciiPrint ("3. Start Linux\n");
284 Print (L"\t- Kernel: %s\n", LinuxKernelDP);
285 AsciiPrint ("\t- Atag: %a\n", LinuxAtag);
286 Print (L"\t- Fdt: %s\n", FdtDP);
287 AsciiPrint ("Choice: ");
288
289 Status = GetHIInputAscii(CmdLine,MAX_CMD_LINE);
290 ASSERT_EFI_ERROR (Status);
291 if (AsciiStrCmp(CmdLine,"1") == 0) {
292 // Start EBL
293 Status = BdsLoadApplication(L"Ebl");
294 if (Status == EFI_NOT_FOUND) {
295 AsciiPrint ("Error: EFI Application not found.\n");
296 } else {
297 AsciiPrint ("Error: Status Code: 0x%X\n",(UINT32)Status);
298 }
299 } else if (AsciiStrCmp(CmdLine,"2") == 0) {
300 ListDevicePaths (TRUE);
301 } else if (AsciiStrCmp(CmdLine,"3") == 0) {
302 // Start Linux Kernel
303 Status = BdsBootLinux(LinuxKernelDP,LinuxAtag,FdtDP);
304 if (EFI_ERROR(Status)) {
305 AsciiPrint ("Error: Fail to start Linux (0x%X)\n",(UINT32)Status);
306 }
307 } else {
308 AsciiPrint ("Error: Invalid choice.\n");
309 }
310 }
311 }
312
313 EFI_STATUS
314 EFIAPI
315 BdsInitialize (
316 IN EFI_HANDLE ImageHandle,
317 IN EFI_SYSTEM_TABLE *SystemTable
318 )
319 {
320 EFI_STATUS Status;
321
322 mBdsImageHandle = ImageHandle;
323
324 Status = gBS->InstallMultipleProtocolInterfaces (
325 &mBdsImageHandle,
326 &gEfiBdsArchProtocolGuid, &gBdsProtocol,
327 NULL
328 );
329 ASSERT_EFI_ERROR (Status);
330
331 return Status;
332 }