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