8553577cf1b1272f63b297033c390a25546a56e4
[mirror_edk2.git] / ArmPlatformPkg / Bds / BdsHelper.c
1 /** @file
2 *
3 * Copyright (c) 2011 - 2014, 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 <Library/NetLib.h>
16 #include "BdsInternal.h"
17
18 EFI_STATUS
19 EditHIInputStr (
20 IN OUT CHAR16 *CmdLine,
21 IN UINTN MaxCmdLine
22 )
23 {
24 UINTN CmdLineIndex;
25 UINTN WaitIndex;
26 CHAR8 Char;
27 EFI_INPUT_KEY Key;
28 EFI_STATUS Status;
29
30 // The command line must be at least one character long
31 ASSERT (MaxCmdLine > 0);
32
33 // Ensure the last character of the buffer is the NULL character
34 CmdLine[MaxCmdLine - 1] = '\0';
35
36 Print (CmdLine);
37
38 // To prevent a buffer overflow, we only allow to enter (MaxCmdLine-1) characters
39 for (CmdLineIndex = StrLen (CmdLine); CmdLineIndex < MaxCmdLine; ) {
40 Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex);
41 ASSERT_EFI_ERROR (Status);
42
43 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
44 ASSERT_EFI_ERROR (Status);
45
46 // Unicode character is valid when Scancode is NUll
47 if (Key.ScanCode == SCAN_NULL) {
48 // Scan code is NUll, hence read Unicode character
49 Char = (CHAR8)Key.UnicodeChar;
50 } else {
51 Char = CHAR_NULL;
52 }
53
54 if ((Char == CHAR_LINEFEED) || (Char == CHAR_CARRIAGE_RETURN) || (Char == 0x7f)) {
55 CmdLine[CmdLineIndex] = '\0';
56 Print (L"\r\n");
57
58 return EFI_SUCCESS;
59 } else if ((Key.UnicodeChar == L'\b') || (Key.ScanCode == SCAN_LEFT) || (Key.ScanCode == SCAN_DELETE)){
60 if (CmdLineIndex != 0) {
61 CmdLineIndex--;
62 Print (L"\b \b");
63 }
64 } else if ((Key.ScanCode == SCAN_ESC) || (Char == 0x1B) || (Char == 0x0)) {
65 return EFI_INVALID_PARAMETER;
66 } else if (CmdLineIndex < (MaxCmdLine-1)) {
67 CmdLine[CmdLineIndex++] = Key.UnicodeChar;
68 Print (L"%c", Key.UnicodeChar);
69 }
70 }
71
72 return EFI_SUCCESS;
73 }
74
75 EFI_STATUS
76 GetHIInputStr (
77 IN OUT CHAR16 *CmdLine,
78 IN UINTN MaxCmdLine
79 )
80 {
81 EFI_STATUS Status;
82
83 // For a new input just passed an empty string
84 CmdLine[0] = L'\0';
85
86 Status = EditHIInputStr (CmdLine, MaxCmdLine);
87
88 return Status;
89 }
90
91 EFI_STATUS
92 EditHIInputAscii (
93 IN OUT CHAR8 *CmdLine,
94 IN UINTN MaxCmdLine
95 )
96 {
97 CHAR16* Str;
98 EFI_STATUS Status;
99
100 Str = (CHAR16*)AllocatePool (MaxCmdLine * sizeof(CHAR16));
101 AsciiStrToUnicodeStr (CmdLine, Str);
102
103 Status = EditHIInputStr (Str, MaxCmdLine);
104 if (!EFI_ERROR(Status)) {
105 UnicodeStrToAsciiStr (Str, CmdLine);
106 }
107 FreePool (Str);
108
109 return Status;
110 }
111
112 EFI_STATUS
113 GetHIInputAscii (
114 IN OUT CHAR8 *CmdLine,
115 IN UINTN MaxCmdLine
116 )
117 {
118 // For a new input just passed an empty string
119 CmdLine[0] = '\0';
120
121 return EditHIInputAscii (CmdLine,MaxCmdLine);
122 }
123
124 EFI_STATUS
125 GetHIInputInteger (
126 OUT UINTN *Integer
127 )
128 {
129 CHAR16 CmdLine[255];
130 EFI_STATUS Status;
131
132 CmdLine[0] = '\0';
133 Status = EditHIInputStr (CmdLine, 255);
134 if (!EFI_ERROR(Status)) {
135 *Integer = StrDecimalToUintn (CmdLine);
136 }
137
138 return Status;
139 }
140
141 /**
142 Get an IPv4 address
143
144 The function asks the user for an IPv4 address. If the input
145 string defines a valid IPv4 address, the four bytes of the
146 corresponding IPv4 address are extracted from the string and returned by
147 the function.
148
149 @param[out] EFI_IP_ADDRESS OutIpAddr Returned IPv4 address. Valid if
150 and only if the returned value
151 is equal to EFI_SUCCESS
152
153 @retval EFI_SUCCESS Input completed
154 @retval EFI_ABORTED Editing aborted by the user
155 @retval EFI_INVALID_PARAMETER The string returned by the user is
156 mal-formated
157 @retval EFI_OUT_OF_RESOURCES Fail to perform the operation due to
158 lack of resource
159 **/
160 EFI_STATUS
161 GetHIInputIP (
162 OUT EFI_IP_ADDRESS *OutIpAddr
163 )
164 {
165 EFI_STATUS Status;
166 CHAR16 CmdLine[48];
167
168 CmdLine[0] = '\0';
169 Status = EditHIInputStr (CmdLine, 48);
170 if (EFI_ERROR (Status)) {
171 return EFI_ABORTED;
172 }
173
174 Status = NetLibStrToIp4 (CmdLine, &OutIpAddr->v4);
175
176 return Status;
177 }
178
179 /**
180 Edit an IPv4 address
181
182 The function displays as a string following the "%d.%d.%d.%d" format the
183 IPv4 address that is passed in and asks the user to modify it. If the
184 resulting string defines a valid IPv4 address, the four bytes of the
185 corresponding IPv4 address are extracted from the string and returned by
186 the function.
187
188 @param[in ] EFI_IP_ADDRESS InIpAddr Input IPv4 address
189 @param[out] EFI_IP_ADDRESS OutIpAddr Returned IPv4 address. Valid if
190 and only if the returned value
191 is equal to EFI_SUCCESS
192
193 @retval EFI_SUCCESS Update completed
194 @retval EFI_ABORTED Editing aborted by the user
195 @retval EFI_INVALID_PARAMETER The string returned by the user is
196 mal-formated
197 @retval EFI_OUT_OF_RESOURCES Fail to perform the operation due to
198 lack of resource
199 **/
200 EFI_STATUS
201 EditHIInputIP (
202 IN EFI_IP_ADDRESS *InIpAddr,
203 OUT EFI_IP_ADDRESS *OutIpAddr
204 )
205 {
206 EFI_STATUS Status;
207 CHAR16 CmdLine[48];
208
209 UnicodeSPrint (
210 CmdLine, 48, L"%d.%d.%d.%d",
211 InIpAddr->v4.Addr[0], InIpAddr->v4.Addr[1],
212 InIpAddr->v4.Addr[2], InIpAddr->v4.Addr[3]
213 );
214
215 Status = EditHIInputStr (CmdLine, 48);
216 if (EFI_ERROR (Status)) {
217 return EFI_ABORTED;
218 }
219
220 Status = NetLibStrToIp4 (CmdLine, &OutIpAddr->v4);
221
222 return Status;
223 }
224
225 EFI_STATUS
226 GetHIInputBoolean (
227 OUT BOOLEAN *Value
228 )
229 {
230 CHAR16 CmdBoolean[2];
231 EFI_STATUS Status;
232
233 while(1) {
234 Print (L"[y/n] ");
235 Status = GetHIInputStr (CmdBoolean, 2);
236 if (EFI_ERROR(Status)) {
237 return Status;
238 } else if ((CmdBoolean[0] == L'y') || (CmdBoolean[0] == L'Y')) {
239 if (Value) *Value = TRUE;
240 return EFI_SUCCESS;
241 } else if ((CmdBoolean[0] == L'n') || (CmdBoolean[0] == L'N')) {
242 if (Value) *Value = FALSE;
243 return EFI_SUCCESS;
244 }
245 }
246 }
247
248 BOOLEAN
249 HasFilePathEfiExtension (
250 IN CHAR16* FilePath
251 )
252 {
253 return (StrCmp (FilePath + (StrSize (FilePath) / sizeof (CHAR16)) - 5, L".EFI") == 0) ||
254 (StrCmp (FilePath + (StrSize (FilePath) / sizeof (CHAR16)) - 5, L".efi") == 0);
255 }
256
257 // Return the last non end-type Device Path Node from a Device Path
258 EFI_DEVICE_PATH*
259 GetLastDevicePathNode (
260 IN EFI_DEVICE_PATH* DevicePath
261 )
262 {
263 EFI_DEVICE_PATH* PrevDevicePathNode;
264
265 PrevDevicePathNode = DevicePath;
266 while (!IsDevicePathEndType (DevicePath)) {
267 PrevDevicePathNode = DevicePath;
268 DevicePath = NextDevicePathNode (DevicePath);
269 }
270
271 return PrevDevicePathNode;
272 }
273
274 EFI_STATUS
275 GenerateDeviceDescriptionName (
276 IN EFI_HANDLE Handle,
277 IN OUT CHAR16* Description
278 )
279 {
280 EFI_STATUS Status;
281 EFI_COMPONENT_NAME_PROTOCOL* ComponentName2Protocol;
282 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
283 EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol;
284 CHAR16* DriverName;
285 CHAR16* DevicePathTxt;
286 EFI_DEVICE_PATH* DevicePathNode;
287
288 ComponentName2Protocol = NULL;
289 Status = gBS->HandleProtocol (Handle, &gEfiComponentName2ProtocolGuid, (VOID **)&ComponentName2Protocol);
290 if (!EFI_ERROR(Status)) {
291 //TODO: Fixme. we must find the best langague
292 Status = ComponentName2Protocol->GetDriverName (ComponentName2Protocol,"en",&DriverName);
293 if (!EFI_ERROR(Status)) {
294 StrnCpy (Description, DriverName, BOOT_DEVICE_DESCRIPTION_MAX);
295 }
296 }
297
298 if (EFI_ERROR(Status)) {
299 // Use the lastest non null entry of the Device path as a description
300 Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol);
301 if (EFI_ERROR(Status)) {
302 return Status;
303 }
304
305 // Convert the last non end-type Device Path Node in text for the description
306 DevicePathNode = GetLastDevicePathNode (DevicePathProtocol);
307 Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
308 ASSERT_EFI_ERROR(Status);
309 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (DevicePathNode, TRUE, TRUE);
310 StrnCpy (Description, DevicePathTxt, BOOT_DEVICE_DESCRIPTION_MAX);
311 FreePool (DevicePathTxt);
312 }
313
314 return EFI_SUCCESS;
315 }
316
317 EFI_STATUS
318 BdsStartBootOption (
319 IN CHAR16* BootOption
320 )
321 {
322 EFI_STATUS Status;
323 BDS_LOAD_OPTION *BdsLoadOption;
324
325 Status = BootOptionFromLoadOptionVariable (BootOption, &BdsLoadOption);
326 if (!EFI_ERROR(Status)) {
327 Status = BootOptionStart (BdsLoadOption);
328 FreePool (BdsLoadOption);
329
330 if (!EFI_ERROR(Status)) {
331 Status = EFI_SUCCESS;
332 } else {
333 Status = EFI_NOT_STARTED;
334 }
335 } else {
336 Status = EFI_NOT_FOUND;
337 }
338 return Status;
339 }
340
341 UINTN
342 GetUnalignedDevicePathSize (
343 IN EFI_DEVICE_PATH* DevicePath
344 )
345 {
346 UINTN Size;
347 EFI_DEVICE_PATH* AlignedDevicePath;
348
349 if ((UINTN)DevicePath & 0x1) {
350 AlignedDevicePath = DuplicateDevicePath (DevicePath);
351 Size = GetDevicePathSize (AlignedDevicePath);
352 FreePool (AlignedDevicePath);
353 } else {
354 Size = GetDevicePathSize (DevicePath);
355 }
356 return Size;
357 }
358
359 EFI_DEVICE_PATH*
360 GetAlignedDevicePath (
361 IN EFI_DEVICE_PATH* DevicePath
362 )
363 {
364 if ((UINTN)DevicePath & 0x1) {
365 return DuplicateDevicePath (DevicePath);
366 } else {
367 return DevicePath;
368 }
369 }
370
371 BOOLEAN
372 IsUnicodeString (
373 IN VOID* String
374 )
375 {
376 // We do not support NULL pointer
377 ASSERT (String != NULL);
378
379 if (*(CHAR16*)String < 0x100) {
380 //Note: We could get issue if the string is an empty Ascii string...
381 return TRUE;
382 } else {
383 return FALSE;
384 }
385 }
386
387 /*
388 * Try to detect if the given string is an ASCII or Unicode string
389 *
390 * There are actually few limitation to this function but it is mainly to give
391 * a user friendly output.
392 *
393 * Some limitations:
394 * - it only supports unicode string that use ASCII character (< 0x100)
395 * - single character ASCII strings are interpreted as Unicode string
396 * - string cannot be longer than 2 x BOOT_DEVICE_OPTION_MAX (600 bytes)
397 *
398 * @param String Buffer that might contain a Unicode or Ascii string
399 * @param IsUnicode If not NULL this boolean value returns if the string is an
400 * ASCII or Unicode string.
401 */
402 BOOLEAN
403 IsPrintableString (
404 IN VOID* String,
405 OUT BOOLEAN *IsUnicode
406 )
407 {
408 BOOLEAN UnicodeDetected;
409 BOOLEAN IsPrintable;
410 UINTN Index;
411 CHAR16 Character;
412
413 // We do not support NULL pointer
414 ASSERT (String != NULL);
415
416 // Test empty string
417 if (*(CHAR16*)String == L'\0') {
418 if (IsUnicode) {
419 *IsUnicode = TRUE;
420 }
421 return TRUE;
422 } else if (*(CHAR16*)String == '\0') {
423 if (IsUnicode) {
424 *IsUnicode = FALSE;
425 }
426 return TRUE;
427 }
428
429 // Limitation: if the string is an ASCII single character string. This comparison
430 // will assume it is a Unicode string.
431 if (*(CHAR16*)String < 0x100) {
432 UnicodeDetected = TRUE;
433 } else {
434 UnicodeDetected = FALSE;
435 }
436
437 IsPrintable = FALSE;
438 for (Index = 0; Index < BOOT_DEVICE_OPTION_MAX * 2; Index++) {
439 if (UnicodeDetected) {
440 Character = ((CHAR16*)String)[Index];
441 } else {
442 Character = ((CHAR8*)String)[Index];
443 }
444
445 if (Character == '\0') {
446 // End of the string
447 IsPrintable = TRUE;
448 break;
449 } else if ((Character < 0x20) || (Character > 0x7f)) {
450 // We only support the range of printable ASCII character
451 IsPrintable = FALSE;
452 break;
453 }
454 }
455
456 if (IsPrintable && IsUnicode) {
457 *IsUnicode = UnicodeDetected;
458 }
459
460 return IsPrintable;
461 }