Home | History | Annotate | Line # | Download | only in lib
      1 /*	$NetBSD: cmdline.c,v 1.1.1.2 2021/09/30 18:50:09 jmcneill Exp $	*/
      2 
      3 #include "lib.h"
      4 
      5 #include "efiprot.h"
      6 #include "efishell.h"
      7 #include "efishellintf.h"
      8 
      9 #ifndef MAX_ARGV_CONTENTS_SIZE
     10 # define MAX_CMDLINE_SIZE 1024
     11 #endif
     12 #ifndef MAX_ARGC
     13 # define MAX_CMDLINE_ARGC 32
     14 #endif
     15 
     16 /*
     17   Parse LoadedImage options area, called only in case the regular
     18   shell protos are not available.
     19 
     20   Format of LoadedImage->LoadOptions appears to be a
     21   single-space-separated list of args (looks like the shell already
     22   pre-parses the input, it apparently folds several consecutive spaces
     23   into one):
     24     argv[0] space argv[1] (etc.) argv[N] space \0 cwd \0 other data
     25   For safety, we support the trailing \0 without a space before, as
     26   well as several consecutive spaces (-> several args).
     27 */
     28 static
     29 INTN
     30 GetShellArgcArgvFromLoadedImage(
     31     EFI_HANDLE ImageHandle,
     32     CHAR16 **ResultArgv[]
     33     )
     34 {
     35   EFI_STATUS Status;
     36   void *LoadedImage = NULL;
     37   static CHAR16 ArgvContents[MAX_CMDLINE_SIZE];
     38   static CHAR16 *Argv[MAX_CMDLINE_ARGC], *ArgStart, *c;
     39   UINTN Argc = 0, BufLen;
     40 
     41   Status = uefi_call_wrapper(BS->OpenProtocol, 6,
     42                              ImageHandle,
     43                              &LoadedImageProtocol,
     44                              &LoadedImage,
     45                              ImageHandle,
     46                              NULL,
     47                              EFI_OPEN_PROTOCOL_GET_PROTOCOL
     48                              );
     49   if (EFI_ERROR(Status))
     50     return -1;
     51 
     52   BufLen = ((EFI_LOADED_IMAGE *)LoadedImage)->LoadOptionsSize;
     53   if (BufLen < 2)  /* We are expecting at least a \0 */
     54     return -1;
     55   else if (BufLen > sizeof(ArgvContents))
     56     BufLen = sizeof(ArgvContents);
     57 
     58   CopyMem(ArgvContents, ((EFI_LOADED_IMAGE *)LoadedImage)->LoadOptions, BufLen);
     59   ArgvContents[MAX_CMDLINE_SIZE - 1] = L'\0';
     60 
     61   for (c = ArgStart = ArgvContents ; *c != L'\0' ; ++c) {
     62     if (*c == L' ') {
     63       *c = L'\0';
     64       if (Argc < MAX_CMDLINE_ARGC) Argv[Argc++] = ArgStart;
     65       ArgStart = c + 1;
     66     }
     67   }
     68 
     69   if ((*ArgStart != L'\0') && (Argc < MAX_CMDLINE_ARGC))
     70     Argv[Argc++] = ArgStart;
     71 
     72   // Print(L"Got argc/argv from loaded image proto\n");
     73   *ResultArgv = Argv;
     74   return Argc;
     75 }
     76 
     77 INTN GetShellArgcArgv(EFI_HANDLE ImageHandle, CHAR16 **Argv[])
     78 {
     79   // Code inspired from EDK2's
     80   // ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.c (BSD)
     81   EFI_STATUS Status;
     82   static const EFI_GUID ShellInterfaceProtocolGuid
     83       = SHELL_INTERFACE_PROTOCOL_GUID;
     84   EFI_SHELL_PARAMETERS_PROTOCOL *EfiShellParametersProtocol = NULL;
     85   EFI_SHELL_INTERFACE *EfiShellInterfaceProtocol = NULL;
     86 
     87   Status = uefi_call_wrapper(BS->OpenProtocol, 6,
     88                              ImageHandle,
     89                              (EFI_GUID*)&ShellParametersProtocolGuid,
     90                              (VOID **)&EfiShellParametersProtocol,
     91                              ImageHandle,
     92                              NULL,
     93                              EFI_OPEN_PROTOCOL_GET_PROTOCOL
     94                              );
     95   if (!EFI_ERROR(Status))
     96   {
     97     // use shell 2.0 interface
     98     // Print(L"Got argc/argv from shell intf proto\n");
     99     *Argv = EfiShellParametersProtocol->Argv;
    100     return EfiShellParametersProtocol->Argc;
    101   }
    102 
    103   // try to get shell 1.0 interface instead.
    104   Status = uefi_call_wrapper(BS->OpenProtocol, 6,
    105                              ImageHandle,
    106                              (EFI_GUID*)&ShellInterfaceProtocolGuid,
    107                              (VOID **)&EfiShellInterfaceProtocol,
    108                              ImageHandle,
    109                              NULL,
    110                              EFI_OPEN_PROTOCOL_GET_PROTOCOL
    111                              );
    112   if (!EFI_ERROR(Status))
    113   {
    114     // Print(L"Got argc/argv from shell params proto\n");
    115     *Argv = EfiShellInterfaceProtocol->Argv;
    116     return EfiShellInterfaceProtocol->Argc;
    117   }
    118 
    119   // shell 1.0 and 2.0 interfaces failed
    120   return GetShellArgcArgvFromLoadedImage(ImageHandle, Argv);
    121 }
    122