Home | History | Annotate | Download | only in Bds
      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. 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
    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
    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
    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*
    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
    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
    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
    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*
    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
    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
    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