1 /** @file
2   Main file for LoadPciRom shell Debug1 function.
3 
4   (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
5   Copyright (c) 2005 - 2019, Intel Corporation. All rights reserved.<BR>
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include "UefiShellDebug1CommandsLib.h"
11 #include <IndustryStandard/Pci22.h>
12 #include <IndustryStandard/Pci23.h>
13 #include <IndustryStandard/PeImage.h>
14 #include <Protocol/Decompress.h>
15 
16 /**
17   Connects all available drives and controllers.
18 
19   @retval EFI_SUCCESS     The operation was successful.
20   @retval EFI_ABORTED     The abort mechanism was received.
21 **/
22 EFI_STATUS
23 LoadPciRomConnectAllDriversToAllControllers (
24   VOID
25   );
26 
27 /**
28   Command entry point.
29 
30   @param[in] RomBar       The Rom Base address.
31   @param[in] RomSize      The Rom size.
32   @param[in] FileName     The file name.
33 
34   @retval EFI_SUCCESS             The command completed successfully.
35   @retval EFI_INVALID_PARAMETER   Command usage error.
36   @retval EFI_UNSUPPORTED         Protocols unsupported.
37   @retval EFI_OUT_OF_RESOURCES    Out of memory.
38   @retval Other value             Unknown error.
39 **/
40 EFI_STATUS
41 LoadEfiDriversFromRomImage (
42   VOID                      *RomBar,
43   UINTN                     RomSize,
44   CONST CHAR16              *FileName
45   );
46 
47 STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
48   {L"-nc", TypeFlag},
49   {NULL, TypeMax}
50   };
51 
52 /**
53   Function for 'loadpcirom' command.
54 
55   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
56   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
57 **/
58 SHELL_STATUS
59 EFIAPI
ShellCommandRunLoadPciRom(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)60 ShellCommandRunLoadPciRom (
61   IN EFI_HANDLE        ImageHandle,
62   IN EFI_SYSTEM_TABLE  *SystemTable
63   )
64 {
65   EFI_SHELL_FILE_INFO     *FileList;
66   UINTN                   SourceSize;
67   UINT8                   *File1Buffer;
68   EFI_STATUS              Status;
69   LIST_ENTRY              *Package;
70   CHAR16                  *ProblemParam;
71   SHELL_STATUS            ShellStatus;
72   BOOLEAN                 Connect;
73   CONST CHAR16            *Param;
74   UINTN                   ParamCount;
75   EFI_SHELL_FILE_INFO     *Node;
76   //
77   // Local variable initializations
78   //
79   File1Buffer = NULL;
80   ShellStatus = SHELL_SUCCESS;
81   FileList    = NULL;
82 
83 
84   //
85   // verify number of arguments
86   //
87   Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
88   if (EFI_ERROR(Status)) {
89     if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
90       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellDebug1HiiHandle, L"loadpcirom", ProblemParam);
91       FreePool(ProblemParam);
92       ShellStatus = SHELL_INVALID_PARAMETER;
93     } else {
94       ASSERT(FALSE);
95     }
96   } else {
97     if (ShellCommandLineGetCount(Package) < 2) {
98       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellDebug1HiiHandle, L"loadpcirom");
99       ShellStatus = SHELL_INVALID_PARAMETER;
100     } else {
101       if (ShellCommandLineGetFlag(Package, L"-nc")) {
102         Connect = FALSE;
103       } else {
104         Connect = TRUE;
105       }
106 
107       //
108       // get a list with each file specified by parameters
109       // if parameter is a directory then add all the files below it to the list
110       //
111       for ( ParamCount = 1, Param = ShellCommandLineGetRawValue(Package, ParamCount)
112           ; Param != NULL
113           ; ParamCount++, Param = ShellCommandLineGetRawValue(Package, ParamCount)
114          ){
115         Status = ShellOpenFileMetaArg((CHAR16*)Param, EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ, &FileList);
116         if (EFI_ERROR(Status)) {
117           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL), gShellDebug1HiiHandle, L"loadpcirom", Param);
118           ShellStatus = SHELL_ACCESS_DENIED;
119           break;
120         }
121       }
122       if (ShellStatus == SHELL_SUCCESS  && FileList != NULL) {
123         //
124         // loop through the list and make sure we are not aborting...
125         //
126         for ( Node = (EFI_SHELL_FILE_INFO*)GetFirstNode(&FileList->Link)
127             ; !IsNull(&FileList->Link, &Node->Link) && !ShellGetExecutionBreakFlag()
128             ; Node = (EFI_SHELL_FILE_INFO*)GetNextNode(&FileList->Link, &Node->Link)
129            ){
130           if (EFI_ERROR(Node->Status)){
131             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL), gShellDebug1HiiHandle, L"loadpcirom", Node->FullName);
132             ShellStatus = SHELL_INVALID_PARAMETER;
133             continue;
134           }
135           if (FileHandleIsDirectory(Node->Handle) == EFI_SUCCESS) {
136             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_FILE_NOT_DIR), gShellDebug1HiiHandle, L"loadpcirom", Node->FullName);
137             ShellStatus = SHELL_INVALID_PARAMETER;
138             continue;
139           }
140           SourceSize  = (UINTN) Node->Info->FileSize;
141           File1Buffer = AllocateZeroPool (SourceSize);
142           if (File1Buffer == NULL) {
143             ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_OUT_MEM), gShellDebug1HiiHandle, L"loadpcirom");
144             ShellStatus = SHELL_OUT_OF_RESOURCES;
145             continue;
146           }
147           Status = gEfiShellProtocol->ReadFile(Node->Handle, &SourceSize, File1Buffer);
148           if (EFI_ERROR(Status)) {
149             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_FILE_READ_FAIL), gShellDebug1HiiHandle, L"loadpcirom", Node->FullName);
150             ShellStatus = SHELL_INVALID_PARAMETER;
151           } else {
152             Status = LoadEfiDriversFromRomImage (
153                       File1Buffer,
154                       SourceSize,
155                       Node->FullName
156                      );
157 
158             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LOAD_PCI_ROM_RES), gShellDebug1HiiHandle, Node->FullName, Status);
159           }
160           FreePool(File1Buffer);
161         }
162       } else if (ShellStatus == SHELL_SUCCESS) {
163         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_FILE_NOT_SPEC), gShellDebug1HiiHandle, "loadpcirom");
164         ShellStatus = SHELL_NOT_FOUND;
165       }
166       if (FileList != NULL && !IsListEmpty(&FileList->Link)) {
167         Status = ShellCloseFileMetaArg(&FileList);
168       }
169       FileList = NULL;
170 
171       if (Connect) {
172         Status = LoadPciRomConnectAllDriversToAllControllers ();
173       }
174     }
175   }
176 
177   return (ShellStatus);
178 }
179 
180 /**
181   Command entry point.
182 
183   @param[in] RomBar       The Rom Base address.
184   @param[in] RomSize      The Rom size.
185   @param[in] FileName     The file name.
186 
187   @retval EFI_SUCCESS             The command completed successfully.
188   @retval EFI_INVALID_PARAMETER   Command usage error.
189   @retval EFI_UNSUPPORTED         Protocols unsupported.
190   @retval EFI_OUT_OF_RESOURCES    Out of memory.
191   @retval Other value             Unknown error.
192 **/
193 EFI_STATUS
LoadEfiDriversFromRomImage(VOID * RomBar,UINTN RomSize,CONST CHAR16 * FileName)194 LoadEfiDriversFromRomImage (
195   VOID                      *RomBar,
196   UINTN                     RomSize,
197   CONST CHAR16              *FileName
198   )
199 
200 {
201   EFI_PCI_EXPANSION_ROM_HEADER  *EfiRomHeader;
202   PCI_DATA_STRUCTURE            *Pcir;
203   UINTN                         ImageIndex;
204   UINTN                         RomBarOffset;
205   UINT32                        ImageSize;
206   UINT16                        ImageOffset;
207   EFI_HANDLE                    ImageHandle;
208   EFI_STATUS                    Status;
209   EFI_STATUS                    ReturnStatus;
210   CHAR16                        RomFileName[280];
211   EFI_DEVICE_PATH_PROTOCOL      *FilePath;
212   BOOLEAN                       SkipImage;
213   UINT32                        DestinationSize;
214   UINT32                        ScratchSize;
215   UINT8                         *Scratch;
216   VOID                          *ImageBuffer;
217   VOID                          *DecompressedImageBuffer;
218   UINT32                        ImageLength;
219   EFI_DECOMPRESS_PROTOCOL       *Decompress;
220   UINT32                        InitializationSize;
221 
222   ImageIndex    = 0;
223   ReturnStatus     = EFI_NOT_FOUND;
224   RomBarOffset  = (UINTN) RomBar;
225 
226   do {
227 
228     EfiRomHeader = (EFI_PCI_EXPANSION_ROM_HEADER *) (UINTN) RomBarOffset;
229 
230     if (EfiRomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
231       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LOADPCIROM_CORRUPT), gShellDebug1HiiHandle, L"loadpcirom", FileName, ImageIndex);
232 //      PrintToken (STRING_TOKEN (STR_LOADPCIROM_IMAGE_CORRUPT), HiiHandle, ImageIndex);
233       return ReturnStatus;
234     }
235 
236     //
237     // If the pointer to the PCI Data Structure is invalid, no further images can be located.
238     // The PCI Data Structure must be DWORD aligned.
239     //
240     if (EfiRomHeader->PcirOffset == 0 ||
241         (EfiRomHeader->PcirOffset & 3) != 0 ||
242         RomBarOffset - (UINTN)RomBar + EfiRomHeader->PcirOffset + sizeof (PCI_DATA_STRUCTURE) > RomSize) {
243       break;
244     }
245 
246     Pcir      = (PCI_DATA_STRUCTURE *) (UINTN) (RomBarOffset + EfiRomHeader->PcirOffset);
247     //
248     // If a valid signature is not present in the PCI Data Structure, no further images can be located.
249     //
250     if (Pcir->Signature != PCI_DATA_STRUCTURE_SIGNATURE) {
251       break;
252     }
253     ImageSize = Pcir->ImageLength * 512;
254     if (RomBarOffset - (UINTN)RomBar + ImageSize > RomSize) {
255       break;
256     }
257 
258     if ((Pcir->CodeType == PCI_CODE_TYPE_EFI_IMAGE) &&
259         (EfiRomHeader->EfiSignature == EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE) &&
260         ((EfiRomHeader->EfiSubsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) ||
261          (EfiRomHeader->EfiSubsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER))) {
262 
263       ImageOffset             = EfiRomHeader->EfiImageHeaderOffset;
264       InitializationSize      = EfiRomHeader->InitializationSize * 512;
265 
266       if (InitializationSize <= ImageSize && ImageOffset < InitializationSize) {
267 
268         ImageBuffer             = (VOID *) (UINTN) (RomBarOffset + ImageOffset);
269         ImageLength             = InitializationSize - ImageOffset;
270         DecompressedImageBuffer = NULL;
271 
272         //
273         // decompress here if needed
274         //
275         SkipImage = FALSE;
276         if (EfiRomHeader->CompressionType > EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED) {
277           SkipImage = TRUE;
278         }
279 
280         if (EfiRomHeader->CompressionType == EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED) {
281           Status = gBS->LocateProtocol (&gEfiDecompressProtocolGuid, NULL, (VOID**)&Decompress);
282           ASSERT_EFI_ERROR(Status);
283           if (EFI_ERROR (Status)) {
284             SkipImage = TRUE;
285           } else {
286             SkipImage = TRUE;
287             Status = Decompress->GetInfo (
288                                   Decompress,
289                                   ImageBuffer,
290                                   ImageLength,
291                                   &DestinationSize,
292                                   &ScratchSize
293                                  );
294             if (!EFI_ERROR (Status)) {
295               DecompressedImageBuffer = AllocateZeroPool (DestinationSize);
296               if (ImageBuffer != NULL) {
297                 Scratch = AllocateZeroPool (ScratchSize);
298                 if (Scratch != NULL) {
299                   Status = Decompress->Decompress (
300                                         Decompress,
301                                         ImageBuffer,
302                                         ImageLength,
303                                         DecompressedImageBuffer,
304                                         DestinationSize,
305                                         Scratch,
306                                         ScratchSize
307                                        );
308                   if (!EFI_ERROR (Status)) {
309                     ImageBuffer = DecompressedImageBuffer;
310                     ImageLength = DestinationSize;
311                     SkipImage   = FALSE;
312                   }
313 
314                   FreePool (Scratch);
315                 }
316               }
317             }
318           }
319         }
320 
321         if (!SkipImage) {
322           //
323           // load image and start image
324           //
325           UnicodeSPrint (RomFileName, sizeof (RomFileName), L"%s[%d]", FileName, ImageIndex);
326           FilePath = FileDevicePath (NULL, RomFileName);
327 
328           Status = gBS->LoadImage (
329                         TRUE,
330                         gImageHandle,
331                         FilePath,
332                         ImageBuffer,
333                         ImageLength,
334                         &ImageHandle
335                        );
336           if (EFI_ERROR (Status)) {
337             //
338             // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created
339             // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.
340             // If the caller doesn't have the option to defer the execution of an image, we should
341             // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.
342             //
343             if (Status == EFI_SECURITY_VIOLATION) {
344               gBS->UnloadImage (ImageHandle);
345             }
346             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LOADPCIROM_LOAD_FAIL), gShellDebug1HiiHandle, L"loadpcirom", FileName, ImageIndex);
347 //            PrintToken (STRING_TOKEN (STR_LOADPCIROM_LOAD_IMAGE_ERROR), HiiHandle, ImageIndex, Status);
348           } else {
349             Status = gBS->StartImage (ImageHandle, NULL, NULL);
350             if (EFI_ERROR (Status)) {
351               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_LOADPCIROM_START_FAIL), gShellDebug1HiiHandle, L"loadpcirom", FileName, ImageIndex);
352 //              PrintToken (STRING_TOKEN (STR_LOADPCIROM_START_IMAGE), HiiHandle, ImageIndex, Status);
353             } else {
354               ReturnStatus = Status;
355             }
356           }
357         }
358 
359         if (DecompressedImageBuffer != NULL) {
360           FreePool (DecompressedImageBuffer);
361         }
362 
363       }
364     }
365 
366     RomBarOffset = RomBarOffset + ImageSize;
367     ImageIndex++;
368   } while (((Pcir->Indicator & 0x80) == 0x00) && ((RomBarOffset - (UINTN) RomBar) < RomSize));
369 
370   return ReturnStatus;
371 }
372 
373 /**
374   Connects all available drives and controllers.
375 
376   @retval EFI_SUCCESS     The operation was successful.
377   @retval EFI_ABORTED     The abort mechanism was received.
378 **/
379 EFI_STATUS
LoadPciRomConnectAllDriversToAllControllers(VOID)380 LoadPciRomConnectAllDriversToAllControllers (
381   VOID
382   )
383 {
384   EFI_STATUS  Status;
385   UINTN       HandleCount;
386   EFI_HANDLE  *HandleBuffer;
387   UINTN       Index;
388 
389   Status = gBS->LocateHandleBuffer (
390                   AllHandles,
391                   NULL,
392                   NULL,
393                   &HandleCount,
394                   &HandleBuffer
395                   );
396   if (EFI_ERROR (Status)) {
397     return Status;
398   }
399 
400   for (Index = 0; Index < HandleCount; Index++) {
401     if (ShellGetExecutionBreakFlag ()) {
402       Status = EFI_ABORTED;
403       break;
404     }
405     gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
406   }
407 
408   if (HandleBuffer != NULL) {
409     FreePool (HandleBuffer);
410   }
411   return Status;
412 }
413