1 /** @file
2 *
3 *  Copyright (c) 2011-2015, 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 "LinuxLoader.h"
16 
17 /**
18   Extract the next item from the command line.
19 
20   The items are separated by spaces. Quotation marks (") are used for argument
21   grouping and the escaping character is "^" as for the EFI Shell command lines.
22 
23   @param[in out]  CommandLine  Command line pointer.
24   @param[out]     Item         Pointer to the allocated buffer where the
25                                item is stored.
26 
27   @retval  EFI_SUCCESS           The token was found and extracted.
28   @retval  EFI_NOT_FOUND         No item found.
29   @retval  EFI_OUT_OF_RESOURCES  The memory allocation failed.
30 
31 **/
32 STATIC
33 EFI_STATUS
ExtractNextItem(IN OUT CONST CHAR16 ** CommandLine,OUT CHAR16 ** Item)34 ExtractNextItem (
35   IN OUT CONST CHAR16  **CommandLine,
36   OUT CHAR16           **Item
37   )
38 {
39   CONST CHAR16  *Walker;
40   VOID          *Buffer;
41   CHAR16        *WritePtr;
42   BOOLEAN       InQuotedString;
43   BOOLEAN       Interpret;
44 
45   for (Walker = *CommandLine; *Walker == L' '; Walker++) {
46     ;
47   }
48 
49   Buffer = AllocatePool (StrSize (Walker));
50   if (Buffer == NULL) {
51     return EFI_OUT_OF_RESOURCES;
52   }
53 
54   for (WritePtr = Buffer, Interpret = TRUE, InQuotedString = FALSE;
55        ((*Walker != L' ') || InQuotedString) && (*Walker != L'\0');
56        Walker++
57        ) {
58     if (Interpret) {
59       if (*Walker == L'^') {
60         Interpret = FALSE;
61         continue;
62       }
63       if (*Walker == L'"') {
64         InQuotedString = !InQuotedString;
65         continue;
66       }
67     } else {
68       Interpret = TRUE;
69     }
70     *(WritePtr++) = *Walker;
71   }
72 
73   if (WritePtr == Buffer) {
74     FreePool (Buffer);
75     return EFI_NOT_FOUND;
76   }
77 
78   *WritePtr = L'\0';
79   *CommandLine = Walker;
80   *Item        = Buffer;
81 
82   return EFI_SUCCESS;
83 }
84 
85 /**
86   Check if an item of the command line is a flag or not.
87 
88   @param[in]  Item  Command line item.
89 
90   @retval  TRUE   The item is a flag.
91   @retval  FALSE  The item is not a flag.
92 
93 **/
94 STATIC
95 BOOLEAN
IsFlag(IN CONST CHAR16 * Item)96 IsFlag (
97   IN CONST CHAR16 *Item
98   )
99 {
100   return ((Item[0] == L'-') && (Item[2] == L'\0'));
101 }
102 
103 /**
104   Process the application command line.
105 
106   @param[out]  KernelTextDevicePath  A pointer to the buffer where the device
107                                      path to the Linux kernel is stored. The
108                                      address of the buffer is NULL in case of
109                                      an error. Otherwise, the returned address
110                                      is the address of a buffer allocated with
111                                      a call to AllocatePool() that has to be
112                                      freed by the caller.
113   @param[out]  FdtTextDevicePath     A pointer to the buffer where the device
114                                      path to the FDT is stored. The address of
115                                      the buffer is NULL in case of an error or
116                                      if the device path to the FDT is not
117                                      defined. Otherwise, the returned address
118                                      is the address of a buffer allocated with
119                                      a call to AllocatePool() that has to be
120                                      freed by the caller.
121   @param[out]  InitrdTextDevicePath  A pointer to the buffer where the device
122                                      path to the RAM root file system is stored.
123                                      The address of the buffer is NULL in case
124                                      of an error or if the device path to the
125                                      RAM root file system is not defined.
126                                      Otherwise, the returned address is the
127                                      address of a buffer allocated with a call
128                                      to AllocatePool() that has to be freed by
129                                      the caller.
130   @param[out]  LinuxCommandLine      A pointer to the buffer where the Linux
131                                      kernel command line is stored. The address
132                                      of the buffer is NULL in case of an error
133                                      or if the Linux command line is not
134                                      defined. Otherwise, the returned address
135                                      is the address of a buffer allocated with
136                                      a call to AllocatePool() that has to be
137                                      freed by the caller.
138 
139   @param[out]  AtagMachineType       Value of the ARM Machine Type
140 
141   @retval  EFI_SUCCESS            The processing was successfull.
142   @retval  EFI_NOT_FOUND          EFI_LOADED_IMAGE_PROTOCOL not found.
143   @retval  EFI_NOT_FOUND          Path to the Linux kernel not found.
144   @retval  EFI_INVALID_PARAMETER  At least one parameter is not valid or there is a
145                                   conflict between two parameters.
146   @retval  EFI_OUT_OF_RESOURCES   A memory allocation failed.
147 
148 **/
149 EFI_STATUS
ProcessAppCommandLine(OUT CHAR16 ** KernelTextDevicePath,OUT CHAR16 ** FdtTextDevicePath,OUT CHAR16 ** InitrdTextDevicePath,OUT CHAR16 ** LinuxCommandLine,OUT UINTN * AtagMachineType)150 ProcessAppCommandLine (
151   OUT  CHAR16   **KernelTextDevicePath,
152   OUT  CHAR16   **FdtTextDevicePath,
153   OUT  CHAR16   **InitrdTextDevicePath,
154   OUT  CHAR16   **LinuxCommandLine,
155   OUT  UINTN    *AtagMachineType
156   )
157 {
158   EFI_STATUS                 Status;
159   EFI_STATUS                 Status2;
160   EFI_LOADED_IMAGE_PROTOCOL  *LoadedImage;
161   CONST CHAR16               *Walker;
162   CHAR16                     *Item;
163   CHAR16                     Flag;
164   BOOLEAN                    HasAtagSupport;
165   BOOLEAN                    HasFdtSupport;
166 
167   *KernelTextDevicePath = NULL;
168   *FdtTextDevicePath    = NULL;
169   *InitrdTextDevicePath = NULL;
170   *LinuxCommandLine     = NULL;
171   *AtagMachineType      = ARM_FDT_MACHINE_TYPE;
172 
173   HasAtagSupport        = FALSE;
174   HasFdtSupport         = FALSE;
175 
176   Status = gBS->HandleProtocol (
177                   gImageHandle,
178                   &gEfiLoadedImageProtocolGuid,
179                   (VOID**)&LoadedImage
180                   );
181   if (EFI_ERROR (Status)) {
182     ASSERT_EFI_ERROR (Status);
183     return Status;
184   }
185 
186   Walker = (CHAR16*)LoadedImage->LoadOptions;
187   if (Walker == NULL) {
188     PrintHelp (NULL);
189     return EFI_INVALID_PARAMETER;
190   }
191 
192   //
193   // Get the device path to the Linux kernel.
194   //
195 
196   Status = ExtractNextItem (&Walker, &Item);
197   if (!EFI_ERROR (Status)) {
198     if (!IsFlag (Item)) {
199       *KernelTextDevicePath = Item;
200     } else {
201       PrintHii (NULL, STRING_TOKEN (STR_MISSING_KERNEL_PATH));
202       FreePool (Item);
203       return EFI_NOT_FOUND;
204     }
205   } else {
206     if (Status != EFI_NOT_FOUND) {
207       return Status;
208     }
209     PrintHelp (NULL);
210     return EFI_INVALID_PARAMETER;
211   }
212 
213   Status = EFI_INVALID_PARAMETER;
214   while (*Walker != L'\0') {
215     Status2 = ExtractNextItem (&Walker, &Item);
216     if (Status2 == EFI_NOT_FOUND) {
217       break;
218     }
219     if (EFI_ERROR (Status2)) {
220       Status = Status2;
221       goto Error;
222     }
223 
224     if (!IsFlag (Item)) {
225       PrintHii (NULL, STRING_TOKEN (STR_INVALID_FLAG), Item[0], Item[1]);
226       FreePool (Item);
227       goto Error;
228     }
229     Flag = Item[1];
230     FreePool (Item);
231 
232     Status2 = ExtractNextItem (&Walker, &Item);
233     if (Status2 == EFI_NOT_FOUND) {
234       PrintHii (NULL, STRING_TOKEN (STR_MISSING_VALUE), Flag);
235       goto Error;
236     }
237     if (EFI_ERROR (Status2)) {
238       Status = Status2;
239       goto Error;
240     }
241     if (IsFlag (Item)) {
242       PrintHii (NULL, STRING_TOKEN (STR_MISSING_VALUE), Flag);
243       FreePool (Item);
244       goto Error;
245     }
246 
247     switch (Flag) {
248     case  L'a':
249       if (HasFdtSupport) {
250         PrintHii (NULL, STRING_TOKEN (STR_ATAG_FDT_CONFLICT));
251         goto Error;
252       }
253       *AtagMachineType = StrDecimalToUintn (Item);
254       HasAtagSupport = TRUE;
255       break;
256     case L'd':
257       *FdtTextDevicePath = Item;
258       if (HasAtagSupport) {
259         PrintHii (NULL, STRING_TOKEN (STR_ATAG_FDT_CONFLICT));
260         goto Error;
261       }
262       HasFdtSupport = TRUE;
263       break;
264 
265     case L'c':
266       *LinuxCommandLine = Item;
267       break;
268 
269     case L'f':
270       *InitrdTextDevicePath = Item;
271       break;
272 
273     default:
274       PrintHii (NULL, STRING_TOKEN (STR_INVALID_FLAG), L'-', Flag);
275       FreePool (Item);
276       goto Error;
277     }
278   }
279 
280   Status = EFI_SUCCESS;
281 
282 Error:
283   if (EFI_ERROR (Status)) {
284     if (*KernelTextDevicePath != NULL) {
285       FreePool (*KernelTextDevicePath);
286       *KernelTextDevicePath = NULL;
287     }
288     if (*FdtTextDevicePath != NULL) {
289       FreePool (*FdtTextDevicePath);
290       *FdtTextDevicePath = NULL;
291     }
292     if (*InitrdTextDevicePath != NULL) {
293       FreePool (*InitrdTextDevicePath);
294       *InitrdTextDevicePath = NULL;
295     }
296     if (*LinuxCommandLine != NULL) {
297       FreePool (*LinuxCommandLine);
298       *LinuxCommandLine = NULL;
299     }
300   }
301 
302   return Status;
303 }
304