1 /** @file
2   Main file for connect shell Driver1 function.
3 
4   (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
5   Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "UefiShellDriver1CommandsLib.h"
17 
18 /**
19   Create all handles associate with every device path node.
20 
21   @param  DevicePathToConnect           The device path which will be connected.
22 
23   @retval EFI_SUCCESS                   All handles associate with every device path node
24                                         have been created.
25   @retval EFI_INVALID_PARAMETER         DevicePathToConnect is NULL.
26   @retval EFI_NOT_FOUND                 Create the handle associate with one device path
27                                         node failed
28 
29 **/
30 EFI_STATUS
ShellConnectDevicePath(IN EFI_DEVICE_PATH_PROTOCOL * DevicePathToConnect)31 ShellConnectDevicePath (
32   IN EFI_DEVICE_PATH_PROTOCOL   *DevicePathToConnect
33   )
34 {
35   EFI_DEVICE_PATH_PROTOCOL  *RemainingDevicePath;
36   EFI_STATUS                Status;
37   EFI_HANDLE                Handle;
38   EFI_HANDLE                PreviousHandle;
39 
40   if (DevicePathToConnect == NULL) {
41     return EFI_INVALID_PARAMETER;
42   }
43 
44   PreviousHandle = NULL;
45   do{
46     RemainingDevicePath = DevicePathToConnect;
47     Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &RemainingDevicePath, &Handle);
48 
49     if (!EFI_ERROR (Status) && (Handle != NULL)) {
50       if (PreviousHandle == Handle) {
51         Status = EFI_NOT_FOUND;
52       } else {
53         PreviousHandle = Handle;
54         Status = gBS->ConnectController (Handle, NULL, RemainingDevicePath, FALSE);
55       }
56     }
57 
58   } while (!EFI_ERROR (Status) && !IsDevicePathEnd (RemainingDevicePath) );
59 
60   return Status;
61 
62 }
63 
64 /**
65   Connect drivers for PCI root bridge.
66 
67   @retval EFI_SUCCESS                     Connect drivers successfully.
68   @retval EFI_NOT_FOUND                   Cannot find PCI root bridge device.
69 
70 **/
71 EFI_STATUS
ShellConnectPciRootBridge(VOID)72 ShellConnectPciRootBridge (
73   VOID
74   )
75 {
76   UINTN               RootBridgeHandleCount;
77   EFI_HANDLE          *RootBridgeHandleBuffer;
78   UINTN               RootBridgeIndex;
79   EFI_STATUS          Status;
80 
81   RootBridgeHandleCount = 0;
82 
83   Status = gBS->LocateHandleBuffer (
84               ByProtocol,
85               &gEfiPciRootBridgeIoProtocolGuid,
86               NULL,
87               &RootBridgeHandleCount,
88               &RootBridgeHandleBuffer
89               );
90   if (EFI_ERROR (Status)) {
91     return Status;
92   }
93 
94   for (RootBridgeIndex = 0; RootBridgeIndex < RootBridgeHandleCount; RootBridgeIndex++) {
95     gBS->ConnectController (RootBridgeHandleBuffer[RootBridgeIndex], NULL, NULL, FALSE);
96   }
97 
98   FreePool (RootBridgeHandleBuffer);
99 
100   return EFI_SUCCESS;
101 }
102 
103 
104 /**
105   Connect controller(s) and driver(s).
106 
107   @param[in] ControllerHandle     The handle to the controller. Should have driver binding on it.
108   @param[in] DriverHandle         The handle to the driver. Should have driver binding.
109   @param[in] Recursive            TRUE to connect recursively, FALSE otherwise.
110   @param[in] Output               TRUE to have info on the screen, FALSE otherwise.
111   @param[in] AlwaysOutput         Override Output for errors.
112 
113   @retval EFI_SUCCESS             The operation was successful.
114 **/
115 EFI_STATUS
ConnectControllers(IN CONST EFI_HANDLE ControllerHandle OPTIONAL,IN CONST EFI_HANDLE DriverHandle OPTIONAL,IN CONST BOOLEAN Recursive,IN CONST BOOLEAN Output,IN CONST BOOLEAN AlwaysOutput)116 ConnectControllers (
117   IN CONST EFI_HANDLE ControllerHandle OPTIONAL,
118   IN CONST EFI_HANDLE DriverHandle OPTIONAL,
119   IN CONST BOOLEAN    Recursive,
120   IN CONST BOOLEAN    Output,
121   IN CONST BOOLEAN    AlwaysOutput
122   )
123 {
124   EFI_STATUS Status;
125   EFI_STATUS Status2;
126   EFI_HANDLE *ControllerHandleList;
127   EFI_HANDLE *DriverHandleList;
128   EFI_HANDLE *HandleWalker;
129 
130   ControllerHandleList  = NULL;
131   Status                = EFI_NOT_FOUND;
132   Status2               = EFI_NOT_FOUND;
133 
134   //
135   // If we have a single handle to connect make that a 'list'
136   //
137   if (DriverHandle == NULL) {
138     DriverHandleList = NULL;
139   } else {
140     DriverHandleList = AllocateZeroPool(2*sizeof(EFI_HANDLE));
141     if (DriverHandleList == NULL) {
142       return (EFI_OUT_OF_RESOURCES);
143     }
144     DriverHandleList[0] = DriverHandle;
145     DriverHandleList[1] = NULL;
146   }
147 
148   //
149   // do we connect all controllers (with a loop) or a single one...
150   // This is where we call the gBS->ConnectController function.
151   //
152   if (ControllerHandle == NULL) {
153     ControllerHandleList = GetHandleListByProtocol(&gEfiDevicePathProtocolGuid);
154     for (HandleWalker = ControllerHandleList
155       ;  HandleWalker != NULL && *HandleWalker != NULL
156       ;  HandleWalker++
157      ){
158       Status = gBS->ConnectController(*HandleWalker, DriverHandleList, NULL, Recursive);
159       if (!EFI_ERROR(Status)) {
160         Status2 = EFI_SUCCESS;
161       }
162       if ((Output && !EFI_ERROR(Status)) || AlwaysOutput) {
163         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_HANDLE_RESULT), gShellDriver1HiiHandle, L"Connect", ConvertHandleToHandleIndex(*HandleWalker), Status);
164       }
165     }
166   } else {
167     Status = gBS->ConnectController(ControllerHandle, DriverHandleList, NULL, Recursive);
168     if (!EFI_ERROR(Status)) {
169       Status2 = EFI_SUCCESS;
170     }
171     if ((Output && !EFI_ERROR(Status)) || AlwaysOutput) {
172       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN(STR_HANDLE_RESULT), gShellDriver1HiiHandle, L"Connect", ConvertHandleToHandleIndex(ControllerHandle), Status);
173     }
174   }
175 
176   //
177   // Free any memory we allocated.
178   //
179   if (ControllerHandleList != NULL) {
180     FreePool(ControllerHandleList);
181   }
182   if (DriverHandleList     != NULL) {
183     FreePool(DriverHandleList);
184   }
185   return (Status2);
186 }
187 
188 /**
189   Do a connect from an EFI variable via it's key name.
190 
191   @param[in] Key      The name of the EFI Variable.
192 
193   @retval EFI_SUCCESS   The operation was successful.
194 **/
195 EFI_STATUS
ShellConnectFromDevPaths(IN CONST CHAR16 * Key)196 ShellConnectFromDevPaths (
197   IN CONST CHAR16 *Key
198   )
199 {
200   EFI_DEVICE_PATH_PROTOCOL  *DevPath;
201   EFI_DEVICE_PATH_PROTOCOL  *CopyOfDevPath;
202   EFI_DEVICE_PATH_PROTOCOL  *Instance;
203   EFI_DEVICE_PATH_PROTOCOL  *Next;
204   UINTN                     Length;
205   UINTN                     Index;
206   UINTN                     HandleArrayCount;
207   UINTN                     Size;
208   EFI_HANDLE                *HandleArray;
209   EFI_STATUS                Status;
210   BOOLEAN                   AtLeastOneConnected;
211   EFI_PCI_IO_PROTOCOL       *PciIo;
212   UINT8                     Class[3];
213 
214   DevPath = NULL;
215   Length  = 0;
216   AtLeastOneConnected = FALSE;
217 
218   //
219   // Get the DevicePath buffer from the variable...
220   //
221   Status = gRT->GetVariable((CHAR16*)Key, (EFI_GUID*)&gEfiGlobalVariableGuid, NULL, &Length, DevPath);
222   if (Status == EFI_BUFFER_TOO_SMALL) {
223     DevPath = AllocateZeroPool(Length);
224     if (DevPath == NULL) {
225       return EFI_OUT_OF_RESOURCES;
226     }
227     Status = gRT->GetVariable((CHAR16*)Key, (EFI_GUID*)&gEfiGlobalVariableGuid, NULL, &Length, DevPath);
228     if (EFI_ERROR (Status)) {
229       if (DevPath != NULL) {
230         FreePool (DevPath);
231       }
232       return Status;
233     }
234   } else if (EFI_ERROR (Status)) {
235     return Status;
236   }
237 
238   Status = EFI_NOT_FOUND;
239 
240   CopyOfDevPath = DevPath;
241   //
242   // walk the list of devices and connect them
243   //
244   do {
245     //
246     // Check every instance of the console variable
247     //
248     Instance = GetNextDevicePathInstance (&CopyOfDevPath, &Size);
249     if (Instance == NULL) {
250       if (DevPath != NULL) {
251         FreePool (DevPath);
252       }
253       return EFI_UNSUPPORTED;
254     }
255 
256     Next = Instance;
257     while (!IsDevicePathEndType (Next)) {
258       Next = NextDevicePathNode (Next);
259     }
260 
261     SetDevicePathEndNode (Next);
262     //
263     // connect short form device path
264     //
265     if ((DevicePathType (Instance) == MESSAGING_DEVICE_PATH) &&
266       ((DevicePathSubType (Instance) == MSG_USB_CLASS_DP)
267       || (DevicePathSubType (Instance) == MSG_USB_WWID_DP)
268       )) {
269 
270       Status = ShellConnectPciRootBridge ();
271       if (EFI_ERROR(Status)) {
272         FreePool(Instance);
273         FreePool(DevPath);
274         return Status;
275       }
276 
277       Status = gBS->LocateHandleBuffer (
278                   ByProtocol,
279                   &gEfiPciIoProtocolGuid,
280                   NULL,
281                   &HandleArrayCount,
282                   &HandleArray
283                   );
284 
285       if (!EFI_ERROR (Status)) {
286         for (Index = 0; Index < HandleArrayCount; Index++) {
287           Status = gBS->HandleProtocol (
288                       HandleArray[Index],
289                       &gEfiPciIoProtocolGuid,
290                       (VOID **)&PciIo
291                       );
292 
293           if (!EFI_ERROR (Status)) {
294             Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0x09, 3, &Class);
295             if (!EFI_ERROR (Status)) {
296               if ((PCI_CLASS_SERIAL == Class[2]) &&
297                   (PCI_CLASS_SERIAL_USB == Class[1])) {
298                 Status = gBS->ConnectController (
299                               HandleArray[Index],
300                               NULL,
301                               Instance,
302                               FALSE
303                               );
304                 if (!EFI_ERROR(Status)) {
305                   AtLeastOneConnected = TRUE;
306                 }
307               }
308             }
309           }
310         }
311       }
312 
313       if (HandleArray != NULL) {
314         FreePool (HandleArray);
315       }
316     } else {
317       //
318       // connect the entire device path
319       //
320       Status = ShellConnectDevicePath (Instance);
321       if (!EFI_ERROR (Status)) {
322         AtLeastOneConnected = TRUE;
323       }
324     }
325     FreePool (Instance);
326 
327   } while (CopyOfDevPath != NULL);
328 
329   if (DevPath != NULL) {
330     FreePool(DevPath);
331   }
332 
333   if (AtLeastOneConnected) {
334     return EFI_SUCCESS;
335   } else {
336     return EFI_NOT_FOUND;
337   }
338 
339 }
340 
341 /**
342   Convert the handle identifiers from strings and then connect them.
343 
344   One of them should have driver binding and either can be NULL.
345 
346   @param[in] Handle1            The first handle.
347   @param[in] Handle2            The second handle.
348   @param[in] Recursive          TRUE to do connect recursively. FALSE otherwise.
349   @param[in] Output             TRUE to have output to screen. FALSE otherwise.
350 
351   @retval EFI_SUCCESS           The operation was successful.
352 **/
353 EFI_STATUS
ConvertAndConnectControllers(IN EFI_HANDLE * Handle1 OPTIONAL,IN EFI_HANDLE * Handle2 OPTIONAL,IN CONST BOOLEAN Recursive,IN CONST BOOLEAN Output)354 ConvertAndConnectControllers (
355   IN EFI_HANDLE     *Handle1 OPTIONAL,
356   IN EFI_HANDLE     *Handle2 OPTIONAL,
357   IN CONST BOOLEAN  Recursive,
358   IN CONST BOOLEAN  Output
359   )
360 {
361   //
362   // if only one is NULL verify it's the proper one...
363   //
364   if ( (Handle1 == NULL && Handle2 != NULL)
365     || (Handle1 != NULL && Handle2 == NULL)
366    ){
367     //
368     // Figure out which one should be NULL and move the handle to the right place.
369     // If Handle1 is NULL then test Handle2 and vise versa.
370     // The one that DOES has driver binding must be Handle2
371     //
372     if (Handle1 == NULL) {
373       if (EFI_ERROR(gBS->OpenProtocol(Handle2, &gEfiDriverBindingProtocolGuid, NULL, NULL, gImageHandle, EFI_OPEN_PROTOCOL_TEST_PROTOCOL))) {
374         // swap
375         Handle1 = Handle2;
376         Handle2 = NULL;
377       } else {
378         // We're all good...
379       }
380     } else {
381       if (EFI_ERROR(gBS->OpenProtocol(Handle1, &gEfiDriverBindingProtocolGuid, NULL, NULL, gImageHandle, EFI_OPEN_PROTOCOL_TEST_PROTOCOL))) {
382         // We're all good...
383       } else {
384         // swap
385         Handle2 = Handle1;
386         Handle1 = NULL;
387       }
388     }
389   }
390 
391   return (ConnectControllers(Handle1, Handle2, Recursive, Output, (BOOLEAN)(Handle2 != NULL && Handle1 != NULL)));
392 }
393 
394 STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
395   {L"-c", TypeFlag},
396   {L"-r", TypeFlag},
397   {NULL, TypeMax}
398   };
399 
400 /**
401   Function for 'connect' command.
402 
403   @param[in] ImageHandle  Handle to the Image (NULL if Internal).
404   @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
405 **/
406 SHELL_STATUS
407 EFIAPI
ShellCommandRunConnect(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)408 ShellCommandRunConnect (
409   IN EFI_HANDLE        ImageHandle,
410   IN EFI_SYSTEM_TABLE  *SystemTable
411   )
412 {
413   EFI_STATUS          Status;
414   LIST_ENTRY          *Package;
415   CHAR16              *ProblemParam;
416   SHELL_STATUS        ShellStatus;
417   CONST CHAR16        *Param1;
418   CONST CHAR16        *Param2;
419   UINTN               Count;
420   EFI_HANDLE          Handle1;
421   EFI_HANDLE          Handle2;
422   UINT64              Intermediate;
423 
424   ShellStatus         = SHELL_SUCCESS;
425   //
426   // initialize the shell lib (we must be in non-auto-init...)
427   //
428   Status = ShellInitialize();
429   ASSERT_EFI_ERROR(Status);
430 
431   Status = CommandInit();
432   ASSERT_EFI_ERROR(Status);
433 
434   //
435   // parse the command line
436   //
437   Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
438   if (EFI_ERROR(Status)) {
439     if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
440       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellDriver1HiiHandle, L"connect", ProblemParam);
441       FreePool(ProblemParam);
442       ShellStatus = SHELL_INVALID_PARAMETER;
443     } else {
444       ASSERT(FALSE);
445     }
446   } else {
447     //
448     // if more than 2 'value' parameters (plus the name one) or either -r or -c with any value parameters we have too many parameters
449     //
450     Count = (gInReconnect?0x4:0x3);
451     if ((ShellCommandLineGetCount(Package) > Count)
452       ||(ShellCommandLineGetFlag(Package, L"-c") && ShellCommandLineGetCount(Package)>1)
453       ||(ShellCommandLineGetFlag(Package, L"-r") && ShellCommandLineGetCount(Package)>2)
454       ||(ShellCommandLineGetFlag(Package, L"-r") && ShellCommandLineGetFlag(Package, L"-c") )
455     ){
456       //
457       // error for too many parameters
458       //
459       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellDriver1HiiHandle, L"connect");
460       ShellStatus = SHELL_INVALID_PARAMETER;
461     } else if (ShellCommandLineGetFlag(Package, L"-c")) {
462       //
463       // do the conin and conout from EFI variables
464       // if the first fails dont 'loose' the error
465       //
466       Status = ShellConnectFromDevPaths(L"ConInDev");
467       if (EFI_ERROR(Status)) {
468         ShellConnectFromDevPaths(L"ConOutDev");
469       } else {
470         Status = ShellConnectFromDevPaths(L"ConOutDev");
471       }
472       if (EFI_ERROR(Status)) {
473         ShellConnectFromDevPaths(L"ErrOutDev");
474       } else {
475         Status = ShellConnectFromDevPaths(L"ErrOutDev");
476       }
477       if (EFI_ERROR(Status)) {
478         ShellConnectFromDevPaths(L"ErrOut");
479       } else {
480         Status = ShellConnectFromDevPaths(L"ErrOut");
481       }
482       if (EFI_ERROR(Status)) {
483         ShellConnectFromDevPaths(L"ConIn");
484       } else {
485         Status = ShellConnectFromDevPaths(L"ConIn");
486       }
487       if (EFI_ERROR(Status)) {
488         ShellConnectFromDevPaths(L"ConOut");
489       } else {
490         Status = ShellConnectFromDevPaths(L"ConOut");
491       }
492       if (EFI_ERROR(Status)) {
493         ShellStatus = SHELL_DEVICE_ERROR;
494       }
495     } else {
496       //
497       // 0, 1, or 2 specific handles and possibly recursive
498       //
499       Param1  = ShellCommandLineGetRawValue(Package, 1);
500       Param2  = ShellCommandLineGetRawValue(Package, 2);
501       Count   = ShellCommandLineGetCount(Package);
502 
503       if (Param1 != NULL) {
504         Status  = ShellConvertStringToUint64(Param1, &Intermediate, TRUE, FALSE);
505         Handle1 = ConvertHandleIndexToHandle((UINTN)Intermediate);
506         if (EFI_ERROR(Status)) {
507           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_INV_HANDLE), gShellDriver1HiiHandle, L"connect", Param1);
508           ShellStatus = SHELL_INVALID_PARAMETER;
509         }
510       } else {
511         Handle1 = NULL;
512       }
513 
514       if (Param2 != NULL) {
515         Status  = ShellConvertStringToUint64(Param2, &Intermediate, TRUE, FALSE);
516         Handle2 = ConvertHandleIndexToHandle((UINTN)Intermediate);
517         if (EFI_ERROR(Status)) {
518           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_INV_HANDLE), gShellDriver1HiiHandle, L"connect", Param2);
519           ShellStatus = SHELL_INVALID_PARAMETER;
520         }
521       } else {
522         Handle2 = NULL;
523       }
524 
525       if (ShellStatus == SHELL_SUCCESS) {
526         if (Param1 != NULL && Handle1 == NULL){
527           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_INV_HANDLE), gShellDriver1HiiHandle, L"connect", Param1);
528           ShellStatus = SHELL_INVALID_PARAMETER;
529         } else if (Param2 != NULL && Handle2 == NULL) {
530           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_INV_HANDLE), gShellDriver1HiiHandle, L"connect", Param2);
531           ShellStatus = SHELL_INVALID_PARAMETER;
532         } else if (Handle2 != NULL && Handle1 != NULL && EFI_ERROR(gBS->OpenProtocol(Handle2, &gEfiDriverBindingProtocolGuid, NULL, gImageHandle, NULL, EFI_OPEN_PROTOCOL_TEST_PROTOCOL))) {
533           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_INV_HANDLE), gShellDriver1HiiHandle, L"connect", Param2);
534           ShellStatus = SHELL_INVALID_PARAMETER;
535         } else {
536           Status = ConvertAndConnectControllers(Handle1, Handle2, ShellCommandLineGetFlag(Package, L"-r"), (BOOLEAN)(Count!=0));
537           if (EFI_ERROR(Status)) {
538             ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_CONNECT_NONE), gShellDriver1HiiHandle);
539             ShellStatus = SHELL_DEVICE_ERROR;
540           }
541         }
542       }
543     }
544 
545     ShellCommandLineFreeVarList (Package);
546   }
547   return (ShellStatus);
548 }
549 
550