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
EditHIInputStr(IN OUT CHAR16 * CmdLine,IN UINTN MaxCmdLine)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
GetHIInputStr(IN OUT CHAR16 * CmdLine,IN UINTN MaxCmdLine)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
EditHIInputAscii(IN OUT CHAR8 * CmdLine,IN UINTN MaxCmdLine)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
GetHIInputAscii(IN OUT CHAR8 * CmdLine,IN UINTN MaxCmdLine)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
GetHIInputInteger(OUT UINTN * Integer)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. As long as the user does not define a valid IP
148   address, he is asked for one. He can always escape by
149   pressing ESC.
150 
151   @param[out]  EFI_IP_ADDRESS  OutIpAddr  Returned IPv4 address. Valid if
152                                           and only if the returned value
153                                           is equal to EFI_SUCCESS
154 
155   @retval  EFI_SUCCESS            Input completed
156   @retval  EFI_ABORTED            Editing aborted by the user
157   @retval  EFI_OUT_OF_RESOURCES   Fail to perform the operation due to
158                                   lack of resource
159 **/
160 EFI_STATUS
GetHIInputIP(OUT EFI_IP_ADDRESS * OutIpAddr)161 GetHIInputIP (
162   OUT  EFI_IP_ADDRESS  *OutIpAddr
163   )
164 {
165   EFI_STATUS  Status;
166   CHAR16      CmdLine[48];
167 
168   while (TRUE) {
169     CmdLine[0] = '\0';
170     Status = EditHIInputStr (CmdLine, 48);
171     if (EFI_ERROR (Status)) {
172       return EFI_ABORTED;
173     }
174 
175     Status = NetLibStrToIp4 (CmdLine, &OutIpAddr->v4);
176     if (Status == EFI_INVALID_PARAMETER) {
177       Print (L"Invalid address\n");
178     } else {
179       return Status;
180     }
181   }
182 }
183 
184 /**
185   Edit an IPv4 address
186 
187   The function displays as a string following the "%d.%d.%d.%d" format the
188   IPv4 address that is passed in and asks the user to modify it. If the
189   resulting string defines a valid IPv4 address, the four bytes of the
190   corresponding IPv4 address are extracted from the string and returned by
191   the function. As long as the user does not define a valid IP
192   address, he is asked for one. He can always escape by
193   pressing ESC.
194 
195   @param[in ]  EFI_IP_ADDRESS  InIpAddr   Input IPv4 address
196   @param[out]  EFI_IP_ADDRESS  OutIpAddr  Returned IPv4 address. Valid if
197                                           and only if the returned value
198                                           is equal to EFI_SUCCESS
199 
200   @retval  EFI_SUCCESS            Update completed
201   @retval  EFI_ABORTED            Editing aborted by the user
202   @retval  EFI_INVALID_PARAMETER  The string returned by the user is
203                                   mal-formated
204   @retval  EFI_OUT_OF_RESOURCES   Fail to perform the operation due to
205                                   lack of resource
206 **/
207 EFI_STATUS
EditHIInputIP(IN EFI_IP_ADDRESS * InIpAddr,OUT EFI_IP_ADDRESS * OutIpAddr)208 EditHIInputIP (
209   IN   EFI_IP_ADDRESS  *InIpAddr,
210   OUT  EFI_IP_ADDRESS  *OutIpAddr
211   )
212 {
213   EFI_STATUS  Status;
214   CHAR16      CmdLine[48];
215 
216   while (TRUE) {
217     UnicodeSPrint (
218       CmdLine, 48, L"%d.%d.%d.%d",
219       InIpAddr->v4.Addr[0], InIpAddr->v4.Addr[1],
220       InIpAddr->v4.Addr[2], InIpAddr->v4.Addr[3]
221       );
222 
223     Status = EditHIInputStr (CmdLine, 48);
224     if (EFI_ERROR (Status)) {
225       return EFI_ABORTED;
226     }
227     Status = NetLibStrToIp4 (CmdLine, &OutIpAddr->v4);
228     if (Status == EFI_INVALID_PARAMETER) {
229       Print (L"Invalid address\n");
230     } else {
231       return Status;
232     }
233   }
234 }
235 
236 EFI_STATUS
GetHIInputBoolean(OUT BOOLEAN * Value)237 GetHIInputBoolean (
238   OUT BOOLEAN *Value
239   )
240 {
241   CHAR16      CmdBoolean[2];
242   EFI_STATUS  Status;
243 
244   while(1) {
245     Print (L"[y/n] ");
246     Status = GetHIInputStr (CmdBoolean, 2);
247     if (EFI_ERROR(Status)) {
248       return Status;
249     } else if ((CmdBoolean[0] == L'y') || (CmdBoolean[0] == L'Y')) {
250       if (Value) *Value = TRUE;
251       return EFI_SUCCESS;
252     } else if ((CmdBoolean[0] == L'n') || (CmdBoolean[0] == L'N')) {
253       if (Value) *Value = FALSE;
254       return EFI_SUCCESS;
255     }
256   }
257 }
258 
259 // Return the last non end-type Device Path Node from a Device Path
260 EFI_DEVICE_PATH*
GetLastDevicePathNode(IN EFI_DEVICE_PATH * DevicePath)261 GetLastDevicePathNode (
262   IN EFI_DEVICE_PATH*  DevicePath
263   )
264 {
265   EFI_DEVICE_PATH*     PrevDevicePathNode;
266 
267   PrevDevicePathNode = DevicePath;
268   while (!IsDevicePathEndType (DevicePath)) {
269     PrevDevicePathNode = DevicePath;
270     DevicePath = NextDevicePathNode (DevicePath);
271   }
272 
273   return PrevDevicePathNode;
274 }
275 
276 EFI_STATUS
GenerateDeviceDescriptionName(IN EFI_HANDLE Handle,IN OUT CHAR16 * Description)277 GenerateDeviceDescriptionName (
278   IN  EFI_HANDLE  Handle,
279   IN OUT CHAR16*  Description
280   )
281 {
282   EFI_STATUS                        Status;
283   EFI_COMPONENT_NAME_PROTOCOL*      ComponentName2Protocol;
284   EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
285   EFI_DEVICE_PATH_PROTOCOL*         DevicePathProtocol;
286   CHAR16*                           DriverName;
287   CHAR16*                           DevicePathTxt;
288   EFI_DEVICE_PATH*                  DevicePathNode;
289 
290   ComponentName2Protocol = NULL;
291   Status = gBS->HandleProtocol (Handle, &gEfiComponentName2ProtocolGuid, (VOID **)&ComponentName2Protocol);
292   if (!EFI_ERROR(Status)) {
293     //TODO: Fixme. we must find the best langague
294     Status = ComponentName2Protocol->GetDriverName (ComponentName2Protocol,"en",&DriverName);
295     if (!EFI_ERROR(Status)) {
296       StrnCpy (Description, DriverName, BOOT_DEVICE_DESCRIPTION_MAX);
297     }
298   }
299 
300   if (EFI_ERROR(Status)) {
301     // Use the lastest non null entry of the Device path as a description
302     Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol);
303     if (EFI_ERROR(Status)) {
304       return Status;
305     }
306 
307     // Convert the last non end-type Device Path Node in text for the description
308     DevicePathNode = GetLastDevicePathNode (DevicePathProtocol);
309     Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
310     ASSERT_EFI_ERROR(Status);
311     DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (DevicePathNode, TRUE, TRUE);
312     StrnCpy (Description, DevicePathTxt, BOOT_DEVICE_DESCRIPTION_MAX);
313     FreePool (DevicePathTxt);
314   }
315 
316   return EFI_SUCCESS;
317 }
318 
319 EFI_STATUS
BdsStartBootOption(IN CHAR16 * BootOption)320 BdsStartBootOption (
321   IN CHAR16* BootOption
322   )
323 {
324   EFI_STATUS          Status;
325   BDS_LOAD_OPTION     *BdsLoadOption;
326 
327   Status = BootOptionFromLoadOptionVariable (BootOption, &BdsLoadOption);
328   if (!EFI_ERROR(Status)) {
329     Status = BootOptionStart (BdsLoadOption);
330     FreePool (BdsLoadOption);
331 
332     if (!EFI_ERROR(Status)) {
333       Status = EFI_SUCCESS;
334     } else {
335       Status = EFI_NOT_STARTED;
336     }
337   } else {
338     Status = EFI_NOT_FOUND;
339   }
340   return Status;
341 }
342 
343 UINTN
GetUnalignedDevicePathSize(IN EFI_DEVICE_PATH * DevicePath)344 GetUnalignedDevicePathSize (
345   IN EFI_DEVICE_PATH* DevicePath
346   )
347 {
348   UINTN Size;
349   EFI_DEVICE_PATH* AlignedDevicePath;
350 
351   if ((UINTN)DevicePath & 0x1) {
352     AlignedDevicePath = DuplicateDevicePath (DevicePath);
353     Size = GetDevicePathSize (AlignedDevicePath);
354     FreePool (AlignedDevicePath);
355   } else {
356     Size = GetDevicePathSize (DevicePath);
357   }
358   return Size;
359 }
360 
361 EFI_DEVICE_PATH*
GetAlignedDevicePath(IN EFI_DEVICE_PATH * DevicePath)362 GetAlignedDevicePath (
363   IN EFI_DEVICE_PATH* DevicePath
364   )
365 {
366   if ((UINTN)DevicePath & 0x1) {
367     return DuplicateDevicePath (DevicePath);
368   } else {
369     return DevicePath;
370   }
371 }
372 
373 BOOLEAN
IsUnicodeString(IN VOID * String)374 IsUnicodeString (
375   IN VOID* String
376   )
377 {
378   // We do not support NULL pointer
379   ASSERT (String != NULL);
380 
381   if (*(CHAR16*)String < 0x100) {
382     //Note: We could get issue if the string is an empty Ascii string...
383     return TRUE;
384   } else {
385     return FALSE;
386   }
387 }
388 
389 /*
390  * Try to detect if the given string is an ASCII or Unicode string
391  *
392  * There are actually few limitation to this function but it is mainly to give
393  * a user friendly output.
394  *
395  * Some limitations:
396  *   - it only supports unicode string that use ASCII character (< 0x100)
397  *   - single character ASCII strings are interpreted as Unicode string
398  *   - string cannot be longer than BOOT_DEVICE_OPTION_MAX characters and
399  *     thus (BOOT_DEVICE_OPTION_MAX*2) bytes for an Unicode string and
400  *     BOOT_DEVICE_OPTION_MAX bytes for an ASCII string.
401  *
402  * @param String    Buffer that might contain a Unicode or Ascii string
403  * @param IsUnicode If not NULL this boolean value returns if the string is an
404  *                  ASCII or Unicode string.
405  */
406 BOOLEAN
IsPrintableString(IN VOID * String,OUT BOOLEAN * IsUnicode)407 IsPrintableString (
408   IN  VOID*    String,
409   OUT BOOLEAN *IsUnicode
410   )
411 {
412   BOOLEAN UnicodeDetected;
413   BOOLEAN IsPrintable;
414   UINTN Index;
415   CHAR16 Character;
416 
417   // We do not support NULL pointer
418   ASSERT (String != NULL);
419 
420   // Test empty string
421   if (*(CHAR16*)String == L'\0') {
422     if (IsUnicode) {
423       *IsUnicode = TRUE;
424     }
425     return TRUE;
426   } else if (*(CHAR16*)String == '\0') {
427     if (IsUnicode) {
428       *IsUnicode = FALSE;
429     }
430     return TRUE;
431   }
432 
433   // Limitation: if the string is an ASCII single character string. This comparison
434   // will assume it is a Unicode string.
435   if (*(CHAR16*)String < 0x100) {
436     UnicodeDetected = TRUE;
437   } else {
438     UnicodeDetected = FALSE;
439   }
440 
441   IsPrintable = FALSE;
442   for (Index = 0; Index < BOOT_DEVICE_OPTION_MAX; Index++) {
443     if (UnicodeDetected) {
444       Character = ((CHAR16*)String)[Index];
445     } else {
446       Character = ((CHAR8*)String)[Index];
447     }
448 
449     if (Character == '\0') {
450       // End of the string
451       IsPrintable = TRUE;
452       break;
453     } else if ((Character < 0x20) || (Character > 0x7f)) {
454       // We only support the range of printable ASCII character
455       IsPrintable = FALSE;
456       break;
457     }
458   }
459 
460   if (IsPrintable && IsUnicode) {
461     *IsUnicode = UnicodeDetected;
462   }
463 
464   return IsPrintable;
465 }
466