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