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
GetShellArgcArgvFromLoadedImage(EFI_HANDLE ImageHandle,CHAR16 ** ResultArgv[])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
GetShellArgcArgv(EFI_HANDLE ImageHandle,CHAR16 ** Argv[])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