]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Application/LinuxLoader/LinuxConfig.c
ArmPkg: Introduce GetGlobalEnvironmentVariable() function.
[mirror_edk2.git] / ArmPkg / Application / LinuxLoader / LinuxConfig.c
1 /** @file
2 *
3 * Copyright (c) 2011-2013, 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 "LinuxInternal.h"
16
17 #define DEFAULT_BOOT_ENTRY_DESCRIPTION L"Linux"
18 #define MAX_STR_INPUT 300
19 #define MAX_ASCII_INPUT 300
20
21 typedef enum {
22 LINUX_LOADER_NEW = 1,
23 LINUX_LOADER_UPDATE
24 } LINUX_LOADER_ACTION;
25
26 STATIC
27 EFI_STATUS
28 EditHIInputStr (
29 IN OUT CHAR16 *CmdLine,
30 IN UINTN MaxCmdLine
31 )
32 {
33 UINTN CmdLineIndex;
34 UINTN WaitIndex;
35 CHAR8 Char;
36 EFI_INPUT_KEY Key;
37 EFI_STATUS Status;
38
39 Print (CmdLine);
40
41 for (CmdLineIndex = StrLen (CmdLine); CmdLineIndex < MaxCmdLine; ) {
42 Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex);
43 ASSERT_EFI_ERROR (Status);
44
45 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
46 ASSERT_EFI_ERROR (Status);
47
48 // Unicode character is valid when Scancode is NUll
49 if (Key.ScanCode == SCAN_NULL) {
50 // Scan code is NUll, hence read Unicode character
51 Char = (CHAR8)Key.UnicodeChar;
52 } else {
53 Char = CHAR_NULL;
54 }
55
56 if ((Char == CHAR_LINEFEED) || (Char == CHAR_CARRIAGE_RETURN) || (Char == 0x7f)) {
57 CmdLine[CmdLineIndex] = '\0';
58 Print (L"\n\r");
59
60 return EFI_SUCCESS;
61 } else if ((Key.UnicodeChar == L'\b') || (Key.ScanCode == SCAN_LEFT) || (Key.ScanCode == SCAN_DELETE)){
62 if (CmdLineIndex != 0) {
63 CmdLineIndex--;
64 Print (L"\b \b");
65 }
66 } else if ((Key.ScanCode == SCAN_ESC) || (Char == 0x1B) || (Char == 0x0)) {
67 return EFI_INVALID_PARAMETER;
68 } else {
69 CmdLine[CmdLineIndex++] = Key.UnicodeChar;
70 Print (L"%c", Key.UnicodeChar);
71 }
72 }
73
74 return EFI_SUCCESS;
75 }
76
77 STATIC
78 EFI_STATUS
79 EditHIInputAscii (
80 IN OUT CHAR8 *CmdLine,
81 IN UINTN MaxCmdLine
82 )
83 {
84 CHAR16* Str;
85 EFI_STATUS Status;
86
87 Str = (CHAR16*)AllocatePool (MaxCmdLine * sizeof(CHAR16));
88 AsciiStrToUnicodeStr (CmdLine, Str);
89
90 Status = EditHIInputStr (Str, MaxCmdLine);
91
92 UnicodeStrToAsciiStr (Str, CmdLine);
93 FreePool (Str);
94
95 return Status;
96 }
97
98 STATIC
99 EFI_STATUS
100 GetHIInputInteger (
101 OUT UINTN *Integer
102 )
103 {
104 CHAR16 CmdLine[255];
105 EFI_STATUS Status;
106
107 CmdLine[0] = '\0';
108 Status = EditHIInputStr (CmdLine, 255);
109 if (!EFI_ERROR(Status)) {
110 *Integer = StrDecimalToUintn (CmdLine);
111 }
112
113 return Status;
114 }
115
116 #if 0
117 EFI_STATUS
118 GenerateDeviceDescriptionName (
119 IN EFI_HANDLE Handle,
120 IN OUT CHAR16* Description
121 )
122 {
123 EFI_STATUS Status;
124 EFI_COMPONENT_NAME_PROTOCOL* ComponentName2Protocol;
125 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
126 EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol;
127 CHAR16* DriverName;
128 CHAR16* DevicePathTxt;
129 EFI_DEVICE_PATH* DevicePathNode;
130
131 ComponentName2Protocol = NULL;
132 Status = gBS->HandleProtocol (Handle, &gEfiComponentName2ProtocolGuid, (VOID **)&ComponentName2Protocol);
133 if (!EFI_ERROR(Status)) {
134 //TODO: Fixme. we must find the best langague
135 Status = ComponentName2Protocol->GetDriverName (ComponentName2Protocol,"en",&DriverName);
136 if (!EFI_ERROR(Status)) {
137 StrnCpy (Description,DriverName,BOOT_DEVICE_DESCRIPTION_MAX);
138 }
139 }
140
141 if (EFI_ERROR(Status)) {
142 // Use the lastest non null entry of the Device path as a description
143 Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol);
144 if (EFI_ERROR(Status)) {
145 return Status;
146 }
147
148 // Convert the last non end-type Device Path Node in text for the description
149 DevicePathNode = GetLastDevicePathNode (DevicePathProtocol);
150 Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
151 ASSERT_EFI_ERROR(Status);
152 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(DevicePathNode,TRUE,TRUE);
153 StrnCpy (Description, DevicePathTxt, BOOT_DEVICE_DESCRIPTION_MAX);
154 FreePool (DevicePathTxt);
155 }
156
157 return EFI_SUCCESS;
158 }
159 #endif
160
161 EFI_STATUS
162 LinuxLoaderConfig (
163 IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage
164 )
165 {
166 EFI_STATUS Status;
167 LINUX_LOADER_ACTION Choice;
168 UINTN BootOrderSize;
169 UINT16* BootOrder;
170 UINTN BootOrderCount;
171 UINTN Index;
172 CHAR16 Description[MAX_ASCII_INPUT];
173 CHAR8 CmdLine[MAX_ASCII_INPUT];
174 CHAR16 Initrd[MAX_STR_INPUT];
175 UINT16 InitrdPathListLength;
176 UINT16 CmdLineLength;
177 BDS_LOAD_OPTION* BdsLoadOption;
178 BDS_LOAD_OPTION** SupportedBdsLoadOptions;
179 UINTN SupportedBdsLoadOptionCount;
180 LINUX_LOADER_OPTIONAL_DATA* LinuxOptionalData;
181 EFI_DEVICE_PATH* DevicePathRoot;
182
183 Choice = (LINUX_LOADER_ACTION)0;
184 SupportedBdsLoadOptions = NULL;
185 SupportedBdsLoadOptionCount = 0;
186
187 do {
188 Print (L"[%d] Create new Linux Boot Entry\n",LINUX_LOADER_NEW);
189 Print (L"[%d] Update Linux Boot Entry\n",LINUX_LOADER_UPDATE);
190
191 Print (L"Option: ");
192 Status = GetHIInputInteger (&Choice);
193 if (Status == EFI_INVALID_PARAMETER) {
194 Print (L"\n");
195 return Status;
196 } else if ((Choice != LINUX_LOADER_NEW) && (Choice != LINUX_LOADER_UPDATE)) {
197 Print (L"Error: the option should be either '%d' or '%d'\n",LINUX_LOADER_NEW,LINUX_LOADER_UPDATE);
198 Status = EFI_INVALID_PARAMETER;
199 }
200 } while (EFI_ERROR(Status));
201
202 if (Choice == LINUX_LOADER_UPDATE) {
203 // If no compatible entry then we just create a new entry
204 Choice = LINUX_LOADER_NEW;
205
206 // Scan the OptionalData of every entry for the correct signature
207 Status = GetGlobalEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
208 if (!EFI_ERROR(Status)) {
209 BootOrderCount = BootOrderSize / sizeof(UINT16);
210
211 // Allocate an array to handle maximum number of supported Boot Entry
212 SupportedBdsLoadOptions = (BDS_LOAD_OPTION**)AllocatePool(sizeof(BDS_LOAD_OPTION*) * BootOrderCount);
213
214 SupportedBdsLoadOptionCount = 0;
215
216 // Check if the signature is present in the list of the current Boot entries
217 for (Index = 0; Index < BootOrderCount; Index++) {
218 Status = BootOptionFromLoadOptionIndex (BootOrder[Index], &BdsLoadOption);
219 if (!EFI_ERROR(Status)) {
220 if ((BdsLoadOption->OptionalDataSize >= sizeof(UINT32)) &&
221 (*(UINT32*)BdsLoadOption->OptionalData == LINUX_LOADER_SIGNATURE)) {
222 SupportedBdsLoadOptions[SupportedBdsLoadOptionCount++] = BdsLoadOption;
223 Choice = LINUX_LOADER_UPDATE;
224 }
225 }
226 }
227 }
228 FreePool (BootOrder);
229 }
230
231 if (Choice == LINUX_LOADER_NEW) {
232 Description[0] = '\0';
233 CmdLine[0] = '\0';
234 Initrd[0] = '\0';
235
236 BdsLoadOption = (BDS_LOAD_OPTION*)AllocateZeroPool (sizeof(BDS_LOAD_OPTION));
237
238 DEBUG_CODE_BEGIN();
239 CHAR16* DevicePathTxt;
240 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
241
242 Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
243 ASSERT_EFI_ERROR(Status);
244 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (LoadedImage->FilePath, TRUE, TRUE);
245
246 Print(L"EFI OS Loader: %s\n",DevicePathTxt);
247
248 FreePool(DevicePathTxt);
249 DEBUG_CODE_END();
250
251 //
252 // Fill the known fields of BdsLoadOption
253 //
254
255 BdsLoadOption->Attributes = LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT;
256
257 // Get the full Device Path for this file
258 Status = gBS->HandleProtocol (LoadedImage->DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathRoot);
259 ASSERT_EFI_ERROR(Status);
260
261 BdsLoadOption->FilePathList = AppendDevicePath (DevicePathRoot, LoadedImage->FilePath);
262 BdsLoadOption->FilePathListLength = GetDevicePathSize (BdsLoadOption->FilePathList);
263 } else {
264 if (SupportedBdsLoadOptionCount > 1) {
265 for (Index = 0; Index < SupportedBdsLoadOptionCount; Index++) {
266 Print (L"[%d] %s\n",Index + 1,SupportedBdsLoadOptions[Index]->Description);
267 }
268
269 do {
270 Print (L"Update Boot Entry: ");
271 Status = GetHIInputInteger (&Choice);
272 if (Status == EFI_INVALID_PARAMETER) {
273 Print (L"\n");
274 return Status;
275 } else if ((Choice < 1) && (Choice > SupportedBdsLoadOptionCount)) {
276 Print (L"Choose entry from 1 to %d\n",SupportedBdsLoadOptionCount);
277 Status = EFI_INVALID_PARAMETER;
278 }
279 } while (EFI_ERROR(Status));
280 BdsLoadOption = SupportedBdsLoadOptions[Choice-1];
281 }
282 StrnCpy (Description, BdsLoadOption->Description, MAX_STR_INPUT);
283
284 LinuxOptionalData = (LINUX_LOADER_OPTIONAL_DATA*)BdsLoadOption->OptionalData;
285 if (LinuxOptionalData->CmdLineLength > 0) {
286 CopyMem (CmdLine, (CHAR8*)LinuxOptionalData + sizeof(LINUX_LOADER_OPTIONAL_DATA), LinuxOptionalData->CmdLineLength);
287 } else {
288 CmdLine[0] = '\0';
289 }
290
291 if (LinuxOptionalData->InitrdPathListLength > 0) {
292 CopyMem (Initrd, (CHAR8*)LinuxOptionalData + sizeof(LINUX_LOADER_OPTIONAL_DATA) + LinuxOptionalData->CmdLineLength, LinuxOptionalData->InitrdPathListLength);
293 } else {
294 Initrd[0] = L'\0';
295 }
296 DEBUG((EFI_D_ERROR,"L\n"));
297 }
298
299 // Description
300 Print (L"Description: ");
301 Status = EditHIInputStr (Description, MAX_STR_INPUT);
302 if (EFI_ERROR(Status)) {
303 return Status;
304 }
305 if (StrLen (Description) == 0) {
306 StrnCpy (Description, DEFAULT_BOOT_ENTRY_DESCRIPTION, MAX_STR_INPUT);
307 }
308 BdsLoadOption->Description = Description;
309
310 // CmdLine
311 Print (L"Command Line: ");
312 Status = EditHIInputAscii (CmdLine, MAX_ASCII_INPUT);
313 if (EFI_ERROR(Status)) {
314 return Status;
315 }
316
317 // Initrd
318 Print (L"Initrd name: ");
319 Status = EditHIInputStr (Initrd, MAX_STR_INPUT);
320 if (EFI_ERROR(Status)) {
321 return Status;
322 }
323
324 CmdLineLength = AsciiStrLen (CmdLine);
325 if (CmdLineLength > 0) {
326 CmdLineLength += sizeof(CHAR8);
327 }
328
329 InitrdPathListLength = StrLen (Initrd) * sizeof(CHAR16);
330 if (InitrdPathListLength > 0) {
331 InitrdPathListLength += sizeof(CHAR16);
332 }
333
334 BdsLoadOption->OptionalDataSize = sizeof(LINUX_LOADER_OPTIONAL_DATA) + CmdLineLength + InitrdPathListLength;
335
336 LinuxOptionalData = (LINUX_LOADER_OPTIONAL_DATA*)AllocatePool (BdsLoadOption->OptionalDataSize);
337 BdsLoadOption->OptionalData = LinuxOptionalData;
338
339 LinuxOptionalData->Signature = LINUX_LOADER_SIGNATURE;
340 LinuxOptionalData->CmdLineLength = CmdLineLength;
341 LinuxOptionalData->InitrdPathListLength = InitrdPathListLength;
342
343 if (CmdLineLength > 0) {
344 CopyMem (LinuxOptionalData + 1, CmdLine, CmdLineLength);
345 }
346 if (InitrdPathListLength > 0) {
347 CopyMem ((UINT8*)(LinuxOptionalData + 1) + CmdLineLength, Initrd, InitrdPathListLength);
348 }
349
350 // Create or Update the boot entry
351 Status = BootOptionToLoadOptionVariable (BdsLoadOption);
352
353 return Status;
354 }