xref: /netbsd-src/sys/external/bsd/gnu-efi/dist/lib/cmdline.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: cmdline.c,v 1.1.1.1 2018/08/16 18:17:47 jmcneill Exp $	*/
2 
3 #include "lib.h"
4 
5 #include "efiprot.h"
6 #include "efishellintf.h"
7 #include "efishellparm.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 EfiShellParametersProtocolGuid
83       = EFI_SHELL_PARAMETERS_PROTOCOL_GUID;
84   static const EFI_GUID ShellInterfaceProtocolGuid
85       = SHELL_INTERFACE_PROTOCOL_GUID;
86   EFI_SHELL_PARAMETERS_PROTOCOL *EfiShellParametersProtocol = NULL;
87   EFI_SHELL_INTERFACE *EfiShellInterfaceProtocol = NULL;
88 
89   Status = uefi_call_wrapper(BS->OpenProtocol, 6,
90                              ImageHandle,
91                              (EFI_GUID*)&EfiShellParametersProtocolGuid,
92                              (VOID **)&EfiShellParametersProtocol,
93                              ImageHandle,
94                              NULL,
95                              EFI_OPEN_PROTOCOL_GET_PROTOCOL
96                              );
97   if (!EFI_ERROR(Status))
98   {
99     // use shell 2.0 interface
100     // Print(L"Got argc/argv from shell intf proto\n");
101     *Argv = EfiShellParametersProtocol->Argv;
102     return EfiShellParametersProtocol->Argc;
103   }
104 
105   // try to get shell 1.0 interface instead.
106   Status = uefi_call_wrapper(BS->OpenProtocol, 6,
107                              ImageHandle,
108                              (EFI_GUID*)&ShellInterfaceProtocolGuid,
109                              (VOID **)&EfiShellInterfaceProtocol,
110                              ImageHandle,
111                              NULL,
112                              EFI_OPEN_PROTOCOL_GET_PROTOCOL
113                              );
114   if (!EFI_ERROR(Status))
115   {
116     // Print(L"Got argc/argv from shell params proto\n");
117     *Argv = EfiShellInterfaceProtocol->Argv;
118     return EfiShellInterfaceProtocol->Argc;
119   }
120 
121   // shell 1.0 and 2.0 interfaces failed
122   return GetShellArgcArgvFromLoadedImage(ImageHandle, Argv);
123 }
124