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